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:Test') --- -- 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 ingredient = frame.args.ingredient or frame.args[3] -- At least one parameter is required. if not productName and not buildingName and not ingredient 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 ingredient then return buildIngredientWikitext(ingredient) else if productName and buildingName then return buildProductBuildingWikitext(productName, buildingName) else if productName then return buildProductWikitext(productName) else if 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