Mercurial > wow > askmrrobot
diff AskMrRobot.lua @ 0:ec731d2fe6ba
Version 1.2.12.0
author | Adam tegen <adam.tegen@gmail.com> |
---|---|
date | Tue, 20 May 2014 21:43:23 -0500 |
parents | |
children | ece9167c0d1c |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AskMrRobot.lua Tue May 20 21:43:23 2014 -0500 @@ -0,0 +1,1245 @@ +local _, AskMrRobot = ... + +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_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") +AskMrRobot.eventListener:RegisterEvent("SOCKET_INFO_CLOSE") +AskMrRobot.eventListener:RegisterEvent("BAG_UPDATE") +AskMrRobot.eventListener:RegisterEvent("ITEM_UNLOCKED") +--AskMrRobot.eventListener:RegisterEvent("PLAYER_REGEN_DISABLED") +AskMrRobot.eventListener:RegisterEvent("ENCOUNTER_START") +AskMrRobot.eventListener:RegisterEvent("CHAT_MSG_ADDON") +AskMrRobot.eventListener:RegisterEvent("UPDATE_INSTANCE_INFO") +AskMrRobot.eventListener:RegisterEvent("PLAYER_DIFFICULTY_CHANGED") + +AskMrRobot.AddonName = ... +AskMrRobot.ChatPrefix = "_AMR" + +local amrLDB +local icon +local reforgequeue +local reforgeFrame = nil +local LoggingCombat = _G.LoggingCombat + +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 +} + +-- 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 +} + +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 + +function AskMrRobot.eventListener:OnEvent(event, ...) + if event == "ADDON_LOADED" then + local addon = select(1, ...) + if (addon == "AskMrRobot") then + print("Loaded Ask Mr. Robot " .. GetAddOnMetadata(AskMrRobot.AddonName, "Version")) + + -- listen for messages from other AMR addons + RegisterAddonMessagePrefix(AskMrRobot.ChatPrefix) + + AmrRealmName = GetRealmName() + AmrCharacterName = UnitName("player") + + if not AmrLogData then AmrLogData = {} end + if not AmrLogData._autoLog then AmrLogData._autoLog = {} end + if not AmrLogData._autoLog[AskMrRobot.instanceIds.SiegeOfOrgrimmar] then + AmrLogData._autoLog[AskMrRobot.instanceIds.SiegeOfOrgrimmar] = "disabled" + end + + 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 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 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("Left Click to open the Ask Mr. Robot window.\n\nShift + Left Click to export your bag and bank data.") + 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 + end + + 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 + --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(); + 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 == "ENCOUNTER_START" then + -- send data about this character when a boss fight starts + AskMrRobot.SaveAll() + AskMrRobot.ExportToAddonChat(time()) + elseif event == "CHAT_MSG_ADDON" then + local chatPrefix, message = select(1, ...) + local isLogging = AskMrRobot_ReforgeFrame.combatLogTab:IsLogging() + if (isLogging and chatPrefix == AskMrRobot.ChatPrefix) then + AskMrRobot_ReforgeFrame.combatLogTab:ReadAddonMessage(message) + end + elseif event == "UPDATE_INSTANCE_INFO" or event == "PLAYER_DIFFICULTY_CHANGED" then + AskMrRobot_ReforgeFrame.combatLogTab:UpdateAutoLogging() + end + +end + +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() + elseif msg == 'show' then + AskMrRobot_ReforgeFrame:Show() + elseif msg == 'hide' then + AskMrRobot_ReforgeFrame:Hide() + elseif msg == 'export' then + OnExport() + else + print('Available AskMrRobot slash commands:\n' .. + ' /amr show -- show the main window\n' .. + ' /amr hide -- hide the main window\n' .. + ' /amr toggle -- toggle the main window\n' .. + ' /amr export -- export bag and bank data (uses your last selected method and either opens the copy/paste window, or saves and reloads ui)') + 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() +end + +local function InitIcon() + icon = LibStub("LibDBIcon-1.0"); + icon:Register("AskMrRobot", amrLDB, AmrIconInfo); +end + +function AskMrRobot.AmrUpdateMinimap() + if (AmrOptions.hideMapIcon) then + if (icon) then + icon:Hide("AskMrRobot"); + end + else + if (not icon) then + InitIcon() + end + icon:Show("AskMrRobot"); + end +end + +local function getToolTipText(tip) + return EnumerateTooltipLines_helper(tip:GetRegions()) +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 +end + +function AskMrRobot.GetAmrProfessions() + + 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" + } + + local prof1, prof2, archaeology, fishing, cooking, firstAid = GetProfessions(); + AmrProfessions = {}; + if prof1 then + local name, icon, skillLevel, maxSkillLevel, numAbilities, spelloffset, skillLine, skillModifier = GetProfessionInfo(prof1); + if profMap[skillLine] ~= nil then + AmrProfessions[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; + 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; + 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; + 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; + 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; + end + end +end + +function AskMrRobot.GetRace() + local race, raceEn = UnitRace("player"); + AmrRace = raceEn; + AmrFaction = UnitFactionGroup("player"); +end + +function AskMrRobot.GetLevel() + AmrLevel = UnitLevel("player"); +end + +local SlotNames = { + "HeadSlot", + "NeckSlot", + "ShoulderSlot", + "ShirtSlot", + "ChestSlot", + "WaistSlot", + "LegsSlot", + "FeetSlot", + "WristSlot", + "HandsSlot", + "Finger0Slot", + "Finger1Slot", + "Trinket0Slot", + "Trinket1Slot", + "BackSlot", + "MainHandSlot", + "SecondaryHandSlot", +-- "RangedSlot", + "TabardSlot", +} + +local function GetAmrTalentsForSpec(spec) + 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 + end + + local str = "" + for i = 1, maxTiers do + if talentInfo[i] then + str = str .. talentInfo[i] + else + str = str .. '0' + end + end + + return str +end + +function AskMrRobot.GetAmrTalents() + AmrTalents = {} + for spec = 1, GetNumSpecGroups() do + AmrTalents[spec] = GetAmrTalentsForSpec(spec); + end +end + +local function GetAmrGlyphsForSpec(spec) + local glyphs = {} + for i = 1, NUM_GLYPH_SLOTS do + local _, _, _, glyphSpellID, _, glyphID = GetGlyphSocketInfo(i, spec) + if (glyphID) then + tinsert(glyphs, glyphSpellID) + end + end + return glyphs; +end + +function AskMrRobot.GetAmrGlyphs() + AmrGlyphs = {} + for spec = 1, GetNumSpecGroups() do + AmrGlyphs[spec] = GetAmrGlyphsForSpec(spec) + 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 +]] + +local function toCompressedNumberList(list) + -- ensure the values are numbers, sorted from lowest to highest + local nums = {} + for i, v in ipairs(list) do + table.insert(nums, tonumber(v)) + end + table.sort(nums) + + local ret = {} + local prev = 0 + for i, v in ipairs(nums) do + local diff = v - prev + table.insert(ret, diff) + prev = v + end + + return table.concat(ret, ",") +end + +-- create a more compact but less readable string +function AskMrRobot.ExportToCompressedString(includeInventory) + 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) + + -- guild name + local guildName = GetGuildInfo("player") + if guildName == nil then + table.insert(fields, "") + else + table.insert(fields, guildName) + end + + -- race, default to pandaren if we can't read it for some reason + local raceval = raceIds[AmrRace] + 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] + if raceval == nil then raceval = 1 end + table.insert(fields, raceval) + + table.insert(fields, AmrLevel) + + local profs = {} + local noprofs = true + for k, v in pairs(AmrProfessions) do + local profval = professionIds[k] + if profval ~= nil then + noprofs = false + table.insert(profs, profval .. ":" .. v) + end + end + + if noprofs then + table.insert(profs, "0:0") + end + + 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, "_") + end + + -- convert items to parsed objects, sorted by id + local itemObjects = {} + for k, v in pairs(AmrEquipedItems) do + local itemData = parseItemLink(v) + itemData.slot = k + table.insert(itemObjects, itemData) + end + + -- if desired, include bank/bag items too + if includeInventory then + for i, v in ipairs(AmrBagItems) do + local itemData = parseItemLink(v) + if itemData.itemId ~= nil then + table.insert(itemObjects, itemData) + end + end + for i, v in ipairs(AmrBankItems) do + local itemData = parseItemLink(v) + if itemData.itemId ~= nil then + table.insert(itemObjects, itemData) + 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, "")) + end + + return "$" .. table.concat(fields, ";") .. "$" +end + +function AskMrRobot.ExportToAddonChat(timestamp) + local data = AskMrRobot.ExportToCompressedString(false) + local msgPrefix = timestamp .. "\n" .. AmrRealmName .. "\n" .. AmrCharacterName .. "\n" + + -- break the data into 250 character chunks (to deal with the short limit on addon message size) + local chunks = {} + local i = 1 + local length = string.len(data) + local chunkLen = 249 - string.len(msgPrefix) + while (i <= length) do + local endpos = math.min(i + chunkLen, length) + table.insert(chunks, msgPrefix .. string.sub(data, i, endpos)) + i = endpos + 1 + end + + for i, v in ipairs(chunks) do + SendAddonMessage(AskMrRobot.ChatPrefix, v, "RAID") + end + + -- send a completion message + SendAddonMessage(AskMrRobot.ChatPrefix, msgPrefix .. "done", "RAID") +end + +-- Create an export string that can be copied to the website +function AskMrRobot.ExportToString() + + --[[ + local fields = {} + + 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], ",") + end + + local items = {} + for k, v in pairs(AmrEquipedItems) do + table.insert(items, ItemLinkToExportString(v, k)) + end + for i, v in ipairs(AmrBagItems) do + table.insert(items, ItemLinkToExportString(v, "-1")) + end + for i, v in ipairs(AmrBankItems) do + table.insert(items, ItemLinkToExportString(v, "-1")) + end + fields["items"] = table.concat(items, "_") + + local fieldList = {} + for k, v in pairs(fields) do + table.insert(fieldList, k .. "=" .. v) + end + ]] + + --return table.concat(fieldList, ";") + + return AskMrRobot.ExportToCompressedString(true) + --return AskMrRobot.ExportToAddonChat(time()) +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 +]]