revert multi target tracker

This commit is contained in:
Jrc13245
2026-01-02 23:16:57 -05:00
parent 35b9baa95a
commit 003d230e52
9 changed files with 83 additions and 2074 deletions
+19 -1
View File
@@ -574,7 +574,25 @@ function CleveRoids.ShowComboTracking()
end
end
-- Combo commands now consolidated under /cleveroid combotrack, comboclear, combodebug
-- 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
-- Export to global namespace NOW, before Extension registration
_G["CleveRoids"] = CleveRoids
+13 -259
View File
@@ -4815,31 +4815,6 @@ 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,
@@ -4866,30 +4841,19 @@ 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, options, conditionals)
function CleveRoids.GetMultiscanScore(unit, priority, currentTargetGuid, specifiedUnitGuid)
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
-- 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
if not isCurrentTarget and not isSpecifiedUnit and not UnitAffectingCombat(unit) then
return nil
end
@@ -4971,212 +4935,6 @@ 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
@@ -5188,10 +4946,16 @@ function CleveRoids.ResolveMultiscanTarget(conditionals, specifiedUnit)
return nil
end
-- Parse priority and options from conditionals.multiscan
local priority, options = CleveRoids.ParseMultiscanOptions(conditionals.multiscan)
-- 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
if not priority then return nil end
priority = string.lower(CleveRoids.Trim(priority))
-- Validate priority type
local priorityType = CleveRoids.MULTISCAN_PRIORITIES[priority]
@@ -5331,7 +5095,7 @@ function CleveRoids.ResolveMultiscanTarget(conditionals, specifiedUnit)
end
end
local score = CleveRoids.GetMultiscanScore(unit, priorityType, currentTargetGuid, specifiedUnitGuid, options, conditionals)
local score = CleveRoids.GetMultiscanScore(unit, priorityType, currentTargetGuid, specifiedUnitGuid)
if not score then return end
-- Validate against target-dependent conditionals
@@ -5340,18 +5104,8 @@ 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)
-- Skip if priotarget already checked it
if currentTargetGuid and not options.priotarget then
if currentTargetGuid then
evaluateCandidate("target")
end
-12
View File
@@ -51,18 +51,6 @@ SlashCmdList.EQUIPMH = CleveRoids.DoEquipMainhand
SLASH_EQUIPOH1 = "/equipoh"
SlashCmdList.EQUIPOH = CleveRoids.DoEquipOffhand
SLASH_EQSLOT111 = "/equip11"
SlashCmdList.EQSLOT11 = CleveRoids.DoEquipRing1
SLASH_EQSLOT121 = "/equip12"
SlashCmdList.EQSLOT12 = CleveRoids.DoEquipRing2
SLASH_EQSLOT131 = "/equip13"
SlashCmdList.EQSLOT13 = CleveRoids.DoEquipTrinket1
SLASH_EQSLOT141 = "/equip14"
SlashCmdList.EQSLOT14 = CleveRoids.DoEquipTrinket2
SLASH_UNSHIFT1 = "/unshift"
SlashCmdList.UNSHIFT = CleveRoids.DoUnshift
+42 -270
View File
@@ -2794,9 +2794,9 @@ function CleveRoids.DoUse(msg)
return false
end
function CleveRoids.EquipBagItem(msg, slotOrOffhand)
function CleveRoids.EquipBagItem(msg, offhand)
if CleveRoids.equipDebugLog then
CleveRoids.Print("|cff00ffff[EquipLog] EquipBagItem called: '" .. tostring(msg) .. "' slot=" .. tostring(slotOrOffhand) .. "|r")
CleveRoids.Print("|cff00ffff[EquipLog] EquipBagItem called: '" .. tostring(msg) .. "' offhand=" .. tostring(offhand) .. "|r")
end
if CleveRoids.equipInProgress then
@@ -2806,13 +2806,7 @@ function CleveRoids.EquipBagItem(msg, slotOrOffhand)
return false
end
-- Accept slot number directly, or boolean for backward compatibility (false=16/MH, true=17/OH)
local invslot
if type(slotOrOffhand) == "number" then
invslot = slotOrOffhand
else
invslot = slotOrOffhand and 17 or 16
end
local invslot = offhand and 17 or 16
local API = CleveRoids.NampowerAPI
-- v2.18+: Use native fast lookup for item ID or name
@@ -2925,38 +2919,7 @@ function CleveRoids.EquipBagItem(msg, slotOrOffhand)
end
end
-- Check if item is already equipped in the paired slot (swap case)
-- EquipItemByName doesn't handle swapping equipped items, so we must do it manually
-- Paired slots: trinkets (13<->14), weapons (16<->17), rings (11<->12)
local pairedSlots = {[13] = 14, [14] = 13, [16] = 17, [17] = 16, [11] = 12, [12] = 11}
local checkSlot = pairedSlots[invslot]
if checkSlot then
local link = GetInventoryItemLink("player", checkSlot)
if link then
local _, _, slotItemName = string_find(link, "|h%[(.-)%]|h")
if slotItemName and string_lower(slotItemName) == string_lower(msg) then
-- Found item in paired slot - swap it manually
if CleveRoids.equipDebugLog then
CleveRoids.Print("|cff00ffff[EquipLog] Swapping from slot " .. checkSlot .. " to slot " .. invslot .. "|r")
end
ClearCursor()
PickupInventoryItem(checkSlot)
if CursorHasItem and CursorHasItem() then
EquipCursorItem(invslot)
ClearCursor()
if CleveRoids.Items then
CleveRoids.Items[msg] = nil
CleveRoids.Items[string_lower(msg)] = nil
end
InvalidateDisplacedItem()
return true
end
ClearCursor()
end
end
end
-- PERFORMANCE: Try EquipItemByName for bag items (fast path)
-- PERFORMANCE: Try EquipItemByName FIRST before any lookups
-- This is the fastest path - no item lookup, no cursor operations
if EquipItemByName then
local ok = pcall(EquipItemByName, msg, invslot)
@@ -3056,14 +3019,6 @@ local function _equipOffhandAction(msg)
return CleveRoids.EquipBagItem(msg, true)
end
local function _equipTrinket1Action(msg)
return CleveRoids.EquipBagItem(msg, 13)
end
local function _equipTrinket2Action(msg)
return CleveRoids.EquipBagItem(msg, 14)
end
local function _unshiftAction()
local currentShapeshiftIndex = CleveRoids.GetCurrentShapeshiftIndex()
if currentShapeshiftIndex ~= 0 then
@@ -3095,118 +3050,6 @@ function CleveRoids.DoEquipOffhand(msg)
return false
end
-- Trinket equip using slot 13 (which works), then swap if needed for slot 14
local function DoEquipTrinketSlot13(msg)
local action = function(m) return CleveRoids.EquipBagItem(m, 13) end
local parts = CleveRoids.splitStringIgnoringQuotes(msg)
for i = 1, table.getn(parts) do
local v = string.gsub(parts[i], "^%?", "")
if CleveRoids.DoWithConditionals(v, action, CleveRoids.FixEmptyTarget, false, action) then
return true
end
end
return false
end
-- For slot 14: normal API for bag items, manual for already-equipped
local function DoEquipTrinketSlot14(msg)
local action = function(m)
-- Find item location
local item = CleveRoids.GetItem(m)
if not item then
return false
end
-- Item in bags: use normal EquipBagItem
if item.bagID and item.slot then
return CleveRoids.EquipBagItem(m, 14)
end
-- Item already equipped: manual pickup and swap
if item.inventoryID then
ClearCursor()
PickupInventoryItem(item.inventoryID)
if CursorHasItem and CursorHasItem() then
EquipCursorItem(14)
-- If displaced item on cursor, put it in original slot
if CursorHasItem and CursorHasItem() then
EquipCursorItem(item.inventoryID)
end
ClearCursor()
return true
end
end
return false
end
local parts = CleveRoids.splitStringIgnoringQuotes(msg)
for i = 1, table.getn(parts) do
local v = string.gsub(parts[i], "^%?", "")
if CleveRoids.DoWithConditionals(v, action, CleveRoids.FixEmptyTarget, false, action) then
return true
end
end
return false
end
CleveRoids.DoEquipTrinket1 = function(msg) return DoEquipTrinketSlot13(msg) end
CleveRoids.DoEquipTrinket2 = function(msg) return DoEquipTrinketSlot14(msg) end
-- Ring slot 11: normal equip
local function DoEquipRingSlot11(msg)
local action = function(m) return CleveRoids.EquipBagItem(m, 11) end
local parts = CleveRoids.splitStringIgnoringQuotes(msg)
for i = 1, table.getn(parts) do
local v = string.gsub(parts[i], "^%?", "")
if CleveRoids.DoWithConditionals(v, action, CleveRoids.FixEmptyTarget, false, action) then
return true
end
end
return false
end
-- Ring slot 12: normal API for bag items, manual for already-equipped
local function DoEquipRingSlot12(msg)
local action = function(m)
local item = CleveRoids.GetItem(m)
if not item then
return false
end
-- Item in bags: use normal EquipBagItem
if item.bagID and item.slot then
return CleveRoids.EquipBagItem(m, 12)
end
-- Item already equipped: manual pickup and swap
if item.inventoryID then
ClearCursor()
PickupInventoryItem(item.inventoryID)
if CursorHasItem and CursorHasItem() then
EquipCursorItem(12)
if CursorHasItem and CursorHasItem() then
EquipCursorItem(item.inventoryID)
end
ClearCursor()
return true
end
end
return false
end
local parts = CleveRoids.splitStringIgnoringQuotes(msg)
for i = 1, table.getn(parts) do
local v = string.gsub(parts[i], "^%?", "")
if CleveRoids.DoWithConditionals(v, action, CleveRoids.FixEmptyTarget, false, action) then
return true
end
end
return false
end
CleveRoids.DoEquipRing1 = function(msg) return DoEquipRingSlot11(msg) end
CleveRoids.DoEquipRing2 = function(msg) return DoEquipRingSlot12(msg) end
function CleveRoids.DoUnshift(msg)
local handled
-- PERFORMANCE: Use numeric iteration to avoid pairs() iterator allocation
@@ -4929,7 +4772,6 @@ 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')
@@ -4940,8 +4782,6 @@ 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)')
@@ -4957,8 +4797,6 @@ 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
@@ -5618,13 +5456,6 @@ 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 ===")
@@ -6383,96 +6214,6 @@ 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: Enabled (will show when tracking)")
elseif val == "hide" then
tracker.Hide()
CleveRoids.Print("Multi-Target Tracker: Disabled (persists across reloads)")
elseif val == "toggle" then
tracker.Toggle()
if tracker.IsDisabled and tracker.IsDisabled() then
CleveRoids.Print("Multi-Target Tracker: Disabled (persists across reloads)")
else
CleveRoids.Print("Multi-Target Tracker: Enabled (will show when tracking)")
end
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 - Enable tracker (persists)")
CleveRoids.Print(" /cleveroid mtt hide - Disable tracker (persists)")
CleveRoids.Print(" /cleveroid mtt toggle - Toggle enabled state (persists)")
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")
@@ -6497,12 +6238,43 @@ 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
-- Equipment queue commands consolidated under /cleveroid equipqueue clear|status
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
File diff suppressed because it is too large Load Diff
-4
View File
@@ -112,10 +112,6 @@ CleveRoids.dynamicCmds = {
["/equip"] = true,
["/equipmh"] = true,
["/equipoh"] = true,
["/equip11"] = true,
["/equip12"] = true,
["/equip13"] = true,
["/equip14"] = true,
}
-- Equipment swap queue system
-50
View File
@@ -347,52 +347,6 @@ 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.
@@ -555,10 +509,6 @@ Default `[noimmune]` checks the debuff school (bleed).
| /equip | * | Equip item by name/ID |
| /equipmh | * | Equip main hand |
| /equipoh | * | Equip off-hand |
| /equip11 | * | Equip ring slot 1 |
| /equip12 | * | Equip ring slot 2 |
| /equip13 | * | Equip trinket slot 1 |
| /equip14 | * | Equip trinket slot 2 |
| /unshift | * | Cancel shapeshift form |
| /cancelaura | | Cancel buff/aura |
| /unbuff | | Alias for cancelaura |
+1 -2
View File
@@ -2,7 +2,7 @@
## Author: DWG, _brain, Torio, yani9o, Avitasia
## Title: [Super]CleveRoidMacros
## Notes: /cleveroid for settings
## Version: 1.9
## Version: 1.8
## OptionalDeps: ClassicFocus, FocusFrame, pfUI, SuperMacro, Bongos_ActionBar
## SavedVariables: CleveRoidMacros, CleveRoids_LearnedDurations, CleveRoids_AuraTextures, CleveRoids_ImmunityData, CleveRoids_ComboDurations, CleveRoids_SpellSchools
Localization.lua
@@ -35,4 +35,3 @@ Extensions\Mouseover\ag_UnitFrames.lua
Extensions\Tooltip\Generic.lua
Extensions\MultiTargetTracker.lua
+8 -58
View File
@@ -17,30 +17,13 @@ local tonumber = tonumber
local tostring = tostring
local GetTime = GetTime
-- GUID normalization: ensures consistent string format for table keys
-- Preserves original format (including 0x prefix) for SuperWoW API compatibility
-- 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
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
@@ -614,18 +597,6 @@ 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?)
@@ -640,17 +611,8 @@ lib.personalDebuffs = lib.personalDebuffs or {
[11285] = 4, -- Gouge (Rank 4)
[11286] = 4, -- Gouge (Rank 5)
-- 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)
-- NOTE: Rupture removed - handled by ComboPointTracker (base 8s + 2s per CP)
-- NOTE: Kidney Shot removed - handled by ComboPointTracker (base 1s/2s + 1s per CP)
[703] = 18, -- Garrote (Rank 1)
[8631] = 18, -- Garrote (Rank 2)
@@ -792,18 +754,6 @@ 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)
@@ -1000,10 +950,10 @@ lib.sharedDebuffs = lib.sharedDebuffs or {
[11597] = 30, -- Sunder Armor (Rank 5)
[6343] = 10, -- Thunder Clap (Rank 1)
[8198] = 14, -- Thunder Clap (Rank 2)
[8205] = 18, -- Thunder Clap (Rank 3)
[11580] = 22, -- Thunder Clap (Rank 4)
[11581] = 30, -- Thunder Clap (Rank 5)
[8198] = 10, -- Thunder Clap (Rank 2)
[8205] = 10, -- Thunder Clap (Rank 3)
[11580] = 10, -- Thunder Clap (Rank 4)
[11581] = 10, -- Thunder Clap (Rank 5)
[1160] = 30, -- Demoralizing Shout (Rank 1)
[6190] = 30, -- Demoralizing Shout (Rank 2)