--[[------------------------------------------------------------------ --SousChef.lua --Author: Wobin inspired by ingeniousclown's Research Assistant ------------------------------------------------------------------]]-- SousChef = {} SousChef.Utility = {} SousChef.Media = {} local SousChef = SousChef local u = SousChef.Utility local LAM = LibStub:GetLibrary("LibAddonMenu-2.0") local BACKPACK = ZO_PlayerInventoryBackpack local BANK = ZO_PlayerBankBackpack local GUILD_BANK = ZO_GuildBankBackpack SousChef.Pantry = {} SousChef.Cookbook = {} SousChef.CookbookIndex = {} SousChef.ReverseCookbook = {} SousChef.settings = nil SousChef.slotLines = {} SousChef.hookedFunctions = {} SousChef.hookedDataFunction = nil local rowHandler = {} local typeIconLookup = { ["Grilled"] = 9, ["Bread and Pies"] = 10, ["Soup and Stew"] = 11, ["Beer"] = 12, ["Spirits"] = 13, ["Wine"] = 14 } -- FUNCTIONS -- AddRecipe(Cookbook, link) adds the linked recipe to the player cookbook if it isn't there already. local function AddRecipe(Cookbook, link) for _,v in pairs(Cookbook) do if v == link then return end end table.insert(Cookbook, link) end -- RefreshViews() incorporates changes to the rendered inventory if it is visible. function SousChef:RefreshViews() ZO_ScrollList_RefreshVisible(BACKPACK) ZO_ScrollList_RefreshVisible(BANK) ZO_ScrollList_RefreshVisible(GUILD_BANK) end -- ParseRecipes() goes through the player's known recipes and records their info. function SousChef:ParseRecipes() local lists = GetNumRecipeLists() for listIndex = 1, lists do local name, count = GetRecipeListInfo(listIndex) for recipeIndex = 1, count do if GetRecipeInfo(listIndex, recipeIndex) then -- Store the recipes known: local recipeName = u.CleanString((GetRecipeResultItemInfo(listIndex, recipeIndex))) -- in this character's cookbook SousChef.Cookbook[recipeName] = true -- and in the settings, so other characters can see what recipes this character knows if not SousChef.settings.Cookbook[recipeName] then SousChef.settings.Cookbook[recipeName] = {} end SousChef.settings.Cookbook[recipeName][GetUnitName("player")] = true -- now record information about the recipe's ingregients local _, _, ingredientCount, level, _, specialType = GetRecipeInfo(listIndex, recipeIndex) -- store the recipe's index numbers and number of ingredients -- XXX: why does this table get indexed with the formatted item name, but the Pantry table uses the whole link? SousChef.CookbookIndex[zo_strformat(SI_TOOLTIP_ITEM_NAME, GetRecipeResultItemLink(listIndex, recipeIndex, LINK_STYLE_BRACKETS))] = {listIndex = listIndex, recipeIndex = recipeIndex, numIngredients = ingredientCount} -- now, for every ingredient in the current recipe... for ingredientIndex = 1, ingredientCount do local link = u.GetItemID(GetRecipeIngredientItemLink(listIndex, recipeIndex, ingredientIndex, LINK_STYLE_DEFAULT)) -- Store the fact that the ingredient is used if ingredientIndex < 3 or not SousChef.settings.showSpecialIngredients then -- if this isn't a special ingredient, or if the user doesn't want special ingredients marked as special, just record the highest-tier recipe this ingredient appears in -- for this character, SousChef.Pantry[link] = math.max(level, SousChef.Pantry[link] or 0) -- and across characters SousChef.settings.Pantry[link] = math.max(level, SousChef.Pantry[link] or 0) else -- if this is a special ingredient, record: -- type icon number if user wants type icons, 7 if ingredient is a flavoring, 8 otherwise (ie, ingredient is a spice) SousChef.Pantry[link] = SousChef.settings.typeIcon and typeIconLookup[name] or specialType == PROVISIONER_SPECIAL_INGREDIENT_TYPE_FLAVORING and 7 or 8 SousChef.settings.Pantry[link] = SousChef.settings.typeIcon and typeIconLookup[name] or specialType == PROVISIONER_SPECIAL_INGREDIENT_TYPE_FLAVORING and 7 or 8 end -- Store the recipe it's used in to the character reverseCookbook if not SousChef.ReverseCookbook[link] then SousChef.ReverseCookbook[link] = {} end AddRecipe(SousChef.ReverseCookbook[link], zo_strformat(SI_TOOLTIP_ITEM_NAME, GetRecipeResultItemLink(listIndex, recipeIndex, LINK_STYLE_BRACKETS))) -- ...and to the account-wide reverseCookbook if not SousChef.settings.ReverseCookbook[link] then SousChef.settings.ReverseCookbook[link] = {} end AddRecipe(SousChef.settings.ReverseCookbook[link], zo_strformat(SI_TOOLTIP_ITEM_NAME, GetRecipeResultItemLink(listIndex, recipeIndex, LINK_STYLE_BRACKETS))) end end end end SousChef:RefreshViews() end -- SousChefCreateSettings() creates the configuration menu for the add-on local function SousChefCreateSettings() local panelData = { type = "panel", name = "Sous Chef", displayName = "Sous Chef", author = "Wobin", version = "1.18", registerForRefresh = true, } LAM:RegisterAddonPanel("SousChefSettings", panelData) local optionsMenu = { [1] = { type = "header", name = "Recipe Options", width = "full", }, [2] = { type = "checkbox", name = "Have Sous Chef check for recipes", tooltip = "Non English clients may want to untick this if the experimental matching isn't sufficient", getFunc = function() return SousChef.settings.processRecipes end, setFunc = function(value) SousChef.settings.processRecipes = not SousChef.settings.processRecipes end, width = "full", }, [3] = { type = "dropdown", name = "Mark if recipes are ", tooltip = "How do you want Sous Chef to indicate your knowledge of a recipe?", choices = {"known", "unknown"}, getFunc = function() return SousChef.settings.checkKnown end, setFunc = function(valueString) SousChef.settings.checkKnown = valueString end, disabled = function() return not SousChef.settings.processRecipes end, }, [4] = { type = "checkbox", name = "Alternate Character Check", tooltip = "Indicate if an alt knows the recipe on unknown recipes. Will only work if the above setting is set to 'unknown'", getFunc = function() return SousChef.settings.markAlt end, setFunc = function(value) SousChef.settings.markAlt = not SousChef.settings.markAlt SousChef:RefreshViews() end, disabled = function() return not SousChef.settings.processRecipes end, }, [5] = { type = "checkbox", name = "Alternate Character Recipe Knowledge", tooltip = "Show in tooltips which characters know a recipe", getFunc = function() return SousChef.settings.showAltKnowledge end, setFunc = function(value) SousChef.settings.showAltKnowledge = not SousChef.settings.showAltKnowledge SousChef:RefreshViews() end, disabled = function() return not SousChef.settings.processRecipes end, }, [6] = { type = "checkbox", name = "Use the Experimental Recipe Matcher", tooltip = "Currently Sous Chef cannot match reliably in other languages. This will attempt to match as best it can by stripping out common prepositions so that the recipe and result match better. (NOTE: please file a typo bug report with Zenimax if you find recipe names that don't match their results exactly)", getFunc = function() return SousChef.settings.experimentalMatch end, setFunc = function(value) SousChef.settings.experimentalMatch = not SousChef.settings.experimentalMatch end, disabled = function() return not SousChef.settings.processRecipes end, }, [7] = { type = "checkbox", name = "Sort the Provisioning Table", tooltip = "Will sort the provisioning table by Rank, Quality, then Name (but not by required level)", getFunc = function() return SousChef.settings.sortProvisioningTable end, setFunc = function(value) SousChef.settings.sortProvisioningTable = not SousChef.settings.sortProvisioningTable end, }, [8] = { type = "header", name = "Tooltip Options", width = "full", }, [9] = { type = "checkbox", name = "Show Sous Chef in Ingredient tooltips after Mouse Click", tooltip = "Only show Sous Chef information in ingredient tooltips after a mouse click, to save space", warning = "Requires UI Reload", getFunc = function() return SousChef.settings.showOnClick end, setFunc = function(value) SousChef.settings.showOnClick = not SousChef.settings.showOnClick end, }, [10] = { type = "checkbox", name = "Show Recipe Result Counts", tooltip = "Show how many of each recipe can be made in the Ingredient tooltip", getFunc = function() return SousChef.settings.showCounts end, setFunc = function(value) SousChef.settings.showCounts = not SousChef.settings.showCounts end, }, [11] = { type = "checkbox", name = "Alternate Character Ingredient Knowledge", tooltip = "Indicte if an alt uses the ingredients", getFunc = function() return SousChef.settings.showAltIngredientKnowledge end, setFunc = function(value) SousChef.settings.showAltIngredientKnowledge = not SousChef.settings.showAltIngredientKnowledge SousChef:RefreshViews() end, }, [12] = { type = "header", name = "Indicator Options", width = "full", }, [13] = { type = "checkbox", name = "Use bolder icons", tooltip = "Swap out rank icons to a more flat display", getFunc = function() return SousChef.settings.boldIcon end, setFunc = function(value) SousChef.settings.boldIcon = not SousChef.settings.boldIcon SousChef:RefreshViews() end, }, [14] = { type = "checkbox", name = "Show Special Ingredients", tooltip = "If an ingredient is considered a Spice/Flavour, indicate that rather than the tier of the highest tier recipe that uses it", getFunc = function() return SousChef.settings.showSpecialIngredients end, setFunc = function(value) SousChef.settings.showSpecialIngredients = not SousChef.settings.showSpecialIngredients SousChef.ParseRecipes() end, }, [15] = { type = "checkbox", name = "Show Special Ingredient Types", tooltip = "Use the icon for the type of food it's a special ingredient for, eg Grilled", getFunc = function() return SousChef.settings.typeIcon end, setFunc = function(value) SousChef.settings.typeIcon = not SousChef.settings.typeIcon SousChef.ParseRecipes() SousChef:RefreshViews() end, disabled = function() not SousChef.settings.showSpecialIngredients end, }, [16] = { type = "colorpicker", name = "Indicator Colour", tooltip = "Allows you to set the colour of the recipe indicator", getFunc = function() return SousChef.settings.colour[1], SousChef.settings.colour[2], SousChef.settings.colour[3] end, setFunc = function(r,g,b) SousChef.settings.colour[1] = r; SousChef.settings.colour[2] = g; SousChef.settings.colour[3] = b; SousChef:RefreshViews() end, }, [17] = { type = "colorpicker", name = "Shopping List Indicator Colour", tooltip = "Allows you to set the colour of hte indicator for ingredients in your Shopping List", getFunc = function() return SousChef.settings.shoppingColour[1], SousChef.settings.shoppingColour[2], SousChef.settings.shoppingColour[3] end, setFunc = function(r,g,b) SousChef.settings.shoppingColour[1] = r; SousChef.settings.shoppingColour[2] = g; SousChef.settings.shoppingColour[3] = b; SousChef:RefreshViews() end, }, [18] = { type = "checkbox", name = "Only Mark Shopping List Ingredients", tooltip = "Only mark ingredients on your Shopping List", getFunc = function() return SousChef.settings.onlyShowShopping end, setFunc = function(value) SousChef.settings.onlyShowShopping = not SousChef.settings.onlyShowShopping SousChef:RefreshViews() end, }, [19] = { type = "checkbox", name = "Sort Ingredients in Inventory", tooltip = "Will sort known ingredients by rank", getFunc = function() return SousChef.settings.sortKnownIngredients end, setFunc = function(value) SousChef.settings.sortKnownIngredients = not SousChef.settings.sortKnownIngredients if not SousChef.settings.sortKnownIngredients then SousChef.UnregisterSort() else SousChef.SetupSort() end end, }, } LAM:RegisterOptionControls("SousChefSettings", optionsMenu) ZO_CreateStringId("SI_BINDING_NAME_SC_MARK_RECIPE", "Mark Recipe") end -- SousChef_Loaded(eventCode, addOnName) runs when the EVENT_ADD_ON_LOADED event fires. local function SousChef_Loaded(eventCode, addOnName) if(addOnName ~= "SousChef") then return end -- default config settings local defaults = { watching = true, checkKnown = "unknown", markAlt = false, colour = {1, 1, 1}, shoppingColour = {0,1,1}, Cookbook = {}, Pantry = {}, ReverseCookbook = {}, showAltKnowledge = false, showAltIngredientKnowledge = false, boldIcon = false, typeIcon = true, experimentalMatch = false, processRecipes = true, showSpecialIngredients = false, ignoredRecipes = {}, showOnClick = false, showCounts = true, shoppingList = {}, onlyShowShopping = false, qualityChecked = false, sortProvisioningTable = true, sortKnownIngredients = false } -- Fetch the saved variables SousChef.settings = ZO_SavedVars:NewAccountWide("SousChef_Settings", 9, GetCVar("Language.2"), defaults) local function tablelength(T) local count = 0 for _ in pairs(T) do count = count + 1 end return count end -- define some slash commands SLASH_COMMANDS['/scstats'] = function() d("Number of recipes known: ".. tablelength(SousChef.settings.Cookbook)) d("Number of ingredients tracked: "..tablelength(SousChef.settings.Pantry)) end SLASH_COMMANDS['/sciadd'] = SousChef.AddRecipeToIgnoreList SLASH_COMMANDS['/sciremove'] = SousChef.RemoveRecipeFromIgnoreList SLASH_COMMANDS['/scilist'] = SousChef.ListIgnoredRecipes -- initialize the configuration menu SousChefCreateSettings() -- parse the recipes this character knows, in a second zo_callLater(SousChef.ParseRecipes, 1000) SousChef:UpdateProvisioningTable() SousChef:HookGetRecipeInfo() if SousChef.settings.sortKnownIngredients then SousChef.SetupSort() end -- Now we register for some events, and hook into the function that sets the details on the inventory slot zo_callLater(SousChef.HookEvents, 3000) end -- HookEvents() registers the add-on for some events function SousChef.HookEvents() -- let us know if we're opening a trading house, so we can look at its contents EVENT_MANAGER:RegisterForEvent("SousChefTrading", EVENT_TRADING_HOUSE_RESPONSE_RECEIVED, SousChef.HookTrading) -- let us know if we've learned a new recipe, so we can integrate it into our cookbook EVENT_MANAGER:RegisterForEvent("SousChefLearnt", EVENT_RECIPE_LEARNED, SousChef.ParseRecipes) -- let us know when we open a crafting station, so we can sort the recipe tree EVENT_MANAGER:RegisterForEvent("SousChefProvi", EVENT_CRAFTING_STATION_INTERACT, function(...) SousChef:HookRecipeTree(...) end) EVENT_MANAGER:RegisterForEvent("SousChefProviEnd", EVENT_END_CRAFTING_STATION_INTERACT, function() SousChef:UnhookRecipeTree() end) -- and finally, hook the opening of the inventory, bank, guild bank, and loot windows so we can add icons and hook the tooltips SousChef.HookInventory() end EVENT_MANAGER:RegisterForEvent("SousChefLoaded", EVENT_ADD_ON_LOADED, SousChef_Loaded)