42dbe9d526
* Enchanting fixes, reverted Crafting module to previous version that supports Ascension specific crafts and Vellums. * Updated enchanting names from DB * Added the remaining Ascension specific recipes * Replace SpellNames2IDs.lua with UnitCastingSpellID * Bugfix: Ascension uses exact quality QueryAuctionItems, TSM was designed for quality or higher * Bugfix: Properly get list of Professions via index * Replaced factionrealm with realm Ascension does not have strict faction seperation * GetTradeSkillCooldown -> SpellHasBaseCooldown GetTradeSkillCooldown only tells you if a spell is on CD, not if it has a CD Data is from DB, which isn't populated fully yet. * Implement backported API GetSpellBaseCooldown * bugfix: hasCD is nil if no cd, not 0
368 lines
14 KiB
Lua
368 lines
14 KiB
Lua
-- ------------------------------------------------------------------------------ --
|
|
-- 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.ignorerealm = operation.ignorerealm 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.ignorerealm = operation.ignorerealm 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 realm = TSM.db.keys.realm
|
|
local playerKey = UnitName("player").." - "..realm
|
|
return operation.ignorePlayer[playerKey] or operation.ignorerealm[realm]
|
|
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 |