vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 --[[
2 Auctioneer Addon for World of Warcraft(tm).
3 Version: 3.9.0.1000 (Kangaroo)
4 Revision: $Id: AucPostManager.lua 931 2006-07-06 07:08:15Z vindicator $
5  
6 AucPostManager - manages posting auctions in the AH
7  
8 License:
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13  
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18  
19 You should have received a copy of the GNU General Public License
20 along with this program(see GPL.txt); if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 --]]
23  
24 -------------------------------------------------------------------------------
25 -- Data Members
26 -------------------------------------------------------------------------------
27 local RequestQueue = {};
28 local ProcessingRequestQueue = false;
29  
30 -------------------------------------------------------------------------------
31 -- State machine states for a request.
32 -------------------------------------------------------------------------------
33 local READY_STATE = "Ready";
34 local COMBINING_STACK_STATE = "CombiningStacks";
35 local SPLITTING_STACK_STATE = "SplittingStack";
36 local SPLITTING_AND_COMBINING_STACK_STATE = "SplittingAndCombiningStacks";
37 local AUCTIONING_STACK_STATE = "AuctioningStack";
38  
39 -------------------------------------------------------------------------------
40 -- Function hooks that are used when processing requests
41 -------------------------------------------------------------------------------
42 local Original_PickupContainerItem;
43 local Original_SplitContainerItem;
44  
45 -------------------------------------------------------------------------------
46 -- Function Prototypes
47 -------------------------------------------------------------------------------
48 local postAuction;
49 local addRequestToQueue;
50 local removeRequestFromQueue;
51 local processRequestQueue;
52 local run;
53 local onEvent;
54 local setState;
55 local findEmptySlot;
56 local findStackBySignature;
57 local getContainerItemName;
58 local getContainerItemSignature;
59 local clearAuctionItem;
60 local findAuctionItem;
61 local getItemQuantityBySignature;
62 local createItemSignature;
63 local breakItemSignature;
64 local printBag;
65  
66 -------------------------------------------------------------------------------
67 -------------------------------------------------------------------------------
68 function AucPostManagerFrame_OnLoad()
69 this:RegisterEvent("AUCTION_HOUSE_CLOSED");
70 end
71  
72 -------------------------------------------------------------------------------
73 -------------------------------------------------------------------------------
74 function AucPostManagerFrame_OnEvent(event)
75 -- Toss all the pending requests when the AH closes.
76 if (event == "AUCTION_HOUSE_CLOSED") then
77 while (table.getn(RequestQueue) > 0) do
78 removeRequestFromQueue();
79 end
80  
81 -- Hand off the event to the current request
82 elseif (table.getn(RequestQueue) > 0) then
83 local request = RequestQueue[1];
84 if (request.state ~= READY_STATE) then
85 onEvent(request, event);
86 processRequestQueue();
87 end
88 end
89 end
90  
91 -------------------------------------------------------------------------------
92 -------------------------------------------------------------------------------
93 function AucPostManager_PickupContainerItem(bag, slot)
94 -- Intentionally empty; don't allow items to be picked up while posting
95 -- auctions.
96 debugPrint("Prevented call to PickupContainerItem()");
97 end
98  
99 -------------------------------------------------------------------------------
100 -------------------------------------------------------------------------------
101 function AucPostManager_SplitContainerItem(bag, slot, count)
102 -- Intentionally empty; don't allow items to be picked up while posting
103 -- auctions.
104 debugPrint("Prevented call to SplitContainerItem()");
105 end
106  
107 -------------------------------------------------------------------------------
108 -- Wrapper around PickupContainerItem() that always calls the Blizzard version.
109 -------------------------------------------------------------------------------
110 function pickupContainerItem(bag, slot)
111 if (Original_PickupContainerItem) then
112 return Original_PickupContainerItem(bag, slot);
113 end
114 return PickupContainerItem(bag, slot);
115 end
116  
117 -------------------------------------------------------------------------------
118 -- Wrapper around SplitContainerItem() that always calls the Blizzard version.
119 -------------------------------------------------------------------------------
120 function splitContainerItem(bag, slot, count)
121 if (Original_SplitContainerItem) then
122 return Original_SplitContainerItem(bag, slot, count);
123 end
124 return SplitContainerItem(bag, slot, count);
125 end
126  
127 -------------------------------------------------------------------------------
128 -- Start an auction.
129 -------------------------------------------------------------------------------
130 function postAuction(itemSignature, stackSize, stackCount, bid, buyout, duration, callbackFunc, callbackParam)
131 -- Problems can occur if the Auctions tab hasn't been shown at least once.
132 if (not AuctionFrameAuctions:IsVisible()) then
133 AuctionFrameAuctions:Show();
134 AuctionFrameAuctions:Hide();
135 end
136  
137 -- Get the item id and name
138 local itemId = breakItemSignature(itemSignature);
139 local itemName = GetItemInfo(itemId);
140  
141 -- Add the request to the queue.
142 local request = {};
143 request.itemSignature = itemSignature;
144 request.name = itemName;
145 request.stackSize = stackSize;
146 request.stackCount = stackCount;
147 request.bid = bid;
148 request.buyout = buyout;
149 request.duration = duration;
150 request.callback = { func = callbackFunc, param = callbackParam };
151 addRequestToQueue(request);
152 processRequestQueue();
153 end
154  
155 -------------------------------------------------------------------------------
156 -- Adds a request to the queue.
157 -------------------------------------------------------------------------------
158 function addRequestToQueue(request)
159 request.state = READY_STATE;
160 request.stackPostCount = 0;
161 request.lockEventsInCurrentState = 0;
162 request.stack = nil;
163 table.insert(RequestQueue, request);
164 end
165  
166 -------------------------------------------------------------------------------
167 -- Removes a request at the head of the queue.
168 -------------------------------------------------------------------------------
169 function removeRequestFromQueue()
170 if (table.getn(RequestQueue) > 0) then
171 local request = RequestQueue[1];
172  
173 -- Make absolutely sure we are back in the READY_STATE so that we
174 -- correctly unregister for events.
175 setState(request, READY_STATE);
176  
177 -- Perform the callback
178 local callback = request.callback;
179 if (callback and callback.func) then
180 callback.func(callback.param, request);
181 end
182  
183 -- Report the auctions posted
184 if (request.stackPostCount == 1) then
185 local output = string.format(_AUCT('FrmtPostedAuction'), request.name, request.stackSize);
186 chatPrint(output);
187 else
188 local output = string.format(_AUCT('FrmtPostedAuctions'), request.stackPostCount, request.name, request.stackSize);
189 chatPrint(output);
190 end
191 table.remove(RequestQueue, 1);
192  
193 -- If this was the last request, end processing the queue.
194 if (table.getn(RequestQueue) == 0) then
195 endProcessingRequestQueue()
196 end
197 end
198 end
199  
200 -------------------------------------------------------------------------------
201 -- Executes the request at the head of the queue.
202 -------------------------------------------------------------------------------
203 function processRequestQueue()
204 if (beginProcessingRequestQueue()) then
205 run(RequestQueue[1]);
206 end
207 end
208  
209 -------------------------------------------------------------------------------
210 -- Starts processing the request queue if possible. Returns true if started.
211 -------------------------------------------------------------------------------
212 function beginProcessingRequestQueue()
213 if (not ProcessingRequestQueue and
214 AuctionFrame and AuctionFrame:IsVisible() and
215 table.getn(RequestQueue) > 0) then
216  
217 ProcessingRequestQueue = true;
218 debugPrint("Begin processing the post queue");
219  
220 -- Hook the functions to disable picking up items. This prevents
221 -- spurious ITEM_LOCK_CHANGED events from confusing us.
222 if (not Original_PickupContainerItem) then
223 Original_PickupContainerItem = PickupContainerItem;
224 PickupContainerItem = AucPostManager_PickupContainerItem;
225 end
226 if (not Original_SplitContainerItem) then
227 Original_SplitContainerItem = SplitContainerItem;
228 SplitContainerItem = AucPostManager_SplitContainerItem;
229 end
230 end
231 return ProcessingRequestQueue;
232 end
233  
234 -------------------------------------------------------------------------------
235 -- Ends processing the request queue
236 -------------------------------------------------------------------------------
237 function endProcessingRequestQueue()
238 if (ProcessingRequestQueue) then
239 -- Unhook the functions.
240 if (Original_PickupContainerItem) then
241 PickupContainerItem = Original_PickupContainerItem;
242 Original_PickupContainerItem = nil;
243 end
244 if (Original_SplitContainerItem) then
245 SplitContainerItem = Original_SplitContainerItem;
246 Original_SplitContainerItem = nil;
247 end
248  
249 debugPrint("End processing the post queue");
250 ProcessingRequestQueue = false;
251 end
252 end
253  
254 -------------------------------------------------------------------------------
255 -- Performs the next step in fulfilling the request.
256 -------------------------------------------------------------------------------
257 function run(request)
258 if (request.state == READY_STATE) then
259 -- Locate a stack of the items. If the request has a stack associated
260 -- with it, that's a hint to try and use it. Otherwise we'll search
261 -- for a stack of the exact size. Failing that, we'll start with the
262 -- first stack we find.
263 local stack1 = nil;
264 if (request.stack and request.itemSignature == getContainerItemSignature(request.stack.bag, request.stack.slot)) then
265 -- Use the stack hint.
266 stack1 = request.stack;
267 else
268 -- Find the first stack.
269 stack1 = findStackBySignature(request.itemSignature);
270  
271 -- Now look for a stack of the exact size to use instead.
272 if (stack1) then
273 local stack2 = { bag = stack1.bag, slot = stack1.slot };
274 local _, stack2Size = GetContainerItemInfo(stack2.bag, stack2.slot);
275 while (stack2 and stack2Size ~= request.stackSize) do
276 stack2 = findStackBySignature(request.itemSignature, stack2.bag, stack2.slot + 1);
277 if (stack2) then
278 _, stack2Size = GetContainerItemInfo(stack2.bag, stack2.slot);
279 end
280 end
281 if (stack2) then
282 stack1 = stack2;
283 end
284 end
285 end
286  
287 -- If we have found a stack, figure out what we should do with it.
288 if (stack1) then
289 local _, stack1Size = GetContainerItemInfo(stack1.bag, stack1.slot);
290 if (stack1Size == request.stackSize) then
291 -- We've done it! Now move the stack to the auction house.
292 request.stack = stack1;
293 setState(request, AUCTIONING_STACK_STATE);
294 pickupContainerItem(stack1.bag, stack1.slot);
295 ClickAuctionSellItemButton();
296  
297 -- Start the auction if requested.
298 if (request.bid and request.buyout and request.duration) then
299 StartAuction(request.bid, request.buyout, request.duration);
300 else
301 removeRequestFromQueue();
302 end
303 elseif (stack1Size < request.stackSize) then
304 -- The stack we have is less than needed. Locate more of the item.
305 local stack2 = findStackBySignature(request.itemSignature, stack1.bag, stack1.slot + 1);
306 if (stack2) then
307 local _, stack2Size = GetContainerItemInfo(stack2.bag, stack2.slot);
308 if (stack1Size + stack2Size <= request.stackSize) then
309 -- Combine all of stack2 with stack1.
310 setState(request, COMBINING_STACK_STATE);
311 pickupContainerItem(stack2.bag, stack2.slot);
312 pickupContainerItem(stack1.bag, stack1.slot);
313 request.stack = stack1;
314 else
315 -- Combine part of stack2 with stack1.
316 setState(request, SPLITTING_AND_COMBINING_STACK_STATE);
317 splitContainerItem(stack2.bag, stack2.slot, request.stackSize - stack1Size);
318 pickupContainerItem(stack1.bag, stack1.slot);
319 request.stack = stack1;
320 end
321 else
322 -- Not enough of the item found!
323 chatPrint(_AUCT('FrmtNoEmptyPackSpace'));
324 removeRequestFromQueue();
325 end
326 else
327 -- The stack we have is more than needed. Locate an empty slot.
328 local stack2 = findEmptySlot();
329 if (stack2) then
330 setState(request, SPLITTING_STACK_STATE);
331 splitContainerItem(stack1.bag, stack1.slot, request.stackSize);
332 pickupContainerItem(stack2.bag, stack2.slot);
333 request.stack = stack2;
334 else
335 -- Not enough of the item!
336 local output = string.format(_AUCT('FrmtNotEnoughOfItem'), request.name);
337 chatPrint(output);
338 removeRequestFromQueue();
339 end
340 end
341 else
342 -- Item not found!
343 local output = string.format(_AUCT('FrmtNotEnoughOfItem'), request.name);
344 chatPrint(output);
345 removeRequestFromQueue();
346 end
347 end
348 end
349  
350 -------------------------------------------------------------------------------
351 -- Processes the event.
352 -------------------------------------------------------------------------------
353 function onEvent(request, event)
354 debugPrint("Received event "..event.. " in state "..request.state);
355  
356 -- Process the event.
357 if (event == "ITEM_LOCK_CHANGED") then
358 -- Check if we are waiting for a stack to be complete.
359 request.lockEventsInCurrentState = request.lockEventsInCurrentState + 1;
360 if (request.lockEventsInCurrentState == 4 and
361 (request.state == SPLITTING_STACK_STATE)) then
362 setState(request, READY_STATE);
363 elseif (request.lockEventsInCurrentState == 3 and
364 (request.state == COMBINING_STACK_STATE or
365 request.state == SPLITTING_AND_COMBINING_STACK_STATE)) then
366 -- Ready to move onto the next step.
367 setState(request, READY_STATE);
368 end
369 elseif (event == "BAG_UPDATE") then
370 -- Check if we are waiting for StartAuction() to complete. If so, check
371 -- if the stack we are trying to auction is now gone.
372 if (request.state == AUCTIONING_STACK_STATE and GetContainerItemInfo(request.stack.bag, request.stack.slot) == nil) then
373 -- Ready to move onto the next step.
374 setState(request, READY_STATE);
375  
376 -- Decrement the auction target count.
377 request.stackPostCount = request.stackPostCount + 1;
378 if (request.stackPostCount == request.stackCount) then
379 removeRequestFromQueue();
380 end
381 end
382 end
383 end
384  
385 -------------------------------------------------------------------------------
386 -- Changes the request state.
387 -------------------------------------------------------------------------------
388 function setState(request, newState)
389 if (request.state ~= newState) then
390 debugPrint("Entered state: "..newState);
391  
392 -- Unregister for events needed in the old state.
393 if (request.state == SPLITTING_STACK_STATE or
394 request.state == COMBINING_STACK_STATE or
395 request.state == SPLITTING_AND_COMBINING_STACK_STATE) then
396 debugPrint("Unregistering for ITEM_LOCK_CHANGED");
397 AucPostManagerFrame:UnregisterEvent("ITEM_LOCK_CHANGED");
398 elseif (request.state == AUCTIONING_STACK_STATE) then
399 debugPrint("Unregistering for BAG_UPDATE");
400 AucPostManagerFrame:UnregisterEvent("BAG_UPDATE");
401 end
402  
403 -- Update the request's state.
404 request.state = newState;
405 request.lockEventsInCurrentState = 0;
406  
407 -- Register for events needed in the new state.
408 if (request.state == SPLITTING_STACK_STATE or
409 request.state == COMBINING_STACK_STATE or
410 request.state == SPLITTING_AND_COMBINING_STACK_STATE) then
411 debugPrint("Registering for ITEM_LOCK_CHANGED");
412 AucPostManagerFrame:RegisterEvent("ITEM_LOCK_CHANGED");
413 elseif (request.state == AUCTIONING_STACK_STATE) then
414 debugPrint("Registering for BAG_UPDATE");
415 AucPostManagerFrame:RegisterEvent("BAG_UPDATE");
416 end
417 end
418 end
419  
420 -------------------------------------------------------------------------------
421 -- Finds an empty slot in the player's containers.
422 --
423 -- TODO: Correctly handle containers like ammo packs
424 -------------------------------------------------------------------------------
425 function findEmptySlot()
426 for bag = 0, 4, 1 do
427 if (GetBagName(bag)) then
428 for item = GetContainerNumSlots(bag), 1, -1 do
429 if (not GetContainerItemInfo(bag, item)) then
430 return { bag=bag, slot=item };
431 end
432 end
433 end
434 end
435 return nil;
436 end
437  
438 -------------------------------------------------------------------------------
439 -- Finds the specified item by id
440 --
441 -- TODO: Correctly handle containers like ammo packs
442 -------------------------------------------------------------------------------
443 function findStackBySignature(itemSignature, startingBag, startingSlot)
444 if (startingBag == nil) then
445 startingBag = 0;
446 end
447 if (startingSlot == nil) then
448 startingSlot = 1;
449 end
450 for bag = startingBag, 4, 1 do
451 if (GetBagName(bag)) then
452 local numItems = GetContainerNumSlots(bag);
453 if (startingSlot <= numItems) then
454 for slot = startingSlot, GetContainerNumSlots(bag), 1 do
455 local thisItemSignature = getContainerItemSignature(bag, slot);
456 if (itemSignature == thisItemSignature) then
457 return { bag=bag, slot=slot };
458 end
459 end
460 end
461 startingSlot = 1;
462 end
463 end
464 return nil;
465 end
466  
467  
468 -------------------------------------------------------------------------------
469 -- Gets the name of the specified
470 -------------------------------------------------------------------------------
471 function getContainerItemName(bag, slot)
472 local link = GetContainerItemLink(bag, slot);
473 if (link) then
474 local _, _, _, _, name = EnhTooltip.BreakLink(link);
475 return name;
476 end
477 end
478  
479 -------------------------------------------------------------------------------
480 -- Gets the signature of the specified item (itemId:suffixId:enchantId)
481 -------------------------------------------------------------------------------
482 function getContainerItemSignature(bag, slot)
483 local link = GetContainerItemLink(bag, slot);
484 if (link) then
485 local itemId, suffixId, enchantId = EnhTooltip.BreakLink(link);
486 return createItemSignature(itemId, suffixId, enchantId);
487 end
488 end
489  
490 -------------------------------------------------------------------------------
491 -- Clears the current auction item, if any.
492 -------------------------------------------------------------------------------
493 function clearAuctionItem()
494 local bag, item = findAuctionItem();
495 if (bag and item) then
496 ClickAuctionSellItemButton();
497 pickupContainerItem(bag, item);
498 end
499 end
500  
501 -------------------------------------------------------------------------------
502 -- Finds the bag and slot for the current auction item.
503 --
504 -- TODO: Correctly handle containers like ammo packs
505 -------------------------------------------------------------------------------
506 function findAuctionItem()
507 local auctionName, _, auctionCount = GetAuctionSellItemInfo();
508 --debugPrint("Searching for "..auctionName.." in a stack of "..auctionCount);
509 if (auctionName and auctionCount) then
510 for bag = 0, 4, 1 do
511 if (GetBagName(bag)) then
512 for item = GetContainerNumSlots(bag), 1, -1 do
513 --debugPrint("Checking "..bag..", "..item);
514 local _, itemCount, itemLocked = GetContainerItemInfo(bag, item);
515 if (itemLocked and itemCount == auctionCount) then
516 local itemName = getContainerItemName(bag, item);
517 --debugPrint("Item "..itemName.." locked");
518 if (itemName == auctionName) then
519 return bag, item;
520 end
521 end
522 end
523 end
524 end
525 end
526 end
527  
528 -------------------------------------------------------------------------------
529 -- Creates an item signature (itemId:suffixId:enchantId)
530 -------------------------------------------------------------------------------
531 function createItemSignature(itemId, suffixId, enchantId)
532 return itemId..":"..suffixId..":"..enchantId;
533 end
534  
535 -------------------------------------------------------------------------------
536 -- Breaks an item signature (itemId:suffixId:enchantId)
537 -------------------------------------------------------------------------------
538 function breakItemSignature(itemSignature)
539 _, _, itemId, suffixId, enchantId = string.find(itemSignature, "(.+):(.+):(.+)");
540 itemId = tonumber(itemId);
541 suffixId = tonumber(suffixId);
542 enchantId = tonumber(enchantId);
543 return itemId, suffixId, enchantId;
544 end
545  
546 -------------------------------------------------------------------------------
547 -- Gets the quanity of the specified item
548 --
549 -- TODO: Correctly handle containers like ammo packs
550 -------------------------------------------------------------------------------
551 function getItemQuantityBySignature(itemSignature)
552 local quantity = 0;
553 for bag = 0, 4, 1 do
554 if (GetBagName(bag)) then
555 for item = GetContainerNumSlots(bag), 1, -1 do
556 local thisItemSignature = getContainerItemSignature(bag, item);
557 if (itemSignature == thisItemSignature) then
558 local _, itemCount = GetContainerItemInfo(bag, item);
559 quantity = quantity + itemCount;
560 end
561 end
562 end
563 end
564 return quantity;
565 end
566  
567 -------------------------------------------------------------------------------
568 -------------------------------------------------------------------------------
569 function nilSafe(string)
570 if (string) then
571 return string;
572 end
573 return "<nil>";
574 end
575  
576 -------------------------------------------------------------------------------
577 -------------------------------------------------------------------------------
578 chatPrint = Auctioneer.Util.ChatPrint;
579  
580 -------------------------------------------------------------------------------
581 -------------------------------------------------------------------------------
582 debugPrint = EnhTooltip.DebugPrint;
583  
584 -------------------------------------------------------------------------------
585 -- Public API
586 -------------------------------------------------------------------------------
587 AucPostManager =
588 {
589 -- Exported functions
590 PostAuction = postAuction;
591 CreateItemSignature = createItemSignature;
592 BreakItemSignature = breakItemSignature;
593 GetItemQuantityBySignature = getItemQuantityBySignature;
594 };