This commit is contained in:
Andrew6810
2022-11-05 21:19:42 -07:00
parent b79f4bd588
commit f3e579cb57
386 changed files with 93729 additions and 2 deletions
@@ -0,0 +1,274 @@
local TSM = select(2, ...)
local Destroying = TSM:NewModule("Destroying")
local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_Shopping") -- loads the localization table
local private = {sources={}}
function Destroying:OnEnable()
TSMAPI:CreateTimeDelay("shoppingDestroyingUpdateTargets", 0, private.UpdateTargetItems, 60) --1)
TSM.db.global.destroyingTargetItems = TSM.db.global.destroyingTargetItems or {}
end
function private:UpdateTargetItems()
local update
for itemString in pairs(TSMAPI.InkConversions) do
private.sources[itemString] = "mill"
if not TSM.db.global.destroyingTargetItems[itemString] then
update = true
local name = TSMAPI:GetSafeItemInfo(itemString)
if name then
TSM.db.global.destroyingTargetItems[itemString] = name
end
end
end
for _, itemString in ipairs(TSMAPI:GetConversionTargetItems("mill")) do
private.sources[itemString] = "mill"
if not TSM.db.global.destroyingTargetItems[itemString] then
update = true
local name = TSMAPI:GetSafeItemInfo(itemString)
if name then
TSM.db.global.destroyingTargetItems[itemString] = name
end
end
end
for _, itemString in ipairs(TSMAPI:GetConversionTargetItems("prospect")) do
private.sources[itemString] = "prospect"
if not TSM.db.global.destroyingTargetItems[itemString] then
update = true
local name = TSMAPI:GetSafeItemInfo(itemString)
if name then
TSM.db.global.destroyingTargetItems[itemString] = name
end
end
end
for _, itemString in ipairs(TSMAPI:GetEnchantingTargetItems()) do
private.sources[itemString] = "disenchant"
if not TSM.db.global.destroyingTargetItems[itemString] then
update = true
local name = TSMAPI:GetSafeItemInfo(itemString)
if name then
TSM.db.global.destroyingTargetItems[itemString] = name
end
end
end
if not update then
TSMAPI:CancelFrame("shoppingDestroyingUpdateTargets")
end
end
function Destroying:StartDestroyingSearch(target, filter, isCrafting)
if not private.sources[target] then return TSM:Printf(L["Invalid destroy target: '%s'"], target) end
TSM.isCrafting = isCrafting
Destroying.maxQuantity = filter.maxQuantity
filter.maxPrice = nil
if private.sources[target] == "mill" then
private:TryStarting(private.StartMillingSearch, target, filter)
elseif private.sources[target] == "prospect" then
private:TryStarting(private.StartProspectingSearch, target, filter)
elseif private.sources[target] == "disenchant" then
private:TryStarting(private.StartDisenchantingSearch, target, filter)
end
TSMAPI:FireEvent("SHOPPING:SEARCH:STARTDESTROYSCAN", {target=target, filter=filter})
end
function private:TryStarting(func, target, filter, attempt)
attempt = attempt or 0
if attempt <= 10 and not func(target, filter, attempt == 10) then
TSMAPI:CreateTimeDelay("destroySearchTryStart", 0.1, function() private:TryStarting(func, target, filter, attempt+1) end)
end
end
function private:AddItemQuery(itemList, filter, itemString)
local name = TSMAPI:GetSafeItemInfo(itemString)
if name then
local query = CopyTable(filter)
query.name = name
tinsert(itemList, query)
return true
end
end
function private.StartMillingSearch(target, filter, lastAttempt)
local matItemString = target
local inkItemString, pigmentItemString
if TSMAPI.InkConversions[target] then
inkItemString = target
pigmentItemString = TSMAPI.InkConversions[target].pigment
else
for itemString, data in pairs(TSMAPI.InkConversions) do
if target == data.pigment then
inkItemString = itemString
pigmentItemString = target
break
end
end
if not inkItemString then return TSM:Printf(L["Unknown milling search target: '%s'"], target) end
end
private.evenFilter = {}
private.conversions = {}
private.conversions[inkItemString] = 1
private.conversions[pigmentItemString] = 1 / TSMAPI.InkConversions[inkItemString].pigmentPerInk
local itemList = {}
-- add ink and pigment
if not private:AddItemQuery(itemList, filter, inkItemString) and not lastAttempt then return end
if not private:AddItemQuery(itemList, filter, pigmentItemString) and not lastAttempt then return end
-- add primary herbs
for itemString, data in pairs(TSMAPI:GetItemConversions(pigmentItemString)) do
if not private:AddItemQuery(itemList, filter, itemString) and not lastAttempt then return end
private.evenFilter[itemString] = filter.evenOnly
private.conversions[itemString] = data.rate / TSMAPI.InkConversions[inkItemString].pigmentPerInk
end
-- deal with vendor trades
local otherInks = TSMAPI.Conversions[inkItemString]
for otherInk, otherInkData in pairs(otherInks or {}) do
if not TSMAPI.Conversions[otherInk] and otherInkData.source == "vendortrade" and TSMAPI.InkConversions[otherInk] then
local vendorTradeRate = otherInkData.rate
for itemString, millData in pairs(TSMAPI:GetItemConversions(TSMAPI.InkConversions[otherInk].pigment)) do
if not private:AddItemQuery(itemList, filter, itemString) and not lastAttempt then return end
private.evenFilter[itemString] = filter.evenOnly
private.conversions[itemString] = vendorTradeRate * millData.rate / TSMAPI.InkConversions[inkItemString].pigmentPerInk
end
if not private:AddItemQuery(itemList, filter, otherInk) and not lastAttempt then return end
if not private:AddItemQuery(itemList, filter, TSMAPI.InkConversions[otherInk].pigment) and not lastAttempt then return end
private.conversions[otherInk] = vendorTradeRate
private.conversions[TSMAPI.InkConversions[otherInk].pigment] = vendorTradeRate / TSMAPI.InkConversions[otherInk].pigmentPerInk
end
end
private.mode = "mill"
private.target = inkItemString
if TSM.isCrafting then
local func = TSMAPI:ParseCustomPrice("matprice")
local price = func and func(matItemString) or nil
private.targetMarketValue = price
TSM.Util:ShowSearchFrame(true, L["% Mat Price"])
else
private.targetMarketValue = TSM:GetMaxPrice(TSM.db.global.marketValueSource, inkItemString)
TSM.Util:ShowSearchFrame(true, L["% Target Value"])
end
TSM.Search:SetSearchBarDisabled(true)
TSM.Util:StartFilterScan(itemList, private.ScanCallback)
return true
end
function private.StartProspectingSearch(target, filter, lastAttempt)
local itemList = {}
private.evenFilter = {}
if not private:AddItemQuery(itemList, filter, target) and not lastAttempt then return end
for itemString in pairs(TSMAPI:GetItemConversions(target)) do
if not private:AddItemQuery(itemList, filter, itemString) and not lastAttempt then return end
private.evenFilter[itemString] = filter.evenOnly
end
private.mode = "prospect"
private.target = target
if TSM.isCrafting then
local func = TSMAPI:ParseCustomPrice("matprice")
local price = func and func(target) or nil
private.targetMarketValue = price
TSM.Util:ShowSearchFrame(true, L["% Max Price"])
else
private.targetMarketValue = TSM:GetMaxPrice(TSM.db.global.marketValueSource, target)
TSM.Util:ShowSearchFrame(true, L["% Target Value"])
end
TSM.Search:SetSearchBarDisabled(true)
TSM.Util:StartFilterScan(itemList, private.ScanCallback)
return true
end
function private.StartDisenchantingSearch(target, filter, lastAttempt)
local disenchantData = TSMAPI:GetDisenchantData(target)
if not disenchantData then return end
local queries = {}
local query = TSMAPI:GetAuctionQueryInfo(target)
if not query and not lastAttempt then return end
if query then
tinsert(queries, query)
end
for itemType, rarityData in pairs(disenchantData.itemTypes) do
local class = 0
if itemType == "Weapon" then
class = 1
elseif itemType == "Armor" then
class = 2
end
for rarity, data in pairs(rarityData) do
local minILevel = data[1].minItemLevel or 0
local maxILevel = data[#data].maxItemLevel or 0
local query = {name="", class=class, subClass=0, minLevel=disenchantData.minLevel, maxLevel=disenchantData.maxLevel, minILevel=minILevel, maxILevel=maxILevel, quality=rarity}
tinsert(queries, query)
end
end
for itemString, data in pairs(TSMAPI.Conversions[target] or {}) do
local query = TSMAPI:GetAuctionQueryInfo(itemString)
if not query and not lastAttempt then return end
if query then
tinsert(queries, query)
end
end
private.mode = "disenchant"
private.target = target
if TSM.isCrafting then
local func = TSMAPI:ParseCustomPrice("matprice")
local price = func and func(target) or nil
private.targetMarketValue = price
TSM.Util:ShowSearchFrame(true, L["% Max Price"])
else
private.targetMarketValue = TSM:GetMaxPrice(TSM.db.global.marketValueSource, target)
TSM.Util:ShowSearchFrame(true, L["% Target Value"])
end
TSM.Search:SetSearchBarDisabled(true)
TSM.Util:StartFilterScan(queries, private.ScanCallback)
return true
end
function private.ScanCallback(event, ...)
if event == "filter" then
return
elseif event == "process" then
local itemString, auctionItem = ...
local rate, shouldEvenFilter
if private.mode == "mill" then
rate = private.conversions[itemString]
shouldEvenFilter = private.evenFilter[itemString]
elseif private.mode == "disenchant" then
if TSMAPI.Conversions[private.target] and TSMAPI.Conversions[private.target][itemString] then
rate = TSMAPI.Conversions[private.target][itemString].rate
else
rate = TSMAPI:GetEnchantingConversionNum(private.target, itemString)
end
elseif private.mode == "prospect" then
shouldEvenFilter = private.evenFilter[itemString]
local conversions = TSMAPI:GetItemConversions(private.target)
rate = conversions and conversions[itemString] and conversions[itemString].rate
rate = rate and (rate / 5)
end
if itemString == private.target then
auctionItem.destroyingNum = 1
auctionItem:SetMarketValue(private.targetMarketValue)
else
if not rate then return end
if shouldEvenFilter then
auctionItem:FilterRecords(function(record) return record.count%5 ~= 0 end)
end
auctionItem.destroyingNum = 1/rate
if private.targetMarketValue then
auctionItem:SetMarketValue(private.targetMarketValue*rate)
end
end
return auctionItem
elseif event == "done" then
TSM.Search:SetSearchBarDisabled(false)
end
end
@@ -0,0 +1,362 @@
-- ------------------------------------------------------------------------------ --
-- TradeSkillMaster_Shopping --
-- http://www.curse.com/addons/wow/tradeskillmaster_shopping --
-- --
-- A TradeSkillMaster Addon (http://tradeskillmaster.com) --
-- All Rights Reserved* - Detailed license information included with addon. --
-- ------------------------------------------------------------------------------ --
-- load the parent file (TSM) into a local variable and register this file as a module
local TSM = select(2, ...)
local Options = TSM:NewModule("Options", "AceEvent-3.0", "AceHook-3.0")
local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_Shopping") -- loads the localization table
local AceGUI = LibStub("AceGUI-3.0") -- load the AceGUI libraries
function Options:Load(parent, operation, group)
Options.treeGroup = AceGUI:Create("TSMTreeGroup")
Options.treeGroup:SetLayout("Fill")
Options.treeGroup:SetCallback("OnGroupSelected", function(...) Options:SelectTree(...) end)
Options.treeGroup:SetStatusTable(TSM.db.global.optionsTreeStatus)
parent:AddChild(Options.treeGroup)
Options:UpdateTree()
if operation then
if operation == "" then
Options.currentGroup = group
Options.treeGroup:SelectByPath(2)
Options.currentGroup = nil
else
Options.treeGroup:SelectByPath(2, operation)
end
else
Options.treeGroup:SelectByPath(1)
end
end
function Options:UpdateTree()
local operationTreeChildren = {}
for name in pairs(TSM.operations) do
tinsert(operationTreeChildren, { value = name, text = name })
end
sort(operationTreeChildren, function(a, b) return a.value < b.value end)
Options.treeGroup:SetTree({ { value = 1, text = L["Options"] }, { value = 2, text = L["Operations"], children = operationTreeChildren } })
end
function Options:SelectTree(treeGroup, _, selection)
treeGroup:ReleaseChildren()
local major, minor = ("\001"):split(selection)
major = tonumber(major)
if major == 1 then
Options:DrawGeneralSettings(treeGroup)
elseif minor then
Options:DrawOperationSettings(treeGroup, minor)
else
Options:DrawNewOperation(treeGroup)
end
end
function Options:DrawGeneralSettings(container)
local page = {
{
type = "ScrollFrame",
layout = "list",
children = {
{
type = "InlineGroup",
layout = "flow",
title = L["General Options"],
children = {
{
type = "EditBox",
label = L["Default Post Undercut Amount"],
settingInfo = { TSM.db.global, "postUndercut" },
relativeWidth = 0.5,
acceptCustom = true,
callback = function(_, _, value) TSMAPI.AuctionControl.undercut = value end,
tooltip = L["What to set the default undercut to when posting items with Shopping."],
},
{
type = "EditBox",
label = L["Market Value Price Source"],
settingInfo = { TSM.db.global, "marketValueSource" },
relativeWidth = 0.5,
acceptCustom = true,
tooltip = L["This is how Shopping calculates the '% Market Value' column in the search results."],
},
{
type = "Slider",
label = L["Max Disenchant Search Percent"],
settingInfo = { TSM.db.global, "maxDeSearchPercent" },
min = .1,
max = 1,
step = .01,
isPercent = true,
relativeWidth = 1,
tooltip = L["This is the maximum percentage of disenchant value that the Other > Disenchant search will display results for."],
},
{
type = "Slider",
label = L["Min Disenchant Level"],
settingInfo = { TSM.db.global, "minDeSearchLvl" },
min = 1,
max = 450,
step = 1,
isPercent = false,
relativeWidth = 0.5,
callback = function(self, _, value)
if value > TSM.db.global.maxDeSearchLvl then
TSM:Print(TSMAPI.Design:GetInlineColor("link2") .. L["Warning: The min disenchant level must be lower than the max disenchant level."] .. "|r")
end
TSM.db.global.minDeSearchLvl = min(value, TSM.db.global.maxDeSearchLvl)
end,
tooltip = L["This is the minimum item level that the Other > Disenchant search will display results for."],
},
{
type = "Slider",
label = L["Max Disenchant Level"],
settingInfo = { TSM.db.global, "maxDeSearchLvl" },
min = 1,
max = 450,
step = 1,
isPercent = false,
callback = function(self, _, value)
if value < TSM.db.global.minDeSearchLvl then
TSM:Print(TSMAPI.Design:GetInlineColor("link2") .. L["Warning: The max disenchant level must be higher than the min disenchant level."] .. "|r")
end
TSM.db.global.maxDeSearchLvl = max(value, TSM.db.global.minDeSearchLvl)
end,
relativeWidth = 0.5,
tooltip = L["This is the maximum item level that the Other > Disenchant search will display results for."],
},
},
},
{
type = "Spacer",
},
{
type = "InlineGroup",
layout = "flow",
title = "Posting Options",
children = {
{
type = "Slider",
label = L["Bid Percent"],
settingInfo = { TSM.db.global, "postBidPercent" },
min = .1,
max = 1,
step = .01,
isPercent = true,
relativeWidth = 0.5,
tooltip = L["This is the percentage of your buyout price that your bid will be set to when posting auctions with Shopping."],
},
{
type = "EditBox",
label = L["Normal Post Price"],
settingInfo = { TSM.db.global, "normalPostPrice" },
relativeWidth = 0.49,
acceptCustom = true,
tooltip = L["This is the default price Shopping will suggest to post items at when there's no others posted."],
},
{
type = "HeadingLine"
},
{
type = "Dropdown",
label = L["Quick Posting Duration"],
list = { AUCTION_DURATION_ONE, AUCTION_DURATION_TWO, AUCTION_DURATION_THREE },
settingInfo = { TSM.db.global, "quickPostingDuration" },
relativeWidth = 0.5,
tooltip = L["The duration at which items will be posted via the 'Quick Posting' frame."],
},
{
type = "EditBox",
label = L["Quick Posting Price"],
settingInfo = { TSM.db.global, "quickPostingPrice" },
relativeWidth = 0.49,
acceptCustom = true,
tooltip = L["This price is used to determine what items will be posted at through the 'Quick Posting' frame."],
},
},
},
{
type = "InlineGroup",
layout = "flow",
title = L["Sniper Options"],
children = {
{
type = "CheckBox",
label = L["Below Vendor Sell Price"],
settingInfo = { TSM.db.global, "sniperVendorPrice" },
relativeWidth = 0.5,
tooltip = L["Items which are below their vendor sell price will be displayed in Sniper searches."],
},
{
type = "CheckBox",
label = L["Below Max Price"],
settingInfo = { TSM.db.global, "sniperMaxPrice" },
relativeWidth = 0.49,
tooltip = L["Items which are below their maximum price (per their group / Shopping operation) will be displayed in Sniper searches."],
},
{
type = "EditBox",
label = L["Below Custom Price ('0c' to disable)"],
settingInfo = { TSM.db.global, "sniperCustomPrice" },
relativeWidth = 0.5,
acceptCustom = true,
tooltip = L["Items which are below this custom price will be displayed in Sniper searches."],
},
},
},
},
},
}
TSMAPI:BuildPage(container, page)
end
function Options:DrawNewOperation(container)
local currentGroup = Options.currentGroup
local page = {
{
-- scroll frame to contain everything
type = "ScrollFrame",
layout = "List",
children = {
{
type = "InlineGroup",
layout = "flow",
title = L["New Operation"],
children = {
{
type = "Label",
text = L["Shopping operations contain settings items which you regularly buy from the auction house."],
relativeWidth = 1,
},
{
type = "EditBox",
label = L["Operation Name"],
relativeWidth = 0.8,
callback = function(self, _, name)
name = (name or ""):trim()
if name == "" then return end
if TSM.operations[name] then
self:SetText("")
return TSM:Printf(L["Error creating operation. Operation with name '%s' already exists."], name)
end
TSM.operations[name] = CopyTable(TSM.operationDefaults)
Options:UpdateTree()
Options.treeGroup:SelectByPath(2, name)
TSMAPI:NewOperationCallback("Shopping", currentGroup, name)
end,
tooltip = L["Give the new operation a name. A descriptive name will help you find this operation later."],
},
},
},
},
},
}
TSMAPI:BuildPage(container, page)
end
function Options:DrawOperationSettings(container, operationName)
local tg = AceGUI:Create("TSMTabGroup")
tg:SetLayout("Fill")
tg:SetFullHeight(true)
tg:SetFullWidth(true)
tg:SetTabs({ { value = 1, text = L["General"] }, { value = 2, text = L["Relationships"] }, { value = 3, text = L["Management"] } })
tg:SetCallback("OnGroupSelected", function(self, _, value)
tg:ReleaseChildren()
TSMAPI:UpdateOperation("Shopping", operationName)
if value == 1 then
Options:DrawOperationGeneral(self, operationName)
elseif value == 2 then
Options:DrawOperationRelationships(self, operationName)
elseif value == 3 then
TSMAPI:DrawOperationManagement(TSM, self, operationName)
end
end)
container:AddChild(tg)
tg:SelectTab(1)
end
function Options:DrawOperationGeneral(container, operationName)
local operation = TSM.operations[operationName]
local page = {
{
type = "ScrollFrame",
layout = "list",
children = {
{
type = "InlineGroup",
layout = "flow",
title = L["General Operation Options"],
children = {
{
type = "EditBox",
label = L["Maximum Auction Price (per item)"],
settingInfo = { operation, "maxPrice" },
relativeWidth = 0.49,
acceptCustom = true,
disabled = operation.relationships.maxprice,
tooltip = L["The highest price per item you will pay for items in affected by this operation."],
},
{
type = "CheckBox",
label = L["Show Auctions Above Max Price"],
settingInfo = { operation, "showAboveMaxPrice" },
disabled = operation.relationships.showAboveMaxPrice,
tooltip = L["If checked, auctions above the max price will be shown."],
},
{
type = "CheckBox",
label = L["Even (5/10/15/20) Stacks Only"],
settingInfo = { operation, "evenStacks" },
disabled = operation.relationships.evenStacks,
tooltip = L["If checked, only auctions posted in even quantities will be considered for purchasing."],
},
},
},
},
},
}
TSMAPI:BuildPage(container, page)
end
function Options:DrawOperationRelationships(container, operationName)
local settingInfo = {
{
label = L["General Settings"],
{ key = "maxPrice", label = L["Maximum Auction Price (per item)"] },
{ key = "showAboveMaxPrice", label = L["Show Auctions Above Max Price"] },
{ key = "evenStacks", label = L["Even (5/10/15/20) Stacks Only"] },
},
}
TSMAPI:ShowOperationRelationshipTab(TSM, container, TSM.operations[operationName], settingInfo)
end
function Options:LoadTooltipOptions(container)
local page = {
{
type = "SimpleGroup",
layout = "Flow",
fullHeight = true,
children = {
{
type = "CheckBox",
label = L["Show Shopping Max Price in Tooltip"],
settingInfo = { TSM.db.global, "tooltip" },
callback = function(_, _, value) container:ReloadTab() end,
tooltip = L["If checked, the maximum shopping price will be shown in the tooltip for the item."],
},
},
},
}
TSMAPI:BuildPage(container, page)
end
@@ -0,0 +1,589 @@
-- ------------------------------------------------------------------------------ --
-- TradeSkillMaster_Shopping --
-- http://www.curse.com/addons/wow/tradeskillmaster_shopping --
-- --
-- A TradeSkillMaster Addon (http://tradeskillmaster.com) --
-- All Rights Reserved* - Detailed license information included with addon. --
-- ------------------------------------------------------------------------------ --
local TSM = select(2, ...)
local Search = TSM:NewModule("Search", "AceEvent-3.0", "AceHook-3.0")
local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_Shopping") -- loads the localization table
local private = {}
-- ------------------------------------------------ --
-- GUI Creation functions --
-- ------------------------------------------------ --
function Search:Show(frame)
TSM.Util:SetParent(frame)
private.searchBar = private.searchBar or private:CreateSearchBar(frame)
private.searchBar:Show()
private.searchBar.editBox:SetFocus()
private.searchBar.editBox:SetText("")
private.searchBar:Enable()
private.searchBar.normal:Click()
TSM.Sidebar:Show(frame)
if TSM.db.global.sidebarBtn > #private.searchBar.buttons then
TSM.db.global.sidebarBtn = 0
end
if TSM.db.global.sidebarBtn > 0 then
if not private.searchBar.buttons[TSM.db.global.sidebarBtn].isSelected then
private.searchBar.buttons[TSM.db.global.sidebarBtn]:Click()
end
else
TSM.Sidebar:Hide()
end
end
function Search:Hide()
if not private.searchBar then return end
private.searchBar:Hide()
TSM.Util:HideSearchFrame()
TSMAPI.AuctionControl:HideControlButtons()
TSMAPI.AuctionScan:StopScan()
TSM.Sidebar:Hide()
end
function private:CreateSearchBar(parent)
local function StartSearch(searchQuery)
if private.mode == "normal" then
Search:StartFilterSearch(searchQuery)
elseif private.mode == "destroy" then
private.searchBar.editBox:SetText(searchQuery)
local filters = Search:GetFilters(searchQuery)
if filters and #filters == 1 then
for itemString, name in pairs(TSM.db.global.destroyingTargetItems) do
if strlower(name) == strlower(filters.currentFilter) then
return TSM.Destroying:StartDestroyingSearch(itemString, filters[1])
end
end
TSM:Printf(L["Invalid target item for destroy search: '%s'"], filters.currentFilter)
else
TSM:Printf(L["Invalid destroy search: '%s'"], searchQuery)
end
end
end
local function HandleModifiedItemClick(link)
local putIntoChat = Search.hooks.HandleModifiedItemClick(link)
if not putIntoChat and private.searchBar:IsVisible() and not private.searchBar.isDisabled and TSMAPI:AHTabIsVisible("Shopping") then
local name = TSMAPI:GetSafeItemInfo(link)
if name then
StartSearch(name.."/exact")
return true
end
end
return putIntoChat
end
local function InsertLink(link)
local putIntoChat = Search.hooks.ChatEdit_InsertLink(link)
if not putIntoChat then
if private.searchBar:IsVisible() and not private.searchBar.isDisabled and TSMAPI:AHTabIsVisible("Shopping") then
local name = TSMAPI:GetSafeItemInfo(link)
if name then
StartSearch(name.."/exact")
return true
end
end
end
return putIntoChat
end
Search:RawHook("ChatEdit_InsertLink", InsertLink, true)
Search:RawHook("HandleModifiedItemClick", HandleModifiedItemClick, true)
local function OnChar(self)
local text = self:GetText()
if private.mode == "normal" then
for i=1, #TSM.db.global.previousSearches do
local prevSearch = strlower(TSM.db.global.previousSearches[i])
if strsub(prevSearch, 1, #text) == strlower(text) then
self:SetText(prevSearch)
self:HighlightText(#text, -1)
break
end
end
elseif private.mode == "destroy" then
for _, name in pairs(TSM.db.global.destroyingTargetItems) do
name = strlower(name)
if strsub(name, 1, #text) == strlower(text) then
self:SetText(name)
self:HighlightText(#text, -1)
break
end
end
end
end
local function OnEditFocusGained(self)
self:HighlightText()
end
local function OnEditFocusLost(self)
self:HighlightText()
end
local function OnUpdate(self)
-- if self:IsEnabled() and not TSMAPI:AHTabIsVisible("Shopping") then
if not TSMAPI:AHTabIsVisible("Shopping") then
self:ClearFocus()
end
end
local function OnEnter(self)
GameTooltip:SetOwner(self, "ANCHOR_BOTTOM")
GameTooltip:SetMinimumWidth(400)
GameTooltip:AddLine(L["Enter what you want to search for in this box. You can also use the following options for more complicated searches."].."\n", 1, 1, 1, 1)
GameTooltip:AddLine(format("|cffffff00"..L["Multiple Search Terms:|r You can search for multiple things at once by simply separated them with a ';'. For example '%scopper ore; gold ore|r' will search for both copper and gold ore."].."\n", TSMAPI.Design:GetInlineColor("link2")), 1, 1, 1, 1)
GameTooltip:AddLine(format("|cffffff00"..L["Inline Filters:|r You can easily add common search filters to your search such as rarity, level, and item type. For example '%sarmor/leather/epic/80/i200/i285|r' will search for all leather armor of epic quality that requires level 80 and has an ilvl between 200 and 285 inclusive. Also, '%sinferno ruby/exact|r' will display only raw inferno rubys (none of the cuts)."].."\n", TSMAPI.Design:GetInlineColor("link2"), TSMAPI.Design:GetInlineColor("link2")), 1, 1, 1, 1)
GameTooltip:Show()
end
local searchBarFrame = CreateFrame("Frame", nil, parent)
searchBarFrame:SetAllPoints()
searchBarFrame:Hide()
local eb = TSMAPI.GUI:CreateInputBox(searchBarFrame)
eb:SetPoint("TOPLEFT", 85, -5)
eb:SetHeight(22)
eb:SetWidth(600)
eb:SetScript("OnShow", eb.SetFocus)
eb:SetScript("OnEnterPressed", function() StartSearch(private.searchBar.editBox:GetText()) end)
eb:SetScript("OnChar", OnChar)
eb:SetScript("OnEditFocusGained", OnEditFocusGained)
eb:SetScript("OnEditFocusLost", OnEditFocusLost)
eb:SetScript("OnEnter", OnEnter)
eb:SetScript("OnLeave", function() GameTooltip:Hide() end)
eb:SetScript("OnUpdate", OnUpdate)
searchBarFrame.editBox = eb
local btn = TSMAPI.GUI:CreateButton(searchBarFrame, 20)
btn:SetPoint("TOPLEFT", eb, "TOPRIGHT", 4, 0)
btn:SetPoint("BOTTOMLEFT", eb, "BOTTOMRIGHT", 4, 0)
btn:SetWidth(85)
btn:SetText(SEARCH)
btn:SetScript("OnClick", function() StartSearch(private.searchBar.editBox:GetText()) end)
searchBarFrame.button = btn
local btn = TSMAPI.GUI:CreateButton(searchBarFrame, 16)
btn:SetPoint("TOPLEFT", searchBarFrame.button, "TOPRIGHT", 4, 0)
btn:SetPoint("BOTTOMLEFT", searchBarFrame.button, "BOTTOMRIGHT", 4, 0)
btn:SetPoint("TOPRIGHT", -5, -5)
btn:SetText(L["Stop"])
btn:Disable()
btn:SetScript("OnClick", function() TSM.Util:StopScan() searchBarFrame:Enable() end)
searchBarFrame.stop = btn
local function OnModeChange(self)
searchBarFrame.normal:UnlockHighlight()
searchBarFrame.destroy:UnlockHighlight()
self:LockHighlight()
if self.mode ~= private.mode then
private.mode = self.mode
private:UpdateMode()
end
end
local btn = TSMAPI.GUI:CreateButton(searchBarFrame, 14)
btn:SetPoint("TOPLEFT", eb, "BOTTOMLEFT", 0, -8)
btn:SetHeight(16)
btn:SetWidth(110)
btn:SetText(L["Normal Mode"])
btn:SetScript("OnClick", OnModeChange)
btn.mode = "normal"
btn.tooltip = L["When in normal mode, you may run simple and filtered searches of the auction house."]
searchBarFrame.normal = btn
local btn = TSMAPI.GUI:CreateButton(searchBarFrame, 14)
btn:SetPoint("TOPLEFT", searchBarFrame.normal, "BOTTOMLEFT", 0, -4)
btn:SetPoint("TOPRIGHT", searchBarFrame.normal, "BOTTOMRIGHT", 0, -4)
btn:SetHeight(16)
btn:SetText(L["Destroy Mode"])
btn:SetScript("OnClick", OnModeChange)
btn.mode = "destroy"
btn.tooltip = L["When in destroy mode, you simply enter a target item (ink/pigment, enchanting mat, gem, etc) into the search box to search for everything you can destroy to get that item."]
searchBarFrame.destroy = btn
local line = TSMAPI.GUI:CreateHorizontalLine(searchBarFrame, 0)
line:ClearAllPoints()
line:SetHeight(4)
line:SetPoint("TOPLEFT", eb, "BOTTOMLEFT", 120, -7)
line:SetPoint("TOPRIGHT", 0, -34)
local pagesLabel = TSMAPI.GUI:CreateLabel(searchBarFrame)
pagesLabel:SetPoint("TOPLEFT", eb, "BOTTOMLEFT", 125, -15)
pagesLabel:SetHeight(20)
pagesLabel:SetJustifyH("CENTER")
pagesLabel:SetJustifyV("CENTER")
pagesLabel:SetText(L["Sidebar Pages:"])
pagesLabel:SetWidth(pagesLabel:GetWidth() + 5)
local buttons = {}
local function OnClick(self)
self.isSelected = not self.isSelected
for _, btn in ipairs(buttons) do
btn:UnlockHighlight()
if btn ~= self then
btn.isSelected = false
end
end
if self.isSelected then
TSM.db.global.sidebarBtn = self.index
self:LockHighlight()
TSM.Sidebar:Show()
else
TSM.db.global.sidebarBtn = 0
self:UnlockHighlight()
TSM.Sidebar:Hide()
end
TSM.Sidebar:ButtonClick(self.label)
end
local pages, callbacks = TSM.Sidebar:GetPages()
for i, label in ipairs(pages) do
local btn = TSMAPI.GUI:CreateButton(searchBarFrame, 16)
btn:SetPoint("TOPLEFT", buttons[i-1] or pagesLabel, "TOPRIGHT", 5, 0)
btn:SetHeight(20)
btn:SetText(label)
btn:SetWidth(ceil(btn:GetTextWidth()+10))
btn:SetScript("OnClick", OnClick)
btn.label = label
btn.index = i
buttons[i] = btn
end
searchBarFrame.buttons = buttons
local line = TSMAPI.GUI:CreateHorizontalLine(searchBarFrame, 0)
line:ClearAllPoints()
line:SetHeight(4)
line:SetPoint("TOPLEFT", eb, "BOTTOMLEFT", 120, -40)
line:SetPoint("TOPRIGHT", 0, -67)
local line = TSMAPI.GUI:CreateVerticalLine(searchBarFrame, 0)
line:ClearAllPoints()
line:SetPoint("TOPLEFT", eb, "BOTTOMLEFT", 120, -7)
line:SetHeight(33)
line:SetWidth(4)
searchBarFrame.Disable = function(self)
self.isDisabled = true
self.editBox:ClearFocus()
self.editBox:HighlightText(0, 0)
-- self.editBox:Disable()
self.button:Disable()
self.stop:Enable()
end
searchBarFrame.Enable = function(self)
self.isDisabled = nil
-- self.editBox:Enable()
self.button:Enable()
self.stop:Disable()
end
return searchBarFrame
end
function private:UpdateMode()
private.searchBar.editBox:SetText("")
if private.mode == "nomal" then
TSM.Util:ShowSearchFrame(nil, L["% Market Value"], true)
elseif private.mode == "destroy" then
TSM.Util:ShowSearchFrame(true, L["% Target Value"], true)
end
TSM.Util:StopScan()
private.searchBar:Enable()
end
local function ScanCallback(event, ...)
if TSM.searchCallback then
TSM.searchCallback(event)
end
if event == "filter" then
local filter = ...
return filter.maxPrice
elseif event == "process" then
local itemString, auctionItem = ...
if auctionItem.query.maxPrice then
auctionItem:FilterRecords(function(record)
return (record:GetItemBuyout() or 0) > auctionItem.query.maxPrice
end)
end
if TSM.isCrafting then
local func = TSMAPI:ParseCustomPrice("matprice")
local price = func and func(itemString) or nil
auctionItem:SetMarketValue(price)
else
auctionItem:SetMarketValue(TSM:GetMaxPrice(TSM.db.global.marketValueSource, itemString))
end
return auctionItem
elseif event == "done" then
private.searchBar:Enable()
return
end
end
function Search:StartFilterSearch(filter, callback, isCrafting)
TSM.isCrafting = isCrafting
TSM.searchCallback = callback
if strfind(filter, "item:([0-9]+):?([0-9]*):?([0-9]*):?([0-9]*):?([0-9]*):?([0-9]*):?%-?([0-9]*)$") then --or strfind(filter, "battlepet:([0-9]+):?([0-9]*):?([0-9]*):?([0-9]*):?([0-9]*):?([0-9]*):?([0-9]*)$") then
filter = TSMAPI:GetSafeItemInfo(filter) or filter
end
if TSM.isCrafting then
TSM.Util:ShowSearchFrame(nil, L["% Mat Price"])
else
TSM.Util:ShowSearchFrame(nil, L["% Market Value"])
end
private.searchBar.editBox:SetText(filter)
private.searchBar:Disable()
if filter ~= "" then
for i=#TSM.db.global.previousSearches, 1, -1 do
if strlower(TSM.db.global.previousSearches[i]) == strlower(filter) then
tremove(TSM.db.global.previousSearches, i)
end
end
tinsert(TSM.db.global.previousSearches, 1, filter)
while #TSM.db.global.previousSearches > 100 do
tremove(TSM.db.global.previousSearches)
end
TSM.Sidebar:UpdateCurrentFrame()
end
local isItemList, itemList = true, {}
for _, searchTerm in ipairs({(";"):split(filter)}) do
if tonumber(searchTerm) then
tinsert(itemList, TSMAPI:GetItemString(searchTerm))
else
isItemList = nil
end
end
TSMAPI:FireEvent("SHOPPING:SEARCH:STARTFILTERSCAN")
if isItemList then
TSM.Util:StartItemScan(itemList, ScanCallback)
else
TSM.Util:StartFilterScan(Search:GetFilters(filter), ScanCallback)
end
end
function Search:SetSearchBarDisabled(disabled)
if disabled then
private.searchBar:Disable()
else
private.searchBar:Enable()
end
end
function Search:SetMode(mode)
if mode == "normal" then
private.searchBar.normal:Click()
elseif mode == "destroy" then
private.searchBar.destroy:Click()
end
end
function Search:SetSearchText(text)
private.searchBar.editBox:SetText(text)
end
-- ------------------------------------------------ --
-- Search Filter Util functions --
-- ------------------------------------------------ --
local function GetMaxQuantity(str)
if #str > 1 and strsub(str, 1, 1) == "x" then
return tonumber(strsub(str, 2))
end
end
local function GetItemLevel(str)
if #str > 1 and strsub(str, 1, 1) == "i" then
return tonumber(strsub(str, 2))
end
end
local function GetItemClass(str)
for i, class in ipairs({GetAuctionItemClasses()}) do
if strlower(str) == strlower(class) then
return i
end
end
end
local function GetItemSubClass(str, class)
if not class then return end
for i, subClass in ipairs({GetAuctionItemSubClasses(class)}) do
if strlower(str) == strlower(subClass) then
return i
end
end
end
local function GetItemRarity(str)
for i=0, 4 do
local text = _G["ITEM_QUALITY"..i.."_DESC"]
if strlower(str) == strlower(text) then
return i
end
end
end
local function GetSearchFilterOptions(searchTerm)
local parts = {("/"):split(searchTerm)}
local queryString, class, subClass, minLevel, maxLevel, minILevel, maxILevel, rarity, usableOnly, exactOnly, evenOnly, maxQuantity, maxPrice
if #parts == 1 then
return true, parts[1]
elseif #parts == 0 then
return false, L["Invalid Filter"]
end
for i, str in ipairs(parts) do
str = str:trim()
if tonumber(str) then
if not minLevel then
minLevel = tonumber(str)
elseif not maxLevel then
maxLevel = tonumber(str)
else
return false, L["Invalid Min Level"]
end
elseif GetMaxQuantity(str) then
if not maxQuantity then
maxQuantity = GetMaxQuantity(str)
else
return false, L["Invalid Max Quantity"]
end
elseif GetItemLevel(str) then
if not minILevel then
minILevel = GetItemLevel(str)
elseif not maxILevel then
maxILevel = GetItemLevel(str)
else
return false, L["Invalid Item Level"]
end
elseif not class and GetItemClass(str) then
if not class then
class = GetItemClass(str)
else
return false, L["Invalid Item Type"]
end
elseif GetItemSubClass(str, class) then
if not subClass then
subClass = GetItemSubClass(str, class)
else
return false, L["Invalid Item SubType"]
end
elseif GetItemRarity(str) then
if not rarity then
rarity = GetItemRarity(str)
else
return false, L["Invalid Item Rarity"]
end
elseif strlower(str) == "usable" then
if not usableOnly then
usableOnly = 1
else
return false, L["Invalid Usable Only Filter"]
end
elseif strlower(str) == "exact" then
if not exactOnly then
exactOnly = 1
else
return false, L["Invalid Exact Only Filter"]
end
elseif strlower(str) == "even" then
if not evenOnly then
evenOnly = 1
else
return false, L["Invalid Even Only Filter"]
end
elseif TSMAPI:UnformatTextMoney(str) then
maxPrice = TSMAPI:UnformatTextMoney(str)
elseif i == 1 then
if strfind(str, "item:([0-9]+):?([0-9]*):?([0-9]*):?([0-9]*):?([0-9]*):?([0-9]*):?%-?([0-9]*)$") then --or strfind(str, "battlepet:([0-9]+):?([0-9]*):?([0-9]*):?([0-9]*):?([0-9]*):?([0-9]*):?([0-9]*)$") then
queryString = TSMAPI:GetSafeItemInfo(str)
else
queryString = str
end
else
return false, L["Unknown Filter"]
end
end
if maxLevel and minLevel and maxLevel < minLevel then
local oldMaxLevel = maxLevel
maxLevel = minLevel
minLevel = oldMaxLevel
end
if maxILevel and minILevel and maxILevel < minILevel then
local oldMaxILevel = maxILevel
maxILevel = minILevel
minILevel = oldMaxILevel
end
return true, queryString or "", class or 0, subClass or 0, minLevel or 0, maxLevel or 0, minILevel or 0, maxILevel or 0, rarity or -1, usableOnly or 0, exactOnly or nil, evenOnly or nil, maxQuantity or 0, maxPrice
--return true, queryString or "", class or 0, subClass or 0, minLevel or 0, maxLevel or 0, minILevel or 0, maxILevel or 0, rarity or 0, usableOnly or 0, exactOnly or nil, evenOnly or nil, maxQuantity or 0, maxPrice
end
-- gets all the filters for a given search term (possibly semicolon-deliminated list of search terms)
function Search:GetFilters(searchQuery)
local filters = {}
local searchTerms = {(";"):split(searchQuery)}
filters.num = 0
for i=1, #searchTerms do
local searchTerm = searchTerms[i]:trim()
if tonumber(searchTerm) then
local filter = TSMAPI:GetAuctionQueryInfo(TSMAPI:GetItemString(searchTerm))
if filter then
tinsert(filters, filter)
filters.num = filters.num + 1
if filters.currentFilter then
filters.currentFilter = filters.currentFilter.."; "..searchTerm
else
filters.currentFilter = searchTerm
end
if filters.currentSearchTerm then
filters.currentSearchTerm = filters.currentSearchTerm .. "; "..searchTerm
else
filters.currentSearchTerm = searchTerm
end
end
else
local isValid, queryString, class, subClass, minLevel, maxLevel, minILevel, maxILevel, rarity, usableOnly, exactOnly, evenOnly, maxQuantity, maxPrice = GetSearchFilterOptions(searchTerm)
if not isValid then
TSM:Print(L["Skipped the following search term because it's invalid."])
TSM:Print("\""..searchTerm.."\": "..queryString)
elseif strlenutf8(queryString) > 63 then
TSM:Print(L["Skipped the following search term because it's too long. Blizzard does not allow search terms over 63 characters."])
TSM:Print("\""..searchTerm.."\"")
isValid = nil
end
if isValid then
filters.num = filters.num + 1
if filters.currentFilter then
filters.currentFilter = filters.currentFilter.."; "..queryString
else
filters.currentFilter = queryString
end
if filters.currentSearchTerm then
filters.currentSearchTerm = filters.currentSearchTerm .. "; "..searchTerm
else
filters.currentSearchTerm = searchTerm
end
tinsert(filters, {name=queryString, usable=usableOnly, minLevel=minLevel, maxLevel=maxLevel, quality=rarity, class=class, subClass=subClass, minILevel=minILevel, maxILevel=maxILevel, exactOnly=exactOnly, evenOnly=evenOnly, maxQuantity=maxQuantity, maxPrice=maxPrice})
end
end
end
return filters
end
function Search:GetCurrentSearchMode()
return private.mode
end
+459
View File
@@ -0,0 +1,459 @@
-- ------------------------------------------------------------------------------ --
-- TradeSkillMaster_Shopping --
-- http://www.curse.com/addons/wow/tradeskillmaster_shopping --
-- --
-- A TradeSkillMaster Addon (http://tradeskillmaster.com) --
-- All Rights Reserved* - Detailed license information included with addon. --
-- ------------------------------------------------------------------------------ --
local TSM = select(2, ...)
local Util = TSM:NewModule("Util", "AceEvent-3.0", "AceHook-3.0")
local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_Shopping") -- loads the localization table
local private = {auctions={}}
TSMAPI:RegisterForTracing(private, "TradeSkillMaster_Shopping_private")
Util.shoppingLog = {}
local function ControlCallback(event, ...)
if event == "OnBuyout" then
local auction = ...
tinsert(Util.shoppingLog, {action="Buyout", link=auction.link, buyout=auction.buyout, count=auction.count})
private:RemoveAuction(auction, event, TSMAPI:GetItemString(auction.link))
elseif event == "OnCancel" then
local auction = ...
tinsert(Util.shoppingLog, {action="Cancel", link=auction.link, buyout=auction.buyout, count=auction.count})
private:RemoveAuction(auction, event, TSMAPI:GetItemString(auction.link))
elseif event == "OnPost" then
local postInfo = ...
local link = select(2, TSMAPI:GetSafeItemInfo(postInfo.itemString))
for i=1, postInfo.numAuctions do
tinsert(Util.shoppingLog, {auction="Post", link=link, buyout=postInfo.buyout, count=postInfo.stackSize})
end
private:AddPostedAuction(postInfo)
end
if TSM.searchCallback then
TSM.searchCallback(event, ...)
end
end
function private:HasInBags(baseItemString)
for _, _, itemString in TSMAPI:GetBagIterator() do
if TSMAPI:GetBaseItemString(itemString) == baseItemString then
return true
end
end
end
function private:CreateSearchFrame()
local function OnShow(self)
if not self.info then return end
if self.info.isDestroying then
self.rtNormal:Hide()
self.rtDestroying:Show()
self.rt = private.searchFrame.rtDestroying
else
self.rtNormal:Show()
self.rtDestroying:Hide()
self.rt = private.searchFrame.rtNormal
end
self.rt:SetColHeadText(#self.rt.headCols, self.info.pctColName)
end
local frame = CreateFrame("Frame", nil, private.parent.content)
frame:Hide()
frame:SetAllPoints()
frame:SetScript("OnShow", OnShow)
local statusBarFrame = CreateFrame("Frame", nil, frame)
statusBarFrame:SetPoint("TOPLEFT", frame, "BOTTOMLEFT", 165, -2)
statusBarFrame:SetWidth(250)
statusBarFrame:SetHeight(30)
frame.statusBar = TSMAPI.GUI:CreateStatusBar(statusBarFrame, "TSMShoppingStatusBar")
local handlers = {
OnClick = function(_, data, self, button)
-- they clicked on a data row
if button == "LeftButton" then
-- go to the page for this item
local record = data.auctionRecord
TSMAPI.AuctionScan:FindAuction(function() end, {itemString=data.itemString, buyout=record.buyout, count=record.count, seller=record.seller}, true)
if data.auctionRecord:IsPlayer() then
private.controlButtons.cancel:Enable()
private.controlButtons.buyout:Disable()
private.controlButtons.post:Disable()
elseif data.auctionRecord.buyout == 0 then
private.controlButtons.buyout:Disable()
private.controlButtons.cancel:Disable()
private.controlButtons.post:Disable()
else
private.controlButtons.buyout:Enable()
private.controlButtons.cancel:Disable()
private.controlButtons.post:Disable()
end
if private:HasInBags(TSMAPI:GetBaseItemString(data.itemString)) then
private.controlButtons.post:Enable()
end
end
end,
}
local rt = TSMAPI:CreateAuctionResultsTable(frame, handlers, true)
rt:SetData({})
rt:SetSort(8, true)
frame.rtNormal = rt
local rt2 = TSMAPI:CreateAuctionResultsTable(frame, handlers, true, true)
rt2:SetData({})
rt2:SetSort(5, true)
frame.rtDestroying = rt2
return frame
end
function Util:SetParent(parent)
private.parent = parent
end
function Util:ShowSearchFrame(isDestroying, pctColName, clearRT)
if private.searchFrame and private.searchFrame:IsVisible() then
Util:HideSearchFrame()
end
private.searchFrame = private.searchFrame or private:CreateSearchFrame()
private.searchFrame.info = {isDestroying=isDestroying, pctColName=pctColName}
private.searchFrame:Show()
if clearRT then
private.searchFrame.rtNormal:SetData({})
private.searchFrame.rtDestroying:SetData({})
end
private.controlButtons = TSMAPI.AuctionControl:ShowControlButtons(private.parent, private.searchFrame.rt, ControlCallback, "Shopping", TSM.db.global.postBidPercent, TSM.db.global.postUndercut)
private.controlButtons.buyout:Disable()
private.controlButtons.cancel:Disable()
private.controlButtons.post:Disable()
TSMAPI.AuctionScan:StopScan()
TSMAPI.AuctionScan:ClearCache()
private.searchFrame.statusBar:SetStatusText("")
private.searchFrame.statusBar:UpdateStatus(0, 0)
private.mode = isDestroying and "destroy" or "normal"
TSM.Search:SetMode(private.mode)
end
function Util:HideSearchFrame()
private.searchFrame:Hide()
TSMAPI.AuctionControl:HideControlButtons()
TSMAPI.AuctionScan:StopScan()
TSMAPI.AuctionScan:ClearCache()
end
function Util:StartItemScan(itemList, callback)
if type(itemList) ~= "table" then return end
private:PrepareForScan(callback)
if #itemList == 1 then private.searchItem = itemList[1] end
TSMAPI:GenerateQueries(itemList, private.ScanCallback)
end
function Util:StartFilterScan(filters, callback)
if type(filters) ~= "table" then return end
private:PrepareForScan(callback)
if #filters == 1 then
for _, _, itemString in TSMAPI:GetBagIterator() do
local name = TSMAPI:GetSafeItemInfo(itemString)
if name and filters[1].name and strlower(name) == strlower(filters[1].name) then
private.searchItem = itemString
break
end
end
end
private.filterList = filters
private.numFilters = #private.filterList
private:ScanNextFilter()
end
function Util:StartLastPageScan(callback)
private:PrepareForScan(callback, true)
TSMAPI.AuctionScan:ScanLastPage(private.ScanCallback)
end
function Util:StopScan()
TSMAPI:CancelFrame("shoppingRestartSniper")
TSMAPI.AuctionScan:StopScan()
private:ScanComplete()
end
function private:PrepareForScan(callback, isLastPageScan)
TSMAPI:CancelFrame("shoppingRestartSniper")
TSMAPI.AuctionScan:StopScan()
private.searchItem = nil
private.isLastPageScan = isLastPageScan
private.callback = callback
wipe(private.auctions)
if private.isLastPageScan then
private.searchFrame.statusBar:SetStatusText("Scanning last page...")
else
private.searchFrame.statusBar:SetStatusText(L["Preparing filters..."])
end
private.searchFrame.rt:SetData({})
private.searchFrame.rt:SetDisabled(true)
private.searchFrame.statusBar:UpdateStatus(0, 0)
TSM.moduleAPICallback = nil
end
local scanStatus, pageStatus
function private.ScanCallback(event, ...)
if event == "QUERY_COMPLETE" then
private.filterList = ...
private.numFilters = #private.filterList
private:ScanNextFilter()
elseif event == "QUERY_UPDATE" then
local arg1, arg2 = ...
private:UpdateStatus("query", arg1, arg2)
elseif event == "SCAN_PAGE_UPDATE" then
private:UpdateStatus("page", ...)
elseif event == "SCAN_TIMEOUT" then
tremove(private.filterList, 1)
private:ScanNextFilter()
elseif event == "SCAN_COMPLETE" then
if not private.filterList or not private.filterList[1] then return end -- protect against sniper scan starts causing issues
local data = ...
if private.filterList[1].items then
for _, itemString in ipairs(private.filterList[1].items) do
if data[itemString] then
if data[itemString].isBaseItem then
for iString, auctionitem in pairs(data) do
if iString ~= itemString and TSMAPI:GetBaseItemString(iString) == itemString then
auctionitem.query = private.filterList[1]
private:ProcessItem(iString, auctionitem)
end
end
else
data[itemString].query = private.filterList[1]
private:ProcessItem(itemString, data[itemString])
end
end
end
else
for itemString, auctionData in pairs(data) do
if not auctionData.isBaseItem then
auctionData.query = private.filterList[1]
private:ProcessItem(itemString, auctionData)
end
end
end
private:UpdateRT()
private.searchFrame.rt:ClearSelection()
tremove(private.filterList, 1)
private:ScanNextFilter()
elseif event == "SCAN_LAST_PAGE_COMPLETE" then
local data = ...
for itemString, auctionData in pairs(data) do
if not auctionData.isBaseItem then
if auctionData and #auctionData.records > 0 then
if private.auctions[itemString] then
private.auctions[itemString].shouldCompact = true
private.auctions[itemString]:PopulateCompactRecords()
local existingRecords = {}
for _, record in ipairs(private.auctions[itemString].compactRecords) do
local key = strjoin("~", record.uniqueID, record.count, record.buyout, record.minBid, record.timeLeft)
existingRecords[key] = true
end
for _, record in ipairs(auctionData.records) do
local key = strjoin("~", record.uniqueID, record.count, record.buyout, record.minBid, record.timeLeft)
if not existingRecords[key] then
private.auctions[itemString]:AddRecord(record)
else
for _, record2 in ipairs(private.auctions[itemString].records) do
local key2 = strjoin("~", record.uniqueID, record.count, record.buyout, record.minBid, record.timeLeft)
if key2 == key and record2.seller ~= "?" then
record2.seller = record.seller
break
end
end
end
end
else
private.auctions[itemString] = auctionData
end
private.auctions[itemString] = private.callback("process", itemString, private.auctions[itemString])
end
end
end
private:UpdateRT()
private.searchFrame.rt:ClearSelection()
TSMAPI:CreateTimeDelay("shoppingRestartSniper", 0, function() TSMAPI.AuctionScan:ScanLastPage(private.ScanCallback) end)
end
end
function private:ScanNextFilter()
if #private.filterList == 0 then
return private:ScanComplete()
end
pageStatus = {0, 1}
private:UpdateStatus("scan", private.numFilters-#private.filterList+1, private.numFilters)
TSMAPI.AuctionScan:RunQuery(private.filterList[1], private.ScanCallback, true, private.callback("filter", private.filterList[1]), true)
end
function private:UpdateStatus(statusType, ...)
if statusType == "query" then
private.searchFrame.statusBar:SetStatusText(format(L["Preparing Filter %d / %d"], ...))
private.searchFrame.statusBar:UpdateStatus(0, 0)
else
if statusType == "scan" then
scanStatus = {...}
elseif statusType == "page" then
pageStatus = {...}
end
private.searchFrame.statusBar:SetStatusText(format(L["Scanning %d / %d (Page %d / %d)"], scanStatus[1], scanStatus[2], pageStatus[1]+1, pageStatus[2]))
private.searchFrame.statusBar:UpdateStatus(100*(scanStatus[1]-1)/scanStatus[2], 100*pageStatus[1]/pageStatus[2])
end
end
function private:ScanComplete()
if not private.callback then return end
private.searchFrame.statusBar:SetStatusText(L["Done Scanning"])
private.searchFrame.statusBar:UpdateStatus(100, 100)
private.searchFrame.rt:SetDisabled(false)
if #private.searchFrame.rt.auctionData == 1 then
private.searchFrame.rt:SetExpanded(private.searchFrame.rt.auctionData[1]:GetItemString(), true)
private.searchFrame.rt.rows[1].cols[1]:Click()
elseif #private.searchFrame.rt.auctionData == 0 and private.searchItem and private:HasInBags(TSMAPI:GetBaseItemString(private.searchItem)) then
private.controlButtons.post:Enable()
local postPrice = TSM:GetMaxPrice(TSM.db.global.normalPostPrice, private.searchItem) or 0
TSMAPI.AuctionControl:SetNoResultItem(private.searchItem, postPrice)
end
if #private.searchFrame.rt.auctionData == 0 and TSM.moduleAPICallback then
TSM.moduleAPICallback()
end
private.callback("done", private.auctions)
TSMAPI:FireEvent("SHOPPING:SEARCH:SCANDONE", #private.searchFrame.rt.auctionData)
end
-- processes scan data for a specific item
function private:ProcessItem(itemString, auctionItem)
-- make sure we haven't already scanned this item (possible with common search terms)
if private.auctions[itemString] then return end
if not itemString or not auctionItem then return end
local query = auctionItem.query
query.minILevel = query.minILevel or 0
query.maxILevel = query.maxILevel or 0
query.minLevel = query.minLevel or 0
query.maxLevel = query.maxLevel or 0
local name, _, _, ilvl, lvl = TSMAPI:GetSafeItemInfo(itemString)
-- check if this item is outside our level or ilvl filters
if query.minILevel > 0 and (ilvl < query.minILevel or (query.maxILevel > 0 and ilvl > query.maxILevel)) then
private.auctions[itemString] = nil
return
end
if query.minLevel > 0 and (lvl < query.minLevel or (query.maxLevel > 0 and lvl > query.maxLevel)) then
private.auctions[itemString] = nil
return
end
-- check for /exact filter
if query.exactOnly and strlower(name) ~= strlower(query.name) then
private.auctions[itemString] = nil
return
end
-- remove any records that don't have buyouts
for i=#auctionItem.records, 1, -1 do
local record = auctionItem.records[i]
if not record.buyout or record.buyout == 0 then
auctionItem:RemoveRecord(i)
end
end
-- check if this auctionItem has records left
if #auctionItem.records == 0 then return end
auctionItem = private.callback("process", itemString, auctionItem)
if not auctionItem or #auctionItem.records == 0 then return end
-- store auctionItem
auctionItem:PopulateCompactRecords()
private.auctions[itemString] = auctionItem
end
function private:UpdateRT()
local rtData = {}
for _, obj in pairs(private.auctions) do
tinsert(rtData, obj)
end
private.searchFrame.rt:SetData(rtData)
end
function private:RemoveAuction(auction, event, itemString)
if private.auctions[itemString] then
-- remove this record from the auctionItem
for i, record in ipairs(private.auctions[itemString].records) do
if record.parent.itemLink == auction.link and record.buyout == auction.buyout and record.count == auction.count and record.seller == auction.seller then
private.auctions[itemString]:RemoveRecord(i)
if #private.auctions[itemString].records == 0 then
private.auctions[itemString] = nil
else
-- handle max quantities on queries
local query = private.auctions[itemString].query
if event == "OnBuyout" and query then
if private.mode == "normal" and (query.maxQuantity or 0) > 0 then
query.maxQuantity = query.maxQuantity - auction.count
if TSM.moduleAPICallback then TSM.moduleAPICallback(max(query.maxQuantity, 0), itemString, auction.count) end
for item, auctionItem in pairs(private.auctions) do
if auctionItem.query and auctionItem.query.maxQuantity and auctionItem.query.maxQuantity <= 0 then
private.auctions[item] = nil
end
end
if not private.auctions[itemString] then
private.controlButtons.buyout:Disable()
TSMAPI.AuctionControl:HideConfirmation()
TSM:Printf(L["Maximum quantity purchased for %s."], auction.link)
end
elseif private.mode == "destroy" and (TSM.Destroying.maxQuantity or 0) > 0 then
TSM.Destroying.maxQuantity = TSM.Destroying.maxQuantity - auction.count / auction.destroyingNum
if TSM.moduleAPICallback then TSM.moduleAPICallback(max(TSM.Destroying.maxQuantity, 0), itemString, auction.count) end
if TSM.Destroying.maxQuantity <= 0 then
private.controlButtons.buyout:Disable()
TSMAPI.AuctionControl:HideConfirmation()
TSM:Printf(L["Maximum quantity purchased for destroy search."])
end
end
end
end
break
end
end
end
local baseItemString = TSMAPI:GetBaseItemString(itemString)
if baseItemString ~= itemString then
return private:RemoveAuction(auction, event, baseItemString)
end
private:UpdateRT()
local selected = private.searchFrame.rt:GetSelectedAuction()
if not TSMAPI.AuctionControl:IsConfirmationVisible() then
-- select the auction that was previously selected
if not private.searchFrame.rt:GetSelectedAuction() and selected then
-- we bought all of this auction, so select the new first occurace of this item
private.searchFrame.rt:SetSelectedAuction(selected.parent:GetItemString())
end
end
end
function private:AddPostedAuction(postInfo)
local link = select(2, TSMAPI:GetSafeItemInfo(postInfo.itemString))
local texture = select(10, TSMAPI:GetSafeItemInfo(postInfo.itemString))
if not private.auctions[postInfo.itemString] then
private.auctions[postInfo.itemString] = TSMAPI.AuctionScan:NewAuctionItem()
private.auctions[postInfo.itemString]:SetItemLink(link)
private.auctions[postInfo.itemString]:SetTexture(texture)
end
private.auctions[postInfo.itemString]:AddAuctionRecord(postInfo.stackSize, postInfo.bid, 0, postInfo.buyout, 0, nil, UnitName("player"), postInfo.duration)
private.auctions[postInfo.itemString]:PopulateCompactRecords()
private:UpdateRT()
private.searchFrame.rt:SetSelectedAuction()
end