mirror of
https://github.com/brues-code/SuperCleveRoidMacros.git
synced 2026-09-16 03:38:00 +00:00
fixes
This commit is contained in:
+86
-25
@@ -835,6 +835,23 @@ function CleveRoids.FindAllCasterAuraByName(targetGuid, searchName)
|
||||
if not targetData then return nil, nil end
|
||||
|
||||
local now = GetTime()
|
||||
|
||||
-- Direct spell ID lookup: O(1) hash table access instead of iterating all entries
|
||||
local searchID = tonumber(searchName)
|
||||
if searchID then
|
||||
local auraData = targetData[searchID]
|
||||
if auraData and auraData.start and auraData.duration then
|
||||
local remaining = auraData.duration + auraData.start - now
|
||||
if remaining > 0 then
|
||||
return remaining, auraData.casterGuid
|
||||
else
|
||||
targetData[searchID] = nil
|
||||
end
|
||||
end
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
-- Name-based search (existing behavior)
|
||||
local searchLower = string.lower(searchName)
|
||||
|
||||
for spellId, auraData in pairs(targetData) do
|
||||
@@ -2997,7 +3014,9 @@ function CleveRoids.ValidateAura(unit, args, isbuff)
|
||||
end
|
||||
|
||||
-- PERFORMANCE: Cache lowercased search name to avoid repeated string.lower calls
|
||||
local searchName = args.name and _string_lower(args.name)
|
||||
-- Support spell ID matching: [mybuff:17941] matches by spell ID instead of name
|
||||
local searchID = args.name and tonumber(args.name)
|
||||
local searchName = not searchID and args.name and _string_lower(args.name) or nil
|
||||
|
||||
-- Primary search: BUFFS if isbuff==true, DEBUFFS if isbuff==false
|
||||
while true do
|
||||
@@ -3020,12 +3039,20 @@ function CleveRoids.ValidateAura(unit, args, isbuff)
|
||||
|
||||
if not texture then break end
|
||||
|
||||
if current_spellID and searchName then
|
||||
-- PERFORMANCE: Use cached lowercase spell name lookup
|
||||
local lowerName = GetLowercaseSpellName(current_spellID)
|
||||
if lowerName and lowerName == searchName then
|
||||
found = true
|
||||
break
|
||||
if current_spellID then
|
||||
if searchID then
|
||||
-- Spell ID matching: [mybuff:17941]
|
||||
if current_spellID == searchID then
|
||||
found = true
|
||||
break
|
||||
end
|
||||
elseif searchName then
|
||||
-- PERFORMANCE: Use cached lowercase spell name lookup
|
||||
local lowerName = GetLowercaseSpellName(current_spellID)
|
||||
if lowerName and lowerName == searchName then
|
||||
found = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3033,7 +3060,7 @@ function CleveRoids.ValidateAura(unit, args, isbuff)
|
||||
end
|
||||
|
||||
-- Overflow handling: when searching DEBUFFS on non-players, also scan BUFFS
|
||||
if not isbuff and not isPlayer and not found and searchName then
|
||||
if not isbuff and not isPlayer and not found and (searchID or searchName) then
|
||||
i = 1
|
||||
while true do
|
||||
local texture
|
||||
@@ -3044,11 +3071,18 @@ function CleveRoids.ValidateAura(unit, args, isbuff)
|
||||
if not texture then break end
|
||||
|
||||
if current_spellID then
|
||||
-- PERFORMANCE: Use cached lowercase spell name lookup
|
||||
local lowerName = GetLowercaseSpellName(current_spellID)
|
||||
if lowerName and lowerName == searchName then
|
||||
found = true
|
||||
break
|
||||
if searchID then
|
||||
if current_spellID == searchID then
|
||||
found = true
|
||||
break
|
||||
end
|
||||
elseif searchName then
|
||||
-- PERFORMANCE: Use cached lowercase spell name lookup
|
||||
local lowerName = GetLowercaseSpellName(current_spellID)
|
||||
if lowerName and lowerName == searchName then
|
||||
found = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3154,12 +3188,15 @@ function CleveRoids.ValidateUnitDebuff(unit, args)
|
||||
-- Convert underscores to spaces for matching (e.g., "Thunder_Clap" -> "Thunder Clap")
|
||||
args.name = string.gsub(args.name, "_", " ")
|
||||
|
||||
-- Support spell ID matching: [debuff:9904] matches by spell ID instead of name
|
||||
local searchID = tonumber(args.name)
|
||||
|
||||
local found = false
|
||||
local texture, stacks, spellID, remaining
|
||||
local i
|
||||
|
||||
-- PERFORMANCE: For non-SuperWoW, early return if no texture registered
|
||||
if not CleveRoids.hasSuperwow and not CleveRoids.auraTextures[args.name] then
|
||||
if not searchID and not CleveRoids.hasSuperwow and not CleveRoids.auraTextures[args.name] then
|
||||
return false
|
||||
end
|
||||
|
||||
@@ -3176,7 +3213,13 @@ function CleveRoids.ValidateUnitDebuff(unit, args)
|
||||
if not guid then return false end
|
||||
|
||||
-- PERFORMANCE: Use cached spell name -> ID mapping instead of iterating every call
|
||||
local matchingSpellIDs = GetSpellIDsForName(args.name) or {}
|
||||
-- For spell ID input, use the ID directly instead of name->ID lookup
|
||||
local matchingSpellIDs
|
||||
if searchID then
|
||||
matchingSpellIDs = {searchID}
|
||||
else
|
||||
matchingSpellIDs = GetSpellIDsForName(args.name) or {}
|
||||
end
|
||||
|
||||
-- Check tracking table for ANY rank of this spell: Did player cast this? Is timer valid?
|
||||
if matchingSpellIDs and table.getn(matchingSpellIDs) > 0 then
|
||||
@@ -3272,9 +3315,14 @@ function CleveRoids.ValidateUnitDebuff(unit, args)
|
||||
if not tex then break end
|
||||
|
||||
if debuffSpellID then
|
||||
-- PERFORMANCE: Use cached spell name lookup
|
||||
local baseName, fullName = GetSpellNames(debuffSpellID)
|
||||
if baseName and (baseName == args.name or fullName == args.name) then
|
||||
local matched = false
|
||||
if searchID then
|
||||
matched = (debuffSpellID == searchID)
|
||||
else
|
||||
local baseName, fullName = GetSpellNames(debuffSpellID)
|
||||
matched = baseName and (baseName == args.name or fullName == args.name)
|
||||
end
|
||||
if matched then
|
||||
-- IMPORTANT: Only use fallback for SHARED debuffs
|
||||
-- Personal debuffs must come from tracking table (caster check)
|
||||
local isShared = lib and lib.IsPersonalDebuff and lib:IsPersonalDebuff(debuffSpellID) == false
|
||||
@@ -3298,9 +3346,14 @@ function CleveRoids.ValidateUnitDebuff(unit, args)
|
||||
if not tex then break end
|
||||
|
||||
if buffSpellID then
|
||||
-- PERFORMANCE: Use cached spell name lookup
|
||||
local baseName, fullName = GetSpellNames(buffSpellID)
|
||||
if baseName and (baseName == args.name or fullName == args.name) then
|
||||
local matched = false
|
||||
if searchID then
|
||||
matched = (buffSpellID == searchID)
|
||||
else
|
||||
local baseName, fullName = GetSpellNames(buffSpellID)
|
||||
matched = baseName and (baseName == args.name or fullName == args.name)
|
||||
end
|
||||
if matched then
|
||||
-- IMPORTANT: Only use fallback for SHARED debuffs
|
||||
local isShared = lib and lib.IsPersonalDebuff and lib:IsPersonalDebuff(buffSpellID) == false
|
||||
if isShared then
|
||||
@@ -3324,8 +3377,12 @@ function CleveRoids.ValidateUnitDebuff(unit, args)
|
||||
texture, stacks, spellID, remaining = CleveRoids.GetPlayerAura(i, false)
|
||||
if not texture then break end
|
||||
|
||||
if CleveRoids.hasSuperwow then
|
||||
-- PERFORMANCE: Use cached spell name lookup
|
||||
if searchID then
|
||||
if spellID == searchID then
|
||||
found = true
|
||||
break
|
||||
end
|
||||
elseif CleveRoids.hasSuperwow then
|
||||
local baseName, fullName = GetSpellNames(spellID)
|
||||
if baseName and (baseName == args.name or fullName == args.name) then
|
||||
found = true
|
||||
@@ -3345,8 +3402,12 @@ function CleveRoids.ValidateUnitDebuff(unit, args)
|
||||
texture, stacks, spellID, remaining = CleveRoids.GetPlayerAura(i, true)
|
||||
if not texture then break end
|
||||
|
||||
if CleveRoids.hasSuperwow then
|
||||
-- PERFORMANCE: Use cached spell name lookup
|
||||
if searchID then
|
||||
if spellID == searchID then
|
||||
found = true
|
||||
break
|
||||
end
|
||||
elseif CleveRoids.hasSuperwow then
|
||||
local baseName, fullName = GetSpellNames(spellID)
|
||||
if baseName and (baseName == args.name or fullName == args.name) then
|
||||
found = true
|
||||
|
||||
@@ -5558,6 +5558,15 @@ function CleveRoids.Frame:SPELL_CAST_EVENT()
|
||||
-- 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 {}
|
||||
|
||||
-- Clean up consumed entries older than 5 seconds (lightweight, runs per-cast)
|
||||
local cleanupTime = GetTime() - 5
|
||||
for id, entry in pairs(CleveRoids.pendingCasts) do
|
||||
if entry.consumed and entry.consumedAt and entry.consumedAt < cleanupTime then
|
||||
CleveRoids.pendingCasts[id] = nil
|
||||
end
|
||||
end
|
||||
|
||||
CleveRoids.pendingCasts[spellId] = {
|
||||
castType = castType,
|
||||
targetGuid = targetGuid,
|
||||
|
||||
+69
-41
@@ -8003,11 +8003,11 @@ function CleveRoids.ParseReactiveCombatLog()
|
||||
local _, targetGUID = UnitExists("target")
|
||||
|
||||
-- Check each reactive ability's trigger patterns
|
||||
-- NOTE: Overpower (enemy_dodge) ALWAYS uses combat log text parsing, even when SPELL_GO
|
||||
-- events are available. SPELL_GO only provides hit/miss COUNTS, not miss TYPES, so it
|
||||
-- cannot distinguish dodge (Overpower proc) from parry/block/resist (no Overpower proc).
|
||||
for spellName, config in pairs(reactivePatterns) do
|
||||
-- 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
|
||||
if 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)
|
||||
@@ -8016,7 +8016,6 @@ function CleveRoids.ParseReactiveCombatLog()
|
||||
CleveRoids.SetReactiveProc(spellName, duration, guid)
|
||||
|
||||
-- For Overpower (enemy dodge), also update LastSwing so [lastswing:dodge] works
|
||||
-- (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
|
||||
@@ -8198,28 +8197,36 @@ if GetNampowerVersion then
|
||||
end
|
||||
end
|
||||
|
||||
-- Check for Nampower v2.25+ SPELL_GO events (replaces combat log for yellow attack miss detection)
|
||||
-- Check for Nampower v2.25+ SPELL_GO events (LastSwing tracking for yellow attack misses)
|
||||
-- NOTE: SPELL_GO only provides hit/miss counts, not miss types, so Overpower still needs
|
||||
-- combat log text parsing. SPELL_GO is used as a gate to avoid parsing combat log on every
|
||||
-- successful spell hit - only parse when SPELL_GO reports a miss.
|
||||
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+)")
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[Nampower]|r Using SPELL_GO events for yellow attack LastSwing tracking (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
|
||||
-- Outgoing dodge detection (Overpower) uses combat log text parsing because SPELL_GO only
|
||||
-- provides hit/miss counts, not miss types (dodge vs parry vs block vs resist). However,
|
||||
-- with SPELL_GO available, CHAT_MSG_SPELL_SELF_DAMAGE parsing is gated behind a miss window
|
||||
-- so we skip parsing on every successful spell hit (see OnEvent handler below).
|
||||
-- AUTO_ATTACK events handle white swing avoidance; combat log handles yellow ability dodges.
|
||||
if hasAutoAttackEvents then
|
||||
-- Incoming avoidance (Riposte/Revenge)
|
||||
reactiveFrame:RegisterEvent("CHAT_MSG_COMBAT_CREATURE_VS_SELF_MISSES")
|
||||
reactiveFrame:RegisterEvent("CHAT_MSG_COMBAT_CREATURE_VS_SELF_HITS")
|
||||
-- Outgoing dodge detection (Overpower) - gated by SPELL_GO miss window when available
|
||||
reactiveFrame:RegisterEvent("CHAT_MSG_COMBAT_SELF_MISSES")
|
||||
reactiveFrame:RegisterEvent("CHAT_MSG_SPELL_SELF_DAMAGE")
|
||||
else
|
||||
-- Full combat log fallback
|
||||
reactiveFrame:RegisterEvent("RAW_COMBATLOG")
|
||||
@@ -8263,10 +8270,11 @@ reactiveFrame:SetScript("OnEvent", function()
|
||||
end
|
||||
|
||||
-- ========================================================================
|
||||
-- NAMPOWER v2.25+ SPELL_GO EVENTS (yellow attack hit/miss detection)
|
||||
-- NAMPOWER v2.25+ SPELL_GO EVENTS (yellow attack LastSwing tracking)
|
||||
-- ========================================================================
|
||||
-- Two-step: SPELL_CAST_EVENT (success=1) → SPELL_GO_SELF (hit/miss outcome)
|
||||
-- Replaces combat log text parsing for outgoing yellow ability misses.
|
||||
-- Updates LastSwing state for yellow ability misses. Overpower detection still uses
|
||||
-- combat log text parsing (SPELL_GO only has hit/miss counts, not types like dodge).
|
||||
-- SPELL_GO_SELF params: itemId, spellId, casterGuid, targetGuid, castFlags, numTargetsHit, numTargetsMissed
|
||||
if event == "SPELL_GO_SELF" then
|
||||
local spellId = arg2
|
||||
@@ -8276,12 +8284,16 @@ reactiveFrame:SetScript("OnEvent", function()
|
||||
|
||||
-- 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
|
||||
if not pending or pending.consumed then
|
||||
return -- No matching SPELL_CAST_EVENT with success=1, or already processed
|
||||
end
|
||||
|
||||
-- Clear the pending cast (consumed)
|
||||
CleveRoids.pendingCasts[spellId] = nil
|
||||
-- Mark pending cast as consumed with timestamp instead of deleting immediately.
|
||||
-- Other handlers (libdebuff SPELL_GO, AURA_CAST, ComboPointTracker) on different
|
||||
-- frames may still need to read this data. Stale entries are cleaned up on next
|
||||
-- SPELL_CAST_EVENT for the same spellId (overwritten) or expire naturally.
|
||||
pending.consumed = true
|
||||
pending.consumedAt = GetTime()
|
||||
|
||||
-- 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
|
||||
@@ -8289,7 +8301,11 @@ reactiveFrame:SetScript("OnEvent", function()
|
||||
return
|
||||
end
|
||||
|
||||
-- Step 2: Check hit/miss outcome - binary check covers dodge, parry, resist, etc.
|
||||
-- Step 2: Check hit/miss outcome for LastSwing tracking
|
||||
-- NOTE: SPELL_GO only provides hit/miss COUNTS, not miss TYPES (dodge vs parry vs
|
||||
-- block vs resist). Overpower detection is handled by combat log text parsing which
|
||||
-- can distinguish "dodged" from other avoidance types. We only update LastSwing here
|
||||
-- as a generic miss indicator (combat log overwrites with specific type if dodge).
|
||||
if numMissed >= 1 and numHit == 0 then
|
||||
-- Use current target GUID if SPELL_GO targetGuid is empty
|
||||
local procTarget = targetGuid
|
||||
@@ -8301,22 +8317,9 @@ reactiveFrame:SetScript("OnEvent", function()
|
||||
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)
|
||||
-- Combat log handler will overwrite with specific type (dodge/parry/etc.) if available
|
||||
if CleveRoids.LastSwing and CleveRoids.LastSwing.timestamp ~= GetTime() then
|
||||
CleveRoids.LastSwing.timestamp = GetTime()
|
||||
CleveRoids.LastSwing.hitInfo = HITINFO_MISS
|
||||
@@ -8327,21 +8330,46 @@ reactiveFrame:SetScript("OnEvent", function()
|
||||
CleveRoids.LastSwing.resistAmount = 0
|
||||
CleveRoids.LastSwing.targetGuid = procTarget
|
||||
end
|
||||
|
||||
-- Gate combat log parsing: set miss window so CHAT_MSG_SPELL_SELF_DAMAGE
|
||||
-- only calls ParseReactiveCombatLog when we know a yellow miss occurred.
|
||||
-- This avoids parsing every successful spell hit message.
|
||||
CleveRoids._yellowMissWindow = GetTime()
|
||||
|
||||
if CleveRoids.debug then
|
||||
local spellName = SpellInfo and SpellInfo(spellId) or tostring(spellId)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(
|
||||
string.format("|cff00ff00[SPELL_GO]|r Yellow miss detected - %s (hit=%d, miss=%d, castType=%s) - deferring to combat log for type",
|
||||
spellName, numHit, numMissed, tostring(pending.castType))
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
-- ========================================================================
|
||||
-- COMBAT LOG FALLBACK FOR REACTIVE ABILITIES
|
||||
-- COMBAT LOG TEXT PARSING 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
|
||||
event == "CHAT_MSG_COMBAT_CREATURE_VS_SELF_MISSES" or
|
||||
event == "CHAT_MSG_COMBAT_CREATURE_VS_SELF_HITS" then
|
||||
-- Handles: Overpower (outgoing dodge), Riposte (incoming parry), Revenge (incoming avoid).
|
||||
-- SPELL_GO provides hit/miss counts but not types, so combat log is still needed to
|
||||
-- distinguish dodge (Overpower) from parry/block/resist. But we only parse when SPELL_GO
|
||||
-- told us a miss occurred, avoiding overhead on every successful spell hit.
|
||||
if event == "CHAT_MSG_SPELL_SELF_DAMAGE" then
|
||||
-- Yellow ability avoidance (e.g., "Your Mortal Strike was dodged by Target")
|
||||
-- With SPELL_GO events: only parse when a yellow miss was recently detected.
|
||||
-- Without SPELL_GO: always parse (fallback for older Nampower).
|
||||
if hasSpellGoEvents then
|
||||
local missWindow = CleveRoids._yellowMissWindow
|
||||
if not missWindow or (GetTime() - missWindow) > 1.0 then
|
||||
return -- No recent yellow miss from SPELL_GO, skip parsing
|
||||
end
|
||||
end
|
||||
CleveRoids.ParseReactiveCombatLog()
|
||||
elseif event == "RAW_COMBATLOG" or
|
||||
event == "CHAT_MSG_COMBAT_SELF_MISSES" or
|
||||
event == "CHAT_MSG_COMBAT_CREATURE_VS_SELF_MISSES" or
|
||||
event == "CHAT_MSG_COMBAT_CREATURE_VS_SELF_HITS" then
|
||||
CleveRoids.ParseReactiveCombatLog()
|
||||
end
|
||||
end)
|
||||
|
||||
Reference in New Issue
Block a user