feature addition multi target debuff tracker

This commit is contained in:
Jrc13245
2026-01-02 15:52:55 -05:00
parent 2e9251ee0c
commit 6af06f1ee3
7 changed files with 3035 additions and 79 deletions
+1 -19
View File
@@ -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
View File
@@ -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
+104 -38
View File
@@ -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
+46
View File
@@ -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 -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
## 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
View File
@@ -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)