Module:FortificationStats: Difference between revisions
Appearance
Warmaninger (talk | contribs) Import generated wiki content (units, buildings, fortifications, overview, categories) |
Warmaninger (talk | contribs) Import generated wiki content (units, buildings, fortifications, overview, categories) |
||
| Line 88: | Line 88: | ||
end | end | ||
-- " | -- "2-10%" for the Trade Hub tax band. | ||
local function taxBandOf( stats ) | local function taxBandOf( stats ) | ||
local lo, hi = stats.trade_tax_min, stats.trade_tax_max | local lo, hi = stats.trade_tax_min, stats.trade_tax_max | ||
| Line 95: | Line 95: | ||
end | end | ||
if lo ~= nil and hi ~= nil then | if lo ~= nil and hi ~= nil then | ||
return string.format( '%s | return string.format( '%s-%s%%', lo, hi ) | ||
end | end | ||
return string.format( '%s%%', lo or hi ) | return string.format( '%s%%', lo or hi ) | ||
| Line 152: | Line 152: | ||
return table.concat( { | return table.concat( { | ||
'{| class="wikitable" style="float:right; margin:0 0 1em 1em; min-width:18em"\n', | '{| class="wikitable" style="float:right; margin:0 0 1em 1em; min-width:18em"\n', | ||
string.format( '|+ %s | string.format( '|+ %s (level %d)\n', fort.name, level ), | ||
row( 'Kind', 'Country fortification' ), | row( 'Kind', 'Country fortification' ), | ||
row( 'Max level', levelsLabel ), | row( 'Max level', levelsLabel ), | ||
| Line 206: | Line 206: | ||
local out = { | local out = { | ||
'{| class="wikitable" style="text-align:center"\n', | '{| class="wikitable" style="text-align:center"\n', | ||
string.format( '|+ %s | string.format( '|+ %s: stats by level\n', fort.name ), | ||
'! Stat\n', | '! Stat\n', | ||
} | } | ||
| Line 222: | Line 222: | ||
cells[#cells + 1] = string.format( '| %s\n', value ) | cells[#cells + 1] = string.format( '| %s\n', value ) | ||
else | else | ||
cells[#cells + 1] = '| | cells[#cells + 1] = '| -\n' | ||
end | end | ||
end | end | ||
Latest revision as of 22:48, 21 August 2026
Documentation for this module may be created at Module:FortificationStats/doc
-- Renders fortification statistics from [[Module:Data/Fortifications]].
--
-- This module is hand-written, in the same style as Module:UnitStats and
-- Module:BuildingStats. Module:Data/Fortifications is meant to be generated by
-- scripts/export-wiki-data.cjs (which only writes under the Module:Data/
-- prefix), so this file is never overwritten by the export.
--
-- Entry points:
-- infobox - stat box for ONE level {{#invoke:FortificationStats|infobox|code=auto_turret|level=1}}
-- levels - comparison table, ALL levels {{#invoke:FortificationStats|levels|code=auto_turret}}
--
-- Expected shape of Module:Data/Fortifications (field names follow the game's
-- country fortification tier table: attack, defense, durability, range,
-- detection_chance, trade_tax_min_pct, trade_tax_max_pct, build_time,
-- required_<resource>, upkeep_<resource>):
--
-- return {
-- auto_turret = {
-- name = 'Automatic Turret',
-- ammo = 'Turret Belts', -- optional
-- max_level = 5, -- optional
-- levels = {
-- [1] = {
-- attack = 10,
-- defense = 5,
-- durability = 100,
-- range = 5, -- km, optional
-- detection = 0.25, -- detection_chance, 0..1, optional (Strategic Radar)
-- trade_tax_min = 2, -- trade_tax_min_pct, optional (Trade Hub)
-- trade_tax_max = 10, -- trade_tax_max_pct, optional (Trade Hub)
-- build_time = '2h',
-- cost = { money = 100, food = 0, metal = 0, concrete = 50, petrol = 0, uranium = 0 },
-- upkeep = { money = 5, metal = 0, concrete = 0, petrol = 0, uranium = 0 },
-- },
-- [2] = { ... },
-- },
-- },
-- }
local p = {}
-- Module:Data/Fortifications is absent until the export has run; fail soft
-- instead of printing a Lua error on every fortification page.
local loaded, data = pcall( mw.loadData, 'Module:Data/Fortifications' )
if not loaded then
data = nil
end
local PENDING = "''Stats not yet available.''"
local RESOURCES = { 'money', 'food', 'metal', 'concrete', 'petrol', 'uranium' }
-- "100 money, 50 concrete", or "free" when nothing is required.
local function formatResources( bundle )
if not bundle then
return nil
end
local parts = {}
for _, name in ipairs( RESOURCES ) do
local amount = bundle[name]
if amount and amount > 0 then
parts[#parts + 1] = string.format( '%s %s', amount, name )
end
end
if #parts == 0 then
return 'free'
end
return table.concat( parts, ', ' )
end
-- Emits nothing at all for a missing value, so empty rows never show up.
local function row( label, value )
if value == nil or value == '' then
return ''
end
return string.format( '|-\n! %s\n| %s\n', label, value )
end
-- "25%" from a 0..1 chance (also accepts a value already in percent).
local function percentOf( value )
if value == nil then
return nil
end
if value <= 1 then
value = value * 100
end
return string.format( '%d%%', math.floor( value + 0.5 ) )
end
-- "2-10%" for the Trade Hub tax band.
local function taxBandOf( stats )
local lo, hi = stats.trade_tax_min, stats.trade_tax_max
if lo == nil and hi == nil then
return nil
end
if lo ~= nil and hi ~= nil then
return string.format( '%s-%s%%', lo, hi )
end
return string.format( '%s%%', lo or hi )
end
-- Level numbers of a fortification, sorted ascending. Works on mw.loadData
-- tables (which support pairs() but not the # operator) and tolerates gaps.
local function levelNumbers( fort )
local nums = {}
for key in pairs( fort.levels or {} ) do
local n = tonumber( key )
if n then
nums[#nums + 1] = n
end
end
table.sort( nums )
return nums
end
local function unknownFort( code )
return string.format(
'<strong class="error">Unknown fortification code: %s</strong>',
tostring( code )
)
end
--- Stat box for one level of one fortification.
-- Usage: {{#invoke:FortificationStats|infobox|code=auto_turret|level=1}}
function p.infobox( frame )
local code = frame.args.code
local level = tonumber( frame.args.level ) or 1
if not data then
return PENDING
end
local fort = data[code]
if not fort then
return unknownFort( code )
end
local stats = fort.levels and fort.levels[level]
if not stats then
return string.format(
'<strong class="error">%s has no level %s</strong>',
fort.name, tostring( level )
)
end
local maxLevel = fort.max_level or #levelNumbers( fort )
local levelsLabel = nil
if maxLevel and maxLevel > 1 then
levelsLabel = string.format( '%d (see [[#Stats|all levels]])', maxLevel )
end
return table.concat( {
'{| class="wikitable" style="float:right; margin:0 0 1em 1em; min-width:18em"\n',
string.format( '|+ %s (level %d)\n', fort.name, level ),
row( 'Kind', 'Country fortification' ),
row( 'Max level', levelsLabel ),
row( 'Attack', stats.attack ),
row( 'Defense', stats.defense ),
row( 'Durability', stats.durability ),
row( 'Range', stats.range ),
row( 'Detection chance', percentOf( stats.detection ) ),
row( 'Trade tax', taxBandOf( stats ) ),
row( 'Ammunition', fort.ammo ),
row( 'Build time', stats.build_time ),
row( 'Cost', formatResources( stats.cost ) ),
row( 'Upkeep', formatResources( stats.upkeep ) ),
'|}',
} )
end
-- Rows of the level comparison table: label + accessor. A row is skipped
-- entirely when no level has a value for it.
local LEVEL_ROWS = {
{ 'Attack', function( s ) return s.attack end },
{ 'Defense', function( s ) return s.defense end },
{ 'Durability', function( s ) return s.durability end },
{ 'Range', function( s ) return s.range end },
{ 'Detection chance', function( s ) return percentOf( s.detection ) end },
{ 'Trade tax', taxBandOf },
{ 'Build time', function( s ) return s.build_time end },
{ 'Cost', function( s ) return formatResources( s.cost ) end },
{ 'Upkeep', function( s ) return formatResources( s.upkeep ) end },
}
--- Comparison table of every level of one fortification (levels as columns).
-- Usage: {{#invoke:FortificationStats|levels|code=auto_turret}}
function p.levels( frame )
local code = frame.args.code
if not data then
return PENDING
end
local fort = data[code]
if not fort then
return unknownFort( code )
end
local nums = levelNumbers( fort )
if #nums == 0 then
return string.format(
'<strong class="error">%s has no level data</strong>', fort.name
)
end
local out = {
'{| class="wikitable" style="text-align:center"\n',
string.format( '|+ %s: stats by level\n', fort.name ),
'! Stat\n',
}
for _, n in ipairs( nums ) do
out[#out + 1] = string.format( '! Level %d\n', n )
end
for _, def in ipairs( LEVEL_ROWS ) do
local label, get = def[1], def[2]
local cells, any = {}, false
for _, n in ipairs( nums ) do
local value = get( fort.levels[n] )
if value ~= nil and value ~= '' then
any = true
cells[#cells + 1] = string.format( '| %s\n', value )
else
cells[#cells + 1] = '| -\n'
end
end
if any then
out[#out + 1] = string.format( '|-\n! style="text-align:left" | %s\n', label )
out[#out + 1] = table.concat( cells )
end
end
out[#out + 1] = '|}'
return table.concat( out )
end
return p