You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

526 lines
18 KiB

-- ------------------------------------------------------------------------------ --
-- TradeSkillMaster --
-- https://tradeskillmaster.com --
-- All Rights Reserved - Detailed license information included with addon. --
-- ------------------------------------------------------------------------------ --
local TSM = select(2, ...) ---@type TSM
local Profession = TSM.Init("Service.Profession") ---@class Service.Profession
local Environment = TSM.Include("Environment")
local State = TSM.Include("Service.ProfessionHelpers.State")
local Scanner = TSM.Include("Service.ProfessionHelpers.Scanner")
local ProfessionInfo = TSM.Include("Data.ProfessionInfo")
local ItemString = TSM.Include("Util.ItemString")
local CraftString = TSM.Include("Util.CraftString")
local ItemInfo = TSM.Include("Service.ItemInfo")
local private = {
classicSpellIdLookup = {},
categoryInfoTemp = {},
}
local EMPTY_TABLE = {}
-- ============================================================================
-- Module Functions
-- ============================================================================
---Register a callback for when the profession state changes.
---@param callback fun() The callback function
function Profession.RegisterStateCallback(callback)
State.RegisterCallback(callback)
end
---Registers scanner hook functions.
---@param scanFunc function
---@param inactiveFunc function
function Profession.SetScanHookFuncs(scanFunc, inactiveFunc)
Scanner.SetHookFuncs(scanFunc, inactiveFunc)
end
---Returns whether or not the profession is closed.
---@return boolean
function Profession.IsClosed()
return State.IsClosed()
end
---Returns whether or not the classic crafting UI is open.
---@return boolean
function Profession.IsClassicCrafting()
return State.IsClassicCrafting()
end
---Sets whether the classic crafting UI is open.
---@param open boolean
---TODO: Better way to handle this
function Profession.SetClassicCraftingOpen(open)
State.SetClassicCraftingOpen(open)
end
---Gets the current stable and open profession info.
---@return string @The name of the profession
---@return Enum.Profession? @The profession enum value (retail only)
function Profession.GetCurrentProfession()
return State.GetCurrentProfession()
end
---Gets the current profession skill line.
---@return string @The name of the profession
---@return Enum.Profession? @The profession enum value (retail only)
---TODO: This should be replaced with GetCurrentProfession() and delayed until the profession data is stable
function Profession.GetSkillLine()
return State.GetSkillLine()
end
---Opens a profession.
---@param profession string|Enum.Profession The name of the profession (classic) or the profession enum value (retail)
function Profession.Open(profession)
if Environment.HasFeature(Environment.FEATURES.C_TRADE_SKILL_UI) then
assert(type(profession) == "number")
C_TradeSkillUI.OpenTradeSkill(profession)
else
assert(type(profession) == "string")
if profession == ProfessionInfo.GetName("Mining") then
-- mining needs to be opened as smelting
profession = ProfessionInfo.GetName("Smelting")
end
local mappedName = ProfessionInfo.MapProfessionName(profession)
if mappedName then
profession = mappedName
end
CastSpellByName(profession)
end
end
---Close the open profession.
---@param closeBoth boolean Whether to close both the craft and tradeskill on classic
function Profession.CloseTradeSkill(closeBoth)
if Environment.HasFeature(Environment.FEATURES.C_TRADE_SKILL_UI) then
C_TradeSkillUI.CloseTradeSkill()
C_Garrison.CloseGarrisonTradeskillNPC()
else
if closeBoth then
CloseCraft()
CloseTradeSkill()
else
if Profession.IsClassicCrafting() then
CloseCraft()
else
CloseTradeSkill()
end
end
end
end
---Returns whether or not the open profession is an NPC profession.
---@return boolean
function Profession.IsNPC()
return State.IsNPC()
end
---Returns whether or not the open profession is linked.
---@return boolean
---@return string? @The character who it's linked from (if available)
function Profession.IsLinked()
return State.IsLinked()
end
---Returns whether or not the open profession is a guild profession.
---@return boolean
function Profession.IsGuild()
return State.IsGuild()
end
---Returns a link for the current profession.
---@return string?
function Profession.GetLink()
return State.GetLink()
end
---Gets whether or not a recipe has a cooldown.
---@param craftString string The craft string for the recipe
---@return boolean
function Profession.HasCooldown(craftString)
local spellId = CraftString.GetSpellId(craftString)
if Environment.HasFeature(Environment.FEATURES.C_TRADE_SKILL_UI) then
return select(2, C_TradeSkillUI.GetRecipeCooldown(spellId)) and true or false
else
spellId = private.classicSpellIdLookup[spellId] or spellId
return GetTradeSkillCooldown(spellId) and true or false
end
end
---Gets the remaining cooldown for a recipe.
---@param craftString string The craft string for the recipe
---@return number?
function Profession.GetRemainingCooldown(craftString)
local spellId = CraftString.GetSpellId(craftString)
local cooldown = nil
if Environment.HasFeature(Environment.FEATURES.C_TRADE_SKILL_UI) then
local cooldownTime, _, charges, maxCharges = C_TradeSkillUI.GetRecipeCooldown(spellId)
if maxCharges and charges and maxCharges > 0 and (charges > 0 or not cooldownTime) then
return nil
end
cooldown = cooldownTime
else
spellId = private.classicSpellIdLookup[spellId] or spellId
cooldown = GetTradeSkillCooldown(spellId)
end
return cooldown and floor(cooldown) or nil
end
---Gets the range of quantitys which the recipe crafts.
---@param craftString string The craft string for the recipe
---@return number @The lower bound of the crafted quantity
---@return number @The upper bound of the crafted quantity
function Profession.GetCraftedQuantityRange(craftString)
return Scanner.GetCraftedQuantityRange(craftString)
end
---Gets the item GUIDs for the specified list of mats.
---@param mats number[] The list of mats as itemIds
---@return CraftingTargetItem[]
function Profession.GetTargetItems(mats)
return C_TradeSkillUI.GetCraftingTargetItems(mats)
end
---Gets the result of a recipe.
---@param craftString string The craft string for the recipe
---@return string|string[]?
function Profession.GetResultItem(craftString)
return Scanner.GetResultItem(craftString)
end
---Gets the vellum item string to use with a recipe.
---@param craftString string The craft string for the recipe
---@return string
function Profession.GetVellumItemString(craftString)
return Scanner.GetVellumItemString(craftString)
end
---Gets the number of different result items of a recipe.
---@param craftString string The craft string for the recipe
---@return number
function Profession.GetNumResultItems(craftString)
return Scanner.GetNumResultItems(craftString)
end
---Gets the result info for a recipe.
---@param craftString string The craft string for the recipe
---@return string? itemString
---@return number? texture
---@return string? name
function Profession.GetResultInfo(craftString)
-- Get the links
local resultItem = Profession.GetResultItem(craftString)
if not resultItem then
return nil, nil, nil
end
if type(resultItem) == "table" then
local itemLink = resultItem[CraftString.GetQuality(craftString) or 1]
return ItemString.Get(itemLink), ItemInfo.GetTexture(itemLink), ItemInfo.GetName(itemLink)
elseif strfind(resultItem, "enchant:") then
-- Result of craft is not an item
local spellId = CraftString.GetSpellId(craftString)
local indirectSpellId = nil
if Environment.IsWrathClassic() then
indirectSpellId = strmatch(resultItem, "enchant:(%d+)")
indirectSpellId = indirectSpellId and tonumber(indirectSpellId)
if not indirectSpellId then
return nil, nil, nil
end
else
indirectSpellId = spellId
end
local itemString = ProfessionInfo.GetIndirectCraftResult(indirectSpellId)
if type(itemString) == "table" then
itemString = itemString[CraftString.GetQuality(craftString) or 1]
end
if itemString and (Environment.IsRetail() or Environment.IsWrathClassic()) then
return itemString, ItemInfo.GetTexture(itemString), ItemInfo.GetName(itemString)
elseif ProfessionInfo.IsEngineeringTinker(spellId) then
local name, _, icon = GetSpellInfo(spellId)
return nil, icon, name
elseif Environment.IsWrathClassic() then
local name, _, icon = GetSpellInfo(indirectSpellId)
return nil, icon, name
else
local name, _, icon = GetSpellInfo(Profession.IsClassicCrafting() and GetCraftInfo(not Environment.IsRetail() and private.classicSpellIdLookup[spellId] or spellId) or spellId)
return nil, icon, name
end
elseif strfind(resultItem, "item:") then
-- result of craft is an item
return ItemString.Get(resultItem), ItemInfo.GetTexture(resultItem), ItemInfo.GetName(resultItem)
else
error("Invalid craft: "..craftString)
end
end
---Returns whether or not a recipe is an enchant.
---@param craftString string The craft string for the recipe
---@return boolean
function Profession.IsEnchant(craftString)
return Scanner.IsEnchant(craftString)
end
---Returns whether or not a recipe is an salvage.
---@param craftString string The craft string for the recipe
---@return boolean
function Profession.IsSalvage(craftString)
return ProfessionInfo.IsSalvage(CraftString.GetSpellId(craftString))
end
---Returns whether or not a recipe is a tinker.
---@param craftString string The craft string for the recipe
---@return boolean
function Profession.IsTinker(craftString)
return ProfessionInfo.IsEngineeringTinker(CraftString.GetSpellId(craftString))
end
---Gets the needed tools string for a recipe.
---@param craftString string The craft string for the recipe
---@return string
function Profession.NeededTools(craftString)
local spellId = CraftString.GetSpellId(craftString)
if Environment.HasFeature(Environment.FEATURES.C_TRADE_SKILL_UI) then
for _, requirement in ipairs(C_TradeSkillUI.GetRecipeRequirements(spellId)) do
if requirement.type == Enum.RecipeRequirementType.Totem and not requirement.met then
return requirement.name
end
end
return nil
else
local toolsStr, hasTools = nil, nil
spellId = private.classicSpellIdLookup[spellId] or spellId
if Profession.IsClassicCrafting() then
toolsStr, hasTools = GetCraftSpellFocus(spellId)
else
toolsStr, hasTools = GetTradeSkillTools(spellId)
end
return not hasTools and toolsStr or nil
end
end
---Gets the description for a recipe.
---@param craftString string The craft string for the recipe
---@return string
function Profession.GetRecipeDescription(craftString)
if not craftString or not Environment.HasFeature(Environment.FEATURES.C_TRADE_SKILL_UI) then
return ""
end
return C_TradeSkillUI.GetRecipeDescription(CraftString.GetSpellId(craftString), EMPTY_TABLE)
end
---Gets the link for a recipe.
---@param craftString string The craft string for the recipe
---@return string
function Profession.GetRecipeLink(craftString)
local spellId = CraftString.GetSpellId(craftString)
if Environment.HasFeature(Environment.FEATURES.C_TRADE_SKILL_UI) then
return C_TradeSkillUI.GetRecipeLink(spellId)
else
spellId = private.classicSpellIdLookup[spellId] or spellId
if Profession.IsClassicCrafting() then
return GetCraftRecipeLink(spellId)
else
return GetTradeSkillRecipeLink(spellId)
end
end
end
---Get information on a profession category.
---@param categoryId number The category ID
---@return string? @The name
---@return number @The number of indents
---@return number? @The parent category ID (on retail)
---@return number? @The current skill level for the category (on retail)
---@return number? @The max skill level for the category (on retail)
function Profession.CategoryInfo(categoryId)
if not Environment.HasFeature(Environment.FEATURES.C_TRADE_SKILL_UI) then
local name = Profession.IsClassicCrafting() and GetCraftDisplaySkillLine() or (categoryId and GetTradeSkillInfo(categoryId) or nil)
return name, 0, nil, nil, nil
end
C_TradeSkillUI.GetCategoryInfo(categoryId, private.categoryInfoTemp)
local name = private.categoryInfoTemp.name
local parentCategoryId = private.categoryInfoTemp.numIndents ~= 0 and private.categoryInfoTemp.parentCategoryID or nil
local currentSkillLevel = private.categoryInfoTemp.skillLineCurrentLevel
local maxSkillLevel = private.categoryInfoTemp.skillLineMaxLevel
local numIndents = 0
if parentCategoryId then
C_TradeSkillUI.GetCategoryInfo(parentCategoryId, private.categoryInfoTemp)
if private.categoryInfoTemp.type == "subheader" then
numIndents = parentCategoryId == private.categoryInfoTemp.parentCategoryID and 0 or 1
end
else
numIndents = 0
end
wipe(private.categoryInfoTemp)
return name, numIndents, parentCategoryId, currentSkillLevel, maxSkillLevel
end
---Gets the number of mats for a recipe.
---@param craftString string The craft string for the recipe
---@return number
function Profession.GetNumMats(craftString)
return Scanner.GetNumMats(craftString)
end
---Gets information on a material.
---@param craftString string The craft string for the recipe
---@param index number The index of the material
---@return string? itemString
---@return number? quantity
---@return string? name
---@return boolean? isQualityMat
function Profession.GetMatInfo(craftString, index)
return Scanner.GetMatInfo(craftString, index)
end
---Gets quality info for a DF recipe.
---@param craftString string The craft string for the recipe
---@return number? baseDifficulty
---@return number? quality
---@return boolean? hasQualityMats
---@return number? inspirationAmount
---@return number? inspirationChance
function Profession.GetRecipeQualityInfo(craftString)
return Scanner.GetRecipeQualityInfo(craftString)
end
---Sets whether or not the scanner is disabled.
---@param disabled boolean
function Profession.SetScannerDisabled(disabled)
Scanner.SetDisabled(disabled)
end
---Gets whether or not the profession has been scanned.
---@return boolean
function Profession.HasScanned()
return Scanner.HasScanned()
end
---Gets whether or not there are skills.
---@return boolean
function Profession.ScannerHasSkills()
return Scanner.HasSkills()
end
---Registers a callback when the profession is scanned.
---@param callback function
function Profession.RegisterHasScannedCallback(callback)
Scanner.RegisterHasScannedCallback(callback)
end
---Ignores the next profession update for scanning purposes.
function Profession.IgnoreNextProfessionUpdates()
Scanner.IgnoreNextProfessionUpdates()
end
---Creates a query for crafts.
---@return DatabaseQuery
function Profession.CreateScannerQuery()
return Scanner.CreateQuery()
end
---Gets the item string for a craft string.
---@param craftString string
---@return string
function Profession.GetItemStringByCraftString(craftString)
return Scanner.GetItemStringByCraftString(craftString)
end
---Gets the index for a craft string.
---@param craftString string
---@return number
function Profession.GetIndexByCraftString(craftString)
return Scanner.GetIndexByCraftString(craftString)
end
---Gets the category ID for a craft string.
---@param craftString string
---@return number
function Profession.GetCategoryIdByCraftString(craftString)
return Scanner.GetCategoryIdByCraftString(craftString)
end
---Gets the name for a craft string.
---@param craftString string
---@return string
function Profession.GetNameByCraftString(craftString)
return Scanner.GetNameByCraftString(craftString)
end
---Gets the craft name for a craft string.
---@param craftString string
---@return string
function Profession.GetCraftNameByCraftString(craftString)
return Scanner.GetCraftNameByCraftString(craftString)
end
---Gets the current experience for a craft string.
---@param craftString string
---@return number
function Profession.GetCurrentExpByCraftString(craftString)
return Scanner.GetCurrentExpByCraftString(craftString)
end
---Gets the next experience for a craft string.
---@param craftString string
---@return number
function Profession.GetNextExpByCraftString(craftString)
return Scanner.GetNextExpByCraftString(craftString)
end
---Gets the difficulty for a craft string.
---@param craftString string
---@return number
function Profession.GetDifficultyByCraftString(craftString)
return Scanner.GetDifficultyByCraftString(craftString)
end
---Checks whether or not a craft string exists.
---@param craftString string
---@return bool
function Profession.HasCraftString(craftString)
return Scanner.HasCraftString(craftString)
end
---Returns an iterator for materials of a craft string.
---@param craftString string
---@return fun(): number, string, number, string @Iterator with fields: `index`, `matString`, `quantity`, `slotText`
function Profession.MatIterator(craftString)
return Scanner.MatIterator(craftString)
end
---Gets the mat string for a specified slot id.
---@param craftString string
---@param slotId number
---@return string
function Profession.GetOptionalMatString(craftString, slotId)
return Scanner.GetOptionalMatString(craftString, slotId)
end
---Gets the number of optional mats of a given type.
---@param craftString string The craft string
---@param matType table The mat type
---@return number
function Profession.GetNumOptionalMats(craftString, matType)
return Scanner.GetNumOptionalMats(craftString, matType)
end
---Gets the quantity for a mat by item id within the current profession.
---@param CraftString string
---@param matItemId number
---@return number
function Profession.GetMatQuantity(craftString, matItemId)
return Scanner.GetMatQuantity(craftString, matItemId)
end
---Gets the slot text for a mat within the current profession.
---@param craftString string
---@param matString string
---@param string
function Profession.GetMatSlotText(craftString, matString)
return Scanner.GetMatSlotText(craftString, matString)
end