local ns = oUF local oUF = ns.oUF assert(oUF, "oUF_AuraBars was unable to locate oUF install.") local format = string.format local floor, huge, min, mod = math.floor, math.huge, math.min, math.mod local tsort, getn = table.sort, table.getn local tremove = table.remove local random = math.random local CreateFrame = CreateFrame 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", mod(s/60,60), mod(s,60)) elseif s < DAY then return format("%dh %dm", s/(60*60), mod(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 index = 1, getn(bars) do local frame = bars[index] local anchor = frame.anchor frame:SetHeight(self.auraBarHeight or 20) frame.statusBar.iconHolder:SetWidth(frame:GetHeight()) frame.statusBar.iconHolder:SetHeight(frame:GetHeight()) frame:SetWidth((self.auraBarWidth or self:GetWidth()) - (frame:GetHeight() + (self.gap or 0))) frame:ClearAllPoints() if self.down == true 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(oUF, anchor) local auraBarParent = oUF.AuraBars local frame = CreateFrame("Frame", nil, auraBarParent) frame:SetHeight(auraBarParent.auraBarHeight or 20) frame:SetWidth((auraBarParent.auraBarWidth or auraBarParent:GetWidth()) - (frame:GetHeight() + (auraBarParent.gap or 0))) frame.anchor = anchor -- the main bar local statusBar = CreateFrame("StatusBar", nil, frame) statusBar:SetStatusBarTexture(auraBarParent.auraBarTexture or [[Interface\TargetingFrame\UI-StatusBar]]) statusBar:SetAlpha(auraBarParent.fgalpha or 1) statusBar:SetAllPoints(frame) frame.statusBar = statusBar if auraBarParent.down == true then if auraBarParent == anchor then -- Root frame so indent for icon frame:SetPoint("TOPLEFT", anchor, "TOPLEFT", (frame:GetHeight() + (auraBarParent.gap or 0) ), -1) else frame:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", 0, (-auraBarParent.spacing or 0)) end else if auraBarParent == anchor then -- Root frame so indent for icon frame:SetPoint("BOTTOMLEFT", anchor, "BOTTOMLEFT", (frame:GetHeight() + (auraBarParent.gap or 0)), 1) else frame:SetPoint("BOTTOMLEFT", anchor, "TOPLEFT", 0, (auraBarParent.spacing or 0)) end end local spark = statusBar:CreateTexture(nil, "OVERLAY", nil) spark:SetTexture([[Interface\CastingBar\UI-CastingBar-Spark]]) spark:SetWidth(12) spark:SetBlendMode("ADD") spark:SetPoint("CENTER", statusBar:GetStatusBarTexture(), "RIGHT") statusBar.spark = spark statusBar.iconHolder = CreateFrame("Button", nil, statusBar) statusBar.iconHolder:SetHeight(frame:GetHeight()) statusBar.iconHolder:SetWidth(frame:GetHeight()) statusBar.iconHolder:SetPoint("BOTTOMRIGHT", frame, "BOTTOMLEFT", -auraBarParent.gap, 0) statusBar.iconHolder.__unit = oUF.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(.07, .93, .07, .93) statusBar.icon:SetAllPoints() statusBar.spelltime = statusBar:CreateFontString(nil, "ARTWORK") if auraBarParent.spellTimeObject then statusBar.spelltime:SetFontObject(auraBarParent.spellTimeObject) else statusBar.spelltime:SetFont(auraBarParent.spellTimeFont or [[Fonts\FRIZQT__.TTF]], auraBarParent.spellTimeSize or 10) end statusBar.spelltime:SetTextColor(1, 1, 1) statusBar.spelltime:SetJustifyH("RIGHT") statusBar.spelltime:SetJustifyV("CENTER") statusBar.spelltime:SetPoint("RIGHT", 0, 0) statusBar.spellname = statusBar:CreateFontString(nil, "ARTWORK") if auraBarParent.spellNameObject then statusBar.spellname:SetFontObject(auraBarParent.spellNameObject) else statusBar.spellname:SetFont(auraBarParent.spellNameFont or [[Fonts\FRIZQT__.TTF]], auraBarParent.spellNameSize or 10) end statusBar.spellname:SetTextColor(1, 1, 1) statusBar.spellname:SetJustifyH("LEFT") statusBar.spellname:SetJustifyV("CENTER") statusBar.spellname:SetPoint("LEFT", 0, 0) statusBar.spellname:SetPoint("RIGHT", statusBar.spelltime, "LEFT") if auraBarParent.PostCreateBar then auraBarParent.PostCreateBar(frame) end return frame end local function UpdateBars(auraBars) local bars = auraBars.bars local timenow = GetTime() for index = 1, getn(bars) do local frame = bars[index] local bar = frame.statusBar if not frame:IsVisible() then break end if bar.aura.noTime then bar.spelltime:SetText() bar.spark:Hide() else local timeleft = bar.aura.expirationTime - timenow bar:SetValue(timeleft) bar.spelltime:SetText(FormatTime(timeleft)) if auraBars.spark == true then if (auraBars.scaleTime and ((auraBars.scaleTime <= 0) or (auraBars.scaleTime > 0 and timeleft < auraBars.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 sort = function(a, b) local compa, compb = a.noTime and huge or a.expirationTime, 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 auraBars = self.AuraBars local helpOrHarm local isFriend = UnitIsFriend("player", unit) == 1 and true or false local both = false if auraBars.friendlyAuraType and auraBars.enemyAuraType then if isFriend then helpOrHarm = auraBars.friendlyAuraType else helpOrHarm = auraBars.enemyAuraType end else helpOrHarm = isFriend and "HELPFUL" or "HARMFUL" end if helpOrHarm == "BOTH" then both = true helpOrHarm = "HELPFUL" end -- Create a table of auras to display local auras = {} local lastAuraIndex = 0 local counter = 0 if auraBars.forceShow then for index = 1, auraBars.maxBars do local spellID = 47540 local name, rank, icon = GetSpellInfo(spellID) local count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate = 5, "Magic", 0, 0, "player", nil, nil 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 for index = 1, 40 do local name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellID if not both then name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellID = UnitAura(unit, index, helpOrHarm) if not name then break end else name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellID = UnitAura(unit, index, "HELPFUL") if not name then name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellID = UnitAura(unit, index, "HARMFUL") if not name then break end end end if (auraBars.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 auraBars.sort and not auraBars.forceShow then tsort(auras, type(auraBars.sort) == "function" and auraBars.sort or sort) end for i=1, getn(auras) do if i > auraBars.maxBars then tremove(auras, i) else lastAuraIndex = i end end -- Show and configure bars for buffs/debuffs. local bars = auraBars.bars if lastAuraIndex == 0 then self.AuraBars:SetHeight(1) end for index = 1 , lastAuraIndex do -- if auraBars:GetWidth() == 0 then break end local aura = auras[index] local frame = bars[index] if not frame then frame = CreateAuraBar(self, index == 1 and auraBars or bars[index - 1]) bars[index] = frame end if index == lastAuraIndex then if self.AuraBars.down then self.AuraBars:SetHeight(self.AuraBars:GetTop() - frame:GetBottom()) elseif frame:GetTop() and self.AuraBars:GetBottom() then self.AuraBars:SetHeight(frame:GetTop() - self.AuraBars:GetBottom()) else self.AuraBars:SetHeight(20) end end local bar = frame.statusBar frame.index = index -- 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 auraBars.scaleTime and auraBars.scaleTime > 0 then local maxvalue = min(auraBars.scaleTime, bar.aura.duration) bar:SetMinMaxValues(0, auraBars.scaleTime) bar:SetWidth( ( maxvalue / auraBars.scaleTime ) * ( ( auraBars.auraBarWidth or auraBars:GetWidth() ) - ( bar:GetHeight() + (auraBars.gap or 0) ) ) ) -- icon size + gap else bar:SetMinMaxValues(0, bar.aura.duration) end bar:SetValue(bar.aura.expirationTime - GetTime()) 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-GetTime())) -- Colour bars local r, g, b = .2, .6, 1 -- Colour for buffs if auraBars.buffColor then r, g, b = unpack(auraBars.buffColor) end if helpOrHarm == "HARMFUL" then local debuffType = bar.aura.debuffType and bar.aura.debuffType or "none" r, g, b = DebuffTypeColor[debuffType].r, DebuffTypeColor[debuffType].g, DebuffTypeColor[debuffType].b if auraBars.debuffColor then r, g, b = unpack(auraBars.debuffColor) else if debuffType == "none" and auraBars.defaultDebuffColor then r, g, b = unpack(auraBars.defaultDebuffColor) end end end bar:SetStatusBarColor(r, g, b) frame:Show() end -- Hide unused bars. for index = lastAuraIndex + 1, getn(bars) do bars[index]:Hide() end if auraBars.PostUpdate then auraBars:PostUpdate(event, unit) end end local function Enable(self) if self.AuraBars then self:RegisterEvent("PLAYER_AURAS_CHANGED", Update) self.AuraBars:SetHeight(1) self.AuraBars.bars = self.AuraBars.bars or {} self.AuraBars.SetAnchors = SetAnchors self.AuraBars:SetScript("OnUpdate", function() UpdateBars(self.AuraBars) end) self.AuraBars.maxBars = self.AuraBars.maxBars or 40 return true end end local function Disable(self) local auraFrame = self.AuraBars if auraFrame then self:UnregisterEvent("PLAYER_AURAS_CHANGED", Update) auraFrame:SetScript("OnUpdate", nil) end end oUF:AddElement("AuraBars", Update, Enable, Disable)