| 
Zerotorescue@80
 | 
     1 local addon = select(2, ...);
 | 
| 
Zerotorescue@80
 | 
     2 local mod = addon:NewModule("Mover", "AceEvent-3.0", "AceTimer-3.0");
 | 
| 
Zerotorescue@80
 | 
     3 
 | 
| 
Zerotorescue@80
 | 
     4 local Scanner;
 | 
| 
Zerotorescue@80
 | 
     5 local queuedMoves = {}; -- table storing all queued moves before BeginMove is called
 | 
| 
Zerotorescue@80
 | 
     6 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 #)
 | 
| 
Zerotorescue@81
 | 
     7 local movesSource;
 | 
| 
Zerotorescue@80
 | 
     8 
 | 
| 
Zerotorescue@110
 | 
     9 addon.Locations = {
 | 
| 
Zerotorescue@110
 | 
    10 	Bag = 0,
 | 
| 
Zerotorescue@110
 | 
    11 	Bank = 1,
 | 
| 
Zerotorescue@110
 | 
    12 	Guild = 2,
 | 
| 
Zerotorescue@110
 | 
    13 	Mailbox = 3,
 | 
| 
Zerotorescue@110
 | 
    14 	Merchant = 4,
 | 
| 
Zerotorescue@110
 | 
    15 };
 | 
| 
Zerotorescue@110
 | 
    16 
 | 
| 
Zerotorescue@110
 | 
    17 local ContainerFunctions = {
 | 
| 
Zerotorescue@110
 | 
    18 	[addon.Locations.Bag] = {
 | 
| 
Zerotorescue@110
 | 
    19 		GetItemId = GetContainerItemID,
 | 
| 
Zerotorescue@110
 | 
    20 		PickupItem = SplitContainerItem,
 | 
| 
Zerotorescue@110
 | 
    21 		IsLocked = function(sourceContainer, sourceSlot)
 | 
| 
Zerotorescue@110
 | 
    22 			return select(3, GetContainerItemInfo(sourceContainer, sourceSlot));
 | 
| 
Zerotorescue@110
 | 
    23 		end,
 | 
| 
Zerotorescue@110
 | 
    24 		Event = "ITEM_LOCK_CHANGED",
 | 
| 
Zerotorescue@110
 | 
    25 	},
 | 
| 
Zerotorescue@110
 | 
    26 	[addon.Locations.Bank] = {
 | 
| 
Zerotorescue@110
 | 
    27 		GetItemId = GetContainerItemID,
 | 
| 
Zerotorescue@110
 | 
    28 		PickupItem = SplitContainerItem,
 | 
| 
Zerotorescue@110
 | 
    29 		IsLocked = function(sourceContainer, sourceSlot)
 | 
| 
Zerotorescue@110
 | 
    30 			return select(3, GetContainerItemInfo(sourceContainer, sourceSlot));
 | 
| 
Zerotorescue@110
 | 
    31 		end,
 | 
| 
Zerotorescue@110
 | 
    32 		Event = "ITEM_LOCK_CHANGED",
 | 
| 
Zerotorescue@110
 | 
    33 	},
 | 
| 
Zerotorescue@110
 | 
    34 	[addon.Locations.Guild] = {
 | 
| 
Zerotorescue@110
 | 
    35 		GetItemId = function(tabId, slotId)
 | 
| 
Zerotorescue@110
 | 
    36 			return addon:GetItemId(GetGuildBankItemLink(tabId, slotId));
 | 
| 
Zerotorescue@110
 | 
    37 		end,
 | 
| 
Zerotorescue@110
 | 
    38 		PickupItem = SplitGuildBankItem,
 | 
| 
Zerotorescue@110
 | 
    39 		IsLocked = function(sourceContainer, sourceSlot)
 | 
| 
Zerotorescue@110
 | 
    40 			return select(3, GetGuildBankItemInfo(sourceContainer, sourceSlot));
 | 
| 
Zerotorescue@110
 | 
    41 		end,
 | 
| 
Zerotorescue@110
 | 
    42 		Event = "ITEM_LOCK_CHANGED",
 | 
| 
Zerotorescue@110
 | 
    43 	},
 | 
| 
Zerotorescue@110
 | 
    44 	[addon.Locations.Mailbox] = {
 | 
| 
Zerotorescue@110
 | 
    45 		GetItemId = function(mailIndex, attachmentId)
 | 
| 
Zerotorescue@110
 | 
    46 			return addon:GetItemId(GetInboxItemLink(mailIndex, attachmentId));
 | 
| 
Zerotorescue@110
 | 
    47 		end,
 | 
| 
Zerotorescue@110
 | 
    48 		PickupItem = TakeInboxItem,
 | 
| 
Zerotorescue@110
 | 
    49 		IsLocked = function() return false; end,
 | 
| 
Zerotorescue@110
 | 
    50 		DoNotDrop = true, -- TakeInboxItem does not support picking up
 | 
| 
Zerotorescue@110
 | 
    51 		Synchronous = true, -- wait after every single move
 | 
| 
Zerotorescue@110
 | 
    52 		Event = "BAG_UPDATE",
 | 
| 
Zerotorescue@110
 | 
    53 	},
 | 
| 
Zerotorescue@110
 | 
    54 	[addon.Locations.Merchant] = {
 | 
| 
Zerotorescue@110
 | 
    55 		GetItemId = function(_, merchantIndex)
 | 
| 
Zerotorescue@110
 | 
    56 			return addon:GetItemId(GetMerchantItemLink(merchantIndex));
 | 
| 
Zerotorescue@110
 | 
    57 		end,
 | 
| 
Zerotorescue@110
 | 
    58 		PickupItem = function(_, merchantIndex, num)
 | 
| 
Zerotorescue@110
 | 
    59 			-- The below behavior was changed in patch 4.0.1, it now acts as expected; quantity requested equals exact quantity bought, even with increased batchsize
 | 
| 
Zerotorescue@110
 | 
    60 			
 | 
| 
Zerotorescue@110
 | 
    61 			-- Some merchant items are sold in batches (e.g. of 5)
 | 
| 
Zerotorescue@110
 | 
    62 			-- In that case BuyMerchantItem wants the num stacks, rather than the quantity to be bought
 | 
| 
Zerotorescue@110
 | 
    63 			--local batchSize = select(4, GetMerchantItemInfo(merchantIndex));
 | 
| 
Zerotorescue@110
 | 
    64 			
 | 
| 
Zerotorescue@110
 | 
    65 			--local batches = math.ceil(num / batchSize);
 | 
| 
Zerotorescue@110
 | 
    66 			
 | 
| 
Zerotorescue@110
 | 
    67 			--BuyMerchantItem(merchantIndex, batches);
 | 
| 
Zerotorescue@110
 | 
    68 			
 | 
| 
Zerotorescue@110
 | 
    69 			return BuyMerchantItem(merchantIndex, num);
 | 
| 
Zerotorescue@110
 | 
    70 		end,
 | 
| 
Zerotorescue@110
 | 
    71 		IsLocked = function() return false; end,
 | 
| 
Zerotorescue@110
 | 
    72 		DoNotDrop = true, -- BuyMerchantItem does not support picking up
 | 
| 
Zerotorescue@110
 | 
    73 		Burst = true, -- spam buy items, the source can take it
 | 
| 
Zerotorescue@110
 | 
    74 		Event = "BAG_UPDATE",
 | 
| 
Zerotorescue@110
 | 
    75 	},
 | 
| 
Zerotorescue@110
 | 
    76 };
 | 
| 
Zerotorescue@110
 | 
    77 
 | 
| 
Zerotorescue@110
 | 
    78 function mod:AddMove(itemId, amount, numMissing, numAvailable, price)
 | 
| 
Zerotorescue@80
 | 
    79 	table.insert(queuedMoves, {
 | 
| 
Zerotorescue@110
 | 
    80 		["itemId"] = itemId,
 | 
| 
Zerotorescue@110
 | 
    81 		["num"] = amount, -- can not be unlimited
 | 
| 
Zerotorescue@110
 | 
    82 		["missing"] = numMissing,
 | 
| 
Zerotorescue@110
 | 
    83 		["available"] = numAvailable,
 | 
| 
Zerotorescue@110
 | 
    84 		["price"] = price,
 | 
| 
Zerotorescue@80
 | 
    85 	});
 | 
| 
Zerotorescue@80
 | 
    86 end
 | 
| 
Zerotorescue@80
 | 
    87 
 | 
| 
Zerotorescue@81
 | 
    88 function mod:HasMoves()
 | 
| 
Zerotorescue@81
 | 
    89 	return (#queuedMoves ~= 0);
 | 
| 
Zerotorescue@81
 | 
    90 end
 | 
| 
Zerotorescue@81
 | 
    91 
 | 
| 
Zerotorescue@101
 | 
    92 function mod:GetMoves()
 | 
| 
Zerotorescue@101
 | 
    93 	return queuedMoves;
 | 
| 
Zerotorescue@101
 | 
    94 end
 | 
| 
Zerotorescue@101
 | 
    95 
 | 
| 
Zerotorescue@101
 | 
    96 function mod:ResetQueue()
 | 
| 
Zerotorescue@101
 | 
    97 	table.wipe(queuedMoves);
 | 
| 
Zerotorescue@101
 | 
    98 end
 | 
| 
Zerotorescue@101
 | 
    99 
 | 
| 
Zerotorescue@84
 | 
   100 if not table.reverse then
 | 
| 
Zerotorescue@84
 | 
   101 	table.reverse = function(orig)
 | 
| 
Zerotorescue@84
 | 
   102 		local temp = {};
 | 
| 
Zerotorescue@84
 | 
   103 		local origLength = #orig;
 | 
| 
Zerotorescue@84
 | 
   104 		for i = 1, origLength do
 | 
| 
Zerotorescue@84
 | 
   105 			temp[(origLength - i + 1)] = orig[i];
 | 
| 
Zerotorescue@84
 | 
   106 		end
 | 
| 
Zerotorescue@84
 | 
   107 		
 | 
| 
Zerotorescue@84
 | 
   108 -- 		-- Update the original table (can't do orig = temp as that would change the reference-link instead of the original table)
 | 
| 
Zerotorescue@84
 | 
   109 -- 		for i, v in pairs(temp) do
 | 
| 
Zerotorescue@84
 | 
   110 -- 			orig[i] = v;
 | 
| 
Zerotorescue@84
 | 
   111 -- 		end
 | 
| 
Zerotorescue@84
 | 
   112 		return temp; -- for speed we choose to do a return instead
 | 
| 
Zerotorescue@84
 | 
   113 	end
 | 
| 
Zerotorescue@84
 | 
   114 end
 | 
| 
Zerotorescue@84
 | 
   115 
 | 
| 
Zerotorescue@110
 | 
   116 local function GetEmptySlots()
 | 
| 
Zerotorescue@110
 | 
   117 	local emptySlots = {};
 | 
| 
Zerotorescue@110
 | 
   118 	
 | 
| 
Zerotorescue@110
 | 
   119 	-- Go through all our bags, including the backpack
 | 
| 
Zerotorescue@110
 | 
   120 	for bagId = 0, NUM_BAG_SLOTS do
 | 
| 
Zerotorescue@110
 | 
   121 		-- Go through all our slots (0 = backpack)
 | 
| 
Zerotorescue@110
 | 
   122 		for slotId = 1, GetContainerNumSlots(bagId) do
 | 
| 
Zerotorescue@110
 | 
   123 			local itemId = GetContainerItemID(bagId, slotId); -- we're scanning our local bags here, so no need to get messy with guild bank support
 | 
| 
Zerotorescue@110
 | 
   124 			local bagFamily = select(2, GetContainerNumFreeSlots(bagId));
 | 
| 
Zerotorescue@110
 | 
   125 			
 | 
| 
Zerotorescue@110
 | 
   126 			if not itemId then
 | 
| 
Zerotorescue@110
 | 
   127 				table.insert(emptySlots, {
 | 
| 
Zerotorescue@110
 | 
   128 					["container"] = bagId,
 | 
| 
Zerotorescue@110
 | 
   129 					["slot"] = slotId,
 | 
| 
Zerotorescue@110
 | 
   130 					["family"] = bagFamily,
 | 
| 
Zerotorescue@110
 | 
   131 				});
 | 
| 
Zerotorescue@110
 | 
   132 			end
 | 
| 
Zerotorescue@110
 | 
   133 		end
 | 
| 
Zerotorescue@110
 | 
   134 	end
 | 
| 
Zerotorescue@110
 | 
   135 	
 | 
| 
Zerotorescue@110
 | 
   136 	return emptySlots;
 | 
| 
Zerotorescue@110
 | 
   137 end
 | 
| 
Zerotorescue@110
 | 
   138 
 | 
| 
Zerotorescue@110
 | 
   139 local function GetFirstEmptySlot(emptySlots, prefFamily)
 | 
| 
Zerotorescue@110
 | 
   140 	while prefFamily do
 | 
| 
Zerotorescue@110
 | 
   141 		for _, slot in pairs(emptySlots) do
 | 
| 
Zerotorescue@110
 | 
   142 			if slot.family == prefFamily then
 | 
| 
Zerotorescue@110
 | 
   143 				return slot;
 | 
| 
Zerotorescue@110
 | 
   144 			end
 | 
| 
Zerotorescue@110
 | 
   145 		end
 | 
| 
Zerotorescue@110
 | 
   146 		
 | 
| 
Zerotorescue@110
 | 
   147 		-- If this was a special family, no special bag available, now check normal bags
 | 
| 
Zerotorescue@110
 | 
   148 		if prefFamily > 0 then
 | 
| 
Zerotorescue@110
 | 
   149 			prefFamily = 0;
 | 
| 
Zerotorescue@110
 | 
   150 		else
 | 
| 
Zerotorescue@110
 | 
   151 			prefFamily = nil;
 | 
| 
Zerotorescue@110
 | 
   152 		end
 | 
| 
Zerotorescue@110
 | 
   153 	end
 | 
| 
Zerotorescue@110
 | 
   154 end
 | 
| 
Zerotorescue@110
 | 
   155 
 | 
| 
Zerotorescue@80
 | 
   156 function mod:BeginMove(location, onFinish)
 | 
| 
Zerotorescue@81
 | 
   157 	addon:Debug("BeginMove");
 | 
| 
Zerotorescue@80
 | 
   158 	
 | 
| 
Zerotorescue@80
 | 
   159 	-- Find the outgoing moves
 | 
| 
Zerotorescue@80
 | 
   160 	-- 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
 | 
| 
Zerotorescue@80
 | 
   161 	
 | 
| 
Zerotorescue@80
 | 
   162 	-- Get a list of items in the source container
 | 
| 
Zerotorescue@80
 | 
   163 	local sourceContents = Scanner:CacheLocation(location, false);
 | 
| 
Zerotorescue@80
 | 
   164 	
 | 
| 
Zerotorescue@80
 | 
   165 	local outgoingMoves = {};
 | 
| 
Zerotorescue@80
 | 
   166 
 | 
| 
Zerotorescue@89
 | 
   167 	addon:Debug("%d moves were queued.", #queuedMoves);
 | 
| 
Zerotorescue@82
 | 
   168 	
 | 
| 
Zerotorescue@81
 | 
   169 	for _, singleMove in pairs(queuedMoves) do
 | 
| 
Zerotorescue@110
 | 
   170 		local sourceItem = sourceContents[singleMove.itemId];
 | 
| 
Zerotorescue@80
 | 
   171 		if not sourceItem then
 | 
| 
Zerotorescue@110
 | 
   172 			addon:Print(("Can't move %s, this doesn't exist in the source."):format(IdToItemLink(singleMove.itemId)), addon.Colors.Red);
 | 
| 
Zerotorescue@80
 | 
   173 		else
 | 
| 
Zerotorescue@82
 | 
   174 			-- We want to move the smallest stacks first to keep stuff pretty (and minimize space usage, splitting a stack takes 2 slots, moving something only 1)
 | 
| 
Zerotorescue@80
 | 
   175 			table.sort(sourceItem.locations, function(a, b)
 | 
| 
Zerotorescue@110
 | 
   176 				-- -1 indicates unlimited, this is always more than an actual amount
 | 
| 
Zerotorescue@110
 | 
   177 				if a.count == -1 then
 | 
| 
Zerotorescue@110
 | 
   178 					return false;
 | 
| 
Zerotorescue@110
 | 
   179 				elseif b.count == -1 then
 | 
| 
Zerotorescue@110
 | 
   180 					return true;
 | 
| 
Zerotorescue@110
 | 
   181 				end
 | 
| 
Zerotorescue@110
 | 
   182 				
 | 
| 
Zerotorescue@81
 | 
   183 				return a.count < b.count;
 | 
| 
Zerotorescue@80
 | 
   184 			end);
 | 
| 
Zerotorescue@80
 | 
   185 			
 | 
| 
Zerotorescue@81
 | 
   186 			for _, itemLocation in pairs(sourceItem.locations) do
 | 
| 
Zerotorescue@80
 | 
   187 				-- if this location has more items than we need, only move what we need, otherwise move everything in this stack
 | 
| 
Zerotorescue@110
 | 
   188 				local movingNum = (((itemLocation.count == -1 or itemLocation.count > singleMove.num) and singleMove.num) or itemLocation.count);
 | 
| 
Zerotorescue@110
 | 
   189 				
 | 
| 
Zerotorescue@110
 | 
   190 				if itemLocation.count == -1 then
 | 
| 
Zerotorescue@110
 | 
   191 					-- If the source has an unlimited quantity, makes moves based on the max stacksize
 | 
| 
Zerotorescue@110
 | 
   192 					
 | 
| 
Zerotorescue@110
 | 
   193 					local stackSize = select(8, GetItemInfo(singleMove.itemId)); -- 8 = stacksize
 | 
| 
Zerotorescue@110
 | 
   194 					
 | 
| 
Zerotorescue@110
 | 
   195 					if stackSize then
 | 
| 
Zerotorescue@110
 | 
   196 						while movingNum > stackSize do
 | 
| 
Zerotorescue@110
 | 
   197 							-- Move a single stack size while the amount remaining to be moved is above the stack size num
 | 
| 
Zerotorescue@110
 | 
   198 							
 | 
| 
Zerotorescue@110
 | 
   199 							table.insert(outgoingMoves, {
 | 
| 
Zerotorescue@110
 | 
   200 								["itemId"] = singleMove.itemId,
 | 
| 
Zerotorescue@110
 | 
   201 								["num"] = stackSize,
 | 
| 
Zerotorescue@110
 | 
   202 								["container"] = itemLocation.container,
 | 
| 
Zerotorescue@110
 | 
   203 								["slot"] = itemLocation.slot,
 | 
| 
Zerotorescue@110
 | 
   204 							});
 | 
| 
Zerotorescue@110
 | 
   205 							
 | 
| 
Zerotorescue@110
 | 
   206 							movingNum = (movingNum - stackSize);
 | 
| 
Zerotorescue@110
 | 
   207 						end
 | 
| 
Zerotorescue@110
 | 
   208 					end
 | 
| 
Zerotorescue@110
 | 
   209 				end
 | 
| 
Zerotorescue@80
 | 
   210 				
 | 
| 
Zerotorescue@80
 | 
   211 				table.insert(outgoingMoves, {
 | 
| 
Zerotorescue@110
 | 
   212 					["itemId"] = singleMove.itemId,
 | 
| 
Zerotorescue@110
 | 
   213 					["num"] = movingNum,
 | 
| 
Zerotorescue@110
 | 
   214 					["container"] = itemLocation.container,
 | 
| 
Zerotorescue@110
 | 
   215 					["slot"] = itemLocation.slot,
 | 
| 
Zerotorescue@80
 | 
   216 				});
 | 
| 
Zerotorescue@80
 | 
   217 				
 | 
| 
Zerotorescue@80
 | 
   218 				singleMove.num = (singleMove.num - movingNum);
 | 
| 
Zerotorescue@80
 | 
   219 				
 | 
| 
Zerotorescue@80
 | 
   220 				if singleMove.num == 0 then
 | 
| 
Zerotorescue@80
 | 
   221 					-- If we have prepared everything we wanted, go to the next queued move
 | 
| 
Zerotorescue@81
 | 
   222 					break; -- stop the locations-loop
 | 
| 
Zerotorescue@80
 | 
   223 				end
 | 
| 
Zerotorescue@80
 | 
   224 			end
 | 
| 
Zerotorescue@80
 | 
   225 		end
 | 
| 
Zerotorescue@80
 | 
   226 	end
 | 
| 
Zerotorescue@82
 | 
   227 
 | 
| 
Zerotorescue@89
 | 
   228 	addon:Debug("%d outgoing moves are possible.", #outgoingMoves);
 | 
| 
Zerotorescue@80
 | 
   229 	
 | 
| 
Zerotorescue@80
 | 
   230 	-- No longer needed
 | 
| 
Zerotorescue@80
 | 
   231 	table.wipe(queuedMoves);
 | 
| 
Zerotorescue@80
 | 
   232 	
 | 
| 
Zerotorescue@110
 | 
   233 	
 | 
| 
Zerotorescue@110
 | 
   234 	
 | 
| 
Zerotorescue@80
 | 
   235 	-- Process every single outgoing move and find fitting targets
 | 
| 
Zerotorescue@80
 | 
   236 	
 | 
| 
Zerotorescue@80
 | 
   237 	-- Get a list of items already in the target container
 | 
| 
Zerotorescue@80
 | 
   238 	local targetContents = Scanner:CacheLocation(addon.Locations.Bag, false);
 | 
| 
Zerotorescue@80
 | 
   239 	
 | 
| 
Zerotorescue@80
 | 
   240 	-- Find all empty slots
 | 
| 
Zerotorescue@110
 | 
   241 	local emptySlots = GetEmptySlots();
 | 
| 
Zerotorescue@82
 | 
   242 
 | 
| 
Zerotorescue@89
 | 
   243 	addon:Debug("%d empty slots are available.", #emptySlots);
 | 
| 
Zerotorescue@80
 | 
   244 	
 | 
| 
Zerotorescue@81
 | 
   245 	-- Remember where we're moving from
 | 
| 
Zerotorescue@81
 | 
   246 	movesSource = location;
 | 
| 
Zerotorescue@81
 | 
   247 	
 | 
| 
Zerotorescue@110
 | 
   248 	local backup = 0; -- the below loop should never break, but if it does for any reason, this is here to stop it from freezing the game
 | 
| 
Zerotorescue@84
 | 
   249 	
 | 
| 
Zerotorescue@80
 | 
   250 	while #outgoingMoves ~= 0 do
 | 
| 
Zerotorescue@110
 | 
   251 		-- Repeat the below loop until nothing is remaining
 | 
| 
Zerotorescue@80
 | 
   252 		
 | 
| 
Zerotorescue@81
 | 
   253 		for _, outgoingMove in pairs(outgoingMoves) do
 | 
| 
Zerotorescue@110
 | 
   254 			if outgoingMove.itemId then -- itemId  will be set to nil when this outgoing move was processed - sanity check
 | 
| 
Zerotorescue@80
 | 
   255 				local targetItem = targetContents[outgoingMove.itemId];
 | 
| 
Zerotorescue@80
 | 
   256 				
 | 
| 
Zerotorescue@110
 | 
   257 				if not targetItem or ContainerFunctions[location].DoNotDrop then
 | 
| 
Zerotorescue@110
 | 
   258 					-- There is no partial stack which can be filled or this source container doesn't allow manual allocation of items (in which case we always assume it takes a full empty slot)
 | 
| 
Zerotorescue@80
 | 
   259 					
 | 
| 
Zerotorescue@110
 | 
   260 					local family = GetItemFamily(outgoingMove.itemId);
 | 
| 
Zerotorescue@110
 | 
   261 					if family and family ~= 0 and select(9, GetItemInfo(outgoingMove.itemId)) == "INVTYPE_BAG" then
 | 
| 
Zerotorescue@110
 | 
   262 						-- Containers can only fit in general slots but GetItemFamily will return what they can contain themselves
 | 
| 
Zerotorescue@110
 | 
   263 						family = 0;
 | 
| 
Zerotorescue@110
 | 
   264 					end
 | 
| 
Zerotorescue@110
 | 
   265 					local firstAvailableSlot = GetFirstEmptySlot(emptySlots, family);
 | 
| 
Zerotorescue@80
 | 
   266 					
 | 
| 
Zerotorescue@80
 | 
   267 					if not firstAvailableSlot then
 | 
| 
Zerotorescue@110
 | 
   268 						-- No empty slot available - bags are full
 | 
| 
Zerotorescue@110
 | 
   269 						
 | 
| 
Zerotorescue@98
 | 
   270 						addon:Print(("Bags are full. Skipping %s."):format(IdToItemLink(outgoingMove.itemId)), addon.Colors.Orange);
 | 
| 
Zerotorescue@80
 | 
   271 						
 | 
| 
Zerotorescue@82
 | 
   272 						outgoingMove.itemId = nil; -- remove this record from the outgoingMoves-table
 | 
| 
Zerotorescue@109
 | 
   273 						
 | 
| 
Zerotorescue@109
 | 
   274 						-- Not a single item with this item id can be moved, since we only want the bags are full announcement once, we remove all other moves with this item id
 | 
| 
Zerotorescue@109
 | 
   275 						for _, otherMove in pairs(outgoingMoves) do
 | 
| 
Zerotorescue@109
 | 
   276 							if otherMove.itemId and otherMove.itemId == outgoingMove.itemId then
 | 
| 
Zerotorescue@109
 | 
   277 								otherMove.itemId = nil;
 | 
| 
Zerotorescue@109
 | 
   278 							end
 | 
| 
Zerotorescue@109
 | 
   279 						end
 | 
| 
Zerotorescue@80
 | 
   280 					else
 | 
| 
Zerotorescue@110
 | 
   281 						-- Consume empty slot
 | 
| 
Zerotorescue@110
 | 
   282 						
 | 
| 
Zerotorescue@80
 | 
   283 						table.insert(combinedMoves, {
 | 
| 
Zerotorescue@110
 | 
   284 							["itemId"] = outgoingMove.itemId,
 | 
| 
Zerotorescue@110
 | 
   285 							["num"] = outgoingMove.num,
 | 
| 
Zerotorescue@110
 | 
   286 							["sourceContainer"] = outgoingMove.container,
 | 
| 
Zerotorescue@110
 | 
   287 							["sourceSlot"] = outgoingMove.slot,
 | 
| 
Zerotorescue@110
 | 
   288 							["targetContainer"] = firstAvailableSlot.container,
 | 
| 
Zerotorescue@110
 | 
   289 							["targetSlot"] = firstAvailableSlot.slot,
 | 
| 
Zerotorescue@80
 | 
   290 						});
 | 
| 
Zerotorescue@80
 | 
   291 						
 | 
| 
Zerotorescue@80
 | 
   292 						-- We filled an empty slot so the target contents now has one more item,
 | 
| 
Zerotorescue@80
 | 
   293 						-- make a new instance of the ItemMove class so any additional items with this id can be stacked on top of it
 | 
| 
Zerotorescue@81
 | 
   294 						local itemMove = addon.ContainerItem:New();
 | 
| 
Zerotorescue@82
 | 
   295 						itemMove:AddLocation(firstAvailableSlot.container, firstAvailableSlot.slot, outgoingMove.num);
 | 
| 
Zerotorescue@80
 | 
   296 						targetContents[outgoingMove.itemId] = itemMove;
 | 
| 
Zerotorescue@80
 | 
   297 						
 | 
| 
Zerotorescue@81
 | 
   298 						table.remove(emptySlots, 1); -- no longer empty
 | 
| 
Zerotorescue@80
 | 
   299 						
 | 
| 
Zerotorescue@82
 | 
   300 						outgoingMove.num = 0; -- nothing remaining - sanity check
 | 
| 
Zerotorescue@80
 | 
   301 						outgoingMove.itemId = nil; -- remove this record from the outgoingMoves-table
 | 
| 
Zerotorescue@80
 | 
   302 					end
 | 
| 
Zerotorescue@80
 | 
   303 				else
 | 
| 
Zerotorescue@80
 | 
   304 					-- Find the maximum stack size for this item
 | 
| 
Zerotorescue@80
 | 
   305 					local itemStackCount = select(8, GetItemInfo(outgoingMove.itemId));
 | 
| 
Zerotorescue@80
 | 
   306 					
 | 
| 
Zerotorescue@80
 | 
   307 					-- We want to move to the largest stacks first to keep stuff pretty
 | 
| 
Zerotorescue@80
 | 
   308 					table.sort(targetItem.locations, function(a, b)
 | 
| 
Zerotorescue@81
 | 
   309 						return a.count > b.count;
 | 
| 
Zerotorescue@80
 | 
   310 					end);
 | 
| 
Zerotorescue@80
 | 
   311 					
 | 
| 
Zerotorescue@81
 | 
   312 					for _, itemLocation in pairs(targetItem.locations) do
 | 
| 
Zerotorescue@82
 | 
   313 						if itemLocation.count < itemStackCount and outgoingMove.num > 0 then
 | 
| 
Zerotorescue@80
 | 
   314 							-- Check if this stack isn't already full (and we still need to move this item)
 | 
| 
Zerotorescue@80
 | 
   315 							
 | 
| 
Zerotorescue@80
 | 
   316 							local remainingSpace = (itemStackCount - itemLocation.count);
 | 
| 
Zerotorescue@84
 | 
   317 							if remainingSpace >= outgoingMove.num then
 | 
| 
Zerotorescue@80
 | 
   318 								-- Enough room to move this entire stack
 | 
| 
Zerotorescue@80
 | 
   319 								-- Deposit this item and then forget this outgoing move as everything in it was processed
 | 
| 
Zerotorescue@80
 | 
   320 								
 | 
| 
Zerotorescue@80
 | 
   321 								table.insert(combinedMoves, {
 | 
| 
Zerotorescue@110
 | 
   322 									["itemId"] = outgoingMove.itemId,
 | 
| 
Zerotorescue@110
 | 
   323 									["num"] = outgoingMove.num,
 | 
| 
Zerotorescue@110
 | 
   324 									["sourceContainer"] = outgoingMove.container,
 | 
| 
Zerotorescue@110
 | 
   325 									["sourceSlot"] = outgoingMove.slot,
 | 
| 
Zerotorescue@110
 | 
   326 									["targetContainer"] = itemLocation.container,
 | 
| 
Zerotorescue@110
 | 
   327 									["targetSlot"] = itemLocation.slot,
 | 
| 
Zerotorescue@80
 | 
   328 								});
 | 
| 
Zerotorescue@80
 | 
   329 								
 | 
| 
Zerotorescue@82
 | 
   330 								itemLocation.count = (itemLocation.count + outgoingMove.num);
 | 
| 
Zerotorescue@84
 | 
   331 								outgoingMove.num = 0; -- nothing remaining
 | 
| 
Zerotorescue@80
 | 
   332 								outgoingMove.itemId = nil; -- remove this record from the outgoingMoves-table
 | 
| 
Zerotorescue@80
 | 
   333 								break; -- stop the locations-loop
 | 
| 
Zerotorescue@80
 | 
   334 							else
 | 
| 
Zerotorescue@80
 | 
   335 								-- Deposit this item but don't remove the outgoing move as there are some items left to move
 | 
| 
Zerotorescue@80
 | 
   336 								
 | 
| 
Zerotorescue@80
 | 
   337 								table.insert(combinedMoves, {
 | 
| 
Zerotorescue@110
 | 
   338 									["itemId"] = outgoingMove.itemId,
 | 
| 
Zerotorescue@110
 | 
   339 									["num"] = outgoingMove.num,
 | 
| 
Zerotorescue@110
 | 
   340 									["sourceContainer"] = outgoingMove.container,
 | 
| 
Zerotorescue@110
 | 
   341 									["sourceSlot"] = outgoingMove.slot,
 | 
| 
Zerotorescue@110
 | 
   342 									["targetContainer"] = itemLocation.container,
 | 
| 
Zerotorescue@110
 | 
   343 									["targetSlot"] = itemLocation.slot,
 | 
| 
Zerotorescue@80
 | 
   344 								});
 | 
| 
Zerotorescue@80
 | 
   345 								
 | 
| 
Zerotorescue@80
 | 
   346 								-- The target will be full when we complete, but the source will still have remaining items left to be moved
 | 
| 
Zerotorescue@80
 | 
   347 								itemLocation.count = itemStackCount;
 | 
| 
Zerotorescue@82
 | 
   348 								outgoingMove.num = (outgoingMove.num - remainingSpace);
 | 
| 
Zerotorescue@80
 | 
   349 							end
 | 
| 
Zerotorescue@80
 | 
   350 						end
 | 
| 
Zerotorescue@80
 | 
   351 					end
 | 
| 
Zerotorescue@80
 | 
   352 					
 | 
| 
Zerotorescue@82
 | 
   353 					if outgoingMove.num > 0 then
 | 
| 
Zerotorescue@80
 | 
   354 						-- We went through all matching items and checked their stack sizes if we could move this there, no room available
 | 
| 
Zerotorescue@80
 | 
   355 						-- 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
 | 
| 
Zerotorescue@84
 | 
   356 						targetContents[outgoingMove.itemId] = nil;
 | 
| 
Zerotorescue@80
 | 
   357 					end
 | 
| 
Zerotorescue@80
 | 
   358 				end
 | 
| 
Zerotorescue@80
 | 
   359 			end
 | 
| 
Zerotorescue@80
 | 
   360 		end
 | 
| 
Zerotorescue@80
 | 
   361 		
 | 
| 
Zerotorescue@80
 | 
   362 		-- Loop through the array to find items that should be removed, start with the last element or the loop would break
 | 
| 
Zerotorescue@80
 | 
   363 		local numOutgoingMoves = #outgoingMoves; -- since LUA-tables start at an index of 1, this is actually an existing index (outgoingMoves[#outgoingMoves] would return a value)
 | 
| 
Zerotorescue@80
 | 
   364 		while numOutgoingMoves ~= 0 do
 | 
| 
Zerotorescue@80
 | 
   365 			-- A not equal-comparison should be quicker than a larger/smaller than-comparison
 | 
| 
Zerotorescue@80
 | 
   366 			
 | 
| 
Zerotorescue@80
 | 
   367 			-- Check if the item id is nil, this is set to nil when this outgoing move has been processed
 | 
| 
Zerotorescue@84
 | 
   368 			if not outgoingMoves[numOutgoingMoves].itemId or outgoingMoves[numOutgoingMoves].num == 0 then
 | 
| 
Zerotorescue@80
 | 
   369 				-- Remove this element from the array
 | 
| 
Zerotorescue@80
 | 
   370 				table.remove(outgoingMoves, numOutgoingMoves);
 | 
| 
Zerotorescue@80
 | 
   371 			end
 | 
| 
Zerotorescue@80
 | 
   372 			
 | 
| 
Zerotorescue@80
 | 
   373 			-- Proceed with the next element (or previous considering we're going from last to first)
 | 
| 
Zerotorescue@80
 | 
   374 			numOutgoingMoves = (numOutgoingMoves - 1);
 | 
| 
Zerotorescue@80
 | 
   375 		end
 | 
| 
Zerotorescue@84
 | 
   376 
 | 
| 
Zerotorescue@89
 | 
   377 		addon:Debug("%d moves remaining.", #outgoingMoves);
 | 
| 
Zerotorescue@84
 | 
   378 		
 | 
| 
Zerotorescue@84
 | 
   379 		backup = (backup + 1);
 | 
| 
Zerotorescue@84
 | 
   380 		if backup > 1000 then
 | 
| 
Zerotorescue@84
 | 
   381 			table.wipe(outgoingMoves);
 | 
| 
Zerotorescue@84
 | 
   382 			self:Abort("mover crashed", "Error preparing moves, hit an endless loop");
 | 
| 
Zerotorescue@84
 | 
   383 			onFinish();
 | 
| 
Zerotorescue@84
 | 
   384 			return;
 | 
| 
Zerotorescue@84
 | 
   385 		end
 | 
| 
Zerotorescue@80
 | 
   386 	end
 | 
| 
Zerotorescue@84
 | 
   387 	
 | 
| 
Zerotorescue@84
 | 
   388 	-- 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
 | 
| 
Zerotorescue@84
 | 
   389 	combinedMoves = table.reverse(combinedMoves);
 | 
| 
Zerotorescue@82
 | 
   390 
 | 
| 
Zerotorescue@89
 | 
   391 	addon:Debug("%d moves should be possible.", #combinedMoves);
 | 
| 
Zerotorescue@80
 | 
   392 	
 | 
| 
Zerotorescue@80
 | 
   393 	-- No longer needed
 | 
| 
Zerotorescue@80
 | 
   394 	table.wipe(emptySlots);
 | 
| 
Zerotorescue@80
 | 
   395 	
 | 
| 
Zerotorescue@81
 | 
   396 	self:ProcessMove();
 | 
| 
Zerotorescue@80
 | 
   397 	
 | 
| 
Zerotorescue@82
 | 
   398 	-- Even though we aren't completely done yet, allow requeueing
 | 
| 
Zerotorescue@81
 | 
   399 	onFinish();
 | 
| 
Zerotorescue@80
 | 
   400 end
 | 
| 
Zerotorescue@80
 | 
   401 
 | 
| 
Zerotorescue@110
 | 
   402 local tmrRetry;
 | 
| 
Zerotorescue@81
 | 
   403 function mod:ProcessMove()
 | 
| 
Zerotorescue@81
 | 
   404 	addon:Debug("ProcessMove");
 | 
| 
Zerotorescue@81
 | 
   405 	
 | 
| 
Zerotorescue@81
 | 
   406 	if #combinedMoves == 0 then
 | 
| 
Zerotorescue@98
 | 
   407 		addon:Print("Nothing to move.");
 | 
| 
Zerotorescue@81
 | 
   408 		
 | 
| 
Zerotorescue@81
 | 
   409 		self:Abort();
 | 
| 
Zerotorescue@81
 | 
   410 		
 | 
| 
Zerotorescue@81
 | 
   411 		return;
 | 
| 
Zerotorescue@110
 | 
   412 	elseif movesSource == addon.Locations.Mailbox and MailAddonBusy and MailAddonBusy ~= addon:GetName() then
 | 
| 
Zerotorescue@110
 | 
   413 		addon:Debug("Anoter addon (%s) is busy with the mailbox.", MailAddonBusy);
 | 
| 
Zerotorescue@110
 | 
   414 		
 | 
| 
Zerotorescue@110
 | 
   415 		self:CancelTimer(tmrRetry, true); -- silent
 | 
| 
Zerotorescue@110
 | 
   416 		tmrRetry = self:ScheduleTimer("ProcessMove", .5);
 | 
| 
Zerotorescue@110
 | 
   417 		
 | 
| 
Zerotorescue@110
 | 
   418 		return;
 | 
| 
Zerotorescue@81
 | 
   419 	end
 | 
| 
Zerotorescue@81
 | 
   420 	
 | 
| 
Zerotorescue@98
 | 
   421 	-- Make sure nothing is at the mouse
 | 
| 
Zerotorescue@98
 | 
   422 	ClearCursor();
 | 
| 
Zerotorescue@98
 | 
   423 	
 | 
| 
Zerotorescue@110
 | 
   424 	if movesSource == addon.Locations.Mailbox then
 | 
| 
Zerotorescue@110
 | 
   425 		MailAddonBusy = addon:GetName();
 | 
| 
Zerotorescue@110
 | 
   426 	end
 | 
| 
Zerotorescue@110
 | 
   427 	
 | 
| 
Zerotorescue@110
 | 
   428 	self:RegisterEvent(ContainerFunctions[movesSource].Event, "SourceUpdated");
 | 
| 
Zerotorescue@81
 | 
   429 	self:RegisterEvent("UI_ERROR_MESSAGE");
 | 
| 
Zerotorescue@81
 | 
   430 	
 | 
| 
Zerotorescue@80
 | 
   431 	-- combinedMoves now has all moves in it (source -> target)
 | 
| 
Zerotorescue@80
 | 
   432 	-- go through list, move everything inside it
 | 
| 
Zerotorescue@81
 | 
   433 	-- add source and target to lists, if either is already in this list, skip the move
 | 
| 
Zerotorescue@81
 | 
   434 	-- repeat every few seconds until we're completely done
 | 
| 
Zerotorescue@80
 | 
   435 	
 | 
| 
Zerotorescue@80
 | 
   436 	local sourceLocationsLocked = {};
 | 
| 
Zerotorescue@80
 | 
   437 	local targetLocationsLocked = {};
 | 
| 
Zerotorescue@80
 | 
   438 	
 | 
| 
Zerotorescue@110
 | 
   439 	local hasMoved;
 | 
| 
Zerotorescue@110
 | 
   440 	
 | 
| 
Zerotorescue@82
 | 
   441 	local combinedMovesOriginalLength = #combinedMoves;
 | 
| 
Zerotorescue@82
 | 
   442 	local numCurrentMove = combinedMovesOriginalLength;
 | 
| 
Zerotorescue@80
 | 
   443 	while numCurrentMove ~= 0 do
 | 
| 
Zerotorescue@80
 | 
   444 		local move = combinedMoves[numCurrentMove];
 | 
| 
Zerotorescue@80
 | 
   445 		
 | 
| 
Zerotorescue@110
 | 
   446 		-- Only check if the source is locked when we're not bursting (some functions allow mass calling simultaneously and don't trigger item locks)
 | 
| 
Zerotorescue@110
 | 
   447 		local isSourceLocked = ((not ContainerFunctions[movesSource].Burst and sourceLocationsLocked[move.sourceContainer] and sourceLocationsLocked[move.sourceContainer][move.sourceSlot]) or ContainerFunctions[movesSource].IsLocked(move.sourceContainer, move.sourceSlot));
 | 
| 
Zerotorescue@109
 | 
   448 		-- Target are always the local bags
 | 
| 
Zerotorescue@109
 | 
   449 		local isTargetLocked = ((targetLocationsLocked[move.targetContainer] and targetLocationsLocked[move.targetContainer][move.targetSlot]) or ContainerFunctions[addon.Locations.Bag].IsLocked(move.targetContainer, move.targetSlot));
 | 
| 
Zerotorescue@88
 | 
   450 		
 | 
| 
Zerotorescue@110
 | 
   451 		if move and not isSourceLocked and not isTargetLocked and (not ContainerFunctions[movesSource].Synchronous or not hasMoved) then
 | 
| 
Zerotorescue@98
 | 
   452 			addon:Print(("Moving %dx%s."):format(move.num, IdToItemLink(move.itemId)));
 | 
| 
Zerotorescue@80
 | 
   453 			
 | 
| 
Zerotorescue@89
 | 
   454 			addon:Debug("Moving %dx%s from (%d,%d) to (%d,%d)", move.num, IdToItemLink(move.itemId), move.sourceContainer, move.sourceSlot, move.targetContainer, move.targetSlot);
 | 
| 
Zerotorescue@81
 | 
   455 			
 | 
| 
Zerotorescue@110
 | 
   456 			-- Check if the source has been changed since out scan
 | 
| 
Zerotorescue@109
 | 
   457 			if ContainerFunctions[movesSource].GetItemId(move.sourceContainer, move.sourceSlot) ~= move.itemId then
 | 
| 
Zerotorescue@81
 | 
   458 				self:Abort("source changed", "Source (" .. move.sourceContainer .. "," .. move.sourceSlot .. ") is not " .. IdToItemLink(move.itemId));
 | 
| 
Zerotorescue@81
 | 
   459 				return;
 | 
| 
Zerotorescue@81
 | 
   460 			end
 | 
| 
Zerotorescue@81
 | 
   461 			
 | 
| 
Zerotorescue@80
 | 
   462 			-- Pickup stack
 | 
| 
Zerotorescue@109
 | 
   463 			ContainerFunctions[movesSource].PickupItem(move.sourceContainer, move.sourceSlot, move.num);
 | 
| 
Zerotorescue@80
 | 
   464 			
 | 
| 
Zerotorescue@110
 | 
   465 			hasMoved = true;
 | 
| 
Zerotorescue@110
 | 
   466 			
 | 
| 
Zerotorescue@80
 | 
   467 			-- Remember we picked this item up and thus it is now locked
 | 
| 
Zerotorescue@80
 | 
   468 			if not sourceLocationsLocked[move.sourceContainer] then
 | 
| 
Zerotorescue@80
 | 
   469 				sourceLocationsLocked[move.sourceContainer] = {};
 | 
| 
Zerotorescue@80
 | 
   470 			end
 | 
| 
Zerotorescue@80
 | 
   471 			sourceLocationsLocked[move.sourceContainer][move.sourceSlot] = true;
 | 
| 
Zerotorescue@80
 | 
   472 			
 | 
| 
Zerotorescue@110
 | 
   473 			if not ContainerFunctions[movesSource].DoNotDrop then
 | 
| 
Zerotorescue@110
 | 
   474 				-- Some sources don't actually pick items up but just move them, this makes the code below unnessary
 | 
| 
Zerotorescue@110
 | 
   475 				
 | 
| 
Zerotorescue@110
 | 
   476 				if movesSource ~= addon.Locations.Bank or CursorHasItem() then -- CursorHasItem only works when moving from the bank
 | 
| 
Zerotorescue@110
 | 
   477 					-- We are moving into our local bags, so the below must check normal bags
 | 
| 
Zerotorescue@110
 | 
   478 					
 | 
| 
Zerotorescue@110
 | 
   479 					-- Check if the target has been changed since out scan
 | 
| 
Zerotorescue@110
 | 
   480 					local targetItemId = ContainerFunctions[addon.Locations.Bag].GetItemId(move.targetContainer, move.targetSlot);
 | 
| 
Zerotorescue@110
 | 
   481 					if targetItemId and targetItemId ~= move.itemId then
 | 
| 
Zerotorescue@110
 | 
   482 						self:Abort("target changed", "Target (" .. move.targetContainer .. "," .. move.targetSlot .. ") is not " .. IdToItemLink(move.itemId) .. " nor empty");
 | 
| 
Zerotorescue@110
 | 
   483 						return;
 | 
| 
Zerotorescue@110
 | 
   484 					end
 | 
| 
Zerotorescue@110
 | 
   485 					
 | 
| 
Zerotorescue@110
 | 
   486 					-- And drop it (this is always a local bag so no need to do any guild-checks)
 | 
| 
Zerotorescue@110
 | 
   487 					PickupContainerItem(move.targetContainer, move.targetSlot);
 | 
| 
Zerotorescue@110
 | 
   488 					
 | 
| 
Zerotorescue@110
 | 
   489 					-- Remember we dropped an item here and thus this is now locked
 | 
| 
Zerotorescue@110
 | 
   490 					if not targetLocationsLocked[move.targetContainer] then
 | 
| 
Zerotorescue@110
 | 
   491 						targetLocationsLocked[move.targetContainer] = {};
 | 
| 
Zerotorescue@110
 | 
   492 					end
 | 
| 
Zerotorescue@110
 | 
   493 					targetLocationsLocked[move.targetContainer][move.targetSlot] = true;
 | 
| 
Zerotorescue@110
 | 
   494 					
 | 
| 
Zerotorescue@110
 | 
   495 					-- This move was processed
 | 
| 
Zerotorescue@110
 | 
   496 					table.remove(combinedMoves, numCurrentMove);
 | 
| 
Zerotorescue@110
 | 
   497 				else
 | 
| 
Zerotorescue@110
 | 
   498 					self:Abort("item disappeared from mouse", "Couldn't move " .. IdToItemLink(move.itemId) .. ", CursorHasItem() is false");
 | 
| 
Zerotorescue@81
 | 
   499 					return;
 | 
| 
Zerotorescue@81
 | 
   500 				end
 | 
| 
Zerotorescue@110
 | 
   501 			else
 | 
| 
Zerotorescue@110
 | 
   502 				-- When items are deposit automatically we still need to remember when a move has been processed
 | 
| 
Zerotorescue@80
 | 
   503 				
 | 
| 
Zerotorescue@80
 | 
   504 				-- This move was processed
 | 
| 
Zerotorescue@80
 | 
   505 				table.remove(combinedMoves, numCurrentMove);
 | 
| 
Zerotorescue@80
 | 
   506 			end
 | 
| 
Zerotorescue@80
 | 
   507 		end
 | 
| 
Zerotorescue@80
 | 
   508 		
 | 
| 
Zerotorescue@80
 | 
   509 		-- Proceed with the next element (or previous considering we're going from last to first)
 | 
| 
Zerotorescue@80
 | 
   510 		numCurrentMove = (numCurrentMove - 1);
 | 
| 
Zerotorescue@80
 | 
   511 	end
 | 
| 
Zerotorescue@81
 | 
   512 	
 | 
| 
Zerotorescue@89
 | 
   513 	addon:Debug("%d moves processed. %d moves remaining.", (combinedMovesOriginalLength - #combinedMoves), #combinedMoves);
 | 
| 
Zerotorescue@82
 | 
   514 	
 | 
| 
Zerotorescue@81
 | 
   515 	if #combinedMoves == 0 then
 | 
| 
Zerotorescue@98
 | 
   516 		addon:Print("Finished.", addon.Colors.Green);
 | 
| 
Zerotorescue@81
 | 
   517 		
 | 
| 
Zerotorescue@81
 | 
   518 		self:Abort();
 | 
| 
Zerotorescue@81
 | 
   519 		
 | 
| 
Zerotorescue@81
 | 
   520 		return;
 | 
| 
Zerotorescue@81
 | 
   521 	end
 | 
| 
Zerotorescue@80
 | 
   522 end
 | 
| 
Zerotorescue@80
 | 
   523 
 | 
| 
Zerotorescue@80
 | 
   524 local tmrProcessNext;
 | 
| 
Zerotorescue@110
 | 
   525 function mod:SourceUpdated()
 | 
| 
Zerotorescue@81
 | 
   526 	self:CancelTimer(tmrProcessNext, true); -- silent
 | 
| 
Zerotorescue@89
 | 
   527 	tmrProcessNext = self:ScheduleTimer("ProcessMove", .5);
 | 
| 
Zerotorescue@80
 | 
   528 end
 | 
| 
Zerotorescue@80
 | 
   529 
 | 
| 
Zerotorescue@81
 | 
   530 function IdToItemLink(itemId)
 | 
| 
Zerotorescue@81
 | 
   531 	local itemLink = select(2, GetItemInfo(itemId));
 | 
| 
Zerotorescue@81
 | 
   532 	itemLink = itemLink or "Unknown (" .. itemId .. ")";
 | 
| 
Zerotorescue@81
 | 
   533 	return itemLink;
 | 
| 
Zerotorescue@81
 | 
   534 end
 | 
| 
Zerotorescue@81
 | 
   535 
 | 
| 
Zerotorescue@81
 | 
   536 function mod:UI_ERROR_MESSAGE(e, errorMessage)
 | 
| 
Zerotorescue@81
 | 
   537 	if errorMessage == ERR_SPLIT_FAILED then
 | 
| 
Zerotorescue@81
 | 
   538 		self:Abort("splitting failed", "Splitting failed.");
 | 
| 
Zerotorescue@110
 | 
   539 	elseif errorMessage == ERR_GUILD_WITHDRAW_LIMIT then
 | 
| 
Zerotorescue@110
 | 
   540 		self:Abort("at guild withdrawal limit", "At guild withdrawal limit");
 | 
| 
Zerotorescue@80
 | 
   541 	end
 | 
| 
Zerotorescue@80
 | 
   542 end
 | 
| 
Zerotorescue@80
 | 
   543 
 | 
| 
Zerotorescue@81
 | 
   544 function mod:Abort(simple, debugMsg)
 | 
| 
Zerotorescue@110
 | 
   545 	-- Announce
 | 
| 
Zerotorescue@81
 | 
   546 	if debugMsg then
 | 
| 
Zerotorescue@89
 | 
   547 		addon:Debug("Aborting:%s", debugMsg);
 | 
| 
Zerotorescue@81
 | 
   548 	end
 | 
| 
Zerotorescue@81
 | 
   549 	if simple then
 | 
| 
Zerotorescue@98
 | 
   550 		addon:Print(("Aborting: %s."):format(simple), addon.Colors.Red);
 | 
| 
Zerotorescue@81
 | 
   551 	end
 | 
| 
Zerotorescue@80
 | 
   552 	
 | 
| 
Zerotorescue@98
 | 
   553 	-- Make sure nothing is at the mouse
 | 
| 
Zerotorescue@98
 | 
   554 	ClearCursor();
 | 
| 
Zerotorescue@98
 | 
   555 	
 | 
| 
Zerotorescue@81
 | 
   556 	-- Stop timer
 | 
| 
Zerotorescue@110
 | 
   557 	self:UnregisterEvent(ContainerFunctions[movesSource].Event);
 | 
| 
Zerotorescue@81
 | 
   558 	self:CancelTimer(tmrProcessNext, true); -- silent
 | 
| 
Zerotorescue@80
 | 
   559 	
 | 
| 
Zerotorescue@81
 | 
   560 	self:UnregisterEvent("UI_ERROR_MESSAGE");
 | 
| 
Zerotorescue@110
 | 
   561 	
 | 
| 
Zerotorescue@110
 | 
   562 	-- Reset vars
 | 
| 
Zerotorescue@110
 | 
   563 	table.wipe(combinedMoves);
 | 
| 
Zerotorescue@110
 | 
   564 	movesSource = nil;
 | 
| 
Zerotorescue@110
 | 
   565 	if MailAddonBusy == addon:GetName() then
 | 
| 
Zerotorescue@110
 | 
   566 		MailAddonBusy = nil;
 | 
| 
Zerotorescue@110
 | 
   567 	end
 | 
| 
Zerotorescue@80
 | 
   568 end
 | 
| 
Zerotorescue@80
 | 
   569 
 | 
| 
Zerotorescue@80
 | 
   570 function mod:OnEnable()
 | 
| 
Zerotorescue@80
 | 
   571 	Scanner = addon:GetModule("Scanner");
 | 
| 
Zerotorescue@80
 | 
   572 end
 | 
| 
Zerotorescue@80
 | 
   573 
 | 
| 
Zerotorescue@80
 | 
   574 function mod:OnDisable()
 | 
| 
Zerotorescue@80
 | 
   575 	Scanner = nil;
 | 
| 
Zerotorescue@81
 | 
   576 	
 | 
| 
Zerotorescue@81
 | 
   577 	self:Abort();
 | 
| 
Zerotorescue@80
 | 
   578 end
 |