local function getSpellId(spellName)
assert (spellName ~= nil)
assert ('string' == type(spellName))
assert (string.len(spellName) >= 2)
assert (string.len(spellName) <= 256)
local _, spellId = GetSpellBookItemInfo(spellName)
if not spellId then
return
end
assert (spellId ~= nil)
assert ('number' == type(spellId))
assert (spellId >= 0)
return spellId
end
local function createButtonLabel(button)
assert (button ~= nil)
local n = (button:GetName() or '') .. 'Text'
local label = button:CreateFontString(n)
label:SetPoint('BOTTOMLEFT', -12, -6)
label:SetPoint('TOPRIGHT', 12, 6)
local fontObject = ConvictionFont or NumberFont_OutlineThick_Mono_Small
assert (fontObject ~= nil)
label:SetFontObject(fontObject)
button:SetFontString(label)
label:SetText(nil)
assert (label ~= nil)
return label
end
local function createButtonBackground(button)
assert (button ~= nil)
local background = button:CreateTexture(button:GetName() .. 'Background', 'BACKGROUND')
background:SetAllPoints()
background:SetTexture(0.4, 0.4, 1)
return background
end
local function createButtonArtwork(button)
assert (button ~= nil)
local buttonWidth = button:GetWidth()
local buttonHeight = button:GetHeight()
local padding = 3
local marginBottom = math.max(buttonWidth, buttonHeight) - math.min(buttonWidth, buttonHeight) + padding
local artwork = button:CreateTexture(button:GetName() .. 'Artwork', 'ARTWORK')
artwork:SetPoint('TOPLEFT', padding, -padding)
artwork:SetPoint('TOPRIGHT', -padding, -padding)
artwork:SetPoint('BOTTOMLEFT', padding, marginBottom)
artwork:SetPoint('BOTTOMRIGHT', padding, marginBottom)
local spellId = button:GetAttribute('spell')
local pictureFile
if spellId then
pictureFile = GetSpellTexture(spellId)
end
if not pictureFile then
pictureFile = "Interface\\Icons\\spell_nature_wispsplode"
end
artwork:SetTexture(pictureFile)
local texMargin = 1.0 / 8.0
artwork:SetTexCoord(texMargin, 1 - texMargin, texMargin, 1 - texMargin)
button:SetNormalTexture(artwork)
return artwork
end
local function updateButtonLabel(button)
assert (button ~= nil)
local label = button:GetFontString()
assert (label ~= nil)
local spellId = button:GetAttribute('spell')
if not spellId then
button:Hide()
return
end
assert (spellId ~= nil)
assert ('number' == type(spellId))
assert (spellId >= 0)
local cooldownInstance, duration = GetSpellCooldown(spellId)
if not cooldownInstance or not duration or cooldownInstance <= 0 or duration <= 0 then
label:SetText(nil)
return
end
local now = GetTime()
local durationRemaining = math.floor(duration - (now - cooldownInstance))
if durationRemaining > 60 then
label:SetText(nil)
return
end
label:SetText(tostring(durationRemaining))
end
local function updateButtonBackground(button, background)
assert (button ~= nil)
assert (background ~= nil)
local spellId = button:GetAttribute('spell')
if not spellId then
button:Hide()
return
end
assert (spellId ~= nil)
assert ('number' == type(spellId))
assert (spellId >= 0)
local usableFlag, manaFlag = IsUsableSpell(spellId)
local isUsable = 1 == usableFlag
local isOutOfMana = nil == usableFlag and 1 == manaFlag
local r, g, b
if isOutOfMana then
r = 0.3
g = 0.0
b = 1
elseif not isUsable then
r = 0
g = r
b = r
else
r = 0.4
g = 0.4
b = 1
end
background:SetTexture(r, g, b)
end
local function updateButtonArtwork(button, artwork)
assert (button ~= nil)
assert (artwork ~= nil)
local spellId = button:GetAttribute('spell')
if not spellId then
button:Hide()
return
end
assert (spellId ~= nil)
assert ('number' == type(spellId))
assert (spellId >= 0)
local a = 1
if 1 ~= IsUsableSpell(spellId) then
a = 1 / 3
end
if GetSpellCooldown(spellId) > 0 then
a = 1 / 3
end
artwork:SetVertexColor(1, 1, 1, a)
end
local function buttonUpdateProcessorFactory(self, background, artwork)
return function(button)
assert (button ~= nil)
assert (background ~= nil)
assert (artwork ~= nil)
local now = GetTime()
local lastUpdateInstance = button.lastUpdateInstance
if not lastUpdateInstance then
lastUpdateInstance = 0
end
local delaySec = 1 / 3
if now - lastUpdateInstance > delaySec then
lastUpdateInstance = now
updateButtonLabel(button)
updateButtonBackground(button, background)
updateButtonArtwork(button, artwork)
end
button.lastUpdateInstance = lastUpdateInstance
end
end
local function buttonEventProcessorFactory(self, background, artwork)
return function(button)
assert (button ~= nil)
assert (background ~= nil)
assert (artwork ~= nil)
updateButtonLabel(button)
updateButtonBackground(button, background)
updateButtonArtwork(button, artwork)
end
end
local function createButton(parentFrame, spellId)
assert (parentFrame ~= nil)
if not spellId then
return
end
assert (spellId ~= nil)
assert ('number' == type(spellId))
assert (spellId >= 0)
local size = math.min(parentFrame:GetWidth(), parentFrame:GetHeight())
size = math.min(math.max(12, size), 48)
local padding = 4
local t = {parentFrame:GetChildren()}
local i = #t or 0
local n = (parentFrame:GetName() or '') .. 'Button' .. tostring(i + 1)
local b = CreateFrame('BUTTON', n, parentFrame, 'SecureActionButtonTemplate')
b:SetSize(size, size)
local x = i * (size + padding)
local y = 0
b:SetPoint('BOTTOMLEFT', x, y)
b:SetAttribute('checkfocuscast', true)
b:SetAttribute('checkselfcast', true)
b:SetAttribute('type', 'spell')
b:SetAttribute('spell', spellId)
local label = createButtonLabel(b)
local background = createButtonBackground(b)
local artwork = createButtonArtwork(b)
local buttonUpdateProcessor = buttonUpdateProcessorFactory(b, background, artwork)
b:SetScript('OnUpdate', buttonUpdateProcessor)
local buttonEventProcessor = buttonEventProcessorFactory(b, background, artwork)
b:SetScript('OnEvent', buttonEventProcessor)
b:RegisterEvent('UNIT_SPELLCAST_FAILED_QUIET')
b:RegisterEvent('UNIT_SPELLCAST_INTERRUPTED')
b:RegisterEvent('UNIT_SPELLCAST_SENT')
b:RegisterEvent('UNIT_SPELLCAST_START')
b:RegisterEvent('UNIT_SPELLCAST_STOP')
b:RegisterEvent('UNIT_SPELLCAST_SUCCEEDED')
b:RegisterEvent('PLAYER_TARGET_CHANGED')
b:RegisterEvent('PLAYER_FOCUS_CHANGED')
b:RegisterEvent('SPELLS_CHANGED')
assert (b ~= nil)
return b
end
local function createSection(parentFrame, width, height)
assert (parentFrame ~= nil)
assert (width ~= nil)
assert ('number' == type(width))
assert (width >= 12 and width <= 1024)
assert (height ~= nil)
assert ('number' == type(height))
assert (height >= 12 and height <= 768)
local t = {parentFrame:GetChildren()}
local n = (parentFrame:GetName() or '') .. 'Section' .. tostring((#t or 0) + 1)
local f = CreateFrame('FRAME', n, parentFrame)
f:SetSize(width, height)
local x = parentFrame:GetWidth() / 2 - f:GetWidth() / 2
local y = 0
local padding = 6
local i = #t
while (i > 0) do
local r = t[i]
if not r then
break
end
y = y + r:GetHeight() + padding
i = i - 1
end
f:SetPoint('BOTTOMLEFT', x, y)
assert (f ~= nil)
return f
end
local function sortSection(section)
assert (section ~= nil)
local size = math.min(section:GetWidth(), section:GetHeight())
local padding = 4
local paddedSize = size + padding
local t = {section:GetChildren()}
local j = #t
local marginLeft = section:GetWidth() / 2 - (paddedSize * j / 2)
local i = 0
while (i < j) do
i = i + 1
local f = t[i]
if not f then
break
end
local x = marginLeft + paddedSize * (i - 1)
local y = 0
f:SetPoint('BOTTOMLEFT', x, y)
end
end
local function init(rootFrame)
assert (rootFrame ~= nil)
rootFrame:UnregisterAllEvents()
rootFrame:SetSize(144, 36)
local x = UIParent:GetWidth() / 2 - rootFrame:GetWidth() / 2
local y = 144
rootFrame:SetPoint('BOTTOMLEFT', x, y)
local section0 = createSection(rootFrame, rootFrame:GetWidth(), 24)
local section3 = createSection(rootFrame, rootFrame:GetWidth(), 36)
local section4 = createSection(rootFrame, rootFrame:GetWidth(), 36)
createButton(section4, getSpellId('Holy Shock'))
createButton(section4, getSpellId('Crusader Strike'))
createButton(section4, getSpellId("Avenger's Shield"))
createButton(section4, getSpellId('Hammer of the Righteous'))
createButton(section4, getSpellId('Word of Glory'))
createButton(section4, getSpellId('Light of Dawn'))
createButton(section4, getSpellId('Shield of the Righteous'))
createButton(section3, getSpellId('Lay on Hands'))
createButton(section3, getSpellId('Divine Shield'))
createButton(section3, getSpellId('Hand of Protection'))
createButton(section3, getSpellId('Hand of Sacrifice'))
createButton(section3, getSpellId('Divine Guardian'))
createButton(section3, getSpellId('Hand of Freedom'))
createButton(section3, getSpellId('Hammer of Justice'))
createButton(section3, getSpellId('Divine Protection'))
createButton(section3, getSpellId('Holy Shield'))
createButton(section3, getSpellId('Avenging Wrath'))
createButton(section3, getSpellId('Aura Mastery'))
createButton(section3, getSpellId('Divine Favor'))
createButton(section3, getSpellId('Divine Plea'))
createButton(section3, getSpellId('Rebuke'))
createButton(section0, getSpellId('Flash of Light'))
createButton(section0, getSpellId('Divine Light'))
createButton(section0, getSpellId('Holy Light'))
createButton(section0, getSpellId('Holy Radiance'))
createButton(section0, getSpellId('Hammer of Wrath'))
createButton(section0, getSpellId('Turn Evil'))
createButton(section0, getSpellId('Holy Wrath'))
createButton(section0, getSpellId('Exorcism'))
createButton(section0, getSpellId('Judgement'))
createButton(section0, getSpellId('Lifeblood'))
createButton(section0, getSpellId('Gift of the Naaru'))
createButton(section0, getSpellId('Stoneform'))
createButton(section0, getSpellId('Every Man for Himself'))
createButton(section0, getSpellId('War Stomp'))
sortSection(section0)
sortSection(section3)
sortSection(section4)
return rootFrame
end
local function main()
local rootFrame = CreateFrame('FRAME', 'ConvictionFrame', UIParent)
rootFrame:RegisterEvent('PLAYER_LOGIN')
rootFrame:SetScript('OnEvent', init)
end
do
main()
end