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@57: local _specs = { yellowfive@57: [1] = true, yellowfive@57: [2] = true yellowfive@57: } yellowfive@57: local _chk1 yellowfive@57: local _chk2 yellowfive@57: local _isAhOpen = false yellowfive@57: 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@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@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@57: lbl:SetWidth(35) 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@57: function Amr:RefreshShoppingUi() yellowfive@57: yellowfive@57: _chk1:SetVisible(false) yellowfive@57: _chk2:SetVisible(false) yellowfive@57: yellowfive@57: _chk1:SetChecked(false) yellowfive@57: _chk2:SetChecked(false) yellowfive@57: yellowfive@57: -- clear out any previous data yellowfive@57: _panelContent:ReleaseChildren() yellowfive@57: yellowfive@57: -- render required gems for the selected player yellowfive@57: local data = Amr.db.global.Shopping[_selectedPlayer] yellowfive@57: if not data then yellowfive@57: _panelContent:SetLayout("None") yellowfive@57: yellowfive@57: local lbl = AceGUI:Create("AmrUiLabel") yellowfive@57: lbl:SetFont(Amr.CreateFont("Italic", 18, Amr.Colors.TextTan)) yellowfive@57: lbl:SetText(L.ShopEmpty) yellowfive@57: lbl:SetJustifyH("CENTER") yellowfive@57: lbl:SetPoint("TOP", _panelContent.content, "TOP", 0, -30) yellowfive@57: _panelContent:AddChild(lbl) yellowfive@57: else yellowfive@57: -- set labels on checkboxes yellowfive@61: if data.specs[1] and data.specs[1] ~= 0 then yellowfive@57: local id, name = GetSpecializationInfoByID(Amr.GetGameSpecId(data.specs[1])) yellowfive@57: _chk1:SetText(name .. " " .. L.ShopSpecLabel) yellowfive@57: _chk1:SetVisible(true) yellowfive@57: _chk1:SetChecked(_specs[1]) yellowfive@57: end yellowfive@57: yellowfive@61: if data.specs[2] and data.specs[2] ~= 0 then yellowfive@57: local id, name = GetSpecializationInfoByID(Amr.GetGameSpecId(data.specs[2])) yellowfive@57: _chk2:SetText(name .. " " .. L.ShopSpecLabel) yellowfive@57: _chk2:SetVisible(true) yellowfive@57: _chk2:SetChecked(_specs[2]) yellowfive@57: end yellowfive@57: yellowfive@57: local spec = 0 yellowfive@57: if not _specs[1] and not _specs[2] then yellowfive@57: -- both unchecked, show nothing yellowfive@57: else yellowfive@57: -- both is 0, otherwise the one that is selected yellowfive@57: if not _specs[1] or not _specs[2] then yellowfive@57: spec = _specs[1] and 1 or 2 yellowfive@57: end yellowfive@57: yellowfive@57: _panelContent:SetLayout("Fill") yellowfive@57: yellowfive@57: local scroll = AceGUI:Create("AmrUiScrollFrame") yellowfive@57: scroll:SetLayout("List") yellowfive@57: _panelContent:AddChild(scroll) yellowfive@57: yellowfive@57: renderShopSection(data.gems[spec], scroll, L.ShopHeaderGems) yellowfive@57: renderShopSection(data.enchants[spec], scroll, L.ShopHeaderEnchants) yellowfive@57: renderShopSection(data.materials[spec], scroll, L.ShopHeaderMaterials) yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: end yellowfive@57: yellowfive@57: -- compare gear to everything the player owns, and return the minimum gems/enchants/materials needed to optimize yellowfive@57: local function getShoppingData(player, gear, spec) yellowfive@57: yellowfive@57: local ret = { yellowfive@57: gems = {}, yellowfive@57: enchants = {}, yellowfive@57: materials = {} yellowfive@57: } yellowfive@57: yellowfive@57: -- used to prevent considering the same item twice yellowfive@57: local usedItems = {} yellowfive@57: yellowfive@57: for slotId, optimalItem in pairs(gear) do yellowfive@57: local matchItemLink, matchItem = Amr:FindMatchingItem(optimalItem, player, usedItems) yellowfive@57: local itemInfo = Amr.db.char.ExtraItemData[spec][optimalItem.id] yellowfive@57: yellowfive@57: -- find gem/enchant differences on the best-matching item yellowfive@57: yellowfive@57: -- gems yellowfive@57: if itemInfo and itemInfo.socketColors then yellowfive@57: for i = 1, #itemInfo.socketColors do yellowfive@57: local g = optimalItem.gemIds[i] yellowfive@57: local isGemEquipped = g ~= 0 and matchItem and matchItem.gemIds and matchItem.gemIds[i] == g yellowfive@57: yellowfive@57: if not isGemEquipped then yellowfive@57: ret.gems[g] = ret.gems[g] and ret.gems[g] + 1 or 1 yellowfive@57: end yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: -- enchant yellowfive@57: if optimalItem.enchantId and optimalItem.enchantId ~= 0 then yellowfive@57: local e = optimalItem.enchantId yellowfive@57: local isEnchantEquipped = matchItem and matchItem.enchantId and matchItem.enchantId == e yellowfive@57: yellowfive@57: if not isEnchantEquipped then yellowfive@57: ret.enchants[e] = ret.enchants[e] and ret.enchants[e] + 1 or 1 yellowfive@57: end yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: return ret yellowfive@57: end yellowfive@57: yellowfive@57: -- get the number of a specified item 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@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: -- 0 is combination of both specs, 1 is primary, 2 is secondary yellowfive@57: local required = { yellowfive@57: gems = { yellowfive@57: [0] = {}, yellowfive@57: [1] = {}, yellowfive@57: [2] = {} yellowfive@57: }, yellowfive@57: enchants = { yellowfive@57: [0] = {}, yellowfive@57: [1] = {}, yellowfive@57: [2] = {} yellowfive@57: }, yellowfive@57: materials = { yellowfive@57: [0] = {}, yellowfive@57: [1] = {}, yellowfive@57: [2] = {} yellowfive@57: }, 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@57: local obj = getShoppingData(player, gear, spec) yellowfive@57: for k, v in pairs(obj.gems) do yellowfive@57: local gemInfo = Amr.db.char.ExtraGemData[spec][k] yellowfive@57: if gemInfo then yellowfive@57: local prev = required.gems[spec][gemInfo.id] yellowfive@57: required.gems[spec][gemInfo.id] = prev and prev + v or v yellowfive@57: yellowfive@57: prev = required.gems[0][gemInfo.id] yellowfive@57: required.gems[0][gemInfo.id] = prev and prev + v or v yellowfive@57: end yellowfive@57: end yellowfive@57: for k, v in pairs(obj.enchants) do yellowfive@57: local enchInfo = Amr.db.char.ExtraEnchantData[spec][k] yellowfive@57: if enchInfo then yellowfive@57: enchantItemIdToId[enchInfo.itemId] = k yellowfive@57: yellowfive@57: local prev = required.enchants[spec][enchInfo.itemId] yellowfive@57: required.enchants[spec][enchInfo.itemId] = prev and prev + v or v yellowfive@57: yellowfive@57: prev = required.enchants[0][enchInfo.itemId] yellowfive@57: required.enchants[0][enchInfo.itemId] = prev and prev + v or v yellowfive@57: end yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: -- now subtract stuff the player already has, and generate a list of materials as well yellowfive@57: for spec = 0, 2 do yellowfive@57: -- now check if the player has any of the gems or enchants in their inventory, and subtract those yellowfive@57: for itemId, count in pairs(required.gems[spec]) do yellowfive@57: required.gems[spec][itemId] = math.max(count - getOwnedCount(itemId), 0) yellowfive@57: yellowfive@57: if required.gems[spec][itemId] == 0 then yellowfive@57: required.gems[spec][itemId] = nil yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: for itemId, count in pairs(required.enchants[spec]) do yellowfive@57: -- look in both spec extra info cache yellowfive@57: local e = enchantItemIdToId[itemId] yellowfive@57: local enchInfo = nil yellowfive@57: if Amr.db.char.ExtraEnchantData[1] then yellowfive@57: enchInfo = Amr.db.char.ExtraEnchantData[1][e] yellowfive@57: end yellowfive@57: if not enchInfo then yellowfive@57: if Amr.db.char.ExtraEnchantData[2] then yellowfive@57: enchInfo = Amr.db.char.ExtraEnchantData[2][e] yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: if enchInfo then yellowfive@57: required.enchants[spec][itemId] = math.max(count - getOwnedCount(itemId), 0) yellowfive@57: yellowfive@57: if required.enchants[spec][itemId] == 0 then yellowfive@57: required.enchants[spec][itemId] = nil yellowfive@57: else yellowfive@57: -- count up required materials yellowfive@57: if enchInfo.materials then yellowfive@57: local c = required.enchants[spec][itemId] yellowfive@57: for k, v in pairs(enchInfo.materials) do yellowfive@57: local prev = required.materials[spec][k] yellowfive@57: required.materials[spec][k] = prev and prev + (v * c) or (v * c) yellowfive@57: end yellowfive@57: end yellowfive@57: end yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: -- check if player has any of the materials already yellowfive@57: for itemId, count in pairs(required.materials[spec]) do yellowfive@57: required.materials[spec][itemId] = math.max(count - getOwnedCount(itemId), 0) yellowfive@57: yellowfive@57: if required.materials[spec][itemId] == 0 then yellowfive@57: required.materials[spec][itemId] = nil yellowfive@57: end yellowfive@57: end 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@57: end)