Module:Recipe
From Against the Storm Official Wiki
Documentation for this module may be created at Module:Recipe/doc
------------------------------------------------------------------------------- -- This module renders the {{Recipe}} template with a nice wikitable. -- https://hoodedhorse.com/wiki/Against_the_Storm/Template:Recipe. -- -- The template #invokes Recipe.render(frame), with provided parameters. -- The template requires at least one of its three parameters, or it will -- return an error. -- -- The first parameter `product` specifies that you want a table rendered to -- show all recipes that result in the specified product. Make sure it is -- spelled correctly, as it appears in the game. -- -- The second parameter `building` species that you want a table rendered to -- show all recipes that are produced in the specified building. Again, make -- sure it is spelled and puncutated (apostrophes) correctly. -- -- The third, parameter `ingredient` species that you want a table rendered to -- show all the recipes that take that ingredient to produce, whether -- specifically (like Wood for Planks) or as an option among many. This -- parameter is intended to be used by itself, without a product or building -- named. -- -- The data for the recipes is retrieved from Module:RecipeData, which hides the -- variations in the original data from this module that extracts only what's -- needed to display one or more wikitables on a wiki page. Using the data, this -- module creates an HTML table. -- -- The table will have exactly one row if both the product and building are -- specified. If either is missing, then expect a table with multiple rows. -- If an ingredient is specified, then the table will likely be very long. -- @module Recipe local Recipe = {} --- -- Constants --- local INDEX_RECIPE_GRADE = 2 local INDEX_RECIPE_PRODTIME = 3 local INDEX_RECIPE_PRODUCT_STACK_SIZE = 4 local INDEX_RECIPE_PRODUCT_ID = 5 local INDEX_RECIPE_INGREDIENTS = 6 local INDEX_WORKSHOP_ID = 1 local INDEX_WORKSHOP_NAME = 2 local INDEX_WORKSHOP_INITIALLY_ESSENTIAL = 9 local RECIPE_INGRED_MAX = 3 local CSS_CLASS_WIKITABLE_SORTABLE = "wikitable sortable" local WIKI_RESOURCE_SMALL_ICON_MARKUP = "|thumb|32px|alt=" local WIKI_RESOURCE_LARGE_ICON_MARKUP = "|thumb|64px|alt=" local WIKI_BUILDING_ICON_MARKUP = "|thumb|left|64px|alt=" local BR = "<br />" local GRADE_TO_STARS = { ["Grade0"] = "[[File:Grade Stars 0.png|12px|link=|alt=0 Stars]]", ["Grade1"] = "★", ["Grade2"] = "★★", ["Grade3"] = "★★★" } --- -- Member variables --- local RecipeData = require('Module:RecipeData') --- -- Called from Template:Recipe to render a table displaying the information. -- -- Retrieves data from Module:RecipeData -- -- @param frame the template includes the calling context, mw.frame -- @return the formatted wikitext/HTML function Recipe.render(frame) -- Extract the passed parameters. local productName = frame.args.product or frame.args[1] local buildingName = frame.args.building or frame.args[2] local ingredientName = frame.args.ingredient or frame.args[3] -- At least one parameter is required. if productName == "" and buildingName == "" and ingredientName == "" then return "Error: The Recipe template requires that you specify at least one of the following: product, building, or ingredient." end -- Split according to parameters if ingredientName and ingredientName ~= "" then return buildIngredientWikitext(ingredientName) else if productName and productName ~= "" and buildingName and buildingName ~= "" then return buildProductBuildingWikitext(productName, buildingName) else if productName and productName ~= "" then return buildProductWikitext(productName) else if buildingName and buildingName ~= "" then return buildBuildingWikitext(buildingName) else return "Error: Unknown parameter error." end end end end end --- -- Renders the table for the specified product. The data retrieved from -- RecipeData looks like this: -- -- table of matches { -- match[recipeID] between recipe and workshops = { -- recipe data { ... } -- workshops that make it { -- workshop data { ... } -- workshop data { ... } -- workshop data { ... } -- } -- } -- match[recipeID] between recipe and workshops = { -- ... -- } -- } -- -- @param productName the name of the product -- @return the wikitext that renders the table function buildProductWikitext(productName) local targetRecipeMatches = RecipeData.getAllRecipesForProduct(productName) if not targetRecipeMatches then error("Error retrieving recipes for product: " .. productName) end local wikiTable = startNewWikiTable("Recipes for " .. productName .. ".") for _, match in pairs(targetRecipeMatches) do local recipe = match[1] for _, building in ipairs(match[2]) do wikiTable = addRowToTable(wikiTable, recipe, building) end end wikiTable = endWikiTable(wikiTable) return wikiTable end --- -- Renders the table for the specified building. The data retrieved from -- RecipeData looks like this: -- -- workshop, -- table of matches { -- match[recipeID] between recipe and workshops = { -- recipe data { ... }, -- workshops that make it { -- workshop data { ... } -- } -- }, -- match[recipeID] between recipe and workshops = { -- recipe data { ... }, -- workshops that make it { -- workshop data { ... } -- } -- } -- } -- -- @param buildingName the name of the building -- @return the wikitext that renders the table function buildBuildingWikitext(buildingName) local _, targetRecipeMatches = RecipeData.getAllRecipesAtBuilding(buildingName) if not targetRecipeMatches then error("Error retrieving recipes at building: " .. buildingName) end local wikiTable = startNewWikiTable("Recipes in the " .. buildingName .. ".") for _, match in pairs(targetRecipeMatches) do local recipe = match[1] for _, building in ipairs(match[2]) do wikiTable = addRowToTable(wikiTable, recipe, building) end end wikiTable = endWikiTable(wikiTable) return wikiTable end --- -- Renders the table for the specified product and building. The data returned -- by RecipeData looks like this: -- -- table of matches { -- match[recipeID] between recipe and workshops = { -- [1] = recipe data { ... } -- [2] = workshops that make it { -- workshop data { ... } -- } -- } -- } -- -- @param productName the name of the product -- @param buildingName the name of the building -- @return the wikitext that renders the table function buildProductBuildingWikitext(productName, buildingName) local targetRecipeMatches = RecipeData.getOneRecipeAtBuilding(productName, buildingName) if not targetRecipeMatches then error("Error retrieving the recipe for: " .. productName .. " (in the) " .. buildingName) end local wikiTable = startNewWikiTable("Recipe for " .. productName .. " in the " .. buildingName .. ".") for _, match in pairs(targetRecipeMatches) do local recipe = match[1] for _, building in ipairs(match[2]) do wikiTable = addRowToTable(wikiTable, recipe, building) end end wikiTable = endWikiTable(wikiTable) return wikiTable end --- -- Renders the table for the specified ingredient. The data retrieved from -- RecipeData looks like this: -- -- ingredient good data { ... }, -- table of matches { -- match[recipeID] between recipe and workshops = { -- recipe data { ... } -- workshops that make it { -- workshop data { ... } -- workshop data { ... } -- workshop data { ... } -- } -- } -- match[recipeID] between recipe and workshops = { -- ... -- } -- } -- -- @param ingredientName the name of the ingredient -- @return the wikitext that renders the table function buildIngredientWikitext(ingredientName) local _, targetRecipeMatches = RecipeData.getAllRecipesWithIngredient(ingredientName) if not targetRecipeMatches then error("Error retrieving recipes that require ingredient: " .. ingredientName) end local wikiTable = startNewWikiTable("Recipes that require " .. ingredientName .. ".") for _, match in pairs(targetRecipeMatches) do local recipe = match[1] for _, building in ipairs(match[2]) do wikiTable = addRowToTable(wikiTable, recipe, building) end end wikiTable = endWikiTable(wikiTable) return wikiTable end --- -- Adds a row to the existing wiki table based on the recipe and building -- provided. -- -- The structures look like this: -- recipe { -- id -- efficiency grade -- production time -- product stack size -- product good's id -- ingredients { -- options for ingredient #1 { -- { option #1 good's stack size, option #1 good's id } -- { option #2 good's stack size, option #2 good's id } -- { option #3 good's stack size, option #3 good's id } -- etc. -- } -- options for ingredient #2 { -- same as above -- } -- options for ingredient #3 { -- same as above -- } -- } -- } -- -- workshop { -- id -- display name -- description -- category -- size x -- size y -- city score -- movable -- initially essential -- storage -- construction time -- required goods { -- required good #1 stack size -- required good #1 id -- required good #2 stack size -- required good #2 id -- required good #3 stack size -- required good #3 id -- } -- workplaces { -- workplace #1 -- workplace #2 -- workplace #3 -- workplace #4 -- } -- recipes { -- recipe #1 -- recipe #2 -- recipe #3 -- recipe #4 -- } -- } -- -- @param wikiTable the table to continue adding to -- @param recipe the recipe to pull data from -- @param workshop the workshop to pull data from -- @return wikiTable, augmented with the new row function addRowToTable(wikiTable, recipe, workshop) if not wikiTable then error("Wikitable in-progress is not set up correctly.") else if not recipe or not workshop then error("Recipe and workshop to display not set up correctly.") end end -- Get everything extracted and ready to go for easier code below local buildingName = workshop[INDEX_WORKSHOP_NAME] local buildingIsEssential = workshop[INDEX_WORKSHOP_INITIALLY_ESSENTIAL] local buildingIconFile = workshop[INDEX_WORKSHOP_ID] .. "_icon.png" local recipeGrade = recipe[INDEX_RECIPE_GRADE] local recipeProdTime = recipe[INDEX_RECIPE_PRODTIME] local recipeProductStackSize = recipe[INDEX_RECIPE_PRODUCT_STACK_SIZE] local recipeProductID = recipe[INDEX_RECIPE_PRODUCT_ID] -- Now build the table tags. We're going to add everything to this row, so -- save the row itself to keep the hierarchy straight. local wikiTableRow = wikiTable:tag('tr') wikiTableRow:wikitext("\n") wikiTableRow:tag('td') :wikitext(writeBuildingCellContent(buildingIconFile, buildingName, recipeGrade, recipeProdTime)) :done() wikiTableRow:wikitext("\n") -- Skip empty cells for this recipe local blanks = 0 for i, tableToCheck in ipairs(recipe[INDEX_RECIPE_INGREDIENTS]) do if #tableToCheck == 0 then blanks = blanks + 1 end end for i = 1,blanks do wikiTableRow:tag('td'):done() wikiTableRow:wikitext("\n") end -- Then fill in the rest with the remaining ingredients, with inner tables -- to keep things aligned well for _, optionsGroup in ipairs(recipe[INDEX_RECIPE_INGREDIENTS]) do local innerTable = wikiTableRow:tag('td'):tag('table') -- Loop with step size of 2 to get the stack size and good together. for _, option in ipairs(optionsGroup) do local ingredientStackSize = option[1] local ingredientGoodID = option[2] innerTable:tag('tr') :tag('td'):wikitext("\'\'" .. ingredientStackSize .. "\'\'"):done() :tag('td'):wikitext(writeIngredientCellContent(ingredientGoodID)):done() :done() innerTable:wikitext("\n") end end wikiTableRow:tag('td') :wikitext("\'\'" .. recipeProductStackSize .. "\'\'" .. writeProductCellContent(recipeProductID)) :done() wikiTableRow:wikitext("\n") wikiTableRow:done() wikiTable:wikitext("\n") return wikiTable end --- -- Writes an icon and link to a resource page. -- -- @param ingredientGoodID the good ID to use to create the link -- @return a string of wiki markup function writeIngredientCellContent(ingredientGoodID) local goodName, goodIconFile = getGoodNameAndIcon(ingredientGoodID) local iconMarkup = "[[File:" .. goodIconFile .. WIKI_RESOURCE_SMALL_ICON_MARKUP .. goodName .. "|" .. goodName .. "]]" local linkMarkup = "[[" .. goodName .. "]]" return iconMarkup .. linkMarkup end --- -- Writes an icon and link to a resource page. -- -- @param productGoodID the good ID to use to create the link -- @return a string of wiki markup function writeProductCellContent(productGoodID) local goodName, goodIconFile = getGoodNameAndIcon(productGoodID) local iconMarkup = "[[File:" .. goodIconFile .. WIKI_RESOURCE_LARGE_ICON_MARKUP .. goodName .. "|" .. goodName .. "]]" local linkMarkup = "[[" .. goodName .. "]]" return iconMarkup .. linkMarkup end --- -- Writes the contents of the building cell and returns a string to be used -- in a call to wikitext() -- -- @param buildingIconFile icon filename, excluding 'File:' -- @param buildingName plain text name of the building to display -- @param recipeGrade efficiency of the recipe -- @param recipeProdTime production time in seconds -- @return a string of wiki markup function writeBuildingCellContent(buildingIconFile, buildingName, recipeGrade, recipeProdTime) local iconMarkup = "[[File:" .. buildingIconFile .. WIKI_BUILDING_ICON_MARKUP .. buildingName .. "|" .. buildingName .. "]]" local linkMarkup = "[[" .. buildingName .. "]]" local grade = GRADE_TO_STARS[recipeGrade] if not grade then error("Incorrectly formatted efficiency grade: " .. recipeGrade) end return iconMarkup .. BR .. linkMarkup .. BR .. grade .. BR .. formatTime(recipeProdTime) end --- -- Formats seconds as mm:ss format, with leading zeros where necessary. -- -- @param seconds number of seconds to reformat -- @return a string representing mm:ss time format function formatTime(seconds) local minutes = math.floor(seconds/60) local remainingSeconds = seconds % 60 return string.format("%02d:%02d", minutes, remainingSeconds) end --- -- Start all the tables the same way so they look the same -- @param the caption, if wanted -- @return the wikitext function startNewWikiTable(caption) local wikiTable = mw.html.create('table'):addClass(CSS_CLASS_WIKITABLE_SORTABLE) wikiTable:wikitext("\n") if caption then wikiTable:tag('caption'):wikitext(caption):done() wikiTable:wikitext("\n") end wikiTable:tag('tr') :tag('th'):wikitext("Building"):done() :tag('th'):wikitext("Ingredient #1"):done() :tag('th'):wikitext("Ingredient #2"):done() :tag('th'):wikitext("Ingredient #3"):done() :tag('th'):wikitext("Product"):done() wikiTable:wikitext("\n") return wikiTable end --- -- Finishes the wikitext by closing out remaining tags. -- -- @paramn wikiTable the table to close out -- @return the wikitext to display function endWikiTable(wikiTable) -- There might be more later. wikiTable:done() return wikiTable end return Recipe