init
This commit is contained in:
@@ -0,0 +1,824 @@
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
-- TradeSkillMaster --
|
||||
-- http://www.curse.com/addons/wow/tradeskill-master --
|
||||
-- --
|
||||
-- A TradeSkillMaster Addon (http://tradeskillmaster.com) --
|
||||
-- All Rights Reserved* - Detailed license information included with addon. --
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
|
||||
local TSM = select(2, ...)
|
||||
local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster") -- loads the localization table
|
||||
|
||||
TSMAPI.AuctionControl = {}
|
||||
local private = {}
|
||||
TSMAPI:RegisterForTracing(private, "TradeSkillMaster.AuctionControl_private")
|
||||
LibStub("AceEvent-3.0"):Embed(private)
|
||||
private.matchList = {}
|
||||
private.currentPage = {}
|
||||
|
||||
|
||||
local function GetNumInBags(baseItemString)
|
||||
local num = 0
|
||||
for _, _, itemString, quantity in TSMAPI:GetBagIterator() do
|
||||
if TSMAPI:GetBaseItemString(itemString) == baseItemString then
|
||||
num = num + quantity
|
||||
end
|
||||
end
|
||||
return num
|
||||
end
|
||||
|
||||
local function ValidateAuction(index, list)
|
||||
if not private.currentAuction then return end
|
||||
local itemString, count, buyout, data, _
|
||||
if type(list) == "table" then
|
||||
itemString, count, buyout = unpack(list)
|
||||
elseif type(list) == "string" then
|
||||
itemString = TSMAPI:GetItemString(GetAuctionItemLink(list, index))
|
||||
-- _, _, count, _, _, _, _, _, _, buyout = GetAuctionItemInfo(list, index)
|
||||
_, _, count, _, _, _, _, _, buyout = GetAuctionItemInfo(list, index)
|
||||
data = {itemString, count, buyout}
|
||||
else
|
||||
return
|
||||
end
|
||||
return count == private.currentAuction.count and buyout == private.currentAuction.buyout and itemString == private.currentAuction.itemString, data
|
||||
end
|
||||
|
||||
local diffFrame = CreateFrame("Frame")
|
||||
diffFrame:Hide()
|
||||
diffFrame.num = 0
|
||||
diffFrame:RegisterEvent("CHAT_MSG_SYSTEM")
|
||||
diffFrame:RegisterEvent("UI_ERROR_MESSAGE")
|
||||
diffFrame:SetScript("OnEvent", function(self, event, arg)
|
||||
if event == "UI_ERROR_MESSAGE" then
|
||||
if arg == ERR_ITEM_NOT_FOUND then
|
||||
local auctionExists
|
||||
for i=1, GetNumAuctionItems("list") do
|
||||
if ValidateAuction(i, "list") then
|
||||
auctionExists = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not auctionExists then
|
||||
self.num = self.num - 1
|
||||
end
|
||||
elseif arg == ERR_AUCTION_HIGHER_BID then
|
||||
local auctionExists
|
||||
for i=1, GetNumAuctionItems("list") do
|
||||
if ValidateAuction(i, "list") then
|
||||
auctionExists = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not auctionExists then
|
||||
self.num = self.num - 1
|
||||
end
|
||||
end
|
||||
elseif event == "CHAT_MSG_SYSTEM" then
|
||||
if arg == ERR_AUCTION_BID_PLACED then
|
||||
self.num = self.num - 1
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local customPriceWarned
|
||||
function private:SetCurrentAuction(record)
|
||||
if not record then
|
||||
private.currentAuction = nil
|
||||
return
|
||||
end
|
||||
|
||||
local buyout = record.buyout
|
||||
if private.confirmationMode == "Post" and not record:IsPlayer() then
|
||||
local undercut = TSMAPI:ParseCustomPrice(private.postUndercut)
|
||||
undercut = undercut and undercut(record.parent:GetItemString())
|
||||
if not undercut and not customPriceWarned then
|
||||
TSM:Print(L["Invalid custom price for undercut amount. Using 1c instead."])
|
||||
customPriceWarned = true
|
||||
undercut = 1
|
||||
end
|
||||
buyout = buyout - undercut
|
||||
end
|
||||
private.currentAuction = {
|
||||
link = record.parent.itemLink,
|
||||
itemString = record.parent:GetItemString(),
|
||||
buyout = buyout,
|
||||
count = record.count,
|
||||
numAuctions = record.numAuctions,
|
||||
seller = record.seller,
|
||||
isPlayer = record:IsPlayer(),
|
||||
num = 1,
|
||||
destroyingNum = record.parent.destroyingNum,
|
||||
}
|
||||
end
|
||||
|
||||
local count = 0
|
||||
function private:FindCurrentAuctionForBuyout(noCache, resetCount)
|
||||
if not private.currentAuction then return end
|
||||
if diffFrame.num > 0 then
|
||||
return TSMAPI:CreateTimeDelay(0.2, private.FindCurrentAuctionForBuyout)
|
||||
end
|
||||
if resetCount then
|
||||
count = 0
|
||||
end
|
||||
count = count + 1
|
||||
|
||||
private:UpdateMatchList(true)
|
||||
if #private.matchList > 0 then
|
||||
-- the next item is on the current page
|
||||
private:UpdateAuctionConfirmation()
|
||||
return
|
||||
end
|
||||
|
||||
private.matchList = {}
|
||||
private.currentPage = {}
|
||||
|
||||
if count > 3 then
|
||||
-- auction no longer exists
|
||||
TSM:Print(L["Skipping auction which no longer exists."])
|
||||
diffFrame.num = diffFrame.num - 1
|
||||
private.justBought = true
|
||||
private:AUCTION_ITEM_LIST_UPDATE()
|
||||
return
|
||||
end
|
||||
TSMAPI.AuctionScan:FindAuction(private.OnAuctionFound, {itemString=private.currentAuction.itemString, buyout=private.currentAuction.buyout, count=private.currentAuction.count, seller=private.currentAuction.seller}, not noCache)
|
||||
private.isSearching = true
|
||||
end
|
||||
|
||||
function private:DoBuyout()
|
||||
if private.isSearching or not private.currentAuction or not private.confirmationFrame:IsVisible() then return end
|
||||
|
||||
for i=#private.matchList, 1, -1 do
|
||||
local aucIndex = private.matchList[i]
|
||||
tremove(private.matchList, i)
|
||||
tremove(private.currentPage, aucIndex)
|
||||
if ValidateAuction(aucIndex, "list") then
|
||||
PlaceAuctionBid("list", aucIndex, private.currentAuction.buyout)
|
||||
private.justBought = true
|
||||
diffFrame.num = diffFrame.num + 1
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
private:FindCurrentAuctionForBuyout()
|
||||
end
|
||||
|
||||
function private:DoCancel()
|
||||
if private.isSearching or not private.currentAuction or not private.confirmationFrame:IsVisible() then return end
|
||||
|
||||
local function OnCancel()
|
||||
private.justBought = true
|
||||
private:AUCTION_ITEM_LIST_UPDATE()
|
||||
end
|
||||
|
||||
for i=GetNumAuctionItems("owner"), 1, -1 do
|
||||
if ValidateAuction(i, "owner") then
|
||||
CancelAuction(i)
|
||||
-- wait for all the events that are triggered by this action
|
||||
private:RegisterMessage("TSM_AH_EVENTS", OnCancel)
|
||||
TSMAPI:WaitForAuctionEvents("Cancel")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
TSM:Print(L["Auction not found. Skipped."])
|
||||
private.justBought = true
|
||||
private:AUCTION_ITEM_LIST_UPDATE()
|
||||
end
|
||||
|
||||
function private:DoPost(postInfo)
|
||||
if private.isSearching or not postInfo or not private.postFrame:IsVisible() then return end
|
||||
|
||||
if not AuctionFrameAuctions.duration then
|
||||
-- Fix in case Blizzard_AuctionUI hasn't set this value yet (which could cause an error)
|
||||
AuctionFrameAuctions.duration = postInfo.duration
|
||||
end
|
||||
|
||||
local bag, slot
|
||||
for b, s, itemString in TSMAPI:GetBagIterator() do
|
||||
if postInfo.itemString == itemString then
|
||||
bag, slot = b, s
|
||||
break
|
||||
end
|
||||
end
|
||||
if not bag then
|
||||
TSM:Print(L["Item not found in bags. Skipping"])
|
||||
return
|
||||
end
|
||||
|
||||
local function OnPost()
|
||||
private.postFrame:Hide()
|
||||
postInfo.duration = postInfo.duration == 1 and 3 or 4
|
||||
TSM:AuctionControlCallback("OnPost", postInfo)
|
||||
TSMAPI:FireEvent("TSM:AUCTIONCONTROL:ITEMPOSTED", postInfo)
|
||||
end
|
||||
private:RegisterMessage("TSM_AH_EVENTS", OnPost)
|
||||
TSMAPI:WaitForAuctionEvents("Post", postInfo.numAuctions > 1)
|
||||
|
||||
PickupContainerItem(bag, slot)
|
||||
ClickAuctionSellItemButton(AuctionsItemButton, "LeftButton")
|
||||
StartAuction(postInfo.bid, postInfo.buyout, postInfo.duration, postInfo.stackSize, postInfo.numAuctions)
|
||||
end
|
||||
|
||||
function private:UpdateMatchList(noPageScanning)
|
||||
private.matchList = {}
|
||||
|
||||
if noPageScanning then
|
||||
for i=1, #private.currentPage do
|
||||
if ValidateAuction(i, private.currentPage[i]) then
|
||||
tinsert(private.matchList, i)
|
||||
end
|
||||
end
|
||||
else
|
||||
private.currentPage = {}
|
||||
for i=1, GetNumAuctionItems("list") do
|
||||
local isValid, data = ValidateAuction(i, "list")
|
||||
private.currentPage[i] = data
|
||||
if isValid then
|
||||
tinsert(private.matchList, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function private:OnAuctionFound(cacheIndex)
|
||||
if not private.isSearching or not private.currentAuction then return end
|
||||
private.isSearching = nil
|
||||
|
||||
private:UpdateMatchList()
|
||||
|
||||
if #private.matchList == 0 then
|
||||
private:FindCurrentAuctionForBuyout(true)
|
||||
else
|
||||
private.currentCacheIndex = cacheIndex
|
||||
private:UpdateAuctionConfirmation()
|
||||
end
|
||||
end
|
||||
|
||||
function private:AUCTION_ITEM_LIST_UPDATE()
|
||||
if not private.currentAuction or not TSMAPI:AHTabIsVisible(private.module) then return end
|
||||
|
||||
if private.justBought then
|
||||
private.justBought = nil
|
||||
private.currentAuction.num = private.currentAuction.num + 1
|
||||
local prevAuction = CopyTable(private.currentAuction)
|
||||
if private.currentAuction.num > private.currentAuction.numAuctions then
|
||||
TSMAPI.AuctionControl:HideConfirmation()
|
||||
else
|
||||
if #private.matchList > 0 then
|
||||
private:UpdateAuctionConfirmation()
|
||||
else
|
||||
private:FindCurrentAuctionForBuyout(nil, true)
|
||||
end
|
||||
end
|
||||
|
||||
if private.currentCacheIndex then
|
||||
TSMAPI.AuctionScan:CacheRemove(prevAuction.itemString, private.currentCacheIndex)
|
||||
private.currentCacheIndex = nil
|
||||
end
|
||||
|
||||
TSM:AuctionControlCallback("OnBuyout", prevAuction)
|
||||
end
|
||||
end
|
||||
|
||||
function TSM:AuctionControlCallback(...)
|
||||
if not private.callback then return end
|
||||
private.callback(...)
|
||||
end
|
||||
|
||||
|
||||
-- **************************************************************************
|
||||
-- Utility TSMAPI Functions
|
||||
-- **************************************************************************
|
||||
|
||||
function TSMAPI.AuctionControl:IsConfirmationVisible()
|
||||
return (private.confirmationFrame and private.confirmationFrame:IsVisible()) or (private.postFrame and private.postFrame:IsVisible())
|
||||
end
|
||||
|
||||
function TSMAPI.AuctionControl:IsBuyingComplete()
|
||||
return diffFrame.num <= 0
|
||||
end
|
||||
|
||||
|
||||
-- **************************************************************************
|
||||
-- GUI Show/Hide/Update Functions
|
||||
-- **************************************************************************
|
||||
|
||||
function TSMAPI.AuctionControl:ShowControlButtons(parent, rt, callback, module, postBidPercent, postUndercut)
|
||||
private.confirmationFrame = private.confirmationFrame or private:CreateConfirmationFrame(parent)
|
||||
private.postFrame = private.postFrame or private:CreatePostFrame(parent)
|
||||
private.controlButtons = private.controlButtons or private:CreateControlButtons(parent)
|
||||
private.controlButtons:Show()
|
||||
private.rt = rt
|
||||
private.callback = callback
|
||||
private.module = module
|
||||
private.postBidPercent = postBidPercent
|
||||
private.postUndercut = postUndercut
|
||||
return private.controlButtons
|
||||
end
|
||||
|
||||
function TSMAPI.AuctionControl:HideControlButtons()
|
||||
private.controlButtons:Hide()
|
||||
private.rt = nil
|
||||
private.callback = nil
|
||||
TSMAPI.AuctionControl:HideConfirmation()
|
||||
end
|
||||
|
||||
function TSMAPI.AuctionControl:SetNoResultItem(itemString, buyout)
|
||||
if not itemString or not buyout then return end
|
||||
local link = select(2, TSMAPI:GetSafeItemInfo(itemString))
|
||||
private.currentAuction = {
|
||||
link = link,
|
||||
itemString = itemString,
|
||||
buyout = buyout,
|
||||
count = 1,
|
||||
numAuctions = 1,
|
||||
num = 1,
|
||||
isNoResult = true,
|
||||
}
|
||||
end
|
||||
|
||||
function private:ShowConfirmationWindow()
|
||||
if private.confirmationFrame:IsVisible() then
|
||||
private.confirmationFrame:UpdateStrata()
|
||||
return
|
||||
elseif private.postFrame:IsVisible() then
|
||||
private.postFrame:UpdateStrata()
|
||||
return
|
||||
end
|
||||
private:SetCurrentAuction(private.rt:GetSelectedAuction())
|
||||
if not private.currentAuction then return end
|
||||
|
||||
private:RegisterEvent("AUCTION_ITEM_LIST_UPDATE")
|
||||
diffFrame.num = 0
|
||||
diffFrame:Show()
|
||||
private.confirmationFrame:Show()
|
||||
private.confirmationFrame.proceed:Disable()
|
||||
private.confirmationFrame.linkText:SetText("")
|
||||
private.confirmationFrame.quantityText:SetText("")
|
||||
private.confirmationFrame.buyoutText:SetText("")
|
||||
private.confirmationFrame.buyoutText2:SetText("")
|
||||
private.confirmationFrame.purchasedText:SetText("")
|
||||
private.confirmationFrame.searchingText:SetText(L["Searching for item..."])
|
||||
if private.confirmationMode == "Buyout" then
|
||||
private:FindCurrentAuctionForBuyout(nil, true)
|
||||
else
|
||||
private:UpdateAuctionConfirmation()
|
||||
end
|
||||
end
|
||||
|
||||
function private:ShowPostWindow()
|
||||
if private.confirmationFrame:IsVisible() then
|
||||
private.confirmationFrame:UpdateStrata()
|
||||
return
|
||||
elseif private.postFrame:IsVisible() then
|
||||
private.postFrame:UpdateStrata()
|
||||
return
|
||||
end
|
||||
if not private.currentAuction or not private.currentAuction.isNoResult then
|
||||
private:SetCurrentAuction(private.rt:GetSelectedAuction())
|
||||
end
|
||||
if not private.currentAuction then return end
|
||||
|
||||
private:RegisterEvent("AUCTION_ITEM_LIST_UPDATE")
|
||||
diffFrame.num = 0
|
||||
diffFrame:Show()
|
||||
private.postFrame:Show()
|
||||
private:UpdatePostFrame()
|
||||
TSMAPI:FireEvent("TSM:AUCTIONCONTROL:POSTSHOWN")
|
||||
end
|
||||
|
||||
function TSMAPI.AuctionControl:HideConfirmation()
|
||||
private:UnregisterEvent("AUCTION_ITEM_LIST_UPDATE")
|
||||
if private.confirmationFrame then private.confirmationFrame:Hide() end
|
||||
if private.postFrame then private.postFrame:Hide() end
|
||||
diffFrame:Hide()
|
||||
private.isSearching = nil
|
||||
private:SetCurrentAuction()
|
||||
TSMAPI.AuctionScan:StopFindScan()
|
||||
end
|
||||
|
||||
function private:UpdateAuctionConfirmation()
|
||||
local buyoutText = TSMAPI:FormatTextMoneyIcon(private.currentAuction.buyout, nil, true)
|
||||
local itemBuyoutText = TSMAPI:FormatTextMoneyIcon(floor(private.currentAuction.buyout/private.currentAuction.count), nil, true)
|
||||
|
||||
private.confirmationFrame.searchingText:SetText("")
|
||||
private.confirmationFrame.linkText:SetText(private.currentAuction.link)
|
||||
private.confirmationFrame.quantityText:SetText("x"..private.currentAuction.count)
|
||||
private.confirmationFrame.buyoutText:SetText(format(L["Item Buyout: %s"], itemBuyoutText))
|
||||
private.confirmationFrame.buyoutText2:SetText(format(L["Auction Buyout: %s"], buyoutText))
|
||||
if private.confirmationMode == "Buyout" then
|
||||
private.confirmationFrame.proceed:SetText(BUYOUT)
|
||||
private.confirmationFrame.purchasedText:SetText(format(L["Purchasing Auction: %d/%d"], private.currentAuction.num, private.currentAuction.numAuctions))
|
||||
elseif private.confirmationMode == "Cancel" then
|
||||
private.confirmationFrame.proceed:SetText(CANCEL)
|
||||
private.confirmationFrame.purchasedText:SetText(format(L["Canceling Auction: %d/%d"], private.currentAuction.num, private.currentAuction.numAuctions))
|
||||
end
|
||||
private.confirmationFrame.proceed:Enable()
|
||||
end
|
||||
|
||||
function private:UpdatePostFrame()
|
||||
local maxQuantity = select(8, TSMAPI:GetSafeItemInfo(private.currentAuction.link))
|
||||
local numInBags = GetNumInBags(TSMAPI:GetBaseItemString(private.currentAuction.itemString))
|
||||
local stackSize = min(private.currentAuction.count, numInBags)
|
||||
local currentPerItem = floor(private.currentAuction.buyout/private.currentAuction.count)
|
||||
local currentBuyout = stackSize == private.currentAuction.count and private.currentAuction.buyout or (currentPerItem*stackSize)
|
||||
|
||||
private.postFrame.numInBags = numInBags
|
||||
private.postFrame.linkText:SetText(private.currentAuction.link)
|
||||
private.postFrame.proceed:Enable()
|
||||
private.postFrame.buyoutInputBox:SetText(TSMAPI:FormatTextMoney(currentBuyout, nil, nil, nil, true))
|
||||
private.postFrame.perItemInputBox:SetText(TSMAPI:FormatTextMoney(currentPerItem, nil, nil, nil, true))
|
||||
private.postFrame.numAuctionsInputBox.max = numInBags
|
||||
private.postFrame.numAuctionsInputBox.btn:SetText(format(L["max %d"], floor(numInBags/stackSize)))
|
||||
private.postFrame.numAuctionsInputBox:SetNumber(1)
|
||||
private.postFrame.stackSizeInputBox.max = min(numInBags, maxQuantity)
|
||||
private.postFrame.stackSizeInputBox.btn:SetText(format(L["max %d"], private.postFrame.stackSizeInputBox.max))
|
||||
private.postFrame.stackSizeInputBox:SetNumber(stackSize)
|
||||
private.postFrame.durationDropdown:SetValue(TSM.db.profile.postDuration)
|
||||
end
|
||||
|
||||
|
||||
-- **************************************************************************
|
||||
-- GUI Creation Code
|
||||
-- **************************************************************************
|
||||
|
||||
function private:CreateConfirmationFrame(parent)
|
||||
local frame = CreateFrame("Frame", nil, parent)
|
||||
TSMAPI.Design:SetFrameBackdropColor(frame)
|
||||
frame:Hide()
|
||||
-- frame:SetPoint("CENTER")
|
||||
frame:SetPoint("BOTTOMRIGHT")
|
||||
frame:SetFrameStrata("DIALOG")
|
||||
frame:SetWidth(300)
|
||||
frame:SetHeight(150)
|
||||
frame.UpdateStrata = function()
|
||||
frame:SetFrameStrata("DIALOG")
|
||||
frame.bg:SetFrameStrata("HIGH")
|
||||
end
|
||||
frame:SetScript("OnShow", frame.UpdateStrata)
|
||||
frame:SetScript("OnUpdate", function()
|
||||
if not TSMAPI:AHTabIsVisible(private.module) then
|
||||
TSMAPI.AuctionControl:HideConfirmation()
|
||||
end
|
||||
end)
|
||||
|
||||
local bg = CreateFrame("Frame", nil, frame)
|
||||
bg:SetFrameStrata("HIGH")
|
||||
bg:SetPoint("TOPLEFT", parent.content)
|
||||
bg:SetPoint("BOTTOMRIGHT", parent.content)
|
||||
bg:EnableMouse(true)
|
||||
TSMAPI.Design:SetFrameBackdropColor(bg)
|
||||
bg:SetAlpha(.2)
|
||||
frame.bg = bg
|
||||
|
||||
local btn = TSMAPI.GUI:CreateButton(frame, 18, "TSMAHConfirmationActionButton")
|
||||
btn:SetPoint("BOTTOMLEFT", 10, 10)
|
||||
btn:SetPoint("BOTTOMRIGHT", frame, "BOTTOM", -2, 10)
|
||||
btn:SetHeight(25)
|
||||
btn:SetText("")
|
||||
btn:SetScript("OnClick", function(self)
|
||||
if not TSMAPI:AHTabIsVisible(private.module) then return end
|
||||
self:Disable()
|
||||
if private.confirmationMode == "Buyout" then
|
||||
private:DoBuyout()
|
||||
elseif private.confirmationMode == "Cancel" then
|
||||
private:DoCancel()
|
||||
end
|
||||
end)
|
||||
frame.proceed = btn
|
||||
|
||||
local btn = TSMAPI.GUI:CreateButton(frame, 18)
|
||||
btn:SetPoint("BOTTOMLEFT", frame, "BOTTOM", 2, 10)
|
||||
btn:SetPoint("BOTTOMRIGHT", -10, 10)
|
||||
btn:SetHeight(25)
|
||||
btn:SetText(CLOSE)
|
||||
btn:SetScript("OnClick", function() frame:Hide() end)
|
||||
frame.close = btn
|
||||
|
||||
local linkText = TSMAPI.GUI:CreateLabel(frame)
|
||||
linkText:SetFontObject(GameFontNormal)
|
||||
linkText:SetPoint("TOP", -10, -10)
|
||||
frame.linkText = linkText
|
||||
|
||||
local bg = frame:CreateTexture(nil, "BACKGROUND")
|
||||
bg:SetPoint("TOPLEFT", linkText, -2, 2)
|
||||
bg:SetPoint("BOTTOMRIGHT", linkText, 2, -2)
|
||||
TSMAPI.Design:SetContentColor(bg)
|
||||
linkText.bg = bg
|
||||
bg:Show()
|
||||
|
||||
local quantityText = TSMAPI.GUI:CreateLabel(frame)
|
||||
quantityText:SetPoint("LEFT", linkText, "RIGHT")
|
||||
frame.quantityText = quantityText
|
||||
|
||||
local buyoutText = TSMAPI.GUI:CreateLabel(frame)
|
||||
buyoutText:SetPoint("TOPLEFT", 10, -41)
|
||||
buyoutText:SetJustifyH("LEFT")
|
||||
frame.buyoutText = buyoutText
|
||||
|
||||
local buyoutText2 = TSMAPI.GUI:CreateLabel(frame)
|
||||
buyoutText2:SetPoint("TOPLEFT", buyoutText, "BOTTOMLEFT")
|
||||
buyoutText2:SetJustifyH("LEFT")
|
||||
frame.buyoutText2 = buyoutText2
|
||||
|
||||
local purchasedText = TSMAPI.GUI:CreateLabel(frame)
|
||||
purchasedText:SetPoint("TOPLEFT", 10, -70)
|
||||
frame.purchasedText = purchasedText
|
||||
|
||||
local searchingText = TSMAPI.GUI:CreateLabel(frame)
|
||||
searchingText:SetPoint("CENTER")
|
||||
frame.searchingText = searchingText
|
||||
|
||||
return frame
|
||||
end
|
||||
|
||||
function private:CreatePostFrame(parent)
|
||||
local frame = CreateFrame("Frame", nil, parent)
|
||||
TSMAPI.Design:SetFrameBackdropColor(frame)
|
||||
frame:Hide()
|
||||
frame:SetPoint("CENTER")
|
||||
frame:SetFrameStrata("DIALOG")
|
||||
frame:SetWidth(250)
|
||||
frame:SetHeight(245)
|
||||
frame.UpdateStrata = function()
|
||||
frame:SetFrameStrata("DIALOG")
|
||||
frame.bg:SetFrameStrata("HIGH")
|
||||
end
|
||||
frame:SetScript("OnShow", frame.UpdateStrata)
|
||||
frame:SetScript("OnUpdate", function()
|
||||
if not TSMAPI:AHTabIsVisible(private.module) then
|
||||
TSMAPI.AuctionControl:HideConfirmation()
|
||||
end
|
||||
end)
|
||||
|
||||
local bg = CreateFrame("Frame", nil, frame)
|
||||
bg:SetFrameStrata("HIGH")
|
||||
bg:SetPoint("TOPLEFT", parent.content)
|
||||
bg:SetPoint("BOTTOMRIGHT", parent.content)
|
||||
bg:EnableMouse(true)
|
||||
TSMAPI.Design:SetFrameBackdropColor(bg)
|
||||
bg:SetAlpha(0.2)
|
||||
frame.bg = bg
|
||||
|
||||
local btn = TSMAPI.GUI:CreateButton(frame, 18, "TSMAHConfirmationPostButton")
|
||||
btn:SetPoint("BOTTOMLEFT", 10, 10)
|
||||
btn:SetPoint("BOTTOMRIGHT", frame, "BOTTOM", -2, 10)
|
||||
btn:SetHeight(25)
|
||||
btn:SetText(L["Post"])
|
||||
btn:SetScript("OnClick", function(self)
|
||||
if not TSMAPI:AHTabIsVisible(private.module) then return end
|
||||
self:Disable()
|
||||
local postInfo = {}
|
||||
postInfo.itemString = private.currentAuction.itemString
|
||||
postInfo.buyout = TSMAPI:UnformatTextMoney(frame.buyoutInputBox:GetText())
|
||||
postInfo.bid = max(floor(postInfo.buyout*private.postBidPercent), 1)
|
||||
postInfo.stackSize = frame.stackSizeInputBox:GetNumber()
|
||||
postInfo.numAuctions = frame.numAuctionsInputBox:GetNumber()
|
||||
postInfo.duration = TSM.db.profile.postDuration
|
||||
|
||||
private:DoPost(postInfo)
|
||||
end)
|
||||
frame.proceed = btn
|
||||
|
||||
local btn = TSMAPI.GUI:CreateButton(frame, 18)
|
||||
btn:SetPoint("BOTTOMLEFT", frame, "BOTTOM", 2, 10)
|
||||
btn:SetPoint("BOTTOMRIGHT", -10, 10)
|
||||
btn:SetHeight(25)
|
||||
btn:SetText(CLOSE)
|
||||
btn:SetScript("OnClick", function() frame:Hide() end)
|
||||
frame.close = btn
|
||||
|
||||
local linkText = TSMAPI.GUI:CreateLabel(frame)
|
||||
linkText:SetFontObject(GameFontNormal)
|
||||
linkText:SetPoint("TOP", -10, -10)
|
||||
frame.linkText = linkText
|
||||
|
||||
local bg = frame:CreateTexture(nil, "BACKGROUND")
|
||||
bg:SetPoint("TOPLEFT", linkText, -2, 2)
|
||||
bg:SetPoint("BOTTOMRIGHT", linkText, 2, -2)
|
||||
TSMAPI.Design:SetContentColor(bg)
|
||||
linkText.bg = bg
|
||||
bg:Show()
|
||||
|
||||
|
||||
local function OnPriceInputBoxTextChanged()
|
||||
local buyout = TSMAPI:UnformatTextMoney(frame.buyoutInputBox:GetText())
|
||||
local perItem = TSMAPI:UnformatTextMoney(frame.perItemInputBox:GetText())
|
||||
if not buyout or not perItem or buyout == 0 then
|
||||
frame.proceed:Disable()
|
||||
else
|
||||
frame.proceed:Enable()
|
||||
end
|
||||
end
|
||||
|
||||
local function OnPriceInputBoxEditFocusLost(self)
|
||||
local copper = TSMAPI:UnformatTextMoney(self:GetText())
|
||||
if copper then
|
||||
local stackSize = frame.stackSizeInputBox:GetNumber()
|
||||
if self == frame.buyoutInputBox then
|
||||
frame.perItemInputBox:SetText(TSMAPI:FormatTextMoney(floor(copper/stackSize), nil, nil, nil, true))
|
||||
elseif self == frame.perItemInputBox then
|
||||
frame.buyoutInputBox:SetText(TSMAPI:FormatTextMoney(copper*stackSize, nil, nil, nil, true))
|
||||
end
|
||||
self:SetText(TSMAPI:FormatTextMoney(copper, nil, nil, nil, true))
|
||||
self:ClearFocus()
|
||||
else
|
||||
self:SetFocus()
|
||||
end
|
||||
end
|
||||
|
||||
local function OnInputBoxTabPressed(self)
|
||||
local boxes = {"buyoutInputBox", "perItemInputBox", "numAuctionsInputBox", "stackSizeInputBox"}
|
||||
self:ClearFocus()
|
||||
for i=1, #boxes-1 do
|
||||
if self == frame[boxes[i]] then
|
||||
frame[boxes[i+1]]:SetFocus()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local buyoutLabel = TSMAPI.GUI:CreateLabel(frame)
|
||||
buyoutLabel:SetPoint("TOPLEFT", 10, -40)
|
||||
buyoutLabel:SetHeight(20)
|
||||
buyoutLabel:SetJustifyH("LEFT")
|
||||
buyoutLabel:SetText(L["Auction Buyout:"])
|
||||
|
||||
local buyoutInputBox = TSMAPI.GUI:CreateInputBox(frame)
|
||||
buyoutInputBox:SetJustifyH("RIGHT")
|
||||
buyoutInputBox:SetPoint("TOPRIGHT", -10, -40)
|
||||
buyoutInputBox:SetPoint("TOPLEFT", buyoutLabel, "TOPRIGHT", 10, 0)
|
||||
buyoutInputBox:SetHeight(20)
|
||||
buyoutInputBox:SetScript("OnEnterPressed", buyoutInputBox.ClearFocus)
|
||||
buyoutInputBox:SetScript("OnEscapePressed", buyoutInputBox.ClearFocus)
|
||||
buyoutInputBox:SetScript("OnEditFocusLost", OnPriceInputBoxEditFocusLost)
|
||||
buyoutInputBox:SetScript("OnTextChanged", OnPriceInputBoxTextChanged)
|
||||
buyoutInputBox:SetScript("OnTabPressed", OnInputBoxTabPressed)
|
||||
frame.buyoutInputBox = buyoutInputBox
|
||||
|
||||
local perItemLabel = TSMAPI.GUI:CreateLabel(frame)
|
||||
perItemLabel:SetPoint("TOPLEFT", 10, -65)
|
||||
perItemLabel:SetHeight(20)
|
||||
perItemLabel:SetJustifyH("LEFT")
|
||||
perItemLabel:SetText(L["Per Item:"])
|
||||
|
||||
local perItemInputBox = TSMAPI.GUI:CreateInputBox(frame)
|
||||
perItemInputBox:SetJustifyH("RIGHT")
|
||||
perItemInputBox:SetPoint("TOPRIGHT", -10, -65)
|
||||
perItemInputBox:SetPoint("TOPLEFT", perItemLabel, "TOPRIGHT", 10, 0)
|
||||
perItemInputBox:SetHeight(20)
|
||||
perItemInputBox:SetScript("OnEnterPressed", perItemInputBox.ClearFocus)
|
||||
perItemInputBox:SetScript("OnEscapePressed", perItemInputBox.ClearFocus)
|
||||
perItemInputBox:SetScript("OnEditFocusLost", OnPriceInputBoxEditFocusLost)
|
||||
perItemInputBox:SetScript("OnTextChanged", OnPriceInputBoxTextChanged)
|
||||
perItemInputBox:SetScript("OnTabPressed", OnInputBoxTabPressed)
|
||||
frame.perItemInputBox = perItemInputBox
|
||||
|
||||
|
||||
local function OnCountInputBoxEditFocusLost(self)
|
||||
local numAuctions = max(1, min(frame.numAuctionsInputBox:GetNumber(), frame.numAuctionsInputBox.max))
|
||||
local stackSize = max(1, min(frame.stackSizeInputBox:GetNumber(), frame.stackSizeInputBox.max))
|
||||
|
||||
if self == frame.stackSizeInputBox then
|
||||
numAuctions = min(numAuctions, floor(frame.numInBags/stackSize))
|
||||
elseif self == frame.numAuctionsInputBox then
|
||||
stackSize = min(stackSize, floor(frame.numInBags/numAuctions))
|
||||
end
|
||||
frame.numAuctionsInputBox:SetNumber(numAuctions)
|
||||
frame.stackSizeInputBox:SetNumber(stackSize)
|
||||
frame.numAuctionsInputBox.btn:SetText(format(L["max %d"], floor(frame.numInBags/stackSize)))
|
||||
frame.stackSizeInputBox.btn:SetText(format(L["max %d"], min(frame.stackSizeInputBox.max, floor(frame.numInBags/numAuctions))))
|
||||
local perItem = TSMAPI:UnformatTextMoney(frame.perItemInputBox:GetText())
|
||||
frame.buyoutInputBox:SetText(TSMAPI:FormatTextMoney(perItem*stackSize, nil, nil, nil, true))
|
||||
end
|
||||
|
||||
local function OnCountInputBoxTextChanged(self)
|
||||
local numAuctions = frame.numAuctionsInputBox:GetNumber()
|
||||
local stackSize = frame.stackSizeInputBox:GetNumber()
|
||||
if numAuctions <= 0 or stackSize <= 0 or numAuctions*stackSize > frame.numInBags then
|
||||
frame.proceed:Disable()
|
||||
else
|
||||
frame.proceed:Enable()
|
||||
end
|
||||
end
|
||||
|
||||
local function OnMaxButtonClicked(self)
|
||||
self.inputBox:SetNumber(self.inputBox.max)
|
||||
self.inputBox:SetFocus()
|
||||
self.inputBox:ClearFocus()
|
||||
end
|
||||
|
||||
local numAuctionsInputBox = TSMAPI.GUI:CreateInputBox(frame)
|
||||
numAuctionsInputBox:SetJustifyH("CENTER")
|
||||
numAuctionsInputBox:SetNumeric(true)
|
||||
numAuctionsInputBox:SetPoint("TOPLEFT", 10, -110)
|
||||
numAuctionsInputBox:SetHeight(20)
|
||||
numAuctionsInputBox:SetScript("OnEnterPressed", numAuctionsInputBox.ClearFocus)
|
||||
numAuctionsInputBox:SetScript("OnEscapePressed", numAuctionsInputBox.ClearFocus)
|
||||
numAuctionsInputBox:SetScript("OnEditFocusLost", OnCountInputBoxEditFocusLost)
|
||||
numAuctionsInputBox:SetScript("OnTextChanged", OnCountInputBoxTextChanged)
|
||||
numAuctionsInputBox:SetScript("OnTabPressed", OnInputBoxTabPressed)
|
||||
frame.numAuctionsInputBox = numAuctionsInputBox
|
||||
|
||||
local stackSizeInputBox = TSMAPI.GUI:CreateInputBox(frame)
|
||||
stackSizeInputBox:SetJustifyH("CENTER")
|
||||
stackSizeInputBox:SetNumeric(true)
|
||||
stackSizeInputBox:SetPoint("TOPRIGHT", -10, -110)
|
||||
stackSizeInputBox:SetHeight(20)
|
||||
stackSizeInputBox:SetScript("OnEnterPressed", stackSizeInputBox.ClearFocus)
|
||||
stackSizeInputBox:SetScript("OnEscapePressed", stackSizeInputBox.ClearFocus)
|
||||
stackSizeInputBox:SetScript("OnEditFocusLost", OnCountInputBoxEditFocusLost)
|
||||
stackSizeInputBox:SetScript("OnTextChanged", OnCountInputBoxTextChanged)
|
||||
stackSizeInputBox:SetScript("OnTabPressed", OnInputBoxTabPressed)
|
||||
frame.stackSizeInputBox = stackSizeInputBox
|
||||
|
||||
local countLabel = TSMAPI.GUI:CreateLabel(frame)
|
||||
countLabel:SetPoint("TOPLEFT", numAuctionsInputBox, "TOPRIGHT", 10, 0)
|
||||
countLabel:SetPoint("TOPRIGHT", stackSizeInputBox, "TOPLEFT", -10, 0)
|
||||
countLabel:SetHeight(20)
|
||||
countLabel:SetJustifyH("CENTER")
|
||||
countLabel:SetText(L["stacks of"])
|
||||
|
||||
local editboxWidth = (frame:GetWidth() - 40 - countLabel:GetStringWidth()) / 2
|
||||
numAuctionsInputBox:SetWidth(editboxWidth)
|
||||
stackSizeInputBox:SetWidth(editboxWidth)
|
||||
|
||||
local maxStackSizeBtn = TSMAPI.GUI:CreateButton(frame, 12)
|
||||
maxStackSizeBtn:SetPoint("TOPLEFT", stackSizeInputBox, "BOTTOMLEFT", 5, -3)
|
||||
maxStackSizeBtn:SetPoint("TOPRIGHT", stackSizeInputBox, "BOTTOMRIGHT", -5, -3)
|
||||
maxStackSizeBtn:SetHeight(14)
|
||||
maxStackSizeBtn:SetText("")
|
||||
maxStackSizeBtn:SetScript("OnClick", OnMaxButtonClicked)
|
||||
maxStackSizeBtn.inputBox = stackSizeInputBox
|
||||
stackSizeInputBox.btn = maxStackSizeBtn
|
||||
|
||||
local maxNumAuctionsBtn = TSMAPI.GUI:CreateButton(frame, 12)
|
||||
maxNumAuctionsBtn:SetPoint("TOPLEFT", numAuctionsInputBox, "BOTTOMLEFT", 5, -3)
|
||||
maxNumAuctionsBtn:SetPoint("TOPRIGHT", numAuctionsInputBox, "BOTTOMRIGHT", -5, -3)
|
||||
maxNumAuctionsBtn:SetHeight(14)
|
||||
maxNumAuctionsBtn:SetText("")
|
||||
maxNumAuctionsBtn:SetScript("OnClick", OnMaxButtonClicked)
|
||||
maxNumAuctionsBtn.inputBox = numAuctionsInputBox
|
||||
numAuctionsInputBox.btn = maxNumAuctionsBtn
|
||||
|
||||
local durationLabel = TSMAPI.GUI:CreateLabel(frame)
|
||||
durationLabel:SetPoint("TOPLEFT", 10, -165)
|
||||
durationLabel:SetHeight(20)
|
||||
durationLabel:SetJustifyH("LEFT")
|
||||
durationLabel:SetText(L["Duration:"])
|
||||
|
||||
local list = {AUCTION_DURATION_ONE, AUCTION_DURATION_TWO, AUCTION_DURATION_THREE}
|
||||
local durationDropdown = TSMAPI.GUI:CreateDropdown(frame, list)
|
||||
durationDropdown:SetPoint("TOPLEFT", durationLabel, "TOPRIGHT", 10, 0)
|
||||
durationDropdown:SetPoint("TOPRIGHT", 0, -165)
|
||||
durationDropdown:SetHeight(20)
|
||||
durationDropdown:SetCallback("OnValueChanged", function(self, _, value) TSM.db.profile.postDuration = value end)
|
||||
frame.durationDropdown = durationDropdown
|
||||
|
||||
return frame
|
||||
end
|
||||
|
||||
function private:CreateControlButtons(parent)
|
||||
local frame = CreateFrame("Frame", nil, parent)
|
||||
frame:SetHeight(24)
|
||||
frame:SetWidth(390)
|
||||
frame:SetPoint("BOTTOMRIGHT", -20, 6)
|
||||
|
||||
local function OnClick(self)
|
||||
if not private.rt or not private.callback then return end
|
||||
private.confirmationMode = self.which
|
||||
if self.which == "Post" then
|
||||
private:ShowPostWindow()
|
||||
else
|
||||
private:ShowConfirmationWindow()
|
||||
end
|
||||
end
|
||||
|
||||
local button = TSMAPI.GUI:CreateButton(frame, 18, "TSMAHTabCancelButton")
|
||||
button:SetPoint("TOPLEFT", 0, 0)
|
||||
button:SetWidth(100)
|
||||
button:SetHeight(24)
|
||||
button:SetText(CANCEL)
|
||||
button.which = "Cancel"
|
||||
button:SetScript("OnClick", OnClick)
|
||||
frame.cancel = button
|
||||
|
||||
local button = TSMAPI.GUI:CreateButton(frame, 18, "TSMAHTabPostButton")
|
||||
button:SetPoint("TOPLEFT", 104, 0)
|
||||
button:SetWidth(100)
|
||||
button:SetHeight(24)
|
||||
button:SetText(L["Post"])
|
||||
button.which = "Post"
|
||||
button:SetScript("OnClick", OnClick)
|
||||
frame.post = button
|
||||
|
||||
local button = TSMAPI.GUI:CreateButton(frame, 18, "TSMAHTabBuyoutButton")
|
||||
button:SetPoint("TOPLEFT", 208, 0)
|
||||
button:SetWidth(100)
|
||||
button:SetHeight(24)
|
||||
button:SetText(BUYOUT)
|
||||
button.which = "Buyout"
|
||||
button:SetScript("OnClick", OnClick)
|
||||
frame.buyout = button
|
||||
|
||||
return frame
|
||||
end
|
||||
@@ -0,0 +1,298 @@
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
-- TradeSkillMaster --
|
||||
-- http://www.curse.com/addons/wow/tradeskill-master --
|
||||
-- --
|
||||
-- A TradeSkillMaster Addon (http://tradeskillmaster.com) --
|
||||
-- All Rights Reserved* - Detailed license information included with addon. --
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
|
||||
local TSM = select(2, ...)
|
||||
local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster") -- loads the localization table
|
||||
|
||||
local private = {auctionTabs={}, queuedTabs={}}
|
||||
TSMAPI:RegisterForTracing(private, "TradeSkillMaster.AuctionFrame_private")
|
||||
LibStub("AceEvent-3.0"):Embed(private)
|
||||
LibStub("AceHook-3.0"):Embed(private)
|
||||
|
||||
local registeredModules = {}
|
||||
function TSM:RegisterAuctionFunction(moduleName, callbackShow, callbackHide)
|
||||
if registeredModules[moduleName] then return end
|
||||
registeredModules[moduleName] = true
|
||||
if AuctionFrame then
|
||||
private:CreateTSMAHTab(moduleName, callbackShow, callbackHide)
|
||||
else
|
||||
tinsert(private.queuedTabs, {moduleName, callbackShow, callbackHide})
|
||||
end
|
||||
end
|
||||
|
||||
function private:CreateTSMAHTab(moduleName, callbackShow, callbackHide)
|
||||
local auctionTab = CreateFrame("Frame", nil, AuctionFrame)
|
||||
auctionTab:Hide()
|
||||
auctionTab:SetAllPoints()
|
||||
auctionTab:EnableMouse(true)
|
||||
auctionTab:SetMovable(true)
|
||||
auctionTab:SetScript("OnMouseDown", function() if AuctionFrame:IsMovable() then AuctionFrame:StartMoving() end end)
|
||||
auctionTab:SetScript("OnMouseUp", function() if AuctionFrame:IsMovable() then AuctionFrame:StopMovingOrSizing() end end)
|
||||
|
||||
TSMAPI:CancelFrame("blizzAHLoadedDelay")
|
||||
local n = AuctionFrame.numTabs + 1
|
||||
|
||||
local tab = CreateFrame("Button", "AuctionFrameTab"..n, AuctionFrame, "AuctionTabTemplate")
|
||||
tab:Hide()
|
||||
tab:SetID(n)
|
||||
tab:SetText(TSMAPI.Design:GetInlineColor("link2")..moduleName.."|r")
|
||||
tab:SetNormalFontObject(GameFontHighlightSmall)
|
||||
tab.isTSMTab = moduleName
|
||||
tab:SetPoint("LEFT", _G["AuctionFrameTab"..n-1], "RIGHT", -8, 0)
|
||||
tab:Show()
|
||||
PanelTemplates_SetNumTabs(AuctionFrame, n)
|
||||
PanelTemplates_EnableTab(AuctionFrame, n)
|
||||
auctionTab.tab = tab
|
||||
|
||||
local closeBtn = TSMAPI.GUI:CreateButton(auctionTab, 18)
|
||||
closeBtn:SetPoint("BOTTOMRIGHT", -5, 5)
|
||||
closeBtn:SetWidth(75)
|
||||
closeBtn:SetHeight(24)
|
||||
closeBtn:SetText(CLOSE)
|
||||
closeBtn:SetScript("OnClick", CloseAuctionHouse)
|
||||
|
||||
local iconFrame = CreateFrame("Frame", nil, auctionTab)
|
||||
iconFrame:SetPoint("CENTER", auctionTab, "TOPLEFT", 30, -30)
|
||||
iconFrame:SetHeight(100)
|
||||
iconFrame:SetWidth(100)
|
||||
local icon = iconFrame:CreateTexture(nil, "ARTWORK")
|
||||
icon:SetAllPoints()
|
||||
icon:SetTexture("Interface\\Addons\\TradeSkillMaster\\Media\\TSM_Icon_Big")
|
||||
local textFrame = CreateFrame("Frame", nil, auctionTab)
|
||||
local iconText = textFrame:CreateFontString(nil, "OVERLAY")
|
||||
iconText:SetPoint("CENTER", iconFrame)
|
||||
iconText:SetHeight(15)
|
||||
iconText:SetJustifyH("CENTER")
|
||||
iconText:SetJustifyV("CENTER")
|
||||
iconText:SetFont(TSMAPI.Design:GetContentFont("normal"))
|
||||
iconText:SetTextColor(165/255, 168/255, 188/255, .7)
|
||||
local version = TSM._version
|
||||
iconText:SetText(version)
|
||||
local ag = iconFrame:CreateAnimationGroup()
|
||||
local spin = ag:CreateAnimation("Rotation")
|
||||
spin:SetOrder(1)
|
||||
spin:SetDuration(2)
|
||||
spin:SetDegrees(90)
|
||||
local spin = ag:CreateAnimation("Rotation")
|
||||
spin:SetOrder(2)
|
||||
spin:SetDuration(4)
|
||||
spin:SetDegrees(-180)
|
||||
local spin = ag:CreateAnimation("Rotation")
|
||||
spin:SetOrder(3)
|
||||
spin:SetDuration(2)
|
||||
spin:SetDegrees(90)
|
||||
ag:SetLooping("REPEAT")
|
||||
iconFrame:SetScript("OnEnter", function() ag:Play() end)
|
||||
iconFrame:SetScript("OnLeave", function() ag:Stop() end)
|
||||
|
||||
local moneyText = TSMAPI.GUI:CreateTitleLabel(auctionTab, 16)
|
||||
moneyText:SetJustifyH("CENTER")
|
||||
moneyText:SetJustifyV("CENTER")
|
||||
moneyText:SetPoint("CENTER", auctionTab, "BOTTOMLEFT", 85, 17)
|
||||
TSMAPI.Design:SetIconRegionColor(moneyText)
|
||||
moneyText.SetMoney = function(self, money)
|
||||
self:SetText(TSMAPI:FormatTextMoneyIcon(money))
|
||||
end
|
||||
auctionTab.moneyText = moneyText
|
||||
|
||||
local moneyTextFrame = CreateFrame("Frame", nil, auctionTab)
|
||||
moneyTextFrame:SetAllPoints(moneyText)
|
||||
moneyTextFrame:EnableMouse(true)
|
||||
moneyTextFrame:SetScript("OnEnter", function(self)
|
||||
local currentTotal = 0
|
||||
local incomingTotal = 0
|
||||
for i=1, GetNumAuctionItems("owner") do
|
||||
-- local count, _, _, _, _, _, _, buyoutAmount = select(3, GetAuctionItemInfo("owner", i))
|
||||
local count, _, _, _, _, _, buyoutAmount = select(3, GetAuctionItemInfo("owner", i))
|
||||
if count == 0 then
|
||||
incomingTotal = incomingTotal + buyoutAmount
|
||||
else
|
||||
currentTotal = currentTotal + buyoutAmount
|
||||
end
|
||||
end
|
||||
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
|
||||
GameTooltip:AddLine("Gold Info:")
|
||||
GameTooltip:AddDoubleLine("Player Gold", TSMAPI:FormatTextMoneyIcon(GetMoney()), 1, 1, 1, 1, 1, 1)
|
||||
GameTooltip:AddDoubleLine("Incoming Auction Sales", TSMAPI:FormatTextMoneyIcon(incomingTotal), 1, 1, 1, 1, 1, 1)
|
||||
GameTooltip:AddDoubleLine("Current Auctions Value", TSMAPI:FormatTextMoneyIcon(currentTotal), 1, 1, 1, 1, 1, 1)
|
||||
GameTooltip:Show()
|
||||
end)
|
||||
moneyTextFrame:SetScript("OnLeave", function()
|
||||
GameTooltip:ClearLines()
|
||||
GameTooltip:Hide()
|
||||
end)
|
||||
|
||||
auctionTab:SetScript("OnShow", function(self)
|
||||
self:SetAllPoints()
|
||||
if not self.minimized then
|
||||
callbackShow(self)
|
||||
end
|
||||
end)
|
||||
auctionTab:SetScript("OnHide", function(self)
|
||||
if not self.minimized then
|
||||
callbackHide()
|
||||
end
|
||||
end)
|
||||
|
||||
local contentFrame = CreateFrame("Frame", nil, auctionTab)
|
||||
contentFrame:SetPoint("TOPLEFT", 4, -80)
|
||||
contentFrame:SetPoint("BOTTOMRIGHT", -4, 35)
|
||||
TSMAPI.Design:SetContentColor(contentFrame)
|
||||
auctionTab.content = contentFrame
|
||||
|
||||
tinsert(private.auctionTabs, auctionTab)
|
||||
end
|
||||
|
||||
function private:InitializeAuctionFrame(auctionTab)
|
||||
-- make the AH movable if this option is enabled
|
||||
AuctionFrame:SetMovable(TSM.db.profile.auctionFrameMovable)
|
||||
AuctionFrame:EnableMouse(true)
|
||||
AuctionFrame:SetScript("OnMouseDown", function(self) if self:IsMovable() then self:StartMoving() end end)
|
||||
AuctionFrame:SetScript("OnMouseUp", function(self) if self:IsMovable() then self:StopMovingOrSizing() end end)
|
||||
|
||||
-- scale the auction frame according to the TSM option
|
||||
if AuctionFrame:GetScale() ~= 1 and TSM.db.profile.auctionFrameScale == 1 then TSM.db.profile.auctionFrameScale = AuctionFrame:GetScale() end
|
||||
AuctionFrame:SetScale(TSM.db.profile.auctionFrameScale)
|
||||
|
||||
local prevTab
|
||||
local function TabChangeHook(self)
|
||||
if self.isTSMTab then
|
||||
for _, tabFrame in ipairs(private.auctionTabs) do
|
||||
if tabFrame.minimized and tabFrame.tab ~= self then
|
||||
tabFrame:Show()
|
||||
tabFrame.minimized = nil
|
||||
tabFrame:Hide()
|
||||
elseif tabFrame:IsShown() then
|
||||
tabFrame:Hide()
|
||||
end
|
||||
end
|
||||
local tabAuctionFrame = private:GetAuctionFrame(self)
|
||||
private:OnTabClick(tabAuctionFrame)
|
||||
AuctionFrame:SetFrameLevel(1)
|
||||
tabAuctionFrame:SetFrameStrata(AuctionFrame:GetFrameStrata())
|
||||
tabAuctionFrame:SetFrameLevel(AuctionFrame:GetFrameLevel() + 1)
|
||||
elseif prevTab and prevTab.isTSMTab then
|
||||
local prevTabAuctionFrame = private:GetAuctionFrame(prevTab)
|
||||
prevTabAuctionFrame.minimized = true
|
||||
prevTabAuctionFrame:Hide()
|
||||
private:TabHidden()
|
||||
end
|
||||
prevTab = self
|
||||
|
||||
end
|
||||
private:Hook("AuctionFrameTab_OnClick", TabChangeHook, true)
|
||||
|
||||
-- Makes sure the TSM tab hides correctly when used with addons that hook this function to change tabs (ie Auctionator)
|
||||
-- This probably doesn't have to be a SecureHook, but does need to be a Post-Hook.
|
||||
private:SecureHook("ContainerFrameItemButton_OnModifiedClick", function()
|
||||
if _G["AuctionFrameTab"..PanelTemplates_GetSelectedTab(AuctionFrame)].isTSMTab then return end
|
||||
TabChangeHook(_G["AuctionFrameTab"..PanelTemplates_GetSelectedTab(AuctionFrame)])
|
||||
end)
|
||||
end
|
||||
|
||||
function private:GetAuctionFrame(targetTab)
|
||||
for _, tabFrame in ipairs(private.auctionTabs) do
|
||||
if tabFrame.tab == targetTab then
|
||||
return tabFrame
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function private:InitializeAHTab()
|
||||
for _, info in ipairs(private.queuedTabs) do
|
||||
private:CreateTSMAHTab(unpack(info))
|
||||
end
|
||||
private.queuedTabs = {}
|
||||
private:InitializeAuctionFrame()
|
||||
private.isInitialized = true
|
||||
if AuctionHouse and AuctionHouse:IsVisible() then
|
||||
private:AUCTION_HOUSE_SHOW()
|
||||
end
|
||||
end
|
||||
|
||||
function TSMAPI:AHTabIsVisible(module)
|
||||
return module and _G["AuctionFrameTab"..AuctionFrame.selectedTab].isTSMTab == module
|
||||
end
|
||||
|
||||
function private:AUCTION_HOUSE_SHOW()
|
||||
if private.isInitialized then
|
||||
for i = AuctionFrame.numTabs, 1, -1 do
|
||||
local text = gsub(_G["AuctionFrameTab"..i]:GetText(), "|r", "")
|
||||
text = gsub(text, "|c[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]", "")
|
||||
if text == TSM.db.profile.defaultAuctionTab then
|
||||
_G["AuctionFrameTab"..i]:Click()
|
||||
return
|
||||
end
|
||||
end
|
||||
_G["AuctionFrameTab1"]:Click()
|
||||
end
|
||||
end
|
||||
|
||||
function private:OnTabClick(tab)
|
||||
AuctionFrameTopLeft:Hide()
|
||||
AuctionFrameTop:Hide()
|
||||
AuctionFrameTopRight:Hide()
|
||||
AuctionFrameBotLeft:Hide()
|
||||
AuctionFrameBot:Hide()
|
||||
AuctionFrameBotRight:Hide()
|
||||
AuctionFrameMoneyFrame:Hide()
|
||||
AuctionFrameCloseButton:Hide()
|
||||
private:RegisterEvent("PLAYER_MONEY")
|
||||
|
||||
if TSM.db.profile.openAllBags then
|
||||
OpenAllBags(true)
|
||||
end
|
||||
TSMAPI:CreateTimeDelay("hideAHMoneyFrame", 0.1, function() AuctionFrameMoneyFrame:Hide() end)
|
||||
|
||||
TSMAPI.Design:SetFrameBackdropColor(tab)
|
||||
AuctionFrameTab1:SetPoint("TOPLEFT", AuctionFrame, "BOTTOMLEFT", 15, 1)
|
||||
|
||||
tab:Show()
|
||||
tab.minimized = nil
|
||||
tab.moneyText:SetMoney(GetMoney())
|
||||
end
|
||||
|
||||
function private:TabHidden()
|
||||
AuctionFrameTopLeft:Show()
|
||||
AuctionFrameTop:Show()
|
||||
AuctionFrameTopRight:Show()
|
||||
AuctionFrameBotLeft:Show()
|
||||
AuctionFrameBot:Show()
|
||||
AuctionFrameBotRight:Show()
|
||||
AuctionFrameMoneyFrame:Show()
|
||||
AuctionFrameCloseButton:Show()
|
||||
AuctionFrameTab1:SetPoint("TOPLEFT", AuctionFrame, "BOTTOMLEFT", 15, 12)
|
||||
end
|
||||
|
||||
function private:PLAYER_MONEY()
|
||||
for _, tab in ipairs(private.auctionTabs) do
|
||||
if tab:IsVisible() then
|
||||
tab.moneyText:SetMoney(GetMoney())
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function private:ADDON_LOADED(event, addonName)
|
||||
if addonName == "Blizzard_AuctionUI" then
|
||||
private:UnregisterEvent("ADDON_LOADED")
|
||||
if TSM.db then
|
||||
private:InitializeAHTab()
|
||||
else
|
||||
TSMAPI:CreateTimeDelay("blizzAHLoadedDelay", 0.2, private.InitializeAHTab, 0.2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
private:RegisterEvent("AUCTION_HOUSE_SHOW")
|
||||
if IsAddOnLoaded("Blizzard_AuctionUI") then
|
||||
private:InitializeAHTab()
|
||||
else
|
||||
private:RegisterEvent("ADDON_LOADED")
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,433 @@
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
-- TradeSkillMaster --
|
||||
-- http://www.curse.com/addons/wow/tradeskill-master --
|
||||
-- --
|
||||
-- A TradeSkillMaster Addon (http://tradeskillmaster.com) --
|
||||
-- All Rights Reserved* - Detailed license information included with addon. --
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
|
||||
-- This file contains code for the AuctionItem objects
|
||||
local TSM = select(2, ...)
|
||||
|
||||
local NewRecord, sortHelpers
|
||||
local AuctionRecord = {
|
||||
Initialize = function(self)
|
||||
self.objType = "AuctionRecord"
|
||||
end,
|
||||
|
||||
SetData = function(self, parent, count, minBid, minIncrement, buyout, bid, highBidder, seller, timeLeft)
|
||||
self.parent = parent
|
||||
self.count = count
|
||||
self.minBid = minBid
|
||||
self.minIncrement = minIncrement
|
||||
self.buyout = buyout
|
||||
self.bid = bid
|
||||
self.highBidder = highBidder
|
||||
self.seller = seller
|
||||
self.timeLeft = timeLeft
|
||||
end,
|
||||
|
||||
IsPlayer = function(self)
|
||||
return TSMAPI:IsPlayer(self.seller) or self.parent.alts[self.seller]
|
||||
end,
|
||||
|
||||
GetPercent = function(self)
|
||||
local itemBuyout = self:GetItemBuyout()
|
||||
local marketValue = self.parent.marketValue
|
||||
if itemBuyout and marketValue then
|
||||
return (itemBuyout / marketValue) * 100
|
||||
end
|
||||
end,
|
||||
|
||||
GetDisplayedBid = function(self)
|
||||
local displayedBid
|
||||
if self.bid == 0 then
|
||||
displayedBid = self.minBid
|
||||
else
|
||||
displayedBid = self.bid
|
||||
end
|
||||
return displayedBid
|
||||
end,
|
||||
|
||||
GetRequiredBid = function(self)
|
||||
local requiredBid
|
||||
if self.bid == 0 then
|
||||
requiredBid = self.minBid
|
||||
else
|
||||
requiredBid = self.bid + self.minIncrement
|
||||
end
|
||||
return requiredBid
|
||||
end,
|
||||
|
||||
GetItemBuyout = function(self)
|
||||
if not self.buyout or self.buyout == 0 then return end
|
||||
return floor(self.buyout / self.count)
|
||||
end,
|
||||
|
||||
GetItemDisplayedBid = function(self)
|
||||
return floor(self:GetDisplayedBid() / self.count)
|
||||
end,
|
||||
|
||||
GetItemDestroyingBuyout = function(self)
|
||||
local itemBuyout = self:GetItemBuyout()
|
||||
if itemBuyout then
|
||||
return itemBuyout * self.parent.destroyingNum
|
||||
end
|
||||
end,
|
||||
|
||||
GetItemDestroyingDisplayedBid = function(self)
|
||||
local itemBid = self:GetItemDisplayedBid()
|
||||
if itemBid then
|
||||
return itemBid * self.parent.destroyingNum
|
||||
end
|
||||
end,
|
||||
|
||||
Copy = function(self)
|
||||
local o = NewRecord()
|
||||
o:SetData(self.parent, self.count, self.minBid, self.minIncrement, self.buyout, self.bid, self.highBidder, self.seller, self.timeLeft)
|
||||
o.uniqueID = self.uniqueID
|
||||
return o
|
||||
end,
|
||||
|
||||
Equals = function(self, other)
|
||||
if self == other then
|
||||
return true
|
||||
end
|
||||
|
||||
local params = self.parent.recordParams
|
||||
for _, key in ipairs(params) do
|
||||
if type(self[key]) == "function" then
|
||||
if self[key](self) ~= other[key](other) then
|
||||
return false
|
||||
end
|
||||
else
|
||||
if self[key] ~= other[key] then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end,
|
||||
}
|
||||
|
||||
NewRecord = function()
|
||||
local o = {}
|
||||
setmetatable(o, AuctionRecord)
|
||||
AuctionRecord.__index = AuctionRecord
|
||||
o:Initialize()
|
||||
return o
|
||||
end
|
||||
|
||||
|
||||
--- Test Documentation
|
||||
-- @name AuctionItem
|
||||
-- @description Test description for the Auction Item object.
|
||||
local AuctionItem = {
|
||||
-- @field Test field
|
||||
Initialize = function(self)
|
||||
self.objType = "AuctionItem"
|
||||
self.itemLink = nil
|
||||
self.marketValue = nil
|
||||
self.playerAuctions = 0
|
||||
self.records = {}
|
||||
self.alts = {}
|
||||
self.recordParams = {"buyout", "count", "seller"}
|
||||
self.shouldCompact = true
|
||||
self.texture = ""
|
||||
end,
|
||||
|
||||
-- sets the item (or battle pet's) texture
|
||||
SetTexture = function(self, texture)
|
||||
self.texture = texture
|
||||
end,
|
||||
|
||||
-- gets the item (or battle pet's) texture
|
||||
GetTexture = function(self)
|
||||
return self.texture
|
||||
end,
|
||||
|
||||
-- sets the alts table used for making other players count as the current player
|
||||
SetAlts = function(self, alts)
|
||||
self.alts = alts
|
||||
end,
|
||||
|
||||
-- sets the list of params we care about
|
||||
SetRecordParams = function(self, params)
|
||||
self.recordParams = params
|
||||
end,
|
||||
|
||||
-- sets the itemLink
|
||||
SetItemLink = function(self, itemLink)
|
||||
self.itemLink = itemLink
|
||||
end,
|
||||
|
||||
-- returns the itemString
|
||||
GetItemString = function(self)
|
||||
return TSMAPI:GetItemString(self.itemLink)
|
||||
end,
|
||||
|
||||
-- returns the itemID
|
||||
GetItemID = function(self)
|
||||
return TSMAPI:GetItemID(self.itemLink)
|
||||
end,
|
||||
|
||||
-- adds a record
|
||||
AddAuctionRecord = function(self, ...)
|
||||
local record = NewRecord()
|
||||
record:SetData(self, ...)
|
||||
-- if strfind(self.itemLink, "battlepet") then
|
||||
-- record.uniqueID = table.concat({TSMAPI:Select({2, 3, 4, 5, 6, 7}, (":"):split(self.itemLink))}, ".")
|
||||
-- else
|
||||
record.uniqueID = select(9, (":"):split(self.itemLink))
|
||||
-- end
|
||||
self:AddRecord(record)
|
||||
end,
|
||||
|
||||
-- adds a record
|
||||
AddRecord = function(self, record)
|
||||
self.shouldCompact = true
|
||||
if record:IsPlayer() then
|
||||
self.playerAuctions = self.playerAuctions + 1
|
||||
end
|
||||
tinsert(self.records, record)
|
||||
end,
|
||||
|
||||
-- sorts the records using the passed sortFunc
|
||||
SortRecords = function(self, sortFunc)
|
||||
sort(self.records, sortFunc)
|
||||
end,
|
||||
|
||||
-- sets the market value of this item
|
||||
SetMarketValue = function(self, value)
|
||||
self.marketValue = value
|
||||
end,
|
||||
|
||||
-- sorts all the records in ascending order by buyout > bid > count > seller
|
||||
DoDefaultSort = function(self)
|
||||
self:SortRecords(function(a, b)
|
||||
local aBuyout = a:GetItemBuyout()
|
||||
local bBuyout = b:GetItemBuyout()
|
||||
if not aBuyout or aBuyout == 0 then
|
||||
return false
|
||||
end
|
||||
if not bBuyout or bBuyout == 0 then
|
||||
return true
|
||||
end
|
||||
if aBuyout == bBuyout then
|
||||
if a.seller == b.seller then
|
||||
if a.count == b.count then
|
||||
local aBid = a:GetItemDisplayedBid()
|
||||
local bBid = b:GetItemDisplayedBid()
|
||||
return aBid < bBid
|
||||
end
|
||||
return a.count < b.count
|
||||
end
|
||||
return a.seller < b.seller
|
||||
end
|
||||
return aBuyout < bBuyout
|
||||
end)
|
||||
end,
|
||||
|
||||
-- populates the compactRecords table
|
||||
PopulateCompactRecords = function(self, sortParams, isAscending)
|
||||
if self.shouldCompact then
|
||||
self.shouldCompact = false
|
||||
self.compactRecords = {}
|
||||
self:DoDefaultSort()
|
||||
local currentRecord
|
||||
for _, record in ipairs(self.records) do
|
||||
local temp = record:Copy()
|
||||
if not currentRecord or not temp:Equals(currentRecord) then
|
||||
currentRecord = temp
|
||||
currentRecord.numAuctions = 1
|
||||
currentRecord.totalQuantity = currentRecord.count
|
||||
tinsert(self.compactRecords, currentRecord)
|
||||
else
|
||||
currentRecord.numAuctions = currentRecord.numAuctions + 1
|
||||
currentRecord.totalQuantity = currentRecord.totalQuantity + temp.count
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if sortParams then
|
||||
sort(self.compactRecords, function(a, b)
|
||||
for _, key in ipairs(sortParams) do
|
||||
local sortVal = sortHelpers[key](a, b)
|
||||
if sortVal < 0 then
|
||||
return isAscending
|
||||
elseif sortVal > 0 then
|
||||
return not isAscending
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
end,
|
||||
|
||||
-- removes all records for which shouldFilter(record) returns true
|
||||
FilterRecords = function(self, shouldFilter)
|
||||
self.shouldCompact = true
|
||||
local toRemove = {}
|
||||
for index, record in ipairs(self.records) do
|
||||
if shouldFilter(record) then
|
||||
tinsert(toRemove, index)
|
||||
end
|
||||
end
|
||||
|
||||
for i=#toRemove, 1, -1 do
|
||||
self:RemoveRecord(toRemove[i])
|
||||
end
|
||||
end,
|
||||
|
||||
-- removes a record at the given index
|
||||
RemoveRecord = function(self, index)
|
||||
local toRemove = self.records[index]
|
||||
if not toRemove then return end
|
||||
self.shouldCompact = true
|
||||
|
||||
if self.compactRecords then
|
||||
for i, record in ipairs(self.compactRecords) do
|
||||
if record:Equals(toRemove) then
|
||||
if record.numAuctions > 1 then
|
||||
record.numAuctions = record.numAuctions - 1
|
||||
else
|
||||
tremove(self.compactRecords, i)
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if toRemove:IsPlayer() then
|
||||
self.playerAuctions = self.playerAuctions - 1
|
||||
end
|
||||
|
||||
tremove(self.records, index)
|
||||
end,
|
||||
|
||||
-- adds up all the counts from all the records
|
||||
GetTotalItemQuantity = function(self)
|
||||
local totalQuantity = 0
|
||||
for _, record in ipairs(self.records) do
|
||||
totalQuantity = totalQuantity + record.count
|
||||
end
|
||||
return totalQuantity
|
||||
end,
|
||||
|
||||
-- counts up the number of items (not auctions) the player has
|
||||
GetPlayerItemQuantity = function(self)
|
||||
local totalQuantity = 0
|
||||
for _, record in ipairs(self.records) do
|
||||
if record:IsPlayer() then
|
||||
totalQuantity = totalQuantity + record.count
|
||||
end
|
||||
end
|
||||
return totalQuantity
|
||||
end,
|
||||
|
||||
IsPlayerOnly = function(self)
|
||||
for _, record in ipairs(self.records) do
|
||||
if not record:IsPlayer() then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end,
|
||||
|
||||
SetDestroyingNum = function(self, num)
|
||||
self.destroyingNum = num
|
||||
end,
|
||||
}
|
||||
|
||||
function TSMAPI.AuctionScan:NewAuctionItem()
|
||||
local o = {}
|
||||
setmetatable(o, AuctionItem)
|
||||
AuctionItem.__index = AuctionItem
|
||||
o:Initialize()
|
||||
return o
|
||||
end
|
||||
|
||||
|
||||
|
||||
local function CompareStrings(a, b)
|
||||
if a < b then
|
||||
return -1
|
||||
elseif a > b then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
-- bunch of helper functions for AuctionItem sorting
|
||||
-- negative return means a < b
|
||||
-- possitive return means a > b
|
||||
-- zero return means a == b
|
||||
sortHelpers = {
|
||||
Percent = function(a, b)
|
||||
return (a:GetPercent() or math.huge) - (b:GetPercent() or math.huge)
|
||||
end,
|
||||
|
||||
Buyout = function(a, b)
|
||||
return (a.buyout or math.huge) - (b.buyout or math.huge)
|
||||
end,
|
||||
|
||||
DisplayedBid = function(a, b)
|
||||
return a:GetDisplayedBid() - b:GetDisplayedBid()
|
||||
end,
|
||||
|
||||
ItemBuyout = function(a, b)
|
||||
return (a:GetItemBuyout() or math.huge) - (b:GetItemBuyout() or math.huge)
|
||||
end,
|
||||
|
||||
ItemDisplayedBid = function(a, b)
|
||||
return a:GetItemDisplayedBid() - b:GetItemDisplayedBid()
|
||||
end,
|
||||
|
||||
Count = function(a, b)
|
||||
return a.count - b.count
|
||||
end,
|
||||
|
||||
Seller = function(a, b)
|
||||
return CompareStrings(a.seller, b.seller)
|
||||
end,
|
||||
|
||||
TimeLeft = function(a, b)
|
||||
return a.timeLeft - b.timeLeft
|
||||
end,
|
||||
|
||||
NumAuctions = function(a, b)
|
||||
return a.numAuctions - b.numAuctions
|
||||
end,
|
||||
|
||||
Name = function(a, b)
|
||||
local aName = TSMAPI:GetSafeItemInfo(a.parent.itemLink)
|
||||
local bName = TSMAPI:GetSafeItemInfo(a.parent.itemLink)
|
||||
return CompareStrings(aName, bName)
|
||||
end,
|
||||
|
||||
DestroyingBuyout = function(a, b)
|
||||
return (a:GetItemDestroyingBuyout() or math.huge) - (b:GetItemDestroyingBuyout() or math.huge)
|
||||
end,
|
||||
}
|
||||
|
||||
function TSMAPI.AuctionScan:SortAuctions(data, sortParams, useCompactRecords, isAscending)
|
||||
local function compareSort(a, b)
|
||||
for _, key in ipairs(sortParams) do
|
||||
local sortVal
|
||||
if useCompactRecords then
|
||||
sortVal = sortHelpers[key](a.compactRecords[1], b.compactRecords[1])
|
||||
else
|
||||
sortVal = sortHelpers[key](a.records[1], b.records[1])
|
||||
end
|
||||
|
||||
if sortVal < 0 then
|
||||
return isAscending
|
||||
elseif sortVal > 0 then
|
||||
return not isAscending
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sort(data, compareSort)
|
||||
end
|
||||
@@ -0,0 +1,314 @@
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
-- TradeSkillMaster --
|
||||
-- http://www.curse.com/addons/wow/tradeskill-master --
|
||||
-- --
|
||||
-- A TradeSkillMaster Addon (http://tradeskillmaster.com) --
|
||||
-- All Rights Reserved* - Detailed license information included with addon. --
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
|
||||
-- This file contains code for scanning the auction house
|
||||
local TSM = select(2, ...)
|
||||
local private = {}
|
||||
TSMAPI:RegisterForTracing(private, "TradeSkillMaster.AuctionQueryUtil_private")
|
||||
|
||||
|
||||
local ITEM_CLASS_LOOKUP = {}
|
||||
for i, class in ipairs({GetAuctionItemClasses()}) do
|
||||
ITEM_CLASS_LOOKUP[class] = {}
|
||||
ITEM_CLASS_LOOKUP[class].index = i
|
||||
for j, subclass in pairs({GetAuctionItemSubClasses(i)}) do
|
||||
ITEM_CLASS_LOOKUP[class][subclass] = j
|
||||
end
|
||||
end
|
||||
|
||||
local function GetItemClasses(itemString)
|
||||
local class, subClass = select(6, TSMAPI:GetSafeItemInfo(itemString))
|
||||
if not class or not ITEM_CLASS_LOOKUP[class] then return end
|
||||
return ITEM_CLASS_LOOKUP[class].index, ITEM_CLASS_LOOKUP[class][subClass]
|
||||
end
|
||||
|
||||
function TSMAPI:GetAuctionQueryInfo(itemString)
|
||||
local name, _, rarity, _, minLevel, class, subClass, _, equipLoc = TSMAPI:GetSafeItemInfo(itemString)
|
||||
local class, subClass = GetItemClasses(itemString)
|
||||
if not name then return end
|
||||
return {name=name, minLevel=minLevel, maxLevel=minLevel, invType=0, class=class, subClass=subClass, quality=rarity}
|
||||
end
|
||||
|
||||
local function GetCommonQueryInfo(name, items)
|
||||
local queries = {}
|
||||
for _, itemString in ipairs(items) do
|
||||
local itemQuery = TSMAPI:GetAuctionQueryInfo(itemString)
|
||||
local existingQuery
|
||||
for _, query in ipairs(queries) do
|
||||
if query.class == itemQuery.class then
|
||||
existingQuery = query
|
||||
break
|
||||
end
|
||||
end
|
||||
if existingQuery then
|
||||
existingQuery.minLevel = min(existingQuery.minLevel, itemQuery.minLevel)
|
||||
existingQuery.maxLevel = max(existingQuery.maxLevel, itemQuery.maxLevel)
|
||||
existingQuery.quality = min(existingQuery.quality, itemQuery.quality)
|
||||
if existingQuery.subClass ~= itemQuery.subClass then
|
||||
existingQuery.subClass = nil
|
||||
end
|
||||
tinsert(existingQuery.items, itemString)
|
||||
else
|
||||
itemQuery.name = name
|
||||
itemQuery.items = {itemString}
|
||||
tinsert(queries, itemQuery)
|
||||
end
|
||||
end
|
||||
return queries
|
||||
end
|
||||
|
||||
local function GetCommonQueryInfoClass(class, items)
|
||||
local resultQuery = TSMAPI:GetAuctionQueryInfo(items[1])
|
||||
resultQuery.name = ""
|
||||
resultQuery.class = class
|
||||
for i=2, #items do
|
||||
local itemQuery = TSMAPI:GetAuctionQueryInfo(items[i])
|
||||
resultQuery.minLevel = min(resultQuery.minLevel, itemQuery.minLevel)
|
||||
resultQuery.maxLevel = max(resultQuery.maxLevel, itemQuery.maxLevel)
|
||||
resultQuery.quality = min(resultQuery.quality, itemQuery.quality)
|
||||
if resultQuery.subClass ~= itemQuery.subClass then resultQuery.subClass = nil end
|
||||
end
|
||||
resultQuery.items = items
|
||||
return {resultQuery}
|
||||
end
|
||||
|
||||
local function GreatestSubstring(str1, str2)
|
||||
local parts1 = {(" "):split(str1)}
|
||||
local parts2 = {(" "):split(str2)}
|
||||
for i=1, #parts1 do
|
||||
if parts1[i] ~= parts2[i] then
|
||||
local subStr = table.concat(parts1, " ", 1, i-1)
|
||||
return subStr ~= "" and subStr
|
||||
end
|
||||
end
|
||||
return table.concat(parts1, " ")
|
||||
end
|
||||
|
||||
local function ReduceStrings(strList)
|
||||
local didReduction = true
|
||||
while didReduction do
|
||||
didReduction = false
|
||||
for i=1, #strList-1 do
|
||||
if i > #strList-1 then break end
|
||||
local subStr = GreatestSubstring(strList[i], strList[i+1])
|
||||
if subStr then
|
||||
strList[i] = subStr
|
||||
tremove(strList, i+1)
|
||||
didReduction = true
|
||||
end
|
||||
end
|
||||
if not private.thread then return end
|
||||
private.thread:Yield()
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function NumPagesCallback(event, numPages)
|
||||
if event == "NUM_PAGES" then
|
||||
local skippedItems = {}
|
||||
local score = max(#private.combinedQueries[1].items-numPages, 0)
|
||||
if private.combinedQueries[1].name == "" then
|
||||
-- This is a common class term so determine if we should use this or not.
|
||||
local cost = 0
|
||||
for _, query in ipairs(private.queries) do
|
||||
if query.score and query.class == private.combinedQueries[1].class then
|
||||
cost = cost + query.score
|
||||
end
|
||||
end
|
||||
if score >= cost and score > 0 then
|
||||
-- use the common class term
|
||||
for i=#private.queries, 1, -1 do
|
||||
local query = private.queries[i]
|
||||
local shouldRemove = (query.class == private.combinedQueries[1].class)
|
||||
if shouldRemove then
|
||||
tremove(private.queries, i)
|
||||
end
|
||||
end
|
||||
tinsert(private.queries, private.combinedQueries[1])
|
||||
end
|
||||
else
|
||||
if numPages > #private.combinedQueries[1].items then
|
||||
for _, itemString in ipairs(private.combinedQueries[1].items) do
|
||||
local query = TSMAPI:GetAuctionQueryInfo(itemString)
|
||||
query.items = {itemString}
|
||||
query.score = 0
|
||||
tinsert(private.queries, query)
|
||||
end
|
||||
elseif numPages == 0 then
|
||||
for _, itemString in ipairs(private.combinedQueries[1].items) do
|
||||
tinsert(skippedItems, itemString)
|
||||
end
|
||||
else
|
||||
-- use the common search term
|
||||
private.combinedQueries[1].score = score
|
||||
tinsert(private.queries, private.combinedQueries[1])
|
||||
end
|
||||
end
|
||||
tremove(private.combinedQueries, 1)
|
||||
private.callback("QUERY_UPDATE", private.totalQueries-#private.combinedQueries, private.totalQueries, skippedItems)
|
||||
end
|
||||
private:CheckNextCombinedQuery()
|
||||
end
|
||||
|
||||
function private:CheckNextCombinedQuery()
|
||||
if not private.isScanning then return end
|
||||
|
||||
if #private.combinedQueries == 0 then
|
||||
-- we're done
|
||||
sort(private.queries, function(a, b) return a.name < b.name end)
|
||||
TSM:StopGeneratingQueries()
|
||||
TSMAPI:CreateTimeDelay("queryUtilCallbackDelay", 0.05, function() private.callback("QUERY_COMPLETE", private.queries) end)
|
||||
return
|
||||
end
|
||||
|
||||
for _, itemString in ipairs(private.combinedQueries[1].items) do
|
||||
if strlower(private.combinedQueries[1].name) == strlower(TSMAPI:GetSafeItemInfo(itemString)) then
|
||||
-- One of the items in this combined query is the same as the common search term,
|
||||
-- so it's always worth using this common search term.
|
||||
NumPagesCallback("NUM_PAGES", 1)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
TSMAPI.AuctionScan:GetNumPages(private.combinedQueries[1], NumPagesCallback)
|
||||
end
|
||||
|
||||
local function GenerateSearchTerms(names, itemList, isReversed)
|
||||
sort(names)
|
||||
if not ReduceStrings(names) then return end -- run the reduction
|
||||
|
||||
-- create a table associating all the reduced names to a list of items
|
||||
local temp = {}
|
||||
for i, filterName in ipairs(names) do
|
||||
for j, itemString in ipairs(itemList) do
|
||||
local itemName = TSMAPI:GetSafeItemInfo(itemString)
|
||||
itemName = itemName and isReversed and strrev(itemName) or itemName -- reverse item name if necessary
|
||||
if itemName and strfind(itemName, "^"..TSMAPI:StrEscape(filterName)) then
|
||||
temp[filterName] = temp[filterName] or {}
|
||||
tinsert(temp[filterName], itemString)
|
||||
end
|
||||
end
|
||||
if not private.thread then return end
|
||||
private.thread:Yield()
|
||||
end
|
||||
|
||||
return temp
|
||||
end
|
||||
|
||||
local function GenerateQueriesThread(self)
|
||||
private.thread = self
|
||||
local function GenerateFilters(reverse)
|
||||
-- create a list of all item names
|
||||
local names = {}
|
||||
for _, itemString in ipairs(private.itemList) do
|
||||
local name = TSMAPI:GetSafeItemInfo(itemString)
|
||||
if type(name) == "string" and name ~= "" then
|
||||
tinsert(names, reverse and strrev(name) or name)
|
||||
end
|
||||
end
|
||||
if not private.thread then return end
|
||||
|
||||
local filters, tempFilters, tempItems = {}, {}, {}
|
||||
local numFilters = 0
|
||||
local tbl = GenerateSearchTerms(names, private.itemList, reverse)
|
||||
if not tbl then return end
|
||||
for filterName, items in pairs(tbl) do
|
||||
if #items > 1 then
|
||||
filters[reverse and strrev(filterName) or filterName] = items
|
||||
numFilters = numFilters + 1
|
||||
else
|
||||
tinsert(tempFilters, strrev(filterName)) -- reverse name for second pass
|
||||
for _, itemString in ipairs(items) do
|
||||
tinsert(tempItems, itemString)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- try to find common search terms of reversed item names
|
||||
local tbl = GenerateSearchTerms(tempFilters, tempItems, not reverse)
|
||||
if not tbl then return end
|
||||
for filterName, items in pairs(tbl) do
|
||||
filters[reverse and filterName or strrev(filterName)] = items
|
||||
numFilters = numFilters + 1
|
||||
end
|
||||
|
||||
return filters, numFilters
|
||||
end
|
||||
|
||||
local endTime = debugprofilestop() + 5000
|
||||
while debugprofilestop() < endTime do
|
||||
-- request all the item info
|
||||
local tryAgain = false
|
||||
for _, itemString in ipairs(private.itemList) do
|
||||
if not TSMAPI:GetSafeItemInfo(itemString) then
|
||||
tryAgain = true
|
||||
end
|
||||
end
|
||||
if not tryAgain then break end
|
||||
self:Sleep(0.1)
|
||||
end
|
||||
|
||||
local filters1, num1 = GenerateFilters()
|
||||
local filters2, num2 = GenerateFilters(true)
|
||||
if not filters1 or not filters2 then return end
|
||||
local filters = num2 < num1 and filters2 or filters1
|
||||
|
||||
-- generate class filters
|
||||
local itemClasses = {}
|
||||
local classes = {GetAuctionItemClasses()}
|
||||
for _, itemString in ipairs(private.itemList) do
|
||||
local classIndex = GetItemClasses(itemString)
|
||||
if classIndex then
|
||||
itemClasses[classIndex] = itemClasses[classIndex] or {}
|
||||
tinsert(itemClasses[classIndex], itemString)
|
||||
end
|
||||
end
|
||||
|
||||
-- create the actual queries
|
||||
local queries, combinedQueries = {}, {}
|
||||
for filterName, items in pairs(filters) do
|
||||
for _, query in ipairs(GetCommonQueryInfo(filterName, items)) do
|
||||
if #query.items > 1 then
|
||||
tinsert(combinedQueries, query)
|
||||
else
|
||||
tinsert(queries, query)
|
||||
end
|
||||
end
|
||||
end
|
||||
for class, items in pairs(itemClasses) do
|
||||
for _, query in ipairs(GetCommonQueryInfoClass(class, items)) do
|
||||
if #query.items > 1 then
|
||||
tinsert(combinedQueries, query)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private.isScanning = true
|
||||
private.queries = queries
|
||||
private.combinedQueries = combinedQueries
|
||||
private.totalQueries = #combinedQueries
|
||||
end
|
||||
|
||||
function TSMAPI:GenerateQueries(itemList, callback)
|
||||
if private.thread then return end
|
||||
private.itemList = itemList
|
||||
private.callback = callback
|
||||
|
||||
local function ThreadDone()
|
||||
if private.thread then
|
||||
private.thread = nil
|
||||
private:CheckNextCombinedQuery()
|
||||
end
|
||||
end
|
||||
TSMAPI.Threading:Start(GenerateQueriesThread, 0.5, ThreadDone)
|
||||
end
|
||||
|
||||
function TSM:StopGeneratingQueries()
|
||||
private.thread = nil
|
||||
private.isScanning = nil
|
||||
end
|
||||
@@ -0,0 +1,730 @@
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
-- TradeSkillMaster --
|
||||
-- http://www.curse.com/addons/wow/tradeskill-master --
|
||||
-- --
|
||||
-- A TradeSkillMaster Addon (http://tradeskillmaster.com) --
|
||||
-- All Rights Reserved* - Detailed license information included with addon. --
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
|
||||
local TSM = select(2, ...)
|
||||
local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster") -- loads the localization table
|
||||
|
||||
local RT_COUNT = 1
|
||||
|
||||
local HEAD_HEIGHT = 27
|
||||
local HEAD_SPACE = 2
|
||||
local purchaseCache = {}
|
||||
local resultsTables = {}
|
||||
|
||||
|
||||
local function OnSizeChanged(rt, width)
|
||||
for i, col in ipairs(rt.headCols) do
|
||||
col:SetWidth(col.info.width*width)
|
||||
end
|
||||
|
||||
for _, row in ipairs(rt.rows) do
|
||||
for i, col in ipairs(row.cols) do
|
||||
col:SetWidth(rt.headCols[i].info.width*width)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local rowTextFunctions = {
|
||||
GetPriceText = function(buyout, displayBid)
|
||||
local bidLine = TSMAPI:FormatTextMoney(displayBid, "|cff999999", true) or "|cff999999---|r"
|
||||
local buyoutLine = buyout and buyout > 0 and TSMAPI:FormatTextMoney(buyout, nil, true) or "---"
|
||||
|
||||
if TSM.db.profile.showBids then
|
||||
return bidLine.."\n"..buyoutLine
|
||||
else
|
||||
return buyoutLine
|
||||
end
|
||||
end,
|
||||
|
||||
GetTimeLeftText = function(timeLeft)
|
||||
return _G["AUCTION_TIME_LEFT"..(timeLeft or "")] or ""
|
||||
end,
|
||||
|
||||
GetNameText = function(_, link)
|
||||
return gsub(gsub(link, "%[", ""), "%]", "")
|
||||
end,
|
||||
|
||||
GetAuctionsText = function(num, player, isExpandable, totalNum)
|
||||
num = totalNum or num
|
||||
local playerText = player and (" |cffffff00("..player..")|r") or ""
|
||||
|
||||
if isExpandable then
|
||||
return TSMAPI.Design:GetInlineColor("link2")..num.."|r"..playerText
|
||||
else
|
||||
return num..playerText
|
||||
end
|
||||
end,
|
||||
|
||||
GetSellerText = function(seller)
|
||||
if TSMAPI:IsPlayer(seller) then
|
||||
return "|cffffff00"..seller.."|r"
|
||||
else
|
||||
return seller or ""
|
||||
end
|
||||
end,
|
||||
|
||||
GetPercentText = function(pct)
|
||||
if not pct then return "---" end
|
||||
return TSMAPI:GetAuctionPercentColor(pct)..floor(pct+0.5).."%|r"
|
||||
end
|
||||
}
|
||||
|
||||
local function GetRowTable(rt, auction, isExpandable)
|
||||
if not auction then return end
|
||||
|
||||
local bid, buyout
|
||||
if TSM.db.profile.pricePerUnit then
|
||||
bid = auction:GetItemDisplayedBid()
|
||||
buyout = auction:GetItemBuyout()
|
||||
else
|
||||
bid = auction:GetDisplayedBid()
|
||||
buyout = auction.buyout
|
||||
end
|
||||
|
||||
local auctionsData, rowTable
|
||||
local itemString = auction.parent:GetItemString()
|
||||
if rt.expanded[itemString] then
|
||||
auctionsData = {#auction.parent.records, nil, nil, auction.numAuctions}
|
||||
else
|
||||
auctionsData = {#auction.parent.records, auction.parent.records[1].playerAuctions, isExpandable}
|
||||
end
|
||||
local name, iLvl
|
||||
-- if strmatch(auction.parent.itemLink, "battlepet") then
|
||||
-- local _, speciesID, itemLvl = strsplit(":", auction.parent.itemLink)
|
||||
-- local itemName = C_PetJournal.GetPetInfoBySpeciesID(speciesID)
|
||||
-- name, iLvl = itemName, itemLvl
|
||||
-- else
|
||||
local itemName, _, _, itemLvl = GetItemInfo(auction.parent.itemLink)
|
||||
name, iLvl = itemName, itemLvl
|
||||
-- end
|
||||
local pct = auction:GetPercent()
|
||||
if not pct or pct < 0 or pct == math.huge then
|
||||
pct = nil
|
||||
end
|
||||
|
||||
if rt.isDestroying then
|
||||
local destroyingBid = auction:GetItemDestroyingDisplayedBid()
|
||||
local destroyingBuyout = auction:GetItemDestroyingBuyout()
|
||||
rowTable = {
|
||||
{value=rowTextFunctions.GetNameText, args={name, auction.parent.itemLink}},
|
||||
{value=rowTextFunctions.GetAuctionsText, args=auctionsData},
|
||||
{value=auction.count, args={auction.count}},
|
||||
{value=rowTextFunctions.GetSellerText, args={auction.seller}},
|
||||
{value=rowTextFunctions.GetPriceText, args={destroyingBuyout, destroyingBid}},
|
||||
{value=rowTextFunctions.GetPriceText, args={buyout, bid}},
|
||||
{value=rowTextFunctions.GetPercentText, args={pct}},
|
||||
}
|
||||
else
|
||||
rowTable = {
|
||||
{value=rowTextFunctions.GetNameText, args={name, auction.parent.itemLink}},
|
||||
{value=(iLvl or "---"), args={iLvl or 0}},
|
||||
{value=rowTextFunctions.GetAuctionsText, args=auctionsData},
|
||||
{value=auction.count, args={auction.count}},
|
||||
{value=rowTextFunctions.GetTimeLeftText, args={auction.timeLeft}},
|
||||
{value=rowTextFunctions.GetSellerText, args={auction.seller}},
|
||||
{value=rowTextFunctions.GetPriceText, args={buyout, bid}},
|
||||
{value=rowTextFunctions.GetPercentText, args={pct}},
|
||||
}
|
||||
end
|
||||
|
||||
rowTable.itemString = itemString
|
||||
rowTable.auctionRecord = auction
|
||||
rowTable.expandable = isExpandable
|
||||
rowTable.texture = auction.parent:GetTexture()
|
||||
rowTable.link = auction.parent.itemLink
|
||||
|
||||
return rowTable
|
||||
end
|
||||
|
||||
local function GetTableIndex(tbl, value)
|
||||
for i, v in pairs(tbl) do
|
||||
if value == v then
|
||||
return i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function OnColumnClick(self, ...)
|
||||
local button = ...
|
||||
local rt = self.rt
|
||||
local column = GetTableIndex(rt.headCols, self)
|
||||
|
||||
if button == "RightButton" and column == #rt.headCols-1 then
|
||||
TSM.db.profile.pricePerUnit = not TSM.db.profile.pricePerUnit
|
||||
local priceColName = TSM.db.profile.pricePerUnit and L["Price Per Item"] or L["Price Per Stack"]
|
||||
self:SetText(priceColName)
|
||||
rt:RefreshRowData()
|
||||
return
|
||||
end
|
||||
|
||||
local ascending = rt.sortInfo.ascending
|
||||
rt:SetSort(column, rt.sortInfo.column ~= column or not ascending)
|
||||
|
||||
local handler = self.rt.handlers.OnColumnClick
|
||||
if handler then
|
||||
handler(self.rt, self.row.data, self, ...)
|
||||
end
|
||||
end
|
||||
|
||||
local methods = {
|
||||
DrawRows = function(rt)
|
||||
if not rt.auctionData then return end
|
||||
for i=1, rt.NUM_ROWS do
|
||||
rt.rows[i]:Hide()
|
||||
end
|
||||
|
||||
wipe(rt.displayRows)
|
||||
local itemsUsed = {}
|
||||
for i, data in ipairs(rt.data) do
|
||||
local itemString = data.itemString
|
||||
if not itemsUsed[itemString] or rt.expanded[itemString] then
|
||||
tinsert(rt.displayRows, data)
|
||||
itemsUsed[itemString] = true
|
||||
elseif i == rt.selected then
|
||||
rt.selected = nil
|
||||
end
|
||||
end
|
||||
|
||||
FauxScrollFrame_Update(rt.scrollFrame, #rt.displayRows, rt.NUM_ROWS, rt.ROW_HEIGHT)
|
||||
local offset = FauxScrollFrame_GetOffset(rt.scrollFrame)
|
||||
rt.offset = offset
|
||||
|
||||
for i=1, min(rt.NUM_ROWS, #rt.displayRows) do
|
||||
rt.rows[i]:Show()
|
||||
local data = rt.displayRows[i+offset]
|
||||
local cols = rt.rows[i].cols
|
||||
rt.rows[i].data = data
|
||||
|
||||
if rt.selected == GetTableIndex(rt.data, data) then
|
||||
rt.rows[i].highlight:Show()
|
||||
else
|
||||
rt.rows[i].highlight:Hide()
|
||||
end
|
||||
|
||||
for j, col in ipairs(rt.rows[i].cols) do
|
||||
local colData = data[j]
|
||||
|
||||
if j == 1 then
|
||||
col.icon:SetTexture(data.texture)
|
||||
if data.indented then
|
||||
col.spacer:SetWidth(10)
|
||||
col.icon:SetAlpha(0.5)
|
||||
col:GetFontString():SetAlpha(0.7)
|
||||
else
|
||||
col.spacer:SetWidth(1)
|
||||
col.icon:SetAlpha(1)
|
||||
col:GetFontString():SetAlpha(1)
|
||||
end
|
||||
end
|
||||
|
||||
if type(colData.value) == "function" then
|
||||
col:SetText(colData.value(unpack(colData.args)))
|
||||
else
|
||||
col:SetText(colData.value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
rt:UpdateActiveRows()
|
||||
end,
|
||||
|
||||
RefreshRowData = function(rt)
|
||||
if not rt.auctionData then return end
|
||||
wipe(rt.data)
|
||||
wipe(rt.displayRows)
|
||||
|
||||
local function RowSort(a, b)
|
||||
if a[rt.sortInfo.column].args[1] == nil then return end
|
||||
|
||||
local aVal
|
||||
local bVal
|
||||
if getn(a[rt.sortInfo.column].args) == 4 then
|
||||
aVal = a[rt.sortInfo.column].args[4]
|
||||
bVal = b[rt.sortInfo.column].args[4]
|
||||
else
|
||||
aVal = a[rt.sortInfo.column].args[1]
|
||||
bVal = b[rt.sortInfo.column].args[1]
|
||||
end
|
||||
-- local aVal = a[rt.sortInfo.column].args[1]
|
||||
-- local bVal = b[rt.sortInfo.column].args[1]
|
||||
|
||||
if type(aVal) ~= "string" or type(bVal) ~= "string" then
|
||||
aVal = tonumber(aVal) or 0
|
||||
bVal = tonumber(bVal) or 0
|
||||
end
|
||||
if aVal == bVal then
|
||||
-- make this a stable sort (abitrarily) by using table reference strings
|
||||
return tostring(a) < tostring(b)
|
||||
end
|
||||
if rt.sortInfo.ascending then
|
||||
return aVal < bVal
|
||||
else
|
||||
return aVal > bVal
|
||||
end
|
||||
end
|
||||
|
||||
local tmp = {}
|
||||
for _, auction in ipairs(rt.auctionData) do
|
||||
local itemString = auction:GetItemString()
|
||||
local itemRowData = {}
|
||||
for i, data in ipairs(auction.compactRecords) do
|
||||
local rowTbl = GetRowTable(rt, data, #auction.compactRecords > 1)
|
||||
rowTbl.indented = true
|
||||
tinsert(itemRowData, rowTbl)
|
||||
end
|
||||
|
||||
sort(itemRowData, RowSort)
|
||||
if itemRowData[1] then
|
||||
itemRowData[1].indented = false
|
||||
end
|
||||
tinsert(tmp, itemRowData)
|
||||
end
|
||||
|
||||
sort(tmp, function(a, b) return RowSort(a[1], b[1]) end)
|
||||
|
||||
for _, itemRows in ipairs(tmp) do
|
||||
for _, row in ipairs(itemRows) do
|
||||
tinsert(rt.data, row)
|
||||
end
|
||||
end
|
||||
|
||||
rt:DrawRows()
|
||||
end,
|
||||
|
||||
SetData = function(rt, auctionData)
|
||||
rt.auctionData = auctionData
|
||||
rt:RefreshRowData()
|
||||
end,
|
||||
|
||||
ClearSelection = function(rt)
|
||||
rt.selected = nil
|
||||
rt:DrawRows()
|
||||
end,
|
||||
|
||||
SetSelectedAuction = function(rt, auction)
|
||||
rt.selected = nil
|
||||
for i, data in ipairs(rt.data) do
|
||||
if type(auction) == "table" then
|
||||
if data.auctionRecord == auction or data.auctionRecord:Equals(auction) then
|
||||
rt.selected = i
|
||||
break
|
||||
end
|
||||
elseif type(auction) == "string" then
|
||||
if data.itemString == auction then
|
||||
rt.selected = i
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
rt:DrawRows()
|
||||
end,
|
||||
|
||||
GetSelectedAuction = function(rt)
|
||||
if not rt.selected or not rt.data[rt.selected] then return end
|
||||
return rt.data[rt.selected].auctionRecord
|
||||
end,
|
||||
|
||||
SetExpanded = function(rt, itemString, expanded)
|
||||
rt.expanded[itemString] = expanded
|
||||
rt:RefreshRowData()
|
||||
end,
|
||||
|
||||
ToggleExpanded = function(rt, itemString)
|
||||
rt.expanded[itemString] = not rt.expanded[itemString]
|
||||
rt:RefreshRowData()
|
||||
end,
|
||||
|
||||
SetSort = function(rt, column, ascending)
|
||||
if not rt.headCols[column or 0] then return end
|
||||
rt.sortInfo.column = column
|
||||
rt.sortInfo.ascending = ascending
|
||||
|
||||
for _, col in ipairs(rt.headCols) do
|
||||
local tex = col:GetNormalTexture()
|
||||
tex:SetTexture("Interface\\WorldStateFrame\\WorldStateFinalScore-Highlight")
|
||||
tex:SetTexCoord(0.017, 1, 0.083, 0.909)
|
||||
tex:SetAlpha(0.5)
|
||||
end
|
||||
|
||||
if ascending then
|
||||
rt.headCols[column]:GetNormalTexture():SetTexture(0.6, 0.8, 1, 0.8)
|
||||
else
|
||||
rt.headCols[column]:GetNormalTexture():SetTexture(0.8, 0.6, 1, 0.8)
|
||||
end
|
||||
rt:RefreshRowData()
|
||||
end,
|
||||
|
||||
SetDisabled = function(rt, disabled)
|
||||
rt.disabled = disabled
|
||||
end,
|
||||
|
||||
SetColHeadText = function(rt, column, text)
|
||||
rt.headCols[column]:SetText(text)
|
||||
end,
|
||||
|
||||
UpdateActiveRows = function(rt)
|
||||
if not rt.quickBuyout then return end
|
||||
for _, row in ipairs(rt.rows) do
|
||||
row:HideActiveBorder()
|
||||
if row.data then
|
||||
local rowRecord = row.data.auctionRecord
|
||||
for i=1, GetNumAuctionItems("list") do
|
||||
local itemString = TSMAPI:GetItemString(GetAuctionItemLink("list", i))
|
||||
-- local _, _, count, _, _, _, _, _, _, buyout, _, _, _, seller = GetAuctionItemInfo("list", i)
|
||||
local _, _, count, _, _, _, _, _, buyout, _, _, seller = GetAuctionItemInfo("list", i)
|
||||
if itemString == row.data.itemString and rowRecord.count == count and rowRecord.buyout == buyout and rowRecord.seller == seller then
|
||||
row:ShowActiveBorder()
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
wipe(purchaseCache)
|
||||
end,
|
||||
}
|
||||
|
||||
local defaultColScripts = {
|
||||
OnEnter = function(self, ...)
|
||||
if self.rt.disabled then return end
|
||||
|
||||
if self ~= self.row.cols[1] or not self.rt.isShowingItemTooltip then
|
||||
GameTooltip:SetOwner(self, "ANCHOR_NONE")
|
||||
GameTooltip:SetPoint("BOTTOMLEFT", self, "TOPLEFT")
|
||||
|
||||
local data = self.row.data
|
||||
local extra = ""
|
||||
if self.row.isActive then
|
||||
extra = TSMAPI.Design:GetInlineColor("link").."\n\n"..L["Alt-Click to immediately buyout this auction."].."|r"
|
||||
end
|
||||
if self.rt.expanded[data.itemString] then
|
||||
GameTooltip:AddLine(L["Double-click to collapse this item and show only the cheapest auction."]..extra, 1, 1, 1, true)
|
||||
elseif data.expandable then
|
||||
GameTooltip:AddLine(L["Double-click to expand this item and show all the auctions."]..extra, 1, 1, 1, true)
|
||||
else
|
||||
GameTooltip:AddLine(L["There is only one price level and seller for this item."]..extra, 1, 1, 1, true)
|
||||
end
|
||||
GameTooltip:Show()
|
||||
end
|
||||
|
||||
self.row.highlight:Show()
|
||||
|
||||
local handler = self.rt.handlers.OnEnter
|
||||
if handler then
|
||||
handler(self.rt, self.row.data, self, ...)
|
||||
end
|
||||
end,
|
||||
|
||||
OnLeave = function(self, ...)
|
||||
if self.rt.disabled then return end
|
||||
|
||||
if self ~= self.row.cols[1] or not self.rt.isShowingItemTooltip then
|
||||
GameTooltip:Hide()
|
||||
end
|
||||
|
||||
if not self.rt.selected or self.rt.selected ~= GetTableIndex(self.rt.data, self.row.data) then
|
||||
self.row.highlight:Hide()
|
||||
end
|
||||
|
||||
local handler = self.rt.handlers.OnLeave
|
||||
if handler then
|
||||
handler(self.rt, self.row.data, self, ...)
|
||||
end
|
||||
end,
|
||||
|
||||
OnClick = function(self, button, ...)
|
||||
if self.rt.disabled then return end
|
||||
self.rt:ClearSelection()
|
||||
self.rt.selected = GetTableIndex(self.rt.data, self.row.data)
|
||||
self.row.highlight:Show()
|
||||
|
||||
if self.rt.quickBuyout and IsAltKeyDown() then
|
||||
local rowRecord = self.row.data.auctionRecord
|
||||
for i=GetNumAuctionItems("list"), 1, -1 do
|
||||
local link = GetAuctionItemLink("list", i)
|
||||
if not purchaseCache[link] then
|
||||
local itemString = TSMAPI:GetItemString(link)
|
||||
-- local _, _, count, _, _, _, _, _, _, buyout, _, _, _, seller = GetAuctionItemInfo("list", i)
|
||||
local _, _, count, _, _, _, _, _, buyout, _, _, seller = GetAuctionItemInfo("list", i)
|
||||
if itemString == self.row.data.itemString and rowRecord.count == count and rowRecord.buyout == buyout and rowRecord.seller == seller then
|
||||
PlaceAuctionBid("list", i, rowRecord.buyout)
|
||||
TSM:AuctionControlCallback("OnBuyout", {itemString=TSMAPI:GetItemString(rowRecord.parent.itemLink), link=rowRecord.parent.itemLink, count=rowRecord.count, seller=rowRecord.seller, buyout=rowRecord.buyout, destroyingNum=rowRecord.parent.destroyingNum})
|
||||
purchaseCache[link] = true
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local handler = self.rt.handlers.OnClick
|
||||
if handler then
|
||||
handler(self.rt, self.row.data, self, button, ...)
|
||||
end
|
||||
end,
|
||||
|
||||
OnDoubleClick = function(self, ...)
|
||||
if self.rt.disabled then return end
|
||||
local data = self.row.data
|
||||
if data.expandable then
|
||||
self.rt:ToggleExpanded(data.itemString)
|
||||
end
|
||||
|
||||
local handler = self.rt.handlers.OnDoubleClick
|
||||
if handler then
|
||||
handler(self.rt, self.row.data, self, ...)
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
function TSMAPI:CreateAuctionResultsTable(parent, handlers, quickBuyout, isDestroying)
|
||||
local priceColName = TSM.db.profile.pricePerUnit and "Price Per Item" or "Price Per Stack"
|
||||
local colInfo = isDestroying and {
|
||||
{name=L["Item"], width=0.43},
|
||||
{name=L["Auctions"], width=0.07, align="CENTER"},
|
||||
{name=L["Stack Size"], width=0.05, align="CENTER"},
|
||||
{name=L["Seller"], width=0.11, align="CENTER"},
|
||||
{name=L["Price Per Target Item"], width=0.13, align="RIGHT", isPrice=true},
|
||||
{name=priceColName, width=0.13, align="RIGHT", isPrice=true},
|
||||
{name=L["% Market Value"], width=0.08, align="CENTER"},
|
||||
} or {
|
||||
{name=L["Item"], width=0.42},
|
||||
{name=L["Item Level"], width=0.05, align="CENTER"},
|
||||
{name=L["Auctions"], width=0.07, align="CENTER"},
|
||||
{name=L["Stack Size"], width=0.05, align="CENTER"},
|
||||
{name=L["Time Left"], width=0.09, align="CENTER"},
|
||||
{name=L["Seller"], width=0.11, align="CENTER"},
|
||||
{name=priceColName, width=0.13, align="RIGHT", isPrice=true},
|
||||
{name=L["% Market Value"], width=0.08, align="CENTER"},
|
||||
}
|
||||
|
||||
local rtName = "TSMAuctionResultsTable"..RT_COUNT
|
||||
RT_COUNT = RT_COUNT + 1
|
||||
local rt = CreateFrame("Frame", rtName, parent)
|
||||
rt.NUM_ROWS = TSM.db.profile.auctionResultRows
|
||||
rt.ROW_HEIGHT = (parent:GetHeight()-HEAD_HEIGHT-HEAD_SPACE)/rt.NUM_ROWS
|
||||
|
||||
rt:SetScript("OnShow", function()
|
||||
local priceColName = TSM.db.profile.pricePerUnit and L["Price Per Item"] or L["Price Per Stack"]
|
||||
rt:SetColHeadText(#rt.headCols-1, priceColName)
|
||||
rt:RefreshRowData()
|
||||
end)
|
||||
|
||||
local contentFrame = CreateFrame("Frame", rtName.."Content", rt)
|
||||
contentFrame:SetPoint("TOPLEFT")
|
||||
contentFrame:SetPoint("BOTTOMRIGHT", -15, 0)
|
||||
contentFrame:SetScript("OnSizeChanged", function(_, width) OnSizeChanged(rt, width) end)
|
||||
rt.contentFrame = contentFrame
|
||||
|
||||
-- frame to hold the header columns and the rows
|
||||
local scrollFrame = CreateFrame("ScrollFrame", rtName.."ScrollFrame", rt, "FauxScrollFrameTemplate")
|
||||
scrollFrame:SetScript("OnVerticalScroll", function(self, offset)
|
||||
FauxScrollFrame_OnVerticalScroll(self, offset, rt.ROW_HEIGHT, function() rt:DrawRows() end)
|
||||
end)
|
||||
scrollFrame:SetAllPoints(contentFrame)
|
||||
rt.scrollFrame = scrollFrame
|
||||
|
||||
-- make the scroll bar consistent with the TSM theme
|
||||
local scrollBar = _G[scrollFrame:GetName().."ScrollBar"]
|
||||
scrollBar:ClearAllPoints()
|
||||
scrollBar:SetPoint("BOTTOMRIGHT", rt, -1, 1)
|
||||
scrollBar:SetPoint("TOPRIGHT", rt, -1, -HEAD_HEIGHT)
|
||||
scrollBar:SetWidth(12)
|
||||
local thumbTex = scrollBar:GetThumbTexture()
|
||||
thumbTex:SetPoint("CENTER")
|
||||
TSMAPI.Design:SetFrameColor(thumbTex)
|
||||
thumbTex:SetHeight(150)
|
||||
thumbTex:SetWidth(scrollBar:GetWidth())
|
||||
_G[scrollBar:GetName().."ScrollUpButton"]:Hide()
|
||||
_G[scrollBar:GetName().."ScrollDownButton"]:Hide()
|
||||
|
||||
-- create the header columns
|
||||
rt.headCols = {}
|
||||
for i, info in ipairs(colInfo) do
|
||||
local col = CreateFrame("Button", rtName.."HeadCol"..i, rt.contentFrame)
|
||||
col:SetHeight(HEAD_HEIGHT)
|
||||
if i == 1 then
|
||||
col:SetPoint("TOPLEFT")
|
||||
else
|
||||
col:SetPoint("TOPLEFT", rt.headCols[i-1], "TOPRIGHT")
|
||||
end
|
||||
col.info = info
|
||||
col.rt = rt
|
||||
col:RegisterForClicks("AnyUp")
|
||||
col:SetScript("OnClick", OnColumnClick)
|
||||
|
||||
local text = col:CreateFontString()
|
||||
text:SetJustifyH("CENTER")
|
||||
text:SetJustifyV("CENTER")
|
||||
text:SetFont(TSMAPI.Design:GetContentFont("small"))
|
||||
TSMAPI.Design:SetWidgetTextColor(text)
|
||||
col:SetFontString(text)
|
||||
col:SetText(info.name or "")
|
||||
text:SetAllPoints()
|
||||
|
||||
local tex = col:CreateTexture()
|
||||
tex:SetAllPoints()
|
||||
tex:SetTexture("Interface\\WorldStateFrame\\WorldStateFinalScore-Highlight")
|
||||
tex:SetTexCoord(0.017, 1, 0.083, 0.909)
|
||||
tex:SetAlpha(0.5)
|
||||
col:SetNormalTexture(tex)
|
||||
|
||||
local tex = col:CreateTexture()
|
||||
tex:SetAllPoints()
|
||||
tex:SetTexture("Interface\\Buttons\\UI-Listbox-Highlight")
|
||||
tex:SetTexCoord(0.025, 0.957, 0.087, 0.931)
|
||||
tex:SetAlpha(0.2)
|
||||
col:SetHighlightTexture(tex)
|
||||
|
||||
tinsert(rt.headCols, col)
|
||||
end
|
||||
|
||||
-- create the rows
|
||||
rt.rows = {}
|
||||
for i=1, rt.NUM_ROWS do
|
||||
local row = CreateFrame("Frame", rtName.."Row"..i, rt.contentFrame)
|
||||
row:SetHeight(rt.ROW_HEIGHT)
|
||||
if i == 1 then
|
||||
row:SetPoint("TOPLEFT", 0, -(HEAD_HEIGHT+HEAD_SPACE))
|
||||
row:SetPoint("TOPRIGHT", 0, -(HEAD_HEIGHT+HEAD_SPACE))
|
||||
else
|
||||
row:SetPoint("TOPLEFT", rt.rows[i-1], "BOTTOMLEFT")
|
||||
row:SetPoint("TOPRIGHT", rt.rows[i-1], "BOTTOMRIGHT")
|
||||
end
|
||||
local highlight = row:CreateTexture()
|
||||
highlight:SetAllPoints()
|
||||
highlight:SetTexture(1, .9, 0, .5)
|
||||
highlight:Hide()
|
||||
row.highlight = highlight
|
||||
row.rt = rt
|
||||
|
||||
row.cols = {}
|
||||
for j=1, #colInfo do
|
||||
local col = CreateFrame("Button", nil, row)
|
||||
local text = col:CreateFontString()
|
||||
if TSM.db.profile.showBids and colInfo[j].isPrice then
|
||||
text:SetFont(TSMAPI.Design:GetContentFont(), min(13, rt.ROW_HEIGHT/2 - 2))
|
||||
else
|
||||
text:SetFont(TSMAPI.Design:GetContentFont(), min(14, rt.ROW_HEIGHT))
|
||||
end
|
||||
text:SetJustifyH(colInfo[j].align or "LEFT")
|
||||
text:SetJustifyV("CENTER")
|
||||
text:SetPoint("TOPLEFT", 1, -1)
|
||||
text:SetPoint("BOTTOMRIGHT", -1, 1)
|
||||
col:SetFontString(text)
|
||||
col:SetHeight(rt.ROW_HEIGHT)
|
||||
col:RegisterForClicks("AnyUp")
|
||||
for name, func in pairs(defaultColScripts) do
|
||||
col:SetScript(name, func)
|
||||
end
|
||||
col.rt = rt
|
||||
col.row = row
|
||||
col.rowNum = i
|
||||
|
||||
if j == 1 then
|
||||
col:SetPoint("TOPLEFT")
|
||||
else
|
||||
col:SetPoint("TOPLEFT", row.cols[j-1], "TOPRIGHT")
|
||||
end
|
||||
|
||||
if j%2 == 1 then
|
||||
local tex = col:CreateTexture()
|
||||
tex:SetAllPoints()
|
||||
tex:SetTexture(1, 1, 1, .03)
|
||||
col:SetNormalTexture(tex)
|
||||
end
|
||||
|
||||
-- special first column to hold spacer / item name / item icon
|
||||
if j == 1 then
|
||||
local spacer = CreateFrame("Frame", nil, col)
|
||||
spacer:SetPoint("TOPLEFT")
|
||||
spacer:SetHeight(rt.ROW_HEIGHT)
|
||||
spacer:SetWidth(1)
|
||||
col.spacer = spacer
|
||||
|
||||
local iconBtn = CreateFrame("Button", nil, col)
|
||||
iconBtn:SetBackdrop({edgeFile="Interface\\Buttons\\WHITE8X8", edgeSize=1.5})
|
||||
iconBtn:SetBackdropBorderColor(0, 1, 0, 0)
|
||||
iconBtn:SetPoint("TOPLEFT", spacer, "TOPRIGHT")
|
||||
iconBtn:SetHeight(rt.ROW_HEIGHT)
|
||||
iconBtn:SetWidth(rt.ROW_HEIGHT)
|
||||
iconBtn:SetScript("OnEnter", function(self)
|
||||
if row.data.link then
|
||||
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
|
||||
TSMAPI:SafeTooltipLink(row.data.link)
|
||||
GameTooltip:Show()
|
||||
rt.isShowingItemTooltip = true
|
||||
end
|
||||
end)
|
||||
iconBtn:SetScript("OnLeave", function(self)
|
||||
-- BattlePetTooltip:Hide()
|
||||
GameTooltip:ClearLines()
|
||||
GameTooltip:Hide()
|
||||
rt.isShowingItemTooltip = false
|
||||
end)
|
||||
iconBtn:SetScript("OnClick", function(_, ...)
|
||||
if IsModifiedClick() then
|
||||
HandleModifiedItemClick(row.data.auctionRecord.parent.itemLink)
|
||||
else
|
||||
col:GetScript("OnClick")(col, ...)
|
||||
end
|
||||
end)
|
||||
iconBtn:SetScript("OnDoubleClick", function(_, ...)
|
||||
col:GetScript("OnDoubleClick")(col, ...)
|
||||
end)
|
||||
local icon = iconBtn:CreateTexture(nil, "ARTWORK")
|
||||
icon:SetPoint("TOPLEFT", 2, -2)
|
||||
icon:SetPoint("BOTTOMRIGHT", -2, 2)
|
||||
col.iconBtn = iconBtn
|
||||
col.icon = icon
|
||||
|
||||
row.ShowActiveBorder = function()
|
||||
if rt.quickBuyout then
|
||||
row.isActive = true
|
||||
iconBtn:SetBackdropBorderColor(0, 1, 0, .7)
|
||||
end
|
||||
end
|
||||
|
||||
row.HideActiveBorder = function()
|
||||
row.isActive = nil
|
||||
iconBtn:SetBackdropBorderColor(0, 0, 0, 0)
|
||||
end
|
||||
|
||||
text:ClearAllPoints()
|
||||
text:SetPoint("TOPLEFT", iconBtn, "TOPRIGHT", 2, 0)
|
||||
text:SetPoint("BOTTOMRIGHT")
|
||||
end
|
||||
tinsert(row.cols, col)
|
||||
end
|
||||
|
||||
if i%2 == 0 then
|
||||
local tex = row:CreateTexture()
|
||||
tex:SetAllPoints()
|
||||
tex:SetTexture("Interface\\WorldStateFrame\\WorldStateFinalScore-Highlight")
|
||||
tex:SetTexCoord(0.017, 1, 0.083, 0.909)
|
||||
tex:SetAlpha(0.3)
|
||||
end
|
||||
|
||||
tinsert(rt.rows, row)
|
||||
end
|
||||
|
||||
rt:SetAllPoints()
|
||||
rt.data = {}
|
||||
rt.expanded = {}
|
||||
rt.displayRows = {}
|
||||
rt.handlers = handlers or {}
|
||||
rt.sortInfo = {}
|
||||
rt.quickBuyout = quickBuyout
|
||||
rt.isDestroying = isDestroying
|
||||
tinsert(resultsTables, rt)
|
||||
|
||||
for name, func in pairs(methods) do
|
||||
rt[name] = func
|
||||
end
|
||||
LibStub("AceEvent-3.0").RegisterEvent(rt, "AUCTION_ITEM_LIST_UPDATE", "UpdateActiveRows")
|
||||
|
||||
return rt
|
||||
end
|
||||
@@ -0,0 +1,628 @@
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
-- TradeSkillMaster --
|
||||
-- http://www.curse.com/addons/wow/tradeskill-master --
|
||||
-- --
|
||||
-- A TradeSkillMaster Addon (http://tradeskillmaster.com) --
|
||||
-- All Rights Reserved* - Detailed license information included with addon. --
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
|
||||
-- This file contains code for scanning the auction house
|
||||
local TSM = select(2, ...)
|
||||
local AuctionScanning = TSM:NewModule("AuctionScanning", "AceEvent-3.0")
|
||||
TSMAPI.AuctionScan = {}
|
||||
|
||||
local RETRY_DELAY = 2
|
||||
local MAX_RETRIES = 4
|
||||
local BASE_DELAY = 0.10 -- time to delay for before trying to scan a page again when it isn't fully loaded
|
||||
local private = { callbackHandler = nil, query = {}, options = {}, data = {}, isScanning = nil }
|
||||
TSMAPI:RegisterForTracing(private, "TradeSkillMaster.AuctionScanning_private")
|
||||
local scanCache = {}
|
||||
|
||||
local CACHE_DECAY_PER_DAY = 5
|
||||
local CACHE_AUTO_HIT_TIME = 10 * 60
|
||||
local SECONDS_PER_DAY = 60 * 60 * 24
|
||||
|
||||
|
||||
local function DoCallback(...)
|
||||
if type(private.callbackHandler) == "function" then
|
||||
private.callbackHandler(...)
|
||||
end
|
||||
end
|
||||
|
||||
local function eventHandler(event)
|
||||
if event == "AUCTION_HOUSE_CLOSED" then
|
||||
-- auction house was closed, make sure all scanning is stopped
|
||||
AuctionScanning:UnregisterEvent("AUCTION_ITEM_LIST_UPDATE")
|
||||
private.auctionHouseShown = false
|
||||
DoCallback("INTERRUPTED")
|
||||
private:StopScanning()
|
||||
elseif event == "AUCTION_ITEM_LIST_UPDATE" then
|
||||
-- gets called whenever the AH window is updated (something is shown in the results section)
|
||||
AuctionScanning:UnregisterEvent("AUCTION_ITEM_LIST_UPDATE")
|
||||
TSMAPI:CancelFrame("updateDelay")
|
||||
-- now that our query was successful, we can get our data
|
||||
private:ScanAuctions()
|
||||
end
|
||||
end
|
||||
|
||||
function AuctionScanning:OnEnable()
|
||||
AuctionScanning:RegisterEvent("AUCTION_HOUSE_CLOSED", eventHandler)
|
||||
end
|
||||
|
||||
function private:ScanAuctionPage(resolveSellers)
|
||||
local shown = GetNumAuctionItems("list")
|
||||
local badData = false
|
||||
local auctions = {}
|
||||
|
||||
for i = 1, shown do
|
||||
-- checks to make sure all the data has been sent to the client
|
||||
-- if not, the data is bad and we'll wait / try again
|
||||
-- local count, _, _, _, _, _, _, buyout, _, _, _, seller = select(3, GetAuctionItemInfo("list", i))
|
||||
local count, _, _, _, _, _, buyout, _, _, seller = select(3, GetAuctionItemInfo("list", i))
|
||||
local itemString = TSMAPI:GetItemString(GetAuctionItemLink("list", i))
|
||||
auctions[i] = { itemString = itemString, index = i, count = count, buyout = buyout, seller = seller }
|
||||
if not (itemString and buyout and count and (seller or not resolveSellers or buyout == 0)) then
|
||||
badData = true
|
||||
end
|
||||
end
|
||||
|
||||
return badData, auctions
|
||||
end
|
||||
|
||||
function IsDuplicatePage()
|
||||
if not private.pageTemp or GetNumAuctionItems("list") == 0 then return false end
|
||||
|
||||
local numLinks, prevLink = 0, nil
|
||||
for i = 1, GetNumAuctionItems("list") do
|
||||
-- local _, _, count, _, _, _, _, minBid, minInc, buyout, bid, _, _, seller = GetAuctionItemInfo("list", i)
|
||||
local _, _, count, _, _, _, minBid, minInc, buyout, bid, _, seller = GetAuctionItemInfo("list", i)
|
||||
local link = GetAuctionItemLink("list", i)
|
||||
local temp = private.pageTemp[i]
|
||||
|
||||
if not prevLink then
|
||||
prevLink = link
|
||||
elseif prevLink ~= link then
|
||||
prevLink = link
|
||||
numLinks = numLinks + 1
|
||||
end
|
||||
|
||||
if not temp or temp.count ~= count or temp.minBid ~= minBid or temp.minInc ~= minInc or temp.buyout ~= buyout or temp.bid ~= bid or temp.seller ~= seller or temp.link ~= link then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
if numLinks > 1 and private.pageTemp.shown == GetNumAuctionItems("list") then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function PopulatePageTemp()
|
||||
local shown = GetNumAuctionItems("list")
|
||||
private.pageTemp = { numShown = shown }
|
||||
|
||||
for i = 1, shown do
|
||||
-- checks to make sure all the data has been sent to the client
|
||||
-- if not, the data is bad and we'll wait / try again
|
||||
-- local _, _, count, _, _, _, _, minBid, minInc, buyout, bid, _, seller = GetAuctionItemInfo("list", i)
|
||||
local _, _, count, _, _, _, minBid, minInc, buyout, bid, _, seller = GetAuctionItemInfo("list", i)
|
||||
local link = GetAuctionItemLink("list", i)
|
||||
|
||||
private.pageTemp[i] = { count = count, minBid = minBid, minInc = minInc, buyout = buyout, bid = bid, seller = seller, link = link }
|
||||
end
|
||||
end
|
||||
|
||||
-- Starts a scan of the auction house.
|
||||
-- query - A single query containing QueryAuctionItem paramters:
|
||||
-- name, minLevel, maxLevel, invType, class, subClass, usable, quality
|
||||
-- resolveSellers - whether or not to resolve seller names
|
||||
-- maxPrice - stop scanning when prices go above this price
|
||||
function TSMAPI.AuctionScan:RunQuery(query, callbackHandler, resolveSellers, maxPrice, doCache)
|
||||
TSMAPI.AuctionScan:StopScan() -- stop any scan in progress
|
||||
|
||||
if not AuctionFrame:IsVisible() then
|
||||
return -1 -- the auction house isn't open (return code -1)
|
||||
elseif type(query) ~= "table" then
|
||||
return -2 -- the scan queue is not a table (return code -2)
|
||||
elseif not CanSendAuctionQuery() then
|
||||
TSMAPI:CreateTimeDelay("cantSendAuctionQueryDelay", 0.1, function() TSMAPI.AuctionScan:RunQuery(query, callbackHandler, resolveSellers, maxPrice, doCache) end)
|
||||
return 0 -- the query will start as soon as it can but did not start immediately (return code 0)
|
||||
end
|
||||
|
||||
-- sort by buyout
|
||||
SortAuctionItems("list", "buyout")
|
||||
if IsAuctionSortReversed("list", "buyout") then
|
||||
SortAuctionItems("list", "buyout")
|
||||
end
|
||||
|
||||
-- setup the query
|
||||
private.query = CopyTable(query)
|
||||
private.query.page = 0 -- the current page of this query we're scanning
|
||||
private.query.timeDelay = 0 -- a delay used to wait for information to show up
|
||||
private.query.retries = 0 -- how many times we've done a hard retry so far
|
||||
private.query.hardRetry = nil -- if a page hasn't loaded after we've tried a delay, we'll do a hard retry and re-send the query
|
||||
private.cache = doCache and { query = CopyTable(query), items = {} } or nil
|
||||
|
||||
-- setup other stuff
|
||||
wipe(private.data)
|
||||
private.isScanning = true
|
||||
private.callbackHandler = callbackHandler
|
||||
private.resolveSellers = resolveSellers
|
||||
private.scanType = "query"
|
||||
private.maxPrice = maxPrice or math.huge
|
||||
|
||||
--starts scanning
|
||||
private:SendQuery()
|
||||
return 1 -- scan started successfully (return code 1)
|
||||
end
|
||||
|
||||
function TSMAPI.AuctionScan:ScanLastPage(callbackHandler)
|
||||
private:StopScanning() -- stop any scan in progress
|
||||
|
||||
if not AuctionFrame:IsVisible() then
|
||||
return -1 -- the auction house isn't open (return code -1)
|
||||
elseif not CanSendAuctionQuery() then
|
||||
TSMAPI:CreateTimeDelay("cantSendAuctionQueryDelay", 0.1, function() TSMAPI.AuctionScan:ScanLastPage(callbackHandler) end)
|
||||
return 0 -- the query will start as soon as it can but did not start immediately (return code 0)
|
||||
end
|
||||
|
||||
-- clear the auction sort
|
||||
SortAuctionClearSort("list")
|
||||
|
||||
-- setup the query
|
||||
private.query = {name="", page=0}
|
||||
private.query.timeDelay = 0 -- a delay used to wait for information to show up
|
||||
private.query.retries = 0 -- how many times we've done a hard retry so far
|
||||
private.query.hardRetry = nil -- if a page hasn't loaded after we've tried a delay, we'll do a hard retry and re-send the query
|
||||
|
||||
-- setup other stuff
|
||||
wipe(private.data)
|
||||
private.isScanning = true
|
||||
private.callbackHandler = callbackHandler
|
||||
private.scanType = "lastPage"
|
||||
|
||||
--starts scanning
|
||||
private:SendQuery()
|
||||
return 1 -- scan started successfully (return code 1)
|
||||
end
|
||||
|
||||
-- sends a query to the AH frame once it is ready to be queried (uses frame as a delay)
|
||||
function private:SendQuery()
|
||||
if not private.isScanning then return end
|
||||
|
||||
if CanSendAuctionQuery() then
|
||||
-- stop delay timer
|
||||
TSMAPI:CancelFrame("queryDelay")
|
||||
|
||||
-- Query the auction house (then waits for AUCTION_ITEM_LIST_UPDATE to fire)
|
||||
AuctionScanning:RegisterEvent("AUCTION_ITEM_LIST_UPDATE", eventHandler)
|
||||
-- [exact] cardinal ruby 0 0 nil 0 0 0 0 0
|
||||
-- [normal] cardinal ruby nil nil nil nil nil 0 nil nil
|
||||
QueryAuctionItems(private.query.name, private.query.minLevel, private.query.maxLevel, private.query.invType, private.query.class, private.query.subClass, private.query.page, private.query.usable, private.query.quality)
|
||||
else
|
||||
-- run delay timer then try again to scan
|
||||
TSMAPI:CreateTimeDelay("queryDelay", 0.05, private.SendQuery)
|
||||
end
|
||||
end
|
||||
|
||||
--scans the currently shown page of auctions and collects all the data
|
||||
function private:ScanAuctions()
|
||||
if not private.isScanning then return end
|
||||
local shown, total = GetNumAuctionItems("list")
|
||||
local totalPages = ceil(total / NUM_AUCTION_ITEMS_PER_PAGE)
|
||||
|
||||
if private.scanType == "numPages" then
|
||||
local cacheData = TSM.db.factionrealm.numPagesCache[private.query.cacheKey]
|
||||
cacheData.lastScan = time()
|
||||
local confidence = (120 - cacheData.confidence) / (CACHE_DECAY_PER_DAY * 2)
|
||||
local diff = abs(cacheData.avg - totalPages)
|
||||
if diff <= 1 and diff > 0.5 then
|
||||
confidence = floor(confidence * (1.5 - diff))
|
||||
elseif diff > 1 then
|
||||
confidence = floor(confidence - CACHE_DECAY_PER_DAY * diff)
|
||||
end
|
||||
cacheData.confidence = max(floor(cacheData.confidence + confidence), 0)
|
||||
cacheData.avg = (cacheData.avg * cacheData.numScans + totalPages) / (cacheData.numScans + 1)
|
||||
cacheData.numScans = cacheData.numScans + 1
|
||||
|
||||
private:StopScanning()
|
||||
return DoCallback("NUM_PAGES", totalPages)
|
||||
elseif private.scanType == "lastPage" then
|
||||
local lastPage = floor(total / NUM_AUCTION_ITEMS_PER_PAGE)
|
||||
if private.query.page ~= lastPage then
|
||||
private.query.page = lastPage
|
||||
return private:SendQuery()
|
||||
end
|
||||
end
|
||||
|
||||
local dataIsBad, auctions = private:ScanAuctionPage(private.resolveSellers)
|
||||
|
||||
-- check that we have good data
|
||||
if dataIsBad or IsDuplicatePage() then
|
||||
if private.query.retries < MAX_RETRIES then
|
||||
if private.query.hardRetry then
|
||||
-- Hard retry
|
||||
-- re-sends the entire query
|
||||
private.query.retries = private.query.retries + 1
|
||||
private.query.timeDelay = 0
|
||||
private.query.hardRetry = nil
|
||||
private:SendQuery()
|
||||
else
|
||||
-- Soft retry
|
||||
-- runs a delay and then tries to scan the query again
|
||||
private.query.timeDelay = private.query.timeDelay + BASE_DELAY
|
||||
TSMAPI:CreateTimeDelay("updateDelay", BASE_DELAY, private.ScanAuctions)
|
||||
|
||||
-- If after 2 seconds of retrying we still don't have data, will go and requery to try and solve the issue
|
||||
-- if we still don't have data, we try to scan it anyway and move on.
|
||||
if private.query.timeDelay >= RETRY_DELAY then
|
||||
private.query.hardRetry = true
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if private.cache then
|
||||
-- store info in cache
|
||||
for i, v in ipairs(auctions) do
|
||||
local cacheTmp = CopyTable(v)
|
||||
cacheTmp.index = private.query.page * 50 + i
|
||||
tinsert(private.cache, cacheTmp)
|
||||
private.cache.items[cacheTmp.itemString] = true
|
||||
end
|
||||
end
|
||||
|
||||
private.query.hardRetry = nil
|
||||
private.query.retries = 0
|
||||
private.query.timeDelay = 0
|
||||
if private.scanType ~= "lastPage" then
|
||||
private.query.page = private.query.page + 1 -- increment current page
|
||||
if totalPages > 0 then
|
||||
DoCallback("SCAN_PAGE_UPDATE", private.query.page, totalPages)
|
||||
end
|
||||
end
|
||||
PopulatePageTemp()
|
||||
|
||||
-- now that we know our query is good, time to verify and then store our data
|
||||
for _, v in ipairs(auctions) do
|
||||
if private:AddAuctionRecord(v.index) then
|
||||
-- we've hit the max price so we're done scanning
|
||||
private:StopScanning()
|
||||
return DoCallback("SCAN_COMPLETE", private.data)
|
||||
end
|
||||
end
|
||||
|
||||
if private.scanType == "lastPage" then
|
||||
return DoCallback("SCAN_LAST_PAGE_COMPLETE", private.data)
|
||||
elseif private.query.page >= totalPages then
|
||||
-- we have finished scanning this query
|
||||
private:StopScanning()
|
||||
return DoCallback("SCAN_COMPLETE", private.data)
|
||||
end
|
||||
|
||||
-- query the next page and continue scanning
|
||||
private:SendQuery()
|
||||
end
|
||||
|
||||
-- Add a new record to the private.data table
|
||||
function private:AddAuctionRecord(index)
|
||||
-- local name, texture, count, _, _, _, _, minBid, minIncrement, buyout, bid, highBidder, highBidder_full, seller, seller_full = GetAuctionItemInfo("list", index)
|
||||
local name, texture, count, _, _, _, minBid, minIncrement, buyout, bid, highBidder, seller = GetAuctionItemInfo("list", index)
|
||||
seller = TSM:GetAuctionPlayer(seller, null)
|
||||
highBidder = TSM:GetAuctionPlayer(highBidder, null)
|
||||
local timeLeft = GetAuctionItemTimeLeft("list", index)
|
||||
local link = GetAuctionItemLink("list", index)
|
||||
local itemString = TSMAPI:GetItemString(link)
|
||||
if not itemString then return end
|
||||
|
||||
-- Create a new entry in the table
|
||||
if not private.data[itemString] then
|
||||
private.data[itemString] = TSMAPI.AuctionScan:NewAuctionItem()
|
||||
private.data[itemString]:SetItemLink(link)
|
||||
private.data[itemString]:SetTexture(texture)
|
||||
end
|
||||
private.data[itemString]:AddAuctionRecord(count, minBid, minIncrement, buyout, bid, highBidder, seller or "?", timeLeft)
|
||||
|
||||
-- add the base item if necessary
|
||||
local baseItemString = TSMAPI:GetBaseItemString(itemString)
|
||||
if baseItemString ~= itemString then
|
||||
-- Create a new entry in the table
|
||||
if not private.data[baseItemString] then
|
||||
private.data[baseItemString] = TSMAPI.AuctionScan:NewAuctionItem()
|
||||
private.data[baseItemString]:SetItemLink(link)
|
||||
private.data[baseItemString]:SetTexture(texture)
|
||||
end
|
||||
private.data[baseItemString]:AddAuctionRecord(count, minBid, minIncrement, buyout, bid, highBidder, seller or "?", timeLeft)
|
||||
private.data[baseItemString].isBaseItem = true
|
||||
end
|
||||
|
||||
if select(8, TSMAPI:GetSafeItemInfo(link)) == count then
|
||||
return (buyout or 0) / count > (private.maxPrice or math.huge)
|
||||
end
|
||||
end
|
||||
|
||||
-- stops the scan when we are finished scanning, it was interrupted, or somebody stopped it
|
||||
function private:StopScanning()
|
||||
TSMAPI:CancelFrame("cantSendAuctionQueryDelay")
|
||||
if not private.isScanning then return end
|
||||
|
||||
if private.cache then
|
||||
-- store the cache info
|
||||
sort(private.cache, function(a, b) return a.index < b.index end)
|
||||
for itemString in pairs(private.cache.items) do
|
||||
scanCache[itemString] = private.cache
|
||||
end
|
||||
wipe(private.cache.items)
|
||||
private.cache = nil
|
||||
end
|
||||
|
||||
-- cancel any delays that might still be running
|
||||
TSMAPI:CancelFrame("queryDelay")
|
||||
TSMAPI:CancelFrame("updateDelay")
|
||||
AuctionScanning:UnregisterEvent("AUCTION_ITEM_LIST_UPDATE")
|
||||
private.isScanning = nil
|
||||
private.pageTemp = nil
|
||||
end
|
||||
|
||||
-- API for stopping the scan
|
||||
-- returns true/false if we were/weren't actually scanning
|
||||
function TSMAPI.AuctionScan:StopScan()
|
||||
private:StopScanning()
|
||||
TSM:StopGeneratingQueries()
|
||||
end
|
||||
|
||||
|
||||
-- Gets the number of pages for a given query
|
||||
function TSMAPI.AuctionScan:GetNumPages(query, callbackHandler)
|
||||
private:StopScanning() -- stop any scan in progress
|
||||
|
||||
if not AuctionFrame:IsVisible() then
|
||||
return -1 -- the auction house isn't open (return code -1)
|
||||
elseif type(query) ~= "table" then
|
||||
return -2 -- the scan queue is not a table (return code -2)
|
||||
elseif not CanSendAuctionQuery() then
|
||||
TSMAPI:CreateTimeDelay("cantSendAuctionQueryDelay", 0.1, function() TSMAPI.AuctionScan:GetNumPages(query, callbackHandler) end)
|
||||
return 0 -- the query will start as soon as it can but did not start immediately (return code 0)
|
||||
end
|
||||
|
||||
-- fancy caching
|
||||
local temp = {}
|
||||
for i, field in ipairs({ "name", "minLevel", "maxLevel", "invType", "class", "subClass", "usable", "quality" }) do
|
||||
temp[i] = tostring(query[field])
|
||||
end
|
||||
local cacheKey = table.concat(temp, "~")
|
||||
local cacheData = TSM.db.factionrealm.numPagesCache[cacheKey]
|
||||
if cacheData then
|
||||
local cacheHit
|
||||
if time() - cacheData.lastScan < CACHE_AUTO_HIT_TIME then
|
||||
-- auto cache hit
|
||||
cacheHit = true
|
||||
elseif random(1, 100) <= cacheData.confidence then
|
||||
-- cache hit
|
||||
cacheData.confidence = cacheData.confidence - floor(((time() - cacheData.lastScan) / SECONDS_PER_DAY) * CACHE_DECAY_PER_DAY + 0.5)
|
||||
cacheData.confidence = max(cacheData.confidence, 0) -- ensure >= 0
|
||||
cacheHit = true
|
||||
end
|
||||
|
||||
if cacheHit then
|
||||
local numPages = max(ceil(cacheData.avg), 1) -- round avg num of pages up and ensure >= 1
|
||||
TSMAPI:CreateTimeDelay("numPagesCacheDelay", 0, function() callbackHandler("NUM_PAGES", numPages) end)
|
||||
return 2
|
||||
end
|
||||
else
|
||||
TSM.db.factionrealm.numPagesCache[cacheKey] = { avg = 0, confidence = 0, numScans = 0, lastScan = 0 }
|
||||
end
|
||||
|
||||
-- setup the query
|
||||
private.query = CopyTable(query)
|
||||
private.query.cacheKey = cacheKey
|
||||
|
||||
-- setup other stuff
|
||||
wipe(private.data)
|
||||
private.isScanning = true
|
||||
private.callbackHandler = callbackHandler
|
||||
private.scanType = "numPages"
|
||||
|
||||
--starts scanning
|
||||
private:SendQuery()
|
||||
return 1 -- scan started successfully (return code 1)
|
||||
end
|
||||
|
||||
function TSMAPI.AuctionScan:CacheRemove(itemString, index)
|
||||
if scanCache[itemString] then
|
||||
tremove(scanCache[itemString], index)
|
||||
end
|
||||
end
|
||||
|
||||
function TSMAPI.AuctionScan:ClearCache()
|
||||
wipe(scanCache)
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
local findPrivate = {}
|
||||
findPrivate.findFrame = findPrivate.findFrame or CreateFrame("Frame")
|
||||
|
||||
local function eventHandler(frame, event)
|
||||
if event == "AUCTION_HOUSE_SHOW" then
|
||||
-- auction house was opened
|
||||
elseif event == "AUCTION_HOUSE_CLOSED" then
|
||||
frame:UnregisterEvent("AUCTION_ITEM_LIST_UPDATE")
|
||||
if findPrivate.isScanning then -- stop scanning if we were scanning (pass true to specify it was interrupted)
|
||||
TSMAPI.AuctionScan:StopFindScan()
|
||||
end
|
||||
elseif event == "AUCTION_ITEM_LIST_UPDATE" then
|
||||
frame:UnregisterEvent("AUCTION_ITEM_LIST_UPDATE")
|
||||
if findPrivate.isScanning then
|
||||
findPrivate.timeDelay = 0
|
||||
TSMAPI:CancelFrame("auctionFindScanDelay")
|
||||
|
||||
-- now that our query was successful we can get our data
|
||||
findPrivate:ScanAuctions()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
findPrivate.findFrame:SetScript("OnEvent", eventHandler)
|
||||
findPrivate.findFrame:RegisterEvent("AUCTION_HOUSE_CLOSED")
|
||||
findPrivate.findFrame:RegisterEvent("AUCTION_HOUSE_SHOW")
|
||||
|
||||
local function CompareTableKeys(tbl1, tbl2)
|
||||
for _, key in ipairs(findPrivate.keys) do
|
||||
if tbl1[key] ~= tbl2[key] then
|
||||
return
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function IsTargetAuction(index)
|
||||
local itemString = TSMAPI:GetItemString(GetAuctionItemLink("list", index))
|
||||
-- local _, _, count, _, _, _, _, minBid, bidIncrement, buyout, bidAmount, _, _, seller, seller_full = GetAuctionItemInfo("list", index)
|
||||
local _, _, count, _, _, _, minBid, bidIncrement, buyout, bidAmount, _, _, seller = GetAuctionItemInfo("list", index)
|
||||
seller = TSM:GetAuctionPlayer(seller, nil)
|
||||
local bid = bidAmount == 0 and minBid or bidAmount
|
||||
local tmp = { itemString = itemString, count = count, bid = bid, buyout = buyout, seller = seller }
|
||||
return CompareTableKeys(tmp, findPrivate.targetInfo)
|
||||
end
|
||||
|
||||
-- valid targetInfo keys: itemString, count, bid, buyout, seller
|
||||
function TSMAPI.AuctionScan:FindAuction(callback, targetInfo, useCache)
|
||||
if findPrivate.isScanning then TSMAPI.AuctionScan:StopFindScan() end
|
||||
|
||||
findPrivate.keys = { "itemString", "count", "bid", "buyout", "seller" }
|
||||
for i = #findPrivate.keys, 1, -1 do
|
||||
if not targetInfo[findPrivate.keys[i]] then
|
||||
tremove(findPrivate.keys, i)
|
||||
end
|
||||
end
|
||||
|
||||
local cacheIndex
|
||||
if useCache and scanCache[targetInfo.itemString] then
|
||||
for i, v in ipairs(scanCache[targetInfo.itemString]) do
|
||||
if CompareTableKeys(v, targetInfo) then
|
||||
cacheIndex = i
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if cacheIndex then
|
||||
findPrivate.page = floor((cacheIndex - 1) / 50)
|
||||
findPrivate.query = scanCache[targetInfo.itemString].query
|
||||
else
|
||||
local name, _, rarity, _, minLevel, class, subClass = TSMAPI:GetSafeItemInfo(targetInfo.itemString)
|
||||
findPrivate.query = { name = name, minLevel = minLevel, maxLevel = minLevel, class = class, subClass = subClass, rarity = rarity }
|
||||
findPrivate.page = 0
|
||||
end
|
||||
findPrivate.targetInfo = targetInfo
|
||||
findPrivate.callback = callback
|
||||
findPrivate.cacheIndex = cacheIndex
|
||||
findPrivate.isScanning = targetInfo.itemString
|
||||
findPrivate.retries = 0
|
||||
findPrivate.hardRetry = nil
|
||||
|
||||
-- check if the item is on the current page
|
||||
for i = 1, GetNumAuctionItems("list") do
|
||||
if IsTargetAuction(i) then
|
||||
TSMAPI.AuctionScan:StopFindScan()
|
||||
TSMAPI:CreateTimeDelay("queryFoundDelay", 0.1, function() findPrivate.callback(i) end)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
findPrivate:SendQuery()
|
||||
end
|
||||
|
||||
-- sends a query to the AH frame once it is ready to be queried (uses frame as a delay)
|
||||
function findPrivate:SendQuery()
|
||||
if not findPrivate.isScanning then return end
|
||||
if CanSendAuctionQuery() then
|
||||
-- stop delay timer
|
||||
TSMAPI:CancelFrame("auctionFindQueryDelay")
|
||||
|
||||
-- query the auction house (then waits for AUCTION_ITEM_LIST_UPDATE to fire)
|
||||
findPrivate.findFrame:RegisterEvent("AUCTION_ITEM_LIST_UPDATE")
|
||||
local q = findPrivate.query
|
||||
QueryAuctionItems(q.name, q.minLevel, q.maxLevel, q.invType, q.class, q.subClass, findPrivate.page, 0, q.rarity)
|
||||
else
|
||||
-- run delay timer then try again to scan
|
||||
TSMAPI:CreateTimeDelay("auctionFindQueryDelay", 0.05, function() findPrivate:SendQuery() end)
|
||||
end
|
||||
end
|
||||
|
||||
-- scans the currently shown page of auctions and collects all the data
|
||||
function findPrivate:ScanAuctions()
|
||||
if not findPrivate.isScanning then return end
|
||||
-- collects data on the query:
|
||||
-- # of auctions on current page
|
||||
-- # of pages total
|
||||
local shown, total = GetNumAuctionItems("list")
|
||||
local totalPages = math.ceil(total / 50)
|
||||
local dataIsBad, temp = private:ScanAuctionPage(findPrivate.targetInfo.seller)
|
||||
|
||||
-- Check for bad data
|
||||
if findPrivate.retries < 3 then
|
||||
if dataIsBad then
|
||||
if findPrivate.hardRetry then
|
||||
-- Hard retry
|
||||
-- re-sends the entire query
|
||||
findPrivate.retries = findPrivate.retries + 1
|
||||
findPrivate:SendQuery()
|
||||
else
|
||||
-- Soft retry
|
||||
-- runs a delay and then tries to scan the query again
|
||||
findPrivate.timeDelay = findPrivate.timeDelay + BASE_DELAY
|
||||
TSMAPI:CreateTimeDelay("auctionFindScanDelay", BASE_DELAY, findPrivate.ScanAuctions)
|
||||
|
||||
-- If after 4 seconds of retrying we still don't have data, will go and requery to try and solve the issue
|
||||
-- if we still don't have data, we try to scan it anyway and move on.
|
||||
if findPrivate.timeDelay >= 4 then
|
||||
findPrivate.hardRetry = true
|
||||
findPrivate.retries = 0
|
||||
end
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
findPrivate.hardRetry = nil
|
||||
findPrivate.retries = 0
|
||||
|
||||
-- now that we know our query is good, time to verify and then store our data
|
||||
for i = 1, shown do
|
||||
if IsTargetAuction(temp[i].index) then
|
||||
TSMAPI.AuctionScan:StopFindScan()
|
||||
return findPrivate.callback(temp[i].index, findPrivate.cacheIndex == findPrivate.page and findPrivate.page * 50 + temp[i].index)
|
||||
end
|
||||
end
|
||||
|
||||
-- This query has more pages to scan
|
||||
-- increment the page # and send the new query
|
||||
if not findPrivate.cacheIndex and totalPages > (findPrivate.page + 1) then
|
||||
findPrivate.page = findPrivate.page + 1
|
||||
findPrivate:SendQuery()
|
||||
return
|
||||
end
|
||||
|
||||
-- we are done scanning!
|
||||
TSMAPI.AuctionScan:StopFindScan()
|
||||
return findPrivate.callback()
|
||||
end
|
||||
|
||||
-- returns whether or not we're currently doing a find scan
|
||||
function TSMAPI.AuctionScan:IsFindScanning()
|
||||
return findPrivate.isScanning
|
||||
end
|
||||
|
||||
-- stops the scan because it was either interrupted or it was completed successfully
|
||||
function TSMAPI.AuctionScan:StopFindScan()
|
||||
findPrivate.findFrame:UnregisterEvent("AUCTION_ITEM_LIST_UPDATE")
|
||||
findPrivate.isScanning = nil
|
||||
TSMAPI:CancelFrame("auctionFindQueryDelay")
|
||||
TSMAPI:CancelFrame("auctionFindScanDelay")
|
||||
end
|
||||
@@ -0,0 +1,116 @@
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
-- TradeSkillMaster --
|
||||
-- http://www.curse.com/addons/wow/tradeskill-master --
|
||||
-- --
|
||||
-- A TradeSkillMaster Addon (http://tradeskillmaster.com) --
|
||||
-- All Rights Reserved* - Detailed license information included with addon. --
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
|
||||
local TSM = select(2, ...)
|
||||
local private = {}
|
||||
TSMAPI:RegisterForTracing(private, "TradeSkillMaster.AuctionUtil_private")
|
||||
LibStub("AceEvent-3.0"):Embed(private)
|
||||
|
||||
|
||||
local eventFrame = CreateFrame("Frame")
|
||||
eventFrame:Hide()
|
||||
eventFrame.data = {}
|
||||
eventFrame.callback = function() end
|
||||
eventFrame:SetScript("OnEvent", function(self, event, ...)
|
||||
if self.interrupt and event == self.interrupt.event and self.interrupt.callback() then
|
||||
self:UnregisterAllEvents()
|
||||
self.data = {}
|
||||
end
|
||||
for i=1, #self.data do
|
||||
if self.data[i].event == event then
|
||||
if self.data[i].callback then
|
||||
if self.data[i].callback(event, ...) then
|
||||
tremove(self.data, i)
|
||||
self:UnregisterEvent(event)
|
||||
end
|
||||
else
|
||||
tremove(self.data, i)
|
||||
self:UnregisterEvent(event)
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
if #self.data == 0 then
|
||||
self:Hide()
|
||||
self.callback()
|
||||
end
|
||||
end)
|
||||
|
||||
local function WaitForEvents(data, callback, interrupt)
|
||||
eventFrame.data = data
|
||||
eventFrame.callback = callback
|
||||
for i=1, #data do
|
||||
eventFrame:RegisterEvent(data[i].event)
|
||||
end
|
||||
if interrupt then
|
||||
eventFrame.interrupt = interrupt
|
||||
eventFrame:RegisterEvent(interrupt.event)
|
||||
end
|
||||
eventFrame:Show()
|
||||
end
|
||||
|
||||
function TSMAPI:CreateEventDelay(event, callback, timeout, validator)
|
||||
if not event then return end
|
||||
local eventName = "eventDelay"..random()
|
||||
if timeout then
|
||||
TSMAPI:CreateTimeDelay(eventName, timeout, function() eventFrame:Hide() end)
|
||||
callback()
|
||||
end
|
||||
|
||||
WaitForEvents({event=event, callback=validator}, function() callback() TSMAPI:CancelFrame(eventName) end)
|
||||
end
|
||||
|
||||
-- Sends the "TSM_AH_EVENTS" message once the action (buyout/bid/cancel/post)
|
||||
-- has been acknowledged by the server and the client has been notified
|
||||
function TSMAPI:WaitForAuctionEvents(mode, isMultiPost)
|
||||
local function ValidateEvent(_, msg)
|
||||
if mode == "Buyout" then
|
||||
return msg:match(gsub(ERR_AUCTION_BID_PLACED, "%%s", ""))
|
||||
elseif mode == "Cancel" then
|
||||
return msg == ERR_AUCTION_REMOVED
|
||||
elseif mode == "Post" then
|
||||
return msg == ERR_AUCTION_STARTED
|
||||
end
|
||||
end
|
||||
|
||||
local events, interrupt
|
||||
if mode == "Buyout" then
|
||||
events = {{event="AUCTION_ITEM_LIST_UPDATE"}, {event="CHAT_MSG_SYSTEM", callback=ValidateEvent}}
|
||||
interrupt = {event="UI_ERROR_MESSAGE", callback=function(_,msg) return msg == ERR_AUCTION_HIGHER_BID end}
|
||||
elseif mode == "Cancel" then
|
||||
events = {{event="CHAT_MSG_SYSTEM", callback=ValidateEvent}, {event="AUCTION_OWNED_LIST_UPDATE"}}
|
||||
elseif mode == "Post" then
|
||||
if isMultiPost then
|
||||
events = {{event="AUCTION_MULTISELL_UPDATE", callback=function(_,arg1,arg2) return arg1 == arg2 end}}
|
||||
else
|
||||
events = {{event="CHAT_MSG_SYSTEM", callback=ValidateEvent}}
|
||||
end
|
||||
end
|
||||
if events then
|
||||
WaitForEvents(events, function() private:SendMessage("TSM_AH_EVENTS", mode) end, interrupt)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function TSMAPI:GetAuctionPercentColor(percent)
|
||||
local colors = {
|
||||
{color="|cff2992ff", value=50}, -- blue
|
||||
{color="|cff16ff16", value=80}, -- green
|
||||
{color="|cffffff00", value=110}, -- yellow
|
||||
{color="|cffff9218", value=135}, -- orange
|
||||
{color="|cffff0000", value=math.huge}, -- red
|
||||
}
|
||||
|
||||
for i=1, #colors do
|
||||
if percent < colors[i].value then
|
||||
return colors[i].color
|
||||
end
|
||||
end
|
||||
|
||||
return "|cffffffff"
|
||||
end
|
||||
Reference in New Issue
Block a user