view KeyButton.lua @ 91:5005aecc2dc8 v8.0.1b

more 8.0.1 fire spotting
author Nenue
date Tue, 17 Jul 2018 20:47:59 -0400
parents 283dacee2850
children f9df7cd7bfd6
line wrap: on
line source
-- SkeletonKey
-- KeyButton.lua
-- Created: 7/28/2016 11:26 PM
-- %file-revision%
-- Deals with display and manipulation of binding slots

local _, kb = ...
local print = (DEVIAN_PNAME == 'SkeletonKey') and function(...) _G.print('KeyButton', ...) end or function() end
local cprint = (DEVIAN_PNAME == 'SkeletonKey') and function(...) _G.print('Cfg', ...) end or function() end
local L = kb.L
local type, tonumber, tostring, tinsert, tremove, ipairs, pairs = type, tonumber, tostring, tinsert, tremove, ipairs, pairs
local _G, unpack, select, tostring = _G, unpack, select, tostring
local GetSpellBookItemName, GetSpellBookItemTexture, GetSpellBookItemInfo, GetPetActionInfo = GetSpellBookItemName, GetSpellBookItemTexture, GetSpellBookItemInfo, GetPetActionInfo
local GetSpellInfo, GetMacroInfo, GetItemInfo, GetItemIcon = GetSpellInfo, GetMacroInfo, GetItemInfo, GetItemIcon
local GetCursorInfo, ClearCursor, ResetCursor = GetCursorInfo, ClearCursor, ResetCursor
local GetSpellTexture, IsTalentSpell, GetMacroIndexByName, IsAltKeyDown, IsControlKeyDown, IsShiftKeyDown = GetSpellTexture, IsTalentSpell, GetMacroIndexByName, IsAltKeyDown, IsControlKeyDown,IsShiftKeyDown
local GetBindingKey, GetProfessionInfo = GetBindingKey, GetProfessionInfo
local GetMountInfoByID, GetPetInfoByPetID = C_MountJournal.GetMountInfoByID, C_PetJournal.GetPetInfoByPetID
local skb = SkeletonKeyButtonMixin
local CURSOR_SPELLSLOT, CURSOR_BOOKTYPE, CURSOR_PETACTION, CURSOR_TEXTURE
local SUMMON_RANDOM_FAVORITE_MOUNT_SPELL = 150544
local BORDER_UNASSIGNED = {0.6,0.6,0.6,1}
local BORDER_ASSIGNED = {1,1,1,1}
local BORDER_DYNAMIC = {1,1,0,1}
local BORDER_PENDING = {1,0.5,0,1}
local BORDER_REPLACED = {0,1,.5,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,

}

local PROFESSION_HEADERS = {
  [1] = 'Profession 1',
  [2] = 'Profession 2',
  [3] = 10,
  [4] = 7,
  [5] = 9,
  [6] = 5
}

-- Spell replacements that can't be easily detected
local TALENT_SPELLS = {
  -- Shimmer
  [212653] = {
    actionName = 'Blink',
    actionID = 1953,
    icon = [[Interface\\ICONS\\spell_arcane_blink]]
  },
  -- Sidewinders; have to assume arcane sadly
  [214579] = {
    actionName = 'Arcane Shot',
    actionID = 185358,
    icon = [[Interface\\ICONS\\ability_impalingbolt]]
  },
  -- Serenity
  [152173] = {
    actionName = 'Storm, Earth, and Fire',
    actionID = 137639,
    icon = [[Interface\\ICONS\\spell_nature_giftofthewild]]
  },
  -- Carve
  [212436] = {
    actionName = 'Carve',
    actionID = 187708,
    icon = [[Interface\\ICONS\\ability_hunter_carve]]
  }
}


-- This is needed to identify a spells that aren't reflected by GetCursorInfo()
kb.OnPickupPetAction = 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.OnPickupSpellBookItem = 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

kb.CreateHooks = function()
  hooksecurefunc("PickupSpellBookItem", kb.OnPickupSpellBookItem)
  hooksecurefunc("PickupPetAction", kb.OnPickupPetAction)
end



function skb:OnLoad()
  self:EnableKeyboard(false)
  self:EnableMouse(true)
  self:RegisterForDrag('LeftButton')
  self:RegisterForClicks('AnyUp')
end

function skb:OnEnter()
  if not self.command then
    return
  end
  if self.statusText then
    SkeletonKey.statustext:SetText(self.statusText .. ': '..self.actionName)
    SkeletonKey.bindingstext:SetText(self.bindingText)
  end


  if kb.db.hoverInput and kb.saveTarget ~= self then
    self:GetParent():ActivateSlot(self)
    SkeletonKey:Update()
  end
end

function skb:OnLeave()
  if kb.db.hoverInput and kb.saveTarget == self then
    self:GetParent():DeactivateSlot(self)
    SkeletonKey:Update()
  end
end

function skb:OnUpdate()
end
function skb:OnClick(click)
  print(self:GetName(), 'OnMouseDown', click)
  local cursorType = GetCursorInfo()
  if click == 'LeftButton' then
    if cursorType then
      self:DropToSlot()
    else
      if self.command then
        if IsShiftKeyDown() then
          kb.db.stickyMode = true
          KeyBinderStickyMode:SetChecked(true)
        end
        self:GetParent():ActivateSlot(self)
      end
    end
  elseif click == 'RightButton' then
    self:ReleaseSlot()
  else
    SkeletonKey:ProcessInput(strupper(click))
  end
  SkeletonKey:Update()
end

function skb:OnDragStart()
  self:PickupSlot()
end

function skb:OnReceiveDrag(...)
  self:DropToSlot()
end

function skb:DropToSlot ()
  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, _, macroName, macroText
    local pickupID, pickupBook

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

        if TALENT_SPELLS[actionID] then
          name = TALENT_SPELLS[actionID].actionName
          actionID  = TALENT_SPELLS[actionID].actionID
          icon = TALENT_SPELLS[actionID].icon
      elseif actionType == 'macro' then
        name, icon, macroText = GetMacroInfo(actionID)
        macroName = name
      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 = GetMountInfoByID(actionID)
        end
      elseif actionType == 'item' then
        name = GetItemInfo(actionID)
        icon = GetItemIcon(actionID)
      elseif actionType == 'battlepet' then

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

      end
      local _, macroBody, command = kb.RegisterAction(actionType, actionID, name)
      local slotInfo = {
        command = command,
        actionName = name,
        iconPath = icon,
        actionType = actionType,
        actionID = actionID,
        macroName = macroName,
        macroText = macroText or macroBody,
        spellbookSlot = pickupID,
        spellbookType = pickupBook,
        assignedKeys = {GetBindingKey(command)}
      }

      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 = {slotInfo}
        SkeletonKey:SetScript('OnMouseWheel', nil) -- disable scrolling
        StaticPopup_Show('SKELETONKEY_CONFIRM_ASSIGN_SLOT')
      else
        kb.currentProfile.buttons[self:GetID()] = slotInfo
        kb.LoadBinding(slotInfo)
        self:SetSlot(slotInfo)
        self:UpdateSlot()
        self.active = nil
        ClearCursor()
        ResetCursor()
      end
    end
  end
end



do
  local PickupAction = {
    spell = _G.PickupSpell,
    petaction =
      function(...)
        -- needs to be enclosed to acquire hooksecurefunc effects
        _G.PickupSpellBookItem(...)
      end,
    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,
  }
  function skb:PickupSlot ()
    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
      self:ReleaseSlot()
      self:UpdateSlot()
    end
  end
end




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

  if force then
    if kb.currentProfile.buttons[slot] then
      print('loading in', slot, kb.db.bindMode)
      self:SetSlot(kb.currentProfile.buttons[slot])
    else
      self:ReleaseSlot()
    end
  end

  local borderType = BORDER_UNASSIGNED
  local displayName = self.actionName
  local displayTexture = self.iconPath
  local altName, altTexture
  if self.command then

    print('|cFFFF4400:', self.actionName, #self.assignedKeys, table.concat(self.assignedKeys, ','))
    print('|cFF00FF88:', self.isAvailable, self.actionID)

    self.bindingText= kb.BindingString(unpack(self.assignedKeys))

    if not self.isAvailable then
      borderType = BORDER_DYNAMIC
      self.ignoreTexture:Show()
    else
      self.ignoreTexture:Hide()

      if self.pending then
        borderType = BORDER_PENDING
      elseif self.dynamicType then
        borderType = BORDER_DYNAMIC
      else
        borderType = BORDER_ASSIGNED
      end
    end

    if self.actionType == 'macro' then
      self.macro:Show()
    else
      self.macro:Hide()
      if self.actionType == 'spell' then
        altName, _, altTexture = GetSpellInfo(self.actionName)
        self.isAvailable = displayName and true or false
        if altName and (altName ~= self.actionName) then
          displayName = '|cFFFFFF00'..altName..'|r ('..self.actionName..')'
          displayTexture = altTexture
          borderType = BORDER_REPLACED
        end

      end
    end


    if self.dynamicType == 'profession'  then
      if self.isAvailable then
        self.statusText = '|cFFFFFF00Profession Slot|r ' .. tostring(self.dynamicIndex) .. '-' .. tostring(self.dynamicSubIndex)
      else

        self.statusText = '|cFFFF4400'..PROFESSION_HEADERS[self.dynamicIndex]..'|r'
        self.actionName = '(#'..self.dynamicIndex..')'
      end
    elseif self.dynamicType == 'talent' then
      self.statusText = '|cFF00FFFF'.. TALENT .. '|r'
    end

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


    print('|cFF00BBFFUpdateSlot|r:', '['..slot..'] =', self.command, self.bindingText, self.dynamicType, self.isAvailable, self.actionID)

  end

  if not self.isAvailable then
    self.bind:SetTextColor(.7,.7,.7,1)
    self.ignoreTexture:SetShown(self.command and true)
    self.icon:SetVertexColor(.5,.5,.5)
  else
    self.ignoreTexture:SetShown(false)
    self.bind:SetTextColor(1,1,1,1)
    self.icon:SetVertexColor(1,1,1)
  end


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

    self:SetAlpha(1)
  end

  --self.alert:SetShown(self.command and not self.isBound)

  self.icon:SetTexture(displayTexture)

  self.border:SetColorTexture(unpack(borderType))
  self.header:SetText(self.statusText)
  self.bind:SetText(self.bindingText)
  self.details:SetText(displayName)
end

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

  -- keep right click from deleting an assignment during bind mode
  if kb.saveTarget == self then
    self:GetParent():DeactivateSlot(self)
    return
  end

  if kb.currentProfile.buttons[slot] then
    kb.currentProfile.buttons[slot] = nil
  end
  if self.command then
    kb.currentProfile.commands[self.command] = nil
  end
  local talentName = self.actionName
  if self.actionType == 'macro' then
    talentName = GetMacroSpell(self.actionID)
  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.dynamicType = nil
  self.bindingText = nil
  self.statusText = nil
  self.command = nil
  self.iconPath = nil
  self.actionType = nil
  self.actionID = nil
  self.actionName = nil
  self.pickupSlot = nil
  self.pickupBook = nil
  self.macroName = nil
  self.profile = nil
  self.border:SetColorTexture(unpack(BORDER_UNASSIGNED))
  self:EnableKeyboard(false)
  self:SetScript('OnKeyDown', nil)
  self.bindingText = nil
  self.icon:SetTexture(nil)
  self.ignoreTexture:Hide()

end

--- Assigns the slot via table copy; any manipulations from this point are temporary and
function skb:SetSlot(slotInfo)
  print('slot info', self:GetID())

  for k,v in pairs(slotInfo) do
    print('  -', k, v)
    self[k] = v
  end
  self.dynamicType = slotInfo.dynamicType
  local command, name, icon, actionType, actionID, macroName, macroText, pickupSlot, pickupBook
  = self.command, self.actionName, self.iconPath, self.actionType, self.actionID, self.macroName, self.macroText, self.spellbookSlot, self.spellbookType


  local slot = self:GetID()
  local isBound = false
  print('|cFFFFFF00SetSlot|r:', self:GetID())
  if self.command then

    isBound = kb.IsCommandBound(self, self.command)
    if actionType == 'spell' then
      local info = kb.ResolveSpellSlot(self)
      name, icon, actionType, actionID, macroName, macroText, pickupSlot, pickupBook = self.actionName, self.iconPath, self.actionType, self.actionID, self.macroName, self.macroText, self.spellbookSlot, self.spellbookType
      self.isAvailable = info and info.isAvailable
    elseif actionType == 'petaction' then
      self.dynamicType = 'petaction'
      local specialType, specialNum = command:match(actionType..'_([%a%s]+)_(%d)')

      if kb.PetCache.subtext[specialType] then

        local info = kb.PetCache.subtext[specialType][tonumber(specialNum)]
        if info then
          print('***dynamic pet skill', specialType, specialNum)
          --[[ i, spellName, subText, spellID,  texture, specialNum[subText ]]

          for k,v in pairs(info) do
            self[k] = v
          end
        end

      end
      self.statusText = 'Pet Action'
      self.isAvailable = (kb.PetCache.spellslot[name])
    elseif actionType == 'macro' then
      if actionID then

        -- Update stored information if it mis-matches
        local nameByID, _, bodyByID = GetMacroInfo(actionID)
        --print(bodyByID, "\n", macroText)
        if (nameByID ~= name) or (bodyByID ~= macroText) then
          --kb:print('mismatches for slot', self:GetID(), actionID, ((nameByID ~= name) and 'name' or ''), ((bodyByID ~= macroText) and 'body' or ''))
          local matchID, matchName, matchBody, hasMultiple
          local roughResult = ""

          local newID = GetMacroIndexByName(name)
          local firstName, _, firstBody = GetMacroInfo(newID)
          if (firstName ~= name) or (firstBody ~= macroText) then


            -- go even deeper
            local numAccount, numCharacter = GetNumMacros()
            local searchID = 1
            while searchID <= (120+numCharacter) do
              --kb:print(searchID)


              local searchName, _ , searchBody = GetMacroInfo(searchID)
              if (searchName == name) and (searchBody == macroText) then
                --kb:print('definitely', matchID, searchName, '\n', searchBody)
                -- complete match
                matchID = searchID
                matchName = searchName
                matchBody = searchBody
                break
              elseif (searchName == name) or (searchBody == macroText) then
                -- partial match, continue the search

                if matchID then
                  hasMultiple = true
                  roughResult = roughResult .. "\n" .. tostring(searchID) .. ':'..tostring(searchName)
                end

                matchID = searchID
                matchName = searchName
                matchBody = searchBody
                --kb:print('possibly', matchID, matchName, '\n', matchBody)
              end

              if searchID == numAccount then
                searchID = 120
              end
              searchID = searchID + 1
            end
          else
            matchID = newID
            matchName = firstName
            matchBody = firstBody
          end

          --kb:print(matchID, hasMultiple)
          if hasMultiple then
            kb:print('Macro assignment in slot #'..tostring(self:GetID())..' has multiple possible indexes:\nSaved Info: '..tostring(actionID)..'/'..tostring(name)..'\n|cFFFFFF00', roughResult)
          elseif matchID then
            kb:print('Macro for slot #'..tostring(self:GetID())..' ('..tostring(name)..') has probably changed:', ((actionID ~= newID) and (' |cFFFF4400'..tostring(actionID)..'|r to |cFF00FF88' .. newID .. '|r.') or ''), 'We\'re not sure, so you may want to re-do that assignment.')
            actionID = matchID
            name = matchName
            macroName = matchName
            macroText = matchBody
          end
        end
      else
        actionID = GetMacroIndexByName(name)
      end
      self.statusText = 'Macro ' .. tostring(actionID)
      self.isAvailable = true
    else
      if not actionID then
        actionID = command:match("^KeyBinderMacro:(.+)")
      end
      self.isAvailable = true
    end

    if self.isAvailable then
      --[[
      local checkCommand = command
      checkCommand = kb.LoadBinding(self)
      if checkCommand and (checkCommand ~= command) then
        print('|cFFFF4400fixing command string', actionType, actionID, name)
        kb.currentProfile.bound[command] = nil
        kb.currentProfile.bound[checkCommand] = slot
        for k,v in pairs(kb.currentProfile.bindings) do
          if v == command then
            kb.currentProfile.bindings[k] = checkCommand
          end
        end
      end
      --]]
    end


    actionID = actionID or 0
    self:EnableKeyboard(true)

    -- this is done to keep legacy key-values from breaking algorithm assumptions
    print('assigned', table.concat(slotInfo.assignedKeys,', '))

    if #slotInfo.assignedKeys == 0 then
      print('updating assigned table to:', GetBindingKey(command))
      slotInfo.assignedKeys = {GetBindingKey(command) }
    end

    self.assignedKeys = slotInfo.assignedKeys

    local slotInfo = {
      command = command,
      actionName = name,
      iconPath = icon,
      actionID = actionID,
      actionType = actionType,
      macroName = macroName,
      macroText = macroText,
      spellbookSlot = pickupSlot,
      spellbookType = pickupBook,
      assignedKeys = slotInfo.assignedKeys,
      dynamicType = self.dynamicType,
      dynamicID = self.dynamicID,
      dynamicIndex = self.dynamicIndex,
      dynamicSubIndex = self.dynamicSubIndex
    }
    kb.currentProfile.buttons[slot] = slotInfo

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

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

    kb.currentProfile.commands[command] = slot
  end

  --self.assignedKeys = slotInfo.assignedKeys
  self.isBound = isBound
  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.iconPath = icon
  self.profile = kb.db.bindMode
  self:RegisterForDrag('LeftButton')

  return slotInfo
end

kb.GetCommandAction = function(command)
  for i, data in ipairs(kb.loadedProfiles) do
    if data.commands[command] then
      if data.buttons[data.commands[command]] then
        local _, _, _, actionType, actionID = unpack(data.buttons[data.commands[command]])
        return actionType, actionID
      end
    end
  end
end