commit 1514202b13a6eb72a946ff63efeb57858e6d2fb1 Author: superp00t Date: Mon Apr 13 17:48:13 2026 -0400 chore: initial commit diff --git a/!Swatter/!Swatter.toc b/!Swatter/!Swatter.toc new file mode 100644 index 0000000..7941b48 --- /dev/null +++ b/!Swatter/!Swatter.toc @@ -0,0 +1,13 @@ +## Title: Swatter +## Notes: Debugging tool for handling and displaying of error messages in a useful way. +## +## Interface: 40000 +## SavedVariables: SwatterData +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: !Swatter.toc 280 2010-10-12 19:52:29Z Esamynn $ +## URL: http://auctioneeraddon.com/dl/Swatter/ +## Author: Norganna's AddOns +## +Support\Load.xml +Swatter.lua diff --git a/!Swatter/Support/LibRevision.lua b/!Swatter/Support/LibRevision.lua new file mode 100644 index 0000000..935ee69 --- /dev/null +++ b/!Swatter/Support/LibRevision.lua @@ -0,0 +1,126 @@ +--[[ +-- LibRevision.lua +-- Finds the revisions of a given addon and works out the version. +-- Written by Ken Allan +-- This code is hereby released into the Public Domain without warranty. +-- +-- Usage: +-- local libRevision = LibStub("LibRevision") +-- +-- libRevision:Set("svnUrl", "svnRev", "5.1.DEV.", ...) +-- libRevision:Get("addon") +--]] + +local LIBRARY_VERSION_MAJOR = "LibRevision" +local LIBRARY_VERSION_MINOR = 1 + +--[[----------------------------------------------------------------- + +LibStub is a simple versioning stub meant for use in Libraries. +See for more info. +LibStub is hereby placed in the Public Domain. +Credits: + Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke + +--]]----------------------------------------------------------------- +do + local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 + local LibStub = _G[LIBSTUB_MAJOR] + + if not LibStub or LibStub.minor < LIBSTUB_MINOR then + LibStub = LibStub or {libs = {}, minors = {} } + _G[LIBSTUB_MAJOR] = LibStub + LibStub.minor = LIBSTUB_MINOR + + function LibStub:NewLibrary(major, minor) + assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)") + minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.") + + local oldminor = self.minors[major] + if oldminor and oldminor >= minor then return nil end + self.minors[major], self.libs[major] = minor, self.libs[major] or {} + return self.libs[major], oldminor + end + + function LibStub:GetLibrary(major, silent) + if not self.libs[major] and not silent then + error(("Cannot find a library instance of %q."):format(tostring(major)), 2) + end + return self.libs[major], self.minors[major] + end + + function LibStub:IterateLibraries() return pairs(self.libs) end + setmetatable(LibStub, { __call = LibStub.GetLibrary }) + end +end +--[End of LibStub]--------------------------------------------------- + +local lib = LibStub:NewLibrary(LIBRARY_VERSION_MAJOR, LIBRARY_VERSION_MINOR) +if not lib then return end + +if not lib.versions then + lib.versions = {} +end + +function lib:Get(addon) + return lib.versions[addon:lower()] +end + +function lib:Set(url, revision, dev, ...) + local repo,file + if (not url and revision) then return end + + local n = select("#", ...) + for i=1, n do + local sub = select(i, ...) + repo, file = url:match("%$URL: .*/("..sub..")/([^%$]+) %$") + if repo then break end + end + local rev = tonumber(revision:match("(%d+)")) or 0 + + if repo then + local branch,name = file:match("^(trunk)/([^/]+)/") + if not branch then + branch,name = file:match("^branches/([^/]+)/([^/]+)/") + end + + if branch then + local embed, addit = false, branch + local addon = name:lower() + + if not lib.versions[addon] then + lib.versions[addon] = {} + end + + local vaddon = lib.versions[addon] + local vrev = max(vaddon['x-revision'] or 0, rev) + vaddon['x-revision'] = vrev + + if not IsAddOnLoaded(addon) then + embed = true + addit = addit.."/embedded" + end + + local ver = GetAddOnMetadata(addon, "Version") + if not ver or ver:sub(0,2) == "<%" then ver = dev..rev end + + if not vaddon["x-revisions"] then + vaddon["x-revisions"] = {} + end + + vaddon.name = name + vaddon.version = ver + vaddon["x-branch"] = branch + vaddon["x-revision"] = vrev + vaddon["x-revisions"][file] = rev + vaddon["x-embedded"] = embed + vaddon["x-swatter-extra"] = addition + + if SetAddOnDetail then + SetAddOnDetail(name, vaddon) + end + + return vaddon, file, rev + end + end +end diff --git a/!Swatter/Support/Load.xml b/!Swatter/Support/Load.xml new file mode 100644 index 0000000..d773ecd --- /dev/null +++ b/!Swatter/Support/Load.xml @@ -0,0 +1,4 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Auc-Advanced/Modules/Auc-Filter-Outlier/Auc-Filter-Outlier.toc b/Auc-Advanced/Modules/Auc-Filter-Outlier/Auc-Filter-Outlier.toc new file mode 100644 index 0000000..17bb6b9 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Filter-Outlier/Auc-Filter-Outlier.toc @@ -0,0 +1,17 @@ +## Title: Auc:Filter:Outlier +## Notes: Allows automatic filtering of outlier auctions, so that their values are ignored when compiling the various statistics used within Auctioneer and its modules. +## +## Interface: 40000 +## LoadOnDemand: 0 +## Disabled: 0 +## Dependencies: Auc-Advanced +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Auc-Filter-Outlier.toc 4933 2010-10-13 17:16:14Z Nechckn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## X-Max-Interface: 40000 +## + +OutlierFilter.lua diff --git a/Auc-Advanced/Modules/Auc-Filter-Outlier/Embed.xml b/Auc-Advanced/Modules/Auc-Filter-Outlier/Embed.xml new file mode 100644 index 0000000..bc25f9a --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Filter-Outlier/Embed.xml @@ -0,0 +1,7 @@ + + + + diff --git a/Auc-Advanced/Modules/Auc-Filter-Outlier/OutlierFilter.lua b/Auc-Advanced/Modules/Auc-Filter-Outlier/OutlierFilter.lua new file mode 100644 index 0000000..c9c3655 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Filter-Outlier/OutlierFilter.lua @@ -0,0 +1,226 @@ +--[[ + Auctioneer - Outlier Filter + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: OutlierFilter.lua 4840 2010-08-04 21:44:00Z 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 libType, libName = "Filter", "Outlier" +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end +local print,decode,_,_,replicate,empty,get,set,default,debugPrint,fill, _TRANS = AucAdvanced.GetModuleLocals() + +local data + +local reset = true +local cache, model, minseen, levels + +function lib.Processor(callbackType, ...) + if callbackType == "config" then + private.SetupConfigGui(...) + elseif callbackType == "scanstats" then + reset = true + end +end + +lib.Processors = {} +function lib.Processors.config(callbackType, ...) + private.SetupConfigGui(...) +end + +function lib.Processors.scanstats(callbackType, ...) + reset = true +end + + +function lib.AuctionFilter(operation, itemData) + if not get("filter.outlier.activated") then + return + end + if reset then + model = get("filter.outlier.model") + if model ~= "market" and not AucAdvanced.API.IsValidAlgorithm(model) then + model = "market" + end + minseen = get("filter.outlier.minseen") + cache = {} + levels = {} + if get("filter.outlier.poor.enabled") then levels[0] = get("filter.outlier.poor.level")/100 end + if get("filter.outlier.common.enabled") then levels[1] = get("filter.outlier.common.level")/100 end + if get("filter.outlier.uncommon.enabled") then levels[2] = get("filter.outlier.uncommon.level")/100 end + if get("filter.outlier.rare.enabled") then levels[3] = get("filter.outlier.rare.level")/100 end + if get("filter.outlier.epic.enabled") then levels[4] = get("filter.outlier.epic.level")/100 end + if get("filter.outlier.legendary.enabled") then levels[5] = get("filter.outlier.legendary.level")/100 end + if get("filter.outlier.artifact.enabled") then levels[6] = get("filter.outlier.artifact.level")/100 end + reset = false + end + + local quality = itemData.quality + -- If we're not allowed to filter this quality of item + if not levels[quality] then return false end + + local link = itemData.link + local value = cache[link] + if not value then + local seen + if model == "market" then + value, seen = AucAdvanced.API.GetMarketValue(link) + else + value, seen = AucAdvanced.API.GetAlgorithmValue(model, link) + end + + if not value or not seen or seen < minseen then + value = 0 + end + cache[link] = value + end + + -- If there's no value then we can't filter it + if value == 0 then return false end + + -- Check to see if the item price is below the price + local price = itemData.buyoutPrice or itemData.price + local level = levels[quality] + local maxcap = value * level + + -- If the price is acceptible then allow it + if itemData.stackSize > 1 then price = math.floor(price / itemData.stackSize) end + if price < maxcap then return false end + + -- Otherwise this item needs to be filtered + -- We need to see if this auction is to be ignored or not. + if nLog then + nLog.AddMessage( + "auc-"..libType.."-"..libName, + "AuctionFilter", + N_INFO, + "Filtered Data", + "Auction Filter Removed Data for ", itemData.itemName, + " from ", (itemData.sellerName or "UNKNOWN"), + ", quality ", tostring(quality or 0), + ", item level ", tostring(itemData.itemLevel or 0), + ".\n", + "Price ", price, " > Cap ", maxcap + ) + end + return true +end + +function lib.OnLoad(addon) + default("filter.outlier.activated", true) + default("filter.outlier.model", "market") + default("filter.outlier.minseen", 10) + default("filter.outlier.poor.enabled", true) + default("filter.outlier.common.enabled", true) + default("filter.outlier.uncommon.enabled", true) + default("filter.outlier.rare.enabled", true) + default("filter.outlier.epic.enabled", true) + default("filter.outlier.legendary.enabled", true) + default("filter.outlier.artifact.enabled", true) + default("filter.outlier.poor.level", 200) + default("filter.outlier.common.level", 200) + default("filter.outlier.uncommon.level", 200) + default("filter.outlier.rare.level", 250) + default("filter.outlier.epic.level", 300) + default("filter.outlier.legendary.level", 300) + default("filter.outlier.artifact.level", 300) +end + +function private.SetupConfigGui(gui) + -- The defaults for the following settings are set in the lib.OnLoad function + local id = gui:AddTab(libName, libType.." Modules") + + gui:AddHelp(id, "what outlier filter", + _TRANS('OUTL_Help_WhatOutlierFilter') ,--What is this Outlier Filter? + _TRANS('OUTL_Help_WhatOutlierFilterAnswer') )--When you get auctions that are posted up, many of the more common ones can become prey to people listing auctions for many times the 'real worth' of the actual item.\nThis filter detects such outliers and prevents them from being entered into the data stream if it\'s value exceeds a specified percentage of the normal value of the item. While still allowing for normal fluctuations in the prices of the items from day to day. + + gui:AddHelp(id, "can retroactive", + _TRANS('OUTL_Help_RemoveOldOutliers') ,--Can it remove old outliers? + _TRANS('OUTL_Help_RemoveOldOutliersAnswer') )--The simple answer is no, it only works from this point on. Any settings you apply are applied from here on in, and any current stats are left alone.\nThe complex answer? Well, you see there's this bowl of soup... + + gui:AddControl(id, "Header", 0, _TRANS('OUTL_Interface_OutlierOptions') )--Outlier options + gui:AddControl(id, "Checkbox", 0, 1, "filter.outlier.activated", _TRANS('OUTL_Interface_EnableOutlierFilter') )--Enable use of the outlier filter + gui:AddTip(id, _TRANS('OUTL_HelpTooltip_EnableOutlierFilter') )--Ticking this box will enable the outlier filter to perform filtering of your auction scans + + gui:AddControl(id, "Subhead", 0, _TRANS('OUTL_Interface_PriceMethod') )--Price valuation method: + gui:AddControl(id, "Selectbox", 0, 1, parent.selectorPriceModels, "filter.outlier.model", _TRANS('OUTL_Interface_PricingModel') )--Pricing model to use for the valuation + gui:AddTip(id, _TRANS('OUTL_HelpTooltip_PricingModel') )--The pricing model that will be used to determine the base pricing level + + gui:AddControl(id, "WideSlider", 0, 1, "filter.outlier.minseen", 1, 50, 1, _TRANS('OUTL_Interface_MinimumSeen') )--Minimum seen: %d + gui:AddTip(id, _TRANS('OUTL_HelpTooltip_MinimumSeen') )--If an item has been seen less than this many times, it will not be filtered + + gui:AddControl(id, "Subhead", 0, _TRANS('OUTL_Interface_SettingsQuality') )--Settings per quality: + + local _,_,_, hex = GetItemQualityColor(0) + gui:AddControl(id, "Checkbox", 0, 1, "filter.outlier.poor.enabled", _TRANS('OUTL_Interface_EnablePoor'):format(hex, "|r") )--Enable filtering %s poor %s quality items + gui:AddTip(id, _TRANS('OUTL_HelpTooltip_EnablePoor') )--Ticking this box will enable outlier filtering on poor quality items + gui:AddControl(id, "WideSlider", 0, 1, "filter.outlier.poor.level", 100, 5000, 25, _TRANS('OUTL_Interface_CapGrowth').." %d%%")--Cap growth to: + gui:AddTip(id, _TRANS('OUTL_HelpTooltip_MaximumPct') )--Set the maximum percentage that an item's price can grow before being filtered + + local _,_,_, hex = GetItemQualityColor(1) + gui:AddControl(id, "Checkbox", 0, 1, "filter.outlier.common.enabled", _TRANS('OUTL_Interface_EnableCommon'):format(hex, "|r") )--Enable filtering %s common %s quality items + gui:AddTip(id, _TRANS('OUTL_HelpTooltip_EnableCommon') )--Ticking this box will enable outlier filtering on common quality items + gui:AddControl(id, "WideSlider", 0, 1, "filter.outlier.common.level", 100, 5000, 25, _TRANS('OUTL_Interface_CapGrowth').." %d%%")--Cap growth to: + gui:AddTip(id, _TRANS('OUTL_HelpTooltip_MaximumPct') )--Set the maximum percentage that an item's price can grow before being filtered + + local _,_,_, hex = GetItemQualityColor(2) + gui:AddControl(id, "Checkbox", 0, 1, "filter.outlier.uncommon.enabled", _TRANS('OUTL_Interface_EnableUnCommon'):format(hex, "|r") )--Enable filtering %s uncommon %s quality items + gui:AddTip(id, _TRANS('OUTL_HelpTooltip_EnableUnCommon') )--Ticking this box will enable outlier filtering on uncommon quality items + gui:AddControl(id, "WideSlider", 0, 1, "filter.outlier.uncommon.level", 100, 5000, 25, _TRANS('OUTL_Interface_CapGrowth').." %d%%")--Cap growth to: + gui:AddTip(id, _TRANS('OUTL_HelpTooltip_MaximumPct') )--Set the maximum percentage that an items price can grow before being filtered + + local _,_,_, hex = GetItemQualityColor(3) + gui:AddControl(id, "Checkbox", 0, 1, "filter.outlier.rare.enabled", _TRANS('OUTL_Interface_EnableRare'):format(hex, "|r") )--Enable filtering %s rare %s quality items + gui:AddTip(id, _TRANS('OUTL_HelpTooltip_EnableRare') )--Ticking this box will enable outlier filtering on rare quality items + gui:AddControl(id, "WideSlider", 0, 1, "filter.outlier.rare.level", 100, 5000, 25, _TRANS('OUTL_Interface_CapGrowth').." %d%%")--Cap growth to: + gui:AddTip(id, _TRANS('OUTL_HelpTooltip_MaximumPct') )--Set the maximum percentage that an items price can grow before being filtered + + local _,_,_, hex = GetItemQualityColor(4) + gui:AddControl(id, "Checkbox", 0, 1, "filter.outlier.epic.enabled", _TRANS('OUTL_Interface_EnableEpic'):format(hex, "|r") )--Enable filtering %s epic %s quality items + gui:AddTip(id, _TRANS('OUTL_HelpTooltip_EnableEpic') )--Ticking this box will enable outlier filtering on epic quality items + gui:AddControl(id, "WideSlider", 0, 1, "filter.outlier.epic.level", 100, 5000, 25, _TRANS('OUTL_Interface_CapGrowth').." %d%%")--Cap growth to: + gui:AddTip(id, _TRANS('OUTL_HelpTooltip_MaximumPct') )--Set the maximum percentage that an items price can grow before being filtered + + local _,_,_, hex = GetItemQualityColor(5) + gui:AddControl(id, "Checkbox", 0, 1, "filter.outlier.legendary.enabled", _TRANS('OUTL_Interface_EnableLegendary'):format(hex, "|r") )--Enable filtering %s legendary %s quality items + gui:AddTip(id, _TRANS('OUTL_HelpTooltip_EnableLegendary') )--Ticking this box will enable outlier filtering on legendary quality items + gui:AddControl(id, "WideSlider", 0, 1, "filter.outlier.legendary.level", 100, 5000, 25, _TRANS('OUTL_Interface_CapGrowth').." %d%%")--Cap growth to: + gui:AddTip(id, _TRANS('OUTL_HelpTooltip_MaximumPct') )--Set the maximum percentage that an items price can grow before being filtered + + local _,_,_, hex = GetItemQualityColor(6) + gui:AddControl(id, "Checkbox", 0, 1, "filter.outlier.artifact.enabled", _TRANS('OUTL_Interface_EnableArtifact'):format(hex, "|r") )--Enable filtering %s artifact %s quality items + gui:AddTip(id, _TRANS('OUTL_HelpTooltip_EnableArtifact') )--Ticking this box will enable outlier filtering on artifact quality items + gui:AddControl(id, "WideSlider", 0, 1, "filter.outlier.artifact.level", 100, 5000, 25, _TRANS('OUTL_Interface_CapGrowth').." %d%%")--Cap growth to: + gui:AddTip(id, _TRANS('OUTL_HelpTooltip_MaximumPct') )--Set the maximum percentage that an items price can grow before being filtered + +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Filter-Outlier/OutlierFilter.lua $", "$Rev: 4840 $") diff --git a/Auc-Advanced/Modules/Auc-Match-Undercut/Auc-Match-Undercut.toc b/Auc-Advanced/Modules/Auc-Match-Undercut/Auc-Match-Undercut.toc new file mode 100644 index 0000000..b7a47d6 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Match-Undercut/Auc-Match-Undercut.toc @@ -0,0 +1,13 @@ +## Title: Auc:Match:Undercut +## Notes: Utility module for Auctioneer that allows you to automatically meet, beat, or exceed the prices set by others when creating auctions at the Auction House, all based upon your chosen settings. +## Interface: 40000 +## Dependancies: Auc-Advanced +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Auc-Match-Undercut.toc 4933 2010-10-13 17:16:14Z Nechckn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## X-Max-Interface: 40000 +## +Embed.xml diff --git a/Auc-Advanced/Modules/Auc-Match-Undercut/Embed.xml b/Auc-Advanced/Modules/Auc-Match-Undercut/Embed.xml new file mode 100644 index 0000000..0731dd4 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Match-Undercut/Embed.xml @@ -0,0 +1,7 @@ + + + + diff --git a/Auc-Advanced/Modules/Auc-Match-Undercut/Undercut.lua b/Auc-Advanced/Modules/Auc-Match-Undercut/Undercut.lua new file mode 100644 index 0000000..5f33dd0 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Match-Undercut/Undercut.lua @@ -0,0 +1,295 @@ +--[[ + Auctioneer - Price Level Utility module + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: Undercut.lua 4840 2010-08-04 21:44:00Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This is an Auctioneer Matcher module that returns an undercut price + based on the current market snapshot + + 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 libType, libName = "Match", "Undercut" +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end + +local print,decode,_,_,replicate,empty,get,set,default,debugPrint,fill, _TRANS = AucAdvanced.GetModuleLocals() +local floor,min,max,ceil = floor,min,max,ceil +local tonumber,tostring = tonumber,tostring +local wipe = wipe + +local GetFaction = AucAdvanced.GetFaction +local QueryImage = AucAdvanced.API.QueryImage +local DecodeLink = AucAdvanced.DecodeLink + +local CONST_BUYOUT = AucAdvanced.Const.BUYOUT +local CONST_COUNT = AucAdvanced.Const.COUNT +local CONST_CURBID = AucAdvanced.Const.CURBID +local CONST_MINBID = AucAdvanced.Const.MINBID +local CONST_SELLER = AucAdvanced.Const.SELLER + +local playerName = UnitName("player") + +local tooltipCache = setmetatable({}, {__mode="v"}) +local matchArrayCache = {} + +function lib.Processor(callbackType, ...) + if (callbackType == "tooltip") then + --Called when the tooltip is being drawn. + private.ProcessTooltip(...) + elseif (callbackType == "config") then + --Called when you should build your Configator tab. + private.SetupConfigGui(...) + elseif (callbackType == "configchanged") then + --Called when your config options (if Configator) have been changed. + private.clearMatchArrayCache() + private.clearTooltipCache() + elseif (callbackType == "scanstats") then + -- AH has been scanned + private.clearMatchArrayCache() + private.clearTooltipCache() + elseif callbackType == "auctionclose" then + private.clearMatchArrayCache() -- this is mostly to conserve RAM, we don't really need to wipe the cache here + end +end + +lib.Processors = {} +function lib.Processors.tooltip(callbackType, ...) + --Called when the tooltip is being drawn. + private.ProcessTooltip(...) +end + +function lib.Processors.config(callbackType, ...) + --Called when you should build your Configator tab. + private.SetupConfigGui(...) +end + +function lib.Processors.configchanged(callbackType, ...) + --Called when your config options (if Configator) have been changed. + private.clearMatchArrayCache() + private.clearTooltipCache() +end + +function lib.Processors.scanstats(callbackType, ...) + -- AH has been scanned + private.clearMatchArrayCache() + private.clearTooltipCache() +end + +function lib.Processors.auctionclose(callbackType, ...) + private.clearMatchArrayCache() -- this is mostly to conserve RAM, we don't really need to wipe the cache here +end + + + +function private.clearMatchArrayCache() -- called from processor + wipe(matchArrayCache) +end +function private.clearTooltipCache() + wipe(tooltipCache) +end + +local query = {itemId = 0, suffix = 0, factor = 0} -- resusable pre-created 3-element table +function lib.GetMatchArray(hyperlink, marketprice, serverKey) + if not get("match.undercut.enable") then return end + local linkType, itemId, suffix, factor = DecodeLink(hyperlink) + if linkType ~= "item" then return end + serverKey = serverKey or GetFaction() + marketprice = marketprice or 0 + + local cacheKey = serverKey .. itemId .."x".. suffix .."x".. factor .."x".. marketprice + if matchArrayCache[cacheKey] then return matchArrayCache[cacheKey] end + + local overmarket = get("match.undermarket.overmarket") + local undermarket = get("match.undermarket.undermarket") + local usevalue = get("match.undercut.usevalue") + local undercut + if usevalue then + undercut = get("match.undercut.value") + else + undercut = get("match.undermarket.undercut") + end + local marketdiff = 0 + local competing = 0 + local matchprice = 0 + local minprice = 0 + local lowest = true + if marketprice > 0 then + matchprice = floor(marketprice*(1+(overmarket/100))) + minprice = ceil(marketprice*(1+(undermarket/100))) + end + + query.itemId = itemId + query.suffix = suffix + query.factor = factor + local data = QueryImage(query, serverKey) + competing = #data + local lowestBidOnly = matchprice + for i = 1, #data do + local item = data[i] + local buyout = item[CONST_BUYOUT] + local count = item[CONST_COUNT] -- should be non-nil and non-zero + if buyout < 1 then + local bid = item[CONST_CURBID] -- should be non-nil + if bid == 0 then bid = item[CONST_MINBID] end -- should be non-nil + bid = bid / count + if bid < lowestBidOnly then lowestBidOnly = bid end -- faster than calling 'min' with 2 parameters + --lowestBidOnly = min(lowestBidOnly, bid/count) + else + buyout = buyout / count + if usevalue then + buyout = buyout - undercut + else + buyout = floor(buyout*((100-undercut)/100)) + end + if buyout <= 0 then + buyout = 1 + end + if (buyout < matchprice) then + if (buyout > minprice) then + if item[CONST_SELLER] ~= playerName then + matchprice = buyout + end + elseif (buyout > 0) then + lowest = false + end + end + end + end + if (marketprice > 0) then + marketdiff = floor((matchprice - marketprice) /marketprice *100 +0.5) + else + marketdiff = 0 + end + local matchArray = {} + matchArray.value = matchprice + if lowest then + if matchprice<=lowestBidOnly then + matchArray.returnstring = _TRANS('UCUT_Interface_UndercutLowestPrice'):format(marketdiff, "\n")--Undercut: %% change: %s%s Undercut: Lowest Price + else + matchArray.returnstring = _TRANS('UCUT_Interface_UndercutLowestBid'):format(marketdiff, "\n")--Undercut: %% change: %s%s Undercut: Lower bid-only auctions + end + else + matchArray.returnstring = _TRANS('UCUT_Interface_UndercutNoMatch'):format(marketdiff, "\n")--Undercut: %% change: %s%s Undercut: Can not match lowest price') + end + matchArray.competing = competing + matchArray.diff = marketdiff + matchArrayCache[cacheKey] = matchArray + return matchArray +end + +function private.ProcessTooltip(tooltip, name, link, quality, quantity, cost, additional) + if not link then return end + if not get("match.undercut.tooltip") then return end + local model = get("match.undercut.model") + if not model then return end + local market + + local matcharray = tooltipCache[link] + if matcharray then + market = matcharray.market + else + if model == "market" then + market = AucAdvanced.API.GetMarketValue(link) + else + market = AucAdvanced.API.GetAlgorithmValue(model, link) + end + if not market then return end + matcharray = lib.GetMatchArray(link, market) + if not matcharray then return end + matcharray = replicate(matcharray) + matcharray.market = market + tooltipCache[link] = matcharray + end + if not matcharray.value or matcharray.value <= 0 then return end + + tooltip:SetColor(0.3, 0.9, 0.8) + + if matcharray.competing == 0 then + tooltip:AddLine(_TRANS('UCUT_Tooltip_NoCompetition'):format("|cff00ff00") )--Undercut: %s No competition + elseif matcharray.returnstring:find("Can not match") then + tooltip:AddLine(_TRANS('UCUT_Tooltip_CannotUndercut'):format("|cffff0000") )--Undercut: %s Cannot Undercut + elseif matcharray.returnstring:find("Lowest") then + if matcharray.value >= market then + tooltip:AddLine(_TRANS('UCUT_Tooltip_CompetitionAbove'):format("|cff40ff00") )--Undercut: %s Competition Above market + else + tooltip:AddLine(_TRANS('UCUT_Tooltip_Undercutting'):format("|cfffff000") )--Undercut: %s Undercutting competition + end + end + tooltip:AddLine(" ".._TRANS('UCUT_Tooltip_MovingPrice').." "..tostring(matcharray.diff).."%:", matcharray.value)--Moving price +end + +function lib.OnLoad() + default("match.undercut.enable", true) + default("match.undermarket.undermarket", -20) + default("match.undermarket.overmarket", 10) + default("match.undermarket.undercut", 1) + default("match.undercut.tooltip", true) + default("match.undercut.model", "market") + default("match.undercut.usevalue", false) + default("match.undercut.value", 1) +end + +--[[ Local functions ]]-- + +function private.SetupConfigGui(gui) + -- The defaults for the following settings are set in the lib.OnLoad function + local id = gui:AddTab(libName, libType.." Modules") + --gui:MakeScrollable(id) + + gui:AddHelp(id, "what undercut module", + _TRANS('UCUT_Help_WhatUndercut') ,--What is this undercut module? + _TRANS('UCUT_Help_WhatUndercutAnswer') )--The undercut module allows you to undercut the lowest price of all currently available items, based on your settings. \n\n It is recommended to have undercut run after any other matcher modules. + + gui:AddControl(id, "Header", 0, _TRANS('UCUT_Interface_UndercutOptions') )--Undercut options + + gui:AddControl(id, "Subhead", 0, _TRANS('UCUT_Interface_CompetitionMatching') )--Competition Matching + + gui:AddControl(id, "Checkbox", 0, 1, "match.undercut.enable", _TRANS('UCUT_Interface_EnableUndercut') )--Enable Auc-Match-Undercut + gui:AddTip(id, _TRANS('UCUT_HelpTooltip_EnableUndercut') )--Enable this module's functions. + + gui:AddControl(id, "WideSlider", 0, 1, "match.undermarket.undermarket", -100, 0, 1, _TRANS('UCUT_Interface_MaxMarkdown').." %d%%")--Max under market price (markdown): + gui:AddTip(id, _TRANS('UCUT_HelpTooltip_MaxMarkdown') )--This controls how much below the market price you are willing to undercut before giving up. \n If Auctioneer cannot beat the lowest price, it will undercut the lowest price it can. + + gui:AddControl(id, "WideSlider", 0, 1, "match.undermarket.overmarket", 0, 100, 1, _TRANS('UCUT_Interface_MaxMarkup').." %d%%")--Max over market price (markup): + gui:AddTip(id, _TRANS('UCUT_HelpTooltip_MaxMarkup') )--This controls how much above the market price you are willing to mark up. If there is no competition, or the competition is marked up higher than this value Auctioneer will set the price to this value above market. + + gui:AddControl(id, "Slider", 0, 1, "match.undermarket.undercut", 0, 20, 0.1, _TRANS('UCUT_Interface_UndercutMinimum').." %g%%")--Undercut: + gui:AddTip(id, _TRANS('UCUT_HelpTooltip_UndercutMinimum') )--This controls the minimum undercut. Auctioneer will try to undercut the competition by this amount + + gui:AddControl(id, "Checkbox", 0, 1, "match.undercut.usevalue", _TRANS('UCUT_Interface_UndercutAmount') )--Specify undercut amount by coin value + gui:AddTip(id, _TRANS('UCUT_HelpTooltip_UndercutAmount') )--Specify the amount to undercut by a specific amount, instead of by a percentage + + gui:AddControl(id, "MoneyFramePinned", 0, 2, "match.undercut.value", 1, 99999999, _TRANS('UCUT_Interface_CurrentValue') )--Undercut Amount + gui:AddControl(id, "Subhead", 0, _TRANS('UCUT_Interface_UndercutTooltipSettings') )--Tooltip Setting + gui:AddControl(id, "Checkbox", 0, 1, "match.undercut.tooltip",_TRANS('UCUT_Interface_ShowInTooltip') )--Show undercut status in tooltip + gui:AddTip(id, _TRANS('UCUT_HelpTooltip_ShowInTooltip') )--Add a line to the tooltip showing whether the current competition is undercuttable + gui:AddControl(id, "Note", 0, 2, 500, 15, _TRANS('UCUT_Interface_TooltipValuationMethod') )--Tooltip price valuation method + gui:AddControl(id, "Selectbox", 0, 2, parent.selectorPriceModels, "match.undercut.model", _TRANS('UCUT_Interface_PricingModel') )--Pricing model to use + gui:AddTip(id, _TRANS('UCUT_HelpTooltip_PricingModel'))--The pricing model to use to compare the competition against. Should be set to the model most often used for posting. --Note: this is ONLY for basing the tooltip on, nothing else + +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Match-Undercut/Undercut.lua $", "$Rev: 4840 $") diff --git a/Auc-Advanced/Modules/Auc-Stat-Sales/Auc-Stat-Sales.toc b/Auc-Advanced/Modules/Auc-Stat-Sales/Auc-Stat-Sales.toc new file mode 100644 index 0000000..93cf698 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Stat-Sales/Auc-Stat-Sales.toc @@ -0,0 +1,14 @@ +## Title: Auc:Stat:Sales Prices +## Notes: Statistics extension for Auctioneer that bases its pricing recommendations on the sales history contained within BeanCounter. +## +## Interface: 40000 +## Dependancies: Auc-Advanced BeanCounter +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Auc-Stat-Sales.toc 4933 2010-10-13 17:16:14Z Nechckn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## X-Max-Interface: 40000 +## +Embed.xml diff --git a/Auc-Advanced/Modules/Auc-Stat-Sales/BeanCount.lua b/Auc-Advanced/Modules/Auc-Stat-Sales/BeanCount.lua new file mode 100644 index 0000000..a4b1419 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Stat-Sales/BeanCount.lua @@ -0,0 +1,468 @@ +--[[ + Auctioneer - Stat-Sales module + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: BeanCount.lua 4840 2010-08-04 21:44:00Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This Auctioneer statistic module calculates a price statistics for items + based on the price that you specifically have sold this item for in the + past, based on your BeanCounter history information. + + 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 libType, libName = "Stat", "Sales" +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end +local print,decode,_,_,replicate,_,get,set,default,debugPrint,fill, _TRANS = AucAdvanced.GetModuleLocals() + +local unpack,pairs,wipe = unpack,pairs,wipe +local floor,abs,sqrt = floor,abs,sqrt +local strmatch = strmatch + +local GetSigFromLink = AucAdvanced.API.GetSigFromLink +local GetFaction = AucAdvanced.GetFaction + +local pricecache = setmetatable({}, {__mode="v"}) +-- don't recalc time every query, that would be ridiculous +local currenttime = time() +local day3time = currenttime - 3*86400 +local day7time = currenttime - 7*86400 +--local name for our saved var file +local SalesDB + +function private.onEvent(frame, event, arg, ...) + if (event == "MAIL_CLOSED") then + -- Clear pricecache + wipe(pricecache) + end +end + +local BellCurve = AucAdvanced.API.GenerateBellCurve(); +----------------------------------------------------------------------------------- +-- The PDF for standard deviation data, standard bell curve +----------------------------------------------------------------------------------- +function lib.GetItemPDF(hyperlink, serverKey) + if not get("stat.sales.enable") then return end + -- Get the data + local average, mean, stddev, variance, confidence, bought, sold, boughtqty, soldqty, boughtseen, soldseen, bought3, sold3, boughtqty3, soldqty3, bought7, sold7, boughtqty7, soldqty7 = lib.GetPrice(hyperlink, serverKey) + + -- If the standard deviation is zero, we'll have some issues, so we'll estimate it by saying + -- the std dev is 100% of the mean divided by square root of number of views + if stddev == 0 then stddev = mean / sqrt(soldqty); end + + if not (mean and stddev) or mean == 0 or stddev == 0 then + return nil; -- No data, cannot determine pricing + end + + local lower, upper = mean - 3 * stddev, mean + 3 * stddev; + + -- Build the PDF based on standard deviation & mean + BellCurve:SetParameters(mean, stddev); + return BellCurve, lower, upper; -- This has a __call metamethod so it's ok +end + +----------------------------------------------------------------------------------- +local ZValues = {.063, .126, .189, .253, .319, .385, .454, .525, .598, .675, .756, .842, .935, 1.037, 1.151, 1.282, 1.441, 1.646, 1.962, 20, 20000} +function private.GetCfromZ(Z) + --C = 0.05*i + if (not Z) then + return .05 + end + if (Z > 10) then + return .99 + end + local i = 1 + while Z > ZValues[i] do + i = i + 1 + end + if i == 1 then + return .05 + else + i = i - 1 + ((Z - ZValues[i-1]) / (ZValues[i] - ZValues[i-1])) + return i*0.05 + end +end + +do + local keycache = {} + local faction2selectbox = {["Horde"]={"1","horde"}, ["Alliance"]={"1","alliance"}, ["Neutral"]={"1","neutral"}} + local basesettings = { + ["bid"] =true, + ["auction"] = true, + ["exact"] = true, + ["servers"] = {}, + } + function private.GetBCSearchSettings(serverKey) + local keysettings = keycache[serverKey] + if not keysettings then + -- only split each unique serverKey once, and cache the results + local realmName, factionName = AucAdvanced.SplitServerKey(serverKey) + local sbox = faction2selectbox[factionName] + if not sbox then -- Invalid faction + return + end + keysettings = {sbox, realmName} + keycache[serverKey] = keysettings + end + basesettings["selectbox"] = keysettings[1] + basesettings.servers[1] = keysettings[2] + return basesettings + end +end + +local Rsn_Success, Rsn_WonBid, Rsn_WonBuy +function lib.GetPrice(hyperlink, serverKey) + if not get("stat.sales.enable") then return end + if not Rsn_Success then + if not (BeanCounter) or not (BeanCounter.API) or not (BeanCounter.API.isLoaded) or not (BeanCounter.getLocals) then return false end + local _, _, _, _, _BC = BeanCounter.getLocals() + -- cache BeanCounter translations so we don't have to look them up every time + Rsn_Success = _BC('UiAucSuccessful') + Rsn_WonBid = _BC('UiWononBid') + Rsn_WonBuy = _BC('UiWononBuyout') + end + + local sig = GetSigFromLink(hyperlink) + if not sig then return end + serverKey = serverKey or GetFaction() + local cachesig = serverKey..sig + local cached = pricecache[cachesig] + if cached == false then + return + elseif cached then + return unpack(cached) + end + local settings = private.GetBCSearchSettings(serverKey) + if not settings then return end + + local tbl = BeanCounter.API.search(hyperlink, settings, true, 99999) + local bought, sold, boughtseen, soldseen, boughtqty, soldqty, bought3, sold3, boughtqty3, soldqty3, bought7, sold7, boughtqty7, soldqty7 = 0,0,0,0,0,0,0,0,0,0,0,0,0,0 + local reason, qty, priceper, thistime + --check for ignore date for current sig or serverKey or all servers + local ignoreDate = SalesDB[cachesig] or SalesDB[serverKey] or SalesDB.ALL or 0 + --[[ + The test for ["ALL"..sig] won't work like this; temporarily removed as it's not used for any Auctioneer functions + local ignoreDate = SalesDB[cachesig] or SalesDB["ALL"..sig] or SalesDB[serverKey] or SalesDB.ALL or 0 + --]] + + if tbl then + for i,v in pairs(tbl) do + -- local itemLink, reason, bid, buy, net, qty, priceper, seller, deposit, fee, wealth, date = v + -- true price per = (net+fee-deposit)/Qty + --1 [Void Crystal] + --2 Won on Buyout + --3 1650000 + --4 1650000 + --5 0 + --6 20 + --7 82500 + --8 Yyzer + --9 0 + --10 0 + --11 10387318 + --12 1198401769 + reason, qty, priceper, thistime = v[2], v[6], v[7], v[12] or 1 + if priceper and qty and priceper>0 and qty>0 and thistime > ignoreDate then + if reason == Rsn_WonBuy or reason == Rsn_WonBid then + boughtqty = boughtqty + qty + bought = bought + priceper*qty + boughtseen = boughtseen + 1 + if thistime >= day3time then + boughtqty3 = boughtqty3 + qty + bought3 = bought3 + priceper*qty + end + if thistime >= day7time then + boughtqty7 = boughtqty7 + qty + bought7 = bought7 + priceper*qty + end + elseif reason == Rsn_Success then + soldqty = soldqty + qty + sold = sold + priceper*qty + soldseen = soldseen + 1 + if thistime >= day3time then + soldqty3 = soldqty3 + qty + sold3 = sold3 + priceper*qty + end + if thistime >= day7time then + soldqty7 = soldqty7 + qty + sold7 = sold7 + priceper*qty + end + end + end + end + if boughtqty>0 then bought = bought / boughtqty end + if soldqty>0 then sold = sold / soldqty end + if boughtqty3>0 then bought3 = bought3 / boughtqty3 end + if soldqty3>0 then sold3 = sold3 / soldqty3 end + if boughtqty7>0 then bought7 = bought7 / boughtqty7 end + if soldqty7>0 then sold7 = sold7 / soldqty7 end + end + if (not sold or sold==0) and (not bought or bought==0) then pricecache[cachesig]=false; return end + -- Start StdDev calculations + local mean = sold + -- Calculate Variance + local variance = 0 + local count = 0 + + for i,v in pairs(tbl) do -- We do multiple passes, but creating a slimmer table would be more memory manipulation and not necessarily faster + reason, qty, priceper, thistime = v[2], v[6], v[7], v[12] or 1 + if priceper and qty and priceper>0 and qty>0 and reason == Rsn_Success and thistime>ignoreDate then + variance = variance + ((mean - priceper) ^ 2); + count = count + 1 + end + end + variance = variance / count; + local stdev = variance ^ 0.5 + + local deviation = 1.5 * stdev + + -- Trim down to those within 1.5 stddev + local number = 0 + local total = 0 + for i,v in pairs(tbl) do -- We do multiple passes, but creating a slimmer table would be more memory manipulation and not necessarily faster + reason, qty, priceper, thistime = v[2], v[6], v[7], v[12] or 1 + if priceper and qty and priceper>0 and qty>0 and reason == Rsn_Success and thistime>ignoreDate then + if (abs(priceper - mean) < deviation) then + total = total + priceper * qty + number = number + qty + end + end + end + + local confidence = .01 + + local average + if (number > 0) then + average = total / number + confidence = (.15*average)*(number^0.5)/(stdev) + confidence = private.GetCfromZ(confidence) + end + + pricecache[cachesig] = {average, mean, stdev, variance, confidence, bought, sold, boughtqty, soldqty, boughtseen, soldseen, bought3, sold3, boughtqty3, soldqty3, bought7, sold7, boughtqty7, soldqty7} + return average, mean, stdev, variance, confidence, bought, sold, boughtqty, soldqty, boughtseen, soldseen, bought3, sold3, boughtqty3, soldqty3, bought7, sold7, boughtqty7, soldqty7 +end + +function lib.GetPriceColumns() + if not (BeanCounter) or not (BeanCounter.API) or not (BeanCounter.API.isLoaded) then return end + return "Average", "Mean","Std Deviation", "Variance", "Confidence", "Bought Price", "Sold Price", "Bought Quantity", "Sold Quantity", "Bought Times", "Sold Times", "3day Bought Price", "3day Sold Price", "3day Bought Quantity", "3day Sold Quantity", "7day Bought Price", "7day Sold Price", "7day Bought Quantity", "7day Sold Quantity" +end + +local array = {} +function lib.GetPriceArray(hyperlink, serverKey) + if not get("stat.sales.enable") then return end + if not (BeanCounter) or not (BeanCounter.API) or not (BeanCounter.API.isLoaded) then return end + -- no need to clean out array; we will just overwrite all entries with new values + + -- Get our statistics + local average, mean, stdev, variance, confidence, bought, sold, boughtqty, soldqty, boughtseen, soldseen, bought3, sold3, boughtqty3, soldqty3, bought7, sold7, boughtqty7, soldqty7 = lib.GetPrice(hyperlink, serverKey) + if not bought and not sold then return end + -- These are the ones that most algorithms will look for + array.price = average or mean + array.confidence = confidence + -- This is additional data + array.normalized = average + array.mean = mean + array.deviation = stdev + array.variance = variance + + array.boughtseen = boughtseen + array.soldseen = soldseen + array.bought = bought + array.sold = sold + array.boughtqty = boughtqty + array.soldqty = soldqty + array.seen = boughtseen + if soldseen then array.seen = array.seen+soldseen end + array.bought3 = bought3 + array.sold3 = sold3 + array.boughtqty3 = boughtqty3 + array.soldqty3 = soldqty3 + array.bought7 = bought7 + array.sold7 = sold7 + array.boughtqty7 = boughtqty7 + array.soldqty7 = soldqty7 + + return array +end + +--[[ + Calling ClearItem or ClearData does not remove anything from the BeanCounter database. + Instead we record the time, and GetPrice will ignore entries with timestamps earlier than that time +--]] +function lib.ClearItem(hyperlink, serverKey) + print(_TRANS('ASAL_Interface_SlashHelpClearingData') )-- Sales does not store data itself. It uses your Beancounter data. BeanCounter data before todays date will be ignored. + local sig = GetSigFromLink(hyperlink) + if not sig then return end + serverKey = serverKey or GetFaction() + sig = serverKey..sig + SalesDB[sig] = time() + wipe(pricecache) +end + +function lib.ClearData(serverKey) + serverKey = serverKey or GetFaction() + if AucAdvanced.API.IsKeyword(serverKey, "ALL") then + -- "ALL" overrides all pre-existing entries in the table. Eliminate those "dead" entries. + wipe(SalesDB) + SalesDB.ALL = time() + print(_TRANS('ASAL_Interface_SlashHelpClearingData').." {{".._TRANS("ADV_Interface_AllRealms").."}}.")-- Sales does not store data itself. It uses your Beancounter data. BeanCounter data before todays date will be ignored. + elseif AucAdvanced.SplitServerKey(serverKey) then -- looks like a valid serverKey + -- Any pre-existing entries *containing* this serverKey are overridden by the new entry for this serverKey; remove them + for key, value in pairs(SalesDB) do + if key:find(serverKey, 1, true) then -- plain text matching + SalesDB[key] = nil + end + end + SalesDB[serverKey] = time() + local _,_,keyText = AucAdvanced.SplitServerKey(serverKey) + print(_TRANS('ASAL_Interface_SlashHelpClearingData').." {{"..keyText.."}}.")-- Sales does not store data itself. It uses your Beancounter data. BeanCounter data before todays date will be ignored. + end + wipe(pricecache) +end + +function lib.OnLoad() + default("stat.sales.tooltip", false) + default("stat.sales.avg", true) + default("stat.sales.avg3", false) + default("stat.sales.avg7", false) + default("stat.sales.normal", true) + default("stat.sales.stddev", false) + default("stat.sales.confidence", false) + default("stat.sales.enable", true) + + SalesDB = get("stat.sales.ignoredsigs") + if not SalesDB then + SalesDB = {} + set("stat.sales.ignoredsigs", SalesDB) + end +end + + +lib.Processors = {} + +function lib.Processors.config(callbackType, ...) + if private.SetupConfigGui then private.SetupConfigGui(...) end +end + +function lib.Processors.tooltip(callbackType, tooltip, name, hyperlink, quality, quantity, cost) + if not get("stat.sales.tooltip") or not (BeanCounter) or not (BeanCounter.API) or not (BeanCounter.API.isLoaded) then return end --If beancounter disabled itself, boughtseen etc are nil and throw errors + + local average, mean, stdev, variance, confidence, bought, sold, boughtqty, soldqty, boughtseen, soldseen, bought3, sold3, boughtqty3, soldqty3, bought7, sold7, boughtqty7, soldqty7 = lib.GetPrice(hyperlink) + if not bought and not sold then return end + if (boughtseen+soldseen>0) then + tooltip:AddLine(_TRANS('ASAL_Tooltip_SalesPrices'))--Sales prices: + + if get("stat.sales.avg") then + if (boughtseen > 0) then + tooltip:AddLine(" ".._TRANS('ASAL_Tooltip_TotalBought'):format(boughtqty), bought) --Total Bought {{%s}} at avg each + end + if (soldseen > 0) then + tooltip:AddLine(" ".._TRANS('ASAL_Tooltip_TotalSold'):format(soldqty), sold) --Total Sold {{%s}} at avg each + end + end + if get("stat.sales.avg7") then + if (boughtqty7 > 0) then + tooltip:AddLine(" ".._TRANS('ASAL_Tooltip_7DaysBought'):format(boughtqty7), bought7) --7 Days Bought {{%s}} at avg each + end + if (soldqty7 > 0) then + tooltip:AddLine(" ".._TRANS('ASAL_Tooltip_7DaysSold'):format(soldqty7), sold7)--7 Days Sold {{%s}} at avg each + end + end + if get("stat.sales.avg3") then + if (boughtqty3 > 0) then + tooltip:AddLine(" ".._TRANS('ASAL_Tooltip_3DaysBought'):format(boughtqty3), bought3)--3 Days Bought {{%s}} at avg each + end + if (soldqty3 > 0) then + tooltip:AddLine(" ".._TRANS('ASAL_Tooltip_3DaysSold'):format(soldqty3), sold3)--3 Days Sold {{%s}} at avg each + end + end + if (average and average > 0) then + if get("stat.sales.normal") then + tooltip:AddLine(" ".._TRANS('ASAL_Tooltip_NormalizedStack'), average*quantity)--Normalized (stack) + if (quantity > 1) then + tooltip:AddLine(" ".._TRANS('ASAL_Tooltip_Individually'), average)--(or individually) + end + end + if get("stat.sales.stdev") then + tooltip:AddLine(" ".._TRANS('ASAL_Tooltip_ StdDeviation'), stdev*quantity)--Std Deviation + if (quantity > 1) then + tooltip:AddLine(" ".._TRANS('ASAL_Tooltip_Individually'), stdev)--(or individually) + end + end + if get("stat.sales.confid") then + tooltip:AddLine(" ".._TRANS('ASAL_Tooltip_Confidence')..(floor(confidence*1000))/1000)--Confidence: + end + end + + end +end + +function private.SetupConfigGui(gui) + private.SetupConfigGui = nil + local id = gui:AddTab(lib.libName, lib.libType.." Modules") + + gui:AddHelp(id, "what sales stats", + _TRANS('ASAL_Help_StatSales') ,--What are sales stats?' + _TRANS('ASAL_Help_StatSalesAnswer') )--Sales stats are the numbers that are generated by the sales module from the BeanCounter database. It averages all of the prices for items that you have sold + + --all options in here will be duplicated in the tooltip frame + function private.addTooltipControls(id) + gui:AddControl(id, "Header", 0, _TRANS('ASAL_Interface_SalesOptions') )--Sales options + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + + gui:AddControl(id, "Checkbox", 0, 1, "stat.sales.enable", _TRANS('ASAL_Interface_EnableSalesStats') )--Enable Sales Stats + gui:AddTip(id, _TRANS('ASAL_HelpTooltip_EnableSalesStats') )--Allow Sales to contribute to Market Price. + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + + gui:AddControl(id, "Checkbox", 0, 4, "stat.sales.tooltip", _TRANS('ASAL_Interface_ShowSalesStat') )--Show sales stats in the tooltips? + gui:AddTip(id, _TRANS('ASAL_HelpTooltip_ShowSalesStat') )--Toggle display of stats from the Sales module on or off + gui:AddControl(id, "Checkbox", 0, 6, "stat.sales.avg3", _TRANS('ASAL_Interface_Display3DayMean') )--Display Moving 3 Day Mean + gui:AddTip(id, _TRANS('ASAL_HelpTooltip_Display3DayMean') )--Toggle display of 3-Day mean from the Sales module on or off + gui:AddControl(id, "Checkbox", 0, 6, "stat.sales.avg7", _TRANS('ASAL_Interface_Display7DayMean') )--Display Moving 7 Day Mean + gui:AddTip(id, _TRANS('ASAL_HelpTooltip_Display7DayMean') )--Toggle display of 7-Day mean from the Sales module on or off + gui:AddControl(id, "Checkbox", 0, 6, "stat.sales.avg", _TRANS('ASAL_Interface_DisplayOverallMean') )--Display Overall Mean + gui:AddTip(id, _TRANS('ASAL_HelpTooltip_DisplayOverallMean') )--Toggle display of Permanent mean from the Sales module on or off + gui:AddControl(id, "Checkbox", 0, 6, "stat.sales.normal", _TRANS('ASAL_Interface_DisplayNormalized') )--Display Normalized + gui:AddTip(id, _TRANS('ASAL_HelpTooltip_DisplayNormalized') )--Toggle display of \'Normalized\' calculation in tooltips on or off + gui:AddControl(id, "Checkbox", 0, 6, "stat.sales.stdev", _TRANS('ASAL_Interface_DisplayStdDeviation') )--Display Standard Deviation + gui:AddTip(id, _TRANS('ASAL_HelpTooltip_DisplayStdDeviation') )--Toggle display of \'Standard Deviation\' calculation in tooltips on or off + gui:AddControl(id, "Checkbox", 0, 6, "stat.sales.confid", _TRANS('ASAL_Interface_DisplayConfidence') )--Display Confidence + gui:AddTip(id,_TRANS('ASAL_HelpTooltip_DisplayConfidence') )--Toggle display of \'Confidence\' calculation in tooltips on or off + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + end + --This is the Tooltip tab provided by aucadvnced so all tooltip configuration is in one place + local tooltipID = AucAdvanced.Settings.Gui.tooltipID + + --now we create a duplicate of these in the tooltip frame + private.addTooltipControls(id) + if tooltipID then private.addTooltipControls(tooltipID) end +end + +--[[Bootstrap Code]] +private.scriptframe = CreateFrame("Frame") +private.scriptframe:SetScript("OnEvent", private.onEvent) + + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Stat-Sales/BeanCount.lua $", "$Rev: 4840 $") diff --git a/Auc-Advanced/Modules/Auc-Stat-Sales/Embed.xml b/Auc-Advanced/Modules/Auc-Stat-Sales/Embed.xml new file mode 100644 index 0000000..68fd8b7 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Stat-Sales/Embed.xml @@ -0,0 +1,7 @@ + + + + diff --git a/Auc-Advanced/Modules/Auc-Stat-WOWEcon/Auc-Stat-WOWEcon.toc b/Auc-Advanced/Modules/Auc-Stat-WOWEcon/Auc-Stat-WOWEcon.toc new file mode 100644 index 0000000..06a4130 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Stat-WOWEcon/Auc-Stat-WOWEcon.toc @@ -0,0 +1,12 @@ +## Title: Auc:Stat:WOWEcon Prices +## Interface: 40000 +## Dependancies: Auc-Advanced +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Auc-Stat-WOWEcon.toc 4933 2010-10-13 17:16:14Z Nechckn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## X-Max-Interface: 40000 +## +Embed.xml diff --git a/Auc-Advanced/Modules/Auc-Stat-WOWEcon/Embed.xml b/Auc-Advanced/Modules/Auc-Stat-WOWEcon/Embed.xml new file mode 100644 index 0000000..b76b040 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Stat-WOWEcon/Embed.xml @@ -0,0 +1,7 @@ + + + + diff --git a/Auc-Advanced/Modules/Auc-Stat-WOWEcon/WOWEcon.lua b/Auc-Advanced/Modules/Auc-Stat-WOWEcon/WOWEcon.lua new file mode 100644 index 0000000..4865cb3 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Stat-WOWEcon/WOWEcon.lua @@ -0,0 +1,243 @@ +--[[ + Auctioneer - WoWEcon price statistics module + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: WOWEcon.lua 4840 2010-08-04 21:44:00Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This is an Auctioneer statistic module that returns a price based on + any WoWEcon data you have. You must have the WoWEcon addon installed + for this statistic to have any affect. + + 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 libType, libName = "Stat", "WOWEcon" +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end +local print,decode,_,_,replicate,empty,get,set,default,debugPrint,fill, _TRANS = AucAdvanced.GetModuleLocals() + +function private.Sanitize(hyperlink) + local lType, id, suffix, factor, enchant, seed = decode(hyperlink) + if lType == "item" then + local newbit, newlink + if AucAdvanced.Settings.GetSetting("stat.wowecon.sanitize") then + -- If the settings say to sanitize this item, them remove all the + -- specificness from the hyperlink before sending it in. + newbit = ("|Hitem:%d:%d:%d:%d:%d:%d:%d:%d|h"):format(id,0,0,0,0,0,suffix,factor) + newlink = hyperlink:gsub("|Hitem:[%d%p:]+|h", newbit) + else + -- Only remove the random seed component from the link, leave the factor + newlink = hyperlink:gsub("(|Hitem:[%d%p:]+):[%p%d]+|h", "%1:"..factor.."|h") + end + assert(newlink, "Link sanitization failed") + return newlink + end + return hyperlink +end + +function lib.GetPrice(hyperlink, faction, realm) + if not AucAdvanced.Settings.GetSetting("stat.wowecon.enable") then return end + if not (Wowecon and Wowecon.API) then return end + hyperlink = private.Sanitize(hyperlink) + + local price,seen,specific = Wowecon.API.GetAuctionPrice_ByLink(hyperlink) + if specific and AucAdvanced.Settings.GetSetting("stat.wowecon.useglobal") then + price,seen = Wowecon.API.GetAuctionPrice_ByLink(hyperlink, Wowecon.API.GLOBAL_PRICE) + end + return price, false, seen, specific +end + +function lib.GetPriceColumns() + if not (Wowecon and Wowecon.API) then return end + return "WOWEcon Price", false, "WOWEcon Seen" +end + +local array = {} +function lib.GetPriceArray(hyperlink, faction, realm) + if not AucAdvanced.Settings.GetSetting("stat.wowecon.enable") then return end + if not (Wowecon and Wowecon.API) then return end + + --Remove trailing :80 from item link, WoWEcon doesn't expect it and can't handle it. + hyperlink = string.gsub(hyperlink, "(|Hitem:[^:]+:[^:]+:[^:]+:[^:]+:[^:]+:[^:]+:[^:]+:[^:]+):%d+(|h)", "%1%2") + + array.hyperlink = hyperlink + hyperlink = private.Sanitize(hyperlink) + array.sanitized = hyperlink + + -- Get our statistics + local price,seen,specific = Wowecon.API.GetAuctionPrice_ByLink(hyperlink) + + array.price = price + array.seen = seen or 0 + array.specific = specific + + if (specific) then + array.s_price = price + array.s_seen = seen + price,seen = Wowecon.API.GetAuctionPrice_ByLink(hyperlink, Wowecon.API.GLOBAL_PRICE) + else + array.s_price = nil + array.s_seen = 0 + end + array.g_price = price + array.g_seen = seen or 0 + + if AucAdvanced.Settings.GetSetting("stat.wowecon.useglobal") then + array.price = array.g_price + array.seen = array.g_seen + array.specific = false + end + + return array +end + +function lib.IsValidAlgorithm() + if not (Wowecon and Wowecon.API) then return false end + return true +end + +function lib.CanSupplyMarket() + if not (Wowecon and Wowecon.API) then return false end + return true +end + +function lib.Processor(callbackType, ...) + if (callbackType == "config") then + --Called when you should build your Configator tab. + private.SetupConfigGui(...) + elseif (callbackType == "load") then + lib.OnLoad(...) + elseif (callbackType == "tooltip") then + lib.ProcessTooltip(...) + end +end + +lib.Processors = {} +function lib.Processors.config(callbackType, ...) + --Called when you should build your Configator tab. + private.SetupConfigGui(...) +end + +function lib.Processors.load(callbackType, ...) + lib.OnLoad(...) +end + +function lib.Processors.tooltip(callbackType, ...) + lib.ProcessTooltip(...) +end + + + +function private.SetupConfigGui(gui) + local id = gui:AddTab(lib.libName, lib.libType.." Modules") + + gui:AddHelp(id, "what global price", + _TRANS('WECN_Help_WhatGlobalPrices') ,--What are global prices? + _TRANS('WECN_Help_WhatGlobalPricesAnswer')--Wowecon provides two different types of prices: a global price, averaged across all servers, and a server specific price, for just your server and faction. + ) + + gui:AddHelp(id, "why use global", + _TRANS('WECN_Help_WhyGlobalPrices') ,--Why should I use global prices? + _TRANS('WECN_Help_WhyGlobalPricesAnswer') --Server specific prices can be useful if your server has prices which are far removed from the average, but often these prices are based on many fewer data points, causing your server specific price to possibly get out of whack for some items. This option lets you force the Wowecon stat to always use global prices, if you\'d prefer. + ) + + gui:AddHelp(id, "prices dont match", + _TRANS('WECN_Help_WoweconNoMatch') ,--The Wowecon price used by Appraiser doesn't match the Wowecon tooltip. What gives? + _TRANS('WECN_Help_WoweconNoMatchAnswer') --Wowecon gives you the option to hide server specific prices if seen fewer than a given number of times. Even though these prices are hidden from the tooltip, they are still reported to Appraiser. If you are not using the global price option here, you should check to make sure there isn\'t a hidden server specific price for your server, with just a small number of seen times. + ) + + gui:AddHelp(id, "sanitize link", + _TRANS('WECN_Help_WhatSanitize') ,--What does the sanitize link option do? + _TRANS('WECN_Help_WhatSanitizeAnswer') --Sanitizing the link can improve the price data you receive from WOWEcon by removing the parts of the link that are very specific (such as enchants, item factors, and gem informatio) to just get the price information for the common base item. This will generally only affect items that are slightly different from the normal base item, and have no, or very little price data due to their uniqueness. + ) + + gui:AddHelp(id, "show price tooltip", + _TRANS('WECN_Help_WhyWOWEcon') ,--Why would I want to show the WOWEcon price in the tooltip? + _TRANS('WECN_Help_WhyWOWEconAnswer') --The pricing data that Appraiser uses for the items may be different to the price data that WOWEcon displays by default, since WOWEcon can get very specific with the data that it returns. Enabling this option will let you see the exact price that this module is reporting for the current item. + ) + + --all options in here will be duplicated in the tooltip frame + function private.addTooltipControls(id) + gui:AddControl(id, "Header", 0, _TRANS('WECN_Interface_WOWEconOptions') )--WOWEcon options + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + + gui:AddControl(id, "Checkbox", 0, 1, "stat.wowecon.enable", _TRANS('WECN_Interface_EnableWOWEconStats') )--Enable WOWEcon Stats + gui:AddTip(id, _TRANS('WECN_HelpTooltip_EnableWOWEconStats') )--Allow WOWEcon to gather and return price data + + gui:AddControl(id, "Checkbox", 0, 1, "stat.wowecon.useglobal", _TRANS('WECN_Interface_AlwaysGlobalPrice') )--Always use global price, not server price + gui:AddTip(id, _TRANS('WECN_HelpTooltip_AlwaysGlobalPrice') )--Toggle use of server specific Wowecon price stats, if they exist + gui:AddControl(id, "Checkbox", 0, 1, "stat.wowecon.sanitize", _TRANS('WECN_Interface_SanitizeWOWEcon') )--Sanitize links before sending to WOWEcon API + gui:AddTip(id, _TRANS('WECN_HelpTooltip_SanitizeWOWEcon') )--Removes ultra-specific item data from links before issuing the price request + gui:AddControl(id, "Checkbox", 0, 1, "stat.wowecon.tooltip", _TRANS('WECN_Interface_ShowWOWEconTooltip') )--Show WOWEcon value in tooltip (see note) + gui:AddTip(id, _TRANS('WECN_HelpTooltip_ShowWOWEconTooltip') )--Note: WOWEcon already shows this by default, this may produce redundant information in your tooltip + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + end + --This is the Tooltip tab provided by aucadvnced so all tooltip configuration is in one place + local tooltipID = AucAdvanced.Settings.Gui.tooltipID + + --now we create a duplicate of these in the tooltip frame + private.addTooltipControls(id) + if tooltipID then private.addTooltipControls(tooltipID) end +end + +function lib.OnLoad(addon) + AucAdvanced.Settings.SetDefault("stat.wowecon.useglobal", true) + AucAdvanced.Settings.SetDefault("stat.wowecon.enable", false) + AucAdvanced.Settings.SetDefault("stat.wowecon.sanitize", true) + AucAdvanced.Settings.SetDefault("stat.wowecon.tooltip", false) +end + +function lib.ProcessTooltip(tooltip, name, hyperlink, quality, quantity, cost, ...) + if not AucAdvanced.Settings.GetSetting("stat.wowecon.enable") then return end + if not AucAdvanced.Settings.GetSetting("stat.wowecon.tooltip") then return end + lib.GetPriceArray(hyperlink) + + if array.seen and array.seen > 0 then + tooltip:SetColor(0.3, 0.9, 0.8) + + tooltip:AddLine(_TRANS('WECN_Tooltip_PricesSeen')..array.seen)--WOWEcon prices seen: + + if array.specific then + tooltip:AddLine(" ".._TRANS('WECN_Tooltip_ServerPrice') , array.price * quantity)--Server price: + else + tooltip:AddLine(" ".._TRANS('WECN_Tooltip_GlobalPrice') , array.price * quantity)--Global price: + end + if (quantity > 1) then + tooltip:AddLine(" ".._TRANS('WECN_Tooltip_Individually') , array.price)--(or individually) + end + + if IsModifierKeyDown() then + if array.specific then + tooltip:AddLine(" ".._TRANS('WECN_Tooltip_GlobalSeen'):format(array.g_seen), array.g_price * quantity)--Global seen {{%s}}: + elseif array.s_seen > 0 then + tooltip:AddLine(" ".._TRANS('WECN_Tooltip_ServerSeen'):format(array.s_seen), array.s_price * quantity)--Server seen {{%s}}: + else + tooltip:AddLine(" ".._TRANS('WECN_Tooltip_NeverSeen') )-- Never seen for server + end + end + + end +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Stat-WOWEcon/WOWEcon.lua $", "$Rev: 4840 $") diff --git a/Auc-Advanced/Modules/Auc-Util-AHWindowControl/Auc-Util-AHWindowControl.lua b/Auc-Advanced/Modules/Auc-Util-AHWindowControl/Auc-Util-AHWindowControl.lua new file mode 100644 index 0000000..4c99a8d --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-AHWindowControl/Auc-Util-AHWindowControl.lua @@ -0,0 +1,270 @@ +--[[ + Auctioneer - AH-WindowControl + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: Auc-Util-AHWindowControl.lua 4840 2010-08-04 21:44:00Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This is an addon for World of Warcraft that adds the abilty to drag and reposition the Auction House Frame. + Protect the Auction Frame from being closed or moved by Escape or Blizzard frames. + It also adds limited Font and Frame Scaling of the Auction House/CompactUI + + 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 libType, libName = "Util", "AHWindowControl" +local lib, parent, private = AucAdvanced.NewModule(libType, libName) + +if not lib then return end + +local print,decode,_,_,replicate,empty,get,set,default,debugPrint,fill, _TRANS = AucAdvanced.GetModuleLocals() + +lib.Private = private + +function lib.GetName() + return libName +end + +local debug = false +local function debugPrint(...) + if debug then + print(...) + end +end + +function lib.Processor(callbackType, ...) + if callbackType == "auctionui" then + private.auctionHook() ---When AuctionHouse loads hook the auction function we need + private.MoveFrame() --Set position back to previous session if options set + elseif callbackType == "configchanged" then + private.MoveFrame() + private.AdjustProtection() + elseif callbackType == "config" then + private.SetupConfigGui(...) + end +end + +lib.Processors = {} +function lib.Processors.auctionui(callbackType, ...) + private.auctionHook() ---When AuctionHouse loads hook the auction function we need + private.MoveFrame() --Set position back to previous session if options set +end + +function lib.Processors.configchanged(callbackType, ...) + private.MoveFrame() + private.AdjustProtection() +end + +function lib.Processors.config(callbackType, ...) + private.SetupConfigGui(...) +end + + + +function lib.OnLoad(addon) + default("util.mover.activated", true) + default("util.mover.rememberlastpos", true) + default("util.mover.anchors", {"TOPLEFT", UIParent, "TOPLEFT", 0, -104}) + default("util.protectwindow.protectwindow", 1) + default("util.ahwindowcontrol.auctionscale", 1) --This is the scale of AuctionFrame 1 == default + default("util.ahwindowcontrol.compactuiscale", 0) --This is the increase of compactUI scale + default("util.ahwindowcontrol.searchuiscale", 1) --This is the default SearchUI scale +end + +--after Auction House Loads Hook the Window Display event +function private.auctionHook() + hooksecurefunc("AuctionFrame_Show", private.setupWindowFunctions) +end + +function private.SetupConfigGui(gui) + -- The defaults for the following settings are set in the lib.OnLoad function + local id, last + --Setup Tab for Mover functions + id = gui:AddTab(libName) + gui:MakeScrollable(id) + gui:AddControl(id, "Header", 0, _TRANS('AHWC_Interface_WindowMovementOptions') ) --"Window Movement Options" + gui:AddControl(id, "Checkbox", 0, 1, "util.mover.activated", _TRANS('AHWC_Interface_AllowMovable') ) --"Allow the auction frame to be movable?" + gui:AddTip(id, _TRANS('AHWC_HelpTooltip_AllowMovable') ) --"Ticking this box will enable the ability to relocate the auction frame" + gui:AddHelp(id, "what is AHWindowControl", + _TRANS('AHWC_Help_whatisthis'),--"What is this utility?" + _TRANS'AHWC_Help_whatisthisAnswer')--This utility allows you to drag and relocate the auction frame for this play session. Just click and move where you desire. It also alows you to protect the Auction House from closing when opening certain Blizzard windows." + gui:AddControl(id, "Checkbox", 0, 1, "util.mover.rememberlastpos", _TRANS('AHWC_Interface_RemberLastPosition') ) --"Remember last known window position?" + gui:AddTip(id, _TRANS('AHWC_HelpToolTip_RemberLastPosition') ) --"If this box is checked, the auction frame will reopen in the last location it was moved to." + gui:AddHelp(id, "what is remeberpos", + _TRANS('AHWC_Help_RemberLastPosition'), --"Remember last known window position?" + _TRANS('AHWC_Help_RemberLastPositionAnswer') ) --"This will remember the auction frame's last position and re-apply it each session." + + --Window Protection + gui:AddControl(id, "Header", 0, _TRANS("AHWC_Interface_WindowProtectionOptions") ) --WindowProtectionOptions + gui:AddControl(id, "Subhead", 0, _TRANS("AHWC_Interface_ProtectAuctionWindow") ) --Protect the Auction House window: + --Note the function reference in the place of the setting name. See changes in getter, setter, and getDefault to accomodate this. + gui:AddControl(id, "Selectbox", 0, 1, { + {1, _TRANS("AHWC_Interface_Never") }, --Never + {2, _TRANS("AHWC_Interface_Always") }, --Always + }, "util.protectwindow.protectwindow", _TRANS("AHWC_Interface_PreventClosingAuctionHouse") ) --"Prevent other windows from closing the Auction House window." + gui:AddTip(id, _TRANS("AHWC_HelpToolTip_PreventClosingAuctionHouse") ) --This will prevent other windows from closing the Auction House window when you open them, according to your settings. + gui:AddHelp(id, "What is ProtectWindow", + _TRANS("AHWC_Help_ProtectWindow"), --What does Protecting the AH Window do? + _TRANS("AHWC_Help_ProtectWindowAnswer") ) + --The Auction House window is normally closed when you open other windows, such as the Social window, the Quest Log, or your profession windows. This option allows it to remain open, behind those other windows. + --AuctionFrame Scale + gui:AddControl(id, "Header", 0, "") --Spacer for options + gui:AddControl(id, "Header", 0, _TRANS("AHWC_Interface_WindowSizeOptions") ) --Window Size Options + gui:AddControl(id, "NumeriSlider", 0, 1, "util.ahwindowcontrol.auctionscale", 0.5, 2, 0.1, _TRANS("AHWC_Interface_AuctionHouseScale") ) --Auction House Scale + gui:AddTip(id, _TRANS("AHWC_HelpToolTip_AuctionHouseScale") ) --This option allows you to adjust the overall size of the Auction House window. Default is 1. + gui:AddHelp(id, "what is Auction House Scale", + _TRANS("AHWC_Help_AuctionHouseScale"), --Auction House Scale? + _TRANS("AHWC_Help_AuctionHouseScaleAnswer") )--The Auction House scale slider adjusts the overall size of the entire Auction House window. The default size is 1. + --CompactUI + gui:AddControl(id, "NumeriSlider", 0, 1, "util.ahwindowcontrol.compactuiscale", -5, 5, 0.2, _TRANS("AHWC_Interface_CompactUIFontScale") ) --CompactUI Font Scale + gui:AddTip(id, _TRANS("AHWC_HelpTooltip_CompactUIFontScale") ) --This option allows you to adjust the text size of the CompactUI on the Browse tab. The default size is 0. + gui:AddHelp(id, "what is CompactUI Font Scale", + _TRANS("AHWC_Help_CompactUIFontScale"), --CompactUI Font Scale? + _TRANS("AHWC_Help_CompactUIFontScaleAnswer") ) --The CompactUI Font Scale slider adjusts the text size displayed in AucAdvance CompactUI option in the Browse Tab. The default size is 0. + --SearchUI + gui:AddControl(id, "NumeriSlider", 0, 1, "util.ahwindowcontrol.searchuiscale", 0.5, 2, 0.1, _TRANS("AHWC_Interface_SearchUIScale") ) --SearchUI Scale + gui:AddTip(id, _TRANS("AHWC_HelpTooltip_SearchUIScale") ) --This option allows you to adjust the overall size of the non auction house SearchUI window. The default size is 1. + gui:AddHelp(id, "what is SearchUI Scale", + _TRANS("AHWC_Help_SearchUIScale"), --SearchUI Scale? + _TRANS("AHWC_Help_SearchUIScaleAnswer") ) --The SearchUI scale slider adjusts the overall size of the non auction house SearchUI window. The default size is 1. +end + + +--[[ Local functions ]]-- + +--Hooks AH show function. This is fired after all Auction Frame methods have been set by Blizzard +--We can now override with our settings +local runonce=true +function private.setupWindowFunctions() + private.recallLastPos() + if runonce then + private.AdjustProtection() + runonce=nil + end +end + +--Enable or Disable the move scripts +function private.MoveFrame() + --AH needs to exist + if AuctionFrame then + if get("util.mover.activated") then + AuctionFrame:SetMovable(true) + AuctionFrame:SetClampedToScreen(true) + AuctionFrame:SetScript("OnMouseDown", function() AuctionFrame:StartMoving() end) + AuctionFrame:SetScript("OnMouseUp", function() AuctionFrame:StopMovingOrSizing() + set("util.mover.anchors", {AuctionFrame:GetPoint()}) --store the current anchor points + end) + else + AuctionFrame:SetMovable(false) + AuctionFrame:SetScript("OnMouseDown", function() end) + AuctionFrame:SetScript("OnMouseUp", function() end) + end + if get("util.ahwindowcontrol.auctionscale") then + AuctionFrame:SetScale(get("util.ahwindowcontrol.auctionscale")) + end + if get("util.compactui.activated") then + for i = 1,14 do + local button = _G["BrowseButton"..i] + local increase = get('util.ahwindowcontrol.compactuiscale') or 0 + if not button.Count then return end -- we get called before compactUI has built the frame + button.Count:SetFont(STANDARD_TEXT_FONT, 11 + increase) + button.Name:SetFont(STANDARD_TEXT_FONT, 10 + increase) + button.rLevel:SetFont(STANDARD_TEXT_FONT, 11 + increase) + button.iLevel:SetFont(STANDARD_TEXT_FONT, 11 + increase) + button.tLeft:SetFont(STANDARD_TEXT_FONT, 11 + increase) + button.Owner:SetFont(STANDARD_TEXT_FONT, 10 + increase) + button.Value:SetFont(STANDARD_TEXT_FONT, 11 + increase) + end + end + end + --searchUi needs to exist + if AucAdvanced.Modules.Util.SearchUI and AucAdvanced.Modules.Util.SearchUI.Private.gui then + if get("util.ahwindowcontrol.searchuiscale") then + AucAdvanced.Modules.Util.SearchUI.Private.gui:SetScale(get("util.ahwindowcontrol.searchuiscale")) + end + end +end + +--Restore previous sessions Window position +function private.recallLastPos() + if get("util.mover.rememberlastpos") then + local anchors = get("util.mover.anchors") + if #anchors ~= 5 then anchors = {"TOPLEFT", UIParent, "TOPLEFT", 0, -104} end + AuctionFrame:ClearAllPoints() + AuctionFrame:SetPoint(anchors[1], anchors[2], anchors[3], anchors[4], anchors[5]) + end +end + +--This script will turn the protection of the AuctionFrame on or off, +--as appropriate. +function private.AdjustProtection () + if not UIPanelWindows["AuctionFrame"] then + debugPrint("AuctionFrame doesn't exist yet.") + return + elseif (get("util.protectwindow.protectwindow") == 1) and not AuctionFrame:GetAttribute("UIPanelLayout-enabled") then + debugPrint("Enabling Standard Frame Handler for Auction Frame because protectwindow ="..get("util.protectwindow.protectwindow")) + AuctionFrame:SetAttribute("UIPanelLayout-enabled", true) + if AuctionFrame:IsVisible() then + AuctionFrame.IsShown = function() end + ShowUIPanel(AuctionFrame, 1) + AuctionFrame.IsShown = nil + end + elseif (get("util.protectwindow.protectwindow") == 2) and AuctionFrame:GetAttribute("UIPanelLayout-enabled") == true then + debugPrint("Disabling Standard Frame Handler for Auction Frame because protectwindow ="..get("util.protectwindow.protectwindow")) + if AuctionFrame:IsVisible() then + AuctionFrame.Hide = function() end + HideUIPanel(AuctionFrame) + AuctionFrame.Hide = nil + end + AuctionFrame:SetAttribute("UIPanelLayout-enabled", nil) + elseif get("util.protectwindow.protectwindow") ~= 1 and get("util.protectwindow.protectwindow") ~=2 then + local protectvalue = get("util.protectwidow.protectwindow") + protectvalue = tostring(protectvalue) + debugPrint("util.protectwindow.protectwindow="..protectvalue.." an invalid value") + set("util.protectwindow.protectwindow", 1) + if not AuctionFrame:GetAttribute("UIPanelLayout-enabled") then + AuctionFrame:SetAttribute("UIPanelLayout-enabled", true) + if AuctionFrame:IsVisible() then + AuctionFrame.IsShown = function() end + ShowUIPanel(AuctionFrame, 1) + AuctionFrame.IsShown = nil + end + end + else + debugPrint("No case matched.") + debugPrint("util.protectwindow.protectwindow="..get("util.protectwindow.protectwindow")) + debugPrint("UIPanelLayout-enabled="..tostring(AuctionFrame:GetAttribute("UIPanelLayout-enabled"))) + end +end + +function lib.ToggleDebug() + if debug then + debug = false + print("Turned debugging text off.") + else + debug = true + print("Turned debugging text on.") + end +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-AHWindowControl/Auc-Util-AHWindowControl.lua $", "$Rev: 4840 $") diff --git a/Auc-Advanced/Modules/Auc-Util-AHWindowControl/Auc-Util-AHWindowControl.toc b/Auc-Advanced/Modules/Auc-Util-AHWindowControl/Auc-Util-AHWindowControl.toc new file mode 100644 index 0000000..c36b9a0 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-AHWindowControl/Auc-Util-AHWindowControl.toc @@ -0,0 +1,16 @@ +## Title: Auc:Util:AHWindowControl +## Notes: Add general utilities to the Auction House Frame - protects from closing, allows repositioning, and limited scaling. +## +## Interface: 40000 +## LoadOnDemand: 0 +## Disabled: 0 +## Dependencies: Auc-Advanced +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Auc-Util-AHWindowControl.toc +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## X-Max-Interface: 40000 +## +Embed.xml diff --git a/Auc-Advanced/Modules/Auc-Util-AHWindowControl/Embed.xml b/Auc-Advanced/Modules/Auc-Util-AHWindowControl/Embed.xml new file mode 100644 index 0000000..34715e9 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-AHWindowControl/Embed.xml @@ -0,0 +1,7 @@ + + + + diff --git a/Auc-Advanced/Modules/Auc-Util-Appraiser/Appraiser.lua b/Auc-Advanced/Modules/Auc-Util-Appraiser/Appraiser.lua new file mode 100644 index 0000000..2de0ca4 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-Appraiser/Appraiser.lua @@ -0,0 +1,518 @@ +--[[ + Auctioneer - Appraisals and Auction Posting + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: Appraiser.lua 4880 2010-09-15 20:02:11Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This is an addon for World of Warcraft that adds an appraisals tab to the AH for + easy posting of your auctionables when you are at the auction house. + + 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 libType, libName = "Util", "Appraiser" +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end +local aucPrint,decode,_,_,replicate,_,get,set,default,debugPrint,fill, _TRANS = AucAdvanced.GetModuleLocals() + +-- reduce global lookups +local wipe, tinsert = wipe, tinsert +local floor, ceil, max = floor, ceil, max +local tonumber = tonumber +local AucAdvanced = AucAdvanced +local GetFaction = AucAdvanced.GetFaction +local SplitServerKey = AucAdvanced.SplitServerKey +local GetMarketValue = AucAdvanced.API.GetMarketValue +local GetAlgorithmValue = AucAdvanced.API.GetAlgorithmValue +local GetBestMatch = AucAdvanced.API.GetBestMatch +local GetSigFromLink = AucAdvanced.API.GetSigFromLink +local GetDepositCost = GetDepositCost +local GetItemInfo = GetItemInfo + +local pricecache -- cache for GetPrice; only used in certain circumstances +local tooltipcache = {} -- cache for ProcessTooltip + +function lib.Processor(callbackType, ...) + if (callbackType == "tooltip") then + lib.ProcessTooltip(...) + elseif (callbackType == "auctionui") then + if private.CreateFrames then private.CreateFrames(...) end + elseif (callbackType == "config") then + private.SetupConfigGui(...) + elseif (callbackType == "configchanged") then + local change, value = ... --get the reason if its a scrollframe color change re-render the window + if private.frame then + private.frame.salebox.config = true + -- private.frame.SetPriceFromModel() + private.frame.UpdatePricing() + private.frame.UpdateDisplay() + -- private.frame.salebox.config = nil + if change == "util.appraiser.color" or change == "util.appraiser.colordirection" then + private.frame.UpdateImage() + end + --show/hide the appraiser tab on the AH + if change == "util.appraiser.displayauctiontab" then + if value then + AucAdvanced.AddTab(private.frame.ScanTab, private.frame) + else + AucAdvanced.RemoveTab(private.frame.ScanTab, private.frame) + end + end + end + if change:sub(1, 20) == "util.appraiser.round" then + private.updateRoundExample() + end + -- clear cache for any changes, as we can't always predict what will change our cached values + wipe(tooltipcache) + elseif (callbackType == "inventory") then + if private.frame and private.frame:IsVisible() then + private.frame.GenerateList() + end + elseif (callbackType == "scanstats") then + if private.frame then + private.frame.cache = {} + -- caution: other modules may not yet have flushed their caches + -- flag to update our display next OnUpdate + private.frame.scanstatsEvent = true + end + wipe(tooltipcache) + elseif (callbackType == "postresult") then + private.SelectNextOnPost(select(3, ...)) + --private.frame.Reselect(select(3, ...)) + elseif callbackType == "postqueue" then + if private.UpdatePostQueueProgress then private.UpdatePostQueueProgress(...) end + elseif callbackType == "searchbegin" then + pricecache = {} -- use cache when SearchUI is running a search + elseif callbackType == "searchcomplete" then + pricecache = nil -- stop using cache when search ends + end +end + +lib.Processors = {} +function lib.Processors.tooltip(callbackType, ...) + lib.ProcessTooltip(...) +end + +function lib.Processors.auctionui(callbackType, ...) + if private.CreateFrames then private.CreateFrames(...) end +end + +function lib.Processors.config(callbackType, ...) + private.SetupConfigGui(...) +end + +function lib.Processors.configchanged(callbackType, ...) + local change, value = ... --get the reason if its a scrollframe color change re-render the window + if private.frame then + private.frame.salebox.config = true + -- private.frame.SetPriceFromModel() + private.frame.UpdatePricing() + private.frame.UpdateDisplay() + -- private.frame.salebox.config = nil + if change == "util.appraiser.color" or change == "util.appraiser.colordirection" then + private.frame.UpdateImage() + end + --show/hide the appraiser tab on the AH + if change == "util.appraiser.displayauctiontab" then + if value then + AucAdvanced.AddTab(private.frame.ScanTab, private.frame) + else + AucAdvanced.RemoveTab(private.frame.ScanTab, private.frame) + end + end + end + if change:sub(1, 20) == "util.appraiser.round" then + private.updateRoundExample() + end + -- clear cache for any changes, as we can't always predict what will change our cached values + wipe(tooltipcache) +end + +function lib.Processors.inventory(callbackType, ...) + if private.frame and private.frame:IsVisible() then + private.frame.GenerateList() + end +end + +function lib.Processors.scanstats(callbackType, ...) + if private.frame then + private.frame.cache = {} + -- caution: other modules may not yet have flushed their caches + -- flag to update our display next OnUpdate + private.frame.scanstatsEvent = true + end + wipe(tooltipcache) +end + +function lib.Processors.postresult(callbackType, ...) + private.SelectNextOnPost(...) +end + +function lib.Processors.postqueue(callbackType, ...) + if private.UpdatePostQueueProgress then private.UpdatePostQueueProgress(...) end +end + +function lib.Processors.searchbegin(callbackType, ...) + pricecache = {} -- use cache when SearchUI is running a search +end + +function lib.Processors.searchcomplete(callbackType, ...) + pricecache = nil -- stop using cache when search ends +end + + + +-- Deprecated. For backwards compatibility, leave these here. This is now a capability of the core API +lib.GetSigFromLink = GetSigFromLink +lib.GetLinkFromSig = AucAdvanced.API.GetLinkFromSig + +function lib.ProcessTooltip(tooltip, name, hyperlink, quality, quantity, cost, additional) + if not get("util.appraiser.enable") then return end + if not get("util.appraiser.model") then return end + local sig = GetSigFromLink(hyperlink) + if not sig then return end + + tooltip:SetColor(0.3, 0.9, 0.8) + + local value, bid, curModel, _ + + if tooltipcache[sig] then + value, bid, curModel = unpack(tooltipcache[sig]) -- all values should be non-nil + else + value, bid, _, curModel = private.GetPriceCore(sig, hyperlink, GetFaction(), true) + if curModel ~= "Enchantrix" then -- don't cache values based on modules which don't broadcast configchanged + tooltipcache[sig] = {value, bid, curModel} + end + end + + if value then + tooltip:AddLine(_TRANS('APPR_Tooltip_AppraiserCurModel'):format(curModel, quantity) , value * quantity)--Appraiser ({{%s}}x{{%s}} + if get("util.appraiser.bidtooltip") then + tooltip:AddLine(" ".._TRANS('APPR_Tooltip_StartingBid'):format(quantity), bid * quantity)--Starting bid x {{%d}} + end + end + if get("util.appraiser.ownauctions") then + local itemName = name + + local colored = get('util.appraiser.manifest.color') and AucAdvanced.Modules.Util.PriceLevel + + local results = lib.ownResults[itemName] + local counts = lib.ownCounts[itemName] + + if counts and #counts>0 then + local sumBid, sumBO = 0, 0 + local countBid, countBO = 0, 0 + for _,count in ipairs(counts) do + local res = results[count] + sumBid = sumBid + res.sumBid --*res.stackCount + sumBO = sumBO + res.sumBO --*res.stackCount + countBid = countBid + res.countBid --*res.stackCount + countBO = countBO + res.countBO --*res.stackCount + end + local avgBid = countBid>0 and (sumBid / countBid) or nil + local avgBO = countBO>0 and (sumBO / countBO) or nil + local r,g,b,_ + if colored then + _, _, r,g,b = AucAdvanced.Modules.Util.PriceLevel.CalcLevel(hyperlink, 1, avgBid, avgBO) + end + r,g,b = r or 1,g or 1, b or 1 + + tooltip:AddLine(" ".._TRANS('APPR_Tooltip_PostedCount'):format(countBO or countBid, avgBO and "" or " (bid)"), avgBO or avgBid, r,g,b)--Posted %2d at avg/ea %s + end + end +end + +function lib.GetPrice(link, serverKey) + local sig = GetSigFromLink(link) + if not sig then + return 0, 0, false, 0, "Unknown", "", 0, 0, 0 + end + if not serverKey then + serverKey = GetFaction() + end + local newBuy, newBid, seen, curModelText, MatchString, stack, number, duration + + if pricecache then + local cacheSig = serverKey..sig + if pricecache[cacheSig] then + newBuy, newBid, seen, curModelText, MatchString, stack, number, duration = unpack(pricecache[cacheSig], 1, 8) -- some values may be nil + else + newBuy, newBid, seen, curModelText, MatchString, stack, number, duration = private.GetPriceCore(sig, link, serverKey, true) + pricecache[cacheSig] = {newBuy, newBid, seen, curModelText, MatchString, stack, number, duration} + end + else + newBuy, newBid, seen, curModelText, MatchString, stack, number, duration = private.GetPriceCore(sig, link, serverKey, true) + end + + return newBuy, newBid, false, seen, curModelText, MatchString, stack, number, duration +end + +function lib.GetPriceUnmatched(link, serverKey) + local sig = GetSigFromLink(link) + if not sig then + return 0, 0, false, 0, "Unknown", "", 0, 0, 0 + end + + local newBuy, newBid, seen, curModelText, MatchString, stack, number, duration + newBuy, newBid, seen, curModelText, MatchString, stack, number, duration = private.GetPriceCore(sig, link, serverKey) + return newBuy, newBid, false, seen, curModelText, MatchString, stack, number, duration +end + +function lib.GetPriceColumns() + return "Buyout", "Bid", false, "seen", "curModelText", "MatchString", "Stack", "Number", "Duration" +end + +local array = {} +--returns pricing and posting settings +function lib.GetPriceArray(link, serverKey) + wipe(array) + + local newBuy, newBid, _, seen, curModelText, MatchString, stack, number, duration = lib.GetPrice(link, serverKey) + + array.price = newBuy + array.seen = seen + + array.stack = stack + array.number = number + array.duration = duration + + return array +end + +function lib.OnLoad() + -- Configure our defaults + AucAdvanced.Settings.SetDefault("util.appraiser.enable", false) + AucAdvanced.Settings.SetDefault("util.appraiser.bidtooltip", true) + AucAdvanced.Settings.SetDefault("util.appraiser.model", "market") + AucAdvanced.Settings.SetDefault("util.appraiser.ownauctions", false) + AucAdvanced.Settings.SetDefault("util.appraiser.altModel", "market") + AucAdvanced.Settings.SetDefault("util.appraiser.duration", 2880) + AucAdvanced.Settings.SetDefault("util.appraiser.round.bid", false) + AucAdvanced.Settings.SetDefault("util.appraiser.round.buy", false) + AucAdvanced.Settings.SetDefault("util.appraiser.round.method", "unit") + AucAdvanced.Settings.SetDefault("util.appraiser.round.position", 0.10) + AucAdvanced.Settings.SetDefault("util.appraiser.round.magstep", 5) + AucAdvanced.Settings.SetDefault("util.appraiser.round.subtract", 1) + AucAdvanced.Settings.SetDefault("util.appraiser.bid.markdown", 10) + AucAdvanced.Settings.SetDefault("util.appraiser.bid.subtract", 0) + AucAdvanced.Settings.SetDefault("util.appraiser.bid.deposit", true) + AucAdvanced.Settings.SetDefault("util.appraiser.bid.vendor", true) + AucAdvanced.Settings.SetDefault("util.appraiser.color", true) + AucAdvanced.Settings.SetDefault("util.appraiser.colordirection","RIGHT") + AucAdvanced.Settings.SetDefault("util.appraiser.manifest.color", true) + AucAdvanced.Settings.SetDefault("util.appraiser.tint.color", true) + AucAdvanced.Settings.SetDefault("util.appraiser.match", "on") + AucAdvanced.Settings.SetDefault("util.appraiser.stack", "max") + AucAdvanced.Settings.SetDefault("util.appraiser.number", "maxplus") + AucAdvanced.Settings.SetDefault("util.appraiser.clickhookany", true) + AucAdvanced.Settings.SetDefault("util.appraiser.reselect", true) + AucAdvanced.Settings.SetDefault("util.appraiser.buttontips", true) + AucAdvanced.Settings.SetDefault("util.appraiser.displayauctiontab", true) + --Default sizes for the scrollframe column widths + AucAdvanced.Settings.SetDefault("util.appraiser.columnwidth.".._TRANS('APPR_Interface_Seller'), 71) --Seller + AucAdvanced.Settings.SetDefault("util.appraiser.columnwidth.".._TRANS('APPR_Interface_Left'), 25) --Left + AucAdvanced.Settings.SetDefault("util.appraiser.columnwidth.".._TRANS('APPR_Interface_Stk'), 27 ) --Stk + AucAdvanced.Settings.SetDefault("util.appraiser.columnwidth.".._TRANS('APPR_Interface_Min/ea'), 65) --Min/ea + AucAdvanced.Settings.SetDefault("util.appraiser.columnwidth.".._TRANS('APPR_Interface_Cur/ea'), 65) --Cur/ea + AucAdvanced.Settings.SetDefault("util.appraiser.columnwidth.".._TRANS('APPR_Interface_Buy/ea'), 65) --Buy/ea + AucAdvanced.Settings.SetDefault("util.appraiser.columnwidth.".._TRANS('APPR_Interface_MinBid'), 65) --MinBid + AucAdvanced.Settings.SetDefault("util.appraiser.columnwidth.".._TRANS('APPR_Interface_CurBid'), 65) --CurBid + AucAdvanced.Settings.SetDefault("util.appraiser.columnwidth.".._TRANS('APPR_Interface_Buyout'), 68) --Buyout + AucAdvanced.Settings.SetDefault("util.appraiser.columnwidth.BLANK", 0.05) +end + +function lib.CanSupplyMarket() + return false +end + +function lib.ClearItem() + -- we only need this to clear caches + wipe(tooltipcache) + if pricecache then wipe(pricecache) end +end +lib.ClearData = lib.ClearItem + +function private.GetPriceCore(sig, link, serverKey, match) + local newBuy, newBid, seen, DiffFromModel, MatchString + + local curModel = get("util.appraiser.item."..sig..".model") or "default" + local curModelText = curModel + local duration = get("util.appraiser.item."..sig..".duration") or get("util.appraiser.duration") + + -- valuation + if curModel == "default" then + curModel = get("util.appraiser.model") or "market" + if curModel == "market" then + newBuy, seen = GetMarketValue(link, serverKey) + else + newBuy, seen = GetAlgorithmValue(curModel, link, serverKey) + end + if (not newBuy) or (newBuy == 0) then + curModel = get("util.appraiser.altModel") + if curModel == "market" then + newBuy, seen = GetMarketValue(link, serverKey) + else + newBuy, seen = GetAlgorithmValue(curModel, link, serverKey) + end + end + curModelText = curModelText.."("..curModel..")" + elseif curModel == "fixed" then + newBuy = get("util.appraiser.item."..sig..".fixed.buy") + newBid = get("util.appraiser.item."..sig..".fixed.bid") + seen = 99 + elseif curModel == "market" then + newBuy, seen = GetMarketValue(link, serverKey) + else + newBuy, seen = GetAlgorithmValue(curModel, link, serverKey) + end + + -- matching + if match then + match = get("util.appraiser.item."..sig..".match") + if match == nil then + match = get("util.appraiser.match") + end + if match then + if curModel == "fixed" then + if newBuy and newBuy > 0 then + local tBuy, _, _, tDiff, tString = GetBestMatch(link, newBuy, serverKey) + if newBid then + newBid = newBid * tBuy / newBuy + end + newBuy = tBuy + DiffFromModel = tDiff + MatchString = tString + end + else + local tBuy, _, _, tDiff, tString = GetBestMatch(link, newBuy, serverKey) + newBuy = tBuy + DiffFromModel = tDiff + MatchString = tString + end + end + end + + -- other return values + local stack = get("util.appraiser.item."..sig..".stack") or get("util.appraiser.stack") + local number = get("util.appraiser.item."..sig..".number") or get("util.appraiser.number") + local _, _, _, _, _, _, _, maxStack = GetItemInfo(link) + --we only officially accept "max" or a number, but user could have input any random string, so add some sanitization + stack = tonumber(stack) + if stack then + if maxStack and stack > maxStack then + stack = maxStack --never allow a saved stack value larger than the item can really stack to + elseif stack < 1 then + stack = 1 + end + else + stack = maxStack + end + if number == "maxplus" then + number = -1 + elseif number == "maxfull" then + number = -2 + end + number = tonumber(number) + + -- generate bid value + if curModel ~= "fixed" and newBuy then + if not newBid then + local markdown = newBuy * (get("util.appraiser.bid.markdown") or 0)/100 + local subtract = get("util.appraiser.bid.subtract") or 0 + local deposit = 0 + if get("util.appraiser.bid.deposit") then + local _, faction = SplitServerKey(serverKey) + local dep = GetDepositCost(link, duration, faction, stack) + if dep and stack then + deposit = dep / stack + end + end + + newBid = newBuy - markdown - subtract - deposit + if newBid < 1 then + newBid = 1 + end + end + + if GetSellValue and get("util.appraiser.bid.vendor") then + local vendor = GetSellValue(link) + if vendor and vendor>0 then + vendor = ceil(vendor / (1 - (AucAdvanced.cutRate or 0.05))) + if newBid < vendor then + newBid = vendor + end + end + end + + if newBid > newBuy then + newBuy = newBid + end + end + + -- pricing: final checks + newBid = floor((newBid or 0) + 0.5) + newBuy = floor((newBuy or 0) + 0.5) + + -- Caution: this is NOT the same return order as GetPrice + return newBuy, newBid, seen, curModelText, MatchString, stack, number, duration +end + +lib.ownResults = {} +lib.ownCounts = {} +function lib.GetOwnAuctionDetails() + local results = lib.ownResults + local counts = lib.ownCounts + wipe(results) + wipe(counts) + local numBatchAuctions, totalAuctions = GetNumAuctionItems("owner"); + if totalAuctions >0 then + for i=1, totalAuctions do + local name, _, count, _, _, _, minBid, minIncrement, buyoutPrice, bidAmount, highBidder, owner = GetAuctionItemInfo("owner", i) + if name and (count>0) then + if not results[name] then + results[name] = {} + counts[name] = {} + end + local r = results[name][count] + if not r then + r = { stackCount=0, countBid=0, sumBid=0, countBO=0, sumBO=0 } + results[name][count] = r + tinsert(counts[name], count) + end + if (minBid or 0)>0 then + r.countBid = r.countBid + count + r.sumBid = r.sumBid + bidAmount + end + if (buyoutPrice or 0)>0 then + r.countBO = r.countBO + count + r.sumBO = r.sumBO + buyoutPrice + end + r.stackCount = r.stackCount + 1 + end + end + end +end +Stubby.RegisterEventHook("AUCTION_OWNED_LIST_UPDATE", "Auc-Util-Appraiser", lib.GetOwnAuctionDetails) + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-Appraiser/Appraiser.lua $", "$Rev: 4880 $") diff --git a/Auc-Advanced/Modules/Auc-Util-Appraiser/AprFrame.lua b/Auc-Advanced/Modules/Auc-Util-Appraiser/AprFrame.lua new file mode 100644 index 0000000..cd2d91d --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-Appraiser/AprFrame.lua @@ -0,0 +1,2799 @@ +--[[ + Auctioneer - Appraisals and Auction Posting + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: AprFrame.lua 4933 2010-10-13 17:16:14Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This is an addon for World of Warcraft that adds an appraisals tab to the AH for + easy posting of your auctionables when you are at the auction house. + + 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 lib = AucAdvanced.Modules.Util.Appraiser +local private = lib.Private +local Const = AucAdvanced.Const +local aucPrint,decode,_,_,replicate,empty,get,set,default,debugPrint,fill, _TRANS = AucAdvanced.GetModuleLocals() + +local frame + +local NUM_ITEMS = 12 + +local SigFromLink = AucAdvanced.API.GetSigFromLink +local GetDistribution -- to be filled in when ScanData loads + +-- Check to see if we are embedded or not +local embedded = false +for _, module in ipairs(AucAdvanced.EmbeddedModules) do + if module == "Auc-Util-Appraiser" then + embedded = true + end +end + +function private.CreateFrames() + private.CreateFrames = nil + + local SelectBox = LibStub:GetLibrary("SelectBox") + local ScrollSheet = LibStub:GetLibrary("ScrollSheet") + + frame = CreateFrame("Frame", "AucAdvAppraiserFrame", AuctionFrame) + private.frame = frame + local DiffFromModel = 0 + local MatchString = "" + frame.list = {} + frame.cache = {} + frame.valuecache = {} + + function frame.GenerateList(repos) + -- repos = flag to force reposition of scroller + if not frame:IsVisible() then return end --If we don't have Appraiser open, we don't need to run this. It will run when we go to Appraiser tab + local ItemList = frame.list + wipe(ItemList) + if not GetDistribution then + -- ScanData is load-on-demand; check each time until it loads, then store the reference for GetDistribution + local scandata = AucAdvanced.GetModule("Util", "ScanData") + if scandata then + GetDistribution = scandata.GetDistribution + end + end + + for bag=0, NUM_BAG_FRAMES do + for slot=1,GetContainerNumSlots(bag) do + local link = GetContainerItemLink(bag,slot) + if link then + local isDirect = false + if frame.direct and frame.direct == link then + isDirect = true + end + + if AucAdvanced.Post.IsAuctionable(bag, slot) or isDirect then + local sig = SigFromLink(link) + if sig then + local texture, itemCount, locked, special, readable = GetContainerItemInfo(bag,slot) + if not itemCount or itemCount < 0 then itemCount = 1 end + local found = false + for i = 1, #ItemList do + local item = ItemList[i] + if item[1] == sig then + item[6] = item[6] + itemCount + found = true + break + end + end + + if not found then + local ignore = get('util.appraiser.item.'..sig..".ignore") + + if frame.showHidden or (not ignore) or isDirect then + local name, _,rarity,_,_,_,_, stack = GetItemInfo(link) + local item = {sig, name, texture, rarity, stack, itemCount, link} + if ignore then + item.ignore = true + end + table.insert(ItemList, item) + + if GetDistribution and not frame.cache[sig] then + local exact, suffix, base, colorDist = GetDistribution(link) + frame.cache[sig] = { exact, suffix, base, {} } + for k,v in pairs(colorDist.exact) do + frame.cache[sig][4][k] = v + end + end + end + end + end + end + end + end + end + + if frame.showAuctions then + local auctionStart = #ItemList + 1 + for auc=1, GetNumAuctionItems("owner") do + local name, texture, count, quality, canUse, level, minBid, minIncrement, buyoutPrice, bidAmount, highBidder, owner = GetAuctionItemInfo("owner", auc) + local link = GetAuctionItemLink("owner", auc) + + local sig = SigFromLink(link) + if sig then + local found = false + for i = auctionStart, #ItemList do + local item = ItemList[i] + if item[1] == sig then + item[6] = item[6] + count + found = true + break + end + end + + if not found then + local name, _,rarity,_,_,_,_, stack = GetItemInfo(link) + + local item = { + sig,name,texture,rarity,stack,count,link, + auction=true + } + if get('util.appraiser.item.'..sig..".ignore") then + item.ignore = true + end + table.insert(ItemList, item) + + if GetDistribution and not frame.cache[sig] then + local exact, suffix, base, colorDist = GetDistribution(link) + frame.cache[sig] = { exact, suffix, base, {} } + for k,v in pairs(colorDist.exact) do + frame.cache[sig][4][k] = v + end + end + end + end + end + end + + table.sort(ItemList, private.sortItems) + + local listLen = #ItemList + -- find the current (or next) selected item and update stored details + local pos + local item + local sig = frame.selected + if sig then + local selected = frame.selectedItem + local wasAuction = selected and selected.auction + for i = 1, listLen do + local itm = ItemList[i] + if itm[1] == sig then + pos = i + item = itm + if not wasAuction or item.auction then + break + end + end + end + end + if not item then + local selected = frame.selectedItem + if selected then + if selected[7] == frame.selectedRawLink then -- we had a raw link and it matches last selected item + item = selected -- note that this item is not included in ItemList + item[6] = AucAdvanced.Post.CountAvailableItems(sig) -- re-count available items + sig = item[1] + -- pos remains nil + elseif get("util.appraiser.reselect") then + for i = 1, listLen do + local itm = ItemList[i] + if itm.ignore then -- don't auto-select ignored item + break -- stop now because all items after first ignored item will also be ignored + end + if not private.sortItems(itm, selected) then -- we want first itm >= selected (see defn of sortItems in AprSettings.lua) + if not itm.auction then -- don't auto-select auction item + pos = i + item = itm + sig = item[1] + break + end + end + end + end + end + end + if item then + frame.DisplaySelectedItem(item, sig, pos) + else + frame.ClearSelectedItem() + end + -- set scroller + if listLen <= NUM_ITEMS then + frame.scroller:Hide() + frame.scroller:SetMinMaxValues(0, 0) + frame.scroller:SetValue(0) + else + frame.scroller:SetMinMaxValues(0, listLen-NUM_ITEMS) + if repos then + if pos then + frame.scroller:SetValue(max(0, min(listLen-NUM_ITEMS+1, pos-(NUM_ITEMS/2)))) + else + frame.scroller:SetValue(0) + end + end + frame.scroller:Show() + end + -- redraw list buttons + frame:SetScroll() + + return pos + end + + function frame.SelectItem(obj, button, rawlink) + local item,sig,pos + + if obj then + if not obj.id then obj = obj:GetParent() end + pos = floor(frame.scroller:GetValue()) + obj.id + if button and pos == frame.selectedPos then + pos = nil + else + item = frame.list[pos] + if item then + sig = item[1] + else + pos = nil + end + end + frame.selectedRawLink = nil + elseif rawlink then + sig = SigFromLink(rawlink) + if not sig then return end + for i,itm in ipairs(frame.list) do + if itm[1]==sig then + item=itm + pos=i + break + end + end + if not item then + local itemName, itemLink, itemRarity, itemLevel, itemMinLevel, itemType, itemSubType, itemStackCount, + itemEquipLoc, itemTexture = GetItemInfo(rawlink) + local myCount = AucAdvanced.Post.CountAvailableItems(sig) + item = { + sig, itemName, itemTexture, itemRarity, itemStackCount, myCount, rawlink + } + end + frame.selectedRawLink = rawlink -- rawlinked item should stay selected until manually deselected + end + + if sig then + frame.DisplaySelectedItem(item, sig, pos) + + --Also pass this search to BeanCounter's frame + if BeanCounter and BeanCounter.API.search and BeanCounter.API.isLoaded then + BeanCounter.API.search(item[7], nil, nil, 50) + end + else + frame.ClearSelectedItem() + end + frame.SetScroll() + + end + + private.empty = {} + function frame.ClearSelectedItem() + frame.selected = nil + frame.selectedPos = nil + frame.selectedItem = nil + frame.selectedPostable = nil + frame.salebox.sig = nil + frame.salebox.name:SetText(_TRANS('APPR_Interface_NoItemSelected') )--No item selected + frame.salebox.name:SetTextColor(0.5, 0.5, 0.7) + if not get("util.appraiser.classic") then + frame.salebox.info:SetText(_TRANS('APPR_Interface_SelectItemLeft') )--Select an item to the left to begin auctioning... + else + frame.salebox.info:SetText(_TRANS('APPR_Interface_SelectItemAuctioning') )--Select an item to begin auctioning... + end + frame.salebox.info:SetTextColor(0.5, 0.5, 0.7) + frame.imageview.sheet:SetData(private.empty) + --frame.UpdatePricing() + frame.UpdateDisplay() + end + + function frame.DisplaySelectedItem(item, sig, pos) + frame.selected = sig + frame.selectedPos = pos + frame.selectedItem = item + frame.selectedPostable = not (item.auction or item[6]<1) + frame.salebox.sig = sig + local _,_,_, hex = GetItemQualityColor(item[4]) + frame.salebox.icon:SetNormalTexture(item[3]) + frame.salebox.name:SetText(hex.."["..item[2].."]|r") + if item.auction then + frame.salebox.info:SetText(_TRANS('APPR_Interface_HaveUpAuction'):format(item[6]) )--You have %s up for auction + else + frame.salebox.info:SetText(_TRANS('APPR_Interface_HaveAvailableAuction'):format(item[6]) )--You have %s available to auction + end + frame.salebox.info:SetTextColor(1,1,1, 0.8) + frame.salebox.link = item[7] + frame.salebox.stacksize = item[5] + frame.salebox.count = item[6] + + frame.UpdateImage() + frame.InitControls() + end + + function frame.SelectNext() + -- select next postable item + local pos, item + pos = frame.selectedPos + if not pos then return end + repeat + pos = pos + 1 + item = ItemList[pos] + until not item or (item[6] > 0 and not item.auction) + if item then + frame.DisplaySelectedItem(item, item[1], pos) + else + frame.ClearSelectedItem() + end + end + + function private.SelectNextOnPost(success, id, postresult, errcode) + --[[ todo: add settings option to enable this feature + local sig = postresult.sig or postresult[1] + if sig == frame.selected then + frame.SelectNext() + end + --]] + end + + -- this function is not used - can it be removed? (along with the supporting code for frame.direct in GenerateList) + function frame.DirectSelect(link) + if frame.direct == link then return end + frame.direct = link + frame.GenerateList() + frame.GetItemByLink(link) + frame.GenerateList(true) + end + + function frame.UpdateImage() + if not frame.salebox.sig then return end + + local itemId, suffix, factor = strsplit(":", frame.salebox.sig) + itemId = tonumber(itemId) + suffix = tonumber(suffix) or 0 + factor = tonumber(factor) or 0 + + local results = AucAdvanced.API.QueryImage({ + itemId = itemId, + suffix = suffix, + factor = factor, + }) + local seen + local seentext = "" + if results[1] then + seen = results[1][Const.TIME] + end + if not seen then + local scanstats = AucAdvanced.API.GetScanStats() + if scanstats then + seen = scanstats.LastFullScan + seentext = _TRANS("APPR_Interface_NotInScan")..". " --Not seen in current scan + end + end + if not seen then seen = 0 end + if (time() - seen) < 60 then + seentext = seentext .. _TRANS('APPR_Interface_Data1MinOld') --Data is < 1 minute old + elseif ((time() - seen) / 3600) <= 48 then + seentext = seentext .. _TRANS('APPR_Interface_DataIsXOld'):format(SecondsToTime(time() - seen, true)) --Data is %s old + elseif seen == 0 then + seentext = _TRANS('APPR_Interface_NoDataFor').." "..string.sub(frame.salebox.name:GetText(), 12, -4) --No data for + else + seentext = seentext .. _TRANS('APPR_Interface_Data48HoursOld') --Data is > 48 hours old + end + frame.age:SetText(seentext) + + local itemkey = string.join(":", "item", itemId, "0", "0", "0", "0", "0", suffix, factor) + + local data = {} + local style = {} + for i = 1, #results do + local result = results[i] + local tLeft = result[Const.TLEFT] + if (tLeft == 1) then tLeft = "30m" + elseif (tLeft == 2) then tLeft = "2h" + elseif (tLeft == 3) then tLeft = "12h" + elseif (tLeft == 4) then tLeft = "48h" + end + local count = result[Const.COUNT] + data[i] = { + --result[Const.NAME], + result[Const.SELLER], + tLeft, + count, + math.floor(0.5+result[Const.MINBID]/count), + math.floor(0.5+result[Const.CURBID]/count), + math.floor(0.5+result[Const.BUYOUT]/count), + result[Const.MINBID], + result[Const.CURBID], + result[Const.BUYOUT], + result[Const.LINK] + } + local curbid = result[Const.CURBID] + if curbid == 0 then + curbid = result[Const.MINBID] + end + --price level color item + local r, g, b, Alpha1, Alpha2, direction = frame.SetPriceColor(itemkey, count, curbid, result[Const.BUYOUT]) + if direction and r then + style[i] = {} + style[i][1] = {} + style[i][1].rowColor = {r, g, b, Alpha1, Alpha2, direction} + end + --color ignored sellers + if AucAdvanced.Modules.Filter.Basic and AucAdvanced.Modules.Filter.Basic.IsPlayerIgnored and AucAdvanced.Modules.Filter.Basic.IsPlayerIgnored(result[Const.SELLER]) then + if not style[i] then style[i] = {} end + style[i][1] = {} + style[i][1].textColor = {1,0,0} + end + end + frame.refresh:Enable() + frame.imageview.sheet:SetData(data, style) + end + + function frame.SetPriceColor(itemID, count, requiredBid, buyoutPrice, rDef, gDef, bDef) + if get('util.appraiser.color') and AucAdvanced.Modules.Util.PriceLevel then + local _, link = GetItemInfo(itemID) + local _, _, r,g,b = AucAdvanced.Modules.Util.PriceLevel.CalcLevel(link, count, requiredBid, buyoutPrice) + + local direction = get("util.appraiser.colordirection") + if (direction == "LEFT") then + return r,g,b, 0, 0.2, "Horizontal" + elseif (direction == "RIGHT") then + return r,g,b, 0.2, 0, "Horizontal" + elseif (direction == "BOTTOM") then + return r,g,b, 0, 0.2, "Vertical" + elseif (direction == "TOP") then + return r,g,b, 0.2, 0, "Vertical" + else + return r,g,b + end + end + return rDef,gDef,bDef + end + + function frame.InitControls() + frame.valuecache = {} + + local curDuration = get('util.appraiser.item.'..frame.salebox.sig..".duration") or + get('util.appraiser.duration') or 2880 + + for i=1, #private.durations do + if (curDuration == private.durations[i][1]) then + frame.salebox.duration:SetValue(i) + frame.valuecache.duration = i + break + end + end + + local defStack = get("util.appraiser.stack") + if defStack == "max" then + defStack = frame.salebox.stacksize + elseif (not (tonumber(defStack))) then + defStack = frame.salebox.stacksize + set("util.appraiser.stack", "max") + end + defStack = tonumber(defStack) + if defStack > frame.salebox.stacksize then + defStack = frame.salebox.stacksize + end + local curStack = get('util.appraiser.item.'..frame.salebox.sig..".stack") or defStack + frame.salebox.stack:SetMinMaxValues(1, frame.salebox.stacksize) + frame.salebox.stack:SetValue(curStack) + frame.salebox.stackentry:SetNumber(curStack) + frame.valuecache.stack = frame.salebox.stack:GetValue() + frame.valuecache.stackentry = frame.salebox.stackentry:GetNumber() + + local defStack = get("util.appraiser.number") + if defStack == "maxplus" then + defStack = -1 + elseif defStack == "maxfull" then + defStack = -2 + elseif (not (tonumber(defStack))) then + defStack = -1 + set("util.appraiser.number", "maxplus") + else + defStack = tonumber(defStack) + end + local curNumber = get('util.appraiser.item.'..frame.salebox.sig..".number") or defStack + local range = math.max(curNumber, math.floor(frame.salebox.count/frame.salebox.stacksize)) + if frame.salebox.stacksize > 1 then + frame.salebox.number:SetAdjustedRange(range, -2, -1)--make sure the slider can handle the setting before we set it + else + frame.salebox.number:SetAdjustedRange(range, -1)--make sure the slider can handle the setting before we set it + end + frame.salebox.number:SetAdjustedValue(curNumber) + if curNumber == -2 then + frame.salebox.numberentry:SetText(_TRANS('APPR_Interface_Full') )--Full + elseif curNumber == -1 then + frame.salebox.numberentry:SetText(_TRANS('APPR_Interface_All') )--All + else + frame.salebox.numberentry:SetNumber(curNumber) + end + frame.valuecache.number = frame.salebox.number:GetAdjustedValue() + frame.valuecache.numberentry = frame.salebox.numberentry:GetText() + + -- Only post above number of items, no more. (ie. keep track of current auctions) + local curNumberOnly = get('util.appraiser.item.'..frame.salebox.sig..".numberonly") + if curNumberOnly == "on" then + frame.salebox.numberonly:SetChecked(true) + elseif curNumberOnly == "off" then + frame.salebox.numberonly:SetChecked(false) + else + frame.salebox.numberonly:SetChecked(curNumberOnly) + end + frame.valuecache.numberonly = frame.salebox.numberonly:GetChecked() + + local defMatch = get("util.appraiser.match") + local curMatch = get('util.appraiser.item.'..frame.salebox.sig..".match") + if curMatch == nil then + curMatch = defMatch + end + if curMatch == "on" then + frame.salebox.matcher:SetChecked(true) + elseif curMatch == "off" then + frame.salebox.matcher:SetChecked(false) + else + frame.salebox.matcher:SetChecked(curMatch) + end + frame.valuecache.matcher = frame.salebox.matcher:GetChecked() + + local ignore = get("util.appraiser.item."..frame.salebox.sig..".ignore") or false + frame.salebox.ignore:SetChecked(ignore) + frame.valuecache.ignore = frame.salebox.ignore:GetChecked() + + local curBulk = get('util.appraiser.item.'..frame.salebox.sig..".bulk") or false + frame.salebox.bulk:SetChecked(curBulk) + frame.valuecache.bulk = frame.salebox.bulk:GetChecked() + + + local curModel = get('util.appraiser.item.'..frame.salebox.sig..".model") or "default" + frame.salebox.model.value = curModel + frame.salebox.model:UpdateValue() + frame.valuecache.model = curModel + + frame.UpdatePricing() + frame.UpdateDisplay() + frame.salebox.config = nil + end + + function frame.ShowOwnAuctionDetails(itemString) + local colored = (get('util.appraiser.manifest.color') and AucAdvanced.Modules.Util.PriceLevel) + + local itemName, itemLink = GetItemInfo(itemString) + + local results = lib.ownResults[itemName] + local counts = lib.ownCounts[itemName] + + if counts and #counts>0 then + table.sort(counts) + + frame.manifest.lines:Add("") + frame.manifest.lines:Add("Own auctions: |cffffffff(price/each)", nil, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b) + + for _,count in ipairs(counts) do + local res = results[count] + local avgBid = res.countBid>0 and (res.sumBid / res.countBid) or nil + local avgBO = res.countBO>0 and (res.sumBO / res.countBO) or nil + + local r,g,b,_ + if colored then + _, _, r,g,b = AucAdvanced.Modules.Util.PriceLevel.CalcLevel(itemLink, 1, avgBid, avgBO) + end + r,g,b = r or 1,g or 1, b or 1 + + frame.manifest.lines:Add(format(" %2d lots of %2dx", res.stackCount, count).. + (avgBO and "" or " (bid)"), avgBO or avgBid, r,g,b) + end + end + end + + function frame.OnUpdate() + if frame.updated then + frame.CheckUpdates() + end + if frame.scanstatsEvent then + frame.GenerateList() + frame.scanstatsEvent = false + end + end + + --This gets run whenever a setting has been changed manually + --Each setting has its section to update other controls as needed, and update the saved and cached settings + --frame.UpdateDisplay() gets run at the end + --if the setting change might change the price, run frame.UpdatePricing() within the setting's section + function frame.CheckUpdates() + frame.updated = nil + if not frame.salebox.sig then return end + + local stack = frame.salebox.stack:GetValue() + local stackentry = frame.salebox.stackentry:GetNumber() + local number = frame.salebox.number:GetAdjustedValue() + local numberentry = frame.salebox.numberentry:GetText() + if stack ~= frame.valuecache.stack then + frame.valuecache.stack = stack + frame.valuecache.stackentry = stack + frame.salebox.stackentry:SetNumber(stack) + set("util.appraiser.item."..frame.salebox.sig..".stack", stack) + elseif stackentry ~= frame.valuecache.stackentry then + frame.salebox.stack:SetValue(stackentry) + stackentry = frame.salebox.stack:GetValue() + frame.salebox.stackentry:SetNumber(stackentry) + frame.valuecache.stack = stackentry + frame.valuecache.stackentry = stackentry + set("util.appraiser.item."..frame.salebox.sig..".stack", stackentry) + elseif number ~= frame.valuecache.number then + if number >= -2 and number < 0 then + if number == -2 then + frame.salebox.numberentry:SetText(_TRANS('APPR_Interface_Full') )--Full + else + frame.salebox.numberentry:SetText(_TRANS('APPR_Interface_All') )--All + end + else + frame.salebox.numberentry:SetNumber(number) + end + frame.valuecache.number = number + frame.valuecache.numberentry = frame.salebox.numberentry:GetText() + set("util.appraiser.item."..frame.salebox.sig..".number", number) + elseif numberentry ~= frame.valuecache.numberentry then + if numberentry:lower() == _TRANS('APPR_Interface_Full') then --Full + frame.salebox.number:SetAdjustedValue(-2) + numberentry = _TRANS('APPR_Interface_Full') --Full + set("util.appraiser.item."..frame.salebox.sig..".number", -2) + elseif numberentry:lower() == _TRANS('APPR_Interface_All') then --All + frame.salebox.number:SetAdjustedValue(-1) + numberentry = _TRANS('APPR_Interface_All') --All + set("util.appraiser.item."..frame.salebox.sig..".number", -1) + elseif tonumber(numberentry) == nil then --we've typed in a partial word. let them keep typing + else + numberentry = frame.salebox.numberentry:GetNumber() + if numberentry > frame.salebox.number.maxStax then + local n = #frame.salebox.number.extra + frame.salebox.number.maxStax = numberentry + frame.salebox.number:SetMinMaxValues(1, numberentry + n) + end + frame.salebox.number:SetAdjustedValue(numberentry) + set("util.appraiser.item."..frame.salebox.sig..".number", numberentry) + end + frame.salebox.numberentry:SetText(numberentry) + frame.valuecache.numberentry = frame.salebox.numberentry:GetText() + frame.valuecache.number = frame.salebox.number:GetAdjustedValue() + end + + local numberonly = frame.salebox.numberonly:GetChecked() + if numberonly ~= frame.valuecache.numberonly then + frame.valuecache.numberonly = numberonly + if numberonly then + numberonly = "on" + else + numberonly = "off" + end + set("util.appraiser.item."..frame.salebox.sig..".numberonly", numberonly) + end + + local ignore = frame.salebox.ignore:GetChecked() + if ignore ~= frame.valuecache.ignore then + frame.valuecache.ignore = ignore + if ignore then + ignore = "on" + else + ignore = "off" + end + set("util.appraiser.item."..frame.salebox.sig..".ignore", ignore) + frame.GenerateList() + end + + local bulk = frame.salebox.bulk:GetChecked() + if bulk ~= frame.valuecache.bulk then + frame.valuecache.bulk = bulk + if bulk then + bulk = "on" + else + bulk = "off" + end + set("util.appraiser.item."..frame.salebox.sig..".bulk", bulk) + + frame.GenerateList() + + end + + local duration = frame.salebox.duration:GetValue() + local matcher = frame.salebox.matcher:GetChecked() + local bid = MoneyInputFrame_GetCopper(frame.salebox.bid) + local buy = MoneyInputFrame_GetCopper(frame.salebox.buy) + local model = frame.salebox.model.value + if duration ~= frame.valuecache.duration then + frame.valuecache.duration = duration + set("util.appraiser.item."..frame.salebox.sig..".duration", private.durations[duration][1]) + frame.UpdatePricing() + elseif matcher ~= frame.valuecache.matcher then + frame.valuecache.matcher = matcher + if matcher then + matcher = "on" + else + matcher = "off" + end + set("util.appraiser.item."..frame.salebox.sig..".match", matcher) + frame.UpdatePricing() + elseif bid ~= frame.valuecache.bid then + frame.valuecache.bid = bid + frame.salebox.matcher:SetChecked(false) + frame.valuecache.matcher = frame.salebox.matcher:GetChecked() + set("util.appraiser.item."..frame.salebox.sig..".match", "off") + set("util.appraiser.item."..frame.salebox.sig..".model", "fixed") + frame.valuecache.model = "fixed" + frame.salebox.model.value = "fixed" + frame.salebox.model:UpdateValue() + set("util.appraiser.item."..frame.salebox.sig..".fixed.bid", bid) + set("util.appraiser.item."..frame.salebox.sig..".fixed.buy", buy) + frame.UpdatePricing() + elseif buy ~= frame.valuecache.buy then + frame.valuecache.buy = buy + frame.salebox.matcher:SetChecked(false) + frame.valuecache.matcher = frame.salebox.matcher:GetChecked() + set("util.appraiser.item."..frame.salebox.sig..".match", "off") + set("util.appraiser.item."..frame.salebox.sig..".model", "fixed") + frame.valuecache.model = "fixed" + frame.salebox.model.value = "fixed" + frame.salebox.model:UpdateValue() + set("util.appraiser.item."..frame.salebox.sig..".fixed.bid", bid) + set("util.appraiser.item."..frame.salebox.sig..".fixed.buy", buy) + frame.UpdatePricing() + elseif model ~= frame.valuecache.model then + set("util.appraiser.item."..frame.salebox.sig..".model", model) + frame.valuecache.model = model + if model == "fixed" then + frame.salebox.matcher:SetChecked(false) + frame.valuecache.matcher = frame.salebox.matcher:GetChecked() + set("util.appraiser.item."..frame.salebox.sig..".match", "off") + set("util.appraiser.item."..frame.salebox.sig..".fixed.bid", bid) + set("util.appraiser.item."..frame.salebox.sig..".fixed.buy", buy) + else + set("util.appraiser.item."..frame.salebox.sig..".fixed.bid") + set("util.appraiser.item."..frame.salebox.sig..".fixed.buy") + end + frame.UpdatePricing() + end + frame.UpdateDisplay() + end + + --Runs whenever the Pricing needs updating + --frame.UpdateDisplay() should be called after calling this function + function frame.UpdatePricing() + if not frame.salebox.sig then return end + local link = lib.GetLinkFromSig(frame.salebox.sig) + local buy, bid, _, _, curModelText + buy, bid, _, _, curModelText, MatchString = lib.GetPrice(link, nil, true) + if not MatchString then + MatchString = "" + end + + local stack = frame.salebox.stack:GetValue() or 1 + MoneyInputFrame_SetCopper(frame.salebox.buy.stack, buy*stack) + MoneyInputFrame_SetCopper(frame.salebox.bid.stack, bid*stack) + MoneyInputFrame_SetCopper(frame.salebox.buy, buy) + MoneyInputFrame_SetCopper(frame.salebox.bid, bid) + + frame.valuecache.bid = MoneyInputFrame_GetCopper(frame.salebox.bid) + frame.valuecache.buy = MoneyInputFrame_GetCopper(frame.salebox.buy) + frame.salebox.model:SetText(curModelText) + --frame.UpdateImage()--Why? I dont see a need to recreate the complete scrollsheet. + end + + --gets called whenever the display needs to be updated. + --except for when selecting or deselecting an item + --this function doesn't change any of the controls, merely the display + function frame.UpdateDisplay() + if (not frame.salebox.sig) then -- nothing selected + frame.salebox.icon:SetAlpha(0) + frame.salebox.stack:Hide() + frame.salebox.number:Hide() + frame.salebox.numberonly:Hide() + frame.salebox.stackentry:Hide() + frame.salebox.numberentry:Hide() + frame.salebox.model:Hide() + frame.salebox.matcher:Hide() + frame.salebox.bid:Hide() + frame.salebox.buy:Hide() + --Stack saleboxes + frame.salebox.bid.stack:Hide() + frame.salebox.buy.stack:Hide() + frame.salebox.duration:Hide() + frame.manifest.lines:Clear() + frame.manifest:Hide() + frame.toggleManifest:Disable() + frame.age:SetText("") + frame.go:Disable() + frame.salebox.ignore:Hide() + frame.salebox.warn:SetText("") + frame.salebox.bulk:Hide() + frame.switchToStack:Hide() + frame.switchToStack2:Hide() + return + end + frame.salebox.icon:SetAlpha(1) + frame.salebox.matcher:Show() + --hides/shows the stack price money entry or per item money entry boxes + if get("util.appraiser.classic") then + frame.salebox.bid.stack:Show() + frame.salebox.buy.stack:Show() + frame.salebox.bid:Hide() + frame.salebox.buy:Hide() + else + frame.salebox.bid:Show() + frame.salebox.buy:Show() + frame.salebox.bid.stack:Hide() + frame.salebox.buy.stack:Hide() + end + frame.switchToStack:Show() + frame.switchToStack2:Show() + + frame.salebox.model:Show() + frame.salebox.duration:Show() + frame.salebox.numberonly:Show() + frame.manifest.lines:Clear() + frame.manifest:SetFrameLevel(AuctionFrame:GetFrameLevel()) + + frame.salebox.ignore:Show() + frame.salebox.bulk:Show() + if not frame.selectedPostable then + frame.salebox.number:Hide() + frame.salebox.stack:Hide() + frame.salebox.stackentry:Hide() + frame.salebox.numberentry:Hide() + else + frame.salebox.number:Show() + frame.salebox.stack:Show() + frame.salebox.stackentry:Show() + frame.salebox.numberentry:Show() + end + + frame.toggleManifest:Enable() + if frame.toggleManifest:GetText() == "Close Sidebar" then + frame.manifest:Show() + end + + frame.refresh:Enable() + local matchers = get("matcherlist") + if not matchers or #matchers == 0 then + frame.salebox.matcher:Disable() + frame.salebox.matcher.label:SetTextColor(.5, .5, .5) + else + frame.salebox.matcher:Enable() + frame.salebox.matcher.label:SetTextColor(1, 1, 1) + end + + local itemId, suffix, factor = strsplit(":", frame.salebox.sig) + itemId = tonumber(itemId) + suffix = tonumber(suffix) or 0 + factor = tonumber(factor) or 0 + + local itemKey = string.join(":", "item", itemId, "0", "0", "0", "0", "0", suffix, factor) + + local curDurationIdx = frame.salebox.duration:GetValue() or 3 + local curDurationMins = private.durations[curDurationIdx][1] + local curDurationText = private.durations[curDurationIdx][2] + frame.salebox.duration.label:SetText(_TRANS('APPR_Interface_Duration: %s'):format(curDurationText))--Duration: %s + + local curIgnore = frame.salebox.ignore:GetChecked() + frame.salebox.icon:GetNormalTexture():SetDesaturated(curIgnore) + + local curModel = get('util.appraiser.item.'..frame.salebox.sig..".model") or "default" + local curBid = MoneyInputFrame_GetCopper(frame.salebox.bid) or 0 + local curBuy = MoneyInputFrame_GetCopper(frame.salebox.buy) or 0 + + local sig = frame.salebox.sig + local totalBid, totalBuy = 0,0 + local totalDeposit + local bidVal, buyVal, depositVal + + local r,g,b,a = 0,0,0,0 + local colored = get('util.appraiser.manifest.color') + local tinted = get('util.appraiser.tint.color') + if tinted then + r,g,b = frame.SetPriceColor(itemKey, 1, curBuy, curBuy, r,g,b) + if r then a = 0.4 end + end + AppraiserSaleboxBuyGold:SetBackdropColor(r,g,b, a) + AppraiserSaleboxBuySilver:SetBackdropColor(r,g,b, a) + AppraiserSaleboxBuyCopper:SetBackdropColor(r,g,b, a) + + AppraiserSaleboxBuyStackGold:SetBackdropColor(r,g,b, a) + AppraiserSaleboxBuyStackSilver:SetBackdropColor(r,g,b, a) + AppraiserSaleboxBuyStackCopper:SetBackdropColor(r,g,b, a) + + r,g,b,a=0,0,0,0 + if tinted then + r,g,b = frame.SetPriceColor(itemKey, 1, curBid, curBid, r,g,b) + if r then a=0.4 end + end + AppraiserSaleboxBidGold:SetBackdropColor(r,g,b, a) + AppraiserSaleboxBidSilver:SetBackdropColor(r,g,b, a) + AppraiserSaleboxBidCopper:SetBackdropColor(r,g,b, a) + + AppraiserSaleboxBidStackGold:SetBackdropColor(r,g,b, a) + AppraiserSaleboxBidStackSilver:SetBackdropColor(r,g,b, a) + AppraiserSaleboxBidStackCopper:SetBackdropColor(r,g,b, a) + + if frame.selectedPostable then + local curNumber = frame.salebox.number:GetAdjustedValue() + -- used in GetDepositCost calls: + local depositHours = curDurationMins / 60 + local depositFaction = AucAdvanced.GetFactionGroup() + + if frame.salebox.stacksize > 1 then + local count = frame.salebox.count + + local curSize = frame.salebox.stack:GetValue() + local extra = "" + local maxStax = math.floor(count / curSize) + local fullPop = maxStax*curSize + local remain = count - fullPop + --we don't want to lose any saved settings, so don't let the maxStax get below the saved value + local SavedNumber = get('util.appraiser.item.'..frame.salebox.sig..".number") or 0 + if (tonumber(SavedNumber) > 0) and SavedNumber > maxStax then + maxStax = SavedNumber + end + + if (curSize > count) then + extra = " |cffffaa40" .. _TRANS('APPR_Interface_StackGreaterAvailable') --(Stack > Available) + elseif ((curSize * maxStax) > count) then + extra = " |cffffaa40" .. _TRANS('APPR_Interface_NumberGreaterAvailable') --(Number > Available) + end + frame.salebox.stack.label:SetText(_TRANS('APPR_Interface_StackSize'):format(curSize)..extra)--Stack size: %d + frame.salebox.number:SetAdjustedRange(maxStax, -2, -1) + if (curNumber >= -2 and curNumber < 0) then + if (curNumber == -2) then + frame.salebox.number.label:SetText(_TRANS('APPR_Interface_NumberAllFullStacks'):format(maxStax, fullPop))--Number: All full stacks (%d) = %d + else + frame.salebox.number.label:SetText(_TRANS('APPR_Interface_NumberAllStacksPlus'):format(maxStax, remain, count))--Number: All stacks (%d) plus %d = %d + end + if (maxStax > 0) then + frame.manifest.lines:Clear() + frame.manifest.lines:Add(_TRANS('APPR_Interface_LotsOfStacks'):format(maxStax, curSize))--%d lots of %dx stacks: + bidVal = lib.RoundBid(curBid * curSize) + buyVal = lib.RoundBuy(curBuy * curSize) + depositVal = GetDepositCost(frame.salebox.link, depositHours, depositFaction, curSize) + + r,g,b=nil,nil,nil + if colored then + r,g,b = frame.SetPriceColor(itemKey, curSize, bidVal, bidVal) + end + frame.manifest.lines:Add(" ".._TRANS('APPR_Interface_BidForX'):format(curSize), bidVal, r,g,b)--Bid for %dx + r,g,b=nil,nil,nil + if colored then + r,g,b = frame.SetPriceColor(itemKey, curSize, buyVal, buyVal) + end + frame.manifest.lines:Add(" ".._TRANS('APPR_Interface_BuyoutForX'):format(curSize), buyVal, r,g,b)--Buyout for %dx + if depositVal then + frame.manifest.lines:Add(" ".._TRANS('APPR_Interface_DepositForX'):format(curSize), depositVal)--Deposit for %dx + totalDeposit = depositVal * maxStax + end + totalBid = totalBid + (bidVal * maxStax) + totalBuy = totalBuy + (buyVal * maxStax) + end + if curNumber == -1 and remain > 0 then + bidVal = lib.RoundBid(curBid * remain) + buyVal = lib.RoundBuy(curBuy * remain) + depositVal = GetDepositCost(frame.salebox.link, depositHours, depositFaction, remain) + + frame.manifest.lines:Add(_TRANS('APPR_Interface_LotsOfStacks') :format(1, remain))--%d lots of %dx stacks: + r,g,b=nil,nil,nil + if colored then + r,g,b = frame.SetPriceColor(itemKey, remain, bidVal, bidVal) + end + frame.manifest.lines:Add(" ".._TRANS('APPR_Interface_BidForX'):format(remain), bidVal, r,g,b)--Bid for %dx + r,g,b=nil,nil,nil + if colored then + r,g,b = frame.SetPriceColor(itemKey, remain, buyVal, buyVal) + end + frame.manifest.lines:Add(" ".._TRANS('APPR_Interface_BuyoutForX'):format(remain), buyVal, r,g,b)--Buyout for %dx + if depositVal then + frame.manifest.lines:Add(" ".._TRANS('APPR_Interface_DepositForX'):format(remain), depositVal)--Deposit for %dx + totalDeposit = (totalDeposit or 0) + depositVal + end + totalBid = totalBid + bidVal + totalBuy = totalBuy + buyVal + end + else + frame.salebox.number.label:SetText(_TRANS('APPR_Interface_NumberStacks'):format(curNumber, curNumber*curSize))--Number: %d stacks = %d + frame.manifest.lines:Clear() + frame.manifest.lines:Add(_TRANS('APPR_Interface_LotsOfStacks'):format(curNumber, curSize))--%d lots of %dx stacks: + bidVal = lib.RoundBid(curBid * curSize) + buyVal = lib.RoundBuy(curBuy * curSize) + depositVal = GetDepositCost(frame.salebox.link, depositHours, depositFaction, curSize) + + r,g,b=nil,nil,nil + if colored then + r,g,b = frame.SetPriceColor(itemKey, curSize, bidVal, bidVal) + end + frame.manifest.lines:Add((" ".._TRANS('APPR_Interface_BidForX')):format(curSize), bidVal, r,g,b)--Bid for %dx + r,g,b=nil,nil,nil + if colored then + r,g,b = frame.SetPriceColor(itemKey, curSize, buyVal, buyVal) + end + frame.manifest.lines:Add((" ".._TRANS('APPR_Interface_BuyoutForX')):format(curSize), buyVal, r,g,b)--Buyout for %dx + if depositVal then + frame.manifest.lines:Add((" ".._TRANS('APPR_Interface_DepositForX')):format(curSize), depositVal)--Deposit for %dx + totalDeposit = depositVal * curNumber + end + totalBid = totalBid + (bidVal * curNumber) + totalBuy = totalBuy + (buyVal * curNumber) + end + else -- non-stackable + frame.salebox.stack.label:SetText(_TRANS('APPR_Interface_NotStackable')) --Item is not stackable + local maxStax = frame.salebox.count + local SavedNumber = get('util.appraiser.item.'..frame.salebox.sig..".number") or 0 + if (tonumber(SavedNumber) > 0) and SavedNumber > maxStax then + maxStax = SavedNumber + end + frame.salebox.number:SetAdjustedRange(maxStax, -1) + if (curNumber == -1) then + curNumber = frame.salebox.count + frame.salebox.number.label:SetText(_TRANS('APPR_Interface_NumberAllItems'):format(curNumber))--Number: All items = %d + else + frame.salebox.number.label:SetText(_TRANS('APPR_Interface_NumberItems'):format(curNumber))--Number: %d items + end + if curNumber > 0 then + frame.manifest.lines:Clear() + frame.manifest.lines:Add(_TRANS('APPR_Interface_Items'):format(curNumber))--%d items + bidVal = lib.RoundBid(curBid) + buyVal = lib.RoundBuy(curBuy) + depositVal = GetDepositCost(frame.salebox.link, depositHours, depositFaction) + + r,g,b=nil,nil,nil + if colored then + r,g,b = frame.SetPriceColor(itemKey, 1, bidVal, bidVal) + end + frame.manifest.lines:Add(" ".._TRANS('APPR_Interface_Bid/item'), bidVal, r,g,b)--Bid /item + r,g,b=nil,nil,nil + if colored then + r,g,b = frame.SetPriceColor(itemKey, 1, buyVal, buyVal) + end + frame.manifest.lines:Add(" ".._TRANS('APPR_Interface_Buyout/item'), buyVal, r,g,b)--Buyout /item + if depositVal then + frame.manifest.lines:Add(" ".._TRANS('APPR_Interface_Deposit/item'), depositVal)--Deposit /item + totalDeposit = depositVal * curNumber + end + totalBid = totalBid + (bidVal * curNumber) + totalBuy = totalBuy + (buyVal * curNumber) + end + end + frame.manifest.lines:Add(_TRANS('APPR_Interface_Totals') )--Totals: + frame.manifest.lines:Add(" ".._TRANS('APPR_Interface_TotalBid'), totalBid)--Total Bid: + frame.manifest.lines:Add(" ".._TRANS('APPR_Interface_TotalBuyout'), totalBuy)--Total Buyout: + if totalDeposit then + frame.manifest.lines:Add(" ".._TRANS('APPR_Interface_TotalDeposit'), totalDeposit)--Total Deposit: + else + frame.manifest.lines:Add(" ".._TRANS('APPR_Interface_UnknownDeposit'))--Unknown deposit cost + end + if (frame.salebox.matcher:GetChecked() and (frame.salebox.matcher:IsEnabled()==1) and (DiffFromModel)) then + local MatchStringList = {strsplit("\n", MatchString)} + for i in pairs(MatchStringList) do + frame.manifest.lines:Add((MatchStringList[i])) + end + end + + if (totalBid < 1) then + frame.manifest.lines:Add(("------------------------------")) + frame.manifest.lines:Add(_TRANS('APPR_Interface_NoteNoAuctionableItems') )--Note: No auctionable items + end + end + + frame.ShowOwnAuctionDetails(itemKey) -- Adds lines to frame.manifest + + frame.salebox.warn:SetText("") + local warnvendor + if GetSellValue then + local sellValue = GetSellValue(frame.salebox.link) + if (sellValue and sellValue > 0) then + sellValue = sellValue + 1 -- the curBuy/curBid have been rounded up earlier, and they MAY be based on vendor values! + sellValue = math.ceil(sellValue / (1 - (AucAdvanced.cutRate or 0.05))) + if curBuy > 0 and curBuy <= sellValue then + warnvendor = "buyout" + elseif curBid > 0 and curBid <= sellValue then + warnvendor = "bid" + end + end + end + + local canAuction = true + if curModel == "fixed" and curBid <= 0 then + frame.salebox.warn:SetText(_TRANS('APPR_Interface_BidPriceMustGreater') )--Bid price must be > 0 + canAuction = false + elseif (curBuy > 0 and curBid > curBuy) then + frame.salebox.warn:SetText(_TRANS('APPR_Interface_BuyPriceMustGreater') )--Buy price must be > bid + canAuction = false + elseif warnvendor == "buyout" then + frame.salebox.warn:SetText("|cffff8010".._TRANS('APPR_Interface_NoteBuyoutLessVendor'))--Note: Buyout <= Vendor + elseif warnvendor == "bid" then + frame.salebox.warn:SetText("|cffeec900".._TRANS('APPR_Interface_NoteMinBidLessVendor'))--Note: Min Bid <= Vendor + else + frame.salebox.warn:SetText("") + end + + if totalBid < 1 then + canAuction = false + end + + if not frame.selectedPostable then + canAuction = false + end + + if canAuction then + frame.go:Enable() + else + frame.go:Disable() + end + end + + function frame.ChangeUI() + if get("util.appraiser.classic") then + --Show per stack + frame.switchToStack:SetText("Bid per Stack") + frame.switchToStack2:SetText("Buy per Stack") + + frame.salebox.bid:Hide() + frame.salebox.buy:Hide() + frame.salebox.bid.stack:Show() + frame.salebox.buy.stack:Show() + frame.salebox:SetBackdropColor(0.1, 0.5, 0.9, 1) + else + --Show per each + frame.switchToStack:SetText(_TRANS('APPR_Interface_BidPerItem') ) --Bid per item: + frame.switchToStack2:SetText(_TRANS('APPR_Interface_BuyPerItem') )--Buy per item: + + frame.salebox.bid:Show() + frame.salebox.buy:Show() + frame.salebox.bid.stack:Hide() + frame.salebox.buy.stack:Hide() + frame.salebox:SetBackdropColor(0, 0, 0, 0.8) + end + + frame.UpdateDisplay() + end + --syncs the stack and single item input boxes, + --only the visible frame fires events + function frame.SyncMoneyFrameSingleBid() + local stack = frame.salebox.stack:GetValue() + local bidStack = MoneyInputFrame_GetCopper(frame.salebox.bid.stack) + MoneyInputFrame_SetCopper(frame.salebox.bid, bidStack/stack) + end + function frame.SyncMoneyFrameSingleBuy() + local stack = frame.salebox.stack:GetValue() + local buyStack = MoneyInputFrame_GetCopper(frame.salebox.buy.stack) + MoneyInputFrame_SetCopper(frame.salebox.buy, buyStack/stack) + end + --Syncs single frame value changes to stack frame + function frame.SyncMoneyFrameStackBid() + local stack = frame.salebox.stack:GetValue() + local bid = MoneyInputFrame_GetCopper(frame.salebox.bid) + MoneyInputFrame_SetCopper(frame.salebox.bid.stack, bid*stack) + end + function frame.SyncMoneyFrameStackBuy() + local stack = frame.salebox.stack:GetValue() + local buy = MoneyInputFrame_GetCopper(frame.salebox.buy) + MoneyInputFrame_SetCopper(frame.salebox.buy.stack, buy*stack) + end + + function frame.GetItemByLink(link) + local sig = SigFromLink(link) + assert(sig, "Item must be a valid link") + for i = 1, #(frame.list) do + if frame.list[i] then + if frame.list[i][1] == sig then + local obj = {} + obj.id = i + local pos = math.floor(frame.scroller:GetValue()) + obj.id = obj.id - pos + frame.SelectItem(obj) + frame.scroller:SetValue(i-(NUM_ITEMS*(i/#frame.list))) + return + end + end + end +-- frame.DirectSelect(link) + frame.SelectItem(nil,nil,link) + end + + function frame.IconClicked() + local objtype, _, itemlink = GetCursorInfo() + ClearCursor() + if objtype == "item" then + frame.GetItemByLink(itemlink) + else + if not get("util.appraiser.classic") then + frame.salebox.ignore:SetChecked(not frame.salebox.ignore:GetChecked()) + frame.updated = true + end + end + end + + function frame.ToggleDisabled() + if not frame.salebox.sig then return end + local curDisable = get('util.appraiser.item.'..frame.salebox.sig..".ignore") or false + set('util.appraiser.item.'..frame.salebox.sig..".ignore", not curDisable) + frame.GenerateList() + end + + function frame.RefreshView(background,link) + if not link then + link = frame.salebox.link + if not link then + -- The user attempted a single-item refresh without selecting anything, just re-enable the button and return. + aucPrint(_TRANS('APPR_Interface_NoItemsSelected') )--No items were selected for refresh. + frame.refresh:Enable() + return + -- else + -- aucPrint(("Got link from salebox: {{%s}}"):format(link)) + end + -- else + -- aucPrint(("Got link from parameter: {{%s}}"):format(link)) + end + local name, _, rarity, _, itemMinLevel, itemType, itemSubType, stack = GetItemInfo(link) + local itemTypeId, itemSubId + for catId, catName in pairs(AucAdvanced.Const.CLASSES) do + if catName == itemType then + itemTypeId = catId + for subId, subName in pairs(AucAdvanced.Const.SUBCLASSES[itemTypeId]) do + if subName == itemSubType then + itemSubId = subId + break + end + end + break + end + end + aucPrint(_TRANS('APPR_Interface_RefreshingView') :format(name))--Refreshing view of {{%s}} + if background and type(background) == 'boolean' then + AucAdvanced.Scan.StartPushedScan(name, itemMinLevel, itemMinLevel, nil, itemTypeId, itemSubId, nil, rarity) + else + AucAdvanced.Scan.PushScan() + AucAdvanced.Scan.StartScan(name, itemMinLevel, itemMinLevel, nil, itemTypeId, itemSubId, nil, rarity) + end + end + + function frame.RefreshAll() + local bg = false + for i = 1, #(frame.list) do + local item = frame.list[i] + if item then + local link = item[7] + frame.RefreshView(bg, link) + bg = true + end + end + end + + -- We use this to make sure the correct number of parameters are passed to RefreshView; otherwise, we can end up with e.g. link="LeftButton". + function frame.SmartRefresh() + frame.refresh:Disable() + if (not IsAltKeyDown()) then + frame.RefreshView() + else + frame.RefreshAll() + end + end + + function frame.PostAuctions(obj) + + local postType = obj.postType + if postType == "single" then + frame.PostBySig(frame.salebox.sig, nil, true) -- singleclick flag for hardware event + elseif postType == "batch" then + if not IsModifierKeyDown() and GetMouseButtonClicked() ~= "RightButton" then + message(_TRANS('APPR_Interface_BatchButtonCombination') )--This button requires you to press a combination of keys when clicked.\nSee help printed in the chat frame for further details. + aucPrint(_TRANS('APPR_Help_BatchPostHelp1') )--The batch post mechanism will allow you to perform automated actions on all the items in your inventory marked for batch posting. + aucPrint(_TRANS('APPR_Help_BatchPostHelp2') )--You must hold down one of the following keys when you click the button: + aucPrint(" ".._TRANS('APPR_Help_BatchPostHelp3') )--- Alt = Auto-refresh all batch postable items. + aucPrint(" ".._TRANS('APPR_Help_BatchPostHelp4') )--- Shift = List all auctions that would be posted without actually posting them. + aucPrint(" ".._TRANS('APPR_Help_BatchPostHelp5') )--- RightClick = Auto-post all batch postable items. + aucPrint(" ".._TRANS('APPR_Help_BatchPostHelp6') )--- Alt+RightClick on an item to toggle batch post on/off + return + end + + local a = IsAltKeyDown() + local s = IsShiftKeyDown() + local c = IsControlKeyDown() + + local mode + + -- Keeping old ability for Ctrl+Alt+Shift for users used to using this modifer setup. + if (a and c and s) or (GetMouseButtonClicked() == "RightButton") then mode = "autopost" end + if a and not c and not s then mode = "refresh" end + if not a and not c and s then mode = "list" end + + if not mode then + message(_TRANS('APPR_Help_BatchUnknownKeyCombo') )--Unknown key combination pressed while clicking batch post button. + return + end + + if mode == "list" then + aucPrint(_TRANS('APPR_Help_BatchFollowingWouldPosted'))--The following items would have be auto-posted: + end + + local bg = false + local obj = {} + for i = 1, #(frame.list) do + local item = frame.list[i] + if item then + local sig = item[1] + if get('util.appraiser.item.'..sig..".bulk") then + if mode == "autopost" then + -- Auto post these items + frame.PostBySig(sig) + elseif mode == "list" then + -- List these items + frame.PostBySig(sig, true) + elseif mode == "refresh" then + -- Refresh these items + local link = item[7] + frame.RefreshView(bg, link) + bg = true + end + end + end + end + end + end + + function frame.PostBySig(sig, dryRun, singleclick) + local link, itemName = AucAdvanced.Modules.Util.Appraiser.GetLinkFromSig(sig) + local total, _, unpostable = AucAdvanced.Post.CountAvailableItems(sig) + if not (link and total) then + UIErrorsFrame:AddMessage(_TRANS('APPR_Interface_UnablePostAuctions') )--Unable to post auctions at this time + aucPrint(_TRANS('APPR_Help_CannotPostAuctions'), "Invalid item sig")--Cannot post auctions: + return + end + local itemBuy, itemBid, _, _, _, _, stack, number, duration = AucAdvanced.Modules.Util.Appraiser.GetPrice(link, nil, true) + local numberOnly = get('util.appraiser.item.'..sig..".numberonly") + + + -- Just a quick bit of sanity checking first + + if not (stack and stack >= 1) then + aucPrint(_TRANS('APPR_Help_SkippingNoStackSize'):format(link) )--Skipping %s: no stack size set + return + elseif (not number) or number < -2 or number == 0 then + aucPrint(_TRANS('APPR_Help_SkippingInvalidNumberStacks'):format(link) )--Skipping %s: invalid number of stacks/items set + return + elseif (not itemBid) or itemBid <= 0 then + aucPrint(_TRANS('APPR_Help_SkippingNoBidValue'):format(link) )--Skipping %s: no bid value set + return + elseif not (itemBuy and (itemBuy == 0 or itemBuy >= itemBid)) then + aucPrint(_TRANS('APPR_Help_SkippingInvalidBuyoutValue'):format(link) )--Skipping %s: invalid buyout value + return + elseif not (duration and (duration == 720 or duration == 1440 or duration == 2880)) then + aucPrint(_TRANS('APPR_Help_SkippingInvalidDuration'):format(link).." "..tostring(duration) )--Skipping %s: invalid duration: + return + elseif total == 0 then + if unpostable > 0 then + aucPrint(_TRANS('APPR_Help_SkippingNoAuctionableItem'):format(link) )--Skipping %s: no auctionable item in bags. May need to repair item + return + else + aucPrint(_TRANS('APPR_Help_SkippingNotEnoughItems'):format(link) )--Skipping %s: You do not have enough items to do that + return + end + elseif (number > 0 and number * stack > total) and not numberOnly then + aucPrint(_TRANS('APPR_Help_SkippingNotEnoughItems'):format(link) )--Skipping %s: You do not have enough items to do that + return + elseif (number ~= -1) and (stack > total) then + aucPrint(_TRANS('APPR_Help_SkippingStackLargerAvailable'):format(link) )--Skipping %s: Stack size larger than available + return + end + if numberOnly and number>0 then + -- get current number of posted auctions + local counts = AucAdvanced.Modules.Util.Appraiser.ownCounts[itemName] + local results = AucAdvanced.Modules.Util.Appraiser.ownResults[itemName] + local currentStackCount = 0 + if counts and #counts>0 then + for _,count in ipairs(counts) do + local res = results[count] + currentStackCount = currentStackCount + res.stackCount + end + end + -- reduce number to post by existing amount + number = number - currentStackCount + if number < 1 then + aucPrint(_TRANS('APPR_Help_StacksAreadyPosted'):format(currentStackCount, link))--%d stacks of %s already posted. + return + end + if number*stack > total then + aucPrint(_TRANS('APPR_Help_NeedOnlyHavePosting'):format(number*stack, link, total, math.floor(total/stack)*stack))--Need %d of %s only have %d, posting %d + number = math.floor(total/stack) + end + end + + aucPrint(_TRANS('APPR_Help_PostingBatch'):format(link))--Posting batch of: %s + + aucPrint(" ".._TRANS('APPR_Help_Duration'):format(duration/60))--- Duration: {{%d hours}} + + local bidVal, buyVal + local totalBid, totalBuy, totalNum = 0,0,0 + + if (stack > 1) then + local fullStacks = math.floor(total / stack) + local fullPop = fullStacks * stack + local remain = total - fullPop + + if (number < 0) then + if (fullStacks > 0) then + bidVal = lib.RoundBid(itemBid * stack) + buyVal = lib.RoundBuy(itemBuy * stack) + if (buyVal ~= 0 and bidVal > buyVal) then buyVal = bidVal end + if dryRun then + aucPrint(" ".._TRANS('APPR_Help_PretendingPostStacks'):format(fullStacks, stack, AucAdvanced.Coins(bidVal, true), AucAdvanced.Coins(buyVal, true)))--- Pretending to post {{%d}} stacks of {{%d}} at {{%s}} min and {{%s}} buyout per stack + else + aucPrint(" ".._TRANS('APPR_Help_QueueingLots'):format(fullStacks, stack))--- Queueing {{%d}} lots of {{%d}} + if singleclick then + singleclick = nil + AucAdvanced.Post.PostAuctionClick(sig, stack, bidVal, buyVal, duration, fullStacks) + else + AucAdvanced.Post.PostAuction(sig, stack, bidVal, buyVal, duration, fullStacks) + end + end + + totalBid = totalBid + (bidVal * fullStacks) + totalBuy = totalBuy + (buyVal * fullStacks) + totalNum = totalNum + (stack * fullStacks) + end + if (number == -1 and remain > 0) then + bidVal = lib.RoundBid(itemBid * remain) + buyVal = lib.RoundBuy(itemBuy * remain) + if (buyVal ~= 0 and bidVal > buyVal) then buyVal = bidVal end + if dryRun then + aucPrint(" ".._TRANS('APPR_Help_PretendingPostStacks'):format(1, remain, AucAdvanced.Coins(bidVal, true), AucAdvanced.Coins(buyVal, true)))--- Pretending to post {{%d}} stacks of {{%d}} at {{%s}} min and {{%s}} buyout per stack + else + aucPrint(" ".._TRANS('APPR_Help_QueueingLots'):format(1, remain))--- Queueing {{%d}} lots of {{%d}} + if singleclick then + singleclick = nil + AucAdvanced.Post.PostAuctionClick(sig, remain, bidVal, buyVal, duration) + else + AucAdvanced.Post.PostAuction(sig, remain, bidVal, buyVal, duration) + end + end + + totalBid = totalBid + bidVal + totalBuy = totalBuy + buyVal + totalNum = totalNum + remain + end + else + bidVal = lib.RoundBid(itemBid * stack) + buyVal = lib.RoundBuy(itemBuy * stack) + if (buyVal ~= 0 and bidVal > buyVal) then buyVal = bidVal end + if dryRun then + aucPrint(" ".._TRANS('APPR_Help_PretendingPostStacks'):format(number, stack, AucAdvanced.Coins(bidVal, true), AucAdvanced.Coins(buyVal, true)))--- Pretending to post {{%d}} stacks of {{%d}} at {{%s}} min and {{%s}} buyout per stack + else + aucPrint(" ".._TRANS('APPR_Help_QueueingLots'):format(number, stack))--- Queueing {{%d}} lots of {{%d}} + if singleclick then + singleclick = nil + AucAdvanced.Post.PostAuctionClick(sig, stack, bidVal, buyVal, duration, number) + else + AucAdvanced.Post.PostAuction(sig, stack, bidVal, buyVal, duration, number) + end + end + + totalBid = totalBid + (bidVal * number) + totalBuy = totalBuy + (buyVal * number) + totalNum = totalNum + (stack * number) + end + else + if number < 0 then number = total end + bidVal = lib.RoundBid(itemBid) + buyVal = lib.RoundBuy(itemBuy) + if (buyVal ~= 0 and bidVal > buyVal) then buyVal = bidVal end + if dryRun then + aucPrint(_TRANS('APPR_Help_PretendingPostStacks'):format(number, stack, AucAdvanced.Coins(bidVal, true), AucAdvanced.Coins(buyVal, true)))--- Pretending to post {{%d}} stacks of {{%d}} at {{%s}} min and {{%s}} buyout per stack + else + aucPrint(_TRANS('APPR_Help_QueueingItems'):format(number))--- Queueing {{%d}} items + if singleclick then + singleclick = nil + AucAdvanced.Post.PostAuctionClick(sig, 1, bidVal, buyVal, duration, number) + else + AucAdvanced.Post.PostAuction(sig, 1, bidVal, buyVal, duration, number) + end + end + + totalBid = totalBid + (bidVal * number) + totalBuy = totalBuy + (buyVal * number) + totalNum = totalNum + number + end + + aucPrint("-----------------------------------") + if dryRun then + aucPrint(_TRANS('APPR_Help_PretendedItems'):format(totalNum))--Pretended {{%d}} items + else + aucPrint(_TRANS('APPR_Help_QueuedUpItems'):format(totalNum))--Queued up {{%d}} items + end + aucPrint(_TRANS('APPR_Help_TotalMinbidValue'):format(AucAdvanced.Coins(totalBid, true)))--Total minbid value: %s' + aucPrint(_TRANS('APPR_Help_TotalBuyoutValue'):format(AucAdvanced.Coins(totalBuy, true)))--Total buyout value: %s + aucPrint("-----------------------------------") + end + + function frame.SetScroll(...) + local pos = floor(frame.scroller:GetValue()) + for i = 1, NUM_ITEMS do + local item = frame.list[pos+i] + local button = frame.items[i] + if item then + local curIgnore = item.ignore + local curAuction = item.auction + local curBulk = get('util.appraiser.item.'..item[1]..".bulk") or false + + button.icon:SetTexture(item[3]) + button.icon:SetDrawLayer("ARTWORK"); + button.icon:SetDesaturated(curIgnore) + + local _,_,_, hex = GetItemQualityColor(item[4]) + local stackX = "x " + if curAuction then + stackX = "" + end + + if curIgnore then + hex = "|cff444444" + stackX = hex..stackX + end + + if curBulk then + button.batchTex:Show(); + else + button.batchTex:Hide(); + end + + button.name:SetText(hex.."["..item[2].."]|r") + + button.size:SetText(stackX..item[6]) + + if curAuction then + button.size:SetAlpha(0.7) + else + button.size:SetAlpha(1) + end + + local info = "" + if frame.cache[item[1]] and not curIgnore then + local exact, suffix, base, dist = unpack(frame.cache[item[1]]) + info = "Counts: "..exact.." +"..suffix.." +"..base + if (dist) then + info = AucAdvanced.Modules.Util.ScanData.Colored(true, dist, nil, true) -- use shortened format + end + end + + button.info:SetText(info) + + local background = button.bg + local alpha = 0.2 + + if curAuction then + background:SetVertexColor(0.9,0.3,0) -- very dark red + else + background:SetVertexColor(1,1,1) + end + + if (item[1] == frame.selected) then + alpha = 0.6 + elseif curIgnore then + alpha = 0.1 + end + + background:SetAlpha(alpha) + background:SetDesaturated(curIgnore) + + button:Show() + else + button:Hide() + end + end + end + + function frame.SetButtonTooltip(self, text) + if self and text and get("util.appraiser.buttontips") then + GameTooltip:SetOwner(self, "ANCHOR_BOTTOMRIGHT") + GameTooltip:SetText(text) + end + end + + frame.DoTooltip = function(self) + if not self.id then self = self:GetParent() end + if self.id then --we're mousing over the itemlist + local id = self.id + local pos = math.floor(frame.scroller:GetValue()) + local item = frame.list[pos + id] + if item then + --local name = item[2] + local link = item[7] + local count = item[6] + GameTooltip:SetOwner(frame.itembox, "ANCHOR_NONE") + AucAdvanced.ShowItemLink(GameTooltip, link, count) + GameTooltip:ClearAllPoints() + GameTooltip:SetPoint("TOPLEFT", frame.itembox, "TOPRIGHT", 10, 0) + end + else --we're mousing over the itemslot + if frame.salebox.sig then + local link = frame.salebox.link + local count = frame.salebox.count + --local _,name = strsplit("[",(strsplit("]",frame.salebox.name:GetText()))) --isolates the text between the [] + GameTooltip:SetOwner(frame.salebox.icon, "ANCHOR_NONE") + AucAdvanced.ShowItemLink(GameTooltip, link, count) + GameTooltip:ClearAllPoints() + GameTooltip:SetPoint("TOPRIGHT", frame.salebox.icon, "TOPLEFT", -10, 0) + end + end + end + frame.UndoTooltip = function () + GameTooltip:Hide() + end + + frame:SetPoint("TOPLEFT", "AuctionFrame", "TOPLEFT", 10,-70) + frame:SetPoint("BOTTOMRIGHT", "AuctionFrame", "BOTTOMRIGHT", 0,0) + frame:SetScript("OnUpdate", frame.OnUpdate) + + local title = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") + title:SetPoint("TOPLEFT", frame, "TOPLEFT", 80, -16) + title:SetText(_TRANS('APPR_Interface_AppraiserAuctionPostingInterface') )--Appraiser: Auction posting interface + + frame.toggleManifest = CreateFrame("Button", nil, frame, "OptionsButtonTemplate") + frame.toggleManifest:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -26, -13) + frame.toggleManifest:SetScript("OnClick", function() + if frame.manifest:IsShown() then + frame.toggleManifest:SetText("Open Sidebar") + frame.manifest:Hide() + else + frame.toggleManifest:SetText("Close Sidebar") + frame.manifest:Show() + end + end) + frame.toggleManifest:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, _TRANS('APPR_HelpTooltip_SidebarINF_Tooltip_AdditionalInfo') ) end)--Open/Close sidebar with additional price info + frame.toggleManifest:SetScript("OnLeave", function() return GameTooltip:Hide() end) + frame.toggleManifest:SetWidth(120) + frame.toggleManifest:SetText("Close Sidebar") + frame.toggleManifest:Disable() + frame.toggleManifest:Show() + + frame.config = CreateFrame("Button", nil, frame, "OptionsButtonTemplate") + frame.config:SetPoint("TOPRIGHT", frame.toggleManifest, "TOPLEFT", 3, 0) + frame.config:SetText(_TRANS('APPR_Interface_Configure') )--Configure + frame.config:SetScript("OnClick", function() + AucAdvanced.Settings.Show() + private.gui:ActivateTab(private.guiId) + end) + + frame.itembox = CreateFrame("Frame", nil, frame) + frame.itembox:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + edgeFile = "Interface/Tooltips/UI-Tooltip-Border", + tile = true, tileSize = 32, edgeSize = 16, + insets = { left = 5, right = 5, top = 5, bottom = 5 } + }) + frame.itembox:SetBackdropColor(0, 0, 0, 0.8) + frame.itembox:SetPoint("TOPLEFT", frame, "TOPLEFT", 13, -71) + frame.itembox:SetWidth(240) + frame.itembox:SetHeight(340) + + -- "Show Auctions" checkbox + frame.itembox.showAuctions = CreateFrame("CheckButton", "Auc_Util_Appraiser_ShowAuctions", frame.itembox, "OptionsCheckButtonTemplate") + frame.itembox.showAuctions:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, _TRANS('APPR_HelpTooltip_IncludeAuctionsListing') ) end)--Include own auctions in the item listing + frame.itembox.showAuctions:SetScript("OnLeave", function() return GameTooltip:Hide() end) + frame.itembox.showAuctions:SetWidth(24) + frame.itembox.showAuctions:SetHeight(24) + Auc_Util_Appraiser_ShowAuctionsText:SetText(_TRANS('APPR_Interface_Auctions') )--Auctions + frame.itembox.showAuctions:SetPoint("BOTTOMRIGHT", frame.itembox, "TOPRIGHT", 0-Auc_Util_Appraiser_ShowAuctionsText:GetWidth(), 0) + frame.itembox.showAuctions:SetHitRectInsets(0, 0-Auc_Util_Appraiser_ShowAuctionsText:GetWidth(), 0, 0) + frame.itembox.showAuctions:SetScript("OnClick", function(self) + frame.showAuctions = self:GetChecked() + frame.GenerateList(true) + PlaySound(frame.showAuctions and "igMainMenuOptionCheckBoxOn" or "igMainMenuOptionCheckBoxOff"); + end) + + -- "Show Hidden" checkbox + frame.itembox.showHidden = CreateFrame("CheckButton", "Auc_Util_Appraiser_ShowHidden", frame.itembox, "OptionsCheckButtonTemplate") + frame.itembox.showHidden:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, _TRANS('APPR_HelpTooltip_IncludeItemsHiddenListing') ) end)--Include items tagged as 'hidden' in the item listing + frame.itembox.showHidden:SetScript("OnLeave", function() return GameTooltip:Hide() end) + frame.itembox.showHidden:SetWidth(24) + frame.itembox.showHidden:SetHeight(24) + Auc_Util_Appraiser_ShowHiddenText:SetText(_TRANS('APPR_Interface_Hidden') )--Hidden + frame.itembox.showHidden:SetPoint("BOTTOMRIGHT", frame.itembox.showAuctions, "BOTTOMLEFT", 0-Auc_Util_Appraiser_ShowHiddenText:GetWidth(), 0) + frame.itembox.showHidden:SetHitRectInsets(0, 0-Auc_Util_Appraiser_ShowHiddenText:GetWidth(), 0, 0) + frame.itembox.showHidden:SetScript("OnClick", function(self) + frame.showHidden = self:GetChecked() + frame.GenerateList(true) + PlaySound(frame.showHidden and "igMainMenuOptionCheckBoxOn" or "igMainMenuOptionCheckBoxOff"); + end) + + -- "Show:" label + frame.itembox.showText = CreateFrame("Frame", nil, frame.itembox) + frame.itembox.showText:SetPoint("BOTTOMRIGHT", frame.itembox.showHidden, "BOTTOMLEFT", 0,0) + frame.itembox.showText.text = frame.itembox.showText:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") + frame.itembox.showText.text:SetAllPoints(frame.itembox.showText) + frame.itembox.showText.text:SetText(_TRANS('APPR_Interface_Show').." " )--Show: + frame.itembox.showText:SetWidth(frame.itembox.showText.text:GetWidth()) + frame.itembox.showText:SetHeight(frame.itembox.showAuctions:GetHeight()) + + frame.items = {} + local function itemButtonClick(self, button) + -- when adding new mod-key combinations, rearrange lines in the the nested 'if' structure as appropriate + local item = frame.list[floor(frame.scroller:GetValue()) + self.id] + local mouseButton = GetMouseButtonClicked() + + if mouseButton == "LeftButton" then + if IsShiftKeyDown() and not IsControlKeyDown() then + if IsAltKeyDown() then -- shift/alt + if item then + frame.PostBySig(item[1]) + return + end + else -- shift only + if item then + ChatEdit_InsertLink(item[7]) + return + end + end + end + elseif mouseButton == "RightButton" then + if IsAltKeyDown() and not IsShiftKeyDown() and not IsControlKeyDown() then -- Alt+RightClick + if item then + local curBulk = get('util.appraiser.item.'..item[1]..".bulk") + set("util.appraiser.item."..item[1]..".bulk", not curBulk) + frame.GenerateList() -- refresh the list and frame after toggling the bulk option. + return + else + debugPrint("item fail") + end + end + end + + frame.SelectItem(self, button) + end + local function itemIconClick(self, button) + return itemButtonClick(self:GetParent(), button) -- go up one level, then tailcall + end + for i=1, NUM_ITEMS do + local item = CreateFrame("Button", nil, frame.itembox) + frame.items[i] = item + + item:SetScript("OnClick", itemButtonClick) + if (i == 1) then + item:SetPoint("TOPLEFT", frame.itembox, "TOPLEFT", 5,-8 ) + else + item:SetPoint("TOPLEFT", frame.items[i-1], "BOTTOMLEFT", 0, -1) + end + item:SetPoint("RIGHT", frame.itembox, "RIGHT", -23,0) + item:SetHeight(26) + + item.id = i + + item:RegisterForClicks("LeftButtonUp", "RightButtonUp") + + item.iconbutton = CreateFrame("Button", nil, item) + item.iconbutton:SetHeight(26) + item.iconbutton:SetWidth(26) + item.iconbutton:SetPoint("LEFT", item, "LEFT", 3,0) + item.iconbutton:SetScript("OnClick", itemIconClick) + item.iconbutton:SetScript("OnEnter", function() frame.DoTooltip(item) end) + item.iconbutton:SetScript("OnLeave", frame.UndoTooltip) + item.iconbutton:RegisterForClicks("LeftButtonUp", "RightButtonUp") + + item.icon = item.iconbutton:CreateTexture(nil, "OVERLAY") + item.icon:SetPoint("TOPLEFT", item.iconbutton, "TOPLEFT", 0,0) + item.icon:SetPoint("BOTTOMRIGHT", item.iconbutton, "BOTTOMRIGHT", 0,0) + item.icon:SetTexture("Interface\\InventoryItems\\WoWUnknownItem01") + + + -- This section is for the Batch Post texture that shows up when an item has the batch post option set + item.batchTex = item.iconbutton:CreateTexture() + + if embedded then + item.batchTex:SetTexture("Interface\\AddOns\\Auc-Advanced\\Modules\\Auc-Util-Appraiser\\Images\\BatchPostTriangle") + else + item.batchTex:SetTexture("Interface\\AddOns\\Auc-Util-Appraiser\\Images\\BatchPostTriangle") + end + + item.batchTex:SetPoint("TOP", item.icon, "TOP", 0,-15) + item.batchTex:SetPoint("LEFT", item.icon, "LEFT", 10,0) + item.batchTex:SetPoint("RIGHT", item.icon, "RIGHT", -3,0) + item.batchTex:SetPoint("BOTTOM", item.icon, "BOTTOM", 0,2) + item.batchTex:SetDrawLayer("OVERLAY") + item.batchTex:Hide() + + item.name = item:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") + item.name:SetJustifyH("LEFT") + item.name:SetJustifyV("TOP") + item.name:SetPoint("TOPLEFT", item.icon, "TOPRIGHT", 3,-1) + item.name:SetPoint("RIGHT", item, "RIGHT", -5,0) + item.name:SetText(_TRANS('APPR_Interface_None') )--[None] + + item.size = item:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") + item.size:SetJustifyH("RIGHT") + item.size:SetJustifyV("BOTTOM") + item.size:SetPoint("BOTTOMLEFT", item.icon, "BOTTOMRIGHT", 3,2) + item.size:SetPoint("RIGHT", item, "RIGHT", -10,0) + item.size:SetText(_TRANS('APPR_Interface_25x') )--25x + + item.info = item:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") + item.info:SetJustifyH("LEFT") + item.info:SetJustifyV("BOTTOM") + item.info:SetPoint("BOTTOMLEFT", item.icon, "BOTTOMRIGHT", 3,2) + item.info:SetPoint("RIGHT", item, "RIGHT", -10,0) + item.info:SetText("11/23/55/112" ) + + item.bg = item:CreateTexture(nil, "ARTWORK") + item.bg:SetTexture("Interface\\FriendsFrame\\UI-FriendsFrame-HighlightBar") + item.bg:SetPoint("TOPLEFT", item, "TOPLEFT", 0,0) + item.bg:SetPoint("BOTTOMRIGHT", item, "BOTTOMRIGHT", 0,0) + item.bg:SetAlpha(0.2) + item.bg:SetBlendMode('ADD') + + item:SetHighlightTexture("Interface\\FriendsFrame\\UI-FriendsFrame-HighlightBar") + end + local scroller = CreateFrame("Slider", "AucAppraiserItemScroll", frame.itembox) + scroller:SetPoint("TOPRIGHT", frame.itembox, "TOPRIGHT", -1,-3) + scroller:SetPoint("BOTTOM", frame.itembox, "BOTTOM", 0,3) + scroller:SetWidth(20) + scroller:SetOrientation("VERTICAL") + scroller:SetThumbTexture("Interface\\Buttons\\UI-ScrollBar-Knob") + scroller:SetMinMaxValues(0, 0) + scroller:SetValue(0) + scroller:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + edgeFile = "Interface/Tooltips/UI-Tooltip-Border", + tile = true, tileSize = 32, edgeSize = 16, + insets = { left = 5, right = 5, top = 5, bottom = 5 } + }) + scroller:SetBackdropColor(0, 0, 0, 0.8) + scroller:SetScript("OnValueChanged", frame.SetScroll) + scroller:Hide() + frame.scroller = scroller + + frame.itembox:EnableMouseWheel(true) + frame.itembox:SetScript("OnMouseWheel", function(obj, dir) scroller:SetValue(scroller:GetValue() - dir) frame.SetScroll() end) + + frame.salebox = CreateFrame("Frame", nil, frame) + frame.salebox:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + edgeFile = "Interface/Tooltips/UI-Tooltip-Border", + tile = true, tileSize = 32, edgeSize = 16, + insets = { left = 5, right = 5, top = 5, bottom = 5 } + }) + frame.salebox:SetBackdropColor(0, 0, 0, 0.8) + frame.salebox:SetPoint("TOPLEFT", frame.itembox, "TOPRIGHT", -3,35) + frame.salebox:SetPoint("RIGHT", frame, "RIGHT", -5,0) + frame.salebox:SetHeight(170) + + frame.salebox.slot = frame.salebox:CreateTexture(nil, "BORDER") + frame.salebox.slot:SetPoint("TOPLEFT", frame.salebox, "TOPLEFT", 10, -10) + frame.salebox.slot:SetWidth(40) + frame.salebox.slot:SetHeight(40) + frame.salebox.slot:SetTexCoord(0.15, 0.85, 0.15, 0.85) + frame.salebox.slot:SetTexture("Interface\\Buttons\\UI-EmptySlot") + + frame.salebox.icon = CreateFrame("Button", nil, frame.salebox) + frame.salebox.icon:SetPoint("TOPLEFT", frame.salebox.slot, "TOPLEFT", 3, -3) + frame.salebox.icon:SetWidth(32) + frame.salebox.icon:SetHeight(32) + frame.salebox.icon:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square.blp") + frame.salebox.icon:SetScript("OnClick", frame.IconClicked) + frame.salebox.icon:SetScript("OnReceiveDrag", frame.IconClicked) + frame.salebox.icon:SetScript("OnEnter", function(self) frame.DoTooltip(self) end) + frame.salebox.icon:SetScript("OnLeave", frame.UndoTooltip) + + frame.salebox.name = frame.salebox:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") + frame.salebox.name:SetPoint("TOPLEFT", frame.salebox.slot, "TOPRIGHT", 5,-2) + frame.salebox.name:SetPoint("RIGHT", frame.salebox, "RIGHT", -15) + frame.salebox.name:SetHeight(20) + frame.salebox.name:SetJustifyH("LEFT") + frame.salebox.name:SetJustifyV("TOP") + frame.salebox.name:SetText(_TRANS('APPR_Interface_NoItemSelected') )--No item selected + frame.salebox.name:SetTextColor(0.5, 0.5, 0.7) + + frame.salebox.info = frame.salebox:CreateFontString(nil, "OVERLAY", "GameFontNormal") + frame.salebox.info:SetPoint("BOTTOMLEFT", frame.salebox.slot, "BOTTOMRIGHT", 5,7) + frame.salebox.info:SetHeight(20) + frame.salebox.info:SetJustifyH("LEFT") + frame.salebox.info:SetJustifyV("BOTTOM") + frame.salebox.info:SetText("APPR_Interface_SelectItemLeftAuctioning")--Select an item to the left to begin auctioning... + frame.salebox.info:SetText(_TRANS('APPR_Interface_SelectItemLeftAuctioning') )--Select an item to the left to begin auctioning... + frame.salebox.info:SetText(_TRANS('APPR_Interface_SelectItemLeftAuctioning') )--Select an item from the list in the left column or drop an item in the square to begin auctioning. + frame.salebox.info:SetTextColor(0.5, 0.5, 0.7) + + frame.salebox.warn = frame.salebox:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + frame.salebox.warn:SetPoint("TOPRIGHT", frame.salebox, "TOPRIGHT", -40,-40) + frame.salebox.warn:SetHeight(12) + frame.salebox.warn:SetTextColor(1, 0.3, 0.06) + frame.salebox.warn:SetText("") + frame.salebox.warn:SetJustifyH("RIGHT") + frame.salebox.warn:SetJustifyV("BOTTOM") + + frame.salebox.stack = CreateFrame("Slider", "AppraiserSaleboxStack", frame.salebox, "OptionsSliderTemplate") + frame.salebox.stack:SetPoint("TOPLEFT", frame.salebox.slot, "BOTTOMLEFT", 0, -5) + frame.salebox.stack:SetHitRectInsets(0,0,0,0) + frame.salebox.stack:SetMinMaxValues(1,20) + frame.salebox.stack:SetValueStep(1) + frame.salebox.stack:SetValue(20) + frame.salebox.stack:SetWidth(180) + frame.salebox.stack:SetScript("OnValueChanged", function() frame.updated = true end) + frame.salebox.stack:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, _TRANS('APPR_HelpTooltip_SetNumberPerStack') ) end)--Set the number of items per posted stack + frame.salebox.stack:SetScript("OnLeave", function() return GameTooltip:Hide() end) + frame.salebox.stack.element = "stack" + frame.salebox.stack:Hide() + + frame.salebox.stack:EnableMouseWheel(1) + frame.salebox.stack:SetScript("OnMouseWheel", function(self, delta) + frame.salebox.stack:SetValue(frame.salebox.stack:GetValue() + -delta) + end) + + AppraiserSaleboxStackLow:SetText("") + AppraiserSaleboxStackHigh:SetText("") + + frame.salebox.stack.label = frame.salebox.stack:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + frame.salebox.stack.label:SetPoint("TOPLEFT", frame.salebox.stack, "BOTTOMLEFT", 0,0) + frame.salebox.stack.label:SetJustifyH("LEFT") + frame.salebox.stack.label:SetJustifyV("CENTER") + + frame.salebox.number = CreateFrame("Slider", "AppraiserSaleboxNumber", frame.salebox, "OptionsSliderTemplate") + frame.salebox.number:SetPoint("TOPLEFT", frame.salebox.stack, "BOTTOMLEFT", 0, -15) + frame.salebox.number:SetHitRectInsets(0,0,0,0) + frame.salebox.number:SetMinMaxValues(1,1) + frame.salebox.number:SetValueStep(1) + frame.salebox.number:SetValue(1) + frame.salebox.number:SetWidth(180) + frame.salebox.number:SetScript("OnValueChanged", function() frame.updated = true end) + frame.salebox.number:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, _TRANS('APPR_HelpTooltip_SetNumberStacksPosted') ) end)--Set the number of stacks to be posted + frame.salebox.number:SetScript("OnLeave", function() return GameTooltip:Hide() end) + frame.salebox.number.element = "number" + frame.salebox.number:Hide() + AppraiserSaleboxNumberLow:SetText("") + AppraiserSaleboxNumberHigh:SetText("") + + function frame.salebox.number:GetAdjustedValue() + local maxStax = self.maxStax or 0 + local value = self:GetValue() + if value > maxStax then + local extraPos = value - maxStax + value = self.extra[extraPos] + end + return value or 1 + end + function frame.salebox.number:SetAdjustedValue(value) + local maxStax = self.maxStax or 0 + if value < 1 or value > maxStax then + for i = 1, #self.extra do + if self.extra[i] == value then + value = maxStax + i + break + end + end + end + self:SetValue(value) + end + frame.salebox.number.extra = {} + function frame.salebox.number:SetAdjustedRange(maxStax, ...) + maxStax = math.max(1,maxStax) + local curVal = self:GetAdjustedValue() + self.maxStax = maxStax + local n = select("#", ...) + for i = 1, #self.extra do self.extra[i] = nil end + for i = 1, select("#", ...) do self.extra[i] = select(i, ...) end + self:SetMinMaxValues(1, maxStax+n) + self:SetAdjustedValue(math.min(curVal, maxStax)) + end + + frame.salebox.number:EnableMouseWheel(1) + frame.salebox.number:SetScript("OnMouseWheel", function(self, delta) + frame.salebox.number:SetValue(frame.salebox.number:GetValue() + -delta) + end) + + frame.salebox.number.label = frame.salebox.number:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + frame.salebox.number.label:SetPoint("TOPLEFT", frame.salebox.number, "BOTTOMLEFT", 0,-4) + frame.salebox.number.label:SetJustifyH("LEFT") + frame.salebox.number.label:SetJustifyV("CENTER") + + frame.salebox.numberonly = CreateFrame("CheckButton", "AppraiserSaleboxNumberOnly", frame.salebox, "OptionsCheckButtonTemplate") + frame.salebox.numberonly:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, _TRANS('APPR_HelpTooltip_RestrictActiveAuctions') ) end)--Restrict active auctions to the 'number' value + frame.salebox.numberonly:SetScript("OnLeave", function() return GameTooltip:Hide() end) + -- Would rather the distance here matched the length of the "All Stacks" text and was recalculated. + frame.salebox.numberonly:SetPoint("BOTTOMLEFT", frame.salebox.number.label, "BOTTOMRIGHT", 0, -4) + frame.salebox.numberonly:SetHeight(20) + frame.salebox.numberonly:SetWidth(20) + frame.salebox.numberonly:SetChecked(false) + frame.salebox.numberonly:SetScript("OnClick", function() frame.updated = true end) + -- This is not the way to make a tooltip! Leaving there to fix when I know how. - Kinesia + -- frame.salebox.numberonly:SetTip("Take existing auctions into account and only post what is needed to maintain this total number.") + frame.salebox.numberonly.label = frame.salebox.numberonly:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + frame.salebox.numberonly.label:SetPoint("BOTTOMLEFT", frame.salebox.numberonly, "BOTTOMRIGHT", 0, 4) + frame.salebox.numberonly.label:SetText(_TRANS('APPR_Interface_Only') )--Only + frame.salebox.numberonly:Hide() + + frame.salebox.duration = CreateFrame("Slider", "AppraiserSaleboxDuration", frame.salebox, "OptionsSliderTemplate") + frame.salebox.duration:SetPoint("TOPLEFT", frame.salebox.number, "BOTTOMLEFT", 0,-25) + frame.salebox.duration:SetHitRectInsets(0,0,0,0) + frame.salebox.duration:SetMinMaxValues(1,3) + frame.salebox.duration:SetValueStep(1) + frame.salebox.duration:SetValue(3) + frame.salebox.duration:SetWidth(80) + frame.salebox.duration:SetScript("OnValueChanged", function() frame.updated = true end) + frame.salebox.duration:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, _TRANS('APPR_HelpTooltip_SetTimePostItem') ) end)--Set the time to post this item for + frame.salebox.duration:SetScript("OnLeave", function() return GameTooltip:Hide() end) + frame.salebox.duration.element = "duration" + frame.salebox.duration:Hide() + AppraiserSaleboxDurationLow:SetText("") + AppraiserSaleboxDurationHigh:SetText("") + + frame.salebox.duration:EnableMouseWheel(1) + frame.salebox.duration:SetScript("OnMouseWheel", function(self, delta) + frame.salebox.duration:SetValue(frame.salebox.duration:GetValue() - delta) + end) + + frame.salebox.duration.label = frame.salebox.duration:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + frame.salebox.duration.label:SetPoint("LEFT", frame.salebox.duration, "RIGHT", 3,2) + frame.salebox.duration.label:SetJustifyH("LEFT") + frame.salebox.duration.label:SetJustifyV("CENTER") + + function frame.GetLinkPriceModels() + return private.GetExtraPriceModels(frame.salebox.link) + end + + frame.salebox.model = SelectBox:Create("AppraiserSaleboxModel", frame.salebox, 140, function() frame.updated = true end, frame.GetLinkPriceModels, "default") + frame.salebox.model.button:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, _TRANS('APPR_HelpTooltip_SelectPricingModel') ) end)--Select the pricing model to use + frame.salebox.model.button:SetScript("OnLeave", function() return GameTooltip:Hide() end) + frame.salebox.model:SetPoint("BOTTOMRIGHT", frame.salebox, "BOTTOMRIGHT", 0, 0) + frame.salebox.model.element = "model" + frame.salebox.model:Hide() + + frame.salebox.model.label = frame.salebox.model:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + frame.salebox.model.label:SetPoint("RIGHT", frame.salebox.model, "LEFT", 15, 5) + frame.salebox.model.label:SetText(_TRANS('APPR_Interface_PricingModelUse') )--Pricing model to use: + + frame.salebox.matcher = CreateFrame("CheckButton", "AppraiserSaleboxMatch", frame.salebox, "OptionsCheckButtonTemplate") + frame.salebox.matcher:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, _TRANS('APPR_HelpTooltip_EnablesMatchersCalculatingPrices') ) end)--Enables the use of matchers (eg Undercut) when calculating prices + frame.salebox.matcher:SetScript("OnLeave", function() return GameTooltip:Hide() end) + frame.salebox.matcher:SetPoint("RIGHT", frame.salebox, "RIGHT", -158, -30) + frame.salebox.matcher:SetHeight(20) + frame.salebox.matcher:SetWidth(20) + frame.salebox.matcher:SetChecked(false) + frame.salebox.matcher:SetScript("OnClick", function() frame.updated = true end) + frame.salebox.matcher:Hide() + + frame.salebox.matcher.label = frame.salebox.matcher:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + frame.salebox.matcher.label:SetPoint("BOTTOMLEFT", frame.salebox.matcher, "BOTTOMRIGHT", 0, 5) + frame.salebox.matcher.label:SetText(_TRANS('APPR_Interface_EnablePriceMatching') )--Enable price matching + + frame.salebox.ignore = CreateFrame("CheckButton", "AppraiserSaleboxIgnore", frame.salebox, "OptionsCheckButtonTemplate") + frame.salebox.ignore:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, _TRANS('APPR_HelpTooltip_RemovesItemListing') ) end)--Removes this item from the item listing + frame.salebox.ignore:SetScript("OnLeave", function() return GameTooltip:Hide() end) + frame.salebox.ignore:SetPoint("TOPRIGHT", frame.salebox, "TOPRIGHT", -160, -3) + frame.salebox.ignore:SetHeight(20) + frame.salebox.ignore:SetWidth(20) + frame.salebox.ignore:SetChecked(false) + frame.salebox.ignore:SetScript("OnClick", function() frame.updated = true end) + frame.salebox.ignore:Hide() + + frame.salebox.ignore.label = frame.salebox.ignore:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + frame.salebox.ignore.label:SetPoint("BOTTOMLEFT", frame.salebox.ignore, "BOTTOMRIGHT", 0, 6) + frame.salebox.ignore.label:SetText(_TRANS('APPR_Interface_HideThisItem') )--Hide this item + + frame.salebox.bulk = CreateFrame("CheckButton", "AppraiserSaleboxBulk", frame.salebox, "OptionsCheckButtonTemplate") + frame.salebox.bulk:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, _TRANS('APPR_HelpTooltip_FlagsBatchPosting') ) end)--Flags this item to be included in Batch Posting + frame.salebox.bulk:SetScript("OnLeave", function() return GameTooltip:Hide() end) + frame.salebox.bulk:SetPoint("TOPRIGHT", frame.salebox.ignore, "BOTTOMRIGHT", 0, 3) + frame.salebox.bulk:SetHeight(20) + frame.salebox.bulk:SetWidth(20) + frame.salebox.bulk:SetChecked(false) + frame.salebox.bulk:SetScript("OnClick", function() frame.updated = true end) + frame.salebox.bulk:Hide() + + frame.salebox.bulk.label = frame.salebox.bulk:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + frame.salebox.bulk.label:SetPoint("BOTTOMLEFT", frame.salebox.bulk, "BOTTOMRIGHT", 0, 6) + frame.salebox.bulk.label:SetText(_TRANS('APPR_Interface_EnableBatchPosting') )--Enable batch posting + + frame.salebox.bid = CreateFrame("Frame", "AppraiserSaleboxBid", frame.salebox, "MoneyInputFrameTemplate") + frame.salebox.bid:SetPoint("RIGHT", frame.salebox, "RIGHT", 0, 20) + frame.salebox.bid:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, _TRANS('APPR_HelpTooltip_EnterBidAmount') ) end)--Enter new bid amount to set a Fixed Price + frame.salebox.bid:SetScript("OnLeave", function() return GameTooltip:Hide() end) + MoneyInputFrame_SetOnValueChangedFunc(frame.salebox.bid, function() frame.SyncMoneyFrameStackBid() frame.updated = true end) + frame.salebox.bid.element = "bid" + frame.salebox.bid:Hide() + AppraiserSaleboxBidGold:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + tile = true, tileSize = 32, + insets = { left = -2, right = 3, top = 4, bottom = 2} + }) + AppraiserSaleboxBidGold:SetBackdropColor(0,0,0, 0) + AppraiserSaleboxBidSilver:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + tile = true, tileSize = 32, + insets = { left = -2, right = 12, top = 4, bottom = 2} + }) + AppraiserSaleboxBidSilver:SetBackdropColor(0,0,0, 0) + AppraiserSaleboxBidCopper:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + tile = true, tileSize = 32, + insets = { left = -2, right = 12, top = 4, bottom = 2} + }) + AppraiserSaleboxBidCopper:SetBackdropColor(0,0,0, 0) + + + frame.salebox.bid.stack = CreateFrame("Frame", "AppraiserSaleboxBidStack", frame.salebox, "MoneyInputFrameTemplate") + frame.salebox.bid.stack:SetPoint("RIGHT", frame.salebox, "RIGHT", 0, 20) + frame.salebox.bid.stack:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, _TRANS('APPR_HelpTooltip_EnterBidAmount') ) end)--Enter new bid amount to set a Fixed Price + frame.salebox.bid.stack:SetScript("OnLeave", function() return GameTooltip:Hide() end) + MoneyInputFrame_SetOnValueChangedFunc(frame.salebox.bid.stack, function() frame.SyncMoneyFrameSingleBid() frame.updated = true end) + frame.salebox.bid.stack.element = "bidStack" + frame.salebox.bid.stack:Hide() + AppraiserSaleboxBidStackGold:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + tile = true, tileSize = 32, + insets = { left = -2, right = 3, top = 4, bottom = 2} + }) + AppraiserSaleboxBidStackGold:SetBackdropColor(0,0,0, 0) + AppraiserSaleboxBidStackSilver:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + tile = true, tileSize = 32, + insets = { left = -2, right = 12, top = 4, bottom = 2} + }) + AppraiserSaleboxBidStackSilver:SetBackdropColor(0,0,0, 0) + AppraiserSaleboxBidStackCopper:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + tile = true, tileSize = 32, + insets = { left = -2, right = 12, top = 4, bottom = 2} + }) + AppraiserSaleboxBidStackCopper:SetBackdropColor(0,0,0, 0) + + + frame.salebox.buy = CreateFrame("Frame", "AppraiserSaleboxBuy", frame.salebox, "MoneyInputFrameTemplate") + frame.salebox.buy:SetPoint("TOPLEFT", frame.salebox.bid, "BOTTOMLEFT", 0,-5) + frame.salebox.buy:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, _TRANS('APPR_HelpTooltip_EnterBuyoutFixedPrice') ) end)--Enter new buyout amount to set a Fixed Price + frame.salebox.buy:SetScript("OnLeave", function() return GameTooltip:Hide() end) + MoneyInputFrame_SetOnValueChangedFunc(frame.salebox.buy, function() frame.SyncMoneyFrameStackBuy() frame.updated = true end) + frame.salebox.buy.element = "buy" + frame.salebox.buy:Hide() + AppraiserSaleboxBuyGold:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + tile = true, tileSize = 32, + insets = { left = -2, right = 3, top = 4, bottom = 2} + }) + AppraiserSaleboxBuyGold:SetBackdropColor(0,0,0, 0) + AppraiserSaleboxBuySilver:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + tile = true, tileSize = 32, + insets = { left = -2, right = 12, top = 4, bottom = 2} + }) + AppraiserSaleboxBuySilver:SetBackdropColor(0,0,0, 0) + AppraiserSaleboxBuyCopper:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + tile = true, tileSize = 32, + insets = { left = -2, right = 12, top = 4, bottom = 2} + }) + AppraiserSaleboxBuyCopper:SetBackdropColor(0,0,0, 0) + + MoneyInputFrame_SetNextFocus(frame.salebox.bid, AppraiserSaleboxBuyGold) + MoneyInputFrame_SetPreviousFocus(frame.salebox.bid, AppraiserSaleboxBuyCopper) + MoneyInputFrame_SetNextFocus(frame.salebox.buy, AppraiserSaleboxBidGold) + MoneyInputFrame_SetPreviousFocus(frame.salebox.buy, AppraiserSaleboxBidCopper) + + frame.salebox.buy.stack = CreateFrame("Frame", "AppraiserSaleboxBuyStack", frame.salebox, "MoneyInputFrameTemplate") + frame.salebox.buy.stack:SetPoint("TOPLEFT", frame.salebox.bid.stack, "BOTTOMLEFT", 0,-5) + frame.salebox.buy.stack:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, _TRANS('APPR_HelpTooltip_EnterBuyoutFixedPrice') ) end)--Enter new buyout amount to set a Fixed Price + frame.salebox.buy.stack:SetScript("OnLeave", function() return GameTooltip:Hide() end) + MoneyInputFrame_SetOnValueChangedFunc(frame.salebox.buy.stack, function() frame.SyncMoneyFrameSingleBuy() frame.updated = true end) + frame.salebox.buy.stack.element = "buyStack" + frame.salebox.buy.stack:Hide() + AppraiserSaleboxBuyStackGold:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + tile = true, tileSize = 32, + insets = { left = -2, right = 3, top = 4, bottom = 2} + }) + AppraiserSaleboxBuyStackGold:SetBackdropColor(0,0,0, 0) + AppraiserSaleboxBuyStackSilver:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + tile = true, tileSize = 32, + insets = { left = -2, right = 12, top = 4, bottom = 2} + }) + AppraiserSaleboxBuyStackSilver:SetBackdropColor(0,0,0, 0) + AppraiserSaleboxBuyStackCopper:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + tile = true, tileSize = 32, + insets = { left = -2, right = 12, top = 4, bottom = 2} + }) + AppraiserSaleboxBuyStackCopper:SetBackdropColor(0,0,0, 0) + + --sets the tab to next field options + MoneyInputFrame_SetNextFocus(frame.salebox.bid.stack, AppraiserSaleboxBuyStackGold) + MoneyInputFrame_SetPreviousFocus(frame.salebox.bid.stack, AppraiserSaleboxBuyStackCopper) + MoneyInputFrame_SetNextFocus(frame.salebox.buy.stack, AppraiserSaleboxBidStackGold) + MoneyInputFrame_SetPreviousFocus(frame.salebox.buy.stack, AppraiserSaleboxBidStackCopper) + + + --Button for Bid frame to toggle stack/single mode + frame.switchToStack = CreateFrame("Button", nil, frame.salebox, "OptionsButtonTemplate") + frame.switchToStack:SetPoint("RIGHT", frame.salebox.bid, "LEFT", -10, 0) + frame.switchToStack:SetText("") + local font = frame.switchToStack:GetNormalFontObject() + font:SetTextColor(1, 1, 1, 1) + frame.switchToStack:SetNormalFontObject(font) + frame.switchToStack:SetWidth(100) + frame.switchToStack:SetHeight(16) + frame.switchToStack:SetScript("OnClick", function() + set("util.appraiser.classic", (not get("util.appraiser.classic"))) + frame.ChangeUI() + end) + frame.switchToStack.TooltipText = _TRANS('APPR_HelpTooltip_PricingMethod')--Switch between 'Per Item' and 'Per Stack' Pricing. + frame.switchToStack:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, self.TooltipText) end) + frame.switchToStack:SetScript("OnLeave", function() return GameTooltip:Hide() end) + frame.switchToStack:Enable() + + --Button for Buy frame to toggle stack/single mode + frame.switchToStack2 = CreateFrame("Button", nil, frame.salebox, "OptionsButtonTemplate") + frame.switchToStack2:SetPoint("RIGHT", frame.salebox.buy, "LEFT", -10, 0) + frame.switchToStack2:SetText("") + frame.switchToStack2:SetNormalFontObject(font) + frame.switchToStack2:SetWidth(100) + frame.switchToStack2:SetHeight(16) + frame.switchToStack2:SetScript("OnClick", function() + set("util.appraiser.classic", (not get("util.appraiser.classic"))) + frame.ChangeUI() + end) + frame.switchToStack2.TooltipText = _TRANS('APPR_HelpTooltip_PricingMethod')--Switch between 'Per Item' and 'Per Stack' Pricing. + frame.switchToStack2:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, self.TooltipText) end) + frame.switchToStack2:SetScript("OnLeave", function() return GameTooltip:Hide() end) + frame.switchToStack2:Enable() + + + frame.go = CreateFrame("Button", nil, frame, "OptionsButtonTemplate") + frame.go:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -7,15) + frame.go:SetText(_TRANS('APPR_Interface_PostItems') )--Post items + frame.go:SetWidth(80) + frame.go:SetScript("OnClick", frame.PostAuctions) + frame.go:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, _TRANS('APPR_HelpTooltip_PostsCurrentItem') ) end)--Posts current item + frame.go:SetScript("OnLeave", function() return GameTooltip:Hide() end) + frame.go.postType = "single" + frame.go:Disable() + + frame.gobatch = CreateFrame("Button", nil, frame, "OptionsButtonTemplate") + frame.gobatch:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -87,15) + frame.gobatch:SetText(_TRANS('APPR_Interface_BatchPost') )--Batch post + frame.gobatch:SetWidth(80) + frame.gobatch:SetScript("OnClick", frame.PostAuctions) + frame.gobatch:RegisterForClicks("LeftButtonUp", "RightButtonUp") + frame.gobatch:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, _TRANS('APPR_HelpTooltip_RefreshesCurrentBatch') ) end)--Alt: Refreshes batch post items Shift: Lists current batch post items RightClick: Posts batch post items Alt+RightClick: Toggles an item's batch post option on/off + frame.gobatch:SetScript("OnLeave", function() return GameTooltip:Hide() end) + frame.gobatch.postType = "batch" + frame.gobatch:Enable() + + frame.refresh = CreateFrame("Button", nil, frame, "OptionsButtonTemplate") + frame.refresh:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -167,15) + frame.refresh:SetText("Refresh") + frame.refresh:SetWidth(80) + frame.refresh:SetScript("OnClick", frame.SmartRefresh) + frame.refresh:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, _TRANS('APPR_HelpTooltip_RefreshesCurrentItem') ) end)--Normal: Refreshes current item Alt: Refreshes whole item list + frame.refresh:SetScript("OnLeave", function() return GameTooltip:Hide() end) + + frame.age = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + frame.age:SetPoint("RIGHT", frame.refresh, "LEFT", -10, 0) + frame.age:SetTextColor(1, 0.8, 0) + frame.age:SetText("") + frame.age:SetJustifyH("RIGHT") + --frame.age:SetJustifyV("BOTTOM") + + frame.cancel = CreateFrame("Button", "AucAdvAppraiserCancelButton", frame, "OptionsButtonTemplate") + frame.cancel:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", 180, 15) + frame.cancel:SetWidth(22) + frame.cancel:SetHeight(18) + frame.cancel:Disable() + frame.cancel:SetScript("OnClick", function() + AucAdvanced.Post.CancelPostQueue() + frame.cancel:Disable() + frame.cancel.tex:SetVertexColor(0.3,0.3,0.3) + end) + frame.cancel:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, _TRANS('APPR_HelpTooltip_ClearsPostQueue') ) end)--Clears post queue + frame.cancel:SetScript("OnLeave", function() return GameTooltip:Hide() end) + frame.cancel.tex = frame.cancel:CreateTexture(nil, "OVERLAY") + frame.cancel.tex:SetPoint("TOPLEFT", frame.cancel, "TOPLEFT", 4, -2) + frame.cancel.tex:SetPoint("BOTTOMRIGHT", frame.cancel, "BOTTOMRIGHT", -4, 2) + frame.cancel.tex:SetTexture("Interface\\Addons\\Auc-Advanced\\Textures\\NavButtons") + frame.cancel.tex:SetTexCoord(0.25, 0.5, 0, 1) + frame.cancel.tex:SetVertexColor(0.3, 0.3, 0.3) + frame.cancel.label = frame.cancel:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + frame.cancel.label:SetPoint("LEFT", frame.cancel, "RIGHT", 5, 0) + frame.cancel.label:SetTextColor(1, 0.8, 0) + frame.cancel.label:SetText("") + frame.cancel.label:SetJustifyH("LEFT") + + local lastPostProgress = 0 + local progressBarOptions = {barColor = {0,0,.6}} + function private.UpdatePostQueueProgress(postnum) + frame.cancel.label:SetText(tostring(postnum)) + --display a progress bar + if postnum > lastPostProgress then lastPostProgress = postnum end + local value = (100 - postnum * 100 / lastPostProgress) or 0 + AucAdvanced.API.ProgressBars("AppraiserBar", value, true, "Appraiser has "..postnum.." more items to post", progressBarOptions) + + if (postnum > 0) and not frame.cancel:IsEnabled() then + frame.cancel:Enable() + frame.cancel.tex:SetVertexColor(1.0, 0.9, 0.1) + elseif (postnum == 0) and frame.cancel:IsEnabled() then + frame.cancel:Disable() + frame.cancel.tex:SetVertexColor(0.3,0.3,0.3) + lastPostProgress = 0 + AucAdvanced.API.ProgressBars("AppraiserBar") + end + end + + frame.manifest = CreateFrame("Frame", nil, frame) + frame.manifest:SetBackdrop({ + bgFile = "Interface\\Tooltips\\ChatBubble-Background", + edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", + tile = true, tileSize = 32, edgeSize = 16, + insets = { left = 5, right = 5, top = 5, bottom = 5 } + }) + frame.manifest:SetBackdropColor(0, 0, 0, 1) + frame.manifest:SetPoint("TOPLEFT", frame, "TOPRIGHT", -20,-30) + frame.manifest:SetPoint("BOTTOM", frame, "BOTTOM", 0,30) + frame.manifest:SetWidth(230) + frame.manifest:SetFrameStrata("MEDIUM") + frame.manifest:SetFrameLevel(AuctionFrame:GetFrameLevel()) + frame.manifest:Hide() + + frame.manifest.close = CreateFrame("Button", nil, frame.manifest) + frame.manifest.close:SetNormalTexture("Interface\\Buttons\\UI-Panel-MinimizeButton-Up") + frame.manifest.close:SetHighlightTexture("Interface\\Buttons\\UI-Panel-MinimizeButton-Highlight") + frame.manifest.close:SetPushedTexture("Interface\\Buttons\\UI-Panel-MinimizeButton-Down") + frame.manifest.close:SetDisabledTexture("Interface\\Buttons\\UI-Panel-MinimizeButton-Disabled") + frame.manifest.close:SetPoint("TOPRIGHT", frame.manifest, "TOPRIGHT", 0,0) + frame.manifest.close:SetWidth(26) + frame.manifest.close:SetHeight(26) + frame.manifest.close:SetScript("OnClick", function() + frame.manifest:Hide() + frame.toggleManifest:SetText("Open Sidebar") + end) + + local function lineHide(obj) + local id = obj.id + local line = frame.manifest.lines[id] + line[1]:Hide() + line[2]:Hide() + end + + local function lineSet(obj, text, coins, r,g,b) + local id = obj.id + local line = frame.manifest.lines[id] + line[1]:SetText(text) + if r and g and b then + line[1]:SetTextColor(r,g,b) + else + line[1]:SetTextColor(1,1,1) + end + line[1]:Show() + + if coins then + line[2]:SetValue(math.floor(tonumber(coins) or 0)) + line[2]:Show() + else + line[2]:Hide() + end + end + + local function lineReset(obj, text, coins) + local id = obj.id + local line = frame.manifest.lines[id] + line[1]:SetText("") + line[2]:SetValue(0) + line[2]:Hide() + end + + local function linesClear(obj) + obj.pos = 0 + for i = 1, obj.max do + obj[i]:Hide() + end + end + + local function linesAdd(obj, text, coins, r,g,b) + obj.pos = obj.pos + 1 + if (obj.pos > obj.max) then return end + obj[obj.pos]:Set(text, coins, r,g,b) + end + + local myStrata = frame.manifest:GetFrameStrata() + + frame.manifest.header = frame.manifest:CreateFontString(nil, "OVERLAY", "GameFontNormal") + frame.manifest.header:SetPoint("TOPLEFT", frame.manifest, "TOPLEFT", 24, -5) + frame.manifest.header:SetPoint("RIGHT", frame.manifest, "RIGHT", 0,0) + frame.manifest.header:SetJustifyH("LEFT") + frame.manifest.header:SetText(_TRANS('APPR_Interface_AuctionDetail') )--Auction detail: + + local lines = { pos = 0, max = 40, Clear = linesClear, Add = linesAdd } + for i=1, lines.max do + local text = frame.manifest:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall") + if i == 1 then + text:SetPoint("TOPLEFT", frame.manifest, "TOPLEFT", 26,-18) + else + text:SetPoint("TOPLEFT", lines[i-1][1], "BOTTOMLEFT", 0,0) + end + text:SetPoint("RIGHT", frame.manifest, "RIGHT", -8,0) + text:SetJustifyH("LEFT") + text:SetHeight(9) + + local coins = AucAdvanced.CreateMoney(8) + coins:SetParent(frame.manifest) + coins:SetPoint("RIGHT", text, "RIGHT", 0,0) + coins:SetFrameStrata(myStrata) + local line = { text, coins, id = i, Hide = lineHide, Set = lineSet, Reset = lineReset } + lines[i] = line + end + frame.manifest.lines = lines + + frame.imageview = CreateFrame("Frame", nil, frame) + frame.imageview:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + edgeFile = "Interface/Tooltips/UI-Tooltip-Border", + tile = true, tileSize = 32, edgeSize = 16, + insets = { left = 5, right = 5, top = 5, bottom = 5 } + }) + frame.imageview:SetBackdropColor(0, 0, 0, 0.8) + frame.imageview:SetPoint("TOPLEFT", frame.salebox, "BOTTOMLEFT") + frame.imageview:SetPoint("TOPRIGHT", frame.salebox, "BOTTOMRIGHT") + frame.imageview:SetPoint("BOTTOM", frame.itembox, "BOTTOM", 0, 20) + --records the column width changes + --store width by header name, that way if column reorginizing is added we apply size to proper column + function private.onResize(self, column, width) + if not width then + set("util.appraiser.columnwidth."..self.labels[column]:GetText(), "default") --reset column if no width is passed. We use CTRL+rightclick to reset column + self.labels[column].button:SetWidth(get("util.appraiser.columnwidth."..self.labels[column]:GetText())) + else + set("util.appraiser.columnwidth."..self.labels[column]:GetText(), width) + end + end + + function private.BuyAuction() + aucPrint(private.buyselection.link) + AucAdvanced.Buy.QueueBuy(private.buyselection.link, private.buyselection.seller, private.buyselection.stack, private.buyselection.minbid, private.buyselection.buyout, private.buyselection.buyout) + frame.imageview.sheet.selected = nil + private.onSelect() + end + function private.BidAuction() + local bid = private.buyselection.minbid + if private.buyselection.curbid and private.buyselection.curbid > 0 then + bid = math.ceil(private.buyselection.curbid*1.05) + end + AucAdvanced.Buy.QueueBuy(private.buyselection.link, private.buyselection.seller, private.buyselection.stack, private.buyselection.minbid, private.buyselection.buyout, bid) + frame.imageview.sheet.selected = nil + private.onSelect() + end + + private.buyselection = {} + function private.onSelect() + if frame.imageview.sheet.prevselected ~= frame.imageview.sheet.selected then + frame.imageview.sheet.prevselected = frame.imageview.sheet.selected + local selected = frame.imageview.sheet:GetSelection() + if (not selected) or (not selected[10]) then + private.buyselection = {} + frame.imageview.purchase.buy:Disable() + frame.imageview.purchase.buy.price:SetText("") + frame.imageview.purchase.bid:Disable() + frame.imageview.purchase.bid.price:SetText("") + else + private.buyselection.link = selected[10] + private.buyselection.seller = selected[1] + private.buyselection.stack = selected[3] + private.buyselection.minbid = selected[7] + private.buyselection.curbid = selected[8] + private.buyselection.buyout = selected[9] + --make sure that it's not one of our auctions, then enable based on buy/bid availability + if (not AucAdvancedConfig["users."..Const.PlayerRealm.."."..private.buyselection.seller]) then + if private.buyselection.buyout and (private.buyselection.buyout > 0) then + frame.imageview.purchase.buy:Enable() + frame.imageview.purchase.buy.price:SetText(AucAdvanced.Coins(private.buyselection.buyout, true)) + else + frame.imageview.purchase.buy:Disable() + frame.imageview.purchase.buy.price:SetText("") + end + + if private.buyselection.minbid then + if private.buyselection.curbid and private.buyselection.curbid > 0 then + frame.imageview.purchase.bid.price:SetText(AucAdvanced.Coins(math.ceil(private.buyselection.curbid*1.05), true)) + else + frame.imageview.purchase.bid.price:SetText(AucAdvanced.Coins(private.buyselection.minbid, true)) + end + frame.imageview.purchase.bid:Enable() + else + frame.imageview.purchase.bid:Disable() + frame.imageview.purchase.bid.price:SetText("") + end + else + frame.imageview.purchase.buy:Disable() + frame.imageview.purchase.buy.price:SetText("") + frame.imageview.purchase.bid:Disable() + frame.imageview.purchase.bid.price:SetText("") + end + end + end + end + + function private.onClick(button, row, index) + if (IsAltKeyDown()) and frame.imageview.sheet.labels[index]:GetText() == "Seller" then + local seller = frame.imageview.sheet.rows[row][index]:GetText() + if not seller or not AucAdvanced.Modules.Filter.Basic or not AucAdvanced.Modules.Filter.Basic.IsPlayerIgnored then frame.sellerIgnore:Hide() return end + + frame.sellerIgnore:SetParent(frame.imageview.sheet.panel) + frame.sellerIgnore:SetFrameStrata("TOOLTIP") + frame.sellerIgnore:ClearAllPoints() + frame.sellerIgnore:SetPoint("TOPLEFT", button, "BOTTOM") + frame.sellerIgnore:Show() + --if toon not ignored the ignore + if not AucAdvanced.Modules.Filter.Basic.IsPlayerIgnored(seller) then + frame.sellerIgnore.yes:SetScript("OnClick", function() BF_IgnoreList_Add( seller ) frame.sellerIgnore:Hide() end) + frame.sellerIgnore.help:SetText(_TRANS('APPR_Interface_AddPlayerIgnore'):format("|CFFFFFFFF", seller))--Add player to ignore list %s%s + else + frame.sellerIgnore.yes:SetScript("OnClick", function() BF_IgnoreList_Remove( seller ) frame.sellerIgnore:Hide() end) + frame.sellerIgnore.help:SetText(_TRANS('APPR_Interface_RemovePlayerIgnore'):format("|CFFFFFFFF", seller))--Remove player from ignore list %s%s + end + end + end + --ignore/unignore seller GUI + frame.sellerIgnore = CreateFrame("Frame", nil, UiParent) + frame.sellerIgnore:Hide() + frame.sellerIgnore:SetBackdrop({ + bgFile = "Interface/Tooltips/ChatBubble-Background", + edgeFile = "Interface/Minimap/TooltipBackdrop", + tile = true, tileSize = 32, edgeSize = 10, + insets = { left = 2, right = 2, top = 2, bottom = 2 } + }) + frame.sellerIgnore:SetBackdropColor(0,0,0, 1) + frame.sellerIgnore:SetWidth(100) + frame.sellerIgnore:SetHeight(70) + frame.sellerIgnore:SetPoint("CENTER", UIParent, "CENTER") + frame.sellerIgnore:SetFrameStrata("TOOLTIP") + + frame.sellerIgnore.help = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall" ) + frame.sellerIgnore.help:SetParent(frame.sellerIgnore) + frame.sellerIgnore.help:SetPoint("CENTER", frame.sellerIgnore, "TOP", 0, -25) + frame.sellerIgnore.help:SetWidth(100) + + frame.sellerIgnore.yes = CreateFrame("Button", nil, frame.sellerIgnore, "UIPanelButtonTemplate") + frame.sellerIgnore.yes:SetNormalFontObject(GameFontNormalSmall) + frame.sellerIgnore.yes:SetPoint("BOTTOMLEFT", frame.sellerIgnore, "BOTTOMLEFT", 5, 10) + frame.sellerIgnore.yes:SetScript("OnClick", function() BF_IgnoreList_Add( name ) end) + frame.sellerIgnore.yes:SetText(_TRANS('APPR_Interface_Yes') )--Yes + frame.sellerIgnore.yes:SetWidth(30) + frame.sellerIgnore.yes:SetHeight(10) + local font = frame.sellerIgnore.yes:GetFontString() + font:SetFontObject("GameFontNormalSmall" ) + font:SetTextHeight(10) + + frame.sellerIgnore.no = CreateFrame("Button", nil, frame.sellerIgnore, "UIPanelButtonTemplate") + frame.sellerIgnore.no:SetNormalFontObject(GameFontNormalSmall) + frame.sellerIgnore.no:SetPoint("BOTTOMRIGHT", frame.sellerIgnore, "BOTTOMRIGHT", -5, 10) + frame.sellerIgnore.no:SetScript("OnClick", function() frame.sellerIgnore:Hide() end) + frame.sellerIgnore.no:SetText(_TRANS("APPR_Interface_No"))--No + frame.sellerIgnore.no:SetWidth(30) + frame.sellerIgnore.no:SetHeight(10) + local font = frame.sellerIgnore.no:GetFontString() + font:SetFontObject("GameFontNormalSmall" ) + font:SetTextHeight(10) + + frame.imageview.sheet = ScrollSheet:Create(frame.imageview, { + --{ "Item", "TEXT", get("util.appraiser.columnwidth.Item")}, -- Default width 105 + { _TRANS('APPR_Interface_Seller') , "TEXT", get("util.appraiser.columnwidth.".._TRANS('APPR_Interface_Seller'))}, + { _TRANS('APPR_Interface_Left') , "INT", get("util.appraiser.columnwidth.".._TRANS('APPR_Interface_Left'))}, + { _TRANS('APPR_Interface_Stk') , "INT", get("util.appraiser.columnwidth.".._TRANS('APPR_Interface_Stk'))}, + { _TRANS('APPR_Interface_Min/ea') , "COIN", get("util.appraiser.columnwidth.".._TRANS('APPR_Interface_Min/ea')), { DESCENDING=true } }, + { _TRANS('APPR_Interface_Cur/ea') , "COIN", get("util.appraiser.columnwidth.".._TRANS('APPR_Interface_Cur/ea')), { DESCENDING=true } }, + { _TRANS('APPR_Interface_Buy/ea') , "COIN", get("util.appraiser.columnwidth.".._TRANS('APPR_Interface_Buy/ea')), { DESCENDING=true, DEFAULT=true } }, + { _TRANS('APPR_Interface_MinBid') , "COIN", get("util.appraiser.columnwidth.".._TRANS('APPR_Interface_MinBid')), { DESCENDING=true } }, + { _TRANS('APPR_Interface_CurBid') , "COIN", get("util.appraiser.columnwidth.".._TRANS('APPR_Interface_CurBid')), { DESCENDING=true } }, + { _TRANS('APPR_Interface_Buyout') , "COIN", get("util.appraiser.columnwidth." .._TRANS('APPR_Interface_Buyout')), { DESCENDING=true } }, + { "", "TEXT", get("util.appraiser.columnwidth.BLANK")}, --Hidden column to carry the link --0 + }) + + frame.imageview.sheet:EnableSelect(true) + + frame.imageview.purchase = CreateFrame("Frame", nil, frame.imageview) + frame.imageview.purchase:SetPoint("TOPLEFT", frame.imageview, "BOTTOMLEFT", 0, 4) + frame.imageview.purchase:SetPoint("BOTTOMRIGHT", frame.imageview, "BOTTOMRIGHT", 0, -16) + frame.imageview.purchase:SetBackdrop({ + bgFile = "Interface\\QuestFrame\\UI-QuestTitleHighlight" + }) + frame.imageview.purchase:SetBackdropColor(0.5, 0.5, 0.5, 1) + + frame.imageview.purchase.buy = CreateFrame("Button", nil, frame.imageview.purchase, "OptionsButtonTemplate") + frame.imageview.purchase.buy:SetPoint("TOPLEFT", frame.imageview.purchase, "TOPLEFT", 5, 0) + frame.imageview.purchase.buy:SetWidth(30) + frame.imageview.purchase.buy:SetText(_TRANS('APPR_Interface_Buy') )--Buy + frame.imageview.purchase.buy:SetScript("OnClick", private.BuyAuction) + frame.imageview.purchase.buy:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, _TRANS('APPR_HelpTooltip_BuyoutSelectedAuction') ) end)--Buyout the selected competing auction + frame.imageview.purchase.buy:SetScript("OnLeave", function() return GameTooltip:Hide() end) + frame.imageview.purchase.buy:Disable() + + frame.imageview.purchase.buy.price = frame.imageview.purchase.buy:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + frame.imageview.purchase.buy.price:SetPoint("TOPLEFT", frame.imageview.purchase.buy, "TOPRIGHT") + frame.imageview.purchase.buy.price:SetPoint("BOTTOMLEFT", frame.imageview.purchase.buy, "BOTTOMRIGHT") + frame.imageview.purchase.buy.price:SetJustifyV("MIDDLE") + + frame.imageview.purchase.bid = CreateFrame("Button", nil, frame.imageview.purchase, "OptionsButtonTemplate") + frame.imageview.purchase.bid:SetPoint("TOPLEFT", frame.imageview.purchase.buy, "TOPLEFT", 120, 0) + frame.imageview.purchase.bid:SetWidth(30) + frame.imageview.purchase.bid:SetText(_TRANS('APPR_Interface_Bid') )--Bid + frame.imageview.purchase.bid:SetScript("OnClick", private.BidAuction) + frame.imageview.purchase.bid:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, _TRANS('APPR_HelpTooltip_BidSelectedAuction') ) end)--Place a bid on the selected competing auction + frame.imageview.purchase.bid:SetScript("OnLeave", function() return GameTooltip:Hide() end) + frame.imageview.purchase.bid:Disable() + + frame.imageview.purchase.bid.price = frame.imageview.purchase.bid:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + frame.imageview.purchase.bid.price:SetPoint("TOPLEFT", frame.imageview.purchase.bid, "TOPRIGHT") + frame.imageview.purchase.bid.price:SetPoint("BOTTOMLEFT", frame.imageview.purchase.bid, "BOTTOMRIGHT") + frame.imageview.purchase.bid.price:SetJustifyV("MIDDLE") + + frame.ScanTab = CreateFrame("Button", "AuctionFrameTabUtilAppraiser", AuctionFrame, "AuctionTabTemplate") + frame.ScanTab:SetText(_TRANS('APPR_Interface_Appraiser') )--Appraiser + frame.ScanTab:Show() + PanelTemplates_DeselectTab(frame.ScanTab) + + if get("util.appraiser.displayauctiontab") then + AucAdvanced.AddTab(frame.ScanTab, frame) + end + + function frame.ScanTab.OnClick(self) + local index = self:GetID() + local tab = getglobal("AuctionFrameTab"..index) + if (tab and tab:GetName() == "AuctionFrameTabUtilAppraiser") then + AuctionFrameTopLeft:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Bid-TopLeft") + AuctionFrameTop:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Bid-Top") + AuctionFrameTopRight:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Bid-TopRight") + AuctionFrameBotLeft:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Bid-BotLeft") + AuctionFrameBot:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Bid-Bot") + AuctionFrameBotRight:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Bid-BotRight") + AuctionFrameMoneyFrame:Show() + if (AuctionDressUpFrame:IsVisible()) then + AuctionDressUpFrame:Hide() + AuctionDressUpFrame.reshow = true + end + frame:Show() + AucAdvanced.Scan.LoadScanData() + frame.GenerateList(true) + else + if (AuctionDressUpFrame.reshow) then + AuctionDressUpFrame:Show() + AuctionDressUpFrame.reshow = nil + end + AuctionFrameMoneyFrame:Show() + frame:Hide() + end + end + + frame.salebox.numberentry = CreateFrame("EditBox", "AppraiserSaleboxNumberEntry", frame.salebox, "InputBoxTemplate") + frame.salebox.numberentry:SetPoint("LEFT", frame.salebox.number, "RIGHT", 10, 0) + frame.salebox.numberentry:SetNumeric(false) + frame.salebox.numberentry:SetHeight(16) + frame.salebox.numberentry:SetWidth(32) + frame.salebox.numberentry:SetNumber(0) + frame.salebox.numberentry:SetAutoFocus(false) + frame.salebox.numberentry:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, _TRANS('APPR_HelpTooltip_SetNumberStacksPosted') ) end)--Set the number of stacks to be posted + frame.salebox.numberentry:SetScript("OnLeave", function() return GameTooltip:Hide() end) + frame.salebox.numberentry:SetScript("OnEnterPressed", function() + frame.salebox.numberentry:ClearFocus() + frame.updated = true + end) + frame.salebox.numberentry:SetScript("OnTabPressed", function() + frame.salebox.stackentry:SetFocus() + frame.updated = true + end) + frame.salebox.numberentry:SetScript("OnTextChanged", function() + local text = frame.salebox.numberentry:GetText():lower() + if (text ~= "") then + frame.updated = true + end + end) + frame.salebox.numberentry:SetScript("OnEscapePressed", function() + frame.salebox.numberentry:ClearFocus() + end) + frame.salebox.numberentry:Hide() + + frame.salebox.stackentry = CreateFrame("EditBox", "AppraiserSaleboxStackEntry", frame.salebox, "InputBoxTemplate") + frame.salebox.stackentry:SetPoint("LEFT", frame.salebox.stack, "RIGHT", 10, 0) + frame.salebox.stackentry:SetNumeric(true) + frame.salebox.stackentry:SetNumber(0) + frame.salebox.stackentry:SetHeight(16) + frame.salebox.stackentry:SetWidth(32) + frame.salebox.stackentry:SetAutoFocus(false) + frame.salebox.stackentry:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, _TRANS('APPR_HelpTooltip_SetNumberPerStack') ) end)--Set the number of items per posted stack + frame.salebox.stackentry:SetScript("OnLeave", function() return GameTooltip:Hide() end) + frame.salebox.stackentry:SetScript("OnEnterPressed", function() + frame.salebox.stackentry:ClearFocus() + frame.updated = true + end) + frame.salebox.stackentry:SetScript("OnTabPressed", function() + frame.salebox.numberentry:SetFocus() + frame.updated = true + end) + frame.salebox.stackentry:SetScript("OnTextChanged", function() + local text = frame.salebox.stackentry:GetText() + if text ~= "" then + frame.updated = true + end + end) + frame.salebox.stackentry:SetScript("OnEscapePressed", function() + frame.salebox.stackentry:ClearFocus() + end) + frame.salebox.stackentry:Hide() + + frame.ChangeUI() + hooksecurefunc("AuctionFrameTab_OnClick", frame.ScanTab.OnClick) + + hooksecurefunc("HandleModifiedItemClick", function (link) + if GetMouseButtonClicked() == "LeftButton" and IsAltKeyDown() and frame:IsVisible() and get("util.appraiser.clickhookany") then + if link then + frame.SelectItem(nil, nil, link) + end + end + end) + + -- GetMouseButtonClicked doesn't work for chatlinks, so we hook SetItemRef as well + hooksecurefunc("SetItemRef", function (shortlink, hyperlink, mousebutton, chatframe) + if mousebutton == "LeftButton" and IsAltKeyDown() and frame:IsVisible() and get("util.appraiser.clickhookany") then + local link = hyperlink or shortlink + if link then + frame.SelectItem(nil, nil, link) + end + end + end) + + --[[These scrollframe functions need to be here to avoid errors, since many elements of appraisers frames are not finished when we create the scrollframe]] + --If we have a saved column arrangement reapply + if get("util.appraiser.columnorder") then + frame.imageview.sheet:SetOrder(get("util.appraiser.columnorder") ) + end + --Apply last column sort used + if get("util.appraiser.columnsortcurSort") then + frame.imageview.sheet.curSort = get("util.appraiser.columnsortcurSort") or 1 + frame.imageview.sheet.curDir = get("util.appraiser.columnsortcurDir") or 1 + frame.imageview.sheet:PerformSort() + end + --callback functions for frame.imageview.sheet events, register for callbacks AFTER we have applied any saved changes + function frame.imageview.sheet.Processor(callback, self, button, column, row, order, curDir, ...) + if (callback == "OnMouseDownCell") then + private.onSelect() + elseif (callback == "OnClickCell") then + private.onClick(button, row, column) + elseif (callback == "ColumnOrder") then + set("util.appraiser.columnorder", order) + elseif (callback == "ColumnWidthSet") then + private.onResize(self, column, button:GetWidth() ) + elseif (callback == "ColumnWidthReset") then + private.onResize(self, column, nil) + elseif (callback == "ColumnSort") then + set("util.appraiser.columnsortcurDir", curDir) + set("util.appraiser.columnsortcurSort", column) + end + end + +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-Appraiser/AprFrame.lua $", "$Rev: 4933 $") diff --git a/Auc-Advanced/Modules/Auc-Util-Appraiser/AprSettings.lua b/Auc-Advanced/Modules/Auc-Util-Appraiser/AprSettings.lua new file mode 100644 index 0000000..fa73d8a --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-Appraiser/AprSettings.lua @@ -0,0 +1,551 @@ +--[[ + Auctioneer - Appraisals and Auction Posting + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: AprSettings.lua 4901 2010-10-05 16:44:24Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This is an addon for World of Warcraft that adds an appraisals tab to the AH for + easy posting of your auctionables when you are at the auction house. + + 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 lib = AucAdvanced.Modules.Util.Appraiser +local private = lib.Private +local print,_,_,_,_,_,get,set,default,_,fill, _TRANS = AucAdvanced.GetModuleLocals() +local coins = AucAdvanced.Coins + +function private.GetPriceModels(itemLink) + if not private.scanValueNames then private.scanValueNames = {} end + for i = 1, #private.scanValueNames do + private.scanValueNames[i] = nil + end + + table.insert(private.scanValueNames,{"market", _TRANS("UCUT_Interface_MarketValue")})--Market value (reusing Undercut's translation) + local algoList = AucAdvanced.API.GetAlgorithms(itemLink) + for pos, name in ipairs(algoList) do + if (name ~= lib.libName) then + table.insert(private.scanValueNames,{name, _TRANS('APPR_Interface_Stats').." "..name})--Stats: + end + end + return private.scanValueNames +end + +function private.GetExtraPriceModels(itemLink) + local vals = private.GetPriceModels(itemLink) + table.insert(vals, {"fixed", _TRANS('APPR_Interface_FixedPrice') })--Fixed price + table.insert(vals, {"default", _TRANS('APPR_Interface_Default') })--Default + return vals +end + +private.durations = { + { 720, _TRANS('APPR_Interface_12Hours') },--12 hours + { 1440, _TRANS('APPR_Interface_24Hours') },--24 hours + { 2880, _TRANS('APPR_Interface_48Hours') },--48 hours +} + +--[[ The items are stored as: +---- id, name, texture, quality +--]] +function private.sortItems(a,b) + if (not a.ignore)~=(not b.ignore) then return (a.ignore and 1 or 0) < (b.ignore and 1 or 0) end + if a[4] ~= b[4] then return a[4] > b[4] end + if a[2] ~= b[2] then return a[2] < b[2] end + if (not a.auction)~=(not b.auction) then return (a.auction and 1 or 0) < (b.auction and 1 or 0) end -- sort an auction AFTER its corresponding inventory entry to drag-to-select picks the inventory item + return a[1] < b[1] +end + +function private.updateRoundExample() + local method = get("util.appraiser.round.method") or "unit" + local pos = tonumber(get("util.appraiser.round.position")) + local mag = tonumber(get("util.appraiser.round.magstep")) + local sub = tonumber(get("util.appraiser.round.subtract")) + if pos == nil then pos = 0.10 end + if mag == nil then mag = 5 end + if sub == nil then sub = 1 end + pos = math.floor(pos * 100) + + local roundname + local output = {} + local example = " %s -> %s" + + local a,b, c,d, e,f + if method == "unit" then + roundname = _TRANS('APPR_Interface_StopValue') + elseif method == "div" then + roundname = _TRANS('APPR_Interface_Divisions') + else + set("util.appraiser.round.method", nil) -- saved setting was invalid, so clear it. + roundname = _TRANS('APPR_Interface_StopValue') + end + + tinsert(output, _TRANS('APPR_Interface_Examples'):format(roundname)) + local limit = 12 + + local l, i, v, p = 0, 0, 1, 0 + while i < limit do + local r = private.roundValue(v) + if r ~= p then + tinsert(output, example:format(coins(v,1), coins(r,1))) + p = r + v = math.max(p, v) + i = i + 1 + end + v = v + 1 + l = l + 1 + if i == limit - 5 and v < mag*100 then + tinsert(output, " ...") + v = (mag * 10000 - pos) + 1 + i = i + 1 + end + if l > 100000 then i = 8 end + end + private.roundNote:SetText(strjoin("\n", unpack(output))) +end + +local round = {} +function round.div(value, pos, mag) + local scale + if value > mag * 1000000 then scale = 1000000 -- Round at the gold level + elseif value > mag * 10000 then scale = 10000 -- Round at the silver level + else scale = 100 end -- Round at the copper level + + local base = math.floor(value / scale) * scale -- This is the major amount that will stay + local part = value - base -- This is the bit that's left over, we need to round this "up" + -- to the next "division" + local step = pos * scale / 100 -- This is how much the division steps up + local divs = math.min(math.ceil(part / step) * step, scale) -- The new rounded up integral divisions + return base + divs +end +function round.unit(value, pos, mag) + local scale + if value > mag * 1000000 then scale = 1000000 -- Round at the gold level + elseif value > mag * 10000 then scale = 10000 -- Round at the silver level + else scale = 100 end -- Round at the copper level + + local base = math.floor(value / scale) * scale -- This is the major amount that will stay + local part = value - base -- This is the bit that's left over, we need to round this "up" + -- to the next "division" + local stop = pos * scale / 100 -- This is how much the division steps up + local divs = stop + if part > stop then + divs = stop + scale + end + return base + divs +end + +-- Function to round a value according to the current rounding method +function private.roundValue(value) + local method = get("util.appraiser.round.method") or "unit" + local pos = tonumber(get("util.appraiser.round.position")) + local mag = tonumber(get("util.appraiser.round.magstep")) + local sub = tonumber(get("util.appraiser.round.subtract")) + if pos == nil then pos = 0.10 end + if mag == nil then mag = 5 end + if sub == nil then sub = 1 end + pos = math.floor(pos * 100) + + local new + if round[method] then new = round[method](value, pos, mag) end + return math.floor(tonumber(new) or tonumber(value) or 1) - sub +end + +function lib.RoundBid(value) + if get("util.appraiser.round.bid") then + return private.roundValue(value) + else + return math.floor(value + 0.5) --We CANNOT allow Decimal values to be passed to hooked modules such as beancounter, even if AH will not throw an error + end +end + +function lib.RoundBuy(value) + if value ~= 0 and get("util.appraiser.round.buy") then + return private.roundValue(value) + else + return math.floor(value + 0.5) --We CANNOT allow Decimal values to be passed to hooked modules such as beancounter, even if AH will not throw an error + end +end + +local scrollItems = {} +function lib.UpdateList() + local n = #scrollItems + for i = 1, n do + scrollItems[i] = nil + end + local filter = get('util.appraiser.filter') or "" + private.currentfilter = filter + filter = filter:lower() + if (filter == "") then filter = nil end + + local d = AucAdvancedData.UtilAppraiser + if not d then + AucAdvancedData.UtilAppraiser = {} + d = AucAdvancedData.UtilAppraiser + end + if not d.items then + d.items = {} + end + + local data + for i = 1, #(d.items) do + data = d.items[i] + if get("util.appraiser.item."..data[1]..".model") then + if not filter or data[2]:find(filter, 1, true) then + table.insert(scrollItems, data) + end + end + end + table.sort(scrollItems, private.sortItems) + + local pos = 0 + n = #scrollItems + if (n <= 8) then + private.scroller:Hide() + private.scroller:SetValue(0) + else + private.scroller:Show() + private.scroller:SetMinMaxValues(0, n-7) + -- Find the current item + for i = 1, n do + if scrollItems[i][1] == private.selected then + pos = i + break + end + end + end + private.scroller:SetValue(math.max(0, math.min(n-7, pos-4))) + private:SetScroll() +end + +function private:SetScroll() + local pos = private.scroller:GetValue() + for i = 0, 7 do + local item = scrollItems[pos+i] + local button = private.items[i+1] + if item then + button.icon:SetTexture(item[3]) + local _,_,_, hex = GetItemQualityColor(item[4]) + button.name:SetText(hex.."["..item[2].."]|r") + button:Show() + if (item[1] == private.selected) then + button.bg:Show() + else + button.bg:Hide() + end + else + button:Hide() + end + end +end + +function private:SelectItem(...) + private.selected = self.id + private:SetScroll() + private:SetVisibility() +end + +function private:SetVisibility() + if private.selected and private.selected ~= "" then + private.itemModel.setting = "util.appraiser.item."..private.selected..".model" + private.itemStack.setting = "util.appraiser.item."..private.selected..".stack" + private.itemFixBid.setting = "util.appraiser.item."..private.selected..".fixed.bid" + private.itemFixBuy.setting = "util.appraiser.item."..private.selected..".fixed.buy" + private.gui:Refresh() + + private.itemModel:Show() + private.itemStack:Show() + private.itemStack.textEl:Show() + if get("util.appraiser.item."..private.selected..".model") == "fixed" then + private.itemFixBid:Show() + private.itemFixBid.textEl:Show() + private.itemFixBuy:Show() + private.itemFixBuy.textEl:Show() + else + private.itemFixBid:Hide() + private.itemFixBid.textEl:Hide() + private.itemFixBuy:Hide() + private.itemFixBuy.textEl:Hide() + end + else + private.itemModel:Hide() + private.itemStack:Hide() + private.itemStack.textEl:Hide() + private.itemFixBid:Hide() + private.itemFixBid.textEl:Hide() + private.itemFixBuy:Hide() + private.itemFixBuy.textEl:Hide() + end +end + +function private.SetupConfigGui(gui) + -- The defaults for the following settings are set in the lib.OnLoad function + local id = gui:AddTab(lib.libName, lib.libType.." Modules") + gui:MakeScrollable(id) + + gui:AddHelp(id, "what appraiser", + _TRANS('APPR_Help_WhatAppraiser') ,--What is Appraiser? + _TRANS('APPR_Help_WhatAppraiserAnswer') )--Appraiser is a tool to allow you to rapidly post auctions, and remembers your last posting prices automatically. The Appraiser interface attaches to your Auction House window as an extra tab at the bottom of the window. When you first select the Appraiser window, it will display all your auctionable items on the left side of your window. When you select an item from the left, you will see the control window at the top and the current auctions list at the bottom. The control window allows you to specify the posting stack size, for posting stack-splitted auctions, and the number of stacks to post by sliding the two sliders left and right. + + function private.addTooltipControls(id) + gui:AddControl(id, "Header", 0, _TRANS('APPR_Interface_AppraiserOptions') )--Appraiser options + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + gui:AddControl(id, "Checkbox", 0, 1, "util.appraiser.enable", _TRANS('APPR_Interface_ShowAppraisalTooltips') )--Show appraisal in the tooltips? + gui:AddTip(id, _TRANS('APPR_HelpTooltip_ShowAppraisalTooltips') )--This option will cause the current Appraiser pricing model and calculated sale price in your tooltips when you mouseover the given item + gui:AddControl(id, "Checkbox", 0, 4, "util.appraiser.bidtooltip", _TRANS('APPR_Interface_AlsoShowBid') )--Also show starting bid + gui:AddTip(id, _TRANS('APPR_HelpTooltip_AlsoShowBid') )--This option will cause Appraiser to also show the starting bid price in the tooltip + gui:AddControl(id, "Checkbox", 0, 4, "util.appraiser.ownauctions", _TRANS('APPR_Interface_ShowOwnAuctionsTooltips') )--Show own Auctions in the tooltips? + gui:AddTip(id, _TRANS('APPR_HelpTooltip_ShowOwnAuctionsTooltips') )--This option will cause your current auctions to be displayed in your tooltips when you mouseover the given item + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + end + --This is the Tooltip tab provided by aucadvanced so all tooltip configuration is in one place + local tooltipID = AucAdvanced.Settings.Gui.tooltipID + + --now we create a duplicate of these in the tooltip frame + private.addTooltipControls(id) + if tooltipID then private.addTooltipControls(tooltipID) end + + gui:AddControl(id, "Checkbox", 0, 1, "util.appraiser.displayauctiontab", _TRANS('APPR_Interface_ShowAppraiserTab') )--Show Appraiser tab at the Auction House + gui:AddTip(id, _TRANS('APPR_HelpTooltip_ShowAppraiserTab') )--Shows the appraiser tab on the auction house + + gui:AddControl(id, "Subhead", 0, _TRANS('APPR_Interface_AppraiserFrameColoration') ) --Appraiser frame coloration + gui:AddControl(id, "Checkbox", 0, 1, "util.appraiser.color", _TRANS('APPR_Interface_ColorAppraiserPriceLevel') )--Color Appraiser items by their PriceLevel data + gui:AddTip(id, _TRANS('APPR_HelpTooltip_ColorAppraiserPriceLevel') )--This option will use information from PriceLevel to tint the current auction valuations by how far above/below the current priceing model's mean in shades from red to blue. + gui:AddControl(id, "Selectbox", 0, 3, { + {"LEFT", _TRANS('APPR_Interface_Left') },--Left + {"RIGHT", _TRANS('APPR_Interface_Right') },--Right + {"TOP", _TRANS('APPR_Interface_Top') },--Top + {"BOTTOM", _TRANS('APPR_Interface_Bottom') },--Bottom + {"OFF", _TRANS('APPR_Interface_Off') },--Off + }, "util.appraiser.colordirection", _TRANS('APPR_Interface_PickGradientDirection') )--Pick the gradient direction + gui:AddTip(id, _TRANS('APPR_HelpTooltip_PickGradientDirection') )--This determines the direction that the above gradient is drawn in for the Appraiser Browse window (if enabled). + + gui:AddControl(id, "Checkbox", 0, 1, "util.appraiser.manifest.color", _TRANS('APPR_Interface_ColorManifestByPriceLevel') )--Color bid and buy prices in the manifest frame by their PriceLevel data + gui:AddTip(id, _TRANS('APPR_HelpTooltip_ColorManifestByPriceLevel') )--This option will use information from PriceLevel to tint the per-stack prices of your bid and buyout lines in the right-side pop-out manifest frame + gui:AddControl(id, "Checkbox", 0, 1, "util.appraiser.tint.color", _TRANS('APPR_Interface_TintBidBuyBoxesPriceLevel') )--Tint bid and buy input boxes by their PriceLevel data + gui:AddTip(id, _TRANS('APPR_HelpTooltip_TintBidBuyBoxesPriceLevel') )--This option will use information from PriceLevel to tint the bid and buyout input boxes +-- gui:AddControl(id, "Checkbox", 0, 1, "util.appraiser.clickhook", "Allow alt-clicking inventory items to load them into Appraiser") +-- gui:AddTip(id, "This option will enable the use of alt-clicking inventory items to select and post (in addition to being able to drag them into the Appraiser window).\nAlt-shift-click automatically posts the (stack of) item(s) clicked. Only active while the Appraiser tab is open.") + gui:AddControl(id, "Checkbox", 0, 1, "util.appraiser.clickhookany", _TRANS('APPR_Interface_AltClickingAppraiserSetting') )--Allow alt-clicking items to change Appraiser settings for any item + gui:AddTip(id, _TRANS('APPR_HelpTooltip_AltClickingAppraiserSetting') )--This option will enable the use of alt-clicking items to bring up the Appraiser settings for the item so that they can be adjusted.\nOnly active while the Appraiser tab is open. + gui:AddControl(id, "Checkbox", 0, 1, "util.appraiser.reselect", _TRANS('APPR_Interface_SelectNextItem') )--Select next item after posting + gui:AddTip(id, _TRANS('APPR_HelpTooltip_SelectNextItem') )--This option will enable Appraiser to select the next item from the list when you post. + gui:AddControl(id, "Checkbox", 0, 1, "util.appraiser.buttontips", _TRANS('APPR_Interface_ShowTooltipControls') )--Show tooltip help over controls + gui:AddTip(id, _TRANS('APPR_HelpTooltip_ShowTooltipControls') )--This option will enable help tooltips for the Appraiser controls. + + gui:AddControl(id, "Subhead", 0, _TRANS('APPR_Interface_DefaultPricingModel') )--Default pricing model + gui:AddControl(id, "Selectbox", 0, 1, private.GetPriceModels, "util.appraiser.model", _TRANS('APPR_Interface_DefaultModelAppraisals') )--Default pricing model to use for appraisals + gui:AddTip(id, _TRANS('APPR_HelpTooltip_DefaultModelAppraisals') )--You may select a default and alternate pricing model for items that do not have a specific model set. + gui:AddControl(id, "Subhead", 0, _TRANS('APPR_Interface_AlternatePricingModel') )--Alternate pricing model + gui:AddControl(id, "Selectbox", 0, 1, private.GetPriceModels, "util.appraiser.altModel", _TRANS('APPR_Interface_AlternatePricingModelSelectBox') )--Alternate pricing model to use for appraisals + gui:AddTip(id, _TRANS('APPR_HelpTooltip_AlternatePricingModelSelectBox') )--You may select a default and alternate pricing model for items that do not have a specific model set. + gui:AddControl(id, "Subhead", 0, _TRANS('APPR_Interface_UseMatchingDefault') )--Use Matching by Default + gui:AddControl(id, "Checkbox", 0, 1, "util.appraiser.match", _TRANS('APPR_Interface_UseMatchingDefault') )--Use Matching by Default + gui:AddTip(id, _TRANS('APPR_HelpTooltip_UseMatchingDefault') )--This option determines whether to default to checking or unchecking the match competition checkbox. + gui:AddControl(id, "Subhead", 0, _TRANS('APPR_Interface_DefaultListingTime') )--Default Listing Time + gui:AddControl(id, "Selectbox", 0, 1, private.durations, "util.appraiser.duration", _TRANS('APPR_Interface_DefaultListingDuration') )--Default listing duration + gui:AddTip(id, _TRANS('APPR_HelpTooltip_DefaultListingDuration') )--You may set a default auction listing duration for items that do not have a specific duration set. + gui:AddControl(id, "Subhead", 0, _TRANS('APPR_Interface_DefaultStackSize') )--Default Stack Size + gui:AddControl(id, "Text", 0, 1, "util.appraiser.stack") + gui:AddTip(id, _TRANS('APPR_HelpTooltip_DefaultStackSize') )--Input number or 'max' to set the default size for stacks + gui:AddControl(id, "Subhead", 0, _TRANS('APPR_Interface_DefaultNumberStacks') )--Default Number of Stacks + gui:AddControl(id, "Text", 0, 1, "util.appraiser.number") + gui:AddTip(id, _TRANS('APPR_HelpTooltip_DefaultNumberStacks') )--Input number, 'maxfull', or 'maxplus' to set the default number of stacks. 'maxfull' sets to all full stacks, while 'maxplus' sets to all stacks, including any incomplete stack + + gui:AddHelp(id, "what is clickhook", + _TRANS('APPR_Help_WhatClickHooks') ,--What are the click-hooks? + _TRANS('APPR_Help_WhatClickHooksAnswer') )--The click-hooks let you select an item by alt-clicking on it, or post it by alt-shift-clicking on it. + gui:AddHelp(id, "what is model", + _TRANS('APPR_Help_WhatDefaultPricing') ,--What is the default pricing model used for? + _TRANS('APPR_Help_WhatDefaultPricingAnswer') )--When Appraiser calculates the price to list an item for, it will use either a market price, which is an average of certain other pricing models, or a price returned by a specific AuctioneerAdvanced statistics module. You may select the model that is used for items that have not had a particular model selected. + + gui:AddControl(id, "Subhead", 0, _TRANS('APPR_Interface_StartingBidCalculation') )--Starting bid calculation + gui:AddControl(id, "WideSlider", 0, 1, "util.appraiser.bid.markdown", 0, 100, 0.1, _TRANS('APPR_Interface_MarkdownBy').." %0.1f%%" )--Markdown by: + gui:AddTip(id, _TRANS('APPR_HelpTooltip_MarkdownBy') )--The markdown amount is a percentage amount that an item's calculated value will be reduced by to produce the bid value. + gui:AddControl(id, "MoneyFramePinned", 0, 1, "util.appraiser.bid.subtract", 0, 9999999, _TRANS('APPR_Interface_SubtractAmount') )--Subtract amount: + gui:AddTip(id, _TRANS('APPR_HelpTooltip_SubtractAmount') )--The subtract amount is a fixed amount that an item's calculated value will have subtracted to produce the bid value. + gui:AddControl(id, "Checkbox", 0, 1, "util.appraiser.bid.deposit", _TRANS('APPR_Interface_SubtractDepositCost') )--Subtract deposit cost + gui:AddTip(id, _TRANS('APPR_HelpTooltip_SubtractDepositCost') )--This option will cause the item's calculated value to be reduced by the value of the deposit rate to produce the bid value. + gui:AddControl(id, "Checkbox", 0, 1, "util.appraiser.bid.vendor", _TRANS('APPR_Interface_VendorPriceMinimum') )--Vendor price as minimum + gui:AddTip(id, _TRANS('APPR_HelpTooltip_VendorPriceMinimum') )--This option will cause the item's bid to never fall below the vendor price of the item, taking into account the Auction House's cut. + + gui:AddHelp(id, "what is bid", + _TRANS('APPR_Help_WhatStartingBid') ,--What is the starting bid? + _TRANS('APPR_Help_WhatStartingBidAnswer') )--The starting bid is also known as the minimum bid. It is the price that the first bidder must match or exceed in order to place the bid. From there, the next bids must go up in bid increments based off the current bid. + + gui:AddHelp(id, "how bid calculated?", + _TRANS('APPR_Help_HowBidPriceCalculated') ,--How does the bid price get calculated? + _TRANS('APPR_Help_HowBidPriceCalculatedAnswer'):format("|cffffff00","|r", "|cffffff00", "|r", " |cffffff00", "|r" ) )--Except for fixed price items, the starting bid price is calculated based off the original buyout price. The bid price calculation options allow you to specify how the bid price is reduced, and the options are cumulative, so if you set both a markdown percent, and subtract the deposit cost, then the bid value will be calculated as: (%s Buyout %s-%s Markdown %s-%s Deposit %s). + + gui:AddHelp(id, "how markdown calculated", + _TRANS('APPR_Help_HowMarkdownPctCalculated') ,--How is the markdown percentage calculated? + _TRANS('APPR_Help_HowMarkdownPctCalculatedAnswer') )--The amount is calculated by multiplying the calculated value by the percentage amount which is specified in the options. This amount is then subtracted from the calculated value (along with the fixed subtract amount and/or the deposit amount if specified) to produce the starting bid price. + + gui:AddControl(id, "Subhead", 0, "".._TRANS('APPR_Interface_ValueRounding'))--Value rounding + gui:AddControl(id, "Checkbox", 0, 1, "util.appraiser.round.bid", _TRANS('APPR_Interface_RoundStartingBid') )--Round starting bid + gui:AddTip(id, _TRANS('APPR_HelpTooltip_RoundStartingBid') )--This option causes the starting bid for any stacks posted to be rounded according to the following rules + gui:AddControl(id, "Checkbox", 0, 1, "util.appraiser.round.buy", _TRANS('APPR_Interface_RoundBuyoutValue') )--Round buyout value + gui:AddTip(id, _TRANS('APPR_HelpTooltip_RoundBuyoutValue') )--This option causes the buyout amount for any stacks posted to be rounded according to the following rules + gui:AddControl(id, "Selectbox", 0, 1, { + {"unit",_TRANS('APPR_Interface_StopValue') },--Stop value + {"div",_TRANS('APPR_Interface_Divisions') }--Divisions + }, "util.appraiser.round.method", _TRANS('APPR_Interface_RoundingMethodUse') )--Rounding method to use + gui:AddTip(id, _TRANS('APPR_HelpTooltip_RoundingMethodUse') )--You select the rounding algorithm to use for rounding the selected stack prices. + gui:AddControl(id, "WideSlider", 0, 1, "util.appraiser.round.position", 0.01, 1.00, 0.01, _TRANS('APPR_Interface_Rounding').." %0.02f" )--Rounding at: + gui:AddTip(id, _TRANS('APPR_HelpTooltip_Rounding') )--This slider allows you to select the position that the rounding algorithm will use to round at + gui:AddControl(id, "WideSlider", 0, 1, "util.appraiser.round.magstep", 1, 100, 1, _TRANS('APPR_Interface_StepMagnitude').." %d" )--Step magnitude at: + gui:AddTip(id, _TRANS('APPR_HelpTooltip_StepMagnitude'):format("|cffffff00") )--This slider allows you to select the point at which the rounding will step up to the next unit place %s( copper->silver->gold ). + gui:AddControl(id, "WideSlider", 0, 1, "util.appraiser.round.subtract", -99, 99, 1, _TRANS('APPR_Interface_RoundSubtract').." %d" )--Subtract fixed amount: + gui:AddTip(id, _TRANS('APPR_HelpTooltip_RoundSubtract'))--This slider allows you to subtract a fixed value from the rounded number to roughen it up (eg: put 1 here to make your "round" 1g25s into 1g24s99c. + + private.roundNote = gui:AddControl(id, "Note", 0, 2, 500, 200, "") + local fontFile = private.roundNote:GetFont() + private.roundNote:SetFont(fontFile, 15) + private.updateRoundExample() + + gui:AddHelp(id, "what is rounding", + _TRANS('APPR_Interface_WhatValueRounding') ,--What is value rounding? + _TRANS('APPR_HelpTooltip_WhatValueRounding') )--Value rounding is used to cause the auction prices of all listings where the prices are calculated (ie: not fixed price auctions) to be rounded out to neat units. An example of this is if you wanted to always round 1g 42s 15c up to 1g 42s 95c. By using this feature, you can make sure all your auctions will have their prices rounded out. + + gui:AddHelp(id, "which method", + "".._TRANS('APPR_Interface_WhichMethod'),--Which method do I use? + _TRANS('APPR_HelpTooltip_WhichMethod') )--The method that you use depends on how you want your values rounded. Appraiser currently supports 2 methods of rounding which round in different ways. The 'Stop value' method will always round up to the next occurunce of the selected rounding position. An example of this is if your stack price is 1g 42s 15c and your rounding position is 0.95, the next occurance would be at 1g 42s 95c. The 'Divisions' method will always round up to the next multiple of the selected rounding position for the selected unit (copper, silver, copper). An example of this is if your stack price is 1g 42s 32c and your rounding position is 0.25, the next occurance would be at 1g 42s 50c. + + gui:AddHelp(id, "what is position", + _TRANS('APPR_Interface_WhatRoundingPosition') ,--'What is the rounding position? + _TRANS('APPR_HelpTooltip_WhatRoundingPosition') )--The rounding position ('Rounding at' value in the settings) used by the rounding methods to determine the point at which they are going to round to. Perhaps the easiest way to see how this works is to open up the Auction House and select an item, then play with the sliders, and watch what happens to the stack prices that are listed on the right side of the auction window. + + gui:AddHelp(id, "what is magnitude", + _TRANS('APPR_Interface_WhatStepMagnitude') ,--What is the step magnitude? + _TRANS('APPR_HelpTooltip_WhatStepMagnitude'):format(" |cffffff00", "|r") )--The step magnitude specifies the point at which the algorithm decides to move up to the next unit place (%s copper->silver->gold %s). For example, if the step magnitude was set to 5, then an amount of 1g 45s 12c would round at the copper place, but an amount of 5g 45s 12c would round at the silver place. + + gui:AddHelp(id, "what is playerignore", + _TRANS('APPR_Interface_HowIgnoreSeller') ,--How to ignore a seller's auctions? + _TRANS('APPR_HelpTooltip_HowIgnoreSeller') )--ALT click on the seller you wish to ignore and select yes in the pop up window. The seller's name will be marked in red and placed in the BASIC FILTER module's ignored list. + gui:AddHelp(id, "what is playerunignore", + _TRANS('APPR_Interface_HowUnIgnoreSeller') ,--How to un-ignore a seller's auctions? + _TRANS('APPR_HelpTooltip_HowUnIgnoreSeller') )--ALT click on the seller you wish to remove from ignore and select yes in the pop up window. The seller's name will be removed from the BASIC FILTER module's ignored list. + + --[[ + gui:AddControl(id, "Subhead", 0, "Item pricing models") + + local last = gui:GetLast(id) + + gui.scalewidth = 0.4 + local content = gui.tabs[id][3] + + local box = CreateFrame("Frame", nil, content) + + local filter = gui:AddControl(id, "Text", 0.01, 1, "util.appraiser.filter", ""); + filter.textEl:Hide() + filter.textEl:SetHeight(0) + filter:SetBackdropColor(0, 0, 0.6, 0.8) + filter:SetTextInsets(50,0,0,0) + filter.clearance = -5 + filter.text = filter:CreateFontString(nil, "OVERLAY", "GameFontNormal") + filter.text:SetPoint("LEFT", filter, "LEFT", 3,0) + filter.text:SetText("Filter:") + set('util.appraiser.filter', "") + + private.items = {} + for i=1, 8 do + local frame = CreateFrame("Button", "AucApraiserItem"..i, box) + private.items[i] = frame + frame:SetHeight(20) + frame:SetScript("OnClick", private.SelectItem) + frame.id = i + + frame.icon = frame:CreateTexture(nil, "OVERLAY") + frame.icon:SetHeight(20) + frame.icon:SetWidth(20) + frame.icon:SetPoint("LEFT", frame, "LEFT", 0,0) + frame.icon:SetTexture("Interface\\InventoryItems\\WoWUnknownItem01") + + frame.name = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + frame.name:SetJustifyH("LEFT") + frame.name:SetJustifyV("BOTTOM") + frame.name:SetPoint("LEFT", frame.icon, "RIGHT", 3,0) + frame.name:SetText("[None]") + + frame.bg = frame:CreateTexture(nil, "ARTWORK") + frame.bg:SetTexture("Interface\\FriendsFrame\\UI-FriendsFrame-HighlightBar") + frame.bg:SetPoint("TOPLEFT", frame, "TOPLEFT", 0,0) + frame.bg:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", 0,0) + frame.bg:SetAlpha(0.6) + frame.bg:SetBlendMode("ADD") + + frame:SetHighlightTexture("Interface\\FriendsFrame\\UI-FriendsFrame-HighlightBar") + + frame.clearance = -3 + gui:AddControl(id, "Custom", 0, 1, frame) + end + gui.scalewidth = nil + + box:SetPoint("TOP", filter, "TOP", 0, 5) + box:SetPoint("LEFT", private.items[8], "LEFT", -5, 0) + box:SetPoint("BOTTOMRIGHT", private.items[8], "BOTTOMRIGHT", 25, -5) + box:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + edgeFile = "Interface/Tooltips/UI-Tooltip-Border", + tile = true, tileSize = 32, edgeSize = 16, + insets = { left = 5, right = 5, top = 5, bottom = 5 } + }) + box:SetBackdropColor(0, 0, 0, 0.8) + + local scroller = CreateFrame("Slider", "AucApraiserItemScroll", content); + scroller:SetPoint("TOPLEFT", private.items[1], "TOPRIGHT", 0,0) + scroller:SetPoint("BOTTOMLEFT", private.items[8], "BOTTOMRIGHT", 0,0) + scroller:SetWidth(20) + scroller:SetOrientation("VERTICAL") + scroller:SetThumbTexture("Interface\\Buttons\\UI-ScrollBar-Knob") + scroller:SetMinMaxValues(1, 30) + scroller:SetValue(1) + scroller:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + edgeFile = "Interface/Tooltips/UI-Tooltip-Border", + tile = true, tileSize = 32, edgeSize = 16, + insets = { left = 5, right = 5, top = 5, bottom = 5 } + }) + scroller:SetBackdropColor(0, 0, 0, 0.8) + scroller:SetScript("OnValueChanged", private.SetScroll) + private.scroller = scroller + + local continue = gui:GetLast(id) + + gui:SetLast(id, last) + + private.itemModel = gui:AddControl(id, "Selectbox", 0.5, 1, private.GetExtraPriceModels, "util.appraiser.item.0.model", "Pricing model to use for this item") + private.itemStack = gui:AddControl(id, "Slider", 0.5, 1, "util.appraiser.item.0.stack", 0, 20, 1, "Stack size: %s") + private.itemFixBid = gui:AddControl(id, "MoneyFramePinned", 0.5, 1, "util.appraiser.item.0.fixed.bid", 0, 99999999, "Fixed bid:") + private.itemFixBuy = gui:AddControl(id, "MoneyFramePinned", 0.5, 1, "util.appraiser.item.0.fixed.buy", 0, 99999999, "Fixed buyout:") + + gui:SetLast(id, continue) + + lib.UpdateList() +]] + + private.gui = gui + private.guiId = id +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-Appraiser/AprSettings.lua $", "$Rev: 4901 $") diff --git a/Auc-Advanced/Modules/Auc-Util-Appraiser/Auc-Util-Appraiser.toc b/Auc-Advanced/Modules/Auc-Util-Appraiser/Auc-Util-Appraiser.toc new file mode 100644 index 0000000..e563e62 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-Appraiser/Auc-Util-Appraiser.toc @@ -0,0 +1,12 @@ +## Title: Auc:Util:Appraiser +## Interface: 40000 +## Dependencies: Stubby, Auc-Advanced +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Auc-Util-Appraiser.toc 4933 2010-10-13 17:16:14Z Nechckn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## X-Max-Interface: 40000 +## +Embed.xml diff --git a/Auc-Advanced/Modules/Auc-Util-Appraiser/Embed.xml b/Auc-Advanced/Modules/Auc-Util-Appraiser/Embed.xml new file mode 100644 index 0000000..70e5652 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-Appraiser/Embed.xml @@ -0,0 +1,9 @@ + + + + diff --git a/Auc-Advanced/Modules/Auc-Util-Appraiser/Images/BatchPostTriangle.blp b/Auc-Advanced/Modules/Auc-Util-Appraiser/Images/BatchPostTriangle.blp new file mode 100644 index 0000000..1296f7f Binary files /dev/null and b/Auc-Advanced/Modules/Auc-Util-Appraiser/Images/BatchPostTriangle.blp differ diff --git a/Auc-Advanced/Modules/Auc-Util-AskPrice/AskPrice.lua b/Auc-Advanced/Modules/Auc-Util-AskPrice/AskPrice.lua new file mode 100644 index 0000000..b3a20ca --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-AskPrice/AskPrice.lua @@ -0,0 +1,575 @@ +--[[ + Auctioneer Addon for World of Warcraft(tm). + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: AskPrice.lua 4901 2010-10-05 16:44:24Z Nechckn $ + URL: http://auctioneeraddon.com/ + + Auctioneer AskPrice created by Mikezter and merged into + Auctioneer by MentalPower. Swarm response functionallity added by Kandoko. + + Functions responsible for AskPrice's operation. + + 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 libType, libName = "Util", "AskPrice" +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end +local print,decode,_,_,replicate,empty,get,set,default,debugPrint,fill,_TRANS = AucAdvanced.GetModuleLocals() + +private.whisperList = {} +private.sentRequest = {} +private.requestQueue = {} +private.sentAskPriceAd = {} +private.timeToWaitForPrices = 2 +private.timeToWaitForResponse = 5 +private.playerName = UnitName("player") +local AskPriceSentMessages = {} + +function lib.Processor(callbackType, ...) + if (callbackType == "config") then + private.SetupConfigGui(...) + end +end + +lib.Processors = {} +function lib.Processors.config(callbackType, ...) + private.SetupConfigGui(...) +end + + +function lib.OnLoad(addon) + private.frame = CreateFrame("Frame") + + private.frame:RegisterEvent("CHAT_MSG_RAID_LEADER") + private.frame:RegisterEvent("CHAT_MSG_IGNORED") + private.frame:RegisterEvent("CHAT_MSG_WHISPER") + private.frame:RegisterEvent("CHAT_MSG_OFFICER") + private.frame:RegisterEvent("CHAT_MSG_PARTY") + private.frame:RegisterEvent("CHAT_MSG_GUILD") + private.frame:RegisterEvent("CHAT_MSG_RAID") + + private.frame:RegisterEvent("CHAT_MSG_ADDON") + + private.frame:SetScript("OnEvent", private.onEvent) + private.frame:SetScript("OnUpdate", private.onUpdate) + + AucAdvanced.Const.PLAYERLANGUAGE = GetDefaultLanguage("player") + + Stubby.RegisterFunctionHook("ChatFrame_OnEvent", -200, private.onEventHook) + ChatFrame_AddMessageEventFilter("CHAT_MSG_WHISPER_INFORM", function(self,event,message,...) + if (AskPriceSentMessages[message] and not private.getOption('util.askprice.whispers')) then + AskPriceSentMessages[message] = nil + return true + end + end) + + --Setup Configator defaults + for config, value in pairs(private.defaults) do + AucAdvanced.Settings.SetDefault(config, value) + end +end + +--[[ Local functions ]]-- +--This function now runs even if AskPrice is disabled so that we don't leave stranded queries in the queue +function private.onUpdate(frame, secondsSinceLastUpdate) + --Check the request queue for timeouts (We've finished waiting for other clients to send prices) + for request, details in pairs(private.requestQueue) do + if ((GetTime() - details.timer) >= private.timeToWaitForPrices) then + if (details.querySeenBy == private.playerName) then --If we were the one who claimed the original query, then respond, else destroy the request. + private.sendRequest(request, details) + end + private.requestQueue[request] = nil + end + end + + --Check the sent queue for timeouts (We've finished waiting the asker to ignore us) + for request, details in pairs(private.sentRequest) do + if ((GetTime() - details.timer) >= private.timeToWaitForResponse) then + private.sentRequest[request] = nil + end + end +end + +function private.onEvent(frame, event, ...) + --Nothing to do if AskPrice is disabled + if (not private.getOption('util.askprice.activated')) then + return + end + + if (event == "CHAT_MSG_ADDON") then + return private.addOnEvent(...) + + elseif (event == "CHAT_MSG_IGNORED") then + return private.beingIgnored(...) + + else + return private.chatEvent(event, ...) + end +end + +function private.chatEvent(event, text, player) + local channel + if (event == "CHAT_MSG_RAID") or (event == "CHAT_MSG_PARTY") or (event == "CHAT_MSG_RAID_LEADER") then + channel = "RAID" + end + + if (event == "CHAT_MSG_GUILD") or (event == "CHAT_MSG_OFFICER") then + channel = "GUILD" + end + + if (event == "CHAT_MSG_WHISPER") then + channel = "WHISPER" + end + + if (not ( + text:find("|Hitem:", 1, true) + and + ( + text:sub(1, 1) == private.getOption('util.askprice.trigger') + or + private.isSmartWordsRequest(text:lower()) + ) + )) then + return + end + + local items = private.getItems(text) + if (channel == "GUILD") or (channel == "RAID") then + for i = 1, #items, 2 do + local count = items[i] + local link = items[i+1] + + private.sendAddOnMessage(channel, "QUERY", link, count, player, channel) + end + + elseif (channel == "WHISPER") then + for i = 1, #items, 2 do + local count = items[i] + local link = items[i+1] + + private.sendResponse(link, count, player, 1, private.getData(link)) + end + end +end + +function private.sendRequest(request, details) + local link = details.itemLink + local count = details.stackCount + local player = details.sourcePlayer + local totalPrice = details.totalPrice + local vendorPrice = details.vendorPrice + local answerCount = details.answerCount + local totalSeenCount = details.totalSeenCount + + --Format pricing data + totalPrice = math.floor(totalPrice/totalSeenCount) + + --Reset the timer and move the request over to the sent queue. + details.timer = GetTime() + private.sentRequest[request] = details + + --Send the response + return private.sendResponse(link, count, player, answerCount, totalSeenCount, totalPrice, vendorPrice) +end +--recreate the simple coin value function from EH TT. Askprice is pretty much the only module that needs to whisper non-color coin values +--look into adding this into nTipHelper +local function coins(money) + money = math.floor(tonumber(money) or 0) + local g = math.floor(money / 10000) + local s = math.floor(money % 10000 / 100) + local c = money % 100 + local gsc = "" + if (g > 0) then + gsc = gsc .. g .. "g " + end + if (s > 0) then + gsc = gsc .. s .. "s " + end + if (c > 0) then + gsc = gsc .. c .. "c " + end + return gsc +end +function private.sendResponse(link, count, player, answerCount, totalSeenCount, totalPrice, vendorPrice) + local marketPrice = totalPrice + + --If the stack size is grater than one, add the unit price to the message + local strMarketOne + if (count > 1) then + strMarketOne = (_TRANS('ASKP_Interface_Each')):format(coins(marketPrice))--(%s each) + else + strMarketOne = "" + end + + if (totalSeenCount > 0) then + local averageSeenCount = math.floor(totalSeenCount/answerCount + 0.5) + private.sendWhisper( + (_TRANS('ASKP_Interface_SeenAverageByAuctioneer') ):format( --%s: Seen an average of %d times at auction by %d people using Auctioneer + link, + averageSeenCount, + answerCount), + player + ) + private.sendWhisper( + (_TRANS('ASKP_Interface_MarketValue') ):format(--%sMarket Value: %s%s + " ", + coins(marketPrice * count), + strMarketOne), + player + ) + else + private.sendWhisper( + (_TRANS('ASKP_Interface_NeverSeenByAuctioneer') ):format(--%s: Never seen at %s by Auctioneer + link, + AucAdvanced.GetFaction() + ), + player + ) + end + + --Send out vendor info if we have it + if --[[private.getOption('util.askprice.vendor') and ]](vendorPrice and (vendorPrice > 0)) then --Vendor pricing option is still disabled + + local strVendOne + --Again if the stack Size is greater than one, add the unit price to the message + if (count > 1) then + strVendOne = (_TRANS('ASKP_Interface_Each') ):format(coins(vendorPrice))--(%s each) + else + strVendOne = "" + end + + private.sendWhisper( + (_TRANS('ASKP_Interface_SellVendor') ):format(--%sSell to vendor for: %s%s + " ", + coins(vendorPrice * count), + strVendOne + ), + player + ) + end + + if (not (count > 1)) and (private.getOption('util.askprice.ad')) then + if (not private.sentAskPriceAd[player]) then --If the player in question has been sent the ad message in this session, don't spam them again. + private.sentAskPriceAd[player] = true + private.sendWhisper((_TRANS('ASKP_Interface_GetStackPrices') ):format(private.getOption('util.askprice.trigger')), player)--Get stack prices with %sCount[ItemLink] (Count = stacksize) + end + end +end + +function private.onEventHook(_, _, self, event, arg1, ...) + if (event == "CHAT_MSG_WHISPER_INFORM") then + if (private.whisperList[arg1]) then + private.whisperList[arg1] = nil + end + end +end + +function private.getData(itemLink) + local marketValue, seenCount = AucAdvanced.API.GetMarketValue(itemLink, AucAdvanced.GetFaction()) + local vendorPrice = GetSellValue and GetSellValue(itemLink) + + return seenCount or 0, marketValue or 0, vendorPrice or 0 +end + +--Many thanks to the guys at irc://chat.freenode.net/wowi-lounge for their help in creating this function +local itemList = {} +function private.getItems(str) + for i = #itemList, 1, -1 do + itemList[i] = nil + end + + for number, color, item, name in str:gmatch("(%d*)%s*|c(%x+)|Hitem:([^|]+)|h%[(.-)%]|h|r") do + table.insert(itemList, tonumber(number) or 1) + table.insert(itemList, "|c"..color.."|Hitem:"..item.."|h["..name.."]|h|r") + end + return itemList +end + +function private.sendWhisper(message, player) + private.whisperList[message] = true + if not private.getOption('util.askprice.whispers') then + AskPriceSentMessages[message] = true + end + ChatThrottleLib:SendChatMessage("ALERT", "AucAdvAskPrice", message, "WHISPER", AucAdvanced.Const.PLAYERLANGUAGE, player) +end + +function private.sendAddOnMessage(channel, ...) + local message = string.join(";", ...) + ChatThrottleLib:SendAddonMessage("NORMAL", "AucAdvAskPrice", message, channel) +end + +function private.addOnEvent(prefix, message, sourceChannel, sourcePlayer) + if (not (prefix == "AucAdvAskPrice")) then + return + end + + --Decode the message + local requestType, link, count, player, channel, totalPrice, totalSeenCount, vendorPrice, answerCount, ignoreList = string.split(";", message) + local request + if (link and count and player and channel) then + request = link..count..player..channel + end + + if (sourceChannel == "PARTY") then --Adjust the source if its party. + sourceChannel = "RAID" + end + + if (requestType == "QUERY") then --AskPrice query was received by someone and is requesting prices for that query. + private.requestQueue[request] = private.requestQueue[request] or { + timer = GetTime(), + querySeenBy = sourcePlayer, + + itemLink = link, + stackCount = tonumber(count), + sourcePlayer = player, + sourceChannel = channel, + + totalPrice = 0, + answerCount = 0, + totalSeenCount = 0, + } + + if not private.requestQueue[request].sentPrice then --Only respond if we have not already done so + local seenCount, marketValue, vendorPrice = private.getData(link) + private.sendAddOnMessage(sourceChannel, "PRICE", link, count, player, channel, marketValue, seenCount, vendorPrice) + private.requestQueue[request].sentPrice = true + end + + elseif (requestType == "PRICE") then --AskPrice users are responding to the query. We only listen to these if we were the first to send out the QUERY event that pertains to this PRICE event (see above) + local request = private.requestQueue[request] + if (request and (request.querySeenBy == private.playerName)) then + if not (request.priceProviders and request.priceProviders:find(sourcePlayer)) then + --Better stat average formula for each response: total = total + (price * seen); count = count + seen; average = total/count + local count = request.totalSeenCount + totalSeenCount + local total = request.totalPrice + (totalPrice * totalSeenCount) + + request.totalPrice = total + request.totalSeenCount = count + request.vendorPrice = request.vendorPrice or tonumber(vendorPrice) + request.answerCount = request.answerCount + 1 + end + end + + if request then --Record providers, even if we are not going to answer the query + if not request.priceProviders then + request.priceProviders = sourcePlayer + else + request.priceProviders = request.priceProviders..":"..sourcePlayer + end + end + + elseif (requestType == "WFAIL") and (not ignoreList:find(private.playerName)) then --Whisper failed (Announcer is being ignored) + private.requestQueue[request] = { + timer = GetTime(), + ignoreList = ignoreList, + + itemLink = link, + stackCount = count, + sourcePlayer = player, + sourceChannel = channel, + + totalPrice = totalPrice, + vendorPrice = vendorPrice, + answerCount = answerCount, + totalSeenCount = totalSeenCount + } + + private.sendAddOnMessage(sourceChannel, "MFAIL", link, count, player, channel, totalPrice, totalSeenCount, vendorPrice, answerCount, ignoreList) + + elseif (requestType == "MFAIL") then --New type for v2, means "My Fail", if the current user receives one for a request in the queue, its either killed if it was not us that sent it, or answered if it was. + if (sourcePlayer == private.playerName) and (private.requestQueue[request]) then + private.sendRequest(request, private.requestQueue[request]) + private.requestQueue[request] = nil + + else + private.requestQueue[request] = nil + end + + elseif (requestType == "MAINR") then --General type of request (Version, login, etc) + local subRequest = link + + if (subRequest == "version") then + private.sendAddOnMessage(sourceChannel, "MAINR", "version:", private.GetVersion()) --Extended for v2 comms (See GetVersion() definition) + + elseif (subRequest == "version:") then + --Responses to the above "version" request, currently ignored + + elseif (subRequest == "login") or (subRequest == "logout") or (subRequest == "online") then + --Used in v1 comms, currently ignored + end + end +end + +function private.beingIgnored(sourcePlayer) + --Check the sent queue for occurances of the ignored player + for request, details in pairs(private.sentRequest) do + if (details.sourcePlayer == sourcePlayer) then + local link, count, player, channel = details.itemLink, details.stackCount, details.sourcePlayer, details.sourceChannel + local totalPrice, totalSeenCount, vendorPrice, answerCount, ignoreList = details.totalPrice, details.totalSeenCount, details.vendorPrice, details.answerCount, details.ignoreList + + if ignoreList then --Either add our list to the existent ignoreList or make our name the first entry + ignoreList = ignoreList..":"..private.playerName + + else + ignoreList = private.playerName + end + + private.sendAddOnMessage(sourceChannel, "WFAIL", link, count, player, channel, totalPrice, totalSeenCount, vendorPrice, answerCount, ignoreList) --Expanded in v2 comms to include the ignoreList + end + end +end + +--This function changed after AskPrice revision 2825 to include AucAdvanced's revision number in adition to AskPrice's +function private.GetVersion() + return tonumber(("$Revision: 4901 $"):match("(%d+)")), (AucAdvanced.GetCurrentRevision()) --We just want the first return from GetCurrentRevision() +end + +--This function is used to check if the received request (which should be lowercased before the function is called) is a valid SmartWords request +function private.isSmartWordsRequest(text) + if private.getOption('util.askprice.smart') then + if private.getOption('util.askprice.smartOr') == 1 then + return text:find(private.getOption('util.askprice.word1'):lower(), 1, true) + or text:find(private.getOption('util.askprice.word2'):lower(), 1, true) + else + return text:find(private.getOption('util.askprice.word1'):lower(), 1, true) + and text:find(private.getOption('util.askprice.word2'):lower(), 1, true) + end + end +end + +private.SlashHandler = {} + +--This is the function that will be called by the slash handler when +--/askprice send is issued. +function private.SlashHandler.send(queryString) + local parseError = false + if queryString then + local player, itemLinks = strsplit(" ", queryString, 2) + print(player, itemLinks) + + --Error out if we have a target, but no potential itemLinks + if itemLinks then + local items = private.getItems(itemLinks) + --Error out if we dont get any items back + if #items == 0 then parseError = true end + + for i = 1, #items, 2 do + local count = items[i] + local link = items[i+1] + + private.sendResponse(link, count, player, 1, private.getData(link)) + end + else + parseError = true + end + else + parseError = true + end + + if parseError then + print("The correct syntax is {{/asprice send Player <#>[Item Link]}}, where {{<#>}} is the stack size (optional) and {{Player}} is the person you wish to send to.") + end +end + +--This function handles parsing of the /askprice commands +function private.slashcommands(commandstring) + if commandstring then + local command, remains = strsplit(" ",commandstring, 2) + if command then + command = command:lower() + if private.SlashHandler[command] then + private.SlashHandler[command](remains) + end + end + end +end + +--Add the slash command +SlashCmdList['AUC_UTIL_ASKPRICE_SEND'] = private.slashcommands +_G['SLASH_AUC_UTIL_ASKPRICE_SEND1'] = '/askprice' + +--[[ Configator Section ]]-- +private.defaults = { + ["util.askprice.activated"] = true, + ["util.askprice.ad"] = true, + ["util.askprice.smart"] = true, + ["util.askprice.trigger"] = "?", + ["util.askprice.vendor"] = false, + ["util.askprice.whispers"] = true, + ["util.askprice.word1"] = "what", + ["util.askprice.word2"] = "worth", + ["util.askprice.smartOr"] = 0, --1/0 instead of true/false due to limitations in the Configator API +} + +function private.getOption(option) + return AucAdvanced.Settings.GetSetting(option) +end + +function private.SetupConfigGui(gui) + -- The defaults for the following settings are set in the lib.OnLoad function + local id = gui:AddTab(libName, libType.." Modules") + gui:MakeScrollable(id) + + gui:AddHelp(id, "what askprice", + _TRANS('ASKP_Help_WhatAskPrice'), --"What is AskPrice and what does it do?" + _TRANS('ASKP_Help_WhatAskPriceAnswer')) --"AskPrice is a module that allows other players to obtain the values of items by sending special messages to various channels, or by sending those messages to you directly, via a whisper." + + gui:AddControl(id, "Header", 0, libName.._TRANS('ASKP_Interface_Options')) --" options" + gui:AddControl(id, "Checkbox", 0, 1, "util.askprice.activated", _TRANS('ASKP_Interface_Activated')) --"Respond to queries for item market values sent via chat" + gui:AddTip(id, _TRANS('ASKP_HelpTooltip_Activated')) --"This checkbox will enable or disable the module." + + gui:AddHelp(id, "what triggers", + _TRANS('ASKP_Help_WhatTriggers'), --"What are these triggers, and how are they used?" + _TRANS('ASKP_Help_WhatTriggersAnswer')) --"The triggers control how someone needs to ask you for the price. \nThe Custom Smartwords allow Auctioneer to respond to natural language queries, while the Trigger Character allows for querying stack sizes \n\nCustom smartwords default respond to \'what is [item link] worth?\' \nTrigger character defaults respond to \'? (stack size) [item link]\'" + + + gui:AddControl(id, "Subhead", 0, _TRANS('ASKP_Interface_SimpleTrigger')) --"Simple trigger:" + gui:AddControl(id, "Text", 0, 1, "util.askprice.trigger", _TRANS('ASKP_Interface_TriggerCharacter')) --"Askprice Trigger character" + gui:AddTip(id, _TRANS('ASKP_HelpTooltip_TriggerCharacter')) --"The trigger character allows for simple querying of a price." + + gui:AddControl(id, "Subhead", 0, _TRANS('ASKP_Interface_SmartWords')) --SmartWords: + gui:AddControl(id, "Checkbox", 0, 1, "util.askprice.smart", _TRANS('ASKP_Interface_EnableSmartWords')) --"Enable SmartWords checking" + gui:AddTip(id, _TRANS('ASKP_HelpTooltip_SmartWords')) --"Toggling this will enable responses to the SmartWords." + local last = gui:GetLast(id) -- Get the current position so we can return here for the second column + gui:AddControl(id, "Text", 0, 1, "util.askprice.word1", _TRANS('ASKP_Interface_SmartWordOne')) --"Askprice Custom SmartWord #1" + gui:AddTip(id, _TRANS('ASKP_HelpTooltip_SmartWordOne')) --"The SmartWords allow for natural language queries." + gui:SetLast(id, last) -- Return to the saved position + gui:AddControl(id, "Text", 0.5, 1, "util.askprice.word2", _TRANS('ASKP_Interface_SmartWordTwo')) --"Askprice Custom SmartWord #2" + gui:AddTip(id, _TRANS('ASKP_HelpTooltip_SmartWordOne')) --"The SmartWords allow for natural language queries." + gui:AddControl(id, "Selectbox", 0, 1, { + {1, "Either"}, + {0, "Both"} + }, "util.askprice.smartOr", "Either or both SmartWords") --1/0 instead of true/false due to limitations in the Configator API + gui:AddTip(id, _TRANS('ASKP_HelpTooltip_EitherBoth')) --"Both SmartWords are required to be present for just one or the other in order for it to trigger a query." + + gui:AddControl(id, "Subhead", 0, _TRANS('ASKP_Interface_Miscellaneous')) --"Miscellaneous:") + gui:AddControl(id, "Checkbox", 0, 1, "util.askprice.ad", _TRANS('ASKP_Interface_TutorialMessage')) --"Enable sending of tutorial message." + gui:AddTip(id, _TRANS('ASKP_HelpTooltip_TutorialMessage')) --"If enabled, this will send players who ask for prices a message telling them how to use the trigger character in conjunction with a stack size parameter." + gui:AddControl(id, "Checkbox", 0, 1, "util.askprice.whispers", _TRANS('ASKP_Interface_Whisper')) --"Show outgoing whispers from Askprice" + gui:AddTip(id, _TRANS('ASKP_HelpTooltip_Whisper')) --"Shows (enabled) or hides (disabled) outgoing whispers from Askprice." + +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-AskPrice/AskPrice.lua $", "$Rev: 4901 $") diff --git a/Auc-Advanced/Modules/Auc-Util-AskPrice/Auc-Util-AskPrice.toc b/Auc-Advanced/Modules/Auc-Util-AskPrice/Auc-Util-AskPrice.toc new file mode 100644 index 0000000..fe090ae --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-AskPrice/Auc-Util-AskPrice.toc @@ -0,0 +1,14 @@ +## Title: Auc:Util:Ask Price +## Notes: Auctioneer utility that automatically responds to requests for pricing via the guild and, or, party chat channesl. +## +## Interface: 40000 +## Dependancies: Auc-Advanced +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Auc-Util-AskPrice.toc 4933 2010-10-13 17:16:14Z Nechckn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## X-Max-Interface: 40000 +## +Embed.xml diff --git a/Auc-Advanced/Modules/Auc-Util-AskPrice/ChatThrottleLib/Changelog-ChatThrottleLib-v20.txt b/Auc-Advanced/Modules/Auc-Util-AskPrice/ChatThrottleLib/Changelog-ChatThrottleLib-v20.txt new file mode 100644 index 0000000..9a6a1fa --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-AskPrice/ChatThrottleLib/Changelog-ChatThrottleLib-v20.txt @@ -0,0 +1,374 @@ +------------------------------------------------------------------------ +r65 | mikk | 2008-10-28 09:52:38 +0000 (Tue, 28 Oct 2008) | 1 line +Changed paths: + A /tags/v20 (from /trunk:64) + +Tagging as v20. +------------------------------------------------------------------------ +r64 | mikk | 2008-10-15 11:44:16 +0000 (Wed, 15 Oct 2008) | 5 lines +Changed paths: + M /trunk/ChatThrottleLib.lua + M /trunk/ChatThrottleLib.toc + +ChatThrottleLib: +- v20 +- TOC version 30000 +- No longer LoD due to 3.0.2 bug +- "prefix" is no longer optional (not that anyone ever left it as nil to my knowledge) +------------------------------------------------------------------------ +r63 | nevcairiel | 2008-10-09 21:51:12 +0000 (Thu, 09 Oct 2008) | 1 line +Changed paths: + M /trunk/ChatThrottleLib.lua + M /trunk/ChatThrottleStats.lua + +WoWAce Post-Processing: Virtually inflate Library Revision numbers for proper upgrade path +------------------------------------------------------------------------ +r62 | root | 2008-09-29 22:14:43 +0000 (Mon, 29 Sep 2008) | 1 line +Changed paths: + A /trunk/.pkgmeta + +Facilitate WowAce-on-CurseForge transition +------------------------------------------------------------------------ +r60 | root | 2008-09-29 20:57:44 +0000 (Mon, 29 Sep 2008) | 1 line +Changed paths: + D /tmp/trunk/ChatThrottleLib + A /trunk (from /tmp/trunk/ChatThrottleLib:59) + +Importing old repo data under /trunk +------------------------------------------------------------------------ +r56 | rabbit | 2008-03-26 11:55:28 +0000 (Wed, 26 Mar 2008) | 1 line +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.toc + +.Some bumps. +------------------------------------------------------------------------ +r54 | mikk | 2007-11-14 10:43:27 +0000 (Wed, 14 Nov 2007) | 1 line +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.toc + +ChatThrottleLib: toc 20300 +------------------------------------------------------------------------ +r53 | mikk | 2007-11-11 08:34:24 +0000 (Sun, 11 Nov 2007) | 2 lines +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.lua + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.toc + M /tmp/trunk/ChatThrottleLib/README.txt + +ChatThrottleLib: v19: +- Add optional "queueName" parameter as last arg to both Send functions. If supplied, queueing will be done according to this name rather than prefix+chattype+destination. Useful for protocols that mangle the prefix, e.g. AceComm3, or addons that want in-sequence delivery of messages regardless of prefix. +------------------------------------------------------------------------ +r52 | mikk | 2007-10-14 07:38:17 +0000 (Sun, 14 Oct 2007) | 1 line +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.toc + +ChatThrottleLib: Up toc to 20200 +------------------------------------------------------------------------ +r51 | mikk | 2007-09-25 21:49:52 +0000 (Tue, 25 Sep 2007) | 3 lines +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.lua + +ChatThrottleLib: +- Sigh, fix the fix. It worked everywhere except AceComm. + +------------------------------------------------------------------------ +r50 | mikk | 2007-09-25 20:46:13 +0000 (Tue, 25 Sep 2007) | 3 lines +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.lua + +ChatThrottleLib: +- Better fix for copy&pasting ChatThrottleLib into a lua file that gets it into its head to "local ChatThrottleLib" (aka AceComm). It should not ALWAYS Do The Right Thing, not just when AceComm loads before other CTL users. + +------------------------------------------------------------------------ +r49 | mikk | 2007-08-21 08:14:31 +0000 (Tue, 21 Aug 2007) | 2 lines +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.lua + +ChatThrottleLib: +getfenv().ChatThrottleLib = ChatThrottleLib -- lets it work embedded inside other files where "ChatThrottleLib" is a local (e.g. AceComm-2.0.lua) +------------------------------------------------------------------------ +r48 | mikk | 2007-08-20 12:28:43 +0000 (Mon, 20 Aug 2007) | 5 lines +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.lua + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.toc + M /tmp/trunk/ChatThrottleLib/README.txt + +ChatThrottleLib: +- v18 +- Fix an unending error barfer if the number of unique destinations ever exceeded 25 (garbage collector was broken) +- Add assert(len<255) to :SendChatMessage() also + +------------------------------------------------------------------------ +r47 | mikk | 2007-06-24 17:42:11 +0000 (Sun, 24 Jun 2007) | 2 lines +Changed paths: + M /tmp/trunk/ChatThrottleLib + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.toc + +ChatThrottleLib: Add TOC fields. Change author to just "Mikk" + +------------------------------------------------------------------------ +r46 | mikk | 2007-06-02 12:53:12 +0000 (Sat, 02 Jun 2007) | 2 lines +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.toc + +ChatThrottleLib: Prefix "Lib: " to name. + +------------------------------------------------------------------------ +r45 | mikk | 2007-05-26 03:42:26 +0000 (Sat, 26 May 2007) | 5 lines +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.lua + M /tmp/trunk/ChatThrottleLib/README.txt + +ChatThrottleLib: +- v17 +- SendAddonMessage: assert() that text+prefix+1<=255 since exceeding it will cause a disconnect, which may look like overload problems when in fact they aren't + + +------------------------------------------------------------------------ +r44 | mikk | 2007-05-23 13:59:42 +0000 (Wed, 23 May 2007) | 2 lines +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.toc + +ChatThrottleLib: 20100 + +------------------------------------------------------------------------ +r43 | ckknight | 2007-04-24 09:11:48 +0000 (Tue, 24 Apr 2007) | 1 line +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.lua + +ChatThrottleLib - remove a SETGLOBAL msg. +------------------------------------------------------------------------ +r42 | ckknight | 2007-04-14 07:51:10 +0000 (Sat, 14 Apr 2007) | 1 line +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.lua + +ChatThrottleLib - v16 - use secure hooks instead of insecure hooks. +------------------------------------------------------------------------ +r41 | mikk | 2007-04-08 20:11:42 +0000 (Sun, 08 Apr 2007) | 6 lines +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.lua + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.toc + M /tmp/trunk/ChatThrottleLib/ChatThrottleStats.lua + M /tmp/trunk/ChatThrottleLib/README.txt + +ChatThrottleLib: +- v15 +- Allow new "target" parameter in SendAddonMessage (gets introduced in 2.1) +- Move constants (cps, burst size, etc) from locals to member variables to allow tweaking from outside sources as requested by power users (huhu, phear). + + +------------------------------------------------------------------------ +r40 | mikk | 2007-04-04 10:53:39 +0000 (Wed, 04 Apr 2007) | 1 line +Changed paths: + M /tmp/trunk/ChatThrottleLib/README.txt + +ChatThrottleLib: README.txt update with new urls, etc. +------------------------------------------------------------------------ +r39 | rabbit | 2007-03-08 17:24:35 +0000 (Thu, 08 Mar 2007) | 1 line +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.lua + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.toc + +ChatThrottleLib: Bump the TOC interface to 20003. +------------------------------------------------------------------------ +r38 | kergoth | 2007-01-30 00:14:59 +0000 (Tue, 30 Jan 2007) | 1 line +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.toc + +.Line ending fixups in .toc files (they werent in my .subversion/config autoprops) +------------------------------------------------------------------------ +r37 | ckknight | 2007-01-27 21:07:25 +0000 (Sat, 27 Jan 2007) | 1 line +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.lua + +ChatThrottleLib - added a few locals, made to take advantage of lua 5.1 in a few places, general syntax cleanup. +------------------------------------------------------------------------ +r36 | kergoth | 2007-01-23 21:50:43 +0000 (Tue, 23 Jan 2007) | 1 line +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.lua + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.xml + M /tmp/trunk/ChatThrottleLib/ChatThrottleStats.lua + M /tmp/trunk/ChatThrottleLib/README.txt + +.Line ending fixups: trunk/ +------------------------------------------------------------------------ +r33 | mikk | 2006-09-24 17:36:37 +0000 (Sun, 24 Sep 2006) | 5 lines +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.toc + +ChatThrottleLib: +- Up version number in .toc to match .lua (12->13) +~smack ckknight + + +------------------------------------------------------------------------ +r32 | ckknight | 2006-09-23 06:10:29 +0000 (Sat, 23 Sep 2006) | 2 lines +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.lua + +ChatThrottleLib - local'd some global functions. Now no longer crashes when someone messes up calling SendChatMessage/SendAddonMessage. +Upped version number +------------------------------------------------------------------------ +r31 | ckknight | 2006-09-21 06:54:05 +0000 (Thu, 21 Sep 2006) | 1 line +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.lua + +ChatThrottleLib - added missing pairs() to an iterator. +------------------------------------------------------------------------ +r30 | mikk | 2006-09-15 09:13:17 +0000 (Fri, 15 Sep 2006) | 4 lines +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.lua + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.toc + M /tmp/trunk/ChatThrottleLib/README.txt + +ChatThrottleLib: Version 12: +- Reduce max CPS from 1000 to 800 +- Reduce max burst from 8000 to 4000 +Apparently, a few (old? misconfigurd?) servers are crankier than all the others. This seems to fix it. +------------------------------------------------------------------------ +r29 | mikk | 2006-08-25 10:05:44 +0000 (Fri, 25 Aug 2006) | 4 lines +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.lua + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.toc + M /tmp/trunk/ChatThrottleLib/README.txt + +ChatThrottleLib: +* Version 11 +* Clamp VERY hard the 5 first seconds after login and zoning. The rate limiter seems touchy at least right after login. + +------------------------------------------------------------------------ +r25 | mikk | 2006-08-10 02:13:53 +0000 (Thu, 10 Aug 2006) | 3 lines +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.lua + +ChatThrottleLib: +- v10 for real now. SIGH. + +------------------------------------------------------------------------ +r24 | mikk | 2006-08-10 02:08:32 +0000 (Thu, 10 Aug 2006) | 4 lines +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.lua + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.toc + M /tmp/trunk/ChatThrottleLib/README.txt + +ChatThrottleLib: +Version 10: +* Don't hook SendAddonMessage if it doesn't exist. Some mod might be using it to test for 1.12. + +------------------------------------------------------------------------ +r23 | mikk | 2006-08-08 22:46:50 +0000 (Tue, 08 Aug 2006) | 4 lines +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.lua + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.toc + M /tmp/trunk/ChatThrottleLib/README.txt + +ChatThrottleLib: +* Use string.format() for string concatenation +* Up to version 9 - pushing to wowi. + +------------------------------------------------------------------------ +r22 | ckknight | 2006-08-08 20:48:48 +0000 (Tue, 08 Aug 2006) | 1 line +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.lua + +ChatThrottleLib - fixed bug that made addon messages spam to standard chat. +------------------------------------------------------------------------ +r21 | mikk | 2006-08-01 19:24:37 +0000 (Tue, 01 Aug 2006) | 2 lines +Changed paths: + M /tmp/trunk/ChatThrottleLib/README.txt + +ChatThrottleLib: Update README.txt too. *sigh* + +------------------------------------------------------------------------ +r20 | mikk | 2006-08-01 19:11:38 +0000 (Tue, 01 Aug 2006) | 7 lines +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.lua + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.toc + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.xml + A /tmp/trunk/ChatThrottleLib/ChatThrottleStats.lua + M /tmp/trunk/ChatThrottleLib/README.txt + +ChatThrottleLib: +- Version 7 +- Adaptive throttling according to frame rate +- Adaptive throttling according to traffic bypassing the library +- Allow "prefix" to be nil in :SendChatMessage() - use one flow per "this" +- Add a stats module that can be used during devel + +------------------------------------------------------------------------ +r19 | mikk | 2006-07-31 21:25:57 +0000 (Mon, 31 Jul 2006) | 3 lines +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.toc + A /tmp/trunk/ChatThrottleLib/ChatThrottleLib.xml + M /tmp/trunk/ChatThrottleLib/README.txt + +ChatThrottleLib: +- Add ChatThrottleLib.xml to allow for easy directory-based embedding. (Just include ChatThrottleLib/ChatThrottleLib.xml in your .toc). + +------------------------------------------------------------------------ +r18 | mikk | 2006-07-30 17:16:05 +0000 (Sun, 30 Jul 2006) | 3 lines +Changed paths: + A /tmp/trunk/ChatThrottleLib/README.txt + +ChatThrottleLib: +Add README.txt and package for upload to wowi. + +------------------------------------------------------------------------ +r17 | mikk | 2006-07-30 14:41:21 +0000 (Sun, 30 Jul 2006) | 7 lines +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.lua + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.toc + +ChatThrottleLib: +- Version 6 +- Will now immediately pass messages to APIs as long as queuing is not necessary (= no lag unless there needs to be) +- Will now allow burst output of up to 8KB. The server input buffer seems to be about 32KB. + +Seems doneish for the initial design. + +------------------------------------------------------------------------ +r16 | ckknight | 2006-07-29 14:07:27 +0000 (Sat, 29 Jul 2006) | 1 line +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.lua + +ChatThrottleLib - changed setn stuff, since it could mess up from a nil language. Upped minor version. +------------------------------------------------------------------------ +r15 | mikk | 2006-07-26 20:21:09 +0000 (Wed, 26 Jul 2006) | 2 lines +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.lua + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.toc + +ChatThrottleLib: +- Removed _optional_ ChatThrottleLib:Hook() function and there was much rejoicing by all. +------------------------------------------------------------------------ +r14 | mikk | 2006-07-25 20:36:33 +0000 (Tue, 25 Jul 2006) | 2 lines +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.lua + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.toc + +ChatThrottleLib: +- Will now clean out portions of its recycling bins too once in a while. Whoops. Tired coder = bad. +------------------------------------------------------------------------ +r13 | mikk | 2006-07-25 20:00:01 +0000 (Tue, 25 Jul 2006) | 4 lines +Changed paths: + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.lua + M /tmp/trunk/ChatThrottleLib/ChatThrottleLib.toc + +ChatThrottleLib: +- Drop CPS down to 1000. 2000 seems safe if nothing else is happening, but we need margins. +- Make SendChatMessage / SendAddonMessage hooking optional. AddOn must call ChatThrottleLib:Hook() in order for it to happen now. + +------------------------------------------------------------------------ +r12 | mikk | 2006-07-25 18:25:24 +0000 (Tue, 25 Jul 2006) | 7 lines +Changed paths: + A /tmp/trunk/ChatThrottleLib + A /tmp/trunk/ChatThrottleLib/ChatThrottleLib.lua + A /tmp/trunk/ChatThrottleLib/ChatThrottleLib.toc + +ChatThrottleLib: +Fully embeddable chat throttling library that: +- Hooks SendChatMessage / SendAdd[Oo]nMessage +- Provides extra functions for sending prioritized messages +- Does full balancing between the three priorities ("ALERT", "NORMAL", "BULK") +- Does round-robin traffic shaping between different prefix+chattype+destination tuples + +------------------------------------------------------------------------ diff --git a/Auc-Advanced/Modules/Auc-Util-AskPrice/ChatThrottleLib/ChatThrottleLib.lua b/Auc-Advanced/Modules/Auc-Util-AskPrice/ChatThrottleLib/ChatThrottleLib.lua new file mode 100644 index 0000000..304a7be --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-AskPrice/ChatThrottleLib/ChatThrottleLib.lua @@ -0,0 +1,454 @@ +-- +-- ChatThrottleLib by Mikk +-- +-- Manages AddOn chat output to keep player from getting kicked off. +-- +-- ChatThrottleLib.SendChatMessage/.SendAddonMessage functions that accept +-- a Priority ("BULK", "NORMAL", "ALERT") as well as prefix for SendChatMessage. +-- +-- Priorities get an equal share of available bandwidth when fully loaded. +-- Communication channels are separated on extension+chattype+destination and +-- get round-robinned. (Destination only matters for whispers and channels, +-- obviously) +-- +-- Will install hooks for SendChatMessage and SendAdd[Oo]nMessage to measure +-- bandwidth bypassing the library and use less bandwidth itself. +-- +-- +-- Fully embeddable library. Just copy this file into your addon directory, +-- add it to the .toc, and it's done. +-- +-- Can run as a standalone addon also, but, really, just embed it! :-) +-- + +local CTL_VERSION = 20 + +if _G.ChatThrottleLib and _G.ChatThrottleLib.version >= CTL_VERSION then + -- There's already a newer (or same) version loaded. Buh-bye. + return +end + +if not _G.ChatThrottleLib then + _G.ChatThrottleLib = {} +end + +ChatThrottleLib = _G.ChatThrottleLib -- in case some addon does "local ChatThrottleLib" above use and we're copypasted (AceComm-2, sigh) +local ChatThrottleLib = _G.ChatThrottleLib + +------------------ TWEAKABLES ----------------- + +ChatThrottleLib.MAX_CPS = 800 -- 2000 seems to be safe if NOTHING ELSE is happening. let's call it 800. +ChatThrottleLib.MSG_OVERHEAD = 40 -- Guesstimate overhead for sending a message; source+dest+chattype+protocolstuff + +ChatThrottleLib.BURST = 4000 -- WoW's server buffer seems to be about 32KB. 8KB should be safe, but seen disconnects on _some_ servers. Using 4KB now. + +ChatThrottleLib.MIN_FPS = 20 -- Reduce output CPS to half (and don't burst) if FPS drops below this value + + +local setmetatable = setmetatable +local table_remove = table.remove +local tostring = tostring +local GetTime = GetTime +local math_min = math.min +local math_max = math.max +local next = next +local strlen = string.len + +ChatThrottleLib.version = CTL_VERSION + + +----------------------------------------------------------------------- +-- Double-linked ring implementation + +local Ring = {} +local RingMeta = { __index = Ring } + +function Ring:New() + local ret = {} + setmetatable(ret, RingMeta) + return ret +end + +function Ring:Add(obj) -- Append at the "far end" of the ring (aka just before the current position) + if self.pos then + obj.prev = self.pos.prev + obj.prev.next = obj + obj.next = self.pos + obj.next.prev = obj + else + obj.next = obj + obj.prev = obj + self.pos = obj + end +end + +function Ring:Remove(obj) + obj.next.prev = obj.prev + obj.prev.next = obj.next + if self.pos == obj then + self.pos = obj.next + if self.pos == obj then + self.pos = nil + end + end +end + + + +----------------------------------------------------------------------- +-- Recycling bin for pipes +-- A pipe is a plain integer-indexed queue, which also happens to be a ring member + +ChatThrottleLib.PipeBin = nil -- pre-v19, drastically different +local PipeBin = setmetatable({}, {__mode="k"}) + +local function DelPipe(pipe) + for i = #pipe, 1, -1 do + pipe[i] = nil + end + pipe.prev = nil + pipe.next = nil + + PipeBin[pipe] = true +end + +local function NewPipe() + local pipe = next(PipeBin) + if pipe then + PipeBin[pipe] = nil + return pipe + end + return {} +end + + + + +----------------------------------------------------------------------- +-- Recycling bin for messages + +ChatThrottleLib.MsgBin = nil -- pre-v19, drastically different +local MsgBin = setmetatable({}, {__mode="k"}) + +local function DelMsg(msg) + msg[1] = nil + -- there's more parameters, but they're very repetetive so the string pool doesn't suffer really, and it's faster to just not delete them. + MsgBin[msg] = true +end + +local function NewMsg() + local msg = next(MsgBin) + if msg then + MsgBin[msg] = nil + return msg + end + return {} +end + + +----------------------------------------------------------------------- +-- ChatThrottleLib:Init +-- Initialize queues, set up frame for OnUpdate, etc + + +function ChatThrottleLib:Init() + + -- Set up queues + if not self.Prio then + self.Prio = {} + self.Prio["ALERT"] = { ByName = {}, Ring = Ring:New(), avail = 0 } + self.Prio["NORMAL"] = { ByName = {}, Ring = Ring:New(), avail = 0 } + self.Prio["BULK"] = { ByName = {}, Ring = Ring:New(), avail = 0 } + end + + -- v4: total send counters per priority + for _, Prio in pairs(self.Prio) do + Prio.nTotalSent = Prio.nTotalSent or 0 + end + + if not self.avail then + self.avail = 0 -- v5 + end + if not self.nTotalSent then + self.nTotalSent = 0 -- v5 + end + + + -- Set up a frame to get OnUpdate events + if not self.Frame then + self.Frame = CreateFrame("Frame") + self.Frame:Hide() + end + self.Frame:SetScript("OnUpdate", self.OnUpdate) + self.Frame:SetScript("OnEvent", self.OnEvent) -- v11: Monitor P_E_W so we can throttle hard for a few seconds + self.Frame:RegisterEvent("PLAYER_ENTERING_WORLD") + self.OnUpdateDelay = 0 + self.LastAvailUpdate = GetTime() + self.HardThrottlingBeginTime = GetTime() -- v11: Throttle hard for a few seconds after startup + + -- Hook SendChatMessage and SendAddonMessage so we can measure unpiped traffic and avoid overloads (v7) + if not self.ORIG_SendChatMessage then + -- use secure hooks instead of insecure hooks (v16) + self.securelyHooked = true + --SendChatMessage + self.ORIG_SendChatMessage = SendChatMessage + hooksecurefunc("SendChatMessage", function(...) + return ChatThrottleLib.Hook_SendChatMessage(...) + end) + self.ORIG_SendAddonMessage = SendAddonMessage + --SendAddonMessage + hooksecurefunc("SendAddonMessage", function(...) + return ChatThrottleLib.Hook_SendAddonMessage(...) + end) + end + self.nBypass = 0 +end + + +----------------------------------------------------------------------- +-- ChatThrottleLib.Hook_SendChatMessage / .Hook_SendAddonMessage +function ChatThrottleLib.Hook_SendChatMessage(text, chattype, language, destination, ...) + local self = ChatThrottleLib + local size = strlen(tostring(text or "")) + strlen(tostring(destination or "")) + self.MSG_OVERHEAD + self.avail = self.avail - size + self.nBypass = self.nBypass + size -- just a statistic + if not self.securelyHooked then + self.ORIG_SendChatMessage(text, chattype, language, destination, ...) + end +end +function ChatThrottleLib.Hook_SendAddonMessage(prefix, text, chattype, destination, ...) + local self = ChatThrottleLib + local size = tostring(text or ""):len() + tostring(prefix or ""):len(); + size = size + tostring(destination or ""):len() + self.MSG_OVERHEAD + self.avail = self.avail - size + self.nBypass = self.nBypass + size -- just a statistic + if not self.securelyHooked then + self.ORIG_SendAddonMessage(prefix, text, chattype, destination, ...) + end +end + + + +----------------------------------------------------------------------- +-- ChatThrottleLib:UpdateAvail +-- Update self.avail with how much bandwidth is currently available + +function ChatThrottleLib:UpdateAvail() + local now = GetTime() + local MAX_CPS = self.MAX_CPS; + local newavail = MAX_CPS * (now - self.LastAvailUpdate) + local avail = self.avail + + if now - self.HardThrottlingBeginTime < 5 then + -- First 5 seconds after startup/zoning: VERY hard clamping to avoid irritating the server rate limiter, it seems very cranky then + avail = math_min(avail + (newavail*0.1), MAX_CPS*0.5) + self.bChoking = true + elseif GetFramerate() < self.MIN_FPS then -- GetFrameRate call takes ~0.002 secs + avail = math_min(MAX_CPS, avail + newavail*0.5) + self.bChoking = true -- just a statistic + else + avail = math_min(self.BURST, avail + newavail) + self.bChoking = false + end + + avail = math_max(avail, 0-(MAX_CPS*2)) -- Can go negative when someone is eating bandwidth past the lib. but we refuse to stay silent for more than 2 seconds; if they can do it, we can. + + self.avail = avail + self.LastAvailUpdate = now + + return avail +end + + +----------------------------------------------------------------------- +-- Despooling logic + +function ChatThrottleLib:Despool(Prio) + local ring = Prio.Ring + while ring.pos and Prio.avail > ring.pos[1].nSize do + local msg = table_remove(Prio.Ring.pos, 1) + if not Prio.Ring.pos[1] then + local pipe = Prio.Ring.pos + Prio.Ring:Remove(pipe) + Prio.ByName[pipe.name] = nil + DelPipe(pipe) + else + Prio.Ring.pos = Prio.Ring.pos.next + end + Prio.avail = Prio.avail - msg.nSize + msg.f(unpack(msg, 1, msg.n)) + Prio.nTotalSent = Prio.nTotalSent + msg.nSize + DelMsg(msg) + end +end + + +function ChatThrottleLib.OnEvent(this,event) + -- v11: We know that the rate limiter is touchy after login. Assume that it's touch after zoning, too. + local self = ChatThrottleLib + if event == "PLAYER_ENTERING_WORLD" then + self.HardThrottlingBeginTime = GetTime() -- Throttle hard for a few seconds after zoning + self.avail = 0 + end +end + + +function ChatThrottleLib.OnUpdate(this,delay) + local self = ChatThrottleLib + + self.OnUpdateDelay = self.OnUpdateDelay + delay + if self.OnUpdateDelay < 0.08 then + return + end + self.OnUpdateDelay = 0 + + self:UpdateAvail() + + if self.avail < 0 then + return -- argh. some bastard is spewing stuff past the lib. just bail early to save cpu. + end + + -- See how many of our priorities have queued messages + local n = 0 + for prioname,Prio in pairs(self.Prio) do + if Prio.Ring.pos or Prio.avail < 0 then + n = n + 1 + end + end + + -- Anything queued still? + if n<1 then + -- Nope. Move spillover bandwidth to global availability gauge and clear self.bQueueing + for prioname, Prio in pairs(self.Prio) do + self.avail = self.avail + Prio.avail + Prio.avail = 0 + end + self.bQueueing = false + self.Frame:Hide() + return + end + + -- There's stuff queued. Hand out available bandwidth to priorities as needed and despool their queues + local avail = self.avail/n + self.avail = 0 + + for prioname, Prio in pairs(self.Prio) do + if Prio.Ring.pos or Prio.avail < 0 then + Prio.avail = Prio.avail + avail + if Prio.Ring.pos and Prio.avail > Prio.Ring.pos[1].nSize then + self:Despool(Prio) + end + end + end + +end + + + + +----------------------------------------------------------------------- +-- Spooling logic + + +function ChatThrottleLib:Enqueue(prioname, pipename, msg) + local Prio = self.Prio[prioname] + local pipe = Prio.ByName[pipename] + if not pipe then + self.Frame:Show() + pipe = NewPipe() + pipe.name = pipename + Prio.ByName[pipename] = pipe + Prio.Ring:Add(pipe) + end + + pipe[#pipe + 1] = msg + + self.bQueueing = true +end + + + +function ChatThrottleLib:SendChatMessage(prio, prefix, text, chattype, language, destination, queueName) + if not self or not prio or not prefix or not text or not self.Prio[prio] then + error('Usage: ChatThrottleLib:SendChatMessage("{BULK||NORMAL||ALERT}", "prefix", "text"[, "chattype"[, "language"[, "destination"]]]', 2) + end + + local nSize = text:len() + + assert(nSize<=255, "text length cannot exceed 255 bytes"); + + nSize = nSize + self.MSG_OVERHEAD + + -- Check if there's room in the global available bandwidth gauge to send directly + if not self.bQueueing and nSize < self:UpdateAvail() then + self.avail = self.avail - nSize + self.ORIG_SendChatMessage(text, chattype, language, destination) + self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize + return + end + + -- Message needs to be queued + local msg = NewMsg() + msg.f = self.ORIG_SendChatMessage + msg[1] = text + msg[2] = chattype or "SAY" + msg[3] = language + msg[4] = destination + msg.n = 4 + msg.nSize = nSize + + self:Enqueue(prio, queueName or (prefix..(chattype or "SAY")..(destination or "")), msg) +end + + +function ChatThrottleLib:SendAddonMessage(prio, prefix, text, chattype, target, queueName) + if not self or not prio or not prefix or not text or not chattype or not self.Prio[prio] then + error('Usage: ChatThrottleLib:SendAddonMessage("{BULK||NORMAL||ALERT}", "prefix", "text", "chattype"[, "target"])', 0) + end + + local nSize = prefix:len() + 1 + text:len(); + + assert(nSize<=255, "prefix + text length cannot exceed 254 bytes"); + + nSize = nSize + self.MSG_OVERHEAD; + + -- Check if there's room in the global available bandwidth gauge to send directly + if not self.bQueueing and nSize < self:UpdateAvail() then + self.avail = self.avail - nSize + self.ORIG_SendAddonMessage(prefix, text, chattype, target) + self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize + return + end + + -- Message needs to be queued + local msg = NewMsg() + msg.f = self.ORIG_SendAddonMessage + msg[1] = prefix + msg[2] = text + msg[3] = chattype + msg[4] = target + msg.n = (target~=nil) and 4 or 3; + msg.nSize = nSize + + self:Enqueue(prio, queueName or (prefix..chattype..(target or "")), msg) +end + + + + +----------------------------------------------------------------------- +-- Get the ball rolling! + +ChatThrottleLib:Init() + +--[[ WoWBench debugging snippet +if(WOWB_VER) then + local function SayTimer() + print("SAY: "..GetTime().." "..arg1) + end + ChatThrottleLib.Frame:SetScript("OnEvent", SayTimer) + ChatThrottleLib.Frame:RegisterEvent("CHAT_MSG_SAY") +end +]] + + diff --git a/Auc-Advanced/Modules/Auc-Util-AskPrice/ChatThrottleLib/ChatThrottleLib.toc b/Auc-Advanced/Modules/Auc-Util-AskPrice/ChatThrottleLib/ChatThrottleLib.toc new file mode 100644 index 0000000..6d21d40 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-AskPrice/ChatThrottleLib/ChatThrottleLib.toc @@ -0,0 +1,15 @@ +## Interface: 30000 +## Title: Lib: ChatThrottleLib +## Notes: Embeddable chat throttling library with traffic shaping and priorities +## Author: Mikk +## Version: 20 +## eMail: dpsgnome@mail.com +### DISABLED FOR 3.0.2 LoadOnDemand: 1 +## X-Category: Library +## X-Website: http://www.wowwiki.com/ChatThrottleLib +## X-Build: $Revision: 4154 $ +## X-ReleaseDate: "$Date: 2009-04-14 15:33:21 -0400 (Tue, 14 Apr 2009) $" +## SavedVariables: +## SavedVariablesPerCharacter: + +ChatThrottleLib.xml diff --git a/Auc-Advanced/Modules/Auc-Util-AskPrice/ChatThrottleLib/ChatThrottleLib.xml b/Auc-Advanced/Modules/Auc-Util-AskPrice/ChatThrottleLib/ChatThrottleLib.xml new file mode 100644 index 0000000..ee3bc1a --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-AskPrice/ChatThrottleLib/ChatThrottleLib.xml @@ -0,0 +1,4 @@ + + + diff --git a/Auc-Advanced/Modules/Auc-Util-AutoMagic/Auc-Util-AutoMagic.lua b/Auc-Advanced/Modules/Auc-Util-AutoMagic/Auc-Util-AutoMagic.lua new file mode 100644 index 0000000..9d71834 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-AutoMagic/Auc-Util-AutoMagic.lua @@ -0,0 +1,749 @@ +--[[ + Auctioneer - AutoMagic Utility module + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: Auc-Util-AutoMagic.lua 4901 2010-10-05 16:44:24Z Nechckn $ + URL: http://auctioneeraddon.com/ + + AutoMagic is an Auctioneer module which automates mundane tasks for you. + + 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 + +--Set up our module with AADV +local libName, libType = "AutoMagic", "Util" +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end +local print,decode,_,_,replicate,empty,get,set,default,debugPrint,fill,_TRANS = AucAdvanced.GetModuleLocals() + +--Start Module Code +local amBTMRule, itemName, itemID, _ +function lib.GetName() + return libName +end +local autosellframe = CreateFrame("Frame", "autosellframe", UIParent); autosellframe:Hide() +local autoselldata = {} +local autosell = {} +local GetPrice = function() return 0,0 end --fake getPrice when Appraiser is not available +if AucAdvanced.Modules.Util.Appraiser then + GetPrice = AucAdvanced.Modules.Util.Appraiser.GetPrice +end +lib.autoSellList = {} -- default empty table in case of no saved data + +function lib.Processor(callbackType, ...) + if (callbackType == "tooltip") then lib.ProcessTooltip(...) --Called when the tooltip is being drawn. + elseif (callbackType == "config") then lib.SetupConfigGui(...) --Called when you should build your Configator tab. + elseif (callbackType == "listupdate") then --Called when the AH Browse screen receives an update. + elseif (callbackType == "configchanged") then --Called when your config options (if Configator) have been changed. + if (get("util.automagic.autosellgui")) then + lib.autoSellGUI() + set("util.automagic.autosellgui", false) -- Resetting our toggle switch + end + + end +end + +lib.Processors = {} +function lib.Processors.tooltip(callbackType, ...) + lib.ProcessTooltip(...) --Called when the tooltip is being drawn. +end + +function lib.Processors.config(callbackType, ...) + lib.SetupConfigGui(...) --Called when you should build your Configator tab. +end + +function lib.Processors.configchanged(callbackType, ...) + if (get("util.automagic.autosellgui")) then + lib.autoSellGUI() + set("util.automagic.autosellgui", false) -- Resetting our toggle switch + end +end + +function lib.ProcessTooltip(tooltip, name, hyperlink, quality, quantity, cost, additional) + if not (get("util.automagic.depositTT")) then + if hyperlink then + local ttdepcost = GetDepositCost(hyperlink, get("util.automagic.deplength"), nil, quantity) + + if (ttdepcost == nil) then + tooltip:AddLine("|cff336699 Unknown deposit cost |r") + elseif (ttdepcost == 0) then + tooltip:AddLine("|cff336699 No deposit cost |r") + else + tooltip:AddLine("|cffCCFF99"..get("util.automagic.deplength").."hr Deposit : |r" , ttdepcost) + end + end + end +end +local ahdeplength = { + {12, "12 hour"}, + {24, "24 hour"}, + {48, "48 hour"}, +} +function lib.OnLoad() + lib.slidebar() + + -- Read saved variables + lib.autoSellList = get("util.automagic.autoSellList") or lib.autoSellList -- will default to empty table if no saved variables + for id, name in pairs(lib.autoSellList) do + lib.ClientItemCacheRefresh("item:"..id) + end + + -- Sets defaults + print("AucAdvanced: {{"..libType..":"..libName.."}} loaded!") + + default("util.automagic.autovendor", false) -- DO NOT SET TRUE ALL AUTOMAGIC OPTIONS SHOULD BE TURNED ON MANUALLY BY END USER!!!!!!! + default("util.automagic.autostopafter12", true) --stops autovendor after 12 items are sold. Want it to be on + default("util.automagic.autosellgrey", false) + default("util.automagic.autocloseenable", false) -- Enables auto close of vendor window after autosale completion + default("util.automagic.showmailgui", false) + default("util.automagic.autosellgui", false) -- Acts as a button and reverts to false anyway + default("util.automagic.chatspam", true) --Supposed to default on has to be unchecked if you don't want the chat text. + default("util.automagic.depositTT", false) --Used for disabling the deposit costs TT + default("util.automagic.ammailguix", 100) --Used for storing mailgui location + default("util.automagic.ammailguiy", 100) --Used for storing mailgui location + default("util.automagic.uierrormsg", 0) --Keeps track of ui error msg's + default("util.automagic.deplength", 48) + default("util.automagic.overidebtmmail", false) -- Item AI for mail rule instead of BTM rule. + + + default("util.automagic.displaybeginerTooltips", true) +end + + -- define what event fires what function +function lib.onEventDo(this, event) + if event == 'MERCHANT_SHOW' then lib.merchantShow() end + if event == 'MERCHANT_CLOSED' then lib.merchantClosed() end + if event == 'MAIL_SHOW' then lib.mailShow() end + if event == 'MAIL_CLOSED' then lib.mailClosed() end + if event == 'UI_ERROR_MESSAGE' then set("util.automagic.uierrormsg", 1) end + if event == 'BAG_UPDATE' then if lib.confirmsellui:IsVisible() then lib.vendorAction() end end --bags changed make sure vendor items are in order +end + +--This will be used to sort our list's rather than the default scrollsheet method. +function lib.CustomSort(data, sort, width, column, dir) + assert(column <= width) + assert(dir == -1 or dir == 1) + table.sort(sort, function(a,b) + local aPos = (a-1)*width+column + local bPos = (b-1)*width+column + local dataA, dataB = data[aPos], data[bPos] + local colorA, nameA = string.match(dataA, "^|cff(%x+)|Hitem.+|h%[(.*)%]|h|r") + local colorB, nameB = string.match(dataB, "^|cff(%x+)|Hitem.+|h%[(.*)%]|h|r") + if colorA and nameA and colorB and nameB then --hyperlink check + dataA = colorA..nameA + dataB = colorB..nameB + end + if dir < 0 then + return (dataA > dataB) or (dataA == dataB and a > b) + end + return (dataA < dataB) or (dataA == dataB and a < b) + end) +end + +function lib.SetupConfigGui(gui) + local id = gui:AddTab(libName) + gui:MakeScrollable(id) + --stores our ID id we use this to open the config button to correct frame + private.gui = gui + private.guiID = id + + + gui:AddHelp(id, "what is AutoMagic?", + _TRANS('AAMU_Help_WhatAutoMagic'), --"What is AutoMagic?" + _TRANS('AAMU_Help_WhatAutoMagicAnswer')) --"AutoMagic is a work-in-progress. Its goal is to automate tasks that auctioneers run into that can be a pain to do, as long as it is within the bounds set by Blizzard. \n\nAutoMagic currently will auto-sell any item bought via SearchUI for vendors, any item that is grey (if enabled) or any item on the auto-sell list. If enabled, when you open a merchant window you will see a listing of the items to sell." + gui:AddHelp(id, "AAMU: vendor options", + _TRANS('AAMU_Help_VendorOptions'), --"AAMU: Vendor Options" + _TRANS('AAMU_Help_VendorOptionsAnswer')) --"AutoMagic will sell items bought for vendoring to the vendor automatically. It also has the option of auto-selling all grey items or items on the custom sell list." + gui:AddHelp(id, "what is Mail GUI?", + _TRANS('AAMU_Help_WhatMailGUI'), --"What is the Mail GUI?" + _TRANS('AAMU_Help_WhatMailGUIAnswer')) --"This displays a window when the mailbox is opened that allows for the auto-loading of items into the send mail window based on purchase reasons from SearchUI. It can also use the ItemSuggest module reasons instead of the provided SearchUI reasons. Very handy for mass mailing items bought for a profession that another character has." + + + gui:AddControl(id, "Header", 0, libName.._TRANS('AAMU_Interface_GeneralOptions')) --" General Options" + gui:AddControl(id, "Checkbox", 0, 1, "util.automagic.displaybeginerTooltips", _TRANS('AAMU_Interface_BeginnerTooltip')) --"Enable AutoMagic beginner tooltips" + gui:AddTip(id, _TRANS('AAMU_HelpTooltip_BeginnerTooltip')) --'Display the beginner tooltips on mouseover.' + + gui:AddControl(id, "Checkbox", 0, 1, "util.automagic.chatspam", _TRANS('AAMU_Interface_Chatspam')) --"Enable AutoMagic chat spam" + gui:AddTip(id, _TRANS('AAMU_HelpTooltip_Chatspam')) --'Display chat messages from AutoMagic.' + + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + gui:AddControl(id, "Checkbox", 0, 1, "util.automagic.depositTT", _TRANS('AAMU_Interface_DepositTooltip')) --"Disable deposit costs in the tooltip" + gui:AddTip(id, _TRANS('AAMU_HelpTooltip_DepositTooltip')) --'Remove item deposit costs from the tooltip.' + + gui:AddControl(id, "Selectbox", 0, 1, ahdeplength, "util.automagic.deplength", _TRANS('AAMU_Interface_DepositLength')) --"Base deposits on what length of auction." + gui:AddTip(id, _TRANS('AAMU_HelpTooltip_DepositLength')) --'Select the auction length deposit cost you want to display in the tooltip.' + + gui:AddControl(id, "Header", 0, _TRANS('AAMU_Interface_VendorOptions')) --" Vendor Options" + gui:AddControl(id, "Checkbox", 0, 1, "util.automagic.autovendor", _TRANS('AAMU_Interface_Vendoring')) --"Enable AutoMagic vendoring (W A R N I N G: READ HELP!) " + gui:AddTip(id, _TRANS('AAMU_HelpTooltip_Vendoring')) --'Enable the auto-vendor options.' + + gui:AddControl(id, "Checkbox", 0, 4, "util.automagic.autostopafter12", _TRANS('AAMU_Interface_AutoStop12')) --"Pause after selling 12 items." + gui:AddTip(id, _TRANS('AAMU_HelpTooltip_AutoStop12')) --'This allows you to buy back an accidental sale, since the server saves the last 12 sales to the vendor' + + gui:AddControl(id, "Subhead", 0, "Which categories will be vendored?") + + gui:AddControl(id, "Checkbox", 0, 4, "util.automagic.autosellgrey", _TRANS('AAMU_Interface_AutoSellGrey')) --"Auto-sell grey items quality items" + gui:AddTip(id, _TRANS('AAMU_HelpTooltip_AutoSellGrey')) --'Auto-sell grey level items at the vendor.' + gui:AddControl(id, "Checkbox", 0, 6, "util.automagic.autosellgreynoprompt", _TRANS('AAMU_Interface_AutoNoPrompt')) --"...without confirmation prompt" + gui:AddTip(id, _TRANS('AAMU_HelpTooltip_AutoSellGreyNoPrompt')) --'No confirmation window will be shown for vendoring grey (trash) items.' + + gui:AddControl(id, "Checkbox", 0, 4, "util.automagic.autosellreason", _TRANS('AAMU_Interface_AutoSellReason')) --"Auto-sell items purchased using the vendor searcher" + gui:AddTip(id, _TRANS('AAMU_HelpTooltip_AutoSellReason')) --'Auto-sell items purchased using the vendor searcher' + gui:AddControl(id, "Checkbox", 0, 6, "util.automagic.autosellreasonnoprompt", _TRANS('AAMU_Interface_AutoNoPrompt')) --"...without confirmation prompt" + gui:AddTip(id, _TRANS('AAMU_HelpTooltip_AutoSellReasonNoPrompt')) --'No confirmation window will be shown for items with a purchased for vendor reason tag' + + gui:AddControl(id, "Checkbox", 0, 4, "util.automagic.vendorunusablebop", _TRANS('AAMU_Interface_AutoSellBOP')) --"Auto-sell unusable soulbound gear" + gui:AddTip(id, _TRANS('AAMU_HelpTooltip_AutoSellBOP')) --'Auto-sell unusable soulbound gear' + gui:AddControl(id, "Checkbox", 0, 6, "util.automagic.autosellbopnoprompt", _TRANS('AAMU_Interface_AutoNoPrompt')) --"...without confirmation prompt" + gui:AddTip(id, _TRANS('AAMU_HelpTooltip_AutoSellBOPNoPrompt')) --'No confirmation window will be shown for selling soulbound items the players class cannot equip.' + + gui:AddControl(id, "Checkbox", 0, 4, "util.automagic.autoselllist", _TRANS('AAMU_Interface_AutoSellListItems')) --"Auto-sell items on the always vendor list" + gui:AddTip(id, _TRANS('AAMU_HelpTooltip_AutoSellListItems')) --'Auto-sell items on the always vendor list.' + gui:AddControl(id, "Checkbox", 0, 6, "util.automagic.autoselllistnoprompt", _TRANS('AAMU_Interface_AutoNoPrompt')) --"...without confirmation prompt" + gui:AddTip(id, _TRANS('AAMU_HelpTooltip_AutoSellListNoPrompt')) --'No confirmation window will be shown for items on the always vendor list' + + --gui:AddControl(id, "Checkbox", 0, 1, "util.automagic.autoclosemerchant", "Auto Merchant Window Close(Power user feature READ HELP)") + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + gui:AddControl(id, "Button", 0, 1, "util.automagic.autosellgui", _TRANS('AAMU_Interface_AutoSellList')) --"Auto-Sell List" + gui:AddTip(id, _TRANS('AAMU_HelpTooltip_AutoSellList')) --'Check the box to view the Auto-Sell configuration GUI.' + + + gui:AddControl(id, "Header", 0, _TRANS('AAMU_Interface_GUIOptions')) --" GUI options" + gui:AddControl(id, "Checkbox", 0, 1, "util.automagic.showmailgui", _TRANS('AAMU_Interface_MailGUI')) --"Enable Mail GUI for additional mail features" + gui:AddTip(id, _TRANS('AAMU_HelpTooltip_MailGUI')) --'Display the auto-mail window at the mail box.') + + gui:AddControl(id, "Checkbox", 0, 1, "util.automagic.overidebtmmail", _TRANS('AAMU_Interface_OverrideSUIMail')) --"Use ItemSuggest values instead of SearchUI's reasons for Mail Loader" + gui:AddTip(id, _TRANS('AAMU_HelpTooltip_OverrideSUIMail')) --"Use the ItemSuggest reasons instead of the SearchUI 'Purchased for' reasons when sorting mail." +end + +--Beginner Tooltips script display for all UI elements +function lib.buttonTooltips(self, text) + if get("util.automagic.displaybeginerTooltips") and text and self then + GameTooltip:SetOwner(self, "ANCHOR_BOTTOMRIGHT") + GameTooltip:SetText(text) + end +end + +function lib.merchantShow() + private.eventframe:RegisterEvent("BAG_UPDATE") + if (get("util.automagic.autovendor")) then + --first lib.vendorAction call will sell all grays, bypassing promopt. Run lib.vendorAction to add anything remaining to the prompt window + if (get("util.automagic.autosellgreynoprompt")) then + lib.vendorAction(true) + end + lib.vendorAction() + +--~ A better option is to auto close vendor when user hits confirm button window +--~ if (get("util.automagic.autoclosemerchant")) then +--~ if (get("util.automagic.chatspam")) then +--~ print("AutoMagic has closed the merchant window for you, to disable you must change this options in the settings.") +--~ end +--~ CloseMerchant() +--~ end + end +end + + +function lib.merchantClosed() + private.eventframe:UnregisterEvent("BAG_UPDATE") + if lib.confirmsellui:IsVisible() then lib.confirmsellui:Hide() end +end + +function lib.mailShow() + if (get("util.automagic.showmailgui")) then + lib.mailGUI() + end +end + +function lib.mailClosed() --Fires on mail box closed event & hides mailgui + local x,y = lib.ammailgui:GetCenter() + set("util.automagic.ammailguix" ,x) + set("util.automagic.ammailguiy" ,y) + lib.ammailgui:Hide() +end + +function lib.mailGUI() --Function is called from lib.mailShow() + lib.makeMailGUI() + lib.ammailgui:Show() +end + +function lib.autoSellGUI() + if (autosellframe:IsVisible()) then autosellframe:Hide() return end + autosellframe:Show() + lib.populateDataSheet() +end + +function lib.closeAutoSellGUI() + autosellframe:Hide() +end + +--Slidebar +function lib.autosellslidebar(_, button) + if (button == "LeftButton") then + lib.autoSellGUI() + else + --if we rightclick open the configuration window for the whole addon + if private.gui and private.gui:IsShown() then + AucAdvanced.Settings.Hide() + else + AucAdvanced.Settings.Show() + private.gui:ActivateTab(private.guiID) + end + end +end + +local sideIcon +function lib.slidebar() + if LibStub then + --Need to figure out if we're embedded first + local embedded = false + for _, module in ipairs(AucAdvanced.EmbeddedModules) do + if module == "Auc-Util-AutoMagic" then + embedded = true + end + end + local sideIcon, sideIconE + if embedded then + sideIcon = "Interface\\AddOns\\Auc-Advanced\\Modules\\Auc-Util-AutoMagic\\Images\\amagicIcon" + sideIconE = "Interface\\AddOns\\Auc-Advanced\\Modules\\Auc-Util-AutoMagic\\Images\\amagicIconE" + else + sideIcon = "Interface\\AddOns\\Auc-Util-AutoMagic\\Images\\amagicIcon" + sideIconE = "Interface\\AddOns\\Auc-Util-AutoMagic\\Images\\amagicIconE" + end + + local LibDataBroker = LibStub:GetLibrary("LibDataBroker-1.1", true) + if LibDataBroker then + private.LDBButton = LibDataBroker:NewDataObject("Auc-Util-AutoMagic", { + type = "launcher", + icon = sideIcon, + OnClick = function(self, button) lib.autosellslidebar(self, button) end, + }) + + function private.LDBButton:OnTooltipShow() + self:AddLine("AutoMagic: Auto-Sell Config", 1,1,0.5, 1) + self:AddLine("|cff1fb3ff".."Left-Click|r to open the 'Auto-Sell' list.", 1,1,0.5, 1) + self:AddLine("|cff1fb3ff".."Right-Click|r to edit the configuration.", 1,1,0.5, 1) + end + --we use a slight hack to LDB to animate our icon on Enter as well as tooltip display. The Tooltip will be hidden by slidebar but will show for other addons + function private.LDBButton:OnEnter() + if self.icon and type(self.icon) == "table" then + self.icon:SetTexture(sideIconE) + end + + GameTooltip:SetOwner(self, "ANCHOR_NONE") + GameTooltip:SetPoint("TOPLEFT", self, "BOTTOMLEFT") + GameTooltip:ClearLines() + private.LDBButton.OnTooltipShow(GameTooltip) + GameTooltip:Show() + end + + function private.LDBButton:OnLeave() + if self.icon and type(self.icon) == "table" then + self.icon:SetTexture(sideIcon) + end + GameTooltip:Hide() + end + end + end +end + +local myworkingtable = {} +function lib.setWorkingItem(link) + if link == nil then return end + local name, _, _, _, _, _, _, _, _, texture = GetItemInfo(link) + local _, id, _, _, _, _ = decode(link) + autosellframe.workingname:SetText(name) + autosellframe.slot:SetTexture(texture) + myworkingtable = {} + for k, n in pairs(myworkingtable) do + myworkingtable[k] = nil + end + myworkingtable[id] = name +end + +function autosellframe.removeitemfromlist() + for k, n in pairs(myworkingtable) do + lib.autoSellList[k] = nil + myworkingtable[k] = nil + end + set("util.automagic.autoSellList", lib.autoSellList)--Store the changed sell list across sessions + myworkingtable = {} + lib.populateDataSheet() + autosellframe.ClearIcon() +end + +function autosellframe.additemtolist() + for k, n in pairs(myworkingtable) do + lib.autoSellList[k] = n + myworkingtable[k] = nil + end + set("util.automagic.autoSellList", lib.autoSellList)--Store the changed sell list across sessions + myworkingtable = {} + lib.populateDataSheet() + autosellframe.ClearIcon() +end + +function autosellframe.ClearIcon() + autosellframe.workingname:SetText("Item Name") + autosellframe.slot:SetTexture("Interface\\Buttons\\UI-EmptySlot") +end + + +function autosellframe.IconClicked() + autosellframe.ClearIcon() +end + + +function lib.autoSellIconDrag() + local objtype, _, link = GetCursorInfo() + ClearCursor() + if objtype == "item" then + lib.setWorkingItem(link) + end +end + + +function lib.ClickLinkHook(_, link, button) + if link and autosellframe:IsShown() and link:find("Hitem:") then + if (button == "LeftButton") then + lib.setWorkingItem(link) + end + end +end +hooksecurefunc("ChatFrame_OnHyperlinkShow", lib.ClickLinkHook) + + +local autoselldata = {}; local bagcontents = {}; local bagcontentsnodups = {} +function lib.populateDataSheet() + for k, v in pairs(autoselldata) do autoselldata[k] = nil; end --Reset table to ensure fresh data. + + for id, name in pairs(lib.autoSellList) do + if (id == nil) then return end + local _, itemLink, _, _, _, _, _, _, _, _ = GetItemInfo(id) + local abid, abuy, vendor + if itemLink then + abid,abuy = GetPrice(itemLink, nil, true) + vendor = GetSellValue and GetSellValue(id) or 0 + else + itemLink = "|cffff0000"..name.."|r" -- item name in red + lib.ClientItemCacheRefresh("item:"..id) + abid, abuy, vendor = 0, 0, 0 + end + table.insert(autoselldata,{ + itemLink, --link form for mouseover tooltips to work + vendor, + tonumber(abuy) or tonumber(abid), + }) + end + autosellframe.resultlist.sheet:SetData(autoselldata, style) --Set the GUI scrollsheet + + for k, v in pairs(bagcontents) do bagcontents[k] = nil; end --Reset table to ensure fresh data. + for bag=0,4 do + for slot=1,GetContainerNumSlots(bag) do + if (GetContainerItemLink(bag,slot)) then + local itemLink = GetContainerItemLink(bag,slot) + if (itemLink == nil) then return end + local _, itemID, _, _, _, _ = decode(itemLink) + local btmRule = "~" + if BtmScan then + local _,itemCount = GetContainerItemInfo(bag,slot) + local reason, bids + local id, suffix, enchant, seed = BtmScan.BreakLink(itemLink) + local sig = ("%d:%d:%d"):format(id, suffix, enchant) + local bidlist = BtmScan.Settings.GetSetting("bid.list") + + if (bidlist) then + bids = bidlist[sig..":"..seed.."x"..itemCount] + if(bids and bids[1]) then + btmRule = bids[1] + end + end + end + bagcontents[itemID] = btmRule + end + end + end + for k, v in pairs(bagcontentsnodups) do bagcontentsnodups[k] = nil; end --Reset 'data' table to ensure fresh data. + for id, btmRule in pairs(bagcontents) do + if (id == nil) then return end + local _, itemLink, _, _, _, _, _, _, _, _ = GetItemInfo(id) + local abid,abuy = GetPrice(itemLink, nil, true) + table.insert(bagcontentsnodups,{ + itemLink, -- link form for mouseover tooltips to work + btmRule, --btm rule + tonumber(abuy) or tonumber(abid), + }) + end + autosellframe.baglist.sheet:SetData(bagcontentsnodups, style) --Set the GUI scrollsheet +end + +function autosell.OnBagListEnter(button, row, index) + if autosellframe.baglist.sheet.rows[row][index]:IsShown()then --Hide tooltip for hidden cells + local link + link = autosellframe.baglist.sheet.rows[row][index]:GetText() + if link:find("\124Hitem:%d") then + GameTooltip:SetOwner(button, "ANCHOR_RIGHT") + AucAdvanced.ShowItemLink(GameTooltip, link, 1) + end + end +end + +function autosell.OnEnter(button, row, index) + if autosellframe.resultlist.sheet.rows[row][index]:IsShown()then --Hide tooltip for hidden cells + local link + link = autosellframe.resultlist.sheet.rows[row][index]:GetText() + if link:find("\124Hitem:%d") then + GameTooltip:SetOwner(button, "ANCHOR_RIGHT") + AucAdvanced.ShowItemLink(GameTooltip, link, 1) + end + end +end + +function autosell.OnLeave(button, row, index) + GameTooltip:Hide() +end + +function autosell.OnClickAutoSellSheet(button, row, index) + for index = 1, 3 do + local link = autosellframe.resultlist.sheet.rows[row][index]:GetText() + if link:find("\124Hitem:%d") then + lib.setWorkingItem(link) + return + end + end + lib.populateDataSheet() +end + +function autosell.OnClickBagSheet(button, row, index) + for index = 1, 3 do + local link = autosellframe.baglist.sheet.rows[row][index]:GetText() + if link:find("\124Hitem:%d") then + lib.setWorkingItem(link) + return + end + end + lib.populateDataSheet() +end + +function lib.makeautosellgui() + autosellframe:SetFrameStrata("HIGH") + autosellframe:SetBackdrop({ + bgFile = "Interface/Tooltips/ChatBubble-Background", + edgeFile = "Interface/Tooltips/ChatBubble-BackDrop", + tile = true, tileSize = 32, edgeSize = 32, + insets = { left = 32, right = 32, top = 32, bottom = 32 } + }) + autosellframe:SetBackdropColor(0,0,0, 1) + autosellframe:Hide() + + autosellframe:SetPoint("CENTER", UIParent, "CENTER") + autosellframe:SetWidth(640) + autosellframe:SetHeight(450) + + autosellframe:SetMovable(true) + autosellframe:EnableMouse(true) + autosellframe.Drag = CreateFrame("Button", nil, autosellframe) + autosellframe.Drag:SetPoint("TOPLEFT", autosellframe, "TOPLEFT", 10,-5) + autosellframe.Drag:SetPoint("TOPRIGHT", autosellframe, "TOPRIGHT", -10,-5) + autosellframe.Drag:SetHeight(6) + autosellframe.Drag:SetHighlightTexture("Interface\\FriendsFrame\\UI-FriendsFrame-HighlightBar") + + autosellframe.Drag:SetScript("OnMouseDown", function() autosellframe:StartMoving() end) + autosellframe.Drag:SetScript("OnMouseUp", function() autosellframe:StopMovingOrSizing() end) + + autosellframe.DragBottom = CreateFrame("Button",nil, autosellframe) + autosellframe.DragBottom:SetPoint("BOTTOMLEFT", autosellframe, "BOTTOMLEFT", 10,5) + autosellframe.DragBottom:SetPoint("BOTTOMRIGHT", autosellframe, "BOTTOMRIGHT", -10,5) + autosellframe.DragBottom:SetHeight(6) + autosellframe.DragBottom:SetHighlightTexture("Interface\\FriendsFrame\\UI-FriendsFrame-HighlightBar") + + autosellframe.DragBottom:SetScript("OnMouseDown", function() autosellframe:StartMoving() end) + autosellframe.DragBottom:SetScript("OnMouseUp", function() autosellframe:StopMovingOrSizing() end) + + local autoselltitle = autosellframe:CreateFontString(asuftitle, "OVERLAY", "GameFontNormalLarge") + autoselltitle:SetText("AutoMagic: Auto Sell Config") + autoselltitle:SetJustifyH("CENTER") + autoselltitle:SetWidth(300) + autoselltitle:SetHeight(10) + autoselltitle:SetPoint("TOPLEFT", autosellframe, "TOPLEFT", 0, -17) + autosellframe.autoselltitle = aautoselltitle + + --Close Button + autosellframe.closeButton = CreateFrame("Button", nil, autosellframe, "OptionsButtonTemplate") + autosellframe.closeButton:SetPoint("BOTTOMRIGHT", autosellframe, "BOTTOMRIGHT", -530, 10) + autosellframe.closeButton:SetText(("Close")) + autosellframe.closeButton:SetScript("OnClick", lib.closeAutoSellGUI) + + local SelectBox = LibStub:GetLibrary("SelectBox") + local ScrollSheet = LibStub:GetLibrary("ScrollSheet") + + autosellframe.slot = autosellframe:CreateTexture(nil, "ARTWORK") + autosellframe.slot:SetPoint("TOPLEFT", autosellframe, "TOPLEFT", 23, -50) + autosellframe.slot:SetWidth(45) + autosellframe.slot:SetHeight(45) + autosellframe.slot:SetTexCoord(0.15, 0.85, 0.15, 0.85) + autosellframe.slot:SetTexture("Interface\\Buttons\\UI-EmptySlot") + + autosellframe.icon = CreateFrame("Button", nil, autosellframe) + autosellframe.icon:SetPoint("TOPLEFT", autosellframe.slot, "TOPLEFT", 3, -3) + autosellframe.icon:SetWidth(38) + autosellframe.icon:SetHeight(38) + autosellframe.icon:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square.blp") + autosellframe.icon:SetScript("OnClick", autosellframe.IconClicked) + autosellframe.icon:SetScript("OnReceiveDrag", lib.autoSellIconDrag) + + autosellframe.slot.help = autosellframe:CreateFontString(nil, "OVERLAY", "GameFontNormal") + autosellframe.slot.help:SetPoint("LEFT", autosellframe.slot, "RIGHT", 2, 7) + autosellframe.slot.help:SetText(("Drop item into box")) --"Drop item into box to search." + autosellframe.slot.help:SetWidth(100) + + autosellframe.workingname = autosellframe:CreateFontString(nil, "OVERLAY", "GameFontNormal") + autosellframe.workingname:SetPoint("TOPLEFT", autosellframe, "TOPLEFT", 15, -100) + autosellframe.workingname:SetText(("")) + autosellframe.workingname:SetWidth(90) + + --Add Item to list button + autosellframe.additem = CreateFrame("Button", nil, autosellframe, "OptionsButtonTemplate") + autosellframe.additem:SetPoint("TOPLEFT", autosellframe, "TOPLEFT", 10, -150) + autosellframe.additem:SetText(('Add Item')) + autosellframe.additem:SetScript("OnClick", autosellframe.additemtolist) + + autosellframe.additem.help = autosellframe:CreateFontString(nil, "OVERLAY", "GameFontNormal") + autosellframe.additem.help:SetPoint("TOPLEFT", autosellframe.additem, "TOPRIGHT", 1, 1) + autosellframe.additem.help:SetText(("(to Auto Sell list)")) + autosellframe.additem.help:SetWidth(90) + + --Remove Item from list button + autosellframe.removeitem = CreateFrame("Button", nil, autosellframe, "OptionsButtonTemplate") + autosellframe.removeitem:SetPoint("TOPLEFT", autosellframe.additem, "BOTTOMLEFT", 0, -20) + autosellframe.removeitem:SetText(('Remove Item')) + autosellframe.removeitem:SetScript("OnClick", autosellframe.removeitemfromlist) + + autosellframe.removeitem.help = autosellframe:CreateFontString(nil, "OVERLAY", "GameFontNormal") + autosellframe.removeitem.help:SetPoint("TOPLEFT", autosellframe.removeitem, "TOPRIGHT", 1, 1) + autosellframe.removeitem.help:SetText(("(from Auto Sell list)")) + autosellframe.removeitem.help:SetWidth(90) + + --Create the autosell list results frame + autosellframe.resultlist = CreateFrame("Frame", nil, autosellframe) + autosellframe.resultlist:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + edgeFile = "Interface/Tooltips/UI-Tooltip-Border", + tile = true, tileSize = 32, edgeSize = 16, + insets = { left = 5, right = 5, top = 5, bottom = 5 } + }) + + autosellframe.resultlist:SetBackdropColor(0, 0, 0.0, 0.5) + autosellframe.resultlist:SetPoint("TOPLEFT", autosellframe, "BOTTOMLEFT", 270, 250) + autosellframe.resultlist:SetPoint("TOPRIGHT", autosellframe, "TOPLEFT",630, 0) + autosellframe.resultlist:SetPoint("BOTTOM", autosellframe, "BOTTOM", 0, 10) + + autosellframe.resultlist.sheet = ScrollSheet:Create(autosellframe.resultlist, { + { ('Auto Selling:'), "TOOLTIP", 170 }, + { "Vendor", "COIN", 70 }, + { "Appraiser", "COIN", 70 }, + }, autosell.OnEnter, autosell.OnLeave, autosell.OnClickAutoSellSheet) + --use our custom sort method not scrollsheets + autosellframe.resultlist.sheet.CustomSort = lib.CustomSort + --Create the bag contents frame + autosellframe.baglist = CreateFrame("Frame", nil, autosellframe) + autosellframe.baglist:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + edgeFile = "Interface/Tooltips/UI-Tooltip-Border", + tile = true, tileSize = 32, edgeSize = 16, + insets = { left = 5, right = 5, top = 5, bottom = 5 } + }) + + autosellframe.baglist:SetBackdropColor(0, 0, 0.0, 0.5) + + autosellframe.baglist:SetPoint("TOPLEFT", autosellframe, "BOTTOMLEFT", 270, 445) + autosellframe.baglist:SetPoint("TOPRIGHT", autosellframe, "TOPLEFT", 630, 0) + autosellframe.baglist:SetPoint("BOTTOM", autosellframe, "BOTTOM", 0, 250) + + autosellframe.bagList = CreateFrame("Button", nil, autosellframe, "OptionsButtonTemplate") + autosellframe.bagList:SetPoint("TOPRIGHT", autosellframe.baglist, "BOTTOMRIGHT", -530, -50) + autosellframe.bagList:SetText(("Re-Scan Bags")) + autosellframe.bagList:SetScript("OnClick", lib.populateDataSheet) + + autosellframe.baglist.sheet = ScrollSheet:Create(autosellframe.baglist, { + { ('Bag Contents:'), "TOOLTIP", 170 }, + { ('BTM Rule'), "TEXT", 70 }, + { "Appraiser", "COIN", 70 }, + }, autosell.OnBagListEnter, autosell.OnLeave, autosell.OnClickBagSheet) + --use our custom sort method not scrollsheets + autosellframe.baglist.sheet.CustomSort = lib.CustomSort +end +lib.makeautosellgui() + +-- Client item cache refresh system +-- (Loosely based on similar code in Gatherer) + +local tooltip = CreateFrame("GameTooltip") +local eventframe = CreateFrame("Frame") -- used for Events and for timer (via Update) +private.eventframe = eventframe +local timercounter = 0 +local refreshlist + +eventframe:SetScript("OnEvent", lib.onEventDo) +eventframe:RegisterEvent("MERCHANT_SHOW") +eventframe:RegisterEvent("MERCHANT_CLOSED") +eventframe:RegisterEvent("MAIL_SHOW") +eventframe:RegisterEvent("MAIL_CLOSED") +eventframe:RegisterEvent("UI_ERROR_MESSAGE") + +local function timerOnUpdate(self, elapsed) + timercounter = timercounter - elapsed + if timercounter <= 0 then + if not refreshlist then -- this is a double-check - should not occur + eventframe:SetScript("OnUpdate", nil) + return + end + local link + repeat -- iterate refreshlist until we find an uncached item + link = next(refreshlist) + if not link then -- no more items in list - stop the timer + refreshlist = nil + eventframe:SetScript("OnUpdate", nil) + return + end + refreshlist[link] = nil + until not GetItemInfo(link) + tooltip:SetHyperlink(link) -- causes client to download item info from server into cache. todo: consider wrapping in pcall? + timercounter = 5 -- 5 seconds throttle between each server request + end +end + +-- lib.ClientItemCacheRefresh +-- link : must be an item link which would work in both GetItemInfo and GameTooltip:SetHyperlink +-- note: the short form "item:" is permissible +function lib.ClientItemCacheRefresh(link) + if not refreshlist then + refreshlist = {} + timercounter = 0 -- refresh on next update + eventframe:SetScript("OnUpdate", timerOnUpdate) + end + refreshlist[link] = true +end + + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-AutoMagic/Auc-Util-AutoMagic.lua $", "$Rev: 4901 $") diff --git a/Auc-Advanced/Modules/Auc-Util-AutoMagic/Auc-Util-AutoMagic.toc b/Auc-Advanced/Modules/Auc-Util-AutoMagic/Auc-Util-AutoMagic.toc new file mode 100644 index 0000000..92f71a6 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-AutoMagic/Auc-Util-AutoMagic.toc @@ -0,0 +1,11 @@ +## Title: Auc:Util:AutoMagic +## Interface: 40000 +## Dependancies: Auc-Advanced +## OptionalDependencies: Auc-Util-Appraiser, Enchantrix, Auc-Util-ItemSuggest, LibDataBroker +## +## Version: 5.9.4961 (WhackyWallaby) +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## X-Max-Interface: 40000 +Embed.xml diff --git a/Auc-Advanced/Modules/Auc-Util-AutoMagic/ConfirmSellUI.lua b/Auc-Advanced/Modules/Auc-Util-AutoMagic/ConfirmSellUI.lua new file mode 100644 index 0000000..af6e957 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-AutoMagic/ConfirmSellUI.lua @@ -0,0 +1,317 @@ +--[[ + Auctioneer - AutoMagic Utility module + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: ConfirmSellUI.lua 4901 2010-10-05 16:44:24Z Nechckn $ + URL: http://auctioneeraddon.com/ + + AutoMagic is an Auctioneer module which automates mundane tasks for you. + + 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 lib = AucAdvanced.Modules.Util.AutoMagic +local print,decode,_,_,replicate,empty,get,set,default,debugPrint,fill = AucAdvanced.GetModuleLocals() +local GetPrice = function() return 0,0 end --fake getPrice when Appraiser is not available +if AucAdvanced.Modules.Util.Appraiser then + GetPrice = AucAdvanced.Modules.Util.Appraiser.GetPrice +end + +local _, selected, selecteditem, selectedvendor, selectedappraiser, selectedwhy, selectedignored +local selecteddata = {} + + +function lib.ASCPrompt() + if next(lib.vendorlist) then + lib.confirmsellui:Show() + end + lib.ASCRefreshSheet() --Always refresh the sheet or it can appear we have items left in GUI after the autosell function +end + +--------------------------------------------------------- +-- Button Functions +--------------------------------------------------------- +-- lib.vendorlist[key] = { link, sig, count, bag, slot, reason } +function lib.ASCConfirmContinue() + lib.confirmsellui:Hide() --hide gui before selling so our bag update events are not registered + local count = 0 + for key, itemdata in pairs(lib.vendorlist) do + count = count +1 + --if stop after 12 then recheck whats left to vendor and re-prompt for a new round. + if get("util.automagic.autostopafter12") and count > 12 then --stop after 12 sells so use can buy back a accidental sale + lib.vendorAction() + return + end + + local _, iID = decode( itemdata[1] ) --check if item is to be ignored + if not get("util.automagic.vidignored"..iID) then --will be nil if not on ignore list + + if (get("util.automagic.chatspam")) then + print("AutoMagic is selling:", itemdata[1], "reason:", itemdata[6]) + end + + UseContainerItem(itemdata[4], itemdata[5]) + end + lib.vendorlist[key] = nil + end +end + +function lib.ASCRemoveItem() + if selecteditem then + for key, itemdata in pairs(lib.vendorlist) do + if selecteditem == itemdata[1] then + lib.vendorlist[key] = nil + break + end + end + end + lib.ASCRefreshSheet() +end + +function lib.ASCIgnoreItem() + if selecteditem then + local _, iID = decode(selecteditem) + set("util.automagic.vidignored"..iID, true) + end + + lib.ASCRefreshSheet() +end + +function lib.ASCUnIgnoreItem() + if selecteditem then + local _, iID = decode(selecteditem) + set("util.automagic.vidignored"..iID, nil) + end + + lib.ASCRefreshSheet() +end + +--------------------------------------------------------- +-- ScrollSheet Functions +--------------------------------------------------------- +-- lib.vendorlist[key] = { link, sig, count, bag, slot, reason } +-- ASCtempstorage[index] = { link, vendorPrice, AppraiserPrice, reason, vendorIgnoreDisplay } +function lib.ASCRefreshSheet() + local ASCtempstorage, style = {}, {} + for k, v in pairs(lib.vendorlist) do + local itemLink, itemSig, count, bag, slot, reason = unpack(v) + local _, iID = decode(itemLink) + local vendor = GetSellValue and GetSellValue(iID) or 0 + local abuy, abid = GetPrice(itemLink, nil, true) + local vendorignored = "YES" + local styleColor = {0,1,0} + if get("util.automagic.vidignored"..iID) == true then + vendorignored = "NO" + styleColor = {1,0,0} + end + table.insert(ASCtempstorage,{ + itemLink, --link form for mouseover tooltips to work + vendor, + tonumber(abuy) or tonumber(abid), + reason, + vendorignored, + }) + style[#ASCtempstorage] = {} + style[#ASCtempstorage][5] = {["textColor"] = styleColor} + end + + lib.confirmsellui.resultlist.sheet:SetData(ASCtempstorage, style) --Set the GUI scrollsheet +end + +function lib.ASCOnEnter(button, row, index) + if lib.confirmsellui.resultlist.sheet.rows[row][index]:IsShown()then --Hide tooltip for hidden cells + local link + link = lib.confirmsellui.resultlist.sheet.rows[row][index]:GetText() + if link and link:find("\124Hitem:%d") then + GameTooltip:SetOwner(button, "ANCHOR_RIGHT") + -- ccox - this was using count (undefined variable), but all similar code in Auc-Util-AutoMagic.lua uses a count of 1 + AucAdvanced.ShowItemLink(GameTooltip, link, 1) + end + end + end + + +function lib.ASCOnClick() + --print("CLICK") +end + +function lib.ASCSelect() + if lib.confirmsellui.resultlist.sheet.selected then + selected = lib.confirmsellui.resultlist.sheet.selected + selecteddata = lib.confirmsellui.resultlist.sheet:GetSelection() + selecteditem = selecteddata[1] + selectedvendor = selecteddata[2] + selectedappraiser = selecteddata[3] + selectedwhy = selecteddata[4] + selectedignored = selecteddata[5] + + lib.confirmsellui.ignoreButton:Enable() + lib.confirmsellui.removeButton:Enable() + lib.confirmsellui.unignoreButton:Enable() + else + selected, selecteditem, selectedvendor, selectedappraiser, selectedwhy, selectedignored = nil, nil, nil, nil, nil, nil + lib.confirmsellui.ignoreButton:Disable() + lib.confirmsellui.removeButton:Disable() + lib.confirmsellui.unignoreButton:Disable() + end +end +--------------------------------------------------------- +-- Confirm AutoSell Interface +--------------------------------------------------------- + +local SelectBox = LibStub:GetLibrary("SelectBox") +local ScrollSheet = LibStub:GetLibrary("ScrollSheet") + +lib.confirmsellui = CreateFrame("Frame", "confirmsellui", UIParent); lib.confirmsellui:Hide() +function lib.makeconfirmsellui() + lib.confirmsellui:ClearAllPoints() + lib.confirmsellui:SetPoint("CENTER", UIParent, "CENTER", 1,1) + lib.confirmsellui:SetFrameStrata("DIALOG") + lib.confirmsellui:SetHeight(220) + lib.confirmsellui:SetWidth(650) + lib.confirmsellui:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + edgeFile = "Interface/Tooltips/UI-Tooltip-Border", + tile = true, tileSize = 32, edgeSize = 32, + insets = { left = 9, right = 9, top = 9, bottom = 9 } + }) + lib.confirmsellui:SetBackdropColor(0,0,0, 0.8) + lib.confirmsellui:EnableMouse(true) + lib.confirmsellui:SetMovable(true) + lib.confirmsellui:SetClampedToScreen(true) + --will add a item to sell list if its droped onto the edges on the grey item sell window + lib.confirmsellui:SetScript("OnReceiveDrag", function() + local objtype, _, link = GetCursorInfo() + ClearCursor() + if objtype == "item" then + lib.setWorkingItem(link) + autosellframe.additemtolist() + lib.vendorAction() + end + end) + + -- Make highlightable drag bar + lib.confirmsellui.Drag = CreateFrame("Button", "", lib.confirmsellui) + lib.confirmsellui.Drag:SetPoint("TOPLEFT", lib.confirmsellui, "TOPLEFT", 10,-5) + lib.confirmsellui.Drag:SetPoint("TOPRIGHT", lib.confirmsellui, "TOPRIGHT", -10,-5) + lib.confirmsellui.Drag:SetHeight(6) + lib.confirmsellui.Drag:SetHighlightTexture("Interface\\FriendsFrame\\UI-FriendsFrame-HighlightBar") + lib.confirmsellui.Drag:SetScript("OnMouseDown", function() lib.confirmsellui:StartMoving() end) + lib.confirmsellui.Drag:SetScript("OnMouseUp", function() lib.confirmsellui:StopMovingOrSizing() end) + lib.confirmsellui.Drag:SetScript("OnEnter", function() lib.buttonTooltips( lib.confirmsellui.Drag, "Click and drag to reposition window") end) + lib.confirmsellui.Drag:SetScript("OnLeave", function() GameTooltip:Hide() end) + + -- Text Header + lib.confirmsellheader = lib.confirmsellui:CreateFontString(one, "OVERLAY", "NumberFontNormalYellow") + lib.confirmsellheader:SetText("AutoMagic: Confirm Pending Sales") + lib.confirmsellheader:SetJustifyH("CENTER") + lib.confirmsellheader:SetWidth(200) + lib.confirmsellheader:SetHeight(10) + lib.confirmsellheader:SetPoint("TOPLEFT", lib.confirmsellui, "TOPLEFT", 0, -10) + lib.confirmsellheader:SetPoint("TOPRIGHT", lib.confirmsellui, "TOPRIGHT", 0, 0) + lib.confirmsellui.confirmsellheader = lib.confirmsellheader + + lib.confirmsellui.help = lib.confirmsellui:CreateFontString(nil, "OVERLAY", "NumberFontNormalYellow") + lib.confirmsellui.help:SetText("Drop items here to add to the sell list") + lib.confirmsellui.help:SetJustifyH("CENTER") + lib.confirmsellui.help:SetWidth(100) + lib.confirmsellui.help:SetPoint("LEFT", lib.confirmsellui, "LEFT", 10, 0) + + -- [name of frame]:SetPoint("[relative to point on my frame]","[frame we want to be relative to]","[point on relative frame]",-left/+right, -down/+up) + + --Create the autosell list results frame + lib.confirmsellui.resultlist = CreateFrame("Frame", nil, lib.confirmsellui) + lib.confirmsellui.resultlist:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + edgeFile = "Interface/Tooltips/UI-Tooltip-Border", + tile = true, tileSize = 32, edgeSize = 16, + insets = { left = 5, right = 5, top = 5, bottom = 5 } + }) + + lib.confirmsellui.resultlist:SetBackdropColor(0, 0, 0.0, 0.5) + lib.confirmsellui.resultlist:SetPoint("TOPLEFT", lib.confirmsellui, "TOPLEFT", 120, -25) + lib.confirmsellui.resultlist:SetPoint("TOPRIGHT", lib.confirmsellui, "TOPRIGHT", -10, -10) + lib.confirmsellui.resultlist:SetPoint("BOTTOM", lib.confirmsellui, "BOTTOM", 0, 30) + + lib.confirmsellui.resultlist.sheet = ScrollSheet:Create(lib.confirmsellui.resultlist, { + { ('Item:'), "TOOLTIP", 170, { DESCENDING=false, DEFAULT=true } }, + { "Vendor", "COIN", 70 }, + { "Appraiser", "COIN", 70 }, + { "Selling for", "TEXT", 70 }, + { "Will be Sold", "TEXT", 100}, + }) + lib.confirmsellui.resultlist.sheet:EnableVerticalScrollReset(false) + lib.confirmsellui.resultlist.sheet:EnableSelect(true) + + --After we have finished creating the scrollsheet and all saved settings have been applied set our event processor + function lib.confirmsellui.resultlist.sheet.Processor(callback, self, button, column, row, order, curDir, ...) + if (callback == "OnEnterCell") then + lib.ASCOnEnter(button, row, column) + elseif (callback == "OnLeaveCell") then + GameTooltip:Hide() + elseif (callback == "OnClickCell") then + lib.ASCOnClick(button, row, column) + elseif (callback == "OnMouseDownCell") then + lib.ASCSelect() + end + end + --use our custom sort method not scrollsheets + lib.confirmsellui.resultlist.sheet.CustomSort = lib.CustomSort + + -- Continue with sales button + lib.confirmsellui.continueButton = CreateFrame("Button", nil, lib.confirmsellui, "OptionsButtonTemplate") + lib.confirmsellui.continueButton:SetPoint("BOTTOMRIGHT", lib.confirmsellui, "BOTTOMRIGHT", -18, 10) + lib.confirmsellui.continueButton:SetText(("Continue")) + lib.confirmsellui.continueButton:SetScript("OnClick", lib.ASCConfirmContinue) + lib.confirmsellui.continueButton:SetScript("OnEnter", function() lib.buttonTooltips( lib.confirmsellui.continueButton, "Click to sell all listed items to vendor.") end) + lib.confirmsellui.continueButton:SetScript("OnLeave", function() GameTooltip:Hide() end) + + --Remove item from sales button + lib.confirmsellui.removeButton = CreateFrame("Button", nil, lib.confirmsellui, "OptionsButtonTemplate") + lib.confirmsellui.removeButton:SetPoint("BOTTOMRIGHT", lib.confirmsellui.continueButton, "BOTTOMLEFT", -18, 0) + lib.confirmsellui.removeButton:SetText(("Remove")) + lib.confirmsellui.removeButton:SetScript("OnClick", lib.ASCRemoveItem) + lib.confirmsellui.removeButton:Disable() + + -- Ignore item from future sales + lib.confirmsellui.ignoreButton = CreateFrame("Button", nil, lib.confirmsellui, "OptionsButtonTemplate") + lib.confirmsellui.ignoreButton:SetPoint("BOTTOMRIGHT", lib.confirmsellui.removeButton, "BOTTOMLEFT", -18, 0) + lib.confirmsellui.ignoreButton:SetText(("Ignore")) + lib.confirmsellui.ignoreButton:SetScript("OnClick", lib.ASCIgnoreItem) + lib.confirmsellui.ignoreButton:Disable() + + -- Un-Ignore item from future sales + lib.confirmsellui.unignoreButton = CreateFrame("Button", nil, lib.confirmsellui, "OptionsButtonTemplate") + lib.confirmsellui.unignoreButton:SetPoint("BOTTOMRIGHT", lib.confirmsellui.ignoreButton, "BOTTOMLEFT", -18, 0) + lib.confirmsellui.unignoreButton:SetText(("Un-Ignore")) + lib.confirmsellui.unignoreButton:SetScript("OnClick", lib.ASCUnIgnoreItem) + lib.confirmsellui.unignoreButton:Disable() + + --Hide sales window + lib.confirmsellui.closeButton = CreateFrame("Button", nil, lib.confirmsellui, "UIPanelCloseButton") + lib.confirmsellui.closeButton:SetScript("OnClick", function() lib.confirmsellui:Hide() end) + lib.confirmsellui.closeButton:SetPoint("TOPRIGHT", lib.confirmsellui, "TOPRIGHT", 0,0) +end + +lib.makeconfirmsellui() +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-AutoMagic/ConfirmSellUI.lua $", "$Rev: 4901 $") diff --git a/Auc-Advanced/Modules/Auc-Util-AutoMagic/Core.lua b/Auc-Advanced/Modules/Auc-Util-AutoMagic/Core.lua new file mode 100644 index 0000000..4f0113b --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-AutoMagic/Core.lua @@ -0,0 +1,523 @@ +--[[ + Auctioneer - AutoMagic Utility module + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: Core.lua 4666 2010-02-15 15:31:28Z Kandoko $ + URL: http://auctioneeraddon.com/ + + AutoMagic is an Auctioneer module which automates mundane tasks for you. + + 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 lib = AucAdvanced.Modules.Util.AutoMagic +local print,decode,_,_,replicate,empty,get,set,default,debugPrint,fill = AucAdvanced.GetModuleLocals() +local AppraiserValue, DisenchantValue, ProspectValue, VendorValue, bestmethod, bestvalue, runstop, _ + +-- This table is validating that each ID within it is a gem from prospecting. +local isGem = + { + [818] = true,--TIGERSEYE + [774] = true,--MALACHITE + [1210] = true,--SHADOWGEM + [1705] = true,--LESSERMOONSTONE + [1206] = true,--MOSSAGATE + [3864] = true,--CITRINE + [1529] = true,--JADE + [7909] = true,--AQUAMARINE + [7910] = true,--STARRUBY + [12800] = true,--AZEROTHIANDIAMOND + [12361] = true,--BLUESAPPHIRE + [12799] = true,--LARGEOPAL + [12364] = true,--HUGEEMERALD + [23077] = true,--BLOODGARNET + [21929] = true,--FLAMESPESSARITE + [23112] = true,--GOLDENDRAENITE + [23709] = true,--DEEPPERIDOT + [23117] = true,--AZUREMOONSTONE + [23107] = true,--SHADOWDRAENITE + [23436] = true,--LIVINGRUBY + [23439] = true,--NOBLETOPAZ + [23440] = true,--DAWNSTONE + [23437] = true,--TALASITE + [23428] = true,--STAROFELUNE + [23441] = true,--NIGHTSEYE + [36920] = true,--SUNCRYSTAL + [36926] = true,--SHADOWCRYSTAL + [36929] = true,--HUGECITRINE + [36932] = true,--DARKJADE + [36923] = true,--CHALCEDONY + [36917] = true,--BLOODSTONE + [36927] = true,--TWILIGHTOPAL + [36924] = true,--SKYSAPPHIRE + [36918] = true,--SCARLETRUBY + [36930] = true,--MONARCHTOPAZ + [36933] = true,---FORESTEMERALD + [36921] = true,--AUTUMNSGLOW + --EPIC 80 + [36919] = true,--CARDNIALRUBY + [36922] = true,--KING'S AMBER + [36925] = true,--Majestic Zircon + [36928] = true,--DREADSTONE + [36931] = true,--AMETRINE + [36934] = true,--EYE OF ZUL +} + +-- This table is validating that each ID within it is a mat from disenchanting. +local isDEMats = + { + [34057] = true,--Abyss Crystal + [22450] = true,--Void Crystal + [20725] = true,--Nexus Crystal + [34052] = true,--Dream Shard + [34053] = true,--Small Dream Shard + [22449] = true,--Large Prismatic Shards + [14344] = true,--Large Brillianr Shards + [11178] = true,--Large Radiant Shards + [11139] = true,--Large Glowing Shards + [11084] = true,--Large Glimmering Shards + [22448] = true,--Small Primatic Shards + [14343] = true,--Small Brilliant Shards + [11177] = true,--Small Radiant Shards + [11138] = true,--Small Glowing Shards + [10978] = true,--Small Glimmering Shards + [34055] = true,--Greater Cosmic Essence + [34056] = true,--Lesser Cosmic Essence + [22446] = true,--Greater Planer Essence + [16203] = true,--Greater Eternal Essence + [11175] = true,--Greater Nether Essence + [11135] = true,--Greater Mystic Essence + [11082] = true,--Greater Astral Essence + [10939] = true,--Greater Magic Essence + [22447] = true,--Lesser Planer Essence + [16202] = true,--Lesser Eternal Essence + [11174] = true,--Lesser Nether Essence + [11134] = true,--Lesser Mystic Essence + [10998] = true,--Lesser Astral Essence + [10938] = true,--Lesser Magic Essence + [34054] = true, --Infinite Dust + [22445] = true,--Arcane Dust + [16204] = true,--Illusion Dust + [11176] = true,--Dream Dust + [11137] = true,--Vision Dust + [11083] = true,--Soul Dust + [10940] = true,--Strange Dust +} + +-- This table is validating that each ID within it is a mat from Milling (table from enchantrix) +local isPigmentMats = + { + [39151] = true, -- ALABASTER_PIGMENT + [39334] = true, -- DUSKY_PIGMENT + [39338] = true, -- GOLDEN_PIGMENT + [39339] = true, -- EMERALD_PIGMENT + [39340] = true, -- VIOLET_PIGMENT + [39341] = true, -- SILVERY_PIGMENT + [43103] = true, -- VERDANT_PIGMENT + [43104] = true, -- BURNT_PIGMENT + [43105] = true, -- INDIGO_PIGMENT + [43106] = true, -- RUBY_PIGMENT + [43107] = true, -- SAPPHIRE_PIGMENT + [39342] = true, -- NETHER_PIGMENT + [43108] = true, -- EBON_PIGMENT + [39343] = true, -- AZURE_PIGMENT + [43109] = true, -- ICY_PIGMENT +} +-- This table is validating that each ID within it is a herb. Data from informant. This allows locale independent herbs +local isHerb = + { + [765] = true, -- Silverleaf + [785] = true, -- Mageroyal + [2447] = true, -- Peacebloom + [2449] = true, -- Earthroot + [2450] = true, -- Briarthorn + [2452] = true, -- Swiftthistle + [2453] = true, -- Bruiseweed + [3355] = true, -- Wild Steelbloom + [3356] = true, -- Kingsblood + [3357] = true, -- Liferoot + [3358] = true, -- Khadgar's Whisker + [3369] = true, -- Grave Moss + [3818] = true, -- Fadeleaf + [3819] = true, -- Wintersbite + [3820] = true, -- Stranglekelp + [3821] = true, -- Goldthorn + [4625] = true, -- Firebloom + [8153] = true, -- Wildvine + [8831] = true, -- Purple Lotus + [8836] = true, -- Arthas' Tears + [8838] = true, -- Sungrass + [8839] = true, -- Blindweed + [8845] = true, -- Ghost Mushroom + [8846] = true, -- Gromsblood + [13463] = true, -- Dreamfoil + [13464] = true, -- Golden Sansam + [13465] = true, -- Mountain Silversage + [13466] = true, -- Plaguebloom + [13467] = true, -- Icecap + [13468] = true, -- Black Lotus + [19726] = true, -- Bloodvine + [19727] = true, -- Blood Scythe + [22710] = true, -- Bloodthistle + [22785] = true, -- Felweed + [22786] = true, -- Dreaming Glory + [22787] = true, -- Ragveil + [22788] = true, -- Flame Cap + [22789] = true, -- Terocone + [22790] = true, -- Ancient Lichen + [22791] = true, -- Netherbloom + [22792] = true, -- Nightmare Vine + [22793] = true, -- Mana Thistle + [22794] = true, -- Fel Lotus + [22797] = true, -- Nightmare Seed + [36901] = true, -- Goldclover + [36902] = true, -- Constrictor Grass + [36903] = true, -- Adder's Tongue + [36904] = true, -- Tiger Lily + [36905] = true, -- Lichbloom + [36906] = true, -- Icethorn + [36907] = true, -- Talandra's Rose + [36908] = true, -- Frost Lotus + [37921] = true, -- Deadnettle + [39970] = true, -- Fire Leaf + } + +--this set of tables allows us to match the locale dependet itemtype to the gear a player class can use +local isGear = { + --armor + ["cloth"] = GetSpellInfo(9078), + ["leather"] = GetSpellInfo(9077), + ["mail"] = GetSpellInfo(8737), + ["plate"] = GetSpellInfo(750), + ["shield"] = GetSpellInfo(9116), + --weapons + ["bows"] = GetSpellInfo(264), + ["crossbows"] = GetSpellInfo(5011), + ["daggers"] = GetSpellInfo(1180), + ["fist weapons"] = GetSpellInfo(15590), + ["guns"] = GetSpellInfo(266), + ["one-handed axes"] = GetSpellInfo(196), + ["one-handed maces"] = GetSpellInfo(198), + ["one-handed swords"] = GetSpellInfo(201), + ["polearms"] = GetSpellInfo(200), + ["staves"] = GetSpellInfo(227), + ["thrown"] = GetSpellInfo(2567), + ["two-handed axes"] = GetSpellInfo(197), + ["two-handed maces"] = GetSpellInfo(199), + ["two-handed swords"] = GetSpellInfo(202), + ["wands"] = GetSpellInfo(5009), +} +--what gear each class CANNOT use +local isClass = { + ["DEATHKNIGHT"] = "shield|thrown|staves|crossbows|bows|fist weapons|leather|mail|guns|cloth|wands|daggers", + ["SHAMAN"] = "one-handed swords|thrown|staves|crossbows|plate|bows|two-handed swords|leather|guns|polearms|cloth|wands", + ["MAGE"] = "two-handed maces|shield|thrown|crossbows|plate|one-handed maces|one-handed axes|bows|fist weapons|two-handed swords|leather|mail|guns|polearms|two-handed axes", + ["PRIEST"] = "one-handed swords|two-handed maces|shield|thrown|crossbows|plate|one-handed axes|bows|fist weapons|two-handed swords|leather|mail|guns|polearms|two-handed axes", + ["WARLOCK"] = "two-handed maces|shield|thrown|crossbows|plate|one-handed maces|one-handed axes|bows|fist weapons|two-handed swords|leather|mail|guns|polearms|two-handed axes", + ["DRUID"] = "one-handed swords|shield|thrown|crossbows|plate|one-handed axes|bows|fist weapons|two-handed swords|mail|guns|polearms|cloth|wands|two-handed axes", + ["ROGUE"] = "two-handed maces|shield|staves|plate|two-handed swords|mail|polearms|cloth|wands|two-handed axes", + --lvl 40 + ["WARRIOR"] = "leather|mail|cloth|wands", + ["WARRIORLOW"] = "leather|cloth|wands", --warriors and paladins plate does not appear before 40, + ["HUNTER"] = "two-handed maces|shield|plate|one-handed maces|leather|cloth|wands", + ["HUNTERLOW"] = "two-handed maces|shield|plate|one-handed maces|mail|cloth|wands", + ["PALADIN"] = "thrown|staves|crossbows|bows|fist weapons|leather|mail|guns|cloth|wands|daggers", + ["PALADINLOW"] = "thrown|staves|crossbows|bows|fist weapons|leather|guns|cloth|wands|daggers", + } + +local playerClassEquipment +function classexpand() + local _, class = UnitClass("player") + local level = UnitLevel("player") + if not isClass[class] then print("Unknown player class..", class) return end + + if level < 40 and (class == "WARRIOR" or class == "PALADIN" or class == "HUNTER") then + class = class.."LOW" + end + + local temp = {} + for usable in isClass[class]:gmatch("(.-)|") do + local skill = isGear[usable] + if skill then + temp[skill] = usable + end + end + return temp +end +--Taken from auc core, used to find soulbound state +local BindTypes = { + [ITEM_SOULBOUND] = "Bound", + [ITEM_BIND_ON_PICKUP] = "Bound", +} +--Auc Core tooltip scanner +local ScanTip = AppraiserTip +local ScanTip2 = AppraiserTipTextLeft2 +local ScanTip3 = AppraiserTipTextLeft3 + +lib.vendorlist = {} +function lib.vendorAction(autovendor) + if not playerClassEquipment then + playerClassEquipment = classexpand()--create the players localized usable gear list + end + empty(lib.vendorlist) --this needs to be cleared on every vendor open + for bag=0,4 do + for slot=1,GetContainerNumSlots(bag) do + if (GetContainerItemLink(bag,slot)) then + local itemLink = GetContainerItemLink(bag,slot) + local texture, itemCount, locked, _, lootable = GetContainerItemInfo(bag, slot) --items that have been vedored but are still in players bag (lag) will be locked by server. + if itemLink and not locked then + if not itemCount then itemCount = 1 end + local _, itemID, _, _, _, _ = decode(itemLink) + local itemSig = AucAdvanced.API.GetSigFromLink(itemLink) -- future plan is to use itemSig in place of itemID throughout - to eliminate problems for items with suffixes + local itemName, _, itemRarity, _, _, itemType, itemSubType = GetItemInfo(itemLink) + local key = bag..":"..slot -- key needs to be unique, but is not currently used for anything. future: rethink if this can be made useful + --tooltip checks soulbound status + ScanTip:SetOwner(UIParent, "ANCHOR_NONE") + ScanTip:ClearLines() + ScanTip:SetBagItem(bag, slot) + local soulbound = BindTypes[ScanTip2:GetText()] or BindTypes[ScanTip3:GetText()] + ScanTip:Hide() + --autovendor is used to sell without confirmation. + if autovendor then + if get("util.automagic.autoselllist") and get("util.automagic.autoselllistnoprompt") and lib.autoSellList[ itemID ] then + lib.vendorlist[key] = {itemLink, itemSig, itemCount, bag, slot, "Sell List"} + elseif itemRarity == 0 and get("util.automagic.autosellgrey") and get("util.automagic.autosellgreynoprompt") then + lib.vendorlist[key] = {itemLink, itemSig, itemCount, bag, slot, "Grey"} + elseif soulbound and get("util.automagic.vendorunusablebop") and get("util.automagic.autosellbopnoprompt") and IsEquippableItem(itemLink) and itemRarity < 3 and not lootable and playerClassEquipment[itemSubType] then + lib.vendorlist[key] = {itemLink, itemSig, itemCount, bag, slot, "Unusable"} + elseif get("util.automagic.autosellreason") and get("util.automagic.autosellreasonnoprompt") then + local reason, text = lib.getReason(itemLink, itemName, itemCount, "vendor") + if reason and text then + lib.vendorlist[key] = {itemLink, itemSig, itemCount, bag, slot, text} + end + end + else + if get("util.automagic.autoselllist") and lib.autoSellList[ itemID ] then + lib.vendorlist[key] = {itemLink, itemSig, itemCount, bag, slot, "Sell List"} + elseif itemRarity == 0 and get("util.automagic.autosellgrey") then + lib.vendorlist[key] = {itemLink, itemSig, itemCount, bag, slot, "Grey"} + elseif soulbound and get("util.automagic.vendorunusablebop") and IsEquippableItem(itemLink) and itemRarity < 3 and not lootable and playerClassEquipment[itemSubType] then + lib.vendorlist[key] = {itemLink, itemSig, itemCount, bag, slot, "Unusable"} + elseif get("util.automagic.autosellreason") then + local reason, text = lib.getReason(itemLink, itemName, itemCount, "vendor") + if reason and text then + lib.vendorlist[key] = {itemLink, itemSig, itemCount, bag, slot, text} + end + end + end + end + end + end + end + if autovendor then + lib.ASCConfirmContinue() + else + lib.ASCPrompt() + end +end + +function lib.disenchantAction() + MailFrameTab_OnClick(nil, 2) + for bag=0,4 do + for slot=1,GetContainerNumSlots(bag) do + if (GetContainerItemLink(bag,slot)) then + local itemLink, itemCount = GetContainerItemLink(bag,slot) + if (itemLink == nil) then return end + if itemCount == nil then _, itemCount = GetContainerItemInfo(bag, slot) end + if itemCount == nil then itemCount = 1 end + runstop = 0 + local _, itemID, _, _, _, _ = decode(itemLink) + local itemName, _, itemRarity, _, _, _, _, _, _, _ = GetItemInfo(itemLink) + if (AucAdvanced.Modules.Util.ItemSuggest and get("util.automagic.overidebtmmail") == true) then + local aimethod = AucAdvanced.Modules.Util.ItemSuggest.itemsuggest(itemLink, itemCount) + if(aimethod == "Disenchant") then + if (get("util.automagic.chatspam")) then + print("AutoMagic has loaded", itemName, " due to Item Suggest(Disenchant)") + end + UseContainerItem(bag, slot) + runstop = 1 + end + else --look for btmScan or SearchUI reason codes if above fails + local reason, text = lib.getReason(itemLink, itemName, itemCount, "disenchant") + if reason and text then + if (get("util.automagic.chatspam")) then + print("AutoMagic has loaded", itemName, " due to", text ,"Rule(Disenchant)") + end + UseContainerItem(bag, slot) + end + end + end + end + end +end + +function lib.prospectAction() + MailFrameTab_OnClick(nil, 2) + for bag=0,4 do + for slot=1,GetContainerNumSlots(bag) do + if (GetContainerItemLink(bag,slot)) then + local itemLink, itemCount = GetContainerItemLink(bag,slot) + if (itemLink == nil) then return end + if itemCount == nil then _, itemCount = GetContainerItemInfo(bag, slot) end + if itemCount == nil then itemCount = 1 end + local _, itemID, _, _, _, _ = decode(itemLink) + local itemName, _, itemRarity, _, _, _, _, _, _, _ = GetItemInfo(itemLink) + runstop = 0 + if (AucAdvanced.Modules.Util.ItemSuggest and get("util.automagic.overidebtmmail") == true) then + local aimethod = AucAdvanced.Modules.Util.ItemSuggest.itemsuggest(itemLink, itemCount) + if(aimethod == "Prospect") then + if (get("util.automagic.chatspam")) then + print("AutoMagic has loaded", itemName, " due to Item Suggest(Prospect)") + end + UseContainerItem(bag, slot) + runstop = 1 + end + else --look for btmScan or SearchUI reason codes if above fails + local reason, text = lib.getReason(itemLink, itemName, itemCount, "prospect") + if reason and text then + if (get("util.automagic.chatspam")) then + print("AutoMagic has loaded", itemName, " due to", text ,"Rule(Prospect)") + end + UseContainerItem(bag, slot) + end + end + end + end + end +end + +function lib.gemAction() + MailFrameTab_OnClick(nil, 2) + for bag=0,4 do + for slot=1,GetContainerNumSlots(bag) do + if (GetContainerItemLink(bag,slot)) then + local itemLink, itemCount = GetContainerItemLink(bag,slot) + if (itemLink == nil) then return end + if itemCount == nil then _, itemCount = GetContainerItemInfo(bag, slot) end + if itemCount == nil then itemCount = 1 end + local _, itemID, _, _, _, _ = decode(itemLink) + local itemName, _, itemRarity, _, _, _, _, _, _, _ = GetItemInfo(itemLink) + if isGem[ itemID ] then + if (get("util.automagic.chatspam")) then + print("AutoMagic has loaded", itemName, " because it is a gem!") + end + UseContainerItem(bag, slot) + end + end + end + end +end + +function lib.dematAction() + MailFrameTab_OnClick(nil, 2) + for bag=0,4 do + for slot=1,GetContainerNumSlots(bag) do + if (GetContainerItemLink(bag,slot)) then + local itemLink, itemCount = GetContainerItemLink(bag,slot) + if (itemLink == nil) then return end + if itemCount == nil then _, itemCount = GetContainerItemInfo(bag, slot) end + if itemCount == nil then itemCount = 1 end + local _, itemID, _, _, _, _ = decode(itemLink) + local itemName, _, itemRarity, _, _, _, _, _, _, _ = GetItemInfo(itemLink) + if isDEMats[ itemID ] then + if (get("util.automagic.chatspam")) then + print("AutoMagic has loaded", itemName, " because it is a mat used for enchanting.") + end + UseContainerItem(bag, slot) + end + end + end + end +end + +function lib.pigmentAction() + MailFrameTab_OnClick(nil, 2) + for bag=0,4 do + for slot=1,GetContainerNumSlots(bag) do + if (GetContainerItemLink(bag,slot)) then + local itemLink, itemCount = GetContainerItemLink(bag,slot) + if (itemLink == nil) then return end + if itemCount == nil then _, itemCount = GetContainerItemInfo(bag, slot) end + if itemCount == nil then itemCount = 1 end + local _, itemID, _, _, _, _ = decode(itemLink) + local itemName, _, itemRarity, _, _, _, _, _, _, _ = GetItemInfo(itemLink) + if isPigmentMats[ itemID ] then + if (get("util.automagic.chatspam")) then + print("AutoMagic has loaded", itemName, " because it is a pigment used for milling.") + end + UseContainerItem(bag, slot) + end + end + end + end +end + +function lib.herbAction() + MailFrameTab_OnClick(nil, 2) + for bag=0,4 do + for slot=1,GetContainerNumSlots(bag) do + if (GetContainerItemLink(bag,slot)) then + local itemLink, itemCount = GetContainerItemLink(bag,slot) + if (itemLink == nil) then return end + if itemCount == nil then _, itemCount = GetContainerItemInfo(bag, slot) end + if itemCount == nil then itemCount = 1 end + local _, itemID, _, _, _, _ = decode(itemLink) + local itemName, _, itemRarity, _, _, _, _, _, _, _ = GetItemInfo(itemLink) + if isHerb[ itemID ] then + if (get("util.automagic.chatspam")) then + print("AutoMagic has loaded", itemName, " because it is a herb.") + end + UseContainerItem(bag, slot) + end + end + end + end +end + +--Searches for reason and returns values if found nil other wise. +--Consolidates code into one function instead of 5-6 places that need editing/maintaining +function lib.getReason(itemLink, itemName, itemCount, text) + if (BtmScan) then + local bidlist = BtmScan.Settings.GetSetting("bid.list") + if (bidlist) then + local id, suffix, enchant, seed = BtmScan.BreakLink(itemLink) + local sig = ("%d:%d:%d"):format(id, suffix, enchant) + local bids = bidlist[sig..":"..seed.."x"..itemCount] + + if(bids and bids[1] and bids[1] == text) then + return bids[1], "BTM" + end + end + end + + if (BeanCounter and BeanCounter.API.isLoaded) then + local reason = BeanCounter.API.getBidReason(itemLink, itemCount) or "" + if reason:lower() == text then + return reason, "SearchUI" + end + end + + return +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-AutoMagic/Core.lua $", "$Rev: 4666 $") diff --git a/Auc-Advanced/Modules/Auc-Util-AutoMagic/Embed.xml b/Auc-Advanced/Modules/Auc-Util-AutoMagic/Embed.xml new file mode 100644 index 0000000..57cbcac --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-AutoMagic/Embed.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/Auc-Advanced/Modules/Auc-Util-AutoMagic/Images/amagicIcon.blp b/Auc-Advanced/Modules/Auc-Util-AutoMagic/Images/amagicIcon.blp new file mode 100644 index 0000000..6cdcbd7 Binary files /dev/null and b/Auc-Advanced/Modules/Auc-Util-AutoMagic/Images/amagicIcon.blp differ diff --git a/Auc-Advanced/Modules/Auc-Util-AutoMagic/Images/amagicIconE.tga b/Auc-Advanced/Modules/Auc-Util-AutoMagic/Images/amagicIconE.tga new file mode 100644 index 0000000..41b69eb Binary files /dev/null and b/Auc-Advanced/Modules/Auc-Util-AutoMagic/Images/amagicIconE.tga differ diff --git a/Auc-Advanced/Modules/Auc-Util-AutoMagic/Mail-GUI.lua b/Auc-Advanced/Modules/Auc-Util-AutoMagic/Mail-GUI.lua new file mode 100644 index 0000000..18bcee5 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-AutoMagic/Mail-GUI.lua @@ -0,0 +1,171 @@ +--[[ + Auctioneer - AutoMagic Utility module + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: Mail-GUI.lua 4556 2009-12-03 22:20:32Z Kandoko $ + URL: http://auctioneeraddon.com/ + + AutoMagic is an Auctioneer module which automates mundane tasks for you. + + 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 lib = AucAdvanced.Modules.Util.AutoMagic +local print,decode,_,_,replicate,empty,get,set,default,debugPrint,fill = AucAdvanced.GetModuleLocals() +local AppraiserValue, DisenchantValue, ProspectValue, VendorValue, bestmethod, bestvalue, runstop, _ + +--------------------------------------------------------- +-- Mail Interface +--------------------------------------------------------- +lib.ammailgui = CreateFrame("Frame", "", UIParent); lib.ammailgui:Hide() +function lib.makeMailGUI() + -- Set frame visuals + -- [name of frame]:SetPoint("[relative to point on my frame]","[frame we want to be relative to]","[point on relative frame]",-left/+right, -down/+up) + lib.ammailgui:ClearAllPoints() + lib.ammailgui:SetPoint("CENTER", UIParent, "BOTTOMLEFT", get("util.automagic.ammailguix"), get("util.automagic.ammailguiy")) + + --Don't need to recreate duplicate frames on each mail box open. + if lib.ammailgui.Drag then return end + + lib.ammailgui:SetFrameStrata("DIALOG") + lib.ammailgui:SetHeight(75) + lib.ammailgui:SetWidth(320) + lib.ammailgui:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + edgeFile = "Interface/Tooltips/UI-Tooltip-Border", + tile = true, tileSize = 32, edgeSize = 32, + insets = { left = 9, right = 9, top = 9, bottom = 9 } + }) + lib.ammailgui:SetBackdropColor(0,0,0, 0.8) + lib.ammailgui:EnableMouse(true) + lib.ammailgui:SetMovable(true) + lib.ammailgui:SetClampedToScreen(true) + + -- Make highlightable drag bar + lib.ammailgui.Drag = CreateFrame("Button", "", lib.ammailgui) + lib.ammailgui.Drag:SetPoint("TOPLEFT", lib.ammailgui, "TOPLEFT", 10,-5) + lib.ammailgui.Drag:SetPoint("TOPRIGHT", lib.ammailgui, "TOPRIGHT", -10,-5) + lib.ammailgui.Drag:SetHeight(6) + lib.ammailgui.Drag:SetHighlightTexture("Interface\\FriendsFrame\\UI-FriendsFrame-HighlightBar") + lib.ammailgui.Drag:SetScript("OnMouseDown", function() lib.ammailgui:StartMoving() end) + lib.ammailgui.Drag:SetScript("OnMouseUp", function() lib.ammailgui:StopMovingOrSizing() end) + lib.ammailgui.Drag:SetScript("OnEnter", function() lib.buttonTooltips( lib.ammailgui.Drag, "Click and drag to reposition window.") end) + lib.ammailgui.Drag:SetScript("OnLeave", function() GameTooltip:Hide() end) + + -- Text Header + lib.mguiheader = lib.ammailgui:CreateFontString(one, "OVERLAY", "NumberFontNormalYellow") + lib.mguiheader:SetText("AutoMagic: Mail Loader") + lib.mguiheader:SetJustifyH("CENTER") + lib.mguiheader:SetWidth(200) + lib.mguiheader:SetHeight(10) + lib.mguiheader:SetPoint("TOPLEFT", lib.ammailgui, "TOPLEFT", 0, 0) + lib.mguiheader:SetPoint("TOPRIGHT", lib.ammailgui, "TOPRIGHT", 0, 0) + lib.ammailgui.mguiheader = lib.mguiheader + + + --Hide mail window + lib.ammailgui.closeButton = CreateFrame("Button", nil, lib.ammailgui, "UIPanelCloseButton") + lib.ammailgui.closeButton:SetScript("OnClick", function() lib.ammailgui:Hide() end) + lib.ammailgui.closeButton:SetPoint("TOPRIGHT", lib.ammailgui, "TOPRIGHT", 0,0) + + -- [name of frame]:SetPoint("[relative to point on my frame]","[frame we want to be relative to]","[point on relative frame]",-left/+right, -down/+up) + + + lib.mguibtmrules = lib.ammailgui:CreateFontString(two, "OVERLAY", "NumberFontNormalYellow") + lib.mguibtmrules:SetText("SUI/IS Rule:") + lib.mguibtmrules:SetJustifyH("LEFT") + lib.mguibtmrules:SetWidth(101) + lib.mguibtmrules:SetHeight(10) + lib.mguibtmrules:SetPoint("TOPLEFT", lib.ammailgui, "TOPLEFT", 8, -16) + lib.ammailgui.mguibtmrules = lib.mguibtmrules + + lib.ammailgui.loadde = CreateFrame("Button", "", lib.ammailgui, "OptionsButtonTemplate") + lib.ammailgui.loadde:SetText(("Disenchant")) + lib.ammailgui.loadde:SetPoint("TOPLEFT", lib.mguibtmrules, "BOTTOMLEFT", 0, 1) + lib.ammailgui.loadde:SetScript("OnClick", lib.disenchantAction) + lib.ammailgui.loadde:SetScript("OnEnter", function() lib.buttonTooltips( lib.ammailgui.loadde, "Add all items tagged \nfor DE to the mail.") end) + lib.ammailgui.loadde:SetScript("OnLeave", function() GameTooltip:Hide() end) + +--[[ lib.ammailgui.mailto = CreateFrame("EditBox", "", lib.ammailgui, "InputBoxTemplate") + lib.ammailgui.mailto:SetPoint("TOPLEFT", lib.ammailgui.loaddemats, "BOTTOMRIGHT", 0, -12) + lib.ammailgui.mailto:SetAutoFocus(false) + lib.ammailgui.mailto:SetHeight(15) + lib.ammailgui.mailto:SetWidth(100) + lib.ammailgui.mailto:SetMaxLetters(12) + --lib.ammailgui.loaddemailto:SetScript("OnEnterPressed", silvertocopper) + --lib.ammailgui.loaddemailto:SetScript("OnTabPressed", silvertocopper) + + lib.mguimailtotxt = lib.ammailgui:CreateFontString(four, "OVERLAY", "NumberFontNormalYellow") + lib.mguimailtotxt:SetText("Set Recipient to:") + lib.mguimailtotxt:SetJustifyH("LEFT") + lib.mguimailtotxt:SetWidth(101) + lib.mguimailtotxt:SetHeight(10) + lib.mguimailtotxt:SetPoint("TOPRIGHT", lib.ammailgui.mailto, "TOPLEFT", 0, -25) + --lib.mguimailfor:SetPoint("TOPRIGHT", lib.ammailgui.loadprospect, "BOTTOMRIGHT", 0, 0) + lib.ammailgui.mguimailtotxt = lib.mguimailtotxt]] + + lib.ammailgui.loadprospect = CreateFrame("Button", "", lib.ammailgui, "OptionsButtonTemplate") + lib.ammailgui.loadprospect:SetText(("Prospect")) + lib.ammailgui.loadprospect:SetPoint("TOPLEFT", lib.ammailgui.loadde, "BOTTOMLEFT", 0, 0) + lib.ammailgui.loadprospect:SetScript("OnClick", lib.prospectAction) + lib.ammailgui.loadprospect:SetScript("OnEnter", function() lib.buttonTooltips( lib.ammailgui.loadprospect, "Add all items tagged \nfor Prospect to the mail.") end) + lib.ammailgui.loadprospect:SetScript("OnLeave", function() GameTooltip:Hide() end) + + lib.mguimailfor = lib.ammailgui:CreateFontString(three, "OVERLAY", "NumberFontNormalYellow") + lib.mguimailfor:SetText("Misc:") + lib.mguimailfor:SetJustifyH("LEFT") + lib.mguimailfor:SetWidth(101) + lib.mguimailfor:SetHeight(10) + lib.mguimailfor:SetPoint("TOPLEFT", lib.mguibtmrules, "TOPRIGHT", 25, 0) + --lib.mguimailfor:SetPoint("TOPRIGHT", lib.ammailgui.loadprospect, "BOTTOMRIGHT", 0, 0) + lib.ammailgui.mguimailfor = lib.mguimailfor + + lib.ammailgui.loadgems = CreateFrame("Button", "", lib.ammailgui, "OptionsButtonTemplate") + lib.ammailgui.loadgems:SetText(("Gems")) + lib.ammailgui.loadgems:SetPoint("TOPLEFT", lib.mguimailfor, "BOTTOMLEFT", 0, 0) + lib.ammailgui.loadgems:SetScript("OnClick", lib.gemAction) + lib.ammailgui.loadgems:SetScript("OnEnter", function() lib.buttonTooltips( lib.ammailgui.loadgems, "Add all Gems to the mail.") end) + lib.ammailgui.loadgems:SetScript("OnLeave", function() GameTooltip:Hide() end) + + lib.ammailgui.loadherb = CreateFrame("Button", "", lib.ammailgui, "OptionsButtonTemplate") + lib.ammailgui.loadherb:SetText(("Herbs")) + lib.ammailgui.loadherb:SetPoint("LEFT", lib.ammailgui.loadgems, "RIGHT", 0, 0) + lib.ammailgui.loadherb:SetScript("OnClick", lib.herbAction) + lib.ammailgui.loadherb:SetScript("OnEnter", function() lib.buttonTooltips( lib.ammailgui.loadherb, "Add all items classified \nas herbs to the mail.") end) + lib.ammailgui.loadherb:SetScript("OnLeave", function() GameTooltip:Hide() end) + + lib.ammailgui.loaddemats = CreateFrame("Button", "", lib.ammailgui, "OptionsButtonTemplate") + lib.ammailgui.loaddemats:SetText(("Chant Mats")) + lib.ammailgui.loaddemats:SetPoint("TOPLEFT", lib.ammailgui.loadgems, "BOTTOMLEFT", 0, 0) + lib.ammailgui.loaddemats:SetScript("OnClick", lib.dematAction) + lib.ammailgui.loaddemats:SetScript("OnEnter", function() lib.buttonTooltips( lib.ammailgui.loaddemats, "Add all Enchanting mats \nto the mail.") end) + lib.ammailgui.loaddemats:SetScript("OnLeave", function() GameTooltip:Hide() end) + + lib.ammailgui.loadpigment = CreateFrame("Button", "", lib.ammailgui, "OptionsButtonTemplate") + lib.ammailgui.loadpigment:SetText(("Pigments")) + lib.ammailgui.loadpigment:SetPoint("LEFT", lib.ammailgui.loaddemats, "RIGHT", 0, 0) + lib.ammailgui.loadpigment:SetScript("OnClick", lib.pigmentAction) + lib.ammailgui.loadpigment:SetScript("OnEnter", function() lib.buttonTooltips( lib.ammailgui.loadpigment, "Add all Pigments \nto the mail.") end) + lib.ammailgui.loadpigment:SetScript("OnLeave", function() GameTooltip:Hide() end) +end +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-AutoMagic/Mail-GUI.lua $", "$Rev: 4556 $") diff --git a/Auc-Advanced/Modules/Auc-Util-CompactUI/Auc-Util-CompactUI.toc b/Auc-Advanced/Modules/Auc-Util-CompactUI/Auc-Util-CompactUI.toc new file mode 100644 index 0000000..59f8bd4 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-CompactUI/Auc-Util-CompactUI.toc @@ -0,0 +1,14 @@ +## Title: Auc:Util:Compact UI +## Notes: Provides additional features and enhancement of the Auction House's "Browse" frame. +## +## Interface: 40000 +## Dependancies: Auc-Advanced +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Auc-Util-CompactUI.toc 4933 2010-10-13 17:16:14Z Nechckn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## X-Max-Interface: 40000 +## +Embed.xml diff --git a/Auc-Advanced/Modules/Auc-Util-CompactUI/CompactUI.lua b/Auc-Advanced/Modules/Auc-Util-CompactUI/CompactUI.lua new file mode 100644 index 0000000..6a9f350 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-CompactUI/CompactUI.lua @@ -0,0 +1,905 @@ +--[[ + Auctioneer - Price Level Utility module + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: CompactUI.lua 4933 2010-10-13 17:16:14Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This is an addon for World of Warcraft that adds a price level indicator + to auctions when browsing the Auction House, so that you may readily see + which items are bargains or overpriced at a glance. + + 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 libType, libName = "Util", "CompactUI" +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end +local print,decode,_,_,replicate,empty,get,set,default,debugPrint,fill,_TRANS = AucAdvanced.GetModuleLocals() + +local data +private.cache = {} + +local searchname, searchminLevel, searchmaxLevel, searchinvTypeIndex, searchclassIndex, searchsubclassIndex, searchpage, searchisUsable, searchqualityIndex, searchGetAll + +function lib.Processor(callbackType, ...) + if (callbackType == "config") then + private.SetupConfigGui(...) + elseif (callbackType == "auctionui") then + private.HookAH(...) + elseif callbackType == "configchanged" + or callbackType == "blockupdate" then + if (private.Active) then + private.MyAuctionFrameUpdate() + end + elseif (callbackType == "scanstats") then + private.cache = {} + end +end + +lib.Processors = {} +function lib.Processors.config(callbackType, ...) + private.SetupConfigGui(...) +end + +function lib.Processors.auctionui(callbackType, ...) + private.HookAH(...) +end + +function lib.Processors.configchanged(callbackType, ...) + if (private.Active) then + private.MyAuctionFrameUpdate() + end +end + +lib.Processors.blockupdate = lib.Processors.configchanged + +function lib.Processors.scanstats(callbackType, ...) + private.cache = {} +end + + +local OldSortAuctionApplySort + +function lib.OnLoad() + --print("AucAdvanced: {{"..libType..":"..libName.."}} loaded!") + if SortAuctionApplySort then + OldSortAuctionApplySort = SortAuctionApplySort + SortAuctionApplySort=private.QueryCurrent + end + hooksecurefunc("QueryAuctionItems", private.CopyQuery) + AucAdvanced.Settings.SetDefault("util.compactui.activated", true) + AucAdvanced.Settings.SetDefault("util.compactui.tooltiphelp", true) + AucAdvanced.Settings.SetDefault("util.compactui.collapse", false) + AucAdvanced.Settings.SetDefault("util.compactui.bidrequired", true) + AucAdvanced.Settings.SetDefault("util.browseoverride.activated", false) +end + +--[[ Local functions ]]-- +private.candy = {} +private.buttons = {} +function private.CopyQuery(...) + searchname, searchminLevel, searchmaxLevel, searchinvTypeIndex, searchclassIndex, searchsubclassIndex, searchpage, searchisUsable, searchqualityIndex, searchGetAll = ... +end + +function private.QueryCurrent(SortTable, SortColumn, reverse) + if SortTable == "bidder" or SortTable == "owner" then + OldSortAuctionApplySort(SortTable, SortColumn, reverse) + else + QueryAuctionItems(searchname, searchminLevel, searchmaxLevel, searchinvTypeIndex, searchclassIndex, searchsubclassIndex, searchpage, searchisUsable, searchqualityIndex, searchGetAll) + end +end + +--Beginner Tooltips script display for all UI elements +function private.buttonTooltips(self, text) + if get("util.compactui.tooltiphelp") and text and self then + GameTooltip:SetOwner(self, "ANCHOR_BOTTOMRIGHT") + GameTooltip:SetText(text) + end +end + +function private.HookAH() + lib.inUse = true + private.switchUI:SetParent(AuctionFrameBrowse) + private.switchUI:SetPoint("TOPRIGHT", AuctionFrameBrowse, "TOPRIGHT", -157, -17) + + + if (not AucAdvanced.Settings.GetSetting("util.compactui.activated")) then + private.MyAuctionFrameUpdate = function() end + return + end + + AuctionFrameBrowse_Update = private.MyAuctionFrameUpdate + local button, lastButton, origButton + local line + + BrowseQualitySort:Hide() + BrowseLevelSort:Hide() + BrowseDurationSort:Hide() + BrowseHighBidderSort:Hide() + BrowseCurrentBidSort:Hide() + + local NEW_NUM_BROWSE = 14 + for i = 1, NEW_NUM_BROWSE do + if (i <= NUM_BROWSE_TO_DISPLAY) then + origButton = _G["BrowseButton"..i] + origButton:Hide() + _G["BrowseButton"..i] = nil + else + origButton = nil + end + button = CreateFrame("Button", "BrowseButton"..i, AuctionFrameBrowse) + button.Orig = origButton + button.pos = i + private.buttons[i] = button + if (i == 1) then + button:SetPoint("TOPLEFT", 188, -103) + else + button:SetPoint("TOPLEFT", lastButton, "BOTTOMLEFT") + end + button:SetWidth(610) + button:SetHeight(19) + button:EnableMouse(true) + button.LineTexture = button:CreateTexture() + button.LineTexture:SetPoint("TOPLEFT", 0,-1) + button.LineTexture:SetPoint("BOTTOMRIGHT") + button.AddTexture = button:CreateTexture() + button.AddTexture:SetPoint("TOPLEFT", 0,-1) + button.AddTexture:SetPoint("BOTTOMRIGHT") + + button.Count = button:CreateFontString(nil,nil,"GameFontHighlight") + button.Count:SetPoint("TOPLEFT", button, "TOPLEFT", 0, 0) + button.Count:SetWidth(28) + button.Count:SetHeight(19) + button.Count:SetJustifyH("RIGHT") + button.Count:SetFont(STANDARD_TEXT_FONT, 11) + button.IconButton = CreateFrame("Button", "AppraiserIconButton"..i, button) + button.IconButton:SetPoint("TOPLEFT", button, "TOPLEFT", 30, 0) + button.IconButton:SetWidth(16) + button.IconButton:SetHeight(19) + button.IconButton:SetScript("OnEnter", private.IconEnter) + button.IconButton:SetScript("OnLeave", private.IconLeave) + button.IconButton:SetFrameLevel(button.IconButton:GetFrameLevel() + 5) + button.Icon = button.IconButton:CreateTexture() + button.Icon:SetPoint("TOPLEFT", button.IconButton, "TOPLEFT", 0,-2) + button.Icon:SetPoint("BOTTOMRIGHT", button.IconButton, "BOTTOMRIGHT" , 0, 1) + button.Name = button:CreateFontString(nil,nil,"GameFontHighlight") + button.Name:SetPoint("TOPLEFT", button, "TOPLEFT", 50, 0) + button.Name:SetWidth(220) + button.Name:SetHeight(19) + button.Name:SetJustifyH("LEFT") + button.Name:SetFont(STANDARD_TEXT_FONT, 10) + button.rLevel = button:CreateFontString(nil,nil,"GameFontHighlight") + button.rLevel:SetPoint("TOPLEFT", button.Name, "TOPRIGHT", 2, 0) + button.rLevel:SetWidth(30) + button.rLevel:SetHeight(19) + button.rLevel:SetJustifyH("RIGHT") + button.rLevel:SetFont(STANDARD_TEXT_FONT, 11) + button.iLevel = button:CreateFontString(nil,nil,"GameFontHighlight") + button.iLevel:SetPoint("TOPLEFT", button.rLevel, "TOPRIGHT", 2, 0) + button.iLevel:SetWidth(30) + button.iLevel:SetHeight(19) + button.iLevel:SetJustifyH("RIGHT") + button.iLevel:SetFont(STANDARD_TEXT_FONT, 11) + button.tLeft = button:CreateFontString(nil,nil,"GameFontHighlight") + button.tLeft:SetPoint("TOPLEFT", button.iLevel, "TOPRIGHT", 2, 0) + button.tLeft:SetWidth(35) + button.tLeft:SetHeight(19) + button.tLeft:SetJustifyH("CENTER") + button.tLeft:SetFont(STANDARD_TEXT_FONT, 11) + button.Owner = button:CreateFontString(nil,nil,"GameFontHighlight") + button.Owner:SetPoint("TOPLEFT", button.tLeft, "TOPRIGHT", 2, 0) + button.Owner:SetWidth(80) + button.Owner:SetHeight(19) + button.Owner:SetJustifyH("LEFT") + button.Owner:SetFont(STANDARD_TEXT_FONT, 10) + button.Bid = AucAdvanced.CreateMoney(10,110) + button.Bid:SetParent(button) + button.Bid.SetMoney = private.SetMoney + button.Bid:SetPoint("TOPRIGHT", button.Owner, "TOPRIGHT", 112, 0) + -- button.Bid:SetFrameStrata("PARENT") + button.Bid:SetDrawLayer("OVERLAY") + button.Buy = AucAdvanced.CreateMoney(10,110) + button.Buy:SetParent(button) + button.Buy.SetMoney = private.SetMoney + button.Buy:SetColor(1,0.82,0) + button.Buy:SetPoint("TOPRIGHT", button.Bid, "BOTTOMRIGHT", 0, 1) + -- button.Buy:SetFrameStrata("PARENT") + button.Buy:SetDrawLayer("OVERLAY") + button.Value = button:CreateFontString(nil,nil,"GameFontHighlight") + button.Value:SetPoint("TOPLEFT", button.Bid, "TOPRIGHT", 2, 0) + button.Value:SetWidth(45) + button.Value:SetHeight(19) + button.Value:SetJustifyH("RIGHT") + button.Value:SetFont(STANDARD_TEXT_FONT, 11) + + button.SetAuction = private.SetAuction + button:SetScript("OnClick", private.ButtonClick) + + lastButton = button + end + NUM_BROWSE_TO_DISPLAY = NEW_NUM_BROWSE + + local function selectHeader(self, ...) + local id = self.id + private.headers.sort = 0 + private.headers.dir = 0 + private.headers.pos = 0 + for i=1, #private.headers do + local header = private.headers[i] + if i == id then + if header.dir ~= 0 then + header.dir = header.dir * -1 + if header.dir == header.defaultdir then + header.pos = header.pos + 1 + end + else + header.pos = 1 + header.dir = header.defaultdir + end + if header.dir > 0 then + header.Back:SetVertexColor(0.6,1,1, 1) + else + header.Back:SetVertexColor(1,0.6,1, 1) + end + if header.cycle then + local headPos = ((header.pos-1) % #header.cycle)+1 + header.Text:SetText(header.cycle[headPos]) + private.headers.pos = headPos + end + private.headers.sort = id + private.headers.dir = header.dir + else + header.dir = 0 + header.Back:SetVertexColor(1,1,1, 0.8) + header.Text:SetText(header.Text.default) + end + end + if SortAuctionSetSort then + local sort = private.headers.sort + local dir = private.headers.dir + local col = "" + if sort then + if sort == 1 then col = "quantity" -- Count + elseif sort == 2 then -- + local pos = private.headers.pos -- + if pos == 1 then col = "name" -- Name + elseif pos == 2 then col = "quality" -- Quality + end -- + elseif sort == 3 then col = "level" -- MinLevel + --elseif sort == 4 then col = 9 -- ItemLevel + elseif sort == 5 then col = "duration" -- TimeLeft + elseif sort == 6 then col = "seller" -- Owner + elseif sort == 7 then -- + local pos = private.headers.pos -- + if pos == 1 then col = "buyoutthenbid" -- Buy + elseif pos == 2 then col = "bid" -- Bid + --elseif pos == 3 then col = 18 -- BuyEach + --elseif pos == 4 then col = 17 -- BidEach + end -- + --elseif sort == 8 then col = 21 -- PriceLevel + end + end + if dir > 0 then + dir = false + else + dir = true + end + if (col ~= "") then + SortAuctionSetSort("list", col, dir) + local pagesize=GetNumAuctionItems("list") + if pagesize <= 50 then + SortAuctionApplySort("list") + elseif pagesize > 50 then + pagesize = 0 + end + end + end + private.MyAuctionFrameUpdate() + end + + + local function createHeader(id, dir, text, parentLeft, parentRight, lOfs, rOfs, cycle) + if not parentRight then parentRight = parentLeft end + + local header = CreateFrame("Button", nil, private.headers) + header:SetHighlightTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") + header:SetPoint("BOTTOMLEFT", parentLeft, "TOPLEFT", lOfs or 0, 2) + header:SetPoint("BOTTOMRIGHT", parentRight, "TOPRIGHT", rOfs or 0, 2) + header:SetHeight(16) + header.Text = header:CreateFontString(nil, "OVERLAY", "GameFontNormal") + header.Text:SetPoint("TOPLEFT", header, "TOPLEFT", 2, 0) + header.Text:SetPoint("BOTTOMRIGHT", header, "BOTTOMRIGHT") + header.Text:SetJustifyH("LEFT") + header.Text:SetJustifyV("CENTER") + header.Text:SetText(text) + header.Text.default = text + header.Back = header:CreateTexture(nil, "ARTWORK") + header.Back:SetPoint("TOPLEFT", header, "TOPLEFT") + header.Back:SetPoint("BOTTOMRIGHT", header, "BOTTOMRIGHT") + header.Back:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") + header.Back:SetTexCoord(0.1, 0.8, 0, 1) + header.Back:SetVertexColor(1,1,1, 0.8) + header:SetScript("OnClick", selectHeader) + header.defaultdir = dir + header.cycle = cycle + header.pos = 1 + header.dir = 0 + header.id = id + private.headers[id] = header + end + + private.headers = CreateFrame("Frame", nil, AuctionFrameBrowse) + table.insert(private.candy, private.headers) + + local bOne = private.buttons[1] + createHeader(1, 1, "#", bOne.Count) + createHeader(2, 1, "Auction Item", bOne.IconButton, bOne.Name, 0, 0, { "Name", "Quality" }) + createHeader(3, -1, "Min", bOne.rLevel) + createHeader(4, -1, "iLvl", bOne.iLevel) + createHeader(5, 1, "Left", bOne.tLeft) + createHeader(6, 1, "Owner", bOne.Owner) + createHeader(7, -1, "Price", bOne.Value, bOne.Bid, -110, 0, {"Buy total", "Bid total", "Buy each", "Bid each"}) + createHeader(8, 1, "Pct", bOne.Value, nil, 0, -2) + + local tex + tex = AuctionFrameBrowse:CreateTexture() + tex:SetTexture(1,1,1, 0.05) + tex:SetPoint("TOPLEFT", private.buttons[1].rLevel, "TOPLEFT") + tex:SetPoint("BOTTOMRIGHT", private.buttons[NEW_NUM_BROWSE].rLevel, "BOTTOMRIGHT") + table.insert(private.candy, tex) + + tex = AuctionFrameBrowse:CreateTexture() + tex:SetTexture(1,1,1, 0.05) + tex:SetPoint("TOPLEFT", private.buttons[1].tLeft, "TOPLEFT") + tex:SetPoint("BOTTOMRIGHT", private.buttons[NEW_NUM_BROWSE].tLeft, "BOTTOMRIGHT") + table.insert(private.candy, tex) + + tex = AuctionFrameBrowse:CreateTexture() + tex:SetTexture(1,1,1, 0.05) + tex:SetPoint("TOPLEFT", private.buttons[1].Owner, "TOPRIGHT", 2, 0) + tex:SetPoint("BOTTOM", private.buttons[NEW_NUM_BROWSE].Buy, "BOTTOM", 0, 0) + tex:SetPoint("RIGHT", private.buttons[1].Bid, "RIGHT", -10, 0) + table.insert(private.candy, tex) + + tex = AuctionFrameBrowse:CreateTexture() + tex:SetTexture(1,1,0.5, 0.1) + tex:SetPoint("TOPLEFT", private.buttons[NEW_NUM_BROWSE].Count, "BOTTOMLEFT", 0, -1) + tex:SetWidth(610) + tex:SetHeight(38) + table.insert(private.candy, tex) + + BrowsePrevPageButton:ClearAllPoints() + BrowsePrevPageButton:SetPoint("BOTTOMRIGHT", tex, "BOTTOMRIGHT", -170, -5) + BrowseNextPageButton:ClearAllPoints() + BrowseNextPageButton:SetPoint("BOTTOMRIGHT", tex, "BOTTOMRIGHT", -5, -5) + BrowseSearchCountText:ClearAllPoints() + BrowseSearchCountText:SetPoint("BOTTOMRIGHT", tex, "BOTTOMRIGHT", -10, 27) + + local check = CreateFrame("CheckButton", nil, AuctionFrameBrowse, "OptionsCheckButtonTemplate") + private.PerItem = check + check:SetChecked(false) + check:SetPoint("TOPLEFT", tex, "TOPLEFT", 5, -5) + check:SetScript("OnClick", AuctionFrameBrowse_Update) + table.insert(private.candy, check) + + local text = AuctionFrameBrowse:CreateFontString(nil,nil,"GameFontNormal") + text:SetPoint("LEFT", check, "LEFT", 30, 0) + text:SetText("Show stacks as price per unit") + table.insert(private.candy, text) + + text = AuctionFrameBrowse:CreateFontString(nil,nil,"GameFontNormal") + private.PageNum = text + text:SetPoint("TOPLEFT", BrowsePrevPageButton, "TOPLEFT") + text:SetPoint("BOTTOMRIGHT", BrowseNextPageButton, "BOTTOMRIGHT") + text:SetFont(STANDARD_TEXT_FONT, 12) + text:SetShadowOffset(2,2) + table.insert(private.candy, text) + + private.Active = true + + -- Select our PCT column + selectHeader(private.headers[8]) +end + +function private.SetMoney(me, value, hasBid, highBidder) + value = tonumber (value) + if not value then me:Hide() return end + value = math.floor (value) + local r,g,b + if (hasBid == true) then r,g,b = 1,1,1 + elseif (hasBid == false) then r,g,b = 0.7,0.7,0.7 end + if (highBidder) then r,g,b = 0.4,1,0.2 end + me:SetValue(value, r,g,b) + me:Show() +end + +function private.ButtonClick(me, mouseButton) + if ( IsControlKeyDown() ) then + DressUpItemLink(GetAuctionItemLink("list", me.id)) + elseif ( IsShiftKeyDown() ) then + ChatEdit_InsertLink(GetAuctionItemLink("list", me.id)) + --Display the ignore player UI + elseif (IsAltKeyDown() ) and me.Owner:GetText() then + if not AucAdvanced.Modules.Filter.Basic or not AucAdvanced.Modules.Filter.Basic.IsPlayerIgnored then private.sellerIgnore:Hide() return end + + private.sellerIgnore:ClearAllPoints() private.sellerIgnore:SetPoint("TOPLEFT", me.Owner,"TOPRIGHT") private.sellerIgnore:Show() + --if toon not ignored the ignore + local seller = me.Owner:GetText() + if not AucAdvanced.Modules.Filter.Basic.IsPlayerIgnored(seller) then + private.sellerIgnore.yes:SetScript("OnClick", function() BF_IgnoreList_Add( seller ) private.sellerIgnore:Hide() end) + private.sellerIgnore.help:SetText("Add player to ignore list\n\n|CFFFFFFFF"..(seller)) + else + private.sellerIgnore.yes:SetScript("OnClick", function() BF_IgnoreList_Remove( seller ) private.sellerIgnore:Hide() end) + private.sellerIgnore.help:SetText("Remove player from ignore list\n\n|CFFFFFFFF"..(seller)) + end + else + if GetCVarBool("auctionDisplayOnCharacter") then + DressUpItemLink(GetAuctionItemLink("list", me.id)) + end + SetSelectedAuctionItem("list", me.id) + -- Close any auction related popups + CloseAuctionStaticPopups() + AuctionFrameBrowse_Update() + end +end + +function private.IconEnter(this) + local button = this:GetParent() + button.Icon:ClearAllPoints() + button.Icon:SetPoint("RIGHT", this, "LEFT") + button.Icon:SetWidth(64) + button.Icon:SetHeight(64) + AuctionFrameItem_OnEnter(this, "list", button.id) +end + +function private.IconLeave(this) + local button = this:GetParent() + button.Icon:ClearAllPoints() + button.Icon:SetPoint("TOPLEFT", this, "TOPLEFT") + button.Icon:SetWidth(16) + button.Icon:SetHeight(16) + GameTooltip:Hide() + ResetCursor() +end + +function private.BrowseSort(a, b) + local sort = private.headers.sort + local dir = private.headers.dir + + if sort then + if sort == 1 then col = 3 -- Count + elseif sort == 2 then -- + local pos = private.headers.pos -- + if pos == 1 then col = 6 -- Name + elseif pos == 2 then col = 5 -- Quality + end -- + elseif sort == 3 then col = 8 -- MinLevel + elseif sort == 4 then col = 9 -- ItemLevel + elseif sort == 5 then col = 10 -- TimeLeft + elseif sort == 6 then col = 11 -- Owner + elseif sort == 7 then -- + local pos = private.headers.pos -- + if pos == 1 then col = 16 -- Buy + elseif pos == 2 then col = 15 -- Bid + elseif pos == 3 then col = 18 -- BuyEach + elseif pos == 4 then col = 17 -- BidEach + end -- + elseif sort == 8 then col = 21 -- PriceLevel + end + end + + if a[col] ~= b[col] then + if dir > 0 then return (a[col] < b[col]) + else return (a[col] > b[col]) + end + end + if a[5] ~= b[5] then return a[5] < b[5] end + if a[6] ~= b[6] then return a[6] < b[6] end + if a[3] ~= b[3] then return a[3] < b[3] end +end + +private.pageContents = {} +private.pageElements = {} +function private.RetrievePage() + for i = 1, #private.pageContents do + private.pageContents[i] = nil + end + + local selected = GetSelectedAuctionItem("list") or 0 + local pagesize = GetNumAuctionItems("list") + if pagesize < 50 then + pagesize = 50 + elseif pagesize > 50 then --If doing a GetAll, don't show anything + pagesize = 0 + end + for i = 1, pagesize do + if not private.pageElements[i] then private.pageElements[i] = {} end + + local link = GetAuctionItemLink("list", i) + if link then + local item = private.pageElements[i] + + item[1] = i + if (selected == i) then + item[2] = true + else + item[2] = false + end + + local name, texture, count, quality, canUse, level, + minBid, minIncrement, buyoutPrice, bidAmount, + highBidder, owner = GetAuctionItemInfo("list", i) + local itemName, itemLink, itemRarity, itemLevel, + itemMinLevel, itemType, itemSubType, itemStackCount, + itemEquipLoc, itemTexture = GetItemInfo(link) + + if not itemLevel then itemLevel = level end + if not itemMinLevel then itemMinLevel = level end + local timeLeft = GetAuctionItemTimeLeft("list", i) + if (timeLeft == 4) then timeLeftText = "48h" + elseif (timeLeft == 3) then timeLeftText = "12h" + elseif (timeLeft == 2) then timeLeftText = "2h" + else timeLeftText = "30m" end + if (not count or count < 1) then count = 1 end + + local requiredBid + if bidAmount > 0 then + requiredBid = bidAmount + minIncrement + if buyoutPrice > 0 and requiredBid > buyoutPrice then + requiredBid = buyoutPrice + end + elseif minBid > 0 then + requiredBid = minBid + else + requiredBid = 1 + end + + if ( requiredBid >= MAXIMUM_BID_PRICE ) then + -- Lie about our buyout price + buyoutPrice = requiredBid + end + + local priceLevel, perItem, r,g,b + local cacheSig = strjoin(":", link, count, requiredBid, buyoutPrice) + if private.cache[cacheSig] then + priceLevel, perItem, r,g,b = unpack(private.cache[cacheSig]) + elseif AucAdvanced.Modules.Util.PriceLevel then + priceLevel, perItem, r,g,b = AucAdvanced.Modules.Util.PriceLevel.CalcLevel(link, count, requiredBid, buyoutPrice) + private.cache[cacheSig] = { priceLevel, perItem, r,g,b } + end + + item[3] = count + item[4] = texture + item[5] = itemRarity + item[6] = name + item[7] = link + item[8] = itemMinLevel + item[9] = itemLevel + item[10] = timeLeft + item[11] = owner or "" + item[12] = minBid + item[13] = bidAmount + item[14] = minIncrement + item[15] = requiredBid + item[16] = buyoutPrice + item[17] = requiredBid / count + item[18] = buyoutPrice / count + item[19] = timeLeftText + item[20] = highBidder + item[21] = priceLevel or 0 + item[22] = perItem + item[23] = r + item[24] = g + item[25] = b + + table.insert(private.pageContents, item) + end + end + + table.sort(private.pageContents, private.BrowseSort) +end + +function lib.GetContents(pos) + if private.pageContents[pos] then + return unpack(private.pageContents[pos]) + end + -- id, selected, count, texture, itemRarity, name, link, itemMinLevel, itemLevel, timeLeft, owner, minBid, bidAmount, minIncrement, requiredBid, buyoutPrice, requiredBidEach, buyoutPriceEach, timeLeftText, highBidder, priceLevel, perItem, r, g, b +end + +function private.SetAuction(button, pos) + local id, selected, count, texture, itemRarity, name, link, itemMinLevel, itemLevel, timeLeft, owner, minBid, bidAmount, minIncrement, requiredBid, buyoutPrice, requiredBidEach, buyoutPriceEach, timeLeftText, highBidder, priceLevel, perItem, r, g, b = lib.GetContents(pos) + + if not id then + button:Hide() + return + end + + if (selected) then + button.LineTexture:SetTexture(1,1,0.3, 0.2) + elseif (pos % 2 == 0) then + button.LineTexture:SetTexture(0.3,0.3,0.4, 0.1) + else + button.LineTexture:SetTexture(0,0,0.1, 0.1) + end + button.id = id + + local showBid + if (AucAdvanced.Settings.GetSetting("util.compactui.bidrequired")) then + showBid = requiredBid + else + showBid = max (bidAmount, minBid) + end + + if (selected) then + if (buyoutPrice > 0 and buyoutPrice >= minBid) then + local canBuyout = 1 + if (GetMoney() < buyoutPrice) then + if (not highBidder or GetMoney()+bidAmount < buyoutPrice) then + canBuyout = nil + end + end + if (canBuyout) then + BrowseBuyoutButton:Enable() + AuctionFrame.buyoutPrice = buyoutPrice + end + else + AuctionFrame.buyoutPrice = nil + end + -- Set bid + MoneyInputFrame_SetCopper(BrowseBidPrice, requiredBid) + + -- See if the user can bid on this + if (not highBidder and owner ~= UnitName("player")) then + if (GetMoney() >= MoneyInputFrame_GetCopper(BrowseBidPrice)) then + if (MoneyInputFrame_GetCopper(BrowseBidPrice) <= MAXIMUM_BID_PRICE) then + BrowseBidButton:Enable() + end + end + end + end + --if player is ignored then color name red otherwise set normal + if owner and AucAdvanced.Modules.Filter.Basic and AucAdvanced.Modules.Filter.Basic.IsPlayerIgnored and AucAdvanced.Modules.Filter.Basic.IsPlayerIgnored(owner) then + button.Owner:SetTextColor(1,0,0) + else + button.Owner:SetTextColor(1,1,1) + end + + local perUnit = 1 + if (private.PerItem:GetChecked()) then + perUnit = count + end + + if itemLevel == 0 then itemLevel = "" end + if itemMinLevel == 0 then itemMinLevel = "" end + + button.Count:SetText(count) + button.Icon:SetTexture(texture) + button.Name:SetText(link) + button.rLevel:SetText(itemMinLevel) + button.iLevel:SetText(itemLevel) + button.tLeft:SetText(timeLeftText) + button.Owner:SetText(owner) + button.Bid:SetMoney(showBid/perUnit, (bidAmount > 0), highBidder) + button.Buy:SetMoney((buyoutPrice > 0) and buyoutPrice/perUnit) + button:Show() +end + +function private.MyAuctionFrameUpdate() + if not BrowseScrollFrame then return end + + if WOWEcon_AH_PerItem_Enable + and WOWEcon_AH_PerItem_Enable:IsVisible() then + WOWEcon_AH_PerItem_Enable:Hide() + end + + if AucAdvanced.API.IsBlocked() then + for pos, candy in ipairs(private.candy) do candy:Hide() end + BrowsePrevPageButton:Hide() + BrowseNextPageButton:Hide() + BrowseSearchCountText:Hide() + return + end + + local numBatchAuctions, totalAuctions = GetNumAuctionItems("list") + local offset = FauxScrollFrame_GetOffset(BrowseScrollFrame) + local index, button + BrowseBidButton:Disable() + BrowseBuyoutButton:Disable() + if (numBatchAuctions > 50) then + numBatchAuctions = 0 + totalAuctions = 0 + end + + if ( numBatchAuctions == 0 ) then + BrowseNoResultsText:Show() + else + BrowseNoResultsText:Hide() + end + + private.RetrievePage() + local pagesize = GetNumAuctionItems("list") + if pagesize < 50 then + pagesize = 50 + elseif pagesize > 50 then + pagesize = 0 + end + for i=1, NUM_BROWSE_TO_DISPLAY do + index = offset + i + (pagesize * AuctionFrameBrowse.page) + button = private.buttons[i] + if ( index > (numBatchAuctions + (pagesize * AuctionFrameBrowse.page)) ) then + button:SetAuction() + -- If the last button is empty then set isLastSlotEmpty var + if ( i == NUM_BROWSE_TO_DISPLAY ) then + isLastSlotEmpty = 1 + end + else + button:SetAuction(offset+i) + end + end + + if (totalAuctions > 0) then + for pos, candy in ipairs(private.candy) do candy:Show() end + BrowsePrevPageButton:Show() + BrowseNextPageButton:Show() + BrowseSearchCountText:Show() + local itemsMin = AuctionFrameBrowse.page * pagesize + 1 + local itemsMax = itemsMin + numBatchAuctions - 1 + BrowseSearchCountText:SetText(format(NUMBER_OF_RESULTS_TEMPLATE, itemsMin, itemsMax, totalAuctions )) + if ( isLastSlotEmpty ) then + if ( AuctionFrameBrowse.page == 0 ) then + BrowsePrevPageButton.isEnabled = nil + else + BrowsePrevPageButton.isEnabled = 1 + end + if ( AuctionFrameBrowse.page == (ceil(totalAuctions/pagesize) - 1) ) then + BrowseNextPageButton.isEnabled = nil + else + BrowseNextPageButton.isEnabled = 1 + end + else + BrowsePrevPageButton.isEnabled = nil + BrowseNextPageButton.isEnabled = nil + end + else + for pos, candy in ipairs(private.candy) do candy:Hide() end + BrowsePrevPageButton:Hide() + BrowseNextPageButton:Hide() + BrowseSearchCountText:Hide() + end + + private.PageNum:SetText(("%d/%d"):format(AuctionFrameBrowse.page+1, ceil(totalAuctions/pagesize))) + FauxScrollFrame_Update(BrowseScrollFrame, numBatchAuctions, NUM_BROWSE_TO_DISPLAY, AUCTIONS_BUTTON_HEIGHT) + BrowseScrollFrame:Show() + AucAdvanced.API.ListUpdate() +end + +--create the configure UI button. +private.switchUI = CreateFrame("Button", nil, UIParent, "OptionsButtonTemplate") +private.switchUI:SetWidth(100) +private.switchUI:SetHeight(15) +private.switchUI:SetText("Configure") +private.switchUI:SetScript("OnClick", function() + if private.gui and private.gui:IsShown() then + AucAdvanced.Settings.Hide() + else + AucAdvanced.Settings.Show() + private.gui:ActivateTab(private.guiID) + end +end) +private.switchUI:SetScript("OnEnter", function() private.buttonTooltips(private.switchUI, "Open the configuration options for the CompactUI window.") end) +private.switchUI:SetScript("OnLeave", function() GameTooltip:Hide() end) + +--ignore/unignore seller GUI +private.sellerIgnore = CreateFrame("Frame", nil, UiParent) +private.sellerIgnore:Hide() +private.sellerIgnore:SetBackdrop({ + bgFile = "Interface/Tooltips/ChatBubble-Background", + edgeFile = "Interface/Minimap/TooltipBackdrop", + tile = true, tileSize = 32, edgeSize = 10, + insets = { left = 2, right = 2, top = 2, bottom = 2 } +}) +private.sellerIgnore:SetBackdropColor(0,0,0, 1) +private.sellerIgnore:SetWidth(100) +private.sellerIgnore:SetHeight(70) +private.sellerIgnore:SetPoint("CENTER", UIParent, "CENTER") +private.sellerIgnore:SetFrameStrata("TOOLTIP") +private.sellerIgnore:SetScale(0.7) + +private.sellerIgnore.help = private.sellerIgnore:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall" ) +private.sellerIgnore.help:SetParent(private.sellerIgnore) +private.sellerIgnore.help:SetPoint("CENTER", private.sellerIgnore, "TOP", 0, -25) +private.sellerIgnore.help:SetWidth(100) + +private.sellerIgnore.yes = CreateFrame("Button", nil, private.sellerIgnore, "UIPanelButtonTemplate") +private.sellerIgnore.yes:SetNormalFontObject(GameFontNormalSmall) +private.sellerIgnore.yes:SetPoint("BOTTOMLEFT", private.sellerIgnore, "BOTTOMLEFT", 5, 10) +private.sellerIgnore.yes:SetScript("OnClick", function() BF_IgnoreList_Add( name ) end) +private.sellerIgnore.yes:SetText("Yes") +private.sellerIgnore.yes:SetWidth(30) +private.sellerIgnore.yes:SetHeight(10) +local font = private.sellerIgnore.yes:GetFontString() +font:SetFontObject("GameFontNormalSmall" ) +font:SetTextHeight(10) + +private.sellerIgnore.no = CreateFrame("Button", nil, private.sellerIgnore, "UIPanelButtonTemplate") +private.sellerIgnore.no:SetNormalFontObject(GameFontNormalSmall) +private.sellerIgnore.no:SetPoint("BOTTOMRIGHT", private.sellerIgnore, "BOTTOMRIGHT", -5, 10) +private.sellerIgnore.no:SetScript("OnClick", function() private.sellerIgnore:Hide() end) +private.sellerIgnore.no:SetText("No") +private.sellerIgnore.no:SetWidth(30) +private.sellerIgnore.no:SetHeight(10) +local font = private.sellerIgnore.no:GetFontString() +font:SetFontObject("GameFontNormalSmall" ) +font:SetTextHeight(10) + +function private.SetupConfigGui(gui) + -- The defaults for the following settings are set in the lib.OnLoad function + local id = gui:AddTab(libName, libType.." Modules") + private.gui = gui --stores our ID id we use this to open the config button to correct frame + private.guiID = id + + gui:AddHelp(id, "what compactui", + _TRANS('COMP_Help_WhatCompactUI'), -- "What is CompactUI?" + _TRANS('COMP_Help_WhatCompactUIAnswer')) --"CompactUI is a space optimized browse interface to replace the default Blizzard auction browse interface. \nDue to the fact that it heavily modifies your auction frames, it may cause it to be incompatible with other addons that also modify the auction frames. If this is the case you will have to make a choice between CompactUI and the other addons. You may disable CompactUI easily by unticking the \"Enable use of CompactUI (requires logout)\" option in the settings window." + + gui:AddControl(id, "Header", 0, libName.." options") + gui:AddControl(id, "Checkbox", 0, 1, "util.compactui.activated", _TRANS('COMP_Interface_CompActivated')) --"Enable use of CompactUI (requires logout)" + gui:AddTip(id, _TRANS('COMP_HelpTooltip_CompActivated')) --"Ticking this box will enable CompactUI to take over your auction browse window after your next reload.") + gui:AddControl(id, "Note", 0, 2, 600, 70, _TRANS('COMP_Interface_CompWarning')) --"Note: This module heavily modifies your standard auction browser window, and may not play well with other auction house addons. Should you enable this module and notice any incompatabilities, please turn this module off again by unticking the above box and reloading your interface." + + gui:AddControl(id, "Checkbox", 0, 1, "util.compactui.tooltiphelp", _TRANS('COMP_Interface_CompTooltipHelp')) --"Displays the pop up help tooltips" + gui:AddTip(id, _TRANS('COMP_HelpTooltip_CompTooltipHelp')) --"This option will display popup help tooltips on the CompactUI display" + gui:AddControl(id, "Checkbox", 0, 1, "util.compactui.collapse", _TRANS('COMP_Interface_CompCollapse')) --"Remove smaller denomination coins when it's zero" + gui:AddTip(id, _TRANS('COMP_HelpTooltip_CompCollapse')) --"This option will cause lower value coins to be hidden when the hiding would not change the value of the displayed price." + gui:AddControl(id, "Checkbox", 0, 1, "util.compactui.bidrequired", _TRANS('COMP_Interface_CompBidRequired')) --"Show the required bid instead of the current bid value" + gui:AddTip(id, _TRANS('COMP_HelpTooltip_CompBidRequired')) --"Toggling this option changes CompactUI's behaviour to show the required bid." + gui:AddControl(id, "Checkbox", 0, 1, "util.browseoverride.activated", _TRANS('COMP_Interface_BrowserOverRide')) --"Prevent other modules from changing the display of the browse tab while scanning" + gui:AddTip(id, _TRANS('COMP_HelpTooltip_BrowseOverRide')) --"Enabling this option will allow CompactUI to continue displaying the auction data, even when another module is installed to hide the display of auctions while scanning." + + gui:AddHelp(id, "what is popup", + _TRANS('COMP_Help_WhatPopup'), --"What does enabling the popup help do?" + _TRANS('COMP_Help_WhatPopupAnswer')) --"Displays a little popup tooltip over various parts of the CompactUI." + + gui:AddHelp(id, "what is collapse", + _TRANS('COMP_Help_WhatCollapse'), --"What does removing smaller denomination coins do?", + _TRANS('COMP_Help_WhatCollapseAnswer')) --"Removing smaller denomination coins removes coins from the lowest order when the coins are zero and their removal would not affect the accuracy of the price display. \nThis has the effect of shortening the length of the displayed prices, however it can cause additional confusion over similar length prices that are of a different order (ie: 1s 55c looks like 1g 55s) and if you're not careful, you may spend more than you meant to." + + gui:AddHelp(id, "what is required", + _TRANS('COMP_Help_WhatRequied'), --"What is the required bid?", + _TRANS('COMP_Help_WhatRequiedAnswered')) --"The stock standard interface shows you the current bid. In the case where the current bid is not the minimum (or starting) bid, your actual amount when you bid on this item will be the current bid, plus the minimum increment. Therefore what you pay will be more than what is displayed in the window when you are not the first bidder. By ticking this option, you change CompactUI's behaviour to instead show the required bid that you will need to bid, so that no matter if there is a bid or not, what you see is what you will pay." + + gui:AddHelp(id, "what is browseoverride", + _TRANS('COMP_Help_WhatBrowseOverride'), --"Why do I want to prevent other modules from changing the Browse window?" + _TRANS('COMP_Help_WhatBrowseOverrideAnswer')) --"If you have a module such as ScanProgress installed and activated, it will change the browse interface so that you cannot see the items as you are scanning. This option will revert the display so that you can see the items while scanning instead." + + gui:AddHelp(id, "what is playerignore", + _TRANS('COMP_Help_HowToIgnore'), --"How do I ignore a seller's auctions?" + _TRANS('COMP_Help_HowToIgnoreAnswer')) --"ALT click on the seller you wish to ignore and select yes in the pop up window. The seller's name will be marked in red and placed in the BASIC FILTER module's ignored list." + gui:AddHelp(id, "what is playerunignore", + _TRANS('COMP_Help_HowToUnignore'), --"How do I un-ignore a seller's auctions?" + _TRANS('COMP_Help_HowToUnignoreAnswer')) --"ALT click on the seller you wish to remove from ignore and select yes in the pop up window. The seller's name will be removed from the BASIC FILTER module's ignored list." + +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-CompactUI/CompactUI.lua $", "$Rev: 4933 $") diff --git a/Auc-Advanced/Modules/Auc-Util-CompactUI/Embed.xml b/Auc-Advanced/Modules/Auc-Util-CompactUI/Embed.xml new file mode 100644 index 0000000..e756b13 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-CompactUI/Embed.xml @@ -0,0 +1,7 @@ + + + + diff --git a/Auc-Advanced/Modules/Auc-Util-EasyBuyout/Auc-Util-EasyBuyout.toc b/Auc-Advanced/Modules/Auc-Util-EasyBuyout/Auc-Util-EasyBuyout.toc new file mode 100644 index 0000000..272e65c --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-EasyBuyout/Auc-Util-EasyBuyout.toc @@ -0,0 +1,14 @@ +## Title: Auc:Util:EasyBuyout +## Interface: 40000 +## Notes: Shift+right-click to easily buyout an auction with no confirmation box! +## Dependancies: Auc-Advanced +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Auc-Util-EasyBuyout.toc 4933 2010-10-13 17:16:14Z Nechckn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## X-Max-Interface: 40000 +## +Embed.xml +EasyBuyout.lua \ No newline at end of file diff --git a/Auc-Advanced/Modules/Auc-Util-EasyBuyout/EasyBuyout.lua b/Auc-Advanced/Modules/Auc-Util-EasyBuyout/EasyBuyout.lua new file mode 100644 index 0000000..fbedfac --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-EasyBuyout/EasyBuyout.lua @@ -0,0 +1,433 @@ +--[[ + Auctioneer - EasyBuyout Utility Module + Version: 1.2.5 (GhostfromTexas) + Revision: $Id: EasyBuyout.lua 4901 2010-10-05 16:44:24Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This Auctioneer module allows for the ability to purchase items from + the Browse tab with a single click. It also allows for canceling your + own auctions from the Auctions tab with a single click. + + 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 libType, libName = "Util", "EasyBuyout" +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end +local print,decode,_,_,replicate,empty,get,set,default,debugPrint,fill = AucAdvanced.GetModuleLocals() +local addShift; +local CompactUImode = false +local orig_AB_OC; +local ebModifier = false + +function lib.GetName() + return libName +end + +function lib.Processor(callbackType, ...) + if (callbackType == "listupdate") then + private.EasyCancelMain() + private.EasyBuyout() + elseif (callbackType == "auctionui") then + private.AHLoaded() + private.EasyBuyout() + private.EasyCancelMain() + elseif (callbackType == "config") then + private.SetupConfigGui(...) + elseif (callbackType == "configchanged") then + private.EasyBuyout() + private.EasyCancelMain() + end +end + +lib.Processors = {} +function lib.Processors.listupdate(callbackType, ...) + private.EasyCancelMain() + private.EasyBuyout() +end + +function lib.Processors.auctionui(callbackType, ...) + private.AHLoaded() + private.EasyBuyout() + private.EasyCancelMain() +end + +function lib.Processors.config(callbackType, ...) + private.SetupConfigGui(...) +end + +function lib.Processors.configchanged(callbackType, ...) + private.EasyBuyout() + private.EasyCancelMain() +end + +function lib.OnLoad() + print("AucAdvanced: {{"..libType..":"..libName.."}} loaded!") + + -- Silent Mode Option + AucAdvanced.Settings.SetDefault("util.EasyBuyout.silentmode", false); + + -- EasyBuyout Default Settings + AucAdvanced.Settings.SetDefault("util.EasyBuyout.active", false) + AucAdvanced.Settings.SetDefault("util.EasyBuyout.modifier.active", true) + AucAdvanced.Settings.SetDefault("util.EasyBuyout.modifier.select", 0) + + -- EasyCancel Default Settings + AucAdvanced.Settings.SetDefault("util.EasyBuyout.EC.active", false) + AucAdvanced.Settings.SetDefault("util.EasyBuyout.EC.modifier.active", true) + AucAdvanced.Settings.SetDefault("util.EasyBuyout.EC.modifier.select", 0) + + -- EasyBid Default Settings + AucAdvanced.Settings.SetDefault("util.EasyBuyout.EBid.active", false) + + -- EasyGoldLimit Default Settings + AucAdvanced.Settings.SetDefault("util.EasyBuyout.EGL.EBuy.active", true) + AucAdvanced.Settings.SetDefault("util.EasyBuyout.EGL.EBuy.limit", 100000) + AucAdvanced.Settings.SetDefault("util.EasyBuyout.EGL.EBid.active", true) + AucAdvanced.Settings.SetDefault("util.EasyBuyout.EGL.EBid.limit", 50000) + +end + +--[[ Local functions ]]-- +function private.AHLoaded() + if AucAdvanced.Modules.Util.CompactUI and AucAdvanced.Modules.Util.CompactUI.Private.ButtonClick and get("util.compactui.activated") then + orig_AB_OC = AucAdvanced.Modules.Util.CompactUI.Private.ButtonClick + AucAdvanced.Modules.Util.CompactUI.Private.ButtonClick = private.BrowseButton_OnClick + CompactUImode = true + else + assert(BrowseButton_OnClick, "BrowseButton_OnClick doesn't exist yet") + orig_AB_OC = function() end -- fake function or it will error when you do return orig_AB_OC(...) since this is not needed or created with the method we used + CompactUImode = false + --go though the AH buttons and hook each ones script - Added by Kandoko on May 14, 2010 to fix AUEB-17 + for i = 1, 8 do + local button = _G["BrowseButton"..i] --This is the same as the global variable named BrowseButton1, BrowseButton2 ..etc + button:HookScript("OnClick", function(self, button, ...) --HookScript is a secure script hook function provided by blizzard + private.BrowseButton_OnClick(self, button, ...) + end) + end + end +end + +function private.SetupConfigGui(gui) + -- The defaults for the following settings are set in the lib.OnLoad function + local id = gui:AddTab(libName) + gui:MakeScrollable(id) + + -- EasyBuyout + gui:AddControl(id, "Header", 0, "EasyBuyout Options") + gui:AddControl(id, "Subhead", 0, "Simply right-click an auction to buy it out with no confirmation box.") + gui:AddControl(id, "Checkbox", 0, 1, "util.EasyBuyout.active", "Enable EasyBuyout") + gui:AddTip(id, "Ticking this box will enable or disable EasyBuyout") + gui:AddControl(id, "Checkbox", 0, 1, "util.EasyBuyout.modifier.active", "Enable key modifier") + gui:AddTip (id, "Ticking this box will add a key modifier to EasyBuyout") + gui:AddControl(id, "Selectbox", 0, 1, { + {0, "Shift"}, + {1, "Alt"}, + {2, "Shift+Alt"} + }, "util.EasyBuyout.modifier.select", "testing here") + gui:AddTip(id, "Select your key modifier for EasyBuyout") + + -- EasyCancel + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + gui:AddControl(id, "Header", 0, "EasyCancel Options") + gui:AddControl(id, "Subhead", 0, "Simply right-click an auction to cancel it out with no confirmation box.") + gui:AddControl(id, "Checkbox", 0, 1, "util.EasyBuyout.EC.active", "Enable EasyCancel") + gui:AddTip(id, "Ticking this box will enable or disable EasyCancel") + gui:AddControl(id, "Checkbox", 0, 1, "util.EasyBuyout.EC.modifier.active", "Enable key modifier") + gui:AddTip (id, "Ticking this box will add a key modifier to EasyCancel") + gui:AddControl(id, "Selectbox", 0, 1, { + {0, "Shift"}, + {1, "Alt"}, + {2, "Shift+Alt"} + }, "util.EasyBuyout.EC.modifier.select", "testing here") + gui:AddTip(id, "Select your key modifier for EasyCancel") + + -- EasyBid + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + gui:AddControl(id, "Header", 0, "EasyBid Options") + gui:AddControl(id, "Subhead", 0, "Simply double-click an auction to bid minimum on it.") + gui:AddControl(id, "Checkbox", 0, 1, "util.EasyBuyout.EBid.active", "Enable EasyBid") + gui:AddTip(id, "Toggle this box to enable or disable EasyBid") + + -- EasyGoldLimit + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + gui:AddControl(id, "Header", 0, "EasyGoldLimit Options") + gui:AddControl(id, "Subhead", 0, "Set a limit on EasyBid & EasyBuyout to prevent accidental purchases of expensive auctions.") + gui:AddControl(id, "Checkbox", 0,1, "util.EasyBuyout.EGL.EBuy.active", "Enable EasyGoldLimit for EasyBuyout") + gui:AddTip(id, "Ticking this box will enable or disable EasyGoldLimit for EasyBuyout") + gui:AddControl(id, "MoneyFramePinned", 0, 1, "util.EasyBuyout.EGL.EBuy.limit", 0, 999999999, "Set EasyBuyout Limit") + gui:AddTip(id, "Use this box to set the max amount of gold you want to spend when using EasyBuyout") + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + gui:AddControl(id, "Checkbox", 0,1, "util.EasyBuyout.EGL.EBid.active", "Enable EasyGoldLimit for EasyBid") + gui:AddTip(id, "Ticking this box will enable or disable EasyGoldLimit for EasyBid") + gui:AddControl(id, "MoneyFramePinned", 0, 1, "util.EasyBuyout.EGL.EBid.limit", 0, 999999999, "Set EasyBid Limit") + gui:AddTip(id, "Use this box to set the max amount of gold you want to spend when using EasyBid") + + -- Silent Mode + gui:AddControl(id, "Header", 0, "Other Options") + gui:AddControl(id, "Subhead", 0, "This section lists other options for this module.") + gui:AddControl(id, "Checkbox", 0, 1, "util.EasyBuyout.silentmode", "Enable Silent Mode"); + + -- help sections + gui:AddHelp(id, "What is EasyBuyout?", + "What is EasyBuyout?", + "EasyBuyout makes it easier to buy auctions in mass, faster! You simply right-click (or 'modifier'+right-click depending on your options) to buyout an auction with no confirmation box") + + gui:AddHelp(id, "What is EasyCancel?", + "What is EasyCancel?", + "Take what EasyBuyout does and apply it to canceling auctions! All you do is right-click (or 'modifier'+right-click) to cancel an auction you have posted up with no conformation box") + + gui:AddHelp(id, "What is EasyBid?", + "What is EasyBid?", + "This part of the EasyBuyout utility does what the name implies, it allows you to double click (or 'modifier'+double-click) to bid minimal on an auction! !!NOTE!! EasyBid can not use key modifiers because of the use of \"left-click\". It conflicts with other parts of Auctioneer.") + + gui:AddHelp(id, "What is EasyGoldLimit?", + "What is EasyGoldLimit", + "This does exactly what the name implies, it places a limit on the amount of gold that will be allowed to be used when bidding or buying an auction. It helps prevent spending more than intended on an auction.") + + gui:AddHelp(id, "What is Silent Mode?", + "What is Silent Mode?", + "Enabling Silent Mode will disable all text output to the chat frame from this module, whether it's apart of EasyBuyout, EasyBid, EasyCancel, or EasyGoldLimit") + +end + +function private.BrowseButton_OnClick(...) + local self, arg1 = ... + -- check for EB enabled + if not get("util.EasyBuyout.active") then + return orig_AB_OC(...) + end + + -- check and assign modifier + if get("util.EasyBuyout.modifier.active") then + local selection = get("util.EasyBuyout.modifier.select"); + + if (selection == 0) and IsShiftKeyDown() then + ebModifier = true; + elseif (selection == 1) and IsAltKeyDown() then + ebModifier = true; + elseif (selection == 2) and IsShiftKeyDown() and IsAltKeyDown() then + ebModifier = true; + else + if(arg1 == "RightButton") then -- only warn of modifier key if the right mouse button is set + private.EBMessage("|cffff5511EasyBuyout - Modifier Key " .. private.EBConvertModifierToText(selection) .. " is set, but not pressed!"); + end + + return orig_AB_OC(...) + end + end + + if (arg1 == "RightButton") then + local button = self + + local id + if CompactUImode then + id = button.id + else + id = button:GetID() + FauxScrollFrame_GetOffset(BrowseScrollFrame) + end + local link = GetAuctionItemLink("list", id) + if link then + local _,_,count = GetAuctionItemInfo("list", id) + private.EBMessage("Rightclick - buying auction of " .. (count or "?") .. "x" .. link) + else + private.EBMessage("Rightclick - not finding anything to buy. If you are mass clicking - try going from the bottom up!") + return + end + SetSelectedAuctionItem("list", id); + private.EasyBuyoutAuction(); + else + return orig_AB_OC(...) + end +end + +function private.EasyBuyout() + if not AuctionFrame then return end + + if get("util.EasyBuyout.shift.active") then + addShift = true; + else + addShift = false; + end + + if (get("util.EasyBuyout.active") or get("util.EasyBuyout.EBid.active")) then + for i=1,50 do + local button = _G["BrowseButton"..i] + if not button then break end + button:RegisterForClicks("LeftButtonUp", "RightButtonUp") + _G["BrowseButton"..i]:RegisterForClicks("LeftButtonUp", "RightButtonUp") + button:SetScript("ondoubleclick", private.NewOnDoubleClick) + end + else + return; + end +end + + +function private.EasyBuyoutAuction() + local EasyBuyoutIndex = GetSelectedAuctionItem("list"); + local EasyBuyoutPrice = select(9, GetAuctionItemInfo("list", EasyBuyoutIndex)) + + -- Easy Gold Limit for EasyBuyout + if get("util.EasyBuyout.EGL.EBuy.active") then + if EasyBuyoutPrice > get("util.EasyBuyout.EGL.EBuy.limit") then + private.EBMessage("|cffCC1100EasyGoldLimit - Auction is over your set limit for EasyBuyout!") + return; + end + end + + -- ready to buy auction + PlaceAuctionBid("list", EasyBuyoutIndex, EasyBuyoutPrice) + CloseAuctionStaticPopups(); +end + + +--[[ EasyCancel Function - Easy Auction Cancel is a lot simpler to incorporate everything in it's own section + rather than incorporating it into everything else coded before this comment. EasyCancel does not need to be + tested for compactUI like EasyBuyout does +--]] + +local function OrigAuctionOnClick(self, ...) + if ( IsModifiedClick() ) then + HandleModifiedItemClick(GetAuctionItemLink("owner", self:GetParent():GetID() + FauxScrollFrame_GetOffset(AuctionsScrollFrame))); + else + AuctionsButton_OnClick(self, ...); + end +end +-- handler for modifiers +local function NewOnClick(self, button) -- used for EasyCancel + local active = get("util.EasyBuyout.EC.active") + local modified = get("util.EasyBuyout.EC.modifier.active") + local modselect = get("util.EasyBuyout.EC.modifier.select") + + if active and button == "RightButton" and + ( + (not modified) + or (modified and modselect == 0 and IsShiftKeyDown()) + or (modified and modselect == 1 and IsAltKeyDown()) + or (modified and modselect == 2 and IsAltKeyDown() and IsShiftKeyDown()) + ) then + private.EasyCancel(self, button) + else + if active and button == "RightButton" then + private.EBMessage("|cffff5511EasyCancel - Modifier Key " .. private.EBConvertModifierToText(modselect) .. " is set, but not pressed!"); + end + OrigAuctionOnClick(self, button) + end + +end + +function private.EasyCancelMain() + + for i=1,199 do + local button = _G["AuctionsButton"..i] + if not button then break end + button:RegisterForClicks("LeftButtonUp", "RightButtonUp") + _G["AuctionsButton"..i.."Item"]:RegisterForClicks("LeftButtonUp", "RightButtonUp") + button:SetScript("onclick", NewOnClick) + end +end + +function private.EasyCancel(self, button) + + local link = GetAuctionItemLink("owner", self:GetID() + FauxScrollFrame_GetOffset(AuctionsScrollFrame)) + if link then + local _,_,count = GetAuctionItemInfo("owner", self:GetID() + FauxScrollFrame_GetOffset(AuctionsScrollFrame)) + private.EBMessage("Rightclick - cancelling auction of " .. (count or "?") .. "x" .. link) + else + return private.EBMessage("Rightclick - not finding anything to cancel. If you are mass clicking - try going from the bottom up!") + end + + SetSelectedAuctionItem("owner", self:GetID() + FauxScrollFrame_GetOffset(AuctionsScrollFrame)); + CancelAuction(GetSelectedAuctionItem("owner")) + CloseAuctionStaticPopups(); + AuctionFrameBid_Update(); +end + +-- EasyBid Function - This section listed below is for EasyBid: the utility that allows users to easily bid on an item simply by double clicking on it. +function private.NewOnDoubleClick(self, button) + -- check for EBid enabled + if not get("util.EasyBuyout.EBid.active") then + return + end + + local id + if CompactUImode then + id = self.id + else + id = self:GetID() + FauxScrollFrame_GetOffset(BrowseScrollFrame) + end + local link = GetAuctionItemLink("list", id) + if button == 'LeftButton' then + if (select(11, GetAuctionItemInfo("list", id))) then + private.EBMessage("You are already the highest bidder on this item!") + return + end + if link then + local _,_,count = GetAuctionItemInfo("list", id) + private.EBMessage("Doubleclick - bidding on auction of " .. (count or "?") .. "x" .. link) + else + private.EBMessage("Doubleclick - not finding anything to bid on. If you are mass clicking - try going from the bottom up!") + return + end + SetSelectedAuctionItem("list", self:GetID() + FauxScrollFrame_GetOffset(BrowseScrollFrame)); + private.EasyBidAuction(id); + CloseAuctionStaticPopups(); + end +end + +-- Function to place a bid on a specific auction using EasyBid +function private.EasyBidAuction(getID) + local EasyBidPrice = select(10, GetAuctionItemInfo("list", getID)) + select(8, GetAuctionItemInfo("list", getID)) + if EasyBidPrice == 0 then + EasyBidPrice = EasyBidPrice + select(7, GetAuctionItemInfo("list", getID)) + end + + -- Easy Gold Limit for EasyBid + if get("util.EasyBuyout.EGL.EBid.active") then + if EasyBidPrice > get("util.EasyBuyout.EGL.EBid.limit") then + private.EBMessage("|cffCC1100EasyGoldLimit - Auction is over your set limit for EasyBid!") + return; + end + end + + PlaceAuctionBid("list", getID, EasyBidPrice) + CloseAuctionStaticPopups(); +end + +-- function added in AUEB-18 to convert a selction to text for chatframe output +function private.EBConvertModifierToText(selection) + if selection == 0 then return "" end + if selection == 1 then return "" end + if selection == 2 then return "" end +end + +-- function added in AUEB-19 - Central location, specifically designed to handle the silent mode option +function private.EBMessage(messageString) + if get("util.EasyBuyout.silentmode") then return end + -- else + print(messageString) +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-EasyBuyout/EasyBuyout.lua $", "$Rev: 4901 $") diff --git a/Auc-Advanced/Modules/Auc-Util-EasyBuyout/Embed.xml b/Auc-Advanced/Modules/Auc-Util-EasyBuyout/Embed.xml new file mode 100644 index 0000000..a73396f --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-EasyBuyout/Embed.xml @@ -0,0 +1,7 @@ + + + + diff --git a/Auc-Advanced/Modules/Auc-Util-Example/Auc-Util-Example.toc b/Auc-Advanced/Modules/Auc-Util-Example/Auc-Util-Example.toc new file mode 100644 index 0000000..a8a12e3 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-Example/Auc-Util-Example.toc @@ -0,0 +1,11 @@ +## Title: Auctioneer:Util:Example +## Interface: 30300 +## Dependancies: Auc-Advanced +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Auc-Util-Example.toc 4566 2009-12-08 14:43:28Z Nechckn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## +Embed.xml diff --git a/Auc-Advanced/Modules/Auc-Util-Example/Embed.xml b/Auc-Advanced/Modules/Auc-Util-Example/Embed.xml new file mode 100644 index 0000000..120116a --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-Example/Embed.xml @@ -0,0 +1,7 @@ + + + + diff --git a/Auc-Advanced/Modules/Auc-Util-Example/Example.lua b/Auc-Advanced/Modules/Auc-Util-Example/Example.lua new file mode 100644 index 0000000..8689e74 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-Example/Example.lua @@ -0,0 +1,176 @@ +--[[ DELETE v DELETE v DELETE v DELETE v DELETE v DELETE v DELETE v DELETE -- + + NOTE: + This is an example addon. Use the below code to start your own + module should you wish. + + This top section should bel deleted from any derivative code + before you distribute it. + +]] + +if true then + --Comment out this return to see the example module running. + return +end + +--^ DELETE ^ DELETE ^ DELETE ^ DELETE ^ DELETE ^ DELETE ^ DELETE ^ DELETE ^-- + +--[[ + Auctioneer - Price Level Utility module + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: Example.lua 4840 2010-08-04 21:44:00Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This is an Auctioneer module that does something nifty. + + 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 libName = "Example" +local libType = "Util" + +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end +local print,decode,_,_,replicate,empty,get,set,default,debugPrint,fill = AucAdvanced.GetModuleLocals() + +--[[ +The following functions are part of the module's exposed methods: + GetName() (required) Should return this module's full name + CommandHandler() (optional) Slash command handler for this module + Processor() (optional) Processes messages sent by Auctioneer + ScanProcessor() (optional) Processes items from the scan manager +* GetPrice() (required) Returns estimated price for item link +* GetPriceColumns() (optional) Returns the column names for GetPrice + OnLoad() (optional) Receives load message for all modules + + (*) Only implemented in stats modules; util modules do not provide +]] + +function lib.GetName() + return libName +end + +function lib.Processor(callbackType, ...) + if (callbackType == "tooltip") then + --Called when the tooltip is being drawn. + lib.ProcessTooltip(...) + elseif (callbackType == "config") then + --Called when you should build your Configator tab. + private.SetupConfigGui(...) + elseif (callbackType == "listupdate") then + --Called when the AH Browse screen receives an update. + elseif (callbackType == "configchanged") then + --Called when your config options (if Configator) have been changed. + end +end +lib.Processors = {} +lib.Processors.tooltip = lib.Processor +lib.Processors.config = lib.Processor +lib.Processors.listupdate = lib.Processor +lib.Processors.configchanged = lib.Processor + + + +function lib.ProcessTooltip(frame, name, hyperlink, quality, quantity, cost, additional) + -- In this function, you are afforded the opportunity to add data to the tooltip should you so + -- desire. You are passed a hyperlink, and it's up to you to determine whether or what you should + -- display in the tooltip. +end + +function lib.OnLoad() + --This function is called when your variables have been loaded. + --You should also set your Configator defaults here + + print("AucAdvanced: {{"..libType..":"..libName.."}} loaded!") + AucAdvanced.Settings.SetDefault("util.example.active", true) + AucAdvanced.Settings.SetDefault("util.example.slider", 50) + AucAdvanced.Settings.SetDefault("util.example.wideslider", 100) + AucAdvanced.Settings.SetDefault("util.example.hardselectbox", 5) + AucAdvanced.Settings.SetDefault("util.example.dynamicselectbox", 5) + AucAdvanced.Settings.SetDefault("util.example.label", "Label") + AucAdvanced.Settings.SetDefault("util.example.text", "") + AucAdvanced.Settings.SetDefault("util.example.numberbox", "5") + AucAdvanced.Settings.SetDefault("util.example.moneyframe", "50000000") + AucAdvanced.Settings.SetDefault("util.example.moneyframepinned", "010101") +end + +--[[ Local functions ]]-- + +function private.SetupConfigGui(gui) + -- The defaults for the following settings are set in the lib.OnLoad function + local id = gui:AddTab(libName) + gui:MakeScrollable(id) + gui:AddControl(id, "Header", 0, libName.." options") + gui:AddControl(id, "Checkbox", 0, 1, "util.example.active", "This is a checkbox, it has two settings true (selected) and false (cleared)") + + gui:AddControl(id, "Subhead", 0, "There are two kinds of sliders:") + gui:AddControl(id, "Slider", 0, 1, "util.example.slider", 0, 100, 1, "Normal Sliders: %d%%") + gui:AddControl(id, "WideSlider", 0, 1, "util.example.wideslider", 0, 200, 1, "And Wide Sliders: %d%%") + + gui:AddControl(id, "Subhead", 0, "There are also two ways to build a selection box:") + gui:AddControl(id, "Selectbox", 0, 1, { + {0, "Zero"}, + {1, "One"}, + {2, "Two"}, + {3, "Three"}, + {4, "Four"}, + {5, "Five"}, + {6, "Six"}, + {7, "Seven"}, + {8, "Eight"}, + {9, "Nine"} + }, "util.example.hardselectbox", "Statically, by hardcoding the values...") + gui:AddControl(id, "Selectbox", 0, 1, private.GetNumbers, "util.example.dynamicselectbox", "Or dynamically by specifying a function instead of a table...") + + gui:AddControl(id, "Subhead", 0, "There are also a few ways to add text:\n The Headers and SubHeaders that you've already seen...") + gui:AddControl(id, "Note", 0, 1, nil, nil, "Notes...") + gui:AddControl(id, "Label", 0, 1, "util.example.label", "And Labels") + + gui:AddControl(id, "Subhead", 0, "There are two ways to get input via keyboard:") + gui:AddControl(id, "Text", 0, 1, "util.example.text", "Via the Text Control...") + gui:AddControl(id, "NumberBox", 0, 1, "util.example.numberbox", 0, 9, "Or using the NumberBox if you only need numbers.") + + gui:AddControl(id, "Subhead", 0, "There are two kinds of Money Frames:") + gui:AddControl(id, "MoneyFrame", 0, 1, "util.example.moneyframe", "MoneyFrames...") + gui:AddControl(id, "MoneyFramePinned", 0, 1, "util.example.moneyframepinned", 0, 101010, "And PinnedMoneyFrames.") + + gui:AddControl(id, "Subhead", 0, "And finally...") + gui:AddControl(id, "Button", 0, 1, "util.example.button", "The Button!") +end + +function private.GetNumbers() + return { {0, "Zero"}, {1, "One"}, {2, "Two"}, {3, "Three"}, {4, "Four"}, {5, "Five"}, {6, "Six"}, {7, "Seven"}, {8, "Eight"}, {9, "Nine"} } +end + +function private.Foo() +end + +function private.Bar() +end + +function private.Baz() +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Advanced/Modules/Auc-Util-Example/Example.lua $", "$Rev: 4840 $") diff --git a/Auc-Advanced/Modules/Auc-Util-Example/Readme.txt b/Auc-Advanced/Modules/Auc-Util-Example/Readme.txt new file mode 100644 index 0000000..a1325a9 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-Example/Readme.txt @@ -0,0 +1,10 @@ +This is an AddOn which is meant as an example of Auctioneer's embeddable +module support. You may either install this addon in WoW's AddOn folder, +or drop it into Auctioneer's "modules" subdirectory and either: + * Execute ../luae.exe (or ../rebuild.pl if you have perl), which will + automatically generate a ../Active.xml with all installed modules + enabled, and/or + * Manually edit the existing ../Active.xml and add/remove entries + to your taste. + + diff --git a/Auc-Advanced/Modules/Auc-Util-Glypher/Auc-Util-Glypher.toc b/Auc-Advanced/Modules/Auc-Util-Glypher/Auc-Util-Glypher.toc new file mode 100644 index 0000000..15d197e --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-Glypher/Auc-Util-Glypher.toc @@ -0,0 +1,14 @@ +## Title: Auc:Util:Glypher +## Notes: Auctioneer utility to assist in the creation of glyphs according to the player's stock and market conditions. +## +## Interface: 30300 +## Dependancies: Auc-Advanced, BeanCounter, Informant +## OptionalDeps: AdvancedTradeSkillWindow, Auc-Util-Appraiser, DataStore, Skillet +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Auc-Util-Glypher.toc 4586 2009-12-17 21:12:52Z Nechckn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## +Embed.xml diff --git a/Auc-Advanced/Modules/Auc-Util-Glypher/Embed.xml b/Auc-Advanced/Modules/Auc-Util-Glypher/Embed.xml new file mode 100644 index 0000000..d93cd4a --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-Glypher/Embed.xml @@ -0,0 +1,7 @@ + + + + diff --git a/Auc-Advanced/Modules/Auc-Util-Glypher/Glypher.lua b/Auc-Advanced/Modules/Auc-Util-Glypher/Glypher.lua new file mode 100644 index 0000000..eeebc05 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-Glypher/Glypher.lua @@ -0,0 +1,1195 @@ + +if not AucAdvanced then return end + +local libType, libName = "Util", "Glypher" +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end +local print,decode,_,_,replicate,empty,get,set,default,debugPrint,fill, _TRANS = AucAdvanced.GetModuleLocals() + +local tooltip = LibStub("nTipHelper:1") +local timeRemaining +local coFG +local onupdateframe +local INSCRIPTION_SPELLNAME = GetSpellInfo(45357) +local _, _, _, _, _, GLYPH_TYPE = GetItemInfo(42956) +if not GLYPH_TYPE then GLYPH_TYPE = "Glyph" end +print("Localized name of itemType for Glyphs: " .. GLYPH_TYPE) + +-- temporary check for Auc-Stat-Glypher +if AucAdvanced.Modules.Stat.Glypher then + message("For this version of Auc-Util-Glypher, you MUST manually copy (in game) your Auc-Stat-Glypher configuration over to Auc-Util-Glypher, AND disable Auc-Stat-Glypher. The Glypher pricing model is now included in Auc-Util-Glypher.") + return +end +-- +function lib.Processor(callbackType, ...) + if (callbackType == "config") then + private.SetupConfigGui(...) + elseif (callbackType == "tooltip") then + private.ProcessTooltip(...) + elseif (callbackType == "configchanged") then + private.ConfigChanged(...) + if private.gui then private.gui:Refresh() end + elseif (callbackType == "auctionui") then + private.auctionHook() ---When AuctionHouse loads hook the auction function we need + elseif (callbackType == "scanprogress") then + private.ScanProgressReceiver(...) + elseif (callbackType == "scanstats") then + private.ScanComplete(...) + end +end + +lib.Processors = {} +function lib.Processors.config(callbackType, ...) + private.SetupConfigGui(...) +end + +function lib.Processors.tooltip(callbackType, ...) + private.ProcessTooltip(...) +end + +function lib.Processors.configchanged(callbackType, ...) + private.ConfigChanged(...) + if private.gui then private.gui:Refresh() end +end + +function lib.Processors.auctionui(callbackType, ...) + private.auctionHook() ---When AuctionHouse loads hook the auction function we need +end + +function lib.Processors.scanprogress(callbackType, ...) + private.ScanProgressReceiver(...) +end + +function lib.Processors.scanstats(callbackType, ...) + private.ScanComplete(...) +end + +--after Auction House Loads Hook the Window Display event +function private.auctionHook() + hooksecurefunc("AuctionFrameAuctions_Update", private.storeCurrentAuctions) +end + +function private.ScanComplete() + --need to set last scan time so we can disable the refresh button for 2-5 minutes after doing a scan (to be nice to Blizzard and also to be able to notice when the refresh is actually done + if private.frame then + private.frame.refreshButton:Enable() + private.frame.refreshButton:SetText("Scan Glyphs") + end +end + +function private.ScanProgressReceiver(state, totalAuctions, scannedAuctions, elapsedTime) + totalAuctions = tonumber(totalAuctions) or 0 + scannedAuctions = tonumber(scannedAuctions) or 0 + elapsedTime = tonumber(elapsedTime) or 0 + + --borrowed this from Auc-Util-ScanProgress/ScanProgress.lua + local numAuctionsPerPage = NUM_AUCTION_ITEMS_PER_PAGE + + -- Prefer the elapsed time which is provided by core and excludes paused time. + local secondsElapsed = elapsedTime or (time() - private.scanStartTime) + + local auctionsToScan = totalAuctions - scannedAuctions + + local currentPage = math.floor(scannedAuctions / numAuctionsPerPage) + + local totalPages = totalAuctions / numAuctionsPerPage + if (totalPages - math.floor(totalPages) > 0) then + totalPages = math.ceil(totalPages) + else + totalPages = math.floor(totalPages) + end + + local auctionsScannedPerSecond = scannedAuctions / secondsElapsed + local secondsToScanCompletion = auctionsToScan / auctionsScannedPerSecond + timeRemaining = SecondsToTime(secondsToScanCompletion) + --print(timeRemaining .. " - " .. secondsToScanCompletion) -- debug to figure out how to eliminate the -hugenumber Sec + -- and when it's blank, then go back to just "Refresh Glyphs" since scanprogress seems to trigger after scancomplete + if private.frame then + if timeRemaining and auctionsScannedPerSecond and secondsElapsed and scannedAuctions and (secondsToScanCompletion > 1) and (secondsToScanCompletion < 600) then + private.frame.refreshButton:SetText(timeRemaining) + else + private.frame.refreshButton:SetText("Scan Glyphs") + end + end + + +end + +function lib.OnLoad() + default("util.glypher.moneyframeprofit", 35000) + default("util.glypher.history", 14) + default("util.glypher.stockdays", 2) + default("util.glypher.minstock", 0) + default("util.glypher.mincraft", 1) + --default("util.glypher.mincraftthreshold", 50) + default("util.glypher.maxstock", 5) + --default("util.glypher.minoverstock", 0) + --default("util.glypher.overstock", 0) + default("util.glypher.failratio", 30) + default("util.glypher.makefornew", 2) + default("util.glypher.herbprice", 8000) + default("util.glypher.profitAppraiser", 100) + default("util.glypher.profitBeancounter", 100) + default("util.glypher.profitMarket", 50) + default("util.glypher.pricemodel.min1", get("util.glypher.pricemodel.min") or 32500) + default("util.glypher.pricemodel.min2", get("util.glypher.pricemodel.min") or 35000) + default("util.glypher.pricemodel.max", 999999) + default("util.glypher.pricemodel.underpct", 1) + default("util.glypher.pricemodel.useundercut", true) + default("util.glypher.pricemodel.undercut", 1) + default("util.glypher.pricemodel.whitelist", "") + default("util.glypher.pricemodel.ignoretime", 0) + default("util.glypher.misc.clearqueue", true) + default("util.glypher.misc.inktrader", false) + default("util.glypher.altlist", "") + default("util.glypher.gvault", "") + default("util.glypher.inks.found", false) + default("util.glypher.debugTooltip", false) + default("util.glypher.makemaxstock", false) + default("util.glypher.hardminstock", false) + + + --Check to see if we've got a recent enough version of AucAdvanced + local rev = AucAdvanced.GetCurrentRevision() or 0 + if rev < 4409 then + local mess = "Auc-Util-Glypher requires Auctioneer Advanced build >= 4409" + DEFAULT_CHAT_FRAME:AddMessage(mess,1.0,0.0,0.0) + mess = "Auc-Util-Glypher will still load, but some features are guaranteed to NOT work" + DEFAULT_CHAT_FRAME:AddMessage(mess,1.0,0.0,0.0) + end + + if LibStub then + local LibDataBroker = LibStub:GetLibrary("LibDataBroker-1.1", true) + if LibDataBroker then + local embedded = false + for _, module in ipairs(AucAdvanced.EmbeddedModules) do + if module == "Auc-Util-Glypher" then + embedded = true + end + end + local sideIcon, sideIconE + if embedded then + sideIcon = "Interface\\AddOns\\Auc-Advanced\\Modules\\Auc-Util-Glypher\\Images\\Glypher" + sideIconE = "Interface\\AddOns\\Auc-Advanced\\Modules\\Auc-Util-Glypher\\Images\\GlypherE" + else + sideIcon = "Interface\\AddOns\\Auc-Util-Glypher\\Images\\Glypher" + sideIconE = "Interface\\AddOns\\Auc-Util-Glypher\\Images\\GlypherE" + end + + private.LDBButton = LibDataBroker:NewDataObject("Auc-Util-Glypher", { + type = "launcher", + icon = sideIcon, + OnClick = function(self, button) private.SlideBarClick(self, button) end, + }) + + function private.LDBButton:OnTooltipShow() + self:AddLine("Auc-Util-Glypher", 1,1,0.5, 1) + self:AddLine("Open the glypher gui", 1,1,0.5, 1) + end + --we use a slight hack to LDB to animate our icon on Enter as well as tooltip display. The Tooltip will be hidden by slidebar but will show for other addons + function private.LDBButton:OnEnter() + if self.icon and type(self.icon) == "table" then + self.icon:SetTexture(sideIconE) + end + GameTooltip:SetOwner(self, "ANCHOR_NONE") + GameTooltip:SetPoint("TOPLEFT", self, "BOTTOMLEFT") + GameTooltip:ClearLines() + private.LDBButton.OnTooltipShow(GameTooltip) + GameTooltip:Show() + end + function private.LDBButton:OnLeave() + if self.icon and type(self.icon) == "table" then + self.icon:SetTexture(sideIcon) + end + GameTooltip:Hide() + end + end + end +end + +function lib.CommandHandler(command, ...) + if (command == "help") then + print("Help for Glypher - "..libName) + local line = AucAdvanced.Config.GetCommandLead(libType, libName) + print(line, "help}} - this", libName, "help") + print(line, "show}} - show/hide the Glypher UI") + print(line, "makemaxstock}} - toggle the makemaxstock flag - Warning: advanced users only") + print(line, "hardminstock}} - toggle the hardminstock flag") + elseif (command == "show") then + private.SlideBarClick() + elseif (command == "makemaxstock") then + local makemaxstock = get("util.glypher.makemaxstock") + if makemaxstock then + set("util.glypher.makemaxstock", false) + print("makemaxstock is now OFF") + else + set("util.glypher.makemaxstock", true) + print("makemaxstock is now ON") + end + elseif (command == "hardminstock") then + local hardminstock = get("util.glypher.hardminstock") + if hardminstock then + set("util.glypher.hardminstock", false) + print("hardminstock is now OFF") + else + set("util.glypher.hardminstock", true) + print("hardminstock is now ON") + end + end +end + +function private.SlideBarClick(_, button) + if private.gui and private.gui:IsShown() then + AucAdvanced.Settings.Hide() + else + AucAdvanced.Settings.Show() + private.gui:ActivateTab(private.id) + end + if not get("util.glypher.inks.found") then + local mess = "The pricing model feature of Auc-Util-Glypher requires that you to use the 'Get Profitable' function in order to populate it's database." + DEFAULT_CHAT_FRAME:AddMessage(mess,1.0,0.0,0.0) + end +end + + +--[[ Local functions ]]-- + +local WARN = "|cffffff00" +local ERR = "|cffff0000" +function private.minstockFormat() + local minstock = get("util.glypher.minstock") + local mincraft = get("util.glypher.mincraft") + local maxstock = get("util.glypher.maxstock") + local makefornew = get("util.glypher.makefornew") + local fmt = "" + if (maxstock - minstock) < mincraft then fmt = WARN end -- This is a fairly illogical situation since the actual minstock is going to be maxstock - mincraft + if makefornew < minstock then fmt = WARN end -- We'd end up making minstock instead + if minstock > maxstock then fmt = ERR end -- This situation is illogical + return fmt +end +function private.mincraftFormat() + local minstock = get("util.glypher.minstock") + local mincraft = get("util.glypher.mincraft") + local maxstock = get("util.glypher.maxstock") + local makefornew = get("util.glypher.makefornew") + local fmt = "" + if (maxstock - minstock) < mincraft then fmt = WARN end -- This is a fairly illogical situation since the actual minstock is going to be maxstock - mincraft + if mincraft > makefornew then fmt = WARN end -- makefornew should be <= mincraft + if maxstock < mincraft then fmt = ERR end -- In this situation we'd never craft anything at all + return fmt +end +function private.maxstockFormat() + local minstock = get("util.glypher.minstock") + local mincraft = get("util.glypher.mincraft") + local maxstock = get("util.glypher.maxstock") + local makefornew = get("util.glypher.makefornew") + local fmt = "" + if (maxstock - minstock) < mincraft then fmt = WARN end -- This is a fairly illogical situation since the actual minstock is going to be maxstock - mincraft + if makefornew > maxstock then fmt = WARN end -- We'd never make the total amount because maxstock is less than makefornew + if maxstock < minstock then fmt = ERR end -- This situation is illogical + if maxstock < mincraft then fmt = ERR end -- In this situation we'd never craft anything at all + return fmt +end +function private.makefornewFormat() + local minstock = get("util.glypher.minstock") + local mincraft = get("util.glypher.mincraft") + local maxstock = get("util.glypher.maxstock") + local makefornew = get("util.glypher.makefornew") + local fmt = "" + if mincraft > makefornew then fmt = WARN end -- makefornew should be <= mincraft + if makefornew < minstock then fmt = WARN end -- We'd end up making minstock instead + if makefornew > maxstock then fmt = WARN end -- We'd never make the total amount because maxstock is less than makefornew + return fmt +end + +local frame +function private.SetupConfigGui(gui) + -- The defaults for the following settings are set in the lib.OnLoad function + local id = gui:AddTab(libName, libType.." Modules") + gui:MakeScrollable(id) + + local SelectBox = LibStub:GetLibrary("SelectBox") + local ScrollSheet = LibStub:GetLibrary("ScrollSheet") + --Add Drag slot / Item icon + frame = gui.tabs[id].content + private.gui = gui + private.id = id + private.frame = frame + + function frame.SetButtonTooltip(this, text) + if text and get("util.appraiser.buttontips") then + GameTooltip:SetOwner(this, "ANCHOR_BOTTOMRIGHT") + GameTooltip:SetText(text) + end + end + + gui:AddHelp(id, "what is Glypher?", + "What is Glypher?", + "Glyher is a work-in-progress. Its goal is to assist in managing a profitable glyph-based business." + ) + + local moreHelp = "" + local DSa, DSc, DSm + if not DataStore then + DSa = true + DSc = true + DSm = true + else + if not DataStore:IsModuleEnabled("DataStore_Auctions") then + DSa = true + end + if not DataStore:IsModuleEnabled("DataStore_Containers") then + DSc = true + end + if not DataStore:IsModuleEnabled( "DataStore_Mails") then + DSm = true + end + end + if DSa then + moreHelp = moreHelp .. "Installing the DataStore_Auctions addon will allow Glypher to see auctions of specified alts.\n\n" + end + if DSc then + moreHelp = moreHelp .. "Installing the DataStore_Containers addon will allow Glypher to see bags and banks of specified alts, as well as an optional guild vault.\n\n" + end + if DSm then + moreHelp = moreHelp .. "Installing the DataStore_Mails addon will allow Glypher to see the mailboxes of specified alts.\n\n" + end + if moreHelp ~= "" then + moreHelp = "Optional DataStore addons can improve your business efficiency by seeing what your glyph-handling alts have:\n\n" .. moreHelp + gui:AddHelp(id, "More features are available:", + "More features are available:", + moreHelp + ) + end + + + gui:AddControl(id, "Subhead", 0, "Glypher: Profitable Glyph Utility") + + gui:AddControl(id, "Subhead", 0, "Glyph creation configuration") + + gui:AddControl(id, "MoneyFrame", 0, 1, "util.glypher.moneyframeprofit", "Minimum profit") + gui:AddTip(id, "Sets the minimum profit required to queue glyphs to craft.") + + gui:AddControl(id, "Text", 0, 1, "util.glypher.ink", "Restrict ink to") + gui:AddTip(id, "Restrict glyphs to those requiring this ink. Leave blank to craft all glyphs regardless of ink.") + + gui:AddControl(id, "NumeriSlider", 0, 1, "util.glypher.history", 1, 31, 1, "Consider sales") + gui:AddTip(id, "Consider sales you've made on all toons from the last number of days selected.") + + gui:AddControl(id, "NumeriSlider", 0, 1, "util.glypher.failratio", 0, 500, 1, "expired filter") + gui:AddTip(id, "The expired:success (slider:1) ratio at which we will not craft glyphs. For failures we go back to the start of BeanCounter history.\nIf set to 0 this feature will be disabled.") + + gui:AddControl(id, "NumeriSlider", 0, 1, "util.glypher.stockdays", 1, 8, 1, "Days to stock") + gui:AddTip(id, "Number of days worth of glyphs to stock based upon your considered sales") + + gui:AddControl(id, "NumeriSlider", 0, 1, "util.glypher.minstock", 0, 40, 1, "%sMin Stock", private.minstockFormat) + gui:AddTip(id, "Minimum number of each glyph to stock. This must be less than or equal to the Max Stock.") + + gui:AddControl(id, "NumeriSlider", 0, 1, "util.glypher.mincraft", 1, 40, 1, "%sMin Craft", private.mincraftFormat) + gui:AddTip(id, "Minimum number of each glyph to craft. Generally this should be less than or equal to the difference between Min Stock and Max Stock") + + --gui:AddControl(id, "NumeriSlider", 0, 1, "util.glypher.mincraftthreshold", 0, 100, 10, "Min craft %%") + --gui:AddTip(id, "Threshold for deciding on whether to craft Min Craft or none.") + + gui:AddControl(id, "NumeriSlider", 0, 1, "util.glypher.maxstock", 1, 40, 1, "%sMax Stock", private.maxstockFormat) + gui:AddTip(id, "Maximum number of each glyph to stock. This must be greater than or equal to the Min Stock.") + + --gui:AddControl(id, "NumeriSlider", 0, 1, "util.glypher.minoverstock", 0, 100, 20, "Min overstock %%") + --gui:AddTip(id, "Minimum percentage of over max stock to allow up to overstock to be made. Set to 0 to disable this feature.") + + --gui:AddControl(id, "NumeriSlider", 0, 1, "util.glypher.overstock", 0, 60, 1, "Overstock") + --gui:AddTip(id, "Maximimum stock to allow when Glypher wants to make at least min overstock more than max stock") + + gui:AddControl(id, "MoneyFrame", 0, 1, "util.glypher.herbprice", "Price of single Northrend herb") + gui:AddTip(id, "Used to calculate the price of Ink of the Sea which can be traded for most other inks.") + + gui:AddControl(id, "Subhead", 0, "New glyph configuration") + + gui:AddControl(id, "NumeriSlider", 0, 1, "util.glypher.makefornew", 0, 20, 1, "%sMake New", private.makefornewFormat) + gui:AddTip(id, "Number of glyphs (newly learned or newly profitable) to make when there are zero sales and zero failures in history.") + + gui:AddControl(id, "Subhead", 0, "Glyph Valuation Method") + + local weightWords = "for evaluation of new or previously unprofitable glyphs." + + if AucAdvanced.Modules.Util.Appraiser then + gui:AddControl(id, "NumeriSlider", 0, 1, "util.glypher.profitAppraiser", 1, 100, 1, "Current model") + gui:AddTip(id, "Relative weight for the current pricing model set " .. weightWords) + else + gui:AddControl(id, "NumeriSlider", 0, 1, "util.glypher.profitAppraiser", 1, 100, 1, "|caaaaaaaaCurrent model") + gui:AddTip(id, "Auc-Util-Appraiser is not enabled, so the current model weighting is disabled") + end + + gui:AddControl(id, "NumeriSlider", 0, 1, "util.glypher.profitBeancounter", 1, 100, 1, "Sales history") + gui:AddTip(id, "Relative weight for the Beancounter sales history, restricted by your consideration period, " .. weightWords) + + gui:AddControl(id, "NumeriSlider", 0, 1, "util.glypher.profitMarket", 1, 100, 1, "Market price") + gui:AddTip(id, "Relative weight for the Market price " .. weightWords) + + gui:AddControl(id, "Subhead", 0, "Glypher pricing model") + + --gui:AddControl(id, "Subhead", 0, "Minimum Sale Price - 1-Ink Glyphs") + gui:AddControl(id, "Note", 0, 1, nil, nil, "Minimum Sale Price - 1-Ink Glyphs") + gui:AddControl(id, "MoneyFrame", 0, 1, "util.glypher.pricemodel.min1") + gui:AddTip(id, "The price that Glypher will never go below on 1-ink glyphs in order to undercut others") + + gui:AddControl(id, "Note", 0, 1, nil, nil, "Minimum Sale Price - 2-Ink Glyphs") + gui:AddControl(id, "MoneyFrame", 0, 1, "util.glypher.pricemodel.min2") + gui:AddTip(id, "The price that Glypher will never go below on 2-ink glyphs in order to undercut others") + + --gui:AddControl(id, "Subhead", 0, "Maximum Sale Price") + gui:AddControl(id, "Note", 0, 1, nil, nil, "Maximum Sale Price") + gui:AddControl(id, "MoneyFrame", 0, 1, "util.glypher.pricemodel.max") + gui:AddTip(id, "The price that Glypher will never go above in order to overcut others") + + + gui:AddControl(id, "Note", 0, 1, nil, nil, "Undercut") + gui:AddControl(id, "Slider", 0, 1, "util.glypher.pricemodel.underpct", 0, 20, 0.1, _TRANS('UCUT_Interface_UndercutMinimum').." %g%%")--Undercut: + + gui:AddControl(id, "Checkbox", 0, 1, "util.glypher.pricemodel.useundercut", _TRANS('UCUT_Interface_UndercutAmount') )--Specify undercut amount by coin value + gui:AddTip(id, _TRANS('UCUT_HelpTooltip_UndercutAmount') )--Specify the amount to undercut by a specific amount, instead of by a percentage + --gui:AddControl(id, "Subhead", 0, "Undercut Amount") + --gui:AddControl(id, "Note", 0, 1, nil, nil, "Undercut Amount") + gui:AddControl(id, "MoneyFramePinned", 0, 2, "util.glypher.pricemodel.undercut", 1, 99999999, _TRANS('UCUT_Interface_CurrentValue') )--Undercut Amount + --gui:AddControl(id, "MoneyFrame", 0, 1, "util.glypher.pricemodel.undercut") + gui:AddTip(id, "The amount that you undercut others") + + --gui:AddControl(id, "Subhead", 0, "Whitelist") + gui:AddControl(id, "Note", 0, 1, nil, nil, "Whitelist") + gui:AddControl(id, "Text", 0, 1, "util.glypher.pricemodel.whitelist") + gui:AddTip(id, "The players to whitelist on undercuts (blank for no whitelist, separarate whitelisted users with a ':')") --eventually have a list to edit + + gui:AddControl(id, "Note", 0, 1, nil, nil, "Ignore Time Left") + gui:AddControl(id, "Selectbox", 0, 1, { + {0, "Disable"}, + {1, "30 Minutes"}, + {2, "2 Hours"} + }, "util.glypher.pricemodel.ignoretime", "Ignore competitor auctions when time left is less than time selected.") + gui:AddTip(id, "Ignore competitor auctions when time left is less than time selected.") + + gui:AddControl(id, "Subhead", 0, "Misc options") + + gui:AddControl(id, "Checkbox", 0, 1, "util.glypher.misc.clearqueue", "Auto clear skill queue") + gui:AddTip(id, "Automatically clear the skill queue when 'Get Profit' or 'Add to Skill' are pressed.") + + gui:AddControl(id, "Checkbox", 0, 1, "util.glypher.misc.inktrader", "Use Ink Trader") + gui:AddTip(id, "Use the ink trader in Dalaran instead of having the skill window recursively queue inks to be made.") + + gui:AddControl(id, "Checkbox", 0, 1, "util.glypher.debugTooltip", "Show debug tooltip") + gui:AddTip(id, "Show some debugging information in item tooltips.") + + if DataStore then + gui:AddControl(id, "Subhead", 0, "DataStore Configuration") + + gui:AddControl(id, "Note", 0, 1, nil, nil, "Alt list") + gui:AddControl(id, "Text", 0, 1, "util.glypher.altlist") + gui:AddTip(id, "List of alts which you use for your glyph business, separate alts with a ':'. Leave blank to disable.") + + gui:AddControl(id, "Note", 0, 1, nil, nil, "Guild") + gui:AddControl(id, "Text", 0, 1, "util.glypher.gvault") + gui:AddTip(id, "Guild name for the vault which you use for your glyph business. Leave blank to disable.") + end + + frame.refreshButton = CreateFrame("Button", nil, frame, "OptionsButtonTemplate") + frame.refreshButton:SetPoint("TOPLEFT", frame, "BOTTOMLEFT", 325, 225) + frame.refreshButton:SetWidth(110) + frame.refreshButton:SetText("Scan Glyphs") + frame.refreshButton:SetScript("OnClick", function() private.refreshAll() end) + frame.refreshButton:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, "Click to do a category scan on glyphs, refreshing Auctioneers image of all glyphs.") end) + frame.refreshButton:SetScript("OnLeave", function() return GameTooltip:Hide() end) + + frame.searchButton = CreateFrame("Button", nil, frame, "OptionsButtonTemplate") + frame.searchButton:SetPoint("TOP", frame.refreshButton, "BOTTOM", 0, -5) + frame.searchButton:SetWidth(110) + frame.searchButton:SetText("Get Profitable") + frame.searchButton:SetScript("OnClick", function() private.findGlyphs() end) + frame.searchButton:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, "Click to get profitable glyphs.") end) + frame.searchButton:SetScript("OnLeave", function() return GameTooltip:Hide() end) + + frame.skilletButton = CreateFrame("Button", nil, frame, "OptionsButtonTemplate") + frame.skilletButton:SetPoint("TOP", frame.searchButton, "BOTTOM", 0, -5) + frame.skilletButton:SetWidth(110) + frame.skilletButton:SetText("Add to Skill") + frame.skilletButton:SetScript("OnClick", function() private.addtoCraft() end) + frame.skilletButton:SetScript("OnEnter", function(self) return frame.SetButtonTooltip(self, "Click to add profitable glyphs from the list to Skillet.") end) + frame.skilletButton:SetScript("OnLeave", function() return GameTooltip:Hide() end) + + --Create the glyph list results frame + frame.glypher = CreateFrame("Frame", nil, frame) + frame.glypher:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + edgeFile = "Interface/Tooltips/UI-Tooltip-Border", + tile = true, tileSize = 32, edgeSize = 16, + insets = { left = 5, right = 5, top = 5, bottom = 5 } + }) + + frame.glypher:SetBackdropColor(0, 0, 0.0, 0.5) + frame.glypher:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -20, -138) + frame.glypher:SetPoint("BOTTOM", frame, "BOTTOM", 0, -135) + frame.glypher:SetWidth(275) + frame.glypher:SetHeight(310) + + frame.glypher.sheet = ScrollSheet:Create(frame.glypher, { + { "Glyph", "TOOLTIP", 150 }, + { "#", "NUMBER", 25 }, + { "Profit", "COIN", 60}, + --{ "index", "TEXT",0 }, + }) + + function frame.glypher.sheet.Processor(callback, self, button, column, row, order, curDir, ...) + if (callback == "OnMouseDownCell") then + + -- elseif (callback == "OnClickCell") then + -- + -- elseif (callback == "ColumnOrder") then + -- + -- elseif (callback == "ColumnWidthSet") then + -- + -- elseif (callback == "ColumnWidthReset") then + -- + -- elseif (callback == "ColumnSort") then + + elseif (callback == "OnEnterCell") then + private.sheetOnEnter(button, row, column) + elseif (callback == "OnLeaveCell") then + GameTooltip:Hide() + end + end +end + +function private.ProcessTooltip(frame, name, link, quality, quantity, cost, additional) + if not get("util.glypher.debugTooltip") then return end + if not link then return end + if not DataStore then return end + frame:SetColor(0.9, 0.3, 0.3) + frame:AddLine("Glypher Debugging Data") + local _, itemId = decode(link) + local stock, a = private.GetStock(itemId) + local i, v + for i, v in ipairs(a) do + frame:AddLine(v) + end + frame:AddLine("Total stock: " .. stock) + local ink = get("util.glypher.inks."..itemId..".ink") or 0 + local count = get("util.glypher.inks."..itemId..".count") or 0 + frame:AddLine("Ink: " .. ink .. " count: " .. count) + + --the following was mostly just copy/pasted from another section to debug a situation with over-production of glyphs - solved + local name = UnitName("player") + local realm = GetRealmName() + local account = "Default" + local currentcharacter = format("%s.%s.%s", account, realm, name) + local altList = get("util.glypher.altlist") + local itemName = itemId + local history = get("util.glypher.history") --how far back in beancounter to look, in days + local HOURS_IN_DAY = 24 + local MINUTES_IN_HOUR = 60; + local SECONDS_IN_MINUTE = 60; + local SECONDS_IN_DAY = HOURS_IN_DAY * MINUTES_IN_HOUR * SECONDS_IN_MINUTE; + local historyTime = time() - (history * SECONDS_IN_DAY) + + local bcSold = 0 + local bcProfit = 0 + if DataStore and DataStore:IsModuleEnabled("DataStore_Auctions") then -- Auctions & Bids + for characterName, character in pairs(DataStore:GetCharacters(realm, account)) do + if string.find(":" .. name .. ":" .. altList .. ":", ":" .. characterName .. ":") then + bcSold = bcSold + (BeanCounter.API.getAHSoldFailed(characterName, link, history) or 0) + bcProfit = bcProfit + (BeanCounter.API.getAHProfit(characterName, itemName, historyTime, time()) or 0) + end + end + else + bcSold = BeanCounter.API.getAHSoldFailed(UnitName("player"), link, history) or 0 + bcProfit, tmpLow, tmpHigh = BeanCounter.API.getAHProfit(UnitName("player"), itemName, historyTime, time()) or 0 + end + frame:AddLine("bcSold: " .. bcSold) + local currentAuctions = stock + local stockdays = get("util.glypher.stockdays") + local make = floor(bcSold/history * stockdays + .5) - currentAuctions -- using .9 for rounding because it's best not to miss a sale + + frame:AddLine("Wanting to make " .. make .. " (" .. bcSold .. "/" .. history .. "*" .. stockdays .. " + .5)") +end + +function private.sheetOnEnter(button, row, column) + local link, name, _ + link = private.frame.glypher.sheet.rows[row][column]:GetText() or "FAILED LINK" + if link:match("^(|c%x+|H.+|h%[.+%])") then + name = GetItemInfo(link) + end + GameTooltip:SetOwner(button, "ANCHOR_RIGHT") + if private.frame.glypher.sheet.rows[row][column]:IsShown()then --Hide tooltip for hidden cells + if link and name then + GameTooltip:SetHyperlink(link) + end + end +end + +function private.ConfigChanged(setting, value, ...) + -- if setting == "util.glypher.craft" and value then + -- private.addtoCraft() + -- set("util.glypher.craft", nil) --for some reason teh button will not toggle a setting http://jira.norganna.org/browse/CNFG-89 + -- + -- elseif setting == "util.glypher.getglyphs" and value then + -- private.findGlyphs() + -- set("util.glypher.getglyphs", nil) + -- end +end + +function private.findGlyphs() + if not AuctionFrame or not AuctionFrame:IsVisible() then + if not DataStore or not DataStore:IsModuleEnabled("DataStore_Auctions") then + print("Please visit your local auctioneer before using this function.") + return + end + end + private.data = nil + private.frame.glypher.sheet:SetData({}, Style) + if get("util.glypher.misc.clearqueue") then + if Skillet then + if Skillet.ClearQueue then + Skillet:ClearQueue() + else + print("Skillet detected but is not Lilsparky's clone of Skillet") + print("Get Lilsparky's clone of Skillet at http://www.wowace.com/addons/skillet/repositories/lilsparkys-clone/files/") + end + end + if ATSW_DeleteQueue then ATSW_DeleteQueue() end + end + if (not coFG) or (coroutine.status(coFG) == "dead") then + coFG = coroutine.create(private.cofindGlyphs) + onupdateframe = CreateFrame("frame") + + onupdateframe:SetScript("OnUpdate", function() + local status, result + status = coroutine.status(coFG) + if status ~= "dead" then + status, result = coroutine.resume(coFG) + if not status and result then + error("Error in search coroutine: "..result.."\n\n{{{Coroutine Stack:}}}\n"..debugstack(coFG)); + end + end + end) + + local status, result = coroutine.resume(coFG) + if not status and result then + error("Error in search coroutine: "..result.."\n\n{{{Coroutine Stack:}}}\n"..debugstack(coFG)); + end + else + print("coroutine already running: "..coroutine.status(coFG)) + end + coroutine.resume(coFG) +end +function private.cofindGlyphs() + private.frame.searchButton:Disable() + + set("util.glypher.inks.found", true) + local MinimumProfit = get("util.glypher.moneyframeprofit") + local quality = 2 --no rare quality items + local history = get("util.glypher.history") --how far back in beancounter to look, in days + local stockdays = get("util.glypher.stockdays") + local minstock = get("util.glypher.minstock") + local hardminstock = get("util.glypher.hardminstock") + local mincraft = get("util.glypher.mincraft") + --local mincraftthreshold = get("util.glypher.mincraftthreshold") + local maxstock = get("util.glypher.maxstock") + --local minoverstock = get("util.glypher.minoverstock") + --local overstock = get("util.glypher.overstock") + local failratio = get("util.glypher.failratio") + local makefornew = get("util.glypher.makefornew") + local herbprice = get("util.glypher.herbprice") + local INK = get("util.glypher.ink") or "" + + local HOURS_IN_DAY = 24 + local MINUTES_IN_HOUR = 60; + local SECONDS_IN_MINUTE = 60; + local SECONDS_IN_DAY = HOURS_IN_DAY * MINUTES_IN_HOUR * SECONDS_IN_MINUTE; + local historyTime = time() - (history * SECONDS_IN_DAY) + local qtyInk = 0 + + local inkCost + local resProb = 2.5 + inkCost = ((herbprice * 5) / resProb) * 2 -- 5 herbs divided by the average pigments you get, 2 to make ink of the sea + + if not inkCost then + print("Error in inkCost - returned nill!!!!!!!") + else + print("Ink cost: " .. inkCost) + end + + if DataStore and not DataStore:IsModuleEnabled("DataStore_Auctions") then + if not private.auctionCount or not BeanCounter then + print("Please visit your local auctioneer before using this function. (auc/BC)") + private.frame.searchButton:Enable() + return + end + end + + private.data = {} + private.Display = {} + local currentSelection = GetTradeSkillLine() -- the "1" in the original code is superfluous -- this function takes no arguments + if currentSelection ~= INSCRIPTION_SPELLNAME then + local hasInscription = GetSpellInfo(INSCRIPTION_SPELLNAME) + if not hasInscription then print("It does not look like this character has INSCRIPTION_SPELLNAME") return end + CastSpellByName(INSCRIPTION_SPELLNAME) --open trade skill + end + --end lilsparky suggested change + local numTradeSkills = GetNumTradeSkills() + local name = UnitName("player") + local realm = GetRealmName() + local account = "Default" + local currentcharacter = format("%s.%s.%s", account, realm, name) + local altList = get("util.glypher.altlist") + + for ID = GetFirstTradeSkill(), GetNumTradeSkills() do + coroutine.yield() + local pctDone = 100 - floor((numTradeSkills-ID)/numTradeSkills*100) + private.frame.searchButton:SetText("(" .. pctDone .. "%)") + + local link = GetTradeSkillItemLink(ID) + local itemName = GetTradeSkillInfo(ID) + + local linkType, itemId, property, factor = decode(link) + itemId = tonumber(itemId) + + if itemName:find("Glyph") then + if link and link:match("^|c%x+|Hitem.+|h%[.*%]") and itemName and select(3, GetItemInfo(link)) <= quality then --if its a craftable line and not a header and lower than our Quality + local reagentCost = 0 + local addInk = 0 + --Sum the cost of the mats to craft this item, parchment is not considered its really too cheap to matter + local inkMatch = false --only match inks choosen by user + for i = 1 ,GetTradeSkillNumReagents(ID) do + local inkName, _, count = GetTradeSkillReagentInfo(ID, 1) + local link = GetTradeSkillReagentItemLink(ID, i) + --local inkPrice = AucAdvanced.API.GetMarketValue(link) or 0 + local _, Id = decode(link) + local isVendored,isLimited,itemCost,toSell,buyStack,maxStack = Informant.getItemVendorInfo(Id) + if string.find(":43126:43120:43124:37101:43118:43116:39774:39469:43122:", ":" .. Id .. ":") then + reagentCost = (reagentCost + (inkCost * count) ) + addInk = addInk + count + set("util.glypher.inks."..itemId..".ink", 43126) + set("util.glypher.inks."..itemId..".count", count) + elseif Id == 43127 then + reagentCost = (reagentCost + (inkCost * count * 10) ) + addInk = addInk + (count * 10) + set("util.glypher.inks."..itemId..".ink", 43127) + set("util.glypher.inks."..itemId..".count", count) + elseif isVendored then + reagentCost = (reagentCost + (itemCost * count) ) + else + print("Not parchment or buyable ink -- " .. link .. "-- need market price") + end + + if INK:match("-") then -- ignore a specific ink + local INK = INK:gsub("-","") + inkMatch = true + if inkName:lower():match(INK:lower()) then + inkMatch = false + end + else + if inkName:lower():match(INK:lower()) then + inkMatch = true + end + end + end + + --We want these local to the loop (at least) because we're zeroing them if there is no price + local profitAppraiser = get("util.glypher.profitAppraiser") + local profitMarket = get("util.glypher.profitMarket") + local profitBeancounter = get("util.glypher.profitBeancounter") + + --local price = AucAdvanced.API.GetMarketValue(link) + local priceAppraiser = 0 + if AucAdvanced.Modules.Util.Appraiser then + priceAppraiser = AucAdvanced.Modules.Util.Appraiser.GetPrice(link, AucAdvanced.GetFaction()) or 0 + end + + if priceAppraiser == 0 then profitAppraiser = 0 end + local priceMarket = AucAdvanced.API.GetMarketValue(link) or 0 + if priceMarket == 0 then profitMarket = 0 end + + local bcSold = 0 + local bcProfit = 0 + if DataStore and DataStore:IsModuleEnabled("DataStore_Auctions") then -- Auctions & Bids + for characterName, character in pairs(DataStore:GetCharacters(realm, account)) do + if string.find(":" .. name .. ":" .. altList .. ":", ":" .. characterName .. ":") then + bcSold = bcSold + (BeanCounter.API.getAHSoldFailed(characterName, link, history) or 0) + bcProfit = bcProfit + (BeanCounter.API.getAHProfit(characterName, itemName, historyTime, time()) or 0) + end + end + else + bcSold = BeanCounter.API.getAHSoldFailed(UnitName("player"), link, history) or 0 + bcProfit, tmpLow, tmpHigh = BeanCounter.API.getAHProfit(UnitName("player"), itemName, historyTime, time()) or 0 + end + + local priceBeancounter + if bcProfit > 0 and bcSold > 0 then + priceBeancounter = floor(bcProfit/bcSold) + else + priceBeancounter = 0 + end + if priceBeancounter == 0 then profitBeancounter = 0 end + --evalutate based upon weights + local profitTotalWeight = profitAppraiser + profitMarket + profitBeancounter + if profitTotalWeight == 0 then + print("profitTotalWeight is 0 - changing to 1") + profitTotalWeight = 1 + end + local worthPrice = floor((priceAppraiser * (profitAppraiser/profitTotalWeight)) + (priceMarket * (profitMarket/profitTotalWeight)) + (priceBeancounter * (profitBeancounter/profitTotalWeight))) + + property = tonumber(property) or 0 + factor = tonumber(factor) or 0 + local data = AucAdvanced.API.QueryImage({ + itemId = itemId, + suffix = property, + factor = factor, + }) + competing = #data + local buyoutMin = 99999999 + for j = 1, #data do + local compet = AucAdvanced.API.UnpackImageItem(data[j]) + compet.buyoutPrice = (compet.buyoutPrice/compet.stackSize) + -- unused -- local postPrice = AucAdvanced.Modules.Util.Appraiser.GetPrice(itemId, serverKey) or 0 + if compet.buyoutPrice < buyoutMin then + buyoutMin = compet.buyoutPrice + end + end + + if worthPrice > buyoutMin then + worthPrice = buyoutMin + end + + --if we match the ink and our profits high enough, check how many we currently have on AH + if worthPrice and (worthPrice - reagentCost) >= MinimumProfit and inkMatch then + local currentAuctions = private.GetStock(itemId) + + local make = floor(bcSold/history * stockdays + .5) - currentAuctions -- using .9 for rounding because it's best not to miss a sale + local failed = 0 + if DataStore and DataStore:IsModuleEnabled("DataStore_Auctions") then -- Auctions & Bids + for characterName, character in pairs(DataStore:GetCharacters(realm, account)) do + if string.find(":" .. name .. ":" .. altList .. ":", ":" .. characterName .. ":") then + local _, characterFailed = BeanCounter.API.getAHSoldFailed(characterName, link) + if characterFailed then failed = failed + characterFailed end + end + end + else + local _, characterFailed = BeanCounter.API.getAHSoldFailed(UnitName("player"), link) + if characterFailed then failed = characterFailed end + end + + if bcSold == 0 and failed == 0 and currentAuctions == 0 then + make = makefornew + local mess = "New glyph: " .. link + DEFAULT_CHAT_FRAME:AddMessage(mess,1.0,0.0,0.0) + end + local makemaxstock = get("util.glypher.makemaxstock") + if makemaxstock and (currentAuctions == 0) and (make < maxstock) then make = maxstock end + if (make > 0 or hardminstock) and ((make + currentAuctions) < minstock) then make = (minstock - currentAuctions) end + if (make > 0) and (make < mincraft) then + --if (make >= (mincraft * (mincraftthreshold/100))) or (currentAuctions < minstock) or (currentAuctions == 0) then + --if (currentAuctions < minstock) or (currentAuctions == 0) then + make = mincraft + --else + -- make = 0 + --end + end + --if (minoverstock > 0) and (make + currentAuctions) > maxstock and (make + currentAuctions) > (((100+minoverstock)/100)*maxstock) then + -- if (make + currentAuctions) > overstock then + -- make = (overstock - currentAuctions) + -- end + --elseif (make + currentAuctions) > maxstock then + if (make + currentAuctions) > maxstock then + make = (maxstock - currentAuctions) + end + if (make > 0) and (make < mincraft) then make = 0 end -- in this case we can't make mincraft because it would put us over the limit, so let's make none at all (this avoids repeatedly "topping off" stacks) + if make > 0 then + local failedratio + if (bcSold > 0) then failedratio = failed/bcSold else failedratio = failed end + --if (bcSold > 0 and failedratio < failratio) or failed == 0 or failratio == 0 then + if failedratio < failratio or failed == 0 or failratio == 0 then + table.insert(private.data, { ["link"] = link, ["ID"] = ID, ["count"] = make, ["name"] = itemName} ) + table.insert(private.Display, {link, make, worthPrice - reagentCost} ) + qtyInk = qtyInk + (addInk * make) + else + local mess = "Skipping " .. link .. ": failedratio = " .. failedratio .. " (failed: " .. failed .. " / sold: " .. bcSold .. ")" + print(mess) + end + end + end + end + end + end + + if get("util.glypher.misc.inktrader") then + local _, link = GetItemInfo(43126) + local mess = "You need " .. link .. "x" .. qtyInk .. " to process this queue." + DEFAULT_CHAT_FRAME:AddMessage(mess,1.0,0.0,0.0) + end + private.frame.glypher.sheet:SetData(private.Display, Style) + private.frame.searchButton:Enable() + private.frame.searchButton:SetText("Get Profitable") +end +--store the players current auctions +function private.storeCurrentAuctions() + local count = {} + local _, totalAuctions = GetNumAuctionItems("owner"); + + if totalAuctions > 0 then + for i = 1, totalAuctions do + local _, _, c = GetAuctionItemInfo("owner", i) + local link = GetAuctionItemLink("owner", i) + local _, itemID = decode(link) + if itemID and c then + if not count[itemID] then + count[itemID] = 0 + end + count[itemID] = (count[itemID] + c) + end + end + end + private.auctionCount = count + private.auctionCountRefresh = time() +end + + +function private.addtoCraft() + local queueRecurse = not get("util.glypher.misc.inktrader") + if not private.data then + print("Glyph list is empty - use Get Profitable first.") + return + end + + if get("util.glypher.misc.clearqueue") then + if Skillet and Skillet.ClearQueue then Skillet:ClearQueue() end + if ATSW_DeleteQueue then ATSW_DeleteQueue() end + end + + local sent + if Skillet and Skillet.QueueAppendCommand and Skillet.ClearQueue then + sent = private.addToSkillet() + elseif ATSW_AddJobRecursive and atsw_preventupdate ~= nil then + sent = private.addToATSW() + elseif GnomeWorks and GnomeWorks.AddToQueue then + sent = private.addToGnomeworks() + elseif ATSW_AddJobRecursive and atsw_preventupdate == nil then + print("Advanced Trade Skill Window found, but needs to be patched in Interface/AddOns/AdvancedTradeSkillWindow/atsw.lua:") + print("Change the following line:") + print("local atsw_preventupdate=false;") + print("To:") + print("atsw_preventupdate=false;") + print("Then type /reloadui") + print("---> Using Lilsparky's clone of Skillet instead of ATSW is HIGHLY recommended. <---") + print("Get Lilsparky's clone of Skillet at http://www.wowace.com/addons/skillet/repositories/lilsparkys-clone/files/") + else + print("Gnomeworks, Lilsparky's clone of Skillet or Advanced Trade Skill Window not found") + print("Get Lilsparky's clone of Skillet at http://www.wowace.com/addons/skillet/repositories/lilsparkys-clone/files/") + end + --clear queue once we sucessfully send it + if sent then + private.data = nil + private.frame.glypher.sheet:SetData({}, Style) + end +end +--[[Functions to add our glyphs to various tradeskill windows]] +function private.addToSkillet() + if not Skillet.reagentsChanged then Skillet.reagentsChanged = {} end --this required table is nil when we use the queue + + for i, glyph in ipairs(private.data) do + local command = {} + --index is needed for skillet to properly make use of our data + local _, index = Skillet:GetRecipeDataByTradeIndex(45357, glyph.ID) + command["recipeID"] = index + command["op"] = "iterate" + command["count"] = glyph.count + Skillet:QueueAppendCommand(command, queueRecurse) + end + Skillet:UpdateTradeSkillWindow() + return true +end + +--gnomeworks. TODO update this when gnomeworks gets a proper API. Its still a WIP +function private.addToGnomeworks() + local player = UnitName("player") + local inscription = 45357 --static spell ID + for i, glyph in ipairs(private.data) do + local recipieLink = GetTradeSkillRecipeLink(glyph.ID) + local recipeID = recipieLink:match("^|c.-|H.-:(%d-)|h") + recipeID = tonumber(recipeID) + GnomeWorks:AddToQueue(player, inscription, recipeID, glyph.count) + end + return true +end + +function private.addToATSW() + local atsw_tmp = atsw_preventupdate + atsw_preventupdate = true + for i, glyph in ipairs(private.data) do + if queueRecurse then + ATSW_AddJobRecursive(glyph.name, glyph.count) + else + ATSW_AddJobLL(glyph.name, glyph.count) + end + end + atsw_preventupdate = atsw_tmp + ATSW_ResetPossibleItemCounts() + ATSWInv_UpdateQueuedItemList() + ATSWFrame_UpdateQueue() + ATSWFrame_Update() + return true +end + +function private.refreshAll() + if not AuctionFrame or not AuctionFrame:IsVisible() then + print("Please visit your local auctioneer before using this function.") + return + end + private.frame.refreshButton:SetText("Scanning") + private.frame.refreshButton:Disable() + AucAdvanced.Scan.StartScan(nil, nil, nil, nil, 5, nil, nil, nil, nil) +end + +function lib.GetPrice(link, faction, realm) + local playerName = UnitName("player") + local linkType, itemId, property, factor = decode(link) + local glypherMin1 = get("util.glypher.pricemodel.min1") + local glypherMin2 = get("util.glypher.pricemodel.min2") + local glypherMax = get("util.glypher.pricemodel.max") + local glypherUnderpct = get("util.glypher.pricemodel.underpct") + local glypherUseundercut = get("util.glypher.pricemodel.useundercut") + local glypherUndercut = get("util.glypher.pricemodel.undercut") + local glypherWhitelist = get("util.glypher.pricemodel.whitelist") + local glypherIgnoretime = get("util.glypher.pricemodel.ignoretime") + if (linkType ~= "item") then return end + itemId = tonumber(itemId) + property = tonumber(property) or 0 + factor = tonumber(factor) or 0 + local data = AucAdvanced.API.QueryImage({ + itemId = itemId, + suffix = property, + factor = factor, + }) + local auctions = #data + local playerLow = glypherMax * 2 + local competitorLow = glypherMax * 2 + local whitelistLow = glypherMax * 2 + for j = 1, #data do + local auction = AucAdvanced.API.UnpackImageItem(data[j]) + auction.buyoutPrice = (auction.buyoutPrice/auction.stackSize) + itemId = auction.itemId + local ink = get("util.glypher.inks."..itemId..".ink") or 43126 + local count = get("util.glypher.inks."..itemId..".count") or 2 + if ink == 43126 then + if count == 1 then + glypherMin = glypherMin1 + elseif count == 2 then + glypherMin = glypherMin2 + else + glypherMin = glypherMin2 + --print("Warning: Item " .. itemId .. " has not been scanned by Glypher:Get Profitable Glyphs, assuming for now that 2 inks are required to make.") + --set("util.glypher.inks."..itemId..".count", 2) + end + else + --print("Cannot find ink type " .. ink .. " for glyph " .. itemId .. " for pricing - check http://forums.norganna.org/8/ for information on an update to Auc-Util-Glypher") + glypherMin = glypherMin2 -- fallback + end + if auction.stackSize == 1 then + if auction.sellerName == playerName then + if auction.buyoutPrice < playerLow then + playerLow = auction.buyoutPrice + end + elseif auction.sellerName ~= "" and string.find(":" .. glypherWhitelist .. ":", ":" .. auction.sellerName .. ":") then + if auction.buyoutPrice < whitelistLow then + if auction.buyoutPrice >= glypherMin then + --this if we're in is so that we don't even both with prices below our min + whitelistLow = auction.buyoutPrice + end + end + else + if auction.timeLeft > glypherIgnoretime then + if auction.buyoutPrice < competitorLow then + if auction.buyoutPrice >= glypherMin then + competitorLow = auction.buyoutPrice + end + end + end + end + end + end + local newPrice = glypherMax + if glypherUseundercut then + newPrice = competitorLow - glypherUndercut + else + newPrice = floor(competitorLow*((100-glypherUnderpct)/100)) + end + if newPrice > glypherMax then + newPrice = glypherMax + elseif newPrice < glypherMin then + --what do we do with the new price in this case? + --ideally we should match the 2nd lowest price minus undercut + newPrice = glypherMin + end + return newPrice +end + +function lib.IsValidAlgorithm(link) + if link then + local _, _, _, _, _, itemType, itemSubtype = GetItemInfo(link) + if (GLYPH_TYPE == itemType) then + return true + end + else + return true + end + return false +end +--Checks datastore and internal scans for current auctions, bag and bank glyph supplies +function private.GetStock(itemId) + local stockCount = 0 + local tooltip = {} + local name = UnitName("player") + local realm = GetRealmName() + local account = "Default" + local currentcharacter = format("%s.%s.%s", account, realm, name) + local altList = get("util.glypher.altlist") + local gVault = get("util.glypher.gvault") + + if DataStore and DataStore:IsModuleEnabled("DataStore_Auctions") then -- Auctions & Bids + for characterName, character in pairs(DataStore:GetCharacters(realm, account)) do + if string.find(":" .. name .. ":" .. altList .. ":", ":" .. characterName .. ":") then + local add = (DataStore:GetAuctionHouseItemCount(character, itemId) or 0) + stockCount = stockCount + add + if add > 0 then table.insert(tooltip, " Auctions " .. characterName .. " = " .. add) end + end + end + else + if private.auctionCount and BeanCounter then + stockCount = stockCount + (private.auctionCount[itemId] or 0) + end + end + if DataStore and DataStore:IsModuleEnabled("DataStore_Containers") then -- Bags, Bank, and Guild Bank + for characterName, character in pairs(DataStore:GetCharacters(realm, account)) do + if string.find(":" .. name .. ":" .. altList .. ":", ":" .. characterName .. ":") then + local bagCount, bankCount = DataStore:GetContainerItemCount(character, itemId) + stockCount = stockCount + bagCount + bankCount + if bagCount > 0 then table.insert(tooltip, " Bags " .. characterName .. " = " .. bagCount) end + if bankCount > 0 then table.insert(tooltip, " Bank " .. characterName .. " = " .. bankCount) end + end + end + if gVault ~= "" then + local guildKey = account .. "." .. realm .. "." .. gVault + local guildCount = DataStore:GetGuildBankItemCount(guildKey, itemId) or 0 + stockCount = stockCount + guildCount + if guildCount > 0 then table.insert(tooltip, " Vault " .. gVault .. " = " .. guildCount) end + end + else + stockCount = stockCount + GetItemCount(itemId, true) + end + if DataStore and DataStore:IsModuleEnabled("DataStore_Mails") then -- Mails + for characterName, character in pairs(DataStore:GetCharacters(realm, account)) do + if string.find(":" .. name .. ":" .. altList .. ":", ":" .. characterName .. ":") then + local add = (DataStore:GetMailItemCount(character, itemId) or 0) + stockCount = stockCount + add + if add > 0 then table.insert(tooltip, " Mail " .. characterName .. " = " .. add) end + end + end + end + return stockCount, tooltip +end diff --git a/Auc-Advanced/Modules/Auc-Util-Glypher/Images/Glypher.blp b/Auc-Advanced/Modules/Auc-Util-Glypher/Images/Glypher.blp new file mode 100644 index 0000000..fd2168f Binary files /dev/null and b/Auc-Advanced/Modules/Auc-Util-Glypher/Images/Glypher.blp differ diff --git a/Auc-Advanced/Modules/Auc-Util-Glypher/Images/Glypher.tga b/Auc-Advanced/Modules/Auc-Util-Glypher/Images/Glypher.tga new file mode 100644 index 0000000..461ed4d Binary files /dev/null and b/Auc-Advanced/Modules/Auc-Util-Glypher/Images/Glypher.tga differ diff --git a/Auc-Advanced/Modules/Auc-Util-Glypher/Images/GlypherE.blp b/Auc-Advanced/Modules/Auc-Util-Glypher/Images/GlypherE.blp new file mode 100644 index 0000000..6a059a1 Binary files /dev/null and b/Auc-Advanced/Modules/Auc-Util-Glypher/Images/GlypherE.blp differ diff --git a/Auc-Advanced/Modules/Auc-Util-GlypherPost/Auc-Util-GlypherPost.toc b/Auc-Advanced/Modules/Auc-Util-GlypherPost/Auc-Util-GlypherPost.toc new file mode 100644 index 0000000..41f4a64 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-GlypherPost/Auc-Util-GlypherPost.toc @@ -0,0 +1,11 @@ +## Title: Auc:Util:GlypherPost +## Interface: 30300 +## Dependancies: Auc-Advanced +## +## Version: 5.6.4409 (KangaII) +## Revision: $Id: Auc-Util-GlypherPost.toc 4383 2009-08-06 16:05:57Z Nechckn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## +Embed.xml diff --git a/Auc-Advanced/Modules/Auc-Util-GlypherPost/Embed.xml b/Auc-Advanced/Modules/Auc-Util-GlypherPost/Embed.xml new file mode 100644 index 0000000..407eeb6 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-GlypherPost/Embed.xml @@ -0,0 +1,7 @@ + + + + diff --git a/Auc-Advanced/Modules/Auc-Util-GlypherPost/GlypherPost.lua b/Auc-Advanced/Modules/Auc-Util-GlypherPost/GlypherPost.lua new file mode 100644 index 0000000..928e9c3 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-GlypherPost/GlypherPost.lua @@ -0,0 +1,588 @@ +--[[ + Auctioneer Advanced - Price Level Utility module + Version: 5.6.4409 (KangaII) + Revision: $Id: GlypherPost.lua 3882 2008-12-02 16:36:58Z kandoko $ + URL: http://auctioneeraddon.com/ + + This is an Auctioneer Advanced module that does something nifty. + + 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 libName = "GlypherPost" +local libType = "Util" + +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end +local print,decode,_,_,replicate,empty,get,set,default,debugPrint,fill, _TRANS = AucAdvanced.GetModuleLocals() + +local _, _, _, _, _, GLYPH_TYPE = GetItemInfo(42956) +if not GLYPH_TYPE then GLYPH_TYPE = "Glyph" end + + +--[[ +The following functions are part of the module's exposed methods: + GetName() (required) Should return this module's full name + CommandHandler() (optional) Slash command handler for this module + Processor() (optional) Processes messages sent by Auctioneer + ScanProcessor() (optional) Processes items from the scan manager +* GetPrice() (required) Returns estimated price for item link +* GetPriceColumns() (optional) Returns the column names for GetPrice + OnLoad() (optional) Receives load message for all modules + + (*) Only implemented in stats modules; util modules do not provide +]] + +function lib.GetName() + return libName +end + +function lib.Processor(callbackType, ...) + if (callbackType == "tooltip") then + --Called when the tooltip is being drawn. + lib.ProcessTooltip(...) + elseif (callbackType == "config") then + --Called when you should build your Configator tab. + private.SetupConfigGui(...) + elseif (callbackType == "listupdate") then + --Called when the AH Browse screen receives an update. + elseif (callbackType == "configchanged") then + --Called when your config options (if Configator) have been changed. + if private.gui then private.gui:Refresh() end + end +end + +lib.Processors = {} +function lib.Processors.toolip(callbackType, ...) + --Called when the tooltip is being drawn. + lib.ProcessTooltip(...) +end + +function lib.Processors.config(callbackType, ...) + --Called when you should build your Configator tab. + private.SetupConfigGui(...) +end + +function lib.Processors.configchanged(callbackType, ...) + --Called when your config options (if Configator) have been changed. + if private.gui then private.gui:Refresh() end +end + + + +function lib.ProcessTooltip(frame, name, hyperlink, quality, quantity, cost, additional) + -- In this function, you are afforded the opportunity to add data to the tooltip should you so + -- desire. You are passed a hyperlink, and it's up to you to determine whether or what you should + -- display in the tooltip. +end + + +function lib.OnLoad() + --This function is called when your variables have been loaded. + --You should also set your Configator defaults here + + print("AucAdvanced: {{"..libType..":"..libName.."}} loaded!") + +private.durations = { + { 720, _TRANS('APPR_Interface_12Hours') },--12 hours + { 1440, _TRANS('APPR_Interface_24Hours') },--24 hours + { 2880, _TRANS('APPR_Interface_48Hours') },--48 hours +} + + default("util.glypherpost.bulk", true) + default("util.glypherpost.duration", 1440) + default("util.glypherpost.fixed.bid", 0) + default("util.glypherpost.fixed.buy", 0) + default("util.glypherpost.ignore", false) + default("util.glypherpost.match", false) + default("util.glypherpost.model", "default") + default("util.glypherpost.number", 1) + default("util.glypherpost.numberonly", true) + default("util.glypherpost.smartonly", false) + default("util.glypherpost.allowunders", 0) + default("util.glypherpost.stack", 1) + default("util.glypherpost.salesbased", false) + default("util.glypherpost.minpost", 0) + default("util.glypherpost.maxpost", 4) + default("util.glypherpost.days", 7) + default("util.glypherpost.hours", 24) + default("util.glypherpost.development", false) + default("util.glypherpost.splitdelay", 100) -- delay for each split in ms + default("util.glypherpost.spliterrdelay", 250) -- delay for each split in ms AFTER a splitting error + + +--tshea local curDurationMins = private.durations[curDurationIdx][1] + --tshea local curDurationText = private.durations[curDurationIdx][2] + +end + +--[[ Local functions ]]-- + +function private.SetupConfigGui(gui) + -- The defaults for the following settings are set in the lib.OnLoad function + local id = gui:AddTab(libName) + local frame = gui.tabs[id].content + private.gui = gui + private.id = id + private.frame = frame + + frame.SetButtonTooltip = function(text) + if text and get("util.appraiser.buttontips") then + GameTooltip:SetOwner(self, "ANCHOR_BOTTOMRIGHT") + GameTooltip:SetText(text) + end + end + + gui:AddHelp(id, "what is GlypherPost?", + "What is GlypherPost?", + "GlyherPost is a work-in-progress. Its goal is to assist in quickly mass-posting glyphs in an easy-to-configure way." + ) + + gui:MakeScrollable(id) + gui:AddControl(id, "Header", 0, libName.." options") + + --gui:AddControl(id, "Checkbox", 0.5, 1, "util.glypherpost.ignore", "Hide glyphs") + --gui:AddTip(id, "After posting, hide glyphs from Appraiser. Will not affect future GlypherPosts.") + gui:AddControl(id, "Checkbox", 0.5, 1, "util.glypherpost.bulk", "Enable Batch Posting") + gui:AddTip(id, "After posting, enable batch posting in Appraiser. Does not affect GlypherPost.") + + --local last = gui:GetLast(id) + gui:AddControl(id, "NumeriSlider", 0, 1, "util.glypherpost.stack", 1, 20, 1, "%sStack Size", private.stackFormat) + gui:AddTip(id, "Number of glyphs per stack. This should probably be set to 1.") + --gui:SetLast(id, last) + --gui:AddControl(id, "MoneyFramePinned", 0.5, 1, "util.glypherpost.fixed.bid", 0, 101010, "Bid Each") + --gui:AddTip(id, "Fixed bid amount. You should probably leave this blank.") + + --last = gui:GetLast(id) + gui:AddControl(id, "NumeriSlider", 0, 1, "util.glypherpost.number", 1, 20, 1, "%sNumber to Post", private.numberFormat) + gui:AddTip(id, "Number of stacks to post. If you're using sales-based posting, this will be ignored.") + --gui:SetLast(id,last) + --gui:AddControl(id, "MoneyFramePinned", 0.5, 1, "util.glypherpost.fixed.buy", 0, 101010, "Buy Each") + --gui:AddTip(id, "Fixed buy amount. You should probably leave this blank.") + + local last = gui:GetLast(id) + gui:AddControl(id, "Checkbox", 0, 1, "util.glypherpost.numberonly", "Only") + gui:AddTip(id, "Have a maximum of the number of stacks selected on the auctionhouse at any time.") + gui:SetLast(id,last) + gui:AddControl(id, "Checkbox", 0.125, 1, "util.glypherpost.smartonly", "Smart") + gui:AddTip(id, "Have a maximum of the number of stacks selected - which you are the lowest priced seller - on the auctionhouse at any time. This could result in more stacks being posted, but you won't have to cancel your undercut auctions to have the lowest-priced auctions. This might require that you stock a lot more glyphs if you use this often.") + + last = gui:GetLast(id) + gui:AddControl(id, "TinySlider", 0.0625, 1, "util.glypherpost.allowunders", -1, 10, 1, "Allow Competitors: %s", private.allowundersFormat) + gui:AddTip(id, "Allow posts if X undercutters are below the lowest price you can post at. Setting to -1 disables this feature.") + gui:SetLast(id,last) + gui:AddControl(id, "Checkbox", 0.5, 1, "util.glypherpost.match", "Enable price matching") + gui:AddTip(id, "Enable price matching. If you're using the Glypher pricing model, this will be ignored.") + + --local curDuration = get("util.glypherpost.duration") or 0 + --for i=1, #private.durations do + -- if (curDuration == private.durations[i][1]) then + --frame.salebox.duration:SetValue(i) + -- duration = i + -- break + -- end + --end + + + local last = gui:GetLast(id) + gui:AddControl(id, "Slider", 0, 1, "util.glypherpost.duration", 1, 3, 1, "Duration: %s hours", private.durationFormat) + gui:AddTip(id, "The number of hours to post for.") + gui:SetLast(id,last) + gui:AddControl(id, "Selectbox", 0.5, 1, private.GetExtraPriceModels, "util.glypherpost.model", "Pricing Model") + gui:AddTip(id, "The pricing model to use for posting.") + + gui:AddControl(id, "Checkbox", 0, 1, "util.glypherpost.salesbased", "Base number of stacks on sales") + gui:AddTip(id, "Base the number of stacks on your sales history instead of the number configured above.") + gui:AddControl(id, "NumeriSlider", 0, 2, "util.glypherpost.minpost", 0, 10, 1, "Minimum to Post") + gui:AddTip(id, "Minimum number of stacks to post.") + gui:AddControl(id, "NumeriSlider", 0, 2, "util.glypherpost.maxpost", 1, 20, 1, "Maximum to Post") + gui:AddTip(id, "Maximum number of stacks to post.") + gui:AddControl(id, "NumeriSlider", 0, 2, "util.glypherpost.days", 1, 28, 1, "Days to Consider") + gui:AddTip(id, "Number of days of sales history to consider when evaluating how many to post.") + gui:AddControl(id, "NumeriSlider", 0, 2, "util.glypherpost.hours", 12, 96, 12, "%sHours of Glyphs to Post", private.hoursFormat) + gui:AddTip(id, "Number of hours worth of anticipated sales to post for.") + +--devel +if get("util.glypherpost.development") then + gui:AddControl(id, "Header", 0, "Developer-only access to tunable options.") + gui:AddControl(id, "NumeriSlider", 0, 1, "util.glypherpost.splitdelay", 10, 1000, 10, "Split Delay") + gui:AddControl(id, "NumeriSlider", 0, 1, "util.glypherpost.spliterrdelay", 20, 2000, 10, "Split Error Delay") +end + +--add button + + frame.refreshButton = CreateFrame("Button", nil, frame, "OptionsButtonTemplate") + frame.refreshButton:SetPoint("TOPLEFT", frame, "BOTTOMLEFT", 25, -100) + frame.refreshButton:SetWidth(110) + frame.refreshButton:SetText("Post Glyphs") + frame.refreshButton:SetScript("OnClick", function() private.postGlyphs() end) + frame.refreshButton:SetScript("OnEnter", function() return frame.SetButtonTooltip("Click to post glyphs as configured.") end) + frame.refreshButton:SetScript("OnLeave", function() return GameTooltip:Hide() end) + +end + + +local WARN = "|cffffff00" +local ERR = "|cffff0000" +local GREY = "|caaaaaaaa" + +function private.numberFormat() + local salesbased = get("util.glypherpost.salesbased") + if salesbased then + return GREY + else + return "" + end +end +function private.allowundersFormat() + local allowunders = get("util.glypherpost.allowunders") + if allowunders < 0 then + allowunders = "(disabled)" + end + return allowunders +end + +function private.stackFormat() + local stack = get("util.glypherpost.stack") + local format = "" + if stack > 1 then + format = WARN + end + return format +end +function private.durationHours() + local duration = get("util.glypherpost.duration") + local hours = 48 + if duration == 1 then + hours = 12 + elseif duration == 2 then + hours = 24 + elseif duration == 3 then + hours = 48 + end + return hours +end +function private.durationFormat() + local duration = get("util.glypherpost.duration") + local hours = private.durationHours() + local format = "" + if hours > get("util.glypherpost.hours") then + format = WARN + end + return format..hours, hours +end + +function private.hoursFormat() + local hours = private.durationHours() + local format = "" + if hours > get("util.glypherpost.hours") then + format = WARN + end + return format +end + +local coPG +local onupdateframe + +function private.postGlyphs() + if (not coPG) or (coroutine.status(coPG) == "dead") then + coPG = coroutine.create(private.copostGlyphs) + onupdateframe = CreateFrame("frame") + + onupdateframe:SetScript("OnUpdate", function() + local status, result + status = coroutine.status(coPG) + if status ~= "dead" then + status, result = coroutine.resume(coPG) + if not status and result then + error("Error in search coroutine: "..result.."\n\n{{{Coroutine Stack:}}}\n"..debugstack(coPG)); + end + end + end) + + local status, result = coroutine.resume(coPG) + if not status and result then + error("Error in search coroutine: "..result.."\n\n{{{Coroutine Stack:}}}\n"..debugstack(coPG)); + end + else + print("coroutine already running: "..coroutine.status(coPG)) + end + coroutine.resume(coPG) +end + +function private.copostGlyphs() + + if not AuctionFrame or not AuctionFrame:IsVisible() then + print("Please visit your local auctioneer before using this function.") + return + end + + -- before diving into pre-splitting, we're just going to get the basics working + -- following based on Norg's batch post macro: + + local bulk = get("util.glypherpost.bulk") + local hours = private.durationHours() + local duration = hours * 60 -- convert hours to minutes for posting + local ignore = get("util.glypherpost.ignore") + local match = get("util.glypherpost.match") + local model = get("util.glypherpost.model") + local number = get("util.glypherpost.number") +print("=================== number: " .. number) + local GPnumber = number + local numberonly = get("util.glypherpost.numberonly") + local stack = get("util.glypherpost.stack") + + local salesbased = get("util.glypherpost.salesbased") + local name = UnitName("player") + local realm = GetRealmName() + local account = "Default" + local days = get("util.glypherpost.days") --how far back in beancounter to look, in days + local minpost = get("util.glypherpost.minpost") + local maxpost = get("util.glypherpost.maxpost") + local days = get("util.glypherpost.days") + local hours = get("util.glypherpost.hours") + -- get our sales for 'days' period from bc + -- use the DataStore list if DataStore is present, otherwise current toon only + local altlist = get("util.glypher.altlist") + + local smartonly = get("util.glypherpost.smartonly") + local allowunders = get("util.glypherpost.allowunders") or -1 + --if smartonly then allowunders = -1 end + if smartonly then numberonly = false end + + + + + local serverKey = AucAdvanced.GetFaction() + local playerName = UnitName("player") + local aprframe = AucAdvAppraiserFrame + local glypherWhitelist = get("util.glypher.pricemodel.whitelist") or "" -- maybe we shouldn't use whitelist in case some users in it aren't ours? tshea + local glypherIgnoretime = get("util.glypher.pricemodel.ignoretime") or 0 +print("Posting glyphs...") +private.Timer("posting start") + --for pos = 1, #(aprframe.list) do + for pos = #(aprframe.list), 1, -1 do + private.Timer("pre-apr-yield") + coroutine.yield() + private.Timer("post-apr-yield") +print("pos: " .. pos) + local item = aprframe.list[pos] + if item then + private.Timer("if item") + local link = item[7] + local _, _, _, _, _, itemType, itemSubtype = GetItemInfo(link) + private.Timer("got GetItemInfo") + local linkType, itemId, property, factor = AucAdvanced.DecodeLink(link) + private.Timer("got DecodeLink") + if (linkType ~= "item") then break end + itemId = tonumber(itemId) + property = tonumber(property) or 0 + factor = tonumber(factor) or 0 + -- get the appraised price for our current item for later comparisons + local buy, bid, _, _, curModel = AucAdvanced.Modules.Util.Appraiser.GetPrice(link, serverKey) + private.Timer("got GetPrice") +--print("itemid/property/factor: " .. itemId .. "/" .. property .. "/" .. factor) + local data = AucAdvanced.API.QueryImage({ + itemId = itemId, + suffix = property, + factor = factor, + }) + + if itemType == GLYPH_TYPE then + private.Timer("is GLYPH_TYPE") + local mycurrent = 0 -- current auctions at or lower than our intended buy price + local cannotmatch = 0 + local auctions = #data + private.Timer("got #data") + for j = 1, #data do + private.Timer("data j: " .. j) + coroutine.yield() + private.Timer("data j post-yield") + if not data[j] then + print("breaking, data j nil") + break + end + local auction = AucAdvanced.API.UnpackImageItem(data[j]) + private.Timer("got UnpackiImageItem") + auction.buyoutPrice = (auction.buyoutPrice/auction.stackSize) + if auction.stackSize <= stack then -- we'll ignore larger stacks + --print("seller name: " .. auction.sellerName) + --print("my name: " .. playerName) + if auction.sellerName == playerName then -- it's ours + if (auction.buyoutPrice/auction.stackSize) <= buy then + mycurrent = mycurrent + 1 + end + elseif (auction.buyoutPrice/auction.stackSize) < buy then + cannotmatch = cannotmatch + 1 + end + --auction.sellerName + --auction.buyoutPrice + --auction.stackSize + end + end + + -- set up this item's appraiser settings, backing up the old + local sig = item[1] + + if salesbased then +--print("salesbased") + local bcSold = 0 + local bcProfit = 0 + if DataStore and DataStore:IsModuleEnabled("DataStore_Auctions") then -- Auctions & Bids + for characterName, character in pairs(DataStore:GetCharacters(realm, account)) do + if string.find(":" .. name .. ":" .. altlist .. ":", ":" .. characterName .. ":") then + bcSold = bcSold + (BeanCounter.API.getAHSoldFailed(characterName, link, days) or 0) + end + end + else + bcSold = BeanCounter.API.getAHSoldFailed(UnitName("player"), link, days) or 0 + end + number = floor(bcSold/(days*24)*hours+.5) + if number < minpost then number = minpost end + if number > maxpost then number = maxpost end + local failed = 0 + if DataStore and DataStore:IsModuleEnabled("DataStore_Auctions") then -- Auctions & Bids + for characterName, character in pairs(DataStore:GetCharacters(realm, account)) do + if string.find(":" .. name .. ":" .. altlist .. ":", ":" .. characterName .. ":") then + local _, characterFailed = BeanCounter.API.getAHSoldFailed(characterName, link) + if characterFailed then failed = failed + characterFailed end + end + end + else + local _, characterFailed = BeanCounter.API.getAHSoldFailed(UnitName("player"), link) + if characterFailed then failed = characterFailed end + end + if failed < 40 then + number = maxpost + end +--print("========== salesbased " .. link .. " = " .. number) + else +--print("not salesbased") + local auctioncount = 0 + if private.auctionCount and BeanCounter then + auctioncount = (private.auctionCount[itemName] or 0) + end +--print("auctioncount: " .. auctioncount) +--print("number: " .. GPnumber) + --if numberonly and (auctioncount < (GPnumber * stack)) then + -- number = floor(((GPnumber * stack) - auctioncount) / stack) +--print("post: " .. number) + --else + --number = GPnumber end + --end + end + + if smartonly then + -- set number to post + --set("util.appraiser.item."..sig..".numberonly", false) -- set to false because we're going to determine the quantity manually + --if mycurrent >= number then + number = number - mycurrent + if number < 0 then number = 0 end + --end + end + if allowunders >= 0 then + -- if there are more than allowunders under our current posting price, then set our number to post to zero +--print(link .. ": " .. cannotmatch .. " > " .. allowunders .. " then number = 0") + if cannotmatch > allowunders then number = 0 end + end + + local stock = GetItemCount(itemId, false) + if (number * stack) > stock then + number = floor(stock/stack) + end + + if numberonly then number = GPnumber end + + set("util.appraiser.item."..sig..".bulk", bulk) + set("util.appraiser.item."..sig..".duration", duration) + set("util.appraiser.item."..sig..".ignore", ignore) + set("util.appraiser.item."..sig..".match", match) + set("util.appraiser.item."..sig..".model", model) + set("util.appraiser.item."..sig..".number", number) + set("util.appraiser.item."..sig..".numberonly", numberonly) + set("util.appraiser.item."..sig..".stack", stack) + if number > 0 then + aprframe.PostBySig(sig) + --set("util.appraiser.item."..sig..".numberonly", numberonly) + print("posted " .. link) + print("number: " .. number) + elseif numberonly then + set("util.appraiser.item."..sig..".numberonly", numberonly) + set("util.appraiser.item."..sig..".number", GPnumber) + end + -- then restore old settings +private.Timer("starting settings restore") + --for pos = 1, #(settings) do + -- coroutine.yield() + -- local setting = settings[pos] + -- set("util.appraiser.item."..sig.."."..setting,orig[pos]) + --end +private.Timer("ending settings restore") + end + end + end + +end + +local timertext = "start" +local timer = time() +function private.Timer(newtimertext) + --local newtimer = time() + --print(timertext .. "->" .. newtimertext .. ": " .. newtimer-timer) + --timertext = newtimertext + --timer = newtimer +end + +local itemLink + +function private.GetExtraPriceModels(itemLink) +--print("Getting Extra Price Models") + local vals = {} + table.insert(vals, {"fixed", _TRANS('APPR_Interface_FixedPrice') })--Fixed price + table.insert(vals, {"default", _TRANS('APPR_Interface_Default') })--Default + table.insert(vals, {"market", _TRANS("UCUT_Interface_MarketValue")})--Market value (reusing Undercut's translation) + local algoList = AucAdvanced.API.GetAlgorithms(itemLink) + for pos, name in ipairs(algoList) do + if (name ~= lib.libName) then + table.insert(vals, {name, _TRANS('APPR_Interface_Stats').." "..name})--Stats: +--print(name .. " -> " .. _TRANS('APPR_Interface_Stats').." "..name) + end + end + return vals +end + +function private.GetNumbers() + return { {0, "Zero"}, {1, "One"}, {2, "Two"}, {3, "Three"}, {4, "Four"}, {5, "Five"}, {6, "Six"}, {7, "Seven"}, {8, "Eight"}, {9, "Nine"} } +end + +function private.Foo() +end + +function private.Bar() +end + +function private.Baz() +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.6/Auc-Advanced/Modules/Auc-Util-GlypherPost/GlypherPost.lua $", "$Rev: 3882 $") diff --git a/Auc-Advanced/Modules/Auc-Util-ItemSuggest/Auc-Util-ItemSuggest.lua b/Auc-Advanced/Modules/Auc-Util-ItemSuggest/Auc-Util-ItemSuggest.lua new file mode 100644 index 0000000..2e42b80 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-ItemSuggest/Auc-Util-ItemSuggest.lua @@ -0,0 +1,523 @@ +--[[ + Auctioneer - Item Suggest module + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: Auc-Util-ItemSuggest.lua 4840 2010-08-04 21:44:00Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This is an Auctioneer module that allows the added tooltip for suggesting + what should be done with an item based on weights and skills set. This module is also + used by other modules in 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. + + 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 libType, libName = "Util", "ItemSuggest" +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end +local print,decode,_,_,replicate,empty,get,set,default,debugPrint,fill = AucAdvanced.GetModuleLocals() +local GetAprPrice = AucAdvanced.Modules.Util.Appraiser.GetPrice +local AppraiserValue, DisenchantValue, ProspectValue, MillingValue, ConvertValue, VendorValue, bestmethod, bestvalue, _ +local resultcache +local cutRate = 0.05 -- "home" AH cut / broker fee. todo: Is it worth introducing a "neutral" AH option? + +function lib.GetName() + return libName +end + +function lib.Processor(callbackType, ...) + if (callbackType == "tooltip") then lib.ProcessTooltip(...) --Called when the tooltip is being drawn. + elseif (callbackType == "config") then lib.SetupConfigGui(...) --Called when you should build your Configator tab. + elseif (callbackType == "listupdate") then --Called when the AH Browse screen receives an update. + elseif (callbackType == "configchanged") then --Called when your config options (if Configator) have been changed. + resultcache = nil + elseif (callbackType == "scanstats") then + resultcache = nil + end +end + +lib.Processors = {} +function lib.Processors.tooltip(callbackType, ...) + lib.ProcessTooltip(...) --Called when the tooltip is being drawn. +end + +function lib.Processors.config(callbackType, ...) + lib.SetupConfigGui(...) --Called when you should build your Configator tab. +end + +function lib.Processors.configchanged(callbackType, ...) + resultcache = nil +end + +lib.Processors.scanstats = lib.Processors.configchanged + + + +function lib.ProcessTooltip(tooltip, name, hyperlink, quality, quantity, cost, additional) + if (get("util.itemsuggest.enablett")) then + local aimethod = lib.itemsuggest(hyperlink, quantity) + tooltip:AddLine("Suggestion: ".. aimethod.. " this item") + end +end + +function lib.OnLoad() + print("AucAdvanced: {{"..libType..":"..libName.."}} loaded!") + -- Check for invalid setting from older versions of ItemSuggest (<=r3872) + local validate, deplength = {[12]=true,[24]=true,[48]=true}, get("util.itemsuggest.deplength") + if not validate[deplength] then + deplength = tonumber(deplength) + deplength = validate[deplength] and deplength or 48 + set ("util.itemsuggest.deplength", deplength) + end +end + +local ahdeplength = { + {12, "12 hour"}, + {24, "24 hour"}, + {48, "48 hour"}, +} +default("util.itemsuggest.enablett", 1) --Enables Item Suggest from Item AI to be displayed in tooltip +default("util.itemsuggest.enchantskill", 450) -- Used for item AI +default("util.itemsuggest.jewelcraftskill", 450)-- Used for item AI +default("util.itemsuggest.inscriptionskill", 450)-- Used for item AI +default("util.itemsuggest.vendorweight", 100)-- Used for item AI +default("util.itemsuggest.auctionweight", 100)-- Used for item AI +default("util.itemsuggest.prospectweight", 100)-- Used for item AI +default("util.itemsuggest.millingweight", 100)-- Used for item AI +default("util.itemsuggest.disenchantweight", 100)-- Used for item AI +default("util.itemsuggest.convertweight", 100)-- Used for item AI +default("util.itemsuggest.relisttimes", 1)-- Used for item AI +default("util.itemsuggest.includebrokerage", 1)-- Used for item AI +default("util.itemsuggest.includedeposit", 1)-- Used for item AI +default("util.itemsuggest.deplength", 48) + +function lib.SetupConfigGui(gui) + local id = gui:AddTab(libName) + gui:MakeScrollable(id) + + gui:AddHelp(id, "what itemsuggest", + "What is the ItemSuggest module?", + "ItemSuggest adds a tooltip line that suggests whether or not to auction, vendor, disenchant, prospect, mill or convert that item.") + + gui:AddControl(id, "Header", 0, "ItemSuggest Options") + gui:AddControl(id, "Checkbox", 0, 1, "util.itemsuggest.enablett", "Display ItemSuggest tooltips") + gui:AddTip(id, "If enabled, will show ItemSuggest tooltip information.") + + gui:AddControl(id, "Header", 0, "Skill usage Limits") + gui:AddControl(id, "WideSlider", 0, 2, "util.itemsuggest.enchantskill", 0, 450, 5, "Max Enchanting Skill On Realm: %s") + gui:AddTip(id, "Set ItemSuggest limits based upon Enchanting skill for your characters on this realm.") + gui:AddControl(id, "WideSlider", 0, 2, "util.itemsuggest.jewelcraftskill", 0, 450, 5, "Max JewelCrafting Skill On Realm: %s") + gui:AddTip(id, "Set ItemSuggest limits based upon Jewelcrafting skill for your characters on this realm.") + gui:AddControl(id, "WideSlider", 0, 2, "util.itemsuggest.inscriptionskill", 0, 450, 5, "Max Inscription Skill On Realm: %s") + gui:AddTip(id, "Set ItemSuggest limits based upon Inscription skill for your characters on this realm.") + + gui:AddControl(id, "Header", 0, "ItemSuggest Recommendation Bias") + gui:AddControl(id, "WideSlider", 0, 2, "util.itemsuggest.vendorweight", 0, 200, 1, "Vendor Bias %s") + gui:AddTip(id, "Weight ItemSuggest recommendations for vendor resale higher or lower.") + gui:AddControl(id, "WideSlider", 0, 2, "util.itemsuggest.auctionweight", 0, 200, 1, "Auction Bias %s") + gui:AddTip(id, "Weight ItemSuggest recommendations for auction resale higher or lower.") + gui:AddControl(id, "WideSlider", 0, 2, "util.itemsuggest.disenchantweight", 0, 200, 1, "Disenchant Bias %s") + gui:AddTip(id, "Weight ItemSuggest recommendations for Disenchanting higher or lower.") + gui:AddControl(id, "WideSlider", 0, 2, "util.itemsuggest.prospectweight", 0, 200, 1, "Prospect Bias %s") + gui:AddTip(id, "Weight ItemSuggest recommendations for Prospecting higher or lower.") + gui:AddControl(id, "WideSlider", 0, 2, "util.itemsuggest.millingweight", 0, 200, 1, "Milling Bias %s") + gui:AddTip(id, "Weight ItemSuggest recommendations for Milling higher or lower.") + gui:AddControl(id, "WideSlider", 0, 2, "util.itemsuggest.convertweight", 0, 200, 1, "Conversion Bias %s") + gui:AddTip(id, "Weight ItemSuggest recommendations for Conversion higher or lower.") + + gui:AddControl(id, "Header", 0, "Deposit cost influence") + gui:AddControl(id, "Checkbox", 0, 1, "util.itemsuggest.includedeposit", "Include deposit costs") + gui:AddTip(id, "Set whether or not to include Auction House deposit costs as part of ItemSuggest tooltip calculations.") + gui:AddControl(id, "Selectbox", 0, 1, ahdeplength, "util.itemsuggest.deplength", "Base deposits on what length of auction.") + gui:AddTip(id, "If Auction House deposit costs are included, set the default Auction period used for purposes of calculating Auction House deposit costs.") + gui:AddControl(id, "WideSlider", 0, 2, "util.itemsuggest.relisttimes", 1, 20, 0.1, "Average # of listings: %0.1fx") + gui:AddTip(id, "Set the estimated average number of times an auction item is relisted.") + gui:AddControl(id, "Checkbox", 0, 1, "util.itemsuggest.includebrokerage", "Include AH brokerage costs") + gui:AddTip(id, "Set whether or not to include Auction House brokerage costs as part of ItemSuggest tooltip calculations.") + +end + +function lib.itemsuggest(hyperlink, quantity) + if resultcache and resultcache[hyperlink] then + local bestmethod, bestvalue = strsplit(";", resultcache[hyperlink]) + return bestmethod, tonumber(bestvalue) + end + -- Determine Base Values + if (quantity == nil) then quantity = 1 end + VendorValue = lib.GetVendorValue(hyperlink, quantity) or 0 + AppraiserValue = lib.GetAppraiserValue(hyperlink, quantity) or 0 + ConvertValue = lib.GetConvertValue (hyperlink, quantity) or 0 + + if (get("util.itemsuggest.jewelcraftskill") == 0) then + ProspectValue = 0 + else + ProspectValue = lib.GetProspectValue(hyperlink, quantity) or 0 + end + + if (get("util.itemsuggest.inscriptionskill") == 0) then + MillingValue = 0 + else + MillingValue = lib.GetMillingValue(hyperlink, quantity) or 0 + end + + if (get("util.itemsuggest.enchantskill") == 0) then + DisenchantValue = 0 + else + DisenchantValue = lib.GetDisenchantValue(hyperlink, quantity) or 0 + end + + -- Adjust final values based on custom weights by enduser + local adjustment = get("util.itemsuggest.vendorweight") or 0 + VendorValue = VendorValue * adjustment / 100 + adjustment = get("util.itemsuggest.auctionweight") or 0 + AppraiserValue = AppraiserValue * adjustment / 100 + adjustment = get("util.itemsuggest.prospectweight") or 0 + ProspectValue = ProspectValue * adjustment / 100 + adjustment = get("util.itemsuggest.millingweight") or 0 + MillingValue = MillingValue * adjustment / 100 + adjustment = get("util.itemsuggest.disenchantweight") or 0 + DisenchantValue = DisenchantValue * adjustment / 100 + adjustment = get("util.itemsuggest.convertweight") or 0 + ConvertValue = ConvertValue * adjustment / 100 + + -- Determine which method 'wins' the battle + bestvalue = math.max(0, VendorValue, AppraiserValue, ProspectValue, MillingValue, ConvertValue, DisenchantValue) + bestmethod = "Unknown" + if bestvalue == 0 then + bestmethod = "Unknown" + bestvalue = "Unknown" + elseif bestvalue == VendorValue then + bestmethod = "Vendor" + elseif bestvalue == AppraiserValue then + bestmethod = "Auction" + elseif bestvalue == ProspectValue then + bestmethod = "Prospect" + elseif bestvalue == MillingValue then + bestmethod = "Mill" + elseif bestvalue == DisenchantValue then + bestmethod = "Disenchant" + elseif bestvalue == ConvertValue then + bestmethod = "Convert" + end + + if not resultcache then resultcache = {} end + resultcache[hyperlink] = strjoin(";", bestmethod, tostring(bestvalue)) + -- Hand the winner back to caller... + return bestmethod, bestvalue +end + +function lib.GetAppraiserValue(hyperlink, quantity) + AppraiserValue = GetAprPrice(hyperlink, nil, true) or 0 + AppraiserValue = AppraiserValue * quantity + local brokerRate, depositRate = 0.05, 0.05 + if (get("util.itemsuggest.includebrokerage")) then + AppraiserValue = AppraiserValue - AppraiserValue * brokerRate + end + if (get("util.itemsuggest.includedeposit")) then + local aadvdepcost = GetDepositCost(hyperlink, get("util.itemsuggest.deplength"), nil, quantity) or 0 + local depcost = aadvdepcost * get("util.itemsuggest.relisttimes") + AppraiserValue = AppraiserValue - depcost + end + +return AppraiserValue end + +function lib.GetDisenchantValue(hyperlink, quantity) + if not (Enchantrix and Enchantrix.Storage) then return end + local DisenchantValue = 0 + local _, _, iQual, iLevel = GetItemInfo(hyperlink) + if (iQual == nil or iQual <= 1 or iLevel == nil) then return end + local skillneeded = Enchantrix.Util.DisenchantSkillRequiredForItemLevel(iLevel, iQual) + local market + + if (skillneeded > get("util.itemsuggest.enchantskill")) then + return DisenchantValue + else + _, _, _, market = Enchantrix.Storage.GetItemDisenchantTotals(hyperlink) + + if (market == 0) then + return DisenchantValue + end + end + + local adjusted = market or 0 + + if (get("util.itemsuggest.includebrokerage")) then + local brokerRate, depositRate = 0.05, 0.05 + local amount = (adjusted * brokerRate) + adjusted = adjusted - amount + end + + DisenchantValue = adjusted * quantity -- quantity may be more than 1 when mousing over Appraiser +return DisenchantValue end + +function lib.GetProspectValue(hyperlink, quantity) + if not Enchantrix then return end + local jcSkillRequired = Enchantrix.Util.JewelCraftSkillRequiredForItem(hyperlink) + if not jcSkillRequired or jcSkillRequired > get("util.itemsuggest.jewelcraftskill") then + return + end + local prospects = Enchantrix.Storage.GetItemProspects(hyperlink) + if not prospects then return end + + local marketTotal, depositTotal = 0, 0 + local depositAucLength, depositRelistTimes + local includeDeposit = get("util.itemsuggest.includedeposit") + if includeDeposit then + depositAucLength = get("util.itemsuggest.deplength") + depositRelistTimes = get("util.itemsuggest.relisttimes") + end + + for result, yield in pairs(prospects) do + -- adjust for stack size + yield = yield * quantity / 5 + + -- fetch value of each result from Enchantrix + local _, _, _, market = Enchantrix.Util.GetReagentPrice(result) + market = market or 0 -- treat nil value as 0 + marketTotal = marketTotal + market * yield + + -- calculate deposit for each result + if includeDeposit then + local aadvdepcost = GetDepositCost(result, depositAucLength, nil, nil) or 0 + depositTotal = depositTotal + aadvdepcost * yield * depositRelistTimes + end + end + + -- Adjustments + if get("util.itemsuggest.includebrokerage") then -- Auction House cut + marketTotal = marketTotal * (1 - cutRate) + end + marketTotal = marketTotal - depositTotal + + return marketTotal +end + +function lib.GetMillingValue(hyperlink, quantity) + if not Enchantrix then return end + local insSkillRequired = Enchantrix.Util.InscriptionSkillRequiredForItem(hyperlink) + if not insSkillRequired or insSkillRequired > get("util.itemsuggest.inscriptionskill") then + return + end + local pigments = Enchantrix.Storage.GetItemMilling(hyperlink) + if not pigments then return end + + local marketTotal, depositTotal = 0, 0 + local depositAucLength, depositRelistTimes + local includeDeposit = get("util.itemsuggest.includedeposit") + if includeDeposit then + depositAucLength = get("util.itemsuggest.deplength") + depositRelistTimes = get("util.itemsuggest.relisttimes") + end + + for result, yield in pairs(pigments) do + -- adjust for stack size + yield = yield * quantity / 5 + + -- fetch value of each result from Enchantrix + local _, _, _, market = Enchantrix.Util.GetReagentPrice(result) + market = market or 0 -- treat nil value as 0 + marketTotal = marketTotal + market * yield + + -- calculate deposit for each result + if includeDeposit then + local aadvdepcost = GetDepositCost(result, depositAucLength, nil, nil) or 0 + depositTotal = depositTotal + aadvdepcost * yield * depositRelistTimes + end + end + + -- Adjustments + if get("util.itemsuggest.includebrokerage") then -- Auction House cut + marketTotal = marketTotal * (1 - cutRate) + end + marketTotal = marketTotal - depositTotal + + return marketTotal +end + +local findConvertable = {} +do -- build table for Converter-suggest + -- Copied and modified from SearcherConverter.lua + -- todo: possibly modify SearchUI to export its table, and reuse the same table here? + -- Set our constants + --Essences + local GCOSMIC = 34055 + local GPLANAR = 22446 + local GETERNAL = 16203 + local GNETHER = 11175 + local GMYSTIC = 11135 + local GASTRAL = 11082 + local GMAGIC = 10939 + local LCOSMIC = 34056 + local LPLANAR = 22447 + local LETERNAL = 16202 + local LNETHER = 11174 + local LMYSTIC = 11134 + local LASTRAL = 10998 + local LMAGIC = 10938 + --Motes/Primals + local PAIR = 22451 + local MAIR = 22572 + local PEARTH= 22452 + local MEARTH = 22573 + local PFIRE = 21884 + local MFIRE = 22574 + local PLIFE = 21886 + local MLIFE = 22575 + local PMANA = 22457 + local MMANA = 22576 + local PSHADOW = 22456 + local MSHADOW = 22577 + local PWATER = 21885 + local MWATER = 22578 + --Crystallized/Eternal + local CAIR = 37700 + local EAIR = 35623 + local CEARTH = 37701 + local EEARTH = 35624 + local CSHADOW = 37703 + local ESHADOW = 35627 + local CLIFE = 37704 + local ELIFE = 35625 + local CFIRE = 37702 + local EFIRE = 36860 + local CWATER = 37705 + local EWATER = 35622 + --Depleted items + local DCBRACER = 32676 -- Depleted Cloth Bracers + local DCBRACERTO = 32655 -- Crystalweave Bracers + local DMGAUNTLETS = 32675 -- Depleted Mail Gauntlets + local DMGAUNTLETSTO = 32656 -- Crystalhide Handwraps + local DBADGE = 32672 -- Depleted Badge + local DBADGETO = 32658 -- Badge of Tenacity + local DCLOAK = 32677 -- Depleted Cloak + local DCLOAKTO = 32665 -- Crystalweave Cape + local DDAGGER = 32673 -- Depleted Dagger + local DDAGGERTO = 32659 -- Crystal-Infused Shiv + local DMACE = 32671 -- Depleted Mace + local DMACETO = 32661 -- Apexis Crystal Mace + local DRING = 32678 -- Depleted Ring + local DRINGTO = 32664 -- Dreamcrystal Band + local DSTAFF = 32679 -- Depleted Staff + local DSTAFFTO = 32662 -- Flaming Quartz Staff + local DSWORD = 32674 -- Depleted Sword + local DSWORDTO = 32660 -- Crystalforged Sword + local DTHAXE = 32670 -- Depleted Two-Handed Axe + local DTHAXETO = 32663 -- Apexis Cleaver + + -- Temporary tables to help build the working table + -- To add new conversions, edit these tables + + -- TWO WAY Tables + + local lesser_greater = { + [LCOSMIC] = GCOSMIC, + [LPLANAR] = GPLANAR, + [LETERNAL] = GETERNAL, + [LNETHER] = GNETHER, + [LMYSTIC] = GMYSTIC, + [LASTRAL] = GASTRAL, + [LMAGIC] = GMAGIC, + } + local crystallized_eternal = { + [CAIR] = EAIR, + [CEARTH] = EEARTH, + [CSHADOW] = ESHADOW, + [CLIFE] = ELIFE, + [CFIRE] = EFIRE, + [CWATER] = EWATER, + } + + -- ONE WAY Tables + + local mote2primal = { + [MAIR] = PAIR, + [MEARTH] = PEARTH, + [MFIRE] = PFIRE, + [MLIFE] = PLIFE, + [MMANA] = PMANA, + [MSHADOW] = PSHADOW, + [MWATER] = PWATER, + } + local depleted2enhanced = { + [DCBRACER] = DCBRACERTO, + [DMGAUNTLETS] = DMGAUNTLETSTO, + [DBADGE] = DBADGETO, + [DCLOAK] = DCLOAKTO, + [DDAGGER] = DDAGGERTO, + [DMACE] = DMACETO, + [DRING] = DRINGTO, + [DSTAFF] = DSTAFFTO, + [DSWORD] = DSWORDTO, + [DTHAXE] = DTHAXETO, + } + + -- Build the table + -- ItemSuggest version + -- Two-way + for idl, idg in pairs (lesser_greater) do + findConvertable[idl] = {idg, 1/3} + findConvertable[idg] = {idl, 3} + end + for idc, ide in pairs (crystallized_eternal) do + findConvertable[idc] = {ide, 0.1} + findConvertable[ide] = {idc, 10} + end + -- One-way + for id, idto in pairs (mote2primal) do + findConvertable[id] = {idto, 0.1} + end + for id, idto in pairs (depleted2enhanced) do + findConvertable[id] = {idto, 1} + end +end + +function lib.GetConvertValue (hyperlink, quantity) + -- assume type(hyperlink) == "string" in all cases... if not, insert test here + local id = tonumber(strmatch(hyperlink, "item:(%d+):")) + local convert = findConvertable[id] + if not convert then return end + + id = convert[1] -- id of item we can convert to + + local market = GetAprPrice(id, nil, true) or 0 + market = market * quantity + if (get("util.itemsuggest.includebrokerage")) then + market = market * (1 - cutRate) + end + if (get("util.itemsuggest.includedeposit")) then + local aadvdepcost = GetDepositCost(id, get("util.itemsuggest.deplength"), nil, quantity) or 0 + market = market - aadvdepcost * get("util.itemsuggest.relisttimes") + end + + -- Adjust for yield + market = market * convert[2] + + return market +end + +function lib.GetVendorValue(hyperlink, quantity) + VendorValue = GetSellValue and GetSellValue(hyperlink) or 0 + VendorValue = VendorValue * quantity +return VendorValue end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-ItemSuggest/Auc-Util-ItemSuggest.lua $", "$Rev: 4840 $") diff --git a/Auc-Advanced/Modules/Auc-Util-ItemSuggest/Auc-Util-ItemSuggest.toc b/Auc-Advanced/Modules/Auc-Util-ItemSuggest/Auc-Util-ItemSuggest.toc new file mode 100644 index 0000000..3500025 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-ItemSuggest/Auc-Util-ItemSuggest.toc @@ -0,0 +1,14 @@ +## Title: Auc:Util:ItemSuggest +## Interface: 40000 +## OptionalDependencies: Auc-Util-Appraiser Enchantrix +## Dependancies: Auc-Advanced +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Auc-Util-ItemSuggest.toc 4933 2010-10-13 17:16:14Z Nechckn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## X-Max-Interface: 40000 +## + +Embed.xml diff --git a/Auc-Advanced/Modules/Auc-Util-ItemSuggest/Embed.xml b/Auc-Advanced/Modules/Auc-Util-ItemSuggest/Embed.xml new file mode 100644 index 0000000..1e51de7 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-ItemSuggest/Embed.xml @@ -0,0 +1,7 @@ + + + + diff --git a/Auc-Advanced/Modules/Auc-Util-PriceLevel/Auc-Util-PriceLevel.toc b/Auc-Advanced/Modules/Auc-Util-PriceLevel/Auc-Util-PriceLevel.toc new file mode 100644 index 0000000..ac46457 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-PriceLevel/Auc-Util-PriceLevel.toc @@ -0,0 +1,12 @@ +## Title: Auc:Util:Price Level +## Interface: 40000 +## Dependancies: Auc-Advanced +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Auc-Util-PriceLevel.toc 4933 2010-10-13 17:16:14Z Nechckn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## X-Max-Interface: 40000 +## +Embed.xml diff --git a/Auc-Advanced/Modules/Auc-Util-PriceLevel/Embed.xml b/Auc-Advanced/Modules/Auc-Util-PriceLevel/Embed.xml new file mode 100644 index 0000000..fb26b02 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-PriceLevel/Embed.xml @@ -0,0 +1,7 @@ + + + + diff --git a/Auc-Advanced/Modules/Auc-Util-PriceLevel/PriceLevel.lua b/Auc-Advanced/Modules/Auc-Util-PriceLevel/PriceLevel.lua new file mode 100644 index 0000000..49c4900 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-PriceLevel/PriceLevel.lua @@ -0,0 +1,333 @@ +--[[ + Auctioneer - Price Level Utility module + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: PriceLevel.lua 4880 2010-09-15 20:02:11Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This is an addon for World of Warcraft that adds a price level indicator + to auctions when browsing the Auction House, so that you may readily see + which items are bargains or overpriced at a glance. + + 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 libType, libName = "Util", "PriceLevel" +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end +local print,decode,_,_,replicate,empty,get,set,default,debugPrint,fill = AucAdvanced.GetModuleLocals() + +local data + +function lib.Processor(callbackType, ...) + if (callbackType == "tooltip") then + lib.ProcessTooltip(...) + elseif (callbackType == "config") then + private.SetupConfigGui(...) + elseif (callbackType == "listupdate") then + private.ListUpdate(...) + elseif (callbackType == "configchanged") then + if (AuctionFrameBrowse_Update) then + AuctionFrameBrowse_Update() + end + end +end + +lib.Processors = {} +function lib.Processors.tooltip(callbackType, ...) + lib.ProcessTooltip(...) +end + +function lib.Processors.config(callbackType, ...) + private.SetupConfigGui(...) +end + +function lib.Processors.listupdate(callbackType, ...) + private.ListUpdate(...) +end + +function lib.Processors.configchanged(callbackType, ...) + if (AuctionFrameBrowse_Update) then + AuctionFrameBrowse_Update() + end +end + + +function lib.ProcessTooltip(tooltip, name, hyperlink, quality, quantity, cost, additional) + -- In this function, you are afforded the opportunity to add data to the tooltip should you so + -- desire. You are passed a hyperlink, and it's up to you to determine whether or what you should + -- display in the tooltip. + if not get("util.pricelevel.single") then return end + + if not additional or not additional.buyoutPrice or not additional.minBid then return end + + local priceLevel, perItem, r,g,b = lib.CalcLevel(hyperlink, quantity, additional.minBid, additional.buyoutPrice) + if (not priceLevel) then return end + + tooltip:AddLine(("Price Level: %d%%"):format(priceLevel), perItem, r,g,b) +end + +function lib.OnLoad() + default("util.pricelevel.colorize", false) + default("util.pricelevel.single", true) + default("util.pricelevel.model", "market") + default("util.pricelevel.basis", "try") + default("util.pricelevel.blue", 0) + default("util.pricelevel.green", 50) + default("util.pricelevel.yellow", 80) + default("util.pricelevel.orange", 110) + default("util.pricelevel.red", 135) + default("util.pricelevel.opacity", 30) + default("util.pricelevel.gradient", true) + default("util.pricelevel.direction", "LEFT") + + set("util.pricelevel.blue", nil) -- blue is a fake slider for display only - always 0 + +end + +--[[ Local functions ]]-- + +function private.SetupConfigGui(gui) + -- The defaults for the following settings are set in the lib.OnLoad function + local id = gui:AddTab(libName, libType.." Modules") + + gui:AddHelp(id, "what is pricelevel", + "What is PriceLevel?", + "PriceLevel is an Auctioneer module that analyses the current market position with regard to the calculated value of the item.\n".. + "PriceLevel is all about determining if what Auctioneer thinks is what the rest of the market currently thinks. It's also about determining if the rest of the market is selling their stuff for crazy prices.\n".. + "What it all comes down to is the color... PriceLevel breaks the current market down into 5 categories: |cff3296ffWay underpriced|r, |cff19ff19Fairly underpriced|r, |cffffff00Just underpriced|r, |cffff9619Reasonable|r, and |cffff0000Overpriced|r. It also has options for adding the calculated level to the tooltip and in the browse window of the Auction House.") + + gui:AddControl(id, "Header", 0, libName.." Options") + gui:AddControl(id, "Checkbox", 0, 1, "util.pricelevel.single", "Show the PriceLevel and unit price in the tooltips") + gui:AddTip(id, "Enable this to display the PriceLevel information in the tooltip when you mouse over an item in your inventory") + + gui:AddHelp(id, "what is ahcolor", + "What does changing the Auction House items' colors do?", + "This will change the background of the items at the Auction House so that you can more easily identify bargains or overpriced items as you are browsing.") + + gui:AddControl(id, "Checkbox", 0, 1, "util.pricelevel.colorize", "Change the color of items in the Auction House") + gui:AddTip(id, "This option changes the color of the items lines in the Auction House so that you may more easily determine whether they are over or under priced prior to purchase") + gui:AddControl(id, "Slider", 0, 2, "util.pricelevel.opacity", 1, 100, 1, "Opacity level: %d%%") + gui:AddTip(id, "This controls the level of opacity for the colored bars in the Auction Browse window. (if enabled)") + gui:AddControl(id, "Checkbox", 0, 2, "util.pricelevel.gradient", "Use a gradient:") + gui:AddTip(id, "This causes the colored bars in the Auction Browse window to be drawn with a gradient instead of a solid color (if enabled).") + gui:AddControl(id, "Selectbox", 0, 3, { + {"LEFT", "Left"}, + {"RIGHT", "Right"}, + {"TOP", "Top"}, + {"BOTTOM", "Bottom"}, + }, "util.pricelevel.direction", "Pick the gradient direction") + gui:AddTip(id, "This determines the direction that the above gradient is drawn in for the Auction Browse window (if enabled).") + gui:AddControl(id, "Subhead", 0, "Price valuation method:") + gui:AddControl(id, "Selectbox", 0, 1, parent.selectorPriceModels, "util.pricelevel.model", "Pricing model to use for the valuation") + gui:AddTip(id, "The pricing model that is used to work out the calculated value of items at the Auction House.") + gui:AddControl(id, "Subhead", 0, "Price level basis:") + gui:AddControl(id, "Selectbox", 0, 1, { + {"cur", "Next bid price"}, + {"buy", "Buyout only"}, + {"try", "Buyout or bid"}, + }, "util.pricelevel.basis", "Which price to use for the PriceLevel") + gui:AddTip(id, "Selects which price to base the PriceLevel calculation off of.") + + gui:AddHelp(id, "what is basis", + "What is the PriceLevel basis?", + "The Auction House has both bids and buyout values to calculate from. You can select to price the item based off either exclusively the buyout or bid, or first the buyout if it exists, and then the bid") + + gui:AddControl(id, "Subhead", 0, "Pricing points:") + gui:AddControl(id, "WideSlider", 0, 1, "util.pricelevel.red", 0, 500, 5, "|cffff0000Red|r price level > %d%%") + gui:AddTip(id, "This determines the minimum level for an item to be counted as a red item.") + gui:AddControl(id, "WideSlider", 0, 1, "util.pricelevel.orange", 0, 500, 5, "|cffff9619Orange|r price level > %d%%") + gui:AddTip(id, "This determines the minimum level for an item to be counted as a orange item.") + gui:AddControl(id, "WideSlider", 0, 1, "util.pricelevel.yellow", 0, 500, 5, "|cffffff00Yellow|r price level > %d%%") + gui:AddTip(id, "This determines the minimum level for an item to be counted as a yellow item.") + gui:AddControl(id, "WideSlider", 0, 1, "util.pricelevel.green", 0, 500, 5, "|cff19ff19Green|r price level > %d%%") + gui:AddTip(id, "This determines the minimum level for an item to be counted as a green item.") + gui:AddControl(id, "WideSlider", 0, 1, "util.pricelevel.blue", 0, 0, 1, "|cff3296ffBlue|r price level > %d%%") + gui:AddTip(id, "This slider does nothing and is just here for completeness to show that blue is under green.") + + gui:AddHelp(id, "what is points", + "What are the pricing points for?", + "The pricing points determine the ranges for the various PriceLevel colored bands.\n".. + "As an item's price moves up through the bands, it will change to the next color.") + + gui:AddHelp(id, "wtf blue stuck qq", + "Why is the blue slider stuck at zero?", + "Something has to start at zero, and blue's the one that does it. If you moved blue off zero, then what would we color stuff under blue?") + + gui:AddHelp(id, "wtf blue stuck l2code", + "Ok, so why did you put the blue slider in then?", + "To appease my wife... Deal. :)") + +end + +function lib.ResetBars() + local tex + for i=1, NUM_BROWSE_TO_DISPLAY do + tex = _G["BrowseButton"..i.."PriceLevel"] + if (tex) then tex:Hide() end + end +end + +function lib.SetBar(i, r,g,b, pct) + local tex + local button = _G["BrowseButton"..i] + local colorize = AucAdvanced.Settings.GetSetting("util.pricelevel.colorize") + + if (button.AddTexture) then + tex = button.AddTexture + if (button.Value) then + if (pct) then + if pct > 999 then + button.Value:SetText(">999%") + else + button.Value:SetText(("%d%%"):format(pct)) + end + if colorize then + button.Value:SetTextColor(1,1,1) + else + button.Value:SetTextColor(r,g,b) + end + else + button.Value:SetText("") + button.Value:SetTextColor(1,1,1) + end + end + if not colorize then + tex:Hide() + end + else + tex = _G["BrowseButton"..i.."PriceLevel"] + end + if not colorize then return end + + if not tex then + tex = button:CreateTexture("BrowseButton"..i.."PriceLevel") + tex:SetPoint("TOPLEFT") + tex:SetPoint("BOTTOMRIGHT", 0, 5) + end + + if (r and g and b) then + local opacity = AucAdvanced.Settings.GetSetting("util.pricelevel.opacity") + opacity = math.floor(tonumber(opacity) or 50) / 100 + if (opacity < 0) then opacity = 0.01 + elseif (opacity > 1) then opacity = 1 end + + local gradient = AucAdvanced.Settings.GetSetting("util.pricelevel.gradient") + tex:SetTexture(1,1,1) + if (gradient) then + local direction = AucAdvanced.Settings.GetSetting("util.pricelevel.direction") + if (direction == "LEFT") then + tex:SetGradientAlpha("Horizontal", r,g,b, 0, r,g,b, opacity) + elseif (direction == "RIGHT") then + tex:SetGradientAlpha("Horizontal", r,g,b, opacity, r,g,b, 0) + elseif (direction == "BOTTOM") then + tex:SetGradientAlpha("Vertical", r,g,b, 0, r,g,b, opacity) + elseif (direction == "TOP") then + tex:SetGradientAlpha("Vertical", r,g,b, opacity, r,g,b, 0) + end + else + tex:SetGradientAlpha("Vertical", r,g,b, opacity, r,g,b, opacity) + end + tex:Show() + else + tex:Hide() + end +end + +function private.ListUpdate() + lib.ResetBars() + local index, link, quantity, minBid, minInc, buyPrice, bidPrice, priceLevel, perItem, r,g,b, _ + local numBatchAuctions, totalAuctions = GetNumAuctionItems("list"); + local offset = FauxScrollFrame_GetOffset(BrowseScrollFrame) + + for i=1, NUM_BROWSE_TO_DISPLAY do + index = offset + i + (NUM_AUCTION_ITEMS_PER_PAGE * AuctionFrameBrowse.page); + if (index <= numBatchAuctions + (NUM_AUCTION_ITEMS_PER_PAGE * AuctionFrameBrowse.page)) then + if AucAdvanced.Modules.Util.CompactUI + and AucAdvanced.Modules.Util.CompactUI.inUse then + _,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_, + priceLevel,_,r,g,b = AucAdvanced.Modules.Util.CompactUI.GetContents(offset+i) + lib.SetBar(i, r,g,b, priceLevel) + else + link = GetAuctionItemLink("list", offset + i) + if link then + _,_, quantity, _,_,_, minBid, minInc, buyPrice, bidPrice = GetAuctionItemInfo("list", offset + i) + if bidPrice>0 then bidPrice = bidPrice + minInc + else bidPrice = minBid end + priceLevel, perItem, r,g,b = lib.CalcLevel(link, quantity, bidPrice, buyPrice) + lib.SetBar(i, r,g,b, priceLevel) + end + end + end + end +end + +function lib.CalcLevel(link, quantity, bidPrice, buyPrice, itemWorth) + if not quantity or quantity < 1 then quantity = 1 end + + local priceModel = AucAdvanced.Settings.GetSetting("util.pricelevel.model") + local priceBasis = AucAdvanced.Settings.GetSetting("util.pricelevel.basis") + + local stackPrice + if (priceBasis == "cur") then + stackPrice = bidPrice + elseif (priceBasis == "buy") then + if not buyPrice or buyPrice <= 0 then return end + stackPrice = buyPrice + elseif (priceBasis == "try") then + stackPrice = buyPrice or 0 + if stackPrice <= 0 then + stackPrice = bidPrice + end + end + if not stackPrice then return end + + if not itemWorth then + if (priceModel == "market") then + itemWorth = AucAdvanced.API.GetMarketValue(link) + else + itemWorth = AucAdvanced.API.GetAlgorithmValue(priceModel, link) + end + if not itemWorth then return end + end + + local perItem = stackPrice / quantity + local priceLevel = perItem / itemWorth * 100 + + local r, g, b, lvl + + r,g,b,lvl = 0.2,0.6,1.0, "blue" + if priceLevel > AucAdvanced.Settings.GetSetting("util.pricelevel.red") then + r,g,b,lvl = 1.0,0.0,0.0, "red" + elseif priceLevel > AucAdvanced.Settings.GetSetting("util.pricelevel.orange") then + r,g,b,lvl = 1.0,0.6,0.1, "orange" + elseif priceLevel > AucAdvanced.Settings.GetSetting("util.pricelevel.yellow") then + r,g,b,lvl = 1.0,1.0,0.0, "yellow" + elseif priceLevel > AucAdvanced.Settings.GetSetting("util.pricelevel.green") then + r,g,b,lvl = 0.1,1.0,0.1, "green" + end + + return priceLevel, perItem, r,g,b, lvl, itemWorth +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-PriceLevel/PriceLevel.lua $", "$Rev: 4880 $") diff --git a/Auc-Advanced/Modules/Auc-Util-ScanButton/Auc-Util-ScanButton.toc b/Auc-Advanced/Modules/Auc-Util-ScanButton/Auc-Util-ScanButton.toc new file mode 100644 index 0000000..7c18b35 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-ScanButton/Auc-Util-ScanButton.toc @@ -0,0 +1,12 @@ +## Title: Auc:Util:Scan Button +## Interface: 40000 +## Dependancies: Auc-Advanced +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Auc-Util-ScanButton.toc 4933 2010-10-13 17:16:14Z Nechckn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## X-Max-Interface: 40000 +## +Embed.xml diff --git a/Auc-Advanced/Modules/Auc-Util-ScanButton/Embed.xml b/Auc-Advanced/Modules/Auc-Util-ScanButton/Embed.xml new file mode 100644 index 0000000..fc63864 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-ScanButton/Embed.xml @@ -0,0 +1,6 @@ + + + diff --git a/Auc-Advanced/Modules/Auc-Util-ScanButton/ScanButton.lua b/Auc-Advanced/Modules/Auc-Util-ScanButton/ScanButton.lua new file mode 100644 index 0000000..08ac690 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-ScanButton/ScanButton.lua @@ -0,0 +1,546 @@ +--[[ + Auctioneer - Scan Button module + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: ScanButton.lua 4880 2010-09-15 20:02:11Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This is an Auctioneer module that adds a textual scan progress + indicator to the Auction House UI. + + 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 libType, libName = "Util", "ScanButton" +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end +local print,decode,_,_,replicate,empty,get,set,default,debugPrint,fill,_TRANS = AucAdvanced.GetModuleLocals() + +function lib.Processor(callbackType, ...) + if (callbackType == "scanprogress") then + private.UpdateScanProgress(...) + elseif (callbackType == "auctionui") then + private.HookAH(...) + elseif (callbackType == "config") then + private.SetupConfigGui(...) + elseif (callbackType == "configchanged") then + private.ConfigChanged(...) + end +end + +lib.Processors = {} +function lib.Processors.scanprogress(callbackType, ...) + private.UpdateScanProgress(...) +end + +function lib.Processors.auctionui(callbackType, ...) + private.HookAH(...) +end + +function lib.Processors.config(callbackType, ...) + private.SetupConfigGui(...) +end + +function lib.Processors.configchanged(callbackType, ...) + private.ConfigChanged(...) +end + + + +function lib.OnLoad() + AucAdvanced.Settings.SetDefault("util.scanbutton.enabled", true) + AucAdvanced.Settings.SetDefault("util.scanbutton.message", true) + AucAdvanced.Settings.SetDefault("util.scanbutton.getall", false) +end + +-- /run local t = AucAdvanced.Modules.Util.ScanButton.Private.buttons.stop.tex t:SetPoint("TOPLEFT", t:GetParent() "TOPLEFT", 3,-3) t:SetPoint("BOTTOMRIGHT", t:GetParent(), "BOTTOMRIGHT", -3,3) +-- /run local t = AucAdvanced.Modules.Util.ScanButton.Private.buttons.stop.tex t:SetTexture("Interface\\AddOns\\Auc-Advanced\\Textures\\NavButtons") t:SetTexCoord(0.25, 0.5, 0, 1) t:SetVertexColor(1.0, 0.9, 0.1) + +function private.HookAH() + private.buttons = CreateFrame("Frame", nil, AuctionFrameBrowse) + private.buttons:SetPoint("TOPLEFT", AuctionFrameBrowse, "TOPLEFT", 180,-15) + private.buttons:SetWidth(22*4 + 4) + private.buttons:SetHeight(18) + private.buttons:SetScript("OnUpdate", private.OnUpdate) + + private.buttons.stop = CreateFrame("Button", nil, private.buttons, "OptionsButtonTemplate") + private.buttons.stop:SetPoint("TOPLEFT", private.buttons, "TOPLEFT", 0,0) + private.buttons.stop:SetWidth(22) + private.buttons.stop:SetHeight(18) + private.buttons.stop:SetScript("OnClick", private.stop) + private.buttons.stop.tex = private.buttons.stop:CreateTexture(nil, "OVERLAY") + private.buttons.stop.tex:SetPoint("TOPLEFT", private.buttons.stop, "TOPLEFT", 4,-2) + private.buttons.stop.tex:SetPoint("BOTTOMRIGHT", private.buttons.stop, "BOTTOMRIGHT", -4,2) + private.buttons.stop:SetScript("OnEnter", function() + GameTooltip:SetOwner(private.buttons.stop, "ANCHOR_TOPRIGHT") + GameTooltip:SetText("Click to stop the current scan") + end) + private.buttons.stop:SetScript("OnLeave", function() + GameTooltip:Hide() + end) + private.buttons.stop.tex:SetTexture("Interface\\AddOns\\Auc-Advanced\\Textures\\NavButtons") + private.buttons.stop.tex:SetTexCoord(0.25, 0.5, 0, 1) + private.buttons.stop.tex:SetVertexColor(1.0, 0.9, 0.1) + --displays remaining # of scans queued + private.buttons.stop.count = private.buttons.stop:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + private.buttons.stop.count:ClearAllPoints() + private.buttons.stop.count:SetPoint("RIGHT", private.buttons.stop, "LEFT", -5, 0) + private.buttons.stop.count:SetTextColor(1, 0.8, 0) + private.buttons.stop.count:SetText("0") + private.buttons.stop.count:SetJustifyH("RIGHT") + + private.buttons.play = CreateFrame("Button", nil, private.buttons, "OptionsButtonTemplate") + private.buttons.play:SetPoint("TOPLEFT", private.buttons.stop, "TOPRIGHT", 2,0) + private.buttons.play:SetWidth(22) + private.buttons.play:SetHeight(18) + private.buttons.play:SetScript("OnClick", private.play) + private.buttons.play.tex = private.buttons.play:CreateTexture(nil, "OVERLAY") + private.buttons.play.tex:SetPoint("TOPLEFT", private.buttons.play, "TOPLEFT", 4,-2) + private.buttons.play.tex:SetPoint("BOTTOMRIGHT", private.buttons.play, "BOTTOMRIGHT", -4,2) + private.buttons.play:SetScript("OnEnter", function() + GameTooltip:SetOwner(private.buttons.play, "ANCHOR_TOPRIGHT") + GameTooltip:SetText("Click here to start/resume a scan of the auction house") + end) + private.buttons.play:SetScript("OnLeave", function() + GameTooltip:Hide() + end) + private.buttons.play.tex:SetTexture("Interface\\AddOns\\Auc-Advanced\\Textures\\NavButtons") + private.buttons.play.tex:SetTexCoord(0, 0.25, 0, 1) + private.buttons.play.tex:SetVertexColor(1.0, 0.9, 0.1) + + private.buttons.pause = CreateFrame("Button", nil, private.buttons, "OptionsButtonTemplate") + private.buttons.pause:SetPoint("TOPLEFT", private.buttons.play, "TOPRIGHT", 2,0) + private.buttons.pause:SetWidth(22) + private.buttons.pause:SetHeight(18) + private.buttons.pause:SetScript("OnClick", private.pause) + private.buttons.pause.tex = private.buttons.pause:CreateTexture(nil, "OVERLAY") + private.buttons.pause.tex:SetPoint("TOPLEFT", private.buttons.pause, "TOPLEFT", 4,-2) + private.buttons.pause.tex:SetPoint("BOTTOMRIGHT", private.buttons.pause, "BOTTOMRIGHT", -4,2) + private.buttons.pause:SetScript("OnEnter", function() + GameTooltip:SetOwner(private.buttons.pause, "ANCHOR_TOPRIGHT") + GameTooltip:SetText("Click here to pause the current scan") + end) + private.buttons.pause:SetScript("OnLeave", function() + GameTooltip:Hide() + end) + private.buttons.pause.tex:SetTexture("Interface\\AddOns\\Auc-Advanced\\Textures\\NavButtons") + private.buttons.pause.tex:SetTexCoord(0.5, 0.75, 0, 1) + private.buttons.pause.tex:SetVertexColor(1.0, 0.9, 0.1) + + private.buttons.getall = CreateFrame("Button", nil, private.buttons, "OptionsButtonTemplate") + private.buttons.getall:SetPoint("TOPLEFT", private.buttons.pause, "TOPRIGHT", 2,0) + private.buttons.getall:SetWidth(22) + private.buttons.getall:SetHeight(18) + private.buttons.getall:SetScript("OnClick", private.getall) + private.buttons.getall.tex = private.buttons.getall:CreateTexture(nil, "OVERLAY") + private.buttons.getall.tex:SetPoint("TOPLEFT", private.buttons.getall, "TOPLEFT", 4,-2) + private.buttons.getall.tex:SetPoint("BOTTOMRIGHT", private.buttons.getall, "BOTTOMRIGHT", -4,2) + private.buttons.getall:SetScript("OnEnter", function() + GameTooltip:SetOwner(private.buttons.getall, "ANCHOR_TOPRIGHT") + GameTooltip:SetText("Click here to do a fast getall scan of the auction house") + end) + private.buttons.getall:SetScript("OnLeave", function() + GameTooltip:Hide() + end) + private.buttons.getall.tex:SetTexture("Interface\\AddOns\\Auc-Advanced\\Textures\\NavButtons") + private.buttons.getall.tex:SetTexCoord(0.75, 1, 0, 1) + private.buttons.getall.tex:SetVertexColor(0.3, 0.7, 1.0) + + local msg = CreateFrame("Frame", nil, UIParent) + private.message = msg + msg:Hide() + msg:SetPoint("CENTER", "UIParent", "CENTER") + msg:SetFrameStrata("DIALOG") + msg:SetHeight(280) + msg:SetWidth(500) + msg:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + edgeFile = "Interface/Tooltips/UI-Tooltip-Border", + tile = true, tileSize = 32, edgeSize = 32, + insets = { left = 9, right = 9, top = 9, bottom = 9 } + }) + msg:SetBackdropColor(0,0,0, 1) + + msg.Done = CreateFrame("Button", "", msg, "OptionsButtonTemplate") + msg.Done:SetText("Done") + msg.Done:SetPoint("BOTTOMRIGHT", msg, "BOTTOMRIGHT", -10, 10) + msg.Done:SetScript("OnClick", function() msg:Hide() end) + + msg.Text = msg:CreateFontString(nil, "HIGH") + msg.Text:SetPoint("TOPLEFT", msg, "TOPLEFT", 20, -20) + msg.Text:SetPoint("BOTTOMRIGHT", msg.Done, "TOPRIGHT", -10, 10) + msg.Text:SetFont("Fonts\\FRIZQT__.TTF",14) + msg.Text:SetJustifyH("LEFT") + msg.Text:SetJustifyV("TOP") + msg.Text:SetShadowColor(0,0,0) + msg.Text:SetShadowOffset(3,-3) + + msg.Text:SetText("|c00ff4400Important note about the GetAll scan option:|r\n\nUtilizing this feature can result in a very fast scan once every 15 minutes, however it requires a fast computer, low latency/ping times to the server, and for the servers to be running in optimum conditions. If any one of these three things are lagging, you may be disconnected from the server during the scan process.\n\nIf you wish to continue click the button again.\n\n|c00444444If you wish, you can disable this warning message via the Scan Button configuration settings.|r") + + private.UpdateScanProgress() +end +local progressBarOptions = {barColor = {0,0.6,0}} +function private.UpdateScanProgress(state, _, _, _, _, _, _, scansQueued) + local scanning, paused = false, false + if AucAdvanced and AucAdvanced.Scan then + scanning, paused = AucAdvanced.Scan.IsScanning(), AucAdvanced.Scan.IsPaused() + end + + private.ConfigChanged() + + if scanning or paused then + private.buttons.stop:Enable() + private.buttons.stop.tex:SetVertexColor(1.0, 0.9, 0.1) + else + private.buttons.stop:Disable() + private.buttons.stop.tex:SetVertexColor(0.3,0.3,0.3) + end + local pending = 0 + if scansQueued then + pending = scansQueued + if scanning then + pending = pending + 1 + end + if pending ~= private.lastpending then + private.lastpending = pending + private.buttons.stop.count:SetText(pending) + --store highest pending + if not progressBarOptions.pending or progressBarOptions.pending < pending then + progressBarOptions.pending = pending or 0 + end + local value = (100 - pending * 100 / progressBarOptions.pending) or 0 + AucAdvanced.API.ProgressBars("ScanButtonLuaStopCount", value, true, pending.." scans remaining", progressBarOptions) + end + end + --handle when we are on the last scan and no more queued when that scan completes set remaining to 0 + if scansQueued == 0 and state == false then + private.buttons.stop.count:SetText(pending) + progressBarOptions.pending = 0 + AucAdvanced.API.ProgressBars("ScanButtonLuaStopCount")--hide progress bar + end + + private.blink = nil + if scanning and not paused then + private.buttons.pause:Enable() + private.buttons.pause.tex:SetVertexColor(1.0, 0.9, 0.1) + private.buttons.play:Disable() + private.buttons.play.tex:SetVertexColor(0.3,0.3,0.3) + else + private.buttons.play:Enable() + private.buttons.play.tex:SetVertexColor(1.0, 0.9, 0.1) + private.buttons.pause:Disable() + private.buttons.pause.tex:SetVertexColor(0.3,0.3,0.3) + if paused then + private.blink = 1 + end + end +end + +local queue, queueFinished = {}, false +function private:OnUpdate(delay) + if private.blink then + private.timer = (private.timer or 0) - delay + if private.timer < 0 then + if not AucAdvanced.Scan.IsPaused() then + private.UpdateScanProgress() + return + end + if private.blink == 1 then + private.buttons.pause.tex:SetVertexColor(0.1, 0.3, 1.0) + private.blink = 2 + else + private.buttons.pause.tex:SetVertexColor(0.3, 0.3, 0.3) + private.blink = 1 + end + private.timer = 0.75 + end + end + --Create the overlay filter buttons the (callbackType == "auctionui") is too early. + if not AuctioneerFilterButton1 and AuctionFilterButton1 then + private.CreateSecondaryFilterButtons() + hooksecurefunc("AuctionFrameFilters_Update", private.AuctionFrameFilters_UpdateClasses)--used to respond to scrollframe + end + --if we still have filters pending process it, unless a scan is in progress or paused + if #queue > 0 and not AucAdvanced.Scan.IsScanning() and not AucAdvanced.Scan.IsPaused() then + private.play() + end + --Used to clear the selected filters/highlights AFTER the last queued scan has completed + if queueFinished and not AucAdvanced.Scan.IsScanning() and not AucAdvanced.Scan.IsPaused() then + queueFinished = false + if AucAdvanced.Settings.GetSetting("util.scanbutton.message") then print("|CFFFFFF00 Last queued Auction Filter completed") end + private.AuctionFrameFilters_ClearSelection() + private.AuctionFrameFilters_ClearHighlight() + end + + local canSend, canSendAll = CanSendAuctionQuery() + if canSendAll and not AucAdvanced.Scan.IsScanning() and private.buttons.play:IsEnabled() then + private.buttons.getall:Enable() + if AucAdvanced.Settings.GetSetting("util.scanbutton.getall") or private.warned then + private.buttons.getall.tex:SetVertexColor(1.0, 0.9, 0.1) + private.buttons.getall.warn = nil + else + private.buttons.getall.tex:SetVertexColor(0.3, 0.7, 1.0) + private.buttons.getall.warn = not private.warned + end + else + private.buttons.getall:Disable() + private.buttons.getall.tex:SetVertexColor(0.3,0.3,0.3) + end +end + +function private.SetupConfigGui(gui) + -- The defaults for the following settings are set in the lib.OnLoad function + local id = gui:AddTab(libName, libType.." Modules") + gui:AddControl(id, "Header", 0, libName.._TRANS('SBTN_Interface_Options')) --" Options" + + gui:AddHelp(id, "what scanbutton", + _TRANS('SBTN_Help_WhatScanButton'), --"What are the scan buttons? + _TRANS('SBTN_Help_WhatScanButtonAnswer')) --"The scan buttons are the Stop/Play/Pause buttons in the titlebar of the Auction House frame." + + gui:AddControl(id, "Checkbox", 0, 1, "util.scanbutton.enabled", _TRANS('SBTN_Interface_Enabled')) --"Show scan buttons in the Auction House" + gui:AddTip(id, _TRANS('SBTN_HelpTooltip_Enabled')) --"Toggles the display of the Stop/Play/Pause/Fast Forward scan buttons in the title bar of the Auction House." + + gui:AddControl(id, "Checkbox", 0, 1, "util.scanbutton.message", _TRANS('SBTN_Interface_Message')) --"Show messages about which category selections have been queued" + gui:AddTip(id, _TRANS('SBTN_HelpTooltip_Message')) --"Toggles the display of the starting search of filtered messages when using the ctr+click to select specific categories of the AH to scan." + + gui:AddControl(id, "Checkbox", 0, 1, "util.scanbutton.getall", _TRANS('SBTN_Interface_Getall')) --"Don't warn about GetAll scanning" + gui:AddTip(id, _TRANS('SBTN_HelpTooltip_Getall')) --"Disable the warning that you get when you click the GetAll button." +end + +function private.ConfigChanged() + if not private.buttons then return end + if AucAdvanced.Settings.GetSetting("util.scanbutton.enabled") then + private.buttons:Show() + else + private.buttons:Hide() + end +end + +function private.stop() + --this just makes the scan queue count decrease by 1 until the next processor event sets it to a proper # helpfull if user is spamming stop button + local count = tonumber(private.buttons.stop.count:GetText() ) + if count > 0 then + count = count -1 + AucAdvanced.API.ProgressBars("ScanButtonLuaStopCount", 100, true, count.." scans remaining", progressBarOptions) + else + AucAdvanced.API.ProgressBars("ScanButtonLuaStopCount") + end + private.buttons.stop.count:SetText(count) + + + AucAdvanced.Scan.SetPaused(false) + AucAdvanced.Scan.Cancel() + private.UpdateScanProgress() + queue = {} + queueFinished = true --Will clear currently selected scan filters with the Next Onupdate event. +end + +function private.play() + if AucAdvanced.Scan.IsPaused() then + AucAdvanced.Scan.SetPaused(false) + elseif not AucAdvanced.Scan.IsScanning() then + if #queue == 0 then queue = private.checkedFrames() end --check for user selected frames + debugPrint(("play: queue count=%i"):format(#queue), "ScanButton", "play", 0, "Debug") + if #queue > 0 then + if AucAdvanced.Settings.GetSetting("util.scanbutton.message") then print("Starting search on filter: |CFFFFFF00", CLASS_FILTERS[queue[1]]) end + AucAdvanced.Scan.StartScan("", "", "", nil, queue[1], nil, nil, nil) + table.remove(queue, 1) + if #queue == 0 then + queueFinished = true --Used to clear the selected filters/highlights AFTER the last queued scan has completed + end + else + AucAdvanced.Scan.StartScan("", "", "", AuctionFrameBrowse.selectedInvtypeIndex, AuctionFrameBrowse.selectedClassIndex, AuctionFrameBrowse.selectedSubclassIndex, nil, nil) + end + end + private.UpdateScanProgress() +end + +function private.getall() + if not AucAdvanced.Scan.IsScanning() then + if (private.buttons.getall.warn) then + private.message:Show() + private.warned = true + return + end + + AucAdvanced.Scan.StartScan(nil, nil, nil, nil, nil, nil, nil, nil, true) + end + private.UpdateScanProgress() +end + +function private.pause() + if not AucAdvanced.Scan.IsPaused() then + AucAdvanced.Scan.SetPaused(true) + end + private.UpdateScanProgress() +end + +--[[ +This adds a transparent replica of the AH filters on the browse frame, we have scripts on this frame to select catagories a user chooses to scan +This means we do not have to directly modify blizzards filter frame +]] +--store the primary AH filter categories, this is a copy of the global table the AH uses CLASS_FILTERS generated via GetAuctionItemClasses() +--Resets the selections table to 0 if an alt click is not used, or after a scan has been implemented +private.Filters = {} +function private.AuctionFrameFilters_ClearSelection() + for i,v in pairs(CLASS_FILTERS) do + private.Filters[v] = {0,i} --store cleared table of selections + end +end +--clear any current highlighting from a prev search +function private.AuctionFrameFilters_ClearHighlight() + for i in pairs(CLASS_FILTERS) do + _G["AuctionFilterButton"..i]:UnlockHighlight() + end +end + +function private.checkedFrames() + queue = {} + for i,v in pairs(private.Filters) do + if v[1] == 1 then + table.insert(queue, v[2]) + end + end + + return queue +end + +function private.CreateSecondaryFilterButtons() +local base, frame, prev = AuctionFrameBrowse, nil, nil + private.AuctionFrameFilters_ClearSelection() --create the filter selection table + for i = 1,15 do + frame = "AuctioneerFilterButton"..i + prev = "AuctioneerFilterButton"..(i - 1) + if i == 1 then + base[frame] = CreateFrame("Button", frame, AuctionFilterButton1, "AuctionClassButtonTemplate") + base[frame]:SetText("TICK-"..i) + base[frame]:SetPoint("LEFT",0,0) + base[frame]:SetWidth(156) + base[frame]:SetAlpha(0) + base[frame]:SetScript("OnClick", function() + if IsControlKeyDown() then +-- Test patch to clear AH settings when CTRL click is used just like we clear ours when non-ctrl is used +if (AuctionFrameBrowse.selectedClassIndex) then + AuctionFrameBrowse.selectedClass = nil + AuctionFrameBrowse.selectedClassIndex = nil + AuctionFrameBrowse.selectedSubclass = nil + AuctionFrameBrowse.selectedSubclassIndex = nil + AuctionFrameBrowse.selectedInvtype = nil + AuctionFrameBrowse.selectedInvtypeIndex = nil + AuctionFrameFilters_Update() +end + if private.Filters[ _G["AuctionFilterButton"..i]:GetText()][1] then + if private.Filters[ _G["AuctionFilterButton"..i]:GetText()][1] == 1 then + private.Filters[ _G["AuctionFilterButton"..i]:GetText()][1] = 0 + _G["AuctionFilterButton"..i]:UnlockHighlight() + else + private.Filters[ _G["AuctionFilterButton"..i]:GetText()][1] = 1 + _G["AuctionFilterButton"..i]:LockHighlight() + end + end + else + private.AuctionFrameFilters_ClearSelection() + AuctionFrameFilter_OnClick(_G["AuctionFilterButton"..i]) + end + end) + else + base[frame] = CreateFrame("Button", frame, AuctionFilterButton1, "AuctionClassButtonTemplate") + base[frame]:SetText("TICK-"..i) + base[frame]:ClearAllPoints() + base[frame]:SetPoint("TOPLEFT", base[prev],"BOTTOMLEFT",0,0) + base[frame]:SetWidth(156) + base[frame]:SetAlpha(0) + base[frame]:SetScript("OnClick", function() + if IsControlKeyDown() then +-- Test patch to clear AH settings when CTRL click is used just like we clear ours when non-ctrl is used +if (AuctionFrameBrowse.selectedClassIndex) then + AuctionFrameBrowse.selectedClass = nil + AuctionFrameBrowse.selectedClassIndex = nil + AuctionFrameBrowse.selectedSubclass = nil + AuctionFrameBrowse.selectedSubclassIndex = nil + AuctionFrameFilters_Update() +end + if private.Filters[ _G["AuctionFilterButton"..i]:GetText()] then + if private.Filters[ _G["AuctionFilterButton"..i]:GetText()][1] == 1 then + private.Filters[ _G["AuctionFilterButton"..i]:GetText()][1] = 0 + _G["AuctionFilterButton"..i]:UnlockHighlight() + else + private.Filters[ _G["AuctionFilterButton"..i]:GetText()][1] = 1 + _G["AuctionFilterButton"..i]:LockHighlight() + end + end + else + private.AuctionFrameFilters_ClearSelection() + AuctionFrameFilter_OnClick(_G["AuctionFilterButton"..i]) + end + end) + end + end + private.AuctionFrameFilters_UpdateClasses() --Changes the frame to match current filter frame, needed for 1 refresh after frame creation. +end + +--Blizzard code base, used to generate a replica of the default filter frame +function private.AuctionFrameFilters_UpdateClasses() + -- Display the list of open filters + local button, index, info, isLast + local offset = FauxScrollFrame_GetOffset(BrowseFilterScrollFrame) + index = offset + for i=1, NUM_FILTERS_TO_DISPLAY do + button = _G["AuctioneerFilterButton"..i] + + if ( getn(OPEN_FILTER_LIST) > NUM_FILTERS_TO_DISPLAY ) then + button:SetWidth(126) + else + button:SetWidth(126) + end + index = index + 1 + if ( index <= getn(OPEN_FILTER_LIST) ) then + info = OPEN_FILTER_LIST[index] + while ((info[2] == "invtype") and (not info[6])) do + index = index + 1 + if ( index <= getn(OPEN_FILTER_LIST) ) then + info = OPEN_FILTER_LIST[index] + else + info = nil + button:Hide() + break + end + end + if ( info ) then + FilterButton_SetType(button, info[2], info[1], info[5]) + button.index = info[3] + if ( info[4] ) then + button:LockHighlight() + else + button:UnlockHighlight() + end + button:Show() + end + else + button:Hide() + end + + end +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-ScanButton/ScanButton.lua $", "$Rev: 4880 $") diff --git a/Auc-Advanced/Modules/Auc-Util-ScanFinish/Auc-Util-ScanFinish.toc b/Auc-Advanced/Modules/Auc-Util-ScanFinish/Auc-Util-ScanFinish.toc new file mode 100644 index 0000000..4e1bb35 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-ScanFinish/Auc-Util-ScanFinish.toc @@ -0,0 +1,12 @@ +## Title: Auc:Util:Scan Finish +## Interface: 40000 +## Dependancies: Auc-Advanced +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Auc-Util-ScanFinish.toc 4933 2010-10-13 17:16:14Z Nechckn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## X-Max-Interface: 40000 +## +Embed.xml diff --git a/Auc-Advanced/Modules/Auc-Util-ScanFinish/Embed.xml b/Auc-Advanced/Modules/Auc-Util-ScanFinish/Embed.xml new file mode 100644 index 0000000..3327860 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-ScanFinish/Embed.xml @@ -0,0 +1,6 @@ + + + diff --git a/Auc-Advanced/Modules/Auc-Util-ScanFinish/ScanComplete.mp3 b/Auc-Advanced/Modules/Auc-Util-ScanFinish/ScanComplete.mp3 new file mode 100644 index 0000000..bb3dc37 Binary files /dev/null and b/Auc-Advanced/Modules/Auc-Util-ScanFinish/ScanComplete.mp3 differ diff --git a/Auc-Advanced/Modules/Auc-Util-ScanFinish/ScanFinish.lua b/Auc-Advanced/Modules/Auc-Util-ScanFinish/ScanFinish.lua new file mode 100644 index 0000000..e107465 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-ScanFinish/ScanFinish.lua @@ -0,0 +1,295 @@ +--[[ + Auctioneer - Scan Finish module + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: ScanFinish.lua 4840 2010-08-04 21:44:00Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This is an Auctioneer module that adds a few event functionalities + to Auctioneer when a successful scan is completed. + + 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 libType, libName = "Util", "ScanFinish" +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end +local print,decode,_,_,replicate,empty,get,set,default,debugPrint,fill = AucAdvanced.GetModuleLocals() + +local blnDebug = false +local blnLibEmbedded = nil + +local intScanMinThreshold = 300 --Safeguard to prevent Auditor Refresh button scans from executing our finish events. Use 300 or more to be safe +local strPrevSound = "AuctioneerClassic" + +function lib.Processor(callbackType, ...) + if blnDebug then + local msg = ("CallbackType=%s, Sound=%s, IsBlocked=%s, IsScanning=%s"):format(callbackType, + tostring(AucAdvanced.Settings.GetSetting("util.scanfinish.soundpath")), + tostring(AucAdvanced.API.IsBlocked()), tostring(AucAdvanced.Scan.IsScanning())) + debugPrint(msg, "ScanFinish Processor", callbackType, 0, "Debug") + end + + if (callbackType == "scanfinish") then + if not AucAdvanced.Settings.GetSetting("util.scanfinish.activated") then + return + end + private.ScanFinish(...) + elseif (callbackType == "scanstart") then + if not AucAdvanced.Settings.GetSetting("util.scanfinish.activated") then + return + end + private.ScanStart(...) + elseif (callbackType == "config") then + private.SetupConfigGui(...) + elseif (callbackType == "configchanged") then + private.ConfigChanged(...) + end +end + +lib.Processors = {} +lib.Processors.scanfinish = lib.Processor +lib.Processors.scanstart = lib.Processor +lib.Processors.config = lib.Processor +lib.Processors.configchanged = lib.Processor + +function lib.OnLoad() + print("Auctioneer: {{"..libType..":"..libName.."}} loaded!") + AucAdvanced.Settings.SetDefault("util.scanfinish.activated", true) + AucAdvanced.Settings.SetDefault("util.scanfinish.shutdown", false) + AucAdvanced.Settings.SetDefault("util.scanfinish.logout", false) + AucAdvanced.Settings.SetDefault("util.scanfinish.message", "So many auctions... so little time") + AucAdvanced.Settings.SetDefault("util.scanfinish.messagechannel", "none") + AucAdvanced.Settings.SetDefault("util.scanfinish.emote", "none") + AucAdvanced.Settings.SetDefault("util.scanfinish.debug", false) + if AucAdvanced.Settings.GetSetting("util.scanfinish.debug") then blnDebug = true end +end + +function private.ScanStart(scanSize, querysig, query) + debugPrint(scanSize, "ScanFinish", "ScanStart", 0, "Debug") + + if (scanSize ~= "Full") then return end + AlertShutdownOrLogOff() +end + + +function private.ScanFinish(scanSize, querysig, query, wasComplete) + debugPrint(scanSize..","..tostring(wasComplete), "ScanFinish", "ScanFinish", 0, "Debug") + + if (scanSize ~= "Full") then return end + if (not wasComplete) then return end + private.PerformFinishEvents() +end + +function private.PerformFinishEvents() + --Clean up/reset local variables + local msg = ("Message: '%s', MessageChannel: '%s', Emote: '%s', Logout: %s, ShutDown %s"):format( + AucAdvanced.Settings.GetSetting("util.scanfinish.message"), + AucAdvanced.Settings.GetSetting("util.scanfinish.messagechannel"), + AucAdvanced.Settings.GetSetting("util.scanfinish.emote"), + tostring(AucAdvanced.Settings.GetSetting("util.scanfinish.logout")), + tostring(AucAdvanced.Settings.GetSetting("util.scanfinish.shutdown"))) + debugPrint(msg, "ScanFinish", "PerformFinishEvents", 0, "Debug") + + --Sound + PlayCompleteSound() + + --Message + if AucAdvanced.Settings.GetSetting("util.scanfinish.messagechannel") == "none" then + --don't do anything + elseif AucAdvanced.Settings.GetSetting("util.scanfinish.messagechannel") == "GENERAL" then + SendChatMessage(AucAdvanced.Settings.GetSetting("util.scanfinish.message"),"CHANNEL",nil,GetChannelName("General")) + else + SendChatMessage(AucAdvanced.Settings.GetSetting("util.scanfinish.message"),AucAdvanced.Settings.GetSetting("util.scanfinish.messagechannel")) + end + + --Emote + if not (AucAdvanced.Settings.GetSetting("util.scanfinish.emote") == "none") then + DoEmote(AucAdvanced.Settings.GetSetting("util.scanfinish.emote")) + end + + --Shutdown or Logoff + if (AucAdvanced.Settings.GetSetting("util.scanfinish.shutdown")) then + print("AucAdvanced: {{"..libName.."}} Shutting Down!!") + if not blnDebug then + Quit() + end + elseif (AucAdvanced.Settings.GetSetting("util.scanfinish.logout")) then + print("AucAdvanced: {{"..libName.."}} Logging Out!") + if not blnDebug then + Logout() + end + end +end + +function AlertShutdownOrLogOff() + if (AucAdvanced.Settings.GetSetting("util.scanfinish.shutdown")) then + PlaySound("TellMessage") + print("AucAdvanced: {{"..libName.."}} |cffff3300Reminder|r: Shutdown is enabled. World of Warcraft will be shut down once the current scan successfully completes.") + elseif (AucAdvanced.Settings.GetSetting("util.scanfinish.logout")) then + PlaySound("TellMessage") + print("AucAdvanced: {{"..libName.."}} |cffff3300Reminder|r: LogOut is enabled. This character will be logged off once the current scan successfully completes.") + end +end + +function PlayCompleteSound() + strConfiguredSoundPath = AucAdvanced.Settings.GetSetting("util.scanfinish.soundpath") + if strConfiguredSoundPath and not (strConfiguredSoundPath == "none") then + if blnDebug then + print("AucAdvanced: {{"..libName.."}} You are listening to "..strConfiguredSoundPath) + end + if strConfiguredSoundPath == "AuctioneerClassic" then + if blnLibEmbedded == nil then + blnLibEmbedded = IsLibEmbedded() + end + strConfiguredSoundPath = "Interface\\AddOns\\Auc-Util-ScanFinish\\ScanComplete.mp3" + if blnLibEmbedded then + strConfiguredSoundPath = "Interface\\AddOns\\Auc-Advanced\\Modules\\Auc-Util-ScanFinish\\ScanComplete.mp3" + end + + --Known PlaySoundFile bug seems to require some event preceeding it to get it to work reliably + --Can get this working as a print to screen or an internal sound. Other developers + --suggested this workaround. + --http://forums.worldofwarcraft.com/thread.html?topicId=1777875494&sid=1&pageNo=4 + PlaySound("GAMEHIGHLIGHTFRIENDLYUNIT") + PlaySoundFile(strConfiguredSoundPath) + + else + PlaySound(strConfiguredSoundPath) + end + end +end + +--Config UI functions +function private.SetupConfigGui(gui) + -- The defaults for the following settings are set in the lib.OnLoad function + local id = gui:AddTab(libName, libType.." Modules") + + gui:AddHelp(id, "what is scanfinish", + "What is ScanFinish?", + "ScanFinish is an Auctioneer module that will execute one or more useful events once Auctioneer has completed a scan successfully.\n\nScanFinish will only execute these events during full Auctioneer scans with a minimum threshold of "..intScanMinThreshold .." items, so there is no worry about logging off or spamming emotes during the incremental scans or SearchUI activities. Unfortunately, this also means the functionality will not be enabled in auction houses with under "..intScanMinThreshold.." items." + ) + + gui:AddControl(id, "Header", 0, libName.." options") + gui:AddControl(id, "Checkbox", 0, 1, "util.scanfinish.activated", "Allow the execution of the events below once a successful full scan completes") + gui:AddTip(id, "Selecting this option will enable Auctioneer to perform the events below once Auctioneer has completed a scan successfully. \n\nUncheck this to disable all events.") + + gui:AddControl(id, "Subhead", 0, "Sound & Emote") + gui:AddControl(id, "Selectbox", 0, 3, { + {"none", "None (do not play a sound)"}, + {"AuctioneerClassic", "Auctioneer Classic"}, + {"QUESTCOMPLETED","Quest Completed"}, + {"LEVELUP","Level Up"}, + {"AuctionWindowOpen","Auction House Open"}, + {"AuctionWindowClose","Auction House Close"}, + {"ReadyCheck","Raid Ready Check"}, + {"RaidWarning","Raid Warning"}, + {"LOOTWINDOWCOINSOUND","Coin"}, + }, "util.scanfinish.soundpath", "Pick the sound to play") + gui:AddTip(id, "Selecting one of these sounds will cause Auctioneer to play that sound once Auctioneer has completed a scan successfully. \n\nBy selecting None, no sound will be played.") + + gui:AddControl(id, "Selectbox", 0, 3, { + {"none" , "None (do not emote)"}, + {"APOLOGIZE" , "Apologize"}, + {"APPLAUD" , "Applaud"}, + {"BRB" , "BRB"}, + {"CACKLE" , "Cackle"}, + {"CHICKEN" , "Chicken"}, + {"DANCE" , "Dance"}, + {"FAREWELL" , "Farewell"}, + {"FLIRT" , "Flirt"}, + {"GLOAT" , "Gloat"}, + {"JOKE" , "Silly"}, + {"SLEEP" , "Sleep"}, + {"VICTORY" , "Victory"}, + {"YAWN" , "Yawn"}, + + }, "util.scanfinish.emote", "Pick the Emote to perform") + gui:AddTip(id, "Selecting one of these emotes will cause your character to perform the selected emote once Auctioneer has completed a scan successfully.\n\nBy selecting None, no emote will be performed.") + + gui:AddControl(id, "Subhead", 0, "Message") + gui:AddControl(id, "Text", 0, 1, "util.scanfinish.message", "Message text:") + gui:AddTip(id, "Enter the message text of what you wish your character to say as well as choosing a channel below. \n\nThis will not execute slash commands.") + gui:AddControl(id, "Selectbox", 0, 3, { + {"none", "None (do not send message)"}, + {"SAY", "Say (/s)"}, + {"PARTY","Party (/p)"}, + {"RAID","Raid (/r)"}, + {"GUILD","Guild (/g)"}, + {"YELL","Yell (/y)"}, + {"EMOTE","Emote (/em)"}, + {"GENERAL","General"}, + }, "util.scanfinish.messagechannel", "Pick the channel to send your message to") + gui:AddTip(id, "Selecting one of these channels will cause your character to say the message text into the selected channel once Auctioneer has completed a scan successfully. \n\nBy choosing Emote, your character will use the text above as a custom emote. \n\nBy selecting None, no message will be sent.") + + + gui:AddControl(id, "Subhead", 0, "Shutdown or Log Out") + gui:AddControl(id, "Checkbox", 0, 1, "util.scanfinish.shutdown", "Shutdown World of Warcraft") + gui:AddTip(id, "Selecting this option will cause Auctioneer to shut down World of Warcraft completely once Auctioneer has completed a scan successfully.") + gui:AddControl(id, "Checkbox", 0, 1, "util.scanfinish.logout", "Log Out the current character") + gui:AddTip(id, "Selecting this option will cause Auctioneer to log out to the character select screen once Auctioneer has completed a scan successfully. \n\nIf Shutdown is enabled, selecting this will have no effect") + + + --Debug switch via gui. Currently not exposed to the end user + gui:AddControl(id, "Subhead", 0, "") + gui:AddControl(id, "Checkbox", 0, 1, "util.scanfinish.debug", "Show Debug Information for this session") + + +end + +function IsLibEmbedded() + blnResult = false + for pos, module in ipairs(AucAdvanced.EmbeddedModules) do + --print(" Debug:Comparing Auc-Util-"..libName.." with "..module) + if "Auc-Util-"..libName == module then + if blnDebug then + print(" Debug:Auc-Util-"..libName.." is an embedded module") + end + blnResult = true + break + end + end + return blnResult +end + +function private.ConfigChanged() + --Debug switch via gui. Currently not exposed to the end user + --blnDebug = AucAdvanced.Settings.GetSetting("util.scanfinish.debug") + if blnDebug then + print(" Debug:Configuration Changed") + end + if AucAdvanced.Settings.GetSetting("util.scanfinish.debug") then blnDebug = true end + + if not (strPrevSound == AucAdvanced.Settings.GetSetting("util.scanfinish.soundpath")) then + PlayCompleteSound() + strPrevSound = AucAdvanced.Settings.GetSetting("util.scanfinish.soundpath") + end + + if (not AucAdvanced.Settings.GetSetting("util.scanfinish.activated")) then + if blnDebug then print(" Debug:Updating ScanFinish:Deactivated") end + elseif (AucAdvanced.Scan.IsScanning()) then + if blnDebug then print(" Debug:Updating ScanFinish with Scan in progress") end + end +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-ScanFinish/ScanFinish.lua $", "$Rev: 4840 $") diff --git a/Auc-Advanced/Modules/Auc-Util-ScanProgress/Auc-Util-ScanProgress.toc b/Auc-Advanced/Modules/Auc-Util-ScanProgress/Auc-Util-ScanProgress.toc new file mode 100644 index 0000000..403e903 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-ScanProgress/Auc-Util-ScanProgress.toc @@ -0,0 +1,14 @@ +## Title: Auc:Util:Scan Progress +## Notes: Auctioneer utility that displays information about the status of on-going Auction House scans. +## +## Interface: 40000 +## Dependancies: Auc-Advanced +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Auc-Util-ScanProgress.toc 4933 2010-10-13 17:16:14Z Nechckn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## X-Max-Interface: 40000 +## +Embed.xml diff --git a/Auc-Advanced/Modules/Auc-Util-ScanProgress/Embed.xml b/Auc-Advanced/Modules/Auc-Util-ScanProgress/Embed.xml new file mode 100644 index 0000000..7e68b18 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-ScanProgress/Embed.xml @@ -0,0 +1,6 @@ + + + diff --git a/Auc-Advanced/Modules/Auc-Util-ScanProgress/ScanProgress.lua b/Auc-Advanced/Modules/Auc-Util-ScanProgress/ScanProgress.lua new file mode 100644 index 0000000..dc75e1a --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-ScanProgress/ScanProgress.lua @@ -0,0 +1,224 @@ +--[[ + Auctioneer - Price Level Utility module + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: ScanProgress.lua 4840 2010-08-04 21:44:00Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This is an Auctioneer module that adds a textual scan progress + indicator to the Auction House UI. + + 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 libType, libName = "Util", "ScanProgress" +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end +local print,decode,_,_,replicate,empty,get,set,default,debugPrint,fill,_TRANS = AucAdvanced.GetModuleLocals() + +function lib.Processor(callbackType, ...) + if (callbackType == "scanprogress") then + private.UpdateScanProgress(...) + elseif (callbackType == "config") then + private.SetupConfigGui(...) + elseif (callbackType == "configchanged") then + private.ConfigChanged(...) + end +end + +lib.Processors = {} +function lib.Processors.scanprogress(callbackType, ...) + private.UpdateScanProgress(...) +end + +function lib.Processors.config(callbackType, ...) + private.SetupConfigGui(...) +end + +function lib.Processors.configchanged(callbackType, ...) + private.ConfigChanged(...) +end + + + +function lib.OnLoad() + --print("AucAdvanced: {{"..libType..":"..libName.."}} loaded!") + AucAdvanced.Settings.SetDefault("util.scanprogress.activated", true) + AucAdvanced.Settings.SetDefault("util.scanprogress.leaveshown", true) +end + +---- Functions to manage the progress indicator ---- +private.scanStartTime = time() +--private.scanProgressFormat = "Auctioneer Advanced: %s\nScanning page %d of %d\n\nAuctions per second: %.2f\nAuctions scanned thus far: %d\n\nEstimated time left: %s\nElapsed scan time: %s" +private.scanProgressFormat = "Auctioneer Advanced: %s\nScanning page %d of %d\n\nAuctions per second: %.2f\nAuctions expected: %d\nAuctions scanned thus far: %d (%3.1f%%)\n\nElapsed scan time: %s\nEstimated time left: %s" + +function private.UpdateScanProgress(state, totalAuctions, scannedAuctions, elapsedTime) + --Check that we're enabled before passing on the callback + if not AucAdvanced.Settings.GetSetting("util.scanprogress.activated") + + --Check to see if browseoverride has been set, if so gracefully allow it to continue as is + or AucAdvanced.Settings.GetSetting("util.browseoverride.activated") then + state = false + end + + --Change the state if we have not scanned any auctions yet. + --This is done so that we don't start the timer too soon and thus get skewed numbers + if (state == nil and ( + not scannedAuctions or + scannedAuctions == 0 or + not AucAdvanced.API.IsBlocked() or + BrowseButton1:IsVisible() + )) then + state = true + end + + --Distribute the callback according to the value of the state variable + if (state == false) then + if AucAdvanced.API.IsBlocked() then + private.HideScanProgressUI() + end + return + elseif (state == true) then + private.ShowScanProgressUI(totalAuctions) + end + if scannedAuctions and scannedAuctions > 0 then + private.UpdateScanProgressUI(totalAuctions, scannedAuctions, elapsedTime) + end +end + +local initShown = false +function private.ShowScanProgressUI(totalAuctions) + if (nLog) then nLog.AddMessage("Auctioneer", "ScanProgress", N_INFO, "ShowScanProgressUI Called") end + for i=1, NUM_BROWSE_TO_DISPLAY do + _G["BrowseButton"..i]:Hide() + end + BrowseNoResultsText:Show() + private.scanStartTime = time() + local msg + if totalAuctions and totalAuctions > 0 then + msg = ("Scanning %d items..."):format(totalAuctions) + else + msg = "Scanning..." + end + BrowseNoResultsText:SetText(msg) + initShown = msg.." DONE" + AucAdvanced.API.BlockUpdate(true, true) +end + +function private.HideScanProgressUI() + if (nLog) then nLog.AddMessage("Auctioneer", "ScanProgress", N_INFO, "HideScanProgressUI Called") end + + if (AucAdvanced.Settings.GetSetting("util.scanprogress.leaveshown")) then + if (initShown) then BrowseNoResultsText:SetText(initShown) end + AucAdvanced.API.BlockUpdate(false, false) + else + BrowseNoResultsText:Hide() + BrowseNoResultsText:SetText(SEARCHING_FOR_ITEMS) + + local numBatchAuctions, totalAuctions = GetNumAuctionItems("list") + local offset = FauxScrollFrame_GetOffset(BrowseScrollFrame) + for i=1, NUM_BROWSE_TO_DISPLAY do + index = offset + i + (NUM_AUCTION_ITEMS_PER_PAGE * AuctionFrameBrowse.page) + if ( index > (numBatchAuctions + (NUM_AUCTION_ITEMS_PER_PAGE * AuctionFrameBrowse.page)) ) then + _G["BrowseButton"..i]:Hide() + else + _G["BrowseButton"..i]:Show() + end + end + AucAdvanced.API.BlockUpdate(false, true) + end + initShown = nil +end + +function private.UpdateScanProgressUI(totalAuctions, scannedAuctions, elapsedTime) + if (nLog) then nLog.AddMessage("Auctioneer", "ScanProgress", N_INFO, "UpdateScanProgressUI Called") end + local numAuctionsPerPage = NUM_AUCTION_ITEMS_PER_PAGE + initShown = false + -- Prefer the elapsed time which is provided by core and excludes paused time. + local secondsElapsed = elapsedTime or (time() - private.scanStartTime) + + local auctionsToScan = totalAuctions - scannedAuctions + + local currentPage = math.floor(scannedAuctions / numAuctionsPerPage) + + local totalPages = math.ceil(totalAuctions / numAuctionsPerPage) + if (totalPages < 1) then + totalPages = 1 + end +-- if (totalPages - math.floor(totalPages) > 0) then +-- totalPages = math.ceil(totalPages) +-- else +-- totalPages = math.floor(totalPages) +-- end + + local auctionsScannedPerSecond = scannedAuctions / secondsElapsed + local secondsToScanCompletion = auctionsToScan / auctionsScannedPerSecond + if (currentPage+1 == totalPages) then + secondsToScanCompletion = "Done" + else + secondsToScanCompletion = SecondsToTime(secondsToScanCompletion) + end + + BrowseNoResultsText:SetText( + private.scanProgressFormat:format( + "Scanning auctions.", + currentPage + 1, totalPages, + auctionsScannedPerSecond, + totalAuctions, + scannedAuctions, ((currentPage+1)/totalPages)*100, + SecondsToTime(secondsElapsed), + secondsToScanCompletion + ) + ) +end + +--Config UI functions +function private.SetupConfigGui(gui) + -- The defaults for the following settings are set in the lib.OnLoad function + local id = gui:AddTab(libName, libType.." Modules") + gui:AddControl(id, "Header", 0, libName.." Options") + + gui:AddHelp(id, "what scanprogress", + _TRANS('SPRG_Help_WhatScanProgress'), --"What is the Scan Progress indicator?" + _TRANS('SPRG_Help_WhatScanProgressAnswer')) --"The Scan Progress indicator is the text that appears while scanning the Auction House. It displays: the speed of the scan, current auctions and total number of auctions scanned, aswell as the current number of pages and total pages scanned." + +-- Old answer, incase the new one is too short and/or vague. +-- "The Scan Progress indicator is the text that appears while scanning the Auction House, indicating ".. +-- "how fast you are scanning, how many auctions you have scanned so far, how many total auctions there are, ".. +-- "how many pages you have scanned so far, and how many total pages there are.") + + gui:AddControl(id, "Checkbox", 0, 1, "util.scanprogress.activated", _TRANS('SPRG_Interface_Activated')) --"Show a textual progress indicator when scanning" + gui:AddTip(id, _TRANS('SPRG_HelpTooltip_Activated')) --"Toggles the display of the scan progress indicator\n\nNOTE: This setting is also affected by the CompactUI option to prevent other modules from changing the display of the browse tab while scanning." + gui:AddControl(id, "Checkbox", 0, 1, "util.scanprogress.leaveshown", _TRANS('SPRG_Interface_LeaveShown')) --"Leave the scan progress text shown after scan completion" + gui:AddTip(id, _TRANS('SPRG_HelpTooltip_LeaveShown')) --"If toggled, it will leave the scan progress indicator on the screen after scan has completed.\n\nIf disabled it will show the last scanned page." +end + +function private.ConfigChanged() + if (not AucAdvanced.Settings.GetSetting("util.scanprogress.activated")) then + private.UpdateScanProgress(false) + elseif (AucAdvanced.Scan.IsScanning()) then + private.UpdateScanProgress(true) + end +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-ScanProgress/ScanProgress.lua $", "$Rev: 4840 $") diff --git a/Auc-Advanced/Modules/Auc-Util-ScanStart/Auc-Util-ScanStart.toc b/Auc-Advanced/Modules/Auc-Util-ScanStart/Auc-Util-ScanStart.toc new file mode 100644 index 0000000..4fad837 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-ScanStart/Auc-Util-ScanStart.toc @@ -0,0 +1,12 @@ +## Title: Auc:Util:Scan Start +## Interface: 40000 +## Dependancies: Auc-Advanced +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Auc-Util-ScanStart.toc 4933 2010-10-13 17:16:14Z Nechckn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## X-Max-Interface: 40000 +## +Embed.xml diff --git a/Auc-Advanced/Modules/Auc-Util-ScanStart/Embed.xml b/Auc-Advanced/Modules/Auc-Util-ScanStart/Embed.xml new file mode 100644 index 0000000..036ae00 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-ScanStart/Embed.xml @@ -0,0 +1,6 @@ + + + diff --git a/Auc-Advanced/Modules/Auc-Util-ScanStart/ScanStart.lua b/Auc-Advanced/Modules/Auc-Util-ScanStart/ScanStart.lua new file mode 100644 index 0000000..413f95d --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-ScanStart/ScanStart.lua @@ -0,0 +1,217 @@ +--[[ + Auctioneer - Scan Start module + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: ScanStart.lua 4840 2010-08-04 21:44:00Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This is an Auctioneer module that adds a few event functionalities + to Auctioneer 5 when a scan is started. + + 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 libType, libName = "Util", "ScanStart" +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end +private.scanTypeList = {partial="Partial",full="Full"} + +local print,decode,_,_,replicate,empty,get,set,default,debugPrint,fill = AucAdvanced.GetModuleLocals() + +local blnDebug = false +local blnLibEmbedded = nil + +function lib.Processor(callbackType, ...) + if blnDebug then + local msg = ("CallbackType=%s, IsBlocked=%s, IsScanning=%s"):format(callbackType, tostring(AucAdvanced.API.IsBlocked()), + tostring(AucAdvanced.Scan.IsScanning())) + debugPrint(msg, "ScanStart Processor", callbackType, 0, "Debug") + end + if (callbackType == "scanstart") then + if not AucAdvanced.Settings.GetSetting("util.scanstart.activated") then + return + end + private.ScanStart(...) + elseif (callbackType == "config") then + private.SetupConfigGui(...) + elseif (callbackType == "configchanged") then + private.ConfigChanged(...) + end +end +lib.Processors = {} +lib.Processors.scanstart = lib.Processor +lib.Processors.config = lib.Processor +lib.Processors.configchanged = lib.Processor + +function lib.OnLoad() + print("AucAdvanced: {{"..libType..":"..libName.."}} loaded!") + AucAdvanced.Settings.SetDefault("util.scanstart.activated", true) + AucAdvanced.Settings.SetDefault("util.scanstart.debug", false) + + for scantype, scantypename in pairs(private.scanTypeList) do + AucAdvanced.Settings.SetDefault("util.scanstart."..scantype..".activated", scantype=="full") + AucAdvanced.Settings.SetDefault("util.scanstart."..scantype..".message.channel", "none") + AucAdvanced.Settings.SetDefault("util.scanstart."..scantype..".message.text", "What do we have today") + AucAdvanced.Settings.SetDefault("util.scanstart."..scantype..".emote", "none") + AucAdvanced.Settings.SetDefault("util.scanstart."..scantype..".dnd.activated", false) + AucAdvanced.Settings.SetDefault("util.scanstart."..scantype..".dnd.text", "Scanning Auction House") + AucAdvanced.Settings.SetDefault("util.scanstart."..scantype..".afk.activated", false) + AucAdvanced.Settings.SetDefault("util.scanstart."..scantype..".afk.text", "Scanning Auction House. What, you want me to stand around twiddling thumbs?") + end + + if AucAdvanced.Settings.GetSetting("util.scanstart.debug") then blnDebug = true end +end + +function private.ScanStart(scanSize, querysig, query) + if blnDebug then + local msg = ("scanSize=%s, querysig=%s"):format(scanSize, querysig) + debugPrint(msg, "ScanStart Handler", "Scan Started", 0, "Debug") + end + + if (scanSize == "Full" or scanSize == "Partial") then + --Message + local scanId = scanSize:lower() + debugPrint(AucAdvanced.Settings.GetSetting("util.scanstart."..scanId..".message.channel"), "ScanStart Handler", "Chat", 0, "Debug") + if AucAdvanced.Settings.GetSetting("util.scanstart."..scanId..".message.channel") == "none" then + --don't do anything + elseif AucAdvanced.Settings.GetSetting("util.scanstart."..scanId..".message.channel") == "GENERAL" then + SendChatMessage(AucAdvanced.Settings.GetSetting("util.scanstart."..scanId..".message.text"),"CHANNEL",nil,GetChannelName("General")) + else + SendChatMessage(AucAdvanced.Settings.GetSetting("util.scanstart."..scanId..".message.text"),AucAdvanced.Settings.GetSetting("util.scanstart."..scanId..".message.channel")) + end + + --Emote + if not (AucAdvanced.Settings.GetSetting("util.scanstart."..scanId..".emote") == "none") then + debugPrint(AucAdvanced.Settings.GetSetting("util.scanstart."..scanId..".emote"), "ScanStart Handler", "Performing EMOTE", 0, "Debug") + DoEmote(AucAdvanced.Settings.GetSetting("util.scanstart."..scanId..".emote")) + end + + --Set AFK and/or DND + if (AucAdvanced.Settings.GetSetting("util.scanstart."..scanId..".dnd.activated")) then + debugPrint(AucAdvanced.Settings.GetSetting("util.scanstart."..scanId..".dnd.text"), "ScanStart Handler", "Setting DND", 0, "Debug") + if not UnitIsDND("player") then SendChatMessage(AucAdvanced.Settings.GetSetting("util.scanstart."..scanId..".dnd.text"), "DND") end + end + if (AucAdvanced.Settings.GetSetting("util.scanstart."..scanId..".afk.activated")) then + debugPrint(AucAdvanced.Settings.GetSetting("util.scanstart."..scanId..".afk.text"), "ScanStart Handler", "Setting AFK", 0, "Debug") + if not UnitIsAFK("player") then SendChatMessage(AucAdvanced.Settings.GetSetting("util.scanstart."..scanId..".afk.text"), "AFK") end + end + end +end + + +--Config UI functions +function private.SetupConfigGui(gui) + -- The defaults for the following settings are set in the lib.OnLoad function + local emoteList = { + {"none" , "None (do not emote)"}, + {"APOLOGIZE" , "Apologize"}, + {"APPLAUD" , "Applaud"}, + {"BRB" , "BRB"}, + {"CACKLE" , "Cackle"}, + {"CHICKEN" , "Chicken"}, + {"DANCE" , "Dance"}, + {"FAREWELL" , "Farewell"}, + {"FLIRT" , "Flirt"}, + {"GLOAT" , "Gloat"}, + {"JOKE" , "Silly"}, + {"SLEEP" , "Sleep"}, + {"VICTORY" , "Victory"}, + {"YAWN" , "Yawn"} + } + local talkTypes = { + {"none", "None (do not send message)"}, + {"SAY", "Say (/s)"}, + {"PARTY","Party (/p)"}, + {"RAID","Raid (/r)"}, + {"GUILD","Guild (/g)"}, + {"YELL","Yell (/y)"}, + {"EMOTE","Emote (/em)"}, + {"GENERAL","General"} + } + + local id = gui:AddTab(libName, libType.." Modules") + gui:MakeScrollable(id) + + gui:AddHelp(id, "what is scanstart", + "What is ScanStart?", + "ScanStart is an AuctioneerAdvanced module that will execute one or more useful events when Auctioneer starts an AH scan.\n") + + gui:AddControl(id, "Header", 0, libName.." options") + gui:AddControl(id, "Checkbox", 0, 1, "util.scanstart.activated", "Allow the execution of events when a scan starts") + gui:AddTip(id, "Selecting this option will enable Auctioneer to perform the selected events once Auctioneer has started a scan. \n\nUncheck this to disable all events.") + + for scantype, scantypename in pairs(private.scanTypeList) do + gui:AddControl(id, "Subhead", 0, scantypename.." AH Scan") + gui:AddControl(id, "Checkbox", 0, 1, "util.scanstart."..scantype..".activated", + "Allow the execution of the events below when a "..scantypename.." scan starts") + gui:AddTip(id, "Selecting this option will enable Auctioneer to perform the selected events once Auctioneer has started a "..scantypename.." AH scan. \n\nUncheck this to disable events for a "..scantypename.." scan.") + gui:AddControl(id, "Selectbox", 0, 3, emoteList, "util.scanstart."..scantype..".emote", "Pick the Emote to perform") + gui:AddTip(id, "Selecting one of these emotes will cause your character to perform the selected emote once Auctioneer has started a scan successfully.\n\nBy selecting None, no emote will be performed.") + + gui:AddControl(id, "Text", 0, 1, "util.scanstart."..scantype..".message.text", "Message text:") + gui:AddTip(id, "Enter the message text of what you wish your character to say as well as choosing a channel below. \n\nThis will not execute slash commands.") + gui:AddControl(id, "Selectbox", 0, 3, talkTypes, "util.scanstart."..scantype..".message.channel", "Pick the channel to send your message to") + gui:AddTip(id, "Selecting one of these channels will cause your character to say the message text into the selected channel once Auctioneer has completed a scan successfully. \n\nBy choosing Emote, your character will use the text above as a custom emote. \n\nBy selecting None, no message will be sent.") + + + gui:AddControl(id, "Checkbox", 0, 1, "util.scanstart."..scantype..".dnd.activated", + "Set character status to DND when scan starts") + gui:AddControl(id, "Text", 0, 1, "util.scanstart."..scantype..".dnd.text", "DND Message:") + gui:AddTip(id, "Enter the text to return when a user whispers while DND is enabled.\n\nThis will not execute slash commands.") + gui:AddControl(id, "Checkbox", 0, 1, "util.scanstart."..scantype..".afk.activated", + "Set character status to AFK when scan starts") + gui:AddControl(id, "Text", 0, 1, "util.scanstart."..scantype..".afk.text", "AFK Message:") + gui:AddTip(id, "Enter the text to return when a user whispers while AFK is enabled.\n\nThis will not execute slash commands.") + end + + --Debug switch via gui. Currently not exposed to the end user + gui:AddControl(id, "Subhead", 0, "Debug ScanStart") + gui:AddControl(id, "Checkbox", 0, 1, "util.scanstart.debug", "Show Debug Information for this session") + + +end + +function lib.IsLibEmbedded() + blnResult = false + for pos, module in ipairs(AucAdvanced.EmbeddedModules) do + if "Auc-Util-"..libName == module then + if blnDebug then + print(" Debug:Auc-Util-"..libName.." is an embedded module") + end + blnResult = true + break + end + end + return blnResult +end + +function private.ConfigChanged() + --Debug switch via gui. Currently not exposed to the end user + --blnDebug = AucAdvanced.Settings.GetSetting("util.scanfinish.debug") + if blnDebug then + print(" Debug:Configuration Changed") + end + if AucAdvanced.Settings.GetSetting("util.scanstart.debug") then blnDebug = true end +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-ScanStart/ScanStart.lua $", "$Rev: 4840 $") diff --git a/Auc-Advanced/Modules/Auc-Util-SearchUI/Auc-Util-SearchUI.toc b/Auc-Advanced/Modules/Auc-Util-SearchUI/Auc-Util-SearchUI.toc new file mode 100644 index 0000000..40c2ac3 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-SearchUI/Auc-Util-SearchUI.toc @@ -0,0 +1,14 @@ +## Title: Auc:Util:Search UI +## Notes: Auctioneer utility module that aids in the search for items posted on the Auction House. Extremely flexible, with multiple, fully configurable Searchers to quickly and easily find specific items to use, resell, craft, and more. +## +## Interface: 40000 +## Dependencies: Auc-Advanced, Stubby +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Auc-Util-SearchUI.toc 4933 2010-10-13 17:16:14Z Nechckn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## X-Max-Interface: 40000 +## +Embed.xml diff --git a/Auc-Advanced/Modules/Auc-Util-SearchUI/DoorBell.mp3 b/Auc-Advanced/Modules/Auc-Util-SearchUI/DoorBell.mp3 new file mode 100644 index 0000000..c53094b Binary files /dev/null and b/Auc-Advanced/Modules/Auc-Util-SearchUI/DoorBell.mp3 differ diff --git a/Auc-Advanced/Modules/Auc-Util-SearchUI/Embed.xml b/Auc-Advanced/Modules/Auc-Util-SearchUI/Embed.xml new file mode 100644 index 0000000..ed0c007 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-SearchUI/Embed.xml @@ -0,0 +1,23 @@ + + + + diff --git a/Auc-Advanced/Modules/Auc-Util-SearchUI/FilterItemAuctionHistory.lua b/Auc-Advanced/Modules/Auc-Util-SearchUI/FilterItemAuctionHistory.lua new file mode 100644 index 0000000..ab6b329 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-SearchUI/FilterItemAuctionHistory.lua @@ -0,0 +1,139 @@ +--[[ + Auctioneer - Search UI - Filter IgnoreItemauctionhistory + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: FilterItemAuctionHistory.lua 4432 2009-08-29 14:55:35Z dinesh $ + URL: http://auctioneeraddon.com/ + + This is a plugin module for the SearchUI that assists in searching by refined paramaters + + 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 licence 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 +--]] +-- Create a new instance of our lib with our parent +local lib, parent, private = AucSearchUI.NewFilter("ItemAuctionHistory") +if not lib then return end +local print,decode,_,_,replicate,empty,_,_,_,debugPrint,fill = AucAdvanced.GetModuleLocals() +local get,set,default,Const = AucSearchUI.GetSearchLocals() +lib.tabname = "ItemAuctionHistory" + +-- Set our defaults +default("ignoreitemauctionhistory.enable", false) +default("ignoreitemauctionhistory.maxtime", 2) +default("ignoreitemauctionhistory.target_ratio", 0.2) +default("ignoreitemauctionhistory.min_scan_filter", 2) + +-- This function is automatically called when we need to create our search parameters +function lib:MakeGuiConfig(gui) + -- Get our tab and populate it with our controls + local id = gui:AddTab(lib.tabname, "Filters") + gui:MakeScrollable(id) + + gui:AddControl(id, "Header", 0, "ItemAuctionHistory Filter Criteria") + + local last = gui:GetLast(id) + gui:AddControl(id, "Checkbox", 0, 1, "ignoreitemauctionhistory.enable", "Enable item auction history filtering") + + gui:AddControl(id, "Subhead", 0, "Never filter unless there at least this many data points:") + gui:AddControl(id, "WideSlider", 0, 1, "ignoreitemauctionhistory.min_scan_filter", 0, 100, 1, "%s") + + gui:AddControl(id, "Subhead", 0, "Filter at and below this success/total ratio:") + gui:AddControl(id, "WideSlider", 0, 1, "ignoreitemauctionhistory.target_ratio", 0, 1, 0.05, "%s") + + gui:SetLast(id, last) + gui:AddControl(id, "Subhead", .5, "Filter for:") + for name, searcher in pairs(AucSearchUI.Searchers) do + if searcher and searcher.Search then + gui:AddControl(id, "Checkbox", 0.5, 1, "ignoreitemauctionhistory.filter."..name, name) + gui:AddTip(id, "Filter Item Auction History when searching with "..name) + default("ignoreitemauctionhistory.filter."..name, false) + end + end +end + +--lib.Filter(item, searcher) +--This function will return true if the item is to be filtered +--Item is the itemtable, and searcher is the name of the searcher being called. If searcher is not given, it will assume you want it active. +function lib.Filter(item, searcher) + + if (not get("ignoreitemauctionhistory.enable")) + or (get("ignoreitemauctionhistory.onlyonbids")) + or (searcher and (not get("ignoreitemauctionhistory.filter."..searcher))) then + return + end + + local price = 0 + local filterModule=false + + -- If this item is grey, forget about it. + if (item.qual == 0) then return end + + local itemId = item[Const.ITEMID] + + local _, _, itemRarity, itemLevel, _, itemType = GetItemInfo(itemId) + + if (not itemLevel) then return end + if (not itemType) then return end + + local minLevel=0 + + local success = 0 + local failed = 0 + + if BeanCounter and BeanCounter.Private.playerData then + if BeanCounter.Private.playerData["completedAuctions"][tostring(itemId)] then + for key in pairs(BeanCounter.Private.playerData["completedAuctions"][tostring(itemId)]) do + success = #BeanCounter.Private.playerData["completedAuctions"][tostring(itemId)][key] + end + end + if BeanCounter.Private.playerData["failedAuctions"][tostring(itemId)] then + for key in pairs(BeanCounter.Private.playerData["failedAuctions"][tostring(itemId)]) do + failed = #BeanCounter.Private.playerData["failedAuctions"][tostring(itemId)][key] + end + end + end + + local s = success + local f = failed + + local target_ratio = get("ignoreitemauctionhistory.target_ratio") + local min_scan_filter = get("ignoreitemauctionhistory.min_scan_filter") + + local actual_ratio = 0 + + -- if we have fewer than the minimum sightings, we're not filtering + if (s+f) < min_scan_filter then + return false -- not filtering + end + + actual_ratio = s/(s+f) + + local sName, sLink, iRarity, iLevel, iMinLevel, sType, sSubType, iStackCount = GetItemInfo(itemId); + + if (actual_ratio <= target_ratio) then +-- print("Filtered "..sLink.." "..tostring(actual_ratio).." <= "..tostring(target_ratio)) + return true, actual_ratio + end + + return false +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-SearchUI/FilterItemAuctionHistory.lua $", "$Rev: 4432 $") diff --git a/Auc-Advanced/Modules/Auc-Util-SearchUI/FilterItemLevel.lua b/Auc-Advanced/Modules/Auc-Util-SearchUI/FilterItemLevel.lua new file mode 100644 index 0000000..bedf64c --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-SearchUI/FilterItemLevel.lua @@ -0,0 +1,105 @@ +--[[ + Auctioneer - Search UI - Filter IgnoreItemQuality + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: FilterItemLevel.lua 4432 2009-08-29 14:55:35Z dinesh $ + URL: http://auctioneeraddon.com/ + + This is a plugin module for the SearchUI that assists in searching by refined parameters + + 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 +--]] +-- Create a new instance of our lib with our parent +local lib, parent, private = AucSearchUI.NewFilter("ItemLevel") +if not lib then return end +local print,decode,_,_,replicate,empty,_,_,_,debugPrint,fill = AucAdvanced.GetModuleLocals() +local get,set,default,Const = AucSearchUI.GetSearchLocals() +lib.tabname = "ItemLevel" +-- Set our defaults +default("ignoreitemlevel.enable", false) + +local typename + +-- This function is automatically called when we need to create our search parameters +function lib:MakeGuiConfig(gui) + if not typename then + typename = {GetAuctionItemClasses()} + end + -- Get our tab and populate it with our controls + local id = gui:AddTab(lib.tabname, "Filters") + gui:MakeScrollable(id) + + -- Add the help + gui:AddSearcher("Item Level", "Filter out items based on their level and type", 600) + gui:AddHelp(id, "itemlevel filter", + "What does this filter do?", + "This filter provides the ability to filter out specific item types which are below a preset level threshold. It can selectively apply it's filters only for certain types of searches.") + + gui:AddControl(id, "Header", 0, "ItemLevel Filter Criteria") + + gui:AddControl(id, "Checkbox", 0, 1, "ignoreitemlevel.enable", "Enable ItemLevel filtering") + gui:AddControl(id, "Subhead", 0, "Filter for:") + for name, searcher in pairs(AucSearchUI.Searchers) do + if searcher and searcher.Search then + gui:AddControl(id, "Checkbox", 0, 1, "ignoreitemlevel.filter."..name, name) + gui:AddTip(id, "Filter Item Level when searching with "..name) + default("ignoreitemlevel.filter."..name, false) + end + end + +-- Assume valid minimum item level is 0 and valid max item level is 300. +-- Configure slider controls to reflect this range of values. +-- See norganna.org JIRA ASER-106 and ASER-132 for additional info about this value range. + gui:AddControl(id, "Subhead", 0, "Minimum itemLevels by Type") + for i = 1, #typename do + default("ignoreitemlevel.minlevel."..typename[i], 61) + gui:AddControl(id, "WideSlider", 0, 1, "ignoreitemlevel.minlevel."..typename[i], 0, 300, 1, "Min iLevel for "..typename[i]..": %s") + end +end + +--lib.Filter(item, searcher) +--This function will return true if the item is to be filtered +--Item is the itemtable, and searcher is the name of the searcher being called. If searcher is not given, it will assume you want it active. +function lib.Filter(item, searcher) + if not typename then + typename = {GetAuctionItemClasses()} + end + + if (not get("ignoreitemlevel.enable")) + or (searcher and (not get("ignoreitemlevel.filter."..searcher))) then + return + end + + local itype = item[Const.ITYPE] + local ilevel = item[Const.ILEVEL] + + local minlevel = get("ignoreitemlevel.minlevel."..itype) + if not ilevel then + return true, "Error: no ilevel" + elseif not minlevel then + return true, "Error: no min level set for "..itype + elseif ilevel < minlevel then + return true, "ItemLevel too low" + end +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-SearchUI/FilterItemLevel.lua $", "$Rev: 4432 $") diff --git a/Auc-Advanced/Modules/Auc-Util-SearchUI/FilterItemPrice.lua b/Auc-Advanced/Modules/Auc-Util-SearchUI/FilterItemPrice.lua new file mode 100644 index 0000000..65de3fd --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-SearchUI/FilterItemPrice.lua @@ -0,0 +1,265 @@ +--[[ + Auctioneer - Search UI - Filter IgnoreItemPrice + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: FilterItemPrice.lua 4840 2010-08-04 21:44:00Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This is a plugin module for the SearchUI that assists in searching by refined paramaters + + 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 +--]] +-- Create a new instance of our lib with our parent +local lib, parent, private = AucSearchUI.NewFilter("ItemPrice") +if not lib then return end +local print,decode,_,_,replicate,empty,_,_,_,debugPrint,fill = AucAdvanced.GetModuleLocals() +local get,set,default,Const = AucSearchUI.GetSearchLocals() +lib.tabname = "ItemPrice" + +-- Set our defaults +default("ignoreitemprice.enable", true) + +local ignorelist = {} +private.tempignorelist = {} +private.sheetdata = {} + +function private.OnEnterSheet(button, row, index) + if private.ignorelistGUI.sheet.rows[row][index]:IsShown()then --Hide tooltip for hidden cells + local link, name + link = private.ignorelistGUI.sheet.rows[row][index]:GetText() + name = GetItemInfo(link) + if link and name then + GameTooltip:SetOwner(button, "ANCHOR_RIGHT") + AucAdvanced.ShowItemLink(GameTooltip, link, count) + end + end +end + +function private.OnLeaveSheet(button, row, index) + GameTooltip:Hide() +end + +function private.OnClickSheet(button, row, index) +end + +--Processor function +--this handles any notifications that SearchUI core needs to send us +function lib.Processor(msg, ...) + if msg == "config" then --saved search has changed, so reload the ignorelist + ignorelist = get("ignoreitemprice.ignorelist") + if not ignorelist then + ignorelist = {} + end + empty(private.sheetdata) + for item, cost in pairs(ignorelist) do + local link = AucAdvanced.API.GetLinkFromSig(item) + if not link then + link = item + end + table.insert(private.sheetdata, {link, cost}) + end + if private.ignorelistGUI and private.ignorelistGUI.sheet then + private.ignorelistGUI.sheet:SetData(private.sheetdata) + end + end +end + +lib.Processors = {} +function lib.Processors.config(msg, ...) + ignorelist = get("ignoreitemprice.ignorelist") + if not ignorelist then + ignorelist = {} + end + empty(private.sheetdata) + for item, cost in pairs(ignorelist) do + local link = AucAdvanced.API.GetLinkFromSig(item) + if not link then + link = item + end + table.insert(private.sheetdata, {link, cost}) + end + if private.ignorelistGUI and private.ignorelistGUI.sheet then + private.ignorelistGUI.sheet:SetData(private.sheetdata) + end +end + +--lib.AddIgnore(sig, price, temp) +--this function adds an item to the ignorelist at copper price +--if temp is true then item will be added to the temp list, which doesn't last across sessions +--this setting will override any current setting for that item +--if price==nil, item will be removed from the list +function lib.AddIgnore(sig, price, temp) + if temp then + private.tempignorelist[sig] = price + else + ignorelist[sig] = price + set("ignoreitemprice.ignorelist", ignorelist) + AucSearchUI.CleanTable(private.sheetdata) + for item, cost in pairs(ignorelist) do + local link = AucAdvanced.API.GetLinkFromSig(item) + if not link then + link = item + end + table.insert(private.sheetdata, {link, cost}) + end + if private.ignorelistGUI and private.ignorelistGUI.sheet then + private.ignorelistGUI.sheet:SetData(private.sheetdata) + end + end +end + +--private.remove() +--removes the selected item from the ignore list +function private.remove() + local link = private.ignorelistGUI.sheet:GetSelection()[1] + if link then + if string.find(link, "item:") then + local sig = AucAdvanced.API.GetSigFromLink(link) + if sig then + lib.AddIgnore(sig) --second var is nil, removes item from list + end + else + lib.AddIgnore(link) + end + end +end + +-- This function is automatically called when we need to create our search parameters +function lib:MakeGuiConfig(gui) + local ScrollSheet = LibStub("ScrollSheet") + + -- Get our tab and populate it with our controls + local id = gui:AddTab(lib.tabname, "Filters") + gui:MakeScrollable(id) + + -- Add the help + gui:AddSearcher("Item Price", "Filter specific items by their price", 600) + gui:AddHelp(id, "itemprice filter", + "What does this filter do?", + "This filter provides the ability to exclude specific items that exceed a certain \"ignore\" price. You can selectively apply this filter to specific searches.") + + gui:AddControl(id, "Header", 0, "ItemPrice Filter Criteria") + + gui:AddControl(id, "Checkbox", 0, 1, "ignoreitemprice.enable", "Enable ItemPrice filtering") + gui:AddControl(id, "Subhead", 0, "Filter for:") + for name, searcher in pairs(AucSearchUI.Searchers) do + if searcher and searcher.Search then + gui:AddControl(id, "Checkbox", 0, 1, "ignoreitemprice.filter."..name, name) + gui:AddTip(id, "Filter ItemPrice (Ignorelist) when searching with "..name) + default("ignoreitemprice.filter."..name, true) + end + end + + function private.UpdateControls() + if private.ignorelistGUI.sheet.selected then + private.removebutton:Enable() + else + private.removebutton:Disable() + end + end + + private.ignorelistGUI = CreateFrame("Frame", nil, gui.tabs[id][3]) + private.ignorelistGUI:SetPoint("BOTTOMRIGHT", gui.tabs[id][3], "TOPRIGHT", -50, -200) + private.ignorelistGUI:SetPoint("TOPLEFT", gui.tabs[id][3], "TOPRIGHT", -350, -20) + private.ignorelistGUI:SetBackdrop({ + bgFile = "Interface/Tooltips/ChatBubble-Background", + edgeFile = "Interface/Tooltips/ChatBubble-BackDrop", + tile = true, tileSize = 32, edgeSize = 32, + insets = { left = 32, right = 32, top = 32, bottom = 32 } + }) + private.ignorelistGUI:SetBackdropColor(0, 0, 0, 1) + + private.ignorelistGUI.sheet = ScrollSheet:Create(private.ignorelistGUI, { + { "Item", "TOOLTIP", 170}, + { "Ignore Price", "COIN", 90}, + }, private.OnEnterSheet, private.OnLeaveSheet, private.OnClickSheet, nil, private.UpdateControls) + private.ignorelistGUI.sheet:EnableSelect(true) + private.ignorelistGUI.sheet:SetData(private.sheetdata) + + private.removebutton = CreateFrame("Button", nil, gui.tabs[id][3], "OptionsButtonTemplate") + private.removebutton:SetPoint("TOPRIGHT", private.ignorelistGUI, "TOPLEFT", -10, -20) + private.removebutton:SetText("Remove Selected") + private.removebutton:SetWidth(150) + private.removebutton:SetScript("OnClick", private.remove) + private.removebutton:Disable() + + local selected +end + +--lib.Filter(item, searcher) +--This function will return true if the item is to be filtered +--Item is the itemtable, and searcher is the name of the searcher being called. If searcher is not given, it will assume you want it active. +--This function only checks if the min bid needed would be above the ignore amount, because this filter prevents the searchers from looking at the item in the first place. +--lib.PostFilter will be run after the searcher is done, to verify that the price found isn't above ignore value. +function lib.Filter(item, searcher) + if (not get("ignoreitemprice.enable")) + or (searcher and (not get("ignoreitemprice.filter."..searcher))) then + return + end + if not item[Const.PRICE] then DevTools_Dump(item) end + local price = item[Const.PRICE] + local count = item[Const.COUNT] or 1 + price = math.floor(price/count) + + local sig = AucAdvanced.API.GetSigFromLink(item[Const.LINK]) + if ignorelist[sig] then + if price >= ignorelist[sig] then + return true, "Item ignored at "..AucAdvanced.Coins(ignorelist[sig], true) + end + end + if private.tempignorelist[sig] then + if price >= private.tempignorelist[sig] then + return true, "Item ignored for session at "..AucAdvanced.Coins(private.tempignorelist[sig], true) + end + end +end + +--lib.PostFilter(item, searcher, buyorbid) +--Similar to lib.Filter, but should get called after the searcher, and gets passed the searcher's buyorbid return +function lib.PostFilter(item, searcher, buyorbid) + if (not get("ignoreitemprice.enable")) + or (searcher and (not get("ignoreitemprice.filter."..searcher))) then + return + end + local price + if buyorbid and buyorbid == "bid" then + price = item[Const.PRICE] + else + price = item[Const.BUYOUT] + end + local count = item[Const.COUNT] or 1 + price = math.floor(price/count) + + local sig = AucAdvanced.API.GetSigFromLink(item[Const.LINK]) + if ignorelist[sig] then + if price >= ignorelist[sig] then + return true, "Item ignored at "..AucAdvanced.Coins(ignorelist[sig], true) + end + end + if private.tempignorelist[sig] then + if price >= private.tempignorelist[sig] then + return true, "Item ignored for session at "..AucAdvanced.Coins(private.tempignorelist[sig], true) + end + end +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-SearchUI/FilterItemPrice.lua $", "$Rev: 4840 $") diff --git a/Auc-Advanced/Modules/Auc-Util-SearchUI/FilterItemQuality.lua b/Auc-Advanced/Modules/Auc-Util-SearchUI/FilterItemQuality.lua new file mode 100644 index 0000000..2aa282c --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-SearchUI/FilterItemQuality.lua @@ -0,0 +1,121 @@ +--[[ + Auctioneer - Search UI - Filter IgnoreItemQuality + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: FilterItemQuality.lua 4432 2009-08-29 14:55:35Z dinesh $ + URL: http://auctioneeraddon.com/ + + This is a plugin module for the SearchUI that assists in searching by refined paramaters + + 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 +--]] +-- Create a new instance of our lib with our parent +local lib, parent, private = AucSearchUI.NewFilter("ItemQuality") +if not lib then return end +local print,decode,_,_,replicate,empty,_,_,_,debugPrint,fill = AucAdvanced.GetModuleLocals() +local get,set,default,Const = AucSearchUI.GetSearchLocals() +lib.tabname = "ItemQuality" +-- Set our defaults +default("ignoreitemquality.enable", false) + +local typename + +local qualname = { + [0] = "Poor", + [1] = "Common", + [2] = "Uncommon", + [3] = "Rare", + [4] = "Epic", + [5] = "Legendary", + [6] = "Artifact", +} + +-- This function is automatically called when we need to create our search parameters +function lib:MakeGuiConfig(gui) + if not typename then + typename = {GetAuctionItemClasses()} + end + -- Get our tab and populate it with our controls + local id = gui:AddTab(lib.tabname, "Filters") + gui:MakeScrollable(id) + + -- Add the help + gui:AddSearcher("ItemQuality", "Filter items by quality and type", 600) + gui:AddHelp(id, "itemquality filter", + "What does this filter do?", + "This filter provides the ability to filter out specific item types which have a given quality. It can selectively apply it's filters only for certain types of searches.") + + gui:AddControl(id, "Header", 0, "ItemQuality Filter Criteria") + + gui:AddControl(id, "Checkbox", 0, 1, "ignoreitemquality.enable", "Enable ItemQuality filtering") + gui:AddControl(id, "Subhead", 0, "Filter for:") + for name, searcher in pairs(AucSearchUI.Searchers) do + if searcher and searcher.Search then + gui:AddControl(id, "Checkbox", 0, 1, "ignoreitemquality.filter."..name, name) + gui:AddTip(id, "Filter Item Quality when searching with "..name) + default("ignoreitemquality.filter."..name, false) + end + end + + gui:AddControl(id, "Subhead", 0, "Ignore Item Quality by Type") + for i = 0, 6 do + local last = gui:GetLast(id) + gui:AddControl(id, "Note", i*0.07, 1, 50, 20, qualname[i]) + if i < 6 then + gui:SetLast(id, last) + end + end + for i = 1, #typename do + for j = 0, 6 do + local last = gui:GetLast(id) + gui:AddControl(id, "Checkbox", j*0.07+0.02, 1, "ignoreitemquality."..typename[i].."."..qualname[j], "") + gui:AddTip(id, "Ignore "..qualname[j].." "..typename[i]) + gui:SetLast(id, last) + end + gui:AddControl(id, "Note", 0.49, 1, 200, 20, typename[i]) + end +end + +--lib.Filter(item, searcher) +--This function will return true if the item is to be filtered +--Item is the itemtable, and searcher is the name of the searcher being called. If searcher is not given, it will assume you want it active. +function lib.Filter(item, searcher) + if not typename then + typename = {GetAuctionItemClasses()} + end + if (not get("ignoreitemquality.enable")) + or (searcher and (not get("ignoreitemquality.filter."..searcher))) then + return + end + + local itype = item[Const.ITYPE] + local quality = item[Const.QUALITY] + quality = qualname[quality] + if not quality then return end -- protection against Blizzard returning an unexpected value, e.g. after a patch + + if get("ignoreitemquality."..itype.."."..quality) then + return true, quality.." "..itype.." filtered" + end + return false +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-SearchUI/FilterItemQuality.lua $", "$Rev: 4432 $") diff --git a/Auc-Advanced/Modules/Auc-Util-SearchUI/FilterTimeLeft.lua b/Auc-Advanced/Modules/Auc-Util-SearchUI/FilterTimeLeft.lua new file mode 100644 index 0000000..8a0d39a --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-SearchUI/FilterTimeLeft.lua @@ -0,0 +1,111 @@ +--[[ + Auctioneer - Search UI - Filter IgnoreTimeLeft + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: FilterTimeLeft.lua 4432 2009-08-29 14:55:35Z dinesh $ + URL: http://auctioneeraddon.com/ + + This is a plugin module for the SearchUI that assists in searching by refined paramaters + + 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 +--]] +-- Create a new instance of our lib with our parent +local lib, parent, private = AucSearchUI.NewFilter("TimeLeft") +if not lib then return end +local print,decode,_,_,replicate,empty,_,_,_,debugPrint,fill = AucAdvanced.GetModuleLocals() +local get,set,default,Const = AucSearchUI.GetSearchLocals() +lib.tabname = "Timeleft" +-- Set our defaults +default("ignoretimeleft.enable", false) +default("ignoretimeleft.maxtime", 2) + +-- This function is automatically called when we need to create our search parameters +function lib:MakeGuiConfig(gui) + -- Get our tab and populate it with our controls + local id = gui:AddTab(lib.tabname, "Filters") + gui:MakeScrollable(id) + + -- Add the help + gui:AddSearcher("Time Left", "Filter out items based on how long is left for the auction", 600) + gui:AddHelp(id, "timeleft filter", + "What does this filter do?", + "This filter provides the ability to filter out items that have more than a given amount of time left at auction. It can selectively apply it's filters only for certain types of searches.") + + gui:AddControl(id, "Header", 0, "TimeLeft Filter Criteria") + + local last = gui:GetLast(id) + gui:AddControl(id, "Checkbox", 0, 1, "ignoretimeleft.enable", "Enable time-left filtering") + gui:AddControl(id, "Checkbox", 0, 2, "ignoretimeleft.onlyonbids", "Only filter for bids") + gui:AddControl(id, "Subhead", 0, "Filter if more than") + gui:AddControl(id, "Selectbox", 0, 2, { + {1, "less than 30 min"}, + {2, "2 hours"}, + {3, "12 hours"}, + }, "ignoretimeleft.maxtime", "Max time left") + + gui:SetLast(id, last) + gui:AddControl(id, "Subhead", .5, "Filter for:") + for name, searcher in pairs(AucSearchUI.Searchers) do + if searcher and searcher.Search then + gui:AddControl(id, "Checkbox", 0.5, 1, "ignoretimeleft.filter."..name, name) + gui:AddTip(id, "Filter Time-left when searching with "..name) + default("ignoretimeleft.filter."..name, false) + end + end +end + +--lib.Filter(item, searcher) +--This function will return true if the item is to be filtered +--Item is the itemtable, and searcher is the name of the searcher being called. If searcher is not given, it will assume you want it active. +function lib.Filter(item, searcher) + if (not get("ignoretimeleft.enable")) + or (get("ignoretimeleft.onlyonbids")) + or (searcher and (not get("ignoretimeleft.filter."..searcher))) then + return + end + local maxtime = get("ignoretimeleft.maxtime") + --now to check the time left on the auction + local tleft = item[Const.TLEFT] + if tleft > maxtime then + return true, "Time left too high" + end + return false +end + +--PostFilter is only needed when we're restricting to bids +function lib.PostFilter(item, searcher, buyorbid) + if (not get("ignoretimeleft.enable")) + or (not get("ignoretimeleft.onlyonbids")) + or (buyorbid ~= "bid") + or (searcher and (not get("ignoretimeleft.filter."..searcher))) then + return + end + local maxtime = get("ignoretimeleft.maxtime") + --now to check the time left on the auction + local tleft = item[Const.TLEFT] + if tleft > maxtime then + return true, "Time left too high" + end + return false +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-SearchUI/FilterTimeLeft.lua $", "$Rev: 4432 $") diff --git a/Auc-Advanced/Modules/Auc-Util-SearchUI/SearchMain.lua b/Auc-Advanced/Modules/Auc-Util-SearchUI/SearchMain.lua new file mode 100644 index 0000000..e8b9221 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-SearchUI/SearchMain.lua @@ -0,0 +1,2062 @@ +--[[ + Auctioneer - Search UI + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: SearchMain.lua 4933 2010-10-13 17:16:14Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This Addon provides a Search tab on the AH interface, which allows + Auctioneer users to use Searcher plug-ins to search for good deals + in the auction house. It searches the "snapshot", which requires + having data from recent auction house scans. + + 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 +--]] + +local libType, libName = "Util", "SearchUI" +local AucAdvanced = AucAdvanced +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end +local print,decode,_,_,replicate,_,get,set,default,debugPrint,fill = AucAdvanced.GetModuleLocals() +local debugPrint = AucAdvanced.Debug.DebugPrint + +local empty = wipe +local ipairs,pairs,type,select = ipairs,pairs,type,select +local tostring,tonumber = tostring,tonumber +local floor,ceil,abs = floor,ceil,abs +local strmatch,format = strmatch,format +local tinsert,tremove = tinsert,tremove +-- GLOBALS: CreateFrame, GameTooltip + +-- Our official name: +AucSearchUI = lib + +function lib.GetName() + return libName +end + +local Const = AucAdvanced.Const +local gui +private.data = {} +private.sheetData = {} +private.sheetStyle = {} +private.isSearching = false +local coSearch +local SettingCache = {} +local currentSettings = {} +local hasUnsaved = nil + +local TAB_NAME = "Search" + +private.tleft = { + "|cff000001|cffe5e5e530m", -- 30m + "|cff000002|cffe5e5e52h", --2h + "|cff000003|cffe5e5e512h", --12h + "|cff000004|cffe5e5e548h" --48h +} + +lib.CleanTable = wipe -- for compatibility + +local resources = {} +lib.Resources = resources +local flagResourcesUpdateRequired = false +local flagScanStats = false + +-- Faction Resources +-- Commonly used values which change depending whether you are at home or neutral Auctionhouse +-- Modules should expect these to always contain valid values; nil tests should not be required +resources.Realm = GetRealmName() -- will not change during session +function private.UpdateFactionResources() + local serverKey, _, Faction = AucAdvanced.GetFaction() + if serverKey ~= resources.serverKey then + -- store new settings + resources.Faction = Faction + resources.faction = Faction:lower() -- lowercase for GetDepositCost + resources.serverKey = serverKey + resources.CutAdjust = 1 - AucAdvanced.cutRate -- multiply price by .CutAdjust to subtract the AH brokerage fees + -- notify the change + lib.NotifyCallbacks("resources", "faction", serverKey) + end +end +-- todo: we really should update when Zone changes, but there in't a processor event for that + +-- Selectbox Resources +--[[ Usages: +gui:AddControl(id, "Selectbox", column, indent, resources.selectorPriceModels, "searcher.model") +gui:AddControl(id, "Selectbox", column, indent, resources.selectorPriceModelsEnx, "searcher.model") +gui:AddControl(id, "Selectbox", column, indent, resources.selectorAuctionLength, "searcher.deplength") +local price, seen, curModel = resources.lookupPriceModel[model](model, link || itemID [, serverKey]) ~ price, seen or curModel may be nil +local price, seen, curModel = resources.GetPrice(model, link || itemID [, serverKey]) ~ simplified wrapper function for lookupPriceModel +if not resources.isValidPriceModel(get("searcher.model")) then +--]] +do -- limit scope of locals + resources.selectorAuctionLength = AucAdvanced.selectorAuctionLength + lib.AucLengthSelector = AucAdvanced.selectorAuctionLength -- for compatibility + resources.selectorPriceModels = AucAdvanced.selectorPriceModels + + local pricemodelsenx + function resources.selectorPriceModelsEnx() + if not pricemodelsenx then + pricemodelsenx = replicate(resources.selectorPriceModels()) + if resources.isEnchantrixLoaded then + tinsert(pricemodelsenx, 1, {"Enchantrix", "Enchantrix"}) + end + end + return pricemodelsenx + end + function private.ResetPriceModelEnx() + pricemodelsenx = nil + end + + function resources.isValidPriceModel(testmodel) + local pricemodels = resources.selectorPriceModelsEnx() -- make sure table is up to date + local found -- default return value nil + for pos, model in ipairs(pricemodels) do + if model[1] == testmodel then + found = model[2] + break + end + end + return found + end + + -- lookup functions + local function UnknownFunc() end -- return nil + + local function MarketFunc(model, link, serverKey) + local price, seen = AucAdvanced.API.GetMarketValue(link, serverKey) + return price, seen, model + end + + local function AppraiserFunc(model, link, serverKey) + local market, bid, _, seen, curModel = AucAdvanced.Modules.Util.Appraiser.GetPrice(link, serverKey) + if not market or market == 0 then + market = bid -- fallback to bid price if no market + end + return market, seen, curModel + end + + local function EnchantrixFunc(model, link, serverKey) + -- GetReagentPrice does not handle serverKey, and it does not return a seen count + local extra, mkt, five, _ + _, extra = Enchantrix.Util.GetPricingModel() + _, _, mkt, five = Enchantrix.Util.GetReagentPrice(link, extra) + return five or mkt, nil, extra or model + end + + local function AlgorithmFunc(model, link, serverKey) + local market, seen = AucAdvanced.API.GetAlgorithmValue(model, link, serverKey) + return market, seen, model + end + + local function indexFunc(lookup, model) + local func + if model == "Appraiser" and AucAdvanced.Modules.Util.Appraiser then + func = AppraiserFunc + elseif model == "Enchantrix" and resources.isEnchantrixLoaded then + func = EnchantrixFunc + elseif AucAdvanced.API.IsValidAlgorithm(model) then + func = AlgorithmFunc + end + if func then + rawset(lookup, model, func) + return func + end + return UnknownFunc + end + + local lookuppricemodel = setmetatable({market = MarketFunc}, {__index = indexFunc}) + + resources.lookupPriceModel = lookuppricemodel + function resources.GetPrice(model, link, serverKey) -- simplified wrapper + return lookuppricemodel[model](model, link, serverKey) + end +end + +-- Enchantrix Load detection +if Enchantrix and Enchantrix.Storage and Enchantrix.Util then + resources.isEnchantrixLoaded = true +else + local _, _, _, enabled, loadable = GetAddOnInfo("Enchantrix") -- check it's actually possible to load + if enabled and loadable then + Stubby.RegisterAddOnHook("Enchantrix", "Auc-Util-SearchUI", function() + if Enchantrix and Enchantrix.Storage and Enchantrix.Util then + Stubby.UnregisterAddOnHook("Enchantrix", "Auc-Util-SearchUI") + resources.isEnchantrixLoaded = true + private.ResetPriceModelEnx() + lib.NotifyCallbacks("onload", "enchantrix") + end + end) + end +end + +function lib.OnLoad(addon) + -- Notify that SearchUI is fully loaded + resources.isSearchUILoaded = true + lib.NotifyCallbacks("onload", addon) + + -- Initialize + private.UpdateFactionResources() +end + +function lib.Processor(callbackType, ...) + if callbackType == "load" then + private.ResetPriceModelEnx() + elseif (callbackType == "auctionclose") then + if private.isAttached then + lib.DetachFromAH() + end + flagResourcesUpdateRequired = true + elseif callbackType == "auctionopen" then + flagResourcesUpdateRequired = true + elseif (callbackType == "auctionui") then + if lib.Searchers.RealTime then + lib.Searchers.RealTime.HookAH() + end + + --we need to make sure that the GUI is made by the time the AH opens, as RealTime could be trying to add lines to it. + if not gui then + lib.MakeGuiConfig() + end + + lib.CreateAuctionFrames() + elseif (callbackType == "pagefinished") then + if lib.Searchers.RealTime then + lib.Searchers.RealTime.FinishedPage(...) + end + elseif (callbackType == "bidcancelled") then --bid was cancelled, we need to ignore auction for current session + private.bidcancelled(...) + elseif (callbackType == "tooltip") then + lib.ProcessTooltip(...) + elseif callbackType == "scanstats" then + -- pass the message in next OnUpdate + flagScanStats = true + elseif callbackType == "scanprogress" and private.UpdateScanProgress then + private.UpdateScanProgress(...) + elseif callbackType == "buyqueue" and private.UpdateBuyQueue then + private.UpdateBuyQueue() + end +end + +lib.Processors = {} +function lib.Processors.load(callbackType, ...) + private.ResetPriceModelEnx() +end + +function lib.Processors.auctionclose(callbackType, ...) + if private.isAttached then + lib.DetachFromAH() + end + flagResourcesUpdateRequired = true +end + +function lib.Processors.auctionopen(callbackType, ...) + flagResourcesUpdateRequired = true +end + +function lib.Processors.auctionui(callbackType, ...) + if lib.Searchers.RealTime then + lib.Searchers.RealTime.HookAH() + end + + --we need to make sure that the GUI is made by the time the AH opens, as RealTime could be trying to add lines to it. + if not gui then + lib.MakeGuiConfig() + end + + lib.CreateAuctionFrames() +end + +function lib.Processors.pagefinished(callbackType, ...) + if lib.Searchers.RealTime then + lib.Searchers.RealTime.FinishedPage(...) + end +end + +function lib.Processors.bidcancelled(callbackType, ...) + private.bidcancelled(...) +end + +function lib.Processors.tooltip(callbackType, ...) + lib.ProcessTooltip(...) +end + +function lib.Processors.scanstats(callbackType, ...) + -- pass the message in next OnUpdate + flagScanStats = true +end + +function lib.Processors.scanprogress(callbackType, ...) + if private.UpdateScanProgress then + private.UpdateScanProgress(...) + end +end + +function lib.Processors.buyqueue(callbackType, ...) + if private.UpdateBuyQueue then + private.UpdateBuyQueue() + end +end + +function lib.ProcessTooltip(tooltip, name, hyperlink, quality, quantity, cost, additional) + if not additional or additional.event ~= "SetAuctionItem" then + --this isn't an auction, so we're not interested + return + end + if not lib.GetSetting("debug.show") then + --we're not interested in adding debug + return + end + local button = GetMouseFocus():GetParent() --Note: check that it works without CompactUI + local id = button.id --CompactUI's layout + if not id then + id = button:GetID() + FauxScrollFrame_GetOffset(BrowseScrollFrame) --without CompactUI + end + + local name, _, count, _, canUse, level, minBid, minInc, buyout, curBid, isHigh, owner = GetAuctionItemInfo("list", id) + local price + if curBid > 0 then + price = curBid + minInc + if buyout > 0 and price > buyout then + price = buyout + end + elseif minBid > 0 then + price = minBid + else + price = 1 + end + owner = owner or "" + local timeleft = GetAuctionItemTimeLeft("list", id) + local _, _, _, iLevel, _, iType, iSubType, stack, iEquip = GetItemInfo(hyperlink) + iEquip = Const.EquipEncode[iEquip] + local _, itemid, suffix, factor, enchant, seed = AucAdvanced.DecodeLink(hyperlink) + local ItemTable = {} + -- put the data into a table laid out the same way as the AAdv Scandata, as that's what the searchers need + ItemTable[Const.LINK] = hyperlink + ItemTable[Const.ILEVEL] = iLevel + ItemTable[Const.ITYPE] = iType + ItemTable[Const.ISUB] = iSubType + ItemTable[Const.IEQUIP] = iEquip + ItemTable[Const.PRICE] = price + ItemTable[Const.TLEFT] = timeleft + ItemTable[Const.NAME] = name + ItemTable[Const.COUNT] = count + ItemTable[Const.QUALITY] = quality + ItemTable[Const.CANUSE] = canUse + ItemTable[Const.ULEVEL] = level + ItemTable[Const.MINBID] = minBid + ItemTable[Const.MININC] = minInc + ItemTable[Const.BUYOUT] = buyout + ItemTable[Const.CURBID] = curBid + ItemTable[Const.AMHIGH] = isHigh + ItemTable[Const.SELLER] = owner + ItemTable[Const.ITEMID] = itemid + ItemTable[Const.SUFFIX] = suffix + ItemTable[Const.FACTOR] = factor + ItemTable[Const.ENCHANT] = enchant + ItemTable[Const.SEED] = seed + + tooltip:AddLine("SearchUI:", 1, 0.7, 0.3) + + local active = false + for name, searcher in pairs(lib.Searchers) do + if lib.GetSetting("debug."..name) then + active = true + local success, returnvalue, value = lib.SearchItem(name, ItemTable, true, true) + if success then + if value then + tooltip:AddLine(" "..name.." profit:"..floor(100*returnvalue/value).."%:", returnvalue, 1, 0.7, 0.3) + elseif returnvalue then + tooltip:AddLine(" "..name.." profit:", returnvalue, 1, 0.7, 0.3) + else + tooltip:AddLine(" "..name.." success", 1, 0.7, 0.3) + end + else + tooltip:AddLine(" "..name..":"..returnvalue, 1, 0.7, 0.3) + end + end + end + if not active then --if it hasn't changed, we're not enabled for any searcher + tooltip:AddLine(" No debugging enabled", 1, 0.7, 0.3) + end +end + +-- Default setting values +local settingDefaults = { + ["processpriority"] = 80, + ["reserve"] = 0, + ["reserve.enable"] = false, + ["global.createtab"] = true, + ["maxprice"] = 10000000, + ["maxprice.enable"] = false, + ["tooltiphelp.show"] = true, + --Scrollframe defaults + ["columnwidth.Item"] = 120, + ["columnwidth.Pct"] = 30, + ["columnwidth.Profit"] = 85, + ["columnwidth.Stk"] = 30, + ["columnwidth.Buyout"] = 85, + ["columnwidth.Bid"] = 85, + ["columnwidth.Reason"] = 85, + ["columnwidth.Seller"] = 75, + ["columnwidth.Left"] = 40, + ["columnwidth.Buy/ea"] = 85, + ["columnwidth.Bid/ea"] = 85, + ["columnwidth.MinBid"] = 85, + ["columnwidth.CurBid"] = 85, + ["columnwidth.Min/ea"] = 85, + ["columnwidth.Cur/ea"] = 85, +} + +local function getDefault(setting) + return settingDefaults[setting] +end + +function lib.GetDefault(setting) + return getDefault(setting) +end + +function lib.SetDefault(setting, default) + settingDefaults[setting] = default +end + +local function initData() + local data = AucAdvancedData.UtilSearchUiData + if not data then + data = {} + data.Version = 1 + AucAdvancedData.UtilSearchUiData = data + end + if not data.SavedSearches then data.SavedSearches = {} end + if not data.Current then data.Current = {} end + if not data.Global then data.Global = {} end +end + +local function isGlobalSetting(setting) + local a,b,c = strsplit(".", setting) + if a == "configator" then return true end + if a == "global" then return true end + return +end + +local function setter(setting, value) + AucAdvancedData.UtilSearchUI = nil -- Remove old settings + initData() + + local db = currentSettings + + -- turn value into a canonical true or false + if value == 'on' then + value = true + elseif value == 'off' then + value = false + end + + if (isGlobalSetting(setting)) then + AucAdvancedData.UtilSearchUiData.Global[setting] = value + return + end + + -- for defaults, just remove the value and it'll fall through + if (value == 'default') or (value == getDefault(setting)) then + -- Don't save default values + value = nil + end + + if db[setting] == value then + if type(value) == "table" then + -- same table as before + -- call UpdateSave to check if the *contents* of the table have changed + -- but don't send Processor message as *setting* is the same (consistent with Core version of 'setter') + hasUnsaved = true + lib.UpdateSave() + end + return + end + db[setting] = value + + hasUnsaved = true + lib.UpdateSave() + + AucAdvanced.SendProcessorMessage("configchanged", setting, value) + lib.NotifyCallbacks('config', 'changed', setting, value) +end + +function lib.SetSetting(...) + setter(...) + if (gui) then + gui:Refresh() + end +end + +local function getter(setting) + initData() + local db = currentSettings + + if (isGlobalSetting(setting)) then + local value = AucAdvancedData.UtilSearchUiData.Global[setting] + if value ~= nil then return value end + return getDefault(setting) + end + + if ( db[setting] ~= nil ) then + return db[setting] + else + return getDefault(setting) + end +end + +function lib.GetSetting(setting, default) + --use settings cache during a search + if private.isSearching then + if SettingCache[setting]~=nil then + return SettingCache[setting] + end + end + local option = getter(setting) + if ( option ~= nil ) then + if private.isSearching then + SettingCache[setting] = option + end + return option + else + if private.isSearching then + SettingCache[setting] = default + end + return default + end +end + +function lib.Show() + if not gui then --no need to make the GUI if it already exists + lib.MakeGuiConfig() + end + gui:Show() + private.UpdateFactionResources() +end + +function lib.Hide() + if (gui) then + gui:Hide() + end +end + +function lib.Toggle() + if private.isAttached then return end + if (gui and gui:IsShown()) then + lib.Hide() + else + lib.Show() + end +end + +private.callbacks = {} +function lib.AddCallback(name, callback) + if private.callbacks[name] then -- prevent overwriting an exisiting callback + error("AucSearchUI.AddCallback Error:\nCallback for "..name.." already exists", 2) -- level 2 is calling function + end + private.callbacks[name] = callback + -- fire certain callbacks, that have already happened, to new callback function + if resources.isEnchantrixLoaded then + callback("onload", "enchantrix") + end + if resources.isSearchUILoaded then + callback("onload", "auc-util-searchui") + end + if gui then + callback("guiconfig", gui) + end +end + +function lib.NotifyCallbacks(msg, ...) + for name, callback in pairs(private.callbacks) do + callback(msg, ...) + end + + for name, searcher in pairs(lib.Searchers) do + local processor = searcher.Processor + if processor then + local pType = type(processor) + if pType == "table" then + processor = processor[msg] + if type(processor) == "function" then + processor(...) + end + elseif pType == "function" then + processor(msg, ...) + end + end + end + for name, filter in pairs(lib.Filters) do + local processor = filter.Processor + if processor then + local pType = type(processor) + if pType == "table" then + processor = processor[msg] + if type(processor) == "function" then + processor(...) + end + elseif pType == "function" then + processor(msg, ...) + end + end + end +end + +function lib.RemoveCallback(name, callback) + if callback and private.callbacks[name] == callback then + private.callbacks[name] = nil + return true + end +end + +local searcherKit = {} +function searcherKit:GetName() + return self.name +end + +lib.Searchers = {} +function lib.NewSearcher(searcherName) + if not lib.Searchers[searcherName] then + local searcher = {} + searcher.name = searcherName + for k,v in pairs(searcherKit) do + searcher[k] = v + end + + lib.Searchers[searcherName] = searcher + return searcher, lib, {} + end +end + +local filterKit = {} +function filterKit:GetName() + return self.name +end + +lib.Filters = {} +function lib.NewFilter(filterName) + if not lib.Filters[filterName] then + local filter = {} + filter.name = filterName + for k,v in pairs(filterKit) do + filter[k] = v + end + + lib.Filters[filterName] = filter + return filter, lib, {} + end +end + +function lib.GetSearchLocals() + return lib.GetSetting, lib.SetSetting, lib.SetDefault, Const, resources +end + +function private.removeline() + local selected = gui.sheet.selected + --find the place in the sort list, so we can select the next one. + for i,j in ipairs(gui.sheet.sort) do + if j == selected then + selected = i + break + end + end + tremove(private.sheetData, gui.sheet.selected) + --regenerate style elements, to match shorter data table + local total = #private.sheetData + for i = gui.sheet.selected, total do + private.sheetStyle[i] = private.sheetStyle[i+1] + if i == total then private.sheetStyle[i+1] = nil end --remove extra style + end + --gui.frame.remove:Disable() + gui.sheet.selected = nil + gui.sheet:SetData(private.sheetData, private.sheetStyle) + lib.UpdateControls() + gui.sheet.selected = gui.sheet.sort[selected] + gui.sheet:Render() --need to redraw, so the selection looks right + lib.UpdateControls() +end + +function private.removeall() + empty(private.sheetData) + empty(private.sheetStyle) + gui.sheet.selected = nil + gui.sheet:SetData(private.sheetData, private.sheetStyle) + gui.sheet:Render() --need to redraw, so the selection looks right + lib.UpdateControls() +end + +function private.repaintSheet() + local wasEmpty = #gui.sheet.data < 1 + gui.sheet:SetData(private.sheetData, private.sheetStyle) + if wasEmpty then --sheet was empty, so select the just added auction + gui.sheet.selected = 1 + gui.sheet:Render() --need to redraw, so the selection looks right + lib.UpdateControls() + end +end + +function private.cropreason(reason) + if reason then + reason = strsplit(":", reason) + return reason + end +end + +function private.buyauction() + AucAdvanced.Buy.QueueBuy(private.data.link, private.data.seller, private.data.stack, private.data.minbid, private.data.buyout, private.data.buyout, private.cropreason(private.data.reason)) + private.removeline() +end + +function private.bidauction() + local bid = MoneyInputFrame_GetCopper(gui.frame.bidbox) + AucAdvanced.Buy.QueueBuy(private.data.link, private.data.seller, private.data.stack, private.data.minbid, private.data.buyout, bid, private.cropreason(private.data.reason)) + private.removeline() +end + +function private.buyfirst() + gui.sheet.selected = gui.sheet.sort[1] + if not gui.sheet.selected then + return + end + lib.UpdateControls() + if strmatch(private.data.reason, ":buy") then + AucAdvanced.Buy.QueueBuy(private.data.link, private.data.seller, private.data.stack, private.data.minbid, private.data.buyout, private.data.buyout, private.cropreason(private.data.reason)) + elseif strmatch(private.data.reason, ":bid") then + AucAdvanced.Buy.QueueBuy(private.data.link, private.data.seller, private.data.stack, private.data.minbid, private.data.buyout, private.data.bid, private.cropreason(private.data.reason)) + elseif private.data.buyout > 0 then + AucAdvanced.Buy.QueueBuy(private.data.link, private.data.seller, private.data.stack, private.data.minbid, private.data.buyout, private.data.buyout, private.cropreason(private.data.reason)) + else + AucAdvanced.Buy.QueueBuy(private.data.link, private.data.seller, private.data.stack, private.data.minbid, private.data.buyout, private.data.bid, private.cropreason(private.data.reason)) + end + private.removeline() +end + +--private.purchase +--Will buy/bid selected auction based on "reason" column +function private.purchase() + if not gui.sheet.selected then + return + end + local enableres = lib.GetSetting("reserve.enable") + local reserve = lib.GetSetting("reserve") or 1 + local bidqueue = gui.frame.cancel.value or 0 + local balance = GetMoney() + balance = balance - bidqueue --account for money we've already "spent" + + local price = 0 + if strmatch(private.data.reason, ":buy") then + price = private.data.buyout + elseif strmatch(private.data.reason, ":bid") then + price = private.data.bid + elseif private.data.buyout > 0 then + price = private.data.buyout + else + price = private.data.bid + end + if ((balance-price) > reserve or not enableres) then + AucAdvanced.Buy.QueueBuy(private.data.link, private.data.seller, private.data.stack, private.data.minbid, private.data.buyout, price, private.cropreason(private.data.reason)) + else + print("Purchase cancelled: Reserve reached") + end + private.removeline() +end +--Will buy/bid ALL auctions based on "reason" column +function private.purchaseall() + gui.sheet.selected = gui.sheet.sort[1] + if not gui.sheet.selected then + return + end + lib.UpdateControls() + local count = 0 + while #gui.sheet.sort > 0 and (gui.sheet.sort[1] or count < 5000 ) do + count = count+1--emergency break routine + local enableres = lib.GetSetting("reserve.enable") + local reserve = lib.GetSetting("reserve") or 1 + local bidqueue = gui.frame.cancel.value or 0 + local balance = GetMoney() + balance = balance - bidqueue --account for money we've already "spent" + + local price = 0 + if strmatch(private.data.reason, ":buy") then + price = private.data.buyout + elseif strmatch(private.data.reason, ":bid") then + price = private.data.bid + elseif private.data.buyout > 0 then + price = private.data.buyout + else + price = private.data.bid + end + if ((balance-price) > reserve or not enableres) then + AucAdvanced.Buy.QueueBuy(private.data.link, private.data.seller, private.data.stack, private.data.minbid, private.data.buyout, price, private.cropreason(private.data.reason)) + else + print("Purchase cancelled: Reserve reached") + end + private.removeline() + end +end +function private.ignore() + local sig = AucAdvanced.API.GetSigFromLink(private.data.link) + local price + if strmatch(private.data.reason, ":buy") then + price = private.data.buyout + elseif strmatch(private.data.reason, ":bid") then + price = private.data.bid + elseif private.data.buyout > 0 then + price = private.data.buyout + else + price = private.data.bid + end + local count = private.data.stack or 1 + price = floor(price/count) + AucSearchUI.Filters.ItemPrice.AddIgnore(sig, price) + print("SearchUI now ignoring "..private.data.link.." at "..AucAdvanced.Coins(price, true)) + private.removeline() +end + +function private.ignoreperm() + local sig = AucAdvanced.API.GetSigFromLink(private.data.link) + AucSearchUI.Filters.ItemPrice.AddIgnore(sig, 0) + print("SearchUI now ignoring "..private.data.link.." at any price") + private.removeline() +end + +--a bid was cancelled, so ignore for session +function private.bidcancelled(callbackstring) + local link, price, count = strsplit(";", callbackstring) + local sig = AucAdvanced.API.GetSigFromLink(link) + local price = floor(price/count) - 1 + if AucSearchUI.Filters.ItemPrice then + AucSearchUI.Filters.ItemPrice.AddIgnore(sig, price, true) + print("SearchUI now ignoring "..link.." at "..AucAdvanced.Coins(price, true).." for the session") + end + +end + +function private.ignoretemp() + local sig = AucAdvanced.API.GetSigFromLink(private.data.link) + local price + if strmatch(private.data.reason, ":buy") then + price = private.data.buyout + elseif strmatch(private.data.reason, ":bid") then + price = private.data.bid + elseif private.data.buyout > 0 then + price = private.data.buyout + else + price = private.data.bid + end + local count = private.data.stack or 1 + price = floor(price/count) + AucSearchUI.Filters.ItemPrice.AddIgnore(sig, price, true) + print("SearchUI now ignoring "..private.data.link.." at "..AucAdvanced.Coins(price, true).." for the session") + private.removeline() +end + +function private.snatch() + local link = private.data.link + local price + if strmatch(private.data.reason, ":buy") then + price = private.data.buyout + elseif strmatch(private.data.reason, ":bid") then + price = private.data.bid + elseif private.data.buyout > 0 then + price = private.data.buyout + else + price = private.data.bid + end + local count = private.data.stack or 1 + price = floor(price/count) + 1 -- +1 so the current item also matches the search + lib.Searchers.Snatch.AddSnatch(link,price) + print("SearchUI will now snatch "..private.data.link.." at "..AucAdvanced.Coins(price, true)) +end + +local function keyPairs(t,f) + local a, i = {}, 0 + for n in pairs(t) do tinsert(a, n) end + table.sort(a, f) + local iter = function () + i = i + 1 + if a[i] == nil then return nil + else return a[i], t[a[i]] end + end + return iter +end + +local function isEqual(a, b, l) + if not l then l = 0 + elseif l > 10 then return false end + + local ta, tb = type(a), type(b) + if ta ~= tb then return false end + if ta == 'table' then + for k,v in pairs(a) do + if not isEqual(v, b[k], l+1) then return false end + end + for k,v in pairs(b) do + if not isEqual(v, a[k], l+1) then return false end + end + else + if tostring(a) ~= tostring(b) then + return false + end + end + return true +end +lib.IsEqual = isEqual + +function lib.LoadCurrent() + initData() + local name = gui.saves.name:GetText() + currentSettings = AucAdvancedData.UtilSearchUiData.Current + if not currentSettings then + lib.LoadSearch() + return + end + + local existing = AucAdvancedData.UtilSearchUiData.SavedSearches[name] + if not existing then + hasUnsaved = true + elseif not isEqual(currentSettings, existing) then + hasUnsaved = true + else + hasUnsaved = nil + end + lib.UpdateSave() + gui:Refresh() + lib.NotifyCallbacks('config', 'loaded', nil) +end + +function lib.LoadSearch() + initData() + local name = gui.saves.name:GetText() + if not AucAdvancedData.UtilSearchUiData.SavedSearches[name] then + message("SearchUI warning:\nThat search does not exist, please select an available search from the menu.") + return + end + currentSettings = replicate(AucAdvancedData.UtilSearchUiData.SavedSearches[name]) + AucAdvancedData.UtilSearchUiData.Current = currentSettings + AucAdvancedData.UtilSearchUiData.Selected = name + hasUnsaved = nil + lib.UpdateSave() + gui:Refresh() + lib.NotifyCallbacks('config', 'loaded', name) +end + +function lib.SaveSearch() + initData() + local name = gui.saves.name:GetText() + AucAdvancedData.UtilSearchUiData.SavedSearches[name] = replicate(currentSettings) + AucAdvancedData.UtilSearchUiData.Selected = name + hasUnsaved = nil + lib.UpdateSave() + gui:Refresh() + lib.NotifyCallbacks('config', 'saved', name) +end + +function lib.DeleteSearch() + initData() + local name = gui.saves.name:GetText() + AucAdvancedData.UtilSearchUiData.SavedSearches[name] = nil + hasUnsaved = nil + AucAdvancedData.UtilSearchUiData.Selected = "" + gui.saves.name:SetText("") + lib.UpdateSave() + gui:Refresh() + lib.NotifyCallbacks('config', 'deleted', name) +end + +function lib.ResetSearch() + initData() + currentSettings = {} + AucAdvancedData.UtilSearchUiData.Current = currentSettings + hasUnsaved = nil + AucAdvancedData.UtilSearchUiData.Selected = "" + gui.saves.name:SetText("") + lib.UpdateSave() + gui:Refresh() + lib.NotifyCallbacks('config', 'reset') +end + +local curColor = "white" +function lib.UpdateSave() + if not (gui and AucAdvancedData.UtilSearchUiData) then return end + + local name = gui.saves.name:GetText() + + if hasUnsaved then + local saved = AucAdvancedData.UtilSearchUiData.SavedSearches[name] + if saved and isEqual(currentSettings, saved) then + hasUnsaved = false + end + end + + if AucAdvancedData.UtilSearchUiData.Selected ~= name then + if curColor ~= "white" then + gui.saves.name:SetTextColor(1, 1, 1, 1) + curColor = "white" + end + elseif hasUnsaved then + if curColor ~= "red" then + gui.saves.name:SetTextColor(1, 0.5, 0.1, 1) + curColor = "red" + end + else + if curColor ~= "green" then + gui.saves.name:SetTextColor(0.3, 1, 0.3, 1) + curColor = "green" + end + end +end + +function lib.AddSearcher(gui, searchType, searchDetail, searchPos) + if not gui.searchers[searchPos] then gui.searchers[searchPos] = {} end + gui.searchers[searchPos][searchType] = searchDetail +end + +function lib.AttachToAH() + if private.isAttached then return end + local height, width = 410, 830 + gui.buttonTop = -30 + gui:SetPosition(gui.AuctionFrame, width, height, 5, 7+height) + gui:HideBackdrop() + gui:EnableMouse(false) + gui:RealSetScale(0.9999) + gui:RealSetScale(1.0) + gui:Show() + private.isAttached = true +end + +function lib.DetachFromAH() + if not private.isAttached then return end + gui.buttonTop = 0 + gui:SetPosition() + gui:EnableMouse(true) + gui:ShowBackdrop() + gui:RealSetScale(0.9999) + gui:RealSetScale(private.scale or 1) + gui:Hide() + private.isAttached = nil +end + +function lib.CreateAuctionFrames() + if not lib.GetSetting("global.createtab") then return end + + local frame = CreateFrame("Frame", "AucAdvSearchUiAuctionFrame", AuctionFrame) + gui.AuctionFrame = frame + + frame:SetParent(AuctionFrame) + frame:SetPoint("TOPLEFT", AuctionFrame, "TOPLEFT") + frame:SetPoint("BOTTOMRIGHT", AuctionFrame, "BOTTOMRIGHT") + + frame.title = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") + frame.title:SetPoint("TOP", frame, "TOP", 20, -17) + frame.title:SetText("SearchUI - Auction search interface") + + local myTabName = "AuctionFrameTabUtilSearchUi" + frame.tab = CreateFrame("Button", myTabName, AuctionFrame, "AuctionTabTemplate") + frame.tab:SetText(TAB_NAME) + frame.tab:Show() + + function frame.tab.OnClick(self, button, down) + local index = self:GetID() + local tab = _G["AuctionFrameTab"..index] + if (tab and tab:GetName() == myTabName) then + AuctionFrameTopLeft:SetTexture("Interface\\AddOns\\Auc-Advanced\\Textures\\AuctionFrameTopLeft") + AuctionFrameTop:SetTexture("Interface\\AddOns\\Auc-Advanced\\Textures\\AuctionFrameTopMid") + AuctionFrameTopRight:SetTexture("Interface\\AddOns\\Auc-Advanced\\Textures\\AuctionFrameTopRight") + AuctionFrameBotLeft:SetTexture("Interface\\AddOns\\Auc-Advanced\\Textures\\AuctionFrameBotLeft") + AuctionFrameBot:SetTexture("Interface\\AddOns\\Auc-Advanced\\Textures\\AuctionFrameBotMid") + AuctionFrameBotRight:SetTexture("Interface\\AddOns\\Auc-Advanced\\Textures\\AuctionFrameBotRight") + AuctionFrameMoneyFrame:Hide() + frame:Show() + lib.AttachToAH() + else + AuctionFrameMoneyFrame:Show() + frame:Hide() + lib.DetachFromAH() + end + end + + PanelTemplates_DeselectTab(frame.tab) + AucAdvanced.AddTab(frame.tab, frame) + + hooksecurefunc("AuctionFrameTab_OnClick", frame.tab.OnClick) + + frame.money = frame:CreateTexture(nil, "ARTWORK") + frame.money:SetTexture("Interface\\AddOns\\Auc-Advanced\\Textures\\GoldMoney") + frame.money:SetPoint("TOPLEFT", frame, "BOTTOMLEFT", 18, 34) + frame.money:SetWidth(256) + frame.money:SetHeight(32) + + frame.backing = CreateFrame("Frame", nil, frame) + frame.backing:SetPoint("TOPLEFT", frame, "TOPLEFT", 17, -70) + frame.backing:SetPoint("BOTTOMRIGHT", frame.money, "TOPLEFT", 145, 50) + frame.backing:SetBackdrop({ bgFile="Interface\\AddOns\\Auc-Advanced\\Textures\\BlackBack", edgeFile="Interface\\AddOns\\Auc-Advanced\\Textures\\WhiteCornerBorder", tile=1, tileSize=8, edgeSize=8, insets={left=3, right=3, top=3, bottom=3} }) + frame.backing:SetBackdropColor(0,0,0, 0.60) + + frame.scanslabel = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + frame.scanslabel:SetPoint("TOPLEFT", frame, "TOPLEFT", 72, -20) + frame.scanslabel:SetText("Pending Scans") + frame.scanscount = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + frame.scanscount:ClearAllPoints() + frame.scanscount:SetPoint("LEFT", frame.scanslabel, "RIGHT", 5, 0) + frame.scanscount:SetText("0") + frame.scanscount:SetJustifyH("RIGHT") + frame.scanscount.last = 0 + function private.UpdateScanProgress(_, _, _, _, _, _, _, scansQueued) + if AucAdvanced.Scan.IsScanning() then + scansQueued = scansQueued + 1 + end + if scansQueued ~= frame.scanscount.last then + frame.scanscount.last = scansQueued + frame.scanscount:SetText(scansQueued) + end + end +end + +function lib.MakeGuiConfig() + if gui then return end + + local Configator = LibStub("Configator") + local ScrollSheet = LibStub("ScrollSheet") + local id, last, cont + local selected + + gui = Configator:Create(setter,getter, 900, 500, 5, 350, 20, 5) + gui:SetBackdropColor(0,0,0,1) + + gui.expandGap = 25 + gui.expandOnActivate = true + gui.autoScrollTabs = true + + gui.searchers = {} + gui.AddSearcher = function (self, searchType, searchDetail, searchPos) + lib.AddSearcher(self, searchType, searchDetail, searchPos) + end + + -- Only set scale if the gui isn't attached to the AH frame + gui.RealSetScale = gui.SetScale + function gui:SetScale(scale) + private.scale = scale + if private.isAttached then return end + gui:RealSetScale(scale) + end + + -- hook ActivateTab to notify callback + gui.RealActivateTab = gui.ActivateTab + function gui:ActivateTab(...) + gui:RealActivateTab(...) + local newtab = gui.config.selectedTab + if newtab ~= gui.LastActiveTab then + gui.LastActiveTab = newtab + lib.NotifyCallbacks("selecttab", newtab) + end + gui.Search.updateDisplay() + end + + private.gui = gui + + -- common functions and scripthandlers, used by various buttons and frames + local function showTooltipText(button) + if lib.GetSetting("tooltiphelp.show") then + GameTooltip:SetOwner(button, "ANCHOR_BOTTOMRIGHT") + GameTooltip:SetText(button.TooltipText) + end + end + local function hideTooltip() + GameTooltip:Hide() + end + + gui.frame = CreateFrame("Frame", nil, gui) + gui.frame:SetPoint("TOP", gui, "TOP", 0, -115) + gui.frame:SetPoint("BOTTOMRIGHT", gui.Done, "TOPRIGHT", 0,25) + gui.frame:SetPoint("LEFT", gui:GetButton(1), "RIGHT", 5,0) + gui.frame:SetBackdrop({ + bgFile = "Interface/Tooltips/ChatBubble-Background", + edgeFile = "Interface/Tooltips/ChatBubble-BackDrop", + tile = true, tileSize = 32, edgeSize = 32, + insets = { left = 32, right = 32, top = 32, bottom = 32 } + }) + gui.frame:SetBackdropColor(0, 0, 0, 1) + + gui.saves = CreateFrame("Frame", nil, gui) + gui.saves:SetPoint("TOPRIGHT", gui, "TOPRIGHT", -5,-7) + gui.saves:SetPoint("LEFT", gui:GetButton(1), "RIGHT", 10,0) + gui.saves:SetHeight(28) +-- gui.saves:SetBackdrop({ +-- bgFile = "Interface/Tooltips/ChatBubble-Background", +-- edgeFile = "Interface/Tooltips/ChatBubble-BackDrop", +-- tile = true, tileSize = 32, edgeSize = 18, +-- insets = { left = 20, right = 20, top = 20, bottom = 20 } +-- }) +-- gui.saves:SetBackdropColor(0, 0, 0, 1) + + gui.saves.title = gui.saves:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + gui.saves.title:SetPoint("LEFT", gui.saves, "LEFT", 10,0) + gui.saves.title:SetText("Saved searches:") + + gui.saves.name = CreateFrame("EditBox", "SearchUiSaveName", gui.saves, "InputBoxTemplate") + gui.saves.name:SetPoint("LEFT", gui.saves.title, "RIGHT", 10,0) + gui.saves.name:SetAutoFocus(false) + gui.saves.name:SetWidth(200) + gui.saves.name:SetHeight(18) + gui.saves.name:SetScript("OnTextChanged", function() lib.UpdateSave() end) + if AucAdvancedData.UtilSearchUiData then + local curSearch = AucAdvancedData.UtilSearchUiData.Selected or "" + if AucAdvancedData.UtilSearchUiData.SavedSearches[curSearch] then + gui.saves.name:SetText(curSearch) + else + gui.saves.name:SetText("") + end + lib.LoadCurrent() + end + + local SelectBox = LibStub:GetLibrary("SelectBox") + + gui.saves.select = SelectBox:Create("SearchUiSaveSelect", gui.saves, 220, function(pos, key, value) + gui.saves.name:SetText(value) + end, function () + local saves = AucAdvancedData.UtilSearchUiData.SavedSearches + local items = {} + if (saves) then + for name, sdata in keyPairs(saves) do + tinsert(items, name) + end + end + return items + end, "") + gui.saves.select:SetParent(gui.saves) + gui.saves.select:SetScale(0.999) + gui.saves.select:SetScale(1.0) + gui.saves.select:SetPoint("RIGHT", gui.saves.name, "RIGHT", 38,-4) + gui.saves.select:SetInputHidden(true) + + gui.saves.load = CreateFrame("Button", nil, gui.saves, "OptionsButtonTemplate") + gui.saves.load:SetPoint("LEFT", gui.saves.name, "RIGHT", 25, 0) + gui.saves.load:SetWidth(70) + gui.saves.load:SetHeight(20) + gui.saves.load:SetText("Load") + gui.saves.load:SetScript("OnClick", function() lib.LoadSearch() end) + + gui.saves.save = CreateFrame("Button", nil, gui.saves, "OptionsButtonTemplate") + gui.saves.save:SetPoint("LEFT", gui.saves.load, "RIGHT", 5, 0) + gui.saves.save:SetWidth(70) + gui.saves.save:SetHeight(20) + gui.saves.save:SetText("Save") + gui.saves.save:SetScript("OnClick", function() lib.SaveSearch() end) + + gui.saves.delete = CreateFrame("Button", nil, gui.saves, "OptionsButtonTemplate") + gui.saves.delete:SetPoint("LEFT", gui.saves.save, "RIGHT", 5, 0) + gui.saves.delete:SetWidth(70) + gui.saves.delete:SetHeight(20) + gui.saves.delete:SetText("Delete") + gui.saves.delete:SetScript("OnClick", function() lib.DeleteSearch() end) + + gui.saves.reset = CreateFrame("Button", nil, gui.saves, "OptionsButtonTemplate") + gui.saves.reset:SetPoint("LEFT", gui.saves.delete, "RIGHT", 10, 0) + gui.saves.reset:SetWidth(70) + gui.saves.reset:SetHeight(20) + gui.saves.reset:SetText("Reset") + gui.saves.reset:SetScript("OnClick", function() + if IsShiftKeyDown() and IsControlKeyDown() and IsAltKeyDown() then + lib.ResetSearch() + print("All searchUI settings have been reset.") + else + print("This resets all searchUI settings, you must hold CTRL + SHIFT + ALT when clicking this button") + end + end) + + function lib.UpdateControls() + if gui.sheet.selected then + gui.frame.ignore:Enable() + gui.frame.ignoreperm:Enable() + gui.frame.notnow:Enable() + gui.frame.snatch:Enable() + else + gui.frame.ignore:Disable() + gui.frame.ignoreperm:Disable() + gui.frame.notnow:Disable() + gui.frame.snatch:Disable() + end + if selected ~= gui.sheet.selected then + selected = gui.sheet.selected + local data = gui.sheet:GetSelection() + if not data then + empty(private.data) + else + private.data.link = data[1] + private.data.seller = data[8] + private.data.stack = data[4] + private.data.bid = data[6] + private.data.minbid = data[12] + private.data.curbid = data[13] + private.data.buyout = data[5] + private.data.reason = data[7] + end + if private.data.buyout and (private.data.buyout > 0) then + gui.frame.buyout:Enable() + gui.frame.buyoutbox:SetText(AucAdvanced.Coins(private.data.buyout, true)) + else + gui.frame.buyout:Disable() + gui.frame.buyoutbox:SetText(AucAdvanced.Coins(0, true)) + end + + if private.data.bid then + MoneyInputFrame_SetCopper(gui.frame.bidbox, private.data.bid) + gui.frame.bid:Enable() + else + MoneyInputFrame_SetCopper(gui.frame.bidbox, 0) + gui.frame.bid:Disable() + end + elseif private.data.curbid then--bid price was changed, so make sure that it's allowable + if MoneyInputFrame_GetCopper(gui.frame.bidbox) < ceil(private.data.curbid*1.05) then + MoneyInputFrame_SetCopper(gui.frame.bidbox, ceil(private.data.curbid*1.05)) + end + gui.frame.bid:Enable() + end + --if bid >= buyout, it's going to be a buyout anyway, so disable bid button to indicate that + if private.data.buyout and (private.data.buyout > 0) and (MoneyInputFrame_GetCopper(gui.frame.bidbox) >= private.data.buyout) then + MoneyInputFrame_SetCopper(gui.frame.bidbox, private.data.buyout) + gui.frame.bid:Disable() + end + gui.frame.purchase.updateEnable() + end + + function lib.OnEnterSheet(button, row, index) + if gui.sheet.rows[row][index]:IsShown() then --Hide tooltip for hidden cells + local link, count + link = gui.sheet.rows[row][index]:GetText() + if link and link:find("\124Hitem:%d") then + if gui.sheet.order then + for i, label in pairs(gui.sheet.labels) do + if label:GetText() == "Stk" then --need to localize this when we localize the rest of searchUI + count = tonumber(gui.sheet.rows[row][i]:GetText()) + break + end + end + else + count = tonumber(gui.sheet.rows[row][4]:GetText() ) + end + + GameTooltip:SetOwner(button, "ANCHOR_RIGHT") + AucAdvanced.ShowItemLink(GameTooltip, link, count) + end + end + end + + function lib.OnClickSheet(button, row, index) + index = index - (index%15-1) + if IsShiftKeyDown() then --Add the item link to chat + local link = gui.sheet.rows[row][index]:GetText() + if not link then + return + end + ChatEdit_InsertLink(link) + elseif IsAltKeyDown() then --Search for the item in browse tab. + local name = gui.sheet.rows[row][index]:GetText() + if not name then + return + end + name = GetItemInfo(name) + QueryAuctionItems(name) + end + end + --records the column width changes + --store width by header name, that way if column reorginizing is added we apply size to proper column + function private.OnResizeSheet(self, column, width) + if not width then + lib.SetSetting("columnwidth."..self.labels[column]:GetText(), "default") --reset column if no width is passed. We use CTRL+rightclick to reset column + self.labels[column].button:SetWidth(lib.GetSetting("columnwidth."..self.labels[column]:GetText())) + else + lib.SetSetting("columnwidth."..self.labels[column]:GetText(), width) + end + end + + gui.sheet = ScrollSheet:Create(gui.frame, { + { "Item", "TOOLTIP", lib.GetSetting("columnwidth.Item") }, --120 + { "Pct", "INT", lib.GetSetting("columnwidth.Pct") }, --30 + { "Profit", "COIN", lib.GetSetting("columnwidth.Profit") , { DESCENDING=true } }, --85 + { "Stk", "INT", lib.GetSetting("columnwidth.Stk") }, --30 + { "Buyout", "COIN", lib.GetSetting("columnwidth.Buyout"), { DESCENDING=true } }, --85 + { "Bid", "COIN", lib.GetSetting("columnwidth.Bid"), { DESCENDING=true } }, --85 + { "Reason", "TEXT", lib.GetSetting("columnwidth.Reason") }, --85 + { "Seller", "TEXT", lib.GetSetting("columnwidth.Seller") }, --75 + { "Left", "TEXT", lib.GetSetting("columnwidth.Left") }, --40 + { "Buy/ea", "COIN", lib.GetSetting("columnwidth.Buy/ea"), { DESCENDING=true, DEFAULT=true } }, --85 + { "Bid/ea", "COIN", lib.GetSetting("columnwidth.Bid/ea"), { DESCENDING=true, DEFAULT=true } }, --85 + { "MinBid", "COIN", lib.GetSetting("columnwidth.MinBid"), { DESCENDING=true } }, --85 + { "CurBid", "COIN", lib.GetSetting("columnwidth.CurBid"), { DESCENDING=true } }, --85 + { "Min/ea", "COIN", lib.GetSetting("columnwidth.Min/ea"), { DESCENDING=true } }, --85 + { "Cur/ea", "COIN", lib.GetSetting("columnwidth.Cur/ea"), { DESCENDING=true } }, --85 + }) + gui.sheet:EnableSelect(true) + gui.sheet:EnableVerticalScrollReset(false) --tells scrollframes we do NOT want to reset position when rendering a new data table + + --If we have a saved order reapply + if lib.GetSetting("columnorder") then + --print("saved order applied") + gui.sheet:SetOrder(lib.GetSetting("columnorder") ) + end + --Apply last column sort used + if lib.GetSetting("columnsortcurSort") then + gui.sheet.curSort = lib.GetSetting("columnsortcurSort") or 1 + gui.sheet.curDir = lib.GetSetting("columnsortcurDir") or 1 + gui.sheet:PerformSort() + end + --After we have finished creating the scrollsheet and all saved settings have been applied set our event processor + function gui.sheet.Processor(callback, self, button, column, row, order, curDir, ...) + if (callback == "ColumnOrder") then + lib.SetSetting("columnorder", order) + elseif (callback == "ColumnWidthSet") then + private.OnResizeSheet(self, column, button:GetWidth() ) + elseif (callback == "ColumnWidthReset") then + private.onResize(self, column, nil) + elseif (callback == "OnEnterCell") then + lib.OnEnterSheet(button, row, column) + elseif (callback == "OnLeaveCell") then + GameTooltip:Hide() + elseif (callback == "OnClickCell") then + lib.OnClickSheet(button, row, column) + elseif (callback == "ColumnSort") then + lib.SetSetting("columnsortcurDir", curDir) + lib.SetSetting("columnsortcurSort", column) + elseif (callback == "OnMouseDownCell") then + lib.UpdateControls() + end + end + + gui.Search = CreateFrame("Button", "AucSearchUISearchButton", gui, "OptionsButtonTemplate") + gui.Search:SetPoint("BOTTOMLEFT", gui, "BOTTOMLEFT", 30, 50) + gui.Search:SetText("Search") + gui.Search:SetScript("OnClick", lib.PerformSearch) + gui.Search:SetFrameLevel(11) + gui.Search.TooltipText = "Search Snapshot using current Searcher" + gui.Search:SetScript("OnEnter", showTooltipText) + gui.Search:SetScript("OnLeave", hideTooltip) + gui.Search:Disable() + gui.Search.updateDisplay = function() + if gui.config.selectedCat == "Searchers" then + gui.Search:Enable() + else + gui.Search:Disable() + end + end + + + gui:AddCat("Welcome") + + id = gui:AddTab("About") + gui.aboutTab = id + gui:MakeScrollable(id) + gui:AddControl(id, "Subhead", 0, "About the SearchUI") + + gui:AddCat("Searchers", nil, nil, true) + gui:AddCat("Filters") + + gui:AddCat("Options") + id = gui:AddTab("General Options", "Options") + gui:MakeScrollable(id) + gui:AddControl(id, "Header", 0, "Setup general options") + gui:AddControl(id, "WideSlider", 0, 1, "processpriority", 10, 100, 10, "Search process priority: %s") + gui:AddControl(id, "Subhead", 0, "Purchase Settings") + gui:AddControl(id, "Checkbox", 0, 1, "reserve.enable", "Enable reserve amount:") + gui:AddControl(id, "MoneyFramePinned", 0, 2, "reserve", 0, 99999999, "Reserve Amount") + gui:AddTip(id, "Sets the amount that you don't want your cash-on-hand to fall below") + gui:AddControl(id, "Checkbox", 0, 1, "maxprice.enable", "Enable maximum price:") + gui:AddControl(id, "MoneyFramePinned", 0, 2, "maxprice", 1, 99999999, "Maximum Price") + gui:AddTip(id, "Sets the amount that you don't want to spend more than") + + id = gui:AddTab("Global Settings", "Settings") + gui:MakeScrollable(id) + gui:AddControl(id, "Header", 0, "Setup global options") + + gui:AddControl(id, "Subhead", 0, "Tooltip") + gui:AddControl(id, "Checkbox", 0, 1, "tooltiphelp.show", "Show tooltip help over buttons") + gui:AddControl(id, "Checkbox", 0, 1, "debug.show", "Show debug line in tooltip for auctions") + for name, searcher in pairs(lib.Searchers) do + if searcher and searcher.Search then + gui:AddControl(id, "Checkbox", 0, 2, "debug."..name, "Show debug for "..name) + gui:AddTip(id, "Show a debug line in the tooltip over auctions for searcher: "..name) + end + end + + gui:AddControl(id, "Subhead", 0, "Integration") + gui:AddControl(id, "Checkbox", 0, 1, "global.createtab", "Create tab in auction house (requires restart)") + + --gui:SetScript("OnKeyDown", lib.UpdateControls) + + gui.frame.purchase = CreateFrame("Button", nil, gui.frame, "OptionsButtonTemplate") + gui.frame.purchase:SetPoint("BOTTOMLEFT", gui, "BOTTOMLEFT", 170, 35) + gui.frame.purchase:SetText("Purchase") + gui.frame.purchase:SetScript("OnClick", private.purchase) + gui.frame.purchase:Disable() + gui.frame.purchase.TooltipText = "Bid/BuyOut selected auction\nbased on 'reason' column. \nHold CTRL+ALT+SHIFT to purchase all items." + gui.frame.purchase:SetScript("OnEnter", showTooltipText) + gui.frame.purchase:SetScript("OnLeave", hideTooltip) + gui.frame.purchase.toggleAll = false + gui.frame.purchase.updateEnable = function() + if gui.frame.purchase.toggleAll then + if gui.sheet.sort[1] then + gui.frame.purchase:Enable() + else + gui.frame.purchase:Disable() + end + else + if (gui.frame.bid:IsEnabled()==1) or (gui.frame.buyout:IsEnabled()==1) then + gui.frame.purchase:Enable() + else + gui.frame.purchase:Disable() + end + end + end + gui.frame.purchase.updateDisplay = function() + local all = IsShiftKeyDown() and IsControlKeyDown() and IsAltKeyDown() + if all ~= gui.frame.purchase.toggleAll then + gui.frame.purchase.toggleAll = all + if all then + gui.frame.purchase:SetText("Purchase All") + gui.frame.purchase:SetScript("OnClick", private.purchaseall) + else + gui.frame.purchase:SetText("Purchase") + gui.frame.purchase:SetScript("OnClick", private.purchase) + end + end + gui.frame.purchase.updateEnable() + end + Stubby.RegisterEventHook("MODIFIER_STATE_CHANGED", "Auc-Util-SearchUI", gui.frame.purchase.updateDisplay) + + + gui.frame.notnow = CreateFrame("Button", nil, gui.frame, "OptionsButtonTemplate") + gui.frame.notnow:SetPoint("BOTTOMLEFT", gui, "BOTTOMLEFT", 260, 35) + gui.frame.notnow:SetText("Not Now") + gui.frame.notnow:SetScript("OnClick", private.ignoretemp) + gui.frame.notnow:Disable() + gui.frame.notnow.TooltipText = "Ignore selected auction for session" + gui.frame.notnow:SetScript("OnEnter", showTooltipText) + gui.frame.notnow:SetScript("OnLeave", hideTooltip) + + gui.frame.ignore = CreateFrame("Button", nil, gui.frame, "OptionsButtonTemplate") + gui.frame.ignore:SetPoint("BOTTOMLEFT", gui, "BOTTOMLEFT", 400, 35) + gui.frame.ignore:SetText("Ignore Price") + gui.frame.ignore:SetScript("OnClick", private.ignore) + gui.frame.ignore:Disable() + gui.frame.ignore.TooltipText = "Ignore selected auction at listed price" + gui.frame.ignore:SetScript("OnEnter", showTooltipText) + gui.frame.ignore:SetScript("OnLeave", hideTooltip) + + gui.frame.ignoreperm = CreateFrame("Button", nil, gui.frame, "OptionsButtonTemplate") + gui.frame.ignoreperm:SetPoint("BOTTOMLEFT", gui, "BOTTOMLEFT", 490, 35) + gui.frame.ignoreperm:SetText("Ignore") + gui.frame.ignoreperm:SetScript("OnClick", private.ignoreperm) + gui.frame.ignoreperm:Disable() + gui.frame.ignoreperm.TooltipText = "Ignore selected auction at any price" + gui.frame.ignoreperm:SetScript("OnEnter", showTooltipText) + gui.frame.ignoreperm:SetScript("OnLeave", hideTooltip) + + gui.frame.snatch = CreateFrame("Button", nil, gui.frame, "OptionsButtonTemplate") + gui.frame.snatch:SetPoint("BOTTOMLEFT", gui, "BOTTOMLEFT", 630, 35) + gui.frame.snatch:SetText("Snatch") + gui.frame.snatch:SetScript("OnClick", private.snatch) + gui.frame.snatch:Disable() + gui.frame.snatch.TooltipText = "Add selected auction to snatch list" + gui.frame.snatch:SetScript("OnEnter", showTooltipText) + gui.frame.snatch:SetScript("OnLeave", hideTooltip) + + gui.frame.clear = CreateFrame("Button", nil, gui.frame, "OptionsButtonTemplate") + gui.frame.clear:SetPoint("BOTTOMLEFT", gui, "BOTTOMLEFT", 170, 10) + gui.frame.clear:SetText("Clear") + gui.frame.clear:SetScript("OnClick", private.removeall) + gui.frame.clear:Enable() + gui.frame.clear.TooltipText = "Clear results list" + gui.frame.clear:SetScript("OnEnter", showTooltipText) + gui.frame.clear:SetScript("OnLeave", hideTooltip) + + gui.frame.cancel = CreateFrame("Button", "AucAdvSearchUICancelButton", gui.frame, "OptionsButtonTemplate") + gui.frame.cancel:SetPoint("BOTTOMLEFT", gui, "BOTTOMLEFT", 30, 30) + gui.frame.cancel:SetWidth(22) + gui.frame.cancel:SetHeight(18) + gui.frame.cancel:Disable() + gui.frame.cancel:SetScript("OnClick", function() + AucAdvanced.Buy.CancelBuyQueue(true) + gui.frame.cancel.value = 0 + end) + gui.frame.cancel.value = 0 + private.UpdateBuyQueue = function () + local queuelen, queuecost, prompt, promptcost = AucAdvanced.Buy.GetQueueStatus() + if prompt then + queuelen = queuelen + 1 + queuecost = queuecost + promptcost + end + if queuelen > 0 then + gui.frame.cancel.label:SetText(tostring(queuelen)..": "..AucAdvanced.Coins(queuecost, true)) + gui.frame.cancel.value = queuecost + gui.frame.cancel:Enable() + gui.frame.cancel.tex:SetVertexColor(1.0, 0.9, 0.1) + else + gui.frame.cancel.label:SetText("") + gui.frame.cancel.value = 0 + gui.frame.cancel:Disable() + gui.frame.cancel.tex:SetVertexColor(0.3, 0.3, 0.3) + end + end + gui.frame.cancel.tex = gui.frame.cancel:CreateTexture(nil, "OVERLAY") + gui.frame.cancel.tex:SetPoint("TOPLEFT", gui.frame.cancel, "TOPLEFT", 4, -2) + gui.frame.cancel.tex:SetPoint("BOTTOMRIGHT", gui.frame.cancel, "BOTTOMRIGHT", -4, 2) + gui.frame.cancel.tex:SetTexture("Interface\\Addons\\Auc-Advanced\\Textures\\NavButtons") + gui.frame.cancel.tex:SetTexCoord(0.25, 0.5, 0, 1) + gui.frame.cancel.tex:SetVertexColor(0.3, 0.3, 0.3) + gui.frame.cancel.label = gui.frame.cancel:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + gui.frame.cancel.label:SetPoint("LEFT", gui.frame.cancel, "RIGHT", 5, 0) + gui.frame.cancel.label:SetTextColor(1, 0.8, 0) + gui.frame.cancel.label:SetText("") + gui.frame.cancel.label:SetJustifyH("LEFT") + + gui.frame.buyout = CreateFrame("Button", nil, gui.frame, "OptionsButtonTemplate") + gui.frame.buyout:SetPoint("BOTTOMLEFT", gui, "BOTTOMLEFT", 650, 10) + gui.frame.buyout:SetText("Buyout") + gui.frame.buyout:SetScript("OnClick", private.buyauction) + gui.frame.buyout:Disable() + gui.frame.buyout.TooltipText = "Buyout selected auction" + gui.frame.buyout:SetScript("OnEnter", showTooltipText) + gui.frame.buyout:SetScript("OnLeave", hideTooltip) + + gui.frame.buyoutbox = gui.frame.buyout:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + gui.frame.buyoutbox:SetPoint("BOTTOMRIGHT", gui.frame.buyout, "BOTTOMLEFT", -4, 4) + gui.frame.buyoutbox:SetWidth(100) + + gui.frame.bid = CreateFrame("Button", nil, gui.frame, "OptionsButtonTemplate") + gui.frame.bid:SetPoint("BOTTOMRIGHT", gui.frame.buyoutbox, "BOTTOMLEFT", -10, -4) + gui.frame.bid:SetText("Bid") + gui.frame.bid:SetScript("OnClick", private.bidauction) + gui.frame.bid:Disable() + gui.frame.bid.TooltipText = "Bid on selected auction using custom price" + gui.frame.bid:SetScript("OnEnter", showTooltipText) + gui.frame.bid:SetScript("OnLeave", hideTooltip) + + gui.frame.bidbox = CreateFrame("Frame", "AucAdvSearchUIBidBox", gui.frame, "MoneyInputFrameTemplate") + gui.frame.bidbox:SetPoint("BOTTOMRIGHT", gui.frame.bid, "BOTTOMLEFT", -4, 4) + MoneyInputFrame_SetOnValueChangedFunc(gui.frame.bidbox, lib.UpdateControls) + + gui.frame.progressbar = CreateFrame("STATUSBAR", nil, gui.frame, "TextStatusBar") + gui.frame.progressbar:SetWidth(400) + gui.frame.progressbar:SetHeight(30) + gui.frame.progressbar:SetPoint("BOTTOM", gui.frame, "BOTTOM", 0, 100) + gui.frame.progressbar:SetBackdrop({ + bgFile="Interface\\Tooltips\\UI-Tooltip-Background", + edgeFile="Interface\\Tooltips\\UI-Tooltip-Border", + tile=1, tileSize=10, edgeSize=10, + insets={left=3, right=3, top=3, bottom=3} + }) + gui.frame.progressbar:SetStatusBarTexture("Interface\\TargetingFrame\\UI-StatusBar") + gui.frame.progressbar:SetStatusBarColor(0.6, 0, 0, 0.4) + gui.frame.progressbar:SetMinMaxValues(0, 1000) + gui.frame.progressbar:SetValue(0) + gui.frame.progressbar:SetFrameLevel(10) + gui.frame.progressbar:Hide() + + gui.frame.progressbar.text = gui.frame.progressbar:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + gui.frame.progressbar.text:SetPoint("CENTER", gui.frame.progressbar, "CENTER") + gui.frame.progressbar.text:SetText("AucAdv SearchUI: Searching") + gui.frame.progressbar.text:SetTextColor(1,1,1) + + gui.frame.progressbar.cancel = CreateFrame("Button", nil, gui.frame.progressbar, "OptionsButtonTemplate") + gui.frame.progressbar.cancel:SetPoint("BOTTOMRIGHT", gui.frame.progressbar, "BOTTOMRIGHT", -5, 5) + gui.frame.progressbar.cancel:SetPoint("TOPLEFT", gui.frame.progressbar, "TOPRIGHT", -25, -5) + gui.frame.progressbar.cancel:SetText("X") + gui.frame.progressbar.cancel:SetScript("OnClick", private.cancelSearch) + + -- Alert our searchers? + for name, searcher in pairs(lib.Searchers) do + if searcher.MakeGuiConfig then + searcher:MakeGuiConfig(gui) + end + end + --Alert our Filters + for name, filter in pairs(lib.Filters) do + if filter.MakeGuiConfig then + filter:MakeGuiConfig(gui) + end + end + + lib.NotifyCallbacks('guiconfig', gui) + + -- Add the welcome text now + local b = " |TInterface\\QuestFrame\\UI-Quest-BulletPoint.blp:13|t " + local w = "" + w=w.."The Auctioneer Search UI is here to assist you in finding the Auctions that are of special interest to you.\n" + w=w.."\n" + w=w.."It has 2 modes of operation:\n" + w=w..b.."Offline - Searches are applied against the current data\n" + w=w..b.."Realtime - Searches are applied against the realtime scans of the AH\n" + w=w.."\n" + w=w.."There are also 2 types of searching modules that exist in the Search UI:\n" + w=w..b.."Searchers - Searches for a specific set of criteria, only 1 searcher is active at once\n" + w=w..b.."Filters - All filters always apply their criteria, excluding non-matching items from the search.\n" + w=w.."\n" + w=w.."In order to begin using the Search UI, you should first setup your filters to exclude the items you don't wish to find, then select a searcher to perform a search.\n" + w=w.."\n" + w=w.."Here's a quick list of the searchers and filters that you may wish to investigate:\n" + for pos, sdata in keyPairs(gui.searchers) do + for searchType, searchDetail in keyPairs(sdata) do + w=w..b.."|cffffce00"..searchType.."|r - "..searchDetail.."\n" + end + end + + gui:AddControl(gui.aboutTab, "Note", 0, 1, nil, nil, w) + gui:ActivateTab(gui.aboutTab) +end + +if LibStub then + --Need to figure out if we're embedded first + local embedded = false + for _, module in ipairs(AucAdvanced.EmbeddedModules) do + if module == "Auc-Util-SearchUI" then + embedded = true + end + end + local sideIcon + if embedded then + sideIcon = "Interface\\AddOns\\Auc-Advanced\\Modules\\Auc-Util-SearchUI\\Textures\\SearchUIIcon" + else + sideIcon = "Interface\\AddOns\\Auc-Util-SearchUI\\Textures\\SearchUIIcon" + end + + local LibDataBroker = LibStub:GetLibrary("LibDataBroker-1.1", true) + if LibDataBroker then + private.LDBButton = LibDataBroker:NewDataObject("Auc-Util-SearchUI", { + type = "launcher", + icon = sideIcon, + OnClick = function(self, button) lib.Toggle(self, button) end, + }) + + function private.LDBButton:OnTooltipShow() + self:AddLine("Auction SearchUI", 1,1,0.5, 1) + self:AddLine("Allows you to perform searches on the Auctioneer auction cache snapshot, even when away from the Auction House", 1,1,0.5, 1) + self:AddLine("|cff1fb3ff".."Click|r to open the Search UI.", 1,1,0.5, 1) + end + + function private.LDBButton:OnEnter() + GameTooltip:SetOwner(self, "ANCHOR_NONE") + GameTooltip:SetPoint("TOPLEFT", self, "BOTTOMLEFT") + GameTooltip:ClearLines() + private.LDBButton.OnTooltipShow(GameTooltip) + GameTooltip:Show() + end + + function private.LDBButton:OnLeave() + GameTooltip:Hide() + end + end +end + +function private.FindSearcher(item) + if not gui.config.selectedTab then + return + end + for name, searcher in pairs(lib.Searchers) do + if searcher and searcher.tabname and searcher.tabname == gui.config.selectedTab and searcher.Search then + return searcher, name + end + end +end + +function private.cancelSearch() + private.SearchCancel = true +end + +--lib.SearchItem(searcherName, item, nodupes) +--purpose: handles sending the item to the specified searcher, and if necessary, adds it to the SearchUI results +--nodupes is boolean flag. If true, no duplicate checking is done. This flag is true for searching from the cache, but false for realtime. +--skipresults is boolean flag, If true, nothing gets added to the results list +--returns true, value, profit when successful +--returns false, reason when not +function lib.SearchItem(searcherName, item, nodupes, skipresults) + if not searcherName or not item or #item == 0 then + return + end + if item[Const.SELLER] == UnitName("player") then + return false, "Blocked: Can't buy own auction" + end + local searcher = lib.Searchers[searcherName] + if not searcher then + return + end + local debugstring + + --next, pass the item through the filters + local isfiltered = false + for filtername, filter in pairs(lib.Filters) do + if filter.Filter then + local dofilter, filterreturn = filter.Filter(item, searcherName) + if dofilter then + return false, "Filter:"..filtername..": "..tostring(filterreturn) + end + end + end + + --buyorbid must be either "bid", "buy", true, false, or nil + --if string is returned for buyorbid, value must be number or nil (in which case value will be Marketprice) + local buyorbid, value, pct, reason + buyorbid, value, pct, reason = searcher.Search(item) + if buyorbid then + --give the filters a second chance to filter out, based on bid/buy differences + for filtername, filter in pairs(lib.Filters) do + if filter.PostFilter then + local dofilter, filterreturn = filter.PostFilter(item, searcherName, buyorbid) + if dofilter then + return false, "Filtered by "..filtername..": "..filterreturn + end + end + end + + local cost + if type(buyorbid) == "string" then + item["reason"] = searcher.tabname..":"..buyorbid + if reason then + item["reason"] = item["reason"]..":"..reason + end + if buyorbid == "bid" then + --don't show result if we're already the highest bidder + if item[Const.AMHIGH] then + return false, "Bid blocked: Already high bidder" + end + cost = item[Const.PRICE] + else + cost = item[Const.BUYOUT] + end + else --the searcher only returned that it matches the criteria, so assume buyout if possible. + item["reason"] = searcher.tabname + if item[Const.BUYOUT] and item[Const.BUYOUT] > 0 then + cost = item[Const.BUYOUT] + elseif item[Const.PRICE] and item[Const.PRICE] > 0 then + cost = item[Const.PRICE] + if item[Const.AMHIGH] then + return false, "Bid blocked: Already high bidder" + end + end + end + if not value then + local market = AucAdvanced.API.GetMarketValue(item[Const.LINK]) + if market then -- needs a nil check + value = item[Const.COUNT]*market + end + end + if not value then + value = 0 + end + if not cost then + return false, "Bid blocked: No valid price possible" + end + item["profit"] = value - cost + local enablemax = lib.GetSetting("maxprice.enable") + local maxprice = lib.GetSetting("maxprice") or 10000000 + local enableres = lib.GetSetting("reserve.enable") + local reserve = lib.GetSetting("reserve") or 1 + local bidqueue = gui.frame.cancel.value or 0 + + local balance = GetMoney() + balance = balance - bidqueue --account for money we've already "spent" + + if (cost <= maxprice or not enablemax) and ((balance-cost) > reserve or not enableres) then + --Check to see whether the item already exists in the results table + local isdupe = false + if not nodupes then + + if not private.sheetData then + private.sheetData = {} + private.sheetStyle = {} + end + for j,k in pairs(private.sheetData) do + if (k[1] == item[Const.LINK]) and (k[7] == item["reason"]) then + isdupe = true + end + end + + end + if nodupes or (not isdupe) then + local level, _, r, g, b + pct = tonumber(pct) --make sure its not a string + if not pct and AucAdvanced.Modules.Util.PriceLevel then + local valueper + if value and value > 0 then + valueper = value/item[Const.COUNT] + end + if buyorbid == "bid" then + level, _, r, g, b = AucAdvanced.Modules.Util.PriceLevel.CalcLevel(item[Const.LINK], item[Const.COUNT], item[Const.PRICE], item[Const.PRICE], valueper) + else + level, _, r, g, b = AucAdvanced.Modules.Util.PriceLevel.CalcLevel(item[Const.LINK], item[Const.COUNT], item[Const.PRICE], item[Const.BUYOUT], valueper) + end + if not r or not b or not g then + --print("price level failure in searchUI") + --print(r,g,b, item[Const.LINK], item[Const.COUNT], item[Const.PRICE], item[Const.BUYOUT], valueper) + r,g,b = 1,1,1 + end + local total = #private.sheetData+1 + private.sheetStyle[total] = {} + private.sheetStyle[total][2] = {["textColor"] = {r, g, b}} + private.sheetStyle[total][1] = {["rowColor"] = {r, g, b, 0, 0.2, "Horizontal"}} --allow row coloring, needs config options + level = level or 0 + pct = floor(level) + end + item["pct"] = pct + item["cost"] = cost + local count = item[Const.COUNT] or 1 + local min = item[Const.MINBID] or 0 + local cur = item[Const.CURBID] or 0 + local buy = item[Const.BUYOUT] or 0 + local price = item[Const.PRICE] or 0 + if not skipresults then + tinsert(private.sheetData, { + item[Const.LINK], + item["pct"], + item["profit"], + count, + buy, + price, + item["reason"], + item[Const.SELLER], + private.tleft[item[Const.TLEFT]], + buy/count, + price/count, + min, + cur, + min/count, + cur/count + }) + end + return true, item["profit"], value + end + elseif cost > maxprice and enablemax then + return false, "Price higher than maxprice" + else + return false, "Balance lower than reserve" + end + end + return false, value +end + +local PerformSearch = function() + if gui.tabs.active then + gui:ContractFrame(gui.tabs.active) + end + gui:ClearFocus() + --Perform the search. We're not using API.QueryImage() because we need it to be a coroutine + local image = AucAdvanced.Scan.GetImageCopy() + local imagesize = #image + local speed = lib.GetSetting("processpriority") or 50 + speed = (speed / 100)^2.5 + local processingTime = speed * 0.1 + 0.02 + local GetTime = GetTime + local nextPause = GetTime() + processingTime + + local searcher, searcherName = private.FindSearcher() + if not searcher then + print("No valid Searches selected") + return + end + gui.frame.progressbar.text:SetText("AucAdv SearchUI: Searching |cffffcc19"..gui.config.selectedTab) + gui.frame.progressbar:Show() + + --clear the results table + private.removeall() + local repaintSheet = false + local nextRepaint = 0 -- can do it immediately + + private.isSearching = true + AucAdvanced.SendProcessorMessage("searchbegin", searcherName) + lib.NotifyCallbacks("search", "begin", searcherName) + for i, data in ipairs(image) do + if GetTime() > nextPause then + gui.frame.progressbar:SetValue((i/imagesize)*1000) + + coroutine.yield() + + nextPause = GetTime() + processingTime + if private.SearchCancel then + private.SearchCancel = nil + break + end + if repaintSheet and GetTime()>=nextRepaint then + local b=GetTime() + private.repaintSheet() + repaintSheet = false + local e=GetTime() + nextRepaint = e + ((e-b)*10) -- only let repainting consume 10% of our total CPU + end + end + if lib.SearchItem(searcher.name, data, true) then + repaintSheet = true + end + end + + private.repaintSheet() + + private.isSearching = false + empty(SettingCache) + gui.frame.progressbar:Hide() + AucAdvanced.SendProcessorMessage("searchcomplete", searcherName) + lib.NotifyCallbacks("search", "complete", searcherName) +end + +function lib.IsSearching() + return private.isSearching +end + +function lib.PerformSearch(searcher) + if (not coSearch) or (coroutine.status(coSearch) == "dead") then + coSearch = coroutine.create(PerformSearch) + local status, result = coroutine.resume(coSearch, searcher) + if not status and result then + error("Error in search coroutine: "..result.."\n\n{{{Coroutine Stack:}}}\n"..debugstack(coSearch)); + end + else + print("coroutine already running: "..coroutine.status(coSearch)) + end +end + +function private.OnUpdate(self, elapsed) + if coSearch then + if coroutine.status(coSearch) == "suspended" then + local status, result = coroutine.resume(coSearch) + if not status and result then + error("Error in search coroutine: "..result.."\n\n{{{Coroutine Stack:}}}\n"..debugstack(coSearch)); + end + elseif coroutine.status(coSearch) == "dead" then + coSearch = nil + end + end + if flagResourcesUpdateRequired then + -- Update Faction resources following Auctionhouse open or close (to handle Neutral AH) + -- Delayed until OnUpdate handler to give GetFaction time to update its own internal settings + flagResourcesUpdateRequired = false + private.UpdateFactionResources() + end + if flagScanStats then + flagScanStats = false + lib.NotifyCallbacks("postscanupdate") + end +end + +private.updater = CreateFrame("Frame", nil, UIParent) +private.updater:SetScript("OnUpdate", private.OnUpdate) + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-SearchUI/SearchMain.lua $", "$Rev: 4933 $") diff --git a/Auc-Advanced/Modules/Auc-Util-SearchUI/SearchRealTime.lua b/Auc-Advanced/Modules/Auc-Util-SearchUI/SearchRealTime.lua new file mode 100644 index 0000000..5e433ee --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-SearchUI/SearchRealTime.lua @@ -0,0 +1,416 @@ +--[[ + Auctioneer - Search UI - Realtime module + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: SearchRealTime.lua 4720 2010-04-20 18:12:29Z brykrys $ + URL: http://auctioneeraddon.com/ + + This Auctioneer module allows the user to search the current Browse tab + results in real time, without requiring scans or an up-to-date snapshot. + It also provides top- and bottom-scanning capabilities (i.e. first and + last AH pages) to find deals on items about to expire, or recently added + to the AH. + + 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 +--]] + +local lib, parent, private = AucSearchUI.NewSearcher("RealTime") +if not lib then return end +local print,decode,_,_,replicate,empty,_,_,_,debugPrint,fill = AucAdvanced.GetModuleLocals() +local get,set,default,Const = AucSearchUI.GetSearchLocals() +lib.tabname = "realtime" + +local Const = AucAdvanced.Const +--default assumption is that we're not embedded. This is checked later. +local embedded = false +local embedpath = "Interface\\AddOns\\" +private.IsScanning = false +private.count = 0 +private.searchertable = {} +private.ItemTable = {} +private.interval = 20 +private.offset = 0 +private.topScan = false +private.IsRefresh = false + +default("realtime.always", true) +default("realtime.reload.enable", true) +default("realtime.reload.interval", 20) +default("realtime.reload.topscan", false) +default("realtime.reload.manpause", 60) +default("realtime.maxprice", 10000000) +default("realtime.alert.chat", true) +default("realtime.alert.showwindow", true) +default("realtime.alert.sound", "DoorBell") +default("realtime.skipresults", false) + +local SearchUIgui +function lib:MakeGuiConfig(gui) + SearchUIgui = gui + local id = gui:AddTab(lib.tabname, "Options") + gui:MakeScrollable(id) + + gui:AddControl(id, "Header", 0, "RealTime Search Options") + + gui:AddControl(id, "Subhead", 0, "Scan Settings") + gui:AddControl(id, "Checkbox", 0, 1, "realtime.always", "Search while browsing") + gui:AddTip(id, "Enables searching for results when browsing or scanning") + gui:AddControl(id, "Checkbox", 0, 1, "realtime.reload.enable", "Enable automatic last page refreshing") + gui:AddTip(id, "Refreshes the last page, looking for new bargains. (Bottomscanning)") + gui:AddControl(id, "Slider", 0, 2, "realtime.reload.interval", 6, 60, 1, "Reload interval: %s seconds") + gui:AddControl(id, "Slider", 0, 2, "realtime.reload.manpause", 10, 120, 1, "Pause after a manual search: %s seconds") + gui:AddControl(id, "Checkbox", 0, 2, "realtime.reload.topscan", "Refresh first page as well") + gui:AddTip(id, "Refreshes the first page, looking for bids about to expire") + + gui:AddControl(id, "Subhead", 0, "Alert Settings") + gui:AddControl(id, "Checkbox", 0, 1, "realtime.alert.chat", "Show alert in chat window") + gui:AddControl(id, "Checkbox", 0, 1, "realtime.alert.showwindow", "Show SearchUI window") + gui:AddTip(id, "When a bargain is found, opens the SearchUI window to facilitate buying the bargain") + gui:AddControl(id, "Selectbox", 0, 1, { + {"none", "None (do not play a sound)"}, + {"LEVELUP", "Level Up"}, + {"AuctionWindowOpen", "AuctionHouse Open"}, + {"AuctionWindowClose", "AuctionHouse Close"}, + {"RaidWarning", "Raid Warning"}, + {"DoorBell", "DoorBell (BottomScan-style)"}, + }, "realtime.alert.sound", "Pick the sound to play") + gui:AddTip(id, "The selected sound will play whenever a bargain is found") + gui:AddControl(id, "Subhead", 0, "Power-user setting: One-Click Buying") + gui:AddControl(id, "Checkbox", 0, 1, "realtime.skipresults", "Skip results and go straight to purchase confirmation !Power-user setting!") + gui:AddTip(id, "One-Click Buying: RTS will queue the purchase instead of listing the item in SearchUI") + gui:AddControl(id, "Subhead", 0, "Searchers to use") + for name, searcher in pairs(AucSearchUI.Searchers) do + if searcher and searcher.Search then + gui:AddControl(id, "Checkbox", 0, 1, "realtime.use."..name, name) + gui:AddTip(id, "Include "..name.." when searching realtime") + end + end +end + +--lib.RefreshPage() +--role: refreshes the page based on settings, and updates private.lastPage, private.interval, and private.manualSearchPause +function lib.RefreshPage() + private.interval = get("realtime.reload.interval") + + --Check to see if the AH is open for business + if not (AuctionFrame and AuctionFrame:IsVisible()) then + private.interval = 1 --Try again in one second + return + end + + --Check to see if we can send a query + if not (CanSendAuctionQuery()) then + private.interval = 1 --try again in one second + return + end + + --Check to see if AucAdv is already scanning + if AucAdvanced.Scan.IsScanning() or AucAdvanced.Scan.IsPaused() then + private.timer = 0 + private.interval = get("realtime.reload.manpause") + return + end + + --Get the current number of auctions and pages + local pageCount, totalCount = GetNumAuctionItems("list") + local totalPages = math.floor((totalCount-1)/NUM_AUCTION_ITEMS_PER_PAGE) + if (totalPages < 0) then + totalPages = 0 + end + + --set the AH page count to a signal value, if this is our first time + if (not private.pageCount) then + private.pageCount = -1 + end + + --Decide whether we are just starting to use the Realtime queries (as opposed to piggybacking), which means we are going to do a few quick scans to get to the last page + if (totalPages ~= private.pageCount) then + private.pageCount = totalPages + private.interval = 3 --cut short the delay, we want to get to the last page quickly + end + + --every 5 pages, go back one just to doublecheck that nothing got by us + private.offset = (private.offset + 1) % 5 + local offset = 0 + if private.offset == 0 then + offset = 1 + end + + local page = private.pageCount - offset or 0 + if get("realtime.reload.topscan") then + private.topScan = not private.topScan --flip the variable, so we alternate first and last pages + else + private.topScan = false --make sure we don't topScan if we don't want to + end + if private.topScan then + page = 0 + end + AuctionFrameBrowse.page = page + SortAuctionClearSort("list") + private.IsRefresh = true + QueryAuctionItems("", "", "", nil, nil, nil, page, nil, nil) +end + +--private.OnUpdate() +--checks whether it's time to refresh the page +function private.OnUpdate(me, elapsed) + if (not private.lastTry) then + private.lastTry = 0 + end + if not private.interval then + private.interval = 6 + end + if not private.timer then + private.timer = 0 + else + private.timer = private.timer + elapsed + + --Check whether enough time has elapsed to do anything + if private.timer < private.interval then + return + end + private.timer = private.timer - private.interval + private.lastTry = private.lastTry - private.interval + end + + --if we've gotten to this point, it's time to refresh the page + if (private.IsScanning) and (get("realtime.reload.enable")) then + lib.RefreshPage() + end +end + +--lib.FinishedPage() +--called by AucAdv via SearchUI main when a page is done +--role: starts the page scan +function lib.FinishedPage() + --if we're not scanning, we don't need to do anything + --if we don't have searching while browsing on, then don't do anything if we're not actively refreshing + local always = get("realtime.always") + if not private.IsRefresh then + private.timer = 0 + private.interval = get("realtime.reload.manpause") + end + if (not private.IsScanning) + or ((not always) and (not private.IsRefresh)) + or ((not always) and (AucAdvanced.Scan.IsScanning())) then + private.timer = 0 + private.interval = get("realtime.reload.manpause") + private.IsRefresh = false + return + else + private.IsRefresh = false + end + --scan the current page + lib.ScanPage() +end + +--[[ + lib.ScanPage() + Called: from lib.FinishedPage, when AA is done with a page + Function: Scans current AH page for bargains + Note: will return if current page has >50 auctions on it +]] +function lib.ScanPage() + if not private.IsScanning then return end + private.IsRefresh = false + local batch, totalCount + batch, totalCount = GetNumAuctionItems("list") + if batch > 50 then + -- we don't want to freeze the computer by trying to process a getall, so return + return + end + + --this is a new page, so no alert sound has been played for it yet + private.playedsound = false + --store the current pagecount + private.pageCount = math.floor((totalCount-1)/NUM_AUCTION_ITEMS_PER_PAGE) + + --Put all the searchers that are activated into our local table, so that the get()s are only called every page, not every auction + for name, searcher in pairs(AucSearchUI.Searchers) do + if get("realtime.use."..name) then + table.insert(private.searchertable, searcher) + end + end + for i = 1, batch do + local link = GetAuctionItemLink("list", i) + if link then + local name, _, count, quality, canUse, level, minBid, minInc, buyout, curBid, isHigh, owner = GetAuctionItemInfo("list", i) + local _, _, quality, iLevel, _, iType, iSubType, stack, iEquip = GetItemInfo(link) + iEquip = Const.EquipEncode[iEquip] + local timeleft = GetAuctionItemTimeLeft("list", i) + local _, id, suffix, factor, enchant, seed = AucAdvanced.DecodeLink(link) + owner = owner or "" + count = count or 1 + + local price + if curBid > 0 then + price = curBid + minInc + if buyout > 0 and price > buyout then + price = buyout + end + elseif minBid > 0 then + price = minBid + else + price = 1 + end + + -- put the data into a table laid out the same way as the AAdv Scandata, as that's what the searchers need + private.ItemTable[Const.LINK] = link + private.ItemTable[Const.ILEVEL] = iLevel + private.ItemTable[Const.ITYPE] = iType + private.ItemTable[Const.ISUB] = iSubType + private.ItemTable[Const.IEQUIP] = iEquip + private.ItemTable[Const.PRICE] = price + private.ItemTable[Const.TLEFT] = timeleft + private.ItemTable[Const.NAME] = name + private.ItemTable[Const.COUNT] = count + private.ItemTable[Const.QUALITY] = quality + private.ItemTable[Const.CANUSE] = canUse + private.ItemTable[Const.ULEVEL] = level + private.ItemTable[Const.MINBID] = minBid + private.ItemTable[Const.MININC] = minInc + private.ItemTable[Const.BUYOUT] = buyout + private.ItemTable[Const.CURBID] = curBid + private.ItemTable[Const.AMHIGH] = isHigh + private.ItemTable[Const.SELLER] = owner + private.ItemTable[Const.ITEMID] = id + private.ItemTable[Const.SUFFIX] = suffix + private.ItemTable[Const.FACTOR] = factor + private.ItemTable[Const.ENCHANT] = enchant + private.ItemTable[Const.SEED] = seed + + local skipresults = get("realtime.skipresults") + for i, searcher in pairs(private.searchertable) do + if AucSearchUI.SearchItem(searcher.name, private.ItemTable, false, skipresults) then + private.alert(private.ItemTable[Const.LINK], private.ItemTable["cost"], private.ItemTable["reason"]) + if skipresults then + AucAdvanced.Buy.QueueBuy(private.ItemTable[Const.LINK], + private.ItemTable[Const.SELLER], + private.ItemTable[Const.COUNT], + private.ItemTable[Const.MINBID], + private.ItemTable[Const.BUYOUT], + private.ItemTable["cost"], + AucSearchUI.Private.cropreason(private.ItemTable["reason"]), + true -- do not trigger a search if CoreBuy is unable to find this auction + ) + else + AucSearchUI.Private.repaintSheet() + end + end + end + end + AucSearchUI.CleanTable(private.ItemTable) + end + AucSearchUI.CleanTable(private.searchertable) +end + +--private.alert() +--alerts the user that a deal has been found, +--both by opening the searchUI panel and playing a sound +--(subject to options) +function private.alert(link, cost, reason) + if get("realtime.alert.chat") then + print("SearchUI: "..reason..": Found "..link.." for "..AucAdvanced.Coins(cost, true)) + end + if get("realtime.alert.showwindow") and (not get("realtime.skipresults")) then + AucSearchUI.Show() + if SearchUIgui then + if SearchUIgui.tabs.active then + SearchUIgui:ContractFrame(SearchUIgui.tabs.active) + end + SearchUIgui:ClearFocus() + end + end + local SoundPath = get("realtime.alert.sound") + if SoundPath and (SoundPath ~= "none") and not private.playedsound then + private.playedsound = true + if SoundPath == "DoorBell" then + SoundPath = embedpath.."Auc-Util-SearchUI\\DoorBell.mp3" + PlaySound("GAMEHIGHLIGHTFRIENDLYUNIT") + PlaySoundFile(SoundPath) + else + PlaySound(SoundPath) + end + end +end + +--[[ + private.ButtonPressed() + Called: when the control button gets pushed + function: switches on/off btmscanning +]] +function private.ButtonPressed(self, button) + if button == "LeftButton" then + if private.IsScanning then + private.IsScanning = false + private.button.control.tex:SetTexCoord(0, .5, 0, 1) + else + private.IsScanning = true + private.interval = 1 --we're starting the scan, so no need to wait + private.button.control.tex:SetTexCoord(0.5, 1, 0, 1) + end + elseif button == "RightButton" then + AucSearchUI.Toggle() + end +end + +--[[ + lib.HookAH() + Called when the AH opens for the first time + function: to create the control button on the AH, to the left of the regular ScanButtons +]] +function lib.HookAH() + private.button = CreateFrame("Frame", nil, AuctionFrameBrowse) + private.button:SetPoint("TOPRIGHT", AuctionFrameBrowse, "TOPLEFT", 310, -15) + private.button:SetWidth(26) + private.button:SetHeight(18) + + private.button.control = CreateFrame("Button", nil, private.button, "OptionsButtonTemplate") + private.button.control:SetPoint("TOPLEFT", private.button, "TOPLEFT", 0, 0) + private.button.control:SetWidth(22) + private.button.control:SetHeight(18) + private.button.control:RegisterForClicks("LeftButtonUp", "RightButtonUp") + private.button.control:SetScript("OnClick", private.ButtonPressed) + private.button.control:SetScript("OnUpdate", private.OnUpdate) + private.button.control.tex = private.button.control:CreateTexture(nil, "OVERLAY") + private.button.control.tex:SetPoint("TOPLEFT", private.button.control, "TOPLEFT", 4, -2) + private.button.control.tex:SetPoint("BOTTOMRIGHT", private.button.control, "BOTTOMRIGHT", -4, 2) + private.button.control:SetScript("OnEnter", function() + GameTooltip:SetOwner(private.button.control, "ANCHOR_TOPRIGHT") + GameTooltip:SetText("Click to start Realtime Search\nRightclick to open SearchUI") + end) + private.button.control:SetScript("OnLeave", function() + GameTooltip:Hide() + end) + + --Figure out whether we're embedded or not. If we are, adjust the path to the texture accordingly. + for _, module in ipairs(AucAdvanced.EmbeddedModules) do + if module == "Auc-Util-SearchUI" then + embedpath = "Interface\\AddOns\\Auc-Advanced\\Modules\\" + end + end + private.button.control.tex:SetTexture(embedpath.."Auc-Util-SearchUI\\Textures\\SearchButton") + private.button.control.tex:SetTexCoord(0,.5, 0, 1) + private.button.control.tex:SetVertexColor(1, 0.9, 0.1) +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-SearchUI/SearchRealTime.lua $", "$Rev: 4720 $") diff --git a/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherArbitrage.lua b/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherArbitrage.lua new file mode 100644 index 0000000..1bc20e2 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherArbitrage.lua @@ -0,0 +1,368 @@ +--[[ + Auctioneer - Search UI - Searcher Arbitrage + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: SearcherArbitrage.lua 4880 2010-09-15 20:02:11Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This is a plugin module for the SearchUI that assists in searching by refined paramaters + + 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 +--]] +-- Create a new instance of our lib with our parent +local lib, parent, private = AucSearchUI.NewSearcher("Arbitrage") +if not lib then return end +--local print,decode,_,_,replicate,empty,_,_,_,debugPrint,fill = AucAdvanced.GetModuleLocals() +local get, set, default, Const, resources = parent.GetSearchLocals() +lib.tabname = "Arbitrage" + +do -- limit scope of locals + local styles = { + "Neutral", + "Cross-Faction", + "Cross-Realm", + } + function private.getStyles() + return styles + end + + local factions = { + "Neutral", + "Alliance", + "Horde", + } + function private.getFactions() + return factions + end + + local realmlist + function private.getRealmList() + return realmlist + end + function private.createRealmList() + -- called from onload event for SearchUI + -- saved variables are loaded but some resources may not be available + private.createRealmList = nil + + realmlist = {} + + local realms = AucAdvancedData.AserArbitrageRealms + if not realms then + realms = {} + AucAdvancedData.AserArbitrageRealms = realms + end + + local playerRealm = GetRealmName() + if not realms[playerRealm] then + realms[playerRealm] = UnitName("player") + end + + for realm, _ in pairs(realms) do + -- apparently some serverKeys got into the table in the past + -- they shouldn't be there, so strip them out + -- eventually this check can be removed altogether + local len = #realm + if strsub(realm, len-7) == "Alliance" + or strsub(realm, len-6) == "Neutral" + or strsub(realm, len-4) == "Horde" + then + realms[realm] = nil + end + + -- insert all realms *including* our current realm + -- this is a workaround for problems with Selectboxes if the current saved setting is not in the list + table.insert(realmlist, realm) + end + end +end + +-- Set our defaults +default("arbitrage.profit.min", 1) +default("arbitrage.profit.pct", 50) +default("arbitrage.seen.check", false) +default("arbitrage.seen.min", 10) +default("arbitrage.adjust.brokerage", true) +default("arbitrage.adjust.deposit", true) +default("arbitrage.adjust.deplength", 48) +default("arbitrage.adjust.listings", 3) +default("arbitrage.allow.bid", true) +default("arbitrage.allow.buy", true) +default("arbitrage.maxprice", 10000000) +default("arbitrage.maxprice.enable", false) +default("arbitrage.model", "market") +default("arbitrage.search.crossrealmfaction", "Alliance") +default("arbitrage.search.style", "Cross-Faction") + +function private.doValidation() + if not resources.isValidPriceModel(get("arbitrage.model")) then + message("Arbitrage Searcher Warning!\nCurrent price model setting ("..get("arbitrage.model")..") is not valid. Select a new price model") + else + private.doValidation = nil + end +end + +function lib.Processor(event, subevent, ...) + if event == "search" and subevent == "complete" and private.factionUpdateRequired then + -- something changed during a search - complete the update now the search has finished + private.factionUpdateRequired = nil + private.SetCurrentFaction() + elseif event == "selecttab" then + if subevent == lib.tabname and private.doValidation then + private.doValidation() + end + elseif event == "config" then + -- update private variables, but only if a relevant setting may have changed + if subevent == "changed" then + local setting = ... + if setting and setting:match("^arbitrage") then + private.SetCurrentFaction() + end + elseif subevent == "loaded" or subevent == "reset" or subevent == "deleted" then + private.SetCurrentFaction() + end + elseif event == "resources" and subevent == "faction" then + private.SetCurrentFaction() + elseif event == "onload" and subevent == "auc-util-searchui" then + if private.createRealmList then + private.createRealmList() + end + end +end + +lib.Processors = {} +function lib.Processors.search(event, subevent, ...) + if subevent == "complete" and private.factionUpdateRequired then + -- something changed during a search - complete the update now the search has finished + private.factionUpdateRequired = nil + private.SetCurrentFaction() + end +end + +function lib.Processors.selecttab(event, subevent, ...) + if subevent == lib.tabname and private.doValidation then + private.doValidation() + end +end + +function lib.Processors.config(event, subevent, ...) + -- update private variables, but only if a relevant setting may have changed + if subevent == "changed" then + local setting = ... + if setting and setting:match("^arbitrage") then + private.SetCurrentFaction() + end + elseif subevent == "loaded" or subevent == "reset" or subevent == "deleted" then + private.SetCurrentFaction() + end +end + +function lib.Processors.resources(event, subevent, ...) + if subevent == "faction" then + private.SetCurrentFaction() + end +end + +function lib.Processors.onload(event, subevent, ...) + if subevent == "auc-util-searchui" then + if private.createRealmList then + private.createRealmList() + end + end +end + +function lib.Processors.selecttab(event, subevent, ...) + if event == "search" and subevent == "complete" and private.factionUpdateRequired then + -- something changed during a search - complete the update now the search has finished + private.factionUpdateRequired = nil + private.SetCurrentFaction() + elseif event == "selecttab" then + if subevent == lib.tabname and private.doValidation then + private.doValidation() + end + elseif event == "config" then + -- update private variables, but only if a relevant setting may have changed + if subevent == "changed" then + local setting = ... + if setting and setting:match("^arbitrage") then + private.SetCurrentFaction() + end + elseif subevent == "loaded" or subevent == "reset" or subevent == "deleted" then + private.SetCurrentFaction() + end + elseif event == "resources" and subevent == "faction" then + private.SetCurrentFaction() + elseif event == "onload" and subevent == "auc-util-searchui" then + if private.createRealmList then + private.createRealmList() + end + end +end + + +-- Keep our internal settings up to date with any changes, so that Search can just use the values +function private.SetCurrentFaction() + if parent.IsSearching() then + -- cannot update settings until the search finishes - flag it and exit + private.factionUpdateRequired = true + return + end + + local playerFaction = UnitFactionGroup("player") + local playerRealm = resources.Realm + + local searchRealm, searchFaction + local searchstyle = get("arbitrage.search.style") + + if searchstyle == "Neutral" then + -- if at neutral compare to home. if at home, compare to neutral + searchFaction = resources.Faction == "Neutral" and playerFaction or "Neutral" + searchRealm = playerRealm + elseif searchstyle == "Cross-Faction" then + -- search opposing faction (even if at neutral AH) + searchFaction = playerFaction == "Alliance" and "Horde" or "Alliance" + searchRealm = playerRealm + elseif searchstyle == "Cross-Realm" then + -- search whatever combination is in the two crossrealm* dropdown boxes + searchFaction = get("arbitrage.search.crossrealmfaction") + searchRealm = get("arbitrage.search.crossrealmrealm") + + -- force there to always be a crossrealmrealm setting, to avoid Selectbox problems + -- we cannot set a default as this causes problems using Arbitrage on different servers - which defeats the purpose + if not searchRealm then + searchRealm = playerRealm + set("arbitrage.search.crossrealmrealm", searchRealm) + end + else + -- invalid setting - clear it and bail out - calling set() will recurse into SetCurrentFaction + set("arbitrage.search.style", nil) + return + end + private.searchKey = searchRealm.."-"..searchFaction -- serverKey + private.searchFaction = searchFaction:lower() + private.searchAdjust = searchFaction == "Neutral" and 0.85 or 0.95 -- cut rate adjustment + private.searchLabel = "|cffffff7fSearching: "..searchRealm.."/"..searchFaction.."|r" + + -- Display our current search destination in the GUI + if private.displaySearch then + private.displaySearch:SetText(private.searchLabel) + end +end + +-- This function is automatically called when we need to create our search parameters +function lib:MakeGuiConfig(gui) + -- Get our tab and populate it with our controls + local id = gui:AddTab(lib.tabname, "Searchers") + gui:MakeScrollable(id) + + -- Add the help + gui:AddSearcher("Arbitrage", "Find items which can be neutral, cross-faction or cross-realm traded", 100) + gui:AddHelp(id, "arbitrage searcher", + "What does this searcher do?", + "This searcher provides the ability to search for specific items that can be traded to neutral, cross faction or cross realm for a profit.") + + gui:AddControl(id, "Header", 0, "Arbitrage search criteria") + + local last = gui:GetLast(id) + + gui:AddControl(id, "MoneyFramePinned", 0, 1, "arbitrage.profit.min", 1, 99999999, "Minimum Profit") + gui:AddControl(id, "Slider", 0, 1, "arbitrage.profit.pct", 1, 100, .5, "Min Discount: %0.01f%%") + gui:AddControl(id, "Checkbox", 0, 1, "arbitrage.seen.check", "Check Seen count") + gui:AddControl(id, "Slider", 0, 2, "arbitrage.seen.min", 1, 100, 1, "Min seen count: %s") + + gui:AddControl(id, "Subhead", 0, "Search against") + private.displaySearch = gui:AddControl(id, "Label", 0, 1, nil, private.searchLabel) + gui:AddControl(id, "Label", 0, 1, nil, "Search type:") + gui:AddControl(id, "Selectbox", 0, 1, private.getStyles, "arbitrage.search.style") + gui:AddControl(id, "Label", 0, 1, nil, "Cross-Realm additional settings:") + gui:AddControl(id, "Selectbox", 0, 1, private.getRealmList, "arbitrage.search.crossrealmrealm") + gui:AddControl(id, "Selectbox", 0, 1, private.getFactions, "arbitrage.search.crossrealmfaction") + + gui:SetLast(id, last) + gui:AddControl(id, "Checkbox", 0.42, 1, "arbitrage.allow.bid", "Allow Bids") + gui:SetLast(id, last) + gui:AddControl(id, "Checkbox", 0.56, 1, "arbitrage.allow.buy", "Allow Buyouts") + gui:AddControl(id, "Checkbox", 0.42, 1, "arbitrage.maxprice.enable", "Enable individual maximum price:") + gui:AddTip(id, "Limit the maximum amount you want to spend with the Arbitrage searcher") + gui:AddControl(id, "MoneyFramePinned", 0.42, 2, "arbitrage.maxprice", 1, 99999999, "Maximum Price for Arbitrage") + + gui:AddControl(id, "Subhead", 0.42, "Price Valuation Method:") + gui:AddControl(id, "Selectbox", 0.42, 1, resources.selectorPriceModels, "arbitrage.model") + gui:AddTip(id, "The pricing model that is used to work out the calculated value of items at the Auction House.") + + gui:AddControl(id, "Subhead", 0.42, "Fees Adjustment") + gui:AddControl(id, "Checkbox", 0.42, 1, "arbitrage.adjust.brokerage", "Subtract auction fees") + gui:AddControl(id, "Checkbox", 0.42, 1, "arbitrage.adjust.deposit", "Subtract deposit:") + gui:AddControl(id, "Selectbox", 0.42, 1, resources.selectorAuctionLength, "arbitrage.adjust.deplength") + gui:AddControl(id, "Slider", 0.42, 1, "arbitrage.adjust.listings", 1, 10, .1, "Ave relistings: %0.1fx") +end + +function lib.Search(item) + local link = item[Const.LINK] + if not link then + return false, "No link" + end + local bidprice, buyprice = item[Const.PRICE], item[Const.BUYOUT] + local maxprice = get("arbitrage.maxprice.enable") and get("arbitrage.maxprice") + if buyprice <= 0 or not get("arbitrage.allow.buy") or (maxprice and buyprice > maxprice) then + buyprice = nil + end + if not get("arbitrage.allow.bid") or (maxprice and bidprice > maxprice) then + bidprice = nil + end + if not (bidprice or buyprice) then + return false, "Does not meet bid/buy requirements" + end + + local market, seen, curModel = resources.GetPrice(get("arbitrage.model"), link, private.searchKey) + if not market then + return false, "No market price" + end + local count = item[Const.COUNT] + market = market * count + + if (get("arbitrage.seen.check")) and curModel ~= "fixed" then + if ((not seen) or (seen < get("arbitrage.seen.min"))) then + return false, "Seen count too low" + end + end + + --set up correct brokerage/deposit costs for our target AH + if get("arbitrage.adjust.brokerage") then + market = market * private.searchAdjust + end + if get("arbitrage.adjust.deposit") then + local amount = GetDepositCost(link, get("arbitrage.adjust.deplength"), private.searchFaction, count) + if amount then + market = market - amount * get("arbitrage.adjust.listings") + end + end + + local value = min(market*(100-get("arbitrage.profit.pct"))/100, market-get("arbitrage.profit.min")) + if buyprice and buyprice <= value then + return "buy", market + elseif bidprice and bidprice <= value then + return "bid", market + end + return false, "Not enough profit" +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-SearchUI/SearcherArbitrage.lua $", "$Rev: 4880 $") diff --git a/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherConverter.lua b/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherConverter.lua new file mode 100644 index 0000000..811bb87 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherConverter.lua @@ -0,0 +1,332 @@ +--[[ + Auctioneer - Search UI - Searcher Converter + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: SearcherConverter.lua 4840 2010-08-04 21:44:00Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This is a plugin module for the SearchUI that assists in searching by refined paramaters + + 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 +--]] +-- Create a new instance of our lib with our parent +local lib, parent, private = AucSearchUI.NewSearcher("Converter") +if not lib then return end +--local print,decode,_,_,replicate,empty,_,_,_,debugPrint,fill = AucAdvanced.GetModuleLocals() +local get, set, default, Const, resources = parent.GetSearchLocals() +lib.tabname = "Converter" + +-- Build a table to do all our work +-- findConvertable[itemID] = {conversionID, yield, checkstring} +-- Note: ItemSuggest uses a copy of this code; if you change it here, change it in ItemSuggest as well! +local findConvertable = {} +do + -- Set our constants + --Essences + local GCOSMIC = 34055 + local GPLANAR = 22446 + local GETERNAL = 16203 + local GNETHER = 11175 + local GMYSTIC = 11135 + local GASTRAL = 11082 + local GMAGIC = 10939 + local LCOSMIC = 34056 + local LPLANAR = 22447 + local LETERNAL = 16202 + local LNETHER = 11174 + local LMYSTIC = 11134 + local LASTRAL = 10998 + local LMAGIC = 10938 + --Motes/Primals + local PAIR = 22451 + local MAIR = 22572 + local PEARTH= 22452 + local MEARTH = 22573 + local PFIRE = 21884 + local MFIRE = 22574 + local PLIFE = 21886 + local MLIFE = 22575 + local PMANA = 22457 + local MMANA = 22576 + local PSHADOW = 22456 + local MSHADOW = 22577 + local PWATER = 21885 + local MWATER = 22578 + --Crystallized/Eternal + local CAIR = 37700 + local EAIR = 35623 + local CEARTH = 37701 + local EEARTH = 35624 + local CSHADOW = 37703 + local ESHADOW = 35627 + local CLIFE = 37704 + local ELIFE = 35625 + local CFIRE = 37702 + local EFIRE = 36860 + local CWATER = 37705 + local EWATER = 35622 + --Depleted items + local DCBRACER = 32676 -- Depleted Cloth Bracers + local DCBRACERTO = 32655 -- Crystalweave Bracers + local DMGAUNTLETS = 32675 -- Depleted Mail Gauntlets + local DMGAUNTLETSTO = 32656 -- Crystalhide Handwraps + local DBADGE = 32672 -- Depleted Badge + local DBADGETO = 32658 -- Badge of Tenacity + local DCLOAK = 32677 -- Depleted Cloak + local DCLOAKTO = 32665 -- Crystalweave Cape + local DDAGGER = 32673 -- Depleted Dagger + local DDAGGERTO = 32659 -- Crystal-Infused Shiv + local DMACE = 32671 -- Depleted Mace + local DMACETO = 32661 -- Apexis Crystal Mace + local DRING = 32678 -- Depleted Ring + local DRINGTO = 32664 -- Dreamcrystal Band + local DSTAFF = 32679 -- Depleted Staff + local DSTAFFTO = 32662 -- Flaming Quartz Staff + local DSWORD = 32674 -- Depleted Sword + local DSWORDTO = 32660 -- Crystalforged Sword + local DTHAXE = 32670 -- Depleted Two-Handed Axe + local DTHAXETO = 32663 -- Apexis Cleaver + + -- Temporary tables to help build the working table + -- To add new conversions, edit these tables + + -- TWO WAY Tables + + local lesser_greater = { + [LCOSMIC] = GCOSMIC, + [LPLANAR] = GPLANAR, + [LETERNAL] = GETERNAL, + [LNETHER] = GNETHER, + [LMYSTIC] = GMYSTIC, + [LASTRAL] = GASTRAL, + [LMAGIC] = GMAGIC, + } + local crystallized_eternal = { + [CAIR] = EAIR, + [CEARTH] = EEARTH, + [CSHADOW] = ESHADOW, + [CLIFE] = ELIFE, + [CFIRE] = EFIRE, + [CWATER] = EWATER, + } + + -- ONE WAY Tables + + local mote2primal = { + [MAIR] = PAIR, + [MEARTH] = PEARTH, + [MFIRE] = PFIRE, + [MLIFE] = PLIFE, + [MMANA] = PMANA, + [MSHADOW] = PSHADOW, + [MWATER] = PWATER, + } + local depleted2enhanced = { + [DCBRACER] = DCBRACERTO, + [DMGAUNTLETS] = DMGAUNTLETSTO, + [DBADGE] = DBADGETO, + [DCLOAK] = DCLOAKTO, + [DDAGGER] = DDAGGERTO, + [DMACE] = DMACETO, + [DRING] = DRINGTO, + [DSTAFF] = DSTAFFTO, + [DSWORD] = DSWORDTO, + [DTHAXE] = DTHAXETO, + } + + --[[ placeholder for future development - not sure how this will work yet... + -- Trade Professions need to be handled differently as yields may vary + local smelt = { + [10] = { + [PEARTH] = MEARTH, + [PFIRE] = MFIRE, + }, + } + --]] + + -- Build the table + -- Two-way + for idl, idg in pairs (lesser_greater) do + findConvertable[idl] = {idg, 1/3, "converter.enableEssence"} + findConvertable[idg] = {idl, 3, "converter.enableEssence"} + end + for idc, ide in pairs (crystallized_eternal) do + findConvertable[idc] = {ide, 0.1, "converter.enableCrystallized"} + findConvertable[ide] = {idc, 10, "converter.enableCrystallized"} + end + -- One-way + for id, idto in pairs (mote2primal) do + findConvertable[id] = {idto, 0.1, "converter.enableMote"} + end + for id, idto in pairs (depleted2enhanced) do + findConvertable[id] = {idto, 1, "converter.enableDepleted"} + end +end +-- export the table for other addons to reference +resources.SearcherConverterLookupTable = findConvertable + +default("converter.profit.min", 1) +default("converter.profit.pct", 50) +default("converter.adjust.brokerage", true) +default("converter.adjust.deposit", true) +default("converter.adjust.deplength", 48) +default("converter.adjust.listings", 3) +default("converter.allow.bid", true) +default("converter.allow.buy", true) +default("converter.maxprice", 10000000) +default("converter.maxprice.enable", false) +--default("converter.matching.check", true) +--default("converter.buyout.check", true) +default("converter.enableEssence", true) +default("converter.enableMote", true) +default("converter.enableCrystallized", true) +default("converter.enableDepleted", false) +default("converter.model", "market") + +function private.doValidation() + if not resources.isValidPriceModel(get("converter.model")) then + message("Converter Searcher Warning!\nCurrent price model setting ("..get("converter.model")..") is not valid. Select a new price model") + else + private.doValidation = nil + end +end + +-- This function is automatically called from AucSearchUI.NotifyCallbacks +private.validationRequired = true +function lib.Processor(event, subevent) + if event == "selecttab" then + if subevent == lib.tabname and private.doValidation then + private.doValidation() + end + end +end + + +lib.Processors = {} +function lib.Processors.selecttab(event, subevent) + if subevent == lib.tabname and private.doValidation then + private.doValidation() + end +end + + +-- This function is automatically called when we need to create our search parameters +function lib:MakeGuiConfig(gui) + -- Get our tab and populate it with our controls + local id = gui:AddTab(lib.tabname, "Searchers") + + -- Add the help + gui:AddSearcher("Converter", "Search for items which can be converted into other items for profit (essences, motes, etc)", 100) + gui:AddHelp(id, "converter searcher", + "What does this searcher do?", + "This searcher provides the ability to search for items that can be converted to another item which is worth more money.") + + gui:AddControl(id, "Header", 0, "Converter search criteria") + local last = gui:GetLast(id) + + gui:AddControl(id, "MoneyFramePinned", 0, 1, "converter.profit.min", 1, 99999999, "Minimum Profit") + gui:AddControl(id, "Slider", 0, 1, "converter.profit.pct", 1, 100, .5, "Min Discount: %0.01f%%") + + gui:AddControl(id, "Subhead", 0, "Include in search") + gui:AddControl(id, "Checkbox", 0, 1, "converter.enableEssence", "Essence: Greater <> Lesser") + gui:AddControl(id, "Checkbox", 0, 1, "converter.enableMote", "Mote > Primal") + gui:AddControl(id, "Checkbox", 0, 1, "converter.enableCrystallized", "Crystallized <> Eternal") + gui:AddControl(id, "Checkbox", 0, 1, "converter.enableDepleted", "Depleted Items") + gui:AddTip(id, "Warning: depleted items require Apexis Shards to convert. Apexis Shards can only be obtained from certain locations in Outland.") + + gui:SetLast(id, last) + gui:AddControl(id, "Checkbox", 0.42, 1, "converter.allow.bid", "Allow Bids") + gui:SetLast(id, last) + gui:AddControl(id, "Checkbox", 0.56, 1, "converter.allow.buy", "Allow Buyouts") + gui:AddControl(id, "Checkbox", 0.42, 1, "converter.maxprice.enable", "Enable individual maximum price:") + gui:AddTip(id, "Limit the maximum amount you want to spend with the Converter searcher") + gui:AddControl(id, "MoneyFramePinned", 0.42, 2, "converter.maxprice", 1, 99999999, "Maximum Price for Converter") + + gui:AddControl(id, "Subhead", 0.42, "Price Valuation Method:") + gui:AddControl(id, "Selectbox", 0.42, 1, resources.selectorPriceModels, "converter.model") + gui:AddTip(id, "The pricing model that is used to work out the calculated value of items at the Auction House.") + + gui:AddControl(id, "Subhead", 0.42, "Fees Adjustment") + gui:AddControl(id, "Checkbox", 0.42, 1, "converter.adjust.brokerage", "Subtract auction fees") + gui:AddControl(id, "Checkbox", 0.42, 1, "converter.adjust.deposit", "Subtract deposit cost") + gui:AddControl(id, "Selectbox", 0.42, 1, resources.selectorAuctionLength, "converter.adjust.deplength") + gui:AddControl(id, "Slider", 0.42, 1, "converter.adjust.listings", 1, 10, .1, "Ave relistings: %0.1fx") + + --gui:AddControl(id, "Subhead", 0.42, "Appraiser Value Origination") + --gui:AddControl(id, "Checkbox", 0.42, 1, "converter.matching.check", "Use Market Matched") + --gui:AddControl(id, "Checkbox", 0.42, 1, "converter.buyout.check", "Use buyout not bid") + + gui:SetLast(id, last) +end + +function lib.Search (item) + local convert = findConvertable[item[Const.ITEMID]] + if not convert then + return false, "Item not convertable" + end + + local newID, yield, test = unpack(convert) + if not get(test) then + return false, "Category disabled" + end + + local bidprice, buyprice = item[Const.PRICE], item[Const.BUYOUT] + local maxprice = get("converter.maxprice.enable") and get("converter.maxprice") + if buyprice <= 0 or not get("converter.allow.buy") or (maxprice and buyprice > maxprice) then + buyprice = nil + end + if not get("converter.allow.bid") or (maxprice and bidprice > maxprice) then + bidprice = nil + end + if not (bidprice or buyprice) then + return false, "Does not meet bid/buy requirements" + end + + local market = resources.GetPrice(get("converter.model"), newID) + if not market then + return false, "No market price" + end + local count = item[Const.COUNT] * yield + market = market * count + + --adjust for brokerage/deposit costs + if get("converter.adjust.brokerage") then + market = market * resources.CutAdjust + end + if get("converter.adjust.deposit") then + -- note: GetDepositCost calls GetSellValue API, which handles numerical itemIDs (prefers them actually) + local amount = GetDepositCost(newID, get("converter.adjust.deplength"), resources.faction, count) + if amount then + market = market - amount * get("converter.adjust.listings") + end + end + + local value = min (market*(100-get("converter.profit.pct"))/100, market-get("converter.profit.min")) + if buyprice and buyprice <= value then + return "buy", market + elseif bidprice and bidprice <= value then + return "bid", market + end + return false, "Not enough profit" +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-SearchUI/SearcherConverter.lua $", "$Rev: 4840 $") diff --git a/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherDisenchant.lua b/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherDisenchant.lua new file mode 100644 index 0000000..95a3b67 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherDisenchant.lua @@ -0,0 +1,192 @@ +--[[ + Auctioneer - Search UI - Searcher Disenchant + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: SearcherDisenchant.lua 4840 2010-08-04 21:44:00Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This is a plugin module for the SearchUI that assists in searching by refined paramaters + + 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 +--]] +-- Create a new instance of our lib with our parent +local lib, parent, private = AucSearchUI.NewSearcher("Disenchant") +if not lib then return end +--local print,decode,_,_,replicate,empty,_,_,_,debugPrint,fill = AucAdvanced.GetModuleLocals() +local get, set, default, Const, resources = parent.GetSearchLocals() +lib.tabname = "Disenchant" + +-- Set our defaults +default("disenchant.profit.min", 1) +default("disenchant.profit.pct", 50) +default("disenchant.level.custom", false) +default("disenchant.level.min", 0) +default("disenchant.level.max", 450) +default("disenchant.adjust.brokerage", true) +default("disenchant.allow.bid", true) +default("disenchant.allow.buy", true) +default("disenchant.maxprice", 10000000) +default("disenchant.maxprice.enable", false) +default("disenchant.model", "Enchantrix") + +function private.doValidation() + if not resources.isEnchantrixLoaded then + message("Disenchant Searcher Warning!\nEnchantrix not detected\nThis searcher will not function until Enchantrix is loaded") + elseif not resources.isValidPriceModel(get("disenchant.model")) then + message("Disenchant Searcher Warning!\nCurrent price model setting ("..get("disenchant.model")..") is not valid. Select a new price model") + else + private.doValidation = nil + end +end + +-- This function is automatically called from AucSearchUI.NotifyCallbacks +function lib.Processor(event, subevent) + if event == "selecttab" then + if subevent == lib.tabname and private.doValidation then + private.doValidation() + end + end +end + +lib.Processors = {} +function lib.Processors.selecttab(event, subevent) + if subevent == lib.tabname and private.doValidation then + private.doValidation() + end +end + + +-- This function is automatically called when we need to create our search parameters +function lib:MakeGuiConfig(gui) + -- Get our tab and populate it with our controls + local id = gui:AddTab(lib.tabname, "Searchers") + + -- Add the help + gui:AddSearcher("Disenchant", "Search for items which can be disenchanted for profit", 100) + gui:AddHelp(id, "disenchant searcher", + "What does this searcher do?", + "This searcher provides the ability to search for items that are able to be disenchanted into reagents that on average will have a greater value than the purchase price of the given item.") + + gui:AddControl(id, "Header", 0, "Disenchant search criteria") + + local last = gui:GetLast(id) + + gui:AddControl(id, "MoneyFramePinned", 0, 1, "disenchant.profit.min", 1, 99999999, "Minimum Profit") + gui:AddControl(id, "Slider", 0, 1, "disenchant.profit.pct", 1, 100, .5, "Min Discount: %0.01f%%") + gui:AddControl(id, "Checkbox", 0, 1, "disenchant.level.custom", "Use custom levels") + gui:AddControl(id, "Slider", 0, 2, "disenchant.level.min", 0, 450, 25, "Minimum skill: %s") + gui:AddControl(id, "Slider", 0, 2, "disenchant.level.max", 25, 450, 25, "Maximum skill: %s") + gui:AddControl(id, "Subhead", 0, "Note:") + gui:AddControl(id, "Note", 0, 1, 290, 30, "The \"Pct\" Column is \% of DE Value") + + gui:SetLast(id, last) + gui:AddControl(id, "Checkbox", 0.42, 1, "disenchant.allow.bid", "Allow Bids") + gui:SetLast(id, last) + gui:AddControl(id, "Checkbox", 0.56, 1, "disenchant.allow.buy", "Allow Buyouts") + gui:AddControl(id, "Checkbox", 0.42, 1, "disenchant.maxprice.enable", "Enable individual maximum price:") + gui:AddTip(id, "Limit the maximum amount you want to spend with the Disenchant searcher") + gui:AddControl(id, "MoneyFramePinned", 0.42, 2, "disenchant.maxprice", 1, 99999999, "Maximum Price for Disenchant") + + gui:AddControl(id, "Subhead", 0.42, "Price Valuation Method:") + gui:AddControl(id, "Selectbox", 0.42, 1, resources.selectorPriceModelsEnx, "disenchant.model") + gui:AddTip(id, "The pricing model that is used to work out the calculated value of items at the Auction House.") + + gui:AddControl(id, "Subhead", 0.42, "Fees Adjustment") + gui:AddControl(id, "Checkbox", 0.42, 1, "disenchant.adjust.brokerage", "Subtract auction fees") +end + +function lib.Search(item) + if not resources.isEnchantrixLoaded then + return false, "Enchantrix not detected" + end + + local itemQuality = item[Const.QUALITY] + if itemQuality <= 1 then + return false, "Item quality too low" + end + + local bidprice, buyprice = item[Const.PRICE], item[Const.BUYOUT] + local maxprice = get("disenchant.maxprice.enable") and get("disenchant.maxprice") + if buyprice <= 0 or not get("disenchant.allow.buy") or (maxprice and buyprice > maxprice) then + buyprice = nil + end + if not get("disenchant.allow.bid") or (maxprice and bidprice > maxprice) then + bidprice = nil + end + if not (bidprice or buyprice) then + return false, "Does not meet bid/buy requirements" + end + + local minskill, maxskill + if get("disenchant.level.custom") then + minskill = get("disenchant.level.min") + maxskill = get("disenchant.level.max") + else + minskill = 0 + maxskill = Enchantrix.Util.GetUserEnchantingSkill() + end + local skillneeded = Enchantrix.Util.DisenchantSkillRequiredForItemLevel(item[Const.ILEVEL], itemQuality) + if (skillneeded < minskill) or (skillneeded > maxskill) then + return false, "Skill not high enough to disenchant" + end + + local data = Enchantrix.Storage.GetItemDisenchants(item[Const.ITEMID]) + if not data then + return false, "Item not Disenchantable" + end + + local total = data.total + local market = 0 + if total and total[1] > 0 then + local totalNumber, totalQuantity = unpack(total) + local model = get("disenchant.model") + local GetPrice = resources.lookupPriceModel[model] + for result, resData in pairs(data) do + if result ~= "total" then + local resNumber, resYield = unpack(resData) + local price = GetPrice(model, result) + if price then + market = market + price * resYield / totalNumber + end + end + end + end + if market <= 0 then + return false, "No price found" + end + + --adjust for brokerage costs + if get("disenchant.adjust.brokerage") then + market = market * resources.CutAdjust + end + + -- check amount of profit + local value = min (market*(100-get("disenchant.profit.pct"))/100, market-get("disenchant.profit.min")) + if buyprice and buyprice <= value then + return "buy", market + elseif bidprice and bidprice <= value then + return "bid", market + end + return false, "Not enough profit" +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-SearchUI/SearcherDisenchant.lua $", "$Rev: 4840 $") diff --git a/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherEnchantMats.lua b/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherEnchantMats.lua new file mode 100644 index 0000000..733f0f6 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherEnchantMats.lua @@ -0,0 +1,364 @@ +--[[ + Auctioneer - Search UI - Searcher EnchantMats + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: SearcherEnchantMats.lua 4840 2010-08-04 21:44:00Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This is a plugin module for the SearchUI that assists in searching by refined paramaters + + 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 +--]] +-- Create a new instance of our lib with our parent +local lib, parent, private = AucSearchUI.NewSearcher("EnchantMats") +if not lib then return end +--local print,decode,_,_,replicate,empty,_,_,_,debugPrint,fill = AucAdvanced.GetModuleLocals() +local get, set ,default ,Const, resources = parent.GetSearchLocals() +lib.tabname = "EnchantMats" + +-- Enchanting reagents, from Enchantrix EnxConstants.lua +local VOID = 22450 +local NEXUS = 20725 +local LPRISMATIC = 22449 +local LBRILLIANT = 14344 +local LRADIANT = 11178 +local LGLOWING = 11139 +local LGLIMMERING = 11084 +local SPRISMATIC = 22448 +local SBRILLIANT = 14343 +local SRADIANT = 11177 +local SGLOWING = 11138 +local SGLIMMERING = 10978 +local GPLANAR = 22446 +local GETERNAL = 16203 +local GNETHER = 11175 +local GMYSTIC = 11135 +local GASTRAL = 11082 +local GMAGIC = 10939 +local LPLANAR = 22447 +local LETERNAL = 16202 +local LNETHER = 11174 +local LMYSTIC = 11134 +local LASTRAL = 10998 +local LMAGIC = 10938 +local ARCANE = 22445 +local ILLUSION = 16204 +local DREAM = 11176 +local VISION = 11137 +local SOUL = 11083 +local STRANGE = 10940 + +local DREAM_SHARD = 34052 +local SDREAM_SHARD = 34053 +local INFINITE = 34054 +local GCOSMIC = 34055 +local LCOSMIC = 34056 +local ABYSS = 34057 + +-- a table we can check for item ids +local validReagents = + { + [VOID] = true, + [NEXUS] = true, + [LPRISMATIC] = true, + [LBRILLIANT] = true, + [LRADIANT] = true, + [LGLOWING] = true, + [LGLIMMERING] = true, + [SPRISMATIC] = true, + [SBRILLIANT] = true, + [SRADIANT] = true, + [SGLOWING] = true, + [SGLIMMERING] = true, + [GPLANAR] = true, + [GETERNAL] = true, + [GNETHER] = true, + [GMYSTIC] = true, + [GASTRAL] = true, + [GMAGIC] = true, + [LPLANAR] = true, + [LETERNAL] = true, + [LNETHER] = true, + [LMYSTIC] = true, + [LASTRAL] = true, + [LMAGIC] = true, + [ARCANE] = true, + [ILLUSION] = true, + [DREAM] = true, + [VISION] = true, + [SOUL] = true, + [STRANGE] = true, + [DREAM_SHARD] = true, + [SDREAM_SHARD] = true, + [INFINITE] = true, + [GCOSMIC] = true, + [LCOSMIC] = true, + [ABYSS] = true, + } + +-- Set our defaults +default("enchantmats.level.custom", false) +default("enchantmats.level.min", 0) +default("enchantmats.level.max", 450) +default("enchantmats.allow.bid", true) +default("enchantmats.allow.buy", true) +default("enchantmats.maxprice", 10000000) +default("enchantmats.maxprice.enable", false) +default("enchantmats.model", "Enchantrix") + +--Slider variables +default("enchantmats.PriceAdjust."..GPLANAR, 100) +default("enchantmats.PriceAdjust."..GETERNAL, 100) +default("enchantmats.PriceAdjust."..GNETHER, 100) +default("enchantmats.PriceAdjust."..GMYSTIC, 100) +default("enchantmats.PriceAdjust."..GASTRAL, 100) +default("enchantmats.PriceAdjust."..GMAGIC, 100) +default("enchantmats.PriceAdjust."..LPLANAR, 100) +default("enchantmats.PriceAdjust."..LETERNAL, 100) +default("enchantmats.PriceAdjust."..LNETHER, 100) +default("enchantmats.PriceAdjust."..LMYSTIC, 100) +default("enchantmats.PriceAdjust."..LASTRAL, 100) +default("enchantmats.PriceAdjust."..LMAGIC, 100) +default("enchantmats.PriceAdjust."..ARCANE, 100) +default("enchantmats.PriceAdjust."..ILLUSION, 100) +default("enchantmats.PriceAdjust."..DREAM, 100) +default("enchantmats.PriceAdjust."..VISION, 100) +default("enchantmats.PriceAdjust."..SOUL, 100) +default("enchantmats.PriceAdjust."..STRANGE, 100) +default("enchantmats.PriceAdjust."..LPRISMATIC, 100) +default("enchantmats.PriceAdjust."..LBRILLIANT, 100) +default("enchantmats.PriceAdjust."..LRADIANT, 100) +default("enchantmats.PriceAdjust."..LGLOWING, 100) +default("enchantmats.PriceAdjust."..LGLIMMERING, 100) +default("enchantmats.PriceAdjust."..SPRISMATIC, 100) +default("enchantmats.PriceAdjust."..SBRILLIANT, 100) +default("enchantmats.PriceAdjust."..SRADIANT, 100) +default("enchantmats.PriceAdjust."..SGLOWING, 100) +default("enchantmats.PriceAdjust."..SGLIMMERING, 100) +default("enchantmats.PriceAdjust."..VOID, 100) +default("enchantmats.PriceAdjust."..NEXUS, 100) +default("enchantmats.PriceAdjust."..DREAM_SHARD, 100) +default("enchantmats.PriceAdjust."..SDREAM_SHARD, 100) +default("enchantmats.PriceAdjust."..INFINITE, 100) +default("enchantmats.PriceAdjust."..GCOSMIC, 100) +default("enchantmats.PriceAdjust."..LCOSMIC, 100) +default("enchantmats.PriceAdjust."..ABYSS, 100) + +function private.doValidation() + if not resources.isEnchantrixLoaded then + message("EnchantMats Searcher Warning!\nEnchantrix not detected\nThis searcher will not function until Enchantrix is loaded") + elseif not resources.isValidPriceModel(get("enchantmats.model")) then + message("EnchantMats Searcher Warning!\nCurrent price model setting ("..get("enchantmats.model")..") is not valid. Select a new price model") + else + private.doValidation = nil + end +end + +-- This function is automatically called from AucSearchUI.NotifyCallbacks +function lib.Processor(event, subevent) + if event == "selecttab" then + if subevent == lib.tabname and private.doValidation then + private.doValidation() + end + end +end + +lib.Processors = {} +function lib.Processors.selecttab(event, subevent) + if subevent == lib.tabname and private.doValidation then + private.doValidation() + end +end + +-- This function is automatically called when we need to create our search parameters +function lib:MakeGuiConfig(gui) + -- Get our tab and populate it with our controls + local id = gui:AddTab(lib.tabname, "Searchers") + gui:MakeScrollable(id) + + -- Add the help + gui:AddSearcher("Enchant Mats", "Search for items which will disenchant for you into given reagents (for levelling)", 100) + gui:AddHelp(id, "enchantmats searcher", + "What does this searcher do?", + "This searcher provides the ability to search for items which will disenchant into the reagents you need to have in order to level your enchanting skill. It is not a searcher meant for profit, but rather least cost for levelling.") + + gui:AddControl(id, "Header", 0, "EnchantMats search criteria") + + local last = gui:GetLast(id) + + gui:AddControl(id, "Checkbox", 0.42, 1, "enchantmats.allow.bid", "Allow Bids") + gui:SetLast(id, last) + gui:AddControl(id, "Checkbox", 0.56, 1, "enchantmats.allow.buy", "Allow Buyouts") + gui:AddControl(id, "Checkbox", 0.42, 1, "enchantmats.maxprice.enable", "Enable individual maximum price:") + gui:AddTip(id, "Limit the maximum amount you want to spend with the EnchantMats searcher") + gui:AddControl(id, "MoneyFramePinned", 0.42, 2, "enchantmats.maxprice", 1, 99999999, "Maximum Price for EnchantMats") + + gui:AddControl(id, "Label", 0.42, 1, nil, "Price Valuation Method:") + gui:AddControl(id, "Selectbox", 0.42, 1, resources.selectorPriceModelsEnx, "enchantmats.model") + gui:AddTip(id, "The pricing model that is used to work out the calculated value of items at the Auction House.") + + gui:SetLast(id, last) + gui:AddControl(id, "Checkbox", 0, 1, "enchantmats.level.custom", "Use custom enchanting skill levels") + gui:AddControl(id, "Slider", 0, 2, "enchantmats.level.min", 0, 450, 25, "Minimum skill: %s") + gui:AddControl(id, "Slider", 0, 2, "enchantmats.level.max", 25, 450, 25, "Maximum skill: %s") + + -- spacer to allow for all the controls on the right hand side + gui:AddControl(id, "Note", 0, 0, nil, 40, "") + + -- aka "what percentage of estimated value am I willing to pay for this reagent"? + gui:AddControl(id, "Subhead", 0, "Reageant Price Modification") + + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..GCOSMIC, 0, 200, 1, "Greater Cosmic Essence %s%%" ) + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..GPLANAR, 0, 200, 1, "Greater Planar Essence %s%%" ) + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..GETERNAL, 0, 200, 1, "Greater Eternal Essence %s%%") + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..GNETHER, 0, 200, 1, "Greater Nether Essence %s%%") + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..GMYSTIC, 0, 200, 1, "Greater Mystic Essence %s%%") + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..GASTRAL, 0, 200, 1, "Greater Astral Essence %s%%") + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..GMAGIC, 0, 200, 1, "Greater Magic Essence %s%%") + + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..LCOSMIC, 0, 200, 1, "Lesser Cosmic Essence %s%%" ) + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..LPLANAR, 0, 200, 1, "Lesser Planar Essence %s%%" ) + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..LETERNAL, 0, 200, 1, "Lesser Eternal Essence %s%%") + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..LNETHER, 0, 200, 1, "Lesser Nether Essence %s%%") + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..LMYSTIC, 0, 200, 1, "Lesser Mystic Essence %s%%") + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..LASTRAL, 0, 200, 1, "Lesser Astral Essence %s%%") + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..LMAGIC, 0, 200, 1, "Lesser Magic Essence %s%%") + + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..INFINITE, 0, 200, 1, "Infinite Dust %s%%" ) + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..ARCANE, 0, 200, 1, "Arcane Dust %s%%" ) + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..ILLUSION, 0, 200, 1, "Illusion Dust %s%%") + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..DREAM, 0, 200, 1, "Dream Dust %s%%") + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..VISION, 0, 200, 1, "Vision Dust %s%%") + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..SOUL, 0, 200, 1, "Soul Dust %s%%") + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..STRANGE, 0, 200, 1, "Strange Dust %s%%") + + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..DREAM_SHARD, 0, 200, 1, "Dream Shard %s%%" ) + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..LPRISMATIC, 0, 200, 1, "Large Prismatic Shard %s%%" ) + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..LBRILLIANT, 0, 200, 1, "Large Brilliant Shard %s%%") + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..LRADIANT, 0, 200, 1, "Large Radiant Shard %s%%") + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..LGLOWING, 0, 200, 1, "Large Glowing Shard %s%%") + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..LGLIMMERING, 0, 200, 1, "Large Glimmering Shard %s%%") + + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..SDREAM_SHARD, 0, 200, 1, "Small Dream Shard %s%%") + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..SPRISMATIC, 0, 200, 1, "Small Prismatic Shard %s%%") + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..SBRILLIANT, 0, 200, 1, "Small Brilliant Shard %s%%") + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..SRADIANT, 0, 200, 1, "Small Radiant Shard %s%%") + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..SGLOWING, 0, 200, 1, "Small Glowing Shard %s%%") + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..SGLIMMERING, 0, 200, 1, "Small Glimmering Shard %s%%") + + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..ABYSS, 0, 200, 1, "Abyss Crystal %s%%" ) + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..VOID, 0, 200, 1, "Void Crystal %s%%" ) + gui:AddControl(id, "WideSlider", 0, 1, "enchantmats.PriceAdjust."..NEXUS, 0, 200, 1, "Nexus Crystal %s%%") +end + +function lib.Search(item) + -- Can't do anything without Enchantrix + if not resources.isEnchantrixLoaded then + return false, "Enchantrix not detected" + end + + local itemID = item[Const.ITEMID] + + local bidprice, buyprice = item[Const.PRICE], item[Const.BUYOUT] + local maxprice = get("enchantmats.maxprice.enable") and get("enchantmats.maxprice") + if buyprice <= 0 or not get("enchantmats.allow.buy") or (maxprice and buyprice > maxprice) then + buyprice = nil + end + if not get("enchantmats.allow.bid") or (maxprice and bidprice > maxprice) then + bidprice = nil + end + if not (bidprice or buyprice) then + return false, "Does not meet bid/buy requirements" + end + + local market + if validReagents[itemID] then + -- item itself is a reagent; just use item's value + market = resources.GetPrice(get("enchantmats.model"), itemID) + if not market then + return false, "No price for item" + end + + -- be safe and handle nil results + local adjustment = get("enchantmats.PriceAdjust."..itemID) or 0 + market = (market * item[Const.COUNT]) * adjustment / 100 + else -- it's not a reagent, figure out what it DEs into + local itemQuality = item[Const.QUALITY] + -- All disenchantable items are "uncommon" quality or higher + -- so bail on items that are white or gray + if itemQuality <= 1 then + return false, "Item quality too low" + end + + local minskill, maxskill + if get("enchantmats.level.custom") then + minskill = get("enchantmats.level.min") + maxskill = get("enchantmats.level.max") + else + minskill = 0 + maxskill = Enchantrix.Util.GetUserEnchantingSkill() + end + + local skillneeded = Enchantrix.Util.DisenchantSkillRequiredForItemLevel(item[Const.ILEVEL], itemQuality) + if (skillneeded < minskill) or (skillneeded > maxskill) then + return false, "Skill not high enough to Disenchant" + end + + local data = Enchantrix.Storage.GetItemDisenchants(itemID) + if not data then -- Give up if it doesn't disenchant to anything + return false, "Item not Disenchantable" + end + + local total = data.total + + if total and total[1] > 0 then + market = 0 + local totalNumber, totalQuantity = unpack(total) + local model = get("enchantmats.model") + local GetPrice = resources.lookupPriceModel[model] + for result, resData in pairs(data) do + if result ~= "total" then + local resNumber, resQuantity = unpack(resData) + local price = GetPrice(model, result) + price = (price or 0) * resQuantity / totalNumber + + -- be safe and handle nil results + local adjustment = get("enchantmats.PriceAdjust."..result) or 0 + market = market + price * adjustment / 100 + end + end + end + + end + if not market or market <= 0 then + return false, "No Price Found" + end + + if buyprice and buyprice <= market then + return "buy", market + elseif bidprice and bidprice <= market then + return "bid", market + end + return false, "Not enough profit" +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-SearchUI/SearcherEnchantMats.lua $", "$Rev: 4840 $") diff --git a/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherGeneral.lua b/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherGeneral.lua new file mode 100644 index 0000000..5c5d66a --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherGeneral.lua @@ -0,0 +1,329 @@ +--[[ + Auctioneer - Search UI - Searcher General + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: SearcherGeneral.lua 4446 2009-09-13 09:33:34Z brykrys $ + URL: http://auctioneeraddon.com/ + + This is a plugin module for the SearchUI that assists in searching by refined paramaters + + 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 +--]] +-- Create a new instance of our lib with our parent +local lib, parent, private = AucSearchUI.NewSearcher("General") +if not lib then return end +local print,decode,_,_,replicate,empty,_,_,_,debugPrint,fill = AucAdvanced.GetModuleLocals() +local get,set,default,Const = AucSearchUI.GetSearchLocals() +lib.tabname = "General" + +function private.getTypes() + if not private.typetable then + private.typetable = {GetAuctionItemClasses()} + table.insert(private.typetable,1, "All") + end + return private.typetable +end + +function private.getSubTypes() + local subtypetable, typenumber + local typename = get("general.type") + local typetable = private.getTypes() + if typename ~= "All" then + for i, j in pairs(typetable) do + if j == typename then + typenumber = i + break + end + end + end + if typenumber then + subtypetable = {GetAuctionItemSubClasses(typenumber-1)}-- subtract 1 because 1 is the "All" category + table.insert(subtypetable, 1, "All") + else + subtypetable = {[1]="All"} + end + return subtypetable +end + +function private.getQuality() + return { + {-1, "All"}, + {0, "Poor"}, + {1, "Common"}, + {2, "Uncommon"}, + {3, "Rare"}, + {4, "Epic"}, + {5, "Legendary"}, + {6, "Artifact"}, + } +end + +function private.getTimeLeft() + return { + {0, "Any"}, + {1, "less than 30 min"}, + {2, "2 hours"}, + {3, "12 hours"}, + {4, "48 hours"}, + } +end + +-- Set our defaults +default("general.name", "") +default("general.name.exact", false) +default("general.name.regexp", false) +default("general.name.invert", false) +default("general.type", "All") +default("general.subtype", "All") +default("general.quality", -1) +default("general.timeleft", 0) +default("general.ilevel.min", 0) +default("general.ilevel.max", 300) +default("general.ulevel.min", 0) +default("general.ulevel.max", 80) +default("general.seller", "") +default("general.seller.exact", false) +default("general.seller.regexp", false) +default("general.seller.invert", false) +default("general.minbid", 0) +default("general.minbuy", 0) +default("general.maxbid", 999999999) +default("general.maxbuy", 999999999) + +-- This function is automatically called when we need to create our search generals +function lib:MakeGuiConfig(gui) + -- Get our tab and populate it with our controls + local id = gui:AddTab(lib.tabname, "Searchers") + + -- Add the help + gui:AddSearcher("General", "Search for items by general properties such as name, level etc", 100) + gui:AddHelp(id, "general searcher", + "What does this searcher do?", + "This searcher provides the ability to search for specific items that are in the scan database by name, level, type, subtype, seller, price, timeleft and other similar generals.") + + gui:MakeScrollable(id) + gui:AddControl(id, "Header", 0, "Search criteria") + + local last = gui:GetLast(id) + gui:SetControlWidth(0.35) + gui:AddControl(id, "Text", 0, 1, "general.name", "Item name") + local cont = gui:GetLast(id) + gui:SetLast(id, last) + gui:AddControl(id, "Checkbox", 0.11, 0, "general.name.exact", "Exact") + gui:SetLast(id, last) + gui:AddControl(id, "Checkbox", 0.21, 0, "general.name.regexp", "Regexp") + gui:SetLast(id, last) + gui:AddControl(id, "Checkbox", 0.35, 0, "general.name.invert", "Invert") + + gui:SetLast(id, cont) + last = cont + + gui:AddControl(id, "Note", 0.0, 1, 100, 14, "Type:") + gui:AddControl(id, "Selectbox", 0.0, 1, private.getTypes, "general.type", "ItemType") + gui:SetLast(id, last) + gui:AddControl(id, "Note", 0.3, 1, 100, 14, "SubType:") + gui:AddControl(id, "Selectbox", 0.3, 1, private.getSubTypes, "general.subtype", "ItemSubType") + gui:SetLast(id, last) + gui:AddControl(id, "Note", 0.7, 1, 100, 14, "TimeLeft:") + gui:AddControl(id, "Selectbox", 0.7, 1, private.getTimeLeft(), "general.timeleft", "TimeLeft") + + gui:AddControl(id, "Note", 0.0, 1, 100, 14, "Quality:") + gui:AddControl(id, "Selectbox", 0.0, 1, private.getQuality(), "general.quality", "ItemQuality") + + last = gui:GetLast(id) + gui:SetControlWidth(0.37) + gui:AddControl(id, "NumeriSlider", 0, 1, "general.ilevel.min", 0, 300, 1, "Min item level") + gui:SetControlWidth(0.37) + gui:AddControl(id, "NumeriSlider", 0, 1, "general.ilevel.max", 0, 300, 1, "Max item level") + cont = gui:GetLast(id) + + gui:SetLast(id, last) + gui:SetControlWidth(0.17) + gui:AddControl(id, "NumeriSlider", 0.6, 0, "general.ulevel.min", 0, 80, 1, "Min user level") + gui:SetControlWidth(0.17) + gui:AddControl(id, "NumeriSlider", 0.6, 0, "general.ulevel.max", 0, 80, 1, "Max user level") + + gui:SetLast(id, cont) + + last = gui:GetLast(id) + gui:SetControlWidth(0.35) + gui:AddControl(id, "Text", 0, 1, "general.seller", "Seller name") + cont = gui:GetLast(id) + gui:SetLast(id, last) + gui:AddControl(id, "Checkbox", 0.13, 0, "general.seller.exact", "Exact") + gui:SetLast(id, last) + gui:AddControl(id, "Checkbox", 0.23, 0, "general.seller.regexp", "Regexp") + gui:SetLast(id, last) + gui:AddControl(id, "Checkbox", 0.37, 0, "general.seller.invert", "Invert") + + gui:SetLast(id, cont) + gui:AddControl(id, "MoneyFramePinned", 0, 1, "general.minbid", 0, 999999999, "Minimum Bid") + gui:SetLast(id, cont) + gui:AddControl(id, "MoneyFramePinned", 0.5, 1, "general.minbuy", 0, 999999999, "Minimum Buyout") + last = gui:GetLast(id) + gui:AddControl(id, "MoneyFramePinned", 0, 1, "general.maxbid", 0, 999999999, "Maximum Bid") + gui:SetLast(id, last) + gui:AddControl(id, "MoneyFramePinned", 0.5, 1, "general.maxbuy", 0, 999999999, "Maximum Buyout") +end + +function lib.Search(item) + private.debug = "" + if private.NameSearch("name", item[Const.NAME]) + and private.TypeSearch(item[Const.ITYPE], item[Const.ISUB]) + and private.TimeSearch(item[Const.TLEFT]) + and private.QualitySearch(item[Const.QUALITY]) + and private.LevelSearch("ilevel", item[Const.ILEVEL]) + and private.LevelSearch("ulevel", item[Const.ULEVEL]) + and private.NameSearch("seller", item[Const.SELLER]) + and private.PriceSearch("Bid", item[Const.PRICE]) + and private.PriceSearch("Buy", item[Const.BUYOUT]) then + return true + else + return false, private.debug + end +end + +function private.LevelSearch(levelType, itemLevel) + local min = get("general."..levelType..".min") + local max = get("general."..levelType..".max") + + if itemLevel < min then + private.debug = levelType.." too low" + return false + end + if itemLevel > max then + private.debug = levelType.." too high" + return false + end + return true +end + +function private.NameSearch(nametype,itemName) + local name = get("general."..nametype) + + -- If there's no name, then this matches + if not name or name == "" then + return true + end + + -- Lowercase the input + name = name:lower() + itemName = itemName:lower() + + -- Get the matching options + local nameExact = get("general."..nametype..".exact") + local nameRegexp = get("general."..nametype..".regexp") + local nameInvert = get("general."..nametype..".invert") + + -- if we need to make a non-regexp, exact match: + if nameExact and not nameRegexp then + -- If the name matches or we are inverted + if name == itemName and not nameInvert then + return true + elseif name ~= itemName and nameInvert then + return true + end + private.debug = nametype.." is not exact match" + return false + end + + local plain, text + text = name + if not nameRegexp then + plain = 1 + elseif nameExact then + text = "^"..name.."$" + end + + local matches = itemName:find(text, 1, plain) + if matches and not nameInvert then + return true + elseif not matches and nameInvert then + return true + end + private.debug = nametype.." does not match critia" + return false +end + +function private.TypeSearch(itype, isubtype) + local searchtype = get("general.type") + if searchtype == "All" then + return true + elseif searchtype == itype then + local searchsubtype = get("general.subtype") + if searchsubtype == "All" then + return true + elseif searchsubtype == isubtype then + return true + else + private.debug = "Wrong Subtype" + return false + end + else + private.debug = "Wrong Type" + return false + end +end + +function private.TimeSearch(iTleft) + local tleft = get("general.timeleft") + if tleft == 0 then + return true + elseif tleft == iTleft then + return true + else + private.debug = "Time left wrong" + return false + end +end + +function private.QualitySearch(iqual) + local quality = get("general.quality") + if quality == -1 then + return true + elseif quality == iqual then + return true + else + private.debug = "Wrong Quality" + return false + end +end + +function private.PriceSearch(buybid, price) + local minprice, maxprice + if buybid == "Bid" then + minprice = get("general.minbid") + maxprice = get("general.maxbid") + else + minprice = get("general.minbuy") + maxprice = get("general.maxbuy") + end + if (price <= maxprice) and (price >= minprice) then + return true + elseif price < minprice then + private.debug = buybid.." price too low" + else + private.debug = buybid.." price too high" + end + return false +end +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-SearchUI/SearcherGeneral.lua $", "$Rev: 4446 $") diff --git a/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherMilling.lua b/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherMilling.lua new file mode 100644 index 0000000..6a9532c --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherMilling.lua @@ -0,0 +1,205 @@ +--[[ + Auctioneer - Search UI - Searcher Milling + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: SearcherMilling.lua 4840 2010-08-04 21:44:00Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This is a plugin module for the SearchUI that assists in searching by refined paramaters + + 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 +--]] +-- Create a new instance of our lib with our parent +local lib, parent, private = AucSearchUI.NewSearcher("Milling") +if not lib then return end +--local print,decode,_,_,replicate,empty,_,_,_,debugPrint,fill = AucAdvanced.GetModuleLocals() +local get, set, default, Const, resources = parent.GetSearchLocals() + +lib.tabname = "Milling" +-- Set our defaults +default("milling.profit.min", 1) +default("milling.profit.pct", 50) +default("milling.level.custom", false) +default("milling.level.min", 0) +default("milling.level.max", 450) +default("milling.adjust.brokerage", true) +default("milling.adjust.deposit", true) +default("milling.adjust.deplength", 48) +default("milling.adjust.listings", 3) +default("milling.allow.bid", true) +default("milling.allow.buy", true) +default("milling.maxprice", 10000000) +default("milling.maxprice.enable", false) +default("milling.model", "Enchantrix") + +function private.doValidation() + if not resources.isEnchantrixLoaded then + message("Milling Searcher Warning!\nEnchantrix not detected\nThis searcher will not function until Enchantrix is loaded") + elseif not resources.isValidPriceModel(get("milling.model")) then + message("Milling Searcher Warning!\nCurrent price model setting ("..get("milling.model")..") is not valid. Select a new price model") + else + private.doValidation = nil + end +end + +-- This function is automatically called from AucSearchUI.NotifyCallbacks +function lib.Processor(event, subevent) + if event == "selecttab" then + if subevent == lib.tabname and private.doValidation then + private.doValidation() + end + end +end + +lib.Processors = {} +function lib.Processors.selecttab(event, subevent) + if subevent == lib.tabname and private.doValidation then + private.doValidation() + end +end + + +-- This function is automatically called when we need to create our search parameters +function lib:MakeGuiConfig(gui) + -- Get our tab and populate it with our controls + local id = gui:AddTab(lib.tabname, "Searchers") + + -- Add the help + gui:AddSearcher("Milling", "Search for items which can be milled for profit", 100) + gui:AddHelp(id, "milling searcher", + "What does this searcher do?", + "This searcher provides the ability to search for herbs which will mill into pigments that on average will have a greater value than the purchase price of the original herbs.") + + gui:AddControl(id, "Header", 0, "Milling search criteria") + + local last = gui:GetLast(id) + + gui:AddControl(id, "MoneyFramePinned", 0, 1, "milling.profit.min", 1, 99999999, "Minimum Profit") + gui:AddControl(id, "Slider", 0, 1, "milling.profit.pct", 1, 100, .5, "Min Discount: %0.01f%%") + gui:AddControl(id, "Checkbox", 0, 1, "milling.level.custom", "Use custom levels") + gui:AddControl(id, "Slider", 0, 2, "milling.level.min", 0, 450, 25, "Minimum skill: %s") + gui:AddControl(id, "Slider", 0, 2, "milling.level.max", 25, 450, 25, "Maximum skill: %s") + gui:AddControl(id, "Subhead", 0, "Note:") + gui:AddControl(id, "Note", 0, 1, 290, 30, "The \"Pct\" Column is \% of Milling Value") + + gui:SetLast(id, last) + gui:AddControl(id, "Checkbox", 0.42, 1, "milling.allow.bid", "Allow Bids") + gui:SetLast(id, last) + gui:AddControl(id, "Checkbox", 0.56, 1, "milling.allow.buy", "Allow Buyouts") + gui:AddControl(id, "Checkbox", 0.42, 1, "milling.maxprice.enable", "Enable individual maximum price:") + gui:AddTip(id, "Limit the maximum amount you want to spend with the Milling searcher") + gui:AddControl(id, "MoneyFramePinned", 0.42, 2, "milling.maxprice", 1, 99999999, "Maximum Price for Milling") + + gui:AddControl(id, "Subhead", 0.42, "Price Valuation Method:") + gui:AddControl(id, "Selectbox", 0.42, 1, resources.selectorPriceModelsEnx, "milling.model") + gui:AddTip(id, "The pricing model that is used to work out the calculated value of items at the Auction House.") + + gui:AddControl(id, "Subhead", 0.42, "Fees Adjustment") + gui:AddControl(id, "Checkbox", 0.42, 1, "milling.adjust.brokerage", "Subtract auction fees") + gui:AddControl(id, "Checkbox", 0.42, 1, "milling.adjust.deposit", "Subtract deposit") + gui:AddControl(id, "Selectbox", 0.42, 1, resources.selectorAuctionLength, "milling.adjust.deplength") + gui:AddControl(id, "Slider", 0.42, 1, "milling.adjust.listings", 1, 10, .1, "Ave relistings: %0.1fx") +end + +function lib.Search(item) + if not resources.isEnchantrixLoaded then + return false, "Enchantrix not detected" + end + if item[Const.QUALITY] ~= 1 then -- All millable herbs are "Common" quality + return false, "Item not millable" + end + + local bidprice, buyprice = item[Const.PRICE], item[Const.BUYOUT] + local maxprice = get("milling.maxprice.enable") and get("milling.maxprice") + if buyprice <= 0 or not get("milling.allow.buy") or (maxprice and buyprice > maxprice) then + buyprice = nil + end + if not get("milling.allow.bid") or (maxprice and bidprice > maxprice) then + bidprice = nil + end + if not (bidprice or buyprice) then + return false, "Does not meet bid/buy requirements" + end + + local itemID = item[Const.ITEMID] + + -- Give up if it doesn't mill to anything + local pigments = Enchantrix.Storage.GetItemMilling(itemID) + if not pigments then + return false, "Item not millable" + end + + local minskill, maxskill + if get("milling.level.custom") then + minskill = get("milling.level.min") + maxskill = get("milling.level.max") + else + minskill = 0 + maxskill = Enchantrix.Util.GetUserInscriptionSkill() + end + local skillneeded = Enchantrix.Util.InscriptionSkillRequiredForItem(itemID) + if (skillneeded < minskill) or (skillneeded > maxskill) then + return false, "Skill not high enough to mill" + end + + local market, deposit = 0, 0 + + -- prep locals to speed up access inside the loop + local depositAucLength, depositRelistTimes, depositFaction + local includeDeposit = get("milling.adjust.deposit") + if includeDeposit then + depositAucLength = get("milling.adjust.deplength") + depositRelistTimes = get("milling.adjust.listings") + depositFaction = resources.faction + end + local model = get("milling.model") + local GetPrice = resources.lookupPriceModel[model] + + for result, yield in pairs(pigments) do + local price = GetPrice(model, result) or 0 + market = market + price * yield + + -- calculate deposit for each result + if includeDeposit then + local aadvdepcost = GetDepositCost(result, depositAucLength, depositFaction, nil) or 0 + deposit = deposit + aadvdepcost * yield * depositRelistTimes + end + end + + -- Adjust for fees and costs + if get("milling.adjust.brokerage") then + market = market * resources.CutAdjust + end + market = market - deposit + + -- Adjust for stack size and note that yield is per stack of 5 + market = market* item[Const.COUNT] / 5 + local value = min (market*(100-get("milling.profit.pct"))/100, market-get("milling.profit.min")) + if buyprice and buyprice <= value then + return "buy", market + elseif bidprice and bidprice <= value then + return "bid", market + end + return false, "Not enough profit" +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-SearchUI/SearcherMilling.lua $", "$Rev: 4840 $") diff --git a/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherProspect.lua b/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherProspect.lua new file mode 100644 index 0000000..3f04d44 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherProspect.lua @@ -0,0 +1,206 @@ +--[[ + Auctioneer - Search UI - Searcher Prospect + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: SearcherProspect.lua 4880 2010-09-15 20:02:11Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This is a plugin module for the SearchUI that assists in searching by refined paramaters + + 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 +--]] +-- Create a new instance of our lib with our parent +local lib, parent, private = AucSearchUI.NewSearcher("Prospect") +if not lib then return end +--local print,decode,_,_,replicate,empty,_,_,_,debugPrint,fill = AucAdvanced.GetModuleLocals() +local get, set, default, Const, resources = parent.GetSearchLocals() + +lib.tabname = "Prospect" +-- Set our defaults +default("prospect.profit.min", 1) +default("prospect.profit.pct", 50) +default("prospect.level.custom", false) +default("prospect.level.min", 0) +default("prospect.level.max", 450) +default("prospect.adjust.brokerage", true) +default("prospect.adjust.deposit", true) +default("prospect.adjust.deplength", 48) +default("prospect.adjust.listings", 3) +default("prospect.allow.bid", true) +default("prospect.allow.buy", true) +default("prospect.maxprice", 10000000) +default("prospect.maxprice.enable", false) +default("prospect.model", "Enchantrix") + +function private.doValidation() + if not resources.isEnchantrixLoaded then + message("Prospect Searcher Warning!\nEnchantrix not detected\nThis searcher will not function until Enchantrix is loaded") + elseif not resources.isValidPriceModel(get("prospect.model")) then + message("Prospect Searcher Warning!\nCurrent price model setting ("..get("prospect.model")..") is not valid. Select a new price model") + else + private.doValidation = nil + end +end + +-- This function is automatically called from AucSearchUI.NotifyCallbacks +function lib.Processor(event, subevent) + if event == "selecttab" then + if subevent == lib.tabname and private.doValidation then + private.doValidation() + end + end +end + +lib.Processors = {} +function lib.Processors.selecttab(event, subevent) + if subevent == lib.tabname and private.doValidation then + private.doValidation() + end +end + + +-- This function is automatically called when we need to create our search parameters +function lib:MakeGuiConfig(gui) + -- Get our tab and populate it with our controls + local id = gui:AddTab(lib.tabname, "Searchers") + + -- Add the help + gui:AddSearcher("Prospect", "Search for items which can be prospected for profit", 100) + gui:AddHelp(id, "prospect searcher", + "What does this searcher do?", + "This searcher provides the ability to search for ores which will prospect into gems that on average will have a greater value than the purchase price of the original ore.") + + gui:AddControl(id, "Header", 0, "Prospect search criteria") + + local last = gui:GetLast(id) + + gui:AddControl(id, "MoneyFramePinned", 0, 1, "prospect.profit.min", 1, 99999999, "Minimum Profit") + gui:AddControl(id, "Slider", 0, 1, "prospect.profit.pct", 1, 100, .5, "Min Discount: %0.01f%%") + gui:AddControl(id, "Checkbox", 0, 1, "prospect.level.custom", "Use custom levels") + gui:AddControl(id, "Slider", 0, 2, "prospect.level.min", 0, 450, 25, "Minimum skill: %s") + gui:AddControl(id, "Slider", 0, 2, "prospect.level.max", 25, 450, 25, "Maximum skill: %s") + gui:AddControl(id, "Subhead", 0, "Note:") + gui:AddControl(id, "Note", 0, 1, 290, 30, "The \"Pct\" Column is \% of Prospect Value") + + gui:SetLast(id, last) + gui:AddControl(id, "Checkbox", 0.42, 1, "prospect.allow.bid", "Allow Bids") + gui:SetLast(id, last) + gui:AddControl(id, "Checkbox", 0.56, 1, "prospect.allow.buy", "Allow Buyouts") + gui:AddControl(id, "Checkbox", 0.42, 1, "prospect.maxprice.enable", "Enable individual maximum price:") + gui:AddTip(id, "Limit the maximum amount you want to spend with the Prospect searcher") + gui:AddControl(id, "MoneyFramePinned", 0.42, 2, "prospect.maxprice", 1, 99999999, "Maximum Price for Prospect") + + gui:AddControl(id, "Subhead", 0.42, "Price Valuation Method:") + gui:AddControl(id, "Selectbox", 0.42, 1, resources.selectorPriceModelsEnx, "milling.model") + gui:AddTip(id, "The pricing model that is used to work out the calculated value of items at the Auction House.") + + gui:AddControl(id, "Subhead", 0.42, "Fees Adjustment") + gui:AddControl(id, "Checkbox", 0.42, 1, "prospect.adjust.brokerage", "Subtract auction fees") + gui:AddControl(id, "Checkbox", 0.42, 1, "prospect.adjust.deposit", "Subtract deposit") + gui:AddControl(id, "Selectbox", 0.42, 1, resources.selectorAuctionLength, "prospect.adjust.deplength") + gui:AddControl(id, "Slider", 0.42, 1, "prospect.adjust.listings", 1, 10, .1, "Ave relistings: %0.1fx") +end + +function lib.Search(item) + if not resources.isEnchantrixLoaded then + return false, "Enchantrix not detected" + end + local quality = item[Const.QUALITY] + if quality < 1 or quality > 2 then -- Ores are common or uncommon quality + return false, "Wrong quality for prospecting" + end + + local bidprice, buyprice = item[Const.PRICE], item[Const.BUYOUT] + local maxprice = get("prospect.maxprice.enable") and get("prospect.maxprice") + if buyprice <= 0 or not get("prospect.allow.buy") or (maxprice and buyprice > maxprice) then + buyprice = nil + end + if not get("prospect.allow.bid") or (maxprice and bidprice > maxprice) then + bidprice = nil + end + if not (bidprice or buyprice) then + return false, "Does not meet bid/buy requirements" + end + + local itemID = item[Const.ITEMID] + + -- Give up if it doesn't prospect to anything + local prospects = Enchantrix.Storage.GetItemProspects(itemID) + if not prospects then + return false, "Item not prospectable" + end + + local minskill, maxskill + if get("prospect.level.custom") then + minskill = get("prospect.level.min") + maxskill = get("prospect.level.max") + else + minskill = 0 + maxskill = Enchantrix.Util.GetUserJewelCraftingSkill() + end + local skillneeded = Enchantrix.Util.JewelCraftSkillRequiredForItem(itemID) + if (skillneeded < minskill) or (skillneeded > maxskill) then + return false, "Skill not high enough to prospect" + end + + local market, deposit = 0, 0 + + -- prep locals to speed up access inside the loop + local depositAucLength, depositRelistTimes, depositFaction + local includeDeposit = get("prospect.adjust.deposit") + if includeDeposit then + depositAucLength = get("prospect.adjust.deplength") + depositRelistTimes = get("prospect.adjust.listings") + depositFaction = resources.faction + end + local model = get("prospect.model") + local GetPrice = resources.lookupPriceModel[model] + + for result, yield in pairs(prospects) do + local price = GetPrice(model, result) or 0 + market = market + price * yield + + -- calculate deposit for each result + if includeDeposit then + local aadvdepcost = GetDepositCost(result, depositAucLength, depositFaction, nil) or 0 + deposit = deposit + aadvdepcost * yield * depositRelistTimes + end + end + + -- Adjust for fees and costs + if get("prospect.adjust.brokerage") then + market = market * resources.CutAdjust + end + market = market - deposit + + -- Adjust for stack size and note that yield is per stack of 5 + market = market* item[Const.COUNT] / 5 + local value = min (market*(100-get("prospect.profit.pct"))/100, market-get("prospect.profit.min")) + if buyprice and buyprice <= value then + return "buy", market + elseif bidprice and bidprice <= value then + return "bid", market + end + return false, "Not enough profit" +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-SearchUI/SearcherProspect.lua $", "$Rev: 4880 $") diff --git a/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherResale.lua b/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherResale.lua new file mode 100644 index 0000000..26af87a --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherResale.lua @@ -0,0 +1,169 @@ +--[[ + Auctioneer - Search UI - Searcher Resale + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: SearcherResale.lua 4840 2010-08-04 21:44:00Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This is a plugin module for the SearchUI that assists in searching by refined paramaters + + 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 +--]] +-- Create a new instance of our lib with our parent +local lib, parent, private = AucSearchUI.NewSearcher("Resale") +if not lib then return end +--local print,decode,_,_,replicate,empty,_,_,_,debugPrint,fill = AucAdvanced.GetModuleLocals() -- commenting out as unused, re-enable if needed +local get, set, default, Const, resources = parent.GetSearchLocals() +lib.tabname = "Resale" + +-- Set our defaults +default("resale.profit.min", 1) +default("resale.profit.pct", 50) +default("resale.seen.check", false) +default("resale.seen.min", 10) +default("resale.adjust.brokerage", true) +default("resale.adjust.deposit", true) +default("resale.adjust.deplength", 48) +default("resale.adjust.listings", 3) +default("resale.allow.bid", true) +default("resale.allow.buy", true) +default("resale.maxprice", 10000000) +default("resale.maxprice.enable", false) +default("resale.model", "market") + +-- This function is automatically called from AucSearchUI.NotifyCallbacks +private.validationRequired = true +function lib.Processor(event, subevent) + if event == "selecttab" then + if subevent == lib.tabname and private.validationRequired then + if not resources.isValidPriceModel(get("resale.model")) then + message("Resale Searcher Warning!\nCurrent price model setting ("..get("resale.model")..") is not valid. Select a new price model") + else + private.validationRequired = nil + end + end + end +end + +lib.Processors = {} +function lib.Processors.selecttab(event, subevent) + if subevent == lib.tabname and private.validationRequired then + if not resources.isValidPriceModel(get("resale.model")) then + message("Resale Searcher Warning!\nCurrent price model setting ("..get("resale.model")..") is not valid. Select a new price model") + else + private.validationRequired = nil + end + end +end + +-- This function is automatically called when we need to create our search parameters +function lib:MakeGuiConfig(gui) + -- Get our tab and populate it with our controls + local id = gui:AddTab(lib.tabname, "Searchers") + + -- Add the help + gui:AddSearcher("Resale", "Search for undervalued items which can be directly resold for profit", 100) + gui:AddHelp(id, "resale searcher", + "What does this searcher do?", + "This searcher provides the ability to search for items that are being sold under market value, and which you can resell for profit after the fees and deposits are accounted for.") + + gui:AddControl(id, "Header", 0, "Resale search criteria") + + local last = gui:GetLast(id) + + gui:AddControl(id, "MoneyFramePinned", 0, 1, "resale.profit.min", 1, 99999999, "Minimum Profit") + gui:AddControl(id, "Slider", 0, 1, "resale.profit.pct", 1, 100, .5, "Min Discount: %0.01f%%") + gui:AddControl(id, "Checkbox", 0, 1, "resale.seen.check", "Check Seen count") + gui:AddControl(id, "Slider", 0, 2, "resale.seen.min", 1, 100, 1, "Min seen count: %s") + + gui:SetLast(id, last) + gui:AddControl(id, "Checkbox", 0.42, 1, "resale.allow.bid", "Allow Bids") + gui:SetLast(id, last) + gui:AddControl(id, "Checkbox", 0.56, 1, "resale.allow.buy", "Allow Buyouts") + gui:AddControl(id, "Checkbox", 0.42, 1, "resale.maxprice.enable", "Enable individual maximum price:") + gui:AddTip(id, "Limit the maximum amount you want to spend with the Resale searcher") + gui:AddControl(id, "MoneyFramePinned", 0.42, 2, "resale.maxprice", 1, 99999999, "Maximum Price for Resale") + + gui:AddControl(id, "Subhead", 0.42, "Price Valuation Method:") + gui:AddControl(id, "Selectbox", 0.42, 1, resources.selectorPriceModels, "resale.model") + gui:AddTip(id, "The pricing model that is used to work out the calculated value of items at the Auction House.") + + gui:AddControl(id, "Subhead", 0.42, "Fees Adjustment") + gui:AddControl(id, "Checkbox", 0.42, 1, "resale.adjust.brokerage", "Subtract auction fees") + gui:AddControl(id, "Checkbox", 0.42, 1, "resale.adjust.deposit", "Subtract deposit") + gui:AddControl(id, "Selectbox", 0.42, 1, resources.selectorAuctionLength, "resale.adjust.deplength") + gui:AddControl(id, "Slider", 0.42, 1, "resale.adjust.listings", 1, 10, .1, "Ave relistings: %0.1fx") +end + +function lib.Search(item) + local link = item[Const.LINK] + if not link then + return false, "No link" + end + + local bidprice, buyprice = item[Const.PRICE], item[Const.BUYOUT] + local maxprice = get("resale.maxprice.enable") and get("resale.maxprice") + if buyprice <= 0 or not get("resale.allow.buy") or (maxprice and buyprice > maxprice) then + buyprice = nil + end + if not get("resale.allow.bid") or (maxprice and bidprice > maxprice) then + bidprice = nil + end + if not (bidprice or buyprice) then + return false, "Does not meet bid/buy requirements" + end + + local model = get("resale.model") + local market, seen, curModel = resources.lookupPriceModel[model](model, link) + if not market then + return false, "No market price" + end + local count = item[Const.COUNT] + market = market * count + + if (get("resale.seen.check")) and curModel ~= "fixed" then + if ((not seen) or (seen < get("resale.seen.min"))) then + return false, "Seen count too low" + end + end + + --adjust for brokerage/deposit costs + if get("resale.adjust.brokerage") then + market = market * resources.CutAdjust + end + if get("resale.adjust.deposit") then + local amount = GetDepositCost(link, get("resale.adjust.deplength"), resources.faction, count) + if amount then + market = market - amount * get("resale.adjust.listings") + end + end + + local value = min(market*(100-get("resale.profit.pct"))/100, market-get("resale.profit.min")) + if buyprice and buyprice <= value then + return "buy", market + elseif bidprice and bidprice <= value then + return "bid", market + end + return false, "Not enough profit" +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-SearchUI/SearcherResale.lua $", "$Rev: 4840 $") diff --git a/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherSnatch.lua b/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherSnatch.lua new file mode 100644 index 0000000..abd2663 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherSnatch.lua @@ -0,0 +1,618 @@ +--[[ + Auctioneer - Search UI - Searcher Snatch + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: SearcherSnatch.lua 4840 2010-08-04 21:44:00Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This is a plugin module for the SearchUI that assists in searching by refined paramaters + + 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 +--]] + +-- Create a new instance of our lib with our parent +local lib, parent, private = AucSearchUI.NewSearcher("Snatch") +if not lib then return end +local print,decode,_,_,replicate,empty,_,_,_,debugPrint,fill, _TRANS = AucAdvanced.GetModuleLocals() +local get, set, default, Const, resources = parent.GetSearchLocals() +lib.tabname = "Snatch" +lib.Private = private + +default("snatch.allow.bid", true) +default("snatch.allow.buy", true) +default("snatch.maxprice", 10000000) +default("snatch.maxprice.enable", false) +default("snatch.allow.beginerTooltips", true) +default("snatch.price.model", "market") +private.snatchList = {} -- dummy table + +private.workingItemLink = nil +local frame + +--[[ +The slash command will take the following input + 1g 2s 3c where g s c are defined in any combination +or + 1gold 2 Silver 3c where g s c are defined in any text form +or + 10203 where the price is given in total copper]] +local tooltip = LibStub("nTipHelper:1") +function lib.SlashCommand(cmd) + local itemlink, GSC, price, extra = cmd:match("(|c%x+|H.+|h%[.*%]|h|r).-((%d+)(.*))") --split command into link and price. check if price is defined as total copper ot as a g s c value + --parse for gsc if more data than just a simple value + if extra and extra ~= "" and price then + local g,s,c + g = GSC:lower():match("(%d+)%s-g") or 0 + s = GSC:lower():match("(%d+)%s-s") or 0 + c = GSC:lower():match("(%d+)%s-c") or 0 + price = (g*COPPER_PER_GOLD + s*COPPER_PER_SILVER + c) --sum gsc to total copper value + end + price = tonumber(price) + + --parse for % command % or percent + local pct + if extra and extra ~= "" and price == 0 then + pct = GSC:lower():match("(%d+)%s-%%") or GSC:lower():match("(%d+)%s-p") + end + pct = tonumber(pct) + + --pass to snatch + if itemlink and price and price > 0 then + lib.AddSnatch(itemlink, price) + price = tooltip:Coins(price)--convert to fancy gsc icon format + print("Added snatch for", itemlink, "at", price, "or lower") + elseif itemlink and pct and pct > 0 then + lib.AddSnatch(itemlink, nil, pct) + print("Added snatch for", itemlink, "at", pct, "% of market price or lower") + else + print("FORMAT: or Xg Xs Xc or X% or Xp") + end +end + +-- This function is automatically called when we need to create our search parameters +function lib:MakeGuiConfig(gui) + lib.MakeGuiConfig = nil + -- Get our tab and populate it with our controls + local id = gui:AddTab(lib.tabname, "Searchers") + gui:MakeScrollable(id) + + -- Add the help + gui:AddSearcher("Snatch", "Search for items which you want to buy when they are available for less than a given price", 100) + gui:AddHelp(id, "snatch searcher", + "What does this searcher do?", + "This searcher provides the ability to snap up items which meet your fixed price constraints. It is useful whenever you say \"I always want to buy this when it is cheaper than X per item\".") + + --we add a single invisible element with height attribute to anchor the normal gui + gui:AddControl(id, "Note", 0, 1, nil, 110, " ") + + local SelectBox = LibStub:GetLibrary("SelectBox") + local ScrollSheet = LibStub:GetLibrary("ScrollSheet") + --Add Drag slot / Item icon + frame = gui.tabs[id].content + private.frame = frame + + --Create the snatch list results frame + frame.snatchlist = CreateFrame("Frame", nil, frame) + frame.snatchlist:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + edgeFile = "Interface/Tooltips/UI-Tooltip-Border", + tile = true, tileSize = 32, edgeSize = 16, + insets = { left = 5, right = 5, top = 5, bottom = 5 } + }) + + frame.snatchlist:SetBackdropColor(0, 0, 0.0, 0.5) + frame.snatchlist:SetPoint("TOPLEFT", frame, "TOPLEFT", 275, 2) --Anchor to left side. The GUI is resized from the right when we embed in AH + frame.snatchlist:SetPoint("BOTTOM", frame, "BOTTOM", 0, -100) + frame.snatchlist:SetWidth(380) + + frame.slot = frame:CreateTexture(nil, "ARTWORK") + frame.slot:SetPoint("TOPLEFT", frame, "TOPLEFT", 8, 0) + frame.slot:SetWidth(45) + frame.slot:SetHeight(45) + frame.slot:SetTexCoord(0.17, 0.83, 0.17, 0.83) + frame.slot:SetTexture("Interface\\Buttons\\UI-EmptySlot") + + --ICON box, used to drag item and display Icon + function frame.IconClicked() + local objtype, _, link = GetCursorInfo() + ClearCursor() + if objtype == "item" then + lib.SetWorkingItem(link) + end + end + frame.icon = CreateFrame("Button", nil, frame) + frame.icon:SetPoint("TOPLEFT", frame.slot, "TOPLEFT", 2, -2) + frame.icon:SetPoint("BOTTOMRIGHT", frame.slot, "BOTTOMRIGHT", -2, 2) + frame.icon:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square.blp") + frame.icon:SetScript("OnClick", frame.IconClicked) + frame.icon:SetScript("OnReceiveDrag", frame.IconClicked) + frame.icon:SetScript("OnEnter", function() --set mouseover tooltip + if private.workingItemLink then + GameTooltip:SetOwner(frame.icon, "ANCHOR_BOTTOMRIGHT") + GameTooltip:SetHyperlink(private.workingItemLink) + end + end) + frame.icon:SetScript("OnLeave", function() GameTooltip:Hide() end) + + frame.slot.help = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + frame.slot.help:SetPoint("LEFT", frame.slot, "RIGHT", 2, 0) + frame.slot.help:SetText(("Drop item into box")) --"Drop item into box to search." + frame.slot.help:SetWidth(220) + frame.slot.help:SetJustifyH("LEFT") + + frame.snatchlist.sheet = ScrollSheet:Create(frame.snatchlist, { + { "Snatching", "TOOLTIP", 170 }, + { "%", "NUMBER", 25 }, + { "Buy each", "COIN", 70}, + { "Valuation", "COIN", 70 }, + }) + + --Processor function for all scrollframe events for this frame + function frame.snatchlist.sheet.Processor(callback, self, button, column, row, order, curDir, ...) + if (callback == "ColumnOrder") then + set("snatch.columnorder", order) + elseif (callback == "ColumnWidthSet") then + private.OnResize(self, column, button:GetWidth() ) + elseif (callback == "ColumnWidthReset") then + private.onResize(self, column, nil) + elseif (callback == "OnEnterCell") then + private.OnEnterSnatch(button, row, column) + elseif (callback == "OnLeaveCell") then + GameTooltip:Hide() + elseif (callback == "OnClickCell") then + private.OnClickSnatch(button, row, column) + elseif (callback == "ColumnSort") then + set("snatch.columnsortcurDir", curDir) + set("snatch.columnsortcurSort", column) + end + end + + --If we have a saved order reapply + if get("snatch.columnorder") then + --print("saved order applied") + frame.snatchlist.sheet:SetOrder( get("snatch.columnorder") ) + end + --Apply last column sort used + if get("snatch.columnsortcurSort") then + frame.snatchlist.sheet.curSort = get("snatch.columnsortcurSort") or 1 + frame.snatchlist.sheet.curDir = get("snatch.columnsortcurDir") or 1 + frame.snatchlist.sheet:PerformSort() + end + + + -- "Price" and "Percent of Valuation" Input Frames + -- Making changes to one of these will affect the other (with some specific exceptions to avoid unwanted recursion) + frame.money = CreateFrame("Frame", "AucSearchUISearcherSnatchMoney", frame, "MoneyInputFrameTemplate") + frame.money.isMoneyFrame = true + frame.money:SetPoint("LEFT", frame.slot, "BOTTOM", -16, -10) + -- Note: never call MoneyInputFrame_SetCopper on frame.money; use private.setMoneyFrame instead (performs required additional checking) + function frame.money.onValueChangedFunc() + if frame.money.internal then + frame.money.internal = nil + else + -- money frame edited manually by user: "fixed price" mode. clear percent box + frame.pctBox:SetText("") + end + end + + frame.pctBox = CreateFrame("EditBox", nil, frame, "InputBoxTemplate") + frame.pctBox:SetPoint("TOP", frame.money, "BOTTOM", -70, 0) + frame.pctBox:SetAutoFocus(false) + frame.pctBox:SetNumeric(true) + frame.pctBox:SetMaxLetters(4) --no more than 4 digits + frame.pctBox:SetTextColor(1, 0, 0) -- red text + frame.pctBox:SetWidth(35) + frame.pctBox:SetHeight(32) + --Set money frame to % of market. Visual Only Calculated each session + frame.pctBox:SetScript("OnTextChanged", function() + local price, pct = 0, frame.pctBox:GetNumber() + if private.workingItemLink then + price = private.getPrice(private.workingItemLink) + end + --this stops us from clearing money when we just reset % after a new selection + if pct ~= 0 then + private.setMoneyFrame(price * pct/100) + frame.money:SetAlpha(.6) -- "fade" money frame (to indicate it is only an example) + else + frame.money:SetAlpha(1) + end + end) + + frame.pctBox.help = frame.pctBox:CreateFontString(nil, "OVERLAY", "GameFontNormal") + frame.pctBox.help:SetPoint("LEFT", frame.pctBox, "RIGHT", 0, 0) + frame.pctBox.help:SetWidth(130) + frame.pctBox.help:SetJustifyH("LEFT") + frame.pctBox.help:SetText("Buy as percent of selected statistic") + + + --Add Item to list button + frame.additem = CreateFrame("Button", nil, frame, "OptionsButtonTemplate") + frame.additem:SetPoint("LEFT", frame.money, "RIGHT", -10, 0) + frame.additem:SetText(('Add Item')) + frame.additem:SetScript("OnClick", function() + local copper = MoneyInputFrame_GetCopper(frame.money) + local percent = frame.pctBox:GetNumber() + lib.AddSnatch(private.workingItemLink, copper, percent) + end) + frame.additem:SetScript("OnEnter", function() lib.buttonTooltips( frame.additem, "Click to add current selection to the snatch list") end) + frame.additem:SetScript("OnLeave", function() GameTooltip:Hide() end) + + --Remove Item from list button + frame.removeitem = CreateFrame("Button", nil, frame, "OptionsButtonTemplate") + frame.removeitem:SetPoint("TOP", frame.additem, "BOTTOM", 0, -10) + frame.removeitem:SetText(('Remove Item')) + frame.removeitem:SetScript("OnClick", function() lib.RemoveSnatch(private.workingItemLink) end) + frame.removeitem:SetScript("OnEnter", function() lib.buttonTooltips( frame.removeitem, "Click to remove current selection from the snatch list") end) + frame.removeitem:SetScript("OnLeave", function() GameTooltip:Hide() end) + + --Reset snatch list + frame.resetList = CreateFrame("Button", nil, frame, "OptionsButtonTemplate") + frame.resetList:SetPoint("TOP", frame.snatchlist, "BOTTOM", 0, -15) + frame.resetList:SetText(("Clear List")) + frame.resetList:SetScript("OnClick", function() + if ( IsAltKeyDown() and IsShiftKeyDown() and IsControlKeyDown() )then + private.snatchList = {} + set("snatch.itemsList", private.snatchList) + private.refreshDisplay() + else + print("This will clear the snatch list permanently. To use hold ALT+CTRL+SHIFT while clicking this button") + end + end) + frame.resetList:SetScript("OnEnter", function() lib.buttonTooltips( frame.resetList, "Shift+Ctrl+Alt Click to remove all items from the snatch list") end) + frame.resetList:SetScript("OnLeave", function() GameTooltip:Hide() end) + + -- Normal GUI controls + -- Anchored to the hidden "Note" control we added earlier + gui:AddControl(id, "Subhead",0, "Snatch search settings:") + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + last = gui:GetLast(id) + gui:AddControl(id, "Checkbox", 0, 1, "snatch.allow.bid", "Allow Bids") + gui:AddTip(id, "Allow Snatch searcher to suggest bids") + gui:SetLast(id, last) + gui:AddControl(id, "Checkbox", 0, 11, "snatch.allow.buy", "Allow Buyouts") + gui:AddTip(id, "Allow Snatch searcher to suggest buyouts") + gui:AddControl(id, "Checkbox", 0, 1, "snatch.maxprice.enable", "Enable individual maximum price:") + gui:AddTip(id, "Limit the maximum amount you want to spend with the Snatch searcher") + gui:AddControl(id, "MoneyFramePinned", 0, 2, "snatch.maxprice", 1, 99999999, "Maximum Price for Snatch") + + gui:AddControl(id, "Subhead", 0, "Price Valuation Method:") + gui:AddControl(id, "Selectbox", 0, 1, resources.selectorPriceModels, "snatch.price.model") + gui:AddTip(id, "The pricing model that is used to work out the calculated value of items at the Auction House.") + + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + gui:AddControl(id, "Checkbox", 0, 1, "snatch.allow.beginerTooltips", "Display beginner popup help.") + gui:AddTip(id, "Display beginner tooltips.") + + gui:AddHelp(id, "what are commands", + 'How to use slash commands', +[[After SearchUI has been opened, you can use the /snatch slash command. +You can easily add items via +/snatch TotalValue in Copper +or +/snatch xG xS xC +if the command is accepted you should see a chat message confirming the item and price snatch will buy at.]] + ) + + private.refreshDisplay() + + + SLASH_SNATCH1 = "/snatch"; + SlashCmdList["SNATCH"] = lib.SlashCommand +end + +--Gets price for choosen item using current selected pricing model +function private.getPrice(link) + local model = get("snatch.price.model") + local price = resources.lookupPriceModel[model](model, link) + return price or 0 +end + +function private.setMoneyFrame(price) + frame.money.internal = true -- flag as internal so we don't clear the pctBox + if price then + MoneyInputFrame_SetCopper(frame.money, math.floor(price)) + else + MoneyInputFrame_ResetMoney(frame.money) + end +end + +function private.doValidation() + if not resources.isValidPriceModel(get("snatch.price.model")) then + message("Snatch Searcher Warning!\nCurrent price model setting ("..get("snatch.price.model")..") is not valid. Select a new price model") + else + private.doValidation = nil + end +end + +--Processor function +--this handles any notifications that SearchUI core needs to send us +function lib.Processor(event, subevent, setting) + if event == "config" then + if subevent == "loaded" or subevent == "reset" or subevent == "deleted" then + --saved search has changed, so reload our private ignorelist + private.snatchList = get("snatch.itemsList") + --if there was no saved snatchList, create an empty table + if not private.snatchList then + private.snatchList = {} + set("snatch.itemsList", private.snatchList) -- Caution: this causes a Processor("config", "changed", ...) event + end + private.refreshDisplay() + elseif subevent == "changed" and setting == "snatch.price.model" then -- this is the only "change" that requires a refresh + private.refreshDisplay() + end + elseif event == "selecttab" and subevent == lib.tabname then + if private.doValidation then + private.doValidation() + end + private.refreshDisplay() -- redraw whenever tab is selected + elseif event == "postscanupdate" then + private.refreshDisplay() -- redraw whenever valuations may have changed + end +end + +lib.Processors = {} +function lib.Processors.config(event, subevent, setting) + if subevent == "loaded" or subevent == "reset" or subevent == "deleted" then + --saved search has changed, so reload our private ignorelist + private.snatchList = get("snatch.itemsList") + --if there was no saved snatchList, create an empty table + if not private.snatchList then + private.snatchList = {} + set("snatch.itemsList", private.snatchList) -- Caution: this causes a Processor("config", "changed", ...) event + end + private.refreshDisplay() + elseif subevent == "changed" and setting == "snatch.price.model" then -- this is the only "change" that requires a refresh + private.refreshDisplay() + end +end + +function lib.Processors.selecttab(event, subevent, setting) + if subevent == lib.tabname then + if private.doValidation then + private.doValidation() + end + private.refreshDisplay() -- redraw whenever tab is selected + end +end + +function lib.Processors.postscanupdate(event, subevent, setting) + private.refreshDisplay() -- redraw whenever valuations may have changed +end + +function private.OnEnterSnatch(button, row, index) + if frame.snatchlist.sheet.rows[row][index]:IsShown() then --Hide tooltip for hidden cells + local link = frame.snatchlist.sheet.rows[row][index]:GetText() + --check is a valid itemlink + if link and link:match("Hitem:.+|h%[(.-)%]|h|r") then + GameTooltip:SetOwner(button, "ANCHOR_RIGHT") + GameTooltip:SetHyperlink(link) + end + end +end + +function private.OnClickSnatch(button, row, index) + local link = frame.snatchlist.sheet.rows[row][index]:GetText() + --lib.SetWorkingItem(link) handles the job of checking that link is valid + lib.SetWorkingItem(link) +end + +function private.OnResize(...) + --print(...) +end + +--Beginner Tooltips script display for all UI elements +function lib.buttonTooltips(self, text) + if get("snatch.allow.beginerTooltips") and text and self then + GameTooltip:SetOwner(self, "ANCHOR_BOTTOMRIGHT") + GameTooltip:SetText(text) + end +end + +--[[ for reference: + ItemTable[Const.LINK] = hyperlink + ItemTable[Const.ILEVEL] = iLevel + ItemTable[Const.ITYPE] = iType + ItemTable[Const.ISUB] = iSubType + ItemTable[Const.IEQUIP] = iEquip + ItemTable[Const.PRICE] = price + ItemTable[Const.TLEFT] = timeleft + ItemTable[Const.NAME] = name + ItemTable[Const.COUNT] = count + ItemTable[Const.QUALITY] = quality + ItemTable[Const.CANUSE] = canUse + ItemTable[Const.ULEVEL] = level + ItemTable[Const.MINBID] = minBid + ItemTable[Const.MININC] = minInc + ItemTable[Const.BUYOUT] = buyout + ItemTable[Const.CURBID] = curBid + ItemTable[Const.AMHIGH] = isHigh + ItemTable[Const.SELLER] = owner + ItemTable[Const.ITEMID] = itemid + ItemTable[Const.SUFFIX] = suffix + ItemTable[Const.FACTOR] = factor + ItemTable[Const.ENCHANT] = enchant + ItemTable[Const.SEED] = seed +]] +--returns if a item meets snatch criteria +function lib.Search(item) + local itemsig = (":"):join(item[Const.ITEMID], item[Const.SUFFIX] , item[Const.ENCHANT]) + local iteminfo = private.snatchList[itemsig] + + if iteminfo then + local bidprice, buyprice = item[Const.PRICE], item[Const.BUYOUT] + local maxprice = get("snatch.maxprice.enable") and get("snatch.maxprice") + if buyprice <= 0 or not get("snatch.allow.buy") or (maxprice and buyprice > maxprice) then + buyprice = nil + end + if not get("snatch.allow.bid") or (maxprice and bidprice > maxprice) then + bidprice = nil + end + if not (bidprice or buyprice) then + return false, "Does not meet bid/buy requirements" + end + + local value + if iteminfo.percent then + value = private.getPrice(iteminfo.link) * iteminfo.percent / 100 + else + value = iteminfo.price + end + value = value * (item[Const.COUNT] or 1) + + if buyprice and buyprice <= value then + return "buy", value + elseif bidprice and bidprice <= value then + return "bid", value + else + return false, "Price not low enough" + end + end + return false, "Not in snatch list" +end + +--[[Snatch GUI functinality code]] +function lib.AddSnatch(itemlink, price, percent, count) + local linkType, itemid, itemsuffix, itemenchant = AucAdvanced.DecodeLink(itemlink) + if linkType ~= "item" or not itemid then return end + local itemsig = (":"):join(itemid, itemsuffix, itemenchant) + + price, count, percent = tonumber(price), tonumber(count), tonumber(percent) + if price and price <=0 then + price = nil + end + if count and count <= 0 then + count = nil + end + if percent then + if percent <= 0 then + percent = nil + end + end + --Do not store a % and a price + if price and percent then + price = nil --it will be calculated as needed + end + + --add item to snatch list + if price or percent then + private.snatchList[itemsig] = {["link"] = itemlink, ["price"] = price, ["count"] = count, ["percent"] = percent} + else + private.snatchList[itemsig] = nil + end + set("snatch.itemsList", private.snatchList) + private.finishedItem() +end + +function lib.RemoveSnatch(itemlink) + local linkType, itemid, itemsuffix, itemenchant = AucAdvanced.DecodeLink(itemlink) + if linkType ~= "item" or not itemid then return end + local itemsig = (":"):join(itemid, itemsuffix, itemenchant) + + --remove from snatch list + private.snatchList[itemsig] = nil + set("snatch.itemsList", private.snatchList) + private.finishedItem() +end + +--set UI for next item choice +function private.finishedItem() + --reset UI + frame.slot.help:SetText(("Drop item into box")) + frame.icon:SetNormalTexture(nil) + frame.pctBox:SetText("") + private.setMoneyFrame() + --reset current working item + private.workingItemLink = nil + --refresh displays + private.refreshDisplay() +end + +--get the current item we may want to add or remove +function lib.SetWorkingItem(link) + if type(link)~="string" then return end + if not frame then return end + local linkType, itemid, itemsuffix, itemenchant = AucAdvanced.DecodeLink(link) + if linkType ~= "item" or not itemid then return end + + --Get the current saved value if already in snatch list + local itemsig = (":"):join(itemid, itemsuffix, itemenchant) + local iteminfo = private.snatchList[itemsig] + + if iteminfo then + if iteminfo.percent then + frame.pctBox:SetText(iteminfo.percent) + else + frame.pctBox:SetText("") + private.setMoneyFrame(iteminfo.price) + end + else + frame.pctBox:SetText("") + private.setMoneyFrame() + end + + --set edit box texture and name + frame.icon:SetNormalTexture(GetItemIcon(link)) --set icon texture + frame.slot.help:SetText(link) + + --set current working item + private.workingItemLink = link +end + +function private.ClickLinkHook(_, _, link, button) + if link and private.frame and private.frame:IsShown() then + if (button == "LeftButton") then --and (IsAltKeyDown()) and itemName then -- Commented mod key, I want to catch any item clicked. + lib.SetWorkingItem(link) + end + end +end +hooksecurefunc("ChatFrame_OnHyperlinkShow", private.ClickLinkHook) + +function private.refreshDisplay() + if not (frame and frame:IsVisible()) then + return -- only redraw if Snatch is visible + end + + -- Repopulate snatch listbox + local Data, Style = {}, {} + for itemsig, iteminfo in pairs(private.snatchList) do + local price + local market = private.getPrice(iteminfo.link) + --if we are buying by % of market price + if iteminfo.percent then + price = math.floor(market * iteminfo.percent/100) --set buy price to % of market + + local row = #Data+1 -- row number of the item we are about to add + Style[row] = {} + Style[row][1] = {["rowColor"] = {0, 1, 0, 0, 0.2, "Horizontal"}} -- green background for row + Style[row][2] = {["textColor"] = {1,0,0}} -- red text + else + price = iteminfo.price + end + tinsert(Data, {iteminfo.link, iteminfo.percent or 0, price, market}) + end + frame.snatchlist.sheet:SetData(Data, Style) + + --update "help" text to display current + frame.pctBox.help:SetText(format("Buy as percent of %s value", get("snatch.price.model") or "market") ) +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-SearchUI/SearcherSnatch.lua $", "$Rev: 4840 $") diff --git a/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherVendor.lua b/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherVendor.lua new file mode 100644 index 0000000..1c64fae --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-SearchUI/SearcherVendor.lua @@ -0,0 +1,113 @@ +--[[ + Auctioneer - Search UI - Searcher Vendor + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: SearcherVendor.lua 4432 2009-08-29 14:55:35Z dinesh $ + URL: http://auctioneeraddon.com/ + + This is a plugin module for the SearchUI that assists in searching by refined paramaters + + 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 +--]] +-- Create a new instance of our lib with our parent +local lib, parent, private = AucSearchUI.NewSearcher("Vendor") +if not lib then return end +local print,decode,_,_,replicate,empty,_,_,_,debugPrint,fill = AucAdvanced.GetModuleLocals() +local get,set,default,Const = AucSearchUI.GetSearchLocals() +lib.tabname = "Vendor" +-- Set our defaults +default("vendor.profit.min", 1) +default("vendor.profit.pct", 0) +default("vendor.allow.bid", true) +default("vendor.allow.buy", true) +default("vendor.maxprice", 10000000) +default("vendor.maxprice.enable", false) + +-- This function is automatically called when we need to create our search parameters +function lib:MakeGuiConfig(gui) + -- Get our tab and populate it with our controls + local id = gui:AddTab(lib.tabname, "Searchers") + + -- Add the help + gui:AddSearcher("Vendor", "Search for items which can be resold to the vendor for profit", 100) + gui:AddHelp(id, "vendor searcher", + "What does this searcher do?", + "This searcher provides the ability to find items which are below the price that a vendor would buy the item for. Using this searcher, you can buy these items and then take them to the vendor to cash in.") + + gui:AddControl(id, "Header", 0, "Vendor search criteria") + + local last = gui:GetLast(id) + + gui:AddControl(id, "MoneyFramePinned", 0, 1, "vendor.profit.min", 1, 99999999, "Minimum Profit") + gui:AddControl(id, "Slider", 0, 1, "vendor.profit.pct", 0, 100, .5, "Min Discount: %0.01f%%") + + gui:SetLast(id, last) + gui:AddControl(id, "Checkbox", 0.42, 1, "vendor.allow.bid", "Allow Bids") + gui:SetLast(id, last) + gui:AddControl(id, "Checkbox", 0.56, 1, "vendor.allow.buy", "Allow Buyouts") + gui:AddControl(id, "Checkbox", 0.42, 1, "vendor.maxprice.enable", "Enable individual maximum price:") + gui:AddTip(id, "Limit the maximum amount you want to spend with the Vendor searcher") + gui:AddControl(id, "MoneyFramePinned", 0.42, 2, "vendor.maxprice", 1, 99999999, "Maximum Price for Vendor") +end + +function lib.Search(item) + local market, seen, _, pctstring + + if not GetSellValue then + return false, "Vendor pricing not available" + end + + local bidprice, buyprice = item[Const.PRICE], item[Const.BUYOUT] + local maxprice = get("vendor.maxprice.enable") and get("vendor.maxprice") + if buyprice <= 0 or not get("vendor.allow.buy") or (maxprice and buyprice > maxprice) then + buyprice = nil + end + if not get("vendor.allow.bid") or (maxprice and bidprice > maxprice) then + bidprice = nil + end + if not (bidprice or buyprice) then + return false, "Does not meet bid/buy requirements" + end + + local market = GetSellValue(item[Const.ITEMID]) + -- If there's no price, then we obviously can't sell it, ignore! + if not market or market == 0 then + return false, "No vendor price" + end + market = market * item[Const.COUNT] + + + local pct = get("vendor.profit.pct") + local minprofit = get("vendor.profit.min") + local value = market * (100-pct) / 100 + if value > (market - minprofit) then + value = market - minprofit + end + if buyprice and buyprice <= value then + return "buy", market + elseif bidprice and bidprice <= value then + return "bid", market + end + return false, "Not enough profit" +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-SearchUI/SearcherVendor.lua $", "$Rev: 4432 $") diff --git a/Auc-Advanced/Modules/Auc-Util-SearchUI/Textures/SearchButton.tga b/Auc-Advanced/Modules/Auc-Util-SearchUI/Textures/SearchButton.tga new file mode 100644 index 0000000..7bad4f7 Binary files /dev/null and b/Auc-Advanced/Modules/Auc-Util-SearchUI/Textures/SearchButton.tga differ diff --git a/Auc-Advanced/Modules/Auc-Util-SearchUI/Textures/SearchUIIcon.blp b/Auc-Advanced/Modules/Auc-Util-SearchUI/Textures/SearchUIIcon.blp new file mode 100644 index 0000000..badf9f9 Binary files /dev/null and b/Auc-Advanced/Modules/Auc-Util-SearchUI/Textures/SearchUIIcon.blp differ diff --git a/Auc-Advanced/Modules/Auc-Util-SimpleAuction/Auc-Util-SimpleAuction.toc b/Auc-Advanced/Modules/Auc-Util-SimpleAuction/Auc-Util-SimpleAuction.toc new file mode 100644 index 0000000..a63a02a --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-SimpleAuction/Auc-Util-SimpleAuction.toc @@ -0,0 +1,14 @@ +## Title: Auc:Util:SimpleAuction +## Notes: Creates a simplified "Post" frame within the main Auction House window for those who want a bit more utility and information when creating auctions. +## +## Interface: 40000 +## Dependancies: Auc-Advanced +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Auc-Util-SimpleAuction.toc 4933 2010-10-13 17:16:14Z Nechckn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## X-Max-Interface: 40000 +## +Embed.xml diff --git a/Auc-Advanced/Modules/Auc-Util-SimpleAuction/AucSimple.lua b/Auc-Advanced/Modules/Auc-Util-SimpleAuction/AucSimple.lua new file mode 100644 index 0000000..3597898 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-SimpleAuction/AucSimple.lua @@ -0,0 +1,289 @@ +--[[ + Auctioneer - Basic Auction Posting + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: AucSimple.lua 4840 2010-08-04 21:44:00Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This is an addon for World of Warcraft that adds a simple dialog for + easy posting of your auctionables when you are at the auction-house. + + 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 libType, libName = "Util", "SimpleAuction" +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end +local print,decode,_,_,replicate,empty,get,set,default,debugPrint,fill = AucAdvanced.GetModuleLocals() + +local data, _ +local ownResults = {} +local ownCounts = {} + +function lib.Processor(callbackType, ...) + if (callbackType == "tooltip") then + lib.ProcessTooltip(...) + elseif (callbackType == "auctionui") then + private.CreateFrames(...) + elseif (callbackType == "config") then + private.SetupConfigGui(...) + elseif (callbackType == "configchanged") then + private.UpdateConfig(...) + elseif (callbackType == "inventory") then + elseif (callbackType == "scanstats") then + private.clearcache() + private.delayedUpdatePricing = true -- Note: calling private.UpdatePricing is unsafe inside "scanstats" + elseif (callbackType == "postresult") then + private.clearcache() + end +end + +lib.Processors = {} +function lib.Processors.tooltip(callbackType, ...) + lib.ProcessTooltip(...) +end + +function lib.Processors.auctionui(callbackType, ...) + private.CreateFrames(...) +end + +function lib.Processors.config(callbackType, ...) + private.SetupConfigGui(...) +end + +function lib.Processors.configchanged(callbackType, ...) + private.UpdateConfig(...) +end + +function lib.Processors.scanstats(callbackType, ...) + private.clearcache() + private.delayedUpdatePricing = true -- Note: calling private.UpdatePricing is unsafe inside "scanstats" +end + +function lib.Processors.postresult(callbackType, ...) + private.clearcache() +end + +local function whitespace(length) + local spaces = "" + for index = length, 0, -1 do + spaces = spaces.." " + end + return spaces +end + +function lib.ProcessTooltip(tooltip, name, link, quality, quantity, cost, additional) + if not get("util.simpleauc.tooltip") then return end + local realm = AucAdvanced.GetFaction() + local id = private.SigFromLink(link) + local settingstr = get("util.simpleauc."..realm.."."..id) + local market, seen, fixbuy, fixbid, stack + local imgseen, image, matchBid, matchBuy, lowBid, lowBuy, aSeen, aveBuy = private.GetItems(link) + local reason = "Market" + + tooltip:SetColor(0.4, 1.0, 0.9) + + market, seen = AucAdvanced.API.GetMarketValue(link) + if (not market) or (market <= 0) or (not (seen > 5 or aSeen < 3)) then + market = aveBuy + reason = "Current" + end + if (not market or market <= 0) and GetSellValue then + local vendor = GetSellValue(link) + if vendor and vendor > 0 then + market = vendor * 3 + reason = "Vendor markup" + end + end + if not market or market <= 0 then + market = 0 + reason = "No data" + end + + local coinsBid, coinsBuy, coinsBidEa, coinsBuyEa = "no","no","no","no" + if market > 0 then + coinsBid = private.coins(market*0.8*quantity) + coinsBidEa = private.coins(market*0.8) + coinsBuy = private.coins(market*quantity) + coinsBuyEa = private.coins(market) + end + if quantity == 1 then + local text = string.format("%s: %s bid/%s buyout", libName, coinsBid, coinsBuy) + tooltip:AddLine(text) + else + local text = string.format("%s x%d: %s bid/%s buyout", libName, quantity, coinsBid, coinsBuy) + local textea = string.format("%s(Or individually: %s/%s)", whitespace(5), coinsBidEa, coinsBuyEa) + tooltip:AddLine(text) + tooltip:AddLine(textea, 0.3, 0.8, 0.7) + end + if settingstr then + fixbid, fixbuy, _, _, stack = strsplit(":", settingstr) + fixbid, fixbuy, stack = tonumber(fixbid), tonumber(fixbuy), tonumber(stack) + fixbid = ceil(fixbid/stack) + fixbuy = ceil(fixbuy/stack) + end + + if fixbid then + coinsBuy = "no" + coinsBid = private.coins(fixbid*quantity) + if fixbuy then + coinsBuy = private.coins(fixbuy*quantity) + end + if quantity == 1 then + local text = string.format("%sFixed: %s bid/%s buyout", whitespace(12), coinsBid, coinsBuy) + tooltip:AddLine(text) + else + local text = string.format("%sFixed x%d: %s bid/%s buyout", whitespace(12), quantity, coinsBid, coinsBuy) + tooltip:AddLine(text) + end + end + if get("util.simpleauc.tooltip.undercut") then + if lowBid and lowBid > 0 then + coinsBuy = "no" + coinsBid = private.coins(lowBid*quantity) + if lowBuy and lowBuy > 0 then + coinsBuy = private.coins(lowBuy*quantity) + end + if quantity == 1 then + local text = string.format("%sUndercut: %s bid/%s buyout", whitespace(8), coinsBid, coinsBuy) + tooltip:AddLine(text) + else + local text = string.format("%sUndercut x%d: %s bid/%s buyout", whitespace(8), quantity, coinsBid, coinsBuy) + tooltip:AddLine(text) + end + else + tooltip:AddLine(" No Competition") + end + end +end + +function lib.OnLoad() + --Default sizes for the scrollframe column widths + default("util.simpleauc.columnwidth.Seller", 89) + default("util.simpleauc.columnwidth.Left", 32) + default("util.simpleauc.columnwidth.Stk", 32 ) + default("util.simpleauc.columnwidth.Min/ea", 65) + default("util.simpleauc.columnwidth.Cur/ea", 65) + default("util.simpleauc.columnwidth.Buy/ea", 65) + default("util.simpleauc.columnwidth.MinBid", 76) + default("util.simpleauc.columnwidth.CurBid", 76) + default("util.simpleauc.columnwidth.Buyout", 80) + default("util.simpleauc.columnwidth.BLANK", 0.05) + --Default options + default("util.simpleauc.clickhook", true) + default("util.simpleauc.clickhook.doubleclick", false) + default("util.simpleauc.scanbutton", true) + default("util.simpleauc.scanbutton.disable.wowecon", true) + default("util.simpleauc.tooltip", true) + default("util.simpleauc.tooltip.undercut", true) + default("util.simpleauc.auto.duration", 48) + default("util.simpleauc.auto.match", true) + default("util.simpleauc.auto.undercut", true) + default("util.simpleauc.undercut", "percent") + default("util.simpleauc.undercut.fixed", 1) + default("util.simpleauc.undercut.percent", 2.5) + default("util.simpleauc.displayauctiontab", true) +end + +function private.UpdateConfig(setting, value) + if private.frame then + local frame = private.frame + local showing = false + if get("util.simpleauc.scanbutton") then + showing = true + if get("util.simpleauc.scanbutton.disable.wowecon") and IsAddOnLoaded("WOWEcon_PriceMod") then + showing = false + end + end + + if showing then + frame.scanbutton:Show() + else + frame.scanbutton:Hide() + end + if setting == "util.simpleauc.displayauctiontab" then + if value then + AucAdvanced.AddTab(private.frame.tab, private.frame) + else + AucAdvanced.RemoveTab(private.frame.tab, private.frame) + end + end + end +end + +function private.SetupConfigGui(gui) + local id = gui:AddTab(lib.libName, lib.libType.." Modules") + gui:MakeScrollable(id) + private.gui = gui + private.guiId = id + + gui:AddHelp(id, "what simpleauc", + "What is SimpleAuction?", + "Simple Auction is a simplified, more automated way of posting items. It focuses it's emphasis on easy pricing and maximum sale speed with a minimum of configuration options and learning curve.\n".. + "It won't get you maximium profit, or ultimate configurability, but the values it provides are reasonable in most circumstances and it is primarily very easy to use.\n") + + gui:AddControl(id, "Header", 0, lib.libName.." options") + + gui:AddControl(id, "Subhead", 0, "") + gui:AddControl(id, "Checkbox", 0, 1, "util.simpleauc.displayauctiontab", "Show Post tab at the Auction House") + gui:AddTip(id, "Shows simple post tab on the auction house") + + gui:AddControl(id, "Subhead", 0, "Tooltip") + gui:AddControl(id, "Checkbox", 0, 1, "util.simpleauc.tooltip", "Show prices in tooltip") + gui:AddTip(id, "Shows market price for the current item in the tooltip") + gui:AddControl(id, "Checkbox", 0, 2, "util.simpleauc.tooltip.undercut", "Show undercut prices in tooltip") + gui:AddTip(id, "Shows potential undercut price for the current item in the tooltip") + + gui:AddControl(id, "Subhead", 0, "Shortcuts") + gui:AddControl(id, "Checkbox", 0, 1, "util.simpleauc.clickhook", "Allow alt-click item in bag instead of drag") + gui:AddTip(id, "Enables an alt-click mouse-hook so you can alt-click your inventory items into the SimpleAuction post frame") + gui:AddControl(id, "Checkbox", 0, 2, "util.simpleauc.clickhook.doubleclick", "Allow double-alt-clicking to auto-post the item") + gui:AddTip(id, "If you alt-click twice in succession, the item will be posted automatically at the current price") + + gui:AddControl(id, "Subhead", 0, "Defaults") + gui:AddControl(id, "Checkbox", 0, 1, "util.simpleauc.auto.match", "Automatically match your current price if not remembering item price") + gui:AddTip(id, "When items are posted, if there is no remembered price, and you currently have auctioning items, your current price will be matched") + gui:AddControl(id, "Checkbox", 0, 1, "util.simpleauc.auto.undercut", "Automatically undercut the current price if not matching or remembering") + gui:AddTip(id, "When items are posted, if there is no remembered price and the item is not automatching, the competition will be undercut") + gui:AddControl(id, "Label", 0, 1, nil, "Automatically set the duration for an item unless remembering:") + gui:AddControl(id, "Selectbox", 0, 2, {{12, "12 hour"}, {24, "24 hour"}, {48, "48 hour"}}, "util.simpleauc.auto.duration", "Auto Duration") + gui:AddTip(id, "When items are posted, if there is no remembered price, the duration will default to this value") + + gui:AddControl(id, "Subhead", 0, "Defaults") + gui:AddControl(id, "Label", 0, 1, nil, "Undercut basis:") + gui:AddControl(id, "Selectbox", 0, 2, {{"fixed", "Fixed value"}, {"percent", "Percentage"}}, "util.simpleauc.undercut", "Undercuts by") + gui:AddTip(id, "When the auction is to be undercut, specify how you want the lowest price to be undercut") + gui:AddControl(id, "Label", 0, 1, nil, "Fixed undercut value amount:") + gui:AddControl(id, "MoneyFramePinned", 0, 2, "util.simpleauc.undercut.fixed", 0, 999999999) + gui:AddTip(id, "This is the fixed amount to undercut the lowest auction by") + gui:AddControl(id, "Label", 0, 1, nil, "Percentage undercut amount:") + gui:AddControl(id, "NumeriWide", 0, 3, "util.simpleauc.undercut.percent", 0,100, 0.5, "Percentage: %s%%") + gui:AddTip(id, "This is the percentage to undercut the lowest auction by") + + gui:AddControl(id, "Subhead", 0, "Scan button") + gui:AddControl(id, "Checkbox", 0, 1, "util.simpleauc.scanbutton", "Show big red scan button at bottom of browse window") + gui:AddTip(id, "Displays the old-style \"Scan\" button at the bottom of the browse window.") + gui:AddControl(id, "Checkbox", 0, 2, "util.simpleauc.scanbutton.disable.wowecon", "Except if WowEcon is loaded") +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-SimpleAuction/AucSimple.lua $", "$Rev: 4840 $") diff --git a/Auc-Advanced/Modules/Auc-Util-SimpleAuction/Embed.xml b/Auc-Advanced/Modules/Auc-Util-SimpleAuction/Embed.xml new file mode 100644 index 0000000..8a6fd2f --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-SimpleAuction/Embed.xml @@ -0,0 +1,8 @@ + + + + diff --git a/Auc-Advanced/Modules/Auc-Util-SimpleAuction/SimpFrame.lua b/Auc-Advanced/Modules/Auc-Util-SimpleAuction/SimpFrame.lua new file mode 100644 index 0000000..0f2b064 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-SimpleAuction/SimpFrame.lua @@ -0,0 +1,1294 @@ +--[[ + Auctioneer - Simplified Auction Posting + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: SimpFrame.lua 4933 2010-10-13 17:16:14Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This is an addon for World of Warcraft that adds a simple dialog for + easy posting of your auctionables when you are at the auction-house. + + 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 lib = AucAdvanced.Modules.Util.SimpleAuction +local private = lib.Private +local const = AucAdvanced.Const +local aucPrint,decode,_,_,replicate,_,get,set,default,debugPrint,fill = AucAdvanced.GetModuleLocals() +local Const = const +local wipe = wipe + +local pricecache + +local frame +local TAB_NAME = "Post" + +function private.clearcache() + pricecache = nil +end + +function private.postPickupContainerItemHook(_,_,bag, slot) + if (CursorHasItem()) then + frame.CursorItem = {bag = bag, slot = slot} + else + frame.CursorItem = nil + end +end + +function private.ShiftFocus(frame, ...) + local dest + if IsShiftKeyDown() then + dest = frame.prevFrame + else + dest = frame.nextFrame + end + if dest and dest.SetFocus then + dest:SetFocus() + else + frame:ClearFocus() + end +end + +function private.SetPrevNext(frame, prevFrame, nextFrame) + frame.prevFrame = prevFrame + frame.nextFrame = nextFrame + frame:SetScript("OnTabPressed", private.ShiftFocus) + frame:SetScript("OnEnterPressed", private.ShiftFocus) +end + +function private.SigFromLink(link) + local itype, id, suffix, factor, enchant, seed = AucAdvanced.DecodeLink(link) + if itype=="item" then + if enchant ~= 0 then + return ("%d:%d:%d:%d"):format(id, suffix, factor, enchant) + elseif factor ~= 0 then + return ("%d:%d:%d"):format(id, suffix, factor) + elseif suffix ~= 0 then + return ("%d:%d"):format(id, suffix) + end + return tostring(id) + end + -- returns nil +end + +function private.GetMyPrice(link, items) + if not link then return end + local uBid, uBuy + local searchname = GetItemInfo (link) + local n = GetNumAuctionItems("owner") + if n and n > 0 then + for i = 1, n do + local item = AucAdvanced.Scan.GetAuctionItem("owner", i) + if item and item[const.NAME] == searchname then + if items then table.insert(items, item) end + local stack = item[const.COUNT] + if stack and stack > 0 then + local bid, buy = item[const.MINBID], item[const.BUYOUT] + if bid and bid > 0 then + bid = bid / stack + uBid = uBid and min(uBid, bid) or bid + -- only check buy value if the bid value is valid + -- avoids including buy prices for "sold" auctions, + -- due to problems with invalid stack counts from Blizzard API + if buy and buy > 0 then + buy = buy / stack + uBuy = uBuy and min(uBuy, buy) or buy + end + end + end + end + end + end + return uBid, uBuy +end + +--Returns: +--numitems: number of competing items +--items: table with competition +--uBid: bid matching your current auction +--uBuy: buy matching your current auction +--lBid: bid undercutting competition by 1c +--lBuy: buy undercutting competition by 1c +--aSeen: number of items in competing auctions +--aBuy: average price for current competing auctions +local query = {} +function private.GetItems(link) + local itype, id, suffix, factor, enchant, seed = AucAdvanced.DecodeLink(link) + local aSeen, lBid, lBuy, uBid, uBuy, aBuy, aveBuy = 0 + local player = UnitName("player") + local items = {} + local sig = AucAdvanced.API.GetSigFromLink(link) + if pricecache and pricecache[sig] then + uBid, uBuy, lBid, lBuy, aSeen, aveBuy = strsplit(":", pricecache[sig][1]) + uBid, uBuy, lBid, lBuy, aSeen, aveBuy = tonumber(uBid), tonumber(uBuy), tonumber(lBid), tonumber(lBuy), tonumber(aSeen), tonumber(aveBuy) + return #pricecache[sig][2], pricecache[sig][2], uBid, uBuy, lBid, lBuy, aSeen, aveBuy + end + query.itemId = id + query.suffix = suffix + query.factor = factor + local matching = AucAdvanced.API.QueryImage(query) + + local live = false + if AuctionFrame and AuctionFrame:IsVisible() then + live = true + uBid, uBuy = private.GetMyPrice(link, items) + end + for pos, item in ipairs(matching) do + local bid, buy, owner, stk = item[const.MINBID], item[const.BUYOUT], item[const.SELLER], item[const.COUNT] + stk = stk or 1 + local bidea, buyea + if bid and bid > 0 then + bidea = bid/stk + end + if buy and buy > 0 then + buyea = buy/stk + end + + if owner and owner == player then + if not live then + if not uBid then uBid = bidea elseif bidea then uBid = min(uBid, bidea) end + if not uBuy then uBuy = buyea elseif buyea then uBuy = min(uBuy, buyea) end + table.insert(items, item) + end + else + if not lBid then + lBid = bidea + elseif bidea then + lBid = min(lBid, bidea) + end + if not lBuy then + lBuy = buyea + elseif buyea then + lBuy = min(lBuy, buyea) + end + if buy then + aBuy = (aBuy or 0) + buy + aSeen = (aSeen or 0)+ stk + end + table.insert(items, item) + end + end + aveBuy = (aBuy and aSeen>=1) and aBuy/aSeen or 0 + if not pricecache then pricecache = {} end + pricecache[sig] = {} + pricecache[sig][1] = strjoin(":", tostring(uBid), tostring(uBuy), tostring(lBid), tostring(lBuy), tostring(aSeen), tostring(aveBuy)) + pricecache[sig][2] = replicate(items) + return #items, items, uBid, uBuy, lBid, lBuy, aSeen, aveBuy +end + +local coins = AucAdvanced.Coins +private.coins = coins + +function private.SetIconCount(itemCount) + local size = 18 + if itemCount > 999 then + local size = 14 + elseif itemCount > 99 then + local size = 16 + elseif itemCount > 9 then + local size = 17 + end + frame.icon.count:SetFont(AucAdvSimpleNumberFontLarge.font, size, "OUTLINE") + frame.icon.count:SetText(itemCount) +end + +function private.UpdateDisplay() + local link = frame.icon.itemLink + if not link then return end + local cBid, cBuy = MoneyInputFrame_GetCopper(frame.minprice), MoneyInputFrame_GetCopper(frame.buyout) + frame.CurItem.buy, frame.CurItem.bid = cBuy, cBid + local cStack = frame.stacks.size:GetNumber() or 1 + frame.CurItem.stack = cStack + local cNum = frame.stacks.num:GetNumber() or 1 + frame.CurItem.number = cNum + local oStack, oBid, oBuy, oReason, oLink = unpack(frame.detail) + local lStack = frame.stacks.size.lastSize or oStack + local duration = frame.duration.time.selected + + local sig = AucAdvanced.API.GetSigFromLink(link) + if not sig then return private.LoadItemLink() end + local _, total, unpostable, _, _, reason = AucAdvanced.Post.CountAvailableItems(sig) + total = total - unpostable + if total < 1 then return private.LoadItemLink() end + private.SetIconCount(total) + + local dBid = abs(cBid/lStack*oStack-oBid) + local dBuy = abs(cBuy/lStack*oStack-oBuy) + + local priceType = "auto" + if dBid >= 1 or dBuy >= 1 then + priceType = "fixed" + frame.err:SetText("Manual pricing on item") + end + + if priceType == "auto" and cStack ~= oStack then + private.UpdatePricing() + return + elseif priceType == "fixed" and cStack ~= lStack then + cBid = cBid / lStack * cStack + cBuy = cBuy / lStack * cStack + cBid = ceil(cBid) + cBuy = ceil(cBuy) + MoneyInputFrame_ResetMoney(frame.minprice) + MoneyInputFrame_SetCopper(frame.minprice, cBid) + frame.CurItem.bid = cBid + frame.CurItem.bidper = cBid/frame.CurItem.stack + MoneyInputFrame_ResetMoney(frame.buyout) + MoneyInputFrame_SetCopper(frame.buyout, cBuy) + frame.CurItem.buy = cBuy + frame.CurItem.buyper = cBuy/frame.CurItem.stack + frame.err:SetText("Adjusted price to new stack size") + end + + if GetSellValue then + local vendor = GetSellValue(oLink) or 0 + if vendor > cBid / cStack then + frame.err:SetText("Warning: Bid is below vendor price") + end + end + + local flagenable = true + local coinsBid, coinsBuy, coinsBidEa, coinsBuyEa + if cBid > 0 then + coinsBid = coins(cBid) + coinsBidEa = coins(cBid/cStack) + else + coinsBid = "no" + coinsBidEa = "no" + frame.err:SetText("Error: No bid price set") + flagenable = false + end + if cBuy > 0 then + coinsBuy = coins(cBuy) + coinsBuyEa = coins(cBuy/cStack) + if cBuy < cBid then + frame.err:SetText("Error: Buyout cannot be less than bid price") + flagenable = false + end + else + coinsBuy = "no" + coinsBuyEa = "no" + end + + local lots = "lot" + if cNum > 1 then lots = "lots" end + + local text + if (cStack > 1) then + text = string.format("Auctioning %d %s of %d sized stacks at %s bid/%s buyout per stack (%s/%s ea)", cNum, lots, cStack, coinsBid, coinsBuy, coinsBidEa, coinsBuyEa) + else + text = string.format("Auctioning %d %s of this item at %s bid/%s buyout each", cNum, lots, coinsBid, coinsBuy) + end + frame.info:SetText(text) + + local faction = "home" + if AucAdvanced.GetFactionGroup() == "Neutral" then + faction = "neutral" + end + local deposit = GetDepositCost(oLink, duration, faction, cStack) + if not deposit then + frame.fees:SetText("Unknown deposit cost") + elseif cNum > 1 then + frame.fees:SetText(("Deposit: %s, %s/stack \n~%s/ea"):format(coins(deposit*cNum), coins(deposit), coins(ceil(deposit/cStack)))) + elseif cStack > 1 then + frame.fees:SetText(("Deposit: %s/stack \n~%s/ea"):format(coins(deposit), coins(ceil(deposit/cStack)))) + else + frame.fees:SetText(("Deposit: %s"):format(coins(deposit))) + end + frame.stacks.equals:SetText("= "..(cStack * cNum)) + if flagenable then + frame.create:Enable() + else + frame.create:Disable() + end +end + +function private.UpdateCompetition(image) + local data = {} + local style = {} + for i = 1, #image do + local result = image[i] + local tLeft = result[Const.TLEFT] + if (tLeft == 1) then tLeft = "30m" + elseif (tLeft == 2) then tLeft = "2h" + elseif (tLeft == 3) then tLeft = "12h" + elseif (tLeft == 4) then tLeft = "48h" + end + local count = result[Const.COUNT] + data[i] = { + --result[Const.NAME], + result[Const.SELLER], + tLeft, + count, + math.floor(0.5+result[Const.MINBID]/count), + math.floor(0.5+result[Const.CURBID]/count), + math.floor(0.5+result[Const.BUYOUT]/count), + result[Const.MINBID], + result[Const.CURBID], + result[Const.BUYOUT], + result[Const.LINK] + } + local curbid = result[Const.CURBID] + if curbid == 0 then + curbid = result[Const.MINBID] + end + --color ignored/self sellers + local seller = result[Const.SELLER] + local player = UnitName("player") + if seller == player then + if not style[i] then style[i] = {} end + style[i][1] = { textColor = {0,1,0} } + elseif AucAdvanced.Modules.Filter.Basic and AucAdvanced.Modules.Filter.Basic.IsPlayerIgnored and AucAdvanced.Modules.Filter.Basic.IsPlayerIgnored(result[Const.SELLER]) then + if not style[i] then style[i] = {} end + style[i][1] = { textColor = {1,0,0} } + end + end + frame.imageview.sheet:SetData(data, style) +end + +function private.UpdatePricing() + local link = frame.icon.itemLink + if not link then return end + local mid, seen = 0,0 + local stack = frame.stacks.size:GetNumber() + local _,_,_,_,_,_,_,stx = GetItemInfo(link) + + local sig = AucAdvanced.API.GetSigFromLink(link) + if not sig then return private.LoadItemLink() end + local _, total, unpostable, _, _, reason = AucAdvanced.Post.CountAvailableItems(sig) + total = total - unpostable + if total < 1 then return private.LoadItemLink() end + private.SetIconCount(total) + + stx = min(stx, total) + + if not stack or stack == 0 or stack > stx then + stack = stx + frame.stacks.size:SetNumber(stack) + frame.CurItem.stack = stack + end + + local num = frame.stacks.num:GetNumber() + if not num or num == 0 then + num = 1 + frame.stacks.num:SetNumber(num) + frame.CurItem.number = num + end + + local buy, bid + local reason = "" + + -- We need this out here because it fetches the items from the image + local imgseen, image, matchBid, matchBuy, lowBid, lowBuy, aSeen, aveBuy = private.GetItems(link) + private.UpdateCompetition(image) + + --check for fixed price + if frame.CurItem.manual then + buy = frame.CurItem.buyper + bid = frame.CurItem.bidper + reason = "Manual pricing on item" + end + if not buy then + --Check for undercut first + if frame.options.undercut:GetChecked() then + if lowBuy and lowBuy > 0 then + local underBuy, underBid, by = 1,1, "default 1c" + + local model = get("util.simpleauc.undercut") + local fixed = get("util.simpleauc.undercut.fixed") + local percent = get("util.simpleauc.undercut.percent") + local pct = tonumber(percent)/100 + if model == "fixed" then + underBuy = fixed + underBid = fixed + by = "fixed amount: "..coins(fixed) + else + underBuy = lowBuy*pct + underBid = (lowBid or 0)*pct + by = percent.."% ("..coins(underBuy)..")" + end + + buy = lowBuy - underBuy + if lowBid and lowBid > 0 and lowBid <= lowBuy then + bid = lowBid - underBid + else + bid = buy * 0.8 + end + reason = "Undercutting market by "..by + end + --then matching current + elseif frame.options.matchmy:GetChecked() then + if matchBuy and matchBuy > 0 then + buy = matchBuy + if matchBid and matchBid > 0 and matchBid <= matchBuy then + bid = matchBid + else + bid = buy * 0.8 + end + reason = "Matching your prices" + end + end + --if no buy price yet, look for marketprice + if not buy then + local market, seen = AucAdvanced.API.GetMarketValue(link) + if market and (market > 0) and (seen > 5 or aSeen < 3) then + buy = market + bid = market * 0.8 + reason = "Using market value" + end + end + --look for average of current competition + if not buy and aveBuy and aveBuy > 0 then + buy = aveBuy + bid = buy * 0.8 + reason = "Using current market data" + end + --Vendor markup + if not buy and GetSellValue then + local vendor = GetSellValue(link) + if vendor and vendor > 0 then + buy = vendor * 3 + bid = buy * 0.8 + reason = "Marking up vendor" + end + end + end + if not buy then + buy = 0 + end + if not bid then + bid = buy * 0.8 + end + --multiply by stacksize + bid = bid * stack + buy = buy * stack + --We give up + if bid == 0 then + bid = 1 + buy = 0 + reason = "Unable to calculate price" + end + + if (stack * num) > total then + reason = "Error: You don't have that many" + end + bid, buy = ceil(tonumber(bid) or 1), ceil(tonumber(buy) or 0) + + MoneyInputFrame_ResetMoney(frame.minprice) + MoneyInputFrame_SetCopper(frame.minprice, bid) + frame.CurItem.bid = bid + frame.CurItem.bidper = bid/stack + + MoneyInputFrame_ResetMoney(frame.buyout) + MoneyInputFrame_SetCopper(frame.buyout, buy) + frame.CurItem.buy = buy + frame.CurItem.buyper = buy/stack + + frame.detail = { stack, bid, buy, reason, link } + frame.err:SetText(reason) + + private.UpdateDisplay(reason) +end + +--function runs when we're alerted to a possible change in one of the controls. +--we check if something is actually different, and if so, update. +--rather than changing ONLY one setting, changed function allow multiple settings to be modified in one round +--this solved issues with alt double click posting items before our onupdate could be called enough cycles to set all the changed values +function private.CheckUpdate() + if not frame.CurItem.link then return end + local buy = MoneyInputFrame_GetCopper(frame.buyout) + local bid = MoneyInputFrame_GetCopper(frame.minprice) + local stack = frame.stacks.size:GetNumber() + local number = frame.stacks.num:GetNumber() + local match = frame.options.matchmy:GetChecked() + local undercut = frame.options.undercut:GetChecked() + local remember = frame.options.remember:GetChecked() + local duration = frame.duration.time.selected + if frame.CurItem.buy ~= buy then --New Buyout manually entered + frame.CurItem.buy = buy + frame.CurItem.buyper = buy/(stack or 1) + frame.CurItem.manual = true + private.UpdateDisplay() + end + if frame.CurItem.bid ~= bid then --New Bid manually entered + frame.CurItem.bid = bid + frame.CurItem.bidper = bid/(stack or 1) + frame.CurItem.manual = true + private.UpdateDisplay() + end + if stack and stack > 0 and frame.CurItem.stack ~= stack then --new stack size entered + frame.CurItem.stack = stack + private.UpdatePricing() + end + if number and number > 0 and frame.CurItem.number ~= number then --new number of stacks entered + frame.CurItem.number = number + private.UpdatePricing() + end + if frame.CurItem.match ~= match then + frame.CurItem.match = match + if match then --turn off other checkboxes + frame.CurItem.manual = false + frame.CurItem.undercut = nil + frame.options.undercut:SetChecked(false) + frame.CurItem.remember = nil + frame.options.remember:SetChecked(false) + end + private.UpdatePricing() + end + if frame.CurItem.undercut ~= undercut then + frame.CurItem.undercut = undercut + if undercut then --turn off other checkboxes + frame.CurItem.manual = false + frame.CurItem.match = nil + frame.options.matchmy:SetChecked(false) + frame.CurItem.remember = nil + frame.options.remember:SetChecked(false) + end + private.UpdatePricing() + end + if frame.CurItem.duration ~= duration then + frame.CurItem.duration = duration + private.UpdatePricing() + end + if frame.CurItem.remember ~= remember then + frame.CurItem.manual = true + frame.CurItem.remember = remember + if remember then + private.SaveConfig() + frame.CurItem.match = nil + frame.options.matchmy:SetChecked(false) + frame.CurItem.undercut = nil + frame.options.undercut:SetChecked(false) + else + private.RemoveConfig() + end + private.UpdatePricing() + + end + if frame.CurItem.remember then + private.SaveConfig() + end +end + +function private.IconClicked() + local objType, _, itemLink = GetCursorInfo() + local size + if CursorHasItem() and frame.CursorItem then + _, size = GetContainerItemInfo(frame.CursorItem.bag, frame.CursorItem.slot) + end + frame.CursorItem = nil + ClearCursor() + if objType ~= "item" then itemLink = nil end + private.LoadItemLink(itemLink, size) +end + +function private.LoadItemLink(itemLink, size) + wipe(frame.CurItem) + if itemLink then + local itemName, itemLink, itemRarity, itemLevel, itemMinLevel, itemType, itemSubType, itemStackCount, itemEquipLoc, itemTexture = GetItemInfo(itemLink) + if not itemName then return private.LoadItemLink() end + local sig = AucAdvanced.API.GetSigFromLink(itemLink) + if not sig then + aucPrint(itemLink.." cannot be posted: Not an item") + return private.LoadItemLink() + end + itemLink = AucAdvanced.SanitizeLink(itemLink) + frame.CurItem.link = itemLink + frame.CurItem.name = itemName + frame.icon.itemLink = itemLink + frame.icon:SetNormalTexture(itemTexture) + + local _, total, unpostable, _, _, reason = AucAdvanced.Post.CountAvailableItems(sig) + local itemCount = total - unpostable + if itemCount < 1 then + if reason == "Damaged" then + aucPrint(itemLink.." is damaged: you may be able to post it after repairing it") + else + aucPrint(itemLink.." cannot be posted: "..(AucAdvanced.Post.ErrorText[reason] or "Unknown Reason")) + end + return private.LoadItemLink() + end + frame.CurItem.count = itemCount + private.SetIconCount(itemCount) + + frame.name:SetText(itemName) + frame.name:SetTextColor(GetItemQualityColor(itemRarity)) + else + frame.icon.itemLink = nil + frame.icon:SetNormalTexture(nil) + frame.icon.count:SetText("") + frame.name:SetText("Drop item onto slot") + frame.create:Disable() + end + frame.info:SetText("To auction an item, drag it from your bag.") + frame.fees:SetText("") + frame.err:SetText("-- No item selected --") + frame.stacks.equals:SetText("= 0") + private.ClearSetting() + private.LoadConfig() + + if not frame.options.remember:GetChecked() then + local uBid, uBuy = private.GetMyPrice(itemLink) + if uBid and get("util.simpleauc.auto.match") then + frame.options.matchmy:SetChecked(true) + frame.options.undercut:SetChecked(false) + frame.CurItem.match = true + frame.CurItem.undercut = nil + elseif get("util.simpleauc.auto.undercut") then + frame.options.matchmy:SetChecked(false) + frame.options.undercut:SetChecked(true) + frame.CurItem.match = nil + frame.CurItem.undercut = true + end + end + + + if itemLink and size then + frame.stacks.size:SetNumber(size) + end + --private.UpdatePricing() +end + +function private.DoTooltip() + if not frame.CurItem.link then return end + GameTooltip:SetOwner(frame.icon, "ANCHOR_NONE") + GameTooltip:SetHyperlink(frame.CurItem.link) + AucAdvanced.ShowItemLink(GameTooltip, frame.CurItem.link, frame.CurItem.count) + GameTooltip:ClearAllPoints() + GameTooltip:SetPoint("TOPLEFT", frame.icon, "TOPRIGHT", 10, 0) +end + +function private.UndoTooltip() + GameTooltip:Hide() +end + +function private.OnUpdate() + -- check for valuechanged on update, so that multiple controls changing at once will only yield one check + if frame.CurItem.valuechanged then + frame.CurItem.valuechanged = nil + private.CheckUpdate() + end + -- delayed call to UpdatePricing triggered by "scanstats" (note: UpdatePricing is unsafe from within "scanstats") + -- also avoids unnecessary processing while the frame is hidden + if private.delayedUpdatePricing then + private.delayedUpdatePricing = nil + private.UpdatePricing() + end +end + +function private.LoadConfig() + if not frame.CurItem.link then return end + local id = private.SigFromLink(frame.CurItem.link) + local settingstring = get("util.simpleauc."..private.realmKey.."."..id) + if not settingstring then return end + local bid, buy, duration, number, stack = strsplit(":", settingstring) + bid = tonumber(bid) + buy = tonumber(buy) + duration = tonumber(duration) + number = tonumber(number) + stack = tonumber(stack) + MoneyInputFrame_ResetMoney(frame.minprice) + MoneyInputFrame_SetCopper(frame.minprice, bid) + MoneyInputFrame_ResetMoney(frame.buyout) + MoneyInputFrame_SetCopper(frame.buyout, buy) + frame.stacks.size:SetNumber(stack) + frame.stacks.num:SetNumber(number) + frame.options.undercut:SetChecked(false) + frame.options.matchmy:SetChecked(false) + frame.options.remember:SetChecked(true) + frame.duration.time.selected = duration + for i, j in pairs(frame.duration.time.intervals) do + if duration == j then + frame.duration.time[i]:SetChecked(true) + else + frame.duration.time[i]:SetChecked(false) + end + end + frame.CurItem.bid = bid + frame.CurItem.bidper = bid/stack + frame.CurItem.buy = buy + frame.CurItem.buyper = buy/stack + frame.CurItem.duration = duration + frame.CurItem.number = number + frame.CurItem.stack = stack + frame.CurItem.match = nil + frame.CurItem.undercut = nil + frame.CurItem.remember = true + frame.CurItem.manual = true + private.UpdatePricing() +end + +function private.RemoveConfig() + if not frame.CurItem.link then return end + local id = private.SigFromLink(frame.CurItem.link) + set("util.simpleauc."..private.realmKey.."."..id, nil) +end + +function private.SaveConfig() + if not frame.CurItem.link then return end + local id = private.SigFromLink(frame.CurItem.link) + local settingstring = strjoin(":", + tostring(frame.CurItem.bid), + tostring(frame.CurItem.buy), + tostring(frame.CurItem.duration), + tostring(frame.CurItem.number), + tostring(frame.CurItem.stack) + ) + set("util.simpleauc."..private.realmKey.."."..id, settingstring) +end + +function private.ClearSetting() + frame.CurItem.bid = nil + frame.CurItem.bidper = nil + frame.CurItem.buy = nil + frame.CurItem.buyper = nil + frame.CurItem.stack = nil + frame.CurItem.number = nil + frame.CurItem.match = nil + frame.CurItem.undercut = nil + frame.CurItem.remember = nil + frame.CurItem.manual = nil + frame.CurItem.duration = nil + MoneyInputFrame_ResetMoney(frame.minprice) + MoneyInputFrame_ResetMoney(frame.buyout) + frame.stacks.num:SetNumber(0) + frame.stacks.size:SetNumber(0) + + local under, match, dur = + get("util.simpleauc.auto.undercut"), + get("util.simpleauc.auto.match"), + get("util.simpleauc.auto.duration") + if under then match = false end + + frame.options.matchmy:SetChecked(match) + frame.options.undercut:SetChecked(under) + frame.options.remember:SetChecked(false) + frame.duration.time.selected = dur + frame.duration.time[1]:SetChecked(dur == 12) + frame.duration.time[2]:SetChecked(dur == 24) + frame.duration.time[3]:SetChecked(dur == 48) + private.UpdatePricing() +end + +function private.PostAuction() + local link = frame.CurItem.link + if not link then + aucPrint("Posting Failed: No Item Selected") + return + end + local sig = private.SigFromLink(link) + local number = frame.CurItem.number + local stack = frame.CurItem.stack + local bid = frame.CurItem.bid + local buy = frame.CurItem.buy + local duration = frame.CurItem.duration or 48 + local success, reason = AucAdvanced.Post.PostAuctionClick(sig, stack, bid, buy, duration, number) + if success then + aucPrint("Posting "..number.." stacks of "..stack.."x "..link.." at Bid:"..coins(bid)..", BO:"..coins(buy).." for "..duration.."h") + else + reason = AucAdvanced.Post.ErrorText[reason] or "Unknown Reason" + aucPrint("Posting Failed for "..link..": "..reason) + end +end + +function private.Refresh(background) + local link = frame.CurItem.link + if not link then return end + local name, _, rarity, _, itemMinLevel, itemType, itemSubType, stack = GetItemInfo(link) + local itemTypeId, itemSubId + for catId, catName in pairs(AucAdvanced.Const.CLASSES) do + if catName == itemType then + itemTypeId = catId + for subId, subName in pairs(AucAdvanced.Const.SUBCLASSES[itemTypeId]) do + if subName == itemSubType then + itemSubId = subId + break + end + end + break + end + end + aucPrint(("Refreshing view of {{%s}}"):format(name))--Refreshing view of {{%s}} + if background and type(background) == 'boolean' then + AucAdvanced.Scan.StartPushedScan(name, itemMinLevel, itemMinLevel, nil, itemTypeId, itemSubId, nil, rarity) + else + AucAdvanced.Scan.PushScan() + AucAdvanced.Scan.StartScan(name, itemMinLevel, itemMinLevel, nil, itemTypeId, itemSubId, nil, rarity) + end +end + +function private.CreateFrames() + if frame then return end + + local SelectBox = LibStub:GetLibrary("SelectBox") + local ScrollSheet = LibStub:GetLibrary("ScrollSheet") + + frame = CreateFrame("Frame", "AucAdvSimpFrame", AuctionFrame) + private.frame = frame + private.realmKey, private.realm = AucAdvanced.GetFaction() + local DiffFromModel = 0 + local MatchString = "" + frame.list = {} + frame.cache = {} + frame.CurItem = {} + frame.detail = {0,0,0,"",""} + Stubby.RegisterFunctionHook("PickupContainerItem", 200, private.postPickupContainerItemHook) + + frame.SetButtonTooltip = function(text) + if text and get("util.appraiser.buttontips") then + GameTooltip:SetOwner(this, "ANCHOR_BOTTOMRIGHT") + GameTooltip:SetText(text) + end + end + + frame:SetParent(AuctionFrame) + frame:SetPoint("TOPLEFT", AuctionFrame, "TOPLEFT") + frame:SetPoint("BOTTOMRIGHT", AuctionFrame, "BOTTOMRIGHT") + frame:SetScript("OnUpdate", private.OnUpdate) + + frame.title = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + frame.title:SetPoint("TOP", frame, "TOP", 0, -20) + frame.title:SetText("Simple Auction - Simplified auction posting interface.") + + frame.slot = frame:CreateTexture(nil, "BORDER") + frame.slot:SetPoint("TOPLEFT", frame, "TOPLEFT", 80, -45) + frame.slot:SetWidth(50) + frame.slot:SetHeight(50) + frame.slot:SetTexCoord(0.15, 0.85, 0.15, 0.85) + frame.slot:SetTexture("Interface\\Buttons\\UI-EmptySlot") + + frame.name = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") + frame.name:SetPoint("TOPLEFT", frame.slot, "TOPRIGHT", 10, -2) + frame.name:SetJustifyV("TOP") + frame.name:SetText("Drop item onto slot") + + frame.info = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + frame.info:SetPoint("TOPLEFT", frame.name, "BOTTOMLEFT", 0, -3) + frame.info:SetJustifyV("TOP") + frame.info:SetTextColor(0.0,1,1) + frame.info:SetText("To auction an item, drag it from your bag.") + + frame.err = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + frame.err:SetPoint("TOPLEFT", frame.info, "BOTTOMLEFT", 0, 0) + frame.err:SetJustifyV("TOP") + frame.err:SetTextColor(1,0.2,0,1) + frame.err:SetText("-- No item selected --") + + frame.icon = CreateFrame("Button", nil, frame) + frame.icon:SetPoint("TOPLEFT", frame.slot, "TOPLEFT", 3, -3) + frame.icon:SetWidth(42) + frame.icon:SetHeight(42) + frame.icon:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square.blp") + frame.icon:SetScript("OnClick", private.IconClicked) + frame.icon:SetScript("OnReceiveDrag", private.IconClicked) + frame.icon:SetScript("OnEnter", private.DoTooltip) + frame.icon:SetScript("OnLeave", private.UndoTooltip) + + local numberFont = CreateFont("AucAdvSimpleNumberFontLarge") + numberFont:CopyFontObject(GameFontHighlight) + numberFont.font = numberFont:GetFont() + numberFont:SetFont(numberFont.font, 12, "OUTLINE") + numberFont:SetShadowColor(0,0,0) + numberFont:SetShadowOffset(3,-2) + + frame.icon.count = frame.icon:CreateFontString(nil, "OVERLAY", "AucAdvSimpleNumberFontLarge") + frame.icon.count:SetPoint("BOTTOMLEFT", frame.icon, "BOTTOMLEFT", 3, 5) + + frame.minprice = CreateFrame("Frame", "AucAdvSimpFrameStart", frame, "MoneyInputFrameTemplate") + frame.minprice.isMoneyFrame = true + frame.minprice:SetPoint("TOPLEFT", frame, "TOPLEFT", 20, -120) + MoneyInputFrame_SetOnValueChangedFunc(frame.minprice, function() + frame.CurItem.valuechanged = true + end) + frame.minprice.label = frame.minprice:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + frame.minprice.label:SetPoint("BOTTOMLEFT", frame.minprice, "TOPLEFT", 0, 0) + frame.minprice.label:SetText("Starting price:") + AucAdvSimpFrameStartGold:SetWidth(50) + + frame.buyout = CreateFrame("Frame", "AucAdvSimpFrameBuyout", frame, "MoneyInputFrameTemplate") + frame.buyout.isMoneyFrame = true + frame.buyout:SetPoint("TOPLEFT", frame.minprice, "BOTTOMLEFT", 0, -20) + MoneyInputFrame_SetOnValueChangedFunc(frame.buyout, function() + frame.CurItem.valuechanged = true + end) + frame.buyout.label = frame.buyout:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + frame.buyout.label:SetPoint("BOTTOMLEFT", frame.buyout, "TOPLEFT", 0, 0) + frame.buyout.label:SetText("Buyout price:") + AucAdvSimpFrameBuyoutGold:SetWidth(50) + + frame.duration = CreateFrame("Frame", "AucAdvSimpFrameDuration", frame) + frame.duration:SetPoint("TOPLEFT", frame.buyout, "BOTTOMLEFT", 0, -20) + frame.duration:SetWidth(140) + frame.duration:SetHeight(20) + frame.duration.label = frame.duration:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + frame.duration.label:SetPoint("BOTTOMLEFT", frame.duration, "TOPLEFT", 0, 0) + frame.duration.label:SetText("Duration:"); + + frame.duration.time = { + intervals = {12, 24, 48}, + selected = 48, + OnClick = function (obj, ...) + frame.CurItem.valuechanged = true + local self = frame.duration.time + for pos, dur in ipairs(self.intervals) do + if obj == self[pos] then + self.selected = dur + self[pos]:SetChecked(true) + else + self[pos]:SetChecked(false) + end + end + end, + } + local t = frame.duration.time + for pos, dur in ipairs(t.intervals) do + t[pos] = CreateFrame("CheckButton", "AucAdvSimpFrameDuration"..dur, frame.duration, "OptionsCheckButtonTemplate") + t[pos]:SetPoint("TOPLEFT", frame.duration, "TOPLEFT", (pos-1)*53,0) + t[pos]:SetHitRectInsets(-1, -25, -1, -1) + t[pos].label = t[pos]:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + t[pos].label:SetPoint("LEFT", t[pos], "RIGHT", 0, 0) + t[pos].label:SetText(dur.."h") + t[pos].dur = dur + t[pos]:SetScript("OnClick", t.OnClick) + if dur == t.selected then + t[pos]:SetChecked(true) + else + t[pos]:SetChecked(false) + end + end + + frame.stacks = CreateFrame("Frame", "AucAdvSimpFrameStacks", frame) + frame.stacks:SetPoint("TOPLEFT", frame.duration, "BOTTOMLEFT", 0, -20) + frame.stacks:SetWidth(140) + frame.stacks:SetHeight(20) + frame.stacks.label = frame.duration:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + frame.stacks.label:SetPoint("BOTTOMLEFT", frame.stacks, "TOPLEFT", 0, 0) + frame.stacks.label:SetText("Stacks: (number x size)"); + + frame.stacks.num = CreateFrame("EditBox", "AucAdvSimpFrameStackNum", frame.stacks, "InputBoxTemplate") + frame.stacks.num:SetPoint("TOPLEFT", frame.stacks, "TOPLEFT", 5, 0) + frame.stacks.num:SetAutoFocus(false) + frame.stacks.num:SetHeight(18) + frame.stacks.num:SetWidth(40) + frame.stacks.num:SetNumeric(true) + frame.stacks.num:SetScript("OnTextChanged", function() frame.CurItem.valuechanged = true end) + + frame.stacks.mult = frame.duration:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + frame.stacks.mult:SetPoint("BOTTOMLEFT", frame.stacks.num, "BOTTOMRIGHT", 5, 0) + frame.stacks.mult:SetText("x") + + frame.stacks.size = CreateFrame("EditBox", "AucAdvSimpFrameStackSize", frame.stacks, "InputBoxTemplate") + frame.stacks.size:SetPoint("TOPLEFT", frame.stacks.num, "TOPRIGHT", 20, 0) + frame.stacks.size:SetAutoFocus(false) + frame.stacks.size:SetHeight(18) + frame.stacks.size:SetWidth(30) + frame.stacks.size:SetNumeric(true) + frame.stacks.size:SetScript("OnTextChanged", function() frame.CurItem.valuechanged = true end) + + frame.stacks.equals = frame.duration:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + frame.stacks.equals:SetPoint("BOTTOMLEFT", frame.stacks.size, "BOTTOMRIGHT", 5, 0) + frame.stacks.equals:SetText("= 0") + + frame.fees = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + frame.fees:SetPoint("TOP", frame.stacks, "BOTTOM", 10, -2) + frame.fees:SetWidth(150) + frame.fees:SetJustifyV("TOP") + frame.fees:SetJustifyH("CENTER") + frame.fees:SetText("") + frame.fees:SetTextColor(0,1,1) + + frame.options = CreateFrame("Frame", "AucAdvSimpFrameOptions", frame) + frame.options:SetPoint("TOPLEFT", frame.stacks, "BOTTOMLEFT", 0, -40) + frame.options:SetWidth(140) + frame.options:SetHeight(300) + frame.options.label = frame.options:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + frame.options.label:SetPoint("BOTTOMLEFT", frame.options, "TOPLEFT", 0, 0) + frame.options.label:SetText("Options:") + + frame.create = CreateFrame("Button", "AucAdvSimpFrameCreate", frame, "OptionsButtonTemplate") + frame.create:SetPoint("BOTTOMRIGHT", AuctionFrameMoneyFrame, "TOPRIGHT", 0, 10) + frame.create:SetWidth(140) + frame.create:SetText("Create Auction") + frame.create:SetScript("OnClick", private.PostAuction) + frame.create:Disable() + + frame.clear = CreateFrame("Button", "AucAdvSimpFrameRemember", frame, "OptionsButtonTemplate") + frame.clear:SetPoint("BOTTOMRIGHT", frame.create, "TOPRIGHT", 0, 5) + frame.clear:SetWidth(140) + frame.clear:SetText("Clear Setting") + frame.clear:SetScript("OnClick", function() private.ClearSetting() private.RemoveConfig() end) + + MoneyInputFrame_SetPreviousFocus(frame.minprice, frame.stacks.size) + MoneyInputFrame_SetNextFocus(frame.minprice, AucAdvSimpFrameBuyoutGold) + MoneyInputFrame_SetPreviousFocus(frame.buyout, AucAdvSimpFrameStartCopper) + MoneyInputFrame_SetNextFocus(frame.buyout, frame.stacks.num) + private.SetPrevNext(frame.stacks.num, AucAdvSimpFrameBuyoutCopper, frame.stacks.size) + private.SetPrevNext(frame.stacks.size, frame.stacks.num, AucAdvSimpFrameStartGold) + + function frame.options:AddOption(option, text) + local item = CreateFrame("CheckButton", "AucAdvSimpFrameOption_"..option, self, "OptionsCheckButtonTemplate") + if self.last then + item:SetPoint("TOPLEFT", self.last, "BOTTOMLEFT", 0,7) + else + item:SetPoint("TOPLEFT", self, "TOPLEFT", 0,0) + end + self.last = item + + item:SetHitRectInsets(-1, -140, 3, 3) + item:SetScript("OnClick", function() frame.CurItem.valuechanged = true end) + + + item.label = item:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + item.label:SetPoint("LEFT", item, "RIGHT", 0, 0) + item.label:SetText(text) + + self[option] = item + end + + frame.options:AddOption("matchmy", "Match my current") + frame.options:AddOption("undercut", "Undercut competitors") + frame.options:AddOption("remember", "Remember fixed price") + + function frame.ClickBagHook(_,_,self,button) + if not (frame:IsVisible() and get("util.simpleauc.clickhook")) then return end + local bag = self:GetParent():GetID() + local slot = self:GetID() + local link = GetContainerItemLink(bag, slot) + local _, size = GetContainerItemInfo(bag, slot) + if link then + if (button == "LeftButton") and (IsAltKeyDown()) then + private.LoadItemLink(link, size) + --see if double clicking to auto post is allowed + if (not get("util.simpleauc.clickhook.doubleclick")) then return end + + if not private.clickdata then private.clickdata = {} end + local last = private.clickdata + local now = GetTime() + if last[1] == bag and last[2] == slot and now - last[3] < 0.5 then + -- Is a double click + aucPrint("Auto auctioning double-alt-clicked item") + if not frame.options.remember:GetChecked() then + frame.options.undercut:SetChecked(true) + end + private.CheckUpdate() + private.PostAuction() + end + last[1] = bag + last[2] = slot + last[3] = now + end + end + end + + frame.tab = CreateFrame("Button", "AuctionFrameTabUtilSimple", AuctionFrame, "AuctionTabTemplate") + frame.tab:SetText(TAB_NAME) + frame.tab:Show() + PanelTemplates_DeselectTab(frame.tab) + if get("util.simpleauc.displayauctiontab") then + AucAdvanced.AddTab(frame.tab, frame) + end + + function frame.tab.OnClick(self, _, index) + if not index then index = self:GetID() end + local tab = _G["AuctionFrameTab"..index] + if (tab and tab:GetName() == "AuctionFrameTabUtilSimple") then + AuctionFrameTopLeft:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Browse-TopLeft") + AuctionFrameTop:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Browse-Top") + AuctionFrameTopRight:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Browse-TopRight") + AuctionFrameBotLeft:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Browse-BotLeft") + AuctionFrameBot:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Browse-Bot") + AuctionFrameBotRight:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Browse-BotRight") + AuctionFrameMoneyFrame:Show() + frame:Show() + AucAdvanced.Scan.LoadScanData() + private.UpdateDisplay() -- update for any bag changes while frame was hidden + else + AuctionFrameMoneyFrame:Show() + frame:Hide() + end + end + + frame.imageview = CreateFrame("Frame", nil, frame) + frame.imageview:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + edgeFile = "Interface/Tooltips/UI-Tooltip-Border", + tile = true, tileSize = 32, edgeSize = 16, + insets = { left = 5, right = 5, top = 5, bottom = 5 } + }) + + frame.imageview:SetBackdropColor(0, 0, 0, 1) + frame.imageview:SetPoint("TOPLEFT", frame, "TOPLEFT", 185, -100) + frame.imageview:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -5, 0) + frame.imageview:SetPoint("BOTTOM", frame, "BOTTOM", 0, 35) + --records the column width changes + --store width by header name, that way if column reorginizing is added we apply size to proper column + function private.onResize(self, column, width) + if not width then + set("util.simpleauc.columnwidth."..self.labels[column]:GetText(), "default") --reset column if no width is passed. We use CTRL+rightclick to reset column + self.labels[column].button:SetWidth(get("util.simpleauc.columnwidth."..self.labels[column]:GetText())) + else + set("util.simpleauc.columnwidth."..self.labels[column]:GetText(), width) + end + end + function private.onClick(button, row, index) + + end + + private.buyselection = {} + function private.onSelect() + if frame.imageview.sheet.prevselected ~= frame.imageview.sheet.selected then + frame.imageview.sheet.prevselected = frame.imageview.sheet.selected + local selected = frame.imageview.sheet:GetSelection() + if (not selected) or (not selected[10]) then + private.buyselection = {} + frame.buy:Disable() + frame.bid:Disable() + else + private.buyselection.link = selected[10] + private.buyselection.seller = selected[1] + private.buyselection.stack = selected[3] + private.buyselection.minbid = selected[7] + private.buyselection.curbid = selected[8] + private.buyselection.buyout = selected[9] + + -- Make sure that it's not one of our auctions + if (not AucAdvancedConfig["users."..private.realm.."."..private.buyselection.seller]) then + if private.buyselection.buyout and (private.buyselection.buyout > 0) then + frame.buy:Enable() + else + frame.buy:Disable() + end + + if private.buyselection.minbid then + frame.bid:Enable() + else + frame.bid:Disable() + end + else + frame.buy:Disable() + frame.bid:Disable() + end + end + end + end + + function private.BuyAuction() + AucAdvanced.Buy.QueueBuy(private.buyselection.link, private.buyselection.seller, private.buyselection.stack, private.buyselection.minbid, private.buyselection.buyout, private.buyselection.buyout) + frame.imageview.sheet.selected = nil + private.onSelect() + end + + function private.BidAuction() + local bid = private.buyselection.minbid + if private.buyselection.curbid and private.buyselection.curbid > 0 then + bid = math.ceil(private.buyselection.curbid*1.05) + end + AucAdvanced.Buy.QueueBuy(private.buyselection.link, private.buyselection.seller, private.buyselection.stack, private.buyselection.minbid, private.buyselection.buyout, bid) + frame.imageview.sheet.selected = nil + private.onSelect() + end + + + frame.imageview.sheet = ScrollSheet:Create(frame.imageview, { + { "Seller", "TEXT", get("util.simpleauc.columnwidth.Seller")}, --89 + { "Left", "INT", get("util.simpleauc.columnwidth.Left")}, --32 + { "Stk", "INT", get("util.simpleauc.columnwidth.Stk")}, --32 + { "Min/ea", "COIN", get("util.simpleauc.columnwidth.Min/ea"), { DESCENDING=true } }, --65 + { "Cur/ea", "COIN", get("util.simpleauc.columnwidth.Cur/ea"), { DESCENDING=true } }, --65 + { "Buy/ea", "COIN", get("util.simpleauc.columnwidth.Buy/ea"), { DESCENDING=true, DEFAULT=true } }, --65 + { "MinBid", "COIN", get("util.simpleauc.columnwidth.MinBid"), { DESCENDING=true } }, --76 + { "CurBid", "COIN", get("util.simpleauc.columnwidth.CurBid"), { DESCENDING=true } }, --76 + { "Buyout", "COIN", get("util.simpleauc.columnwidth.Buyout"), { DESCENDING=true } }, --80 + { "", "TEXT", get("util.simpleauc.columnwidth.BLANK")}, --Hidden column to carry the link --0 + }, nil, nil, private.onClick, private.onResize, private.onSelect) + frame.imageview.sheet:EnableSelect(true) + + frame.config = CreateFrame("Button", nil, frame, "OptionsButtonTemplate") + frame.config:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -25, -13) + frame.config:SetText("Configure") + frame.config:SetScript("OnClick", function() + AucAdvanced.Settings.Show() + private.gui:ActivateTab(private.guiId) + end) + + frame.scanbutton = CreateFrame("Button", "AucAdvScanButton", AuctionFrameBrowse, "OptionsButtonTemplate") + frame.scanbutton:SetText("Scan") + frame.scanbutton:SetParent("AuctionFrameBrowse") + frame.scanbutton:SetPoint("LEFT", "AuctionFrameMoneyFrame", "RIGHT", 5,0) + frame.scanbutton:SetScript("OnClick", function() + if not AucAdvanced.Scan.IsScanning() then + AucAdvanced.Scan.StartScan("", "", "", AuctionFrameBrowse.selectedInvtypeIndex, AuctionFrameBrowse.selectedClassIndex, AuctionFrameBrowse.selectedSubclassIndex, nil, nil) + end + end) + + frame.refresh = CreateFrame("Button", nil, frame, "OptionsButtonTemplate") + frame.refresh:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -167,15) + frame.refresh:SetText("Refresh") + frame.refresh:SetWidth(80) + frame.refresh:SetScript("OnClick", private.Refresh) + + frame.bid = CreateFrame("Button", nil, frame, "OptionsButtonTemplate") + frame.bid:SetPoint("TOPLEFT", frame.refresh, "TOPRIGHT", 1, 0) + frame.bid:SetWidth(80) + frame.bid:SetText("Bid")--Bid + frame.bid:SetScript("OnClick", private.BidAuction) + frame.bid:Disable() + + frame.buy = CreateFrame("Button", nil, frame, "OptionsButtonTemplate") + frame.buy:SetPoint("TOPLEFT", frame.bid, "TOPRIGHT", 1, 0) + frame.buy:SetWidth(80) + frame.buy:SetText("Buy")--Buy + frame.buy:SetScript("OnClick", private.BuyAuction) + frame.buy:Disable() + + if get("util.simpleauc.scanbutton") then + frame.scanbutton:Show() + else + frame.scanbutton:Hide() + end + + Stubby.RegisterFunctionHook("ContainerFrameItemButton_OnModifiedClick", -300, frame.ClickBagHook) + hooksecurefunc("AuctionFrameTab_OnClick", frame.tab.OnClick) + + hooksecurefunc("SetItemRef", function (shortlink, hyperlink, mousebutton, chatframe) + if mousebutton == "LeftButton" and IsAltKeyDown() and frame:IsVisible() and get("util.simpleauc.clickhook") then + local link = hyperlink or shortlink + if link then + private.LoadItemLink(link) + end + end + end) + + function frame:OnEvent(event, ...) + if event == "BAG_UPDATE" then + if frame:IsVisible() then + private.UpdateDisplay() + end + end + end + + frame:SetScript("OnEvent", frame.OnEvent) + frame:RegisterEvent("BAG_UPDATE") +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-SimpleAuction/SimpFrame.lua $", "$Rev: 4933 $") diff --git a/Auc-Advanced/Modules/Auc-Util-VendMarkup/Auc-Util-VendMarkup.toc b/Auc-Advanced/Modules/Auc-Util-VendMarkup/Auc-Util-VendMarkup.toc new file mode 100644 index 0000000..cc12514 --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-VendMarkup/Auc-Util-VendMarkup.toc @@ -0,0 +1,16 @@ +## Title: Auc:Util:Vendor Markup +## Notes: Vendor Markup for Auctioneer. Provides a default vendor markup value for items never seen on the auction house. +## +## Interface: 40000 +## LoadOnDemand: 0 +## Disabled: 0 +## Dependencies: Auc-Advanced +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Auc-Util-VendMarkup.toc 4933 2010-10-13 17:16:14Z Nechckn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## X-Max-Interface: 40000 +## +Embed.xml diff --git a/Auc-Advanced/Modules/Auc-Util-VendMarkup/Embed.xml b/Auc-Advanced/Modules/Auc-Util-VendMarkup/Embed.xml new file mode 100644 index 0000000..dcc02fd --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-VendMarkup/Embed.xml @@ -0,0 +1,7 @@ + + + + diff --git a/Auc-Advanced/Modules/Auc-Util-VendMarkup/vendMarkup.lua b/Auc-Advanced/Modules/Auc-Util-VendMarkup/vendMarkup.lua new file mode 100644 index 0000000..47fd2bc --- /dev/null +++ b/Auc-Advanced/Modules/Auc-Util-VendMarkup/vendMarkup.lua @@ -0,0 +1,100 @@ +--[[ + Auctioneer - VendMarkup + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: vendMarkup.lua 4840 2010-08-04 21:44:00Z Nechckn $ + URL: http://auctioneeraddon.com/ + + This is an addon for World of Warcraft that provides a vendor mark-up + price statistic for items. It is intended to be used only at times when + you do not have a more reliable price statistic, and wish to post an item + for just a simple vendor markup price instead. + + 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 libType, libName = "Util", "VendMarkup" +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end +local print,decode,_,_,replicate,empty,get,set,default,debugPrint,fill = AucAdvanced.GetModuleLocals() + +function lib.GetPrice(hyperlink, serverKey) + local linkType,itemId,property,factor = decode(hyperlink) + if linkType == "item" and itemId and itemId > 0 and type(GetSellValue) == "function" then + local vendorFor = GetSellValue(itemId) + if vendorFor then + return vendorFor * get("util.vendmarkup.multiplier") / 100 + end + end +end + +function lib.GetPriceColumns() + return "Vendor Price Markup" +end + +local array = {} +function lib.GetPriceArray(hyperlink, serverKey) + -- no need to clean the array; we will overwrite the single entry anyway + + -- this module only provides "price" + array.price = lib.GetPrice(hyperlink) + + -- Return a temporary array. Data in this array is + -- only valid until this function is called again. + return array +end + +function lib.Processor(callbackType, ...) + if callbackType == "config" then + private.SetupConfigGui(...) + end +end + +lib.Processors = {} +function lib.Processors.config(callbackType, ...) + private.SetupConfigGui(...) +end + + + +function lib.OnLoad(addon) + default("util.vendmarkup.multiplier", 300) +end + +function private.SetupConfigGui(gui) + -- The defaults for the following settings are set in the lib.OnLoad function + + local id = gui:AddTab(libName, libType.." Modules") + + gui:AddHelp(id, "what vendmarkup", + "What is the Vendor Markup module?", + "This module will give you the price to vendor an item multiplied by a percentage of that vendor's price to give you the vendor markup price.\n".. + "This vendor markup is most often used when posting items for auction which do not have any data, you can tell Appraiser to use the vendor markup for the buyout price, and that will give you a good starting point for what the item might sell for.\n") + + gui:AddControl(id, "Header", 0, libName.." options") + + gui:AddControl(id, "TinyNumber", 0, 1, "util.vendmarkup.multiplier", 100, 1000, "Vendor markup (in percent)") + +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Util-VendMarkup/vendMarkup.lua $", "$Rev: 4840 $") diff --git a/Auc-Advanced/Modules/Readme.txt b/Auc-Advanced/Modules/Readme.txt new file mode 100644 index 0000000..7a2e1d4 --- /dev/null +++ b/Auc-Advanced/Modules/Readme.txt @@ -0,0 +1,16 @@ +This direcory contains embeddable modules for Auctioneer. +Auctioneer embeddable modules may be either installed as fully fledged +AddOns in you main AddOns folder, or if you prefer, added into this folder +and have the Active modules list updated. + +To update you active modules list, you must either: + * Run the luae executable in this folder, which will automatically + generate a Active.xml, activating all valid installed modules. + * If you have a perl interpreter on your system (eg ActivePerl for + Windows, or perl on Unix/OSX) you may simply run rebuild.pl instead. + * You may manually edit the existing Active.xml and add/remove entries + to your taste. + + + + diff --git a/Auc-Advanced/Modules/autorun.lua b/Auc-Advanced/Modules/autorun.lua new file mode 100644 index 0000000..6323732 --- /dev/null +++ b/Auc-Advanced/Modules/autorun.lua @@ -0,0 +1,65 @@ + +function traceback(message) + print("Error occured during execution of script") + print(" "..message) +end + + +local pathsep = [[/]] +if os.execute'uname' ~= 0 then pathsep = [[\]] end + +local function files(dirname) + local dh = assert(os.opendir(dirname)) + return function() + return dh:read() or assert(dh:close()) and nil + end +end + +print("Scanning plugins folder...") + +local output = io.open("Active.xml", "w"); +output:write([[]].."\n") + +local dirs = {} +local embeddedModules = {} +local active, invalid = 0,0 +for fn in files(".") do + if fn and fn:lower():sub(0,4) == "auc-" then + local info = os.stat(fn) + if info and info.dir then + local fh = io.open(fn..pathsep.."Embed.xml", "r") + if (fh) then + active = active + 1 + embeddedModules[active] = fn + print(" + Activating: "..fn) + else + invalid = invalid + 1 + print(" ! Module \""..fn.."\" is not embeddable") + end + end + end +end + +output:write("\t\n\n") +for index, module in ipairs(embeddedModules) do + output:write("\t\n"); +end +output:write("") + +print("Activated: "..active.." modules.") +if (invalid > 0) then + print("WARNING: There were "..invalid.." non-embeddable modules detected.") + print("Sometimes Auctioneer modules are not embeddable, and need to be installed as normal addons.") + print("Embeddable modules will have an Embed.xml file in the folder.") + alert("Warning: Non-embeddable modules detected, please check log!") +else + sleep(2.5) + exit() +end + diff --git a/Auc-Advanced/Modules/rebuild.pl b/Auc-Advanced/Modules/rebuild.pl new file mode 100644 index 0000000..23c2b07 --- /dev/null +++ b/Auc-Advanced/Modules/rebuild.pl @@ -0,0 +1,53 @@ +#!/usr/bin/perl + +print "Scanning plugins folder...\n"; + +open OUTPUT, "> Active.xml"; +print OUTPUT qq(\n); + +$active = $inactive = $count = 0; +my @embeddedModules; +for $fn (<[aA]uc-*>) { + if (-d $fn) { + if (-f "$fn/Embed.xml") { + @embeddedModules[$active] = $fn; + $active++; + print " + Activating: $fn\n"; + } + else { + $invalid++; + print " ! Module \"$fn\" is not embeddable\n"; + } + } +} + +print OUTPUT "\t\n\n"; + +$count = 0; +while ($count < @embeddedModules) { + print OUTPUT "\t\n"; + $count++ +} +print OUTPUT ""; + +print "Activated: $active modules.\n"; +if ($invalid > 0) { + print "WARNING: There were $invalid non-embeddable modules detected.\n"; + print "Sometimes Auctioneer modules are not embeddable,\n"; + print "and need to be installed as normal addons.\n"; + print "Embeddable modules will have an Embed.xml file in the folder.\n"; + print "Press to exit..."; + <>; +} +else { + sleep(2.5); +} + diff --git a/Auc-Advanced/Support/LibGroupStore.lua b/Auc-Advanced/Support/LibGroupStore.lua new file mode 100644 index 0000000..a6b12f5 --- /dev/null +++ b/Auc-Advanced/Support/LibGroupStore.lua @@ -0,0 +1,194 @@ +--[[ +-- LibGroupStorage.lua +-- Group data storage +-- A storage array for variable sized groups of data +-- Written by Ken Allan +-- This code is hereby released into the Public Domain without warranty. +-- +-- Basic Usage: +-- local GroupStore = LibStub("LibGroupStore") +-- local storage = GroupStore() +-- +-- local id = storage:InsertGroup(122, 233, 344, 411) +-- local a, b, c, d = storage:GetGroup(id) +-- +-- Advanced Usage: +-- storage:Clear() -- Remove all groups from the storage +-- storage:RemoveGroup(7) -- Removes group 7 from the array, shifting later groups down. +-- base = storage:GetBase() -- Returns the base array (so you can save it, etc) +-- storage = GroupStore(base) -- Resurrect a saved storage array +-- +-- Shortcuts: +-- storage(nil, 1,2,3) = storage:InsertGroup(1,2,3) +-- storage - 3 = storage:RemoveGroup(3) +-- storage(1) = storage:GetGroup(1) +-- #storage = storage:GetNumGroups() +--]] + +local LIBRARY_VERSION_MAJOR = "LibGroupStore" +local LIBRARY_VERSION_MINOR = 1 + + +--[[----------------------------------------------------------------- + +LibStub is a simple versioning stub meant for use in Libraries. +See for more info. +LibStub is hereby placed in the Public Domain. +Credits: + Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke + +--]]----------------------------------------------------------------- +do + local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 + local LibStub = _G[LIBSTUB_MAJOR] + + if not LibStub or LibStub.minor < LIBSTUB_MINOR then + LibStub = LibStub or {libs = {}, minors = {} } + _G[LIBSTUB_MAJOR] = LibStub + LibStub.minor = LIBSTUB_MINOR + + function LibStub:NewLibrary(major, minor) + assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)") + minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.") + + local oldminor = self.minors[major] + if oldminor and oldminor >= minor then return nil end + self.minors[major], self.libs[major] = minor, self.libs[major] or {} + return self.libs[major], oldminor + end + + function LibStub:GetLibrary(major, silent) + if not self.libs[major] and not silent then + error(("Cannot find a library instance of %q."):format(tostring(major)), 2) + end + return self.libs[major], self.minors[major] + end + + function LibStub:IterateLibraries() return pairs(self.libs) end + setmetatable(LibStub, { __call = LibStub.GetLibrary }) + end +end +--[End of LibStub]--------------------------------------------------- + +local lib = LibStub:NewLibrary(LIBRARY_VERSION_MAJOR, LIBRARY_VERSION_MINOR) +if not lib then return end + +local kit={} + +lib.data = {} + +local function findFunction(self, index) + return kit[index] +end + +-- Create a new storage instance +function lib:New(base) + -- Init it's variables + if base then + assert(base.index or #base == 0, "Cannot specify a non-group base") + else + base = {} + base.index = {} + end + + local proxy = newproxy(true) + local mt = getmetatable(proxy) + + mt.__len = kit.GetNumGroups + mt.__sub = kit.RemoveGroup + mt.__call = kit.GetGroup + mt.__index = findFunction + + lib.data[proxy] = base + + -- Return the data + return proxy +end +setmetatable(lib, { __call = lib.New }) + +-- Insert the items on the stack as a group into the storage +function kit:InsertGroup(...) + local base = lib.data[self] + local pos = #base + local len = select('#', ...) + table.insert(base.index, pos) + table.insert(base.index, len) + for i = 1, len do + base[pos+i] = select(i, ...) + end + -- Return the group id + return (#base.index / 2) +end + +-- Remove specified group from the storage, shifting all higher groups down +function kit:RemoveGroup(group) + local base = lib.data[self] + local max = #base.index / 2 + group = tonumber(group) + assert(group, "Must specify a group number") + assert(group > 0, "Attempt to get group range <= 0") + assert(group <= max, "Attempt to get group range > max") + local index = group * 2 - 1 + local pos = table.remove(base.index, index) + local len = table.remove(base.index, index) + for i = 1, len do + table.remove(base, pos+1) + end + for i = group, max-1 do + index = i * 2 - 1 + base.index[index] = base.index[index]-len + end + return self +end + +-- Fetch the start, end and length of a specified group (for direct access) +function kit:GetGroupRange(group) + local base = lib.data[self] + local max = #base.index / 2 + group = tonumber(group) + assert(group, "Must specify a group number") + assert(group > 0, "Attempt to get group range <= 0") + assert(group <= max, "Attempt to get group range > max") + local index = group * 2 - 1 + local pos = base.index[index] + local len = base.index[index] + -- Return start, end, length + return pos+1, pos+len, len +end + +-- Return the specified group on the stack +function kit:GetGroup(group, ...) + if not group or group <= 0 then return kit.InsertGroup(self, ...) end + local base = lib.data[self] + local max = #base.index / 2 + group = tonumber(group) + assert(group, "Must specify a group number") + assert(group > 0, "Attempt to get group range <= 0") + assert(group <= max, "Attempt to get group range > max") + local index = group * 2 - 1 + local pos = base.index[index] + local len = base.index[index+1] + return unpack(base, pos+1, pos+len) +end + +-- Return the number of groups +function kit:GetNumGroups() + local base = lib.data[self] + return #base.index / 2 +end + +-- Return a reference to the base object (for saves) +function kit:GetBase() + return lib.data[self] +end + +-- Clear all groups from the entire storage array +function kit:Clear() + local base = lib.data[self] + while #base.index > 0 do table.remove(base.index) end + while #base > 0 do table.remove(base) end + + return self +end + + diff --git a/Auc-Advanced/Support/LibRevision.lua b/Auc-Advanced/Support/LibRevision.lua new file mode 100644 index 0000000..935ee69 --- /dev/null +++ b/Auc-Advanced/Support/LibRevision.lua @@ -0,0 +1,126 @@ +--[[ +-- LibRevision.lua +-- Finds the revisions of a given addon and works out the version. +-- Written by Ken Allan +-- This code is hereby released into the Public Domain without warranty. +-- +-- Usage: +-- local libRevision = LibStub("LibRevision") +-- +-- libRevision:Set("svnUrl", "svnRev", "5.1.DEV.", ...) +-- libRevision:Get("addon") +--]] + +local LIBRARY_VERSION_MAJOR = "LibRevision" +local LIBRARY_VERSION_MINOR = 1 + +--[[----------------------------------------------------------------- + +LibStub is a simple versioning stub meant for use in Libraries. +See for more info. +LibStub is hereby placed in the Public Domain. +Credits: + Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke + +--]]----------------------------------------------------------------- +do + local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 + local LibStub = _G[LIBSTUB_MAJOR] + + if not LibStub or LibStub.minor < LIBSTUB_MINOR then + LibStub = LibStub or {libs = {}, minors = {} } + _G[LIBSTUB_MAJOR] = LibStub + LibStub.minor = LIBSTUB_MINOR + + function LibStub:NewLibrary(major, minor) + assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)") + minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.") + + local oldminor = self.minors[major] + if oldminor and oldminor >= minor then return nil end + self.minors[major], self.libs[major] = minor, self.libs[major] or {} + return self.libs[major], oldminor + end + + function LibStub:GetLibrary(major, silent) + if not self.libs[major] and not silent then + error(("Cannot find a library instance of %q."):format(tostring(major)), 2) + end + return self.libs[major], self.minors[major] + end + + function LibStub:IterateLibraries() return pairs(self.libs) end + setmetatable(LibStub, { __call = LibStub.GetLibrary }) + end +end +--[End of LibStub]--------------------------------------------------- + +local lib = LibStub:NewLibrary(LIBRARY_VERSION_MAJOR, LIBRARY_VERSION_MINOR) +if not lib then return end + +if not lib.versions then + lib.versions = {} +end + +function lib:Get(addon) + return lib.versions[addon:lower()] +end + +function lib:Set(url, revision, dev, ...) + local repo,file + if (not url and revision) then return end + + local n = select("#", ...) + for i=1, n do + local sub = select(i, ...) + repo, file = url:match("%$URL: .*/("..sub..")/([^%$]+) %$") + if repo then break end + end + local rev = tonumber(revision:match("(%d+)")) or 0 + + if repo then + local branch,name = file:match("^(trunk)/([^/]+)/") + if not branch then + branch,name = file:match("^branches/([^/]+)/([^/]+)/") + end + + if branch then + local embed, addit = false, branch + local addon = name:lower() + + if not lib.versions[addon] then + lib.versions[addon] = {} + end + + local vaddon = lib.versions[addon] + local vrev = max(vaddon['x-revision'] or 0, rev) + vaddon['x-revision'] = vrev + + if not IsAddOnLoaded(addon) then + embed = true + addit = addit.."/embedded" + end + + local ver = GetAddOnMetadata(addon, "Version") + if not ver or ver:sub(0,2) == "<%" then ver = dev..rev end + + if not vaddon["x-revisions"] then + vaddon["x-revisions"] = {} + end + + vaddon.name = name + vaddon.version = ver + vaddon["x-branch"] = branch + vaddon["x-revision"] = vrev + vaddon["x-revisions"][file] = rev + vaddon["x-embedded"] = embed + vaddon["x-swatter-extra"] = addition + + if SetAddOnDetail then + SetAddOnDetail(name, vaddon) + end + + return vaddon, file, rev + end + end +end diff --git a/Auc-Advanced/Support/LibTickTock.lua b/Auc-Advanced/Support/LibTickTock.lua new file mode 100644 index 0000000..caa6222 --- /dev/null +++ b/Auc-Advanced/Support/LibTickTock.lua @@ -0,0 +1,121 @@ +--[[ +-- LibTickTock.lua +-- Timer functionality +-- Written by Ken Allan +-- This code is hereby released into the Public Domain without warranty. +-- +-- Usage: +-- local LibTickTock = LibStub("LibTickTock") +-- local timer = LibTickTock:New() +-- +-- timer.func = myFunction +-- timer:RegisterEvent("EVENT_NAME") +-- timer:Wait() +-- timer:CountDown(1.5) +-- -- The timer will start when event fires or in 1.5 seconds +-- -- If function is not set, or returns false, the timer will stop +-- timer:Start() -- Manually start the timer +-- timer:Stop() -- Manually stop the timer/countdown/waits +--]] + +local LIBRARY_VERSION_MAJOR = "LibTickTock" +local LIBRARY_VERSION_MINOR = 1 + + +--[[----------------------------------------------------------------- + +LibStub is a simple versioning stub meant for use in Libraries. +See for more info. +LibStub is hereby placed in the Public Domain. +Credits: + Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke + +--]]----------------------------------------------------------------- +do + local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 + local LibStub = _G[LIBSTUB_MAJOR] + + if not LibStub or LibStub.minor < LIBSTUB_MINOR then + LibStub = LibStub or {libs = {}, minors = {} } + _G[LIBSTUB_MAJOR] = LibStub + LibStub.minor = LIBSTUB_MINOR + + function LibStub:NewLibrary(major, minor) + assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)") + minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.") + + local oldminor = self.minors[major] + if oldminor and oldminor >= minor then return nil end + self.minors[major], self.libs[major] = minor, self.libs[major] or {} + return self.libs[major], oldminor + end + + function LibStub:GetLibrary(major, silent) + if not self.libs[major] and not silent then + error(("Cannot find a library instance of %q."):format(tostring(major)), 2) + end + return self.libs[major], self.minors[major] + end + + function LibStub:IterateLibraries() return pairs(self.libs) end + setmetatable(LibStub, { __call = LibStub.GetLibrary }) + end +end +--[End of LibStub]--------------------------------------------------- + +local lib = LibStub:NewLibrary(LIBRARY_VERSION_MAJOR, LIBRARY_VERSION_MINOR) +if not lib then return end + +lib.kit={} + +function lib:New() + local timer = CreateFrame("Frame", nil, UIParent) + for k,v in pairs(self.kit) do + timer[k] = v + end + return timer +end + +function lib.kit:OnUpdate(delay) + -- Check if we're counting down + if self.countdown and self.countdown > 0 then + self.countdown = self.countdown - delay + -- Check if we've just finished counting down + elseif self.countdown then + self.countdown = nil + self:Start() + -- Process the function + elseif (self.func and self.func()) then + return + -- Otherwise stop the timer + else + self:Stop() + end +end + +function lib.kit:Start() + -- Clear the auction list update event waiting and begin ticking + self:SetScript("OnEvent", nil) + self:SetScript("OnUpdate", self.OnUpdate) + if self.feedback then self.feedback("Started timer") end +end + +function lib.kit:Stop() + -- Stop ticking and waiting for auction list update + self:SetScript("OnEvent", nil) + self:SetScript("OnUpdate", nil) + if self.feedback then self.feedback("Stopped timer") end +end + +function lib.kit:CountDown(n) + self.countdown = n + self:SetScript("OnUpdate", self.OnUpdate) + if self.feedback then self.feedback("Beginning countdown of", n, "seconds") end +end + +function lib.kit:Wait() + -- Wait for the auction list to update then start the timer + self:SetScript("OnEvent", self.Start) + if self.feedback then self.feedback("Timer is waiting for event") end +end + diff --git a/Auc-Advanced/Support/Load.xml b/Auc-Advanced/Support/Load.xml new file mode 100644 index 0000000..fc95950 --- /dev/null +++ b/Auc-Advanced/Support/Load.xml @@ -0,0 +1,6 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + self:SetFrameLevel(self:GetFrameLevel() + 3 ); + ScrollFrame_OnLoad(self); + + + FauxScrollFrame_OnVerticalScroll(self, offset, 16, BF_IgnoreList_Update); + + + + + + diff --git a/Auc-ScanData/Auc-ScanData.toc b/Auc-ScanData/Auc-ScanData.toc new file mode 100644 index 0000000..53516b7 --- /dev/null +++ b/Auc-ScanData/Auc-ScanData.toc @@ -0,0 +1,17 @@ +## Title: Auc:ScanData +## Notes: Allows Auctioneer to delay loading the, probably, large auction scan dataset until it is needed. [5.9.4961] This AddOn is licensed under the GNU GPL, see GPL.txt for details. +## +## Interface: 40000 +## LoadOnDemand: 1 +## SavedVariables: AucScanData +## Dependencies: Auc-Advanced +## OptionalDeps: Auc-Util-PriceLevel +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Auc-ScanData.toc 4933 2010-10-13 17:16:14Z Nechckn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## +StringRope.lua +ScanData.lua diff --git a/Auc-ScanData/ScanData.lua b/Auc-ScanData/ScanData.lua new file mode 100644 index 0000000..cd28037 --- /dev/null +++ b/Auc-ScanData/ScanData.lua @@ -0,0 +1,545 @@ +--[[ + Auctioneer - ScanData + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: ScanData.lua 4840 2010-08-04 21:44:00Z 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 libType, libName = "Util", "ScanData" +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end + +local DATABASE_VERSION = 1.3 +local INTERFACE_VERSION = "A" -- must match CoreScan's SCANDATA_VERSION + +local aucPrint,decode,_,_,replicate,empty,get,set,default,debugPrint,fill = AucAdvanced.GetModuleLocals() + +private.distributionCache = {} +private.worthCache = {} + +local Const = AucAdvanced.Const +local QueryImage = AucAdvanced.API.QueryImage +local PriceCalcLevel = AucAdvanced.Modules.Util.PriceLevel and AucAdvanced.Modules.Util.PriceLevel.CalcLevel + +local type = type +local pairs = pairs +local format = format +local floor = floor +local tostring, strjoin = tostring, strjoin +local tinsert, tremove, tconcat, unpack, wipe = tinsert, tremove, table.concat, unpack, wipe + +local colorDist = { + exact = { red=0, orange=0, yellow=0, green=0, blue=0 }, + suffix = { red=0, orange=0, yellow=0, green=0, blue=0 }, + base = { red=0, orange=0, yellow=0, green=0, blue=0 }, + stack = { }, + all = { red=0, orange=0, yellow=0, green=0, blue=0 }, +} + +--[[ MODULE FUNCTIONS ]]-- + +lib.Processors = {} +--[[ +function lib.Processors.tooltip(callbackType, ...) + private.ProcessTooltip(...) +end +--]] +function lib.Processors.scanstats() + wipe(private.distributionCache) + wipe(private.worthCache) +end +function lib.Processors.load(callbackType, addon) + if addon == "auc-scandata" then + if private.OnLoad then + private.OnLoad() + end + end +end + + +local tmp = {} +function lib.Colored(doIt, counts, alt, shorten) + local n=0 + if (counts.blue > 0) then + n=n+1 + if shorten and counts.blue>=1000 then + tmp[n] = format("|cff3399ff%dk|r", floor(counts.blue/1000+0.5)) + else + tmp[n] = format("|cff3399ff%d|r", counts.blue) + end + end + if (counts.green > 0) then + n=n+1 + if shorten and counts.green>=1000 then + tmp[n] = format("|cff33ff44%dk|r", floor(counts.green/1000+0.5)) + else + tmp[n] = format("|cff33ff44%d|r", counts.green) + end + end + if (counts.yellow > 0) then + n=n+1 + if shorten and counts.yellow>=1000 then + tmp[n] = format("|cffffff00%dk|r", floor(counts.yellow/1000+0.5)) + else + tmp[n] = format("|cffffff00%d|r", counts.yellow) + end + end + if (counts.orange > 0) then + n=n+1 + if shorten and counts.orange>=1000 then + tmp[n] = format("|cffff9900%dk|r", floor(counts.orange/1000+0.5)) + else + tmp[n] = format("|cffff9900%d|r", counts.orange) + end + end + if (counts.red > 0) then + n=n+1 + if shorten and counts.red>=1000 then + tmp[n] = format("|cffff0000%dk|r", floor(counts.red/1000+0.5)) + else + tmp[n] = format("|cffff0000%d|r", counts.red) + end + end + local text = tconcat(tmp, " / ", 1, n) + if alt then + if text and text ~= "" then + text = "( "..text.." )" + else + text = alt + end + end + return text +end + +local query3 = {} -- 3 fields itemId, suffix & factor +function lib.GetImageCounts(hyperlink, maxPrice, items, serverKey) + if type(hyperlink) == "number" then + query3.itemId = hyperlink + query3.suffix = 0 + query3.factor = 0 + else + local iType, iID, iSuffix, iFactor = decode(hyperlink) + if iType == "item" then + query3.itemId = iID + query3.suffix = iSuffix + query3.factor = iFactor + else + return + end + end + local image = QueryImage(query3, serverKey) + + local totalBid, totalBuy = 0, 0 + + for i=1, #image do + local item = image[i] + local count = item[Const.COUNT] + local bid = item[Const.PRICE] + local buy = item[Const.BUYOUT] + + local matched = false + if maxPrice then + if buy > 0 and buy <= maxPrice then + totalBuy = totalBuy + count + matched = true + elseif bid <= maxPrice then + totalBid = totalBid + count + matched = true + end + else + if buy > 0 then + totalBuy = totalBuy + count + matched = true + else + totalBid = totalBid + count + matched = true + end + end + if items and matched then + tinsert(items, item) + end + end + + return totalBuy, totalBid +end + +local query1 = {} -- only 1 field itemId +function lib.GetDistribution(hyperlink) + local iType, iID, iSuffix, iFactor = decode(hyperlink) + if iType ~= "item" then return end + local sig = strjoin(":", iID, iSuffix, iFactor) + if private.distributionCache[sig] then return unpack(private.distributionCache[sig]) end + + local exact, suffix, base, myColors = 0,0,0,{} + for k,v in pairs(colorDist) do + myColors[k] = {} + for c,n in pairs(v) do + myColors[k][c] = 0 + end + end + + query1.itemId = iID + local image = QueryImage(query1) + local sigTemplate = iID..":%d:%d" + for i=1, #image do + local item = image[i] + local vSuffix = item[Const.SUFFIX] + local vFactor = item[Const.FACTOR] + local vCount = item[Const.COUNT] + + local vColor + if (PriceCalcLevel) then + local _ + local vLink = item[Const.LINK] + local vBid = item[Const.PRICE] + local vBuy = item[Const.BUYOUT] + local vSig = sigTemplate:format(vSuffix, vFactor) + _,_,_,_,_, vColor, private.worthCache[vSig] = PriceCalcLevel(vLink, vCount, vBid, vBuy, private.worthCache[vSig]) + end + + if (vSuffix == iSuffix) then + if (vFactor == iFactor) then + exact = exact + vCount + if (vColor) then + myColors.exact[vColor] = myColors.exact[vColor] + vCount + end + else + suffix = suffix + vCount + if (vColor) then + myColors.suffix[vColor] = myColors.suffix[vColor] + vCount + end + end + else + base = base + vCount + if (vColor) then + myColors.base[vColor] = myColors.base[vColor] + vCount + end + end + if (vColor) then + myColors.all[vColor] = myColors.all[vColor] + vCount + -- Set up colours per stack size as well. + if not myColors.stack[vCount] then myColors.stack[vCount] = { red=0, orange=0, yellow=0, green=0, blue=0 } end + myColors.stack[vCount][vColor] = myColors.stack[vCount][vColor] + vCount + end + end + + private.distributionCache[sig] = {exact, suffix, base, myColors} + return exact, suffix, base, myColors +end + +function lib.Processors.tooltip(callbackType, tooltip, name, hyperlink, quality, quantity, cost) + if not get("scandata.tooltip.display") then return end + + tooltip:SetColor(0.3, 0.9, 0.8) + + local doColor = true + local exact, suffix, base, dist = lib.GetDistribution(hyperlink) + + if base+suffix+exact <= 0 then + tooltip:AddLine("No matches in image.") + else + if get("scandata.tooltip.modifier") and IsShiftKeyDown() then + tooltip:AddLine("Items in image:") + if (exact > 0) then + tooltip:AddLine(" |cffddeeff"..exact.."|r exact "..lib.Colored(doColor, dist.exact, "matches")) + end + if (suffix > 0) then + tooltip:AddLine(" |cffddeeff"..suffix.."|r suffix "..lib.Colored(doColor, dist.suffix, "matches")) + end + if (base > 0) then + tooltip:AddLine(" |cffddeeff"..base.."|r base "..lib.Colored(doColor, dist.base, "matches")) + end + if (dist.stack) then + for stackSize, stackColor in pairs(dist.stack) do + tooltip:AddLine(" Stacks of "..stackSize.." "..lib.Colored(doColor, stackColor, "in image")) + end + end + else + if (suffix+base > 0) then + tooltip:AddLine("|cffddeeff"..exact.." +"..(suffix+base).."|r matches "..lib.Colored(doColor, dist.all, "in image")) + else + tooltip:AddLine("|cffddeeff"..exact.."|r matches "..lib.Colored(doColor, dist.exact, "in image")) + end + end + end +end + +--[[ DATABASE FUNCTIONS ]]-- +function lib.GetAddOnInfo() + return private.isLoaded, INTERFACE_VERSION +end + +private.dataCache = {} +function lib.GetScanData(serverKey) + local cache = private.dataCache[serverKey] + if cache then return cache end + + local realm, faction = AucAdvanced.SplitServerKey(serverKey) + if not realm then + debugPrint("AucScanData: invalid serverKey passed to GetScanData: "..tostring(serverKey), "ScanData", "Invalid serverKey", "Error") + return + end + + local realmdata = AucScanData.scans[realm] + if not realmdata then return end -- not in database + + local livedata = serverKey == Const.ServerKeyHome or serverKey == Const.ServerKeyNeutral -- 'live' data can be changed by scanning + local scandata = realmdata[faction] + if scandata then + if not livedata then + -- Copy scandata info into a clone table and call Unpack on that + -- The original does not get unpacked, so does not need repacking + local clone = { + image = scandata.image, -- will be overwritten by unpack + ropes = scandata.ropes, -- will be deleted by unpack + scanstats = replicate(scandata.scanstats) + } + scandata = clone + end + if type(scandata.scanstats) ~= "table" then + scandata.scanstats = {ImageUpdated = scandata.time or time()} + end + if not scandata.image then + scandata.image = {} + scandata.scanstats.ImageUpdated = time() + end + -- delete obsolete entries + scandata.nextID = nil + scandata.time = nil + scandata.LastFullScan = nil + scandata.LastGetAll = nil + else + scandata = {image = {}, scanstats = {ImageUpdated = time()} } + if livedata then + realmdata[faction] = scandata + end + end + + private.Unpack(scandata) + private.dataCache[serverKey] = scandata + return scandata +end + +function lib.ClearScanData(command) + local report, serverKey + local keyword, extra = "faction", "" -- default + + if type(command) == "string" then + local _, ind, key = strfind(command, "(%S+)") + if key then + key = AucAdvanced.API.IsKeyword(key) + if key then + keyword = key -- recognised keyword + extra = strtrim(strsub(command, ind+1)) + else + extra = strtrim(command) -- processor will try to resolve whole command + end + end + elseif command then -- only valid types are string or nil + error("Unrecognised parameter type to ClearScanData: "..type(command)..":"..tostring(command)) + end + + if keyword == "ALL" then + if extra == "" then + wipe(AucScanData.scans) + report = "All realms" + end + else + if keyword == "server" then + if extra == "" then extra = Const.PlayerRealm end + elseif keyword == "faction" then + if extra == "" then extra = AucAdvanced.GetFaction() end + end + if AucScanData.scans[extra] then -- it's a realm name in our database + AucScanData.scans[extra] = nil + report = extra + else + local fac = AucAdvanced.IsFaction(extra) + if fac then -- convert faction group to serverKey + extra = Const.PlayerRealm.."-"..fac + end + local realm, faction, text = AucAdvanced.SplitServerKey(extra) + if faction and AucScanData.scans[realm] then + AucScanData.scans[realm][faction] = nil + report = text + serverKey = extra + end + end + end + + wipe(private.dataCache) + -- Our functions expect home faction to exist - create a new one if it has just been deleted + if not AucScanData.scans[Const.PlayerRealm] then AucScanData.scans[Const.PlayerRealm] = {} end + lib.GetScanData(Const.ServerKeyHome) -- force create (if needed) and put back in cache + if report then + aucPrint("Auctioneer: ScanData cleared for {{"..report.."}}.") + local clearstats = { + source = "clear", + clearType = "scandata", + clearRequest = command, + clearReport = report, + serverKey = serverKey, + } + AucAdvanced.SendProcessorMessage("scanstats", clearstats) -- notify modules to flush caches + else + aucPrint("Auctioneer: Unable to clear ScanData for {{"..command.."}}") + end +end + +function private.Unpack(scandata) + if type(scandata.image) == "string" then + if scandata.image ~= "rope" then + scandata.ropes = { scandata.image } + end + + scandata.image = {} + for pos, rope in ipairs(scandata.ropes) do + local loader, err = loadstring(rope) + if loader then + local test, items = pcall(loader) + if test then + for pos, item in ipairs(items) do + tinsert(scandata.image, item) + end + err = nil + else + err = items + end + end + if err then + aucPrint("Error loading scan image: {{", err, "}}") + -- if we get an error from any rope, assume the whole packed image is corrupt + scandata.image = {} + scandata.scanstats.ImageUpdated = time() + break + end + end + elseif type(scandata.image) ~= "table" then + scandata.image = {} + scandata.scanstats.ImageUpdated = time() + end + scandata.ropes = nil +end + +function private.OnLoad() + private.OnLoad = nil + private.UpgradeDB() + if not AucScanData.scans[Const.PlayerRealm] then AucScanData.scans[Const.PlayerRealm] = {} end + lib.GetScanData(Const.ServerKeyHome) -- force unpack of home faction data + private.isLoaded = true +end + +function private.UpgradeDB() + private.UpgradeDB = nil + + if AucScanData then + if type(AucScanData.scans) ~= "table" then AucScanData.scans = {} end + if AucScanData.Version == DATABASE_VERSION then return end + + if AucScanData.Version == "1.2" then + -- version "1.2" to version 1.3 + -- Database structure is virtually the same, we won't try to update the whole database here + -- Each time GetScanData is called it will check/update that record as needed + aucPrint("Auc-ScanData is upgrading database version 1.2 to 1.3") + AucScanData.Version = DATABASE_VERSION + return + end + + -- Unknown version - wipe and start from fresh + aucPrint("Auc-ScanData database error: unknown version, resetting database") + wipe(AucScanData.scans) + AucScanData.Version = DATABASE_VERSION + else + AucScanData = { Version = DATABASE_VERSION, scans = {} } + end +end + +function lib.OnUnload() + local StringRope = LibStub:GetLibrary("StringRope") + local rope = StringRope:New(-1) + + local maxLen = 2^22 + + if not (AucScanData and AucScanData.scans) then return end + + -- Convert all image data to loadstring strings + for server, sData in pairs(AucScanData.scans) do + for faction, fData in pairs(sData) do + if fData.image and type(fData.image) == "table" then + fData.ropes = {} + rope:Add("return {") + local fCount = #fData.image + for i = 1, fCount do + local item = fData.image[i] + if item and type(item) == "table" then + rope:Add("{") + local pos = 1 + while item[pos] or item[pos+1] or item[pos+2] or item[pos+3] do + local v = item[pos] + if v == nil then + rope:Add("nil,") + else + local t = type(v) + if t == "string" then + rope:Add(("%q,"):format(v)) + elseif t == "number" then + rope:Add(v..",") + elseif t == "boolean" then + rope:Add(tostring(v)..",") + else + rope:Add("nil--[["..t.."]],") + end + end + pos = pos + 1 + end + rope:Add("},") + elseif item == nil then + rope:Add("nil,") + else + rope:Add("nil--[["..type(item).."]],") + end + if rope.len and rope.len > maxLen then + rope:Add("}"); + tinsert(fData.ropes, rope:Get()) + rope:Clear() + rope:Add("return {") + end + end + rope:Add("}") + fData.image = "rope" + tinsert(fData.ropes, rope:Get()) + rope:Clear() + end + end + end +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-ScanData/ScanData.lua $", "$Rev: 4840 $") diff --git a/Auc-ScanData/StringRope.lua b/Auc-ScanData/StringRope.lua new file mode 100644 index 0000000..37ef909 --- /dev/null +++ b/Auc-ScanData/StringRope.lua @@ -0,0 +1,141 @@ +--[[ + StringRope Lib for World of Warcraft (tm) + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: StringRope.lua 3938 2008-12-27 09:35:32Z norganna $ + URL: http://norganna.org + + 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 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 +]] + +local LIBRARY_VERSION_MAJOR = "StringRope" +local LIBRARY_VERSION_MINOR = 5 + +--[[----------------------------------------------------------------- + +LibStub is a simple versioning stub meant for use in Libraries. +See for more info. +LibStub is hereby placed in the Public Domain. +Credits: + Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke + +--]]----------------------------------------------------------------- +do + local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 + local LibStub = _G[LIBSTUB_MAJOR] + + if not LibStub or LibStub.minor < LIBSTUB_MINOR then + LibStub = LibStub or {libs = {}, minors = {} } + _G[LIBSTUB_MAJOR] = LibStub + LibStub.minor = LIBSTUB_MINOR + + function LibStub:NewLibrary(major, minor) + assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)") + minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.") + + local oldminor = self.minors[major] + if oldminor and oldminor >= minor then return nil end + self.minors[major], self.libs[major] = minor, self.libs[major] or {} + return self.libs[major], oldminor + end + + function LibStub:GetLibrary(major, silent) + if not self.libs[major] and not silent then + error(("Cannot find a library instance of %q."):format(tostring(major)), 2) + end + return self.libs[major], self.minors[major] + end + + function LibStub:IterateLibraries() return pairs(self.libs) end + setmetatable(LibStub, { __call = LibStub.GetLibrary }) + end +end +--[End of LibStub]--------------------------------------------------- + +local lib = LibStub:NewLibrary(LIBRARY_VERSION_MAJOR, LIBRARY_VERSION_MINOR) +if not lib then return end + +--Local references of used functions +local pairs = pairs +local tconcat = table.concat +local tinsert = table.insert + +-- Create a new StringRope instance +function lib:New(size) + if not size then size = 512 end + if size < 0 then size = 65500 end + if size > 65500 then size = 65500 end + if size < 25 then size = 25 end + local new = { strings = {}, size = size, len = 0 } + for k,v in pairs(self.kit) do + new[k] = v + end + return new +end + +-- Holds the functions to be copied to new StringRope instances +lib.kit = {} + +-- Clears the StringRope instance +function lib.kit:Clear() + local size = #self.strings + for i = size, 1, -1 do self.strings[i] = nil end + self.len = 0 +end +-- Checks to see if the rope is empty +function lib.kit:IsEmpty() + return (#self.strings == 0) +end +-- Gets the current StringRope instance +function lib.kit:Get() + return tconcat(self.strings) +end +-- Adds a substring to the StringRope instance +function lib.kit:Add(text) + if #self.strings >= self.size then + local text = self:Get() + self:Clear() + self:Add(text) + end + self.len = self.len + tostring(text):len() + return tinsert(self.strings, text) +end +-- Adds a number of substrings to the StringRope instance +function lib.kit:AddMultiple(...) + local v + local n = select("#", ...) + for i = 1, n do + v = select(i, ...) + self:Add(v) + end +end +-- Adds a number of substrings to the StringRope instance, +-- all delimited by a given character. +function lib.kit:AddDelimited(delimiter, ...) + local v + local n = select("#", ...) + for i = 1, n do + v = select(i, ...) + if i > 1 then self:Add(delimiter) end + self:Add(v) + end +end diff --git a/Auc-Stat-Histogram/Auc-Stat-Histogram.toc b/Auc-Stat-Histogram/Auc-Stat-Histogram.toc new file mode 100644 index 0000000..12e8570 --- /dev/null +++ b/Auc-Stat-Histogram/Auc-Stat-Histogram.toc @@ -0,0 +1,18 @@ +## Title: Auc:Stat:Histogram +## Notes: Statistics extension for Auctioneer that uses a histogram data collection method. +## +## Interface: 40000 +## LoadOnDemand: 0 +## Disabled: 1 +## Dependencies: Auc-Advanced +## SavedVariables: AucAdvancedStatHistogramData, AucAdvancedStatHistogramTotalData +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Auc-Stat-Histogram.toc 4933 2010-10-13 17:16:14Z Nechckn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## X-Max-Interface: 40000 +## + +StatHistogram.lua diff --git a/Auc-Stat-Histogram/StatHistogram.lua b/Auc-Stat-Histogram/StatHistogram.lua new file mode 100644 index 0000000..ecf5750 --- /dev/null +++ b/Auc-Stat-Histogram/StatHistogram.lua @@ -0,0 +1,843 @@ +--[[ + Auctioneer - Histogram Statistics module + Version: 5.7.4568 (KillerKoala) + Revision: $Id: StatHistogram.lua 4840 2010-08-04 21:44:00Z 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 libType, libName = "Stat", "Histogram" +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end + +local aucPrint,decode,_,_,replicate,empty,get,set,default,debugPrint,fill, _TRANS = AucAdvanced.GetModuleLocals() +local tonumber,pairs,type,setmetatable=tonumber,pairs,type,setmetatable +local strsplit,strjoin = strsplit,strjoin +local min,max,abs,ceil,floor = min,max,abs,ceil,floor +local concat = table.concat +local wipe,unpack = wipe,unpack + +local GetFaction = AucAdvanced.GetFaction + +local data +local totaldata +local stattable = {} +local PDcurve = {} +local newstats = {} +local array = {} +local frame +local pricecache + +function lib.CommandHandler(command, ...) + if (not data) then private.makeData() end + local serverKey = GetFaction() + local _,_,keyText = AucAdvanced.SplitServerKey(serverKey) + if (command == "help") then + aucPrint(_TRANS('SHTG_Help_SlashHelp1') )--Help for Auctioneer - Histogram + local line = AucAdvanced.Config.GetCommandLead(libType, libName) + aucPrint(line, "help}} - ".._TRANS('SHTG_Help_SlashHelp2') ) -- this Histogram help + aucPrint(line, "clear}} - ".._TRANS('SHTG_Help_SlashHelp3'):format(keyText) ) --clear current %s Histogram price database + elseif (command == "clear") then + lib.ClearData(serverKey) + end +end + +function lib.Processor(callbackType, ...) + if (not data) then private.makeData() end + if (callbackType == "tooltip") then + lib.ProcessTooltip(...) + elseif (callbackType == "config") then + --Called when you should build your Configator tab. + private.SetupConfigGui(...) + elseif (callbackType == "load") then + lib.OnLoad(...) + elseif (callbackType == "scanstats") then + pricecache = nil + elseif callbackType == "auctionclose" then + pricecache = nil -- not actually needed, just conserving RAM + end +end + +lib.Processors = {} +function lib.Processors.tooltip(callbackType, ...) + if (not data) then private.makeData() end + lib.ProcessTooltip(...) +end +function lib.Processors.config(callbackType, ...) + if (not data) then private.makeData() end + --Called when you should build your Configator tab. + private.SetupConfigGui(...) +end +function lib.Processors.load(callbackType, ...) + if (not data) then private.makeData() end + lib.OnLoad(...) +end +function lib.Processors.scanstats(callbackType, ...) + if (not data) then private.makeData() end + pricecache = nil +end +function lib.Processors.auctionclose(callbackType, ...) + if (not data) then private.makeData() end + pricecache = nil -- not actually needed, just conserving RAM +end + + +function private.GetPriceData() + if not stattable["count"] then + debugPrint("GetPriceData: No stattable", libType.."-"..libName, "Warning") + return + end + local median = 0 + local Qone = 0 + local Qthree = 0 + local percent40, percent30 = 0, 0 + local count = stattable["count"] + local refactored = false + local recount = 0 + --now find the Q1, median, and Q3 values + if stattable["min"] == stattable["max"] then + --no need to do fancy median calculations + median = stattable["min"]*stattable["step"] + --count = value at the only index we have + count = stattable[stattable["min"]] + stattable["count"] = count + else + for i = stattable["min"], stattable["max"] do + recount = recount + (stattable[i] or 0) + if Qone == 0 and count > 4 then --Q1 is meaningless with very little data + if recount >= count/4 then + Qone = i*stattable["step"] + end + end + if percent30 == 0 then + if recount >= count*0.3 then + percent30 = i*stattable["step"] + end + end + if percent40 == 0 then + if recount >= count*0.4 then + percent40 = i*stattable["step"] + end + end + if median == 0 then + if recount >= count/2 then + median = i*stattable["step"] + end + end + if Qthree == 0 and count > 4 then--Q3 is meaningless with very little data + if recount >= count * 3/4 then + Qthree = i*stattable["step"] + end + end + end + if count ~= recount then + count = recount --We've just rechecked the count, so save the correct value + stattable["count"] = count + refactored = true + end + end + local step = stattable["step"] + if count > 20 then --we've seen enough to get a fairly decent price to base the precision on + if (step > (median/85)) and (step > 1) then + private.refactor(median*3, 300) + refactored = true + elseif step < (median/115) then + private.refactor(median*3, 300) + refactored = true + end + end + return median, Qone, Qthree, step, count, refactored, percent40, percent30 +end + +function lib.GetPrice(link, faction) + if not get("stat.histogram.enable") then return end + wipe(stattable) + local linkType,itemId,property,factor = AucAdvanced.DecodeLink(link) + if (linkType ~= "item") then return end + if (factor and factor ~= 0) then property = property.."x"..factor end + + if not faction then faction = GetFaction() end + if (not data[faction]) or (not data[faction][itemId]) or (not data[faction][itemId][property]) then + debugPrint("GetPrice: No data", libType.."-"..libName, "Info") + return + end + if pricecache and pricecache[faction] and pricecache[faction][itemId] and pricecache[faction][itemId][property] then + return unpack(pricecache[faction][itemId][property]) + end + private.UnpackStats(data[faction][itemId][property]) + local median, Qone, Qthree, step, count, refactored, percent40, percent30 = private.GetPriceData() + if refactored then + --data has been refactored, so we need to repack it + data[faction][itemId][property] = private.PackStats() + --get the updated data + median, Qone, Qthree, step, count = private.GetPriceData() + end + --we're done with the data, so clear the table + wipe(stattable) + if not pricecache then pricecache = {} end + if not pricecache[faction] then pricecache[faction] = {} end + if not pricecache[faction][itemId] then pricecache[faction][itemId] = {} end + pricecache[faction][itemId][property] = {median or false, Qone or false, Qthree or false, step or false, count or false} + return median, Qone, Qthree, step, count, percent40, percent30 +end + + +function lib.GetPriceColumns() + return "Median", "Q1", "Q3", "step", "Seen" +end + +function lib.GetPriceArray(link, faction) + if not get("stat.histogram.enable") then return end + --make sure that array is empty + wipe(array) + local median, Qone, Qthree, step, count = lib.GetPrice(link, faction) + --these are the two values that GetMarketPrice cares about + array.price = median + array.seen = count + --additional data + array.Qone = Qone + array.Qthree = Qthree + array.step = step + + -- Return a temporary array. Data in this array is + -- only valid until this function is called again. + return array +end + +function private.ItemPDF(price) + if not PDcurve["step"] then return 0 end + local index = floor(price/PDcurve["step"]) + if (index >= PDcurve["min"]) and (index <= PDcurve["max"]) then + return PDcurve[index] + else + return 0 + end +end + +function lib.GetItemPDF(link, faction) + if not get("stat.histogram.enable") then return end + wipe(PDcurve) + wipe(stattable) + local linkType,itemId,property,factor = AucAdvanced.DecodeLink(link) + if (linkType ~= "item") then return end + if (factor and factor ~= 0) then property = property.."x"..factor end + + if not faction then faction = GetFaction() end + if not data[faction] then return end + if not data[faction][itemId] then return end + if not data[faction][itemId][property] then return end + local median, Qone, Qthree, step, count, refactored + if pricecache and pricecache[faction] and pricecache[faction][itemId] and pricecache[faction][itemId][property] then + median, Qone, Qthree, step, count = unpack(pricecache[faction][itemId][property]) + end + + private.UnpackStats(data[faction][itemId][property]) + if not count or count == 0 then + median, Qone, Qthree, step, count, refactored = private.GetPriceData() + end + if refactored then + --data has been refactored, so we need to repack it + data[faction][itemId][property] = private.PackStats() + --get the updated data + median, Qone, Qthree, step, count = private.GetPriceData() + end + if not count or count == 0 then + return + end + + if not pricecache then pricecache = {} end + if not pricecache[faction] then pricecache[faction] = {} end + if not pricecache[faction][itemId] then pricecache[faction][itemId] = {} end + pricecache[faction][itemId][property] = {median or false, Qone or false, Qthree or false, step or false, count or false} + + local curcount = 0 + local area = 0 + local targetarea = min(1, count/30) --if count is less than thirty, we're not very sure about the price + + PDcurve["step"] = step + PDcurve["min"] = stattable["min"]-1 + PDcurve["max"] = stattable["max"]+1 + + for i = stattable["min"], stattable["max"] do + curcount = curcount + stattable[i] + if count == stattable[i] then + PDcurve[i] = 1 + else + PDcurve[i] = 1-(abs(2*curcount - count)/count) + end + area = area + step*PDcurve[i] + end + + local areamultiplier = 1 + if area > 0 then + areamultiplier = targetarea/area + end + for i = PDcurve["min"], PDcurve["max"] do + PDcurve[i]= (PDcurve[i] or 0) * areamultiplier + end + return private.ItemPDF, PDcurve["min"]*PDcurve["step"], PDcurve["max"]*PDcurve["step"], targetarea +end + +lib.ScanProcessors = {} +function lib.ScanProcessors.create(operation, itemData, oldData) + if not get("stat.histogram.enable") then return end + if (not data) then private.makeData() end + + -- This function is responsible for processing and storing the stats after each scan + -- Note: itemData gets reused over and over again, so do not make changes to it, or use + -- it in places where you rely on it. Make a deep copy of it if you need it after this + -- function returns. + + -- We're only interested in items with buyouts. + local buyout = itemData.buyoutPrice + if not buyout or buyout == 0 then return end + if (itemData.stackSize > 1) then + buyout = buyout/itemData.stackSize + end + local priceindex + + -- Get the signature of this item and find it's stats. + local linkType,itemId,property,factor = AucAdvanced.DecodeLink(itemData.link) + if (linkType ~= "item") then return end + if (factor and factor ~= 0) then property = property.."x"..factor end + wipe(stattable) + local faction = GetFaction() + if not data[faction] then data[faction] = {} end + if not data[faction][itemId] then data[faction][itemId] = {} end + if data[faction][itemId][property] then + private.UnpackStats(data[faction][itemId][property]) + end + if not stattable["count"] then + --start out with first 20 prices pushing max to 100. This should help prevent losing data due to the first price being way too low + --also keeps data small initially, as we don't need extremely accurate prices with that little data + stattable["step"] = ceil(buyout / 100) + stattable["count"] = 0 + end + priceindex = ceil(buyout / stattable["step"]) + if stattable["count"] <= 20 then + stattable["count"] = stattable["count"] + 1 + --get the refactoring out of the way first, because we're not capping the price yet + --failure to do this now can cause major trouble in the form of massive tables + if priceindex > 100 then + private.refactor(buyout, 100) + priceindex = 100 + end + if not stattable["min"] then + stattable["min"] = priceindex + stattable["max"] = priceindex + stattable[priceindex] = 0 + elseif stattable["min"] > priceindex then + for i = priceindex, (stattable["min"]-1) do + stattable[i] = 0 + end + stattable["min"] = priceindex + end + if not stattable[priceindex] then stattable[priceindex] = 0 end + stattable[priceindex] = stattable[priceindex] + 1 + data[faction][itemId][property] = private.PackStats() + elseif priceindex <= 300 then --we don't want prices too high: they'll bloat the data. If range needs to go higher, we'll refactor later + stattable["count"] = stattable["count"] + 1 + if not stattable["min"] then --first time we've seen this + stattable["min"] = priceindex + stattable["max"] = priceindex + stattable[priceindex] = 0 + elseif stattable["min"] > priceindex then + for i = priceindex, (stattable["min"]-1) do + stattable[i] = 0 + end + stattable["min"] = priceindex + elseif stattable["max"] < priceindex then + for i = (stattable["max"]+1),priceindex do + stattable[i] = 0 + end + stattable["max"] = priceindex + end + if not stattable[priceindex] then stattable[priceindex] = 0 end + stattable[priceindex] = stattable[priceindex] + 1 + data[faction][itemId][property] = private.PackStats() + end + wipe(stattable) +end + +function private.SetupConfigGui(gui) + local id = gui:AddTab(lib.libName, lib.libType.." Modules") + + gui:AddHelp(id, "what histogram stats", + _TRANS('SHTG_Help_WhatHistogramStats') ,--What are Histogram stats? + _TRANS('SHTG_Help_WhatHistogramStatsAnswer') )--Histogram stats record a histogram of past prices. + gui:AddHelp(id, "what advantages", + _TRANS('SHTG_Help_WhatAdvantageHistogram') ,--What advantages does Histogram have? + _TRANS('SHTG_Help_WhatAdvantageHistogramAnswer') )--Histogram stats don't have a limitation to how many, or how long, it can keep data, so it can keep track of high-volume items well + gui:AddHelp(id, "what disadvantage", + _TRANS('SHTG_Help_WhatDisadvantagesHistogram') ,--What disadvantages does Histogram have? + _TRANS('SHTG_Help_WhatDisadvantagesHistogramAnswer') )--Histogram rounds prices slightly to help store them, so there is a slight precision loss. However, it is precise to 1/250th of market price. (an item with market price 250g will have prices stored to the nearest 1g) + + frame = gui.tabs[id].content + private.frame = frame + + frame.slot = frame:CreateTexture(nil, "BORDER") + frame.slot:SetDrawLayer("Artwork") -- or the border shades it + frame.slot:SetPoint("TOPLEFT", frame, "TOPLEFT", 30, -210) + frame.slot:SetWidth(45) + frame.slot:SetHeight(45) + frame.slot:SetTexCoord(0.17, 0.83, 0.17, 0.83) + frame.slot:SetTexture("Interface\\Buttons\\UI-EmptySlot") + function frame.IconClicked() + local objtype, _, link = GetCursorInfo() + ClearCursor() + if objtype == "item" then + lib.SetWorkingItem(link) + else + lib.SetWorkingItem() + end + end + function frame.ClickHook(link) + if not frame.slot:IsVisible() then return end + lib.SetWorkingItem(link) + end + hooksecurefunc("HandleModifiedItemClick", frame.ClickHook) + frame.icon = CreateFrame("Button", nil, frame) + frame.icon:SetPoint("TOPLEFT", frame.slot, "TOPLEFT", 2, -2) + frame.icon:SetPoint("BOTTOMRIGHT", frame.slot, "BOTTOMRIGHT", -2, 2) + frame.icon:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square.blp") + frame.icon:SetScript("OnClick", frame.IconClicked) + frame.icon:SetScript("OnReceiveDrag", frame.IconClicked) + frame.icon:SetScript("OnEnter", function() --set mouseover tooltip + if not frame.link then return end + GameTooltip:SetOwner(frame.icon, "ANCHOR_BOTTOMRIGHT") + GameTooltip:SetHyperlink(frame.link) + end) + frame.icon:SetScript("OnLeave", function() GameTooltip:Hide() end) + + frame.name = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") + frame.name:SetPoint("TOPLEFT", frame.slot, "TOPRIGHT", 5,-2) + frame.name:SetPoint("RIGHT", frame, "RIGHT", -15) + frame.name:SetHeight(20) + frame.name:SetJustifyH("LEFT") + frame.name:SetJustifyV("TOP") + frame.name:SetText("Insert or Alt-Click Item to start") + frame.name:SetTextColor(0.5, 0.5, 0.7) + + frame.bargraph = CreateFrame("Frame", nil, frame) + frame.bargraph:SetPoint("TOPLEFT", frame, "TOPLEFT", 30, -260) + frame.bargraph:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -30, -260) + frame.bargraph:SetHeight(300) + frame.bargraph:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + edgeFile = "Interface/Tooltips/UI-Tooltip-Border", + tile = true, tileSize = 32, edgeSize = 16, + insets = { left = 5, right = 5, top = 5, bottom = 5 } + }) + frame.bargraph:SetBackdropColor(0, 0, 0.0, 0.5) + frame.bargraph.bars = {} + frame.bargraph.pdf = {} + frame.bargraph.max = 100 + local graphwidth = frame.bargraph:GetWidth()-10 + local graphheight = frame.bargraph:GetHeight()-10 + for i = 1, 300 do + frame.bargraph.bars[i] = frame.bargraph:CreateTexture(nil) + local bar = frame.bargraph.bars[i] + bar:SetPoint("BOTTOMLEFT", frame.bargraph, "BOTTOMLEFT", (graphwidth*(i-1)/300)+5, 5) + bar:SetWidth(graphwidth/300) + bar:SetTexture(0.2, 0.8, 0.2) + function bar:SetValue(value) + if value == 0 then value = 0.001 end + self:SetHeight((self:GetParent():GetHeight()-20)*value) + end + bar:SetValue(0) + end + for i = 1, 300 do + frame.bargraph.pdf[i] = frame.bargraph:CreateTexture(nil) + local pdf = frame.bargraph.pdf[i] + pdf.offset = (graphwidth*(i-1)/300)+5 + pdf:SetPoint("BOTTOMLEFT", frame.bargraph, "BOTTOMLEFT", pdf.offset, 50) + pdf:SetWidth(graphwidth/300) + pdf:SetHeight(5) + pdf:SetTexture(.2, .2, 0.8, .6) + function pdf:SetValue(value) + local bottom = (self:GetParent():GetHeight()-20)*value + self:SetPoint("BOTTOMLEFT", frame.bargraph, "BOTTOMLEFT", self.offset, bottom) + end + pdf:SetValue(0) + end + + frame.key = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") + frame.key:SetPoint("BOTTOMRIGHT", frame.bargraph, "TOPRIGHT", 0, 2) + frame.key:SetPoint("TOPLEFT", frame.bargraph, "TOPLEFT", 5, 22) + frame.key:SetJustifyH("RIGHT") + frame.key:SetJustifyV("TOP") + frame.key:SetText("|cff3fff3fRaw Data |cffff3f3fMedian |cff3f3fffPrice Probability") + + frame.med = frame.bargraph:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + frame.med:SetPoint("TOP", frame.bargraph, "BOTTOM", -50, -10) + frame.med:SetPoint("BOTTOM", frame.bargraph, "BOTTOM", -50, -25) + frame.med:SetWidth(150) + + frame.max = frame.bargraph:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + frame.max:SetPoint("TOPRIGHT", frame.bargraph, "BOTTOMRIGHT", 0, -10) + frame.max:SetPoint("BOTTOMLEFT", frame.bargraph, "BOTTOMRIGHT", -150, -25) + + --all options in here will be duplicated in the tooltip frame + function private.addTooltipControls(id) + gui:AddControl(id, "Header", 0, _TRANS('SHTG_Interface_HistogramOptions') )--Histogram options + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + gui:AddControl(id, "Checkbox", 0, 1, "stat.histogram.enable", _TRANS('SHTG_Interface_EnableHistogram') )--Enable Histogram Stats + gui:AddTip(id, _TRANS('SHTG_HelpTooltip_EnableHistogram') )--Allow Histogram to gather and return price data + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + + gui:AddControl(id, "Checkbox", 0, 4, "stat.histogram.tooltip", _TRANS('SHTG_Interface_ShowHistogramTooltips') )--Show Histogram stats in the tooltips? + gui:AddTip(id, _TRANS('SHTG_HelpTooltip_ShowHistogramTooltips') )--Toggle display of stats from the Histogram module on or off + gui:AddControl(id, "Checkbox", 0, 6, "stat.histogram.median", _TRANS('SHTG_Interface_DisplayMedian') )--Display Median + gui:AddTip(id, _TRANS('SHTG_HelpTooltip_DisplayMedian') )--Toggle display of \'Median\' calculation in tooltips on or off + gui:AddControl(id, "Checkbox", 0, 6, "stat.histogram.iqr", _TRANS('SHTG_Interface_DisplayIQR') )--Display IQR + gui:AddTip(id, _TRANS('SHTG_HelpTooltip_DisplayIQR') )--Toggle display of \'IQR\' calculation in tooltips on or off. See help for further explanation. + gui:AddControl(id, "Checkbox", 0, 6, "stat.histogram.precision", _TRANS('SHTG_Interface_DisplayPrecision') )--Display Precision + gui:AddTip(id, _TRANS('SHTG_HelpTooltip_DisplayPrecision') )--Toggle display of \'precision\' calculation in tooltips on or off + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + gui:AddControl(id, "Checkbox", 0, 4, "stat.histogram.quantmul", _TRANS('SHTG_Interface_MultiplyStack') )--Multiply by Stack Size + gui:AddTip(id, _TRANS('SHTG_HelpTooltip_MultiplyStack') )--Multiplies by current Stack Size if on + + gui:AddHelp(id, "what median", + _TRANS('SHTG_Help_WhatMedian') ,--What is the median? + _TRANS('SHTG_Help_WhatMedianAnswer') )--The median value is the value where half of the prices seen are above, and half are below. + gui:AddHelp(id, "what IQR", + _TRANS('SHTG_Help_WhatIQR') ,--What is the IQR? + _TRANS('SHTG_Help_WhatIQRAnswer') )--The IQR is a measure of spread. The middle half of the prices seen is confined with the range of IQR. An item with median 100g, and IQR 10g, has very consistent data. If the IQR was 100g, the prices are all over the place. + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + end + --This is the Tooltip tab provided by aucadvnced so all tooltip configuration is in one place + local tooltipID = AucAdvanced.Settings.Gui.tooltipID + + --now we create a duplicate of these in the tooltip frame + private.addTooltipControls(id) + if tooltipID then private.addTooltipControls(tooltipID) end + + gui:MakeScrollable(id) + + gui:AddControl(id, "Subhead", 0, _TRANS('SHTG_Interface_ItemDataViewer') )--Item Data Viewer + +end + +function lib.SetWorkingItem(link) + --clear the graph + frame.name:SetText(_TRANS('SHTG_Interface_InsertItemStart') )--Insert or Alt-Click Item to start + frame.icon:SetNormalTexture(nil) --set icon texture + frame.link = nil + for i = 1,300 do + frame.bargraph.bars[i]:SetValue(0) + frame.bargraph.bars[i]:SetTexture(0.2, 0.8, 0.2) + frame.bargraph.pdf[i]:SetValue(0) + end + + local linkType,itemId,property,factor = AucAdvanced.DecodeLink(link) + if (linkType ~= "item") then return end + local name, _, _, _, _, _, _, _, _, texture = GetItemInfo(link) + if not name or not texture then return end + frame.name:SetText(link) + frame.link = link + frame.icon:SetNormalTexture(texture) --set icon texture + + wipe(stattable) + if (factor and factor ~= 0) then property = property.."x"..factor end + + local faction = GetFaction() + if (not data[faction]) or (not data[faction][itemId]) or (not data[faction][itemId][property]) then + debugPrint("SetWorkingItem: No data", libType.."-"..libName, "Info") + return + end + private.UnpackStats(data[faction][itemId][property]) + + local maxvalue = 0 + local indexes = 0 + local count = stattable["count"] + local recount = 0 + local median = 0 + for i = stattable["min"], stattable["max"] do + indexes = indexes +1 + if stattable[i] > maxvalue then maxvalue = stattable[i] end + recount = recount + stattable[i] + if median == 0 then + if recount >= count/2 then + median = i + frame.bargraph.bars[i]:SetTexture(0.8, 0.2, 0.2) + end + end + end + for i = stattable["min"], stattable["max"] do + frame.bargraph.bars[i]:SetValue(stattable[i]/maxvalue) + end + + --now show the PD curve + wipe(PDcurve) + PDcurve["step"] = stattable["step"] + PDcurve["min"] = stattable["min"] + PDcurve["max"] = stattable["max"] + local curcount = 0 + count = stattable["count"] + maxvalue = 0 + for i = stattable["min"], stattable["max"] do + curcount = curcount + stattable[i] + if count == stattable[i] then + PDcurve[i] = 1 + else + PDcurve[i] = 1-(abs(2*curcount - count)/count) + end + if PDcurve[i] > maxvalue then + maxvalue = PDcurve[i] + end + end + for i = PDcurve["min"], PDcurve["max"] do + PDcurve[i]= (PDcurve[i] or 0) + end + + for i = 1,300 do + if (i >= PDcurve["min"]) and (i <= PDcurve["max"]) then + frame.bargraph.pdf[i]:SetValue(PDcurve[i]) + else + frame.bargraph.pdf[i]:SetValue(0) + end + end + + frame.med:SetText("Median: "..AucAdvanced.Coins(median*stattable["step"], true)) + frame.max:SetText("Max: "..AucAdvanced.Coins(300*stattable["step"], true)) +end + +function lib.ProcessTooltip(tooltip, name, hyperlink, quality, quantity, cost, ...) + -- In this function, you are afforded the opportunity to add data to the tooltip should you so + -- desire. You are passed a hyperlink, and it's up to you to determine whether or what you should + -- display in the tooltip. + + if not get("stat.histogram.tooltip") then return end + + local quantmul = get("stat.histogram.quantmul") + if (not quantmul) or (not quantity) or (quantity < 1) then quantity = 1 end + local median, Qone, Qthree, step, count, percent40, percent30 = lib.GetPrice(hyperlink) + if not count then + count = 0 + end + if median then + if quantity == 1 then + tooltip:AddLine(_TRANS('SHTG_Tooltip_PricesSeenOnce'):format(count) )--Histogram prices: (seen {{%s}}) + else + tooltip:AddLine(_TRANS('SHTG_Tooltip_Prices'):format(quantity, count) )--Histogram prices x {{%s}}) : (seen {{%s}}) + end + local iqr = Qthree-Qone + if get("stat.histogram.median") then + tooltip:AddLine(" ".._TRANS('SHTG_Tooltip_Median'), median*quantity)--median: + if quantity > 1 then + tooltip:AddLine(" ".._TRANS('SHTG_Tooltip_Individually'), median)--(or individually): + end + end + if (iqr > 0) and (get("stat.histogram.iqr")) then + tooltip:AddLine(" ".._TRANS('SHTG_Tooltip_IQR') , iqr*quantity)-- IQR: + end + if get("stat.histogram.precision") then + tooltip:AddLine(" ".._TRANS('SHTG_Tooltip_Precision'), step*quantity)--precision: + end + end +end + +function lib.OnLoad(addon) + private.makeData() + private.makeTotalData() + AucAdvanced.Settings.SetDefault("stat.histogram.tooltip", false) + AucAdvanced.Settings.SetDefault("stat.histogram.median", false) + AucAdvanced.Settings.SetDefault("stat.histogram.iqr", true) + AucAdvanced.Settings.SetDefault("stat.histogram.precision", false) + AucAdvanced.Settings.SetDefault("stat.histogram.quantmul", true) + AucAdvanced.Settings.SetDefault("stat.histogram.enable", true) +end + +function lib.ClearItem(hyperlink, serverKey) + local linkType,itemId,property,factor = AucAdvanced.DecodeLink(hyperlink) + if (linkType ~= "item") then return end + if (factor and factor ~= 0) then property = property.."x"..factor end + + if not serverKey then serverKey = GetFaction() end + if not data[serverKey] then return end + if not data[serverKey][itemId] then return end + if not data[serverKey][itemId][property] then return end + data[serverKey][itemId][property] = nil + local _,_,keyText = AucAdvanced.SplitServerKey(serverKey) + aucPrint(libType.._TRANS('SHTG_Interface_ClearingData'):format(hyperlink, keyText))--- Histogram: clearing data for %s for {{%s}} +end + +function lib.ClearData(serverKey) + if not serverKey then serverKey = GetFaction() end + if AucAdvanced.API.IsKeyword(serverKey, "ALL") then + local version = data.version -- save this so it doesn't get lost in the wipe + wipe(data) + data.version = version + aucPrint(_TRANS('SHTG_Help_SlashHelp5').." {{".._TRANS("ADV_Interface_AllRealms").."}}") --Clearing Histogram stats for // All realms + elseif data[serverKey] then + data[serverKey] = nil + local _,_,keyText = AucAdvanced.SplitServerKey(serverKey) + aucPrint(_TRANS('SHTG_Help_SlashHelp5').." {{"..keyText.."}}") --Clearing Histogram stats for + end +end + +--[[ Local functions ]]-- + +function private.DataLoaded() + -- This function gets called when the data is first loaded. You may do any required maintenence + -- here before the data gets used. + --Forces all data to be refactored if the database hasn't been updated yet + local VERSION = 2 + if (data["version"]) and (data["version"] >= VERSION) then return end + local function findallprices() + aucPrint("Auc-Stat-Histogram: Updating database. Please be patient") + local i = 1 + for faction, itemlist in pairs(data) do + if type(itemlist) == "table" then + for itemId, proplist in pairs(itemlist) do + for prop, datastring in pairs(proplist) do + i = i+1 + wipe(stattable) + private.UnpackStats(datastring) + local _,_,_,_,_,refactored = private.GetPriceData() + if refactored then + data[faction][itemId][prop] = private.PackStats() + end + if i%5000 == 0 then + coroutine.yield() + end + end + end + end + end + aucPrint("Auc-Stat-Histogram: Database is updated. Thank You for your patience") + data["version"] = VERSION + end + local co = coroutine.create(findallprices) + coroutine.resume(co) + local function onupdate(self) + if coroutine.status(co) ~= "dead" then + coroutine.resume(co) + else + self:Hide() -- stops further onupdates + end + end + local onupdateframe = CreateFrame("Frame") + onupdateframe:SetScript("OnUpdate", onupdate) +end + +function private.makeData() + if data then return end + if (not AucAdvancedStatHistogramData) then AucAdvancedStatHistogramData = {} end + data = AucAdvancedStatHistogramData + private.DataLoaded() +end + +function private.makeTotalData() + if totaldata then return end + AucAdvancedStatHistogramTotalData = "" + totaldata = AucAdvancedStatHistogramTotalData + --private.DataLoaded() +end + +function private.UnpackStats(dataItem) + wipe(stattable) + if dataItem then + local firstvalue, maxvalue, step, count, newdataItem = strsplit(";",dataItem) + if not newdataItem then + debugPrint("UnpackStats: dataItem only 4 long", libType.."-"..libName, "Critical") + return + end + stattable["min"] = tonumber(firstvalue) + stattable["max"] = tonumber(maxvalue) + stattable["step"] = tonumber(step) + stattable["count"] = tonumber(count) + local index = stattable["min"] + if not index then + aucPrint(dataItem) + end + for n in newdataItem:gmatch("[0-9]+") do + stattable[index] = tonumber(n) + index = index + 1 + end + else + debugPrint("UnpackStats: No data passed", libType.."-"..libName, "Warning") + end +end + +local meta0 = { __index = function(tbl,key) return 0 end } +function private.PackStats() + + setmetatable(stattable, meta0) -- Instead of looping through and checking for nil->0. /Mikk + + local values = concat(stattable, ",", stattable.min, stattable.max) + + local ret = strjoin(";",stattable.min, stattable.max, stattable.step, stattable.count, values) + + setmetatable(stattable, nil) -- I'm not even sure if this needs to be unset, but I don't want to change the behavior of code I don't fully understand, so unsetting it. /Mikk + + return ret +end + +--private.refactor(pmax, precision) +--pmax is the max for the distribution +--redistributes the price data so that pmax is at precision +--this does cause some loss of accuracy, but should only be necessary every once in a great while +--and increases future accuracy. +--If data points would end up having an index > 750, they get cut off. They're more than 3x market price, and should not be taken into account anyway +--called by the GetPrice function when price is detected as being too far off an index of 250 +--Also called when adding new data early on that would push the max up. +function private.refactor(pmax, precision) + if type(stattable) ~= "table" or type(pmax)~="number" or pmax == 0 then + return + end + wipe(newstats) + newstats["step"] = ceil(pmax/precision) + local conversion = stattable["step"]/newstats["step"] + newstats["min"] = ceil(conversion*stattable["min"]) + newstats["max"] = ceil(conversion*stattable["max"]) + local count = 0 + if newstats["max"] > 300 then + --we need to crop off the top end + newstats["max"] = 300 + stattable["max"] = floor(300/conversion) + end + for i = newstats["min"], newstats["max"] do + newstats[i] = 0 + end + for i = stattable["min"], stattable["max"] do + local j = ceil(conversion*i) + if not newstats[j] then newstats[j] = 0 end + newstats[j]= newstats[j] + stattable[i] + count = count + stattable[i] + end + wipe(stattable) + for i,j in pairs(newstats) do + stattable[i] = j + end + stattable["count"] = count +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Stat-Histogram/StatHistogram.lua $", "$Rev: 4840 $") diff --git a/Auc-Stat-Purchased/Auc-Stat-Purchased.toc b/Auc-Stat-Purchased/Auc-Stat-Purchased.toc new file mode 100644 index 0000000..bfd37e9 --- /dev/null +++ b/Auc-Stat-Purchased/Auc-Stat-Purchased.toc @@ -0,0 +1,18 @@ +## Title: Auc:Stat:Purchased Stats +## Notes: Statistics extension for Auctioneer that performs historical exponential moving averages at the 1, 3, 7 and 14 day intervals, using the item's last known price before expiration and buy-out price if auction ends early. +## +## Interface: 40000 +## LoadOnDemand: 0 +## Disabled: 0 +## Dependencies: Auc-Advanced +## SavedVariables: AucAdvancedStatPurchasedData +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Auc-Stat-Purchased.toc 4933 2010-10-13 17:16:14Z Nechckn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## X-Max-Interface: 40000 +## + +StatPurchased.lua diff --git a/Auc-Stat-Purchased/StatPurchased.lua b/Auc-Stat-Purchased/StatPurchased.lua new file mode 100644 index 0000000..43139b5 --- /dev/null +++ b/Auc-Stat-Purchased/StatPurchased.lua @@ -0,0 +1,668 @@ +--[[ + Auctioneer - StatPurchased + Version: 5.7.4568 (KillerKoala) + Revision: $Id: StatPurchased.lua 4840 2010-08-04 21:44:00Z 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 libType, libName = "Stat", "Purchased" +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end + +-- AucAdvanced locals +local aucPrint,decode,_,_,replicate,empty,get,set,default,debugPrint,fill, _TRANS = AucAdvanced.GetModuleLocals() +local Const = AucAdvanced.Const +local GetFaction = AucAdvanced.GetFaction + +-- globals -> locals +local assert = assert +local tinsert = tinsert +local sqrt = sqrt +local select,ipairs,pairs,unpack,type,wipe = select,ipairs,pairs,unpack,type,wipe +local tonumber = tonumber +local strsplit = strsplit +local time = time +local concat = table.concat +local strmatch = strmatch + +-- Internal variables +local SPRealmData + +local pricecache = setmetatable({}, {__mode="v"}) + +function private.ClearCache() + wipe(pricecache) +end + +function lib.CommandHandler(command, ...) + local serverKey = GetFaction() + local _,_,keyText = AucAdvanced.SplitServerKey(serverKey) + if (command == "help") then + aucPrint(_TRANS('PURC_Help_SlashHelp1') )--Help for Auctioneer - Purchased + local line = AucAdvanced.Config.GetCommandLead(libType, libName) + aucPrint(line, "help}} - ".._TRANS('PURC_Help_SlashHelp2') ) --this Purchased help + aucPrint(line, "clear}} - ".._TRANS('PURC_Help_SlashHelp3'):format(keyText) ) --clear current {{%s}} Purchased price database + aucPrint(line, "push}} - ".._TRANS('PURC_Help_SlashHelp4'):format(keyText) ) --force the {{%s}} Purchased daily stats to archive (start a new day) + elseif (command == _TRANS('clear') ) then + lib.ClearData(serverKey) + elseif (command == _TRANS('push') ) then + aucPrint(_TRANS('PURC_Help_SlashHelp5'):format(keyText) ) --Archiving {{%s}} daily stats and starting a new day + private.PushStats(serverKey) + end +end + +function lib.Processor(callbackType, ...) + if (callbackType == "tooltip") then + private.ProcessTooltip(...) + elseif (callbackType == "config") then + --Called when you should build your Configator tab. + private.SetupConfigGui(...) + elseif callbackType == "scanstats" then + private.ClearCache() + end +end +lib.Processors = {} +function lib.Processors.tooltip(callbackType, ...) + private.ProcessTooltip(...) +end +function lib.Processors.config(callbackType, ...) + --Called when you should build your Configator tab. + private.SetupConfigGui(...) +end +function lib.Processors.scanstats(callbackType, ...) + private.ClearCache() +end + + + +lib.ScanProcessors = {} +function lib.ScanProcessors.delete(operation, itemData, oldData) + local price + + local auctionmaxtime = Const.AucMinTimes[itemData.timeLeft] or 86400 + if (time() - itemData.seenTime <= auctionmaxtime) then + -- Auction deleted earlier than expected expiry time + local buy = itemData.buyoutPrice + if buy and buy > 0 then + -- assume bought out + price = buy + end + --[[ disabled code + -- bid detection code temporarily disabled + -- assumed bids should probably not be treated the same as assumed buyouts with respect to market price + -- needs further development + else -- Auction deleted later than earliest expected expiry time + local bid = itemData.curBid + if bid and bid > 0 then + -- assume won on last bid we saw + price = bid + end + -- end disabled code]] + end + + if not price then return end + + price = price / itemData.stackSize + + local pricedata = private.GetPriceData(GetFaction()) + local itemType, itemId, property, factor = decode(itemData.link) + if (factor ~= 0) then property = property.."x"..factor end + if not pricedata.daily[itemId] then pricedata.daily[itemId] = "" end + local stats = private.UnpackStats(pricedata.daily[itemId]) + if not stats[property] then stats[property] = { 0, 0 } end + stats[property][1] = stats[property][1] + price + stats[property][2] = stats[property][2] + 1 + pricedata.daily[itemId] = private.PackStats(stats) +end + +-- Determines the sample estimated standard deviation based on the deviation +-- of the daily, 3day, 7day, and 14day averages. This is not technically +-- correct because they are not independent samples (the 7 day average +-- includes data from the 3 day and daily averages, for example). Still +-- it provides a good estimation in the presence of lack of data. If you +-- want to discuss the math behind this estimation, find Shirik +-- @param hyperlink The item to look up +-- @param serverKey The realm-faction key from which to look up the data +-- @return The estimated population mean +-- @return The estimated population standard deviation +-- @return The number of views found to base the standard deviation upon + +local dataset = {} + +function private.EstimateStandardDeviation(hyperlink, serverKey) + local dayAverage, avg3, avg7, avg14, _, dayTotal, dayCount, seenDays, seenCount = lib.GetPrice(hyperlink, serverKey) + + if not seenDays then return end + + local count = 0 + if dayAverage then --[[dayAverage should never be nil at this point. It may be 0 if item has not been seen today - does that mess up the stats?]] + count = count + 1 + dataset[count] = dayAverage + end + if seenDays >= 3 then + count = count + 1 + dataset[count] = avg3 + if seenDays >= 7 then + count = count + 1 + dataset[count] = avg7 + if seenDays >= 14 then + count = count + 1 + dataset[count] = avg14 + end + end + end + + if count == 0 then -- No data + aucPrint(_TRANS('PURC_Interface_WarningPurchasedEmpty'):format(hyperlink) )--Warning: Purchased dataset for %s is empty. + return + end + + local mean = 0 + for i=1,count do + mean = mean + dataset[count] + end + mean = mean / count + + local variance = 0 + for i=1,count do + variance = variance + (mean - dataset[count])^2 + end + + return mean, sqrt(variance), count +end + + +local bellCurve = AucAdvanced.API.GenerateBellCurve(); +-- Gets the PDF curve for a given item. This curve indicates +-- the probability of an item's mean price. Uses an estimation +-- of the normally distributed bell curve by performing +-- calculations on the daily, 3-day, 7-day, and 14-day averages +-- stored by SIMP +-- @param hyperlink The item to generate the PDF curve for +-- @param serverKey The realm-faction key from which to look up the data +-- @return The PDF for the requested item, or nil if no data is available +-- @return The lower limit of meaningful data for the PDF (determined +-- as the mean minus 5 standard deviations) +-- @return The upper limit of meaningful data for the PDF (determined +-- as the mean plus 5 standard deviations) +function lib.GetItemPDF(hyperlink, serverKey) + -- TODO: This is an estimate. Can we touch this up later? Especially the stddev==0 case + if not get("stat.purchased.enable") then return end --disable purchased if desired + + -- Calculate the SE estimated standard deviation & mean + local mean, stddev, count = private.EstimateStandardDeviation(hyperlink, serverKey) + + if stddev ~= stddev or mean ~= mean or not mean or mean == 0 then + return ; -- No available data or cannot estimate + end + + if not count or count == 0 then + aucPrint(mean) + aucPrint(stddev) + aucPrint(count) + aucPrint(_TRANS('PURC_Interface_CountBroken') ..hyperlink)--count broken! for + count = 1 + end + -- If the standard deviation is zero, we'll have some issues, so we'll estimate it by saying + -- the std dev is 100% of the mean divided by square root of number of views + if stddev == 0 then stddev = mean / sqrt(count); end + + + -- Calculate the lower and upper bounds as +/- 3 standard deviations + local lower, upper = mean - 3*stddev, mean + 3*stddev; + + bellCurve:SetParameters(mean, stddev); + return bellCurve, lower, upper; +end + +function lib.GetPrice(hyperlink, serverKey) + if not get("stat.purchased.enable") then return end --disable purchased if desired + serverKey = serverKey or GetFaction() + + local data = private.GetPriceData(serverKey) + + local linkType,itemId,property,factor = decode(hyperlink) + if (linkType ~= "item") then return end + if (factor ~= 0) then property = property.."x"..factor end + + local cachesig = serverKey..itemId..":"..property + if pricecache[cachesig] then + local dayAverage, avg3, avg7, avg14, dayTotal, dayCount, seenDays, seenCount = unpack(pricecache[cachesig], 1, 8) + return dayAverage, avg3, avg7, avg14, false, dayTotal, dayCount, seenDays, seenCount + end + + local dayTotal, dayCount, dayAverage = 0,0,0 + local seenDays, seenCount, avg3, avg7, avg14 = 0,0,0,0,0 + + if data.daily[itemId] then + local stats = private.UnpackStats(data.daily[itemId]) + if stats[property] then + dayTotal, dayCount = unpack(stats[property]) + dayAverage = dayTotal/dayCount + end + end + if data.means[itemId] then + local stats = private.UnpackStats(data.means[itemId]) + if stats[property] then + seenDays, seenCount, avg3, avg7, avg14 = unpack(stats[property]) + end + end + pricecache[cachesig] = {dayAverage, avg3, avg7, avg14, dayTotal, dayCount, seenDays, seenCount} + return dayAverage, avg3, avg7, avg14, false, dayTotal, dayCount, seenDays, seenCount +end + +function lib.GetPriceColumns() + return "Daily Avg", "3 Day Avg", "7 Day Avg", "14 Day Avg", false, "Daily Total", "Daily Count", "Seen Days", "Seen Count" +end + +local pricearray={} -- used to return stuff in +function lib.GetPriceArray(hyperlink, serverKey) + if not get("stat.purchased.enable") then return end --disable purchased if desired + wipe(pricearray) + + -- Get our statistics + local dayAverage, avg3, avg7, avg14, _, dayTotal, dayCount, seenDays, seenCount = lib.GetPrice(hyperlink, serverKey) + + -- pricearray.price and pricearray.seen are the ones that most algorithms will look for + pricearray.seen = seenCount + if not get("stat.purchased.reportsafe") then + pricearray.price = avg3 or dayAverage + else + -- Safe mode: prefer longer-running averages for low-volume items + if seenCount>100 and seenCount > seenDays*10 then + pricearray.price = avg3 or dayAverage + -- aucPrint(hyperlink..": seen "..seenCount.." over "..seenDays.. "days. going with avg3") + else + local a3 = avg3 or dayAverage + local a7 = avg7 or a3 + local a14 = avg14 or a7 + if seenCount >= seenDays*7 then + pricearray.price = (a3+a7)/2 + -- aucPrint(hyperlink..": seen "..seenCount.." over "..seenDays.. "days. going with avg(a3,a7)") + else + local mix3 = seenCount / (seenDays*7*2) -- 0.07 for 1/1, 0.5 for 7/1 + local mix14 = 0.5-mix3 + local mix7 = 1-mix3-mix14 -- actually always==0.5 :-) + pricearray.price = a3*mix3 + a7*mix7 + a14*mix14 + -- aucPrint(hyperlink..": seen "..seenCount.." over "..seenDays.. "days. mix3="..mix3.." mix7="..mix7.." mix14="..mix14) + end + end + end + + -- This is additional data + pricearray.avgday = dayAverage + pricearray.avg3 = avg3 + pricearray.avg7 = avg7 + pricearray.avg14 = avg14 + pricearray.daytotal = dayTotal + pricearray.daycount = dayCount + pricearray.seendays = seenDays + + return pricearray +end + +function lib.OnLoad(addon) + if SPRealmData then return end + -- set defaults + default("stat.purchased.tooltip", false) + default("stat.purchased.avg3", false) + default("stat.purchased.avg7", false) + default("stat.purchased.avg14", false) + default("stat.purchased.quantmul", true) + default("stat.purchased.enable", true) + default("stat.purchased.reportsafe", false) + + private.InitData() +end + +function private.SetupConfigGui(gui) + local id = gui:AddTab(lib.libName, lib.libType.." Modules") + --gui:MakeScrollable(id) + + gui:AddHelp(id, "what purchased stats", + _TRANS('PURC_Help_WhatPurchasedStats') ,--What are purchased stats? + _TRANS('PURC_Help_WhatPurchasedStatsAnswer') )--Purchased stats are the numbers that are generated by the Purchased module, the Purchased module attempts to determine if an auction was bought out or purchased on a bid. It also calculates a Moving Average (3/7/14). + + --all options in here will be duplicated in the tooltip frame + function private.addTooltipControls(id) + gui:AddHelp(id, "what moving day average purchased", + _TRANS('PURC_Help_WhatMovingAverage') ,--What does 'moving average' mean? + _TRANS('PURC_Help_WhatMovingAverageAnswer') )--'Moving average means that it places more value on yesterday\'s moving average than today\'s average. The determined amount is then used for tomorrow\'s moving average calculation. + + gui:AddHelp(id, "how day average calculated purchased", + _TRANS('PURC_Help_HowMovingAverage') ,--How is the moving day averages calculated exactly? + _TRANS('PURC_Help_HowMovingAverageAnswer') )--Todays Moving Average is ((X-1)*YesterdaysMovingAverage + TodaysAverage) / X, where X is the number of days (3,7, or 14). + + gui:AddHelp(id, "no day saved purchased", + _TRANS('PURC_Help_DayAverage') ,--So you aren\'t saving a day-to-day average? + _TRANS('PURC_Help_DayAverageAnswer') )--No, that would not only take up space, but heavy calculations on each auction house scan. + + gui:AddHelp(id, "why multiply stack size stddev", + _TRANS('PURC_Help_MultiplyStack') ,--Why have the option to multiply stack size? + _TRANS('PURC_Help_MultiplyStackAnswer') )--The original Stat-Purchased multiplied by the stack size of the item, but some like dealing on a per-item basis. + + gui:AddHelp(id, "report safe safer price prices value low volume item items", + _TRANS('PURC_Help_SaferPrices') ,--How are the \'safer\' prices computed? + _TRANS('PURC_Help_SaferPricesAnswer') )--For anything seen more than 100 times and selling more than 10 items per day (on average), we simply use the 3 day average.\n\nFor others, we value the 7-day average at 50%, and the 3- and 14-day averages at between 0--50% and 50--0%, respectively, depending on how many are seen per day (between 1 and 7). + + + gui:AddControl(id, "Header", 0, libName.." options") + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + + gui:AddControl(id, "Checkbox", 0, 1, "stat.purchased.enable", _TRANS('PURC_Interface_EnablePurchasedStats') )--Enable Purchased Stats + gui:AddTip(id, _TRANS('PURC_HelpTooltip_EnablePurchasedStats') )--Allow Stat Purchased to gather and return price data + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + + gui:AddControl(id, "Checkbox", 0, 4, "stat.purchased.tooltip", _TRANS('PURC_Interface_ShowPurchased') )--Show purchased stats in the tooltips? + gui:AddTip(id, _TRANS('PURC_HelpTooltip_ShowPurchased') )--Toggle display of stats from the Purchased module on or off + + gui:AddControl(id, "Checkbox", 0, 6, "stat.purchased.avg3", _TRANS('PURC_Interface_Toggle3Day') )--Display Moving 3 Day Average + gui:AddTip(id, _TRANS('PURC_HelpTooltip_Toggle3Day') )--Toggle display of 3-Day average from the Purchased module on or off + gui:AddControl(id, "Checkbox", 0, 6, "stat.purchased.avg7", _TRANS('PURC_Interface_Toggle7Day') )--Display Moving 7 Day Average + gui:AddTip(id, _TRANS('PURC_HelpTooltip_Toggle7Day') )--Toggle display of 7-Day average from the Purchased module on or off + gui:AddControl(id, "Checkbox", 0, 6, "stat.purchased.avg14", _TRANS('PURC_Interface_Toggle14Day') )--Display Moving 14 Day Average + gui:AddTip(id, _TRANS('PURC_HelpTooltip_Toggle14Day') )--Toggle display of 14-Day average from the Purchased module on or off + + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + + gui:AddControl(id, "Checkbox", 0, 4, "stat.purchased.reportsafe", _TRANS('PURC_Interface_ReportSaferPrices') )--Report safer prices for low volume items + gui:AddTip(id, _TRANS('PURC_HelpTooltip_ReportSaferPrices') )--Returns longer averages (7-day, or even 14-day) for low-volume items + gui:AddControl(id, "Checkbox", 0, 4, "stat.purchased.quantmul", _TRANS('PURC_Interface_MultiplyStack') )--Multiply by Stack Size + gui:AddTip(id, _TRANS('PURC_HelpTooltip_MultiplyStack') )--Multiplies by current Stack Size if on + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + end + --This is the Tooltip tab provided by aucadvnced so all tooltip configuration is in one place + local tooltipID = AucAdvanced.Settings.Gui.tooltipID + + --now we create a duplicate of these in the tooltip frame + private.addTooltipControls(id) + if tooltipID then private.addTooltipControls(tooltipID) end + +end + +--[[ Local functions ]]-- +function private.ProcessTooltip(tooltip, name, hyperlink, quality, quantity, cost) + if not get("stat.purchased.tooltip") then return end + + if not quantity or quantity < 1 then quantity = 1 end + if not get("stat.purchased.quantmul") then quantity = 1 end + local dayAverage, avg3, avg7, avg14, _, dayTotal, dayCount, seenDays, seenCount = lib.GetPrice(hyperlink) + if (not dayAverage) then return end + + if (seenDays + dayCount > 0) then + tooltip:AddLine(_TRANS('PURC_Tooltip_PurchasedPrices'))--Purchased prices: + + if (seenDays > 0) then + if (dayCount>0) then seenDays = seenDays + 1 end + tooltip:AddLine(" ".._TRANS('PURC_Tooltip_SeenNumberDays'):format(seenCount+dayCount, seenDays) )--Seen {{%s}} over {{%s}} days: + end + if (seenDays > 6) and get("stat.purchased.avg14") then + tooltip:AddLine(" ".._TRANS('PURC_Tooltip_14DayAverage'), avg14*quantity)-- 14 day average + end + if (seenDays > 2) and get("stat.purchased.avg7") then + tooltip:AddLine(" ".._TRANS('PURC_Tooltip_7DayAverage'), avg7*quantity)-- 7 day average + end + if (seenDays > 0) and get("stat.purchased.avg3") then + tooltip:AddLine(" ".._TRANS('PURC_Tooltip_3DayAverage'), avg3*quantity)-- 3 day average + end + if (dayCount > 0) then + tooltip:AddLine(" ".._TRANS('PURC_Tooltip_SeenToday'):format(dayCount), dayAverage*quantity)--Seen {{%s}} today + end + end +end + +-- This is a function which migrates the data from a daily average to the +-- Exponential Moving Averages over the 3, 7 and 14 day ranges. +function private.PushStats(serverKey) + local dailyAvg + + local data = private.GetPriceData(serverKey) + + local pdata, fdata + for itemId, stats in pairs(data.daily) do + if (itemId ~= "created") then + pdata = private.UnpackStats(stats) + fdata = private.UnpackStats(data.means[itemId] or "") + for property, info in pairs(pdata) do + dailyAvg = info[1] / info[2] + if not fdata[property] then + fdata[property] = { + 1, + info[2], + ("%0.01f"):format(dailyAvg), + ("%0.01f"):format(dailyAvg), + ("%0.01f"):format(dailyAvg), + } + else + fdata[property][1] = fdata[property][1] + 1 + fdata[property][2] = fdata[property][2] + info[2] + fdata[property][3] = ("%0.01f"):format(((fdata[property][3] * 2) + dailyAvg)/3) + fdata[property][4] = ("%0.01f"):format(((fdata[property][4] * 6) + dailyAvg)/7) + fdata[property][5] = ("%0.01f"):format(((fdata[property][5] * 13) + dailyAvg)/14) + end + end + data.means[itemId] = private.PackStats(fdata) + end + end + data.daily = { created = time() } + private.ClearCache() +end + +function private.UnpackStatIter(data, ...) + local c = select("#", ...) + local v + for i = 1, c do + v = select(i, ...) + local property, info = strsplit(":", v) + property = tonumber(property) or property + if (property and info) then + data[property] = { strsplit(";", info) } + local item + for i=1, #data[property] do + item = data[property][i] + data[property][i] = tonumber(item) or item + end + end + end +end + +function private.UnpackStats(dataItem) + local data = {} + private.UnpackStatIter(data, strsplit(",", dataItem)) + return data +end + +local tmp={} +function private.PackStats(data) + local n=0 + for property, info in pairs(data) do + tmp[n+1]=property + tmp[n+2]=":" + tmp[n+3]=concat(info, ";") + tmp[n+4]="," + n=n+4 + end + return concat(tmp, "", 1, n-1) -- n-1 to skip last "," +end + +-- The following Functions are the routines used to access the permanent store data + +function private.UpgradeDb() + private.UpgradeDb = nil + if type(AucAdvancedStatPurchasedData) == "table" and AucAdvancedStatPurchasedData.Version == "2.0" then return end + + local newSave = { Version = "2.0", RealmData = {} } + + if type(AucAdvancedStatPurchasedData) == "table" and AucAdvancedStatPurchasedData.Version == "1.0" then + -- convert from type "1.0" database to type "2.0" + for realm, realmData in pairs (AucAdvancedStatPurchasedData.RealmData) do + if type(realm) == "string" and type(realmData) == "table" then + for faction, data in pairs (realmData) do + if type(faction) == "string" and type(data) == "table" and strmatch(faction, "^%u%l+$") then + -- looks like a valid realm/faction combination + local serverKey = realm.."-"..faction + local stats = data.stats + if type(stats) == "table" then + if type(data.means) ~= "table" then + data.means = {} + end + if type(data.daily) ~= "table" then + data.daily = { created = time () } + elseif type(data.daily.created) ~= "number" then + data.daily.created = time () + end + newSave.RealmData[serverKey] = stats + end + end + end + end + end + end + + AucAdvancedStatPurchasedData = newSave +end + +function lib.ClearData(serverKey) + serverKey = serverKey or GetFaction() + if AucAdvanced.API.IsKeyword(serverKey, "ALL") then + wipe(SPRealmData) + private.ClearCache() + aucPrint(_TRANS('PURC_Interface_ClearingPurchased').." {{".._TRANS("ADV_Interface_AllRealms").."}}") --Clearing Purchased stats for // All realms + elseif SPRealmData[serverKey] then + local _,_,keyText = AucAdvanced.SplitServerKey(serverKey) + SPRealmData[serverKey] = nil + private.ClearCache() + aucPrint(_TRANS('PURC_Interface_ClearingPurchased').." {{"..keyText.."}}")--Clearing Purchased stats for + end +end + +function lib.ClearItem(hyperlink, serverKey) + local linkType,itemID,property,factor = decode(hyperlink) + if (linkType ~= "item") then return end + if (factor and factor ~= 0) then property = property.."x"..factor end + + serverKey = serverKey or GetFaction () + + local data = private.GetPriceData(serverKey) + + local cleareditem = false + + if data.daily[itemID] then + local stats = private.UnpackStats (data.daily[itemID]) + if stats[property] then + stats[property] = nil + cleareditem = true + data.daily[itemID] = private.PackStats (stats) + end + end + + if data.means[itemID] then + local stats = private.UnpackStats (data.means[itemID]) + if stats[property] then + stats[property] = nil + cleareditem = true + data.means[itemID] = private.PackStats (stats) + end + end + + if cleareditem then + local _, _, keyText = AucAdvanced.SplitServerKey(serverKey) + aucPrint(_TRANS('PURC_Interface_ClearingPurchasedLink'):format(hyperlink, keyText) )--Stat - Purchased: clearing data for {{%s}} for {{%s}} + private.ClearCache() + end +end + +function private.GetPriceData(serverKey) + local data = SPRealmData[serverKey] + if not data then + if not AucAdvanced.SplitServerKey(serverKey) then + error("Invalid serverKey passed to Stat-Purchased") + end + data = {means = {}, daily = {created = time()}} + SPRealmData[serverKey] = data + end + return data +end + +function private.InitData() + private.InitData = nil + + -- Load Data + private.UpgradeDb() + SPRealmData = AucAdvancedStatPurchasedData.RealmData + if not SPRealmData then + SPRealmData = {} -- dummy table to avoid errors in future events; data will not be saved + error("Error loading or creating Stat-Purchased database") + end + + -- Data maintenance + for serverKey, data in pairs(SPRealmData) do + if type(serverKey) ~= "string" or not strmatch(serverKey, ".%-%u%l") then + -- not a valid serverKey - remove it + SPRealmData[serverKey] = nil + else + -- lots of checks to make sure we ONLY have valid data in this table + for key, _ in pairs (data) do + if key ~= "means" and key ~= "daily" then + data[key] = nil + end + end + if type(data.means) == "table" then + for id, packed in pairs(data.means) do + if type(id) ~= "number" or type(packed) ~= "string" then + data.means[id] = nil + end + end + else + data.means = {} + end + if type(data.daily) == "table" then + for id, packed in pairs(data.daily) do + if id ~= "created" and (type(id) ~= "number" or type(packed) ~= "string") then + data.daily[id] = nil + end + end + if type(data.daily.created) ~= "number" then + data.daily.created = time() + end + else + data.daily = {created = time()} + end + + if time() - data.daily.created > 3600*16 then + -- This data is more than 16 hours old, we classify this as "yesterday's data" + private.PushStats(serverKey) + end + end + end +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Stat-Purchased/StatPurchased.lua $", "$Rev: 4840 $") diff --git a/Auc-Stat-Simple/Auc-Stat-Simple.toc b/Auc-Stat-Simple/Auc-Stat-Simple.toc new file mode 100644 index 0000000..8af850b --- /dev/null +++ b/Auc-Stat-Simple/Auc-Stat-Simple.toc @@ -0,0 +1,18 @@ +## Title: Auc:Stat:Simple +## Notes: Simple statistics extension for Auctioneer. Performs historical exponential moving averages at the 1, 3, 7 and 14 day intervals. +## +## Interface: 40000 +## LoadOnDemand: 0 +## Disabled: 0 +## Dependencies: Auc-Advanced +## SavedVariables: AucAdvancedStatSimpleData +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Auc-Stat-Simple.toc 4933 2010-10-13 17:16:14Z Nechckn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## X-Max-Interface: 40000 +## + +StatSimple.lua diff --git a/Auc-Stat-Simple/StatSimple.lua b/Auc-Stat-Simple/StatSimple.lua new file mode 100644 index 0000000..81f95fc --- /dev/null +++ b/Auc-Stat-Simple/StatSimple.lua @@ -0,0 +1,675 @@ +--[[ +Auctioneer - StatSimple +Version: 5.7.4568 (KillerKoala) +Revision: $Id: StatSimple.lua 4840 2010-08-04 21:44:00Z 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 libType, libName = "Stat", "Simple" +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end + +local aucPrint,decode,_,_,replicate,_,get,set,default,debugPrint,fill, _TRANS = AucAdvanced.GetModuleLocals() + +local GetFaction = AucAdvanced.GetFaction + +-- Eliminate some global lookups +local select = select +local sqrt = sqrt +local ipairs = ipairs +local unpack = unpack +local tinsert = table.insert +local assert = assert +local tonumber = tonumber +local pairs = pairs +local type,time,wipe,ceil = type,time,wipe,ceil +local concat=table.concat +local strsplit,strfind=strsplit,strfind +-- GLOBALS: AucAdvancedStatSimpleData + + +-- local reference to our saved stats table +local SSRealmData + +function lib.CommandHandler(command, ...) + local serverKey = GetFaction() + local _,_,keyText = AucAdvanced.SplitServerKey(serverKey) + if (command == "help") then + aucPrint(_TRANS('SIMP_Help_SlashHelp1') ) --Help for Auctioneer Advanced - Simple + local line = AucAdvanced.Config.GetCommandLead(libType, libName) + aucPrint(line, "help}} - ".._TRANS('SIMP_Help_SlashHelp2') ) --this Simple help + aucPrint(line, "clear}} - ".._TRANS('SIMP_Help_SlashHelp3'):format(keyText) ) --clear current %s Simple price database + aucPrint(line, "push}} - ".._TRANS('SIMP_Help_SlashHelp4'):format(keyText) ) --force the %s Simple daily stats to archive (start a new day) + elseif (command == "clear") then + lib.ClearData(serverKey) + elseif (command == "push") then + aucPrint(_TRANS('SIMP_Help_SlashHelp6'):format(keyText) ) --Archiving {{%s}} daily stats and starting a new day + private.PushStats(serverKey) + end +end + +function lib.Processor(callbackType, ...) + if (callbackType == "tooltip") then + private.ProcessTooltip(...) + elseif (callbackType == "config") then + --Called when you should build your Configator tab. + private.SetupConfigGui(...) + end +end + +lib.Processors = {} +lib.Processors.tooltip = lib.Processor +lib.Processors.config = lib.Processor + +lib.ScanProcessors = {} +function lib.ScanProcessors.create(operation, itemData, oldData) + if not get("stat.simple.enable") then return end + -- This function is responsible for processing and storing the stats after each scan + -- Note: itemData gets reused over and over again, so do not make changes to it, or use + -- it in places where you rely on it. Make a deep copy of it if you need it after this + -- function returns. + + -- We're only interested in items with buyouts. + local buyout = itemData.buyoutPrice + if not buyout or buyout == 0 then return end + local buyoutper = ceil(buyout/itemData.stackSize) + + -- In this case, we're only interested in the initial create, other + -- Get the signature of this item and find it's stats. + local itemType, itemId, property, factor = AucAdvanced.DecodeLink(itemData.link) + if (factor ~= 0) then property = property.."x"..factor end + + local data = private.GetPriceData(GetFaction()) + if not data.daily[itemId] then data.daily[itemId] = "" end + local stats = private.UnpackStats(data.daily[itemId]) + if not stats[property] then stats[property] = { 0, 0 , buyoutper } end + if not stats[property][3] then stats[property][3] = buyoutper end + stats[property][1] = stats[property][1] + buyout + stats[property][2] = stats[property][2] + itemData.stackSize + if stats[property][3] > buyoutper then stats[property][3] = buyoutper end + data.daily[itemId] = private.PackStats(stats) +end + + +local dataset = {} + +function lib.GetPrice(hyperlink, serverKey) + if not get("stat.simple.enable") then return end + + local linkType,itemId,property,factor = AucAdvanced.DecodeLink(hyperlink) + if (linkType ~= "item") then return end + if (factor ~= 0) then property = property.."x"..factor end + serverKey = serverKey or GetFaction() + local data = private.GetPriceData(serverKey) + + local dayTotal, dayCount, dayAverage, minBuyout = 0,0,0,0 + local seenDays, seenCount, avg3, avg7, avg14, avgmins = 0,0,0,0,0,0 + -- Stddev calculations for market price + local count=0 -- index into dataset[] (living static outside the function) + local daysUsed = 0 -- used to keep running track of which daily averages we have + + + if data.daily[itemId] then + local stats = private.UnpackStats(data.daily[itemId]) + if stats[property] then + dayTotal, dayCount, minBuyout = unpack(stats[property]) + dayAverage = dayTotal/dayCount + if not minBuyout then minBuyout = 0 end + -- Stddev calculations for market price + count=count+1 + dataset[count] = dayAverage + daysUsed = 1 + end + end + if data.means[itemId] then + local stats = private.UnpackStats(data.means[itemId]) + if stats[property] then + seenDays, seenCount, avg3, avg7, avg14, avgmins = unpack(stats[property]) + if not avgmins then avgmins = 0 end + -- Stddev calculations for market price + if seenDays >= 3 then + for n = 1, 3-daysUsed do + count=count+1 + dataset[count] = avg3 + end + daysUsed = 3 + end + if seenDays >= 7 then + for n = 1, 7-daysUsed do + count=count+1 + dataset[count] = avg7 + end + daysUsed = 7 + end + if seenDays >= 14 then + for n = 1, 14-daysUsed do + count=count+1 + dataset[count] = avg14 + end + daysUsed = 14 + end + end + end + local mean = 0 + if count > 0 then + for n=1,count do + mean=mean+dataset[n] + end + mean = mean/count + end + local variance = 0 + if count == 1 then + variance = 0 + else + for n=1,count do + variance = variance + (mean - dataset[n])^2; + end + variance = sqrt(variance/(count-1)) + end + + return dayAverage, avg3, avg7, avg14, minBuyout, avgmins, false, dayTotal, dayCount, seenDays, seenCount, mean, variance +end + +function lib.GetPriceColumns() + return "Daily Avg", "3 Day Avg", "7 Day Avg", "14 Day Avg", "Min BO", "Avg MBO", false, "Daily Total", "Daily Count", "Seen Days", "Seen Count", "Mean", "StdDev" +end + +local array = {} +function lib.GetPriceArray(hyperlink, serverKey) + if not get("stat.simple.enable") then return end + -- Clean out the old array + wipe(array) + + -- Get our statistics + local dayAverage, avg3, avg7, avg14, minBuyout, avgmins, _, dayTotal, dayCount, seenDays, seenCount, mean, stddev = lib.GetPrice(hyperlink, serverKey) + + --if nothing is returned, return nil + if not dayCount then return end + + -- If reportsafe is on use the mean of all 14 day samples. Else use the "traditional" Simple values. + if not get("stat.simple.reportsafe") then + if (avg3 and seenDays > 3) or dayCount == 0 then + array.price = avg3 + elseif dayCount > 0 then + array.price = dayAverage + end + else + array.price = mean + end + array.stddev = stddev + array.seen = seenCount + array.avgday = dayAverage + array.avg3 = avg3 + array.avg7 = avg7 + array.avg14 = avg14 + array.mbo = minBuyout + array.avgmins = avgmins + array.daytotal = dayTotal + array.daycount = dayCount + array.seendays = seenDays + + -- Return a temporary array. Data in this array is + -- only valid until this function is called again. + return array +end + +local bellCurve = AucAdvanced.API.GenerateBellCurve(); +-- Gets the PDF curve for a given item. This curve indicates +-- the probability of an item's mean price. Uses an estimation +-- of the normally distributed bell curve by performing +-- calculations on the daily, 3-day, 7-day, and 14-day averages +-- stored by SIMP +-- @param hyperlink The item to generate the PDF curve for +-- @param serverKey The realm-faction key from which to look up the data +-- @return The PDF for the requested item, or nil if no data is available +-- @return The lower limit of meaningful data for the PDF (determined +-- as the mean minus 5 standard deviations) +-- @return The upper limit of meaningful data for the PDF (determined +-- as the mean plus 5 standard deviations) +function lib.GetItemPDF(hyperlink, serverKey) + -- TODO: This is an estimate. Can we touch this up later? Especially the stddev==0 case + + if not get("stat.simple.enable") then return end + -- Calculate the SE estimated standard deviation & mean + local dayAverage, avg3, avg7, avg14, minBuyout, avgmins, _, dayTotal, dayCount, seenDays, seenCount, mean, stddev = lib.GetPrice(hyperlink, serverKey) + + if seenCount == 0 or stddev ~= stddev or mean ~= mean or not mean or mean == 0 then + return ; -- No available data or cannot estimate + end + + -- If the standard deviation is zero, we'll have some issues, so we'll estimate it by saying + -- the std dev is 100% of the mean divided by square root of number of views + if stddev == 0 then stddev = mean / sqrt(seenCount); end + + -- Calculate the lower and upper bounds as +/- 3 standard deviations + local lower, upper = mean - 3*stddev, mean + 3*stddev; + + bellCurve:SetParameters(mean, stddev); + return bellCurve, lower, upper; +end + +function lib.OnLoad(addon) + if SSRealmData then return end + + -- Set defaults + default("stat.simple.tooltip", false) + default("stat.simple.avg3", false) + default("stat.simple.avg7", false) + default("stat.simple.avg14", false) + default("stat.simple.minbuyout", true) + default("stat.simple.avgmins", true) + default("stat.simple.quantmul", true) + default("stat.simple.enable", true) + default("stat.simple.reportsafe", false) + + -- Load and check data + private.InitData() +end + +function lib.ClearItem(hyperlink, serverKey) + local linkType, itemID, property, factor = AucAdvanced.DecodeLink(hyperlink) + if linkType ~= "item" then + return + end + if (factor ~= 0) then property = property.."x"..factor end + + serverKey = serverKey or GetFaction () + local data = private.GetPriceData (serverKey) + + local cleareditem = false + + if data.daily[itemID] then + local stats = private.UnpackStats (data.daily[itemID]) + if stats[property] then + stats[property] = nil + cleareditem = true + data.daily[itemID] = private.PackStats (stats) + end + end + + if data.means[itemID] then + local stats = private.UnpackStats (data.means[itemID]) + if stats[property] then + stats[property] = nil + cleareditem = true + data.means[itemID] = private.PackStats (stats) + end + end + + if cleareditem then + local _, _, keyText = AucAdvanced.SplitServerKey(serverKey) + aucPrint(_TRANS('SIMP_Help_SlashHelpClearingData'):format(libType, hyperlink, keyText)) --%s - Simple: clearing data for %s for {{%s}} + end +end + +function private.SetupConfigGui(gui) + local id = gui:AddTab(lib.libName, lib.libType.." Modules" ) + + gui:AddHelp(id, "what simple stats", + _TRANS('SIMP_Help_SimpleStats') ,--What are simple stats? + _TRANS('SIMP_Help_SimpleStatsAnswer') + )--Simple stats are the numbers that are generated by the Simple module, the Simple module averages all of the prices for items that it sees and provides moving 3, 7, and 14 day averages. It also provides daily minimum buyout along with a running average minimum buyout within 10% variance. + + --all options in here will be duplicated in the tooltip frame + function private.addTooltipControls(id) + gui:AddHelp(id, "what moving day average", + _TRANS('SIMP_Help_MovingAverage') , --What does \'moving day average\' mean? + _TRANS('SIMP_Help_MovingAverageAnswer') --Moving average means that it places more value on yesterday\'s moving averagethan today\'s average. The determined amount is then used for tomorrow\'s moving average calculation. + ) + + gui:AddHelp(id, "how day average calculated", + _TRANS('SIMP_Help_HowAveragesCalculated') , --How is the moving day averages calculated exactly? + _TRANS('SIMP_Help_HowAveragesCalculatedAnswer') --Todays Moving Average is ((X-1)*YesterdaysMovingAverage + TodaysAverage) / X, where X is the number of days (3,7, or 14). + ) + + gui:AddHelp(id, "no day saved", + _TRANS('SIMP_Help_NoDaySaved') ,--So you aren't saving a day-to-day average? + _TRANS('SIMP_Help_NoDaySavedAnswer') )--No, that would not only take up space, but heavy calculations on each auction house scan, and this is only a simple model. + + gui:AddHelp(id, "minimum buyout", + _TRANS('SIMP_Help_MinimumBuyout') ,--Why do I need to know minimum buyout? + _TRANS('SIMP_Help_MinimumBuyoutAnswer')--While some items will sell very well at average within 2 days, others may sell only if it is the lowest price listed. This was an easy calculation to do, so it was put in this module. + ) + + gui:AddHelp(id, "average minimum buyout", + _TRANS('SIMP_Help_AverageMinimumBuyout') ,--What's the point in an average minimum buyout? + _TRANS('SIMP_Help_AverageMinimumBuyoutAnswer')--This way you know how good a market is dealing. If the MBO (minimum buyout) is bigger than the average MBO, then it\'s usually a good time to sell, and if the average MBO is greater than the MBO, then it\'s a good time to buy. + ) + + gui:AddHelp(id, "average minimum buyout variance", + _TRANS('SIMP_Help_MinimumBuyoutVariance') ,--What\'s the \'10% variance\' mentioned earlier for? + _TRANS('SIMP_Help_MinimumBuyoutVarianceAnswer')--If the current MBO is inside a 10% range of the running average, the current MBO is averaged in to the running average at 50% (normal). If the current MBO is outside the 10% range, the current MBO will only be averaged in at a 12.5% rate. + ) + + gui:AddHelp(id, "why have variance", + _TRANS('SIMP_Help_WhyVariance') ,--What\'s the point of a variance on minimum buyout? + _TRANS('SIMP_Help_WhyVarianceAnswer') --Because some people put their items on the market for rediculous price (too low or too high), so this helps keep the average from getting out of hand. + ) + + gui:AddHelp(id, "why multiply stack size simple", + _TRANS('SIMP_Help_WhyMultiplyStack') ,--Why have the option to multiply stack size? + _TRANS('SIMP_Help_WhyMultiplyStackAnswer') --The original Stat-Simple multiplied by the stack size of the item, but some like dealing on a per-item basis. + ) + + gui:AddControl(id, "Header", 0, _TRANS('SIMP_Interface_SimpleOptions') )--Simple options' + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + gui:AddControl(id, "Checkbox", 0, 1, "stat.simple.enable", _TRANS('SIMP_Interface_EnableSimpleStats') )--Enable Simple Stats + gui:AddTip(id, _TRANS('SIMP_HelpTooltip_EnableSimpleStats') )--Allow Simple Stats to gather and return price data + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + + gui:AddControl(id, "Checkbox", 0, 4, "stat.simple.tooltip", _TRANS('SIMP_Interface_Show') )--Show simple stats in the tooltips? + gui:AddTip(id, _TRANS('SIMP_HelpTooltip_Show') )--Toggle display of stats from the Simple module on or off + gui:AddControl(id, "Checkbox", 0, 6, "stat.simple.avg3", _TRANS('SIMP_Interface_Toggle3Day') )--Display Moving 3 Day Average + gui:AddTip(id, _TRANS('SIMP_HelpTooltip_Toggle3Day') )--Toggle display of 3-Day average from the Simple module on or off + gui:AddControl(id, "Checkbox", 0, 6, "stat.simple.avg7", _TRANS('SIMP_Interface_Toggle7Day') )--Display Moving 7 Day Average + gui:AddTip(id, _TRANS('SIMP_HelpTooltip_Toggle7Day') )--Toggle display of 7-Day average from the Simple module on or off + gui:AddControl(id, "Checkbox", 0, 6, "stat.simple.avg14", _TRANS('SIMP_Interface_Toggle14Day') )--Display Moving 14 Day Average + gui:AddTip(id,_TRANS( 'SIMP_HelpTooltip_Toggle14Day') )--Toggle display of 14-Day average from the Simple module on or off + gui:AddControl(id, "Checkbox", 0, 6, "stat.simple.minbuyout", _TRANS('SIMP_Interface_MinBuyout') )--Display Daily Minimum Buyout + gui:AddTip(id, _TRANS('SIMP_HelpTooltip_MinBuyout') )--Toggle display of Minimum Buyout from the Simple module on or offMultiplies by current stack size if on + gui:AddControl(id, "Checkbox", 0, 6, "stat.simple.avgmins", _TRANS('SIMP_Interface_MinBuyoutAverage') )--Display Average of Daily Minimum Buyouts + gui:AddTip(id,_TRANS( 'SIMP_HelpTooltip_MinBuyoutAverage') )--Toggle display of Minimum Buyout average from the Simple module on or off + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + gui:AddControl(id, "Checkbox", 0, 4, "stat.simple.quantmul", _TRANS('SIMP_Interface_MultiplyStack') )--Multiply by stack size + gui:AddTip(id, _TRANS('SIMP_HelpTooltip_MultiplyStack') )--Multiplies by current stack size if on + gui:AddControl(id, "Checkbox", 0, 4, "stat.simple.reportsafe", _TRANS('SIMP_Interface_LongerAverage') )--Report safer prices for low volume items + gui:AddTip(id, _TRANS('SIMP_HelpTooltip_LongerAverage') )--Returns longer averages (7-day, or even 14-day) for low-volume items + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + end + --This is the Tooltip tab provided by aucadvnced so all tooltip configuration is in one place + local tooltipID = AucAdvanced.Settings.Gui.tooltipID + + --now we create a duplicate of these in the tooltip frame + private.addTooltipControls(id) + if tooltipID then private.addTooltipControls(tooltipID) end +end + +--[[ Local functions ]]-- + +function private.ProcessTooltip(tooltip, name, hyperlink, quality, quantity, cost) + -- In this function, you are afforded the opportunity to add data to the tooltip should you so + -- desire. You are passed a hyperlink, and it's up to you to determine whether or what you should + -- display in the tooltip. + + if not get("stat.simple.tooltip") then return end + + if not quantity or quantity < 1 then quantity = 1 end + if not get("stat.simple.quantmul") then quantity = 1 end + + local serverKey, realm, faction = GetFaction () -- realm/faction requested for anticipated changes to add cross-faction tooltips + local dayAverage, avg3, avg7, avg14, minBuyout, avgmins, _, dayTotal, dayCount, seenDays, seenCount = lib.GetPrice(hyperlink, serverKey) + local dispAvg3 = get("stat.simple.avg3") + local dispAvg7 = get("stat.simple.avg7") + local dispAvg14 = get("stat.simple.avg14") + local dispMinB = get("stat.simple.minbuyout") + local dispAvgMBO = get("stat.simple.avgmins") + if (not dayAverage) then return end + + if (seenDays + dayCount > 0) then + tooltip:AddLine(_TRANS('SIMP_Tooltip_SimplePrices') )--Simple prices: + + if (seenDays > 0) then + if (dayCount>0) then seenDays = seenDays + 1 end + tooltip:AddLine(" ".._TRANS('SIMP_Tooltip_SeenNumberDays'):format(seenCount+dayCount, seenDays) ) --Seen {{%s}} over {{%s}} days: + + end + if (seenDays > 6) and dispAvg14 then + tooltip:AddLine(" ".._TRANS('SIMP_Tooltip_14DayAverage') , avg14*quantity)-- 14 day average + end + if (seenDays > 2) and dispAvg7 then + tooltip:AddLine(" ".._TRANS('SIMP_Tooltip_7DayAverage') , avg7*quantity) -- 7 day average + end + if (seenDays > 0) and dispAvg3 then + tooltip:AddLine(" ".._TRANS('SIMP_Tooltip_3DayAverage') , avg3*quantity)-- 3 day average + end + if (seenDays > 0) and (avgmins > 0) and dispAvgMBO then + tooltip:AddLine(" ".._TRANS('SIMP_Tooltip_AverageMBO') , avgmins*quantity)-- Average MBO + end + if (dayCount > 0) then + tooltip:AddLine(" ".._TRANS('SIMP_Tooltip_SeenToday'):format(dayCount) , dayAverage*quantity) --Seen {{%s}} today: + end + if (dayCount > 0) and (minBuyout > 0) and dispMinB then + tooltip:AddLine(" ".._TRANS('SIMP_Tooltip_TodaysMBO') , minBuyout*quantity)-- Today's Min BO + end + end +end + +-- This is a function which migrates the data from a daily average to the +-- Exponential Moving Averages over the 3, 7 and 14 day ranges. +function private.PushStats(serverKey) + local dailyAvg + + local data = private.GetPriceData(serverKey) + + local pdata, fdata, temp + for itemId, stats in pairs(data.daily) do + if (itemId ~= "created") then + pdata = private.UnpackStats(stats) + fdata = private.UnpackStats(data.means[itemId] or "") + for property, info in pairs(pdata) do + dailyAvg = info[1] / info[2] + if not info[3] then info[3] = 0 end + if not fdata[property] then + fdata[property] = { + 1, + info[2], + ("%0.01f"):format(dailyAvg), + ("%0.01f"):format(dailyAvg), + ("%0.01f"):format(dailyAvg), + ("%0.01f"):format(info[3]) + } + else + fdata[property][1] = fdata[property][1] + 1 + fdata[property][2] = fdata[property][2] + info[2] + fdata[property][3] = ("%0.01f"):format(((fdata[property][3] * 2) + dailyAvg)/3) + fdata[property][4] = ("%0.01f"):format(((fdata[property][4] * 6) + dailyAvg)/7) + fdata[property][5] = ("%0.01f"):format(((fdata[property][5] * 13) + dailyAvg)/14) + if not fdata[property][6] then fdata[property][6] = 0 end + temp = fdata[property][6] + if temp < 1 then + fdata[property][6] = info[3] + else + if info[3] ~= 0 then + if temp < info[3] then + if (temp*10/info[3]) < 9 then + fdata[property][6] = ("%0.01f"):format((temp+info[3])/2) + else + fdata[property][6] = ("%0.01f"):format((temp*7+info[3])/8) + end + else + if (info[3]*10/temp) < 9 then + fdata[property][6] = ("%0.01f"):format((temp+info[3])/2) + else + fdata[property][6] = ("%0.01f"):format((temp*7+info[3])/8) + end + end + end + end + end + end + data.means[itemId] = private.PackStats(fdata) + end + end + data.daily = {created = time()} +end + +function private.UnpackStatIter(data, ...) + local c = select("#", ...) + local v + for i = 1, c do + v = select(i, ...) + local property, info = strsplit(":", v) + property = tonumber(property) or property + if (property and info) then + data[property] = {strsplit(";", info)} + local item + for i=1, #data[property] do + item = data[property][i] + data[property][i] = tonumber(item) or item + end + end + end +end + +function private.UnpackStats(dataItem) + local data = {} + private.UnpackStatIter(data, strsplit(",", dataItem)) + return data +end + +local tmp={} +function private.PackStats(data) + local n=0 + for property, info in pairs(data) do + n=n+1 + tmp[n]=property..":"..concat(info, ";") + end + return concat(tmp,",",1,n) +end + +-- The following Functions are the routines used to access the permanent store data + +function private.UpgradeDb() + private.UpgradeDb = nil + if type(AucAdvancedStatSimpleData) == "table" and AucAdvancedStatSimpleData.Version == "2.0" then return end + + local newSave = {Version = "2.0", RealmData = {}} + + -- Will only be run once per user account; however must run smoothly every time + -- Can afford to perform extra type-checking for safety + if type(AucAdvancedStatSimpleData) == "table" and AucAdvancedStatSimpleData.Version == "1.0" then + -- perform upgrade from "1.0" to "2.0" + for realm, realmData in pairs (AucAdvancedStatSimpleData.RealmData) do + if type (realm) == "string" and type (realmData) == "table" then + -- valid stats will only be stored in serverKeys which match realm + local realmPattern = realm.."%-%u%l" + for serverKey, data in pairs (realmData) do + if type (serverKey) == "string" and type (data) == "table" and strfind (serverKey, realmPattern) then + -- found a valid serverKey + -- ensure all required subtables are present + if type (data.means) ~= "table" then + data.means = {} + end + if type (data.daily) ~= "table" then + data.daily = {created = time()} + elseif type (data.daily.created) ~= "number" then + data.daily.created = time() + end + newSave.RealmData[serverKey] = data + end + end + end + end + end + AucAdvancedStatSimpleData = newSave +end + +function lib.ClearData(serverKey) + serverKey = serverKey or GetFaction() + if AucAdvanced.API.IsKeyword(serverKey, "ALL") then + wipe(SSRealmData) + aucPrint(_TRANS('SIMP_Interface_ClearingSimple').." {{".._TRANS("ADV_Interface_AllRealms").."}}") --Clearing Simple stats for // All realms + elseif SSRealmData[serverKey] then + local _,_,keyText = AucAdvanced.SplitServerKey(serverKey) + keyText = keyText or tostring(serverKey) -- avoid display error if database entry is not a valid serverKey (due to minor database corruption) + SSRealmData[serverKey] = nil + aucPrint(_TRANS('SIMP_Interface_ClearingSimple').." {{"..keyText.."}}") --Clearing Simple stats for + end +end + +function private.GetPriceData(serverKey) + local data = SSRealmData[serverKey] + if not data then + if not AucAdvanced.SplitServerKey(serverKey) then + error("Invalid serverKey passed to Stat-Simple") + end + data = {means = {}, daily = {created = time ()}} + SSRealmData[serverKey] = data + end + return data +end + +function private.InitData() + private.InitData = nil + + -- Load data + private.UpgradeDb() + SSRealmData = AucAdvancedStatSimpleData.RealmData + if not SSRealmData then + SSRealmData = {} -- dummy value to avoid more errors - will not get saved + error("Error loading or creating StatSimple database") + end + + -- Note: database errors can occur if user tries to run an older version of StatSimple after the database is upgraded. + for serverKey, data in pairs (SSRealmData) do + if type(serverKey) ~= "string" or not strfind (serverKey, ".%-%u%l") then + -- not a valid serverKey - remove it + SSRealmData[serverKey] = nil + else + -- aggressive checks to strip out any data that is the wrong type + for key, _ in pairs (data) do + if key ~= "means" and key ~= "daily" then + data[key] = nil + end + end + if type(data.means) == "table" then + for id, packed in pairs (data.means) do + if type(id) ~= "number" or type(packed) ~= "string" then + data.means[id] = nil + end + end + else + data.means = {} + end + if type(data.daily) == "table" then + for id, packed in pairs (data.daily) do + if id ~= "created" and (type(id) ~= "number" or type(packed) ~= "string") then + data.daily[id] = nil + end + end + if type(data.daily.created) ~= "number" then + data.daily.created = time () + end + else + data.daily = {created = time()} + end + + -- database maintenance + if time() - data.daily.created > 3600*16 then + -- This data is more than 16 hours old, we classify this as "yesterday's data" + private.PushStats(serverKey) + end + end + end +end + + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Stat-Simple/StatSimple.lua $", "$Rev: 4840 $") diff --git a/Auc-Stat-StdDev/Auc-Stat-StdDev.toc b/Auc-Stat-StdDev/Auc-Stat-StdDev.toc new file mode 100644 index 0000000..7acda38 --- /dev/null +++ b/Auc-Stat-StdDev/Auc-Stat-StdDev.toc @@ -0,0 +1,18 @@ +## Title: Auc:Stat:Std Deviation +## Notes: Statistics extension for Auctioneer that uses the variance to detect, and ignore, outliers and provides pricing recommendations. +## +## Interface: 40000 +## LoadOnDemand: 0 +## Disabled: 1 +## Dependencies: Auc-Advanced +## SavedVariables: AucAdvancedStatStdDevData +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Auc-Stat-StdDev.toc 4933 2010-10-13 17:16:14Z Nechckn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## X-Max-Interface: 40000 +## + +StatStdDev.lua diff --git a/Auc-Stat-StdDev/StatStdDev.lua b/Auc-Stat-StdDev/StatStdDev.lua new file mode 100644 index 0000000..d339050 --- /dev/null +++ b/Auc-Stat-StdDev/StatStdDev.lua @@ -0,0 +1,469 @@ +--[[ + Auctioneer - Standard Deviation Statistics module + Version: 5.7.4568 (KillerKoala) + Revision: $Id: StatStdDev.lua 4840 2010-08-04 21:44:00Z 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 libType, libName = "Stat", "StdDev" +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end + +local aucPrint,decode,_,_,replicate,empty,get,set,default,debugPrint,fill, _TRANS = AucAdvanced.GetModuleLocals() +local tonumber,strsplit,select,pairs=tonumber,strsplit,select,pairs +local setmetatable=setmetatable +local wipe=wipe +local floor,ceil,abs=floor,ceil,abs +local concat=table.concat +local tinsert,tremove=table.insert,table.remove +-- GLOBALS: AucAdvancedStatStdDevData + +local GetFaction = AucAdvanced.GetFaction + +local SSDRealmData + +local cache = {} -- setmetatable({}, {__mode="v"}) + +local ZValues = {.063, .126, .189, .253, .319, .385, .454, .525, .598, .675, .756, .842, .935, 1.037, 1.151, 1.282, 1.441, 1.646, 1.962, 20, 20000} + +function lib.CommandHandler(command, ...) + local serverKey = GetFaction() + local _,_,keyText = AucAdvanced.SplitServerKey(serverKey) + if (command == "help") then + aucPrint(_TRANS('SDEV_Help_SlashHelp1') )--Help for Auctioneer - StdDev + local line = AucAdvanced.Config.GetCommandLead(libType, libName) + aucPrint(line, "help}} - ".._TRANS('SDEV_Help_SlashHelp2') ) --this StdDev help + aucPrint(line, "clear}} - ".._TRANS('SDEV_Help_SlashHelp3'):format(keyText) ) --clear current %s StdDev price database + elseif (command == "clear") then + lib.ClearData(serverKey) + end +end + +function lib.Processor(callbackType, ...) + if (callbackType == "tooltip") then + lib.ProcessTooltip(...) + elseif (callbackType == "config") then + --Called when you should build your Configator tab. + private.SetupConfigGui(...) + elseif (callbackType == "scanstats") then + wipe(cache) + end +end +lib.Processors = {} +function lib.Processors.tooltip(callbackType, ...) + lib.ProcessTooltip(...) +end +function lib.Processors.config(callbackType, ...) + --Called when you should build your Configator tab. + private.SetupConfigGui(...) +end +function lib.Processors.scanstats(callbackType, ...) + wipe(cache) +end + + + +lib.ScanProcessors = {} +function lib.ScanProcessors.create(operation, itemData, oldData) + if not AucAdvanced.Settings.GetSetting("stat.stddev.enable") then return end + + -- This function is responsible for processing and storing the stats after each scan + -- Note: itemData gets reused over and over again, so do not make changes to it, or use + -- it in places where you rely on it. Make a deep copy of it if you need it after this + -- function returns. + + -- We're only interested in items with buyouts. + local buyout = itemData.buyoutPrice + if not buyout or buyout == 0 then return end + if (itemData.stackSize > 1) then + buyout = buyout.."/"..itemData.stackSize + end + + -- Get the signature of this item and find it's stats. + local linkType,itemId,property,factor = AucAdvanced.DecodeLink(itemData.link) + if (linkType ~= "item") then return end + if (factor and factor ~= 0) then property = property.."x"..factor end + + local serverKey = GetFaction() + if not SSDRealmData[serverKey] then SSDRealmData[serverKey] = {} end + local stats = private.UnpackStats(SSDRealmData[serverKey][itemId]) + if not stats[property] then stats[property] = {} end + if #stats[property] >= 100 then + tremove(stats[property], 1) + end + tinsert(stats[property], buyout) + SSDRealmData[serverKey][itemId] = private.PackStats(stats) +end + +local BellCurve = AucAdvanced.API.GenerateBellCurve(); +----------------------------------------------------------------------------------- +-- The PDF for standard deviation data, standard bell curve +----------------------------------------------------------------------------------- +function lib.GetItemPDF(hyperlink, serverKey) + if not AucAdvanced.Settings.GetSetting("stat.stddev.enable") then return end + -- Get the data + local average, mean, _, stddev, variance, count, confidence = lib.GetPrice(hyperlink, serverKey) + + if not (average and stddev) or average == 0 or stddev == 0 then + return nil; -- No data, cannot determine pricing + end + + local lower, upper = average - 3 * stddev, average + 3 * stddev; + + -- Build the PDF based on standard deviation & average + BellCurve:SetParameters(average, stddev); + return BellCurve, lower, upper; -- This has a __call metamethod so it's ok +end + +----------------------------------------------------------------------------------- + +function private.GetCfromZ(Z) + --C = 0.05*i + if (not Z) then + return .05 + end + if (Z > 10) then + return .99 + end + local i = 1 + while Z > ZValues[i] do + i = i + 1 + end + if i == 1 then + return .05 + else + i = i - 1 + ((Z - ZValues[i-1]) / (ZValues[i] - ZValues[i-1])) + return i*0.05 + end +end + +local datapoints_price = {} -- used temporarily in .GetPrice() to avoid unpacking strings multiple times +local datapoints_stack = {} + +function lib.GetPrice(hyperlink, serverKey) + if not AucAdvanced.Settings.GetSetting("stat.stddev.enable") then return end + + local linkType,itemId,property,factor = AucAdvanced.DecodeLink(hyperlink) + if (linkType ~= "item") then return end + if (factor and factor ~= 0) then property = property.."x"..factor end + + if not serverKey then serverKey = GetFaction() end + + if not SSDRealmData[serverKey] then return end + if not SSDRealmData[serverKey][itemId] then return end + + local cacheKey = serverKey ..":"..itemId..":"..property + if cache[cacheKey] then + return unpack(cache[cacheKey]) + end + + local stats = private.UnpackStats(SSDRealmData[serverKey][itemId]) + if not stats[property] then return end + + local count = #stats[property] + if (count < 1) then return end + + local total, number = 0, 0 + for i = 1, count do + local price, stack = strsplit("/", stats[property][i]) + price = tonumber(price) or 0 + stack = tonumber(stack) or 1 + if (stack < 1) then stack = 1 end + datapoints_price[i] = price -- cache these for further processing below (so they don't need to strsplit etc) + datapoints_stack[i] = stack + total = total + price + number = number + stack + end + local mean = total / number + + if (count < 2) then return 0,0,0, mean, count end + + local variance = 0 + for i = 1, count do + variance = variance + ((mean - datapoints_price[i]/datapoints_stack[i]) ^ 2); + end + + variance = variance / count; + local stdev = variance ^ 0.5 + + local deviation = 1.5 * stdev + + total = 0 -- recompute them from entries inside the allowed deviation + number = 0 + + for i = 1, count do + local price,stack = datapoints_price[i], datapoints_stack[i] + if abs((price/stack) - mean) < deviation then + total = total + price + number = number + stack + end + end + + local confidence = .01 + local average + if (number > 0) then -- number<1 will happen if we have e.g. two big clusters: one at 1g and one at 10g + average = total / number + confidence = (.15*average)*(number^0.5)/(stdev) + confidence = private.GetCfromZ(confidence) + end + + cache[cacheKey] = { average, mean, false, stdev, variance, count, confidence } + return average, mean, false, stdev, variance, count, confidence +end + +function lib.GetPriceColumns() + return "Average", "Mean", false, "Std Deviation", "Variance", "Count" +end + +local array = {} +function lib.GetPriceArray(hyperlink, serverKey) + if not AucAdvanced.Settings.GetSetting("stat.stddev.enable") then return end + -- Clean out the old array + wipe(array) + + -- Get our statistics + local average, mean, _, stdev, variance, count, confidence = lib.GetPrice(hyperlink, serverKey) + + -- These 3 are the ones that most algorithms will look for + array.price = average or mean + array.seen = count + array.confidence = confidence + -- This is additional data + array.normalized = average + array.mean = mean + array.deviation = stdev + array.variance = variance + + -- Return a temporary array. Data in this array is + -- only valid until this function is called again. + return array +end + +function private.SetupConfigGui(gui) + local id = gui:AddTab(lib.libName, lib.libType.." Modules") + --gui:MakeScrollable(id) + + gui:AddHelp(id, "what stddev stats", + _TRANS('SDEV_Help_StdDevStats') ,--What are StdDev stats? + _TRANS('SDEV_Help_StdDevStatsAnswer') --StdDev stats are the numbers that are generated by the StdDev module consisting of a filtered Standard Deviation calculation of item cost. + ) + + --all options in here will be duplicated in the tooltip frame + function private.addTooltipControls(id) + gui:AddHelp(id, "filtered stddev", + _TRANS('SDEV_Help_Filtered') ,--What do you mean filtered? + _TRANS('SDEV_Help_FilteredAnswer') --Items outside a (1.5*Standard) variance are ignored and assumed to be wrongly priced when calculating the deviation. + ) + + gui:AddHelp(id, "what standard deviation", + _TRANS('SDEV_Help_StandardDeviationCalculation') ,--What is a Standard Deviation calculation? + _TRANS('SDEV_Help_StandardDeviationCalculationAnswer') --In short terms, it is a distance to mean average calculation. + ) + + gui:AddHelp(id, "what normalized", + _TRANS('SDEV_Help_Normalized') ,--What is the Normalized calculation? + _TRANS('SDEV_Help_NormalizedAnswer') --In short terms again, it is the average of those values determined within the standard deviation variance calculation. + ) + + gui:AddHelp(id, "what confidence", + _TRANS('SDEV_Help_Confidence') ,--What does confidence mean? + _TRANS('SDEV_Help_ConfidenceAnswer') --Confidence is a value between 0 and 1 that determines the strength of the calculations (higher the better). + ) + + gui:AddHelp(id, "why multiply stack size stddev", + _TRANS('SDEV_Help_WhyMultiplyStack') ,--Why have the option to multiply by stack size? + _TRANS('SDEV_Help_WhyMultiplyStackAnswer') --The original Stat-StdDev multiplied by the stack size of the item, but some like dealing on a per-item basis. + ) + + gui:AddControl(id, "Header", 0, _TRANS('SDEV_Interface_StdDevOptions') )--StdDev options + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + gui:AddControl(id, "Checkbox", 0, 1, "stat.stddev.enable", _TRANS('SDEV_Interface_EnableStdDevStats') )--Enable StdDev Stats + gui:AddTip(id, _TRANS('SDEV_HelpTooltip_EnableStdDevStats') )--Allow StdDev to gather and return price data + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + + gui:AddControl(id, "Checkbox", 0, 4, "stat.stddev.tooltip", _TRANS('SDEV_Interface_Show') )--Show stddev stats in the tooltips? + gui:AddTip(id, _TRANS('SDEV_HelpTooltip_Show') )--Toggle display of stats from the StdDev module on or off + gui:AddControl(id, "Checkbox", 0, 6, "stat.stddev.mean", _TRANS('SDEV_Interface_DisplayMean') )--Display Mean + gui:AddTip(id, _TRANS('SDEV_HelpTooltip_DisplayMean') )--Toggle display of 'Mean' calculation in tooltips on or off + gui:AddControl(id, "Checkbox", 0, 6, "stat.stddev.normal", _TRANS('SDEV_Interface_DisplayNormalized') )--Display Normalized + gui:AddTip(id, _TRANS('SDEV_HelpTooltip_DisplayNormalized') )--Toggle display of 'Normalized' calculation in tooltips on or off' + gui:AddControl(id, "Checkbox", 0, 6, "stat.stddev.stdev", _TRANS('SDEV_Interface_DisplayStandardDeviation') )--Display Standard Deviation + gui:AddTip(id,_TRANS('SDEV_HelpTooltip_DisplayStandardDeviation') )--Toggle display of 'Standard Deviation' calculation in tooltips on or off + gui:AddControl(id, "Checkbox", 0, 6, "stat.stddev.confid", _TRANS('SDEV_Interface_DisplayConfidence') )--Display Confidence + gui:AddTip(id,_TRANS('SDEV_HelpTooltip_DisplayConfidence') )--Toggle display of 'Confidence' calculation in tooltips on or off + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + gui:AddControl(id, "Checkbox", 0, 4, "stat.stddev.quantmul", _TRANS('SDEV_Interface_MultiplyStack') )--Multiply by Stack Size + gui:AddTip(id,_TRANS('SDEV_HelpTooltip_MultiplyStack') )--Multiplies by current stack size if on + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + end + --This is the Tooltip tab provided by aucadvnced so all tooltip configuration is in one place + local tooltipID = AucAdvanced.Settings.Gui.tooltipID + + --now we create a duplicate of these in the tooltip frame + private.addTooltipControls(id) + if tooltipID then private.addTooltipControls(tooltipID) end +end + +function lib.ProcessTooltip(tooltip, name, hyperlink, quality, quantity, cost, ...) + -- In this function, you are afforded the opportunity to add data to the tooltip should you so + -- desire. You are passed a hyperlink, and it's up to you to determine whether or what you should + -- display in the tooltip. + + if not AucAdvanced.Settings.GetSetting("stat.stddev.tooltip") then return end + + if not quantity or quantity < 1 then quantity = 1 end + if not AucAdvanced.Settings.GetSetting("stat.stddev.quantmul") then quantity = 1 end + local average, mean, _, stdev, var, count, confidence = lib.GetPrice(hyperlink) + + if (mean and mean > 0) then + tooltip:AddLine(_TRANS('SDEV_Tooltip_PricesPoints'):format(count) )--StdDev prices %d points: + + if AucAdvanced.Settings.GetSetting("stat.stddev.mean") then + tooltip:AddLine(" ".._TRANS('SDEV_Tooltip_MeanPrice'), mean*quantity)-- Mean price + end + if (average and average > 0) then + if AucAdvanced.Settings.GetSetting("stat.stddev.normal") then + tooltip:AddLine(" ".._TRANS('SDEV_Tooltip_Normalized'), average*quantity)-- Normalized + if (quantity > 1) then + tooltip:AddLine(" ".._TRANS('SDEV_Tooltip_Individually'), average)-- (or individually) + end + end + if AucAdvanced.Settings.GetSetting("stat.stddev.stdev") then + tooltip:AddLine(" ".._TRANS('SDEV_Tooltip_StdDeviation'), stdev*quantity)-- Std Deviation + if (quantity > 1) then + tooltip:AddLine(" ".._TRANS('SDEV_Tooltip_Individually'), stdev)-- (or individually) + end + + end + if AucAdvanced.Settings.GetSetting("stat.stddev.confid") then + tooltip:AddLine(" ".._TRANS('SDEV_Tooltip_Confidence')..(floor(confidence*1000))/1000)-- Confidence: + end + end + end +end + +function lib.OnLoad(addon) + if SSDRealmData then return end + + AucAdvanced.Settings.SetDefault("stat.stddev.tooltip", false) + AucAdvanced.Settings.SetDefault("stat.stddev.mean", false) + AucAdvanced.Settings.SetDefault("stat.stddev.normal", false) + AucAdvanced.Settings.SetDefault("stat.stddev.stdev", true) + AucAdvanced.Settings.SetDefault("stat.stddev.confid", true) + AucAdvanced.Settings.SetDefault("stat.stddev.quantmul", true) + AucAdvanced.Settings.SetDefault("stat.stddev.enable", true) + + private.InitData() +end + +function lib.ClearItem(hyperlink, serverKey) + local linkType, itemID, property, factor = AucAdvanced.DecodeLink(hyperlink) + if (linkType ~= "item") then + return + end + if (factor ~= 0) then property = property.."x"..factor end + if not serverKey then serverKey = GetFaction() end + if SSDRealmData[serverKey] and SSDRealmData[serverKey][itemID] then + local stats = private.UnpackStats(SSDRealmData[serverKey][itemID]) + if stats[property] then + stats[property] = nil + SSDRealmData[serverKey][itemID] = private.PackStats(stats) + wipe(cache) + local _, _, keyText = AucAdvanced.SplitServerKey(serverKey) + aucPrint(libType.._TRANS('SDEV_Interface_ClearingData'):format(hyperlink, keyText))--- StdDev: clearing data for %s for {{%s}} + end + end +end + +function lib.ClearData(serverKey) + serverKey = serverKey or GetFaction() + wipe(cache) + if AucAdvanced.API.IsKeyword(serverKey, "ALL") then + wipe(SSDRealmData) + aucPrint(_TRANS('SDEV_Help_SlashHelp4').." {{".._TRANS("ADV_Interface_AllRealms").."}}") --Clearing StdDev stats for // All realms + elseif SSDRealmData[serverKey] then + SSDRealmData[serverKey] = nil + local _, _, keyText = AucAdvanced.SplitServerKey(serverKey) + aucPrint(_TRANS('SDEV_Help_SlashHelp4').." {{"..keyText.."}}") --Clearing StdDev stats for + end +end + +--[[ Local functions ]]-- + +function private.UnpackStatIter(data, ...) + local c = select("#", ...) + local v + for i = 1, c do + v = select(i, ...) + local property, info = strsplit(":", v) + property = tonumber(property) or property + if (property and info) then + data[property] = {strsplit(";", info)} + local item + for i=1, #data[property] do + item = data[property][i] + data[property][i] = tonumber(item) or item + end + end + end +end +function private.UnpackStats(dataItem) + local data = {} + if (dataItem) then + private.UnpackStatIter(data, strsplit(",", dataItem)) + end + return data +end + +local tmp={} +function private.PackStats(data) + local n=0 + for property, info in pairs(data) do + n=n+1 + tmp[n]=property..":"..concat(info, ";") + end + return concat(tmp,",",1,n) +end + +function private.InitData() + private.InitData = nil + + -- Do any database upgrades here + if not AucAdvancedStatStdDevData then AucAdvancedStatStdDevData = {} end + + SSDRealmData = AucAdvancedStatStdDevData + + -- Do any regular database maintenance here +end + + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Stat-StdDev/StatStdDev.lua $", "$Rev: 4840 $") diff --git a/Auc-Stat-iLevel/Auc-Stat-iLevel.toc b/Auc-Stat-iLevel/Auc-Stat-iLevel.toc new file mode 100644 index 0000000..418f15a --- /dev/null +++ b/Auc-Stat-iLevel/Auc-Stat-iLevel.toc @@ -0,0 +1,18 @@ +## Title: Auc:Stat:iLevel +## Notes: Statistics extension for Auctioneer that uses the variance to detect outliers, grouping data upon the item quality, type and item level. +## +## Interface: 40000 +## LoadOnDemand: 0 +## Disabled: 1 +## Dependencies: Auc-Advanced +## SavedVariables: AucAdvancedStat_iLevelData +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Auc-Stat-iLevel.toc 4933 2010-10-13 17:16:14Z Nechckn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## X-Max-Interface: 40000 +## + +iLevel.lua diff --git a/Auc-Stat-iLevel/iLevel.lua b/Auc-Stat-iLevel/iLevel.lua new file mode 100644 index 0000000..063fd3e --- /dev/null +++ b/Auc-Stat-iLevel/iLevel.lua @@ -0,0 +1,533 @@ +--[[ + Auctioneer - iLevel Standard Deviation Statistics module + Version: 5.7.4568 (KillerKoala) + Revision: $Id: iLevel.lua 4840 2010-08-04 21:44:00Z 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 libType, libName = "Stat", "iLevel" +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end +local aucPrint,decode,_,_,replicate,_,get,set,default,debugPrint,fill, _TRANS = AucAdvanced.GetModuleLocals() + +local select,next,pairs,ipairs,type,unpack,wipe = select,next,pairs,ipairs,type,unpack,wipe +local tonumber,tostring,strsplit,strjoin = tonumber,tostring,strsplit,strjoin +local floor,abs,max = floor,abs,max +local concat = table.concat +local strmatch = strmatch + +local iTypes = AucAdvanced.Const.InvTypes +local GetFaction = AucAdvanced.GetFaction + +local KEEP_NUM_POINTS = 250 + +local ZValues = {.063, .126, .189, .253, .319, .385, .454, .525, .598, .675, .756, .842, .935, 1.037, 1.151, 1.282, 1.441, 1.646, 1.962, 20, 20000} + +function lib.CommandHandler(command, ...) + local serverKey = GetFaction() + local _,_,keyText = AucAdvanced.SplitServerKey(serverKey) + if (command == "help") then + aucPrint(_TRANS('ILVL_Help_SlashHelp1') )--Help for Auctioneer Advanced - iLevel + local line = AucAdvanced.Config.GetCommandLead(libType, libName) + aucPrint(line, "help}} - ".._TRANS('ILVL_Help_SlashHelp2') ) -- this iLevel help + aucPrint(line, "clear}} - ".._TRANS('ILVL_Help_SlashHelp3'):format(keyText) ) --clear current %s iLevel price database + elseif (command ==_TRANS( 'clear') ) then + lib.ClearData(serverKey) + end +end + +function lib.Processor(callbackType, ...) + if (callbackType == "tooltip") then + lib.ProcessTooltip(...) + elseif (callbackType == "config") then + if private.SetupConfigGui then -- only call it once + private.SetupConfigGui(...) + end + elseif (callbackType == "scanstats") then + private.ResetCache() + private.RepackStats() + end +end +lib.Processors = {} +function lib.Processors.tooltip(callbackType, ...) + lib.ProcessTooltip(...) +end +function lib.Processors.config(callbackType, ...) + if private.SetupConfigGui then -- only call it once + private.SetupConfigGui(...) + end +end +function lib.Processors.scanstats(callbackType, ...) + private.ResetCache() + private.RepackStats() +end + + + +lib.ScanProcessors = {} +function lib.ScanProcessors.create(operation, itemData, oldData) + if not get("stat.ilevel.enable") then return end + -- This function is responsible for processing and storing the stats after each scan + -- Note: itemData gets reused over and over again, so do not make changes to it, or use + -- it in places where you rely on it. Make a deep copy of it if you need it after this + -- function returns. + + -- We're only interested in items with buyouts. + local buyout = itemData.buyoutPrice + if not buyout or buyout == 0 then return end + if (itemData.stackSize > 1) then + buyout = buyout.."/"..itemData.stackSize + end + + -- Get the signature of this item and find it's stats. + local iLevel, quality, equipPos = itemData.itemLevel, itemData.quality, itemData.equipPos + if quality < 1 then return end + if not equipPos then return end + if equipPos < 1 then return end + local itemSig = ("%d:%d"):format(equipPos, quality) + + local serverKey = GetFaction() + local stats = private.GetUnpackedStats(serverKey, itemSig, true) -- read/write + if not stats[iLevel] then stats[iLevel] = {} end + local sz = #stats[iLevel] + stats[iLevel][sz+1] = buyout +end + +local BellCurve = AucAdvanced.API.GenerateBellCurve(); +----------------------------------------------------------------------------------- +-- The PDF for standard deviation data, standard bell curve +----------------------------------------------------------------------------------- +function lib.GetItemPDF(hyperlink, serverKey) + if not get("stat.ilevel.enable") then return end + -- Get the data + local average, mean, _, stddev, variance, count, confidence = lib.GetPrice(hyperlink, serverKey) + + if not (average and stddev) or average == 0 or stddev == 0 then + return nil; -- No data, cannot determine pricing + end + + local lower, upper = average - 3 * stddev, average + 3 * stddev; + + -- Build the PDF based on standard deviation & average + BellCurve:SetParameters(average, stddev); + return BellCurve, lower, upper; -- This has a __call metamethod so it's ok +end + +----------------------------------------------------------------------------------- + +function private.GetCfromZ(Z) + --C = 0.05*i + if (not Z) then + return .05 + end + if (Z > 10) then + return .99 + end + local i = 1 + while Z > ZValues[i] do + i = i + 1 + end + if i == 1 then + return .05 + else + i = i - 1 + ((Z - ZValues[i-1]) / (ZValues[i] - ZValues[i-1])) + return i*0.05 + end +end + +local weakmeta = {__mode="kv"} +local pricecache = setmetatable({}, weakmeta) +function private.ResetCache() + wipe(pricecache) +end + +local datapoints_price = {} -- used temporarily in .GetPrice() to avoid unpacking strings multiple times +local datapoints_stack = {} + +function lib.GetPrice(hyperlink, serverKey) + if not get("stat.ilevel.enable") then return end + local itemSig, iLevel = private.GetItemDetail(hyperlink) + if not itemSig then return end + if not serverKey then serverKey = GetFaction() end + + local average, mean, stdev, variance, count, confidence + + local cacheSig = serverKey..itemSig..";"..iLevel + if pricecache[cacheSig] then + average, mean, stdev, variance, count, confidence = unpack(pricecache[cacheSig], 1, 6) + return average, mean, false, stdev, variance, count, confidence + end + + local stats = private.GetUnpackedStats(serverKey, itemSig) -- read only + if not stats[iLevel] then return end + + count = #stats[iLevel] + if (count < 1) then return end + + local total, number = 0, 0 + for i = 1, count do + local price, stack = strsplit("/", stats[iLevel][i]) + price = tonumber(price) or 0 + stack = tonumber(stack) or 1 + if (stack < 1) then stack = 1 end + datapoints_price[i] = price + datapoints_stack[i] = stack + total = total + price + number = number + stack + end + mean = total / number + + if (count < 2) then return 0,0,0, mean, count end + + variance = 0 + for i = 1, count do + variance = variance + ((mean - datapoints_price[i]/datapoints_stack[i]) ^ 2); + end + + variance = variance / count; + stdev = variance ^ 0.5 + + local deviation = 1.5 * stdev + total = 0 -- recomputing with only data within deviation + number = 0 + + for i = 1, count do + local price,stack = datapoints_price[i], datapoints_stack[i] + if abs((price/stack) - mean) < deviation then + total = total + price + number = number + stack + end + end + + confidence = .01 + if (number > 0) then -- number<1 will happen if we have e.g. two big clusters: one at 1g and one at 10g + average = total / number + confidence = (.15*average)*(number^0.5)/(stdev) + confidence = private.GetCfromZ(confidence) + end + pricecache[cacheSig] = {average, mean, stdev, variance, count, confidence} + return average, mean, false, stdev, variance, count, confidence +end + +function lib.GetPriceColumns() + return "Average", "Mean", false, "Std Deviation", "Variance", "Count", "Confidence" +end + +local array = {} +function lib.GetPriceArray(hyperlink, serverKey) + if not get("stat.ilevel.enable") then return end + -- Clean out the old array + wipe(array) + + -- Get our statistics + local average, mean, _, stdev, variance, count, confidence = lib.GetPrice(hyperlink, serverKey) + + -- These 3 are the ones that most algorithms will look for + array.price = average or mean + array.seen = 0 + array.confidence = confidence + -- This is additional data + array.normalized = average + array.mean = mean + array.deviation = stdev + array.variance = variance + array.processed = count + + -- Return a temporary array. Data in this array is + -- only valid until this function is called again. + return array +end + +function private.SetupConfigGui(gui) + private.SetupConfigGui = nil + local id = gui:AddTab(lib.libName, lib.libType.." Modules") + --gui:MakeScrollable(id) + + gui:AddHelp(id, "what ilevel stats", + _TRANS('ILVL_Help_WhatIlevelStats') ,--What are ilevel stats? + _TRANS('ILVL_Help_WhatIlevelStatsAnswer') )--ilevel stats are the numbers that are generated by the iLevel module consisting of a filtered Standard Deviation calculation of item cost. + + gui:AddHelp(id, "filtered ilevel", + _TRANS('ILVL_Help_WhatFiltered') ,--What do you mean filtered? + _TRANS('ILVL_Help_WhatFilteredAnswer') )--Items outside a (1.5*Standard) variance are ignored and assumed to be wrongly priced when calculating the deviation. + + --all options in here will be duplicated in the tooltip frame + function private.addTooltipControls(id) + gui:AddHelp(id, "what standard deviation", + _TRANS('ILVL_Help_WhatStdDev') ,--What is a Standard Deviation calculation? + _TRANS('ILVL_Help_WhatStdDevAnswer') )--In short terms, it is a distance to mean average calculation. + + gui:AddHelp(id, "what normalized", + _TRANS('ILVL_Help_WhatNormalized') ,--What is the Normalized calculation? + _TRANS('ILVL_Help_WhatNormalizedAnswer') )--In short terms again, it is the average of those values determined within the standard deviation variance calculation. + + gui:AddHelp(id, "what confidence", + _TRANS('ILVL_Help_WhatConfidence') ,--What does confidence mean? + _TRANS('ILVL_Help_WhatConfidenceAnswer') )--Confidence is a value between 0 and 1 that determines the strength of the calculations (higher the better). + + gui:AddHelp(id, "why multiply stack size ilevel", + _TRANS('ILVL_Help_WhyStackSize') ,--Why have the option to multiply by stack size? + _TRANS('ILVL_Help_WhyStackSizeAnswer') )--The original Stat-ilevel multiplied by the stack size of the item, but some like dealing on a per-item basis. + + gui:AddControl(id, "Header", 0, _TRANS('ILVL_Interface_IlevelOptions') )--ilevel options + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + gui:AddControl(id, "Checkbox", 0, 1, "stat.ilevel.enable", _TRANS('ILVL_Interface_EnableILevelStats') )--Enable iLevel Stats + gui:AddTip(id, _TRANS('ILVL_HelpTooltip_EnableILevelStats') )--Allow iLevel to gather and return price data + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + + gui:AddControl(id, "Checkbox", 0, 4, "stat.ilevel.tooltip", _TRANS('ILVL_Interface_ShowiLevel') )--Show iLevel stats in the tooltips? + gui:AddTip(id, _TRANS('ILVL_HelpTooltip_ShowiLevel') )--Toggle display of stats from the iLevel module on or off + gui:AddControl(id, "Checkbox", 0, 6, "stat.ilevel.mean", _TRANS('ILVL_Interface_DisplayMean') )--Display Mean + gui:AddTip(id, _TRANS('ILVL_HelpTooltip_DisplayMean') )--Toggle display of 'Mean' calculation in tooltips on or off + gui:AddControl(id, "Checkbox", 0, 6, "stat.ilevel.normal", _TRANS('ILVL_Interface_DisplayNormalized') )--Display Normalized' + gui:AddTip(id, _TRANS('ILVL_HelpTooltip_DisplayNormalized') )--Toggle display of \'Normalized\' calculation in tooltips on or off + gui:AddControl(id, "Checkbox", 0, 6, "stat.ilevel.stdev", _TRANS('ILVL_Interface_DisplayStdDeviation') )--Display Standard Deviation + gui:AddTip(id, _TRANS('ILVL_HelpTooltip_DisplayStdDeviation') )--Toggle display of \'Standard Deviation\' calculation in tooltips on or off + gui:AddControl(id, "Checkbox", 0, 6, "stat.ilevel.confid", _TRANS('ILVL_Interface_DisplayConfidence') )--Display Confidence + gui:AddTip(id, _TRANS('ILVL_HelpTooltip_DisplayConfidence') )--Toggle display of \'Confidence\' calculation in tooltips on or off + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + gui:AddControl(id, "Checkbox", 0, 4, "stat.ilevel.quantmul", _TRANS('ILVL_Interface_MultiplyStack') )--Multiply by Stack Size + gui:AddTip(id, _TRANS('ILVL_HelpTooltip_MultiplyStack') )--Multiplies by current stack size if on + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + end + --This is the Tooltip tab provided by Auctioneer so all tooltip configuration is in one place + local tooltipID = AucAdvanced.Settings.Gui.tooltipID + + --now we create a duplicate of these in the tooltip frame + private.addTooltipControls(id) + if tooltipID then private.addTooltipControls(tooltipID) end +end + +function lib.ProcessTooltip(tooltip, name, hyperlink, quality, quantity, cost, ...) + if not get("stat.ilevel.tooltip") then return end + + if not quantity or quantity < 1 then quantity = 1 end + if not get("stat.ilevel.quantmul") then quantity = 1 end + local average, mean, _, stdev, var, count, confidence = lib.GetPrice(hyperlink) + + if (mean and mean > 0) then + tooltip:SetColor(0.3, 0.9, 0.8) + + tooltip:AddLine(_TRANS('ILVL_Tooltip_iLevelPrices'):format(count) )--iLevel prices (%s points): + + if get("stat.ilevel.mean") then + tooltip:AddLine(" ".._TRANS('ILVL_Tooltip_MeanPrice') , mean*quantity)--Mean price + end + if (average and average > 0) then + if get("stat.ilevel.normal") then + tooltip:AddLine(" ".._TRANS('ILVL_Tooltip_Normalized') , average*quantity)--Normalized + if (quantity > 1) then + tooltip:AddLine(" ".._TRANS('ILVL_Tooltip_Individually') , average)--(or individually) + end + end + if get("stat.ilevel.stdev") then + tooltip:AddLine(" ".._TRANS('ILVL_Tooltip_StdDeviation') , stdev*quantity)--Std Deviation + if (quantity > 1) then + tooltip:AddLine(" ".._TRANS('ILVL_Tooltip_Individually') , stdev)--(or individually) + end + + end + if get("stat.ilevel.confid") then + tooltip:AddLine(" ".._TRANS('ILVL_Tooltip_Confidence'):format((floor(confidence*1000))/1000) )--Confidence: %s + end + end + end +end + +function lib.OnLoad(addon) + default("stat.ilevel.tooltip", false) + default("stat.ilevel.mean", false) + default("stat.ilevel.normal", false) + default("stat.ilevel.stdev", true) + default("stat.ilevel.confid", true) + default("stat.ilevel.quantmul", true) + default("stat.ilevel.enable", true) + if private.InitData then private.InitData() end +end + +function lib.OnUnload() + private.RepackStats() +end + +function lib.ClearItem(hyperlink, serverKey) + local itemSig, iLevel, equipPos, quality = private.GetItemDetail(hyperlink) + if not itemSig then return end + + if not serverKey then serverKey = GetFaction() end + local stats = private.GetUnpackedStats(serverKey, itemSig, true) + if stats[iLevel] then + stats[iLevel] = nil + private.RepackStats() + private.ResetCache() + local _, _, keyText = AucAdvanced.SplitServerKey(serverKey) + aucPrint(_TRANS('ILVL_Interface_ClearingItems'):format(iLevel, quality, equipPos, keyText))--Stat-iLevel: clearing data for iLevel=%d/quality=%d/equip=%d items for {{%s}} + return + end + aucPrint(_TRANS('ILVL_Interface_ItemNotFound') )--Stat-iLevel: item is not in database +end + +--[[ Database Management functions ]]-- + +local ILRealmData +local unpacked, updated = {}, {} + +function private.InitData() + private.InitData = nil + if not AucAdvancedStat_iLevelData then AucAdvancedStat_iLevelData = {} end + ILRealmData = AucAdvancedStat_iLevelData +end + +function lib.ClearData(serverKey) + serverKey = serverKey or GetFaction() + private.ResetCache() + if AucAdvanced.API.IsKeyword(serverKey, "ALL") then + wipe(ILRealmData) + wipe(unpacked) + wipe(updated) + aucPrint(_TRANS('ILVL_Help_SlashHelp5').." {{".._TRANS("ADV_Interface_AllRealms").."}}") --Clearing iLevel stats for // All realms + elseif ILRealmData[serverKey] then + ILRealmData[serverKey] = nil + unpacked[serverKey] = nil + -- 'updated' may contain orphaned entries - these will be cleaned up in next RepackStats + local _, _, keyText = AucAdvanced.SplitServerKey(serverKey) + aucPrint(_TRANS('ILVL_Help_SlashHelp5').." {{"..keyText.."}}") --Clearing iLevel stats for + end +end + +--[[ +itemSig, iLevel, equipPos, quality = GetItemDetail(hyperlink) +--]] +function private.GetItemDetail(hyperlink) + if type(hyperlink) ~= "string" then return end + if not hyperlink:match("item:%d") then return end + + local _,_, quality, iLevel, _,_,_,_, equipPos = GetItemInfo(hyperlink) + if not quality or quality < 1 then return end + equipPos = tonumber(iTypes[equipPos]) + if not equipPos or equipPos < 1 then return end + local itemSig = ("%d:%d"):format(equipPos, quality) + + return itemSig, iLevel, equipPos, quality +end + +--[[ +stats = GetUnpackedStats (serverKey, itemSig, writing) +Obtain a cached data table for itemSig in serverKey's data. +Set writing to true if you intend to change the data +Caution: if you set 'writing' to true, RepackStats() must be called before the end of the session to save the changes +--]] +function private.GetUnpackedStats(serverKey, itemSig, writing) + local stats = unpacked[serverKey] and unpacked[serverKey][itemSig] + if stats then + if writing then + updated[stats] = true + end + return stats + end + + local realmdata = ILRealmData[serverKey] + if not realmdata then + if not AucAdvanced.SplitServerKey(serverKey) then + error("Invalid serverKey passed to Stat-iLevel") + end + realmdata = {} + ILRealmData[serverKey] = realmdata + end + + stats = private.UnpackStats(realmdata, itemSig) + + if not unpacked[serverKey] then unpacked[serverKey] = {} end + unpacked[serverKey][itemSig] = stats + if writing then + updated[stats] = true + end + + return stats +end + +--[[ +RepackStats() +Write any changed tables in the unpacked cache back to ILRealmData +--]] +function private.RepackStats() + if not next(updated) then return end -- bail out if no updated entries + for serverKey, realmData in pairs(unpacked) do + for item, stats in pairs(realmData) do + if updated[stats] then + local packed = private.PackStats(stats) + if packed == "" then + ILRealmData[serverKey][item] = nil -- delete empty entries from the database + else + ILRealmData[serverKey][item] = packed + end + end + end + end + wipe(updated) +end + +--[[ Subfunctions ]]-- + +function private.UnpackStatIter(data, ...) + local c = select("#", ...) + local v + for i = 1, c do + v = select(i, ...) + local property, info = strsplit(":", v) + property = tonumber(property) or property + if (property and info) then + local t= {strsplit(";", info)} + for k,v in ipairs(t) do + t[k] = tonumber(v) or v + end + data[property] = t + end + end +end +function private.UnpackStats(data, item) + local stats = {} + if (data and data[item]) then + private.UnpackStatIter(stats, strsplit(",", data[item])) + end + return stats +end +local tmp={} +function private.PackStats(data) + local ntmp=0 + for property, info in pairs(data) do + ntmp=ntmp+1 + local n = max(1, #info - KEEP_NUM_POINTS + 1) + tmp[ntmp] = property..":"..concat(info, ";", n) + end + return concat(tmp, ",", 1, ntmp) +end + +AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Stat-iLevel/iLevel.lua $", "$Rev: 4840 $") diff --git a/Auc-Util-FixAH/Auc-Util-FixAH.toc b/Auc-Util-FixAH/Auc-Util-FixAH.toc new file mode 100644 index 0000000..7442672 --- /dev/null +++ b/Auc-Util-FixAH/Auc-Util-FixAH.toc @@ -0,0 +1,13 @@ +## Title: Auc:Util:FixAH +## Notes: Works around a paging issue when viewing items in the Auction House "browse" frame. Simply forces all new searches to show the results beginning on the first page. +## +## Interface: 40000 +## Dependancies: Auc-Advanced +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Auc-Util-FixAH.toc 4155 2009-04-14 19:40:57Z aesir $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## +Load.xml diff --git a/Auc-Util-FixAH/Load.xml b/Auc-Util-FixAH/Load.xml new file mode 100644 index 0000000..4b08f87 --- /dev/null +++ b/Auc-Util-FixAH/Load.xml @@ -0,0 +1,7 @@ + + + + diff --git a/Auc-Util-FixAH/PageOneReturn.lua b/Auc-Util-FixAH/PageOneReturn.lua new file mode 100644 index 0000000..dc7ef7c --- /dev/null +++ b/Auc-Util-FixAH/PageOneReturn.lua @@ -0,0 +1,114 @@ +--[[ + Auctioneer - Fix for searches not returning to page one in Blizzard code. + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: Example.lua 3882 2008-12-02 16:36:58Z kandoko $ + URL: http://auctioneeraddon.com/ + + This is an Auctioneer module that temporarily patches known errors and issues + with Blizzard's code. + + 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 libName = "FixAH" +local libType = "Util" + +local lib,parent,private = AucAdvanced.NewModule(libType, libName) +if not lib then return end +local print,decode,_,_,replicate,empty,get,set,default,debugPrint,fill = AucAdvanced.GetModuleLocals() + +--[[ +The following functions are part of the module's exposed methods: + GetName() (required) Should return this module's full name + CommandHandler() (optional) Slash command handler for this module + Processor() (optional) Processes messages sent by Auctioneer + ScanProcessor() (optional) Processes items from the scan manager +* GetPrice() (required) Returns estimated price for item link +* GetPriceColumns() (optional) Returns the column names for GetPrice + OnLoad() (optional) Receives load message for all modules + + (*) Only implemented in stats modules; util modules do not provide + + +-- Auto disable if build version is not correct: +local requiredBuildLive = 10314 +local requiredBuildPTR = 10147 +local version, build = GetBuildInfo() +if (tonumber(build) ~= requiredBuildLive) and (tonumber(build) ~= requiredBuildPTR) then + print("AucAdvanced: {{"..libType..":"..libName.."}} not loading: Build ("..build..") detected. Requires Live build ("..requiredBuildLive.." or PTR build "..requiredBuildPTR.." )") + DisableAddOn("Auc-Util-FixAH") + return +end]] + +function lib.GetName() + return libName +end + +function lib.Processor(callbackType, ...) + if (callbackType == "config") then + private.SetupConfigGui(...) + elseif (callbackType == "auctionui") then + private.HookAH(...) + end +end + +lib.Processors = {} +function lib.Processors.config(callbackType, ...) + private.SetupConfigGui(...) +end +function lib.Processors.auctionui(callbackType, ...) + private.HookAH(...) +end + + +function lib.OnLoad() + --This function is called when your variables have been loaded. + --You should also set your Configator defaults here + print("AucAdvanced: {{"..libType..":"..libName.."}} loaded!") + AucAdvanced.Settings.SetDefault("util.fixah.pageonereturn", true) +end + +--[[ Local functions ]]-- + +function private.SetupConfigGui(gui) + -- The defaults for the following settings are set in the lib.OnLoad function + local id = gui:AddTab(libName) + gui:MakeScrollable(id) + gui:AddControl(id, "Header", 0, libName.." options") + gui:AddControl(id, "Checkbox", 0, 1, "util.fixah.pageonereturn", "Fix Blizzard search bug where new search stays on current page (requires reload)") +end + +function private.HookAH(...) + if (AucAdvanced.Settings.GetSetting("util.fixah.pageonereturn")) then + private.RealSearchButtonClick = BrowseSearchButton:GetScript("OnClick") + BrowseSearchButton:SetScript("OnClick", private.SearchButtonClick); + end +end + +function private.SearchButtonClick(...) + AuctionFrameBrowse.page = 0 + private.RealSearchButtonClick(...) +end + +AucAdvanced.RegisterRevision("$URL: http://dev.norganna.org/auctioneer/trunk/Auc-Advanced/Modules/Auc-Util-Example/Example.lua $", "$Rev: 3882 $") diff --git a/BeanCounter/BeanCounter.lua b/BeanCounter/BeanCounter.lua new file mode 100644 index 0000000..d96c4b9 --- /dev/null +++ b/BeanCounter/BeanCounter.lua @@ -0,0 +1,616 @@ +--[[ + Auctioneer Addon for World of Warcraft(tm). + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: BeanCounter.lua 4933 2010-10-13 17:16:14Z Nechckn $ + + BeanCounterCore - BeanCounter: Auction House History + URL: http://auctioneeraddon.com/ + + 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 it's designated purpose as per: + http://www.fsf.org/licensing/licenses/gpl-faq.html#InterpreterIncompat +]] +LibStub("LibRevision"):Set("$URL: http://svn.norganna.org/auctioneer/branches/5.9/BeanCounter/BeanCounter.lua $","$Rev: 4933 $","5.1.DEV.", 'auctioneer', 'libs') + +--AucAdvanced.Modules["Util"]["BeanCounter"] + +local select,ipairs,pairs=select,ipairs,pairs +local concat=table.concat +local tonumber,tostring,strsplit,strjoin=tonumber,tostring,strsplit,strjoin +local tinsert,tremove = tinsert,tremove + +local libName = "BeanCounter" +local libType = "Util" +local lib +BeanCounter={} +lib = BeanCounter +lib.API = {} +local private = { + --BeanCounterCore + playerName = UnitName("player"), + realmName = GetRealmName(), + AucModule, --registers as an auctioneer module if present and stores module local functions + faction = nil, + version = 3.00, + wealth, --This characters current net worth. This will be appended to each transaction. + compressed = false, + + playerData, --Alias for BeanCounterDB[private.realmName][private.playerName] + serverData, --Alias for BeanCounterDB[private.realmName] + playerSettings, --Alias for BeanCounterDBSettings[private.realmName][private.playerName] + DBSumEntry = 0, + DBSumItems = 0, + --BeanCounter Bids/posts + PendingBids = {}, + PendingPosts = {}, + + --BeanCounterMail + reconcilePending = {}, + inboxStart = {}, + serverVersion = select(4, GetBuildInfo()),--WOW 3.0 HACK +} + +local tooltip = LibStub("nTipHelper:1") +private.tooltip = tooltip + +lib.Private = private --allow beancounter's sub lua's access +--Taken from AucAdvCore +function BeanCounter.Print(...) + local output, part + for i=1, select("#", ...) do + part = select(i, ...) + part = tostring(part):gsub("{{", "|cffddeeff"):gsub("}}", "|r") + if (output) then output = output .. " " .. part + else output = part end + end + DEFAULT_CHAT_FRAME:AddMessage(output, 0.3, 0.9, 0.8) +end + +local print = BeanCounter.Print + +local function debugPrint(...) + if lib.GetSetting("util.beancounter.debugCore") then + private.debugPrint("BeanCounterCore",...) + end +end + +--used to allow beancounter to recive Processor events from Auctioneer. Allows us to send a search request to BC GUI +if AucAdvanced and AucAdvanced.NewModule then + private.AucModule = AucAdvanced.NewModule(libType, libName) --register as an Adv Module for callbacks + local get, set, default = select(7, AucAdvanced.GetModuleLocals()) --Get locals for use getting settings + private.AucModule.locals = {["get"] = get, ["set"] = set, ["default"] = default} + + function private.AucModule.Processor(callbackType, ...) + if (callbackType == "querysent") and lib.API.isLoaded then --if BeanCounter has disabled itself dont try looking for auction House links + local item = ... + if item.name then + if item.name ~= "" then + lib.API.search(item.name) + end + end + elseif (callbackType == "bidplaced") and lib.API.isLoaded then + private.storeReasonForBid(...) + end + end + private.AucModule.Processors = {} + function private.AucModule.Processors.querysent(callbackType, ...) + if lib.API.isLoaded then + local item = ... + if item.name then + if item.name ~= "" then + lib.API.search(item.name) + end + end + end + end + function private.AucModule.Processors.bidplaced(callbackType, ...) + if lib.API.isLoaded then + private.storeReasonForBid(...) + end + end +end + +-- lib.API.isLoaded is false until DB is ready and all gui and API elements have been created +lib.API.isLoaded = false +function lib.OnLoad(addon) + private.initializeDB() --create or initialize the saved DB + --OK we now have our Database ready, lets create an Alias to make refrencing easier + local db = BeanCounterDB + private.playerData = db[private.realmName][private.playerName] + private.serverData = db[private.realmName] + private.playerSettings = BeanCounterDBSettings[private.realmName][private.playerName] + private.wealth = private.playerSettings["wealth"] + --Upgrade DB if needed + private.UpgradeDatabaseVersion() + --Check if user is trying to use old client with newer database or if the database has failed to update + if private.version and private.playerData.version then + if private.version < private.playerData.version then + private.CreateErrorFrames("bean older", private.version, private.playerData.version) + return + elseif private.version ~= private.playerData.version then + private.CreateErrorFrames("failed update", private.version, private.playerData.version) + return + end + end + --Continue loading if the Database is ready + lib.MakeGuiConfig() --create the configurator GUI frame + private.CreateFrames() --create our framework used for AH and GUI + private.createDeleteItemPrompt() --create the item delete prompt + private.slidebar() --create slidebar icon + + private.scriptframe:RegisterEvent("PLAYER_MONEY") + private.scriptframe:RegisterEvent("PLAYER_ENTERING_WORLD") + private.scriptframe:RegisterEvent("MAIL_INBOX_UPDATE") + private.scriptframe:RegisterEvent("UI_ERROR_MESSAGE") + private.scriptframe:RegisterEvent("MAIL_SHOW") + private.scriptframe:RegisterEvent("MAIL_CLOSED") + private.scriptframe:RegisterEvent("UPDATE_PENDING_MAIL") + private.scriptframe:RegisterEvent("MERCHANT_SHOW") + private.scriptframe:RegisterEvent("MERCHANT_UPDATE") + private.scriptframe:RegisterEvent("MERCHANT_CLOSED") + private.scriptframe:RegisterEvent("UNIT_SPELLCAST_SENT") + + private.scriptframe:SetScript("OnUpdate", private.onUpdate) + + -- Hook all the methods we need + Stubby.RegisterAddOnHook("Blizzard_AuctionUi", "BeanCounter", private.AuctionUI) --To be standalone we cannot depend on AucAdv for lib.Processor + --mail + Stubby.RegisterFunctionHook("TakeInboxMoney", -100, private.PreTakeInboxMoneyHook) + Stubby.RegisterFunctionHook("TakeInboxItem", -100, private.PreTakeInboxItemHook) + + --Bids + Stubby.RegisterFunctionHook("PlaceAuctionBid", 50, private.postPlaceAuctionBidHook) + --Posting + Stubby.RegisterFunctionHook("StartAuction", -100, private.preStartAuctionHook) + --Vendor + --hooksecurefunc("BuyMerchantItem", private.merchantBuy) + + tooltip:Activate() + tooltip:AddCallback(private.processTooltip, 700) + + lib.API.isLoaded = true +end + +--Create the database +--server and player are passed by upgrade code when we need to reset a toons DB +function private.initializeDB(server, player) + if not server then server = private.realmName end + if not player then player = private.playerName end + + local db = BeanCounterDB + if not db then + db = {} + BeanCounterDB = db + end + + if not db[server] then + db[server] = {} + end + --data + if not db[server][player] then + local playerData = {} + db[server][player] = playerData + + playerData["vendorbuy"] = {} + playerData["vendorsell"] = {} + + playerData["postedAuctions"] = {} + playerData["completedAuctions"] = {} + playerData["failedAuctions"] = {} + + playerData["postedBids"] = {} + playerData["completedBidsBuyouts"] = {} + playerData["failedBids"] = {} + + playerData["completedAuctionsNeutral"] = {} + playerData["failedAuctionsNeutral"] = {} + + playerData["completedBidsBuyoutsNeutral"] = {} + playerData["failedBidsNeutral"] = {} + end + --settings + local db = BeanCounterDBSettings + if not db then + db = {} + BeanCounterDBSettings = db + end + + if not db[server] then + db[server] = {} + end + + if not db[server][player] then + local playerData = {} + db[server][player] = playerData + + playerData["version"] = private.version + playerData["faction"] = "unknown" --faction is recorded when we get the login event + playerData["wealth"] = GetMoney() + playerData["mailbox"] = {} + end + --item Name table + local db = BeanCounterDBNames + if not db then + db = {} + BeanCounterDBNames = db + end +end + +--[[ Configator Section ]]-- +--See BeanCounterConfig.lua +--sets sub luas print, get, set, localization and any future locals +function lib.getLocals() + return lib.Private, lib.Print, lib.GetSetting, lib.SetSetting, private.localizations +end + +--[[Sidebar Section]]-- +local sideIcon +function private.slidebar() + if LibStub then + local LibDataBroker = LibStub:GetLibrary("LibDataBroker-1.1", true) + if not LibDataBroker then return end + private.LDBButton = LibDataBroker:NewDataObject("BeanCounter", { + type = "launcher", + icon = "Interface\\AddOns\\BeanCounter\\Textures\\BeanCounterIcon", + OnClick = function(self, button) + private.GUI(self, button) + end, + }) + + function private.LDBButton:OnTooltipShow() + local count, items = private.DBSumEntry or 0, private.DBSumItems or 0 + self:AddLine("BeanCounter", 1,1,0.5, 1) + self:AddLine("Tracks your trading activities so that you may review your expenditures and income, perform searches and use this data to determine your successes and failures.", 1,1,0.5, 1) + self:AddLine(string.format("Items:|CFF00FFF1 %s |r", count), 1,1,0.5, 1 ) + self:AddLine(string.format("Entries:|CFF00FFF1 %s |r", items), 1,1,0.5, 1 ) + self:AddLine("|cff1fb3ff".."Click|r to view your activity report.", 1,1,0.5, 1) + self:AddLine("|cff1fb3ff".."Right-Click|r to edit the configuration", 1,1,0.5, 1) + end + + function private.LDBButton:OnEnter() + --print(self) + GameTooltip:SetOwner(self, "ANCHOR_NONE") + GameTooltip:SetPoint("TOPLEFT", self, "BOTTOMLEFT") + GameTooltip:ClearLines() + private.LDBButton.OnTooltipShow(GameTooltip) + GameTooltip:Show() + end + function private.LDBButton:OnLeave() + --print(self) + GameTooltip:Hide() + end + end +end + +--[[ Local functions ]]-- +function private.onUpdate() + private.mailonUpdate() +end + +--sum the Mats value +function private.sumDEValue() + local deMat, quantity = private.bag["link"], private.bag["quantity"] + local itemLink = private.bag["DElink"] + if not itemLink or not deMat or not quantity then + debugPrint("Missing data for DE event", itemLink, deMat, quantity) + return + end + + --use average sell price or fall back and use auctionner if possible + local settings = {["selectbox"] = {"1", "server"} ,["auction"] = true} + local data = lib.API.search(deMat, settings, true) + local profit, count = 0, 0 + local days = 7*24*60*60 --one week + if data and #data > 0 then + for i = #data, 1, -1 do + count = count + 1 + profit = profit + data[i][7] + if data[i][12] < time() - days then + --print(i,data[i][7], date("%c", data[i][12]), count) + break + end + end + profit = floor(profit/count) + end + --fall back to auctioneer + if AucAdvanced and AucAdvanced.API.GetMarketValue and profit < 1 then + profit = AucAdvanced.API.GetMarketValue(deMat) + end + --convert to itemID + deMat = lib.API.decodeLink(deMat) + --print("We Disnechnated ", itemLink, " into ", deMat, quantity, profit) + if not deMat or not quantity or not profit then return end + + local meta = string.join(":", "DE", deMat, quantity, profit) + meta = meta.."|" + private.attachMeta( itemLink, meta ) +end + +function private.attachMeta( itemLink, meta ) + local itemString = lib.API.getItemString(itemLink) + local itemID, suffix = lib.API.decodeLink(itemLink) + + for player, playerData in pairs(private.serverData) do + for DB, data in pairs(playerData) do + if DB == "completedBidsBuyouts" or DB == "completedBidsBuyoutsNeutral" then + if data[itemID] and data[itemID][itemString] then + for i, text in pairs(data[itemID][itemString]) do + local STACK, NET, DEPOSIT , FEE, BUY , BID, SELLERNAME, TIME, REASON, META = private.unpackString(text) + if not META:match("DE:(.-):(.-):(.-)|") then --no DE Meta so add + if META == 0 then + META = meta + else + META = META.."|"..meta + end + + local newText = private.packString(STACK, NET, DEPOSIT, FEE, BUY, BID, SELLERNAME, TIME, REASON, META) + + table.remove(data[itemID][itemString], i) + private.databaseAdd(DB, nil, itemString, newText) + --print(newText) + private.wipeSearchCache() --clear cached searches + return + end + end + end + end + end + end +end + +function private.onEvent(frame, event, arg, ...) + if (event == "PLAYER_MONEY") then + private.wealth = GetMoney() + private.playerSettings["wealth"] = private.wealth + + elseif (event == "PLAYER_ENTERING_WORLD") then --used to record one time info when player loads + private.scriptframe:UnregisterEvent("PLAYER_ENTERING_WORLD") --no longer care about this event after we get our current wealth + private.wealth = GetMoney() + private.playerSettings["wealth"] = private.wealth + + elseif (event == "MAIL_INBOX_UPDATE") or (event == "MAIL_SHOW") or (event == "MAIL_CLOSED") then + private.mailMonitor(event, arg, ...) + + elseif (event == "MERCHANT_CLOSED") or (event == "MERCHANT_SHOW") or (event == "MERCHANT_UPDATE") then + --private.vendorOnevent(event, arg, ...) + + elseif (event == "UPDATE_PENDING_MAIL") then + private.hasUnreadMail() + --we also use this event to get faction data since the faction often returns nil if called after "PLAYER_ENTERING_WORLD" + private.faction = UnitFactionGroup(UnitName("player")) + private.playerSettings["faction"] = private.faction or "unknown" + + elseif (event == "ADDON_LOADED") then + if arg == "BeanCounter" then + lib.OnLoad() + private.scriptframe:UnregisterEvent("ADDON_LOADED") + end + end +end +--scripts that handle recording DE events +local inDEState = false +local DisenchantLocale = GetSpellInfo(13262) or "Disenchant" --localized Spell name for Disenchant +function private.onEventDisenchant(frame, event, arg, spell, ...) + if event == "UNIT_SPELLCAST_SUCCEEDED" and arg == "player" and spell == DisenchantLocale then + inDEState = true + private.bag = {} + elseif event == "ITEM_LOCK_CHANGED" and inDEState then + local bagID, slot = arg, spell + local link = GetContainerItemLink(bagID, slot) + private.bag["DElink"] = link + elseif event == "LOOT_OPENED" and inDEState then --what did it DE into + for slot = 1, GetNumLootItems() do + local link = GetLootSlotLink(slot) + local _, _, quantity = GetLootSlotInfo(slot) + private.bag["link"] = link + private.bag["quantity"] = quantity + end + private.sumDEValue() + inDEState = false + end +end + +--[[ Utility Functions]]-- +--External Search Stub, allows other addons searches to search to display in BC or get results of a BC search +--Can be item Name or link or itemID +--If itemID or link search will be much faster than a plain text lookup +function lib.externalSearch(name, settings, queryReturn, count) + lib.ShowDeprecationAlert("Depreciated API Call Used", "") + --print("|CFFFF3300 WARNING: |CFFFFFFFF A module just called a depreciated Beancounter API") + --print(" |CFFFF3300 BeanCounter.externalSearch() ") + --print("Please update the module to use the function |CFFFFFF00 BeanCounter.API.search() ") + return lib.API.search(name, settings, queryReturn, count) or {} +end + +--will return any length arguments into a ; seperated string +local tmp={} +function private.packString(...) + local num = select("#", ...) + for n = 1, num do + local msg = select(n, ...) + if msg == nil then + msg = "" + elseif msg == true then + msg = "boolean true" + elseif msg == false then + msg = "boolean false" + elseif msg == "0" then + msg = "" + elseif msg == 0 then + msg = "" + elseif msg == "" then + msg = "" + end + tmp[n] = msg + end + return concat(tmp,";",1,num) +end +--Will split any string and return a table value, replace gsub with tbl compare, slightly faster this way. +function private.unpackString(text) + if not text then return end + local stack, money, deposit , fee, buyout , bid, buyer, Time, reason, meta = strsplit(";", text) + if stack == "" then stack = "0" end + if money == "" then money = "0" end + if deposit == "" then deposit = "0" end + if fee == "" then fee = "0" end + if buyout == "" then buyout = "0" end + if bid == "" then bid = "0" end + if buyer == "" then buyer = "0" end + if Time == "" then Time = "0" end + if reason == "" then reason = "0" end + if meta == "" then meta = "0" end + + return stack, money, deposit , fee, buyout , bid, buyer, Time, reason, meta +end +--[[ +Adds data to the database in proper place, adds link to itemName array, optionally compresses the itemstring into compact format +return false if data fails to write +]] +function private.databaseAdd(key, itemLink, itemString, value, compress) + --if we are passed a link and not both then extract the string + if itemLink and not itemString then + itemString = lib.API.getItemString(itemLink) + end + + if not key or not itemString or not value then + debugPrint("BeanCounter database add error: Missing required data") + debugPrint("Database:", key, "itemString:", itemString, "Value:", value, "compress:",compress) + return false + end + --some keys do not need the uniqueID so Always compress em + --if key == "failedBids" or key == "failedAuctions" or key == "failedAuctionsNeutral" or key == "failedBidsNeutral" then + --compress = true + --end + + local item, itemID, enchantID, jewelID1, jewelID2, jewelID3, jewelID4, suffixID, uniqueID, linkLevel = strsplit(":", itemString) + --if this will be a compressed entry replace uniqueID with 0 or its scaling factor + if compress then + suffixID = tonumber(suffixID) + --print(itemString) + if suffixID < 0 then --scaling factor built into uniqueID, extract it and store so we can create properly scaled itemLinks + uniqueID = bit.band(uniqueID, 65535) + -- print(uniqueID) + else + uniqueID = 0 + end + itemString = strjoin(":", item, itemID, enchantID, jewelID1, jewelID2, jewelID3, jewelID4, suffixID, uniqueID, linkLevel) + --print(itemString) + end + + if private.playerData[key][itemID] then --if ltemID exists + if private.playerData[key][itemID][itemString] then + tinsert(private.playerData[key][itemID][itemString], value) + else + private.playerData[key][itemID][itemString] = {value} + end + else + private.playerData[key][itemID]={[itemString] = {value}} + end + --Insert into the ItemName:ItemID dictionary array + if itemLink then + lib.API.storeItemLinkToArray(itemLink) + end + return true +end + +--remove item (for pending bids only atm) +function private.databaseRemove(key, itemID, itemLink, NAME, COUNT) + if key == "postedBids" then + local itemString = lib.API.getItemString(itemLink) + local _, suffix = lib.API.decodeLink(itemLink) + if private.playerData[key][itemID] and private.playerData[key][itemID][itemString] then + for i, v in pairs(private.playerData[key][itemID][itemString]) do + local postCount, postBid, postSeller, isBuyout, postTimeLeft, postTime, postReason = private.unpackString(v) + if postSeller and itemID and NAME then + if postSeller == NAME and tonumber(postCount) == COUNT then + --debugPrint("Removing entry from postedBids this is a match", itemID, NAME, "vs", postedName, postedCount, "vs", COUNT) + tremove(private.playerData[key][itemID][itemString], i)--Just remove the key + break + end + end + end + end + end +end + +--Store reason Code for BTM/SearchUI +--tostring(bid["link"]), tostring(bid["sellername"]), tostring(bid["count"]), tostring(bid["buyout"]), tostring(bid["price"]), tostring(bid["reason"])) +function private.storeReasonForBid(CallBack) + --debugPrint("bidplaced", CallBack) + if not CallBack then return end + + local itemLink, seller, count, buyout, price, reason = strsplit(";", CallBack) + local itemString = lib.API.getItemString(itemLink) + local itemID, suffix = lib.API.decodeLink(itemLink) + + if private.playerData.postedBids[itemID] and private.playerData.postedBids[itemID][itemString] then + for i, v in pairs(private.playerData.postedBids[itemID][itemString]) do + local postCount, postBid, postSeller, isBuyout, postTimeLeft, postTime, postReason = private.unpackString(v) + if postCount and postBid and itemID and price and count then + if postCount == count and postBid == price then + local text = private.packString(postCount, postBid, postSeller, isBuyout, postTimeLeft, postTime, reason) + --debugPrint("before", private.playerData.postedBids[itemID][itemString][i]) + private.playerData.postedBids[itemID][itemString][i] = text + --debugPrint("after", private.playerData.postedBids[itemID][itemString][i]) + break + end + end + end + end +end + +--Get item Info or a specific subset. accepts itemID or "itemString" or "itemName ONLY IF THE ITEM IS IN PLAYERS BAG" or "itemLink" +function private.getItemInfo(link, cmd) + --debugPrint(link, cmd) + local itemName, itemLink, itemRarity, itemLevel, itemMinLevel, itemType, itemSubType, itemStackCount, itemEquipLoc, itemTexture = GetItemInfo(link) + if not cmd and itemLink then --return all + return itemName, itemLink, itemRarity, itemLevel, itemMinLevel, itemType, itemSubType, itemStackCount, itemEquipLoc, itemTexture + + elseif itemLink and (cmd == "itemid") then + local itemID = lib.API.decodeLink(itemLink) + return itemID, itemLink + + elseif itemName and itemTexture and (cmd == "name") then + return itemName, itemTexture + + elseif itemStackCount and (cmd == "stack") then + return itemStackCount + end + return +end + +function private.debugPrint(...) + if lib.GetSetting("util.beancounter.debug") then + print(...) + end +end + +--[[DE event frame]] +private.DisenchantScriptframe = CreateFrame("Frame") +private.DisenchantScriptframe:RegisterEvent( "UNIT_SPELLCAST_SUCCEEDED" ) --Start watching for DE results +private.DisenchantScriptframe:RegisterEvent( "LOOT_OPENED" ) --record what teh DE result is +private.DisenchantScriptframe:RegisterEvent("ITEM_LOCK_CHANGED") --get item that was DE +private.DisenchantScriptframe:SetScript("OnEvent", private.onEventDisenchant) + +--[[Bootstrap Code]] +private.scriptframe = CreateFrame("Frame") +private.scriptframe:RegisterEvent("ADDON_LOADED") +private.scriptframe:SetScript("OnEvent", private.onEvent) diff --git a/BeanCounter/BeanCounter.toc b/BeanCounter/BeanCounter.toc new file mode 100644 index 0000000..8cd9e49 --- /dev/null +++ b/BeanCounter/BeanCounter.toc @@ -0,0 +1,19 @@ +## Title: BeanCounter +## Notes: Auction House transaction history to include purchases and sales, failures and successes. Data viewable in game and stored for use within other Auctioneer Modules and other AddOns. +## +## Interface: 40000 +## Dependencies: Stubby +## OptionalDeps: !nLog, Babylonian, DebugLib, SlideBar, Auc-Advanced, Configator, LibExtraTip, TipHelper, LibDataBroker +## SavedVariables: BeanCounterDB, BeanCounterDBSettings, BeanCounterDBNames, BeanCounterAccountDB +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: BeanCounter.toc 4933 2010-10-13 17:16:14Z Nechckn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## X-Part-Of: Auctioneer +## X-Max-Interface: 40000 +## + +libs\Load.xml +Embed.xml + diff --git a/BeanCounter/BeanCounterAPI.lua b/BeanCounter/BeanCounterAPI.lua new file mode 100644 index 0000000..ac85dbc --- /dev/null +++ b/BeanCounter/BeanCounterAPI.lua @@ -0,0 +1,553 @@ +--[[ + Auctioneer Addon for World of Warcraft(tm). + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: BeanCounterAPI.lua 4933 2010-10-13 17:16:14Z Nechckn $ + + BeanCounterAPI - Functions for other addons to get BeanCounter Data + URL: http://auctioneeraddon.com/ + + 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 it's designated purpose as per: + http://www.fsf.org/licensing/licenses/gpl-faq.html#InterpreterIncompat +]] +LibStub("LibRevision"):Set("$URL: http://svn.norganna.org/auctioneer/branches/5.9/BeanCounter/BeanCounterAPI.lua $","$Rev: 4933 $","5.1.DEV.", 'auctioneer', 'libs') + +local lib = BeanCounter +lib.API = {} +local private, print, get, set, _BC = lib.getLocals() + +--[[Use lib.API.isLoaded to check if beancounter is ready for use. It is false until DB is ready and all gui and API elements have been created]] + +local type,select,strsplit,strjoin,ipairs,pairs = type,select,strsplit,strjoin,ipairs,pairs +local tostring,tonumber,strlower = tostring,tonumber,strlower +local tinsert,tremove,sort = tinsert,tremove,sort +local wipe = wipe +local time = time + +local GetRealmName = GetRealmName +-- GLOBALS: BeanCounter, BeanCounterDB + + +local function debugPrint(...) + if get("util.beancounter.debugAPI") then + private.debugPrint("BeanCounterAPI",...) + end +end +--[[External Search Stub, allows other addons searches to search to display in BC or get results of a BC search +Can be item Name or Link or itemID +If itemID or Link search will be faster than a plain text lookup +]] +local _ +function lib.API.search(name, settings, queryReturn) + if get("util.beancounter.externalSearch") then --is option enabled and have we already searched for this name (stop spam) + --check for blank search request + if name == "" and not queryReturn then return end + name = tostring(name) + + --serverName is used as part of our cache ID string + local serverName + if settings and settings.servers and settings.servers[1] then + serverName = settings.servers[1] + else + serverName = GetRealmName() + end + + --playerName is also used as part of our cache ID string + local playerName + if settings and settings.selectbox and settings.selectbox[2] then + playerName = settings.selectbox[2] + else + playerName = "server" + end + + --the function getItemInfo will return a plain text name on itemID or itemLink searches and nil if a plain text search is passed + local itemID, itemLink, itemName + itemID, itemLink = private.getItemInfo(name, "itemid") + if not itemLink then + itemName = name + else + _, itemName = lib.API.getItemString(itemLink) + end + + local cached = private.checkSearchCache(itemName, serverName, playerName) + --return cached search + if queryReturn and cached then + return cached + end + --if API query lacks a settings table use whatever filter options the player has currently selected + if not settings then + settings = private.getCheckboxSettings() + end + + --search data + if itemLink then + --itemKey is used to filter results if exact is used. We need the key to remove of the XXX style items from returns + _, settings.suffix = lib.API.decodeLink(itemLink) + if settings.suffix == 0 then settings.suffix = nil end + --cache search request + private.searchByItemID(itemID, settings, queryReturn, nil, nil, itemName) + else + private.startSearch(itemName, settings, queryReturn) + end + --return data or displayItemName in select box + if queryReturn then + return private.checkSearchCache(itemName, serverName, playerName) + else + private.frame.searchBox:SetText(itemName) + end + end +end + + + + +-- Cache system for searches +local cache = setmetatable({}, {__mode="v"}) + +function private.checkSearchCache(name, serverName, playerName) + if not name or not serverName or not playerName then return end --nil safe the cache check + return cache[strlower(name)..serverName..playerName] +end + +function private.addSearchCache(name, data, serverName, playerName) + if not name or not serverName or not playerName then return end --nil safe the cache add + cache[strlower(name)..serverName..playerName] = data +end + +function private.wipeSearchCache() + wipe(cache) +end + + +--[[ Returns the Sum of all AH sold vs AH buys along with the date range +If no player name is supplied then the entire server profit will be totaled +if no item name is provided then all items will be returned +if no date range is supplied then a sufficently large range to cover the entire BeanCounter History will be used. +The meta data option mean s we will add in the estimated value from an items discenchant. +This really only works when looking ONLY at the bid/buy datasets. When looking at sales as well you will end up with higher than expected profits +]] +local function addDEValue(meta) + local mat, count, value = meta:match("DE:(%d-):(%d-):(%d-)|") + if mat and count and value then + return value*count + end + return 0 +end +function lib.API.getAHProfit(player, item, lowDate, highDate, includeMeta) + if not player or player == "" then player = "server" end + if not item then item = "" end + + local sum, low, high, date = 0, 9999999999, 0 + local settings = {["selectbox"] = {"1", player} , ["bid"] = true, ["auction"] = true, ["failedauction"] = true} + local tbl + --allow a already API searched data table to be passed instead of just a text string + if type(item) == "string" then + tbl = private.startSearch(item, settings, true) + elseif type(item) == "table" then + tbl = item + end + if not tbl then return end + + for i,v in pairs(tbl) do + date = tonumber(v[12]) + --if user passes a low and high date to use, filter out any not in the range + local dateRange = true + if date and lowDate and highDate then--if we have high/low then set range to false + dateRange = false + if lowDate < date and date < highDate then --set back to true if we meet conditions + dateRange = true + end + end + + if dateRange then + --store lower and upper date ranges + if date and date < low then low = date end + if date and date > high then high = date end + --Sum the trxns + if v[2] == _BC('UiAucSuccessful') then + sum = sum + v[5] - v[9] --sum sale - deposit. fee's have already been subtracted + elseif v[2] == _BC('UiAucExpired') then + sum = sum - v[9] --subtract failed deposits + elseif v[2] == _BC('UiWononBid') then + sum = sum - v[3] --subtract bought items + if includeMeta then + sum = sum + addDEValue(v[14]) --meta data is the exact mats/count/and market value at time of de + end + elseif v[2] == _BC('UiWononBuyout') then + sum = sum - v[4] + if includeMeta then + sum = sum + addDEValue(v[14]) + end + end + end + end + return sum, lowDate or low, highDate or high +end + +--[[This will return profits in date segments allow easy to create graphs +Similar to API.getProfit() This utility return a table containing the profit earned in day based segments. useful for graphing a change over time +example: entering (player, "arcane dust", 7) would return the the profit for arcane dust in 7 day segments starting from most recent to oldest +]] +function lib.API.getAHProfitGraph(player, item ,days) + if not player or player == "" then player = "server" end + if not item then item = "" end + if not days then days = 7 end + --Get data from BeanCounter + local settings = {["selectbox"] = {"1", player} , ["bid"] =true, ["auction"] = true} + local tbl = private.startSearch(item, settings, "none") + --Merge and edit provided table to needed format + for i,v in pairs(tbl) do + for a,b in pairs(v) do + tinsert(tbl, b) + end + end + --remove now redundant table entries + tbl.completedAuctions, tbl["completedBidsBuyouts"], tbl.failedAuctions, tbl.failedBids = nil, nil, nil, nil + --check if we actually have any results from the search + if #tbl == 0 then return {0}, 0, 0 end + --sort by date + sort(tbl, function(a,b) return a[5] > b[5] end) + --get min and max dates. + local high, low, count, sum, number = tbl[1][5], tbl[#tbl][5], 1, 0, 0 + local range = high - (days* 86400) + + tbl.sums = {} + tbl.sums[count] = {} + for i,v in ipairs(tbl) do + if tonumber(v[5]) >= range then + if v[4] == "Auction successful" then + number = tonumber(v[3]:match(".-;.-;.-;.-;.-;(.-);.*")) or 0 + sum = sum + number + elseif v[4] == "Auction won" then + number = tonumber(v[3]:match(".-;.-;.-;.-;.-;.-;(.-);.*")) or 0 + sum = sum - number + end + tbl.sums[count] = sum + else + count = count + 1 + range = range - (days * 86400) + tbl.sums[count] = {} + sum = 0 + if v[4] == "Auction successful" then + number = tonumber(v[3]:match(".-;.-;.-;.-;.-;(.-);.*")) or 0 + sum = sum + number + elseif v[4] == "Auction won" then + number = tonumber(v[3]:match(".-;.-;.-;.-;.-;.-;(.-);.*")) or 0 + sum = sum - number + end + tbl.sums[count] = sum + end + end + return tbl.sums, low, high +end + +--[[ +Get Sold / Failed Ratio +Used by match beancounter, made into an API to allow other addons easier access to this data +This returns the Sold/Failed number of auctions and Sold/Failed number of items +Adds ability to use serverKey == realm.."-"..faction +]] +function lib.API.getAHSoldFailed(player, link, days, serverKey) + if not link or not player then return end + --check for server key or use home + local server + if serverKey then + server = lib.API.SplitServerKey(serverKey) + else + server = private.realmName + end + + if not BeanCounterDB[server] or not BeanCounterDB[server][player] then return end + local playerData = BeanCounterDB[server][player] --alias + + local itemID = lib.API.decodeLink(link) + if not itemID then return end + + local now = time() + local success, failed, sucessStack, failedStack = 0, 0, 0, 0 + --if we want to filter to a date range then we use this, if we want EVERY trxn uses second lookup + --the second lookup is mesurably faster but not noticable in real use due to not having to expand the DB. 100 trxns may have a 0.0001 sec diffrence + if days then + days = days * 86400 --days to seconds + if playerData["completedAuctions"][itemID] then + for key in pairs(playerData["completedAuctions"][itemID] ) do + for i, text in pairs(playerData["completedAuctions"][itemID][key]) do + local stack, _, _, _, _, _, _, auctime = strsplit(";", text) + auctime, stack = tonumber(auctime), tonumber(stack) + + if (now - auctime) < (days) then + success = success + 1 + sucessStack = sucessStack + stack + end + end + end + end + if playerData["failedAuctions"][itemID] then + for key in pairs(playerData["failedAuctions"][itemID]) do + for i, text in pairs(playerData["failedAuctions"][itemID][key]) do + local stack, _, _, _, _, _, _, auctime = strsplit(";", text) + auctime, stack = tonumber(auctime), tonumber(stack) + + if (now - auctime) < (days) then + failed = failed + 1 + failedStack = failedStack + stack + end + end + end + end + else + if private.playerData then + if playerData["completedAuctions"][itemID] then + for key in pairs(playerData["completedAuctions"][itemID] ) do + success = success + #playerData["completedAuctions"][itemID][key] + end + end + if playerData["failedAuctions"][itemID] then + for key in pairs(playerData["failedAuctions"][itemID]) do + failed = failed + #playerData["failedAuctions"][itemID][key] + end + end + end + end + + return success, failed, sucessStack, failedStack +end +--[[Change or add a reason code to a transaction]] +function lib.API.updatedReason(serverKey, newReason, itemLink, bid, buy, net, stack, sellerName, deposit, fee, currentReason, Time) + --to string all number values for comparison to stored data + bid, buy, net, stack, deposit, fee, Time = tostring(bid), tostring(buy), tostring(net), tostring(stack), tostring(deposit), tostring(fee), tostring(Time) + --convert ... back to 0 + if currentReason == "..." then currentReason = "0" end + if sellerName == "..." then sellerName = "0" end + --if no serverKey provided use current server + local server = lib.API.SplitServerKey(serverKey) or private.realmName + local itemString = lib.API.getItemString(itemLink) + local itemID, suffix = lib.API.decodeLink(itemLink) + + for player, playerData in pairs(BeanCounterDB[server]) do + for DB, data in pairs(playerData) do + if DB == "failedBids" or DB == "failedAuctions" or DB == "completedAuctions" or DB == "completedBidsBuyouts" or DB == "failedBidsNeutral" or DB == "failedAuctionsNeutral" or DB == "completedAuctionsNeutral" or DB == "completedBidsBuyoutsNeutral" then + if data[itemID] and data[itemID][itemString] then + for i, text in pairs(data[itemID][itemString]) do + local STACK, NET, DEPOSIT , FEE, BUY , BID, SELLERNAME, TIME, CURRENTREASON, LOCATION = private.unpackString(text) + if currentReason == CURRENTREASON and stack == STACK and sellerName == SELLERNAME and bid == BID and buy == BUY and net == NET and deposit == DEPOSIT and Time == TIME then + local newText = private.packString(STACK, NET, DEPOSIT , FEE, BUY , BID, SELLERNAME, TIME, newReason, LOCATION) + + table.remove(data[itemID][itemString], i) + private.databaseAdd(DB, nil, itemString, newText) + private.wipeSearchCache() --clear cached searches + return + end + end + end + end + end + end +end + + +--********************************************************************************************************************** +--ITEMLINK AND STRING API'S USE THESE IN PLACE OF LOCAL :MATCH() CALLS +--[[ Retrives the itemLink from the name array when passed an itemKey +we store itemKeys with a unique ID but our name array does not +]] +function lib.API.getArrayItemLink(itemString) + local itemID, suffix, uniqueID = lib.API.decodeLink(itemString) + local itemKey = itemID..":"..suffix + if BeanCounterDBNames[itemKey] then + return lib.API.createItemLinkFromArray(itemKey, uniqueID) --uniqueID is used as a scaling factor for "of the" suffix items + end + debugPrint("Searching DB for ItemID..", suffix, itemID, "Failed Item does not exist") + return +end + +--[[Converts the compressed link stored in the itemIDArray back to a standard blizzard format]] +function lib.API.createItemLinkFromArray(itemKey, uniqueID) + if BeanCounterDBNames[itemKey] then + if not uniqueID then uniqueID = 0 end + local itemID, suffix = strsplit(":", itemKey) + local color, name = strsplit(";", BeanCounterDBNames[itemKey]) + return strjoin("", "|", color, "|Hitem:", itemID,":0:0:0:0:0:", suffix, ":", uniqueID, ":80|h[", name, "]|h|r") + end + return +end +--[[Convert and store an itemLink into teh compressed format used in teh itemIDArray]] +function lib.API.storeItemLinkToArray(itemLink) + if not itemLink then return end + local color, itemID, suffix, name = itemLink:match("|(.-)|Hitem:(.-):.-:.-:.-:.-:.-:(.-):.+|h%[(.-)%]|h|r") + + if color and itemID and suffix and name then + BeanCounterDBNames[itemID..":"..suffix] = color..";"..name + end +end + +--[[Turns an itemLink into an itemString and extracts the itemName +Returns sanitized itemlinks. Since hyperlinks now vary depending on level of player who looks/creates them +]] +function lib.API.getItemString(itemLink) + if not itemLink or not type(itemLink) == "string" then return end + local itemString, itemName = itemLink:match("H(item:.-)|h%[(.-)%]") + if not itemString then return end + itemString = itemString:gsub("(item:[^:]+:[^:]+:[^:]+:[^:]+:[^:]+:[^:]+:[^:]+:[^:]+):%d+", "%1:80") + return itemString, itemName +end + +--[[Returns id, suffix, uniqueID when passed an itemLink or itemString, this a mildly tweaked version of the one found in AucAdv. +Handles Hitem:string, item:string, or full itemlinks +]] +local function breakHyperlink(match, matchlen, ...) + local v + local n = select("#", ...) + for i = 1, n do + v = select(i, ...) + if (v:sub(1,matchlen) == match) then + return strsplit(":", v) --for item:0:... style links bean stores + elseif(v:sub(2, matchlen+1) == match) then + return strsplit(":", v:sub(2)) --for Hitem:0:... normal itemStrings and hyperlinks + end + end +end +function lib.API.decodeLink(link) + local vartype = type(link) + if (vartype == "string") then + local lType, id, enchant, gem1, gem2, gem3, gemBonus, suffix, uniqueID, lichKing = breakHyperlink("item:", 5, strsplit("|", link)) + if (lType ~= "item") then return end + return id, suffix, uniqueID + end + return +end + +--[[Return REASON codes for tooltip or other use +This allows a way to get it that wont break if I change the internal DB layout +Pass a itemlink and stack count +Returns : "Reason, time of purchase, what you payed" or nil +NOTE: Reason could possibly be "", decided to return data anyways, calling module can decide if it want to use data or not +]] +function lib.API.getBidReason(itemLink, quantity) + if not itemLink or not quantity then return end + + local itemString = lib.API.getItemString(itemLink) + local itemID, suffix = lib.API.decodeLink(itemLink) + + if private.playerData["completedBidsBuyouts"][itemID] and private.playerData["completedBidsBuyouts"][itemID][itemString] then + for i,v in pairs(private.playerData["completedBidsBuyouts"][itemID][itemString]) do + local quan, _, _ , _, _, bid, _, Time, reason = private.unpackString(v) + if tonumber(quan) == tonumber(quantity) and reason and Time then + return reason, Time, tonumber(bid) + end + end + end + --not found on the current player lets see if we bought it on another player + for player in pairs(private.serverData) do + if private.serverData[player]["completedBidsBuyouts"][itemID] and private.serverData[player]["completedBidsBuyouts"][itemID][itemString] then + for i,v in pairs(private.serverData[player]["completedBidsBuyouts"][itemID][itemString]) do + local quan, _, _ , _, _, bid, _, Time, reason = private.unpackString(v) + if tonumber(quan) == tonumber(quantity) and reason and Time then + return reason, Time, tonumber(bid), player + end + end + end + end + + return --if nothing found return nil +end +--[[Any itemlink passed into this function will be prompted to remove from the database]] +function lib.API.deleteItem(itemLink) + if itemLink and itemLink:match("^(|c%x+|Hitem.+|h%[.+%])") then + private.deletePromptFrame.item:SetText(itemLink) + private.deletePromptFrame:Show() + else + print("Invalid itemLink") + end +end + +--[[Duplicate of the function in auctioneer, Splits and caches serverKey variable + realm, faction, localizedFaction = lib.API.SplitServerKey(serverKey) ]] +local splitcache = {} +local localizedfactions = { +["Alliance"] = FACTION_ALLIANCE, +["Horde"] = FACTION_HORDE, +["Neutral"] = COMBATLOG_FILTER_STRING_NEUTRAL_UNITS, -- if this is not the right context in other locales, may need to create our own localizer entry +} +function lib.API.SplitServerKey(serverKey) + if not serverKey then return end + local split = splitcache[serverKey] + if not split then + local realm, faction = strmatch(serverKey, "^(.+)%-(%u%l+)$") + local transfaction = localizedfactions[faction] + if not transfaction then return end + split = {realm, faction, realm.." - "..transfaction} + splitcache[serverKey] = split + end + return split[1], split[2], split[3] +end + + + + +--[[=========================================================================== +--|| Deprecation Alert Functions +--||=========================================================================]] +-- GLOBALS: debugstack, geterrorhandler + --Ths function was created by Shirik all thanks and blame go to him :P +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 + + caller, source, functionName = caller or "Unknown.lua:000", source or "Unknown.lua:000", functionName or "Unknown" --Stop nil errors if data is missing + 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 + debugPrint( + "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 BeanCounter 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.. + (comments and "\n\n"..comments or "") + ) + end + + end + +end diff --git a/BeanCounter/BeanCounterConfig.lua b/BeanCounter/BeanCounterConfig.lua new file mode 100644 index 0000000..71b6d32 --- /dev/null +++ b/BeanCounter/BeanCounterConfig.lua @@ -0,0 +1,493 @@ +--[[ + Auctioneer Addon for World of Warcraft(tm). + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: BeanCounterConfig.lua 4933 2010-10-13 17:16:14Z Nechckn $ + URL: http://auctioneeraddon.com/ + + BeanCounterConfig - Controls Configuration data + + 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 it's designated purpose as per: + http://www.fsf.org/licensing/licenses/gpl-faq.html#InterpreterIncompat +]] +LibStub("LibRevision"):Set("$URL: http://svn.norganna.org/auctioneer/branches/5.9/BeanCounter/BeanCounterConfig.lua $","$Rev: 4933 $","5.1.DEV.", 'auctioneer', 'libs') + +--Most of this code is from enchantrix by ccox +local lib = BeanCounter +local private, print, _, _, _BC = lib.getLocals() +local gui, settings + +local function debugPrint(...) + if get("util.beancounter.debugConfig") then + private.debugPrint("BeanCounterConfig",...) + end +end + +local function getUserSig() + local userSig = string.format("users.%s.%s", GetRealmName(), UnitName("player")) + return userSig +end + +local function getUserProfileName() + if (not settings) then settings = BeanCounterDBSettings end + local userSig = getUserSig() + return settings[userSig] or "Default" +end + +local function getUserProfile() + if (not settings) then settings = BeanCounterDBSettings end + local profileName = getUserProfileName() + if (not settings["profile."..profileName]) then + if profileName ~= "Default" then + profileName = "Default" + settings[getUserSig()] = "Default" + end + if profileName == "Default" then + settings["profile."..profileName] = {} + end + end + return settings["profile."..profileName] +end + + +local function cleanse( profile ) + if (profile) then + profile = {} + end +end + + +-- Default setting values +local Buyer, Seller = string.match(_BC('UiBuyerSellerHeader'), "(.*)/(.*)") --We have no direct translation so this is a temp workaround +private.settingDefaults = { + ["util.beancounter.ButtonExactCheck"] = false, + ["util.beancounter.ButtonClassicCheck"] = false, + ["util.beancounter.ButtonBidCheck"] = true, + ["util.beancounter.ButtonBidFailedCheck"] = true, + ["util.beancounter.ButtonAuctionCheck"] = true, + ["util.beancounter.ButtonAuctionFailedCheck"] = true, + + ["util.beancounter.activated"] = true, + ["util.beancounter.integrityCheckComplete"] = false, + ["util.beancounter.integrityCheck"] = true, + + --Tootip Settings + ["util.beancounter.displayReasonCodeTooltip"] = true, + ["util.beancounter.displaybeginerTooltips"] = true, + + --Debug settings + ["util.beancounter.debug"] = false, + ["util.beancounter.debugMail"] = true, + ["util.beancounter.debugCore"] = true, + ["util.beancounter.debugConfig"] = true, + ["util.beancounter.debugVendor"] = true, + ["util.beancounter.debugBid"] = true, + ["util.beancounter.debugPost"] = true, + ["util.beancounter.debugUpdate"] = true, + ["util.beancounter.debugFrames"] = true, + ["util.beancounter.debugAPI"] = true, + ["util.beancounter.debugSearch"] = true, + + ["util.beacounter.invoicetime"] = 5, + ["util.beancounter.mailrecolor"] = "off", + ["util.beancounter.externalSearch"] = true, + + ["util.beancounter.hasUnreadMail"] = false, + + --["util.beancounter.dateFormat"] = "%c", + ["dateString"] = "%c", + + --Data storage + ["monthstokeepdata"] = 48, + + --Color gradient + ["colorizeSearch"] = true, + ["colorizeSearchopacity"] = 0.2, + + --Search settings + ["numberofdisplayedsearchs"] = 500, + + --GUI column default widths + ["columnwidth.".._BC('UiNameHeader')] = 120, + ["columnwidth.".._BC('UiTransactions')] = 100, + ["columnwidth.".._BC('UiBidTransaction')] = 60, + ["columnwidth.".._BC('UiBuyTransaction')] = 60, + ["columnwidth.".._BC('UiNetHeader')] = 65, + ["columnwidth.".._BC('UiQuantityHeader')] = 40, + ["columnwidth.".._BC('UiPriceper')] = 70, + + ["columnwidth.".."|CFFFFFF00"..Seller.."/|CFF4CE5CC"..Buyer] = 90, + + ["columnwidth.".._BC('UiDepositTransaction')] = 58, + ["columnwidth.".._BC('UiPriceper')] = 50, + ["columnwidth.".._BC("UiFee")] = 70, + ["columnwidth.".._BC('UiReason')] = 70, + ["columnwidth.".._BC('UiDateHeader')] = 250, + + ["columnwidth.".._BC('UiProfit')] = 65, + ["ModTTShow"] = false, + } + +local function getDefault(setting) + local a,b,c = strsplit(".", setting) + + -- basic settings + if (a == "show") then return true end + if (b == "enable") then return true end + + -- lookup the simple settings + local result = private.settingDefaults[setting]; + + return result +end + +function lib.GetDefault(setting) + local val = getDefault(setting); + return val; +end +local tbl = {} +local function setter(setting, value) + if (not settings) then settings = BeanCounterDBSettings end + -- turn value into a canonical true or false + if value == 'on' then + value = true + elseif value == 'off' then + value = false + end + + -- for defaults, just remove the value and it'll fall through + if (value == 'default') or (value == getDefault(setting)) then + -- Don't save default values + value = nil + end + + --button to check database integrity pushed + if (setting == "database.validate") then + print("Checking") + private.integrityCheck(true) + value = nil + elseif (setting == "database.sort") then + private.sortArrayByDate() + value = time() + + --settings for gui + elseif (setting == "monthstokeepdata") then + local text = format("Enable purging transactions older than %s months from the database. \nYou must hold the SHIFT key to check this box since this will DELETE data.", gui.elements.monthstokeepdata:GetValue()/100 or 48) + gui.elements.oldDataExpireEnabled.textEl:SetText(text) + + --Always uncheck and set valure to off when they change the slider value as a safety precaution + local db = getUserProfile() + db["oldDataExpireEnabled"] = false + gui.elements.oldDataExpireEnabled:SetChecked(false) + elseif (setting == "oldDataExpireEnabled") and value then + if not IsShiftKeyDown() then --We wont allow the user to check this box unless shift key is down + print("You will need to hold down the SHIFT key to check this box") + lib.SetSetting("oldDataExpireEnabled", false) + return + end + end + + + --This is used to do the DateString + local a,b = strsplit(".", setting) + if (a == "dateString") then --used to update the Config GUI when a user enters a new date string + if not value then value = "%c" end + tbl = {} + for w in string.gmatch(value, "%%(.)" ) do --look for the date commands prefaced by % + tinsert(tbl, w) + end + + local valid, invalid = {['a']= 1,['A'] =1,['b'] =1,['B'] =1,['c']=1,['d']=1,['H']=1,['I']=1,['m']=1,['M']=1,['p']=1,['S']=1,['U']=1,['w']=1,['x']=1,['X']=1,['y']=1,['Y']=1} --valid date commands + + for i,v in pairs(tbl) do + if not valid[v] then invalid = v break end + end + if invalid then print("Invalid Date Format", "%"..invalid) return end --Prevent processing if we have an invalid command + + local text = gui.elements.dateString:GetText() + gui.elements.dateStringdisplay.textEl:SetText(_BC('C_DateStringExample').." "..date(text, 1196303661)) + end + + --[[Check for profile changes or store settings ]] + + if (a == "profile") then + if (setting == "profile.save") then + value = gui.elements["profile.name"]:GetText() + + -- Create the new profile + settings["profile."..value] = {} + + -- Set the current profile to the new profile + settings[getUserSig()] = value + -- Get the new current profile + local newProfile = getUserProfile() + + -- Clean it out and then resave all data + cleanse(newProfile) + gui:Resave() + + -- Add the new profile to the profiles list + local profiles = settings["profiles"] + if (not profiles) then + profiles = { "Default" } + settings["profiles"] = profiles + end + + -- Check to see if it already exists + local found = false + for pos, name in ipairs(profiles) do + if (name == value) then found = true end + end + + -- If not, add it and then sort it + if (not found) then + table.insert(profiles, value) + table.sort(profiles) + end + + print("ChatSavedProfile",value) + + elseif (setting == "profile.delete") then + -- User clicked the Delete button, see what the select box's value is. + value = gui.elements["profile"].value + + -- If there's a profile name supplied + if (value) then + -- Clean it's profile container of values + cleanse(settings["profile."..value]) + + -- Delete it's profile container + settings["profile."..value] = nil + + -- Find it's entry in the profiles list + local profiles = settings["profiles"] + if (profiles) then + for pos, name in ipairs(profiles) do + -- If this is it, then extract it + if (name == value and name ~= "Default") then + table.remove(profiles, pos) + end + end + end + + -- If the user was using this one, then move them to Default + if (getUserProfileName() == value) then + settings[getUserSig()] = 'Default' + end + + print("ChatDeletedProfile",value) + + end + + elseif (setting == "profile.default") then + -- User clicked the reset settings button + + -- Get the current profile from the select box + value = gui.elements["profile"].value + + -- Clean it's profile container of values + settings["profile."..value] = {} + + print("ChatResetProfile",value) + + elseif (setting == "profile") then + -- User selected a different value in the select box, get it + value = gui.elements["profile"].value + + -- Change the user's current profile to this new one + settings[getUserSig()] = value + + print("ChatUsingProfile",value) + + end + + -- Refresh all values to reflect current data + gui:Refresh() + else + -- Set the value for this setting in the current profile + local db = getUserProfile() + db[setting] = value + --setUpdated() + end + +end + +function lib.SetSetting(...) + setter(...) + if (gui) then + gui:Refresh() + end +end + +local function getter(setting) + if (not settings) then settings = BeanCounterDBSettings end + if not setting then return end + + local a,b,c = strsplit(".", setting) + if (a == 'profile') then + if (b == 'profiles') then + local pList = settings["profiles"] + if (not pList) then + pList = { "Default" } + end + return pList + end + end + + + + if (setting == 'profile') then + return getUserProfileName() + end + local db = getUserProfile() + if ( db[setting] ~= nil ) then + return db[setting] + else + return getDefault(setting) + end +end + +function lib.GetSetting(setting, default) + local option = getter(setting) + if ( option ~= nil ) then + return option + else + return default + end +end +local _, _, get, set, _ = lib.getLocals()--now we can set our get, set locals to the above functions +private.setter = setter +private.getter = getter +function lib.MakeGuiConfig() + if gui then return end + + local id + local Configator = LibStub:GetLibrary("Configator") + gui = Configator:Create(setter, getter) + + lib.Gui = gui + + gui:AddCat("BeanCounter") + + id = gui:AddTab(_BC('C_BeanCounterConfig')) --"BeanCounter Config") + gui:MakeScrollable(id) + gui:AddControl(id, "Header", 0, _BC('C_BeanCounterOptions')) --"BeanCounter options") + gui:AddControl(id, "Checkbox", 0 , 1, "ModTTShow", _BC('C_ModTTShow'))--Only show extra tooltip if Alt is pressed. + gui:AddTip(id, _BC('TTModTTShow'))--This option will display BeanCounter's extra tooltip only if Alt is pressed. + gui:AddControl(id, "Checkbox", 0, 1, "util.beancounter.displaybeginerTooltips", _BC('C_ShowBeginnerTooltips'))--"Show beginner tooltips on mouse over" + gui:AddTip(id, _BC('TTShowBeginnerTooltips')) --Turns on the beginner tooltips that display on mouse eover + + gui:AddControl(id, "Checkbox", 0, 1, "util.beancounter.displayReasonCodeTooltip", _BC('C_ShowReasonPurchase'))--Show reason for purchase in the games Tooltips + gui:AddTip(id, _BC('TTShowReasonPurchase'))--Turns on the SearchUI reason an item was purchased for in the tooltip + + gui:AddControl(id, "Checkbox", 0, 1, "util.beancounter.externalSearch", _BC('C_ExtenalSearch')) --"Allow External Addons to use BeanCounter's Search?") + gui:AddTip(id, _BC('TTExtenalSearch')) --"When entering a search in another addon, BeanCounter will also display a search for that item.") + + gui:AddControl(id, "Checkbox", 0, 1, "sendSearchBrowseFrame", _BC('C_SendToSearch')) --Add BeanCounter's searched item to the Main Auction House Window? + gui:AddTip(id, _BC('TT_SendToSearch')) --"When entering a search in BeanCounter it will also add the string to the AH browse frame. + + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + gui:AddControl(id, "WideSlider", 0, 1, "util.beacounter.invoicetime", 1, 20, 1, _BC('C_MailInvoiceTimeout')) --"Mail Invoice Timeout = %d seconds") + gui:AddTip(id, _BC('TTMailInvoiceTimeout')) --Chooses how long BeanCounter will attempt to get a mail invoice from the server before giving up. Lower == quicker but more chance of missing data, Higher == slower but improves chances of getting data if the Mail server is extremely busy. + + gui:AddControl(id, "Subhead", 0, _BC('C_MailRecolor')) --"Mail Re-Color Method") + gui:AddControl(id, "Selectbox", 0, 1, {{"off",_BC("NoRe-Color")},{"icon",_BC("Re-ColorIcons")},{"both",_BC("Re-ColorIconsandText")},{"text",_BC("Re-ColorText")}}, "util.beancounter.mailrecolor", _BC("MailRe-ColorMethod")) + gui:AddTip(id, _BC('TTMailRecolor')) --"Choose how Mail will appear after BeanCounter has scanned the Mail Box") + + gui:AddControl(id, "Text", 0, 1, "dateString", "|CCFFFCC00".._BC('C_DateString')) --"|CCFFFCC00Date format to use:") + gui:AddTip(id, _BC('TTDateString'))--"Enter the format that you would like your date field to show. Default is %c") + gui:AddControl(id, "Checkbox", 0, 1, "dateStringdisplay", "|CCFFFCC00".._BC('C_DateStringExample').." 11/28/07 21:34:21") --"|CCFFFCC00Example Date: 11/28/07 21:34:21") + gui:AddTip(id, _BC('TTDateStringExample'))--"Displays an example of what your formated date will look like") + + gui:AddControl(id, "Note", 0, 1, nil, nil, " ") + gui:AddControl(id, "Checkbox", 0, 1, "colorizeSearch", _BC('C_ColorizeSearch'))--Add a gradient color to each result in the search window + gui:AddTip(id, _BC('TT_ColorizeSearch'))--This option changes the color of the items lines in the BeanCounter search window. + + gui:AddControl(id, "NumeriSlider", 0, 3, "colorizeSearchopacity", 0, 1, 0.1, _BC('C_OpacityLevel')) --Opacity level + gui:AddTip(id, _BC('TT_OpacityLevel')) --This controls the level of opacity for the colored bars in the BeanCounter search window (if enabled) + + + gui:AddControl(id, "Subhead", 0, _BC('C_SearchConfiguration')) --Search Configuration + + gui:AddControl(id, "NumeriSlider", 0, 1, "numberofdisplayedsearchs", 500, 5000, 250, _BC('C_MaxDisplayedResults')) --Max displayed search results (from each database) + gui:AddTip(id, _BC('TT_MaxDisplayedResults')) --This controls the total number of results displayed in the scroll frame. + + gui:AddHelp(id, "what is invoice", + _BC('Q_MailInvoiceTimeout'), --"What is Mail Invoice Timeout?", + _BC('A_MailInvoiceTimeout') --"The length of time BeanCounter will wait on the server to respond to an invoice request. A invoice is the who, what, how of an Auction house mail" + ) + gui:AddHelp(id, "what is recolor", + _BC('Q_MailRecolor'), --"What is Mail Re-Color Method?", + _BC('A_MailRecolor') --"BeanCounter reads all mail from the Auction House, This option tells Beancounter how the user want's to Recolor the messages to make them look unread." + ) + gui:AddHelp(id, "what is tooltip", + _BC('Q_BeanCountersTooltip'),--What is BeanCounters Tooltip + _BC('A_BeanCountersTooltip')--BeanCounter will store the SearchUI reason an item was purchased and display it in the tooltip + ) + gui:AddHelp(id, "what is external", + _BC('Q_ExtenalSearch'), --"Allow External Addons to use BeanCounter?", + _BC('A_ExtenalSearch') --"Other addons can have BeanCounter search for an item to be displayed in BeanCounter's GUI. For example this allows BeanCounter to show what items you are looking at in Appraiser" + ) + gui:AddHelp(id, "what is date", + _BC('Q_DateString'), --"Date Format to use?", + _BC('A_DateString') --"This controls how the Date field of BeanCounter's GUI is shown. Commands are prefaced by % and multiple commands and text can be mixed. For example %a == %X would display Wed == 21:34:21" + ) + gui:AddHelp(id, "what is date command", + _BC('Q_DateStringCommands'), --"Acceptable Date Commands?", + _BC('A_DateStringCommands') --"Commands: \n %a = abr. weekday name, \n %A = weekday name, \n %b = abr. month name, \n %B = month name,\n %c = date and time, \n %d = day of the month (01-31),\n %H = hour (24), \n %I = hour (12),\n %M = minute, \n %m = month,\n %p = am/pm, \n %S = second,\n %U = week number of the year ,\n %w = numerical weekday (0-6),\n %x = date, \n %X = time,\n %Y = full year (2007), \n %y = two-digit year (07)" + ) + + id = gui:AddTab(_BC('C_DataMaintenance')) --"Data Maintenance" + lib.Id = id + gui:MakeScrollable(id) + gui:AddControl(id, "Header", 0, _BC('C_BeanCounterDatabaseMaintenance')) --"BeanCounter Database Maintenance" + gui:AddControl(id, "Subhead", 0, _BC('C_Resortascendingtime')) --"Resort all entries by ascending time" + gui:AddControl(id, "Button", 0, 1, "database.sort", _BC('C_ResortDatabase')) --"Resort Database" + gui:AddTip(id, _BC('TTResort Database'))--"This will scan Beancounter's Data sort all entries in ascending time order. This helps speed up the database compression functions" + + gui:AddControl(id, "Subhead", 0, _BC('C_ScanDatabase')) --"Scan Database for errors: Use if you have errors when searching BeanCounter. \n Backup BeanCounter's saved variables before using." + gui:AddControl(id, "Button", 0, 1, "database.validate", _BC('C_ValidateDatabase')) --"Validate Database" + gui:AddTip(id, _BC('TTValidateDatabase')) --"This will scan Beancounter's Data and attempt to correct any error it may find. Use if you are getting errors on search" + + gui:AddControl(id, "Subhead", 0, _BC('C_DatabaseLength')) --"Determines how long BeanCounter will save Auction House Transactions." + gui:AddControl(id, "Checkbox", 0, 1, "oldDataExpireEnabled", format("Enable purging transactions older than %s months from the database. \nYou must hold the SHIFT key to check this box since this will DELETE data.", get("monthstokeepdata") or 48) )--Enable purging transactions older than %s months from the database. This will DELETE data. + gui:AddTip(id, _BC('TTDataExpireEnabled'))--Data older than the selected time range will be DELETED + gui:AddControl(id, "NumeriSlider", 0, 3, "monthstokeepdata", 6, 48 , 2, "How many months of data to keep?") + + id = gui:AddTab("BeanCounter Debug") + gui:AddControl(id, "Header", 0, "BeanCounter Debug") + gui:AddControl(id, "Checkbox", 0, 1, "util.beancounter.debug", "Turn on BeanCounter Debugging.") + gui:AddControl(id, "Subhead", 0, "Reports From Specific Modules") + + gui:AddControl(id, "Checkbox", 0, 1, "util.beancounter.debug", "Turn on BeanCounter Debugging text. Dont check unless asked to do so") + gui:AddControl(id, "Checkbox", 0, 2, "util.beancounter.debugMail", "Mail") + gui:AddControl(id, "Checkbox", 0, 2, "util.beancounter.debugCore", "Core") + gui:AddControl(id, "Checkbox", 0, 2, "util.beancounter.debugConfig", "Config") + gui:AddControl(id, "Checkbox", 0, 2, "util.beancounter.debugVendor", "Vendor") + gui:AddControl(id, "Checkbox", 0, 2, "util.beancounter.debugBid", "Bid") + gui:AddControl(id, "Checkbox", 0, 2, "util.beancounter.debugPost", "Post") + gui:AddControl(id, "Checkbox", 0, 2, "util.beancounter.debugUpdate", "Update") + gui:AddControl(id, "Checkbox", 0, 2, "util.beancounter.debugFrames", "Frames") + gui:AddControl(id, "Checkbox", 0, 2, "util.beancounter.debugAPI", "API") + gui:AddControl(id, "Checkbox", 0, 2, "util.beancounter.debugSearch", "Search") + gui:AddControl(id, "Checkbox", 0, 2, "util.beancounter.debugTidyUp", "TidyUp") + +end diff --git a/BeanCounter/BeanCounterFrames.lua b/BeanCounter/BeanCounterFrames.lua new file mode 100644 index 0000000..051eab5 --- /dev/null +++ b/BeanCounter/BeanCounterFrames.lua @@ -0,0 +1,815 @@ +--[[ + Auctioneer Addon for World of Warcraft(tm). + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: BeanCounterFrames.lua 4880 2010-09-15 20:02:11Z Nechckn $ + URL: http://auctioneeraddon.com/ + + BeanCounterFrames - AuctionHouse UI for Beancounter + + 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 it's designated purpose as per: + http://www.fsf.org/licensing/licenses/gpl-faq.html#InterpreterIncompat +]] +LibStub("LibRevision"):Set("$URL: http://svn.norganna.org/auctioneer/branches/5.9/BeanCounter/BeanCounterFrames.lua $","$Rev: 4880 $","5.1.DEV.", 'auctioneer', 'libs') + +local lib = BeanCounter +local private, print, get, set, _BC = lib.getLocals() + +local function debugPrint(...) + if get("util.beancounter.debugFrames") then + private.debugPrint("BeanCounterFrames",...) + end +end + +local frame +local auctionUICreated +function private.AuctionUI() + if auctionUICreated then return end + frame = private.frame + + --Create the TAB + frame.ScanTab = CreateFrame("Button", "AuctionFrameTabUtilBeanCounter", AuctionFrame, "AuctionTabTemplate") + frame.ScanTab:SetText("BeanCounter") + frame.ScanTab:SetScript("OnEnter", function() private.buttonTooltips( frame.ScanTab, _BC('TT_BeanCounterAHTab')) end) -- Right click to display BeanCounter in a external window. + frame.ScanTab:SetScript("OnLeave", function() GameTooltip:Hide() end) + frame.ScanTab:RegisterForClicks("LeftButtonUp" , "RightButtonUp") + frame.ScanTab:Show() + PanelTemplates_DeselectTab(frame.ScanTab) + + if AucAdvanced then + AucAdvanced.AddTab(frame.ScanTab, frame) + else + private.AddTab(frame.ScanTab, frame) + end + local last = 1 + function frame.ScanTab.OnClick(self, button, index) + if not index then index = self:GetID() end + local tab = _G["AuctionFrameTab"..index] + + if button == "RightButton" and tab and tab:GetName() == "AuctionFrameTabUtilBeanCounter" then + local tab = _G["AuctionFrameTab"..last] + tab:Click() --use this so we stay on users currently selected tab + private.displayGUI( ) + return + end + last = index + + if (tab and tab:GetName() == "AuctionFrameTabUtilBeanCounter") then + --Modified Textures + AuctionFrameTopLeft:SetTexture("Interface\\AddOns\\BeanCounter\\Textures\\BC-TopLeft") + AuctionFrameTop:SetTexture("Interface\\AddOns\\BeanCounter\\Textures\\BC-Top") + AuctionFrameTopRight:SetTexture("Interface\\AddOns\\BeanCounter\\Textures\\BC-TopRight") + --Default AH textures + AuctionFrameBotLeft:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Bid-BotLeft") + AuctionFrameBot:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Bid-Bot") + AuctionFrameBotRight:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Bid-BotRight") + + if (AuctionDressUpFrame:IsVisible()) then + AuctionDressUpFrame:Hide() + AuctionDressUpFrame.reshow = true + end + private.displayGUI("ShowAHGUI") + else + if (AuctionDressUpFrame.reshow) then + AuctionDressUpFrame:Show() + AuctionDressUpFrame.reshow = nil + end + AuctionFrameMoneyFrame:Show() + private.displayGUI("HideAHGUI") + end + end + auctionUICreated = true + hooksecurefunc("AuctionFrameTab_OnClick", frame.ScanTab.OnClick) +end + +--function handles showing the standalone or intergrated UI +function private.displayGUI( action ) + frame = private.frame + + if action == "HideAHGUI" then --hide unless is on the external GUI + if not BeanCounterBaseFrame:IsVisible() then + frame:Hide() + else --when tab is created frame parent is set to AH, we dont want this + frame:SetParent("BeanCounterBaseFrame") + frame:SetAllPoints(BeanCounterBaseFrame) + end + elseif action == "ShowAHGUI" then + frame:SetParent(AuctionFrame) + frame:SetAllPoints("AuctionFrame") + private.relevelFrame(frame)--make sure our frame stays in proper order + BeanCounterBaseFrame:Hide() + frame:Show() + elseif BeanCounterBaseFrame:IsVisible() then + BeanCounterBaseFrame:Hide() + frame:Hide() + else + frame:SetParent("BeanCounterBaseFrame") + frame:SetAllPoints(BeanCounterBaseFrame) + private.relevelFrame(frame)--make sure our frame stays in proper order + BeanCounterBaseFrame:Show() + frame:Show() + end + --show the last postponed query if one was sent while frame was hidden + if private.storedQuery and frame:IsVisible() then + private.searchByItemID(private.storedQuery) + private.storedQuery = nil + end +end +--Change parent to our GUI base frame/ Also used to display our Config frame +function private.GUI(_, button) + if (button == "LeftButton") then + private.displayGUI() + else + if not lib.Gui:IsVisible() then + lib.Gui:Show() + else + lib.Gui:Hide() + end + end +end + +--Seperated frame items from frame creation, this should allow the same code to be reused for AH UI and Standalone UI +function private.CreateFrames() + + --Create the base frame for external GUI + local base = CreateFrame("Frame", "BeanCounterBaseFrame", UIParent) + base:SetFrameStrata("HIGH") + base:SetBackdrop({ + bgFile = "Interface/Tooltips/ChatBubble-Background", + edgeFile = "Interface/Tooltips/ChatBubble-BackDrop", + tile = true, tileSize = 32, edgeSize = 32, + insets = { left = 32, right = 32, top = 32, bottom = 32 } + }) + base:SetBackdropColor(0,0,0, 1) + base:Hide() + + base:SetPoint("CENTER", UIParent, "CENTER") + base:SetWidth(834.5) + base:SetHeight(450) + + + base:SetMovable(true) + base:EnableMouse(true) + base:SetToplevel(true) + base:SetResizable(true) + base:SetMinResize(834, 450) + base:SetMaxResize(1500, 450) + + --resize button for base GUI + base.Resizer = CreateFrame("Button", nil, base) + base.Resizer:SetPoint("BOTTOMRIGHT", base, "BOTTOMRIGHT", -8, 8) + base.Resizer:SetHeight(32) + base.Resizer:SetWidth(32) + base.Resizer:SetHighlightTexture("Interface\\Buttons\\UI-Common-MouseHilight") + base.Resizer:SetNormalTexture("Interface\\Buttons\\UI-SpellbookIcon-NextPage-Up") + base.Resizer:SetScript("OnMouseDown", function() base:StartSizing() end) + base.Resizer:SetScript("OnMouseUp", function() base:StopMovingOrSizing() end) + base.Resizer:SetScript("OnEnter", function() private.buttonTooltips( base.Resizer, _BC('Click and drag to resize window')) end) + base.Resizer:SetScript("OnLeave", function() GameTooltip:Hide() end) + + + base.Drag = CreateFrame("Button", nil, base) + base.Drag:SetPoint("TOPLEFT", base, "TOPLEFT", 10,-5) + base.Drag:SetPoint("TOPRIGHT", base, "TOPRIGHT", -10,-5) + base.Drag:SetHeight(6) + base.Drag:SetHighlightTexture("Interface\\FriendsFrame\\UI-FriendsFrame-HighlightBar") + + base.Drag:SetScript("OnMouseDown", function() base:StartMoving() end) + base.Drag:SetScript("OnMouseUp", function() base:StopMovingOrSizing() private.setter("configator.left", base:GetLeft()) private.setter("configator.top", base:GetTop()) end) + + base.DragBottom = CreateFrame("Button",nil, base) + base.DragBottom:SetPoint("BOTTOMLEFT", base, "BOTTOMLEFT", 10,5) + base.DragBottom:SetPoint("BOTTOMRIGHT", base, "BOTTOMRIGHT", -10,5) + base.DragBottom:SetHeight(6) + base.DragBottom:SetHighlightTexture("Interface\\FriendsFrame\\UI-FriendsFrame-HighlightBar") + + base.DragBottom:SetScript("OnMouseDown", function() base:StartMoving() end) + base.DragBottom:SetScript("OnMouseUp", function() base:StopMovingOrSizing() private.setter("configator.left", base:GetLeft()) private.setter("configator.top", base:GetTop()) end) + + --Close BeanCounter GUI Config frame + base.Done = CreateFrame("Button", nil, base, "OptionsButtonTemplate") + base.Done:SetPoint("BOTTOMRIGHT", base, "BOTTOMRIGHT", -50, 10) + base.Done:SetScript("OnClick", function() base:Hide() end) + base.Done:SetText(_BC('UiDone')) + + --Create the Actual Usable Frame + local frame = CreateFrame("Frame", "BeanCounterUiFrame", base) + private.frame = frame + frame:Hide() + + private.frame:SetPoint("TOPLEFT", base, "TOPLEFT") + private.frame:SetWidth(828) + private.frame:SetHeight(450) + + --Add Title to the Top + local title = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") + title:SetPoint("TOPLEFT", frame, "TOPLEFT", 80, -17) + title:SetText(_BC("UiAddonTitle")) + + local SelectBox = LibStub:GetLibrary("SelectBox") + local ScrollSheet = LibStub:GetLibrary("ScrollSheet") + + --Beginner Tooltips script display for all UI elements + function private.buttonTooltips(self, text) + if get("util.beancounter.displaybeginerTooltips") and text and self then + GameTooltip:SetOwner(self, "ANCHOR_BOTTOMRIGHT") + GameTooltip:SetText(text) + end + end + + --Add Configuration Button for those who dont use sidebar. + frame.Config = CreateFrame("Button", nil, frame, "OptionsButtonTemplate") + frame.Config:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -139, -13) + frame.Config:SetScript("OnClick", function() private.GUI() end) + frame.Config:SetText("Configure") + frame.Config:SetScript("OnEnter", function() private.buttonTooltips( frame.Config, _BC('TTOpenconfig')) end)--"Opens the BeanCounter configuration window" + frame.Config:SetScript("OnLeave", function() GameTooltip:Hide() end) + + + + --ICON box, used to drag item and display ICo for item being searched. Based Appraiser Code + function frame.IconClicked() + local objtype, _, link = GetCursorInfo() + ClearCursor() + if objtype == "item" then + local itemID = lib.API.decodeLink(link) + local _, itemName = lib.API.getItemString(link) + local itemTexture = select(2, private.getItemInfo(link, "name")) + frame.searchBox:SetText(itemName) + private.searchByItemID(itemID, private.getCheckboxSettings(), nil, 150, itemTexture, itemName) + end + end + + frame.slot = frame:CreateTexture(nil, "BORDER") + frame.slot:SetPoint("TOPLEFT", frame, "TOPLEFT", 23, -125) + frame.slot:SetWidth(45) + frame.slot:SetHeight(45) + frame.slot:SetTexCoord(0.15, 0.85, 0.15, 0.85) + frame.slot:SetTexture("Interface\\Buttons\\UI-EmptySlot") + + frame.icon = CreateFrame("Button", nil, frame) + frame.icon:SetPoint("TOPLEFT", frame.slot, "TOPLEFT", 3, -3) + frame.icon:SetWidth(38) + frame.icon:SetHeight(38) + frame.icon:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square.blp") + frame.icon:SetScript("OnClick", frame.IconClicked) + frame.icon:SetScript("OnReceiveDrag", frame.IconClicked) + frame.icon:SetScript("OnEnter", function() private.buttonTooltips( frame.icon, _BC('TT_ItemIconBox')) end) --"Drop an item here to start a search for it.\nDisplays current search's icon if possible" + frame.icon:SetScript("OnLeave", function() GameTooltip:Hide() end) + + --help text + frame.slot.help = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + frame.slot.help:SetPoint("LEFT", frame.slot, "RIGHT", 2, 7) + frame.slot.help:SetText(_BC('HelpGuiItemBox')) --"Drop item into box to search." + frame.slot.help:SetWidth(100) + + --Select box, used to chooose where the stats comefrom we show server/faction/player/all + frame.SelectBoxSetting = {"1","server"} + function private.ChangeControls(obj, arg1,arg2,...) + frame.SelectBoxSetting = {arg1, arg2} + end + --Default Server wide + --Used GLOBALSTRINGS for the horde alliance translations + local vals = {{"server", private.realmName.." ".._BC('UiData')},{"alliance", FACTION_ALLIANCE.." ".._BC('UiData')},{"horde", FACTION_HORDE.." ".._BC('UiData')}, {"neutral", _BC('MailNeutralAuctionHouse')}} + for name,data in pairs(private.serverData) do + table.insert(vals,{name, name.." ".._BC('UiData')}) + end + + frame.selectbox = CreateFrame("Frame", "BeanCounterSelectBox", frame) + frame.selectbox.box = SelectBox:Create("BeanCounterSelectBox", frame.selectbox, 140, private.ChangeControls, vals, "default") + frame.selectbox.box:SetPoint("TOPLEFT", frame, "TOPLEFT", 4,-80) + frame.selectbox.box.element = "selectBox" + frame.selectbox.box:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + frame.selectbox.box:SetPoint("BOTTOMLEFT", frame, "TOPLEFT", 0,-90) + frame.selectbox.box:SetText(private.realmName.." ".._BC('UiData')) + frame.selectbox.box.button:SetScript("OnEnter", function() private.buttonTooltips( frame.selectbox.box.button, _BC('TT_BeanCounterSelectBox')) end)--"Filter search results by server, player, faction" + frame.selectbox.box.button:SetScript("OnLeave", function() GameTooltip:Hide() end) + + --Search box + frame.searchBox = CreateFrame("EditBox", "BeancountersearchBox", frame, "InputBoxTemplate") + frame.searchBox:SetPoint("TOPLEFT", frame, "TOPLEFT", 29, -180) + frame.searchBox:SetAutoFocus(false) + frame.searchBox:SetHeight(15) + frame.searchBox:SetWidth(150) + frame.searchBox:SetScript("OnEnterPressed", function() + local itemName = frame.searchBox:GetText() + -- Allow beancounter searches to add the text to the AH frame + if AuctionFrame and get("sendSearchBrowseFrame") then + BrowseName:SetText(itemName) + end + private.startSearch(itemName, private.getCheckboxSettings()) + end) + frame.searchBox:SetScript("OnEnter", function() private.buttonTooltips( frame.searchBox, _BC("TT_SearchBox")) end) --"Enter search query's here or leave blank to search all" + frame.searchBox:SetScript("OnLeave", function() GameTooltip:Hide() end) + + + --Search Button + frame.searchButton = CreateFrame("Button", nil, frame, "OptionsButtonTemplate") + frame.searchButton:SetPoint("TOPLEFT", frame.searchBox, "BOTTOMLEFT", -6, -1) + frame.searchButton:SetText(_BC('UiSearch')) + frame.searchButton:SetScript("OnClick", function() + local itemName = frame.searchBox:GetText() + -- Allow beancounter searches to add the text to the AH frame + if AuctionFrame and get("sendSearchBrowseFrame") then + BrowseName:SetText(itemName) + end + private.startSearch(itemName, private.getCheckboxSettings()) + end) + --Clicking for BC search --Thanks for the code Rockslice + function private.ClickBagHook(_, _, self, button) + if (frame.searchBox and frame.searchBox:IsVisible()) then + local bag = self:GetParent():GetID() + local slot = self:GetID() + local link = GetContainerItemLink(bag, slot) + if link then + local itemID = lib.API.decodeLink(link) + local _, itemName = lib.API.getItemString(link) + local _, itemTexture = private.getItemInfo(link, "name") + if (button == "LeftButton") and (IsAltKeyDown()) and itemName then + debugPrint(itemName, itemID,itemTexture, link) + frame.searchBox:SetText(itemName) + private.searchByItemID(itemID, private.getCheckboxSettings(), nil, 150, itemTexture, itemName) + end + end + end + end + Stubby.RegisterFunctionHook("ContainerFrameItemButton_OnModifiedClick", -50, private.ClickBagHook) + + function private.ClickLinkHook(self, itemString, link, button) + if (frame.searchBox and frame.searchBox:IsVisible()) then + if link then + local itemID = lib.API.decodeLink(link) + local _, itemName = lib.API.getItemString(link) + local _, itemTexture = private.getItemInfo(link, "name") + if (button == "LeftButton") and (IsAltKeyDown()) and itemName then + --debugPrint(itemName, itemID,itemTexture, link) + frame.searchBox:SetText(itemName) + private.searchByItemID(itemID, private.getCheckboxSettings(), nil, 150, itemTexture, itemName) + end + end + end + end + hooksecurefunc("ChatFrame_OnHyperlinkShow", private.ClickLinkHook) + + --Check boxes to narrow our search + frame.exactCheck = CreateFrame("CheckButton", "BeancounterexactCheck", frame, "OptionsCheckButtonTemplate") + frame.exactCheck:SetChecked(get("util.beancounter.ButtonExactCheck")) --get the last used checked/unchecked value Then use below script to store state changes + frame.exactCheck:SetPoint("TOPLEFT", frame, "TOPLEFT", 19, -217) + frame.exactCheck:SetScript("OnClick", function() local on if frame.exactCheck:GetChecked() then on = true end set("util.beancounter.ButtonExactCheck", on) private.wipeSearchCache() end) + _G["BeancounterexactCheckText"]:SetText(_BC('UiExactNameSearch')) + frame.exactCheck:SetScript("OnEnter", function() private.buttonTooltips( frame.exactCheck, _BC('TT_ExactCheck')) end) --"Only match the Exact text in the search box" + frame.exactCheck:SetScript("OnLeave", function() GameTooltip:Hide() end) + + --search classic data + frame.neutralCheck = CreateFrame("CheckButton", "BeancounterneutralCheck", frame, "OptionsCheckButtonTemplate") + frame.neutralCheck:SetChecked(false) --Set this to false We only want this to be true/searchabe if there is a classic DB to search + frame.neutralCheck:SetScript("OnClick", function() local on if frame.neutralCheck:GetChecked() then on = true end set("util.beancounter.ButtonneutralCheck", on) private.wipeSearchCache() end) + _G["BeancounterneutralCheckText"]:SetText(_BC('UiNeutralCheckBox')) + frame.neutralCheck:SetPoint("TOPLEFT", frame, "TOPLEFT", 19, -242) + frame.neutralCheck:SetScript("OnEnter", function() private.buttonTooltips( frame.neutralCheck, _BC('TT_neutralCheck')) end) --"Display results from BeanCounter Classic Database" + frame.neutralCheck:SetScript("OnLeave", function() GameTooltip:Hide() end) + + + + --search bids + frame.bidCheck = CreateFrame("CheckButton", "BeancounterbidCheck", frame, "OptionsCheckButtonTemplate") + frame.bidCheck:SetChecked(get("util.beancounter.ButtonBidCheck")) + frame.bidCheck:SetScript("OnClick", function() local on if frame.bidCheck:GetChecked() then on = true end set("util.beancounter.ButtonBidCheck", on) private.wipeSearchCache() end) + _G["BeancounterbidCheckText"]:SetText(_BC('UiBids')) + frame.bidCheck:SetScale(0.85) + frame.bidCheck:SetPoint("TOPLEFT", frame, "TOPLEFT", 25, -335) + frame.bidCheck:SetScript("OnEnter", function() private.buttonTooltips( frame.bidCheck, _BC('TT_BidCheck')) end) --"Display items bought from the Auction House" + frame.bidCheck:SetScript("OnLeave", function() GameTooltip:Hide() end) + + + + frame.bidFailedCheck = CreateFrame("CheckButton", "BeancounterbidFailedCheck", frame, "OptionsCheckButtonTemplate") + frame.bidFailedCheck:SetChecked(get("util.beancounter.ButtonBidFailedCheck")) + frame.bidFailedCheck:SetScript("OnClick", function() local on if frame.bidFailedCheck:GetChecked() then on = true end set("util.beancounter.ButtonBidFailedCheck", on) private.wipeSearchCache() end) + frame.bidFailedCheck:SetScale(0.85) + _G["BeancounterbidFailedCheckText"]:SetText(_BC('UiOutbids')) + frame.bidFailedCheck:SetPoint("TOPLEFT", frame, "TOPLEFT", 25, -435) + frame.bidFailedCheck:SetScript("OnEnter", function() private.buttonTooltips( frame.bidFailedCheck, _BC('TT_BidFailedCheck')) end) --"Display items you were outbided on." + frame.bidFailedCheck:SetScript("OnLeave", function() GameTooltip:Hide() end) + + --search Auctions + frame.auctionCheck = CreateFrame("CheckButton", "BeancounterauctionCheck", frame, "OptionsCheckButtonTemplate") + frame.auctionCheck:SetChecked(get("util.beancounter.ButtonAuctionCheck")) + frame.auctionCheck:SetScript("OnClick", function() local on if frame.auctionCheck:GetChecked() then on = true end set("util.beancounter.ButtonAuctionCheck", on) private.wipeSearchCache() end) + _G["BeancounterauctionCheckText"]:SetText(_BC('UiAuctions')) + frame.auctionCheck:SetScale(0.85) + frame.auctionCheck:SetPoint("TOPLEFT", frame, "TOPLEFT", 25, -360) + frame.auctionCheck:SetScript("OnEnter", function() private.buttonTooltips( frame.auctionCheck, _BC('TT_AuctionCheck')) end) --"Display items sold at the Auction House" + frame.auctionCheck:SetScript("OnLeave", function() GameTooltip:Hide() end) + + + + frame.auctionFailedCheck = CreateFrame("CheckButton", "BeancounterauctionFailedCheck", frame, "OptionsCheckButtonTemplate") + frame.auctionFailedCheck:SetChecked(get("util.beancounter.ButtonAuctionFailedCheck")) + frame.auctionFailedCheck:SetScript("OnClick", function() local on if frame.auctionFailedCheck:GetChecked() then on = true end set("util.beancounter.ButtonAuctionFailedCheck", on) private.wipeSearchCache() end) + frame.auctionFailedCheck:SetScale(0.85) + _G["BeancounterauctionFailedCheckText"]:SetText(_BC('UiFailedAuctions')) + frame.auctionFailedCheck:SetPoint("TOPLEFT", frame, "TOPLEFT", 25, -460) + frame.auctionFailedCheck:SetScript("OnEnter", function() private.buttonTooltips( frame.auctionFailedCheck, _BC('TT_AuctionFailedCheck')) end) --Display items you failed to sell. + frame.auctionFailedCheck:SetScript("OnLeave", function() GameTooltip:Hide() end) + + --[[search Purchases (vendor/trade) + frame.buyCheck = CreateFrame("CheckButton", "BeancounterbuyCheck", frame, "OptionsCheckButtonTemplate") + frame.buyCheck:SetChecked(true) + _G[BeancounterbuyCheck:GetName().."Text"]:SetText("Buys") + frame.buyCheck:SetPoint("TOPLEFT", frame, "TOPLEFT", 19, -255) + --search Sold (vendor/trade) + frame.sellCheck = CreateFrame("CheckButton", "BeancountersellCheck", frame, "OptionsCheckButtonTemplate") + frame.sellCheck:SetChecked(true) + _G[BeancountersellCheck:GetName().."Text"]:SetText("Sold") + frame.sellCheck:SetPoint("TOPLEFT", frame, "TOPLEFT", 19, -330)]] + --creates teh report text that tells info on # of entries + frame.DBCount = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + frame.DBCount:SetPoint("TOPLEFT", frame, "TOPLEFT", 70, -40) + frame.DBCount:SetText("Items: "..private.DBSumEntry) + + frame.DBItems = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + frame.DBItems:SetPoint("TOPLEFT", frame, "TOPLEFT", 70, -55) + frame.DBItems:SetText("Entries: "..private.DBSumItems) + private.sumDatabase() --Sums database Done on first Start and Search of the session + + --Edit box for changing UI reason codes + frame.reasonEditBox = CreateFrame("EditBox", nil, frame, "InputBoxTemplate") + frame.reasonEditBox:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, 0) + frame.reasonEditBox:SetAutoFocus(true) + frame.reasonEditBox:Hide() + frame.reasonEditBox:SetHeight(15) + frame.reasonEditBox:SetFrameStrata("DIALOG") + frame.reasonEditBox:SetWidth(30) + frame.reasonEditBox:SetScript("OnEscapePressed", function() frame.reasonEditBox:Hide() end) + frame.reasonEditBox:SetScript("OnEnterPressed", function() + local self = frame.resultlist.sheet + self.selected = self.selectedForEdit[1] --pass stored selection back to frame + + local data = frame.resultlist.sheet:GetSelection() + local newReason = frame.reasonEditBox:GetText() + if data then + local itemLink, bid, buy, net, quanity, seller, deposit, fee, currentReason, date = data[1], data[3], data[4], data[5], data[6], data[8], data[9], data[10], data[11], data[12] + lib.API.updatedReason(nil, newReason, itemLink, bid, buy, net, quanity, seller, deposit, fee, currentReason, date) + end + + --update the current display data to reflect change + local rows = self.rows + local data = self.data + local sort = self.sort + + local vPos = math.floor(self.panel.vPos) + local hSize = self.hSize + local rowNum = sort[vPos + self.selectedForEdit[2]] + local rowPos = (rowNum-1)*hSize -- + local pos = rowPos + self.selectedForEdit[3] + + self.data[pos] = newReason + self:Render() + + frame.reasonEditBox:Hide() + end) + + + --Create the results window + frame.resultlist = CreateFrame("Frame", nil, frame) + frame.resultlist:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + edgeFile = "Interface/Tooltips/UI-Tooltip-Border", + tile = true, tileSize = 32, edgeSize = 16, + insets = { left = 5, right = 5, top = 5, bottom = 5 } + }) + + frame.resultlist:SetBackdropColor(0, 0, 0.0, 0.5) + frame.resultlist:SetPoint("TOPLEFT", frame, "TOPLEFT", 187, -32.5) --change anchors to allow for vert resize + frame.resultlist:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -3, 0) + frame.resultlist:SetPoint("BOTTOM", frame, "BOTTOM", 0, 35) + + --Scripts that are executed when we mouse over a TOOLTIP frame + function private.scrollSheetOnEnter(button, row, index) + local link, name, _ + link = frame.resultlist.sheet.rows[row][index]:GetText() or "FAILED LINK" + if link:match("^(|c%x+|H.+|h%[.+%])") then + _, name = lib.API.getItemString(link) + end + GameTooltip:SetOwner(button, "ANCHOR_RIGHT") + if frame.resultlist.sheet.rows[row][index]:IsShown()then --Hide tooltip for hidden cells + if link and name then + GameTooltip:SetHyperlink(link) + --private.tooltip:ShowItemLink(GameTooltip, link, 1) + end + end + end + --records the column width changes + --store width by header name, that way if column reorginizing is added we apply size to proper column + function private.onResize(self, column, width) + if not width then + set("columnwidth."..self.labels[column]:GetText(), "default") --reset column if no width is passed. We use CTRL+rightclick to reset column + self.labels[column].button:SetWidth(get("columnwidth."..self.labels[column]:GetText())) + else + set("columnwidth."..self.labels[column]:GetText(), width) + end + end + --Allows user to shift click on itemlinks in beacounter to Chat or BeanCounters select box + function private.scrollSheetOnClick(button, row, column) + local self = frame.resultlist.sheet + local clickedFrame = self.rows[row][column] + if not clickedFrame:IsVisible() then return end --Old data is still in the frames, just hidden + + local text = clickedFrame:GetText() + if not text then text = "" end + + local columnName = self.labels[column]:GetText() + if columnName == _BC('UiNameHeader') then + if IsShiftKeyDown() then + ChatEdit_InsertLink(text)--sends to chat or auction house + elseif IsAltKeyDown() then -- Search for the item in BeanCounter + private.wipeSearchCache() + local _, text = lib.API.getItemString(text) --convert itemlink to plain text + frame.searchBox:SetText(text) + private.startSearch(text, private.getCheckboxSettings()) + end + elseif columnName == _BC('UiReason') then + frame.reasonEditBox:ClearAllPoints() + frame.reasonEditBox:SetAllPoints(clickedFrame) + frame.reasonEditBox:ClearFocus() --clear focus then set so we highlight current text + frame.reasonEditBox:SetText(text) + frame.reasonEditBox:Show() + frame.reasonEditBox:SetFocus() + self.selectedForEdit = {self.sort[row + math.floor(self.panel.vPos)], row, column} + + end + end + + local Buyer, Seller = string.match(_BC('UiBuyerSellerHeader'), "(.*)/(.*)") + frame.resultlist.sheet = ScrollSheet:Create(frame.resultlist, { + { _BC('UiNameHeader'), "TOOLTIP", get("columnwidth.".._BC('UiNameHeader')) }, + { _BC('UiTransactions'), "TEXT", get("columnwidth.".._BC('UiTransactions')) }, + + {_BC('UiBidTransaction') , "COIN", get("columnwidth.".._BC('UiBidTransaction')) }, + { _BC('UiBuyTransaction') , "COIN", get("columnwidth.".._BC('UiBuyTransaction')) }, + { _BC('UiNetHeader'), "COIN", get("columnwidth.".._BC('UiNetHeader')) }, + { _BC('UiQuantityHeader'), "TEXT", get("columnwidth.".._BC('UiQuantityHeader')) }, + { _BC('UiPriceper'), "COIN", get("columnwidth.".._BC('UiPriceper')) }, + + { "|CFFFFFF00"..Seller.."/|CFF4CE5CC"..Buyer, "TEXT", get("columnwidth.".."|CFFFFFF00"..Seller.."/|CFF4CE5CC"..Buyer) }, + + { _BC('UiDepositTransaction'), "COIN", get("columnwidth.".._BC('UiDepositTransaction')) }, + { _BC("UiFee"), "COIN", get("columnwidth.".._BC("UiFee")) }, + { _BC('UiReason'), "TEXT", get("columnwidth.".._BC('UiReason')) }, + { _BC('UiDateHeader'), "TEXT", get("columnwidth.".._BC('UiDateHeader')) }, + { _BC('Profit'), "COIN", get("columnwidth.".._BC('UiProfit')) }, + {"", "TEXT", 0.001 }, --metadata thats not used in visual display + } ) + + --Add tooltip help to the scrollframe headers + for i = 1, #frame.resultlist.sheet.labels do + local self = frame.resultlist.sheet.labels[i].button + self:SetScript("OnEnter", function() private.buttonTooltips( self, _BC('TT_ScrollHeader') ) end) --Rightclick+Drag to move\nALT++RightClick to resize\nCTRL+RightClick to reset + self:SetScript("OnLeave", function() GameTooltip:Hide() end) + end + --If we have a saved order reapply + if get("columnorder") then + --print("saved order applied") + frame.resultlist.sheet:SetOrder(get("columnorder") ) + end + --Apply last column sort used + if get("columnsortcurSort") then + frame.resultlist.sheet.curSort = get("columnsortcurSort") or 1 + frame.resultlist.sheet.curDir = get("columnsortcurDir") or 1 + frame.resultlist.sheet:PerformSort() + end + --After we have finished creating the scrollsheet and all saved settings have been applyed set our event processor + function frame.resultlist.sheet.Processor(callback, self, button, column, row, order, curDir, ...) + if (callback == "ColumnOrder") then + set("columnorder", order) + elseif (callback == "ColumnWidthSet") then + private.onResize(self, column, button:GetWidth() ) + elseif (callback == "ColumnWidthReset") then + private.onResize(self, column, nil) + elseif (callback == "OnEnterCell") then + private.scrollSheetOnEnter(button, row, column) + elseif (callback == "OnLeaveCell") then + GameTooltip:Hide() + elseif (callback == "OnClickCell") then + private.scrollSheetOnClick(button, row, column) + elseif (callback == "ColumnSort") then + set("columnsortcurDir", curDir) + set("columnsortcurSort", column) + end + end + + --All the UI settings are stored here. We then split it to get the appropriate search settings + function private.getCheckboxSettings() + return {["selectbox"] = frame.SelectBoxSetting , ["exact"] = frame.exactCheck:GetChecked(), ["neutral"] = frame.neutralCheck:GetChecked(), + ["bid"] = frame.bidCheck:GetChecked(), ["failedbid"] = frame.bidFailedCheck:GetChecked(), ["auction"] = frame.auctionCheck:GetChecked(), + ["failedauction"] = frame.auctionFailedCheck:GetChecked() + } + end + --set parent of the Reason edit box to the created scrollframe. This prevents overlaping. + frame.reasonEditBox:SetParent(frame.resultlist.sheet.content) + private.CreateMailFrames() + +end + +function private.CreateMailFrames() + local frame = CreateFrame("Frame", "BeanCounterMail", MailFrame) + frame:Hide() + private.MailGUI = frame + local count, total = 0,0 + + frame:SetPoint("TOPLEFT", MailFrame, "TOPLEFT", 19,-71) + frame:SetPoint("BOTTOMRIGHT", MailFrame, "BOTTOMRIGHT", -39,115) + --Add Title to the Top + local title = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") + title:SetPoint("CENTER", frame, "CENTER", 0,60) + title:SetText(_BC('UiMailFrameRecording')) --BeanCounter is recording your mail + + local body = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + body:SetPoint("CENTER", frame, "CENTER", 0, 30) + body:SetText(_BC('UiMailFrameWait1')) --Please do not close the mail frame or + local body1 = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + body1:SetPoint("CENTER", frame, "CENTER", 0,0) + body1:SetText(_BC('UiMailFrameWait2')) --"Auction Items will not be recorded" + + local countdown = frame:CreateFontString("BeanCounterMailCount", "OVERLAY", "GameFontNormalLarge") + private.CountGUI = countdown + countdown:SetPoint("CENTER", frame, "CENTER", 0, -60) + countdown:SetText("Recording: "..count.." of "..total.." items") +end + + +function private.createDeleteItemPrompt() + --Create the base frame for external GUI + local frame = CreateFrame("Frame", nil, UIParent) + frame:SetFrameStrata("DIALOG") + frame:SetBackdrop({ + bgFile = "Interface/Tooltips/ChatBubble-Background", + edgeFile = "Interface/Tooltips/ChatBubble-BackDrop", + tile = true, tileSize = 32, edgeSize = 32, + insets = { left = 32, right = 32, top = 32, bottom = 32 } + }) + frame:SetBackdropColor(0,0,0, 1) + frame:Hide() + + frame:SetPoint("CENTER", UIParent, "CENTER") + frame:SetWidth(500) + frame:SetHeight(200) + + frame.title = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") + frame.title:SetPoint("CENTER", frame, "CENTER", 0,30) + frame.title:SetText(_BC('Do you want to delete this item from the database?')) + + frame.item = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") + frame.item:SetPoint("CENTER", frame, "CENTER", 0,0) + + frame.yes = CreateFrame("Button", nil, frame, "OptionsButtonTemplate") + frame.yes:SetPoint("CENTER", frame, "CENTER", -120, -60) + frame.yes:SetText(_BC('Yes')) + frame.yes:SetScript("OnClick", function() private.deleteExactItem( frame.item:GetText() ) + frame:Hide() + frame.item:SetText("") + end) + + frame.no = CreateFrame("Button", nil, frame, "OptionsButtonTemplate") + frame.no:SetPoint("LEFT", frame.yes, "RIGHT", 50, 0) + frame.no:SetText(_BC('No')) + frame.no:SetScale(1.8) + frame.no:SetScript("OnClick", function() frame:Hide() frame.item:SetText("") end) + + private.deletePromptFrame = frame +end + +--ONLOAD Error frame, used to show missmatched DB versus client errors that stop BC load NEEDS LOCALIZATION +function private.CreateErrorFrames(error, expectedVersion, playerVersion) + frame = private.scriptframe + frame.loadError = CreateFrame("Frame", nil, UIParent) + frame.loadError:SetFrameStrata("HIGH") + frame.loadError:SetBackdrop({ + bgFile = "Interface/Tooltips/ChatBubble-Background", + edgeFile = "Interface/Tooltips/ChatBubble-BackDrop", + tile = true, tileSize = 32, edgeSize = 32, + insets = { left = 32, right = 32, top = 32, bottom = 32 } + }) + frame.loadError:SetBackdropColor(0,0,0, 1) + frame.loadError:Show() + + frame.loadError:SetPoint("CENTER", UIParent, "CENTER") + frame.loadError:SetWidth(300) + frame.loadError:SetHeight(200) + + frame.loadError.close = CreateFrame("Button", nil, frame.loadError, "OptionsButtonTemplate") + frame.loadError.close:SetPoint("BOTTOMRIGHT", frame.loadError, "BOTTOMRIGHT", -10, 10) + frame.loadError.close:SetScript("OnClick", function() frame.loadError:Hide() end) + frame.loadError.close:SetText( _BC('Ok') ) + + frame.loadError.text = frame.loadError:CreateFontString(nil, "OVERLAY", "GameFontNormal") + frame.loadError.text:SetPoint("LEFT", frame.loadError, "LEFT", 25, 30) + frame.loadError.text:SetWidth(250) + if error == "bean older" then + print ("Your database has been created with a newer version of BeanCounter than the one you are currently using. BeanCounter will not load to prevent possibly corrupting the saved data. Please go to http://auctioneeraddon.com and update to the latest version") + frame.loadError.text:SetText("Your database has been created with a newer version of BeanCounter "..playerVersion.." than the one you are currently using. "..expectedVersion.." BeanCounter will not load to prevent possibly corrupting the saved data. Please go to http://auctioneeraddon.com and update to the latest version") + elseif error == "failed update" then + print ("Your database has failed to update. BeanCounter expects "..private.version.."BeanCounter will not load to prevent possibly corrupting the saved data. Please go to the forums at http://auctioneeraddon.com") + frame.loadError.text:SetText("Your database has failed to update. BeanCounter expects "..expectedVersion.." But the player's version is still "..playerVersion.." BeanCounter will not load to prevent possibly corrupting the saved data. Please go to the forums at http://auctioneeraddon.com") + else + frame.loadError.text:SetText("Unknow Error on loading. BeanCounter will not load to prevent possibly corrupting the saved data. Please go to the forums at http://auctioneeraddon.com") + end + frame.loadError.title = frame.loadError:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") + frame.loadError.title:SetPoint("CENTER", frame.loadError, "TOP", 0,-15) + frame.loadError.title:SetText("|CFFFF0000 BEANCOUNTER WARNING") +end + +--Taken from AucCore to make beancounter Standalone, Need to remove Redudundant stuff +function private.AddTab(tabButton, tabFrame) + -- Count the number of auction house tabs (including the tab we are going + -- to insert). + local tabCount = 1; + while (_G["AuctionFrameTab"..(tabCount)]) do + tabCount = tabCount + 1 + end + + -- Find the correct location to insert our tab + local tabIndex = 1 + while _G["AuctionFrameTab"..(tabIndex)] do + tabIndex = tabIndex + 1 + end + + -- Make room for the tab, if needed. + for index = tabCount, tabIndex + 1, -1 do + _G["AuctionFrameTab"..(index)] = _G["AuctionFrameTab"..(index - 1)] + _G["AuctionFrameTab"..(index)]:SetID(index) + end + + -- Configure the frame. + tabFrame:SetParent("AuctionFrame") + tabFrame:SetPoint("TOPLEFT", "AuctionFrame", "TOPLEFT", 0, 0) + private.relevelFrame(tabFrame); + + -- Configure the tab button. + _G["AuctionFrameTab"..tabIndex] = tabButton + tabButton:SetParent("AuctionFrame"); + tabButton:SetPoint("TOPLEFT", _G["AuctionFrameTab"..(tabIndex - 1)]:GetName(), "TOPRIGHT", -8, 0); + tabButton:SetID(tabIndex); + tabButton:Show(); + + -- Update the tab count. + PanelTemplates_SetNumTabs(AuctionFrame, tabCount) +end + +function private.relevelFrame(frame) + return private.relevelFrames(frame:GetFrameLevel() + 2, frame:GetChildren()) +end + +function private.relevelFrames(myLevel, ...) + for i = 1, select("#", ...) do + local child = select(i, ...) + child:SetFrameLevel(myLevel) + private.relevelFrame(child) + end +end +--Created SearchUI reason code data into tooltips +function private.processTooltip(tip, itemLink, quantity) + if not itemLink then return end + if get("ModTTShow") and not IsAltKeyDown() then + return + end + + if not get("util.beancounter.displayReasonCodeTooltip") then return end + + private.tooltip:SetFrame(tip) + local reason, Time, bid, player = lib.API.getBidReason(itemLink, quantity) + + --debugPrint("Add to Tooltip", itemLink, reason) + if reason then + if reason == "0" then reason = "Unknown" end + Time = SecondsToTime((time() - Time)) + local text = "" + if player then + text = ("Last won by {{%s}} for {{%s}} { {{%s}} } ago}"):format(player or "", reason, Time) + else + text = ("Last won for {{%s}} { {{%s}} } ago}"):format(reason, Time) + end + local cost = tonumber(bid) + private.tooltip:AddLine(text, cost, 0.9,0.6,0.2) + end + private.tooltip:ClearFrame(tip) +end diff --git a/BeanCounter/BeanCounterLocale.lua b/BeanCounter/BeanCounterLocale.lua new file mode 100644 index 0000000..ca199ff --- /dev/null +++ b/BeanCounter/BeanCounterLocale.lua @@ -0,0 +1,69 @@ +--[[ + BeanCounter Addon for World of Warcraft(tm). + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: BeanCounterLocale.lua 3583 2008-10-11 16:50:02Z Norganna $ + URL: http://auctioneeraddon.com/ + + BeanCounterLocale + Functions for localizing BeanCounter + Thanks to Telo for the LootLink code from which this was based. + + 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 it's designated purpose as per: + http://www.fsf.org/licensing/licenses/gpl-faq.html#InterpreterIncompat +]] +LibStub("LibRevision"):Set("$URL: http://svn.norganna.org/auctioneer/branches/5.9/BeanCounter/BeanCounterLocale.lua $","$Rev: 3583 $","5.1.DEV.", 'auctioneer', 'libs') + +local libName = "BeanCounter" +local libType = "Util" +local lib = BeanCounter +local private = lib.Private +local print = BeanCounter.Print + +local Babylonian = LibStub("Babylonian") +assert(Babylonian, "Babylonian is not installed") + +local babylonian = Babylonian(BeanCounterLocalizations) + +local BeanCounter_CustomLocalizations = { + ['MailAllianceAuctionHouse'] = GetLocale(), + ['MailAuctionCancelledSubject'] = GetLocale(), + ['MailAuctionExpiredSubject'] = GetLocale(), + ['MailAuctionSuccessfulSubject'] = GetLocale(), + ['MailAuctionWonSubject'] = GetLocale(), + ['MailHordeAuctionHouse'] = GetLocale(), + ['MailOutbidOnSubject'] = GetLocale(), +} + +function private.localizations(stringKey, locale) +--locale = "deDE" + if (locale) then + if (type(locale) == "string") then + return babylonian(locale, stringKey) or stringKey + else + return babylonian(GetLocale(), stringKey) + end + elseif (BeanCounter_CustomLocalizations[stringKey]) then + return babylonian(BeanCounter_CustomLocalizations[stringKey], stringKey) + else + return babylonian[stringKey] or stringKey + end +end diff --git a/BeanCounter/BeanCounterMail.lua b/BeanCounter/BeanCounterMail.lua new file mode 100644 index 0000000..9fda628 --- /dev/null +++ b/BeanCounter/BeanCounterMail.lua @@ -0,0 +1,655 @@ +--[[ + Auctioneer Addon for World of Warcraft(tm). + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: BeanCounterMail.lua 4933 2010-10-13 17:16:14Z Nechckn $ + URL: http://auctioneeraddon.com/ + + BeanCounterMail - Handles recording of all auction house related mail + + 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 it's designated purpose as per: + http://www.fsf.org/licensing/licenses/gpl-faq.html#InterpreterIncompat +]] +LibStub("LibRevision"):Set("$URL: http://svn.norganna.org/auctioneer/branches/5.9/BeanCounter/BeanCounterMail.lua $","$Rev: 4933 $","5.1.DEV.", 'auctioneer', 'libs') + +local lib = BeanCounter +local private, print, get, set, _BC = lib.getLocals() --_BC localization function + +local _G = _G +local pairs,ipairs,select,type = pairs,ipairs,select,type +local tonumber,tostring,format = tonumber,tostring,format +local tinsert,tremove = tinsert,tremove +local strsplit = strsplit +local time = time +local GetTime = GetTime +local floor = floor + +local function debugPrint(...) + if get("util.beancounter.debugMail") then + private.debugPrint("BeanCounterMail",...) + end +end + +local expiredLocale = AUCTION_EXPIRED_MAIL_SUBJECT:gsub("(.+)%%s", "%1") +local salePendingLocale = AUCTION_INVOICE_MAIL_SUBJECT:gsub("(.+)%%s", "%1") --sale pending +local outbidLocale = AUCTION_OUTBID_MAIL_SUBJECT:gsub("(.+)%%s", "%1") +local cancelledLocale = AUCTION_REMOVED_MAIL_SUBJECT:gsub("(.+)%%s", "%1") +local successLocale = AUCTION_SOLD_MAIL_SUBJECT:gsub("(.+)%%s", "%1") +local wonLocale = AUCTION_WON_MAIL_SUBJECT:gsub("(.+)%%s", "%1") +--[[ +Essamyns improved parser. Need to test before comiting +local expiredLocale = AUCTION_EXPIRED_MAIL_SUBJECT:gsub("%%s", "(.+)") +local salePendingLocale = AUCTION_INVOICE_MAIL_SUBJECT:gsub("%%s", "(.+)") --sale pending +local outbidLocale = AUCTION_OUTBID_MAIL_SUBJECT:gsub("%%s", "(.+)") +local cancelledLocale = AUCTION_REMOVED_MAIL_SUBJECT:gsub("%%s", "(.+)") +local successLocale = AUCTION_SOLD_MAIL_SUBJECT:gsub("%%s", "(.+)") +local wonLocale = AUCTION_WON_MAIL_SUBJECT:gsub("%%s", "(.+)") +]] +local reportTotalMail, reportReadMail = 0, 0 --Used as a debug check on mail scanning engine + +local registeredAltaholicHook = false +local registeredInboxFrameHook = false +function private.mailMonitor(event,arg1) + if (event == "MAIL_INBOX_UPDATE") then + private.updateInboxStart() + + elseif (event == "MAIL_SHOW") then + private.inboxStart = {} --clear the inbox list, if we errored out this should give us a fresh start. + if not registeredInboxFrameHook then --make sure we only ever register this hook once + registeredInboxFrameHook = true + hooksecurefunc("InboxFrame_OnClick", private.mailFrameClick) + hooksecurefunc("InboxFrame_Update", private.mailFrameUpdate) + end + --We cannot use mail show since the GetInboxNumItems() returns 0 till the first "MAIL_INBOX_UPDATE" + + elseif (event == "MAIL_CLOSED") then + private.HideMailGUI() + private.sumDatabase() --Sum total fo DB for the display on browse pane + end +end + +--[[Watch who reads the mail. and what they read. Use this to play nicely with altaholic and other addons]] +private.mailReadOveride = {} +function private.PreGetInboxTextHook(n, ...) + if n and n > 0 then + local _, _, sender, subject, money, _, daysLeft, _, wasRead, _, _, _ = GetInboxHeaderInfo(n) + if sender and subject and not wasRead then + --print("they read", n, sender, subject) + private.mailReadOveride[n] = sender..n + elseif wasRead then + --print("Already read", n, sender, subject) + end + end + return private.GetInboxText(n, ...) +end +--hook and replace GetInboxText() +private.GetInboxText = GetInboxText +GetInboxText = private.PreGetInboxTextHook + +--New function to hide/unhide mail GUI. +local HideMailGUI +function private.HideMailGUI( hide ) + if hide then + HideMailGUI = true + InboxCloseButton:Hide() + InboxFrame:Hide() + MailFrameTab2:Hide() + private.MailGUI:Show() + private.wipeSearchCache() --clear the search cache, we are updating data so it is now outdated + else + HideMailGUI = false + InboxCloseButton:Show() + InboxFrame:Show() + MailFrameTab2:Show() + private.MailGUI:Hide() + private.wipeSearchCache() --clear the search cache, we are updating data so it is now outdated + end +end +--Mailbox Snapshots +function private.updateInboxStart() + reportTotalMail = GetInboxNumItems() + for n = reportTotalMail, 1, -1 do + local _, _, sender, subject, money, _, daysLeft, _, wasRead, _, _, _ = GetInboxHeaderInfo(n) + if sender and subject and (not wasRead or private.mailReadOveride[n]) then + local auctionHouse --A, H, N flag for which AH the trxn came from + if sender ==_BC('MailAllianceAuctionHouse') then + auctionHouse = "A" + elseif sender == _BC('MailHordeAuctionHouse') then + auctionHouse = "H" + elseif sender == _BC('MailNeutralAuctionHouse') then + auctionHouse = "N" + end + if subject == "" then debugPrint("Skipping mail #", n, "The server is not sending the subject data. Mail will be left unread and we will retry") end + + if auctionHouse and subject ~= "" then -- subject ~= "" when the server fails, this will prevent us from reading the mail giving the server more time to get its shit togather + private.HideMailGUI(true) + wasRead = wasRead or 0 --its nil unless its has been read + local itemLink = GetInboxItemLink(n, 1) + local _, _, stack, _, _ = GetInboxItem(n) + local invoiceType, itemName, playerName, bid, buyout, deposit, consignment, retrieved, startTime = private.getInvoice(n,sender, subject) + tinsert(private.inboxStart, {["n"] = n, ["sender"]=sender, ["subject"]=subject,["money"]=money, ["read"]=wasRead, ["age"] = daysLeft, + ["invoiceType"] = invoiceType, ["itemName"] = itemName, ["Seller/buyer"] = playerName, ['bid'] = bid, ["buyout"] = buyout, + ["deposit"] = deposit, ["fee"] = consignment, ["retrieved"] = retrieved, ["startTime"] = startTime, ["itemLink"] = itemLink, ["stack"] = stack, ["auctionHouse"] = auctionHouse, + }) + private.GetInboxText(n) --read message + reportReadMail = reportReadMail + 1 + end + end + private.lastCheckedMail = GetTime() --this keeps us from hiding the mail UI to early and causing flicker + end + private.mailReadOveride = {} + private.wipeSearchCache() --clear the search cache, we are updating data so it is now outdated +end + +function private.getInvoice(n, sender, subject) + if sender:match(_BC('MailAllianceAuctionHouse')) or sender:match(_BC('MailHordeAuctionHouse')) or sender:match(_BC('MailNeutralAuctionHouse')) then + if subject:match(successLocale) or subject:match(wonLocale) then + local invoiceType, itemName, playerName, bid, buyout, deposit, consignment = GetInboxInvoiceInfo(n) + if invoiceType and playerName and playerName ~= "" and bid and bid > 0 then --Silly name throttling lead to missed invoice lookups + --debugPrint("getInvoice", invoiceType, itemName, playerName, bid, buyout, deposit, consignment, "yes") + return invoiceType, itemName, playerName, bid, buyout, deposit, consignment, "yes", time() + else + --debugPrint("getInvoice", invoiceType, itemName, playerName, bid, buyout, deposit, consignment, "no") + return invoiceType, itemName, playerName, bid, buyout, deposit, consignment, "no", time() + end + end + end + return +end + +private.lastCheckedMail = GetTime() +function private.mailonUpdate() + local total = #private.inboxStart + if total > 0 then + for i = total, 1, -1 do -- in pairs(private.inboxStart) do + --update mail GUI Count + local count = #private.inboxStart + --private.CountGUI:SetText("Recording: "..total-count.." of "..total.." items") + private.CountGUI:SetText("Recording: "..reportReadMail.." items, Please wait")--not happy, would like a better count + local data = private.inboxStart[i] + if not data.retrieved then --Send non invoiceable mails through + tinsert(private.reconcilePending, data) + --private.inboxStart[i] = nil + tremove(private.inboxStart, i) + --debugPrint("not a invoice mail type", i) + + elseif data.retrieved == "failed" then + tinsert(private.reconcilePending, data) + --private.inboxStart[i] = nil + tremove(private.inboxStart, i) + --debugPrint("data.retrieved == failed", i) + + elseif data.retrieved == "yes" then + tinsert(private.reconcilePending, data) + --private.inboxStart[i] = nil + tremove(private.inboxStart, i) + --debugPrint("data.retrieved == yes", i) + + elseif time() - data.startTime > get("util.beacounter.invoicetime") then --time exceded so fail it and process on next update + debugPrint("time to retrieve invoice exceeded, most likely waiting on players name if this is blank>..", data["Seller/buyer"], i) + data["retrieved"] = "failed" --time to get invoice exceded + else + --debugPrint("Invoice retieve attempt",data["subject"]) + data["invoiceType"], data["itemName"], data["Seller/buyer"], data['bid'], data["buyout"] , data["deposit"] , data["fee"], data["retrieved"], _ = private.getInvoice(data.n, data.sender, data.subject) + end + end + end + if (#private.inboxStart == 0) and (HideMailGUI == true) and (private.lastCheckedMail + 1 < GetTime() ) then --time delay added to prevent possible flicker + debugPrint("Total Mail in inbox:{{", reportTotalMail, "}}Reading:{{",reportReadMail, "}}new AH mails") + reportTotalMail, reportReadMail = 0, 0 + private.HideMailGUI( ) + private.mailBoxColorStart() --delay recolor system till we have had a chance to read the mail + end + + if (#private.reconcilePending > 0) then + private.mailSort() + end +end + +function private.mailSort() + for i in pairs(private.reconcilePending) do + --Get Age of message for timestamp + local messageAgeInSeconds = floor((30 - private.reconcilePending[i]["age"]) * 24 * 60 * 60) + private.reconcilePending[i]["time"] = (time() - messageAgeInSeconds) + + if private.reconcilePending[i]["sender"]:match(_BC('MailAllianceAuctionHouse')) or private.reconcilePending[i]["sender"]:match(_BC('MailHordeAuctionHouse')) or private.reconcilePending[i]["sender"]:match(_BC('MailNeutralAuctionHouse')) then + if private.reconcilePending[i].subject:match(successLocale) and (private.reconcilePending[i].retrieved == "yes" or private.reconcilePending[i].retrieved == "failed") then + private.sortCompletedAuctions( i ) + + elseif private.reconcilePending[i].subject:match(expiredLocale)then + private.sortFailedAuctions( i ) + + elseif private.reconcilePending[i].subject:match(wonLocale) and (private.reconcilePending[i].retrieved == "yes" or private.reconcilePending[i].retrieved == "failed") then + private.sortCompletedBidsBuyouts( i ) + + elseif private.reconcilePending[i].subject:match(outbidLocale) then + private.sortFailedBids( i ) + + elseif private.reconcilePending[i].subject:match(cancelledLocale) then + private.sortCancelledAuctions( i ) + + elseif private.reconcilePending[i].subject:match(salePendingLocale) then + --ignore We dont care about this message + tremove(private.reconcilePending,i) + else + debugPrint("We had an Auction mail that failed mailsort(). Subject:", private.reconcilePending[i].subject, "at index", i) + tremove(private.reconcilePending,i) + end + else --if its not AH do we care? We need to record cash arrival from other toons + debugPrint("OTHER", private.reconcilePending[i].subject) + tremove(private.reconcilePending, i) + + end + end +end +--retrieves the itemID from the DB +function private.matchDB(text) + local itemID + for itemKey, data in pairs(BeanCounterDBNames) do + local _, name = strsplit(";", data) + if text == name then + itemID = string.split(":", itemKey) + local itemLink = lib.API.createItemLinkFromArray(itemKey) + --debugPrint("|CFFFFFF00Searching",data,"for",text,"Sucess: link is",itemLink) + return itemID, itemLink + end + end + debugPrint("Searching DB for ItemID..", text, "Failed Item does not exist in the name array") + return nil +end + +function private.sortCompletedAuctions( i ) + --Get itemID from database + local itemName = private.reconcilePending[i].subject:match(successLocale.."(.*)") + local itemID, itemLink = private.matchDB(itemName) + if itemID then --Get the Bid and stack size if possible + local stack, bid = private.findStackcompletedAuctions("postedAuctions", itemID, itemLink, private.reconcilePending[i].deposit, private.reconcilePending[i]["buyout"], private.reconcilePending[i]["time"]) + if stack then + local value = private.packString(stack, private.reconcilePending[i]["money"], private.reconcilePending[i]["deposit"], private.reconcilePending[i]["fee"], private.reconcilePending[i]["buyout"], bid, private.reconcilePending[i]["Seller/buyer"], private.reconcilePending[i]["time"], "", private.reconcilePending[i]["auctionHouse"]) + if private.reconcilePending[i]["auctionHouse"] == "A" or private.reconcilePending[i]["auctionHouse"] == "H" then + private.databaseAdd("completedAuctions", itemLink, nil, value) + --debugPrint("databaseAdd completedAuctions", itemID, itemLink) + else + private.databaseAdd("completedAuctionsNeutral", itemLink, nil, value) + end + else + debugPrint("Failure for completedAuctions", itemID, itemLink, "index", private.reconcilePending[i].n) + end + end + tremove(private.reconcilePending, i) +end +--Find the stack information in postedAuctions to add into the completedAuctions DB on mail arrivial +function private.findStackcompletedAuctions(key, itemID, itemLink, soldDeposit, soldBuy, soldTime) + if not private.playerData[key][itemID] then return end --if no keys present abort + + local soldDeposit, soldBuy, soldTime ,oldestPossible = tonumber(soldDeposit), tonumber(soldBuy), tonumber(soldTime), tonumber(soldTime - 208800) --58H 15min oldest we will go back + --ItemLink will be used minus its unique ID + local itemString = lib.API.getItemString(itemLink) + itemString = itemString:match("(item:.+):.-:.-")-- ignore Unique ID + + for i,v in pairs (private.playerData[key][itemID]) do + if i:match(itemString) or i == itemString then + for index, text in pairs(v) do + if not text:match(".*USED.*") then + local postStack, postBid, postBuy, postRunTime, postDeposit, postTime, postReason = strsplit(";", private.playerData[key][itemID][i][index]) + postDeposit, postBuy, postBid, postTime = tonumber(postDeposit), tonumber(postBuy), tonumber(postBid), tonumber(postTime) + --if the deposits and buyouts match, check if time range would make this a possible match + if postDeposit == soldDeposit and postBuy >= soldBuy and postBid <= soldBuy then --We may have sold it on a bid so we need to loosen this search + if (soldTime > postTime) and (oldestPossible < postTime) then + tremove(private.playerData[key][itemID][i], index) --remove the matched item From postedAuctions DB + --private.playerData[key][itemID][i][index] = private.playerData[key][itemID][i][index]..";USED Sold" + --debugPrint("postedAuction removed as sold", itemID, itemLink) + return tonumber(postStack), tonumber(postBid) + end + end + + end + end + end + end + --return 1 if the item is nonstackable and no match was found + if private.getItemInfo(itemID, "stack") == 1 then return 1 end +end + +function private.sortFailedAuctions( i ) + local itemID = lib.API.decodeLink(private.reconcilePending[i]["itemLink"]) + if itemID then + local stack, bid, buyout, deposit = private.findStackfailedAuctions("postedAuctions", itemID, private.reconcilePending[i]["itemLink"], private.reconcilePending[i]["stack"], private.reconcilePending[i]["time"]) + if stack then + local value = private.packString(stack, "", deposit , "", buyout, bid, "", private.reconcilePending[i]["time"], "", private.reconcilePending[i]["auctionHouse"]) + if private.reconcilePending[i]["auctionHouse"] == "A" or private.reconcilePending[i]["auctionHouse"] == "H" then + private.databaseAdd("failedAuctions", private.reconcilePending[i]["itemLink"], nil, value) + --debugPrint("databaseAdd failedAuctions", itemID, private.reconcilePending[i]["itemLink"]) + else + private.databaseAdd("failedAuctionsNeutral", private.reconcilePending[i]["itemLink"], nil, value) + end + else + debugPrint("Failure for failedAuctions", itemID, private.reconcilePending[i]["itemLink"], "index", private.reconcilePending[i].n) + end + end + tremove(private.reconcilePending, i, private.reconcilePending[i]["itemLink"]) +end +--find stack, bid and buy info for failedauctions +function private.findStackfailedAuctions(key, itemID, itemLink, returnedStack, expiredTime) + if not private.playerData[key][itemID] then return end --if no keys present abort + local itemString = lib.API.getItemString(itemLink) --use the UniqueID stored to match this + for i,v in pairs (private.playerData[key][itemID]) do + if i:match(itemString) or i == itemString then --we still stack check and data range check but match should be assured by now + for index, text in pairs(v) do + if not text:match(".*USED.*") then + + local postStack, postBid, postBuy, postRunTime, postDeposit, postTime, postReason = strsplit(";", private.playerData[key][itemID][i][index]) + if returnedStack == tonumber(postStack) then --stacks same see if we can match time + local timeAuctionPosted, timeFailedAuctionStarted = tonumber(postTime), tonumber(expiredTime - (postRunTime * 60)) --Time this message should have been posted + if (timeAuctionPosted - 21600) <= timeFailedAuctionStarted and timeFailedAuctionStarted <= (timeAuctionPosted + 21600) then + tremove(private.playerData[key][itemID][i], index) --remove the matched item From postedAuctions DB + --private.playerData[key][itemID][i][index] = private.playerData[key][itemID][i][index]..";USED Failed" + --debugPrint("postedAuction removed as Failed", itemID, itemLink ) + return postStack, postBid, postBuy, postDeposit + end + end + + end + end + end + end +end + +--Cancled auctions are stored and treated as failed auctions with just cancelled added as the reason tag +function private.sortCancelledAuctions( i ) + local itemID = lib.API.decodeLink(private.reconcilePending[i]["itemLink"]) + if itemID then + local stack, bid, buyout, deposit = private.findStackCancelledAuctions("postedAuctions", itemID, private.reconcilePending[i]["itemLink"], private.reconcilePending[i]["stack"], private.reconcilePending[i]["time"]) + if stack then + local value = private.packString(stack, "", deposit , "", buyout, bid, "", private.reconcilePending[i]["time"], _BC('Cancelled'), private.reconcilePending[i]["auctionHouse"]) + if private.reconcilePending[i]["auctionHouse"] == "A" or private.reconcilePending[i]["auctionHouse"] == "H" then + private.databaseAdd("failedAuctions", private.reconcilePending[i]["itemLink"], nil, value) + --debugPrint("databaseAdd failedAuctions", itemID, private.reconcilePending[i]["itemLink"]) + else + private.databaseAdd("failedAuctionsNeutral", private.reconcilePending[i]["itemLink"], nil, value) + end + else + debugPrint("Failure for cancelledAuctions", itemID, private.reconcilePending[i]["itemLink"], "index", private.reconcilePending[i].n) + end + end + tremove(private.reconcilePending, i, private.reconcilePending[i]["itemLink"]) +end +--find stack, bid and buy info for Cancelledauctions +function private.findStackCancelledAuctions(key, itemID, itemLink, returnedStack, expiredTime) + if not private.playerData[key][itemID] then return end --if no keys present abort + local itemString = lib.API.getItemString(itemLink) --use the UniqueID stored to match this + for i,v in pairs (private.playerData[key][itemID]) do + if i:match(itemString) or i == itemString then --we still stack check and data range check but match should be assured by now + for index, text in pairs(v) do + if not text:match(".*USED.*") then + local postStack, postBid, postBuy, postRunTime, postDeposit, postTime, postReason = strsplit(";", private.playerData[key][itemID][i][index]) + if returnedStack == tonumber(postStack) then --stacks same see if we can match time + local timeAuctionPosted, timeCancelledAuctionStarted = tonumber(postTime), tonumber(expiredTime - (postRunTime * 60)) --Earrliest time we could have posted the auction + if (timeAuctionPosted - 21600) > (timeCancelledAuctionStarted) then --cancelled auctions could have just been posted so no way to age check beyond oldest possible + tremove(private.playerData[key][itemID][i], index) --remove the matched item From postedAuctions DB + --private.playerData[key][itemID][i][index] = private.playerData[key][itemID][i][index]..";USED Cancelled" + --debugPrint("postedAuction removed as Cancelled", itemID, itemLink ) + return postStack, postBid, postBuy, postDeposit + end + end + + end + end + end + end +end + --No need to reconcile, all needed data has been provided in the invoice We do need to clear entries so outbid has less to wade through +function private.sortCompletedBidsBuyouts( i ) + local itemID = lib.API.decodeLink(private.reconcilePending[i]["itemLink"]) + local reason = private.findCompletedBids(itemID, private.reconcilePending[i]["Seller/buyer"], private.reconcilePending[i]["bid"], private.reconcilePending[i]["itemLink"]) + if itemID then + --For a Won Auction money, deposit, fee are always 0 so we can use them as placeholders for BeanCounter Data + local value = private.packString(private.reconcilePending[i]["stack"], private.reconcilePending[i]["money"], deposite, private.reconcilePending[i]["fee"], private.reconcilePending[i]["buyout"], private.reconcilePending[i]["bid"], private.reconcilePending[i]["Seller/buyer"], private.reconcilePending[i]["time"], reason, private.reconcilePending[i]["auctionHouse"]) + if private.reconcilePending[i]["auctionHouse"] == "A" or private.reconcilePending[i]["auctionHouse"] == "H" then + private.databaseAdd("completedBidsBuyouts", private.reconcilePending[i]["itemLink"], nil, value) + else + private.databaseAdd("completedBidsBuyoutsNeutral", private.reconcilePending[i]["itemLink"], nil, value) + end + --debugPrint("databaseAdd completedBidsBuyouts", itemID, private.reconcilePending[i]["itemLink"]) + else + debugPrint("Failure for completedBidsBuyouts", itemID, private.reconcilePending[i]["itemLink"], value, "index", private.reconcilePending[i].n) + end + + tremove(private.reconcilePending,i) +end +--Used only to clear postedBid entries so failed bids is less likely to miss +function private.findCompletedBids(itemID, seller, bid, itemLink) + local bid = tonumber(bid) + local itemString = lib.API.getItemString(itemLink) --use the UniqueID stored to match this + --debugPrint("Starting search to remove posted Bid") + if private.playerData["postedBids"][itemID] and private.playerData["postedBids"][itemID][itemString] then + for index, text in pairs(private.playerData["postedBids"][itemID][itemString]) do + if not text:match(".*USED.*") then + local postStack, postBid, postSeller, isBuyout, postTimeLeft, postTime, reason = private.unpackString(text) + postStack, postBid = tonumber(postStack), tonumber(postBid) + --if seller == postSeller and postBid == bid then --Seller is mostly useless thanks to blizzards item name cahce chamges. Can often be nil esp after a getall + if postBid == bid then + tremove(private.playerData["postedBids"][itemID][itemString], index) --remove the matched item From postedBids DB + --private.playerData["postedBids"][itemID][itemString][index] = private.playerData["postedBids"][itemID][itemString][index] ..";USED WON" + --debugPrint("posted Bid removed as Won", itemString, index, reason) + return reason --return the reason code provided for why we bid/bought item + end + end + end + end +end +function private.sortFailedBids( i ) + local itemName = private.reconcilePending[i].subject:match(outbidLocale.."(.*)") + local itemID, itemLink = private.matchDB(itemName) + local postStack, postSeller, reason = private.findFailedBids(itemID, itemLink, private.reconcilePending[i]["money"]) + if itemID then + local value = private.packString(postStack, "", "", "", "", private.reconcilePending[i]["money"], postSeller, private.reconcilePending[i]["time"], reason, private.reconcilePending[i]["auctionHouse"]) + if private.reconcilePending[i]["auctionHouse"] == "A" or private.reconcilePending[i]["auctionHouse"] == "H" then + private.databaseAdd("failedBids", itemLink, nil, value) + else + private.databaseAdd("failedBidsNeutral", itemLink, nil, value) + end + --debugPrint("databaseAdd failedBids", itemID, itemLink, value) + else + debugPrint("Failure for failedBids", itemID, itemLink, "index", private.reconcilePending[i].n) + end + tremove(private.reconcilePending,i) +end +function private.findFailedBids(itemID, itemLink, gold) + gold = tonumber(gold) + if not itemLink then debugPrint("Failed auction ItemStrig nil", itemID, itemLink) return end + local itemString = lib.API.getItemString(itemLink) --use the UniqueID stored to match this + if private.playerData["postedBids"][itemID] and private.playerData["postedBids"][itemID][itemString] then + for index, text in pairs(private.playerData["postedBids"][itemID][itemString]) do + if not text:match(".*USED.*") then + local postStack, postBid, postSeller, isBuyout, postTimeLeft, postTime, reason = private.unpackString(text) + if tonumber(postBid) == gold then + tremove(private.playerData["postedBids"][itemID][itemString], index) --remove the matched item From postedBids DB + --private.playerData["postedBids"][itemID][itemString][index] = private.playerData["postedBids"][itemID][itemString][index] ..";USED FAILED" + --debugPrint("posted Bid removed as Failed", itemString, index) + return postStack, postSeller, reason + end + end + end + end +end +--Hook, take money event, if this still has an unretrieved invoice we delay X sec or invoice retrieved +local inboxHookMessage = false --Stops spam of the message. +function private.PreTakeInboxMoneyHook(funcArgs, retVal, index, ignore) + if #private.inboxStart > 0 or HideMailGUI then + if not inboxHookMessage then + print("Please allow BeanCounter time to reconcile the mail box") + inboxHookMessage = true + end + return "abort" + end +end + +--Hook, take item event, if this still has an unretrieved invoice we delay X sec or invoice retrieved +function private.PreTakeInboxItemHook( ignore, retVal, index) + if #private.inboxStart > 0 or HideMailGUI then + if not inboxHookMessage then + print("Please allow BeanCounter time to reconcile the mail box") + inboxHookMessage = true + end + return "abort" + end +end + +--[[ +The below code manages the mailboxes Icon color /read/unread status + +]]-- +function private.mailFrameClick(self, index) + if private.playerSettings["mailbox"][index] then + private.playerSettings["mailbox"][index]["read"] = 2 + end +end + +local NORMAL_FONT_COLOR, HIGHLIGHT_FONT_COLOR = NORMAL_FONT_COLOR, HIGHLIGHT_FONT_COLOR + +function private.mailFrameUpdate() + --Change Icon back color if only addon read + local db = private.playerSettings + if not db["mailbox"] then return end --we havn't read mail yet + if get("util.beancounter.mailrecolor") == "off" then return end + + local numItems = GetInboxNumItems() + local index + if (InboxFrame.pageNum * 7) < numItems then + index = 7 + else + index = 7 - ((InboxFrame.pageNum * 7) - numItems) + end + for i = 1, index do + local basename=format("MailItem%d",i) + local button = _G[basename.."Button"] + local buttonIcon = _G[basename.."ButtonIcon"] + local senderText = _G[basename.."Sender"] + local subjectText = _G[basename.."Subject"] + button:Show() + + local itemindex = ((InboxFrame.pageNum * 7) - 7 + i) --this gives us the actual itemindex as oposed to teh 1-7 button index + local _, _, sender, subject, money, _, daysLeft, _, wasRead, _, _, _ = GetInboxHeaderInfo(itemindex) + if db["mailbox"][itemindex] then + local sender = db["mailbox"][itemindex]["sender"] + if sender and (sender:match(_BC('MailHordeAuctionHouse')) or sender:match(_BC('MailAllianceAuctionHouse')) or sender:match(_BC('MailNeutralAuctionHouse'))) then + if (db["mailbox"][itemindex]["read"] < 2) then + if get("util.beancounter.mailrecolor") == "icon" or get("util.beancounter.mailrecolor") == "both" then + _G[basename.."ButtonSlot"]:SetVertexColor(1.0, 0.82, 0) + SetDesaturation(buttonIcon, nil) + end + if get("util.beancounter.mailrecolor") == "text" or get("util.beancounter.mailrecolor") == "both" then + senderText:SetTextColor(NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b) + subjectText:SetTextColor(HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b) + end + end + end + end + end + +end +--CHANGE THIS TO REVERSE ORDER +local mailCurrent +local group = {["n"] = "", ["start"] = 1, ["end"] = 1} --stores the start and end locations for a group of same name items +function private.mailBoxColorStart() + mailCurrent = {} --clean table every update + local db=BeanCounterDBSettings[private.realmName][private.playerName] + + for n = 1,GetInboxNumItems() do + local _, _, sender, subject, money, _, daysLeft, _, wasRead, _, _, _ = GetInboxHeaderInfo(n); + mailCurrent[n] = {["time"] = daysLeft ,["sender"] = sender, ["subject"] = subject, ["read"] = wasRead or 0 } + end + + --Fix reported errors of mail DB not existing for some reason. + if not db["mailbox"] then db["mailbox"] = {} end + --Create Characters Mailbox, or resync if we get more that 5 mails out of tune + if #db["mailbox"] > (#mailCurrent+2) or #db["mailbox"] == 0 then + --debugPrint("Mail tables too far out of sync, resyncing #mailCurrent", #mailCurrent,"#mailData" ,#BeanCounterDB[private.realmName][private.playerName]["mailbox"]) + db["mailbox"] = {} + for i, v in pairs(mailCurrent) do + db["mailbox"][i] = v + end + end + + if #db["mailbox"] >= #mailCurrent then --mail removed or same + for i in ipairs(mailCurrent) do + if db["mailbox"][i]["subject"] == group["n"] then + if group["start"] then group["end"] = i else group["start"] = i end + else + group["n"], group["start"], group["end"] = db["mailbox"][i]["subject"], i, i + end + + if mailCurrent[i]["subject"] ~= db["mailbox"][i]["subject"] then + --debugPrint("group = ",group["n"], group["start"], group["end"]) + if db["mailbox"][i]["read"] == 2 then + --debugPrint("This is marked read so removing ", i) + tremove(db["mailbox"], i) + break + elseif db["mailbox"][i]["read"] < 2 then + --This message has not been read, so we have a sequence of messages with the same name. Need to go back recursivly till we find the "Real read" message that need removal + for V = group["end"], group["start"], -1 do + if db["mailbox"][V]["read"] == 2 then + --debugPrint("recursive read group",group["end"] ,"--",group["start"], "found read at",V ) + tremove(db["mailbox"], V) + break + end + end + end + break + end + end + elseif #db["mailbox"] < #mailCurrent then --mail added + for i,v in ipairs(mailCurrent) do + if db["mailbox"][i] then + if mailCurrent[i]["subject"] ~= db["mailbox"][i]["subject"] then + --debugPrint("#private.mailData < #mailCurrent adding", i, mailCurrent[i]["subject"]) + tinsert(db["mailbox"], i, v) + end + else + --debugPrint("need to add key ", i) + tinsert(db["mailbox"], i, v) + end + end + + end + private.mailFrameUpdate() + private.hasUnreadMail() +end + +function private.hasUnreadMail() + --[[if HasNewMail() then MiniMapMailFrame:Show() debugPrint("We have real unread mail, mail icon show/hide code bypassed") return end --no need to process if we have real unread messages waiting + if not get("util.beancounter.mailrecolor") then MiniMapMailFrame:Hide() return end --no need to do this if user isn't using recolor system, and mail icon should not show since HasnewMail() failed + + local mailunread = false + for i,v in pairs(BeanCounterDB[private.realmName][private.playerName]["mailbox"]) do + if BeanCounterDB[private.realmName][private.playerName]["mailbox"][i]["read"] < 2 then + mailunread = true + end + end + if mailunread then + lib.SetSetting("util.beancounter.hasUnreadMail", true) + MiniMapMailFrame:Show() + else + lib.SetSetting("util.beancounter.hasUnreadMail", false) + MiniMapMailFrame:Hide() + end]] +end diff --git a/BeanCounter/BeanCounterSearch.lua b/BeanCounter/BeanCounterSearch.lua new file mode 100644 index 0000000..732be5e --- /dev/null +++ b/BeanCounter/BeanCounterSearch.lua @@ -0,0 +1,468 @@ +--[[ + Auctioneer Addon for World of Warcraft(tm). + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: BeanCounterSearch.lua 4933 2010-10-13 17:16:14Z Nechckn $ + + BeanCounterSearch - Search routines for BeanCounter data + URL: http://auctioneeraddon.com/ + + 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 it's designated purpose as per: + http://www.fsf.org/licensing/licenses/gpl-faq.html#InterpreterIncompat +]] +LibStub("LibRevision"):Set("$URL: http://svn.norganna.org/auctioneer/branches/5.9/BeanCounter/BeanCounterSearch.lua $","$Rev: 4933 $","5.1.DEV.", 'auctioneer', 'libs') + +local lib = BeanCounter +local private, print, get, set, _BC = lib.getLocals() + +local ipairs,pairs,select,type,next = ipairs,pairs,select,type,next +local tinsert = tinsert +local tonumber,tostring = tonumber,tostring +local abs = abs +local strsplit = strsplit + +local function debugPrint(...) + if get("util.beancounter.debugSearch") then + private.debugPrint("BeanCounterSearch",...) + end +end + + +local data = {} +local style = {} +local temp ={} +local tbl = {} + + +--This is all handled by ITEMIDS need to remove/rename this to be a utility to convert text searches to itemID searches +function private.startSearch(itemName, settings, queryReturn, count, itemTexture) --queryReturn is passed by the externalsearch routine, when an addon wants to see what data BeanCounter knows + --Run the compression function once per session, use first search as trigger + --Check the postedDB tables and remove any entries that are older than 31 Days + if not private.compressed then private.refreshItemIDArray() private.sortArrayByDate() private.compactDB() private.prunePostedDB() private.sumDatabase() private.compressed = true end + + if not itemName then return end + if not settings then settings = private.getCheckboxSettings() end + + tbl = {} + for itemKey, data in pairs(BeanCounterDBNames) do + if data:lower():find(itemName:lower(), 1, true) then + if settings.exact and private.frame.searchBox:GetText() ~= "" then --if the search field is blank do not exact check + local _, name = strsplit(";", data) + if itemName:lower() == name:lower() then + local itemID, suffix = strsplit(":", itemKey)--Create a list of itemIDs that match the search text + settings.suffix = suffix -- Store Suffix used to later filter unwated results from the itemID search + tbl[itemID] = itemID --Since its possible to have the same itemID returned multiple times this will only allow one instance to be recorded + break + end + else + local itemID = strsplit(":", itemKey)--Create a list of itemIDs that match the search text + tbl[itemID] = itemID --Since its possible to have the same itemID returned multiple times this will only allow one instance to be recorded + end + end + end + + if queryReturn then --need to return the ItemID results to calling function + return private.searchByItemID(tbl, settings, queryReturn, count, itemTexture, itemName) + else + --get the itemTexture for display in the drop box + for i, data in pairs(BeanCounterDBNames) do + local _, name = strsplit(";", data) + if name:lower() == itemName:lower() then + local itemID = strsplit(":", i) or "" + _, itemTexture = private.getItemInfo(itemID, "name") + break + end + end + private.searchByItemID(tbl, settings, queryReturn, count, itemTexture, itemName) + end +end + + +function private.searchByItemID(id, settings, queryReturn, count, itemTexture, classic) + if not id then return end + if not settings then settings = private.getCheckboxSettings() end + if not count then count = get("numberofdisplayedsearchs") end --count determines how many results we show or display High # ~to display all + + tbl = {} + if type(id) == "table" then --we can search for a sinlge itemID or an array of itemIDs + for i,v in pairs(id)do + tinsert(tbl, tostring(v)) + end + else + tbl[1] = tostring(id) + end + + data = {} + style = {} + + local profit, low, high, serverName, playerName + --serverName and playerName are used as part of our cache ID string + if settings.servers and settings.servers[1] then + serverName = settings.servers[1] + else + serverName = GetRealmName() + end + if settings.selectbox and settings.selectbox[2] then + playerName = settings.selectbox[2] + else + playerName = "server" + end + + --check if we have a cache of this search + local cached = private.checkSearchCache(classic or tbl[1], serverName, playerName) + if cached then + data = cached + else + data = private.searchServerData(serverName, data, tbl, settings) + --format raw into displayed data, the cached version is already in this format + data = private.formatServerData(data, settings) + end + + --add item to cache + if not cached then + private.addSearchCache(classic or tbl[1], data, serverName, playerName) + end + + --If query return + if queryReturn then --this lets us know it was not an external addon asking for beancounter data + return data --All results are now returned, calling addons can filter + end + + --if BeanCounters frame is not visible then store till we are and cease processing + if not private.frame:IsVisible() then + private.storedQuery = id + return + end + + --store profit for this item, need to do this before we reduce number of results for display + local player = private.frame.SelectBoxSetting[2] + profit, low, high = lib.API.getAHProfit(player, data) + + --reduce results to the latest XXXX ammount based on how many user wants displayed + if #data > count then + data = private.reduceSize(data, count) + end + + style = private.styleServerData(data) --create a style sheet for this data + + --Adds itemtexture to display box and if possible the gain/loss on the item + if itemTexture then + private.frame.icon:SetNormalTexture(itemTexture) + else + private.frame.icon:SetNormalTexture(nil) + end + + --display profit for the search term + if profit then + local change = "|CFF33FF33Gained" + if profit < 0 then change = "|CFFFF3333Lost" profit = abs(profit) end-- if profit negative ABS to keep tiplib from missrepresenting # + profit = private.tooltip:Coins(profit) + private.frame.slot.help:SetTextColor(.8, .5, 1) + private.frame.slot.help:SetText(change..(" %s from %s to %s"):format(profit or "", date("%x", low) or "", date("%x", high) or "")) + else + private.frame.slot.help:SetTextColor(1, 0.8, 0) + private.frame.slot.help:SetText(_BC('HelpGuiItemBox')) --"Drop item into box to search." + end + + private.frame.resultlist.sheet:SetData(data, style) --Set the GUI scrollsheet + return data, style +end + +--Helper functions for the Search +function private.searchServerData(serverName, data, tbl, settings) + local server = BeanCounterDB[serverName] + if not server then return end + + --Retrives all matching results + for i in pairs(server) do + if settings.selectbox[2] == "alliance" and server[i]["faction"] and server[i]["faction"]:lower() ~= settings.selectbox[2] then + --If looking for alliance and player is not alliance fall into this null + elseif settings.selectbox[2] == "horde" and server[i]["faction"] and server[i]["faction"]:lower() ~= settings.selectbox[2] then + --If looking for horde and player is not horde fall into this null + elseif (settings.selectbox[2] ~= "server" and settings.selectbox[2] ~= "alliance" and settings.selectbox[2] ~= "horde" and settings.selectbox[2] ~= "neutral") and i ~= settings.selectbox[2] then + --If we are not doing a whole server search and the chosen search player is not "i" then we fall into this null + --otherwise we search the server or toon as normal + else + --flag on how we handle neutral AH nil = no filter 1 = remove neutral AH 2 = remove NON neutral + local filterNeutral = 1 --by default HIDE neutral trxns + if settings.neutral then filterNeutral = nil end --GUI check to display neutral trxn over ridden by select box + if settings.selectbox[2] == "neutral" then filterNeutral = 2 end + for _, id in pairs(tbl) do + if settings.auction and server[i]["completedAuctions"][id] and filterNeutral ~= 2 then + data = private.searchDB(data, server, i, "completedAuctions", id) + end + if settings.failedauction and server[i]["failedAuctions"][id] and filterNeutral ~= 2 then + data = private.searchDB(data, server, i, "failedAuctions", id) + end + if settings.bid and server[i]["completedBidsBuyouts"][id] and filterNeutral ~= 2 then + data = private.searchDB(data, server, i, "completedBidsBuyouts", id) + end + if settings.failedbid and server[i]["failedBids"][id] and filterNeutral ~= 2 then + data = private.searchDB(data, server, i, "failedBids", id) + end + --neutral AH handling + if settings.auction and server[i]["completedAuctionsNeutral"][id] and filterNeutral ~= 1 then + data = private.searchDB(data, server, i, "completedAuctionsNeutral", id) + end + if settings.failedauction and server[i]["failedAuctionsNeutral"][id] and filterNeutral ~= 1 then + data = private.searchDB(data, server, i, "failedAuctionsNeutral", id) + end + if settings.bid and server[i]["completedBidsBuyoutsNeutral"][id] and filterNeutral ~= 1 then + data = private.searchDB(data, server, i, "completedBidsBuyoutsNeutral", id) + end + if settings.failedbid and server[i]["failedBidsNeutral"][id] and filterNeutral ~= 1 then + data = private.searchDB(data, server, i, "failedBidsNeutral", id) + end + end + end + end + return data +end +function private.searchDB(data, server, player, DB, itemID) + for index, itemKey in pairs(server[player][DB][itemID]) do + DB = DB:gsub("Neutral", "")--remove the Neutral part so we send it to the proper function + for _, text in ipairs(itemKey) do + tinsert(data, {DB:upper(), itemID, index, text}) + end + end + return data +end + +function private.formatServerData(data, settings) + local formatedData = {} + --Format Data for display via scroll private.frame + for i,v in pairs(data) do + local match = true + --to provide exact match filtering for of the tems we compare names to the itemKey on API searches + if settings.exact and settings.suffix then + local _, suffix = lib.API.decodeLink(v[3]) + if suffix == settings.suffix then + -- do nothing and add item to data table + else + match = false --we want exact matches and this is not one + end + end + if match and v[1] then + local database = v[1] + --just a wrapper to call the correct function for the database we are wanting to format. Example function private.FAILEDBIDS(...) == private["FAILEDBIDS"](...) + local store = private[database] + local entry = store(v[2], v[3], v[4], settings) + tinsert(formatedData, entry) + end + end + + return formatedData +end +--take collected data and format +local function styleColors(database) --helper takes formated data table and looks to what colors we use for style + -- style colors for the various databases + if database == _BC('UiAucSuccessful') then + return 0.3, 0.9, 0.8 + elseif database == _BC('UiAucExpired') then + return 1, 0, 0 + elseif database == _BC('UiWononBuyout') or database == _BC('UiWononBid') then + return 1, 1, 0 + elseif database == _BC('UiOutbid') then + return 1, 1, 1 + else --return default + return 1, .5, .1 + end +end +function private.styleServerData(data) + --create style data for entries that are going to be displayed, created seperatly to allow us to reduce the data table entries + local dateString = get("dateString") or "%c" + for i,v in pairs(data) do + local database = v[2] + local r, g, b = styleColors(database) + style[i] = {} + if get("colorizeSearch") then style[i][1] = {["rowColor"] = {r, g, b, 0, get("colorizeSearchopacity") or 0, "Horizontal"}} end + style[i][12] = {["date"] = dateString} + style[i][2] = {["textColor"] = {r, g, b}} + style[i][8] ={["textColor"] = {r, g, b}} + end + return style +end + +function private.reduceSize(tbl, count) + --The data provided is from multiple toons tables, so we need to resort the merged data back into sequential time order + table.sort(tbl, function(a, b) + return a[12] > b[12] + end) + local data = {} -- this will be a new table, this prevents chages from being propagated back to the cached "data" refrence + for i = 1, count do + tinsert(data, tbl[i]) + end + return data +end + +--To simplify having two seperate search routines, the Data creation of each table has been made a local function + function private.COMPLETEDAUCTIONS(id, itemKey, text) + local uStack, uMoney, uDeposit , uFee, uBuyout , uBid, uSeller, uTime, uReason, uMeta = private.unpackString(text) + if uSeller == "0" then uSeller = "..." end + if uReason == "0" then uReason = "..." end + + local pricePer = 0 + local stack = tonumber(uStack) or 0 + local profit = (uMoney - uDeposit + uFee) + if stack > 0 then pricePer = profit/stack end + + local itemID, suffix, uniqueID = lib.API.decodeLink(itemKey) + local itemLink = lib.API.createItemLinkFromArray(itemID..":"..suffix, uniqueID) + + if not itemLink then itemLink = private.getItemInfo(id, "name") end--if not in our DB ask the server + + return { + itemLink or "Failed to get Link", --itemname + _BC('UiAucSuccessful'), --status + + tonumber(uBid) or 0, --bid + tonumber(uBuyout) or 0, --buyout + tonumber(uMoney), --Net + tonumber(stack), --stacksize + tonumber(pricePer), --Profit/per + + uSeller, --seller/seller + + tonumber(uDeposit), --deposit + tonumber(uFee), --fee + uReason, --reason bought + tonumber(uTime), --time, --Make this a user choosable option. + tonumber(profit), --Profit + uMeta or "", + } + end + --STACK; BUY; BID; DEPOSIT; TIME; DATE; WEALTH + function private.FAILEDAUCTIONS(id, itemKey, text) + local uStack, uMoney, uDeposit , uFee, uBuyout , uBid, uSeller, uTime, uReason, uMeta = private.unpackString(text) + if uSeller == "0" then uSeller = "..." end + if uReason == "0" then uReason = "..." end + local status =_BC('UiAucExpired') + if uReason == _BC('Cancelled') then status = _BC('UiAucCancelled') end --if its a cancel rather than true expired auction + + local itemID, suffix, uniqueID = lib.API.decodeLink(itemKey) + local itemLink = lib.API.createItemLinkFromArray(itemID..":"..suffix, uniqueID) + if not itemLink then itemLink = private.getItemInfo(id, "name") end--if not in our DB ask the server + + return { + itemLink, --itemname + status, + + tonumber(uBid) or 0, --bid + tonumber(uBuyout) or 0, --buyout + 0, --money, + tonumber(uStack) or 0, + 0, --Profit/per + + uSeller, --seller/buyer + + tonumber(uDeposit) or 0, --deposit + 0, --fee + uReason, --reason bought + tonumber(uTime), --time, --Make this a user choosable option. + 0, --Profit + uMeta or "", + } + end + function private.COMPLETEDBIDSBUYOUTS(id, itemKey, text) + --local value = "stack"], "money"], p"fee"], buyout"], "bid"], p"Seller/buyer"], ["time"], reason) + + local uStack, uMoney, uDeposit , uFee, uBuyout , uBid, uSeller, uTime, uReason, uMeta = private.unpackString(text) + if uSeller == "0" then uSeller = "..." end + if uReason == "0" then uReason = "..." end + + local pricePer, stack, text = 0, tonumber(uStack), _BC('UiWononBuyout') + local profit + --If the auction was won on bid change text, and adjust ProfitPer + if uBuyout ~= uBid then + text = _BC('UiWononBid') + profit = (uBid - uMoney + uFee) + if stack > 0 then pricePer = profit/stack end + else --Devide by BUY price if it was won on Buy + profit = (uBuyout - uMoney + uFee) + if stack > 0 then pricePer = profit/stack end + end + + --replace reason with DE info + if not uMeta then uMeta = "" end + local mat, count, value = uMeta:match("DE:(%d-):(%d-):(%d-)|") + if mat and count and value then + local _, link = GetItemInfo(mat) + if link then + uReason = link.." X "..count + --change the profit to be the diff between bought and what we DE into + profit = count*value - profit + end + end + + local itemID, suffix, uniqueID = lib.API.decodeLink(itemKey) + local itemLink = lib.API.createItemLinkFromArray(itemID..":"..suffix, uniqueID) + if not itemLink then itemLink = private.getItemInfo(id, "name") end--if not in our DB ask the server + + return { + itemLink, --itemname + text, --status + + tonumber(uBid), --bid + tonumber(uBuyout), --buyout + 0, --money, + tonumber(stack), --stacksize + tonumber(pricePer), --Profit/per + + uSeller, --seller/buyer + + tonumber(uDeposit), --deposit + tonumber(uFee), --fee + uReason, --reason bought + tonumber(uTime), --time, --Make this a user choosable option. + tonumber(profit), --Profit/per + uMeta, + } + end + function private.FAILEDBIDS(id, itemKey, text) + + local uStack, uMoney, uDeposit , uFee, uBuyout , uBid, uSeller, uTime, uReason, uMeta = private.unpackString(text) + if uSeller == "0" then uSeller = "..." end + if uReason == "0" then uReason = "..." end + + local itemID, suffix, uniqueID = lib.API.decodeLink(itemKey) + local itemLink = lib.API.createItemLinkFromArray(itemID..":"..suffix, uniqueID) + if not itemLink then itemLink = private.getItemInfo(id, "name") end--if not in our DB ask the server + + return { + itemLink, --itemname + _BC('UiOutbid'), --status + + tonumber(uBid), --bid + 0, --buyout + tonumber(uMoney), --money, + tonumber(uStack), --stack + 0, --Profit/per + + uSeller, --seller/buyer + + tonumber(uDeposit), --deposit + tonumber(uFee), --fee + uReason, --reason bought + tonumber(uTime), --time, --Make this a user choosable option. + 0, --Profit/per + uMeta or "", + } + end diff --git a/BeanCounter/BeanCounterStrings.lua b/BeanCounter/BeanCounterStrings.lua new file mode 100644 index 0000000..63d582b --- /dev/null +++ b/BeanCounter/BeanCounterStrings.lua @@ -0,0 +1,1455 @@ +--[[ + WARNING: This is a generated file. + If you wish to perform or update localizations, please go to our Localizer website at: + http://localizer.norganna.org/ + + AddOn: BeanCounter + Revision: $Id: BeanCounterStrings.lua 4840 2010-08-04 21:44:00Z Nechckn $ + Version: 5.9.4961 (WhackyWallaby) + + 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 licence 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 +]] + +BeanCounterLocalizations = { + + csCZ = { + + -- Section: Mail + ["MailAllianceAuctionHouse"] = "Aliancni aukce"; + ["MailHordeAuctionHouse"] = "Hordacka aukce"; + + -- Section: User Interface + ["UiAuctions"] = "Aukce"; + ["UiAuctionTransaction"] = "Aukce"; + ["UiBids"] = "Bidy"; + ["UiBidTransaction"] = "Bid"; + ["UiBuyerSellerHeader"] = "Zakaznik/prodavajici"; + ["UiBuyTransaction"] = "Kup"; + ["UiDateHeader"] = "Datum"; + ["UiDepositTransaction"] = "Sklad"; + ["UiExactNameSearch"] = "Presny nazev"; + ["UiNameHeader"] = "Vec"; + ["UiNetHeader"] = "Sit"; + ["UiNetPerHeader"] = "Sit per"; + ["UiPriceHeader"] = "Cena"; + ["UiPricePerHeader"] = "Cenova hlava"; + ["UiPurchases"] = "Nakupy"; + ["UiQuantityHeader"] = "Mnozstvi"; + ["UiSales"] = "Prodeje"; + ["UiSearch"] = "Hledat"; + ["UiSearchForLabel"] = "Hledej"; + ["UiSellTransaction"] = "Prodej"; + ["UiTransactions"] = "Transakce"; + ["UiTransactionsLabel"] = "Transakce"; + ["UiTransactionTypeHeader"] = "Typ"; + + }; + + daDK = { + + -- Section: Config Text + ["C_BeanCounterConfig"] = "Beancounter Configuration"; + ["C_BeanCounterDatabaseMaintenance"] = "Beancounter Database opgradering"; + ["C_BeanCounterOptions"] = "Beancounter indstillinger"; + ["C_DataMaintenance"] = "Data opgradering"; + ["C_DateString"] = "Dato format til brug:"; + ["C_DateStringExample"] = "Eksempel dato:"; + ["C_ExtenalSearch"] = "Vil du tillade andre addons til at bruge beancounter's søgning?"; + ["C_ResortDatabase"] = "resorter Database"; + ["C_ShowBeginnerTooltips"] = "Vis begynder hjælpe-tips, når musen bevæges over"; + ["C_ShowReasonPurchase"] = "Skriv grund til købet i hjælpe-tips"; + ["C_ValidateDatabase"] = "Validerer Database"; + + -- Section: Generic Strings + ["off"] = "slået fra"; + ["Ok"] = "Ok"; + ["on"] = "slået til"; + ["TooltipFailed"] = "Ikke muligt at få hjælpe-tip info"; + + -- Section: Mail + ["MailAllianceAuctionHouse"] = "Alliance Auktions Hus"; + ["MailHordeAuctionHouse"] = "Horde Auktions Hus"; + + -- Section: User Interface + ["UiAuctions"] = "Auktioner"; + ["UiAuctionTransaction"] = "Auktion"; + ["UiBids"] = "Bud"; + ["UiBidTransaction"] = "Byd"; + ["UiBuyerSellerHeader"] = "Køber/Sælger"; + ["UiBuyTransaction"] = "Køb"; + ["UiDateHeader"] = "Dato"; + ["UiDepositTransaction"] = "Indskud"; + ["UiExactNameSearch"] = "Eksakt navne-søgning"; + ["UiNameHeader"] = "Item"; + ["UiNetHeader"] = "Net"; + ["UiNetPerHeader"] = "Net Per"; + ["UiPriceHeader"] = "Pris"; + ["UiPricePerHeader"] = "Styk Pris"; + ["UiPurchases"] = "Køb"; + ["UiQuantityHeader"] = "Stk"; + ["UiSales"] = "Salg"; + ["UiSearch"] = "Søg"; + ["UiSearchForLabel"] = "Søg efter:"; + ["UiSellTransaction"] = "Sælg"; + ["UiTransactions"] = "Transaktioner"; + ["UiTransactionsLabel"] = "Transaktioner:"; + ["UiTransactionTypeHeader"] = "Type"; + + }; + + deDE = { + + -- Section: Config Text + ["C_BeanCounterConfig"] = "BeanCounter Konfiguration"; + ["C_BeanCounterDatabaseMaintenance"] = "BeanCounter Datenbankwartung"; + ["C_BeanCounterOptions"] = "BeanCounter Optionen"; + ["C_ColorizeSearch"] = "Fügt eine Wertungsfarbe jedem Resultat im Suchfenster hinzu"; + ["C_DatabaseLength"] = "Bestimmt die Dauer, wie lange BeanCounter Auktionshaustransaktionen speichert."; + ["C_DataMaintenance"] = "Datenwartung"; + ["C_DateString"] = "Zu verwendendes Datumsformat:"; + ["C_DateStringExample"] = "Beispieldatum:"; + ["C_ExtenalSearch"] = "Externen AddOns die BeanCounter Suche erlauben"; + ["C_MailInvoiceTimeout"] = "Mail-Zeitüberschreitung = %d Sekunden"; + ["C_MailRecolor"] = "Mail einfärben"; + ["C_ModTTShow"] = "Zeigt den extra Tooltip nur an, wenn ALT gedrückt wird."; + ["C_MonthsToKeep"] = "Wieviele Monate sollen die Daten aufbewahrt werden ?"; + ["C_OpacityLevel"] = "Opazitätsstufe"; + ["C_Resortascendingtime"] = "Eintragungen nach aufsteigender Zeit sortieren"; + ["C_ResortDatabase"] = "Daten sortieren"; + ["C_ScanDatabase"] = "Daten auf Fehler überprüfen: Benutze dies, wenn Fehler bei der Suche in BeanCounter\nauftreten. Erstelle vor der Anwendung eine Sicherung von BeanCounters\ngespeicherten Variablen."; + ["C_SearchConfiguration"] = "Suchkonfiguration"; + ["C_SendToSearch"] = "Zeige in BeanCounter gesuchte Items auch im Auktionshausfenster ?"; + ["C_ShowBeginnerTooltips"] = "Zeige Anfängertooltips beim Überfahren mit der Maus."; + ["C_ShowReasonPurchase"] = "Zeige Kaufgrund in den Spieletooltips."; + ["C_ValidateDatabase"] = "Daten überprüfen"; + + -- Section: Generic Strings + ["NoRe-Color"] = "Kein Einfärben"; + ["off"] = "aus"; + ["Ok"] = "Ok"; + ["on"] = "ein"; + ["Re-ColorIcons"] = "Icons einfärben"; + ["Re-ColorIconsandText"] = "Icons und Text einfärben"; + ["Re-ColorText"] = "Text einfärben"; + ["TooltipFailed"] = "Tooltipinfo nicht vorhanden"; + ["UiNeutralCheckBox"] = "Zeige neutrale AH-Daten"; + + -- Section: Help Text + ["A_BeanCountersTooltip"] = "BeanCounter speichert den Suchgrund, zu dem ein Gegenstand gekauft wurde und zeigt ihn im Tooltip an."; + ["A_DateString"] = "Dies bestimmt, wie das Datum von BeanCounter angezeigt wird. Befehle werden durch % eingeleitet und mehrfache Befehle und Text können gemischt werden. Z.B. würde %a == %X Mi == 21:34:21 anzeigen"; + ["A_DateStringCommands"] = "Commands: \n %a = gek. Wochentagsname, \n %A = voller Wochentagsname, \n %b = gek. Monatsname, \n %B = voller Monatsname,\n %c = Datum und Zeit, \n %d = Monatstag (01-31),\n %H = Stunde (24), \n %I = Stunde (12),\n %M = Minute, \n %m = Monat,\n %p = am/pm, \n %S = Sekunde,\n %U = Jahreswochennummer ,\n %w = Nummerischer Wochentag (0-6),\n %x = Datum, \n %X = Zeit,\n %Y = Jahr lang (2007), \n %y = Jahr kurz (07)"; + ["A_ExtenalSearch"] = "Andere Addons können BeanCounters Suche nutzen, damit ein Gegenstand von BeanCounter angezeigt werden kann. Dies läßt BeanCounter z.B. zeigen, welche Gegenstände in Appraiser betrachtet wurden"; + ["A_MailInvoiceTimeout"] = "Die Zeitspanne, die BeanCounter auf eine Antwort des Servers wartet, um auf eine Mailabfrage zu reagieren. Eine Abfrage ist das \"wer\", \"was\" und \"wie\" einer Auktionshaus-Mail."; + ["A_MailRecolor"] = "BeanCounter liest alle Mails vom Auktionshaus. Diese Option bestimmt, wie BeanCounter die Anzeige der Mails einfärben soll, um sie als ungelesen darzustellen."; + ["HelpGuiItemBox"] = "Ziehe einen Gegenstand in die Box für Suche"; + ["Q_BeanCountersTooltip"] = "Was ist BeanCounters Tooltip"; + ["Q_DateString"] = "Anzuwendendes Datumsformat?"; + ["Q_DateStringCommands"] = "Gültige Datumsbefehle?"; + ["Q_ExtenalSearch"] = "Erlaube externen AddOns die Benutzung von BeanCounter?"; + ["Q_MailInvoiceTimeout"] = "Was ist eine Mail-Zeitüberschreitung?"; + ["Q_MailRecolor"] = "Was ist Mail einfärben ?"; + + -- Section: HelpTooltip + ["C_MaxDisplayedResults"] = "Maximal anzuzeigende Suchtreffer (von jeder Datenbank)"; + ["TTDataExpireEnabled"] = "Datensätze älter wie der gewählte Zeitraum werden GELÖSCHT"; + ["TT_ColorizeSearch"] = "Diese Option ändert die Farben der Itemlinien im BeanCounter Suchfenster"; + ["TT_MaxDisplayedResults"] = "Dies kontrolliert die Anzahl der im Fenster angezeigten Suchtreffer."; + ["TT_neutralCheck"] = "Ergebnisanzeige für neutrales Schwarzmeer Auktionshaus"; + ["TT_OpacityLevel"] = "Dies steuert das Niveau der Opazität für die farbigen Balken im BeanCounter Suchfenster (wenn aktiviert) "; + ["TT_sendtosearch"] = "Wenn eine Suche in BeanCounter eingetragen wird übernimmt das AH Browserfenster die Abfrage ebenfalls."; + + -- Section: Interface + ["Cancelled"] = "Abgebrochen"; + ["UiAucCancelled"] = "Auktion abgebrochen"; + + -- Section: Mail + ["MailAllianceAuctionHouse"] = "Auktionshaus der Allianz"; + ["MailHordeAuctionHouse"] = "Auktionshaus der Horde"; + ["MailNeutralAuctionHouse"] = "Auktionshaus der Schwarzmeerräuber"; + + -- Section: Tooltip Messages + ["TTDateString"] = "Trage das Format ein, wie das Datum dargestellt werden soll. Standard ist %c"; + ["TTDateStringExample"] = "Zeigt ein Beispiel an, wie die Datumsausgabe aussehen wird"; + ["TTExtenalSearch"] = "Wenn eine Suche in ein anderes AddOn eingetragen wird, zeigt BeanCounter auch eine Suche nach diesem Item an."; + ["TTMailInvoiceTimeout"] = "Bestimmt wie lange BeanCounter versucht, eine Mail vom Server zu erhalten, bevor er aufgibt. Tiefer == schneller, aber grössere Wahrscheinlichkeit auf fehlende Daten; Höher == langsamer, verbessert aber die Wahrscheinlichkeit alle Daten zu erhalten, wenn der Mailserver extrem beschäftigt ist."; + ["TTMailRecolor"] = "Wähle, wie die Mailanzeige erscheint, nachdem BeanCounter den Briefkasten durchsucht hat."; + ["TTModTTShow"] = "Zeige BeanCounter erweiterten Tooltip nur, wenn ALT gedrückt wird."; + ["TTOpenconfig"] = "Öffnet das Konfigurationsfenster von BeanCounter"; + ["TTResort Database"] = "Dies durchsucht die Daten von BeanCounter und sortiert die Eintragungen nach aufsteigender Zeit. Dies hilft, die Datenbank zu beschleunigen."; + ["TTShowBeginnerTooltips"] = "Aktiviert die Anzeige von Anfängertooltips beim überfahren mit der Maus."; + ["TTShowReasonPurchase"] = "Aktiviert die Anzeige des Suchgrundes, zu dem ein Item gekauft wurde und zeigt ihn im Tooltip an."; + ["TTValidateDatabase"] = "Dies durchsucht die Daten von BeanCounter und versucht alle gefundenen Fehler zu beheben. Benutze dies, wenn Fehler bei der Suche auftreten."; + ["TT_AuctionCheck"] = "Zeige im Auktionshaus verkaufte Gegenstände"; + ["TT_AuctionFailedCheck"] = "Zeige Gegenstände, die nicht verkauft wurden."; + ["TT_BeanCounterAHTab"] = "Rechtsklicke um BeanCounter in einem eigenen Fenster anzuzeigen."; + ["TT_BeanCounterSelectBox"] = "Filtere Suchresultate per Server, Spieler oder Fraktion"; + ["TT_BidCheck"] = "Zeige Gegenstände, die im Auktionshaus gekauft wurden."; + ["TT_BidFailedCheck"] = "Zeige Gegenstände bei denen Du überboten wurdest"; + ["TT_ClassicCheck"] = "Zeige Resultate aus der klassischen BeanCounter Datenbank"; + ["TT_ExactCheck"] = "Genaue Textsuche"; + ["TT_ItemIconBox"] = "Ziehe für eine Suche einen Gegenstand hierher.\nZeigt wenn möglich das aktuelle Suchicon."; + ["TT_ScrollHeader"] = "\nRechtsklick+Ziehen zur Größenänderung\nSTRG+Rechtsklick zum Zurücksetzen"; + ["TT_SearchBox"] = "Suchanfragen hier eingeben. Freilassen zur Suche nach allem"; + + -- Section: User Interface + ["UiAddonTitle"] = "BeanCounter: Auktionsverlaufdatenbank"; + ["UiAucExpired"] = "Auktion abgelaufen"; + ["UiAucSuccessful"] = "Auktion erfolgreich"; + ["UiAuctions"] = "Auktionen"; + ["UiAuctionTransaction"] = "Auktion"; + ["UiBids"] = "Gebote"; + ["UiBidTransaction"] = "Gebot"; + ["UiBuyerSellerHeader"] = "Käufer/Verkäufer"; + ["UiBuyTransaction"] = "Kaufen"; + ["UiClassicCheckBox"] = "Zeige klassische BC-Daten"; + ["UiData"] = "Daten"; + ["UiDateHeader"] = "Datum"; + ["UiDepositTransaction"] = "Anzahlung"; + ["UiDone"] = "Fertig"; + ["UiExactNameSearch"] = "Genaue Namenssuche"; + ["UiFailedAuctions"] = "Fehlgeschlagene Auktion"; + ["UiFee"] = "Gebühr"; + ["UiMailFrameRecording"] = "BeanCounter liest die Mails ein"; + ["UiMailFrameWait1"] = "Bitte das Mailfenster nicht schließen oder"; + ["UiMailFrameWait2"] = "Auktionen der Gegenstände werden nicht aufgezeichnet"; + ["UiNameHeader"] = "Gegenstand"; + ["UiNetHeader"] = "Netto"; + ["UiNetPerHeader"] = "Netto pro"; + ["UiOutbid"] = "Überboten"; + ["UiOutbids"] = "Überbotene"; + ["UiPriceHeader"] = "Preis"; + ["UiPriceper"] = "Einzelpreis"; + ["UiPricePerHeader"] = "Stückpreis"; + ["UiPurchases"] = "Einkäufe"; + ["UiQuantityHeader"] = "Anz."; + ["UiReason"] = "Grund"; + ["UiSales"] = "Verkäufe"; + ["UiSearch"] = "Suche"; + ["UiSearchForLabel"] = "Suche nach:"; + ["UiSellTransaction"] = "Verkaufen"; + ["UiServer"] = "Server"; + ["UiTransactions"] = "Transaktionen"; + ["UiTransactionsLabel"] = "Transaktionen:"; + ["UiTransactionTypeHeader"] = "Typ"; + ["UiWealth"] = "Vermögen"; + ["UiWononBid"] = "Gebotskauf"; + ["UiWononBuyout"] = "Sofortkauf"; + + }; + + elGR = { + + -- Section: Config Text + ["C_MailRecolor"] = "Mail Re-Color Method"; + + }; + + enUS = { + + -- Section: Config Text + ["C_BeanCounterConfig"] = "BeanCounter Config"; + ["C_BeanCounterDatabaseMaintenance"] = "BeanCounter Database Maintenance"; + ["C_BeanCounterOptions"] = "BeanCounter Options"; + ["C_ColorizeSearch"] = "Add a gradient color to each result in the search window"; + ["C_DatabaseLength"] = "Determines how long BeanCounter will save Auction House Transactions."; + ["C_DataMaintenance"] = "Data Maintenance"; + ["C_DateString"] = "Date format to use:"; + ["C_DateStringExample"] = "Example Date:"; + ["C_ExtenalSearch"] = "Allow External Addons to use BeanCounter's Search"; + ["C_MailInvoiceTimeout"] = "Mail Invoice Timeout = %d seconds"; + ["C_MailRecolor"] = "Mail Re-Color Method"; + ["C_ModTTShow"] = "Display the extra tooltip only if Alt is pressed."; + ["C_MonthsToKeep"] = "How many months of data to keep?"; + ["C_OpacityLevel"] = "Opacity level"; + ["C_Resortascendingtime"] = "Resort all entries by ascending time"; + ["C_ResortDatabase"] = "Resort Database"; + ["C_ScanDatabase"] = "Scan Database for errors: Use if you have errors when searching BeanCounter.\nBackup BeanCounter's saved variables before using."; + ["C_SearchConfiguration"] = "Search Configuration"; + ["C_SendToSearch"] = "Add BeanCounter's searched item to the Main Auction House Window?"; + ["C_ShowBeginnerTooltips"] = "Show beginner tooltips on mouse over"; + ["C_ShowReasonPurchase"] = "Show reason for purchase in the tooltip"; + ["C_ValidateDatabase"] = "Validate Database"; + + -- Section: Generic Strings + ["NoRe-Color"] = "No Re-Color"; + ["off"] = "off"; + ["Ok"] = "Ok"; + ["on"] = "on"; + ["Re-ColorIcons"] = "Re-Color Icons"; + ["Re-ColorIconsandText"] = "Re-Color Icons and Text"; + ["Re-ColorText"] = "Re-Color Text"; + ["TooltipFailed"] = "Unable to get Tooltip Info"; + ["UiNeutralCheckBox"] = "Show Neutral AH data"; + + -- Section: Help Text + ["A_BeanCountersTooltip"] = "BeanCounter will store the SearchUI reason an item was purchased and display it in the tooltip."; + ["A_DateString"] = "This controls how the Date field of BeanCounter's GUI is shown. Commands are prefaced by % and multiple commands and text can be mixed. For example %a == %X would display Wed == 21:34:21"; + ["A_DateStringCommands"] = "Commands: \n %a = abr. weekday name, \n %A = weekday name, \n %b = abr. month name, \n %B = month name,\n %c = date and time, \n %d = day of the month (01-31),\n %H = hour (24), \n %I = hour (12),\n %M = minute, \n %m = month,\n %p = am/pm, \n %S = second,\n %U = week number of the year ,\n %w = numerical weekday (0-6),\n %x = date, \n %X = time,\n %Y = full year (2007), \n %y = two-digit year (07)"; + ["A_ExtenalSearch"] = "Other addons can have BeanCounter search for an item to be displayed in BeanCounter's GUI. For example this allows BeanCounter to show what items you are looking at in Appraiser"; + ["A_MailInvoiceTimeout"] = "The length of time BeanCounter will wait on the server to respond to an invoice request. An invoice is the \"who\", \"what\", and \"how\" of an Auction House mail."; + ["A_MailRecolor"] = "BeanCounter reads all mail from the Auction House. This option tells BeanCounter how the user wants to re-color the messages to make them look unread."; + ["HelpGuiItemBox"] = "Drop item into box to search."; + ["Q_BeanCountersTooltip"] = "What is BeanCounter's Tooltip"; + ["Q_DateString"] = "Date Format to use?"; + ["Q_DateStringCommands"] = "Acceptable Date Commands?"; + ["Q_ExtenalSearch"] = "Allow External Addons to use BeanCounter?"; + ["Q_MailInvoiceTimeout"] = "What is Mail Invoice Timeout?"; + ["Q_MailRecolor"] = "What is Mail Re-color Method?"; + + -- Section: HelpTooltip + ["C_MaxDisplayedResults"] = "Max displayed search results (from each database)"; + ["TTDataExpireEnabled"] = "Data older than the selected time range will be DELETED"; + ["TT_ColorizeSearch"] = "This option changes the color of the items lines in the BeanCounter search window."; + ["TT_MaxDisplayedResults"] = "This controls the total number of results displayed in the scroll frame."; + ["TT_neutralCheck"] = "Display results from the Neutral Blackwater Auction house"; + ["TT_OpacityLevel"] = "This controls the level of opacity for the colored bars in the BeanCounter search window (if enabled)"; + ["TT_sendtosearch"] = "When entering a search in BeanCounter it will also add the string to the AH browse frame."; + + -- Section: Interface + ["Cancelled"] = "Cancelled"; + ["UiAucCancelled"] = "Auc Cancelled"; + + -- Section: Mail + ["MailAllianceAuctionHouse"] = "Alliance Auction House"; + ["MailHordeAuctionHouse"] = "Horde Auction House"; + ["MailNeutralAuctionHouse"] = "Blackwater Auction House"; + + -- Section: Tooltip Messages + ["TTDateString"] = "Enter the format that you would like your date field to show. Default is %c."; + ["TTDateStringExample"] = "Displays an example of what your formatted date will look like."; + ["TTExtenalSearch"] = "When entering a search in another addon, BeanCounter will also display a search for that item."; + ["TTMailInvoiceTimeout"] = "Chooses how long BeanCounter will attempt to get a mail invoice from the server before giving up. Lower == quicker but more chance of missing data. Higher == slower, but improves chances of getting data if the Mail server is extremely busy."; + ["TTMailRecolor"] = "Choose how mail will appear after BeanCounter has scanned the Mail Box."; + ["TTModTTShow"] = "This option will display BeanCounter's extra tooltip only if Alt is pressed."; + ["TTOpenconfig"] = "Opens the BeanCounter configuration window"; + ["TTResort Database"] = "This will scan Beancounter's Data sort all entries in ascending time order. This helps speed up the database compression functions."; + ["TTShowBeginnerTooltips"] = "Display the beginner tooltips on mouse over."; + ["TTShowReasonPurchase"] = "Turns on the SearchUI reason an item was purchased for in the tooltip."; + ["TTValidateDatabase"] = "This will scan Beancounter's Data and attempt to correct any errors it may find. Use if you are getting errors on search."; + ["TT_AuctionCheck"] = "Display items sold at the Auction House."; + ["TT_AuctionFailedCheck"] = "Display items you failed to sell."; + ["TT_BeanCounterAHTab"] = "Right click to display BeanCounter in a external window."; + ["TT_BeanCounterSelectBox"] = "Filter search results by server, player or faction."; + ["TT_BidCheck"] = "Display items bought from the Auction House."; + ["TT_BidFailedCheck"] = "Display items you were outbid on"; + ["TT_ClassicCheck"] = "Display results from BeanCounter Classic Database."; + ["TT_ExactCheck"] = "Only match the exact text in the search box"; + ["TT_ItemIconBox"] = "Drop an item here to start a search for it.\nDisplays current search's icon if possible."; + ["TT_ScrollHeader"] = "\nRightClick+Drag to Move\nALT+RightClick to resize\nCTRL+RightClick to reset"; + ["TT_SearchBox"] = "Enter search queries here or leave blank to search all"; + + -- Section: User Interface + ["UiAddonTitle"] = "BeanCounter: Auction History Database"; + ["UiAucExpired"] = "Auc Expired"; + ["UiAucSuccessful"] = "Auc Successful"; + ["UiAuctions"] = "Auctions"; + ["UiAuctionTransaction"] = "Auction"; + ["UiBids"] = "Bids"; + ["UiBidTransaction"] = "Bid"; + ["UiBuyerSellerHeader"] = "Buyer/Seller"; + ["UiBuyTransaction"] = "Buy"; + ["UiClassicCheckBox"] = "Show BC Classic data."; + ["UiData"] = "Data"; + ["UiDateHeader"] = "Date"; + ["UiDepositTransaction"] = "Deposit"; + ["UiDone"] = "Done"; + ["UiExactNameSearch"] = "Exact name search"; + ["UiFailedAuctions"] = "Failed Auctions "; + ["UiFee"] = "Fee"; + ["UiMailFrameRecording"] = "BeanCounter is recording your mail"; + ["UiMailFrameWait1"] = "Please do not close the mail frame or"; + ["UiMailFrameWait2"] = "Auction Items will not be recorded"; + ["UiNameHeader"] = "Item"; + ["UiNetHeader"] = "Net"; + ["UiNetPerHeader"] = "Net Per"; + ["UiOutbid"] = "Outbid"; + ["UiOutbids"] = "Outbids"; + ["UiPriceHeader"] = "Price"; + ["UiPriceper"] = "Price/Per"; + ["UiPricePerHeader"] = "Price Header"; + ["UiPurchases"] = "Purchases"; + ["UiQuantityHeader"] = "Qty"; + ["UiReason"] = "Reason"; + ["UiSales"] = "Sales"; + ["UiSearch"] = "Search"; + ["UiSearchForLabel"] = "Search for:"; + ["UiSellTransaction"] = "Sell"; + ["UiServer"] = "Server"; + ["UiTransactions"] = "Transactions"; + ["UiTransactionsLabel"] = "Transactions:"; + ["UiTransactionTypeHeader"] = "Type"; + ["UiWealth"] = "Wealth"; + ["UiWononBid"] = "Won on Bid"; + ["UiWononBuyout"] = "Won on Buyout"; + + }; + + esES = { + + -- Section: Config Text + ["C_BeanCounterConfig"] = "Configuración"; + ["C_BeanCounterDatabaseMaintenance"] = "Mantenimiento de la base de datos del BeanCounter"; + ["C_BeanCounterOptions"] = "Opciones de BeanCounter"; + ["C_ColorizeSearch"] = "Colorear los resultados en la ventana de busqueda"; + ["C_DatabaseLength"] = "Determina cuanto tiempo BeanCounter salvará las transaciones de la casa de subastas."; + ["C_DataMaintenance"] = "Mantenimiento de los datos"; + ["C_DateString"] = "Formato de fecha a usar:"; + ["C_DateStringExample"] = "Fecha de ejemplo:"; + ["C_ExtenalSearch"] = "¿Permitir que otros Addons activen la busqueda de BeanCounter?"; + ["C_MailInvoiceTimeout"] = "Tiempo de espera del correo = %d segundos"; + ["C_MailRecolor"] = "Metodo de recolorear los correos"; + ["C_ModTTShow"] = "Mostrar el tooltip extra solo si se presiona Alt."; + ["C_MonthsToKeep"] = "¿Cuantos meses se guardarän los datos?"; + ["C_OpacityLevel"] = "Nivel de opacidad"; + ["C_Resortascendingtime"] = "Reordenar todas las entradas de modo ascendente por la fecha "; + ["C_ResortDatabase"] = "Reordenar datos"; + ["C_ScanDatabase"] = "Escanear la base de datos: Usar si tienes errores al buscar.\nSalva los datos anteriores de BeanCounter antes de usarlo."; + ["C_SearchConfiguration"] = "Configuración de busqueda"; + ["C_SendToSearch"] = "¿Añadir el objeto buscado en BeanCounter a la ventana principal de la casa de subastas?"; + ["C_ShowBeginnerTooltips"] = "Mostrar ayuda al pasar el ratón sobre los campos."; + ["C_ShowReasonPurchase"] = "Mostrar precio en el tooltip."; + ["C_ValidateDatabase"] = "Validar base de datos"; + + -- Section: Generic Strings + ["NoRe-Color"] = "No recolorear"; + ["off"] = "apagado"; + ["Ok"] = "Ok"; + ["on"] = "Encendido"; + ["Re-ColorIcons"] = "Recolorear iconos"; + ["Re-ColorIconsandText"] = "Recolorear iconos y texto"; + ["Re-ColorText"] = "Recolorear texto"; + ["TooltipFailed"] = "No se encuentra informacion"; + ["UiNeutralCheckBox"] = "Casa de subastas neutral"; + + -- Section: Help Text + ["A_BeanCountersTooltip"] = "BeanCounter almacenará el precio de un objeto cuando fue comprado y lo mostrará en el tooltip."; + ["A_DateString"] = "Controla como se muestra el campo de fecha en el interfaz de BeanCounter. Los comandos estan predecidos por un % y se puede mezclar comandos y texto. Por ejemplo %a == %X mostrará Mie == 21:34:21"; + ["A_DateStringCommands"] = "Comandos:\n%a = Abreviatura del día de la semana.\n%A = Día de la semana (en texto).\n%b = Abreviatura del mes.\n%B = Mes (en texto).\n%c = Fecha y hora.\n%d = Dia del mes (01-31).\n%H = Hora (24).\n%I = Hora (12).\n%M = Minuto.\n%m = Mes.\n%p = AM/PM.\n%S = Segundo.\n%U = Semana del año.\n%w = Día de la semana (0-6).\n%x = Fecha.\n%X = Hora (tiempo).\n%Y = Año 4 digitos.\n%y = Año 2 digitos."; + ["A_ExtenalSearch"] = "Otros addons pueden aprovechar la funcionalidad de BeanCounter y solicitar que un objeto se muestre en el interfaz de BeanCounter. Por ejemplo: esto permite que se muestre el objeto que estas mirando en el Evaluador de forma automatica."; + ["A_MailInvoiceTimeout"] = "El tiempo que BeanCounter esperará por una petición de una factura al servidor. Una factura es \"el Quien\", \"el Que\" y \"el Como\" de un correo de la casa de subastas."; + ["A_MailRecolor"] = "BeanCounter lee todos los correos de la casa de subastas. Esta opción indica si se recolorearán los mensajes (y como) para que parezcan como no leidos."; + ["HelpGuiItemBox"] = "Arrastra un objeto hasta la caja para buscar."; + ["Q_BeanCountersTooltip"] = "¿Qué es el tooltip de BeanCounter?"; + ["Q_DateString"] = "¿Cual es el formato de fecha a utilizar?"; + ["Q_DateStringCommands"] = "¿Comandos para la fecha aceptables?"; + ["Q_ExtenalSearch"] = "¿Permitir que addons externos utilicen BeanCounter?"; + ["Q_MailInvoiceTimeout"] = "¿Cuánto tiempo se escaneará un correo?"; + ["Q_MailRecolor"] = "¿Cual es el método de recolorear correos?"; + + -- Section: HelpTooltip + ["C_MaxDisplayedResults"] = "Número máximo de resultados mostrados (de cada base de datos)"; + ["TTDataExpireEnabled"] = "Los datos más antiguos de la fecha seleccionada serán BORRADOS"; + ["TT_ColorizeSearch"] = "Cambia el color de las lineas de los objetos en la ventana de busqueda de BeanCounter"; + ["TT_MaxDisplayedResults"] = "Controla el número total de resultados a mostrar."; + ["TT_neutralCheck"] = "Muestra los resultados de la casa de subastas neutral "; + ["TT_OpacityLevel"] = "Controla el nivel de opacidad de las barras de colores en la ventana de busqueda de BeanCounter (Si está habilitado)"; + ["TT_sendtosearch"] = "Cuando se realiza una busqueda en BeanCounter añadirá la cadena al marco de busqueda de la casa de subastas."; + + -- Section: Interface + ["Cancelled"] = "Cancelada"; + ["UiAucCancelled"] = "Subasta Cancelada"; + + -- Section: Mail + ["MailAllianceAuctionHouse"] = "Casa de subastas: Alianza"; + ["MailHordeAuctionHouse"] = "Casa de subastas: Horda"; + ["MailNeutralAuctionHouse"] = "Casa de subastas: Aguasnegras"; + + -- Section: Tooltip Messages + ["TTDateString"] = "Introduzca el formato de fecha que quiera mostrar. El valor predeterminado es %c."; + ["TTDateStringExample"] = "Muestra un ejemplo de como será el formato de fecha seleccionado."; + ["TTExtenalSearch"] = "Cuando se realiza una búsqueda con otro addon, BeanCounter hará la misma busqueda."; + ["TTMailInvoiceTimeout"] = "Seleciona cuanto tiempo esperará para conseguir la factura de un correo del servidor. Bajo = más rápido pero pueden perderse facturas. Alto = más lento pero aumentan las posibilidades de conseguir datos aunque el servidor esté muy ocupado."; + ["TTMailRecolor"] = "Seleciona como aparecerán los correos una vez escaneados por BeanCounter."; + ["TTModTTShow"] = "Muestra el tooltip de BeanCounter solo si se pulsa Alt."; + ["TTOpenconfig"] = "Abre la ventana de configuración de BeanCounter."; + ["TTResort Database"] = "Reordena los datos de BeanCounter de forma ascendente por fecha. Esto ayuda a acelerar las funciones de compresión de la base de datos."; + ["TTShowBeginnerTooltips"] = "Mostrar ayuda al pasar el ratón sobre los campos."; + ["TTShowReasonPurchase"] = "Muestra el comentario almacenado al comprar un objeto en el tooltip."; + ["TTValidateDatabase"] = "Escanea los datos de BeanCounter e intenta corregir cualquier error que encuentre. Usalo si te aparecen errores al buscar."; + ["TT_AuctionCheck"] = "Muestra los objetos vendidos en la casa de subastas."; + ["TT_AuctionFailedCheck"] = "Muestra las subastas que no se consiguieron vender."; + ["TT_BeanCounterAHTab"] = "Click derecho para mostrar BeanCounter en una ventana externa."; + ["TT_BeanCounterSelectBox"] = "Filtrar los resultados por servidor, jugador o facción."; + ["TT_BidCheck"] = "Muestra los objetos comprados en la casa de subastas."; + ["TT_BidFailedCheck"] = "Muestra los objetos en los que tu puja fue superada."; + ["TT_ClassicCheck"] = "Muestra los resultados de la base de datos de BeanCounter Classic."; + ["TT_ExactCheck"] = "Solo mostrará los objetos que coincidan exactamente con el texto introducido."; + ["TT_ItemIconBox"] = "Arrastra un objeto aquí para buscarlo.\nMuestra el icono de la busqueda actual si es posible.\n(todos los resultados de la busqueda son del mismo objeto)"; + ["TT_ScrollHeader"] = "Click derecho + arrastrar para redimensionar.\nCtrl + click derecho para resetear."; + ["TT_SearchBox"] = "Introduce la consulta a buscar aquí o dejalo en blanco para buscar todo."; + + -- Section: User Interface + ["UiAddonTitle"] = "BeanCounter: Base de datos con la historia de subastas"; + ["UiAucExpired"] = "Subasta no conseguida"; + ["UiAucSuccessful"] = "Subasta conseguida"; + ["UiAuctions"] = "Subastas"; + ["UiAuctionTransaction"] = "Subasta"; + ["UiBids"] = "Pujas"; + ["UiBidTransaction"] = "Puja"; + ["UiBuyerSellerHeader"] = "Comprador/Vendedor"; + ["UiBuyTransaction"] = "Compra"; + ["UiClassicCheckBox"] = "Mostrar datos BC Classic."; + ["UiData"] = "Datos"; + ["UiDateHeader"] = "Fecha"; + ["UiDepositTransaction"] = "Depósito"; + ["UiDone"] = "Cerrar"; + ["UiExactNameSearch"] = "Solo nombre exacto"; + ["UiFailedAuctions"] = "Subastas no conseguidas"; + ["UiFee"] = "Comisión"; + ["UiMailFrameRecording"] = "BeanCounter esta registrando tu correo"; + ["UiMailFrameWait1"] = "Por favor no cierre la ventana del correo"; + ["UiMailFrameWait2"] = "o los objetos no serán registrados"; + ["UiNameHeader"] = "Artículo"; + ["UiNetHeader"] = "Neto"; + ["UiNetPerHeader"] = "Neto c/u"; + ["UiOutbid"] = "Puja superada"; + ["UiOutbids"] = "Pujas superadas"; + ["UiPriceHeader"] = "Precio"; + ["UiPriceper"] = "Precio ud"; + ["UiPricePerHeader"] = "Precio c/u"; + ["UiPurchases"] = "Compras"; + ["UiQuantityHeader"] = "Ctd"; + ["UiReason"] = "Razón"; + ["UiSales"] = "Ventas"; + ["UiSearch"] = "Buscar"; + ["UiSearchForLabel"] = "Buscar por:"; + ["UiSellTransaction"] = "Vender"; + ["UiServer"] = "Servidor"; + ["UiTransactions"] = "Transacciones"; + ["UiTransactionsLabel"] = "Transacciones:"; + ["UiTransactionTypeHeader"] = "Tipo"; + ["UiWealth"] = "Ganancias"; + ["UiWononBid"] = "Ganado en puja"; + ["UiWononBuyout"] = "Ganado en compra directa"; + + }; + + esMX = { + + -- Section: Mail + ["MailAllianceAuctionHouse"] = "Casa de subastas: Alianza"; + ["MailHordeAuctionHouse"] = "Casa de subastas: Horda"; + ["MailNeutralAuctionHouse"] = "Casa de subastas: Aguasnegras"; + + }; + + frFR = { + + -- Section: Config Text + ["C_BeanCounterConfig"] = "Configuration de BeanCounter"; + ["C_BeanCounterDatabaseMaintenance"] = "Maintenance de la base de données BeanCounter"; + ["C_BeanCounterOptions"] = "Options de BeanCounter"; + ["C_ColorizeSearch"] = "Ajouter un dégradé de couleur pour chaque résultat dans la fenêtre de recherche"; + ["C_DatabaseLength"] = "Détermine combien de temps BeanCounter sauvegardera les transactions à l'Hôtel des Ventes"; + ["C_DataMaintenance"] = "Maintenance des données"; + ["C_DateString"] = "Format de date à utiliser:"; + ["C_DateStringExample"] = "Exemple de Date:"; + ["C_ExtenalSearch"] = "Autoriser les Addons Externes à utiliser la recherche BeanCounter?"; + ["C_MailInvoiceTimeout"] = "Temps au bout duquel la récupération des mails est abandonnée = %d secondes"; + ["C_MailRecolor"] = "Méthode de re colorisation de mail"; + ["C_ModTTShow"] = "Afficher l'infobulle supplémentaire uniquement si Alt est enfoncée"; + ["C_MonthsToKeep"] = "Combien de mois une donnée doit-elle être gardée?"; + ["C_OpacityLevel"] = "Niveau d'opacité"; + ["C_Resortascendingtime"] = "Trier les entrées par ordre chronologique ascendant"; + ["C_ResortDatabase"] = "Trier la base de données"; + ["C_ScanDatabase"] = "Scanner les erreurs de la base de données: A utiliser si vous avez des erreurs en faisant des recherches avec BeanCounter.\n Sauvegarder les \"saved variables\" de BeanCounter avant d'utiliser cette option."; + ["C_SearchConfiguration"] = "Configuration de la recherche"; + ["C_SendToSearch"] = "Ajouter les objets cherchés dans BeanCounter dans la fenêtre principale de l'Hôtel des Ventes?"; + ["C_ShowBeginnerTooltips"] = "Montre les encadrés d'aide pour débutant au passage de la souris"; + ["C_ShowReasonPurchase"] = "Afficher la raison d'achat dans l'infobulle"; + ["C_ValidateDatabase"] = "Valider la base de données"; + + -- Section: Generic Strings + ["NoRe-Color"] = "Pas de changement de couleur"; + ["off"] = "Eteint"; + ["Ok"] = "Ok"; + ["on"] = "Allumé"; + ["Re-ColorIcons"] = "Changement couleur d'icones"; + ["Re-ColorIconsandText"] = "Changement couleur d'icones et du texte"; + ["Re-ColorText"] = "Changement couleur du texte"; + ["TooltipFailed"] = "Impossible d'obtenir la bulle d'aide"; + ["UiNeutralCheckBox"] = "Montrer les données de l'HV neutre"; + + -- Section: Help Text + ["A_BeanCountersTooltip"] = "BeanCounter enregistrera la raison SearchUI pour laquelle un objet a été acheté et l'affichera dans l'encadré d'aide."; + ["A_DateString"] = "Ceci contrôle le mode d'affichage de la date pour BeanCounter GUI. Les commandes doivent être précédées de % et plusieurs commandes peuvent être associées avec du texte. Par exemple %a == %X affichera Mer == 21:34:21"; + ["A_DateStringCommands"] = "Commandes: \n %a = abr.jour de la semaine,\n %A = jour de la semaine,\n %b = abr.nom du mois,\n %B = nom du mois,\n %c = date et heure,\n %d = jour du mois (01-31),\n %H = Heure (24),\n %I = heure (12)\n %M = minute, \n %m = month,\n %p = am/pm, \n %S = secondes,\n %U = semaine de l'année (01-52) ,\n %w = chiffre du jour de la semaine (0-6),\n %x = date, \n %X = heure,\n %Y = année complète (2007), \n %y = année (deux derniers chiffres ex: 07)"; + ["A_ExtenalSearch"] = "D'autres adjonctions peuvent avoir la recherche de BeanCounter d'un article à montrer dans BeanCounter' ; GUI de s. Par exemple ceci permet à BeanCounter de montrer quels articles vous regardez dans le priseur"; + ["A_MailInvoiceTimeout"] = "La durée BeanCounter attendra sur le serveur pour répondre à une demande de facture. Une facture est le \" ; who\" ; , \" ; what\" ; , et \" ; how\" ; d'un courrier de maison des ventes aux-enchères."; + ["A_MailRecolor"] = "BeanCounter lit tout le courrier de la maison des ventes aux-enchères. Cette option indique à BeanCounter que l'utilisateur veut que la re-couleur les messages rende leur le sembler non lu."; + ["HelpGuiItemBox"] = "Déplacer l'item dans la boite de recherche"; + ["Q_BeanCountersTooltip"] = "Qu'est ce que l'encadré d'aide BeanCounter"; + ["Q_DateString"] = "Format de date à utiliser"; + ["Q_DateStringCommands"] = "Commandes acceptables de date ? "; + ["Q_ExtenalSearch"] = "Autoriser les addons externes à utiliser BeanCounter"; + ["Q_MailInvoiceTimeout"] = "Quel est temps mort de facture de courrier ? "; + ["Q_MailRecolor"] = "Quelle est méthode de Re-couleur de courrier ? "; + + -- Section: HelpTooltip + ["C_MaxDisplayedResults"] = "Affichage Max des résultats de recherche (à partir de chaque base de données)"; + ["TTDataExpireEnabled"] = "Des données plus anciennes que la période sélectionnée sera SUPPRIMÉ"; + ["TT_ColorizeSearch"] = "Cette option change la couleur de la ligne des objets dans la fenêtre de recherche de BeanCounter"; + ["TT_MaxDisplayedResults"] = "Cela contrôle le nombre total de résultats afficher"; + ["TT_neutralCheck"] = "Afficher les résultats de l'hôtel des ventes \"Blackwater\""; + ["TT_OpacityLevel"] = "Cela contrôle le niveau d'opacité des barres colorées dans la fenêtre de recherche de BeanCounter (si activées)"; + + -- Section: Interface + ["Cancelled"] = "Annulé"; + ["UiAucCancelled"] = "Vente aux enchères annulée"; + + -- Section: Mail + ["MailAllianceAuctionHouse"] = "Hôtel des ventes de l'Alliance"; + ["MailHordeAuctionHouse"] = "Hôtel des ventes de la Horde"; + ["MailNeutralAuctionHouse"] = "Maison des ventes aux-enchères de Blackwater "; + + -- Section: Tooltip Messages + ["TTDateString"] = "Entrez le format voulu d'affichage de votre date.Par défaut c'est %c"; + ["TTDateStringExample"] = "Montre un exemple de votre format de date"; + ["TTExtenalSearch"] = "Quand vous entrez une recherche dans un autre addon,BeanCounter affichera aussi une recherche pour cette objet"; + ["TTMailInvoiceTimeout"] = "Choisit pendant combien de temps BeanCounter attendra l'arrivée d'un message du serveur avant d'abandonner. Bas = plus rapide mais plus de chance de rater une donnée. Haut = plus lent mais augmente les chances d'avoir les données si le serveur de messagerie est extrêmement occupé."; + ["TTMailRecolor"] = "Choisissez comment un mail va apparaitre après que BeanCounter\naura scanné la boite au lettre"; + ["TTModTTShow"] = "Cette option affichera la bulle d'aide de BeanCounter uniquement si Alt est enfoncé"; + ["TTOpenconfig"] = "Ouvrir la fenêtre de configuration de BeanCounter"; + ["TTResort Database"] = "Ceci scanne les données de BeanCounter et les trie par date d'arrivée ascendant. Ceci réduit le temps de compression de la base de données."; + ["TTShowBeginnerTooltips"] = "Activer les bulles d'aide pour débutant qui s'affichent au survol de la souris."; + ["TTShowReasonPurchase"] = "Affiche la raison pour laquelle un objet a été acheté dans l'encadré d'aide."; + ["TTValidateDatabase"] = "Ceci scanne les données de BeanCounter et tente de corriger les erreurs éventuelles. A utiliser si vous avez des erreurs lors de vos recherches."; + ["TT_AuctionCheck"] = "Afficher les objets vendus à l'hôtel des ventes."; + ["TT_AuctionFailedCheck"] = "Afficher les objets que vous n'avez pas réussi à vendre."; + ["TT_BeanCounterAHTab"] = "Clic droit pour afficher BeanCounter dans une autre fenêtre"; + ["TT_BeanCounterSelectBox"] = "Filtrer les résultats de la recherche par serveur, joueur, ou faction."; + ["TT_BidCheck"] = "Afficher les objets achetés à l'hôtel des ventes."; + ["TT_BidFailedCheck"] = "Affiche les objets pour lesquels vous avez été surenchéri."; + ["TT_ClassicCheck"] = "Affiche les résultats de la base de données de BeanCounter Classic."; + ["TT_ExactCheck"] = "Affiche uniquement les objets correspondant au texte exact de la recherche."; + ["TT_ItemIconBox"] = "Déposez un objet ici pour commencer une recherche dessus.\nAffiche la fenêtre de recherche actuelle si c'est possible."; + ["TT_ScrollHeader"] = "maintenir clic droit et bouger la souris pour redimensionner.\nCTRL + Clic droit pour réinitialiser la taille."; + ["TT_SearchBox"] = "Entrez les critèresde recherche ici ou laisser vide pour une recherche générale"; + + -- Section: User Interface + ["UiAddonTitle"] = "BeanCounter: Historique de vente"; + ["UiAucExpired"] = "Vente terminée"; + ["UiAucSuccessful"] = "Vente réussie"; + ["UiAuctions"] = "Enchères"; + ["UiAuctionTransaction"] = "Enchère"; + ["UiBids"] = "Offres"; + ["UiBidTransaction"] = "Offre"; + ["UiBuyerSellerHeader"] = "Acheteur/Vendeur"; + ["UiBuyTransaction"] = "Acheter"; + ["UiClassicCheckBox"] = "Afficher les données BeanCounter classique"; + ["UiData"] = "Donnée"; + ["UiDateHeader"] = "Date"; + ["UiDepositTransaction"] = "Dépôt"; + ["UiDone"] = "Effectué"; + ["UiExactNameSearch"] = "Nom exact"; + ["UiFailedAuctions"] = "Vente ratée"; + ["UiFee"] = "Commission"; + ["UiMailFrameRecording"] = "BeanCounter enregistre votre mail"; + ["UiMailFrameWait1"] = "Veuillez ne pas fermer la fenêtre de mail ou"; + ["UiMailFrameWait2"] = "les objets d'enchère ne seront pas enregistrés"; + ["UiNameHeader"] = "Objet"; + ["UiNetHeader"] = "Net"; + ["UiNetPerHeader"] = "Net unit."; + ["UiOutbid"] = "Surenchere"; + ["UiOutbids"] = "Surencheres"; + ["UiPriceHeader"] = "Prix"; + ["UiPriceper"] = "Prix/par"; + ["UiPricePerHeader"] = "Prix unit."; + ["UiPurchases"] = "Achats"; + ["UiQuantityHeader"] = "Qte"; + ["UiReason"] = "Raison"; + ["UiSales"] = "Ventes"; + ["UiSearch"] = "Recherche"; + ["UiSearchForLabel"] = "Rechercher :"; + ["UiSellTransaction"] = "Vendre"; + ["UiServer"] = "Serveur"; + ["UiTransactions"] = "Transactions"; + ["UiTransactionsLabel"] = "Transactions:"; + ["UiTransactionTypeHeader"] = "Type"; + ["UiWealth"] = "Fortune"; + ["UiWononBid"] = "Gagné sur enchere"; + ["UiWononBuyout"] = "Gagné sur achat direct"; + + }; + + itIT = { + + -- Section: Mail + ["MailAllianceAuctionHouse"] = "Casa d'aste dell'Alleanza"; + ["MailHordeAuctionHouse"] = "Casa d'aste dell'Orda"; + ["MailNeutralAuctionHouse"] = "Casa d'aste di Blackwater"; + + -- Section: Tooltip Messages + ["TTDateString"] = "Entra il formato desiderato per mostrate la data. L'opzione predefinita è %c"; + ["TTDateStringExample"] = "Mostra un esempio della data formattata"; + ["TTExtenalSearch"] = "Se fate una ricerca in un altro addon, BeanCounter vi mostrerà la stessa ricerca"; + ["TTMailInvoiceTimeout"] = "Sceglie quanto tempo BeanCounter cecherà di recuperare le mail dal server prima di lasciar perdere. Più basso == più veloce ma con più possibilità di mancare una mail. Più alto == più lento, ma migliori risultati se il server di posta è estremamente occupato."; + + -- Section: User Interface + ["UiAuctions"] = "Aste"; + ["UiAuctionTransaction"] = "Asta"; + ["UiBids"] = "Offerte"; + ["UiBidTransaction"] = "Offerta"; + ["UiBuyerSellerHeader"] = "Compratore/Venditore"; + ["UiBuyTransaction"] = "Compra"; + ["UiDateHeader"] = "Data"; + ["UiDepositTransaction"] = "Deposito"; + ["UiExactNameSearch"] = "Ricerca nome esatto"; + ["UiNameHeader"] = "Oggetto"; + ["UiNetHeader"] = "Netto"; + ["UiNetPerHeader"] = "Netto per"; + ["UiPriceHeader"] = "Prezzo"; + ["UiPricePerHeader"] = "Prezzo per"; + ["UiPurchases"] = "Compere"; + ["UiQuantityHeader"] = "Q.tà"; + ["UiSales"] = "Vendite"; + ["UiSearch"] = "Ricerca"; + ["UiSearchForLabel"] = "Cerca per:"; + ["UiSellTransaction"] = "Vendi"; + ["UiTransactions"] = "Transazioni"; + ["UiTransactionsLabel"] = "Transazioni:"; + ["UiTransactionTypeHeader"] = "Tipo"; + + }; + + koKR = { + + -- Section: Config Text + ["C_BeanCounterConfig"] = "콩순이 설정"; + ["C_BeanCounterDatabaseMaintenance"] = "콩순이 데이터베이스 관리"; + ["C_BeanCounterOptions"] = "콩순이 옵션"; + ["C_DataMaintenance"] = "데이터 관리"; + ["C_DateString"] = "사용할 날짜 형식: "; + ["C_DateStringExample"] = "날짜 예제: "; + ["C_ExtenalSearch"] = "외부 애드온이 콩순이 검색을 사용하도록 허락합니까?"; + ["C_MailInvoiceTimeout"] = "우편 송장 타임아웃 = %d 초"; + ["C_MailRecolor"] = "우편에 다시 색입히는 방법"; + ["C_ModTTShow"] = "Alt키를 눌렀을 때만 별도의 툴팁을 보여줍니다."; + ["C_Resortascendingtime"] = "시간순으로 모든 항목 재정렬"; + ["C_ResortDatabase"] = "데이터베이스 재정렬"; + ["C_ScanDatabase"] = "데이터베이스에서 오류를 검사합니다 : 콩순이 검색시 오류가 발생하면 사용하십시오. \n 사용하기 전에 콩순이의 SavedVariables을 백업해두십시오."; + ["C_SearchConfiguration"] = "검색 설정"; + ["C_ShowBeginnerTooltips"] = "마우스를 가져다 대면 초보자를 위한 툴팁을 표시합니다."; + ["C_ShowReasonPurchase"] = "게임 툴팁에 구매사유를 보여줍니다."; + ["C_ValidateDatabase"] = "데이터베이스 검증"; + + -- Section: Generic Strings + ["NoRe-Color"] = "다시 색을 입히지 않음"; + ["off"] = "끄기"; + ["Ok"] = "확인"; + ["on"] = "켜기"; + ["Re-ColorIcons"] = "아이콘에 다시 색입히기"; + ["Re-ColorIconsandText"] = "아이콘과 글자에 다시 색입히기"; + ["Re-ColorText"] = "글씨에 다시 색입히기"; + ["TooltipFailed"] = "툴팁 정보를 가져올수 없습니다"; + ["UiNeutralCheckBox"] = "중립 경매장 결과도 보기"; + + -- Section: Help Text + ["A_BeanCountersTooltip"] = "SearchUI의 아이템 구입사유를 저장하고 툴팁에 보여줍니다."; + ["A_DateString"] = "콩순이의 GUI창에 보이는 날짜의 형식을 결정합니다.\n명령어 앞에 %를 붙여야 하고, 복수의 명령어와 글씨가 혼합될 수 있습니다.\n예를 들어 %a == %X라고 하면 수요일 == 21:34:21로 표시됩니다."; + ["A_DateStringCommands"] = "명령어 : \n %a = 요일 약자, \n %A = 요일, \n %b = 월 약자, \n %B = 월,\n %c = 날짜와 시간, \n %d = 일(01-31일),\n %H = 시간 (24시간제), \n %I = 시간(12시간제),\n %M = 분, \n %m = 월(숫자),\n %p = a "; + ["A_ExtenalSearch"] = "다른 애드온이 콩순이를 이용해 아이템을 검색하여 콩순이 GUI에 보이도록 할 수 있습니다. 예를 들면 Appraiser에서 보고있는 아이템이 무엇인지 콩순이가 보여주도록 합니다."; + ["A_MailInvoiceTimeout"] = "송장 요청에 서버가 반응하기를 기다리는 시간입니다. 송장이란 경매장에서 온 우편의 누구, 무엇, 어떻게 부분입니다."; + ["A_MailRecolor"] = "콩순이는 경매장에서 온 모든 우편을 읽습니다. 이 옵션은 우편을 안 읽은 것처럼 다시 색을 입힐지를 결정합니다."; + ["HelpGuiItemBox"] = "아이템을 검색창에 떨굽니다."; + ["Q_BeanCountersTooltip"] = "콩순이 툴팁이란?"; + ["Q_DateString"] = "사용할 날짜 형식이란?"; + ["Q_DateStringCommands"] = "알맞는 날짜 명령어란?"; + ["Q_ExtenalSearch"] = "외부 애드온의 콩순이 사용이란?"; + ["Q_MailInvoiceTimeout"] = "우편 송장 타임아웃이란?"; + ["Q_MailRecolor"] = "우편에 다시 색입히기 방법이란?"; + + -- Section: HelpTooltip + ["TTDataExpireEnabled"] = "선택된 시간보다 오래된 데이터가 삭제됩니다"; + ["TT_neutralCheck"] = "중립 경매장 결과도 보기"; + + -- Section: Mail + ["MailAllianceAuctionHouse"] = "얼라이언스 경매장"; + ["MailHordeAuctionHouse"] = "호드 경매장"; + ["MailNeutralAuctionHouse"] = "중립 경매장"; + + -- Section: Tooltip Messages + ["TTDateString"] = "날짜 부분에 보일 형식을 넣습니다. 기본값은 %c입니다."; + ["TTDateStringExample"] = "설정된 날짜 형식의 예제를 보여줍니다."; + ["TTExtenalSearch"] = "다른 애드온에서 검색을 하면, 콩순이도 그 아이템에 대한 검색결과를 보여줍니다."; + ["TTMailInvoiceTimeout"] = "서버로부터 우편 송장을 얻기 위해 몇번이나 시도할지를 정합니다. 값을 낮추면 빠르지만 데이터가 빠져있을 확률이 있고, 값을 높이면 느리지만 우편 서버가 극히 바쁠경우 데이터를 가져올 확률을 높입니다."; + ["TTMailRecolor"] = "콩순이가 우편함을 검색한 다음에 우편이 어떻게 보일지를 결정합니다."; + ["TTModTTShow"] = "이 옵션은 Alt키를 눌렀을때 별도의 툴팁을 보여줍니다."; + ["TTOpenconfig"] = "콩순이 설정 창을 엽니다."; + ["TTResort Database"] = "콩순이의 데이터를 검색하여 모든 항목을 시간순서대로 정렬합니다. 데이터베이스 압축기능이 빨라집니다."; + ["TTShowBeginnerTooltips"] = "초보자를 위한 툴팁을 켭니다.(마우스를 가져다 대면 나옵니다.)"; + ["TTShowReasonPurchase"] = "SearchUI를 켭니다. 툴팁에 아이템구입 내용이 표시됩니다."; + ["TTValidateDatabase"] = "콩순이의 데이터를 검색하여 발견된 오류를 바로잡습니다. 검색시 오류가 발생하는 경우 사용하십시오."; + ["TT_AuctionCheck"] = "경매장에서 팔린 아이템 보기."; + ["TT_AuctionFailedCheck"] = "구매에 실패한 아이템 보기."; + ["TT_BeanCounterSelectBox"] = "서버나 플레이어, 혹은 진영별로 검색결과를 분류합니다."; + ["TT_BidCheck"] = "경매장에서 구매한 아이템 보기."; + ["TT_BidFailedCheck"] = "상회입찰한 아이템 보기."; + ["TT_ClassicCheck"] = "콩순이 구버전 DB에서 가져온 결과를 표시합니다."; + ["TT_ExactCheck"] = "정확히 일치하는 문장만 검색"; + ["TT_ItemIconBox"] = "아이템을 이곳에 가져다 놓으면 해당 아이템에 대한 검색을 시작합니다.\n가능한 경우 현재 검색중인 아이템의 아이콘을 표시합니다."; + ["TT_ScrollHeader"] = "\n마우스 오른버튼을 누르고 끌면 크기 변경\nCTRL+마우스 오른버튼을 누르면 초기화"; + ["TT_SearchBox"] = "검색쿼리를 이곳에 입력하세요(전부검색시 공백)"; + + -- Section: User Interface + ["UiAddonTitle"] = "콩순이: 경매결과 데이터베이스"; + ["UiAucExpired"] = "경매 만료"; + ["UiAucSuccessful"] = "경매 완료"; + ["UiAuctions"] = "경매품"; + ["UiAuctionTransaction"] = "경매"; + ["UiBids"] = "입찰품"; + ["UiBidTransaction"] = "입찰"; + ["UiBuyerSellerHeader"] = "구매자/판매자"; + ["UiBuyTransaction"] = "구입"; + ["UiClassicCheckBox"] = "불타는 성전의 옛 데이터 표시"; + ["UiData"] = "데이터"; + ["UiDateHeader"] = "날짜"; + ["UiDepositTransaction"] = "보증금"; + ["UiDone"] = "완료"; + ["UiExactNameSearch"] = "정확한 이름 찾기"; + ["UiFailedAuctions"] = "실패한 경매"; + ["UiFee"] = "수수료"; + ["UiMailFrameRecording"] = "콩순이가 메일을 기록합니다."; + ["UiMailFrameWait1"] = "메일함을 닫지 마세요."; + ["UiMailFrameWait2"] = "경매 아이템은 저장되지 않습니다."; + ["UiNameHeader"] = "아이템"; + ["UiNetHeader"] = "항목"; + ["UiNetPerHeader"] = "항목내용"; + ["UiOutbid"] = "상회 입찰"; + ["UiOutbids"] = "상회 입찰"; + ["UiPriceHeader"] = "가격"; + ["UiPriceper"] = "개당 가격"; + ["UiPricePerHeader"] = "가격 항목"; + ["UiPurchases"] = "구입"; + ["UiQuantityHeader"] = "수량"; + ["UiSales"] = "판매"; + ["UiSearch"] = "검색"; + ["UiSearchForLabel"] = "검색:"; + ["UiSellTransaction"] = "판매"; + ["UiServer"] = "서버"; + ["UiTransactions"] = "거래"; + ["UiTransactionsLabel"] = "거래:"; + ["UiTransactionTypeHeader"] = "형식"; + ["UiWealth"] = "재산"; + ["UiWononBid"] = "구입"; + ["UiWononBuyout"] = "즉시 구입"; + + }; + + nlNL = { + + -- Section: Config Text + ["C_BeanCounterConfig"] = "BeanCounter configuratie"; + ["C_BeanCounterOptions"] = "Beancounter opties"; + ["C_ColorizeSearch"] = "Voeg een kleurverloop toe aan elk resultaat in het zoekvenster"; + ["C_DatabaseLength"] = "Bepaald hoe lang BeanCounter Auction House transacties bewaard"; + ["C_DataMaintenance"] = "Data Onderhoud"; + ["C_DateString"] = "Datum formaat wat gebruikt moet worden:"; + ["C_DateStringExample"] = "Voorbeeld datum:"; + ["C_ExtenalSearch"] = "Sta externe addons toe om BeanCounters zoek te gebruiken"; + ["C_MailInvoiceTimeout"] = "Post Factuur Timeout = %d seconden"; + ["C_MailRecolor"] = "Post herkleur methode"; + ["C_ModTTShow"] = "Toon de extra tooltip alleen als Alt is ingedrukt."; + ["C_MonthsToKeep"] = "Hoeveel maanden moeten de gegevens bewaard worden?"; + ["C_OpacityLevel"] = "Doorzichtigheid niveau"; + ["C_Resortascendingtime"] = "Sorteer alle items, met oplopende tijd"; + ["C_ResortDatabase"] = "Sorteer de database"; + ["C_ScanDatabase"] = "Scan de database voor fouten: Gebruik dit als je fouten hebt tijdens het zoeken met BeanCounter.¶\nBewaar BeanCounter's bewaarde variablen voordat je dit gebruikt. "; + ["C_SearchConfiguration"] = "Zoek configuratie"; + ["C_SendToSearch"] = "BeanCounters gezochte items aan het hoofd Auction House venster toevoegen?"; + ["C_ShowBeginnerTooltips"] = "Toon beginnerstooltips in mouse-over"; + ["C_ShowReasonPurchase"] = "Toon de resen om te kopen in de tooltip"; + + -- Section: Mail + ["MailAllianceAuctionHouse"] = "Alliance Veiling"; + ["MailHordeAuctionHouse"] = "Horde Veiling"; + ["MailNeutralAuctionHouse"] = "Blackwater Veiling"; + + -- Section: User Interface + ["UiAuctions"] = "Veilingen"; + ["UiAuctionTransaction"] = "Veiling"; + ["UiBids"] = "Biedingen"; + ["UiBidTransaction"] = "Bod"; + ["UiBuyerSellerHeader"] = "Koper/Verkoper"; + ["UiBuyTransaction"] = "Koop"; + ["UiData"] = "Data"; + ["UiDateHeader"] = "Datum"; + ["UiDepositTransaction"] = "Storting"; + ["UiDone"] = "Klaar"; + ["UiExactNameSearch"] = "Zoek op exacte naam"; + ["UiNameHeader"] = "Item"; + ["UiNetHeader"] = "Net"; + ["UiNetPerHeader"] = "Net Per"; + ["UiPriceHeader"] = "Prijs"; + ["UiPriceper"] = "Prijs/Per"; + ["UiPricePerHeader"] = "Stukprijs"; + ["UiPurchases"] = "Aankopen"; + ["UiQuantityHeader"] = "Hoev."; + ["UiSales"] = "Verkopen"; + ["UiSearch"] = "Zoek"; + ["UiSearchForLabel"] = "Zoek naar:"; + ["UiSellTransaction"] = "Verkoop"; + ["UiTransactions"] = "Transacties"; + ["UiTransactionsLabel"] = "Transacties:"; + ["UiTransactionTypeHeader"] = "Type"; + + }; + + plPL = { + + -- Section: Config Text + ["C_BeanCounterConfig"] = "Konfiguracja BeanCounter'a"; + ["C_BeanCounterOptions"] = "Opcje BeanCounter'a"; + ["C_DateString"] = "Format Daty:"; + ["C_DateStringExample"] = "Przykładowa Data:"; + ["C_ExtenalSearch"] = "Zezwól innym Addonom na użycie wyszukiwarki BeanCounter"; + + -- Section: Generic Strings + ["NoRe-Color"] = "Brak przemalowywania"; + ["off"] = "wyłączony"; + ["Ok"] = "Ok"; + ["on"] = "włączony"; + ["Re-ColorIcons"] = "Przemaluj Ikony"; + ["Re-ColorIconsandText"] = "Przemaluj Ikony i Tekst"; + ["Re-ColorText"] = "Przemaluj Tekst"; + ["TooltipFailed"] = "Nie można zdobyć informacji na temat okienka"; + + -- Section: Mail + ["MailAllianceAuctionHouse"] = "Auction House Przymierza"; + ["MailHordeAuctionHouse"] = "Auction House Hordy"; + ["MailNeutralAuctionHouse"] = "Auction House Czarnej wody"; + + -- Section: Tooltip Messages + ["TTDateString"] = "Wpisz format, w którym chcesz mieć wyświetloną datę. Domyślny to %c."; + ["TTDateStringExample"] = "Wyświetla przykład pokazujący, jak twoja sformatowana data będzie wyglądać."; + ["TTExtenalSearch"] = "Gdy wpisujesz wyszukiwanie w innym AddOn'ie, BeanCounter także wyświetli wyszukiwanie tej rzeczy."; + ["TTMailInvoiceTimeout"] = "Wybiera, jak długo BeanCounter będzie próbować dostać odpowiedź z serwera, zanim się podda. Mniejszy czas = szybciej, ale jest większa szansa na zgubienie danych, Większy czas = wolniej, ale zwiększa szanse na zdobycie informacji jeśli serwer jest zajęty. "; + ["TTMailRecolor"] = "Pokaż, jak poczta się będzie pojawiać, gdy BeanCounter już zeskanował skrzynkę odbiorczą."; + ["TTOpenconfig"] = "Włącza okno konfiguracji BeanCounter'a"; + + -- Section: User Interface + ["UiAddonTitle"] = "BeanCounter: Baza Danych Historii Aukcji"; + ["UiAucExpired"] = "Aukcja Wygasła"; + ["UiAucSuccessful"] = "Aukcja Udana"; + ["UiAuctions"] = "Aukcje"; + ["UiAuctionTransaction"] = "Aukcja"; + ["UiBids"] = "Licytacje"; + ["UiBidTransaction"] = "Licytacja"; + ["UiBuyerSellerHeader"] = "Kupujący/Sprzedawca"; + ["UiBuyTransaction"] = "Kup"; + ["UiClassicCheckBox"] = "Pokaż klasyczne dane BC."; + ["UiData"] = "Dane"; + ["UiDateHeader"] = "Data"; + ["UiDepositTransaction"] = "Depozyt"; + ["UiDone"] = "Zrobione"; + ["UiExactNameSearch"] = "Wyszukiwanie dokładnej nazwy"; + ["UiFailedAuctions"] = "Nieudane Aukcje"; + ["UiFee"] = "Zapłacono"; + ["UiMailFrameRecording"] = "BeanCounter zapisuje twoją pocztę"; + ["UiMailFrameWait1"] = "Proszę nie zamykać okienka poczty, lub"; + ["UiMailFrameWait2"] = "Przedmioty z aukcji nie zostaną zapisane"; + ["UiNameHeader"] = "Przedmiot"; + ["UiNetHeader"] = "Sieć"; + ["UiNetPerHeader"] = "Sieć za"; + ["UiOutbid"] = "Licytacja przegrana"; + ["UiOutbids"] = "Licytacje przegrane"; + ["UiPriceHeader"] = "Cena"; + ["UiPriceper"] = "Cena/za"; + ["UiPricePerHeader"] = "Nagłówek ceny"; + ["UiPurchases"] = "Zakupy"; + ["UiQuantityHeader"] = "Ilość"; + ["UiSales"] = "Sprzedaże"; + ["UiSearch"] = "Szukaj"; + ["UiSearchForLabel"] = "Szukaj:"; + ["UiSellTransaction"] = "Sprzedaj"; + ["UiServer"] = "Serwer"; + ["UiTransactions"] = "Transakcje"; + ["UiTransactionsLabel"] = "Transakcje:"; + ["UiTransactionTypeHeader"] = "Typ"; + ["UiWealth"] = "Zasób"; + ["UiWononBid"] = "Wygrane w licytacji"; + ["UiWononBuyout"] = "Wygrane w wykupie"; + + }; + + ptPT = { + + -- Section: Config Text + ["C_BeanCounterConfig"] = "Configurações do BeanCounter"; + ["C_BeanCounterDatabaseMaintenance"] = "Manutenção do Banco de Dados do BeanCounter"; + ["C_BeanCounterOptions"] = "Opções do BeanCounter"; + ["C_DataMaintenance"] = "Manutenção de Dados"; + ["C_DateString"] = "Formato de data a ser usado:"; + ["C_DateStringExample"] = "Exemplo de Data"; + ["C_ExtenalSearch"] = "Permitir que AddOns externos usem a pesquisa do BeanCounter?"; + ["C_MailRecolor"] = "Método de Recolorização de Mail"; + ["C_Resortascendingtime"] = "Reorganizar todas as entradas por data em ordem crescente"; + ["C_ResortDatabase"] = "Reorganizar Banco de Dados"; + ["C_ScanDatabase"] = "Analizar o Banco de Dados por erros: Use se você encontra erros enquanto pesquisa usando o BeanCounter. \n Faça um backup das variáveis salvas do BeanCounter antes de usar."; + ["C_ValidateDatabase"] = "Validar Banco de Dados"; + + -- Section: Generic Strings + ["off"] = "desliga"; + ["on"] = "liga"; + + -- Section: Mail + ["MailAllianceAuctionHouse"] = "Leilão da Alliance"; + ["MailHordeAuctionHouse"] = "Leilão da Horde"; + + -- Section: User Interface + ["UiAuctions"] = "Leilões"; + ["UiAuctionTransaction"] = "Leilão"; + ["UiBids"] = "Ofertas"; + ["UiBidTransaction"] = "Oferta"; + ["UiBuyerSellerHeader"] = "Comprador/Vendedor"; + ["UiBuyTransaction"] = "Compra"; + ["UiDateHeader"] = "Data"; + ["UiDepositTransaction"] = "Depósito"; + ["UiExactNameSearch"] = "Procura por nome exacto"; + ["UiNameHeader"] = "Objecto"; + ["UiNetHeader"] = "Rede"; + ["UiNetPerHeader"] = "Rede por"; + ["UiPriceHeader"] = "Preço"; + ["UiPricePerHeader"] = "Cabeçalho de Preço"; + ["UiPurchases"] = "Compras"; + ["UiQuantityHeader"] = "Qtn"; + ["UiSales"] = "Vendas"; + ["UiSearch"] = "Procura"; + ["UiSearchForLabel"] = "Procurar por:"; + ["UiSellTransaction"] = "Vender"; + ["UiTransactions"] = "Transacções"; + ["UiTransactionsLabel"] = "Transacções"; + ["UiTransactionTypeHeader"] = "Tipo"; + + }; + + ruRU = { + + -- Section: Config Text + ["C_BeanCounterConfig"] = "Настройки BeanCounter"; + ["C_BeanCounterDatabaseMaintenance"] = "Обслуживание базы данных"; + ["C_BeanCounterOptions"] = "Настройки BeanCounter"; + ["C_ColorizeSearch"] = "Добавить градиент цвета для каждого результата в окне поиска"; + ["C_DatabaseLength"] = "Определить, как долго BeanCounter должен хранить данные о торговых операциях."; + ["C_DataMaintenance"] = "Обработка данных"; + ["C_DateString"] = "Используемый формат даты:"; + ["C_DateStringExample"] = "Пример даты:"; + ["C_ExtenalSearch"] = "Позволить сторонним аддонам использовать поиск BeanCounter'а?"; + ["C_MailInvoiceTimeout"] = "Таймаут получения почты = %d секунд"; + ["C_MailRecolor"] = "Способ окрашивания почты"; + ["C_ModTTShow"] = "Отображать доп. подсказку только если нажат ALT"; + ["C_MonthsToKeep"] = "Сколько месяцев сохранять данные?"; + ["C_OpacityLevel"] = "Уровень прозрачности"; + ["C_Resortascendingtime"] = "Сортировать записи по возрастанию времени"; + ["C_ResortDatabase"] = "Сортировать базу данных"; + ["C_ScanDatabase"] = "Проверка базы данных: воспользуйтесь, если при поиске возникают ошибки. \n Создайте резервную копию данных перед использованием."; + ["C_SearchConfiguration"] = "Поиск Конфигурации"; + ["C_SendToSearch"] = "Добавить предметы, найденные BeanCounter, в главное окно?"; + ["C_ShowBeginnerTooltips"] = "Показывать всплывающие подсказки при наведении мыши."; + ["C_ShowReasonPurchase"] = "Показывать цель покупки во всплывающей подсказке."; + ["C_ValidateDatabase"] = "Проверка базы данных."; + + -- Section: Generic Strings + ["NoRe-Color"] = "Не изменять цвет"; + ["off"] = "выкл"; + ["Ok"] = "Ok"; + ["on"] = "вкл"; + ["Re-ColorIcons"] = "Изменить цвет иконок"; + ["Re-ColorIconsandText"] = "Изменить цвет иконок и текста"; + ["Re-ColorText"] = "Изменить цвет текста"; + ["TooltipFailed"] = "Не удалось получить информацию для всплывающей подсказки"; + ["UiNeutralCheckBox"] = "Показать нейтральные данные аукциона"; + + -- Section: Help Text + ["A_BeanCountersTooltip"] = "BeanCounter сохраняет причины покупки предметов и отображает их в подсказке."; + ["A_DateString"] = "Эта опция определяет, как будет отображаться дата в окне BeanCounter'а. Каждая команда должна начинаться со знака % (несколько команд и текст могут быть объединены). Например, при значении %a == %X дата будет отображаться так: Mon == 21:34:21"; + ["A_DateStringCommands"] = "Комманды: \n %a = аббр. дня недели, \n %A = полное название дня недели, \n %b = аббр. месяца, \n %B = month name - название месяца, \n %c = дата и время, \n %d = день месяца, \n %H = часы (24), \n %I = часы (12),\n %M = минуты,\n %m = месяц (12),\n %p = am/pm, \n %S = секунды,\n %U = номер недели в году,\n %w = день недели (0-6),\n %x = дата, \n %X = время,\n %Y = год (2008), \n %y = год из двух цифр (07)"; + ["A_ExtenalSearch"] = "Другие аддоны могут использовать поиск BeanCounter'а. Например, это позволит BeanCounter'у показывать свои данные в окне Appraiser'а"; + ["A_MailInvoiceTimeout"] = "Время ожидания ответа от сервера при посылке запросов почте аукциона. Запросы могут быть - \"кто\", \"что\" и \"как\"."; + ["A_MailRecolor"] = "BeanCounter просматривает всю почту, поступающую от аукционного дома. Этот параметр указывает BeanCounter'у как необходимо окрашивать сообщения, чтобы они выглядели непрочитанными."; + ["HelpGuiItemBox"] = "Поместите предмет в ячейку для поиска."; + ["Q_BeanCountersTooltip"] = "Что такое подсказка BeanCounter'а"; + ["Q_DateString"] = "Используемый формат даты?"; + ["Q_DateStringCommands"] = "Приемлемые команды даты?"; + ["Q_ExtenalSearch"] = "Позволить сторонним аддонам использовать BeanCounter?"; + ["Q_MailInvoiceTimeout"] = "Что такое таймаут получения почты?"; + ["Q_MailRecolor"] = "Что такое метод окрашивания почты?"; + + -- Section: HelpTooltip + ["C_MaxDisplayedResults"] = "Макс. кол-во отображаемых результатов поиска (для каждой базы данных)"; + ["TTDataExpireEnabled"] = "Данные, созданные ранее указонного периода будут УДАЛЕНЫ"; + ["TT_ColorizeSearch"] = "Эта настройка изменяет цвета отображения предметов в окне поиска BeanCounter."; + ["TT_MaxDisplayedResults"] = "Это управляет общим числом результатов, отображаемых в окне прокрутки."; + ["TT_neutralCheck"] = "Показать результаты из Neutral Blackwater Аукционного дома\n"; + ["TT_OpacityLevel"] = "Управляет уровнем прозрачности цветных линеек в окне поиска BeanCounter (если они разрешены)"; + ["TT_sendtosearch"] = "Данные, введенные в окне поиска BeanCounter добавляются в окно просмотра Аукциона."; + + -- Section: Interface + ["Cancelled"] = "Отменен"; + ["UiAucCancelled"] = "Аукцион отменен"; + + -- Section: Mail + ["MailAllianceAuctionHouse"] = "Аукционный дом Альянса"; + ["MailHordeAuctionHouse"] = "Аукционный дом Орды"; + ["MailNeutralAuctionHouse"] = "Аукцион Черной Воды"; + + -- Section: Tooltip Messages + ["TTDateString"] = "Введите формат, в котором Вы хотели бы видеть отображаемую дату. По умолчанию %c"; + ["TTDateStringExample"] = "Пример даты, отформатированной в соответствии с текущими параметрами."; + ["TTExtenalSearch"] = "Когда производится поиск в другом аддоне, BeanCounter будет также отображать поиск для этого предмета."; + ["TTMailInvoiceTimeout"] = "Выбор того, как долго BeanCounter будет пытаться получить входящую почту с сервера до прекращения попыток. Меньшее значение == быстрее, но велик риск потерять часть данных, большее значение == медленнее, но увеличивает шансы получить данные, даже если почтовый сервер слишком занят."; + ["TTMailRecolor"] = "Выберите, как именно будет отображаться почта, после того, как BeanCounter просканирует почтовый ящик."; + ["TTModTTShow"] = "Эта настройка отображает доп. подсказки BeanCounter только если нажат ALT"; + ["TTOpenconfig"] = "Открывает окно настроек BeanCounter"; + ["TTResort Database"] = "Пересортировка данных BeanCounter'а по возрастанию времени. Это ускорит функции сжатия базы."; + ["TTShowBeginnerTooltips"] = "Включает всплывающие подсказки, отображаемые при наведении мыши."; + ["TTShowReasonPurchase"] = "Включает отображение причины покупки в подсказке."; + ["TTValidateDatabase"] = "Просканировать данные BeanCounter'а и попытаться исправить ошибки. Воспользуйтесь данной функцией, если вы получаете ошибки при поиске."; + ["TT_AuctionCheck"] = "Отображать предметы, проданные на аукционе"; + ["TT_AuctionFailedCheck"] = "Отображать предметы, которые не удалось продать."; + ["TT_BeanCounterAHTab"] = "Кликните правой клавишей для показа BeanCounter в отдельном окне. "; + ["TT_BeanCounterSelectBox"] = "Фильтровать результаты поиска по серверу, персонажу или фракции."; + ["TT_BidCheck"] = "Отображать предметы, купленные на аукционе."; + ["TT_BidFailedCheck"] = "Отображать предметы, на которые вы сделали ставку."; + ["TT_ClassicCheck"] = "Отображать результаты из базы данных BeanCounter Classic."; + ["TT_ExactCheck"] = "Искать только те предметы, название которых в точности совпадает с текстом в строке поиска."; + ["TT_ItemIconBox"] = "Перетащите предмет, чтобы начать его поиск.\n Отобразить иконку искомого (если возможно)"; + ["TT_ScrollHeader"] = "\nПКМ - изменение размера\nCTRL+ПКМ - сброс"; + ["TT_SearchBox"] = "Введите поисковый запрос здесь или оставьте пустым, чтобы найти всё"; + + -- Section: User Interface + ["UiAddonTitle"] = "BeanCounter: Аукционная база данных"; + ["UiAucExpired"] = "Просроченный аукцион"; + ["UiAucSuccessful"] = "Успешный аукцион"; + ["UiAuctions"] = "Аукционы"; + ["UiAuctionTransaction"] = "Аукцион"; + ["UiBids"] = "Ставки"; + ["UiBidTransaction"] = "Ставка"; + ["UiBuyerSellerHeader"] = "Покупатель/Продавец"; + ["UiBuyTransaction"] = "Цена выкупа"; + ["UiClassicCheckBox"] = "Показать данные BC Classic"; + ["UiData"] = "Данные"; + ["UiDateHeader"] = "Дата"; + ["UiDepositTransaction"] = "Депозит"; + ["UiDone"] = "Завершить"; + ["UiExactNameSearch"] = "Поиск точного совпадения"; + ["UiFailedAuctions"] = "Неудавщиеся аукционы"; + ["UiFee"] = "Оплата"; + ["UiMailFrameRecording"] = "BeanCounter сканирует вашу почту"; + ["UiMailFrameWait1"] = "Пожалуйста, не закрывайте почтовое окно, иначе"; + ["UiMailFrameWait2"] = "данные не будут корректно обработаны"; + ["UiNameHeader"] = "Предмет"; + ["UiNetHeader"] = "Нетто"; + ["UiNetPerHeader"] = "Нетто за"; + ["UiOutbid"] = "Ставка перебита"; + ["UiOutbids"] = "Перебитые ставки"; + ["UiPriceHeader"] = "Цена"; + ["UiPriceper"] = "Цена за шт."; + ["UiPricePerHeader"] = "Заголовок Цены"; + ["UiPurchases"] = "Покупки"; + ["UiQuantityHeader"] = "Кол-во"; + ["UiReason"] = "Причина"; + ["UiSales"] = "Продажи"; + ["UiSearch"] = "Поиск"; + ["UiSearchForLabel"] = "Поиск:"; + ["UiSellTransaction"] = "Продажа"; + ["UiServer"] = "Сервер"; + ["UiTransactions"] = "Операции"; + ["UiTransactionsLabel"] = "Операции:"; + ["UiTransactionTypeHeader"] = "Тип"; + ["UiWealth"] = "Состояние"; + ["UiWononBid"] = "Выиграно по ставке"; + ["UiWononBuyout"] = "Выиграно по выкупу"; + + }; + + zhCN = { + + -- Section: Config Text + ["C_BeanCounterConfig"] = "BeanCounter 设置"; + ["C_BeanCounterDatabaseMaintenance"] = "BeanCounter 数据库维护"; + ["C_BeanCounterOptions"] = "BeanCounter 选项"; + ["C_ColorizeSearch"] = "在搜索窗口为每个搜索的结果添加一个色彩渐层"; + ["C_DataMaintenance"] = "数据维护"; + ["C_DateString"] = "使用的日期格式:"; + ["C_DateStringExample"] = "示例日期:"; + ["C_ExtenalSearch"] = "允许外部插件使用BeanCounter的搜索?"; + ["C_MailInvoiceTimeout"] = "邮箱通知延迟=%d秒"; + ["C_MailRecolor"] = "邮箱上色方法"; + ["C_ModTTShow"] = "按下Alt时显示提示"; + ["C_OpacityLevel"] = "透明度"; + ["C_Resortascendingtime"] = "将所有数据按照增序排序"; + ["C_ResortDatabase"] = "重排序数据库"; + ["C_ScanDatabase"] = "搜索数据库错误:当你在搜索BeanCounter时出错使用.\n 用前备份BeanCounter的配置文件."; + ["C_SearchConfiguration"] = "搜索设置"; + ["C_ShowBeginnerTooltips"] = "当鼠标经过时显示新手提示"; + ["C_ShowReasonPurchase"] = "于游戏提示中显示购买原因"; + ["C_ValidateDatabase"] = "验证数据库"; + + -- Section: Generic Strings + ["NoRe-Color"] = "无上色"; + ["off"] = "关闭"; + ["Ok"] = "确定"; + ["on"] = "开启"; + ["Re-ColorIcons"] = "为图标上色"; + ["Re-ColorIconsandText"] = "为图标和文字上色"; + ["Re-ColorText"] = "为文字上色"; + ["TooltipFailed"] = "无法获得提示信息"; + ["UiNeutralCheckBox"] = "显示中立拍卖行数据"; + + -- Section: Help Text + ["A_BeanCountersTooltip"] = "BeanCounter将储存物品被购买的搜索界面原因并于提示中显示它."; + ["A_DateString"] = "控制BeanCounter的GUI如何显示日期.命令以%作为前缀,并且可以混用多个命令与文字.例如%a==%X将显示Wed==21:34:21"; + ["A_DateStringCommands"] = "命令:\n %a = 缩写的日期名,\n %A = 日期名, \n %b = 缩写的月名, \n %B = 月名,\n %c = 日期和时间, \n %d = 日 (01-31),\n %H = 小时 (24), \n %I = 小时 (12),\n %M = 分, \n %m = 月,\n %p = 每"; + ["A_ExtenalSearch"] = "其他插件可以请求于BeanCounter的GUI内显示一件物品的搜索结果. 例如 此允许BeanCounter显示你在Appraiser查找的物品."; + ["A_MailInvoiceTimeout"] = "BeanCounter将等待的服务器的通知请求的时间.通知是一个记载了谁,什么,多少钱的拍卖行邮件."; + ["A_MailRecolor"] = "BeanCounter读取来自拍卖行的所有邮件,此选项告诉BeanCounter如何将未读邮件上色以便用户阅读."; + ["HelpGuiItemBox"] = "拖入物品以开始搜索"; + ["Q_BeanCountersTooltip"] = "什么是BeanCounter提示"; + ["Q_DateString"] = "使用日期格式?"; + ["Q_DateStringCommands"] = "可接受日期命令?"; + ["Q_ExtenalSearch"] = "允许外部插件使用BeanCounter?"; + ["Q_MailInvoiceTimeout"] = "什么是邮件通知延迟?"; + ["Q_MailRecolor"] = "什么是上色?"; + + -- Section: HelpTooltip + ["TT_neutralCheck"] = "显示黑水拍卖行数据结果"; + + -- Section: Interface + ["Cancelled"] = "已取消"; + ["UiAucCancelled"] = "拍卖已取消"; + + -- Section: Mail + ["MailAllianceAuctionHouse"] = "联盟拍卖行"; + ["MailHordeAuctionHouse"] = "部落拍卖行"; + ["MailNeutralAuctionHouse"] = "黑水拍卖行"; + + -- Section: Tooltip Messages + ["TTDateString"] = "键入你期望的日期显示格式.默认为%c"; + ["TTDateStringExample"] = "显示一个示例的日期"; + ["TTExtenalSearch"] = "当在其他插件中键入一个搜索,BeanCounter也将显示一个搜索结果"; + ["TTMailInvoiceTimeout"] = "选择BeanCounter将尝试从服务器获取一个邮件通知的时间.低==更快但较高几率丢失数据,高==更慢但可提高获取数据的几率(如果邮件服务器非常忙)."; + ["TTMailRecolor"] = "选择BeanCounter扫描邮箱后邮件的显示方式."; + ["TTModTTShow"] = "Alt被按下时显示BeanCounter选项的提示。"; + ["TTOpenconfig"] = "打开BeanCounter配置窗口"; + ["TTResort Database"] = "这将把BeanCounter数据按照增序排序.可提高数据库压缩速度."; + ["TTShowBeginnerTooltips"] = "开启鼠标经过时的新手提示"; + ["TTShowReasonPurchase"] = "开启物品购买原因"; + ["TTValidateDatabase"] = "这将搜索BeanCounter数据并且尝试纠正任何错误.当搜索遇到错误时使用."; + ["TT_AuctionCheck"] = "显示在拍卖行已售出物品。"; + ["TT_AuctionFailedCheck"] = "显示你未能售出的物品。"; + ["TT_BeanCounterSelectBox"] = "根据服务器、角色、阵营过滤搜索结果。"; + ["TT_BidCheck"] = "显示在拍卖行拍到的物品。"; + ["TT_BidFailedCheck"] = "显示你出价最高的物品。"; + ["TT_ClassicCheck"] = "显示BeanCounter经典数据库返回的结果。"; + ["TT_ExactCheck"] = "精确匹配搜索框中的文本。"; + ["TT_ItemIconBox"] = "拖入一件物品并搜索该物品。可能\n显示 当前搜索物品图标。"; + ["TT_ScrollHeader"] = "\n右击拖动 改变尺寸 \nCTRL+右击 重置"; + ["TT_SearchBox"] = "在这里输入搜索条件或者留空则显示所有记录"; + + -- Section: User Interface + ["UiAddonTitle"] = "BeanCounter: 拍卖历史数据库"; + ["UiAucExpired"] = "拍卖过期"; + ["UiAucSuccessful"] = "拍卖成功"; + ["UiAuctions"] = "拍卖"; + ["UiAuctionTransaction"] = "拍卖"; + ["UiBids"] = "出价"; + ["UiBidTransaction"] = "竞标"; + ["UiBuyerSellerHeader"] = "买主/卖主"; + ["UiBuyTransaction"] = "购买"; + ["UiClassicCheckBox"] = "显示BC传统数据"; + ["UiData"] = "数据"; + ["UiDateHeader"] = "日期"; + ["UiDepositTransaction"] = "保管费"; + ["UiDone"] = "完成"; + ["UiExactNameSearch"] = "确切名称搜索"; + ["UiFailedAuctions"] = "已失败的拍卖"; + ["UiFee"] = "费用"; + ["UiMailFrameRecording"] = "BeanCounter正在记录你的邮箱"; + ["UiMailFrameWait1"] = "请不要关闭邮箱界面或者"; + ["UiMailFrameWait2"] = "拍卖物品将不会被记录"; + ["UiNameHeader"] = "物品"; + ["UiNetHeader"] = "净利"; + ["UiNetPerHeader"] = "单件净利"; + ["UiOutbid"] = "最高出价"; + ["UiOutbids"] = "最高出价"; + ["UiPriceHeader"] = "价格"; + ["UiPriceper"] = "价格/单件"; + ["UiPricePerHeader"] = "单件价格"; + ["UiPurchases"] = "购买"; + ["UiQuantityHeader"] = "数量"; + ["UiSales"] = "销售"; + ["UiSearch"] = "搜索"; + ["UiSearchForLabel"] = "搜索"; + ["UiSellTransaction"] = "出售"; + ["UiServer"] = "服务器"; + ["UiTransactions"] = "交易"; + ["UiTransactionsLabel"] = "交易"; + ["UiTransactionTypeHeader"] = "类型"; + ["UiWealth"] = "大量"; + ["UiWononBid"] = "竞拍获胜"; + ["UiWononBuyout"] = "一口价获胜"; + + }; + + zhTW = { + + -- Section: Config Text + ["C_BeanCounterConfig"] = "拍賣記錄器設定"; + ["C_BeanCounterDatabaseMaintenance"] = "拍賣記錄器資料庫維護"; + ["C_BeanCounterOptions"] = "拍賣記錄器選項"; + ["C_ColorizeSearch"] = "增加漸變色彩到搜尋視窗中的每一個結果"; + ["C_DatabaseLength"] = "設定多久之後 BeanCounter 將儲存拍賣場的資料。"; + ["C_DataMaintenance"] = "資料庫維護"; + ["C_DateString"] = "日期的格式"; + ["C_DateStringExample"] = "例如日期:"; + ["C_ExtenalSearch"] = "允許其他插件使用拍賣記錄器的搜尋?"; + ["C_MailInvoiceTimeout"] = "郵件到期通知 = %d 秒"; + ["C_MailRecolor"] = "信件重新上色方法"; + ["C_ModTTShow"] = "當按下Alt的時候顯示額外提示"; + ["C_MonthsToKeep"] = "要保留多少個月份的資料?"; + ["C_OpacityLevel"] = "不透明度"; + ["C_Resortascendingtime"] = "將所有資料依增序排列"; + ["C_ResortDatabase"] = "資料庫重新排序"; + ["C_ScanDatabase"] = "掃描數據庫的錯誤: 如果您在搜索BeanCounter時發生錯誤,請使用這選項. \n 使用前請先備份BeanCounter的saved variables."; + ["C_SearchConfiguration"] = "搜尋設置"; + ["C_SendToSearch"] = "是否要將 BeanCounter 搜尋過的物件加入到主拍賣場視窗?"; + ["C_ShowBeginnerTooltips"] = "當鼠標停留時顯示beginner提示框."; + ["C_ShowReasonPurchase"] = "在提示框內顯示購買的原因."; + ["C_ValidateDatabase"] = "驗證數據庫"; + + -- Section: Generic Strings + ["NoRe-Color"] = "不重新上色"; + ["off"] = "關閉"; + ["Ok"] = "確定"; + ["on"] = "開啟"; + ["Re-ColorIcons"] = "圖標重新上色"; + ["Re-ColorIconsandText"] = "圖標和文字重新上色"; + ["Re-ColorText"] = "文字重新上色"; + ["TooltipFailed"] = "無法取得訊息窗資訊"; + ["UiNeutralCheckBox"] = "顯示中立拍賣場的資料"; + + -- Section: Help Text + ["A_BeanCountersTooltip"] = "BeanCounter將儲存物品被購買的搜索界面原因並於提示中顯示它."; + ["A_DateString"] = "控制BeanCounter的GUI如何顯示日期.命令以%作為前綴,並且可以混用多個命令與文字.例如%a==%X將顯示Wed==21:34:21"; + ["A_DateStringCommands"] = "命令:\n %a = 縮寫的日期名,\n %A = 日期名, \n %b = 縮寫的月名, \n %B = 月名,\n %c = 日期和時間, \n %d = 日 (01-31),\n %H = 小時 (24), \n %I = 小時 (12),\n %M = 分, \n %m = 月,\n %p = 每"; + ["A_ExtenalSearch"] = "其他插件可以請求於BeanCounter的GUI內顯示一件物品的搜索結果. 例如 此允許BeanCounter顯示你在Appraiser查找的物品."; + ["A_MailInvoiceTimeout"] = "BeanCounter將等待的服務器的通知請求的時間.通知是一個記載了誰,什麼,多少錢的拍賣行郵件."; + ["A_MailRecolor"] = "BeanCounter讀取來自拍賣行的所有郵件,此選項告訴BeanCounter如何將未讀郵件上色以便用戶閱讀."; + ["HelpGuiItemBox"] = "拖入物品以開始搜索"; + ["Q_BeanCountersTooltip"] = "什麼是BeanCounter提示"; + ["Q_DateString"] = "使用日期格式?"; + ["Q_DateStringCommands"] = "可接受日期命令?"; + ["Q_ExtenalSearch"] = "允許外部插件使用BeanCounter?"; + ["Q_MailInvoiceTimeout"] = "什麼是郵件通知逾時?"; + ["Q_MailRecolor"] = "什麼是重新上色?"; + + -- Section: HelpTooltip + ["C_MaxDisplayedResults"] = "最大搜尋結果顯示量(從每個資料庫)"; + ["TTDataExpireEnabled"] = "老於這個選擇過的時間區間的資料將會被[刪除]"; + ["TT_ColorizeSearch"] = "這個選項將會改變 BeanCounter 搜尋視窗中的物件欄位色彩。"; + ["TT_MaxDisplayedResults"] = "這將控制有多少數量的結果會顯示在卷軸框架上。"; + ["TT_neutralCheck"] = "顯示中立的黑水灣拍賣場的結果"; + ["TT_OpacityLevel"] = "這將控制 BeanCounter 搜尋窗中的色彩條的不透明度程度(如果已啟用的話)"; + ["TT_sendtosearch"] = "當於 BeanCounter 中輸入一個搜尋字串名稱時這也會將該字串名稱加入拍賣場瀏覽頁面上。"; + + -- Section: Interface + ["Cancelled"] = "已取消"; + ["UiAucCancelled"] = "拍賣已取消"; + + -- Section: Mail + ["MailAllianceAuctionHouse"] = "聯盟拍賣場"; + ["MailHordeAuctionHouse"] = "部落拍賣場"; + ["MailNeutralAuctionHouse"] = "中立拍賣場"; + + -- Section: Tooltip Messages + ["TTDateString"] = "键入你期望的日期显示格式.默认为%c"; + ["TTDateStringExample"] = "顯示一個示例的日期"; + ["TTExtenalSearch"] = "當在其他插件中鍵入一個搜索,BeanCounter也將顯示一個搜索結果"; + ["TTMailInvoiceTimeout"] = "選擇BeanCounter將嘗試從服務器獲取一個郵件通知的時間.低==更快但較高幾率丟失數據,高==更慢但可提高獲取數據的幾率(如果郵件服務器非常忙)."; + ["TTMailRecolor"] = "選擇BeanCounter掃瞄郵箱後郵件的顯示方式."; + ["TTModTTShow"] = "Alt被按下時將顯示選項BeanCounter的額外提示。\n"; + ["TTOpenconfig"] = "打開BeanCounter配置窗口"; + ["TTResort Database"] = "這將把BeanCounter數據按照增序排序.可提高數據庫壓縮速度."; + ["TTShowBeginnerTooltips"] = "開啟鼠標經過時的新手提示"; + ["TTShowReasonPurchase"] = "開啟物品購買原因"; + ["TTValidateDatabase"] = "這將搜索BeanCounter數據並且嘗試糾正任何錯誤.當搜索遇到錯誤時使用."; + ["TT_AuctionCheck"] = "顯示在拍賣行已售出物品。"; + ["TT_AuctionFailedCheck"] = "顯示你未能售出的物品。"; + ["TT_BeanCounterAHTab"] = "右鍵點選來將 BeanCounter 顯示在一個額外視窗。"; + ["TT_BeanCounterSelectBox"] = "根據服務器、角色、陣營過濾搜索結果。"; + ["TT_BidCheck"] = "顯示在拍賣行拍到的物品。"; + ["TT_BidFailedCheck"] = "顯示你出價最高的物品。"; + ["TT_ClassicCheck"] = "顯示BeanCounter經典數據庫返回的結果。"; + ["TT_ExactCheck"] = "精確匹配搜索框中的文本。"; + ["TT_ItemIconBox"] = "拖入一件物品並搜索該物品。可能\n顯示 當前搜索物品圖標。"; + ["TT_ScrollHeader"] = "\n右擊拖動 改變尺寸 \nCTRL+右擊 重置"; + ["TT_SearchBox"] = "在這裡輸入搜索條件或者留空則顯示所有記錄"; + + -- Section: User Interface + ["UiAddonTitle"] = "BeanCounter:拍賣交易歷史資料庫"; + ["UiAucExpired"] = "拍賣逾時"; + ["UiAucSuccessful"] = "拍賣成功!"; + ["UiAuctions"] = "拍賣場"; + ["UiAuctionTransaction"] = "拍賣"; + ["UiBids"] = "出價"; + ["UiBidTransaction"] = "出價"; + ["UiBuyerSellerHeader"] = "買方/賣方"; + ["UiBuyTransaction"] = "買"; + ["UiClassicCheckBox"] = "顯示拍賣交易的傳統資料"; + ["UiData"] = "資料"; + ["UiDateHeader"] = "日期"; + ["UiDepositTransaction"] = "保管費"; + ["UiDone"] = "完成"; + ["UiExactNameSearch"] = "完整名稱比對"; + ["UiFailedAuctions"] = "失敗的拍賣"; + ["UiFee"] = "費用"; + ["UiMailFrameRecording"] = "BeanCounter 正在紀錄您的信件"; + ["UiMailFrameWait1"] = "請勿關閉信箱視窗或"; + ["UiMailFrameWait2"] = "拍賣物品將不會被記錄"; + ["UiNameHeader"] = "物品"; + ["UiNetHeader"] = "淨利"; + ["UiNetPerHeader"] = "單件淨利"; + ["UiOutbid"] = "超過出價"; + ["UiOutbids"] = "超過出價"; + ["UiPriceHeader"] = "價格"; + ["UiPriceper"] = "單一價格"; + ["UiPricePerHeader"] = "價格標題"; + ["UiPurchases"] = "購買"; + ["UiQuantityHeader"] = "數量"; + ["UiReason"] = "原因"; + ["UiSales"] = "銷售"; + ["UiSearch"] = "搜尋"; + ["UiSearchForLabel"] = "搜尋"; + ["UiSellTransaction"] = "出售"; + ["UiServer"] = "伺服器"; + ["UiTransactions"] = "交易"; + ["UiTransactionsLabel"] = "交易"; + ["UiTransactionTypeHeader"] = "類別"; + ["UiWealth"] = "財產"; + ["UiWononBid"] = "成功出價"; + ["UiWononBuyout"] = "成功直購"; + + }; + +} \ No newline at end of file diff --git a/BeanCounter/BeanCounterTidyUp.lua b/BeanCounter/BeanCounterTidyUp.lua new file mode 100644 index 0000000..19a24ce --- /dev/null +++ b/BeanCounter/BeanCounterTidyUp.lua @@ -0,0 +1,329 @@ +--[[ + Auctioneer Addon for World of Warcraft(tm). + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: BeanCounterTidyUp.lua 4953 2010-10-17 19:37:42Z Nechckn $ + URL: http://auctioneeraddon.com/ + + BeanCounterTidyUp - Database clean up and maintenance functions + + 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 it's designated purpose as per: + http://www.fsf.org/licensing/licenses/gpl-faq.html#InterpreterIncompat +]] +LibStub("LibRevision"):Set("$URL: http://svn.norganna.org/auctioneer/branches/5.9/BeanCounter/BeanCounterTidyUp.lua $","$Rev: 4953 $","5.1.DEV.", 'auctioneer', 'libs') + +local lib = BeanCounter +local private = lib.Private +local private, print, get, set, _BC = lib.getLocals() +local pairs,ipairs,next,select,type = pairs,ipairs,next,select,type +local strsplit = strsplit + +local function debugPrint(...) + if get("util.beancounter.debugTidyUp") then + private.debugPrint("BeanCounterTidyUp", ...) + end +end +--Sum all entries for display in window TODO:Add in check for lua key value limitations +function private.sumDatabase() + private.DBSumEntry, private.DBSumItems = 0, 0 + for player, v in pairs(private.serverData) do + for DB, data in pairs(v) do + if DB == "failedBids" or DB == "failedAuctions" or DB == "completedAuctions" or DB == "completedBidsBuyouts" or DB == "failedBidsNeutral" or DB == "failedAuctionsNeutral" or DB == "completedAuctionsNeutral" or DB == "completedBidsBuyoutsNeutralNeutral" then + for itemID, value in pairs(data) do + for itemString, data in pairs(value) do + private.DBSumEntry = private.DBSumEntry +1 + for index, text in pairs(data) do + private.DBSumItems = private.DBSumItems + 1 + end + end + end + end + end + end + private.frame.DBCount:SetText("Items: "..private.DBSumEntry) + private.frame.DBItems:SetText("Entries: "..private.DBSumItems) +end + +--Recreate/refresh ItemIName to ItemID array +function private.refreshItemIDArray(announce) + for player, v in pairs(private.serverData)do + for DB,data in pairs(private.serverData[player]) do + for itemID, value in pairs(data) do + for itemString, text in pairs(value) do + local key, suffix = lib.API.decodeLink(itemString) + if not BeanCounterDBNames[key..":"..suffix] then + local _, itemLink = private.getItemInfo(itemString, "itemid") + if itemLink then + debugPrint("Added to array, missing value", itemLink) + lib.API.storeItemLinkToArray(itemLink) + end + end + end + end + end + end + if announce then print("Finished refresing ItemName Array") end +end + +--[[ --Possible code to use to purge itemID array links that no longer have transactions +]] +local function pruneItemNameArrayHelper(itemID) + for server, serverData in pairs(BeanCounterDB) do + for player, playerData in pairs(serverData) do + for DB, data in pairs(playerData) do + if data[itemID] then + --found a match + return true + end + end + end + end +end + +function private.pruneItemNameArray() + local itemID, key + for i, link in pairs(BeanCounterDBNames) do + itemID, key = strsplit(":", i) + if not pruneItemNameArrayHelper(itemID) then + print("Never seen", itemID, link) + end + end +end + +--Moves entries older than 40 days into compressed( non uniqueID) Storage +--Removes data older than X months from the DB +--Array refresh needs to run before this function +local TIME = time()--no need to call this for every loop +function private.compactDB(announce) + debugPrint("Compressing database entries older than 40 days") + TIME = time() --sets TIME for this compact operation + for DB,data in pairs(private.playerData) do -- just do current player to make process as fast as possible + if DB == "failedBids" or DB == "failedAuctions" or DB == "completedAuctions" or DB == "completedBidsBuyouts" or DB == "failedBidsNeutral" or DB == "failedAuctionsNeutral" or DB == "completedAuctionsNeutral" or DB == "completedBidsBuyoutsNeutralNeutral" then + for itemID, value in pairs(data) do + for itemString, index in pairs(value) do + local _, suffix, uniqueID = lib.API.decodeLink(itemString) + if uniqueID ~= "0" and string.len(uniqueID) > 8 then --ignore the already compacted keys, compacted keys are uniqueID of 0 or the scaling factor for negative suffix items + private.removeUniqueID(index, DB, itemString) + elseif lib.GetSetting("oldDataExpireEnabled") then + --for non unique strings we know they are already older than the compress date, So check to see if they are old enough to be pruned by the Remove Old transactions option + local months = lib.GetSetting("monthstokeepdata") + local expire = TIME - (months * 30 * 24 * 60 * 60) + private.removeOldData(index, DB, itemString, expire) + end + --remove itemStrings that are now empty, all the keys have been moved to compressed format + if #index == 0 then debugPrint("Removed empty table:", itemString) private.playerData[DB][itemID][itemString] = nil end + end + end + end + end + if announce then print("Finished compressing Databases") end +end +function private.removeUniqueID(data, DB, itemString) + local _, _, _, _, _, _, _, postTime = private.unpackString(data[1]) + if data[1] and (TIME - postTime) >= 3456000 then --we have an old data entry lets process this + --debugPrint("Compressed", "|H"..itemString, data[1] ) + private.databaseAdd(DB, nil, itemString, data[1], true) --store using the compress option set to true + table.remove(data, 1) + private.removeUniqueID(data, DB, itemString) + end +end + +function private.removeOldData(data, DB, itemString, expire) + local _, _, _, _, _, _, _, postTime = private.unpackString(data[1]) + postTime = tonumber(postTime) + if data[1] and (postTime) <= expire then --we have an old data entry lets process this + --debugPrint("Removed", "|H"..itemString, data[1] , date("%c", postTime), "Older than", date("%c", keep) ) + table.remove(data, 1) + private.removeOldData(data, DB, itemString, expire) + end +end + +--Sort all array entries by Date oldest to newest +--Helps make compact more efficent needs to run once per week or so +function private.sortArrayByDate(announce) + for player, v in pairs(private.serverData)do + for DB, data in pairs(private.serverData[player]) do + if DB == "failedBids" or DB == "failedAuctions" or DB == "completedAuctions" or DB == "completedBidsBuyouts" or DB == "failedBidsNeutral" or DB == "failedAuctionsNeutral" or DB == "completedAuctionsNeutral" or DB == "completedBidsBuyoutsNeutralNeutral" then + for itemID, value in pairs(data) do + for itemString, index in pairs(value) do + table.sort(index, function(a,b) + local _, _, _, _, _, _, _, postTimeA = private.unpackString(a) + local _, _, _, _, _, _, _, postTimeB = private.unpackString(b) + return postTimeA < postTimeB end) + end + end + end + end + end + if announce then print("Finished sorting database") end +end +--Prune Old keys from postedXXXX tables +--First we find a itemID that needs pruning then we check all other keys for that itemID and prune. +function private.prunePostedDB(announce) + --Used to clean up post DB + debugPrint("Cleaning posted Databases") + for DB,data in pairs(private.playerData) do -- just do current player to make process as fast as possible + if DB == "postedBids" or DB == "postedAuctions" then + for itemID, value in pairs(data) do + for itemString, index in pairs(value) do + --While the entrys remain 40 days old remove entry + local _, _ ,_ ,_ ,_ ,TIME + if index[1] then + _, _ ,_ ,_ ,_ ,TIME = strsplit(";", index[1]) + end + while index[1] and (time() - TIME) >= 3456000 do + --debugPrint("Removed Old posted entry", itemString) + table.remove(index, 1) + if index[1] then + _, _ ,_ ,_ ,_ ,TIME = strsplit(";", index[1]) + end + end + -- remove empty itemString tables + if #index == 0 then + --debugPrint("Removed empty itemString table", itemID, itemString) + private.playerData[DB][itemID][itemString] = nil + end + end + --after removing the itemStrings look to see if there are itemID's that need removing + if next (value) == nil then + debugPrint("Removed empty itemID:", itemID) + private.playerData[DB][itemID] = nil + end + end + end + end + if announce then print("Finished pruning Posted Databases") end +end +--deletes all entries matching a itemLink from database for that server +function private.deleteExactItem(itemLink) + if not itemLink or not itemLink:match("^(|c%x+|H.+|h%[.+%])") then return end + for player, playerData in pairs(private.serverData) do + for DB, data in pairs(playerData) do + for itemID, itemIDData in pairs(data) do + for itemString, itemStringData in pairs(itemIDData) do + local _,_,_,_,_,_, _, suffix, uniqueID = strsplit(":", itemString) + local linkID, linkSuffix = lib.API.decodeLink(itemLink) + if linkID == itemID and suffix == linkSuffix then + debugPrint("matched", itemLink, itemString, linkSuffix, suffix) + itemIDData[itemString] = nil + end + end + end + end + end +end + +--[[INTEGRITY CHECKS +Make sure the DB format is correct removing any entries that were missed by updating. +To be run after every DB update +]]-- +local integrity = {} --table containing teh DB layout + integrity["completedBidsBuyouts"] = {"number", "number", "number", "number", "number", "number", "string", "number", "string", "string"}--10 + integrity["completedAuctions"] = {"number", "number", "number", "number", "number", "number", "string", "number", "string", "string"}--10 + integrity["failedBids"] = {"number", "number", "number", "number", "number", "number", "string", "number", "string", "string"}--10 + integrity["failedAuctions"] = {"number", "number", "number", "number", "number", "number", "string", "number", "string", "string"}--10 + integrity["postedBids"] = {"number", "number", "string", "string", "number", "number", "string" } --7 + integrity["postedAuctions"] = {"number", "number", "number", "number", "number" ,"number", "string"} --7 + + integrity["completedBidsBuyoutsNeutral"] = {"number", "number", "number", "number", "number", "number", "string", "number", "string", "string"}--10 + integrity["completedAuctionsNeutral"] = {"number", "number", "number", "number", "number", "number", "string", "number", "string", "string"}--10 + integrity["failedBidsNeutral"] = {"number", "number", "number", "number", "number", "number", "string", "number", "string", "string"}--10 + integrity["failedAuctionsNeutral"] = {"number", "number", "number", "number", "number", "number", "string", "number", "string", "string"}--10 +local integrityClean, integrityCount = true, 1 + function private.integrityCheck(complete, server) + if not server then server = private.realmName end + local tbl + debugPrint(integrityCount) + for player, v in pairs(BeanCounterDB[server])do + for DB, data in pairs(v) do + for itemID, value in pairs(data) do + for itemString, data in pairs(value) do + local _, itemStringLength = itemString:gsub(":", ":") + --check that the data is a string and table + if type(itemString) ~= "string" or type(data) ~= "table" then + BeanCounterDB[server][player][DB][itemID][itemString] = nil + debugPrint("Failed: Invalid format", DB, data, "", itemString) + integrityClean = false + elseif itemStringLength > 10 then --Bad itemstring purge + debugPrint("Failed: Invalid itemString", DB, data, "", itemString) + local _, link = GetItemInfo(itemString) --ask server for a good itemlink + local itemStringNew = lib.API.getItemString(link) --get NEW itemString from itemlink + if itemStringNew then + debugPrint(itemStringNew, "New link recived replacing") + BeanCounterDB[server][player][DB][itemID][itemStringNew] = data + BeanCounterDB[server][player][DB][itemID][itemString] = nil + else + debugPrint(itemString, "New link falied purging item") + BeanCounterDB[server][player][DB][itemID][itemString] = nil + end + integrityClean = false + elseif itemStringLength < 9 then + local itemStringNew = itemString..":80" + BeanCounterDB[server][player][DB][itemID][itemStringNew] = data + BeanCounterDB[server][player][DB][itemID][itemString] = nil + integrityClean = false + else + for index, text in pairs(data) do + tbl = {strsplit(";", text)} + --check entries for missing data points, skip any DB we havnt made a key for + if integrity[DB] then + if #integrity[DB] ~= #tbl then + debugPrint("Failed: Number of entries invalid", player, DB, #tbl, text) + table.remove(data, index) + integrityClean = false + elseif complete and private.IC(tbl, DB) then + --do a full check type() = check + debugPrint("Failed type() check", player, DB) + table.remove(data, index) + integrityClean = false + end + end + end + end + end + end + end + end + --rerun integrity 10 times or until it goes cleanly + if not integrityClean and integrityCount < 10 then + integrityCount = integrityCount + 1 + integrityClean = true + private.integrityCheck(complete, server) + else + print("BeanCounter Integrity Check Completed after:",integrityCount, "passes") + integrityClean, integrityCount = true, 1 + --set("util.beancounter.integrityCheckComplete", true) + --set("util.beancounter.integrityCheck", true) + end + +end +--look at each value and compare to the number, string, number pattern for that specific DB +function private.IC(tbl, DB, text) + for i,v in pairs(tbl) do + if v ~= "" then -- is a placeholder for string and number values, ignore + v = tonumber(v) or v + if type(v) ~= integrity[DB][i] then + return true + end + end + end + return false +end diff --git a/BeanCounter/BeanCounterUpdate.lua b/BeanCounter/BeanCounterUpdate.lua new file mode 100644 index 0000000..a944240 --- /dev/null +++ b/BeanCounter/BeanCounterUpdate.lua @@ -0,0 +1,345 @@ +--[[ + Auctioneer Addon for World of Warcraft(tm). + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: BeanCounterUpdate.lua 4933 2010-10-13 17:16:14Z Nechckn $ + URL: http://auctioneeraddon.com/ + + BeanCounterUpdate - Upgrades the Beancounter Database to latest version + + 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 it's designated purpose as per: + http://www.fsf.org/licensing/licenses/gpl-faq.html#InterpreterIncompat +]] +LibStub("LibRevision"):Set("$URL: http://svn.norganna.org/auctioneer/branches/5.9/BeanCounter/BeanCounterUpdate.lua $","$Rev: 4933 $","5.1.DEV.", 'auctioneer', 'libs') + +local libName = "BeanCounter" +local libType = "Util" +local lib = BeanCounter +local private, print, get, set, _BC = lib.getLocals() +private.update = {} + +local function debugPrint(...) + if get("util.beancounter.debugUpdate") then + private.debugPrint("BeanCounterUpdate",...) + end +end + +local performedUpdate = false +function private.UpgradeDatabaseVersion() + --Recreate the itemID array if for some reason user lacks it. Changed locations in version 3.0 database + if not BeanCounterDBNames then BeanCounterDBNames = {} private.refreshItemIDArray() end + + for server, serverData in pairs(BeanCounterDB) do + for player, playerData in pairs(serverData) do + private.startPlayerUpgrade(server, player, playerData) + end + --validate the DB for this server after all upgrades have completed + if performedUpdate then --only id we actually had to update + private.integrityCheck(true, server) + end + end +end + +function private.startPlayerUpgrade(server, player, playerData) + if playerData["version"] then + if playerData["version"] < 2.0 then --Delete and start fresh + BeanCounterDB[server][player] = nil + private.initializeDB(server, player) + performedUpdate = true + end + if playerData["version"] < 2.01 then --removes old "Wealth entry to make room for reason codes + private.update._2_01(server, player) + performedUpdate = true + end + if playerData["version"] < 2.02 then --bump version # only, the fix it implemented is merged into later updates + private.update._2_02(server, player) + performedUpdate = true + end + if playerData["version"] < 2.03 then--if not upgraded yet then upgrade + private.update._2_03(server, player) + performedUpdate = true + end + if playerData["version"] < 2.04 then --bump version # only, the fix it implemented is merged into later updates + private.update._2_04(server, player) + performedUpdate = true + end + if playerData["version"] < 2.05 then --bump version # only, the fix it implemented is merged into later updates + private.update._2_05(server, player) + performedUpdate = true + end + if playerData["version"] < 2.06 then --bump version # only 2.09 nukes the itemIDArray no need to wast time "updating" it + private.update._2_06(server, player) + performedUpdate = true + end + if playerData["version"] < 2.07 then -- removes all 0 entries from stored strings. Makes all database entries same length for easier parsing + private.update._2_07(server, player) + performedUpdate = true + end + if playerData["version"] < 2.08 then -- removes all 0 entries from stored strings. Makes all database entries same length for easier parsing + private.update._2_08(server, player) + performedUpdate = true + end + if playerData["version"] < 2.09 then -- removes all 0 entries from stored strings. Makes all database entries same length for easier parsing + private.update._2_09(server, player) + performedUpdate = true + end + if playerData["version"] < 2.10 then -- remove slash from completedBids/Buys table so its completedBidsBuys + private.update._2_10(server, player) + performedUpdate = true + end + if playerData["version"] < 2.11 then -- adds neutral AH DB + private.update._2_11(server, player) + performedUpdate = true + end + if playerData["version"] < 2.12 then -- corrects nil index bug in 2.11 upgrade + private.update._2_12(server, player) + performedUpdate = true + end + if playerData["version"] < 3 then -- Moves extra tables (settings, mail, wealth, version and name array tables into their own DB's leaving only data in the BeanCounterDB table + private.update._3_00(server, player) + performedUpdate = true + end + end + --[[Make sure to refrence the new version location for all new updates after _3_00]] + +end + +function private.update._2_01(server, player) + for DB, data in pairs(BeanCounterDB[server][player]) do + if DB == "failedBids" or DB == "failedAuctions" or DB == "completedAuctions" or DB == "completedBidsBuyouts" then + for itemID, value in pairs(data) do + for itemString, index in pairs(value) do + for i, item in pairs(index) do + local reason = item:match(".+;(.-)$") + if tonumber(reason) or reason == "" then + item = item:gsub("(.+);.-$", "%1;", 1) + BeanCounterDB[server][player][DB][itemID][itemString][i] = item + end + end + end + end + end + end + BeanCounterDB[server][player]["version"] = 2.01 +end + +function private.update._2_02(server, player) + BeanCounterDB[server][player]["version"] = 2.02 +end + +function private.update._2_03(server, player) + if BeanCounterDB[server][player]["version"] < 2.03 then + for DB, data in pairs(BeanCounterDB[server][player]) do + if DB == "failedBids" or DB == "failedAuctions" or DB == "completedAuctions" or DB == "completedBidsBuyouts" or DB == "postedAuctions" or DB == "postedBids" then + for itemID, value in pairs(data) do + local temp = {} + for itemString, index in pairs(value) do + itemString = itemString..":80" + temp[itemString] = index + end + BeanCounterDB[server][player][DB][itemID] = temp + end + end + end + end + BeanCounterDB[server][player]["version"] = 2.03 + + print("WOW version 30000 Update finished") +end + +function private.update._2_04(server, player) + BeanCounterDB[server][player]["version"] = 2.04 +end + +function private.update._2_05(server, player) + BeanCounterDB[server][player]["version"] = 2.05 +end + +function private.update._2_06(server, player) --2.09 nukes the itemIDArray no need to wast time "updating" it + BeanCounterDB[server][player]["version"] = 2.06 +end + +local function convert(DB , text) + if DB == "failedBids" then + local money, Time = strsplit(";", text) + text = private.packString("", money,"", "", "", "", "", Time, "","") + elseif DB == "failedAuctions" then + local stack, buyout, bid, deposit, Time, reason = strsplit(";", text) + text = private.packString(stack, "", deposit, "", buyout, bid, "", Time, reason, "") + elseif DB == "completedAuctions" then + local stack, money, deposit, fee, buyout, bid, buyer, Time, reason = strsplit(";", text) + text = private.packString(stack, money, deposit , fee, buyout , bid, buyer, Time, reason, "") + elseif DB == "completedBidsBuyouts" then + local stack, money, fee, buyout, bid, buyer, Time, reason = strsplit(";", text) + text = private.packString(stack, money, "" , fee, buyout , bid, buyer, Time, reason, "") + end + return text +end +function private.update._2_07(server, player) + for DB, data in pairs(BeanCounterDB[server][player]) do + if DB == "failedBids" or DB == "failedAuctions" or DB == "completedAuctions" or DB == "completedBidsBuyouts" then + for itemID, value in pairs(data) do + for itemString, data in pairs(value) do + for index, text in pairs(data) do + text = convert(DB , text) + BeanCounterDB[server][player][DB][itemID][itemString][index] = text + end + end + end + end + end + BeanCounterDB[server][player]["version"] = 2.07 +end + +--[[moves itemNameArray to not store full itemlinks but generate when needed +from "10155:1046" = "|cff1eff00|Hitem:10155:0:0:0:0:0:1046:898585428:15|h[Mercurial Greaves of the Whale]|h|r", +to "10155:1046" = "cff1eff00:Mercurial Greaves of the Whale", +reduces saved variable size and slighty increases text string matching speed even with the overhead needed to change it back to an itemlink +]] +function private.update._2_08(server, player) +--just let 2.09 do it. + BeanCounterDB[server][player]["version"] = 2.08 +end +--Storing the data using a colon caused issues with schematics so store using a ; instead. +--Easiest to just regenerate the ItemID array +function private.update._2_09(server, player) + if BeanCounterDB["ItemIDArray"] then + local _, item = next(BeanCounterDB["ItemIDArray"]) + --if not in new format then upgrade itemID array otherwise leave it alone + if item and not item:match("c........;.-") then + debugPrint("UPGRADE itemName", item) + for itemKey, itemLink in pairs(BeanCounterDB["ItemIDArray"]) do + local color, name = itemLink:match("|(.-)|.item.*%[(.+)%].*") + local data = string.join(";", color, name) + BeanCounterDB["ItemIDArray"][itemKey] = data + end + end + end + BeanCounterDB[server][player]["version"] = 2.09 +end + +--removes slash from DB name completedBidsBuyouts +function private.update._2_10(server, player) + for DB, data in pairs(BeanCounterDB[server][player]) do + if DB == "completedBids/Buyouts" then + BeanCounterDB[server][player]["completedBidsBuyouts"] = data + BeanCounterDB[server][player]["completedBids/Buyouts"] = nil + end + end + BeanCounterDB[server][player]["version"] = 2.10 +end + +--Helper function for 2.11 update +local function migrateNeutralData(server, player, key, itemID, itemString, value) + key = key.."Neutral" + if not value then return end --Possible nil values could be inserted. + if BeanCounterDB[server][player][key][itemID] then --if ltemID exists + if BeanCounterDB[server][player][key][itemID][itemString] then + table.insert(BeanCounterDB[server][player][key][itemID][itemString], value) + else + BeanCounterDB[server][player][key][itemID][itemString] = {value} + end + else + BeanCounterDB[server][player][key][itemID]={[itemString] = {value}} + end +end +--adds in databases used for neutral AH trx handling, migrates neutral AH data over to these DB +function private.update._2_11(server, player) + BeanCounterDB[server][player]["completedAuctionsNeutral"] = {} + BeanCounterDB[server][player]["failedAuctionsNeutral"] = {} + + BeanCounterDB[server][player]["completedBidsBuyoutsNeutral"] = {} + BeanCounterDB[server][player]["failedBidsNeutral"] = {} + + for DB, data in pairs(BeanCounterDB[server][player]) do + if DB == "failedBids" or DB == "failedAuctions" or DB == "completedAuctions" or DB == "completedBidsBuyouts" then + for itemID, itemIDData in pairs(data) do + for itemString, itemStringData in pairs(itemIDData) do + for i = #itemStringData, 1, -1 do + local stack, money, deposit , fee, buyout , bid, buyer, Time, reason, location = private.unpackString(itemStringData[i]) + if location and location == "N" then + print(player, server, itemString) + migrateNeutralData(server, player, DB, itemID, itemString, itemStringData[i]) --local help[er function + --itemStringData[i] = nil --This was a bad idea, left nil holes in my indexed data tables. We correct Nils in upgrade 2.12 + table.remove(itemStringData, i) + end + end + end + end + end + end + + BeanCounterDB[server][player]["version"] = 2.11 +end +--correct nil holes in the transaction tables indexes +function private.update._2_12(server, player) + for DB, data in pairs(BeanCounterDB[server][player]) do + if DB == "failedBids" or DB == "failedAuctions" or DB == "completedAuctions" or DB == "completedBidsBuyouts" then + for itemID, itemIDData in pairs(data) do + for itemString, itemStringData in pairs(itemIDData) do + for i = #itemStringData, 1, -1 do + if not itemStringData[i] then --catch Nil values in indexed tables and remove em' + table.remove(itemStringData, i) + end + end + end + end + end + end + + BeanCounterDB[server][player]["version"] = 2.12 +end +--Moves settings, name array into dedicated saved variables. leaving only transaction data in BeanCounterDB +function private.update._3_00(server, player) + --move settings table, run once + if BeanCounterDB.settings then + BeanCounterDBSettings = BeanCounterDB.settings + BeanCounterDB.settings = nil + end + --move the itemName array, run once + if BeanCounterDB.ItemIDArray then + BeanCounterDBNames = BeanCounterDB.ItemIDArray + BeanCounterDB.ItemIDArray = nil + end + --move mail, wealth, faction, version to settings table, run per toon + for DB, data in pairs(BeanCounterDB[server][player]) do + --create server, player settings seperate from Global settings + if not BeanCounterDBSettings[server] then BeanCounterDBSettings[server] = {} end + if not BeanCounterDBSettings[server][player] then BeanCounterDBSettings[server][player] = {} end + if DB == "wealth" then + BeanCounterDBSettings[server][player].wealth = BeanCounterDB[server][player].wealth + BeanCounterDB[server][player].wealth = nil + elseif DB == "mailbox" then + BeanCounterDBSettings[server][player].mailbox = BeanCounterDB[server][player].mailbox + BeanCounterDB[server][player].mailbox = nil + elseif DB == "version" then + BeanCounterDBSettings[server][player].version = BeanCounterDB[server][player].version + BeanCounterDB[server][player].version = nil + elseif DB == "faction" then + BeanCounterDBSettings[server][player].faction = BeanCounterDB[server][player].faction + BeanCounterDB[server][player].faction = nil + end + end + --new location for version info + BeanCounterDBSettings[server][player].version = 3 +end + + \ No newline at end of file diff --git a/BeanCounter/BeancounterVendor.lua b/BeanCounter/BeancounterVendor.lua new file mode 100644 index 0000000..c716c5d --- /dev/null +++ b/BeanCounter/BeancounterVendor.lua @@ -0,0 +1,91 @@ +--[[ + Auctioneer Addon for World of Warcraft(tm). + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: BeancounterVendor.lua 3583 2008-10-11 16:50:02Z Norganna $ + URL: http://auctioneeraddon.com/ + + BeanCounterVendor - Records Vendor Transactions + + 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 it's designated purpose as per: + http://www.fsf.org/licensing/licenses/gpl-faq.html#InterpreterIncompat +]] +LibStub("LibRevision"):Set("$URL: http://svn.norganna.org/auctioneer/branches/5.9/BeanCounter/BeancounterVendor.lua $","$Rev: 3583 $","5.1.DEV.", 'auctioneer', 'libs') + +local libName = "BeanCounter" +local libType = "Util" +local lib = BeanCounter +local private, print, get, set, _BC = lib.getLocals() + +local function debugPrint(...) + if get("util.beancounter.debugVendor") then + private.debugPrint("BeanCounterVendor",...) + end +end + +function private.vendorOnevent(event,...) + if (event == "MERCHANT_SHOW") then + private.merchantShow() + elseif (event == "MERCHANT_CLOSED") then + + + elseif (event == "MERCHANT_UPDATE") then + --private.merchantUpdate() + end +end +local moneyStart, LastitemSold, repairAllCost + +function private.merchantShow() +--hooksecurefunc("ShowContainerSellCursor", private.merchantUpdate) + +moneyStart = private.wealth + + if CanMerchantRepair() then + repairAllCost = GetRepairAllCost() + end +end + + function private.merchantUpdate(...) +--print("SOLD",...) + + end + + function private.merchantRepairAllItems() + if CanMerchantRepair() then + if (GetRepairAllCost() == 0) and (repairAllCost > 0) then --we repaired + --print("we repaired this amount",repairAllCost) + end + end + + end + +function private.merchantBuy(id, amount) --Hooked function + local name, _, price, quantity, _, _, _ = GetMerchantItemInfo(id) + local link = GetMerchantItemLink(id) + local itemID, _ = private.getItemInfo(link, "itemid") + + if amount then quantity = amount end --Amount only send for stacked items + + local value = private.packString(link, price, quantity, time()) + + --private.databaseAdd("vendorbuy", itemID, value) + debugPrint("Vendor buy added..",itemID, value) +end diff --git a/BeanCounter/BidMonitor.lua b/BeanCounter/BidMonitor.lua new file mode 100644 index 0000000..e9ba2ee --- /dev/null +++ b/BeanCounter/BidMonitor.lua @@ -0,0 +1,155 @@ +--[[ + Auctioneer Addon for World of Warcraft(tm). + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: BidMonitor.lua 4426 2009-08-28 01:39:55Z kandoko $ + URL: http://auctioneeraddon.com/ + + BidMonitor - Records bids posted in the Auctionhouse + + 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 it's designated purpose as per: + http://www.fsf.org/licensing/licenses/gpl-faq.html#InterpreterIncompat +]] +LibStub("LibRevision"):Set("$URL: http://svn.norganna.org/auctioneer/branches/5.9/BeanCounter/BidMonitor.lua $","$Rev: 4426 $","5.1.DEV.", 'auctioneer', 'libs') + +local libName = "BeanCounter" +local libType = "Util" +local lib = BeanCounter +local private, print, get, set, _BC = lib.getLocals() + +local function debugPrint(...) + if get("util.beancounter.debugBid") then + private.debugPrint("BidMonitor",...) + end +end + +------------------------------------------------------------------------------- +-- Called after PlaceAuctionBid() +------------------------------------------------------------------------------- +function private.postPlaceAuctionBidHook(_, _, listType, index, bid) + local name, texture, count, quality, canUse, level, minBid, minIncrement, buyoutPrice, bidAmount, highBidder, owner = GetAuctionItemInfo(listType, index) + local itemLink = GetAuctionItemLink(listType, index) + local timeLeft = GetAuctionItemTimeLeft(listType, index) + if (name and count and bid) then + private.addPendingBid(name, count, bid, owner, (bid == buyoutPrice), highBidder, timeLeft, itemLink) + end +end + +------------------------------------------------------------------------------- +-- Adds a pending bid to the queue. +------------------------------------------------------------------------------- +function private.addPendingBid(name, count, bid, owner, isBuyout, isHighBidder, timeLeft, itemLink) + -- Add a pending bid to the queue. + local pendingBid = {} + pendingBid.name = name + pendingBid.count = count + pendingBid.bid = bid + pendingBid.owner = owner + pendingBid.isBuyout = isBuyout + pendingBid.isHighBidder = isHighBidder + pendingBid.timeLeft = timeLeft + pendingBid.itemLink = itemLink + table.insert(private.PendingBids, pendingBid) + debugPrint("addPendingBid() - Added pending bid") + + -- Register for the response events if this is the first pending bid. + if (#private.PendingBids == 1) then + debugPrint("addPendingBid() - Registering for CHAT_MSG_SYSTEM and UI_ERROR_MESSAGE") + Stubby.RegisterEventHook("CHAT_MSG_SYSTEM", "BeanCounter_BidMonitor", private.onEventHookBid) + Stubby.RegisterEventHook("UI_ERROR_MESSAGE", "BeanCounter_BidMonitor", private.onEventHookBid) + end +end + +------------------------------------------------------------------------------- +-- Removes the pending bid from the queue. +------------------------------------------------------------------------------- +function private.removePendingBid() + if (#private.PendingBids > 0) then + -- Remove the first pending bid. + local bid = private.PendingBids[1] + table.remove(private.PendingBids, 1) + debugPrint("removePendingBid() - Removed pending bid") + + -- Unregister for the response events if this is the last pending bid. + if (#private.PendingBids == 0) then + debugPrint("removePendingBid() - Unregistering for CHAT_MSG_SYSTEM and UI_ERROR_MESSAGE") + Stubby.UnregisterEventHook("CHAT_MSG_SYSTEM", "BeanCounter_BidMonitor", private.onEventHookBid) + Stubby.UnregisterEventHook("UI_ERROR_MESSAGE", "BeanCounter_BidMonitor", private.onEventHookBid) + end + + return bid + end + + -- No pending bid to remove! + return nil +end + +------------------------------------------------------------------------------- +-- OnEvent handler BIDS. these are unhooked when not needed +------------------------------------------------------------------------------- +function private.onEventHookBid(_, event, arg1) + if (event == "CHAT_MSG_SYSTEM" and arg1) then + if (arg1 == ERR_AUCTION_BID_PLACED) then + private.onBidAccepted() + end + elseif (event == "UI_ERROR_MESSAGE" and arg1) then + if (arg1) then debugPrint(" "..arg1) end + if (arg1 == ERR_ITEM_NOT_FOUND or + arg1 == ERR_NOT_ENOUGH_MONEY or + arg1 == ERR_AUCTION_BID_OWN or + arg1 == ERR_AUCTION_HIGHER_BID or + arg1 == ERR_ITEM_MAX_COUNT) then + private.onBidFailed() + end + end +end + +------------------------------------------------------------------------------- +-- Called when a bid is accepted by the server. +------------------------------------------------------------------------------- +function private.onBidAccepted() + local bid = private.removePendingBid() + if (bid) then + + local itemID = lib.API.decodeLink(bid.itemLink) + local text = private.packString(bid.count, bid.bid, bid.owner, bid.isBuyout, bid.timeLeft, time(), "") + debugPrint(bid.isBuyout, bid.isHighBidder) + --we use the bid/buy data for storing "BTM/SearchUI reasons" and outbid data + if (bid.isBuyout) then + if bid.isHighBidder then-- If the player is buying out an auction they already bid on, we need to remove the pending bid + debugPrint('private.databaseRemove(',"postedBids", itemID, bid.name, bid.owner, bid.bid) + private.databaseRemove("postedBids", itemID, bid.itemLink, bid.owner, bid.count) --remove old entry + end + debugPrint('private.databaseAdd(pendingBids', itemID, text) + private.databaseAdd("postedBids", bid.itemLink, nil, text) --replace with buyout entry. + else + debugPrint('private.databaseAdd(pendingBids', itemID, text) + private.databaseAdd("postedBids", bid.itemLink, nil, text) + end + end +end + +------------------------------------------------------------------------------- +-- Called when a bid is rejected by the server. +------------------------------------------------------------------------------- +function private.onBidFailed() + private.removePendingBid() +end diff --git a/BeanCounter/Embed.xml b/BeanCounter/Embed.xml new file mode 100644 index 0000000..3996b18 --- /dev/null +++ b/BeanCounter/Embed.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + EnchantrixBarker_OptionsSlider_OnValueChanged(self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Enchantrix_BarkerOptions_OnShow(); + PanelTemplates_SetNumTabs(Enchantrix_BarkerOptions_Frame, 4); + Enchantrix_BarkerOptions_Frame.selectedTab=1; + PanelTemplates_UpdateTabs(Enchantrix_BarkerOptions_Frame); + + + + + + + + EnchantrixBarker_OnLoad(); + self:RegisterEvent("CRAFT_SHOW") + self:RegisterEvent("CRAFT_CLOSE") + self:RegisterEvent("TRADE_SKILL_SHOW") + self:RegisterEvent("TRADE_SKILL_CLOSE") + + + EnchantrixBarker_OnEvent(event,...); + + + + + diff --git a/Enchantrix-Barker/Libs/Babylonian/Babylonian.lua b/Enchantrix-Barker/Libs/Babylonian/Babylonian.lua new file mode 100644 index 0000000..4af30f0 --- /dev/null +++ b/Enchantrix-Barker/Libs/Babylonian/Babylonian.lua @@ -0,0 +1,168 @@ +--[[ + Babylonian - A sub-addon that manages the locales for other addons. + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: Babylonian.lua 130 2008-10-11 12:38:07Z Norganna $ + URL: http://auctioneeraddon.com/dl/ + + 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 +]] + +local LIBRARY_VERSION_MAJOR = "Babylonian" +local LIBRARY_VERSION_MINOR = 2 + + +--[[----------------------------------------------------------------- + +LibStub is a simple versioning stub meant for use in Libraries. +See for more info. +LibStub is hereby placed in the Public Domain. +Credits: + Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke + +--]]----------------------------------------------------------------- +do + local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 + local LibStub = _G[LIBSTUB_MAJOR] + + if not LibStub or LibStub.minor < LIBSTUB_MINOR then + LibStub = LibStub or {libs = {}, minors = {} } + _G[LIBSTUB_MAJOR] = LibStub + LibStub.minor = LIBSTUB_MINOR + + function LibStub:NewLibrary(major, minor) + assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)") + minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.") + + local oldminor = self.minors[major] + if oldminor and oldminor >= minor then return nil end + self.minors[major], self.libs[major] = minor, self.libs[major] or {} + return self.libs[major], oldminor + end + + function LibStub:GetLibrary(major, silent) + if not self.libs[major] and not silent then + error(("Cannot find a library instance of %q."):format(tostring(major)), 2) + end + return self.libs[major], self.minors[major] + end + + function LibStub:IterateLibraries() return pairs(self.libs) end + setmetatable(LibStub, { __call = LibStub.GetLibrary }) + end +end +--[End of LibStub]--------------------------------------------------- + +local lib = LibStub:NewLibrary(LIBRARY_VERSION_MAJOR, LIBRARY_VERSION_MINOR) +if not lib then return end + +LibStub("LibRevision"):Set("$URL: http://svn.norganna.org/libs/trunk/Babylonian/Babylonian.lua $","$Rev: 130 $","5.1.DEV.", 'auctioneer', 'libs') + +if not lib.private then + lib.private = {} +end +local private = lib.private +local tinsert = table.insert + +function lib:SetOrder(order) + if (not order) then + private.order = {} + else + private.order = { strsplit(",", order) } + end + + tinsert(private.order, GetLocale()) + tinsert(private.order, "enUS") + + local curOrder = SetCVar("BabylonianOrder", order) + + if lib.notifyList then + for _, func in pairs(lib.notifyList) do + func() + end + end + + return curOrder +end + +function lib:GetOrder() + return GetCVar("BabylonianOrder") +end + +function lib:FetchString(stringTable, locale, stringKey) + if ((type(stringTable) == "table") and (type(stringTable[locale]) == "table") and (stringTable[locale][stringKey])) then + return stringTable[locale][stringKey] + end +end + +function lib:GetString(stringTable, stringKey, default) + local val + for i = 1, #private.order do + val = lib:FetchString(stringTable, private.order[i], stringKey) + if (val) then + return val + end + end + return default +end + +lib.notifyList = {} +function lib:AddNotify(func) + table.insert(lib.notifyList, func) +end + +local kit = { + GetOrder = lib.GetOrder, + SetOrder = lib.SetOrder, +} + +function kit:GetString(stringKey, default) + return (lib:GetString(self.stringTable, stringKey, default)) +end + +function kit:FetchString(locale, stringKey) + return (lib:FetchString(self.stringTable, locale, stringKey)) +end + +local function noKey(t, key) + error("Localization for "..tostring(key).." is unavailable") +end + +function lib:New(stringTable) + assert(stringTable, "Usage: Babylonian(stringTable) -- Must supply a stringTable") + assert(type(stringTable)=="table", "Usage: Babylonian(stringTable) -- Supplied stringTable must be a table") + + local new = { + stringTable = stringTable + } + for k,v in pairs(kit) do + new[k] = v + end + setmetatable(new, { __call = new.FetchString, __index = new.GetString, __newindex = noKey }) + return new +end +setmetatable(lib, { __call = lib.New }) + +if not private.order then + RegisterCVar("BabylonianOrder", "") + lib:SetOrder(lib:GetOrder()) +end diff --git a/Enchantrix-Barker/Libs/Babylonian/Babylonian.toc b/Enchantrix-Barker/Libs/Babylonian/Babylonian.toc new file mode 100644 index 0000000..4349819 --- /dev/null +++ b/Enchantrix-Barker/Libs/Babylonian/Babylonian.toc @@ -0,0 +1,12 @@ +## Title: Babylonian |cff224477(lib) +## Notes: Provides the facilites to support languages other than those supported by the client locale. +## +## Interface: 40000 +## LoadOnDemand: 0 +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Babylonian.toc 280 2010-10-12 19:52:29Z Esamynn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## +Load.xml diff --git a/Enchantrix-Barker/Libs/Babylonian/Load.xml b/Enchantrix-Barker/Libs/Babylonian/Load.xml new file mode 100644 index 0000000..9affb07 --- /dev/null +++ b/Enchantrix-Barker/Libs/Babylonian/Load.xml @@ -0,0 +1,4 @@ + + + + + + + + + + + + + + + + + + + Informant.FrameLoaded() + + + Informant.FrameActive(true); + + + Informant.FrameActive(false); + + + Informant.OnEvent(event, ...); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Informant/Libs/Babylonian/Babylonian.lua b/Informant/Libs/Babylonian/Babylonian.lua new file mode 100644 index 0000000..4af30f0 --- /dev/null +++ b/Informant/Libs/Babylonian/Babylonian.lua @@ -0,0 +1,168 @@ +--[[ + Babylonian - A sub-addon that manages the locales for other addons. + Version: 5.9.4961 (WhackyWallaby) + Revision: $Id: Babylonian.lua 130 2008-10-11 12:38:07Z Norganna $ + URL: http://auctioneeraddon.com/dl/ + + 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 +]] + +local LIBRARY_VERSION_MAJOR = "Babylonian" +local LIBRARY_VERSION_MINOR = 2 + + +--[[----------------------------------------------------------------- + +LibStub is a simple versioning stub meant for use in Libraries. +See for more info. +LibStub is hereby placed in the Public Domain. +Credits: + Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke + +--]]----------------------------------------------------------------- +do + local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 + local LibStub = _G[LIBSTUB_MAJOR] + + if not LibStub or LibStub.minor < LIBSTUB_MINOR then + LibStub = LibStub or {libs = {}, minors = {} } + _G[LIBSTUB_MAJOR] = LibStub + LibStub.minor = LIBSTUB_MINOR + + function LibStub:NewLibrary(major, minor) + assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)") + minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.") + + local oldminor = self.minors[major] + if oldminor and oldminor >= minor then return nil end + self.minors[major], self.libs[major] = minor, self.libs[major] or {} + return self.libs[major], oldminor + end + + function LibStub:GetLibrary(major, silent) + if not self.libs[major] and not silent then + error(("Cannot find a library instance of %q."):format(tostring(major)), 2) + end + return self.libs[major], self.minors[major] + end + + function LibStub:IterateLibraries() return pairs(self.libs) end + setmetatable(LibStub, { __call = LibStub.GetLibrary }) + end +end +--[End of LibStub]--------------------------------------------------- + +local lib = LibStub:NewLibrary(LIBRARY_VERSION_MAJOR, LIBRARY_VERSION_MINOR) +if not lib then return end + +LibStub("LibRevision"):Set("$URL: http://svn.norganna.org/libs/trunk/Babylonian/Babylonian.lua $","$Rev: 130 $","5.1.DEV.", 'auctioneer', 'libs') + +if not lib.private then + lib.private = {} +end +local private = lib.private +local tinsert = table.insert + +function lib:SetOrder(order) + if (not order) then + private.order = {} + else + private.order = { strsplit(",", order) } + end + + tinsert(private.order, GetLocale()) + tinsert(private.order, "enUS") + + local curOrder = SetCVar("BabylonianOrder", order) + + if lib.notifyList then + for _, func in pairs(lib.notifyList) do + func() + end + end + + return curOrder +end + +function lib:GetOrder() + return GetCVar("BabylonianOrder") +end + +function lib:FetchString(stringTable, locale, stringKey) + if ((type(stringTable) == "table") and (type(stringTable[locale]) == "table") and (stringTable[locale][stringKey])) then + return stringTable[locale][stringKey] + end +end + +function lib:GetString(stringTable, stringKey, default) + local val + for i = 1, #private.order do + val = lib:FetchString(stringTable, private.order[i], stringKey) + if (val) then + return val + end + end + return default +end + +lib.notifyList = {} +function lib:AddNotify(func) + table.insert(lib.notifyList, func) +end + +local kit = { + GetOrder = lib.GetOrder, + SetOrder = lib.SetOrder, +} + +function kit:GetString(stringKey, default) + return (lib:GetString(self.stringTable, stringKey, default)) +end + +function kit:FetchString(locale, stringKey) + return (lib:FetchString(self.stringTable, locale, stringKey)) +end + +local function noKey(t, key) + error("Localization for "..tostring(key).." is unavailable") +end + +function lib:New(stringTable) + assert(stringTable, "Usage: Babylonian(stringTable) -- Must supply a stringTable") + assert(type(stringTable)=="table", "Usage: Babylonian(stringTable) -- Supplied stringTable must be a table") + + local new = { + stringTable = stringTable + } + for k,v in pairs(kit) do + new[k] = v + end + setmetatable(new, { __call = new.FetchString, __index = new.GetString, __newindex = noKey }) + return new +end +setmetatable(lib, { __call = lib.New }) + +if not private.order then + RegisterCVar("BabylonianOrder", "") + lib:SetOrder(lib:GetOrder()) +end diff --git a/Informant/Libs/Babylonian/Babylonian.toc b/Informant/Libs/Babylonian/Babylonian.toc new file mode 100644 index 0000000..4349819 --- /dev/null +++ b/Informant/Libs/Babylonian/Babylonian.toc @@ -0,0 +1,12 @@ +## Title: Babylonian |cff224477(lib) +## Notes: Provides the facilites to support languages other than those supported by the client locale. +## +## Interface: 40000 +## LoadOnDemand: 0 +## +## Version: 5.9.4961 (WhackyWallaby) +## Revision: $Id: Babylonian.toc 280 2010-10-12 19:52:29Z Esamynn $ +## URL: http://auctioneeraddon.com/ +## Author: Norganna's AddOns +## +Load.xml diff --git a/Informant/Libs/Babylonian/Load.xml b/Informant/Libs/Babylonian/Load.xml new file mode 100644 index 0000000..9affb07 --- /dev/null +++ b/Informant/Libs/Babylonian/Load.xml @@ -0,0 +1,4 @@ + + + + + + + + + + self:RegisterEvent("ADDON_LOADED"); + + + Stubby.Events(event, ...) + + + + diff --git a/Stubby/Support/LibRevision.lua b/Stubby/Support/LibRevision.lua new file mode 100644 index 0000000..935ee69 --- /dev/null +++ b/Stubby/Support/LibRevision.lua @@ -0,0 +1,126 @@ +--[[ +-- LibRevision.lua +-- Finds the revisions of a given addon and works out the version. +-- Written by Ken Allan +-- This code is hereby released into the Public Domain without warranty. +-- +-- Usage: +-- local libRevision = LibStub("LibRevision") +-- +-- libRevision:Set("svnUrl", "svnRev", "5.1.DEV.", ...) +-- libRevision:Get("addon") +--]] + +local LIBRARY_VERSION_MAJOR = "LibRevision" +local LIBRARY_VERSION_MINOR = 1 + +--[[----------------------------------------------------------------- + +LibStub is a simple versioning stub meant for use in Libraries. +See for more info. +LibStub is hereby placed in the Public Domain. +Credits: + Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke + +--]]----------------------------------------------------------------- +do + local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 + local LibStub = _G[LIBSTUB_MAJOR] + + if not LibStub or LibStub.minor < LIBSTUB_MINOR then + LibStub = LibStub or {libs = {}, minors = {} } + _G[LIBSTUB_MAJOR] = LibStub + LibStub.minor = LIBSTUB_MINOR + + function LibStub:NewLibrary(major, minor) + assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)") + minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.") + + local oldminor = self.minors[major] + if oldminor and oldminor >= minor then return nil end + self.minors[major], self.libs[major] = minor, self.libs[major] or {} + return self.libs[major], oldminor + end + + function LibStub:GetLibrary(major, silent) + if not self.libs[major] and not silent then + error(("Cannot find a library instance of %q."):format(tostring(major)), 2) + end + return self.libs[major], self.minors[major] + end + + function LibStub:IterateLibraries() return pairs(self.libs) end + setmetatable(LibStub, { __call = LibStub.GetLibrary }) + end +end +--[End of LibStub]--------------------------------------------------- + +local lib = LibStub:NewLibrary(LIBRARY_VERSION_MAJOR, LIBRARY_VERSION_MINOR) +if not lib then return end + +if not lib.versions then + lib.versions = {} +end + +function lib:Get(addon) + return lib.versions[addon:lower()] +end + +function lib:Set(url, revision, dev, ...) + local repo,file + if (not url and revision) then return end + + local n = select("#", ...) + for i=1, n do + local sub = select(i, ...) + repo, file = url:match("%$URL: .*/("..sub..")/([^%$]+) %$") + if repo then break end + end + local rev = tonumber(revision:match("(%d+)")) or 0 + + if repo then + local branch,name = file:match("^(trunk)/([^/]+)/") + if not branch then + branch,name = file:match("^branches/([^/]+)/([^/]+)/") + end + + if branch then + local embed, addit = false, branch + local addon = name:lower() + + if not lib.versions[addon] then + lib.versions[addon] = {} + end + + local vaddon = lib.versions[addon] + local vrev = max(vaddon['x-revision'] or 0, rev) + vaddon['x-revision'] = vrev + + if not IsAddOnLoaded(addon) then + embed = true + addit = addit.."/embedded" + end + + local ver = GetAddOnMetadata(addon, "Version") + if not ver or ver:sub(0,2) == "<%" then ver = dev..rev end + + if not vaddon["x-revisions"] then + vaddon["x-revisions"] = {} + end + + vaddon.name = name + vaddon.version = ver + vaddon["x-branch"] = branch + vaddon["x-revision"] = vrev + vaddon["x-revisions"][file] = rev + vaddon["x-embedded"] = embed + vaddon["x-swatter-extra"] = addition + + if SetAddOnDetail then + SetAddOnDetail(name, vaddon) + end + + return vaddon, file, rev + end + end +end diff --git a/Stubby/Support/Load.xml b/Stubby/Support/Load.xml new file mode 100644 index 0000000..d773ecd --- /dev/null +++ b/Stubby/Support/Load.xml @@ -0,0 +1,4 @@ + +