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@57: local _selectedPlayer 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 _chk1 yellowfive@57: local _chk2 yellowfive@81: local _chk3 yellowfive@81: local _chk4 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: _chk1 = nil yellowfive@57: _chk2 = nil yellowfive@81: _chk3 = nil yellowfive@81: _chk4 = 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@57: _selectedPlayer = 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@57: QueryAuctionItems(name) yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: function Amr:ShowShopWindow() 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@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: _frameShop:AddChild(lbl) 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@57: _cboPlayers:SetPoint("TOPLEFT", _frameShop.content, "TOPLEFT", 0, -30) yellowfive@57: _frameShop:AddChild(_cboPlayers) yellowfive@57: yellowfive@57: -- spec pickers yellowfive@57: _chk1 = AceGUI:Create("AmrUiCheckBox") yellowfive@57: _chk1:SetPoint("TOPLEFT", _cboPlayers.frame, "BOTTOMLEFT", 0, -20) yellowfive@57: _chk1:SetUserData("spec", 1) yellowfive@57: _chk1:SetCallback("OnClick", onSpecClick) yellowfive@57: _frameShop:AddChild(_chk1) yellowfive@57: yellowfive@57: _chk2 = AceGUI:Create("AmrUiCheckBox") yellowfive@57: _chk2:SetPoint("LEFT", _chk1.frame, "RIGHT", 30, 0) yellowfive@57: _chk2:SetUserData("spec", 2) yellowfive@57: _chk2:SetCallback("OnClick", onSpecClick) yellowfive@57: _frameShop:AddChild(_chk2) yellowfive@57: yellowfive@81: _chk3 = AceGUI:Create("AmrUiCheckBox") yellowfive@81: _chk3:SetPoint("LEFT", _chk2.frame, "RIGHT", 30, 0) yellowfive@81: _chk3:SetUserData("spec", 3) yellowfive@81: _chk3:SetCallback("OnClick", onSpecClick) yellowfive@81: _frameShop:AddChild(_chk3) yellowfive@81: yellowfive@81: _chk4 = AceGUI:Create("AmrUiCheckBox") yellowfive@81: _chk4:SetPoint("LEFT", _chk3.frame, "RIGHT", 30, 0) yellowfive@81: _chk4:SetUserData("spec", 4) yellowfive@81: _chk4:SetCallback("OnClick", onSpecClick) yellowfive@81: _frameShop:AddChild(_chk4) yellowfive@81: yellowfive@57: _panelContent = AceGUI:Create("AmrUiPanel") yellowfive@57: _panelContent:SetLayout("None") yellowfive@57: _panelContent:SetTransparent() yellowfive@57: _panelContent:SetPoint("TOPLEFT", _chk1.frame, "BOTTOMLEFT", 0, -10) yellowfive@57: _panelContent:SetPoint("BOTTOMRIGHT", _frameShop.content, "BOTTOMRIGHT") yellowfive@57: _frameShop:AddChild(_panelContent) 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@57: for name, data in pairs(Amr.db.global.Shopping) do yellowfive@57: table.insert(playerList, { text = name, value = name }) yellowfive@57: end yellowfive@57: _cboPlayers:SetItems(playerList) yellowfive@57: yellowfive@57: -- set default selected player yellowfive@57: if not _selectedPlayer then yellowfive@57: _selectedPlayer = player.Name .. "-" .. player.Realm yellowfive@57: end yellowfive@57: _cboPlayers:SelectItem(_selectedPlayer) 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@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@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: panel:AddChild(lbl) 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@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: panel:AddChild(lbl) 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@57: icon:SetPoint("LEFT", lbl.frame, "RIGHT", 5, 0) yellowfive@57: panel:AddChild(icon) 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:SetPoint("LEFT", icon.frame, "RIGHT", 5, 0) yellowfive@57: btn:SetCallback("OnClick", onItemClick) yellowfive@57: panel:AddChild(btn) yellowfive@57: yellowfive@57: Amr.GetItemInfo(itemId, function(obj, name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture) yellowfive@57: -- set icon, name, and a tooltip yellowfive@57: obj.itemIcon:SetIcon(texture) yellowfive@57: obj.itemText:SetText(link:gsub("%[", ""):gsub("%]", "")) yellowfive@57: obj.itemText:SetUserData("itemName", name) yellowfive@57: Amr:SetItemTooltip(obj.itemText, link) yellowfive@57: end, { itemIcon = icon, itemText = btn }) 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@57: list = Amr.db.char.BankItemsAndCounts yellowfive@57: if list and list[itemId] then yellowfive@57: ret = ret + list[itemId] 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: local posToCheck = { _chk1, _chk2, _chk3, _chk4 } yellowfive@89: local chk yellowfive@89: yellowfive@89: -- reset spec checkboxes yellowfive@89: for specPos = 1,4 do yellowfive@89: chk = posToCheck[specPos] yellowfive@89: chk:SetVisible(false) yellowfive@89: chk:SetChecked(false) yellowfive@89: end yellowfive@89: yellowfive@89: -- clear out any previous data yellowfive@89: _panelContent:ReleaseChildren() yellowfive@89: yellowfive@89: local data = Amr.db.global.Shopping[_selectedPlayer] yellowfive@89: if not data then yellowfive@89: _panelContent:SetLayout("None") yellowfive@89: yellowfive@89: local lbl = AceGUI:Create("AmrUiLabel") 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: _panelContent:AddChild(lbl) yellowfive@89: else yellowfive@89: local allStuff = { gems = {}, enchants = {}, materials = {} } yellowfive@89: local hasStuff = false yellowfive@89: local visited = {} yellowfive@89: yellowfive@89: for specPos = 1,4 do yellowfive@89: -- set labels on checkboxes yellowfive@89: if data.specs[specPos] and data.specs[specPos] ~= 0 then yellowfive@89: chk = posToCheck[specPos] yellowfive@89: chk:SetText(L.SpecsShort[data.specs[specPos]]) yellowfive@89: chk:SetVisible(true) yellowfive@89: chk:SetChecked(_specs[specPos]) yellowfive@89: yellowfive@89: -- gather up all stuff for checked specs yellowfive@89: if _specs[specPos] then yellowfive@89: hasStuff = true yellowfive@89: yellowfive@89: for inventoryId, stuff in pairs(data.stuff[specPos]) do yellowfive@89: if not visited[inventoryId] then yellowfive@89: if stuff.gems then yellowfive@89: for itemId, count in pairs(stuff.gems) do yellowfive@89: incrementTableItem(allStuff.gems, itemId, count) yellowfive@89: end yellowfive@89: end yellowfive@89: yellowfive@89: if stuff.enchants then yellowfive@89: for itemId, count in pairs(stuff.enchants) do yellowfive@89: incrementTableItem(allStuff.enchants, itemId, count) yellowfive@89: end yellowfive@89: end yellowfive@89: yellowfive@89: if stuff.materials then yellowfive@89: for itemId, count in pairs(stuff.materials) do yellowfive@89: incrementTableItem(allStuff.materials, itemId, count) yellowfive@89: end yellowfive@89: end yellowfive@89: yellowfive@89: -- make sure not to count the same physical item twice yellowfive@89: if inventoryId ~= -1 then yellowfive@89: visited[inventoryId] = true yellowfive@89: end yellowfive@89: end yellowfive@89: end yellowfive@89: end yellowfive@89: yellowfive@89: end yellowfive@89: 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@89: -- compare gear to everything the player owns, and return the minimum gems/enchants/materials needed to optimize, grouped by inventory ID so that we can combine multiple specs without double-counting yellowfive@89: local function getShoppingData(player, gear, spec) 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@89: local matchItemLink, matchItem = Amr:FindMatchingItem(optimalItem, player, usedItems) yellowfive@89: local itemInfo = Amr.db.char.ExtraItemData[spec][optimalItem.id] yellowfive@89: local inventoryId = optimalItem.inventoryId or -1 yellowfive@89: yellowfive@89: -- find gem/enchant differences on the best-matching item yellowfive@89: yellowfive@89: -- gems, but skip artifact relics (will have relicBonusIds set) yellowfive@89: if not optimalItem.relicBonusIds and itemInfo and itemInfo.socketColors then yellowfive@89: for i = 1, #itemInfo.socketColors do yellowfive@89: local g = optimalItem.gemIds[i] yellowfive@89: local isGemEquipped = g ~= 0 and matchItem and matchItem.gemIds and matchItem.gemIds[i] == g yellowfive@89: 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@89: -- enchant info, look in all spec extra info cache yellowfive@89: local enchInfo = nil yellowfive@89: for specPos = 1,4 do yellowfive@89: if Amr.db.char.ExtraEnchantData[specPos] then yellowfive@89: enchInfo = Amr.db.char.ExtraEnchantData[specPos][e] yellowfive@89: if enchInfo then break end yellowfive@89: end yellowfive@89: end yellowfive@89: 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@57: -- look at both gear sets and find stuff that a player needs to acquire to gem/enchant their gear yellowfive@57: function Amr:UpdateShoppingData(player) yellowfive@57: yellowfive@57: local required = { yellowfive@89: stuff = {}, yellowfive@57: specs = player.Specs yellowfive@57: } yellowfive@57: yellowfive@57: local enchantItemIdToId = {} yellowfive@57: yellowfive@57: for spec, gear in pairs(Amr.db.char.GearSets) do yellowfive@89: required.stuff[spec] = getShoppingData(player, gear, spec) yellowfive@57: end yellowfive@57: yellowfive@57: Amr.db.global.Shopping[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@89: end)