updates to prevent removal of immunities on certain debuff casts

This commit is contained in:
Jrc13245
2025-12-30 15:01:53 -05:00
parent 3ccaec5e32
commit 397a381c18
+193 -22
View File
@@ -1787,6 +1787,11 @@ lib.pendingPersonalDebuffs = lib.pendingPersonalDebuffs or {}
-- Format: { [index] = { timestamp = GetTime(), targetGUID = guid, targetName = name, spellID = id, ccType = "stun" } }
lib.pendingCCDebuffs = lib.pendingCCDebuffs or {}
-- Shared debuff pending tracking system
-- Stores shared debuffs to verify they landed (for immunity detection by spell school)
-- Format: { [index] = { timestamp = GetTime(), targetGUID = guid, targetName = name, spellID = id, school = "arcane", duration = X, stacks = N } }
lib.pendingSharedDebuffs = lib.pendingSharedDebuffs or {}
-- Spells with HIDDEN CC debuffs - these apply CC effects but don't show visible debuffs
-- Pounce stun is hidden - the target is stunned but no debuff icon appears
-- These spells use extended verification (0.4s instead of 0.2s) to allow time for:
@@ -2189,7 +2194,7 @@ delayedTrackingFrame:SetScript("OnUpdate", function()
-- If bleed is missing and target has few debuffs, it's likely immunity (not debuff cap)
if not bleedVerified then
local DEBUFF_CAP_THRESHOLD = 12
local DEBUFF_CAP_THRESHOLD = 47 -- Max is 48 (16 visible + 32 overflow)
if totalDebuffs < DEBUFF_CAP_THRESHOLD then
-- Few debuffs = likely bleed immunity, not debuff cap
if pending.targetName and pending.targetName ~= "" then
@@ -2239,8 +2244,9 @@ delayedTrackingFrame:SetScript("OnUpdate", function()
lib:AddEffect(pending.targetGUID, pending.targetName, pending.spellID, pending.duration, 0, "player")
-- Remove any existing bleed immunity record for this NPC since the bleed landed
if pending.targetName and pending.targetName ~= "" then
-- Remove any existing bleed immunity record for this NPC ONLY if a bleed spell landed
-- Previously this unconditionally removed bleed immunity for ANY personal debuff
if isBleedSpell and pending.targetName and pending.targetName ~= "" then
CleveRoids.RemoveSpellImmunity(pending.targetName, "bleed")
end
@@ -2411,7 +2417,7 @@ delayedTrackingFrame:SetScript("OnUpdate", function()
end
end
local DEBUFF_CAP_THRESHOLD = 12
local DEBUFF_CAP_THRESHOLD = 47 -- Max is 48 (16 visible + 32 overflow)
if totalDebuffs < DEBUFF_CAP_THRESHOLD then
-- Few debuffs = likely CC immunity
if resolvedTargetName and resolvedTargetName ~= "" and pending.ccType then
@@ -2465,6 +2471,108 @@ delayedTrackingFrame:SetScript("OnUpdate", function()
end
end
-- Process pending shared debuffs for immunity detection
if lib.pendingSharedDebuffs then
local toRemove = {}
for i, pending in ipairs(lib.pendingSharedDebuffs) do
local elapsed = GetTime() - pending.timestamp
-- Check after 0.2s delay (enough time for server sync)
if elapsed >= 0.2 then
local debuffVerified = false
local totalDebuffs = 0
-- Skip verification if target is dead (debuffs are removed on death)
if CleveRoids.hasSuperwow and pending.targetGUID then
if UnitIsDead(pending.targetGUID) then
-- Target died - can't verify immunity, assume debuff landed
debuffVerified = true
if CleveRoids.debug then
local spellNameDebug = SpellInfo(pending.spellID) or "Shared Debuff"
DEFAULT_CHAT_FRAME:AddMessage(
string.format("|cffff6600[Shared Verify Skip]|r Target %s is dead - skipping immunity check for %s",
pending.targetName or "Unknown", spellNameDebug)
)
end
else
-- Target is alive - check if debuff exists
for slot = 1, 48 do
local _, _, _, debuffSpellID = UnitDebuff(pending.targetGUID, slot)
if not debuffSpellID then
if slot <= 16 then break end -- Regular debuffs are dense, overflow continues on nil
else
totalDebuffs = totalDebuffs + 1
if debuffSpellID == pending.spellID then
debuffVerified = true
-- Don't break - continue counting total debuffs for immunity vs cap detection
end
end
end
end
else
-- No SuperWoW or no GUID - assume debuff landed (can't verify)
debuffVerified = true
end
-- Process result
if debuffVerified then
-- Debuff landed - add to tracking
if CleveRoids.debug then
local spellNameDebug = SpellInfo(pending.spellID) or "Unknown"
DEFAULT_CHAT_FRAME:AddMessage(
string.format("|cff88ff88[Shared Verified]|r %s (ID:%d) verified on %s",
spellNameDebug, pending.spellID, pending.targetName or "Unknown")
)
end
lib:AddEffect(pending.targetGUID, pending.targetName, pending.spellID, pending.duration, pending.stacks or 0, "player")
-- Remove any existing immunity record for this NPC for this spell's school
if pending.school and pending.targetName and pending.targetName ~= "" then
CleveRoids.RemoveSpellImmunity(pending.targetName, pending.school)
end
else
-- Debuff didn't land - check if it's immunity or debuff cap
local DEBUFF_CAP_THRESHOLD = 47 -- Max is 48 (16 visible + 32 overflow)
if totalDebuffs < DEBUFF_CAP_THRESHOLD then
-- Few debuffs = likely immunity
if pending.targetName and pending.targetName ~= "" and pending.school then
-- Record immunity for this spell's school
if not CleveRoids_ImmunityData[pending.school] then
CleveRoids_ImmunityData[pending.school] = {}
end
CleveRoids_ImmunityData[pending.school][pending.targetName] = true
if CleveRoids.debug then
local spellNameDebug = SpellInfo(pending.spellID) or "Unknown"
DEFAULT_CHAT_FRAME:AddMessage(
string.format("|cffff6600[Shared Immunity]|r %s is immune to %s (%s) - only %d debuffs on target",
pending.targetName, pending.school, spellNameDebug, totalDebuffs)
)
end
end
else
-- Many debuffs = likely pushed off at debuff cap
if CleveRoids.debug then
local spellNameDebug = SpellInfo(pending.spellID) or "Unknown"
DEFAULT_CHAT_FRAME:AddMessage(
string.format("|cffff6600[Shared Debuff Cap]|r %s not found on %s - likely pushed off (%d debuffs)",
spellNameDebug, pending.targetName or "Unknown", totalDebuffs)
)
end
end
end
table.insert(toRemove, i)
end
end
-- Remove processed entries
for i = table.getn(toRemove), 1, -1 do
table.remove(lib.pendingSharedDebuffs, toRemove[i])
end
end
-- Clean up old rank refresh overrides (older than 2 seconds)
if lib.rankRefreshOverrides then
local currentTime = GetTime()
@@ -2831,7 +2939,7 @@ ev:SetScript("OnEvent", function()
end
-- If rankCheck == false, skip entirely (shouldn't happen for personal debuffs with new logic)
else
-- Shared debuff - add immediately with predicted stack count
-- Shared debuff - schedule for delayed verification (immunity detection)
-- For stacking debuffs (Sunder, Faerie Fire), predict new stack count
local newStacks = 0
@@ -2869,38 +2977,55 @@ ev:SetScript("OnEvent", function()
if newStacks > 5 then
newStacks = 5
end
if CleveRoids.debug then
local spellName = SpellInfo(spellID) or "Unknown"
DEFAULT_CHAT_FRAME:AddMessage(
string.format("|cff00ff00[Shared Debuff]|r %s (ID:%d) predicted stacks:%d",
spellName, spellID, newStacks)
)
end
else
-- Target changed or not available, default to 1 stack
newStacks = 1
end
-- Determine spell school for immunity tracking
-- GetSpellSchool is defined later in file, use CleveRoids wrapper if available
local spellSchool = nil
local spellNameForSchool = SpellInfo(spellID)
if CleveRoids.GetSpellSchool then
spellSchool = CleveRoids.GetSpellSchool(spellNameForSchool, spellID)
end
-- Check if we should apply based on rank comparison
local rankCheck = lib:ShouldApplyDebuffRank(targetGUID, spellID)
if rankCheck == true then
-- Normal application
lib:AddEffect(targetGUID, targetName, spellID, duration, newStacks, "player")
elseif type(rankCheck) == "table" and rankCheck.preserve then
local trackingSpellID = spellID
local trackingDuration = duration
if type(rankCheck) == "table" and rankCheck.preserve then
-- Preserve higher rank's timer
lib:AddEffect(targetGUID, targetName, rankCheck.preserve, rankCheck.timeRemaining, newStacks, "player")
trackingSpellID = rankCheck.preserve
trackingDuration = rankCheck.timeRemaining
elseif rankCheck == false then
-- Skip entirely
trackingSpellID = nil
end
if trackingSpellID then
-- Schedule shared debuff for delayed verification
table.insert(lib.pendingSharedDebuffs, {
timestamp = GetTime(),
targetGUID = targetGUID,
targetName = targetName,
spellID = trackingSpellID,
castSpellID = spellID, -- Original cast spell ID (for dodge/parry matching)
duration = trackingDuration,
stacks = newStacks,
school = spellSchool,
isRankPreserve = (trackingSpellID ~= spellID)
})
if CleveRoids.debug then
local spellName = SpellInfo(rankCheck.preserve) or "Unknown"
local spellName = SpellInfo(trackingSpellID) or "Unknown"
DEFAULT_CHAT_FRAME:AddMessage(
string.format("|cff00aaff[Shared Debuff Rank Preserve]|r Preserved %s timer (%.1fs remaining)",
spellName, rankCheck.timeRemaining)
string.format("|cffaaff00[Pending Shared]|r Scheduled %s (ID:%d, school:%s) for tracking on %s",
spellName, trackingSpellID, spellSchool or "unknown", targetName or "Unknown")
)
end
end
-- If rankCheck == false, skip entirely (shouldn't happen with new logic)
end
-- Track this cast for miss/dodge/parry removal
@@ -3165,6 +3290,52 @@ evLearn:SetScript("OnEvent", function()
end
end
-- SHARED DEBUFFS: Cancel pending tracking if spell was dodged/parried/blocked/missed/resisted
if isAvoidanceOrResist and lib.pendingSharedDebuffs then
local messageSpellName = string.gsub(spellName, "%s*%(%s*Rank%s+%d+%s*%)", "")
local toRemove = {}
for i, pending in ipairs(lib.pendingSharedDebuffs) do
-- Check both the tracking spell name AND the original cast spell name
local pendingSpellName = SpellInfo(pending.spellID)
local castSpellName = pending.castSpellID and SpellInfo(pending.castSpellID)
local matchesTracking = false
local matchesCast = false
if pendingSpellName then
pendingSpellName = string.gsub(pendingSpellName, "%s*%(%s*Rank%s+%d+%s*%)", "")
matchesTracking = lower(pendingSpellName) == lower(messageSpellName)
end
if castSpellName then
castSpellName = string.gsub(castSpellName, "%s*%(%s*Rank%s+%d+%s*%)", "")
matchesCast = lower(castSpellName) == lower(messageSpellName)
end
if matchesTracking or matchesCast then
-- Found the pending shared debuff that was avoided/resisted - cancel it (don't record immunity)
if CleveRoids.debug then
local displayName = matchesCast and castSpellName or pendingSpellName
local avoidType = find(message, "dodged") and "dodged" or
find(message, "parried") and "parried" or
find(message, "blocked") and "blocked" or
find(message, "resist") and "resisted" or "missed"
DEFAULT_CHAT_FRAME:AddMessage(
string.format("|cffff0000[Shared Track]|r Cancelled %s (school:%s) - %s by %s (not immunity)",
displayName or "Unknown", pending.school or "unknown", avoidType, targetName or "Unknown")
)
end
table.insert(toRemove, i)
end
end
-- Remove cancelled shared debuff tracking (iterate backwards to avoid index shifting)
for i = table.getn(toRemove), 1, -1 do
table.remove(lib.pendingSharedDebuffs, toRemove[i])
end
end
-- Use the last player cast info if available and recent (within 1 second)
if lib.lastPlayerCast and lib.lastPlayerCast.timestamp and
(GetTime() - lib.lastPlayerCast.timestamp) < 1.0 then