--[[------------------------------------------------------------------ --SousChef.lua --Author: Wobin inspired by ingeniousclown's Research Assistant Thanks to Ayantir for the French translations, and sirinsidiator for the German ------------------------------------------------------------------]]-- SousChef = {} SousChef.Utility = {} SousChef.Media = {} SousChef.version = "2.18" 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 SousChef.lang = GetCVar("Language.2") if SousChef.lang ~= "en" and SousChef.lang ~= "de" and SousChef.lang ~= "fr" then SousChef.lang = "en" end local rowHandler = {} local typeIconLookup = { -- en ["Grilled"] = 9, ["Bread and Pies"] = 10, ["Soup and Stew"] = 11, ["Beer"] = 12, ["Spirits"] = 13, ["Wine"] = 14, -- de ["Gegrillt"] = 9, ["Brot und Kuchen"] = 10, ["Suppen und Eintöpfe"] = 11, ["Bier"] = 12, ["Getränke"] = 13, ["Wein"] = 14, -- fr ["Grillades"] = 9, ["Pain et Tartes"] = 10, ["Soupe et Ragoût"] = 11, ["Bi\195\169re"] = 12, ["Spiritueux"] = 13, ["Vin"] = 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 -- every time you change the main character in the settings menu, you need to update the "player" cookbook, cookbook index, pantry, and reverse cookbook with the new character's data local function ChangeMainChar() if SousChef.settings.mainChar == "(current)" then SousChef.CookbookIndex = SousChef.settings.CookbookIndex[GetUnitName("player")] else SousChef.CookbookIndex = SousChef.settings.CookbookIndex[SousChef.settings.mainChar] end SousChef.Cookbook = {} SousChef.Pantry = {} SousChef.ReverseCookbook = {} if SousChef.CookbookIndex == nil then SousChef.CookbookIndex = {} end for name, data in pairs(SousChef.CookbookIndex) do SousChef.Cookbook[u.CleanString((GetRecipeResultItemInfo(data.listIndex, data.recipeIndex)))] = true local _, _, _, level, _, specialType = GetRecipeInfo(data.listIndex, data.recipeIndex) for ingredient = 1, data.numIngredients do local ingredientID = u.GetItemID(GetRecipeIngredientItemLink(data.listIndex, data.recipeIndex, ingredient)) if ingredient < 3 or not SousChef.settings.showSpecialIngredients then SousChef.Pantry[ingredientID] = math.max(level, SousChef.Pantry[ingredientID] or 0) else SousChef.Pantry[ingredientID] = SousChef.settings.typeIcon and typeIconLookup[name] or specialType == PROVISIONER_SPECIAL_INGREDIENT_TYPE_FLAVORING and 7 or 8 end if not SousChef.ReverseCookbook[ingredientID] then SousChef.ReverseCookbook[ingredientID] = {} end AddRecipe(SousChef.ReverseCookbook[ingredientID], name) end end 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 local resultLink = GetRecipeResultItemLink(listIndex, recipeIndex) local coloredName = u.GetColoredLinkName(resultLink) if not SousChef.settings.CookbookIndex[GetUnitName("player")] then SousChef.settings.CookbookIndex[GetUnitName("player")] = {} end SousChef.settings.CookbookIndex[GetUnitName("player")][coloredName] = {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], coloredName) -- ...and to the account-wide reverseCookbook if not SousChef.settings.ReverseCookbook[link] then SousChef.settings.ReverseCookbook[link] = {} end AddRecipe(SousChef.settings.ReverseCookbook[link], coloredName) end end end end ChangeMainChar() SousChef:RefreshViews() end -- auto-junk ingredients if they're not in the shopping list local function AutoJunker() local bagSize = GetBagSize(BAG_BACKPACK) for i = 0, bagSize do local itemLink = GetItemLink(BAG_BACKPACK, i) if itemLink ~= "" then local itemType = GetItemLinkItemType(itemLink) if itemType == ITEMTYPE_FLAVORING or itemType == ITEMTYPE_SPICE or itemType == ITEMTYPE_INGREDIENT then if not SousChef:IsOnShoppingList(u.GetItemID(itemLink)) then SetItemIsJunk(BAG_BACKPACK, i, true) end end end end end -- SousChefCreateSettings() creates the configuration menu for the add-on local function SousChefCreateSettings() local str = SousChef.Strings[SousChef.lang] local panelData = { type = "panel", name = "Sous Chef", displayName = "Sous Chef", author = "Wobin & KatKat42 & CrazyDutchGuy", version = SousChef.version, registerForRefresh = true, slashCommand = "/souschef", } LAM:RegisterAddonPanel("SousChefSettings", panelData) local optionsMenu = { [1] = { type = "header", name = str.MENU_RECIPE_HEADER, width = "full", }, [2] = { type = "checkbox", name = str.MENU_PROCESS_RECIPES, tooltip = str.MENU_PROCESS_RECIPES_TOOLTIP, getFunc = function() return SousChef.settings.processRecipes end, setFunc = function(value) SousChef.settings.processRecipes = value end, width = "full", }, [3] = { type = "dropdown", name = str.MENU_MAIN_CHAR, tooltip = str.MENU_MAIN_CHAR_TOOLTIP, choices = SousChef.settings.knownChars, getFunc = function() return SousChef.settings.mainChar end, setFunc = function(value) SousChef.settings.mainChar = value ChangeMainChar() end, disabled = function() return not SousChef.settings.processRecipes end, }, [4] = { type = "dropdown", name = str.MENU_MARK_IF_KNOWN, tooltip = str.MENU_MARK_IF_KNOWN_TOOLTIP, choices = {str.MENU_KNOWN, str.MENU_UNKNOWN}, getFunc = function() if SousChef.settings.checkKnown == "known" then return str.MENU_KNOWN elseif SousChef.settings.checkKnown == "unknown" then return str.MENU_UNKNOWN else -- can't happen d("Yikes! MENU_MARK_IF_KNOWN getter") return str.MENU_UNKNOWN end end, setFunc = function(valueString) if valueString == str.MENU_KNOWN then SousChef.settings.checkKnown = "known" elseif valueString == str.MENU_UNKNOWN then SousChef.settings.checkKnown = "unknown" else -- can't happen d("Oops! MENU_MARK_IF_KNOWN setter") SousChef.settings.checkKnown = "unknown" end end, disabled = function() return not SousChef.settings.processRecipes end, }, [5] = { type = "checkbox", name = str.MENU_MARK_IF_ALT_KNOWS, tooltip = str.MENU_MARK_IF_ALT_KNOWS_TOOLTIP, getFunc = function() return SousChef.settings.markAlt end, setFunc = function(value) SousChef.settings.markAlt = value SousChef:RefreshViews() end, disabled = function() return (not SousChef.settings.processRecipes) or (SousChef.settings.checkKnown == "known") end, }, [6] = { type = "checkbox", name = str.MENU_TOOLTIP_IF_ALT_KNOWS, tooltip = str.MENU_TOOLTIP_IF_ALT_KNOWS_TOOLTIP, getFunc = function() return SousChef.settings.showAltKnowledge end, setFunc = function(value) SousChef.settings.showAltKnowledge = value SousChef:RefreshViews() end, disabled = function() return not SousChef.settings.processRecipes end, }, [7] = { type = "checkbox", name = str.MENU_MATCHER, tooltip = str.MENU_MATCHER_TOOLTIP, getFunc = function() return SousChef.settings.experimentalMatch end, setFunc = function(value) SousChef.settings.experimentalMatch = value end, disabled = function() return not SousChef.settings.processRecipes end, }, [8] = { type = "checkbox", name = str.MENU_SORT_PROVISIONING, tooltip = str.MENU_SORT_PROVISIONING_TOOLTIP, getFunc = function() return SousChef.settings.sortProvisioningTable end, setFunc = function(value) SousChef.settings.sortProvisioningTable = value if value then SousChef:HookGetRecipeInfo() else SousChef:UnhookGetRecipeInfo() end end, }, [9] = { type = "header", name = str.MENU_TOOLTIP_HEADER, width = "full", }, [10] = { type = "checkbox", name = str.MENU_TOOLTIP_CLICK, tooltip = str.MENU_TOOLTIP_CLICK_TOOLTIP, warning = str.MENU_RELOAD, getFunc = function() return SousChef.settings.showOnClick end, setFunc = function(value) SousChef.settings.showOnClick = value end, }, [11] = { type = "checkbox", name = str.MENU_RESULT_COUNTS, tooltip = str.MENU_RESULT_COUNTS_TOOLTIP, getFunc = function() return SousChef.settings.showCounts end, setFunc = function(value) SousChef.settings.showCounts = value end, }, [12] = { type = "checkbox", name = str.MENU_ALT_USE, tooltip = str.MENU_ALT_USE_TOOLTIP, getFunc = function() return SousChef.settings.showAltIngredientKnowledge end, setFunc = function(value) SousChef.settings.showAltIngredientKnowledge = value SousChef:RefreshViews() end, }, [13] = { type = "header", name = str.MENU_INDICATOR_HEADER, width = "full", }, [14] = { type = "checkbox", name = str.MENU_ICON_SET, tooltip = str.MENU_ICON_SET_TOOLTIP, getFunc = function() return SousChef.settings.boldIcon end, setFunc = function(value) SousChef.settings.boldIcon = value SousChef:RefreshViews() end, }, [15] = { type = "checkbox", name = str.MENU_SPECIAL_ICONS, tooltip = str.MENU_SPECIAL_ICONS_TOOLTIP, getFunc = function() return SousChef.settings.showSpecialIngredients end, setFunc = function(value) SousChef.settings.showSpecialIngredients = value SousChef.ParseRecipes() end, }, [16] = { type = "checkbox", name = str.MENU_SPECIAL_TYPES, tooltip = str.MENU_SPECIAL_TYPES_TOOLTIP, getFunc = function() return SousChef.settings.typeIcon end, setFunc = function(value) SousChef.settings.typeIcon = value SousChef.ParseRecipes() SousChef:RefreshViews() end, disabled = function() return not SousChef.settings.showSpecialIngredients end, }, [17] = { type = "colorpicker", name = str.MENU_INDICATOR_COLOR, tooltip = str.MENU_INDICATOR_COLOR_TOOLTIP, 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, }, [18] = { type = "colorpicker", name = str.MENU_SHOPPING_COLOR, tooltip = str.MENU_SHOPPING_COLOR_TOOLTIP, 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, }, [19] = { type = "checkbox", name = str.MENU_SHOW_ALT_SHOPPING, tooltip = str.MENU_SHOW_ALT_SHOPPING_TOOLTIP, getFunc = function() return SousChef.settings.showAltShopping end, setFunc = function(value) SousChef.settings.showAltShopping = value SousChef:RefreshViews() end, }, [20] = { type = "checkbox", name = str.MENU_ONLY_MARK_SHOPPING, tooltip = str.MENU_ONLY_MARK_SHOPPING_TOOLTIP, getFunc = function() return SousChef.settings.onlyShowShopping end, setFunc = function(value) SousChef.settings.onlyShowShopping = value SousChef:RefreshViews() end, }, [21] = { type = "checkbox", name = str.MENU_AUTO_JUNK, tooltip = str.MENU_AUTO_JUNK_TOOLTIP, getFunc = function() return SousChef.settings.autoJunk end, setFunc = function(value) if value then SousChef.settings.autoJunk = true EVENT_MANAGER:RegisterForEvent("SousChefLootJunker", EVENT_LOOT_CLOSED, function(...) zo_callLater(AutoJunker, 100) end) else SousChef.settings.autoJunk = false EVENT_MANAGER:UnregisterForEvent("SousChefLootJunker", EVENT_LOOT_CLOSED) end end, warning = str.MENU_AUTO_JUNK_WARNING, }, [22] = { type = "checkbox", name = str.MENU_SORT_INGREDIENTS, tooltip = str.MENU_SORT_INGREDIENTS_TOOLTIP, 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) 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 = {}, CookbookIndex = {}, 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, mainChar = GetUnitName("player"), knownChars = { "(current)" }, autoJunk = false, showAltShopping = true, } local localized = SousChef.Strings[SousChef.lang] -- Fetch the saved variables SousChef.settings = ZO_SavedVars:NewAccountWide("SousChef_Settings", 10, SousChef.lang, defaults) -- if this character isn't in the list of known chars, add it local addMe = true local addCurrent = true for _, v in pairs(SousChef.settings.knownChars) do if GetUnitName("player") == v then addMe = false end if v == "(current)" then addCurrent = false end end if addMe then local myName = GetUnitName("player") table.insert(SousChef.settings.knownChars, GetUnitName("player")) end if addCurrent then table.insert(SousChef.settings.knownChars, "(current)") end -- define some slash commands SLASH_COMMANDS['/scstats'] = function() d(localized.SC_NUM_RECIPES_KNOWN.. NonContiguousCount(SousChef.settings.Cookbook)) d(localized.SC_NUM_INGREDIENTS_TRACKED..NonContiguousCount(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, 500) SousChef:UpdateProvisioningTable() SousChef:HookGetRecipeInfo() if SousChef.settings.sortKnownIngredients then SousChef.SetupSort() end ZO_CreateStringId("SI_BINDING_NAME_SC_MARK_RECIPE", localized.KEY_MARK) -- Now we register for some events, and hook into the function that sets the details on the inventory slot zo_callLater(SousChef.HookEvents, 2000) 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) -- if the user has turned on auto-junking unmarked ingredients, set that up if SousChef.settings.autoJunk then EVENT_MANAGER:RegisterForEvent("SousChefLootJunker", EVENT_LOOT_CLOSED, function(...) zo_callLater(AutoJunker, 100) end) end -- 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)