annotate SkeletonKey.lua @ 78:d4c100b0fd01 v7.1.5-80-release

- Fixed an issue with talent bindings not loading between specialization changes. - Fixed action button text corruption that occurred after changing assignments on an inactive spell.
author Nenue
date Thu, 26 Jan 2017 20:25:04 -0500
parents 6623b7f2c1ca
children 1605319122ce
rev   line source
Nenue@70 1 --------------------------------------------
Nenue@70 2 -- SkeletonKey
Nenue@70 3 -- Krakyn-Mal'Ganis
Nenue@70 4 -- @project-revision@ @project-hash@
Nenue@70 5 -- @file-revision@ @file-hash@
Nenue@70 6 -- Created: 6/16/2016 3:47 AM
Nenue@70 7 --------------------------------------------
Nenue@70 8 -- Header script
Nenue@70 9
Nenue@70 10 local addonName, kb = ...
Nenue@72 11 local print = DEVIAN_WORKSPACE and function(...) _G.print('SK',...) end or nop
Nenue@72 12 local cprint = DEVIAN_WORKSPACE and function(...) _G.print('Cfg',...) end or nop
Nenue@70 13 SkeletonKeyMixin = {
Nenue@70 14 scrollCache = {},
Nenue@70 15 tabButtons = {},
Nenue@70 16 keyButtons = {},
Nenue@70 17 panelButtons = {},
Nenue@70 18 numTabs = 0,
Nenue@70 19 }
Nenue@70 20 kb.L = setmetatable({}, {
Nenue@70 21 __call = function(t, k, ...) return format(t[k] or k, ...) end
Nenue@70 22 })
Nenue@70 23 local L = kb.L
Nenue@70 24
Nenue@70 25 --- Caps Lock literals
Nenue@70 26 L.UNSELECTED_TALENT_ASSIGNED = '|cFF00FF00%s|r added for |cFFFFFF00%s|r (%s).'
Nenue@70 27 L.BINDING_ASSIGNED = '|cFF00FF00%s|r assigned to |cFFFFFF00%s|r (%s).'
Nenue@70 28 L.BINDING_REMOVED = '|cFFFFFF00%s|r (|cFF00FFFF%s|r) unbound.'
Nenue@70 29 L.BINDING_FAILED_PROTECTED = '|cFFFF4400Cannot use |r|cFF00FF00%s|r|cFFFF4400 (currently |cFFFFFF00%s|r|cFFFF4400). Uncheck "Safety" to ignore this restraint.|r'
Nenue@70 30
Nenue@70 31
Nenue@70 32 local BINDING_TYPE_SPECIALIZATION = 3
Nenue@70 33 local BINDING_TYPE_CHARACTER = 2
Nenue@70 34 local BINDING_TYPE_GLOBAL = 1
Nenue@70 35 kb.configTitle = {
Nenue@70 36 [BINDING_TYPE_GLOBAL] = L('Global Binds'),
Nenue@70 37 [BINDING_TYPE_CHARACTER] = L('%%s'),
Nenue@70 38 [BINDING_TYPE_SPECIALIZATION] = L('%%s')
Nenue@70 39 }
Nenue@70 40 kb.configDescription = {
Nenue@70 41 [BINDING_TYPE_GLOBAL] = L('The bindings are applied globally.'),
Nenue@70 42 [BINDING_TYPE_CHARACTER] = L('Applied when you log onto this character.'),
Nenue@70 43 [BINDING_TYPE_SPECIALIZATION] = L('Applied when you select this specialization.'),
Nenue@70 44 }
Nenue@70 45
Nenue@70 46
Nenue@70 47 kb.ChangedBindings = {}
Nenue@70 48 kb.SystemBindings = {}
Nenue@70 49 kb.ActionTypes = {}
Nenue@78 50 -- pet spell categories
Nenue@70 51 kb.PetCache = {
Nenue@70 52 spell = {},
Nenue@70 53 spellslot = {},
Nenue@70 54 action = {},
Nenue@70 55 special = {},
Nenue@70 56 subtext = {}
Nenue@70 57 }
Nenue@78 58 -- general spell resolution
Nenue@70 59 kb.DynamicSpells = {
Nenue@70 60 profession = {},
Nenue@70 61 petaction = {},
Nenue@70 62 talent = {},
Nenue@70 63 }
Nenue@78 64
Nenue@70 65 kb.ProfessionCache = {}
Nenue@78 66
Nenue@78 67 kb.specInfo = {}
Nenue@70 68 kb.pendingCalls = {}
Nenue@70 69 kb.pendingAttributes = {}
Nenue@70 70 kb.configHeaders = {}
Nenue@70 71 kb.loadedProfiles = {}
Nenue@70 72 kb.orderedProfiles = {}
Nenue@70 73 kb.buttons = {}
Nenue@70 74 kb.macros = {}
Nenue@72 75 kb.bindings = setmetatable({}, {__newindex = function(t,k,v) rawset(t,k,v) cprint('adding', k, v) end})
Nenue@70 76 kb.petFrames = {} -- pet data is slightly delayed, their buttons are indexed here so they can be refreshed
Nenue@70 77 kb.talentFrames = {}
Nenue@70 78 kb.professionFrames = {}
Nenue@70 79
Nenue@70 80 -- these are sent to plugin
Nenue@70 81
Nenue@70 82
Nenue@70 83 local db
Nenue@70 84 local _G = _G
Nenue@70 85 local UnitName, SelectedRealmName, InCombatLockdown, UnitClass = UnitName, SelectedRealmName, InCombatLockdown, UnitClass
Nenue@70 86 local tostring, select, tinsert, pairs = tostring, select, tinsert, pairs
Nenue@70 87 local concat, wipe = table.concat, table.wipe
Nenue@70 88 local classHeader, className, classID = '', '', 0
Nenue@70 89
Nenue@70 90
Nenue@70 91 local CloseButton_OnClick = function()
Nenue@70 92
Nenue@70 93 kb.db.showUI = false
Nenue@70 94 print(kb.db.showUI)
Nenue@70 95 SkeletonKey:SetShown(kb.db.showUI)
Nenue@70 96 end
Nenue@70 97
Nenue@70 98 --- Returns conflicting assignment and binding profiles for use in displaying confirmations
Nenue@70 99 kb.IsCommandBound = function(self, command)
Nenue@70 100 local isAssigned, assignedBy = false, kb.db.bindMode
Nenue@70 101 local isBound, boundBy = false, kb.db.bindMode
Nenue@70 102 command = command or self.command
Nenue@70 103 for i = 1, #kb.orderedProfiles do
Nenue@70 104 local profile = kb.orderedProfiles[i]
Nenue@70 105 if i ~= kb.db.bindMode then
Nenue@70 106
Nenue@70 107 if profile.commands[command] then
Nenue@70 108 print(' command: ', i , kb.configHeaders[i], profile.commands[command])
Nenue@70 109 isAssigned = true
Nenue@70 110 assignedBy = i
Nenue@70 111 end
Nenue@70 112 if profile.bound[command] then
Nenue@70 113 print(' bound: ', i , kb.configHeaders[i], profile.bound[command])
Nenue@70 114 isBound = true
Nenue@70 115 boundBy = i
Nenue@70 116 end
Nenue@70 117
Nenue@70 118
Nenue@70 119
Nenue@70 120
Nenue@70 121 if isAssigned and isBound then
Nenue@70 122 print(' hit: ', i , kb.configHeaders[i], profile.commands[command], profile.bound[command])
Nenue@70 123 break
Nenue@70 124 end
Nenue@70 125 end
Nenue@70 126
Nenue@70 127 end
Nenue@70 128
Nenue@70 129 print('|cFFFFFF00IsCommandBound:|r', command,'|r [profile:', kb.db.bindMode .. ']', isAssigned, isBound, assignedBy, boundBy)
Nenue@70 130 return isAssigned, isBound, assignedBy, boundBy
Nenue@70 131 end
Nenue@70 132
Nenue@70 133 local talentSpellHardCodes = {
Nenue@70 134 [109248] = 'Binding Shot',
Nenue@70 135 }
Nenue@70 136
Nenue@70 137 --- Returns a value for use with Texture:SetDesaturated()
Nenue@70 138 kb.BindingIsLocked = function(key)
Nenue@70 139 local success = false
Nenue@70 140 for i = 1, db.bindMode-1 do
Nenue@70 141 local tier = kb.orderedProfiles[i]
Nenue@70 142 if tier.bindings[key] then
Nenue@70 143 success = true
Nenue@70 144 break
Nenue@70 145 end
Nenue@70 146 end
Nenue@70 147 return success
Nenue@70 148 end
Nenue@70 149
Nenue@70 150 --- Translates GetBindingKey() results into a printable string.
Nenue@70 151 kb.BindingString = function(...)
Nenue@70 152 local stack = {}
Nenue@70 153 for i = 1, select('#', ...) do
Nenue@70 154 local key = select(i, ...)
Nenue@70 155 if type(key) == 'string' then
Nenue@74 156 stack[i] = key:gsub('SHIFT', 's'):gsub('ALT', 'a'):gsub('CTRL', 'c'):gsub('SPACE', 'Sp'):gsub('BUTTON', 'M '):gsub('NUMPAD', '# ')
Nenue@74 157 end
Nenue@70 158 end
Nenue@70 159
Nenue@70 160 if #stack >= 1 then
Nenue@70 161 return concat(stack, ',')
Nenue@70 162 else
Nenue@70 163 return nil
Nenue@70 164 end
Nenue@70 165 end
Nenue@70 166
Nenue@70 167
Nenue@70 168 function kb:print(...)
Nenue@70 169
Nenue@70 170 local msg = '|cFF0088FFSkeletonKey|r:'
Nenue@70 171 for i = 1, select('#', ...) do
Nenue@70 172 msg = msg .. ' ' .. tostring(select(i, ...))
Nenue@70 173 end
Nenue@70 174 DEFAULT_CHAT_FRAME:AddMessage(msg)
Nenue@70 175 end
Nenue@70 176
Nenue@70 177
Nenue@70 178 kb.Command = function(args, editor)
Nenue@70 179 if args:match("import") then
Nenue@70 180 kb.ImportCommmit(args)
Nenue@70 181 return
Nenue@70 182 elseif args:match("scan") then
Nenue@70 183 kb.ImportScan(args)
Nenue@70 184 SkeletonKey:Update()
Nenue@70 185 return
Nenue@70 186 elseif args:match("load") then
Nenue@70 187 kb:ApplyAllBindings()
Nenue@70 188 return
Nenue@70 189 end
Nenue@70 190
Nenue@70 191 if db.showUI then
Nenue@70 192 db.showUI = false
Nenue@70 193 else
Nenue@70 194 db.showUI = true
Nenue@70 195 if not InCombatLockdown() then
Nenue@70 196 kb:print(L('Config frame opened.'))
Nenue@70 197 else
Nenue@70 198 kb:print(L('Config frame will open upon exiting combat.'))
Nenue@70 199 end
Nenue@70 200 end
Nenue@70 201 SkeletonKey:SetShown(db.showUI)
Nenue@70 202 SkeletonKey:Update(true)
Nenue@70 203 end
Nenue@70 204
Nenue@70 205 kb.InitProfile = function(profile, prototype)
Nenue@70 206 print('|cFF00FFFFkb.InitProfile()', profile, prototype)
Nenue@70 207 if not profile then
Nenue@70 208 profile = {}
Nenue@70 209 end
Nenue@70 210 if prototype then
Nenue@70 211 for k,v in pairs(prototype) do
Nenue@70 212 if not profile[k] then
Nenue@70 213 profile[k] = v
Nenue@70 214 end
Nenue@70 215 end
Nenue@70 216 end
Nenue@70 217
Nenue@70 218 profile.bound = profile.bound or {}
Nenue@70 219 profile.buttons = profile.buttons or {}
Nenue@70 220 profile.commands = profile.commands or {}
Nenue@70 221 profile.bindings = profile.bindings or {}
Nenue@70 222 profile.macros = profile.macros or {}
Nenue@70 223 profile.talents = profile.talents or {}
Nenue@70 224 return profile
Nenue@70 225 end
Nenue@70 226
Nenue@70 227 kb.ResetProfile = function(profile, prototype)
Nenue@70 228 if profile == kb.currentProfile then
Nenue@70 229 for i, button in pairs(kb.buttons) do
Nenue@70 230 kb.ReleaseSlot(button)
Nenue@70 231 end
Nenue@70 232 end
Nenue@70 233 wipe(profile)
Nenue@70 234 kb.InitProfile(profile, prototype)
Nenue@70 235 end
Nenue@70 236
Nenue@70 237
Nenue@70 238
Nenue@70 239 --- Handles constructing spec profiles as they are selected
Nenue@70 240
Nenue@70 241
Nenue@70 242 --- Obtains profile data or creates the necessary tables
Nenue@70 243 kb.SelectProfileSet = function(name)
Nenue@70 244
Nenue@70 245 local defaultMode
Nenue@70 246 --- General info
Nenue@70 247 classHeader, className, classID = UnitClass('player')
Nenue@74 248 --kb:print('|cFF00FF00profile:|r', name)
Nenue@74 249 --kb:print('|cFF00FF00class:|r', UnitClass('player'))
Nenue@70 250
Nenue@70 251 defaultMode = BINDING_TYPE_GLOBAL
Nenue@70 252 if db[name] then
Nenue@70 253 defaultMode = BINDING_TYPE_CHARACTER
Nenue@70 254 if db[name][kb.specInfo.id] then
Nenue@70 255 defaultMode = BINDING_TYPE_SPECIALIZATION
Nenue@70 256 end
Nenue@70 257 end
Nenue@70 258
Nenue@70 259 db[name] = kb.InitProfile(db[name],
Nenue@70 260 {
Nenue@70 261 classHeader = classHeader,
Nenue@70 262 className = className,
Nenue@70 263 classID = classID
Nenue@70 264 })
Nenue@70 265 db[name][kb.specInfo.id] = kb.InitProfile(db[name][kb.specInfo.id],
Nenue@70 266 {
Nenue@70 267 specID = kb.specInfo.id,
Nenue@70 268 specName = kb.specInfo.name
Nenue@70 269 })
Nenue@70 270
Nenue@70 271 kb.loadedProfiles[BINDING_TYPE_GLOBAL] = db
Nenue@70 272 kb.loadedProfiles[BINDING_TYPE_CHARACTER] = db[name]
Nenue@70 273 kb.loadedProfiles[BINDING_TYPE_SPECIALIZATION] = db[name][kb.specInfo.id]
Nenue@70 274 kb.orderedProfiles = {db, db[name], db[name][kb.specInfo.id]}
Nenue@70 275
Nenue@70 276 if (not db.bindMode) or (not kb.configTitle[db.bindMode]) then
Nenue@70 277 print('fixing bad bindMode value, was', db.bindMode)
Nenue@70 278 db.bindMode = defaultMode
Nenue@70 279 end
Nenue@70 280
Nenue@70 281
Nenue@70 282 print(BINDING_TYPE_GLOBAL)
Nenue@70 283 kb.configHeaders[BINDING_TYPE_GLOBAL] = kb.configTitle[BINDING_TYPE_GLOBAL]
Nenue@70 284 kb.configHeaders[BINDING_TYPE_CHARACTER] = kb.configTitle[BINDING_TYPE_CHARACTER]:format(UnitName('player', true))
Nenue@70 285 kb.configHeaders[BINDING_TYPE_SPECIALIZATION] = kb.configTitle[BINDING_TYPE_SPECIALIZATION]:format(kb.specInfo.name or '')
Nenue@70 286
Nenue@70 287
Nenue@70 288 setmetatable(kb.loadedProfiles[BINDING_TYPE_GLOBAL], {__tostring =function() return kb.configHeaders[BINDING_TYPE_GLOBAL] end})
Nenue@70 289 setmetatable(kb.loadedProfiles[BINDING_TYPE_CHARACTER], {__tostring =function() return kb.configHeaders[BINDING_TYPE_CHARACTER] end})
Nenue@70 290 setmetatable(kb.loadedProfiles[BINDING_TYPE_SPECIALIZATION], {__tostring =function() return kb.configHeaders[BINDING_TYPE_SPECIALIZATION] end})
Nenue@70 291
Nenue@70 292 kb.currentProfile = kb.loadedProfiles[db.bindMode]
Nenue@70 293 kb.currentHeader = kb.configHeaders[db.bindMode]
Nenue@74 294
Nenue@74 295 print('|cFF88FF00SelectProfile()|r', kb.profileName, classHeader, kb.specInfo.name)
Nenue@70 296 end
Nenue@70 297
Nenue@70 298
Nenue@70 299 function SkeletonKeyMixin:SetTab (id)
Nenue@70 300 self.scrollCache[db.bindMode] = kb.scrollOffset
Nenue@70 301 db.bindMode =id
Nenue@70 302 kb.currentProfile = kb.loadedProfiles[id]
Nenue@70 303 kb.currentHeader = kb.configHeaders[db.bindMode]
Nenue@70 304 kb.scrollOffset = self.scrollCache[db.bindMode] or 0
Nenue@70 305 self:Update(true)
Nenue@70 306 end
Nenue@70 307
Nenue@70 308 kb.ConfirmBindings = function()
Nenue@70 309 kb.ApplyAllBindings()
Nenue@70 310 if #kb.pendingAttributes == 0 then
Nenue@70 311 kb:print(L("Manual bindings update finished."))
Nenue@70 312 else
Nenue@70 313 kb:print(L("Manual update will complete upon exiting combat."))
Nenue@70 314 end
Nenue@70 315 SkeletonKey:Update()
Nenue@70 316 end
Nenue@70 317
Nenue@70 318 function SkeletonKeyMixin:OnLoad()
Nenue@70 319 kb.frame = self
Nenue@70 320 print('|cFF0088FF'..self:GetName()..':OnLoad()')
Nenue@70 321
Nenue@70 322 self.CloseButton:SetScript('OnClick', CloseButton_OnClick)
Nenue@70 323 self:RegisterEvent('PLAYER_ENTERING_WORLD')
Nenue@70 324 self:RegisterEvent('ADDON_LOADED')
Nenue@74 325 self:RegisterEvent('PLAYER_LOGIN')
Nenue@74 326 self:RegisterUnitEvent('PLAYER_SPECIALIZATION_CHANGED', 'player')
Nenue@70 327 self:EnableKeyboard(false)
Nenue@70 328
Nenue@70 329 self.zoomScale = self:GetScale()
Nenue@70 330 self.backdrop = self:GetBackdrop()
Nenue@70 331 self.backdropColor = {self:GetBackdropColor() }
Nenue@70 332 self.backdropBorder = {self:GetBackdropBorderColor() }
Nenue@70 333
Nenue@70 334 end
Nenue@70 335
Nenue@70 336 function SkeletonKeyMixin:OnEvent(event, arg)
Nenue@74 337 print('|cFFFF0088'.. event..'|r', unit)
Nenue@74 338 if event == 'PLAYER_LOGIN' then
Nenue@70 339
Nenue@70 340 print('|cFF00FFFF'..event ..'|r', arg or '', IsLoggedIn())
Nenue@74 341 if not self.initialized then
Nenue@70 342 self:Setup()
Nenue@70 343 self.initialized = true
Nenue@74 344 kb.ApplyAllBindings()
Nenue@74 345 self:Update(true)
Nenue@70 346 end
Nenue@70 347
Nenue@70 348
Nenue@70 349 elseif kb[event] then
Nenue@70 350 if self.initialized then
Nenue@70 351 print('|cFF0088FF'..event ..'|r', arg or '')
Nenue@70 352 kb[event](self, event, arg)
Nenue@70 353 else
Nenue@70 354
Nenue@70 355 print('|cFF004488'..event ..'|r', arg or '')
Nenue@70 356 end
Nenue@70 357 end
Nenue@70 358 end
Nenue@70 359
Nenue@74 360 function SkeletonKeyMixin:RefreshSpells()
Nenue@74 361
Nenue@74 362 kb.UpdateTalentInfo()
Nenue@74 363 kb.UpdatePetInfo()
Nenue@74 364 end
Nenue@70 365
Nenue@70 366 --- post ADDON_LOADED
Nenue@70 367 function SkeletonKeyMixin:Setup ()
Nenue@70 368 print('|cFF00FFFF'..self:GetName()..':Setup()')
Nenue@70 369 SkeletonKeyDB = kb.InitProfile(SkeletonKeyDB, {})
Nenue@70 370 kb.db = _G.SkeletonKeyDB
Nenue@70 371 kb.playerName = UnitName('player')
Nenue@70 372 kb.playerRealm = SelectedRealmName()
Nenue@70 373 kb.profileName = kb.playerRealm .. '_' .. kb.playerName
Nenue@70 374 db = kb.db
Nenue@70 375
Nenue@70 376 kb.UpdateSpecInfo()
Nenue@70 377 kb.UpdateTalentInfo()
Nenue@70 378 kb.SelectProfileSet(kb.profileName)
Nenue@74 379 self:SetShown(kb.db.showUI)
Nenue@70 380 -- todo: redo import checking
Nenue@70 381
Nenue@70 382 kb.UpdateSystemBinds()
Nenue@70 383
Nenue@70 384 if not InCombatLockdown() then
Nenue@70 385 kb.CreateHooks()
Nenue@70 386 else
Nenue@70 387 kb:print('Some functionality will not load until breaking combat.')
Nenue@70 388 tinsert(kb.pendingCalls, kb.CreateHooks)
Nenue@70 389 end
Nenue@70 390 SLASH_SKB1 = "/skb"
Nenue@70 391 SLASH_SKB2 = "/skeletonkey"
Nenue@70 392 SlashCmdList.SKB = kb.Command
Nenue@70 393
Nenue@70 394
Nenue@70 395 self:RegisterEvent('UPDATE_MACROS')
Nenue@74 396 --self:RegisterEvent('UPDATE_BINDINGS')
Nenue@70 397 self:RegisterUnitEvent('UNIT_PORTRAIT_UPDATE', 'player', 'pet')
Nenue@70 398 self:RegisterEvent('PLAYER_REGEN_DISABLED')
Nenue@70 399 self:RegisterEvent('PLAYER_REGEN_ENABLED')
Nenue@74 400 self:RegisterEvent('SPELLS_CHANGED')
Nenue@74 401 self:RegisterEvent('PLAYER_TALENT_UPDATE')
Nenue@76 402 self:RegisterEvent('PLAYER_PVP_TALENT_UPDATE')
Nenue@70 403
Nenue@70 404 self:RegisterForDrag('LeftButton')
Nenue@70 405 self:SetMovable(true)
Nenue@70 406 for index, frame in ipairs(self.Plugins) do
Nenue@70 407 frame:Setup()
Nenue@70 408 end
Nenue@70 409 end
Nenue@70 410
Nenue@70 411
Nenue@70 412 -- Volatiles Access
Nenue@70 413 kb.FormatActionID = function(actionType, actionID) return tostring(actionType) .. '_' .. tostring(actionID) end
Nenue@70 414 kb.GetBindings = function() return kb.bindings end
Nenue@70 415 kb.GetButtons = function() return kb.buttons end
Nenue@70 416 kb.GetCharacterProfile = function () return kb.loadedProfiles[BINDING_TYPE_CHARACTER] end
Nenue@70 417 kb.GetGlobalProfile = function () return kb.loadedProfiles[BINDING_TYPE_GLOBAL] end
Nenue@70 418 kb.GetSpecProfile = function () return kb.loadedProfiles[BINDING_TYPE_SPECIALIZATION] end
Nenue@70 419
Nenue@70 420