Mercurial > wow > skeletonkey
view SkeletonKey.lua @ 74:9824d524a661
- binding slot mixin:
- store key binding definitions under their slot's data table
- apply action button attributes when a slot is assigned
- obtain correct macro body text when a macro is slotted
- fix algorithm for resolving renamed macro indices
- move spell detail lookup code out of mixin script
- event chains:
- initialize addon from PLAYER_LOGIN
- reload keybinds from PLAYER_SPECIALIZATION_CHANGED, after spec profile is resolved
- refresh interface content from SPELLS_CHANGED
- hotkey text:
- restore communication and detection of key binding updates and reflect them accordingly
- properly respond to dynamic bindings that result from talent updates
author | Nenue |
---|---|
date | Sat, 14 Jan 2017 02:29:33 -0500 |
parents | c48913c5924c |
children | 6623b7f2c1ca |
line wrap: on
line source
-------------------------------------------- -- SkeletonKey -- Krakyn-Mal'Ganis -- @project-revision@ @project-hash@ -- @file-revision@ @file-hash@ -- Created: 6/16/2016 3:47 AM -------------------------------------------- -- Header script local addonName, kb = ... local print = DEVIAN_WORKSPACE and function(...) _G.print('SK',...) end or nop local cprint = DEVIAN_WORKSPACE and function(...) _G.print('Cfg',...) end or nop SkeletonKeyMixin = { scrollCache = {}, tabButtons = {}, keyButtons = {}, panelButtons = {}, numTabs = 0, } kb.L = setmetatable({}, { __call = function(t, k, ...) return format(t[k] or k, ...) end }) local L = kb.L --- Caps Lock literals L.UNSELECTED_TALENT_ASSIGNED = '|cFF00FF00%s|r added for |cFFFFFF00%s|r (%s).' L.BINDING_ASSIGNED = '|cFF00FF00%s|r assigned to |cFFFFFF00%s|r (%s).' L.BINDING_REMOVED = '|cFFFFFF00%s|r (|cFF00FFFF%s|r) unbound.' L.BINDING_FAILED_PROTECTED = '|cFFFF4400Cannot use |r|cFF00FF00%s|r|cFFFF4400 (currently |cFFFFFF00%s|r|cFFFF4400). Uncheck "Safety" to ignore this restraint.|r' local BINDING_TYPE_SPECIALIZATION = 3 local BINDING_TYPE_CHARACTER = 2 local BINDING_TYPE_GLOBAL = 1 kb.configTitle = { [BINDING_TYPE_GLOBAL] = L('Global Binds'), [BINDING_TYPE_CHARACTER] = L('%%s'), [BINDING_TYPE_SPECIALIZATION] = L('%%s') } kb.configDescription = { [BINDING_TYPE_GLOBAL] = L('The bindings are applied globally.'), [BINDING_TYPE_CHARACTER] = L('Applied when you log onto this character.'), [BINDING_TYPE_SPECIALIZATION] = L('Applied when you select this specialization.'), } kb.ChangedBindings = {} kb.SystemBindings = {} kb.ActionTypes = {} kb.TalentBindings = {} kb.PetCache = { spell = {}, spellslot = {}, action = {}, special = {}, subtext = {} } kb.DynamicSpells = { profession = {}, petaction = {}, talent = {}, } kb.TalentCache = {} kb.ProfessionCache = {} kb.pendingCalls = {} kb.pendingAttributes = {} kb.configHeaders = {} kb.loadedProfiles = {} kb.orderedProfiles = {} kb.buttons = {} kb.macros = {} kb.bindings = setmetatable({}, {__newindex = function(t,k,v) rawset(t,k,v) cprint('adding', k, v) end}) kb.petFrames = {} -- pet data is slightly delayed, their buttons are indexed here so they can be refreshed kb.talentFrames = {} kb.professionFrames = {} -- these are sent to plugin local db local _G = _G local UnitName, SelectedRealmName, InCombatLockdown, UnitClass = UnitName, SelectedRealmName, InCombatLockdown, UnitClass local tostring, select, tinsert, pairs = tostring, select, tinsert, pairs local concat, wipe = table.concat, table.wipe local classHeader, className, classID = '', '', 0 local CloseButton_OnClick = function() kb.db.showUI = false print(kb.db.showUI) SkeletonKey:SetShown(kb.db.showUI) end --- Returns conflicting assignment and binding profiles for use in displaying confirmations kb.IsCommandBound = function(self, command) local isAssigned, assignedBy = false, kb.db.bindMode local isBound, boundBy = false, kb.db.bindMode command = command or self.command for i = 1, #kb.orderedProfiles do local profile = kb.orderedProfiles[i] if i ~= kb.db.bindMode then if profile.commands[command] then print(' command: ', i , kb.configHeaders[i], profile.commands[command]) isAssigned = true assignedBy = i end if profile.bound[command] then print(' bound: ', i , kb.configHeaders[i], profile.bound[command]) isBound = true boundBy = i end if isAssigned and isBound then print(' hit: ', i , kb.configHeaders[i], profile.commands[command], profile.bound[command]) break end end end print('|cFFFFFF00IsCommandBound:|r', command,'|r [profile:', kb.db.bindMode .. ']', isAssigned, isBound, assignedBy, boundBy) return isAssigned, isBound, assignedBy, boundBy end local talentSpellHardCodes = { [109248] = 'Binding Shot', } --- Returns a value for use with Texture:SetDesaturated() kb.BindingIsLocked = function(key) local success = false for i = 1, db.bindMode-1 do local tier = kb.orderedProfiles[i] if tier.bindings[key] then success = true break end end return success end --- Translates GetBindingKey() results into a printable string. kb.BindingString = function(...) local stack = {} for i = 1, select('#', ...) do local key = select(i, ...) if type(key) == 'string' then stack[i] = key:gsub('SHIFT', 's'):gsub('ALT', 'a'):gsub('CTRL', 'c'):gsub('SPACE', 'Sp'):gsub('BUTTON', 'M '):gsub('NUMPAD', '# ') end end if #stack >= 1 then return concat(stack, ',') else return nil end end function kb:print(...) local msg = '|cFF0088FFSkeletonKey|r:' for i = 1, select('#', ...) do msg = msg .. ' ' .. tostring(select(i, ...)) end DEFAULT_CHAT_FRAME:AddMessage(msg) end kb.Command = function(args, editor) if args:match("import") then kb.ImportCommmit(args) return elseif args:match("scan") then kb.ImportScan(args) SkeletonKey:Update() return elseif args:match("load") then kb:ApplyAllBindings() return end if db.showUI then db.showUI = false else db.showUI = true if not InCombatLockdown() then kb:print(L('Config frame opened.')) else kb:print(L('Config frame will open upon exiting combat.')) end end SkeletonKey:SetShown(db.showUI) SkeletonKey:Update(true) end kb.InitProfile = function(profile, prototype) print('|cFF00FFFFkb.InitProfile()', profile, prototype) if not profile then profile = {} end if prototype then for k,v in pairs(prototype) do if not profile[k] then profile[k] = v end end end profile.bound = profile.bound or {} profile.buttons = profile.buttons or {} profile.commands = profile.commands or {} profile.bindings = profile.bindings or {} profile.macros = profile.macros or {} profile.talents = profile.talents or {} return profile end kb.ResetProfile = function(profile, prototype) if profile == kb.currentProfile then for i, button in pairs(kb.buttons) do kb.ReleaseSlot(button) end end wipe(profile) kb.InitProfile(profile, prototype) end --- Handles constructing spec profiles as they are selected --- Obtains profile data or creates the necessary tables kb.SelectProfileSet = function(name) local defaultMode --- General info classHeader, className, classID = UnitClass('player') --kb:print('|cFF00FF00profile:|r', name) --kb:print('|cFF00FF00class:|r', UnitClass('player')) defaultMode = BINDING_TYPE_GLOBAL if db[name] then defaultMode = BINDING_TYPE_CHARACTER if db[name][kb.specInfo.id] then defaultMode = BINDING_TYPE_SPECIALIZATION end end db[name] = kb.InitProfile(db[name], { classHeader = classHeader, className = className, classID = classID }) db[name][kb.specInfo.id] = kb.InitProfile(db[name][kb.specInfo.id], { specID = kb.specInfo.id, specName = kb.specInfo.name }) kb.loadedProfiles[BINDING_TYPE_GLOBAL] = db kb.loadedProfiles[BINDING_TYPE_CHARACTER] = db[name] kb.loadedProfiles[BINDING_TYPE_SPECIALIZATION] = db[name][kb.specInfo.id] kb.orderedProfiles = {db, db[name], db[name][kb.specInfo.id]} if (not db.bindMode) or (not kb.configTitle[db.bindMode]) then print('fixing bad bindMode value, was', db.bindMode) db.bindMode = defaultMode end print(BINDING_TYPE_GLOBAL) kb.configHeaders[BINDING_TYPE_GLOBAL] = kb.configTitle[BINDING_TYPE_GLOBAL] kb.configHeaders[BINDING_TYPE_CHARACTER] = kb.configTitle[BINDING_TYPE_CHARACTER]:format(UnitName('player', true)) kb.configHeaders[BINDING_TYPE_SPECIALIZATION] = kb.configTitle[BINDING_TYPE_SPECIALIZATION]:format(kb.specInfo.name or '') setmetatable(kb.loadedProfiles[BINDING_TYPE_GLOBAL], {__tostring =function() return kb.configHeaders[BINDING_TYPE_GLOBAL] end}) setmetatable(kb.loadedProfiles[BINDING_TYPE_CHARACTER], {__tostring =function() return kb.configHeaders[BINDING_TYPE_CHARACTER] end}) setmetatable(kb.loadedProfiles[BINDING_TYPE_SPECIALIZATION], {__tostring =function() return kb.configHeaders[BINDING_TYPE_SPECIALIZATION] end}) kb.currentProfile = kb.loadedProfiles[db.bindMode] kb.currentHeader = kb.configHeaders[db.bindMode] print('|cFF88FF00SelectProfile()|r', kb.profileName, classHeader, kb.specInfo.name) end function SkeletonKeyMixin:SetTab (id) self.scrollCache[db.bindMode] = kb.scrollOffset db.bindMode =id kb.currentProfile = kb.loadedProfiles[id] kb.currentHeader = kb.configHeaders[db.bindMode] kb.scrollOffset = self.scrollCache[db.bindMode] or 0 self:Update(true) end kb.ConfirmBindings = function() kb.ApplyAllBindings() if #kb.pendingAttributes == 0 then kb:print(L("Manual bindings update finished.")) else kb:print(L("Manual update will complete upon exiting combat.")) end SkeletonKey:Update() end function SkeletonKeyMixin:OnLoad() kb.frame = self print('|cFF0088FF'..self:GetName()..':OnLoad()') self.CloseButton:SetScript('OnClick', CloseButton_OnClick) self:RegisterEvent('PLAYER_ENTERING_WORLD') self:RegisterEvent('ADDON_LOADED') self:RegisterEvent('PLAYER_LOGIN') self:RegisterUnitEvent('PLAYER_SPECIALIZATION_CHANGED', 'player') self:EnableKeyboard(false) self.zoomScale = self:GetScale() self.backdrop = self:GetBackdrop() self.backdropColor = {self:GetBackdropColor() } self.backdropBorder = {self:GetBackdropBorderColor() } end function SkeletonKeyMixin:OnEvent(event, arg) print('|cFFFF0088'.. event..'|r', unit) if event == 'PLAYER_LOGIN' then print('|cFF00FFFF'..event ..'|r', arg or '', IsLoggedIn()) if not self.initialized then self:Setup() self.initialized = true kb.ApplyAllBindings() self:Update(true) end elseif kb[event] then if self.initialized then print('|cFF0088FF'..event ..'|r', arg or '') kb[event](self, event, arg) else print('|cFF004488'..event ..'|r', arg or '') end end end function SkeletonKeyMixin:RefreshSpells() kb.UpdateTalentInfo() kb.UpdatePetInfo() end --- post ADDON_LOADED function SkeletonKeyMixin:Setup () print('|cFF00FFFF'..self:GetName()..':Setup()') SkeletonKeyDB = kb.InitProfile(SkeletonKeyDB, {}) kb.db = _G.SkeletonKeyDB kb.playerName = UnitName('player') kb.playerRealm = SelectedRealmName() kb.profileName = kb.playerRealm .. '_' .. kb.playerName db = kb.db kb.UpdateSpecInfo() kb.UpdateTalentInfo() kb.SelectProfileSet(kb.profileName) self:SetShown(kb.db.showUI) -- todo: redo import checking kb.UpdateSystemBinds() if not InCombatLockdown() then kb.CreateHooks() else kb:print('Some functionality will not load until breaking combat.') tinsert(kb.pendingCalls, kb.CreateHooks) end SLASH_SKB1 = "/skb" SLASH_SKB2 = "/skeletonkey" SlashCmdList.SKB = kb.Command self:RegisterEvent('UPDATE_MACROS') --self:RegisterEvent('UPDATE_BINDINGS') self:RegisterUnitEvent('UNIT_PORTRAIT_UPDATE', 'player', 'pet') self:RegisterEvent('PLAYER_REGEN_DISABLED') self:RegisterEvent('PLAYER_REGEN_ENABLED') self:RegisterEvent('SPELLS_CHANGED') self:RegisterEvent('PLAYER_TALENT_UPDATE') self:RegisterForDrag('LeftButton') self:SetMovable(true) for index, frame in ipairs(self.Plugins) do frame:Setup() end end -- Volatiles Access kb.FormatActionID = function(actionType, actionID) return tostring(actionType) .. '_' .. tostring(actionID) end kb.GetBindings = function() return kb.bindings end kb.GetButtons = function() return kb.buttons end kb.GetCharacterProfile = function () return kb.loadedProfiles[BINDING_TYPE_CHARACTER] end kb.GetGlobalProfile = function () return kb.loadedProfiles[BINDING_TYPE_GLOBAL] end kb.GetSpecProfile = function () return kb.loadedProfiles[BINDING_TYPE_SPECIALIZATION] end