You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
672 lines
25 KiB
672 lines
25 KiB
|
3 years ago
|
local Auras = {}
|
||
|
|
local playerUnits = {player = true, vehicle = true, pet = true}
|
||
|
|
local mainHand, offHand, tempEnchantScan = {time = 0}, {time = 0}
|
||
|
|
local canCure = ShadowUF.Units.canCure
|
||
|
|
ShadowUF:RegisterModule(Auras, "auras", ShadowUF.L["Auras"])
|
||
|
|
|
||
|
|
function Auras:OnEnable(frame)
|
||
|
|
frame.auras = frame.auras or {}
|
||
|
|
|
||
|
|
frame:RegisterNormalEvent("PLAYER_ENTERING_WORLD", self, "Update")
|
||
|
|
frame:RegisterUnitEvent("UNIT_AURA", self, "Update")
|
||
|
|
frame:RegisterUpdateFunc(self, "Update")
|
||
|
|
|
||
|
|
self:UpdateFilter(frame)
|
||
|
|
end
|
||
|
|
|
||
|
|
function Auras:OnDisable(frame)
|
||
|
|
frame:UnregisterAll(self)
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Aura positioning code
|
||
|
|
-- Definitely some of the more unusual code I've done, not sure I really like this method
|
||
|
|
-- but it does allow more flexibility with how things are anchored without me having to hardcode the 10 different growth methods
|
||
|
|
local function load(text)
|
||
|
|
local result, err = loadstring(text)
|
||
|
|
if( err ) then
|
||
|
|
error(err, 3)
|
||
|
|
return nil
|
||
|
|
end
|
||
|
|
|
||
|
|
return result()
|
||
|
|
end
|
||
|
|
|
||
|
|
local positionData = setmetatable({}, {
|
||
|
|
__index = function(tbl, index)
|
||
|
|
local data = {}
|
||
|
|
local columnGrowth = ShadowUF.Layout:GetColumnGrowth(index)
|
||
|
|
local auraGrowth = ShadowUF.Layout:GetAuraGrowth(index)
|
||
|
|
data.xMod = (columnGrowth == "RIGHT" or auraGrowth == "RIGHT") and 1 or -1
|
||
|
|
data.yMod = (columnGrowth ~= "TOP" and auraGrowth ~= "TOP") and -1 or 1
|
||
|
|
|
||
|
|
local auraX, colX, auraY, colY, xOffset, yOffset, initialXOffset, initialYOffset = 0, 0, 0, 0, "", "", "", ""
|
||
|
|
if( columnGrowth == "LEFT" or columnGrowth == "RIGHT" ) then
|
||
|
|
colX = 1
|
||
|
|
xOffset = " + offset"
|
||
|
|
initialXOffset = string.format(" + (%d * offset)", data.xMod)
|
||
|
|
auraY = 3
|
||
|
|
data.isSideGrowth = true
|
||
|
|
elseif( columnGrowth == "TOP" or columnGrowth == "BOTTOM" ) then
|
||
|
|
colY = 2
|
||
|
|
yOffset = " + offset"
|
||
|
|
initialYOffset = string.format(" + (%d * offset)", data.yMod)
|
||
|
|
auraX = 2
|
||
|
|
end
|
||
|
|
|
||
|
|
data.initialAnchor = load(string.format([[return function(button, offset)
|
||
|
|
button:ClearAllPoints()
|
||
|
|
button:SetPoint(button.point, button.anchorTo, button.relativePoint, button.xOffset%s, button.yOffset%s)
|
||
|
|
button.anchorOffset = offset
|
||
|
|
end]], initialXOffset, initialYOffset))
|
||
|
|
data.column = load(string.format([[return function(button, positionTo, offset)
|
||
|
|
button:ClearAllPoints()
|
||
|
|
button:SetPoint("%s", positionTo, "%s", %d * (%d%s), %d * (%d%s)) end
|
||
|
|
]], ShadowUF.Layout:ReverseDirection(columnGrowth), columnGrowth, data.xMod, colX, xOffset, data.yMod, colY, yOffset))
|
||
|
|
data.aura = load(string.format([[return function(button, positionTo)
|
||
|
|
button:ClearAllPoints()
|
||
|
|
button:SetPoint("%s", positionTo, "%s", %d, %d) end
|
||
|
|
]], ShadowUF.Layout:ReverseDirection(auraGrowth), auraGrowth, data.xMod * auraX, data.yMod * auraY))
|
||
|
|
|
||
|
|
tbl[index] = data
|
||
|
|
return tbl[index]
|
||
|
|
end,
|
||
|
|
})
|
||
|
|
|
||
|
|
local function positionButton(id, group, config)
|
||
|
|
local position = positionData[group.forcedAnchorPoint or config.anchorPoint]
|
||
|
|
local button = group.buttons[id]
|
||
|
|
button.isAuraAnchor = nil
|
||
|
|
|
||
|
|
-- Alright, in order to find out where an aura group is going to be anchored to certain buttons need
|
||
|
|
-- to be flagged as suitable anchors visually, this speeds it up because this data is cached and doesn't
|
||
|
|
-- have to be recalculated unless auras are specifically changed
|
||
|
|
if( id > 1 ) then
|
||
|
|
if( position.isSideGrowth and id <= config.perRow ) then
|
||
|
|
button.isAuraAnchor = true
|
||
|
|
end
|
||
|
|
|
||
|
|
if( id % config.perRow == 1 or config.perRow == 1 ) then
|
||
|
|
position.column(button, group.buttons[id - config.perRow], 0)
|
||
|
|
|
||
|
|
if( not position.isSideGrowth ) then
|
||
|
|
button.isAuraAnchor = true
|
||
|
|
end
|
||
|
|
else
|
||
|
|
position.aura(button, group.buttons[id - 1])
|
||
|
|
end
|
||
|
|
else
|
||
|
|
button.isAuraAnchor = true
|
||
|
|
button.point = ShadowUF.Layout:GetPoint(config.anchorPoint)
|
||
|
|
button.relativePoint = ShadowUF.Layout:GetRelative(config.anchorPoint)
|
||
|
|
button.xOffset = config.x + (position.xMod * ShadowUF.db.profile.backdrop.inset)
|
||
|
|
button.yOffset = config.y + (position.yMod * ShadowUF.db.profile.backdrop.inset)
|
||
|
|
button.anchorTo = group.anchorTo
|
||
|
|
|
||
|
|
position.initialAnchor(button, 0)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
|
||
|
|
local columnsHaveScale = {}
|
||
|
|
local function positionAllButtons(group, config)
|
||
|
|
local position = positionData[group.forcedAnchorPoint or config.anchorPoint]
|
||
|
|
|
||
|
|
-- Figure out which columns have scaling so we can work out positioning
|
||
|
|
local columnID = 0
|
||
|
|
for id, button in pairs(group.buttons) do
|
||
|
|
if( id % config.perRow == 1 or config.perRow == 1 ) then
|
||
|
|
columnID = columnID + 1
|
||
|
|
columnsHaveScale[columnID] = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
if( not columnsHaveScale[columnID] and button.isSelfScaled ) then
|
||
|
|
local size = math.ceil(button:GetSize() * button:GetScale())
|
||
|
|
columnsHaveScale[columnID] = columnsHaveScale[columnID] and math.max(size, columnsHaveScale[columnID]) or size
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
columnID = 1
|
||
|
|
for id, button in pairs(group.buttons) do
|
||
|
|
if( id > 1 ) then
|
||
|
|
if( id % config.perRow == 1 or config.perRow == 1 ) then
|
||
|
|
columnID = columnID + 1
|
||
|
|
|
||
|
|
local anchorButton = group.buttons[id - config.perRow]
|
||
|
|
local previousScale, currentScale = columnsHaveScale[columnID - 1], columnsHaveScale[columnID]
|
||
|
|
local offset = 0
|
||
|
|
-- Previous column has a scaled aura, and the button we are anchoring to is not scaled
|
||
|
|
if( previousScale and not anchorButton.isSelfScaled ) then
|
||
|
|
offset = (previousScale / 4)
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Current column has a scaled aura, and the button isn't scaled
|
||
|
|
if( currentScale and not button.isSelfScaled ) then
|
||
|
|
offset = offset + (currentScale / 4)
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Current anchor is scaled, previous is not
|
||
|
|
if( button.isSelfScaled and not anchorButton.isSelfScaled ) then
|
||
|
|
offset = offset - (currentScale / 6)
|
||
|
|
end
|
||
|
|
|
||
|
|
-- At least one of them is scaled
|
||
|
|
if( ( not button.isSelfScaled or not anchorButton.isSelfScaled ) and offset > 0 ) then
|
||
|
|
offset = offset + 1
|
||
|
|
end
|
||
|
|
|
||
|
|
position.column(button, anchorButton, math.ceil(offset))
|
||
|
|
else
|
||
|
|
position.aura(button, group.buttons[id - 1])
|
||
|
|
end
|
||
|
|
else
|
||
|
|
-- If the initial column is self scaled, but the initial anchor isn't, will have to reposition it
|
||
|
|
local offset = 0
|
||
|
|
if( columnsHaveScale[columnID] ) then
|
||
|
|
offset = math.ceil(columnsHaveScale[columnID] / 8)
|
||
|
|
if( button.isSelfScaled ) then
|
||
|
|
offset = -(offset / 2)
|
||
|
|
else
|
||
|
|
offset = offset + 2
|
||
|
|
end
|
||
|
|
|
||
|
|
offset = offset
|
||
|
|
end
|
||
|
|
|
||
|
|
if( offset ~= button.anchorOffset ) then
|
||
|
|
position.initialAnchor(button, offset)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Aura button functions
|
||
|
|
-- Updates the X seconds left on aura tooltip while it's shown
|
||
|
|
local function updateTooltip(self)
|
||
|
|
if( not GameTooltip:IsForbidden() and GameTooltip:IsOwned(self) ) then
|
||
|
|
GameTooltip:SetUnitAura(self.unit, self.auraID, self.filter)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function showTooltip(self)
|
||
|
|
if( not ShadowUF.db.profile.locked ) then return end
|
||
|
|
if( GameTooltip:IsForbidden() ) then return end
|
||
|
|
|
||
|
|
GameTooltip:SetOwner(self, "ANCHOR_BOTTOMLEFT")
|
||
|
|
if( self.filter == "TEMP" ) then
|
||
|
|
GameTooltip:SetInventoryItem("player", self.auraID)
|
||
|
|
self:SetScript("OnUpdate", nil)
|
||
|
|
else
|
||
|
|
GameTooltip:SetUnitAura(self.unit, self.auraID, self.filter)
|
||
|
|
self:SetScript("OnUpdate", updateTooltip)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function hideTooltip(self)
|
||
|
|
self:SetScript("OnUpdate", nil)
|
||
|
|
if not GameTooltip:IsForbidden() then
|
||
|
|
GameTooltip:Hide()
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function cancelAura(self, mouse)
|
||
|
|
if( mouse ~= "RightButton" or ( not UnitIsUnit(self.parent.unit, "player") and not UnitIsUnit(self.parent.unit, "vehicle") ) or InCombatLockdown() or self.filter == "TEMP" ) then
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
CancelUnitBuff(self.parent.unit, self.auraID, self.filter)
|
||
|
|
end
|
||
|
|
|
||
|
|
local function updateButton(id, group, config)
|
||
|
|
local button = group.buttons[id]
|
||
|
|
if( not button ) then
|
||
|
|
group.buttons[id] = CreateFrame("Button", nil, group)
|
||
|
|
|
||
|
|
button = group.buttons[id]
|
||
|
|
button:SetScript("OnEnter", showTooltip)
|
||
|
|
button:SetScript("OnLeave", hideTooltip)
|
||
|
|
button:RegisterForClicks("RightButtonUp")
|
||
|
|
|
||
|
|
button.cooldown = CreateFrame("Cooldown", group.parent:GetName() .. "Aura" .. group.type .. id .. "Cooldown", button, "CooldownFrameTemplate")
|
||
|
|
button.cooldown:SetAllPoints(button)
|
||
|
|
button.cooldown:SetReverse(true)
|
||
|
|
button.cooldown:SetDrawEdge(false)
|
||
|
|
button.cooldown:SetDrawSwipe(true)
|
||
|
|
button.cooldown:SetSwipeColor(0, 0, 0, 0.8)
|
||
|
|
button.cooldown:Hide()
|
||
|
|
|
||
|
|
button.stack = button:CreateFontString(nil, "OVERLAY")
|
||
|
|
button.stack:SetFont("Interface\\AddOns\\ShadowedUnitFrames\\media\\fonts\\Myriad Condensed Web.ttf", 10, "OUTLINE")
|
||
|
|
button.stack:SetShadowColor(0, 0, 0, 1.0)
|
||
|
|
button.stack:SetShadowOffset(0.50, -0.50)
|
||
|
|
button.stack:SetHeight(1)
|
||
|
|
button.stack:SetWidth(1)
|
||
|
|
button.stack:SetAllPoints(button)
|
||
|
|
button.stack:SetJustifyV("BOTTOM")
|
||
|
|
button.stack:SetJustifyH("RIGHT")
|
||
|
|
|
||
|
|
button.border = button:CreateTexture(nil, "OVERLAY")
|
||
|
|
button.border:SetPoint("CENTER", button)
|
||
|
|
|
||
|
|
button.icon = button:CreateTexture(nil, "BACKGROUND")
|
||
|
|
button.icon:SetAllPoints(button)
|
||
|
|
button.icon:SetTexCoord(0.07, 0.93, 0.07, 0.93)
|
||
|
|
end
|
||
|
|
|
||
|
|
if( ShadowUF.db.profile.auras.borderType == "" ) then
|
||
|
|
button.border:Hide()
|
||
|
|
elseif( ShadowUF.db.profile.auras.borderType == "blizzard" ) then
|
||
|
|
button.border:SetTexture("Interface\\Buttons\\UI-Debuff-Overlays")
|
||
|
|
button.border:SetTexCoord(0.296875, 0.5703125, 0, 0.515625)
|
||
|
|
button.border:Show()
|
||
|
|
else
|
||
|
|
button.border:SetTexture("Interface\\AddOns\\ShadowedUnitFrames\\media\\textures\\border-" .. ShadowUF.db.profile.auras.borderType)
|
||
|
|
button.border:SetTexCoord(0, 1, 0, 1)
|
||
|
|
button.border:Show()
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Set the button sizing
|
||
|
|
button.cooldown.noCooldownCount = ShadowUF.db.profile.omnicc
|
||
|
|
button.cooldown:SetHideCountdownNumbers(ShadowUF.db.profile.blizzardcc)
|
||
|
|
button:SetHeight(config.size)
|
||
|
|
button:SetWidth(config.size)
|
||
|
|
button.border:SetHeight(config.size + 1)
|
||
|
|
button.border:SetWidth(config.size + 1)
|
||
|
|
button.stack:SetFont("Interface\\AddOns\\ShadowedUnitFrames\\media\\fonts\\Myriad Condensed Web.ttf", math.floor((config.size * 0.60) + 0.5), "OUTLINE")
|
||
|
|
|
||
|
|
button:SetScript("OnClick", cancelAura)
|
||
|
|
button.parent = group.parent
|
||
|
|
button:ClearAllPoints()
|
||
|
|
button:Hide()
|
||
|
|
|
||
|
|
-- Position the button quickly
|
||
|
|
positionButton(id, group, config)
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Let the mover access this for creating aura things
|
||
|
|
Auras.updateButton = updateButton
|
||
|
|
|
||
|
|
-- Create an aura anchor as well as the buttons to contain it
|
||
|
|
local function updateGroup(self, type, config, reverseConfig)
|
||
|
|
self.auras[type] = self.auras[type] or CreateFrame("Frame", nil, self.highFrame)
|
||
|
|
|
||
|
|
local group = self.auras[type]
|
||
|
|
group.buttons = group.buttons or {}
|
||
|
|
|
||
|
|
group.maxAuras = config.perRow * config.maxRows
|
||
|
|
group.totalAuras = 0
|
||
|
|
group.temporaryEnchants = 0
|
||
|
|
group.type = type
|
||
|
|
group.parent = self
|
||
|
|
group.anchorTo = self
|
||
|
|
group:SetFrameLevel(self.highFrame:GetFrameLevel() + 1)
|
||
|
|
group:Show()
|
||
|
|
|
||
|
|
-- If debuffs are anchored to buffs, debuffs need to grow however buffs do
|
||
|
|
if( config.anchorOn and reverseConfig.enabled ) then
|
||
|
|
group.forcedAnchorPoint = reverseConfig.anchorPoint
|
||
|
|
end
|
||
|
|
|
||
|
|
if( self.unit == "player" ) then
|
||
|
|
mainHand.time = 0
|
||
|
|
offHand.time = 0
|
||
|
|
|
||
|
|
group:SetScript("OnUpdate", config.temporary and tempEnchantScan or nil)
|
||
|
|
else
|
||
|
|
group:SetScript("OnUpdate", nil)
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Update filters used for the anchor
|
||
|
|
group.filter = group.type == "buffs" and "HELPFUL" or group.type == "debuffs" and "HARMFUL" or ""
|
||
|
|
|
||
|
|
for id, button in pairs(group.buttons) do
|
||
|
|
updateButton(id, group, config)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Update aura positions based off of configuration
|
||
|
|
function Auras:OnLayoutApplied(frame, config)
|
||
|
|
if( frame.auras ) then
|
||
|
|
if( frame.auras.buffs ) then
|
||
|
|
for _, button in pairs(frame.auras.buffs.buttons) do
|
||
|
|
button:Hide()
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if( frame.auras.debuffs ) then
|
||
|
|
for _, button in pairs(frame.auras.debuffs.buttons) do
|
||
|
|
button:Hide()
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if( not frame.visibility.auras ) then return end
|
||
|
|
|
||
|
|
if( config.auras.buffs.enabled ) then
|
||
|
|
updateGroup(frame, "buffs", config.auras.buffs, config.auras.debuffs)
|
||
|
|
end
|
||
|
|
|
||
|
|
if( config.auras.debuffs.enabled ) then
|
||
|
|
updateGroup(frame, "debuffs", config.auras.debuffs, config.auras.buffs)
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Anchor an aura group to another aura group
|
||
|
|
frame.auras.anchorAurasOn = nil
|
||
|
|
if( config.auras.buffs.enabled and config.auras.debuffs.enabled ) then
|
||
|
|
if( config.auras.buffs.anchorOn ) then
|
||
|
|
frame.auras.anchorAurasOn = frame.auras.debuffs
|
||
|
|
frame.auras.anchorAurasChild = frame.auras.buffs
|
||
|
|
elseif( config.auras.debuffs.anchorOn ) then
|
||
|
|
frame.auras.anchorAurasOn = frame.auras.buffs
|
||
|
|
frame.auras.anchorAurasChild = frame.auras.debuffs
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Check if either auras are anchored to each other
|
||
|
|
if( config.auras.buffs.anchorPoint == config.auras.debuffs.anchorPoint and config.auras.buffs.enabled and config.auras.debuffs.enabled and not config.auras.buffs.anchorOn and not config.auras.debuffs.anchorOn ) then
|
||
|
|
frame.auras.anchor = frame.auras[config.auras.buffs.prioritize and "buffs" or "debuffs"]
|
||
|
|
frame.auras.primary = config.auras.buffs.prioritize and "buffs" or "debuffs"
|
||
|
|
frame.auras.secondary = frame.auras.primary == "buffs" and "debuffs" or "buffs"
|
||
|
|
else
|
||
|
|
frame.auras.anchor = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
self:UpdateFilter(frame)
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Temporary enchant support
|
||
|
|
local timeElapsed = 0
|
||
|
|
local function updateTemporaryEnchant(frame, slot, tempData, hasEnchant, enchantId, timeLeft, charges)
|
||
|
|
-- If there's less than a 750 millisecond differences in the times, we don't need to bother updating.
|
||
|
|
-- Any sort of enchant takes more than 0.750 seconds to cast so it's impossible for the user to have two
|
||
|
|
-- temporary enchants with that little difference, as totems don't really give pulsing auras anymore.
|
||
|
|
charges = charges or 0
|
||
|
|
if( tempData.has and tempData.enchantId == enchantId and ( timeLeft < tempData.time and ( tempData.time - timeLeft ) < 750 ) ) then return false end
|
||
|
|
|
||
|
|
-- Some trickys magic, we can't get the start time of temporary enchants easily.
|
||
|
|
-- So will save the first time we find when a new enchant is added
|
||
|
|
if( timeLeft > tempData.time or not tempData.has ) then
|
||
|
|
tempData.startTime = GetTime()
|
||
|
|
end
|
||
|
|
|
||
|
|
tempData.has = hasEnchant
|
||
|
|
tempData.time = timeLeft
|
||
|
|
tempData.charges = charges
|
||
|
|
tempData.enchantId = enchantId
|
||
|
|
|
||
|
|
local config = ShadowUF.db.profile.units[frame.parent.unitType].auras[frame.type]
|
||
|
|
|
||
|
|
-- Create any buttons we need
|
||
|
|
if( #(frame.buttons) < frame.temporaryEnchants ) then
|
||
|
|
updateButton(frame.temporaryEnchants, frame, config)
|
||
|
|
end
|
||
|
|
|
||
|
|
local button = frame.buttons[frame.temporaryEnchants]
|
||
|
|
|
||
|
|
-- Purple border
|
||
|
|
button.border:SetVertexColor(0.50, 0, 0.50)
|
||
|
|
|
||
|
|
-- Show the cooldown ring
|
||
|
|
if( not ShadowUF.db.profile.auras.disableCooldown ) then
|
||
|
|
button.cooldown:SetCooldown(tempData.startTime, timeLeft / 1000)
|
||
|
|
button.cooldown:Show()
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Enlarge our own auras
|
||
|
|
if( config.enlarge.SELF ) then
|
||
|
|
button.isSelfScaled = true
|
||
|
|
button:SetScale(config.selfScale)
|
||
|
|
else
|
||
|
|
button.isSelfScaled = nil
|
||
|
|
button:SetScale(1)
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Size it
|
||
|
|
button:SetHeight(config.size)
|
||
|
|
button:SetWidth(config.size)
|
||
|
|
button.border:SetHeight(config.size + 1)
|
||
|
|
button.border:SetWidth(config.size + 1)
|
||
|
|
|
||
|
|
-- Stack + icon + show! Never understood why, auras sometimes return 1 for stack even if they don't stack
|
||
|
|
button.auraID = slot
|
||
|
|
button.filter = "TEMP"
|
||
|
|
button.unit = nil
|
||
|
|
button.columnHasScaled = nil
|
||
|
|
button.previousHasScale = nil
|
||
|
|
button.icon:SetTexture(GetInventoryItemTexture("player", slot))
|
||
|
|
button.stack:SetText(charges > 1 and charges or "")
|
||
|
|
button:Show()
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Unfortunately, temporary enchants have basically no support beyond hacks. So we will hack!
|
||
|
|
tempEnchantScan = function(self, elapsed)
|
||
|
|
if( self.parent.unit == self.parent.vehicleUnit and self.lastTemporary > 0 ) then
|
||
|
|
mainHand.has = false
|
||
|
|
offHand.has = false
|
||
|
|
|
||
|
|
self.temporaryEnchants = 0
|
||
|
|
self.lastTemporary = 0
|
||
|
|
|
||
|
|
Auras:Update(self.parent)
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
timeElapsed = timeElapsed + elapsed
|
||
|
|
if( timeElapsed < 0.50 ) then return end
|
||
|
|
timeElapsed = timeElapsed - 0.50
|
||
|
|
|
||
|
|
|
||
|
|
local hasMain, mainTimeLeft, mainCharges, mainEnchantId, hasOff, offTimeLeft, offCharges, offEnchantId = GetWeaponEnchantInfo()
|
||
|
|
self.temporaryEnchants = 0
|
||
|
|
|
||
|
|
if( hasMain ) then
|
||
|
|
self.temporaryEnchants = self.temporaryEnchants + 1
|
||
|
|
updateTemporaryEnchant(self, 16, mainHand, hasMain, mainEnchantId, mainTimeLeft or 0, mainCharges)
|
||
|
|
mainHand.time = mainTimeLeft or 0
|
||
|
|
end
|
||
|
|
|
||
|
|
mainHand.has = hasMain
|
||
|
|
|
||
|
|
if( hasOff and self.temporaryEnchants < self.maxAuras ) then
|
||
|
|
self.temporaryEnchants = self.temporaryEnchants + 1
|
||
|
|
updateTemporaryEnchant(self, 17, offHand, hasOff, offEnchantId, offTimeLeft or 0, offCharges)
|
||
|
|
offHand.time = offTimeLeft or 0
|
||
|
|
end
|
||
|
|
|
||
|
|
offHand.has = hasOff
|
||
|
|
|
||
|
|
-- Update if totals changed
|
||
|
|
if( self.lastTemporary ~= self.temporaryEnchants ) then
|
||
|
|
self.lastTemporary = self.temporaryEnchants
|
||
|
|
Auras:Update(self.parent)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Nice and simple, don't need to do a full update because either this is called in an OnEnable or
|
||
|
|
-- the zone monitor will handle it all cleanly. The fun part of this code is aura filtering itself takes 10 seconds
|
||
|
|
-- but making the configuration clean takes two weeks and another 2-3 days of implementing
|
||
|
|
-- This isn't actually filled with data, it's just to stop any errors from triggering if no filter is added
|
||
|
|
local filterDefault = {}
|
||
|
|
function Auras:UpdateFilter(frame)
|
||
|
|
local zone = select(2, IsInInstance()) or "none"
|
||
|
|
if( zone == "scenario" ) then zone = "party" end
|
||
|
|
|
||
|
|
local id = zone .. frame.unitType
|
||
|
|
|
||
|
|
local white = ShadowUF.db.profile.filters.zonewhite[zone .. frame.unitType]
|
||
|
|
local black = ShadowUF.db.profile.filters.zoneblack[zone .. frame.unitType]
|
||
|
|
local override = ShadowUF.db.profile.filters.zoneoverride[zone .. frame.unitType]
|
||
|
|
frame.auras.whitelist = white and ShadowUF.db.profile.filters.whitelists[white] or filterDefault
|
||
|
|
frame.auras.blacklist = black and ShadowUF.db.profile.filters.blacklists[black] or filterDefault
|
||
|
|
frame.auras.overridelist = override and ShadowUF.db.profile.filters.overridelists[override] or filterDefault
|
||
|
|
end
|
||
|
|
|
||
|
|
local function categorizeAura(type, curable, auraType, caster, isRemovable, canApplyAura, isBossDebuff)
|
||
|
|
-- Player casted it
|
||
|
|
if( playerUnits[caster] ) then
|
||
|
|
return "player"
|
||
|
|
-- Boss aura
|
||
|
|
elseif( isBossDebuff ) then
|
||
|
|
return "boss"
|
||
|
|
-- Can dispell, curable checks type already
|
||
|
|
elseif( curable and canCure[auraType] ) then
|
||
|
|
return "raid"
|
||
|
|
-- Can apply it ourselves
|
||
|
|
elseif( type == "buffs" and canApplyAura ) then
|
||
|
|
return "raid"
|
||
|
|
-- Can be stolen/purged (dispellable)
|
||
|
|
elseif( type == "debuffs" and isRemovable ) then
|
||
|
|
return "raid"
|
||
|
|
else
|
||
|
|
return "misc"
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function renderAura(parent, frame, type, config, displayConfig, index, filter, isFriendly, curable, name, texture, count, auraType, duration, endTime, caster, isRemovable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff)
|
||
|
|
-- aura filters are all saved as strings, so need to override here
|
||
|
|
spellID = tostring(spellID)
|
||
|
|
-- Do our initial list check to see if we can quick filter it out
|
||
|
|
if( parent.whitelist[type] and not parent.whitelist[name] and not parent.whitelist[spellID] ) then return end
|
||
|
|
if( parent.blacklist[type] and ( parent.blacklist[name] or parent.blacklist[spellID] ) ) then return end
|
||
|
|
|
||
|
|
-- Now do our type filter
|
||
|
|
local category = categorizeAura(type, curable, auraType, caster, isRemovable, canApplyAura, isBossDebuff)
|
||
|
|
-- check override and type filters
|
||
|
|
if( not ( parent.overridelist[type] and ( parent.overridelist[name] or parent.overridelist[spellID] ) ) and not config.show[category] and (not config.show.relevant or (type == "debuffs") ~= isFriendly) ) then return end
|
||
|
|
|
||
|
|
-- Create any buttons we need
|
||
|
|
frame.totalAuras = frame.totalAuras + 1
|
||
|
|
if( #(frame.buttons) < frame.totalAuras ) then
|
||
|
|
updateButton(frame.totalAuras, frame, ShadowUF.db.profile.units[frame.parent.unitType].auras[frame.type])
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Show debuff border, or a special colored border if it's stealable
|
||
|
|
local button = frame.buttons[frame.totalAuras]
|
||
|
|
if( isRemovable and not isFriendly and not ShadowUF.db.profile.auras.disableColor ) then
|
||
|
|
button.border:SetVertexColor(ShadowUF.db.profile.auraColors.removable.r, ShadowUF.db.profile.auraColors.removable.g, ShadowUF.db.profile.auraColors.removable.b)
|
||
|
|
elseif( ( not isFriendly or type == "debuffs" ) and not ShadowUF.db.profile.auras.disableColor ) then
|
||
|
|
local color = auraType and DebuffTypeColor[auraType] or DebuffTypeColor.none
|
||
|
|
button.border:SetVertexColor(color.r, color.g, color.b)
|
||
|
|
else
|
||
|
|
button.border:SetVertexColor(0.60, 0.60, 0.60)
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Show the cooldown ring
|
||
|
|
if( not ShadowUF.db.profile.auras.disableCooldown and duration > 0 and endTime > 0 and ( config.timers.ALL or ( category == "player" and config.timers.SELF ) or ( category == "boss" and config.timers.BOSS ) ) ) then
|
||
|
|
button.cooldown:SetCooldown(endTime - duration, duration)
|
||
|
|
button.cooldown:Show()
|
||
|
|
else
|
||
|
|
button.cooldown:Hide()
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Enlarge auras
|
||
|
|
if( ( category == "player" and config.enlarge.SELF ) or ( category == "boss" and config.enlarge.BOSS ) or ( config.enlarge.REMOVABLE and ( ( isRemovable and not isFriendly ) or ( curable and canCure[auraType]) ) ) ) then
|
||
|
|
button.isSelfScaled = true
|
||
|
|
button:SetScale(config.selfScale)
|
||
|
|
else
|
||
|
|
button.isSelfScaled = nil
|
||
|
|
button:SetScale(1)
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Size it
|
||
|
|
button:SetHeight(config.size)
|
||
|
|
button:SetWidth(config.size)
|
||
|
|
button.border:SetHeight(config.size + 1)
|
||
|
|
button.border:SetWidth(config.size + 1)
|
||
|
|
|
||
|
|
-- Stack + icon + show! Never understood why, auras sometimes return 1 for stack even if they don't stack
|
||
|
|
button.auraID = index
|
||
|
|
button.filter = filter
|
||
|
|
button.unit = frame.parent.unit
|
||
|
|
button.columnHasScaled = nil
|
||
|
|
button.previousHasScale = nil
|
||
|
|
button.icon:SetTexture(texture)
|
||
|
|
button.stack:SetText(count > 1 and count or "")
|
||
|
|
button:Show()
|
||
|
|
end
|
||
|
|
|
||
|
|
|
||
|
|
-- Scan for auras
|
||
|
|
local function scan(parent, frame, type, config, displayConfig, filter)
|
||
|
|
if( frame.totalAuras >= frame.maxAuras or not config.enabled ) then return end
|
||
|
|
|
||
|
|
-- UnitIsFriend returns true during a duel, which breaks stealable/curable detection
|
||
|
|
local isFriendly = not UnitIsEnemy(frame.parent.unit, "player")
|
||
|
|
local curable = (isFriendly and type == "debuffs")
|
||
|
|
local index = 0
|
||
|
|
while( true ) do
|
||
|
|
index = index + 1
|
||
|
|
local name, texture, count, auraType, duration, endTime, caster, isRemovable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff = UnitAura(frame.parent.unit, index, filter)
|
||
|
|
if( not name ) then break end
|
||
|
|
|
||
|
|
renderAura(parent, frame, type, config, displayConfig, index, filter, isFriendly, curable, name, texture, count, auraType, duration, endTime, caster, isRemovable, nameplateShowPersonal, spellID, canApplyAura, isBossDebuff)
|
||
|
|
|
||
|
|
-- Too many auras shown, break out
|
||
|
|
-- Get down
|
||
|
|
if( frame.totalAuras >= frame.maxAuras ) then break end
|
||
|
|
end
|
||
|
|
|
||
|
|
for i=frame.totalAuras + 1, #(frame.buttons) do frame.buttons[i]:Hide() end
|
||
|
|
|
||
|
|
-- The default 1.30 scale doesn't need special handling, after that it does
|
||
|
|
if( config.selfScale > 1.30 ) then
|
||
|
|
positionAllButtons(frame, displayConfig)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
Auras.scan = scan
|
||
|
|
|
||
|
|
local function anchorGroupToGroup(frame, config, group, childConfig, childGroup)
|
||
|
|
-- Child group has nothing in it yet, so don't care
|
||
|
|
if( not childGroup.buttons[1] ) then return end
|
||
|
|
|
||
|
|
-- Group we want to anchor to has nothing in it, takeover the postion
|
||
|
|
if( group.totalAuras == 0 ) then
|
||
|
|
local position = positionData[config.anchorPoint]
|
||
|
|
childGroup.buttons[1]:ClearAllPoints()
|
||
|
|
childGroup.buttons[1]:SetPoint(ShadowUF.Layout:GetPoint(config.anchorPoint), group.anchorTo, ShadowUF.Layout:GetRelative(config.anchorPoint), config.x + (position.xMod * ShadowUF.db.profile.backdrop.inset), config.y + (position.yMod * ShadowUF.db.profile.backdrop.inset))
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
local anchorTo
|
||
|
|
for i=#(group.buttons), 1, -1 do
|
||
|
|
local button = group.buttons[i]
|
||
|
|
if( button.isAuraAnchor and button:IsVisible() ) then
|
||
|
|
anchorTo = button
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local position = positionData[childGroup.forcedAnchorPoint or childConfig.anchorPoint]
|
||
|
|
if( position.isSideGrowth ) then
|
||
|
|
position.aura(childGroup.buttons[1], anchorTo)
|
||
|
|
else
|
||
|
|
position.column(childGroup.buttons[1], anchorTo, 2)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
Auras.anchorGroupToGroup = anchorGroupToGroup
|
||
|
|
|
||
|
|
-- Do an update and figure out what we need to scan
|
||
|
|
function Auras:Update(frame)
|
||
|
|
local config = ShadowUF.db.profile.units[frame.unitType].auras
|
||
|
|
if( frame.auras.anchor ) then
|
||
|
|
frame.auras.anchor.totalAuras = frame.auras.anchor.temporaryEnchants
|
||
|
|
|
||
|
|
scan(frame.auras, frame.auras.anchor, frame.auras.primary, config[frame.auras.primary], config[frame.auras.primary], frame.auras[frame.auras.primary].filter)
|
||
|
|
scan(frame.auras, frame.auras.anchor, frame.auras.secondary, config[frame.auras.secondary], config[frame.auras.primary], frame.auras[frame.auras.secondary].filter)
|
||
|
|
else
|
||
|
|
if( config.buffs.enabled ) then
|
||
|
|
frame.auras.buffs.totalAuras = frame.auras.buffs.temporaryEnchants
|
||
|
|
scan(frame.auras, frame.auras.buffs, "buffs", config.buffs, config.buffs, frame.auras.buffs.filter)
|
||
|
|
end
|
||
|
|
|
||
|
|
if( config.debuffs.enabled ) then
|
||
|
|
frame.auras.debuffs.totalAuras = 0
|
||
|
|
scan(frame.auras, frame.auras.debuffs, "debuffs", config.debuffs, config.debuffs, frame.auras.debuffs.filter)
|
||
|
|
end
|
||
|
|
|
||
|
|
if( frame.auras.anchorAurasOn ) then
|
||
|
|
anchorGroupToGroup(frame, config[frame.auras.anchorAurasOn.type], frame.auras.anchorAurasOn, config[frame.auras.anchorAurasChild.type], frame.auras.anchorAurasChild)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|