diff Shopping.lua @ 57:01b63b8ed811 v21

total rewrite to version 21
author yellowfive
date Fri, 05 Jun 2015 11:05:15 -0700
parents
children cf2b6b9a8337
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Shopping.lua	Fri Jun 05 11:05:15 2015 -0700
@@ -0,0 +1,433 @@
+local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
+local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true)
+local AceGUI = LibStub("AceGUI-3.0")
+
+local _frameShop
+local _panelContent
+local _cboPlayers
+local _selectedPlayer
+local _specs = {
+	[1] = true,
+	[2] = true
+}
+local _chk1
+local _chk2
+local _isAhOpen = false
+
+local function onShopFrameClose(widget)
+	AceGUI:Release(widget)
+	_frameShop = nil
+	_cboPlayers = nil
+	_chk1 = nil
+	_chk2 = nil
+	_panelContent = nil
+end
+
+function Amr:HideShopWindow()
+	if not _frameShop then return end
+	_frameShop:Hide()
+end
+
+local function onPlayerChange(widget, eventName, value)
+	_selectedPlayer = value
+	Amr:RefreshShoppingUi()
+end
+
+local function onSpecClick(widget)
+	local spec = widget:GetUserData("spec")	
+	_specs[spec] = not _specs[spec]
+	
+	Amr:RefreshShoppingUi()
+end
+
+local function onItemClick(widget)
+	local name = widget:GetUserData("itemName")
+	if name then
+		QueryAuctionItems(name)
+	end
+end
+
+function Amr:ShowShopWindow()
+	if not _frameShop then
+		_frameShop = AceGUI:Create("AmrUiFrame")
+		_frameShop:SetStatusTable(Amr.db.profile.shopWindow) -- window position is remembered in db
+		_frameShop:SetCallback("OnClose", onShopFrameClose)
+		_frameShop:SetLayout("None")
+		_frameShop:SetWidth(500)
+		_frameShop:SetHeight(500)
+		_frameShop:SetBorderColor(Amr.Colors.BorderBlue)
+		_frameShop:SetBackgroundColor(Amr.Colors.Bg)
+		
+		local lbl = AceGUI:Create("AmrUiLabel")
+		lbl:SetWidth(400)
+		lbl:SetFont(Amr.CreateFont("Bold", 28, Amr.Colors.White))
+		lbl:SetText(L.ShopTitle)
+		lbl:SetWordWrap(false)
+		lbl:SetJustifyH("CENTER")
+		lbl:SetPoint("TOP", _frameShop.content, "TOP", 0, 30)
+		_frameShop:AddChild(lbl)
+		
+		lbl:SetCallback("OnMouseDown", function(widget) _frameShop:StartMove() end)
+		lbl:SetCallback("OnMouseUp", function(widget) _frameShop:EndMove() end)
+		
+		-- player picker
+		_cboPlayers = AceGUI:Create("AmrUiDropDown")
+		_cboPlayers:SetWidth(400)
+		_cboPlayers:SetPoint("TOPLEFT", _frameShop.content, "TOPLEFT", 0, -30)
+		_frameShop:AddChild(_cboPlayers)
+		
+		-- spec pickers
+		_chk1 = AceGUI:Create("AmrUiCheckBox")
+		_chk1:SetPoint("TOPLEFT", _cboPlayers.frame, "BOTTOMLEFT", 0, -20)
+		_chk1:SetUserData("spec", 1)
+		_chk1:SetCallback("OnClick", onSpecClick)
+		_frameShop:AddChild(_chk1)
+		
+		_chk2 = AceGUI:Create("AmrUiCheckBox")
+		_chk2:SetPoint("LEFT", _chk1.frame, "RIGHT", 30, 0)
+		_chk2:SetUserData("spec", 2)
+		_chk2:SetCallback("OnClick", onSpecClick)
+		_frameShop:AddChild(_chk2)
+		
+		_panelContent = AceGUI:Create("AmrUiPanel")
+		_panelContent:SetLayout("None")
+		_panelContent:SetTransparent()
+		_panelContent:SetPoint("TOPLEFT", _chk1.frame, "BOTTOMLEFT", 0, -10)
+		_panelContent:SetPoint("BOTTOMRIGHT", _frameShop.content, "BOTTOMRIGHT")
+		_frameShop:AddChild(_panelContent)
+		
+		-- update shopping list data
+		local player = Amr:ExportCharacter()
+		Amr:UpdateShoppingData(player)
+		
+		-- fill player list	
+		local playerList = {}
+		for name, data in pairs(Amr.db.global.Shopping) do
+			table.insert(playerList, { text = name, value = name })
+		end	
+		_cboPlayers:SetItems(playerList)
+		
+		-- set default selected player
+		if not _selectedPlayer then
+			_selectedPlayer = player.Name .. "-" .. player.Realm
+		end	
+		_cboPlayers:SelectItem(_selectedPlayer)
+		
+		Amr:RefreshShoppingUi()
+		
+		-- set event on dropdown after UI has been initially rendered
+		_cboPlayers:SetCallback("OnChange", onPlayerChange)
+	else
+		_frameShop:Show()
+		Amr:RefreshShoppingUi()
+	end
+	
+	_frameShop:Raise()
+end
+
+-- helper to render a section of the shopping list
+local function renderShopSection(list, scroll, header)
+	if not list or next(list) == nil then return end
+	
+	local w = 440
+	
+	local panel = AceGUI:Create("AmrUiPanel")
+	panel:SetLayout("None")
+	panel:SetTransparent()
+	panel:SetWidth(w)
+	panel:SetHeight(40)
+	scroll:AddChild(panel)
+	
+	local lbl = AceGUI:Create("AmrUiLabel")
+	lbl:SetWidth(w)
+	lbl:SetFont(Amr.CreateFont("Regular", 18, Amr.Colors.TextHeaderActive))
+	lbl:SetText(header)
+	lbl:SetPoint("BOTTOMLEFT", panel.content, "BOTTOMLEFT")
+	panel:AddChild(lbl)
+	
+	for itemId, count in pairs(list) do
+		panel = AceGUI:Create("AmrUiPanel")
+		panel:SetLayout("None")
+		panel:SetTransparent()
+		panel:SetWidth(w)
+		panel:SetHeight(30)
+		scroll:AddChild(panel)
+		
+		lbl = AceGUI:Create("AmrUiLabel")
+		lbl:SetWidth(35)
+		lbl:SetWordWrap(false)
+		lbl:SetFont(Amr.CreateFont("Bold", 20, Amr.Colors.White))
+		lbl:SetText(count .. "x")
+		lbl:SetPoint("LEFT", panel.content, "LEFT")
+		panel:AddChild(lbl)
+		
+		local icon = AceGUI:Create("AmrUiIcon")
+		icon:SetBorderWidth(1)
+		icon:SetIconBorderColor(Amr.Colors.White)
+		icon:SetWidth(18)
+		icon:SetHeight(18)
+		icon:SetPoint("LEFT", lbl.frame, "RIGHT", 5, 0)
+		panel:AddChild(icon)
+		
+		local btn = AceGUI:Create("AmrUiTextButton")
+		btn:SetWidth(w - 30 - 18 - 15)
+		btn:SetJustifyH("LEFT")
+		btn:SetWordWrap(false)
+		btn:SetFont(Amr.CreateFont("Bold", 14, Amr.Colors.White))
+		btn:SetHoverFont(Amr.CreateFont("Bold", 14, Amr.Colors.White))
+		btn:SetPoint("LEFT", icon.frame, "RIGHT", 5, 0)
+		btn:SetCallback("OnClick", onItemClick)
+		panel:AddChild(btn)
+		
+		Amr.GetItemInfo(itemId, function(obj, name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture)					
+			-- set icon, name, and a tooltip
+			obj.itemIcon:SetIcon(texture)
+			obj.itemText:SetText(link:gsub("%[", ""):gsub("%]", ""))
+			obj.itemText:SetUserData("itemName", name)
+			Amr:SetItemTooltip(obj.itemText, link)
+		end, { itemIcon = icon, itemText = btn })
+	end
+	
+end
+
+function Amr:RefreshShoppingUi()
+
+	_chk1:SetVisible(false)
+	_chk2:SetVisible(false)
+	
+	_chk1:SetChecked(false)
+	_chk2:SetChecked(false)
+	
+	-- clear out any previous data
+	_panelContent:ReleaseChildren()
+	
+	-- render required gems for the selected player
+	local data = Amr.db.global.Shopping[_selectedPlayer]
+	if not data then		
+		_panelContent:SetLayout("None")
+		
+		local lbl = AceGUI:Create("AmrUiLabel")
+		lbl:SetFont(Amr.CreateFont("Italic", 18, Amr.Colors.TextTan))
+		lbl:SetText(L.ShopEmpty)
+		lbl:SetJustifyH("CENTER")
+		lbl:SetPoint("TOP", _panelContent.content, "TOP", 0, -30)
+		_panelContent:AddChild(lbl)
+	else
+		-- set labels on checkboxes
+		if data.specs[1] then
+			local id, name = GetSpecializationInfoByID(Amr.GetGameSpecId(data.specs[1]))
+			_chk1:SetText(name .. " " .. L.ShopSpecLabel)
+			_chk1:SetVisible(true)
+			_chk1:SetChecked(_specs[1])
+		end
+		
+		if data.specs[2] then
+			local id, name = GetSpecializationInfoByID(Amr.GetGameSpecId(data.specs[2]))
+			_chk2:SetText(name .. " " .. L.ShopSpecLabel)
+			_chk2:SetVisible(true)
+			_chk2:SetChecked(_specs[2])
+		end
+		
+		local spec = 0
+		if not _specs[1] and not _specs[2] then
+			-- both unchecked, show nothing
+		else
+			-- both is 0, otherwise the one that is selected
+			if not _specs[1] or not _specs[2] then
+				spec = _specs[1] and 1 or 2
+			end
+			
+			_panelContent:SetLayout("Fill")
+			
+			local scroll = AceGUI:Create("AmrUiScrollFrame")
+			scroll:SetLayout("List")
+			_panelContent:AddChild(scroll)
+			
+			renderShopSection(data.gems[spec], scroll, L.ShopHeaderGems)
+			renderShopSection(data.enchants[spec], scroll, L.ShopHeaderEnchants)		
+			renderShopSection(data.materials[spec], scroll, L.ShopHeaderMaterials)
+		end
+	end
+				
+end
+
+-- compare gear to everything the player owns, and return the minimum gems/enchants/materials needed to optimize
+local function getShoppingData(player, gear, spec)
+
+	local ret = {
+		gems = {},
+		enchants = {},
+		materials = {}
+	}
+	
+	-- used to prevent considering the same item twice
+	local usedItems = {}
+	
+	for slotId, optimalItem in pairs(gear) do
+		local matchItemLink, matchItem = Amr:FindMatchingItem(optimalItem, player, usedItems)
+		local itemInfo = Amr.db.char.ExtraItemData[spec][optimalItem.id]
+		
+		-- find gem/enchant differences on the best-matching item
+		
+		-- gems
+		if itemInfo and itemInfo.socketColors then
+			for i = 1, #itemInfo.socketColors do
+				local g = optimalItem.gemIds[i]
+				local isGemEquipped = g ~= 0 and matchItem and matchItem.gemIds and matchItem.gemIds[i] == g
+				
+				if not isGemEquipped then
+					ret.gems[g] = ret.gems[g] and ret.gems[g] + 1 or 1
+				end
+			end
+		end
+		
+		-- enchant
+		if optimalItem.enchantId and optimalItem.enchantId ~= 0 then
+			local e = optimalItem.enchantId
+			local isEnchantEquipped = matchItem and matchItem.enchantId and matchItem.enchantId == e
+			
+			if not isEnchantEquipped then
+				ret.enchants[e] = ret.enchants[e] and ret.enchants[e] + 1 or 1
+			end
+		end
+	end
+	
+	return ret
+end
+
+-- get the number of a specified item that the player currently owns
+local function getOwnedCount(itemId)
+	local ret = 0
+	
+	local list = Amr.db.char.BagItemsAndCounts
+	if list and list[itemId] then
+		ret = ret + list[itemId]
+	end
+	
+	list = Amr.db.char.BankItemsAndCounts
+	if list and list[itemId] then
+		ret = ret + list[itemId]
+	end
+	
+	return ret
+end
+
+-- look at both gear sets and find stuff that a player needs to acquire to gem/enchant their gear
+function Amr:UpdateShoppingData(player)
+
+	-- 0 is combination of both specs, 1 is primary, 2 is secondary
+	local required = {
+		gems = {
+			[0] = {},
+			[1] = {},
+			[2] = {}
+		},
+		enchants = {
+			[0] = {},
+			[1] = {},
+			[2] = {}
+		},
+		materials = {
+			[0] = {},
+			[1] = {},
+			[2] = {}
+		},
+		specs = player.Specs
+	}
+	
+	local enchantItemIdToId = {}
+	
+	for spec, gear in pairs(Amr.db.char.GearSets) do
+		local obj = getShoppingData(player, gear, spec)
+		for k, v in pairs(obj.gems) do
+			local gemInfo = Amr.db.char.ExtraGemData[spec][k]
+			if gemInfo then
+				local prev = required.gems[spec][gemInfo.id]
+				required.gems[spec][gemInfo.id] = prev and prev + v or v
+				
+				prev = required.gems[0][gemInfo.id]
+				required.gems[0][gemInfo.id] = prev and prev + v or v
+			end
+		end
+		for k, v in pairs(obj.enchants) do
+			local enchInfo = Amr.db.char.ExtraEnchantData[spec][k]
+			if enchInfo then
+				enchantItemIdToId[enchInfo.itemId] = k
+				
+				local prev = required.enchants[spec][enchInfo.itemId]
+				required.enchants[spec][enchInfo.itemId] = prev and prev + v or v
+				
+				prev = required.enchants[0][enchInfo.itemId]
+				required.enchants[0][enchInfo.itemId] = prev and prev + v or v
+			end
+		end
+	end
+	
+	-- now subtract stuff the player already has, and generate a list of materials as well
+	for spec = 0, 2 do
+		-- now check if the player has any of the gems or enchants in their inventory, and subtract those
+		for itemId, count in pairs(required.gems[spec]) do
+			required.gems[spec][itemId] = math.max(count - getOwnedCount(itemId), 0)
+			
+			if required.gems[spec][itemId] == 0 then
+				required.gems[spec][itemId] = nil
+			end
+		end
+		
+		for itemId, count in pairs(required.enchants[spec]) do
+			-- look in both spec extra info cache
+			local e = enchantItemIdToId[itemId]		
+			local enchInfo = nil
+			if Amr.db.char.ExtraEnchantData[1] then
+				enchInfo = Amr.db.char.ExtraEnchantData[1][e]
+			end
+			if not enchInfo then
+				if Amr.db.char.ExtraEnchantData[2] then
+					enchInfo = Amr.db.char.ExtraEnchantData[2][e]
+				end
+			end
+			
+			if enchInfo then
+				required.enchants[spec][itemId] = math.max(count - getOwnedCount(itemId), 0)
+
+				if required.enchants[spec][itemId] == 0 then
+					required.enchants[spec][itemId] = nil
+				else
+					-- count up required materials
+					if enchInfo.materials then
+						local c = required.enchants[spec][itemId]
+						for k, v in pairs(enchInfo.materials) do
+							local prev = required.materials[spec][k]
+							required.materials[spec][k] = prev and prev + (v * c) or (v * c)
+						end
+					end
+				end			
+			end	
+		end
+		
+		-- check if player has any of the materials already
+		for itemId, count in pairs(required.materials[spec]) do
+			required.materials[spec][itemId] = math.max(count - getOwnedCount(itemId), 0)
+			
+			if required.materials[spec][itemId] == 0 then
+				required.materials[spec][itemId] = nil
+			end
+		end
+	end
+	
+	Amr.db.global.Shopping[player.Name .. "-" .. player.Realm] = required
+end
+
+Amr:AddEventHandler("AUCTION_HOUSE_SHOW", function() 
+	_isAhOpen = true
+	if Amr.db.profile.options.shopAh then
+		Amr:ShowShopWindow()
+	end
+end)
+
+Amr:AddEventHandler("AUCTION_HOUSE_CLOSED", function() 
+	_isAhOpen = false
+	if Amr.db.profile.options.shopAh then
+		Amr:HideShopWindow()
+	end
+end)
\ No newline at end of file