Zerotorescue@80: local addon = select(2, ...); Zerotorescue@84: local mod = addon:NewModule("Scanner", "AceEvent-3.0", "AceTimer-3.0"); Zerotorescue@80: Zerotorescue@80: addon.Locations = { Zerotorescue@80: Bag = 0, Zerotorescue@80: Bank = 1, Zerotorescue@80: Guild = 2, Zerotorescue@80: }; Zerotorescue@80: Zerotorescue@101: local Mover, paused, currentLocation; Zerotorescue@80: local itemCache = {}; Zerotorescue@80: Zerotorescue@101: local function OnMoveAccept(this) Zerotorescue@101: mod:Pause(); Zerotorescue@101: Mover:BeginMove(currentLocation, mod.Unpause); Zerotorescue@101: Zerotorescue@101: InventoriumItemMover:Hide(); Zerotorescue@101: end Zerotorescue@101: Zerotorescue@101: local function OnMoveCancel(this) Zerotorescue@101: Mover:ResetQueue(); Zerotorescue@101: currentLocation = nil; Zerotorescue@101: Zerotorescue@101: InventoriumItemMover:Hide(); Zerotorescue@101: end Zerotorescue@101: Zerotorescue@80: function mod:ClearCache() Zerotorescue@80: table.wipe(itemCache); Zerotorescue@80: end Zerotorescue@80: Zerotorescue@80: function mod:CacheLocation(location, remember) Zerotorescue@89: -- Reset cache just in case it was filled Zerotorescue@89: self:ClearCache(); Zerotorescue@89: Zerotorescue@80: if location == addon.Locations.Bag or location == addon.Locations.Bank then Zerotorescue@80: local start, stop; Zerotorescue@80: if location == addon.Locations.Bag then Zerotorescue@80: start = 0; Zerotorescue@80: stop = NUM_BAG_SLOTS; Zerotorescue@80: else Zerotorescue@80: -- If we requested the bank then we don't want the bag info Zerotorescue@80: start = ( NUM_BAG_SLOTS + 1 ); Zerotorescue@80: stop = ( NUM_BAG_SLOTS + NUM_BANKBAGSLOTS ); Zerotorescue@80: end Zerotorescue@80: Zerotorescue@80: -- Go through all our bags, including the backpack Zerotorescue@81: for i = start, ((location == addon.Locations.Bag and stop) or (location == addon.Locations.Bank and (stop + 1))) do -- if scanning bags stop at normal bag slot, if scanning bank, stop one later to allow BANK_CONTAINER to be scanned too Zerotorescue@80: -- Scan the default 100 slots whenever we're at a non-existing index Zerotorescue@80: local bagId = (i == (stop + 1) and BANK_CONTAINER) or i; Zerotorescue@80: local slotId = GetContainerNumSlots(bagId); Zerotorescue@80: Zerotorescue@80: while slotId ~= 0 do Zerotorescue@80: -- A not equal-comparison should be quicker than a larger than-comparison Zerotorescue@80: Zerotorescue@80: local itemId = GetContainerItemID(bagId, slotId); Zerotorescue@80: local itemCount = itemId and select(2, GetContainerItemInfo(bagId, slotId)); Zerotorescue@80: Zerotorescue@80: if itemId and itemCount and itemCount > 0 then Zerotorescue@80: local itemMove; Zerotorescue@80: if not itemCache[itemId] then Zerotorescue@80: -- If this is the first time we see this item, make a new object Zerotorescue@81: itemMove = addon.ContainerItem:New(); Zerotorescue@81: itemCache[itemId] = itemMove; Zerotorescue@80: else Zerotorescue@80: -- If we had this item in another slot too Zerotorescue@80: itemMove = itemCache[itemId]; Zerotorescue@80: end Zerotorescue@80: Zerotorescue@81: itemMove:AddLocation(bagId, slotId, itemCount); Zerotorescue@80: end Zerotorescue@80: Zerotorescue@80: -- Continue scanning a different slot Zerotorescue@80: slotId = (slotId - 1); Zerotorescue@80: end Zerotorescue@80: end Zerotorescue@80: elseif location == addon.Locations.Guild then Zerotorescue@84: for tabId = 1, GetNumGuildBankTabs() do Zerotorescue@84: local isViewable = select(3, GetGuildBankTabInfo(tabId)); Zerotorescue@80: Zerotorescue@84: if isViewable == 1 then Zerotorescue@84: local slotId = (MAX_GUILDBANK_SLOTS_PER_TAB or 98); -- start by scanning the last slot Zerotorescue@80: Zerotorescue@84: while slotId ~= 0 do Zerotorescue@84: -- A not equal-comparison should be quicker than a larger than-comparison Zerotorescue@84: Zerotorescue@84: local itemLink = GetGuildBankItemLink(tabId, slotId); Zerotorescue@95: local itemId = itemLink and addon:GetItemId(itemLink); Zerotorescue@84: local itemCount = itemLink and select(2, GetGuildBankItemInfo(tabId, slotId)); Zerotorescue@84: Zerotorescue@84: if itemLink and itemId and itemCount and itemCount > 0 then Zerotorescue@84: -- If there is actually an item in this slot Zerotorescue@84: local itemMove; Zerotorescue@84: if not itemCache[itemId] then Zerotorescue@84: -- If this is the first time we see this item, make a new object Zerotorescue@84: itemMove = addon.ContainerItem:New(); Zerotorescue@84: itemCache[itemId] = itemMove; Zerotorescue@84: else Zerotorescue@84: -- If we had this item in another slot too Zerotorescue@84: itemMove = itemCache[itemId]; Zerotorescue@84: end Zerotorescue@84: Zerotorescue@84: itemMove:AddLocation(tabId, slotId, itemCount); Zerotorescue@84: end Zerotorescue@84: Zerotorescue@84: -- Continue scanning a different slot Zerotorescue@84: slotId = (slotId - 1); Zerotorescue@80: end Zerotorescue@80: end Zerotorescue@80: end Zerotorescue@80: else Zerotorescue@82: error("Invalid location provided for CacheLocation. Must be Bank or Guild."); Zerotorescue@80: end Zerotorescue@80: Zerotorescue@80: if not remember then Zerotorescue@80: -- Copy the table as clearing the cache wipes it empty (and tables are passed by reference) Zerotorescue@80: local cacheCopy = CopyTable(itemCache); Zerotorescue@80: Zerotorescue@80: self:ClearCache(); Zerotorescue@80: Zerotorescue@80: return cacheCopy; Zerotorescue@80: end Zerotorescue@80: end Zerotorescue@80: Zerotorescue@80: function mod:Scan(location) Zerotorescue@80: -- We might pause the scanning when we invoke moves ourself Zerotorescue@80: if paused then Zerotorescue@80: return; Zerotorescue@80: end Zerotorescue@80: Zerotorescue@80: local playerName = UnitName("player"); Zerotorescue@80: Zerotorescue@101: currentLocation = location; Zerotorescue@80: self:CacheLocation(location, true); Zerotorescue@80: Zerotorescue@80: -- Go through all groups Zerotorescue@80: for groupName, values in pairs(addon.db.profile.groups) do Zerotorescue@80: local trackAt = addon:GetOptionByKey(groupName, "trackAtCharacters"); Zerotorescue@84: local localItemData = addon:GetOptionByKey(groupName, "localItemData"); Zerotorescue@80: Zerotorescue@82: if values.items and trackAt[playerName] and addon:GetOptionByKey(groupName, "autoRefill") and (location ~= addon.Locations.Bank or not localItemData or not localItemData["Bank"]) then Zerotorescue@80: -- Is this character interested in this data? Zerotorescue@80: Zerotorescue@80: local minLocalStock = addon:GetOptionByKey(groupName, "minLocalStock"); Zerotorescue@80: Zerotorescue@80: -- Go through all items Zerotorescue@80: for itemId, _ in pairs(values.items) do Zerotorescue@80: Zerotorescue@81: -- Check if we have enough items local (but only do so if this location also has enough available) Zerotorescue@81: local missingItems = itemCache[itemId] and (minLocalStock - addon:GetLocalItemCount(itemId, groupName)); Zerotorescue@80: Zerotorescue@81: if itemCache[itemId] and missingItems > 0 then Zerotorescue@80: -- Check how many are available Zerotorescue@81: local availableItems = ((itemCache[itemId] and itemCache[itemId].totalCount) or 0); Zerotorescue@101: -- Calculate how many we'll be moving (less missing than available? use missing, otherwise use available) Zerotorescue@101: local moving = (((missingItems <= availableItems) and missingItems) or availableItems); Zerotorescue@80: Zerotorescue@80: if availableItems > 0 then Zerotorescue@101: --addon:Print("Insufficient " .. IdToItemLink(itemId) .. " but this location has " .. availableItems .. " (moving " .. moving .. ")"); Zerotorescue@80: Zerotorescue@101: Mover:AddMove(itemId, moving, missingItems, availableItems); Zerotorescue@80: else Zerotorescue@101: --addon:Print("Insufficient " .. IdToItemLink(itemId)); Zerotorescue@80: end Zerotorescue@80: end Zerotorescue@80: end Zerotorescue@80: end Zerotorescue@80: end Zerotorescue@80: Zerotorescue@80: self:ClearCache(); Zerotorescue@80: Zerotorescue@81: if Mover:HasMoves() then Zerotorescue@101: if addon.db.profile.defaults.autoRefillSkipConfirm then Zerotorescue@101: OnMoveAccept(true); Zerotorescue@101: else Zerotorescue@106: -- This table is never copied, just referenced. It is the same for every row. Zerotorescue@101: local columns = { Zerotorescue@101: { Zerotorescue@106: value = function(data, cols, realrow, column, table) Zerotorescue@106: return IdToItemLink(data[realrow].rowData.id); Zerotorescue@101: end, Zerotorescue@101: }, -- item Zerotorescue@101: { Zerotorescue@106: value = function(data, cols, realrow, column, table) Zerotorescue@106: return data[realrow].rowData.num; Zerotorescue@101: end, Zerotorescue@101: }, -- moving Zerotorescue@101: { Zerotorescue@106: value = function(data, cols, realrow, column, table) Zerotorescue@106: return addon:DisplayItemCount(data[realrow].rowData.available, data[realrow].rowData.missing); -- available / missing Zerotorescue@101: end, Zerotorescue@106: color = function(data, cols, realrow, column, table) Zerotorescue@106: return ((data[realrow].rowData.available < data[realrow].rowData.missing) and { r = 1, g = 0, b = 0, a = 1 }) or { r = 1, g = 1, b = 1, a = 1 }; Zerotorescue@101: end, Zerotorescue@106: }, -- missing / available Zerotorescue@101: }; Zerotorescue@101: Zerotorescue@106: -- Store the list with rows in this Zerotorescue@106: local data = {}; Zerotorescue@101: Zerotorescue@101: for i, move in pairs(Mover:GetMoves()) do Zerotorescue@106: table.insert(data, { Zerotorescue@106: ["rowData"] = move, -- this is not a key usually found in a row item and ignored by the library Zerotorescue@101: ["cols"] = columns, Zerotorescue@106: }); Zerotorescue@101: end Zerotorescue@101: Zerotorescue@101: addon:SetMoverFrameData(data); Zerotorescue@101: end Zerotorescue@81: end Zerotorescue@81: end Zerotorescue@81: Zerotorescue@84: Zerotorescue@84: Zerotorescue@84: -- Events Zerotorescue@84: Zerotorescue@84: -- Player bank Zerotorescue@84: Zerotorescue@84: function mod:BANKFRAME_OPENED() Zerotorescue@84: addon:Debug("Scanner:BANKFRAME_OPENED"); Zerotorescue@84: Zerotorescue@84: mod:RegisterEvent("BANKFRAME_CLOSED"); Zerotorescue@84: Zerotorescue@84: -- Scan once when the bank is opened, but no need to scan after Zerotorescue@84: mod:Scan(addon.Locations.Bank); Zerotorescue@84: end Zerotorescue@84: Zerotorescue@84: function mod:BANKFRAME_CLOSED() Zerotorescue@81: addon:Debug("Scanner:BANKFRAME_CLOSED"); Zerotorescue@81: Zerotorescue@89: self:ClearCache(); Zerotorescue@89: Zerotorescue@81: mod:UnregisterEvent("BANKFRAME_CLOSED"); Zerotorescue@81: Zerotorescue@101: InventoriumItemMover:Hide(); Zerotorescue@101: Mover:ResetQueue(); Zerotorescue@80: end Zerotorescue@80: Zerotorescue@84: -- Guild bank Zerotorescue@84: Zerotorescue@84: local tmrScanGuild, scanned; Zerotorescue@84: function mod:GUILDBANKBAGSLOTS_CHANGED() Zerotorescue@84: -- This event is spammed the first time the guild bank is opened Zerotorescue@84: if not scanned then Zerotorescue@84: self:CancelTimer(tmrScanGuild, true); -- silent Zerotorescue@84: tmrScanGuild = self:ScheduleTimer("DoScanGuild", 1); Zerotorescue@84: end Zerotorescue@80: end Zerotorescue@80: Zerotorescue@84: function mod:DoScanGuild() Zerotorescue@84: if not scanned then Zerotorescue@89: addon:Debug("Scanner:DoScanGuild"); Zerotorescue@84: Zerotorescue@84: scanned = true; Zerotorescue@84: Zerotorescue@84: self:Scan(addon.Locations.Guild); Zerotorescue@84: end Zerotorescue@84: end Zerotorescue@80: Zerotorescue@84: function mod:GUILDBANKFRAME_CLOSED() Zerotorescue@81: addon:Debug("Scanner:GUILDBANKFRAME_CLOSED"); Zerotorescue@81: Zerotorescue@84: scanned = nil; Zerotorescue@89: self:ClearCache(); Zerotorescue@81: Zerotorescue@84: self:UnregisterEvent("GUILDBANKFRAME_CLOSED"); Zerotorescue@84: self:UnregisterEvent("GUILDBANKBAGSLOTS_CHANGED"); Zerotorescue@84: Zerotorescue@84: self:CancelTimer(tmrScanGuild, true); -- silent Zerotorescue@80: Zerotorescue@101: InventoriumItemMover:Hide(); Zerotorescue@101: Mover:ResetQueue(); Zerotorescue@80: end Zerotorescue@80: Zerotorescue@84: function mod:GUILDBANKFRAME_OPENED() Zerotorescue@81: addon:Debug("Scanner:GUILDBANKFRAME_OPENED"); Zerotorescue@81: Zerotorescue@84: scanned = nil; Zerotorescue@80: Zerotorescue@84: -- Get the contents for every tab into our cache Zerotorescue@84: for tabId = 1, GetNumGuildBankTabs() do Zerotorescue@84: local isViewable = select(3, GetGuildBankTabInfo(tabId)); Zerotorescue@84: if isViewable == 1 then Zerotorescue@84: QueryGuildBankTab(tabId); Zerotorescue@84: end Zerotorescue@84: end Zerotorescue@84: Zerotorescue@84: self:RegisterEvent("GUILDBANKFRAME_CLOSED"); Zerotorescue@84: self:RegisterEvent("GUILDBANKBAGSLOTS_CHANGED"); Zerotorescue@80: end Zerotorescue@80: Zerotorescue@80: function mod:OnEnable() Zerotorescue@80: -- Scan once when the bankframe is opened Zerotorescue@84: self:RegisterEvent("BANKFRAME_OPENED"); Zerotorescue@84: self:RegisterEvent("GUILDBANKFRAME_OPENED"); Zerotorescue@80: Zerotorescue@80: Mover = addon:GetModule("Mover"); Zerotorescue@101: Zerotorescue@101: if not InventoriumItemMover then Zerotorescue@101: addon:CreateMoverFrame(OnMoveAccept, OnMoveCancel); Zerotorescue@101: end Zerotorescue@80: end Zerotorescue@80: Zerotorescue@80: function mod:OnDisable() Zerotorescue@80: Mover = nil; Zerotorescue@101: currentLocation = nil; Zerotorescue@101: paused = nil; Zerotorescue@80: Zerotorescue@80: -- Bank Zerotorescue@84: self:UnregisterEvent("BANKFRAME_OPENED"); Zerotorescue@80: Zerotorescue@80: -- Guild Zerotorescue@84: self:GUILDBANKFRAME_CLOSED(); Zerotorescue@84: self:UnregisterEvent("GUILDBANKFRAME_OPENED"); Zerotorescue@80: end Zerotorescue@80: Zerotorescue@80: function mod:Pause() Zerotorescue@80: paused = true; Zerotorescue@80: end Zerotorescue@80: Zerotorescue@80: function mod:Unpause() Zerotorescue@80: paused = nil; Zerotorescue@80: end