--[[ Auctioneer Addon for World of Warcraft(tm). Version: 3.9.0.1000 (Kangaroo) Revision: $Id: AucBidManager.lua 927 2006-07-06 02:15:28Z mentalpower $ BidManager - manages bid requests in the AH License: This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program(see GPL.txt); if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --]] ------------------------------------------------------------------------------- -- Data Members ------------------------------------------------------------------------------- -- Queue of bid requests to be worked on local LastRequest; local RequestQueue = {}; local ProcessingRequestQueue = false; -- Bid queue actions local BID_AUCTION = "bid"; local BUYOUT_AUCTION = "buyout"; -- Parameters of QueryAuctionItems() last time it was called. local CurrentSearchParams = { name = nil; minLevel = nil; maxLevel = nil; invTypeIndex = nil; classIndex = nil; subclassIndex = nil; page = nil; isUsable = nil; qualityIndex = nil; queryTime = nil; -- Time that the query was made or the last response was received queryResponse = true; -- Flag indicates if any response has been received queryComplete = true; -- Flag indicates if the entire response has been received targetCountForPage = nil; -- Number of items expected on the page (nil for unknown) }; -- Queue of bids submitted to the server, but not yet accepted or rejected local PendingBids = {}; -- Function hooks that are used when processing requests local Original_CanSendAuctionQuery; local Original_AuctionFrameBrowse_OnEvent; local Original_AuctionFrameBrowse_Update; -- Result codes for bid requests. BidResultCodes = {} BidResultCodes["BidAccepted"] = 0; BidResultCodes["ItemNotFound"] = 1; BidResultCodes["NotEnoughMoney"] = 2; BidResultCodes["OwnAuction"] = 3; BidResultCodes["AlreadyHigherBid"] = 4; BidResultCodes["AlreadyHighBidder"] = 5; BidResultCodes["CurrentBidLower"] = 6; BidResultCodes["MaxBidsReached"] = 7; ------------------------------------------------------------------------------- -- Function Prototypes ------------------------------------------------------------------------------- local addPlayerToAccount; local isPlayerOnAccount; local isBidInProgress; local addPendingBid; local removePendingBid; local placeAuctionBidHook; local onBidResponse; local isQueryInProgress; local queryAuctionItemsHook; local checkQueryComplete; local addRequestToQueue; local removeRequestFromQueue; local isProcessingRequest; local getRequestCount; local beginProcessingRequestQueue; local endProcessingRequestQueue; local processRequestQueue; local processPage; local nilSafe; local boolString; local chatPrint; local debugPrint; local bidAuction; local dumpState; ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- function AucBidManagerFrame_OnLoad() this:RegisterEvent("ADDON_LOADED"); this:RegisterEvent("AUCTION_ITEM_LIST_UPDATE"); this:RegisterEvent("AUCTION_HOUSE_CLOSED"); Stubby.RegisterFunctionHook("PlaceAuctionBid", -50, placeAuctionBidHook) Stubby.RegisterFunctionHook("QueryAuctionItems", -50, queryAuctionItemsHook) end ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- function AucBidManagerFrame_OnEvent(event) if (event == "ADDON_LOADED" and string.lower(arg1) == "auctioneer") then addPlayerToAccount(UnitName("player")); this:UnregisterEvent("ADDON_LOADED"); elseif (event == "AUCTION_ITEM_LIST_UPDATE") then debugPrint(event); checkQueryComplete(); elseif (event == "CHAT_MSG_SYSTEM" and arg1) then debugPrint(event); if (arg1) then debugPrint(" "..arg1) end; if (arg1 == ERR_AUCTION_BID_PLACED) then onBidResponse(BidResultCodes["BidAccepted"]); end elseif (event == "UI_ERROR_MESSAGE" and arg1) then debugPrint(event); if (arg1) then debugPrint(" "..arg1) end; if (arg1 == ERR_ITEM_NOT_FOUND) then onBidResponse(BidResultCodes["ItemNotFound"]); elseif (arg1 == ERR_NOT_ENOUGH_MONEY) then onBidResponse(BidResultCodes["NotEnoughMoney"]); elseif (arg1 == ERR_AUCTION_BID_OWN) then onBidResponse(BidResultCodes["OwnAuction"]); elseif (arg1 == ERR_AUCTION_HIGHER_BID) then onBidResponse(BidResultCodes["AlreadyHigherBid"]); end elseif (event == "AUCTION_HOUSE_CLOSED") then endProcessingRequestQueue(); end end ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- function AucBidManagerFrame_OnUpdate() processRequestQueue(); end ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- function AucBidManager_CanSendAuctionQuery() -- Intentionally empty; don't allow the auction UI to update while we're processing requests return false; end ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- function AucBidManager_AuctionFrameBrowse_OnEvent() -- Intentionally empty; don't allow the auction UI to update while we're processing requests end ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- function AucBidManager_AuctionFrameBrowse_Update() -- Intentionally empty; don't allow the auction UI to update while we're processing requests end ------------------------------------------------------------------------------- -- Adds a player to the list of players on the current account. ------------------------------------------------------------------------------- function addPlayerToAccount(player) -- List of players on the same account as the current player (including the -- current player). Auctions owned by these players cannot be bid on. if (not AuctionConfig.players) then AuctionConfig.players = {}; end if (not isPlayerOnAccount(player)) then table.insert(AuctionConfig.players, player); end end ------------------------------------------------------------------------------- -- Checks if a player is on the same account as the current player. ------------------------------------------------------------------------------- function isPlayerOnAccount(player) if (not AuctionConfig.players) then AuctionConfig.players = {}; end for _, p in pairs(AuctionConfig.players) do if (p == player) then return true; end end return false; end ------------------------------------------------------------------------------- -- Returns true if a bid request is in flight to the server ------------------------------------------------------------------------------- function isBidInProgress() return (table.getn(PendingBids) > 0); end ------------------------------------------------------------------------------- -- Adds a pending bid to the queue. ------------------------------------------------------------------------------- function addPendingBid(name, count, bid, owner, request) -- Add a pending bid to the queue. local pendingBid = {}; pendingBid.name = name; pendingBid.count = count; pendingBid.bid = bid; pendingBid.owner = owner; pendingBid.request = request; table.insert(PendingBids, pendingBid); debugPrint("addPendingBid() - Added pending bid"); if (request) then debugPrint("addPendingBid() - Associated request with pending bid "..table.getn(PendingBids)); end -- Register for the response events if this is the first pending bid. if (table.getn(PendingBids) == 1) then debugPrint("addPendingBid() - Registering for CHAT_MSG_SYSTEM and UI_ERROR_MESSAGE"); AucBidManagerFrame:RegisterEvent("CHAT_MSG_SYSTEM"); AucBidManagerFrame:RegisterEvent("UI_ERROR_MESSAGE"); end end ------------------------------------------------------------------------------- -- Removes the pending bid from the queue. ------------------------------------------------------------------------------- function removePendingBid() if (table.getn(PendingBids) > 0) then -- Remove the first pending bid. local bid = PendingBids[1]; table.remove(PendingBids, 1); debugPrint("removePendingBid() - Removed pending bid"); -- Unregister for the response events if this is the last pending bid. if (table.getn(PendingBids) == 0) then debugPrint("removePendingBid() - Unregistering for CHAT_MSG_SYSTEM and UI_ERROR_MESSAGE"); AucBidManagerFrame:UnregisterEvent("CHAT_MSG_SYSTEM"); AucBidManagerFrame:UnregisterEvent("UI_ERROR_MESSAGE"); end return bid; end -- No pending bid to remove! return nil; end ------------------------------------------------------------------------------- -- Called before PlaceAuctionBid() ------------------------------------------------------------------------------- function placeAuctionBidHook(_, _, listType, index, bid, request) local name, texture, count, quality, canUse, level, minBid, minIncrement, buyoutPrice, bidAmount, highBidder, owner = GetAuctionItemInfo(listType, index); if (name and count and bid) then addPendingBid(name, count, bid, owner, request); return "setparams", {listType, index, bid}; else debugPrint("PlaceAuctionBid() - Ignoring bid"); return "abort"; end end ------------------------------------------------------------------------------- -- Called whenever a response to a bid is received ------------------------------------------------------------------------------- function onBidResponse(result) if (table.getn(PendingBids) > 0) then -- We always assume the bid response is for the next bid in PendingBids. local bid = removePendingBid(); -- If there is an associated request, add our result to it. local request = bid.request; if (request) then debugPrint("Found request associated with bid"); if (result == BidResultCodes["BidAccepted"]) then table.insert(request.results, result); if (request.bid == request.buyout) then local output = string.format(_AUCT('FrmtBoughtAuction'), request.name, request.count); chatPrint(output); else local output = string.format(_AUCT('FrmtBidAuction'), request.name, request.count); chatPrint(output); end elseif (result == BidResultCodes["ItemNotFound"]) then -- nothing to do elseif (result == BidResultCodes["NotEnoughMoney"]) then table.insert(request.results, result); local output = string.format(_AUCT('FrmtNotEnoughMoney'), request.name, request.count); chatPrint(output); elseif (result == BidResultCodes["OwnAuction"]) then table.insert(request.results, result); local output = string.format(_AUCT('FrmtSkippedBiddingOnOwnAuction'), request.name, request.count); chatPrint(output); elseif (result == BidResultCodes["AlreadyHigherBid"]) then if (request.bid ~= request.buyout) then table.insert(request.results, result); local output = string.format(_AUCT('FrmtSkippedAuctionWithHigherBid'), request.name, request.count); chatPrint(output); else debugPrint("Attempted to buyout the same auction"); end end else debugPrint("Did not find request associated with bid"); end -- Process the bid result . if (result == BidResultCodes["BidAccepted"] and request) then -- Check if there is a corresponding request and upate it to -- reflect the successful bid. if (request) then request.bidCount = request.bidCount + 1; -- Increment the request's current index if the auction was not bought out. if (request.bid ~= request.buyout) then request.currentIndex = request.currentIndex + 1; debugPrint("Incrementing the request's currentIndex to "..request.currentIndex); end end elseif (result ~= BidResultCodes["BidAccepted"]) then -- We were expecting the list to update after our bid/buyout, but -- our bid/buyout failed so we won't be getting an update. CurrentSearchParams.queryComplete = true; CurrentSearchParams.queryResponse = true; CurrentSearchParams.targetCountForPage = nil; -- Skip over the auction we failed to bid on. if (request) then request.currentIndex = request.currentIndex + 1; debugPrint("Incrementing the request's currentIndex to "..request.currentIndex); end if (result == BidResultCodes["OwnAuction"] and bid.owner) then -- We tried bidding on our own auction! Blizzard doesn't -- allow bids from any player on the account that posted -- the auction. Therefore we keep a dynamic list of all -- of these failures so we can avoid these auctions in the -- future. addPlayerToAccount(bid.owner); end end else -- We got out of sync somehow... this indicates a bug in how we determine -- the results of bid requests. chatPrint(_AUCT('FrmtBidQueueOutOfSync')); end end ------------------------------------------------------------------------------- -- Returns true if a query is in progress ------------------------------------------------------------------------------- function isQueryInProgress() return (not CurrentSearchParams.queryComplete); end ------------------------------------------------------------------------------- -- Wrapper around CanSendAuctionQuery() that always calls the Blizzard version. ------------------------------------------------------------------------------- function canSendAuctionQuery() if (Original_CanSendAuctionQuery) then return Original_CanSendAuctionQuery(); end return CanSendAuctionQuery(); end ------------------------------------------------------------------------------- -- Called before QueryAuctionItems() ------------------------------------------------------------------------------- function queryAuctionItemsHook(_, _, name, minLevel, maxLevel, invTypeIndex, classIndex, subclassIndex, page, isUsable, qualityIndex) if (not Auctioneer.Scanning.IsScanningRequested and canSendAuctionQuery()) then CurrentSearchParams.name = name; CurrentSearchParams.minLevel = minLevel; CurrentSearchParams.maxLevel = maxLevel; CurrentSearchParams.invTypeIndex = invTypeIndex; CurrentSearchParams.classIndex = classIndex; CurrentSearchParams.subclassIndex = subclassIndex; CurrentSearchParams.page = page; CurrentSearchParams.isUsable = isUsable; CurrentSearchParams.qualityIndex = qualityIndex; CurrentSearchParams.queryTime = time(); CurrentSearchParams.queryResponse = false; CurrentSearchParams.queryComplete = false; CurrentSearchParams.targetCountForPage = nil; debugPrint("queryAuctionItemsHook()"); else CurrentSearchParams.name = nil; CurrentSearchParams.minLevel = nil; CurrentSearchParams.maxLevel = nil; CurrentSearchParams.invTypeIndex = nil; CurrentSearchParams.classIndex = nil; CurrentSearchParams.subclassIndex = nil; CurrentSearchParams.page = nil; CurrentSearchParams.isUsable = nil; CurrentSearchParams.qualityIndex = nil; CurrentSearchParams.queryTime = time(); CurrentSearchParams.queryResponse = true; CurrentSearchParams.queryComplete = true; CurrentSearchParams.targetCountForPage = nil; debugPrint("queryAuctionItemsHook() - ignoring"); end -- Toss the information about the last request processed. LastRequest = nil; end ------------------------------------------------------------------------------- -- Checks a query to see if its complete. Often times the owner information -- of auctions are missing at first. Asking for it triggers another update -- of the auction list. ------------------------------------------------------------------------------- function checkQueryComplete() if (not CurrentSearchParams.queryComplete) then -- Get the number of auctions on the page. local lastIndexOnPage, totalAuctions = GetNumAuctionItems("list"); debugPrint("LastIndexOnPage = "..lastIndexOnPage); debugPrint("TotalAuctions = "..totalAuctions); if (CurrentSearchParams.targetCountForPage == nil or CurrentSearchParams.targetCountForPage == lastIndexOnPage) then -- Assume true until otherwise proven false. CurrentSearchParams.queryTime = time(); CurrentSearchParams.queryResponse = true; CurrentSearchParams.queryComplete = true; for indexOnPage = 1, lastIndexOnPage do local _,_,_,_,_,_,_,_,_,_,_, owner = GetAuctionItemInfo("list", indexOnPage); if (owner == nil) then -- No dice... there are more updates coming... CurrentSearchParams.queryComplete = false; break; end end if (CurrentSearchParams.queryComplete) then debugPrint("checkQueryComplete() - true"); else debugPrint("checkQueryComplete() - false (waiting for owner information on auctions)"); end else debugPrint("checkQueryComplete() - false (waiting for "..CurrentSearchParams.targetCountForPage.." auctions on page)"); end end end ------------------------------------------------------------------------------- -- Adds a request to the queue. ------------------------------------------------------------------------------- function addRequestToQueue(request) -- Add the request state information request.bidAttempts = 0; request.bidCount = 0; request.currentPage = 0; request.currentIndex = 1; request.continuation = false; request.results = {}; -- Check if the previous request is the same as this request. If so we -- want to pickup where the last request left off. if (table.getn(RequestQueue) == 0 and LastRequest ~= nil and LastRequest.id == request.id and LastRequest.rprop == request.rprop and LastRequest.enchant == request.enchant and LastRequest.name == request.name and LastRequest.count == request.count and LastRequest.min == request.min and LastRequest.buyout == request.buyout and LastRequest.unique == request.unique and LastRequest.bid == request.bid) then request.currentPage = LastRequest.currentPage; request.currentIndex = LastRequest.currentIndex; request.continuation = true; end -- Add the request to the queue. table.insert(RequestQueue, request); debugPrint("Added request to queue: "..request.name..", "..request.count..", "..nilSafe(request.buyout)..", "..nilSafe(request.bid)); end ------------------------------------------------------------------------------- -- Removes a request at the head of the queue. ------------------------------------------------------------------------------- function removeRequestFromQueue() if (table.getn(RequestQueue) > 0) then -- Remove the request from the queue. local request = RequestQueue[1]; table.remove(RequestQueue, 1); debugPrint("Removed request from queue: "..request.name..", "..request.count..", "..nilSafe(request.buyout)..", "..nilSafe(request.bid)); -- Make the callback, if requested. local callback = request.callback; if (callback and callback.func) then callback.func(callback.param, request); end -- Inform the user if no auctions were found. if (table.getn(request.results) == 0) then if (request.continuation) then local output = string.format(_AUCT('FrmtNoMoreAuctionsFound'), request.name, request.count); chatPrint(output); else local output = string.format(_AUCT('FrmtNoAuctionsFound'), request.name, request.count); chatPrint(output); end end -- Check if the next request is the same as the previous. If so we -- want to pickup where the last request left off. if (LastRequest ~= nil and table.getn(RequestQueue) > 0) then local nextRequest = RequestQueue[1]; if (LastRequest.id == nextRequest.id and LastRequest.rprop == nextRequest.rprop and LastRequest.enchant == nextRequest.enchant and LastRequest.name == nextRequest.name and LastRequest.count == nextRequest.count and LastRequest.min == nextRequest.min and LastRequest.buyout == nextRequest.buyout and LastRequest.unique == nextRequest.unique and LastRequest.bid == nextRequest.bid) then nextRequest.currentPage = LastRequest.currentPage; nextRequest.currentIndex = LastRequest.currentIndex; nextRequest.continuation = true; end end end end ------------------------------------------------------------------------------- -- Checks if the BidManager is currently working on a request. ------------------------------------------------------------------------------- function isProcessingRequest() return ProcessingRequestQueue; end ------------------------------------------------------------------------------- -- Gets the number of pending requests. ------------------------------------------------------------------------------- function getRequestCount() return table.getn(RequestQueue); end ------------------------------------------------------------------------------- -- Starts processing the request queue if possible. Returns true if started. ------------------------------------------------------------------------------- function beginProcessingRequestQueue() if (not ProcessingRequestQueue and AuctionFrame and AuctionFrame:IsVisible() and table.getn(RequestQueue) > 0 and not Auctioneer.Scanning.IsScanningRequested and not isQueryInProgress() and not isBidInProgress()) then ProcessingRequestQueue = true; AucBidManagerFrame:Show(); debugPrint("Begin processing the bid queue"); -- Hook the functions to disable the Browse UI if (not Original_CanSendAuctionQuery) then Original_CanSendAuctionQuery = CanSendAuctionQuery; CanSendAuctionQuery = AucBidManager_CanSendAuctionQuery; end if (not Original_AuctionFrameBrowse_OnEvent) then Original_AuctionFrameBrowse_OnEvent = AuctionFrameBrowse_OnEvent; AuctionFrameBrowse_OnEvent = AucBidManager_AuctionFrameBrowse_OnEvent; end if (not Original_AuctionFrameBrowse_Update) then Original_AuctionFrameBrowse_Update = AuctionFrameBrowse_Update; AuctionFrameBrowse_Update = AucBidManager_AuctionFrameBrowse_Update; end -- Hide the UI from any current results, show the no results text so we can use it BrowseNoResultsText:Show(); BrowseNoResultsText:SetText(_AUCT('UiProcessingBidRequests')); for iButton = 1, NUM_BROWSE_TO_DISPLAY do button = getglobal("BrowseButton"..iButton); button:Hide(); end BrowsePrevPageButton:Hide(); BrowseNextPageButton:Hide(); BrowseSearchCountText:Hide(); BrowseBidButton:Disable(); BrowseBuyoutButton:Disable(); end return ProcessingRequestQueue; end ------------------------------------------------------------------------------- -- Ends processing the request queue ------------------------------------------------------------------------------- function endProcessingRequestQueue() if (ProcessingRequestQueue) then ProcessingRequestQueue = false; AucBidManagerFrame:Hide(); debugPrint("End processing the bid queue"); -- Unhook the functions if( Original_CanSendAuctionQuery ) then CanSendAuctionQuery = Original_CanSendAuctionQuery; Original_CanSendAuctionQuery = nil; end if( Original_AuctionFrameBrowse_OnEvent ) then AuctionFrameBrowse_OnEvent = Original_AuctionFrameBrowse_OnEvent; Original_AuctionFrameBrowse_OnEvent = nil; end if( Original_AuctionFrameBrowse_Update ) then AuctionFrameBrowse_Update = Original_AuctionFrameBrowse_Update; Original_AuctionFrameBrowse_Update = nil; end -- Update the Browse UI. BrowseNoResultsText:Hide(); BrowseNoResultsText:SetText(""); AuctionFrameBrowse_Update(); end end ------------------------------------------------------------------------------- -- Attempt to process the request queue. Based on the current state, this -- method performs the next action needed to process the queue. ------------------------------------------------------------------------------- function processRequestQueue() -- Check if we should toss the results of the last query. Sometimes -- Blizzard never updates all the owner information. We give it 20 -- seconds, then give up and try again. if (CurrentSearchParams.queryResponse == true and CurrentSearchParams.queryComplete == false and CurrentSearchParams.queryTime and time() - CurrentSearchParams.queryTime >= 20) then -- Discard the results of the search and try again. CurrentSearchParams.name = nil; CurrentSearchParams.minLevel = nil; CurrentSearchParams.maxLevel = nil; CurrentSearchParams.invTypeIndex = nil; CurrentSearchParams.classIndex = nil; CurrentSearchParams.subclassIndex = nil; CurrentSearchParams.page = nil; CurrentSearchParams.isUsable = nil; CurrentSearchParams.qualityIndex = nil; CurrentSearchParams.queryTime = nil; CurrentSearchParams.queryResponse = true; CurrentSearchParams.queryComplete = true; CurrentSearchParams.targetCountForPage = nil; Auctioneer.Util.ChatPrint(string.format(_AUCT('AuctionScanRedo'), 20)); end -- Process the bid queue! if (beginProcessingRequestQueue()) then while (table.getn(RequestQueue) > 0 and not isQueryInProgress() and not isBidInProgress()) do local request = RequestQueue[1]; --debugPrint("Processing bid queue: "..request.name..", "..request.count..", "..nilSafe(request.owner)..", "..nilSafe(request.bid)..", "..nilSafe(request.buyout)); --debugPrint("CurrentSearchParams: "); --debugPrint(" name: "..nilSafe(CurrentSearchParams.name)); --debugPrint(" minLevel: "..nilSafe(CurrentSearchParams.minLevel)); --debugPrint(" maxLevel: "..nilSafe(CurrentSearchParams.maxLevel)); if (CurrentSearchParams.name and CurrentSearchParams.name == request.name and CurrentSearchParams.minLevel == "" and CurrentSearchParams.maxLevel == "" and CurrentSearchParams.invTypeIndex == nil and CurrentSearchParams.classIndex == nil and CurrentSearchParams.page == request.currentPage and CurrentSearchParams.isUsable == nil and CurrentSearchParams.qualityIndex == nil) then processPage(); elseif (canSendAuctionQuery()) then QueryAuctionItems(request.name, "", "", nil, nil, nil, request.currentPage, nil, nil); else -- We gotta wait to be able to send a query. break; end end -- If we've emptied the RequestQueue, then end the processing if (table.getn(RequestQueue) == 0) then endProcessingRequestQueue(); end end end ------------------------------------------------------------------------------- -- Searches the current page for an auction matching the first request in -- the request queue. If it finds the auction, it carries out the specified -- action (bid/buyout). ------------------------------------------------------------------------------- function processPage() local request = RequestQueue[1]; -- Iterate through each item on the page, searching for a match local lastIndexOnPage, totalAuctions = GetNumAuctionItems("list"); debugPrint("Processing page "..request.currentPage.." starting at index "..request.currentIndex.." ("..lastIndexOnPage.." on page; "..totalAuctions.." in total)"); debugPrint("Searching for item: "..request.id..", "..request.rprop..", "..request.enchant..", "..request.name..", "..request.count..", "..request.min..", "..request.buyout..", "..request.unique..", "..request.bid); local foundMatchingAuction = false; for indexOnPage = request.currentIndex, lastIndexOnPage do -- Check if this item matches local name, texture, count, quality, canUse, level, minBid, minIncrement, buyoutPrice, bidAmount, highBidder, owner = GetAuctionItemInfo("list", indexOnPage); local link = GetAuctionItemLink("list", indexOnPage); local id, rprop, enchant, unique = EnhTooltip.BreakLink(link); debugPrint("Processing item "..indexOnPage..": "..id..", "..rprop..", "..enchant..", "..name..", "..count..", "..minBid..", "..buyoutPrice..", "..unique..", "..bidAmount); if ((request.id == id) and (request.rprop == rprop) and (request.enchant == enchant) and (request.name == name) and (request.count == count) and (request.min == minBid) and (request.buyout == buyoutPrice) and (request.unique == unique)) then local bid = nil; -- Check if the auction is owned by the player. if (isPlayerOnAccount(owner)) then table.insert(request.results, BidResultCodes["OwnAuction"]); local output = string.format(_AUCT('FrmtSkippedBiddingOnOwnAuction'), request.name, request.count); chatPrint(output); -- Check for a buyout request elseif (request.buyout == request.bid) then bid = request.buyout; -- Otherwise it must be a bid request else -- Check if we are already the high bidder if (highBidder) then table.insert(request.results, BidResultCodes["AlreadyHighBidder"]); local output = string.format(_AUCT('FrmtAlreadyHighBidder'), request.name, request.count); chatPrint(output); -- Check if the item has been bid on elseif (bidAmount ~= 0) then -- Check the bid matches what we are looking for if (bidAmount == request.bid) then bid = bidAmount + minIncrement; -- Check if there is already a higher bidder elseif (bidAmount > request.bid) then table.insert(request.results, BidResultCodes["AlreadyHigherBid"]); local output = string.format(_AUCT('FrmtSkippedAuctionWithHigherBid'), request.name, request.count); chatPrint(output); -- Otherwise the bid must be lower... else table.insert(request.results, BidResultCodes["CurrentBidLower"]); local output = string.format(_AUCT('FrmtSkippedAuctionWithLowerBid'), request.name, request.count); chatPrint(output); end -- Otherwise the item hasn't been bid on else -- Check the bid matches what we are looking for if (minBid == request.bid) then bid = minBid; -- Otherwise the min bid is lower... else table.insert(request.results, BidResultCodes["CurrentBidLower"]); local output = string.format(_AUCT('FrmtSkippedAuctionWithLowerBid'), request.name, request.count); chatPrint(output); end end end -- If we've settled on a bid, do it now! if (bid) then foundMatchingAuction = true; -- Check if we've reached the bid limit on this request. if (request.bidCount == request.maxBids) then -- Report that the maximum number of bids has been reached. local output = string.format(_AUCT('FrmtMaxBidsReached'), request.name, request.count, request.maxBids); chatPrint(output); debugPrint("Reached max bids!"); -- Add the bid result to the request. table.insert(request.results, BidResultCodes["MaxBidsReached"]); -- Since this request didn't fully completed due to bid -- limits, update the currentIndex and save it as the -- last request. If the next request is for the same item, -- it will pickup where this request left off. LastRequest = request; request.currentIndex = indexOnPage; removeRequestFromQueue(); else -- Successful bid/buyouts result in the query results being -- updated. To prevent additional queries from being sent -- until the list is updated, we flip the complete flag -- back to false. If the bid fails we'll manually flip -- the flag back to true again. CurrentSearchParams.queryTime = time(); CurrentSearchParams.queryResponse = false; CurrentSearchParams.queryComplete = false; if (bid == buyoutPrice) then CurrentSearchParams.targetCountForPage = lastIndexOnPage - 1; end -- Update the starting point for this page request.currentIndex = indexOnPage; request.bidAttempts = request.bidAttempts + 1; -- Place the bid! This MUST be done last since the response -- can be received during the call to PlaceAuctionBid. debugPrint("Placing bid on "..name.. " at "..bid.." (index "..indexOnPage..")"); PlaceAuctionBid("list", indexOnPage, bid, request); end break; end end end -- If an item was not found to bid on... if (not foundMatchingAuction) then -- When an item is bought out on the page, the item is not replaced -- with an item from a subsequent page. Nor is the item removed from -- the total count. Thus if there were 7 items total before the buyout, -- GetNumAuctionItems() will report 6 items on the page and but still -- 7 total after the buyout. if (lastIndexOnPage == 0 or request.currentPage * NUM_AUCTION_ITEMS_PER_PAGE + lastIndexOnPage == totalAuctions) then -- Reached the end of the line for this item, remove it from the queue request.currentIndex = lastIndexOnPage + 1; removeRequestFromQueue(); else -- Continue looking for items on the next page. request.currentPage = request.currentPage + 1; request.currentIndex = 1; end end end ------------------------------------------------------------------------------- -- Adds a bid request to the queue. For bids, the bid parameter should be the -- current bid (or minimum bid in the case the item isn't bid on). For buyouts, -- the bid parameter should be the buyout price. ------------------------------------------------------------------------------- function bidAuction(bid, signature, maxBids, callbackFunc, callbackParam) debugPrint("BidAuction("..bid..", "..signature..")"); local id,rprop,enchant,name,count,min,buyout,unique = Auctioneer.Core.GetItemSignature(signature); if (bid and id and rprop and enchant and name and count and min and buyout and unique) then -- Make sure we have a valid maxBids if (maxBids == nil or maxBids < 1) then maxBids = 25; end -- Create a bid request. local request = {}; request.id = id; request.rprop = rprop; request.enchant = enchant; request.name = name; request.count = count; request.min = min; request.buyout = buyout; request.unique = unique; request.bid = bid; request.maxBids = maxBids; request.callback = { func = callbackFunc, param = callbackParam }; -- Queue the bid request and kick off request processing! addRequestToQueue(request); processRequestQueue(); end end ------------------------------------------------------------------------------- -- Dumps the state of the BidManager to the chat window. ------------------------------------------------------------------------------- function dumpState() chatPrint("BidManager State:"); chatPrint(" IsBidInProgress: "..boolString(isBidInProgress())); chatPrint(" IsQueryInProgress: "..boolString(isQueryInProgress())); chatPrint(" RequestQueue ("..table.getn(RequestQueue).."):"); chatPrint(" PendingBids ("..table.getn(PendingBids).."):"); chatPrint(" CurrentSearchParams:"); chatPrint(" name: "..nilSafe(CurrentSearchParams.name)); chatPrint(" minLevel: "..nilSafe(CurrentSearchParams.minLevel)); chatPrint(" maxLevel: "..nilSafe(CurrentSearchParams.maxLevel)); chatPrint(" invTypeIndex: "..nilSafe(CurrentSearchParams.invTypeIndex)); chatPrint(" classIndex: "..nilSafe(CurrentSearchParams.classIndex)); chatPrint(" subclassIndex: "..nilSafe(CurrentSearchParams.subclassIndex)); chatPrint(" page: "..nilSafe(CurrentSearchParams.page)); chatPrint(" isUsable: "..boolString(CurrentSearchParams.isUsable)); chatPrint(" qualityIndex: "..nilSafe(CurrentSearchParams.qualityIndex)); chatPrint(" queryResponse: "..boolString(CurrentSearchParams.queryResponse)); chatPrint(" queryComplete: "..boolString(CurrentSearchParams.queryComplete)); chatPrint(" targetCountForPage: "..nilSafe(CurrentSearchParams.targetCountForPage)); end ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- function nilSafe(string) if (string) then return string; end return ""; end ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- function boolString(value) if (value) then return "true"; end return "false"; end ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- chatPrint = Auctioneer.Util.ChatPrint; ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- debugPrint = EnhTooltip.DebugPrint; ------------------------------------------------------------------------------- -- Public API ------------------------------------------------------------------------------- AucBidManager = { -- Exported functions BidAuction = bidAuction; IsProcessingRequest = isProcessingRequest; GetRequestCount = getRequestCount; DumpState = dumpState; };