Mercurial > wow > inventory
view Mover.lua @ 80:c0bf2ddb5288
Added initial item refilling from the bank/guild. Not yet fully functional.
author | Zerotorescue |
---|---|
date | Wed, 05 Jan 2011 13:05:15 +0100 |
parents | |
children | 58617c7827fa |
line wrap: on
line source
local addon = select(2, ...); local mod = addon:NewModule("Mover", "AceEvent-3.0", "AceTimer-3.0"); local Scanner; local queuedMoves = {}; -- table storing all queued moves before BeginMove is called local combinedMoves = {}; -- table storing all combined moves (with source and target) that is to be processed by the actual mover in the order of the index (1 to #) function mod:AddMove(itemId, amount) table.insert(queuedMoves, { id = itemId, num = amount, }); end function mod:BeginMove(location, onFinish) -- Find the outgoing moves -- We need the source container and slot, find all the requires sources and put them in a list which we go through later to find matching targets -- Get a list of items in the source container local sourceContents = Scanner:CacheLocation(location, false); local outgoingMoves = {}; for singleMove in pairs(queuedMoves) do local sourceItem = sourceContents[singleMove.id]; if not sourceItem then print("Can't move " .. IdToItemLink(singleMove.id) .. ", non-existant in source"); else -- We want to move the smallest stacks first to keep stuff pretty table.sort(sourceItem.locations, function(a, b) return a.count > b.count; end); for itemLocation in pairs(sourceItem.locations) do -- if this location has more items than we need, only move what we need, otherwise move everything in this stack local movingNum = ((itemLocation.count > singleMove.num and singleMove.num) or itemLocation.count); table.insert(outgoingMoves, { itemId = singleMove.id, container = itemLocation.container, slot = itemLocation.slot, count = movingNum, }); singleMove.num = (singleMove.num - movingNum); if singleMove.num == 0 then -- If we have prepared everything we wanted, go to the next queued move break; end end end end -- No longer needed table.wipe(queuedMoves); -- Process every single outgoing move and find fitting targets -- Get a list of items already in the target container local targetContents = Scanner:CacheLocation(addon.Locations.Bag, false); -- Find all empty slots local emptySlots = {}; local start = 0; local stop = NUM_BAG_SLOTS; -- Go through all our bags, including the backpack for bagId = start, stop do -- Go through all our slots for slotId = 1, GetContainerNumSlots(bagId) do local itemId = GetContainerItemID(bagId, slotId); if not itemId then table.insert(emptySlots, { container: bagId, slot: slotId, }); end end end while #outgoingMoves ~= 0 do -- A not equal-comparison should be quicker than a larger/smaller than-comparison for outgoingMove in pairs(outgoingMoves) do -- itemId will be set to nil when this outgoing move was processed - sanity check if outgoingMove.itemId then local targetItem = targetContents[outgoingMove.itemId]; if not targetItem then -- grab an empty slot -- make new instance of ItemMove -- populate targetContents with it so future moves of this item can be put on top of it if this isn't a full stack local firstAvailableSlot = emptySlots[1]; if not firstAvailableSlot then print("Bags are full. Skipping " .. IdToItemLink(outgoingMove.itemId) .. "."); outgoingMove.itemId = nil; else table.insert(combinedMoves, { sourceContainer = outgoingMove.container, sourceSlot = outgoingMove.slot, targetContainer = firstAvailableSlot.container, targetSlot = firstAvailableSlot.slot, itemId = outgoingMove.itemId, num = outgoingMove.count, }); -- We filled an empty slot so the target contents now has one more item, -- make a new instance of the ItemMove class so any additional items with this id can be stacked on top of it local itemMove = addon.ItemMove:New(); itemMove.AddLocation(firstAvailableSlot.container, firstAvailableSlot.slot, outgoingMove.count); targetContents[outgoingMove.itemId] = itemMove; firstAvailableSlot = nil; -- no longer empty outgoingMove.count = 0; -- nothing remaining - sanity check outgoingMove.itemId = nil; -- remove this record from the outgoingMoves-table end else -- Find the maximum stack size for this item local itemStackCount = select(8, GetItemInfo(outgoingMove.itemId)); -- We want to move to the largest stacks first to keep stuff pretty table.sort(targetItem.locations, function(a, b) return a.count < b.count; end); for itemLocation in pairs(targetItem.locations) do if itemLocation.count < itemStackCount and outgoingMove.count > 0 then -- Check if this stack isn't already full (and we still need to move this item) local remainingSpace = (itemStackCount - itemLocation.count); if remainingSpace > outgoingMove.count then -- Enough room to move this entire stack -- Deposit this item and then forget this outgoing move as everything in it was processed table.insert(combinedMoves, { sourceContainer = outgoingMove.container, sourceSlot = outgoingMove.slot, targetContainer = itemLocation.container, targetSlot = itemLocation.slot, itemId = outgoingMove.itemId, num = outgoingMove.count, }); itemLocation.count = (itemLocation.count + outgoingMove.count); outgoingMove.count = 0; -- nothing remaining outgoingMove.itemId = nil; -- remove this record from the outgoingMoves-table break; -- stop the locations-loop else -- Deposit this item but don't remove the outgoing move as there are some items left to move table.insert(combinedMoves, { sourceContainer = outgoingMove.container, sourceSlot = outgoingMove.slot, targetContainer = itemLocation.container, targetSlot = itemLocation.slot, itemId = outgoingMove.itemId, num = outgoingMove.count, }); -- The target will be full when we complete, but the source will still have remaining items left to be moved itemLocation.count = itemStackCount; outgoingMove.count = (outgoingMove.count - remainingSpace); end end end if outgoingMove.count > 0 then -- We went through all matching items and checked their stack sizes if we could move this there, no room available -- So forget about the target item (even though it may just have full locations, these are useless anyway) and the next loop move it onto an empty slot targetItem = nil; end end end end -- Loop through the array to find items that should be removed, start with the last element or the loop would break local numOutgoingMoves = #outgoingMoves; -- since LUA-tables start at an index of 1, this is actually an existing index (outgoingMoves[#outgoingMoves] would return a value) while numOutgoingMoves ~= 0 do -- A not equal-comparison should be quicker than a larger/smaller than-comparison -- Check if the item id is nil, this is set to nil when this outgoing move has been processed if not outgoingMoves[numOutgoingMoves].itemId then -- Remove this element from the array table.remove(outgoingMoves, numOutgoingMoves); end -- Proceed with the next element (or previous considering we're going from last to first) numOutgoingMoves = (numOutgoingMoves - 1); end end -- No longer needed table.wipe(emptySlots); DoMoveNow(); --ProcessMove(); --mod:RegisterEvent("BAG_UPDATE", BAG_UPDATE); --onFinish(); end function DoMoveNow() -- combinedMoves now has all moves in it (source -> target) -- go through list, move everything inside it -- add source and target to one single list -- if either is already in this list, skip this move -- repeat every 5 seconds until we're completely done local sourceLocationsLocked = {}; local targetLocationsLocked = {}; local numCurrentMove = #combinedMoves; while numCurrentMove ~= 0 do local move = combinedMoves[numCurrentMove]; -- sourceContainer, sourceSlot, targetContainer, targetSlot, itemId, num if (not sourceLocationsLocked[move.sourceContainer] or not sourceLocationsLocked[move.sourceContainer][move.sourceSlot]) and (not targetLocationsLocked[move.targetContainer] or not targetLocationsLocked[move.targetContainer][move.targetSlot]) then print("Moving " .. IdToItemLink(move.itemId)); -- Pickup stack SplitGuildBankItem(move.sourceContainer, move.sourceSlot, move.num); -- Remember we picked this item up and thus it is now locked if not sourceLocationsLocked[move.sourceContainer] then sourceLocationsLocked[move.sourceContainer] = {}; end sourceLocationsLocked[move.sourceContainer][move.sourceSlot] = true; if CursorHasItem() then -- And drop it PickupContainerItem(move.targetContainer, move.targetSlot); -- Remember we dropped an item here and thus this is now locked if not sourceLocationsLocked[move.targetContainer] then sourceLocationsLocked[move.targetContainer] = {}; end sourceLocationsLocked[move.targetContainer][move.targetSlot] = true; -- This move was processed table.remove(combinedMoves, numCurrentMove); end end -- Proceed with the next element (or previous considering we're going from last to first) numCurrentMove = (numCurrentMove - 1); end end local tmrProcessNext; function BAG_UPDATE() mod:CancelTimer(tmrProcessNext, true); -- silent tmrProcessNext = mod:ScheduleTimer(function() ProcessMove(); end, 2); end function ProcessMove() local currentMove = queuedMoves[1]; if currentMove then addon:Debug("Moving " .. currentMove.num .. " of " .. IdToItemLink(currentMove.id)); local requestedMoves = currentMove.num; if currentMove.src == addon.Locations.Bank then MoveBankItem(currentMove); elseif currentMove.src == addon.Locations.Guild then MoveGuildItem(currentMove); end if requestedMoves == currentMove.num then print("Skipping " .. IdToItemLink(move.id)); move.num = 0; elseif currentMove.num > 0 then -- bags are full print("bags are full"); end if currentMove.num == 0 then table.remove(queuedMoves, 1); end end end function MoveGuildItem(move) local tabId = GetCurrentGuildBankTab(); local slotId = (MAX_GUILDBANK_SLOTS_PER_TAB or 98); -- start by scanning the last slot if tabId == nil or tabId < 1 then return; end while slotId ~= 0 do -- A not equal-comparison should be quicker than a larger than-comparison local itemLink = GetGuildBankItemLink(tabId, slotId); if itemLink then -- If there is actually an item in this slot local itemId = GetItemId(itemLink); if itemId and move.id == itemId then -- This is one of the items we're looking for local itemCount = select(2, GetGuildBankItemInfo(tabId, slotId)); if itemCount and itemCount > 0 then -- if the amount we still have to move is more than this stack, move the entire stack, otherwise move the still needed part of the stack local moveable = (move.num > itemCount and itemCount) or move.num; -- Pickup stack SplitGuildBankItem(tabId, slotId, moveable); -- Find an target slot and put it there local reallyMoved = DropItem(itemId, moveable); if reallyMoved then -- Keep track of how many we have moved moved = (moved + reallyMoved); -- Update the required amount of items so it has the remaining num move.num = (move.num - reallyMoved); --if reallyMoved ~= moveable then -- -- Scan this slot again because if we moved a 18 stack onto a 16 stack, we'd actually have moved only 4 items and still need to move the remaining 14 (we're capping stacks before using empty slots) -- slotId = (slotId + 1); --end --if move.num == 0 then -- if no required items are left to move, then stop and tell the caller function how many we moved return moved; --end end end end end -- Continue scanning a different slot slotId = (slotId - 1); end end function MoveBankItem(move) local start = 0; local stop = NUM_BAG_SLOTS; -- If we requested the bank then we don't want the bag info start = ( NUM_BAG_SLOTS + 1 ); stop = ( NUM_BAG_SLOTS + NUM_BANKBAGSLOTS ); -- Scan the default 100 slots move.num = (move.num - MoveFromContainter(BANK_CONTAINER, move)); -- Go through all our bags, including the backpack for bagID = start, stop do move.num = (move.num - MoveFromContainter(bagID, move)); end end -- Go through all slots of this bag and if a match was found, move the items function MoveFromContainter(bagID, move) -- Keep track of how many we have moved local moved = 0; -- Go through all slots of this bag for slot = 1, GetContainerNumSlots(bagID) do local itemId = GetContainerItemID(bagID, slot); if itemId and move.id == itemId then -- This is one of the items we're looking for local itemCount = select(2, GetContainerItemInfo(bagID, slot)); if itemCount and itemCount > 0 then -- if the amount we still have to move is more than this stack, move the entire stack, otherwise move the still needed part of the stack local moveable = (move.num > itemCount and itemCount) or move.num; -- Pickup stack SplitContainerItem(bagID, slot, moveable); addon:Debug("Picked " .. IdToItemLink(itemId) .. " up"); -- Find an target slot and put it there if CursorHasItem() then local reallyMoved = DropItem(itemId, moveable); if reallyMoved then addon:Debug("Dropped " .. reallyMoved .. " of " .. IdToItemLink(itemId)); -- Keep track of how many we have moved moved = (moved + reallyMoved); -- Update the required amount of items so it has the remaining num move.num = (move.num - reallyMoved); --if reallyMoved ~= moveable then -- Scan this slot again because if we moved a 18 stack onto a 16 stack, we'd actually have moved only 4 items and still need to move the remaining 14 (we're capping stacks before using empty slots) -- slot = (slot - 1); --end --if move.num == 0 then -- if no required items are left to move, then stop and tell the caller function how many we moved return moved; --end end end end end end return moved; end -- This currently only uses empty slots, it will have to fill stacks in the future function DropItem(itemId, amount) local start = 0; local stop = NUM_BAG_SLOTS; -- Go through all our bags, including the backpack for bagID = start, stop do -- Go through all our slots for slot = 1, GetContainerNumSlots(bagID) do local itemId = GetContainerItemID(bagID, slot); if not itemId then -- If this slot is empty, put the item here PickupContainerItem(bagID, slot); return amount; end end end return; end function IdToItemLink(itemId) return select(2, GetItemInfo(itemId)); end function mod:OnEnable() Scanner = addon:GetModule("Scanner"); end function mod:OnDisable() Scanner = nil; end