diff Mover.lua @ 81:58617c7827fa

Item refilling should now be working. Probably very slow if your bags, bank or guild bank is filled with over 200 unique items or so (needs some real testing, but know that it is a known (possible) issue).
author Zerotorescue
date Thu, 06 Jan 2011 01:01:25 +0100
parents c0bf2ddb5288
children f885805da5d6
line wrap: on
line diff
--- a/Mover.lua	Wed Jan 05 13:05:15 2011 +0100
+++ b/Mover.lua	Thu Jan 06 01:01:25 2011 +0100
@@ -4,6 +4,7 @@
 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 #)
+local movesSource;
 
 function mod:AddMove(itemId, amount)
 	table.insert(queuedMoves, {
@@ -12,7 +13,12 @@
 	});
 end
 
+function mod:HasMoves()
+	return (#queuedMoves ~= 0);
+end
+
 function mod:BeginMove(location, onFinish)
+	addon:Debug("BeginMove");
 	
 	-- 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
@@ -22,17 +28,17 @@
 	
 	local outgoingMoves = {};
 
-	for singleMove in pairs(queuedMoves) do
+	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;
+				return a.count < b.count;
 			end);
 			
-			for itemLocation in pairs(sourceItem.locations) do
+			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);
 				
@@ -47,7 +53,7 @@
 				
 				if singleMove.num == 0 then
 					-- If we have prepared everything we wanted, go to the next queued move
-					break;
+					break; -- stop the locations-loop
 				end
 			end
 		end
@@ -76,17 +82,20 @@
 			
 			if not itemId then
 				table.insert(emptySlots, {
-					container: bagId,
-					slot: slotId,
+					container = bagId,
+					slot = slotId,
 				});
 			end
 		end
 	end
 	
+	-- Remember where we're moving from
+	movesSource = location;
+	
 	while #outgoingMoves ~= 0 do
 		-- A not equal-comparison should be quicker than a larger/smaller than-comparison
 		
-		for outgoingMove in pairs(outgoingMoves) do
+		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];
@@ -114,11 +123,11 @@
 						
 						-- 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);
+						local itemMove = addon.ContainerItem:New();
+						itemMove:AddLocation(firstAvailableSlot.container, firstAvailableSlot.slot, outgoingMove.count);
 						targetContents[outgoingMove.itemId] = itemMove;
 						
-						firstAvailableSlot = nil; -- no longer empty
+						table.remove(emptySlots, 1); -- no longer empty
 						
 						outgoingMove.count = 0; -- nothing remaining - sanity check
 						outgoingMove.itemId = nil; -- remove this record from the outgoingMoves-table
@@ -129,10 +138,10 @@
 					
 					-- 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;
+						return a.count > b.count;
 					end);
 					
-					for itemLocation in pairs(targetItem.locations) do
+					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)
 							
@@ -201,37 +210,84 @@
 	-- No longer needed
 	table.wipe(emptySlots);
 	
-	DoMoveNow();
+	self:ProcessMove();
 	
-	--ProcessMove();
+	self:RegisterEvent("BAG_UPDATE");
 	
-	--mod:RegisterEvent("BAG_UPDATE", BAG_UPDATE);
-	
-	--onFinish();
+	onFinish();
 end
 
-function DoMoveNow()
+if not table.reverse then
+-- 	table.reverse = function(orig)
+-- 		local temp = CopyTable(orig);
+-- 		local origLength = #temp;
+-- 		for i = 1, origLength do
+-- 			orig[(origLength - i + 1)] = temp[i];
+-- 		end
+-- 	end
+	table.reverse = function(orig)
+		local temp = {};
+		local origLength = #orig;
+		for i = 1, origLength do
+			temp[(origLength - i + 1)] = orig[i];
+		end
+		
+-- 		-- Update the original table (can't do orig = temp as that would change the reference-link instead of the original table)
+-- 		for i, v in pairs(temp) do
+-- 			orig[i] = v;
+-- 		end
+		return temp; -- for speed we choose to do a return instead
+	end
+end
+
+function mod:ProcessMove()
+	addon:Debug("ProcessMove");
+	
+	if #combinedMoves == 0 then
+		print("Nothing to move.");
+		
+		self:Abort();
+		
+		return;
+	end
+	
+	self:RegisterEvent("UI_ERROR_MESSAGE");
+	
 	-- 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
+	-- add source and target to lists, if either is already in this list, skip the move
+	-- repeat every few seconds until we're completely done
 	
 	local sourceLocationsLocked = {};
 	local targetLocationsLocked = {};
 	
+	-- Reverse table, we need to go through it from last to first because we'll be removing elements, but we don't want the actions to be executed in a different order
+    combinedMoves = table.reverse(combinedMoves);
+	
 	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 
+		if move and (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));
 			
+			addon:Debug(("Moving %dx%s from (%d,%d) to (%d,%d)"):format(move.num, IdToItemLink(move.itemId), move.sourceContainer, move.sourceSlot, move.targetContainer, move.targetSlot));
+			
+			if GetContainerItemID(move.sourceContainer, move.sourceSlot) ~= move.itemId then
+				self:Abort("source changed", "Source (" .. move.sourceContainer .. "," .. move.sourceSlot .. ") is not " .. IdToItemLink(move.itemId));
+				
+				return;
+			end
+			
 			-- Pickup stack
-			SplitGuildBankItem(move.sourceContainer, move.sourceSlot, move.num);
+			if movesSource == addon.Locations.Bank then
+				SplitContainerItem(move.sourceContainer, move.sourceSlot, move.num);
+			elseif movesSource == addon.Locations.Guild then
+				SplitGuildBankItem(move.sourceContainer, move.sourceSlot, move.num);
+			end
 			
 			-- Remember we picked this item up and thus it is now locked
 			if not sourceLocationsLocked[move.sourceContainer] then
@@ -239,214 +295,75 @@
 			end
 			sourceLocationsLocked[move.sourceContainer][move.sourceSlot] = true;
 			
-			if CursorHasItem() then
+			if movesSource == addon.Locations.Guild or CursorHasItem() then -- CursorHasItem is always false if source is a guild tab
+				if GetContainerItemID(move.targetContainer, move.targetSlot) and GetContainerItemID(move.targetContainer, move.targetSlot) ~= move.itemId then
+					self:Abort("target changed", "Target (" .. move.targetContainer .. "," .. move.targetSlot .. ") is not " .. IdToItemLink(move.itemId) .. " nor empty");
+					return;
+				end
+				
 				-- 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] = {};
+				if not targetLocationsLocked[move.targetContainer] then
+					targetLocationsLocked[move.targetContainer] = {};
 				end
-				sourceLocationsLocked[move.targetContainer][move.targetSlot] = true;
+				targetLocationsLocked[move.targetContainer][move.targetSlot] = true;
 				
 				-- This move was processed
 				table.remove(combinedMoves, numCurrentMove);
+			else
+				self:Abort("item disappeared from mouse", "Couldn't move " .. IdToItemLink(move.itemId) .. ", CursorHasItem() is false");
+				return;
 			end
 		end
 		
 		-- Proceed with the next element (or previous considering we're going from last to first)
 		numCurrentMove = (numCurrentMove - 1);
 	end
+	
+	if #combinedMoves == 0 then
+		print("Finished.");
+		
+		self:Abort();
+		
+		return;
+	end
 end
 
 local tmrProcessNext;
-function BAG_UPDATE()
-	mod:CancelTimer(tmrProcessNext, true); -- silent
-	tmrProcessNext = mod:ScheduleTimer(function()
-		ProcessMove();
-	end, 2);
+function mod:BAG_UPDATE()
+	self:CancelTimer(tmrProcessNext, true); -- silent
+	tmrProcessNext = self:ScheduleTimer("ProcessMove", 1);
 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
+function IdToItemLink(itemId)
+	local itemLink = select(2, GetItemInfo(itemId));
+	itemLink = itemLink or "Unknown (" .. itemId .. ")";
+	return itemLink;
+end
+
+function mod:UI_ERROR_MESSAGE(e, errorMessage)
+	if errorMessage == ERR_SPLIT_FAILED then
+		self:Abort("splitting failed", "Splitting failed.");
 	end
 end
 
-function MoveGuildItem(move)
-	local tabId = GetCurrentGuildBankTab();
-	local slotId = (MAX_GUILDBANK_SLOTS_PER_TAB or 98); -- start by scanning the last slot
+function mod:Abort(simple, debugMsg)
+	if debugMsg then
+		addon:Debug("Aborting:" .. debugMsg);
+	end
+	if simple then
+		print("|cffff0000Aborting: " .. simple .. ".|r");
+	end
+	table.wipe(combinedMoves);
+	movesSource = nil;
 	
-	if tabId == nil or tabId < 1 then return; end
+	-- Stop timer
+	self:UnregisterEvent("BAG_UPDATE");
+	self:CancelTimer(tmrProcessNext, true); -- silent
 	
-	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));
+	self:UnregisterEvent("UI_ERROR_MESSAGE");
 end
 
 function mod:OnEnable()
@@ -455,4 +372,6 @@
 
 function mod:OnDisable()
 	Scanner = nil;
+	
+	self:Abort();
 end