| Nenue@16 | 1 -- SkeletonKey | 
| Nenue@27 | 2 -- ActionTemplates.lua | 
| Nenue@16 | 3 -- Created: 7/29/2016 9:14 PM | 
| Nenue@16 | 4 -- %file-revision% | 
| Nenue@27 | 5 -- Code dealing with the implementation of action hotkeys | 
| Nenue@27 | 6 | 
| Nenue@21 | 7 local tostring, tonumber, pairs, ipairs = tostring, tonumber, pairs, ipairs | 
| Nenue@21 | 8 local unpack, SetBinding = unpack, SetBinding | 
| Nenue@21 | 9 local tinsert, tContains, select, wipe =  tinsert, tContains, select, table.wipe | 
| Nenue@21 | 10 local GetSpellBookItemInfo, GetSpellBookItemName, GetSpellInfo = GetSpellBookItemInfo, GetSpellBookItemName, GetSpellInfo | 
| Nenue@21 | 11 local GetSpecialization, GetSpecializationInfo, IsPassiveSpell, IsTalentSpell = GetSpecialization, GetSpecializationInfo, IsPassiveSpell, IsTalentSpell | 
| Nenue@21 | 12 local PetHasSpellbook, PetHasActionBar, GetPetActionInfo, HasPetSpells = PetHasSpellbook, PetHasActionBar, GetPetActionInfo, HasPetSpells | 
| Nenue@21 | 13 local GetProfessions, GetProfessionInfo, GetTalentInfo = GetProfessions, GetProfessionInfo, GetTalentInfo | 
| Nenue@21 | 14 local GetNumBindings, GetBinding = GetNumBindings, GetBinding | 
| Nenue@21 | 15 | 
| Nenue@21 | 16 local kb, print, wrap = LibStub('LibKraken').register(KeyBinder, 'Info') | 
| Nenue@34 | 17 local cprint = (DEVIAN_PNAME == 'SkeletonKey') and function(...) _G.print('Cfg', ...) end or function() end | 
| Nenue@16 | 18 | 
| Nenue@17 | 19 local CLICK_KEYBINDER_MACRO = "CLICK KeyBinderMacro:" | 
| Nenue@17 | 20 local CLICK_KEYBINDER_KEY = "CLICK KeyBinderKey:" | 
| Nenue@21 | 21 local PET_BASIC_SUBTEXT = 'Basic Attack' | 
| Nenue@21 | 22 local PET_SPECIAL_SUBTEXT = 'Special Ability' | 
| Nenue@16 | 23 local PETACTION_SCRIPT = { | 
| Nenue@22 | 24   [PET_ACTION_MOVE_TO] = {'pet_move_to', SLASH_PET_MOVE_TO1}, | 
| Nenue@22 | 25   [PET_ACTION_ATTACK] = {'pet_attack', SLASH_PET_ATTACK1}, | 
| Nenue@22 | 26   [PET_ACTION_FOLLOW] = {'pet_follow', SLASH_PET_FOLLOW1}, | 
| Nenue@22 | 27   [PET_ACTION_WAIT] = {'pet_stay', SLASH_PET_STAY1 }, | 
| Nenue@22 | 28   [PET_MODE_AGGRESSIVE] = {'pet_aggressive', SLASH_PET_AGGRESSIVE1 }, | 
| Nenue@22 | 29   [PET_MODE_DEFENSIVE] = { 'pet_defensive', SLASH_PET_DEFENSIVE1}, | 
| Nenue@22 | 30   [PET_MODE_PASSIVE] = { 'pet_passive', SLASH_PET_PASSIVE1}, | 
| Nenue@22 | 31   [PET_MODE_ASSIST] = {'pet_assist', SLASH_PET_ASSIST1}, | 
| Nenue@16 | 32 } | 
| Nenue@21 | 33 local SECONDARY_PROFESSIONS = { | 
| Nenue@21 | 34   [5] = 3, | 
| Nenue@21 | 35   [7] = 4, | 
| Nenue@21 | 36   [9] = 5, | 
| Nenue@21 | 37   [10] = 6 | 
| Nenue@21 | 38 } | 
| Nenue@54 | 39 local petSpellCache,petSubtextCache | 
| Nenue@21 | 40 local SUMMON_RANDOM_FAVORITE_MOUNT_SPELL = 150544 | 
| Nenue@16 | 41 | 
| Nenue@27 | 42 --kb.ChangedBindings = {} | 
| Nenue@27 | 43 --kb.ActionTypes = {} | 
| Nenue@19 | 44 | 
| Nenue@19 | 45 local atype = kb.ActionTypes | 
| Nenue@19 | 46 | 
| Nenue@17 | 47 --- Caps Lock | 
| Nenue@19 | 48 atype['mount'] = function(id, name) | 
| Nenue@17 | 49   if id == SUMMON_RANDOM_FAVORITE_MOUNT_SPELL then | 
| Nenue@24 | 50     return CLICK_KEYBINDER_MACRO, 'mount_random', "/script C_MountJournal.SummonByID(0)" | 
| Nenue@17 | 51   else | 
| Nenue@24 | 52     return CLICK_KEYBINDER_MACRO, 'mount_'..id, "/script C_MountJournal.SummonByID("..id..")" | 
| Nenue@17 | 53   end | 
| Nenue@16 | 54 end | 
| Nenue@19 | 55 | 
| Nenue@19 | 56 atype['macro'] = function(id, name) | 
| Nenue@17 | 57   return CLICK_KEYBINDER_MACRO, 'macro_' .. tostring(name), id | 
| Nenue@17 | 58 end | 
| Nenue@19 | 59 | 
| Nenue@19 | 60 atype['equipset'] = function(id, name) | 
| Nenue@17 | 61     return CLICK_KEYBINDER_MACRO, 'equipset_'..tostring(name), "/script UseEquipmentSet("..tostring(id)..")" | 
| Nenue@17 | 62 end | 
| Nenue@19 | 63 | 
| Nenue@19 | 64 atype['spell'] = function(id, name) | 
| Nenue@17 | 65   local attributeName = name | 
| Nenue@17 | 66   if kb.ProfessionCache[id] then | 
| Nenue@17 | 67     attributeName = "profession_".. kb.ProfessionCache[id].profOffset .. '_' .. kb.ProfessionCache[id].spellNum | 
| Nenue@17 | 68   end | 
| Nenue@17 | 69   return CLICK_KEYBINDER_KEY, attributeName, name | 
| Nenue@17 | 70 end | 
| Nenue@19 | 71 | 
| Nenue@19 | 72 atype['petaction'] = function(_, name) | 
| Nenue@17 | 73   -- ID doesn't exist for basic commands, even though they can be picked up | 
| Nenue@17 | 74   local attributeName, attributeValue = "petaction_" .. tostring(name), "/cast "..tostring(name) | 
| Nenue@54 | 75 | 
| Nenue@54 | 76   if not petSpellCache then | 
| Nenue@54 | 77     kb.UpdatePetInfo() | 
| Nenue@54 | 78   end | 
| Nenue@54 | 79   -- Compose a multi-macro for subtext abilities | 
| Nenue@54 | 80   if petSpellCache[name] then | 
| Nenue@54 | 81     attributeValue = "" | 
| Nenue@54 | 82     for spellName, enabled in pairs(petSubtextCache[petSpellCache[name]]) do | 
| Nenue@54 | 83       attributeValue = attributeValue .. "/cast " .. spellName .. "\n" | 
| Nenue@54 | 84     end | 
| Nenue@54 | 85   end | 
| Nenue@54 | 86 | 
| Nenue@17 | 87   if PETACTION_SCRIPT[name] then | 
| Nenue@22 | 88     attributeName, attributeValue = unpack(PETACTION_SCRIPT[name]) | 
| Nenue@19 | 89   elseif kb.PetCache.special[name] then | 
| Nenue@21 | 90     attributeName = "petaction_"..kb.PetCache.special[name][3].."_" .. tonumber(kb.PetCache.special[name][6]) | 
| Nenue@17 | 91   end | 
| Nenue@17 | 92   return CLICK_KEYBINDER_MACRO, attributeName, attributeValue | 
| Nenue@16 | 93 end | 
| Nenue@16 | 94 | 
| Nenue@19 | 95 atype['battlepet'] = function(id, name) | 
| Nenue@17 | 96   return CLICK_KEYBINDER_MACRO, 'battlepet_' .. tostring(name), SLASH_SUMMON_BATTLE_PET1 .. " " .. tostring(name) | 
| Nenue@17 | 97 end | 
| Nenue@19 | 98 | 
| Nenue@19 | 99 atype['item'] = function(id, name) | 
| Nenue@17 | 100   return CLICK_KEYBINDER_KEY, 'item_' .. tostring(name), id | 
| Nenue@17 | 101 end | 
| Nenue@16 | 102 | 
| Nenue@16 | 103 | 
| Nenue@17 | 104 --- Resolves the SecureActionButton attribute names used for the given action | 
| Nenue@16 | 105 kb.RegisterAction = function(actionType, id, name) | 
| Nenue@19 | 106   assert(atype[actionType], 'Missing actionType handler for `'..tostring(actionType)..'`') | 
| Nenue@19 | 107   local target, attributeName, attributeValue  = atype[actionType](id, name) | 
| Nenue@17 | 108   local command = target .. attributeName | 
| Nenue@17 | 109   kb.macros[attributeName] = {attributeValue, command} | 
| Nenue@17 | 110   return attributeName, attributeValue, command | 
| Nenue@17 | 111 end | 
| Nenue@17 | 112 | 
| Nenue@17 | 113 | 
| Nenue@17 | 114 | 
| Nenue@17 | 115 | 
| Nenue@17 | 116 kb.ApplyTalentBinding = function(talentInfo, cache) | 
| Nenue@17 | 117   for i = 5, #talentInfo do | 
| Nenue@17 | 118     local command = CLICK_KEYBINDER_KEY.. talentInfo[2] | 
| Nenue@17 | 119     SetBinding(talentInfo[i], command) | 
| Nenue@62 | 120     --cprint(' **', talentInfo[i], '->', command) | 
| Nenue@17 | 121     tinsert(cache, talentInfo[i]) | 
| Nenue@17 | 122   end | 
| Nenue@17 | 123 end | 
| Nenue@17 | 124 kb.CacheTalentBinding = function(talentInfo, cache) | 
| Nenue@17 | 125 | 
| Nenue@17 | 126   local spellID = talentInfo[4] | 
| Nenue@19 | 127   cache[spellID] = cache[spellID] or {} | 
| Nenue@19 | 128   cache[spellID] = {select(5,unpack(talentInfo)) } | 
| Nenue@19 | 129   --cprint(spellID, unpack(kb.TalentBindings[spellID])) | 
| Nenue@17 | 130 end | 
| Nenue@17 | 131 | 
| Nenue@27 | 132 | 
| Nenue@17 | 133 do | 
| Nenue@63 | 134   local commandActions = {} | 
| Nenue@17 | 135   local bindings = kb.bindings | 
| Nenue@17 | 136   local key, macro = KeyBinderKey, KeyBinderMacro | 
| Nenue@17 | 137   kb.LoadBinding = function(command, name, icon, actionType, actionID, macroName, macroText ) | 
| Nenue@17 | 138 | 
| Nenue@63 | 139     local indexKey = actionType..'_'..actionID | 
| Nenue@62 | 140     local buttonTypeKey, buttonTypeValue = "*type-"..name, actionType | 
| Nenue@62 | 141     local buttonActionKey, buttonActionValue = "*"..actionType.."-"..name, actionID | 
| Nenue@62 | 142     local button = key | 
| Nenue@17 | 143     if actionType == 'spell' then | 
| Nenue@62 | 144       buttonTypeKey = "*type-"..name | 
| Nenue@62 | 145       buttonTypeValue = actionType | 
| Nenue@62 | 146       buttonActionKey = "*"..actionType.."-"..name | 
| Nenue@62 | 147       buttonActionValue = name | 
| Nenue@17 | 148     elseif actionType == 'item' then | 
| Nenue@62 | 149       buttonTypeKey = "*type-"..name | 
| Nenue@62 | 150       buttonTypeValue = actionType | 
| Nenue@62 | 151       buttonActionKey = "*"..actionType.."-"..name | 
| Nenue@62 | 152       buttonActionValue = actionID | 
| Nenue@17 | 153     elseif actionType == 'macro' then | 
| Nenue@62 | 154       button = macro | 
| Nenue@62 | 155       buttonTypeKey = "*macro-"..macroName | 
| Nenue@62 | 156       buttonTypeValue = actionID | 
| Nenue@62 | 157       buttonActionKey = nil | 
| Nenue@16 | 158     else | 
| Nenue@62 | 159       button = macro | 
| Nenue@62 | 160       buttonTypeKey = "*macrotext-"..macroName | 
| Nenue@62 | 161       buttonTypeValue = macroText | 
| Nenue@62 | 162       buttonActionKey = nil | 
| Nenue@16 | 163     end | 
| Nenue@62 | 164 | 
| Nenue@62 | 165     --cprint(actionType, actionID, name) | 
| Nenue@62 | 166     kb.SecureAttribute(button, buttonTypeKey, buttonTypeValue) | 
| Nenue@62 | 167     if buttonActionKey then | 
| Nenue@62 | 168       --cprint(button:GetName(), buttonActionKey,'=', buttonActionValue) | 
| Nenue@62 | 169       kb.SecureAttribute(button, buttonActionKey, buttonActionValue) | 
| Nenue@62 | 170     end | 
| Nenue@62 | 171 | 
| Nenue@63 | 172     kb.bindings[indexKey] = kb.bindings[indexKey] or {} | 
| Nenue@63 | 173     kb.bindings[command] = kb.bindings[indexKey] | 
| Nenue@63 | 174     commandActions[command] = indexKey | 
| Nenue@50 | 175     return bindings[indexKey], actionID | 
| Nenue@16 | 176   end | 
| Nenue@16 | 177 | 
| Nenue@17 | 178   kb.ApplyBindings = function (profile) | 
| Nenue@62 | 179     --cprint('binding profile', profile) | 
| Nenue@63 | 180 | 
| Nenue@17 | 181     for slot, data in pairs(profile.buttons) do | 
| Nenue@63 | 182       local bindsTable, actionID = kb.LoadBinding(unpack(data)) | 
| Nenue@63 | 183       local command = data[1] | 
| Nenue@17 | 184     end | 
| Nenue@17 | 185 | 
| Nenue@17 | 186     for key, command in pairs(profile.bindings) do | 
| Nenue@63 | 187       cprint('|cFF00FFFF'.. key .. '|r to|cFF00FF00', command, commandActions[command]) | 
| Nenue@17 | 188       SetBinding(key, command) | 
| Nenue@64 | 189       if kb.bindings[command] and not tContains(kb.bindings[command], key) then | 
| Nenue@63 | 190         tinsert(kb.bindings[command], key) | 
| Nenue@17 | 191       end | 
| Nenue@17 | 192     end | 
| Nenue@17 | 193 | 
| Nenue@17 | 194     for spellName, talentInfo in pairs(profile.talents) do | 
| Nenue@17 | 195       local dummy = GetSpellInfo(spellName) | 
| Nenue@17 | 196       local func = kb.CacheTalentBinding | 
| Nenue@19 | 197       local dest = kb.TalentBindings | 
| Nenue@17 | 198       if dummy then | 
| Nenue@62 | 199         --cprint('|cFFBBFF00Active:|r', dummy) | 
| Nenue@17 | 200         local macroName, spellName, actionType, actionID = unpack(talentInfo) | 
| Nenue@50 | 201         local indexKey = actionType .. '_' .. actionID | 
| Nenue@63 | 202         kb.bindings[indexKey] = {} | 
| Nenue@17 | 203         func = kb.ApplyTalentBinding | 
| Nenue@50 | 204         dest = kb.bindings[indexKey] | 
| Nenue@17 | 205       else | 
| Nenue@17 | 206 | 
| Nenue@62 | 207         --cprint('|cFFFF4400Inactive:|r', talentInfo[2]) | 
| Nenue@17 | 208       end | 
| Nenue@17 | 209       func(talentInfo, dest) | 
| Nenue@17 | 210     end | 
| Nenue@17 | 211 | 
| Nenue@17 | 212   end | 
| Nenue@17 | 213 | 
| Nenue@17 | 214   kb.ApplyAllBindings =function () | 
| Nenue@21 | 215     wipe(kb.TalentBindings) | 
| Nenue@62 | 216     wipe(kb.bindings) | 
| Nenue@63 | 217     --kb:print('Loading binding profile', kb.profileName) | 
| Nenue@17 | 218 | 
| Nenue@17 | 219     -- reflect action key settings | 
| Nenue@17 | 220     if GetCVarBool("ActionButtonUseKeyDown") then | 
| Nenue@17 | 221       KeyBinderMacro:RegisterForClicks("AnyDown") | 
| Nenue@17 | 222       KeyBinderKey:RegisterForClicks("AnyDown") | 
| Nenue@17 | 223     else | 
| Nenue@17 | 224       KeyBinderMacro:RegisterForClicks("AnyUp") | 
| Nenue@17 | 225       KeyBinderKey:RegisterForClicks("AnyUp") | 
| Nenue@17 | 226     end | 
| Nenue@17 | 227 | 
| Nenue@17 | 228     for i, profile in ipairs(kb.orderedProfiles) do | 
| Nenue@17 | 229       kb.ApplyBindings(profile) | 
| Nenue@17 | 230     end | 
| Nenue@17 | 231     -- do this after to ensure that profession binds are properly overridden | 
| Nenue@17 | 232     kb.UpdateProfessionInfo() | 
| Nenue@17 | 233 | 
| Nenue@17 | 234     SaveBindings(GetCurrentBindingSet()) | 
| Nenue@17 | 235   end | 
| Nenue@19 | 236 end | 
| Nenue@19 | 237 | 
| Nenue@19 | 238 | 
| Nenue@19 | 239 kb.specInfo = {} | 
| Nenue@19 | 240 kb.UpdateSpecInfo = function() | 
| Nenue@19 | 241   kb.specInfo.id = GetSpecialization() | 
| Nenue@19 | 242   kb.specInfo.globalID, kb.specInfo.name, kb.specInfo.desc, kb.specInfo.texture = GetSpecializationInfo(kb.specInfo.id) | 
| Nenue@19 | 243 end | 
| Nenue@19 | 244 | 
| Nenue@63 | 245 kb.UpdateMacroInfo =  function() | 
| Nenue@64 | 246   for index = 1, GetNumMacros() do | 
| Nenue@63 | 247     local name = GetMacroInfo(index) | 
| Nenue@63 | 248     kb.SecureAttribute(KeyBinderMacro, "*macro_"..tostring(name), i) | 
| Nenue@63 | 249   end | 
| Nenue@63 | 250 end | 
| Nenue@63 | 251 | 
| Nenue@19 | 252 kb.UpdateTalentInfo = function() | 
| Nenue@19 | 253   if kb.talentsPushed then | 
| Nenue@19 | 254     return | 
| Nenue@19 | 255   end | 
| Nenue@21 | 256   wipe(kb.TalentCache) | 
| Nenue@19 | 257   for row =1, MAX_TALENT_TIERS do | 
| Nenue@19 | 258     for col = 1, NUM_TALENT_COLUMNS do | 
| Nenue@19 | 259       local talentID, talentName, icon, selected, available, spellID = GetTalentInfo(row, col, 1) | 
| Nenue@19 | 260       local talentInfo = kb.TalentCache[spellID] or {} | 
| Nenue@19 | 261       talentInfo.row = 1 | 
| Nenue@19 | 262       talentInfo.col = col | 
| Nenue@19 | 263       talentInfo.name = talentName | 
| Nenue@19 | 264       talentInfo.talentID = talentID | 
| Nenue@19 | 265       talentInfo.selected = selected | 
| Nenue@19 | 266       talentInfo.available = available | 
| Nenue@19 | 267       talentInfo.spellID = spellID | 
| Nenue@19 | 268       kb.TalentCache[spellID] = talentInfo | 
| Nenue@19 | 269       kb.TalentCache[talentName] = talentInfo | 
| Nenue@62 | 270       --print('Talent ', row, col, spellID, talentName) | 
| Nenue@19 | 271     end | 
| Nenue@19 | 272   end | 
| Nenue@19 | 273   kb.talentsPushed = true | 
| Nenue@19 | 274 | 
| Nenue@19 | 275   kb.UpdateDynamicButtons('talent') | 
| Nenue@19 | 276 end | 
| Nenue@19 | 277 | 
| Nenue@19 | 278 kb.UpdateProfessionInfo = function() | 
| Nenue@21 | 279   wipe(kb.ProfessionCache) | 
| Nenue@19 | 280   local profs = {GetProfessions() } | 
| Nenue@62 | 281   --print(GetProfessions()) | 
| Nenue@19 | 282   local primaryNum = 0 | 
| Nenue@30 | 283   for i = 1, 6 do | 
| Nenue@30 | 284     if profs[i] then | 
| Nenue@30 | 285       local index = profs[i] | 
| Nenue@30 | 286       local profName, texture, _, _, numSpells, spellOffset = GetProfessionInfo(index) | 
| Nenue@62 | 287       --print(i, index, profName, numSpells, spellOffset) | 
| Nenue@30 | 288       if not SECONDARY_PROFESSIONS[index] then | 
| Nenue@30 | 289         primaryNum = primaryNum + 1 | 
| Nenue@30 | 290       end | 
| Nenue@30 | 291       local profNum = SECONDARY_PROFESSIONS[index] or primaryNum | 
| Nenue@62 | 292       --print(i, profNum) | 
| Nenue@19 | 293 | 
| Nenue@19 | 294 | 
| Nenue@30 | 295       kb.ProfessionCache[profNum] = kb.ProfessionCache[profNum] or {} | 
| Nenue@19 | 296 | 
| Nenue@30 | 297       for j = 1, numSpells do | 
| Nenue@30 | 298         local spellName, _, icon, _, _, _, spellID = GetSpellInfo(spellOffset+j, BOOKTYPE_PROFESSION) | 
| Nenue@19 | 299 | 
| Nenue@30 | 300         local profInfo = { | 
| Nenue@30 | 301           spellName = spellName, | 
| Nenue@30 | 302           spellID = spellID, | 
| Nenue@30 | 303           icon = icon, | 
| Nenue@30 | 304           profOffset = i, | 
| Nenue@30 | 305           profIndex = index, | 
| Nenue@30 | 306           spellOffset = (spellOffset+j), | 
| Nenue@30 | 307           spellNum = j | 
| Nenue@30 | 308         } | 
| Nenue@26 | 309 | 
| Nenue@30 | 310         kb.SecureAttribute(KeyBinderKey, "*type-profession_"..i .. '_' ..j, "spell") | 
| Nenue@30 | 311         kb.SecureAttribute(KeyBinderKey, "*spell-profession_"..i .. '_' ..j, spellName) | 
| Nenue@19 | 312 | 
| Nenue@30 | 313         kb.ProfessionCache[i .. '_' .. j] = profInfo | 
| Nenue@30 | 314         kb.ProfessionCache[spellName] = profInfo | 
| Nenue@30 | 315         kb.ProfessionCache[spellID] = profInfo | 
| Nenue@62 | 316         --print('  |cFF0088FF['..i..']|r|cFFFF44BB['..spellOffset+i..']|r', spellName, "profession_"..i .. '_' ..j) | 
| Nenue@30 | 317       end | 
| Nenue@19 | 318     end | 
| Nenue@19 | 319 | 
| Nenue@19 | 320   end | 
| Nenue@19 | 321 | 
| Nenue@19 | 322   kb.UpdateDynamicButtons('profession') | 
| Nenue@19 | 323 end | 
| Nenue@19 | 324 | 
| Nenue@19 | 325 | 
| Nenue@19 | 326 | 
| Nenue@19 | 327 kb.UpdatePetInfo = function() | 
| Nenue@19 | 328   local hasPetSpells, petType = HasPetSpells() | 
| Nenue@34 | 329 | 
| Nenue@56 | 330   -- reconcile saved data if it becomes available | 
| Nenue@56 | 331   if kb.db then | 
| Nenue@56 | 332     kb.db.petSpellsDB = kb.db.petSpellsDB or {} | 
| Nenue@56 | 333     kb.db.petSpellsDB.subtext = kb.db.petSpellsDB.subtext or {} | 
| Nenue@56 | 334     kb.db.petSpellsDB.spell = kb.db.petSpellsDB.spell or {} | 
| Nenue@56 | 335     local spellCache = kb.db.petSpellsDB.spell | 
| Nenue@56 | 336     local subtextCache = kb.db.petSpellsDB.subtext | 
| Nenue@56 | 337     if petSpellCache then | 
| Nenue@56 | 338       for k,v in pairs(petSpellCache) do | 
| Nenue@56 | 339         if not spellCache[k] then | 
| Nenue@56 | 340           spellCache[k] = v | 
| Nenue@56 | 341         end | 
| Nenue@56 | 342       end | 
| Nenue@56 | 343     end | 
| Nenue@57 | 344     petSpellCache = spellCache | 
| Nenue@56 | 345     if petSubtextCache then | 
| Nenue@56 | 346       for k,v in pairs(petSubtextCache) do | 
| Nenue@56 | 347         if not subtextCache[k] then | 
| Nenue@56 | 348           subtextCache[k] = v | 
| Nenue@56 | 349         end | 
| Nenue@56 | 350       end | 
| Nenue@56 | 351     end | 
| Nenue@57 | 352     petSubtextCache = subtextCache | 
| Nenue@56 | 353   else | 
| Nenue@56 | 354     petSpellCache = {} | 
| Nenue@56 | 355     petSubtextCache = {} | 
| Nenue@56 | 356   end | 
| Nenue@54 | 357 | 
| Nenue@19 | 358   if PetHasSpellbook() then | 
| Nenue@62 | 359     --print('PET SPELLBOOK') | 
| Nenue@19 | 360     local i = 1 | 
| Nenue@21 | 361     local specialNum = {} | 
| Nenue@19 | 362     repeat | 
| Nenue@19 | 363 | 
| Nenue@19 | 364       local spellType, spellID = GetSpellBookItemInfo(i, BOOKTYPE_PET) | 
| Nenue@19 | 365       local spellName, subText = GetSpellBookItemName(i, BOOKTYPE_PET) | 
| Nenue@21 | 366       local texture = GetSpellBookItemTexture(i, BOOKTYPE_PET) | 
| Nenue@19 | 367       local isPassive = IsPassiveSpell(i, BOOKTYPE_PET) | 
| Nenue@19 | 368       if not isPassive then | 
| Nenue@19 | 369         if spellName then | 
| Nenue@21 | 370           kb.PetCache.spellslot[spellName] = {i, spellName, subText, spellID, texture} | 
| Nenue@62 | 371           --print('|cFF00FF88spellslot['..spellName..']|r', '=>', i, subText) | 
| Nenue@19 | 372 | 
| Nenue@21 | 373           if subText then | 
| Nenue@34 | 374             -- make sure that pet specialization subtext maps correctly | 
| Nenue@34 | 375             --if match(subText, kb.PetCache.specName) then | 
| Nenue@34 | 376             --  subText = 'specialization' | 
| Nenue@34 | 377             --end | 
| Nenue@21 | 378             kb.PetCache.subtext[subText] = kb.PetCache.subtext[subText] or {} | 
| Nenue@21 | 379             specialNum[subText] = (specialNum[subText] or 0) + 1 | 
| Nenue@21 | 380 | 
| Nenue@54 | 381             petSpellCache[spellName] = subText | 
| Nenue@54 | 382             petSubtextCache[subText] = petSubtextCache[subText] or {} | 
| Nenue@54 | 383 | 
| Nenue@54 | 384             -- add to the list | 
| Nenue@54 | 385             if not petSubtextCache[subText][spellName] then | 
| Nenue@54 | 386               petSubtextCache[subText][spellName] = true | 
| Nenue@54 | 387 | 
| Nenue@54 | 388               local macrotext = "" | 
| Nenue@54 | 389               for spellName, enabled in pairs(petSubtextCache[subText]) do | 
| Nenue@54 | 390                 macrotext = macrotext .. "/cast " .. spellName .. "\n" | 
| Nenue@54 | 391               end | 
| Nenue@54 | 392               kb.SecureAttribute(KeyBinderMacro, "*macrotext-petaction_"..subText.."_"..specialNum[subText], macrotext) | 
| Nenue@62 | 393               --print('|cFF00FFFFspecial['..spellName..']|r', '\n','|cFF00FFFFsubtext['..subText..']['..specialNum[subText]..']|r', '=>', i, spellName, subText, spellID,  texture, specialNum[subText]) | 
| Nenue@54 | 394             end | 
| Nenue@54 | 395 | 
| Nenue@54 | 396 | 
| Nenue@54 | 397 | 
| Nenue@21 | 398             local entry = {i, spellName, subText, spellID,  texture, specialNum[subText]} | 
| Nenue@21 | 399 | 
| Nenue@21 | 400             kb.PetCache.special[spellName] = entry | 
| Nenue@21 | 401             kb.PetCache.subtext[subText][specialNum[subText]] = entry | 
| Nenue@19 | 402           end | 
| Nenue@19 | 403 | 
| Nenue@19 | 404           if spellID then | 
| Nenue@19 | 405             kb.PetCache.spell[i] = {spellID, spellName, subText} | 
| Nenue@62 | 406             --print('|cFF0088FFspell['..i..']|r', '=>', spellID, spellName, subText) | 
| Nenue@19 | 407           end | 
| Nenue@19 | 408         end | 
| Nenue@19 | 409 | 
| Nenue@19 | 410 | 
| Nenue@19 | 411       end | 
| Nenue@19 | 412 | 
| Nenue@19 | 413       i = i + 1 | 
| Nenue@19 | 414     until spellType == nil | 
| Nenue@19 | 415   else | 
| Nenue@62 | 416     --print('NO PET SPELLBOOK') | 
| Nenue@21 | 417     wipe(kb.PetCache.spell) | 
| Nenue@21 | 418     wipe(kb.PetCache.spellslot) | 
| Nenue@19 | 419   end | 
| Nenue@19 | 420 | 
| Nenue@19 | 421   if PetHasActionBar() then | 
| Nenue@62 | 422     --print('PET ACTION BAR') | 
| Nenue@19 | 423     for i = 1, 10 do | 
| Nenue@19 | 424 | 
| Nenue@19 | 425 | 
| Nenue@19 | 426       local name, subtext, texture, isToken, isActive = GetPetActionInfo(i) | 
| Nenue@19 | 427       if name then | 
| Nenue@19 | 428         kb.PetCache.action[i] = {name, subtext, texture, isToken, isActive } | 
| Nenue@19 | 429 | 
| Nenue@19 | 430 | 
| Nenue@19 | 431       end | 
| Nenue@62 | 432       --print('|cFFFFFF00action['..i..']|r', name, subtext, texture) | 
| Nenue@19 | 433     end | 
| Nenue@19 | 434   else | 
| Nenue@62 | 435     --print('NO PET ACTION BAR') | 
| Nenue@21 | 436     wipe(kb.PetCache.action) | 
| Nenue@19 | 437   end | 
| Nenue@19 | 438 | 
| Nenue@19 | 439   kb.UpdateDynamicButtons('petaction') | 
| Nenue@19 | 440 | 
| Nenue@19 | 441 end | 
| Nenue@19 | 442 | 
| Nenue@19 | 443 kb.UpdateSystemBinds = function() | 
| Nenue@21 | 444   wipe(kb.SystemBindings) | 
| Nenue@19 | 445   local n = GetNumBindings() | 
| Nenue@19 | 446   for i=1, n do | 
| Nenue@19 | 447     local command, key1, key2 = GetBinding(i) | 
| Nenue@34 | 448     if not command:match('ACTION.*%d+') then | 
| Nenue@34 | 449       if key1 then | 
| Nenue@34 | 450         kb.SystemBindings[key1] = command | 
| Nenue@34 | 451       end | 
| Nenue@34 | 452       if key2 then | 
| Nenue@34 | 453         kb.SystemBindings[key2] = command | 
| Nenue@34 | 454       end | 
| Nenue@34 | 455     else | 
| Nenue@62 | 456       --print('ignoring action button binding', command) | 
| Nenue@19 | 457     end | 
| Nenue@19 | 458   end | 
| Nenue@19 | 459 end | 
| Nenue@19 | 460 | 
| Nenue@19 | 461 kb.UpdateDynamicButtons = function(dynamicType) | 
| Nenue@19 | 462   for i, button in ipairs(kb.buttons) do | 
| Nenue@19 | 463     if button.isDynamic == dynamicType then | 
| Nenue@19 | 464       kb.UpdateSlot(button, true) | 
| Nenue@19 | 465     end | 
| Nenue@19 | 466   end | 
| Nenue@26 | 467 end | 
| Nenue@26 | 468 | 
| Nenue@26 | 469 kb.pendingAttributes = {} | 
| Nenue@26 | 470 kb.SecureAttribute = function(target, name, value) | 
| Nenue@26 | 471   if InCombatLockdown() then | 
| Nenue@34 | 472     if #kb.pendingAttributes == 0 then | 
| Nenue@49 | 473       kb:print(kb.L('Key bindings will be applied when you exit combat.')) | 
| Nenue@34 | 474     end | 
| Nenue@34 | 475 | 
| Nenue@26 | 476     tinsert(kb.pendingAttributes, {target, name, value}) | 
| Nenue@26 | 477     kb:RegisterEvent('PLAYER_REGEN_ENABLED') | 
| Nenue@34 | 478 | 
| Nenue@26 | 479   else | 
| Nenue@26 | 480 | 
| Nenue@63 | 481     cprint('|cFFFF4444' .. target:GetName()..'|r.|cFFFFFF00'.. tostring(name)..'|r = "'..tostring(value)..'"') | 
| Nenue@26 | 482     target:SetAttribute(name, value) | 
| Nenue@26 | 483   end | 
| Nenue@26 | 484 end | 
| Nenue@26 | 485 | 
| Nenue@26 | 486 kb.PLAYER_REGEN_ENABLED = function() | 
| Nenue@26 | 487   if #kb.pendingAttributes >= 1 then | 
| Nenue@26 | 488     local args = tremove(kb.pendingAttributes) | 
| Nenue@26 | 489     while args do | 
| Nenue@26 | 490       local target, name, value = unpack(args) | 
| Nenue@62 | 491       --print(target:GetName(), 'attribute', '"'.. tostring(name)..'" = "'..tostring(value)..'"') | 
| Nenue@63 | 492       cprint('deferred', target:GetName(), 'attribute', '"'.. tostring(name)..'" = "'..tostring(value)..'"') | 
| Nenue@26 | 493       target:SetAttribute(name, value) | 
| Nenue@26 | 494       args = tremove(kb.pendingAttributes) | 
| Nenue@26 | 495     end | 
| Nenue@26 | 496   end | 
| Nenue@27 | 497 | 
| Nenue@27 | 498   if #kb.pendingCalls >= 1 then | 
| Nenue@27 | 499 | 
| Nenue@27 | 500     local func = tremove(kb.pendingCalls) | 
| Nenue@27 | 501     while func do | 
| Nenue@27 | 502       func() | 
| Nenue@27 | 503     end | 
| Nenue@27 | 504   end | 
| Nenue@27 | 505 end | 
| Nenue@27 | 506 | 
| Nenue@27 | 507 kb.UpdateBindingsCache = function(actionType, actionID, bindings) | 
| Nenue@50 | 508   local indexKey = actionType .. '_' .. actionID | 
| Nenue@50 | 509   kb.bindings[indexKey] = bindings | 
| Nenue@27 | 510 | 
| Nenue@62 | 511   --print('|cFF00FF00'..indexKey..'|r = {', table.concat(bindings,', '), '}') | 
| Nenue@27 | 512   tinsert(kb.ChangedBindings, {actionType, actionID}) | 
| Nenue@16 | 513 end |