adam@0: local _, AskMrRobot = ... yellowfive@11: local L = AskMrRobot.L; adam@0: adam@17: AskMrRobot.eventListener = CreateFrame("FRAME") -- Need a frame to respond to events adam@17: AskMrRobot.eventListener:RegisterEvent("ADDON_LOADED") -- Fired when saved variables are loaded adam@17: AskMrRobot.eventListener:RegisterEvent("ITEM_PUSH") adam@17: AskMrRobot.eventListener:RegisterEvent("DELETE_ITEM_CONFIRM") adam@17: AskMrRobot.eventListener:RegisterEvent("UNIT_INVENTORY_CHANGED") adam@17: AskMrRobot.eventListener:RegisterEvent("BANKFRAME_OPENED") adam@17: --AskMrRobot.eventListener:RegisterEvent("BANKFRAME_CLOSED"); adam@17: AskMrRobot.eventListener:RegisterEvent("PLAYERBANKSLOTS_CHANGED") adam@17: AskMrRobot.eventListener:RegisterEvent("CHARACTER_POINTS_CHANGED") adam@17: AskMrRobot.eventListener:RegisterEvent("CONFIRM_TALENT_WIPE") adam@17: AskMrRobot.eventListener:RegisterEvent("PLAYER_TALENT_UPDATE") adam@17: AskMrRobot.eventListener:RegisterEvent("ACTIVE_TALENT_GROUP_CHANGED") adam@17: AskMrRobot.eventListener:RegisterEvent("PLAYER_ENTERING_WORLD") adam@17: --AskMrRobot.eventListener:RegisterEvent("PLAYER_LOGOUT") -- Fired when about to log out adam@17: --AskMrRobot.eventListener:RegisterEvent("PLAYER_LEVEL_UP") adam@0: --AskMrRobot.eventListener:RegisterEvent("GET_ITEM_INFO_RECEIVED") adam@0: AskMrRobot.eventListener:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED") adam@0: AskMrRobot.eventListener:RegisterEvent("SOCKET_INFO_UPDATE") adam@0: AskMrRobot.eventListener:RegisterEvent("SOCKET_INFO_CLOSE") adam@0: AskMrRobot.eventListener:RegisterEvent("BAG_UPDATE") adam@0: AskMrRobot.eventListener:RegisterEvent("ITEM_UNLOCKED") yellowfive@11: AskMrRobot.eventListener:RegisterEvent("PLAYER_REGEN_DISABLED") yellowfive@11: --AskMrRobot.eventListener:RegisterEvent("ENCOUNTER_START") adam@0: AskMrRobot.eventListener:RegisterEvent("CHAT_MSG_ADDON") adam@0: AskMrRobot.eventListener:RegisterEvent("UPDATE_INSTANCE_INFO") adam@0: AskMrRobot.eventListener:RegisterEvent("PLAYER_DIFFICULTY_CHANGED") adam@21: AskMrRobot.eventListener:RegisterEvent("VOID_STORAGE_OPEN") adam@21: AskMrRobot.eventListener:RegisterEvent("VOID_STORAGE_CONTENTS_UPDATE") adam@21: AskMrRobot.eventListener:RegisterEvent("PLAYER_DIFFICULTY_CHANGED") adam@21: AskMrRobot.eventListener:RegisterEvent("VOID_STORAGE_UPDATE") adam@0: adam@0: AskMrRobot.AddonName = ... adam@0: AskMrRobot.ChatPrefix = "_AMR" adam@0: yellowfive@31: -- flag to turn on debugging behavior yellowfive@31: AskMrRobot.debug = false yellowfive@31: adam@17: -- the main user interface window adam@17: AskMrRobot.mainWindow = nil adam@0: adam@17: local _amrLDB adam@17: local _minimapIcon adam@0: adam@0: function AskMrRobot.eventListener:OnEvent(event, ...) adam@0: if event == "ADDON_LOADED" then adam@0: local addon = select(1, ...) adam@0: if (addon == "AskMrRobot") then adam@17: AskMrRobot.InitializeSettings() adam@17: AskMrRobot.InitializeMinimap() adam@0: yellowfive@35: AskMrRobot.mainWindow = AskMrRobot.AmrUI:new() yellowfive@35: adam@0: -- listen for messages from other AMR addons yellowfive@33: RegisterAddonMessagePrefix(AskMrRobot.ChatPrefix) adam@0: end adam@17: adam@17: elseif event == "UNIT_INVENTORY_CHANGED" then adam@17: AskMrRobot.ScanEquipped() adam@0: adam@17: elseif event == "ITEM_PUSH" or event == "DELETE_ITEM_CONFIRM" or event == "SOCKET_INFO_CLOSE" or event == "PLAYER_SPECIALIZATION_CHANGED" or event == "BAG_UPDATE" then adam@17: --if AskMrRobot_ReforgeFrame then adam@17: -- AskMrRobot_ReforgeFrame:OnUpdate() adam@17: --end adam@0: --AskMrRobot.SaveBags(); adam@0: --AskMrRobot.SaveEquiped(); adam@0: --AskMrRonot.GetCurrencies(); adam@0: --AskMrRobot.GetGold(); adam@17: adam@0: elseif event == "BANKFRAME_OPENED" or event == "PLAYERBANKSLOTS_CHANGED" then adam@0: AskMrRobot.ScanBank(); adam@21: elseif event == "VOID_STORAGE_OPEN" or event == "VOID_STORAGE_CONTENTS_UPDATE" or event == "VOID_STORAGE_DEPOSIT_UPDATE" or event == "VOID_STORAGE_UPDATE" then adam@21: AskMrRobot.ScanVoidStorage(); adam@0: elseif event == "CHARACTER_POINTS_CHANGED" or event == "CONFIRM_TALENT_WIPE" or event == "PLAYER_TALENT_UPDATE" or event == "ACTIVE_TALENT_GROUP_CHANGED" then adam@0: --AskMrRobot.GetAmrSpecializations(); adam@17: --if AskMrRobot_ReforgeFrame then adam@17: -- AskMrRobot_ReforgeFrame:OnUpdate() adam@17: --end adam@17: adam@0: elseif event == "ITEM_UNLOCKED" then adam@17: --AskMrRobot.On_ITEM_UNLOCKED() yellowfive@11: adam@17: elseif event == "PLAYER_ENTERING_WORLD" then adam@17: AskMrRobot.RecordLogin() yellowfive@11: yellowfive@11: elseif event == "PLAYER_REGEN_DISABLED" then yellowfive@11: -- send data about this character when a player enters combat in a supported zone yellowfive@11: if AskMrRobot.IsSupportedInstance() then yellowfive@11: local t = time() yellowfive@11: AskMrRobot.SaveAll() yellowfive@11: AskMrRobot.ExportToAddonChat(t) adam@17: AskMrRobot.CombatLogTab.SaveExtras(t) yellowfive@11: end yellowfive@11: adam@0: elseif event == "CHAT_MSG_ADDON" then adam@0: local chatPrefix, message = select(1, ...) adam@17: local isLogging = AskMrRobot.CombatLogTab.IsLogging() adam@0: if (isLogging and chatPrefix == AskMrRobot.ChatPrefix) then yellowfive@35: if AskMrRobot.mainWindow then yellowfive@35: AskMrRobot.mainWindow.combatLogTab:ReadAddonMessage(message) yellowfive@35: end adam@0: end adam@17: adam@0: elseif event == "UPDATE_INSTANCE_INFO" or event == "PLAYER_DIFFICULTY_CHANGED" then yellowfive@35: if AskMrRobot.mainWindow then yellowfive@35: AskMrRobot.mainWindow.combatLogTab:UpdateAutoLogging() yellowfive@35: end adam@0: end adam@0: adam@0: end adam@0: adam@17: AskMrRobot.eventListener:SetScript("OnEvent", AskMrRobot.eventListener.OnEvent) adam@0: adam@0: adam@0: SLASH_AMR1 = "/amr"; adam@0: function SlashCmdList.AMR(msg) adam@0: adam@0: if msg == 'toggle' then adam@17: AskMrRobot.mainWindow:Toggle() adam@0: elseif msg == 'show' then adam@17: AskMrRobot.mainWindow:Show() adam@0: elseif msg == 'hide' then adam@17: AskMrRobot.mainWindow:Hide() adam@0: elseif msg == 'export' then adam@17: AskMrRobot.mainWindow.exportTab:Show() yellowfive@11: elseif msg == 'wipe' then adam@17: AskMrRobot.mainWindow.combatLogTab:LogWipe() yellowfive@11: elseif msg == 'unwipe' then adam@17: AskMrRobot.mainWindow.combatLogTab:LogUnwipe() adam@0: else yellowfive@11: 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) adam@0: end adam@0: end adam@0: adam@17: -- initialize settings when the addon loads adam@17: function AskMrRobot.InitializeSettings() adam@17: adam@17: -- global settings adam@17: if not AmrSettings then AmrSettings = {} end adam@17: if not AmrSettings.Logins then AmrSettings.Logins = {} end adam@17: adam@17: -- per-character settings adam@17: if not AmrDb then AmrDb = {} end adam@17: adam@17: -- addon stuff adam@17: if not AmrDb.IconInfo then AmrDb.IconInfo = {} end adam@17: if not AmrDb.Options then AmrDb.Options = {} end adam@17: if not AmrDb.LastCharacterImport then AmrDb.LastCharacterImport = "" end adam@17: if not AmrDb.LastCharacterImportDate then AmrDb.LastCharacterImportDate = "" end adam@17: adam@17: if not AmrDb.SendSettings then adam@17: AmrDb.SendSettings = { adam@17: SendGems = true, adam@17: SendEnchants = true, adam@17: SendEnchantMaterials = true, adam@17: SendToType = "a friend", adam@17: SendTo = "" adam@17: } adam@17: end adam@17: adam@17: -- character stuff adam@17: AskMrRobot.ScanCharacter() adam@17: if not AmrDb.BankItems then AmrDb.BankItems = {} end adam@17: if not AmrDb.BagItems then AmrDb.BagItems = {} end adam@17: if not AmrDb.Currencies then AmrDb.Currencies = {} end adam@17: if not AmrDb.Reps then AmrDb.Reps = {} end adam@17: -- data saved for both specs adam@17: if not AmrDb.Specs then AmrDb.Specs = {} end adam@17: if not AmrDb.Glyphs then AmrDb.Glyphs = {} end adam@17: if not AmrDb.Talents then AmrDb.Talents = {} end adam@17: if not AmrDb.Equipped then AmrDb.Equipped = {} end adam@17: adam@17: -- combat log specific settings adam@17: AskMrRobot.CombatLogTab.InitializeVariable() adam@0: end adam@0: adam@17: -- record logins when the addon starts up, used to help figure out which character logged which parts of a log file adam@17: function AskMrRobot.RecordLogin() adam@17: adam@17: -- delete entries that are more than 10 days old to prevent the table from growing indefinitely adam@17: if AmrSettings.Logins and #AmrSettings.Logins > 0 then adam@17: local now = time() adam@17: local oldDuration = 60 * 60 * 24 * 10 adam@17: local entryTime adam@17: repeat adam@17: -- parse entry and get time adam@17: local parts = {} adam@17: for part in string.gmatch(AmrSettings.Logins[1], "([^;]+)") do adam@17: tinsert(parts, part) adam@17: end adam@17: entryTime = tonumber(parts[3]) adam@17: adam@17: -- entries are in order, remove first entry if it is old adam@17: if difftime(now, entryTime) > oldDuration then adam@17: tremove(AmrSettings.Logins, 1) adam@17: end adam@17: until #AmrSettings.Logins == 0 or difftime(now, entryTime) <= oldDuration adam@17: end adam@17: adam@17: -- record the time a player logs in, used to figure out which player logged which parts of their log file adam@17: local key = AmrDb.RealmName .. ";" .. AmrDb.CharacterName .. ";" adam@17: local loginData = key .. time() adam@17: if AmrSettings.Logins and #AmrSettings.Logins > 0 then adam@17: local last = AmrSettings.Logins[#AmrSettings.Logins] adam@17: if string.len(last) >= string.len(key) and string.sub(last, 1, string.len(key)) ~= key then adam@17: table.insert(AmrSettings.Logins, loginData) adam@17: end adam@17: else adam@17: table.insert(AmrSettings.Logins, loginData) adam@17: end adam@17: end adam@17: adam@17: function AskMrRobot.InitializeMinimap() adam@17: adam@17: -- minimap icon and data broker icon plugin thingy adam@17: _amrLDB = LibStub("LibDataBroker-1.1"):NewDataObject("AskMrRobot", { adam@17: type = "launcher", adam@17: text = "Ask Mr. Robot", adam@17: icon = "Interface\\AddOns\\AskMrRobot\\Media\\icon", adam@17: OnClick = function() adam@17: if IsControlKeyDown() then adam@17: AskMrRobot.mainWindow.combatLogTab:LogWipe() adam@17: elseif IsModifiedClick("CHATLINL") then adam@17: AskMrRobot.mainWindow.exportTab:Show() adam@17: else adam@17: AskMrRobot.mainWindow:Toggle() adam@17: end adam@17: end, adam@17: OnTooltipShow = function(tt) adam@17: tt:AddLine("Ask Mr. Robot", 1, 1, 1); adam@17: tt:AddLine(" "); adam@17: tt:AddLine(L.AMR_ON_EVENT_TOOLTIP) adam@17: end adam@17: }); adam@17: adam@17: AskMrRobot.AmrUpdateMinimap() adam@0: end adam@0: yellowfive@11: function AskMrRobot.AmrUpdateMinimap() adam@17: if AmrDb.Options.hideMapIcon then adam@17: if _minimapIcon then adam@17: _minimapIcon:Hide("AskMrRobot") adam@0: end adam@0: else adam@17: if not _minimapIcon then adam@17: _minimapIcon = LibStub("LibDBIcon-1.0") adam@17: _minimapIcon:Register("AskMrRobot", _amrLDB, AmrDb.IconInfo) adam@0: end adam@17: adam@17: if AskMrRobot.CombatLogTab.IsLogging() then adam@17: _amrLDB.icon = 'Interface\\AddOns\\AskMrRobot\\Media\\icon_green' yellowfive@11: else adam@17: _amrLDB.icon = 'Interface\\AddOns\\AskMrRobot\\Media\\icon' yellowfive@11: end adam@17: adam@17: _minimapIcon:Show("AskMrRobot") adam@0: end adam@0: end adam@0: adam@17: adam@17: function AskMrRobot.SaveAll() adam@17: AskMrRobot.ScanCharacter() adam@17: AskMrRobot.ScanBags() adam@17: AskMrRobot.ScanEquipped() adam@17: AskMrRobot.GetCurrencies() adam@17: AskMrRobot.GetReputations() adam@17: AskMrRobot.GetSpecs() adam@0: end adam@0: adam@17: -- gets all basic character properties adam@17: function AskMrRobot.ScanCharacter() adam@17: AmrDb.RealmName = GetRealmName() adam@17: AmrDb.CharacterName = UnitName("player") adam@17: AmrDb.Guild = GetGuildInfo("player") adam@17: AmrDb.ActiveSpec = GetActiveSpecGroup() adam@17: AmrDb.Level = UnitLevel("player"); adam@17: adam@17: local cls, clsEn = UnitClass("player") adam@17: AmrDb.Class = clsEn; adam@17: adam@17: local race, raceEn = UnitRace("player") adam@17: AmrDb.Race = raceEn; adam@17: AmrDb.Faction = UnitFactionGroup("player") adam@17: adam@17: AskMrRobot.GetAmrProfessions() adam@0: end adam@0: adam@0: function AskMrRobot.GetAmrProfessions() adam@0: adam@0: local profMap = { adam@0: [794] = "Archaeology", adam@0: [171] = "Alchemy", adam@0: [164] = "Blacksmithing", adam@0: [185] = "Cooking", adam@0: [333] = "Enchanting", adam@0: [202] = "Engineering", adam@0: [129] = "First Aid", adam@0: [356] = "Fishing", adam@0: [182] = "Herbalism", adam@0: [773] = "Inscription", adam@0: [755] = "Jewelcrafting", adam@0: [165] = "Leatherworking", adam@0: [186] = "Mining", adam@0: [393] = "Skinning", adam@0: [197] = "Tailoring" adam@0: } adam@0: adam@0: local prof1, prof2, archaeology, fishing, cooking, firstAid = GetProfessions(); adam@17: AmrDb.Professions = {}; adam@0: if prof1 then adam@0: local name, icon, skillLevel, maxSkillLevel, numAbilities, spelloffset, skillLine, skillModifier = GetProfessionInfo(prof1); adam@0: if profMap[skillLine] ~= nil then adam@17: AmrDb.Professions[profMap[skillLine]] = skillLevel; adam@0: end adam@0: end adam@0: if prof2 then adam@0: local name, icon, skillLevel, maxSkillLevel, numAbilities, spelloffset, skillLine, skillModifier = GetProfessionInfo(prof2); adam@0: if profMap[skillLine] ~= nil then adam@17: AmrDb.Professions[profMap[skillLine]] = skillLevel; adam@0: end adam@0: end adam@0: if archaeology then adam@0: local name, icon, skillLevel, maxSkillLevel, numAbilities, spelloffset, skillLine, skillModifier = GetProfessionInfo(archaeology); adam@0: if profMap[skillLine] ~= nil then adam@17: AmrDb.Professions[profMap[skillLine]] = skillLevel; adam@0: end adam@0: end adam@0: if fishing then adam@0: local name, icon, skillLevel, maxSkillLevel, numAbilities, spelloffset, skillLine, skillModifier = GetProfessionInfo(fishing); adam@0: if profMap[skillLine] ~= nil then adam@17: AmrDb.Professions[profMap[skillLine]] = skillLevel; adam@0: end adam@0: end adam@0: if cooking then adam@0: local name, icon, skillLevel, maxSkillLevel, numAbilities, spelloffset, skillLine, skillModifier = GetProfessionInfo(cooking); adam@0: if profMap[skillLine] ~= nil then adam@17: AmrDb.Professions[profMap[skillLine]] = skillLevel; adam@0: end adam@0: end adam@0: if firstAid then adam@0: local name, icon, skillLevel, maxSkillLevel, numAbilities, spelloffset, skillLine, skillModifier = GetProfessionInfo(firstAid); adam@0: if profMap[skillLine] ~= nil then adam@17: AmrDb.Professions[profMap[skillLine]] = skillLevel; adam@0: end adam@0: end adam@0: end adam@0: adam@17: adam@17: -- use some local variables to deal with the fact that a user can close the bank before a scan completes adam@17: local _lastBankBagId = nil adam@17: local _lastBankSlotId = nil adam@17: local BACKPACK_CONTAINER = 0 adam@17: local BANK_CONTAINER = -1 adam@17: adam@17: local function scanBag(bagId, isBank, bagTable, bagItemsWithCount) adam@17: local numSlots = GetContainerNumSlots(bagId) adam@17: for slotId = 1, numSlots do adam@17: local _, itemCount, _, _, _, _, itemLink = GetContainerItemInfo(bagId, slotId) adam@17: -- we skip any stackable item, as far as we know, there is no equippable gear that can be stacked adam@17: if itemLink ~= nil then adam@17: local itemData = AskMrRobot.parseItemLink(itemLink) adam@17: if itemData ~= nil then adam@17: if itemCount == 1 then adam@17: if isBank then adam@17: _lastBankBagId = bagId adam@17: _lastBankSlotId = slotId adam@17: end adam@17: tinsert(bagTable, itemLink) adam@17: end adam@17: if bagItemsWithCount then adam@17: if bagItemsWithCount[itemData.id] then adam@17: bagItemsWithCount[itemData.id] = bagItemsWithCount[itemData.id] + itemCount adam@17: else adam@17: bagItemsWithCount[itemData.id] = itemCount adam@17: end adam@17: end adam@17: end adam@17: end adam@17: end adam@17: end adam@17: adam@21: function AskMrRobot.ScanBank() adam@21: --REAGENTBANK_CONTAINER (-3) adam@17: local bankItems = {} adam@17: local bankItemsAndCounts = {} adam@17: adam@17: scanBag(BANK_CONTAINER, true, bankItems, bankItemsAndCounts) adam@21: scanBag(REAGENTBANK_CONTAINER, true, bankItems, bankItemsAndCounts) adam@17: for bagId = NUM_BAG_SLOTS + 1, NUM_BAG_SLOTS + NUM_BANKBAGSLOTS do adam@17: scanBag(bagId, true, bankItems, bankItemsAndCounts) adam@17: end adam@17: adam@17: -- see if the scan completed before the window closed, otherwise we don't overwrite with partial data adam@17: if _lastBankBagId ~= nil then adam@17: local itemLink = GetContainerItemLink(_lastBankBagId, _lastBankSlotId) adam@17: if itemLink ~= nil then --still open adam@17: AmrDb.BankItems = bankItems adam@17: AmrDb.BankItemsAndCounts = bankItemsAndCounts adam@17: end adam@17: end adam@0: end adam@0: adam@21: function AskMrRobot.ScanVoidStorage() adam@21: if IsVoidStorageReady() then adam@21: local voidItems = {} adam@21: local VOID_STORAGE_MAX = 80 adam@21: local VOID_STORAGE_PAGES = 2 adam@21: local i adam@21: for page = 1,VOID_STORAGE_PAGES do adam@21: for i = 1,VOID_STORAGE_MAX do adam@21: local itemId = GetVoidItemInfo(page, i) adam@21: if itemId then adam@21: local itemLink = GetVoidItemHyperlinkString(page, i); adam@21: if itemLink then adam@21: tinsert(voidItems, itemLink) adam@21: end adam@21: end adam@21: end adam@21: end adam@21: AmrDb.VoidItems = voidItems adam@21: end adam@21: end adam@21: adam@17: function AskMrRobot.ScanBags(bagItemsWithCount) adam@17: local bagItems = {} adam@17: scanBag(BACKPACK_CONTAINER, false, bagItems, bagItemsWithCount) -- backpack adam@17: for bagId = 1, NUM_BAG_SLOTS do adam@17: scanBag(bagId, false, bagItems, bagItemsWithCount) adam@17: end adam@17: AmrDb.BagItems = bagItems adam@0: end adam@0: adam@17: function AskMrRobot.ScanEquipped() adam@17: local equippedItems = {}; adam@17: for slotNum = 1, #AskMrRobot.slotIds do adam@17: local slotId = AskMrRobot.slotIds[slotNum]; adam@17: local itemLink = GetInventoryItemLink("player", slotId); adam@17: if (itemLink ~= nil) then adam@17: equippedItems[slotId] = itemLink; adam@17: end adam@17: end adam@17: adam@17: -- store last-seen equipped gear for each spec adam@17: AmrDb.Equipped[GetActiveSpecGroup()] = equippedItems adam@17: end adam@0: adam@17: adam@17: local function getCurrencyAmount(index) adam@17: local localized_label, amount, icon_file_name = GetCurrencyInfo(index); adam@17: return amount; adam@17: end adam@17: adam@17: function AskMrRobot.GetCurrencies() adam@17: local currencies = {}; adam@17: currencies[-1] = GetMoney() adam@17: yellowfive@31: 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, 823} adam@17: for i, currency in pairs(currencyList) do adam@17: local amount = getCurrencyAmount(currency) adam@17: if amount ~= 0 then adam@17: currencies[currency] = amount adam@17: end adam@17: end adam@17: adam@17: AmrDb.Currencies = currencies adam@17: end adam@17: adam@17: adam@17: local function getRepStanding(factionId) adam@17: local name, description, standingId, _ = GetFactionInfoByID(factionId) adam@17: return standingId - 1; -- our rep enum correspond to what the armory returns, are 1 less than what the game returns adam@17: end adam@17: adam@17: function AskMrRobot.GetReputations() adam@17: local reps = {} adam@17: adam@17: local repList = {1375,1376,1270,1269,1341,1337,1387,1388,1435} adam@17: for i, repId in pairs(repList) do adam@17: local standing = getRepStanding(repId) adam@17: if standing >= 0 then adam@17: reps[repId] = standing adam@17: end adam@17: end adam@17: adam@17: AmrDb.Reps = reps; adam@17: end adam@17: adam@17: adam@17: local function getSpecId(specGroup) adam@17: local spec = GetSpecialization(false, false, specGroup); adam@17: return spec and GetSpecializationInfo(spec); adam@17: end adam@17: adam@17: local function getTalents(specGroup) adam@0: local talentInfo = {} adam@17: local maxTiers = 7 adam@17: for tier = 1, maxTiers do adam@17: for col = 1, 3 do adam@17: local id, name, texture, selected, available = GetTalentInfo(tier, col, specGroup) adam@17: if selected then adam@17: talentInfo[tier] = col adam@17: end adam@17: end adam@0: end adam@0: adam@0: local str = "" adam@0: for i = 1, maxTiers do adam@0: if talentInfo[i] then adam@0: str = str .. talentInfo[i] adam@0: else adam@0: str = str .. '0' adam@0: end adam@0: end adam@0: adam@0: return str adam@0: end adam@0: adam@17: local function getGlyphs(specGroup) adam@0: local glyphs = {} adam@0: for i = 1, NUM_GLYPH_SLOTS do adam@17: local _, _, _, glyphSpellID, _, glyphID = GetGlyphSocketInfo(i, specGroup) adam@0: if (glyphID) then adam@0: tinsert(glyphs, glyphSpellID) adam@0: end adam@0: end adam@0: return glyphs; adam@0: end adam@0: adam@17: -- get specs, talents, and glyphs adam@17: function AskMrRobot.GetSpecs() adam@17: adam@17: AmrDb.Specs = {} adam@17: AmrDb.Talents = {} adam@17: AmrDb.Glyphs = {} adam@17: adam@17: for group = 1, GetNumSpecGroups() do adam@17: -- spec, convert game spec id to one of our spec ids adam@17: local specId = getSpecId(group) adam@17: if specId then adam@17: AmrDb.Specs[group] = AskMrRobot.specIds[specId] adam@17: else adam@17: AmrDb.Specs[group] = 0 adam@17: end adam@17: adam@17: AmrDb.Talents[group] = getTalents(group) adam@17: AmrDb.Glyphs[group] = getGlyphs(group) adam@0: end adam@0: end adam@0: adam@17: ---------------------------------------------------------------------------- adam@17: -- Export adam@17: ---------------------------------------------------------------------------- adam@0: adam@17: function AskMrRobot.toCompressedNumberList(list) adam@0: -- ensure the values are numbers, sorted from lowest to highest adam@0: local nums = {} adam@0: for i, v in ipairs(list) do adam@0: table.insert(nums, tonumber(v)) adam@0: end adam@0: table.sort(nums) adam@0: adam@0: local ret = {} adam@0: local prev = 0 adam@0: for i, v in ipairs(nums) do adam@0: local diff = v - prev adam@0: table.insert(ret, diff) adam@0: prev = v adam@0: end adam@0: adam@0: return table.concat(ret, ",") adam@0: end adam@0: adam@17: -- appends a list of items to the export adam@17: local function appendItemsToExport(fields, itemObjects) adam@17: adam@17: -- sort by item id so we can compress it more easily adam@17: table.sort(itemObjects, function(a, b) return a.id < b.id end) adam@17: adam@17: -- append to the export string adam@17: local prevItemId = 0 adam@17: local prevGemId = 0 adam@17: local prevEnchantId = 0 adam@17: local prevUpgradeId = 0 adam@17: local prevBonusId = 0 adam@17: for i, itemData in ipairs(itemObjects) do adam@17: local itemParts = {} adam@17: adam@17: table.insert(itemParts, itemData.id - prevItemId) adam@17: prevItemId = itemData.id adam@17: adam@17: if itemData.slot ~= nil then table.insert(itemParts, "s" .. itemData.slot) end adam@17: if itemData.suffixId ~= 0 then table.insert(itemParts, "f" .. itemData.suffixId) end adam@17: if itemData.upgradeId ~= 0 then adam@17: table.insert(itemParts, "u" .. (itemData.upgradeId - prevUpgradeId)) adam@17: prevUpgradeId = itemData.upgradeId adam@17: end adam@17: if itemData.bonusIds then adam@17: for bIndex, bValue in ipairs(itemData.bonusIds) do adam@17: table.insert(itemParts, "b" .. (bValue - prevBonusId)) adam@17: prevBonusId = bValue adam@17: end adam@17: end adam@17: if itemData.gemIds[1] ~= 0 then adam@17: table.insert(itemParts, "x" .. (itemData.gemIds[1] - prevGemId)) adam@17: prevGemId = itemData.gemIds[1] adam@17: end adam@17: if itemData.gemIds[2] ~= 0 then adam@17: table.insert(itemParts, "y" .. (itemData.gemIds[2] - prevGemId)) adam@17: prevGemId = itemData.gemIds[2] adam@17: end adam@17: if itemData.gemIds[3] ~= 0 then adam@17: table.insert(itemParts, "z" .. (itemData.gemIds[3] - prevGemId)) adam@17: prevGemId = itemData.gemIds[3] adam@17: end adam@17: if itemData.enchantId ~= 0 then adam@17: table.insert(itemParts, "e" .. (itemData.enchantId - prevEnchantId)) adam@17: prevEnchantId = itemData.enchantId adam@17: end adam@17: adam@17: table.insert(fields, table.concat(itemParts, "")) adam@17: end adam@17: end adam@17: adam@17: -- create a compact string representing this player adam@17: -- if complete is true, exports everything (inventory, both specs) adam@17: -- otherwise only exports the player's active gear, spec, etc. adam@17: function AskMrRobot.ExportToCompressedString(complete) adam@0: local fields = {} adam@0: adam@0: -- compressed string uses a fixed order rather than inserting identifiers adam@0: table.insert(fields, GetAddOnMetadata(AskMrRobot.AddonName, "Version")) adam@17: table.insert(fields, AmrDb.RealmName) adam@17: table.insert(fields, AmrDb.CharacterName) adam@0: adam@0: -- guild name adam@0: local guildName = GetGuildInfo("player") adam@0: if guildName == nil then adam@0: table.insert(fields, "") adam@0: else adam@0: table.insert(fields, guildName) adam@0: end adam@0: adam@0: -- race, default to pandaren if we can't read it for some reason adam@17: local raceval = AskMrRobot.raceIds[AmrDb.Race] adam@0: if raceval == nil then raceval = 13 end adam@0: table.insert(fields, raceval) adam@0: adam@0: -- faction, default to alliance if we can't read it for some reason adam@17: raceval = AskMrRobot.factionIds[AmrDb.Faction] adam@0: if raceval == nil then raceval = 1 end adam@0: table.insert(fields, raceval) adam@0: adam@17: table.insert(fields, AmrDb.Level) adam@0: adam@0: local profs = {} adam@0: local noprofs = true adam@17: if AmrDb.Professions then adam@17: for k, v in pairs(AmrDb.Professions) do adam@17: local profval = AskMrRobot.professionIds[k] yellowfive@11: if profval ~= nil then yellowfive@11: noprofs = false yellowfive@11: table.insert(profs, profval .. ":" .. v) yellowfive@11: end yellowfive@11: end yellowfive@11: end adam@0: adam@0: if noprofs then adam@0: table.insert(profs, "0:0") adam@0: end adam@0: adam@0: table.insert(fields, table.concat(profs, ",")) adam@0: adam@17: -- export specs adam@17: table.insert(fields, AmrDb.ActiveSpec) adam@17: for spec = 1, 2 do adam@17: if AmrDb.Specs[spec] and (complete or spec == AmrDb.ActiveSpec) then adam@17: table.insert(fields, ".s" .. spec) -- indicates the start of a spec block adam@17: table.insert(fields, AmrDb.Specs[spec]) adam@17: table.insert(fields, AmrDb.Talents[spec]) adam@17: table.insert(fields, AskMrRobot.toCompressedNumberList(AmrDb.Glyphs[spec])) adam@17: end adam@0: end adam@0: adam@17: -- export equipped gear adam@17: if AmrDb.Equipped then adam@17: for spec = 1, 2 do adam@17: if AmrDb.Equipped[spec] and (complete or spec == AmrDb.ActiveSpec) then adam@17: table.insert(fields, ".q" .. spec) -- indicates the start of an equipped gear block adam@17: adam@17: local itemObjects = {} adam@17: for k, v in pairs(AmrDb.Equipped[spec]) do adam@17: local itemData = AskMrRobot.parseItemLink(v) adam@17: itemData.slot = k adam@17: table.insert(itemObjects, itemData) adam@17: end adam@17: adam@17: appendItemsToExport(fields, itemObjects) adam@17: end adam@17: end yellowfive@11: end adam@0: adam@17: -- if doing a complete export, include reputations and bank/bag items too adam@17: if complete then adam@17: adam@17: -- export reputations adam@17: local reps = {} adam@17: local noreps = true adam@17: if AmrDb.Reps then adam@17: for k, v in pairs(AmrDb.Reps) do adam@17: noreps = false adam@17: table.insert(reps, k .. ":" .. v) adam@17: end adam@17: end adam@17: if noreps then adam@17: table.insert(reps, "_") adam@17: end adam@17: adam@17: table.insert(fields, ".r") adam@17: table.insert(fields, table.concat(reps, ",")) adam@17: adam@17: -- export bag and bank adam@17: local itemObjects = {} adam@17: if AmrDb.BagItems then adam@17: for i, v in ipairs(AmrDb.BagItems) do adam@21: local _,_,_,_,_,_,_,stackCount = GetItemInfo(v) adam@21: if stackCount == 1 then adam@21: local itemData = AskMrRobot.parseItemLink(v) adam@21: if itemData ~= nil then adam@21: table.insert(itemObjects, itemData) adam@21: end adam@21: end yellowfive@11: end yellowfive@11: end adam@17: if AmrDb.BankItems then adam@17: for i, v in ipairs(AmrDb.BankItems) do adam@21: local _,_,_,_,_,_,_,stackCount = GetItemInfo(v) adam@21: if stackCount == 1 then adam@21: local itemData = AskMrRobot.parseItemLink(v) adam@21: if itemData ~= nil then adam@21: table.insert(itemObjects, itemData) adam@21: end yellowfive@11: end yellowfive@11: end yellowfive@11: end adam@21: if AmrDb.VoidItems then adam@21: for i, v in ipairs(AmrDb.VoidItems) do adam@21: local _,_,_,_,_,_,_,stackCount = GetItemInfo(v) adam@21: if stackCount == 1 then adam@21: local itemData = AskMrRobot.parseItemLink(v) adam@21: if itemData ~= nil then adam@21: table.insert(itemObjects, itemData) adam@21: end adam@21: end adam@21: end adam@21: end adam@0: adam@17: table.insert(fields, ".inv") adam@17: appendItemsToExport(fields, itemObjects) adam@0: end yellowfive@11: adam@0: return "$" .. table.concat(fields, ";") .. "$" adam@0: end adam@0: adam@0: function AskMrRobot.ExportToAddonChat(timestamp) yellowfive@11: local msg = AskMrRobot.ExportToCompressedString(false) yellowfive@29: local msgPrefix = timestamp .. "\n" .. AmrDb.RealmName .. "\n" .. AmrDb.CharacterName .. "\n" adam@0: adam@0: -- break the data into 250 character chunks (to deal with the short limit on addon message size) adam@0: local chunks = {} adam@0: local i = 1 yellowfive@11: local length = string.len(msg) adam@0: local chunkLen = 249 - string.len(msgPrefix) adam@0: while (i <= length) do adam@0: local endpos = math.min(i + chunkLen, length) yellowfive@11: table.insert(chunks, msgPrefix .. string.sub(msg, i, endpos)) adam@0: i = endpos + 1 adam@0: end adam@0: adam@0: for i, v in ipairs(chunks) do adam@0: SendAddonMessage(AskMrRobot.ChatPrefix, v, "RAID") adam@0: end adam@0: adam@0: -- send a completion message adam@0: SendAddonMessage(AskMrRobot.ChatPrefix, msgPrefix .. "done", "RAID") adam@0: end adam@0: adam@0: -- Create an export string that can be copied to the website adam@0: function AskMrRobot.ExportToString() adam@17: return AskMrRobot.ExportToCompressedString(true) adam@17: end adam@0: adam@17: adam@17: ---------------------------------------------------------------------------- adam@17: -- Import adam@17: ---------------------------------------------------------------------------- adam@17: adam@17: -- imports will give us extra information about items, gems, and enchants adam@17: AskMrRobot.ExtraItemData = {} -- keyed by item id adam@17: AskMrRobot.ExtraGemData = {} -- keyed by gem enchant id adam@17: AskMrRobot.ExtraEnchantData = {} -- keyed by enchant id adam@17: adam@17: -- the data that was last imported adam@17: AskMrRobot.ImportData = nil -- keyed by slot id adam@17: adam@17: local MIN_IMPORT_VERSION = 2 adam@17: adam@17: -- adam@17: -- Import a character, returning nil on success, otherwise an error message, import result stored in AskMrRobot.ImportData adam@17: -- yellowfive@31: function AskMrRobot.ImportCharacter(data, isTest) adam@17: adam@17: -- make sure all data is up to date before importing adam@17: AskMrRobot.SaveAll() adam@0: adam@17: if data == nil or string.len(data) == 0 then adam@17: return L.AMR_IMPORT_ERROR_EMPTY adam@0: end adam@0: adam@17: local data1 = { strsplit("$", data) } adam@17: if #data1 ~= 3 then adam@17: return L.AMR_IMPORT_ERROR_FORMAT adam@0: end adam@17: adam@17: local parts = { strsplit(";", data1[2]) } adam@17: adam@17: -- require a minimum version adam@17: local ver = tonumber(parts[1]) adam@17: if ver < MIN_IMPORT_VERSION then adam@17: return L.AMR_IMPORT_ERROR_VERSION adam@0: end adam@17: adam@17: -- require realm/name match yellowfive@31: if not isTest then yellowfive@31: local realm = parts[2] yellowfive@31: local name = parts[3] yellowfive@31: if name ~= AmrDb.CharacterName then yellowfive@31: local badPers = name .. " (" .. realm .. ")" yellowfive@31: local goodPers = AmrDb.CharacterName .. " (" .. AmrDb.RealmName .. ")" yellowfive@31: return L.AMR_IMPORT_ERROR_CHAR:format(badPers, goodPers) yellowfive@31: end yellowfive@31: yellowfive@31: -- require race match yellowfive@31: local race = tonumber(parts[5]) yellowfive@31: if race ~= AskMrRobot.raceIds[AmrDb.Race] then yellowfive@31: return L.AMR_IMPORT_ERROR_RACE yellowfive@31: end yellowfive@31: yellowfive@31: -- require faction match yellowfive@31: local faction = tonumber(parts[6]) yellowfive@31: if faction ~= AskMrRobot.factionIds[AmrDb.Faction] then yellowfive@31: return L.AMR_IMPORT_ERROR_FACTION yellowfive@31: end yellowfive@31: yellowfive@31: -- require level match yellowfive@31: local level = tonumber(parts[7]) yellowfive@31: if level ~= AmrDb.Level then yellowfive@31: return L.AMR_IMPORT_ERROR_LEVEL yellowfive@31: end yellowfive@31: yellowfive@31: -- require spec match yellowfive@31: local spec = tonumber(parts[11]) yellowfive@31: if spec ~= AmrDb.Specs[AmrDb.ActiveSpec] then yellowfive@31: print(AmrDb.ActiveSpec) yellowfive@31: print(spec) yellowfive@31: print(AmrDb.Specs[AmrDb.ActiveSpec]) yellowfive@31: local _, specName = GetSpecializationInfoByID(AskMrRobot.gameSpecIds[spec]) yellowfive@31: return L.AMR_IMPORT_ERROR_SPEC:format(specName) yellowfive@31: end yellowfive@31: yellowfive@31: -- require talent match yellowfive@31: local talents = parts[12] yellowfive@31: if talents ~= AmrDb.Talents[AmrDb.ActiveSpec] then yellowfive@31: return L.AMR_IMPORT_ERROR_TALENT yellowfive@31: end yellowfive@31: yellowfive@31: -- require glyph match yellowfive@31: -- TODO: re-enable this check when glyphs are more consistent yellowfive@31: --local glyphs = parts[13] yellowfive@31: --if glyphs ~= AskMrRobot.toCompressedNumberList(AmrDb.Glyphs[AmrDb.ActiveSpec]) then yellowfive@31: -- return L.AMR_IMPORT_ERROR_GLYPH yellowfive@31: --end adam@0: end adam@0: adam@17: adam@17: -- if we make it this far, the data is valid, so read item information adam@17: local importData = {} adam@17: adam@17: local itemInfo = {} adam@17: local gemInfo = {} adam@17: local enchantInfo = {} adam@17: adam@17: local prevItemId = 0 adam@17: local prevGemId = 0 adam@17: local prevEnchantId = 0 adam@17: local prevUpgradeId = 0 adam@17: local prevBonusId = 0 adam@17: local digits = { adam@17: ["-"] = true, adam@17: ["0"] = true, adam@17: ["1"] = true, adam@17: ["2"] = true, adam@17: ["3"] = true, adam@17: ["4"] = true, adam@17: ["5"] = true, adam@17: ["6"] = true, adam@17: ["7"] = true, adam@17: ["8"] = true, adam@17: ["9"] = true, adam@17: } adam@17: for i = 15, #parts do adam@17: local itemString = parts[i] adam@17: if itemString ~= "" and itemString ~= "_" then adam@17: local tokens = {} adam@17: local bonusIds = {} adam@17: local hasBonuses = false adam@17: local token = "" adam@17: local prop = "i" adam@17: local tokenComplete = false adam@17: for j = 1, string.len(itemString) do adam@17: local c = string.sub(itemString, j, j) adam@17: if digits[c] == nil then adam@17: tokenComplete = true adam@17: else adam@17: token = token .. c adam@17: end adam@17: adam@17: if tokenComplete or j == string.len(itemString) then adam@17: local val = tonumber(token) adam@17: if prop == "i" then adam@17: val = val + prevItemId adam@17: prevItemId = val adam@17: elseif prop == "u" then adam@17: val = val + prevUpgradeId adam@17: prevUpgradeId = val adam@17: elseif prop == "b" then adam@17: val = val + prevBonusId adam@17: prevBonusId = val adam@17: elseif prop == "x" or prop == "y" or prop == "z" then adam@17: val = val + prevGemId adam@17: prevGemId = val adam@17: elseif prop == "e" then adam@17: val = val + prevEnchantId adam@17: prevEnchantId = val adam@17: end adam@17: adam@17: if prop == "b" then adam@17: table.insert(bonusIds, val) adam@17: hasBonuses = true adam@17: else adam@17: tokens[prop] = val adam@17: end adam@17: adam@17: token = "" adam@17: tokenComplete = false adam@17: adam@17: -- we have moved on to the next token adam@17: prop = c adam@17: end adam@17: end adam@17: adam@17: local obj = {} adam@17: importData[tonumber(tokens["s"])] = obj adam@17: adam@17: obj.id = tokens["i"] adam@17: obj.suffixId = tokens["f"] or 0 adam@17: obj.upgradeId = tokens["u"] or 0 adam@17: obj.enchantId = tokens["e"] or 0 adam@17: adam@17: obj.gemIds = {} adam@17: table.insert(obj.gemIds, tokens["x"] or 0) adam@17: table.insert(obj.gemIds, tokens["y"] or 0) adam@17: table.insert(obj.gemIds, tokens["z"] or 0) adam@17: table.insert(obj.gemIds, 0) adam@17: adam@17: if hasBonuses then adam@17: obj.bonusIds = bonusIds adam@17: end adam@17: adam@17: local itemObj = {} adam@17: itemObj.id = obj.id adam@17: itemInfo[obj.id] = itemObj adam@17: adam@17: -- look for any socket color information, add to our extra data adam@17: if tokens["c"] then adam@17: itemObj.socketColors = {} adam@17: for j = 1, string.len(tokens["c"]) do adam@17: table.insert(itemObj.socketColors, tonumber(string.sub(tokens["c"], j, j))) yellowfive@33: end adam@17: end adam@17: adam@17: -- look for item ID duplicate info, deals with old SoO items adam@17: if tokens["d"] then adam@17: itemObj.duplicateId = tonumber(tokens["d"]) yellowfive@33: itemInfo[itemObj.duplicateId] = itemObj adam@17: end adam@17: adam@17: end adam@17: end adam@17: adam@17: -- now read any extra display information adam@17: parts = { strsplit("@", data1[3]) } adam@17: for i = 1, #parts do adam@17: local infoParts = { strsplit("\\", parts[i]) } adam@17: adam@17: if infoParts[1] == "g" then adam@17: adam@17: local gemObj = {} adam@17: gemObj.enchantId = tonumber(infoParts[2]) adam@17: gemObj.id = tonumber(infoParts[3]) adam@17: adam@17: local identicalGems = infoParts[4] adam@17: if string.len(identicalGems) > 0 then adam@17: gemObj.identicalGroup = {} adam@17: identicalGems = { strsplit(",", identicalGems) } adam@17: for j = 1, #identicalGems do adam@17: gemObj.identicalGroup[tonumber(identicalGems[j])] = true adam@17: end adam@17: end adam@17: adam@17: gemObj.text = string.gsub(infoParts[5], "_(%a+)_", function(s) return L.AMR_STAT_SHORT_STRINGS[s] end) adam@17: if infoParts[6] == nil or string.len(infoParts[6]) == 0 then adam@17: gemObj.identicalItemGroup = {[gemObj.id]=true} adam@17: else adam@17: local identicalIds = { strsplit(',', infoParts[6]) } adam@17: gemObj.identicalItemGroup = {} adam@17: for j = 1, #identicalIds do adam@17: gemObj.identicalItemGroup[tonumber(identicalIds[j])] = true adam@17: end adam@17: end adam@17: adam@17: gemInfo[gemObj.enchantId] = gemObj adam@17: adam@17: elseif infoParts[1] == "e" then adam@17: adam@17: local enchObj = {} adam@17: enchObj.id = tonumber(infoParts[2]) adam@17: enchObj.itemId = tonumber(infoParts[3]) adam@17: enchObj.spellId = tonumber(infoParts[4]) adam@17: enchObj.text = string.gsub(infoParts[5], "_(%a+)_", function(s) return L.AMR_STAT_SHORT_STRINGS[s] end) adam@17: adam@17: local mats = infoParts[6] adam@17: if string.len(mats) > 0 then adam@17: enchObj.materials = {} adam@17: mats = { strsplit(",", mats) } adam@17: for j = 1, #mats do adam@17: local kv = { strsplit("=", mats[j]) } adam@17: enchObj.materials[tonumber(kv[1])] = tonumber(kv[2]) adam@17: end adam@17: end adam@17: adam@17: enchantInfo[enchObj.id] = enchObj adam@17: adam@17: end adam@17: end adam@17: yellowfive@31: if isTest then yellowfive@33: yellowfive@31: -- print result for debugging yellowfive@33: --for k,v in pairs(importData) do yellowfive@35: -- local blah = AskMrRobot.createItemLink(v) yellowfive@35: -- print(blah) yellowfive@35: --local name, link = GetItemInfo(blah) yellowfive@35: --print(link) yellowfive@35: --if link == nil then yellowfive@35: -- print(blah) yellowfive@35: -- print("bad item: " .. v.id) yellowfive@35: --end yellowfive@33: --end yellowfive@33: yellowfive@31: yellowfive@31: else yellowfive@31: -- we have succeeded, record the result yellowfive@31: AskMrRobot.ImportData = importData yellowfive@31: AskMrRobot.ExtraItemData = itemInfo yellowfive@31: AskMrRobot.ExtraGemData = gemInfo yellowfive@31: AskMrRobot.ExtraEnchantData = enchantInfo yellowfive@31: yellowfive@31: AmrDb.LastCharacterImport = data yellowfive@31: AmrDb.LastCharacterImportDate = date() yellowfive@31: end adam@0: end