annotate Shopping.lua @ 194:02bbbf5ef3db

Added tag v94 for changeset cb7eb9b9cc24
author yellowfive
date Wed, 11 Nov 2020 23:58:39 -0800
parents eec8032ba9df
children
rev   line source
yellowfive@57 1 local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
yellowfive@57 2 local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true)
yellowfive@57 3 local AceGUI = LibStub("AceGUI-3.0")
yellowfive@57 4
yellowfive@57 5 local _frameShop
yellowfive@57 6 local _panelContent
yellowfive@57 7 local _cboPlayers
yellowfive@139 8 local _selectedSetup
yellowfive@81 9
yellowfive@57 10 local _specs = {
yellowfive@57 11 [1] = true,
yellowfive@81 12 [2] = true,
yellowfive@81 13 [3] = true,
yellowfive@81 14 [4] = true,
yellowfive@57 15 }
yellowfive@81 16
yellowfive@57 17 local _isAhOpen = false
yellowfive@57 18
yellowfive@89 19 local function incrementTableItem(tbl, key, inc)
yellowfive@89 20 tbl[key] = tbl[key] and tbl[key] + inc or inc
yellowfive@89 21 end
yellowfive@89 22
yellowfive@57 23 local function onShopFrameClose(widget)
yellowfive@57 24 AceGUI:Release(widget)
yellowfive@57 25 _frameShop = nil
yellowfive@57 26 _cboPlayers = nil
yellowfive@57 27 _panelContent = nil
yellowfive@57 28 end
yellowfive@57 29
yellowfive@57 30 function Amr:HideShopWindow()
yellowfive@57 31 if not _frameShop then return end
yellowfive@57 32 _frameShop:Hide()
yellowfive@57 33 end
yellowfive@57 34
yellowfive@57 35 local function onPlayerChange(widget, eventName, value)
yellowfive@139 36 _selectedSetup = value
yellowfive@57 37 Amr:RefreshShoppingUi()
yellowfive@57 38 end
yellowfive@57 39
yellowfive@57 40 local function onSpecClick(widget)
yellowfive@57 41 local spec = widget:GetUserData("spec")
yellowfive@57 42 _specs[spec] = not _specs[spec]
yellowfive@57 43
yellowfive@57 44 Amr:RefreshShoppingUi()
yellowfive@57 45 end
yellowfive@57 46
yellowfive@57 47 local function onItemClick(widget)
yellowfive@57 48 local name = widget:GetUserData("itemName")
yellowfive@57 49 if name then
yellowfive@177 50
yellowfive@177 51 local query = {}
yellowfive@177 52 query.searchString = name
yellowfive@177 53 query.sorts = {}
yellowfive@177 54 C_AuctionHouse.SendBrowseQuery(query)
yellowfive@177 55
yellowfive@177 56 --QueryAuctionItems(name)
yellowfive@57 57 end
yellowfive@57 58 end
yellowfive@57 59
yellowfive@124 60
yellowfive@57 61 function Amr:ShowShopWindow()
yellowfive@124 62
yellowfive@179 63 if InCombatLockdown() then return end
yellowfive@179 64
yellowfive@57 65 if not _frameShop then
yellowfive@57 66 _frameShop = AceGUI:Create("AmrUiFrame")
yellowfive@57 67 _frameShop:SetStatusTable(Amr.db.profile.shopWindow) -- window position is remembered in db
yellowfive@57 68 _frameShop:SetCallback("OnClose", onShopFrameClose)
yellowfive@57 69 _frameShop:SetLayout("None")
yellowfive@57 70 _frameShop:SetWidth(500)
yellowfive@57 71 _frameShop:SetHeight(500)
yellowfive@57 72 _frameShop:SetBorderColor(Amr.Colors.BorderBlue)
yellowfive@57 73 _frameShop:SetBackgroundColor(Amr.Colors.Bg)
yellowfive@57 74
yellowfive@61 75 if Amr.db.profile.options.uiScale ~= 1 then
yellowfive@61 76 local scale = tonumber(Amr.db.profile.options.uiScale)
yellowfive@61 77 _frameShop:SetScale(scale)
yellowfive@61 78 end
yellowfive@61 79
yellowfive@57 80 local lbl = AceGUI:Create("AmrUiLabel")
yellowfive@124 81 _frameShop:AddChild(lbl)
yellowfive@57 82 lbl:SetWidth(400)
yellowfive@57 83 lbl:SetFont(Amr.CreateFont("Bold", 28, Amr.Colors.White))
yellowfive@57 84 lbl:SetText(L.ShopTitle)
yellowfive@57 85 lbl:SetWordWrap(false)
yellowfive@57 86 lbl:SetJustifyH("CENTER")
yellowfive@57 87 lbl:SetPoint("TOP", _frameShop.content, "TOP", 0, 30)
yellowfive@57 88
yellowfive@57 89 lbl:SetCallback("OnMouseDown", function(widget) _frameShop:StartMove() end)
yellowfive@57 90 lbl:SetCallback("OnMouseUp", function(widget) _frameShop:EndMove() end)
yellowfive@57 91
yellowfive@57 92 -- player picker
yellowfive@57 93 _cboPlayers = AceGUI:Create("AmrUiDropDown")
yellowfive@57 94 _cboPlayers:SetWidth(400)
yellowfive@124 95 _frameShop:AddChild(_cboPlayers)
yellowfive@57 96 _cboPlayers:SetPoint("TOPLEFT", _frameShop.content, "TOPLEFT", 0, -30)
yellowfive@57 97
yellowfive@57 98 _panelContent = AceGUI:Create("AmrUiPanel")
yellowfive@57 99 _panelContent:SetLayout("None")
yellowfive@57 100 _panelContent:SetTransparent()
yellowfive@124 101 _frameShop:AddChild(_panelContent)
yellowfive@139 102 _panelContent:SetPoint("TOPLEFT", _cboPlayers.frame, "BOTTOMLEFT", 0, -10)
yellowfive@57 103 _panelContent:SetPoint("BOTTOMRIGHT", _frameShop.content, "BOTTOMRIGHT")
yellowfive@57 104
yellowfive@57 105 -- update shopping list data
yellowfive@57 106 local player = Amr:ExportCharacter()
yellowfive@57 107 Amr:UpdateShoppingData(player)
yellowfive@57 108
yellowfive@57 109 -- fill player list
yellowfive@57 110 local playerList = {}
yellowfive@139 111 local firstData = nil
yellowfive@139 112 for name, v in pairs(Amr.db.global.Shopping2) do
yellowfive@139 113 for setupName, data in pairs(v.setups) do
yellowfive@139 114 table.insert(playerList, { text = name .. " " .. setupName, value = name .. "@" .. setupName })
yellowfive@139 115 if not firstData then
yellowfive@139 116 firstData = name .. "@" .. setupName
yellowfive@139 117 end
yellowfive@139 118 end
yellowfive@57 119 end
yellowfive@57 120 _cboPlayers:SetItems(playerList)
yellowfive@57 121
yellowfive@57 122 -- set default selected player
yellowfive@139 123 local playerData = Amr.db.global.Shopping2[player.Name .. "-" .. player.Realm]
yellowfive@139 124 if playerData and playerData.setups then
yellowfive@139 125 _selectedSetup = Amr:GetActiveSetupLabel()
yellowfive@139 126 if not _selectedSetup then
yellowfive@139 127 Amr:PickFirstSetupForSpec()
yellowfive@139 128 _selectedSetup = Amr:GetActiveSetupLabel()
yellowfive@139 129 end
yellowfive@139 130 if _selectedSetup and not playerData.setups[_selectedSetup] then
yellowfive@139 131 _selectedSetup = nil
yellowfive@139 132 else
yellowfive@139 133 _selectedSetup = player.Name .. "-" .. player.Realm .. "@" .. _selectedSetup
yellowfive@139 134 end
yellowfive@139 135 end
yellowfive@139 136
yellowfive@139 137 if not _selectedSetup then
yellowfive@139 138 if playerData and playerData.setups then
yellowfive@139 139 for k,v in pairs(playerData.setups) do
yellowfive@139 140 _selectedSetup = player.Name .. "-" .. player.Realm .. "@" .. k
yellowfive@139 141 break
yellowfive@139 142 end
yellowfive@139 143 else
yellowfive@139 144 _selectedSetup = firstData
yellowfive@139 145 end
yellowfive@139 146 end
yellowfive@139 147 _cboPlayers:SelectItem(_selectedSetup)
yellowfive@57 148
yellowfive@57 149 Amr:RefreshShoppingUi()
yellowfive@57 150
yellowfive@57 151 -- set event on dropdown after UI has been initially rendered
yellowfive@57 152 _cboPlayers:SetCallback("OnChange", onPlayerChange)
yellowfive@139 153
yellowfive@139 154 -- set a timer to refresh a bit after opening b/c sometimes some item info isn't available
yellowfive@139 155 Amr.Wait(2, function()
yellowfive@139 156 if _frameShop then
yellowfive@139 157 Amr:RefreshShoppingUi()
yellowfive@139 158 end
yellowfive@139 159 end)
yellowfive@57 160 else
yellowfive@57 161 _frameShop:Show()
yellowfive@57 162 Amr:RefreshShoppingUi()
yellowfive@57 163 end
yellowfive@57 164
yellowfive@57 165 _frameShop:Raise()
yellowfive@57 166 end
yellowfive@57 167
yellowfive@57 168 -- helper to render a section of the shopping list
yellowfive@57 169 local function renderShopSection(list, scroll, header)
yellowfive@57 170 if not list or next(list) == nil then return end
yellowfive@57 171
yellowfive@57 172 local w = 440
yellowfive@57 173
yellowfive@57 174 local panel = AceGUI:Create("AmrUiPanel")
yellowfive@57 175 panel:SetLayout("None")
yellowfive@57 176 panel:SetTransparent()
yellowfive@57 177 panel:SetWidth(w)
yellowfive@57 178 panel:SetHeight(40)
yellowfive@57 179 scroll:AddChild(panel)
yellowfive@57 180
yellowfive@57 181 local lbl = AceGUI:Create("AmrUiLabel")
yellowfive@124 182 panel:AddChild(lbl)
yellowfive@57 183 lbl:SetWidth(w)
yellowfive@57 184 lbl:SetFont(Amr.CreateFont("Regular", 18, Amr.Colors.TextHeaderActive))
yellowfive@57 185 lbl:SetText(header)
yellowfive@57 186 lbl:SetPoint("BOTTOMLEFT", panel.content, "BOTTOMLEFT")
yellowfive@57 187
yellowfive@57 188 for itemId, count in pairs(list) do
yellowfive@57 189 panel = AceGUI:Create("AmrUiPanel")
yellowfive@57 190 panel:SetLayout("None")
yellowfive@57 191 panel:SetTransparent()
yellowfive@57 192 panel:SetWidth(w)
yellowfive@57 193 panel:SetHeight(30)
yellowfive@57 194 scroll:AddChild(panel)
yellowfive@57 195
yellowfive@57 196 lbl = AceGUI:Create("AmrUiLabel")
yellowfive@124 197 panel:AddChild(lbl)
yellowfive@89 198 lbl:SetWidth(40)
yellowfive@57 199 lbl:SetWordWrap(false)
yellowfive@57 200 lbl:SetFont(Amr.CreateFont("Bold", 20, Amr.Colors.White))
yellowfive@57 201 lbl:SetText(count .. "x")
yellowfive@57 202 lbl:SetPoint("LEFT", panel.content, "LEFT")
yellowfive@57 203
yellowfive@57 204 local icon = AceGUI:Create("AmrUiIcon")
yellowfive@57 205 icon:SetBorderWidth(1)
yellowfive@57 206 icon:SetIconBorderColor(Amr.Colors.White)
yellowfive@57 207 icon:SetWidth(18)
yellowfive@57 208 icon:SetHeight(18)
yellowfive@124 209 panel:AddChild(icon)
yellowfive@57 210 icon:SetPoint("LEFT", lbl.frame, "RIGHT", 5, 0)
yellowfive@57 211
yellowfive@57 212 local btn = AceGUI:Create("AmrUiTextButton")
yellowfive@57 213 btn:SetWidth(w - 30 - 18 - 15)
yellowfive@57 214 btn:SetJustifyH("LEFT")
yellowfive@57 215 btn:SetWordWrap(false)
yellowfive@57 216 btn:SetFont(Amr.CreateFont("Bold", 14, Amr.Colors.White))
yellowfive@57 217 btn:SetHoverFont(Amr.CreateFont("Bold", 14, Amr.Colors.White))
yellowfive@57 218 btn:SetCallback("OnClick", onItemClick)
yellowfive@57 219 panel:AddChild(btn)
yellowfive@124 220 btn:SetPoint("LEFT", icon.frame, "RIGHT", 5, 0)
yellowfive@57 221
yellowfive@131 222 local item = Item:CreateFromItemID(itemId)
yellowfive@131 223 if item then
yellowfive@131 224 local itemLink = item:GetItemLink()
yellowfive@131 225 if itemLink then
yellowfive@131 226 icon:SetIcon(item:GetItemIcon())
yellowfive@131 227 btn:SetText(itemLink:gsub("%[", ""):gsub("%]", ""))
yellowfive@131 228 btn:SetUserData("itemName", item:GetItemName())
yellowfive@131 229 Amr:SetItemTooltip(btn, itemLink)
yellowfive@131 230 end
yellowfive@131 231 end
yellowfive@57 232 end
yellowfive@57 233
yellowfive@57 234 end
yellowfive@57 235
yellowfive@89 236 -- get the number of a specified gem/enchant/material that the player currently owns
yellowfive@57 237 local function getOwnedCount(itemId)
yellowfive@57 238 local ret = 0
yellowfive@57 239
yellowfive@57 240 local list = Amr.db.char.BagItemsAndCounts
yellowfive@57 241 if list and list[itemId] then
yellowfive@57 242 ret = ret + list[itemId]
yellowfive@57 243 end
yellowfive@57 244
yellowfive@124 245 local bankBags = Amr.db.char.BankItemsAndCounts
yellowfive@124 246 if bankBags then
yellowfive@124 247 for bagId,bagList in pairs(bankBags) do
yellowfive@124 248 if bagList[itemId] then
yellowfive@124 249 ret = ret + bagList[itemId]
yellowfive@124 250 end
yellowfive@124 251 end
yellowfive@57 252 end
yellowfive@57 253
yellowfive@57 254 return ret
yellowfive@57 255 end
yellowfive@57 256
yellowfive@89 257 local function removeOwned(list, owned)
yellowfive@89 258
yellowfive@89 259 for itemId, count in pairs(list) do
yellowfive@89 260 -- load up how many of an item we have
yellowfive@89 261 if not owned.loaded[itemId] then
yellowfive@89 262 owned.counts[itemId] = getOwnedCount(itemId)
yellowfive@89 263 owned.loaded[itemId] = true
yellowfive@89 264 end
yellowfive@89 265
yellowfive@89 266 -- see how many we can remove from the required count
yellowfive@89 267 local used = math.min(owned.counts[itemId], count)
yellowfive@89 268
yellowfive@89 269 -- update owned count so we can't double-use something
yellowfive@89 270 owned.counts[itemId] = owned.counts[itemId] - used;
yellowfive@89 271
yellowfive@89 272 -- reduce the requirement, removing entirely if we have it completely covered
yellowfive@89 273 list[itemId] = list[itemId] - used;
yellowfive@89 274 if list[itemId] == 0 then
yellowfive@89 275 list[itemId] = nil
yellowfive@89 276 end
yellowfive@89 277 end
yellowfive@89 278 end
yellowfive@89 279
yellowfive@89 280 function Amr:RefreshShoppingUi()
yellowfive@89 281
yellowfive@89 282 -- clear out any previous data
yellowfive@89 283 _panelContent:ReleaseChildren()
yellowfive@89 284
yellowfive@139 285 local parts = { strsplit("@", _selectedSetup) }
yellowfive@139 286
yellowfive@139 287 local data = Amr.db.global.Shopping2[parts[1]].setups[parts[2]]
yellowfive@89 288 if not data then
yellowfive@89 289 _panelContent:SetLayout("None")
yellowfive@89 290
yellowfive@89 291 local lbl = AceGUI:Create("AmrUiLabel")
yellowfive@124 292 _panelContent:AddChild(lbl)
yellowfive@89 293 lbl:SetFont(Amr.CreateFont("Italic", 18, Amr.Colors.TextTan))
yellowfive@89 294 lbl:SetText(L.ShopEmpty)
yellowfive@89 295 lbl:SetJustifyH("CENTER")
yellowfive@89 296 lbl:SetPoint("TOP", _panelContent.content, "TOP", 0, -30)
yellowfive@89 297 else
yellowfive@89 298 local allStuff = { gems = {}, enchants = {}, materials = {} }
yellowfive@89 299 local hasStuff = false
yellowfive@89 300 local visited = {}
yellowfive@89 301
yellowfive@139 302 for inventoryId, stuff in pairs(data) do
yellowfive@139 303 hasStuff = true
yellowfive@139 304 if not visited[inventoryId] then
yellowfive@139 305 if stuff.gems then
yellowfive@139 306 for itemId, count in pairs(stuff.gems) do
yellowfive@139 307 incrementTableItem(allStuff.gems, itemId, count)
yellowfive@89 308 end
yellowfive@89 309 end
yellowfive@89 310
yellowfive@139 311 if stuff.enchants then
yellowfive@139 312 for itemId, count in pairs(stuff.enchants) do
yellowfive@139 313 incrementTableItem(allStuff.enchants, itemId, count)
yellowfive@139 314 end
yellowfive@139 315 end
yellowfive@139 316
yellowfive@139 317 if stuff.materials then
yellowfive@139 318 for itemId, count in pairs(stuff.materials) do
yellowfive@139 319 incrementTableItem(allStuff.materials, itemId, count)
yellowfive@139 320 end
yellowfive@139 321 end
yellowfive@139 322
yellowfive@139 323 -- make sure not to count the same physical item twice
yellowfive@139 324 if inventoryId ~= -1 then
yellowfive@139 325 visited[inventoryId] = true
yellowfive@139 326 end
yellowfive@89 327 end
yellowfive@89 328 end
yellowfive@89 329
yellowfive@89 330 if hasStuff then
yellowfive@89 331 -- remove what we already own
yellowfive@89 332 local owned = { counts = {}, loaded = {} }
yellowfive@89 333 removeOwned(allStuff.gems, owned)
yellowfive@89 334 removeOwned(allStuff.enchants, owned)
yellowfive@89 335 removeOwned(allStuff.materials, owned)
yellowfive@89 336
yellowfive@89 337 _panelContent:SetLayout("Fill")
yellowfive@89 338
yellowfive@89 339 local scroll = AceGUI:Create("AmrUiScrollFrame")
yellowfive@89 340 scroll:SetLayout("List")
yellowfive@89 341 _panelContent:AddChild(scroll)
yellowfive@89 342
yellowfive@89 343 renderShopSection(allStuff.gems, scroll, L.ShopHeaderGems)
yellowfive@89 344 renderShopSection(allStuff.enchants, scroll, L.ShopHeaderEnchants)
yellowfive@89 345 renderShopSection(allStuff.materials, scroll, L.ShopHeaderMaterials)
yellowfive@89 346 end
yellowfive@89 347 end
yellowfive@89 348
yellowfive@89 349 end
yellowfive@89 350
yellowfive@124 351 -- compare gear to everything the player owns, and return the minimum gems/enchants/materials needed to optimize,
yellowfive@124 352 -- grouped by inventory ID so that we can combine multiple specs without double-counting
yellowfive@131 353 local function getShoppingData(player, gear)
yellowfive@89 354
yellowfive@89 355 local ret = {}
yellowfive@89 356
yellowfive@89 357 -- used to prevent considering the same item twice
yellowfive@89 358 local usedItems = {}
yellowfive@89 359
yellowfive@89 360 for slotId, optimalItem in pairs(gear) do
yellowfive@124 361 local matchItem = Amr:FindMatchingItem(optimalItem, player, usedItems)
yellowfive@89 362 local inventoryId = optimalItem.inventoryId or -1
yellowfive@89 363
yellowfive@89 364 -- find gem/enchant differences on the best-matching item
yellowfive@89 365
yellowfive@124 366 -- gems
yellowfive@124 367 if not optimalItem.relicBonusIds and (not matchItem or not matchItem.relicBonusIds) then
yellowfive@124 368 for i = 1, 3 do
yellowfive@89 369 local g = optimalItem.gemIds[i]
yellowfive@131 370 local isGemEquipped = g == 0 or (matchItem and matchItem.gemIds and matchItem.gemIds[i] == g)
yellowfive@89 371 if not isGemEquipped then
yellowfive@89 372 if not ret[inventoryId] then
yellowfive@89 373 ret[inventoryId] = { gems = {}, enchants = {}, materials = {} }
yellowfive@89 374 end
yellowfive@89 375 incrementTableItem(ret[inventoryId].gems, g, 1)
yellowfive@89 376 end
yellowfive@89 377 end
yellowfive@89 378 end
yellowfive@89 379
yellowfive@89 380 -- enchant
yellowfive@89 381 if optimalItem.enchantId and optimalItem.enchantId ~= 0 then
yellowfive@89 382 local e = optimalItem.enchantId
yellowfive@89 383 local isEnchantEquipped = matchItem and matchItem.enchantId and matchItem.enchantId == e
yellowfive@89 384
yellowfive@89 385 if not isEnchantEquipped then
yellowfive@124 386 -- enchant info
yellowfive@124 387 local enchInfo = Amr.db.char.ExtraEnchantData[e]
yellowfive@89 388 if enchInfo then
yellowfive@89 389 if not ret[inventoryId] then
yellowfive@89 390 ret[inventoryId] = { gems = {}, enchants = {}, materials = {} }
yellowfive@89 391 end
yellowfive@89 392 incrementTableItem(ret[inventoryId].enchants, enchInfo.itemId, 1)
yellowfive@89 393
yellowfive@89 394 if enchInfo.materials then
yellowfive@89 395 for k, v in pairs(enchInfo.materials) do
yellowfive@89 396 incrementTableItem(ret[inventoryId].materials, k, v)
yellowfive@89 397 end
yellowfive@89 398 end
yellowfive@89 399 end
yellowfive@89 400 end
yellowfive@89 401 end
yellowfive@89 402 end
yellowfive@89 403
yellowfive@89 404 return ret
yellowfive@89 405 end
yellowfive@89 406
yellowfive@139 407 -- look at all gear sets and find stuff that a player needs to acquire to gem/enchant their gear for each setup
yellowfive@57 408 function Amr:UpdateShoppingData(player)
yellowfive@57 409
yellowfive@57 410 local required = {
yellowfive@139 411 setups = {}
yellowfive@57 412 }
yellowfive@139 413
yellowfive@139 414 for i, setup in ipairs(Amr.db.char.GearSetups) do
yellowfive@139 415 local gear = setup.Gear
yellowfive@124 416 if gear then
yellowfive@139 417 required.setups[setup.Label] = getShoppingData(player, gear)
yellowfive@124 418 end
yellowfive@57 419 end
yellowfive@139 420
yellowfive@139 421 Amr.db.global.Shopping2[player.Name .. "-" .. player.Realm] = required
yellowfive@57 422 end
yellowfive@57 423
yellowfive@57 424 Amr:AddEventHandler("AUCTION_HOUSE_SHOW", function()
yellowfive@57 425 _isAhOpen = true
yellowfive@57 426 if Amr.db.profile.options.shopAh then
yellowfive@57 427 Amr:ShowShopWindow()
yellowfive@57 428 end
yellowfive@57 429 end)
yellowfive@57 430
yellowfive@57 431 Amr:AddEventHandler("AUCTION_HOUSE_CLOSED", function()
yellowfive@57 432 _isAhOpen = false
yellowfive@57 433 if Amr.db.profile.options.shopAh then
yellowfive@57 434 Amr:HideShopWindow()
yellowfive@57 435 end
yellowfive@122 436 end)