From 60bf1741edd0c5a9a8e73856ce39151fb8f2eafc Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 16 Nov 2025 18:15:33 +0000 Subject: [PATCH 1/3] Add pfUI cooldown integration for combo-aware durations Hook into pfUI.api.libdebuff to inject combo-based durations: - Hook GetDuration: Returns learned combo durations for combo spells - Hook AddEffect: Injects tracked combo durations when debuffs are applied This ensures pfUI's cooldown module displays the correct duration (e.g., 28s for 5 CP Rip instead of base 12s). Debug messages show when pfUI hooks are triggered in debug mode. --- Compatibility/pfUI.lua | 89 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/Compatibility/pfUI.lua b/Compatibility/pfUI.lua index 992cf25..3dd0a0e 100644 --- a/Compatibility/pfUI.lua +++ b/Compatibility/pfUI.lua @@ -91,6 +91,92 @@ function Extension.HandleSendChatMessageHook() end end +-- Hook pfUI's libdebuff to use our combo-aware durations +function Extension.HookPfUILibdebuff() + if not pfUI or not pfUI.api or not pfUI.api.libdebuff then + return false + end + + local pflib = pfUI.api.libdebuff + + -- Hook GetDuration if it exists + if pflib.GetDuration and not Extension.pfLibDebuffHooked then + local originalGetDuration = pflib.GetDuration + + pflib.GetDuration = function(self, spellID, casterGUID, comboPoints) + -- Check if this is a combo scaling spell first + if CleveRoids.IsComboScalingSpellID and CleveRoids.IsComboScalingSpellID(spellID) then + -- If combo points provided, check learned combo durations + if comboPoints and CleveRoids_ComboDurations and CleveRoids_ComboDurations[spellID] then + local comboDur = CleveRoids_ComboDurations[spellID][comboPoints] + if comboDur and comboDur > 0 then + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cff00ff00[pfUI Hook]|r Returning %ds for spell %d at %d CP", + comboDur, spellID, comboPoints) + ) + end + return comboDur + end + end + + -- Fallback: check for highest learned combo duration + if CleveRoids_ComboDurations and CleveRoids_ComboDurations[spellID] then + for cp = 5, 1, -1 do + if CleveRoids_ComboDurations[spellID][cp] then + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cff00ff00[pfUI Hook]|r Returning %ds for spell %d (fallback %d CP)", + CleveRoids_ComboDurations[spellID][cp], spellID, cp) + ) + end + return CleveRoids_ComboDurations[spellID][cp] + end + end + end + end + + -- Not a combo spell or no learned duration, use original + return originalGetDuration(self, spellID, casterGUID, comboPoints) + end + + Extension.pfLibDebuffHooked = true + Extension.DLOG("Hooked pfUI.api.libdebuff.GetDuration for combo duration support") + end + + -- Hook AddEffect if it exists to inject combo-aware durations + if pflib.AddEffect and not Extension.pfLibAddEffectHooked then + local originalAddEffect = pflib.AddEffect + + pflib.AddEffect = function(self, guid, unitName, spellID, duration, stacks, caster) + -- For combo spells, override duration with our tracked value + if CleveRoids.IsComboScalingSpellID and CleveRoids.IsComboScalingSpellID(spellID) then + -- Check if we have tracking data for this spell + if CleveRoids.ComboPointTracking and CleveRoids.ComboPointTracking.byID and + CleveRoids.ComboPointTracking.byID[spellID] then + local tracking = CleveRoids.ComboPointTracking.byID[spellID] + if tracking.target_guid == guid and tracking.duration then + duration = tracking.duration + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cff00ff00[pfUI AddEffect Hook]|r Overriding duration to %ds for spell %d", + duration, spellID) + ) + end + end + end + end + + return originalAddEffect(self, guid, unitName, spellID, duration, stacks, caster) + end + + Extension.pfLibAddEffectHooked = true + Extension.DLOG("Hooked pfUI.api.libdebuff.AddEffect for combo duration support") + end + + return Extension.pfLibDebuffHooked or Extension.pfLibAddEffectHooked +end + -- Main compatibility check and setup function Extension.SetupCompatibility() Extension.pfUILoaded = (pfUI ~= nil) @@ -99,6 +185,9 @@ function Extension.SetupCompatibility() if Extension.pfUILoaded then Extension.DLOG("pfUI detected") + -- Hook libdebuff for combo duration support + Extension.HookPfUILibdebuff() + if Extension.macrotweakLoaded then Extension.DLOG("pfUI macrotweak module detected") From d6b4ae3d97387b00d000d12e7317828b27b6ea48 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 16 Nov 2025 18:18:41 +0000 Subject: [PATCH 2/3] Add direct pfUI duration synchronization for combo spells After analyzing pfUI's cooldown module, we need to directly sync combo durations to pfUI's libdebuff.objects storage: - Added SyncComboDurationToPfUI function to force-update pfUI's stored debuff duration after tracking combo spells - Called from UNIT_CASTEVENT handler after AddEffect - This ensures pfUI's cooldown display shows the correct combo duration (e.g., 28s for 5 CP Rip) instead of base 12s pfUI's cooldown module is just a display layer - it shows durations from pfUI.api.libdebuff.objects. By directly updating this storage, we ensure the correct duration is displayed regardless of timing. --- Compatibility/pfUI.lua | 24 ++++++++++++++++++++++++ Utility.lua | 6 ++++++ 2 files changed, 30 insertions(+) diff --git a/Compatibility/pfUI.lua b/Compatibility/pfUI.lua index 3dd0a0e..0aa8833 100644 --- a/Compatibility/pfUI.lua +++ b/Compatibility/pfUI.lua @@ -177,6 +177,27 @@ function Extension.HookPfUILibdebuff() return Extension.pfLibDebuffHooked or Extension.pfLibAddEffectHooked end +-- Synchronize combo durations to pfUI's libdebuff objects +function Extension.SyncComboDurationToPfUI(guid, spellID, duration) + if not pfUI or not pfUI.api or not pfUI.api.libdebuff then + return + end + + -- Update pfUI's stored debuff duration + local pflib = pfUI.api.libdebuff + if pflib.objects and pflib.objects[guid] and pflib.objects[guid][spellID] then + local old_duration = pflib.objects[guid][spellID].duration + pflib.objects[guid][spellID].duration = duration + + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cff00ffaa[pfUI Sync]|r Updated spell %d duration: %ds -> %ds", + spellID, old_duration or 0, duration) + ) + end + end +end + -- Main compatibility check and setup function Extension.SetupCompatibility() Extension.pfUILoaded = (pfUI ~= nil) @@ -204,6 +225,9 @@ function Extension.OnLoad() Extension.DLOG("Extension pfUI Loaded.") Extension.HookMethod(CleveRoids, "GetFocusName", "FocusNameHook", true) + -- Export extension for external access + CleveRoids.Compatibility_pfUI = Extension + -- Initial compatibility check Extension.SetupCompatibility() end diff --git a/Utility.lua b/Utility.lua index 29b9719..81ed0ca 100644 --- a/Utility.lua +++ b/Utility.lua @@ -956,6 +956,12 @@ ev:SetScript("OnEvent", function() end lib:AddEffect(targetGUID, targetName, spellID, duration, 0, "player") + -- Sync combo duration to pfUI if it's loaded + if comboPoints and CleveRoids.Compatibility_pfUI and + CleveRoids.Compatibility_pfUI.SyncComboDurationToPfUI then + CleveRoids.Compatibility_pfUI.SyncComboDurationToPfUI(targetGUID, spellID, duration) + end + -- ALWAYS set up learning for combo spells (even if we have calculated duration) if comboPoints then lib.learnCastTimers[targetGUID] = lib.learnCastTimers[targetGUID] or {} From 3e669a7b7a0f022fe0c5a78754793535b94e3703 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 16 Nov 2025 18:20:51 +0000 Subject: [PATCH 3/3] Fix pfUI integration - use unit names and spell names, not GUIDs/IDs CRITICAL FIX: pfUI's libdebuff uses completely different data structures: - Uses unit NAME (not GUID) - Uses unit LEVEL as a key - Uses spell NAME (effect, not ID) - Storage: pflib.objects[unitName][unitLevel][effectName] Fixed SyncComboDurationToPfUI: - Convert GUID to unit name (check target, fallback to guidToName map) - Get unit level from target or default to 0 - Convert spell ID to spell name using SpellInfo() - Remove rank from spell name to match pfUI's format - Search both specific level and level 0 (pfUI's fallback) Fixed AddEffect hook: - Updated parameters to match pfUI signature: (unit, unitlevel, effect, duration, caster) - Check spell by name instead of ID - Map spell name back to ID to find tracking data This ensures pfUI's cooldown displays show correct combo durations. --- Compatibility/pfUI.lua | 107 ++++++++++++++++++++++++++++++++--------- 1 file changed, 84 insertions(+), 23 deletions(-) diff --git a/Compatibility/pfUI.lua b/Compatibility/pfUI.lua index 0aa8833..587e810 100644 --- a/Compatibility/pfUI.lua +++ b/Compatibility/pfUI.lua @@ -148,26 +148,37 @@ function Extension.HookPfUILibdebuff() if pflib.AddEffect and not Extension.pfLibAddEffectHooked then local originalAddEffect = pflib.AddEffect - pflib.AddEffect = function(self, guid, unitName, spellID, duration, stacks, caster) - -- For combo spells, override duration with our tracked value - if CleveRoids.IsComboScalingSpellID and CleveRoids.IsComboScalingSpellID(spellID) then - -- Check if we have tracking data for this spell - if CleveRoids.ComboPointTracking and CleveRoids.ComboPointTracking.byID and - CleveRoids.ComboPointTracking.byID[spellID] then - local tracking = CleveRoids.ComboPointTracking.byID[spellID] - if tracking.target_guid == guid and tracking.duration then - duration = tracking.duration - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cff00ff00[pfUI AddEffect Hook]|r Overriding duration to %ds for spell %d", - duration, spellID) - ) + pflib.AddEffect = function(self, unit, unitlevel, effect, duration, caster) + -- effect is a spell name, not ID + -- Check if this is a combo scaling spell by name + if CleveRoids.IsComboScalingSpell and CleveRoids.IsComboScalingSpell(effect) then + -- Try to get learned duration for this combo spell + local spellData = CleveRoids.GetComboScalingData(effect) + if spellData then + -- Find the spell ID for this effect + for spellID, data in pairs(CleveRoids.ComboScalingSpellsByID) do + if data.name == effect then + -- Check if we have tracking data + if CleveRoids.ComboPointTracking and CleveRoids.ComboPointTracking.byID and + CleveRoids.ComboPointTracking.byID[spellID] then + local tracking = CleveRoids.ComboPointTracking.byID[spellID] + if tracking.duration then + duration = tracking.duration + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cff00ff00[pfUI AddEffect Hook]|r Overriding %s duration to %ds", + effect, duration) + ) + end + break + end + end end end end end - return originalAddEffect(self, guid, unitName, spellID, duration, stacks, caster) + return originalAddEffect(self, unit, unitlevel, effect, duration, caster) end Extension.pfLibAddEffectHooked = true @@ -183,19 +194,69 @@ function Extension.SyncComboDurationToPfUI(guid, spellID, duration) return end + -- Get unit name from GUID + local unitName = nil + local unitLevel = 0 + + -- Check if this is the current target + local _, targetGUID = UnitExists("target") + if targetGUID == guid then + unitName = UnitName("target") + unitLevel = UnitLevel("target") or 0 + end + + -- If we couldn't find the unit, use GUID to name mapping from libdebuff + if not unitName and CleveRoids.libdebuff and CleveRoids.libdebuff.guidToName then + unitName = CleveRoids.libdebuff.guidToName[guid] + -- Default to level 0 if we don't have the unit targeted + unitLevel = 0 + end + + if not unitName then + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[pfUI Sync]|r Could not find unit name for GUID") + end + return + end + + -- Get spell name from spell ID + local spellName = SpellInfo(spellID) + if not spellName then + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[pfUI Sync]|r Could not find spell name for ID " .. spellID) + end + return + end + + -- Remove rank from spell name to match pfUI's format + local effectName = string.gsub(spellName, "%s*%(Rank %d+%)", "") + -- Update pfUI's stored debuff duration local pflib = pfUI.api.libdebuff - if pflib.objects and pflib.objects[guid] and pflib.objects[guid][spellID] then - local old_duration = pflib.objects[guid][spellID].duration - pflib.objects[guid][spellID].duration = duration + if pflib.objects and pflib.objects[unitName] then + -- Try both the specific level and level 0 (fallback) + for _, level in ipairs({unitLevel, 0}) do + if pflib.objects[unitName][level] and pflib.objects[unitName][level][effectName] then + local old_duration = pflib.objects[unitName][level][effectName].duration + pflib.objects[unitName][level][effectName].duration = duration - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cff00ffaa[pfUI Sync]|r Updated spell %d duration: %ds -> %ds", - spellID, old_duration or 0, duration) - ) + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cff00ffaa[pfUI Sync]|r Updated %s on %s (L%d): %ds -> %ds", + effectName, unitName, level, old_duration or 0, duration) + ) + end + return + end end end + + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cffaaaa00[pfUI Sync]|r Effect not found in pfUI storage: %s on %s", + effectName, unitName) + ) + end end -- Main compatibility check and setup