comparison Shopping.lua @ 57:01b63b8ed811 v21

total rewrite to version 21
author yellowfive
date Fri, 05 Jun 2015 11:05:15 -0700
parents
children cf2b6b9a8337
comparison
equal deleted inserted replaced
56:75431c084aa0 57:01b63b8ed811
1 local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
2 local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true)
3 local AceGUI = LibStub("AceGUI-3.0")
4
5 local _frameShop
6 local _panelContent
7 local _cboPlayers
8 local _selectedPlayer
9 local _specs = {
10 [1] = true,
11 [2] = true
12 }
13 local _chk1
14 local _chk2
15 local _isAhOpen = false
16
17 local function onShopFrameClose(widget)
18 AceGUI:Release(widget)
19 _frameShop = nil
20 _cboPlayers = nil
21 _chk1 = nil
22 _chk2 = nil
23 _panelContent = nil
24 end
25
26 function Amr:HideShopWindow()
27 if not _frameShop then return end
28 _frameShop:Hide()
29 end
30
31 local function onPlayerChange(widget, eventName, value)
32 _selectedPlayer = value
33 Amr:RefreshShoppingUi()
34 end
35
36 local function onSpecClick(widget)
37 local spec = widget:GetUserData("spec")
38 _specs[spec] = not _specs[spec]
39
40 Amr:RefreshShoppingUi()
41 end
42
43 local function onItemClick(widget)
44 local name = widget:GetUserData("itemName")
45 if name then
46 QueryAuctionItems(name)
47 end
48 end
49
50 function Amr:ShowShopWindow()
51 if not _frameShop then
52 _frameShop = AceGUI:Create("AmrUiFrame")
53 _frameShop:SetStatusTable(Amr.db.profile.shopWindow) -- window position is remembered in db
54 _frameShop:SetCallback("OnClose", onShopFrameClose)
55 _frameShop:SetLayout("None")
56 _frameShop:SetWidth(500)
57 _frameShop:SetHeight(500)
58 _frameShop:SetBorderColor(Amr.Colors.BorderBlue)
59 _frameShop:SetBackgroundColor(Amr.Colors.Bg)
60
61 local lbl = AceGUI:Create("AmrUiLabel")
62 lbl:SetWidth(400)
63 lbl:SetFont(Amr.CreateFont("Bold", 28, Amr.Colors.White))
64 lbl:SetText(L.ShopTitle)
65 lbl:SetWordWrap(false)
66 lbl:SetJustifyH("CENTER")
67 lbl:SetPoint("TOP", _frameShop.content, "TOP", 0, 30)
68 _frameShop:AddChild(lbl)
69
70 lbl:SetCallback("OnMouseDown", function(widget) _frameShop:StartMove() end)
71 lbl:SetCallback("OnMouseUp", function(widget) _frameShop:EndMove() end)
72
73 -- player picker
74 _cboPlayers = AceGUI:Create("AmrUiDropDown")
75 _cboPlayers:SetWidth(400)
76 _cboPlayers:SetPoint("TOPLEFT", _frameShop.content, "TOPLEFT", 0, -30)
77 _frameShop:AddChild(_cboPlayers)
78
79 -- spec pickers
80 _chk1 = AceGUI:Create("AmrUiCheckBox")
81 _chk1:SetPoint("TOPLEFT", _cboPlayers.frame, "BOTTOMLEFT", 0, -20)
82 _chk1:SetUserData("spec", 1)
83 _chk1:SetCallback("OnClick", onSpecClick)
84 _frameShop:AddChild(_chk1)
85
86 _chk2 = AceGUI:Create("AmrUiCheckBox")
87 _chk2:SetPoint("LEFT", _chk1.frame, "RIGHT", 30, 0)
88 _chk2:SetUserData("spec", 2)
89 _chk2:SetCallback("OnClick", onSpecClick)
90 _frameShop:AddChild(_chk2)
91
92 _panelContent = AceGUI:Create("AmrUiPanel")
93 _panelContent:SetLayout("None")
94 _panelContent:SetTransparent()
95 _panelContent:SetPoint("TOPLEFT", _chk1.frame, "BOTTOMLEFT", 0, -10)
96 _panelContent:SetPoint("BOTTOMRIGHT", _frameShop.content, "BOTTOMRIGHT")
97 _frameShop:AddChild(_panelContent)
98
99 -- update shopping list data
100 local player = Amr:ExportCharacter()
101 Amr:UpdateShoppingData(player)
102
103 -- fill player list
104 local playerList = {}
105 for name, data in pairs(Amr.db.global.Shopping) do
106 table.insert(playerList, { text = name, value = name })
107 end
108 _cboPlayers:SetItems(playerList)
109
110 -- set default selected player
111 if not _selectedPlayer then
112 _selectedPlayer = player.Name .. "-" .. player.Realm
113 end
114 _cboPlayers:SelectItem(_selectedPlayer)
115
116 Amr:RefreshShoppingUi()
117
118 -- set event on dropdown after UI has been initially rendered
119 _cboPlayers:SetCallback("OnChange", onPlayerChange)
120 else
121 _frameShop:Show()
122 Amr:RefreshShoppingUi()
123 end
124
125 _frameShop:Raise()
126 end
127
128 -- helper to render a section of the shopping list
129 local function renderShopSection(list, scroll, header)
130 if not list or next(list) == nil then return end
131
132 local w = 440
133
134 local panel = AceGUI:Create("AmrUiPanel")
135 panel:SetLayout("None")
136 panel:SetTransparent()
137 panel:SetWidth(w)
138 panel:SetHeight(40)
139 scroll:AddChild(panel)
140
141 local lbl = AceGUI:Create("AmrUiLabel")
142 lbl:SetWidth(w)
143 lbl:SetFont(Amr.CreateFont("Regular", 18, Amr.Colors.TextHeaderActive))
144 lbl:SetText(header)
145 lbl:SetPoint("BOTTOMLEFT", panel.content, "BOTTOMLEFT")
146 panel:AddChild(lbl)
147
148 for itemId, count in pairs(list) do
149 panel = AceGUI:Create("AmrUiPanel")
150 panel:SetLayout("None")
151 panel:SetTransparent()
152 panel:SetWidth(w)
153 panel:SetHeight(30)
154 scroll:AddChild(panel)
155
156 lbl = AceGUI:Create("AmrUiLabel")
157 lbl:SetWidth(35)
158 lbl:SetWordWrap(false)
159 lbl:SetFont(Amr.CreateFont("Bold", 20, Amr.Colors.White))
160 lbl:SetText(count .. "x")
161 lbl:SetPoint("LEFT", panel.content, "LEFT")
162 panel:AddChild(lbl)
163
164 local icon = AceGUI:Create("AmrUiIcon")
165 icon:SetBorderWidth(1)
166 icon:SetIconBorderColor(Amr.Colors.White)
167 icon:SetWidth(18)
168 icon:SetHeight(18)
169 icon:SetPoint("LEFT", lbl.frame, "RIGHT", 5, 0)
170 panel:AddChild(icon)
171
172 local btn = AceGUI:Create("AmrUiTextButton")
173 btn:SetWidth(w - 30 - 18 - 15)
174 btn:SetJustifyH("LEFT")
175 btn:SetWordWrap(false)
176 btn:SetFont(Amr.CreateFont("Bold", 14, Amr.Colors.White))
177 btn:SetHoverFont(Amr.CreateFont("Bold", 14, Amr.Colors.White))
178 btn:SetPoint("LEFT", icon.frame, "RIGHT", 5, 0)
179 btn:SetCallback("OnClick", onItemClick)
180 panel:AddChild(btn)
181
182 Amr.GetItemInfo(itemId, function(obj, name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture)
183 -- set icon, name, and a tooltip
184 obj.itemIcon:SetIcon(texture)
185 obj.itemText:SetText(link:gsub("%[", ""):gsub("%]", ""))
186 obj.itemText:SetUserData("itemName", name)
187 Amr:SetItemTooltip(obj.itemText, link)
188 end, { itemIcon = icon, itemText = btn })
189 end
190
191 end
192
193 function Amr:RefreshShoppingUi()
194
195 _chk1:SetVisible(false)
196 _chk2:SetVisible(false)
197
198 _chk1:SetChecked(false)
199 _chk2:SetChecked(false)
200
201 -- clear out any previous data
202 _panelContent:ReleaseChildren()
203
204 -- render required gems for the selected player
205 local data = Amr.db.global.Shopping[_selectedPlayer]
206 if not data then
207 _panelContent:SetLayout("None")
208
209 local lbl = AceGUI:Create("AmrUiLabel")
210 lbl:SetFont(Amr.CreateFont("Italic", 18, Amr.Colors.TextTan))
211 lbl:SetText(L.ShopEmpty)
212 lbl:SetJustifyH("CENTER")
213 lbl:SetPoint("TOP", _panelContent.content, "TOP", 0, -30)
214 _panelContent:AddChild(lbl)
215 else
216 -- set labels on checkboxes
217 if data.specs[1] then
218 local id, name = GetSpecializationInfoByID(Amr.GetGameSpecId(data.specs[1]))
219 _chk1:SetText(name .. " " .. L.ShopSpecLabel)
220 _chk1:SetVisible(true)
221 _chk1:SetChecked(_specs[1])
222 end
223
224 if data.specs[2] then
225 local id, name = GetSpecializationInfoByID(Amr.GetGameSpecId(data.specs[2]))
226 _chk2:SetText(name .. " " .. L.ShopSpecLabel)
227 _chk2:SetVisible(true)
228 _chk2:SetChecked(_specs[2])
229 end
230
231 local spec = 0
232 if not _specs[1] and not _specs[2] then
233 -- both unchecked, show nothing
234 else
235 -- both is 0, otherwise the one that is selected
236 if not _specs[1] or not _specs[2] then
237 spec = _specs[1] and 1 or 2
238 end
239
240 _panelContent:SetLayout("Fill")
241
242 local scroll = AceGUI:Create("AmrUiScrollFrame")
243 scroll:SetLayout("List")
244 _panelContent:AddChild(scroll)
245
246 renderShopSection(data.gems[spec], scroll, L.ShopHeaderGems)
247 renderShopSection(data.enchants[spec], scroll, L.ShopHeaderEnchants)
248 renderShopSection(data.materials[spec], scroll, L.ShopHeaderMaterials)
249 end
250 end
251
252 end
253
254 -- compare gear to everything the player owns, and return the minimum gems/enchants/materials needed to optimize
255 local function getShoppingData(player, gear, spec)
256
257 local ret = {
258 gems = {},
259 enchants = {},
260 materials = {}
261 }
262
263 -- used to prevent considering the same item twice
264 local usedItems = {}
265
266 for slotId, optimalItem in pairs(gear) do
267 local matchItemLink, matchItem = Amr:FindMatchingItem(optimalItem, player, usedItems)
268 local itemInfo = Amr.db.char.ExtraItemData[spec][optimalItem.id]
269
270 -- find gem/enchant differences on the best-matching item
271
272 -- gems
273 if itemInfo and itemInfo.socketColors then
274 for i = 1, #itemInfo.socketColors do
275 local g = optimalItem.gemIds[i]
276 local isGemEquipped = g ~= 0 and matchItem and matchItem.gemIds and matchItem.gemIds[i] == g
277
278 if not isGemEquipped then
279 ret.gems[g] = ret.gems[g] and ret.gems[g] + 1 or 1
280 end
281 end
282 end
283
284 -- enchant
285 if optimalItem.enchantId and optimalItem.enchantId ~= 0 then
286 local e = optimalItem.enchantId
287 local isEnchantEquipped = matchItem and matchItem.enchantId and matchItem.enchantId == e
288
289 if not isEnchantEquipped then
290 ret.enchants[e] = ret.enchants[e] and ret.enchants[e] + 1 or 1
291 end
292 end
293 end
294
295 return ret
296 end
297
298 -- get the number of a specified item that the player currently owns
299 local function getOwnedCount(itemId)
300 local ret = 0
301
302 local list = Amr.db.char.BagItemsAndCounts
303 if list and list[itemId] then
304 ret = ret + list[itemId]
305 end
306
307 list = Amr.db.char.BankItemsAndCounts
308 if list and list[itemId] then
309 ret = ret + list[itemId]
310 end
311
312 return ret
313 end
314
315 -- look at both gear sets and find stuff that a player needs to acquire to gem/enchant their gear
316 function Amr:UpdateShoppingData(player)
317
318 -- 0 is combination of both specs, 1 is primary, 2 is secondary
319 local required = {
320 gems = {
321 [0] = {},
322 [1] = {},
323 [2] = {}
324 },
325 enchants = {
326 [0] = {},
327 [1] = {},
328 [2] = {}
329 },
330 materials = {
331 [0] = {},
332 [1] = {},
333 [2] = {}
334 },
335 specs = player.Specs
336 }
337
338 local enchantItemIdToId = {}
339
340 for spec, gear in pairs(Amr.db.char.GearSets) do
341 local obj = getShoppingData(player, gear, spec)
342 for k, v in pairs(obj.gems) do
343 local gemInfo = Amr.db.char.ExtraGemData[spec][k]
344 if gemInfo then
345 local prev = required.gems[spec][gemInfo.id]
346 required.gems[spec][gemInfo.id] = prev and prev + v or v
347
348 prev = required.gems[0][gemInfo.id]
349 required.gems[0][gemInfo.id] = prev and prev + v or v
350 end
351 end
352 for k, v in pairs(obj.enchants) do
353 local enchInfo = Amr.db.char.ExtraEnchantData[spec][k]
354 if enchInfo then
355 enchantItemIdToId[enchInfo.itemId] = k
356
357 local prev = required.enchants[spec][enchInfo.itemId]
358 required.enchants[spec][enchInfo.itemId] = prev and prev + v or v
359
360 prev = required.enchants[0][enchInfo.itemId]
361 required.enchants[0][enchInfo.itemId] = prev and prev + v or v
362 end
363 end
364 end
365
366 -- now subtract stuff the player already has, and generate a list of materials as well
367 for spec = 0, 2 do
368 -- now check if the player has any of the gems or enchants in their inventory, and subtract those
369 for itemId, count in pairs(required.gems[spec]) do
370 required.gems[spec][itemId] = math.max(count - getOwnedCount(itemId), 0)
371
372 if required.gems[spec][itemId] == 0 then
373 required.gems[spec][itemId] = nil
374 end
375 end
376
377 for itemId, count in pairs(required.enchants[spec]) do
378 -- look in both spec extra info cache
379 local e = enchantItemIdToId[itemId]
380 local enchInfo = nil
381 if Amr.db.char.ExtraEnchantData[1] then
382 enchInfo = Amr.db.char.ExtraEnchantData[1][e]
383 end
384 if not enchInfo then
385 if Amr.db.char.ExtraEnchantData[2] then
386 enchInfo = Amr.db.char.ExtraEnchantData[2][e]
387 end
388 end
389
390 if enchInfo then
391 required.enchants[spec][itemId] = math.max(count - getOwnedCount(itemId), 0)
392
393 if required.enchants[spec][itemId] == 0 then
394 required.enchants[spec][itemId] = nil
395 else
396 -- count up required materials
397 if enchInfo.materials then
398 local c = required.enchants[spec][itemId]
399 for k, v in pairs(enchInfo.materials) do
400 local prev = required.materials[spec][k]
401 required.materials[spec][k] = prev and prev + (v * c) or (v * c)
402 end
403 end
404 end
405 end
406 end
407
408 -- check if player has any of the materials already
409 for itemId, count in pairs(required.materials[spec]) do
410 required.materials[spec][itemId] = math.max(count - getOwnedCount(itemId), 0)
411
412 if required.materials[spec][itemId] == 0 then
413 required.materials[spec][itemId] = nil
414 end
415 end
416 end
417
418 Amr.db.global.Shopping[player.Name .. "-" .. player.Realm] = required
419 end
420
421 Amr:AddEventHandler("AUCTION_HOUSE_SHOW", function()
422 _isAhOpen = true
423 if Amr.db.profile.options.shopAh then
424 Amr:ShowShopWindow()
425 end
426 end)
427
428 Amr:AddEventHandler("AUCTION_HOUSE_CLOSED", function()
429 _isAhOpen = false
430 if Amr.db.profile.options.shopAh then
431 Amr:HideShopWindow()
432 end
433 end)