Compare with Previous | Blame | View Log
-- Adapted from code appearing in World of Warcraft Programming: A Guide and -- Reference for Creating WoW Addons. http://wowprogramming.com MacroSequence = MacroSequence or {} local L = MacroSequenceLocals local print = CogsUtils:GetPrint(L.PRINT_HEADER) -- Initialize saved variable from Sequences.lua if MacroSequence.sequences then MacroSequenceSequences = MacroSequence.sequences print(L.IMPORTED) end function MacroSequence:Initialize() if not MacroSequenceSequences then MacroSequenceSequences = {} end for name, data in pairs(MacroSequenceSequences) do if _G[name] then print(format(L.NAME_EXISTS, name)) else self:CreateSequence(name, data) end end end -- No global variable of name should exist function MacroSequence:CreateSequence(name, data) local button = self:NewButton(name) if #data == 0 then return end self:ApplySettings(button, data) end function MacroSequence:ApplySettings(button, data) -- Add entries to the sequence self:CreateSequenceEntries(button, data) -- Only add states and reset conditions if there are multiple entries if #data > 1 then -- Set up a newstate attribute to cycle through each possible state button:SetAttribute("newstate", "0-"..(#data - 1)) -- Set up reset conditions for the sequence self:ApplyResetConditions(button, data) end end function MacroSequence:RemoveSettings(button) button:GetParent():SetAttribute("state", 0) for attribute in pairs(button.attributes) do button:SetAttribute(attribute, nil) end end MacroSequence.freeHeaders = {} MacroSequence.usedHeaders = {} MacroSequence.namedButtons = {} function MacroSequence:NewButton(name) local header = tremove(self.freeHeaders) or CreateFrame( "Frame", nil, nil, "SecureStateHeaderTemplate" ) self.usedHeaders[name] = header -- Add the header to the used pool local button = self.namedButtons[name] if not button then button = CreateFrame( "Button", name, header, "SecureActionButtonTemplate" ) self.namedButtons[name] = button button:SetAttribute("type", "macro") -- Store attributes for possible removal button.attributes = {} button:SetScript("OnAttributeChanged", function(button, name, value) if value == nil then button.attributes[name] = nil if name == "_combatreset" then button:UnregisterEvent("PLAYER_REGEN_ENABLED") end else button.attributes[name] = true if name == "_combatreset" then button:RegisterEvent("PLAYER_REGEN_ENABLED") end end end) -- Used for combat reset, if present button:SetScript("OnEvent", function(button) header:SetAttribute("state", 0) end) end header:SetAttribute("addchild", button) header.button = button _G[name] = button -- Make a global reference to the button return header.button end -- Unused child buttons are parented to this header to satisfy assumptions made -- by the state header code local dummyHeader = CreateFrame( "Frame", "MacroSequenceDummyHeader", UIParent, "SecureStateHeaderTemplate" ) function MacroSequence:RemoveSequence(name) local header = self.usedHeaders[name] local button = header.button -- Clear the button's attributes self:RemoveSettings(button) button:SetParent(dummyHeader) -- Remove the sequence from various tables MacroSequenceSequences[name] = nil _G[name] = nil self.usedHeaders[name] = nil -- Put the header into the free pool tinsert(self.freeHeaders, header) end local function deepCopy(inTable) local outTable = {} for k, v in pairs(inTable) do if type(v) == "table" then outTable[k] = deepCopy(v) else outTable[k] = v end end return outTable end -- There must not be a global variable named the same as new function MacroSequence:CopySequence(old, new) MacroSequenceSequences[new] = deepCopy(MacroSequenceSequences[old]) self:CreateSequence(new, MacroSequenceSequences[new]) local oldHeader = self.usedHeaders[old] local newHeader = self.usedHeaders[new] local oldButton = oldHeader.button local newButton = newHeader.button -- Copy attributes between buttons for name in pairs(oldButton.attributes) do newButton:SetAttribute(name, oldButton:GetAttribute(name)) end -- Copy state from header newHeader:SetAttribute("state", oldHeader:GetAttribute("state")) end function MacroSequence:RenameSequence(old, new) self:CopySequence(old, new) self:RemoveSequence(old) end function MacroSequence:CreateSequenceEntries(button, data) local statebutton = "" for state, macro in ipairs(data) do state = state - 1 -- Add a statebutton entry for the current macro in the form "0:b0;" statebutton = statebutton..format("%d:b%1$d;", state) -- Create a macrotext attribute using the new statebutton button:SetAttribute("*macrotext-b"..state, macro) end button:SetAttribute("statebutton", statebutton) end -- This table describes which prefixes to use for each reset modifier's newstate -- attribute. For the sake of simplicity, ApplyModifierResets will rewrite -- certain attributes if multiple modifier resets are used on a single sequence. local modifierPrefixes = { alt = { "alt", "alt-ctrl", "alt-shift", "alt-ctrl-shift" }, ctrl = { "ctrl", "alt-ctrl", "ctrl-shift", "alt-ctrl-shift" }, shift = { "shift", "alt-shift", "ctrl-shift", "alt-ctrl-shift" } } local resetFuncs = { -- Combat function(button, data) if data.reset.combat then button:SetAttribute("_combatreset", true) end end, -- Timer function(button, data) local seconds = data.reset.seconds if seconds and seconds > 0 then -- Switch to state 0 after the specified delay unless the state changes by -- other means first button:SetAttribute("delaystate", "0") button:SetAttribute("delaytime", seconds) end end, -- Modifiers function(button, data) for modifier, prefixes in pairs(modifierPrefixes) do if data.reset[modifier] then for _, prefix in ipairs(prefixes) do -- Make the modified click run the first macro in the sequence button:SetAttribute(prefix.."-statebutton*", "b0") -- And then move on to the next button:SetAttribute(prefix.."-newstate*", "1") end end end end, -- Cycle function(button, data) local cycle = tonumber(data.reset.cycle) if cycle and cycle < #data then -- This converts a newstate attribute from, e.g. "0-5" to "5:3;0-5" button:SetAttribute("newstate", format( "%d:%d;%s", #data - 1, #data - cycle, button:GetAttribute("newstate") )) end end } function MacroSequence:ApplyResetConditions(button, data) if not data.reset then return end for _, func in ipairs(resetFuncs) do func(button, data) end end function MacroSequence:ShowGUI() if not IsAddOnLoaded("MacroSequenceGUI") then LoadAddOn("MacroSequenceGUI") end MacroSequenceGUI:Show() end local events = { ["VARIABLES_LOADED"] = function() MacroSequence:Initialize() end, ["PLAYER_DEAD"] = function() for _, header in ipairs(MacroSequence.usedHeaders) do header:SetAttribute("state", 0) end end, } local handler = CreateFrame("Frame") for name, func in pairs(events) do handler:RegisterEvent(name) end handler:SetScript("OnEvent", function(self, event, ...) local func = events[event] if func then func(event, ...) end end) SLASH_MACROSEQUENCE1 = "/macrosequence" SLASH_MACROSEQUENCE2 = "/sequence" SLASH_MACROSEQUENCE3 = "/seq" SlashCmdList["MACROSEQUENCE"] = function(msg) MacroSequence:ShowGUI() end