Module:Goodbox: Difference between revisions
From Against the Storm Official Wiki
m (But it helps if you call the right method) |
(Refactored to use the getter methods. Improved documentation; reordered methods; made private method for the inner table for better grouping) |
||
Line 6: | Line 6: | ||
--- | --- | ||
--- This should be invoked from Template:Goodbox with the parameter of the | --- This should be invoked from Template:Goodbox with the parameter of the | ||
--- good whose infobox should be shown | --- good whose infobox should be shown. See the template documentation for | ||
--- more information about parameters and errors and to see examples. | |||
--- | --- | ||
--- @module Goodbox | --- @module Goodbox | ||
Line 18: | Line 19: | ||
--region Private constants | --region Private constants | ||
-- Template parameters | |||
local ARG_GOOD_NAME = "name" | local ARG_GOOD_NAME = "name" | ||
local ARG_GOOD_PURPOSE = "purpose" | local ARG_GOOD_PURPOSE = "purpose" | ||
local ARG_GOOD_SPECIES_PREF = "species_preference" | local ARG_GOOD_SPECIES_PREF = "species_preference" | ||
-- Infobox header labels | |||
local TITLE_ID = "ID" | local TITLE_ID = "ID" | ||
local TITLE_CATEGORY = " | local TITLE_CATEGORY = "Storage Category" | ||
local TITLE_EATABLE = "Eatable" | local TITLE_EATABLE = "Eatable" | ||
local TITLE_BURNABLE = "Burnable" | local TITLE_BURNABLE = "Burnable" | ||
Line 31: | Line 32: | ||
local TITLE_BUY_VALUE = "Price when bought" | local TITLE_BUY_VALUE = "Price when bought" | ||
local PATTERN_FORMAT_TWO_DECIMALS = "%1.2f" | local PATTERN_FORMAT_TWO_DECIMALS = "%1.2f" | ||
local PATTERN_CAPTURE_FIRST_LETTER_IN_WORD = "(%a)([%w]*)" | |||
local NBSP = " " | local NBSP = " " | ||
local WIKIMARKUP_MED_ICON = "|32px|alt=" | local WIKIMARKUP_MED_ICON = "|32px|alt=" | ||
local | local CSS_INLINE_INFOBOX = { | ||
["float"] = "right", | ["float"] = "right", | ||
["clear"] = "right", | ["clear"] = "right", | ||
Line 55: | Line 44: | ||
["border"] = "1px solid #a2a9b1", | ["border"] = "1px solid #a2a9b1", | ||
["border-spacing"] = "2px", | ["border-spacing"] = "2px", | ||
["border-collapse"] = "collapse", | |||
["margin"] = "0.5em 0 0.5em 1em" | ["margin"] = "0.5em 0 0.5em 1em" | ||
} | } | ||
local CSS_INLINE_INFOBOX_INNER_TABLE = { | |||
local | ["border-collapse"] = "collapse" | ||
} | |||
local CSS_INLINE_INFOBOX_TR_BOTTOM_DIVIDER = { | |||
["border-bottom"] = "1px solid #a2a9b1" | ["border-bottom"] = "1px solid #a2a9b1" | ||
} | |||
local CSS_INLINE_INFOBOX_TR_TOP_DIVIDER = { | |||
["border-top"] = "1px solid #a2a9b1" | |||
} | |||
-- Lookup table for storage category icons. | |||
local ICON_FILENAMES_CATEGORIES = { | |||
["Building Materials"] = "Icon_UI_CategoryBuilding.png", | |||
["Consumable Items"] = "Icon_UI_CategoryConsumable.png", | |||
["Crafting Resources"] = "Icon_UI_CategoryCrafting.png", | |||
["Food"] = "Icon UI CategoryFood.png", | |||
["Fuel & Exploration"] = "Icon_UI_CategoryFuel.png", | |||
["Trade Goods"] = "Icon_UI_CategoryTrade.png" | |||
} | } | ||
Line 85: | Line 89: | ||
local goodBurnTime = GoodsData.getGoodBurnTime(goodName) | local goodBurnTime = GoodsData.getGoodBurnTime(goodName) | ||
local goodSellValue, goodBuyValue = GoodsData.getGoodValue(goodName) | local goodSellValue, goodBuyValue = GoodsData.getGoodValue(goodName) | ||
-- Start the inner table | -- Start the inner table | ||
local innerTable = wikiInfobox:tag("tr"):tag("td"):tag("table") | local innerTable = wikiInfobox:tag("tr"):tag("td"):newline():tag("table"):css(CSS_INLINE_INFOBOX_INNER_TABLE) | ||
innerTable:newline() | innerTable:newline() | ||
Line 99: | Line 102: | ||
end | end | ||
if speciesPref and speciesPref ~= "" then | if speciesPref and speciesPref ~= "" then | ||
innerTable:tag("tr"):css( | innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_BOTTOM_DIVIDER) | ||
:tag("th"):wikitext(toTitleCase(ARG_GOOD_SPECIES_PREF)):done() | :tag("th"):wikitext(toTitleCase(ARG_GOOD_SPECIES_PREF)):done() | ||
:tag("td"):wikitext(speciesPref):done() | :tag("td"):wikitext(speciesPref):done() | ||
Line 105: | Line 108: | ||
end | end | ||
-- | -- Storage category (this is the same as the subtitle for now) | ||
if goodCategory then | if goodCategory then | ||
categoryIcon = "[[File:" .. ICON_FILENAMES_CATEGORIES[goodCategory] .. WIKIMARKUP_MED_ICON .. goodCategory .. "]]" | local categoryIcon = "[[File:" .. ICON_FILENAMES_CATEGORIES[goodCategory] .. WIKIMARKUP_MED_ICON .. goodCategory .. "]]" | ||
innerTable:tag("tr"):css( | innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_TOP_DIVIDER) | ||
:tag("th"):wikitext(TITLE_CATEGORY):done() | :tag("th"):wikitext(TITLE_CATEGORY):done() | ||
:tag("td"):wikitext(categoryIcon .. NBSP .. goodCategory):done() | :tag("td"):wikitext(categoryIcon .. NBSP .. goodCategory):done() | ||
:done():newline() | :done():newline() | ||
end | end | ||
-- | -- Since false is a valid value, have to directly check if it's nil. | ||
if goodIsEatable then | if goodIsEatable ~= nil then | ||
innerTable:tag("tr") | innerTable:tag("tr") | ||
:tag("th"):wikitext(TITLE_EATABLE):done() | :tag("th"):wikitext(TITLE_EATABLE):done() | ||
Line 120: | Line 123: | ||
:done():newline() | :done():newline() | ||
end | end | ||
if goodIsBurnable then | if goodIsBurnable ~= nil then | ||
innerTable:tag("tr") | innerTable:tag("tr") | ||
:tag("th"):wikitext(TITLE_BURNABLE):done() | :tag("th"):wikitext(TITLE_BURNABLE):done() | ||
Line 127: | Line 130: | ||
end | end | ||
if goodIsBurnable and goodBurnTime then | if goodIsBurnable and goodBurnTime then | ||
innerTable:tag("tr"):css( | innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_BOTTOM_DIVIDER) | ||
:tag("th"):wikitext(TITLE_BURN_TIME):done() | :tag("th"):wikitext(TITLE_BURN_TIME):done() | ||
:tag("td"):wikitext(goodBurnTime):done() | :tag("td"):wikitext(goodBurnTime):done() | ||
Line 134: | Line 137: | ||
-- Trade values. | -- Trade values. | ||
if goodSellValue then | if goodSellValue then | ||
innerTable:tag("tr") | innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_TOP_DIVIDER) | ||
:tag("th"):wikitext(TITLE_SELL_VALUE):done() | :tag("th"):wikitext(TITLE_SELL_VALUE):done() | ||
:tag("td"):wikitext(toDecimal(goodSellValue)):done() | :tag("td"):wikitext(toDecimal(goodSellValue)):done() | ||
Line 140: | Line 143: | ||
end | end | ||
if goodBuyValue then | if goodBuyValue then | ||
innerTable:tag("tr"):css( | innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_BOTTOM_DIVIDER) | ||
:tag("th"):wikitext(TITLE_BUY_VALUE):done() | :tag("th"):wikitext(TITLE_BUY_VALUE):done() | ||
:tag("td"):wikitext(toDecimal(goodBuyValue)):done() | :tag("td"):wikitext(toDecimal(goodBuyValue)):done() | ||
Line 147: | Line 150: | ||
-- End with the ID. | -- End with the ID. | ||
if goodID then | if goodID then | ||
innerTable:tag("tr") | innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_TOP_DIVIDER) | ||
:tag("th"):wikitext(TITLE_ID):done() | :tag("th"):wikitext(TITLE_ID):done() | ||
:tag("td"):wikitext("\'" .. goodID .. "\'"):done() | :tag("td"):wikitext("\'" .. goodID .. "\'"):done() | ||
Line 154: | Line 157: | ||
-- Close the table. | -- Close the table. | ||
innerTable:done() | innerTable:done():newline() | ||
-- Close the table cell and row the inner table is in. | -- Close the table cell and row the inner table is in. | ||
wikiInfobox:done():done():newline() | wikiInfobox:done():done():newline() | ||
end | |||
--- | |||
--- Shortcut string to reformat numbers with two decimals. | |||
--- | |||
---@param value number whole or with a fractional part | |||
---@return string reformatted to force two decimal places | |||
function toDecimal(value) | |||
return string.format(PATTERN_FORMAT_TWO_DECIMALS, value) | |||
end | |||
--- | |||
--- Capitalizes the first character of each word to "Make It Title Case." Also | |||
--- converts any underscores to spaces. | |||
--- | |||
--- @param title string to capitalize | |||
--- @return string capitalized with title case | |||
function toTitleCase(title) | |||
title = title:gsub("_", " ") | |||
local newTitle = title:gsub(PATTERN_CAPTURE_FIRST_LETTER_IN_WORD, function(firstLetter, rest) | |||
return firstLetter:upper() .. rest:lower() | |||
end) | |||
return newTitle | |||
end | end | ||
Line 192: | Line 225: | ||
local goodIconFilename = GoodsData.getGoodIcon(goodName) | local goodIconFilename = GoodsData.getGoodIcon(goodName) | ||
-- Make the top of the infobox that every item has. | -- Make the top of the infobox that every item has... | ||
wikiInfobox = mw.html.create("table"):css( | local wikiInfobox = mw.html.create("table"):css(CSS_INLINE_INFOBOX):newline() | ||
-- with a title | -- with a title... | ||
wikiInfobox:tag("tr") | wikiInfobox:tag("tr") | ||
:tag(" | :tag("th"):wikitext(goodName):done() | ||
:done():newline() | :done():newline() | ||
-- and a subtitle, showing the category | -- and a subtitle, showing the category... | ||
if goodCategory then | if goodCategory then | ||
wikiInfobox:tag("tr") | wikiInfobox:tag("tr") | ||
Line 209: | Line 242: | ||
:tag("td"):wikitext("[[File:" .. goodIconFilename .. "]]"):done() | :tag("td"):wikitext("[[File:" .. goodIconFilename .. "]]"):done() | ||
:done():newline() | :done():newline() | ||
wikiInfobox:tag("tr"):css( | wikiInfobox:tag("tr"):css(CSS_INLINE_INFOBOX_TR_BOTTOM_DIVIDER) | ||
:tag("td"):wikitext(goodName .. ", as seen in-game"):done() | :tag("td"):wikitext(goodName .. ", as seen in-game"):done() | ||
:done():newline() | :done():newline() | ||
Line 218: | Line 251: | ||
-- Finish with the flavor text. | -- Finish with the flavor text. | ||
if goodDescription then | if goodDescription then | ||
wikiInfobox:tag("tr") | wikiInfobox:tag("tr"):css(CSS_INLINE_INFOBOX_TR_TOP_DIVIDER) | ||
:tag("td"):wikitext(goodDescription):done() | :tag("td"):wikitext(goodDescription):done() | ||
:done():newline() | :done():newline() | ||
Line 226: | Line 259: | ||
end | end | ||
--endregion | |||
-- | |||
return Goodbox | return Goodbox |
Revision as of 16:24, 18 November 2023
Documentation for this module may be created at Module:Goodbox/doc
--- --- Module to create an infobox for displaying on a good's wiki page. --- --- Shows the facts from the data about the specified good in an easy-to-read --- table, with a large icon, information, categories, and the flavor text. --- --- This should be invoked from Template:Goodbox with the parameter of the --- good whose infobox should be shown. See the template documentation for --- more information about parameters and errors and to see examples. --- --- @module Goodbox local Goodbox = {} local GoodsData = require("Module:GoodsData") --region Private constants -- Template parameters local ARG_GOOD_NAME = "name" local ARG_GOOD_PURPOSE = "purpose" local ARG_GOOD_SPECIES_PREF = "species_preference" -- Infobox header labels local TITLE_ID = "ID" local TITLE_CATEGORY = "Storage Category" local TITLE_EATABLE = "Eatable" local TITLE_BURNABLE = "Burnable" local TITLE_BURN_TIME = "Fuel burn time" local TITLE_SELL_VALUE = "Value when sold" local TITLE_BUY_VALUE = "Price when bought" local PATTERN_FORMAT_TWO_DECIMALS = "%1.2f" local PATTERN_CAPTURE_FIRST_LETTER_IN_WORD = "(%a)([%w]*)" local NBSP = " " local WIKIMARKUP_MED_ICON = "|32px|alt=" local CSS_INLINE_INFOBOX = { ["float"] = "right", ["clear"] = "right", ["width"] = "256px", ["border"] = "1px solid #a2a9b1", ["border-spacing"] = "2px", ["border-collapse"] = "collapse", ["margin"] = "0.5em 0 0.5em 1em" } local CSS_INLINE_INFOBOX_INNER_TABLE = { ["border-collapse"] = "collapse" } local CSS_INLINE_INFOBOX_TR_BOTTOM_DIVIDER = { ["border-bottom"] = "1px solid #a2a9b1" } local CSS_INLINE_INFOBOX_TR_TOP_DIVIDER = { ["border-top"] = "1px solid #a2a9b1" } -- Lookup table for storage category icons. local ICON_FILENAMES_CATEGORIES = { ["Building Materials"] = "Icon_UI_CategoryBuilding.png", ["Consumable Items"] = "Icon_UI_CategoryConsumable.png", ["Crafting Resources"] = "Icon_UI_CategoryCrafting.png", ["Food"] = "Icon UI CategoryFood.png", ["Fuel & Exploration"] = "Icon_UI_CategoryFuel.png", ["Trade Goods"] = "Icon_UI_CategoryTrade.png" } --endregion --region Private methods --- --- Builds using the provided wikiInfobox a subtable to lay out headers and --- fields. --- ---@param wikiInfobox table mw.html object into which we're building this ---@param goodName string the name of the good the infobox is about ---@param purpose string the parameter provided to the template ---@param speciesPref string the parameter provided to the template function makeInnerTable(wikiInfobox, goodName, purpose, speciesPref) -- Grab the data we'll use to populate this. local goodID = GoodsData.getGoodID(goodName) local goodCategory = GoodsData.getGoodCategory(goodName) local goodIsEatable = GoodsData.isGoodEatable(goodName) local goodIsBurnable = GoodsData.isGoodBurnable(goodName) local goodBurnTime = GoodsData.getGoodBurnTime(goodName) local goodSellValue, goodBuyValue = GoodsData.getGoodValue(goodName) -- Start the inner table local innerTable = wikiInfobox:tag("tr"):tag("td"):newline():tag("table"):css(CSS_INLINE_INFOBOX_INNER_TABLE) innerTable:newline() -- Start with the things not provided by the data. if purpose and purpose ~= "" then innerTable:tag("tr") :tag("th"):wikitext(toTitleCase(ARG_GOOD_PURPOSE)):done() :tag("td"):wikitext(purpose):done() :done():newline() end if speciesPref and speciesPref ~= "" then innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_BOTTOM_DIVIDER) :tag("th"):wikitext(toTitleCase(ARG_GOOD_SPECIES_PREF)):done() :tag("td"):wikitext(speciesPref):done() :done():newline() end -- Storage category (this is the same as the subtitle for now) if goodCategory then local categoryIcon = "[[File:" .. ICON_FILENAMES_CATEGORIES[goodCategory] .. WIKIMARKUP_MED_ICON .. goodCategory .. "]]" innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_TOP_DIVIDER) :tag("th"):wikitext(TITLE_CATEGORY):done() :tag("td"):wikitext(categoryIcon .. NBSP .. goodCategory):done() :done():newline() end -- Since false is a valid value, have to directly check if it's nil. if goodIsEatable ~= nil then innerTable:tag("tr") :tag("th"):wikitext(TITLE_EATABLE):done() :tag("td"):wikitext(goodIsEatable and "Yes" or "No"):done() :done():newline() end if goodIsBurnable ~= nil then innerTable:tag("tr") :tag("th"):wikitext(TITLE_BURNABLE):done() :tag("td"):wikitext(goodIsBurnable and "Yes" or "No"):done() :done():newline() end if goodIsBurnable and goodBurnTime then innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_BOTTOM_DIVIDER) :tag("th"):wikitext(TITLE_BURN_TIME):done() :tag("td"):wikitext(goodBurnTime):done() :done():newline() end -- Trade values. if goodSellValue then innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_TOP_DIVIDER) :tag("th"):wikitext(TITLE_SELL_VALUE):done() :tag("td"):wikitext(toDecimal(goodSellValue)):done() :done():newline() end if goodBuyValue then innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_BOTTOM_DIVIDER) :tag("th"):wikitext(TITLE_BUY_VALUE):done() :tag("td"):wikitext(toDecimal(goodBuyValue)):done() :done():newline() end -- End with the ID. if goodID then innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_TOP_DIVIDER) :tag("th"):wikitext(TITLE_ID):done() :tag("td"):wikitext("\'" .. goodID .. "\'"):done() :done():newline() end -- Close the table. innerTable:done():newline() -- Close the table cell and row the inner table is in. wikiInfobox:done():done():newline() end --- --- Shortcut string to reformat numbers with two decimals. --- ---@param value number whole or with a fractional part ---@return string reformatted to force two decimal places function toDecimal(value) return string.format(PATTERN_FORMAT_TWO_DECIMALS, value) end --- --- Capitalizes the first character of each word to "Make It Title Case." Also --- converts any underscores to spaces. --- --- @param title string to capitalize --- @return string capitalized with title case function toTitleCase(title) title = title:gsub("_", " ") local newTitle = title:gsub(PATTERN_CAPTURE_FIRST_LETTER_IN_WORD, function(firstLetter, rest) return firstLetter:upper() .. rest:lower() end) return newTitle end --endregion --region Public methods --- --- Creates an html html and wiki markup to display the data in an infobox. --- --- @param frame table from template's calling context --- @return string of html and wiki markup function Goodbox.infobox(frame) -- Every good must have a name. local goodName = frame.args[ARG_GOOD_NAME] if not goodName or goodName == "" then return "You must specify the name of the good. See [[Template:Goodbox]] for examples." end -- Use this method here to check to see whether the provided name is valid. if not GoodsData.getGoodID(goodName) then return "Infobox can't find the specified good: " .. goodName .. "." end -- Additional template parameters, may or may not be present local purpose = frame.args[ARG_GOOD_PURPOSE] local speciesPref = frame.args[ARG_GOOD_SPECIES_PREF] -- Get the data. local goodDescription = GoodsData.getGoodDescription(goodName) local goodCategory = GoodsData.getGoodCategory(goodName) local goodIconFilename = GoodsData.getGoodIcon(goodName) -- Make the top of the infobox that every item has... local wikiInfobox = mw.html.create("table"):css(CSS_INLINE_INFOBOX):newline() -- with a title... wikiInfobox:tag("tr") :tag("th"):wikitext(goodName):done() :done():newline() -- and a subtitle, showing the category... if goodCategory then wikiInfobox:tag("tr") :tag("td"):wikitext(goodCategory):done() :done():newline() end -- and a large icon. if goodIconFilename then wikiInfobox:tag("tr") :tag("td"):wikitext("[[File:" .. goodIconFilename .. "]]"):done() :done():newline() wikiInfobox:tag("tr"):css(CSS_INLINE_INFOBOX_TR_BOTTOM_DIVIDER) :tag("td"):wikitext(goodName .. ", as seen in-game"):done() :done():newline() end makeInnerTable(wikiInfobox, goodName, purpose, speciesPref) -- Finish with the flavor text. if goodDescription then wikiInfobox:tag("tr"):css(CSS_INLINE_INFOBOX_TR_TOP_DIVIDER) :tag("td"):wikitext(goodDescription):done() :done():newline() end return wikiInfobox end --endregion return Goodbox