increase supermacro support

This commit is contained in:
Jrc13245
2025-08-28 16:19:51 -04:00
parent 86df1583c4
commit 2da28c2653
2 changed files with 412 additions and 229 deletions
+132 -54
View File
@@ -1,75 +1,153 @@
-- CleveRoidMacros/Compatibility/SuperMacro.lua
-- Make SuperMacro’s RunLine play nicely with CleveRoidMacros (incl. /castsequence)
--[[
SuperMacro compatibility shim for CleveRoidMacros (1.12.1-safe)
- Installs only when BOTH SuperMacro and SuperCleveRoidMacros/CleveRoidMacros are loaded
- Hooks SuperMacro's RunLine order-agnostically (ADDON_LOADED + PLAYER_LOGIN)
- Intercepts CRM-owned slashcommands (including {…}, […], !, ?, ~)
- Forwards to CRM's SlashCmdList handlers; preserves /castsequence
- Leaves SuperMacro unmodified; safe fallback to original RunLine
]]
local _G = _G or getfenv(0)
local CRM = _G.CleveRoids or {}
_G.CleveRoids = CRM
do
local _G = _G or getfenv(0)
local CRM = _G.CleveRoids or {}
_G.CleveRoids = CRM
CRM.Hooks = CRM.Hooks or {}
local function install_supermacro_hook()
if CRM.SM_RunLineHooked then return end
if type(_G.RunLine) ~= "function" then return end
-- Commands we only intercept when they use bracket/extended syntax
local bracketHooks = {
cast = CRM.DoCast,
target = CRM.DoTarget,
use = CRM.DoUse,
-- Commands owned by CRM (keep in sync with Console.lua registrations)
local INTERCEPT = {
cast=true, castsequence=true, use=true,
startattack=true, stopattack=true, stopcasting=true,
target=true, retarget=true, cancelaura=true, unbuff=true,
unqueue=true, unshift=true, equip=true, equipmh=true, equipoh=true,
runmacro=true,
-- pet
petattack=true, petfollow=true, petpassive=true,
petaggressive=true, petdefensive=true, petwait=true,
}
CRM.Hooks = CRM.Hooks or {}
CRM.Hooks.RunLine = CRM.Hooks.RunLine or _G.RunLine
-- alias map (if CRM only registers /cancelaura, but /unbuff appears)
local ALIASES = { unbuff = "cancelaura" }
_G.RunLine = function(...)
for i = 1, arg.n do
local text = arg[i]
local function normalize_cmd(cmd)
if not cmd or cmd == "" then return nil end
local _, _, s = string.find(cmd, "^(.*)$")
s = s and string.lower(s) or nil
if s and ALIASES[s] then s = ALIASES[s] end
return s
end
local function has_handler(cmd)
local list = _G.SlashCmdList
return cmd and list and type(list[string.upper(cmd)]) == "function"
end
local function parse_slash(text)
if type(text) ~= "string" then return nil, nil end
text = (string.gsub(text, "^[%s]+", ""))
if not string.find(text, "^/") then return nil, nil end
local _, _, cmd, rest = string.find(text, "^/(%S+)%s*(.*)$")
return cmd, rest
end
local function install_supermacro_hook()
if CRM.SM_RunLineHooked then return end
if type(_G.RunLine) ~= "function" then return end
local orig_RunLine = _G.RunLine
CRM.Hooks.RunLine = CRM.Hooks.RunLine or orig_RunLine
_G.RunLine = function(...)
-- SuperMacro calls RunLine(line) one line at a time; handle first arg.
local text = arg and arg[1]
-- respect /stopmacro guard
if CRM.stopmacro then
CRM.stopmacro = false
return true
end
local handled = false
if type(text) == "string" then
-- 1) Always handle /castsequence (with or without conditionals)
local b1, e1, rest = string.find(text, "^%s*/castsequence%s*(.*)")
if b1 then
local msg = CRM.Trim(rest or "")
local seq = CRM.GetSequence(msg)
if seq then pcall(CRM.DoCastSequence, seq) end
handled = true
-- 1) special-case /castsequence → call CRM directly and RETURN TRUE
local b, e, rest = string.find(text, "^%s*/castsequence%s*(.*)")
if b then
if type(CleveRoids) == "table" and type(CleveRoids.DoCastSequence) == "function" then
pcall(CleveRoids.DoCastSequence, rest or "")
return true
end
-- fallback: slash handler if defined
local fn = _G.SlashCmdList and _G.SlashCmdList["CASTSEQUENCE"]
if type(fn) == "function" then
pcall(fn, rest or "")
return true
end
-- nothing to do; pass through to SM
else
-- 2) Bracketed/advanced forms for /cast, /target, /use
for cmd, action in bracketHooks do
local b2, e2 = string.find(text, "^%s*/" .. cmd .. "%s+[!%[]")
if b2 then
local msg = string.sub(text, e2)
msg = string.gsub(msg, "^%s+", "")
pcall(action, msg)
handled = true
break
-- 2) generic /cmd forwarding (lets CRM handlers parse [])
local _, _, raw, msg = string.find(text, "^/(%S+)%s*(.*)$")
if raw then
local cmd = string.lower(raw)
-- alias map: /unbuff → /cancelaura
if cmd == "unbuff" then cmd = "cancelaura" end
if INTERCEPT[cmd] and has_handler(cmd) then
pcall(_G.SlashCmdList[string.upper(cmd)], msg or "")
return true -- IMPORTANT: tell SM we handled it
end
end
-- 3) fast-path for extended/bracket tokens (/cast {…} [/…] ! ? ~)
local hooks = { cast = CRM.DoCast, target = CRM.DoTarget, use = CRM.DoUse }
for k, fn in pairs(hooks) do
if type(fn) == "function" then
local b2, e2 = string.find(text, "^%s*/" .. k .. "%s+[!%[%{%?~]")
if b2 then
local msg2 = string.gsub(string.sub(text, e2 + 1), "^%s+", "")
pcall(fn, msg2)
return true -- IMPORTANT
end
end
end
end
end
if not handled then
CRM.Hooks.RunLine(text) -- fall back to SuperMacro’s original logic
end
-- Not handled by CRM: let SuperMacro do its normal processing
return orig_RunLine(text)
end
CRM.SM_RunLineHooked = true
end
-- ===== Order-agnostic loader: install only when BOTH addons are present =====
local SM_LOADED, CRM_LOADED = false, false
local f = CreateFrame("Frame")
f:RegisterEvent("ADDON_LOADED")
f:RegisterEvent("PLAYER_LOGIN")
local function note_loaded(name)
if name == "SuperMacro" then
SM_LOADED = true
elseif name == "SuperCleveRoidMacros" or name == "CleveRoidMacros" then
CRM_LOADED = true
end
end
CRM.SM_RunLineHooked = true
local function both_loaded()
if SM_LOADED and CRM_LOADED then return true end
-- heuristic fallback in case this file loads late:
local sm_ok = type(_G.RunLine) == "function"
local crm_ok = _G.CleveRoids and _G.SlashCmdList and type(_G.SlashCmdList.CAST) == "function"
return sm_ok and crm_ok
end
local function try_install()
if both_loaded() then install_supermacro_hook() end
end
f:SetScript("OnEvent", function(_, evt, addon)
if evt == "ADDON_LOADED" then
if type(addon) == "string" then note_loaded(addon) end
try_install()
elseif evt == "PLAYER_LOGIN" then
try_install()
end
end)
end
-- Install after either addon is ready (order-agnostic)
local f = CreateFrame("Frame")
f:RegisterEvent("ADDON_LOADED")
f:RegisterEvent("PLAYER_LOGIN")
f:SetScript("OnEvent", function(_, event, addon)
if event == "ADDON_LOADED" then
if addon == "SuperMacro" or addon == "CleveRoidMacros" then
install_supermacro_hook()
end
else
install_supermacro_hook()
end
end)
+280 -175
View File
@@ -447,17 +447,41 @@ function CleveRoids.GetMacroBody(name)
return macro and macro.body
end
-- Attempts to execute a macro by the given name
-- name: The name of the macro
-- returns: Whether the macro was executed or not
-- Attempts to execute a macro by the given name (Blizzard or Super tab)
-- Returns: true if something was executed, false otherwise
function CleveRoids.ExecuteMacroByName(name)
local body = CleveRoids.GetMacroBody(name)
if not body then
return false
if not name or name == "" then return false end
local body
-- 1) Blizzard macro runner by name/ID
local id = GetMacroIndexByName(name)
if id and id ~= 0 and type(RunMacro) == "function" then
if pcall(RunMacro, name) then return true end
if pcall(RunMacro, id) then return true end
local _n, _tex, b2 = GetMacroInfo(id)
if b2 and b2 ~= "" then body = body or b2 end
end
CleveRoids.ExecuteMacroBody(body)
return true
-- 2) SuperMacro runner by name
if type(GetSuperMacroInfo) == "function" and type(RunSuperMacro) == "function" then
local _n2, _t2, b3 = GetSuperMacroInfo(name)
if b3 and b3 ~= "" then
if pcall(RunSuperMacro, name) then return true end
body = body or b3
end
end
-- 3) CRM cache fallback
if not body and type(CleveRoids.GetMacro) == "function" then
local m = CleveRoids.GetMacro(name)
if m and m.body and m.body ~= "" then
body = m.body
end
end
if not body or body == "" then return false end
return CleveRoids.ExecuteMacroBody(body)
end
function CleveRoids.SetHelp(conditionals)
@@ -607,44 +631,54 @@ function CleveRoids.SplitCommandAndArgs(text)
end
function CleveRoids.ParseSequence(text)
if not text or text == "" then return end
-- normalize commas
local args = string.gsub(text, "(%s*,%s*)", ",")
local _, c, cond = string.find(args, "(%[.*%])")
local _, r, reset, resetVal = string.find(args, "(%s*%]*%s*reset=([%w/]+)%s+)")
actionSeq = CleveRoids.Trim((r and string.sub(args, r+1)) or (c and string.sub(args, c+1)) or args)
args = (cond or "") .. actionSeq
-- optional [conditionals] block
local _, condEnd, condBlock = string.find(args, "(%[.*%])")
if not actionSeq then
return
-- accept reset= anywhere; no trailing space required; strip it out once
local _, _, resetVal = string.find(args, "[Rr][Ee][Ss][Ee][Tt]=([%w/]+)")
if resetVal then
args = string.gsub(args, "%s*[Rr][Ee][Ss][Ee][Tt]=[%w/]+%s*", " ", 1)
end
-- actions are what's left after any ] (if present)
local actionSeq = CleveRoids.Trim((condEnd and string.sub(args, condEnd + 1)) or args)
args = (condBlock or "") .. actionSeq
if actionSeq == "" then return end
local sequence = {
index = 1,
reset = {},
status = 0,
list = {},
index = 1,
reset = {},
status = 0,
lastUpdate = 0,
cond = cond,
args = args,
cmd = "/castsequence"
args = args,
list = {},
}
-- fill reset rules: seconds or flags (target/combat/alt/ctrl/shift)
if resetVal then
for _, rule in ipairs(CleveRoids.Split(resetVal, "/")) do
for _, rule in pairs(CleveRoids.Split(resetVal, "/")) do
rule = string.lower(CleveRoids.Trim(rule))
local secs = tonumber(rule)
if secs and secs > 0 then
sequence.reset.secs = secs
else
sequence.reset[string.lower(rule)] = true
sequence.reset[rule] = true
end
end
end
-- build steps
for _, a in ipairs(CleveRoids.Split(actionSeq, ",")) do
local sa = CleveRoids.CreateActionInfo(CleveRoids.GetParsedMsg(a))
table.insert(sequence.list, sa)
end
CleveRoids.Sequences[text] = sequence
CleveRoids.Sequences[text] = sequence
return sequence
end
@@ -652,22 +686,25 @@ function CleveRoids.ParseMacro(name)
if not name then return end
local macroID = GetMacroIndexByName(name)
if not macroID then return end
local _, texture, body = GetMacroInfo(macroID)
-- Try Blizzard macro first (macroID may be nil/0 if not found on 1.12)
local _, texture, body
if macroID and macroID ~= 0 then
_, texture, body = GetMacroInfo(macroID)
end
if not body and GetSuperMacroInfo then
-- Fallback: SuperMacro “Super” tab
if (not body) and GetSuperMacroInfo then
_, texture, body = GetSuperMacroInfo(name)
end
if not texture or not body then return end
local macro = {
id = macroID,
name = name,
id = macroID,
name = name,
texture = texture,
body = body,
body = body,
actions = {},
}
macro.actions.list = {}
@@ -689,21 +726,20 @@ function CleveRoids.ParseMacro(name)
-- #showtooltip and item/spell/macro specified, only use this tooltip
if st and tt ~= "" then
for _, arg in ipairs(CleveRoids.splitStringIgnoringQuotes(tt)) do
macro.actions.tooltip = CleveRoids.CreateActionInfo(arg)
local action = CleveRoids.CreateActionInfo(CleveRoids.GetParsedMsg(arg))
action.cmd = "/cast"
action.args = arg
action.isReactive = CleveRoids.reactiveSpells[action.action]
table.insert(macro.actions.list, action)
end
for _, arg in ipairs(CleveRoids.splitStringIgnoringQuotes(tt)) do
macro.actions.tooltip = CleveRoids.CreateActionInfo(arg)
local action = CleveRoids.CreateActionInfo(CleveRoids.GetParsedMsg(arg))
action.cmd = "/cast"
action.args = arg
action.isReactive = CleveRoids.reactiveSpells[action.action]
table.insert(macro.actions.list, action)
end
break
end
else
-- make sure we have a testable action
if line ~= "" and args ~= "" and CleveRoids.dynamicCmds[cmd] then
for _, arg in ipairs(CleveRoids.splitStringIgnoringQuotes(args)) do
local action = CleveRoids.CreateActionInfo(CleveRoids.GetParsedMsg(arg))
if cmd == "/castsequence" then
@@ -729,16 +765,38 @@ function CleveRoids.ParseMsg(msg)
if not msg then return end
local conditionals = {}
msg, conditionals.ignoretooltip = string.gsub(CleveRoids.Trim(msg), "^%?", "")
-- reset side flag for this parse
CleveRoids._ignoretooltip = 0
-- strip optional leading '?' and remember how many we stripped
local ignorecount
msg, ignorecount = string.gsub(CleveRoids.Trim(msg), "^%?", "")
conditionals.ignoretooltip = ignorecount
CleveRoids._ignoretooltip = ignorecount
-- capture a single [...] conditional block if present
local _, cbEnd, conditionBlock = string.find(msg, "%[(.+)%]")
local _, _, noSpam, cancelAura, action = string.find(string.sub(msg, (cbEnd or 0) + 1), "^%s*(!?)(~?)([^!~]+.*)")
-- split off flags/action after the condition block (or from start if none)
local _, _, noSpam, cancelAura, action = string.find(
string.sub(msg, (cbEnd or 0) + 1),
"^%s*(!?)(~?)([^!~]+.*)"
)
action = CleveRoids.Trim(action or "")
-- Store the action along with the conditionals incase it's needed
-- store the raw action for callers and strip trailing "(Rank X)" for comparisons
conditionals.action = action
-- FLEXIBLY remove rank text like "(Rank 9)" to get the base spell name
action = string.gsub(action, "%s*%(.-%)%s*$", "")
-- IMPORTANT: if there's NO conditional block, return nil conditionals so
-- DoWithConditionals will hit the {macroName} execution branch.
if not conditionBlock then
return conditionals.action, nil
end
-- With a condition block present, build out the conditionals table
-- optional spam/cancel flags (apply only when we actually have a [] block)
if noSpam and noSpam ~= "" then
local spamCond = CleveRoids.GetSpammableConditional(action)
if spamCond then
@@ -749,18 +807,14 @@ function CleveRoids.ParseMsg(msg)
conditionals.cancelaura = action
end
if not conditionBlock then
return conditionals.action, conditionals
end
-- Set the action's target to @unitid if found
-- Set the action's target to @unitid if found (e.g., @mouseover)
local _, _, target = string.find(conditionBlock, "(@[^%s,]+)")
if target then
conditionBlock = CleveRoids.Trim(string.gsub(conditionBlock, target, ""))
conditionals.target = string.sub(target, 2)
end
if conditionBlock and action then
if conditionBlock and conditionals.action then
-- Split the conditional block by comma or space
for _, conditionGroups in CleveRoids.splitStringIgnoringQuotes(conditionBlock, {",", " "}) do
if conditionGroups ~= "" then
@@ -768,25 +822,25 @@ function CleveRoids.ParseMsg(msg)
local conditionGroup = CleveRoids.splitStringIgnoringQuotes(conditionGroups, ":")
local condition, args = conditionGroup[1], conditionGroup[2]
-- No args, the action is the implicit argument
-- No args → the action is the implicit argument
if not args or args == "" then
if not conditionals[condition] then
conditionals[condition] = action
conditionals[condition] = conditionals.action
else
if type(conditionals[condition]) ~= "table" then
conditionals[condition] = { conditionals[condition] }
conditionals[condition] = { conditionals[condition] }
end
table.insert(conditionals[condition], action)
table.insert(conditionals[condition], conditionals.action)
end
else
-- Has args. Ensure the key's value is a table and add the new arguments.
-- Has args. Ensure the key's value is a table and add new arguments.
if not conditionals[condition] then
conditionals[condition] = {}
elseif type(conditionals[condition]) ~= "table" then
conditionals[condition] = { conditionals[condition] }
end
-- Split the args by / for multiple values
-- Split args by '/' for multiple values
for _, arg_item in CleveRoids.splitString(args, "/") do
local processed_arg = CleveRoids.Trim(arg_item)
@@ -794,24 +848,24 @@ function CleveRoids.ParseMsg(msg)
processed_arg = string.gsub(processed_arg, "_", " ")
processed_arg = CleveRoids.Trim(processed_arg)
-- normalize "name#N" → "name=#N" and "#N" → "=#N"
local arg_for_find = processed_arg
arg_for_find = string.gsub(arg_for_find, "^#(%d+)$", "=#%1")
arg_for_find = string.gsub(arg_for_find, "([^>~=<]+)#(%d+)", "%1=#%2")
-- FIXED: This regex now accepts decimal numbers
-- accept decimals too; capture name/op/amount
local _, _, name, operator, amount = string.find(arg_for_find, "([^>~=<]*)([>~=<]+)(#?%d*%.?%d+)")
if not operator or not amount then
table.insert(conditionals[condition], processed_arg)
else
local name_to_use = (name and name ~= "") and name or conditionals.action
local final_amount_str, num_replacements = string.gsub(amount, "#", "")
local should_check_stacks = (num_replacements == 1)
table.insert(conditionals[condition], {
name = CleveRoids.Trim(name_to_use),
operator = operator,
amount = tonumber(final_amount_str),
name = CleveRoids.Trim(name_to_use),
operator = operator,
amount = tonumber(final_amount_str),
checkStacks = should_check_stacks
})
end
@@ -823,20 +877,32 @@ function CleveRoids.ParseMsg(msg)
end
end
-- Get previously parsed or parse, store and return
function CleveRoids.GetParsedMsg(msg)
if not msg then return end
if CleveRoids.ParsedMsg[msg] then
return CleveRoids.ParsedMsg[msg].action, CleveRoids.ParsedMsg[msg].conditionals
-- ALWAYS refresh the side-flag for '?' even when we hit the cache
local _, ignorecount = string.gsub(CleveRoids.Trim(msg), "^%?", "")
CleveRoids._ignoretooltip = ignorecount
local cached = CleveRoids.ParsedMsg[msg]
if cached then
-- keep a per-msg copy too (helps future readers/tools)
cached.ignoretooltip = cached.ignoretooltip or ignorecount
return cached.action, cached.conditionals
end
CleveRoids.ParsedMsg[msg] = {}
CleveRoids.ParsedMsg[msg].action, CleveRoids.ParsedMsg[msg].conditionals = CleveRoids.ParseMsg(msg)
return CleveRoids.ParsedMsg[msg].action, CleveRoids.ParsedMsg[msg].conditionals
local action, conditionals = CleveRoids.ParseMsg(msg)
CleveRoids.ParsedMsg[msg] = {
action = action,
conditionals = conditionals,
ignoretooltip = ignorecount,
}
return action, conditionals
end
function CleveRoids.GetMacro(name)
return CleveRoids.Macros[name] or CleveRoids.ParseMacro(name)
end
@@ -864,47 +930,48 @@ end
function CleveRoids.TestAction(cmd, args)
local msg, conditionals = CleveRoids.GetParsedMsg(args)
if string.find(msg, "#showtooltip") or conditionals.ignoretooltip == 1 then
-- Nil-safe guards
local hasShowTooltip = (type(msg) == "string") and string.find(msg, "#showtooltip")
local ignoreTooltip = ((type(conditionals) == "table") and (conditionals.ignoretooltip == 1))
or (CleveRoids._ignoretooltip == 1)
-- If the line is explicitly ignored for tooltip (via '?'),
-- or it is a '#showtooltip' token, do not contribute icon/tooltip.
if hasShowTooltip or ignoreTooltip then
CleveRoids._ignoretooltip = 0 -- clear for next parse
return
end
-- No [] block → return a testable token so the UI can pick a texture
if not conditionals then
if not msg then
if not msg or msg == "" then
return
else
-- action is a {macro} or item/spell
return CleveRoids.GetMacroNameFromAction(msg) or msg
end
end
local origTarget = conditionals.target
if cmd == "" or not CleveRoids.dynamicCmds[cmd] then
-- untestables
return
end
if conditionals.target == "focus" then
local focusUnitId = nil
-- First, try to get the specific UnitID from pfUI's data for reliability.
if pfUI and pfUI.uf and pfUI.uf.focus and pfUI.uf.focus.label and pfUI.uf.focus.id and UnitExists(pfUI.uf.focus.label .. pfUI.uf.focus.id) then
if pfUI and pfUI.uf and pfUI.uf.focus and pfUI.uf.focus.label and pfUI.uf.focus.id
and UnitExists(pfUI.uf.focus.label .. pfUI.uf.focus.id) then
focusUnitId = pfUI.uf.focus.label .. pfUI.uf.focus.id
end
if focusUnitId then
-- If we found a specific unit (e.g., "party1", "raid2"), use it for the test.
conditionals.target = focusUnitId
else
-- If we only have a name, check if that name exists. If not, the condition fails.
if not CleveRoids.GetFocusName() then
return -- No focus exists, so this action is not valid.
return
end
-- Fallback for testing: We can't safely target by name here, so we default to "target".
-- This part of the original logic remains as a fallback, but the pfUI check above will handle most cases.
conditionals.target = "target"
end
end
if conditionals.target == "mouseover" then
if not CleveRoids.IsValidTarget("mouseover", conditionals.help) then
return false
@@ -912,19 +979,16 @@ function CleveRoids.TestAction(cmd, args)
end
CleveRoids.FixEmptyTarget(conditionals)
-- CleveRoids.SetHelp(conditionals)
for k, v in pairs(conditionals) do
if not CleveRoids.ignoreKeywords[k] then
if not CleveRoids.Keywords[k] or not CleveRoids.Keywords[k](conditionals) then
-- failed test
conditionals.target = origTarget
return
end
end
end
-- tests passed
conditionals.target = origTarget
return CleveRoids.GetMacroNameFromAction(msg) or msg
end
@@ -1087,7 +1151,7 @@ end
function CleveRoids.DoTarget(msg)
local action, conditionals = CleveRoids.GetParsedMsg(msg)
if action ~= "" or not next(conditionals) then
if action ~= "" or type(conditionals) ~= "table" or not next(conditionals) then
CleveRoids.Hooks.TARGET_SlashCmd(msg)
return true
end
@@ -1450,55 +1514,67 @@ end
return handled
end
-- Keep THIS version
function CleveRoids.DoCastSequence(sequence)
if not CleveRoids.hasSuperwow then
CleveRoids.Print("|cFFFF0000/castsequence|r requires |cFF00FFFFSuperWoW|r.")
return
if not CleveRoids.hasSuperwow then
CleveRoids.Print("|cFFFF0000/castsequence|r requires |cFF00FFFFSuperWoW|r.")
return
end
if type(sequence) == "string" then
sequence = CleveRoids.GetSequence(sequence)
if not sequence then return end
end
if CleveRoids.currentSequence and not CleveRoids.CheckSpellCast("player") then
CleveRoids.currentSequence = nil
elseif CleveRoids.currentSequence then
return
end
if sequence.index > 1 and sequence.reset then
for k,_ in pairs(sequence.reset) do
if CleveRoids.kmods[k] and CleveRoids.kmods[k]() then
CleveRoids.ResetSequence(sequence)
break
end
end
end
if CleveRoids.currentSequence and not CleveRoids.CheckSpellCast("player") then
CleveRoids.currentSequence = nil
elseif CleveRoids.currentSequence then
return
local active = CleveRoids.GetCurrentSequenceAction(sequence)
if not (active and active.action) then return end
sequence.status = 0
sequence.lastUpdate = GetTime()
sequence.expires = 0
local prevSeq = CleveRoids.currentSequence
CleveRoids.currentSequence = sequence
local actionText = (sequence.cond or "") .. active.action
local resolvedText, conds = CleveRoids.GetParsedMsg(actionText)
local function cast_by_name(msg)
msg = msg or ""
if not string.find(msg, "%(%s*.-%s*%)%s*$") then
local sp = CleveRoids.GetSpell(msg)
local r = (sp and sp.rank) or (sp and sp.highest and sp.highest.rank)
if r and r ~= "" then msg = msg .. "(" .. r .. ")" end
end
CastSpellByName(msg)
return true
end
if sequence.index > 1 then
if sequence.reset then
for k, _ in sequence.reset do
if CleveRoids.kmods[k] and CleveRoids.kmods[k]() then
CleveRoids.ResetSequence(sequence)
end
end
end
end
local attempted = false
if not conds then
attempted = cast_by_name(resolvedText or active.action)
else
local final = CleveRoids.DoWithConditionals(actionText, nil, CleveRoids.FixEmptyTarget, false, CastSpellByName)
if final then attempted = cast_by_name(final) end
end
local active = CleveRoids.GetCurrentSequenceAction(sequence)
if active and active.action then
sequence.status = 0
sequence.lastUpdate = GetTime()
sequence.expires = 0
-- Tentatively claim the active sequence, but only keep it if a cast actually fires.
local prevSeq = CleveRoids.currentSequence
CleveRoids.currentSequence = sequence
local action = (sequence.cond or "") .. active.action
-- Ensure we still cast if there are no [] conditionals
local result = CleveRoids.DoWithConditionals(
action,
function(msg) CastSpellByName(msg) end, -- fallback cast when no []
CleveRoids.FixEmptyTarget, -- optional: preserves your targeting behavior
not CleveRoids.hasSuperwow,
CastSpellByName
)
-- If the conditionals failed and no cast was started, don't steal currentSequence
if not result then
CleveRoids.currentSequence = prevSeq
end
return result
end
if not attempted then
CleveRoids.currentSequence = prevSeq
end
end
CleveRoids.DoConditionalCancelAura = function(msg)
@@ -1883,11 +1959,68 @@ CleveRoids.Frame:RegisterEvent("STOP_AUTOREPEAT_SPELL")
CleveRoids.Frame:RegisterEvent("SPELLCAST_CHANNEL_START")
CleveRoids.Frame:RegisterEvent("SPELLCAST_CHANNEL_STOP")
-- Order-agnostic SuperMacro hook installer
local function CRM_SM_InstallHook()
if CleveRoids.SM_RunLineHooked then return end
if not SuperMacroFrame or type(RunLine) ~= "function" then return end
local orig_RunLine = RunLine
-- Fast-path targets for extended/bracket syntax
local tokenHooks = {
cast = CleveRoids.DoCast,
target = CleveRoids.DoTarget,
use = CleveRoids.DoUse,
}
RunLine = function(...)
local text = (arg and arg[1]) or nil
if CleveRoids.stopmacro then
CleveRoids.stopmacro = false
return true
end
if type(text) == "string" then
-- 1) SPECIAL-CASE: /castsequence (no token required)
local b, e, rest = string.find(text, "^%s*/castsequence%s*(.*)")
if b then
if type(CleveRoids.DoCastSequence) == "function" then
pcall(CleveRoids.DoCastSequence, rest or "")
return true
end
local fn = _G.SlashCmdList and _G.SlashCmdList["CASTSEQUENCE"]
if type(fn) == "function" then
pcall(fn, rest or "")
return true
end
-- fall through to SM if no handler
else
-- 2) FAST-PATH: /cast|/use|/target followed by extended token
for k, fn in pairs(tokenHooks) do
if type(fn) == "function" and string.find(text, "^%s*/"..k.."%s+[!%[%{%?~]") then
-- IMPORTANT: keep the opening token — grab the entire remainder
local _, _, remainder = string.find(text, "^%s*/"..k.."%s+(.*)$")
remainder = remainder and string.gsub(remainder, "^%s+", "") or ""
pcall(fn, remainder)
return true
end
end
end
end
return orig_RunLine(text)
end
CleveRoids.SM_RunLineHooked = true
end
function CleveRoids.Frame:PLAYER_LOGIN()
_, CleveRoids.playerClass = UnitClass("player")
_, CleveRoids.playerGuid = UnitExists("player")
CleveRoids.IndexSpells()
CleveRoids.initializationTimer = GetTime() + 1.5
CRM_SM_InstallHook()
if not CleveRoids.hasSuperwow or not IsSpellInRange then
if not CleveRoids.hasSuperwow then
CleveRoids.Print("|cFFFF0000CleveRoidMacros|r requires |cFF00FFFFbalakethelock's SuperWoW|r:")
@@ -1905,47 +2038,13 @@ function CleveRoids.Frame:PLAYER_LOGIN()
end
function CleveRoids.Frame:ADDON_LOADED(addon)
if addon ~= "CleveRoidMacros" then
return
-- keep your existing init for CRM:
if addon == "CleveRoidMacros" then
CleveRoids.InitializeExtensions()
end
CleveRoids.InitializeExtensions()
if SuperMacroFrame then
local hooks = {
cast = { action = CleveRoids.DoCast },
target = { action = CleveRoids.DoTarget },
use = { action = CleveRoids.DoUse },
castsequence = { action = CleveRoids.DoCastSequence }
}
-- Hook SuperMacro's RunLine to stay compatible
CleveRoids.Hooks.RunLine = RunLine
CleveRoids.RunLine = function(...)
for i = 1, arg.n do
if CleveRoids.stopmacro then
CleveRoids.stopmacro = false
return true
end
local intercepted = false
local text = arg[i]
for k,v in pairs(hooks) do
local begin, _end = string.find(text, "^/"..k.."%s+[!%[]")
if begin then
local msg = string.sub(text, _end)
v.action(msg)
intercepted = true
break
end
end
if not intercepted then
CleveRoids.Hooks.RunLine(text)
end
end
end
RunLine = CleveRoids.RunLine
-- (re)attempt hook when either addon arrives
if addon == "SuperMacro" or addon == "CleveRoidMacros" then
CRM_SM_InstallHook()
end
end
@@ -1965,21 +2064,26 @@ function CleveRoids.Frame:UNIT_CASTEVENT(caster,target,action,spell_id,cast_time
CleveRoids.spell_tracking[caster] = nil
end
-- handle cast sequence
-- handle cast sequence (SuperWoW)
if CleveRoids.currentSequence and caster == CleveRoids.playerGuid then
local active = CleveRoids.GetCurrentSequenceAction(CleveRoids.currentSequence)
local name, rank = SpellInfo(spell_id)
local isSeqSpell = (active.action == name or active.action == (name.."("..rank..")"))
local nameRank = (rank and rank ~= "") and (name .. "(" .. rank .. ")") or nil
local isSeqSpell = active and active.action and (
active.action == name or
(nameRank and active.action == nameRank)
)
if isSeqSpell then
local status = CleveRoids.currentSequence.status
if status == 0 and (action == "START" or action == "CHANNEL") and cast_time > 0 then
CleveRoids.currentSequence.status = 1
CleveRoids.currentSequence.expires = GetTime() + cast_time - 2000
-- cast_time is ms; GetTime() is seconds
CleveRoids.currentSequence.status = 1
CleveRoids.currentSequence.expires = GetTime() + (cast_time / 1000) - 2
elseif (status == 0 and action == "CAST" and cast_time == 0)
or (status == 1 and action == "CAST" and CleveRoids.currentSequence.expires)
then
CleveRoids.currentSequence.status = 2
or (status == 1 and action == "CAST" and CleveRoids.currentSequence.expires) then
CleveRoids.currentSequence.status = 2
CleveRoids.currentSequence.lastUpdate = GetTime()
CleveRoids.AdvanceSequence(CleveRoids.currentSequence)
CleveRoids.currentSequence = nil
@@ -1988,6 +2092,7 @@ function CleveRoids.Frame:UNIT_CASTEVENT(caster,target,action,spell_id,cast_time
end
end
end
if CleveRoidMacros.realtime == 0 then
CleveRoids.QueueActionUpdate()
end