--[[------------------------------------------------------------------ --SousChef.lua --Author: Wobin inspired by ingeniousclown's Research Assistant ------------------------------------------------------------------]]-- SousChef = {} local LAM = LibStub:GetLibrary("LibAddonMenu-1.0") local BACKPACK = ZO_PlayerInventoryBackpack local BANK = ZO_PlayerBankBackpack local GUILD_BANK = ZO_GuildBankBackpack local COOKING_RANK_1 = [[SousChef\media\One.dds]] local COOKING_RANK_2 = [[SousChef\media\Two.dds]] local COOKING_RANK_3 = [[SousChef\media\Three.dds]] local COOKING_RANK_4 = [[SousChef\media\Four.dds]] local COOKING_RANK_5 = [[SousChef\media\Five.dds]] local COOKING_RANK_6 = [[SousChef\media\Six.dds]] local COOKING_RANK_1B = [[SousChef\media\One_flat.dds]] local COOKING_RANK_2B = [[SousChef\media\Two_flat.dds]] local COOKING_RANK_3B = [[SousChef\media\Three_flat.dds]] local COOKING_RANK_4B = [[SousChef\media\Four_flat.dds]] local COOKING_RANK_5B = [[SousChef\media\Five_flat.dds]] local COOKING_RANK_6B = [[SousChef\media\Six_flat.dds]] local COOKING = { COOKING_RANK_1, COOKING_RANK_2, COOKING_RANK_3, COOKING_RANK_4, COOKING_RANK_5, COOKING_RANK_6 } local COOKINGB = { COOKING_RANK_1B, COOKING_RANK_2B, COOKING_RANK_3B, COOKING_RANK_4B, COOKING_RANK_5B, COOKING_RANK_6B } local CANLEARN = [[/esoui/art/loot/loot_finesseitem.dds]] local containerHooks = { INVENTORY_BACKPACK, INVENTORY_BANK, INVENTORY_GUILD_BANK } local itemQuality = { ITEM_QUALITY_MAGIC = { 0, 0, 1 }, ITEM_QUALITY_NORMAL = {1,1,1}, ITEM_QUALITY_ARCANE = {1, 0, 1}} SousChef.Pantry = {} SousChef.Cookbook = {} SousChef.ReverseCookbook = {} SousChef.settings = nil SousChef.slotLines = {} SousChef.hookedFunctions = {} SousChef.hookedDataFunction = nil local function GetItemID(link) return tonumber(string.match(string.match(link, "%d+:"), "%d+")) end local function EndsWith(String,End) return End=='' or string.sub(String,-string.len(End))==End end local function StartsWith(String,Start) return Start=='' or string.sub(String, 1, string.len(Start))==Start end local function CleanString(entry) return entry:gsub("%^(%a+)", ""):gsub(" ",""):gsub("-",""):lower() end local function MatchesRecipe(entry) return CleanString(entry):find(CleanString(GetString(SI_ITEMTYPE29))) end local function MatchInCookbook(name) name = CleanString(name) for recipe,known in pairs(SousChef.Cookbook) do if StartsWith(name,recipe) or EndsWith(name, recipe) then if (#recipe + #CleanString(GetString(SI_ITEMTYPE29)) - #name) < 3 then return known end end end return nil end local function MatchInGlobalCookbook(name) name = CleanString(name) for recipe,known in pairs(SousChef.settings.Cookbook) do if StartsWith(name,recipe) or EndsWith(name, recipe) then if (#recipe + #CleanString(GetString(SI_ITEMTYPE29)) - #name) < 3 then return known end end end return nil end rowClicked = {} local function AddDetails(row) if not row.dataEntry or not row.dataEntry.data or rowClicked[row] then return false end local rowInfo = row.dataEntry.data local bagId = rowInfo.bagId local slotIndex = rowInfo.slotIndex if MatchesRecipe(rowInfo.name) then local gmatch = MatchInGlobalCookbook(rowInfo.name) if gmatch then ItemTooltip:AddLine("") ItemTooltip:AddLine("Known by ", "ZoFontWinH5", 1,1,1, BOTTOM, MODIFY_TEXT_TYPE_UPPERCASE) for i,v in pairs(gmatch) do ItemTooltip:AddLine(i) end rowClicked[row] = true return end end if ((GetItemCraftingInfo(bagId, slotIndex)) ~= CRAFTING_TYPE_PROVISIONING) then return false end local usableIngredient = SousChef.ReverseCookbook[GetItemID(GetItemLink(bagId, slotIndex))] if SousChef.settings.showAltKnowledge then usableIngredient = SousChef.settings.ReverseCookbook[GetItemID(GetItemLink(bagId, slotIndex))] end if usableIngredient then ItemTooltip:AddLine("Used in:", "ZoFontWinH5", 1,1,1, BOTTOM, MODIFY_TEXT_TYPE_UPPERCASE) for i,v in ipairs(usableIngredient) do ItemTooltip:AddLine(v) end rowClicked[row] = true end return false end local function getIcon(row) local rankIcon = SousChef.slotLines[row:GetName()] if(not rankIcon) then rankIcon = WINDOW_MANAGER:CreateControl(row:GetName() .. "SousChef", row, CT_TEXTURE) SousChef.slotLines[row:GetName()] = rankIcon ZO_PreHookHandler(row, "OnMouseDown", AddDetails) ZO_PreHookHandler(row, "OnMouseExit", function(self) rowClicked[self] = nil return false end ) end return rankIcon end local rowHandler = {} local function AddRankToSlot(row) local slot = row.dataEntry.data local bagId = slot.bagId local slotIndex = slot.slotIndex local rankIcon = getIcon(row) -- Allow for ingeniousclown's Inventory Grid View if row:GetWidth() - row:GetHeight() < 5 then -- if we're mostly square rankIcon:SetDimensions(20,20) rankIcon:SetAnchor(TOPLEFT, row, TOPLEFT, 2) else rankIcon:SetDimensions(30, 30) rankIcon:SetAnchor(CENTER, row, CENTER, 200) end rankIcon:SetHidden(true) if ((GetItemCraftingInfo(bagId, slotIndex)) == CRAFTING_TYPE_PROVISIONING) then local texture = SousChef.Pantry[GetItemID(GetItemLink(bagId, slotIndex))] if SousChef.settings.showAltKnowledge then texture = SousChef.settings.Pantry[GetItemID(GetItemLink(bagId, slotIndex))] end if texture then rankIcon:SetColor(SousChef.settings.colour[1], SousChef.settings.colour[2], SousChef.settings.colour[3]) rankIcon:SetHidden(false) if SousChef.settings.boldIcon then rankIcon:SetTexture(COOKINGB[texture]) else rankIcon:SetTexture(COOKING[texture]) end end else if GetItemType(bagId, slotIndex) == ITEMTYPE_RECIPE then local match = MatchInCookbook(slot.name) local gmatch = MatchInGlobalCookbook(slot.name) if (match and SousChef.settings.checkKnown == "known") or (not match and SousChef.settings.checkKnown == "unknown")then rankIcon:SetTexture(CANLEARN) rankIcon:SetHidden(false) if not match and gmatch and SousChef.settings.checkKnown == "unknown" and SousChef.settings.markAlt then rankIcon:SetColor(1,1,1,0.2) else rankIcon:SetColor(1,1,1,1) end end end end end local function AddTradingSlot(row, result) local rankIcon = getIcon(row) rankIcon:SetHidden(true) if MatchesRecipe(result.name) then local match = MatchInCookbook(result.name) local gmatch = MatchInGlobalCookbook(result.name) if (match and SousChef.settings.checkKnown == "known") or (not match and SousChef.settings.checkKnown == "unknown")then rankIcon:SetDimensions(30, 30) rankIcon:SetAnchor(CENTER, row, CENTER, 230) rankIcon:SetTexture(CANLEARN) rankIcon:SetHidden(false) if not match and gmatch and SousChef.settings.checkKnown == "unknown" and SousChef.settings.markAlt then rankIcon:SetColor(1,1,1,0.2) else rankIcon:SetColor(1,1,1,1) end end end end local function AddRecipe(Cookbook, link) for _,v in pairs(Cookbook) do if v == link then return end end table.insert(Cookbook, link) end local function 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 = CleanString((GetRecipeResultItemInfo(listIndex, recipeIndex))) SousChef.Cookbook[recipeName] = true if not SousChef.settings.Cookbook[recipeName] then SousChef.settings.Cookbook[recipeName] = {} end SousChef.settings.Cookbook[recipeName][GetUnitName("player")] = true local _, _, ingredientCount, level = GetRecipeInfo(listIndex, recipeIndex) for ingredientIndex = 1, ingredientCount do local link = GetItemID(GetRecipeIngredientItemLink(listIndex, recipeIndex, ingredientIndex, LINK_STYLE_NORMAL)) -- Store the fact that the ingredient is used SousChef.Pantry[link] = math.max(level, SousChef.Pantry[link] or 0) SousChef.settings.Pantry[link] = math.max(level, SousChef.Pantry[link] or 0) -- Store the recipe it's used in 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))) -- Store the global reference 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 end local function SousChefCreateSettings() d("Creating menu") local panel = LAM:CreateControlPanel("SousChefMenu", "Sous Chef") LAM:AddHeader(panel, "SousChef_General", "Settings") LAM:AddDropdown(panel, "markLearnt", "Mark if recipes are ", "How do you want Sous Chef to indicate your knowledge of a recipe?", {"known", "unknown"}, function() return SousChef.settings.checkKnown end, function(valueString) SousChef.settings.checkKnown = valueString end) LAM:AddCheckbox(panel, "markAltKnows", "Alternate Character Check", "Indicate if an alt knows the recipe on unknown recipes. Will only work if the above setting is set to 'unknown'", function() return SousChef.settings.markAlt end, function(value) SousChef.settings.markAlt = not SousChef.settings.markAlt end) LAM:AddCheckbox(panel, "showAltKnows", "Alternate Character Recipe Knowledge", "Show rank indicators on alts for all recipe knowledge of all alternate characters", function() return SousChef.settings.showAltKnowledge end, function(value) SousChef.settings.showAltKnowledge = not SousChef.settings.showAltKnowledge end) LAM:AddCheckbox(panel, "useBold", "Use bolder icons", "Swap out rank icon to a more flat display", function() return SousChef.settings.boldIcon end, function(value) SousChef.settings.boldIcon = not SousChef.settings.boldIcon end) LAM:AddColorPicker(panel, "setColour", "Indicator colour", "Allows you to set the colour of the indicator", function() return SousChef.settings.colour[1], SousChef.settings.colour[2], SousChef.settings.colour[3] end, function(r,g,b) SousChef.settings.colour[1] = r; SousChef.settings.colour[2] = g; SousChef.settings.colour[3] = b end) end local function HookTrading(...) if SousChef.hookedDataFunction then return end SousChef.hookedDataFunction = ZO_TradingHouseItemPaneSearchResults.dataTypes[1].setupCallback ZO_TradingHouseItemPaneSearchResults.dataTypes[1].setupCallback = function(...) local row, data = ... SousChef.hookedDataFunction(...) AddTradingSlot(row, data) end end local function SousChef_Loaded(eventCode, addOnName) if(addOnName ~= "SousChef") then return end local defaults = { watching = true, checkKnown = "unknown", markAlt = false, colour = {1, 1, 1}, Cookbook = {}, Pantry = {}, ReverseCookbook = {}, showAltKnowledge = false, boldIcon = false } SousChef.settings = ZO_SavedVars:NewAccountWide("SousChef_Settings", 8, nil, defaults) local function tablelength(T) local count = 0 for _ in pairs(T) do count = count + 1 end return count end SLASH_COMMANDS['/scstats'] = function() d("Number of recipes known: ".. tablelength(SousChef.settings.Cookbook)) d("Number of ingredients tracked: "..tablelength(SousChef.settings.Pantry)) end SousChefCreateSettings() ParseRecipes() -- Now we want to hook into the function that sets the details on the inventory slot for _,v in pairs(PLAYER_INVENTORY.inventories) do local listView = v.listView if listView and listView.dataTypes and listView.dataTypes[1] then SousChef.hookedFunctions = listView.dataTypes[1].setupCallback listView.dataTypes[1].setupCallback = function(rowControl, slot) SousChef.hookedFunctions(rowControl, slot) AddRankToSlot(rowControl) end end end ZO_ScrollList_RefreshVisible(BACKPACK) ZO_ScrollList_RefreshVisible(BANK) ZO_ScrollList_RefreshVisible(GUILD_BANK) EVENT_MANAGER:RegisterForEvent("SousChefTRading", EVENT_TRADING_HOUSE_RESPONSE_RECEIVED, HookTrading) EVENT_MANAGER:RegisterForEvent("SousChefLearnt", EVENT_RECIPE_LEARNED, ParseRecipes) end local function SousChef_Initialized() EVENT_MANAGER:RegisterForEvent("SousChefLoaded", EVENT_ADD_ON_LOADED, SousChef_Loaded) end SousChef_Initialized()