Module:Goodbox: Difference between revisions

From Against the Storm Official Wiki
m (added margin around infobox)
(Refactoring to use the new getters instead of storing the whole record and worrying about indexes.)
Line 1: Line 1:
---
---
-- Module to create infobox for displaying on a good's wiki page.
--- Module to create an infobox for displaying on a good's wiki page.
--
---
-- @module Good
--- 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, for example, with {{PAGENAME}}.
---
--- @module Goodbox
local Goodbox = {}
local Goodbox = {}


---
 
-- Dependencies
 
---
local GoodsData = require("Module:GoodsData")
local GoodsData = require("Module:GoodsData")


---
 
-- Constants
 
---
--region  Private constants
 
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"
local INDEX_GOOD_ID = 1
local INDEX_GOOD_NAME = 2
local INDEX_GOOD_DESCRIPTION = 3
local INDEX_GOOD_CATEGORY = 4
local INDEX_GOOD_EATABLE = 5
local INDEX_GOOD_CAN_BE_BURNED = 6
local INDEX_GOOD_BURNING_TIME = 7
local INDEX_GOOD_TRADING_SELL_VALUE = 8
local INDEX_GOOD_TRADING_BUY_VALUE = 9
local INDEX_GOOD_ICON_FILENAME = 10


local TITLE_ID = "ID"
local TITLE_ID = "ID"
local TITLE_CATEGORY = "Inventory Category"
local TITLE_CATEGORY = "Inventory Category"
local TITLE_TYPE = "ID Type"
local TITLE_EATABLE = "Eatable"
local TITLE_EATABLE = "Is eatable?"
local TITLE_BURNABLE = "Burnable"
local TITLE_BURNABLE = "Is burnable?"
local TITLE_BURN_TIME = "Fuel burn time"
local TITLE_BURN_TIME = "Fuel burn time"
local TITLE_SELL_VALUE = "Value when sold"
local TITLE_SELL_VALUE = "Value when sold"
Line 42: Line 36:
local PATTERN_FORMAT_TWO_DECIMALS = "%1.2f"
local PATTERN_FORMAT_TWO_DECIMALS = "%1.2f"


local BR = "<br />"
local NBSP = "&nbsp;"
local NBSP = "&nbsp;"


local WIKI_MED_ICON_MARKUP = "|32px|alt="
local WIKIMARKUP_MED_ICON = "|32px|alt="


local categoryIcons = {
local ICON_FILENAMES_CATEGORIES = {
["Building Materials"] = "Icon_UI_CategoryBuilding.png",
["Building Materials"] = "Icon_UI_CategoryBuilding.png",
["Consumable Items"] = "Icon_UI_CategoryConsumable.png",
["Consumable Items"] = "Icon_UI_CategoryConsumable.png",
["Crafting Resources"] = "Icon_UI_CategoryCrafting.png",
["Crafting Resources"] = "Icon_UI_CategoryCrafting.png",
["Food"] = "Icon UI CategoryFood.png",
["Food"] = "Icon UI CategoryFood.png",
["Fuel & Exploration"] = "Icon_UI_CategoryFuel.png",
["Fuel & Exploration"] = "Icon_UI_CategoryFuel.png",
["Trade Goods"] = "Icon_UI_CategoryTrade.png"
["Trade Goods"] = "Icon_UI_CategoryTrade.png"
}
}


Line 65: Line 58:
}
}


local CSS_BOTTOM_BORDER_INLINE_STYLE = {
["border-bottom"] = "1px solid #a2a9b1"
}


--endregion


---
-- Member variable: Good
--
--
local good




--region Private methods


---
---
-- Creates an html table to display the data in a floating box.
--- Builds using the provided wikiInfobox a subtable to lay out headers and
--
--- fields.
-- @param frame the template's calling context
---
-- @return wiki markup for the box
---@param wikiInfobox table mw.html object into which we're building this
function Goodbox.infobox(frame)
---@param goodName string the name of the good the infobox is about
---@param purpose string the parameter provided to the template
-- Every good must have a name.
---@param speciesPref string the parameter provided to the template
local name = frame.args[ARG_GOOD_NAME]
function makeInnerTable(wikiInfobox, goodName, purpose, speciesPref)
 
if not name or name == "" then
-- Grab the data we'll use to populate this.
error("You must specify the name of the good in the Goodbox template.")
local goodID = GoodsData.getGoodID(goodName)
end
local goodCategory = GoodsData.getGoodCategory(goodName)
local goodIsEatable = GoodsData.isGoodEatable(goodName)
-- Every Good should have a purpose.
local goodIsBurnable = GoodsData.isGoodBurnable(goodName)
local purpose = frame.args[ARG_GOOD_PURPOSE]
local goodBurnTime = GoodsData.getGoodBurnTime(goodName)
-- Specific parameters, may or may not be present.
local goodSellValue, goodBuyValue = GoodsData.getGoodValue(goodName)
local speciesPref = frame.args[ARG_GOOD_SPECIES_PREF]
local categoryIcon
 
-- Retrieve the actual data for the good.
-- Start the inner table
good = GoodsData.getAllDataForGood(name)
if not good then
return "No good found for infobox."
end
local goodID = good[INDEX_GOOD_ID]
local goodName = good[INDEX_GOOD_NAME]
local goodDescription = good[INDEX_GOOD_DESCRIPTION]
local goodCategory = good[INDEX_GOOD_CATEGORY]
local goodIsEatable = good[INDEX_GOOD_EATABLE]
local goodIsBurnable = good[INDEX_GOOD_CAN_BE_BURNED]
local goodBurnTime = good[INDEX_GOOD_BURNING_TIME]
local goodSellValue = good[INDEX_GOOD_TRADING_SELL_VALUE]
local goodBuyValue = good[INDEX_GOOD_TRADING_BUY_VALUE]
local goodIconFilename = good[INDEX_GOOD_ICON_FILENAME]
local goodType = extractType(goodID)
-- Make the top of the infobox that every item has.
wikiInfobox = mw.html.create("table"):css( CSS_INFOBOX_INLINE_STYLE_TABLE )
-- Title of infobox.
if name then
wikiInfobox:tag("tr"):tag("td"):wikitext(name):done():done():newline()
end
-- Subtitle for category.
if goodCategory then
wikiInfobox:tag("tr"):tag("td"):wikitext(goodCategory):done():done():newline()
end
-- Large icon of infobox.
if goodIconFilename then
wikiInfobox:tag("tr"):tag("td"):wikitext("[[File:" .. goodIconFilename .. ".png]]"):done():done():newline()
wikiInfobox:tag("tr"):tag("td"):wikitext(name .. " icon as seen in-game"):done():done():newline()
end
-- Data fields of the infobox with labels and values
local innerTable = wikiInfobox:tag("tr"):tag("td"):tag("table")
local innerTable = wikiInfobox:tag("tr"):tag("td"):tag("table")
innerTable:newline()
innerTable:newline()
 
-- Start with the things not provided by the data.
if purpose and purpose ~= "" then
if purpose and purpose ~= "" then
innerTable:tag("tr"):tag("th"):wikitext(toTitleCase(ARG_GOOD_PURPOSE)):done()
innerTable:tag("tr")
:tag("td"):wikitext(purpose):done():done():newline()
:tag("th"):wikitext(toTitleCase(ARG_GOOD_PURPOSE)):done()
:tag("td"):wikitext(purpose):done()
:done():newline()
end
end
if speciesPref and speciesPref ~= "" then
if speciesPref and speciesPref ~= "" then
innerTable:tag("tr"):tag("th"):wikitext(toTitleCase(ARG_GOOD_SPECIES_PREF)):done()
innerTable:tag("tr"):css(CSS_BOTTOM_BORDER_INLINE_STYLE)
:tag("td"):wikitext(speciesPref):done():done():newline()
:tag("th"):wikitext(toTitleCase(ARG_GOOD_SPECIES_PREF)):done()
:tag("td"):wikitext(speciesPref):done()
:done():newline()
end
end
 
-- Data provided by the CSV
-- Inventory category (this is the same as the subtitle for now)
if goodCategory then
if goodCategory then
local catIcon = "[[File:" .. categoryIcons[goodCategory] .. WIKI_MED_ICON_MARKUP .. goodCategory .. "]]"
categoryIcon = "[[File:" .. ICON_FILENAMES_CATEGORIES[goodCategory] .. WIKIMARKUP_MED_ICON .. goodCategory .. "]]"
innerTable:tag("tr"):tag("th"):wikitext(TITLE_CATEGORY):done()
innerTable:tag("tr"):css(CSS_BOTTOM_BORDER_INLINE_STYLE)
:tag("td"):wikitext(catIcon .. NBSP .. goodCategory):done():done():newline()
:tag("th"):wikitext(TITLE_CATEGORY):done()
:tag("td"):wikitext(categoryIcon .. NBSP .. goodCategory):done()
:done():newline()
end
end
-- Eatable is superfluous when the category is food.
-- Uses
--if goodIsEatable then
if goodIsEatable then
-- innerTable:tag("tr"):tag("th"):wikitext(TITLE_EATABLE):done()
innerTable:tag("tr")
-- :tag("td"):wikitext(goodIsEatable):done():done():newline()
:tag("th"):wikitext(TITLE_EATABLE):done()
--end
:tag("td"):wikitext(goodIsEatable and "Yes" or "No"):done()
-- Burnable is superfluous but we can use it to determine whether to show
:done():newline()
-- how long the fuel burns.
-- if goodIsBurnable then
-- innerTable:tag("tr"):tag("th"):wikitext(TITLE_BURNABLE):done()
-- :tag("td"):wikitext(goodIsBurnable):done():done():newline()
-- end
if goodIsBurnable == "True" and goodBurnTime then
innerTable:tag("tr"):tag("th"):wikitext(TITLE_BURN_TIME):done()
:tag("td"):wikitext(goodBurnTime):done():done():newline()
end
end
if goodIsBurnable 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_BOTTOM_BORDER_INLINE_STYLE)
:tag("th"):wikitext(TITLE_BURN_TIME):done()
:tag("td"):wikitext(goodBurnTime):done()
:done():newline()
end
-- Trade values.
if goodSellValue then
if goodSellValue then
innerTable:tag("tr"):tag("th"):wikitext(TITLE_SELL_VALUE):done()
innerTable:tag("tr")
:tag("td"):wikitext(toDecimal(goodSellValue)):done():done():newline()
:tag("th"):wikitext(TITLE_SELL_VALUE):done()
:tag("td"):wikitext(toDecimal(goodSellValue)):done()
:done():newline()
end
end
if goodBuyValue then
if goodBuyValue then
innerTable:tag("tr"):tag("th"):wikitext(TITLE_BUY_VALUE):done()
innerTable:tag("tr"):css(CSS_BOTTOM_BORDER_INLINE_STYLE)
:tag("td"):wikitext(toDecimal(goodBuyValue)):done():done():newline()
:tag("th"):wikitext(TITLE_BUY_VALUE):done()
:tag("td"):wikitext(toDecimal(goodBuyValue)):done()
:done():newline()
end
end
-- End with the ID.
if goodID then
if goodID then
innerTable:tag("tr"):tag("th"):wikitext(TITLE_ID):done()
innerTable:tag("tr")
:tag("td"):wikitext("\"" .. goodID .. "\""):done():done():newline()
:tag("th"):wikitext(TITLE_ID):done()
:tag("td"):wikitext("\'" .. goodID .. "\'"):done()
:done():newline()
end
end
 
-- Close the table.
innerTable:done()
innerTable:done()
-- Close the table cell and row the inner table is in.
wikiInfobox:done():done():newline()
wikiInfobox:done():done():newline()
if goodDescription then
wikiInfobox:tag("tr"):tag("td"):wikitext(goodDescription):done():done():newline()
end
return wikiInfobox
end
end


--endregion




--region Public methods
---
--- Creates an html html and wiki markup to display the data in an infobox.
---
---
-- Extracts the beginning of the ID into the type of good. For example, when
--- @param frame table from template's calling context
-- ID = [Vessel] Pottery, the type is Vessel.
--- @return string of html and wiki markup
--
function Goodbox.infobox(frame)
-- @param goodID the ID of the good from the data
-- @return the type of good
function extractType(goodID)
local typecode = goodID:match(PATTERN_CAPTURE_BEGINNING_WITHIN_BRACKETS)
-- 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
if not typecode then
-- Additional template parameters, may or may not be present
typecode = goodID:match(PATTERN_CAPTURE_BEGINNING_AFTER_UNDERSCORE)
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.
wikiInfobox = mw.html.create("table"):css(CSS_INFOBOX_INLINE_STYLE_TABLE)
-- with a title
wikiInfobox:tag("tr")
:tag("td"):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_BOTTOM_BORDER_INLINE_STYLE)
:tag("td"):wikitext(goodName .. ", as seen in-game"):done()
:done():newline()
end
end
 
if not typecode then
makeInnerTable(wikiInfobox, goodName, purpose, speciesPref)
typecode = goodID:match(PATTERN_CAPTURE_FIRST_WORD)
 
-- Finish with the flavor text.
if goodDescription then
wikiInfobox:tag("tr")
:tag("td"):wikitext(goodDescription):done()
:done():newline()
end
end
 
return typecode
wikiInfobox.allDone()
 
return wikiInfobox
end
end



Revision as of 05:38, 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, for example, with {{PAGENAME}}.
---
--- @module Goodbox
local Goodbox = {}



local GoodsData = require("Module:GoodsData")



--region  Private constants

local ARG_GOOD_NAME = "name"
local ARG_GOOD_PURPOSE = "purpose"
local ARG_GOOD_SPECIES_PREF = "species_preference"

local TITLE_ID = "ID"
local TITLE_CATEGORY = "Inventory 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_CAPTURE_BEGINNING_WITHIN_BRACKETS = "^%[(.-)%]"
local PATTERN_CAPTURE_BEGINNING_AFTER_UNDERSCORE = "^_(.-)%s"
local PATTERN_CAPTURE_FIRST_WORD = "^(%w-)%s"
local PATTERN_FORMAT_TWO_DECIMALS = "%1.2f"

local NBSP = "&nbsp;"

local WIKIMARKUP_MED_ICON = "|32px|alt="

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"
}

local CSS_INFOBOX_INLINE_STYLE_TABLE = { 
	["float"] = "right", 
	["clear"] = "right", 
	["width"] = "256px", 
	["border"] = "1px solid #a2a9b1", 
	["border-spacing"] = "2px",
	["margin"] = "0.5em 0 0.5em 1em"
}

local CSS_BOTTOM_BORDER_INLINE_STYLE = {
	["border-bottom"] = "1px solid #a2a9b1"
}

--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)
	local categoryIcon

	-- Start the inner table
	local innerTable = wikiInfobox:tag("tr"):tag("td"):tag("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_BOTTOM_BORDER_INLINE_STYLE)
				:tag("th"):wikitext(toTitleCase(ARG_GOOD_SPECIES_PREF)):done()
				:tag("td"):wikitext(speciesPref):done()
			:done():newline()
	end

	-- Inventory category (this is the same as the subtitle for now)
	if goodCategory then
		categoryIcon = "[[File:" .. ICON_FILENAMES_CATEGORIES[goodCategory] .. WIKIMARKUP_MED_ICON .. goodCategory .. "]]"
		innerTable:tag("tr"):css(CSS_BOTTOM_BORDER_INLINE_STYLE)
				:tag("th"):wikitext(TITLE_CATEGORY):done()
				:tag("td"):wikitext(categoryIcon .. NBSP .. goodCategory):done()
			:done():newline()
	end
	-- Uses
	if goodIsEatable then
		innerTable:tag("tr")
				:tag("th"):wikitext(TITLE_EATABLE):done()
				:tag("td"):wikitext(goodIsEatable and "Yes" or "No"):done()
			:done():newline()
	end
	if goodIsBurnable 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_BOTTOM_BORDER_INLINE_STYLE)
				:tag("th"):wikitext(TITLE_BURN_TIME):done()
				:tag("td"):wikitext(goodBurnTime):done()
			:done():newline()
	end
	-- Trade values.
	if goodSellValue then
		innerTable:tag("tr")
				:tag("th"):wikitext(TITLE_SELL_VALUE):done()
				:tag("td"):wikitext(toDecimal(goodSellValue)):done()
			:done():newline()
	end
	if goodBuyValue then
		innerTable:tag("tr"):css(CSS_BOTTOM_BORDER_INLINE_STYLE)
				: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")
				:tag("th"):wikitext(TITLE_ID):done()
				:tag("td"):wikitext("\'" .. goodID .. "\'"):done()
			:done():newline()
	end

	-- Close the table.
	innerTable:done()
	-- Close the table cell and row the inner table is in.
	wikiInfobox:done():done():newline()
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
	
	-- 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.
	wikiInfobox = mw.html.create("table"):css(CSS_INFOBOX_INLINE_STYLE_TABLE)
	-- with a title
	wikiInfobox:tag("tr")
			:tag("td"):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_BOTTOM_BORDER_INLINE_STYLE)
				: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")
				:tag("td"):wikitext(goodDescription):done()
			:done():newline()
	end

	wikiInfobox.allDone()

	return wikiInfobox
end



function toDecimal(value)
	
	return string.format(PATTERN_FORMAT_TWO_DECIMALS, value)
end



---
-- Capitalizes the first character of each word.
--
-- @param title the title to capitalize
-- @return the title once capitalized
function toTitleCase(title)
	
	local newTitle = title:gsub("(%a)([%w_']*)", function(first, rest)
			return first:upper() .. rest:lower()
		end)
	
	newTitle, _ = newTitle:gsub("_", " ")
	
	return newTitle
end



return Goodbox