--[[ Auctioneer Addon for World of Warcraft(tm). Version: 3.9.0.1000 (Kangaroo) Revision: $Id: AucCore.lua 997 2006-09-12 02:07:48Z mentalpower $ Auctioneer core functions and variables. Functions central to the major operation of Auctioneer. License: This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program(see GPL.txt); if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ]] --Local function prototypes local getItemData, getItemDataByID, storeMedianList, loadMedianList, getAuctionPriceItem, saveAuctionPriceItem, getAuctionBuyoutHistory, getAuctionPrices, getItemSignature, getItemCategory, isPlayerMade, getInfo, getSnapshot, getSnapshotFromData, getSnapshotInfo, getSnapshotInfoFromData, saveSnapshot, saveSnapshotInfo, addonLoaded, hookAuctionHouse, lockAndLoad; --Local variables -- Counter to count the total number of auctions scanned local totalAuctionsScannedCount = 0; local newAuctionsCount = 0; local oldAuctionsCount = 0; local defunctAuctionsCount = 0; -- Auction scan start time counter local auctionScanStart = 0; -- Temp table that is copied into AHSnapshotItemPrices only when a scan fully completes local snapshotItemPrices = {}; --Local constants local maxAllowedFormatInt = 2000000000; -- numbers much greater than this overflow when using format("%d") --MAX_ALLOWED_FORMAT_INT -- Auction time constants --Auctioneer.Core.Constants.TimeLeft. local timeLeft = { Short = 1; --TIME_LEFT_SHORT Medium = 2; --TIME_LEFT_MEDIUM Long = 3; --TIME_LEFT_LONG VeryLong = 4; --TIME_LEFT_VERY_LONG Seconds = { --TIME_LEFT_SECONDS [0] = 0, -- Could expire any second... the current bid is relatively accurate. [1] = 1800, -- If it disappears within 30 mins of last seing it, it was BO'd [2] = 7200, -- Ditto but for 2 hours. [3] = 28800, -- 8 hours. [4] = 86400, -- 24 hours. } } -- Item quality constants --Auctioneer.Core.Constants.Quality local quality = { Legendary = 5; --QUALITY_LEGENDARY Epic = 4; --QUALITY_EPIC Rare = 3; --QUALITY_RARE Uncommon = 2; --QUALITY_UNCOMMON Common = 1; --QUALITY_COMMON Poor = 0; --QUALITY_POOR } -- The maximum number of elements we store in our buyout prices history table local maxBuyoutHistorySize = 35; -- Min median buyout price for an item to show up in the list of items below median local minProfitMargin = 5000; --MIN_PROFIT_MARGIN -- Min median buyout price for an item to show up in the list of items below median local defaultCompeteLess = 5; --DEFAULT_COMPETE_LESS -- Min times an item must be seen before it can show up in the list of items below median local minBuyoutSeenCount = 5; --MIN_BUYOUT_SEEN_COUNT -- Max buyout price for an auction to display as a good deal item local maxBuyoutPrice = 800000; --MAX_BUYOUT_PRICE -- The default percent less, only find auctions that are at a minimum this percent less than the median local minPercentLessThanHSP = 60; -- 60% default --MIN_PERCENT_LESS_THAN_HSP -- The minimum profit/price percent that an auction needs to be displayed as a resellable auction local minProfitPricePercent = 30; -- 30% default --MIN_PROFIT_PRICE_PERCENT -- The minimum percent of bids placed on an item to be considered an "in-demand" enough item to be traded, this is only applied to Weapons and Armor and Recipies local minBidPercent = 10; --MIN_BID_PERCENT -- categories that the brokers and HSP look at the bid data for -- 1 = weapon -- 2 = armor -- 3 = container -- 4 = dissipatable -- 5 = tradeskillitems -- 6 = projectile -- 7 = quiver -- 8 = recipe -- 9 = reagence -- 10 = miscellaneous local bidBasedCategories = {[1]=true, [2]=true, [8]=true, [10]=true} --BID_BASED_CATEGORIES --[[ SavedVariables --]] AuctionConfig = {}; --Table that stores config settings Auction_DoneItems = {}; --Table to keep a record of auction items that have been scanned AuctionBackup = {} --Table to backup old data which can't be converted at once AuctionConfig.version = 30600; -- Table to store our cached HSP values (since they're expensive to calculate) Auctioneer_HSPCache = {}; Auctioneer_Lowests = {}; -- Default filter configuration local filterDefaults = { --Auctioneer_FilterDefaults ["all"] = "on", ["autofill"] = "on", ["embed"] = "off", ["also"] = "off", ["auction-click"] = "on", ["show-link"] = "off", ["show-embed-blankline"] = "off", ["show-verbose"] = "on", ["show-stats"] = "on", ["show-average"] = "on", ["show-median"] = "on", ["show-suggest"] = "on", ["show-warning"] = "on", ["scan-class1"] = "on", ["scan-class2"] = "on", ["scan-class3"] = "on", ["scan-class4"] = "on", ["scan-class5"] = "on", ["scan-class6"] = "on", ["scan-class7"] = "on", ["scan-class8"] = "on", ["scan-class9"] = "on", ["scan-class10"] = "on", ["warn-color"] = "on", ["finish-sound"] = "on", ["printframe"] = 1, ["last-auction-duration"] = 1440, ["auction-duration"] = 3, ["protect-window"] = 1, ["finish"] = 0, ["pct-bidmarkdown"] = 20, ["pct-markup"] = 300, ["pct-maxless"] = 30, ["pct-nocomp"] = 2, ["pct-underlow"] = 5, ["pct-undermkt"] = 20, ["locale"] = "default", --AskPrice related commands ["askprice"] = "on", ["askprice-vendor"] = "off", ["askprice-guild"] = "off", ["askprice-party"] = "off", ["askprice-smart"] = "off", ["askprice-trigger"] = "?", ["askprice-ad"] = "on", ["askprice-whispers"] = "on", ["askprice-word1"] = _AUCT('CmdAskPriceSmartWord1', "enUS"), --Initially set these two filters to match the stock english custom words ["askprice-word2"] = _AUCT('CmdAskPriceSmartWord2', "enUS"), -- Auction House tab UI ["bid-limit"] = 1, ["update-price"] = "off", } function getItemData(itemKey) local itemID, itemRand, enchant = Auctioneer.Util.BreakItemKey(itemKey); if (Informant) then return Informant.GetItem(itemID); end return nil; end function getItemDataByID(itemID) if (Informant) then return Informant.GetItem(itemID); end return nil; end function storeMedianList(list) local hist = ""; local function GrowList(last, n) if (n == 1) then if (hist == "") then hist = last; else hist = string.format("%s:%d", hist, last); end elseif (n ~= 0) then if (hist == "") then hist = string.format("%dx%d", last, n); else hist = string.format("%s:%dx%d", hist, last, n); end end end local n = 0; local last = 0; for pos, hPrice in pairs(list) do if (pos == 1) then last = hPrice; elseif (hPrice ~= last) then GrowList(last, n) last = hPrice; n = 0; end n = n + 1 end GrowList(last, n) return hist; end function loadMedianList(str) local splut = {}; if (str) then for x,c in string.gfind(str, '([^%:]*)(%:?)') do local _,_,y,n = string.find(x, '(%d*)x(%d*)') if (y == nil) then table.insert(splut, tonumber(x)); else for i = 1,n do table.insert(splut, tonumber(y)); end end if (c == '') then break end end end return splut; end -- Returns an AuctionConfig.data item from the table based on an item name function getAuctionPriceItem(itemKey, from) local serverFaction; local auctionPriceItem, data,info; if (from ~= nil) then serverFaction = from; else serverFaction = Auctioneer.Util.GetAuctionKey(); end; EnhTooltip.DebugPrint("Getting data from/for", serverFaction, itemKey); if (AuctionConfig.data == nil) then AuctionConfig.data = {}; end if (AuctionConfig.info == nil) then AuctionConfig.info = {}; end if (AuctionConfig.data[serverFaction] == nil) then EnhTooltip.DebugPrint("Data from serverfaction is nil"); AuctionConfig.data[serverFaction] = {}; else data = AuctionConfig.data[serverFaction][itemKey]; info = AuctionConfig.info[itemKey]; end auctionPriceItem = {}; if (data) then local dataItem = Auctioneer.Util.Split(data, "|"); auctionPriceItem.data = dataItem[1]; auctionPriceItem.buyoutPricesHistoryList = loadMedianList(dataItem[2]); end if (info) then local infoItem = Auctioneer.Util.Split(info, "|"); auctionPriceItem.category = infoItem[1]; auctionPriceItem.name = infoItem[2]; end local playerMade, reqSkill, reqLevel = isPlayerMade(itemKey); auctionPriceItem.playerMade = playerMade; auctionPriceItem.reqSkill = reqSkill; auctionPriceItem.reqLevel = reqLevel; return auctionPriceItem; end function saveAuctionPriceItem(auctKey, itemKey, iData) if (not auctKey) then return end if (not itemKey) then return end if (not iData) then return end if (not AuctionConfig.info) then AuctionConfig.info = {}; end if (not AuctionConfig.data) then AuctionConfig.data = {}; end if (not AuctionConfig.data[auctKey]) then AuctionConfig.data[auctKey] = {}; end local hist = storeMedianList(iData.buyoutPricesHistoryList); AuctionConfig.data[auctKey][itemKey] = string.format("%s|%s", iData.data, hist); AuctionConfig.info[itemKey] = string.format("%s|%s", iData.category, iData.name); Auctioneer_Lowests = nil; -- save median to the savedvariablesfile Auctioneer.Storage.SetHistMed(auctKey, itemKey, Auctioneer.Statistic.GetMedian(iData.buyoutPricesHistoryList)) end -- Returns the auction buyout history for this item function getAuctionBuyoutHistory(itemKey, auctKey) local auctionItem = getAuctionPriceItem(itemKey, auctKey); local buyoutHistory = {}; if (auctionItem) then buyoutHistory = auctionItem.buyoutPricesHistoryList; end return buyoutHistory; end -- Returns the parsed auction price data function getAuctionPrices(priceData) if (not priceData) then return 0,0,0,0,0,0,0 end local i,j, count,minCount,minPrice,bidCount,bidPrice,buyCount,buyPrice = string.find(priceData, "^(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+)"); return Auctioneer.Util.NullSafe(count),Auctioneer.Util.NullSafe(minCount),Auctioneer.Util.NullSafe(minPrice),Auctioneer.Util.NullSafe(bidCount),Auctioneer.Util.NullSafe(bidPrice),Auctioneer.Util.NullSafe(buyCount),Auctioneer.Util.NullSafe(buyPrice); end -- Parse the data from the auction signature function getItemSignature(sigData) if (not sigData) then return nil end for id,rprop,enchant,name,count,min,buyout,uniq in string.gfind(sigData, "(%d+):(%d+):(%d+):(.-):(%d+):(.-):(%d+):(.+)") do if (name == nil) then name = ""; end return tonumber(id),tonumber(rprop),tonumber(enchant),name,tonumber(count),tonumber(min),tonumber(buyout),tonumber(uniq); end return nil; end -- Returns the category i.e. 1, 2 for an item function getItemCategory(itemKey) local category; local auctionItem = getAuctionPriceItem(itemKey); if auctionItem then category = auctionItem.category; end return category; end function isPlayerMade(itemKey, itemData) if (not itemData) and (Informant) then local itemID, itemRand, enchant = Auctioneer.Util.BreakItemKey(itemKey) itemData = Informant.GetItem(itemID) end local reqSkill = 0 local reqLevel = 0 if (itemData) then reqSkill = itemData.reqSkill reqLevel = itemData.reqLevel end return (reqSkill ~= 0), reqSkill, reqLevel end function getInfo(itemKey) if (not AuctionConfig.info[itemKey]) then return {}; end local info = AuctionConfig.info[itemKey]; local infosplit = Auctioneer.Util.Split(info, "|"); local cat = tonumber(infosplit[1]); local name = infosplit[2]; return { category = cat, name = name, }; end function getSnapshot(auctKey, catID, auctSig) if (not catID) then catID = 0 end if (not AuctionConfig.snap) then AuctionConfig.snap = {}; end if (not AuctionConfig.snap[auctKey]) then AuctionConfig.snap[auctKey] = {}; end if (not AuctionConfig.snap[auctKey][catID]) then AuctionConfig.snap[auctKey][catID] = {}; end if (not AuctionConfig.snap[auctKey][catID][auctSig]) then return nil; end local snap = AuctionConfig.snap[auctKey][catID][auctSig]; return getSnapshotFromData(snap); end function getSnapshotFromData(snap) if (not snap) then return nil end for dirty,bid,level,quality,left,fseen,last,link,owner in string.gfind(snap, "(%d+);(%d+);(%d+);(%d+);(%d+);(%d+);(%d+);([^;]+);(.+)") do return { bidamount = tonumber(bid), owner = owner, dirty = dirty, lastSeenTime = tonumber(last), itemLink = link, category = cat, initialSeenTime = tonumber(fseen), level = level, timeLeft = tonumber(left), quality = quality, }; end return nil; end function getSnapshotInfo(auctKey, itemKey) if (not AuctionConfig.sbuy) then AuctionConfig.sbuy = {}; end if (not AuctionConfig.sbuy[auctKey]) then AuctionConfig.sbuy[auctKey] = {}; end if (not AuctionConfig.sbuy[auctKey][itemKey]) then return nil; end local buy = AuctionConfig.sbuy[auctKey][itemKey]; return getSnapshotInfoFromData(buy); end function getSnapshotInfoFromData(buy) local buysplit = loadMedianList(buy); return { buyoutPrices = buysplit, }; end function saveSnapshot(auctKey, cat, sig, iData) local bid = iData.bidamount; local owner = iData.owner; local dirty = iData.dirty; local last = iData.lastSeenTime; local link = iData.itemLink; local fseen = iData.initialSeenTime; local level = iData.level; local left = iData.timeLeft; local qual = iData.quality; if (not cat) then cat = 0 end if (not AuctionConfig.snap) then AuctionConfig.snap = {}; end if (not AuctionConfig.snap[auctKey]) then AuctionConfig.snap[auctKey] = {}; end if (not AuctionConfig.snap[auctKey][cat]) then AuctionConfig.snap[auctKey][cat] = {}; end if (dirty~=nil and bid~=nil and level~=nil and qual~=nil and left~=nil and fseen~=nil and last~=nil and link~=nil and owner~=nil) then local saveData = string.format("%d;%d;%d;%d;%d;%d;%d;%s;%s", dirty, bid, level, qual, left, fseen, last, link, owner); EnhTooltip.DebugPrint("Saving", auctKey, cat, sig, "as", saveData); AuctionConfig.snap[auctKey][cat][sig] = saveData; local itemKey = Auctioneer.Util.GetKeyFromSig(sig); Auctioneer_Lowests = nil; Auctioneer.Storage.SetSnapMed(auctKey, itemKey, nil) else EnhTooltip.DebugPrint("Not saving", auctKey, cat, sig, "because", dirty, bid, level, qual, left, fseen, last, link, owner); end end function saveSnapshotInfo(auctKey, itemKey, iData) AuctionConfig.sbuy[auctKey][itemKey] = storeMedianList(iData.buyoutPrices); Auctioneer_Lowests = nil; Auctioneer.Storage.SetSnapMed(auctKey, itemKey, Auctioneer.Statistic.GetMedian(iData.buyoutPrices)) end function addonLoaded() -- Load the category and subcategory id's Auctioneer.Util.LoadCategories(); Auctioneer.Util.SetFilterDefaults(); if (not AuctionConfig.version) then AuctionConfig.version = 30000; end if (AuctionConfig.version < 30600) then StaticPopupDialogs["CONVERT_AUCTIONEER"] = { text = _AUCT('MesgConvert'), button1 = _AUCT('MesgConvertYes'), button2 = _AUCT('MesgConvertNo'), OnAccept = function() Auctioneer.Convert.Convert(); end, OnCancel = function() Auctioneer.Util.ChatPrint(_AUCT('MesgNotconverting')); end, timeout = 0, whileDead = 1, exclusive = 1 }; StaticPopup_Show("CONVERT_AUCTIONEER", "",""); end lockAndLoad(); end -- This is the old (local) hookAuctionHouse() function function hookAuctionHouse() Stubby.RegisterFunctionHook("AuctionFrame_Show", 200, Auctioneer.Scanner.AuctHouseShow); Stubby.RegisterEventHook("NEW_AUCTION_UPDATE", "Auctioneer", Auctioneer.Scanner.NewAuction); Stubby.RegisterEventHook("AUCTION_HOUSE_CLOSED", "Auctioneer", Auctioneer.Scanner.AuctHouseClose); Stubby.RegisterEventHook("AUCTION_ITEM_LIST_UPDATE", "Auctioneer", Auctioneer.Scanner.AuctHouseUpdate); Stubby.RegisterFunctionHook("Auctioneer.Event.ScanAuction", 200, Auctioneer.Scanner.AuctionEntryHook); Stubby.RegisterFunctionHook("Auctioneer.Event.StartAuctionScan", 200, Auctioneer.Scanner.AuctionStartHook); Stubby.RegisterFunctionHook("Auctioneer.Event.FinishedAuctionScan", 200, Auctioneer.Scanner.FinishedAuctionScanHook); Stubby.RegisterFunctionHook("StartAuction", 200, Auctioneer.Scanner.StartAuction) Stubby.RegisterFunctionHook("PlaceAuctionBid", 200, Auctioneer.Scanner.PlaceAuctionBid) Stubby.RegisterFunctionHook("FilterButton_SetType", 200, Auctioneer.Scanner.FilterButtonSetType) Stubby.RegisterFunctionHook("QueryAuctionItems", 200, Auctioneer.Scanning.QueryAuctionItemsHook) Stubby.RegisterFunctionHook("AuctionsRadioButton_OnClick", 200, Auctioneer.Scanner.OnChangeAuctionDuration); Stubby.RegisterFunctionHook("AuctionFrameFilters_UpdateClasses", 200, Auctioneer.Scanner.AuctionFrameFiltersUpdateClasses); end function lockAndLoad() Stubby.RegisterFunctionHook("AuctionFrame_LoadUI", 200, Auctioneer.Scanner.ConfigureAH); Stubby.RegisterFunctionHook("ContainerFrameItemButton_OnClick", -200, Auctioneer.Util.ContainerFrameItemButtonOnClick); SLASH_AUCTIONEER1 = "/auctioneer"; SLASH_AUCTIONEER2 = "/auction"; SLASH_AUCTIONEER3 = "/auc"; SlashCmdList["AUCTIONEER"] = Auctioneer.Command.MainHandler; -- Rearranges elements in the AH window. Auctioneer.Scanner.ConfigureAH(); --GUI Registration code added by MentalPower Auctioneer.Command.Register(); --Init AskPrice Auctioneer.AskPrice.Init(); Auctioneer.Util.ChatPrint(string.format(_AUCT('FrmtWelcome'), Auctioneer.Version), 0.8, 0.8, 0.2); collectgarbage() --Cleanup after that massive mem spike end Auctioneer.Core = { Variables = {}, Constants = {}, GetItemData = getItemData, GetItemDataByID = getItemDataByID, StoreMedianList = storeMedianList, LoadMedianList = loadMedianList, GetAuctionPriceItem = getAuctionPriceItem, SaveAuctionPriceItem = saveAuctionPriceItem, GetAuctionBuyoutHistory = getAuctionBuyoutHistory, GetAuctionPrices = getAuctionPrices, GetItemSignature = getItemSignature, GetItemCategory = getItemCategory, IsPlayerMade = isPlayerMade, GetInfo = getInfo, GetSnapshot = getSnapshot, GetSnapshotFromData = getSnapshotFromData, GetSnapshotInfo = getSnapshotInfo, GetSnapshotInfoFromData = getSnapshotInfoFromData, SaveSnapshot = saveSnapshot, SaveSnapshotInfo = saveSnapshotInfo, AddonLoaded = addonLoaded, HookAuctionHouse = hookAuctionHouse, LockAndLoad = lockAndLoad, } Auctioneer.Core.Variables = { TotalAuctionsScannedCount = totalAuctionsScannedCount, NewAuctionsCount = newAuctionsCount, OldAuctionsCount = oldAuctionsCount, DefunctAuctionsCount = defunctAuctionsCount, AuctionScanStart = auctionScanStart, SnapshotItemPrices = snapshotItemPrices, } Auctioneer.Core.Constants = { MaxAllowedFormatInt = maxAllowedFormatInt, TimeLeft = timeLeft, Quality = quality, MaxBuyoutHistorySize = maxBuyoutHistorySize, MinProfitMargin = minProfitMargin, DefaultCompeteLess = defaultCompeteLess, MinBuyoutSeenCount = minBuyoutSeenCount, MaxBuyoutPrice = maxBuyoutPrice, MinPercentLessThanHSP = minPercentLessThanHSP, MinProfitPricePercent = minProfitPricePercent, MinBidPercent = minBidPercent, BidBasedCategories = bidBasedCategories, FilterDefaults = filterDefaults, }