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 |