enhance immunity tracking with SPELL_GO cross-reference and improve combo point capture

Use SPELL_GO hit/miss data to skip redundant debuff scanning for CC/shared
immunity verification. Add stale entry cleanup (>2s) for pending debuffs.
Capture combo points at SPELL_CAST_EVENT (client-side, pre-server) for
reliable finisher duration tracking. Add SPELL_GO reactive ability detection
for yellow attack dodges replacing combat log text parsing.
This commit is contained in:
Jrc13245
2026-02-07 12:54:34 -05:00
parent dd0f6572d4
commit 4c00ade625
4 changed files with 297 additions and 37 deletions
+12 -2
View File
@@ -467,8 +467,18 @@ function CleveRoids.TrackComboPointCastByID(spellID, targetGUID)
-- If combo points are 0, try multiple fallback sources
if comboPoints == 0 then
-- First, try lastComboPoints (don't reset immediately - let it persist)
if CleveRoids.lastComboPoints > 0 then
-- PRIMARY: Check SPELL_CAST_EVENT capture (most reliable - client-side pre-server)
local pending = CleveRoids.pendingCasts and CleveRoids.pendingCasts[spellID]
if pending and pending.comboPoints and pending.comboPoints > 0 then
comboPoints = pending.comboPoints
if CleveRoids.debug then
DEFAULT_CHAT_FRAME:AddMessage(
string.format("|cff00ff88[TrackComboByID]|r Using SPELL_CAST_EVENT CP: %d for spell ID %d",
comboPoints, spellID)
)
end
-- Fallback: lastComboPoints (don't reset immediately - let it persist)
elseif CleveRoids.lastComboPoints > 0 then
comboPoints = CleveRoids.lastComboPoints
-- Don't reset here - let it persist for multiple events
if CleveRoids.debug then
+7 -1
View File
@@ -867,10 +867,16 @@ local HITINFO_GLANCING = 16384 -- 0x4000
local HITINFO_CRUSHING = 32768 -- 0x8000
local HITINFO_LEFTSWING = 4 -- 0x4 (Off-hand attack)
-- VictimState values
-- VictimState values (from AUTO_ATTACK / SPELL_GO events)
local VICTIMSTATE_UNAFFECTED = 0 -- Generic miss (seen with HITINFO_MISS)
local VICTIMSTATE_NORMAL = 1 -- Hit landed
local VICTIMSTATE_DODGE = 2
local VICTIMSTATE_PARRY = 3
local VICTIMSTATE_INTERRUPT = 4
local VICTIMSTATE_BLOCKS = 5
local VICTIMSTATE_EVADES = 6
local VICTIMSTATE_IS_IMMUNE = 7
local VICTIMSTATE_DEFLECTS = 8
-- Aura cap status bitfield
local AURA_CAP_BUFF_FULL = 1
+36
View File
@@ -5533,6 +5533,8 @@ function CleveRoids.Frame:SPELL_CAST_EVENT()
if event == "SPELL_CAST_EVENT" then
local success = arg1
local spellId = arg2
local castType = arg3
local targetGuid = arg4
-- BUGFIX: Update casting state on spell cast events (for [casting] conditional)
if CleveRoids.UpdateCastingState then
@@ -5542,6 +5544,8 @@ function CleveRoids.Frame:SPELL_CAST_EVENT()
if success == 1 then
CleveRoids.lastCastSpell = {
spellId = spellId,
castType = castType,
targetGuid = targetGuid,
timestamp = GetTime()
}
if SpellInfo then
@@ -5550,6 +5554,38 @@ function CleveRoids.Frame:SPELL_CAST_EVENT()
CleveRoids.lastCastSpell.spellName = name
end
end
-- Track pending cast for SPELL_GO correlation (reactive ability detection)
-- Keyed by spellId so concurrent casts don't overwrite each other
CleveRoids.pendingCasts = CleveRoids.pendingCasts or {}
CleveRoids.pendingCasts[spellId] = {
castType = castType,
targetGuid = targetGuid,
timestamp = GetTime(),
comboPoints = nil,
}
-- Capture combo points NOW (before server consumes them)
-- SPELL_CAST_EVENT fires client-side, so CP are guaranteed available
if (CleveRoids.IsComboScalingSpellID and CleveRoids.IsComboScalingSpellID(spellId)) or
(CleveRoids.FerociousBiteSpellIDs and CleveRoids.FerociousBiteSpellIDs[spellId]) then
local cp = CleveRoids.GetComboPoints and CleveRoids.GetComboPoints() or 0
if cp > 0 then
CleveRoids.pendingCasts[spellId].comboPoints = cp
if CleveRoids.debug then
local castSpellName = SpellInfo and SpellInfo(spellId) or "Unknown"
DEFAULT_CHAT_FRAME:AddMessage(
string.format("|cff00ff88[SPELL_CAST_EVENT]|r Captured %d CP for %s (ID:%d)",
cp, castSpellName, spellId)
)
end
end
end
else
-- Cast failed - clear pending entry for this spell
if CleveRoids.pendingCasts then
CleveRoids.pendingCasts[spellId] = nil
end
end
end
end
+242 -34
View File
@@ -3280,7 +3280,17 @@ delayedTrackingFrame:SetScript("OnUpdate", function()
-- Check after delay: 0.4s for hidden CC (no visible debuff), 0.2s for normal CC
-- Hidden CC (e.g., Pounce stun) needs longer delay to wait for "afflicted by" messages
local verifyDelay = pending.isHiddenCC and 0.4 or 0.2
if elapsed >= verifyDelay then
-- Drop stale entries (>2s) without recording immunity - inconclusive due to severe lag
if elapsed > 2.0 then
if debug then
DEFAULT_CHAT_FRAME:AddMessage(
_string_format("|cffff6600[CC Stale]|r Dropping stale CC entry for %s on %s (%.1fs old)",
pending.ccType or "CC", pending.targetName or "Unknown", elapsed)
)
end
-- Don't add to newPendingList - silently discard
elseif elapsed >= verifyDelay then
local ccVerified = false
local totalDebuffs = 0
@@ -3290,10 +3300,24 @@ delayedTrackingFrame:SetScript("OnUpdate", function()
ccVerified = true
end
-- SPELL_GO early-exit: Use definitive hit/miss data when available
-- This is faster and more reliable than debuff scanning, and handles debuff cap correctly
if not ccVerified and pending.spellGoHit then
ccVerified = true
if debug then
DEFAULT_CHAT_FRAME:AddMessage(
_string_format("|cff00ff00[CC Verified via SPELL_GO]|r %s landed on %s - skipping debuff scan",
pending.ccType or "CC", pending.targetName or "Unknown")
)
end
end
-- CC IMMUNITY VERIFICATION: Check if CC effect actually landed
-- Uses hybrid approach: direct spell ID match OR mechanic-based validation
-- (CC debuff IDs often differ from cast IDs, e.g., Pounce cast ≠ Pounce Stun debuff)
if not ccVerified and hasSuperwow and pending.targetGUID then
-- Guard: skip debuff scanning if SPELL_GO already determined outcome
if not ccVerified and not pending.spellGoHit and not pending.spellGoMissed
and hasSuperwow and pending.targetGUID then
-- Skip verification if target is dead (debuffs are removed on death)
if _UnitIsDead(pending.targetGUID) then
ccVerified = true -- Assume CC landed, can't verify on dead target
@@ -3350,6 +3374,13 @@ delayedTrackingFrame:SetScript("OnUpdate", function()
end
end
if not ccVerified and pending.spellGoMissed and debug then
DEFAULT_CHAT_FRAME:AddMessage(
_string_format("|cffff6600[CC Missed via SPELL_GO]|r %s missed on %s - checking immunity",
pending.ccType or "CC", pending.targetName or "Unknown")
)
end
-- If CC didn't land, check if it's immunity or debuff cap
if not ccVerified and not _UnitIsDead(pending.targetGUID) then
-- totalDebuffs already counted above, reuse it
@@ -3392,7 +3423,8 @@ delayedTrackingFrame:SetScript("OnUpdate", function()
-- SPLIT CC SPELLS: Skip immunity recording for spells with physical damage + resistable CC
-- (e.g., Master Strike) - physical damage lands but CC can be resisted independently
local isSplitCCSpell = SPLIT_CC_SPELLS[pending.spellID]
-- spellGoHit: SPELL_GO confirmed hit, so CC was resisted independently (not true immunity)
local isSplitCCSpell = SPLIT_CC_SPELLS[pending.spellID] or pending.spellGoHit
if isSplitCCSpell then
if debug then
local spellNameDebug = pending.spellName or (_SpellInfo(pending.spellID) or "Unknown")
@@ -3485,13 +3517,36 @@ delayedTrackingFrame:SetScript("OnUpdate", function()
)
end
-- Check after 0.2s delay (enough time for server sync)
if elapsed >= 0.2 then
-- Drop stale entries (>2s) without recording immunity - inconclusive due to severe lag
if elapsed > 2.0 then
if debug then
local spellNameDebug = pending.spellID and _SpellInfo(pending.spellID) or "Unknown"
DEFAULT_CHAT_FRAME:AddMessage(
_string_format("|cffff6600[Shared Stale]|r Dropping stale shared entry for %s on %s (%.1fs old)",
spellNameDebug, pending.targetName or "Unknown", elapsed)
)
end
-- Don't add to newPendingList - silently discard
elseif elapsed >= 0.2 then
local debuffVerified = false
local totalDebuffs = 0
-- SPELL_GO early-exit: Use definitive hit/miss data when available
if pending.spellGoHit then
debuffVerified = true
if debug then
local spellNameDebug = _SpellInfo(pending.spellID) or "Shared Debuff"
DEFAULT_CHAT_FRAME:AddMessage(
_string_format("|cff00ff00[Shared Verified via SPELL_GO]|r %s landed on %s - skipping debuff scan",
spellNameDebug, pending.targetName or "Unknown")
)
end
end
-- Skip verification if target is dead (debuffs are removed on death)
if hasSuperwow and pending.targetGUID then
-- Guard: skip debuff scanning if SPELL_GO already determined outcome
if not debuffVerified and not pending.spellGoHit and not pending.spellGoMissed
and hasSuperwow and pending.targetGUID then
if _UnitIsDead(pending.targetGUID) then
-- Target died - can't verify immunity, assume debuff landed
debuffVerified = true
@@ -3545,7 +3600,8 @@ delayedTrackingFrame:SetScript("OnUpdate", function()
-- SPLIT CC SPELLS: Skip immunity recording for spells with physical damage + resistable CC
-- (e.g., Master Strike) - the physical damage lands but CC can be resisted independently
local isSplitCCSpell = SPLIT_CC_SPELLS[pending.spellID] or SPLIT_CC_SPELLS[pending.castSpellID]
-- spellGoHit: SPELL_GO confirmed hit, so debuff was resisted independently (not true immunity)
local isSplitCCSpell = SPLIT_CC_SPELLS[pending.spellID] or SPLIT_CC_SPELLS[pending.castSpellID] or pending.spellGoHit
if isSplitCCSpell then
if debug then
local spellNameDebug = _SpellInfo(pending.castSpellID or pending.spellID) or "Unknown"
@@ -4389,6 +4445,34 @@ ev:SetScript("OnEvent", function()
local _, playerGUID = UnitExists("player")
local isOurs = (casterGuid == playerGUID)
-- Annotate pending CC/shared debuffs with SPELL_GO hit/miss outcome
-- This data is consumed by the OnUpdate verification loop for early-exit paths
if isOurs then
-- Annotate pendingCCDebuffs
for _, pending in ipairs(lib.pendingCCDebuffs) do
if pending and pending.spellID == spellId and pending.targetGUID == targetGuid then
if numHit > 0 then
pending.spellGoHit = true
elseif numMissed > 0 then
pending.spellGoMissed = true
end
break
end
end
-- Annotate pendingSharedDebuffs (match against both castSpellID and spellID)
for _, pending in ipairs(lib.pendingSharedDebuffs) do
if pending and pending.targetGUID == targetGuid
and (pending.castSpellID == spellId or pending.spellID == spellId) then
if numHit > 0 then
pending.spellGoHit = true
elseif numMissed > 0 then
pending.spellGoMissed = true
end
break
end
end
end
-- Spell missed - clear pending, mark as failed, track for immunity detection
if numHit == 0 and numMissed > 0 then
if lib.pendingCasts[targetGuid] then
@@ -4482,14 +4566,26 @@ ev:SetScript("OnEvent", function()
-- time AURA_CAST fires. Store in pendingCasts for AURA_CAST to use.
if isOurs and lib.combopointAbilities and lib.combopointAbilities[spellName] then
-- Capture combo points from multiple sources
local comboPoints = CleveRoids.GetComboPoints and CleveRoids.GetComboPoints() or 0
-- Priority: SPELL_CAST_EVENT (client-side, pre-server) > GetComboPoints > lastComboPoints > ComboPointTracking
local comboPoints = 0
-- If already consumed, use lastComboPoints fallback
-- PRIMARY: Check SPELL_CAST_EVENT capture (most reliable - captured before server consumed CP)
local pending = CleveRoids.pendingCasts and CleveRoids.pendingCasts[spellId]
if pending and pending.comboPoints and pending.comboPoints > 0 then
comboPoints = pending.comboPoints
end
-- Fallback: Try GetComboPoints (may still be available)
if comboPoints == 0 then
comboPoints = CleveRoids.GetComboPoints and CleveRoids.GetComboPoints() or 0
end
-- Fallback: lastComboPoints (from pre-cast hooks or UNIT_CASTEVENT START)
if comboPoints == 0 and CleveRoids.lastComboPoints and CleveRoids.lastComboPoints > 0 then
comboPoints = CleveRoids.lastComboPoints
end
-- Also check ComboPointTracking for recent data (from /cast hook pre-population)
-- Fallback: ComboPointTracking for recent data (from /cast hook pre-population)
if comboPoints == 0 and CleveRoids.ComboPointTracking then
local tracking = CleveRoids.ComboPointTracking[spellName]
if tracking and tracking.combo_points and tracking.combo_points > 0 then
@@ -4551,24 +4647,35 @@ ev:SetScript("OnEvent", function()
-- Handle combo point abilities for our casts
if isOurs and lib.combopointAbilities[spellName] then
-- Priority order for combo points:
-- 1. pendingCasts (captured by SPELL_GO - most reliable)
-- 2. CleveRoids.lastComboPoints (from /cast hook pre-population)
-- 3. GetComboPoints() (might be 0 if already consumed)
-- 1. SPELL_CAST_EVENT capture (client-side, before server consumes CP)
-- 2. pendingCasts from SPELL_GO (captured on spell landing)
-- 3. CleveRoids.lastComboPoints (from pre-cast hooks)
-- 4. GetComboPoints() (might be 0 if already consumed)
local comboPoints = 0
local comboSource = "none"
-- Check pendingCasts first (populated by SPELL_GO)
if lib.pendingCasts[targetGuid] and lib.pendingCasts[targetGuid][spellName] then
-- PRIMARY: Check SPELL_CAST_EVENT capture (most reliable - client-side pre-server)
local castPending = CleveRoids.pendingCasts and CleveRoids.pendingCasts[spellId]
if castPending and castPending.comboPoints and castPending.comboPoints > 0 then
comboPoints = castPending.comboPoints
comboSource = "SPELL_CAST_EVENT"
end
-- Fallback: Check lib.pendingCasts (populated by SPELL_GO)
if comboPoints == 0 and lib.pendingCasts[targetGuid] and lib.pendingCasts[targetGuid][spellName] then
local pending = lib.pendingCasts[targetGuid][spellName]
local age = GetTime() - (pending.capturedAt or 0)
if pending.comboPoints and pending.comboPoints > 0 and age < 2.0 then
comboPoints = pending.comboPoints
comboSource = "SPELL_GO"
-- Clean up after use
lib.pendingCasts[targetGuid][spellName] = nil
end
end
-- Clean up lib.pendingCasts after reading (regardless of which source won)
if lib.pendingCasts[targetGuid] and lib.pendingCasts[targetGuid][spellName] then
lib.pendingCasts[targetGuid][spellName] = nil
end
-- Fallback to lastComboPoints
if comboPoints == 0 and CleveRoids.lastComboPoints and CleveRoids.lastComboPoints > 0 then
comboPoints = CleveRoids.lastComboPoints
@@ -7897,9 +8004,10 @@ function CleveRoids.ParseReactiveCombatLog()
-- Check each reactive ability's trigger patterns
for spellName, config in pairs(reactivePatterns) do
-- Check if this is a known reactive spell (skip hasSpell check - spellbook may not be indexed yet)
-- If player uses [reactive:SpellName], they have the ability
if CleveRoids.reactiveSpells and CleveRoids.reactiveSpells[spellName] then
-- Skip outgoing dodge patterns when SPELL_GO_SELF handles yellow attack miss detection
if CleveRoids.usingSpellGoEvents and config.type == "enemy_dodge" then
-- Overpower handled by SPELL_GO_SELF (hit/miss binary, no text parsing needed)
elseif CleveRoids.reactiveSpells and CleveRoids.reactiveSpells[spellName] then
for _, pattern in ipairs(config.patterns) do
if strfind(message, pattern) then
-- Found a trigger event (works in any stance)
@@ -7908,7 +8016,7 @@ function CleveRoids.ParseReactiveCombatLog()
CleveRoids.SetReactiveProc(spellName, duration, guid)
-- For Overpower (enemy dodge), also update LastSwing so [lastswing:dodge] works
-- This handles ability dodges that AUTO_ATTACK events don't capture
-- (Only reached when SPELL_GO is NOT available - combat log fallback)
if config.type == "enemy_dodge" and CleveRoids.LastSwing then
CleveRoids.LastSwing.timestamp = GetTime()
CleveRoids.LastSwing.victimState = VICTIMSTATE_DODGE_REACTIVE
@@ -7964,10 +8072,19 @@ end
-- Uses native events for dodge/parry/block detection when available.
-- Falls back to combat log parsing for older Nampower versions.
-- VictimState constants from NampowerAPI (copied for performance)
-- VictimState constants (from AUTO_ATTACK / SPELL_GO events)
local VICTIMSTATE_UNAFFECTED = 0 -- Generic miss (seen with HITINFO_MISS)
local VICTIMSTATE_NORMAL = 1 -- Hit landed
local VICTIMSTATE_DODGE = 2
local VICTIMSTATE_PARRY = 3
local VICTIMSTATE_INTERRUPT = 4
local VICTIMSTATE_BLOCKS = 5
local VICTIMSTATE_EVADES = 6
local VICTIMSTATE_IS_IMMUNE = 7
local VICTIMSTATE_DEFLECTS = 8
-- HitInfo constant for SPELL_GO miss tracking
local HITINFO_MISS = 16 -- 0x10
-- Track if we're using Nampower events (set during initialization)
CleveRoids.usingNampowerAutoAttack = false
@@ -8081,12 +8198,36 @@ if GetNampowerVersion then
end
end
-- Always register combat log events as fallback (or primary if no Nampower)
reactiveFrame:RegisterEvent("RAW_COMBATLOG")
reactiveFrame:RegisterEvent("CHAT_MSG_COMBAT_SELF_MISSES")
reactiveFrame:RegisterEvent("CHAT_MSG_SPELL_SELF_DAMAGE")
reactiveFrame:RegisterEvent("CHAT_MSG_COMBAT_CREATURE_VS_SELF_MISSES")
reactiveFrame:RegisterEvent("CHAT_MSG_COMBAT_CREATURE_VS_SELF_HITS")
-- Check for Nampower v2.25+ SPELL_GO events (replaces combat log for yellow attack miss detection)
local hasSpellGoEvents = false
if GetNampowerVersion then
local npMajor, npMinor = GetNampowerVersion()
if npMajor > 2 or (npMajor == 2 and npMinor >= 25) then
hasSpellGoEvents = true
CleveRoids.usingSpellGoEvents = true
reactiveFrame:RegisterEvent("SPELL_GO_SELF")
if CleveRoids.debug then
DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[Nampower]|r Using SPELL_GO events for yellow attack miss detection (v2.25+)")
end
end
end
-- Register combat log events for reactive abilities
-- When SPELL_GO + AUTO_ATTACK are both available, outgoing dodges are handled natively;
-- combat log only needed for incoming avoidance (Riposte/Revenge from enemy abilities)
if hasAutoAttackEvents and hasSpellGoEvents then
reactiveFrame:RegisterEvent("CHAT_MSG_COMBAT_CREATURE_VS_SELF_MISSES")
reactiveFrame:RegisterEvent("CHAT_MSG_COMBAT_CREATURE_VS_SELF_HITS")
else
-- Full combat log fallback
reactiveFrame:RegisterEvent("RAW_COMBATLOG")
reactiveFrame:RegisterEvent("CHAT_MSG_COMBAT_SELF_MISSES")
reactiveFrame:RegisterEvent("CHAT_MSG_SPELL_SELF_DAMAGE")
reactiveFrame:RegisterEvent("CHAT_MSG_COMBAT_CREATURE_VS_SELF_MISSES")
reactiveFrame:RegisterEvent("CHAT_MSG_COMBAT_CREATURE_VS_SELF_HITS")
end
reactiveFrame:SetScript("OnEvent", function()
-- ========================================================================
@@ -8122,13 +8263,80 @@ reactiveFrame:SetScript("OnEvent", function()
end
-- ========================================================================
-- COMBAT LOG PARSING FOR REACTIVE ABILITIES
-- NAMPOWER v2.25+ SPELL_GO EVENTS (yellow attack hit/miss detection)
-- ========================================================================
-- NOTE: This must ALWAYS run, even when Nampower AUTO_ATTACK events are available.
-- AUTO_ATTACK events only fire for white (auto-attack) swings, NOT for yellow
-- (ability) damage like Mortal Strike, Heroic Strike, etc. being dodged/parried.
-- Combat log parsing catches ability dodges that AUTO_ATTACK misses.
-- The AUTO_ATTACK handlers above already `return` early, so no double-processing.
-- Two-step: SPELL_CAST_EVENT (success=1) → SPELL_GO_SELF (hit/miss outcome)
-- Replaces combat log text parsing for outgoing yellow ability misses.
-- SPELL_GO_SELF params: itemId, spellId, casterGuid, targetGuid, castFlags, numTargetsHit, numTargetsMissed
if event == "SPELL_GO_SELF" then
local spellId = arg2
local targetGuid = arg4
local numHit = arg6 or 0
local numMissed = arg7 or 0
-- Step 1: Validate against SPELL_CAST_EVENT (confirms player-initiated, successful cast)
local pending = CleveRoids.pendingCasts and CleveRoids.pendingCasts[spellId]
if not pending then
return -- No matching SPELL_CAST_EVENT with success=1, skip
end
-- Clear the pending cast (consumed)
CleveRoids.pendingCasts[spellId] = nil
-- Skip channels and targeting spells (not melee/ranged attacks)
-- CastType: NORMAL=1, NON_GCD=2, ON_SWING=3, CHANNEL=4, TARGETING=5, TARGETING_NON_GCD=6
if pending.castType and pending.castType >= 4 then
return
end
-- Step 2: Check hit/miss outcome - binary check covers dodge, parry, resist, etc.
if numMissed >= 1 and numHit == 0 then
-- Use current target GUID if SPELL_GO targetGuid is empty
local procTarget = targetGuid
if not procTarget or procTarget == "0x0000000000000000" then
procTarget = pending.targetGuid
if not procTarget or procTarget == "0x0000000000000000" then
local _, currentTargetGUID = UnitExists("target")
procTarget = currentTargetGUID
end
end
-- Overpower: enemy avoided our yellow attack
if CleveRoids.reactiveSpells and CleveRoids.reactiveSpells["Overpower"] then
CleveRoids.SetReactiveProc("Overpower", 4.0, procTarget)
CleveRoids.QueueActionUpdate()
if CleveRoids.debug then
local spellName = SpellInfo and SpellInfo(spellId) or tostring(spellId)
DEFAULT_CHAT_FRAME:AddMessage(
string.format("|cff00ff00[SPELL_GO]|r Overpower proc - %s missed (hit=%d, miss=%d, castType=%s)",
spellName, numHit, numMissed, tostring(pending.castType))
)
end
end
-- Update LastSwing for yellow miss (victimState unknown from SPELL_GO, use UNAFFECTED)
-- Don't overwrite AUTO_ATTACK data from the same frame (on-swing abilities fire both)
if CleveRoids.LastSwing and CleveRoids.LastSwing.timestamp ~= GetTime() then
CleveRoids.LastSwing.timestamp = GetTime()
CleveRoids.LastSwing.hitInfo = HITINFO_MISS
CleveRoids.LastSwing.victimState = VICTIMSTATE_UNAFFECTED
CleveRoids.LastSwing.damage = 0
CleveRoids.LastSwing.blockedAmount = 0
CleveRoids.LastSwing.absorbAmount = 0
CleveRoids.LastSwing.resistAmount = 0
CleveRoids.LastSwing.targetGuid = procTarget
end
end
return
end
-- ========================================================================
-- COMBAT LOG FALLBACK FOR REACTIVE ABILITIES
-- ========================================================================
-- Only used for incoming avoidance (Riposte/Revenge) from enemy abilities.
-- Outgoing dodges handled by SPELL_GO_SELF (v2.25+) or AUTO_ATTACK (white swings).
if event == "RAW_COMBATLOG" or
event == "CHAT_MSG_COMBAT_SELF_MISSES" or
event == "CHAT_MSG_SPELL_SELF_DAMAGE" or