initial commit
This commit is contained in:
480
Core/Service/Crafting/ProfessionUtil.lua
Normal file
480
Core/Service/Crafting/ProfessionUtil.lua
Normal file
@@ -0,0 +1,480 @@
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
-- TradeSkillMaster --
|
||||
-- https://tradeskillmaster.com --
|
||||
-- All Rights Reserved - Detailed license information included with addon. --
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
|
||||
local _, TSM = ...
|
||||
local ProfessionUtil = TSM.Crafting:NewPackage("ProfessionUtil")
|
||||
local ProfessionInfo = TSM.Include("Data.ProfessionInfo")
|
||||
local Event = TSM.Include("Util.Event")
|
||||
local TempTable = TSM.Include("Util.TempTable")
|
||||
local Log = TSM.Include("Util.Log")
|
||||
local Delay = TSM.Include("Util.Delay")
|
||||
local ItemString = TSM.Include("Util.ItemString")
|
||||
local ItemInfo = TSM.Include("Service.ItemInfo")
|
||||
local BagTracking = TSM.Include("Service.BagTracking")
|
||||
local Inventory = TSM.Include("Service.Inventory")
|
||||
local CustomPrice = TSM.Include("Service.CustomPrice")
|
||||
local private = {
|
||||
craftQuantity = nil,
|
||||
craftSpellId = nil,
|
||||
craftCallback = nil,
|
||||
craftName = nil,
|
||||
castingTimeout = nil,
|
||||
craftTimeout = nil,
|
||||
preparedSpellId = nil,
|
||||
preparedTime = 0,
|
||||
categoryInfoTemp = {},
|
||||
}
|
||||
local PROFESSION_LOOKUP = {
|
||||
["Costura"] = "Sastrería",
|
||||
["Marroquinería"] = "Peletería",
|
||||
["Ingénierie"] = "Ingénieur",
|
||||
["Secourisme"] = "Premiers soins",
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- Module Functions
|
||||
-- ============================================================================
|
||||
|
||||
function ProfessionUtil.OnInitialize()
|
||||
Event.Register("UNIT_SPELLCAST_SUCCEEDED", function(_, unit, _, spellId)
|
||||
if unit ~= "player" then
|
||||
return
|
||||
end
|
||||
if (TSM.IsWowClassic() and GetSpellInfo(spellId) ~= private.craftName) or (not TSM.IsWowClassic() and spellId ~= private.craftSpellId) then
|
||||
return
|
||||
end
|
||||
|
||||
-- check if we need to update bank quantity manually
|
||||
for _, itemString, quantity in TSM.Crafting.MatIterator(private.craftSpellId) do
|
||||
local bankUsed = quantity - (Inventory.GetBagQuantity(itemString) + Inventory.GetReagentBankQuantity(itemString))
|
||||
if bankUsed > 0 and bankUsed <= Inventory.GetBankQuantity(itemString) then
|
||||
Log.Info("Used %d from bank", bankUsed)
|
||||
BagTracking.ForceBankQuantityDeduction(itemString, bankUsed)
|
||||
end
|
||||
end
|
||||
|
||||
local callback = private.craftCallback
|
||||
assert(callback)
|
||||
private.craftQuantity = private.craftQuantity - 1
|
||||
private.DoCraftCallback(true, private.craftQuantity == 0)
|
||||
-- ignore profession updates from crafting something
|
||||
TSM.Crafting.ProfessionScanner.IgnoreNextProfessionUpdates()
|
||||
-- restart the timeout
|
||||
end)
|
||||
local function SpellcastFailedEventHandler(_, unit, _, spellId)
|
||||
if unit ~= "player" then
|
||||
return
|
||||
end
|
||||
if (TSM.IsWowClassic() and GetSpellInfo(spellId) ~= private.craftName) or (not TSM.IsWowClassic() and spellId ~= private.craftSpellId) then
|
||||
return
|
||||
end
|
||||
private.DoCraftCallback(false, true)
|
||||
end
|
||||
local function ClearCraftCast()
|
||||
private.craftQuantity = nil
|
||||
private.craftSpellId = nil
|
||||
private.craftName = nil
|
||||
private.castingTimeout = nil
|
||||
private.craftTimeout = nil
|
||||
end
|
||||
Event.Register("UNIT_SPELLCAST_INTERRUPTED", SpellcastFailedEventHandler)
|
||||
Event.Register("UNIT_SPELLCAST_FAILED", SpellcastFailedEventHandler)
|
||||
Event.Register("UNIT_SPELLCAST_FAILED_QUIET", SpellcastFailedEventHandler)
|
||||
Event.Register("TRADE_SKILL_CLOSE", ClearCraftCast)
|
||||
if TSM.IsWowClassic() then
|
||||
Event.Register("CRAFT_CLOSE", ClearCraftCast)
|
||||
end
|
||||
end
|
||||
|
||||
function ProfessionUtil.GetCurrentProfessionName()
|
||||
if TSM.IsWowClassic() then
|
||||
local name = TSM.Crafting.ProfessionState.IsClassicCrafting() and GetCraftSkillLine(1) or GetTradeSkillLine()
|
||||
return name
|
||||
else
|
||||
local _, name, _, _, _, _, parentName = C_TradeSkillUI.GetTradeSkillLine()
|
||||
return parentName or name
|
||||
end
|
||||
end
|
||||
|
||||
function ProfessionUtil.GetResultInfo(spellId)
|
||||
-- get the links
|
||||
local itemLink = ProfessionUtil.GetRecipeInfo(spellId)
|
||||
assert(itemLink, "Invalid craft: "..tostring(spellId))
|
||||
|
||||
if strfind(itemLink, "enchant:") then
|
||||
-- result of craft is not an item
|
||||
local itemString = ProfessionInfo.GetIndirectCraftResult(spellId)
|
||||
if itemString and not TSM.IsWowClassic() then
|
||||
return TSM.UI.GetColoredItemName(itemString), itemString, ItemInfo.GetTexture(itemString)
|
||||
elseif ProfessionInfo.IsEngineeringTinker(spellId) then
|
||||
local name, _, icon = GetSpellInfo(spellId)
|
||||
return name, nil, icon
|
||||
else
|
||||
local name, _, icon = GetSpellInfo(TSM.Crafting.ProfessionState.IsClassicCrafting() and GetCraftInfo(TSM.IsWowClassic() and TSM.Crafting.ProfessionScanner.GetIndexBySpellId(spellId) or spellId) or spellId)
|
||||
return name, nil, icon
|
||||
end
|
||||
elseif strfind(itemLink, "item:") then
|
||||
-- result of craft is an item
|
||||
return TSM.UI.GetColoredItemName(itemLink), ItemString.Get(itemLink), ItemInfo.GetTexture(itemLink)
|
||||
else
|
||||
error("Invalid craft: "..tostring(spellId))
|
||||
end
|
||||
end
|
||||
|
||||
function ProfessionUtil.GetNumCraftable(spellId)
|
||||
local num, numAll = math.huge, math.huge
|
||||
for i = 1, ProfessionUtil.GetNumMats(spellId) do
|
||||
local matItemLink, _, _, quantity = ProfessionUtil.GetMatInfo(spellId, i)
|
||||
local itemString = ItemString.Get(matItemLink)
|
||||
local totalQuantity = CustomPrice.GetItemPrice(itemString, "NumInventory") or 0
|
||||
if not itemString or not quantity or totalQuantity == 0 then
|
||||
return 0, 0
|
||||
end
|
||||
local bagQuantity = Inventory.GetBagQuantity(itemString)
|
||||
if not TSM.IsWowClassic() then
|
||||
bagQuantity = bagQuantity + Inventory.GetReagentBankQuantity(itemString) + Inventory.GetBankQuantity(itemString)
|
||||
end
|
||||
num = min(num, floor(bagQuantity / quantity))
|
||||
numAll = min(numAll, floor(totalQuantity / quantity))
|
||||
end
|
||||
if num == math.huge or numAll == math.huge then
|
||||
return 0, 0
|
||||
end
|
||||
return num, numAll
|
||||
end
|
||||
|
||||
function ProfessionUtil.IsCraftable(spellId)
|
||||
for i = 1, ProfessionUtil.GetNumMats(spellId) do
|
||||
local matItemLink, _, _, quantity = ProfessionUtil.GetMatInfo(spellId, i)
|
||||
local itemString = ItemString.Get(matItemLink)
|
||||
if not itemString or not quantity then
|
||||
return false
|
||||
end
|
||||
local bagQuantity = Inventory.GetBagQuantity(itemString)
|
||||
if not TSM.IsWowClassic() then
|
||||
bagQuantity = bagQuantity + Inventory.GetReagentBankQuantity(itemString) + Inventory.GetBankQuantity(itemString)
|
||||
end
|
||||
if floor(bagQuantity / quantity) == 0 then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function ProfessionUtil.GetNumCraftableFromDB(spellId)
|
||||
local num = math.huge
|
||||
for _, itemString, quantity in TSM.Crafting.MatIterator(spellId) do
|
||||
local bagQuantity = Inventory.GetBagQuantity(itemString)
|
||||
if not TSM.IsWowClassic() then
|
||||
bagQuantity = bagQuantity + Inventory.GetReagentBankQuantity(itemString) + Inventory.GetBankQuantity(itemString)
|
||||
end
|
||||
num = min(num, floor(bagQuantity / quantity))
|
||||
end
|
||||
if num == math.huge then
|
||||
return 0
|
||||
end
|
||||
return num
|
||||
end
|
||||
|
||||
function ProfessionUtil.IsEnchant(spellId)
|
||||
local name = ProfessionUtil.GetCurrentProfessionName()
|
||||
if name ~= GetSpellInfo(7411) or TSM.IsWowClassic() then
|
||||
return false
|
||||
end
|
||||
if not strfind(C_TradeSkillUI.GetRecipeItemLink(spellId), "enchant:") then
|
||||
return false
|
||||
end
|
||||
local recipeInfo = nil
|
||||
if not TSM.IsShadowlands() then
|
||||
recipeInfo = TempTable.Acquire()
|
||||
assert(C_TradeSkillUI.GetRecipeInfo(spellId, recipeInfo) == recipeInfo)
|
||||
else
|
||||
recipeInfo = C_TradeSkillUI.GetRecipeInfo(spellId)
|
||||
end
|
||||
local altVerb = recipeInfo.alternateVerb
|
||||
if not TSM.IsShadowlands() then
|
||||
TempTable.Release(recipeInfo)
|
||||
end
|
||||
return altVerb and true or false
|
||||
end
|
||||
|
||||
function ProfessionUtil.OpenProfession(profession, skillId)
|
||||
if TSM.IsWowClassic() then
|
||||
if profession == ProfessionInfo.GetName("Mining") then
|
||||
-- mining needs to be opened as smelting
|
||||
profession = ProfessionInfo.GetName("Smelting")
|
||||
end
|
||||
if PROFESSION_LOOKUP[profession] then
|
||||
profession = PROFESSION_LOOKUP[profession]
|
||||
end
|
||||
CastSpellByName(profession)
|
||||
else
|
||||
C_TradeSkillUI.OpenTradeSkill(skillId)
|
||||
end
|
||||
end
|
||||
|
||||
function ProfessionUtil.PrepareToCraft(spellId, quantity)
|
||||
quantity = min(quantity, ProfessionUtil.GetNumCraftable(spellId))
|
||||
if quantity == 0 then
|
||||
return
|
||||
end
|
||||
if ProfessionUtil.IsEnchant(spellId) then
|
||||
quantity = 1
|
||||
end
|
||||
|
||||
if not TSM.IsWowClassic() then
|
||||
C_TradeSkillUI.SetRecipeRepeatCount(spellId, quantity)
|
||||
end
|
||||
private.preparedSpellId = spellId
|
||||
private.preparedTime = GetTime()
|
||||
end
|
||||
|
||||
function ProfessionUtil.Craft(spellId, quantity, useVellum, callback)
|
||||
assert(TSM.Crafting.ProfessionScanner.HasSpellId(spellId))
|
||||
if private.craftSpellId then
|
||||
private.craftCallback = callback
|
||||
private.DoCraftCallback(false, true)
|
||||
return 0
|
||||
end
|
||||
quantity = min(quantity, ProfessionUtil.GetNumCraftable(spellId))
|
||||
if quantity == 0 then
|
||||
return 0
|
||||
end
|
||||
local isEnchant = ProfessionUtil.IsEnchant(spellId)
|
||||
if isEnchant then
|
||||
quantity = 1
|
||||
elseif spellId ~= private.preparedSpellId or private.preparedTime == GetTime() then
|
||||
-- We can only craft one of this item due to a bug on Blizzard's end
|
||||
quantity = 1
|
||||
end
|
||||
private.craftQuantity = quantity
|
||||
private.craftSpellId = spellId
|
||||
private.craftCallback = callback
|
||||
if TSM.IsWowClassic() then
|
||||
spellId = TSM.Crafting.ProfessionScanner.GetIndexBySpellId(spellId)
|
||||
if TSM.Crafting.ProfessionState.IsClassicCrafting() then
|
||||
private.craftName = GetCraftInfo(spellId)
|
||||
else
|
||||
private.craftName = GetTradeSkillInfo(spellId)
|
||||
DoTradeSkill(spellId, quantity)
|
||||
end
|
||||
else
|
||||
C_TradeSkillUI.CraftRecipe(spellId, quantity)
|
||||
end
|
||||
if useVellum and isEnchant then
|
||||
UseItemByName(ItemInfo.GetName(ProfessionInfo.GetVellumItemString()))
|
||||
end
|
||||
private.castingTimeout = nil
|
||||
private.craftTimeout = nil
|
||||
Delay.AfterTime("PROFESSION_CRAFT_TIMEOUT_MONITOR", 0.5, private.CraftTimeoutMonitor, 0.5)
|
||||
return quantity
|
||||
end
|
||||
|
||||
function ProfessionUtil.IsDataStable()
|
||||
return TSM.IsWowClassic() or (C_TradeSkillUI.IsTradeSkillReady() and not C_TradeSkillUI.IsDataSourceChanging())
|
||||
end
|
||||
|
||||
function ProfessionUtil.HasCooldown(spellId)
|
||||
if TSM.IsWowClassic() then
|
||||
return GetTradeSkillCooldown(spellId) and true or false
|
||||
else
|
||||
return select(2, C_TradeSkillUI.GetRecipeCooldown(spellId)) and true or false
|
||||
end
|
||||
end
|
||||
|
||||
function ProfessionUtil.GetRemainingCooldown(spellId)
|
||||
if TSM.IsWowClassic() then
|
||||
return GetTradeSkillCooldown(spellId)
|
||||
else
|
||||
return C_TradeSkillUI.GetRecipeCooldown(spellId)
|
||||
end
|
||||
end
|
||||
|
||||
function ProfessionUtil.GetRecipeInfo(spellId)
|
||||
local itemLink, lNum, hNum, toolsStr, hasTools = nil, nil, nil, nil, nil
|
||||
if TSM.IsWowClassic() then
|
||||
spellId = TSM.Crafting.ProfessionScanner.GetIndexBySpellId(spellId) or spellId
|
||||
itemLink = TSM.Crafting.ProfessionState.IsClassicCrafting() and GetCraftItemLink(spellId) or GetTradeSkillItemLink(spellId)
|
||||
if TSM.Crafting.ProfessionState.IsClassicCrafting() then
|
||||
lNum, hNum = 1, 1
|
||||
toolsStr, hasTools = GetCraftSpellFocus(spellId)
|
||||
else
|
||||
lNum, hNum = GetTradeSkillNumMade(spellId)
|
||||
toolsStr, hasTools = GetTradeSkillTools(spellId)
|
||||
end
|
||||
else
|
||||
itemLink = C_TradeSkillUI.GetRecipeItemLink(spellId)
|
||||
lNum, hNum = C_TradeSkillUI.GetRecipeNumItemsProduced(spellId)
|
||||
toolsStr, hasTools = C_TradeSkillUI.GetRecipeTools(spellId)
|
||||
end
|
||||
return itemLink, lNum, hNum, toolsStr, hasTools
|
||||
end
|
||||
|
||||
function ProfessionUtil.GetNumMats(spellId)
|
||||
local numMats = nil
|
||||
if TSM.IsWowClassic() then
|
||||
spellId = TSM.Crafting.ProfessionScanner.GetIndexBySpellId(spellId) or spellId
|
||||
numMats = TSM.Crafting.ProfessionState.IsClassicCrafting() and GetCraftNumReagents(spellId) or GetTradeSkillNumReagents(spellId)
|
||||
else
|
||||
numMats = C_TradeSkillUI.GetRecipeNumReagents(spellId)
|
||||
end
|
||||
return numMats
|
||||
end
|
||||
|
||||
function ProfessionUtil.GetMatInfo(spellId, index)
|
||||
local itemLink, name, texture, quantity = nil, nil, nil, nil
|
||||
if TSM.IsWowClassic() then
|
||||
spellId = TSM.Crafting.ProfessionScanner.GetIndexBySpellId(spellId) or spellId
|
||||
itemLink = TSM.Crafting.ProfessionState.IsClassicCrafting() and GetCraftReagentItemLink(spellId, index) or GetTradeSkillReagentItemLink(spellId, index)
|
||||
if TSM.Crafting.ProfessionState.IsClassicCrafting() then
|
||||
name, texture, quantity = GetCraftReagentInfo(spellId, index)
|
||||
else
|
||||
name, texture, quantity = GetTradeSkillReagentInfo(spellId, index)
|
||||
end
|
||||
else
|
||||
itemLink = C_TradeSkillUI.GetRecipeReagentItemLink(spellId, index)
|
||||
name, texture, quantity = C_TradeSkillUI.GetRecipeReagentInfo(spellId, index)
|
||||
if itemLink then
|
||||
name = name or ItemInfo.GetName(itemLink)
|
||||
texture = texture or ItemInfo.GetTexture(itemLink)
|
||||
end
|
||||
end
|
||||
return itemLink, name, texture, quantity
|
||||
end
|
||||
|
||||
function ProfessionUtil.CloseTradeSkill(closeBoth)
|
||||
if TSM.IsWowClassic() then
|
||||
if closeBoth then
|
||||
CloseCraft()
|
||||
CloseTradeSkill()
|
||||
else
|
||||
if TSM.Crafting.ProfessionState.IsClassicCrafting() then
|
||||
CloseCraft()
|
||||
else
|
||||
CloseTradeSkill()
|
||||
end
|
||||
end
|
||||
else
|
||||
C_TradeSkillUI.CloseTradeSkill()
|
||||
C_Garrison.CloseGarrisonTradeskillNPC()
|
||||
end
|
||||
end
|
||||
|
||||
function ProfessionUtil.IsNPCProfession()
|
||||
return not TSM.IsWowClassic() and C_TradeSkillUI.IsNPCCrafting()
|
||||
end
|
||||
|
||||
function ProfessionUtil.IsLinkedProfession()
|
||||
if TSM.IsWowClassic() then
|
||||
return nil, nil
|
||||
else
|
||||
return C_TradeSkillUI.IsTradeSkillLinked()
|
||||
end
|
||||
end
|
||||
|
||||
function ProfessionUtil.IsGuildProfession()
|
||||
return not TSM.IsWowClassic() and C_TradeSkillUI.IsTradeSkillGuild()
|
||||
end
|
||||
|
||||
function ProfessionUtil.GetCategoryInfo(categoryId)
|
||||
local name, numIndents, parentCategoryId, currentSkillLevel, maxSkillLevel = nil, nil, nil, nil, nil
|
||||
if TSM.IsWowClassic() then
|
||||
name = TSM.Crafting.ProfessionState.IsClassicCrafting() and GetCraftDisplaySkillLine() or (categoryId and GetTradeSkillInfo(categoryId) or nil)
|
||||
numIndents = 0
|
||||
parentCategoryId = nil
|
||||
else
|
||||
C_TradeSkillUI.GetCategoryInfo(categoryId, private.categoryInfoTemp)
|
||||
assert(private.categoryInfoTemp.numIndents)
|
||||
name = private.categoryInfoTemp.name
|
||||
numIndents = private.categoryInfoTemp.numIndents
|
||||
parentCategoryId = private.categoryInfoTemp.numIndents ~= 0 and private.categoryInfoTemp.parentCategoryID or nil
|
||||
currentSkillLevel = private.categoryInfoTemp.skillLineCurrentLevel
|
||||
maxSkillLevel = private.categoryInfoTemp.skillLineMaxLevel
|
||||
wipe(private.categoryInfoTemp)
|
||||
end
|
||||
return name, numIndents, parentCategoryId, currentSkillLevel, maxSkillLevel
|
||||
end
|
||||
|
||||
function ProfessionUtil.StoreOptionalMatText(matList, text)
|
||||
TSM.db.global.internalData.optionalMatTextLookup[matList] = TSM.db.global.internalData.optionalMatTextLookup[matList] or text
|
||||
end
|
||||
|
||||
function ProfessionUtil.GetOptionalMatText(matList)
|
||||
return TSM.db.global.internalData.optionalMatTextLookup[matList]
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- Private Helper Functions
|
||||
-- ============================================================================
|
||||
|
||||
function private.DoCraftCallback(result, isDone)
|
||||
local callback = private.craftCallback
|
||||
assert(callback)
|
||||
-- reset timeouts
|
||||
private.castingTimeout = nil
|
||||
private.craftTimeout = nil
|
||||
if isDone then
|
||||
private.craftQuantity = nil
|
||||
private.craftSpellId = nil
|
||||
private.craftCallback = nil
|
||||
private.craftName = nil
|
||||
Delay.Cancel("PROFESSION_CRAFT_TIMEOUT_MONITOR")
|
||||
end
|
||||
callback(result, isDone)
|
||||
end
|
||||
|
||||
function private.CraftTimeoutMonitor()
|
||||
if not private.craftSpellId then
|
||||
Log.Info("No longer crafting")
|
||||
private.castingTimeout = nil
|
||||
private.craftTimeout = nil
|
||||
Delay.Cancel("PROFESSION_CRAFT_TIMEOUT_MONITOR")
|
||||
return
|
||||
end
|
||||
local _, _, _, _, castEndTimeMs, _, _, _, spellId = private.GetPlayerCastingInfo()
|
||||
if spellId then
|
||||
private.castingTimeout = nil
|
||||
else
|
||||
private.craftTimeout = nil
|
||||
end
|
||||
if not spellId then
|
||||
-- no active cast
|
||||
if GetTime() > (private.castingTimeout or math.huge) then
|
||||
Log.Err("Craft timed out (%s)", private.craftSpellId)
|
||||
private.DoCraftCallback(false, true)
|
||||
return
|
||||
end
|
||||
-- set the casting timeout to 1 second from now
|
||||
private.castingTimeout = GetTime() + 1
|
||||
return
|
||||
elseif private.craftSpellId ~= spellId then
|
||||
Log.Err("Crafting something else (%s, %s)", private.craftSpellId, spellId)
|
||||
private.castingTimeout = nil
|
||||
private.craftTimeout = nil
|
||||
Delay.Cancel("PROFESSION_CRAFT_TIMEOUT_MONITOR")
|
||||
return
|
||||
end
|
||||
|
||||
if GetTime() > (private.craftTimeout or math.huge) then
|
||||
Log.Err("Craft timed out (%s)", private.craftSpellId)
|
||||
private.DoCraftCallback(false, true)
|
||||
return
|
||||
end
|
||||
-- set the timeout to 1 second after the end time
|
||||
private.craftTimeout = castEndTimeMs / 1000 + 1
|
||||
end
|
||||
|
||||
function private.GetPlayerCastingInfo()
|
||||
if TSM.IsWowClassic() then
|
||||
return CastingInfo()
|
||||
else
|
||||
return UnitCastingInfo("player")
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user