MxW_Addon/Libraries/oUF/elements/aura.lua

526 lines
15 KiB
Lua

--[[ Element: Auras
Handles creation and updating of aura icons.
Widget
Auras - A Frame to hold icons representing both buffs and debuffs.
Buffs - A Frame to hold icons representing buffs.
Debuffs - A Frame to hold icons representing debuffs.
Options
.disableCooldown - Disables the cooldown spiral. Defaults to false.
.size - Aura icon size. Defaults to 16.
.onlyShowPlayer - Only show auras created by player/vehicle.
.showStealableBuffs - Display the stealable texture on buffs that can be
stolen.
.spacing - Spacing between each icon. Defaults to 0.
.['spacing-x'] - Horizontal spacing between each icon. Takes priority over
`spacing`.
.['spacing-y'] - Vertical spacing between each icon. Takes priority over
`spacing`.
.['growth-x'] - Horizontal growth direction. Defaults to RIGHT.
.['growth-y'] - Vertical growth direction. Defaults to UP.
.initialAnchor - Anchor point for the icons. Defaults to BOTTOMLEFT.
.filter - Custom filter list for auras to display. Defaults to
HELPFUL on buffs and HARMFUL on debuffs.
Options Auras
.numBuffs - The maximum number of buffs to display. Defaults to 32.
.numDebuffs - The maximum number of debuffs to display. Defaults to 40.
.gap - Controls the creation of an invisible icon between buffs and
debuffs. Defaults to false.
.buffFilter - Custom filter list for buffs to display. Takes priority over
`filter`.
.debuffFilter - Custom filter list for debuffs to display. Takes priority over
`filter`.
Options Buffs
.num - Number of buffs to display. Defaults to 32.
Options Debuffs
.num - Number of debuffs to display. Defaults to 40.
Examples
-- Position and size
local Buffs = CreateFrame("Frame", nil, self)
Buffs:SetPoint("RIGHT", self, "LEFT")
Buffs:SetSize(16 * 2, 16 * 16)
-- Register with oUF
self.Buffs = Buffs
Hooks and Callbacks
]]
local parent, ns = ...
local oUF = ns.oUF
local VISIBLE = 1
local HIDDEN = 0
local UpdateTooltip = function(self)
GameTooltip:SetUnitAura(self:GetParent().__owner.unit, self:GetID(), self.filter)
end
local OnEnter = function(self)
if(not self:IsVisible()) then return end
GameTooltip:SetOwner(self, "ANCHOR_BOTTOMRIGHT")
self:UpdateTooltip()
end
local OnLeave = function()
GameTooltip:Hide()
end
local createAuraIcon = function(icons, index)
local button = CreateFrame("Button", icons:GetDebugName().."Button"..index, icons)
button:RegisterForClicks'RightButtonUp'
local cd = CreateFrame("Cooldown", "$parentCooldown", button, "CooldownFrameTemplate")
cd:SetAllPoints(button)
local icon = button:CreateTexture(nil, "BORDER")
icon:SetAllPoints(button)
local count = button:CreateFontString(nil, "OVERLAY")
count:SetFontObject(NumberFontNormal)
count:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", -1, 0)
local overlay = button:CreateTexture(nil, "OVERLAY")
overlay:SetTexture"Interface\\Buttons\\UI-Debuff-Overlays"
overlay:SetAllPoints(button)
overlay:SetTexCoord(.296875, .5703125, 0, .515625)
button.overlay = overlay
local stealable = button:CreateTexture(nil, 'OVERLAY')
stealable:SetTexture[[Interface\TargetingFrame\UI-TargetingFrame-Stealable]]
stealable:SetPoint('TOPLEFT', -3, 3)
stealable:SetPoint('BOTTOMRIGHT', 3, -3)
stealable:SetBlendMode'ADD'
button.stealable = stealable
button.UpdateTooltip = UpdateTooltip
button:SetScript("OnEnter", OnEnter)
button:SetScript("OnLeave", OnLeave)
button.icon = icon
button.count = count
button.cd = cd
--[[ :PostCreateIcon(button)
Callback which is called after a new aura icon button has been created.
Arguments
button - The newly created aura icon button.
]]
if(icons.PostCreateIcon) then icons:PostCreateIcon(button) end
return button
end
local customFilter = function(icons, unit, icon, name)
if((icons.onlyShowPlayer and icon.isPlayer) or (not icons.onlyShowPlayer and name)) then
return true
end
end
local updateIcon = function(unit, icons, index, offset, filter, isDebuff, visible)
local name, rank, texture, count, dispelType, duration, expiration, caster, isStealable,
nameplateShowSelf, spellID, canApply, isBossDebuff, casterIsPlayer, nameplateShowAll,
timeMod, effect1, effect2, effect3 = UnitAura(unit, index, filter)
if icons.forceShow then
spellID = 47540
name, rank, texture = GetSpellInfo(spellID)
count, dispelType, duration, expiration, caster, isStealable, nameplateShowSelf, canApplyAura, isBossDebuff = 5, 'Magic', 0, 60, 'player', nil, nil, nil, nil
end
if(name) then
local n = visible + offset + 1
local icon = icons[n]
if(not icon) then
--[[ :CreateIcon(index)
A function which creates the aura icon for a given index.
Arguments
index - The offset the icon should be created at.
Returns
A button used to represent aura icons.
]]
local prev = icons.createdIcons
icon = (icons.CreateIcon or createAuraIcon) (icons, n)
-- XXX: Update the counters if the layout doesn't.
if(prev == icons.createdIcons) then
table.insert(icons, icon)
icons.createdIcons = icons.createdIcons + 1
end
end
local isPlayer
if(caster == 'player' or caster == 'vehicle') then
isPlayer = true
end
icon.owner = caster
icon.filter = filter
icon.isDebuff = isDebuff
icon.isPlayer = isPlayer
--[[ :CustomFilter(unit, icon, ...)
Defines a custom filter which controls if the aura icon should be shown
or not.
Arguments
self - The widget that holds the aura icon.
unit - The unit that has the aura.
icon - The button displaying the aura.
... - The return values from
[UnitAura](http://wowprogramming.com/docs/api/UnitAura).
Returns
A boolean value telling the aura element if it should be show the icon
or not.
]]
local show = true
if not icons.forceShow then
show = (icons.CustomFilter or customFilter) (icons, unit, icon, name, rank, texture,
count, dispelType, duration, expiration, caster, isStealable, nameplateShowSelf, spellID,
canApply, isBossDebuff, casterIsPlayer, nameplateShowAll,timeMod, effect1, effect2, effect3)
end
if(show) then
-- We might want to consider delaying the creation of an actual cooldown
-- object to this point, but I think that will just make things needlessly
-- complicated.
local cd = icon.cd
if(cd and not icons.disableCooldown) then
if(duration and duration > 0) then
cd:SetCooldown(expiration - duration, duration)
cd:Show()
else
cd:Hide()
end
end
if((isDebuff and icons.showDebuffType) or (not isDebuff and icons.showBuffType) or icons.showType) then
local color = DebuffTypeColor[dispelType] or DebuffTypeColor.none
icon.overlay:SetVertexColor(color.r, color.g, color.b)
icon.overlay:Show()
else
icon.overlay:Hide()
end
local stealable = not isDebuff and isStealable
if(stealable and icons.showStealableBuffs and not UnitIsUnit('player', unit)) then
icon.stealable:Show()
else
icon.stealable:Hide()
end
icon.icon:SetTexture(texture)
icon.count:SetText((count > 1 and count))
local size = icons.size or 16
icon:SetSize(size, size)
icon:EnableMouse(true)
icon:SetID(index)
icon:Show()
--[[ :PostUpdateIcon(unit, icon, index, offest)
Callback which is called after the aura icon was updated.
Arguments
self - The widget that holds the aura icon.
unit - The unit that has the aura.
icon - The button that was updated.
index - The index of the aura.
offset - The offset the button was created at.
]]
if(icons.PostUpdateIcon) then
icons:PostUpdateIcon(unit, icon, index, n)
end
return VISIBLE
else
return HIDDEN
end
end
end
--[[ :SetPosition(from, to)
Function used to (re-)anchor aura icons. This function is only called when
new aura icons have been created or if :PreSetPosition is defined.
Arguments
self - The widget that holds the aura icons.
from - The aura icon before the new aura icon.
to - The current number of created icons.
]]
local SetPosition = function(icons, from, to)
local sizex = (icons.size or 16) + (icons['spacing-x'] or icons.spacing or 0)
local sizey = (icons.size or 16) + (icons['spacing-y'] or icons.spacing or 0)
local anchor = icons.initialAnchor or "BOTTOMLEFT"
local growthx = (icons["growth-x"] == "LEFT" and -1) or 1
local growthy = (icons["growth-y"] == "DOWN" and -1) or 1
local cols = math.floor(icons:GetWidth() / sizex + .5)
for i = from, to do
local button = icons[i]
-- Bail out if the to range is out of scope.
if(not button) then break end
local col = (i - 1) % cols
local row = math.floor((i - 1) / cols)
button:ClearAllPoints()
button:SetPoint(anchor, icons, anchor, col * sizex * growthx, row * sizey * growthy)
end
end
local filterIcons = function(unit, icons, filter, limit, isDebuff, offset, dontHide)
if(not offset) then offset = 0 end
local index = 1
local visible = 0
local hidden = 0
while(visible < limit) do
local result = updateIcon(unit, icons, index, offset, filter, isDebuff, visible)
if(not result) then
break
elseif(result == VISIBLE) then
visible = visible + 1
elseif(result == HIDDEN) then
hidden = hidden + 1
end
index = index + 1
end
if(not dontHide) then
for i = visible + offset + 1, #icons do
icons[i]:Hide()
end
end
return visible, hidden
end
local UpdateAuras = function(self, event, unit)
if(self.unit ~= unit) then return end
local auras = self.Auras
if(auras) then
if(auras.PreUpdate) then auras:PreUpdate(unit) end
local numBuffs = auras.numBuffs or 32
local numDebuffs = auras.numDebuffs or 40
local max = numBuffs + numDebuffs
local visibleBuffs, hiddenBuffs = filterIcons(unit, auras, auras.buffFilter or auras.filter or 'HELPFUL', numBuffs, nil, 0, true)
local hasGap
if(visibleBuffs ~= 0 and auras.gap) then
hasGap = true
visibleBuffs = visibleBuffs + 1
local icon = auras[visibleBuffs]
if(not icon) then
local prev = auras.createdIcons
icon = (auras.CreateIcon or createAuraIcon) (auras, visibleBuffs)
-- XXX: Update the counters if the layout doesn't.
if(prev == auras.createdIcons) then
table.insert(auras, icon)
auras.createdIcons = auras.createdIcons + 1
end
end
-- Prevent the icon from displaying anything.
if(icon.cd) then icon.cd:Hide() end
icon:EnableMouse(false)
icon.icon:SetTexture()
icon.overlay:Hide()
icon.stealable:Hide()
icon.count:SetText()
icon:Show()
--[[ :PostUpdateGapIcon(unit, icon, visibleBuffs)
Callback which is called after an invisible aura icon has been
created. This is only used by Auras when the `gap` option is enabled.
Arguments
self - The widget that holds the aura icon.
unit - The unit that has the aura icon.
icon - The invisible aura icon / gap.
visibleBuffs - The number of currently visible buffs.
]]
if(auras.PostUpdateGapIcon) then
auras:PostUpdateGapIcon(unit, icon, visibleBuffs)
end
end
local visibleDebuffs, hiddenDebuffs = filterIcons(unit, auras, auras.debuffFilter or auras.filter or 'HARMFUL', numDebuffs, true, visibleBuffs)
auras.visibleDebuffs = visibleDebuffs
if(hasGap and visibleDebuffs == 0) then
auras[visibleBuffs]:Hide()
visibleBuffs = visibleBuffs - 1
end
auras.visibleBuffs = visibleBuffs
auras.visibleAuras = auras.visibleBuffs + auras.visibleDebuffs
local fromRange, toRange
if(auras.PreSetPosition) then
fromRange, toRange = auras:PreSetPosition(max)
end
if(fromRange or auras.createdIcons > auras.anchoredIcons) then
(auras.SetPosition or SetPosition) (auras, fromRange or auras.anchoredIcons + 1, toRange or auras.createdIcons)
auras.anchoredIcons = auras.createdIcons
end
if(auras.PostUpdate) then auras:PostUpdate(unit) end
end
local buffs = self.Buffs
if(buffs) then
if(buffs.PreUpdate) then buffs:PreUpdate(unit) end
local numBuffs = buffs.num or 32
local visibleBuffs, hiddenBuffs = filterIcons(unit, buffs, buffs.filter or 'HELPFUL', numBuffs)
buffs.visibleBuffs = visibleBuffs
local fromRange, toRange
if(buffs.PreSetPosition) then
fromRange, toRange = buffs:PreSetPosition(numBuffs)
end
if(fromRange or buffs.createdIcons > buffs.anchoredIcons) then
(buffs.SetPosition or SetPosition) (buffs, fromRange or buffs.anchoredIcons + 1, toRange or buffs.createdIcons)
buffs.anchoredIcons = buffs.createdIcons
end
if(buffs.PostUpdate) then buffs:PostUpdate(unit) end
end
local debuffs = self.Debuffs
if(debuffs) then
if(debuffs.PreUpdate) then debuffs:PreUpdate(unit) end
local numDebuffs = debuffs.num or 40
local visibleDebuffs, hiddenDebuffs = filterIcons(unit, debuffs, debuffs.filter or 'HARMFUL', numDebuffs, true)
debuffs.visibleDebuffs = visibleDebuffs
local fromRange, toRange
if(debuffs.PreSetPosition) then
fromRange, toRange = debuffs:PreSetPosition(numDebuffs)
end
if(fromRange or debuffs.createdIcons > debuffs.anchoredIcons) then
(debuffs.SetPosition or SetPosition) (debuffs, fromRange or debuffs.anchoredIcons + 1, toRange or debuffs.createdIcons)
debuffs.anchoredIcons = debuffs.createdIcons
end
if(debuffs.PostUpdate) then debuffs:PostUpdate(unit) end
end
end
local Update = function(self, event, unit)
if(self.unit ~= unit) then return end
UpdateAuras(self, event, unit)
-- Assume no event means someone wants to re-anchor things. This is usually
-- done by UpdateAllElements and :ForceUpdate.
if(event == 'ForceUpdate' or not event) then
local buffs = self.Buffs
if(buffs) then
(buffs.SetPosition or SetPosition) (buffs, 1, buffs.createdIcons)
end
local debuffs = self.Debuffs
if(debuffs) then
(debuffs.SetPosition or SetPosition) (debuffs, 1, debuffs.createdIcons)
end
local auras = self.Auras
if(auras) then
(auras.SetPosition or SetPosition) (auras, 1, auras.createdIcons)
end
end
end
local ForceUpdate = function(element)
return Update(element.__owner, 'ForceUpdate', element.__owner.unit)
end
local Enable = function(self)
if(self.Buffs or self.Debuffs or self.Auras) then
self:RegisterEvent("UNIT_AURA", UpdateAuras)
local buffs = self.Buffs
if(buffs) then
buffs.__owner = self
buffs.ForceUpdate = ForceUpdate
buffs.createdIcons = 0
buffs.anchoredIcons = 0
end
local debuffs = self.Debuffs
if(debuffs) then
debuffs.__owner = self
debuffs.ForceUpdate = ForceUpdate
debuffs.createdIcons = 0
debuffs.anchoredIcons = 0
end
local auras = self.Auras
if(auras) then
auras.__owner = self
auras.ForceUpdate = ForceUpdate
auras.createdIcons = 0
auras.anchoredIcons = 0
end
return true
end
end
local Disable = function(self)
if(self.Buffs or self.Debuffs or self.Auras) then
self:UnregisterEvent("UNIT_AURA", UpdateAuras)
end
end
oUF:AddElement('Aura', Update, Enable, Disable)