Mercurial > wow > inventory
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 79:b89b6981783f | 80:c0bf2ddb5288 |
|---|---|
| 1 local addon = select(2, ...); | |
| 2 local mod = addon:NewModule("Mover", "AceEvent-3.0", "AceTimer-3.0"); | |
| 3 | |
| 4 local Scanner; | |
| 5 local queuedMoves = {}; -- table storing all queued moves before BeginMove is called | |
| 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 #) | |
| 7 | |
| 8 function mod:AddMove(itemId, amount) | |
| 9 table.insert(queuedMoves, { | |
| 10 id = itemId, | |
| 11 num = amount, | |
| 12 }); | |
| 13 end | |
| 14 | |
| 15 function mod:BeginMove(location, onFinish) | |
| 16 | |
| 17 -- Find the outgoing moves | |
| 18 -- 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 | |
| 19 | |
| 20 -- Get a list of items in the source container | |
| 21 local sourceContents = Scanner:CacheLocation(location, false); | |
| 22 | |
| 23 local outgoingMoves = {}; | |
| 24 | |
| 25 for singleMove in pairs(queuedMoves) do | |
| 26 local sourceItem = sourceContents[singleMove.id]; | |
| 27 if not sourceItem then | |
| 28 print("Can't move " .. IdToItemLink(singleMove.id) .. ", non-existant in source"); | |
| 29 else | |
| 30 -- We want to move the smallest stacks first to keep stuff pretty | |
| 31 table.sort(sourceItem.locations, function(a, b) | |
| 32 return a.count > b.count; | |
| 33 end); | |
| 34 | |
| 35 for itemLocation in pairs(sourceItem.locations) do | |
| 36 -- if this location has more items than we need, only move what we need, otherwise move everything in this stack | |
| 37 local movingNum = ((itemLocation.count > singleMove.num and singleMove.num) or itemLocation.count); | |
| 38 | |
| 39 table.insert(outgoingMoves, { | |
| 40 itemId = singleMove.id, | |
| 41 container = itemLocation.container, | |
| 42 slot = itemLocation.slot, | |
| 43 count = movingNum, | |
| 44 }); | |
| 45 | |
| 46 singleMove.num = (singleMove.num - movingNum); | |
| 47 | |
| 48 if singleMove.num == 0 then | |
| 49 -- If we have prepared everything we wanted, go to the next queued move | |
| 50 break; | |
| 51 end | |
| 52 end | |
| 53 end | |
| 54 end | |
| 55 | |
| 56 -- No longer needed | |
| 57 table.wipe(queuedMoves); | |
| 58 | |
| 59 -- Process every single outgoing move and find fitting targets | |
| 60 | |
| 61 -- Get a list of items already in the target container | |
| 62 local targetContents = Scanner:CacheLocation(addon.Locations.Bag, false); | |
| 63 | |
| 64 -- Find all empty slots | |
| 65 | |
| 66 local emptySlots = {}; | |
| 67 | |
| 68 local start = 0; | |
| 69 local stop = NUM_BAG_SLOTS; | |
| 70 | |
| 71 -- Go through all our bags, including the backpack | |
| 72 for bagId = start, stop do | |
| 73 -- Go through all our slots | |
| 74 for slotId = 1, GetContainerNumSlots(bagId) do | |
| 75 local itemId = GetContainerItemID(bagId, slotId); | |
| 76 | |
| 77 if not itemId then | |
| 78 table.insert(emptySlots, { | |
| 79 container: bagId, | |
| 80 slot: slotId, | |
| 81 }); | |
| 82 end | |
| 83 end | |
| 84 end | |
| 85 | |
| 86 while #outgoingMoves ~= 0 do | |
| 87 -- A not equal-comparison should be quicker than a larger/smaller than-comparison | |
| 88 | |
| 89 for outgoingMove in pairs(outgoingMoves) do | |
| 90 -- itemId will be set to nil when this outgoing move was processed - sanity check | |
| 91 if outgoingMove.itemId then | |
| 92 local targetItem = targetContents[outgoingMove.itemId]; | |
| 93 | |
| 94 if not targetItem then | |
| 95 -- grab an empty slot | |
| 96 -- make new instance of ItemMove | |
| 97 -- populate targetContents with it so future moves of this item can be put on top of it if this isn't a full stack | |
| 98 | |
| 99 local firstAvailableSlot = emptySlots[1]; | |
| 100 | |
| 101 if not firstAvailableSlot then | |
| 102 print("Bags are full. Skipping " .. IdToItemLink(outgoingMove.itemId) .. "."); | |
| 103 | |
| 104 outgoingMove.itemId = nil; | |
| 105 else | |
| 106 table.insert(combinedMoves, { | |
| 107 sourceContainer = outgoingMove.container, | |
| 108 sourceSlot = outgoingMove.slot, | |
| 109 targetContainer = firstAvailableSlot.container, | |
| 110 targetSlot = firstAvailableSlot.slot, | |
| 111 itemId = outgoingMove.itemId, | |
| 112 num = outgoingMove.count, | |
| 113 }); | |
| 114 | |
| 115 -- We filled an empty slot so the target contents now has one more item, | |
| 116 -- make a new instance of the ItemMove class so any additional items with this id can be stacked on top of it | |
| 117 local itemMove = addon.ItemMove:New(); | |
| 118 itemMove.AddLocation(firstAvailableSlot.container, firstAvailableSlot.slot, outgoingMove.count); | |
| 119 targetContents[outgoingMove.itemId] = itemMove; | |
| 120 | |
| 121 firstAvailableSlot = nil; -- no longer empty | |
| 122 | |
| 123 outgoingMove.count = 0; -- nothing remaining - sanity check | |
| 124 outgoingMove.itemId = nil; -- remove this record from the outgoingMoves-table | |
| 125 end | |
| 126 else | |
| 127 -- Find the maximum stack size for this item | |
| 128 local itemStackCount = select(8, GetItemInfo(outgoingMove.itemId)); | |
| 129 | |
| 130 -- We want to move to the largest stacks first to keep stuff pretty | |
| 131 table.sort(targetItem.locations, function(a, b) | |
| 132 return a.count < b.count; | |
| 133 end); | |
| 134 | |
| 135 for itemLocation in pairs(targetItem.locations) do | |
| 136 if itemLocation.count < itemStackCount and outgoingMove.count > 0 then | |
| 137 -- Check if this stack isn't already full (and we still need to move this item) | |
| 138 | |
| 139 local remainingSpace = (itemStackCount - itemLocation.count); | |
| 140 if remainingSpace > outgoingMove.count then | |
| 141 -- Enough room to move this entire stack | |
| 142 -- Deposit this item and then forget this outgoing move as everything in it was processed | |
| 143 | |
| 144 table.insert(combinedMoves, { | |
| 145 sourceContainer = outgoingMove.container, | |
| 146 sourceSlot = outgoingMove.slot, | |
| 147 targetContainer = itemLocation.container, | |
| 148 targetSlot = itemLocation.slot, | |
| 149 itemId = outgoingMove.itemId, | |
| 150 num = outgoingMove.count, | |
| 151 }); | |
| 152 | |
| 153 itemLocation.count = (itemLocation.count + outgoingMove.count); | |
| 154 outgoingMove.count = 0; -- nothing remaining | |
| 155 outgoingMove.itemId = nil; -- remove this record from the outgoingMoves-table | |
| 156 break; -- stop the locations-loop | |
| 157 else | |
| 158 -- Deposit this item but don't remove the outgoing move as there are some items left to move | |
| 159 | |
| 160 table.insert(combinedMoves, { | |
| 161 sourceContainer = outgoingMove.container, | |
| 162 sourceSlot = outgoingMove.slot, | |
| 163 targetContainer = itemLocation.container, | |
| 164 targetSlot = itemLocation.slot, | |
| 165 itemId = outgoingMove.itemId, | |
| 166 num = outgoingMove.count, | |
| 167 }); | |
| 168 | |
| 169 -- The target will be full when we complete, but the source will still have remaining items left to be moved | |
| 170 itemLocation.count = itemStackCount; | |
| 171 outgoingMove.count = (outgoingMove.count - remainingSpace); | |
| 172 end | |
| 173 end | |
| 174 end | |
| 175 | |
| 176 if outgoingMove.count > 0 then | |
| 177 -- We went through all matching items and checked their stack sizes if we could move this there, no room available | |
| 178 -- 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 | |
| 179 targetItem = nil; | |
| 180 end | |
| 181 end | |
| 182 end | |
| 183 end | |
| 184 | |
| 185 -- Loop through the array to find items that should be removed, start with the last element or the loop would break | |
| 186 local numOutgoingMoves = #outgoingMoves; -- since LUA-tables start at an index of 1, this is actually an existing index (outgoingMoves[#outgoingMoves] would return a value) | |
| 187 while numOutgoingMoves ~= 0 do | |
| 188 -- A not equal-comparison should be quicker than a larger/smaller than-comparison | |
| 189 | |
| 190 -- Check if the item id is nil, this is set to nil when this outgoing move has been processed | |
| 191 if not outgoingMoves[numOutgoingMoves].itemId then | |
| 192 -- Remove this element from the array | |
| 193 table.remove(outgoingMoves, numOutgoingMoves); | |
| 194 end | |
| 195 | |
| 196 -- Proceed with the next element (or previous considering we're going from last to first) | |
| 197 numOutgoingMoves = (numOutgoingMoves - 1); | |
| 198 end | |
| 199 end | |
| 200 | |
| 201 -- No longer needed | |
| 202 table.wipe(emptySlots); | |
| 203 | |
| 204 DoMoveNow(); | |
| 205 | |
| 206 --ProcessMove(); | |
| 207 | |
| 208 --mod:RegisterEvent("BAG_UPDATE", BAG_UPDATE); | |
| 209 | |
| 210 --onFinish(); | |
| 211 end | |
| 212 | |
| 213 function DoMoveNow() | |
| 214 -- combinedMoves now has all moves in it (source -> target) | |
| 215 -- go through list, move everything inside it | |
| 216 -- add source and target to one single list | |
| 217 -- if either is already in this list, skip this move | |
| 218 -- repeat every 5 seconds until we're completely done | |
| 219 | |
| 220 local sourceLocationsLocked = {}; | |
| 221 local targetLocationsLocked = {}; | |
| 222 | |
| 223 local numCurrentMove = #combinedMoves; | |
| 224 while numCurrentMove ~= 0 do | |
| 225 local move = combinedMoves[numCurrentMove]; | |
| 226 | |
| 227 -- sourceContainer, sourceSlot, targetContainer, targetSlot, itemId, num | |
| 228 if (not sourceLocationsLocked[move.sourceContainer] or not sourceLocationsLocked[move.sourceContainer][move.sourceSlot]) and | |
| 229 (not targetLocationsLocked[move.targetContainer] or not targetLocationsLocked[move.targetContainer][move.targetSlot]) then | |
| 230 | |
| 231 print("Moving " .. IdToItemLink(move.itemId)); | |
| 232 | |
| 233 -- Pickup stack | |
| 234 SplitGuildBankItem(move.sourceContainer, move.sourceSlot, move.num); | |
| 235 | |
| 236 -- Remember we picked this item up and thus it is now locked | |
| 237 if not sourceLocationsLocked[move.sourceContainer] then | |
| 238 sourceLocationsLocked[move.sourceContainer] = {}; | |
| 239 end | |
| 240 sourceLocationsLocked[move.sourceContainer][move.sourceSlot] = true; | |
| 241 | |
| 242 if CursorHasItem() then | |
| 243 -- And drop it | |
| 244 PickupContainerItem(move.targetContainer, move.targetSlot); | |
| 245 | |
| 246 -- Remember we dropped an item here and thus this is now locked | |
| 247 if not sourceLocationsLocked[move.targetContainer] then | |
| 248 sourceLocationsLocked[move.targetContainer] = {}; | |
| 249 end | |
| 250 sourceLocationsLocked[move.targetContainer][move.targetSlot] = true; | |
| 251 | |
| 252 -- This move was processed | |
| 253 table.remove(combinedMoves, numCurrentMove); | |
| 254 end | |
| 255 end | |
| 256 | |
| 257 -- Proceed with the next element (or previous considering we're going from last to first) | |
| 258 numCurrentMove = (numCurrentMove - 1); | |
| 259 end | |
| 260 end | |
| 261 | |
| 262 local tmrProcessNext; | |
| 263 function BAG_UPDATE() | |
| 264 mod:CancelTimer(tmrProcessNext, true); -- silent | |
| 265 tmrProcessNext = mod:ScheduleTimer(function() | |
| 266 ProcessMove(); | |
| 267 end, 2); | |
| 268 end | |
| 269 | |
| 270 function ProcessMove() | |
| 271 local currentMove = queuedMoves[1]; | |
| 272 | |
| 273 if currentMove then | |
| 274 addon:Debug("Moving " .. currentMove.num .. " of " .. IdToItemLink(currentMove.id)); | |
| 275 | |
| 276 local requestedMoves = currentMove.num; | |
| 277 | |
| 278 if currentMove.src == addon.Locations.Bank then | |
| 279 MoveBankItem(currentMove); | |
| 280 elseif currentMove.src == addon.Locations.Guild then | |
| 281 MoveGuildItem(currentMove); | |
| 282 end | |
| 283 | |
| 284 if requestedMoves == currentMove.num then | |
| 285 print("Skipping " .. IdToItemLink(move.id)); | |
| 286 move.num = 0; | |
| 287 elseif currentMove.num > 0 then | |
| 288 -- bags are full | |
| 289 print("bags are full"); | |
| 290 end | |
| 291 | |
| 292 if currentMove.num == 0 then | |
| 293 table.remove(queuedMoves, 1); | |
| 294 end | |
| 295 end | |
| 296 end | |
| 297 | |
| 298 function MoveGuildItem(move) | |
| 299 local tabId = GetCurrentGuildBankTab(); | |
| 300 local slotId = (MAX_GUILDBANK_SLOTS_PER_TAB or 98); -- start by scanning the last slot | |
| 301 | |
| 302 if tabId == nil or tabId < 1 then return; end | |
| 303 | |
| 304 while slotId ~= 0 do | |
| 305 -- A not equal-comparison should be quicker than a larger than-comparison | |
| 306 | |
| 307 local itemLink = GetGuildBankItemLink(tabId, slotId); | |
| 308 if itemLink then | |
| 309 -- If there is actually an item in this slot | |
| 310 | |
| 311 local itemId = GetItemId(itemLink); | |
| 312 | |
| 313 if itemId and move.id == itemId then | |
| 314 -- This is one of the items we're looking for | |
| 315 | |
| 316 local itemCount = select(2, GetGuildBankItemInfo(tabId, slotId)); | |
| 317 | |
| 318 if itemCount and itemCount > 0 then | |
| 319 -- 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 | |
| 320 local moveable = (move.num > itemCount and itemCount) or move.num; | |
| 321 | |
| 322 -- Pickup stack | |
| 323 SplitGuildBankItem(tabId, slotId, moveable); | |
| 324 | |
| 325 -- Find an target slot and put it there | |
| 326 local reallyMoved = DropItem(itemId, moveable); | |
| 327 if reallyMoved then | |
| 328 -- Keep track of how many we have moved | |
| 329 moved = (moved + reallyMoved); | |
| 330 | |
| 331 -- Update the required amount of items so it has the remaining num | |
| 332 move.num = (move.num - reallyMoved); | |
| 333 | |
| 334 --if reallyMoved ~= moveable then | |
| 335 -- -- 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) | |
| 336 -- slotId = (slotId + 1); | |
| 337 --end | |
| 338 | |
| 339 --if move.num == 0 then | |
| 340 -- if no required items are left to move, then stop and tell the caller function how many we moved | |
| 341 return moved; | |
| 342 --end | |
| 343 end | |
| 344 end | |
| 345 end | |
| 346 end | |
| 347 | |
| 348 -- Continue scanning a different slot | |
| 349 slotId = (slotId - 1); | |
| 350 end | |
| 351 end | |
| 352 | |
| 353 function MoveBankItem(move) | |
| 354 local start = 0; | |
| 355 local stop = NUM_BAG_SLOTS; | |
| 356 | |
| 357 -- If we requested the bank then we don't want the bag info | |
| 358 start = ( NUM_BAG_SLOTS + 1 ); | |
| 359 stop = ( NUM_BAG_SLOTS + NUM_BANKBAGSLOTS ); | |
| 360 | |
| 361 -- Scan the default 100 slots | |
| 362 move.num = (move.num - MoveFromContainter(BANK_CONTAINER, move)); | |
| 363 | |
| 364 -- Go through all our bags, including the backpack | |
| 365 for bagID = start, stop do | |
| 366 move.num = (move.num - MoveFromContainter(bagID, move)); | |
| 367 end | |
| 368 end | |
| 369 | |
| 370 -- Go through all slots of this bag and if a match was found, move the items | |
| 371 function MoveFromContainter(bagID, move) | |
| 372 -- Keep track of how many we have moved | |
| 373 local moved = 0; | |
| 374 | |
| 375 -- Go through all slots of this bag | |
| 376 for slot = 1, GetContainerNumSlots(bagID) do | |
| 377 local itemId = GetContainerItemID(bagID, slot); | |
| 378 | |
| 379 if itemId and move.id == itemId then | |
| 380 -- This is one of the items we're looking for | |
| 381 | |
| 382 local itemCount = select(2, GetContainerItemInfo(bagID, slot)); | |
| 383 | |
| 384 if itemCount and itemCount > 0 then | |
| 385 -- 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 | |
| 386 local moveable = (move.num > itemCount and itemCount) or move.num; | |
| 387 | |
| 388 -- Pickup stack | |
| 389 SplitContainerItem(bagID, slot, moveable); | |
| 390 | |
| 391 addon:Debug("Picked " .. IdToItemLink(itemId) .. " up"); | |
| 392 | |
| 393 -- Find an target slot and put it there | |
| 394 if CursorHasItem() then | |
| 395 local reallyMoved = DropItem(itemId, moveable); | |
| 396 | |
| 397 if reallyMoved then | |
| 398 addon:Debug("Dropped " .. reallyMoved .. " of " .. IdToItemLink(itemId)); | |
| 399 | |
| 400 -- Keep track of how many we have moved | |
| 401 moved = (moved + reallyMoved); | |
| 402 | |
| 403 -- Update the required amount of items so it has the remaining num | |
| 404 move.num = (move.num - reallyMoved); | |
| 405 | |
| 406 --if reallyMoved ~= moveable then | |
| 407 -- 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) | |
| 408 -- slot = (slot - 1); | |
| 409 --end | |
| 410 | |
| 411 --if move.num == 0 then | |
| 412 -- if no required items are left to move, then stop and tell the caller function how many we moved | |
| 413 return moved; | |
| 414 --end | |
| 415 end | |
| 416 end | |
| 417 end | |
| 418 end | |
| 419 end | |
| 420 | |
| 421 return moved; | |
| 422 end | |
| 423 | |
| 424 | |
| 425 -- This currently only uses empty slots, it will have to fill stacks in the future | |
| 426 function DropItem(itemId, amount) | |
| 427 local start = 0; | |
| 428 local stop = NUM_BAG_SLOTS; | |
| 429 | |
| 430 -- Go through all our bags, including the backpack | |
| 431 for bagID = start, stop do | |
| 432 -- Go through all our slots | |
| 433 for slot = 1, GetContainerNumSlots(bagID) do | |
| 434 local itemId = GetContainerItemID(bagID, slot); | |
| 435 | |
| 436 if not itemId then | |
| 437 -- If this slot is empty, put the item here | |
| 438 PickupContainerItem(bagID, slot); | |
| 439 | |
| 440 return amount; | |
| 441 end | |
| 442 end | |
| 443 end | |
| 444 | |
| 445 return; | |
| 446 end | |
| 447 | |
| 448 function IdToItemLink(itemId) | |
| 449 return select(2, GetItemInfo(itemId)); | |
| 450 end | |
| 451 | |
| 452 function mod:OnEnable() | |
| 453 Scanner = addon:GetModule("Scanner"); | |
| 454 end | |
| 455 | |
| 456 function mod:OnDisable() | |
| 457 Scanner = nil; | |
| 458 end |
