Merge pull request #23 from jrc13245/main

merge
This commit is contained in:
Torio
2025-11-16 10:23:13 -05:00
committed by GitHub
6 changed files with 702 additions and 33 deletions
-2
View File
@@ -1,2 +0,0 @@
wow-api-type-definitions/
.gitignore
+173 -18
View File
@@ -337,6 +337,17 @@ function CleveRoids.ValidateComboPoints(operator, amount)
return false
end
function CleveRoids.ValidateLevel(unit, operator, amount)
if not unit or not operator or not amount then return false end
local level = UnitLevel(unit)
if level and CleveRoids.operators[operator] then
return CleveRoids.comparators[operator](level, amount)
end
return false
end
function CleveRoids.ValidateKnown(args)
if not args then
return false
@@ -821,15 +832,38 @@ function CleveRoids.ValidatePlayerDebuff(args)
end
function CleveRoids.ValidateWeaponImbue(slot, imbueName)
-- Check if weapon has enchant
-- Check if weapon has enchant via API
local hasMainEnchant, mainExpiration, mainCharges, hasOffEnchant, offExpiration, offCharges = GetWeaponEnchantInfo()
local hasEnchant, expiration, charges
if slot == "mh" then
hasEnchant = hasMainEnchant
expiration = mainExpiration
charges = mainCharges
else
hasEnchant = hasOffEnchant
expiration = offExpiration
charges = offCharges
end
-- Only consider temporary enchants (with time or charges)
-- This filters out permanent enchants like Crusader, Lifestealing, etc.
local hasTemporaryEnchant = hasEnchant and (expiration and expiration > 0 or charges and charges > 0)
local hasEnchant = (slot == "mh" and hasMainEnchant) or (slot == "oh" and hasOffEnchant)
if not hasEnchant then return false end
-- If no specific imbue requested, just return enchant exists
if not imbueName or imbueName == "" then return true end
-- If no specific imbue requested, return temporary enchant status
if not imbueName or imbueName == "" then
return hasTemporaryEnchant
end
-- If no temporary enchant, don't bother scanning
if not hasTemporaryEnchant then
return false
end
-- For specific imbue names, scan tooltip to match the name
-- BUT only check lines that have time markers (temporary enchants)
-- This prevents matching weapon stats like "Equip: ... critical strike ..."
-- Create tooltip scanner if needed
if not CleveRoidsTooltip then
CreateFrame("GameTooltip", "CleveRoidsTooltip", nil, "GameTooltipTemplate")
@@ -840,22 +874,33 @@ function CleveRoids.ValidateWeaponImbue(slot, imbueName)
CleveRoidsTooltip:ClearLines()
CleveRoidsTooltip:SetInventoryItem("player", slot == "mh" and 16 or 17)
-- Look for green text (temporary enchant)
-- Normalize search term once
local searchTerm = string.lower(string.gsub(imbueName, "_", " "))
-- Look for green text with time markers - check ALL green lines with time
for i = 1, CleveRoidsTooltip:NumLines() do
local text = _G["CleveRoidsTooltipTextLeft"..i]
if text then
local line = text:GetText()
local r, g, b = text:GetTextColor()
-- Green text indicates temporary enchant
if line and g > 0.8 and r < 0.2 and b < 0.2 then
-- Normalize and compare
line = string.lower(line)
imbueName = string.lower(string.gsub(imbueName, "_", " "))
return string.find(line, imbueName, 1, true) ~= nil
if line then
local r, g, b = text:GetTextColor()
-- Green text indicates enchant
if g > 0.8 and r < 0.2 and b < 0.2 then
local lowerLine = string.lower(line)
-- Only check lines with time markers (temporary enchants)
-- This skips permanent weapon stats like "Equip: ... critical strike ..."
if string.find(lowerLine, "%(") and (string.find(lowerLine, " min%)") or string.find(lowerLine, " sec%)") or string.find(lowerLine, " charge")) then
-- This is a temporary enchant line, check if it matches
if string.find(lowerLine, searchTerm, 1, true) then
return true -- Found it!
end
end
end
end
end
end
-- Checked all lines, didn't find it
return false
end
@@ -1554,6 +1599,20 @@ CleveRoids.Keywords = {
end)
end,
level = function(conditionals)
return And(conditionals.level, function(args)
if type(args) ~= "table" then return false end
return CleveRoids.ValidateLevel(conditionals.target, args.operator, args.amount)
end)
end,
mylevel = function(conditionals)
return And(conditionals.mylevel, function(args)
if type(args) ~= "table" then return false end
return CleveRoids.ValidateLevel("player", args.operator, args.amount)
end)
end,
myhp = function(conditionals)
return And(conditionals.myhp, function(args)
if type(args) ~= "table" then return false end
@@ -2020,18 +2079,114 @@ CleveRoids.Keywords = {
end,
mhimbue = function(conditionals)
return CleveRoids.ValidateWeaponImbue("mh", conditionals.value)
local imbueName = nil
-- Case 1: conditionals.mhimbue is a string (e.g., [mhimbue]Frostbrand)
if type(conditionals.mhimbue) == "string" then
imbueName = conditionals.mhimbue
-- Case 2: conditionals.mhimbue is a table (e.g., [mhimbue:Frostbrand])
elseif type(conditionals.mhimbue) == "table" and table.getn(conditionals.mhimbue) > 0 then
imbueName = conditionals.mhimbue[1] -- Use first value
-- Case 3: Boolean true means check for any imbue
elseif conditionals.mhimbue == true then
imbueName = nil -- Check for existence only
end
return CleveRoids.ValidateWeaponImbue("mh", imbueName)
end,
nomhimbue = function(conditionals)
return not CleveRoids.ValidateWeaponImbue("mh", conditionals.value)
local imbueName = nil
-- Case 1: conditionals.nomhimbue is a string
if type(conditionals.nomhimbue) == "string" then
imbueName = conditionals.nomhimbue
-- Case 2: conditionals.nomhimbue is a table
elseif type(conditionals.nomhimbue) == "table" and table.getn(conditionals.nomhimbue) > 0 then
imbueName = conditionals.nomhimbue[1]
-- Case 3: Boolean true
elseif conditionals.nomhimbue == true then
imbueName = nil
end
return not CleveRoids.ValidateWeaponImbue("mh", imbueName)
end,
ohimbue = function(conditionals)
return CleveRoids.ValidateWeaponImbue("oh", conditionals.value)
local imbueName = nil
-- Case 1: conditionals.ohimbue is a string
if type(conditionals.ohimbue) == "string" then
imbueName = conditionals.ohimbue
-- Case 2: conditionals.ohimbue is a table
elseif type(conditionals.ohimbue) == "table" and table.getn(conditionals.ohimbue) > 0 then
imbueName = conditionals.ohimbue[1]
-- Case 3: Boolean true
elseif conditionals.ohimbue == true then
imbueName = nil
end
return CleveRoids.ValidateWeaponImbue("oh", imbueName)
end,
noohimbue = function(conditionals)
return not CleveRoids.ValidateWeaponImbue("oh", conditionals.value)
local imbueName = nil
-- Case 1: conditionals.noohimbue is a string
if type(conditionals.noohimbue) == "string" then
imbueName = conditionals.noohimbue
-- Case 2: conditionals.noohimbue is a table
elseif type(conditionals.noohimbue) == "table" and table.getn(conditionals.noohimbue) > 0 then
imbueName = conditionals.noohimbue[1]
-- Case 3: Boolean true
elseif conditionals.noohimbue == true then
imbueName = nil
end
return not CleveRoids.ValidateWeaponImbue("oh", imbueName)
end,
immune = function(conditionals)
-- Check if target is immune to the spell being cast or damage school
-- Usage: [immune] SpellName OR [immune:SpellName] OR [immune:fire]
local checkValue = nil
-- Case 1: [immune:SpellName] or [immune:fire]
if type(conditionals.immune) == "table" and table.getn(conditionals.immune) > 0 then
checkValue = conditionals.immune[1]
elseif type(conditionals.immune) == "string" then
checkValue = conditionals.immune
-- Case 2: [immune] SpellName (check the action being cast)
elseif conditionals.action then
checkValue = conditionals.action
end
if not checkValue then
return false
end
return CleveRoids.CheckImmunity(conditionals.target or "target", checkValue)
end,
noimmune = function(conditionals)
-- Check if target is NOT immune to the spell being cast or damage school
-- Usage: [noimmune] SpellName OR [noimmune:SpellName] OR [noimmune:fire]
local checkValue = nil
-- Case 1: [noimmune:SpellName] or [noimmune:fire]
if type(conditionals.noimmune) == "table" and table.getn(conditionals.noimmune) > 0 then
checkValue = conditionals.noimmune[1]
elseif type(conditionals.noimmune) == "string" then
checkValue = conditionals.noimmune
-- Case 2: [noimmune] SpellName (check the action being cast)
elseif conditionals.action then
checkValue = conditionals.action
end
if not checkValue then
return true -- If we can't determine spell/school, assume not immune
end
return not CleveRoids.CheckImmunity(conditionals.target or "target", checkValue)
end
}
+126 -11
View File
@@ -459,6 +459,7 @@ function CleveRoids.TestForActiveAction(actions)
local hasActive = false
local newActiveAction = nil
local newSequence = nil
local firstUnconditional = nil
if actions.tooltip and table.getn(actions.list) == 0 then
if CleveRoids.TestAction(actions.cmd or "", actions.args or "") then
@@ -467,9 +468,20 @@ function CleveRoids.TestForActiveAction(actions)
actions.active = actions.tooltip
end
else
-- First pass: find first action with conditionals that passes
for _, action in ipairs(actions.list) do
local result = CleveRoids.TestAction(action.cmd, action.args)
-- Check if action has conditionals
local _, conditionals = CleveRoids.GetParsedMsg(action.args)
if not conditionals and not firstUnconditional then
-- Track first unconditional action as fallback
firstUnconditional = action
end
-- break on first action that passes tests
if CleveRoids.TestAction(action.cmd, action.args) then
if result then
hasActive = true
if action.sequence then
newSequence = action.sequence
@@ -481,6 +493,12 @@ function CleveRoids.TestForActiveAction(actions)
if hasActive then break end
end
end
-- If no conditional action passed, use first unconditional action
if not hasActive and firstUnconditional then
hasActive = true
newActiveAction = firstUnconditional
end
end
local changed = false
@@ -1021,6 +1039,7 @@ function CleveRoids.ParseMacro(name)
-- #showtooltip and item/spell/macro specified, only use this tooltip
if st and tt ~= "" then
showTooltipHasArg = true
for _, arg in ipairs(CleveRoids.splitStringIgnoringQuotes(tt)) do
macro.actions.tooltip = CleveRoids.CreateActionInfo(arg)
local action = CleveRoids.CreateActionInfo(CleveRoids.GetParsedMsg(arg))
@@ -1052,6 +1071,14 @@ function CleveRoids.ParseMacro(name)
end
end
-- If #showtooltip was present but had no argument, use the first action as the tooltip
if hasShowTooltip and not showTooltipHasArg and table.getn(macro.actions.list) > 0 then
macro.actions.tooltip = macro.actions.list[1]
end
-- Store whether #showtooltip had an explicit argument (for icon fallback logic)
macro.actions.explicitTooltip = showTooltipHasArg
CleveRoids.Macros[name] = macro
return macro
end
@@ -1152,6 +1179,10 @@ function CleveRoids.ParseMsg(msg)
noresting = true,
isplayer = true,
isnpc = true,
mhimbue = true,
nomhimbue = true,
ohimbue = true,
noohimbue = true,
}
if booleanConditionals[condition] then
@@ -2385,28 +2416,47 @@ CleveRoids.Hooks.GetActionTexture = GetActionTexture
function GetActionTexture(slot)
local actions = CleveRoids.GetAction(slot)
-- Check if this is one of our macros
if actions and (actions.active or actions.tooltip) then
-- This block handles the case where all conditionals fail and no explicit
-- #showtooltip was set. It defaults to the macro's chosen icon (e.g., the red '?')
if not actions.active and not actions.explicitTooltip and actions.list and table.getn(actions.list) > 0 then
local macroName = GetActionText(slot)
if macroName then
local macroID = GetMacroIndexByName(macroName)
if macroID and macroID > 0 then
local _, macroTexture = GetMacroInfo(macroID)
if macroTexture then
return macroTexture
end
end
end
return CleveRoids.unknownTexture
end
-- Prioritize active action, fall back to tooltip
local a = actions.active or actions.tooltip
-- NEW: For slot-based actions, always fetch current equipment texture
-- Handle numeric slot actions (e.g., /use 13)
local slotId = tonumber(a.action)
if slotId and slotId >= 1 and slotId <= 19 then
local currentTexture = GetInventoryItemTexture("player", slotId)
if currentTexture then
return currentTexture
end
-- No item equipped in that slot, return unknown texture
return CleveRoids.unknownTexture
end
local proxySlot = (actions.active and actions.active.spell) and CleveRoids.GetProxyActionSlot(actions.active.spell.name)
if proxySlot and CleveRoids.Hooks.GetActionTexture(proxySlot) ~= actions.active.spell.texture then
return CleveRoids.Hooks.GetActionTexture(proxySlot)
else
return (actions.active and actions.active.texture) or (actions.tooltip and actions.tooltip.texture) or CleveRoids.unknownTexture
end
-- *** THIS IS THE FIX ***
-- If an action is active, return its texture directly.
-- If no action is active, return the tooltip's texture.
-- This bypasses the 'proxySlot' check that required the spell to be on the action bar.
return (actions.active and actions.active.texture) or (actions.tooltip and actions.tooltip.texture) or CleveRoids.unknownTexture
end
-- Not one of our macros, use the original function
return CleveRoids.Hooks.GetActionTexture(slot)
end
@@ -3158,6 +3208,66 @@ SlashCmdList["CLEVEROID"] = function(msg)
return
end
-- listimmune (list immunity data)
if cmd == "listimmune" or cmd == "immunelist" then
CleveRoids.ListImmunities(val ~= "" and val or nil)
return
end
-- clearimmune (clear immunity data)
if cmd == "clearimmune" then
CleveRoids.ClearImmunities(val ~= "" and val or nil)
return
end
-- addimmune (manually add immunity)
if cmd == "addimmune" then
-- Parse: /cleveroid addimmune <NPC Name> <school> [buff]
-- Example: /cleveroid addimmune "Golemagg the Incinerator" fire
-- Example: /cleveroid addimmune Vaelastrasz fire "Burning Adrenaline"
local npcName, school, buffName = nil, nil, nil
-- Try to extract quoted NPC name
local _, _, quotedNpc, rest = string.find(msg, '^addimmune%s+"([^"]+)"%s*(.*)$')
if quotedNpc then
npcName = quotedNpc
-- Parse school and optional buff from rest
local _, _, sch, buff = string.find(rest, "^(%S+)%s*(.*)$")
school = sch
if buff and buff ~= "" then
-- Check if buff is quoted
local _, _, quotedBuff = string.find(buff, '^"([^"]+)"$')
buffName = quotedBuff or buff
end
else
-- No quoted NPC, use simple parsing
npcName = val
school = val2
end
CleveRoids.AddImmunity(npcName, school, buffName)
return
end
-- removeimmune (manually remove immunity)
if cmd == "removeimmune" then
-- Parse: /cleveroid removeimmune <NPC Name> <school>
local npcName, school = nil, nil
-- Try to extract quoted NPC name
local _, _, quotedNpc, sch = string.find(msg, '^removeimmune%s+"([^"]+)"%s*(%S*)$')
if quotedNpc then
npcName = quotedNpc
school = sch
else
npcName = val
school = val2
end
CleveRoids.RemoveImmunity(npcName, school)
return
end
-- Unknown command fallback
CleveRoids.Print("Usage:")
DEFAULT_CHAT_FRAME:AddMessage("/cleveroid - Show current settings")
@@ -3168,6 +3278,11 @@ SlashCmdList["CLEVEROID"] = function(msg)
DEFAULT_CHAT_FRAME:AddMessage("/cleveroid forget <spellID|all> - Forget learned duration(s)")
DEFAULT_CHAT_FRAME:AddMessage("/cleveroid debug [0|1] - Toggle learning debug messages")
end
DEFAULT_CHAT_FRAME:AddMessage("|cffffaa00Immunity Tracking:|r")
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid listimmune [school] - List immunity data')
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid addimmune "<NPC>" <school> [buff] - Add immunity')
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid removeimmune "<NPC>" <school> - Remove immunity')
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid clearimmune [school] - Clear immunity data')
end
SLASH_CLEAREQUIPQUEUE1 = "/clearequipqueue"
+5 -1
View File
@@ -31,7 +31,10 @@ Check slash command and all conditional lists for new usages!
* `/cleveroid refresh X` - Set refresh rate (1 to 10 updates per second. Default: 5)
* `/cleveroid learn <spellID> <duration>` - Manually set spell duration in seconds
* `/cleveroid forget <spellID|all>` - Forget learned spell duration(s)
* `/cleveroid debug [0|1]` - Toggle debug messages for spell duration learning
* `/cleveroid listimmune [school]` - List all or specific school immunities
* `/cleveroid addimmune "<NPC>" <school> [buff]` - Add manual immunity
* `/cleveroid removeimmune "<NPC>" <school>` - Remove immunity
* `/cleveroid clearimmune [school]` - Clear data
---
@@ -363,6 +366,7 @@ The system includes pre-configured durations for 329+ debuffs across all classes
| behind | [behind] | | * | If the player is behind the target.|
| insight | [insight] | | * | If the player is in line of sight of the target. |
| meleerange | [meleerange] | | * | If the player is melee range of the target.|
| immune | [immune:fire]<br/>[immune:Flame_Shock] | * | * | If the npc has immunities to a damage type or spell. check slash commands section for more information. |
### Unitids
| Name (N=party/raid slot number) |
+1 -1
View File
@@ -4,7 +4,7 @@
## Notes: /cleveroid for settings
## Version: 1.3
## OptionalDeps: ClassicFocus, FocusFrame, pfUI, SuperMacro, Bongos_ActionBar
## SavedVariables: CleveRoidMacros, CleveRoids_LearnedDurations, CleveRoids_AuraTextures
## SavedVariables: CleveRoidMacros, CleveRoids_LearnedDurations, CleveRoids_AuraTextures, CleveRoids_ImmunityData
Localization.lua
Init.lua
Utility.lua
+397
View File
@@ -996,3 +996,400 @@ evCleanup:SetScript("OnEvent", function()
end
end
end)
-- ============================================================================
-- IMMUNITY TRACKING SYSTEM
-- ============================================================================
-- Initialize SavedVariables for immunity tracking
CleveRoids_ImmunityData = CleveRoids_ImmunityData or {}
-- Spell school constants
local IMMUNITY_SCHOOLS = {
physical = 1,
holy = 2,
fire = 3,
nature = 4,
frost = 5,
shadow = 6,
arcane = 7,
}
-- Cache for spell school lookups
local spellSchoolCache = {}
-- Get the damage school of a spell
local function GetSpellSchool(spellName)
if not spellName then return nil end
-- Remove rank information for cache consistency
local baseName = string.gsub(spellName, "%s*%(.-%)%s*$", "")
-- Check cache first
if spellSchoolCache[baseName] then
return spellSchoolCache[baseName]
end
-- Try to find spell in player's spellbook
local spell = CleveRoids.GetSpell(baseName)
if not spell then
return nil
end
-- Create tooltip if needed
if not CleveRoidsSchoolTooltip then
CreateFrame("GameTooltip", "CleveRoidsSchoolTooltip", nil, "GameTooltipTemplate")
CleveRoidsSchoolTooltip:SetOwner(WorldFrame, "ANCHOR_NONE")
end
CleveRoidsSchoolTooltip:ClearLines()
CleveRoidsSchoolTooltip:SetSpell(spell.spellSlot, spell.bookType)
-- Scan tooltip for school keywords
local school = nil
for i = 1, CleveRoidsSchoolTooltip:NumLines() do
local line = _G["CleveRoidsSchoolTooltipTextLeft" .. i]
if line then
local text = string.lower(line:GetText() or "")
if string.find(text, "fire") or string.find(text, "flame") then
school = "fire"
break
elseif string.find(text, "frost") or string.find(text, "ice") then
school = "frost"
break
elseif string.find(text, "nature") or string.find(text, "poison") then
school = "nature"
break
elseif string.find(text, "shadow") or string.find(text, "dark") then
school = "shadow"
break
elseif string.find(text, "arcane") then
school = "arcane"
break
elseif string.find(text, "holy") or string.find(text, "divine") then
school = "holy"
break
end
end
end
-- Fallback: Use spell name patterns for common spells
if not school then
local lower = string.lower(baseName)
if string.find(lower, "fire") or string.find(lower, "flame") or string.find(lower, "immolat") or string.find(lower, "scorch") then
school = "fire"
elseif string.find(lower, "frost") or string.find(lower, "ice") or string.find(lower, "blizzard") then
school = "frost"
elseif string.find(lower, "nature") or string.find(lower, "poison") or string.find(lower, "sting") then
school = "nature"
elseif string.find(lower, "shadow") or string.find(lower, "curse") or string.find(lower, "corruption") then
school = "shadow"
elseif string.find(lower, "arcane") then
school = "arcane"
elseif string.find(lower, "holy") or string.find(lower, "smite") or string.find(lower, "exorcism") then
school = "holy"
else
school = "physical"
end
end
-- Cache the result
spellSchoolCache[baseName] = school
return school
end
-- Get current buffs on a unit
local function GetUnitBuffs(unit)
local buffs = {}
if not CleveRoids.hasSuperwow then return buffs end
for i = 1, 32 do
local texture, stacks, spellID = UnitBuff(unit, i)
if not texture then break end
if spellID then
local buffName = SpellInfo(spellID)
if buffName then
buffs[buffName] = true
end
end
end
return buffs
end
-- Record an immunity (permanent or buff-based)
local function RecordImmunity(npcName, spellName, conditionalBuff)
if not npcName or not spellName or npcName == "" then
return
end
local school = GetSpellSchool(spellName)
if not school then
return
end
-- Initialize school table
if not CleveRoids_ImmunityData[school] then
CleveRoids_ImmunityData[school] = {}
end
-- Record immunity
if conditionalBuff then
-- Buff-based immunity
CleveRoids_ImmunityData[school][npcName] = {
buff = conditionalBuff
}
if CleveRoids.debug then
CleveRoids.Print("|cffff6600Immunity:|r " .. npcName .. " is immune to " .. school .. " when buffed with: " .. conditionalBuff)
end
else
-- Permanent immunity
if CleveRoids_ImmunityData[school][npcName] ~= true then
CleveRoids_ImmunityData[school][npcName] = true
if CleveRoids.debug then
CleveRoids.Print("|cffff6600Immunity:|r " .. npcName .. " is permanently immune to " .. school)
end
end
end
end
-- Combat log parser for immunity detection
local function ParseImmunityCombatLog()
local message = arg1 -- Formatted message
local rawMessage = arg2 -- Raw message
if not rawMessage then return end
-- Pattern: "X's SpellName fails. Y is immune."
local spellName = nil
local targetName = nil
-- Extract spell name from raw message
local _, _, extractedSpell = string.find(rawMessage, "'s%s+(.-)%s+fails%.")
if not extractedSpell then
_, _, extractedSpell = string.find(rawMessage, "Your%s+(.-)%s+fails%.")
end
-- Extract target name from formatted message (more reliable)
if message then
local _, _, extractedTarget = string.find(message, "fails%.%s+(.-)%s+is immune")
if extractedTarget then
targetName = extractedTarget
end
end
-- Fallback: Try to get target from current target
if not targetName and UnitExists("target") then
local _, targetGUID = UnitExists("target")
if targetGUID and rawMessage and string.find(rawMessage, targetGUID) then
targetName = UnitName("target")
end
end
if extractedSpell and targetName then
-- Check if target has any buffs (for conditional immunity)
local buffs = nil
if UnitExists("target") and UnitName("target") == targetName then
buffs = GetUnitBuffs("target")
end
-- If target has exactly one buff, assume it's causing the immunity
if buffs and next(buffs) then
local buffCount = 0
local singleBuff = nil
for buff, _ in pairs(buffs) do
buffCount = buffCount + 1
singleBuff = buff
if buffCount > 1 then
singleBuff = nil
break
end
end
if singleBuff then
RecordImmunity(targetName, extractedSpell, singleBuff)
return
end
end
-- No single buff detected, record as permanent immunity
RecordImmunity(targetName, extractedSpell, nil)
end
end
-- Check if a unit is immune to a spell or damage school
-- Supports: CheckImmunity(unitId, "Flame Shock") or CheckImmunity(unitId, "fire")
function CleveRoids.CheckImmunity(unitId, spellOrSchool)
if not unitId or not UnitExists(unitId) then
return false
end
-- Only works on NPCs
if UnitIsPlayer(unitId) then
return false
end
local targetName = UnitName(unitId)
if not targetName or targetName == "" then
return false
end
if not spellOrSchool or spellOrSchool == "" then
return false
end
-- Check if input is a spell school name directly
local inputLower = string.lower(spellOrSchool)
local school = nil
if IMMUNITY_SCHOOLS[inputLower] then
-- Input is a damage school name (fire, frost, nature, etc.)
school = inputLower
else
-- Input is a spell name, need to determine its school
school = GetSpellSchool(spellOrSchool)
if not school then
return false
end
end
-- Check immunity data for this school
if not CleveRoids_ImmunityData[school] then
return false
end
local immunityData = CleveRoids_ImmunityData[school][targetName]
-- No immunity data for this NPC
if not immunityData then
return false
end
-- Permanent immunity
if immunityData == true then
return true
end
-- Buff-based immunity (check if NPC has the required buff)
if type(immunityData) == "table" and immunityData.buff then
local requiredBuff = immunityData.buff
-- Check target's buffs
if CleveRoids.hasSuperwow then
for i = 1, 32 do
local texture, stacks, spellID = UnitBuff(unitId, i)
if not texture then break end
if spellID then
local buffName = SpellInfo(spellID)
if buffName and buffName == requiredBuff then
return true
end
end
end
end
-- Buff not found, not currently immune
return false
end
return false
end
-- Management functions for immunity data
function CleveRoids.ListImmunities(school)
if school then
school = string.lower(school)
if not CleveRoids_ImmunityData[school] then
CleveRoids.Print("No immunity data for school: " .. school)
return
end
CleveRoids.Print("|cff00ff00" .. school .. " immunities:|r")
local count = 0
for npc, data in pairs(CleveRoids_ImmunityData[school]) do
if data == true then
CleveRoids.Print(" - " .. npc .. " (permanent)")
elseif type(data) == "table" and data.buff then
CleveRoids.Print(" - " .. npc .. " (when buffed: " .. data.buff .. ")")
end
count = count + 1
end
CleveRoids.Print("Total: " .. count)
else
-- List all schools
CleveRoids.Print("|cff00ff00Immunity Data by School:|r")
for schoolName, npcs in pairs(CleveRoids_ImmunityData) do
local count = 0
for _ in pairs(npcs) do
count = count + 1
end
if count > 0 then
CleveRoids.Print(" " .. schoolName .. ": " .. count .. " NPCs")
end
end
end
end
function CleveRoids.ClearImmunities(school)
if school then
school = string.lower(school)
CleveRoids_ImmunityData[school] = {}
CleveRoids.Print("Cleared " .. school .. " immunity data")
else
CleveRoids_ImmunityData = {}
CleveRoids.Print("Cleared all immunity data")
end
end
function CleveRoids.AddImmunity(npcName, school, buffName)
if not npcName or not school then
CleveRoids.Print("Usage: /cleveroid addimmune <npc name> <school> [buff name]")
CleveRoids.Print("Schools: fire, frost, nature, shadow, arcane, holy, physical")
return
end
school = string.lower(school)
if not IMMUNITY_SCHOOLS[school] then
CleveRoids.Print("Invalid school. Use: fire, frost, nature, shadow, arcane, holy, physical")
return
end
if not CleveRoids_ImmunityData[school] then
CleveRoids_ImmunityData[school] = {}
end
if buffName and buffName ~= "" then
CleveRoids_ImmunityData[school][npcName] = { buff = buffName }
CleveRoids.Print("Added: " .. npcName .. " is immune to " .. school .. " when buffed with: " .. buffName)
else
CleveRoids_ImmunityData[school][npcName] = true
CleveRoids.Print("Added: " .. npcName .. " is permanently immune to " .. school)
end
end
function CleveRoids.RemoveImmunity(npcName, school)
if not npcName or not school then
CleveRoids.Print("Usage: /cleveroid removeimmune <npc name> <school>")
return
end
school = string.lower(school)
if CleveRoids_ImmunityData[school] and CleveRoids_ImmunityData[school][npcName] then
CleveRoids_ImmunityData[school][npcName] = nil
CleveRoids.Print("Removed: " .. npcName .. " from " .. school .. " immunities")
else
CleveRoids.Print("Not found: " .. npcName .. " in " .. school .. " immunities")
end
end
-- Register combat log event for immunity tracking
local immunityFrame = CreateFrame("Frame", "CleveRoidsImmunityFrame")
immunityFrame:RegisterEvent("RAW_COMBATLOG")
immunityFrame:SetScript("OnEvent", function()
if event == "RAW_COMBATLOG" then
ParseImmunityCombatLog()
end
end)