Asa@63: local ItemAuditor = select(2, ...) Asa@63: ItemAuditor = LibStub("AceAddon-3.0"):NewAddon(ItemAuditor, "ItemAuditor", "AceEvent-3.0", "AceBucket-3.0") Asa@65: --@debug@ Asa@65: _G['ItemAuditor'] = ItemAuditor Asa@65: --@end-debug@ Asa@0: Asa@87: if not DevTools_Dump then Asa@87: function DevTools_Dump() Asa@87: end Asa@87: end Asa@87: Asa@98: local allMailboxes = {} Asa@98: local myMailbox = {} Asa@67: Asa@67: ItemAuditor.Options = { Asa@67: handler = ItemAuditor, Asa@67: name = "ItemAuditor @project-version@", Asa@67: type = 'group', Asa@67: args = { Asa@67: options = { Asa@67: type = "execute", Asa@67: name = "options", Asa@67: desc = "Show Blizzard's options GUI", Asa@67: func = "ShowOptionsGUI", Asa@67: guiHidden = true, Asa@67: }, Asa@67: debug = { Asa@67: type = "execute", Asa@67: name = "debug", Asa@67: desc = "Shows the debug frame", Asa@67: func = function() ItemAuditor_DebugFrame:Show() end, Asa@67: guiHidden = true, Asa@67: }, Asa@67: suspend = { Asa@67: type = "toggle", Asa@67: name = "suspend", Asa@67: desc = "Suspends ItemAuditor", Asa@67: get = "IsEnabled", Asa@67: set = "SetEnabled", Asa@67: guiHidden = true, Asa@67: }, Asa@67: }, Asa@67: } Asa@67: Asa@100: ItemAuditor.DB_defaults = { Asa@100: char = { Asa@100: ah = 1, Asa@100: use_quick_auctions = false, Asa@101: profitable_threshold = 10000, Asa@100: auction_threshold = 0.15, Asa@111: auction_threshold_value = 0, Asa@100: qa_extra = 0, Asa@100: output_chat_frame = nil, Asa@109: cod_warnings = true, Asa@100: }, Asa@100: profile = { Asa@100: messages = { Asa@100: cost_updates = true, Asa@100: queue_skip = false, Asa@100: }, Asa@100: ItemAuditor_enabled = true, Asa@100: queue_destination = nil, Asa@100: disabled_deciders = {}, Asa@100: pricing_method = 'low', Asa@100: }, Asa@100: factionrealm = { Asa@100: item_account = {}, Asa@100: items = {}, Asa@100: outbound_cod = {}, Asa@100: mailbox = {}, Asa@100: }, Asa@100: } Asa@100: Asa@63: function ItemAuditor:OnInitialize() Asa@100: self.db = LibStub("AceDB-3.0"):New("ItemAuditorDB", ItemAuditor.DB_defaults, true) Asa@98: Asa@98: allMailboxes = self.db.factionrealm.mailbox Asa@98: if not allMailboxes[UnitName("player")] then Asa@98: allMailboxes[UnitName("player")] = {} Asa@98: end Asa@98: myMailbox = allMailboxes[UnitName("player")] Asa@98: Asa@67: self.optionsFrame = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("ItemAuditor", "ItemAuditor") Asa@67: Asa@67: LibStub("AceConfig-3.0"):RegisterOptionsTable("ItemAuditor", ItemAuditor.Options, {"ia"}) Asa@38: ItemAuditor:RegisterFrame(ItemAuditor_DebugFrame) Asa@23: Asa@86: LibStub("AceConsole-3.0"):RegisterChatCommand('rl', ReloadUI) Asa@100: Asa@101: if self.db.char.crafting_threshold then Asa@101: local threshold = self.db.char.crafting_threshold Asa@101: if threshold == 1 then Asa@101: self.db.char.profitable_threshold = 5000 Asa@101: elseif threshold == 2 then Asa@101: self.db.char.profitable_threshold = 10000 Asa@101: elseif threshold == 3 then Asa@101: self.db.char.profitable_threshold = 50000 Asa@101: end Asa@101: Asa@101: self.db.char.crafting_threshold = nil Asa@101: end Asa@101: Asa@119: ItemAuditor:IsQAEnabled() Asa@112: Asa@65: --@debug@ Asa@59: -- ItemAuditor_DebugFrame:Show() Asa@59: -- self:CreateFrame('tab_crafting') Asa@74: self:RegisterEvent("TRADE_SKILL_SHOW", function() Asa@93: ItemAuditor:DisplayCrafting() Asa@74: end) Asa@65: --@end-debug@ Asa@0: end Asa@0: Asa@67: Asa@67: Asa@38: local registeredEvents = {} Asa@63: local originalRegisterEvent = ItemAuditor.RegisterEvent Asa@63: function ItemAuditor:RegisterEvent(event, callback, arg) Asa@38: registeredEvents[event] = true Asa@38: if arg ~= nil then Asa@38: return originalRegisterEvent(self, event, callback, arg) Asa@38: elseif callback ~= nil then Asa@38: return originalRegisterEvent(self, event, callback) Asa@38: else Asa@38: return originalRegisterEvent(self, event) Asa@38: end Asa@38: end Asa@38: Asa@63: local originalUnregisterEvent = ItemAuditor.UnregisterEvent Asa@63: function ItemAuditor:UnregisterEvent(event) Asa@38: registeredEvents[event] = nil Asa@38: return originalUnregisterEvent(self, event) Asa@38: end Asa@38: Asa@63: function ItemAuditor:UnregisterAllEvents() Asa@38: for event in pairs(registeredEvents) do Asa@38: self:UnregisterEvent(event) Asa@38: end Asa@38: end Asa@38: Asa@38: local registeredFrames = {} Asa@63: function ItemAuditor:RegisterFrame(frame) Asa@38: tinsert(registeredFrames, frame) Asa@38: end Asa@38: Asa@63: function ItemAuditor:HideAllFrames() Asa@38: for key, frame in pairs(registeredFrames) do Asa@38: if frame then Asa@38: frame:Hide() Asa@38: end Asa@38: end Asa@38: end Asa@38: Asa@63: function ItemAuditor:ConvertItems() Asa@8: for itemName, value in pairs(self.db.factionrealm.item_account) do Asa@15: local itemID = self:GetItemID(itemName) Asa@8: if itemID ~= nil then Asa@8: self:GetItem('item:' .. itemID) Asa@8: end Asa@8: if value == 0 then Asa@8: self.db.factionrealm.item_account[itemName] = nil Asa@8: end Asa@8: end Asa@8: Asa@8: for link, data in pairs(self.db.factionrealm.items) do Asa@8: if self:GetItem(link).count == 0 or self:GetItem(link).invested == 0 then Asa@8: self:RemoveItem(link) Asa@8: end Asa@10: end Asa@10: Asa@12: self:RefreshQAGroups() Asa@12: end Asa@12: Asa@65: -- Options doesn't exist when this file is created the first time, so getOptions will Asa@65: -- make one call to :GetModule and return the result and replace itself with a Asa@65: -- function that simply returns the same object. The permanent solution will probably be Asa@65: -- to move :Print to a different module. Asa@65: local function getOptions() Asa@65: local Options = ItemAuditor:GetModule("Options") Asa@65: getOptions = function() return Options end Asa@65: return Options Asa@65: end Asa@65: Asa@24: local printPrefix = "|cFFA3CEFFItemAuditor|r: " Asa@63: function ItemAuditor:Print(message, ...) Asa@24: message = format(message, ...) Asa@65: getOptions().GetSelectedChatWindow():AddMessage( printPrefix .. tostring(message)) Asa@16: end Asa@16: Asa@81: local bankOpen = false Asa@81: Asa@81: function ItemAuditor:BankFrameChanged(event) Asa@81: bankOpen = (event == 'BANKFRAME_OPENED') Asa@81: ItemAuditor:UpdateCurrentInventory() Asa@81: end Asa@81: Asa@80: local function scanBag(bagID, i) Asa@80: bagSize=GetContainerNumSlots(bagID) Asa@80: for slotID = 0, bagSize do Asa@80: local link= GetContainerItemLink(bagID, slotID); Asa@80: link = link and ItemAuditor:GetSafeLink(link) Asa@80: Asa@80: if link ~= nil and i[link] == nil then Asa@81: i[link] = GetItemCount(link, bankOpen); Asa@80: end Asa@80: end Asa@80: end Asa@80: Asa@63: function ItemAuditor:GetCurrentInventory() Asa@8: local i = {} Asa@8: local bagID Asa@8: local slotID Asa@8: Asa@8: for bagID = 0, NUM_BAG_SLOTS do Asa@80: scanBag(bagID, i) Asa@8: end Asa@80: Asa@81: if bankOpen then Asa@81: scanBag(BANK_CONTAINER, i) Asa@81: for bagID = NUM_BAG_SLOTS+1, NUM_BANKBAGSLOTS do Asa@81: scanBag(bagID, i) Asa@81: end Asa@80: end Asa@80: Asa@8: return {items = i, money = GetMoney()} Asa@0: end Asa@0: Asa@63: function ItemAuditor:GetInventoryDiff(pastInventory, current) Asa@8: if current == nil then Asa@8: current = self:GetCurrentInventory() Asa@8: end Asa@8: local diff = {} Asa@8: Asa@8: for link, count in pairs(current.items) do Asa@8: if pastInventory.items[link] == nil then Asa@8: diff[link] = count Asa@23: self:Debug("1 diff[" .. link .. "]=" .. diff[link]) Asa@8: elseif count - pastInventory.items[link] ~= 0 then Asa@8: diff[link] = count - pastInventory.items[link] Asa@23: self:Debug("2 diff[" .. link .. "]=" .. diff[link]) Asa@8: end Asa@8: end Asa@8: Asa@8: for link, count in pairs(pastInventory.items) do Asa@8: if current.items[link] == nil then Asa@8: diff[link] = -count Asa@23: self:Debug("3 diff[" .. link .. "]=" .. diff[link]) Asa@8: elseif current.items[link] - count ~= 0 then Asa@8: diff[link] = current.items[link] - pastInventory.items[link] Asa@23: self:Debug("4 diff[" .. link .. "]=" .. diff[link]) Asa@8: end Asa@8: end Asa@8: Asa@8: local moneyDiff = current.money - pastInventory.money Asa@23: if abs(moneyDiff) > 0 then Asa@23: self:Debug("moneyDiff: " .. moneyDiff) Asa@23: end Asa@8: Asa@8: return {items = diff, money = moneyDiff} Asa@0: end Asa@0: Asa@39: local inboundCOD = {} Asa@39: local skipMail = {} Asa@63: function ItemAuditor:ScanMail() Asa@0: local results = {} Asa@39: local CODPaymentRegex = gsub(COD_PAYMENT, "%%s", "(.*)") Asa@39: Asa@0: for mailIndex = 1, GetInboxNumItems() or 0 do Asa@39: local sender, msgSubject, msgMoney, msgCOD, daysLeft, msgItem, _, _, msgText, _, isGM = select(3, GetInboxHeaderInfo(mailIndex)) Asa@15: local mailType = self:GetMailType(msgSubject) Asa@6: Asa@39: local mailSignature = msgSubject .. '-' .. msgMoney .. '-' .. msgCOD .. '-' .. daysLeft Asa@39: Asa@6: results[mailType] = (results[mailType] or {}) Asa@6: Asa@39: if skipMail[mailSignature] ~= nil then Asa@39: -- do nothing Asa@39: elseif mailType == "NonAHMail" and msgCOD > 0 then Asa@6: mailType = 'COD' Asa@6: results[mailType] = (results[mailType] or {}) Asa@5: Asa@5: local itemTypes = {} Asa@5: for itemIndex = 1, ATTACHMENTS_MAX_RECEIVE do Asa@5: local itemName, _, count, _, _= GetInboxItem(mailIndex, itemIndex) Asa@5: if itemName ~= nil then Asa@39: itemTypes[itemName] = (itemTypes[itemName] or 0) + count Asa@5: end Asa@5: end Asa@5: Asa@15: if self:tcount(itemTypes) == 1 then Asa@5: for itemName, count in pairs(itemTypes) do Asa@39: results[mailType][itemName] = (results[mailType][itemName] or {total=0,count=0}) Asa@39: results[mailType][itemName].total = results[mailType][itemName].total + msgCOD Asa@39: Asa@39: if inboundCOD[mailSignature] == nil then Asa@39: results[mailType][itemName].count = results[mailType][itemName].count + count Asa@39: inboundCOD[mailSignature] = (inboundCOD[mailSignature] or 0) + count Asa@39: else Asa@39: results[mailType][itemName].count = inboundCOD[mailSignature] Asa@39: end Asa@39: Asa@39: Asa@5: end Asa@5: else Asa@5: self:Debug("Don't know what to do with more than one item type on COD mail.") Asa@5: end Asa@6: elseif mailType == "CODPayment" then Asa@39: -- /dump ItemAuditor.db.factionrealm.outbound_cod Asa@39: self:Debug(msgSubject) Asa@39: self:Debug(CODPaymentRegex) Asa@39: local outboundSubject = select(3, msgSubject:find(CODPaymentRegex)) Asa@39: local trackID Asa@39: if outboundSubject ~= nil then Asa@39: self:Debug(outboundSubject) Asa@45: trackID = select(3, outboundSubject:find('[[]IA: (%d*)[]]')) Asa@39: Asa@39: if trackID ~= nil then Asa@45: trackID = tonumber(trackID) Asa@45: self:Debug('COD ID: %s', trackID) Asa@39: local cod = self.db.factionrealm.outbound_cod[trackID] Asa@39: if cod == nil then Asa@39: skipMail[mailSignature] = true Asa@39: self:Print("WARNING: {%s} has an invalid ItemAuditor tracking number.", msgSubject) Asa@39: else Asa@39: itemName = trackID .. "|" .. cod['link'] Asa@39: Asa@39: Asa@39: results[mailType][itemName] = (results[mailType][itemName] or {total=0,count=0}) Asa@39: results[mailType][itemName].total = results[mailType][itemName].total - msgMoney Asa@39: results[mailType][itemName].count = results[mailType][itemName].count - cod.count Asa@39: end Asa@39: end Asa@39: end Asa@5: Asa@39: if trackID == nil then Asa@39: skipMail[mailSignature] = true Asa@39: self:Print("WARNING: {%s} is a COD payment but doesn't have an ItemAuditor tracking number.", msgSubject) Asa@39: end Asa@5: Asa@0: elseif mailType == "AHSuccess" then Asa@0: local invoiceType, itemName, playerName, bid, buyout, deposit, consignment = GetInboxInvoiceInfo(mailIndex); Asa@26: results[mailType][itemName] = (results[mailType][itemName] or {total=0,count=0}) Asa@26: results[mailType][itemName].total = results[mailType][itemName].total - deposit - buyout + consignment Asa@26: Asa@0: Asa@0: elseif mailType == "AHWon" then Asa@0: local invoiceType, itemName, playerName, bid, buyout, deposit, consignment = GetInboxInvoiceInfo(mailIndex); Asa@26: results[mailType][itemName] = (results[mailType][itemName] or {total=0,count=0}) Asa@26: results[mailType][itemName].total = results[mailType][itemName].total + bid Asa@26: Asa@98: local count = select(3, GetInboxItem(mailIndex,1)) Asa@26: results[mailType][itemName].count = results[mailType][itemName].count + count Asa@5: elseif mailType == "AHExpired" or mailType == "AHCancelled" or mailType == "AHOutbid" then Asa@0: -- These should be handled when you pay the deposit at the AH Asa@0: else Asa@24: -- self:Debug("Unhandled mail type: " .. mailType) Asa@24: -- self:Debug(msgSubject) Asa@0: end Asa@0: Asa@0: end Asa@98: Asa@98: wipe(myMailbox) Asa@23: for mailType, collection in pairs(results) do Asa@98: myMailbox[mailType] = {} Asa@26: for item, data in pairs(collection) do Asa@98: myMailbox[mailType][item] = { Asa@98: total = data.total, Asa@98: count = data.count, Asa@98: } Asa@98: -- self:Print(format("|cFF00FF00MailScan|r: %s - %s - %s x %s", mailType, item, data.total, data.count)) Asa@23: end Asa@23: end Asa@0: return results Asa@0: end Asa@0: Asa@105: local realm = GetRealmName() Asa@105: local ds_account = 'Default' Asa@82: function ItemAuditor:GetItemCount(searchID) Asa@105: local count = 0 Asa@105: for _, character in pairs(DataStore:GetCharacters(realm, ds_account)) do Asa@105: local bag, bank = DataStore:GetContainerItemCount(character, searchID) Asa@105: count = count + (bag or 0) + (bank or 0) Asa@105: count = count + (DataStore:GetAuctionHouseItemCount(character, searchID) or 0) Asa@105: count = count + (DataStore:GetInventoryItemCount(character, searchID) or 0) Asa@105: count = count + (DataStore:GetMailItemCount(character, searchID) or 0) Asa@105: count = count + (DataStore:GetCurrencyItemCount(character, searchID) or 0) Asa@105: end Asa@98: local itemName = GetItemInfo(searchID) Asa@98: for character, mailbox in pairs(allMailboxes) do Asa@98: for type, items in pairs(mailbox) do Asa@98: if type == 'AHWon' or type == 'COD' then Asa@98: for name, data in pairs(items) do Asa@98: if name == itemName then Asa@98: count = count - data.count Asa@82: Asa@98: end Asa@98: end Asa@98: end Asa@98: end Asa@82: end Asa@82: return count Asa@82: end Asa@82: Asa@63: function ItemAuditor:GetItem(link, viewOnly) Asa@9: if viewOnly == nil then Asa@9: viewOnly = false Asa@9: end Asa@8: Asa@9: local itemName = nil Asa@9: if self:GetSafeLink(link) == nil then Asa@9: itemName = link Asa@9: else Asa@9: link = self:GetSafeLink(link) Asa@9: itemName = GetItemInfo(link) Asa@9: end Asa@9: Asa@12: Asa@9: if self.db.factionrealm.item_account[itemName] ~= nil then Asa@65: self.db.factionrealm.items[link] = { Asa@82: count = ItemAuditor:GetItemCount(self:GetIDFromLink(link)), Asa@8: invested = abs(self.db.factionrealm.item_account[itemName] or 0), Asa@8: } Asa@8: self.db.factionrealm.item_account[itemName] = nil Asa@8: end Asa@8: Asa@65: if viewOnly == false and self.db.factionrealm.items[link] == nil then Asa@24: Asa@65: self.db.factionrealm.items[link] = { Asa@82: count = ItemAuditor:GetItemCount(self:GetIDFromLink(link)), Asa@9: invested = abs(self.db.factionrealm.item_account[itemName] or 0), Asa@9: } Asa@9: Asa@9: end Asa@9: Asa@65: if self.db.factionrealm.items[link] ~= nil then Asa@82: self.db.factionrealm.items[link].count = ItemAuditor:GetItemCount(self:GetIDFromLink(link)) Asa@45: Asa@65: if self.db.factionrealm.items[link].invested == nil then Asa@65: self.db.factionrealm.items[link].invested = 0 Asa@45: end Asa@37: end Asa@37: Asa@65: if viewOnly == true and self.db.factionrealm.items[link] == nil then Asa@9: return {count = 0, invested = 0} Asa@9: elseif viewOnly == true then Asa@28: Asa@65: return {count = self.db.factionrealm.items[link].count, invested = self.db.factionrealm.items[link].invested} Asa@9: end Asa@37: Asa@28: Asa@28: Asa@65: return self.db.factionrealm.items[link] Asa@8: end Asa@8: Asa@63: function ItemAuditor:RemoveItem(link) Asa@9: self.db.factionrealm.item_account[link] = nil Asa@9: link = self:GetSafeLink(link) Asa@9: if link ~= nil then Asa@63: local item = ItemAuditor:GetItem(link) Asa@35: item.invested = 0 Asa@24: else Asa@24: self:Debug('Failed to convert link' .. tostring(link)) Asa@9: end Asa@8: end Asa@8: Asa@63: function ItemAuditor:SaveValue(link, value, countChange) Asa@26: self:Debug("SaveValue(%s, %s, %s)", tostring(link), value, (countChange or 'default')) Asa@26: countChange = countChange or 0 Asa@9: local item = nil Asa@9: local realLink = self:GetSafeLink(link) Asa@9: local itemName = nil Asa@9: if realLink == nil then Asa@26: itemName = link Asa@23: self:Debug('SaveValue: GetSafeLink failed, falling back to storing by name: ' .. tostring(itemName)) Asa@9: self.db.factionrealm.item_account[itemName] = (self.db.factionrealm.item_account[itemName] or 0) + value Asa@9: item = {invested = self.db.factionrealm.item_account[itemName], count = 1} Asa@9: else Asa@23: Asa@9: item = self:GetItem(realLink) Asa@9: item.invested = item.invested + value Asa@9: itemName = GetItemInfo(realLink) Asa@9: end Asa@8: Asa@26: if value > 0 and countChange > 0 and item.invested == value and item.count ~= countChange then Asa@26: local costPerItem = value / countChange Asa@26: value = costPerItem * item.count Asa@26: item.invested = value Asa@26: self:Print("You already owned %s %s with an unknown price, so they have also been updated to %s each", (item.count - countChange), itemName, self:FormatMoney(costPerItem)) Asa@26: end Asa@26: Asa@7: if abs(value) > 0 then Asa@22: if item.invested < 0 then Asa@16: if self.db.profile.messages.cost_updates then Asa@103: self:Print(format("Updated price of %s from %s to %s. |cFF00FF00You just made a profit of %s.", itemName, self:FormatMoney(item.invested - value), self:FormatMoney(0), self:FormatMoney(abs(item.invested)))) Asa@16: end Asa@12: self:RemoveItem(link) Asa@12: -- This doesn't work when you mail the only copy of an item you have to another character. Asa@12: --[[ Asa@82: elseif item.count == 0 and realLink and ItemAuditor:GetItemCount(self:GetIDFromLink(realLink)) then Asa@15: self:Print("You ran out of " .. itemName .. " and never recovered " .. self:FormatMoney(item.invested)) Asa@12: self:RemoveItem(link) Asa@12: ]] Asa@16: else Asa@16: if self.db.profile.messages.cost_updates then Asa@16: self:Print(format("Updated price of %s from %s to %s. (total change:%s)", itemName, self:FormatMoney(item.invested - value), self:FormatMoney(item.invested), self:FormatMoney(value))) Asa@16: end Asa@12: end Asa@0: end Asa@10: Asa@10: if realLink ~= nil then Asa@63: ItemAuditor:UpdateQAThreshold(realLink) Asa@112: self:SendMessage("IA_COST_CHANGED", realLink, unpack({ItemAuditor:GetItemCost(realLink)})) Asa@10: end Asa@35: UpdateInvestedData() Asa@10: end Asa@12: Asa@0: Asa@63: function ItemAuditor:WatchBags() Asa@4: if self.watch_handle == nil then Asa@63: ItemAuditor:UpdateCurrentInventory() Asa@23: self.watch_handle = self:RegisterBucketEvent({"BAG_UPDATE", "PLAYER_MONEY"}, 0.3, "UpdateAudit") Asa@4: end Asa@0: end Asa@0: Asa@63: function ItemAuditor:UnwatchBags() Asa@4: if self.watch_handle ~= nil then Asa@4: self:UnregisterBucket(self.watch_handle) Asa@4: self.watch_handle = nil Asa@4: end Asa@0: end Asa@0: Asa@9: Asa@63: function ItemAuditor:GetSafeLink(link) Asa@9: local newLink = nil Asa@9: Asa@24: if link and link == string.match(link, '.-:[-0-9]+[:0-9]*') then Asa@24: newLink = link Asa@24: elseif link then Asa@9: newLink = link and string.match(link, "|H(.-):([-0-9]+):([0-9]+)|h") Asa@9: end Asa@9: if newLink == nil then Asa@9: local itemID = self:GetItemID(link) Asa@9: if itemID ~= nil then Asa@9: _, newLink = GetItemInfo(itemID) Asa@9: return self:GetSafeLink(newLink) Asa@9: end Asa@9: end Asa@9: return newLink and string.gsub(newLink, ":0:0:0:0:0:0", "") Asa@9: end Asa@9: Asa@63: function ItemAuditor:GetIDFromLink(link) Asa@9: local _, _, _, _, Id = string.find(link, "|?c?f?f?(%x*)|?H?([^:]*):?(%d+):?(%d*):?(%d*):?(%d*):?(%d*):?(%d*):?(%-?%d*):?(%-?%d*):?(%d*)|?h?%[?([^%[%]]*)%]?|?h?|?r?") Asa@9: return tonumber(Id) Asa@9: end Asa@9: Asa@63: function ItemAuditor:GetItemCost(link, countModifier) Asa@9: local item = self:GetItem(link, true) Asa@8: Asa@9: if item.invested > 0 then Asa@9: local count = item.count Asa@9: Asa@9: if countModifier ~= nil then Asa@9: count = count - countModifier Asa@0: end Asa@9: if count > 0 then Asa@45: return ceil(item.invested), ceil(item.invested/count), count Asa@9: end Asa@9: Asa@0: end Asa@82: return 0, 0, ItemAuditor:GetItemCount(ItemAuditor:GetIDFromLink(link)) Asa@0: end