Asa@0: local addon = LibStub("AceAddon-3.0"):NewAddon("ItemAuditor", "AceConsole-3.0", "AceEvent-3.0", "AceBucket-3.0") Asa@0: Asa@0: ItemAuditor = addon Asa@0: Asa@0: local WHITE = "|cFFFFFFFF" Asa@0: local RED = "|cFFFF0000" Asa@0: local GREEN = "|cFF00FF00" Asa@0: local YELLOW = "|cFFFFFF00" Asa@0: local ORANGE = "|cFFFF7F00" Asa@0: local TEAL = "|cFF00FF9A" Asa@0: local GOLD = "|cFFFFD700" Asa@0: Asa@0: function addon:OnInitialize() Asa@0: local DB_defaults = { Asa@0: char = { Asa@0: debug = false Asa@0: }, Asa@0: factionrealm = { Asa@0: item_account = {} Asa@0: }, Asa@0: } Asa@0: self.db = LibStub("AceDB-3.0"):New("ItemAuditorDB", DB_defaults, true) Asa@0: Asa@0: self.db.char.debug = true Asa@0: Asa@0: self:RegisterOptions() Asa@0: Asa@0: self:RegisterEvent("MAIL_SHOW") Asa@0: self:WatchBags() Asa@0: end Asa@0: Asa@1: local function IA_tcount(tab) Asa@0: local n = #tab Asa@0: if (n == 0) then Asa@0: for _ in pairs(tab) do Asa@0: n = n + 1 Asa@0: end Asa@0: end Asa@0: return n Asa@0: end Asa@0: Asa@0: Asa@0: local options = { Asa@0: name = "ItemAuditor", Asa@0: handler = ItemAuditor, Asa@0: type = 'group', Asa@0: args = { Asa@0: debug = { Asa@0: type = "toggle", Asa@0: name = "Debug", Asa@0: desc = "Toggles debug messages in chat", Asa@0: get = "GetDebug", Asa@0: set = "SetDebug" Asa@0: }, Asa@0: dump = { Asa@0: type = "execute", Asa@0: name = "dump", Asa@0: desc = "dumps IA database", Asa@0: func = "DumpInfo", Asa@0: }, Asa@0: options = { Asa@0: type = "execute", Asa@0: name = "options", Asa@0: desc = "Show Blizzard's options GUI", Asa@0: func = "ShowOptionsGUI", Asa@0: guiHidden = true, Asa@0: }, Asa@0: }, Asa@0: } Asa@0: Asa@0: Asa@0: function addon:DumpInfo() Asa@0: self:Print("self.db.char") Asa@0: DevTools_Dump(self.db.char) Asa@0: self:Print("self.db.factionrealm") Asa@0: DevTools_Dump(self.db.factionrealm) Asa@0: end Asa@0: Asa@0: function addon:RegisterOptions() Asa@0: self.optionsFrame = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("ItemAuditor", "ItemAuditor") Asa@0: Asa@0: LibStub("AceConfig-3.0"):RegisterOptionsTable("ItemAuditor", options, {"ia"}) Asa@0: end Asa@0: Asa@0: function addon:GetMessage(info) Asa@0: return self.message Asa@0: end Asa@0: Asa@0: function addon:SetMessage(info, newValue) Asa@0: self.message = newValue Asa@0: end Asa@0: Asa@0: Asa@0: function addon:ShowOptionsGUI() Asa@0: InterfaceOptionsFrame_OpenToCategory(self.optionsFrame) Asa@0: end Asa@0: Asa@0: function addon:GetDebug(info) Asa@0: return self.db.char.debug Asa@0: end Asa@0: Asa@0: function addon:SetDebug(info, input) Asa@0: self.db.char.debug = input Asa@0: local value = "off" Asa@0: if input then Asa@0: value = "on" Asa@0: end Asa@0: self:Print("Debugging is now: " .. value) Asa@0: end Asa@0: Asa@0: Asa@0: -- ================ DEBUG ================ Asa@0: addon.OriginalRegisterEvent = addon.RegisterEvent Asa@0: addon.OriginalUnregisterEvent = addon.UnregisterEvent Asa@0: Asa@0: function addon:RegisterEvent(event, callback, arg) Asa@0: self:Debug("RegisterEvent " .. event ) Asa@0: if arg ~= nil then Asa@0: addon:OriginalRegisterEvent(event, callback, arg) Asa@0: elseif callback ~= nil then Asa@0: addon:OriginalRegisterEvent(event, callback) Asa@0: else Asa@0: addon:OriginalRegisterEvent(event) Asa@0: end Asa@0: end Asa@0: Asa@0: function addon:UnregisterEvent(event) Asa@0: self:Debug("UnregisterEvent " .. event ) Asa@0: addon:OriginalUnregisterEvent (event) Asa@0: end Asa@0: Asa@0: -- ================ DEBUG ================ Asa@0: Asa@0: function addon:FormatMoney(money) Asa@0: return Altoholic:GetMoneyString(money, WHITE, false) Asa@0: end Asa@0: Asa@0: function addon:GetCurrentInventory() Asa@0: local i = {} Asa@0: local link Asa@0: Asa@0: for bagID = 0, NUM_BAG_SLOTS do Asa@0: bagSize=GetContainerNumSlots(bagID) Asa@0: for slotID = 0, bagSize do Asa@0: itemID = GetContainerItemID(bagID, slotID); Asa@0: Asa@0: if itemID ~= nil then Asa@0: _, itemCount, _, _, _= GetContainerItemInfo(bagID, slotID); Asa@0: name = GetItemInfo(itemID) Asa@0: if i[name] == nil then Asa@0: i[name] = 0 Asa@0: end Asa@0: i[name] = i[name] + (itemCount or 0) Asa@0: end Asa@0: Asa@0: end Asa@0: Asa@0: end Asa@0: return {items = i, money = GetMoney()} Asa@0: end Asa@0: Asa@0: function addon:GetInventoryDiff(pastInventory, current) Asa@0: if current == nil then Asa@0: current = self:GetCurrentInventory() Asa@0: end Asa@0: local diff = {} Asa@0: Asa@0: for name, count in pairs(current.items) do Asa@0: if pastInventory.items[name] == nil then Asa@0: diff[name] = count Asa@0: self:Debug("1 diff[" .. name .. "]=" .. diff[name]) Asa@0: elseif count - pastInventory.items[name] ~= 0 then Asa@0: diff[name] = count - pastInventory.items[name] Asa@0: self:Debug("2 diff[" .. name .. "]=" .. diff[name]) Asa@0: end Asa@0: end Asa@0: Asa@0: for name, count in pairs(pastInventory.items) do Asa@0: if current.items[name] == nil then Asa@0: diff[name] = -count Asa@0: self:Debug("3 diff[" .. name .. "]=" .. diff[name]) Asa@0: elseif current.items[name] - count ~= 0 then Asa@0: diff[name] = current.items[name] - pastInventory.items[name] Asa@0: self:Debug("4 diff[" .. name .. "]=" .. diff[name]) Asa@0: end Asa@0: end Asa@0: Asa@0: local moneyDiff = current.money - pastInventory.money Asa@0: Asa@0: return {items = diff, money = moneyDiff} Asa@0: end Asa@0: Asa@0: Asa@0: function addon:ScanMail() Asa@0: local results = {} Asa@0: for mailIndex = 1, GetInboxNumItems() or 0 do Asa@0: local sender, msgSubject, msgMoney, msgCOD, _, msgItem, _, _, msgText, _, isGM = select(3, GetInboxHeaderInfo(mailIndex)) Asa@0: local mailType = Postal:GetMailType(msgSubject) Asa@0: Asa@0: if mailType == "NonAHMail" then Asa@0: -- Don't know how to handle these yet Asa@0: elseif mailType == "AHSuccess" then Asa@0: local invoiceType, itemName, playerName, bid, buyout, deposit, consignment = GetInboxInvoiceInfo(mailIndex); Asa@0: if results[itemName] == nil then Asa@0: results[itemName] = 0 Asa@0: end Asa@0: results[itemName] = results[itemName] + deposit + buyout - consignment Asa@0: Asa@0: elseif mailType == "AHWon" then Asa@0: local invoiceType, itemName, playerName, bid, buyout, deposit, consignment = GetInboxInvoiceInfo(mailIndex); Asa@0: if results[itemName] == nil then Asa@0: results[itemName] = 0 Asa@0: end Asa@0: results[itemName] = results[itemName] - bid Asa@0: elseif mailType == "AHExpired" or mailType == "AHCancelled" then Asa@0: -- These should be handled when you pay the deposit at the AH Asa@0: else Asa@0: self:Debug("Unhandled mail type: " .. mailType) Asa@0: self:Debug(msgSubject) Asa@0: end Asa@0: Asa@0: end Asa@0: return results Asa@0: end Asa@0: Asa@0: function addon:MAIL_SHOW() Asa@0: self:Debug("MAIL_SHOW") Asa@0: self.lastMailScan = self:ScanMail() Asa@0: self:UnregisterEvent("MAIL_SHOW") Asa@0: self:RegisterEvent("MAIL_CLOSED") Asa@0: self:RegisterEvent("MAIL_INBOX_UPDATE") Asa@0: self:Debug("MAIL_SHOW complete") Asa@0: end Asa@0: Asa@0: function addon:MAIL_CLOSED() Asa@0: addon:UnregisterEvent("MAIL_CLOSED") Asa@0: self:UnregisterEvent("MAIL_INBOX_UPDATE") Asa@0: self:RegisterEvent("MAIL_SHOW") Asa@0: end Asa@0: Asa@0: function addon:MAIL_INBOX_UPDATE() Asa@0: local newScan = addon:ScanMail() Asa@0: local diff Asa@0: for item, total in pairs(self.lastMailScan) do Asa@0: Asa@0: if newScan[item] == nil then Asa@0: newScan[item] = 0 Asa@0: end Asa@0: diff = total - newScan[item] Asa@0: if diff ~= 0 then Asa@0: self:SaveValue(item, diff) Asa@0: end Asa@0: Asa@0: end Asa@0: Asa@0: self.lastMailScan = newScan Asa@0: end Asa@0: Asa@0: function addon:SaveValue(item, value) Asa@0: local item_account = self.db.factionrealm.item_account Asa@0: if item_account[item] == nil then Asa@0: item_account[item] = 0 Asa@0: end Asa@0: item_account[item] = item_account[item] + value Asa@0: Asa@0: if item_account[item] >= 0 then Asa@0: item_account[item] = nil Asa@0: end Asa@0: end Asa@0: Asa@0: function addon:OnEnable() Asa@0: self:Debug("Hello, world! OnEnable") Asa@0: end Asa@0: Asa@0: function addon:Debug(msg) Asa@0: if self.db.char.debug then Asa@0: self:Print(msg) Asa@0: end Asa@0: end Asa@0: Asa@0: function addon:WatchBags() Asa@0: if self.watch_handle == nil then Asa@0: self.lastInventory = self:GetCurrentInventory() Asa@0: self.watch_handle = self:RegisterBucketEvent({"BAG_UPDATE", "PLAYER_MONEY"}, 0.2, "UpdateAudit") Asa@0: end Asa@0: end Asa@0: Asa@0: function addon:UnwatchBags() Asa@0: if self.watch_handle ~= nil then Asa@0: self:UnregisterBucket(self.watch_handle) Asa@0: self.watch_handle = nil Asa@0: end Asa@0: end Asa@0: Asa@0: function addon:UpdateAudit() Asa@0: self:Debug("UpdateAudit") Asa@0: local currentInventory = self:GetCurrentInventory() Asa@0: local diff = addon:GetInventoryDiff(self.lastInventory, currentInventory) Asa@0: -- this is only here for debugging Asa@0: self.lastdiff = diff Asa@0: Asa@0: if abs(diff.money) > 0 and IA_tcount(diff.items) == 1 then Asa@0: self:Debug("purchase or sale") Asa@0: Asa@0: for itemName, count in pairs(diff.items) do Asa@0: self:SaveValue(itemName, diff.money) Asa@0: end Asa@0: elseif IA_tcount(diff.items) > 1 then Asa@0: local positive, negative = {}, {} Asa@0: local positiveCount, negativeCount = 0, 0 Asa@0: for item, count in pairs(diff.items) do Asa@0: if count > 0 then Asa@0: positive[item] = count Asa@0: positiveCount = positiveCount + count Asa@0: elseif count < 0 then Asa@0: negative[item] = count Asa@0: negativeCount = negativeCount + abs(count) Asa@0: end Asa@0: end Asa@0: Asa@0: if IA_tcount(positive) > 0 and IA_tcount(negative) > 0 then Asa@0: -- we must have created/converted something Asa@0: self:Debug("conversion") Asa@0: local totalChange = 0 Asa@0: for itemName, change in pairs(negative) do Asa@0: local _, itemCost, count = self:GetItemCost(itemName, change) Asa@0: self:SaveValue(itemName, abs(itemCost * change)) Asa@0: Asa@0: totalChange = totalChange + abs(itemCost * change) Asa@0: end Asa@0: Asa@0: self:Debug("totalChange") Asa@0: self:Debug(totalChange) Asa@0: Asa@0: local valuePerItem = totalChange / positiveCount Asa@0: self:Debug(valuePerItem ) Asa@0: for itemName, change in pairs(positive) do Asa@0: self:Debug(itemName) Asa@0: self:Debug(0-abs(valuePerItem * change)) Asa@0: self:SaveValue(itemName, 0-abs(valuePerItem * change)) Asa@0: end Asa@0: end Asa@0: end Asa@0: Asa@0: self.lastInventory = currentInventory Asa@0: end Asa@0: Asa@0: function addon:GetItemCost(itemName, countModifier) Asa@0: local invested = abs(self.db.factionrealm.item_account[itemName] or 0) Asa@0: Asa@0: if invested > 0 then Asa@0: local _, itemLink = GetItemInfo (itemName); Asa@0: local _, _, _, _, Id = string.find(itemLink, "|?c?f?f?(%x*)|?H?([^:]*):?(%d+):?(%d*):?(%d*):?(%d*):?(%d*):?(%d*):?(%-?%d*):?(%-?%d*):?(%d*)|?h?%[?([^%[%]]*)%]?|?h?|?r?") Asa@0: local count = Altoholic:GetItemCount(tonumber(Id)) Asa@0: if countModifier ~= nil then Asa@0: count = count - countModifier Asa@0: end Asa@0: if count == 0 then Asa@0: self.db.factionrealm.item_account[itemName] = nil Asa@0: self:Print("You ran out of " .. itemName .. "and never recovered " .. self:FormatMoney(invested)) Asa@0: return 0, 0, 0 Asa@0: end Asa@0: return ceil(invested), ceil(invested/count), count Asa@0: end Asa@0: return 0, 0, 0 Asa@0: end Asa@0: Asa@0: function addon:ShowTooltip(tip, link, num) Asa@0: if (link == nil) then Asa@0: return; Asa@0: end Asa@0: Asa@0: local itemName, itemLink, itemRarity, itemLevel, itemMinLevel, itemType, _, _, _, _, itemVendorPrice = GetItemInfo (link); Asa@0: -- local _, _, Color, Ltype, Id, Enchant, Gem1, Gem2, Gem3, Gem4, Suffix, Unique, LinkLvl, Name = string.find(link, "|?c?f?f?(%x*)|?H?([^:]*):?(%d+):?(%d*):?(%d*):?(%d*):?(%d*):?(%d*):?(%-?%d*):?(%-?%d*):?(%d*)|?h?%[?([^%[%]]*)%]?|?h?|?r?") Asa@0: Asa@0: local investedTotal, investedPerItem, count = self:GetItemCost(itemName) Asa@0: Asa@0: local AHCut = 0.05 Asa@0: local keep = 1 - AHCut Asa@0: Asa@0: if investedTotal > 0 then Asa@0: tip:AddDoubleLine("\124cffffffffIA: Total Invested", self:FormatMoney(investedTotal)); Asa@0: tip:AddDoubleLine("\124cffffffffIA: Invested/Item (" .. count .. ")", self:FormatMoney(ceil(investedPerItem))); Asa@0: tip:AddDoubleLine("\124cffffffffIA: Minimum faction AH Price: ", self:FormatMoney(ceil(investedPerItem/keep))) Asa@0: tip:Show() Asa@0: end Asa@0: end Asa@0: Asa@0: local function ShowTipWithPricing(tip, link, num) Asa@0: addon:ShowTooltip(tip, link, num) Asa@0: end Asa@0: Asa@0: hooksecurefunc (GameTooltip, "SetBagItem", Asa@0: function(tip, bag, slot) Asa@0: local _, num = GetContainerItemInfo(bag, slot); Asa@0: ShowTipWithPricing (tip, GetContainerItemLink(bag, slot), num); Asa@0: end Asa@0: ); Asa@0: Asa@0: Asa@0: hooksecurefunc (GameTooltip, "SetAuctionItem", Asa@0: function (tip, type, index) Asa@0: ShowTipWithPricing (tip, GetAuctionItemLink(type, index)); Asa@0: end Asa@0: ); Asa@0: Asa@0: hooksecurefunc (GameTooltip, "SetAuctionSellItem", Asa@0: function (tip) Asa@0: local name, _, count = GetAuctionSellItemInfo(); Asa@0: local __, link = GetItemInfo(name); Asa@0: ShowTipWithPricing (tip, link, num); Asa@0: end Asa@0: ); Asa@0: Asa@0: Asa@0: hooksecurefunc (GameTooltip, "SetLootItem", Asa@0: function (tip, slot) Asa@0: if LootSlotIsItem(slot) then Asa@0: local link, _, num = GetLootSlotLink(slot); Asa@0: ShowTipWithPricing (tip, link, num); Asa@0: end Asa@0: end Asa@0: ); Asa@0: Asa@0: hooksecurefunc (GameTooltip, "SetLootRollItem", Asa@0: function (tip, slot) Asa@0: local _, _, num = GetLootRollItemInfo(slot); Asa@0: ShowTipWithPricing (tip, GetLootRollItemLink(slot), num); Asa@0: end Asa@0: ); Asa@0: Asa@0: Asa@0: hooksecurefunc (GameTooltip, "SetInventoryItem", Asa@0: function (tip, unit, slot) Asa@0: ShowTipWithPricing (tip, GetInventoryItemLink(unit, slot), GetInventoryItemCount(unit, slot)); Asa@0: end Asa@0: ); Asa@0: Asa@0: hooksecurefunc (GameTooltip, "SetGuildBankItem", Asa@0: function (tip, tab, slot) Asa@0: local _, num = GetGuildBankItemInfo(tab, slot); Asa@0: ShowTipWithPricing (tip, GetGuildBankItemLink(tab, slot), num); Asa@0: end Asa@0: ); Asa@0: Asa@0: hooksecurefunc (GameTooltip, "SetTradeSkillItem", Asa@0: function (tip, skill, id) Asa@0: local link = GetTradeSkillItemLink(skill); Asa@0: local num = GetTradeSkillNumMade(skill); Asa@0: if id then Asa@0: link = GetTradeSkillReagentItemLink(skill, id); Asa@0: num = select (3, GetTradeSkillReagentInfo(skill, id)); Asa@0: end Asa@0: Asa@0: ShowTipWithPricing (tip, link, num); Asa@0: end Asa@0: ); Asa@0: Asa@0: hooksecurefunc (GameTooltip, "SetTradePlayerItem", Asa@0: function (tip, id) Asa@0: local _, _, num = GetTradePlayerItemInfo(id); Asa@0: ShowTipWithPricing (tip, GetTradePlayerItemLink(id), num); Asa@0: end Asa@0: ); Asa@0: Asa@0: hooksecurefunc (GameTooltip, "SetTradeTargetItem", Asa@0: function (tip, id) Asa@0: local _, _, num = GetTradeTargetItemInfo(id); Asa@0: ShowTipWithPricing (tip, GetTradeTargetItemLink(id), num); Asa@0: end Asa@0: ); Asa@0: Asa@0: hooksecurefunc (GameTooltip, "SetQuestItem", Asa@0: function (tip, type, index) Asa@0: local _, _, num = GetQuestItemInfo(type, index); Asa@0: ShowTipWithPricing (tip, GetQuestItemLink(type, index), num); Asa@0: end Asa@0: ); Asa@0: Asa@0: hooksecurefunc (GameTooltip, "SetQuestLogItem", Asa@0: function (tip, type, index) Asa@0: local num, _; Asa@0: if type == "choice" then Asa@0: _, _, num = GetQuestLogChoiceInfo(index); Asa@0: else Asa@0: _, _, num = GetQuestLogRewardInfo(index) Asa@0: end Asa@0: Asa@0: ShowTipWithPricing (tip, GetQuestLogItemLink(type, index), num); Asa@0: end Asa@0: ); Asa@0: Asa@0: hooksecurefunc (GameTooltip, "SetInboxItem", Asa@0: function (tip, index, attachIndex) Asa@0: local _, _, num = GetInboxItem(index, attachIndex); Asa@0: ShowTipWithPricing (tip, GetInboxItemLink(index, attachIndex), num); Asa@0: end Asa@0: ); Asa@0: Asa@0: hooksecurefunc (GameTooltip, "SetSendMailItem", Asa@0: function (tip, id) Asa@0: local name, _, num = GetSendMailItem(id) Asa@0: local name, link = GetItemInfo(name); Asa@0: ShowTipWithPricing (tip, link, num); Asa@0: end Asa@0: ); Asa@0: Asa@0: hooksecurefunc (GameTooltip, "SetHyperlink", Asa@0: function (tip, itemstring, num) Asa@0: local name, link = GetItemInfo (itemstring); Asa@0: ShowTipWithPricing (tip, link, num); Asa@0: end Asa@0: ); Asa@0: Asa@0: hooksecurefunc (ItemRefTooltip, "SetHyperlink", Asa@0: function (tip, itemstring) Asa@0: local name, link = GetItemInfo (itemstring); Asa@0: ShowTipWithPricing (tip, link); Asa@0: end Asa@0: );