mirror of
https://github.com/brues-code/SuperCleveRoidMacros.git
synced 2026-09-16 03:38:00 +00:00
@@ -1,2 +0,0 @@
|
||||
wow-api-type-definitions/
|
||||
.gitignore
|
||||
+173
-18
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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) |
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user