Files
coa-tsm/TradeSkillMaster_Crafting/Modules/Inventory.lua
T
Kian Soltani 4a03ece3d3 Added personal and realm bank implementations (#27)
* added personal and realm bank implementations

* fix gathering from personal and realm banks

* faster gathering from personal and realm banks

* Fix Profession Multiselect on Gathering window

* cleaner GetAscensionBankType function

* craft queue update
2026-02-16 08:12:24 -07:00

416 lines
17 KiB
Lua

-- ------------------------------------------------------------------------------ --
-- TradeSkillMaster_Crafting --
-- http://www.curse.com/addons/wow/tradeskillmaster_crafting --
-- --
-- A TradeSkillMaster Addon (http://tradeskillmaster.com) --
-- All Rights Reserved* - Detailed license information included with addon. --
-- ------------------------------------------------------------------------------ --
-- load the parent file (TSM) into a local variable and register this file as a module
local TSM = select(2, ...)
local Inventory = TSM:NewModule("Inventory", "AceEvent-3.0")
local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_Crafting") -- loads the localization table
-- gets the number of an item in the current player's bags
function Inventory:GetPlayerBagNum(itemString)
if not itemString then return end
if TSMAPI.SOULBOUND_MATS[itemString] then
return GetItemCount(itemString)
else
local bags = TSMAPI:ModuleAPI("ItemTracker", "playerbags", UnitName("player")) or {}
return bags and bags[itemString] or 0
end
end
function Inventory:GetTotals()
local bagTotal, auctionTotal, otherTotal, total = {}, {}, {}, {}
for _, player in pairs(TSMAPI:ModuleAPI("ItemTracker", "playerlist") or {}) do
if player == UnitName("player") or not TSM.db.global.ignoreCharacters[player] then
local bags = TSMAPI:ModuleAPI("ItemTracker", "playerbags", player) or {}
local bank = TSMAPI:ModuleAPI("ItemTracker", "playerbank", player) or {}
local mail = TSMAPI:ModuleAPI("ItemTracker", "playermail", player) or {}
local auctions = TSMAPI:ModuleAPI("ItemTracker", "playerauctions", player) or {}
for itemString, quantity in pairs(bags) do
if player == UnitName("player") then
bagTotal[itemString] = (bagTotal[itemString] or 0) + quantity
total[itemString] = (total[itemString] or 0) + quantity
else
otherTotal[itemString] = (otherTotal[itemString] or 0) + quantity
total[itemString] = (total[itemString] or 0) + quantity
end
end
for itemString, quantity in pairs(bank) do
otherTotal[itemString] = (otherTotal[itemString] or 0) + quantity
total[itemString] = (total[itemString] or 0) + quantity
end
for itemString, quantity in pairs(mail) do
otherTotal[itemString] = (otherTotal[itemString] or 0) + quantity
total[itemString] = (total[itemString] or 0) + quantity
end
for itemString, quantity in pairs(auctions) do
auctionTotal[itemString] = (auctionTotal[itemString] or 0) + quantity
total[itemString] = (total[itemString] or 0) + quantity
end
end
end
for itemString in pairs(TSMAPI.SOULBOUND_MATS) do
local bagNum = GetItemCount(itemString)
local bankNum = GetItemCount(itemString, true) - GetItemCount(itemString)
bagTotal[itemString] = (bagTotal[itemString] or 0) + bagNum
otherTotal[itemString] = (otherTotal[itemString] or 0) + bankNum
total[itemString] = (total[itemString] or 0) + bagNum + bankNum
end
-- add gbank counts of all non-ignored guilds
for _, guild in pairs(TSMAPI:ModuleAPI("ItemTracker", "guildlist") or {}) do
if not TSM.db.global.ignoreGuilds[guild] then
local gbank = TSMAPI:ModuleAPI("ItemTracker", "guildbank", guild) or {}
for itemString, quantity in pairs(gbank) do
otherTotal[itemString] = (otherTotal[itemString] or 0) + quantity
total[itemString] = (total[itemString] or 0) + quantity
end
end
end
-- Ascension WoW: add personal bank counts for all non-ignored characters
for _, player in pairs(TSMAPI:ModuleAPI("ItemTracker", "playerlist") or {}) do
if player == UnitName("player") or not TSM.db.global.ignoreCharacters[player] then
local personalBank = TSMAPI:ModuleAPI("ItemTracker", "personalbank", player) or {}
for itemString, quantity in pairs(personalBank) do
otherTotal[itemString] = (otherTotal[itemString] or 0) + quantity
total[itemString] = (total[itemString] or 0) + quantity
end
end
end
-- Ascension WoW: add realm bank counts
local realmBank = TSMAPI:ModuleAPI("ItemTracker", "realmbank") or {}
for itemString, quantity in pairs(realmBank) do
otherTotal[itemString] = (otherTotal[itemString] or 0) + quantity
total[itemString] = (total[itemString] or 0) + quantity
end
return bagTotal, auctionTotal, otherTotal, total
end
-- gets the total number of some item that they have
function Inventory:GetTotalQuantity(itemString)
if not itemString then return 0 end
local count = 0
-- add bags/bank/mail/auction counts of all non-ignored characters (always including current character)
for _, player in pairs(TSMAPI:ModuleAPI("ItemTracker", "playerlist") or {}) do
if player == UnitName("player") or not TSM.db.global.ignoreCharacters[player] then
local bags = TSMAPI:ModuleAPI("ItemTracker", "playerbags", player) or {}
local bank = TSMAPI:ModuleAPI("ItemTracker", "playerbank", player) or {}
local mail = TSMAPI:ModuleAPI("ItemTracker", "playermail", player) or {}
local auctions = TSMAPI:ModuleAPI("ItemTracker", "playerauctions", player) or {}
count = count + (bags[itemString] or 0)
count = count + (bank[itemString] or 0)
count = count + (mail[itemString] or 0)
count = count + (auctions[itemString] or 0)
end
end
-- add gbank counts of all non-ignored guilds
for _, guild in pairs(TSMAPI:ModuleAPI("ItemTracker", "guildlist") or {}) do
if not TSM.db.global.ignoreGuilds[guild] then
local bank = TSMAPI:ModuleAPI("ItemTracker", "guildbank", guild) or {}
count = count + (bank[itemString] or 0)
end
end
-- Ascension WoW: add personal bank counts for all non-ignored characters
for _, player in pairs(TSMAPI:ModuleAPI("ItemTracker", "playerlist") or {}) do
if player == UnitName("player") or not TSM.db.global.ignoreCharacters[player] then
local personalBank = TSMAPI:ModuleAPI("ItemTracker", "personalbank", player) or {}
count = count + (personalBank[itemString] or 0)
end
end
-- Ascension WoW: add realm bank count
local realmBank = TSMAPI:ModuleAPI("ItemTracker", "realmbank") or {}
count = count + (realmBank[itemString] or 0)
if TSMAPI.SOULBOUND_MATS[itemString] then
count = count + GetItemCount(itemString, true)
end
return count
end
function Inventory:GetItemSources(crafter, neededMats)
if not neededMats then return end
local sources = {}
local gbank = {}
local next = next
local crafterBags = TSMAPI:ModuleAPI("ItemTracker", "playerbags", crafter) or {}
local crafterMail = TSMAPI:ModuleAPI("ItemTracker", "playermail", crafter) or {}
local crafterBank = TSMAPI:ModuleAPI("ItemTracker", "playerbank", crafter) or {}
-- Ascension WoW: crafter's personal bank
local crafterPersonalBank = TSMAPI:ModuleAPI("ItemTracker", "personalbank", crafter) or {}
-- add vendor items
local task = {}
local items = {}
for itemString, quantity in pairs(neededMats) do
if TSMAPI:GetVendorCost(itemString) then
local vendorNeed = quantity - ((crafterBags[itemString] or 0) + (crafterMail[itemString] or 0) + (crafterBank[itemString] or 0) + (crafterPersonalBank[itemString] or 0))
if vendorNeed > 0 then
items[itemString] = vendorNeed
end
elseif TSMAPI.Conversions[itemString] and TSMAPI.InkConversions[itemString] then
local tradeItem, data = next(TSMAPI.Conversions[itemString])
if data.source == "vendortrade" then
local num = floor(Inventory:GetTotalQuantity(tradeItem) * data.rate)
if quantity > Inventory:GetTotalQuantity(itemString) and num >= (quantity - Inventory:GetTotalQuantity(itemString)) then
items[itemString] = quantity - Inventory:GetTotalQuantity(itemString)
neededMats[tradeItem] = (neededMats[tradeItem] or 0) + quantity / data.rate -- add the qty of IOD to needed mats
end
end
end
end
if next(items) then
tinsert(task, { taskType = L["Visit Vendor"], items = items })
tinsert(sources, { sourceName = L["Vendor"], isCrafter = false, isVendor = true, isAH = false, tasks = task })
end
-- double check if crafter already has all the items needed
local shortItems = {}
for itemString, quantity in pairs(neededMats) do
local soulboundBagCount
if TSMAPI.SOULBOUND_MATS[itemString] then
soulboundBagCount = GetItemCount(itemString)
end
local inBags = crafterBags[itemString] or soulboundBagCount or 0
local need = max(quantity - inBags, 0)
if need > 0 then
shortItems[itemString] = need
end
end
if not next(shortItems) then
return sources -- Return sources instead of nil so vendor items are not lost
end
-- add bags/bank/mail "tasks" for needed items of all non-ignored characters (always include crafter)
local playerList = TSMAPI:ModuleAPI("ItemTracker", "playerlist") or {}
for _, player in pairs(playerList) do
if player == crafter or not TSM.db.global.ignoreCharacters[player] then
local task = {}
local bags = TSMAPI:ModuleAPI("ItemTracker", "playerbags", player) or {}
local bank = TSMAPI:ModuleAPI("ItemTracker", "playerbank", player) or {}
local guild = TSMAPI:ModuleAPI("ItemTracker", "playerguild", player) or {}
local gbank = {}
if guild and not TSM.db.global.ignoreGuilds[guild] then
gbank = TSMAPI:ModuleAPI("ItemTracker", "guildbank", guild) or {}
end
local mail = TSMAPI:ModuleAPI("ItemTracker", "playermail", player) or {}
-- Ascension WoW: personal bank for this player
local personalBank = TSMAPI:ModuleAPI("ItemTracker", "personalbank", player) or {}
local bankItems = {}
local gbankItems = {}
local mailItems = {}
local bagItems = {}
local personalBankItems = {}
for itemString in pairs(neededMats) do
local soulboundBagCount, soulboundBankCount
if TSMAPI.SOULBOUND_MATS[itemString] then
soulboundBagCount = GetItemCount(itemString)
soulboundBankCount = GetItemCount(itemString, true) - soulboundBagCount
end
if (bank[itemString] or (soulboundBankCount and soulboundBankCount > 0)) and shortItems[itemString] then
if shortItems[itemString] - (crafterMail[itemString] or 0) - (player ~= crafter and bags[itemString] or 0) > 0 then
bankItems[itemString] = bank[itemString] or soulboundBankCount
end
end
if gbank[itemString] and shortItems[itemString] then
if shortItems[itemString] - (crafterMail[itemString] or 0) - (player ~= crafter and bags[itemString] or 0) > 0 then
gbankItems[itemString] = gbank[itemString]
end
end
-- Ascension WoW: check personal bank
if personalBank[itemString] and shortItems[itemString] then
if shortItems[itemString] - (crafterMail[itemString] or 0) - (player ~= crafter and bags[itemString] or 0) > 0 then
personalBankItems[itemString] = personalBank[itemString]
end
end
if mail[itemString] and shortItems[itemString] then
mailItems[itemString] = mail[itemString]
end
if bags[itemString] and shortItems[itemString] then
if player ~= crafter then
if shortItems[itemString] - (crafterMail[itemString] or 0) > 0 then
bagItems[itemString] = bags[itemString]
end
end
end
end
-- add mail tasks for destroyable items bought through shopping search (exclude items already added to mail tasks)
for itemString, quantity in pairs(TSM.db.realm.gathering.destroyingMats) do
if mail[itemString] and not shortItems[itemString] then
mailItems[itemString] = quantity
end
end
if next(bankItems) then
tinsert(task, { taskType = L["Visit Bank"], items = bankItems })
end
if next(gbankItems) then
tinsert(task, { taskType = L["Visit Guild Bank"], items = gbankItems })
end
-- Ascension WoW: personal bank task
if next(personalBankItems) then
tinsert(task, { taskType = L["Visit Personal Bank"], items = personalBankItems })
end
if next(mailItems) then
tinsert(task, { taskType = L["Collect Mail"], items = mailItems })
end
if next(bagItems) then
tinsert(task, { taskType = L["Mail Items"], items = bagItems })
end
if next(task) then
tinsert(sources, { sourceName = player, isCrafter = player == crafter, isVendor = false, isAH = false, tasks = task, isCurrent = (player == UnitName("player")) })
end
end
end
-- Ascension WoW: add realm bank as a separate source
local realmBank = TSMAPI:ModuleAPI("ItemTracker", "realmbank") or {}
local realmBankTask = {}
local realmBankItems = {}
for itemString in pairs(neededMats) do
if realmBank[itemString] and shortItems[itemString] then
if shortItems[itemString] - (crafterMail[itemString] or 0) > 0 then
realmBankItems[itemString] = realmBank[itemString]
end
end
end
if next(realmBankItems) then
tinsert(realmBankTask, { taskType = L["Visit Realm Bank"], items = realmBankItems })
tinsert(sources, { sourceName = L["Realm Bank"], isCrafter = false, isVendor = false, isAH = false, tasks = realmBankTask, isCurrent = true })
end
-- add auction house tasks
local auctionTask = {}
local auctionItems = {}
for itemString, quantity in pairs(neededMats) do
if not TSMAPI.SOULBOUND_MATS[itemString] and not TSMAPI:GetVendorCost(itemString) then
local need
if TSM.Inventory.gatherItem == itemString and TSM.Inventory.gatherQuantity then
need = TSM.Inventory.gatherQuantity
else
need = max(quantity - (TSM.Inventory:GetTotalQuantity(itemString) or 0), 0)
end
if need > 0 then
auctionItems[itemString] = need
end
end
end
if next(auctionItems) then
tinsert(auctionTask, { taskType = L["Search for Mats"], items = auctionItems })
tinsert(sources, { sourceName = L["Auction House"], isCrafter = false, isVendor = false, isAH = true, tasks = auctionTask })
end
-- add destroying tasks
local destroyingTask, millItems, prospectItems, transformItems, deItems = {}, {}, {}, {}, {}
for itemString, quantity in pairs(neededMats) do
local need = max(quantity - (TSM.Inventory:GetTotalQuantity(itemString) or 0), 0)
-- conversion items
for destroyItem, data in pairs(TSMAPI.Conversions[itemString] or {}) do
if TSM.db.realm.gathering.destroyingMats[destroyItem] then
if need > 0 then
local destroyNeed
if data.source == "mill" then
destroyNeed = floor(TSM.db.realm.gathering.destroyingMats[destroyItem] / 5)
if destroyNeed > 0 then
millItems[destroyItem] = (millItems[destroyItem] or 0) + destroyNeed
end
elseif data.source == "prospect" then
destroyNeed = floor(TSM.db.realm.gathering.destroyingMats[destroyItem] / 5)
if destroyNeed > 0 then
prospectItems[destroyItem] = (prospectItems[destroyItem] or 0) + destroyNeed
end
elseif data.source == "transform" then
if data.rate == 1 / 3 then
destroyNeed = floor(TSM.db.realm.gathering.destroyingMats[destroyItem] / 3)
elseif data.rate == 1 / 10 then
destroyNeed = floor(TSM.db.realm.gathering.destroyingMats[destroyItem] / 10)
else
destroyNeed = TSM.db.realm.gathering.destroyingMats[destroyItem]
end
if destroyNeed > 0 then
transformItems[destroyItem] = (transformItems[destroyItem] or 0) + destroyNeed
end
end
else
TSM.db.realm.gathering.destroyingMats[destroyItem] = nil
end
end
end
-- disenchantable items
if next(TSM.db.realm.gathering.destroyingMats) then
for deItemString, quantity in pairs(TSM.db.realm.gathering.destroyingMats) do
if Inventory:IsDisenchantable(deItemString) then
if need > 0 then
deItems[deItemString] = quantity
else
TSM.db.realm.gathering.destroyingMats[deItemString] = nil
end
end
end
end
end
if next(millItems) then
tinsert(destroyingTask, { taskType = L["Milling"], items = millItems })
end
if next(prospectItems) then
tinsert(destroyingTask, { taskType = L["Prospect"], items = prospectItems })
end
if next(transformItems) then
tinsert(destroyingTask, { taskType = L["Transform"], items = transformItems })
end
if next(deItems) then
tinsert(destroyingTask, { taskType = L["Disenchant"], items = deItems })
end
if next(destroyingTask) then
tinsert(sources, { sourceName = L["Destroying"], isCrafter = false, isVendor = false, isAH = true, tasks = destroyingTask })
end
sort(sources, function(a, b)
-- isCurrent sources come first
if a.isCurrent ~= b.isCurrent then
return a.isCurrent
end
-- AH sources come last
if a.isAH ~= b.isAH then
return b.isAH
end
-- Vendor sources come near last (before AH)
if a.isVendor ~= b.isVendor then
return b.isVendor
end
-- Crafter sources come after other players
if a.isCrafter ~= b.isCrafter then
return b.isCrafter
end
-- Alphabetical by name
return a.sourceName < b.sourceName
end)
return sources
end
function Inventory:IsDisenchantable(itemString)
local _, link, quality, _, _, iType = TSMAPI:GetSafeItemInfo(itemString)
local WEAPON, ARMOR = GetAuctionItemClasses()
if itemString and not TSMAPI.DisenchantingData.notDisenchantable[itemString] and (iType == ARMOR or iType == WEAPON) then
return true
end
end