381 lines
11 KiB
Lua
381 lines
11 KiB
Lua
local _, ns = ...
|
|
local oUF = oUF or ns.oUF
|
|
assert(oUF, "oUF_AuraBars was unable to locate oUF install.")
|
|
|
|
local type = type
|
|
local unpack = unpack
|
|
local floor, huge, min = math.floor, math.huge, math.min
|
|
local format = string.format
|
|
local tsort, tremove = table.sort, table.remove
|
|
|
|
local CreateFrame = CreateFrame
|
|
local GetTime = GetTime
|
|
local UnitAura = UnitAura
|
|
local UnitIsFriend = UnitIsFriend
|
|
|
|
local DAY, HOUR, MINUTE = 86400, 3600, 60
|
|
local function formatTime(s)
|
|
if s < MINUTE then
|
|
return format("%.1fs", s)
|
|
elseif s < HOUR then
|
|
return format("%dm %ds", s / 60 % 60, s % 60)
|
|
elseif s < DAY then
|
|
return format("%dh %dm", s / HOUR, s / 60 % 60)
|
|
else
|
|
return format("%dd %dh", s/DAY, (s / HOUR) - (floor(s / DAY) * 24))
|
|
end
|
|
end
|
|
|
|
local function UpdateTooltip(self)
|
|
GameTooltip:SetUnitAura(self.__unit, self:GetParent().aura.name, self:GetParent().aura.rank, self:GetParent().aura.filter)
|
|
end
|
|
|
|
local function OnEnter(self)
|
|
if not self:IsVisible() then return end
|
|
|
|
GameTooltip:SetOwner(self, "ANCHOR_BOTTOMRIGHT")
|
|
self:UpdateTooltip()
|
|
end
|
|
|
|
local function OnLeave(self)
|
|
GameTooltip:Hide()
|
|
end
|
|
|
|
local function SetAnchors(self)
|
|
local bars = self.bars
|
|
|
|
for i = 1, #bars do
|
|
local frame = bars[i]
|
|
local anchor = frame.anchor
|
|
|
|
frame:Height(self.auraBarHeight or 20)
|
|
frame:Width((self.auraBarWidth or self:GetWidth()) - (frame:GetHeight() + (self.gap or 0)))
|
|
frame.statusBar.iconHolder:Size(frame:GetHeight())
|
|
|
|
frame:ClearAllPoints()
|
|
if self.down then
|
|
if self == anchor then -- Root frame so indent for icon
|
|
frame:SetPoint("TOPLEFT", anchor, "TOPLEFT", (frame:GetHeight() + (self.gap or 0)), -1)
|
|
else
|
|
frame:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", 0, (-self.spacing or 0))
|
|
end
|
|
else
|
|
if self == anchor then -- Root frame so indent for icon
|
|
frame:SetPoint("BOTTOMLEFT", anchor, "BOTTOMLEFT", (frame:GetHeight() + (self.gap or 0)), 1)
|
|
else
|
|
frame:SetPoint("BOTTOMLEFT", anchor, "TOPLEFT", 0, (self.spacing or 0))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function CreateAuraBar(self, anchor)
|
|
local element = self.AuraBars
|
|
|
|
local frame = CreateFrame("Frame", nil, element)
|
|
frame:Height(element.auraBarHeight or 20)
|
|
frame:Width((element.auraBarWidth or element:GetWidth()) - (frame:GetHeight() + (element.gap or 0)))
|
|
frame.anchor = anchor
|
|
|
|
-- the main bar
|
|
local statusBar = CreateFrame("StatusBar", nil, frame)
|
|
statusBar:SetStatusBarTexture(element.auraBarTexture or [[Interface\TargetingFrame\UI-StatusBar]])
|
|
statusBar:SetAlpha(element.fgalpha or 1)
|
|
statusBar:SetAllPoints(frame)
|
|
|
|
frame.statusBar = statusBar
|
|
|
|
if element.down then
|
|
if element == anchor then -- Root frame so indent for icon
|
|
frame:SetPoint("TOPLEFT", anchor, "TOPLEFT", (frame:GetHeight() + (element.gap or 0)), -1)
|
|
else
|
|
frame:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", 0, (-element.spacing or 0))
|
|
end
|
|
else
|
|
if element == anchor then -- Root frame so indent for icon
|
|
frame:SetPoint("BOTTOMLEFT", anchor, "BOTTOMLEFT", (frame:GetHeight() + (element.gap or 0)), 1)
|
|
else
|
|
frame:SetPoint("BOTTOMLEFT", anchor, "TOPLEFT", 0, (element.spacing or 0))
|
|
end
|
|
end
|
|
|
|
local spark = statusBar:CreateTexture(nil, "OVERLAY", nil)
|
|
spark:SetTexture([[Interface\CastingBar\UI-CastingBar-Spark]])
|
|
spark:Width(12)
|
|
spark:SetBlendMode("ADD")
|
|
spark:SetPoint("CENTER", statusBar:GetStatusBarTexture(), "RIGHT")
|
|
statusBar.spark = spark
|
|
|
|
statusBar.iconHolder = CreateFrame("Button", nil, statusBar)
|
|
statusBar.iconHolder:Size(frame:GetHeight())
|
|
statusBar.iconHolder:SetPoint("BOTTOMRIGHT", frame, "BOTTOMLEFT", -element.gap, 0)
|
|
statusBar.iconHolder.__unit = self.unit
|
|
statusBar.iconHolder:SetScript("OnEnter", OnEnter)
|
|
statusBar.iconHolder:SetScript("OnLeave", OnLeave)
|
|
statusBar.iconHolder.UpdateTooltip = UpdateTooltip
|
|
|
|
statusBar.icon = statusBar.iconHolder:CreateTexture(nil, "BACKGROUND")
|
|
statusBar.icon:SetTexCoord(unpack(ElvUI[1].TexCoords))
|
|
statusBar.icon:SetAllPoints()
|
|
|
|
statusBar.spelltime = statusBar:CreateFontString(nil, "ARTWORK")
|
|
if element.spellTimeObject then
|
|
statusBar.spelltime:SetFontObject(element.spellTimeObject)
|
|
else
|
|
statusBar.spelltime:SetFont(element.spellTimeFont or [[Fonts\FRIZQT__.TTF]], element.spellTimeSize or 10)
|
|
end
|
|
statusBar.spelltime:SetTextColor(1, 1, 1)
|
|
statusBar.spelltime:SetJustifyH("RIGHT")
|
|
statusBar.spelltime:SetJustifyV("CENTER")
|
|
statusBar.spelltime:SetPoint("RIGHT")
|
|
|
|
statusBar.spellname = statusBar:CreateFontString(nil, "ARTWORK")
|
|
if element.spellNameObject then
|
|
statusBar.spellname:SetFontObject(element.spellNameObject)
|
|
else
|
|
statusBar.spellname:SetFont(element.spellNameFont or [[Fonts\FRIZQT__.TTF]], element.spellNameSize or 10)
|
|
end
|
|
statusBar.spellname:SetTextColor(1, 1, 1)
|
|
statusBar.spellname:SetJustifyH("LEFT")
|
|
statusBar.spellname:SetJustifyV("CENTER")
|
|
statusBar.spellname:SetPoint("LEFT")
|
|
statusBar.spellname:SetPoint("RIGHT", statusBar.spelltime, "LEFT")
|
|
|
|
if element.PostCreateBar then
|
|
element.PostCreateBar(frame)
|
|
end
|
|
|
|
return frame
|
|
end
|
|
|
|
local function UpdateBars(element)
|
|
local bars = element.bars
|
|
local currentTime = GetTime()
|
|
|
|
for i = 1, #bars do
|
|
local frame = bars[i]
|
|
if not frame:IsVisible() then break end
|
|
|
|
local bar = frame.statusBar
|
|
|
|
if bar.aura.noTime then
|
|
bar.spelltime:SetText()
|
|
bar.spark:Hide()
|
|
else
|
|
local timeleft = bar.aura.expirationTime - currentTime
|
|
bar:SetValue(timeleft)
|
|
bar.spelltime:SetText(formatTime(timeleft))
|
|
|
|
if element.spark == true then
|
|
if element.scaleTime and ((element.scaleTime <= 0) or (element.scaleTime > 0 and timeleft < element.scaleTime)) then
|
|
bar.spark:Show()
|
|
else
|
|
bar.spark:Hide()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function DefaultFilter(self, unit, name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate)
|
|
if unitCaster == "player" and not shouldConsolidate then
|
|
return true
|
|
end
|
|
end
|
|
|
|
local function sortByTime(a, b)
|
|
local compa = a.noTime and huge or a.expirationTime
|
|
local compb = b.noTime and huge or b.expirationTime
|
|
return compa > compb
|
|
end
|
|
|
|
local function Update(self, event, unit)
|
|
if self.unit ~= unit then return end
|
|
|
|
local element = self.AuraBars
|
|
local helpOrHarm
|
|
local isFriend = UnitIsFriend("player", unit) == 1 and true or false
|
|
|
|
if element.friendlyAuraType and element.enemyAuraType then
|
|
if isFriend then
|
|
helpOrHarm = element.friendlyAuraType
|
|
else
|
|
helpOrHarm = element.enemyAuraType
|
|
end
|
|
else
|
|
helpOrHarm = isFriend and "HELPFUL" or "HARMFUL"
|
|
end
|
|
|
|
-- Create a table of auras to display
|
|
local auras = {}
|
|
local lastAuraIndex = 0
|
|
|
|
if element.forceShow then
|
|
local spellID = 47540
|
|
local name, rank, icon = GetSpellInfo(spellID)
|
|
local count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate = 5, "Magic", 0, 0, "player", nil, nil
|
|
|
|
for i = 1, element.maxBars do
|
|
lastAuraIndex = lastAuraIndex + 1
|
|
auras[lastAuraIndex] = {}
|
|
auras[lastAuraIndex].spellID = spellID
|
|
auras[lastAuraIndex].name = name
|
|
auras[lastAuraIndex].rank = rank
|
|
auras[lastAuraIndex].icon = icon
|
|
auras[lastAuraIndex].count = count
|
|
auras[lastAuraIndex].debuffType = debuffType
|
|
auras[lastAuraIndex].duration = duration
|
|
auras[lastAuraIndex].expirationTime = expirationTime
|
|
auras[lastAuraIndex].unitCaster = unitCaster
|
|
auras[lastAuraIndex].isStealable = isStealable
|
|
auras[lastAuraIndex].noTime = (duration == 0 and expirationTime == 0)
|
|
auras[lastAuraIndex].filter = helpOrHarm
|
|
auras[lastAuraIndex].shouldConsolidate = shouldConsolidate
|
|
end
|
|
else
|
|
local i = 0
|
|
while lastAuraIndex <= element.maxBars do
|
|
i = i + 1
|
|
|
|
local name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellID = UnitAura(unit, i, helpOrHarm)
|
|
if not name then break end
|
|
|
|
if (element.filter or DefaultFilter)(self, unit, name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellID) then
|
|
lastAuraIndex = lastAuraIndex + 1
|
|
auras[lastAuraIndex] = {}
|
|
auras[lastAuraIndex].spellID = spellID
|
|
auras[lastAuraIndex].name = name
|
|
auras[lastAuraIndex].rank = rank
|
|
auras[lastAuraIndex].icon = icon
|
|
auras[lastAuraIndex].count = count
|
|
auras[lastAuraIndex].debuffType = debuffType
|
|
auras[lastAuraIndex].duration = duration
|
|
auras[lastAuraIndex].expirationTime = expirationTime
|
|
auras[lastAuraIndex].unitCaster = unitCaster
|
|
auras[lastAuraIndex].isStealable = isStealable
|
|
auras[lastAuraIndex].noTime = (duration == 0 and expirationTime == 0)
|
|
auras[lastAuraIndex].filter = helpOrHarm
|
|
auras[lastAuraIndex].shouldConsolidate = shouldConsolidate
|
|
end
|
|
end
|
|
end
|
|
|
|
if element.sort and not element.forceShow then
|
|
tsort(auras, type(element.sort) == "function" and element.sort or sortByTime)
|
|
end
|
|
|
|
-- Show and configure bars for buffs/debuffs.
|
|
local bars = element.bars
|
|
if lastAuraIndex == 0 then
|
|
element:Height(1)
|
|
end
|
|
|
|
local currentTime = GetTime()
|
|
|
|
for i = 1, lastAuraIndex do
|
|
if element:GetWidth() == 0 then break end
|
|
|
|
local aura = auras[i]
|
|
local frame = bars[i]
|
|
|
|
if not frame then
|
|
frame = CreateAuraBar(self, i == 1 and element or bars[i - 1])
|
|
bars[i] = frame
|
|
end
|
|
|
|
if i == lastAuraIndex then
|
|
if element.down then
|
|
element:Height(element:GetTop() - frame:GetBottom())
|
|
elseif frame:GetTop() and element:GetBottom() then
|
|
element:Height(frame:GetTop() - element:GetBottom())
|
|
else
|
|
element:Height(20)
|
|
end
|
|
end
|
|
|
|
local bar = frame.statusBar
|
|
frame.index = i
|
|
|
|
-- Backup the details of the aura onto the bar, so the OnUpdate function can use it
|
|
bar.aura = aura
|
|
|
|
-- Configure
|
|
if bar.aura.noTime then
|
|
bar:SetMinMaxValues(0, 1)
|
|
bar:SetValue(1)
|
|
else
|
|
if element.scaleTime and element.scaleTime > 0 then
|
|
local maxValue = min(element.scaleTime, bar.aura.duration)
|
|
bar:SetMinMaxValues(0, element.scaleTime)
|
|
bar:Width((maxValue / element.scaleTime) * ((element.auraBarWidth or element:GetWidth()) - (bar:GetHeight() + (element.gap or 0)))) -- icon size + gap
|
|
else
|
|
bar:SetMinMaxValues(0, bar.aura.duration)
|
|
end
|
|
|
|
bar:SetValue(bar.aura.expirationTime - currentTime)
|
|
end
|
|
|
|
bar.icon:SetTexture(bar.aura.icon)
|
|
|
|
bar.spellname:SetText(bar.aura.count > 1 and format("%s [%d]", bar.aura.name, bar.aura.count) or bar.aura.name)
|
|
bar.spelltime:SetText(not bar.noTime and formatTime(bar.aura.expirationTime - currentTime))
|
|
|
|
-- Colour bars
|
|
local r, g, b
|
|
|
|
if helpOrHarm == "HARMFUL" then
|
|
local debuffType = bar.aura.debuffType and bar.aura.debuffType or "none"
|
|
|
|
if element.debuffColor then
|
|
r, g, b = unpack(element.debuffColor)
|
|
elseif debuffType == "none" and element.defaultDebuffColor then
|
|
r, g, b = unpack(element.defaultDebuffColor)
|
|
else
|
|
r, g, b = DebuffTypeColor[debuffType].r, DebuffTypeColor[debuffType].g, DebuffTypeColor[debuffType].b
|
|
end
|
|
elseif element.buffColor then
|
|
r, g, b = unpack(element.buffColor)
|
|
else
|
|
-- buffs default
|
|
r, g, b = .2, .6, 1
|
|
end
|
|
|
|
bar:SetStatusBarColor(r, g, b)
|
|
frame:Show()
|
|
end
|
|
|
|
-- Hide unused bars
|
|
for i = lastAuraIndex + 1, #bars do
|
|
bars[i]:Hide()
|
|
end
|
|
|
|
if element.PostUpdate then
|
|
element:PostUpdate(event, unit)
|
|
end
|
|
end
|
|
|
|
local function Enable(self)
|
|
local element = self.AuraBars
|
|
|
|
if element then
|
|
self:RegisterEvent("UNIT_AURA", Update)
|
|
element.SetAnchors = SetAnchors
|
|
element.maxBars = element.maxBars or 40
|
|
element.bars = element.bars or {}
|
|
element:Height(1)
|
|
element:SetScript("OnUpdate", UpdateBars)
|
|
|
|
return true
|
|
end
|
|
end
|
|
|
|
local function Disable(self)
|
|
local element = self.AuraBars
|
|
|
|
if element then
|
|
element:SetScript("OnUpdate", nil)
|
|
self:UnregisterEvent("UNIT_AURA", Update)
|
|
end
|
|
end
|
|
|
|
oUF:AddElement("AuraBars", Update, Enable, Disable) |