555 lines
18 KiB
Lua
555 lines
18 KiB
Lua
-- ------------------------------------------------------------------------------ --
|
|
-- TradeSkillMaster --
|
|
-- https://tradeskillmaster.com --
|
|
-- All Rights Reserved - Detailed license information included with addon. --
|
|
-- ------------------------------------------------------------------------------ --
|
|
|
|
local _, TSM = ...
|
|
local ProfessionScanner = TSM.Crafting:NewPackage("ProfessionScanner")
|
|
local ProfessionInfo = TSM.Include("Data.ProfessionInfo")
|
|
local Database = TSM.Include("Util.Database")
|
|
local Event = TSM.Include("Util.Event")
|
|
local Delay = TSM.Include("Util.Delay")
|
|
local TempTable = TSM.Include("Util.TempTable")
|
|
local Math = TSM.Include("Util.Math")
|
|
local Log = TSM.Include("Util.Log")
|
|
local String = TSM.Include("Util.String")
|
|
local ItemString = TSM.Include("Util.ItemString")
|
|
local ItemInfo = TSM.Include("Service.ItemInfo")
|
|
local private = {
|
|
db = nil,
|
|
hasScanned = false,
|
|
callbacks = {},
|
|
disabled = false,
|
|
ignoreUpdatesUntil = 0,
|
|
optionalMatArrayTemp = { { itemID = nil, count = 1, index = nil } },
|
|
}
|
|
-- don't want to scan a bunch of times when the profession first loads so add a 10 frame debounce to update events
|
|
local SCAN_DEBOUNCE_FRAMES = 10
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
-- Module Functions
|
|
-- ============================================================================
|
|
|
|
function ProfessionScanner.OnInitialize()
|
|
private.db = Database.NewSchema("CRAFTING_RECIPES")
|
|
:AddUniqueNumberField("index")
|
|
:AddUniqueNumberField("spellId")
|
|
:AddStringField("name")
|
|
:AddNumberField("categoryId")
|
|
:AddStringField("difficulty")
|
|
:AddNumberField("rank")
|
|
:AddNumberField("numSkillUps")
|
|
:Commit()
|
|
TSM.Crafting.ProfessionState.RegisterUpdateCallback(private.ProfessionStateUpdate)
|
|
if TSM.IsWowClassic() then
|
|
Event.Register("CRAFT_UPDATE", private.OnTradeSkillUpdateEvent)
|
|
Event.Register("TRADE_SKILL_UPDATE", private.OnTradeSkillUpdateEvent)
|
|
else
|
|
Event.Register("TRADE_SKILL_LIST_UPDATE", private.OnTradeSkillUpdateEvent)
|
|
end
|
|
Event.Register("CHAT_MSG_SKILL", private.ChatMsgSkillEventHandler)
|
|
end
|
|
|
|
function ProfessionScanner.SetDisabled(disabled)
|
|
if private.disabled == disabled then
|
|
return
|
|
end
|
|
private.disabled = disabled
|
|
if not disabled then
|
|
private.ScanProfession()
|
|
end
|
|
end
|
|
|
|
function ProfessionScanner.HasScanned()
|
|
return private.hasScanned
|
|
end
|
|
|
|
function ProfessionScanner.HasSkills()
|
|
return private.hasScanned and private.db:GetNumRows() > 0
|
|
end
|
|
|
|
function ProfessionScanner.RegisterHasScannedCallback(callback)
|
|
tinsert(private.callbacks, callback)
|
|
end
|
|
|
|
function ProfessionScanner.IgnoreNextProfessionUpdates()
|
|
private.ignoreUpdatesUntil = GetTime() + 1
|
|
end
|
|
|
|
function ProfessionScanner.CreateQuery()
|
|
return private.db:NewQuery()
|
|
end
|
|
|
|
function ProfessionScanner.GetIndexBySpellId(spellId)
|
|
assert(TSM.IsWowClassic() or private.hasScanned)
|
|
return private.db:GetUniqueRowField("spellId", spellId, "index")
|
|
end
|
|
|
|
function ProfessionScanner.GetCategoryIdBySpellId(spellId)
|
|
assert(private.hasScanned)
|
|
return private.db:GetUniqueRowField("spellId", spellId, "categoryId")
|
|
end
|
|
|
|
function ProfessionScanner.GetNameBySpellId(spellId)
|
|
assert(private.hasScanned)
|
|
return private.db:GetUniqueRowField("spellId", spellId, "name")
|
|
end
|
|
|
|
function ProfessionScanner.GetRankBySpellId(spellId)
|
|
assert(private.hasScanned)
|
|
return private.db:GetUniqueRowField("spellId", spellId, "rank")
|
|
end
|
|
|
|
function ProfessionScanner.GetNumSkillupsBySpellId(spellId)
|
|
assert(private.hasScanned)
|
|
return private.db:GetUniqueRowField("spellId", spellId, "numSkillUps")
|
|
end
|
|
|
|
function ProfessionScanner.GetDifficultyBySpellId(spellId)
|
|
assert(private.hasScanned)
|
|
return private.db:GetUniqueRowField("spellId", spellId, "difficulty")
|
|
end
|
|
|
|
function ProfessionScanner.GetFirstSpellId()
|
|
if not private.hasScanned then
|
|
return
|
|
end
|
|
return private.db:NewQuery()
|
|
:Select("spellId")
|
|
:OrderBy("index", true)
|
|
:GetFirstResultAndRelease()
|
|
end
|
|
|
|
function ProfessionScanner.HasSpellId(spellId)
|
|
return private.hasScanned and private.db:GetUniqueRowField("spellId", spellId, "index") and true or false
|
|
end
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
-- Event Handlers
|
|
-- ============================================================================
|
|
|
|
function private.ProfessionStateUpdate()
|
|
private.hasScanned = false
|
|
for _, callback in ipairs(private.callbacks) do
|
|
callback()
|
|
end
|
|
if TSM.Crafting.ProfessionState.GetCurrentProfession() then
|
|
private.db:Truncate()
|
|
private.OnTradeSkillUpdateEvent()
|
|
else
|
|
Delay.Cancel("PROFESSION_SCAN_DELAY")
|
|
end
|
|
end
|
|
|
|
function private.OnTradeSkillUpdateEvent()
|
|
Delay.Cancel("PROFESSION_SCAN_DELAY")
|
|
private.QueueProfessionScan()
|
|
end
|
|
|
|
function private.ChatMsgSkillEventHandler(_, msg)
|
|
local professionName = TSM.Crafting.ProfessionState.GetCurrentProfession()
|
|
if not professionName or not strmatch(msg, professionName) then
|
|
return
|
|
end
|
|
private.ignoreUpdatesUntil = 0
|
|
private.QueueProfessionScan()
|
|
end
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
-- Profession Scanning
|
|
-- ============================================================================
|
|
|
|
function private.QueueProfessionScan()
|
|
Delay.AfterFrame("PROFESSION_SCAN_DELAY", SCAN_DEBOUNCE_FRAMES, private.ScanProfession)
|
|
end
|
|
|
|
function private.ScanProfession()
|
|
if InCombatLockdown() then
|
|
-- we are in combat, so try again in a bit
|
|
private.QueueProfessionScan()
|
|
return
|
|
elseif private.disabled then
|
|
return
|
|
elseif GetTime() < private.ignoreUpdatesUntil then
|
|
return
|
|
end
|
|
|
|
local professionName = TSM.Crafting.ProfessionState.GetCurrentProfession()
|
|
if not professionName then
|
|
-- profession hasn't fully opened yet
|
|
private.QueueProfessionScan()
|
|
return
|
|
end
|
|
|
|
assert(professionName and TSM.Crafting.ProfessionUtil.IsDataStable())
|
|
if TSM.IsWowClassic() then
|
|
-- TODO: check and clear filters on classic
|
|
else
|
|
local hadFilter = false
|
|
if C_TradeSkillUI.GetOnlyShowUnlearnedRecipes() then
|
|
C_TradeSkillUI.SetOnlyShowLearnedRecipes(true)
|
|
C_TradeSkillUI.SetOnlyShowUnlearnedRecipes(false)
|
|
hadFilter = true
|
|
end
|
|
if C_TradeSkillUI.GetOnlyShowMakeableRecipes() then
|
|
C_TradeSkillUI.SetOnlyShowMakeableRecipes(false)
|
|
hadFilter = true
|
|
end
|
|
if C_TradeSkillUI.GetOnlyShowSkillUpRecipes() then
|
|
C_TradeSkillUI.SetOnlyShowSkillUpRecipes(false)
|
|
hadFilter = true
|
|
end
|
|
if C_TradeSkillUI.AnyRecipeCategoriesFiltered() then
|
|
C_TradeSkillUI.ClearRecipeCategoryFilter()
|
|
hadFilter = true
|
|
end
|
|
if C_TradeSkillUI.AreAnyInventorySlotsFiltered() then
|
|
C_TradeSkillUI.ClearInventorySlotFilter()
|
|
hadFilter = true
|
|
end
|
|
for i = 1, C_PetJournal.GetNumPetSources() do
|
|
if C_TradeSkillUI.IsAnyRecipeFromSource(i) and C_TradeSkillUI.IsRecipeSourceTypeFiltered(i) then
|
|
C_TradeSkillUI.ClearRecipeSourceTypeFilter()
|
|
hadFilter = true
|
|
break
|
|
end
|
|
end
|
|
if C_TradeSkillUI.GetRecipeItemNameFilter() ~= "" then
|
|
C_TradeSkillUI.SetRecipeItemNameFilter(nil)
|
|
hadFilter = true
|
|
end
|
|
local minItemLevel, maxItemLevel = C_TradeSkillUI.GetRecipeItemLevelFilter()
|
|
if minItemLevel ~= 0 or maxItemLevel ~= 0 then
|
|
C_TradeSkillUI.SetRecipeItemLevelFilter(0, 0)
|
|
hadFilter = true
|
|
end
|
|
|
|
if hadFilter then
|
|
-- an update event will be triggered
|
|
return
|
|
end
|
|
end
|
|
|
|
if TSM.IsWowClassic() then
|
|
local lastHeaderIndex = 0
|
|
private.db:TruncateAndBulkInsertStart()
|
|
for i = 1, TSM.Crafting.ProfessionState.IsClassicCrafting() and GetNumCrafts() or GetNumTradeSkills() do
|
|
local name, _, skillType, hash = nil, nil, nil, nil
|
|
if TSM.Crafting.ProfessionState.IsClassicCrafting() then
|
|
name, _, skillType = GetCraftInfo(i)
|
|
if skillType ~= "header" then
|
|
hash = Math.CalculateHash(name)
|
|
for j = 1, GetCraftNumReagents(i) do
|
|
local _, _, quantity = GetCraftReagentInfo(i, j)
|
|
hash = Math.CalculateHash(ItemString.Get(GetCraftReagentItemLink(i, j)), hash)
|
|
hash = Math.CalculateHash(quantity, hash)
|
|
end
|
|
end
|
|
else
|
|
name, skillType = GetTradeSkillInfo(i)
|
|
if skillType ~= "header" then
|
|
hash = Math.CalculateHash(name)
|
|
for j = 1, GetTradeSkillNumReagents(i) do
|
|
local _, _, quantity = GetTradeSkillReagentInfo(i, j)
|
|
hash = Math.CalculateHash(ItemString.Get(GetTradeSkillReagentItemLink(i, j)), hash)
|
|
hash = Math.CalculateHash(quantity, hash)
|
|
end
|
|
end
|
|
end
|
|
if skillType == "header" then
|
|
lastHeaderIndex = i
|
|
else
|
|
if name then
|
|
private.db:BulkInsertNewRow(i, hash, name, lastHeaderIndex, skillType, -1, 1)
|
|
end
|
|
end
|
|
end
|
|
private.db:BulkInsertEnd()
|
|
else
|
|
local prevRecipeIds = TempTable.Acquire()
|
|
local nextRecipeIds = TempTable.Acquire()
|
|
local recipeLearned = TempTable.Acquire()
|
|
local recipes = TempTable.Acquire()
|
|
assert(C_TradeSkillUI.GetFilteredRecipeIDs(recipes) == recipes)
|
|
local spellIdIndex = TempTable.Acquire()
|
|
for index, spellId in ipairs(recipes) do
|
|
-- There's a Blizzard bug where First Aid duplicates spellIds, so check that we haven't seen this before
|
|
if not spellIdIndex[spellId] then
|
|
spellIdIndex[spellId] = index
|
|
local info = nil
|
|
if not TSM.IsShadowlands() then
|
|
info = TempTable.Acquire()
|
|
assert(C_TradeSkillUI.GetRecipeInfo(spellId, info) == info)
|
|
else
|
|
info = C_TradeSkillUI.GetRecipeInfo(spellId)
|
|
end
|
|
if info.previousRecipeID then
|
|
prevRecipeIds[spellId] = info.previousRecipeID
|
|
nextRecipeIds[info.previousRecipeID] = spellId
|
|
end
|
|
if info.nextRecipeID then
|
|
nextRecipeIds[spellId] = info.nextRecipeID
|
|
prevRecipeIds[info.nextRecipeID] = spellId
|
|
end
|
|
recipeLearned[spellId] = info.learned
|
|
if not TSM.IsShadowlands() then
|
|
TempTable.Release(info)
|
|
end
|
|
end
|
|
end
|
|
private.db:TruncateAndBulkInsertStart()
|
|
local inactiveSpellIds = TempTable.Acquire()
|
|
for index, spellId in ipairs(recipes) do
|
|
local hasHigherRank = nextRecipeIds[spellId] and recipeLearned[nextRecipeIds[spellId]]
|
|
-- TODO: show unlearned recipes in the TSM UI
|
|
-- There's a Blizzard bug where First Aid duplicates spellIds, so check that this is the right index
|
|
if spellIdIndex[spellId] == index and recipeLearned[spellId] and not hasHigherRank then
|
|
local info = nil
|
|
if not TSM.IsShadowlands() then
|
|
info = TempTable.Acquire()
|
|
assert(C_TradeSkillUI.GetRecipeInfo(spellId, info) == info)
|
|
else
|
|
info = C_TradeSkillUI.GetRecipeInfo(spellId)
|
|
end
|
|
local rank = -1
|
|
if prevRecipeIds[spellId] or nextRecipeIds[spellId] then
|
|
rank = 1
|
|
local tempSpellId = spellId
|
|
while prevRecipeIds[tempSpellId] do
|
|
rank = rank + 1
|
|
tempSpellId = prevRecipeIds[tempSpellId]
|
|
end
|
|
end
|
|
local numSkillUps = info.difficulty == "optimal" and info.numSkillUps or 1
|
|
private.db:BulkInsertNewRow(index, spellId, info.name, info.categoryID, info.difficulty, rank, numSkillUps)
|
|
if not TSM.IsShadowlands() then
|
|
TempTable.Release(info)
|
|
end
|
|
else
|
|
inactiveSpellIds[spellId] = true
|
|
end
|
|
end
|
|
private.db:BulkInsertEnd()
|
|
-- remove spells which are not active (i.e. older ranks)
|
|
if next(inactiveSpellIds) then
|
|
TSM.Crafting.RemovePlayerSpells(inactiveSpellIds)
|
|
end
|
|
TempTable.Release(inactiveSpellIds)
|
|
TempTable.Release(spellIdIndex)
|
|
TempTable.Release(recipes)
|
|
TempTable.Release(prevRecipeIds)
|
|
TempTable.Release(nextRecipeIds)
|
|
TempTable.Release(recipeLearned)
|
|
end
|
|
|
|
if TSM.Crafting.ProfessionUtil.IsNPCProfession() or TSM.Crafting.ProfessionUtil.IsLinkedProfession() or TSM.Crafting.ProfessionUtil.IsGuildProfession() then
|
|
-- we don't want to store this profession in our DB, so we're done
|
|
if not private.hasScanned then
|
|
private.hasScanned = true
|
|
for _, callback in ipairs(private.callbacks) do
|
|
callback()
|
|
end
|
|
end
|
|
return
|
|
end
|
|
|
|
if not TSM.db.sync.internalData.playerProfessions[professionName] then
|
|
-- we are in combat or the player's professions haven't been scanned yet by PlayerProfessions.lua, so try again in a bit
|
|
private.QueueProfessionScan()
|
|
return
|
|
end
|
|
|
|
-- update the link for this profession
|
|
TSM.db.sync.internalData.playerProfessions[professionName].link = not TSM.IsWowClassic() and C_TradeSkillUI.GetTradeSkillListLink() or nil
|
|
|
|
-- scan all the recipes
|
|
TSM.Crafting.SetSpellDBQueryUpdatesPaused(true)
|
|
local query = private.db:NewQuery()
|
|
:Select("spellId")
|
|
local numFailed = 0
|
|
for _, spellId in query:Iterator() do
|
|
if not private.ScanRecipe(professionName, spellId) then
|
|
numFailed = numFailed + 1
|
|
end
|
|
end
|
|
query:Release()
|
|
TSM.Crafting.SetSpellDBQueryUpdatesPaused(false)
|
|
|
|
Log.Info("Scanned %s (failed to scan %d)", professionName, numFailed)
|
|
if numFailed > 0 then
|
|
-- didn't completely scan, so we'll try again
|
|
private.QueueProfessionScan()
|
|
end
|
|
if not private.hasScanned then
|
|
private.hasScanned = true
|
|
for _, callback in ipairs(private.callbacks) do
|
|
callback()
|
|
end
|
|
end
|
|
|
|
-- explicitly run GC
|
|
collectgarbage()
|
|
end
|
|
|
|
function private.ScanRecipe(professionName, spellId)
|
|
-- get the links
|
|
local itemLink, lNum, hNum = TSM.Crafting.ProfessionUtil.GetRecipeInfo(TSM.IsWowClassic() and ProfessionScanner.GetIndexBySpellId(spellId) or spellId)
|
|
assert(itemLink, "Invalid craft: "..tostring(spellId))
|
|
|
|
-- get the itemString and craft name
|
|
local itemString, craftName = nil, nil
|
|
if strfind(itemLink, "enchant:") then
|
|
if TSM.IsWowClassic() then
|
|
return true
|
|
else
|
|
-- result of craft is not an item
|
|
itemString = ProfessionInfo.GetIndirectCraftResult(spellId)
|
|
if not itemString then
|
|
-- we don't care about this craft
|
|
return true
|
|
end
|
|
craftName = GetSpellInfo(spellId)
|
|
end
|
|
elseif strfind(itemLink, "item:") then
|
|
-- result of craft is item
|
|
itemString = ItemString.GetBase(itemLink)
|
|
craftName = ItemInfo.GetName(itemLink)
|
|
-- Blizzard broke Brilliant Scarlet Ruby in 8.3, so just hard-code a workaround
|
|
if spellId == 53946 and not itemString and not craftName then
|
|
itemString = "i:39998"
|
|
craftName = GetSpellInfo(spellId)
|
|
end
|
|
else
|
|
error("Invalid craft: "..tostring(spellId))
|
|
end
|
|
if not itemString or not craftName then
|
|
Log.Warn("No itemString (%s) or craftName (%s) found (%s, %s)", tostring(itemString), tostring(craftName), tostring(professionName), tostring(spellId))
|
|
return false
|
|
end
|
|
|
|
-- get the result number
|
|
local numResult = nil
|
|
local isEnchant = professionName == GetSpellInfo(7411) and strfind(itemLink, "enchant:")
|
|
if isEnchant then
|
|
numResult = 1
|
|
else
|
|
-- workaround for incorrect values returned for Temporal Crystal
|
|
if spellId == 169092 and itemString == "i:113588" then
|
|
lNum, hNum = 1, 1
|
|
end
|
|
-- workaround for incorrect values returned for new mass milling recipes
|
|
if ProfessionInfo.IsMassMill(spellId) then
|
|
if spellId == 210116 then -- Yseralline
|
|
lNum, hNum = 4, 4 -- always four
|
|
elseif spellId == 209664 then -- Felwort
|
|
lNum, hNum = 42, 42 -- amount is variable but the values are conservative
|
|
elseif spellId == 247861 then -- Astral Glory
|
|
lNum, hNum = 4, 4 -- amount is variable but the values are conservative
|
|
else
|
|
lNum, hNum = 8, 8.8
|
|
end
|
|
end
|
|
numResult = floor(((lNum or 1) + (hNum or 1)) / 2)
|
|
end
|
|
|
|
-- store general info about this recipe
|
|
local hasCD = TSM.Crafting.ProfessionUtil.HasCooldown(spellId)
|
|
TSM.Crafting.CreateOrUpdate(spellId, itemString, professionName, craftName, numResult, UnitName("player"), hasCD)
|
|
|
|
-- get the mat quantities and add mats to our DB
|
|
local matQuantities = TempTable.Acquire()
|
|
local haveInvalidMats = false
|
|
local numReagents = TSM.Crafting.ProfessionUtil.GetNumMats(spellId)
|
|
for i = 1, numReagents do
|
|
local matItemLink, name, _, quantity = TSM.Crafting.ProfessionUtil.GetMatInfo(spellId, i)
|
|
local matItemString = ItemString.GetBase(matItemLink)
|
|
if not matItemString then
|
|
Log.Warn("Failed to get itemString for mat %d (%s, %s)", i, tostring(professionName), tostring(spellId))
|
|
haveInvalidMats = true
|
|
break
|
|
end
|
|
if not name or not quantity then
|
|
Log.Warn("Failed to get name (%s) or quantity (%s) for mat (%s, %s, %d)", tostring(name), tostring(quantity), tostring(professionName), tostring(spellId), i)
|
|
haveInvalidMats = true
|
|
break
|
|
end
|
|
ItemInfo.StoreItemName(matItemString, name)
|
|
TSM.db.factionrealm.internalData.mats[matItemString] = TSM.db.factionrealm.internalData.mats[matItemString] or {}
|
|
matQuantities[matItemString] = quantity
|
|
end
|
|
-- if this is an enchant, add a vellum to the list of mats
|
|
if isEnchant then
|
|
local matItemString = ProfessionInfo.GetVellumItemString()
|
|
TSM.db.factionrealm.internalData.mats[matItemString] = TSM.db.factionrealm.internalData.mats[matItemString] or {}
|
|
matQuantities[matItemString] = 1
|
|
end
|
|
|
|
if not haveInvalidMats then
|
|
local optionalMats = private.GetOptionalMats(spellId)
|
|
if optionalMats then
|
|
for _, matStr in ipairs(optionalMats) do
|
|
local _, _, mats = strsplit(":", matStr)
|
|
for itemId in String.SplitIterator(mats, ",") do
|
|
local matItemString = "i:"..itemId
|
|
TSM.db.factionrealm.internalData.mats[matItemString] = TSM.db.factionrealm.internalData.mats[matItemString] or {}
|
|
end
|
|
matQuantities[matStr] = -1
|
|
end
|
|
end
|
|
TSM.Crafting.SetMats(spellId, matQuantities)
|
|
end
|
|
TempTable.Release(matQuantities)
|
|
return not haveInvalidMats
|
|
end
|
|
|
|
function private.GetOptionalMats(spellId)
|
|
local optionalMats = TSM.IsShadowlands() and C_TradeSkillUI.GetOptionalReagentInfo(spellId) or nil
|
|
if not optionalMats or #optionalMats == 0 then
|
|
return nil
|
|
end
|
|
for i, info in ipairs(optionalMats) do
|
|
if info.requiredSkillRank ~= 0 then
|
|
-- TODO: handle this case
|
|
return nil
|
|
else
|
|
-- process the options
|
|
assert(#info.options > 0)
|
|
-- sort the optional mats by itemId
|
|
sort(info.options)
|
|
-- cache the optional mat info
|
|
for _, itemId in ipairs(info.options) do
|
|
assert(type(itemId) == "number")
|
|
private.CacheOptionalMatInfo(spellId, i, itemId)
|
|
end
|
|
local matList = table.concat(info.options, ",")
|
|
TSM.Crafting.ProfessionUtil.StoreOptionalMatText(matList, info.slotText)
|
|
optionalMats[i] = "o:"..i..":"..matList
|
|
end
|
|
end
|
|
return optionalMats
|
|
end
|
|
|
|
function private.CacheOptionalMatInfo(spellId, index, itemId)
|
|
if TSM.db.global.internalData.optionalMatBonusIdLookup[itemId] then
|
|
return
|
|
end
|
|
if not TSMScanTooltip then
|
|
CreateFrame("GameTooltip", "TSMScanTooltip", UIParent, "GameTooltipTemplate")
|
|
end
|
|
private.optionalMatArrayTemp.itemID = itemId
|
|
private.optionalMatArrayTemp.slot = index
|
|
TSMScanTooltip:SetOwner(UIParent, "ANCHOR_NONE")
|
|
TSMScanTooltip:ClearLines()
|
|
TSMScanTooltip:SetRecipeResultItem(spellId, private.optionalMatArrayTemp)
|
|
local _, itemLink = TSMScanTooltip:GetItem()
|
|
local bonusId = strmatch(itemLink, "item:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:2:3524:([0-9]+)")
|
|
TSM.db.global.internalData.optionalMatBonusIdLookup[itemId] = tonumber(bonusId)
|
|
end
|