Mercurial > wow > askmrrobot
diff AskMrRobot.lua @ 17:e77e01abce98
Warlords of Draenor pre-patch
author | Adam tegen <adam.tegen@gmail.com> |
---|---|
date | Mon, 13 Oct 2014 21:28:32 -0500 |
parents | bb0c8ce689d1 |
children | a400b906acca |
line wrap: on
line diff
--- a/AskMrRobot.lua Thu Jul 10 15:32:11 2014 -0700 +++ b/AskMrRobot.lua Mon Oct 13 21:28:32 2014 -0500 @@ -1,21 +1,21 @@ local _, AskMrRobot = ... local L = AskMrRobot.L; -AskMrRobot.eventListener = CreateFrame("FRAME"); -- Need a frame to respond to events -AskMrRobot.eventListener:RegisterEvent("ADDON_LOADED"); -- Fired when saved variables are loaded -AskMrRobot.eventListener:RegisterEvent("ITEM_PUSH"); -AskMrRobot.eventListener:RegisterEvent("DELETE_ITEM_CONFIRM"); -AskMrRobot.eventListener:RegisterEvent("UNIT_INVENTORY_CHANGED"); -AskMrRobot.eventListener:RegisterEvent("BANKFRAME_OPENED"); -AskMrRobot.eventListener:RegisterEvent("BANKFRAME_CLOSED"); -AskMrRobot.eventListener:RegisterEvent("PLAYERBANKSLOTS_CHANGED"); -AskMrRobot.eventListener:RegisterEvent("CHARACTER_POINTS_CHANGED"); -AskMrRobot.eventListener:RegisterEvent("CONFIRM_TALENT_WIPE"); -AskMrRobot.eventListener:RegisterEvent("PLAYER_TALENT_UPDATE"); -AskMrRobot.eventListener:RegisterEvent("ACTIVE_TALENT_GROUP_CHANGED"); -AskMrRobot.eventListener:RegisterEvent("PLAYER_ENTERING_WORLD"); -AskMrRobot.eventListener:RegisterEvent("PLAYER_LOGOUT"); -- Fired when about to log out -AskMrRobot.eventListener:RegisterEvent("PLAYER_LEVEL_UP"); +AskMrRobot.eventListener = CreateFrame("FRAME") -- Need a frame to respond to events +AskMrRobot.eventListener:RegisterEvent("ADDON_LOADED") -- Fired when saved variables are loaded +AskMrRobot.eventListener:RegisterEvent("ITEM_PUSH") +AskMrRobot.eventListener:RegisterEvent("DELETE_ITEM_CONFIRM") +AskMrRobot.eventListener:RegisterEvent("UNIT_INVENTORY_CHANGED") +AskMrRobot.eventListener:RegisterEvent("BANKFRAME_OPENED") +--AskMrRobot.eventListener:RegisterEvent("BANKFRAME_CLOSED"); +AskMrRobot.eventListener:RegisterEvent("PLAYERBANKSLOTS_CHANGED") +AskMrRobot.eventListener:RegisterEvent("CHARACTER_POINTS_CHANGED") +AskMrRobot.eventListener:RegisterEvent("CONFIRM_TALENT_WIPE") +AskMrRobot.eventListener:RegisterEvent("PLAYER_TALENT_UPDATE") +AskMrRobot.eventListener:RegisterEvent("ACTIVE_TALENT_GROUP_CHANGED") +AskMrRobot.eventListener:RegisterEvent("PLAYER_ENTERING_WORLD") +--AskMrRobot.eventListener:RegisterEvent("PLAYER_LOGOUT") -- Fired when about to log out +--AskMrRobot.eventListener:RegisterEvent("PLAYER_LEVEL_UP") --AskMrRobot.eventListener:RegisterEvent("GET_ITEM_INFO_RECEIVED") AskMrRobot.eventListener:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED") AskMrRobot.eventListener:RegisterEvent("SOCKET_INFO_UPDATE") @@ -31,553 +31,252 @@ AskMrRobot.AddonName = ... AskMrRobot.ChatPrefix = "_AMR" -local amrLDB -local icon -local reforgequeue -local reforgeFrame = nil -local LoggingCombat = _G.LoggingCombat +-- the main user interface window +AskMrRobot.mainWindow = nil -AskMrRobot.itemDiffs = { - items = {}, -- slotNum -> nil (no change) or current <item id>, optimized <item id> - enchants = {}, -- slotNum -> nil (no change) or current <enchant id>, optimized <enchant id> - gems = {}, -- slotNum -> nil (no change) or ? - reforges = {} -- slotNum -> nil (no change) or current <reforge id>, optimized <reforge id> -} - -AskMrRobot.instanceIds = { - HeartOfFear = 1009, - MogushanVaults = 1008, - SiegeOfOrgrimmar = 1136, - TerraceOfEndlessSpring = 996, - ThroneOfThunder = 1098 -} - --- instances that we currently support logging for -AskMrRobot.supportedInstanceIds = { - [1136] = true -} - --- returns true if currently in a supported instance -function AskMrRobot.IsSupportedInstance() - - local zone, _, difficultyIndex, _, _, _, _, instanceMapID = GetInstanceInfo() - if AskMrRobot.supportedInstanceIds[tonumber(instanceMapID)] then - return true - else - return false - end -end - --- upgrade id -> upgrade level -local upgradeTable = { - [0] = 0, - [1] = 1, -- 1/1 -> 8 - [373] = 1, -- 1/2 -> 4 - [374] = 2, -- 2/2 -> 8 - [375] = 1, -- 1/3 -> 4 - [376] = 2, -- 2/3 -> 4 - [377] = 3, -- 3/3 -> 4 - [378] = 1, -- 1/1 -> 7 - [379] = 1, -- 1/2 -> 4 - [380] = 2, -- 2/2 -> 4 - [445] = 0, -- 0/2 -> 0 - [446] = 1, -- 1/2 -> 4 - [447] = 2, -- 2/2 -> 8 - [451] = 0, -- 0/1 -> 0 - [452] = 1, -- 1/1 -> 8 - [453] = 0, -- 0/2 -> 0 - [454] = 1, -- 1/2 -> 4 - [455] = 2, -- 2/2 -> 8 - [456] = 0, -- 0/1 -> 0 - [457] = 1, -- 1/1 -> 8 - [458] = 0, -- 0/4 -> 0 - [459] = 1, -- 1/4 -> 4 - [460] = 2, -- 2/4 -> 8 - [461] = 3, -- 3/4 -> 12 - [462] = 4, -- 4/4 -> 16 - [465] = 0, -- 0/2 -> 0 - [466] = 1, -- 1/2 -> 4 - [467] = 2, -- 2/2 -> 8 - [468] = 0, -- 0/4 -> 0 - [469] = 1, -- 1/4 -> 4 - [470] = 2, -- 2/4 -> 8 - [471] = 3, -- 3/4 -> 12 - [472] = 4, -- 4/4 -> 16 - [476] = 0, -- ? -> 0 - [479] = 0, -- ? -> 0 - [491] = 0, -- ? -> 0 - [492] = 1, -- ? -> 0 - [493] = 2, -- ? -> 0 - [494] = 0, - [495] = 1, - [496] = 2, - [497] = 3, - [498] = 4, - [504] = 3, - [505] = 4, - [506] = 5, - [507] = 6 -} - -local professionIds = { - ["None"] = 0, - ["Mining"] = 1, - ["Skinning"] = 2, - ["Herbalism"] = 3, - ["Enchanting"] = 4, - ["Jewelcrafting"] = 5, - ["Engineering"] = 6, - ["Blacksmithing"] = 7, - ["Leatherworking"] = 8, - ["Inscription"] = 9, - ["Tailoring"] = 10, - ["Alchemy"] = 11, - ["Fishing"] = 12, - ["Cooking"] = 13, - ["First Aid"] = 14, - ["Archaeology"] = 15 -} - -local raceIds = { - ["None"] = 0, - ["BloodElf"] = 1, - ["Draenei"] = 2, - ["Dwarf"] = 3, - ["Gnome"] = 4, - ["Human"] = 5, - ["NightElf"] = 6, - ["Orc"] = 7, - ["Tauren"] = 8, - ["Troll"] = 9, - ["Scourge"] = 10, - ["Undead"] = 10, - ["Goblin"] = 11, - ["Worgen"] = 12, - ["Pandaren"] = 13 -} - -local factionIds = { - ["None"] = 0, - ["Alliance"] = 1, - ["Horde"] = 2 -} - -local function OnExport() - if (AmrOptions.exportToClient) then - AskMrRobot.SaveAll() - ReloadUI() - else - AskMrRobot_ReforgeFrame:Show() - AskMrRobot_ReforgeFrame:ShowTab("export") - end -end +local _amrLDB +local _minimapIcon function AskMrRobot.eventListener:OnEvent(event, ...) if event == "ADDON_LOADED" then local addon = select(1, ...) if (addon == "AskMrRobot") then - print(L.AMR_ON_EVENT_LOADED.format(GetAddOnMetadata(AskMrRobot.AddonName, "Version"))) + --print(L.AMR_ON_EVENT_LOADED:format(GetAddOnMetadata(AskMrRobot.AddonName, "Version"))) + + AskMrRobot.InitializeSettings() + AskMrRobot.InitializeMinimap() -- listen for messages from other AMR addons - RegisterAddonMessagePrefix(AskMrRobot.ChatPrefix) - - AmrRealmName = GetRealmName() - AmrCharacterName = UnitName("player") + RegisterAddonMessagePrefix(AskMrRobot.ChatPrefix) - AskMrRobot.CombatLogTab.InitializeVariable() - - if not AmrIconInfo then AmrIconInfo = {} end - if not AmrBankItems then AmrBankItems = {} end - if not AmrCurrencies then AmrCurrencies = {} end - if not AmrSpecializations then AmrSpecializations = {} end - if not AmrOptions then AmrOptions = {} end - if not AmrGlyphs then AmrGlyphs = {} end - if not AmrTalents then AmrTalents = {} end - if not AmrBankItemsAndCounts then AmrBankItemsAndCounts = {} end - if not AmrImportString then AmrImportString = "" end - if not AmrImportDate then AmrImportDate = "" end - - if not AmrSettings then AmrSettings = {} end - if not AmrSettings.Logins then AmrSettings.Logins = {} end - - if not AmrSendSettings then - AmrSendSettings = { - SendGems = true, - SendEnchants = true, - SendEnchantMaterials = true, - SendToType = "a friend", - SendTo = "" - } - end - - amrLDB = LibStub("LibDataBroker-1.1"):NewDataObject("AskMrRobot", { - type = "launcher", - text = "Ask Mr. Robot", - icon = "Interface\\AddOns\\AskMrRobot\\Media\\icon", - OnClick = function() - if IsControlKeyDown() then - AskMrRobot_ReforgeFrame.combatLogTab:LogWipe() - elseif IsModifiedClick("CHATLINK") then - OnExport() - else - AskMrRobot_ReforgeFrame:Toggle() - end - end, - OnTooltipShow = function(tt) - tt:AddLine("Ask Mr. Robot", 1, 1, 1); - tt:AddLine(" "); - tt:AddLine(L.AMR_ON_EVENT_TOOLTIP) - end - }); - - - AskMrRobot.AmrUpdateMinimap() - - AskMrRobot_ReforgeFrame = AskMrRobot.AmrUI:new() - - -- remember the import settings between sessions - AskMrRobot_ReforgeFrame.summaryTab.importDate = AmrImportDate or "" - AskMrRobot_ReforgeFrame.buttons[2]:Click() - - -- the previous import string is loaded when the UI is first shown, otherwise the game spams events and it lags + AskMrRobot.mainWindow = AskMrRobot.AmrUI:new() end + + elseif event == "UNIT_INVENTORY_CHANGED" then + AskMrRobot.ScanEquipped() - elseif event == "ITEM_PUSH" or event == "DELETE_ITEM_CONFIRM" or event == "UNIT_INVENTORY_CHANGED" or event == "SOCKET_INFO_CLOSE" or event == "PLAYER_SPECIALIZATION_CHANGED" or event == "BAG_UPDATE" then - if AskMrRobot_ReforgeFrame then - AskMrRobot_ReforgeFrame:OnUpdate() - end + elseif event == "ITEM_PUSH" or event == "DELETE_ITEM_CONFIRM" or event == "SOCKET_INFO_CLOSE" or event == "PLAYER_SPECIALIZATION_CHANGED" or event == "BAG_UPDATE" then + --if AskMrRobot_ReforgeFrame then + -- AskMrRobot_ReforgeFrame:OnUpdate() + --end --AskMrRobot.SaveBags(); --AskMrRobot.SaveEquiped(); --AskMrRonot.GetCurrencies(); --AskMrRobot.GetGold(); + elseif event == "BANKFRAME_OPENED" or event == "PLAYERBANKSLOTS_CHANGED" then - --print("Scanning Bank: " .. event); AskMrRobot.ScanBank(); - elseif event == "BANKFRAME_CLOSED" then - --print("Stop Scanning Bank"); - --inBank = false; + elseif event == "CHARACTER_POINTS_CHANGED" or event == "CONFIRM_TALENT_WIPE" or event == "PLAYER_TALENT_UPDATE" or event == "ACTIVE_TALENT_GROUP_CHANGED" then --AskMrRobot.GetAmrSpecializations(); - if AskMrRobot_ReforgeFrame then - AskMrRobot_ReforgeFrame:OnUpdate() - end - elseif event == "PLAYER_LEVEL_UP" then - --GetLevel(); + --if AskMrRobot_ReforgeFrame then + -- AskMrRobot_ReforgeFrame:OnUpdate() + --end + elseif event == "ITEM_UNLOCKED" then - AskMrRobot.On_ITEM_UNLOCKED() - elseif event == "PLAYER_LOGOUT" then - -- doing nothing right now, but leaving this in case we need something here - elseif event == "PLAYER_ENTERING_WORLD" then + --AskMrRobot.On_ITEM_UNLOCKED() - -- delete entries that are more than 10 days old to prevent the table from growing indefinitely - if AmrSettings.Logins and #AmrSettings.Logins > 0 then - local now = time() - local oldDuration = 60 * 60 * 24 * 10 - local entryTime - repeat - -- parse entry and get time - local parts = {} - for part in string.gmatch(AmrSettings.Logins[1], "([^;]+)") do - tinsert(parts, part) - end - entryTime = tonumber(parts[3]) - - -- entries are in order, remove first entry if it is old - if difftime(now, entryTime) > oldDuration then - tremove(AmrSettings.Logins, 1) - end - until #AmrSettings.Logins == 0 or difftime(now, entryTime) <= oldDuration - end - - -- record the time a player logs in, used to figure out which player logged which parts of their log file - local key = AmrRealmName .. ";" .. AmrCharacterName .. ";" - local loginData = key .. time() - if AmrSettings.Logins and #AmrSettings.Logins > 0 then - local last = AmrSettings.Logins[#AmrSettings.Logins] - if string.len(last) >= string.len(key) and string.sub(last, 1, string.len(key)) ~= key then - table.insert(AmrSettings.Logins, loginData) - end - else - table.insert(AmrSettings.Logins, loginData) - end + elseif event == "PLAYER_ENTERING_WORLD" then + AskMrRobot.RecordLogin() elseif event == "PLAYER_REGEN_DISABLED" then - -- send data about this character when a player enters combat in a supported zone if AskMrRobot.IsSupportedInstance() then local t = time() AskMrRobot.SaveAll() AskMrRobot.ExportToAddonChat(t) - AskMrRobot.ExportLoggingData(t) + AskMrRobot.CombatLogTab.SaveExtras(t) end elseif event == "CHAT_MSG_ADDON" then local chatPrefix, message = select(1, ...) - local isLogging = AskMrRobot_ReforgeFrame.combatLogTab:IsLogging() + local isLogging = AskMrRobot.CombatLogTab.IsLogging() if (isLogging and chatPrefix == AskMrRobot.ChatPrefix) then - AskMrRobot_ReforgeFrame.combatLogTab:ReadAddonMessage(message) + AskMrRobot.mainWindow.combatLogTab:ReadAddonMessage(message) end + elseif event == "UPDATE_INSTANCE_INFO" or event == "PLAYER_DIFFICULTY_CHANGED" then - AskMrRobot_ReforgeFrame.combatLogTab:UpdateAutoLogging() + AskMrRobot.mainWindow.combatLogTab:UpdateAutoLogging() + end end -AskMrRobot.eventListener:SetScript("OnEvent", AskMrRobot.eventListener.OnEvent); +AskMrRobot.eventListener:SetScript("OnEvent", AskMrRobot.eventListener.OnEvent) -local function parseItemLink(input) - local itemId, enchantId, gemEnchantId1, gemEnchantId2, gemEnchantId3, gemEnchantId4, suffixId, _, _, reforgeId, upgradeId = string.match(input, "^|.-|Hitem:(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(-?%d+):(-?%d+):(-?%d+):(%d+):(%d+)|"); - local item = {} - item.itemId = tonumber(itemId) - item.suffixId = tonumber(suffixId) - item.enchantId = tonumber(enchantId) - item.reforgeId = tonumber(reforgeId) - item.upgradeId = tonumber(upgradeId) - item.gemEnchantIds = { tonumber(gemEnchantId1), tonumber(gemEnchantId2), tonumber(gemEnchantId3), tonumber(gemEnchantId4) } - return item -end SLASH_AMR1 = "/amr"; function SlashCmdList.AMR(msg) if msg == 'toggle' then - AskMrRobot_ReforgeFrame:Toggle() + AskMrRobot.mainWindow:Toggle() elseif msg == 'show' then - AskMrRobot_ReforgeFrame:Show() + AskMrRobot.mainWindow:Show() elseif msg == 'hide' then - AskMrRobot_ReforgeFrame:Hide() + AskMrRobot.mainWindow:Hide() elseif msg == 'export' then - OnExport() + AskMrRobot.mainWindow.exportTab:Show() elseif msg == 'wipe' then - AskMrRobot_ReforgeFrame.combatLogTab:LogWipe() + AskMrRobot.mainWindow.combatLogTab:LogWipe() elseif msg == 'unwipe' then - AskMrRobot_ReforgeFrame.combatLogTab:LogUnwipe() + AskMrRobot.mainWindow.combatLogTab:LogUnwipe() else print(L.AMR_SLASH_COMMAND_TEXT_1 .. L.AMR_SLASH_COMMAND_TEXT_2 .. L.AMR_SLASH_COMMAND_TEXT_3 .. L.AMR_SLASH_COMMAND_TEXT_4 .. L.AMR_SLASH_COMMAND_TEXT_5 .. L.AMR_SLASH_COMMAND_TEXT_6 .. L.AMR_SLASH_COMMAND_TEXT_7) end end -function AskMrRobot.SaveAll() - AskMrRobot.ScanBank() - AskMrRobot.SaveBags() - AskMrRobot.SaveEquiped() - AskMrRobot.GetCurrencies() - AskMrRobot.GetGold() - AskMrRobot.GetAmrSpecializations() - AskMrRobot.GetAmrProfessions() - AskMrRobot.GetRace() - AskMrRobot.GetLevel() - AskMrRobot.GetAmrGlyphs() - AskMrRobot.GetAmrTalents() - --ReloadUI() +-- initialize settings when the addon loads +function AskMrRobot.InitializeSettings() + + -- global settings + if not AmrSettings then AmrSettings = {} end + if not AmrSettings.Logins then AmrSettings.Logins = {} end + + -- per-character settings + if not AmrDb then AmrDb = {} end + + -- addon stuff + if not AmrDb.IconInfo then AmrDb.IconInfo = {} end + if not AmrDb.Options then AmrDb.Options = {} end + if not AmrDb.LastCharacterImport then AmrDb.LastCharacterImport = "" end + if not AmrDb.LastCharacterImportDate then AmrDb.LastCharacterImportDate = "" end + + if not AmrDb.SendSettings then + AmrDb.SendSettings = { + SendGems = true, + SendEnchants = true, + SendEnchantMaterials = true, + SendToType = "a friend", + SendTo = "" + } + end + + -- character stuff + AskMrRobot.ScanCharacter() + if not AmrDb.BankItems then AmrDb.BankItems = {} end + if not AmrDb.BagItems then AmrDb.BagItems = {} end + if not AmrDb.Currencies then AmrDb.Currencies = {} end + if not AmrDb.Reps then AmrDb.Reps = {} end + -- data saved for both specs + if not AmrDb.Specs then AmrDb.Specs = {} end + if not AmrDb.Glyphs then AmrDb.Glyphs = {} end + if not AmrDb.Talents then AmrDb.Talents = {} end + if not AmrDb.Equipped then AmrDb.Equipped = {} end + + -- combat log specific settings + AskMrRobot.CombatLogTab.InitializeVariable() end -local function InitIcon() - icon = LibStub("LibDBIcon-1.0"); - icon:Register("AskMrRobot", amrLDB, AmrIconInfo); +-- record logins when the addon starts up, used to help figure out which character logged which parts of a log file +function AskMrRobot.RecordLogin() + + -- delete entries that are more than 10 days old to prevent the table from growing indefinitely + if AmrSettings.Logins and #AmrSettings.Logins > 0 then + local now = time() + local oldDuration = 60 * 60 * 24 * 10 + local entryTime + repeat + -- parse entry and get time + local parts = {} + for part in string.gmatch(AmrSettings.Logins[1], "([^;]+)") do + tinsert(parts, part) + end + entryTime = tonumber(parts[3]) + + -- entries are in order, remove first entry if it is old + if difftime(now, entryTime) > oldDuration then + tremove(AmrSettings.Logins, 1) + end + until #AmrSettings.Logins == 0 or difftime(now, entryTime) <= oldDuration + end + + -- record the time a player logs in, used to figure out which player logged which parts of their log file + local key = AmrDb.RealmName .. ";" .. AmrDb.CharacterName .. ";" + local loginData = key .. time() + if AmrSettings.Logins and #AmrSettings.Logins > 0 then + local last = AmrSettings.Logins[#AmrSettings.Logins] + if string.len(last) >= string.len(key) and string.sub(last, 1, string.len(key)) ~= key then + table.insert(AmrSettings.Logins, loginData) + end + else + table.insert(AmrSettings.Logins, loginData) + end +end + +function AskMrRobot.InitializeMinimap() + + -- minimap icon and data broker icon plugin thingy + _amrLDB = LibStub("LibDataBroker-1.1"):NewDataObject("AskMrRobot", { + type = "launcher", + text = "Ask Mr. Robot", + icon = "Interface\\AddOns\\AskMrRobot\\Media\\icon", + OnClick = function() + if IsControlKeyDown() then + AskMrRobot.mainWindow.combatLogTab:LogWipe() + elseif IsModifiedClick("CHATLINL") then + AskMrRobot.mainWindow.exportTab:Show() + else + AskMrRobot.mainWindow:Toggle() + end + end, + OnTooltipShow = function(tt) + tt:AddLine("Ask Mr. Robot", 1, 1, 1); + tt:AddLine(" "); + tt:AddLine(L.AMR_ON_EVENT_TOOLTIP) + end + }); + + AskMrRobot.AmrUpdateMinimap() end function AskMrRobot.AmrUpdateMinimap() - if AmrOptions.hideMapIcon then - if icon then - icon:Hide("AskMrRobot"); + if AmrDb.Options.hideMapIcon then + if _minimapIcon then + _minimapIcon:Hide("AskMrRobot") end else - if not icon then - InitIcon() + if not _minimapIcon then + _minimapIcon = LibStub("LibDBIcon-1.0") + _minimapIcon:Register("AskMrRobot", _amrLDB, AmrDb.IconInfo) end - --if AskMrRobot_ReforgeFrame.combatLogTab:IsLogging() then - if AskMrRobot.CombatLogTab.IsLogging(nil) then - amrLDB.icon = 'Interface\\AddOns\\AskMrRobot\\Media\\icon_green' + + if AskMrRobot.CombatLogTab.IsLogging() then + _amrLDB.icon = 'Interface\\AddOns\\AskMrRobot\\Media\\icon_green' else - amrLDB.icon = 'Interface\\AddOns\\AskMrRobot\\Media\\icon' + _amrLDB.icon = 'Interface\\AddOns\\AskMrRobot\\Media\\icon' end - icon:Show("AskMrRobot"); + + _minimapIcon:Show("AskMrRobot") end end -local function getToolTipText(tip) - return EnumerateTooltipLines_helper(tip:GetRegions()) + +function AskMrRobot.SaveAll() + AskMrRobot.ScanCharacter() + AskMrRobot.ScanBank() + AskMrRobot.ScanBags() + AskMrRobot.ScanEquipped() + AskMrRobot.GetCurrencies() + AskMrRobot.GetReputations() + AskMrRobot.GetSpecs() end -local bagItems = {} -local bagItemsWithCount = {} - -function AskMrRobot.ScanBag(bagId) - local numSlots = GetContainerNumSlots(bagId); - for slotId = 1, numSlots do - local _, itemCount, _, _, _, _, itemLink = GetContainerItemInfo(bagId, slotId); - if itemLink ~= nil then - local itemData = parseItemLink(itemLink) - if itemData.itemId ~= nil then - tinsert(bagItems, itemLink); - tinsert(bagItemsWithCount, {link = itemLink, count = itemCount}) - end - end - end -end - -local BACKPACK_CONTAINER = 0; -local BANK_CONTAINER = -1; - -function AskMrRobot.ScanEquiped() - local equipedItems = {}; - for slotNum = 1, #AskMrRobot.slotIds do - local slotId = AskMrRobot.slotIds[slotNum]; - local itemLink = GetInventoryItemLink("player", slotId); - if (itemLink ~= nil) then - equipedItems[slotId .. ""] = itemLink; - end - end - return equipedItems -end - -function AskMrRobot.SaveEquiped() - AmrEquipedItems = AskMrRobot.ScanEquiped(); -end - -function AskMrRobot.ScanBags() - bagItems = {} - bagItemsWithCount = {} - - AskMrRobot.ScanBag(BACKPACK_CONTAINER); -- backpack - - for bagId = 1, NUM_BAG_SLOTS do - AskMrRobot.ScanBag(bagId); - end - - - return bagItems, bagItemsWithCount -end - -function AskMrRobot.SaveBags() - AmrBagItems, _ = AskMrRobot.ScanBags() -end - -function AskMrRobot.GetGold() - AmrGold = GetMoney(); -end - -local lastBankBagId = nil; -local lastBankSlotId = nil; -local bankItems = {}; -local bankItemsAndCount = {}; -AmrBankItemsAndCounts = {}; - -local function ScanBankBag(bagId) - local numSlots = GetContainerNumSlots(bagId); - for slotId = 1, numSlots do - local _, itemCount, _, _, _, _, itemLink = GetContainerItemInfo(bagId, slotId); - if itemLink ~= nil then - local itemData = parseItemLink(itemLink) - if itemData.itemId ~= nil then - lastBankBagId = bagId; - lastBankSlotId = slotId; - tinsert(bankItems, itemLink); - tinsert(bankItemsAndCount, {link = itemLink, count = itemCount}) - end - end - end -end - -function AskMrRobot.ScanBank() - - bankItems = {}; - bankItemsAndCount = {} - - ScanBankBag(BANK_CONTAINER); - for bagId = NUM_BAG_SLOTS + 1, NUM_BAG_SLOTS + NUM_BANKBAGSLOTS do - ScanBankBag(bagId); - end - - -- see if the scan completed before the window closed - if lastBankBagId ~= nil then - local itemLink = GetContainerItemLink(lastBankBagId, lastBankSlotId); - if itemLink ~= nil then --still open - AmrBankItems = bankItems; - AmrBankItemsAndCounts = bankItemsAndCount - end - end -end - -local function GetCurrencyAmount(index) - local localized_label, amount, icon_file_name = GetCurrencyInfo(index); - return amount; -end - -function AskMrRobot.GetCurrencies() - local currencies = {}; - local currencyList = {61, 81, 241, 361, 384, 394, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 416, 515, 614, 615, 676, 679}; - - for i, currency in pairs(currencyList) do - local amount = GetCurrencyAmount(currency); - if amount ~= 0 then - currencies[currencyList[i]] = amount; - end - end - AmrCurrencies = currencies; -end - -local function GetAmrSpecialization(specGroup) - local spec = GetSpecialization(false, false, specGroup); - return spec and GetSpecializationInfo(spec); -end - -function AskMrRobot.GetAmrSpecializations() - - AmrSpecializations = {}; - - AmrActiveSpec = GetActiveSpecGroup(); - - for group = 1, 2 do - AmrSpecializations[group .. ""] = GetAmrSpecialization(group) - end - --- Death Knight --- 250 - Blood --- 251 - Frost --- 252 - Unholy --- Druid --- 102 - Balance --- 103 - Feral Combat --- 104 - Guardian --- 105 - Restoration --- Hunter --- 253 - Beast Mastery --- 254 - Marksmanship --- 255 - Survival --- Mage --- 62 - Arcane --- 63 - Fire --- 64 - Frost --- Monk --- 268 - Brewmaster --- 269 - Windwalker --- 270 - Mistweaver --- Paladin --- 65 - Holy --- 66 - Protection --- 70 - Retribution --- Priest --- 256 Discipline --- 257 Holy --- 258 Shadow --- Rogue --- 259 - Assassination --- 260 - Combat --- 261 - Subtlety --- Shaman --- 262 - Elemental --- 263 - Enhancement --- 264 - Restoration --- Warlock --- 265 - Affliction --- 266 - Demonology --- 267 - Destruction --- Warrior --- 71 - Arms --- 72 - Fury --- 73 - Protection +-- gets all basic character properties +function AskMrRobot.ScanCharacter() + AmrDb.RealmName = GetRealmName() + AmrDb.CharacterName = UnitName("player") + AmrDb.Guild = GetGuildInfo("player") + AmrDb.ActiveSpec = GetActiveSpecGroup() + AmrDb.Level = UnitLevel("player"); + + local cls, clsEn = UnitClass("player") + AmrDb.Class = clsEn; + + local race, raceEn = UnitRace("player") + AmrDb.Race = raceEn; + AmrDb.Faction = UnitFactionGroup("player") + + AskMrRobot.GetAmrProfessions() end function AskMrRobot.GetAmrProfessions() @@ -601,88 +300,178 @@ } local prof1, prof2, archaeology, fishing, cooking, firstAid = GetProfessions(); - AmrProfessions = {}; + AmrDb.Professions = {}; if prof1 then local name, icon, skillLevel, maxSkillLevel, numAbilities, spelloffset, skillLine, skillModifier = GetProfessionInfo(prof1); if profMap[skillLine] ~= nil then - AmrProfessions[profMap[skillLine]] = skillLevel; + AmrDb.Professions[profMap[skillLine]] = skillLevel; end end if prof2 then local name, icon, skillLevel, maxSkillLevel, numAbilities, spelloffset, skillLine, skillModifier = GetProfessionInfo(prof2); if profMap[skillLine] ~= nil then - AmrProfessions[profMap[skillLine]] = skillLevel; + AmrDb.Professions[profMap[skillLine]] = skillLevel; end end if archaeology then local name, icon, skillLevel, maxSkillLevel, numAbilities, spelloffset, skillLine, skillModifier = GetProfessionInfo(archaeology); if profMap[skillLine] ~= nil then - AmrProfessions[profMap[skillLine]] = skillLevel; + AmrDb.Professions[profMap[skillLine]] = skillLevel; end end if fishing then local name, icon, skillLevel, maxSkillLevel, numAbilities, spelloffset, skillLine, skillModifier = GetProfessionInfo(fishing); if profMap[skillLine] ~= nil then - AmrProfessions[profMap[skillLine]] = skillLevel; + AmrDb.Professions[profMap[skillLine]] = skillLevel; end end if cooking then local name, icon, skillLevel, maxSkillLevel, numAbilities, spelloffset, skillLine, skillModifier = GetProfessionInfo(cooking); if profMap[skillLine] ~= nil then - AmrProfessions[profMap[skillLine]] = skillLevel; + AmrDb.Professions[profMap[skillLine]] = skillLevel; end end if firstAid then local name, icon, skillLevel, maxSkillLevel, numAbilities, spelloffset, skillLine, skillModifier = GetProfessionInfo(firstAid); if profMap[skillLine] ~= nil then - AmrProfessions[profMap[skillLine]] = skillLevel; + AmrDb.Professions[profMap[skillLine]] = skillLevel; end end end -function AskMrRobot.GetRace() - local race, raceEn = UnitRace("player"); - AmrRace = raceEn; - AmrFaction = UnitFactionGroup("player"); + +-- use some local variables to deal with the fact that a user can close the bank before a scan completes +local _lastBankBagId = nil +local _lastBankSlotId = nil +local BACKPACK_CONTAINER = 0 +local BANK_CONTAINER = -1 + +local function scanBag(bagId, isBank, bagTable, bagItemsWithCount) + local numSlots = GetContainerNumSlots(bagId) + for slotId = 1, numSlots do + local _, itemCount, _, _, _, _, itemLink = GetContainerItemInfo(bagId, slotId) + -- we skip any stackable item, as far as we know, there is no equippable gear that can be stacked + if itemLink ~= nil then + local itemData = AskMrRobot.parseItemLink(itemLink) + if itemData ~= nil then + if itemCount == 1 then + if isBank then + _lastBankBagId = bagId + _lastBankSlotId = slotId + end + tinsert(bagTable, itemLink) + end + if bagItemsWithCount then + if bagItemsWithCount[itemData.id] then + bagItemsWithCount[itemData.id] = bagItemsWithCount[itemData.id] + itemCount + else + bagItemsWithCount[itemData.id] = itemCount + end + end + end + end + end +end + +function AskMrRobot.ScanBank(bankItemsWithCount) + local bankItems = {} + local bankItemsAndCounts = {} + + scanBag(BANK_CONTAINER, true, bankItems, bankItemsAndCounts) + for bagId = NUM_BAG_SLOTS + 1, NUM_BAG_SLOTS + NUM_BANKBAGSLOTS do + scanBag(bagId, true, bankItems, bankItemsAndCounts) + end + + -- see if the scan completed before the window closed, otherwise we don't overwrite with partial data + if _lastBankBagId ~= nil then + local itemLink = GetContainerItemLink(_lastBankBagId, _lastBankSlotId) + if itemLink ~= nil then --still open + AmrDb.BankItems = bankItems + AmrDb.BankItemsAndCounts = bankItemsAndCounts + end + end end -function AskMrRobot.GetLevel() - AmrLevel = UnitLevel("player"); +function AskMrRobot.ScanBags(bagItemsWithCount) + local bagItems = {} + scanBag(BACKPACK_CONTAINER, false, bagItems, bagItemsWithCount) -- backpack + for bagId = 1, NUM_BAG_SLOTS do + scanBag(bagId, false, bagItems, bagItemsWithCount) + end + AmrDb.BagItems = bagItems end -local SlotNames = { - "HeadSlot", - "NeckSlot", - "ShoulderSlot", - "ShirtSlot", - "ChestSlot", - "WaistSlot", - "LegsSlot", - "FeetSlot", - "WristSlot", - "HandsSlot", - "Finger0Slot", - "Finger1Slot", - "Trinket0Slot", - "Trinket1Slot", - "BackSlot", - "MainHandSlot", - "SecondaryHandSlot", --- "RangedSlot", - "TabardSlot", -} +function AskMrRobot.ScanEquipped() + local equippedItems = {}; + for slotNum = 1, #AskMrRobot.slotIds do + local slotId = AskMrRobot.slotIds[slotNum]; + local itemLink = GetInventoryItemLink("player", slotId); + if (itemLink ~= nil) then + equippedItems[slotId] = itemLink; + end + end + + -- store last-seen equipped gear for each spec + AmrDb.Equipped[GetActiveSpecGroup()] = equippedItems +end -local function GetAmrTalentsForSpec(spec) + +local function getCurrencyAmount(index) + local localized_label, amount, icon_file_name = GetCurrencyInfo(index); + return amount; +end + +function AskMrRobot.GetCurrencies() + local currencies = {}; + currencies[-1] = GetMoney() + + local currencyList = {61, 81, 241, 361, 384, 394, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 416, 515, 614, 615, 676, 679} + for i, currency in pairs(currencyList) do + local amount = getCurrencyAmount(currency) + if amount ~= 0 then + currencies[currency] = amount + end + end + + AmrDb.Currencies = currencies +end + + +local function getRepStanding(factionId) + local name, description, standingId, _ = GetFactionInfoByID(factionId) + return standingId - 1; -- our rep enum correspond to what the armory returns, are 1 less than what the game returns +end + +function AskMrRobot.GetReputations() + local reps = {} + + local repList = {1375,1376,1270,1269,1341,1337,1387,1388,1435} + for i, repId in pairs(repList) do + local standing = getRepStanding(repId) + if standing >= 0 then + reps[repId] = standing + end + end + + AmrDb.Reps = reps; +end + + +local function getSpecId(specGroup) + local spec = GetSpecialization(false, false, specGroup); + return spec and GetSpecializationInfo(spec); +end + +local function getTalents(specGroup) local talentInfo = {} - local maxTiers = 6 - for talent = 1, GetNumTalents() do - local name, texture, tier, column, selected, available = GetTalentInfo(talent, false, spec) - if tier > maxTiers then - maxTiers = tier - end - if selected then - talentInfo[tier] = column - end + local maxTiers = 7 + for tier = 1, maxTiers do + for col = 1, 3 do + local id, name, texture, selected, available = GetTalentInfo(tier, col, specGroup) + if selected then + talentInfo[tier] = col + end + end end local str = "" @@ -697,17 +486,10 @@ return str end -function AskMrRobot.GetAmrTalents() - AmrTalents = {} - for spec = 1, GetNumSpecGroups() do - AmrTalents[spec] = GetAmrTalentsForSpec(spec); - end -end - -local function GetAmrGlyphsForSpec(spec) +local function getGlyphs(specGroup) local glyphs = {} for i = 1, NUM_GLYPH_SLOTS do - local _, _, _, glyphSpellID, _, glyphID = GetGlyphSocketInfo(i, spec) + local _, _, _, glyphSpellID, _, glyphID = GetGlyphSocketInfo(i, specGroup) if (glyphID) then tinsert(glyphs, glyphSpellID) end @@ -715,31 +497,32 @@ return glyphs; end -function AskMrRobot.GetAmrGlyphs() - AmrGlyphs = {} - for spec = 1, GetNumSpecGroups() do - AmrGlyphs[spec] = GetAmrGlyphsForSpec(spec) +-- get specs, talents, and glyphs +function AskMrRobot.GetSpecs() + + AmrDb.Specs = {} + AmrDb.Talents = {} + AmrDb.Glyphs = {} + + for group = 1, GetNumSpecGroups() do + -- spec, convert game spec id to one of our spec ids + local specId = getSpecId(group) + if specId then + AmrDb.Specs[group] = AskMrRobot.specIds[specId] + else + AmrDb.Specs[group] = 0 + end + + AmrDb.Talents[group] = getTalents(group) + AmrDb.Glyphs[group] = getGlyphs(group) end end ---[[ -local function ItemLinkToExportString(itemLink, slot) - local itemData = parseItemLink(itemLink) - local ret = {} - table.insert(ret, slot) - table.insert(ret, itemData.itemId) - table.insert(ret, itemData.suffixId) - table.insert(ret, itemData.upgradeId) - table.insert(ret, itemData.gemEnchantIds[1]) - table.insert(ret, itemData.gemEnchantIds[2]) - table.insert(ret, itemData.gemEnchantIds[3]) - table.insert(ret, itemData.enchantId) - table.insert(ret, itemData.reforgeId) - return table.concat(ret, ":") -end -]] +---------------------------------------------------------------------------- +-- Export +---------------------------------------------------------------------------- -local function toCompressedNumberList(list) +function AskMrRobot.toCompressedNumberList(list) -- ensure the values are numbers, sorted from lowest to highest local nums = {} for i, v in ipairs(list) do @@ -758,14 +541,67 @@ return table.concat(ret, ",") end --- create a more compact but less readable string -function AskMrRobot.ExportToCompressedString(includeInventory) +-- appends a list of items to the export +local function appendItemsToExport(fields, itemObjects) + + -- sort by item id so we can compress it more easily + table.sort(itemObjects, function(a, b) return a.id < b.id end) + + -- append to the export string + local prevItemId = 0 + local prevGemId = 0 + local prevEnchantId = 0 + local prevUpgradeId = 0 + local prevBonusId = 0 + for i, itemData in ipairs(itemObjects) do + local itemParts = {} + + table.insert(itemParts, itemData.id - prevItemId) + prevItemId = itemData.id + + if itemData.slot ~= nil then table.insert(itemParts, "s" .. itemData.slot) end + if itemData.suffixId ~= 0 then table.insert(itemParts, "f" .. itemData.suffixId) end + if itemData.upgradeId ~= 0 then + table.insert(itemParts, "u" .. (itemData.upgradeId - prevUpgradeId)) + prevUpgradeId = itemData.upgradeId + end + if itemData.bonusIds then + for bIndex, bValue in ipairs(itemData.bonusIds) do + table.insert(itemParts, "b" .. (bValue - prevBonusId)) + prevBonusId = bValue + end + end + if itemData.gemIds[1] ~= 0 then + table.insert(itemParts, "x" .. (itemData.gemIds[1] - prevGemId)) + prevGemId = itemData.gemIds[1] + end + if itemData.gemIds[2] ~= 0 then + table.insert(itemParts, "y" .. (itemData.gemIds[2] - prevGemId)) + prevGemId = itemData.gemIds[2] + end + if itemData.gemIds[3] ~= 0 then + table.insert(itemParts, "z" .. (itemData.gemIds[3] - prevGemId)) + prevGemId = itemData.gemIds[3] + end + if itemData.enchantId ~= 0 then + table.insert(itemParts, "e" .. (itemData.enchantId - prevEnchantId)) + prevEnchantId = itemData.enchantId + end + + table.insert(fields, table.concat(itemParts, "")) + end +end + +-- create a compact string representing this player +-- if complete is true, exports everything (inventory, both specs) +-- otherwise only exports the player's active gear, spec, etc. +function AskMrRobot.ExportToCompressedString(complete) local fields = {} -- compressed string uses a fixed order rather than inserting identifiers table.insert(fields, GetAddOnMetadata(AskMrRobot.AddonName, "Version")) - table.insert(fields, AmrRealmName) - table.insert(fields, AmrCharacterName) + table.insert(fields, AmrDb.RealmName) + table.insert(fields, AmrDb.CharacterName) -- guild name local guildName = GetGuildInfo("player") @@ -776,22 +612,22 @@ end -- race, default to pandaren if we can't read it for some reason - local raceval = raceIds[AmrRace] + local raceval = AskMrRobot.raceIds[AmrDb.Race] if raceval == nil then raceval = 13 end table.insert(fields, raceval) -- faction, default to alliance if we can't read it for some reason - raceval = factionIds[AmrFaction] + raceval = AskMrRobot.factionIds[AmrDb.Faction] if raceval == nil then raceval = 1 end table.insert(fields, raceval) - table.insert(fields, AmrLevel) + table.insert(fields, AmrDb.Level) local profs = {} local noprofs = true - if AmrProfessions then - for k, v in pairs(AmrProfessions) do - local profval = professionIds[k] + if AmrDb.Professions then + for k, v in pairs(AmrDb.Professions) do + local profval = AskMrRobot.professionIds[k] if profval ~= nil then noprofs = false table.insert(profs, profval .. ":" .. v) @@ -805,149 +641,80 @@ table.insert(fields, table.concat(profs, ",")) - if (AmrActiveSpec ~= nil) then - table.insert(fields, AmrActiveSpec) - table.insert(fields, AmrSpecializations[AmrActiveSpec .. ""]) - table.insert(fields, AmrTalents[AmrActiveSpec]) - table.insert(fields, toCompressedNumberList(AmrGlyphs[AmrActiveSpec])) - else - table.insert(fields, "_") - table.insert(fields, "_") - table.insert(fields, "_") - table.insert(fields, "_") + -- export specs + table.insert(fields, AmrDb.ActiveSpec) + for spec = 1, 2 do + if AmrDb.Specs[spec] and (complete or spec == AmrDb.ActiveSpec) then + table.insert(fields, ".s" .. spec) -- indicates the start of a spec block + table.insert(fields, AmrDb.Specs[spec]) + table.insert(fields, AmrDb.Talents[spec]) + table.insert(fields, AskMrRobot.toCompressedNumberList(AmrDb.Glyphs[spec])) + end end - -- convert items to parsed objects, sorted by id - local itemObjects = {} - if AmrEquipedItems then - for k, v in pairs(AmrEquipedItems) do - local itemData = parseItemLink(v) - itemData.slot = k - table.insert(itemObjects, itemData) - end + -- export equipped gear + if AmrDb.Equipped then + for spec = 1, 2 do + if AmrDb.Equipped[spec] and (complete or spec == AmrDb.ActiveSpec) then + table.insert(fields, ".q" .. spec) -- indicates the start of an equipped gear block + + local itemObjects = {} + for k, v in pairs(AmrDb.Equipped[spec]) do + local itemData = AskMrRobot.parseItemLink(v) + itemData.slot = k + table.insert(itemObjects, itemData) + end + + appendItemsToExport(fields, itemObjects) + end + end end - -- if desired, include bank/bag items too - if includeInventory then - if AmrBagItems then - for i, v in ipairs(AmrBagItems) do - local itemData = parseItemLink(v) - if itemData.itemId ~= nil then + -- if doing a complete export, include reputations and bank/bag items too + if complete then + + -- export reputations + local reps = {} + local noreps = true + if AmrDb.Reps then + for k, v in pairs(AmrDb.Reps) do + noreps = false + table.insert(reps, k .. ":" .. v) + end + end + if noreps then + table.insert(reps, "_") + end + + table.insert(fields, ".r") + table.insert(fields, table.concat(reps, ",")) + + -- export bag and bank + local itemObjects = {} + if AmrDb.BagItems then + for i, v in ipairs(AmrDb.BagItems) do + local itemData = AskMrRobot.parseItemLink(v) + if itemData ~= nil then table.insert(itemObjects, itemData) end end end - if AmrBankItems then - for i, v in ipairs(AmrBankItems) do - local itemData = parseItemLink(v) - if itemData.itemId ~= nil then + if AmrDb.BankItems then + for i, v in ipairs(AmrDb.BankItems) do + local itemData = AskMrRobot.parseItemLink(v) + if itemData ~= nil then table.insert(itemObjects, itemData) end end end - end - - -- sort by item id so we can compress it more easily - table.sort(itemObjects, function(a, b) return a.itemId < b.itemId end) - - -- append to the export string - local prevItemId = 0 - local prevGemId = 0 - local prevEnchantId = 0 - for i, itemData in ipairs(itemObjects) do - - local itemParts = {} - table.insert(itemParts, itemData.itemId - prevItemId) - prevItemId = itemData.itemId - - if itemData.slot ~= nil then table.insert(itemParts, "s" .. itemData.slot) end - if itemData.suffixId ~= 0 then table.insert(itemParts, "f" .. itemData.suffixId) end - if upgradeTable[itemData.upgradeId] ~= 0 then table.insert(itemParts, "u" .. upgradeTable[itemData.upgradeId]) end - if itemData.gemEnchantIds[1] ~= 0 then - table.insert(itemParts, "a" .. (itemData.gemEnchantIds[1] - prevGemId)) - prevGemId = itemData.gemEnchantIds[1] - end - if itemData.gemEnchantIds[2] ~= 0 then - table.insert(itemParts, "b" .. (itemData.gemEnchantIds[2] - prevGemId)) - prevGemId = itemData.gemEnchantIds[2] - end - if itemData.gemEnchantIds[3] ~= 0 then - table.insert(itemParts, "c" .. (itemData.gemEnchantIds[3] - prevGemId)) - prevGemId = itemData.gemEnchantIds[3] - end - if itemData.enchantId ~= 0 then - table.insert(itemParts, "e" .. (itemData.enchantId - prevEnchantId)) - prevEnchantId = itemData.enchantId - end - if itemData.reforgeId ~= 0 then table.insert(itemParts, "r" .. (itemData.reforgeId - 113)) end - - table.insert(fields, table.concat(itemParts, "")) + table.insert(fields, ".inv") + appendItemsToExport(fields, itemObjects) end return "$" .. table.concat(fields, ";") .. "$" end -local function GetPlayerExtraData(data, index) - - local unitId = "raid" .. index - - local guid = UnitGUID(unitId) - if guid == nil then - return nil - end - - local fields = {} - - local buffs = {} - for i=1,40 do - local _,_,_,count,_,_,_,_,_,_,spellId = UnitAura(unitId, i, "HELPFUL") - table.insert(buffs, spellId) - end - if #buffs == 0 then - table.insert(fields, "_") - else - table.insert(fields, toCompressedNumberList(buffs)) - end - - local petGuid = UnitGUID("raidpet" .. index) - if petGuid then - table.insert(fields, guid .. "," .. petGuid) - else - table.insert(fields, '_') - end - - local name = GetRaidRosterInfo(index) - local realm = GetRealmName() - local splitPos = string.find(name, "-") - if splitPos ~= nil then - realm = string.sub(name, splitPos + 1) - name = string.sub(name, 1, splitPos - 1) - end - - data[realm .. ":" .. name] = table.concat(fields, ";") -end - -function AskMrRobot.ExportLoggingData(timestamp) - - local isLogging = AskMrRobot_ReforgeFrame.combatLogTab:IsLogging() - if not isLogging then - return - end - - -- we only get extra information for people if in a raid - if not IsInRaid() then - return - end - - local data = {} - for i = 1,40 do - GetPlayerExtraData(data, i) - end - - AskMrRobot.CombatLogTab.SaveExtras(data, timestamp) -end - function AskMrRobot.ExportToAddonChat(timestamp) local msg = AskMrRobot.ExportToCompressedString(false) local msgPrefix = timestamp .. "\n" .. AmrRealmName .. "\n" .. AmrCharacterName .. "\n" @@ -973,408 +740,275 @@ -- Create an export string that can be copied to the website function AskMrRobot.ExportToString() + return AskMrRobot.ExportToCompressedString(true) +end - --[[ - local fields = {} + +---------------------------------------------------------------------------- +-- Import +---------------------------------------------------------------------------- + +-- imports will give us extra information about items, gems, and enchants +AskMrRobot.ExtraItemData = {} -- keyed by item id +AskMrRobot.ExtraGemData = {} -- keyed by gem enchant id +AskMrRobot.ExtraEnchantData = {} -- keyed by enchant id + +-- the data that was last imported +AskMrRobot.ImportData = nil -- keyed by slot id + +local MIN_IMPORT_VERSION = 2 + +-- +-- Import a character, returning nil on success, otherwise an error message, import result stored in AskMrRobot.ImportData +-- +function AskMrRobot.ImportCharacter(data) + + -- make sure all data is up to date before importing + AskMrRobot.SaveAll() - fields["realm"] = AmrRealmName - fields["name"] = AmrCharacterName - fields["race"] = AmrRace - fields["faction"] = AmrFaction - fields["level"] = AmrLevel - - local profs = {} - for k, v in pairs(AmrProfessions) do - table.insert(profs, k .. ":" .. v) - end - fields["professions"] = table.concat(profs, ",") - - if (AmrActiveSpec ~= nil) then - fields["activespec"] = AmrActiveSpec - fields["spec"] = AmrSpecializations[AmrActiveSpec .. ""] - fields["talents"] = AmrTalents[AmrActiveSpec] - fields["glyphs"] = table.concat(AmrGlyphs[AmrActiveSpec], ",") + if data == nil or string.len(data) == 0 then + return L.AMR_IMPORT_ERROR_EMPTY end - local items = {} - for k, v in pairs(AmrEquipedItems) do - table.insert(items, ItemLinkToExportString(v, k)) + local data1 = { strsplit("$", data) } + if #data1 ~= 3 then + return L.AMR_IMPORT_ERROR_FORMAT end - for i, v in ipairs(AmrBagItems) do - table.insert(items, ItemLinkToExportString(v, "-1")) + + local parts = { strsplit(";", data1[2]) } + + -- require a minimum version + local ver = tonumber(parts[1]) + if ver < MIN_IMPORT_VERSION then + return L.AMR_IMPORT_ERROR_VERSION end - for i, v in ipairs(AmrBankItems) do - table.insert(items, ItemLinkToExportString(v, "-1")) + + -- require realm/name match + local realm = parts[2] + local name = parts[3] + if realm ~= AmrDb.RealmName or name ~= AmrDb.CharacterName then + local badPers = name .. " (" .. realm .. ")" + local goodPers = AmrDb.CharacterName .. " (" .. AmrDb.RealmName .. ")" + return L.AMR_IMPORT_ERROR_CHAR:format(badPers, goodPers) end - fields["items"] = table.concat(items, "_") - local fieldList = {} - for k, v in pairs(fields) do - table.insert(fieldList, k .. "=" .. v) + -- require race match + local race = tonumber(parts[5]) + if race ~= AskMrRobot.raceIds[AmrDb.Race] then + return L.AMR_IMPORT_ERROR_RACE end - ]] - --return table.concat(fieldList, ";") + -- require faction match + local faction = tonumber(parts[6]) + if faction ~= AskMrRobot.factionIds[AmrDb.Faction] then + return L.AMR_IMPORT_ERROR_FACTION + end - return AskMrRobot.ExportToCompressedString(true) - --return AskMrRobot.ExportToAddonChat(time()) + -- require level match + local level = tonumber(parts[7]) + if level ~= AmrDb.Level then + return L.AMR_IMPORT_ERROR_LEVEL + end + + -- require spec match + local spec = tonumber(parts[11]) + if spec ~= AmrDb.Specs[AmrDb.ActiveSpec] then + print(AmrDb.ActiveSpec) + print(spec) + print(AmrDb.Specs[AmrDb.ActiveSpec]) + local _, specName = GetSpecializationInfoByID(AskMrRobot.gameSpecIds[spec]) + return L.AMR_IMPORT_ERROR_SPEC:format(specName) + end + + -- require talent match + local talents = parts[12] + if talents ~= AmrDb.Talents[AmrDb.ActiveSpec] then + return L.AMR_IMPORT_ERROR_TALENT + end + + -- require glyph match + -- TODO: re-enable this check when glyphs are more consistent + --local glyphs = parts[13] + --if glyphs ~= AskMrRobot.toCompressedNumberList(AmrDb.Glyphs[AmrDb.ActiveSpec]) then + -- return L.AMR_IMPORT_ERROR_GLYPH + --end + + -- if we make it this far, the data is valid, so read item information + local importData = {} + + local itemInfo = {} + local gemInfo = {} + local enchantInfo = {} + + local prevItemId = 0 + local prevGemId = 0 + local prevEnchantId = 0 + local prevUpgradeId = 0 + local prevBonusId = 0 + local digits = { + ["-"] = true, + ["0"] = true, + ["1"] = true, + ["2"] = true, + ["3"] = true, + ["4"] = true, + ["5"] = true, + ["6"] = true, + ["7"] = true, + ["8"] = true, + ["9"] = true, + } + for i = 15, #parts do + local itemString = parts[i] + if itemString ~= "" and itemString ~= "_" then + local tokens = {} + local bonusIds = {} + local hasBonuses = false + local token = "" + local prop = "i" + local tokenComplete = false + for j = 1, string.len(itemString) do + local c = string.sub(itemString, j, j) + if digits[c] == nil then + tokenComplete = true + else + token = token .. c + end + + if tokenComplete or j == string.len(itemString) then + local val = tonumber(token) + if prop == "i" then + val = val + prevItemId + prevItemId = val + elseif prop == "u" then + val = val + prevUpgradeId + prevUpgradeId = val + elseif prop == "b" then + val = val + prevBonusId + prevBonusId = val + elseif prop == "x" or prop == "y" or prop == "z" then + val = val + prevGemId + prevGemId = val + elseif prop == "e" then + val = val + prevEnchantId + prevEnchantId = val + end + + if prop == "b" then + table.insert(bonusIds, val) + hasBonuses = true + else + tokens[prop] = val + end + + token = "" + tokenComplete = false + + -- we have moved on to the next token + prop = c + end + end + + local obj = {} + importData[tonumber(tokens["s"])] = obj + + obj.id = tokens["i"] + obj.suffixId = tokens["f"] or 0 + obj.upgradeId = tokens["u"] or 0 + obj.enchantId = tokens["e"] or 0 + + obj.gemIds = {} + table.insert(obj.gemIds, tokens["x"] or 0) + table.insert(obj.gemIds, tokens["y"] or 0) + table.insert(obj.gemIds, tokens["z"] or 0) + table.insert(obj.gemIds, 0) + + if hasBonuses then + obj.bonusIds = bonusIds + end + + local itemObj = {} + itemObj.id = obj.id + itemInfo[obj.id] = itemObj + + -- look for any socket color information, add to our extra data + if tokens["c"] then + itemObj.socketColors = {} + for j = 1, string.len(tokens["c"]) do + table.insert(itemObj.socketColors, tonumber(string.sub(tokens["c"], j, j))) + end + end + + -- look for item ID duplicate info, deals with old SoO items + if tokens["d"] then + itemObj.duplicateId = tonumber(tokens["d"]) + end + + end + end + + -- now read any extra display information + parts = { strsplit("@", data1[3]) } + for i = 1, #parts do + local infoParts = { strsplit("\\", parts[i]) } + + if infoParts[1] == "g" then + + local gemObj = {} + gemObj.enchantId = tonumber(infoParts[2]) + gemObj.id = tonumber(infoParts[3]) + + local identicalGems = infoParts[4] + if string.len(identicalGems) > 0 then + gemObj.identicalGroup = {} + identicalGems = { strsplit(",", identicalGems) } + for j = 1, #identicalGems do + gemObj.identicalGroup[tonumber(identicalGems[j])] = true + end + end + + gemObj.text = string.gsub(infoParts[5], "_(%a+)_", function(s) return L.AMR_STAT_SHORT_STRINGS[s] end) + if infoParts[6] == nil or string.len(infoParts[6]) == 0 then + gemObj.identicalItemGroup = {[gemObj.id]=true} + else + local identicalIds = { strsplit(',', infoParts[6]) } + gemObj.identicalItemGroup = {} + for j = 1, #identicalIds do + gemObj.identicalItemGroup[tonumber(identicalIds[j])] = true + end + end + + gemInfo[gemObj.enchantId] = gemObj + + elseif infoParts[1] == "e" then + + local enchObj = {} + enchObj.id = tonumber(infoParts[2]) + enchObj.itemId = tonumber(infoParts[3]) + enchObj.spellId = tonumber(infoParts[4]) + enchObj.text = string.gsub(infoParts[5], "_(%a+)_", function(s) return L.AMR_STAT_SHORT_STRINGS[s] end) + + local mats = infoParts[6] + if string.len(mats) > 0 then + enchObj.materials = {} + mats = { strsplit(",", mats) } + for j = 1, #mats do + local kv = { strsplit("=", mats[j]) } + enchObj.materials[tonumber(kv[1])] = tonumber(kv[2]) + end + end + + enchantInfo[enchObj.id] = enchObj + + end + end + + -- we have succeeded, record the result + AskMrRobot.ImportData = importData + AskMrRobot.ExtraItemData = itemInfo + AskMrRobot.ExtraGemData = gemInfo + AskMrRobot.ExtraEnchantData = enchantInfo + + AmrDb.LastCharacterImport = data + AmrDb.LastCharacterImportDate = date() end - -local function parseGlyphs(input) - local glyphs = {} - for glyph in string.gmatch(input, "([^,]+)") do - tinsert(glyphs, glyph) - end - table.sort(glyphs) - return glyphs -end - -local function parseProfessions(input) - local professions = {} - for prof, v in string.gmatch(input, "([^:,]+):([^,]+)") do - professions[prof] = tonumber(v); - end - return professions; -end - -local gemColorMapping = { - y = 'Yellow', - b = 'Blue', - r = 'Red', - h = 'Hydraulic', - p = 'Prismatic', - m = 'Meta', - c = 'Cogwheel' -} - -local function parseAmrItem(input) - local slot, itemId, suffixList, upgradeId, gemColorString, gemEnchantIdString, gemIdString, enchantId, reforgeId = string.match(input, "^(%d+):(%d+):([^:]+):([^:]+):([^:]+):([^:]+):([^:]+):(%d+):(%d+)"); - -- parse the gem enchant ids out of their comma seperated list - local gems = {} - for gemEnchantId in string.gmatch(gemEnchantIdString, '(%d+)') do - tinsert(gems, {enchantId = tonumber(gemEnchantId), id = 0}) - end - -- make sure we have 4 gem ids - for i = #gems + 1, 4 do - tinsert(gems, {enchantId = 0, id = 0}) - end - -- parse the gem ids out of their comma seperated list - local gemIds = {} - i = 1 - for gemId in string.gmatch(gemIdString, '(%d+)') do - gems[i].id = tonumber(gemId) - i = i + 1 - end - i = 1 - for gemColor in string.gmatch(gemColorString, '([^,])') do - gems[i].color = gemColorMapping[gemColor] - i = i + 1 - end - - -- parse the possible suffixes out of their comma seperated list and put them in a set (number -> bool) - local suffixes = {} - for suffixId in string.gmatch(suffixList, '(%-?%d+)') do - suffixes[tonumber(suffixId)] = true - end - - local item = { - itemId = tonumber(itemId), - suffixes = suffixes, - upgradeId = tonumber(upgradeId), - gems = gems, - enchantId = tonumber(enchantId), - reforgeId = tonumber(reforgeId) - } - return slot, item -end - - -function AskMrRobot.parseAmr(input) - local parsedInput = {} - parsedInput.items = {} - for k, v in string.gmatch(input, "([^=;]+)=([^;]*)") do - if (k == 'item') then - local slot, item = parseAmrItem(v); - parsedInput.items[AskMrRobot.slotIdToSlotNum[tonumber(slot) + 1]] = item; - elseif (k == 'glyphs') then - parsedInput.glyphs = parseGlyphs(v) - elseif (k == 'professions') then - parsedInput.professions = parseProfessions(v) - else - parsedInput[k]=v - end - end - return parsedInput -end - -function AskMrRobot.validateRealm(realm) - return realm == GetRealmName(); -end - -function AskMrRobot.validateCharacterName(characterName) - return UnitName("player") == characterName -end - -function AskMrRobot.validateRace(race) - local _, raceEn = UnitRace("player") - return raceEn == race or (raceEn == "Scourge" and race == "Undead") -end - -function AskMrRobot.validateFaction(faction) - return faction == UnitFactionGroup("player") -end - -function AskMrRobot.validateSpec(spec) - if spec == 'nil' then - spec = nil - end - local currentSpec = GetAmrSpecialization(GetActiveSpecGroup()) - return (not currentSpec and not spec) or tostring(currentSpec) == spec -end - -function AskMrRobot.validateTalents(talents) - if talents == nil then - talents = '' - end - return talents == GetAmrTalentsForSpec(GetActiveSpecGroup()) -end - -function AskMrRobot.validateGlyphs(glyphs) - if (glyphs == nil) then - glyphs = {}; - end - local currentGlyphs = GetAmrGlyphsForSpec(GetActiveSpecGroup()) - table.sort(glyphs, function(a,b) return tostring(a) < tostring(b) end) - table.sort(currentGlyphs, function(a,b) return tostring(a) < tostring(b) end) - - if #glyphs ~= #currentGlyphs then - return false - end - for i = 1, #glyphs do - if tostring(glyphs[i]) ~= tostring(currentGlyphs[i]) then - return false - end - end - - return true -end - -local function getPrimaryProfessions() - local profs = {} - local prof1, prof2 = GetProfessions() - local profMap = { - [794] = "Archaeology", - [171] = "Alchemy", - [164] = "Blacksmithing", - [185] = "Cooking", - [333] = "Enchanting", - [202] = "Engineering", - [129] = "First Aid", - [356] = "Fishing", - [182] = "Herbalism", - [773] = "Inscription", - [755] = "Jewelcrafting", - [165] = "Leatherworking", - [186] = "Mining", - [393] = "Skinning", - [197] = "Tailoring" - } - - if prof1 then - local _, _, skillLevel, _, _, _, skillLine = GetProfessionInfo(prof1); - if profMap[skillLine] ~= nil then - profs[profMap[skillLine]] = skillLevel - end - end - if prof2 then - local _, _, skillLevel, _, _, _, skillLine = GetProfessionInfo(prof2); - if profMap[skillLine] ~= nil then - profs[profMap[skillLine]] = skillLevel - end - end - return profs; -end - -local professionThresholds = { - Leatherworking = 575, - Inscription = 600, - Alchemy = 50, - Enchanting = 550, - Jewelcrafting = 550, - Blacksmithing = 550, - Tailoring = 550 -} - -function AskMrRobot.validateProfessions(professions) - local currentProfessions = getPrimaryProfessions() - if #currentProfessions ~= #professions then - return false - end - for k, v in pairs(professions) do - if currentProfessions[k] then - local threshold = professionThresholds[k] - if not threshold then - threshold = 1 - end - -- compare the desired profession against the threshold - local desired = v >= threshold - -- compare the current profession against the threshold - local has = currentProfessions[k] and currentProfessions[k] >= threshold - -- if the current value is on the other side of the threshold - -- then we don't match - if desired ~= has then - return false - end - else - return false - end - end - return true -end - -function AskMrRobot.populateItemDiffs(amrItem, itemLink, slotNum) - AskMrRobot.itemDiffs.items[slotNum] = nil - AskMrRobot.itemDiffs.gems[slotNum] = nil - AskMrRobot.itemDiffs.enchants[slotNum] = nil - AskMrRobot.itemDiffs.reforges[slotNum] = nil - - local needsUpgrade = false - local aSuffix = 0 - if amrItem then - for k,v in pairs(amrItem.suffixes) do - aSuffix = k - end - end - - if itemLink == nil then - if amrItem ~= nil then - AskMrRobot.itemDiffs.items[slotNum] = { - current = nil, - optimized = { itemId = amrItem.itemId, upgradeId = amrItem.upgradeId, suffixId = aSuffix }, - needsUpgrade = false - } - end - return - end - local item = parseItemLink(itemLink) - local isItemBad = false - - if amrItem == nil or item.itemId ~= amrItem.itemId then - isItemBad = true - else - if item.suffixId == 0 then - if #amrItem.suffixes > 0 then - isItemBad = true - end - else - if not amrItem.suffixes[item.suffixId] then - isItemBad = true - end - end - if not isItemBad and upgradeTable[item.upgradeId] ~= upgradeTable[amrItem.upgradeId] then - isItemBad = true - needsUpgrade = true - end - end - - if isItemBad then - AskMrRobot.itemDiffs.items[slotNum] = { - current = item.itemId, - optimized = { itemId = amrItem and amrItem.itemId or 0, upgradeId = amrItem and amrItem.upgradeId or 0, suffixId = aSuffix }, - needsUpgrade = needsUpgrade - } - return - end - - local badGemCount, gemInfo = AskMrRobot.MatchesGems(itemLink, item.gemEnchantIds, amrItem.gems) - if badGemCount > 0 then - AskMrRobot.itemDiffs.gems[slotNum] = gemInfo - end - - if item.enchantId ~= amrItem.enchantId then - AskMrRobot.itemDiffs.enchants[slotNum] = { - current = item.enchantId, - optimized = amrItem.enchantId - } - end - - if item.reforgeId ~= amrItem.reforgeId then - AskMrRobot.itemDiffs.reforges[slotNum] = { - current = item.reforgeId, - optimized = amrItem.reforgeId - } - end -end - ---[[ -function AskMrRobot.StartLogging() - if not LoggingCombat() then - LoggingCombat(1) - print("Started Combat Logging") - end -end - -function AskMrRobot.FinishLogging() - if LoggingCombat() then - LoggingCombat(0) - print("Finished Combat Logging") - end -end - --- local difficultyLookup = { --- DUNGEON_DIFFICULTY1, --- DUNGEON_DIFFICULTY2, --- RAID_DIFFICULTY_10PLAYER, --- RAID_DIFFICULTY_25PLAYER, --- RAID_DIFFICULTY_10PLAYER_HEROIC, --- RAID_DIFFICULTY_25PLAYER_HEROIC, --- RAID_FINDER, --- CHALLENGE_MODE, --- RAID_DIFFICULTY_40PLAYER, --- nil, --- nil, -- Norm scen --- nil, -- heroic scen --- nil, --- PLAYER_DIFFICULTY4 --- } - ---http://wowpedia.org/InstanceMapID -local instanceMaps = { - HeartOfFear = 1009, - MogushanVaults = 1008, - SiegeOfOrgrimmar = 1136, - TerraceOfEndlessSpring = 996, - ThroneOfThunder = 1098 -} - -function AskMrRobot.UpdateLogging() - - -- get the info about the instance - --local zone, zonetype, difficultyIndex, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceMapID = GetInstanceInfo() - local zone, _, difficultyIndex, _, _, _, _, instanceMapID = GetInstanceInfo() - --local difficulty = difficultyIndex - -- Unless Blizzard fixes scenarios to not return nil, let's hardcode this into returning "scenario" -Znuff - --if zonetype == nil and difficultyIndex == 1 then - --zonetype = "scenario" - --end - - -- if nothing has changed, then bail - --if (not zone) and difficulty == 0 then return end - if zone == AskMrRobot.lastzone and difficultyIndex == AskMrRobot.lastdiff then - -- do nothing if the zone hasn't ACTUALLY changed - -- otherwise we may override the user's manual enable/disable - return - end - - AskMrRobot.lastzone = zone - AskMrRobot.lastdiff = difficultyIndex - - if AmrOptions.autoLog[tonumber(instanceMapID)] then - if instanceMapID == instanceMaps.SiegeOfOrgrimmar then - AskMrRobot.StartLogging() - else - AskMrRobot.FinishLogging() - end - end -end -]]