From cc12682c765d2b067bd1c3cbd411e521687482aa Mon Sep 17 00:00:00 2001 From: Jason Gagner Date: Sun, 21 Sep 2025 11:16:04 -0400 Subject: [PATCH 1/8] New conditionals level and mylevel --- Conditionals.lua | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Conditionals.lua b/Conditionals.lua index 0f31579..f22c96d 100644 --- a/Conditionals.lua +++ b/Conditionals.lua @@ -300,6 +300,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 @@ -1162,6 +1173,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 From 92562ab1a47a681c0fe9250a4920b755223d255f Mon Sep 17 00:00:00 2001 From: Jrc13245 Date: Thu, 13 Nov 2025 15:46:00 -0500 Subject: [PATCH 2/8] Fix macro icons with syntax not using conditionals and fix imbue conditionals --- Conditionals.lua | 122 ++++++++++++++++++++++++++++++++++++++++------- Core.lua | 51 +++++++++++++++++++- 2 files changed, 154 insertions(+), 19 deletions(-) diff --git a/Conditionals.lua b/Conditionals.lua index f8f1554..8475a1c 100644 --- a/Conditionals.lua +++ b/Conditionals.lua @@ -821,15 +821,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 +863,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 @@ -2020,18 +2054,70 @@ 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 } diff --git a/Core.lua b/Core.lua index 2d61106..968962e 100644 --- a/Core.lua +++ b/Core.lua @@ -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 @@ -2386,6 +2417,24 @@ function GetActionTexture(slot) local actions = CleveRoids.GetAction(slot) if actions and (actions.active or actions.tooltip) then + -- If #showtooltip had NO argument and all conditionals failed, show macro icon + -- (Get the macro's default texture instead of tooltip or unknown) + if not actions.active and not actions.explicitTooltip and actions.list and table.getn(actions.list) > 0 then + -- Return the macro's default icon + 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 + -- Fallback to unknown if we can't get macro texture + return CleveRoids.unknownTexture + end + -- Prioritize active action, fall back to tooltip local a = actions.active or actions.tooltip From 122fc0a9081f709433a5d1ac0e07aad72b1d99f3 Mon Sep 17 00:00:00 2001 From: Jrc13245 Date: Thu, 13 Nov 2025 16:40:37 -0500 Subject: [PATCH 3/8] no longer require abilities on actionbar to get icon --- Core.lua | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/Core.lua b/Core.lua index 968962e..24b01d9 100644 --- a/Core.lua +++ b/Core.lua @@ -1075,7 +1075,7 @@ function CleveRoids.ParseMacro(name) 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 @@ -2416,11 +2416,12 @@ 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 - -- If #showtooltip had NO argument and all conditionals failed, show macro icon - -- (Get the macro's default texture instead of tooltip or unknown) + + -- 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 - -- Return the macro's default icon local macroName = GetActionText(slot) if macroName then local macroID = GetMacroIndexByName(macroName) @@ -2431,31 +2432,31 @@ function GetActionTexture(slot) end end end - -- Fallback to unknown if we can't get macro texture 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 From 96e3c9b90ed6f8dc8c386a91cf7218074521118c Mon Sep 17 00:00:00 2001 From: Torio <41833378+jrc13245@users.noreply.github.com> Date: Sun, 16 Nov 2025 09:46:37 -0500 Subject: [PATCH 4/8] Delete .gitignore --- .gitignore | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 .gitignore diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 598ccb7..0000000 --- a/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -wow-api-type-definitions/ -.gitignore -CLAUDE.md From 1610920dda01ac2c1a394e682a91d4340b0275ec Mon Sep 17 00:00:00 2001 From: Jrc13245 Date: Sun, 16 Nov 2025 09:46:51 -0500 Subject: [PATCH 5/8] immunity tracking system --- .gitignore | 1 + Conditionals.lua | 44 +++++ Core.lua | 65 +++++++ SuperCleveRoidMacros.toc | 2 +- Utility.lua | 397 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 508 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7d097d6..598ccb7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ wow-api-type-definitions/ .gitignore +CLAUDE.md diff --git a/Conditionals.lua b/Conditionals.lua index f9f07ef..03b2598 100644 --- a/Conditionals.lua +++ b/Conditionals.lua @@ -2144,5 +2144,49 @@ CleveRoids.Keywords = { 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 } diff --git a/Core.lua b/Core.lua index 24b01d9..110d35a 100644 --- a/Core.lua +++ b/Core.lua @@ -3208,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 [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 + 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") @@ -3218,6 +3278,11 @@ SlashCmdList["CLEVEROID"] = function(msg) DEFAULT_CHAT_FRAME:AddMessage("/cleveroid forget - 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 "" [buff] - Add immunity') + DEFAULT_CHAT_FRAME:AddMessage('/cleveroid removeimmune "" - Remove immunity') + DEFAULT_CHAT_FRAME:AddMessage('/cleveroid clearimmune [school] - Clear immunity data') end SLASH_CLEAREQUIPQUEUE1 = "/clearequipqueue" diff --git a/SuperCleveRoidMacros.toc b/SuperCleveRoidMacros.toc index 87ea771..feb25dc 100644 --- a/SuperCleveRoidMacros.toc +++ b/SuperCleveRoidMacros.toc @@ -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 diff --git a/Utility.lua b/Utility.lua index 2d9d101..7ad8c6b 100644 --- a/Utility.lua +++ b/Utility.lua @@ -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 [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 ") + 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) From 93c73e8d933b238b99829061b0bb987dce1e03da Mon Sep 17 00:00:00 2001 From: Jrc13245 Date: Sun, 16 Nov 2025 09:56:54 -0500 Subject: [PATCH 6/8] update readme --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4ec847a..7540f2d 100644 --- a/README.md +++ b/README.md @@ -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 ` - Manually set spell duration in seconds * `/cleveroid forget ` - 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 "" [buff]` - Add manual immunity +* `/cleveroid removeimmune "" ` - 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]
[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) | From 68ea467e1ac85a8ce9bef361f79cd74a30010eb3 Mon Sep 17 00:00:00 2001 From: Jrc13245 Date: Sun, 16 Nov 2025 10:02:38 -0500 Subject: [PATCH 7/8] fix gitignore --- gitignore | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 gitignore diff --git a/gitignore b/gitignore deleted file mode 100644 index 598ccb7..0000000 --- a/gitignore +++ /dev/null @@ -1,3 +0,0 @@ -wow-api-type-definitions/ -.gitignore -CLAUDE.md From edfb44edd7f93526c31f054ae10e5fa72f1687df Mon Sep 17 00:00:00 2001 From: Jrc13245 Date: Sun, 16 Nov 2025 10:03:56 -0500 Subject: [PATCH 8/8] another fix --- CLAUDE.md | 362 --------------------------------------- wow-api-type-definitions | 1 - 2 files changed, 363 deletions(-) delete mode 100644 CLAUDE.md delete mode 160000 wow-api-type-definitions diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index d895411..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,362 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Project Overview - -SuperCleveRoidMacros is a World of Warcraft 1.12.1 (Vanilla) addon that provides enhanced macro functionality with dynamic tooltips, conditional execution, and extended syntax. It requires three DLL mods: **SuperWoW**, **Nampower**, and **UnitXP_SP3** - all are mandatory dependencies. - -This addon is built for Turtle WoW (TWoW) private server and combines features from CleverMacro and Roid-Macros with significant extensions. - -## Critical Requirements - -### Required DLL Mods -The addon **WILL NOT FUNCTION** without all three DLL mods installed: -1. **SuperWoW** (balakethelock) - Provides extended API functions like `SetAutoloot`, `SpellInfo()`, `UnitBuff/UnitDebuff` with spell IDs -2. **Nampower** - Provides `QueueSpellByName`, `IsSpellInRange`, `GetCurrentCastingInfo`, and spell queueing -3. **UnitXP_SP3** (konaka) - Provides `UnitXP()` for distance/positioning checks - -The addon checks for these on load in `Core.lua:18-48` and will disable itself with error messages if any are missing. - -## Code Architecture - -### File Loading Order (SuperCleveRoidMacros.toc) -Files are loaded in this exact order, which is critical for initialization: -1. `Localization.lua` - Sets up `CleveRoids.Localized` for multi-language support -2. `Init.lua` - Initializes the `CleveRoids` global table and environment flags -3. `Utility.lua` - Utility functions, string parsing, comparators, and the debuff tracking library (`CleveRoids.libdebuff`) -4. `Core.lua` - Main addon logic, macro parsing, action evaluation, UI hooks -5. `Conditionals.lua` - Conditional keyword validation functions (`CleveRoids.Keywords`) -6. `Console.lua` - Slash command handlers -7. `ExtensionsManager.lua` - Extension/plugin system for modular features -8. Compatibility layers for other addons (SuperMacro, pfUI, Bongos) -9. Extension modules (MacroLengthWarn, Mouseover integrations, Tooltips) - -### Key Global Objects - -**`CleveRoids`** - Main global namespace containing: -- `.playerClass` - Player's class (set in `Console.lua`) -- `.Spells`, `.Items`, `.Talents` - Indexed lookups for spells/items/talents -- `.Actions` - Parsed macro data per action button -- `.Sequences` - Cast sequence state tracking -- `.Keywords` - Conditional validation function table (from `Conditionals.lua`) -- `.libdebuff` - Debuff duration tracking system (SuperWoW-based) -- `.hasSuperwow`, `.hasNampower`, `.hasUnitXP` - Feature detection flags -- `.mouseoverUnit` - Current mouseover unit (managed by Extensions) - -**`CleveRoidMacros`** - SavedVariables configuration: -- `.realtime` - 0 = event-based updates, 1 = continuous updates (default: 0) -- `.refresh` - Update rate in Hz when realtime=1 (1-10, default: 5) - -**`CleveRoids_LearnedDurations`** - SavedVariables for learned debuff durations (per-caster GUID) - -**`CleveRoids_ImmunityData`** - SavedVariables for NPC immunity tracking (organized by damage school) - -### Core Systems - -#### 1. Conditional System (`Conditionals.lua`) -The `CleveRoids.Keywords` table maps conditional keywords to validation functions. Each function receives a `conditionals` table containing: -- `.target` - The UnitID being evaluated (default: "target") -- `.help` / `.harm` - Friendly/enemy flag -- `.action` - The spell/item name being cast -- Additional conditional-specific values (e.g., `.mod`, `.stance`, `.buff`) - -**Multi-value conditionals** (marked as "Multi" in README): -- Use `Or()` helper - returns true if ANY value matches -- Example: `[zone:Stormwind/Ironforge]` → true if in either zone - -**Negatable conditionals** (marked as "Noable"): -- Use `And()` helper - ALL must be false -- Example: `[nozone:Stormwind/Ironforge]` → true only if in neither zone - -**Numeric comparisons** (hp, power, cooldown, etc.): -- Use `CleveRoids.operators` and `CleveRoids.comparators` tables -- Operators: `<`, `>`, `=`, `<=`, `>=`, `~=` (and string aliases: `lt`, `gt`, `eq`, etc.) -- Format in args table: `{operator = ">", amount = 50}` - -#### 2. Debuff Timer System (`Utility.lua:328-998`) -The `CleveRoids.libdebuff` system provides accurate debuff tracking: -- Pre-configured durations for 329+ vanilla spells (see `lib.durations`) -- Auto-learning via `UNIT_CASTEVENT` (cast start) and `RAW_COMBATLOG` (fade detection) -- Per-caster GUID storage in `CleveRoids_LearnedDurations` (handles talent variations) -- GUID-based tracking (supports multiple mobs with same name) -- Fallback to static database for unknown spells - -**Key functions:** -- `lib:GetDuration(spellID, casterGUID)` - Returns learned or static duration -- `lib:AddEffect(guid, unitName, spellID, duration, stacks, caster)` - Track new debuff -- `lib:UnitDebuff(unit, id)` - Returns: name, _, texture, stacks, dtype, duration, timeleft, caster - -#### 3. Immunity Tracking System (`Utility.lua:1000-1380`) -The immunity tracking system automatically learns and tracks NPC immunities from combat log messages: -- **Auto-learning** from combat log: "X's Spell fails. Y is immune." -- Stores immunities by **damage school** (fire, frost, nature, shadow, arcane, holy, physical) in `CleveRoids_ImmunityData` -- Supports both **permanent immunities** and **buff-based immunities** (e.g., boss immune during a shield phase) -- Automatic spell school detection via tooltip scanning and name pattern matching - -**Data Structure:** -```lua -CleveRoids_ImmunityData = { - fire = { - ["Ragnaros"] = true, -- Permanent immunity - ["Vaelastrasz the Corrupt"] = { buff = "Burning Adrenaline" } -- Conditional immunity - }, - frost = { - ["Frozen Core"] = true - } -} -``` - -**Key Functions:** -- `CleveRoids.CheckImmunity(unitId, spellOrSchool)` - Check immunity to spell name OR damage school - - Example: `CheckImmunity("target", "Flame Shock")` or `CheckImmunity("target", "fire")` - - Automatically detects buff-based immunity by checking target's current buffs -- `CleveRoids.ListImmunities(school)` - List immunity data (all or specific school) -- `CleveRoids.AddImmunity(npcName, school, buffName)` - Manually add immunity -- `CleveRoids.RemoveImmunity(npcName, school)` - Remove immunity entry -- `CleveRoids.ClearImmunities(school)` - Clear immunity data - -**Combat Log Parsing:** -- Listens to `RAW_COMBATLOG` event for immunity messages -- Extracts spell name and target name from messages -- If target has exactly one buff when immunity occurs, assumes buff causes immunity -- Otherwise records as permanent immunity - -**Console Commands:** -```bash -/cleveroid listimmune [school] # List all or specific school immunities -/cleveroid addimmune "" [buff] # Add manual immunity -/cleveroid removeimmune "" # Remove immunity -/cleveroid clearimmune [school] # Clear data -``` - -**Conditional Usage:** -- `[immune:fire]` or `[immune:Flame Shock]` - Check if target is immune -- `[noimmune:nature]` or `[noimmune:"Serpent Sting"]` - Check if NOT immune -- Can omit spell/school to check the action being cast: `[noimmune] Fireball` - -#### 4. Macro Parsing and Execution -Macros are parsed in `Core.lua` into action lists. Each action has: -- `.cmd` - Command (e.g., "/cast", "/use", "/castsequence") -- `.args` - Arguments string -- `.conditionals` - Parsed conditional table -- `.sequence` - Cast sequence data (if `/castsequence`) - -**Evaluation flow:** -1. `CleveRoids.TestForActiveAction(actions)` iterates action list -2. For each action, `CleveRoids.TestAction(cmd, args)` evaluates conditionals -3. First passing action becomes `.active` and determines icon/tooltip -4. `#showtooltip` at macro start enables dynamic icon/tooltip updates - -#### 5. Equipment Swapping System (`Core.lua:57-227`) -Implements queued equipment changes with cooldowns: -- Per-slot cooldown: 1.5s (`EQUIP_COOLDOWN`) -- Global cooldown: 0.5s (`EQUIP_GLOBAL_COOLDOWN`) -- Combat weapon swap protection (checks casting state, on-swing spells) -- `CleveRoids.QueueEquipItem(item, slotName)` - Queue swap -- `CleveRoids.ProcessEquipmentQueue()` - Process queue (called from OnUpdate) - -#### 6. Extension System (`ExtensionsManager.lua`) -Modular plugin architecture for addon integrations: -- `CleveRoids.RegisterExtension(name)` creates extension object -- Extensions can register events, hook functions/methods -- Mouseover extensions set `CleveRoids.SetMouseoverFrom(source, unit)` with priority system (pfUI > blizz > tooltip) -- Compatibility extensions handle SuperMacro, pfUI MacroTweak, Bongos action bars - -## Common Development Tasks - -### Adding a New Conditional Keyword - -1. **Add validation function to `Conditionals.lua`:** -```lua -CleveRoids.Keywords = { - -- Existing conditionals... - - mynewconditional = function(conditionals) - -- Access parsed values from conditionals table - local checkValue = conditionals.mynewconditional - - -- For multi-value support, use Or() helper: - return Or(checkValue, function(val) - return YourCheckFunction(val) - end) - end, - - nomynewconditional = function(conditionals) - -- Negated version uses And() helper: - return And(conditionals.nomynewconditional, function(val) - return not YourCheckFunction(val) - end) - end, -} -``` - -2. **Update README.md** with syntax, examples, and whether it's Multi/Noable - -### Adding Support for a New Spell Duration - -1. **Add to static database in `Utility.lua`:** -```lua -lib.durations = lib.durations or { - -- Existing durations... - [12345] = 30, -- Spell Name (Rank X) - duration in seconds -} -``` - -2. **Or let players learn it manually:** -``` -/cleveroid learn 12345 30 -``` - -The system will auto-learn durations as players cast spells if not in database. - -### Modifying Mouseover Behavior - -Mouseover unit determination uses a priority system in `Utility.lua:94-145`: -- Priority levels: pfUI (3), blizzard (3), tooltip (1) -- Extensions call `CleveRoids.SetMouseoverFrom(source, unit)` to set mouseover -- Highest priority source wins -- `CleveRoids.ClearMouseoverFrom(source, unitIfMatch)` to remove - -To add new source: -1. Create extension in `Extensions/Mouseover/YourAddon.lua` -2. Hook the addon's frame OnEnter/OnLeave scripts -3. Call `SetMouseoverFrom("youraddon", unitID)` with appropriate priority - -### Testing Conditionals - -Use `/dump` command to inspect values: -```lua -/script DEFAULT_CHAT_FRAME:AddMessage(tostring(CleveRoids.ValidateHp("target", "<=", 50))) -``` - -Check `CleveRoids.Keywords` table for function existence: -```lua -/script DEFAULT_CHAT_FRAME:AddMessage(tostring(CleveRoids.Keywords.myconditional ~= nil)) -``` - -## Important Implementation Details - -### String Parsing and Quotation Handling -- Spell/item names with spaces can use underscores OR quotes: `[buff:Mark_of_the_Wild]` or `[buff:"Mark of the Wild"]` -- The parser in `Core.lua` uses `CleveRoids.splitStringIgnoringQuotes()` to handle quoted strings -- Rank syntax MUST include parentheses: `Faerie Fire (Feral)(Rank 4)` (not just `Faerie Fire (Feral)`) - -### Macro Icon/Tooltip Updates -- Icons update via `CleveRoids.QueueActionUpdate()` which sets `isActionUpdateQueued = true` -- OnUpdate handler processes queue and calls `ActionButton_Update()` on affected buttons -- If `realtime=0` (default), updates only on events (ACTIONBAR_UPDATE, UNIT_AURA, etc.) -- If `realtime=1`, updates continuously at `refresh` rate (CPU intensive) - -### Spell Cooldown Handling -- `CleveRoids.GetSpellCooldown(spellName, ignoreGCD)` returns absolute expiry time -- `ignoreGCD=true` treats 1.5s cooldowns as "not on cooldown" (GCD) -- Item cooldowns are separate: `CleveRoids.GetItemCooldown(item)` returns remaining seconds - -### Cast Sequence Reset Logic -Cast sequences reset on: -- `reset=X` - X seconds of inactivity -- `reset=target` - Target change -- `reset=combat` - Leaving combat -- `reset=mod` - Modifier key press -- Can combine: `reset=3/target/combat` - -State tracked in `CleveRoids.Sequences` by macro slot. - -## File Structure Patterns - -### Adding New Compatibility Layer -Create `Compatibility/YourAddon.lua`: -```lua -local _G = _G or getfenv(0) -local CleveRoids = _G.CleveRoids or {} - -if not YourAddon then return end -- Check if addon exists - --- Your compatibility code here --- Usually hooks or event handlers to prevent conflicts - -_G["CleveRoids"] = CleveRoids -``` - -Add to `.toc` file in Compatibility section. - -### Adding New Extension -Create `Extensions/YourExtension.lua`: -```lua -local extension = CleveRoids.RegisterExtension("YourExtension") - -extension.OnLoad = function() - -- Initialization code - extension.RegisterEvent("SOME_EVENT", "OnSomeEvent") -end - -extension.OnSomeEvent = function() - -- Event handler using 'event', 'arg1', etc. globals -end - -return extension -``` - -Add to `.toc` file in Extensions section. - -## Performance Considerations - -- **Avoid `realtime=1` unless necessary** - Event-based updates (realtime=0) are significantly more efficient -- **Debuff table cleanup** - Runs on target change, throttled to 2s intervals (see `Utility.lua:948`) -- **Spell/item indexing** - Only rebuilt on SPELLS_CHANGED, LEARNED_SPELL_IN_TAB events -- **Reactive spell detection** - Requires abilities on action bars to detect usability (limitation may be removed with Nampower enhancements) - -## Console Commands - -Defined in `Console.lua`: - -```bash -# View/modify settings -/cleveroid # Show current settings -/cleveroid realtime 0|1 # Toggle realtime updates -/cleveroid refresh 1-10 # Set update rate (Hz) - -# Debuff duration learning -/cleveroid learn # Manually set duration (seconds) -/cleveroid forget # Forget learned duration(s) -/cleveroid debug 0|1 # Toggle learning debug messages -``` - -## Debugging Tips - -1. **Enable debug mode for debuff learning:** - ``` - /cleveroid debug 1 - ``` - Shows learned durations in chat as debuffs fade. - -2. **Check for missing dependencies:** - Look for error messages on login mentioning SuperWoW/Nampower/UnitXP. - -3. **Macro syntax errors:** - - Check for unmatched quotes or brackets - - Verify spell/item names are spelled correctly (case-sensitive) - - Use `#showtooltip` to see which action is evaluating as active - -4. **Icon not updating:** - - Ensure macro starts with `#showtooltip` - - Check if `realtime=0` and conditional needs event trigger - - Verify action bar addon compatibility (see Extensions) - -## Localization - -The `Localization.lua` file provides `CleveRoids.Localized` table with: -- `.Spells` - Localized spell names (keyed by English name) -- `.CreatureTypes` - Creature type translations -- `.Dagger`, `.Sword`, `.Shield`, etc. - Weapon type names - -Always use `CleveRoids.Localized.Spells["EnglishName"]` for hardcoded spell references to support non-English clients. - -## Known Limitations - -1. **Reactive abilities** (Revenge, Overpower, etc.) must be on action bars for detection (workaround may be possible with Nampower) -2. **Aquatic Form** detection uses reactive system (must be on non-stance action bar) -3. **Debuff time-left conditionals** only work on player's own debuffs unless pfUI or internal libdebuff has data -4. **Macro names** must be unique - no blank names, duplicates, or using spell names -5. **Parenthesis in spell names** (e.g., "Faerie Fire (Feral)") MUST include rank: "Faerie Fire (Feral)(Rank X)" diff --git a/wow-api-type-definitions b/wow-api-type-definitions deleted file mode 160000 index e566a0e..0000000 --- a/wow-api-type-definitions +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e566a0ef067e10a17cf23c41c83137056c232423