PHP код:
local aura = CreateFrame("frame")
aura.BuffsPerRow = 12 -- количество аур в ряде
aura.Size = 40 -- размер аур
aura.Spacing = aura.Size + 5 -- промежутки между аурами , менять последнюю цифру
aura.BuffFilter = "HELPFUL"
aura.DebuffFilter = "HARMFUL"
aura.Sort = "TIME" -- INDEX или NAME или TIME
aura.BPoint = {"TOPRIGHT", Minimap, "TOPLEFT", -(aura.Spacing-30), 1.5} -- позиции бафов
aura.DPoint ={"BOTTOMRIGHT", Minimap, "BOTTOMLEFT", -(aura.Spacing-30), 1.5} -- позиции дебафов
aura.SetVertical = false
aura:SetScript("OnEvent", function(self, event, ...)self[event](self, ...)end)
local Animation = true
local Flash = true
aura.Headers = {}
aura.Filter = false
aura.FlashTimer = 30
local sortingTable = {}
local groupingTable = {}
local function Kill(self)
if self.UnregisterAllEvents then
self:UnregisterAllEvents()
end
self.Show = function() end
self:Hide()
end
function aura:DisableBlizzardAuras()
Kill(BuffFrame)
Kill(TemporaryEnchantFrame)
Kill(ConsolidatedBuffs)
InterfaceOptionsFrameCategoriesButton12:SetScale(0.00001)
InterfaceOptionsFrameCategoriesButton12:SetAlpha(0)
end
function aura:StartOrStopFlash(timeleft)
if (timeleft < aura.FlashTimer) then
if (not self:IsPlaying()) then
self:Play()
end
elseif self:IsPlaying() then
self:Stop()
end
end
local function FormatTime(seconds)
local Day, Hour, Minute = 86400, 3600, 60
if (seconds >= Day) then
return format("%dd", ceil(seconds / Day))
elseif (seconds >= Hour) then
return format("%dh", ceil(seconds / Hour))
elseif (seconds >= Minute) then
return format("%dm", ceil(seconds / Minute))
elseif (seconds >= Minute / 12) then
return floor(seconds)
end
return format("%.1f", seconds)
end
function aura:OnUpdate(elapsed)
local TimeLeft = self.TimeLeft - elapsed
self.TimeLeft = TimeLeft
if (TimeLeft <= 0) then
self.TimeLeft = nil
self.Duration:SetText("")
return self:SetScript("OnUpdate", nil)
else
local Text = FormatTime(TimeLeft)
if (TimeLeft < 60.5) then
if Flash then
aura.StartOrStopFlash(self.Animation, TimeLeft)
end
if (TimeLeft < 5) then
self.Duration:SetTextColor(1, 0.08, 0.08)
else
self.Duration:SetTextColor(1, 0.65, 0)
end
else
if (self.Animation and self.Animation:IsPlaying()) then
self.Animation:Stop()
end
self.Duration:SetTextColor(0.9, 0.9, 0.9)
end
self.Duration:SetText(Text)
end
end
local UpdateTooltip = function(self)
GameTooltip:SetUnitAura(self:GetParent().unit, self:GetID(), self:GetParent().filter)
end
local OnEnter = function(self)
if(not self:IsVisible()) then return end
GameTooltip:SetOwner(self, "ANCHOR_BOTTOMLEFT", -5, -5)
self:UpdateTooltip()
end
local OnLeave = function()
GameTooltip:Hide()
end
local OnClick = function(self)
CancelUnitBuff(self:GetParent().unit, self:GetID(), self:GetParent().filter)
end
function aura:Skin()
local Proxy = self.IsProxy
self:RegisterForClicks("RightButtonUp")
self:SetSize(aura.Size, aura.Size)
local Icon = self:CreateTexture(nil, "BORDER")
Icon:SetPoint("TOPLEFT", self, 2, -2)
Icon:SetPoint("BOTTOMRIGHT", self, -2, 2)
local Count = self:CreateFontString(nil, "OVERLAY", "ChatFontNormal")
Count:SetShadowColor(0, 0, 0)
Count:SetShadowOffset(1.25, -1.25)
Count:SetPoint("BOTTOMRIGHT", self, -1, 1)
local Duration = self:CreateFontString(nil, "OVERLAY", "ChatFontNormal")
Duration:SetShadowColor(0, 0, 0)
Duration:SetShadowOffset(1.25, -1.25)
if aura.SetVertical then
if (C["aura"].Position == "TOPLEFT") then
Duration:SetPoint("LEFT", self, "RIGHT", 2, 0)
else
Duration:SetPoint("RIGHT", self, "LEFT", -2, 0)
end
else
Duration:SetPoint("TOP", self, "BOTTOM", 0, -2)
end
if Flash then
local Animation = self:CreateAnimationGroup()
Animation:SetLooping("BOUNCE")
local FadeOut = Animation:CreateAnimation("Alpha")
FadeOut:SetChange(-0.5)
FadeOut:SetDuration(0.6)
FadeOut:SetSmoothing("IN_OUT")
self.Animation = Animation
end
self.Duration = Duration
self.Filter = self:GetParent().filter
self.Icon = Icon
self.Count = Count
self.UpdateTooltip = UpdateTooltip
self:SetScript("OnEnter", OnEnter)
self:SetScript("OnLeave", OnLeave)
self:SetScript("OnClick", OnClick)
end
local buttons = {}
local function configureAuras(header, auraTable)
local point = header.point or "TOPRIGHT"
local xOffset = header.xOffset or 0
local yOffset = header.yOffset or 0
local wrapXOffset = header.wrapXOffset or 0
local wrapYOffset = header.wrapYOffset or 0
local wrapAfter = header.wrapAfter
if ( wrapAfter == 0 ) then wrapAfter = nil end
local maxWraps = header.maxWraps
if ( maxWraps == 0 ) then maxWraps = nil end
local minWidth = header.minWidth or 0
local minHeight = header.minHeight or 0
local name = header:GetName()
wipe(buttons)
for i=1, #auraTable do
local button = select(i, header:GetChildren())
if(button) then
button:ClearAllPoints()
else
button = CreateFrame("Button", name and name.."AuraButton"..i, header)
aura.Skin(button)
end
local buffInfo = auraTable[i]
button:SetID(buffInfo.index)
button.index = buffInfo.index
button.filter = buffInfo.filter
if(buffInfo.duration > 0 and buffInfo.expires) then
local TimeLeft = buffInfo.expires - GetTime()
if(not button.TimeLeft) then
button.TimeLeft = TimeLeft
button:SetScript("OnUpdate", aura.OnUpdate)
else
button.TimeLeft = TimeLeft
end
button.Dur = buffInfo.duration
if Flash then
aura.StartOrStopFlash(button.Animation, TimeLeft)
end
button.Duration:SetText(button.Dur)
else
if Flash then
button.Animation:Stop()
end
button.TimeLeft = nil
button.Duration:SetText("")
button:SetScript("OnUpdate", nil)
end
if(buffInfo.count > 1) then
button.Count:SetText(buffInfo.count)
else
button.Count:SetText("")
end
if(buffInfo.filter == "HARMFUL") then
local Color = DebuffTypeColor[buffInfo.dtype or ""]
button:SetBackdropBorderColor(Color.r * 0.6, Color.g * 0.6, Color.b * 0.6)
end
button.Icon:SetTexture(buffInfo.icon)
buttons[i] = button
end
local display = #buttons
if(wrapAfter and maxWraps) then
display = min(display, wrapAfter * maxWraps)
end
local left, right, top, bottom = math.huge, -math.huge, -math.huge, math.huge
for index=1,display do
local button = buttons[index]
local wrapAfter = wrapAfter or index
local tick, cycle = floor((index - 1) % wrapAfter), floor((index - 1) / wrapAfter)
button:SetPoint(point, header, cycle * wrapXOffset + tick * xOffset, cycle * wrapYOffset + tick * yOffset)
button:Show()
left = min(left, button:GetLeft() or math.huge)
right = max(right, button:GetRight() or -math.huge)
top = max(top, button:GetTop() or -math.huge)
bottom = min(bottom, button:GetBottom() or math.huge)
end
local deadIndex = #(auraTable) + 1
local button = select(deadIndex, header:GetChildren())
while(button) do
button:Hide()
deadIndex = deadIndex + 1
button = select(deadIndex, header:GetChildren())
end
if(display >= 1) then
header:SetWidth(max(right - left, minWidth))
header:SetHeight(max(top - bottom, minHeight))
else
header:SetWidth(minWidth)
header:SetHeight(minHeight)
end
end
local tremove = table.remove
local function stripRAID(filter)
return filter and tostring(filter):upper():gsub("RAID", ""):gsub("|+", "|"):match("^|?(.+[^|])|?$")
end
local freshTable
local releaseTable
do
local tableReserve = {}
freshTable = function ()
local t = next(tableReserve) or {}
tableReserve[t] = nil
return t
end
releaseTable = function (t)
tableReserve[t] = wipe(t)
end
end
local sorters = {}
local function sortFactory(key, separateOwn, reverse)
if(separateOwn ~= 0) then
if(reverse) then
return function (a, b)
if(groupingTable[a.filter] == groupingTable[b.filter]) then
local ownA, ownB = a.caster == "player", b.caster == "player"
if(ownA ~= ownB) then
return ownA == (separateOwn > 0)
end
return a[key] > b[key]
else
return groupingTable[a.filter] < groupingTable[b.filter]
end
end
else
return function (a, b)
if(groupingTable[a.filter] == groupingTable[b.filter]) then
local ownA, ownB = a.caster == "player", b.caster == "player"
if(ownA ~= ownB) then
return ownA == (separateOwn > 0)
end
return a[key] < b[key]
else
return groupingTable[a.filter] < groupingTable[b.filter]
end
end
end
else
if(reverse) then
return function (a, b)
if(groupingTable[a.filter] == groupingTable[b.filter]) then
return a[key] > b[key]
else
return groupingTable[a.filter] < groupingTable[b.filter]
end
end
else
return function (a, b)
if(groupingTable[a.filter] == groupingTable[b.filter]) then
return a[key] < b[key]
else
return groupingTable[a.filter] < groupingTable[b.filter]
end
end
end
end
end
for i, key in ipairs{"index", "name", "expires"} do
local label = key:upper()
sorters[label] = {}
for bool in pairs{[true] = true, [false] = false} do
sorters[label][bool] = {}
for sep=-1,1 do
sorters[label][bool][sep] = sortFactory(key, sep, bool)
end
end
end
sorters.TIME = sorters.EXPIRES
function aura:UpdateHeaders(func, ...)
for _, Header in pairs(aura.Headers) do
local Child = Header:GetChildren()
local i = 0
while Child do
aura:UpdateHeader(Header)
i = i + 1
Child = select(i, Header:GetChildren())
if func then
func(Child, ...)
end
end
end
end
function aura:CreateHeaders()
local Headers = aura.Headers
for i = 1, 2 do
local Header = CreateFrame("Frame", nil, UIParent)
Header:SetClampedToScreen(true)
Header.unit = "player"
Header.minHeight = 10
Header.wrapAfter = aura.BuffsPerRow
Header.wrapYOffset = -55
Header.xOffset = -aura.Spacing
Header.minWidth = aura.BuffsPerRow * 35
Header:SetSize(aura.Size, aura.Size)
Header:RegisterEvent("UNIT_AURA")
Header:SetScript("OnEvent", function(self, event, ...)
if(self:IsVisible()) then
local unit = self.unit
if(event == "UNIT_AURA" and ... == unit) then
aura:UpdateHeader(self)
end
end
end)
table.insert(Headers, Header)
end
local Buffs = Headers[1]
local Debuffs = Headers[2]
Buffs:SetPoint(unpack(aura.BPoint))
Buffs.filter = aura.BuffFilter
aura:UpdateHeader(Buffs)
Buffs:Show()
Debuffs:SetPoint(unpack(aura.DPoint))
Debuffs.filter = aura.DebuffFilter
aura:UpdateHeader(Debuffs)
Debuffs:Show()
end
function aura:UpdateHeader(self)
local filter = self.filter
local groupBy = self.groupBy
local unit = self.unit
local sortDirection = "-"
local separateOwn = 0
if ( separateOwn > 0 ) then
separateOwn = 1
elseif (separateOwn < 0 ) then
separateOwn = -1
end
local sortMethod = (sorters[tostring(self.sortMethod):upper()] or sorters["INDEX"])[sortDirection == "-"][separateOwn]
local time = GetTime()
wipe(sortingTable)
wipe(groupingTable)
if(groupBy) then
local i = 1
for subFilter in groupBy:gmatch("[^,]+") do
if(filter) then
subFilter = stripRAID(filter.."|"..subFilter)
else
subFilter = stripRAID(subFilter)
end
groupingTable[subFilter], groupingTable[i] = i, subFilter
i = i + 1
end
else
filter = stripRAID(filter)
groupingTable[filter], groupingTable[1] = 1, filter
end
for filterIndex, fullFilter in ipairs(groupingTable) do
local i = 1
repeat
local aura, _, duration = freshTable()
aura.name, _, aura.icon, aura.count, aura.dtype, duration, expires, aura.caster, _, aura.shouldConsolidate, _ = UnitAura(unit, i, fullFilter)
if(aura.name) then
aura.filter = fullFilter
aura.index = i
aura.duration = duration
if duration<=0 then
aura.expires = 1e6
else
aura.expires = expires
end
local targetList = sortingTable
tinsert(targetList, aura)
else
releaseTable(aura)
end
i = i + 1
until(not aura.name)
end
table.sort(sortingTable, sortMethod)
configureAuras(self, sortingTable)
while(sortingTable[1]) do
releaseTable(tremove(sortingTable))
end
end
local UpdateSorting = function(value)
aura.Headers[1].sortMethod = aura.Sort
aura.Headers[2].sortMethod = aura.Sort
end
function aura:PLAYER_LOGIN()
self:RegisterEvent("PLAYER_ENTERING_WORLD")
self:SetScript("OnEvent", function(self)
self:UpdateHeaders()
end)
self:DisableBlizzardAuras()
self:CreateHeaders()
UpdateSorting()
end
aura:RegisterEvent("PLAYER_LOGIN")