diff --git a/Conditionals.lua b/Conditionals.lua index 4f1b2aa..e2ecfe9 100644 --- a/Conditionals.lua +++ b/Conditionals.lua @@ -2286,12 +2286,16 @@ function CleveRoids.IsReactiveUsable(spellName) -- For other reactive spells, use fallback methods -- Use Nampower's IsSpellUsable if available (more accurate) if IsSpellUsable then - local usable, oom = IsSpellUsable(spellName) - if usable == 1 and oom ~= 1 then - return 1 - else - return nil, oom + -- pcall to handle spells not in spellbook (Nampower throws error) + local ok, usable, oom = pcall(IsSpellUsable, spellName) + if ok then + if usable == 1 and oom ~= 1 then + return 1 + else + return nil, oom + end end + -- If pcall failed, spell not in spellbook - fall through to action bar check end -- Fallback to action bar slot checking (requires correct stance) @@ -2312,8 +2316,12 @@ function CleveRoids.CheckSpellUsable(spellName) -- Use Nampower's IsSpellUsable if available if IsSpellUsable then - local usable, oom = IsSpellUsable(spellName) - return (usable == 1 and oom ~= 1) + -- pcall to handle spells not in spellbook (Nampower throws error) + local ok, usable, oom = pcall(IsSpellUsable, spellName) + if ok then + return (usable == 1 and oom ~= 1) + end + -- If pcall failed, spell not in spellbook - fall through to fallback end -- Fallback: check if spell exists and player has mana/rage/energy diff --git a/Core.lua b/Core.lua index 0d28968..8c405d1 100644 --- a/Core.lua +++ b/Core.lua @@ -884,7 +884,11 @@ function CleveRoids.TestForActiveAction(actions) -- Check if spell is usable first (handles forms, stances, and power type correctly) local isUsableBySpell, notEnoughPower = nil, nil if IsSpellUsable then - isUsableBySpell, notEnoughPower = IsSpellUsable(actions.active.action) + -- pcall to handle spells not in spellbook (Nampower throws error) + local ok, usable, oom = pcall(IsSpellUsable, actions.active.action) + if ok then + isUsableBySpell, notEnoughPower = usable, oom + end end -- For OOM check, use proper mana source @@ -959,8 +963,9 @@ function CleveRoids.TestForActiveAction(actions) end -- Use Nampower's IsSpellUsable if available (stance-aware fallback) elseif IsSpellUsable then - local usable, oom = IsSpellUsable(actions.active.action) - if usable == 1 and oom ~= 1 then + -- pcall to handle spells not in spellbook (Nampower throws error) + local ok, usable, oom = pcall(IsSpellUsable, actions.active.action) + if ok and usable == 1 and oom ~= 1 then actions.active.usable = (pfUI and pfUI.bars) and nil or 1 else actions.active.usable = nil @@ -1657,6 +1662,11 @@ function CleveRoids.ParseMsg(msg) local spamCond = CleveRoids.GetSpammableConditional(action) if spamCond then conditionals[spamCond] = { action } + -- Also create _groups entry for consistency with Multi() + if not conditionals._groups then + conditionals._groups = {} + end + conditionals._groups[spamCond] = { { values = { action }, operator = "OR" } } end end if cancelAura ~= "" then @@ -1674,6 +1684,11 @@ function CleveRoids.ParseMsg(msg) local spamCond = CleveRoids.GetSpammableConditional(action) if spamCond then conditionals[spamCond] = { action } + -- Also create _groups entry so Multi() finds it when combined with explicit conditionals + if not conditionals._groups then + conditionals._groups = {} + end + conditionals._groups[spamCond] = { { values = { action }, operator = "OR" } } end end if cancelAura and cancelAura ~= "" then @@ -3391,6 +3406,7 @@ end CleveRoids.Hooks.PickupAction = PickupAction function PickupAction(slot) + if not slot then return end CleveRoids.ClearAction(slot) CleveRoids.ClearSlot(CleveRoids.actionSlots, slot) CleveRoids.ClearAction(CleveRoids.reactiveSlots, slot) @@ -3399,6 +3415,7 @@ end CleveRoids.Hooks.ActionHasRange = ActionHasRange function ActionHasRange(slot) + if not slot then return nil end local actions = CleveRoids.GetAction(slot) -- Only override range when #showtooltip is present and we have valid range data -- inRange == -1 means IsSpellInRange couldn't determine (channeled spells, etc.) @@ -3419,6 +3436,7 @@ end CleveRoids.Hooks.IsActionInRange = IsActionInRange function IsActionInRange(slot, unit) + if not slot then return nil end local actions = CleveRoids.GetAction(slot) -- Only override range when #showtooltip is present and we have valid range data -- inRange == -1 means IsSpellInRange couldn't determine (channeled spells, etc.) @@ -3440,6 +3458,7 @@ end CleveRoids.Hooks.OriginalIsUsableAction = IsUsableAction CleveRoids.Hooks.IsUsableAction = IsUsableAction function IsUsableAction(slot, unit) + if not slot then return nil, nil end local actions = CleveRoids.GetAction(slot) -- If this is one of our macros AND it uses #showtooltip @@ -3462,6 +3481,7 @@ end CleveRoids.Hooks.IsCurrentAction = IsCurrentAction function IsCurrentAction(slot) + if not slot then return nil end local actions = CleveRoids.GetAction(slot) -- Use the same priority as GetActionTexture: active first, then tooltip @@ -3509,6 +3529,7 @@ end CleveRoids.Hooks.GetActionTexture = GetActionTexture function GetActionTexture(slot) + if not slot then return nil end local actions = CleveRoids.GetAction(slot) -- Check if this is one of our macros @@ -3636,6 +3657,9 @@ end -- TODO: Look into https://github.com/Stanzilla/WoWUIBugs/issues/47 if needed CleveRoids.Hooks.GetActionCooldown = GetActionCooldown function GetActionCooldown(slot) + -- Guard against nil/invalid slot + if not slot then return 0, 0, 0 end + local actions = CleveRoids.GetAction(slot) -- Check for actions.active OR actions.tooltip if actions and (actions.active or actions.tooltip) then @@ -3664,6 +3688,9 @@ end CleveRoids.Hooks.GetActionCount = GetActionCount function GetActionCount(slot) + -- Guard against nil/invalid slot + if not slot then return 0 end + local action = CleveRoids.GetAction(slot) local count -- Use the same priority as GetActionTexture: active first, then tooltip @@ -3702,6 +3729,9 @@ end CleveRoids.Hooks.IsConsumableAction = IsConsumableAction function IsConsumableAction(slot) + -- Guard against nil/invalid slot + if not slot then return nil end + local action = CleveRoids.GetAction(slot) -- Use the same priority as GetActionTexture: active first, then tooltip local actionToCheck = (action and action.active) or (action and action.tooltip) diff --git a/NampowerAPI.lua b/NampowerAPI.lua index f8b43c2..0b67b64 100644 --- a/NampowerAPI.lua +++ b/NampowerAPI.lua @@ -1485,6 +1485,7 @@ function API.IsSpellInRange(spellIdentifier, unit) end -- Check if a spell is usable (wrapper around IsSpellUsable) +-- Returns usable, oom on success; nil on error (spell not in spellbook) function API.IsSpellUsable(spellIdentifier) if not _G.IsSpellUsable then return nil @@ -1492,18 +1493,24 @@ function API.IsSpellUsable(spellIdentifier) -- If enhanced functions available, pass directly if API.features.hasEnhancedSpellFunctions then - return _G.IsSpellUsable(spellIdentifier) + local ok, usable, oom = pcall(_G.IsSpellUsable, spellIdentifier) + if ok then return usable, oom end + return nil end -- Convert to ID if name if type(spellIdentifier) == "string" then local spellId = API.GetSpellIdFromName(spellIdentifier) if spellId then - return _G.IsSpellUsable(spellId) + local ok, usable, oom = pcall(_G.IsSpellUsable, spellId) + if ok then return usable, oom end + return nil end end - return _G.IsSpellUsable(spellIdentifier) + local ok, usable, oom = pcall(_G.IsSpellUsable, spellIdentifier) + if ok then return usable, oom end + return nil end --------------------------------------------------------------------------------