mirror of
https://github.com/brues-code/SuperCleveRoidMacros.git
synced 2026-09-16 03:38:00 +00:00
feature addition multi target debuff tracker
This commit is contained in:
+1
-19
@@ -574,25 +574,7 @@ function CleveRoids.ShowComboTracking()
|
||||
end
|
||||
end
|
||||
|
||||
-- Slash command for debugging
|
||||
SLASH_COMBOTRACK1 = "/combotrack"
|
||||
SlashCmdList.COMBOTRACK = function(msg)
|
||||
if msg == "show" then
|
||||
CleveRoids.ShowComboTracking()
|
||||
elseif msg == "clear" then
|
||||
CleveRoids.ComboPointTracking = {}
|
||||
CleveRoids.ComboPointTracking.byID = {}
|
||||
CleveRoids.Print("Combo tracking data cleared")
|
||||
elseif msg == "debug" then
|
||||
CleveRoids.debug = not CleveRoids.debug
|
||||
CleveRoids.Print("Combo tracking debug: " .. (CleveRoids.debug and "ON" or "OFF"))
|
||||
else
|
||||
CleveRoids.Print("ComboTrack commands:")
|
||||
CleveRoids.Print(" /combotrack show - Display current tracking data")
|
||||
CleveRoids.Print(" /combotrack clear - Clear tracking data")
|
||||
CleveRoids.Print(" /combotrack debug - Toggle debug output")
|
||||
end
|
||||
end
|
||||
-- Combo commands now consolidated under /cleveroid combotrack, comboclear, combodebug
|
||||
|
||||
-- Export to global namespace NOW, before Extension registration
|
||||
_G["CleveRoids"] = CleveRoids
|
||||
|
||||
+259
-13
@@ -4815,6 +4815,31 @@ CleveRoids.MULTISCAN_PRIORITIES = {
|
||||
triangle = 4, diamond = 3, circle = 2, star = 1,
|
||||
}
|
||||
|
||||
-- Multiscan options (Cursive-inspired filters that can combine with any priority)
|
||||
-- Usage: [multiscan:priority,option1,option2,minhp=1000,refreshtime=3,name="Mob Name"]
|
||||
-- Options:
|
||||
-- allowooc - Allow out of combat targets (default: only in-combat)
|
||||
-- priotarget - Always prioritize current target first
|
||||
-- ignoretarget - Ignore current target when scanning
|
||||
-- playeronly - Only target players, ignore NPCs
|
||||
-- npconly - Only target NPCs, ignore players
|
||||
-- tracked - Only consider targets in MultiTargetTracker
|
||||
-- minhp=X - Minimum HP threshold (raw value)
|
||||
-- maxhp=X - Maximum HP threshold (raw value)
|
||||
-- minhppct=X - Minimum HP percentage (0-100)
|
||||
-- maxhppct=X - Maximum HP percentage (0-100)
|
||||
-- refreshtime=X - Only target if debuff has less than X seconds remaining (for DoT refresh)
|
||||
-- name=X - Filter by name (partial match, use quotes for spaces: name="Mob Name")
|
||||
-- noname=X - Exclude targets with name containing X
|
||||
CleveRoids.MULTISCAN_OPTIONS = {
|
||||
allowooc = true,
|
||||
priotarget = true,
|
||||
ignoretarget = true,
|
||||
playeronly = true,
|
||||
npconly = true,
|
||||
tracked = true,
|
||||
}
|
||||
|
||||
-- Static conditionals that don't depend on target (checked before scanning)
|
||||
CleveRoids.STATIC_CONDITIONALS = {
|
||||
group = true, nogroup = true,
|
||||
@@ -4841,19 +4866,30 @@ CleveRoids.STATIC_CONDITIONALS = {
|
||||
--- @param priority string Priority type (e.g., "nearest", "highesthp")
|
||||
--- @param currentTargetGuid string|nil GUID of player's current target (exempt from combat check)
|
||||
--- @param specifiedUnitGuid string|nil GUID of @unit specified in macro (also exempt from combat check)
|
||||
--- @param options table|nil Parsed multiscan options
|
||||
--- @param conditionals table|nil Full conditionals for options checking
|
||||
--- @return number|nil Score (lower = better) or nil if invalid candidate
|
||||
function CleveRoids.GetMultiscanScore(unit, priority, currentTargetGuid, specifiedUnitGuid)
|
||||
function CleveRoids.GetMultiscanScore(unit, priority, currentTargetGuid, specifiedUnitGuid, options, conditionals)
|
||||
if not unit or not UnitExists(unit) then return nil end
|
||||
if UnitIsDeadOrGhost(unit) then return nil end
|
||||
|
||||
-- Must be attackable
|
||||
if not UnitCanAttack("player", unit) then return nil end
|
||||
|
||||
-- Combat check: must be in combat with player, UNLESS it's current target OR specified @unit
|
||||
local _, unitGuid = UnitExists(unit)
|
||||
local isCurrentTarget = currentTargetGuid and unitGuid == currentTargetGuid
|
||||
local isSpecifiedUnit = specifiedUnitGuid and unitGuid == specifiedUnitGuid
|
||||
if not isCurrentTarget and not isSpecifiedUnit and not UnitAffectingCombat(unit) then
|
||||
|
||||
-- Combat check: must be in combat with player, UNLESS:
|
||||
-- - it's current target OR specified @unit
|
||||
-- - OR allowooc option is set
|
||||
local allowOOC = options and options.allowooc
|
||||
if not allowOOC and not isCurrentTarget and not isSpecifiedUnit and not UnitAffectingCombat(unit) then
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Check additional options filters
|
||||
if options and not CleveRoids.CheckMultiscanOptions(unit, unitGuid, options, conditionals, currentTargetGuid) then
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -4935,6 +4971,212 @@ function CleveRoids.CheckStaticConditionals(conditionals)
|
||||
return true
|
||||
end
|
||||
|
||||
--- Parse multiscan options from the conditional value
|
||||
--- @param multiscanValue string|table The raw multiscan conditional value
|
||||
--- @return string priority, table options
|
||||
function CleveRoids.ParseMultiscanOptions(multiscanValue)
|
||||
local options = {
|
||||
allowooc = false,
|
||||
priotarget = false,
|
||||
ignoretarget = false,
|
||||
playeronly = false,
|
||||
npconly = false,
|
||||
tracked = false,
|
||||
minhp = nil,
|
||||
maxhp = nil,
|
||||
minhppct = nil,
|
||||
maxhppct = nil,
|
||||
refreshtime = nil,
|
||||
name = nil,
|
||||
noname = nil,
|
||||
}
|
||||
|
||||
local priority = nil
|
||||
local values = {}
|
||||
|
||||
-- Collect all values
|
||||
if type(multiscanValue) == "table" then
|
||||
for i = 1, table.getn(multiscanValue) do
|
||||
table.insert(values, multiscanValue[i])
|
||||
end
|
||||
elseif type(multiscanValue) == "string" then
|
||||
-- Single value, might contain commas? No, conditionals system splits by /
|
||||
-- The value is just the priority in this case
|
||||
table.insert(values, multiscanValue)
|
||||
end
|
||||
|
||||
-- First value is always the priority
|
||||
if table.getn(values) > 0 then
|
||||
priority = string.lower(CleveRoids.Trim(values[1]))
|
||||
end
|
||||
|
||||
-- Remaining values are options
|
||||
for i = 2, table.getn(values) do
|
||||
local opt = CleveRoids.Trim(values[i])
|
||||
local optLower = string.lower(opt)
|
||||
|
||||
-- Check for key=value options
|
||||
local eqStart, eqEnd = string.find(optLower, "=")
|
||||
if eqStart then
|
||||
local key = string.sub(optLower, 1, eqStart - 1)
|
||||
local value = string.sub(opt, eqEnd + 1) -- Keep original case for name
|
||||
|
||||
-- Remove quotes from value if present
|
||||
if string.sub(value, 1, 1) == '"' and string.sub(value, -1) == '"' then
|
||||
value = string.sub(value, 2, -2)
|
||||
end
|
||||
|
||||
if key == "minhp" then
|
||||
options.minhp = tonumber(value)
|
||||
elseif key == "maxhp" then
|
||||
options.maxhp = tonumber(value)
|
||||
elseif key == "minhppct" then
|
||||
options.minhppct = tonumber(value)
|
||||
elseif key == "maxhppct" then
|
||||
options.maxhppct = tonumber(value)
|
||||
elseif key == "refreshtime" then
|
||||
options.refreshtime = tonumber(value)
|
||||
elseif key == "name" then
|
||||
options.name = value
|
||||
elseif key == "noname" then
|
||||
options.noname = value
|
||||
end
|
||||
else
|
||||
-- Boolean flag options
|
||||
if optLower == "allowooc" then
|
||||
options.allowooc = true
|
||||
elseif optLower == "priotarget" then
|
||||
options.priotarget = true
|
||||
elseif optLower == "ignoretarget" then
|
||||
options.ignoretarget = true
|
||||
elseif optLower == "playeronly" then
|
||||
options.playeronly = true
|
||||
elseif optLower == "npconly" then
|
||||
options.npconly = true
|
||||
elseif optLower == "tracked" then
|
||||
options.tracked = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return priority, options
|
||||
end
|
||||
|
||||
--- Check if a unit passes multiscan options filters
|
||||
--- @param unit string Unit token
|
||||
--- @param guid string Unit GUID
|
||||
--- @param options table Parsed options
|
||||
--- @param conditionals table Full conditionals table (for refreshtime spell check)
|
||||
--- @param currentTargetGuid string|nil Current target GUID
|
||||
--- @return boolean passes
|
||||
function CleveRoids.CheckMultiscanOptions(unit, guid, options, conditionals, currentTargetGuid)
|
||||
-- ignoretarget: Skip current target
|
||||
if options.ignoretarget and currentTargetGuid and guid == currentTargetGuid then
|
||||
return false
|
||||
end
|
||||
|
||||
-- playeronly: Only players
|
||||
if options.playeronly and not UnitIsPlayer(unit) then
|
||||
return false
|
||||
end
|
||||
|
||||
-- npconly: Only NPCs
|
||||
if options.npconly and UnitIsPlayer(unit) then
|
||||
return false
|
||||
end
|
||||
|
||||
-- tracked: Only tracked targets
|
||||
if options.tracked then
|
||||
local tracker = CleveRoids.MultiTargetTracker
|
||||
if tracker and tracker.IsTracked then
|
||||
if not tracker.IsTracked(guid) then
|
||||
return false
|
||||
end
|
||||
else
|
||||
return false -- Tracker not available
|
||||
end
|
||||
end
|
||||
|
||||
-- minhp: Minimum raw HP
|
||||
if options.minhp then
|
||||
local hp = UnitHealth(unit)
|
||||
if hp < options.minhp then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- maxhp: Maximum raw HP
|
||||
if options.maxhp then
|
||||
local hp = UnitHealth(unit)
|
||||
if hp > options.maxhp then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- minhppct: Minimum HP percentage
|
||||
if options.minhppct then
|
||||
local maxHp = UnitHealthMax(unit)
|
||||
if maxHp > 0 then
|
||||
local pct = (UnitHealth(unit) / maxHp) * 100
|
||||
if pct < options.minhppct then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- maxhppct: Maximum HP percentage
|
||||
if options.maxhppct then
|
||||
local maxHp = UnitHealthMax(unit)
|
||||
if maxHp > 0 then
|
||||
local pct = (UnitHealth(unit) / maxHp) * 100
|
||||
if pct > options.maxhppct then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- name: Filter by name (partial match)
|
||||
if options.name then
|
||||
local unitName = UnitName(unit) or ""
|
||||
if not string.find(string.lower(unitName), string.lower(options.name)) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- noname: Exclude by name (partial match)
|
||||
if options.noname then
|
||||
local unitName = UnitName(unit) or ""
|
||||
if string.find(string.lower(unitName), string.lower(options.noname)) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- refreshtime: Only target if debuff expires within X seconds
|
||||
-- Uses the spell from conditionals.action (the spell being cast)
|
||||
if options.refreshtime then
|
||||
local tracker = CleveRoids.MultiTargetTracker
|
||||
if tracker and tracker.GetActiveDebuffs then
|
||||
local debuffs, count = tracker.GetActiveDebuffs(guid)
|
||||
if debuffs and count > 0 then
|
||||
-- Check if any debuff has more than refreshtime remaining
|
||||
local hasLongDebuff = false
|
||||
for i = 1, count do
|
||||
if debuffs[i].remaining > options.refreshtime then
|
||||
hasLongDebuff = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if hasLongDebuff then
|
||||
return false -- Has debuff with too much time remaining
|
||||
end
|
||||
end
|
||||
-- No debuffs or all debuffs are expiring soon - pass
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--- Main multiscan resolution function
|
||||
--- Scans enemies using UnitXP, finds best target matching priority and conditionals
|
||||
--- @param conditionals table The conditionals table containing multiscan value
|
||||
@@ -4946,16 +5188,10 @@ function CleveRoids.ResolveMultiscanTarget(conditionals, specifiedUnit)
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Parse priority from conditionals.multiscan
|
||||
local priority = nil
|
||||
if type(conditionals.multiscan) == "table" then
|
||||
priority = conditionals.multiscan[1]
|
||||
elseif type(conditionals.multiscan) == "string" then
|
||||
priority = conditionals.multiscan
|
||||
end
|
||||
-- Parse priority and options from conditionals.multiscan
|
||||
local priority, options = CleveRoids.ParseMultiscanOptions(conditionals.multiscan)
|
||||
|
||||
if not priority then return nil end
|
||||
priority = string.lower(CleveRoids.Trim(priority))
|
||||
|
||||
-- Validate priority type
|
||||
local priorityType = CleveRoids.MULTISCAN_PRIORITIES[priority]
|
||||
@@ -5095,7 +5331,7 @@ function CleveRoids.ResolveMultiscanTarget(conditionals, specifiedUnit)
|
||||
end
|
||||
end
|
||||
|
||||
local score = CleveRoids.GetMultiscanScore(unit, priorityType, currentTargetGuid, specifiedUnitGuid)
|
||||
local score = CleveRoids.GetMultiscanScore(unit, priorityType, currentTargetGuid, specifiedUnitGuid, options, conditionals)
|
||||
if not score then return end
|
||||
|
||||
-- Validate against target-dependent conditionals
|
||||
@@ -5104,8 +5340,18 @@ function CleveRoids.ResolveMultiscanTarget(conditionals, specifiedUnit)
|
||||
end
|
||||
end
|
||||
|
||||
-- Handle priotarget option: always check current target first
|
||||
if options.priotarget and currentTargetGuid then
|
||||
evaluateCandidate("target")
|
||||
-- If current target is valid, return it immediately
|
||||
if table.getn(candidates) > 0 then
|
||||
return candidates[1].guid
|
||||
end
|
||||
end
|
||||
|
||||
-- Always consider current target (exempt from combat check via GetMultiscanScore)
|
||||
if currentTargetGuid then
|
||||
-- Skip if priotarget already checked it
|
||||
if currentTargetGuid and not options.priotarget then
|
||||
evaluateCandidate("target")
|
||||
end
|
||||
|
||||
|
||||
@@ -4772,6 +4772,7 @@ SlashCmdList["CLEVEROID"] = function(msg)
|
||||
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid combotrack - Show combo point tracking info')
|
||||
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid comboclear - Clear combo tracking data')
|
||||
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid combolearn - Show learned combo durations (per CP)')
|
||||
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid combodebug - Toggle combo tracking debug output')
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffaa00Talent Modifiers:|r")
|
||||
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid talenttabs - Show talent tab IDs for your class')
|
||||
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid listtab <tab> - List all talents in a tab with their IDs')
|
||||
@@ -4782,6 +4783,8 @@ SlashCmdList["CLEVEROID"] = function(msg)
|
||||
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid testequip <spellID> - Test equipment modifier for a spell')
|
||||
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid equipdebug <item name> - Debug item lookup for /equip')
|
||||
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid equiplog - Toggle real-time equip command logging')
|
||||
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid equipqueue clear - Clear pending equipment queue')
|
||||
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid equipqueue status - Show queue status')
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffaa00Reactive Proc Tracking:|r")
|
||||
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid listprocs - Show active reactive ability procs')
|
||||
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid setproc <spell> [duration] - Manually set proc (testing)')
|
||||
@@ -4797,6 +4800,8 @@ SlashCmdList["CLEVEROID"] = function(msg)
|
||||
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid slamdebug - Show Slam cast time and clip window calculations')
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffaa00Debuff Tracking Debug:|r")
|
||||
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid debuffdebug [spell] - Debug debuff tracking on target')
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffaa00Multi-Target Tracker:|r")
|
||||
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid mtt - Show Multi-Target Tracker commands')
|
||||
return
|
||||
end
|
||||
|
||||
@@ -5456,6 +5461,13 @@ SlashCmdList["CLEVEROID"] = function(msg)
|
||||
return
|
||||
end
|
||||
|
||||
-- combodebug (toggle combo tracking debug)
|
||||
if cmd == "combodebug" then
|
||||
CleveRoids.debug = not CleveRoids.debug
|
||||
CleveRoids.Print("Combo tracking debug: " .. (CleveRoids.debug and "ON" or "OFF"))
|
||||
return
|
||||
end
|
||||
|
||||
-- talenttabs (show talent tab IDs)
|
||||
if cmd == "talenttabs" or cmd == "tabs" then
|
||||
CleveRoids.Print("=== Talent Tabs ===")
|
||||
@@ -6214,6 +6226,91 @@ SlashCmdList["CLEVEROID"] = function(msg)
|
||||
return
|
||||
end
|
||||
|
||||
-- equipqueue (Equipment Queue management)
|
||||
if cmd == "equipqueue" or cmd == "eq" then
|
||||
if val == "clear" then
|
||||
-- Release all entries back to pool
|
||||
for i = 1, CleveRoids.equipmentQueueLen do
|
||||
local entry = CleveRoids.equipmentQueue[i]
|
||||
if entry then
|
||||
CleveRoids.equipmentQueue[i] = nil
|
||||
-- Return to pool if space available
|
||||
if table.getn(CleveRoids.queueEntryPool) < 10 then
|
||||
entry.item = nil
|
||||
entry.slotName = nil
|
||||
entry.inventoryId = nil
|
||||
table.insert(CleveRoids.queueEntryPool, entry)
|
||||
end
|
||||
end
|
||||
end
|
||||
CleveRoids.equipmentQueueLen = 0
|
||||
if CleveRoids.equipQueueFrame then
|
||||
CleveRoids.equipQueueFrame:Hide()
|
||||
end
|
||||
CleveRoids.Print("Equipment queue cleared")
|
||||
elseif val == "status" then
|
||||
local count = CleveRoids.equipmentQueueLen
|
||||
CleveRoids.Print("Equipment queue has " .. count .. " pending items")
|
||||
for i = 1, count do
|
||||
local entry = CleveRoids.equipmentQueue[i]
|
||||
if entry then
|
||||
local itemName = (entry.item and entry.item.name) or "Unknown"
|
||||
local slotName = entry.slotName or "Unknown"
|
||||
local retries = entry.retries or 0
|
||||
CleveRoids.Print(i .. ". " .. itemName .. " -> " .. slotName .. " (retries: " .. retries .. ")")
|
||||
end
|
||||
end
|
||||
else
|
||||
CleveRoids.Print("Equipment Queue commands:")
|
||||
CleveRoids.Print(" /cleveroid equipqueue clear - Clear pending equipment queue")
|
||||
CleveRoids.Print(" /cleveroid equipqueue status - Show queue status")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- mtt (Multi-Target Tracker)
|
||||
if cmd == "mtt" then
|
||||
local tracker = CleveRoids.MultiTargetTracker
|
||||
if not tracker then
|
||||
CleveRoids.Print("Multi-Target Tracker not loaded!")
|
||||
return
|
||||
end
|
||||
|
||||
if val == "unlock" then
|
||||
tracker.Unlock()
|
||||
elseif val == "lock" then
|
||||
tracker.Lock()
|
||||
elseif val == "show" then
|
||||
tracker.Show()
|
||||
CleveRoids.Print("Multi-Target Tracker: Shown")
|
||||
elseif val == "hide" then
|
||||
tracker.Hide()
|
||||
CleveRoids.Print("Multi-Target Tracker: Hidden")
|
||||
elseif val == "toggle" then
|
||||
tracker.Toggle()
|
||||
elseif val == "clear" then
|
||||
tracker.ClearTargets()
|
||||
elseif val == "reset" then
|
||||
tracker.ResetPosition()
|
||||
elseif val == "debug" then
|
||||
tracker.Debug()
|
||||
elseif val == "dump" then
|
||||
tracker.DumpState()
|
||||
else
|
||||
CleveRoids.Print("Multi-Target Tracker commands:")
|
||||
CleveRoids.Print(" /cleveroid mtt unlock - Show frame for positioning")
|
||||
CleveRoids.Print(" /cleveroid mtt lock - Lock and resume auto-hide")
|
||||
CleveRoids.Print(" /cleveroid mtt show - Show the tracker frame")
|
||||
CleveRoids.Print(" /cleveroid mtt hide - Hide the tracker frame")
|
||||
CleveRoids.Print(" /cleveroid mtt toggle - Toggle frame visibility")
|
||||
CleveRoids.Print(" /cleveroid mtt clear - Clear all tracked targets")
|
||||
CleveRoids.Print(" /cleveroid mtt reset - Reset frame position")
|
||||
CleveRoids.Print(" /cleveroid mtt debug - Toggle debug mode")
|
||||
CleveRoids.Print(" /cleveroid mtt dump - Dump tracking state")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- Unknown command fallback
|
||||
CleveRoids.Print("Usage:")
|
||||
DEFAULT_CHAT_FRAME:AddMessage("/cleveroid - Show current settings")
|
||||
@@ -6238,43 +6335,12 @@ SlashCmdList["CLEVEROID"] = function(msg)
|
||||
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid combotrack - Show combo point tracking info')
|
||||
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid comboclear - Clear combo tracking data')
|
||||
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid combolearn - Show learned combo durations (per CP)')
|
||||
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid combodebug - Toggle combo tracking debug output')
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffaa00Equipment Queue:|r")
|
||||
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid equipqueue clear - Clear pending equipment queue')
|
||||
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid equipqueue status - Show queue status')
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffaa00Multi-Target Tracker:|r")
|
||||
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid mtt - Show Multi-Target Tracker commands')
|
||||
end
|
||||
|
||||
SLASH_CLEAREQUIPQUEUE1 = "/clearequipqueue"
|
||||
SlashCmdList.CLEAREQUIPQUEUE = function()
|
||||
-- Release all entries back to pool
|
||||
for i = 1, CleveRoids.equipmentQueueLen do
|
||||
local entry = CleveRoids.equipmentQueue[i]
|
||||
if entry then
|
||||
CleveRoids.equipmentQueue[i] = nil
|
||||
-- Return to pool if space available
|
||||
if table.getn(CleveRoids.queueEntryPool) < 10 then
|
||||
entry.item = nil
|
||||
entry.slotName = nil
|
||||
entry.inventoryId = nil
|
||||
table.insert(CleveRoids.queueEntryPool, entry)
|
||||
end
|
||||
end
|
||||
end
|
||||
CleveRoids.equipmentQueueLen = 0
|
||||
if CleveRoids.equipQueueFrame then
|
||||
CleveRoids.equipQueueFrame:Hide()
|
||||
end
|
||||
CleveRoids.Print("Equipment queue cleared")
|
||||
end
|
||||
|
||||
SLASH_EQUIPQUEUESTATUS1 = "/equipqueuestatus"
|
||||
SlashCmdList.EQUIPQUEUESTATUS = function()
|
||||
local count = CleveRoids.equipmentQueueLen
|
||||
CleveRoids.Print("Equipment queue has " .. count .. " pending items")
|
||||
|
||||
for i = 1, count do
|
||||
local entry = CleveRoids.equipmentQueue[i]
|
||||
if entry then
|
||||
local itemName = (entry.item and entry.item.name) or "Unknown"
|
||||
local slotName = entry.slotName or "Unknown"
|
||||
local retries = entry.retries or 0
|
||||
CleveRoids.Print(i .. ". " .. itemName .. " -> " .. slotName .. " (retries: " .. retries .. ")")
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Equipment queue commands consolidated under /cleveroid equipqueue clear|status
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -347,6 +347,52 @@ Works with ALL casting methods:
|
||||
|
||||
---
|
||||
|
||||
## Multi-Target Tracker
|
||||
|
||||
Visual UI extension that tracks your debuffs across multiple enemy targets. Displays health bars with debuff icons and timers, styled to match Cursive/ShaguScan.
|
||||
|
||||
### Features
|
||||
- Tracks up to 15 targets simultaneously
|
||||
- Shows health bar, name, raid icon, and debuff timers per target
|
||||
- Automatic cleanup when targets die or leave combat
|
||||
- Only shows YOUR debuffs (class-aware filtering)
|
||||
- Click target rows to target that enemy
|
||||
|
||||
### Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `/cleveroid mtt` | Show all MTT commands |
|
||||
| `/cleveroid mtt unlock` | Unlock frame for repositioning |
|
||||
| `/cleveroid mtt lock` | Lock and resume auto-hide |
|
||||
| `/cleveroid mtt show` | Show the tracker frame |
|
||||
| `/cleveroid mtt hide` | Hide the tracker frame |
|
||||
| `/cleveroid mtt toggle` | Toggle frame visibility |
|
||||
| `/cleveroid mtt clear` | Clear all tracked targets |
|
||||
| `/cleveroid mtt reset` | Reset frame position |
|
||||
| `/cleveroid mtt debug` | Toggle debug mode |
|
||||
| `/cleveroid mtt dump` | Dump tracking state |
|
||||
|
||||
### Tracked Debuff Categories
|
||||
|
||||
**AoE Debuffs** (multi-target tracking):
|
||||
|
||||
**Proc/Special Debuffs** (item procs, taunts):
|
||||
|
||||
**Personal Debuffs**:
|
||||
All class-specific DoTs, debuffs, and effects from the debuff timer system are automatically tracked.
|
||||
|
||||
**Shared Debuffs**:
|
||||
Debuffs like Sunder Armor, Faerie Fire, and Judgements are only shown if YOU can cast them (class-aware filtering prevents showing other players' debuffs).
|
||||
|
||||
### Auto-Cleanup Behavior
|
||||
- Dead targets are removed immediately
|
||||
- Targets with no active debuffs are removed when:
|
||||
- The target leaves combat, OR
|
||||
- You leave combat
|
||||
|
||||
---
|
||||
|
||||
## Talent Modifiers
|
||||
|
||||
Automatically applies talent-based duration modifications to debuff tracking.
|
||||
|
||||
@@ -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
|
||||
## SavedVariables: CleveRoidMacros, CleveRoids_LearnedDurations, CleveRoids_AuraTextures, CleveRoids_ImmunityData, CleveRoids_ComboDurations, CleveRoids_SpellSchools
|
||||
Localization.lua
|
||||
@@ -35,3 +35,4 @@ Extensions\Mouseover\ag_UnitFrames.lua
|
||||
|
||||
Extensions\Tooltip\Generic.lua
|
||||
|
||||
Extensions\MultiTargetTracker.lua
|
||||
|
||||
+58
-8
@@ -17,13 +17,30 @@ local tonumber = tonumber
|
||||
local tostring = tostring
|
||||
local GetTime = GetTime
|
||||
|
||||
-- GUID normalization: ensure all GUIDs are strings for consistent table key lookups
|
||||
-- In Lua, table["123"] is different from table[123], so we must normalize
|
||||
-- GUID normalization: ensures consistent string format for table keys
|
||||
-- Preserves original format (including 0x prefix) for SuperWoW API compatibility
|
||||
function CleveRoids.NormalizeGUID(guid)
|
||||
if not guid then return nil end
|
||||
return tostring(guid)
|
||||
end
|
||||
|
||||
-- Compare two GUIDs for equality, handling format differences (0x prefix, case)
|
||||
-- Use this when comparing GUIDs from different sources (e.g., UnitExists vs AURA_CAST_ON_OTHER)
|
||||
function CleveRoids.GUIDsMatch(guid1, guid2)
|
||||
if not guid1 or not guid2 then return false end
|
||||
local s1 = tostring(guid1)
|
||||
local s2 = tostring(guid2)
|
||||
-- Strip 0x prefix if present for comparison
|
||||
if string.sub(s1, 1, 2) == "0x" or string.sub(s1, 1, 2) == "0X" then
|
||||
s1 = string.sub(s1, 3)
|
||||
end
|
||||
if string.sub(s2, 1, 2) == "0x" or string.sub(s2, 1, 2) == "0X" then
|
||||
s2 = string.sub(s2, 3)
|
||||
end
|
||||
-- Case-insensitive comparison
|
||||
return string.lower(s1) == string.lower(s2)
|
||||
end
|
||||
|
||||
-- Hidden tooltip for scanning spell info
|
||||
local SpellScanTooltip = nil
|
||||
|
||||
@@ -597,6 +614,18 @@ lib.personalDebuffs = lib.personalDebuffs or {
|
||||
|
||||
[12323] = 6, -- Piercing Howl
|
||||
|
||||
[12721] = 12, -- Deep Wound (Rank 1) - actual debuff, not talent trigger
|
||||
[12722] = 12, -- Deep Wound (Rank 2)
|
||||
[12723] = 12, -- Deep Wound (Rank 3)
|
||||
|
||||
[355] = 3, -- Taunt
|
||||
[1161] = 6, -- Challenging Shout
|
||||
[694] = 6, -- Mocking Blow (Rank 1)
|
||||
[7400] = 6, -- Mocking Blow (Rank 2)
|
||||
[7402] = 6, -- Mocking Blow (Rank 3)
|
||||
[20559] = 6, -- Mocking Blow (Rank 4)
|
||||
[20560] = 6, -- Mocking Blow (Rank 5)
|
||||
|
||||
-- ROGUE
|
||||
[2094] = 10, -- Blind
|
||||
[21060] = 10, -- Blind (alternate?)
|
||||
@@ -611,8 +640,17 @@ lib.personalDebuffs = lib.personalDebuffs or {
|
||||
[11285] = 4, -- Gouge (Rank 4)
|
||||
[11286] = 4, -- Gouge (Rank 5)
|
||||
|
||||
-- NOTE: Rupture removed - handled by ComboPointTracker (base 8s + 2s per CP)
|
||||
-- NOTE: Kidney Shot removed - handled by ComboPointTracker (base 1s/2s + 1s per CP)
|
||||
-- NOTE: Rupture/Kidney Shot durations calculated by ComboPointTracker
|
||||
-- Base durations here for MultiTargetTracker recognition
|
||||
[1943] = 16, -- Rupture (Rank 1) - base 8s + 2s per CP (max 5CP = 16s)
|
||||
[8639] = 16, -- Rupture (Rank 2)
|
||||
[8640] = 16, -- Rupture (Rank 3)
|
||||
[11273] = 16, -- Rupture (Rank 4)
|
||||
[11274] = 16, -- Rupture (Rank 5)
|
||||
[11275] = 16, -- Rupture (Rank 6)
|
||||
|
||||
[408] = 6, -- Kidney Shot (Rank 1) - base 1s + 1s per CP (max 5CP = 6s)
|
||||
[8643] = 6, -- Kidney Shot (Rank 2)
|
||||
|
||||
[703] = 18, -- Garrote (Rank 1)
|
||||
[8631] = 18, -- Garrote (Rank 2)
|
||||
@@ -754,6 +792,18 @@ lib.personalDebuffs = lib.personalDebuffs or {
|
||||
[9824] = 18, -- Pounce Bleed (Rank 2) - triggered by Pounce 9823
|
||||
[9826] = 18, -- Pounce Bleed (Rank 3) - triggered by Pounce 9827
|
||||
|
||||
[9005] = 2, -- Pounce Stun (Rank 1)
|
||||
[9823] = 3, -- Pounce Stun (Rank 2)
|
||||
[9827] = 3, -- Pounce Stun (Rank 3)
|
||||
|
||||
[99] = 30, -- Demoralizing Roar (Rank 1)
|
||||
[1735] = 30, -- Demoralizing Roar (Rank 2)
|
||||
[9490] = 30, -- Demoralizing Roar (Rank 3)
|
||||
[9747] = 30, -- Demoralizing Roar (Rank 4)
|
||||
[9898] = 30, -- Demoralizing Roar (Rank 5)
|
||||
|
||||
[5209] = 6, -- Challenging Roar
|
||||
|
||||
-- WARLOCK
|
||||
[172] = 12, -- Corruption (Rank 1)
|
||||
[6222] = 15, -- Corruption (Rank 2)
|
||||
@@ -950,10 +1000,10 @@ lib.sharedDebuffs = lib.sharedDebuffs or {
|
||||
[11597] = 30, -- Sunder Armor (Rank 5)
|
||||
|
||||
[6343] = 10, -- Thunder Clap (Rank 1)
|
||||
[8198] = 10, -- Thunder Clap (Rank 2)
|
||||
[8205] = 10, -- Thunder Clap (Rank 3)
|
||||
[11580] = 10, -- Thunder Clap (Rank 4)
|
||||
[11581] = 10, -- Thunder Clap (Rank 5)
|
||||
[8198] = 14, -- Thunder Clap (Rank 2)
|
||||
[8205] = 18, -- Thunder Clap (Rank 3)
|
||||
[11580] = 22, -- Thunder Clap (Rank 4)
|
||||
[11581] = 30, -- Thunder Clap (Rank 5)
|
||||
|
||||
[1160] = 30, -- Demoralizing Shout (Rank 1)
|
||||
[6190] = 30, -- Demoralizing Shout (Rank 2)
|
||||
|
||||
Reference in New Issue
Block a user