view SkeletonKey/SkeletonKey.lua @ 16:cdd387d39137

filename refactor
author Nenue
date Fri, 29 Jul 2016 21:18:15 -0400
parents 32d64e42ec9b
children 500f9b2bd9ac
line wrap: on
line source
--------------------------------------------
-- SkeletonKey
-- Krakyn-Mal'Ganis
-- @project-revision@ @project-hash@
-- @file-revision@ @file-hash@
-- Created: 6/16/2016 3:47 AM
--------------------------------------------
-- kb
--   .StoreBinding(button, key)                     bind current keystroke to command
--   .GetSlot(index)                         return display slot
--   .SetSlot(button, command, name, icon)   assign display slot
--   .ReleaseSlot(button)                       clear button command
--   .UpdateSlot(button)                       update button contents
--   .SelectProfile(name)                         set profile character
--   .ApplyBindings(bindings)                   walk table with SetBinding()

local _
local kb, print = LibStub("LibKraken").register(KeyBinder)
local cprint = DEVIAN_WORKSPACE and function(...) _G.print('Cfg', ...) end or function() end
kb.L = setmetatable({}, {
  __call = function(t, k, ...) return format(t[k] or '', ...) end
})
local L = kb.L

--- Caps Lock literals
local CLICK_KEYBINDER_MACRO = "CLICK KeyBinderMacro:"
local CLICK_KEYBINDER_KEY = "CLICK KeyBinderKey:"
local CLASS_ICON_TEXTURE = "Interface\\GLUES\\CHARACTERCREATE\\UI-CHARACTERCREATE-CLASSES"
local FOOTER_OFFSET
local HEADER_OFFSET
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 = '|cFFFF4400Unable to use |r|cFF00FF00%s|r|cFFFF4400 (currently |cFFFFFF00%s|r|cFFFF4400)|r'


local BINDING_TYPE_SPECIALIZATION = 3
local BINDING_TYPE_CHARACTER = 2
local BINDING_TYPE_GLOBAL = 1
kb.configTitle = {
  [BINDING_TYPE_GLOBAL] = 'Global Binds',
  [BINDING_TYPE_CHARACTER] = 'Character: %s',
  [BINDING_TYPE_SPECIALIZATION] = 'Specialization: %s'
}
kb.configDescription = {
  [BINDING_TYPE_GLOBAL] = 'The bindings are applied globally.',
  [BINDING_TYPE_CHARACTER] = 'Applied when you log onto this character.',
  [BINDING_TYPE_SPECIALIZATION] = 'Applied when you log onto this character and are that specialization.',
}



kb.inactiveTalentBindings = {}
kb.configHeaders = {}
kb.loadedProfiles = {}
kb.orderedProfiles = {}
kb.buttons = {}
kb.macros = {}
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 bindings = {}
local macros = {}
local talentBindings = {}

local protected = {
  ['OPENCHATSLASH'] = true,
  ['OPENCHAT'] = true,
}


local db
local bindHeader, currentHeader = '', ''
local specID, specGlobalID, specName, specDesc, specTexture, characterHeader = 0, 0, 'SPEC_NAME', 'SPEC_DESCRIPTION', 'Interface\\ICONS\\INV_Misc_QuestionMark', 'PLAYER_NAME'
local classHeader, className, classID = '', '', 0
local bindsCommitted = true
local forceButtonUpdate = false

--- Control handles
local saveButton, restoreButton, clearButton

--- Returns conflicting assignment and binding profiles for use in displaying confirmations
kb.IsCommandBound = function(self, command)
  local isAssigned, assignedBy = false, db.bindMode
  local isBound, boundBy = false, db.bindMode


  command = command or self.command
  for i = 1, #kb.orderedProfiles do
    local tier = kb.orderedProfiles[i]
    if i ~= db.bindMode then

      if tier.commands[command] then
        isAssigned = true
        assignedBy = i
      end
      if tier.bound[command] then
        isBound = true
        boundBy = i
      end


      --print(' *', configHeaders[i], tier.commands[command], tier.bound[command])

      if isAssigned and isBound then
        break
      end
    end

  end

  print('|cFFFFFF00IsCommandBound:|r', command:gsub(CLICK_KEYBINDER_MACRO, ''),'|r [profile:', 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, ...)
    stack[i] = key:gsub('SHIFT', 's'):gsub('ALT', 'a'):gsub('CTRL', 'c'):gsub('SPACE', 'Sp'):gsub('BUTTON', 'M '):gsub('NUMPAD', '# ')
  end

  if #stack >= 1 then
    return table.concat(stack, ',')
  else
    return nil
  end
end






kb.inactiveTalentBindings = {}
kb.ApplyTalentBinding = function(talentInfo, cache)
  for i = 5, #talentInfo do
    local command = CLICK_KEYBINDER_KEY.. talentInfo[2]
    SetBinding(talentInfo[i], command)
    cprint(' **', talentInfo[i], '->', command)
    tinsert(cache, talentInfo[i])
  end
end
kb.CacheTalentBinding = function(talentInfo, cache)

  local spellID = talentInfo[4]
  kb.inactiveTalentBindings[spellID] = kb.inactiveTalentBindings[spellID] or {}
  kb.inactiveTalentBindings[spellID] = {select(5,unpack(talentInfo)) }
  --cprint(spellID, unpack(kb.inactiveTalentBindings[spellID]))
end

kb.LoadBinding = function(command, name, icon, actionType, actionID, macroName, macroText )

  if actionType == 'spell' then
    KeyBinderKey:SetAttribute("*type-"..name, actionType)
    KeyBinderKey:SetAttribute("*"..actionType.."-"..name, name)

  elseif actionType == 'item' then
    KeyBinderKey:SetAttribute("*type-"..name, actionType)
    KeyBinderKey:SetAttribute("*"..actionType.."-"..name, name)
  elseif actionType == 'macro' then
    KeyBinderMacro:SetAttribute("*macro-"..macroName, actionID)
  else
    KeyBinderMacro:SetAttribute("*macrotext-"..macroName, macroText)
  end

  cprint('Loading binding', actionType, actionID)
  bindings[actionType] = bindings[actionType] or {}
  bindings[actionType][actionID] = bindings[actionType][actionID] or {}
  bindings[command] = bindings[actionType][actionID]
  return bindings[actionType], actionID
end

kb.ApplyBindings = function (profile)
  cprint('binding profile', profile)
  for slot, data in pairs(profile.buttons) do
    kb.LoadBinding(unpack(data))
  end

  for key, command in pairs(profile.bindings) do

    cprint(' *', key, '->', command)

    --_G.print('HotKey','loading', key, command)
    SetBinding(key, command)
    if bindings[command] and not tContains(bindings[command], key) then
      tinsert(bindings[command], key)
    end
  end

  for spellName, talentInfo in pairs(profile.talents) do
    local dummy = GetSpellInfo(spellName)
    local func = kb.CacheTalentBinding
    local dest = kb.inactiveTalentBindings
    if dummy then
      cprint('|cFFBBFF00Active:|r', dummy)
      local macroName, spellName, actionType, actionID = unpack(talentInfo)
      bindings[actionType] = bindings[actionType] or {}
      bindings[actionType][actionID] = {}
      func = kb.ApplyTalentBinding
      dest = bindings[actionType][actionID]
    else

      cprint('|cFFFF4400Inactive:|r', talentInfo[2])
    end
    func(talentInfo, dest)
  end

  SaveBindings(GetCurrentBindingSet())
end

kb.ApplyAllBindings =function ()
  table.wipe(kb.inactiveTalentBindings)


  -- reflect action key settings
  if GetCVarBool("ActionButtonUseKeyDown") then
    KeyBinderMacro:RegisterForClicks("AnyDown")
    KeyBinderKey:RegisterForClicks("AnyDown")
  else
    KeyBinderMacro:RegisterForClicks("AnyUp")
    KeyBinderKey:RegisterForClicks("AnyUp")
  end

  for i, profile in ipairs(kb.orderedProfiles) do
    kb.ApplyBindings(profile)
  end
  -- do this after to ensure that profession binds are properly overridden
  kb.UpdateProfessionInfo()


end

kb.Command = function(args, editor)
  if args:match("import") then
    kb.ImportCommmit(args)
    return
  elseif args:match("scan") then
    kb.ImportScan(args)
    kb.ui()
    return
  elseif args:match("load") then
    kb:ApplyAllBindings()
    return
  end

  if db.showUI then
    db.showUI = false
    kb:print('|cFFFFFF00KeyBinds|r trace, |cFFFF0000OFF|r.')
    kb:Hide()
  else
    db.showUI = true
    kb:print('|cFFFFFF00KeyBinds|r trace, |cFF00FF00ON|r.')
  end
  kb.ui(true)
end

kb.InitProfile = function(profile, prototype)
  if not profile then
    profile = {}
  end
  if prototype then
    print('appplying prototype', prototype)
    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(buttons) do
      kb.ReleaseSlot(button)
    end
  end
  table.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')
  print('|cFF00FF00profile:|r', name)
  print('|cFF00FF00class:|r', UnitClass('player'))

  --- Global
  defaultMode = BINDING_TYPE_GLOBAL
  kb.InitProfile(db)
  kb.loadedProfiles[BINDING_TYPE_GLOBAL] = db

  --- Character
  if name then
    db[name] = kb.InitProfile(db[name],
      {classHeader = classHeader, className = className, classID = classID})
    kb.loadedProfiles[BINDING_TYPE_CHARACTER] = db[name]
    defaultMode = BINDING_TYPE_CHARACTER
  end

  --- Mutable skills data
  kb.UpdateSpecInfo()
  kb.UpdateTalentInfo()

  kb.orderedProfiles = {kb.loadedProfiles[BINDING_TYPE_GLOBAL], kb.loadedProfiles[BINDING_TYPE_CHARACTER], kb.loadedProfiles[BINDING_TYPE_SPECIALIZATION]}
  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)


  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})

  print('|cFF00FF00bindMode:|r', db.bindMode)
  kb.currentProfile = kb.loadedProfiles[db.bindMode]
end

local scrollCache = {}
kb.SelectTab = function(self)
  scrollCache[db.bindMode] = kb.scrollOffset
  db.bindMode = self:GetID()
  kb.currentProfile = kb.loadedProfiles[self:GetID()]
  kb.scrollOffset = scrollCache[db.bindMode] or 0
  kb.ui(true)
end

kb.RevertBindings = function()
  -- todo: reversion code
end

kb.ConfirmBindings = function()
  kb.ApplyAllBindings()
  kb.ui()
end




--- post ADDON_LOADED
kb.variables = function()
  SkeletonKeyDB = SkeletonKeyDB or {spec = {}}
  kb.db = SkeletonKeyDB
  kb.playerName = UnitName('player')
  kb.playerRealm = SelectedRealmName()
  kb.profileName = kb.playerRealm .. '_' .. kb.playerName
  db = kb.db

  kb.SelectProfileSet(kb.profileName)
  -- todo: redo import checking

  kb.UpdateSystemBinds()
  kb.ApplyAllBindings()

  kb.ui(true)
end


kb.wrap = function(module)
  kb.modules = kb.modules or {}
  tinsert(kb.modules, module)
end

-- Volatiles Access
kb.GetBindings = function() return bindings end
kb.GetButtons = function() return buttons end
kb.GetCharacterProfile = function () return kb.loadedProfiles[BINDING_TYPE_CHARACTER] end
kb.GetGlobalProfile = function () return kb.loadedProfiles[BINDING_TYPE_GLOBAL] end
kb.GetLooseTalents = function() return talentBindings end
kb.GetReverts = function() return reverts end
kb.GetSpecProfile = function () return kb.loadedProfiles[BINDING_TYPE_SPECIALIZATION] end


SLASH_SKB1 = "/skb"
SLASH_SKB2 = "/skeletonkey"
SlashCmdList.SKB = kb.Command