From 62371a4bc5201d3c251a8eaae0f1fc1138f77665 Mon Sep 17 00:00:00 2001 From: Jrc13245 Date: Mon, 29 Dec 2025 16:20:47 -0500 Subject: [PATCH] fully update immunities system and force clear for version usage --- Conditionals.lua | 61 +-------------- Init.lua | 23 ++++++ SuperCleveRoidMacros.toc | 2 +- Utility.lua | 156 ++++++++++++++++++++++++++++++++------- 4 files changed, 159 insertions(+), 83 deletions(-) diff --git a/Conditionals.lua b/Conditionals.lua index 2941535..81f8b8d 100644 --- a/Conditionals.lua +++ b/Conditionals.lua @@ -2147,16 +2147,8 @@ function CleveRoids.ValidatePlayerBuff(args) local numForms = GetNumShapeshiftForms() for i = 1, numForms do local icon, name, isActive, isCastable = GetShapeshiftFormInfo(i) - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage(string.format( - "|cff00ff00[ValidatePlayerBuff]|r Form %d: name=%s, isActive=%s, searching=%s", - i, tostring(name), tostring(isActive), searchName)) - end -- PERFORMANCE: Use cached lowercase to avoid per-iteration string allocation if name and isActive and GetLowercaseString(name) == searchName then - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[ValidatePlayerBuff]|r MATCH FOUND - returning true") - end return true end end @@ -3386,25 +3378,9 @@ CleveRoids.Keywords = { end, nodebuff = function(conditionals) - if CleveRoids.debug then - local vals = conditionals.nodebuff - local valStr = type(vals) == "table" and table.concat(vals, ", ") or tostring(vals) - DEFAULT_CHAT_FRAME:AddMessage("|cff00ffff[nodebuff]|r Checking: " .. valStr .. " on " .. (conditionals.target or "target")) - end - local result = NegatedMulti(conditionals.nodebuff, function(v) - local hasDebuff = CleveRoids.ValidateUnitDebuff(conditionals.target, v) - if CleveRoids.debug then - local vName = type(v) == "table" and (v.name or "table") or tostring(v) - DEFAULT_CHAT_FRAME:AddMessage(string.format( - "|cff00ffff[nodebuff]|r ValidateUnitDebuff(%s) = %s, returning %s", - vName, tostring(hasDebuff), tostring(not hasDebuff))) - end - return not hasDebuff + return NegatedMulti(conditionals.nodebuff, function(v) + return not CleveRoids.ValidateUnitDebuff(conditionals.target, v) end, conditionals, "nodebuff") - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage("|cff00ffff[nodebuff]|r Final result: " .. tostring(result)) - end - return result end, mybuff = function(conditionals) @@ -3414,24 +3390,9 @@ CleveRoids.Keywords = { end, nomybuff = function(conditionals) - if CleveRoids.debug then - local vals = conditionals.nomybuff - local valStr = type(vals) == "table" and table.concat(vals, ", ") or tostring(vals) - DEFAULT_CHAT_FRAME:AddMessage("|cffff00ff[nomybuff]|r Checking: " .. valStr) - end - local result = NegatedMulti(conditionals.nomybuff, function(v) - local hasBuff = CleveRoids.ValidatePlayerBuff(v) - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage(string.format( - "|cffff00ff[nomybuff]|r ValidatePlayerBuff(%s) = %s, returning %s", - tostring(v), tostring(hasBuff), tostring(not hasBuff))) - end - return not hasBuff + return NegatedMulti(conditionals.nomybuff, function(v) + return not CleveRoids.ValidatePlayerBuff(v) end, conditionals, "nomybuff") - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage("|cffff00ff[nomybuff]|r Final result: " .. tostring(result)) - end - return result end, mydebuff = function(conditionals) @@ -4291,25 +4252,11 @@ CleveRoids.Keywords = { checkValue = conditionals.action end - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage(string.format( - "|cffff9900[noimmune]|r Checking: %s (action=%s) on %s", - tostring(checkValue), tostring(conditionals.action), conditionals.target or "target")) - end - if not checkValue then - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage("|cffff9900[noimmune]|r No checkValue, returning true") - end return true -- If we can't determine spell/school, assume not immune end local isImmune = CleveRoids.CheckImmunity(conditionals.target or "target", checkValue) - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage(string.format( - "|cffff9900[noimmune]|r CheckImmunity(%s) = %s, returning %s", - checkValue, tostring(isImmune), tostring(not isImmune))) - end return not isImmune end, diff --git a/Init.lua b/Init.lua index 88acb99..9558ffd 100644 --- a/Init.lua +++ b/Init.lua @@ -259,12 +259,35 @@ local function PrintFeatures() end end +-- Immunity data version - increment this when changing immunity data format +-- This will cause all immunity data to be reset on addon update +CleveRoids.IMMUNITY_DATA_VERSION = 2 + -- Call on next frame to ensure everything is loaded local initFrame = CreateFrame("Frame") initFrame:RegisterEvent("PLAYER_ENTERING_WORLD") initFrame:SetScript("OnEvent", function() this:UnregisterAllEvents() + -- Check immunity data version and reset if outdated + CleveRoidMacros = CleveRoidMacros or {} + CleveRoids_ImmunityData = CleveRoids_ImmunityData or {} + local savedVersion = CleveRoidMacros.immunityDataVersion or 0 + + if savedVersion < CleveRoids.IMMUNITY_DATA_VERSION then + -- Check if there was existing data to clear + local hadData = next(CleveRoids_ImmunityData) ~= nil + + -- Version changed - reset all immunity data + CleveRoids_ImmunityData = {} + CleveRoidMacros.immunityDataVersion = CleveRoids.IMMUNITY_DATA_VERSION + + if hadData then + -- Show message if we actually cleared existing data + CleveRoids.Print("|cffff9900Immunity data reset|r - addon updated to data version " .. CleveRoids.IMMUNITY_DATA_VERSION) + end + end + -- Initialize NampowerAPI if available if CleveRoids.NampowerAPI then local API = CleveRoids.NampowerAPI diff --git a/SuperCleveRoidMacros.toc b/SuperCleveRoidMacros.toc index d3def30..e6c297b 100644 --- a/SuperCleveRoidMacros.toc +++ b/SuperCleveRoidMacros.toc @@ -2,7 +2,7 @@ ## Author: DWG, _brain, Torio, yani9o, Avitasia ## Title: [Super]CleveRoidMacros ## Notes: /cleveroid for settings -## Version: 1.7 +## Version: 1.8 ## OptionalDeps: ClassicFocus, FocusFrame, pfUI, SuperMacro, Bongos_ActionBar ## SavedVariables: CleveRoidMacros, CleveRoids_LearnedDurations, CleveRoids_AuraTextures, CleveRoids_ImmunityData, CleveRoids_ComboDurations, CleveRoids_SpellSchools Localization.lua diff --git a/Utility.lua b/Utility.lua index c6ab87f..7ce3d44 100644 --- a/Utility.lua +++ b/Utility.lua @@ -1787,6 +1787,34 @@ lib.pendingPersonalDebuffs = lib.pendingPersonalDebuffs or {} -- Format: { [index] = { timestamp = GetTime(), targetGUID = guid, targetName = name, spellID = id, ccType = "stun" } } lib.pendingCCDebuffs = lib.pendingCCDebuffs or {} +-- Spells with HIDDEN CC debuffs - these apply CC effects but don't show visible debuffs +-- Skip debuff-based immunity verification for these (only trust combat log "immune" messages) +-- Pounce stun is hidden - the target is stunned but no debuff icon appears +lib.hiddenCCSpells = { + [9005] = true, -- Pounce (Rank 1) + [9823] = true, -- Pounce (Rank 2) + [9827] = true, -- Pounce (Rank 3) +} + +-- Reverse lookup: spell NAME -> immunity type for combat log tracking +-- Used by ParseAfflictedCombatLog to confirm spells landed via "afflicted by" messages +-- Format: { ["SpellName"] = { type = "cc" or "school", value = "stun" or "bleed" } } +lib.trackedAfflictions = { + -- Hidden CC effects (no visible debuff, must use combat log) + ["Pounce"] = { type = "cc", value = "stun" }, + + -- Bleed effects (visible debuff, but combat log is more reliable) + ["Pounce Bleed"] = { type = "school", value = "bleed" }, + ["Rake"] = { type = "school", value = "bleed" }, + ["Rip"] = { type = "school", value = "bleed" }, + ["Lacerate"] = { type = "school", value = "bleed" }, + ["Garrote"] = { type = "school", value = "bleed" }, + ["Rupture"] = { type = "school", value = "bleed" }, + ["Deep Wound"] = { type = "school", value = "bleed" }, + ["Deep Wounds"] = { type = "school", value = "bleed" }, + ["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 @@ -2515,32 +2543,55 @@ ev:SetScript("OnEvent", function() -- CC IMMUNITY TRACKING: Check if this spell is a CC spell and track for immunity verification -- Uses the original spellID (not trackingSpellID) to detect CC type local ccType = CleveRoids.GetSpellCCType and CleveRoids.GetSpellCCType(spellID) - if ccType then - local spellName = SpellInfo(spellID) - -- Get target name from cache or current target - local ccTargetName = lib.guidToName[targetGUID] - if not ccTargetName then - local _, currentTargetGUID = UnitExists("target") - currentTargetGUID = CleveRoids.NormalizeGUID(currentTargetGUID) - if currentTargetGUID == targetGUID then - ccTargetName = UnitName("target") - lib.guidToName[targetGUID] = ccTargetName - end - end - table.insert(lib.pendingCCDebuffs, { - timestamp = GetTime(), - targetGUID = targetGUID, - targetName = ccTargetName, - spellID = spellID, - spellName = spellName, - ccType = ccType, - }) - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cff00ff00[CC Track]|r Tracking %s (%s) on %s for immunity verification", - spellName or "Unknown", ccType, ccTargetName or "Unknown") - ) + -- Debug: Show what GetSpellCCType returns for this spell + if CleveRoids.debug then + local spellNameDebug = SpellInfo(spellID) or "Unknown" + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cffaaaaaa[CC Check]|r %s (ID:%d) → ccType: %s", + spellNameDebug, spellID, ccType or "nil") + ) + end + + if ccType then + -- Skip debuff-based verification for spells with hidden CC effects (e.g., Pounce stun) + -- These spells apply CC but don't show visible debuffs, so verification always fails + -- Only trust combat log "immune" messages for these spells + if lib.hiddenCCSpells and lib.hiddenCCSpells[spellID] then + if CleveRoids.debug then + local spellName = SpellInfo(spellID) or "Unknown" + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cffff6600[CC Skip]|r %s (%s) has hidden debuff - skipping verification", + spellName, ccType) + ) + end + else + local spellName = SpellInfo(spellID) + -- Get target name from cache or current target + local ccTargetName = lib.guidToName[targetGUID] + if not ccTargetName then + local _, currentTargetGUID = UnitExists("target") + currentTargetGUID = CleveRoids.NormalizeGUID(currentTargetGUID) + if currentTargetGUID == targetGUID then + ccTargetName = UnitName("target") + lib.guidToName[targetGUID] = ccTargetName + end + end + table.insert(lib.pendingCCDebuffs, { + timestamp = GetTime(), + targetGUID = targetGUID, + targetName = ccTargetName, + spellID = spellID, + spellName = spellName, + ccType = ccType, + }) + + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cff00ff00[CC Track]|r Tracking %s (%s) on %s for immunity verification", + spellName or "Unknown", ccType, ccTargetName or "Unknown") + ) + end end end @@ -4929,6 +4980,54 @@ local function ParseImmunityCombatLog() end end +-- Combat log parser for "afflicted by" messages +-- Tracks when CC effects (like Pounce stun) and bleeds successfully land +-- This confirms the effect worked and removes any false immunity records +local function ParseAfflictedCombatLog() + local message = arg1 + if not message then return end + + -- Quick check: must contain "afflicted by" + if not string.find(message, "afflicted by") then return end + + -- Pattern: "X is afflicted by Y" or "X is afflicted by Y (N)." + -- Handle both with and without trailing period + local _, _, targetName, spellName = string.find(message, "^(.-)%s+is afflicted by%s+(.+)") + if not targetName or not spellName then return end + + -- Remove trailing period if present + spellName = string.gsub(spellName, "%.$", "") + -- Remove stack count like "(1)" from spell name + spellName = string.gsub(spellName, "%s*%(%d+%)$", "") + + -- Check if this is a tracked spell (CC or bleed) + local affliction = lib.trackedAfflictions and lib.trackedAfflictions[spellName] + if not affliction then + return -- Not a tracked spell, ignore + end + + if affliction.type == "cc" then + -- CC effect landed - remove any false CC immunity + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cff00ff00[CC Landed]|r %s afflicted by %s (%s)", + targetName, spellName, affliction.value) + ) + end + RemoveCCImmunity(targetName, affliction.value) + + elseif affliction.type == "school" then + -- School/bleed effect landed - remove any false school immunity + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cff00ff00[Bleed Landed]|r %s afflicted by %s (%s)", + targetName, spellName, affliction.value) + ) + end + RemoveSpellImmunity(targetName, affliction.value) + end +end + -- Check if a unit is immune to a spell, damage school, or CC type -- Supports: CheckImmunity(unitId, "Flame Shock") or CheckImmunity(unitId, "fire") or CheckImmunity(unitId, "stun") function CleveRoids.CheckImmunity(unitId, spellOrSchool) @@ -5315,14 +5414,21 @@ end -- PERFORMANCE: Only use RAW_COMBATLOG and SPELL_FAILURE to avoid spam from damage events -- CHAT_MSG_SPELL_*_DAMAGE fires on EVERY hit/resist (100+ times/second in combat) -- EXCEPTION: CHAT_MSG_SPELL_SELF_DAMAGE is needed for immunity detection (includes "is immune" messages) +-- EXCEPTION: CHAT_MSG_SPELL_PERIODIC_CREATURE_DAMAGE is needed for "afflicted by" detection (hidden CC spells) local immunityFrame = CreateFrame("Frame", "CleveRoidsImmunityFrame") immunityFrame:RegisterEvent("RAW_COMBATLOG") immunityFrame:RegisterEvent("CHAT_MSG_SPELL_FAILURE") immunityFrame:RegisterEvent("CHAT_MSG_SPELL_SELF_DAMAGE") +immunityFrame:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_CREATURE_DAMAGE") immunityFrame:SetScript("OnEvent", function() if event == "RAW_COMBATLOG" or event == "CHAT_MSG_SPELL_FAILURE" or event == "CHAT_MSG_SPELL_SELF_DAMAGE" then ParseImmunityCombatLog() end + -- Check for "afflicted by" messages for hidden CC spells (e.g., Pounce stun) + -- These come through RAW_COMBATLOG and PERIODIC_CREATURE_DAMAGE + if event == "RAW_COMBATLOG" or event == "CHAT_MSG_SPELL_PERIODIC_CREATURE_DAMAGE" then + ParseAfflictedCombatLog() + end end) -- ============================================================================