init
This commit is contained in:
@@ -0,0 +1,415 @@
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
-- TradeSkillMaster --
|
||||
-- http://www.curse.com/addons/wow/tradeskill-master --
|
||||
-- --
|
||||
-- A TradeSkillMaster Addon (http://tradeskillmaster.com) --
|
||||
-- All Rights Reserved* - Detailed license information included with addon. --
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
|
||||
-- TSM's error handler.
|
||||
|
||||
local TSM = select(2, ...)
|
||||
local AceGUI = LibStub("AceGUI-3.0")
|
||||
local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster")
|
||||
|
||||
|
||||
local origErrorHandler, ignoreErrors, isErrorFrameVisible, isAssert
|
||||
TSMERRORLOG = {}
|
||||
local tsmStack = {}
|
||||
local stackNameLookup = {}
|
||||
|
||||
local addonSuites = {
|
||||
{name="ArkInventory"},
|
||||
{name="AtlasLoot"},
|
||||
{name="Altoholic"},
|
||||
{name="Auc-Advanced", commonTerm="Auc-"},
|
||||
{name="Bagnon"},
|
||||
{name="BigWigs"},
|
||||
{name="Broker"},
|
||||
{name="ButtonFacade"},
|
||||
{name="Carbonite"},
|
||||
{name="DataStore"},
|
||||
{name="DBM"},
|
||||
{name="Dominos"},
|
||||
{name="DXE"},
|
||||
{name="EveryQuest"},
|
||||
{name="Forte"},
|
||||
{name="FuBar"},
|
||||
{name="GatherMate2"},
|
||||
{name="Grid"},
|
||||
{name="LightHeaded"},
|
||||
{name="LittleWigs"},
|
||||
{name="Masque"},
|
||||
{name="MogIt"},
|
||||
{name="Odyssey"},
|
||||
{name="Overachiever"},
|
||||
{name="PitBull4"},
|
||||
{name="Prat-3.0"},
|
||||
{name="RaidAchievement"},
|
||||
{name="Skada"},
|
||||
{name="SpellFlash"},
|
||||
{name="TidyPlates"},
|
||||
{name="TipTac"},
|
||||
{name="Titan"},
|
||||
{name="UnderHood"},
|
||||
{name="WowPro"},
|
||||
{name="ZOMGBuffs"},
|
||||
}
|
||||
|
||||
local function StrStartCmp(str, startStr)
|
||||
local startLen = strlen(startStr)
|
||||
|
||||
if startLen <= strlen(str) then
|
||||
return strsub(str, 1, startLen) == startStr
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function GetModule(msg)
|
||||
if strfind(msg, "TradeSkillMaster_") then
|
||||
return strmatch(msg, "TradeSkillMaster_[A-Za-z]+")
|
||||
elseif strfind(msg, "TradeSkillMaster\\") then
|
||||
return "TradeSkillMaster"
|
||||
end
|
||||
return "?"
|
||||
end
|
||||
|
||||
local function ExtractErrorMessage(...)
|
||||
local msg = ""
|
||||
|
||||
for _, var in ipairs({...}) do
|
||||
local varStr
|
||||
local varType = type(var)
|
||||
|
||||
if varType == "boolean" then
|
||||
varStr = var and "true" or "false"
|
||||
elseif varType == "table" then
|
||||
varStr = "<table>"
|
||||
elseif varType == "function" then
|
||||
varStr = "<function>"
|
||||
elseif var == nil then
|
||||
varStr = "<nil>"
|
||||
else
|
||||
varStr = var
|
||||
end
|
||||
|
||||
msg = msg.." "..varStr
|
||||
end
|
||||
|
||||
return msg
|
||||
end
|
||||
|
||||
local function GetDebugStack()
|
||||
local stackInfo = {}
|
||||
local stackString = ""
|
||||
local stack = debugstack(2) or debugstack(1)
|
||||
|
||||
if type(stack) == "string" then
|
||||
local lines = {("\n"):split(stack)}
|
||||
for _, line in ipairs(lines) do
|
||||
local strStart = strfind(line, "in function")
|
||||
if strStart and not strfind(line, "ErrorHandler.lua") then
|
||||
line = gsub(line, "`", "<", 1)
|
||||
line = gsub(line, "'", ">", 1)
|
||||
local inFunction = strmatch(line, "<[^>]*>", strStart)
|
||||
if inFunction then
|
||||
inFunction = gsub(gsub(inFunction, ".*\\", ""), "<", "")
|
||||
if inFunction ~= "" then
|
||||
local str = strsub(line, 1, strStart-2)
|
||||
str = strsub(str, strfind(str, "TradeSkillMaster") or 1)
|
||||
if strfind(inFunction, "`") then
|
||||
inFunction = strsub(inFunction, 2, -2)..">"
|
||||
end
|
||||
str = gsub(str, "TradeSkillMaster", "TSM")
|
||||
tinsert(stackInfo, str.." <"..inFunction)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return table.concat(stackInfo, "\n")
|
||||
end
|
||||
|
||||
local function GetTSMStack()
|
||||
local stackInfo = {}
|
||||
local index = #tsmStack
|
||||
for i=1, 10 do -- only show up to 10 lines
|
||||
if not tsmStack[index] then break end
|
||||
tinsert(stackInfo, tsmStack[index])
|
||||
index = index - 1
|
||||
end
|
||||
return table.concat(stackInfo, "\n")
|
||||
end
|
||||
|
||||
local function GetEventLog()
|
||||
local eventInfo = {}
|
||||
local eventLog = TSM:GetEventLog()
|
||||
for i, entry in ipairs(eventLog) do
|
||||
tinsert(eventInfo, format("%d | %s | %s", i, entry.event, tostring(entry.arg)))
|
||||
end
|
||||
return table.concat(eventInfo, "\n")
|
||||
end
|
||||
|
||||
local function GetAddonList()
|
||||
local hasAddonSuite = {}
|
||||
local addons = {}
|
||||
local addonString = ""
|
||||
|
||||
for i = 1, GetNumAddOns() do
|
||||
local name, _, _, enabled = GetAddOnInfo(i)
|
||||
local version = GetAddOnMetadata(name, "X-Curse-Packaged-Version") or GetAddOnMetadata(name, "Version") or ""
|
||||
if enabled then
|
||||
local isSuite
|
||||
|
||||
for _, addonSuite in ipairs(addonSuites) do
|
||||
local commonTerm = addonSuite.commonTerm or addonSuite.name
|
||||
|
||||
if StrStartCmp(name, commonTerm) then
|
||||
isSuite = commonTerm
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if isSuite then
|
||||
if not hasAddonSuite[isSuite] then
|
||||
tinsert(addons, {name=name, version=version})
|
||||
hasAddonSuite[isSuite] = true
|
||||
end
|
||||
elseif StrStartCmp(name, "TradeSkillMaster") then
|
||||
tinsert(addons, {name=gsub(name, "TradeSkillMaster", "TSM"), version=version})
|
||||
else
|
||||
tinsert(addons, {name=name, version=version})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for i, addonInfo in ipairs(addons) do
|
||||
local info = addonInfo.name .. " (" .. addonInfo.version .. ")"
|
||||
if i == #addons then
|
||||
addonString = addonString .. " " .. info
|
||||
else
|
||||
addonString = addonString .. " " .. info .. "\n"
|
||||
end
|
||||
end
|
||||
|
||||
return addonString
|
||||
end
|
||||
|
||||
local function ShowError(msg, isVerify)
|
||||
if not AceGUI then
|
||||
TSMAPI:CreateTimeDelay("errHandlerShowDelay", 0.1, function()
|
||||
if AceGUI and UIParent then
|
||||
CancelFrame("errHandlerShowDelay")
|
||||
ShowError(msg, isVerify)
|
||||
end
|
||||
end, 0.1)
|
||||
return
|
||||
end
|
||||
|
||||
local f = AceGUI:Create("TSMWindow")
|
||||
f:SetCallback("OnClose", function(self) isErrorFrameVisible = false AceGUI:Release(self) end)
|
||||
f:SetTitle(L["TradeSkillMaster Error Window"])
|
||||
f:SetLayout("Flow")
|
||||
f:SetWidth(500)
|
||||
f:SetHeight(400)
|
||||
|
||||
local l = AceGUI:Create("Label")
|
||||
l:SetFullWidth(true)
|
||||
l:SetFontObject(GameFontNormal)
|
||||
if isVerify then
|
||||
l:SetText(L["Looks like TradeSkillMaster has detected an error with your configuration. Please address this in order to ensure TSM remains functional."].."\n"..L["|cffffff00DO NOT report this as an error to the developers.|r If you require assistance with this, make a post on the TSM forums instead."].."|r")
|
||||
else
|
||||
l:SetText(L["Looks like TradeSkillMaster has encountered an error. Please help the author fix this error by copying the entire error below and following the instructions for reporting bugs listed here (unless told elsewhere by the author):"].." |cffffff00http://tradeskillmaster.com/wiki|r")
|
||||
end
|
||||
f:AddChild(l)
|
||||
|
||||
local heading = AceGUI:Create("Heading")
|
||||
heading:SetText("")
|
||||
heading:SetFullWidth(true)
|
||||
f:AddChild(heading)
|
||||
|
||||
local eb = AceGUI:Create("MultiLineEditBox")
|
||||
eb:SetLabel(L["Error Info:"])
|
||||
eb:SetMaxLetters(0)
|
||||
eb:SetFullWidth(true)
|
||||
eb:SetText(msg)
|
||||
eb:DisableButton(true)
|
||||
eb:SetFullHeight(true)
|
||||
f:AddChild(eb)
|
||||
|
||||
f.frame:SetFrameStrata("FULLSCREEN_DIALOG")
|
||||
f.frame:SetFrameLevel(100)
|
||||
isErrorFrameVisible = true
|
||||
end
|
||||
|
||||
function TSM:IsValidError(...)
|
||||
if ignoreErrors then return end
|
||||
ignoreErrors = true
|
||||
local msg = ExtractErrorMessage(...)
|
||||
ignoreErrors = false
|
||||
if not strfind(msg, "TradeSkillMaster") then return end
|
||||
if strfind(msg, "auc%-stat%-wowuction") then return end
|
||||
return msg
|
||||
end
|
||||
|
||||
function TSMAPI:Verify(cond, err)
|
||||
if cond then return end
|
||||
|
||||
ignoreErrors = true
|
||||
|
||||
tinsert(TSMERRORLOG, err)
|
||||
if not isErrorFrameVisible then
|
||||
TSM:Print(L["Looks like TradeSkillMaster has detected an error with your configuration. Please address this in order to ensure TSM remains functional."])
|
||||
ShowError(err, true)
|
||||
elseif isErrorFrameVisible == true then
|
||||
TSM:Print(L["Additional error suppressed"])
|
||||
isErrorFrameVisible = 1
|
||||
end
|
||||
|
||||
ignoreErrors = false
|
||||
end
|
||||
|
||||
local function TSMErrorHandler(msg)
|
||||
-- ignore errors while we are handling this error
|
||||
ignoreErrors = true
|
||||
TSMERRORTEMP = msg
|
||||
|
||||
local color = TSMAPI.Design and TSMAPI.Design:GetInlineColor("link2") or ""
|
||||
local color2 = TSMAPI.Design and TSMAPI.Design:GetInlineColor("advanced") or ""
|
||||
local errorMessage = ""
|
||||
errorMessage = errorMessage..color.."Addon:|r "..color2..GetModule(msg).."|r\n"
|
||||
errorMessage = errorMessage..color.."Message:|r "..msg.."\n"
|
||||
errorMessage = errorMessage..color.."Date:|r "..date("%m/%d/%y %H:%M:%S").."\n"
|
||||
errorMessage = errorMessage..color.."Client:|r "..GetBuildInfo().."\n"
|
||||
errorMessage = errorMessage..color.."Locale:|r "..GetLocale().."\n"
|
||||
errorMessage = errorMessage..color.."Stack:|r\n"..GetDebugStack().."\n"
|
||||
errorMessage = errorMessage..color.."TSM Stack:|r\n"..GetTSMStack().."\n"
|
||||
errorMessage = errorMessage..color.."Local Variables:|r\n"..(debuglocals(isAssert and 5 or 4) or "").."\n"
|
||||
errorMessage = errorMessage..color.."TSM Event Log:|r\n"..GetEventLog().."\n"
|
||||
errorMessage = errorMessage..color.."Addons:|r\n"..GetAddonList().."\n"
|
||||
tinsert(TSMERRORLOG, errorMessage)
|
||||
if not isErrorFrameVisible then
|
||||
TSM:Print(L["Looks like TradeSkillMaster has encountered an error. Please help the author fix this error by following the instructions shown."])
|
||||
ShowError(errorMessage)
|
||||
elseif isErrorFrameVisible == true then
|
||||
TSM:Print(L["Additional error suppressed"])
|
||||
isErrorFrameVisible = 1
|
||||
end
|
||||
|
||||
-- need to clear the stack
|
||||
tsmStack = {}
|
||||
ignoreErrors = false
|
||||
end
|
||||
|
||||
function TSMAPI:Assert(cond, err)
|
||||
if cond then return end
|
||||
isAssert = true
|
||||
TSMErrorHandler(err)
|
||||
isAssert = false
|
||||
end
|
||||
|
||||
do
|
||||
origErrorHandler = geterrorhandler()
|
||||
local errHandlerFrame = CreateFrame("Frame", nil, nil, "TSMErrorHandlerTemplate")
|
||||
errHandlerFrame.errorHandler = TSMErrorHandler
|
||||
errHandlerFrame.origErrorHandler = origErrorHandler
|
||||
seterrorhandler(errHandlerFrame.handler)
|
||||
end
|
||||
|
||||
--[===[@debug@
|
||||
--- Disables TSM's error handler until the game is reloaded.
|
||||
-- This is mainly used for debugging errors with TSM's error handler and should not be used in actual code.
|
||||
function TSMAPI:DisableErrorHandler()
|
||||
seterrorhandler(origErrorHandler)
|
||||
end
|
||||
--@end-debug@]===]
|
||||
|
||||
|
||||
|
||||
-- other debug functions
|
||||
TSMAPI.Debug = {}
|
||||
|
||||
local dumpDefaults = {
|
||||
DEVTOOLS_MAX_ENTRY_CUTOFF = 30, -- Maximum table entries shown
|
||||
DEVTOOLS_LONG_STRING_CUTOFF = 200, -- Maximum string size shown
|
||||
DEVTOOLS_DEPTH_CUTOFF = 10, -- Maximum table depth
|
||||
}
|
||||
|
||||
function TSMAPI.Debug:DumpTable(tbl, maxDepth, maxItems, maxStr)
|
||||
DEVTOOLS_DEPTH_CUTOFF = maxDepth or dumpDefaults.DEVTOOLS_DEPTH_CUTOFF
|
||||
DEVTOOLS_MAX_ENTRY_CUTOFF = maxItems or dumpDefaults.DEVTOOLS_MAX_ENTRY_CUTOFF
|
||||
DEVTOOLS_DEPTH_CUTOFF = maxStr or dumpDefaults.DEVTOOLS_DEPTH_CUTOFF
|
||||
|
||||
if not IsAddOnLoaded("Blizzard_DebugTools") then
|
||||
LoadAddOn("Blizzard_DebugTools")
|
||||
end
|
||||
|
||||
DevTools_Dump(tbl)
|
||||
|
||||
for i, v in pairs(dumpDefaults) do
|
||||
_G[i] = v
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- stack tracing functions
|
||||
local function FormatTSMStack(obj, name, ...)
|
||||
local args
|
||||
for i=2, select('#', ...) do
|
||||
local arg = select(i, ...)
|
||||
local str
|
||||
if stackNameLookup[arg] then
|
||||
str = "<"..stackNameLookup[arg]..">"
|
||||
elseif type(arg) == "table" then
|
||||
if getmetatable(arg) and getmetatable(arg).__tostring then
|
||||
str = "<"..tostring(arg)..">"
|
||||
else
|
||||
local _, addr = (":"):split(tostring(arg))
|
||||
str = "table:"..tonumber(addr, 16)
|
||||
end
|
||||
elseif type(arg) == "string" then
|
||||
str = '"'..tostring(arg)..'"'
|
||||
elseif type(arg) == "function" then
|
||||
local _, addr = (":"):split(tostring(arg))
|
||||
str = "function:"..tonumber(addr, 16)
|
||||
else
|
||||
str = tostring(arg)
|
||||
end
|
||||
|
||||
if args then
|
||||
args = args..", "..str
|
||||
else
|
||||
args = str
|
||||
end
|
||||
end
|
||||
|
||||
local funcCall = "?"
|
||||
if obj == select(1, ...) and args then
|
||||
funcCall = (stackNameLookup[obj] or tostring(obj))..":"..name.."("..args..")"
|
||||
end
|
||||
return funcCall
|
||||
end
|
||||
|
||||
-- this must be a separate function so we can return the ... after popping off the stack
|
||||
local function TrackPopStack(...)
|
||||
tremove(tsmStack, #tsmStack)
|
||||
return ...
|
||||
end
|
||||
|
||||
local function RegisterForTracing(obj, name)
|
||||
stackNameLookup[obj] = name
|
||||
for name, v in pairs(obj) do
|
||||
if type(v) == "function" then
|
||||
TSM:RawHook(obj, name, function(...)
|
||||
tinsert(tsmStack, FormatTSMStack(obj, name, ...))
|
||||
return TrackPopStack(v(...))
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function TSMAPI:RegisterForTracing(obj, name)
|
||||
-- wait one frame to ensure all functions are declared
|
||||
TSMAPI:CreateTimeDelay(0, function() RegisterForTracing(obj, name) end)
|
||||
end
|
||||
@@ -0,0 +1,49 @@
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
-- TradeSkillMaster --
|
||||
-- http://www.curse.com/addons/wow/tradeskill-master --
|
||||
-- --
|
||||
-- A TradeSkillMaster Addon (http://tradeskillmaster.com) --
|
||||
-- All Rights Reserved* - Detailed license information included with addon. --
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
|
||||
-- TSM's error handler.
|
||||
|
||||
local TSM = select(2, ...)
|
||||
local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster")
|
||||
|
||||
local eventObj = TSMAPI:GetEventObject()
|
||||
local currentIndex = 1
|
||||
local NUM_LOG_ENTRIES = 20
|
||||
local debugLog = {}
|
||||
|
||||
local alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_="
|
||||
local base = #alpha
|
||||
local alphaTable = {}
|
||||
local alphaTableLookup = {}
|
||||
for i = 1, base do
|
||||
local char = strsub(alpha, i, i)
|
||||
tinsert(alphaTable, char)
|
||||
alphaTableLookup[char] = i
|
||||
end
|
||||
|
||||
local function EventCallback(event, arg)
|
||||
debugLog[currentIndex] = {event=event, arg=arg}
|
||||
currentIndex = currentIndex + 1
|
||||
if currentIndex > NUM_LOG_ENTRIES then
|
||||
currentIndex = 1
|
||||
end
|
||||
end
|
||||
eventObj:SetCallbackAnyEvent(EventCallback)
|
||||
|
||||
|
||||
function TSM:GetEventLog()
|
||||
local temp = {}
|
||||
for i=1, #debugLog do
|
||||
local index = currentIndex - i
|
||||
if index <= 0 then
|
||||
index = index + NUM_LOG_ENTRIES
|
||||
end
|
||||
tinsert(temp, debugLog[index])
|
||||
end
|
||||
return temp
|
||||
end
|
||||
@@ -0,0 +1,62 @@
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
-- TradeSkillMaster --
|
||||
-- http://www.curse.com/addons/wow/tradeskill-master --
|
||||
-- --
|
||||
-- A TradeSkillMaster Addon (http://tradeskillmaster.com) --
|
||||
-- All Rights Reserved* - Detailed license information included with addon. --
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
|
||||
-- TSM's event handler.
|
||||
|
||||
local TSM = select(2, ...)
|
||||
local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster")
|
||||
local private = {}
|
||||
TSMAPI:RegisterForTracing(private, "TradeSkillMaster.Events_private")
|
||||
private.objects = {}
|
||||
|
||||
|
||||
private.eventObjectCallbacks = {
|
||||
SetCallbackAnyEvent = function(self, callback)
|
||||
self._anyEventCallback = callback
|
||||
end,
|
||||
SetCallback = function(self, event, callback, matchAll)
|
||||
self._callbacks[event] = {func = callback, matchAll = (matchAll and true or false)} -- need to convert matchAll to a boolean
|
||||
end,
|
||||
ClearAllCallbacks = function(self)
|
||||
wipe(self._callbacks)
|
||||
end
|
||||
}
|
||||
|
||||
function TSMAPI:GetEventObject()
|
||||
local obj = {}
|
||||
obj._callbacks = {}
|
||||
obj._anyEventCallback = nil
|
||||
for name, func in pairs(private.eventObjectCallbacks) do
|
||||
obj[name] = func
|
||||
end
|
||||
tinsert(private.objects, obj)
|
||||
return obj
|
||||
end
|
||||
|
||||
function private:OnEventFired(event, arg, fullEvent)
|
||||
local isPartial = event ~= fullEvent and true or false
|
||||
for _, obj in ipairs(private.objects) do
|
||||
if not isPartial and obj._anyEventCallback then
|
||||
obj._anyEventCallback(fullEvent, arg)
|
||||
end
|
||||
local callback = obj._callbacks[event]
|
||||
if callback then
|
||||
if isPartial == callback.matchAll then
|
||||
callback.func(fullEvent, arg)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function TSMAPI:FireEvent(event, arg)
|
||||
local parts = {(":"):split(event)}
|
||||
for i=1, #parts do
|
||||
local partialEvent = table.concat(parts, ":", 1, i)
|
||||
private:OnEventFired(partialEvent, arg, event)
|
||||
end
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,368 @@
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
-- TradeSkillMaster --
|
||||
-- http://www.curse.com/addons/wow/tradeskill-master --
|
||||
-- --
|
||||
-- A TradeSkillMaster Addon (http://tradeskillmaster.com) --
|
||||
-- All Rights Reserved* - Detailed license information included with addon. --
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
|
||||
-- This file contains all the code for the new standardized module registration / format
|
||||
|
||||
local TSM = select(2, ...)
|
||||
local Modules = TSM:NewModule("Modules", "AceConsole-3.0")
|
||||
local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster") -- loads the localization table
|
||||
local moduleObjects = TSM.moduleObjects
|
||||
local moduleNames = TSM.moduleNames
|
||||
|
||||
|
||||
-- initialization stuff
|
||||
function Modules:OnEnable()
|
||||
-- register the chat commands (slash commands) - whenver '/tsm' or '/tradeskillmaster' is typed by the user, Modules:ChatCommand() will be called
|
||||
Modules:RegisterChatCommand("tsm", "ChatCommand")
|
||||
Modules:RegisterChatCommand("tradeskillmaster", "ChatCommand")
|
||||
|
||||
-- tooltip setup
|
||||
TSM:SetupTooltips()
|
||||
|
||||
-- no modules popup
|
||||
TSMAPI:CreateTimeDelay("noModulesPopup", 3, function()
|
||||
if #moduleNames == 1 then
|
||||
StaticPopupDialogs["TSMInfoPopup"] = {
|
||||
text = L["|cffffff00Important Note:|r You do not currently have any modules installed / enabled for TradeSkillMaster! |cff77ccffYou must download modules for TradeSkillMaster to have some useful functionality!|r\n\nPlease visit http://www.curse.com/addons/wow/tradeskill-master and check the project description for links to download modules."],
|
||||
button1 = L["I'll Go There Now!"],
|
||||
timeout = 0,
|
||||
whileDead = true,
|
||||
OnAccept = function() TSM:Print(L["Just incase you didn't read this the first time:"]) TSM:Print(L["|cffffff00Important Note:|r You do not currently have any modules installed / enabled for TradeSkillMaster! |cff77ccffYou must download modules for TradeSkillMaster to have some useful functionality!|r\n\nPlease visit http://www.curse.com/addons/wow/tradeskill-master and check the project description for links to download modules."]) end,
|
||||
preferredIndex = 3,
|
||||
}
|
||||
TSMAPI:ShowStaticPopupDialog("TSMInfoPopup")
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
-- **************************************************************************
|
||||
-- TSMAPI:NewModule API
|
||||
-- **************************************************************************
|
||||
|
||||
-- info on all the possible fields of the module objects which TSM core cares about
|
||||
local moduleFieldInfo = {
|
||||
-- operation fields
|
||||
{ key = "operations", type = "table", subFieldInfo = { maxOperations = "number", callbackOptions = "function", callbackInfo = "function" } },
|
||||
-- tooltip fields
|
||||
{ key = "GetTooltip", type = "function" },
|
||||
-- tooltip options
|
||||
{ key = "tooltipOptions", type = "table", subFieldInfo = { callback = "function" } },
|
||||
-- shared feature fields
|
||||
{ key = "slashCommands", type = "table", subTableInfo = { key = "string", label = "string", callback = "function" } },
|
||||
{ key = "icons", type = "table", subTableInfo = { side = "string", desc = "string", callback = "function", icon = "string" } },
|
||||
{ key = "auctionTab", type = "table", subFieldInfo = { callbackShow = "function", callbackHide = "function" } },
|
||||
{ key = "bankUiButton", type = "table", subFieldInfo = { callback = "function" } },
|
||||
-- data access fields
|
||||
{ key = "priceSources", type = "table", subTableInfo = { key = "string", label = "string", callback = "function" } },
|
||||
{ key = "moduleAPIs", type = "table", subTableInfo = { key = "string", callback = "function" } },
|
||||
-- multi-account sync fields
|
||||
{ key = "sync", type = "table", subFieldInfo = { callback = "function" } },
|
||||
}
|
||||
|
||||
-- if the passed function is a string, will check if it's a method of the object and return a wrapper function
|
||||
function Modules:GetFunction(obj, func)
|
||||
if type(func) == "string" then
|
||||
local part1, part2 = (":"):split(func)
|
||||
if part2 and obj[part1] and obj[part1][part2] then
|
||||
return function(...) return obj[part1][part2](obj[part1], ...) end
|
||||
elseif obj[part1] then
|
||||
return function(...) return obj[part1](obj, ...) end
|
||||
end
|
||||
end
|
||||
return func
|
||||
end
|
||||
|
||||
-- validates a simple list of sub-tables which have the basic key/label/callback fields
|
||||
function Modules:ValidateList(obj, val, keys)
|
||||
for i, v in ipairs(val) do
|
||||
if type(v) ~= "table" then
|
||||
return "invalid entry in list at index " .. i
|
||||
end
|
||||
for key, valType in pairs(keys) do
|
||||
if valType == "function" then
|
||||
v[key] = Modules:GetFunction(obj, v[key])
|
||||
end
|
||||
if type(v[key]) ~= valType then
|
||||
return format("expected %s type for field %s, got %s at index %d", valType, key, type(v[key]), i)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Modules:ValidateModuleObject(obj)
|
||||
-- make sure it's a table
|
||||
if type(obj) ~= "table" then
|
||||
return format("Expected table, got %s.", type(obj))
|
||||
end
|
||||
-- simple check that it's an AceAddon object which stores the name in .name and implements a .__tostring metamethod.
|
||||
if tostring(obj) ~= obj.name then
|
||||
return "Passed object is not an AceAddon-3.0 object."
|
||||
end
|
||||
|
||||
-- validate all the fields
|
||||
for _, fieldInfo in ipairs(moduleFieldInfo) do
|
||||
local val = obj[fieldInfo.key]
|
||||
if val then
|
||||
-- make sure it's of the correct type
|
||||
if type(val) ~= fieldInfo.type then
|
||||
return format("For field '%s', expected type of %s, got %s.", fieldInfo.key, fieldInfo.type, type(val))
|
||||
end
|
||||
-- if there's required subfields, check them
|
||||
if fieldInfo.subFieldInfo then
|
||||
for key, valType in pairs(fieldInfo.subFieldInfo) do
|
||||
if valType == "function" then
|
||||
val[key] = Modules:GetFunction(obj, val[key])
|
||||
end
|
||||
if type(val[key]) ~= valType then
|
||||
return format("expected %s type for field %s, got %s at index %d", valType, key, type(val[key]), key)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- if there's subTableInfo specified, run Modules:ValidateList on this field
|
||||
if fieldInfo.subTableInfo then
|
||||
local errMsg = Modules:ValidateList(obj, val, fieldInfo.subTableInfo)
|
||||
if errMsg then
|
||||
return format("Invalid value for '%s': %s.", fieldInfo.key, errMsg)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Modules:GetInfo()
|
||||
local info = {}
|
||||
for _, name in ipairs(moduleNames) do
|
||||
local obj = moduleObjects[name]
|
||||
tinsert(info, { name = name, version = obj._version, author = obj._author, desc = obj._desc })
|
||||
end
|
||||
return info
|
||||
end
|
||||
|
||||
function TSMAPI:NewModule(obj)
|
||||
local errMsg
|
||||
if obj == TSM then
|
||||
local tmp = TSM.operations
|
||||
TSM.operations = nil
|
||||
errMsg = Modules:ValidateModuleObject(obj)
|
||||
TSM.operations = tmp
|
||||
else
|
||||
errMsg = Modules:ValidateModuleObject(obj)
|
||||
end
|
||||
if errMsg then
|
||||
error(errMsg, 2)
|
||||
end
|
||||
|
||||
-- register the db callback
|
||||
if obj.db and obj.OnTSMDBShutdown then
|
||||
obj.db:RegisterCallback("OnDatabaseShutdown", TSM.ModuleOnDatabaseShutdown)
|
||||
end
|
||||
|
||||
-- register it for debug tracing
|
||||
TSMAPI:RegisterForTracing(obj)
|
||||
for _, subModule in pairs(obj.modules or {}) do
|
||||
local name = obj.name.."."..subModule.moduleName
|
||||
TSMAPI:RegisterForTracing(subModule, name)
|
||||
end
|
||||
|
||||
-- sets the _version, _author, and _desc fields
|
||||
local fullName = gsub(obj.name, "TSM_", "TradeSkillMaster_")
|
||||
obj._version = GetAddOnMetadata(fullName, "X-Curse-Packaged-Version") or GetAddOnMetadata(fullName, "Version")
|
||||
if strsub(obj._version, 1, 1) == "@" then
|
||||
obj._version = "Dev"
|
||||
end
|
||||
obj._author = GetAddOnMetadata(fullName, "Author")
|
||||
obj._desc = GetAddOnMetadata(fullName, "Notes")
|
||||
|
||||
-- store the object in the local table
|
||||
local moduleName = gsub(obj.name, "TradeSkillMaster_", "")
|
||||
moduleName = gsub(obj.name, "TSM_", "")
|
||||
moduleObjects[moduleName] = obj
|
||||
tinsert(moduleNames, moduleName)
|
||||
sort(moduleNames, function(a, b)
|
||||
if a == "TradeSkillMaster" then
|
||||
return true
|
||||
elseif b == "TradeSkillMaster" then
|
||||
return false
|
||||
else
|
||||
return a < b
|
||||
end
|
||||
end)
|
||||
|
||||
-- register icons with main frame code
|
||||
if obj.icons then
|
||||
for _, info in ipairs(obj.icons) do
|
||||
if info.slashCommand then
|
||||
obj.slashCommands = obj.slashCommands or {}
|
||||
tinsert(obj.slashCommands, {key=info.slashCommand, label=format("Opens the TSM window to the '%s' page", info.desc), callback=function() TSMAPI:OpenFrame() TSMAPI:SelectIcon(obj.name, info.desc) end})
|
||||
end
|
||||
TSM:RegisterMainFrameIcon(info.desc, info.icon, info.callback, obj.name, info.side)
|
||||
end
|
||||
end
|
||||
|
||||
-- register auction buttons with auction frame code
|
||||
if obj.auctionTab then
|
||||
TSM:RegisterAuctionFunction(moduleName, obj.auctionTab.callbackShow, obj.auctionTab.callbackHide)
|
||||
end
|
||||
if obj ~= TSM and obj.operations then
|
||||
-- conversion code from early beta versions
|
||||
if obj.db and obj.db.global.operations then
|
||||
TSM.operations[moduleName] = CopyTable(obj.db.global.operations)
|
||||
obj.db.global.operations = nil
|
||||
end
|
||||
TSM:RegisterOperationInfo(moduleName, obj.operations)
|
||||
TSM.operations[moduleName] = TSM.operations[moduleName] or {}
|
||||
obj.operations = TSM.operations[moduleName]
|
||||
for _, operation in pairs(obj.operations) do
|
||||
operation.ignorePlayer = operation.ignorePlayer or {}
|
||||
operation.ignoreFactionrealm = operation.ignoreFactionrealm or {}
|
||||
operation.relationships = operation.relationships or {}
|
||||
end
|
||||
TSM:CheckOperationRelationships(moduleName)
|
||||
end
|
||||
-- register tooltip options
|
||||
if obj.tooltipOptions then
|
||||
TSM:RegisterTooltipInfo(moduleName, obj.tooltipOptions)
|
||||
end
|
||||
-- register bankUi Tabs
|
||||
if obj.bankUiButton then
|
||||
TSM:RegisterBankUiButton(moduleName, obj.bankUiButton.callback)
|
||||
end
|
||||
-- -- register sync callback
|
||||
-- if obj.sync then
|
||||
-- TSM:RegisterSyncCallback(moduleName, obj.sync.callback)
|
||||
-- end
|
||||
|
||||
-- replace default Print and Printf functions
|
||||
local Print = obj.Print
|
||||
obj.Print = function(self, ...) Print(self, TSMAPI:GetChatFrame(), ...) end
|
||||
local Printf = obj.Printf
|
||||
obj.Printf = function(self, ...) Printf(self, TSMAPI:GetChatFrame(), ...) end
|
||||
end
|
||||
|
||||
function TSM:UpdateModuleProfiles()
|
||||
if TSM.db.global.globalOperations then
|
||||
for moduleName, obj in pairs(moduleObjects) do
|
||||
if obj.operations then
|
||||
TSM.db.global.operations[moduleName] = TSM.db.global.operations[moduleName] or {}
|
||||
obj.operations = TSM.db.global.operations[moduleName]
|
||||
end
|
||||
end
|
||||
TSM.operations = TSM.db.global.operations
|
||||
else
|
||||
for moduleName, obj in pairs(moduleObjects) do
|
||||
if obj.operations then
|
||||
TSM.db.profile.operations[moduleName] = TSM.db.profile.operations[moduleName] or {}
|
||||
obj.operations = TSM.db.profile.operations[moduleName]
|
||||
end
|
||||
end
|
||||
TSM.operations = TSM.db.profile.operations
|
||||
end
|
||||
for module, operations in pairs(TSM.operations) do
|
||||
for _, operation in pairs(operations) do
|
||||
operation.ignorePlayer = operation.ignorePlayer or {}
|
||||
operation.ignoreFactionrealm = operation.ignoreFactionrealm or {}
|
||||
operation.relationships = operation.relationships or {}
|
||||
end
|
||||
TSM:CheckOperationRelationships(module)
|
||||
end
|
||||
if not TSM.db.profile.design then
|
||||
TSM:LoadDefaultDesign()
|
||||
end
|
||||
end
|
||||
|
||||
local didDBShutdown = false
|
||||
function TSM:ModuleOnDatabaseShutdown()
|
||||
if didDBShutdown then return end
|
||||
didDBShutdown = true
|
||||
local originalProfile = TSM.db:GetCurrentProfile()
|
||||
for _, obj in pairs(moduleObjects) do
|
||||
-- erroring here would cause the profile to be reset, so use pcall
|
||||
if obj.OnTSMDBShutdown and not pcall(obj.OnTSMDBShutdown) then
|
||||
-- the callback hit an error, so ensure the correct profile is restored
|
||||
TSM.db:SetProfile(originalProfile)
|
||||
end
|
||||
end
|
||||
-- ensure we're back on the correct profile
|
||||
TSM.db:SetProfile(originalProfile)
|
||||
end
|
||||
|
||||
function TSM:IsOperationIgnored(module, operationName)
|
||||
local obj = moduleObjects[module]
|
||||
local operation = obj.operations[operationName]
|
||||
if not operation then return end
|
||||
local factionrealm = TSM.db.keys.factionrealm
|
||||
local playerKey = UnitName("player").." - "..factionrealm
|
||||
return operation.ignorePlayer[playerKey] or operation.ignoreFactionrealm[factionrealm]
|
||||
end
|
||||
|
||||
function TSM:CheckOperationRelationships(moduleName)
|
||||
for _, operation in pairs(TSM.operations[moduleName]) do
|
||||
for key, target in pairs(operation.relationships or {}) do
|
||||
if not TSM.operations[moduleName][target] then
|
||||
operation.relationships[key] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- **************************************************************************
|
||||
-- Module APIs
|
||||
-- **************************************************************************
|
||||
|
||||
function TSMAPI:ModuleAPI(moduleName, key, ...)
|
||||
if type(moduleName) ~= "string" or type(key) ~= "string" then return nil, "Invalid args" end
|
||||
if not moduleObjects[moduleName] then return nil, "Invalid module" end
|
||||
|
||||
for _, info in ipairs(moduleObjects[moduleName].moduleAPIs or {}) do
|
||||
if info.key == key then
|
||||
return info.callback(...)
|
||||
end
|
||||
end
|
||||
return nil, "Key not found"
|
||||
end
|
||||
|
||||
|
||||
-- **************************************************************************
|
||||
-- Slash Commands
|
||||
-- **************************************************************************
|
||||
|
||||
function Modules:ChatCommand(input)
|
||||
local parts = { (" "):split(input) }
|
||||
local cmd, args = strlower(parts[1] or ""), table.concat(parts, " ", 2)
|
||||
|
||||
if cmd == "" then
|
||||
TSMAPI:OpenFrame()
|
||||
TSMAPI:SelectIcon("TradeSkillMaster", L["TSM Status / Options"])
|
||||
else
|
||||
local foundCmd
|
||||
for _, obj in pairs(moduleObjects) do
|
||||
if obj.slashCommands then
|
||||
for _, info in ipairs(obj.slashCommands) do
|
||||
if strlower(info.key) == cmd then
|
||||
info.callback(args)
|
||||
foundCmd = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- If not a registered command, print out slash command help
|
||||
if not foundCmd then
|
||||
local chatFrame = TSMAPI:GetChatFrame()
|
||||
TSM:Print(L["Slash Commands:"])
|
||||
chatFrame:AddMessage("|cffffaa00" .. L["/tsm|r - opens the main TSM window."])
|
||||
chatFrame:AddMessage("|cffffaa00" .. L["/tsm help|r - Shows this help listing"])
|
||||
for _, name in ipairs(moduleNames) do
|
||||
for _, info in ipairs(moduleObjects[name].slashCommands or {}) do
|
||||
chatFrame:AddMessage("|cffffaa00/tsm " .. info.key .. "|r - " .. info.label)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,699 @@
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
-- TradeSkillMaster --
|
||||
-- http://www.curse.com/addons/wow/tradeskill-master --
|
||||
-- --
|
||||
-- A TradeSkillMaster Addon (http://tradeskillmaster.com) --
|
||||
-- All Rights Reserved* - Detailed license information included with addon. --
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
|
||||
-- This file contains all the code for moving items between bags / bank.
|
||||
|
||||
local TSM = select(2, ...)
|
||||
local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster") -- loads the localization table
|
||||
local AceGUI = LibStub("AceGUI-3.0") -- load the AceGUI libraries
|
||||
local lib = TSMAPI
|
||||
|
||||
local bankType
|
||||
local fullMoves, splitMoves, bagState = {}, {}, {}
|
||||
local callbackMsg = {}
|
||||
|
||||
-- this is a set of wrapper functions so that I can switch
|
||||
-- between guildbank and bank function easily (taken from warehousing)
|
||||
|
||||
TSM.pickupContainerItemSrc = nil
|
||||
TSM.getContainerItemIDSrc = nil
|
||||
TSM.getContainerNumSlotsSrc = nil
|
||||
TSM.getContainerItemLinkSrc = nil
|
||||
TSM.getContainerNumFreeSlotsSrc = nil
|
||||
TSM.splitContainerItemSrc = nil
|
||||
|
||||
TSM.pickupContainerItemDest = nil
|
||||
TSM.getContainerItemIDDest = nil
|
||||
TSM.getContainerNumSlotsDest = nil
|
||||
TSM.getContainerItemLinkDest = nil
|
||||
TSM.getContainerNumFreeSlotsDest = nil
|
||||
|
||||
|
||||
TSM.autoStoreItem = nil
|
||||
TSM.getContainerItemQty = nil
|
||||
|
||||
function TSM:OnEnable()
|
||||
local next = next
|
||||
|
||||
TSM:RegisterEvent("GUILDBANKFRAME_OPENED", function(event)
|
||||
bankType = "guildbank"
|
||||
end)
|
||||
|
||||
TSM:RegisterEvent("BANKFRAME_OPENED", function(event)
|
||||
bankType = "bank"
|
||||
end)
|
||||
|
||||
TSM:RegisterEvent("GUILDBANKFRAME_CLOSED", function(event, addon)
|
||||
bankType = nil
|
||||
TSM:UnregisterEvent("GUILDBANKBAGSLOTS_CHANGED")
|
||||
end)
|
||||
|
||||
TSM:RegisterEvent("BANKFRAME_CLOSED", function(event)
|
||||
bankType = nil
|
||||
TSM:UnregisterEvent("BAG_UPDATE")
|
||||
end)
|
||||
end
|
||||
|
||||
local function setSrcBagFunctions(bagType)
|
||||
if bagType == "guildbank" then
|
||||
TSM.autoStoreItem = function(bag, slot) AutoStoreGuildBankItem(bag, slot)
|
||||
end
|
||||
TSM.getContainerItemQty = function(bag, slot) return select(2, GetGuildBankItemInfo(bag, slot))
|
||||
end
|
||||
TSM.splitContainerItemSrc = function(bag, slot, need) SplitGuildBankItem(bag, slot, need);
|
||||
end
|
||||
TSM.pickupContainerItemSrc = function(bag, slot) PickupGuildBankItem(bag, slot)
|
||||
end
|
||||
TSM.getContainerNumSlotsSrc = function(bag) return MAX_GUILDBANK_SLOTS_PER_TAB or 98
|
||||
end
|
||||
TSM.getContainerItemLinkSrc = function(bag, slot) return GetGuildBankItemLink(bag, slot)
|
||||
end
|
||||
TSM.getContainerNumFreeSlotsSrc = function(bag) return MAX_GUILDBANK_SLOTS_PER_TAB or 98
|
||||
end --need to change this eventually
|
||||
TSM.getContainerItemIDSrc = function(bag, slot)
|
||||
local tmpLink = GetGuildBankItemLink(bag, slot)
|
||||
local quantity = select(2, GetGuildBankItemInfo(bag, slot))
|
||||
if tmpLink then
|
||||
return TSMAPI:GetBaseItemString(tmpLink, true), quantity
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
else
|
||||
TSM.autoStoreItem = function(bag, slot) UseContainerItem(bag, slot)
|
||||
end
|
||||
TSM.getContainerItemQty = function(bag, slot) return select(2, GetContainerItemInfo(bag, slot))
|
||||
end
|
||||
TSM.splitContainerItemSrc = function(bag, slot, need) SplitContainerItem(bag, slot, need)
|
||||
end
|
||||
TSM.pickupContainerItemSrc = function(bag, slot) PickupContainerItem(bag, slot)
|
||||
end
|
||||
TSM.getContainerItemIDSrc = function(bag, slot)
|
||||
local tmpLink = GetContainerItemLink(bag, slot)
|
||||
local quantity = select(2, GetContainerItemInfo(bag, slot))
|
||||
return TSMAPI:GetBaseItemString(tmpLink, true), quantity
|
||||
end
|
||||
TSM.getContainerNumSlotsSrc = function(bag) return GetContainerNumSlots(bag)
|
||||
end
|
||||
TSM.getContainerItemLinkSrc = function(bag, slot) return GetContainerItemLink(bag, slot)
|
||||
end
|
||||
TSM.getContainerNumFreeSlotsSrc = function(bag) return GetContainerNumFreeSlots(bag)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function setDestBagFunctions(bagType)
|
||||
if bagType == "guildbank" then
|
||||
TSM.pickupContainerItemDest = function(bag, slot) PickupGuildBankItem(bag, slot)
|
||||
end
|
||||
TSM.getContainerNumSlotsDest = function(bag) return MAX_GUILDBANK_SLOTS_PER_TAB or 98
|
||||
end
|
||||
TSM.getContainerNumFreeSlotsDest = function(bag) return GetEmptySlotCount(bag)
|
||||
end --need to change this eventually
|
||||
TSM.getContainerItemLinkDest = function(bag, slot) return GetGuildBankItemLink(bag, slot)
|
||||
end
|
||||
TSM.getContainerItemIDDest = function(bag, slot)
|
||||
local tmpLink = GetGuildBankItemLink(bag, slot)
|
||||
local quantity = select(2, GetGuildBankItemInfo(bag, slot))
|
||||
if tmpLink then
|
||||
return TSMAPI:GetBaseItemString(tmpLink, true), quantity
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
else
|
||||
TSM.pickupContainerItemDest = function(bag, slot) PickupContainerItem(bag, slot)
|
||||
end
|
||||
TSM.getContainerItemIDDest = function(bag, slot)
|
||||
local tmpLink = GetContainerItemLink(bag, slot)
|
||||
local quantity = select(2, GetContainerItemInfo(bag, slot))
|
||||
return TSMAPI:GetBaseItemString(tmpLink, true), quantity
|
||||
end
|
||||
TSM.getContainerNumSlotsDest = function(bag) return GetContainerNumSlots(bag)
|
||||
end
|
||||
TSM.getContainerItemLinkDest = function(bag, slot) return GetContainerItemLink(bag, slot)
|
||||
end
|
||||
TSM.getContainerNumFreeSlotsDest = function(bag) return GetContainerNumFreeSlots(bag)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function getContainerTable(cnt)
|
||||
local t = {}
|
||||
|
||||
if cnt == "bank" then
|
||||
local numSlots, _ = GetNumBankSlots()
|
||||
|
||||
for i = 1, numSlots + 1 do
|
||||
if i == 1 then
|
||||
t[i] = -1
|
||||
else
|
||||
t[i] = i + 3
|
||||
end
|
||||
end
|
||||
|
||||
return t
|
||||
|
||||
elseif cnt == "guildbank" then
|
||||
for i = 1, GetNumGuildBankTabs() do
|
||||
local canView, canDeposit, stacksPerDay = GetGuildBankTabInfo(i);
|
||||
if canView and canDeposit and stacksPerDay then
|
||||
t[i] = i
|
||||
end
|
||||
end
|
||||
|
||||
return t
|
||||
elseif cnt == "bags" then
|
||||
for i = 1, NUM_BAG_SLOTS + 1 do t[i] = i - 1
|
||||
end
|
||||
return t
|
||||
end
|
||||
end
|
||||
|
||||
local function GetEmptySlots(container)
|
||||
local emptySlots = {}
|
||||
for i, bag in ipairs(getContainerTable(container)) do
|
||||
if TSM.getContainerNumSlotsDest(bag) > 0 then
|
||||
for slot = 1, TSM.getContainerNumSlotsDest(bag) do
|
||||
if not TSM.getContainerItemIDDest(bag, slot) then
|
||||
if not emptySlots[bag] then emptySlots[bag] = {}
|
||||
end
|
||||
table.insert(emptySlots[bag], slot)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return emptySlots
|
||||
end
|
||||
|
||||
local function GetEmptySlotCount(bag)
|
||||
local count = 0
|
||||
for slot = 1, TSM.getContainerNumSlotsDest(bag) do
|
||||
if not TSM.getContainerItemLinkDest(bag, slot) then
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
if count ~= 0 then
|
||||
return count
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local function canGoInBag(itemString, destTable)
|
||||
local itemFamily = GetItemFamily(itemString)
|
||||
local default
|
||||
for _, bag in pairs(destTable) do
|
||||
local bagFamily = GetItemFamily(GetBagName(bag)) or 0
|
||||
if itemFamily and bagFamily and bagFamily > 0 and bit.band(itemFamily, bagFamily) > 0 then
|
||||
if GetEmptySlotCount(bag) then
|
||||
return bag
|
||||
end
|
||||
elseif bagFamily == 0 then
|
||||
if GetEmptySlotCount(bag) then
|
||||
if not default then
|
||||
default = bag
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return default
|
||||
end
|
||||
|
||||
local function findExistingStack(itemLink, dest, quantity, gbank)
|
||||
for i, bag in ipairs(getContainerTable(dest)) do
|
||||
if gbank then
|
||||
if bag == GetCurrentGuildBankTab() then
|
||||
for slot = 1, TSM.getContainerNumSlotsDest(bag) do
|
||||
if TSM.getContainerItemIDDest(bag, slot) == TSMAPI:GetBaseItemString(itemLink, true) then
|
||||
local maxStack = select(8, TSMAPI:GetSafeItemInfo(itemLink))
|
||||
local _, currentQuantity = TSM.getContainerItemIDDest(bag, slot)
|
||||
if currentQuantity and (currentQuantity + quantity) <= maxStack then
|
||||
return bag, slot
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
for slot = 1, TSM.getContainerNumSlotsDest(bag) do
|
||||
if TSM.getContainerItemIDDest(bag, slot) == TSMAPI:GetBaseItemString(itemLink, true) then
|
||||
local maxStack = select(8, TSMAPI:GetSafeItemInfo(itemLink))
|
||||
local _, currentQuantity = TSM.getContainerItemIDDest(bag, slot)
|
||||
if currentQuantity and (currentQuantity + quantity) <= maxStack then
|
||||
return bag, slot
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function getTotalItems(src)
|
||||
local results = {}
|
||||
if src == "bank" then
|
||||
for _, _, itemString, quantity in TSMAPI:GetBankIterator(true, true) do
|
||||
results[itemString] = (results[itemString] or 0) + quantity
|
||||
end
|
||||
|
||||
return results
|
||||
elseif src == "guildbank" then
|
||||
for bag = 1, GetNumGuildBankTabs() do
|
||||
for slot = 1, MAX_GUILDBANK_SLOTS_PER_TAB or 98 do
|
||||
local link = GetGuildBankItemLink(bag, slot)
|
||||
local itemString = TSMAPI:GetBaseItemString(link, true)
|
||||
if itemString then
|
||||
local quantity = select(2, GetGuildBankItemInfo(bag, slot))
|
||||
results[itemString] = (results[itemString] or 0) + quantity
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return results
|
||||
elseif src == "bags" then
|
||||
for _, _, itemString, quantity in TSMAPI:GetBagIterator(true, true) do
|
||||
results[itemString] = (results[itemString] or 0) + quantity
|
||||
end
|
||||
|
||||
return results
|
||||
end
|
||||
end
|
||||
|
||||
function TSM.generateMoves(includeSoulbound)
|
||||
if not TSM:areBanksVisible() then
|
||||
wipe(splitMoves)
|
||||
wipe(fullMoves)
|
||||
for _, callback in ipairs(callbackMsg) do
|
||||
callback(L["Cancelled - You must be at a bank or guildbank"])
|
||||
end
|
||||
wipe(callbackMsg)
|
||||
return
|
||||
end
|
||||
|
||||
local next = next
|
||||
local bagsFull, bankFull = false, false
|
||||
local bagMoves, bankMoves = {}, {}
|
||||
wipe(splitMoves)
|
||||
wipe(fullMoves)
|
||||
|
||||
local currentBagState = getTotalItems("bags")
|
||||
|
||||
for itemString, quantity in pairs(bagState) do
|
||||
local currentQty = currentBagState[itemString] or 0
|
||||
if quantity < currentQty then
|
||||
bagMoves[itemString] = currentQty - quantity
|
||||
elseif quantity > currentQty then
|
||||
bankMoves[itemString] = quantity - currentQty
|
||||
end
|
||||
end
|
||||
|
||||
if next(bagMoves) ~= nil then -- generate moves from bags to bank
|
||||
setSrcBagFunctions("bags")
|
||||
setDestBagFunctions(bankType)
|
||||
for item, _ in pairs(bagMoves) do
|
||||
for i, bag in ipairs(getContainerTable("bags")) do
|
||||
for slot = 1, TSM.getContainerNumSlotsSrc(bag) do
|
||||
local itemLink = TSM.getContainerItemLinkSrc(bag, slot)
|
||||
local itemString = TSMAPI:GetBaseItemString(itemLink, true)
|
||||
if itemString and itemString == item then
|
||||
if not TSMAPI:IsSoulbound(bag, slot) or includeSoulbound then
|
||||
local have = TSM.getContainerItemQty(bag, slot)
|
||||
local need = bagMoves[itemString]
|
||||
if have and need then
|
||||
-- check if the source item stack can fit into a destination bag
|
||||
local destBag
|
||||
if bankType == "guildbank" then
|
||||
destBag = findExistingStack(itemLink, bankType, min(have, need), true)
|
||||
if not destBag then
|
||||
if GetEmptySlotCount(GetCurrentGuildBankTab()) ~= false then
|
||||
destBag = GetCurrentGuildBankTab()
|
||||
end
|
||||
end
|
||||
else
|
||||
destBag = findExistingStack(itemLink, bankType, min(have, need))
|
||||
if not destBag then
|
||||
if next(GetEmptySlots(bankType)) ~= nil then
|
||||
destBag = canGoInBag(itemString, getContainerTable(bankType))
|
||||
end
|
||||
end
|
||||
end
|
||||
if destBag then
|
||||
if have > need then
|
||||
tinsert(splitMoves, { src = "bags", bag = bag, slot = slot, quantity = need })
|
||||
bagMoves[itemString] = nil
|
||||
else
|
||||
tinsert(fullMoves, { src = "bags", bag = bag, slot = slot, quantity = have })
|
||||
bagMoves[itemString] = bagMoves[itemString] - have
|
||||
if bagMoves[itemString] <= 0 then
|
||||
bagMoves[itemString] = nil
|
||||
end
|
||||
end
|
||||
else
|
||||
bankFull = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if next(bankMoves) ~= nil then -- generate moves from bank to bags
|
||||
setSrcBagFunctions(bankType)
|
||||
setDestBagFunctions("bags")
|
||||
for item, _ in pairs(bankMoves) do
|
||||
for i, bag in ipairs(getContainerTable(bankType)) do
|
||||
for slot = 1, TSM.getContainerNumSlotsSrc(bag) do
|
||||
local itemLink = TSM.getContainerItemLinkSrc(bag, slot)
|
||||
local itemString = TSMAPI:GetBaseItemString(itemLink, true)
|
||||
if itemString and itemString == item then
|
||||
local have = TSM.getContainerItemQty(bag, slot)
|
||||
local need = bankMoves[itemString]
|
||||
if have and need then
|
||||
if not TSMAPI:IsSoulbound(bag, slot) or includeSoulbound then
|
||||
local destBag = findExistingStack(itemLink, "bags", min(have, need)) or canGoInBag(itemString, getContainerTable("bags"))
|
||||
if destBag then
|
||||
if have > need then
|
||||
tinsert(splitMoves, { src = bankType, bag = bag, slot = slot, quantity = need })
|
||||
bankMoves[itemString] = nil
|
||||
else
|
||||
tinsert(fullMoves, { src = bankType, bag = bag, slot = slot, quantity = have })
|
||||
bankMoves[itemString] = bankMoves[itemString] - have
|
||||
if bankMoves[itemString] <= 0 then
|
||||
bankMoves[itemString] = nil
|
||||
end
|
||||
end
|
||||
else
|
||||
bagsFull = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if next(fullMoves) ~= nil then
|
||||
if bankType == "guildbank" then
|
||||
TSMAPI:CreateTimeDelay("moveItem", 0.05, TSM.moveItem, 0.35)
|
||||
else
|
||||
TSMAPI:CreateTimeDelay("moveItem", 0.05, TSM.moveItem, 0.05)
|
||||
end
|
||||
elseif next(splitMoves) ~= nil then
|
||||
if bankType == "guildbank" then
|
||||
TSMAPI:CreateTimeDelay("moveSplitItem", 0.05, TSM.moveSplitItem, 0.75)
|
||||
else
|
||||
TSMAPI:CreateTimeDelay("moveSplitItem", 0.05, TSM.moveSplitItem, 0.4)
|
||||
end
|
||||
else
|
||||
if bagsFull and not bankFull then
|
||||
for _, callback in ipairs(callbackMsg) do
|
||||
callback(L["Cancelled - Bags are full"])
|
||||
end
|
||||
elseif bankFull and not bagsFull then
|
||||
for _, callback in ipairs(callbackMsg) do
|
||||
if bankType == "guildbank" then
|
||||
callback(L["Cancelled - Guildbank is full"])
|
||||
elseif bankType == "bank" then
|
||||
callback(L["Cancelled - Bank is full"])
|
||||
else
|
||||
callback("Cancelled - " .. bankType .. " is full")
|
||||
end
|
||||
end
|
||||
elseif bagsFull and bankFull then
|
||||
for _, callback in ipairs(callbackMsg) do
|
||||
if bankType == "guildbank" then
|
||||
callback(L["Cancelled - Bags and guildbank are full"])
|
||||
elseif bankType == "bank" then
|
||||
callback(L["Cancelled - Bags and bank are full"])
|
||||
else
|
||||
callback("Cancelled - Bags and " .. bankType .. " are full")
|
||||
end
|
||||
end
|
||||
else
|
||||
for _, callback in ipairs(callbackMsg) do
|
||||
callback(L["Done"])
|
||||
end
|
||||
end
|
||||
wipe(callbackMsg)
|
||||
end
|
||||
end
|
||||
|
||||
function TSM.moveItem()
|
||||
if not TSM:areBanksVisible() then
|
||||
wipe(fullMoves)
|
||||
wipe(splitMoves)
|
||||
TSMAPI:CancelFrame("moveItem")
|
||||
for _, callback in ipairs(callbackMsg) do
|
||||
callback(L["Cancelled - You must be at a bank or guildbank"])
|
||||
end
|
||||
wipe(callbackMsg)
|
||||
return
|
||||
end
|
||||
|
||||
local next = next
|
||||
if #fullMoves > 0 then
|
||||
local i = next(fullMoves)
|
||||
if fullMoves[i].src == "bags" then
|
||||
setSrcBagFunctions("bags")
|
||||
setDestBagFunctions(bankType)
|
||||
local itemString = TSMAPI:GetBaseItemString(TSM.getContainerItemLinkSrc(fullMoves[i].bag, fullMoves[i].slot), true)
|
||||
local itemLink = TSM.getContainerItemLinkSrc(fullMoves[i].bag, fullMoves[i].slot)
|
||||
local have = TSM.getContainerItemQty(fullMoves[i].bag, fullMoves[i].slot)
|
||||
local need = fullMoves[i].quantity
|
||||
if have and need then
|
||||
if bankType == "guildbank" then
|
||||
if findExistingStack(itemLink, bankType, need, true) then
|
||||
TSM.autoStoreItem(fullMoves[i].bag, fullMoves[i].slot)
|
||||
elseif GetEmptySlotCount(GetCurrentGuildBankTab()) then
|
||||
TSM.autoStoreItem(fullMoves[i].bag, fullMoves[i].slot)
|
||||
else
|
||||
TSMAPI:CancelFrame("moveItem")
|
||||
TSMAPI:CreateTimeDelay("generateMoves", 0.4, TSM.generateMoves)
|
||||
end
|
||||
else
|
||||
if findExistingStack(itemLink, bankType, need) then
|
||||
TSM.autoStoreItem(fullMoves[i].bag, fullMoves[i].slot)
|
||||
elseif next(GetEmptySlots(bankType)) ~= nil and canGoInBag(itemString, getContainerTable(bankType)) then
|
||||
TSM.autoStoreItem(fullMoves[i].bag, fullMoves[i].slot)
|
||||
else
|
||||
TSMAPI:CancelFrame("moveItem")
|
||||
TSMAPI:CreateTimeDelay("generateMoves", 0.4, TSM.generateMoves)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
setSrcBagFunctions(bankType)
|
||||
setDestBagFunctions("bags")
|
||||
local itemString = TSMAPI:GetBaseItemString(TSM.getContainerItemLinkSrc(fullMoves[i].bag, fullMoves[i].slot), true)
|
||||
local itemLink = TSM.getContainerItemLinkSrc(fullMoves[i].bag, fullMoves[i].slot)
|
||||
local have = TSM.getContainerItemQty(fullMoves[i].bag, fullMoves[i].slot)
|
||||
local need = fullMoves[i].quantity
|
||||
if have and need then
|
||||
if findExistingStack(itemLink, "bags", need) then
|
||||
if bankType == "guildbank" then
|
||||
if GetCurrentGuildBankTab() ~= fullMoves[i].bag then
|
||||
SetCurrentGuildBankTab(fullMoves[i].bag)
|
||||
end
|
||||
end
|
||||
TSM.autoStoreItem(fullMoves[i].bag, fullMoves[i].slot)
|
||||
elseif next(GetEmptySlots("bags")) ~= nil and canGoInBag(itemString, getContainerTable("bags")) then
|
||||
if bankType == "guildbank" then
|
||||
if GetCurrentGuildBankTab() ~= fullMoves[i].bag then
|
||||
SetCurrentGuildBankTab(fullMoves[i].bag)
|
||||
end
|
||||
end
|
||||
TSM.autoStoreItem(fullMoves[i].bag, fullMoves[i].slot)
|
||||
else
|
||||
TSMAPI:CancelFrame("moveItem")
|
||||
TSMAPI:CreateTimeDelay("generateMoves", 0.4, TSM.generateMoves)
|
||||
end
|
||||
end
|
||||
end
|
||||
tremove(fullMoves, i)
|
||||
else
|
||||
TSMAPI:CancelFrame("moveItem")
|
||||
TSMAPI:CreateTimeDelay("generateMoves", 0.4, TSM.generateMoves)
|
||||
end
|
||||
end
|
||||
|
||||
function TSM.moveSplitItem()
|
||||
if not TSM:areBanksVisible() then
|
||||
wipe(fullMoves)
|
||||
wipe(splitMoves)
|
||||
TSMAPI:CancelFrame("moveSplitItem")
|
||||
for _, callback in ipairs(callbackMsg) do
|
||||
callback(L["Cancelled - You must be at a bank or guildbank"])
|
||||
end
|
||||
wipe(callbackMsg)
|
||||
return
|
||||
end
|
||||
local next = next
|
||||
--if next(moves) ~= nil then
|
||||
if #splitMoves > 0 then
|
||||
local i = next(splitMoves)
|
||||
if splitMoves[i].src == "bags" then
|
||||
setSrcBagFunctions("bags")
|
||||
setDestBagFunctions(bankType)
|
||||
local itemLink = TSM.getContainerItemLinkSrc(splitMoves[i].bag, splitMoves[i].slot)
|
||||
local itemString = TSMAPI:GetBaseItemString(itemLink, true)
|
||||
local have = TSM.getContainerItemQty(splitMoves[i].bag, splitMoves[i].slot)
|
||||
local need = splitMoves[i].quantity
|
||||
if have and need then
|
||||
local destBag, destSlot
|
||||
destBag, destSlot = findExistingStack(itemLink, bankType, need)
|
||||
if destBag and destSlot then
|
||||
TSM.splitContainerItemSrc(splitMoves[i].bag, splitMoves[i].slot, need)
|
||||
TSM.pickupContainerItemDest(destBag, destSlot)
|
||||
else
|
||||
local emptyBankSlots = GetEmptySlots(bankType)
|
||||
destBag = canGoInBag(itemString, getContainerTable(bankType))
|
||||
if emptyBankSlots[destBag] then
|
||||
destSlot = emptyBankSlots[destBag][1]
|
||||
end
|
||||
if destBag and destSlot then
|
||||
if bankType == "guildbank" then
|
||||
if GetCurrentGuildBankTab() ~= destBag then
|
||||
SetCurrentGuildBankTab(destBag)
|
||||
end
|
||||
end
|
||||
if GetEmptySlotCount(destBag) then
|
||||
TSM.splitContainerItemSrc(splitMoves[i].bag, splitMoves[i].slot, need)
|
||||
TSM.pickupContainerItemDest(destBag, destSlot)
|
||||
else
|
||||
TSMAPI:CancelFrame("moveSplitItem")
|
||||
TSMAPI:CreateTimeDelay("generateMoves", 0.4, TSM.generateMoves)
|
||||
end
|
||||
else
|
||||
if next(GetEmptySlots(bankType)) ~= nil then
|
||||
TSM.splitContainerItemSrc(splitMoves[i].bag, splitMoves[i].slot, need)
|
||||
TSM.pickupContainerItemDest(destBag, destSlot)
|
||||
else
|
||||
TSMAPI:CancelFrame("moveSplitItem")
|
||||
TSMAPI:CreateTimeDelay("generateMoves", 0.4, TSM.generateMoves)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
TSMAPI:CancelFrame("moveSplitItem")
|
||||
TSMAPI:CreateTimeDelay("generateMoves", 0.2, TSM.generateMoves)
|
||||
end
|
||||
else
|
||||
setSrcBagFunctions(bankType)
|
||||
setDestBagFunctions("bags")
|
||||
local itemLink = TSM.getContainerItemLinkSrc(splitMoves[i].bag, splitMoves[i].slot)
|
||||
local itemString = TSMAPI:GetBaseItemString(itemLink, true)
|
||||
local have = TSM.getContainerItemQty(splitMoves[i].bag, splitMoves[i].slot)
|
||||
local need = splitMoves[i].quantity
|
||||
if have and need then
|
||||
local destBag, destSlot
|
||||
destBag, destSlot = findExistingStack(itemLink, "bags", need)
|
||||
if destBag and destSlot then
|
||||
TSM.splitContainerItemSrc(splitMoves[i].bag, splitMoves[i].slot, need)
|
||||
TSM.pickupContainerItemDest(destBag, destSlot)
|
||||
else
|
||||
local emptyBagSlots = GetEmptySlots("bags")
|
||||
destBag = canGoInBag(itemString, getContainerTable("bags"))
|
||||
if emptyBagSlots[destBag] then
|
||||
destSlot = emptyBagSlots[destBag][1]
|
||||
end
|
||||
if destBag and destSlot then
|
||||
if bankType == "guildbank" then
|
||||
if GetCurrentGuildBankTab() ~= splitMoves[i].bag then
|
||||
SetCurrentGuildBankTab(splitMoves[i].bag)
|
||||
end
|
||||
end
|
||||
TSM.splitContainerItemSrc(splitMoves[i].bag, splitMoves[i].slot, need)
|
||||
TSM.pickupContainerItemDest(destBag, destSlot)
|
||||
end
|
||||
end
|
||||
else
|
||||
TSMAPI:CancelFrame("moveSplitItem")
|
||||
TSMAPI:CreateTimeDelay("generateMoves", 0.2, TSM.generateMoves)
|
||||
end
|
||||
end
|
||||
tremove(splitMoves, i)
|
||||
else
|
||||
TSMAPI:CancelFrame("moveSplitItem")
|
||||
TSMAPI:CreateTimeDelay("generateMoves", 0.2, TSM.generateMoves)
|
||||
end
|
||||
end
|
||||
|
||||
function TSMAPI:MoveItems(requestedItems, callback, includeSoulbound)
|
||||
wipe(bagState)
|
||||
|
||||
if callback then
|
||||
assert(type(callback) == "function", format("Expected function, got %s.", type(callback)))
|
||||
tinsert(callbackMsg, callback)
|
||||
end
|
||||
|
||||
|
||||
bagState = getTotalItems("bags") -- create initial bagstate
|
||||
|
||||
-- iterates over the requested items and adjusts bagState quantities , negative removes from bagState, positive adds to bagState
|
||||
-- this gives the final states to generate the moves from
|
||||
for itemString, qty in pairs(requestedItems) do
|
||||
if not bagState[itemString] then bagState[itemString] = 0
|
||||
end
|
||||
bagState[itemString] = bagState[itemString] + qty
|
||||
if bagState[itemString] < 0 then
|
||||
bagState[itemString] = 0
|
||||
end
|
||||
end
|
||||
|
||||
TSMAPI:CreateTimeDelay("generateMoves", 0.2, TSM.generateMoves(includeSoulbound))
|
||||
end
|
||||
|
||||
function TSM:areBanksVisible()
|
||||
if BagnonFrameguildbank and BagnonFrameguildbank:IsVisible() then
|
||||
return true
|
||||
elseif BagnonFramebank and BagnonFramebank:IsVisible() then
|
||||
return true
|
||||
elseif GuildBankFrame and GuildBankFrame:IsVisible() then
|
||||
return true
|
||||
elseif BankFrame and BankFrame:IsVisible() then
|
||||
return true
|
||||
elseif (ARKINV_Frame4 and ARKINV_Frame4:IsVisible()) or (ARKINV_Frame3 and ARKINV_Frame3:IsVisible()) then
|
||||
return true
|
||||
elseif (BagginsBag8 and BagginsBag8:IsVisible()) or (BagginsBag9 and BagginsBag9:IsVisible()) or (BagginsBag10 and BagginsBag10:IsVisible()) or (BagginsBag11 and BagginsBag11:IsVisible()) or (BagginsBag12 and BagginsBag12:IsVisible()) then
|
||||
return true
|
||||
elseif (CombuctorFrame2 and CombuctorFrame2:IsVisible()) then
|
||||
return true
|
||||
elseif (BaudBagContainer2_1 and BaudBagContainer2_1:IsVisible()) then
|
||||
return true
|
||||
elseif (AdiBagsContainer2 and AdiBagsContainer2:IsVisible()) then
|
||||
return true
|
||||
elseif (OneBankFrame and OneBankFrame:IsVisible()) then
|
||||
return true
|
||||
elseif (EngBank_frame and EngBank_frame:IsVisible()) then
|
||||
return true
|
||||
elseif (TBnkFrame and TBnkFrame:IsVisible()) then
|
||||
return true
|
||||
elseif (famBankFrame and famBankFrame:IsVisible()) then
|
||||
return true
|
||||
elseif (LUIBank and LUIBank:IsVisible()) then
|
||||
return true
|
||||
elseif (ElvUI_BankContainerFrame and ElvUI_BankContainerFrame:IsVisible()) then
|
||||
return true
|
||||
elseif (TukuiBank and TukuiBank:IsShown()) then
|
||||
return true
|
||||
elseif (AdiBagsContainer1 and AdiBagsContainer1.isBank and AdiBagsContainer1:IsVisible()) or (AdiBagsContainer2 and AdiBagsContainer2.isBank and AdiBagsContainer2:IsVisible()) then
|
||||
return true
|
||||
elseif BagsFrameBank and BagsFrameBank:IsVisible() then
|
||||
return true
|
||||
elseif AspUIBank and AspUIBank:IsVisible() then
|
||||
return true
|
||||
elseif NivayacBniv_Bank and NivayacBniv_Bank:IsVisible() then
|
||||
return true
|
||||
elseif DufUIBank and DufUIBank:IsVisible() then
|
||||
return true
|
||||
end
|
||||
return nil
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,458 @@
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
-- TradeSkillMaster --
|
||||
-- http://www.curse.com/addons/wow/tradeskill-master --
|
||||
-- --
|
||||
-- A TradeSkillMaster Addon (http://tradeskillmaster.com) --
|
||||
-- All Rights Reserved* - Detailed license information included with addon. --
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
|
||||
-- This file contains price related TSMAPI functions.
|
||||
|
||||
local TSM = select(2, ...)
|
||||
local moduleObjects = TSM.moduleObjects
|
||||
local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster") -- loads the localization table
|
||||
|
||||
TSM_PRICE_TEMP = {loopError=function(str) TSM:Printf(L["Loop detected in the following custom price:"].." "..TSMAPI.Design:GetInlineColor("link")..str.."|r") end}
|
||||
local MONEY_PATTERNS = {
|
||||
"([0-9]+g[ ]*[0-9]+s[ ]*[0-9]+c)", -- g/s/c
|
||||
"([0-9]+g[ ]*[0-9]+s)", -- g/s
|
||||
"([0-9]+g[ ]*[0-9]+c)", -- g/c
|
||||
"([0-9]+s[ ]*[0-9]+c)", -- s/c
|
||||
"([0-9]+g)", -- g
|
||||
"([0-9]+s)", -- s
|
||||
"([0-9]+c)", -- c
|
||||
}
|
||||
local MATH_FUNCTIONS = {
|
||||
["avg"] = "_avg",
|
||||
["min"] = "_min",
|
||||
["max"] = "_max",
|
||||
["first"] = "_first",
|
||||
["check"] = "_check",
|
||||
}
|
||||
|
||||
function TSMAPI:GetPriceSources()
|
||||
local sources = {}
|
||||
for _, obj in pairs(moduleObjects) do
|
||||
if obj.priceSources then
|
||||
for _, info in ipairs(obj.priceSources) do
|
||||
sources[info.key] = info.label
|
||||
end
|
||||
end
|
||||
end
|
||||
return sources
|
||||
end
|
||||
|
||||
local itemValueKeyCache = {}
|
||||
function TSMAPI:GetItemValue(link, key)
|
||||
local itemLink = select(2, TSMAPI:GetSafeItemInfo(link)) or link
|
||||
if not itemLink then return end
|
||||
|
||||
-- look in module objects for this key
|
||||
if itemValueKeyCache[key] then
|
||||
local info = itemValueKeyCache[key]
|
||||
return info.callback(itemLink, info.arg)
|
||||
end
|
||||
for _, obj in pairs(moduleObjects) do
|
||||
if obj.priceSources then
|
||||
for _, info in ipairs(obj.priceSources) do
|
||||
if info.key == key then
|
||||
itemValueKeyCache[key] = info
|
||||
local value = info.callback(itemLink, info.arg)
|
||||
return (type(value) == "number" and value > 0) and value or nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- validates a price string that was passed into TSMAPI:ParseCustomPrice
|
||||
local supportedOperators = { "+", "-", "*", "/" }
|
||||
local function ParsePriceString(str, badPriceSource)
|
||||
if tonumber(str) then
|
||||
return function() return tonumber(str) end
|
||||
end
|
||||
|
||||
local origStr = str
|
||||
|
||||
-- make everything lower case
|
||||
str = strlower(str)
|
||||
|
||||
|
||||
-- remove any colors around gold/silver/copper
|
||||
str = gsub(str, "|cff([0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])g|r", "g")
|
||||
str = gsub(str, "|cff([0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])s|r", "s")
|
||||
str = gsub(str, "|cff([0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])c|r", "c")
|
||||
|
||||
-- replace all formatted gold amount with their copper value
|
||||
local start = 1
|
||||
local goldAmountContinue = true
|
||||
while goldAmountContinue do
|
||||
goldAmountContinue = false
|
||||
local minFind = {}
|
||||
for _, pattern in ipairs(MONEY_PATTERNS) do
|
||||
local s, e, sub = strfind(str, pattern, start)
|
||||
if s and (not minFind.s or minFind.s > s) then
|
||||
minFind.s = s
|
||||
minFind.e = e
|
||||
minFind.sub = sub
|
||||
end
|
||||
end
|
||||
if minFind.s then
|
||||
local value = TSMAPI:UnformatTextMoney(minFind.sub)
|
||||
if not value then return end -- sanity check
|
||||
local preStr = strsub(str, 1, minFind.s-1)
|
||||
local postStr = strsub(str, minFind.e+1)
|
||||
str = preStr .. value .. postStr
|
||||
start = #str - #postStr + 1
|
||||
goldAmountContinue = true
|
||||
end
|
||||
end
|
||||
|
||||
-- remove up to 1 occurance of convert(priceSource[, item])
|
||||
local convertPriceSource, convertItem
|
||||
local _, _, convertParams = strfind(str, "convert%(([^%)]+)%)")
|
||||
if convertParams then
|
||||
local source
|
||||
local s = strfind(convertParams, "|c")
|
||||
if s then
|
||||
local _, e = strfind(convertParams, "|r")
|
||||
local itemString = e and TSMAPI:GetItemString(strsub(convertParams, s, e))
|
||||
if not itemString then return nil, L["Invalid item link."] end -- there's an invalid item link in the convertParams
|
||||
convertItem = itemString
|
||||
source = strsub(convertParams, 1, s - 1)
|
||||
elseif strfind(convertParams, "item:") then
|
||||
local s, e = strfind(convertParams, "item:([0-9]+):?([0-9]*):?([0-9]*):?([0-9]*):?([0-9]*):?([0-9]*):?([0-9]*)")
|
||||
convertItem = strsub(convertParams, s, e)
|
||||
source = strsub(convertParams, 1, s - 1)
|
||||
elseif strfind(convertParams, "battlepet:") then
|
||||
local s, e = strfind(convertParams, "item:([0-9]+):?([0-9]*):?([0-9]*):?([0-9]*):?([0-9]*):?([0-9]*):?([0-9]*)")
|
||||
convertItem = strsub(convertParams, s, e)
|
||||
source = strsub(convertParams, 1, s - 1)
|
||||
else
|
||||
source = convertParams
|
||||
end
|
||||
source = gsub(source:trim(), ",$", ""):trim()
|
||||
for key in pairs(TSMAPI:GetPriceSources()) do
|
||||
if strlower(key) == source then
|
||||
convertPriceSource = key
|
||||
break
|
||||
end
|
||||
end
|
||||
if not convertPriceSource then
|
||||
return nil, L["Invalid price source in convert."]
|
||||
end
|
||||
local num = 0
|
||||
str, num = gsub(str, "convert%(([^%)]+)%)", "~convert~")
|
||||
if num > 1 then
|
||||
return nil, L["A maximum of 1 convert() function is allowed."]
|
||||
end
|
||||
end
|
||||
|
||||
-- replace all item links with "~item~"
|
||||
local items = {}
|
||||
while true do
|
||||
local s = strfind(str, "|c")
|
||||
if not s then break end -- no more item links
|
||||
local _, e = strfind(str, "|r")
|
||||
local itemString = e and TSMAPI:GetItemString(strsub(str, s, e))
|
||||
if not itemString then return nil, L["Invalid item link."] end -- there's an invalid item link in the str
|
||||
tinsert(items, itemString)
|
||||
str = strsub(str, 1, s - 1) .. "~item~" .. strsub(str, e + 1)
|
||||
end
|
||||
|
||||
-- replace all itemStrings with "~item~"
|
||||
while true do
|
||||
local s, e
|
||||
if strfind(str, "item:") then
|
||||
s, e = strfind(str, "item:([0-9]+):?([0-9]*):?([0-9]*):?([0-9]*):?([0-9]*):?([0-9]*):?([0-9]*)")
|
||||
elseif strfind(str, "battlepet:") then
|
||||
s, e = strfind(str, "battlepet:([0-9]+):?([0-9]*):?([0-9]*):?([0-9]*):?([0-9]*):?([0-9]*):?([0-9]*)")
|
||||
else
|
||||
break
|
||||
end
|
||||
local itemString = strsub(str, s, e)
|
||||
tinsert(items, itemString)
|
||||
str = strsub(str, 1, s - 1) .. "~item~" .. strsub(str, e + 1)
|
||||
end
|
||||
|
||||
-- make sure there's spaces on either side of operators
|
||||
for _, operator in ipairs(supportedOperators) do
|
||||
str = gsub(str, TSMAPI:StrEscape(operator), " " .. operator .. " ")
|
||||
end
|
||||
|
||||
-- add space to start of string for percentage matching
|
||||
str = " "..str
|
||||
-- fix any whitespace issues around item links and remove parenthesis
|
||||
str = gsub(str, "%(([ ]*)~item~([ ]*)%)", " ~item~")
|
||||
-- ensure a space on either side of parenthesis and commas
|
||||
str = gsub(str, "%(", " ( ")
|
||||
str = gsub(str, "%)", " ) ")
|
||||
str = gsub(str, ",", " , ")
|
||||
-- remove any occurances of more than one consecutive space
|
||||
str = gsub(str, "([ ]+)", " ")
|
||||
|
||||
-- ensure equal number of left/right parenthesis
|
||||
if select(2, gsub(str, "%(", "")) ~= select(2, gsub(str, "%)", "")) then return nil, L["Unbalanced parentheses."] end
|
||||
|
||||
-- convert percentages to decimal numbers
|
||||
for leading, pctValue in gmatch(str, "([^0-9%.])([0-9%.]+)%%") do
|
||||
if tonumber(pctValue) then
|
||||
local number = tonumber(pctValue) / 100
|
||||
str = gsub(str, leading..pctValue.."%%", leading .. number .. " *")
|
||||
end
|
||||
end
|
||||
|
||||
-- create array of valid price sources
|
||||
local priceSourceKeys = {}
|
||||
for key in pairs(TSMAPI:GetPriceSources()) do
|
||||
tinsert(priceSourceKeys, strlower(key))
|
||||
end
|
||||
for key in pairs(TSM.db.global.customPriceSources) do
|
||||
tinsert(priceSourceKeys, strlower(key))
|
||||
end
|
||||
|
||||
-- validate all words in the string
|
||||
local parts = TSMAPI:SafeStrSplit(str:trim(), " ")
|
||||
for i, word in ipairs(parts) do
|
||||
if tContains(supportedOperators, word) then
|
||||
if i == #parts then
|
||||
return nil, L["Invalid operator at end of custom price."]
|
||||
end
|
||||
-- valid operand
|
||||
elseif badPriceSource == word then
|
||||
-- price source that's explicitly invalid
|
||||
return nil, format(L["You cannot use %s as part of this custom price."], word)
|
||||
elseif tContains(priceSourceKeys, word) then
|
||||
-- valid price source
|
||||
elseif tonumber(word) then
|
||||
-- make sure it's not an itemID (incorrect)
|
||||
if i > 2 and parts[i-1] == "(" and tContains(priceSourceKeys, parts[i-2]) then
|
||||
return nil, L["Invalid parameter to price source."]
|
||||
end
|
||||
-- valid number
|
||||
elseif word == "~item~" then
|
||||
-- make sure previous word was a price source
|
||||
if i > 1 and tContains(priceSourceKeys, parts[i-1]) then
|
||||
-- valid item parameter
|
||||
else
|
||||
return nil, L["Item links may only be used as parameters to price sources."]
|
||||
end
|
||||
elseif word == "(" or word == ")" then
|
||||
-- valid parenthesis
|
||||
elseif word == "," then
|
||||
if not parts[i+1] or parts[i+1] == ")" then
|
||||
return nil, L["Misplaced comma"]
|
||||
else
|
||||
-- we're hoping this is a valid comma within a function, will be caught by loadstring otherwise
|
||||
end
|
||||
elseif MATH_FUNCTIONS[word] then
|
||||
if not parts[i+1] or parts[i+1] ~= "(" then
|
||||
return nil, format(L["Invalid word: '%s'"], word)
|
||||
end
|
||||
-- valid math function
|
||||
elseif word == "~convert~" then
|
||||
-- valid convert statement
|
||||
elseif word:trim() == "" then
|
||||
-- harmless extra spaces
|
||||
else
|
||||
return nil, format(L["Invalid word: '%s'"], word)
|
||||
end
|
||||
end
|
||||
|
||||
for key in pairs(TSMAPI:GetPriceSources()) do
|
||||
-- replace all "<priceSource> ~item~" occurances with the parameters to TSMAPI:GetItemValue (with "~item~" left in for the item)
|
||||
for match in gmatch(" "..str.." ", " "..strlower(key).." ~item~") do
|
||||
match = match:trim()
|
||||
str = gsub(str, match, "(\"~item~\",\"" .. key .. "\",\"reg\")")
|
||||
end
|
||||
-- replace all "<priceSource>" occurances with the parameters to TSMAPI:GetItemValue (with _item for the item)
|
||||
for match in gmatch(" "..str.." ", " "..strlower(key).." ") do
|
||||
match = match:trim()
|
||||
str = gsub(str, match, "(\"_item\",\"" .. key .. "\",\"reg\")")
|
||||
end
|
||||
end
|
||||
|
||||
for key in pairs(TSM.db.global.customPriceSources) do
|
||||
-- price sources need to have at least 1 capital letter for this algorithm to work, so temporarily give it one
|
||||
key = strupper(strsub(key, 1, 1))..strsub(key, 2)
|
||||
-- replace all "<customPriceSource> ~item~" occurances with the parameters to TSMAPI:GetCustomPriceSourceValue (with "~item~" left in for the item)
|
||||
for match in gmatch(" "..str.." ", " " .. strlower(key) .. " ~item~") do
|
||||
match = match:trim()
|
||||
str = gsub(str, match, "(\"~item~\",\"" .. key .. "\",\"custom\")")
|
||||
end
|
||||
-- replace all "<customPriceSource>" occurances with the parameters to TSMAPI:GetCustomPriceSourceValue (with _item for the item)
|
||||
for match in gmatch(" "..str.." ", " " .. strlower(key) .. " ") do
|
||||
match = match:trim()
|
||||
str = gsub(str, match, "(\"_item\",\"" .. key .. "\",\"custom\")")
|
||||
end
|
||||
|
||||
-- change custom price sources back to lower case
|
||||
str = gsub(str, TSMAPI:StrEscape("(\"~item~\",\"" .. key .. "\",\"custom\")"), strlower("(\"~item~\",\"" .. key .. "\",\"custom\")"))
|
||||
str = gsub(str, TSMAPI:StrEscape("(\"_item\",\"" .. key .. "\",\"custom\")"), strlower("(\"_item\",\"" .. key .. "\",\"custom\")"))
|
||||
end
|
||||
|
||||
-- replace all occurances of "~item~" with the item link
|
||||
for match in gmatch(str, "~item~") do
|
||||
local itemString = tremove(items, 1)
|
||||
if not itemString then return nil, L["Wrong number of item links."] end
|
||||
str = gsub(str, match, itemString, 1)
|
||||
end
|
||||
|
||||
-- replace any itemValue API calls with a lookup in the 'values' array
|
||||
local itemValues = {}
|
||||
for match in gmatch(str, "%(\"([^%)]+)%)") do
|
||||
local index = #itemValues + 1
|
||||
itemValues[index] = "{\"" .. match .. "}"
|
||||
str = gsub(str, TSMAPI:StrEscape("(\"" .. match .. ")"), "values[" .. index .. "]")
|
||||
end
|
||||
|
||||
-- replace "~convert~" appropriately
|
||||
if convertPriceSource then
|
||||
tinsert(itemValues, "{\"" .. (convertItem or "_item") .. "\",\"convert\",\"" .. convertPriceSource .. "\"}")
|
||||
str = gsub(str, "~convert~", "values[" .. #itemValues .. "]")
|
||||
end
|
||||
|
||||
-- replace math functions special custom function names
|
||||
for word, funcName in pairs(MATH_FUNCTIONS) do
|
||||
str = gsub(str, word, funcName)
|
||||
end
|
||||
|
||||
-- remove any unused values
|
||||
for i in ipairs(itemValues) do
|
||||
if not strfind(" "..str.." ", " values%["..i.."%] ") then
|
||||
itemValues[i] = "{}"
|
||||
end
|
||||
end
|
||||
|
||||
-- finally, create and return the function
|
||||
local funcTemplate = [[
|
||||
return function(_item)
|
||||
local NAN = math.huge*0
|
||||
local origStr = %s
|
||||
local isTop
|
||||
if not TSM_PRICE_TEMP.num then
|
||||
TSM_PRICE_TEMP.num = 0
|
||||
isTop = true
|
||||
end
|
||||
TSM_PRICE_TEMP.num = TSM_PRICE_TEMP.num + 1
|
||||
if TSM_PRICE_TEMP.num > 100 then
|
||||
if (TSM_PRICE_TEMP.lastPrint or 0) + 1 < time() then
|
||||
TSM_PRICE_TEMP.lastPrint = time()
|
||||
TSM_PRICE_TEMP.loopError(origStr)
|
||||
end
|
||||
return
|
||||
end
|
||||
local function isNAN(num)
|
||||
return tostring(num) == tostring(NAN)
|
||||
end
|
||||
local function _min(...)
|
||||
local nums = {...}
|
||||
for i=#nums, 1, -1 do
|
||||
if isNAN(nums[i]) then
|
||||
tremove(nums, i)
|
||||
end
|
||||
end
|
||||
if #nums == 0 then return NAN end
|
||||
return min(unpack(nums))
|
||||
end
|
||||
local function _max(...)
|
||||
local nums = {...}
|
||||
for i=#nums, 1, -1 do
|
||||
if isNAN(nums[i]) then
|
||||
tremove(nums, i)
|
||||
end
|
||||
end
|
||||
if #nums == 0 then return NAN end
|
||||
return max(unpack(nums))
|
||||
end
|
||||
local function _first(...)
|
||||
local nums = {...}
|
||||
for i=1, #nums do
|
||||
if type(nums[i]) == "number" and not isNAN(nums[i]) then
|
||||
return nums[i]
|
||||
end
|
||||
end
|
||||
return NAN
|
||||
end
|
||||
local function _avg(...)
|
||||
local nums = {...}
|
||||
local total, count = 0, 0
|
||||
for i=#nums, 1, -1 do
|
||||
if type(nums[i]) == "number" and not isNAN(nums[i]) then
|
||||
total = total + nums[i]
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
if count == 0 then return NAN end
|
||||
return floor(total / count + 0.5)
|
||||
end
|
||||
local function _check(...)
|
||||
if select('#', ...) > 3 then return end
|
||||
local check, ifValue, elseValue = ...
|
||||
check = check or NAN
|
||||
ifValue = ifValue or NAN
|
||||
elseValue = elseValue or NAN
|
||||
return check > 0 and ifValue or elseValue
|
||||
end
|
||||
local values = {}
|
||||
for i, params in ipairs({%s}) do
|
||||
local itemString, key, extraParam = unpack(params)
|
||||
if itemString then
|
||||
itemString = (itemString == "_item") and _item or itemString
|
||||
if key == "convert" then
|
||||
values[i] = TSMAPI:GetConvertCost(itemString, extraParam)
|
||||
elseif extraParam == "custom" then
|
||||
values[i] = TSMAPI:GetCustomPriceSourceValue(itemString, key)
|
||||
else
|
||||
values[i] = TSMAPI:GetItemValue(itemString, key)
|
||||
end
|
||||
values[i] = values[i] or NAN
|
||||
end
|
||||
end
|
||||
local result = floor((%s) + 0.5)
|
||||
if TSM_PRICE_TEMP.num then
|
||||
TSM_PRICE_TEMP.num = TSM_PRICE_TEMP.num - 1
|
||||
end
|
||||
if isTop then
|
||||
TSM_PRICE_TEMP.num = nil
|
||||
end
|
||||
return not isNAN(result) and result or nil
|
||||
end
|
||||
]]
|
||||
local func, loadErr = loadstring(format(funcTemplate, "\""..origStr.."\"", table.concat(itemValues, ","), str))
|
||||
if loadErr then
|
||||
loadErr = gsub(loadErr:trim(), "([^:]+):.", "")
|
||||
return nil, L["Invalid function."].." Details: "..loadErr
|
||||
end
|
||||
local success, func = pcall(func)
|
||||
if not success then return nil, L["Invalid function."] end
|
||||
return func
|
||||
end
|
||||
|
||||
local customPriceCache = {}
|
||||
local badCustomPriceCache = {}
|
||||
function TSMAPI:ParseCustomPrice(priceString, badPriceSource)
|
||||
priceString = strlower(tostring(priceString):trim())
|
||||
if priceString == "" then return nil, L["Empty price string."] end
|
||||
if badCustomPriceCache[priceString] then return nil, badCustomPriceCache[priceString] end
|
||||
if customPriceCache[priceString] then return customPriceCache[priceString] end
|
||||
|
||||
local func, err = ParsePriceString(priceString, badPriceSource)
|
||||
if err then
|
||||
badCustomPriceCache[priceString] = err
|
||||
return nil, err
|
||||
end
|
||||
|
||||
customPriceCache[priceString] = func
|
||||
return func
|
||||
end
|
||||
|
||||
function TSMAPI:GetCustomPriceSourceValue(itemString, key)
|
||||
local source = TSM.db.global.customPriceSources[key]
|
||||
if not source then return end
|
||||
local func = TSMAPI:ParseCustomPrice(source)
|
||||
if not func then return end
|
||||
return func(itemString)
|
||||
end
|
||||
@@ -0,0 +1,205 @@
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
-- TradeSkillMaster --
|
||||
-- http://www.curse.com/addons/wow/tradeskill-master --
|
||||
-- --
|
||||
-- A TradeSkillMaster Addon (http://tradeskillmaster.com) --
|
||||
-- All Rights Reserved* - Detailed license information included with addon. --
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
|
||||
-- This file handled multi-account syncing and communication
|
||||
|
||||
-- register this file with Ace libraries
|
||||
local TSM = select(2, ...)
|
||||
local Sync = TSM:NewModule("Sync", "AceComm-3.0", "AceEvent-3.0")
|
||||
TSMAPI.Sync = {}
|
||||
local private = {callbacks={}, addedFriends={}, invalidPlayers={}}
|
||||
TSMAPI:RegisterForTracing(private, "TradeSkillMaster.Sync_private")
|
||||
local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster") -- loads the localization table
|
||||
|
||||
-- Request friend info from the server
|
||||
ShowFriends()
|
||||
|
||||
|
||||
function Sync:OnEnable()
|
||||
Sync:RegisterComm("TSMSyncData")
|
||||
Sync:RegisterEvent("CHAT_MSG_SYSTEM")
|
||||
|
||||
local data = {characters={}, accountKey=TSMAPI.Sync:GetAccountKey()}
|
||||
for name in pairs(TSM.db.factionrealm.characters) do
|
||||
data.characters[name] = TSMAPI.Sync:GetAccountKey()
|
||||
end
|
||||
TSMAPI:CreateTimeDelay("syncSetupDelay", 3, function() TSMAPI.Sync:BroadcastData("TradeSkillMaster", "SETUP", data) end)
|
||||
end
|
||||
|
||||
--Load the libraries
|
||||
local libS = LibStub:GetLibrary("AceSerializer-3.0")
|
||||
local libC = LibStub:GetLibrary("LibCompress")
|
||||
local libCE = libC:GetAddonEncodeTable()
|
||||
|
||||
function Sync:OnCommReceived(_, data, _, source)
|
||||
-- remove realm name from source
|
||||
source = ("-"):split(source)
|
||||
|
||||
-- Decode the compressed data
|
||||
data = libCE:Decode(data)
|
||||
|
||||
-- Decompress the decoded data
|
||||
local data = libC:Decompress(data)
|
||||
if not data then return end
|
||||
|
||||
-- Deserialize the decompressed data
|
||||
local success
|
||||
local tmpData = data
|
||||
success, data = libS:Deserialize(data)
|
||||
if not success or not data then return end
|
||||
|
||||
-- data is good, do callback
|
||||
local key = data.__key
|
||||
local module = data.__module
|
||||
local account = data.__account
|
||||
if not key or not module or not private.callbacks[module] then return end
|
||||
data.__key = nil
|
||||
data.__module = nil
|
||||
data.__account = nil
|
||||
|
||||
-- make sure we are getting this from a known source
|
||||
if not TSM.db.factionrealm.syncAccounts[account] and (module ~= "TradeSkillMaster" and not data.isSetup) then return end
|
||||
private.callbacks[module](key, data, source)
|
||||
end
|
||||
|
||||
function Sync:CHAT_MSG_SYSTEM(_, msg)
|
||||
if #private.addedFriends == 0 then return end
|
||||
if msg == ERR_FRIEND_NOT_FOUND then
|
||||
tremove(private.addedFriends, 1)
|
||||
else
|
||||
for i, v in ipairs(private.addedFriends) do
|
||||
if format(ERR_FRIEND_ADDED_S, v) == msg then
|
||||
tremove(private.addedFriends, i)
|
||||
private.invalidPlayers[strlower(v)] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function TSMAPI.Sync:GetAccountKey()
|
||||
return TSM.db.factionrealm.accountKey
|
||||
end
|
||||
|
||||
function TSM:RegisterSyncCallback(module, callback)
|
||||
private.callbacks[module] = callback
|
||||
end
|
||||
|
||||
function private:IsPlayerOnline(target, noAdd)
|
||||
for i=1, GetNumFriends() do
|
||||
local name, _, _, _, connected = GetFriendInfo(i)
|
||||
if name and strlower(name) == strlower(target) then
|
||||
return connected
|
||||
end
|
||||
end
|
||||
|
||||
if not noAdd and not private.invalidPlayers[strlower(target)] and GetNumFriends() ~= 50 then
|
||||
-- add them as a friend
|
||||
AddFriend(target)
|
||||
tinsert(private.addedFriends, target)
|
||||
for i=1, GetNumFriends() do
|
||||
local name, _, _, _, connected = GetFriendInfo(i)
|
||||
if name and strlower(name) == strlower(target) then
|
||||
return connected
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function TSMAPI.Sync:SendData(module, key, data, target)
|
||||
local playerOnline = private:IsPlayerOnline(target)
|
||||
if not playerOnline then return end
|
||||
|
||||
data.__module = module
|
||||
data.__key = key
|
||||
data.__account = TSMAPI.Sync:GetAccountKey()
|
||||
|
||||
-- we will encode using Huffman, LZW, and no compression separately
|
||||
-- this is to deal with a bug in the compression code
|
||||
local orig = data
|
||||
local serialized = libS:Serialize(data)
|
||||
local encodedData = {}
|
||||
encodedData[1] = libCE:Encode(libC:CompressHuffman(serialized))
|
||||
encodedData[2] = libCE:Encode(libC:CompressLZW(serialized))
|
||||
encodedData[3] = libCE:Encode("\001"..serialized)
|
||||
|
||||
-- verify each compresion and pick the shorted valid one
|
||||
local minIndex = -1
|
||||
local minLen = math.huge
|
||||
for i=#encodedData, 1, -1 do
|
||||
local test = libC:Decompress(libCE:Decode(encodedData[i]))
|
||||
if test and test == serialized and #encodedData[i] < minLen then
|
||||
minLen = #encodedData[i]
|
||||
minIndex = i
|
||||
end
|
||||
end
|
||||
|
||||
-- sanity check
|
||||
if not encodedData[minIndex] then error("Could not encode data.") end
|
||||
-- send off the serialized, compressed, and encoded data
|
||||
Sync:SendCommMessage("TSMSyncData", encodedData[minIndex], "WHISPER", target)
|
||||
end
|
||||
|
||||
|
||||
function TSMAPI.Sync:BroadcastData(module, key, data)
|
||||
for account, players in pairs(TSM.db.factionrealm.syncAccounts) do
|
||||
if account ~= TSMAPI.Sync:GetAccountKey() then
|
||||
local sent
|
||||
for player in pairs(players) do
|
||||
if private:IsPlayerOnline(player, true) then
|
||||
TSMAPI.Sync:SendData(module, key, data, player)
|
||||
sent = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not sent then
|
||||
for player in pairs(players) do
|
||||
if private:IsPlayerOnline(player) then
|
||||
TSMAPI.Sync:SendData(module, key, data, player)
|
||||
sent = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function private:SendSetupData(target, isResponse, isSetup)
|
||||
local data = {isResponse=isResponse, isSetup=isSetup, characters={}, accountKey=TSMAPI.Sync:GetAccountKey()}
|
||||
for name in pairs(TSM.db.factionrealm.characters) do
|
||||
data.characters[name] = true
|
||||
end
|
||||
TSMAPI.Sync:SendData("TradeSkillMaster", "SETUP", data, target)
|
||||
end
|
||||
|
||||
function TSM:DoSyncSetup(target)
|
||||
if target == "" then
|
||||
private.syncSetupTarget = nil
|
||||
return
|
||||
end
|
||||
private.syncSetupTarget = target
|
||||
private:SendSetupData(target, nil, true)
|
||||
end
|
||||
|
||||
function TSM:SyncCallback(key, data, source)
|
||||
if key == "SETUP" then
|
||||
if (data.isSetup and strlower(source) ~= strlower(private.syncSetupTarget or "")) or (not data.isSetup and not TSM.db.factionrealm.syncAccounts[data.accountKey]) then
|
||||
return
|
||||
end
|
||||
TSMAPI:Verify(data.accountKey ~= TSMAPI.Sync:GetAccountKey(), "It appears that you've manually copied your saved variables between accounts which will cause TSM's automatic sync'ing to not work. You'll need to undo this, and/or delete the TradeSkillMaster, TSM_Crafting, and TSM_ItemTracker saved variables files on both accounts (with WoW closed) in order to fix this.")
|
||||
TSM.db.factionrealm.syncAccounts[data.accountKey] = data.characters
|
||||
if data.isSetup then
|
||||
TSMAPI:CloseFrame()
|
||||
TSM:Printf(L["Setup account sync'ing with the account which '%s' is on."], source)
|
||||
end
|
||||
if data.isResponse then return end -- already responded
|
||||
private:SendSetupData(source, true, data.isSetup)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
<Ui>
|
||||
<Frame name="TSMErrorHandlerTemplate" virtual="true">
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
self.handler = function(...)
|
||||
local msg = LibStub("AceAddon-3.0"):GetAddon("TradeSkillMaster"):IsValidError(...)
|
||||
if msg then
|
||||
local status, ret = pcall(self.errorHandler, msg, ...)
|
||||
if status then
|
||||
return ret
|
||||
else
|
||||
self.origErrorHandler(...)
|
||||
end
|
||||
elseif self.origErrorHandler then
|
||||
return self.origErrorHandler(...)
|
||||
end
|
||||
end
|
||||
</OnLoad>
|
||||
</Scripts>
|
||||
</Frame>
|
||||
</Ui>
|
||||
@@ -0,0 +1,113 @@
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
-- TradeSkillMaster --
|
||||
-- http://www.curse.com/addons/wow/tradeskill-master --
|
||||
-- --
|
||||
-- A TradeSkillMaster Addon (http://tradeskillmaster.com) --
|
||||
-- All Rights Reserved* - Detailed license information included with addon. --
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
|
||||
-- This file contains code for running stuff in a pseudo-thread
|
||||
|
||||
local TSM = select(2, ...)
|
||||
local private = {frames={}, threads={}}
|
||||
TSMAPI:RegisterForTracing(private, "TradeSkillMaster.Threading_private")
|
||||
TSMAPI.Threading = {}
|
||||
local STATUS_YIELD = {}
|
||||
local STATUS_DEAD = {}
|
||||
local MAX_QUANTUM = 50
|
||||
|
||||
|
||||
local ThreadPrototype = {
|
||||
Resume = function(self, runTime)
|
||||
self._endTime = debugprofilestop() + runTime
|
||||
local noErr, status = coroutine.resume(self._co, self, self._param)
|
||||
if noErr then
|
||||
return status
|
||||
else
|
||||
-- throw error in delay so we don't kill this execution path
|
||||
TSMAPI:CreateTimeDelay(0, function() error(status) end)
|
||||
return STATUS_DEAD
|
||||
end
|
||||
end,
|
||||
|
||||
Yield = function(self, force)
|
||||
local currentTime = debugprofilestop()
|
||||
if force or currentTime > self._endTime then
|
||||
coroutine.yield(STATUS_YIELD)
|
||||
end
|
||||
end,
|
||||
|
||||
Sleep = function(self, seconds)
|
||||
self._sleeping = seconds
|
||||
coroutine.yield(STATUS_YIELD)
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
|
||||
local quantums = {}
|
||||
function private.RunScheduler(_, elapsed)
|
||||
-- deal with sleeping threads and try and assign requested quantums
|
||||
local totalTime = min(elapsed * 1000, MAX_QUANTUM)
|
||||
local usedTime = 0
|
||||
for i, thread in ipairs(private.threads) do
|
||||
if thread._sleeping then
|
||||
thread._sleeping = thread._sleeping - elapsed
|
||||
if thread._sleeping <= 0 then
|
||||
thread._sleeping = nil
|
||||
end
|
||||
end
|
||||
quantums[i] = thread._sleeping and 0 or (thread._percent * totalTime)
|
||||
usedTime = usedTime + quantums[i]
|
||||
end
|
||||
|
||||
-- scale everything down if the total is > the total time
|
||||
if usedTime > totalTime then
|
||||
for i=1, #quantums do
|
||||
quantums[i] = quantums[i] * (totalTime / usedTime)
|
||||
end
|
||||
end
|
||||
|
||||
-- run the threads that aren't sleeping
|
||||
for i, thread in ipairs(private.threads) do
|
||||
if quantums[i] > 0 then
|
||||
-- resume running for a quantum based on its percent
|
||||
local status = thread:Resume(quantums[i])
|
||||
if status ~= STATUS_YIELD then
|
||||
tremove(private.threads, i)
|
||||
if thread._callback and status ~= STATUS_DEAD then thread._callback() end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function TSMAPI.Threading:Start(func, percent, callback, param)
|
||||
local thread = CopyTable(ThreadPrototype)
|
||||
thread._co = coroutine.create(func)
|
||||
thread._percent = percent
|
||||
thread._callback = callback
|
||||
thread._param = param
|
||||
thread._sleeping = nil
|
||||
tinsert(private.threads, thread)
|
||||
end
|
||||
|
||||
do
|
||||
local frame = CreateFrame("Frame")
|
||||
frame:SetScript("OnUpdate", private.RunScheduler)
|
||||
end
|
||||
|
||||
-- -- EXAMPLE USAGE:
|
||||
|
||||
-- local function TestFunc(self, letter)
|
||||
-- for i = 1, 10 do
|
||||
-- self:Sleep(1)
|
||||
-- print(letter, i)
|
||||
-- if letter == "B" and i == 10 then print(_G[i].nonExistant) end
|
||||
-- end
|
||||
-- end
|
||||
|
||||
-- function TSMTest()
|
||||
-- local start = GetTime()
|
||||
-- TSMAPI.Threading:Start(TestFunc, 1, function() print("DONE", GetTime()-start) end, "A")
|
||||
-- TSMAPI.Threading:Start(TestFunc, 1, function() print("DONE", GetTime()-start) end, "B")
|
||||
-- end
|
||||
@@ -0,0 +1,300 @@
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
-- TradeSkillMaster --
|
||||
-- http://www.curse.com/addons/wow/tradeskill-master --
|
||||
-- --
|
||||
-- A TradeSkillMaster Addon (http://tradeskillmaster.com) --
|
||||
-- All Rights Reserved* - Detailed license information included with addon. --
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
|
||||
-- This file contains all the code for the new tooltip options
|
||||
|
||||
local TSM = select(2, ...)
|
||||
local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster") -- loads the localization table
|
||||
local AceGUI = LibStub("AceGUI-3.0") -- load the AceGUI libraries
|
||||
local lib = TSMAPI
|
||||
local tooltipLib = LibStub("LibExtraTip-1")
|
||||
local moduleObjects = TSM.moduleObjects
|
||||
local moduleNames = TSM.moduleNames
|
||||
|
||||
local private = {}
|
||||
TSMAPI:RegisterForTracing(private, "TradeSkillMaster.Tooltips_private")
|
||||
private.tooltipInfo = {}
|
||||
|
||||
-- **************************************************************************
|
||||
-- LibExtraTip Functions
|
||||
-- **************************************************************************
|
||||
|
||||
function TSM:SetupTooltips()
|
||||
-- tooltipLib:AddCallback({type = "battlepet", callback = private.LoadTooltip})
|
||||
tooltipLib:AddCallback({type = "item", callback = private.LoadTooltip})
|
||||
tooltipLib:RegisterTooltip(GameTooltip)
|
||||
tooltipLib:RegisterTooltip(ItemRefTooltip)
|
||||
-- tooltipLib:RegisterTooltip(BattlePetTooltip)
|
||||
local orig = OpenMailAttachment_OnEnter
|
||||
OpenMailAttachment_OnEnter = function(self, index)
|
||||
private.lastMailTooltipUpdate = private.lastMailTooltipUpdate or 0
|
||||
if private.lastMailTooltipIndex ~= index or private.lastMailTooltipUpdate + 0.1 < GetTime() then
|
||||
private.lastMailTooltipUpdate = GetTime()
|
||||
private.lastMailTooltipIndex = index
|
||||
orig(self, index)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local tooltipLines = {lastUpdate = 0, modifier=0}
|
||||
local function GetTooltipLines(itemString, quantity)
|
||||
local modifier = (IsShiftKeyDown() and 4 or 0) + (IsAltKeyDown() and 2 or 0) + (IsControlKeyDown() and 1 or 0)
|
||||
if modifier ~= tooltipLines.modifier then
|
||||
tooltipLines.modifier = modifier
|
||||
tooltipLines.lastUpdate = 0
|
||||
end
|
||||
if tooltipLines.itemString ~= itemString or tooltipLines.quantity ~= quantity or (tooltipLines.lastUpdate + 0.5) < GetTime() then
|
||||
wipe(tooltipLines)
|
||||
for _, moduleName in ipairs(moduleNames) do
|
||||
if moduleObjects[moduleName].GetTooltip then
|
||||
local moduleLines = moduleObjects[moduleName]:GetTooltip(itemString, quantity)
|
||||
if type(moduleLines) ~= "table" then moduleLines = {} end
|
||||
for _, line in ipairs(moduleLines) do
|
||||
tinsert(tooltipLines, line)
|
||||
end
|
||||
end
|
||||
end
|
||||
tooltipLines.itemString = itemString
|
||||
tooltipLines.quantity = quantity
|
||||
tooltipLines.lastUpdate = GetTime()
|
||||
end
|
||||
return tooltipLines
|
||||
end
|
||||
|
||||
function private.LoadTooltip(tipFrame, link, quantity)
|
||||
local itemString = TSMAPI:GetItemString(link)
|
||||
if not itemString then return end
|
||||
local lines = GetTooltipLines(itemString, quantity)
|
||||
if #lines > 0 then
|
||||
tooltipLib:AddLine(tipFrame, " ", 1, 1, 0, TSM.db.profile.embeddedTooltip)
|
||||
local r, g, b = unpack(TSM.db.profile.design.inlineColors.tooltip or { 130, 130, 250 })
|
||||
|
||||
for i = 1, #lines do
|
||||
if type(lines[i]) == "table" then
|
||||
tooltipLib:AddDoubleLine(tipFrame, lines[i].left, lines[i].right, r / 255, g / 255, b / 255, r / 255, g / 255, b / 255, TSM.db.profile.embeddedTooltip)
|
||||
else
|
||||
tooltipLib:AddLine(tipFrame, lines[i], r / 255, g / 255, b / 255, TSM.db.profile.embeddedTooltip)
|
||||
end
|
||||
end
|
||||
tooltipLib:AddLine(tipFrame, " ", 1, 1, 0, TSM.db.profile.embeddedTooltip)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- **************************************************************************
|
||||
-- TSM Tooltip Options
|
||||
-- **************************************************************************
|
||||
|
||||
function TSM:RegisterTooltipInfo(module, info)
|
||||
info = CopyTable(info)
|
||||
info.module = module
|
||||
tinsert(private.tooltipInfo, info)
|
||||
end
|
||||
|
||||
function TSMAPI:GetMoneyCoinsTooltip()
|
||||
return TSM.db.profile.moneyCoinsTooltip
|
||||
end
|
||||
|
||||
local loadTooltipOptionsTab
|
||||
function TSM:LoadTooltipOptions(parent)
|
||||
local tabs = {}
|
||||
local next = next
|
||||
|
||||
for _, info in ipairs(private.tooltipInfo) do
|
||||
tinsert(tabs, { text = info.module, value = info.module })
|
||||
end
|
||||
|
||||
if next(tabs) then
|
||||
sort(tabs, function(a, b)
|
||||
return a.text < b.text
|
||||
end)
|
||||
end
|
||||
|
||||
tinsert(tabs, 1, { text = L["General"], value = "Help" })
|
||||
|
||||
local tabGroup = AceGUI:Create("TSMTabGroup")
|
||||
tabGroup:SetLayout("Fill")
|
||||
tabGroup:SetTabs(tabs)
|
||||
tabGroup:SetCallback("OnGroupSelected", function(_, _, value)
|
||||
tabGroup:ReleaseChildren()
|
||||
if value == "Help" then
|
||||
private:DrawTooltipHelp(tabGroup)
|
||||
else
|
||||
for _, info in ipairs(private.tooltipInfo) do
|
||||
if info.module == value then
|
||||
info.callback(tabGroup, loadTooltipOptionsTab and loadTooltipOptionsTab.tooltip)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
parent:AddChild(tabGroup)
|
||||
|
||||
tabGroup:SelectTab(loadTooltipOptionsTab and loadTooltipOptionsTab.module or "Help")
|
||||
end
|
||||
|
||||
function private:DrawTooltipHelp(container)
|
||||
local priceSources = TSMAPI:GetPriceSources()
|
||||
priceSources["Crafting"] = nil
|
||||
priceSources["VendorBuy"] = nil
|
||||
priceSources["VendorSell"] = nil
|
||||
priceSources["Disenchant"] = nil
|
||||
local page = {
|
||||
{
|
||||
-- scroll frame to contain everything
|
||||
type = "ScrollFrame",
|
||||
layout = "List",
|
||||
children = {
|
||||
{
|
||||
type = "InlineGroup",
|
||||
layout = "flow",
|
||||
title = L["General Options"],
|
||||
children = {
|
||||
{
|
||||
type = "Label",
|
||||
text = L["Display prices in tooltips as:"],
|
||||
relativeWidth = 0.25,
|
||||
},
|
||||
{
|
||||
type = "CheckBox",
|
||||
label = L["Coins:"],
|
||||
relativeWidth = 0.09,
|
||||
settingInfo = {TSM.db.profile, "moneyCoinsTooltip"},
|
||||
callback = function(_, _, value)
|
||||
if value == true then
|
||||
TSM.db.profile.moneyTextTooltip = false
|
||||
end
|
||||
container:ReloadTab()
|
||||
end,
|
||||
},
|
||||
{
|
||||
type = "Label",
|
||||
relativeWidth = 0.22,
|
||||
text = TSMAPI:FormatTextMoneyIcon(3451267, "|cffffffff", false, true),
|
||||
},
|
||||
{
|
||||
type = "CheckBox",
|
||||
label = L["Text:"],
|
||||
relativeWidth = 0.09,
|
||||
settingInfo = {TSM.db.profile, "moneyTextTooltip"},
|
||||
callback = function(_, _, value)
|
||||
if value == true then
|
||||
TSM.db.profile.moneyCoinsTooltip = false
|
||||
end
|
||||
container:ReloadTab()
|
||||
end,
|
||||
},
|
||||
{
|
||||
type = "Label",
|
||||
text = TSMAPI:FormatTextMoney(3451267, "|cffffffff", false, true),
|
||||
},
|
||||
{
|
||||
type = "HeadingLine",
|
||||
},
|
||||
{
|
||||
type = "CheckBox",
|
||||
label = L["Embed TSM Tooltips"],
|
||||
settingInfo = {TSM.db.profile, "embeddedTooltip"},
|
||||
tooltip = L["If checked, TSM's tooltip lines will be embedded in the item tooltip. Otherwise, it will show as a separate box below the item's tooltip."],
|
||||
},
|
||||
{
|
||||
type = "CheckBox",
|
||||
label = L["Display Group / Operation Info in Tooltips"],
|
||||
settingInfo = {TSM.db.profile, "tooltip"},
|
||||
},
|
||||
{
|
||||
type = "CheckBox",
|
||||
label = L["Display vendor buy price in tooltip."],
|
||||
settingInfo = { TSM.db.profile, "vendorBuyTooltip" },
|
||||
tooltip = L["If checked, the price of buying the item from a vendor is displayed."],
|
||||
},
|
||||
{
|
||||
type = "CheckBox",
|
||||
label = L["Display vendor sell price in tooltip."],
|
||||
settingInfo = { TSM.db.profile, "vendorSellTooltip" },
|
||||
tooltip = L["If checked, the price of selling the item to a vendor displayed."],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type = "InlineGroup",
|
||||
layout = "flow",
|
||||
title = L["Destroy Values"],
|
||||
children = {
|
||||
{
|
||||
type = "Dropdown",
|
||||
label = L["Destroy Value Source:"],
|
||||
settingInfo = {TSM.db.profile, "destroyValueSource"},
|
||||
list = priceSources,
|
||||
relativeWidth = 0.5,
|
||||
tooltip = L["Select the price source for calculating destroy values."],
|
||||
},
|
||||
{
|
||||
type = "CheckBox",
|
||||
label = L["Display Detailed Destroy Tooltips"],
|
||||
settingInfo = { TSM.db.profile, "detailedDestroyTooltip" },
|
||||
relativeWidth = 0.49,
|
||||
tooltip = L["If checked, a detailed list of items which an item destroys into will be displayed below the destroy value in the tooltip."],
|
||||
},
|
||||
{
|
||||
type = "HeadingLine",
|
||||
},
|
||||
{
|
||||
type = "CheckBox",
|
||||
label = L["Display mill value in tooltip."],
|
||||
settingInfo = { TSM.db.profile, "millTooltip" },
|
||||
relativeWidth = 0.5,
|
||||
tooltip = L["If checked, the mill value of the item will be shown. This value is calculated using the average market value of materials the item will mill into."],
|
||||
},
|
||||
{
|
||||
type = "CheckBox",
|
||||
label = L["Display prospect value in tooltip."],
|
||||
settingInfo = { TSM.db.profile, "prospectTooltip" },
|
||||
relativeWidth = 0.5,
|
||||
tooltip = L["If checked, the prospect value of the item will be shown. This value is calculated using the average market value of materials the item will prospect into."],
|
||||
},
|
||||
{
|
||||
type = "CheckBox",
|
||||
label = L["Display disenchant value in tooltip."],
|
||||
settingInfo = { TSM.db.profile, "deTooltip" },
|
||||
relativeWidth = 0.5,
|
||||
tooltip = L["If checked, the disenchant value of the item will be shown. This value is calculated using the average market value of materials the item will disenchant into."],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if next(TSM.db.global.customPriceSources) then
|
||||
local inlineGroup = {
|
||||
type = "InlineGroup",
|
||||
layout = "flow",
|
||||
title = L["Custom Price Sources"],
|
||||
children = {
|
||||
{
|
||||
type = "Label",
|
||||
text = L["Custom price sources to display in item tooltips:"],
|
||||
relativeWidth = 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
for name in pairs(TSM.db.global.customPriceSources) do
|
||||
local checkbox = {
|
||||
type = "CheckBox",
|
||||
label = name,
|
||||
relativeWidth = 0.5,
|
||||
settingInfo = { TSM.db.global.customPriceTooltips, name },
|
||||
tooltip = L["If checked, this custom price will be displayed in item tooltips."],
|
||||
}
|
||||
tinsert(inlineGroup.children, checkbox)
|
||||
end
|
||||
tinsert(page[1].children, inlineGroup)
|
||||
end
|
||||
|
||||
TSMAPI:BuildPage(container, page)
|
||||
end
|
||||
Reference in New Issue
Block a user