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 _frameShop yellowfive@57: local _panelContent yellowfive@57: local _cboPlayers yellowfive@139: local _selectedSetup yellowfive@81: yellowfive@57: local _specs = { yellowfive@57: [1] = true, yellowfive@81: [2] = true, yellowfive@81: [3] = true, yellowfive@81: [4] = true, yellowfive@57: } yellowfive@81: yellowfive@57: local _isAhOpen = false yellowfive@57: yellowfive@89: local function incrementTableItem(tbl, key, inc) yellowfive@89: tbl[key] = tbl[key] and tbl[key] + inc or inc yellowfive@89: end yellowfive@89: yellowfive@57: local function onShopFrameClose(widget) yellowfive@57: AceGUI:Release(widget) yellowfive@57: _frameShop = nil yellowfive@57: _cboPlayers = nil yellowfive@57: _panelContent = nil yellowfive@57: end yellowfive@57: yellowfive@57: function Amr:HideShopWindow() yellowfive@57: if not _frameShop then return end yellowfive@57: _frameShop:Hide() yellowfive@57: end yellowfive@57: yellowfive@57: local function onPlayerChange(widget, eventName, value) yellowfive@139: _selectedSetup = value yellowfive@57: Amr:RefreshShoppingUi() yellowfive@57: end yellowfive@57: yellowfive@57: local function onSpecClick(widget) yellowfive@57: local spec = widget:GetUserData("spec") yellowfive@57: _specs[spec] = not _specs[spec] yellowfive@57: yellowfive@57: Amr:RefreshShoppingUi() yellowfive@57: end yellowfive@57: yellowfive@57: local function onItemClick(widget) yellowfive@57: local name = widget:GetUserData("itemName") yellowfive@57: if name then yellowfive@177: yellowfive@177: local query = {} yellowfive@177: query.searchString = name yellowfive@177: query.sorts = {} yellowfive@177: C_AuctionHouse.SendBrowseQuery(query) yellowfive@177: yellowfive@177: --QueryAuctionItems(name) yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@124: yellowfive@57: function Amr:ShowShopWindow() yellowfive@124: yellowfive@179: if InCombatLockdown() then return end yellowfive@179: yellowfive@57: if not _frameShop then yellowfive@57: _frameShop = AceGUI:Create("AmrUiFrame") yellowfive@57: _frameShop:SetStatusTable(Amr.db.profile.shopWindow) -- window position is remembered in db yellowfive@57: _frameShop:SetCallback("OnClose", onShopFrameClose) yellowfive@57: _frameShop:SetLayout("None") yellowfive@57: _frameShop:SetWidth(500) yellowfive@57: _frameShop:SetHeight(500) yellowfive@57: _frameShop:SetBorderColor(Amr.Colors.BorderBlue) yellowfive@57: _frameShop:SetBackgroundColor(Amr.Colors.Bg) yellowfive@57: yellowfive@61: if Amr.db.profile.options.uiScale ~= 1 then yellowfive@61: local scale = tonumber(Amr.db.profile.options.uiScale) yellowfive@61: _frameShop:SetScale(scale) yellowfive@61: end yellowfive@61: yellowfive@57: local lbl = AceGUI:Create("AmrUiLabel") yellowfive@124: _frameShop:AddChild(lbl) yellowfive@57: lbl:SetWidth(400) yellowfive@57: lbl:SetFont(Amr.CreateFont("Bold", 28, Amr.Colors.White)) yellowfive@57: lbl:SetText(L.ShopTitle) yellowfive@57: lbl:SetWordWrap(false) yellowfive@57: lbl:SetJustifyH("CENTER") yellowfive@57: lbl:SetPoint("TOP", _frameShop.content, "TOP", 0, 30) yellowfive@57: yellowfive@57: lbl:SetCallback("OnMouseDown", function(widget) _frameShop:StartMove() end) yellowfive@57: lbl:SetCallback("OnMouseUp", function(widget) _frameShop:EndMove() end) yellowfive@57: yellowfive@57: -- player picker yellowfive@57: _cboPlayers = AceGUI:Create("AmrUiDropDown") yellowfive@57: _cboPlayers:SetWidth(400) yellowfive@124: _frameShop:AddChild(_cboPlayers) yellowfive@57: _cboPlayers:SetPoint("TOPLEFT", _frameShop.content, "TOPLEFT", 0, -30) yellowfive@57: yellowfive@57: _panelContent = AceGUI:Create("AmrUiPanel") yellowfive@57: _panelContent:SetLayout("None") yellowfive@57: _panelContent:SetTransparent() yellowfive@124: _frameShop:AddChild(_panelContent) yellowfive@139: _panelContent:SetPoint("TOPLEFT", _cboPlayers.frame, "BOTTOMLEFT", 0, -10) yellowfive@57: _panelContent:SetPoint("BOTTOMRIGHT", _frameShop.content, "BOTTOMRIGHT") yellowfive@57: yellowfive@57: -- update shopping list data yellowfive@57: local player = Amr:ExportCharacter() yellowfive@57: Amr:UpdateShoppingData(player) yellowfive@57: yellowfive@57: -- fill player list yellowfive@57: local playerList = {} yellowfive@139: local firstData = nil yellowfive@139: for name, v in pairs(Amr.db.global.Shopping2) do yellowfive@139: for setupName, data in pairs(v.setups) do yellowfive@139: table.insert(playerList, { text = name .. " " .. setupName, value = name .. "@" .. setupName }) yellowfive@139: if not firstData then yellowfive@139: firstData = name .. "@" .. setupName yellowfive@139: end yellowfive@139: end yellowfive@57: end yellowfive@57: _cboPlayers:SetItems(playerList) yellowfive@57: yellowfive@57: -- set default selected player yellowfive@139: local playerData = Amr.db.global.Shopping2[player.Name .. "-" .. player.Realm] yellowfive@139: if playerData and playerData.setups then yellowfive@139: _selectedSetup = Amr:GetActiveSetupLabel() yellowfive@139: if not _selectedSetup then yellowfive@139: Amr:PickFirstSetupForSpec() yellowfive@139: _selectedSetup = Amr:GetActiveSetupLabel() yellowfive@139: end yellowfive@139: if _selectedSetup and not playerData.setups[_selectedSetup] then yellowfive@139: _selectedSetup = nil yellowfive@139: else yellowfive@139: _selectedSetup = player.Name .. "-" .. player.Realm .. "@" .. _selectedSetup yellowfive@139: end yellowfive@139: end yellowfive@139: yellowfive@139: if not _selectedSetup then yellowfive@139: if playerData and playerData.setups then yellowfive@139: for k,v in pairs(playerData.setups) do yellowfive@139: _selectedSetup = player.Name .. "-" .. player.Realm .. "@" .. k yellowfive@139: break yellowfive@139: end yellowfive@139: else yellowfive@139: _selectedSetup = firstData yellowfive@139: end yellowfive@139: end yellowfive@139: _cboPlayers:SelectItem(_selectedSetup) yellowfive@57: yellowfive@57: Amr:RefreshShoppingUi() yellowfive@57: yellowfive@57: -- set event on dropdown after UI has been initially rendered yellowfive@57: _cboPlayers:SetCallback("OnChange", onPlayerChange) yellowfive@139: yellowfive@139: -- set a timer to refresh a bit after opening b/c sometimes some item info isn't available yellowfive@139: Amr.Wait(2, function() yellowfive@139: if _frameShop then yellowfive@139: Amr:RefreshShoppingUi() yellowfive@139: end yellowfive@139: end) yellowfive@57: else yellowfive@57: _frameShop:Show() yellowfive@57: Amr:RefreshShoppingUi() yellowfive@57: end yellowfive@57: yellowfive@57: _frameShop:Raise() yellowfive@57: end yellowfive@57: yellowfive@57: -- helper to render a section of the shopping list yellowfive@57: local function renderShopSection(list, scroll, header) yellowfive@57: if not list or next(list) == nil then return end yellowfive@57: yellowfive@57: local w = 440 yellowfive@57: yellowfive@57: local panel = AceGUI:Create("AmrUiPanel") yellowfive@57: panel:SetLayout("None") yellowfive@57: panel:SetTransparent() yellowfive@57: panel:SetWidth(w) yellowfive@57: panel:SetHeight(40) yellowfive@57: scroll:AddChild(panel) yellowfive@57: yellowfive@57: local lbl = AceGUI:Create("AmrUiLabel") yellowfive@124: panel:AddChild(lbl) yellowfive@57: lbl:SetWidth(w) yellowfive@57: lbl:SetFont(Amr.CreateFont("Regular", 18, Amr.Colors.TextHeaderActive)) yellowfive@57: lbl:SetText(header) yellowfive@57: lbl:SetPoint("BOTTOMLEFT", panel.content, "BOTTOMLEFT") yellowfive@57: yellowfive@57: for itemId, count in pairs(list) do yellowfive@57: panel = AceGUI:Create("AmrUiPanel") yellowfive@57: panel:SetLayout("None") yellowfive@57: panel:SetTransparent() yellowfive@57: panel:SetWidth(w) yellowfive@57: panel:SetHeight(30) yellowfive@57: scroll:AddChild(panel) yellowfive@57: yellowfive@57: lbl = AceGUI:Create("AmrUiLabel") yellowfive@124: panel:AddChild(lbl) yellowfive@89: lbl:SetWidth(40) yellowfive@57: lbl:SetWordWrap(false) yellowfive@57: lbl:SetFont(Amr.CreateFont("Bold", 20, Amr.Colors.White)) yellowfive@57: lbl:SetText(count .. "x") yellowfive@57: lbl:SetPoint("LEFT", panel.content, "LEFT") yellowfive@57: yellowfive@57: local icon = AceGUI:Create("AmrUiIcon") yellowfive@57: icon:SetBorderWidth(1) yellowfive@57: icon:SetIconBorderColor(Amr.Colors.White) yellowfive@57: icon:SetWidth(18) yellowfive@57: icon:SetHeight(18) yellowfive@124: panel:AddChild(icon) yellowfive@57: icon:SetPoint("LEFT", lbl.frame, "RIGHT", 5, 0) yellowfive@57: yellowfive@57: local btn = AceGUI:Create("AmrUiTextButton") yellowfive@57: btn:SetWidth(w - 30 - 18 - 15) yellowfive@57: btn:SetJustifyH("LEFT") yellowfive@57: btn:SetWordWrap(false) yellowfive@57: btn:SetFont(Amr.CreateFont("Bold", 14, Amr.Colors.White)) yellowfive@57: btn:SetHoverFont(Amr.CreateFont("Bold", 14, Amr.Colors.White)) yellowfive@57: btn:SetCallback("OnClick", onItemClick) yellowfive@57: panel:AddChild(btn) yellowfive@124: btn:SetPoint("LEFT", icon.frame, "RIGHT", 5, 0) yellowfive@57: yellowfive@131: local item = Item:CreateFromItemID(itemId) yellowfive@131: if item then yellowfive@131: local itemLink = item:GetItemLink() yellowfive@131: if itemLink then yellowfive@131: icon:SetIcon(item:GetItemIcon()) yellowfive@131: btn:SetText(itemLink:gsub("%[", ""):gsub("%]", "")) yellowfive@131: btn:SetUserData("itemName", item:GetItemName()) yellowfive@131: Amr:SetItemTooltip(btn, itemLink) yellowfive@131: end yellowfive@131: end yellowfive@57: end yellowfive@57: yellowfive@57: end yellowfive@57: yellowfive@89: -- get the number of a specified gem/enchant/material that the player currently owns yellowfive@57: local function getOwnedCount(itemId) yellowfive@57: local ret = 0 yellowfive@57: yellowfive@57: local list = Amr.db.char.BagItemsAndCounts yellowfive@57: if list and list[itemId] then yellowfive@57: ret = ret + list[itemId] yellowfive@57: end yellowfive@57: yellowfive@124: local bankBags = Amr.db.char.BankItemsAndCounts yellowfive@124: if bankBags then yellowfive@124: for bagId,bagList in pairs(bankBags) do yellowfive@124: if bagList[itemId] then yellowfive@124: ret = ret + bagList[itemId] yellowfive@124: end yellowfive@124: end yellowfive@57: end yellowfive@57: yellowfive@57: return ret yellowfive@57: end yellowfive@57: yellowfive@89: local function removeOwned(list, owned) yellowfive@89: yellowfive@89: for itemId, count in pairs(list) do yellowfive@89: -- load up how many of an item we have yellowfive@89: if not owned.loaded[itemId] then yellowfive@89: owned.counts[itemId] = getOwnedCount(itemId) yellowfive@89: owned.loaded[itemId] = true yellowfive@89: end yellowfive@89: yellowfive@89: -- see how many we can remove from the required count yellowfive@89: local used = math.min(owned.counts[itemId], count) yellowfive@89: yellowfive@89: -- update owned count so we can't double-use something yellowfive@89: owned.counts[itemId] = owned.counts[itemId] - used; yellowfive@89: yellowfive@89: -- reduce the requirement, removing entirely if we have it completely covered yellowfive@89: list[itemId] = list[itemId] - used; yellowfive@89: if list[itemId] == 0 then yellowfive@89: list[itemId] = nil yellowfive@89: end yellowfive@89: end yellowfive@89: end yellowfive@89: yellowfive@89: function Amr:RefreshShoppingUi() yellowfive@89: yellowfive@89: -- clear out any previous data yellowfive@89: _panelContent:ReleaseChildren() yellowfive@89: yellowfive@139: local parts = { strsplit("@", _selectedSetup) } yellowfive@139: yellowfive@139: local data = Amr.db.global.Shopping2[parts[1]].setups[parts[2]] yellowfive@89: if not data then yellowfive@89: _panelContent:SetLayout("None") yellowfive@89: yellowfive@89: local lbl = AceGUI:Create("AmrUiLabel") yellowfive@124: _panelContent:AddChild(lbl) yellowfive@89: lbl:SetFont(Amr.CreateFont("Italic", 18, Amr.Colors.TextTan)) yellowfive@89: lbl:SetText(L.ShopEmpty) yellowfive@89: lbl:SetJustifyH("CENTER") yellowfive@89: lbl:SetPoint("TOP", _panelContent.content, "TOP", 0, -30) yellowfive@89: else yellowfive@89: local allStuff = { gems = {}, enchants = {}, materials = {} } yellowfive@89: local hasStuff = false yellowfive@89: local visited = {} yellowfive@89: yellowfive@139: for inventoryId, stuff in pairs(data) do yellowfive@139: hasStuff = true yellowfive@139: if not visited[inventoryId] then yellowfive@139: if stuff.gems then yellowfive@139: for itemId, count in pairs(stuff.gems) do yellowfive@139: incrementTableItem(allStuff.gems, itemId, count) yellowfive@89: end yellowfive@89: end yellowfive@89: yellowfive@139: if stuff.enchants then yellowfive@139: for itemId, count in pairs(stuff.enchants) do yellowfive@139: incrementTableItem(allStuff.enchants, itemId, count) yellowfive@139: end yellowfive@139: end yellowfive@139: yellowfive@139: if stuff.materials then yellowfive@139: for itemId, count in pairs(stuff.materials) do yellowfive@139: incrementTableItem(allStuff.materials, itemId, count) yellowfive@139: end yellowfive@139: end yellowfive@139: yellowfive@139: -- make sure not to count the same physical item twice yellowfive@139: if inventoryId ~= -1 then yellowfive@139: visited[inventoryId] = true yellowfive@139: end yellowfive@89: end yellowfive@89: end yellowfive@89: yellowfive@89: if hasStuff then yellowfive@89: -- remove what we already own yellowfive@89: local owned = { counts = {}, loaded = {} } yellowfive@89: removeOwned(allStuff.gems, owned) yellowfive@89: removeOwned(allStuff.enchants, owned) yellowfive@89: removeOwned(allStuff.materials, owned) yellowfive@89: yellowfive@89: _panelContent:SetLayout("Fill") yellowfive@89: yellowfive@89: local scroll = AceGUI:Create("AmrUiScrollFrame") yellowfive@89: scroll:SetLayout("List") yellowfive@89: _panelContent:AddChild(scroll) yellowfive@89: yellowfive@89: renderShopSection(allStuff.gems, scroll, L.ShopHeaderGems) yellowfive@89: renderShopSection(allStuff.enchants, scroll, L.ShopHeaderEnchants) yellowfive@89: renderShopSection(allStuff.materials, scroll, L.ShopHeaderMaterials) yellowfive@89: end yellowfive@89: end yellowfive@89: yellowfive@89: end yellowfive@89: yellowfive@124: -- compare gear to everything the player owns, and return the minimum gems/enchants/materials needed to optimize, yellowfive@124: -- grouped by inventory ID so that we can combine multiple specs without double-counting yellowfive@131: local function getShoppingData(player, gear) yellowfive@89: yellowfive@89: local ret = {} yellowfive@89: yellowfive@89: -- used to prevent considering the same item twice yellowfive@89: local usedItems = {} yellowfive@89: yellowfive@89: for slotId, optimalItem in pairs(gear) do yellowfive@124: local matchItem = Amr:FindMatchingItem(optimalItem, player, usedItems) yellowfive@89: local inventoryId = optimalItem.inventoryId or -1 yellowfive@89: yellowfive@89: -- find gem/enchant differences on the best-matching item yellowfive@89: yellowfive@124: -- gems yellowfive@124: if not optimalItem.relicBonusIds and (not matchItem or not matchItem.relicBonusIds) then yellowfive@124: for i = 1, 3 do yellowfive@89: local g = optimalItem.gemIds[i] yellowfive@131: local isGemEquipped = g == 0 or (matchItem and matchItem.gemIds and matchItem.gemIds[i] == g) yellowfive@89: if not isGemEquipped then yellowfive@89: if not ret[inventoryId] then yellowfive@89: ret[inventoryId] = { gems = {}, enchants = {}, materials = {} } yellowfive@89: end yellowfive@89: incrementTableItem(ret[inventoryId].gems, g, 1) yellowfive@89: end yellowfive@89: end yellowfive@89: end yellowfive@89: yellowfive@89: -- enchant yellowfive@89: if optimalItem.enchantId and optimalItem.enchantId ~= 0 then yellowfive@89: local e = optimalItem.enchantId yellowfive@89: local isEnchantEquipped = matchItem and matchItem.enchantId and matchItem.enchantId == e yellowfive@89: yellowfive@89: if not isEnchantEquipped then yellowfive@124: -- enchant info yellowfive@124: local enchInfo = Amr.db.char.ExtraEnchantData[e] yellowfive@89: if enchInfo then yellowfive@89: if not ret[inventoryId] then yellowfive@89: ret[inventoryId] = { gems = {}, enchants = {}, materials = {} } yellowfive@89: end yellowfive@89: incrementTableItem(ret[inventoryId].enchants, enchInfo.itemId, 1) yellowfive@89: yellowfive@89: if enchInfo.materials then yellowfive@89: for k, v in pairs(enchInfo.materials) do yellowfive@89: incrementTableItem(ret[inventoryId].materials, k, v) yellowfive@89: end yellowfive@89: end yellowfive@89: end yellowfive@89: end yellowfive@89: end yellowfive@89: end yellowfive@89: yellowfive@89: return ret yellowfive@89: end yellowfive@89: yellowfive@139: -- look at all gear sets and find stuff that a player needs to acquire to gem/enchant their gear for each setup yellowfive@57: function Amr:UpdateShoppingData(player) yellowfive@57: yellowfive@57: local required = { yellowfive@139: setups = {} yellowfive@57: } yellowfive@139: yellowfive@139: for i, setup in ipairs(Amr.db.char.GearSetups) do yellowfive@139: local gear = setup.Gear yellowfive@124: if gear then yellowfive@139: required.setups[setup.Label] = getShoppingData(player, gear) yellowfive@124: end yellowfive@57: end yellowfive@139: yellowfive@139: Amr.db.global.Shopping2[player.Name .. "-" .. player.Realm] = required yellowfive@57: end yellowfive@57: yellowfive@57: Amr:AddEventHandler("AUCTION_HOUSE_SHOW", function() yellowfive@57: _isAhOpen = true yellowfive@57: if Amr.db.profile.options.shopAh then yellowfive@57: Amr:ShowShopWindow() yellowfive@57: end yellowfive@57: end) yellowfive@57: yellowfive@57: Amr:AddEventHandler("AUCTION_HOUSE_CLOSED", function() yellowfive@57: _isAhOpen = false yellowfive@57: if Amr.db.profile.options.shopAh then yellowfive@57: Amr:HideShopWindow() yellowfive@57: end yellowfive@122: end)