Mercurial > wow > inventory
comparison Mover.lua @ 82:f885805da5d6
Added options to toggle the automatic refilling. This defaults to true.
Normalized property amount names; a move has a ?num? that must be moved and a location has a ?count? indicating the amount of items at that slot.
Target/source item verification should now be working properly for guilds.
When ?bank? is included in the local item count, we will skip trying to auto refill from this.
| author | Zerotorescue |
|---|---|
| date | Thu, 06 Jan 2011 10:48:56 +0100 |
| parents | 58617c7827fa |
| children |
comparison
equal
deleted
inserted
replaced
| 81:58617c7827fa | 82:f885805da5d6 |
|---|---|
| 26 -- Get a list of items in the source container | 26 -- Get a list of items in the source container |
| 27 local sourceContents = Scanner:CacheLocation(location, false); | 27 local sourceContents = Scanner:CacheLocation(location, false); |
| 28 | 28 |
| 29 local outgoingMoves = {}; | 29 local outgoingMoves = {}; |
| 30 | 30 |
| 31 addon:Debug(#queuedMoves .. " moves were queued."); | |
| 32 | |
| 31 for _, singleMove in pairs(queuedMoves) do | 33 for _, singleMove in pairs(queuedMoves) do |
| 32 local sourceItem = sourceContents[singleMove.id]; | 34 local sourceItem = sourceContents[singleMove.id]; |
| 33 if not sourceItem then | 35 if not sourceItem then |
| 34 print("Can't move " .. IdToItemLink(singleMove.id) .. ", non-existant in source"); | 36 print("Can't move " .. IdToItemLink(singleMove.id) .. ", non-existant in source"); |
| 35 else | 37 else |
| 36 -- We want to move the smallest stacks first to keep stuff pretty | 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) |
| 37 table.sort(sourceItem.locations, function(a, b) | 39 table.sort(sourceItem.locations, function(a, b) |
| 38 return a.count < b.count; | 40 return a.count < b.count; |
| 39 end); | 41 end); |
| 40 | 42 |
| 41 for _, itemLocation in pairs(sourceItem.locations) do | 43 for _, itemLocation in pairs(sourceItem.locations) do |
| 42 -- if this location has more items than we need, only move what we need, otherwise move everything in this stack | 44 -- if this location has more items than we need, only move what we need, otherwise move everything in this stack |
| 43 local movingNum = ((itemLocation.count > singleMove.num and singleMove.num) or itemLocation.count); | 45 local movingNum = ((itemLocation.count > singleMove.num and singleMove.num) or itemLocation.count); |
| 44 | 46 |
| 45 table.insert(outgoingMoves, { | 47 table.insert(outgoingMoves, { |
| 46 itemId = singleMove.id, | 48 itemId = singleMove.id, |
| 49 num = movingNum, | |
| 47 container = itemLocation.container, | 50 container = itemLocation.container, |
| 48 slot = itemLocation.slot, | 51 slot = itemLocation.slot, |
| 49 count = movingNum, | |
| 50 }); | 52 }); |
| 51 | 53 |
| 52 singleMove.num = (singleMove.num - movingNum); | 54 singleMove.num = (singleMove.num - movingNum); |
| 53 | 55 |
| 54 if singleMove.num == 0 then | 56 if singleMove.num == 0 then |
| 56 break; -- stop the locations-loop | 58 break; -- stop the locations-loop |
| 57 end | 59 end |
| 58 end | 60 end |
| 59 end | 61 end |
| 60 end | 62 end |
| 63 | |
| 64 addon:Debug(#outgoingMoves .. " outgoing moves are possible."); | |
| 61 | 65 |
| 62 -- No longer needed | 66 -- No longer needed |
| 63 table.wipe(queuedMoves); | 67 table.wipe(queuedMoves); |
| 64 | 68 |
| 65 -- Process every single outgoing move and find fitting targets | 69 -- Process every single outgoing move and find fitting targets |
| 76 | 80 |
| 77 -- Go through all our bags, including the backpack | 81 -- Go through all our bags, including the backpack |
| 78 for bagId = start, stop do | 82 for bagId = start, stop do |
| 79 -- Go through all our slots | 83 -- Go through all our slots |
| 80 for slotId = 1, GetContainerNumSlots(bagId) do | 84 for slotId = 1, GetContainerNumSlots(bagId) do |
| 81 local itemId = GetContainerItemID(bagId, slotId); | 85 local itemId = GetContainerItemID(bagId, slotId); -- we're scanning our local bags here, so no need to get messy with guild bank support |
| 82 | 86 |
| 83 if not itemId then | 87 if not itemId then |
| 84 table.insert(emptySlots, { | 88 table.insert(emptySlots, { |
| 85 container = bagId, | 89 container = bagId, |
| 86 slot = slotId, | 90 slot = slotId, |
| 87 }); | 91 }); |
| 88 end | 92 end |
| 89 end | 93 end |
| 90 end | 94 end |
| 95 | |
| 96 addon:Debug(#emptySlots .. " empty slots are available."); | |
| 91 | 97 |
| 92 -- Remember where we're moving from | 98 -- Remember where we're moving from |
| 93 movesSource = location; | 99 movesSource = location; |
| 94 | 100 |
| 95 while #outgoingMoves ~= 0 do | 101 while #outgoingMoves ~= 0 do |
| 108 local firstAvailableSlot = emptySlots[1]; | 114 local firstAvailableSlot = emptySlots[1]; |
| 109 | 115 |
| 110 if not firstAvailableSlot then | 116 if not firstAvailableSlot then |
| 111 print("Bags are full. Skipping " .. IdToItemLink(outgoingMove.itemId) .. "."); | 117 print("Bags are full. Skipping " .. IdToItemLink(outgoingMove.itemId) .. "."); |
| 112 | 118 |
| 113 outgoingMove.itemId = nil; | 119 outgoingMove.itemId = nil; -- remove this record from the outgoingMoves-table |
| 114 else | 120 else |
| 115 table.insert(combinedMoves, { | 121 table.insert(combinedMoves, { |
| 122 itemId = outgoingMove.itemId, | |
| 123 num = outgoingMove.num, | |
| 116 sourceContainer = outgoingMove.container, | 124 sourceContainer = outgoingMove.container, |
| 117 sourceSlot = outgoingMove.slot, | 125 sourceSlot = outgoingMove.slot, |
| 118 targetContainer = firstAvailableSlot.container, | 126 targetContainer = firstAvailableSlot.container, |
| 119 targetSlot = firstAvailableSlot.slot, | 127 targetSlot = firstAvailableSlot.slot, |
| 120 itemId = outgoingMove.itemId, | |
| 121 num = outgoingMove.count, | |
| 122 }); | 128 }); |
| 123 | 129 |
| 124 -- We filled an empty slot so the target contents now has one more item, | 130 -- We filled an empty slot so the target contents now has one more item, |
| 125 -- make a new instance of the ItemMove class so any additional items with this id can be stacked on top of it | 131 -- make a new instance of the ItemMove class so any additional items with this id can be stacked on top of it |
| 126 local itemMove = addon.ContainerItem:New(); | 132 local itemMove = addon.ContainerItem:New(); |
| 127 itemMove:AddLocation(firstAvailableSlot.container, firstAvailableSlot.slot, outgoingMove.count); | 133 itemMove:AddLocation(firstAvailableSlot.container, firstAvailableSlot.slot, outgoingMove.num); |
| 128 targetContents[outgoingMove.itemId] = itemMove; | 134 targetContents[outgoingMove.itemId] = itemMove; |
| 129 | 135 |
| 130 table.remove(emptySlots, 1); -- no longer empty | 136 table.remove(emptySlots, 1); -- no longer empty |
| 131 | 137 |
| 132 outgoingMove.count = 0; -- nothing remaining - sanity check | 138 outgoingMove.num = 0; -- nothing remaining - sanity check |
| 133 outgoingMove.itemId = nil; -- remove this record from the outgoingMoves-table | 139 outgoingMove.itemId = nil; -- remove this record from the outgoingMoves-table |
| 134 end | 140 end |
| 135 else | 141 else |
| 136 -- Find the maximum stack size for this item | 142 -- Find the maximum stack size for this item |
| 137 local itemStackCount = select(8, GetItemInfo(outgoingMove.itemId)); | 143 local itemStackCount = select(8, GetItemInfo(outgoingMove.itemId)); |
| 140 table.sort(targetItem.locations, function(a, b) | 146 table.sort(targetItem.locations, function(a, b) |
| 141 return a.count > b.count; | 147 return a.count > b.count; |
| 142 end); | 148 end); |
| 143 | 149 |
| 144 for _, itemLocation in pairs(targetItem.locations) do | 150 for _, itemLocation in pairs(targetItem.locations) do |
| 145 if itemLocation.count < itemStackCount and outgoingMove.count > 0 then | 151 if itemLocation.count < itemStackCount and outgoingMove.num > 0 then |
| 146 -- Check if this stack isn't already full (and we still need to move this item) | 152 -- Check if this stack isn't already full (and we still need to move this item) |
| 147 | 153 |
| 148 local remainingSpace = (itemStackCount - itemLocation.count); | 154 local remainingSpace = (itemStackCount - itemLocation.count); |
| 149 if remainingSpace > outgoingMove.count then | 155 if remainingSpace > outgoingMove.num then |
| 150 -- Enough room to move this entire stack | 156 -- Enough room to move this entire stack |
| 151 -- Deposit this item and then forget this outgoing move as everything in it was processed | 157 -- Deposit this item and then forget this outgoing move as everything in it was processed |
| 152 | 158 |
| 153 table.insert(combinedMoves, { | 159 table.insert(combinedMoves, { |
| 160 itemId = outgoingMove.itemId, | |
| 161 num = outgoingMove.num, | |
| 154 sourceContainer = outgoingMove.container, | 162 sourceContainer = outgoingMove.container, |
| 155 sourceSlot = outgoingMove.slot, | 163 sourceSlot = outgoingMove.slot, |
| 156 targetContainer = itemLocation.container, | 164 targetContainer = itemLocation.container, |
| 157 targetSlot = itemLocation.slot, | 165 targetSlot = itemLocation.slot, |
| 158 itemId = outgoingMove.itemId, | |
| 159 num = outgoingMove.count, | |
| 160 }); | 166 }); |
| 161 | 167 |
| 162 itemLocation.count = (itemLocation.count + outgoingMove.count); | 168 itemLocation.count = (itemLocation.count + outgoingMove.num); |
| 163 outgoingMove.count = 0; -- nothing remaining | 169 outgoingMove.count = 0; -- nothing remaining |
| 164 outgoingMove.itemId = nil; -- remove this record from the outgoingMoves-table | 170 outgoingMove.itemId = nil; -- remove this record from the outgoingMoves-table |
| 165 break; -- stop the locations-loop | 171 break; -- stop the locations-loop |
| 166 else | 172 else |
| 167 -- Deposit this item but don't remove the outgoing move as there are some items left to move | 173 -- Deposit this item but don't remove the outgoing move as there are some items left to move |
| 168 | 174 |
| 169 table.insert(combinedMoves, { | 175 table.insert(combinedMoves, { |
| 176 itemId = outgoingMove.itemId, | |
| 177 num = outgoingMove.num, | |
| 170 sourceContainer = outgoingMove.container, | 178 sourceContainer = outgoingMove.container, |
| 171 sourceSlot = outgoingMove.slot, | 179 sourceSlot = outgoingMove.slot, |
| 172 targetContainer = itemLocation.container, | 180 targetContainer = itemLocation.container, |
| 173 targetSlot = itemLocation.slot, | 181 targetSlot = itemLocation.slot, |
| 174 itemId = outgoingMove.itemId, | |
| 175 num = outgoingMove.count, | |
| 176 }); | 182 }); |
| 177 | 183 |
| 178 -- The target will be full when we complete, but the source will still have remaining items left to be moved | 184 -- The target will be full when we complete, but the source will still have remaining items left to be moved |
| 179 itemLocation.count = itemStackCount; | 185 itemLocation.count = itemStackCount; |
| 180 outgoingMove.count = (outgoingMove.count - remainingSpace); | 186 outgoingMove.num = (outgoingMove.num - remainingSpace); |
| 181 end | 187 end |
| 182 end | 188 end |
| 183 end | 189 end |
| 184 | 190 |
| 185 if outgoingMove.count > 0 then | 191 if outgoingMove.num > 0 then |
| 186 -- We went through all matching items and checked their stack sizes if we could move this there, no room available | 192 -- We went through all matching items and checked their stack sizes if we could move this there, no room available |
| 187 -- 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 | 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 |
| 188 targetItem = nil; | 194 targetItem = nil; |
| 189 end | 195 end |
| 190 end | 196 end |
| 204 | 210 |
| 205 -- Proceed with the next element (or previous considering we're going from last to first) | 211 -- Proceed with the next element (or previous considering we're going from last to first) |
| 206 numOutgoingMoves = (numOutgoingMoves - 1); | 212 numOutgoingMoves = (numOutgoingMoves - 1); |
| 207 end | 213 end |
| 208 end | 214 end |
| 215 | |
| 216 addon:Debug(#combinedMoves .. " moves should be possible."); | |
| 209 | 217 |
| 210 -- No longer needed | 218 -- No longer needed |
| 211 table.wipe(emptySlots); | 219 table.wipe(emptySlots); |
| 212 | 220 |
| 213 self:ProcessMove(); | 221 self:ProcessMove(); |
| 214 | 222 |
| 215 self:RegisterEvent("BAG_UPDATE"); | 223 -- Even though we aren't completely done yet, allow requeueing |
| 216 | |
| 217 onFinish(); | 224 onFinish(); |
| 218 end | 225 end |
| 219 | 226 |
| 220 if not table.reverse then | 227 if not table.reverse then |
| 221 -- table.reverse = function(orig) | 228 -- table.reverse = function(orig) |
| 249 self:Abort(); | 256 self:Abort(); |
| 250 | 257 |
| 251 return; | 258 return; |
| 252 end | 259 end |
| 253 | 260 |
| 261 self:RegisterEvent("BAG_UPDATE"); | |
| 254 self:RegisterEvent("UI_ERROR_MESSAGE"); | 262 self:RegisterEvent("UI_ERROR_MESSAGE"); |
| 255 | 263 |
| 256 -- combinedMoves now has all moves in it (source -> target) | 264 -- combinedMoves now has all moves in it (source -> target) |
| 257 -- go through list, move everything inside it | 265 -- go through list, move everything inside it |
| 258 -- add source and target to lists, if either is already in this list, skip the move | 266 -- add source and target to lists, if either is already in this list, skip the move |
| 260 | 268 |
| 261 local sourceLocationsLocked = {}; | 269 local sourceLocationsLocked = {}; |
| 262 local targetLocationsLocked = {}; | 270 local targetLocationsLocked = {}; |
| 263 | 271 |
| 264 -- 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 | 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 |
| 265 combinedMoves = table.reverse(combinedMoves); | 273 combinedMoves = table.reverse(combinedMoves); |
| 266 | 274 |
| 267 local numCurrentMove = #combinedMoves; | 275 local GetContainerItemID; |
| 276 if movesSource == addon.Locations.Guild then | |
| 277 GetContainerItemID = GetGuildBankItemLink; | |
| 278 else | |
| 279 GetContainerItemID = GetContainerItemID; | |
| 280 end | |
| 281 | |
| 282 local combinedMovesOriginalLength = #combinedMoves; | |
| 283 local numCurrentMove = combinedMovesOriginalLength; | |
| 268 while numCurrentMove ~= 0 do | 284 while numCurrentMove ~= 0 do |
| 269 local move = combinedMoves[numCurrentMove]; | 285 local move = combinedMoves[numCurrentMove]; |
| 270 | 286 |
| 271 -- sourceContainer, sourceSlot, targetContainer, targetSlot, itemId, num | 287 -- sourceContainer, sourceSlot, targetContainer, targetSlot, itemId, num |
| 272 if move and (not sourceLocationsLocked[move.sourceContainer] or not sourceLocationsLocked[move.sourceContainer][move.sourceSlot]) and | 288 if move and (not sourceLocationsLocked[move.sourceContainer] or not sourceLocationsLocked[move.sourceContainer][move.sourceSlot]) and |
| 276 | 292 |
| 277 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)); | 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)); |
| 278 | 294 |
| 279 if GetContainerItemID(move.sourceContainer, move.sourceSlot) ~= move.itemId then | 295 if GetContainerItemID(move.sourceContainer, move.sourceSlot) ~= move.itemId then |
| 280 self:Abort("source changed", "Source (" .. move.sourceContainer .. "," .. move.sourceSlot .. ") is not " .. IdToItemLink(move.itemId)); | 296 self:Abort("source changed", "Source (" .. move.sourceContainer .. "," .. move.sourceSlot .. ") is not " .. IdToItemLink(move.itemId)); |
| 281 | |
| 282 return; | 297 return; |
| 283 end | 298 end |
| 284 | 299 |
| 285 -- Pickup stack | 300 -- Pickup stack |
| 286 if movesSource == addon.Locations.Bank then | 301 if movesSource == addon.Locations.Bank then |
| 299 if GetContainerItemID(move.targetContainer, move.targetSlot) and GetContainerItemID(move.targetContainer, move.targetSlot) ~= move.itemId then | 314 if GetContainerItemID(move.targetContainer, move.targetSlot) and GetContainerItemID(move.targetContainer, move.targetSlot) ~= move.itemId then |
| 300 self:Abort("target changed", "Target (" .. move.targetContainer .. "," .. move.targetSlot .. ") is not " .. IdToItemLink(move.itemId) .. " nor empty"); | 315 self:Abort("target changed", "Target (" .. move.targetContainer .. "," .. move.targetSlot .. ") is not " .. IdToItemLink(move.itemId) .. " nor empty"); |
| 301 return; | 316 return; |
| 302 end | 317 end |
| 303 | 318 |
| 304 -- And drop it | 319 -- And drop it (this is always a local bag so no need to do any guild-checks) |
| 305 PickupContainerItem(move.targetContainer, move.targetSlot); | 320 PickupContainerItem(move.targetContainer, move.targetSlot); |
| 306 | 321 |
| 307 -- Remember we dropped an item here and thus this is now locked | 322 -- Remember we dropped an item here and thus this is now locked |
| 308 if not targetLocationsLocked[move.targetContainer] then | 323 if not targetLocationsLocked[move.targetContainer] then |
| 309 targetLocationsLocked[move.targetContainer] = {}; | 324 targetLocationsLocked[move.targetContainer] = {}; |
| 320 | 335 |
| 321 -- Proceed with the next element (or previous considering we're going from last to first) | 336 -- Proceed with the next element (or previous considering we're going from last to first) |
| 322 numCurrentMove = (numCurrentMove - 1); | 337 numCurrentMove = (numCurrentMove - 1); |
| 323 end | 338 end |
| 324 | 339 |
| 340 addon:Debug((combinedMovesOriginalLength - #combinedMoves) .. " moves processed. " .. #combinedMoves .. " moves remaining."); | |
| 341 | |
| 325 if #combinedMoves == 0 then | 342 if #combinedMoves == 0 then |
| 326 print("Finished."); | 343 print("Finished."); |
| 327 | 344 |
| 328 self:Abort(); | 345 self:Abort(); |
| 329 | 346 |
