annotate Mover.lua @ 83:6b60f7a1410c

Disabled a bunch of probably unused libraries, will have to verify if Inventorium still runs without any other addon. Updated todo list. Probably nolib-creation support.
author Zerotorescue
date Thu, 06 Jan 2011 11:04:55 +0100
parents f885805da5d6
children
rev   line source
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@80 9 function mod:AddMove(itemId, amount)
Zerotorescue@80 10 table.insert(queuedMoves, {
Zerotorescue@80 11 id = itemId,
Zerotorescue@80 12 num = amount,
Zerotorescue@80 13 });
Zerotorescue@80 14 end
Zerotorescue@80 15
Zerotorescue@81 16 function mod:HasMoves()
Zerotorescue@81 17 return (#queuedMoves ~= 0);
Zerotorescue@81 18 end
Zerotorescue@81 19
Zerotorescue@80 20 function mod:BeginMove(location, onFinish)
Zerotorescue@81 21 addon:Debug("BeginMove");
Zerotorescue@80 22
Zerotorescue@80 23 -- Find the outgoing moves
Zerotorescue@80 24 -- 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 25
Zerotorescue@80 26 -- Get a list of items in the source container
Zerotorescue@80 27 local sourceContents = Scanner:CacheLocation(location, false);
Zerotorescue@80 28
Zerotorescue@80 29 local outgoingMoves = {};
Zerotorescue@80 30
Zerotorescue@82 31 addon:Debug(#queuedMoves .. " moves were queued.");
Zerotorescue@82 32
Zerotorescue@81 33 for _, singleMove in pairs(queuedMoves) do
Zerotorescue@80 34 local sourceItem = sourceContents[singleMove.id];
Zerotorescue@80 35 if not sourceItem then
Zerotorescue@80 36 print("Can't move " .. IdToItemLink(singleMove.id) .. ", non-existant in source");
Zerotorescue@80 37 else
Zerotorescue@82 38 -- 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 39 table.sort(sourceItem.locations, function(a, b)
Zerotorescue@81 40 return a.count < b.count;
Zerotorescue@80 41 end);
Zerotorescue@80 42
Zerotorescue@81 43 for _, itemLocation in pairs(sourceItem.locations) do
Zerotorescue@80 44 -- if this location has more items than we need, only move what we need, otherwise move everything in this stack
Zerotorescue@80 45 local movingNum = ((itemLocation.count > singleMove.num and singleMove.num) or itemLocation.count);
Zerotorescue@80 46
Zerotorescue@80 47 table.insert(outgoingMoves, {
Zerotorescue@80 48 itemId = singleMove.id,
Zerotorescue@82 49 num = movingNum,
Zerotorescue@80 50 container = itemLocation.container,
Zerotorescue@80 51 slot = itemLocation.slot,
Zerotorescue@80 52 });
Zerotorescue@80 53
Zerotorescue@80 54 singleMove.num = (singleMove.num - movingNum);
Zerotorescue@80 55
Zerotorescue@80 56 if singleMove.num == 0 then
Zerotorescue@80 57 -- If we have prepared everything we wanted, go to the next queued move
Zerotorescue@81 58 break; -- stop the locations-loop
Zerotorescue@80 59 end
Zerotorescue@80 60 end
Zerotorescue@80 61 end
Zerotorescue@80 62 end
Zerotorescue@82 63
Zerotorescue@82 64 addon:Debug(#outgoingMoves .. " outgoing moves are possible.");
Zerotorescue@80 65
Zerotorescue@80 66 -- No longer needed
Zerotorescue@80 67 table.wipe(queuedMoves);
Zerotorescue@80 68
Zerotorescue@80 69 -- Process every single outgoing move and find fitting targets
Zerotorescue@80 70
Zerotorescue@80 71 -- Get a list of items already in the target container
Zerotorescue@80 72 local targetContents = Scanner:CacheLocation(addon.Locations.Bag, false);
Zerotorescue@80 73
Zerotorescue@80 74 -- Find all empty slots
Zerotorescue@80 75
Zerotorescue@80 76 local emptySlots = {};
Zerotorescue@80 77
Zerotorescue@80 78 local start = 0;
Zerotorescue@80 79 local stop = NUM_BAG_SLOTS;
Zerotorescue@80 80
Zerotorescue@80 81 -- Go through all our bags, including the backpack
Zerotorescue@80 82 for bagId = start, stop do
Zerotorescue@80 83 -- Go through all our slots
Zerotorescue@80 84 for slotId = 1, GetContainerNumSlots(bagId) do
Zerotorescue@82 85 local itemId = GetContainerItemID(bagId, slotId); -- we're scanning our local bags here, so no need to get messy with guild bank support
Zerotorescue@80 86
Zerotorescue@80 87 if not itemId then
Zerotorescue@80 88 table.insert(emptySlots, {
Zerotorescue@81 89 container = bagId,
Zerotorescue@81 90 slot = slotId,
Zerotorescue@80 91 });
Zerotorescue@80 92 end
Zerotorescue@80 93 end
Zerotorescue@80 94 end
Zerotorescue@82 95
Zerotorescue@82 96 addon:Debug(#emptySlots .. " empty slots are available.");
Zerotorescue@80 97
Zerotorescue@81 98 -- Remember where we're moving from
Zerotorescue@81 99 movesSource = location;
Zerotorescue@81 100
Zerotorescue@80 101 while #outgoingMoves ~= 0 do
Zerotorescue@80 102 -- A not equal-comparison should be quicker than a larger/smaller than-comparison
Zerotorescue@80 103
Zerotorescue@81 104 for _, outgoingMove in pairs(outgoingMoves) do
Zerotorescue@80 105 -- itemId will be set to nil when this outgoing move was processed - sanity check
Zerotorescue@80 106 if outgoingMove.itemId then
Zerotorescue@80 107 local targetItem = targetContents[outgoingMove.itemId];
Zerotorescue@80 108
Zerotorescue@80 109 if not targetItem then
Zerotorescue@80 110 -- grab an empty slot
Zerotorescue@80 111 -- make new instance of ItemMove
Zerotorescue@80 112 -- populate targetContents with it so future moves of this item can be put on top of it if this isn't a full stack
Zerotorescue@80 113
Zerotorescue@80 114 local firstAvailableSlot = emptySlots[1];
Zerotorescue@80 115
Zerotorescue@80 116 if not firstAvailableSlot then
Zerotorescue@80 117 print("Bags are full. Skipping " .. IdToItemLink(outgoingMove.itemId) .. ".");
Zerotorescue@80 118
Zerotorescue@82 119 outgoingMove.itemId = nil; -- remove this record from the outgoingMoves-table
Zerotorescue@80 120 else
Zerotorescue@80 121 table.insert(combinedMoves, {
Zerotorescue@82 122 itemId = outgoingMove.itemId,
Zerotorescue@82 123 num = outgoingMove.num,
Zerotorescue@80 124 sourceContainer = outgoingMove.container,
Zerotorescue@80 125 sourceSlot = outgoingMove.slot,
Zerotorescue@80 126 targetContainer = firstAvailableSlot.container,
Zerotorescue@80 127 targetSlot = firstAvailableSlot.slot,
Zerotorescue@80 128 });
Zerotorescue@80 129
Zerotorescue@80 130 -- We filled an empty slot so the target contents now has one more item,
Zerotorescue@80 131 -- make a new instance of the ItemMove class so any additional items with this id can be stacked on top of it
Zerotorescue@81 132 local itemMove = addon.ContainerItem:New();
Zerotorescue@82 133 itemMove:AddLocation(firstAvailableSlot.container, firstAvailableSlot.slot, outgoingMove.num);
Zerotorescue@80 134 targetContents[outgoingMove.itemId] = itemMove;
Zerotorescue@80 135
Zerotorescue@81 136 table.remove(emptySlots, 1); -- no longer empty
Zerotorescue@80 137
Zerotorescue@82 138 outgoingMove.num = 0; -- nothing remaining - sanity check
Zerotorescue@80 139 outgoingMove.itemId = nil; -- remove this record from the outgoingMoves-table
Zerotorescue@80 140 end
Zerotorescue@80 141 else
Zerotorescue@80 142 -- Find the maximum stack size for this item
Zerotorescue@80 143 local itemStackCount = select(8, GetItemInfo(outgoingMove.itemId));
Zerotorescue@80 144
Zerotorescue@80 145 -- We want to move to the largest stacks first to keep stuff pretty
Zerotorescue@80 146 table.sort(targetItem.locations, function(a, b)
Zerotorescue@81 147 return a.count > b.count;
Zerotorescue@80 148 end);
Zerotorescue@80 149
Zerotorescue@81 150 for _, itemLocation in pairs(targetItem.locations) do
Zerotorescue@82 151 if itemLocation.count < itemStackCount and outgoingMove.num > 0 then
Zerotorescue@80 152 -- Check if this stack isn't already full (and we still need to move this item)
Zerotorescue@80 153
Zerotorescue@80 154 local remainingSpace = (itemStackCount - itemLocation.count);
Zerotorescue@82 155 if remainingSpace > outgoingMove.num then
Zerotorescue@80 156 -- Enough room to move this entire stack
Zerotorescue@80 157 -- Deposit this item and then forget this outgoing move as everything in it was processed
Zerotorescue@80 158
Zerotorescue@80 159 table.insert(combinedMoves, {
Zerotorescue@82 160 itemId = outgoingMove.itemId,
Zerotorescue@82 161 num = outgoingMove.num,
Zerotorescue@80 162 sourceContainer = outgoingMove.container,
Zerotorescue@80 163 sourceSlot = outgoingMove.slot,
Zerotorescue@80 164 targetContainer = itemLocation.container,
Zerotorescue@80 165 targetSlot = itemLocation.slot,
Zerotorescue@80 166 });
Zerotorescue@80 167
Zerotorescue@82 168 itemLocation.count = (itemLocation.count + outgoingMove.num);
Zerotorescue@80 169 outgoingMove.count = 0; -- nothing remaining
Zerotorescue@80 170 outgoingMove.itemId = nil; -- remove this record from the outgoingMoves-table
Zerotorescue@80 171 break; -- stop the locations-loop
Zerotorescue@80 172 else
Zerotorescue@80 173 -- Deposit this item but don't remove the outgoing move as there are some items left to move
Zerotorescue@80 174
Zerotorescue@80 175 table.insert(combinedMoves, {
Zerotorescue@82 176 itemId = outgoingMove.itemId,
Zerotorescue@82 177 num = outgoingMove.num,
Zerotorescue@80 178 sourceContainer = outgoingMove.container,
Zerotorescue@80 179 sourceSlot = outgoingMove.slot,
Zerotorescue@80 180 targetContainer = itemLocation.container,
Zerotorescue@80 181 targetSlot = itemLocation.slot,
Zerotorescue@80 182 });
Zerotorescue@80 183
Zerotorescue@80 184 -- The target will be full when we complete, but the source will still have remaining items left to be moved
Zerotorescue@80 185 itemLocation.count = itemStackCount;
Zerotorescue@82 186 outgoingMove.num = (outgoingMove.num - remainingSpace);
Zerotorescue@80 187 end
Zerotorescue@80 188 end
Zerotorescue@80 189 end
Zerotorescue@80 190
Zerotorescue@82 191 if outgoingMove.num > 0 then
Zerotorescue@80 192 -- We went through all matching items and checked their stack sizes if we could move this there, no room available
Zerotorescue@80 193 -- 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@80 194 targetItem = nil;
Zerotorescue@80 195 end
Zerotorescue@80 196 end
Zerotorescue@80 197 end
Zerotorescue@80 198 end
Zerotorescue@80 199
Zerotorescue@80 200 -- Loop through the array to find items that should be removed, start with the last element or the loop would break
Zerotorescue@80 201 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 202 while numOutgoingMoves ~= 0 do
Zerotorescue@80 203 -- A not equal-comparison should be quicker than a larger/smaller than-comparison
Zerotorescue@80 204
Zerotorescue@80 205 -- Check if the item id is nil, this is set to nil when this outgoing move has been processed
Zerotorescue@80 206 if not outgoingMoves[numOutgoingMoves].itemId then
Zerotorescue@80 207 -- Remove this element from the array
Zerotorescue@80 208 table.remove(outgoingMoves, numOutgoingMoves);
Zerotorescue@80 209 end
Zerotorescue@80 210
Zerotorescue@80 211 -- Proceed with the next element (or previous considering we're going from last to first)
Zerotorescue@80 212 numOutgoingMoves = (numOutgoingMoves - 1);
Zerotorescue@80 213 end
Zerotorescue@80 214 end
Zerotorescue@82 215
Zerotorescue@82 216 addon:Debug(#combinedMoves .. " moves should be possible.");
Zerotorescue@80 217
Zerotorescue@80 218 -- No longer needed
Zerotorescue@80 219 table.wipe(emptySlots);
Zerotorescue@80 220
Zerotorescue@81 221 self:ProcessMove();
Zerotorescue@80 222
Zerotorescue@82 223 -- Even though we aren't completely done yet, allow requeueing
Zerotorescue@81 224 onFinish();
Zerotorescue@80 225 end
Zerotorescue@80 226
Zerotorescue@81 227 if not table.reverse then
Zerotorescue@81 228 -- table.reverse = function(orig)
Zerotorescue@81 229 -- local temp = CopyTable(orig);
Zerotorescue@81 230 -- local origLength = #temp;
Zerotorescue@81 231 -- for i = 1, origLength do
Zerotorescue@81 232 -- orig[(origLength - i + 1)] = temp[i];
Zerotorescue@81 233 -- end
Zerotorescue@81 234 -- end
Zerotorescue@81 235 table.reverse = function(orig)
Zerotorescue@81 236 local temp = {};
Zerotorescue@81 237 local origLength = #orig;
Zerotorescue@81 238 for i = 1, origLength do
Zerotorescue@81 239 temp[(origLength - i + 1)] = orig[i];
Zerotorescue@81 240 end
Zerotorescue@81 241
Zerotorescue@81 242 -- -- Update the original table (can't do orig = temp as that would change the reference-link instead of the original table)
Zerotorescue@81 243 -- for i, v in pairs(temp) do
Zerotorescue@81 244 -- orig[i] = v;
Zerotorescue@81 245 -- end
Zerotorescue@81 246 return temp; -- for speed we choose to do a return instead
Zerotorescue@81 247 end
Zerotorescue@81 248 end
Zerotorescue@81 249
Zerotorescue@81 250 function mod:ProcessMove()
Zerotorescue@81 251 addon:Debug("ProcessMove");
Zerotorescue@81 252
Zerotorescue@81 253 if #combinedMoves == 0 then
Zerotorescue@81 254 print("Nothing to move.");
Zerotorescue@81 255
Zerotorescue@81 256 self:Abort();
Zerotorescue@81 257
Zerotorescue@81 258 return;
Zerotorescue@81 259 end
Zerotorescue@81 260
Zerotorescue@82 261 self:RegisterEvent("BAG_UPDATE");
Zerotorescue@81 262 self:RegisterEvent("UI_ERROR_MESSAGE");
Zerotorescue@81 263
Zerotorescue@80 264 -- combinedMoves now has all moves in it (source -> target)
Zerotorescue@80 265 -- go through list, move everything inside it
Zerotorescue@81 266 -- add source and target to lists, if either is already in this list, skip the move
Zerotorescue@81 267 -- repeat every few seconds until we're completely done
Zerotorescue@80 268
Zerotorescue@80 269 local sourceLocationsLocked = {};
Zerotorescue@80 270 local targetLocationsLocked = {};
Zerotorescue@80 271
Zerotorescue@81 272 -- 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@82 273 combinedMoves = table.reverse(combinedMoves);
Zerotorescue@81 274
Zerotorescue@82 275 local GetContainerItemID;
Zerotorescue@82 276 if movesSource == addon.Locations.Guild then
Zerotorescue@82 277 GetContainerItemID = GetGuildBankItemLink;
Zerotorescue@82 278 else
Zerotorescue@82 279 GetContainerItemID = GetContainerItemID;
Zerotorescue@82 280 end
Zerotorescue@82 281
Zerotorescue@82 282 local combinedMovesOriginalLength = #combinedMoves;
Zerotorescue@82 283 local numCurrentMove = combinedMovesOriginalLength;
Zerotorescue@80 284 while numCurrentMove ~= 0 do
Zerotorescue@80 285 local move = combinedMoves[numCurrentMove];
Zerotorescue@80 286
Zerotorescue@80 287 -- sourceContainer, sourceSlot, targetContainer, targetSlot, itemId, num
Zerotorescue@81 288 if move and (not sourceLocationsLocked[move.sourceContainer] or not sourceLocationsLocked[move.sourceContainer][move.sourceSlot]) and
Zerotorescue@80 289 (not targetLocationsLocked[move.targetContainer] or not targetLocationsLocked[move.targetContainer][move.targetSlot]) then
Zerotorescue@80 290
Zerotorescue@80 291 print("Moving " .. IdToItemLink(move.itemId));
Zerotorescue@80 292
Zerotorescue@81 293 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));
Zerotorescue@81 294
Zerotorescue@81 295 if GetContainerItemID(move.sourceContainer, move.sourceSlot) ~= move.itemId then
Zerotorescue@81 296 self:Abort("source changed", "Source (" .. move.sourceContainer .. "," .. move.sourceSlot .. ") is not " .. IdToItemLink(move.itemId));
Zerotorescue@81 297 return;
Zerotorescue@81 298 end
Zerotorescue@81 299
Zerotorescue@80 300 -- Pickup stack
Zerotorescue@81 301 if movesSource == addon.Locations.Bank then
Zerotorescue@81 302 SplitContainerItem(move.sourceContainer, move.sourceSlot, move.num);
Zerotorescue@81 303 elseif movesSource == addon.Locations.Guild then
Zerotorescue@81 304 SplitGuildBankItem(move.sourceContainer, move.sourceSlot, move.num);
Zerotorescue@81 305 end
Zerotorescue@80 306
Zerotorescue@80 307 -- Remember we picked this item up and thus it is now locked
Zerotorescue@80 308 if not sourceLocationsLocked[move.sourceContainer] then
Zerotorescue@80 309 sourceLocationsLocked[move.sourceContainer] = {};
Zerotorescue@80 310 end
Zerotorescue@80 311 sourceLocationsLocked[move.sourceContainer][move.sourceSlot] = true;
Zerotorescue@80 312
Zerotorescue@81 313 if movesSource == addon.Locations.Guild or CursorHasItem() then -- CursorHasItem is always false if source is a guild tab
Zerotorescue@81 314 if GetContainerItemID(move.targetContainer, move.targetSlot) and GetContainerItemID(move.targetContainer, move.targetSlot) ~= move.itemId then
Zerotorescue@81 315 self:Abort("target changed", "Target (" .. move.targetContainer .. "," .. move.targetSlot .. ") is not " .. IdToItemLink(move.itemId) .. " nor empty");
Zerotorescue@81 316 return;
Zerotorescue@81 317 end
Zerotorescue@81 318
Zerotorescue@82 319 -- And drop it (this is always a local bag so no need to do any guild-checks)
Zerotorescue@80 320 PickupContainerItem(move.targetContainer, move.targetSlot);
Zerotorescue@80 321
Zerotorescue@80 322 -- Remember we dropped an item here and thus this is now locked
Zerotorescue@81 323 if not targetLocationsLocked[move.targetContainer] then
Zerotorescue@81 324 targetLocationsLocked[move.targetContainer] = {};
Zerotorescue@80 325 end
Zerotorescue@81 326 targetLocationsLocked[move.targetContainer][move.targetSlot] = true;
Zerotorescue@80 327
Zerotorescue@80 328 -- This move was processed
Zerotorescue@80 329 table.remove(combinedMoves, numCurrentMove);
Zerotorescue@81 330 else
Zerotorescue@81 331 self:Abort("item disappeared from mouse", "Couldn't move " .. IdToItemLink(move.itemId) .. ", CursorHasItem() is false");
Zerotorescue@81 332 return;
Zerotorescue@80 333 end
Zerotorescue@80 334 end
Zerotorescue@80 335
Zerotorescue@80 336 -- Proceed with the next element (or previous considering we're going from last to first)
Zerotorescue@80 337 numCurrentMove = (numCurrentMove - 1);
Zerotorescue@80 338 end
Zerotorescue@81 339
Zerotorescue@82 340 addon:Debug((combinedMovesOriginalLength - #combinedMoves) .. " moves processed. " .. #combinedMoves .. " moves remaining.");
Zerotorescue@82 341
Zerotorescue@81 342 if #combinedMoves == 0 then
Zerotorescue@81 343 print("Finished.");
Zerotorescue@81 344
Zerotorescue@81 345 self:Abort();
Zerotorescue@81 346
Zerotorescue@81 347 return;
Zerotorescue@81 348 end
Zerotorescue@80 349 end
Zerotorescue@80 350
Zerotorescue@80 351 local tmrProcessNext;
Zerotorescue@81 352 function mod:BAG_UPDATE()
Zerotorescue@81 353 self:CancelTimer(tmrProcessNext, true); -- silent
Zerotorescue@81 354 tmrProcessNext = self:ScheduleTimer("ProcessMove", 1);
Zerotorescue@80 355 end
Zerotorescue@80 356
Zerotorescue@81 357 function IdToItemLink(itemId)
Zerotorescue@81 358 local itemLink = select(2, GetItemInfo(itemId));
Zerotorescue@81 359 itemLink = itemLink or "Unknown (" .. itemId .. ")";
Zerotorescue@81 360 return itemLink;
Zerotorescue@81 361 end
Zerotorescue@81 362
Zerotorescue@81 363 function mod:UI_ERROR_MESSAGE(e, errorMessage)
Zerotorescue@81 364 if errorMessage == ERR_SPLIT_FAILED then
Zerotorescue@81 365 self:Abort("splitting failed", "Splitting failed.");
Zerotorescue@80 366 end
Zerotorescue@80 367 end
Zerotorescue@80 368
Zerotorescue@81 369 function mod:Abort(simple, debugMsg)
Zerotorescue@81 370 if debugMsg then
Zerotorescue@81 371 addon:Debug("Aborting:" .. debugMsg);
Zerotorescue@81 372 end
Zerotorescue@81 373 if simple then
Zerotorescue@81 374 print("|cffff0000Aborting: " .. simple .. ".|r");
Zerotorescue@81 375 end
Zerotorescue@81 376 table.wipe(combinedMoves);
Zerotorescue@81 377 movesSource = nil;
Zerotorescue@80 378
Zerotorescue@81 379 -- Stop timer
Zerotorescue@81 380 self:UnregisterEvent("BAG_UPDATE");
Zerotorescue@81 381 self:CancelTimer(tmrProcessNext, true); -- silent
Zerotorescue@80 382
Zerotorescue@81 383 self:UnregisterEvent("UI_ERROR_MESSAGE");
Zerotorescue@80 384 end
Zerotorescue@80 385
Zerotorescue@80 386 function mod:OnEnable()
Zerotorescue@80 387 Scanner = addon:GetModule("Scanner");
Zerotorescue@80 388 end
Zerotorescue@80 389
Zerotorescue@80 390 function mod:OnDisable()
Zerotorescue@80 391 Scanner = nil;
Zerotorescue@81 392
Zerotorescue@81 393 self:Abort();
Zerotorescue@80 394 end