local currentChar = FurnitureCatalogue.CharacterName local task = LibStub("LibAsync"):Create("FurnitureCatalogue_ScanDataFiles") local task2 = LibStub("LibAsync"):Create("FurnitureCatalogue_ScanCharacterKnowledge") local characterAlliance = GetUnitAlliance('player') local NUMBER_TYPE = "number" local STRING_TYPE = "string" local STRING_EMPTY = "" local lastLink = nil local recipeArray = nil local FURC_STRING_TRADINGHOUSE = "Seen in trading house" local function getCurrentChar() if nil == currentChar then currentChar = zo_strformat(GetUnitName("player")) end return currentChar end local p = FurC.DebugOut local function startupMessage(text) if FurC.GetStartupSilently() then return end d(text) end local function getItemId(itemLink) if nil == itemLink or STRING_EMPTY == itemLink then return end if type(itemLink) == NUMBER_TYPE and itemLink > 9999 then return itemLink end local _, _, _, itemId = ZO_LinkHandler_ParseLink(itemLink) return tonumber(itemId) end FurC.GetItemId = getItemId local function getItemLink(itemId) if nil == itemId then return end itemId = tostring(itemId) if #itemId > 55 then return itemId end if #itemId < 4 then return end return zo_strformat("|H1:item:<<1>>:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0|h|h", itemId) end FurC.GetItemLink = getItemLink local function printItemLink(itemId) if nil == itemId then return end itemId = tostring(itemId) local itemLink = nil if #itemId > 55 then itemLink = itemId end itemLink = itemLink or zo_strformat("|H1:item:<<1>>:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0|h|h", itemId) d(zo_strformat("[<<1>>] = '',\t\t-- <<2>>", itemId, GetItemLinkName(itemLink))) end FurC.PrintItemLink = printItemLink local function addDatabaseEntry(recipeKey, recipeArray) if recipeKey and recipeArray and {} ~= recipeArray then FurC.settings.data[recipeKey] = recipeArray end end local function makeMaterial(recipeKey, recipeArray, tryPlaintext, forcePlaintext) if nil == recipeArray or (nil == recipeArray.blueprint and nil == recipeArray.recipeIndex and nil == recipeArray.recipeListIndex) then return "couldn't get material list, please re-scan character knowledge" end local ret = "" local ingredients = FurC.GetIngredients(recipeKey, recipeArray) forcePlaintext = forcePlaintext or tryPlaintext and NonContiguousCount(ingredients) > 4 for ingredientLink, qty in pairs(ingredients) do -- auto-capitalize because for some reason the ZOS API doesn't local itemText = (forcePlaintext and string.gsub(" "..GetItemLinkName(ingredientLink), "%W%l", string.upper):sub(2)) or ingredientLink ret = zo_strformat("<<1>> <<2>>x <<3>>, ", ret, qty, itemText) end return ret:sub(0, -3) end FurC.GetMats = makeMaterial function FurC.GetIngredients(itemLink, recipeArray) recipeArray = recipeArray or FurC.Find(itemLink) local ingredients = {} if recipeArray.blueprint then local blueprintLink = FurC.GetItemLink(recipeArray.blueprint) numIngredients = GetItemLinkRecipeNumIngredients(blueprintLink) for ingredientIndex=1, numIngredients do name, _, qty = GetItemLinkRecipeIngredientInfo(blueprintLink, ingredientIndex) ingredientLink = GetItemLinkRecipeIngredientItemLink(blueprintLink, ingredientIndex) ingredients[ingredientLink] = qty end else _, name, numIngredients = GetRecipeInfo(recipeArray.recipeListIndex, recipeArray.recipeIndex) for ingredientIndex=1, numIngredients do name, _, qty = GetRecipeIngredientItemInfo(recipeArray.recipeListIndex, recipeArray.recipeIndex, ingredientIndex) ingredientLink = GetRecipeIngredientItemLink(recipeArray.recipeListIndex, recipeArray.recipeIndex, ingredientIndex) ingredients[ingredientLink] = qty end end return ingredients end local function parseFurnitureItem(itemLink, override) -- saves to DB, returns recipeArray if not ( override or IsItemLinkPlaceableFurniture(itemLink) or GetItemLinkItemType(itemLink) == ITEMTYPE_FURNITURE ) then return end local recipeKey = getItemId(itemLink) local recipeArray = FurC.settings.data[recipeKey] if nil ~= recipeArray then return recipeArray end recipeArray = {} addDatabaseEntry(recipeKey, recipeArray) return recipeArray end local function parseBlueprint(blueprintLink) -- saves to DB, returns recipeArray local itemLink = GetItemLinkRecipeResultItemLink(blueprintLink, LINK_STYLE_BRACKETS) local blueprintId = getItemId(blueprintLink) local recipeKey = getItemId(itemLink) if nil == recipeKey or -- we don't have a key to access the database nil == itemLink or -- we don't have an item link to parse nil == GetItemLinkName(itemLink) -- we didn't find an item result for our recipe then return end local recipeArray = FurC.settings.data[recipeKey] or {} recipeArray.origin = recipeArray.origin or FURC_CRAFTING recipeArray.characters = recipeArray.characters or {} recipeArray.craftingSkill = recipeArray.craftingSkill or GetItemLinkCraftingSkillType(blueprintLink) recipeArray.blueprint = recipeArray.blueprint or getItemId(blueprintLink) if (IsItemLinkRecipeKnown(blueprintLink)) then recipeArray.characters[getCurrentChar()] = true end addDatabaseEntry(recipeKey, recipeArray) return recipeArray end function FurC.Find(itemOrBlueprintLink) -- sets recipeArray, returns it - calls scanItemLink if tonumber(itemOrBlueprintLink) == itemOrBlueprintLink then itemOrBlueprintLink = FurC.GetItemLink(itemOrBlueprintLink) end if nil == itemOrBlueprintLink or #itemOrBlueprintLink == 0 then return end -- p("scanItemLink(<<1>>)...", itemOrBlueprintLink) -- do not return empty arrays. If this returns nil, abort! if itemOrBlueprintLink == lastLink and nil ~= recipeArray then return recipeArray else recipeArray = nil lastLink = itemOrBlueprintLink end if IsItemLinkFurnitureRecipe(itemOrBlueprintLink) then recipeArray = parseBlueprint(itemOrBlueprintLink) elseif IsItemLinkPlaceableFurniture(itemOrBlueprintLink) then recipeArray = parseFurnitureItem(itemOrBlueprintLink) end return recipeArray end function FurC.Delete(itemOrBlueprintLink) -- sets recipeArray, returns it - calls scanItemLink local recipeArray = scanItemLink(itemOrBlueprintLink) if nil == recipeArray then return end local itemLink = recipeArray.itemId local itemKey = getItemId(itemLink) FurC.settings.data[itemKey] = nil end function FurC.GetEntry(itemOrBlueprintLink) local itemLink = (IsItemLinkFurnitureRecipe(itemOrBlueprintLink) and GetRecipeResultItemLink(itemOrBlueprintLink)) or itemOrBlueprintLink local recipeArray = FurC.Find(itemLink) -- d(string.format("Trying to get entry for %s: %s", itemLink, recipeArray)) if not recipeArray then return end local itemId = getItemId(itemOrBlueprintLink) if recipeArray.blueprint then itemId = getItemId(GetItemLinkRecipeResultItemLink(blueprintLink)) end return itemId, recipeArray end function FurC.IsFavorite(itemLink, recipeArray) recipeArray = recipeArray or FurC.Find(itemLink) return recipeArray.favorite end function FurC.Fave(itemLink, recipeArray) recipeArray = recipeArray or FurC.Find(itemLink) recipeArray.favorite = not recipeArray.favorite if not recipeArray.favorite then recipeArray.favorite = nil end FurC.UpdateGui() end local function scanRecipeIndices(recipeListIndex, recipeIndex) -- returns recipeArray or nil, initialises local itemLink = GetRecipeResultItemLink(recipeListIndex, recipeIndex, LINK_STYLE_BRACKETS) if nil == itemLink or #itemLink == 0 or not IsItemLinkPlaceableFurniture(itemLink) then return end local recipeKey = getItemId(itemLink) local recipeArray = FurC.settings.data[recipeKey] or {} recipeArray.origin = FURC_CRAFTING recipeArray.version = recipeArray.version or 2 recipeArray.recipeListIndex = recipeArray.recipeListIndex or recipeListIndex recipeArray.recipeIndex = recipeArray.recipeIndex or recipeIndex recipeArray.characters = recipeArray.characters or {} if GetRecipeInfo(recipeListIndex, recipeIndex) then recipeArray.characters[getCurrentChar()] = true FurC.settings.accountCharacters = FurC.settings.accountCharacters or {} FurC.settings.accountCharacters[getCurrentChar()] = FurC.settings.accountCharacters[getCurrentChar()] or true end addDatabaseEntry(recipeKey, recipeArray) return recipeArray end function FurC.TryCreateRecipeEntry(recipeListIndex, recipeIndex) -- returns scanRecipeIndices, called from Events.lua return scanRecipeIndices(recipeListIndex, recipeIndex) end function FurC.IsAccountKnown(recipeKey, recipeArray) if recipeKey == nil and recipeArray == nil then return false end recipeArray = recipeArray or FurC.settings.data[recipeKey] return not (nil == recipeArray or nil == recipeArray.characters or NonContiguousCount(recipeArray.characters) == 0) end function FurC.CanCraft(recipeKey, recipeArray) if recipeKey == nil and recipeArray == nil then return false end recipeArray = recipeArray or FurC.settings.data[recipeKey] if FurC.IsAccountKnown(recipeKey, recipeArray) then return recipeArray.characters[getCurrentChar()] end return false end function FurC.GetCraftingSkillType(recipeKey, recipeArray) local itemLink = FurC.GetItemLink(recipeKey) local craftingSkillType = GetItemLinkCraftingSkillType(itemLink) if 0 == craftingSkillType and recipeArray.blueprint then craftingSkillType = GetItemLinkRecipeCraftingSkillType(FurC.GetItemLink(recipeArray.blueprint)) elseif 0 == craftingSkillType and recipeArray.recipeListIndex and recipeArray.recipeIndex then _, _, _, _, _, _, craftingSkillType = GetRecipeInfo(recipeArray.recipeListIndex, recipeArray.recipeIndex) end return craftingSkillType end local function scanCharacter() local listName, numRecipes for recipeListIndex=1, GetNumRecipeLists() do listName, numRecipes = GetRecipeListInfo(recipeListIndex) for recipeIndex=1, numRecipes do scanRecipeIndices(recipeListIndex, recipeIndex) -- returns true on success end end p((GetString(SI_FURC_DEBUG_CHARSCANCOMPLETE))) end FurC.ScanCharacter = scanCharacter function FurC.RescanRumourRecipes() local function rescan() for itemId, recipeArray in pairs(FurC.settings.data) do if recipeArray.source == FURC_RUMOUR then local itemLink = recipeArray[itemLink] if not FurC.RumourRecipes[itemLink] then recipeArray.source = FURC_CRAFTING recipeArray.origin = nil end end end end task:Call(rescan) :Then(FurC.UpdateGui) end local recipeArray local function scanFromFiles(shouldScanCharacter) local function parseZoneData(zoneName, zoneData, versionNumber, origin) for vendorName, vendorData in pairs(zoneData) do for itemId, itemData in pairs(vendorData) do recipeArray = parseFurnitureItem(FurC.GetItemLink(itemId), true) if not recipeArray then p("Error when scanning <<1>>", itemId) else recipeArray.origin = origin recipeArray.version = versionNumber addDatabaseEntry(itemId, recipeArray) end end end end local function scanRecipeFile() local recipeKey, recipeArray local function makeKeySet(versionData) local keySet = {} for k, v in pairs(versionData) do table.insert(keySet, k) end return keySet end local function scanArray(ary, versionNumber, origin) if nil == ary then return end for _, recipeId in ipairs(ary) do local recipeLink = FurC.GetItemLink(recipeId) local itemLink = GetItemLinkRecipeResultItemLink(recipeLink) or FurC.GetItemLink(recipeId) recipeArray = FurC.Find(itemLink) or parseBlueprint(recipeLink) or parseFurnitureItem(itemLink) local recipeListIndex, recipeIndex = GetItemLinkGrantedRecipeIndices(recipeLink) if nil == recipeArray then p("scanRecipeFile: error for <<1>> (ID was <<2>>)", recipeLink, recipeId) else recipeKey = getItemId(itemLink) recipeArray.version = versionNumber recipeArray.origin = origin recipeArray.blueprint = recipeId addDatabaseEntry(recipeKey, recipeArray) end end end for versionNumber, versionData in pairs(FurC.Recipes) do scanArray(versionData, versionNumber, FURC_CRAFTING) end for versionNumber, versionData in pairs(FurC.RolisRecipes) do scanArray(makeKeySet(versionData), versionNumber, FURC_CRAFTING) end for versionNumber, versionData in pairs(FurC.FaustinaRecipes) do scanArray(makeKeySet(versionData), versionNumber, FURC_CRAFTING) end end local function scanRolis() for versionNumber, versionData in pairs(FurC.Rolis) do for itemId, itemSource in pairs(versionData) do recipeArray = parseFurnitureItem(FurC.GetItemLink(itemId), true) if nil ~= recipeArray then recipeArray.version = versionNumber recipeArray.origin = FURC_ROLIS addDatabaseEntry(itemId, recipeArray) end end end for versionNumber, versionData in pairs(FurC.Faustina) do for itemId, itemSource in pairs(versionData) do recipeArray = parseFurnitureItem(FurC.GetItemLink(itemId), true) if nil ~= recipeArray then recipeArray.version = versionNumber recipeArray.origin = FURC_ROLIS addDatabaseEntry(itemId, recipeArray) end end end end local function scanFestivalFiles() for versionNumber, versionData in pairs(FurC.EventItems) do for eventName, eventData in pairs(versionData) do for eventItemSource, eventItemData in pairs(eventData) do for itemId, _ in pairs(eventItemData) do recipeArray = {} recipeArray.craftable = false recipeArray.version = versionNumber recipeArray.origin = FURC_FESTIVAL_DROP addDatabaseEntry(itemId, recipeArray) end end end end end local function scanMiscItemFile() for versionNumber, versionData in pairs(FurC.MiscItemSources) do for origin, originData in pairs(versionData) do for itemId, itemSource in pairs(originData) do local itemLink = FurC.GetItemLink(itemId) recipeArray = parseFurnitureItem(FurC.GetItemLink(itemId)) if nil ~= recipeArray then recipeArray.version = versionNumber recipeArray.origin = origin -- 3.5: moved FURC_RUMOUR to beginning of table so it'll get overwritten addDatabaseEntry(itemId, recipeArray) else p("scanMiscItemFile: Error when scanning <<1>> (<<2>>) -> <<3>>", itemLink, itemId, origin) end end end end end local function scanVendorFiles() FurC.InitAchievementVendorList() local recipeKey, recipeArray, itemSource for versionNumber, versionData in pairs(FurC.AchievementVendors) do for zoneName, zoneData in pairs(versionData) do parseZoneData(zoneName, zoneData, versionNumber, FURC_VENDOR) end end for versionNumber, vendorData in pairs(FurC.LuxuryFurnisher) do for itemId, itemData in pairs(vendorData) do local recipeArray = {} recipeArray.origin = FURC_LUXURY recipeArray.version = versionNumber addDatabaseEntry(itemId, recipeArray) end end for versionNumber, versionData in pairs(FurC.PVP) do for zoneName, zoneData in pairs(versionData) do parseZoneData(zoneName, zoneData, versionNumber, FURC_PVP) end end end local function scanRumourRecipes() for index, blueprintId in pairs(FurC.RumourRecipes) do local blueprintLink = FurC.GetItemLink(blueprintId) local itemLink = GetItemLinkRecipeResultItemLink(blueprintLink, LINK_STYLE_BRACKETS) if #itemLink == 0 then itemLink = blueprintLink end local itemId = getItemId(itemLink) recipeArray = parseBlueprint(blueprintLink) or parseFurnitureItem(itemLink) or {} if blueprintId ~= itemId then recipeArray.blueprint = blueprintId end recipeArray.recipeListIndex, recipeArray.recipeIndex = GetItemLinkGrantedRecipeIndices(blueprintLink) recipeArray.origin = FURC_RUMOUR recipeArray.verion = FURC_HOMESTEAD addDatabaseEntry(itemId, recipeArray) end end local function scanCharacterOrMaybeNot() if shouldScanCharacter then scanCharacter() else startupMessage(GetString(SI_FURC_VERBOSE_STARTUP)) end end local function rescanRumourRecipes() -- make sure that all rumour items for recipeKey, recipeArray in pairs(FurC.settings.data) do if FurC.RumourRecipes[recipeKey] or recipeArray.blueprint and FurC.RumourRecipes[recipeArray.blueprint] then end end end FurC.IsLoading(true) task:Call(scanRecipeFile) :Then(scanMiscItemFile) :Then(scanVendorFiles) :Then(scanRolis) :Then(scanFestivalFiles) :Then(scanCharacterOrMaybeNot) :Then(scanRumourRecipes) :Then(FurC.UpdateGui) startupMessage(GetString(SI_FURC_VERBOSE_DB_UPTODATE)) end FurC.ScanFromFiles = scanFromFiles local function getScanFromFiles() if (FurC.settings.version < FurC.version) then FurC.settings.version = FurC.version return true end return FurC.settings.data == {} end local function getScanCharacter() if nil == FurC.settings.accountCharacters[FurC.CharacterName] then FurC.settings.accountCharacters[FurC.CharacterName] = false return true end end function FurC.ScanRecipes(shouldScanFiles, shouldScanCharacter) -- returns database shouldScanFiles = shouldScanFiles or getScanFromFiles() shouldScanCharacter = (shouldScanCharacter or getScanCharacter()) if (shouldScanFiles) then p(GetString(SI_FURC_VERBOSE_SCANNING_DATA_FILE)) scanFromFiles(shouldScanCharacter) elseif (shouldScanCharacter) then p(GetString(SI_FURC_VERBOSE_SCANNING_CHARS)) scanCharacter() end end function FurC.GetItemDescription(recipeKey, recipeArray, stripColor, attachItemLink) recipeKey = FurC.GetItemId(recipeKey) FurC.settings.emptyItemSources = FurC.settings.emptyItemSources or {} recipeArray = recipeArray or FurC.Find(recipeKey, recipeArray) if not recipeArray then return "" end local origin = recipeArray.origin if origin == FURC_CRAFTING or origin == FURC_WRIT_VENDOR then return FurC.GetMats(recipeKey, recipeArray, stripColor, attachItemLink) elseif origin == FURC_ROLIS then return FurC.getRolisSource(recipeKey, recipeArray, stripColor, attachItemLink) elseif origin == FURC_LUXURY then return FurC.getLuxurySource(recipeKey, recipeArray, stripColor, attachItemLink) elseif origin == FURC_GUILDSTORE then return GetString(SI_FURC_SEEN_IN_GUILDSTORE) elseif origin == FURC_VENDOR then return FurC.getAchievementVendorSource(recipeKey, recipeArray, stripColor, attachItemLink) elseif origin == FURC_FESTIVAL_DROP then return FurC.getEventDropSource(recipeKey, recipeArray, stripColor, attachItemLink) elseif origin == FURC_PVP then return FurC.getPvpSource(recipeKey, recipeArray, stripColor, attachItemLink) elseif origin == FURC_RUMOUR then return FurC.getRumourSource(recipeKey, recipeArray, stripColor, attachItemLink) else itemSource = FurC.GetMiscItemSource(recipeKey, recipeArray, stripColor, attachItemLink) end if not itemSource then FurC.settings.emptyItemSources[recipeKey] = ", --" .. GetItemLinkName(FurC.GetItemLink(recipeKey)) end return itemSource or GetString(SI_FURC_ITEMSOURCE_EMPTY) end function IsInFurC(link) link = FurC.GetItemLink(link) if IsItemLinkPlaceableFurniture(link) then return nil ~= FurnitureCatalogue.settings.data[ GetItemLinkItemId(link)] elseif IsItemLinkFurnitureRecipe(link) then local resultId = GetItemLinkItemId(GetItemLinkRecipeResultItemLink(link)) return nil ~= FurnitureCatalogue.settings.data[resultId] end return true end