diff --git a/ComboPointTracker.lua b/ComboPointTracker.lua index 1169daa..7dafc61 100644 --- a/ComboPointTracker.lua +++ b/ComboPointTracker.lua @@ -16,8 +16,10 @@ CleveRoids.ComboPointTracking = CleveRoids.ComboPointTracking or {} CleveRoids_ComboDurations = CleveRoids_ComboDurations or {} -- Storage for last Rip cast (for Carnage talent mechanic) --- Carnage talent: Ferocious Bite at 5 CP refreshes Rip and Rake back to their original duration +-- 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, @@ -56,7 +58,8 @@ CleveRoids.ComboScalingSpellsByID = { } -- Ferocious Bite spell IDs (for Carnage talent mechanic) --- Carnage talent: Ferocious Bite at 5 CP refreshes Rip and Rake back to their original duration +-- 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 CleveRoids.FerociousBiteSpellIDs = { [22557] = true, -- Rank 1 @@ -67,7 +70,7 @@ CleveRoids.FerociousBiteSpellIDs = { [31018] = true, -- Rank 6 } --- Rip spell IDs (for Carnage talent - refreshed by Ferocious Bite at 5 CP) +-- Rip spell IDs (for Carnage talent - refreshed when Carnage procs) CleveRoids.RipSpellIDs = { [1079] = true, -- Rank 1 [9492] = true, -- Rank 2 @@ -77,7 +80,7 @@ CleveRoids.RipSpellIDs = { [9896] = true, -- Rank 6 } --- Rake spell IDs (for Carnage talent - refreshed by Ferocious Bite at 5 CP) +-- Rake spell IDs (for Carnage talent - refreshed when Carnage procs) CleveRoids.RakeSpellIDs = { [1822] = true, -- Rank 1 [1823] = true, -- Rank 2 @@ -85,6 +88,102 @@ CleveRoids.RakeSpellIDs = { [9904] = true, -- Rank 4 } +-- ============================================================================= +-- SHAMAN: Molten Blast → Flame Shock Refresh (TWoW Custom) +-- When Molten Blast hits, it refreshes Flame Shock duration on the target +-- Detection: Monitor combat log for Molten Blast damage, then refresh Flame Shock +-- ============================================================================= +CleveRoids.MoltenBlastSpellIDs = { + [36916] = true, -- Rank 1 + [36917] = true, -- Rank 2 + [36918] = true, -- Rank 3 + [36919] = true, -- Rank 4 + [36920] = true, -- Rank 5 + [36921] = true, -- Rank 6 +} + +CleveRoids.FlameShockSpellIDs = { + [8050] = true, -- Rank 1 + [8052] = true, -- Rank 2 + [8053] = true, -- Rank 3 + [10447] = true, -- Rank 4 + [10448] = true, -- Rank 5 + [29228] = true, -- Rank 6 +} + +-- ============================================================================= +-- WARLOCK: Conflagrate → Immolate Duration Reduction +-- When Conflagrate is cast, it reduces Immolate duration by 3 seconds +-- ============================================================================= +CleveRoids.ConflagrateSpellIDs = { + [17962] = true, -- Rank 1 + [18930] = true, -- Rank 2 + [18931] = true, -- Rank 3 + [18932] = true, -- Rank 4 +} + +CleveRoids.ImmolateSpellIDs = { + [348] = true, -- Rank 1 + [707] = true, -- Rank 2 + [1094] = true, -- Rank 3 + [2941] = true, -- Rank 4 + [11665] = true, -- Rank 5 + [11667] = true, -- Rank 6 + [11668] = true, -- Rank 7 + [25309] = true, -- Rank 8 +} + +-- ============================================================================= +-- WARLOCK: Dark Harvest Duration Acceleration (TWoW Custom) +-- Channeled spell that accelerates DoT tick rate by 30% while channeling +-- Complex tracking: debuff expires 30% faster while Dark Harvest is active +-- ============================================================================= +CleveRoids.DarkHarvestSpellIDs = { + [52550] = true, -- Rank 1 + [52551] = true, -- Rank 2 + [52552] = true, -- Rank 3 +} + +-- ============================================================================= +-- DRUID: Rake Debuff Cap Boss Whitelist +-- These bosses are likely to hit the 48 debuff cap, causing Rake to get pushed off +-- For mobs NOT in this list, we verify Rake is actually on the target before tracking +-- Prevents false tracking when debuffs get pushed off at cap +-- ============================================================================= +CleveRoids.MobsThatBleed = { + -- Karazhan Crypts / TWoW Custom + ["0xF13000F1F3276A33"] = true, -- Keeper Gnarlmoon + ["0xF13000F1FA276A32"] = true, -- Ley-Watcher Incantagos + ["0xF13000EA3F276C05"] = true, -- King + ["0xF13000EA31279058"] = true, -- Queen + ["0xF13000EA43276C06"] = true, -- Bishop + ["0xF13000EA44279044"] = true, -- Pawn + ["0xF13000EA42276C07"] = true, -- Rook + ["0xF13000EA4D05DA44"] = true, -- Sanv Tas'dal + ["0xF13000EA57276C04"] = true, -- Kruul + ["0xF130016C95276DAF"] = true, -- Mephistroth + + -- Naxxramas + ["0xF130003E5401591A"] = true, -- Anub'Rekhan + ["0xF130003E510159B0"] = true, -- Grand Widow Faerlina + ["0xF130003E500159A3"] = true, -- Maexxna + ["0xF130003EBD01598C"] = true, -- Razuvious + ["0xF130003EBC01599F"] = true, -- Gothik + ["0xF130003EBF015AB2"] = true, -- Zeliek + ["0xF130003EBE015AB3"] = true, -- Mograine + ["0xF130003EC1015AB1"] = true, -- Blaumeux + ["0xF130003EC0015AB0"] = true, -- Thane + ["0xF130003E52015824"] = true, -- Noth + ["0xF130003E4001588D"] = true, -- Heigan + ["0xF130003E8B0158A2"] = true, -- Loatheb + ["0xF130003E9C0158EA"] = true, -- Patchwerk + ["0xF130003E3B0158EF"] = true, -- Grobbulus + ["0xF130003E3C0158F0"] = true, -- Gluth + ["0xF130003E380159A0"] = true, -- Thaddius + ["0xF130003E75015AB4"] = true, -- Sapphiron + ["0xF130003E76015AED"] = true, -- Kel'Thuzad +} + -- Legacy name-based table for backwards compatibility CleveRoids.ComboScalingSpells = { ["Rupture"] = { base_duration = 8, increment = 2, all_ranks = true }, @@ -652,6 +751,55 @@ 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 9a65af1..bd8d58a 100644 --- a/Core.lua +++ b/Core.lua @@ -4240,6 +4240,26 @@ function CleveRoids.Frame:SPELLCAST_CHANNEL_STOP() CleveRoids.CurrentSpell.spellName = "" CleveRoids.CurrentSpell.castingSpellId = nil + -- WARLOCK DARK HARVEST: Mark channeling as ended + -- Credits: Pepopo / Cursive addon + if CleveRoids.darkHarvestData and CleveRoids.darkHarvestData.isActive then + CleveRoids.darkHarvestData.isActive = false + CleveRoids.darkHarvestData.endTime = GetTime() + + -- Apply Dark Harvest end to all DoTs on target (finalizes reduction) + if CleveRoids.libdebuff and CleveRoids.libdebuff.ApplyDarkHarvestEnd then + CleveRoids.libdebuff.ApplyDarkHarvestEnd(CleveRoids.darkHarvestData.targetGUID) + end + + if CleveRoids.debug then + local activeTime = CleveRoids.darkHarvestData.endTime - CleveRoids.darkHarvestData.startTime + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cff9482c9[Dark Harvest]|r Channel ended after %.1fs (DoT acceleration stopped)", + activeTime) + ) + end + end + -- Force immediate action update CleveRoids.TestForAllActiveActions() end diff --git a/README.md b/README.md index 8c499a1..f1551b9 100644 --- a/README.md +++ b/README.md @@ -273,6 +273,7 @@ Macro 1 shows Macro 2's icon when out of combat: --- ## Debuff Timer System +*Credits: yani9o* Built-in debuff tracking using SuperWoW's advanced features. @@ -370,13 +371,58 @@ Rupture with 5 CP and Taste for Blood 3/3: 3. Talent: 16s + (3×2) = **22s final** ### Carnage (Druid - TWoW Custom) -Ferocious Bite at 5 CP refreshes Rip/Rake to original duration. +*Credits: Pepopo / Cursive addon* + +When Ferocious Bite procs Carnage, it refreshes Rip/Rake to their original duration. + +**Talent Details:** +- Rank 1: 10% chance per combo point to proc +- Rank 2: 20% chance per combo point to proc + +**How Detection Works:** +- Uses Cursive-style proc detection via PLAYER_COMBO_POINTS event +- After Ferocious Bite, combo points should drop to 0 +- If Carnage procs, combo points will be 1 (the Carnage-granted point) +- Detection triggers only when the proc actually occurs **Requirements:** -- Carnage talent rank 2+ -- Ferocious Bite with exactly 5 CP +- Carnage talent (any rank) - Rip/Rake active on target +### Rake Debuff Cap Verification (Druid) +*Credits: Pepopo / Cursive addon* + +In high-debuff scenarios (Naxxramas, etc.), Rake may get pushed off the target at the 48 debuff cap. For non-whitelisted mobs, the addon verifies Rake is actually present on the target before tracking. + +**Whitelisted Bosses:** +- All Naxxramas bosses +- Karazhan Crypts bosses +- Kruul, Mephistroth + +### Molten Blast → Flame Shock Refresh (Shaman - TWoW Custom) +*Credits: Pepopo / Cursive addon* + +When Molten Blast hits a target, it refreshes any active Flame Shock to its full duration. + +**How Detection Works:** +- Monitors combat log for Molten Blast damage +- On hit confirmation, resets Flame Shock timer to full duration + +### Conflagrate → Immolate Reduction (Warlock) +*Credits: Pepopo / Cursive addon* + +When Conflagrate is cast, it reduces the remaining Immolate duration by 3 seconds. + +### Dark Harvest Duration Acceleration (Warlock - TWoW Custom) +*Credits: Pepopo / Cursive addon* + +Dark Harvest is a channeled spell that accelerates all DoT tick rates on the target by 30%. + +**How It Works:** +- While channeling Dark Harvest, all DoTs on the target tick 30% faster +- Debuff timers account for this acceleration +- When channel ends, the acceleration stops but accumulated reduction is preserved + --- ## Equipment Modifiers diff --git a/Utility.lua b/Utility.lua index 85c4c09..8095597 100644 --- a/Utility.lua +++ b/Utility.lua @@ -1702,18 +1702,88 @@ local function SeedUnit(unit) end end --- Carnage pending refresh system --- Stores pending Carnage refreshes to be applied after verifying the Ferocious Bite hit --- Format: { timestamp = GetTime(), targetGUID = guid, targetName = name, biteSpellID = id } -lib.pendingCarnageRefresh = nil +-- 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: Pepopo / Cursive addon +-- Dark Harvest is a channeled spell that accelerates DoT tick rate by 30% +-- While channeling, DoTs on the target expire 30% faster +-- ============================================================================= + +-- Calculate Dark Harvest reduction for a debuff record +-- Returns the amount of time to subtract from remaining duration +function lib.GetDarkHarvestReduction(rec) + if not rec or not rec.dhStartTime then + return 0 + end + + local endTime = rec.dhEndTime or GetTime() + local dhActiveTime = endTime - rec.dhStartTime + if dhActiveTime > 0 then + return dhActiveTime * 0.3 -- 30% acceleration + end + return 0 +end + +-- Track Dark Harvest start for all DoTs on target +function lib.ApplyDarkHarvestStart(targetGUID) + if not lib.objects[targetGUID] then return end + + local now = GetTime() + for spellID, rec in pairs(lib.objects[targetGUID]) do + if type(rec) == "table" and rec.duration and rec.start then + -- Only affect DoTs (debuffs with duration > 0 that tick) + -- Skip if already has dhStartTime (avoid double-tracking) + if not rec.dhStartTime then + rec.dhStartTime = now + rec.dhEndTime = nil -- Clear any previous end time + end + end + end + + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cff9482c9[Dark Harvest]|r Started accelerating DoTs on %s", + lib.guidToName[targetGUID] or "Unknown") + ) + end +end + +-- Track Dark Harvest end for all DoTs on target +function lib.ApplyDarkHarvestEnd(targetGUID) + if not lib.objects[targetGUID] then return end + + local now = GetTime() + for spellID, rec in pairs(lib.objects[targetGUID]) do + if type(rec) == "table" and rec.dhStartTime and not rec.dhEndTime then + rec.dhEndTime = now + end + end +end + +-- Get time remaining for a debuff, accounting for Dark Harvest acceleration +function lib.GetTimeRemainingWithDarkHarvest(rec) + if not rec or not rec.duration or not rec.start then + return 0 + end + + local baseRemaining = rec.duration + rec.start - GetTime() + local dhReduction = lib.GetDarkHarvestReduction(rec) + + return baseRemaining - dhReduction +end -- Personal debuff pending tracking system -- Stores personal debuffs to be added after 0.5s delay (to verify they weren't dodged/parried/blocked) -- Format: { [index] = { timestamp = GetTime(), targetGUID = guid, targetName = name, spellID = id, duration = X, comboPoints = CP } } lib.pendingPersonalDebuffs = lib.pendingPersonalDebuffs or {} --- Function to apply Carnage refresh (extracted for delayed execution) -local function ApplyCarnageRefresh(targetGUID, targetName, biteSpellID) +-- Function to apply Carnage refresh (exposed for ComboPointTracker to call on proc detection) +function lib.ApplyCarnageRefresh(targetGUID, targetName, biteSpellID) if CleveRoids.debug then DEFAULT_CHAT_FRAME:AddMessage( string.format("|cffff00ff[Carnage]|r ApplyCarnageRefresh called for %s", targetName or "Unknown") @@ -1899,31 +1969,10 @@ local function ApplyCarnageRefresh(targetGUID, targetName, biteSpellID) end end --- Frame for delayed Carnage refresh and personal debuff tracking +-- Frame for delayed personal debuff tracking and judgement scanning +-- NOTE: Carnage refresh is now handled via PLAYER_COMBO_POINTS event in ComboPointTracker.lua local delayedTrackingFrame = CreateFrame("Frame", "CleveRoidsDelayedTrackingFrame", UIParent) delayedTrackingFrame:SetScript("OnUpdate", function() - -- Process pending Carnage refresh - if lib.pendingCarnageRefresh then - local pending = lib.pendingCarnageRefresh - local elapsed = GetTime() - pending.timestamp - - -- Apply refresh after 0.3 second delay (enough time for dodge/parry/block messages) - if elapsed >= 0.3 then - -- Apply the Carnage refresh - ApplyCarnageRefresh(pending.targetGUID, pending.targetName, pending.biteSpellID) - - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cffff00ff[Carnage]|r Applied delayed refresh for Ferocious Bite on %s", - pending.targetName or "Unknown") - ) - end - - -- Clear the pending refresh - lib.pendingCarnageRefresh = nil - end - end - -- Process pending judgement scans to detect actual debuff IDs if lib.pendingJudgements then local toRemove = {} @@ -2058,6 +2107,32 @@ ev:SetScript("OnEvent", function() end end end + + -- WARLOCK DARK HARVEST: Track channeling for DoT acceleration (TWoW Custom) + -- Credits: Pepopo / Cursive addon + -- Dark Harvest accelerates DoT tick rate by 30% while channeling + if eventType == "CHANNEL" and CleveRoids.DarkHarvestSpellIDs and CleveRoids.DarkHarvestSpellIDs[spellID] then + -- Get channel duration from tooltip or use base duration (8 seconds) + local channelDuration = 8 -- Base Dark Harvest duration + + CleveRoids.darkHarvestData = { + targetGUID = targetGUID, + spellID = spellID, + startTime = GetTime(), + channelDuration = channelDuration, + isActive = true + } + + -- Apply Dark Harvest acceleration to all existing DoTs on target + lib.ApplyDarkHarvestStart(targetGUID) + + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cff9482c9[Dark Harvest]|r Started channeling on %s (DoTs will tick 30%% faster)", + lib.guidToName[targetGUID] or "Unknown") + ) + end + end end end @@ -2065,20 +2140,16 @@ ev:SetScript("OnEvent", function() local _, playerGUID = UnitExists("player") if casterGUID == playerGUID and targetGUID then - -- DRUID CARNAGE TALENT: Check if this is Ferocious Bite with 5 CP - -- Schedule a delayed refresh to allow dodge/parry/block detection + -- 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 - -- Get combo points used (captured before consumption) - local biteComboPoints = CleveRoids.lastComboPoints or 0 - - -- Check if player has Carnage talent at rank 2 + -- Check if player has Carnage talent (any rank) -- Carnage: Tab 2 (Feral Combat), Talent 17 - local carnageRank = 0 local _, _, _, _, rank = GetTalentInfo(2, 17) - carnageRank = tonumber(rank) or 0 + local carnageRank = tonumber(rank) or 0 - -- Carnage 2/2+: Ferocious Bite at 5 CP schedules a refresh (applied only if spell hits) - if carnageRank >= 2 and biteComboPoints == 5 then + if carnageRank >= 1 then local targetName = lib.guidToName[targetGUID] if not targetName then local _, currentTargetGUID = UnitExists("target") @@ -2091,23 +2162,60 @@ ev:SetScript("OnEvent", function() end end - -- Schedule the Carnage refresh (will be applied after 0.5s if not cancelled) - lib.pendingCarnageRefresh = { - timestamp = GetTime(), - targetGUID = targetGUID, - targetName = targetName, - biteSpellID = spellID - } + -- 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 Scheduled refresh for Ferocious Bite on %s (will apply if hit)", + 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: Pepopo / Cursive addon + if CleveRoids.MoltenBlastSpellIDs and CleveRoids.MoltenBlastSpellIDs[spellID] then + CleveRoids.lastMoltenBlastTime = GetTime() + CleveRoids.lastMoltenBlastTargetGUID = targetGUID + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cff0070dd[Molten Blast]|r Tracking cast on %s (waiting for hit confirmation)", + lib.guidToName[targetGUID] or "Unknown") + ) + end + end + + -- WARLOCK CONFLAGRATE: Reduces Immolate duration by 3 seconds + -- Credits: Pepopo / Cursive addon + if CleveRoids.ConflagrateSpellIDs and CleveRoids.ConflagrateSpellIDs[spellID] then + -- Find active Immolate on target and reduce its duration + if lib.objects[targetGUID] then + for immolateID, _ in pairs(CleveRoids.ImmolateSpellIDs or {}) do + local rec = lib.objects[targetGUID][immolateID] + if rec and rec.duration and rec.start then + local remaining = rec.duration + rec.start - GetTime() + if remaining > 0 then + -- Reduce duration by 3 seconds + rec.duration = rec.duration - 3 + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cff9482c9[Conflagrate]|r Reduced Immolate duration by 3s (now %.1fs remaining)", + rec.duration + rec.start - GetTime()) + ) + end + break + end + end + end + end + end + -- Check if this is a combo point scaling spell first local duration = nil local comboPoints = nil @@ -2340,8 +2448,35 @@ ev:SetScript("OnEvent", function() end -- DRUID CARNAGE TALENT: Save Rake cast duration for later refresh by Ferocious Bite + -- Also includes debuff cap verification (Credits: Pepopo / Cursive addon) if CleveRoids.RakeSpellIDs and CleveRoids.RakeSpellIDs[spellID] then - if CleveRoids.lastRakeCast then + -- Check if mob is in bleed whitelist (high-debuff scenarios) + local isWhitelisted = CleveRoids.MobsThatBleed and CleveRoids.MobsThatBleed[targetGUID] + local rakeVerified = true + + if not isWhitelisted and CleveRoids.hasSuperwow then + -- Verify Rake is actually on the target (may have been pushed off at debuff cap) + rakeVerified = false + for slot = 1, 48 do + local _, _, _, debuffSpellID = UnitDebuff(targetGUID, slot) + if not debuffSpellID then + if slot <= 16 then break end -- Regular debuffs are dense, overflow continues on nil + elseif debuffSpellID == spellID then + rakeVerified = true + break + end + end + + if not rakeVerified and CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cffff6600[Rake Debuff Cap]|r Rake not found on %s - likely pushed off at debuff cap", + targetName or "Unknown") + ) + end + end + + -- Only save Rake cast data if verified (or can't verify) + if rakeVerified and CleveRoids.lastRakeCast then CleveRoids.lastRakeCast.spellID = spellID CleveRoids.lastRakeCast.duration = duration CleveRoids.lastRakeCast.targetGUID = targetGUID @@ -2429,8 +2564,9 @@ evLearn:SetScript("OnEvent", function() end if spellName and targetName then - -- CARNAGE: Cancel pending refresh if Ferocious Bite was dodged/parried/blocked - if lib.pendingCarnageRefresh 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 @@ -2448,27 +2584,18 @@ evLearn:SetScript("OnEvent", function() end if isFerociousBite then - -- Cancel the pending Carnage refresh - if CleveRoids.debug and lib.pendingCarnageRefresh then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cffff00ff[Carnage]|r Cancelling pending refresh (time since cast: %.2fs)", - GetTime() - lib.pendingCarnageRefresh.timestamp) - ) - end - - lib.pendingCarnageRefresh = nil - - -- Clear any stale Carnage duration overrides to prevent pfUI from using old data - if CleveRoids.carnageDurationOverrides then - CleveRoids.carnageDurationOverrides = {} - end - if CleveRoids.debug then DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cffff00ff[Carnage]|r Cancelled refresh - Ferocious Bite was avoided by %s", + 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 @@ -2595,6 +2722,50 @@ evLearn:SetScript("OnEvent", function() end end + -- SHAMAN MOLTEN BLAST: Check for damage hit to trigger Flame Shock refresh (TWoW Custom) + -- Credits: Pepopo / Cursive addon + -- Pattern: "Your Molten Blast hits/crits X for Y Fire damage" + if CleveRoids.lastMoltenBlastTime and CleveRoids.lastMoltenBlastTargetGUID then + local timeSinceCast = GetTime() - CleveRoids.lastMoltenBlastTime + -- Check within 1 second of cast + if timeSinceCast < 1.0 then + -- Check if message mentions Molten Blast damage + if find(message, "Molten Blast") and find(message, "Fire damage") then + local targetGUID = CleveRoids.lastMoltenBlastTargetGUID + + -- Find active Flame Shock on target and refresh it + if lib.objects[targetGUID] then + for flameShockID, _ in pairs(CleveRoids.FlameShockSpellIDs or {}) do + local rec = lib.objects[targetGUID][flameShockID] + -- Only refresh player's own Flame Shock, not other shamans' + if rec and rec.duration and rec.start and rec.caster == "player" then + local remaining = rec.duration + rec.start - GetTime() + if remaining > 0 then + -- Refresh: reset start time to now + rec.start = GetTime() + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cff0070dd[Molten Blast]|r Refreshed Flame Shock to %.1fs on %s", + rec.duration, lib.guidToName[targetGUID] or "Unknown") + ) + end + break + end + end + end + end + + -- Clear tracking + CleveRoids.lastMoltenBlastTime = nil + CleveRoids.lastMoltenBlastTargetGUID = nil + end + else + -- Time window expired + CleveRoids.lastMoltenBlastTime = nil + CleveRoids.lastMoltenBlastTargetGUID = nil + end + end + elseif event == "RAW_COMBATLOG" then local raw = arg2 -- PERFORMANCE: Quick length check before string search @@ -2964,8 +3135,9 @@ CleveRoids.talentModifiers[9823] = { tab = 2, id = 4, talent = "Brutal Impact", CleveRoids.talentModifiers[9827] = { tab = 2, id = 4, talent = "Brutal Impact", modifier = function(base, rank) return base + (rank * 0.5) end } -- Pounce Rank 3 -- NOTE: Carnage talent (Tab 2, ID 17) is NOT a duration modifier! --- Carnage is a refresh mechanic: Ferocious Bite at 5 CP refreshes Rip/Rake to their original duration --- This is handled separately in the Carnage refresh logic (lines 1125-1192) +-- 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)