local TSM = select(2, ...) local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_Shopping") -- loads the localization table local private = {itemOperations={}, purchasedCounts={}, itemQuantities={}, operationSettings={}, itemOperationSettings={}, itemOperationMaxPrices={}} local function GetCachedItemQuantity(itemString) local quantity = private.itemQuantities[itemString] if quantity == nil then local totalQty = TSM:GetTotalQuantity(itemString) or 0 totalQty = totalQty + (private.purchasedCounts[itemString] or 0) private.itemQuantities[itemString] = totalQty quantity = totalQty end return quantity end local function GetOperationSettings(opName) local settings = private.operationSettings[opName] if settings == nil then TSMAPI:UpdateOperation("Shopping", opName) settings = TSM.operations[opName] private.operationSettings[opName] = settings or false end return settings ~= false and settings or nil end local function GetOperationMaxPrice(itemString, opName, opSettings) private.itemOperationMaxPrices[itemString] = private.itemOperationMaxPrices[itemString] or {} local cached = private.itemOperationMaxPrices[itemString][opName] if cached ~= nil then return cached ~= false and cached or nil end local operationPrice = TSM:GetMaxPrice(opSettings.maxPrice, itemString) private.itemOperationMaxPrices[itemString][opName] = operationPrice or false return operationPrice end local function GetItemOperationSettings(itemString, operations) local settingsList = private.itemOperationSettings[itemString] if settingsList then return settingsList end settingsList = {} for _, opName in ipairs(operations or {}) do local opSettings = GetOperationSettings(opName) if opSettings then tinsert(settingsList, {name=opName, settings=opSettings}) end end private.itemOperationSettings[itemString] = settingsList return settingsList end local function GetActiveOperations(itemString, operations) if not operations or #operations == 0 then return end local totalQty = GetCachedItemQuantity(itemString) local active = {} for _, opEntry in ipairs(GetItemOperationSettings(itemString, operations)) do local opSettings = opEntry.settings if opSettings then local maxRestock = tonumber(opSettings.maxRestock) or 0 if maxRestock <= 0 or totalQty < maxRestock then tinsert(active, opEntry) end end end return active end function private.Create(parent) local frame = CreateFrame("Frame", nil, parent) frame:SetAllPoints() local stContainer = CreateFrame("Frame", nil, frame) stContainer:SetPoint("TOPLEFT", 0, -35) stContainer:SetPoint("BOTTOMRIGHT", 0, 30) TSMAPI.Design:SetFrameColor(stContainer) frame.groupTree = TSMAPI:CreateGroupTree(stContainer, "Shopping", "Shopping_AH") private.groupTree = frame.groupTree local helpText = TSMAPI.GUI:CreateLabel(frame) helpText:SetPoint("TOPLEFT") helpText:SetPoint("TOPRIGHT") helpText:SetHeight(35) helpText:SetJustifyH("CENTER") helpText:SetJustifyV("CENTER") helpText:SetText(L["Select the groups which you would like to include in the search."]) frame.helpText = helpText local startBtn = TSMAPI.GUI:CreateButton(frame, 16) startBtn:SetPoint("BOTTOMLEFT", 3, 3) startBtn:SetPoint("BOTTOMRIGHT", -3, 3) startBtn:SetHeight(20) startBtn:SetText(L["Start Search"]) startBtn:SetScript("OnClick", private.StartScan) frame.startBtn = startBtn return frame end function private.ScanCallback(event, ...) if event == "filter" then local filter = ... local maxPrice for _, itemString in ipairs(filter.items) do local operations = GetActiveOperations(itemString, private.itemOperations[itemString]) if not operations or #operations == 0 then return end local itemMaxPrice for _, opEntry in ipairs(operations) do local opSettings = opEntry.settings if opSettings.showAboveMaxPrice then itemMaxPrice = nil break end local operationPrice = GetOperationMaxPrice(itemString, opEntry.name, opSettings) if operationPrice then itemMaxPrice = itemMaxPrice and max(itemMaxPrice, operationPrice) or operationPrice end end if itemMaxPrice == nil then maxPrice = nil break end if not itemMaxPrice then return end maxPrice = maxPrice and max(maxPrice, itemMaxPrice) or itemMaxPrice end return maxPrice elseif event == "process" then local itemString, auctionItem = ... -- filter out auctions according to operation settings itemString = TSMAPI:GetBaseItemString(itemString, true) local operations = GetActiveOperations(itemString, private.itemOperations[itemString]) if not operations or #operations == 0 then return end local totalQty = GetCachedItemQuantity(itemString) auctionItem:FilterRecords(function(record) local buyout = record:GetItemBuyout() or 0 for _, opEntry in ipairs(operations) do local opSettings = opEntry.settings local maxRestock = tonumber(opSettings.maxRestock) or 0 if maxRestock > 0 and (totalQty + record.count) > maxRestock then -- this operation can't accept more of this item elseif opSettings.evenStacks and record.count % 5 ~= 0 then -- not an even stack for this operation elseif opSettings.showAboveMaxPrice then return false else local operationPrice = GetOperationMaxPrice(itemString, opEntry.name, opSettings) if operationPrice and buyout <= operationPrice then return false end end end return true end) local marketValue for _, opEntry in ipairs(operations) do local operationPrice = GetOperationMaxPrice(itemString, opEntry.name, opEntry.settings) if operationPrice then marketValue = marketValue and max(marketValue, operationPrice) or operationPrice end end auctionItem:SetMarketValue(marketValue) return auctionItem elseif event == "done" then TSM.Search:SetSearchBarDisabled(false) return end end function private.StartScan() TSMAPI:FireEvent("SHOPPING:GROUPS:STARTSCAN") TSM.shoppingGroupSearchActive = true wipe(private.itemOperations) wipe(private.purchasedCounts) private.itemQuantities = {} private.operationSettings = {} private.itemOperationSettings = {} private.itemOperationMaxPrices = {} TSM.searchCallback = function(event, auction) if event == "OnBuyout" and auction then local linkItemString = auction.link and TSMAPI:GetItemString(auction.link) if linkItemString then local itemString = TSMAPI:GetBaseItemString(linkItemString, true) private.purchasedCounts[itemString] = (private.purchasedCounts[itemString] or 0) + (auction.count or 0) private.itemQuantities[itemString] = nil end end end TSM.groupBuyoutCheck = function(itemString, count, buyoutPerItem) itemString = TSMAPI:GetBaseItemString(itemString, true) local operations = GetActiveOperations(itemString, private.itemOperations[itemString]) if not operations or #operations == 0 then return false end local totalQty = GetCachedItemQuantity(itemString) for _, opEntry in ipairs(operations) do local opSettings = opEntry.settings local maxRestock = tonumber(opSettings.maxRestock) or 0 if maxRestock > 0 and (totalQty + count) > maxRestock then -- doesn't fit this operation elseif opSettings.evenStacks and count % 5 ~= 0 then -- not an even stack for this operation elseif opSettings.showAboveMaxPrice then return true else local operationPrice = GetOperationMaxPrice(itemString, opEntry.name, opSettings) if operationPrice and buyoutPerItem and buyoutPerItem > 0 and buyoutPerItem <= operationPrice then return true end end end return false end for groupName, data in pairs(private.groupTree:GetSelectedGroupInfo()) do groupName = TSMAPI:FormatGroupPath(groupName, true) for _, opName in ipairs(data.operations) do TSMAPI:UpdateOperation("Shopping", opName) local opSettings = TSM.operations[opName] if not opSettings then -- operation doesn't exist anymore in Auctioning TSM:Printf(L["'%s' has a Shopping operation of '%s' which no longer exists. Shopping will ignore this group until this is fixed."], groupName, opName) else -- it's a valid operation for itemString in pairs(data.items) do local baseItemString = TSMAPI:GetBaseItemString(itemString, true) or itemString local _, err = TSM:GetMaxPrice(opSettings.maxPrice, itemString) if err then TSM:Printf(L["Invalid custom price source for %s. %s"], TSMAPI:GetSafeItemInfo(itemString) or itemString, err) else local totalQty = GetCachedItemQuantity(itemString) local maxRestock = tonumber(opSettings.maxRestock) or 0 if maxRestock <= 0 or totalQty < maxRestock then private.itemOperations[baseItemString] = private.itemOperations[baseItemString] or {} tinsert(private.itemOperations[baseItemString], opName) end end end end end end local itemList = {} for itemString in pairs(private.itemOperations) do tinsert(itemList, itemString) end TSM.Search:SetSearchBarDisabled(true) TSM.Util:ShowSearchFrame(nil, L["% Max Price"]) TSM.Util:StartItemScan(itemList, private.ScanCallback) end do TSM:AddSidebarFeature(L["TSM Groups"], private.Create) end