annotate SkeletonKey/SkeletonKey.lua @ 67:ecd6c6116b9c

make item bindings great again
author Nenue
date Sat, 10 Sep 2016 20:56:19 -0400
parents 556e075983a6
children
rev   line source
Nenue@0 1 --------------------------------------------
Nenue@5 2 -- SkeletonKey
Nenue@5 3 -- Krakyn-Mal'Ganis
Nenue@0 4 -- @project-revision@ @project-hash@
Nenue@0 5 -- @file-revision@ @file-hash@
Nenue@0 6 -- Created: 6/16/2016 3:47 AM
Nenue@0 7 --------------------------------------------
Nenue@54 8 -- Header script
Nenue@0 9
Nenue@5 10 local _
Nenue@5 11 local kb, print = LibStub("LibKraken").register(KeyBinder)
Nenue@14 12 kb.L = setmetatable({}, {
Nenue@17 13 __call = function(t, k, ...) return format(t[k] or k, ...) end
Nenue@14 14 })
Nenue@14 15 local L = kb.L
Nenue@7 16
Nenue@5 17 --- Caps Lock literals
Nenue@14 18 L.BINDING_ASSIGNED = '|cFF00FF00%s|r assigned to |cFFFFFF00%s|r (%s).'
Nenue@14 19 L.BINDING_REMOVED = '|cFFFFFF00%s|r (|cFF00FFFF%s|r) unbound.'
Nenue@52 20 L.BINDING_FAILED_PROTECTED = '|cFFFF4400Cannot use |r|cFF00FF00%s|r|cFFFF4400 (currently |cFFFFFF00%s|r|cFFFF4400). Uncheck "Safety" to ignore this restraint.|r'
Nenue@14 21
Nenue@14 22
Nenue@5 23 local BINDING_TYPE_SPECIALIZATION = 3
Nenue@5 24 local BINDING_TYPE_CHARACTER = 2
Nenue@5 25 local BINDING_TYPE_GLOBAL = 1
Nenue@6 26 kb.configTitle = {
Nenue@17 27 [BINDING_TYPE_GLOBAL] = L('Global Binds'),
Nenue@17 28 [BINDING_TYPE_CHARACTER] = L('Character: %%s'),
Nenue@17 29 [BINDING_TYPE_SPECIALIZATION] = L('Specialization: %%s')
Nenue@0 30 }
Nenue@6 31 kb.configDescription = {
Nenue@17 32 [BINDING_TYPE_GLOBAL] = L('The bindings are applied globally.'),
Nenue@17 33 [BINDING_TYPE_CHARACTER] = L('Applied when you log onto this character.'),
Nenue@17 34 [BINDING_TYPE_SPECIALIZATION] = L('Applied when you select this specialization.'),
Nenue@5 35 }
Nenue@5 36
Nenue@27 37 kb.ChangedBindings = {}
Nenue@19 38 kb.SystemBindings = {}
Nenue@19 39 kb.ActionTypes = {}
Nenue@19 40 kb.TalentBindings = {}
Nenue@27 41 kb.PetCache = {
Nenue@27 42 spell = {},
Nenue@27 43 spellslot = {},
Nenue@27 44 action = {},
Nenue@27 45 special = {},
Nenue@27 46 subtext = {}
Nenue@27 47 }
Nenue@27 48 kb.TalentCache = {}
Nenue@27 49 kb.ProfessionCache = {}
Nenue@27 50 kb.pendingCalls = {}
Nenue@27 51 kb.pendingAttributes = {}
Nenue@0 52
Nenue@6 53 kb.configHeaders = {}
Nenue@6 54 kb.loadedProfiles = {}
Nenue@6 55 kb.orderedProfiles = {}
Nenue@6 56 kb.buttons = {}
Nenue@6 57 kb.macros = {}
Nenue@17 58 kb.bindings = {}
Nenue@15 59 kb.petFrames = {} -- pet data is slightly delayed, their buttons are indexed here so they can be refreshed
Nenue@15 60 kb.talentFrames = {}
Nenue@15 61 kb.professionFrames = {}
Nenue@0 62
Nenue@14 63 -- these are sent to plugin
Nenue@14 64
Nenue@14 65
Nenue@14 66 local db
Nenue@50 67 local _G = _G
Nenue@50 68 local UnitName, SelectedRealmName, InCombatLockdown, UnitClass = UnitName, SelectedRealmName, InCombatLockdown, UnitClass
Nenue@50 69 local tostring, select, tinsert, pairs = tostring, select, tinsert, pairs
Nenue@50 70 local concat, wipe = table.concat, table.wipe
Nenue@5 71 local classHeader, className, classID = '', '', 0
Nenue@5 72
Nenue@5 73 --- Control handles
Nenue@0 74 local saveButton, restoreButton, clearButton
Nenue@0 75
Nenue@5 76 --- Returns conflicting assignment and binding profiles for use in displaying confirmations
Nenue@6 77 kb.IsCommandBound = function(self, command)
Nenue@6 78 local isAssigned, assignedBy = false, db.bindMode
Nenue@6 79 local isBound, boundBy = false, db.bindMode
Nenue@5 80 command = command or self.command
Nenue@6 81 for i = 1, #kb.orderedProfiles do
Nenue@6 82 local tier = kb.orderedProfiles[i]
Nenue@6 83 if i ~= db.bindMode then
Nenue@5 84
Nenue@5 85 if tier.commands[command] then
Nenue@5 86 isAssigned = true
Nenue@5 87 assignedBy = i
Nenue@5 88 end
Nenue@5 89 if tier.bound[command] then
Nenue@5 90 isBound = true
Nenue@5 91 boundBy = i
Nenue@5 92 end
Nenue@5 93
Nenue@5 94
Nenue@5 95 --print(' *', configHeaders[i], tier.commands[command], tier.bound[command])
Nenue@5 96
Nenue@5 97 if isAssigned and isBound then
Nenue@0 98 break
Nenue@0 99 end
Nenue@0 100 end
Nenue@5 101
Nenue@0 102 end
Nenue@5 103
Nenue@17 104 print('|cFFFFFF00IsCommandBound:|r', command,'|r [profile:', db.bindMode .. ']', isAssigned, isBound, assignedBy, boundBy)
Nenue@5 105 return isAssigned, isBound, assignedBy, boundBy
Nenue@0 106 end
Nenue@0 107
Nenue@5 108 local talentSpellHardCodes = {
Nenue@5 109 [109248] = 'Binding Shot',
Nenue@5 110 }
Nenue@5 111
Nenue@0 112 --- Returns a value for use with Texture:SetDesaturated()
Nenue@6 113 kb.BindingIsLocked = function(key)
Nenue@0 114 local success = false
Nenue@6 115 for i = 1, db.bindMode-1 do
Nenue@6 116 local tier = kb.orderedProfiles[i]
Nenue@0 117 if tier.bindings[key] then
Nenue@0 118 success = true
Nenue@0 119 break
Nenue@0 120 end
Nenue@0 121 end
Nenue@0 122 return success
Nenue@0 123 end
Nenue@0 124
Nenue@0 125 --- Translates GetBindingKey() results into a printable string.
Nenue@6 126 kb.BindingString = function(...)
Nenue@0 127 local stack = {}
Nenue@0 128 for i = 1, select('#', ...) do
Nenue@0 129 local key = select(i, ...)
Nenue@5 130 stack[i] = key:gsub('SHIFT', 's'):gsub('ALT', 'a'):gsub('CTRL', 'c'):gsub('SPACE', 'Sp'):gsub('BUTTON', 'M '):gsub('NUMPAD', '# ')
Nenue@0 131 end
Nenue@0 132
Nenue@0 133 if #stack >= 1 then
Nenue@50 134 return concat(stack, ',')
Nenue@0 135 else
Nenue@0 136 return nil
Nenue@0 137 end
Nenue@0 138 end
Nenue@0 139
Nenue@5 140
Nenue@5 141
Nenue@5 142
Nenue@0 143
Nenue@5 144 kb.Command = function(args, editor)
Nenue@5 145 if args:match("import") then
Nenue@5 146 kb.ImportCommmit(args)
Nenue@5 147 return
Nenue@5 148 elseif args:match("scan") then
Nenue@5 149 kb.ImportScan(args)
Nenue@5 150 kb.ui()
Nenue@5 151 return
Nenue@5 152 elseif args:match("load") then
Nenue@5 153 kb:ApplyAllBindings()
Nenue@0 154 return
Nenue@0 155 end
Nenue@0 156
Nenue@5 157 if db.showUI then
Nenue@5 158 db.showUI = false
Nenue@5 159 kb:Hide()
Nenue@5 160 else
Nenue@1 161 db.showUI = true
Nenue@65 162 if not InCombatLockdown() then
Nenue@65 163 kb:print(L('Config frame opened.'))
Nenue@65 164 else
Nenue@65 165 kb:print(L('Config frame will open upon exiting combat.'))
Nenue@65 166 end
Nenue@5 167 end
Nenue@5 168 kb.ui(true)
Nenue@5 169 end
Nenue@5 170
Nenue@5 171 kb.InitProfile = function(profile, prototype)
Nenue@5 172 if not profile then
Nenue@5 173 profile = {}
Nenue@5 174 end
Nenue@5 175 if prototype then
Nenue@5 176 print('appplying prototype', prototype)
Nenue@5 177 for k,v in pairs(prototype) do
Nenue@5 178 if not profile[k] then
Nenue@5 179 profile[k] = v
Nenue@5 180 end
Nenue@5 181 end
Nenue@0 182 end
Nenue@0 183
Nenue@5 184 profile.bound = profile.bound or {}
Nenue@5 185 profile.buttons = profile.buttons or {}
Nenue@5 186 profile.commands = profile.commands or {}
Nenue@5 187 profile.bindings = profile.bindings or {}
Nenue@5 188 profile.macros = profile.macros or {}
Nenue@5 189 profile.talents = profile.talents or {}
Nenue@5 190 return profile
Nenue@5 191 end
Nenue@5 192
Nenue@5 193 kb.ResetProfile = function(profile, prototype)
Nenue@6 194 if profile == kb.currentProfile then
Nenue@50 195 for i, button in pairs(kb.buttons) do
Nenue@5 196 kb.ReleaseSlot(button)
Nenue@5 197 end
Nenue@5 198 end
Nenue@50 199 wipe(profile)
Nenue@5 200 kb.InitProfile(profile, prototype)
Nenue@5 201 end
Nenue@5 202
Nenue@5 203
Nenue@5 204
Nenue@5 205 --- Handles constructing spec profiles as they are selected
Nenue@5 206
Nenue@5 207
Nenue@5 208 --- Obtains profile data or creates the necessary tables
Nenue@5 209 kb.SelectProfileSet = function(name)
Nenue@5 210
Nenue@6 211 local defaultMode
Nenue@5 212 --- General info
Nenue@5 213 classHeader, className, classID = UnitClass('player')
Nenue@5 214 print('|cFF00FF00profile:|r', name)
Nenue@5 215 print('|cFF00FF00class:|r', UnitClass('player'))
Nenue@5 216
Nenue@6 217 defaultMode = BINDING_TYPE_GLOBAL
Nenue@17 218 if db[name] then
Nenue@6 219 defaultMode = BINDING_TYPE_CHARACTER
Nenue@17 220 if db[name][kb.specInfo.id] then
Nenue@17 221 defaultMode = BINDING_TYPE_SPECIALIZATION
Nenue@17 222 end
Nenue@5 223 end
Nenue@5 224
Nenue@17 225 db[name] = kb.InitProfile(db[name],
Nenue@17 226 {
Nenue@17 227 classHeader = classHeader,
Nenue@17 228 className = className,
Nenue@17 229 classID = classID
Nenue@17 230 })
Nenue@17 231 db[name][kb.specInfo.id] = kb.InitProfile(db[name][kb.specInfo.id],
Nenue@17 232 {
Nenue@17 233 specID = kb.specInfo.id,
Nenue@17 234 specName = kb.specInfo.name
Nenue@17 235 })
Nenue@5 236
Nenue@17 237 kb.loadedProfiles[BINDING_TYPE_GLOBAL] = db
Nenue@17 238 kb.loadedProfiles[BINDING_TYPE_CHARACTER] = db[name]
Nenue@17 239 kb.loadedProfiles[BINDING_TYPE_SPECIALIZATION] = db[name][kb.specInfo.id]
Nenue@17 240 kb.orderedProfiles = {db, db[name], db[name][kb.specInfo.id]}
Nenue@17 241
Nenue@15 242 if (not db.bindMode) or (not kb.configTitle[db.bindMode]) then
Nenue@6 243 print('fixing bad bindMode value, was', db.bindMode)
Nenue@6 244 db.bindMode = defaultMode
Nenue@5 245 end
Nenue@5 246
Nenue@5 247
Nenue@5 248 print(BINDING_TYPE_GLOBAL)
Nenue@6 249 kb.configHeaders[BINDING_TYPE_GLOBAL] = kb.configTitle[BINDING_TYPE_GLOBAL]
Nenue@6 250 kb.configHeaders[BINDING_TYPE_CHARACTER] = kb.configTitle[BINDING_TYPE_CHARACTER]:format(UnitName('player', true))
Nenue@10 251 kb.configHeaders[BINDING_TYPE_SPECIALIZATION] = kb.configTitle[BINDING_TYPE_SPECIALIZATION]:format(kb.specInfo.name)
Nenue@5 252
Nenue@5 253
Nenue@6 254 setmetatable(kb.loadedProfiles[BINDING_TYPE_GLOBAL], {__tostring =function() return kb.configHeaders[BINDING_TYPE_GLOBAL] end})
Nenue@6 255 setmetatable(kb.loadedProfiles[BINDING_TYPE_CHARACTER], {__tostring =function() return kb.configHeaders[BINDING_TYPE_CHARACTER] end})
Nenue@6 256 setmetatable(kb.loadedProfiles[BINDING_TYPE_SPECIALIZATION], {__tostring =function() return kb.configHeaders[BINDING_TYPE_SPECIALIZATION] end})
Nenue@5 257
Nenue@6 258 print('|cFF00FF00bindMode:|r', db.bindMode)
Nenue@6 259 kb.currentProfile = kb.loadedProfiles[db.bindMode]
Nenue@17 260 kb.currentHeader = kb.configHeaders[db.bindMode]
Nenue@5 261 end
Nenue@5 262
Nenue@5 263 local scrollCache = {}
Nenue@5 264 kb.SelectTab = function(self)
Nenue@6 265 scrollCache[db.bindMode] = kb.scrollOffset
Nenue@5 266 db.bindMode = self:GetID()
Nenue@6 267 kb.currentProfile = kb.loadedProfiles[self:GetID()]
Nenue@17 268 kb.currentHeader = kb.configHeaders[db.bindMode]
Nenue@6 269 kb.scrollOffset = scrollCache[db.bindMode] or 0
Nenue@5 270 kb.ui(true)
Nenue@5 271 end
Nenue@5 272
Nenue@5 273 kb.RevertBindings = function()
Nenue@5 274 -- todo: reversion code
Nenue@5 275 end
Nenue@5 276
Nenue@5 277 kb.ConfirmBindings = function()
Nenue@5 278 kb.ApplyAllBindings()
Nenue@5 279 kb.ui()
Nenue@65 280 if #kb.pendingAttributes == 0 then
Nenue@65 281 kb:print(L("Manual bindings update finished."))
Nenue@65 282 else
Nenue@65 283 kb:print(L("Manual update will complete upon exiting combat."))
Nenue@65 284 end
Nenue@5 285 end
Nenue@5 286
Nenue@5 287 --- post ADDON_LOADED
Nenue@5 288 kb.variables = function()
Nenue@50 289 _G.SkeletonKeyDB = kb.InitProfile(_G.SkeletonKeyDB, {})
Nenue@50 290 kb.db = _G.SkeletonKeyDB
Nenue@5 291 kb.playerName = UnitName('player')
Nenue@5 292 kb.playerRealm = SelectedRealmName()
Nenue@5 293 kb.profileName = kb.playerRealm .. '_' .. kb.playerName
Nenue@5 294 db = kb.db
Nenue@5 295
Nenue@17 296 kb.UpdateSpecInfo()
Nenue@17 297 kb.UpdateTalentInfo()
Nenue@5 298 kb.SelectProfileSet(kb.profileName)
Nenue@6 299 -- todo: redo import checking
Nenue@6 300
Nenue@15 301 kb.UpdateSystemBinds()
Nenue@5 302 kb.ApplyAllBindings()
Nenue@5 303
Nenue@27 304 if not InCombatLockdown() then
Nenue@27 305 kb.CreateHooks()
Nenue@27 306 else
Nenue@27 307 kb:print('Some functionality will not load until breaking combat.')
Nenue@27 308 tinsert(kb.pendingCalls, kb.CreateHooks)
Nenue@27 309 end
Nenue@27 310
Nenue@27 311
Nenue@5 312 kb.ui(true)
Nenue@0 313 end
Nenue@0 314
Nenue@1 315
Nenue@5 316 -- Volatiles Access
Nenue@50 317 kb.FormatActionID = function(actionType, actionID) return tostring(actionType) .. '_' .. tostring(actionID) end
Nenue@17 318 kb.GetBindings = function() return kb.bindings end
Nenue@17 319 kb.GetButtons = function() return kb.buttons end
Nenue@6 320 kb.GetCharacterProfile = function () return kb.loadedProfiles[BINDING_TYPE_CHARACTER] end
Nenue@6 321 kb.GetGlobalProfile = function () return kb.loadedProfiles[BINDING_TYPE_GLOBAL] end
Nenue@6 322 kb.GetSpecProfile = function () return kb.loadedProfiles[BINDING_TYPE_SPECIALIZATION] end
Nenue@0 323
Nenue@0 324
Nenue@5 325 SLASH_SKB1 = "/skb"
Nenue@5 326 SLASH_SKB2 = "/skeletonkey"
Nenue@5 327 SlashCmdList.SKB = kb.Command