Files
ElvUIModernized/ElvUI/Modules/NamePlates/Elements/Auras.lua
T

544 lines
16 KiB
Lua

local E, L, V, P, G = unpack(ElvUI)
local mod = E:GetModule("NamePlates")
local LSM = LibStub("LibSharedMedia-3.0")
local select, unpack, pairs = select, unpack, pairs
local tonumber = tonumber
local band = bit.band
local gsub = string.gsub
local tinsert, tremove, wipe = table.insert, table.remove, table.wipe
local CreateFrame = CreateFrame
local UnitAura = UnitAura
local UnitGUID = UnitGUID
local GetSpellTexture = GetSpellTexture
local RAID_CLASS_COLORS = RAID_CLASS_COLORS
local AURA_TYPE_BUFF = AURA_TYPE_BUFF
local AURA_TYPE_DEBUFF = AURA_TYPE_DEBUFF
local RaidIconBit = {
["STAR"] = "0x00100000",
["CIRCLE"] = "0x00200000",
["DIAMOND"] = "0x00400000",
["TRIANGLE"] = "0x00800000",
["MOON"] = "0x01000000",
["SQUARE"] = "0x02000000",
["CROSS"] = "0x04000000",
["SKULL"] = "0x08000000"
}
local RaidIconIndex = {
"STAR",
"CIRCLE",
"DIAMOND",
"TRIANGLE",
"MOON",
"SQUARE",
"CROSS",
"SKULL"
}
local ByRaidIcon = {}
local ByName = {}
local auraCache = {}
local buffCache = {}
local debuffCache = {}
local auraList = {}
local auraSpellID = {}
local auraName = {}
local auraExpiration = {}
local auraStacks = {}
local auraCaster = {}
local auraDuration = {}
local auraTexture = {}
local auraType = {}
local cachedAuraDurations = {}
local TimeColors = {
[0] = "|cffeeeeee",
[1] = "|cffeeeeee",
[2] = "|cffeeeeee",
[3] = "|cffFFEE00",
[4] = "|cfffe0000"
}
local AURA_UPDATE_INTERVAL = 0.1
local PolledHideIn
do
local Framelist = {}
local Watcherframe = CreateFrame("Frame")
local WatcherframeActive = false
local timeToUpdate = 0
local function CheckFramelist()
local curTime = GetTime()
if curTime < timeToUpdate then return end
local framecount = 0
timeToUpdate = curTime + AURA_UPDATE_INTERVAL
for frame, expiration in pairs(Framelist) do
if expiration < curTime then
frame:Hide()
Framelist[frame] = nil
else
if frame.Poll then frame:Poll(expiration) end
framecount = framecount + 1
end
end
if framecount == 0 then Watcherframe:SetScript("OnUpdate", nil); WatcherframeActive = false end
end
function PolledHideIn(frame, expiration)
if expiration == 0 then
frame:Hide()
Framelist[frame] = nil
else
Framelist[frame] = expiration
frame:Show()
if not WatcherframeActive then
Watcherframe:SetScript("OnUpdate", CheckFramelist)
WatcherframeActive = true
end
end
end
end
local function GetSpellDuration(spellID)
if spellID then return cachedAuraDurations[spellID] end
end
local function SetSpellDuration(spellID, duration)
if spellID then cachedAuraDurations[spellID] = duration end
end
local function UpdateAuraTime(frame, expiration)
local timeleft = expiration - GetTime()
local timervalue, formatid = E:GetTimeInfo(timeleft, 4)
local timeFormat = E.TimeFormats[3][2]
if timervalue < 4 then
timeFormat = E.TimeFormats[4][2]
end
frame.timeLeft:SetText(format("%s%s|r", TimeColors[formatid], timeFormat), timervalue)
end
local function RemoveAuraInstance(guid, spellID, caster)
if guid and spellID and auraList[guid] then
local instanceID = tostring(guid)..tostring(spellID)..(tostring(caster or "UNKNOWN_CASTER"))
local auraID = spellID..(tostring(caster or "UNKNOWN_CASTER"))
if auraList[guid][auraID] then
auraSpellID[instanceID] = nil
auraName[instanceID] = nil
auraExpiration[instanceID] = nil
auraStacks[instanceID] = nil
auraCaster[instanceID] = nil
auraDuration[instanceID] = nil
auraTexture[instanceID] = nil
auraType[instanceID] = nil
auraList[guid][auraID] = nil
end
end
end
local function GetAuraList(guid)
if guid and auraList[guid] then return auraList[guid] end
end
local function GetAuraInstance(guid, auraID)
if guid and auraID then
local instanceID = guid..auraID
local spellID, name, expiration, stacks, caster, duration, texture, type
spellID = auraSpellID[instanceID]
name = auraName[instanceID]
expiration = auraExpiration[instanceID]
stacks = auraStacks[instanceID]
caster = auraCaster[instanceID]
duration = auraDuration[instanceID]
texture = auraTexture[instanceID]
type = auraType[instanceID]
return spellID, name, expiration, stacks, caster, duration, texture, type
end
end
local function SetAuraInstance(guid, name, spellID, expiration, stacks, caster, duration, texture, type)
if guid and spellID and caster and texture then
local auraID = spellID..(tostring(caster or "UNKNOWN_CASTER"))
local instanceID = guid..auraID
auraList[guid] = auraList[guid] or {}
auraList[guid][auraID] = instanceID
auraSpellID[instanceID] = spellID
auraName[instanceID] = name
auraExpiration[instanceID] = expiration
auraStacks[instanceID] = stacks
auraCaster[instanceID] = caster
auraDuration[instanceID] = duration
auraTexture[instanceID] = texture
auraType[instanceID] = type
end
end
local function WipeAuraList(guid)
if guid and auraList[guid] then
local unitAuraList = auraList[guid]
for auraID, instanceID in pairs(unitAuraList) do
auraSpellID[instanceID] = nil
auraName[instanceID] = nil
auraExpiration[instanceID] = nil
auraStacks[instanceID] = nil
auraCaster[instanceID] = nil
auraDuration[instanceID] = nil
auraTexture[instanceID] = nil
auraType[instanceID] = nil
unitAuraList[auraID] = nil
end
end
end
function mod:CleanAuraLists()
local currentTime = GetTime()
for guid, instanceList in pairs(auraList) do
local auraCount = 0
for auraID, instanceID in pairs(instanceList) do
local expiration = auraExpiration[instanceID]
if expiration and expiration < currentTime then
auraList[guid][auraID] = nil
auraSpellID[instanceID] = nil
auraName[instanceID] = nil
auraExpiration[instanceID] = nil
auraStacks[instanceID] = nil
auraCaster[instanceID] = nil
auraDuration[instanceID] = nil
auraTexture[instanceID] = nil
auraType[instanceID] = nil
else
auraCount = auraCount + 1
end
end
if auraCount == 0 then
auraList[guid] = nil
end
end
end
function mod:SetAura(aura, icon, count, expirationTime)
aura.icon:SetTexture(icon)
if count > 1 then
aura.count:SetText(count)
else
aura.count:SetText("")
end
aura:Show()
PolledHideIn(aura, expirationTime)
end
function mod:HideAuraIcons(auras)
for i = 1, getn(auras.icons) do
PolledHideIn(auras.icons[i], 0)
end
end
local currentAura = {}
function mod:UpdateElement_Auras(frame)
if not frame.HealthBar:IsShown() then return end
local guid = frame.guid
if not guid then
if RAID_CLASS_COLORS[frame.UnitClass] then
local name = gsub(frame.oldName:GetText(), "%s%(%*%)","")
guid = ByName[name]
elseif frame.RaidIcon:IsShown() then
guid = ByRaidIcon[frame.RaidIconType]
end
if guid then
frame.guid = guid
else
return
end
end
local hasDebuffs = false
local hasBuffs = false
local numDebuff = 0
local numBuff = 0
local aurasOnUnit = GetAuraList(guid)
debuffCache = wipe(debuffCache)
buffCache = wipe(buffCache)
if aurasOnUnit then
local numAuras = 0
local aura
for instanceid in pairs(aurasOnUnit) do
numAuras = (numDebuff + numBuff) + 1
aura = wipe(currentAura[numAuras] or {})
aura.spellID, aura.name, aura.expirationTime, aura.count, aura.caster, aura.duration, aura.icon, aura.type = GetAuraInstance(guid, instanceid)
local filter = false
local db = self.db.units[frame.UnitType].buffs.filters
if aura.type == AURA_TYPE_DEBUFF then
db = self.db.units[frame.UnitType].debuffs.filters
end
if db.personal and aura.caster == UnitGUID("player") then
filter = true
end
local trackFilter = E.global["unitframe"]["aurafilters"][db.filter]
if db.filter and trackFilter then
local spell = trackFilter.spells[tonumber(aura.spellID)] or trackFilter.spells[aura.name]
if trackFilter.type == "Whitelist" then
if spell and spell.enable then
filter = true
end
elseif trackFilter.type == "Blacklist" and spell and spell.enable then
filter = false
end
end
if filter ~= true then
numAuras = numAuras - 1
RemoveAuraInstance(guid, aura.spellID, aura.caster)
wipe(aura)
end
if tonumber(aura.spellID) then
aura.unit = frame.unit
if aura.expirationTime > GetTime() then
if aura.type == "BUFF" then
numBuff = numBuff + 1
buffCache[numBuff] = aura
else
numDebuff = numDebuff + 1
debuffCache[numDebuff] = aura
end
end
end
end
wipe(currentAura)
end
local frameNum = 1
local maxAuras = getn(frame.Debuffs.icons)
local maxDuration = self.db.units[frame.UnitType].debuffs.filters.maxDuration
self:HideAuraIcons(frame.Debuffs)
if numDebuff > 0 and self.db.units[frame.UnitType].debuffs.enable then
for index = 1, getn(debuffCache) do
if frameNum > maxAuras then break end
local debuff = debuffCache[index]
if debuff.spellID and debuff.expirationTime and debuff.duration <= maxDuration then
self:SetAura(frame.Debuffs.icons[frameNum], debuff.icon, debuff.count, debuff.expirationTime)
frameNum = frameNum + 1
hasDebuffs = true
end
end
end
frameNum = 1
maxAuras = getn(frame.Buffs.icons)
maxDuration = self.db.units[frame.UnitType].buffs.filters.maxDuration
self:HideAuraIcons(frame.Buffs)
if numBuff > 0 and self.db.units[frame.UnitType].buffs.enable then
for index = 1, getn(buffCache) do
if frameNum > maxAuras then break end
local buff = buffCache[index]
if buff.spellID and buff.expirationTime and buff.duration <= maxDuration then
self:SetAura(frame.Buffs.icons[frameNum], buff.icon, buff.count, buff.expirationTime)
frameNum = frameNum + 1
hasBuffs = true
end
end
end
debuffCache = wipe(debuffCache)
buffCache = wipe(buffCache)
local TopLevel = frame.HealthBar
local TopOffset = ((self.db.units[frame.UnitType].showName and select(2, frame.Name:GetFont()) + 5) or 0)
if hasDebuffs then
TopOffset = TopOffset + 3
frame.Debuffs:SetPoint("BOTTOMLEFT", TopLevel, "TOPLEFT", 0, TopOffset)
frame.Debuffs:SetPoint("BOTTOMRIGHT", TopLevel, "TOPRIGHT", 0, TopOffset)
TopLevel = frame.Debuffs
TopOffset = 3
end
if hasBuffs then
if not hasDebuffs then
TopOffset = TopOffset + 3
end
frame.Buffs:SetPoint("BOTTOMLEFT", TopLevel, "TOPLEFT", 0, TopOffset)
frame.Buffs:SetPoint("BOTTOMRIGHT", TopLevel, "TOPRIGHT", 0, TopOffset)
TopLevel = frame.Buffs
TopOffset = 3
end
if frame.TopLevelFrame ~= TopLevel then
frame.TopLevelFrame = TopLevel
frame.TopOffset = TopOffset
end
end
function mod:UpdateElement_AurasByUnitID(unit)
--[[local guid = UnitGUID(unit)
WipeAuraList(guid)
local index = 1
local name, _, texture, count, _, duration, expirationTime, unitCaster, _, _, spellID = UnitAura(unit, index, "HARMFUL")
while name do
SetSpellDuration(spellID, duration)
SetAuraInstance(guid, name, spellID, expirationTime, count, UnitGUID(unitCaster or ""), duration, texture, AURA_TYPE_DEBUFF)
index = index + 1
name , _, texture, count, _, duration, expirationTime, unitCaster, _, _, spellID = UnitAura(unit, index, "HARMFUL")
end
index = 1
local name, _, texture, count, _, duration, expirationTime, unitCaster, _, _, spellID = UnitAura(unit, index, "HELPFUL")
while name do
SetSpellDuration(spellID, duration)
SetAuraInstance(guid, name, spellID, expirationTime, count, UnitGUID(unitCaster or ""), duration, texture, AURA_TYPE_BUFF)
index = index + 1
name, _, texture, count, _, duration, expirationTime, unitCaster, _, _, spellID = UnitAura(unit, index, "HELPFUL")
end
local raidIcon, name
if UnitPlayerControlled(unit) then name = UnitName(unit) end
raidIcon = RaidIconIndex[GetRaidTargetIndex(unit) or ""]
if raidIcon then ByRaidIcon[raidIcon] = guid end
local frame = self:SearchForFrame(guid, raidIcon, name)
if frame then
self:UpdateElement_Auras(frame)
end]]
end
function mod:COMBAT_LOG_EVENT_UNFILTERED(_, _, event, sourceGUID, _, _, destGUID, destName, destFlags, ...)
if destGUID == UnitGUID("target") then return end
--if band(destFlags, COMBATLOG_OBJECT_REACTION_FRIENDLY) ~= 0 then then return
if not (event == "SPELL_AURA_APPLIED" or event == "SPELL_AURA_REFRESH" or event == "SPELL_AURA_APPLIED_DOSE" or event == "SPELL_AURA_REMOVED_DOSE" or event == "SPELL_AURA_BROKEN" or event == "SPELL_AURA_BROKEN_SPELL" or event == "SPELL_AURA_REMOVED") then return end
--local spellID, spellName, _, auraType, stackCount = ...
if event == "SPELL_AURA_APPLIED" or event == "SPELL_AURA_REFRESH" then
local duration = GetSpellDuration(spellID)
local texture = GetSpellTexture(spellID)
SetAuraInstance(destGUID, spellName, spellID, GetTime() + (duration or 0), 1, sourceGUID, duration, texture, auraType)
elseif event == "SPELL_AURA_APPLIED_DOSE" or event == "SPELL_AURA_REMOVED_DOSE" then
local duration = GetSpellDuration(spellID)
local texture = GetSpellTexture(spellID)
SetAuraInstance(destGUID, spellName, spellID, GetTime() + (duration or 0), stackCount, sourceGUID, duration, texture, auraType)
elseif event == "SPELL_AURA_BROKEN" or event == "SPELL_AURA_BROKEN_SPELL" or event == "SPELL_AURA_REMOVED" then
RemoveAuraInstance(destGUID, spellID, sourceGUID)
end
local name, raidIcon
if band(destFlags, COMBATLOG_OBJECT_CONTROL_PLAYER) > 0 then
local rawName = strsplit("-", destName)
ByName[rawName] = destGUID
name = rawName
end
for iconName, bitmask in pairs(RaidIconBit) do
if band(destFlags, bitmask) > 0 then
ByRaidIcon[iconName] = destGUID
raidIcon = iconName
break
end
end
local frame = self:SearchForFrame(destGUID, raidIcon, name)
if frame then
self:UpdateElement_Auras(frame)
end
end
function mod:CreateAuraIcon(parent)
local aura = CreateFrame("Frame", nil, parent)
self:StyleFrame(aura, true)
aura.icon = aura:CreateTexture(nil, "OVERLAY")
aura.icon:SetAllPoints()
aura.icon:SetTexCoord(unpack(E.TexCoords))
aura.timeLeft = aura:CreateFontString(nil, "OVERLAY")
aura.timeLeft:SetPoint("TOPLEFT", 2, 2)
aura.timeLeft:SetFont(LSM:Fetch("font", self.db.font), self.db.fontSize, self.db.fontOutline)
aura.count = aura:CreateFontString(nil, "OVERLAY")
aura.count:SetPoint("BOTTOMRIGHT", 0, 0)
aura.count:SetFont(LSM:Fetch("font", self.db.font), self.db.fontSize, self.db.fontOutline)
aura.Poll = parent.PollFunction
return aura
end
function mod:Auras_SizeChanged(width)
local numAuras = getn(self.icons)
for i = 1, numAuras do
self.icons[i]:SetWidth(((width - numAuras) / numAuras) - (E.private.general.pixelPerfect and 0 or 3))
self.icons[i]:SetHeight((self.db.baseHeight or 18) * (self:GetParent().HealthBar.currentScale or 1))
end
self:SetHeight((self.db.baseHeight or 18) * (self:GetParent().HealthBar.currentScale or 1))
end
function mod:UpdateAuraIcons(auras)
local maxAuras = auras.db.numAuras
local numCurrentAuras = getn(auras.icons)
if(numCurrentAuras > maxAuras) then
for i = maxAuras, numCurrentAuras do
tinsert(auraCache, auras.icons[i])
auras.icons[i]:Hide()
auras.icons[i] = nil
end
end
if(numCurrentAuras ~= maxAuras) then
self.Auras_SizeChanged(auras, auras:GetWidth(), auras:GetHeight())
end
for i = 1, maxAuras do
auras.icons[i] = auras.icons[i] or tremove(auraCache) or mod:CreateAuraIcon(auras)
auras.icons[i]:SetParent(auras)
auras.icons[i]:ClearAllPoints()
auras.icons[i]:Hide()
auras.icons[i]:SetHeight(auras.db.baseHeight or 18)
if(auras.side == "LEFT") then
if(i == 1) then
auras.icons[i]:SetPoint("BOTTOMLEFT", auras, "BOTTOMLEFT")
else
auras.icons[i]:SetPoint("LEFT", auras.icons[i-1], "RIGHT", E.Border + E.Spacing*3, 0)
end
else
if(i == 1) then
auras.icons[i]:SetPoint("BOTTOMRIGHT", auras, "BOTTOMRIGHT")
else
auras.icons[i]:SetPoint("RIGHT", auras.icons[i-1], "LEFT", -(E.Border + E.Spacing*3), 0)
end
end
end
end
function mod:ConstructElement_Auras(frame, side)
local auras = CreateFrame("Frame", nil, frame)
auras:SetScript("OnSizeChanged", mod.Auras_SizeChanged)
auras:SetHeight(18)
auras.side = side
auras.PollFunction = UpdateAuraTime
auras.icons = {}
return auras
end