fix macro icons, enhance libdebuff further with nampower, code cleanup, improve compatibility with supermacro

This commit is contained in:
Jrc13245
2026-01-31 15:02:03 -05:00
parent 36355a42aa
commit 174c636673
7 changed files with 1946 additions and 326 deletions
+17 -3
View File
@@ -29,6 +29,11 @@ do
petaggressive=true, petdefensive=true, petwait=true,
-- healing (requires QuickHeal addon)
quickheal=true, qh=true,
-- additional commands that were missing
castpet=true, cleartarget=true,
applymain=true, applyoff=true,
-- slot-specific equip commands
["equip11"]=true, ["equip12"]=true, ["equip13"]=true, ["equip14"]=true,
}
-- alias map (if CRM only registers /cancelaura, but /unbuff appears)
@@ -66,6 +71,12 @@ do
-- SuperMacro calls RunLine(line) one line at a time; handle first arg.
local text = arg and arg[1]
-- FIX: Skip comment lines (SuperMacro uses -- for comments)
-- Let SuperMacro handle these to avoid any processing
if type(text) == "string" and string.find(text, "^%s*%-%-") then
return orig_RunLine(text)
end
-- IMPORTANT: Check for /nofirstaction BEFORE the stop flag check
-- This allows /nofirstaction to clear the stopMacroFlag set by /firstaction
if type(text) == "string" then
@@ -109,7 +120,8 @@ do
-- nothing to do; pass through to SM
else
-- 2) generic /cmd forwarding (lets CRM handlers parse [])
local _, _, raw, msg = string.find(text, "^/(%S+)%s*(.*)$")
-- FIX: Added %s* to handle leading whitespace (like /castsequence pattern above)
local _, _, raw, msg = string.find(text, "^%s*/(%S+)%s*(.*)$")
if raw then
local cmd = string.lower(raw)
-- alias map: /unbuff → /cancelaura
@@ -148,9 +160,10 @@ do
CRM.Hooks.SuperMacro_RunMacro = orig_SuperMacro_RunMacro
local function hooked_RunMacro(index)
-- Clear macro stop flags at macro start
-- Clear ALL macro stop flags at macro start (including firstaction flag)
CRM.stopMacroFlag = false
CRM.skipMacroFlag = false
CRM.stopOnCastFlag = false -- FIX: Also clear firstaction flag
return orig_SuperMacro_RunMacro(index)
end
@@ -166,9 +179,10 @@ do
CRM.Hooks.RunSuperMacro = orig_RunSuperMacro
_G.RunSuperMacro = function(index)
-- Clear macro stop flags at macro start
-- Clear ALL macro stop flags at macro start (including firstaction flag)
CRM.stopMacroFlag = false
CRM.skipMacroFlag = false
CRM.stopOnCastFlag = false -- FIX: Also clear firstaction flag
return orig_RunSuperMacro(index)
end
end
+111 -1
View File
@@ -1259,6 +1259,97 @@ local function PFUI_HasLibDebuff()
and type(pfUI.api.libdebuff.UnitDebuff) == "function"
end
-- ============================================================================
-- PENDING DEBUFF CAST DETECTION
-- ============================================================================
-- Prevents double-application when spamming [nodebuff] macros with spell queue.
-- If we're currently casting or have queued a spell that applies this debuff,
-- treat the debuff as "pending" so [nodebuff] returns false.
-- Helper: Normalize spell name for comparison (strip rank, lowercase)
local function NormalizeSpellNameForComparison(spellName)
if not spellName then return nil end
-- Strip rank suffix: "Faerie Fire (Feral)(Rank 4)" -> "Faerie Fire (Feral)"
local normalized = string.gsub(spellName, "%s*%(%s*Rank%s+%d+%s*%)", "")
-- Convert underscores to spaces and lowercase
normalized = string.lower(string.gsub(normalized, "_", " "))
return normalized
end
-- Check if player is currently casting or has queued a spell that would apply this debuff
-- This prevents [nodebuff] from returning true while the cast is in-flight
-- @param spellName string - The debuff spell name to check
-- @param targetUnit string - The target unit (to verify we're casting AT this target)
-- @return boolean - True if this debuff is pending (being cast/queued to this target)
local function IsPendingDebuffCast(spellName, targetUnit)
if not spellName then return false end
local normalizedCheck = NormalizeSpellNameForComparison(spellName)
if not normalizedCheck then return false end
-- Get target GUID for verification (only count as pending if casting AT this target)
local targetGuid = nil
if targetUnit and UnitExists(targetUnit) then
local _, guid = UnitExists(targetUnit)
targetGuid = guid
end
-- Check if currently CASTING this spell
-- CleveRoids.CurrentSpell tracks the spell being cast with time-based duration
if CleveRoids.CurrentSpell and CleveRoids.CurrentSpell.type == "cast" then
local castingName = CleveRoids.CurrentSpell.spellName
if castingName then
local normalizedCasting = NormalizeSpellNameForComparison(castingName)
if normalizedCasting == normalizedCheck then
-- Verify cast is still in progress (not finished)
if CleveRoids.castStartTime and CleveRoids.castDuration then
local remaining = CleveRoids.castDuration - (GetTime() - CleveRoids.castStartTime)
if remaining > 0.1 then
-- Cast is in-flight, debuff is pending
return true
end
else
-- No timing info but spell type is "cast" - assume pending
return true
end
end
end
end
-- Check if this spell is QUEUED via Nampower
-- CleveRoids.queuedSpell tracks the spell waiting to fire after GCD/cast
if CleveRoids.queuedSpell and CleveRoids.queuedSpell.spellName then
local queuedName = CleveRoids.queuedSpell.spellName
local normalizedQueued = NormalizeSpellNameForComparison(queuedName)
if normalizedQueued == normalizedCheck then
-- Spell is queued, debuff is pending
return true
end
end
-- Check pfUI's pending debuff tracking (uses SPELL_GO events for accuracy)
-- pfUI.libdebuff_pending[guid][spellName] = true when cast is in-flight
-- Cleared on SPELL_GO miss/resist, so this is very accurate
if pfUI and pfUI.libdebuff_pending and targetGuid then
local pendingForTarget = pfUI.libdebuff_pending[targetGuid]
if pendingForTarget then
-- pfUI stores by spell name (may or may not have rank)
-- Check both the exact name and stripped name
for pendingSpell, _ in pairs(pendingForTarget) do
local normalizedPending = NormalizeSpellNameForComparison(pendingSpell)
if normalizedPending == normalizedCheck then
return true
end
end
end
end
return false
end
-- Expose for use in nodebuff conditional
CleveRoids.IsPendingDebuffCast = IsPendingDebuffCast
-- Helper: Get debuff time-left (seconds) from CleveRoids.libdebuff only
local function _get_debuff_timeleft(unitToken, auraName)
-- Defensive: verify libdebuff is a table before accessing properties
@@ -4792,6 +4883,16 @@ CleveRoids.Keywords = {
nodebuff = function(conditionals)
return NegatedMulti(conditionals.nodebuff, function(v)
-- Extract spell name from args (could be string or table with name/operator/amount)
local spellName = type(v) == "table" and v.name or v
-- Check if this debuff is PENDING (being cast or queued)
-- This prevents double-application when spamming macros with spell queue
if CleveRoids.IsPendingDebuffCast(spellName, conditionals.target) then
return false -- Treat as if debuff exists (nodebuff returns false)
end
-- Debuff not pending, check if it actually exists on target
return not CleveRoids.ValidateUnitDebuff(conditionals.target, v)
end, conditionals, "nodebuff")
end,
@@ -6495,11 +6596,20 @@ CleveRoids.Keywords = {
-- Negated spell name form
return NegatedMulti(conditionals.nocursive, function(args)
-- Extract spell name from args
local spellName = type(args) == "string" and args or (type(args) == "table" and args.name)
-- Check if this debuff is PENDING (being cast or queued)
-- This prevents double-application when spamming macros with spell queue
if spellName and CleveRoids.IsPendingDebuffCast(spellName, target) then
return false -- Treat as if debuff exists (nocursive returns false)
end
if type(args) == "string" then
-- Simple spell name check: [nocursive:Rake] = true if Rake is missing
return not CleveRoids.ValidateCursiveDebuff(target, args, nil, nil)
elseif type(args) == "table" then
local spellName = args.name
spellName = args.name
-- Handle multi-comparison negation
if args.comparisons and type(args.comparisons) == "table" then
+7
View File
@@ -100,6 +100,13 @@ local StartAttack = function(msg)
CleveRoids.CurrentSpell.autoAttackLock = true
CleveRoids.autoAttackLockElapsed = GetTime()
AttackTarget()
-- FIX: Immediately set autoAttack flag so subsequent macro lines know attack started
-- Don't wait for PLAYER_ENTER_COMBAT event which has a delay
CleveRoids.CurrentSpell.autoAttack = true
-- FIX: Queue icon update so action bars reflect the new state
if CleveRoidMacros and CleveRoidMacros.realtime == 0 then
CleveRoids.QueueActionUpdate()
end
end
end
+243 -61
View File
@@ -757,14 +757,24 @@ function CleveRoids.TestForActiveAction(actions)
local newSequence = nil
local firstUnconditional = nil
-- Only use tooltip-only branch when #showtooltip exists but action list is empty
-- (e.g., bare "#showtooltip" with no /cast commands). When explicitTooltip is true,
-- the spell was added to actions.list with proper cmd/args, so use the ELSE branch.
if actions.tooltip and table.getn(actions.list) == 0 then
if CleveRoids.TestAction(actions.cmd or "", actions.args or "") then
-- Use tooltip-only branch when:
-- 1. #showtooltip has an explicit argument (explicitTooltip = true), OR
-- 2. #showtooltip exists but action list is empty (bare "#showtooltip")
-- This ensures explicit tooltips always show their icon/usability correctly.
if actions.tooltip and (actions.explicitTooltip or table.getn(actions.list) == 0) then
-- For explicit tooltips, directly use the tooltip as active action
-- (actions.cmd/args are empty, so TestAction would fail)
if actions.explicitTooltip then
hasActive = true
newActiveAction = actions.tooltip
-- Resolve nested macro references for #showtooltip propagation
elseif CleveRoids.TestAction(actions.cmd or "", actions.args or "") then
-- Bare #showtooltip (no arg) - validate with TestAction
hasActive = true
newActiveAction = actions.tooltip
end
-- Resolve nested macro references for #showtooltip propagation
if hasActive and newActiveAction then
local macroName = CleveRoids.GetMacroNameFromAction(actions.tooltip.action)
if macroName then
local resolved = CleveRoids.ResolveNestedMacroActive(actions.tooltip, 0)
@@ -795,6 +805,15 @@ function CleveRoids.TestForActiveAction(actions)
if action.sequence then
newSequence = action.sequence
newActiveAction = CleveRoids.GetCurrentSequenceAction(newSequence)
-- Check if current step is null/pass/noop - if so, skip this sequence for icon
if newActiveAction and newActiveAction.action then
local stepLower = string.lower(CleveRoids.Trim(newActiveAction.action))
if stepLower == "null" or stepLower == "pass" or stepLower == "noop" then
hasActive = false
newActiveAction = nil
newSequence = nil
end
end
if not newActiveAction then hasActive = false end
else
newActiveAction = action
@@ -1659,6 +1678,7 @@ function CleveRoids.ParseSequence(text)
lastUpdate = 0,
args = args,
list = {},
perTargetState = {}, -- [guid] = {index = N, lastUpdate = T} for reset=target
}
-- fill reset rules: seconds or flags (target/combat/alt/ctrl/shift)
@@ -2077,14 +2097,41 @@ function CleveRoids.GetCurrentSequenceAction(sequence)
end
function CleveRoids.ResetSequence(sequence)
-- Save the GUID before clearing it
local targetGuid = sequence.lastTargetGuid
sequence.index = 1
sequence.lastTargetGuid = nil -- Clear stored GUID so next use captures fresh target
-- Also reset per-target state for current target if tracking
if sequence.reset and sequence.reset.target and targetGuid then
if sequence.perTargetState then
sequence.perTargetState[targetGuid] = nil
end
end
end
function CleveRoids.AdvanceSequence(sequence)
if sequence.index < table.getn(sequence.list) then
-- Not at the end yet, just advance normally
local oldIndex = sequence.index
sequence.index = sequence.index + 1
if CleveRoids.debug then
DEFAULT_CHAT_FRAME:AddMessage(string.format(
"|cff00ff00[Sequence Advanced]|r %d -> %d (of %d)",
oldIndex, sequence.index, table.getn(sequence.list)
))
end
-- Sync per-target state if tracking by target
if sequence.reset and sequence.reset.target and sequence.lastTargetGuid then
sequence.perTargetState = sequence.perTargetState or {}
sequence.perTargetState[sequence.lastTargetGuid] = {
index = sequence.index,
lastUpdate = GetTime(),
}
end
else
-- At the end of sequence - check if we should auto-reset or stay at last step
local hasNonModifierReset = false
@@ -2703,6 +2750,13 @@ local function _startAttackAction()
CleveRoids.CurrentSpell.autoAttackLock = true
CleveRoids.autoAttackLockElapsed = GetTime()
AttackTarget()
-- FIX: Immediately set autoAttack flag so subsequent macro lines know attack started
-- Don't wait for PLAYER_ENTER_COMBAT event which has a delay
CleveRoids.CurrentSpell.autoAttack = true
-- FIX: Queue icon update so action bars reflect the new state
if CleveRoidMacros and CleveRoidMacros.realtime == 0 then
CleveRoids.QueueActionUpdate()
end
end
end
@@ -3484,6 +3538,28 @@ function CleveRoids.DoCastSequence(sequence)
local active = CleveRoids.GetCurrentSequenceAction(sequence)
if not (active and active.action) then return end
-- Debug: Show sequence state
if CleveRoids.debug then
DEFAULT_CHAT_FRAME:AddMessage(string.format(
"|cff00ffff[Sequence]|r Step %d/%d: '%s'",
sequence.index, table.getn(sequence.list), active.action
))
end
-- Check for "null" step - does nothing, allows macro to fall through
-- Usage: /castsequence reset=target Sunder Armor, null
local actionLower = string.lower(CleveRoids.Trim(active.action))
if actionLower == "null" or actionLower == "pass" or actionLower == "noop" then
-- Update sequence state (so per-target tracking works)
sequence.status = 0
sequence.lastUpdate = GetTime()
if CleveRoids.debug then
DEFAULT_CHAT_FRAME:AddMessage("|cff00ffff[Sequence]|r Hit null step, falling through")
end
-- Don't cast anything, just return - allows macro to continue to next line
return
end
sequence.status = 0
sequence.lastUpdate = GetTime()
sequence.expires = 0
@@ -3814,6 +3890,19 @@ function CleveRoids.OnUpdate(self)
CleveRoids.ClearSpellNameCaches()
end
end
-- MEMORY: Clean up stale perTargetState entries in sequences (older than 5 minutes)
-- This prevents memory bloat from tracking many targets over time
local SEQUENCE_TARGET_STALE_TIME = 300 -- 5 minutes
for _, sequence in pairs(CleveRoids.Sequences) do
if sequence.perTargetState then
for guid, state in pairs(sequence.perTargetState) do
if state.lastUpdate and (time - state.lastUpdate) > SEQUENCE_TARGET_STALE_TIME then
sequence.perTargetState[guid] = nil
end
end
end
end
end
end
@@ -3938,15 +4027,33 @@ function ActionHasRange(slot)
if not slot then return nil end
local actions = CleveRoids.GetAction(slot)
-- Only override for our macros with #showtooltip
if actions and actions.tooltip and actions.active then
if actions.active.inRange ~= -1 then
return 1 -- Has range check with valid data
else
-- For channeled spells (inRange == -1), try proxy slot lookup
local spellName = actions.active.spell and actions.active.spell.name
local proxySlot = spellName and CleveRoids.GetProxyActionSlot(spellName)
if proxySlot then
return CleveRoids.Hooks.ActionHasRange(proxySlot)
if actions and actions.tooltip then
-- If we have an active action with valid range data, use it
if actions.active then
if actions.active.inRange ~= -1 then
return 1 -- Has range check with valid data
else
-- For channeled spells (inRange == -1), try proxy slot lookup
local spellName = actions.active.spell and actions.active.spell.name
local proxySlot = spellName and CleveRoids.GetProxyActionSlot(spellName)
if proxySlot then
return CleveRoids.Hooks.ActionHasRange(proxySlot)
end
end
end
-- FIX: Even without actions.active, if the macro has potential spell actions,
-- return 1 to ensure pfUI calls IsActionInRange (which will do proper checks)
-- This fixes Super macros where actions.active might not be populated yet
if actions.tooltip.spell or actions.tooltip.type == "spell" then
return 1 -- Tooltip is a spell - has range
end
-- Check if any action in the list is a spell
if actions.list then
for i = 1, table.getn(actions.list) do
local action = actions.list[i]
if action and (action.spell or action.type == "spell") then
return 1 -- Has at least one spell action
end
end
end
end
@@ -3959,12 +4066,40 @@ function IsActionInRange(slot, unit)
if not slot then return nil end
local actions = CleveRoids.GetAction(slot)
-- Only override for our macros with #showtooltip
if actions and actions.tooltip and actions.active and actions.active.type == "spell" then
if actions.active.inRange ~= -1 then
return actions.active.inRange
else
-- For channeled spells (inRange == -1), try proxy slot lookup
local spellName = actions.active.spell and actions.active.spell.name
if actions and actions.tooltip then
-- If we have an active spell action, use its range data
if actions.active and actions.active.type == "spell" then
if actions.active.inRange ~= -1 then
return actions.active.inRange
else
-- For channeled spells (inRange == -1), try proxy slot lookup
local spellName = actions.active.spell and actions.active.spell.name
local proxySlot = spellName and CleveRoids.GetProxyActionSlot(spellName)
if proxySlot then
return CleveRoids.Hooks.IsActionInRange(proxySlot, unit)
end
end
end
-- FIX: If no active action but macro has tooltip spell, check its range directly
-- This ensures range coloring works even when actions.active hasn't been populated
if not actions.active and actions.tooltip.spell then
local spell = actions.tooltip.spell
local spellName = spell.name
if spellName and IsSpellInRange then
local targetUnit = unit or "target"
if UnitExists(targetUnit) then
-- FIX: Wrap in pcall - spell might not be in spellbook (items, other class spells, etc.)
local ok, result = pcall(IsSpellInRange, spellName, targetUnit)
if ok then
if result == 0 then
return 0 -- Out of range
elseif result == 1 then
return 1 -- In range
end
end
end
end
-- Try proxy slot as fallback
local proxySlot = spellName and CleveRoids.GetProxyActionSlot(spellName)
if proxySlot then
return CleveRoids.Hooks.IsActionInRange(proxySlot, unit)
@@ -3989,7 +4124,35 @@ function IsUsableAction(slot, unit)
-- We have an active action - return its usable state
return actions.active.usable, actions.active.oom
else
-- This is our macro but no action is active (all conditionals failed)
-- FIX: If no active action but tooltip has a spell, check its usability directly
-- This ensures usability coloring works even when actions.active hasn't been populated
if actions.tooltip.spell then
local spell = actions.tooltip.spell
local spellName = spell.name
if spellName and IsSpellUsable then
-- FIX: Wrap in pcall - spell might not be in spellbook (items, other class spells, etc.)
local ok, usable, notEnoughMana = pcall(IsSpellUsable, spellName)
if ok then
if usable == 1 then
return 1, nil -- Usable
elseif notEnoughMana == 1 then
return nil, 1 -- Out of mana
else
return nil, nil -- Not usable (wrong stance, etc.)
end
end
end
-- Fallback: check if spell has a cost and compare to current mana
if spell.cost and spell.cost > 0 then
local currentMana = UnitMana("player")
if currentMana < spell.cost then
return nil, 1 -- Out of mana
end
end
-- Default to usable if we can't determine otherwise
return 1, nil
end
-- No tooltip spell - this macro's conditionals all failed
-- Return nil to make the icon dark
return nil, nil
end
@@ -4605,45 +4768,40 @@ function CleveRoids.Frame:UNIT_CASTEVENT(caster,target,action,spell_id,cast_time
if target and lib and lib.objects then
local normalizedTarget = CleveRoids.NormalizeGUID(target)
if normalizedTarget and lib.objects[normalizedTarget] then
-- Refresh all active Judgements on the target (pfUI-style: by name, not just ID)
-- Refresh all active Judgements on the target
for spellID, rec in pairs(lib.objects[normalizedTarget]) do
if rec.start and rec.duration then
local spellName = SpellInfo(spellID)
if spellName then
-- Remove rank to get base name
local baseName = string.gsub(spellName, "%s*%(Rank %d+%)", "")
-- Check if this is a judgement by spell ID
if lib.judgementSpells and lib.judgementSpells[spellID] and rec.start and rec.duration then
-- Only refresh if the Judgement is still active and was cast by player
local remaining = rec.duration + rec.start - GetTime()
if remaining > 0 and rec.caster == "player" then
-- Refresh the Judgement by updating the start time
rec.start = GetTime()
-- Check if this is a judgement by name (pfUI approach)
if lib.judgementNames and lib.judgementNames[baseName] then
-- Only refresh if the Judgement is still active and was cast by player
local remaining = rec.duration + rec.start - GetTime()
if remaining > 0 and rec.caster == "player" then
-- Refresh the Judgement by updating the start time
rec.start = GetTime()
local spellName = SpellInfo(spellID)
local baseName = spellName and string.gsub(spellName, "%s*%(Rank %d+%)", "") or "Unknown"
if CleveRoids.debug then
DEFAULT_CHAT_FRAME:AddMessage(
string.format("|cff00ffaa[Judgement Refresh]|r Refreshed %s (ID:%d) on %s hit - new duration: %ds",
baseName, spellID, action, rec.duration)
)
end
-- Also sync to pfUI if it's loaded
if pfUI and pfUI.api and pfUI.api.libdebuff then
local targetName = (lib.guidToName and lib.guidToName[normalizedTarget]) or UnitName("target")
local targetLevel = UnitLevel("target") or 0
if targetName then
-- Refresh in pfUI's tracking (by name)
pfUI.api.libdebuff:AddEffect(targetName, targetLevel, baseName, rec.duration, "player")
if CleveRoids.debug then
DEFAULT_CHAT_FRAME:AddMessage(
string.format("|cff00ffaa[Judgement Refresh]|r Refreshed %s (ID:%d) on %s hit - new duration: %ds",
baseName, spellID, action, rec.duration)
string.format("|cff00ffaa[pfUI Judgement Refresh]|r Synced %s refresh to pfUI", baseName)
)
end
-- Also sync to pfUI if it's loaded
if pfUI and pfUI.api and pfUI.api.libdebuff then
local targetName = (lib.guidToName and lib.guidToName[normalizedTarget]) or UnitName("target")
local targetLevel = UnitLevel("target") or 0
if targetName then
-- Refresh in pfUI's tracking (by name)
pfUI.api.libdebuff:AddEffect(targetName, targetLevel, baseName, rec.duration, "player")
if CleveRoids.debug then
DEFAULT_CHAT_FRAME:AddMessage(
string.format("|cff00ffaa[pfUI Judgement Refresh]|r Synced %s refresh to pfUI", baseName)
)
end
end
end
end
end
end
@@ -4983,19 +5141,43 @@ function CleveRoids.Frame:PLAYER_TARGET_CHANGED()
-- Clear resist state when target changes
CleveRoids.ClearResistState()
-- Reset any sequence with reset=target that has progressed past the first step
-- Only reset if the target GUID actually changed (not just re-targeting same mob)
-- Handle sequences with reset=target - save/restore per-target state
-- Instead of resetting, we remember each target's progress in the sequence
local currentGuid = nil
if UnitExists("target") then
local _, guid = UnitExists("target")
currentGuid = guid
end
for _, sequence in pairs(CleveRoids.Sequences) do
if sequence.index > 1 and sequence.reset and sequence.reset.target then
-- Compare with stored GUID - reset only if different
-- If no target now, currentGuid is nil, which differs from stored GUID
if sequence.lastTargetGuid ~= currentGuid then
CleveRoids.ResetSequence(sequence)
if sequence.reset and sequence.reset.target then
local oldGuid = sequence.lastTargetGuid
-- Only process if target actually changed
if oldGuid ~= currentGuid then
-- Save current state for the old target (if we had one and progressed)
if oldGuid and sequence.index > 1 then
sequence.perTargetState = sequence.perTargetState or {}
sequence.perTargetState[oldGuid] = {
index = sequence.index,
lastUpdate = sequence.lastUpdate,
}
end
-- Restore state for the new target (or start fresh)
if currentGuid and sequence.perTargetState and sequence.perTargetState[currentGuid] then
-- Restore saved state for this target
local saved = sequence.perTargetState[currentGuid]
sequence.index = saved.index
sequence.lastUpdate = saved.lastUpdate
else
-- New target we haven't seen - start at step 1
sequence.index = 1
sequence.status = 0
end
-- Update the current target GUID
sequence.lastTargetGuid = currentGuid
end
end
end
+73 -1
View File
@@ -44,11 +44,21 @@
- Detailed hit info: damage, crit/glancing/crushing, dodge/parry/block
- Requires NP_EnableAutoAttackEvents=1 CVar to enable
Spell Start/Go Events (v2.25+):
- SPELL_START_SELF / SPELL_START_OTHER - Server notifies cast time spell begun
- SPELL_GO_SELF / SPELL_GO_OTHER - Server notifies spell completed casting
- Requires NP_EnableSpellStartEvents=1 and NP_EnableSpellGoEvents=1 CVars
Spell Heal/Energize Events (v2.26+):
- SPELL_HEAL_BY_SELF / SPELL_HEAL_BY_OTHER / SPELL_HEAL_ON_SELF
- SPELL_ENERGIZE_BY_SELF / SPELL_ENERGIZE_BY_OTHER / SPELL_ENERGIZE_ON_SELF
- Requires NP_EnableSpellHealEvents=1 and NP_EnableSpellEnergizeEvents=1 CVars
Settings Integration:
- Reads from NampowerSettings addon when available
- Falls back to CVars when addon not present
Current version: v2.24.0
Current version: v2.26.0
]]
local _G = _G or getfenv(0)
@@ -171,6 +181,14 @@ API.VERSION_REQUIREMENTS = {
-- v2.24+ - Auto-attack events
["AutoAttackEvents"] = { 2, 24, 0 }, -- AUTO_ATTACK_SELF/OTHER events
-- v2.25+ - Spell start/go events (improved granularity over UNIT_CASTEVENT)
["SpellStartEvents"] = { 2, 25, 0 }, -- SPELL_START_SELF/OTHER events
["SpellGoEvents"] = { 2, 25, 0 }, -- SPELL_GO_SELF/OTHER events
-- v2.26+ - Spell heal/energize events
["SpellHealEvents"] = { 2, 26, 0 }, -- SPELL_HEAL_BY_SELF/OTHER/ON_SELF events
["SpellEnergizeEvents"] = { 2, 26, 0 }, -- SPELL_ENERGIZE_BY_SELF/OTHER/ON_SELF events
}
-- Check if a specific feature is available
@@ -277,6 +295,14 @@ local function InitializeFeatures()
-- v2.24+ Auto-attack events
f.hasAutoAttackEvents = API.HasFeature("AutoAttackEvents")
-- v2.25+ Spell start/go events
f.hasSpellStartEvents = API.HasFeature("SpellStartEvents")
f.hasSpellGoEvents = API.HasFeature("SpellGoEvents")
-- v2.26+ Spell heal/energize events
f.hasSpellHealEvents = API.HasFeature("SpellHealEvents")
f.hasSpellEnergizeEvents = API.HasFeature("SpellEnergizeEvents")
-- Runtime detection for enhanced spell functions (verify by testing)
if f.hasEnhancedSpellFunctions and GetSpellTexture then
local success, result = pcall(function()
@@ -336,6 +362,12 @@ API.defaultSettings = {
NP_EnableAuraCastEvents = "0", -- Enable AURA_CAST_ON_SELF/OTHER events
-- v2.24+ CVars
NP_EnableAutoAttackEvents = "0", -- Enable AUTO_ATTACK_SELF/OTHER events
-- v2.25+ CVars
NP_EnableSpellStartEvents = "0", -- Enable SPELL_START_SELF/OTHER events
NP_EnableSpellGoEvents = "0", -- Enable SPELL_GO_SELF/OTHER events
-- v2.26+ CVars
NP_EnableSpellHealEvents = "0", -- Enable SPELL_HEAL_BY_SELF/OTHER/ON_SELF events
NP_EnableSpellEnergizeEvents = "0", -- Enable SPELL_ENERGIZE_BY_SELF/OTHER/ON_SELF events
}
-- Get a Nampower setting value
@@ -1922,6 +1954,46 @@ API.VICTIMSTATE = {
DEFLECTS = 8,
}
-- Spell start/go event names (v2.25+)
-- These provide improved granularity over UNIT_CASTEVENT for spell cast tracking
-- SPELL_START: Server notifies a spell with cast time has begun
-- SPELL_GO: Server notifies a spell has completed casting (projectile launched, instant landed)
API.SPELL_START_EVENTS = {
"SPELL_START_SELF", -- Player starts casting (requires NP_EnableSpellStartEvents=1)
"SPELL_START_OTHER", -- Other unit starts casting
}
API.SPELL_GO_EVENTS = {
"SPELL_GO_SELF", -- Player's spell completed/fired (requires NP_EnableSpellGoEvents=1)
"SPELL_GO_OTHER", -- Other unit's spell completed/fired
}
-- Spell heal event names (v2.26+, requires NP_EnableSpellHealEvents=1)
-- Parameters: targetGuid, casterGuid, spellId, amount, isCritical, isPeriodic
API.SPELL_HEAL_EVENTS = {
"SPELL_HEAL_BY_SELF", -- Player healed a target
"SPELL_HEAL_BY_OTHER", -- Other unit healed a target
"SPELL_HEAL_ON_SELF", -- Player was healed
}
-- Spell energize event names (v2.26+, requires NP_EnableSpellEnergizeEvents=1)
-- Parameters: targetGuid, casterGuid, spellId, powerType, amount, isPeriodic
-- powerType: 0=mana, 1=rage, 2=focus, 3=energy, 4=happiness
API.SPELL_ENERGIZE_EVENTS = {
"SPELL_ENERGIZE_BY_SELF", -- Player energized a target
"SPELL_ENERGIZE_BY_OTHER", -- Other unit energized a target
"SPELL_ENERGIZE_ON_SELF", -- Player was energized
}
-- Power type constants (for SPELL_ENERGIZE events)
API.POWER_TYPE = {
MANA = 0,
RAGE = 1,
FOCUS = 2,
ENERGY = 3,
HAPPINESS = 4,
}
-- Unit events (v2.20+)
API.UNIT_EVENTS = {
"UNIT_DIED", -- Parameters: guid
+1 -1
View File
@@ -2,7 +2,7 @@
## Author: DWG, _brain, Torio, yani9o, Avitasia
## Title: [Super]CleveRoidMacros
## Notes: /cleveroid for settings
## Version: 1.8
## Version: 1.9
## OptionalDeps: ClassicFocus, FocusFrame, pfUI, SuperMacro, Bongos_ActionBar, Cursive, XPerl, LunaUnitFrames, DragonflightReloaded, -Dragonflight3
## SavedVariables: CleveRoidMacros, CleveRoids_LearnedDurations, CleveRoids_AuraTextures, CleveRoids_ImmunityData, CleveRoids_ComboDurations, CleveRoids_SpellSchools
Localization.lua
+1494 -259
View File
File diff suppressed because it is too large Load Diff