mirror of
https://github.com/brues-code/SuperCleveRoidMacros.git
synced 2026-09-16 03:38:00 +00:00
980 lines
30 KiB
Lua
980 lines
30 KiB
Lua
--[[
|
|
Author: Dennis Werner Garske (DWG) / brian / Mewtiny
|
|
License: MIT License
|
|
]]
|
|
local _G = _G or getfenv(0)
|
|
local CleveRoids = _G.CleveRoids or {}
|
|
local _G = getfenv(0)
|
|
local strfind = string.find
|
|
local find = string.find -- Add this line
|
|
local gsub = string.gsub
|
|
local lower = string.lower
|
|
local floor = math.floor
|
|
local pairs = pairs
|
|
local ipairs = ipairs
|
|
local type = type
|
|
local tonumber = tonumber
|
|
local tostring = tostring
|
|
local GetTime = GetTime
|
|
|
|
if type(hooksecurefunc) ~= "function" then
|
|
function hooksecurefunc(arg1, arg2, arg3)
|
|
local tgt, fname, post
|
|
|
|
if type(arg1) == "string" and type(arg2) == "function" and arg3 == nil then
|
|
fname, post = arg1, arg2
|
|
local orig = _G[fname]
|
|
if type(orig) ~= "function" then return end
|
|
_G[fname] = function(...)
|
|
local args = arg or {}
|
|
local n = (type(args) == "table" and args.n) or 0
|
|
local r = { orig(unpack(args, 1, n)) }
|
|
post(unpack(args, 1, n))
|
|
return unpack(r, 1, table.getn(r))
|
|
end
|
|
return
|
|
end
|
|
|
|
if type(arg1) == "table" and type(arg2) == "string" and type(arg3) == "function" then
|
|
tgt, fname, post = arg1, arg2, arg3
|
|
local orig = tgt[fname]
|
|
if type(orig) ~= "function" then return end
|
|
tgt[fname] = function(...)
|
|
local args = arg or {}
|
|
local n = (type(args) == "table" and args.n) or 0
|
|
local r = { orig(unpack(args, 1, n)) }
|
|
post(unpack(args, 1, n))
|
|
return unpack(r, 1, table.getn(r))
|
|
end
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
if type(string.match) ~= "function" then
|
|
function string.match(s, pattern, init)
|
|
if s == nil or pattern == nil then return nil end
|
|
local i, j, c1, c2, c3, c4, c5 = string.find(s, pattern, init)
|
|
if not i then return nil end
|
|
if c1 ~= nil then return c1, c2, c3, c4, c5 end
|
|
return string.sub(s, i, j)
|
|
end
|
|
end
|
|
|
|
if type(string.gmatch) ~= "function" then
|
|
function string.gmatch(s, pattern)
|
|
local pos = 1
|
|
return function()
|
|
if s == nil or pattern == nil then return nil end
|
|
local i, j, c1, c2, c3, c4, c5 = string.find(s, pattern, pos)
|
|
if not i then return nil end
|
|
pos = j + 1
|
|
if c1 ~= nil then return c1, c2, c3, c4, c5 end
|
|
return string.sub(s, i, j)
|
|
end
|
|
end
|
|
end
|
|
|
|
function CleveRoids.Seq(_, i)
|
|
return (i or 0) + 1
|
|
end
|
|
|
|
function CleveRoids.Trim(str)
|
|
if not str then
|
|
return nil
|
|
end
|
|
return string.gsub(str,"^%s*(.-)%s*$", "%1")
|
|
end
|
|
|
|
do
|
|
local _G = _G or getfenv(0)
|
|
local CleveRoids = _G.CleveRoids or {}
|
|
_G.CleveRoids = CleveRoids
|
|
|
|
CleveRoids.__mo = CleveRoids.__mo or { sources = {}, current = nil }
|
|
|
|
local PRIORITY = {
|
|
pfui = 3,
|
|
blizz = 3,
|
|
tooltip = 1,
|
|
}
|
|
|
|
local function getBest()
|
|
local bestSource, bestUnit, bestP = nil, nil, -1
|
|
for source, unit in CleveRoids.__mo.sources do
|
|
if unit and unit ~= "" then
|
|
local p = PRIORITY[source] or 0
|
|
if p > bestP then
|
|
bestP, bestSource, bestUnit = p, source, unit
|
|
end
|
|
end
|
|
end
|
|
return bestSource, bestUnit
|
|
end
|
|
|
|
local function apply(unit)
|
|
if CleveRoids.hasSuperwow and _G.SetMouseoverUnit then
|
|
_G.SetMouseoverUnit(unit)
|
|
else
|
|
CleveRoids.mouseoverUnit = unit
|
|
end
|
|
if CleveRoids.QueueActionUpdate then CleveRoids.QueueActionUpdate() end
|
|
end
|
|
|
|
function CleveRoids.SetMouseoverFrom(source, unit)
|
|
if not source then return end
|
|
CleveRoids.__mo.sources[source] = unit
|
|
local _, bestUnit = getBest()
|
|
if bestUnit ~= CleveRoids.__mo.current then
|
|
CleveRoids.__mo.current = bestUnit
|
|
apply(bestUnit)
|
|
end
|
|
end
|
|
|
|
function CleveRoids.ClearMouseoverFrom(source, unitIfMatch)
|
|
if not source then return end
|
|
if unitIfMatch and CleveRoids.__mo.sources[source] ~= unitIfMatch then
|
|
return
|
|
end
|
|
CleveRoids.__mo.sources[source] = nil
|
|
local _, bestUnit = getBest()
|
|
if bestUnit ~= CleveRoids.__mo.current then
|
|
CleveRoids.__mo.current = bestUnit
|
|
apply(bestUnit)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- TODO: Get rid of one Split function. CleveRoids.splitString is ~10% slower
|
|
function CleveRoids.Split(s, p, trim)
|
|
local r, o = {}, 1
|
|
|
|
if not p or p == "" then
|
|
if trim then
|
|
s = CleveRoids.Trim(s)
|
|
end
|
|
for i = 1, string.len(s) do
|
|
table.insert(r, string.sub(s, i, 1))
|
|
end
|
|
return r
|
|
end
|
|
|
|
repeat
|
|
local b, e = string.find(s, p, o)
|
|
if b == nil then
|
|
local sub = string.sub(s, o)
|
|
table.insert(r, trim and CleveRoids.Trim(sub) or sub)
|
|
return r
|
|
end
|
|
if b > 1 then
|
|
local sub = string.sub(s, o, b - 1)
|
|
table.insert(r, trim and CleveRoids.Trim(sub) or sub)
|
|
else
|
|
table.insert(r, "")
|
|
end
|
|
o = e + 1
|
|
until false
|
|
end
|
|
|
|
function CleveRoids.splitString(str, seperatorPattern)
|
|
local tbl = {}
|
|
if not str then
|
|
return tbl
|
|
end
|
|
local pattern = "(.-)" .. seperatorPattern
|
|
local lastEnd = 1
|
|
local s, e, cap = string.find(str, pattern, 1)
|
|
|
|
while s do
|
|
if s ~= 1 or cap ~= "" then
|
|
table.insert(tbl,cap)
|
|
end
|
|
lastEnd = e + 1
|
|
s, e, cap = string.find(str, pattern, lastEnd)
|
|
end
|
|
|
|
if lastEnd <= string.len(str) then
|
|
cap = string.sub(str, lastEnd)
|
|
table.insert(tbl, cap)
|
|
end
|
|
|
|
return tbl
|
|
end
|
|
|
|
function CleveRoids.splitStringIgnoringQuotes(str, separator)
|
|
local result = {}
|
|
local temp = ""
|
|
local insideQuotes = false
|
|
local separators = {}
|
|
|
|
if type(separator) == "table" then
|
|
for _, s in separator do
|
|
separators[s] = s
|
|
end
|
|
else
|
|
separators[separator or ";"] = separator or ";"
|
|
end
|
|
|
|
for i = 1, string.len(str) do
|
|
local char = string.sub(str, i, i)
|
|
|
|
if char == "\"" then
|
|
insideQuotes = not insideQuotes
|
|
temp = temp .. char
|
|
elseif char == separators[char] and not insideQuotes then
|
|
temp = CleveRoids.Trim(temp)
|
|
if temp ~= "" then table.insert(result, temp) end
|
|
temp = ""
|
|
else
|
|
temp = temp .. char
|
|
end
|
|
end
|
|
|
|
if temp ~= "" then
|
|
temp = CleveRoids.Trim(temp)
|
|
table.insert(result, temp)
|
|
end
|
|
|
|
return (next(result) and result or {""})
|
|
end
|
|
|
|
function CleveRoids.Print(...)
|
|
local c = "|cFF4477FFCleveR|r|cFFFFFFFFoid :: |r"
|
|
local out = ""
|
|
|
|
for i=1, arg.n, 1 do
|
|
out = out..tostring(arg[i]).." "
|
|
end
|
|
if WowLuaFrameOutput then
|
|
WowLuaFrameOutput:AddMessage(out)
|
|
else
|
|
if not DEFAULT_CHAT_FRAME:IsVisible() then
|
|
FCF_SelectDockFrame(DEFAULT_CHAT_FRAME)
|
|
end
|
|
DEFAULT_CHAT_FRAME:AddMessage(c..out)
|
|
end
|
|
end
|
|
|
|
function CleveRoids.PrintI(msg, depth)
|
|
depth = depth or 0
|
|
msg = string.rep(" ", depth) .. tostring(msg)
|
|
CleveRoids.Print(msg)
|
|
end
|
|
|
|
function CleveRoids.PrintT(t, depth)
|
|
depth = depth or 0
|
|
local cs = "|cffc8c864"
|
|
local ce = "|r"
|
|
|
|
if t == nil or type(t) ~= "table" then
|
|
CleveRoids.PrintI(t, depth)
|
|
else
|
|
for k, v in pairs(t) do
|
|
if type(v) == "table" then
|
|
CleveRoids.PrintI(cs..tostring(k)..":"..ce.." <TABLE>", depth)
|
|
CleveRoids.PrintT(v, depth + 1)
|
|
else
|
|
CleveRoids.PrintI(cs..tostring(k)..ce..": "..tostring(v), depth)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
CleveRoids.kmods = {
|
|
ctrl = IsControlKeyDown,
|
|
alt = IsAltKeyDown,
|
|
shift = IsShiftKeyDown,
|
|
mod = function() return (IsControlKeyDown() or IsAltKeyDown() or IsShiftKeyDown()) end,
|
|
nomod = function() return (not IsControlKeyDown() and not IsAltKeyDown() and not IsShiftKeyDown()) end,
|
|
}
|
|
|
|
CleveRoids.operators = {
|
|
["<"] = "lt",
|
|
["lt"] = "<",
|
|
[">"] = "gt",
|
|
["gt"] = ">",
|
|
["="] = "eq",
|
|
["eq"] = "=",
|
|
["<="] = "lte",
|
|
["lte"] = "<=",
|
|
[">="] = "gte",
|
|
["gte"] = ">=",
|
|
["~="] = "ne",
|
|
["ne"] = "~=",
|
|
}
|
|
|
|
CleveRoids.comparators = {
|
|
lt = function(a, b) return (a < b) end,
|
|
gt = function(a, b) return (a > b) end,
|
|
eq = function(a, b) return (a == b) end,
|
|
lte = function(a, b) return (a <= b) end,
|
|
gte = function(a, b) return (a >= b) end,
|
|
ne = function(a, b) return (a ~= b) end,
|
|
}
|
|
CleveRoids.comparators["<"] = CleveRoids.comparators.lt
|
|
CleveRoids.comparators[">"] = CleveRoids.comparators.gt
|
|
CleveRoids.comparators["="] = CleveRoids.comparators.eq
|
|
CleveRoids.comparators["<="] = CleveRoids.comparators.lte
|
|
CleveRoids.comparators[">="] = CleveRoids.comparators.gte
|
|
CleveRoids.comparators["~="] = CleveRoids.comparators.ne
|
|
|
|
|
|
_G["CleveRoids"] = CleveRoids
|
|
|
|
local _G = _G or getfenv(0)
|
|
local CleveRoids = _G.CleveRoids or {}
|
|
_G.CleveRoids = CleveRoids
|
|
|
|
CleveRoids.libdebuff = CleveRoids.libdebuff or {}
|
|
local lib = CleveRoids.libdebuff
|
|
|
|
lib.objects = lib.objects or {}
|
|
lib.guidToName = lib.guidToName or {}
|
|
|
|
lib.durations = lib.durations or {
|
|
-- WARRIOR
|
|
[772] = 9, -- Rend (Rank 1)
|
|
[6546] = 12, -- Rend (Rank 2)
|
|
[6547] = 15, -- Rend (Rank 3)
|
|
[6548] = 18, -- Rend (Rank 4)
|
|
[11572] = 21, -- Rend (Rank 5)
|
|
[11573] = 21, -- Rend (Rank 6)
|
|
[11574] = 21, -- Rend (Rank 7)
|
|
|
|
[7386] = 30, -- Sunder Armor (Rank 1)
|
|
[7405] = 30, -- Sunder Armor (Rank 2)
|
|
[8380] = 30, -- Sunder Armor (Rank 3)
|
|
[8647] = 30, -- Sunder Armor (Rank 4)
|
|
[11597] = 30, -- Sunder Armor (Rank 5)
|
|
|
|
[7372] = 15, -- Hamstring (Rank 1)
|
|
[7373] = 15, -- Hamstring (Rank 2)
|
|
[1715] = 15, -- Hamstring (Rank 3)
|
|
|
|
[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)
|
|
|
|
[1160] = 30, -- Demoralizing Shout (Rank 1)
|
|
[6190] = 30, -- Demoralizing Shout (Rank 2)
|
|
[11554] = 30, -- Demoralizing Shout (Rank 3)
|
|
[11555] = 30, -- Demoralizing Shout (Rank 4)
|
|
[11556] = 30, -- Demoralizing Shout (Rank 5)
|
|
|
|
[12323] = 6, -- Piercing Howl
|
|
|
|
-- ROGUE
|
|
[2094] = 10, -- Blind
|
|
[21060] = 10, -- Blind (alternate?)
|
|
|
|
[6770] = 25, -- Sap (Rank 1)
|
|
[2070] = 35, -- Sap (Rank 2)
|
|
[11297] = 45, -- Sap (Rank 3)
|
|
|
|
[1776] = 4, -- Gouge (Rank 1) - base duration
|
|
[1777] = 4, -- Gouge (Rank 2)
|
|
[8629] = 4, -- Gouge (Rank 3)
|
|
[11285] = 4, -- Gouge (Rank 4)
|
|
[11286] = 4, -- Gouge (Rank 5)
|
|
|
|
[1943] = 6, -- Rupture (Rank 1) - base duration (scales with combo points)
|
|
[8639] = 6, -- Rupture (Rank 2)
|
|
[8640] = 6, -- Rupture (Rank 3)
|
|
[11273] = 6, -- Rupture (Rank 4)
|
|
[11274] = 6, -- Rupture (Rank 5)
|
|
[11275] = 6, -- Rupture (Rank 6)
|
|
|
|
[408] = 1, -- Kidney Shot (Rank 1) - base duration (scales with combo points)
|
|
[8643] = 1, -- Kidney Shot (Rank 2)
|
|
|
|
[8647] = 30, -- Expose Armor (Rank 1)
|
|
[8649] = 30, -- Expose Armor (Rank 2)
|
|
[8650] = 30, -- Expose Armor (Rank 3)
|
|
[11197] = 30, -- Expose Armor (Rank 4)
|
|
[11198] = 30, -- Expose Armor (Rank 5)
|
|
|
|
[703] = 18, -- Garrote (Rank 1)
|
|
[8631] = 18, -- Garrote (Rank 2)
|
|
[8632] = 18, -- Garrote (Rank 3)
|
|
[8633] = 18, -- Garrote (Rank 4)
|
|
[11289] = 18, -- Garrote (Rank 5)
|
|
[11290] = 18, -- Garrote (Rank 6)
|
|
|
|
[2818] = 12, -- Deadly Poison (Rank 1)
|
|
[2819] = 12, -- Deadly Poison II (Rank 2)
|
|
[11353] = 12, -- Deadly Poison III (Rank 3)
|
|
[11354] = 12, -- Deadly Poison IV (Rank 4)
|
|
[25349] = 12, -- Deadly Poison V (Rank 5)
|
|
|
|
[16511] = 15, -- Hemorrhage
|
|
|
|
[1833] = 4, -- Cheap Shot
|
|
[14902] = 4, -- Cheap Shot (alternate?)
|
|
|
|
-- Rogue poisons (not in Cursive, estimates):
|
|
[3409] = 12, -- Crippling Poison
|
|
[5760] = 16, -- Mind-numbing Poison (Rank 1)
|
|
[8694] = 16, -- Mind-numbing Poison (Rank 2)
|
|
[11399] = 16, -- Mind-numbing Poison (Rank 3)
|
|
[13218] = 15, -- Wound Poison (Rank 1)
|
|
[13222] = 15, -- Wound Poison (Rank 2)
|
|
[13223] = 15, -- Wound Poison (Rank 3)
|
|
[13224] = 15, -- Wound Poison (Rank 4)
|
|
|
|
-- HUNTER
|
|
[3043] = 20, -- Scorpid Sting (Rank 1)
|
|
[14275] = 20, -- Scorpid Sting (Rank 2)
|
|
[14276] = 20, -- Scorpid Sting (Rank 3)
|
|
[14277] = 20, -- Scorpid Sting (Rank 4)
|
|
|
|
[1978] = 15, -- Serpent Sting (Rank 1)
|
|
[13549] = 15, -- Serpent Sting (Rank 2)
|
|
[13550] = 15, -- Serpent Sting (Rank 3)
|
|
[13551] = 15, -- Serpent Sting (Rank 4)
|
|
[13552] = 15, -- Serpent Sting (Rank 5)
|
|
[13553] = 15, -- Serpent Sting (Rank 6)
|
|
[13554] = 15, -- Serpent Sting (Rank 7)
|
|
[13555] = 15, -- Serpent Sting (Rank 8)
|
|
[25295] = 15, -- Serpent Sting (Rank 9)
|
|
|
|
[3034] = 8, -- Viper Sting (Rank 1)
|
|
[14279] = 8, -- Viper Sting (Rank 2)
|
|
[14280] = 8, -- Viper Sting (Rank 3)
|
|
|
|
[2974] = 10, -- Wing Clip (Rank 1)
|
|
[14267] = 10, -- Wing Clip (Rank 2)
|
|
[14268] = 10, -- Wing Clip (Rank 3)
|
|
|
|
[5116] = 4, -- Concussive Shot
|
|
|
|
[19386] = 12, -- Wyvern Sting (Rank 1)
|
|
[24132] = 12, -- Wyvern Sting (Rank 2)
|
|
[24133] = 12, -- Wyvern Sting (Rank 3)
|
|
|
|
[19306] = 5, -- Counterattack (Rank 1)
|
|
[20909] = 5, -- Counterattack (Rank 2)
|
|
[20910] = 5, -- Counterattack (Rank 3)
|
|
|
|
[1130] = 120, -- Hunter's Mark (Rank 1)
|
|
[14323] = 120, -- Hunter's Mark (Rank 2)
|
|
[14324] = 120, -- Hunter's Mark (Rank 3)
|
|
[14325] = 120, -- Hunter's Mark (Rank 4)
|
|
|
|
-- DRUID
|
|
[339] = 12, -- Entangling Roots (Rank 1)
|
|
[1062] = 15, -- Entangling Roots (Rank 2)
|
|
[5195] = 18, -- Entangling Roots (Rank 3)
|
|
[5196] = 21, -- Entangling Roots (Rank 4)
|
|
[9852] = 24, -- Entangling Roots (Rank 5)
|
|
[9853] = 27, -- Entangling Roots (Rank 6)
|
|
|
|
[700] = 20, -- Sleep (Rank 1)
|
|
[1090] = 30, -- Sleep (Rank 2)
|
|
[2937] = 40, -- Sleep (Rank 3)
|
|
|
|
[770] = 40, -- Faerie Fire (Rank 1)
|
|
[778] = 40, -- Faerie Fire (Rank 2)
|
|
[9749] = 40, -- Faerie Fire (Rank 3)
|
|
[9907] = 40, -- Faerie Fire (Rank 4)
|
|
|
|
[16855] = 40, -- Faerie Fire (Bear) (Rank 1)
|
|
[17387] = 40, -- Faerie Fire (Bear) (Rank 2)
|
|
[17388] = 40, -- Faerie Fire (Bear) (Rank 3)
|
|
[17389] = 40, -- Faerie Fire (Bear) (Rank 4)
|
|
|
|
[16857] = 40, -- Faerie Fire (Feral) (Rank 1)
|
|
[17390] = 40, -- Faerie Fire (Feral) (Rank 2)
|
|
[17391] = 40, -- Faerie Fire (Feral) (Rank 3)
|
|
[17392] = 40, -- Faerie Fire (Feral) (Rank 4)
|
|
|
|
[2637] = 20, -- Hibernate (Rank 1)
|
|
[18657] = 30, -- Hibernate (Rank 2)
|
|
[18658] = 40, -- Hibernate (Rank 3)
|
|
|
|
[5570] = 18, -- Insect Swarm (Rank 1) - 12s with talent
|
|
[24974] = 18, -- Insect Swarm (Rank 2)
|
|
[24975] = 18, -- Insect Swarm (Rank 3)
|
|
[24976] = 18, -- Insect Swarm (Rank 4)
|
|
[24977] = 18, -- Insect Swarm (Rank 5)
|
|
|
|
[8921] = 9, -- Moonfire (Rank 1) - 12s with talent
|
|
[8924] = 18, -- Moonfire (Rank 2) - 12s with talent
|
|
[8925] = 18, -- Moonfire (Rank 3)
|
|
[8926] = 18, -- Moonfire (Rank 4)
|
|
[8927] = 18, -- Moonfire (Rank 5)
|
|
[8928] = 18, -- Moonfire (Rank 6)
|
|
[8929] = 18, -- Moonfire (Rank 7)
|
|
[9833] = 18, -- Moonfire (Rank 8)
|
|
[9834] = 18, -- Moonfire (Rank 9)
|
|
[9835] = 18, -- Moonfire (Rank 10)
|
|
|
|
[1822] = 9, -- Rake (Rank 1)
|
|
[1823] = 9, -- Rake (Rank 2)
|
|
[1824] = 9, -- Rake (Rank 3)
|
|
[9904] = 9, -- Rake (Rank 4)
|
|
|
|
[1079] = 8, -- Rip (Rank 1) - base duration (scales with combo points)
|
|
[9492] = 8, -- Rip (Rank 2)
|
|
[9493] = 8, -- Rip (Rank 3)
|
|
[9752] = 8, -- Rip (Rank 4)
|
|
[9894] = 8, -- Rip (Rank 5)
|
|
[9896] = 8, -- Rip (Rank 6)
|
|
|
|
[2908] = 15, -- Soothe Animal (Rank 1)
|
|
[8955] = 15, -- Soothe Animal (Rank 2)
|
|
[9901] = 15, -- Soothe Animal (Rank 3)
|
|
|
|
[5211] = 2, -- Bash (Rank 1)
|
|
[6798] = 3, -- Bash (Rank 2)
|
|
[8983] = 4, -- Bash (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
|
|
|
|
[9005] = 18, -- Pounce Bleed (Rank 1)
|
|
[9823] = 18, -- Pounce Bleed (Rank 2)
|
|
[9827] = 18, -- Pounce Bleed (Rank 3)
|
|
|
|
-- WARLOCK
|
|
[172] = 12, -- Corruption (Rank 1) - 18s with talent
|
|
[6222] = 15, -- Corruption (Rank 2) - 18s with talent
|
|
[6223] = 18, -- Corruption (Rank 3) - 18s with talent
|
|
[7648] = 18, -- Corruption (Rank 4)
|
|
[11671] = 18, -- Corruption (Rank 5)
|
|
[11672] = 18, -- Corruption (Rank 6)
|
|
[25311] = 18, -- Corruption (Rank 7)
|
|
|
|
[980] = 24, -- Curse of Agony (Rank 1)
|
|
[1014] = 24, -- Curse of Agony (Rank 2)
|
|
[6217] = 24, -- Curse of Agony (Rank 3)
|
|
[11711] = 24, -- Curse of Agony (Rank 4)
|
|
[11712] = 24, -- Curse of Agony (Rank 5)
|
|
[11713] = 24, -- Curse of Agony (Rank 6)
|
|
|
|
[18265] = 30, -- Siphon Life (Rank 1)
|
|
[18879] = 30, -- Siphon Life (Rank 2)
|
|
[18880] = 30, -- Siphon Life (Rank 3)
|
|
[18881] = 30, -- Siphon Life (Rank 4)
|
|
|
|
[52550] = 8, -- Dark Harvest (Rank 1)
|
|
[52551] = 8, -- Dark Harvest (Rank 2)
|
|
[52552] = 8, -- Dark Harvest (Rank 3)
|
|
|
|
[603] = 60, -- Curse of Doom
|
|
|
|
[704] = 120, -- Curse of Recklessness (Rank 1)
|
|
[7658] = 120, -- Curse of Recklessness (Rank 2)
|
|
[7659] = 120, -- Curse of Recklessness (Rank 3)
|
|
[11717] = 120, -- Curse of Recklessness (Rank 4)
|
|
|
|
[17862] = 300, -- Curse of Shadow (Rank 1)
|
|
[17937] = 300, -- Curse of Shadow (Rank 2)
|
|
|
|
[1490] = 300, -- Curse of Elements (Rank 1)
|
|
[11721] = 300, -- Curse of Elements (Rank 2)
|
|
[11722] = 300, -- Curse of Elements (Rank 3)
|
|
|
|
[1714] = 30, -- Curse of Tongues (Rank 1)
|
|
[11719] = 30, -- Curse of Tongues (Rank 2)
|
|
|
|
[702] = 120, -- Curse of Weakness (Rank 1)
|
|
[1108] = 120, -- Curse of Weakness (Rank 2)
|
|
[6205] = 120, -- Curse of Weakness (Rank 3)
|
|
[7646] = 120, -- Curse of Weakness (Rank 4)
|
|
[11707] = 120, -- Curse of Weakness (Rank 5)
|
|
[11708] = 120, -- Curse of Weakness (Rank 6)
|
|
|
|
[18223] = 12, -- Curse of Exhaustion
|
|
|
|
[348] = 15, -- Immolate (Rank 1)
|
|
[707] = 15, -- Immolate (Rank 2)
|
|
[1094] = 15, -- Immolate (Rank 3)
|
|
[2941] = 15, -- Immolate (Rank 4)
|
|
[11665] = 15, -- Immolate (Rank 5)
|
|
[11667] = 15, -- Immolate (Rank 6)
|
|
[11668] = 15, -- Immolate (Rank 7)
|
|
[25309] = 15, -- Immolate (Rank 8)
|
|
|
|
[6789] = 3, -- Death Coil (Rank 1)
|
|
[17925] = 3, -- Death Coil (Rank 2)
|
|
[17926] = 3, -- Death Coil (Rank 3)
|
|
|
|
[710] = 20, -- Banish (Rank 1)
|
|
[18647] = 30, -- Banish (Rank 2)
|
|
|
|
[5782] = 10, -- Fear (Rank 1)
|
|
[6213] = 15, -- Fear (Rank 2)
|
|
[6215] = 20, -- Fear (Rank 3)
|
|
|
|
-- MAGE
|
|
[118] = 20, -- Polymorph (Rank 1)
|
|
[12824] = 30, -- Polymorph (Rank 2)
|
|
[12825] = 40, -- Polymorph (Rank 3)
|
|
[12826] = 50, -- Polymorph (Rank 4)
|
|
|
|
[28270] = 50, -- Polymorph: Cow
|
|
[28271] = 50, -- Polymorph: Turtle
|
|
[28272] = 50, -- Polymorph: Pig
|
|
|
|
[116] = 5, -- Frostbolt slow
|
|
[120] = 5, -- Cone of Cold slow
|
|
[6136] = 15, -- Chilled
|
|
[12484] = 15, -- Improved Blizzard slow
|
|
[12486] = 15, -- Blizzard slow
|
|
|
|
-- PRIEST
|
|
[1425] = 30, -- Shackle Undead (Rank 1)
|
|
[9486] = 40, -- Shackle Undead (Rank 2)
|
|
[10956] = 50, -- Shackle Undead (Rank 3)
|
|
|
|
[453] = 15, -- Mind Soothe (Rank 1)
|
|
[8192] = 15, -- Mind Soothe (Rank 2)
|
|
[10953] = 15, -- Mind Soothe (Rank 3)
|
|
|
|
[605] = 60, -- Mind Control (Rank 1)
|
|
[10911] = 30, -- Mind Control (Rank 2)
|
|
[10912] = 30, -- Mind Control (Rank 3)
|
|
|
|
[2944] = 24, -- Devouring Plague (Rank 1)
|
|
[19276] = 24, -- Devouring Plague (Rank 2)
|
|
[19277] = 24, -- Devouring Plague (Rank 3)
|
|
[19278] = 24, -- Devouring Plague (Rank 4)
|
|
[19279] = 24, -- Devouring Plague (Rank 5)
|
|
[19280] = 24, -- Devouring Plague (Rank 6)
|
|
|
|
[9035] = 120, -- Hex of Weakness (Rank 1)
|
|
[19281] = 120, -- Hex of Weakness (Rank 2)
|
|
[19282] = 120, -- Hex of Weakness (Rank 3)
|
|
[19283] = 120, -- Hex of Weakness (Rank 4)
|
|
[19284] = 120, -- Hex of Weakness (Rank 5)
|
|
[19285] = 120, -- Hex of Weakness (Rank 6)
|
|
|
|
[589] = 24, -- Shadow Word: Pain (Rank 1) - 18s base, 24s with talent
|
|
[594] = 24, -- Shadow Word: Pain (Rank 2)
|
|
[970] = 24, -- Shadow Word: Pain (Rank 3)
|
|
[992] = 24, -- Shadow Word: Pain (Rank 4)
|
|
[2767] = 24, -- Shadow Word: Pain (Rank 5)
|
|
[10892] = 24, -- Shadow Word: Pain (Rank 6)
|
|
[10893] = 24, -- Shadow Word: Pain (Rank 7)
|
|
[10894] = 24, -- Shadow Word: Pain (Rank 8)
|
|
|
|
[15286] = 60, -- Vampiric Embrace
|
|
|
|
[14914] = 10, -- Holy Fire (Rank 1)
|
|
[15262] = 10, -- Holy Fire (Rank 2)
|
|
[15263] = 10, -- Holy Fire (Rank 3)
|
|
[15264] = 10, -- Holy Fire (Rank 4)
|
|
[15265] = 10, -- Holy Fire (Rank 5)
|
|
[15266] = 10, -- Holy Fire (Rank 6)
|
|
[15267] = 10, -- Holy Fire (Rank 7)
|
|
[15261] = 10, -- Holy Fire (Rank 8)
|
|
|
|
-- Mind Flay
|
|
[15407] = 3, -- Mind Flay (Rank 1)
|
|
[17311] = 3, -- Mind Flay (Rank 2)
|
|
[17312] = 3, -- Mind Flay (Rank 3)
|
|
[17313] = 3, -- Mind Flay (Rank 4)
|
|
[17314] = 3, -- Mind Flay (Rank 5)
|
|
[18807] = 3, -- Mind Flay (Rank 6)
|
|
|
|
-- PALADIN
|
|
[853] = 6, -- Hammer of Justice (Rank 1)
|
|
[5588] = 6, -- Hammer of Justice (Rank 2)
|
|
[5589] = 6, -- Hammer of Justice (Rank 3)
|
|
[10308] = 6, -- Hammer of Justice (Rank 4)
|
|
|
|
[20184] = 10, -- Judgement of Justice
|
|
[20185] = 10, -- Judgement of Light (Rank 1)
|
|
[20267] = 10, -- Judgement of Light (Rank 2)
|
|
[20268] = 10, -- Judgement of Light (Rank 3)
|
|
[20271] = 10, -- Judgement of Light (Rank 4)
|
|
[20186] = 10, -- Judgement of Wisdom (Rank 1)
|
|
[20354] = 10, -- Judgement of Wisdom (Rank 2)
|
|
[20355] = 10, -- Judgement of Wisdom (Rank 3)
|
|
|
|
-- SHAMAN
|
|
[8050] = 12, -- Flame Shock (Rank 1)
|
|
[8052] = 12, -- Flame Shock (Rank 2)
|
|
[8053] = 12, -- Flame Shock (Rank 3)
|
|
[10447] = 12, -- Flame Shock (Rank 4)
|
|
[10448] = 12, -- Flame Shock (Rank 5)
|
|
[29228] = 12, -- Flame Shock (Rank 6)
|
|
|
|
-- Frost Shock
|
|
[8056] = 8, -- Frost Shock (Rank 1)
|
|
[8058] = 8, -- Frost Shock (Rank 2)
|
|
[10472] = 8, -- Frost Shock (Rank 3)
|
|
[10473] = 8, -- Frost Shock (Rank 4)
|
|
}
|
|
|
|
lib.learnCastTimers = lib.learnCastTimers or {}
|
|
|
|
CleveRoids_LearnedDurations = CleveRoids_LearnedDurations or {}
|
|
|
|
function lib:GetDuration(spellID, casterGUID)
|
|
if casterGUID and CleveRoids_LearnedDurations[spellID] then
|
|
local learned = CleveRoids_LearnedDurations[spellID][casterGUID]
|
|
if learned and learned > 0 then
|
|
return learned
|
|
end
|
|
end
|
|
|
|
return self.durations[spellID] or 0
|
|
end
|
|
|
|
function lib:AddEffect(guid, unitName, spellID, duration, stacks, caster)
|
|
if not guid or not spellID then return end
|
|
|
|
duration = duration or lib:GetDuration(spellID, caster)
|
|
if duration <= 0 then return end
|
|
|
|
lib.objects[guid] = lib.objects[guid] or {}
|
|
lib.guidToName[guid] = unitName
|
|
|
|
local rec = lib.objects[guid][spellID] or {}
|
|
rec.spellID = spellID
|
|
rec.start = GetTime()
|
|
rec.duration = duration
|
|
rec.stacks = stacks or 0
|
|
rec.caster = caster
|
|
|
|
lib.objects[guid][spellID] = rec
|
|
end
|
|
|
|
function lib:UnitDebuff(unit, id)
|
|
local _, guid = UnitExists(unit)
|
|
if not guid then return nil end
|
|
|
|
local texture, stacks, dtype, spellID = UnitDebuff(unit, id)
|
|
if not texture or not spellID then return nil end
|
|
|
|
local name = SpellInfo(spellID)
|
|
local duration, timeleft, caster = nil, -1, nil
|
|
|
|
local rec = lib.objects[guid] and lib.objects[guid][spellID]
|
|
|
|
if rec and rec.duration and rec.start then
|
|
local remaining = rec.duration + rec.start - GetTime()
|
|
if remaining > 0 then
|
|
duration = rec.duration
|
|
timeleft = remaining
|
|
caster = rec.caster
|
|
stacks = rec.stacks or stacks
|
|
else
|
|
lib.objects[guid][spellID] = nil
|
|
end
|
|
end
|
|
|
|
return name, nil, texture, stacks, dtype, duration, timeleft, caster
|
|
end
|
|
|
|
local function SeedUnit(unit)
|
|
local _, guid = UnitExists(unit)
|
|
if not guid then return end
|
|
local unitName = UnitName(unit)
|
|
|
|
for i=1, 16 do
|
|
local tex, stacks, dtype, spellID = UnitDebuff(unit, i)
|
|
if not tex then break end
|
|
|
|
if spellID and lib.durations[spellID] then
|
|
if not (lib.objects[guid] and lib.objects[guid][spellID]) then
|
|
lib:AddEffect(guid, unitName, spellID, lib:GetDuration(spellID), stacks)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local ev = CreateFrame("Frame", "CleveRoidsLibDebuffFrame", UIParent)
|
|
ev:RegisterEvent("PLAYER_TARGET_CHANGED")
|
|
ev:RegisterEvent("UNIT_AURA")
|
|
|
|
if CleveRoids.hasSuperwow then
|
|
ev:RegisterEvent("UNIT_CASTEVENT")
|
|
end
|
|
|
|
ev:SetScript("OnEvent", function()
|
|
if event == "PLAYER_TARGET_CHANGED" then
|
|
SeedUnit("target")
|
|
|
|
elseif event == "UNIT_AURA" and arg1 == "target" then
|
|
SeedUnit("target")
|
|
|
|
elseif event == "UNIT_CASTEVENT" then
|
|
local casterGUID = arg1
|
|
local targetGUID = arg2
|
|
local eventType = arg3
|
|
local spellID = arg4
|
|
|
|
if eventType == "CAST" and spellID then
|
|
local _, playerGUID = UnitExists("player")
|
|
if casterGUID == playerGUID and targetGUID then
|
|
|
|
local duration = lib:GetDuration(spellID, casterGUID)
|
|
|
|
if duration > 0 then
|
|
local targetName = lib.guidToName[targetGUID]
|
|
if not targetName then
|
|
local _, currentTargetGUID = UnitExists("target")
|
|
if currentTargetGUID == targetGUID then
|
|
targetName = UnitName("target")
|
|
lib.guidToName[targetGUID] = targetName
|
|
else
|
|
targetName = "Unknown"
|
|
end
|
|
end
|
|
lib:AddEffect(targetGUID, targetName, spellID, duration, 0, "player")
|
|
|
|
else
|
|
lib.learnCastTimers[targetGUID] = lib.learnCastTimers[targetGUID] or {}
|
|
lib.learnCastTimers[targetGUID][spellID] = {
|
|
start = GetTime(),
|
|
caster = casterGUID
|
|
}
|
|
|
|
lib.objects[targetGUID] = lib.objects[targetGUID] or {}
|
|
lib.objects[targetGUID][spellID] = {
|
|
spellID = spellID,
|
|
start = GetTime(),
|
|
duration = 999, -- Placeholder
|
|
stacks = 0,
|
|
caster = casterGUID
|
|
}
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
local evLearn = CreateFrame("Frame", "CleveRoidsLibDebuffLearnFrame", UIParent)
|
|
evLearn:RegisterEvent("RAW_COMBATLOG")
|
|
|
|
evLearn:SetScript("OnEvent", function()
|
|
if event == "RAW_COMBATLOG" then
|
|
local raw = arg2
|
|
if not raw or not find(raw, "fades from") then return end
|
|
|
|
local _, _, spellName = find(raw, "^(.-) fades from ")
|
|
local _, _, targetGUID = find(raw, "from (.-).$")
|
|
|
|
if lower(targetGUID or "") == "you" then
|
|
_, targetGUID = UnitExists("player")
|
|
end
|
|
targetGUID = gsub(targetGUID or "", "^0x", "")
|
|
|
|
if not spellName or targetGUID == "" then return end
|
|
if not lib.objects[targetGUID] then return end
|
|
|
|
local timestamp = GetTime()
|
|
|
|
for spellID in pairs(lib.objects[targetGUID]) do
|
|
local name = SpellInfo(spellID)
|
|
if name then
|
|
name = gsub(name, "%s*%(%s*Rank%s+%d+%s*%)", "")
|
|
if name == spellName then
|
|
if lib.learnCastTimers[targetGUID] and
|
|
lib.learnCastTimers[targetGUID][spellID] then
|
|
|
|
local castTime = lib.learnCastTimers[targetGUID][spellID].start
|
|
local casterGUID = lib.learnCastTimers[targetGUID][spellID].caster
|
|
local actualDuration = timestamp - castTime
|
|
|
|
CleveRoids_LearnedDurations[spellID] = CleveRoids_LearnedDurations[spellID] or {}
|
|
CleveRoids_LearnedDurations[spellID][casterGUID] = floor(actualDuration + 0.5)
|
|
|
|
if CleveRoids.debug then
|
|
DEFAULT_CHAT_FRAME:AddMessage(
|
|
"|cff4b7dccCleveRoids:|r Learned " .. spellName ..
|
|
" (ID:" .. spellID .. ") = " .. floor(actualDuration + 0.5) .. "s"
|
|
)
|
|
end
|
|
|
|
lib.learnCastTimers[targetGUID][spellID] = nil
|
|
if not next(lib.learnCastTimers[targetGUID]) then
|
|
lib.learnCastTimers[targetGUID] = nil
|
|
end
|
|
end
|
|
|
|
if lib.objects[targetGUID][spellID].start +
|
|
lib.objects[targetGUID][spellID].duration <= timestamp then
|
|
lib.objects[targetGUID][spellID] = nil
|
|
end
|
|
|
|
if not next(lib.objects[targetGUID]) then
|
|
lib.objects[targetGUID] = nil
|
|
end
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
-- Cleanup dead/invalid targets
|
|
local evCleanup = CreateFrame("Frame", "CleveRoidsLibDebuffCleanupFrame", UIParent)
|
|
evCleanup:RegisterEvent("PLAYER_TARGET_CHANGED")
|
|
evCleanup:RegisterEvent("PLAYER_DEAD")
|
|
evCleanup:RegisterEvent("PLAYER_ENTERING_WORLD")
|
|
|
|
local lastCleanup = 0
|
|
local CLEANUP_THROTTLE = 2 -- Only run cleanup every 2 seconds max
|
|
|
|
evCleanup:SetScript("OnEvent", function()
|
|
local timestamp = GetTime()
|
|
|
|
-- Cleanup on zone change / login / death
|
|
if event == "PLAYER_ENTERING_WORLD" or event == "PLAYER_DEAD" then
|
|
-- Keep only current target's data
|
|
local _, currentGUID = UnitExists("target")
|
|
if currentGUID then
|
|
local temp = lib.objects[currentGUID]
|
|
lib.objects = {}
|
|
if temp then
|
|
lib.objects[currentGUID] = temp
|
|
end
|
|
else
|
|
lib.objects = {} -- Clear everything
|
|
end
|
|
lastCleanup = timestamp
|
|
return
|
|
end
|
|
|
|
-- Throttle cleanup on target change
|
|
if event == "PLAYER_TARGET_CHANGED" then
|
|
if (timestamp - lastCleanup) < CLEANUP_THROTTLE then
|
|
return -- Don't cleanup too frequently
|
|
end
|
|
lastCleanup = timestamp
|
|
|
|
-- Remove expired effects from all GUIDs
|
|
for guid, effects in pairs(lib.objects) do
|
|
local _, targetGUID = UnitExists("target")
|
|
local isCurrentTarget = (targetGUID == guid)
|
|
|
|
-- Check if current target is dead
|
|
if isCurrentTarget and UnitIsDead("target") then
|
|
lib.objects[guid] = nil
|
|
else
|
|
-- Remove expired effects
|
|
for spellID, effect in pairs(effects) do
|
|
if effect.start + effect.duration < timestamp then
|
|
effects[spellID] = nil
|
|
end
|
|
end
|
|
|
|
-- Remove GUID if no effects remain (but keep current target)
|
|
if not next(effects) and not isCurrentTarget then
|
|
lib.objects[guid] = nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end)
|