You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
437 lines
16 KiB
437 lines
16 KiB
local _, Cell = ...
|
|
local L = Cell.L
|
|
local F = Cell.funcs
|
|
local I = Cell.iFuncs
|
|
local LCG = LibStub("LibCustomGlow-1.0")
|
|
|
|
local UnitIsVisible = UnitIsVisible
|
|
local UnitExists = UnitExists
|
|
local UnitGUID = UnitGUID
|
|
local UnitIsUnit = UnitIsUnit
|
|
local UnitIsEnemy = UnitIsEnemy
|
|
local UnitCastingInfo = UnitCastingInfo
|
|
local UnitChannelInfo = UnitChannelInfo
|
|
|
|
local casts = {}
|
|
local castsOnUnit, sortedCastsOnUnit = {}, {}
|
|
local recheck = {}
|
|
local maxIcons, showAllSpells
|
|
local eventFrame = CreateFrame("Frame")
|
|
|
|
local function Reset()
|
|
wipe(recheck)
|
|
wipe(casts)
|
|
wipe(castsOnUnit)
|
|
wipe(sortedCastsOnUnit)
|
|
end
|
|
|
|
-------------------------------------------------
|
|
-- show / hide
|
|
-------------------------------------------------
|
|
local function HideCasts(b)
|
|
b.indicators.targetedSpells:UpdateSize(0)
|
|
b.indicators.targetedSpells:HideGlow()
|
|
end
|
|
|
|
local function ShowCasts(b, showGlow, sortedCasts, num)
|
|
num = min(maxIcons, num)
|
|
for i = 1, num do
|
|
local cast = sortedCasts[i]
|
|
b.indicators.targetedSpells[i].cooldown:SetReverse(not cast.isChanneling)
|
|
b.indicators.targetedSpells[i]:SetCooldown(cast.startTime, cast.endTime-cast.startTime, cast.icon, cast.count)
|
|
end
|
|
b.indicators.targetedSpells:UpdateSize(num)
|
|
|
|
if showGlow then
|
|
b.indicators.targetedSpells:ShowGlow(unpack(Cell.vars.targetedSpellsGlow))
|
|
else
|
|
b.indicators.targetedSpells:HideGlow()
|
|
end
|
|
end
|
|
|
|
-------------------------------------------------
|
|
-- update casts for guid
|
|
-------------------------------------------------
|
|
local function GetCastsOnUnit(guid)
|
|
if castsOnUnit[guid] then
|
|
wipe(castsOnUnit[guid])
|
|
wipe(sortedCastsOnUnit[guid])
|
|
else
|
|
castsOnUnit[guid] = {}
|
|
sortedCastsOnUnit[guid] = {}
|
|
end
|
|
|
|
local inListFound
|
|
for sourceGUID, castInfo in pairs(casts) do
|
|
if guid == castInfo["targetGUID"] then
|
|
if castInfo["endTime"] > GetTime() then -- not expired
|
|
local spellId = castInfo["spellId"]
|
|
if not castsOnUnit[guid][spellId] then
|
|
castsOnUnit[guid][spellId] = {["count"] = 0}
|
|
end
|
|
if not castsOnUnit[guid][spellId]["endTime"] or castsOnUnit[guid][spellId]["endTime"] > castInfo["endTime"] then --! shorter duration
|
|
castsOnUnit[guid][spellId]["startTime"] = castInfo["startTime"]
|
|
castsOnUnit[guid][spellId]["endTime"] = castInfo["endTime"]
|
|
castsOnUnit[guid][spellId]["icon"] = castInfo["icon"]
|
|
end
|
|
castsOnUnit[guid][spellId]["count"] = castsOnUnit[guid][spellId]["count"] + 1
|
|
|
|
if Cell.vars.targetedSpellsList[spellId] then
|
|
castsOnUnit[guid][spellId]["inList"] = true
|
|
inListFound = true
|
|
end
|
|
else
|
|
casts[sourceGUID] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
return castsOnUnit[guid], inListFound
|
|
end
|
|
|
|
local function Comparator(a, b)
|
|
if a.inList ~= b.inList then
|
|
return a.inList
|
|
end
|
|
return a.startTime < b.startTime
|
|
end
|
|
|
|
local function UpdateCastsOnUnit(guid)
|
|
if not guid then return end
|
|
|
|
-- local startTime, endTime, spellId, icon, isChanneling
|
|
local t, showGlow = GetCastsOnUnit(guid)
|
|
|
|
for spellId, castInfo in pairs(t) do
|
|
tinsert(sortedCastsOnUnit[guid], castInfo)
|
|
|
|
-- if not endTime then --! init
|
|
-- startTime, endTime, spellId, icon, isChanneling = castInfo["startTime"], castInfo["endTime"], castInfo["spellId"], castInfo["icon"], castInfo["isChanneling"]
|
|
-- else
|
|
-- spellId = castInfo["spellId"]
|
|
-- if Cell.vars.targetedSpellsList[spellId] then --! [IN LIST]
|
|
-- if not inListFound or endTime > castInfo["endTime"] then --! NOT FOUND BEFORE or SHORTER DURATION
|
|
-- startTime, endTime, icon, isChanneling = castInfo["startTime"], castInfo["endTime"], castInfo["icon"], castInfo["isChanneling"]
|
|
-- end
|
|
-- elseif not inListFound and endTime > castInfo["endTime"] then --! [NOT IN LIST] NOT FOUND BEFORE and SHORTER DURATION
|
|
-- startTime, endTime, icon, isChanneling = castInfo["startTime"], castInfo["endTime"], castInfo["icon"], castInfo["isChanneling"]
|
|
-- end
|
|
-- end
|
|
|
|
-- if Cell.vars.targetedSpellsList[spellId] then
|
|
-- inListFound = true
|
|
-- end
|
|
end
|
|
|
|
local n = #sortedCastsOnUnit[guid]
|
|
|
|
if n == 0 then
|
|
F:HandleUnitButton("guid", guid, HideCasts)
|
|
else
|
|
table.sort(sortedCastsOnUnit[guid], Comparator)
|
|
F:HandleUnitButton("guid", guid, ShowCasts, showGlow, sortedCastsOnUnit[guid], n)
|
|
end
|
|
end
|
|
|
|
-------------------------------------------------
|
|
-- check if sourceUnit is casting
|
|
-------------------------------------------------
|
|
local function CheckUnitCast(sourceUnit, isRecheck)
|
|
if not UnitIsEnemy("player", sourceUnit) then return end
|
|
|
|
local sourceGUID = UnitGUID(sourceUnit)
|
|
local targetGUID
|
|
local previousTarget, isChanneling
|
|
|
|
if casts[sourceGUID] then
|
|
previousTarget = casts[sourceGUID]["targetGUID"]
|
|
if casts[sourceGUID]["endTime"] <= GetTime() then
|
|
--! expired
|
|
casts[sourceGUID] = nil
|
|
UpdateCastsOnUnit(previousTarget)
|
|
previousTarget = nil
|
|
end
|
|
end
|
|
|
|
-- name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId
|
|
local name, _, texture, startTimeMS, endTimeMS, _, _, notInterruptible, spellId = UnitCastingInfo(sourceUnit)
|
|
if not name then
|
|
-- name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId
|
|
name, _, texture, startTimeMS, endTimeMS, _, notInterruptible, spellId = UnitChannelInfo(sourceUnit)
|
|
isChanneling = true
|
|
end
|
|
|
|
-- print(sourceUnit, name, spellId)
|
|
|
|
if spellId and (Cell.vars.targetedSpellsList[spellId] or showAllSpells) then
|
|
if casts[sourceGUID] then
|
|
casts[sourceGUID]["startTime"] = startTimeMS/1000
|
|
casts[sourceGUID]["endTime"] = endTimeMS/1000
|
|
casts[sourceGUID]["spellId"] = spellId
|
|
casts[sourceGUID]["icon"] = texture
|
|
else
|
|
casts[sourceGUID] = {
|
|
["startTime"] = startTimeMS/1000,
|
|
["endTime"] = endTimeMS/1000,
|
|
["spellId"] = spellId,
|
|
["icon"] = texture,
|
|
["isChanneling"] = isChanneling,
|
|
-- ["targetGUID"] = targetGUID,
|
|
-- ["sourceUnit"] = sourceUnit,
|
|
-- ["targetUnit"] = targetUnit,
|
|
["recheck"] = 0,
|
|
}
|
|
end
|
|
|
|
local targetUnit = sourceUnit.."target"
|
|
targetUnit = F:GetTargetUnitID(targetUnit) -- units in group (players/pets), no npcs
|
|
if targetUnit then targetGUID = UnitGUID(targetUnit) end
|
|
|
|
-- update spell target
|
|
casts[sourceGUID]["targetUnit"] = targetUnit
|
|
casts[sourceGUID]["targetGUID"] = targetGUID
|
|
casts[sourceGUID]["nonNameplate"] = not strfind(sourceUnit, "^nameplate")
|
|
|
|
UpdateCastsOnUnit(targetGUID)
|
|
|
|
if not isRecheck then
|
|
if not recheck[sourceGUID] or not (strfind(sourceUnit, "target$") or strfind(sourceUnit, "^nameplate")) then
|
|
recheck[sourceGUID] = sourceUnit
|
|
end
|
|
eventFrame:Show()
|
|
end
|
|
end
|
|
|
|
if previousTarget and previousTarget ~= targetGUID then
|
|
UpdateCastsOnUnit(previousTarget)
|
|
end
|
|
end
|
|
|
|
-------------------------------------------------
|
|
-- recheck
|
|
-------------------------------------------------
|
|
eventFrame:Hide()
|
|
eventFrame:SetScript("OnUpdate", function(self, elapsed)
|
|
self.elapsed = (self.elapsed or 0) + elapsed
|
|
if self.elapsed >= 0.1 then
|
|
self.elapsed = 0
|
|
|
|
local empty = true
|
|
|
|
for guid, unit in pairs(recheck) do
|
|
if casts[guid] then
|
|
casts[guid]["recheck"] = casts[guid]["recheck"] + 1
|
|
if casts[guid]["recheck"] >= 6 then
|
|
recheck[guid] = nil
|
|
else
|
|
empty = false
|
|
local recheckRequired = (not casts[guid]["targetUnit"] and UnitExists(unit.."target")) or (casts[guid]["targetUnit"] and not UnitIsUnit(unit.."target", casts[guid]["targetUnit"]))
|
|
if recheckRequired then
|
|
-- print(unit, casts[guid]["recheck"], recheckRequired)
|
|
CheckUnitCast(unit, true)
|
|
end
|
|
end
|
|
else
|
|
recheck[guid] = nil
|
|
end
|
|
end
|
|
|
|
if empty then
|
|
eventFrame:Hide()
|
|
end
|
|
end
|
|
end)
|
|
|
|
-------------------------------------------------
|
|
-- events
|
|
-------------------------------------------------
|
|
eventFrame:SetScript("OnEvent", function(_, event, sourceUnit)
|
|
if event == "ENCOUNTER_END" then
|
|
Reset()
|
|
F:IterateAllUnitButtons(HideCasts, true)
|
|
return
|
|
end
|
|
|
|
if sourceUnit and strfind(sourceUnit, "^soft") then return end
|
|
|
|
if event == "PLAYER_TARGET_CHANGED" then
|
|
CheckUnitCast("target")
|
|
|
|
elseif event == "UNIT_SPELLCAST_START" or event == "UNIT_SPELLCAST_CHANNEL_START" or event == "UNIT_SPELLCAST_DELAYED" or event == "UNIT_SPELLCAST_CHANNEL_UPDATE" or event == "NAME_PLATE_UNIT_ADDED" then
|
|
CheckUnitCast(sourceUnit)
|
|
|
|
elseif event == "UNIT_SPELLCAST_STOP" or event == "UNIT_SPELLCAST_INTERRUPTED" or event == "UNIT_SPELLCAST_FAILED" or event == "UNIT_SPELLCAST_CHANNEL_STOP" then
|
|
local sourceGUID = UnitGUID(sourceUnit)
|
|
if casts[sourceGUID] then
|
|
previousTarget = casts[sourceGUID]["targetGUID"]
|
|
casts[sourceGUID] = nil
|
|
UpdateCastsOnUnit(previousTarget)
|
|
end
|
|
|
|
elseif event == "NAME_PLATE_UNIT_REMOVED" then
|
|
local sourceGUID = UnitGUID(sourceUnit)
|
|
if casts[sourceGUID] and not casts[sourceGUID]["nonNameplate"] then
|
|
previousTarget = casts[sourceGUID]["targetGUID"]
|
|
casts[sourceGUID] = nil
|
|
UpdateCastsOnUnit(previousTarget)
|
|
end
|
|
end
|
|
end)
|
|
|
|
-------------------------------------------------
|
|
-- create
|
|
-------------------------------------------------
|
|
local function SetCooldown(frame, start, duration, icon, count)
|
|
frame.duration:Hide()
|
|
|
|
if count ~= 1 then
|
|
frame.stack:Show()
|
|
frame.stack:SetText(count)
|
|
else
|
|
frame.stack:Hide()
|
|
end
|
|
|
|
frame.border:Show()
|
|
frame.cooldown:Show()
|
|
frame.cooldown:SetSwipeColor(unpack(Cell.vars.targetedSpellsGlow[2]))
|
|
frame.cooldown:SetCooldown(start, duration)
|
|
frame.icon:SetTexture(icon)
|
|
frame:Show()
|
|
end
|
|
|
|
local function SetFont(frame, ...)
|
|
for i = 1, #frame do
|
|
I.SetFont(frame[i].stack, frame[i], ...)
|
|
end
|
|
end
|
|
|
|
local function ShowGlowPreview(frame)
|
|
frame:ShowGlow(unpack(Cell.vars.targetedSpellsGlow))
|
|
end
|
|
|
|
local function ShowGlow(frame, glowType, color, arg1, arg2, arg3, arg4)
|
|
if glowType == "Normal" then
|
|
LCG.PixelGlow_Stop(frame.tsGlowFrame)
|
|
LCG.AutoCastGlow_Stop(frame.tsGlowFrame)
|
|
LCG.ProcGlow_Stop(frame.tsGlowFrame)
|
|
LCG.ButtonGlow_Start(frame.tsGlowFrame, color)
|
|
elseif glowType == "Pixel" then
|
|
LCG.ButtonGlow_Stop(frame.tsGlowFrame)
|
|
LCG.AutoCastGlow_Stop(frame.tsGlowFrame)
|
|
LCG.ProcGlow_Stop(frame.tsGlowFrame)
|
|
-- color, N, frequency, length, thickness
|
|
LCG.PixelGlow_Start(frame.tsGlowFrame, color, arg1, arg2, arg3, arg4)
|
|
elseif glowType == "Shine" then
|
|
LCG.ButtonGlow_Stop(frame.tsGlowFrame)
|
|
LCG.PixelGlow_Stop(frame.tsGlowFrame)
|
|
LCG.ProcGlow_Stop(frame.tsGlowFrame)
|
|
-- color, N, frequency, scale
|
|
LCG.AutoCastGlow_Start(frame.tsGlowFrame, color, arg1, arg2, arg3)
|
|
elseif glowType == "Proc" then
|
|
LCG.ButtonGlow_Stop(frame.tsGlowFrame)
|
|
LCG.PixelGlow_Stop(frame.tsGlowFrame)
|
|
LCG.AutoCastGlow_Stop(frame.tsGlowFrame)
|
|
-- color, duration
|
|
LCG.ProcGlow_Start(frame.tsGlowFrame, {color=color, duration=arg1, startAnim=false})
|
|
else
|
|
LCG.ButtonGlow_Stop(frame.tsGlowFrame)
|
|
LCG.PixelGlow_Stop(frame.tsGlowFrame)
|
|
LCG.AutoCastGlow_Stop(frame.tsGlowFrame)
|
|
LCG.ProcGlow_Stop(frame.tsGlowFrame)
|
|
end
|
|
end
|
|
|
|
local function HideGlow(frame)
|
|
LCG.ButtonGlow_Stop(frame.tsGlowFrame)
|
|
LCG.PixelGlow_Stop(frame.tsGlowFrame)
|
|
LCG.AutoCastGlow_Stop(frame.tsGlowFrame)
|
|
LCG.ProcGlow_Stop(frame.tsGlowFrame)
|
|
end
|
|
|
|
function I.CreateTargetedSpells(parent)
|
|
local targetedSpells = CreateFrame("Frame", parent:GetName().."TargetedSpellsParent", parent.widgets.indicatorFrame)
|
|
parent.indicators.targetedSpells = targetedSpells
|
|
targetedSpells:Hide()
|
|
|
|
targetedSpells.tsGlowFrame = parent.widgets.tsGlowFrame
|
|
targetedSpells._SetSize = targetedSpells.SetSize
|
|
targetedSpells.SetSize = I.Cooldowns_SetSize
|
|
targetedSpells.SetBorder = I.Cooldowns_SetBorder
|
|
targetedSpells.UpdateSize = I.Cooldowns_UpdateSize_WithSpacing
|
|
targetedSpells.SetOrientation = I.Cooldowns_SetOrientation_WithSpacing
|
|
targetedSpells.ShowGlow = ShowGlow
|
|
targetedSpells.HideGlow = HideGlow
|
|
targetedSpells.SetFont = SetFont
|
|
targetedSpells.ShowGlowPreview = ShowGlowPreview
|
|
targetedSpells.HideGlowPreview = HideGlow
|
|
|
|
for i = 1, 3 do
|
|
local frame = I.CreateAura_BorderIcon(parent:GetName().."TargetedSpells"..i, targetedSpells, 2)
|
|
tinsert(targetedSpells, frame)
|
|
frame.SetCooldown = SetCooldown
|
|
-- frame:SetScript("OnShow", targetedSpells.UpdateSize)
|
|
-- frame:SetScript("OnHide", targetedSpells.UpdateSize)
|
|
frame.cooldown:SetScript("OnCooldownDone", function()
|
|
frame:Hide()
|
|
end)
|
|
end
|
|
end
|
|
|
|
-------------------------------------------------
|
|
-- functions
|
|
-------------------------------------------------
|
|
-- NOTE: in case there's a casting spell, hide!
|
|
local function EnterLeaveInstance()
|
|
Reset()
|
|
F:IterateAllUnitButtons(HideCasts, true)
|
|
end
|
|
|
|
function I.EnableTargetedSpells(enabled)
|
|
if enabled then
|
|
F:IterateAllUnitButtons(function(b)
|
|
b.indicators.targetedSpells:Show()
|
|
end, true)
|
|
|
|
-- UNIT_SPELLCAST_DELAYED UNIT_SPELLCAST_FAILED UNIT_SPELLCAST_INTERRUPTED UNIT_SPELLCAST_START UNIT_SPELLCAST_STOP
|
|
-- UNIT_SPELLCAST_CHANNEL_START UNIT_SPELLCAST_CHANNEL_STOP
|
|
-- PLAYER_TARGET_CHANGED ENCOUNTER_END
|
|
|
|
eventFrame:RegisterEvent("UNIT_SPELLCAST_START")
|
|
eventFrame:RegisterEvent("UNIT_SPELLCAST_STOP")
|
|
eventFrame:RegisterEvent("UNIT_SPELLCAST_DELAYED")
|
|
eventFrame:RegisterEvent("UNIT_SPELLCAST_FAILED")
|
|
eventFrame:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED")
|
|
eventFrame:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START")
|
|
eventFrame:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP")
|
|
eventFrame:RegisterEvent("UNIT_SPELLCAST_CHANNEL_UPDATE")
|
|
|
|
eventFrame:RegisterEvent("PLAYER_TARGET_CHANGED")
|
|
eventFrame:RegisterEvent("NAME_PLATE_UNIT_ADDED")
|
|
eventFrame:RegisterEvent("NAME_PLATE_UNIT_REMOVED")
|
|
|
|
eventFrame:RegisterEvent("ENCOUNTER_END")
|
|
|
|
Cell:RegisterCallback("EnterInstance", "TargetedSpells_EnterInstance", EnterLeaveInstance)
|
|
Cell:RegisterCallback("LeaveInstance", "TargetedSpells_LeaveInstance", EnterLeaveInstance)
|
|
else
|
|
Reset()
|
|
eventFrame:Hide()
|
|
eventFrame:UnregisterAllEvents()
|
|
|
|
Cell:UnregisterCallback("EnterInstance", "TargetedSpells_EnterInstance")
|
|
Cell:UnregisterCallback("LeaveInstance", "TargetedSpells_LeaveInstance")
|
|
|
|
F:IterateAllUnitButtons(function(b)
|
|
HideCasts(b)
|
|
b.indicators.targetedSpells:Hide()
|
|
end, true)
|
|
end
|
|
end
|
|
|
|
function I.ShowAllTargetedSpells(showAll)
|
|
showAllSpells = showAll
|
|
end
|
|
|
|
function I.UpdateTargetedSpellsNum(num)
|
|
maxIcons = num
|
|
end
|