Compare commits

...

8 Commits

  1. 2
      README.md
  2. 79
      TradeSkillMaster/Auction/AuctionControl.lua
  3. 1
      TradeSkillMaster/Auction/AuctionFrame.lua
  4. 6
      TradeSkillMaster/TradeSkillMaster.lua
  5. 2
      TradeSkillMaster/TradeSkillMaster.toc
  6. 4
      TradeSkillMaster_Accounting/Locale/enUS.lua
  7. 73
      TradeSkillMaster_Accounting/TradeSkillMaster_Accounting.lua
  8. 4
      TradeSkillMaster_AuctionDB/TradeSkillMaster_AuctionDB.toc
  9. 21
      TradeSkillMaster_Auctioning/modules/GUI.lua
  10. 29
      TradeSkillMaster_Crafting/Modules/CraftingGUI.lua
  11. 2
      TradeSkillMaster_Crafting/Modules/Gather.lua
  12. 24
      TradeSkillMaster_Crafting/Modules/Queue.lua
  13. 2
      TradeSkillMaster_Crafting/TradeSkillMaster_Crafting.lua
  14. 106
      TradeSkillMaster_Mailing/Modules/Inbox.lua
  15. 1
      TradeSkillMaster_Mailing/TradeSkillMaster_Mailing.lua
  16. 2
      TradeSkillMaster_Shopping/Locale/enUS.lua
  17. 52
      TradeSkillMaster_Shopping/TradeSkillMaster_Shopping.lua
  18. 13
      TradeSkillMaster_Shopping/modules/Options.lua
  19. 8
      TradeSkillMaster_Shopping/modules/Util.lua
  20. 120
      TradeSkillMaster_Shopping/sidebar/Groups.lua
  21. 18
      TradeSkillMaster_Shopping/sidebar/Other.lua

2
README.md

@ -43,6 +43,8 @@ TSM price strings can reference built-in data sources and combine them with math
### Accounting sources
- `avgSell`: Average sell price.
- `avgBuy`: Average buy price.
- `smartAvgBuy`: Smart average buy price (uses current holdings to weight recent buys).
- `lastBuy`: Last buy price (most recent purchase; also shown under Last Purchased in Accounting tooltips).
- `maxSell`: Maximum sell price.
- `maxBuy`: Maximum buy price.
- `minSell`: Minimum sell price.

79
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

1
TradeSkillMaster/Auction/AuctionFrame.lua

@ -43,6 +43,7 @@ function private:CreateTSMAHTab(moduleName, callbackShow, callbackHide)
tab:SetText(TSMAPI.Design:GetInlineColor("link2")..moduleName.."|r")
tab:SetNormalFontObject(GameFontHighlightSmall)
tab.isTSMTab = moduleName
_G["TSM"..moduleName.."TabButton"] = tab
tab:SetPoint("LEFT", _G["AuctionFrameTab"..n-1], "RIGHT", -8, 0)
tab:Show()
PanelTemplates_SetNumTabs(AuctionFrame, n)

6
TradeSkillMaster/TradeSkillMaster.lua

@ -735,6 +735,12 @@ function TSMAPI:GetItemPrices(itemLink)
-- Use TSM:GetCustomPrice to get avgBuy price
prices.avgBuy = TSM:GetCustomPrice("avgBuy", itemString) or 0
-- Use TSM:GetCustomPrice to get smartAvgBuy price
prices.smartAvgBuy = TSM:GetCustomPrice("smartAvgBuy", itemString) or 0
-- Use TSM:GetCustomPrice to get lastBuy price
prices.lastBuy = TSM:GetCustomPrice("lastBuy", itemString) or 0
-- Use TSM:GetCustomPrice to get avgSell price
prices.avgSell = TSM:GetCustomPrice("avgSell", itemString) or 0

2
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.24
## Version: 2.3.39
## 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

4
TradeSkillMaster_Accounting/Locale/enUS.lua

@ -24,6 +24,10 @@ L["Average Prices:"] = true
L["Avg Buy Price"] = true
L["Avg Resale Profit"] = true
L["Avg Sell Price"] = true
L["Last Buy Price"] = true
L["Last Buy Price:"] = true
L["Smart Avg Buy Price:"] = true
L["Smart Avg Buy Price"] = true
L["Back to Previous Page"] = true
L["Balance"] = true
L["Below is a graph of the your character's gold on hand over time.\n\nThe x-axis is time and goes from %s to %s\nThe y-axis is gold."] = true

73
TradeSkillMaster_Accounting/TradeSkillMaster_Accounting.lua

@ -125,6 +125,8 @@ function TSM:RegisterModule()
TSM.priceSources = {
{ key = "avgSell", label = L["Avg Sell Price"], callback = "GetAvgSellPrice" },
{ key = "avgBuy", label = L["Avg Buy Price"], callback = "GetAvgBuyPrice" },
{ key = "smartAvgBuy", label = L["Smart Avg Buy Price"], callback = "GetSmartAvgBuyPrice" },
{ key = "lastBuy", label = L["Last Buy Price"], callback = "GetLastBuyPrice" },
{ key = "maxSell", label = L["Max Sell Price"], callback = "GetMaxSellPrice" },
{ key = "maxBuy", label = L["Max Buy Price"], callback = "GetMaxBuyPrice" },
{ key = "minSell", label = L["Min Sell Price"], callback = "GetMinSellPrice" },
@ -187,6 +189,7 @@ function TSM:GetTooltip(itemString)
if TSM.db.realm.tooltip.purchase and TSM.items[itemString] and #TSM.items[itemString].buys > 0 then
local lastPurchased = TSM.items[itemString].buys[#TSM.items[itemString].buys].time
local lastBuyPrice = TSM:GetLastBuyPrice(itemString)
local totalPrice, totalNum = 0, 0
for _, record in ipairs(TSM.items[itemString].buys) do
totalNum = totalNum + record.quantity
@ -202,15 +205,30 @@ function TSM:GetTooltip(itemString)
else
local avgPrice = TSM:GetAvgBuyPrice(itemString)
local maxPrice = TSM:GetMaxBuyPrice(itemString)
local smartAvgPrice = TSM:GetSmartAvgBuyPrice(itemString)
if moneyCoinsTooltip then
tinsert(text, { left = " " .. L["Purchased (Avg/Max Price):"], right = format("%s (%s / %s)", "|cffffffff" .. totalNum .. "|r", (TSMAPI:FormatTextMoneyIcon(avgPrice, "|cffffffff", true) or ("|cffffffff" .. "?")), (TSMAPI:FormatTextMoneyIcon(maxPrice, "|cffffffff", true) or ("|cffffffff" .. "?"))) })
else
tinsert(text, { left = " " .. L["Purchased (Avg/Max Price):"], right = format("%s (%s / %s)", "|cffffffff" .. totalNum .. "|r", (TSMAPI:FormatTextMoney(avgPrice, "|cffffffff", true) or ("|cffffffff" .. "?")), (TSMAPI:FormatTextMoney(maxPrice, "|cffffffff", true) or ("|cffffffff" .. "?"))) })
end
if smartAvgPrice then
if moneyCoinsTooltip then
tinsert(text, { left = " " .. L["Smart Avg Buy Price:"], right = (TSMAPI:FormatTextMoneyIcon(smartAvgPrice, "|cffffffff", true) or ("|cffffffff" .. "?")) })
else
tinsert(text, { left = " " .. L["Smart Avg Buy Price:"], right = (TSMAPI:FormatTextMoney(smartAvgPrice, "|cffffffff", true) or ("|cffffffff" .. "?")) })
end
end
end
if lastPurchased then
local timeDiff = SecondsToTime(time() - lastPurchased)
tinsert(text, { left = " " .. L["Last Purchased:"], right = "|cffffffff" .. format(L["%s ago"], timeDiff) })
if lastBuyPrice then
if moneyCoinsTooltip then
tinsert(text, { left = " " .. L["Last Buy Price:"], right = (TSMAPI:FormatTextMoneyIcon(lastBuyPrice, "|cffffffff", true) or ("|cffffffff" .. "?")) })
else
tinsert(text, { left = " " .. L["Last Buy Price:"], right = (TSMAPI:FormatTextMoney(lastBuyPrice, "|cffffffff", true) or ("|cffffffff" .. "?")) })
end
end
end
end
@ -424,12 +442,24 @@ function TSM:GetAvgSellPrice(itemString)
return TSM.cache[itemString].avgSellPrice, TSM.cache[itemString].avgSellNum
end
local function GetAvgerageBuyPrice(itemString, noBaseItem)
local function GetSmartBuyItemCount(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
local function GetAvgerageBuyPrice(itemString, noBaseItem, useSmart)
if not noBaseItem and itemString and baseItemLookup[itemString] then
local totalPrice, totalNum = 0, 0
for _, item in ipairs(baseItemLookup[itemString]) do
if not baseItemLookup[item] then
local price, num = GetAvgerageBuyPrice(item, true)
local price, num = GetAvgerageBuyPrice(item, true, useSmart)
if price and num and num > 0 then
totalPrice = totalPrice + price
totalNum = totalNum + num
@ -443,16 +473,11 @@ local function GetAvgerageBuyPrice(itemString, noBaseItem)
if not (TSM.items[itemString] and #TSM.items[itemString].buys > 0) then return end
local itemCount = 0
if TSM.db.realm.smartBuyPrice then
local player, alts = TSMAPI:ModuleAPI("ItemTracker", "playertotal", itemString)
if not player then
alts = nil
if useSmart or TSM.db.realm.smartBuyPrice then
itemCount = GetSmartBuyItemCount(itemString)
if useSmart and itemCount <= 0 then
return
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
itemCount = player + alts + guild + auctions
end
local num, totalPrice = 0, 0
@ -482,6 +507,19 @@ function TSM:GetAvgBuyPrice(itemString)
return TSM.cache[itemString].avgBuyPrice, TSM.cache[itemString].avgBuyNum
end
function TSM:GetSmartAvgBuyPrice(itemString)
itemString = TSMAPI:GetItemString(select(2, TSMAPI:GetSafeItemInfo(itemString)))
if not itemString then return end
TSM.cache[itemString] = TSM.cache[itemString] or {}
TSM:UpdateBaseItemLookup()
if not TSM.cache[itemString].smartAvgBuyPrice then
local price, num = GetAvgerageBuyPrice(itemString, nil, true)
TSM.cache[itemString].smartAvgBuyPrice = price
TSM.cache[itemString].smartAvgBuyNum = num
end
return TSM.cache[itemString].smartAvgBuyPrice, TSM.cache[itemString].smartAvgBuyNum
end
function TSM:GetMaxSellPrice(itemString)
itemString = TSMAPI:GetItemString(select(2, TSMAPI:GetSafeItemInfo(itemString)))
if not (itemString and TSM.items[itemString] and #TSM.items[itemString].sales > 0) then return end
@ -546,6 +584,19 @@ function TSM:GetMinBuyPrice(itemString)
return TSM.cache[itemString].minBuyPrice
end
function TSM:GetLastBuyPrice(itemString)
itemString = TSMAPI:GetItemString(select(2, TSMAPI:GetSafeItemInfo(itemString)))
if not (itemString and TSM.items[itemString] and #TSM.items[itemString].buys > 0) then return end
TSM.cache[itemString] = TSM.cache[itemString] or {}
if not TSM.cache[itemString].lastBuyPrice then
local lastRecord = TSM.items[itemString].buys[#TSM.items[itemString].buys]
TSM.cache[itemString].lastBuyPrice = lastRecord and lastRecord.copper or nil
end
return TSM.cache[itemString].lastBuyPrice
end
function TSM:Round(value, sig)
sig = sig or 1
local gold = value / sig

4
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.24
## Version: 2.3.39
## SavedVariables: AscensionTSM_AuctionDB
## Dependency: TradeSkillMaster
## X-Curse-Packaged-Version: 2.3.24
## X-Curse-Packaged-Version: 2.3.39
## X-Curse-Project-Name: TradeSkillMaster_AuctionDB
## X-Curse-Project-ID: tradeskillmaster_auctiondb
## X-Curse-Repository-ID: wow/tradeskillmaster_auctiondb/mainline

21
TradeSkillMaster_Auctioning/modules/GUI.lua

@ -12,6 +12,17 @@ local GUI = TSM:NewModule("GUI", "AceEvent-3.0", "AceHook-3.0")
local AceGUI = LibStub("AceGUI-3.0")
local private = {}
function private:SetSelectionButtonsEnabled(enabled)
if not private.selectionFrame then return end
local action = enabled and "Enable" or "Disable"
if private.selectionFrame.postBtn then
private.selectionFrame.postBtn[action](private.selectionFrame.postBtn)
end
if private.selectionFrame.cancelBtn then
private.selectionFrame.cancelBtn[action](private.selectionFrame.cancelBtn)
end
end
function private:CreateButtons(parent)
local height = 24
local frame = CreateFrame("Frame", nil, parent)
@ -62,6 +73,7 @@ function private:CreateButtons(parent)
if self.which == "stop" and self.isDone then
GUI:HideSelectionFrame()
private.selectionFrame:Show()
private:SetSelectionButtonsEnabled(true)
elseif frame:IsVisible() and private.OnAction then
private:OnAction(self.which)
end
@ -86,13 +98,14 @@ function private:CreateButtons(parent)
button:SetScript("OnClick", OnClick)
frame.cancel = button
local button = TSMAPI.GUI:CreateButton(frame, 18)
local button = TSMAPI.GUI:CreateButton(frame, 18, "TSMAuctioningSkipButton")
button:SetPoint("TOPLEFT", frame.post, "TOPRIGHT", 5, 0)
button:SetWidth(60)
button:SetHeight(height)
button:SetText(L["Skip"])
button.which = "skip"
button:SetScript("OnClick", OnClick)
_G["TSMAuctioningCancelSkipButton"] = button
frame.skip = button
local button = TSMAPI.GUI:CreateButton(frame, 18)
@ -752,7 +765,7 @@ function GUI:CreateSelectionFrame(parent)
frame.helpText = helpText
local btnWidth = floor((stContainer:GetWidth() - 10)/3)
local postBtn = TSMAPI.GUI:CreateButton(frame, 16)
local postBtn = TSMAPI.GUI:CreateButton(frame, 16, "TSMAuctioningPostScanButton")
postBtn:SetPoint("BOTTOMLEFT", 5, 5)
postBtn:SetHeight(20)
postBtn:SetWidth(btnWidth)
@ -764,7 +777,7 @@ function GUI:CreateSelectionFrame(parent)
end)
frame.postBtn = postBtn
local cancelBtn = TSMAPI.GUI:CreateButton(frame, 16)
local cancelBtn = TSMAPI.GUI:CreateButton(frame, 16, "TSMAuctioningCancelScanButton")
cancelBtn:SetPoint("BOTTOMLEFT", postBtn, "BOTTOMRIGHT", 5, 0)
cancelBtn:SetHeight(20)
cancelBtn:SetWidth(btnWidth)
@ -908,6 +921,7 @@ function GUI:CreateScanFrame(parent)
end
function GUI:StartScan(frame)
private:SetSelectionButtonsEnabled(false)
private.selectionFrame:Hide()
private.scanFrame = private.scanFrame or GUI:CreateScanFrame(frame)
private.scanFrame:Show()
@ -967,6 +981,7 @@ function GUI:ShowSelectionFrame(frame)
if private.scanFrame then private.scanFrame:Hide() end
private.selectionFrame = private.selectionFrame or GUI:CreateSelectionFrame(frame)
private.selectionFrame:Show()
private:SetSelectionButtonsEnabled(true)
TSMAPI.AuctionScan:StopScan(false)
end

29
TradeSkillMaster_Crafting/Modules/CraftingGUI.lua

@ -2834,7 +2834,7 @@ function GUI:UpdateGathering()
if task.taskType == L["Search for Mats"] then
availQty = taskQuantity
end
if task.taskType == L["Mail Items"] then
if task.taskType == L["Mail Items"] or task.taskType == L["Collect Mail"] then
availQty = min(need, taskQuantity)
elseif not crafter == UnitName("player") then
availQty = min(need, taskQuantity) - (playerBags[itemString] or 0)
@ -2878,16 +2878,18 @@ function GUI:UpdateGathering()
TSM.db.realm.gathering.availableMats = CopyTable(availableMats)
GUI.gatheringFrame.gatherButton:SetText(L["Gather Items"])
if next(stData) then
GUI.gatheringFrame.gatherButton:Enable()
if private.currentTask == L["Visit Vendor"] then
GUI.gatheringFrame.gatherButton:SetText(L["Buy Vendor Items"])
elseif private.currentTask == L["Mail Items"] then
GUI.gatheringFrame.gatherButton:SetText(L["Mail Items"])
if next(stData) then
GUI.gatheringFrame.gatherButton:Enable()
if private.currentTask == L["Visit Vendor"] then
GUI.gatheringFrame.gatherButton:SetText(L["Buy Vendor Items"])
elseif private.currentTask == L["Mail Items"] then
GUI.gatheringFrame.gatherButton:SetText(L["Mail Items"])
elseif private.currentTask == L["Collect Mail"] then
GUI.gatheringFrame.gatherButton:SetText(L["Collect Mail"])
end
else
GUI.gatheringFrame.gatherButton:Disable()
end
else
GUI.gatheringFrame.gatherButton:Disable()
end
sort(stData, function(a, b) return a.name < b.name end)
GUI.gatheringFrame.availableST:SetData(stData)
@ -2919,7 +2921,12 @@ function GUI:GatheringEventHandler(event)
GUI.gatheringFrame.gatherButton:Disable()
elseif event == "MAIL_SHOW" then
private.currentSource = UnitName("player")
private.currentTask = L["Mail Items"]
local crafter = TSM.db.realm.gathering.crafter
if crafter and crafter == UnitName("player") then
private.currentTask = L["Collect Mail"]
else
private.currentTask = L["Mail Items"]
end
elseif event == "MAIL_CLOSED" then
private.currentSource = nil
private.currentTask = nil

2
TradeSkillMaster_Crafting/Modules/Gather.lua

@ -36,6 +36,8 @@ function Gather:gatherItems(source, task)
Gather:BuyFromMerchant(items)
elseif source == UnitName("player") and (task == L["Visit Bank"] or task == L["Visit Guild Bank"]) then
Gather:GatherBank(items)
elseif source == UnitName("player") and task == L["Collect Mail"] then
TSMAPI:ModuleAPI("Mailing", "collectItems", items, Gather.PrintMsg)
elseif source == UnitName("player") and task == L["Mail Items"] then
Gather:MailItems(items)
elseif source == L["Auction House"] then

24
TradeSkillMaster_Crafting/Modules/Queue.lua

@ -33,6 +33,9 @@ end
function Queue:CreateRestockQueue(groupInfo)
TSM:UpdateCraftReverseLookup()
local numItems = 0
local bestQueued = {}
local bestSpellID = {}
local seenItems = {}
for _, data in pairs(groupInfo) do
for _, opName in ipairs(data.operations) do
@ -42,6 +45,7 @@ function Queue:CreateRestockQueue(groupInfo)
-- it's a valid operation
for itemString in pairs(data.items) do
itemString = TSMAPI:GetItemString(itemString)
seenItems[itemString] = true
local spellID = TSM.craftReverseLookup[itemString] and TSM.craftReverseLookup[itemString][1]
if spellID and TSM.db.realm.crafts[spellID] then
local maxQueueCount = max(opSettings.maxRestock - TSM.Inventory:GetTotalQuantity(itemString), 0)
@ -61,10 +65,11 @@ function Queue:CreateRestockQueue(groupInfo)
end
local craft = TSM.db.realm.crafts[spellID]
craft.queued = floor(numToQueue / craft.numResult)
craft.queued = craft.queued >= opSettings.minRestock and craft.queued or 0
if craft.queued > 0 then
numItems = numItems + 1
local queued = floor(numToQueue / craft.numResult)
queued = queued >= opSettings.minRestock and queued or 0
if queued > 0 and queued > (bestQueued[itemString] or 0) then
bestQueued[itemString] = queued
bestSpellID[itemString] = spellID
end
end
end
@ -72,6 +77,17 @@ function Queue:CreateRestockQueue(groupInfo)
end
end
for itemString in pairs(seenItems) do
local queued = bestQueued[itemString] or 0
local spellID = bestSpellID[itemString] or (TSM.craftReverseLookup[itemString] and TSM.craftReverseLookup[itemString][1])
if spellID and TSM.db.realm.crafts[spellID] then
TSM.db.realm.crafts[spellID].queued = queued
if queued > 0 then
numItems = numItems + 1
end
end
end
if numItems==0 then
TSM:Printf("There are no items to restock.")
end

2
TradeSkillMaster_Crafting/TradeSkillMaster_Crafting.lua

@ -105,7 +105,7 @@ end
-- registers this module with TSM by first setting all fields and then calling TSMAPI:NewModule().
function TSM:RegisterModule()
TSM.icons = { { side = "module", desc = "Crafting", slashCommand = "crafting", callback = "Options:LoadCrafting", icon = "Interface\\Icons\\INV_Misc_Gear_08" } }
TSM.operations = { maxOperations = 1, callbackOptions = "Options:Load", callbackInfo = "GetOperationInfo" }
TSM.operations = { maxOperations = 5, callbackOptions = "Options:Load", callbackInfo = "GetOperationInfo" }
TSM.priceSources = {
{ key = "Crafting", label = L["Crafting Cost"], callback = "GetCraftingCost" },
{ key = "matPrice", label = L["Crafting Material Cost"], callback = "GetCraftingMatCost" },

106
TradeSkillMaster_Mailing/Modules/Inbox.lua

@ -74,7 +74,7 @@ function Inbox:CreateTab(parent)
st:SetData({})
frame.st = st
local btn = TSMAPI.GUI:CreateButton(frame, 18)
local btn = TSMAPI.GUI:CreateButton(frame, 18, "TSMMailingOpenAllButton")
btn:SetPoint("BOTTOMLEFT", 5, 30)
btn:SetPoint("BOTTOMRIGHT", -5, 30)
btn:SetHeight(20)
@ -91,7 +91,7 @@ function Inbox:CreateTab(parent)
local btnWidth = (frame:GetWidth() - label:GetWidth() - 25) / 5
local btn = TSMAPI.GUI:CreateButton(frame, 18)
local btn = TSMAPI.GUI:CreateButton(frame, 18, "TSMMailingSalesButton")
btn:SetPoint("BOTTOMLEFT", label, "BOTTOMRIGHT", 5, 0)
btn:SetWidth(btnWidth)
btn:SetHeight(20)
@ -99,7 +99,7 @@ function Inbox:CreateTab(parent)
btn:SetScript("OnClick", function() private:StartAutoLooting("sales") end)
frame.salesBtn = btn
local btn = TSMAPI.GUI:CreateButton(frame, 18)
local btn = TSMAPI.GUI:CreateButton(frame, 18, "TSMMailingBuysButton")
btn:SetPoint("BOTTOMLEFT", frame.salesBtn, "BOTTOMRIGHT", 5, 0)
btn:SetWidth(btnWidth)
btn:SetHeight(20)
@ -107,7 +107,7 @@ function Inbox:CreateTab(parent)
btn:SetScript("OnClick", function() private:StartAutoLooting("buys") end)
frame.buysBtn = btn
local btn = TSMAPI.GUI:CreateButton(frame, 18)
local btn = TSMAPI.GUI:CreateButton(frame, 18, "TSMMailingCancelsButton")
btn:SetPoint("BOTTOMLEFT", frame.buysBtn, "BOTTOMRIGHT", 5, 0)
btn:SetWidth(btnWidth)
btn:SetHeight(20)
@ -115,7 +115,7 @@ function Inbox:CreateTab(parent)
btn:SetScript("OnClick", function() private:StartAutoLooting("cancels") end)
frame.cancelsBtn = btn
local btn = TSMAPI.GUI:CreateButton(frame, 18)
local btn = TSMAPI.GUI:CreateButton(frame, 18, "TSMMailingExpiresButton")
btn:SetPoint("BOTTOMLEFT", frame.cancelsBtn, "BOTTOMRIGHT", 5, 0)
btn:SetWidth(btnWidth)
btn:SetHeight(20)
@ -436,6 +436,102 @@ function private:StartAutoLooting(mode)
private:AutoLoot()
end
local function CanLootItem(itemString, quantity)
if not itemString or not quantity then return false end
local maxStack = select(8, TSMAPI:GetSafeItemInfo(itemString))
local hasPartial = false
local hasEmpty = false
local totalFreeSlots = 0
for bag = 0, NUM_BAG_SLOTS do
if TSMAPI:ItemWillGoInBag(itemString, bag) then
totalFreeSlots = totalFreeSlots + (GetContainerNumFreeSlots(bag) or 0)
for slot = 1, GetContainerNumSlots(bag) do
local iLink = GetContainerItemLink(bag, slot)
if iLink then
if TSMAPI:GetItemString(iLink) == itemString and maxStack then
local stackSize = select(2, GetContainerItemInfo(bag, slot))
if stackSize and (maxStack - stackSize) >= quantity then
hasPartial = true
end
end
else
hasEmpty = true
end
end
end
end
if hasPartial then
return true
end
if not hasEmpty then
return false
end
if TSM.db.global.keepMailSpace and TSM.db.global.keepMailSpace > 0 then
return (totalFreeSlots - 1) >= TSM.db.global.keepMailSpace
end
return true
end
local function HasFittableStack(itemString, needed)
if not itemString or not needed or needed <= 0 then return false end
for i = 1, GetInboxNumItems() do
local _, _, _, _, _, cod, _, hasItem = GetInboxHeaderInfo(i)
if hasItem and hasItem > 0 and (not cod or cod == 0) then
for j = 1, hasItem do
local itemLink = GetInboxItemLink(i, j)
if itemLink and TSMAPI:GetItemString(itemLink) == itemString then
local quantity = select(3, GetInboxItem(i, j)) or 0
if quantity > 0 and quantity <= needed then
return true
end
end
end
end
end
return false
end
function Inbox:CollectItems(items, callback)
if not items or not next(items) then return end
if not MailFrame or not MailFrame:IsShown() then return end
local remaining = CopyTable(items)
local collected = false
for i = GetInboxNumItems(), 1, -1 do
local _, _, _, _, _, cod, _, hasItem = GetInboxHeaderInfo(i)
if hasItem and hasItem > 0 and (not cod or cod == 0) then
for j = hasItem, 1, -1 do
local itemLink = GetInboxItemLink(i, j)
local itemString = itemLink and TSMAPI:GetItemString(itemLink)
local need = itemString and remaining[itemString]
if need and need > 0 then
local quantity = select(3, GetInboxItem(i, j)) or 0
if quantity > 0 and (quantity <= need or not HasFittableStack(itemString, need)) then
if CanLootItem(itemString, quantity) then
TakeInboxItem(i, j)
remaining[itemString] = max(need - quantity, 0)
collected = true
elseif callback then
callback(L["Cannot finish auto looting, inventory is full or too many unique items."])
return
end
end
end
end
end
end
-- Keep looting as new mail indexes shift, while preserving 1 free slot.
if collected then
TSMAPI:CreateTimeDelay("craftingGatherMailLoop", 0.4, function()
Inbox:CollectItems(remaining, callback)
end)
end
end
function private:AutoLoot()
TSMAPI:CancelFrame("mailSkipDelay")

1
TradeSkillMaster_Mailing/TradeSkillMaster_Mailing.lua

@ -62,6 +62,7 @@ function TSM:RegisterModule()
TSM.operations = {maxOperations=12, callbackOptions="Options:Load", callbackInfo="GetOperationInfo"}
TSM.moduleAPIs = {
{key="mailItems", callback="AutoMail:SendItems"},
{key="collectItems", callback="Inbox:CollectItems"},
}
TSMAPI:NewModule(TSM)

2
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

52
TradeSkillMaster_Shopping/TradeSkillMaster_Shopping.lua

@ -53,7 +53,7 @@ end
-- registers this module with TSM by first setting all fields and then calling TSMAPI:NewModule().
function TSM:RegisterModule()
TSM.operations = { maxOperations = 1, callbackOptions = "Options:Load", callbackInfo = "GetOperationInfo" }
TSM.operations = { maxOperations = 5, callbackOptions = "Options:Load", callbackInfo = "GetOperationInfo" }
TSM.auctionTab = { callbackShow = "Search:Show", callbackHide = "Search:Hide" }
TSM.tooltipOptions = { callback = "Options:LoadTooltipOptions" }
TSM.moduleAPIs = {
@ -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
@ -136,20 +150,32 @@ function TSM:GetTooltip(itemString)
itemString = TSMAPI:GetBaseItemString(itemString, true)
local operations = TSMAPI:GetItemOperation(itemString, "Shopping")
if not operations then return end
local operationName = operations[1]
TSMAPI:UpdateOperation("Shopping", operationName)
local operation = TSM.operations[operationName]
if operation then
local maxPrice = TSM:GetMaxPrice(operation.maxPrice, itemString)
if maxPrice then
local priceText
if moneyCoinsTooltip then
priceText = (TSMAPI:FormatTextMoneyIcon(maxPrice, "|cffffffff", true) or "|cffffffff---|r")
else
priceText = (TSMAPI:FormatTextMoney(maxPrice, "|cffffffff", true) or "|cffffffff---|r")
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
tinsert(text, { left = " " .. L["Max Shopping Price:"], right = format("%s", priceText) })
end
if operation then
local price = TSM:GetMaxPrice(operation.maxPrice, itemString)
if price then
maxPrice = maxPrice and max(maxPrice, price) or price
end
end
end
if maxPrice then
local priceText
if moneyCoinsTooltip then
priceText = (TSMAPI:FormatTextMoneyIcon(maxPrice, "|cffffffff", true) or "|cffffffff---|r")
else
priceText = (TSMAPI:FormatTextMoney(maxPrice, "|cffffffff", true) or "|cffffffff---|r")
end
tinsert(text, { left = " " .. L["Max Shopping Price:"], right = format("%s", priceText) })
end
if #text > 0 then
tinsert(text, 1, "|cffffff00" .. "TSM Shopping:")

13
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"] },
},

8
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

120
TradeSkillMaster_Shopping/sidebar/Groups.lua

@ -1,7 +1,25 @@
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
local maxRestock = tonumber(opSettings.maxRestock) or 0
if maxRestock <= 0 or totalQty < maxRestock then
tinsert(active, opSettings)
end
end
end
return active
end
function private.Create(parent)
local frame = CreateFrame("Frame", nil, parent)
@ -39,33 +57,62 @@ function private.ScanCallback(event, ...)
local filter = ...
local maxPrice
for _, itemString in ipairs(filter.items) do
local operation = private.itemOperations[itemString]
local operationPrice = TSM:GetMaxPrice(operation.maxPrice, itemString)
if not operationPrice then return end
if operation.showAboveMaxPrice then
local operations = GetActiveOperations(itemString, private.itemOperations[itemString])
if not operations or #operations == 0 then return end
local itemMaxPrice
for _, operation in ipairs(operations) do
if operation.showAboveMaxPrice then
itemMaxPrice = nil
break
end
local operationPrice = TSM:GetMaxPrice(operation.maxPrice, itemString)
if operationPrice then
itemMaxPrice = itemMaxPrice and max(itemMaxPrice, operationPrice) or operationPrice
end
end
if itemMaxPrice == nil then
maxPrice = nil
break
end
maxPrice = maxPrice and max(maxPrice, operationPrice) or operationPrice
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 operation = private.itemOperations[itemString]
if not operation then return end
local operationPrice = TSM:GetMaxPrice(operation.maxPrice, itemString)
if not operationPrice then return end
local operations = GetActiveOperations(itemString, private.itemOperations[itemString])
if not operations or #operations == 0 then return end
auctionItem:FilterRecords(function(record)
if operation.evenStacks and record.count % 5 ~= 0 then
return true
end
if not operation.showAboveMaxPrice then
return (record:GetItemBuyout() or 0) > operationPrice
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
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
else
local operationPrice = TSM:GetMaxPrice(operation.maxPrice, itemString)
if operationPrice and buyout <= operationPrice then
return false
end
end
end
return true
end)
auctionItem:SetMarketValue(operationPrice)
local marketValue
for _, operation in ipairs(operations) do
local operationPrice = TSM:GetMaxPrice(operation.maxPrice, itemString)
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)
@ -76,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
@ -87,11 +167,17 @@ function private.StartScan()
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
private.itemOperations[itemString] = opSettings
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

18
TradeSkillMaster_Shopping/sidebar/Other.lua

@ -230,9 +230,21 @@ function private.SniperScanCallback(event, itemString, auctionItem)
vendorPrice = vendor
end
local operations = TSMAPI:GetItemOperation(itemString, "Shopping")
local opSettings = operations and operations[1] and TSM.operations[operations[1]]
if opSettings and opSettings.maxPrice then
maxPrice = TSM:GetMaxPrice(opSettings.maxPrice, itemString)
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)
end

Loading…
Cancel
Save