view SkeletonKey/KeySlot.lua @ 22:f6dd297cb812

- fix pet stance/actioni bindings - fix pet action availability check
author Nenue
date Sat, 30 Jul 2016 20:44:03 -0400
parents 564015ef0317
children
line wrap: on
line source
-- SkeletonKey
-- KeySlot.lua
-- Created: 7/28/2016 11:26 PM
-- %file-revision%
-- Code dealing with the slot button innards; they are invoked by frame script and should only chain to Set/Release

local kb, print = LibStub('LibKraken').register(KeyBinder, 'Slot')
local L = kb.L
local CURSOR_SPELLSLOT, CURSOR_BOOKTYPE, CURSOR_PETACTION, CURSOR_TEXTURE
local SUMMON_RANDOM_FAVORITE_MOUNT_SPELL = 150544
local BORDER_UNASSIGNED = {0.2,0.2,0.2,1}
local BORDER_ASSIGNED = {1,1,1,1}
local BORDER_DYNAMIC = {1,1,0,1}
local BORDER_PENDING = {1,0.5,0,1 }
local BUTTON_HEADERS = {
  ['spell'] = SPELLS,
  ['macro'] = MACRO,
  ['petaction'] = PET,
  ['mount'] = MOUNT,
  ['battlepet'] = BATTLEPET,


  [5] = PROFESSIONS_FIRST_AID,
  [7] = PROFESSIONS_COOKING,
  [9] = PROFESSIONS_FISHING,
  [10] = PROFESSIONS_ARCHAEOLOGY,

}

-- This is needed to identify a spells that aren't reflected by GetCursorInfo()
hooksecurefunc("PickupSpellBookItem", function(slot, bookType)
  print('|cFFFF4400PickupSpellBookItem('.. tostring(slot).. ', '..tostring(bookType)..')')
  CURSOR_SPELLSLOT = slot
  CURSOR_BOOKTYPE = bookType
  CURSOR_TEXTURE = GetSpellBookItemTexture(slot, bookType)
  print('current cursor info', CURSOR_SPELLSLOT, CURSOR_BOOKTYPE, CURSOR_TEXTURE)
end)

-- Pet actions
hooksecurefunc("PickupPetAction", function(slot, ...)
  local isPickup = GetCursorInfo()
  print(slot, ...)
  if kb.PetCache.action[slot] then
    if isPickup then
      local key, _, texture = unpack(kb.PetCache.action[slot])
      local spellName = _G[key] or key
      if spellName and kb.PetCache.spellslot[spellName] then
        CURSOR_SPELLSLOT = kb.PetCache.spellslot[spellName][1]
        CURSOR_BOOKTYPE = BOOKTYPE_PET
        CURSOR_TEXTURE = _G[texture] or texture
      end

    else
      CURSOR_SPELLSLOT = nil
      CURSOR_BOOKTYPE = nil
      CURSOR_TEXTURE = nil
    end
  print('|cFFFF4400PickupPetAction|r', isPickup, CURSOR_PETACTION)
  end

  local name, subtext, texture, isToken = GetPetActionInfo(slot)
  if name then
      kb.PetCache.action[slot] = {name, subtext, texture, isToken}
  end


  print('current cursor info', CURSOR_SPELLSLOT, CURSOR_BOOKTYPE, CURSOR_TEXTURE)

end)


kb.DropToSlot = function(self)
  print(self:GetName(),'|cFF0088FFreceived|r')
  local actionType, actionID, subType, subData = GetCursorInfo()
  print('GetCursorInfo', GetCursorInfo())
  if actionType then

    if actionType == 'flyout' then
      ClearCursor()
      ResetCursor()
      return
    end


    local name, icon, _
    local pickupID, pickupBook

    if actionType == 'spell' then
      actionID = subData
      name, _, icon = GetSpellInfo(actionID)

    elseif actionType == 'macro' then
      name, icon = GetMacroInfo(actionID)
    elseif actionType == 'petaction' then
      if CURSOR_SPELLSLOT and CURSOR_BOOKTYPE then

        local spellType, spellID = GetSpellBookItemInfo(CURSOR_SPELLSLOT, CURSOR_BOOKTYPE)
        local spellName, spellText = GetSpellBookItemName(CURSOR_SPELLSLOT, CURSOR_BOOKTYPE)
        if spellType == 'PETACTION' then
          name = spellName
          actionID = spellText
          icon = CURSOR_TEXTURE
        else
          name, _, icon = GetSpellInfo(spellID)
          actionID = spellID
        end

        pickupID = CURSOR_SPELLSLOT
        pickupBook = CURSOR_BOOKTYPE
      else

      end

    elseif actionType == 'mount' then
      if subType == 0 then
        name, _, icon = GetSpellInfo(SUMMON_RANDOM_FAVORITE_MOUNT_SPELL)
        actionID = 0
      else
        name, _, icon = C_MountJournal.GetMountInfoByID(actionID)
      end
    elseif actionType == 'item' then
      name = GetItemInfo(actionID)
      icon = GetItemIcon(actionID)
      actionID = name
    elseif actionType == 'battlepet' then

      local speciesID, customName, level, xp, maxXp, displayID, isFavorite, petName, petIcon, petType, creatureID = C_PetJournal.GetPetInfoByPetID(detail);
      name = customName or petName
      icon = petIcon

    end
    local macroName, macroText, command = kb.RegisterAction(actionType, actionID, name)


    local isAssigned, isBound, assignedBy, boundBy = kb.IsCommandBound(self, command)
    if isAssigned then
      local popup = StaticPopupDialogs["SKELETONKEY_CONFIRM_ASSIGN_SLOT"]
      popup.slot = self
      popup.text = "Currently assigned in |cFFFFFF00"..tostring(kb.configHeaders[assignedBy]).."|r. Are you sure?"
      popup.oldProfile = assignedBy
      popup.args = {command, name, icon, actionType, actionID, macroName, macroText, pickupID, pickupBook }
      kb:SetScript('OnMouseWheel', nil) -- disable scrolling
      StaticPopup_Show('SKELETONKEY_CONFIRM_ASSIGN_SLOT')
    else
      kb.SetSlot(self, command, name, icon, actionType, actionID, macroName, macroText, pickupID, pickupBook)
      kb.UpdateSlot(self)
      self.active = nil
      ClearCursor()
      ResetCursor()
    end
  end
end


do
  local PickupAction = {
    spell = _G.PickupSpell,
    petaction = _G.PickupSpellBookItem,
    macro = _G.PickupMacro,
    item = _G.PickupItem,
    mount = _G.C_MountJournal.Pickup
  }
  local GetPickupValue = {
    spell = function(self) return select(7, GetSpellInfo(self.actionID)) end,
    petaction = function(self) return self.pickupSlot, self.pickupBook end,
  }
  kb.PickupSlot = function(self)
    if not (self.command and self.isAvailable) then
      return
    end
    print(self.actionType)
    if self.actionType == 'spell' then
      -- It can't be picked up if SpellInfo(name) returns void
      local dummy = GetSpellInfo(self.actionName)
      if not dummy then
        return
      end
    end
    if PickupAction[self.actionType] then
      if GetPickupValue[self.actionType] then
        PickupAction[self.actionType](GetPickupValue[self.actionType](self))
      else
        PickupAction[self.actionType](self.actionID)
      end
      kb.ReleaseSlot(self)
      kb.UpdateSlot(self)
    end
  end
end

kb.UnbindSlot = function(self)

  local keys = {GetBindingKey(self.command) }
  --print('detected', #keys, 'bindings')
  for i, key in pairs(keys) do
    --print('clearing', key)
    SetBinding(key, nil)
    SaveBindings(GetCurrentBindingSet())
    if kb.currentProfile.bindings[key] then
      --kb:print(L('BINDING_REMOVED', self.actionName, kb.currentHeader))
      kb.currentProfile.bindings[key] = nil
    end
    if kb.currentProfile.talents[self.actionName] then
      kb.currentProfile.talents[self.actionName] = nil
    end
    kb.bindings[self.actionType][self.actionID] = nil
  end
  if kb.currentProfile.bound[self.command] then
    kb.currentProfile.bound[self.command] = nil
    --kb:print(BINDING_REMOVED:format(self.actionName, configHeaders[db.bindMode]))
  end

  self.active = false
  kb.UpdateSlot(self, true)
end

--- Updates the current KeyBinding for the button's command
kb.SaveSlot = function(self, key)

  if not self.command then
    return
  end
  print('|cFFFFFF00received|cFFFFFF00', self:GetID(), '|cFF00FFFF', key)

  local modifier = ''
  if IsAltKeyDown() then
    modifier = 'ALT-'
  end
  if IsControlKeyDown() then
    modifier = modifier.. 'CTRL-'
  end
  if IsShiftKeyDown() then
    modifier = modifier..'SHIFT-'
  end
  local binding = modifier..key

  if key == 'ESCAPE' then
  else
    if kb.SystemBindings[binding] then
      kb.statustext:SetText(L('BINDING_FAILED_PROTECTED', binding, kb.SystemBindings[binding]))
      return
    end


    if self.command then

      local previousAction = GetBindingAction(binding)
      if previousAction ~= "" and previousAction ~= self.command then
        if kb.SystemBindings[binding] then
          -- bounce out if trying to use a protected key
          kb.statustext:SetText(L('BINDING_FAILED_PROTECTED', key, GetBindingAction(binding)))
          kb.bindingstext:SetText(nil)
          return false
        else
          kb:print('Discarding keybind for', previousAction)
          -- todo: sort out retcon'd talent spells
        end
      end

      self.binding = binding

      SetBinding(self.binding, self.command)
      SaveBindings(GetCurrentBindingSet())

      local talentInfo
      if self.actionType == 'spell' and kb.TalentCache[self.actionID] then
        print('conditional binding (talent = "'..self.actionName..'")')
        talentInfo = {self.macroName, self.actionName, self.actionType, self.actionID}
        local bindings = {GetBindingKey(self.command) }
        for i, key in ipairs(bindings) do
          tinsert(talentInfo, key)
        end
      end

      for level, profile in ipairs(kb.orderedProfiles) do
        if (level == kb.db.bindMode) then
          profile.bound[self.command] = true
          if talentInfo then
            profile.bindings[self.binding] = nil
          else
            profile.bindings[self.binding] = self.command
          end
          profile.talents[self.actionName] = talentInfo
        else
          profile.bindings[self.binding] = nil
          profile.bound[self.command] = nil
          kb.currentProfile.talents[self.actionName] = nil
        end
        if kb.currentProfile.talents[self.actionID] then
          kb.currentProfile.talents[self.actionID] = nil
        end
      end

      kb:print(L('BINDING_ASSIGNED', self.binding, self.actionName, kb.currentHeader))
    end
  end
  kb.UpdateSlot(self, true)
  return true
end


--- Updates profile assignment and button contents
kb.UpdateSlot = function(self, force)
  local slot = self:GetID()

  if force then
    if kb.currentProfile.buttons[slot] then
      kb.SetSlot(self, unpack(kb.currentProfile.buttons[slot]))
    else
      kb.ReleaseSlot(self)
    end
  end

  if self.command then
    print('['..slot..'] =', self.command, GetBindingKey(self.command))

    if not self.isAvailable then
      self.border:SetColorTexture(1,0,0,1)
      self.ignoreTexture:Show()
    else
      self.ignoreTexture:Hide()

      if self.pending then
        self.border:SetColorTexture(unpack(BORDER_PENDING))
      elseif self.isDynamic then
        self.border:SetColorTexture(unpack(BORDER_DYNAMIC))
      else
        self.border:SetColorTexture(unpack(BORDER_ASSIGNED))
      end
    end


    if self.actionType == 'macro' then
      self.macro:Show()
    else
      self.macro:Hide()
      if self.actionType == 'spell' then
        local dummy = GetSpellInfo(self.actionName)
        if not dummy then
          self.icon:SetDesaturated(true)
        else
          self.icon:SetDesaturated(false)
        end

      end
    end

    if self.isDynamic then
      print('|cFF00BBFFUpdateSlot|r:', self.isDynamic, self.isAvailable, self.actionID)
    end

    if self.isDynamic == 'profession'  then
      local profText = (self.spellNum == 1) and TRADE_SKILLS or (BUTTON_HEADERS[self.profIndex] or GetProfessionInfo(self.profIndex))
      if self.isAvailable then
        print(self.profIndex, 'spnum', type(self.spellNum), (self.spellNum == 1))

        self.statusText = '|cFFFFFF00'..profText..'|r'
        self.bindingText = kb.BindingString(GetBindingKey(self.command))
      else
        self.statusText = '|cFFFF4400'..profText..'|r'
        self.actionName = '(need to train profession #'..self.profNum..')'
        self.bindingText ='?'
      end
    elseif self.isDynamic == 'talent' then

      self.statusText = '|cFF00FFFF'.. TALENT .. '|r'
      if self.isAvailable then
        self.bindingText = kb.BindingString(GetBindingKey(self.command))
      else
        if kb.TalentBindings[self.actionID] then
          print(self.actionID, #kb.TalentBindings[self.actionID])
          self.bindingText= kb.BindingString(unpack(kb.TalentBindings[self.actionID]))
        end

      end
    elseif self.isDynamic == 'petaction' then
      local specialType, specialNum =  self.command:match("petaction_([%a%s]+)_(%d)")
      if specialType and specialNum then
        print('pet skill|cFF00FF00', specialType..'|r', specialNum)
        self.statusText = L(specialType..' %%d'):format(specialNum)
      else
        self.statusText = L('Pet Action')
      end
      self.bindingText = kb.BindingString(GetBindingKey(self.command))
    else
      self.statusText = '|cFF00FF00'.. (BUTTON_HEADERS[self.actionType] and BUTTON_HEADERS[self.actionType] or self.actionType) .. '|r'
      self.bindingText = kb.BindingString(GetBindingKey(self.command))
    end

    local locked, layer = kb.IsCommandBound(self)
    if locked then
      self.icon:SetAlpha(0.5)
    else
      self.icon:SetAlpha(1)
    end


    if self.actionType == 'spell' then
      self.icon:SetTexture(GetSpellTexture(self.actionID))
    end
  else
    self.ignoreTexture:Hide()
  end

  if not self.isAvailable then
    self.bind:SetTextColor(0.7,0.7,0.7,1)
  else
    self.bind:SetTextColor(1,1,1,1)
  end


  if kb.saveTarget and kb.saveTarget ~= self then
    self:SetAlpha(0.25)
  else

    self:SetAlpha(1)
  end


  self.header:SetText(self.statusText)
  self.bind:SetText(self.bindingText)
  self.details:SetText(self.actionName)
end

--- Resets button command
kb.ReleaseSlot = function(self)
  local slot = self:GetID()


  if kb.currentProfile.buttons[slot] then
    kb.currentProfile.buttons[slot] = nil
  end
  if self.command then
    kb.currentProfile.commands[self.command] = nil
  end
  if self.actionType == 'spell' and IsTalentSpell(self.actionName) then
    if kb.currentProfile.talents[self.actionID] then
      kb.currentProfile.talents[self.actionID] = nil
    end
  end
  local droppedKeys = {}

  -- doing removal in second loop to avoid possible iterator shenanigans
  for k,v in pairs(kb.currentProfile.bindings) do
    if v == self.command then
      tinsert(droppedKeys, k)
    end
  end
  if #droppedKeys >=1 then
    for i, k in ipairs(droppedKeys) do
      kb.currentProfile.bindings[k] = nil
    end
  end

  self.isAvailable = nil
  self.isDynamic = nil
  self.bindingText = nil
  self.statusText = nil
  self.command = nil
  self.actionType = nil
  self.actionID = nil
  self.actionName = nil
  self.pickupSlot = nil
  self.pickupBook = nil
  self.macroName = nil
  self.profile = nil
  self.icon:SetTexture(nil)
  self.border:SetColorTexture(unpack(BORDER_UNASSIGNED))
  self:EnableKeyboard(false)
  self:SetScript('OnKeyDown', nil)
  self.ignoreTexture:Hide()
end

kb.SetSlot = function(self, command, name, icon, actionType, actionID, macroName, macroText, pickupSlot, pickupBook)
  local slot = self:GetID()
  local isDynamic, isAvailable

  print('|cFFFFFF00SetSlot|r:', self:GetID())
  if command then

    if actionType == 'spell' then
      local professionNum, spellNum = command:match("profession_(%d)_(%d)")
      if (professionNum and spellNum) then
        isDynamic = 'profession'
        local cacheInfo = kb.ProfessionCache[professionNum..'_'..spellNum]
        if cacheInfo then
          isAvailable = true
          name = cacheInfo.spellName
          icon = cacheInfo.icon
          actionID = cacheInfo.spellID
          self.profIndex = cacheInfo.profIndex
          self.spellOffset = cacheInfo.spellOffset
        end
        print(' Special slot: |cFF00FFFFProfession|r', professionNum, spellNum, isDynamic, isAvailable)

        self.professionNum = tonumber(professionNum)
        self.spellNum = tonumber(spellNum)

      else
        if kb.TalentCache[actionID] then
          isDynamic = 'talent'
          print(' Special slot: |cFFBBFF00talent|r', name, isAvailable)
        end

        isAvailable = GetSpellInfo(name)
      end
    elseif actionType == 'petaction' then
      isDynamic = 'petaction'
      local specialType, specialNum = command:match(actionType..'_([%a%s]+)_(%d)')

      if kb.PetCache.subtext[specialType] and kb.PetCache.subtext[specialType][tonumber(specialNum)] then
        print('***dynamic pet thign', specialType, specialNum)
        --[[ i, spellName, subText, spellID,  texture, specialNum[subText ]]
        pickupSlot, name, specialType, actionID, icon, specialNum = unpack(kb.PetCache.subtext[specialType][tonumber(specialNum)])
        pickupBook = BOOKTYPE_PET
      end

      isAvailable = (kb.PetCache.spellslot[name])


    elseif actionType == 'macro' then
      if not actionID then
        actionID = GetMacroIndexByName(name)
      end
      isAvailable = true
    else
      --- Journal selections
      -- todo: consider using the deep end of blizzard action bar instead
      if not actionID then
        actionID = command:match("^KeyBinderMacro:(.+)")
      end
      isAvailable = true
    end

    if isAvailable then
      local oldCommand = command
      macroName, macroText, command = kb.RegisterAction(actionType, actionID, name)
      if oldCommand ~= command then
        print('|cFFFF4400fixing command string', actionType, actionID, name)
        kb.currentProfile.bound[oldCommand] = nil
        kb.currentProfile.bound[command] = slot
        for k,v in pairs(kb.currentProfile.bindings) do
          if v == oldCommand then
            kb.currentProfile.bindings[k] = command
          end
        end
      end
      kb.LoadBinding(command, name, icon, actionType, actionID, macroName, macroText)
    end


    actionID = actionID or 0
    self:EnableKeyboard(true)
    print(' |cFF00FF00kb.currentProfile.buttons['..slot..'] |cFF00FFFF=|r |cFF00FFFF"'.. command.. '"|r |cFF00FF00"'.. name.. '"|r |cFFFFFF00icon:'.. tostring(icon) .. '|r |cFFFF8800"'.. actionType, '"|r |cFFFF0088id:'.. actionID ..'|r |cFF00FF00"'.. macroName .. '"|r')
    kb.currentProfile.buttons[slot] = {command, name, icon, actionType, actionID, macroName, macroText, pickupSlot, pickupBook }


    -- Clean up conflicting entries for loaded button
    local previous = kb.currentProfile.commands[command]
    if previous ~= slot and kb.buttons[previous] then
      kb.ReleaseSlot(kb.buttons[previous])
    end

    if not (kb.IsCommandBound(self, command) or kb.currentProfile.bound[command]) then

      local binds = {GetBindingKey(command) }
        if #binds >= 1 then
        kb:print('Recovered key binding for', name)
        for i, key in ipairs(binds) do
          kb.currentProfile.bindings[key] = command
          kb.currentProfile.bound[command] = true
        end
      end
    end


    kb.currentProfile.commands[command] = slot

  end

  self.isAvailable = isAvailable
  self.isDynamic = isDynamic

  self.pickupSlot = pickupSlot
  self.pickupBook = pickupBook
  self.macroText = macroText
  self.macroName = macroName
  self.actionType = actionType
  self.actionID = actionID
  self.actionName = name
  self.command = command
  self.icon:SetTexture(icon)
  self.profile = kb.db.bindMode
  self:RegisterForDrag('LeftButton')
end




--- Add to blizzard interfaces
StaticPopupDialogs["SKELETONKEY_CONFIRM_ASSIGN_SLOT"] = {
  text = "Confirm moving an assigned command.",
  button1 = OKAY,
  button2 = CANCEL,
  timeout = 0,
  whileDead = 1,
  showAlert = 1,
  OnAccept = kb.AcceptAssignment,
  OnCancel = function() kb:SetScript('OnMouseWheel', KeyBinder_OnMouseWheel) end
}