Module:Recipe: Difference between revisions
From Against the Storm Official Wiki
(handling the check for "list" in lua instead of the template) |
(Made the product row sort by the product name instead of the stack size. This is important for tables other than specifying product, since in that case the stack sizes are all the same anyway.) |
||
Line 275: | Line 275: | ||
wikiTable = startNewWikiTable("Recipes that require " .. ingredientName .. ".") | wikiTable = startNewWikiTable("Recipes that require " .. ingredientName .. ".") | ||
end | end | ||
-- Add the data retrieved to the wiki table | -- Add the data retrieved to the wiki table | ||
for _, match in pairs(targetRecipeMatches) do | for _, match in pairs(targetRecipeMatches) do | ||
Line 433: | Line 433: | ||
end | end | ||
wikiTableRow:tag('td') | local productCell, productName = writeProductCellContent(recipeProductID) | ||
:wikitext(BOLD .. recipeProductStackSize .. BOLD .. NBSP .. | |||
-- Sort on product name, not the stack size | |||
wikiTableRow:tag('td'):attr("data-sort-value", productName) | |||
:wikitext(BOLD .. recipeProductStackSize .. BOLD .. NBSP .. productCell) | |||
:done() | :done() | ||
wikiTableRow:wikitext("\n") | wikiTableRow:wikitext("\n") | ||
Line 468: | Line 471: | ||
-- @param productGoodID the good ID to use to create the link | -- @param productGoodID the good ID to use to create the link | ||
-- @return a string of wiki markup | -- @return a string of wiki markup | ||
-- @return the string name of the product | |||
function writeProductCellContent(productGoodID) | function writeProductCellContent(productGoodID) | ||
Line 475: | Line 479: | ||
local linkMarkup = "[[" .. goodName .. "]]" | local linkMarkup = "[[" .. goodName .. "]]" | ||
return iconMarkup .. NBSP .. linkMarkup | return iconMarkup .. NBSP .. linkMarkup, goodName | ||
end | end | ||
Revision as of 23:12, 11 November 2023
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 -- -- Currently this is the max number of ingredients a recipe can have local RECIPE_INGRED_MAX = 3 -- Indexes to use for recipe records as they come from the CSV data 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 -- Indexes to use for workshop records as they come from the CSV data local INDEX_WORKSHOP_ID = 1 local INDEX_WORKSHOP_NAME = 2 -- CSS styling for the recipe table... for now local CSS_CLASS_WIKITABLE = "wikitable sortable mw-collapsible" -- Shortcut markup to go after the filename to add the extension and display parameters local WIKI_MED_ICON_MARKUP = ".png|32px|alt=" local WIKI_LARGE_ICON_MARKUP = ".png|64px|alt=" -- Shortcut markup that's easier for Lua string concatenations local BR = "<br />" local NBSP = " " local BOLD = "'''" -- Shortcuts for displaying the efficency grades as stars or the "low meter" 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. The data retrieved looks like this: -- -- (sometimes there's an extra first variable like workshop or ingredient) -- targetRecipeMatches = { -- [recipeID] match between recipe and workshops = { -- [1] = { recipe data }, -- [2] = list of workshops that make it = { -- { workshop data }, -- { workshop data }, -- { workshop data } -- } -- }, -- [recipeID] match between recipe and workshops = { -- similar -- } -- } -- -- @param frame the template passes parameters and page data via 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 local display = frame.args.display local displayListOverride = false if display == "list" then displayListOverride = true end -- At least one parameter is required. if productName == "" and buildingName == "" and ingredientName == "" then return "The Recipe template requires that you specify at least one of the following: product, building, or ingredient." end -- Decide according to parameters if ingredientName and ingredientName ~= "" then return buildIngredientWikitext(ingredientName, displayListOverride) else if productName and productName ~= "" and buildingName and buildingName ~= "" then return buildProductBuildingWikitext(productName, buildingName, displayListOverride) else if productName and productName ~= "" then return buildProductWikitext(productName, displayListOverride) else if buildingName and buildingName ~= "" then return buildBuildingWikitext(buildingName, displayListOverride) else return "Error in Recipe template: Unknown parameter error." end end end end end --- -- Renders the table for the specified product. -- -- @param productName the name of the product -- @return the wikitext that renders the table function buildProductWikitext(productName, displayListOverride) local targetRecipeMatches = RecipeData.getAllRecipesForProduct(productName) if not targetRecipeMatches then return "There are no recipes for product " .. productName .. "." end -- Start a wiki table the same way each time, and then populate each row. local wikiTable if displayListOverride then wikiTable = startList() else wikiTable = startNewWikiTable("Recipes for " .. productName .. ".") end -- Add the data retrieved to the wiki table for _, match in pairs(targetRecipeMatches) do local recipe = match[1] for _, workshop in ipairs(match[2]) do wikiTable = addRowToTable(wikiTable, recipe, workshop, displayListOverride) end end wikiTable = endWikiTable(wikiTable) return wikiTable end --- -- Renders the table for the specified building. -- -- @param buildingName the name of the building -- @return the wikitext that renders the table function buildBuildingWikitext(buildingName, displayListOverride) -- Ignore the workshop also returned. local _, targetRecipeMatches = RecipeData.getAllRecipesAtBuilding(buildingName) if not targetRecipeMatches then return "There are no recipes in the building " .. buildingName .. "." end -- Start a wiki table the same way each time, and then populate each row. local wikiTable if displayListOverride then wikiTable = startList() else wikiTable = startNewWikiTable("Recipes in the " .. buildingName .. ".") end -- Add the data retrieved to the wiki table for _, match in pairs(targetRecipeMatches) do local recipe = match[1] for _, workshop in ipairs(match[2]) do wikiTable = addRowToTable(wikiTable, recipe, workshop, displayListOverride) end end wikiTable = endWikiTable(wikiTable) return wikiTable end --- -- Renders the table for the specified product and building. -- -- @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, displayListOverride) local targetRecipeMatches = RecipeData.getOneRecipeAtBuilding(productName, buildingName) if not targetRecipeMatches then return "There is no recipe for product " .. productName .. " in the building " .. buildingName .. "." end -- Start a wiki table the same way each time, and then populate each row. local wikiTable if displayListOverride then wikiTable = startList() else wikiTable = startNewWikiTable("Recipe for " .. productName .. " in the " .. buildingName .. ".") end -- Add the data retrieved to the wiki table for _, match in pairs(targetRecipeMatches) do local recipe = match[1] for _, workshop in ipairs(match[2]) do wikiTable = addRowToTable(wikiTable, recipe, workshop, displayListOverride) end end wikiTable = endWikiTable(wikiTable) return wikiTable end --- -- Renders the table for the specified ingredient. -- -- @param ingredientName the name of the ingredient -- @return the wikitext that renders the table function buildIngredientWikitext(ingredientName, displayListOverride) -- Ignore the ingredient also returned local _, targetRecipeMatches = RecipeData.getAllRecipesWithIngredient(ingredientName) if not targetRecipeMatches then return "There are no recipes for ingredient " .. ingredientName .. "." end -- Start a wiki table the same way each time, and then populate each row. local wikiTable if displayListOverride then wikiTable = startList() else wikiTable = startNewWikiTable("Recipes that require " .. ingredientName .. ".") end -- Add the data retrieved to the wiki table for _, match in pairs(targetRecipeMatches) do local recipe = match[1] for _, workshop in ipairs(match[2]) do wikiTable = addRowToTable(wikiTable, recipe, workshop, displayListOverride) 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, displayListOverride) 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] -- Do not add the extension here local buildingIconFile = workshop[INDEX_WORKSHOP_ID] .. "_icon" 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:newline() -- Simple name and stars if display overriding if displayListOverride then wikiTableRow:tag("td") :wikitext("[[" .. buildingName .. "]]" .. NBSP) :wikitext("(" .. GRADE_TO_STARS[recipeGrade] .. ")" .. BR) :allDone():newline() return wikiTable end wikiTableRow:tag('td') :wikitext(writeBuildingCellContent(buildingIconFile, buildingName, recipeGrade, recipeProdTime)) :done():newline() -- Skip empty cells of ingredients in 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():newline() 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 -- Skip blank groups; we already inserted blanks earlier. if #optionsGroup > 0 then local innerTable = wikiTableRow:tag('td'):tag('table') innerTable:newline() -- Loop through each option, but only up until they are blank for _, option in ipairs(optionsGroup) do local ingredientStackSize = option[1] local ingredientGoodID = option[2] innerTable:tag('tr') :tag('td'):wikitext(BOLD .. ingredientStackSize .. BOLD):done() :tag('td'):wikitext(writeIngredientCellContent(ingredientGoodID)):done() :done() innerTable:wikitext("\n") end end end local productCell, productName = writeProductCellContent(recipeProductID) -- Sort on product name, not the stack size wikiTableRow:tag('td'):attr("data-sort-value", productName) :wikitext(BOLD .. recipeProductStackSize .. BOLD .. NBSP .. productCell) :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 = RecipeData.getGoodNameAndIcon(ingredientGoodID) local iconMarkup = "[[File:" .. goodIconFile .. WIKI_MED_ICON_MARKUP .. goodName .. "|link=" .. goodName .. "]]" local linkMarkup = "[[" .. goodName .. "]]" return iconMarkup .. NBSP .. 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 -- @return the string name of the product function writeProductCellContent(productGoodID) local goodName, goodIconFile = RecipeData.getGoodNameAndIcon(productGoodID) local iconMarkup = "[[File:" .. goodIconFile .. WIKI_LARGE_ICON_MARKUP .. goodName .. "|link=" .. goodName .. "]]" local linkMarkup = "[[" .. goodName .. "]]" return iconMarkup .. NBSP .. linkMarkup, goodName 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:', including extension -- @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_LARGE_ICON_MARKUP .. buildingName .. "|link=" .. buildingName .. "]]" local linkMarkup = "[[" .. buildingName .. "]]" local grade = GRADE_TO_STARS[recipeGrade] if not grade then error("Incorrectly formatted efficiency grade: " .. recipeGrade) end local innerTable = mw.ustring.format("<table><tr><td>%s</td><td>%s<br />%s<br />%s</td></tr></table>", iconMarkup, linkMarkup, grade, formatTime(recipeProdTime)) return innerTable 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) wikiTable:newline() if caption then wikiTable:tag('caption'):wikitext(caption):done():newline() end wikiTable:tag('tr') :tag('th'):wikitext("Building"):done() :tag('th'):wikitext("Ingredient #1 options"):done() :tag('th'):wikitext("Ingredient #2 options"):done() :tag('th'):wikitext("Ingredient #3 options"):done() :tag('th'):wikitext("Product"):done() :done():newline() return wikiTable end --- -- Start the simple list display -- -- @return the wikitext function startList() local wikiTable = mw.html.create('table') wikiTable:newline() 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