From 073621e233e5a76ebff1c7152a3dcdda7b6a0375 Mon Sep 17 00:00:00 2001 From: Brues <5278969+brues-code@users.noreply.github.com> Date: Sat, 12 Sep 2026 20:25:12 -0500 Subject: [PATCH] Delete the Lua Carnage refresh Carnage refreshes the caster's Rip and Rake when Ferocious Bite procs it, and 1.12 tells an observing caster nothing about it -- no packet carries a debuff's new remaining duration on another unit. So this addon inferred the proc: track every Ferocious Bite cast with its target, watch PLAYER_COMBO_POINTS for combo points failing to drop to 0 within 0.5s, then rewrite the stored Rip and Rake records and push the new durations into pfUI's tables by hand. Roughly 570 lines across three files, and it could only ever be a guess, because the roll that decides whether Carnage procs is server-side. ClassicAPI does it in the DLL (src/turtle/Carnage.cpp), roll-gated, so expirationTime is simply correct and there is nothing to infer. Removed with it: ApplyCarnageRefresh, the Ferocious Bite cast tracking on both the UNIT_CASTEVENT and nampower paths, the avoided-Bite clearing in the miss handler, the Rip/Rake cast snapshots on the UNIT_CASTEVENT and SPELL_GO paths, the Rake landed/failed verification hooks, the proc detector in ComboPointTracker, and lastRipCast / lastRakeCast / RipSpellIDs / RakeSpellIDs, which had no readers left afterwards. carnageDurationOverrides goes too, and it never worked: every reference to it was a write, a clear, or the 30-second sweep in Core.lua's cleanup. Nothing ever read it to change a duration. FerociousBiteSpellIDs stays -- the combo-point snapshot still needs to know a finisher when it sees one. --- ComboPointTracker.lua | 76 +------ Core.lua | 14 -- Utility.lua | 486 ------------------------------------------ 3 files changed, 2 insertions(+), 574 deletions(-) diff --git a/ComboPointTracker.lua b/ComboPointTracker.lua index 11f229d..b696296 100644 --- a/ComboPointTracker.lua +++ b/ComboPointTracker.lua @@ -15,23 +15,6 @@ CleveRoids.ComboPointTracking = CleveRoids.ComboPointTracking or {} -- Structure: CleveRoids_ComboDurations[spellID][comboPoints] = duration CleveRoids_ComboDurations = CleveRoids_ComboDurations or {} --- Storage for last Rip cast (for Carnage talent mechanic) --- Carnage talent: When Ferocious Bite procs Carnage, it refreshes Rip and Rake to their original duration --- Detection: When combo points don't drop to 0 after FB (they stay at 1 = Carnage proc) --- Talent Position: Tab 2 (Feral Combat), Talent 17 --- Rank 1: 10% per CP, Rank 2: 20% per CP -CleveRoids.lastRipCast = CleveRoids.lastRipCast or { - duration = nil, - targetGUID = nil, - timestamp = 0 -} - -CleveRoids.lastRakeCast = CleveRoids.lastRakeCast or { - duration = nil, - targetGUID = nil, - timestamp = 0 -} - -- Family membership without hardcoded rank lists. C_Spell.GetSpellName resolves -- ANY spellID from the client's Spell.dbc -- every rank (so no enumeration or -- spellbook scan) and TWoW's custom spells alike -- so "is this spellID a Rip?" @@ -86,10 +69,8 @@ CleveRoids.ComboScalingSpellsByID = { [9896] = { base = 10, increment = 2, name = "Rip" }, -- Rank 6 } --- Ferocious Bite spell IDs (for Carnage talent mechanic) --- Carnage talent: When FB procs Carnage, refreshes Rip and Rake back to their original duration --- Proc detection: combo points stay at 1 after FB instead of dropping to 0 --- Talent Position: Tab 2 (Feral Combat), Talent 17 +-- Ferocious Bite spell IDs. A combo-point finisher, so the cast-time combo +-- snapshot has to know it. CleveRoids.FerociousBiteSpellIDs = { [22557] = true, -- Rank 1 [22568] = true, -- Rank 2 @@ -99,10 +80,6 @@ CleveRoids.FerociousBiteSpellIDs = { [31018] = true, -- Rank 6 } --- Rip / Rake families (for Carnage talent). Seeded by Rank 1; matches every rank. -CleveRoids.RipSpellIDs = RankSet(1079) -CleveRoids.RakeSpellIDs = RankSet(1822) - -- Combined table for all bleed spells that need immunity detection -- Used when checking if a cast bleed failed to apply (indicates bleed immunity) -- NOTE: These are the DEBUFF spell IDs (what appears on target), not cast spell IDs @@ -773,55 +750,6 @@ end function Extension.OnComboPointsChanged() CleveRoids.UpdateComboPoints() - - -- CARNAGE PROC DETECTION (Cursive-style) - -- When Ferocious Bite is used, combo points should drop to 0 - -- If Carnage procs, combo points will be 1 instead (the Carnage-granted combo point) - -- Check: After Ferocious Bite (within 0.5s), if combo points > 0, Carnage procced - if CleveRoids.lastFerociousBiteTime and CleveRoids.lastFerociousBiteTargetGUID then - local timeSinceBite = GetTime() - CleveRoids.lastFerociousBiteTime - if timeSinceBite < 0.5 then - local currentCP = CleveRoids.GetComboPoints() - if currentCP > 0 then - -- Carnage procced! Combo points didn't drop to 0 (or rose back to 1) - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cffff00ff[Carnage]|r PROC DETECTED! CP=%d after Ferocious Bite (%.2fs ago)", - currentCP, timeSinceBite) - ) - end - - -- Apply the Carnage refresh to Rip and Rake - local targetGUID = CleveRoids.lastFerociousBiteTargetGUID - local targetName = CleveRoids.lastFerociousBiteTargetName or "Unknown" - local biteSpellID = CleveRoids.lastFerociousBiteSpellID - - -- Call the Carnage refresh function in Utility.lua - if CleveRoids.libdebuff and CleveRoids.libdebuff.ApplyCarnageRefresh then - CleveRoids.libdebuff.ApplyCarnageRefresh(targetGUID, targetName, biteSpellID) - end - - -- Clear the tracking to prevent multiple refreshes - CleveRoids.lastFerociousBiteTime = nil - CleveRoids.lastFerociousBiteTargetGUID = nil - CleveRoids.lastFerociousBiteTargetName = nil - CleveRoids.lastFerociousBiteSpellID = nil - end - else - -- Time window expired, clear tracking - if CleveRoids.lastFerociousBiteTime then - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cffff00ff[Carnage]|r No proc - time window expired (%.2fs)", timeSinceBite) - ) - end - CleveRoids.lastFerociousBiteTime = nil - CleveRoids.lastFerociousBiteTargetGUID = nil - CleveRoids.lastFerociousBiteTargetName = nil - CleveRoids.lastFerociousBiteSpellID = nil - end - end - end end -- Event handlers diff --git a/Core.lua b/Core.lua index c500a81..8774d3a 100644 --- a/Core.lua +++ b/Core.lua @@ -4260,20 +4260,6 @@ function CleveRoids.OnUpdate(self) if (time - CR.lastCleanupTime) >= CR.CLEANUP_INTERVAL then CR.lastCleanupTime = time - -- MEMORY: Clean up carnageDurationOverrides older than 30 seconds - -- PERFORMANCE: Use next() directly instead of pairs() to avoid iterator allocation - local carnageOverrides = CR.carnageDurationOverrides - if carnageOverrides then - local spellID, data = next(carnageOverrides) - while spellID do - local nextID = next(carnageOverrides, spellID) - if data.timestamp and (time - data.timestamp) > 30 then - carnageOverrides[spellID] = nil - end - spellID, data = nextID, nextID and carnageOverrides[nextID] - end - end - -- MEMORY: Clean up old ComboPointTracking entries (older than 60 seconds) -- PERFORMANCE: Use next() directly instead of pairs() to avoid iterator allocation local comboTracking = CR.ComboPointTracking diff --git a/Utility.lua b/Utility.lua index a671cf6..e2b8a6a 100644 --- a/Utility.lua +++ b/Utility.lua @@ -2347,11 +2347,6 @@ local function SeedUnit(unit) end end --- Carnage refresh system --- NOTE: Carnage proc detection is now handled in ComboPointTracker.lua via PLAYER_COMBO_POINTS event --- When Carnage procs, combo points don't drop to 0 after Ferocious Bite (they stay at 1) --- The ApplyCarnageRefresh function below is called from ComboPointTracker when a proc is detected - -- WARLOCK DARK HARVEST: Duration Acceleration System (TWoW Custom) -- Credits: Avitasia / Cursive addon -- Dark Harvest is a channeled spell that accelerates DoT tick rate by 30% @@ -2466,264 +2461,7 @@ lib.trackedAfflictions = { ["Rend"] = { type = "school", value = "bleed" }, } --- Function to apply Carnage refresh (exposed for ComboPointTracker to call on proc detection) -function lib.ApplyCarnageRefresh(targetGUID, targetName, biteSpellID) - if CleveRoids.debug then - -- Compare Carnage GUID with current target GUID - local currentTargetGUID = CleveRoids.GetGUID("target") - local guidMatch = (targetGUID == currentTargetGUID) and "MATCH" or "MISMATCH" - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cffff00ff[Carnage]|r ApplyCarnageRefresh called for %s (GUID:%s, current:%s, %s)", - targetName or "Unknown", tostring(targetGUID), tostring(currentTargetGUID), guidMatch) - ) - end - - -- Only refresh debuffs if they're currently active on the target - if not lib.objects[targetGUID] then - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cffff6600[Carnage]|r No tracking data for GUID %s", tostring(targetGUID)) - ) - end - return - end - - -- Try to refresh Rip - if CleveRoids.lastRipCast and CleveRoids.lastRipCast.duration and CleveRoids.lastRipCast.spellID and - CleveRoids.lastRipCast.targetGUID == targetGUID then - -- Only refresh if the SAME rank that was cast is still active - local ripSpellID = CleveRoids.lastRipCast.spellID - local rec = lib.objects[targetGUID] and lib.objects[targetGUID][ripSpellID] - if rec and rec.duration and rec.start then - -- Check if Rip is still active (not expired) - local remaining = rec.duration + rec.start - GetTime() - if remaining > 0 then - -- Found the exact same rank active, refresh it with the saved duration - local ripDuration = CleveRoids.lastRipCast.duration - local ripComboPoints = CleveRoids.lastRipCast.comboPoints or 5 - - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cffff00ff[Carnage]|r About to refresh Rip: %ds on %s (spellID:%d)", - ripDuration, targetName or "Unknown", ripSpellID) - ) - end - - -- Store duration override for pfUI hooks (BEFORE updating tracking) - if not CleveRoids.carnageDurationOverrides then - CleveRoids.carnageDurationOverrides = {} - end - CleveRoids.carnageDurationOverrides[ripSpellID] = { - duration = ripDuration, - timestamp = GetTime(), - targetGUID = targetGUID - } - - -- Update CleveRoids internal tracking - if lib.objects[targetGUID] and lib.objects[targetGUID][ripSpellID] then - local oldCaster = lib.objects[targetGUID][ripSpellID].caster - lib.objects[targetGUID][ripSpellID].duration = ripDuration - lib.objects[targetGUID][ripSpellID].start = GetTime() - lib.objects[targetGUID][ripSpellID].expiry = GetTime() + ripDuration - -- Ensure caster is preserved (required for personal debuff tracking) - if not lib.objects[targetGUID][ripSpellID].caster then - lib.objects[targetGUID][ripSpellID].caster = "player" - end - - if CleveRoids.debug then - -- Verify the record was actually saved - local verifyRec = lib.objects[targetGUID] and lib.objects[targetGUID][ripSpellID] - if verifyRec then - local verifyRemaining = verifyRec.duration + verifyRec.start - GetTime() - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cffff00ff[Carnage]|r Updated CleveRoids tracking for Rip (GUID:%s, caster:%s->%s)", - tostring(targetGUID), tostring(oldCaster), tostring(verifyRec.caster)) - ) - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cffff00ff[Carnage VERIFY]|r Rip record: dur=%s, start=%s, remaining=%.1fs", - tostring(verifyRec.duration), tostring(verifyRec.start), verifyRemaining) - ) - else - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cffff0000[Carnage ERROR]|r Rip record MISSING immediately after update!") - ) - end - end - else - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cffff6600[Carnage]|r WARNING: Rip record not found! GUID:%s, objects[GUID]:%s", - tostring(targetGUID), tostring(lib.objects[targetGUID])) - ) - end - end - - -- DON'T call pfUI's AddEffect - just update the existing entry directly - -- pfUI will pick up the new duration through our GetDuration/UnitDebuff hooks - if pfUI and pfUI.api and pfUI.api.libdebuff then - local pflib = pfUI.api.libdebuff - local ripSpellName = C_Spell.GetSpellName(ripSpellID) - local baseName = CleveRoids.StripRank(ripSpellName) or "Rip" - - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cffff00ff[Carnage]|r Updating existing pfUI entry for Rip directly") - ) - end - - -- Find and update the existing pfUI entry (don't create new ones) - if pflib.objects and pflib.objects[targetName] then - local updated = false - for level, effects in pairs(pflib.objects[targetName]) do - if type(effects) == "table" and effects[baseName] then - -- Update the existing entry - effects[baseName].start = GetTime() - effects[baseName].duration = ripDuration - effects[baseName].caster = "player" - updated = true - - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cffff00ff[Carnage]|r Updated pfUI Rip at level %s", tostring(level)) - ) - end - -- Only update the FIRST occurrence to avoid duplicates - break - end - end - - if updated and pflib.UpdateUnits then - pflib:UpdateUnits() - end - end - end - - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cffff00ff[Carnage]|r Finished refreshing Rip: %ds on %s", - ripDuration, targetName or "Unknown") - ) - end - end - end - end - - -- Try to refresh Rake - if CleveRoids.lastRakeCast and CleveRoids.lastRakeCast.duration and CleveRoids.lastRakeCast.spellID and - CleveRoids.lastRakeCast.targetGUID == targetGUID then - -- Only refresh if the SAME rank that was cast is still active - local rakeSpellID = CleveRoids.lastRakeCast.spellID - local rec = lib.objects[targetGUID] and lib.objects[targetGUID][rakeSpellID] - if rec and rec.duration and rec.start then - -- Check if Rake is still active (not expired) - local remaining = rec.duration + rec.start - GetTime() - if remaining > 0 then - -- Found the exact same rank active, refresh it with the saved duration - local rakeDuration = CleveRoids.lastRakeCast.duration - local rakeComboPoints = CleveRoids.lastRakeCast.comboPoints or 5 - - -- Store duration override for pfUI hooks (BEFORE updating tracking) - if not CleveRoids.carnageDurationOverrides then - CleveRoids.carnageDurationOverrides = {} - end - CleveRoids.carnageDurationOverrides[rakeSpellID] = { - duration = rakeDuration, - timestamp = GetTime(), - targetGUID = targetGUID - } - - -- Update CleveRoids internal tracking - if lib.objects[targetGUID] and lib.objects[targetGUID][rakeSpellID] then - local oldCaster = lib.objects[targetGUID][rakeSpellID].caster - lib.objects[targetGUID][rakeSpellID].duration = rakeDuration - lib.objects[targetGUID][rakeSpellID].start = GetTime() - lib.objects[targetGUID][rakeSpellID].expiry = GetTime() + rakeDuration - -- Ensure caster is preserved (required for personal debuff tracking) - if not lib.objects[targetGUID][rakeSpellID].caster then - lib.objects[targetGUID][rakeSpellID].caster = "player" - end - - if CleveRoids.debug then - -- Verify the record was actually saved - local verifyRec = lib.objects[targetGUID] and lib.objects[targetGUID][rakeSpellID] - if verifyRec then - local verifyRemaining = verifyRec.duration + verifyRec.start - GetTime() - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cffff00ff[Carnage]|r Updated CleveRoids tracking for Rake (GUID:%s, caster:%s->%s)", - tostring(targetGUID), tostring(oldCaster), tostring(verifyRec.caster)) - ) - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cffff00ff[Carnage VERIFY]|r Rake record: dur=%s, remaining=%.1fs", - tostring(verifyRec.duration), verifyRemaining) - ) - else - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cffff0000[Carnage ERROR]|r Rake record MISSING immediately after update!") - ) - end - end - else - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cffff6600[Carnage]|r WARNING: Rake record not found! GUID:%s", - tostring(targetGUID)) - ) - end - end - - -- DON'T call pfUI's AddEffect - just update the existing entry directly - -- pfUI will pick up the new duration through our GetDuration/UnitDebuff hooks - if pfUI and pfUI.api and pfUI.api.libdebuff then - local pflib = pfUI.api.libdebuff - local rakeSpellName = C_Spell.GetSpellName(rakeSpellID) - local baseName = CleveRoids.StripRank(rakeSpellName) or "Rake" - - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cffff00ff[Carnage]|r Updating existing pfUI entry for Rake directly") - ) - end - - -- Find and update the existing pfUI entry (don't create new ones) - if pflib.objects and pflib.objects[targetName] then - local updated = false - for level, effects in pairs(pflib.objects[targetName]) do - if type(effects) == "table" and effects[baseName] then - -- Update the existing entry - effects[baseName].start = GetTime() - effects[baseName].duration = rakeDuration - effects[baseName].caster = "player" - updated = true - - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cffff00ff[Carnage]|r Updated pfUI Rake at level %s", tostring(level)) - ) - end - -- Only update the FIRST occurrence to avoid duplicates - break - end - end - - if updated and pflib.UpdateUnits then - pflib:UpdateUnits() - end - end - end - - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cffff00ff[Carnage]|r Refreshed Rake: %ds on %s", - rakeDuration, targetName or "Unknown") - ) - end - end - end - end -end - -- Frame for delayed personal debuff tracking and judgement scanning --- NOTE: Carnage refresh is now handled via PLAYER_COMBO_POINTS event in ComboPointTracker.lua -- PERFORMANCE: Upvalues for frequently called functions local _GetTime = GetTime @@ -3325,21 +3063,6 @@ delayedTrackingFrame:SetScript("OnUpdate", function() CleveRoids.RemoveSpellImmunity(pending.targetName, "bleed") end - -- CARNAGE TALENT: Mark Rake as verified for Ferocious Bite refresh - local isRakeSpell = CleveRoids.RakeSpellIDs and CleveRoids.RakeSpellIDs[pending.spellID] - if isRakeSpell and CleveRoids.lastRakeCast and CleveRoids.lastRakeCast.pending then - if CleveRoids.lastRakeCast.targetGUID == pending.targetGUID and - CleveRoids.lastRakeCast.spellID == pending.spellID then - CleveRoids.lastRakeCast.pending = nil -- Mark as verified - if debug then - DEFAULT_CHAT_FRAME:AddMessage( - _string_format("|cff00ff00[Carnage]|r Rake verified on %s - ready for Ferocious Bite refresh", - pending.targetName or "Unknown") - ) - end - end - end - if debug then local spellName = C_Spell.GetSpellName(pending.spellID) or "Unknown" DEFAULT_CHAT_FRAME:AddMessage( @@ -3347,21 +3070,6 @@ delayedTrackingFrame:SetScript("OnUpdate", function() spellName, pending.spellID, pending.targetName or "Unknown") ) end - else - -- Bleed didn't land - clear pending Rake data for Carnage if applicable - local isRakeSpell = CleveRoids.RakeSpellIDs and CleveRoids.RakeSpellIDs[pending.spellID] - if isRakeSpell and CleveRoids.lastRakeCast and CleveRoids.lastRakeCast.pending then - if CleveRoids.lastRakeCast.targetGUID == pending.targetGUID and - CleveRoids.lastRakeCast.spellID == pending.spellID then - CleveRoids.lastRakeCast = nil -- Clear invalid Rake data - if debug then - DEFAULT_CHAT_FRAME:AddMessage( - _string_format("|cffff6600[Carnage]|r Rake failed to apply on %s - clearing tracking data", - pending.targetName or "Unknown") - ) - end - end - end end -- Item processed, don't add to new list else @@ -4039,43 +3747,6 @@ ev:SetScript("OnEvent", function() local playerGUID = CleveRoids.GetGUID("player") if casterGUID == playerGUID and targetGUID then - -- DRUID CARNAGE TALENT: Track Ferocious Bite cast for proc detection - -- Carnage proc is detected in ComboPointTracker via PLAYER_COMBO_POINTS event - -- When Carnage procs, combo points don't decrease (or increase by 1) - if CleveRoids.FerociousBiteSpellIDs and CleveRoids.FerociousBiteSpellIDs[spellID] then - -- Check if player has Carnage talent (any rank) - -- Carnage: Tab 2 (Feral Combat), Talent 17 - local _, _, _, _, rank = GetTalentInfo(2, 17) - local carnageRank = tonumber(rank) or 0 - - if carnageRank >= 1 then - local targetName = lib.guidToName[targetGUID] - if not targetName then - local currentTargetGUID = CleveRoids.GetGUID("target") - if currentTargetGUID == targetGUID then - targetName = UnitName("target") - lib.guidToName[targetGUID] = targetName - else - targetName = "Unknown" - end - end - - -- Track Ferocious Bite cast for Carnage proc detection (via PLAYER_COMBO_POINTS) - -- Unlike the old system, we don't schedule a refresh here - we wait for the proc - CleveRoids.lastFerociousBiteTime = GetTime() - CleveRoids.lastFerociousBiteTargetGUID = targetGUID - CleveRoids.lastFerociousBiteTargetName = targetName - CleveRoids.lastFerociousBiteSpellID = spellID - - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cffff00ff[Carnage]|r Tracking Ferocious Bite on %s (waiting for proc detection)", - targetName or "Unknown") - ) - end - end - end - -- SHAMAN MOLTEN BLAST: Track for Flame Shock refresh detection (TWoW Custom) -- Credits: Avitasia / Cursive addon if CleveRoids.MoltenBlastSpellIDs and CleveRoids.MoltenBlastSpellIDs[spellID] then @@ -4447,70 +4118,11 @@ ev:SetScript("OnEvent", function() timestamp = GetTime() } - -- DRUID CARNAGE TALENT: Save Rip cast duration for later refresh by Ferocious Bite - if CleveRoids.RipSpellIDs and CleveRoids.RipSpellIDs[spellID] then - -- Clear any stale Carnage duration override for this spell - -- New cast should use its own duration, not old Carnage refresh duration - if CleveRoids.carnageDurationOverrides and CleveRoids.carnageDurationOverrides[spellID] then - CleveRoids.carnageDurationOverrides[spellID] = nil - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cff888888[Carnage]|r Cleared stale Rip override (new cast replaces)") - ) - end - end - - if CleveRoids.lastRipCast then - CleveRoids.lastRipCast.spellID = spellID - CleveRoids.lastRipCast.duration = duration - CleveRoids.lastRipCast.targetGUID = targetGUID - CleveRoids.lastRipCast.comboPoints = comboPoints or 0 - CleveRoids.lastRipCast.timestamp = GetTime() - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cff00ff00[Carnage]|r Saved Rip cast: %ds duration (%d CP) on target %s", - duration, comboPoints or 0, targetName or "Unknown") - ) - end - end - end - -- NOTE: Bleed immunity detection is now handled in the delayed pending debuff -- processing system (see pendingPersonalDebuffs OnUpdate handler above). -- This prevents false positives from checking UnitDebuff() immediately after -- UNIT_CASTEVENT, before the server has synced the debuff to the client. - -- CARNAGE TALENT: Save Rake cast data for potential Ferocious Bite refresh - -- Verification that the bleed landed happens in the delayed tracking system - local isRakeSpell = CleveRoids.RakeSpellIDs and CleveRoids.RakeSpellIDs[spellID] - if isRakeSpell then - -- Clear any stale Carnage duration override for this spell - -- New cast should use its own duration, not old Carnage refresh duration - if CleveRoids.carnageDurationOverrides and CleveRoids.carnageDurationOverrides[spellID] then - CleveRoids.carnageDurationOverrides[spellID] = nil - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cff888888[Carnage]|r Cleared stale Rake override (new cast replaces)") - ) - end - end - - if CleveRoids.lastRakeCast then - CleveRoids.lastRakeCast.spellID = spellID - CleveRoids.lastRakeCast.duration = duration - CleveRoids.lastRakeCast.targetGUID = targetGUID - CleveRoids.lastRakeCast.comboPoints = comboPoints or 0 - CleveRoids.lastRakeCast.timestamp = GetTime() - CleveRoids.lastRakeCast.pending = true -- Mark as pending verification - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cff00ff00[Carnage]|r Saved Rake cast (pending verification): %ds duration (%d CP) on target %s", - duration, comboPoints or 0, targetName or "Unknown") - ) - end - end - end - -- ALWAYS set up learning for combo spells (even if we have calculated duration) if comboPoints then lib.learnCastTimers[targetGUID] = lib.learnCastTimers[targetGUID] or {} @@ -4955,19 +4567,6 @@ ev:SetScript("OnEvent", function() -- Nampower fallback: Cast-complete features from UNIT_CASTEVENT CAST -- Only needed when SuperWoW is not available and spell hit the target if isOurs and not CleveRoids.hasSuperwow and numHit > 0 then - -- DRUID CARNAGE TALENT: Track Ferocious Bite cast for proc detection - if CleveRoids.FerociousBiteSpellIDs and CleveRoids.FerociousBiteSpellIDs[spellId] then - local _, _, _, _, rank = GetTalentInfo(2, 17) - local carnageRank = tonumber(rank) or 0 - if carnageRank >= 1 then - local targetName = lib.guidToName[targetGuid] or UnitName("target") or "Unknown" - CleveRoids.lastFerociousBiteTime = GetTime() - CleveRoids.lastFerociousBiteTargetGUID = targetGuid - CleveRoids.lastFerociousBiteTargetName = targetName - CleveRoids.lastFerociousBiteSpellID = spellId - end - end - -- SHAMAN MOLTEN BLAST: Track for Flame Shock refresh detection if CleveRoids.MoltenBlastSpellIDs and CleveRoids.MoltenBlastSpellIDs[spellId] then CleveRoids.lastMoltenBlastTime = GetTime() @@ -5017,20 +4616,6 @@ ev:SetScript("OnEvent", function() }) end - -- DRUID CARNAGE: Save Rip cast data for potential Ferocious Bite refresh - if CleveRoids.RipSpellIDs and CleveRoids.RipSpellIDs[spellId] then - if CleveRoids.carnageDurationOverrides and CleveRoids.carnageDurationOverrides[spellId] then - CleveRoids.carnageDurationOverrides[spellId] = nil - end - end - - -- CARNAGE: Save Rake cast data for potential Ferocious Bite refresh - if CleveRoids.RakeSpellIDs and CleveRoids.RakeSpellIDs[spellId] then - if CleveRoids.carnageDurationOverrides and CleveRoids.carnageDurationOverrides[spellId] then - CleveRoids.carnageDurationOverrides[spellId] = nil - end - end - -- Track last player cast for miss/dodge/parry removal lib.lastPlayerCast = { spellID = spellId, @@ -5148,37 +4733,6 @@ ev:SetScript("OnEvent", function() end end - -- Save Rip cast data for Ferocious Bite Carnage refresh - if CleveRoids.RipSpellIDs and CleveRoids.RipSpellIDs[spellId] and CleveRoids.lastRipCast then - CleveRoids.lastRipCast.spellID = spellId - CleveRoids.lastRipCast.duration = debuffDuration - CleveRoids.lastRipCast.targetGUID = targetGuid - CleveRoids.lastRipCast.comboPoints = debuffComboPoints or 0 - CleveRoids.lastRipCast.timestamp = GetTime() - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cff00ff00[SPELL_GO Carnage]|r Saved Rip cast: %ds duration (%d CP) on target %s", - debuffDuration, debuffComboPoints or 0, targetName) - ) - end - end - - -- Save Rake cast data for Ferocious Bite Carnage refresh - if CleveRoids.RakeSpellIDs and CleveRoids.RakeSpellIDs[spellId] and CleveRoids.lastRakeCast then - CleveRoids.lastRakeCast.spellID = spellId - CleveRoids.lastRakeCast.duration = debuffDuration - CleveRoids.lastRakeCast.targetGUID = targetGuid - CleveRoids.lastRakeCast.comboPoints = debuffComboPoints or 0 - CleveRoids.lastRakeCast.timestamp = GetTime() - CleveRoids.lastRakeCast.pending = true - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cff00ff00[SPELL_GO Carnage]|r Saved Rake cast (pending verification): %ds duration (%d CP) on target %s", - debuffDuration, debuffComboPoints or 0, targetName) - ) - end - end - -- Name-based combo tracking for pfUI compatibility if debuffComboPoints and debuffComboPoints > 0 and CleveRoids.ComboPointTracking then local baseName = CleveRoids.StripRank(spellName) @@ -5644,41 +5198,6 @@ evLearn:SetScript("OnEvent", function() end if spellName and targetName then - -- CARNAGE: Clear Ferocious Bite tracking if it was dodged/parried/blocked - -- This prevents false proc detection in edge cases - if CleveRoids.lastFerociousBiteTime then - -- Check if the failed spell is Ferocious Bite - local isFerociousBite = false - if CleveRoids.FerociousBiteSpellIDs then - for biteSpellID, _ in pairs(CleveRoids.FerociousBiteSpellIDs) do - local biteName = C_Spell.GetSpellName(biteSpellID) - if biteName then - biteName = CleveRoids.StripRank(biteName) - local messageSpellName = CleveRoids.StripRank(spellName) - if lower(biteName) == lower(messageSpellName) then - isFerociousBite = true - break - end - end - end - end - - if isFerociousBite then - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cffff00ff[Carnage]|r Ferocious Bite avoided by %s - clearing tracking", - targetName or "Unknown") - ) - end - - -- Clear Ferocious Bite tracking (no proc possible since it was avoided) - CleveRoids.lastFerociousBiteTime = nil - CleveRoids.lastFerociousBiteTargetGUID = nil - CleveRoids.lastFerociousBiteTargetName = nil - CleveRoids.lastFerociousBiteSpellID = nil - end - end - -- PERSONAL DEBUFFS: Cancel pending tracking if spell was dodged/parried/blocked if lib.pendingPersonalDebuffs then local messageSpellName = CleveRoids.StripRank(spellName) @@ -6176,11 +5695,6 @@ CleveRoids.talentModifiers[8983] = { tab = 2, id = 4, talent = "Brutal Impact", -- The BLEED portion (triggered spell IDs 9007, 9824, 9826) is NOT affected by Brutal Impact -- We track the bleed for immunity detection, not the stun, so no talent modifiers needed here --- NOTE: Carnage talent (Tab 2, ID 17) is NOT a duration modifier! --- Carnage is a refresh mechanic: When Ferocious Bite procs Carnage, it refreshes Rip/Rake to original duration --- Proc detection: Combo points stay at 1 after FB instead of dropping to 0 --- This is handled in ComboPointTracker.lua via PLAYER_COMBO_POINTS event - -- WARRIOR talent modifiers -- Booming Voice: Increases duration of Battle Shout and Demoralizing Shout by 12% per rank (5 ranks max) -- Talent Position: Tab 2 (Fury), Talent 1