Module:RecipeController: Difference between revisions
From Against the Storm Official Wiki
(Added method to filter ingredients at a building; fixed service working as a product) |
m (Preparing better rendering of display override list view) |
||
Line 130: | Line 130: | ||
buildingName, buildingIcon, | buildingName, buildingIcon, | ||
gradeStars, productionTime, ingredientsList, | gradeStars, productionTime, ingredientsList, | ||
productStackSize, productName, productIcon) | productStackSize, productName, productIcon, requiredBuilding) | ||
end | end | ||
Line 168: | Line 168: | ||
buildingName, buildingIcon, | buildingName, buildingIcon, | ||
gradeStars, productionTime, ingredientsList, | gradeStars, productionTime, ingredientsList, | ||
resultStackSize, serviceName, serviceIcon) | resultStackSize, serviceName, serviceIcon, requiredBuilding) | ||
end | end | ||
end | end | ||
Line 203: | Line 203: | ||
gradeStars, (plantingTime + harvestingTime), | gradeStars, (plantingTime + harvestingTime), | ||
ingredientsList, stackSize, | ingredientsList, stackSize, | ||
productName, productIcon) | productName, productIcon, requiredBuilding) | ||
end | end | ||
Line 240: | Line 240: | ||
gradeStars, gatheringTime, | gradeStars, gatheringTime, | ||
ingredientsList, stackSize, | ingredientsList, stackSize, | ||
productName, productIcon) | productName, productIcon, requiredBuilding) | ||
end | end | ||
Revision as of 03:14, 27 November 2023
Documentation for this module may be created at Module:RecipeController/doc
--- --- Module for merging recipe information from data managers. --- --- @module RecipeData local RecipeController = {} --region Dependencies local RecipeView = require("Module:RecipeView") local WorkshopsRecipesData = require("Module:WorkshopsRecipesData") local ServicesRecipesData = require("Module:ServicesRecipesData") local InstitutionsData = require("Module:InstitutionsData") local WorkshopsData = require("Module:WorkshopsData") local FarmsData = require("Module:FarmsData") local CampsData = require("Module:CampsData") local GoodsData = require("Module:GoodsData") --endregion --region Private member variables --endregion --region Private constants local MAX_INGREDIENTS = 3 local INDEX_OPTION_STACK_SIZE = "stackSize" local INDEX_OPTION_GOOD_NAME = "name" local INDEX_OPTION_GOOD_ICON = "icon" local TABLE_HEADER_BOTH = "Product or Service" local TABLE_HEADER_PRODUCT = "Product" local TABLE_HEADER_SERVICE = "Service" --endregion --region Private methods ---extractIngredientsNamesAndIcons ---@param recipeID string the ID of the recipe from which to extract ingredients ---@return table of ingredients and options (stack size, name, and icon) local function extractIngredientsNamesAndIcons(recipeID) local ingredientsList = {} -- Need to convert IDs to names and icons and keep stack size. for i = 1, MAX_INGREDIENTS do for j = 1, WorkshopsRecipesData.getRecipeNumberOfOptionsByID(recipeID, i) do if not ingredientsList[i] then ingredientsList[i] = {} end local goodID, stackSize = WorkshopsRecipesData.getRecipeOptionByID(recipeID, i, j) ingredientsList[i][j] = {} ingredientsList[i][j][INDEX_OPTION_STACK_SIZE] = stackSize ingredientsList[i][j][INDEX_OPTION_GOOD_NAME] = GoodsData.getGoodNameByID(goodID) ingredientsList[i][j][INDEX_OPTION_GOOD_ICON] = GoodsData.getGoodIconByID(goodID) end end return ingredientsList end ---extractServiceGoodsNamesAndIcons ---@param recipeID string the ID of the recipe from which to extract ingredients ---@return table of ingredients and options (stack size, name, and icon) local function extractServiceGoodsNamesAndIcons(recipeID) local ingredientsList = {} -- Need to convert IDs to names and icons and keep stack size. for i = 1, ServicesRecipesData.getRecipeNumberOfServiceGoodsByID(recipeID) do ingredientsList[i] = {} -- Need to structure this the same way as when there are several -- ingredient option groups. But for services there's just one. local j = 1 ingredientsList[i][j] = {} local goodID, stackSize = ServicesRecipesData.getRecipeOptionByID(recipeID, i) ingredientsList[i][j][INDEX_OPTION_STACK_SIZE] = stackSize ingredientsList[i][j][INDEX_OPTION_GOOD_NAME] = GoodsData.getGoodNameByID(goodID) ingredientsList[i][j][INDEX_OPTION_GOOD_ICON] = GoodsData.getGoodIconByID(goodID) end return ingredientsList end local function buildRowsForWorkshops(recipeListFromWorkshops, displayOverride, numIngredients, requiredBuilding) -- Build the rows of the middle of the view. If it's nil and there are no -- recipes, skip it by getting ipairs on an empty table. for _, recipeID in ipairs(recipeListFromWorkshops or {}) do local gradeStars = WorkshopsRecipesData.getRecipeGradeByID(recipeID) local productionTime = WorkshopsRecipesData.getRecipeProductionTimeByID(recipeID) local productID, productStackSize = WorkshopsRecipesData.getRecipeProductByID(recipeID) local ingredientsList = extractIngredientsNamesAndIcons(recipeID) local productName = GoodsData.getGoodNameByID(productID) local productIcon = GoodsData.getGoodIconByID(productID) local buildingListFromWorkshops = WorkshopsData.getWorkshopNamesWithRecipeID(recipeID) for _, buildingName in ipairs(buildingListFromWorkshops) do -- If we're screening for a building (not nil), then we have -- to match the current building if not requiredBuilding or buildingName == requiredBuilding then local buildingIcon = WorkshopsData.getWorkshopIcon(buildingName) RecipeView.addRowForRecipe(displayOverride, numIngredients, buildingName, buildingIcon, gradeStars, productionTime, ingredientsList, productStackSize, productName, productIcon, requiredBuilding) end end end end local function buildRowsForServices(recipeListFromServices, displayOverride, numIngredients, requiredBuilding) -- Same. If it's nil and there are no recipes, skip it by getting ipairs -- on an empty table. for _, recipeID in ipairs(recipeListFromServices or {}) do local gradeStars = ServicesRecipesData.getRecipeGradeByID(recipeID) local serviceName = ServicesRecipesData.getRecipeServiceNameByID(recipeID) local serviceIcon = ServicesRecipesData.getRecipeServiceIconByID(recipeID) local ingredientsList = extractServiceGoodsNamesAndIcons(recipeID) local buildingListFromInstitutions = InstitutionsData.getInstitutionNamesWithRecipeID(recipeID) local productionTime -- = nil local resultStackSize -- = nil for _, buildingName in ipairs(buildingListFromInstitutions) do -- If we're screening for a building (not nil), then we have -- to match the current building if not requiredBuilding or buildingName == requiredBuilding then local buildingIcon = InstitutionsData.getInstitutionIcon(buildingName) RecipeView.addRowForRecipe(displayOverride, numIngredients, buildingName, buildingIcon, gradeStars, productionTime, ingredientsList, resultStackSize, serviceName, serviceIcon, requiredBuilding) end end end end local function buildRowsForFarms(buildingListFromFarms, displayOverride, numIngredients, productID, requiredBuilding) for _, buildingName in ipairs(buildingListFromFarms or {}) do -- If we're screening for a building (not nil), then we have to match -- the current building if not requiredBuilding or buildingName == requiredBuilding then for i = 1, FarmsData.getFarmNumberOfRecipes(buildingName) do local goodID, stackSize = FarmsData.getFarmRecipeProduct(buildingName, i) -- If we're screening for a productID (not nil), then we have -- to match the current goodID if not productID or goodID == productID then local buildingIcon = FarmsData.getFarmIcon(buildingName) local gradeStars, plantingTime, harvestingTime = FarmsData.getFarmRecipeStats(buildingName, i) local ingredientsList = {} local productName = GoodsData.getGoodNameByID(goodID) local productIcon = GoodsData.getGoodIconByID(goodID) RecipeView.addRowForRecipe(displayOverride, numIngredients, buildingName, buildingIcon, gradeStars, (plantingTime + harvestingTime), ingredientsList, stackSize, productName, productIcon, requiredBuilding) end end end end end local function buildRowsForCamps(buildingListFromCamps, displayOverride, numIngredients, productID, requiredBuilding) for _, buildingName in ipairs(buildingListFromCamps or {}) do -- If we're screening for a building (not nil), then we have to match -- the current building if not requiredBuilding or buildingName == requiredBuilding then for i = 1, CampsData.getCampNumberOfRecipes(buildingName) do local goodID, stackSize = CampsData.getCampRecipeProduct(buildingName, i) -- If we're screening for a productID (not nil), then we have to -- match the current goodID if not productID or goodID == productID then local buildingIcon = CampsData.getCampIcon(buildingName) local gradeStars, gatheringTime = CampsData.getCampRecipeStats(buildingName, i) local ingredientsList = {} local productName = GoodsData.getGoodNameByID(goodID) local productIcon = GoodsData.getGoodIconByID(goodID) RecipeView.addRowForRecipe(displayOverride, numIngredients, buildingName, buildingIcon, gradeStars, gatheringTime, ingredientsList, stackSize, productName, productIcon, requiredBuilding) end end end end end --- --- Takes two lists and returns the intersection of the two. Slightly more --- efficient if the second list is smaller than the first. --- ---@param list1 table a flat table (list of values) ---@param list2 table another flat table ---@return table a similarly flat table consisting only of the values that are in both provided lists local function findIntersection(list1, list2) local intersection = {} -- Create a new mapping to efficiently check for existence in list2 by -- checking the same ID in the next loop. local setList2 = {} for _, value in ipairs(list2 or {}) do setList2[value] = true end -- Check for intersection and populate the result table. for _, valueToFind in ipairs(list1 or {}) do if setList2[valueToFind] then table.insert(intersection, valueToFind) end end if #intersection == 0 then return nil end return intersection end --endregion --region Public methods function RecipeController.renderWithIngredientAndBuilding(ingredientName, buildingName, displayOverride) -- Service and workshops recipes have ingredients. Camps and farms do not. local ingredientID = GoodsData.getGoodID(ingredientName) if not ingredientID then return "No ingredient found with that name: " .. ingredientName .. "." end local recipeListFromWorkshopsRecipes = WorkshopsRecipesData.getAllRecipeIDsWithIngredientID(ingredientID) local recipeListFromWorkshops = WorkshopsData.getAllWorkshopRecipes(buildingName) recipeListFromWorkshops = findIntersection(recipeListFromWorkshopsRecipes, recipeListFromWorkshops) local recipeListFromServicesRecipes = ServicesRecipesData.getAllRecipeIDsWithServiceGoodID(ingredientID) local recipeListFromServices = InstitutionsData.getAllInstitutionRecipes(buildingName) recipeListFromServices = findIntersection(recipeListFromServicesRecipes, recipeListFromServices) -- Find out what the largest number of ingredients in this table is. Need -- this before we start building any of the beginning and the middle so -- that they are all the same. local numIngredients = 1 if recipeListFromWorkshops then for _, recipeID in ipairs(recipeListFromWorkshops) do local thisNum = WorkshopsRecipesData.getRecipeNumberOfIngredientsByID(recipeID) if numIngredients < thisNum then numIngredients = thisNum end end end if recipeListFromServices then for _, recipeID in ipairs(recipeListFromServices) do local thisNum = ServicesRecipesData.getRecipeNumberOfServiceGoodsByID(recipeID) if numIngredients < thisNum then numIngredients = thisNum end end end -- Count the number of recipes in both the Workshops and Services. local numRecipes = 0 local header -- = nil if recipeListFromWorkshops ~= nil then numRecipes = numRecipes + #recipeListFromWorkshops header = TABLE_HEADER_PRODUCT end if recipeListFromServices ~= nil then if numRecipes > 0 then header = TABLE_HEADER_BOTH else header = TABLE_HEADER_SERVICE end numRecipes = numRecipes + #recipeListFromServices end if numRecipes == 0 then return "No recipes found for ingredient " .. ingredientName .. " in the building " .. buildingName .. "." end RecipeView.startViewForIngredientAndBuilding(ingredientName, buildingName, displayOverride, numRecipes, numIngredients, header) buildRowsForWorkshops(recipeListFromWorkshops, displayOverride, numIngredients, buildingName) buildRowsForServices(recipeListFromServices, displayOverride, numIngredients, buildingName) return RecipeView.endView(displayOverride) end function RecipeController.renderWithIngredient(ingredientName, displayOverride) -- Service and workshops recipes have ingredients. Camps and farms do not. local ingredientID = GoodsData.getGoodID(ingredientName) if not ingredientID then return "No ingredient found with that name: " .. ingredientName .. "." end local recipeListFromWorkshops = WorkshopsRecipesData.getAllRecipeIDsWithIngredientID(ingredientID) local recipeListFromServices = ServicesRecipesData.getAllRecipeIDsWithServiceGoodID(ingredientID) -- Find out what the largest number of ingredients in this table is. Need -- this before we start building any of the beginning and the middle so -- that they are all the same. local numIngredients = 1 if recipeListFromWorkshops then for _, recipeID in ipairs(recipeListFromWorkshops) do local thisNum = WorkshopsRecipesData.getRecipeNumberOfIngredientsByID(recipeID) if numIngredients < thisNum then numIngredients = thisNum end end end if recipeListFromServices then for _, recipeID in ipairs(recipeListFromServices) do local thisNum = ServicesRecipesData.getRecipeNumberOfServiceGoodsByID(recipeID) if numIngredients < thisNum then numIngredients = thisNum end end end -- Count the number of recipes in both the Workshops and Services. local numRecipes = 0 local header -- = nil if recipeListFromWorkshops ~= nil then numRecipes = numRecipes + #recipeListFromWorkshops header = TABLE_HEADER_PRODUCT end if recipeListFromServices ~= nil then if numRecipes > 0 then header = TABLE_HEADER_BOTH else header = TABLE_HEADER_SERVICE end numRecipes = numRecipes + #recipeListFromServices end if numRecipes == 0 then return "No recipes found for ingredient: " .. ingredientName .. "." end RecipeView.startViewForIngredient(ingredientName, displayOverride, numRecipes, numIngredients, header) buildRowsForWorkshops(recipeListFromWorkshops, displayOverride, numIngredients) buildRowsForServices(recipeListFromServices, displayOverride, numIngredients) return RecipeView.endView(displayOverride) end function RecipeController.renderWithProductAndBuilding(productName, buildingName, displayOverride) -- Get lists from all buildings local productID = GoodsData.getGoodID(productName) local recipeListFromWorkshopsRecipes if productID then recipeListFromWorkshopsRecipes = WorkshopsRecipesData.getAllRecipeIDsForProductID(productID) end local recipeListFromWorkshops = WorkshopsData.getAllWorkshopRecipes(buildingName) recipeListFromWorkshops = findIntersection(recipeListFromWorkshopsRecipes, recipeListFromWorkshops) -- It could be the name of a service instead of the name of a product. local recipeListFromServicesRecipes = ServicesRecipesData.getAllRecipeIDsForServiceName(productName) local recipeListFromServices = InstitutionsData.getAllInstitutionRecipes(buildingName) recipeListFromServices = findIntersection(recipeListFromServicesRecipes, recipeListFromServices) -- These result in a list of buildings, not recipes. local buildingListFromCamps local buildingListFromFarms if productID then buildingListFromCamps = CampsData.getCampNamesWithRecipeProductID(productID) buildingListFromFarms = FarmsData.getFarmNamesWithRecipeProductID(productID) end buildingListFromCamps = findIntersection(buildingListFromCamps, {buildingName} ) buildingListFromFarms = findIntersection(buildingListFromFarms, {buildingName}) -- Find out what the largest number of ingredients in this table is. Need -- this before we start building any of the beginning and the middle so -- that they are all the same. local numIngredients = 1 if recipeListFromWorkshops then for _, recipeID in ipairs(recipeListFromWorkshops) do local thisNum = WorkshopsRecipesData.getRecipeNumberOfIngredientsByID(recipeID) if numIngredients < thisNum then numIngredients = thisNum end end end if recipeListFromServices then for _, recipeID in ipairs(recipeListFromServices) do local thisNum = ServicesRecipesData.getRecipeNumberOfServiceGoodsByID(recipeID) if numIngredients < thisNum then numIngredients = thisNum end end end -- Count the number of recipes in all the lists local numRecipes = 0 local header -- = nil if recipeListFromWorkshops ~= nil then numRecipes = numRecipes + #recipeListFromWorkshops header = TABLE_HEADER_PRODUCT end if buildingListFromCamps ~= nil then numRecipes = numRecipes + #buildingListFromCamps header = TABLE_HEADER_PRODUCT end if buildingListFromFarms ~= nil then numRecipes = numRecipes + #buildingListFromFarms header = TABLE_HEADER_PRODUCT end if recipeListFromServices ~= nil then if numRecipes > 0 then header = TABLE_HEADER_BOTH else header = TABLE_HEADER_SERVICE end numRecipes = numRecipes + #recipeListFromServices end if numRecipes == 0 then return "No recipes found for product " .. productName .. " at the building " .. buildingName .. "." end RecipeView.startViewForProductAndBuilding(productName, buildingName, displayOverride, numRecipes, numIngredients, header) buildRowsForWorkshops(recipeListFromWorkshops, displayOverride, numIngredients, buildingName) buildRowsForServices(recipeListFromServices, displayOverride, numIngredients, buildingName) buildRowsForFarms(buildingListFromFarms, displayOverride, numIngredients, productID, buildingName) buildRowsForCamps(buildingListFromCamps, displayOverride, numIngredients, productID, buildingName) return RecipeView.endView(displayOverride) end function RecipeController.renderWithProduct(productName, displayOverride) -- Get lists from all buildings local productID = GoodsData.getGoodID(productName) local recipeListFromWorkshops if productID then recipeListFromWorkshops = WorkshopsRecipesData.getAllRecipeIDsForProductID(productID) end -- It could be the name of a service instead of the name of a product. local recipeListFromServices = ServicesRecipesData.getAllRecipeIDsForServiceName(productName) -- These result in a list of buildings, not recipes. local buildingListFromCamps local buildingListFromFarms if productID then buildingListFromCamps = CampsData.getCampNamesWithRecipeProductID(productID) buildingListFromFarms = FarmsData.getFarmNamesWithRecipeProductID(productID) end -- Find out what the largest number of ingredients in this table is. Need -- this before we start building any of the beginning and the middle so -- that they are all the same. local numIngredients = 1 if recipeListFromWorkshops then for _, recipeID in ipairs(recipeListFromWorkshops) do local thisNum = WorkshopsRecipesData.getRecipeNumberOfIngredientsByID(recipeID) if numIngredients < thisNum then numIngredients = thisNum end end end if recipeListFromServices then for _, recipeID in ipairs(recipeListFromServices) do local thisNum = ServicesRecipesData.getRecipeNumberOfServiceGoodsByID(recipeID) if numIngredients < thisNum then numIngredients = thisNum end end end -- Count the number of recipes in all the lists local numRecipes = 0 local header -- = nil if recipeListFromWorkshops ~= nil then numRecipes = numRecipes + #recipeListFromWorkshops header = TABLE_HEADER_PRODUCT end if buildingListFromCamps ~= nil then numRecipes = numRecipes + #buildingListFromCamps header = TABLE_HEADER_PRODUCT end if buildingListFromFarms ~= nil then numRecipes = numRecipes + #buildingListFromFarms header = TABLE_HEADER_PRODUCT end if recipeListFromServices ~= nil then if numRecipes > 0 then header = TABLE_HEADER_BOTH else header = TABLE_HEADER_SERVICE end numRecipes = numRecipes + #recipeListFromServices end if numRecipes == 0 then return "No recipes found for product: " .. productName .. "." end RecipeView.startViewForProduct(productName, displayOverride, numRecipes, numIngredients, header) buildRowsForWorkshops(recipeListFromWorkshops, displayOverride, numIngredients) buildRowsForServices(recipeListFromServices, displayOverride, numIngredients) buildRowsForFarms(buildingListFromFarms, displayOverride, numIngredients, productID) buildRowsForCamps(buildingListFromCamps, displayOverride, numIngredients, productID) return RecipeView.endView(displayOverride) end function RecipeController.renderWithBuilding(buildingName, displayOverride) local recipeListFromWorkshops = WorkshopsData.getAllWorkshopRecipes(buildingName) local recipeListFromServices = InstitutionsData.getAllInstitutionRecipes(buildingName) local buildingListFromCamps = {} if CampsData.getCampNumberOfRecipes(buildingName) > 0 then buildingListFromCamps = { buildingName } end local buildingListFromFarms = {} if FarmsData.getFarmNumberOfRecipes(buildingName) > 0 then buildingListFromFarms = { buildingName } end -- Find out what the largest number of ingredients in this table is. Need -- this before we start building any of the beginning and the middle so -- that they are all the same. local numIngredients = 1 if recipeListFromWorkshops then for _, recipeID in ipairs(recipeListFromWorkshops) do local thisNum = WorkshopsRecipesData.getRecipeNumberOfIngredientsByID(recipeID) if numIngredients < thisNum then numIngredients = thisNum end end end if recipeListFromServices then for _, recipeID in ipairs(recipeListFromServices) do local thisNum = ServicesRecipesData.getRecipeNumberOfServiceGoodsByID(recipeID) if numIngredients < thisNum then numIngredients = thisNum end end end -- Count the number of recipes in all the lists local numRecipes = 0 local header -- = nil if recipeListFromWorkshops ~= nil then numRecipes = numRecipes + #recipeListFromWorkshops header = TABLE_HEADER_PRODUCT end if buildingListFromCamps ~= nil then numRecipes = numRecipes + #buildingListFromCamps header = TABLE_HEADER_PRODUCT end if buildingListFromFarms ~= nil then numRecipes = numRecipes + #buildingListFromFarms header = TABLE_HEADER_PRODUCT end if recipeListFromServices ~= nil then if numRecipes > 0 then header = TABLE_HEADER_BOTH else header = TABLE_HEADER_SERVICE end numRecipes = numRecipes + #recipeListFromServices end if numRecipes == 0 then return "No recipes found at building: " .. buildingName .. "." end RecipeView.startViewForBuilding(buildingName, displayOverride, numRecipes, numIngredients, header) buildRowsForWorkshops(recipeListFromWorkshops, displayOverride, numIngredients, buildingName) buildRowsForServices(recipeListFromServices, displayOverride, numIngredients, buildingName) buildRowsForFarms(buildingListFromFarms, displayOverride, numIngredients, nil, buildingName) buildRowsForCamps(buildingListFromCamps, displayOverride, numIngredients, nil, buildingName) return RecipeView.endView(displayOverride) end --endregion --region Public methods function RecipeController.renderRecipe(frame) -- Extract the template parameters. local productName = frame.args.product or frame.args[1] local buildingName = frame.args.building or frame.args[2] local ingredientName = frame.args.ingredient local displayOverride = frame.args.display if ingredientName and ingredientName ~= "" and buildingName and buildingName ~= "" then return RecipeController.renderWithIngredientAndBuilding(ingredientName, buildingName, displayOverride) else if ingredientName and ingredientName ~= "" then return RecipeController.renderWithIngredient(ingredientName, displayOverride) else if productName and productName ~= "" and buildingName and buildingName ~= "" then return RecipeController.renderWithProductAndBuilding(productName, buildingName, displayOverride) else if productName and productName ~= "" then return RecipeController.renderWithProduct(productName, displayOverride) else if buildingName and buildingName ~= "" then return RecipeController.renderWithBuilding(buildingName, displayOverride) else return "Unknown parameter in Recipe template." end end end end end end --endregion return RecipeController