view SkeletonKey.lua @ 76:6623b7f2c1ca v7.1.5-78-release

Added: - key assignment and unbinding for inactive talents Bug Fixes: - fixed spell access check for talents - fixed detection of Legion PvP talents - fixed unbind command not clearing assigned key text - fixed frame content not loading after combat Miscellaneous: - bindings are determined from the button's assignment list - increment profile version to remove deprecated talents list
author Nenue
date Mon, 23 Jan 2017 20:07:30 -0500
parents 9824d524a661
children d4c100b0fd01
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:RegisterEvent('PLAYER_PVP_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