819 lines
29 KiB
Lua
819 lines
29 KiB
Lua
|
-- ------------------------------------------------------------------------------ --
|
||
|
-- TradeSkillMaster --
|
||
|
-- https://tradeskillmaster.com --
|
||
|
-- All Rights Reserved - Detailed license information included with addon. --
|
||
|
-- ------------------------------------------------------------------------------ --
|
||
|
|
||
|
local _, TSM = ...
|
||
|
local Sniper = TSM.UI.AuctionUI:NewPackage("Sniper")
|
||
|
local L = TSM.Include("Locale").GetTable()
|
||
|
local Delay = TSM.Include("Util.Delay")
|
||
|
local Event = TSM.Include("Util.Event")
|
||
|
local FSM = TSM.Include("Util.FSM")
|
||
|
local Sound = TSM.Include("Util.Sound")
|
||
|
local Money = TSM.Include("Util.Money")
|
||
|
local Log = TSM.Include("Util.Log")
|
||
|
local ItemString = TSM.Include("Util.ItemString")
|
||
|
local ItemInfo = TSM.Include("Service.ItemInfo")
|
||
|
local AuctionScan = TSM.Include("Service.AuctionScan")
|
||
|
local MailTracking = TSM.Include("Service.MailTracking")
|
||
|
local Settings = TSM.Include("Service.Settings")
|
||
|
local AuctionHouseWrapper = TSM.Include("Service.AuctionHouseWrapper")
|
||
|
local PlayerInfo = TSM.Include("Service.PlayerInfo")
|
||
|
local UIElements = TSM.Include("UI.UIElements")
|
||
|
local private = {
|
||
|
settings = nil,
|
||
|
fsm = nil,
|
||
|
selectionFrame = nil,
|
||
|
hasLastScan = nil,
|
||
|
contentPath = "selection",
|
||
|
}
|
||
|
local PHASED_TIME = 60
|
||
|
local RETAIL_RESCAN_DELAY = 30
|
||
|
|
||
|
|
||
|
|
||
|
-- ============================================================================
|
||
|
-- Module Functions
|
||
|
-- ============================================================================
|
||
|
|
||
|
function Sniper.OnInitialize()
|
||
|
private.settings = Settings.NewView()
|
||
|
:AddKey("global", "auctionUIContext", "sniperScrollingTable")
|
||
|
:AddKey("global", "sniperOptions", "sniperSound")
|
||
|
TSM.UI.AuctionUI.RegisterTopLevelPage(L["Sniper"], private.GetSniperFrame, private.OnItemLinked)
|
||
|
private.FSMCreate()
|
||
|
end
|
||
|
|
||
|
|
||
|
|
||
|
-- ============================================================================
|
||
|
-- Sniper UI
|
||
|
-- ============================================================================
|
||
|
|
||
|
function private.GetSniperFrame()
|
||
|
TSM.UI.AnalyticsRecordPathChange("auction", "sniper")
|
||
|
if not private.hasLastScan then
|
||
|
private.contentPath = "selection"
|
||
|
end
|
||
|
return UIElements.New("ViewContainer", "sniper")
|
||
|
:SetNavCallback(private.GetSniperContentFrame)
|
||
|
:AddPath("selection")
|
||
|
:AddPath("scan")
|
||
|
:SetPath(private.contentPath)
|
||
|
end
|
||
|
|
||
|
function private.GetSniperContentFrame(viewContainer, path)
|
||
|
private.contentPath = path
|
||
|
if path == "selection" then
|
||
|
return private.GetSelectionFrame()
|
||
|
elseif path == "scan" then
|
||
|
return private.GetScanFrame()
|
||
|
else
|
||
|
error("Unexpected path: "..tostring(path))
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function private.GetSelectionFrame()
|
||
|
TSM.UI.AnalyticsRecordPathChange("auction", "sniper", "selection")
|
||
|
local frame = UIElements.New("Frame", "selection")
|
||
|
:SetLayout("VERTICAL")
|
||
|
:SetBackgroundColor("PRIMARY_BG_ALT")
|
||
|
:AddChildIf(TSM.IsWowClassic(), UIElements.New("Text", "text")
|
||
|
:SetHeight(20)
|
||
|
:SetMargin(8, 8, 12, 0)
|
||
|
:SetFont("BODY_BODY2_MEDIUM")
|
||
|
:SetJustifyH("CENTER")
|
||
|
:SetText(L["Start either a 'Buyout' or 'Bid' sniper using the buttons above."])
|
||
|
)
|
||
|
:AddChild(UIElements.New("Frame", "buttons")
|
||
|
:SetLayout("HORIZONTAL")
|
||
|
:SetHeight(24)
|
||
|
:SetMargin(8, 8, 12, 12)
|
||
|
:AddChild(UIElements.New("ActionButton", "buyoutScanBtn")
|
||
|
:SetMargin(0, TSM.IsWowClassic() and 8 or 0, 0, 0)
|
||
|
:SetText(L["Run Buyout Sniper"])
|
||
|
:SetScript("OnClick", private.BuyoutScanButtonOnClick)
|
||
|
)
|
||
|
:AddChildIf(TSM.IsWowClassic(), UIElements.New("ActionButton", "bidScanBtn")
|
||
|
:SetText(L["Run Bid Sniper"])
|
||
|
:SetScript("OnClick", private.BidScanButtonOnClick)
|
||
|
)
|
||
|
)
|
||
|
:AddChild(UIElements.New("SniperScrollingTable", "auctions")
|
||
|
:SetSettingsContext(private.settings, "sniperScrollingTable")
|
||
|
)
|
||
|
:SetScript("OnHide", private.SelectionFrameOnHide)
|
||
|
private.selectionFrame = frame
|
||
|
return frame
|
||
|
end
|
||
|
|
||
|
function private.GetScanFrame()
|
||
|
TSM.UI.AnalyticsRecordPathChange("auction", "sniper", "scan")
|
||
|
return UIElements.New("Frame", "scan")
|
||
|
:SetLayout("VERTICAL")
|
||
|
:SetBackgroundColor("PRIMARY_BG_ALT")
|
||
|
:AddChild(UIElements.New("Frame", "header")
|
||
|
:SetLayout("HORIZONTAL")
|
||
|
:SetHeight(48)
|
||
|
:SetPadding(8, 8, 14, 14)
|
||
|
:AddChild(UIElements.New("ActionButton", "backBtn")
|
||
|
:SetSize(64, 24)
|
||
|
:SetMargin(0, 16, 0, 0)
|
||
|
:SetIcon("iconPack.14x14/Chevron/Right@180")
|
||
|
:SetText(BACK)
|
||
|
:SetScript("OnClick", private.BackButtonOnClick)
|
||
|
)
|
||
|
:AddChild(UIElements.New("Text", "title")
|
||
|
:SetFont("BODY_BODY2_MEDIUM")
|
||
|
:SetJustifyH("CENTER")
|
||
|
)
|
||
|
:AddChild(UIElements.New("ActionButton", "restartBtn")
|
||
|
:SetSize(80, 24)
|
||
|
:SetText(L["Restart"])
|
||
|
:SetScript("OnClick", private.RestartButtonOnClick)
|
||
|
)
|
||
|
)
|
||
|
:AddChild(UIElements.New("SniperScrollingTable", "auctions")
|
||
|
:SetSettingsContext(private.settings, "sniperScrollingTable")
|
||
|
:SetScript("OnSelectionChanged", private.AuctionsOnSelectionChanged)
|
||
|
:SetScript("OnRowRemoved", private.AuctionsOnRowRemoved)
|
||
|
)
|
||
|
:AddChild(UIElements.New("Texture", "line")
|
||
|
:SetHeight(2)
|
||
|
:SetTexture("ACTIVE_BG")
|
||
|
)
|
||
|
:AddChild(UIElements.New("Frame", "bottom")
|
||
|
:SetLayout("HORIZONTAL")
|
||
|
:SetHeight(40)
|
||
|
:SetPadding(8)
|
||
|
:SetBackgroundColor("PRIMARY_BG_ALT")
|
||
|
:AddChild(UIElements.New("ActionButton", "pauseResumeBtn")
|
||
|
:SetSize(24, 24)
|
||
|
:SetMargin(0, 8, 0, 0)
|
||
|
:SetIcon("iconPack.18x18/PlayPause")
|
||
|
:SetScript("OnClick", private.PauseResumeBtnOnClick)
|
||
|
)
|
||
|
:AddChild(UIElements.New("ProgressBar", "progressBar")
|
||
|
:SetHeight(24)
|
||
|
:SetMargin(0, 8, 0, 0)
|
||
|
:SetProgress(0)
|
||
|
:SetText(L["Starting Scan..."])
|
||
|
)
|
||
|
:AddChild(UIElements.NewNamed("ActionButton", "actionBtn", "TSMSniperBtn")
|
||
|
:SetSize(165, 24)
|
||
|
:SetText(BID)
|
||
|
:SetDisabled(true)
|
||
|
:DisableClickCooldown(true)
|
||
|
:SetScript("OnClick", private.ActionButtonOnClick)
|
||
|
)
|
||
|
)
|
||
|
:SetScript("OnUpdate", private.ScanFrameOnUpdate)
|
||
|
:SetScript("OnHide", private.ScanFrameOnHide)
|
||
|
end
|
||
|
|
||
|
|
||
|
|
||
|
-- ============================================================================
|
||
|
-- Local Script Handlers
|
||
|
-- ============================================================================
|
||
|
|
||
|
function private.OnItemLinked(name, itemLink)
|
||
|
if private.selectionFrame then
|
||
|
return false
|
||
|
end
|
||
|
private.fsm:ProcessEvent("EV_STOP_CLICKED")
|
||
|
TSM.UI.AuctionUI.SetOpenPage(L["Browse"])
|
||
|
TSM.UI.AuctionUI.Shopping.StartItemSearch(itemLink)
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
function private.SelectionFrameOnHide(frame)
|
||
|
assert(frame == private.selectionFrame)
|
||
|
private.selectionFrame = nil
|
||
|
end
|
||
|
|
||
|
function private.StartScanHelper(viewContainer, searchContext)
|
||
|
if not TSM.UI.AuctionUI.StartingScan(L["Sniper"]) then
|
||
|
return
|
||
|
end
|
||
|
viewContainer:SetPath("scan", true)
|
||
|
private.fsm:ProcessEvent("EV_START_SCAN", searchContext)
|
||
|
end
|
||
|
|
||
|
function private.BuyoutScanButtonOnClick(button)
|
||
|
local viewContainer = button:GetParentElement():GetParentElement():GetParentElement()
|
||
|
local searchContext = TSM.Sniper.BuyoutSearch.GetSearchContext()
|
||
|
private.StartScanHelper(viewContainer, searchContext)
|
||
|
end
|
||
|
|
||
|
function private.BidScanButtonOnClick(button)
|
||
|
local viewContainer = button:GetParentElement():GetParentElement():GetParentElement()
|
||
|
local searchContext = TSM.Sniper.BidSearch.GetSearchContext()
|
||
|
private.StartScanHelper(viewContainer, searchContext)
|
||
|
end
|
||
|
|
||
|
function private.AuctionsOnSelectionChanged()
|
||
|
private.fsm:ProcessEvent("EV_AUCTION_SELECTION_CHANGED")
|
||
|
end
|
||
|
|
||
|
function private.AuctionsOnRowRemoved(_, row)
|
||
|
private.fsm:ProcessEvent("EV_AUCTION_ROW_REMOVED", row)
|
||
|
end
|
||
|
|
||
|
function private.BackButtonOnClick()
|
||
|
private.fsm:ProcessEvent("EV_STOP_CLICKED")
|
||
|
end
|
||
|
|
||
|
function private.PauseResumeBtnOnClick(button)
|
||
|
private.fsm:ProcessEvent("EV_PAUSE_RESUME_CLICKED")
|
||
|
end
|
||
|
|
||
|
function private.ActionButtonOnClick(button)
|
||
|
private.fsm:ProcessEvent("EV_ACTION_CLICKED")
|
||
|
end
|
||
|
|
||
|
function private.RestartButtonOnClick(button)
|
||
|
if not TSM.UI.AuctionUI.StartingScan(L["Sniper"]) then
|
||
|
return
|
||
|
end
|
||
|
local lastScanType = private.hasLastScan
|
||
|
local sniperFrame = button:GetParentElement():GetParentElement():GetParentElement()
|
||
|
private.fsm:ProcessEvent("EV_STOP_CLICKED")
|
||
|
if lastScanType == "bid" then
|
||
|
sniperFrame:GetElement("selection.buttons.bidScanBtn"):Click()
|
||
|
elseif lastScanType == "buyout" then
|
||
|
sniperFrame:GetElement("selection.buttons.buyoutScanBtn"):Click()
|
||
|
else
|
||
|
error("Invalid last scan type: "..tostring(lastScanType))
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function private.ScanFrameOnUpdate(frame)
|
||
|
frame:SetScript("OnUpdate", nil)
|
||
|
private.fsm:ProcessEvent("EV_SCAN_FRAME_SHOWN", frame)
|
||
|
end
|
||
|
|
||
|
function private.ScanFrameOnHide(frame)
|
||
|
private.fsm:ProcessEvent("EV_SCAN_FRAME_HIDDEN")
|
||
|
end
|
||
|
|
||
|
|
||
|
|
||
|
-- ============================================================================
|
||
|
-- FSM
|
||
|
-- ============================================================================
|
||
|
|
||
|
function private.FSMCreate()
|
||
|
local fsmContext = {
|
||
|
scanFrame = nil,
|
||
|
auctionScan = nil,
|
||
|
query = nil,
|
||
|
progress = 0,
|
||
|
progressText = L["Running Sniper Scan"],
|
||
|
buttonsDisabled = true,
|
||
|
findHash = nil,
|
||
|
findAuction = nil,
|
||
|
findResult = nil,
|
||
|
numFound = 0,
|
||
|
maxQuantity = 0,
|
||
|
numActioned = 0,
|
||
|
lastBuyQuantity = 0,
|
||
|
numConfirmed = 0,
|
||
|
searchContext = nil,
|
||
|
scanDone = false,
|
||
|
}
|
||
|
Event.Register("CHAT_MSG_SYSTEM", private.FSMMessageEventHandler)
|
||
|
Event.Register("UI_ERROR_MESSAGE", private.FSMMessageEventHandler)
|
||
|
if not TSM.IsWowClassic() then
|
||
|
Event.Register("COMMODITY_PURCHASE_SUCCEEDED", private.FSMBuyoutSuccess)
|
||
|
end
|
||
|
Event.Register("AUCTION_HOUSE_CLOSED", function()
|
||
|
private.fsm:ProcessEvent("EV_AUCTION_HOUSE_CLOSED")
|
||
|
end)
|
||
|
AuctionHouseWrapper.RegisterAuctionIdUpdateCallback(function(...)
|
||
|
private.fsm:ProcessEvent("EV_AUCTION_ID_UPDATE", ...)
|
||
|
end)
|
||
|
local function UpdateScanFrame(context)
|
||
|
if not context.scanFrame or not context.searchContext then
|
||
|
return
|
||
|
end
|
||
|
local actionText = nil
|
||
|
if context.searchContext:IsBuyoutScan() then
|
||
|
actionText = BUYOUT
|
||
|
elseif context.searchContext:IsBidScan() then
|
||
|
actionText = BID
|
||
|
else
|
||
|
error("Invalid scan type")
|
||
|
end
|
||
|
local bottom = context.scanFrame:GetElement("bottom")
|
||
|
bottom:GetElement("actionBtn")
|
||
|
:SetText(actionText)
|
||
|
:SetDisabled(context.buttonsDisabled)
|
||
|
bottom:GetElement("progressBar")
|
||
|
:SetProgress(context.progress)
|
||
|
:SetText(context.progressText or "")
|
||
|
local auctionList = context.scanFrame:GetElement("auctions")
|
||
|
:SetContext(context.auctionScan)
|
||
|
:SetAuctionScan(context.auctionScan)
|
||
|
:SetMarketValueFunction(context.searchContext:GetMarketValueFunc())
|
||
|
if context.findAuction and not auctionList:GetSelection() then
|
||
|
auctionList:SetSelection(context.findAuction)
|
||
|
end
|
||
|
local title = context.scanFrame:GetElement("header.title")
|
||
|
if context.scanPausing or auctionList:GetSelection() then
|
||
|
if context.searchContext:IsBuyoutScan() then
|
||
|
title:SetText(L["Buyout Sniper Paused"])
|
||
|
elseif context.searchContext:IsBidScan() then
|
||
|
title:SetText(L["Bid Sniper Paused"])
|
||
|
else
|
||
|
error("Invalid scan type")
|
||
|
end
|
||
|
else
|
||
|
if context.searchContext:IsBuyoutScan() then
|
||
|
title:SetText(L["Buyout Sniper Running"])
|
||
|
elseif context.searchContext:IsBidScan() then
|
||
|
title:SetText(L["Bid Sniper Running"])
|
||
|
else
|
||
|
error("Invalid scan type")
|
||
|
end
|
||
|
end
|
||
|
context.scanFrame:Draw()
|
||
|
end
|
||
|
local function ScanOnFilterDone(self, filter, numNewResults)
|
||
|
if numNewResults > 0 then
|
||
|
Sound.PlaySound(private.settings.sniperSound)
|
||
|
end
|
||
|
end
|
||
|
private.fsm = FSM.New("SNIPER")
|
||
|
:AddState(FSM.NewState("ST_INIT")
|
||
|
:SetOnEnter(function(context, searchContext)
|
||
|
private.hasLastScan = nil
|
||
|
if context.searchContext then
|
||
|
context.searchContext:KillThread()
|
||
|
context.searchContext = nil
|
||
|
end
|
||
|
context.progress = 0
|
||
|
context.progressText = L["Running Sniper Scan"]
|
||
|
context.buttonsDisabled = true
|
||
|
context.findHash = nil
|
||
|
context.findAuction = nil
|
||
|
context.findResult = nil
|
||
|
context.numFound = 0
|
||
|
context.numActioned = 0
|
||
|
context.lastBuyQuantity = 0
|
||
|
context.numConfirmed = 0
|
||
|
if context.auctionScan then
|
||
|
context.auctionScan:Release()
|
||
|
context.auctionScan = nil
|
||
|
end
|
||
|
if searchContext then
|
||
|
context.searchContext = searchContext
|
||
|
return "ST_RUNNING_SCAN"
|
||
|
elseif context.scanFrame then
|
||
|
context.scanFrame:GetParentElement():SetPath("selection", true)
|
||
|
context.scanFrame = nil
|
||
|
end
|
||
|
TSM.UI.AuctionUI.EndedScan(L["Sniper"])
|
||
|
end)
|
||
|
:AddTransition("ST_INIT")
|
||
|
:AddTransition("ST_RUNNING_SCAN")
|
||
|
:AddEventTransition("EV_START_SCAN", "ST_INIT")
|
||
|
)
|
||
|
:AddState(FSM.NewState("ST_RUNNING_SCAN")
|
||
|
:SetOnEnter(function(context)
|
||
|
context.scanDone = false
|
||
|
if not context.searchContext then
|
||
|
private.hasLastScan = nil
|
||
|
elseif context.searchContext:IsBuyoutScan() then
|
||
|
private.hasLastScan = "buyout"
|
||
|
elseif context.searchContext:IsBidScan() then
|
||
|
private.hasLastScan = "bid"
|
||
|
else
|
||
|
error("Invalid scan type")
|
||
|
end
|
||
|
if not context.auctionScan then
|
||
|
context.auctionScan = AuctionScan.GetManager()
|
||
|
:SetResolveSellers(false)
|
||
|
:SetScript("OnQueryDone", ScanOnFilterDone)
|
||
|
end
|
||
|
if context.scanFrame then
|
||
|
context.scanFrame:GetElement("bottom.progressBar"):SetProgressIconHidden(false)
|
||
|
end
|
||
|
UpdateScanFrame(context)
|
||
|
context.searchContext:StartThread(private.FSMScanCallback, context.auctionScan)
|
||
|
if TSM.IsWowClassic() then
|
||
|
Delay.AfterTime("sniperPhaseDetect", PHASED_TIME, private.FSMPhasedCallback)
|
||
|
end
|
||
|
end)
|
||
|
:SetOnExit(function(context)
|
||
|
Delay.Cancel("sniperPhaseDetect")
|
||
|
end)
|
||
|
:AddTransition("ST_RESULTS")
|
||
|
:AddTransition("ST_WAITING_FOR_PAUSE")
|
||
|
:AddTransition("ST_FINDING_AUCTION")
|
||
|
:AddTransition("ST_INIT")
|
||
|
:AddEvent("EV_PAUSE_RESUME_CLICKED", function(context)
|
||
|
return "ST_WAITING_FOR_PAUSE"
|
||
|
end)
|
||
|
:AddEvent("EV_SCAN_COMPLETE", function(context)
|
||
|
local selection = context.scanFrame and context.scanFrame:GetElement("auctions"):GetSelection()
|
||
|
if selection and selection:IsSubRow() then
|
||
|
return "ST_FINDING_AUCTION"
|
||
|
else
|
||
|
if TSM.IsWowClassic() then
|
||
|
return "ST_RESULTS"
|
||
|
else
|
||
|
-- wait 30 seconds before rescanning to avoid spamming the server with API calls
|
||
|
context.scanDone = true
|
||
|
Delay.AfterTime("SNIPER_RESCAN_DELAY", RETAIL_RESCAN_DELAY, private.FSMRescanDelayed)
|
||
|
end
|
||
|
end
|
||
|
end)
|
||
|
:AddEventTransition("EV_RESCAN_DELAYED", "ST_RESULTS")
|
||
|
:AddEventTransition("EV_SCAN_FAILED", "ST_INIT")
|
||
|
:AddEvent("EV_PHASED", function()
|
||
|
Log.PrintUser(L["You've been phased which has caused the AH to stop working due to a bug on Blizzard's end. Please close and reopen the AH and restart Sniper."])
|
||
|
return "ST_INIT"
|
||
|
end)
|
||
|
:AddEvent("EV_AUCTION_SELECTION_CHANGED", function(context)
|
||
|
assert(context.scanFrame)
|
||
|
if context.scanFrame:GetElement("auctions"):GetSelection() then
|
||
|
if context.scanDone then
|
||
|
return "ST_RESULTS"
|
||
|
else
|
||
|
-- the user selected something, so cancel the current scan
|
||
|
context.auctionScan:Cancel()
|
||
|
end
|
||
|
end
|
||
|
end)
|
||
|
)
|
||
|
:AddState(FSM.NewState("ST_WAITING_FOR_PAUSE")
|
||
|
:SetOnEnter(function(context)
|
||
|
context.scanPausing = true
|
||
|
context.progressText = L["Scan Paused"]
|
||
|
if context.scanFrame then
|
||
|
context.scanFrame:GetElement("bottom.progressBar"):SetProgressIconHidden(true)
|
||
|
end
|
||
|
UpdateScanFrame(context)
|
||
|
end)
|
||
|
:SetOnExit(function(context)
|
||
|
context.scanPausing = false
|
||
|
context.progressText = L["Running Sniper Scan"]
|
||
|
if context.scanFrame then
|
||
|
context.scanFrame:GetElement("bottom.progressBar"):SetProgressIconHidden(false)
|
||
|
end
|
||
|
UpdateScanFrame(context)
|
||
|
end)
|
||
|
:AddEvent("EV_PAUSE_RESUME_CLICKED", function(context)
|
||
|
return "ST_RESULTS"
|
||
|
end)
|
||
|
:AddTransition("ST_RESULTS")
|
||
|
:AddTransition("ST_INIT")
|
||
|
)
|
||
|
:AddState(FSM.NewState("ST_RESULTS")
|
||
|
:SetOnEnter(function(context)
|
||
|
context.searchContext:KillThread()
|
||
|
context.findAuction = nil
|
||
|
context.findResult = nil
|
||
|
context.numFound = 0
|
||
|
context.numActioned = 0
|
||
|
context.lastBuyQuantity = 0
|
||
|
context.numConfirmed = 0
|
||
|
context.progress = 0
|
||
|
context.progressText = L["Running Sniper Scan"]
|
||
|
context.buttonsDisabled = true
|
||
|
UpdateScanFrame(context)
|
||
|
local selection = context.scanFrame and context.scanFrame:GetElement("auctions"):GetSelection()
|
||
|
if selection and selection:IsSubRow() then
|
||
|
return "ST_FINDING_AUCTION"
|
||
|
else
|
||
|
return "ST_RUNNING_SCAN"
|
||
|
end
|
||
|
end)
|
||
|
:AddTransition("ST_RUNNING_SCAN")
|
||
|
:AddTransition("ST_AUCTION_FOUND")
|
||
|
:AddTransition("ST_FINDING_AUCTION")
|
||
|
:AddTransition("ST_INIT")
|
||
|
)
|
||
|
:AddState(FSM.NewState("ST_FINDING_AUCTION")
|
||
|
:SetOnEnter(function(context)
|
||
|
assert(context.scanFrame)
|
||
|
context.findAuction = context.scanFrame:GetElement("auctions"):GetSelection()
|
||
|
assert(context.findAuction:IsSubRow())
|
||
|
context.findHash = context.findAuction:GetHashes()
|
||
|
context.progress = 0
|
||
|
context.progressText = L["Finding Selected Auction"]
|
||
|
context.buttonsDisabled = true
|
||
|
if context.scanFrame then
|
||
|
context.scanFrame:GetElement("bottom.progressBar"):SetProgressIconHidden(false)
|
||
|
end
|
||
|
UpdateScanFrame(context)
|
||
|
TSM.Shopping.SearchCommon.StartFindAuction(context.auctionScan, context.findAuction, private.FSMFindAuctionCallback, true)
|
||
|
end)
|
||
|
:SetOnExit(function(context)
|
||
|
TSM.Shopping.SearchCommon.StopFindAuction()
|
||
|
end)
|
||
|
:AddTransition("ST_RESULTS")
|
||
|
:AddTransition("ST_FINDING_AUCTION")
|
||
|
:AddTransition("ST_AUCTION_FOUND")
|
||
|
:AddTransition("ST_AUCTION_NOT_FOUND")
|
||
|
:AddTransition("ST_INIT")
|
||
|
:AddEventTransition("EV_AUCTION_FOUND", "ST_AUCTION_FOUND")
|
||
|
:AddEventTransition("EV_AUCTION_NOT_FOUND", "ST_AUCTION_NOT_FOUND")
|
||
|
:AddEvent("EV_AUCTION_SELECTION_CHANGED", function(context)
|
||
|
assert(context.scanFrame)
|
||
|
local selection = context.scanFrame and context.scanFrame:GetElement("auctions"):GetSelection()
|
||
|
if selection and selection:IsSubRow() then
|
||
|
return "ST_FINDING_AUCTION"
|
||
|
else
|
||
|
return "ST_RESULTS"
|
||
|
end
|
||
|
end)
|
||
|
:AddEvent("EV_AUCTION_ROW_REMOVED", function(context, row)
|
||
|
if not row:IsSubRow() then
|
||
|
return
|
||
|
end
|
||
|
local removingFindAuction = context.findAuction == row
|
||
|
row:GetResultRow():RemoveSubRow(row)
|
||
|
context.scanFrame:GetElement("auctions"):UpdateData(true)
|
||
|
if removingFindAuction then
|
||
|
return "ST_RESULTS"
|
||
|
end
|
||
|
end)
|
||
|
:AddEvent("EV_SCAN_FRAME_HIDDEN", function(context)
|
||
|
context.scanFrame = nil
|
||
|
context.findAuction = nil
|
||
|
return "ST_RESULTS"
|
||
|
end)
|
||
|
)
|
||
|
:AddState(FSM.NewState("ST_AUCTION_FOUND")
|
||
|
:SetOnEnter(function(context, result)
|
||
|
local selection = context.scanFrame:GetElement("auctions"):GetSelection()
|
||
|
-- update the selection in case the result rows changed
|
||
|
if context.findHash == selection:GetHashes() then
|
||
|
context.findAuction = selection
|
||
|
end
|
||
|
if TSM.IsWowClassic() then
|
||
|
context.findResult = result
|
||
|
context.numFound = #result
|
||
|
else
|
||
|
local maxCommodity = context.findAuction:IsCommodity() and context.findAuction:GetResultRow():GetMaxQuantities()
|
||
|
local numCanBuy = maxCommodity or result
|
||
|
context.findResult = numCanBuy > 0
|
||
|
context.numFound = numCanBuy
|
||
|
context.maxQuantity = maxCommodity or 1
|
||
|
end
|
||
|
assert(context.numActioned == 0 and context.numConfirmed == 0)
|
||
|
return "ST_BIDDING_BUYING"
|
||
|
end)
|
||
|
:AddTransition("ST_BIDDING_BUYING")
|
||
|
)
|
||
|
:AddState(FSM.NewState("ST_AUCTION_NOT_FOUND")
|
||
|
:SetOnEnter(function(context)
|
||
|
local _, rawLink = context.findAuction:GetLinks()
|
||
|
context.findAuction:GetResultRow():RemoveSubRow(context.findAuction)
|
||
|
Log.PrintfUser(L["Failed to find auction for %s, so removing it from the results."], rawLink)
|
||
|
return "ST_RESULTS"
|
||
|
end)
|
||
|
:AddTransition("ST_RESULTS")
|
||
|
)
|
||
|
:AddState(FSM.NewState("ST_BIDDING_BUYING")
|
||
|
:SetOnEnter(function(context, numToRemove)
|
||
|
if numToRemove then
|
||
|
-- remove the one we just bought
|
||
|
context.findAuction:DecrementQuantity(numToRemove)
|
||
|
context.scanFrame:GetElement("auctions"):UpdateData()
|
||
|
context.findAuction = context.scanFrame and context.scanFrame:GetElement("auctions"):GetSelection()
|
||
|
if context.findAuction and not context.findAuction:IsSubRow() then
|
||
|
context.findAuction = nil
|
||
|
end
|
||
|
end
|
||
|
local selection = context.scanFrame and context.scanFrame:GetElement("auctions"):GetSelection()
|
||
|
if selection and not selection:IsSubRow() then
|
||
|
selection = nil
|
||
|
end
|
||
|
local isPlayer, isHighBidder = false, false
|
||
|
if selection then
|
||
|
assert(selection:IsSubRow())
|
||
|
local ownerStr = selection and selection:GetOwnerInfo() or nil
|
||
|
isPlayer = PlayerInfo.IsPlayer(ownerStr, true, true, true)
|
||
|
isHighBidder = select(4, selection:GetBidInfo())
|
||
|
end
|
||
|
local auctionSelected = selection and context.findHash == selection:GetHashes()
|
||
|
local numCanAction = not auctionSelected and 0 or (context.numFound - context.numActioned)
|
||
|
local numConfirming = context.numActioned - context.numConfirmed
|
||
|
local progressText = nil
|
||
|
local actionFormatStr = nil
|
||
|
if context.searchContext:IsBuyoutScan() then
|
||
|
actionFormatStr = L["Buy %d / %d"]
|
||
|
elseif context.searchContext:IsBidScan() then
|
||
|
actionFormatStr = L["Bid %d / %d"]
|
||
|
else
|
||
|
error("Invalid scan type")
|
||
|
end
|
||
|
if numConfirming == 0 and numCanAction == 0 then
|
||
|
-- we're done bidding/buying and confirming this batch
|
||
|
return "ST_RESULTS"
|
||
|
elseif numConfirming == 0 then
|
||
|
-- we can still bid/buy more
|
||
|
progressText = format(actionFormatStr, context.numActioned + 1, context.numFound)
|
||
|
elseif numCanAction == 0 then
|
||
|
-- we're just confirming
|
||
|
progressText = format(L["Confirming %d / %d"], context.numConfirmed + 1, context.numFound)
|
||
|
else
|
||
|
-- we can bid/buy more while confirming
|
||
|
progressText = format(actionFormatStr.." ("..L["Confirming %d / %d"]..")", context.numActioned + 1, context.numFound, context.numConfirmed + 1, context.numFound)
|
||
|
end
|
||
|
context.progress = context.numConfirmed / context.numFound
|
||
|
context.progressText = L["Scan Paused"].." - "..progressText
|
||
|
if numCanAction == 0 or isPlayer or (not TSM.IsWowClassic() and numConfirming > 0) then
|
||
|
context.buttonsDisabled = true
|
||
|
else
|
||
|
if context.searchContext:IsBuyoutScan() then
|
||
|
context.buttonsDisabled = not AuctionScan.CanBuyout(selection, context.auctionScan)
|
||
|
elseif context.searchContext:IsBidScan() then
|
||
|
context.buttonsDisabled = not AuctionScan.CanBid(selection)
|
||
|
else
|
||
|
error("Invalid scan type")
|
||
|
end
|
||
|
end
|
||
|
if context.scanFrame then
|
||
|
context.scanFrame:GetElement("bottom.progressBar")
|
||
|
:SetProgressIconHidden(context.numConfirmed == context.numActioned)
|
||
|
context.scanFrame:GetElement("bottom.actionBtn")
|
||
|
:SetDisabled(isPlayer or (isHighBidder and context.searchContext:IsBidScan()))
|
||
|
:Draw()
|
||
|
end
|
||
|
UpdateScanFrame(context)
|
||
|
end)
|
||
|
:AddTransition("ST_BID_BUY_CONFIRMATION")
|
||
|
:AddTransition("ST_BIDDING_BUYING")
|
||
|
:AddTransition("ST_PLACING_BID_BUY")
|
||
|
:AddTransition("ST_CONFIRMING_BID_BUY")
|
||
|
:AddTransition("ST_RESULTS")
|
||
|
:AddTransition("ST_INIT")
|
||
|
:AddEvent("EV_PAUSE_RESUME_CLICKED", function(context)
|
||
|
context.scanFrame:GetElement("auctions"):SetSelection(nil)
|
||
|
return "ST_RESULTS"
|
||
|
end)
|
||
|
:AddEventTransition("EV_AUCTION_SELECTION_CHANGED", "ST_RESULTS")
|
||
|
:AddEventTransition("EV_ACTION_CLICKED", "ST_BID_BUY_CONFIRMATION")
|
||
|
:AddEvent("EV_CONFIRMED", function(context, isBuy, quantity)
|
||
|
assert(isBuy == context.searchContext:IsBuyoutScan())
|
||
|
return "ST_PLACING_BID_BUY", quantity
|
||
|
end)
|
||
|
:AddEvent("EV_MSG", function(context, msg)
|
||
|
local _, rawLink = context.findAuction:GetLinks()
|
||
|
if msg == LE_GAME_ERR_AUCTION_HIGHER_BID or msg == LE_GAME_ERR_ITEM_NOT_FOUND or msg == LE_GAME_ERR_AUCTION_BID_OWN or msg == LE_GAME_ERR_NOT_ENOUGH_MONEY or msg == LE_GAME_ERR_ITEM_MAX_COUNT then
|
||
|
-- failed to bid/buy an auction
|
||
|
return "ST_CONFIRMING_BID_BUY", false
|
||
|
elseif context.searchContext:IsBidScan() and msg == ERR_AUCTION_BID_PLACED then
|
||
|
-- bid on an auction
|
||
|
return "ST_CONFIRMING_BID_BUY", true
|
||
|
elseif context.searchContext:IsBuyoutScan() and msg == format(ERR_AUCTION_WON_S, ItemInfo.GetName(rawLink)) then
|
||
|
-- bought an auction
|
||
|
return "ST_CONFIRMING_BID_BUY", true
|
||
|
end
|
||
|
end)
|
||
|
:AddEvent("EV_BUYOUT_SUCCESS", function(context)
|
||
|
return "ST_CONFIRMING_BID_BUY", true
|
||
|
end)
|
||
|
:AddEvent("EV_AUCTION_ID_UPDATE", function(context, oldAuctionId, newAuctionId, newResultInfo)
|
||
|
if not context.findAuction or select(2, context.findAuction:GetListingInfo()) ~= oldAuctionId then
|
||
|
return
|
||
|
end
|
||
|
context.findAuction:UpdateResultInfo(newAuctionId, newResultInfo)
|
||
|
context.findHash = context.findAuction:GetHashes()
|
||
|
end)
|
||
|
)
|
||
|
:AddState(FSM.NewState("ST_BID_BUY_CONFIRMATION")
|
||
|
:SetOnEnter(function(context)
|
||
|
local selection = context.scanFrame:GetElement("auctions"):GetSelection()
|
||
|
local index = TSM.IsWowClassic() and context.findResult[#context.findResult] or nil
|
||
|
if TSM.UI.AuctionUI.BuyUtil.ShowConfirmation(context.scanFrame, selection, context.searchContext:IsBuyoutScan(), context.numConfirmed + 1, context.numFound, context.maxQuantity, private.FSMConfirmationCallback, context.auctionScan, index, true, context.searchContext:GetMarketValueFunc()) then
|
||
|
return "ST_BIDDING_BUYING"
|
||
|
else
|
||
|
local quantity = selection:GetQuantities()
|
||
|
return "ST_PLACING_BID_BUY", quantity
|
||
|
end
|
||
|
end)
|
||
|
:AddTransition("ST_PLACING_BID_BUY")
|
||
|
:AddTransition("ST_BIDDING_BUYING")
|
||
|
)
|
||
|
:AddState(FSM.NewState("ST_PLACING_BID_BUY")
|
||
|
:SetOnEnter(function(context, quantity)
|
||
|
local index = TSM.IsWowClassic() and tremove(context.findResult, #context.findResult) or nil
|
||
|
assert(not TSM.IsWowClassic() or index)
|
||
|
local bidBuyout = nil
|
||
|
if context.searchContext:IsBuyoutScan() then
|
||
|
bidBuyout = context.findAuction:GetBuyouts()
|
||
|
elseif context.searchContext:IsBidScan() then
|
||
|
bidBuyout = context.findAuction:GetRequiredBid()
|
||
|
else
|
||
|
error("Invalid scan type")
|
||
|
end
|
||
|
local result = context.auctionScan:PlaceBidOrBuyout(index, bidBuyout, context.findAuction, quantity)
|
||
|
if result then
|
||
|
MailTracking.RecordAuctionBuyout(ItemString.GetBaseFast(context.findAuction:GetItemString()), quantity)
|
||
|
context.numActioned = context.numActioned + (TSM.IsWowClassic() and 1 or quantity)
|
||
|
context.lastBuyQuantity = quantity
|
||
|
else
|
||
|
local _, rawLink = context.findAuction:GetLinks()
|
||
|
if context.searchContext:IsBuyoutScan() then
|
||
|
Log.PrintfUser(L["Failed to buy auction of %s (x%s) for %s."], rawLink, quantity, Money.ToString(bidBuyout, nil, "OPT_83_NO_COPPER"))
|
||
|
elseif context.searchContext:IsBidScan() then
|
||
|
Log.PrintfUser(L["Failed to bid on auction of %s (x%s) for %s."], rawLink, quantity, Money.ToString(bidBuyout, nil, "OPT_83_NO_COPPER"))
|
||
|
else
|
||
|
error("Invalid scan type")
|
||
|
end
|
||
|
end
|
||
|
return "ST_BIDDING_BUYING"
|
||
|
end)
|
||
|
:AddTransition("ST_BIDDING_BUYING")
|
||
|
)
|
||
|
:AddState(FSM.NewState("ST_CONFIRMING_BID_BUY")
|
||
|
:SetOnEnter(function(context, success)
|
||
|
if not success then
|
||
|
local _, rawLink = context.findAuction:GetLinks()
|
||
|
local quantity = context.findAuction:GetQuantities()
|
||
|
local bidBuyout = nil
|
||
|
if context.searchContext:IsBuyoutScan() then
|
||
|
bidBuyout = context.findAuction:GetBuyouts()
|
||
|
elseif context.searchContext:IsBidScan() then
|
||
|
bidBuyout = context.findAuction:GetRequiredBid()
|
||
|
else
|
||
|
error("Invalid scan type")
|
||
|
end
|
||
|
if context.searchContext:IsBuyoutScan() then
|
||
|
Log.PrintfUser(L["Failed to buy auction of %s (x%s) for %s."], rawLink, quantity, Money.ToString(bidBuyout, nil, "OPT_83_NO_COPPER"))
|
||
|
elseif context.searchContext:IsBidScan() then
|
||
|
Log.PrintfUser(L["Failed to bid on auction of %s (x%s) for %s."], rawLink, quantity, Money.ToString(bidBuyout, nil, "OPT_83_NO_COPPER"))
|
||
|
else
|
||
|
error("Invalid scan type")
|
||
|
end
|
||
|
end
|
||
|
context.numConfirmed = context.numConfirmed + (TSM.IsWowClassic() and 1 or context.lastBuyQuantity)
|
||
|
context.findAuction = context.scanFrame and context.scanFrame:GetElement("auctions"):GetSelection()
|
||
|
return "ST_BIDDING_BUYING", context.lastBuyQuantity
|
||
|
end)
|
||
|
:AddTransition("ST_BIDDING_BUYING")
|
||
|
)
|
||
|
:AddDefaultEvent("EV_SCAN_FRAME_SHOWN", function(context, scanFrame)
|
||
|
context.scanFrame = scanFrame
|
||
|
UpdateScanFrame(context)
|
||
|
end)
|
||
|
:AddDefaultEvent("EV_SCAN_FRAME_HIDDEN", function(context)
|
||
|
context.scanFrame = nil
|
||
|
context.findAuction = nil
|
||
|
end)
|
||
|
:AddDefaultEventTransition("EV_AUCTION_HOUSE_CLOSED", "ST_INIT")
|
||
|
:AddDefaultEventTransition("EV_STOP_CLICKED", "ST_INIT")
|
||
|
:AddDefaultEvent("EV_AUCTION_ROW_REMOVED", function(context, row)
|
||
|
if not row:IsSubRow() then
|
||
|
return
|
||
|
end
|
||
|
row:GetResultRow():RemoveSubRow(row)
|
||
|
context.scanFrame:GetElement("auctions"):UpdateData(true)
|
||
|
end)
|
||
|
:Init("ST_INIT", fsmContext)
|
||
|
end
|
||
|
|
||
|
function private.FSMMessageEventHandler(_, msg)
|
||
|
private.fsm:SetLoggingEnabled(false)
|
||
|
private.fsm:ProcessEvent("EV_MSG", msg)
|
||
|
private.fsm:SetLoggingEnabled(true)
|
||
|
end
|
||
|
|
||
|
function private.FSMBuyoutSuccess()
|
||
|
private.fsm:ProcessEvent("EV_BUYOUT_SUCCESS")
|
||
|
end
|
||
|
|
||
|
function private.FSMRescanDelayed()
|
||
|
private.fsm:ProcessEvent("EV_RESCAN_DELAYED")
|
||
|
end
|
||
|
|
||
|
function private.FSMScanCallback(success)
|
||
|
if success then
|
||
|
private.fsm:ProcessEvent("EV_SCAN_COMPLETE")
|
||
|
else
|
||
|
private.fsm:ProcessEvent("EV_SCAN_FAILED")
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function private.FSMPhasedCallback()
|
||
|
private.fsm:ProcessEvent("EV_PHASED")
|
||
|
end
|
||
|
|
||
|
function private.FSMFindAuctionCallback(result)
|
||
|
if result then
|
||
|
private.fsm:ProcessEvent("EV_AUCTION_FOUND", result)
|
||
|
else
|
||
|
private.fsm:ProcessEvent("EV_AUCTION_NOT_FOUND")
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function private.FSMConfirmationCallback(isBuy, quantity)
|
||
|
private.fsm:ProcessEvent("EV_CONFIRMED", isBuy, quantity)
|
||
|
end
|