--[[ Auctioneer Advanced Version: 5.9.4961 (WhackyWallaby) Revision: $Id: CoreAPI.lua 4933 2010-10-13 17:16:14Z Nechckn $ URL: http://auctioneeraddon.com/ This is an addon for World of Warcraft that adds statistical history to the auction data that is collected when the auction is scanned, so that you can easily determine what price you will be able to sell an item for at auction or at a vendor whenever you mouse-over an item in the game 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. Note: This AddOn's source code is specifically designed to work with World of Warcraft's interpreted AddOn system. You have an implicit license to use this AddOn with these facilities since that is its designated purpose as per: http://www.fsf.org/licensing/licenses/gpl-faq.html#InterpreterIncompat ]] if not AucAdvanced then return end local AucAdvanced = AucAdvanced local coremodule = AucAdvanced.GetCoreModule("CoreAPI") if not coremodule then return end -- Someone has explicitely broken us AucAdvanced.API = {} local lib = AucAdvanced.API local private = {} lib.Print = AucAdvanced.Print local Const = AucAdvanced.Const local GetFaction = AucAdvanced.GetFaction local GetSetting = AucAdvanced.Settings.GetSetting local DecodeLink = AucAdvanced.DecodeLink local SanitizeLink = AucAdvanced.SanitizeLink local tinsert = table.insert local tremove = table.remove local next,pairs,ipairs,type = next,pairs,ipairs,type local wipe = wipe local ceil,floor,max,abs = ceil,floor,max,abs local tostring,tonumber,strjoin,strsplit,format = tostring,tonumber,strjoin,strsplit,format local GetItemInfo = GetItemInfo local time = time -- GLOBALS: nLog, N_NOTICE, N_WARNING, N_ERROR coremodule.Processors = {} function coremodule.Processors.scanstats() lib.ClearMarketCache() end function coremodule.Processors.configchanged() lib.ClearMarketCache() end function coremodule.Processors.newmodule() private.ClearEngineCache() lib.ClearMarketCache() end do local EPSILON = 0.000001; local IMPROVEMENT_FACTOR = 0.8; local CORRECTION_FACTOR = 1000; -- 10 silver per gold, integration steps at tail local FALLBACK_ERROR = 1; -- 1 silver per gold fallback error max -- cache[serverKey][itemsig]={value, seen, #stats} local cache = setmetatable({}, { __index = function(tbl,key) tbl[key] = {} return tbl[key] end }) local pdfList = {}; local engines = {}; local ERROR = 0.05; -- local LOWER_INT_LIMIT, HIGHER_INT_LIMIT = -100000, 10000000; --[[ This function acquires the current market value of the mentioned item using a configurable algorithm to process the data used by the other installed algorithms. The returned value is the most probable value that the item is worth using the algorithms in each of the STAT modules as specified by the GetItemPDF() function. AucAdvanced.API.GetMarketValue(itemLink, serverKey) ]] function lib.GetMarketValue(itemLink, serverKey) local _; if type(itemLink) == 'number' then _, itemLink = GetItemInfo(itemLink) end if not itemLink then return end local cacheSig = lib.GetSigFromLink(itemLink) if not cacheSig then return end -- not a valid item link serverKey = serverKey or GetFaction() -- call GetFaction once here, instead of in every Stat module local cacheEntry = cache[serverKey][cacheSig] if cacheEntry then return cacheEntry[1], cacheEntry[2], cacheEntry[3] -- explicit indexing faster than 'unpack' for 3 values end ERROR = GetSetting("marketvalue.accuracy"); local saneLink = SanitizeLink(itemLink) local upperLimit, lowerLimit, seen = 0, 1e11, 0; if #engines == 0 then -- Rebuild the engine cache local modules = AucAdvanced.GetAllModules(nil, "Stat") for pos, engineLib in ipairs(modules) do local fn = engineLib.GetItemPDF; if fn then tinsert(engines, {pdf = fn, array = engineLib.GetPriceArray}); elseif nLog then nLog.AddMessage("Auctioneer", "Market Pricing", N_WARNING, "Missing PDF", "Auctioneer engine '"..engineLib.GetName().."' does not have a GetItemPDF() function. This check will be removed in the near future in favor of faster calls. Implement this function."); end end end -- Run through all of the stat modules and get the PDFs local c, oldPdfMax, total = 0, #pdfList, 0; local convergedFallback = nil; for _, engine in ipairs(engines) do local i, min, max, area = engine.pdf(saneLink, serverKey); if type(i) == 'number' then -- This is a fallback if convergedFallback == nil or (type(convergedFallback) == 'number' and abs(convergedFallback - i) < FALLBACK_ERROR * convergedFallback / 10000) then convergedFallback = i; else convergedFallback = false; -- Cannot converge on fallback pricing end end local priceArray = engine.array(saneLink, serverKey); if priceArray and (priceArray.seen or 0) > seen then seen = priceArray.seen; end if i and type(i) ~= 'number' then -- pdfList[++c] = i; total = total + (area or 1); -- Add total area, assume 1 if not supplied c = c + 1; pdfList[c] = i; if min < lowerLimit then lowerLimit = min; end if max > upperLimit then upperLimit = max; end end end -- Clean out extras if needed for i = c+1, oldPdfMax do pdfList[i] = nil; end if #pdfList == 0 and convergedFallback then if nLog then nLog.AddMessage("Auctioneer", "Market Pricing", N_WARNING, "Fallback Pricing Used", "Fallback pricing used due to no available PDFs on item "..itemLink); end return convergedFallback, 1, 1; end if not (lowerLimit > -1/0 and upperLimit < 1/0) then error("Invalid bounds detected while pricing "..(GetItemInfo(itemLink) or itemLink)..": "..tostring(lowerLimit).." to "..tostring(upperLimit)) end -- Determine the totals from the PDFs local delta = (upperLimit - lowerLimit) * .01; if #pdfList == 0 or delta < EPSILON or total < EPSILON then return; -- No PDFs available for this item end local limit = total/2; local midpoint, lastMidpoint = 0, 0; -- Now find the 50% point repeat lastMidpoint = midpoint; total = 0; if not(delta > 0) then error("Infinite loop detected during market pricing for "..(GetItemInfo(itemLink) or itemLink)) end for x = lowerLimit, upperLimit, delta do for i = 1, #pdfList do local val = pdfList[i](x); total = total + val * delta; end if total > limit then midpoint = x; break; end end delta = delta * IMPROVEMENT_FACTOR; if midpoint ~= midpoint or midpoint == 0 then if nLog and midpoint ~= midpoint then nLog.AddMessage("Auctioneer", "Market Pricing", N_WARNING, "Unable To Calculate", "A NaN value was detected while processing the midpoint for PDF of "..(GetItemInfo(itemLink) or itemLink).."... Giving up."); elseif nLog then nLog.AddMessage("Auctioneer", "Market Pricing", N_NOTICE, "Unable To Calculate", "A zero total was detected while processing the midpoint for PDF of "..(GetItemInfo(itemLink) or itemLink).."... Giving up."); end if convergedFallback then if nLog then nLog.AddMessage("Auctioneer", "Market Pricing", N_WARNING, "Fallback Pricing Used", "Fallback pricing used due to NaN/Zero total for item "..itemLink); end return convergedFallback, 1, 1; end return; -- Cannot calculate: NaN end until abs(midpoint - lastMidpoint)/midpoint < ERROR; if midpoint and midpoint > 0 then midpoint = floor(midpoint + 0.5); -- Round to nearest copper -- Cache before finishing up cache[serverKey][cacheSig] = {midpoint, seen, #pdfList} return midpoint, seen, #pdfList; else if nLog then nLog.AddMessage("Auctioneer", "Market Pricing", N_WARNING, "Unable To Calculate", "No midpoint was detected for item "..(GetItemInfo(itemLink) or itemLink).."... Giving up."); end return; end end -- Clear the cache of Stats engines (called if a new module is registered) function private.ClearEngineCache() wipe(engines) end -- Clears the results cache for AucAdvanced.API.GetMarketValue() function lib.ClearMarketCache() wipe(cache) end end function lib.ClearItem(itemLink, serverKey) local saneLink = SanitizeLink(itemLink) local modules = AucAdvanced.GetAllModules("ClearItem") for pos, engineLib in ipairs(modules) do engineLib.ClearItem(saneLink, serverKey) end lib.ClearMarketCache() end --[[ AucAdvanced.API.IsKeyword(testword [, keyword]) Determine whether testword is equal to or an alias of keyword Returns the keyword if it matches, nil otherwise For case-insensitive keywords, tries both unmodified and lowercase Note: default cases must be handled separately --]] do -- allowable keywords (so far): ALL, faction, server local keywords = { -- entry: alias = keyword, ALL = "ALL", faction = "faction", server = "server", realm = "server", } -- todo: functions to add new keywords, and to add new aliases for keywords function lib.IsKeyword(testword, keyword) if type(testword) ~= "string" then return end local key = keywords[testword] or keywords[testword:lower()] -- try unmodified and lowercased if key then if not keyword or keyword == key then return key end end end end function lib.ClearData(command) local serverKey1, serverKey2, serverKey3 -- split command into keyword and extra parts local keyword, extra = "faction", "" -- default if type(command) == "string" then local _, ind, key = strfind(command, "(%S+)") if key then key = lib.IsKeyword(key) if key then keyword = key -- recognised keyword extra = strtrim(strsub(command, ind+1)) else extra = strtrim(command) -- try to resolve whole command (as a "faction") end end elseif command then -- only valid types are string or nil error("Unrecognised parameter type to ClearData: "..type(command)..":"..tostring(command)) end -- At this point keyword should be one of the strings in the following if-block -- extra should be a string, where 'no extra information' is denoted by "" if keyword == "ALL" then if extra == "" then serverKey1 = "ALL" end elseif keyword == "server" then if extra == "" then extra = Const.PlayerRealm end -- otherwise assume the user typed the server name correctly -- modules should silently ignore unrecognised serverKeys serverKey1 = extra.."-Alliance" serverKey2 = extra.."-Horde" serverKey3 = extra.."-Neutral" elseif keyword == "faction" then if extra == "" then serverKey1 = GetFaction() elseif AucAdvanced.SplitServerKey(extra) then -- it's a valid serverKey serverKey1 = extra else local fac = AucAdvanced.IsFaction(extra) -- it's a valid faction group if fac then serverKey1 = Const.PlayerRealm.."-"..fac end end end if serverKey1 then local modules = AucAdvanced.GetAllModules("ClearData") for pos, lib in ipairs(modules) do lib.ClearData(serverKey1) if serverKey2 then lib.ClearData(serverKey2) lib.ClearData(serverKey3) end end lib.ClearMarketCache() else lib.Print("Auctioneer: Unrecognized keyword or faction for ClearData {{"..command.."}}") end end function lib.GetAlgorithms(itemLink) local saneLink = SanitizeLink(itemLink) local engines = {} local modules = AucAdvanced.GetAllModules() for pos, engineLib in ipairs(modules) do if engineLib.GetPrice or engineLib.GetPriceArray then if not engineLib.IsValidAlgorithm or engineLib.IsValidAlgorithm(saneLink) then local engine = engineLib.GetName() tinsert(engines, engine) end end end return engines end function lib.IsValidAlgorithm(algorithm, itemLink) local saneLink = SanitizeLink(itemLink) local modules = AucAdvanced.GetAllModules() for pos, engineLib in ipairs(modules) do if engineLib.GetName() == algorithm and (engineLib.GetPrice or engineLib.GetPriceArray) then if engineLib.IsValidAlgorithm then return engineLib.IsValidAlgorithm(saneLink) end return true end end return false end --store the last data request and just return a cache value for the next 5 secs (5 secs is just arbitrary) local LastAlgorithmSig, LastAlgorithmTime, LastAlgorithmPrice, LastAlgorithmSeen, LastAlgorithmArray function lib.GetAlgorithmValue(algorithm, itemLink, serverKey, reserved) if (not algorithm) then if nLog then nLog.AddMessage("Auctioneer", "API", N_ERROR, "Incorrect Usage", "No pricing algorithm supplied to GetAlgorithmValue") end return end if type(itemLink) == "number" then local _ _, itemLink = GetItemInfo(itemLink) end if (not itemLink) then if nLog then nLog.AddMessage("Auctioneer", "API", N_ERROR, "Incorrect Usage", "No itemLink supplied to GetAlgorithmValue") end return end if reserved then lib.ShowDeprecationAlert("AucAdvanced.API.GetAlgorithmValue(algorithm, itemLink, serverKey)", "The 'faction' and 'realm' parameters are deprecated in favor of the new 'serverKey' parameter. Use this instead." ); serverKey = reserved.."-"..serverKey; end serverKey = serverKey or GetFaction() local saneLink = SanitizeLink(itemLink) --check if this was just retrieved and return that value local algosig = strjoin(":", algorithm, saneLink, serverKey) if algosig == LastAlgorithmSig and LastAlgorithmTime + 5 > time() then return LastAlgorithmPrice, LastAlgorithmSeen, LastAlgorithmArray end local modules = AucAdvanced.GetAllModules() for pos, engineLib in ipairs(modules) do if engineLib.GetName() == algorithm and (engineLib.GetPrice or engineLib.GetPriceArray) then if engineLib.IsValidAlgorithm and not engineLib.IsValidAlgorithm(saneLink) then return end local price, seen, array if (engineLib.GetPriceArray) then array = engineLib.GetPriceArray(saneLink, serverKey) if (array) then price = array.price seen = array.seen end else price = engineLib.GetPrice(saneLink, serverKey) end LastAlgorithmSig = algosig LastAlgorithmTime = time() LastAlgorithmPrice, LastAlgorithmSeen, LastAlgorithmArray = price, seen, array return price, seen, array end end --error(("Cannot find pricing algorithm: %s"):format(algorithm)) return end --[[ resultsTable = AucAdvanced.API.QueryImage(queryTable, serverKey, reserved, ...) 'queryTable' specifies the query to perform 'serverKey' defaults to the current faction 'reserved' must always be nil The working code can be viewed in CoreScan.lua for more details. --]] lib.QueryImage = AucAdvanced.Scan.QueryImage -- unpackedTable = AucAdvanced.API.UnpackImageItem(imageItem) -- imageItem is one of the values (subtables) in the table returned by QueryImage or GetImageCopy lib.UnpackImageItem = AucAdvanced.Scan.UnpackImageItem -- scanStatsTable = AucAdvanced.API.GetScanStats(serverKey) -- Timestamps: scanstats.LastScan, scanstats.LastFullScan, scanstats.ImageUpdated -- Scan statistics subtables: scanstats[0] (last scan), scanstats[1], scanstats[2] (two scans prior to last scan) lib.GetScanStats = AucAdvanced.Scan.GetScanStats -- imageTable = AucAdvanced.API.GetImageCopy(serverKey) -- Generates an independent copy of the current scan data image for the specified serverKey lib.GetImageCopy = AucAdvanced.Scan.GetImageCopy function lib.ListUpdate() if lib.IsBlocked() then return end AucAdvanced.SendProcessorMessage("listupdate") end function lib.BlockUpdate(block, propagate) local blocked if block == true then blocked = true private.isBlocked = true AuctionFrameBrowse:UnregisterEvent("AUCTION_ITEM_LIST_UPDATE") else blocked = false private.isBlocked = nil AuctionFrameBrowse:RegisterEvent("AUCTION_ITEM_LIST_UPDATE") end if (propagate) then AucAdvanced.SendProcessorMessage("blockupdate", blocked) end end function lib.IsBlocked() return private.isBlocked == true end --[[Progress bars that are usable by any addon. name = string - unique bar name value = 0-100 the % the bar should be filled show = boolean true will keep bar displayed, false will hide the bar and free it for use by another addon text = string - the text to display on the bar options = table containing formatting commands. options.barColor = { R,G,B, A} red, green, blue, alpha values. options.textColor = { R,G,B, A} red, green, blue, alpha values. value, text, color, and options are all optional variables ]] local availableBars = {} local NumGenericBars = 0 --generate new bars as needed local function newBar() local bar = CreateFrame("STATUSBAR", nil, UIParent, "TextStatusBar") bar:SetWidth(300) bar:SetHeight(18) bar:SetBackdrop({ bgFile="Interface\\Tooltips\\UI-Tooltip-Background", edgeFile="Interface\\Tooltips\\UI-Tooltip-Border", tile=1, tileSize=10, edgeSize=10, insets={left=1, right=1, top=1, bottom=1} }) bar:SetStatusBarTexture("Interface\\TargetingFrame\\UI-StatusBar") bar:SetStatusBarColor(0.6,0,0,0.6) bar:SetMinMaxValues(0,100) bar:SetValue(50) bar:Hide() bar.text = bar:CreateFontString(nil, "OVERLAY", "GameFontHighlight") bar.text:SetPoint("CENTER", bar, "CENTER") bar.text:SetJustifyH("CENTER") bar.text:SetJustifyV("CENTER") bar.text:SetTextColor(1,1,1) if NumGenericBars < 1 then bar:SetPoint("CENTER", UIParent, "CENTER", -5,5) else--attach to previous bar bar:SetPoint("BOTTOM", lib["GenericProgressBar"..NumGenericBars], "TOP", 0, 0) end NumGenericBars = NumGenericBars + 1 lib["GenericProgressBar"..(NumGenericBars)] = bar return NumGenericBars end --create 1 bar to start for anchoring newBar() -- handles the rendering local function renderBars(ID, name, value, text, options) local self = lib["GenericProgressBar"..ID] if not self then assert("No bar found available for ID", ID, name, text) end --reset all generated bars that are not inuse to defaults if self and not name then self:Hide() self.text:SetText("") self:SetStatusBarColor(0.6, 0, 0, 0.6) --light red color self.text:SetTextColor(1, 1, 1, 1) return end self:Show() --update progress if value then self:SetValue(value) end --change bars text if desired if text then self.text:SetText(text) end --[[options is a table that contains, "tweaks" ie text or bar color changes Nothing below this line will be processed unless an options table is passed]] if not options or type(options) ~= "table" then return end --change bars color local barColor = options.barColor if barColor then local r, g, b, a = barColor[1],barColor[2], barColor[3], barColor[4] if r and g and b then a = a or 0.6 self:SetStatusBarColor(r, g, b, a) end end --change text color local textColor = options.textColor if textColor then local r, g, b, a = textColor[1],textColor[2], textColor[3], textColor[4] if r and g and b then a = a or 1 self.text:SetTextColor(r, g, b, a) end end end --main entry point. Handles which bar will be assigned and recycling bars function lib.ProgressBars(name, value, show, text, options) --setup parent so we can display even if AH is closed if AuctionFrame and AuctionFrame:IsShown() then lib.GenericProgressBar1:SetParent(AuctionFrame) lib.GenericProgressBar1:SetPoint("TOPRIGHT", AuctionFrame, "TOPRIGHT", -5, 5) else lib.GenericProgressBar1:SetParent(UIParent) end if not name then return end --find a generic bar available for use local ID = availableBars[name] if show and not ID then --find a bar for i = 1, NumGenericBars do if not availableBars[i] then availableBars[i] = {name, value, text, options} availableBars[name] = i ID = i break end end --no bar available make a new one if not ID then ID = newBar() availableBars[ID] = {name, value, text, options} availableBars[name] = ID end end --Render Bars if show then renderBars(ID, name, value, text, options) else table.remove(availableBars, ID) availableBars[name] = nil --ReRender bars for ID = 1, NumGenericBars do if availableBars[ID] then barData = availableBars[ID] renderBars(ID, barData[1], barData[2], barData[3], barData[4]) else--blank bars renderBars(ID) end end end end --[[ Market matcher APIs ]]-- function lib.GetBestMatch(itemLink, algorithm, serverKey, reserved) local saneLink = SanitizeLink(itemLink) if reserved then lib.ShowDeprecationAlert("AucAdvanced.API.GetBestMatch(itemLink, algorithm, serverKey)", "The 'realm' and 'faction' parameters have been removed in favor of a single ".. "variable 'serverKey' which should be used in the future." ); serverKey = reserved.."-"..serverKey; end -- TODO: Make a configurable algorithm. -- This algorithm is currently less than adequate. local price if algorithm == "market" then price = lib.GetMarketValue(saneLink, serverKey) elseif type(algorithm) == "string" then price = lib.GetAlgorithmValue(algorithm, saneLink, serverKey) else price = algorithm end if not price then return end local matchers = lib.GetMatchers(saneLink) local total, count, diff = 0, 0, 0 local infoString = "" for index, matcher in ipairs(matchers) do if lib.IsValidMatcher(matcher, saneLink) then -- todo: shoudn't this already be valid from calling lib.GetMatchers(saneLink) ? local value, MatchpriceArray = lib.GetMatcherValue(matcher, saneLink, price, serverKey) price = value count = count + 1 diff = diff + MatchpriceArray.diff if MatchpriceArray.returnstring then infoString = infoString.."\n"..MatchpriceArray.returnstring -- using two .. is faster than calling strjoin end end end if count > 1 then diff = diff / count end if price > 0 then return price, total, count, diff, infoString end end function lib.GetMatcherDropdownList() private.matcherlist = GetSetting("matcherlist") if not private.matcherlist or #private.matcherlist == 0 then lib.GetMatchers() end if not private.matcherlist or #private.matcherlist == 0 then return end local dropdownlist = {} for index, value in ipairs(private.matcherlist) do dropdownlist[index] = tostring(index)..": "..tostring(private.matcherlist[index]) end return dropdownlist end function lib.GetMatchers(itemLink) local saneLink = SanitizeLink(itemLink) private.matcherlist = GetSetting("matcherlist") local engines = {} local modules = AucAdvanced.GetAllModules() for pos, engineLib in ipairs(modules) do if engineLib.GetMatchArray then if not engineLib.IsValidMatcher or engineLib.IsValidMatcher(saneLink) then local engine = engineLib.GetName() tinsert(engines, engine) end end end local insetting = false local stillactive = false --check to see if there are any new matchers. If so, add them to the end of the running order. --There is no check to see if matchers are missing, as this would destroy the saved order. Instead, invalid matchers can be called without errors. if private.matcherlist then for index, matcher in ipairs(engines) do for i, j in ipairs(private.matcherlist) do if matcher == j then insetting = true end end if not insetting then AucAdvanced.Print("AucAdvanced: New matcher found: "..tostring(matcher)) tinsert(private.matcherlist, matcher) end insetting = false end else private.matcherlist = engines end AucAdvanced.Settings.SetSetting("matcherlist", private.matcherlist) return private.matcherlist end function lib.IsValidMatcher(matcher, itemLink) local saneLink = SanitizeLink(itemLink) local engines = {} local modules = AucAdvanced.GetAllModules() for pos, engineLib in ipairs(modules) do local engine = engineLib.GetName() if engine == matcher and engineLib.GetMatchArray then if engineLib.IsValidMatcher then return engineLib.IsValidMatcher(saneLink) end return engineLib end end return false end function lib.GetMatcherValue(matcher, itemLink, price, serverKey) local saneLink = SanitizeLink(itemLink) if (type(matcher) == "string") then matcher = lib.IsValidMatcher(matcher, saneLink) end if not matcher then return end --If matcher is not a table at this point, the following code will throw an "attempt to index a <something> value" type error local matchArray = matcher.GetMatchArray(saneLink, price, serverKey) if not matchArray then matchArray = {} matchArray.value = price matchArray.diff = 0 end return matchArray.value, matchArray end -- Signature conversion functions -- Creates an AucAdvanced signature from an item link function lib.GetSigFromLink(link) local sig local itype, id, suffix, factor, enchant = DecodeLink(link) if itype == "item" then if enchant ~= 0 then sig = ("%d:%d:%d:%d"):format(id, suffix, factor, enchant) elseif factor ~= 0 then sig = ("%d:%d:%d"):format(id, suffix, factor) elseif suffix ~= 0 then sig = ("%d:%d"):format(id, suffix) else sig = tostring(id) end end return sig end -- Creates an item link from an AucAdvanced signature function lib.GetLinkFromSig(sig) local id, suffix, factor, enchant = strsplit(":", sig) local itemstring = format("item:%d:%d:0:0:0:0:%d:%d:0", id, enchant or 0, suffix or 0, factor or 0) local name, link = GetItemInfo(itemstring) link = SanitizeLink(link) return link, name -- name is ignored by most calls end -- Decodes an AucAdvanced signature into numerical values -- Can be compared to the return values from DecodeLink function lib.DecodeSig(sig) if type(sig) ~= "string" then return end local id, suffix, factor, enchant = strsplit(":", sig) id = tonumber(id) if not id or id == 0 then return end suffix = tonumber(suffix) or 0 factor = tonumber(factor) or 0 enchant = tonumber(enchant) or 0 return id, suffix, factor, enchant end ------------------------------------------------------------------------------- -- Statistical devices created by Matthew 'Shirik' Del Buono -- For Auctioneer ------------------------------------------------------------------------------- local sqrtpi = math.sqrt(math.pi); local sqrtpiinv = 1/sqrtpi; local sq2pi = math.sqrt(2*math.pi); local pi = math.pi; local exp = math.exp; local bellCurveMeta = { __index = { SetParameters = function(self, mean, stddev) if (stddev == 0) then error("Standard deviation cannot be zero"); elseif (stddev ~= stddev) then error("Standard deviation must be a real number"); end if stddev < .1 then --need to prevent obsurdly small stddevs like 1e-11, as they cause freeze-ups stddev = .1 end self.mean = mean; self.stddev = stddev; self.param1 = 1/(stddev*sq2pi); -- Make __call a little faster where we can self.param2 = 2*stddev^2; end }, -- Simple bell curve call __call = function(self, x) local n = self.param1*exp(-(x-self.mean)^2/self.param2); -- if n ~= n then -- DEFAULT_CHAT_FRAME:AddMessage("-----------------"); -- DevTools_Dump{param1 = self.param1, param2 = self.param2, x = x, mean = self.mean, stddev = self.stddev, exp = exp(-(x-self.mean)^2/self.param2)}; -- error(x.." produced NAN ("..tostring(n)..")"); -- end return n; end } ------------------------------------------------------------------------------- -- Creates a bell curve object that can then be manipulated to pass -- as a PDF function. This is a recyclable object -- the mean and -- standard deviation can be updated as necessary so that it does not have -- to be regenerated -- -- Note: This creates a bell curve with a standard deviation of 1 and -- mean of 0. You will probably want to update it to your own desired -- values by calling return:SetParameters(mean, stddev) ------------------------------------------------------------------------------- function lib.GenerateBellCurve() return setmetatable({mean=0, stddev=1, param1=sqrtpiinv, param2=2}, bellCurveMeta); end -- Dumps out market pricing information for debugging. Only handles bell curves for now. function lib.DumpMarketPrice(itemLink, serverKey) local modules = AucAdvanced.GetAllModules(nil, "Stat"); for pos, engineLib in ipairs(modules) do local success, result = pcall(engineLib.GetItemPDF, itemLink, serverKey); if success then if getmetatable(result) == bellCurveMeta then print(engineLib.GetName() .. ": Mean = " .. result.mean .. ", Standard Deviation = " .. result.stddev); else print(engineLib.GetName() .. ": Non-Standard PDF: " .. tostring(result)); end else print(engineLib.GetName() .. ": Reported error: " .. tostring(result)); end end end --[[=========================================================================== --|| Deprecation Alert Functions --||=========================================================================]] do local SOURCE_PATTERN = "([^\\/:]+:%d+): in function `([^\"']+)[\"']"; local seenCalls = {}; local uid = 0; ------------------------------------------------------------------------------- -- Shows a deprecation alert. Indicates that a deprecated function has -- been called and provides a stack trace that can be used to help -- find the culprit. -- @param replacementName (Optional) The displayable name of the replacement function -- @param comments (Optional) Any extra text to display ------------------------------------------------------------------------------- function lib.ShowDeprecationAlert(replacementName, comments) local caller, source, functionName = debugstack(3):match(SOURCE_PATTERN), -- Keep in mind this will be truncated to only the first in the tuple debugstack(2):match(SOURCE_PATTERN); -- This will give us both the source and the function name functionName = functionName .. "()"; -- Check for this source & caller combination seenCalls[source] = seenCalls[source] or {}; if not seenCalls[source][caller] then -- Not warned yet, so warn them! seenCalls[source][caller]=true -- Display it AucAdvanced.Print( "Auctioneer: ".. functionName .. " has been deprecated and was called by |cFF9999FF"..caller:match("^(.+)%.[lLxX][uUmM][aAlL]:").."|r. ".. (replacementName and ("Please use "..replacementName.." instead. ") or "").. (comments or "") ); geterrorhandler()( "Deprecated function call occurred in Auctioneer API:\n {{{Deprecated Function:}}} "..functionName.. "\n {{{Source Module:}}} "..source:match("^(.+)%.[lLxX][uUmM][aAlL]:").. "\n {{{Calling Module:}}} "..caller:match("^(.+)%.[lLxX][uUmM][aAlL]:").. "\n {{{Available Replacement:}}} "..(replacementName or "None").. (comments and "\n\n"..comments or "") ) end end end AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Advanced/CoreAPI.lua $", "$Rev: 4933 $")