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.
937 lines
30 KiB
937 lines
30 KiB
-- Neuron is a World of Warcraft® user interface addon.
|
|
-- Copyright (c) 2017-2021 Britt W. Yazel
|
|
-- Copyright (c) 2006-2014 Connor H. Chenoweth
|
|
-- This code is licensed under the MIT license (see LICENSE for details)
|
|
|
|
local _, addonTable = ...
|
|
local Neuron = addonTable.Neuron
|
|
|
|
---@class NeuronButton : CheckButton @define Button as inheriting from CheckButton
|
|
local Button = setmetatable({}, {__index = CreateFrame("CheckButton")}) --this is the metatable for our button object
|
|
Neuron.Button = Button
|
|
|
|
local Skin = LibStub("Masque", true)
|
|
local L = LibStub("AceLocale-3.0"):GetLocale("Neuron")
|
|
|
|
LibStub("AceBucket-3.0"):Embed(Button)
|
|
LibStub("AceEvent-3.0"):Embed(Button)
|
|
LibStub("AceTimer-3.0"):Embed(Button)
|
|
LibStub("AceHook-3.0"):Embed(Button)
|
|
|
|
local ButtonEditor = addonTable.overlay.ButtonEditor
|
|
|
|
local DEFAULT_VIRTUAL_KEY = "LeftButton"
|
|
local NEURON_VIRTUAL_KEY = "Hotkey"
|
|
|
|
---Constructor: Create a new Neuron Button object (this is the base object for all Neuron button types)
|
|
---@param bar Bar @Bar Object this button will be a child of
|
|
---@param buttonID number @Button ID that this button will be assigned
|
|
---@param baseObj Button @Base object class for this specific button
|
|
---@param barClass string @Class type for the bar the button will be on
|
|
---@param objType string @Type of object this button will be
|
|
---@param template string @The template name that this frame will derive from
|
|
---@return NeuronButton @ A newly created Button object
|
|
function Button.new(bar, buttonID, baseObj, barClass, objType, template)
|
|
local newButton
|
|
local newButtonName = bar:GetName().."_"..objType..buttonID
|
|
|
|
if _G[newButtonName] then --try to reuse a current frame if it exists instead of making a new one
|
|
newButton = _G[newButtonName]
|
|
else
|
|
newButton = CreateFrame("CheckButton", newButtonName, bar, template) --create the new button frame using the desired parameters
|
|
setmetatable(newButton, {__index = baseObj})
|
|
end
|
|
|
|
--crosslink the bar and button for easy referencing
|
|
bar.buttons[buttonID] = newButton
|
|
newButton.bar = bar
|
|
|
|
newButton.class = barClass
|
|
newButton.id = buttonID
|
|
newButton.objType = objType
|
|
|
|
if not bar.data.buttons[buttonID] then --if the database for a bar doesn't exist (because it's a new bar) make a new table
|
|
bar.data.buttons[buttonID] = {}
|
|
end
|
|
newButton.DB = bar.data.buttons[buttonID] --set our button database table as the DB for our object
|
|
|
|
--this is a hack to add some unique information to an object so it doesn't get wiped from the database
|
|
if newButton.DB.config then
|
|
newButton.DB.config.date = date("%m/%d/%y %H:%M:%S")
|
|
end
|
|
|
|
return newButton
|
|
end
|
|
|
|
-------------------------------------------------
|
|
-----Base Methods that all buttons have----------
|
|
---These will often be overwritten per bar type--
|
|
------------------------------------------------
|
|
|
|
function Button.ChangeSelectedButton(newButton)
|
|
if newButton == Neuron.currentButton then
|
|
return
|
|
elseif newButton and Neuron.currentButton then
|
|
if Neuron.currentButton.bar ~= newButton.bar then
|
|
local bar = Neuron.currentButton.bar
|
|
|
|
if bar.handler:GetAttribute("assertstate") then
|
|
bar.handler:SetAttribute("state-"..bar.handler:GetAttribute("assertstate"), bar.handler:GetAttribute("activestate") or "homestate")
|
|
end
|
|
|
|
newButton.bar.handler:SetAttribute("fauxstate", bar.handler:GetAttribute("activestate"))
|
|
end
|
|
|
|
ButtonEditor.deactivate(Neuron.currentButton.editFrame)
|
|
ButtonEditor.activate(newButton.editFrame)
|
|
Neuron.currentButton = newButton
|
|
Neuron.currentBar = newButton.bar
|
|
elseif newButton and not Neuron.currentButton then
|
|
ButtonEditor.activate(newButton.editFrame)
|
|
Neuron.currentButton = newButton
|
|
Neuron.currentBar = newButton.bar
|
|
else -- not newButton and Neuron.currentButton
|
|
ButtonEditor.deactivate(Neuron.currentButton.editFrame)
|
|
Neuron.currentButton = nil
|
|
end
|
|
end
|
|
|
|
function Button:CancelCooldownTimer(stopAnimation)
|
|
--cleanup so on state changes the cooldowns don't persist
|
|
if self:TimeLeft(self.Cooldown.cooldownTimer) ~= 0 then
|
|
self:CancelTimer(self.Cooldown.cooldownTimer)
|
|
end
|
|
|
|
if self:TimeLeft(self.Cooldown.cooldownUpdateTimer) ~= 0 then
|
|
self:CancelTimer(self.Cooldown.cooldownUpdateTimer)
|
|
end
|
|
|
|
self.Countdown:SetText("")
|
|
|
|
self.Cooldown.showCountdownTimer = false
|
|
self.Cooldown.showCountdownAlpha = false
|
|
|
|
--clear previous sweeping cooldown animations
|
|
if stopAnimation then
|
|
CooldownFrame_Clear(self.Cooldown) --clear the cooldown frame
|
|
end
|
|
|
|
self:UpdateVisibility()
|
|
end
|
|
|
|
function Button:SetCooldownTimer(start, duration, enable, modrate, showCountdownTimer, color1, color2, showCountdownAlpha, charges, maxCharges)
|
|
if not self.isShown then --if the button isn't shown, don't do set any cooldowns
|
|
--if there's currently a timer, cancel it
|
|
self:CancelCooldownTimer(true)
|
|
return
|
|
end
|
|
|
|
if start and start > 0 and duration > 0 and enable > 0 then
|
|
|
|
if duration > 2 then --sets non GCD cooldowns
|
|
if charges and charges > 0 and maxCharges > 1 then
|
|
self.Cooldown:SetDrawSwipe(false);
|
|
CooldownFrame_Set(self.Cooldown, start, duration, enable, true, modrate) --set clock style cooldown animation. Show Draw Edge.
|
|
else
|
|
self.Cooldown:SetDrawSwipe(true);
|
|
CooldownFrame_Set(self.Cooldown, start, duration, enable, true, modrate) --set clock style cooldown animation for ability cooldown. Show Draw Edge.
|
|
end
|
|
else --sets GCD cooldowns
|
|
self.Cooldown:SetDrawSwipe(true);
|
|
CooldownFrame_Set(self.Cooldown, start, duration, enable, false, modrate) --don't show the Draw Edge for the GCD
|
|
end
|
|
|
|
--this is only for abilities that have CD's >4 sec. Any less than that and we don't want to track the CD with text or alpha, just with the standard animation
|
|
if duration >= Neuron.TIMERLIMIT then --if spells have a cooldown less than 4sec then don't show a full cooldown
|
|
|
|
if showCountdownTimer or showCountdownAlpha then --only set a timer if we explicitely want to (this saves CPU for a lot of people)
|
|
|
|
--set a local variable to the boolean state of either Timer or the Alpha
|
|
self.Cooldown.showCountdownTimer = showCountdownTimer
|
|
self.Cooldown.showCountdownAlpha = showCountdownAlpha
|
|
|
|
|
|
self.Cooldown.charges = charges or 0 --used to know if we should set alpha on the button (if cdAlpha is enabled) immediately, or if we need to wait for charges to run out
|
|
|
|
--clear old timer before starting a new one
|
|
if self:TimeLeft(self.Cooldown.cooldownTimer) ~= 0 then
|
|
self:CancelTimer(self.Cooldown.cooldownTimer)
|
|
end
|
|
|
|
--Get the remaining time left so when we re-call the timer when switching back to a state it has the correct time left instead of the full time
|
|
local timeleft = duration-(GetTime()-start)
|
|
|
|
--safety check in case some timeleft value comes back ridiculously long. This happened once after a weird game glitch, it came back as like 42000000. We should cap it at 1 day max (even that's overkill)
|
|
if timeleft > 86400 then
|
|
timeleft = 86400
|
|
end
|
|
|
|
--set timer that is both our cooldown counter, but also the cancels the repeating updating timer at the end
|
|
self.Cooldown.cooldownTimer = self:ScheduleTimer(function() self:CancelTimer(self.Cooldown.cooldownUpdateTimer) end, timeleft + 1) --add 1 to the length of the timer to keep it going for 1 second once the spell cd is over (to fully finish the animations/alpha transition)
|
|
|
|
--clear old timer before starting a new one
|
|
if self:TimeLeft(self.Cooldown.cooldownUpdateTimer) ~= 0 then
|
|
self:CancelTimer(self.Cooldown.cooldownUpdateTimer)
|
|
end
|
|
|
|
--schedule a repeating timer that is physically keeping track of the countdown and switching the alpha and count text
|
|
self.Cooldown.cooldownUpdateTimer = self:ScheduleRepeatingTimer("CooldownCounterUpdate", 0.20)
|
|
self.Cooldown.normalcolor = color1
|
|
self.Cooldown.expirecolor = color2
|
|
else
|
|
self.Cooldown.showCountdownTimer = false
|
|
self.Cooldown.showCountdownAlpha = false
|
|
end
|
|
|
|
else
|
|
self:CancelCooldownTimer(false)
|
|
end
|
|
else
|
|
self:CancelCooldownTimer(true)
|
|
end
|
|
end
|
|
|
|
---this function is called by a repeating timer set in SetCooldownTimer every 0.2sec, which is autoGmatically is set to terminate 1sec after the cooldown timer ends
|
|
---this function's job is to overlay the countdown text on a button and set the button's cooldown alpha
|
|
function Button:CooldownCounterUpdate()
|
|
local coolDown, formatted, size
|
|
|
|
local normalcolor = self.Cooldown.normalcolor
|
|
local expirecolor = self.Cooldown.expirecolor
|
|
|
|
coolDown = self:TimeLeft(self.Cooldown.cooldownTimer) - 1 --subtract 1 from the timer because we added 1 in SetCooldownTimer to keep the timer runing for 1 extra second after the spell
|
|
|
|
if self.Cooldown.showCountdownTimer then --check if flag is set, otherwise skip
|
|
|
|
if coolDown < 1 then
|
|
if coolDown <= 0 then
|
|
self.Countdown:SetText("")
|
|
self.Cooldown.expirecolor = nil
|
|
self.Cooldown.cdsize = nil
|
|
|
|
elseif coolDown > 0 then
|
|
if self.Cooldown.alphafade then
|
|
self.Cooldown:SetAlpha(coolDown)
|
|
end
|
|
end
|
|
else
|
|
if coolDown >= 86400 then --append a "d" if the timer is longer than 1 day
|
|
formatted = string.format( "%.0f", coolDown/86400)
|
|
formatted = formatted.."d"
|
|
size = self:GetWidth()*0.3
|
|
self.Countdown:SetTextColor(normalcolor[1], normalcolor[2], normalcolor[3])
|
|
|
|
elseif coolDown >= 3600 then --append a "h" if the timer is longer than 1 hour
|
|
formatted = string.format( "%.0f",coolDown/3600)
|
|
formatted = formatted.."h"
|
|
size = self:GetWidth()*0.3
|
|
self.Countdown:SetTextColor(normalcolor[1], normalcolor[2], normalcolor[3])
|
|
|
|
elseif coolDown >= 60 then --append a "m" if the timer is longer than 1 min
|
|
formatted = string.format( "%.0f",coolDown/60)
|
|
formatted = formatted.."m"
|
|
size = self:GetWidth()*0.3
|
|
self.Countdown:SetTextColor(normalcolor[1], normalcolor[2], normalcolor[3])
|
|
|
|
elseif coolDown >=6 then --this is the 'normal' countdown text state
|
|
formatted = string.format( "%.0f",coolDown)
|
|
size = self:GetWidth()*0.45
|
|
self.Countdown:SetTextColor(normalcolor[1], normalcolor[2], normalcolor[3])
|
|
|
|
elseif coolDown < 6 then --this is the countdown text state but with the text larger and set to the expire color (usually red)
|
|
formatted = string.format( "%.0f",coolDown)
|
|
size = self:GetWidth()*0.6
|
|
if expirecolor then
|
|
self.Countdown:SetTextColor(expirecolor[1], expirecolor[2], expirecolor[3])
|
|
expirecolor = nil
|
|
end
|
|
|
|
end
|
|
|
|
if not self.Cooldown.cdsize or self.Cooldown.cdsize ~= size then
|
|
self.Countdown:SetFont(STANDARD_TEXT_FONT, size, "OUTLINE")
|
|
self.Cooldown.cdsize = size
|
|
end
|
|
|
|
self.Countdown:SetText(formatted)
|
|
|
|
end
|
|
end
|
|
|
|
if self.Cooldown.showCountdownAlpha and self.Cooldown.charges == 0 then --check if flag is set and if charges are nil or zero, otherwise skip
|
|
if self.Cooldown.showCountdownAlpha and self.Cooldown.charges == 0 then --check if flag is set and if charges are nil or zero, otherwise skip
|
|
|
|
if coolDown > 0 then
|
|
self:SetAlpha(0.2)
|
|
else
|
|
self:SetAlpha(1)
|
|
end
|
|
else
|
|
self:SetAlpha(1)
|
|
end
|
|
end
|
|
end
|
|
|
|
function Button:LoadDataFromDatabase(curSpec, curState)
|
|
self.config = self.DB.config
|
|
self.keys = self.DB.keys
|
|
|
|
if self.class ~= "ActionBar" then
|
|
self.data = self.DB.data
|
|
else
|
|
self.statedata = self.DB[curSpec] --all of the states for a given spec
|
|
self.data = self.statedata[curState] --loads a single state of a single spec into self.data
|
|
|
|
for state, data in pairs(self.statedata) do
|
|
self:SetAttribute(state.."-macro_Text", data.macro_Text)
|
|
self:SetAttribute(state.."-actionID", data.actionID)
|
|
end
|
|
end
|
|
end
|
|
|
|
function Button:SetDefaults(defaults)
|
|
if not defaults then
|
|
return
|
|
end
|
|
|
|
if defaults.config then
|
|
for k, v in pairs(defaults.config) do
|
|
self.DB.config[k] = v
|
|
end
|
|
end
|
|
|
|
if defaults.keys then
|
|
for k, v in pairs(defaults.keys) do
|
|
self.DB.keys[k] = v
|
|
end
|
|
end
|
|
end
|
|
|
|
function Button:SetSkinned(flyout)
|
|
if Skin then
|
|
local btnData = {
|
|
Normal = self.Normal,
|
|
Icon = self.Icon,
|
|
HotKey = self.Hotkey,
|
|
Count = self.Count,
|
|
Name = self.Name,
|
|
Border = self.Border,
|
|
Shine = self.Shine,
|
|
Cooldown = self.Cooldown,
|
|
AutoCastable = self.AutoCastable,
|
|
Checked = self.Checked,
|
|
Pushed = self.Pushed,
|
|
Disabled = self:GetDisabledTexture(),
|
|
Highlight = self.Highlight,
|
|
}
|
|
if flyout then
|
|
Skin:Group("Neuron", self.anchor.bar.data.name):AddButton(self, btnData, "Action")
|
|
else
|
|
Skin:Group("Neuron", self.bar.data.name):AddButton(self, btnData, "Action")
|
|
end
|
|
end
|
|
end
|
|
|
|
function Button:HasAction()
|
|
if self.actionID then
|
|
if self.actionID == 0 then
|
|
return true
|
|
elseif HasAction(self.actionID) then
|
|
return true
|
|
elseif self.class == "PetBar" and GetPetActionInfo(self.actionID) then
|
|
return true
|
|
end
|
|
elseif self.spell or self.item then
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
|
|
-----------------------------------------------------------------------------------------
|
|
------------------------------------- Update Functions ----------------------------------
|
|
-----------------------------------------------------------------------------------------
|
|
|
|
--- Applies binding to button
|
|
function Button:ApplyBindings()
|
|
local virtualKey
|
|
|
|
---checks if the button is a Neuron action or a special Blizzard action (such as a zone ability)
|
|
---this is necessary because Blizzard buttons usually won't work and can give very weird results
|
|
---if clicked with a virtual key other than the default "LeftButton"
|
|
if self.class == "ActionBar" then
|
|
virtualKey = NEURON_VIRTUAL_KEY
|
|
else
|
|
virtualKey = DEFAULT_VIRTUAL_KEY
|
|
end
|
|
|
|
if self:IsVisible() or self:GetParent():GetAttribute("concealed") then
|
|
self.keys.hotKeys:gsub("[^:]+", function(key) SetOverrideBindingClick(self, self.keys.hotKeyPri, key, self:GetName(), virtualKey) end)
|
|
end
|
|
|
|
if not InCombatLockdown() then
|
|
self:SetAttribute("hotkeypri", self.keys.hotKeyPri)
|
|
self:SetAttribute("hotkeys", self.keys.hotKeys)
|
|
end
|
|
|
|
self.Hotkey:SetText(Button.hotKeyText(self.keys.hotKeys:match("^:([^:]+)") or ""))
|
|
|
|
if self.bar:GetShowBindText() then
|
|
self.Hotkey:Show()
|
|
else
|
|
self.Hotkey:Hide()
|
|
end
|
|
end
|
|
|
|
-- spell in action, extra, pet, zone,
|
|
-- actionID in action, extra, pet,
|
|
-- spellID in action, extra, zone
|
|
-- macroequipmentset used in action
|
|
-- item in action
|
|
-- macro_BlizzMacro in action
|
|
function Button:UpdateAll()
|
|
self:UpdateData()
|
|
self:UpdateIcon()
|
|
self:UpdateStatus()
|
|
self:UpdateCooldown()
|
|
self:UpdateUsable()
|
|
end
|
|
|
|
function Button:UpdateData()
|
|
-- empty --
|
|
end
|
|
|
|
function Button:UpdateIcon()
|
|
-- empty --
|
|
end
|
|
|
|
function Button:UpdateNormalTexture()
|
|
local actionTexture, noactionTexture
|
|
|
|
if self.__MSQ_NormalSkin and self.__MSQ_NormalSkin.Texture then
|
|
actionTexture = self.__MSQ_NormalSkin.Texture
|
|
noactionTexture = self.__MSQ_NormalSkin.Texture.EmptyTexture
|
|
else
|
|
actionTexture = "Interface\\Buttons\\UI-Quickslot2"
|
|
noactionTexture = "Interface\\Buttons\\UI-Quickslot"
|
|
end
|
|
|
|
if self:HasAction() then
|
|
self:SetNormalTexture(actionTexture or "")
|
|
self:GetNormalTexture():SetVertexColor(1,1,1,1)
|
|
else
|
|
self:SetNormalTexture(noactionTexture or "")
|
|
self:GetNormalTexture():SetVertexColor(1,1,1,0.5)
|
|
end
|
|
end
|
|
|
|
function Button:UpdateVisibility()
|
|
if self.isShown or Neuron.barEditMode or (Neuron.buttonEditMode and self.editFrame) or (Neuron.bindingMode and self.keybindFrame) then
|
|
self.isShown = true
|
|
else
|
|
self.isShown = false
|
|
end
|
|
|
|
if self.isShown then
|
|
self:SetAlpha(1)
|
|
else
|
|
self:SetAlpha(0)
|
|
end
|
|
end
|
|
|
|
--- Returns the text value of a keybind
|
|
--- @param key string @The key to look up
|
|
--- @return string @The text value for the key
|
|
function Button.hotKeyText(key)
|
|
local keytext
|
|
if key:find("Button") then
|
|
keytext = key:gsub("([Bb][Uu][Tt][Tt][Oo][Nn])(%d+)","m%2")
|
|
elseif key:find("NUMPAD") then
|
|
keytext = key:gsub("NUMPAD","n")
|
|
keytext = keytext:gsub("DIVIDE","/")
|
|
keytext = keytext:gsub("MULTIPLY","*")
|
|
keytext = keytext:gsub("MINUS","-")
|
|
keytext = keytext:gsub("PLUS","+")
|
|
keytext = keytext:gsub("DECIMAL",".")
|
|
elseif key:find("MOUSEWHEEL") then
|
|
keytext = key:gsub("MOUSEWHEEL","mw")
|
|
keytext = keytext:gsub("UP","U")
|
|
keytext = keytext:gsub("DOWN","D")
|
|
else
|
|
keytext = key
|
|
end
|
|
keytext = keytext:gsub("ALT%-","a")
|
|
keytext = keytext:gsub("CTRL%-","c")
|
|
keytext = keytext:gsub("SHIFT%-","s")
|
|
keytext = keytext:gsub("INSERT","Ins")
|
|
keytext = keytext:gsub("DELETE","Del")
|
|
keytext = keytext:gsub("HOME","Home")
|
|
keytext = keytext:gsub("END","End")
|
|
keytext = keytext:gsub("PAGEUP","PgUp")
|
|
keytext = keytext:gsub("PAGEDOWN","PgDn")
|
|
keytext = keytext:gsub("BACKSPACE","Bksp")
|
|
keytext = keytext:gsub("SPACE","Spc")
|
|
return keytext
|
|
end
|
|
|
|
-----------------------------------------------------------------------------------------
|
|
------------------------------------- Set Count/Charge ----------------------------------
|
|
-----------------------------------------------------------------------------------------
|
|
|
|
function Button:UpdateCount()
|
|
if self.actionID then
|
|
self:UpdateActionCount()
|
|
elseif self.spell then
|
|
self:UpdateSpellCount()
|
|
elseif self.item then
|
|
self:UpdateItemCount()
|
|
else
|
|
self.Count:SetText("")
|
|
end
|
|
end
|
|
|
|
|
|
---Updates the buttons "count", i.e. the spell charges
|
|
function Button:UpdateSpellCount()
|
|
local charges, maxCharges = GetSpellCharges(self.spell)
|
|
local count = GetSpellCount(self.spell)
|
|
|
|
if maxCharges and maxCharges > 1 then
|
|
self.Count:SetText(charges)
|
|
elseif count and count > 0 then
|
|
self.Count:SetText(count)
|
|
else
|
|
self.Count:SetText("")
|
|
end
|
|
end
|
|
|
|
|
|
---Updates the buttons "count", i.e. the item stack size
|
|
function Button:UpdateItemCount()
|
|
local count = GetItemCount(self.item,nil,true)
|
|
|
|
if count and count > 1 then
|
|
self.Count:SetText(count)
|
|
else
|
|
self.Count:SetText("")
|
|
end
|
|
end
|
|
|
|
|
|
function Button:UpdateActionCount()
|
|
local count = GetActionCount(self.actionID)
|
|
|
|
if count and count > 0 then
|
|
self.Count:SetText(count)
|
|
else
|
|
self.Count:SetText("")
|
|
end
|
|
end
|
|
|
|
-----------------------------------------------------------------------------------------
|
|
------------------------------------- Set Cooldown --------------------------------------
|
|
-----------------------------------------------------------------------------------------
|
|
|
|
function Button:UpdateCooldown()
|
|
if self.actionID then
|
|
self:UpdateActionCooldown()
|
|
elseif self.spell then
|
|
self:UpdateSpellCooldown()
|
|
elseif self.item then
|
|
self:UpdateItemCooldown()
|
|
else
|
|
--this is super important for removing CD's from empty buttons, like when switching states. You don't want the CD from one state to show on a different state.
|
|
self:CancelCooldownTimer(true)
|
|
end
|
|
end
|
|
|
|
function Button:UpdateSpellCooldown()
|
|
if self.spell and self.isShown then
|
|
local start, duration, enable, modrate = GetSpellCooldown(self.spell)
|
|
local charges, maxCharges, chStart, chDuration, chargemodrate = GetSpellCharges(self.spell)
|
|
|
|
if charges and maxCharges and maxCharges > 0 and charges < maxCharges then
|
|
self:SetCooldownTimer(chStart, chDuration, enable, chargemodrate, self.bar:GetShowCooldownText(), self.bar:GetCooldownColor1(), self.bar:GetCooldownColor2(), self.bar:GetShowCooldownAlpha(), charges, maxCharges) --only evoke charge cooldown (outer border) if charges are present and less than maxCharges (this is the case with the GCD)
|
|
else
|
|
self:SetCooldownTimer(start, duration, enable, modrate, self.bar:GetShowCooldownText(), self.bar:GetCooldownColor1(), self.bar:GetCooldownColor2(), self.bar:GetShowCooldownAlpha()) --call standard cooldown, handles both abilty cooldowns and GCD
|
|
end
|
|
else
|
|
self:CancelCooldownTimer(true)
|
|
end
|
|
end
|
|
|
|
function Button:UpdateItemCooldown()
|
|
if self.item and self.isShown then
|
|
local start, duration, enable, modrate
|
|
if Neuron.itemCache[self.item:lower()] then
|
|
start, duration, enable, modrate = C_Container.GetItemCooldown(Neuron.itemCache[self.item:lower()])
|
|
else
|
|
local itemID = GetItemInfoInstant(self.item)
|
|
start, duration, enable, modrate = C_Container.GetItemCooldown(itemID)
|
|
end
|
|
self:SetCooldownTimer(start, duration, enable, modrate, self.bar:GetShowCooldownText(), self.bar:GetCooldownColor1(), self.bar:GetCooldownColor2(), self.bar:GetShowCooldownAlpha())
|
|
else
|
|
self:CancelCooldownTimer(true)
|
|
end
|
|
end
|
|
|
|
function Button:UpdateActionCooldown()
|
|
if self.actionID and self.isShown then
|
|
if HasAction(self.actionID) then
|
|
local start, duration, enable, modrate = GetActionCooldown(self.actionID)
|
|
self:SetCooldownTimer(start, duration, enable, modrate, self.bar:GetShowCooldownText(), self.bar:GetCooldownColor1(), self.bar:GetCooldownColor2(), self.bar:GetShowCooldownAlpha())
|
|
end
|
|
else
|
|
self:CancelCooldownTimer(true)
|
|
end
|
|
end
|
|
|
|
-----------------------------------------------------------------------------------------
|
|
------------------------------------- Set Usable ----------------------------------------
|
|
-----------------------------------------------------------------------------------------
|
|
|
|
function Button:UpdateUsable()
|
|
if Neuron.buttonEditMode or Neuron.bindingMode then
|
|
self.Icon:SetVertexColor(0.2, 0.2, 0.2)
|
|
elseif self.actionID then
|
|
self:UpdateUsableAction()
|
|
elseif self.spell then
|
|
self:UpdateUsableSpell()
|
|
elseif self.item then
|
|
self:UpdateUsableItem()
|
|
else
|
|
self.Icon:SetVertexColor(1.0, 1.0, 1.0)
|
|
end
|
|
end
|
|
|
|
function Button:UpdateUsableSpell()
|
|
local isUsable, notEnoughMana = IsUsableSpell(self.spell)
|
|
|
|
if notEnoughMana and self.bar:GetManaColor() then
|
|
self.Icon:SetVertexColor(self.bar:GetManaColor()[1], self.bar:GetManaColor()[2], self.bar:GetManaColor()[3])
|
|
elseif isUsable then
|
|
if self.bar:GetShowRangeIndicator() and IsSpellInRange(self.spell, self.unit) == 0 then
|
|
self.Icon:SetVertexColor(self.bar:GetRangeColor()[1], self.bar:GetRangeColor()[2], self.bar:GetRangeColor()[3])
|
|
elseif self.bar:GetShowRangeIndicator() and Neuron.spellCache[self.spell:lower()] and IsSpellInRange(Neuron.spellCache[self.spell:lower()].index,"spell", self.unit) == 0 then
|
|
self.Icon:SetVertexColor(self.bar:GetRangeColor()[1], self.bar:GetRangeColor()[2], self.bar:GetRangeColor()[3])
|
|
else
|
|
self.Icon:SetVertexColor(1.0, 1.0, 1.0)
|
|
end
|
|
else
|
|
self.Icon:SetVertexColor(0.4, 0.4, 0.4)
|
|
end
|
|
end
|
|
|
|
function Button:UpdateUsableItem()
|
|
local isUsable, notEnoughMana = IsUsableItem(self.item)
|
|
|
|
--for some reason toys don't show as usable items, so this is a workaround for that
|
|
if not isUsable then
|
|
local itemID = GetItemInfoInstant(self.item)
|
|
if Neuron.isWoWRetail and itemID and PlayerHasToy(itemID) then
|
|
isUsable = true
|
|
end
|
|
end
|
|
|
|
if notEnoughMana and self.bar:GetManaColor() then
|
|
self.Icon:SetVertexColor(self.bar:GetManaColor()[1], self.bar:GetManaColor()[2], self.bar:GetManaColor()[3])
|
|
elseif isUsable then
|
|
if self.bar:GetShowRangeIndicator() and IsItemInRange(self.item, self.unit) == 0 then
|
|
self.Icon:SetVertexColor(self.bar:GetRangeColor()[1], self.bar:GetRangeColor()[2], self.bar:GetRangeColor()[3])
|
|
elseif Neuron.itemCache[self.item:lower()] and self.bar:GetShowRangeIndicator() and IsItemInRange(Neuron.itemCache[self.item:lower()], self.unit) == 0 then
|
|
self.Icon:SetVertexColor(self.bar:GetRangeColor()[1], self.bar:GetRangeColor()[2], self.bar:GetRangeColor()[3])
|
|
else
|
|
self.Icon:SetVertexColor(1.0, 1.0, 1.0)
|
|
end
|
|
else
|
|
self.Icon:SetVertexColor(0.4, 0.4, 0.4)
|
|
end
|
|
end
|
|
|
|
function Button:UpdateUsableAction()
|
|
if self.actionID == 0 then
|
|
self.Icon:SetVertexColor(1.0, 1.0, 1.0)
|
|
return
|
|
end
|
|
|
|
local isUsable, notEnoughMana = IsUsableAction(self.actionID)
|
|
|
|
if notEnoughMana and self.bar:GetManaColor() then
|
|
self.Icon:SetVertexColor(self.bar:GetManaColor()[1], self.bar:GetManaColor()[2], self.bar:GetManaColor()[3])
|
|
elseif isUsable then
|
|
if self.bar:GetShowRangeIndicator() and IsActionInRange(self.spell, self.unit) == 0 then
|
|
self.Icon:SetVertexColor(self.bar:GetRangeColor()[1], self.bar:GetRangeColor()[2], self.bar:GetRangeColor()[3])
|
|
else
|
|
self.Icon:SetVertexColor(1.0, 1.0, 1.0)
|
|
end
|
|
else
|
|
self.Icon:SetVertexColor(0.4, 0.4, 0.4)
|
|
end
|
|
end
|
|
|
|
-----------------------------------------------------------------------------------------
|
|
-------------------------------------- Set Status ---------------------------------------
|
|
-----------------------------------------------------------------------------------------
|
|
|
|
---used by actionbutton, extra, zone, exit
|
|
function Button:UpdateStatus()
|
|
-- actionID in pet, action, extra
|
|
if self.actionID then
|
|
self:UpdateActionStatus()
|
|
-- macroequipmentset used in action and flyout
|
|
elseif self:GetMacroEquipmentSet() then
|
|
self.Name:SetText(self:GetMacroName())
|
|
-- spell in zone, extra, pet, action
|
|
-- spellID in zone, extra, action
|
|
elseif self.spell then
|
|
self:UpdateSpellStatus()
|
|
-- item in action
|
|
elseif self.item then
|
|
self:UpdateItemStatus()
|
|
-- macro must go after spells and items, for blizz macro #showtooltip to work
|
|
-- macro_BlizzMacro in action
|
|
elseif self:GetMacroBlizzMacro() then
|
|
self.Name:SetText(self:GetMacroName())
|
|
else
|
|
self:SetChecked(false)
|
|
self.Name:SetText("")
|
|
self.Count:SetText("")
|
|
end
|
|
end
|
|
|
|
function Button:UpdateSpellStatus()
|
|
if IsCurrentSpell(self.spell) or IsAutoRepeatSpell(self.spell) then
|
|
self:SetChecked(true)
|
|
else
|
|
self:SetChecked(false)
|
|
end
|
|
|
|
self.Name:SetText(self:GetMacroName())
|
|
self:UpdateCount()
|
|
self:UpdateUsable()
|
|
end
|
|
|
|
function Button:UpdateItemStatus()
|
|
if IsCurrentItem(self.item) then
|
|
self:SetChecked(true)
|
|
else
|
|
self:SetChecked(false)
|
|
end
|
|
|
|
self.Name:SetText(self:GetMacroName())
|
|
self:UpdateCount()
|
|
self:UpdateUsable()
|
|
end
|
|
|
|
function Button:UpdateActionStatus()
|
|
local name
|
|
|
|
if self.actionID then
|
|
if IsCurrentAction(self.actionID) or IsAutoRepeatAction(self.actionID) then
|
|
self:SetChecked(true)
|
|
else
|
|
self:SetChecked(false)
|
|
end
|
|
|
|
--find out the action name
|
|
local type, id, _ = GetActionInfo(self.actionID)
|
|
if type == "spell" then
|
|
name = GetSpellInfo(id)
|
|
elseif type == "item" then
|
|
name = GetItemInfo(id)
|
|
end
|
|
else
|
|
self:SetChecked(false)
|
|
end
|
|
|
|
if name then
|
|
self.Name:SetText(name)
|
|
else
|
|
self.Name:SetText("")
|
|
end
|
|
|
|
self:UpdateUsable()
|
|
end
|
|
|
|
-----------------------------------------------------------------------------------------
|
|
------------------------------------- Set Tooltip ---------------------------------------
|
|
-----------------------------------------------------------------------------------------
|
|
|
|
function Button:UpdateTooltip()
|
|
if self.bar:GetTooltipOption() ~= "off" then --if the bar isn't showing tooltips, don't proceed
|
|
|
|
--if we are in combat and we don't have tooltips enable in-combat, don't go any further
|
|
if InCombatLockdown() and not self.bar:GetTooltipCombat() then
|
|
return
|
|
end
|
|
|
|
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
|
|
|
|
if self.actionID then
|
|
self:UpdateActionTooltip()
|
|
elseif self:GetMacroEquipmentSet() then
|
|
GameTooltip:SetEquipmentSet(self:GetMacroEquipmentSet())
|
|
elseif self.spell then
|
|
self:UpdateSpellTooltip()
|
|
elseif self.item then
|
|
self:UpdateItemTooltip()
|
|
-- macro must go after spells and items, for blizz macro #showtooltip to work
|
|
elseif self:GetMacroBlizzMacro() then
|
|
GameTooltip:SetText(self:GetMacroName())
|
|
elseif self:GetMacroText() and #self:GetMacroText() > 0 then
|
|
GameTooltip:SetText(self:GetMacroName())
|
|
end
|
|
|
|
-- it seems that recently wow decided to start hiding tooltips
|
|
-- if you call "show" on them and they are already visible
|
|
if not GameTooltip:IsShown() then
|
|
GameTooltip:Show()
|
|
end
|
|
|
|
end
|
|
end
|
|
|
|
function Button:UpdateSpellTooltip()
|
|
if self.spell and self.spellID then --try to get the correct spell from the spellbook first
|
|
if self.bar:GetTooltipOption() == "normal" then
|
|
GameTooltip:SetSpellByID(self.spellID)
|
|
elseif self.bar:GetTooltipOption() == "minimal" then
|
|
GameTooltip:SetText(self.spell, 1, 1, 1)
|
|
end
|
|
elseif Neuron.spellCache[self.spell:lower()] then --if the spell isn't in the spellbook, check our spell cache
|
|
if self.bar:GetTooltipOption() == "normal" then
|
|
GameTooltip:SetSpellByID(Neuron.spellCache[self.spell:lower()].spellID)
|
|
elseif self.bar:GetTooltipOption() == "minimal" then
|
|
GameTooltip:SetText(Neuron.spellCache[self.spell:lower()].spellName, 1, 1, 1)
|
|
end
|
|
else
|
|
GameTooltip:SetText(UNKNOWN, 1, 1, 1)
|
|
end
|
|
end
|
|
|
|
function Button:UpdateItemTooltip()
|
|
local name, link = GetItemInfo(self.item)
|
|
name = name or Neuron.itemCache[self.item:lower()]
|
|
link = link or "item:"..name..":0:0:0:0:0:0:0"
|
|
|
|
if not name or not link then
|
|
return
|
|
end
|
|
|
|
if self.bar:GetTooltipOption() == "normal" then
|
|
GameTooltip:SetHyperlink(link)
|
|
elseif self.bar:GetTooltipOption() == "minimal" then
|
|
GameTooltip:SetText(name, 1, 1, 1)
|
|
end
|
|
end
|
|
|
|
function Button:UpdateActionTooltip()
|
|
if HasAction(self.actionID) then
|
|
if self.bar:GetTooltipOption() == "normal" or self.bar:GetTooltipOption() == "minimal" then
|
|
GameTooltip:SetAction(self.actionID)
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
-----------------------------------------------------
|
|
-------------------Sets and Gets---------------------
|
|
-----------------------------------------------------
|
|
|
|
--Macro Icon
|
|
function Button:SetMacroIcon(newIcon)
|
|
if newIcon then
|
|
self.data.macro_Icon = newIcon
|
|
else
|
|
self.data.macro_Icon = false
|
|
end
|
|
end
|
|
|
|
function Button:GetMacroIcon()
|
|
return self.data.macro_Icon
|
|
end
|
|
|
|
|
|
--Macro Text
|
|
function Button:SetMacroText(newText)
|
|
if newText then
|
|
self.data.macro_Text = newText
|
|
else
|
|
self.data.macro_Text = ""
|
|
end
|
|
end
|
|
|
|
function Button:GetMacroText()
|
|
return self.data.macro_Text
|
|
end
|
|
|
|
|
|
--Macro Name
|
|
function Button:SetMacroName(newName)
|
|
if newName then
|
|
self.data.macro_Name = newName
|
|
else
|
|
self.data.macro_Name = ""
|
|
end
|
|
end
|
|
|
|
function Button:GetMacroName()
|
|
return self.data.macro_Name
|
|
end
|
|
|
|
|
|
--Macro Note
|
|
function Button:SetMacroNote(newNote)
|
|
if newNote then
|
|
self.data.macro_Note = newNote
|
|
else
|
|
self.data.macro_Note = ""
|
|
end
|
|
end
|
|
|
|
function Button:GetMacroNote()
|
|
return self.data.macro_Note
|
|
end
|
|
|
|
|
|
--Macro Use Note
|
|
function Button:SetMacroUseNote(newUseNote)
|
|
if newUseNote then
|
|
self.data.macro_UseNote = newUseNote
|
|
else
|
|
self.data.macro_UseNote = false
|
|
end
|
|
end
|
|
|
|
function Button:GetMacroUseNote()
|
|
return self.data.macro_UseNote
|
|
end
|
|
|
|
|
|
--Macro Blizz Macro
|
|
function Button:SetMacroBlizzMacro(newBlizzMacro)
|
|
if newBlizzMacro then
|
|
self.data.macro_BlizzMacro = newBlizzMacro
|
|
else
|
|
self.data.macro_BlizzMacro = false
|
|
end
|
|
end
|
|
|
|
function Button:GetMacroBlizzMacro()
|
|
return self.data.macro_BlizzMacro
|
|
end
|
|
|
|
|
|
--Macro EquipmentSet
|
|
function Button:SetMacroEquipmentSet(newEquipmentSet)
|
|
if newEquipmentSet then
|
|
self.data.macro_EquipmentSet = newEquipmentSet
|
|
else
|
|
self.data.macro_EquipmentSet = false
|
|
end
|
|
end
|
|
|
|
function Button:GetMacroEquipmentSet()
|
|
return self.data.macro_EquipmentSet
|
|
end
|
|
|