diff --git a/TradeSkillMaster/Auction/AuctionControl.lua b/TradeSkillMaster/Auction/AuctionControl.lua index a68e17f..2f01543 100644 --- a/TradeSkillMaster/Auction/AuctionControl.lua +++ b/TradeSkillMaster/Auction/AuctionControl.lua @@ -15,6 +15,7 @@ TSMAPI:RegisterForTracing(private, "TradeSkillMaster.AuctionControl_private") LibStub("AceEvent-3.0"):Embed(private) private.matchList = {} private.currentPage = {} +private.shoppingPurchasedCounts = {} local function GetNumInBags(baseItemString) @@ -43,6 +44,57 @@ local function ValidateAuction(index, list) return count == private.currentAuction.count and buyout == private.currentAuction.buyout and itemString == private.currentAuction.itemString, data end +local function GetShoppingTotalQuantity(itemString) + itemString = TSMAPI:GetBaseItemString(itemString, true) or itemString + local player, alts = TSMAPI:ModuleAPI("ItemTracker", "playertotal", itemString) + if not player then + alts = nil + end + player = player or 0 + alts = alts or 0 + local guild = TSMAPI:ModuleAPI("ItemTracker", "guildtotal", itemString) or 0 + local auctions = TSMAPI:ModuleAPI("ItemTracker", "auctionstotal", itemString) or 0 + local bought = private.shoppingPurchasedCounts[itemString] or 0 + return player + alts + guild + auctions + bought +end + +local function GetOperationMaxPrice(opSettings, itemString) + if not opSettings or opSettings.maxPrice == nil then return end + if type(opSettings.maxPrice) == "number" then + return opSettings.maxPrice + elseif type(opSettings.maxPrice) == "string" then + local func = TSMAPI:ParseCustomPrice(opSettings.maxPrice) + return func and func(itemString) or nil + end +end + +local function ShoppingBuyoutAllowed(itemString, count, perItemBuyout) + local operations = TSMAPI:GetItemOperation(itemString, "Shopping") + if not operations or #operations == 0 then return true end + itemString = TSMAPI:GetBaseItemString(itemString, true) or itemString + for _, opName in ipairs(operations) do + TSMAPI:UpdateOperation("Shopping", opName) + local opSettings = TSM.operations["Shopping"] and TSM.operations["Shopping"][opName] + if opSettings then + local maxRestock = tonumber(opSettings.maxRestock) or 0 + local totalQty = GetShoppingTotalQuantity(itemString) + if maxRestock > 0 and (totalQty + count) > maxRestock then + -- over restock for 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 opMaxPrice = GetOperationMaxPrice(opSettings, itemString) + if opMaxPrice and perItemBuyout and perItemBuyout > 0 and perItemBuyout <= opMaxPrice then + return true + end + end + end + end + return false +end + local diffFrame = CreateFrame("Frame") diffFrame:Hide() diffFrame.num = 0 @@ -146,6 +198,15 @@ end function private:DoBuyout() if private.isSearching or not private.currentAuction or not private.confirmationFrame:IsVisible() then return end + local perItemBuyout = private.currentAuction.buyout and private.currentAuction.count and (private.currentAuction.buyout / private.currentAuction.count) + if TSM.groupBuyoutCheck and (not perItemBuyout or not TSM.groupBuyoutCheck(private.currentAuction.itemString, private.currentAuction.count, perItemBuyout)) then + TSM:Print("Max restock quantity reached for this item.") + return + end + if private.module == "Shopping" and not ShoppingBuyoutAllowed(private.currentAuction.itemString, private.currentAuction.count, perItemBuyout) then + TSM:Print("Max restock quantity reached for this item.") + return + end for i=#private.matchList, 1, -1 do local aucIndex = private.matchList[i] @@ -275,6 +336,10 @@ function private:AUCTION_ITEM_LIST_UPDATE() TSMAPI.AuctionScan:CacheRemove(prevAuction.itemString, private.currentCacheIndex) private.currentCacheIndex = nil end + if private.module == "Shopping" and prevAuction.itemString and prevAuction.count then + local baseItemString = TSMAPI:GetBaseItemString(prevAuction.itemString, true) + private.shoppingPurchasedCounts[baseItemString] = (private.shoppingPurchasedCounts[baseItemString] or 0) + prevAuction.count + end TSM:AuctionControlCallback("OnBuyout", prevAuction) TSMAPI:FireEvent("TSM:AUCTIONCONTROL:ITEMBOUGHT", prevAuction) @@ -312,6 +377,9 @@ function TSMAPI.AuctionControl:ShowControlButtons(parent, rt, callback, module, private.rt = rt private.callback = callback private.module = module + if module == "Shopping" then + wipe(private.shoppingPurchasedCounts) + end private.postBidPercent = postBidPercent private.postUndercut = postUndercut return private.controlButtons @@ -348,6 +416,17 @@ function private:ShowConfirmationWindow() end private:SetCurrentAuction(private.rt:GetSelectedAuction()) if not private.currentAuction then return end + if private.confirmationMode == "Buyout" then + local perItemBuyout = private.currentAuction.buyout and private.currentAuction.count and (private.currentAuction.buyout / private.currentAuction.count) + if TSM.groupBuyoutCheck and (not perItemBuyout or not TSM.groupBuyoutCheck(private.currentAuction.itemString, private.currentAuction.count, perItemBuyout)) then + TSM:Print("Max restock quantity reached for this item.") + return + end + if private.module == "Shopping" and not ShoppingBuyoutAllowed(private.currentAuction.itemString, private.currentAuction.count, perItemBuyout) then + TSM:Print("Max restock quantity reached for this item.") + return + end + end private:RegisterEvent("AUCTION_ITEM_LIST_UPDATE") diffFrame.num = 0 diff --git a/TradeSkillMaster/TradeSkillMaster.toc b/TradeSkillMaster/TradeSkillMaster.toc index cc5e2db..e17b611 100644 --- a/TradeSkillMaster/TradeSkillMaster.toc +++ b/TradeSkillMaster/TradeSkillMaster.toc @@ -2,7 +2,7 @@ ## Title: |cff00fe00TradeSkillMaster: Revived|r ## Notes: Core addon for the TradeSkillMaster suite, revived for Wrath of the Lich King. Does nothing without modules installed. ## Author: Sapu94, Bart39, Gnomezilla [Warmane-Icecrown(A)], BlueAo [Warmane], andrew6180, Yoshiyuka, DimaSheiko, and other contributors... -## Version: 2.3.33 +## Version: 2.3.34 ## SavedVariables: TradeSkillMasterAppDB, AscensionTSMDB ## OptionalDeps: AccurateTime, Ace3, LibDataBroker-1.1, LibDBIcon-1.0, LibExtraTip, TipHelper, LibParse, LibCompress, LibGraph-2.0, SharedMedia, TheUndermineJournal, TheUndermineJournalGE ## X-Embeds: AccurateTime, Ace3, LibDataBroker-1.1, LibDBIcon-1.0, LibExtraTip, TipHelper, LibParse, LibCompress, LibGraph-2.0 diff --git a/TradeSkillMaster_AuctionDB/TradeSkillMaster_AuctionDB.toc b/TradeSkillMaster_AuctionDB/TradeSkillMaster_AuctionDB.toc index 5fa20f7..1348b19 100644 --- a/TradeSkillMaster_AuctionDB/TradeSkillMaster_AuctionDB.toc +++ b/TradeSkillMaster_AuctionDB/TradeSkillMaster_AuctionDB.toc @@ -2,10 +2,10 @@ ## Title: |cff00ff00TradeSkillMaster_AuctionDB|r ## Notes: Stores auction house data and calculates market prices. ## Author: Sapu94, Bart39 -## Version: 2.3.33 +## Version: 2.3.34 ## SavedVariables: AscensionTSM_AuctionDB ## Dependency: TradeSkillMaster -## X-Curse-Packaged-Version: 2.3.33 +## X-Curse-Packaged-Version: 2.3.34 ## X-Curse-Project-Name: TradeSkillMaster_AuctionDB ## X-Curse-Project-ID: tradeskillmaster_auctiondb ## X-Curse-Repository-ID: wow/tradeskillmaster_auctiondb/mainline diff --git a/TradeSkillMaster_Shopping/Locale/enUS.lua b/TradeSkillMaster_Shopping/Locale/enUS.lua index 0a5d54e..21ac177 100644 --- a/TradeSkillMaster_Shopping/Locale/enUS.lua +++ b/TradeSkillMaster_Shopping/Locale/enUS.lua @@ -81,6 +81,8 @@ L["Max Disenchant Level"] = true L["Max Disenchant Search Percent"] = true L["Max Shopping Price:"] = true L["Maximum Auction Price (per item)"] = true +L["Max Restock Quantity"] = true +L["If set above 0, Shopping will only buy up to this total quantity for items in groups using this operation."] = true L["Maximum Quantity to Buy:"] = true L["Maximum quantity purchased for %s."] = true L["Maximum quantity purchased for destroy search."] = true @@ -159,4 +161,4 @@ L["Warning: The max disenchant level must be higher than the min disenchant leve L["Warning: The min disenchant level must be lower than the max disenchant level."] = true L["What to set the default undercut to when posting items with Shopping."] = true L["When in destroy mode, you simply enter a target item (ink/pigment, enchanting mat, gem, etc) into the search box to search for everything you can destroy to get that item."] = true -L["When in normal mode, you may run simple and filtered searches of the auction house."] = true \ No newline at end of file +L["When in normal mode, you may run simple and filtered searches of the auction house."] = true diff --git a/TradeSkillMaster_Shopping/TradeSkillMaster_Shopping.lua b/TradeSkillMaster_Shopping/TradeSkillMaster_Shopping.lua index 6d3322f..7cd9e19 100644 --- a/TradeSkillMaster_Shopping/TradeSkillMaster_Shopping.lua +++ b/TradeSkillMaster_Shopping/TradeSkillMaster_Shopping.lua @@ -68,6 +68,7 @@ end TSM.operationDefaults = { maxPrice = 1, + maxRestock = 0, evenStacks = nil, showAboveMaxPrice = nil, ignorePlayer = {}, @@ -125,6 +126,19 @@ function TSM:GetMaxPrice(operationPrice, itemString) return price ~= 0 and price or nil, err end +function TSM:GetTotalQuantity(itemString) + itemString = TSMAPI:GetBaseItemString(itemString, true) or itemString + local player, alts = TSMAPI:ModuleAPI("ItemTracker", "playertotal", itemString) + if not player then + alts = nil + end + player = player or 0 + alts = alts or 0 + local guild = TSMAPI:ModuleAPI("ItemTracker", "guildtotal", itemString) or 0 + local auctions = TSMAPI:ModuleAPI("ItemTracker", "auctionstotal", itemString) or 0 + return player + alts + guild + auctions +end + function TSM:AddSidebarFeature(...) TSM.modules.Sidebar:AddSidebarFeature(...) end @@ -137,9 +151,16 @@ function TSM:GetTooltip(itemString) local operations = TSMAPI:GetItemOperation(itemString, "Shopping") if not operations then return end local maxPrice + local totalQty = TSM:GetTotalQuantity(itemString) or 0 for _, operationName in ipairs(operations) do TSMAPI:UpdateOperation("Shopping", operationName) local operation = TSM.operations[operationName] + if operation then + local maxRestock = tonumber(operation.maxRestock) or 0 + if maxRestock > 0 and totalQty >= maxRestock then + operation = nil + end + end if operation then local price = TSM:GetMaxPrice(operation.maxPrice, itemString) if price then diff --git a/TradeSkillMaster_Shopping/modules/Options.lua b/TradeSkillMaster_Shopping/modules/Options.lua index bbb02b2..032b154 100644 --- a/TradeSkillMaster_Shopping/modules/Options.lua +++ b/TradeSkillMaster_Shopping/modules/Options.lua @@ -305,6 +305,18 @@ function Options:DrawOperationGeneral(container, operationName) disabled = operation.relationships.maxprice, tooltip = L["The highest price per item you will pay for items in affected by this operation."], }, + { + type = "Slider", + settingInfo = { operation, "maxRestock" }, + label = L["Max Restock Quantity"], + disabled = operation.relationships.maxRestock, + isPercent = false, + min = 0, + max = 5000, + step = 1, + relativeWidth = 0.49, + tooltip = L["If set above 0, Shopping will only buy up to this total quantity for items in groups using this operation."], + }, { type = "CheckBox", label = L["Show Auctions Above Max Price"], @@ -333,6 +345,7 @@ function Options:DrawOperationRelationships(container, operationName) { label = L["General Settings"], { key = "maxPrice", label = L["Maximum Auction Price (per item)"] }, + { key = "maxRestock", label = L["Max Restock Quantity"] }, { key = "showAboveMaxPrice", label = L["Show Auctions Above Max Price"] }, { key = "evenStacks", label = L["Even (5/10/15/20) Stacks Only"] }, }, @@ -359,4 +372,4 @@ function Options:LoadTooltipOptions(container) } TSMAPI:BuildPage(container, page) -end \ No newline at end of file +end diff --git a/TradeSkillMaster_Shopping/modules/Util.lua b/TradeSkillMaster_Shopping/modules/Util.lua index 93656f4..38c25c3 100644 --- a/TradeSkillMaster_Shopping/modules/Util.lua +++ b/TradeSkillMaster_Shopping/modules/Util.lua @@ -91,6 +91,12 @@ function private:CreateSearchFrame() private.controlButtons.cancel:Disable() private.controlButtons.post:Disable() end + if private.controlButtons.buyout:IsEnabled() and TSM.groupBuyoutCheck then + local perItemBuyout = data.auctionRecord.GetItemBuyout and data.auctionRecord:GetItemBuyout() or data.auctionRecord.buyout + if not TSM.groupBuyoutCheck(data.itemString, data.auctionRecord.count, perItemBuyout) then + private.controlButtons.buyout:Disable() + end + end if private:HasInBags(TSMAPI:GetBaseItemString(data.itemString)) then private.controlButtons.post:Enable() end @@ -143,6 +149,8 @@ function Util:HideSearchFrame(forceStop) TSMAPI.AuctionControl:HideControlButtons() TSMAPI.AuctionScan:StopScan(forceStop ~= false) TSMAPI.AuctionScan:ClearCache() + TSM.searchCallback = nil + TSM.groupBuyoutCheck = nil end diff --git a/TradeSkillMaster_Shopping/sidebar/Groups.lua b/TradeSkillMaster_Shopping/sidebar/Groups.lua index 96edec9..be92ddb 100644 --- a/TradeSkillMaster_Shopping/sidebar/Groups.lua +++ b/TradeSkillMaster_Shopping/sidebar/Groups.lua @@ -1,16 +1,21 @@ local TSM = select(2, ...) local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_Shopping") -- loads the localization table -local private = {itemOperations={}} +local private = {itemOperations={}, purchasedCounts={}} local function GetActiveOperations(itemString, operations) if not operations or #operations == 0 then return end + local totalQty = TSM:GetTotalQuantity(itemString) or 0 + totalQty = totalQty + (private.purchasedCounts[itemString] or 0) local active = {} for _, opName in ipairs(operations) do TSMAPI:UpdateOperation("Shopping", opName) local opSettings = TSM.operations[opName] if opSettings then - tinsert(active, opSettings) + local maxRestock = tonumber(opSettings.maxRestock) or 0 + if maxRestock <= 0 or totalQty < maxRestock then + tinsert(active, opSettings) + end end end return active @@ -81,8 +86,13 @@ function private.ScanCallback(event, ...) if not operations or #operations == 0 then return end auctionItem:FilterRecords(function(record) local buyout = record:GetItemBuyout() or 0 + local totalQty = TSM:GetTotalQuantity(itemString) or 0 + totalQty = totalQty + (private.purchasedCounts[itemString] or 0) for _, operation in ipairs(operations) do - if operation.evenStacks and record.count % 5 ~= 0 then + local maxRestock = tonumber(operation.maxRestock) or 0 + if maxRestock > 0 and (totalQty + record.count) > maxRestock then + -- this operation can't accept more of this item + elseif operation.evenStacks and record.count % 5 ~= 0 then -- not an even stack for this operation elseif operation.showAboveMaxPrice then return false @@ -113,6 +123,39 @@ end function private.StartScan() TSMAPI:FireEvent("SHOPPING:GROUPS:STARTSCAN") wipe(private.itemOperations) + wipe(private.purchasedCounts) + 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) + 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 + for _, operation in ipairs(operations) do + local maxRestock = tonumber(operation.maxRestock) or 0 + local totalQty = TSM:GetTotalQuantity(itemString) or 0 + totalQty = totalQty + (private.purchasedCounts[itemString] or 0) + if maxRestock > 0 and (totalQty + count) > maxRestock then + -- doesn't fit this operation + elseif operation.evenStacks and count % 5 ~= 0 then + -- not an even stack for this operation + elseif operation.showAboveMaxPrice then + return true + else + local operationPrice = TSM:GetMaxPrice(operation.maxPrice, itemString) + 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 @@ -129,8 +172,12 @@ function private.StartScan() if err then TSM:Printf(L["Invalid custom price source for %s. %s"], TSMAPI:GetSafeItemInfo(itemString) or itemString, err) else - private.itemOperations[baseItemString] = private.itemOperations[baseItemString] or {} - tinsert(private.itemOperations[baseItemString], opName) + local totalQty = TSM:GetTotalQuantity(itemString) or 0 + 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 diff --git a/TradeSkillMaster_Shopping/sidebar/Other.lua b/TradeSkillMaster_Shopping/sidebar/Other.lua index 15b9e24..5372cfd 100644 --- a/TradeSkillMaster_Shopping/sidebar/Other.lua +++ b/TradeSkillMaster_Shopping/sidebar/Other.lua @@ -230,14 +230,20 @@ function private.SniperScanCallback(event, itemString, auctionItem) vendorPrice = vendor end local operations = TSMAPI:GetItemOperation(itemString, "Shopping") + local totalQty = TSM:GetTotalQuantity(itemString) or 0 for _, opName in ipairs(operations or {}) do TSMAPI:UpdateOperation("Shopping", opName) local opSettings = TSM.operations[opName] if opSettings and opSettings.maxPrice then + local maxRestock = tonumber(opSettings.maxRestock) or 0 + if maxRestock > 0 and totalQty >= maxRestock then + -- skip this operation if we're already restocked + else local opPrice = TSM:GetMaxPrice(opSettings.maxPrice, itemString) if opPrice then maxPrice = maxPrice and max(maxPrice, opPrice) or opPrice end + end end end customPrice = TSM:GetMaxPrice(TSM.db.global.sniperCustomPrice, itemString)