Mercurial > wow > inventory
view Modules/Scanner.lua @ 110:67bd5057ecb7
Implemented vendor restocking with the mover. Comitting so I can always review this working version, but I?ll be disabling all part of it as it is not going to work properly without seriously compromising the code structure.
Debug messages are now appended with ?Inventorium? (my MailOpener addon was making stuff difficult).
Now properly removing the refill window from the displayed static popup windows list so new popups won?t be aligned at odd locations.
Changed ?CreateMoverFrame? to not contain any scenario-specific info. All settings can be set with SetFrameSettings.
Items that belong to speciality bags are now put there. Other items now ignore spaciality bags.
Implemented test code for mailbox refill support. It has been disabled due to some issues but may be introduced later.
The guild withdrawal limit is now taken into consideration.
Queue is now reset before scanning again.
author | Zerotorescue |
---|---|
date | Fri, 14 Jan 2011 23:25:05 +0100 |
parents | 3bbad0429d87 |
children | 41f0689dfda1 |
line wrap: on
line source
local addon = select(2, ...); local mod = addon:NewModule("Scanner", "AceEvent-3.0", "AceTimer-3.0"); local Mover, paused, currentLocation; local itemCache = {}; local function OnMoveAccept() mod:Pause(); Mover:BeginMove(currentLocation, mod.Unpause); InventoriumItemMover:Hide(); end local function OnMoveCancel() Mover:ResetQueue(); currentLocation = nil; InventoriumItemMover:Hide(); end local function UseStorageRefillST(withPrices) local frame = InventoriumItemMover; -- both for speed as code-consistency -- Scrolling table with a list of items to be moved local scrollTableWidth = ( frame.frmMeasureDummy:GetWidth() - 30 ); -- adjust width by the scrollbar size local headers = { { ["name"] = "Item", ["width"] = (scrollTableWidth * ((withPrices and .5) or .60)), ["defaultsort"] = "asc", ["comparesort"] = function(this, aRow, bRow, column) local aName, _, aRarity = GetItemInfo(this:GetRow(aRow).rowData.itemId); local bName, _, bRarity = GetItemInfo(this:GetRow(bRow).rowData.itemId); local template = "%d%s"; aName = template:format((10 - (aRarity or 10)), (aName or ""):lower()); bName = template:format((10 - (bRarity or 10)), (bName or ""):lower()); if this.cols[column].sort == "dsc" then return aName > bName; else return aName < bName; end end, ["sort"] = "asc", -- when the data is set, use this column so sort the default data ["tooltipTitle"] = (not addon.db.profile.defaults.hideHelp and "Item"), ["tooltip"] = (not addon.db.profile.defaults.hideHelp and "Click to sort the list by item quality then item name."), }, { ["name"] = "Moving", ["width"] = (scrollTableWidth * .15), ["align"] = "RIGHT", ["defaultsort"] = "dsc", ["sortnext"] = 1, ["tooltipTitle"] = (not addon.db.profile.defaults.hideHelp and "Moving"), ["tooltip"] = (not addon.db.profile.defaults.hideHelp and "Click to sort the list by the amount of movable items."), }, { ["name"] = "Available", ["width"] = (scrollTableWidth * ((withPrices and .2) or .25)), ["align"] = "RIGHT", ["defaultsort"] = "dsc", ["sortnext"] = 1, ["comparesort"] = function(this, aRow, bRow, column) local aAvailablePercent = (this:GetRow(aRow).rowData.available / this:GetRow(aRow).rowData.missing); local bAvailablePercent = (this:GetRow(bRow).rowData.available / this:GetRow(bRow).rowData.missing); if this.cols[column].sort == "dsc" then return aAvailablePercent > bAvailablePercent; else return aAvailablePercent < bAvailablePercent; end end, ["tooltipTitle"] = (not addon.db.profile.defaults.hideHelp and "Item"), ["tooltip"] = (not addon.db.profile.defaults.hideHelp and "Click to sort the list by the availibility percentage."), }, }; if withPrices then table.insert(headers, { ["name"] = "Price", ["width"] = (scrollTableWidth * .15), ["align"] = "RIGHT", ["defaultsort"] = "dsc", ["sortnext"] = 1, ["tooltipTitle"] = (not addon.db.profile.defaults.hideHelp and "Price"), ["tooltip"] = (not addon.db.profile.defaults.hideHelp and "Click to sort the list by the price of this item at this vendor."), }); end local proceedButton = { text = "Move Items", tooltipTitle = (not addon.db.profile.defaults.hideHelp and "Move Items"), tooltip = (not addon.db.profile.defaults.hideHelp and "Start moving these items from the bank."), onClick = OnMoveAccept, }; local cancelButton = { text = "Cancel", tooltipTitle = (not addon.db.profile.defaults.hideHelp and "Cancel"), tooltip = (not addon.db.profile.defaults.hideHelp and "Do not move anything and close the window."), onClick = OnMoveCancel, }; addon:SetFrameSettings("Inventorium Bank Refill", "The items listed below can be refilled from this location, do you wish to move them to your bags?", proceedButton, cancelButton, headers); end function mod:ClearCache() table.wipe(itemCache); end function mod:CacheLocation(location, remember) -- Reset cache just in case it was filled self:ClearCache(); if location == addon.Locations.Bag or location == addon.Locations.Bank then local start, stop; if location == addon.Locations.Bag then start = 0; stop = NUM_BAG_SLOTS; else -- If we requested the bank then we don't want the bag info start = ( NUM_BAG_SLOTS + 1 ); stop = ( NUM_BAG_SLOTS + NUM_BANKBAGSLOTS ); end -- Go through all our bags, including the backpack 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 -- Scan the default 100 slots whenever we're at a non-existing index local bagId = (i == (stop + 1) and BANK_CONTAINER) or i; local slotId = GetContainerNumSlots(bagId); while slotId ~= 0 do -- A not equal-comparison should be quicker than a larger than-comparison local itemId = GetContainerItemID(bagId, slotId); local itemCount = itemId and select(2, GetContainerItemInfo(bagId, slotId)); if itemId and itemCount and itemCount > 0 then local itemMove; if not itemCache[itemId] then -- If this is the first time we see this item, make a new object itemMove = addon.ContainerItem:New(); itemCache[itemId] = itemMove; else -- If we had this item in another slot too itemMove = itemCache[itemId]; end itemMove:AddLocation(bagId, slotId, itemCount); end -- Continue scanning a different slot slotId = (slotId - 1); end end elseif location == addon.Locations.Guild then for tabId = 1, GetNumGuildBankTabs() do local _, _, isViewable, _, _, remainingWithdrawals = GetGuildBankTabInfo(tabId); if isViewable and (remainingWithdrawals > 0 or remainingWithdrawals == -1) then local slotId = (MAX_GUILDBANK_SLOTS_PER_TAB or 98); -- start by scanning the last slot while slotId ~= 0 do -- A not equal-comparison should be quicker than a larger than-comparison local itemLink = GetGuildBankItemLink(tabId, slotId); local itemId = itemLink and addon:GetItemId(itemLink); local itemCount = itemLink and select(2, GetGuildBankItemInfo(tabId, slotId)); if itemLink and itemId and itemCount and itemCount > 0 then -- If there is actually an item in this slot local itemMove; if not itemCache[itemId] then -- If this is the first time we see this item, make a new object itemMove = addon.ContainerItem:New(); itemCache[itemId] = itemMove; else -- If we had this item in another slot too itemMove = itemCache[itemId]; end itemMove:AddLocation(tabId, slotId, itemCount); end -- Continue scanning a different slot slotId = (slotId - 1); end end end elseif location == addon.Locations.Mailbox then for mailIndex = 1, GetInboxNumItems() do -- All mail items for attachIndex = 1, ATTACHMENTS_MAX_RECEIVE do -- All attachments local itemLink = GetInboxItemLink(mailIndex, attachIndex); local itemId = itemLink and addon:GetItemId(itemLink); local itemCount = itemLink and select(3, GetInboxItem(mailIndex, attachIndex)); if itemLink and itemId and itemCount and itemCount > 0 then local itemMove; if not itemCache[itemId] then -- If this is the first time we see this item, make a new object itemMove = addon.ContainerItem:New(); itemCache[itemId] = itemMove; else -- If we had this item in another slot too itemMove = itemCache[itemId]; end itemMove:AddLocation(mailIndex, attachIndex, itemCount); end end end elseif location == addon.Locations.Merchant then for itemIndex = 1, GetMerchantNumItems() do -- All merchant items local itemLink = GetMerchantItemLink(itemIndex); local itemId = itemLink and addon:GetItemId(itemLink); local _, _, vendorValue, _, numAvailable, _, extendedCost = GetMerchantItemInfo(index); if itemLink and itemId and numAvailable ~= 0 and not extendedCost then local itemMove; if not itemCache[itemId] then -- If this is the first time we see this item, make a new object itemMove = addon.ContainerItem:New(); itemCache[itemId] = itemMove; else -- If we had this item in another slot too itemMove = itemCache[itemId]; end itemMove:AddLocation(1, itemIndex, numAvailable, vendorValue); end end else error("Invalid location provided for CacheLocation. Must be Bank or Guild."); end if not remember then -- Copy the table as clearing the cache wipes it empty (and tables are passed by reference) local cacheCopy = CopyTable(itemCache); self:ClearCache(); return cacheCopy; end end function mod:Scan(location) -- We might pause the scanning when we invoke moves ourself if paused then addon:Debug("Not scanning; paused..."); return; end local playerName = UnitName("player"); currentLocation = location; self:CacheLocation(location, true); -- Ensure previous queue isn't remaining Mover:ResetQueue(); -- Go through all groups for groupName, values in pairs(addon.db.profile.groups) do local trackAt = addon:GetOptionByKey(groupName, "trackAtCharacters"); local localItemData = addon:GetOptionByKey(groupName, "localItemData"); if values.items and trackAt[playerName] and addon:GetOptionByKey(groupName, "autoRefill") and (location ~= addon.Locations.Bank or not localItemData or not localItemData["Bank"]) then -- Is this character interested in this data? local minLocalStock = addon:GetOptionByKey(groupName, "minLocalStock"); -- Go through all items for itemId, _ in pairs(values.items) do -- Check if we have enough items local (but only do so if this location also has enough available) local missingItems = itemCache[itemId] and (minLocalStock - addon:GetLocalItemCount(itemId, groupName)); if itemCache[itemId] and missingItems > 0 then -- Check how many are available local availableItems = ((itemCache[itemId] and itemCache[itemId].totalCount) or 0); -- Calculate how many we'll be moving (less missing than available? use missing, otherwise use available) local moving = (((availableItems == -1 or missingItems <= availableItems) and missingItems) or availableItems); if availableItems ~= 0 then addon:Debug("Insufficient %s but this location has %s (moving %d)", IdToItemLink(itemId), ((availableItems == -1 and "unlimited") or availableItems), moving); Mover:AddMove(itemId, moving, missingItems, availableItems, itemCache[itemId]:GetVendorPrice()); end end end end end self:ClearCache(); if Mover:HasMoves() then if addon.db.profile.defaults.autoRefillSkipConfirm then OnMoveAccept(); else UseStorageRefillST((location == addon.Locations.Merchant)); -- This table is never copied, just referenced. It is the same for every row. local columns = { { ["value"] = function(data, cols, realrow, column, table) return IdToItemLink(data[realrow].rowData.itemId); end, }, -- item { ["value"] = function(data, cols, realrow, column, table) return data[realrow].rowData.num; end, }, -- moving { ["value"] = function(data, cols, realrow, column, table) return addon:DisplayItemCount(data[realrow].rowData.available, data[realrow].rowData.missing); -- available / missing end, ["color"] = function(data, cols, realrow, column, table) 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 }; end, }, -- missing / available }; if location == addon.Locations.Merchant then table.insert(columns, { ["value"] = function(data, cols, realrow, column, table) return GetCoinTextureString(data[realrow].rowData.price * data[realrow].rowData.num); end, }); end -- Store the list with rows in this local data = {}; for i, move in pairs(Mover:GetMoves()) do table.insert(data, { ["rowData"] = move, -- this is not a key usually found in a row item and ignored by the library ["cols"] = columns, }); end addon:SetMoverFrameData(data); end end end -- Events -- Player bank function mod:BANKFRAME_OPENED() addon:Debug("Scanner:BANKFRAME_OPENED"); mod:RegisterEvent("BANKFRAME_CLOSED"); -- Scan once when the bank is opened, but no need to scan after mod:Scan(addon.Locations.Bank); end function mod:BANKFRAME_CLOSED() addon:Debug("Scanner:BANKFRAME_CLOSED"); self:ClearCache(); mod:UnregisterEvent("BANKFRAME_CLOSED"); InventoriumItemMover:Hide(); Mover:ResetQueue(); end -- Guild bank local tmrScanGuild, scanned; function mod:GUILDBANKBAGSLOTS_CHANGED() -- This event is spammed the first time the guild bank is opened if not scanned then self:CancelTimer(tmrScanGuild, true); -- silent tmrScanGuild = self:ScheduleTimer("DoScanGuild", 1); end end function mod:DoScanGuild() if not scanned then addon:Debug("Scanner:DoScanGuild"); scanned = true; self:Scan(addon.Locations.Guild); end end function mod:GUILDBANKFRAME_CLOSED() addon:Debug("Scanner:GUILDBANKFRAME_CLOSED"); scanned = nil; self:ClearCache(); self:UnregisterEvent("GUILDBANKFRAME_CLOSED"); self:UnregisterEvent("GUILDBANKBAGSLOTS_CHANGED"); self:CancelTimer(tmrScanGuild, true); -- silent InventoriumItemMover:Hide(); Mover:ResetQueue(); end function mod:GUILDBANKFRAME_OPENED() addon:Debug("Scanner:GUILDBANKFRAME_OPENED"); scanned = nil; -- Get the contents for every tab into our cache for tabId = 1, GetNumGuildBankTabs() do local _, _, isViewable, _, _, remainingWithdrawals = GetGuildBankTabInfo(tabId); if isViewable and (remainingWithdrawals > 0 or remainingWithdrawals == -1) then QueryGuildBankTab(tabId); end end self:RegisterEvent("GUILDBANKFRAME_CLOSED"); self:RegisterEvent("GUILDBANKBAGSLOTS_CHANGED"); end function mod:MERCHANT_SHOW() addon:Debug("Scanner:MERCHANT_SHOW"); self:RegisterEvent("MERCHANT_CLOSED"); self:Scan(addon.Locations.Merchant); end function mod:MERCHANT_CLOSED() addon:Debug("Scanner:MERCHANT_CLOSED"); self:ClearCache(); self:UnregisterEvent("MERCHANT_CLOSED"); InventoriumItemMover:Hide(); Mover:ResetQueue(); end --local previousMailCount; --function mod:MAIL_SHOW() -- addon:Debug("Scanner:MAIL_SHOW"); -- -- self:RegisterEvent("MAIL_INBOX_UPDATE"); -- self:RegisterEvent("MAIL_CLOSED"); -- -- scanned = nil; -- previousMailCount = nil; -- -- self:Scan(addon.Locations.Mailbox); --end --function mod:MAIL_INBOX_UPDATE() -- if not scanned then -- addon:Debug("Scanner:MAIL_INBOX_UPDATE"); -- -- local current, total = GetInboxNumItems(); -- -- if not previousMailCount or current > previousMailCount then -- -- New mail received -- -- scanned = true; -- -- self:Scan(addon.Locations.Mailbox); -- end -- -- -- Also remember the new mailcount when losing items, otherwise deleting item 50 and getting to 50 again wouldn't trigger a re-scan -- previousMailCount = current; -- else -- addon:Debug("Scanner:MAIL_INBOX_UPDATE skipped, already scanned"); -- end --end --function mod:MAIL_CLOSED() -- addon:Debug("Scanner:MAIL_CLOSED"); -- -- previousMailCount = nil; -- scanned = nil; -- self:ClearCache(); -- -- self:UnregisterEvent("MAIL_INBOX_UPDATE"); -- self:UnregisterEvent("MAIL_CLOSED"); -- -- InventoriumItemMover:Hide(); -- Mover:ResetQueue(); --end function mod:OnEnable() -- Scan once when the bankframe is opened self:RegisterEvent("BANKFRAME_OPENED"); self:RegisterEvent("GUILDBANKFRAME_OPENED"); -- self:RegisterEvent("MAIL_SHOW"); self:RegisterEvent("MERCHANT_SHOW"); Mover = addon:GetModule("Mover"); if not InventoriumItemMover then addon:CreateMoverFrame(OnMoveAccept, OnMoveCancel); end end function mod:OnDisable() Mover = nil; currentLocation = nil; paused = nil; -- Bank self:BANKFRAME_CLOSED(); self:UnregisterEvent("BANKFRAME_OPENED"); -- Guild self:GUILDBANKFRAME_CLOSED(); self:UnregisterEvent("GUILDBANKFRAME_OPENED"); -- -- Mailbox -- self:MAIL_CLOSED(); -- self:UnregisterEvent("MAIL_SHOW"); end function mod:Pause() paused = true; end function mod:Unpause() paused = nil; end