Advertisement
Guest User

Auc-Util-SearchUI/SearchRealTime.lua

a guest
Jul 20th, 2016
1,359
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 18.42 KB | None | 0 0
  1. --[[
  2.     Auctioneer - Search UI - Realtime module
  3.     Version: 5.21f.5579 (SanctimoniousSwamprat)
  4.     Revision: $Id: SearchRealTime.lua 5515 2014-10-31 12:07:47Z brykrys $
  5.     URL: http://auctioneeraddon.com/
  6.  
  7.     This Auctioneer module allows the user to search the current Browse tab
  8.     results in real time, without requiring scans or an up-to-date snapshot.
  9.     It also provides top- and bottom-scanning capabilities (i.e. first and
  10.     last AH pages) to find deals on items about to expire, or recently added
  11.     to the AH.
  12.  
  13.     License:
  14.         This program is free software; you can redistribute it and/or
  15.         modify it under the terms of the GNU General Public License
  16.         as published by the Free Software Foundation; either version 2
  17.         of the License, or (at your option) any later version.
  18.  
  19.         This program is distributed in the hope that it will be useful,
  20.         but WITHOUT ANY WARRANTY; without even the implied warranty of
  21.         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  22.         GNU General Public License for more details.
  23.  
  24.         You should have received a copy of the GNU General Public License
  25.         along with this program(see GPL.txt); if not, write to the Free Software
  26.         Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  27.  
  28.     Note:
  29.         This AddOn's source code is specifically designed to work with
  30.         World of Warcraft's interpreted AddOn system.
  31.         You have an implicit license to use this AddOn with these facilities
  32.         since that is its designated purpose as per:
  33.         http://www.fsf.org/licensing/licenses/gpl-faq.html#InterpreterIncompat
  34. --]]
  35.  
  36. if not AucSearchUI then return end
  37.  
  38. local AddOnName = ...
  39. local embedpath
  40. AddOnName = AddOnName:lower()
  41. if AddOnName == "auc-util-searchui" then
  42.     embedpath = "Interface\\AddOns\\"
  43. elseif AddOnName == "auc-advanced" then
  44.     embedpath = "Interface\\AddOns\\Auc-Advanced\\Modules\\"
  45. else
  46.     return -- unknown location, cannot continue
  47. end
  48.  
  49. local lib, parent, private = AucSearchUI.NewSearcher("RealTime")
  50. if not lib then return end
  51. local AucPrint,decode,_,_,replicate,empty,_,_,_,debugPrint,fill = AucAdvanced.GetModuleLocals()
  52. local get,set,default,Const = AucSearchUI.GetSearchLocals()
  53. lib.tabname = "RealTime"
  54.  
  55. private.IsScanning = false
  56. private.ItemTable = {}
  57. private.searchertable = {}
  58. private.buttontable = {}
  59. private.offset = 0
  60. private.IsRefresh = false
  61. private.pageCount = -1 -- signal value, to indicate not previously tested
  62. private.scannedPage = -1
  63.  
  64. default("realtime.always", true)
  65. default("realtime.reload.enable", true)
  66. default("realtime.reload.interval", 20)
  67. default("realtime.reload.topscan", false)
  68. default("realtime.reload.manpause", 60)
  69. default("realtime.maxprice", 10000000)
  70. default("realtime.alert.chat", true)
  71. default("realtime.alert.showwindow", true)
  72. default("realtime.alert.sound", "DoorBell")
  73. default("realtime.skipresults", false)
  74.  
  75. function private.MakeGuiConfig(gui)
  76.     private.MakeGuiConfig = nil
  77.     private.SearchUIgui = gui
  78.     local id = gui:AddTab(lib.tabname, "Options")
  79.     gui:MakeScrollable(id)
  80.  
  81.     gui:AddControl(id, "Header",      0,   "RealTime Search Options")
  82.  
  83.     gui:AddControl(id, "Subhead",       0,    "Scan Settings")
  84.     gui:AddControl(id, "Checkbox",      0, 1, "realtime.always", "Search while browsing")
  85.     gui:AddTip(id, "Enables searching for results when browsing or scanning")
  86.     gui:AddControl(id, "Checkbox",      0, 1, "realtime.reload.enable", "Enable automatic last page refreshing")
  87.     gui:AddTip(id, "Refreshes the last page, looking for new bargains. (Bottomscanning)")
  88.     gui:AddControl(id, "Slider",        0, 2, "realtime.reload.interval", 6, 60, 1, "Reload interval: %s seconds")
  89.     gui:AddControl(id, "Slider",        0, 2, "realtime.reload.manpause", 10, 120, 1, "Pause after a manual search: %s seconds")
  90.     gui:AddControl(id, "Checkbox",      0, 2, "realtime.reload.topscan", "Refresh first page as well")
  91.     gui:AddTip(id, "Refreshes the first page, looking for bids about to expire")
  92.  
  93.     gui:AddControl(id, "Subhead",       0,    "Alert Settings")
  94.     gui:AddControl(id, "Checkbox",      0, 1, "realtime.alert.chat", "Show alert in chat window")
  95.     gui:AddControl(id, "Checkbox",      0, 1, "realtime.alert.showwindow", "Show SearchUI window")
  96.     gui:AddTip(id, "When a bargain is found, opens the SearchUI window to facilitate buying the bargain")
  97.     gui:AddControl(id, "Selectbox",     0, 1, {
  98.         {"none", "None (do not play a sound)"},
  99.         {"LEVELUP", "Level Up"},
  100.         {"AuctionWindowOpen", "AuctionHouse Open"},
  101.         {"AuctionWindowClose", "AuctionHouse Close"},
  102.         {"RaidWarning", "Raid Warning"},
  103.         {"DoorBell", "DoorBell (BottomScan-style)"},
  104.     }, "realtime.alert.sound")
  105.     gui:AddTip(id, "The selected sound will play whenever a bargain is found")
  106.     gui:AddControl(id, "Subhead",       0,    "Power-user setting: One-Click Buying")
  107.     gui:AddControl(id, "Checkbox",      0, 1, "realtime.skipresults", "Skip results and go straight to purchase confirmation !Power-user setting!")
  108.     gui:AddTip(id, "One-Click Buying: RTS will queue the purchase instead of listing the item in SearchUI")
  109.     gui:AddControl(id, "Subhead",          0,    "Searchers to use")
  110.     for name, searcher in pairs(AucSearchUI.Searchers) do
  111.         if searcher and searcher.Search then
  112.             gui:AddControl(id, "Checkbox", 0, 1, "realtime.use."..name, name)
  113.             gui:AddTip(id, "Include "..name.." when searching realtime")
  114.         end
  115.     end
  116. end
  117. function lib:MakeGuiConfig(gui)
  118.     if private.MakeGuiConfig then private.MakeGuiConfig(gui) end
  119. end
  120.  
  121. lib.Processor = {
  122.     config = function(sub, setting, value)
  123.         -- perform certain resets on config change
  124.  
  125.         -- Changes that affect RTSButtons status
  126.         if sub == "changed" and setting == "realtime.reload.enable" then
  127.             for _, button in ipairs(private.buttontable) do
  128.                 button:SetState()
  129.             end
  130.         end
  131.     end,
  132.  
  133.     auctionopen = function()
  134.         lib.SetTimerInterval(get("realtime.reload.interval"))
  135.     end,
  136. }
  137.  
  138. --lib.RefreshPage()
  139. --role: refreshes the page based on settings, and updates private.lastPage, private.interval, and private.manualSearchPause
  140. function lib.RefreshPage()
  141.     --Check to see if the AH is open for business
  142.     if not (AuctionFrame and AuctionFrame:IsVisible()) then
  143.         lib.SetTimerInterval(120) -- basically do nothing - we wait for auctionopen event
  144.         return
  145.     end
  146.  
  147.     --Check to see if AucAdv is already scanning
  148.     if AucAdvanced.Scan.IsScanning() or AucAdvanced.Scan.IsPaused() then
  149.         lib.SetTimerInterval(get("realtime.reload.manpause"))
  150.         private.findLast = nil
  151.         return
  152.     end
  153.  
  154.     --Check to see if we can send a query
  155.     if not (CanSendAuctionQuery()) then
  156.         lib.SetTimerInterval(1) --try again in one second
  157.         return
  158.     end
  159.  
  160.     lib.SetTimerInterval(get("realtime.reload.interval"))
  161.  
  162.     --Get the current number of auctions and pages
  163.     local count, totalCount = GetNumAuctionItems("list")
  164.     local totalPages = floor((totalCount-1)/NUM_AUCTION_ITEMS_PER_PAGE)
  165.     if (totalPages < 0) then
  166.         totalPages = 0
  167.     end
  168.  
  169.     -- Detect change of number of pages, and go into findLast mode to find the (new) last page quickly
  170.     if totalPages ~= private.pageCount then
  171.         private.pageCount = totalPages
  172.         private.findLast = true
  173.     end
  174.  
  175.     local page = private.pageCount
  176.  
  177.     if private.findLast then
  178.         private.topScan = nil
  179.     else
  180.         if get("realtime.reload.topscan") then
  181.             private.topScan = not private.topScan -- flip the variable, so we alternate first and last pages
  182.             if private.topScan then
  183.                 page = 0
  184.             end
  185.         else
  186.             private.topScan = nil -- make sure we don't topScan if we don't want to
  187.         end
  188.         if page > 0 then
  189.             --every 5 pages, go back one just to doublecheck that nothing got by us
  190.             private.offset = (private.offset + 1) % 5
  191.             if private.offset == 0 then
  192.                 page = page - 1
  193.                 private.findLast = true
  194.             end
  195.         end
  196.     end
  197.  
  198.     AuctionFrameBrowse.page = page
  199.     private.scannedPage = page
  200.     SortAuctionClearSort("list")
  201.     if private.topScan then
  202.         -- When top scanning, sort auctions by time left
  203.         SortAuctionSetSort("list", "duration")
  204.     end
  205.     private.IsRefresh = true
  206.     AucAdvanced.Scan.SetAuctioneerQuery()
  207.     QueryAuctionItems("", "", "", nil, nil, nil, page, nil, nil)
  208.     lib.SignalRTSButton()
  209. end
  210.  
  211. -- private.OnUpdate()
  212. -- checks whether it's time to refresh the page
  213. -- called from the OnUpdate script of the currently visible RTSButton
  214. function private.OnUpdate(me, elapsed)
  215.     -- Bail out immediately if we're not scanning
  216.     if not private.IsScanning then
  217.         return
  218.     end
  219.  
  220.     -- We use GetTime() to check how long since last update, instead of using elapsed
  221.     -- Workaround for the possibility of OnUpdate getting called multiple times per frame
  222.     local now = GetTime()
  223.  
  224.     --Check whether enough time has elapsed to do anything
  225.     if now - private.lasttime < private.interval then
  226.         return
  227.     end
  228.     private.lasttime = now
  229.  
  230.     if get("realtime.reload.enable") then
  231.         -- if we've gotten to this point, it's time to refresh the page
  232.         lib.RefreshPage()
  233.     else
  234.         -- at this point we are waiting to see if the user enables scanning
  235.         -- keep checking at short intervals
  236.         private.interval = 2
  237.     end
  238. end
  239.  
  240. function lib.SetTimerInterval(interval)
  241.     private.interval = interval
  242.     private.lasttime = GetTime()
  243. end
  244. lib.SetTimerInterval(2) -- initialize
  245.  
  246. --lib.FinishedPage()
  247. --called by AucAdv via SearchUI main when a page is done
  248. --role: starts the page scan
  249. function lib.FinishedPage()
  250.     --if we're not scanning, we don't need to do anything
  251.     --if we don't have searching while browsing on, then don't do anything if we're not actively refreshing
  252.     local always = get("realtime.always")
  253.     if not private.IsRefresh then
  254.         lib.SetTimerInterval(get("realtime.reload.manpause"))
  255.         private.findLast = nil
  256.     end
  257.     if (not private.IsScanning)
  258.             or (not always and (not private.IsRefresh or AucAdvanced.Scan.IsScanning())) then
  259.         lib.SetTimerInterval(get("realtime.reload.manpause"))
  260.         private.IsRefresh = false
  261.         private.findLast = nil
  262.         return
  263.     end
  264.     --scan the current page
  265.     lib.ScanPage()
  266. end
  267.  
  268. --[[
  269.     lib.ScanPage()
  270.     Called: from lib.FinishedPage, when AA is done with a page
  271.     Function: Scans current AH page for bargains
  272.     Note: will return if current page has > NUM_AUCTION_ITEMS_PER_PAGE auctions on it
  273.     (NUM_AUCTION_ITEMS_PER_PAGE defined as 50 in Blizzard_AuctionUI.lua)
  274. ]]
  275. function lib.ScanPage()
  276.     local isRefresh = private.IsRefresh
  277.     private.IsRefresh = false
  278.     if not private.IsScanning then return end
  279.     local batch, totalCount = GetNumAuctionItems("list")
  280.     if batch > NUM_AUCTION_ITEMS_PER_PAGE then
  281.         -- we don't want to freeze the computer by trying to process a getall, so return
  282.         return
  283.     end
  284.     if batch == 0 then -- found an empty page
  285.         if isRefresh and totalCount > 0 then
  286.             lib.SetTimerInterval(.5) -- immediate refresh to find the last page
  287.             private.findLast = true
  288.         end
  289.         return
  290.     end
  291.  
  292.     --this is a new page, so no alert sound has been played for it yet
  293.     private.playedsound = false
  294.  
  295.     if isRefresh and private.findLast then
  296.         -- in findLast mode - hunting for the last page
  297.         local topPage = floor((totalCount-1)/NUM_AUCTION_ITEMS_PER_PAGE)
  298.         if topPage < 0 then
  299.             topPage = 0
  300.         end
  301.         if private.scannedPage == topPage then
  302.             private.findLast = nil -- found the last page
  303.         else
  304.             lib.SetTimerInterval(3) -- short interval to find the last page quickly
  305.         end
  306.     end
  307.  
  308.     --Put all the searchers that are activated into a local table, so that the get()s are only called every page, not every auction
  309.     for name, searcher in pairs(AucSearchUI.Searchers) do
  310.         if get("realtime.use."..name) then
  311.             tinsert(private.searchertable, searcher)
  312.         end
  313.     end
  314.     local skipresults = get("realtime.skipresults")
  315.     for i = 1, batch do
  316.         if AucAdvanced.Scan.GetAuctionItem("list", i, private.ItemTable) then
  317.             for i, searcher in pairs(private.searchertable) do
  318.                 if AucSearchUI.SearchItem(searcher.name, private.ItemTable, false, skipresults) then
  319.                     private.alert(private.ItemTable[Const.LINK], private.ItemTable["cost"], private.ItemTable["reason"])
  320.                     if skipresults then
  321.                         AucAdvanced.Buy.QueueBuy(private.ItemTable[Const.LINK],
  322.                             private.ItemTable[Const.SELLER],
  323.                             private.ItemTable[Const.COUNT],
  324.                             private.ItemTable[Const.MINBID],
  325.                             private.ItemTable[Const.BUYOUT],
  326.                             private.ItemTable["cost"],
  327.                             AucSearchUI.Private.cropreason(private.ItemTable["reason"]),
  328.                             true -- do not trigger a search if CoreBuy is unable to find this auction
  329.                             )
  330.                     else
  331.                         AucSearchUI.Private.repaintSheet()
  332.                     end
  333.                 end
  334.             end
  335.         end
  336.         wipe(private.ItemTable)
  337.     end
  338.     wipe(private.searchertable)
  339. end
  340.  
  341. --private.alert()
  342. --alerts the user that a deal has been found,
  343. --both by opening the searchUI panel and playing a sound
  344. --(subject to options)
  345. function private.alert(link, cost, reason)
  346.     if get("realtime.alert.chat") then
  347.         AucPrint("SearchUI: "..reason..": Found "..link.." for "..AucAdvanced.Coins(cost, true))
  348.     end
  349.     if get("realtime.alert.showwindow") and (not get("realtime.skipresults")) then
  350.         AucSearchUI.Show()
  351.         if private.SearchUIgui then
  352.             if private.SearchUIgui.tabs.active then
  353.                 private.SearchUIgui:ContractFrame(private.SearchUIgui.tabs.active)
  354.             end
  355.             private.SearchUIgui:ClearFocus()
  356.         end
  357.     end
  358.     local SoundPath = get("realtime.alert.sound")
  359.     if SoundPath and (SoundPath ~= "none") and not private.playedsound then
  360.         private.playedsound = true
  361.         if SoundPath == "DoorBell" then
  362.             PlaySoundFile(embedpath.."Auc-Util-SearchUI\\DoorBell.mp3")
  363.         else
  364.             PlaySound(SoundPath)
  365.         end
  366.     end
  367. end
  368.  
  369. --[[
  370.     lib.SetScanning(boolean)
  371.     Turn RTS bottom/top scanning on or off
  372.     Note: active RTS scanning can only occur if one of the RTS buttons is visible
  373. --]]
  374. function lib.SetScanning(active)
  375.     if active then
  376.         if not private.IsScanning then
  377.             private.IsScanning = true
  378.             lib.SetTimerInterval(.5) -- just started scanning so do first scan immediately
  379.         end
  380.     else
  381.         private.IsScanning = false
  382.     end
  383.     for _, button in ipairs(private.buttontable) do
  384.         button:SetState()
  385.     end
  386. end
  387.  
  388. -- Scripts for RTSButtons
  389. local function OnClick(self, button)
  390.     if button == "LeftButton" then
  391.         lib.SetScanning(not private.IsScanning)
  392.     elseif button == "RightButton" then
  393.         if self.hasRight then
  394.             AucSearchUI.Toggle()
  395.         end
  396.     end
  397. end
  398. local function OnEnter(self)
  399.     local text
  400.     if private.IsScanning then
  401.         text = "Click to stop RealTime Search"
  402.     else
  403.         text = "Click to start RealTime Search"
  404.     end
  405.     if self.hasRight then
  406.         text = text .. "\nRight-Click to open SearchUI"
  407.     end
  408.     GameTooltip:SetOwner(self, "ANCHOR_TOPRIGHT")
  409.     GameTooltip:SetText(text)
  410. end
  411. local function OnLeave(self)
  412.     GameTooltip:Hide()
  413. end
  414. local function SetState(self)
  415.     if private.IsScanning then
  416.         self.tex:SetTexCoord(0.5, 1, 0, 1)
  417.         if not self.isSpiking and get("realtime.reload.enable") then
  418.             self.pulse:Play()
  419.         else
  420.             self.pulse:Stop()
  421.         end
  422.     else
  423.         self.tex:SetTexCoord(0, .5, 0, 1)
  424.         self.pulse:Stop()
  425.         self.spike:Stop()
  426.     end
  427. end
  428. local function OnSpikeFinished(self)
  429.     -- pulse is temporarily disabled while spiking; restart pulse animation when spike ends
  430.     self.button.isSpiking = nil
  431.     self.button:SetState()
  432. end
  433.  
  434. --[[
  435.     lib.CreateRTSButton(parent, norightclick)
  436.     Create a new RTS scan toggle button to add to a GUI
  437.     Caller will need to anchor it using SetPoint
  438.     parent (frame) - parent to use when creating the new button
  439.     norightclick (boolean) - if true, disables right-clicking the button to open the SearchUI panel
  440.     Note: active RTS scanning can only occur if one of these buttons is visible
  441. --]]
  442. function lib.CreateRTSButton(parent, norightclick)
  443.     local right = not norightclick -- invert and force type to boolean
  444.  
  445.     local button = CreateFrame("Button", nil, parent, "OptionsButtonTemplate")
  446.     button:SetWidth(22)
  447.     button:SetHeight(18)
  448.     if right then
  449.         button:RegisterForClicks("LeftButtonUp", "RightButtonUp")
  450.     else
  451.         button:RegisterForClicks("LeftButtonUp")
  452.     end
  453.     button.hasRight = right
  454.     button:SetScript("OnClick", OnClick)
  455.     button:SetScript("OnEnter", OnEnter)
  456.     button:SetScript("OnLeave", OnLeave)
  457.     button:SetScript("OnUpdate", private.OnUpdate)
  458.     button.tex = button:CreateTexture(nil, "OVERLAY")
  459.     button.tex:SetPoint("TOPLEFT", button, "TOPLEFT", 4, -2)
  460.     button.tex:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", -4, 2)
  461.     button.tex:SetTexture(embedpath.."Auc-Util-SearchUI\\Textures\\SearchButton")
  462.     button.tex:SetVertexColor(1, 0.9, 0.1)
  463.  
  464.     -- continuous animation to indicate that RTS is active
  465.     local pulse = button.tex:CreateAnimationGroup()
  466.     pulse:SetLooping("REPEAT")
  467.     local pulse1 = pulse:CreateAnimation("Alpha")
  468.     pulse1:SetStartDelay(1)
  469.     --pulse1:SetChange(-0.8) -- SetChange removed in 7.x (Legion)
  470.     pulse1:SetDuration(.5)
  471.     pulse1:SetOrder(1)
  472.     local pulse2 = pulse:CreateAnimation("Alpha")
  473.     --pulse2:SetChange(0.8) -- SetChange removed in 7.x (Legion)
  474.     pulse2:SetDuration(.5)
  475.     pulse2:SetOrder(2)
  476.     button.pulse = pulse
  477.  
  478.     -- single animation to indicate that RTS has refreshed the page
  479.     local spike = button.tex:CreateAnimationGroup()
  480.     local spike1 = spike:CreateAnimation("Scale")
  481.     spike1:SetOrigin("CENTER", 0, 0)
  482.     spike1:SetScale(.1, .1)
  483.     spike1:SetDuration(.1)
  484.     spike1:SetOrder(1)
  485.     local spike2 = spike:CreateAnimation("Scale")
  486.     spike2:SetOrigin("CENTER", 0, 0)
  487.     spike2:SetScale(10, 10)
  488.     spike2:SetDuration(1.5)
  489.     spike2:SetSmoothing("OUT") -- animation will be initially fast, then slowing
  490.     spike2:SetOrder(2)
  491.     spike.button = button -- save so we don't have to drill up through GetParent()'s
  492.     spike:SetScript("OnFinished", OnSpikeFinished)
  493.     button.spike = spike
  494.  
  495.     button.SetState = SetState
  496.     button:SetState()
  497.  
  498.     tinsert(private.buttontable, button)
  499.  
  500.     return button
  501. end
  502.  
  503. --[[
  504.     lib.SignalRTSButton()
  505.     Cause the RTS button to display a one-off animation, to indicate that the scan page has changed
  506.     Only applies to the current visible button
  507. --]]
  508. function lib.SignalRTSButton()
  509.     for _, button in ipairs(private.buttontable) do
  510.         if button:IsVisible() then
  511.             -- internally this is called a Spike
  512.             button.isSpiking = true
  513.             button.pulse:Stop() -- don't pulse while spiking
  514.             button.spike:Stop() -- reset if it's already playing
  515.             button.spike:Play()
  516.             return
  517.         end
  518.     end
  519. end
  520.  
  521. --[[
  522.     lib.HookAH()
  523.     Called from SearchMain when the AH opens for the first time
  524.     function: to create the control button on the AH, to the right of the regular ScanButtons
  525. ]]
  526. function lib.HookAH()
  527.     if private.HookAH then private.HookAH() end
  528. end
  529. function private.HookAH()
  530.     private.HookAH = nil -- prevent calling more than once
  531.     local BrowseRTSButton = lib.CreateRTSButton(AuctionFrameBrowse)
  532.     BrowseRTSButton:SetPoint("TOPRIGHT", AuctionFrameBrowse, "TOPLEFT", 310, -15)
  533. end
  534.  
  535. AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.21f/Auc-Util-SearchUI/SearchRealTime.lua $", "$Rev: 5515 $")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement