Permalink
Switch branches/tags
Find file
Fetching contributors…
Cannot retrieve contributors at this time
968 lines (878 sloc) 24.4 KB
BalerocHealers = LibStub("AceAddon-3.0"):NewAddon("BalerocHealers", "AceConsole-3.0", "AceEvent-3.0")
local core = BalerocHealers
local L = LibStub("AceLocale-3.0"):GetLocale("BalerocHealers")
local LSM; -- LibSharedMedia-3.0, checked later
local factory = {}
local db = {}
local VITAL_SPARK, VITAL_FLAME, TORMENTED = GetSpellInfo(99262), GetSpellInfo(99263), GetSpellInfo(99257)
-- Upvalues
local GetTime = GetTime
-- TODO
-- healers respeccing not being removed from frames correctly?
-- attach left/right
-- Sort functions
local sortRosterMap = {}
local SORT_FUNC = {
["CLASS"] = function(a,b)
if UnitClass(a) == UnitClass(b) then
return a < b
end
return UnitClass(a) < UnitClass(b)
end,
["NAME"] = function(a,b)
return a < b
end,
["GROUP"] = function(a,b)
if sortRosterMap[a] == sortRosterMap[b] or not sortRosterMap[a] or not sortRosterMap[b] then
return a < b
end
return sortRosterMap[a] < sortRosterMap[b]
end,
}
local defaults = {
profile = {
anchor = {
x = 500,
y = 500,
locked = false,
},
scale = 1,
sort = "CLASS",
iconSize = 25,
spacing = 5,
growthDirection = "DOWN",
barEnable = true,
barWidth = 200,
barHeight = 6,
barTexture = "Minimalist",
barIconPadding = 6,
playerTextAlign = "TOP",
playerFontSize = 12,
stacksEnable = true,
stacksFontSize = 10,
stacksAlways = true,
timerEnable = true,
timerFontSize = 10,
timerLocation = "BAR",
clockEnable = true,
clockDisableCDC = true,
tormentOverride = true,
-- No options yet
nameFont = STANDARD_TEXT_FONT or "Fonts\\FRIZQT__.TTF",
numberFont = NumberFont_Outline_Med and NumberFont_Outline_Med:GetFont() or "Fonts\\ARIALN.TTF",
},
}
local options_setter = function(info, v) local t=core.db.profile for k=1,#info-1 do t=t[info[k]] end t[info[#info]]=v core:UpdateFrames() end
local options_getter = function(info) local t=core.db.profile for k=1,#info-1 do t=t[info[k]] end return t[info[#info]] end
local options = {
name = "BalerocHealers",
type = 'group',
set = options_setter,
get = options_getter,
args = {
toggleLock = {
name = L["Toggle Anchor"],
type = 'execute',
order = 10,
func = function() core:SetLocked(not db.anchor.locked) end,
},
toggleTest = {
name = L["Toggle Test Mode"],
type = 'execute',
order = 10,
hidden = true,
func = function() core:SetTestMode() end,
},
br = {
name = '',
type = 'description',
order = 20,
},
scale = {
name = L["Scale"],
type = 'range',
order = 80,
min = 0.1,
max = 2,
step = 0.1,
set = function(...) options_setter(...) core:RebuildUI() end,
},
sort = {
name = L["Sort"],
type = 'select',
order = 90,
values = {
["CLASS"] = L["Class"],
["GROUP"] = L["Group"],
["NAME"] = L["Name"],
},
set = function(...) options_setter(...) core:RebuildUI() end,
},
iconSize = {
name = L["Icon Size"],
type = 'range',
min = 8,
max = 100,
step = 1,
order = 100,
},
spacing = {
name = L["Spacing"],
type = 'range',
min = 0,
max = 20,
step = 1,
order = 110,
},
growthDirection = {
name = L["Growth Direction"],
type = 'select',
order = 120,
values = {
["UP"] = L["Up"],
["DOWN"] = L["Down"],
},
set = function(...) options_setter(...) core:RebuildUI() end,
},
barHeader = {
name = L["Bar"],
type = 'header',
order = 200,
},
barEnable = {
name = L["Enable"],
type = 'toggle',
order = 205,
},
barWidth = {
name = L["Width"],
type = 'range',
disabled = function() return not db.barEnable end,
min = 30,
max = 500,
step = 1,
order = 210,
},
barHeight = {
name = L["Height"],
type = 'range',
disabled = function() return not db.barEnable end,
min = 4,
max = 40,
step = 1,
order = 220,
},
barTexture = {
name = L["Texture"],
type = 'select',
disabled = function() return not db.barEnable end,
hidden = function() return not LSM end,
order = 230,
values = {},
},
barIconPadding = {
name = L["Icon Spacing"],
type = 'range',
disabled = function() return not db.barEnable end,
min = 0,
max = 20,
step = 1,
order = 240,
},
playerHeader = {
name = L["Player Text"],
type = 'header',
order = 300,
},
playerFontSize = {
name = L["Font Size"],
type = 'range',
order = 310,
min = 6,
max = 20,
step = 1,
},
playerTextAlign = {
name = L["Vertical Alignment"],
type = 'select',
order = 320,
values = {
["TOP"] = L["Top"],
["MIDDLE"] = L["Middle"],
["BOTTOM"] = L["Bottom"],
},
},
stacksHeader = {
name = L["Stacks Text"],
type = 'header',
order = 400,
},
stacksEnable = {
name = L["Enable"],
type = 'toggle',
order = 405,
},
stacksAlways = {
name = L["Always Show"],
desc = L["Always show stack count even if Vital Flame is currently active"],
type = 'toggle',
disabled = function() return not db.stacksEnable end,
order = 408,
},
stacksFontSize = {
name = L["Font Size"],
type = 'range',
disabled = function() return not db.stacksEnable end,
min = 6,
max = 20,
step = 1,
order = 410,
},
timerHeader = {
name = L["Timer Text"],
type = 'header',
order = 500,
},
timerEnable = {
name = L["Enable"],
type = 'toggle',
order = 505,
},
timerFontSize = {
name = L["Font Size"],
type = 'range',
disabled = function() return not db.timerEnable end,
min = 6,
max = 20,
step = 1,
order = 510,
},
timerLocation = {
name = L["Timer Placement"],
type = 'select',
disabled = function() return not db.timerEnable end,
order = 520,
values = {
["ICON"] = L["Icon"],
["BAR"] = L["Bar"],
},
},
clockHeader = {
name = L["Cooldown Swipe Animation"],
type = 'header',
order = 600,
},
clockEnable = {
name = L["Enable"],
type = 'toggle',
order = 605,
},
clockDisableCDC = {
name = L["Disable OmniCC"],
type = 'toggle',
disabled = function() return not db.clockEnable end,
order = 630,
},
tormentHeader = {
name = L["Tormented"],
type = 'header',
order = 700,
},
tormentOverride = {
name = L["Override Display"],
desc = L["If the player has the Tormented debuff, a bar and timer icon will be shown for that instead of whichever buff they currently have"],
type = 'toggle',
order = 710,
},
},
}
local healerSpecs = {
DRUID = {
-- Balance
[78674] = false, -- Starsurge
[24858] = false, -- Moonkin Form
[50516] = false, -- Typhoon
[78675] = false, -- Solar Beam
[33831] = false, -- Force of Nature
[48505] = false, -- Starfall
-- Feral
[33876] = false, -- Mangle (Cat)
[33878] = false, -- Mangle (Bear)
[49376] = false, -- Feral Charge (Cat)
[16979] = false, -- Feral Charge (Bear)
[61336] = false, -- Survival Instincts
[80313] = false, -- Pulverize
[50334] = false, -- Berserk
-- Restoration
[18562] = true, -- Swiftmend
[17116] = true, -- Nature's Swiftness
[48438] = true, -- Wild Growth
[33891] = true, -- Tree of Life
},
PALADIN = {
-- Holy
[20473] = true, -- Holy Shock
[31842] = true, -- Divine Favor
[53563] = true, -- Beacon of Light
[85222] = true, -- Light of Dawn
-- Protection
[31935] = false, -- Avenger's Shield
[53595] = false, -- Hammer of the Righteous
[53600] = false, -- Shield of the Righteous
[70940] = false, -- Divine Guardian
[31850] = false, -- Ardent Defender
-- Retribution
[85256] = false, -- Templar's Verdict
[53385] = false, -- Divine Storm
[85696] = false, -- Zealotry
},
PRIEST = {
-- Discipline
[47540] = true, -- Penance
[10060] = true, -- Power Infusion
[89485] = true, -- Inner Focus
[33206] = true, -- Pain Suppression
[62618] = true, -- Power Word: Barrier
-- Holy
[88625] = true, -- Holy Word: Chastise
[724] = true, -- Lightwell
[14751] = true, -- Chakra
[34861] = true, -- Circle of Healing
[47788] = true, -- Guardian Spirit
-- Shadow
[15407] = false, -- Mind Flay
[15473] = false, -- Shadowform
[15487] = false, -- Silence
[34914] = false, -- Vampiric Touch
[64044] = false, -- Psychic Horror
[47585] = false, -- Dispersion
},
SHAMAN = {
-- Elemental
[51490] = false, -- Thunderstorm
[16166] = false, -- Elemental Mastery
[61882] = false, -- Earthquake
-- Enhancement
[60103] = false, -- Lava Lash
[17364] = false, -- Stormstrike
[30823] = false, -- Shamanistic Rage
[51533] = false, -- Feral Spirit
-- Restoration
[974] = true, -- Earth Shield
[16188] = true, -- Nature's Swiftness
[16190] = true, -- Mana Tide Totem
[61295] = true, -- Riptide
},
}
local active = false -- whether events are active
local display = false -- whether display is enabled (boss and testing)
local testMode = false -- displays test characters
local healerFrames = {} -- map of healer name -> frame object
local rosterMap = {} -- map of roster name -> isHealer flag
local debug = false
--@debug@
-- debug = true
-- local VITAL_SPARK, VITAL_FLAME, TORMENTED = GetSpellInfo(33763), GetSpellInfo(48438), GetSpellInfo(774)
-- local UnitDebuff = UnitBuff -- hack for tormented testing
--@end-debug@
function core:OnInitialize()
self.db = LibStub("AceDB-3.0"):New("BalerocHealersDB", defaults, "Default")
db = self.db.profile
self:RegisterChatCommand("balerochealers", "ChatCommand")
self:RegisterChatCommand("bal", "ChatCommand")
self:RegisterChatCommand("bh", "ChatCommand")
LibStub("AceConfigRegistry-3.0"):RegisterOptionsTable("BalerocHealers", options)
local ACD = LibStub("AceConfigDialog-3.0")
ACD:AddToBlizOptions("BalerocHealers", "BalerocHealers")
end
function core:OnEnable()
-- check if user has LSM
LSM = LibStub:GetLibrary("LibSharedMedia-3.0", true);
if (LSM and LibStub:GetLibrary("AceGUISharedMediaWidgets-1.0", true)) then
options.args.barTexture.values = LSM:HashTable("statusbar");
options.args.barTexture.dialogControl = "LSM30_Statusbar"
end
self:RegisterEvent("ZONE_CHANGED_NEW_AREA", "ZoneChange")
self:RegisterEvent("ZONE_CHANGED", "ZoneChange")
self:RegisterEvent("PLAYER_ENTERING_WORLD", "ZoneChange")
self:RegisterEvent("INSTANCE_ENCOUNTER_ENGAGE_UNIT", "UpdateBossState")
factory.parent:Show()
self:RebuildUI()
active = true
end
function core:OnDisable()
for i,v in pairs(healerFrames) do
factory:Free(v)
end
wipe(healerFrames)
factory.parent:Hide()
active = false
end
function core:ChatCommand(msg)
if msg == "test" and false then
self:SetTestMode()
else
InterfaceOptionsFrame_OpenToCategory("BalerocHealers")
end
end
-- ZoneChange(): Activate or deactivate addon operation
function core:ZoneChange()
if GetRealZoneText() == L["Firelands"] or debug then
active = true
self:RegisterEvent("UNIT_AURA") -- for buff status change
self:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED") -- for detecting spec
else
active = false
self:UnregisterEvent("UNIT_AURA")
self:UnregisterEvent("UNIT_SPELLCAST_SUCCEEDED")
end
self:UpdateBossState()
self:UpdateDisplayState()
end
-- UpdateBossState(): Checks for the presence of boss1 and tests the name
function core:UpdateBossState()
local boss = UnitName("boss1")
if boss == L["Baleroc"] then
display = true
self:SetTestMode(false)
-- Display state will be updated after TestMode is toggled off
else
display = false
self:UpdateDisplayState()
end
end
-- UpdateDisplayState(state): Shows or hides the frame
function core:UpdateDisplayState()
if not db.anchor.locked or testMode or (active and display) then
factory.parent:Show()
else
factory.parent:Hide()
end
end
-- UpdateFrames(): Apply settings to each frame
function core:UpdateFrames()
for _,frame in pairs(healerFrames) do
frame:ApplySettings()
end
if testMode then
for frame in factory:GetTestFrames() do
frame:ApplySettings()
end
end
factory:GetAnchor():ApplySettings()
BalerocHealersFrame:SetScale(db.scale)
end
-- SetLocked(locked): Sets status to locked and shows/hides anchor; called from options
function core:SetLocked(locked)
db.anchor.locked = locked
if factory.anchor then
if locked then
factory.anchor:Hide()
else
factory.anchor:Show()
end
end
self:RebuildUI()
end
-- SetTestMode(mode): Sets the test mode(true/false), or toggles it if mode is nil
function core:SetTestMode(mode)
testMode = mode==nil and not testMode or mode
self:UpdateFrames()
self:RebuildUI()
end
-- RebuildUI(): Sorts the healers list and positions frames relative to each
-- other and the anchor point
function core:RebuildUI()
-- Make sure our display is correct
self:UpdateDisplayState()
-- Setup base point
local last = factory.parent
local scale = last:GetEffectiveScale()
last:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", db.anchor.x/scale, db.anchor.y/scale)
-- Check for anchor
local anchor = factory:GetAnchor()
if not db.anchor.locked then
anchor:Show()
anchor:AttachTo(last)
last = anchor
else
anchor:Hide()
end
-- If we're in test mode, take care of that now and exit
if testMode then
-- Attach the frames; they will be shown using ApplySettings()
for frame in factory:GetTestFrames() do
frame:AttachTo(last)
last = frame
end
end
-- Pre-sort data
local playerZone = {}
wipe(sortRosterMap)
for i=1,GetNumRaidMembers() do
local name, _, group, _, _, _, zone = GetRaidRosterInfo(i)
if name then
sortRosterMap[name] = group
playerZone[name] = zone
end
end
-- Check for valid healers
local healerList = {}
-- Can't call remove here without affecting the order
for player in pairs(healerFrames) do
if UnitName("player") == player or (sortRosterMap[player] and playerZone[player] == L["Firelands"]) or debug then
table.insert(healerList, player)
healerFrames[player]:Show()
else
healerFrames[player]:Hide()
end
end
-- Sort healers
table.sort(healerList, SORT_FUNC[db.sort] or function() end)
-- Attach them
for _,v in ipairs(healerList) do
healerFrames[v]:AttachTo(last)
last = healerFrames[v]
end
end
-- UNIT_AURA(): Triggers Vital Spark/Flame updates on the unit's frame
function core:UNIT_AURA(_, unit) local name = UnitName(unit)
if active and healerFrames[name] then
healerFrames[name]:Update()
end
end
-- UNIT_SPELLCAST_SUCCEEDED(): Used for spec/healer detection
function core:UNIT_SPELLCAST_SUCCEEDED(_, unit, _, _, _, spellId)
local name = UnitName(unit)
if active and (UnitGUID(unit) == UnitGUID("player") or UnitInRaid(name)) then
local _, class = UnitClass(name)
if healerSpecs[class] and healerSpecs[class][spellId] ~= nil then
local isHealer = healerSpecs[class][spellId]
if isHealer and not rosterMap[name] then
-- New healer detected
self:InsertHealer(name)
self:RebuildUI()
elseif not isHealer and rosterMap[name] then
-- Old healer switched
self:RemoveHealer(name)
self:RebuildUI()
end
end
end
end
function core:InsertHealer(name)
rosterMap[name] = true
healerFrames[name] = factory:GetFrame():Initialize(name)
end
function core:RemoveHealer(name)
rosterMap[name] = false
factory:Free(healerFrames[name])
healerFrames[name] = nil
end
-- GetClassColors(name, tint): name accepts a player name or unlocalized class name
function core:GetClassColors(name, tint)
tint = tint or 1
local class = select(2, UnitClass(name)) or name
local colors = class and RAID_CLASS_COLORS[class]
if not colors then
return 1*tint, 1*tint, 1*tint, 1
else
return colors.r*tint, colors.g*tint, colors.b*tint, 1
end
end
-- factory -------------------------------------------------------------------
factory.frameList = {}
factory.parent = CreateFrame("Frame", "BalerocHealersFrame", UIParent)
factory.parent:SetSize(1, 1)
factory.anchor = nil
factory.testFrames = {}
-- GetAnchor(): Returns a reference to the anchor object, or creates one
function factory:GetAnchor()
if self.anchor then
return self.anchor
end
local frame = self:GetFrame()
frame:SetText("BalerocHealers", 0.4, 0.4, 0.4, 0.8)
frame:SetTexture(0.1, 0.1, 0.1, 0.8)
frame:SetBar(1, 0.1, 0.1, 0.1, 0.8)
frame:SetFrameStrata("LOW")
frame:SetMovable(true)
frame:SetClampedToScreen(true)
frame:RegisterForDrag("LeftButton")
frame:EnableMouse(true)
frame:SetScript("OnDragStart", function(self)
if not db.anchor.locked then
self:StartMoving()
end
end)
frame:SetScript("OnDragStop", function(self)
local scale = self:GetEffectiveScale()
self:StopMovingOrSizing()
db.anchor.x = self:GetLeft()*scale
db.anchor.y = self:GetTop()*scale
end)
-- Setup tooltip and click for options
frame:SetScript("OnEnter", function(self)
GameTooltip:SetOwner(self, "ANCHOR_CURSOR")
GameTooltip:ClearLines()
GameTooltip:AddLine(L["BalerocHealers Anchor"])
GameTooltip:AddLine(L["Click and drag to position"])
GameTooltip:AddLine(L["Right-click to open options"])
GameTooltip:Show()
end)
frame:SetScript("OnLeave", function(self)
GameTooltip:Hide()
end)
frame:SetScript("OnMouseUp", function(self, button)
if button == "RightButton" then
core:ChatCommand()
end
end)
-- Hide initially
frame:Hide()
self.anchor = frame
return frame
end
-- GetFrame(): Returns a reference to an unused frame
function factory:GetFrame()
for k,v in pairs(self.frameList) do
if not v then
self.frameList[k] = true
k:ApplySettings()
return k
end
end
local newFrame = self:CreateFrame():ApplySettings():Update()
self.frameList[newFrame] = true
return newFrame
end
-- Free(): Called when the UI is done using a frame
function factory:Free(frame)
if frame and self.frameList[frame] then
self.frameList[frame] = false
frame:Hide()
end
end
-- CreateFrame(): Called internally to create a new frame
function factory:CreateFrame()
local frame = CreateFrame("Frame", nil, self.parent)
-- Icon
local icon = CreateFrame("Frame", nil, frame)
icon:SetPoint("TOPLEFT", frame)
icon:SetFrameStrata("LOW")
icon.texture = icon:CreateTexture()
icon.texture:SetAllPoints()
-- Stacks
local stacks = frame:CreateFontString()
stacks:SetSize(60, 20)
stacks:SetPoint("BOTTOMRIGHT", icon, "BOTTOMRIGHT", -1, 1)
stacks:SetJustifyH("RIGHT")
stacks:SetJustifyV("BOTTOM")
-- Timer
local timer = frame:CreateFontString()
timer:SetSize(60, 20)
-- Clock
local clock = CreateFrame("Cooldown", nil, icon)
clock:SetAllPoints()
-- Bar
local bar = CreateFrame("StatusBar", nil, frame)
bar:SetFrameStrata("LOW")
bar:SetMinMaxValues(0, 1)
bar:SetValue(0)
-- Player Name
local playerName = frame:CreateFontString()
playerName:SetSize(300, 20)
frame.player = "" -- Player's name
frame.current = nil -- can be nil, VITAL_SPARK, or VITAL_FLAME
frame.torment = 0 -- expiration time for torment so we know when it refreshes
frame.testFrame = false -- flag indicating this is a test mode frame
frame.ApplySettings = function(self)
-- Showing frames only in the correct mode
if testMode == self.testFrame then
self:Show()
else
self:Hide()
end
self:SetSize(db.iconSize + db.barWidth + 5, db.iconSize + db.spacing)
icon:SetSize(db.iconSize, db.iconSize)
clock.noCooldownCount = db.clockDisableCDC
stacks:SetFont(db.numberFont, db.stacksFontSize, "OUTLINE")
timer:SetFont(db.numberFont, db.timerFontSize, "OUTLINE")
bar:ClearAllPoints()
bar:SetPoint("BOTTOMLEFT", icon, "BOTTOMRIGHT", db.barIconPadding, 0)
bar:SetSize(db.barWidth, db.barHeight)
playerName:SetFont(db.nameFont, db.playerFontSize, "OUTLINE")
local barTexture;
if LSM then
barTexture = LSM:Fetch("statusbar", db.barTexture);
else
barTexture = "Interface\\AddOns\\BalerocHealers\\Textures\\Minimalist.tga"
end
bar:SetStatusBarTexture(barTexture)
local align = db.playerTextAlign == "MIDDLE" and "" or db.playerTextAlign
playerName:ClearAllPoints()
playerName:SetPoint(align.."LEFT", icon, align.."RIGHT", 5, 0)
playerName:SetJustifyH("LEFT")
playerName:SetJustifyV(align)
timer:ClearAllPoints()
if db.timerLocation == "ICON" then
timer:SetPoint("CENTER", icon)
timer:SetJustifyH("CENTER")
timer:SetJustifyV("MIDDLE")
else
timer:SetPoint("BOTTOMLEFT", bar, "BOTTOMLEFT", 2, 2)
timer:SetJustifyH("LEFT")
timer:SetJustifyV("BOTTOM")
end
return self
end
frame.Initialize = function(self, player)
if UnitInRaid(player) or UnitName("player") == player then
self.player = player
self:SetBar(0, core:GetClassColors(player))
self:SetText(player, core:GetClassColors(player))
self:Show()
else
self:Hide()
end
return self
end
frame.Update = function(self)
-- Check tormented
if db.tormentOverride and UnitDebuff(self.player, TORMENTED) then
local _, _, texture, _, _, duration, expires = UnitDebuff(self.player, TORMENTED)
if self.current ~= TORMENTED or self.torment ~= expires then
-- First run: applied or refreshed
icon:Show()
clock:Show()
if db.timerEnable then timer:Show() end
if db.stacksAlways then stacks:Show() else stacks:Hide() end
if db.barEnable then bar:Show() end
-- tint bar
self:SetBar(0, core:GetClassColors(self.player, 0.5))
self:SetTexture(texture)
if db.clockEnable then self:SetCooldown(expires-duration, duration) end
self.current = TORMENTED
self.torment = expires
self:OnUpdate(expires, duration, bar, timer)
end
-- Always check for stack changes
local _, _, _, count = UnitBuff(self.player, VITAL_SPARK)
if count then
stacks:SetText(count)
end
-- Check buff
elseif UnitBuff(self.player, VITAL_FLAME) then
-- Vital Flame active
local _, _, texture, _, _, duration, expires = UnitBuff(self.player, VITAL_FLAME)
if self.current ~= VITAL_FLAME then
-- First run: show flame graphic, clock, and timer
icon:Show()
clock:Show()
if db.timerEnable then timer:Show() end
if db.stacksAlways then stacks:Show() else stacks:Hide() end
if db.barEnable then bar:Show() end
-- untint bar
self:SetBar(0, core:GetClassColors(self.player))
self:SetTexture(texture)
if db.clockEnable then self:SetCooldown(expires-duration, duration) end
self.current = VITAL_FLAME
self:OnUpdate(expires, duration, bar, timer)
end
elseif UnitBuff(self.player, VITAL_SPARK) then
-- Vital Spark active
local _, _, texture, count = UnitBuff(self.player, VITAL_SPARK)
if self.current ~= VITAL_SPARK then
-- First run: show spark graphic and stack count
icon:Show()
clock:Hide()
timer:Hide()
if db.stacksEnable then stacks:Show() end
bar:Hide()
self:SetTexture(texture)
self.current = VITAL_SPARK
end
-- Normal update
stacks:SetText(count)
else
-- Neither buff
if self.current ~= nil then
icon:Hide()
clock:Hide()
timer:Hide()
stacks:Hide()
bar:Hide()
self.current = nil
end
end
return self
end
frame.AttachTo = function(self, target)
local fromCorner, toCorner = "TOPLEFT", "BOTTOMLEFT"
if db.growthDirection == "UP" and target ~= factory.parent then
fromCorner, toCorner = toCorner, fromCorner
end
self:ClearAllPoints()
self:SetPoint(fromCorner, target, toCorner)
return self
end
frame.SetBar = function(self, value, r, g, b, a)
bar:SetValue(value)
bar:SetStatusBarColor(r, g, b, a)
return self
end
frame.SetText = function(self, text, r, g, b, a)
playerName:SetText(text)
playerName:SetTextColor(r, g, b, a)
return self
end
frame.SetTexture = function(self, texture, ...)
if type(texture) == "string" then
icon.texture:SetTexture(texture)
else
icon.texture:SetTexture(texture, ...)
end
return self
end
frame.SetCooldown = function(self, ...)
clock:SetCooldown(...)
return self
end
frame.OnUpdate = function(self, expires, duration, bar, timer)
self:SetScript("OnUpdate", function()
if expires-GetTime() <= 0 then
self:SetScript("OnUpdate", nil)
self.current = nil
self:Update()
else
bar:SetValue((expires-GetTime())/duration)
if expires-GetTime() < 10 then
timer:SetFormattedText("%.1f", expires-GetTime())
else
timer:SetFormattedText("%d", expires-GetTime())
end
end
end)
end
return frame
end
function factory:GetTestFrames()
if next(self.testFrames) then
return pairs(self.testFrames)
end
-- Create four frames
local VITAL_SPARK, VITAL_FLAME, TORMENTED = 99262, 99263, 99257
local names = {"Ethlin", "Moulder", "Lethee", "Ashunera"}
local classes = {"PALADIN", "PRIEST", "DRUID", "SHAMAN"}
local buff = {VITAL_FLAME, VITAL_SPARK, TORMENTED, VITAL_SPARK}
for i in ipairs(names) do
local frame = self:GetFrame()
frame:SetText(names[i], core:GetClassColors(classes[i]))
frame.Update = function(self)
self:SetTexture(select(3, GetSpellInfo(buff[i])))
end
frame:Update()
self.testFrames[frame] = true
end
return pairs(self.testFrames)
end