yellowfive@57: local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot") yellowfive@57: local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true) yellowfive@57: local AceGUI = LibStub("AceGUI-3.0") yellowfive@57: yellowfive@57: local _lastExport = nil yellowfive@57: local _txt = nil yellowfive@57: yellowfive@57: local function createLabel(container, text, width) yellowfive@57: local lbl = AceGUI:Create("AmrUiLabel") yellowfive@124: container:AddChild(lbl) yellowfive@57: lbl:SetWidth(width or 800) yellowfive@57: lbl:SetText(text) yellowfive@57: lbl:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.Text)) yellowfive@57: return lbl yellowfive@57: end yellowfive@57: yellowfive@57: local function onSplashClose() yellowfive@57: Amr:HideCover() yellowfive@57: Amr.db.char.FirstUse = false yellowfive@57: end yellowfive@57: yellowfive@69: local function onTextChanged(widget) yellowfive@69: local val = _txt:GetText() yellowfive@69: if val == "overwolf-bib" then yellowfive@69: -- go to the gear tab, open import window, and show a cover yellowfive@69: Amr:ShowTab("Gear") yellowfive@69: Amr:ShowImportWindow(true) yellowfive@69: end yellowfive@69: end yellowfive@69: yellowfive@57: -- render a splash screen with first-time help yellowfive@57: local function renderSplash(container) yellowfive@57: local panel = Amr:RenderCoverChrome(container, 700, 450) yellowfive@57: yellowfive@57: local lbl = createLabel(panel, L.ExportSplashTitle, 650) yellowfive@57: lbl:SetJustifyH("CENTER") yellowfive@57: lbl:SetFont(Amr.CreateFont("Bold", 24, Amr.Colors.TextHeaderActive)) yellowfive@57: lbl:SetPoint("TOP", panel.content, "TOP", 0, -10) yellowfive@57: yellowfive@57: local lbl2 = createLabel(panel, L.ExportSplashSubtitle, 650) yellowfive@57: lbl2:SetJustifyH("CENTER") yellowfive@57: lbl2:SetFont(Amr.CreateFont("Bold", 18, Amr.Colors.TextTan)) yellowfive@57: lbl2:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -20) yellowfive@57: yellowfive@57: lbl = createLabel(panel, L.ExportSplash1, 650) yellowfive@57: lbl:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.Text)) yellowfive@57: lbl:SetPoint("TOPLEFT", lbl2.frame, "BOTTOMLEFT", 0, -70) yellowfive@57: yellowfive@57: lbl2 = createLabel(panel, L.ExportSplash2, 650) yellowfive@57: lbl2:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.Text)) yellowfive@57: lbl2:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 0, -15) yellowfive@57: yellowfive@57: local btn = AceGUI:Create("AmrUiButton") yellowfive@57: btn:SetText(L.ExportSplashClose) yellowfive@57: btn:SetBackgroundColor(Amr.Colors.Green) yellowfive@57: btn:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White)) yellowfive@57: btn:SetWidth(120) yellowfive@57: btn:SetHeight(28) yellowfive@57: btn:SetCallback("OnClick", onSplashClose) yellowfive@57: panel:AddChild(btn) yellowfive@124: btn:SetPoint("BOTTOM", panel.content, "BOTTOM", 0, 20) yellowfive@57: end yellowfive@57: yellowfive@57: -- renders the main UI for the Export tab yellowfive@57: function Amr:RenderTabExport(container) yellowfive@57: yellowfive@57: local lbl = createLabel(container, L.ExportTitle) yellowfive@57: lbl:SetFont(Amr.CreateFont("Bold", 24, Amr.Colors.TextHeaderActive)) yellowfive@57: lbl:SetPoint("TOPLEFT", container.content, "TOPLEFT", 0, -40) yellowfive@57: yellowfive@57: local lbl2 = createLabel(container, L.ExportHelp1) yellowfive@57: lbl2:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 0, -10) yellowfive@57: yellowfive@57: lbl = createLabel(container, L.ExportHelp2) yellowfive@57: lbl:SetPoint("TOPLEFT", lbl2.frame, "BOTTOMLEFT", 0, -10) yellowfive@57: yellowfive@57: lbl2 = createLabel(container, L.ExportHelp3) yellowfive@57: lbl2:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 0, -10) yellowfive@57: yellowfive@57: _txt = AceGUI:Create("AmrUiTextarea") yellowfive@57: _txt:SetWidth(800) yellowfive@57: _txt:SetHeight(300) yellowfive@57: _txt:SetFont(Amr.CreateFont("Regular", 12, Amr.Colors.Text)) yellowfive@69: _txt:SetCallback("OnTextChanged", onTextChanged) yellowfive@57: container:AddChild(_txt) yellowfive@124: _txt:SetPoint("TOP", lbl2.frame, "BOTTOM", 0, -20) yellowfive@57: yellowfive@57: local data = self:ExportCharacter() yellowfive@57: local txt = Amr.Serializer:SerializePlayerData(data, true) yellowfive@57: _txt:SetText(txt) yellowfive@57: _txt:SetFocus(true) yellowfive@57: yellowfive@57: -- update shopping list data yellowfive@57: Amr:UpdateShoppingData(data) yellowfive@57: yellowfive@57: -- show help splash if first time a user is using this yellowfive@57: if Amr.db.char.FirstUse then yellowfive@57: Amr:ShowCover(renderSplash) yellowfive@57: AceGUI:ClearFocus() yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: function Amr:ReleaseTabExport() yellowfive@57: end yellowfive@57: yellowfive@57: function Amr:GetExportText() yellowfive@57: return _txt:GetText() yellowfive@57: end yellowfive@57: yellowfive@57: yellowfive@57: -- use some local variables to deal with the fact that a user can close the bank before a scan completes yellowfive@57: local _lastBankBagId = nil yellowfive@57: local _lastBankSlotId = nil yellowfive@124: local _bankOpen = false yellowfive@57: yellowfive@57: local function scanBag(bagId, isBank, bagTable, bagItemsWithCount) yellowfive@57: local numSlots = GetContainerNumSlots(bagId) yellowfive@124: local loc = ItemLocation.CreateEmpty() yellowfive@57: for slotId = 1, numSlots do yellowfive@57: local _, itemCount, _, _, _, _, itemLink = GetContainerItemInfo(bagId, slotId) yellowfive@57: if itemLink ~= nil then yellowfive@57: local itemData = Amr.Serializer.ParseItemLink(itemLink) yellowfive@57: if itemData ~= nil then yellowfive@124: yellowfive@124: -- see if this is an azerite item and read azerite power ids yellowfive@185: --[[loc:SetBagAndSlot(bagId, slotId) yellowfive@124: if C_AzeriteEmpoweredItem.IsAzeriteEmpoweredItem(loc) then yellowfive@124: local powers = Amr.ReadAzeritePowers(loc) yellowfive@124: if powers then yellowfive@124: itemData.azerite = powers yellowfive@124: end yellowfive@185: end]] yellowfive@124: yellowfive@124: if isBank then yellowfive@124: _lastBankBagId = bagId yellowfive@124: _lastBankSlotId = slotId yellowfive@124: end yellowfive@57: yellowfive@124: table.insert(bagTable, itemData) yellowfive@57: yellowfive@57: -- all items and counts, used for e.g. shopping list and reagents, etc. yellowfive@57: if bagItemsWithCount then yellowfive@57: if bagItemsWithCount[itemData.id] then yellowfive@57: bagItemsWithCount[itemData.id] = bagItemsWithCount[itemData.id] + itemCount yellowfive@57: else yellowfive@57: bagItemsWithCount[itemData.id] = itemCount yellowfive@57: end yellowfive@57: end yellowfive@57: end yellowfive@57: end yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@124: -- cache the currently equipped gear for this spec yellowfive@124: local function cacheEquipped() yellowfive@124: local data = Amr.Serializer:GetEquipped() yellowfive@57: yellowfive@124: local spec = GetSpecialization() yellowfive@57: Amr.db.char.Equipped[spec] = data.Equipped[spec] yellowfive@57: end yellowfive@57: yellowfive@57: local function scanBags() yellowfive@57: yellowfive@57: local bagItems = {} yellowfive@57: local itemsAndCounts = {} yellowfive@57: yellowfive@57: scanBag(BACKPACK_CONTAINER, false, bagItems, itemsAndCounts) -- backpack yellowfive@57: for bagId = 1, NUM_BAG_SLOTS do yellowfive@57: scanBag(bagId, false, bagItems, itemsAndCounts) yellowfive@57: end yellowfive@57: yellowfive@57: Amr.db.char.BagItems = bagItems yellowfive@57: Amr.db.char.BagItemsAndCounts = itemsAndCounts yellowfive@57: end yellowfive@57: yellowfive@57: -- scan the player's bank and save the contents, must be at the bank yellowfive@57: local function scanBank() yellowfive@57: yellowfive@57: local bankItems = {} yellowfive@57: local itemsAndCounts = {} yellowfive@127: yellowfive@127: local bagList = {} yellowfive@127: table.insert(bagList, BANK_CONTAINER) yellowfive@127: table.insert(bagList, REAGENTBANK_CONTAINER) yellowfive@127: for bagId = NUM_BAG_SLOTS + 1, NUM_BAG_SLOTS + NUM_BANKBAGSLOTS do yellowfive@127: table.insert(bagList, bagId) yellowfive@127: end yellowfive@57: yellowfive@127: for i,bagId in ipairs(bagList) do yellowfive@124: local bagItems = {} yellowfive@124: local bagItemsAndCounts = {} yellowfive@124: scanBag(bagId, true, bagItems, bagItemsAndCounts) yellowfive@124: yellowfive@124: bankItems[bagId] = bagItems yellowfive@124: itemsAndCounts[bagId] = bagItemsAndCounts yellowfive@57: end yellowfive@57: yellowfive@57: -- see if the scan completed before the window closed, otherwise we don't overwrite with partial data yellowfive@124: if _bankOpen and _lastBankBagId then yellowfive@57: local itemLink = GetContainerItemLink(_lastBankBagId, _lastBankSlotId) yellowfive@124: if itemLink then --still open yellowfive@57: Amr.db.char.BankItems = bankItems yellowfive@57: Amr.db.char.BankItemsAndCounts = itemsAndCounts yellowfive@57: end yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@124: local function onBankOpened() yellowfive@124: _bankOpen = true yellowfive@124: scanBank() yellowfive@124: end yellowfive@124: yellowfive@124: local function onBankClosed() yellowfive@124: _bankOpen = false yellowfive@124: end yellowfive@124: yellowfive@124: -- if a bank bag is updated while the bank is open, re-scan that bag yellowfive@124: local function onBankUpdated(bagID) yellowfive@124: if _bankOpen and (bagID == BANK_CONTAINER or bagID == REAGENTBANK_CONTAINER or (bagID >= NUM_BAG_SLOTS + 1 and bagID <= NUM_BAG_SLOTS + NUM_BANKBAGSLOTS)) then yellowfive@124: local bagItems = {} yellowfive@124: local bagItemsAndCounts = {} yellowfive@124: scanBag(bagID, true, bagItems, bagItemsAndCounts) yellowfive@124: yellowfive@124: -- see if the scan completed before the window closed, otherwise we don't overwrite with partial data yellowfive@124: if _bankOpen and _lastBankBagId == bagID then yellowfive@124: local itemLink = GetContainerItemLink(_lastBankBagId, _lastBankSlotId) yellowfive@124: if itemLink then yellowfive@124: Amr.db.char.BankItems[bagID] = bagItems yellowfive@124: Amr.db.char.BankItemsAndCounts[bagID] = bagItemsAndCounts yellowfive@124: end yellowfive@124: end yellowfive@124: end yellowfive@124: end yellowfive@124: yellowfive@124: --[[ yellowfive@57: -- scan the player's void storage and save the contents, must be at void storage yellowfive@57: local function scanVoid() yellowfive@57: yellowfive@57: if IsVoidStorageReady() then yellowfive@57: local voidItems = {} yellowfive@57: local VOID_STORAGE_MAX = 80 yellowfive@57: local VOID_STORAGE_PAGES = 2 yellowfive@57: yellowfive@57: for page = 1,VOID_STORAGE_PAGES do yellowfive@57: for i = 1,VOID_STORAGE_MAX do yellowfive@57: local itemId = GetVoidItemInfo(page, i) yellowfive@57: if itemId then yellowfive@57: local itemLink = GetVoidItemHyperlinkString(((page - 1) * VOID_STORAGE_MAX) + i); yellowfive@57: if itemLink then yellowfive@57: tinsert(voidItems, itemLink) yellowfive@57: end yellowfive@57: end yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: Amr.db.char.VoidItems = voidItems yellowfive@57: end yellowfive@57: yellowfive@57: end yellowfive@124: ]] yellowfive@57: yellowfive@185: local function scanSoulbinds() yellowfive@185: if not C_Soulbinds then return end yellowfive@185: yellowfive@185: -- read which conduits this player has unlocked yellowfive@185: Amr.db.char.UnlockedConduits = {} yellowfive@185: yellowfive@185: for t = 0,2 do yellowfive@185: local conduits = C_Soulbinds.GetConduitCollection(t) yellowfive@185: for i, conduit in ipairs(conduits) do yellowfive@185: table.insert(Amr.db.char.UnlockedConduits, { conduit.conduitID, conduit.conduitRank }) yellowfive@185: end yellowfive@185: end yellowfive@185: yellowfive@185: if not Amr.db.char.ActiveSoulbinds then yellowfive@185: Amr.db.char.ActiveSoulbinds = {} yellowfive@185: end yellowfive@185: yellowfive@185: -- read the currently active soulbind for this spec yellowfive@185: local specPos = GetSpecialization() yellowfive@185: if specPos and specPos >= 1 and specPos <= 4 then yellowfive@185: Amr.db.char.ActiveSoulbinds[specPos] = C_Soulbinds.GetActiveSoulbindID() or 0 yellowfive@185: end yellowfive@185: yellowfive@185: -- update soulbind tree info for all soulbinds yellowfive@185: Amr.db.char.Soulbinds = {} yellowfive@185: yellowfive@185: local covenantData = C_Covenants.GetCovenantData(C_Covenants.GetActiveCovenantID()) yellowfive@185: yellowfive@185: if covenantData and covenantData.soulbindIDs then yellowfive@185: for i, soulbindId in ipairs(covenantData.soulbindIDs) do yellowfive@185: local soulbindData = soulbindId and C_Soulbinds.GetSoulbindData(soulbindId) yellowfive@185: local nodes = {} yellowfive@185: local unlockedTier = 0 yellowfive@185: yellowfive@185: if soulbindData and soulbindData.tree and soulbindData.tree.nodes then yellowfive@185: for i, node in ipairs(soulbindData.tree.nodes) do yellowfive@185: if node.state == 3 then yellowfive@185: nodes[node.row] = { soulbindId, node.row, node.column, node.conduitID, node.conduitRank } yellowfive@185: end yellowfive@185: if node.state > 0 then yellowfive@185: unlockedTier = math.max(node.row, unlockedTier) yellowfive@185: end yellowfive@185: end yellowfive@185: end yellowfive@185: yellowfive@185: Amr.db.char.Soulbinds[soulbindId] = { yellowfive@185: UnlockedTier = unlockedTier, yellowfive@185: Nodes = nodes yellowfive@185: } yellowfive@185: yellowfive@185: end yellowfive@185: end yellowfive@185: yellowfive@185: end yellowfive@185: yellowfive@185: --[[ yellowfive@165: local function scanEssences() yellowfive@165: if not C_AzeriteEssence then return end yellowfive@165: yellowfive@165: -- read which essences this player has unlocked yellowfive@165: Amr.db.char.UnlockedEssences = {} yellowfive@165: yellowfive@165: local essences = C_AzeriteEssence.GetEssences() yellowfive@165: if essences then yellowfive@165: for i, essence in ipairs(essences) do yellowfive@165: if essence.unlocked then yellowfive@165: table.insert(Amr.db.char.UnlockedEssences, { essence.ID, essence.rank }) yellowfive@165: end yellowfive@165: end yellowfive@165: end yellowfive@165: yellowfive@165: local specPos = GetSpecialization() yellowfive@165: if not specPos or specPos < 1 or specPos > 4 then return end yellowfive@165: yellowfive@165: if not Amr.db.char.Essences then yellowfive@165: Amr.db.char.Essences = {} yellowfive@165: end yellowfive@165: yellowfive@165: Amr.db.char.Essences[specPos] = {} yellowfive@165: local active = Amr.db.char.Essences[specPos] yellowfive@165: yellowfive@165: local milestones = C_AzeriteEssence.GetMilestones() yellowfive@165: if milestones then yellowfive@165: for i, milestone in ipairs(milestones) do yellowfive@165: -- if no slot, it corresponds to the stamina nodes, skip those yellowfive@165: if milestone.slot ~= nil then yellowfive@165: if milestone.unlocked then yellowfive@165: local essenceId = C_AzeriteEssence.GetMilestoneEssence(milestone.ID) yellowfive@165: if essenceId then yellowfive@165: local essence = C_AzeriteEssence.GetEssenceInfo(essenceId) yellowfive@165: table.insert(active, { milestone.slot, essence.ID, essence.rank }) yellowfive@165: end yellowfive@165: end yellowfive@165: end yellowfive@165: end yellowfive@165: end yellowfive@165: end yellowfive@185: ]] yellowfive@165: yellowfive@81: local function scanTalents() yellowfive@81: local specPos = GetSpecialization() yellowfive@81: if not specPos or specPos < 1 or specPos > 4 then return end yellowfive@81: yellowfive@81: local talentInfo = {} yellowfive@81: local maxTiers = 7 yellowfive@81: for tier = 1, maxTiers do yellowfive@81: for col = 1, 3 do yellowfive@81: local id, name, _, _, _, spellId, _, t, c, selected = GetTalentInfoBySpecialization(specPos, tier, col) yellowfive@81: if selected then yellowfive@81: talentInfo[tier] = col yellowfive@81: end yellowfive@81: end yellowfive@81: end yellowfive@81: yellowfive@81: local str = "" yellowfive@81: for i = 1, maxTiers do yellowfive@81: if talentInfo[i] then yellowfive@81: str = str .. talentInfo[i] yellowfive@81: else yellowfive@81: str = str .. '0' yellowfive@81: end yellowfive@81: end yellowfive@81: yellowfive@81: Amr.db.char.Talents[specPos] = str yellowfive@81: end yellowfive@81: yellowfive@57: -- Returns a data object containing all information about the current player needed for an export: yellowfive@57: -- gear, spec, reputations, bag, bank, and void storage items. yellowfive@57: function Amr:ExportCharacter() yellowfive@57: yellowfive@124: -- get all necessary player data yellowfive@124: local data = Amr.Serializer:GetPlayerData() yellowfive@124: yellowfive@124: -- cache latest-seen equipped gear for current spec yellowfive@124: local spec = GetSpecialization() yellowfive@124: Amr.db.char.Equipped[spec] = data.Equipped[spec] yellowfive@124: yellowfive@124: -- scan current inventory just before export so that it is always fresh yellowfive@57: scanBags() yellowfive@57: yellowfive@81: -- scan current spec's talents just before exporting yellowfive@81: scanTalents() yellowfive@165: yellowfive@185: -- scan all soulbinds just before exporting yellowfive@185: scanSoulbinds() yellowfive@185: yellowfive@165: -- scan current spec's essences just before exporting yellowfive@185: --scanEssences() yellowfive@81: yellowfive@124: data.Talents = Amr.db.char.Talents yellowfive@185: data.UnlockedConduits = Amr.db.char.UnlockedConduits yellowfive@185: data.ActiveSoulbinds = Amr.db.char.ActiveSoulbinds yellowfive@185: data.Soulbinds = Amr.db.char.Soulbinds yellowfive@185: --data.UnlockedEssences = Amr.db.char.UnlockedEssences yellowfive@185: --data.Essences = Amr.db.char.Essences yellowfive@124: data.Equipped = Amr.db.char.Equipped yellowfive@57: data.BagItems = Amr.db.char.BagItems yellowfive@124: yellowfive@124: -- flatten bank data (which is stored by bag for more efficient updating) yellowfive@124: data.BankItems = {} yellowfive@124: for k,v in pairs(Amr.db.char.BankItems) do yellowfive@124: for i,v2 in ipairs(v) do yellowfive@124: table.insert(data.BankItems, v2) yellowfive@124: end yellowfive@124: end yellowfive@124: yellowfive@124: --data.VoidItems = Amr.db.char.VoidItems yellowfive@57: yellowfive@57: return data yellowfive@57: end yellowfive@57: yellowfive@57: function Amr:InitializeExport() yellowfive@57: Amr:AddEventHandler("UNIT_INVENTORY_CHANGED", function(unitID) yellowfive@57: if unitID and unitID ~= "player" then return end yellowfive@124: cacheEquipped() yellowfive@57: end) yellowfive@57: end yellowfive@57: yellowfive@124: Amr:AddEventHandler("BANKFRAME_OPENED", onBankOpened) yellowfive@124: Amr:AddEventHandler("BANKFRAME_CLOSED", onBankClosed) yellowfive@124: Amr:AddEventHandler("BAG_UPDATE", onBankUpdated) yellowfive@57: yellowfive@124: --Amr:AddEventHandler("VOID_STORAGE_OPEN", scanVoid) yellowfive@124: --Amr:AddEventHandler("VOID_STORAGE_CONTENTS_UPDATE", scanVoid) yellowfive@124: --Amr:AddEventHandler("VOID_STORAGE_DEPOSIT_UPDATE", scanVoid) yellowfive@124: --Amr:AddEventHandler("VOID_STORAGE_UPDATE", scanVoid) yellowfive@81: yellowfive@81: Amr:AddEventHandler("PLAYER_TALENT_UPDATE", scanTalents) yellowfive@165: yellowfive@185: --if C_AzeriteEssence then yellowfive@185: -- Amr:AddEventHandler("AZERITE_ESSENCE_UPDATE", scanEssences) yellowfive@185: --end yellowfive@185: yellowfive@185: if C_Soulbinds then yellowfive@185: Amr:AddEventHandler("SOULBIND_ACTIVATED", scanSoulbinds) yellowfive@165: end