Module:Resource: Difference between revisions
From Against the Storm Official Wiki
moved a public function into private because it wasn't the right public interface |
m stack now skips blank-string arguments safely |
||
| (11 intermediate revisions by the same user not shown) | |||
| Line 5: | Line 5: | ||
---@class Resource | ---@class Resource | ||
---@field _id ResourceID Unique ID code of the resource. | ---@field _id ResourceID Unique ID code of the resource. | ||
---@field _displayName | ---@field _displayName ResourceName The display name in-game. | ||
---@field _description string The in-game descriptive text, including sprite icons, newlines, and escape characters. | ---@field _description string The in-game descriptive text, including sprite icons, newlines, and escape characters. | ||
---@field _iconFilename Filename The file name of the icon for this resource. | ---@field _iconFilename Filename The file name of the icon for this resource. | ||
| Line 17: | Line 17: | ||
-- The ID code of a good or resource. | -- The ID code of a good or resource. | ||
---@alias ResourceID string | ---@alias ResourceID string | ||
-- The display name of a resource. | |||
---@alias ResourceName string | |||
-- Currency in-game. | -- Currency in-game. | ||
| Line 22: | Line 25: | ||
-- The ID and amount of a good or service. | -- The ID and amount of a good or service. | ||
---@alias ResourcePair {_id: ResourceID, _amount: | ---@alias ResourcePair {_id: ResourceID, _amount: Amount} | ||
-- The resource category based on the info bar.<br> | -- The resource category based on the info bar.<br> | ||
| Line 65: | Line 68: | ||
--#region Private Members | --#region Private Members | ||
---@type Resource | ---@type table<ResourceID, Resource> | ||
local resourceData | local resourceData | ||
---@type table< | ---@type table<ResourceName, ResourceID> | ||
local mapNamesToIDs | local mapNamesToIDs | ||
| Line 74: | Line 77: | ||
mapNamesToIDs = {} | mapNamesToIDs = {} | ||
resourceData = mw.loadData(RESOURCES_DATA_FILE) | resourceData = mw.loadData(RESOURCES_DATA_FILE) | ||
for id, | for id, resource in pairs(resourceData) do | ||
mapNamesToIDs[ | mapNamesToIDs[resource._displayName] = id | ||
end | end | ||
end | end | ||
| Line 82: | Line 85: | ||
-- Finds a resource by its display name. | -- Finds a resource by its display name. | ||
---@param resourceName | ---@param resourceName ResourceName display name | ||
---@return Resource|nil foundResource or nil if not found | ---@return Resource|nil foundResource or nil if not found | ||
local function findName(resourceName) | local function findName(resourceName) | ||
| Line 101: | Line 104: | ||
---@param needsIcon boolean|nil or nil for default | ---@param needsIcon boolean|nil or nil for default | ||
---@param needsText boolean|nil or nil for default | ---@param needsText boolean|nil or nil for default | ||
---@param targetElementID string|nil #name or #id of the DOM element to link directly to, if any | |||
---@return Wikitext wikitext | ---@return Wikitext wikitext | ||
local function resourceLink(resource, iconSize, needsIcon, needsText) | local function resourceLink(resource, iconSize, needsIcon, needsText, targetElementID) | ||
iconSize = iconSize or standards.small | iconSize = iconSize or standards.small | ||
needsIcon = needsIcon or (needsIcon == nil and true) | needsIcon = needsIcon or (needsIcon == nil and true) | ||
| Line 113: | Line 117: | ||
iconSize = "x" .. iconSize | iconSize = "x" .. iconSize | ||
end | end | ||
wikitext = wikitext .. wrapClasses(icon(resource._iconFilename, iconSize, | local directLink = resource._displayName .. (targetElementID and ("" .. targetElementID) or "") | ||
wikitext = wikitext .. wrapClasses(icon(resource._iconFilename, iconSize, directLink, resource._displayName), "ats-link-resource", sizeN and sizeN < 23 and "ats-flag-small" or nil) | |||
end | end | ||
if (needsIcon and isValidSize and sizeN >= MIN_ICON_SIZE) and needsText then | if (needsIcon and isValidSize and sizeN >= MIN_ICON_SIZE) and needsText then | ||
| Line 119: | Line 124: | ||
end | end | ||
if needsText then | if needsText then | ||
wikitext = wikitext .. link(resource._displayName, resource._displayName) | wikitext = wikitext .. link(resource._displayName, resource._displayName, targetElementID) | ||
end | end | ||
return nowrap(wikitext) | return nowrap(wikitext) | ||
| Line 138: | Line 143: | ||
-- Finds a resource's ID by its display name. | -- Finds a resource's ID by its display name. | ||
---@param displayName | ---@param displayName ResourceName | ||
---@return ResourceID|nil | ---@return ResourceID|nil | ||
function Resource.getID(displayName) | function Resource.getID(displayName) | ||
data() | data() | ||
return mapNamesToIDs[displayName] | return mapNamesToIDs[displayName] | ||
end | |||
-- Gets the specified resource's display name. | |||
---@param id ResourceID | |||
---@return ResourceName | |||
function Resource.getName(id) | |||
return data()[id]._displayName | |||
end | end | ||
-- Renders a table of pairs of resource IDs and amounts. | -- Renders a table of pairs of resource IDs and amounts. | ||
---@param pairsList ResourcePair[] array of pairs of resource IDs and amounts | ---@param pairsList ResourcePair[] array of pairs of resource IDs and amounts | ||
---@vararg string additional classes to add to the table, if any | ---@param iconSize string |nil size of the icon including any units, e.g., `20em` or `x16px` or assumes `px` if no units, or nil if not relevant | ||
---@vararg string|nil additional classes to add to the table, if any | |||
---@return Wikitext wikitext | ---@return Wikitext wikitext | ||
function Resource.tableStack(pairsList, ...) | function Resource.tableStack(pairsList, iconSize, ...) | ||
local classes = {...} | local classes = {...} | ||
-- quick check to see if amount column needed | |||
local needsAmountColumn = false | |||
for _, pair in ipairs(pairsList) do | |||
if pair._amount and pair._amount ~= "" then | |||
needsAmountColumn = true | |||
end | |||
end | |||
local wikitext = "{|" .. ((#classes > 0) and ("class=" .. table.concat(classes, " ")) or "") .. "\n" | local wikitext = "{|" .. ((#classes > 0) and ("class=" .. table.concat(classes, " ")) or "") .. "\n" | ||
for _, pair in ipairs(pairsList) do | for _, pair in ipairs(pairsList) do | ||
| Line 156: | Line 176: | ||
if not resource then return "[Resource table stack ID not found: " .. pair._id .. "]" end | if not resource then return "[Resource table stack ID not found: " .. pair._id .. "]" end | ||
wikitext = wikitext .. "|-\n" | wikitext = wikitext .. "|-\n" | ||
wikitext = wikitext .. "| " .. pair._amount .. "\n" | wikitext = wikitext .. (needsAmountColumn and ("|style=text-align:right| " .. (pair._amount or "") .. "\n") or "") | ||
wikitext = wikitext .. | wikitext = wikitext .. "| " .. resourceLink(resource, iconSize or standards.medium, true, true, "#Product") .. "\n" | ||
end | end | ||
return wikitext .. "|}" | return wikitext .. "|}" | ||
| Line 164: | Line 184: | ||
-- Renders a link to the given resource's wiki page by its ID. | -- Renders a link to the given resource's wiki page by its ID. | ||
---@param resourceID ResourceID | ---@param resourceID ResourceID | ||
---@param iconSize string size of the icon including units, e.g., "x16px" | ---@param iconSize string|nil size of the icon including units, e.g., "x16px" or nil for default | ||
---@param needsIcon boolean | ---@param needsIcon boolean|nil or nil for default | ||
---@param needsText boolean | ---@param needsText boolean|nil or nil for default | ||
---@param targetElementID string|nil #name or #id of the DOM element to link directly to, if any | |||
---@return Wikitext wikitext | ---@return Wikitext wikitext | ||
function Resource.resourceLinkByID(resourceID, iconSize, needsIcon, needsText) | function Resource.resourceLinkByID(resourceID, iconSize, needsIcon, needsText, targetElementID) | ||
return resourceLink(data()[resourceID], iconSize, needsIcon, needsText) | return resourceLink(data()[resourceID], iconSize, needsIcon, needsText, targetElementID) | ||
end | end | ||
| Line 196: | Line 217: | ||
end | end | ||
return resourceLink(resource, iconSize, needsIcon, needsText) | return resourceLink(resource, iconSize, needsIcon, needsText) | ||
end | |||
function Resource.stack(frame) | |||
local parent = frame:getParent() | |||
local args = parent and parent.args or frame.args | |||
local iconSize = args.icon and (args.icon ~= "" and standards[args.icon] or standards.medium) | |||
local classes = args.classes or "" | |||
--@type ResourcePair[] | |||
local pairsList = {} | |||
-- handle the indeterminate number of unnamed parameters passed to the template, but skip empty ones | |||
for _, pair in ipairs(args) do | |||
if pair ~= "" then | |||
local amount, name = pair:match("^(%d*)%s*(.+)$") | |||
local resource = findName(name) | |||
if not resource then | |||
return "[Resource not found: " .. name .. "]" | |||
end | |||
pairsList[#pairsList + 1] = {_id = resource._id, _amount = amount} | |||
end | |||
end | |||
if #pairsList == 0 then | |||
return "[Resource stack needs at least one resource]" | |||
end | |||
return Resource.tableStack(pairsList, iconSize, classes) | |||
end | end | ||
Latest revision as of 19:53, 13 November 2025
Documentation for this module may be created at Module:Resource/doc
-- Provides a standard way of interacting with data about goods, resources, and metaresources.
local Resource = {}
-- A single resource with attributes like ID, name, category, etc.
---@class Resource
---@field _id ResourceID Unique ID code of the resource.
---@field _displayName ResourceName The display name in-game.
---@field _description string The in-game descriptive text, including sprite icons, newlines, and escape characters.
---@field _iconFilename Filename The file name of the icon for this resource.
---@field _category CategoryResource Resource type from the info bar.
---@field _isEatable boolean `true` if the resource satisfies s villager's need for food.
---@field _isBurnable boolean `true` if the resource can be burned or sacrificed in a hearth.
---@field _burningTime integer Base number of seconds the item burns, or zero if it is not burnable, before any upgrades.
---@field _tradingSellValue Amber Base value when selling to traders, before any upgrades.
---@field _tradingBuyValue Amber Base cost when buying from traders, before any upgrades.
-- The ID code of a good or resource.
---@alias ResourceID string
-- The display name of a resource.
---@alias ResourceName string
-- Currency in-game.
---@alias Amber number
-- The ID and amount of a good or service.
---@alias ResourcePair {_id: ResourceID, _amount: Amount}
-- The resource category based on the info bar.<br>
-- Metaresources are categorized with trade goods.
---@enum CategoryResource
local CATEGORY_RESOURCE = {
Building = "Building Materials",
Consumable = "Consumable Items",
Crafting = "Crafting Resources",
Food = "Food",
Fuel = "Fuel & Exploration",
Trade = "Trade Goods"
}
--#region Dependencies
local RESOURCES_DATA_FILE = "Module:Resource/resources_data"
local Wiki_Utility = require("Module:Wiki_Utility")
local icon = Wiki_Utility.renderIcon
local nowrap = Wiki_Utility.surroundWithNoWrap
local wrapClasses = Wiki_Utility.surroundWithClasses
local link = Wiki_Utility.renderWikiLink
local NBSP = Wiki_Utility.NBSP
local standards = Wiki_Utility.StandardizedSizes
local isValidIconSize = Wiki_Utility.isValidIconSize
--#endregion Dependencies
--#region Constants
local MIN_ICON_SIZE = 16
--#endregion Constants
--#region Private Members
---@type table<ResourceID, Resource>
local resourceData
---@type table<ResourceName, ResourceID>
local mapNamesToIDs
local function data()
if not resourceData then
mapNamesToIDs = {}
resourceData = mw.loadData(RESOURCES_DATA_FILE)
for id, resource in pairs(resourceData) do
mapNamesToIDs[resource._displayName] = id
end
end
return resourceData
end
-- Finds a resource by its display name.
---@param resourceName ResourceName display name
---@return Resource|nil foundResource or nil if not found
local function findName(resourceName)
local foundResource = nil
for _, resource in pairs(data()) do
if resource._displayName == resourceName then
foundResource = resource
break
end
end
return foundResource
end
-- Renders a link to the given resource's wiki page.<br>
-- If the specified iconSize is too small, the icon will be ommitted instead of drawn so small as to be unrecognizable.
---@param resource Resource must not be nil
---@param iconSize string|nil size of the icon including units, e.g., "x16px", or nil for default
---@param needsIcon boolean|nil or nil for default
---@param needsText boolean|nil or nil for default
---@param targetElementID string|nil #name or #id of the DOM element to link directly to, if any
---@return Wikitext wikitext
local function resourceLink(resource, iconSize, needsIcon, needsText, targetElementID)
iconSize = iconSize or standards.small
needsIcon = needsIcon or (needsIcon == nil and true)
needsText = needsText or (needsText == nil and true)
local wikitext = ""
local isValidSize, sizeN = isValidIconSize(iconSize)
if needsIcon and isValidSize and sizeN >= MIN_ICON_SIZE then
-- Make it a height if it's not already.
if not iconSize:match("^x") then
iconSize = "x" .. iconSize
end
local directLink = resource._displayName .. (targetElementID and ("" .. targetElementID) or "")
wikitext = wikitext .. wrapClasses(icon(resource._iconFilename, iconSize, directLink, resource._displayName), "ats-link-resource", sizeN and sizeN < 23 and "ats-flag-small" or nil)
end
if (needsIcon and isValidSize and sizeN >= MIN_ICON_SIZE) and needsText then
wikitext = wikitext .. NBSP
end
if needsText then
wikitext = wikitext .. link(resource._displayName, resource._displayName, targetElementID)
end
return nowrap(wikitext)
end
--#endregion Private Methods
--#region Public Methods
-- Checks if the given ID is a valid resource ID.
---@param id ResourceID
---@return boolean
function Resource.isGood(id)
return data()[id] ~= nil
end
-- Finds a resource's ID by its display name.
---@param displayName ResourceName
---@return ResourceID|nil
function Resource.getID(displayName)
data()
return mapNamesToIDs[displayName]
end
-- Gets the specified resource's display name.
---@param id ResourceID
---@return ResourceName
function Resource.getName(id)
return data()[id]._displayName
end
-- Renders a table of pairs of resource IDs and amounts.
---@param pairsList ResourcePair[] array of pairs of resource IDs and amounts
---@param iconSize string |nil size of the icon including any units, e.g., `20em` or `x16px` or assumes `px` if no units, or nil if not relevant
---@vararg string|nil additional classes to add to the table, if any
---@return Wikitext wikitext
function Resource.tableStack(pairsList, iconSize, ...)
local classes = {...}
-- quick check to see if amount column needed
local needsAmountColumn = false
for _, pair in ipairs(pairsList) do
if pair._amount and pair._amount ~= "" then
needsAmountColumn = true
end
end
local wikitext = "{|" .. ((#classes > 0) and ("class=" .. table.concat(classes, " ")) or "") .. "\n"
for _, pair in ipairs(pairsList) do
local resource = data()[pair._id]
if not resource then return "[Resource table stack ID not found: " .. pair._id .. "]" end
wikitext = wikitext .. "|-\n"
wikitext = wikitext .. (needsAmountColumn and ("|style=text-align:right| " .. (pair._amount or "") .. "\n") or "")
wikitext = wikitext .. "| " .. resourceLink(resource, iconSize or standards.medium, true, true, "#Product") .. "\n"
end
return wikitext .. "|}"
end
-- Renders a link to the given resource's wiki page by its ID.
---@param resourceID ResourceID
---@param iconSize string|nil size of the icon including units, e.g., "x16px" or nil for default
---@param needsIcon boolean|nil or nil for default
---@param needsText boolean|nil or nil for default
---@param targetElementID string|nil #name or #id of the DOM element to link directly to, if any
---@return Wikitext wikitext
function Resource.resourceLinkByID(resourceID, iconSize, needsIcon, needsText, targetElementID)
return resourceLink(data()[resourceID], iconSize, needsIcon, needsText, targetElementID)
end
-- Resource_link template invokes this method from MediaWiki.
---@param frame Frame MediaWiki template context
---@return Wikitext wikitext wikitext markup to link to the resource's wiki page
function Resource.link(frame)
local name = frame.args.name or ""
if name == "" then
return "[Resource Link needs resource name]"
end
local iconSize = standards[frame.args.icon] or frame.args.icon or ""
local needsIcon = iconSize ~= "none"
-- Check if the size string is valid and doesn't represent a negative or too-large number.
if needsIcon and not isValidIconSize(iconSize) then
return "[Resource Link size not valid: " .. iconSize .. "]"
end
local display = frame.args.display or ""
if display ~= "" and display ~= "notext" then
return "[Resource Link display override not supported: " .. display .. "]"
end
local needsText = display ~= "notext"
local resource = findName(name)
if not resource then
return "[Resource not found: " .. name .. "]"
end
return resourceLink(resource, iconSize, needsIcon, needsText)
end
function Resource.stack(frame)
local parent = frame:getParent()
local args = parent and parent.args or frame.args
local iconSize = args.icon and (args.icon ~= "" and standards[args.icon] or standards.medium)
local classes = args.classes or ""
--@type ResourcePair[]
local pairsList = {}
-- handle the indeterminate number of unnamed parameters passed to the template, but skip empty ones
for _, pair in ipairs(args) do
if pair ~= "" then
local amount, name = pair:match("^(%d*)%s*(.+)$")
local resource = findName(name)
if not resource then
return "[Resource not found: " .. name .. "]"
end
pairsList[#pairsList + 1] = {_id = resource._id, _amount = amount}
end
end
if #pairsList == 0 then
return "[Resource stack needs at least one resource]"
end
return Resource.tableStack(pairsList, iconSize, classes)
end
--#endregion Public Methods
return Resource


