Module:HabModuleLister
From Terra Invicta Official Wiki
Documentation for this module may be created at Module:HabModuleLister/doc
---
---Reads the Hab Module data files and presents them in readable formats.
---
---@module HabModuleLister
local HabModuleLister = {}
--region Dependencies
local RestrictionChecker = require('Module:RestrictionChecker')
local IconModule = require('Module:Icon')
local TVModule = require('Module:TV')
local HmsModule = require('Module:HabModuleSpecial')
local HabModuleData = mw.loadData('Module:CSVReader/HabModule')
local HabModuleNames = mw.loadData('Module:ENReader/HabModule')
local ProjectNameBuilder = require('Module:ProjectLister/NameBuilder')
-- Science category names mapping (Note: These were converting in the wrong direction, I had to flip them around.)
local ScienceCategories = {
Energy = 'energy',
InformationScience='information',
LifeScience='life',
Materials='material',
MilitaryScience='military',
SocialScience='social',
SpaceScience='space',
Xenology='xenology'
}
--endregion
--region Private constants
local floor = math.floor
local round = function(num, decimals)
local mult = 10^(decimals or 0)
return floor(num * mult + 0.5) / mult
end
--endregion
--region Private methods
-- Safe value access with fallback with type conversion
local function getValue(row, key, default)
local value = row[key]
if value == nil or value == '' then
return default or 0
end
-- Convert to number if it looks like a number
if type(value) == 'string' and value:match('^[%-%d%.]+$') then
return tonumber(value) or default or 0
end
-- Convert boolean strings
if value == 'true' then return true end
if value == 'false' then return false end
return value
end
-- Calculate build cost for a resource
local function calculateBuildCost(mass, resourceWeight)
return round(tonumber(mass) * tonumber(resourceWeight) / 10, 2)
end
-- Format resource with TV module
local function formatResource(amount, resourceType, showZero)
amount = tonumber(amount) or 0
if amount == 0 and not showZero then
return ''
end
return TVModule.TV({args={resourceType, amount, 'true'}})
end
-- Get upgrade module name
local function getUpgradeModuleName(upgradeFromName)
if not upgradeFromName or upgradeFromName == '' then
return nil
end
return HabModuleNames['TIHabModuleTemplate.displayName.' .. upgradeFromName] or upgradeFromName
end
-- Format special rules
-- Format special rules
local function formatSpecialRules(row)
local specialRules = {}
-- Add special rules from array (using correct field names from CSV)
local shipbuilder = false
for i = 0, 5 do
local rule = getValue(row, 'specialRules/' .. i)
if rule and rule ~= '' and rule ~= 0 then
table.insert(specialRules, "* " .. HmsModule.hms({args={rule, tier = getValue(row, 'tier'), constructionTimeModifier = getValue(row, 'constructionTimeModifier'), specialRulesValue = getValue(row, 'specialRulesValue'), techBonus = getValue(row, 'techBonuses/0/bonus'), techCat = (ScienceCategories[getValue(row, 'techBonuses/0/category')] or getValue(row, 'techBonuses/0/category') or ''), controlPointCapacity = getValue(row, 'controlPointCapacity')}}))
-- table.insert(specialRules, "* {{HabModuleSpecial|" .. rule .. "|constructionTimeModifier=" ..
-- getValue(row, 'constructionTimeModifier') .. "|specialRulesValue=" ..
-- getValue(row, 'specialRulesValue') .. "|techBonus=" ..
-- getValue(row, 'techBonuses/0/bonus') .. "|techCat=" ..
-- (ScienceCategories[getValue(row, 'techBonuses/0/category')] or getValue(row, 'techBonuses/0/category') or '') ..
-- "|controlPointCapacity=" .. getValue(row, 'controlPointCapacity') .. "}}")
if rule == 'Shipyard' then
shipbuilder = true
end
end
end
if not shipbuilder and getValue(row, 'constructionTimeModifier') ~= 1 then
table.insert(specialRules, "* " .. HmsModule.hms({args={'WikiRuleConstruction', tier = getValue(row, 'tier'), constructionTimeModifier = getValue(row, 'constructionTimeModifier'), specialRulesValue = getValue(row, 'specialRulesValue'), techBonus = getValue(row, 'techBonuses/0/bonus'), techCat = (ScienceCategories[getValue(row, 'techBonuses/0/category')] or getValue(row, 'techBonuses/0/category') or ''), controlPointCapacity = getValue(row, 'controlPointCapacity')}}))
end
-- Add one per hab restriction
if getValue(row, 'onePerHab') == true or getValue(row, 'onePerHab') == 'true' then
table.insert(specialRules, "* Max one per [[Habs|Hab]].")
end
-- Add resupply capability
if getValue(row, 'allowsResupply') == true or getValue(row, 'allowsResupply') == 'true' then
table.insert(specialRules, "* Allows [[Spaceships|Ships]] to resupply.")
end
return "\n" .. table.concat(specialRules, "\n")
end
-- Filter modules by alien status
local function filterModulesByAlienStatus(alien)
local filtered = {}
for rowID, row in pairs(HabModuleData) do
local isAlien = getValue(row, 'alienModule') == true or getValue(row, 'alienModule') == 'true'
local destroyedModule = getValue(row, 'destroyed') == true or getValue(row, 'destroyed') == 'true'
if isAlien == alien and not destroyedModule then
filtered[rowID] = row
end
end
return filtered
end
local function filterModulesByShowOnly(showOnly, inputTable)
local filtered = {}
for rowID, row in pairs(inputTable) do
if not showOnly then
filtered[rowID] = row
elseif showOnly == 'CPCap' and (tonumber(getValue(row, 'controlPointCapacity', 0)) or 0) > 0 then
filtered[rowID] = row
end
end
return filtered
end
-- Safe number comparison function
local function isGreaterThanZero(value)
local num = tonumber(value) or 0
return num > 0
end
-- Safe negative number check
local function isLessThanZero(value)
local num = tonumber(value) or 0
return num < 0
end
--endregion
--region Public methods
---@param restrictionsTable table of restrictions
function HabModuleLister.ListHabModules(restrictionsTable)
local returnString = '{| class="wikitable sortable mw-collapsible" style="width:100%;text-align:center;"'
returnString = returnString .. '\n|-'
returnString = returnString .. '\n! Hab Module'
returnString = returnString .. '\n! Build Cost'
returnString = returnString .. '\n! Monthly Upkeep'
returnString = returnString .. '\n! Monthly Income'
returnString = returnString .. '\n! Special'
returnString = returnString .. '\n|-'
for rowID, row in pairs(HabModuleData) do
if ((not row['disable']) or row['disable'] ~= 'true')
and ((not row['destroyed']) or row['destroyed'] ~= 'true')
and RestrictionChecker.AllPass(restrictionsTable, row) then
local moduleName = HabModuleNames['TIHabModuleTemplate.displayName.' .. rowID] or rowID
local friendlyName = getValue(row, 'friendlyName') or moduleName
-- Module name column
returnString = returnString .. '\n| style="text-align:left;" | ' .. friendlyName
-- Upgrades information
local upgradeFromName = getValue(row, 'upgradesFromName')
if upgradeFromName and upgradeFromName ~= '' and upgradeFromName ~= 0 then
local upgradeName = getUpgradeModuleName(upgradeFromName)
if upgradeName then
returnString = returnString .. '\n* Upgrades: ' .. upgradeName
end
end
-- Required project
if not alien then
local projectID = getValue(row, 'requiredProjectName')
if projectID and projectID ~= '' and projectID ~= 0 then
returnString = returnString .. '\n* Project: ' .. ProjectNameBuilder.buildName({args={projectID}})
end
end
-- Build cost column
local mass = getValue(row, 'baseMass_tons', 0)
local buildCosts = {}
local waterCost = calculateBuildCost(mass, getValue(row, 'weightedBuildMaterials/water'))
if waterCost > 0 then
table.insert(buildCosts, formatResource(waterCost, 'water', true))
end
local volatilesCost = calculateBuildCost(mass, getValue(row, 'weightedBuildMaterials/volatiles'))
if volatilesCost > 0 then
table.insert(buildCosts, formatResource(volatilesCost, 'volatiles', true))
end
local metalsCost = calculateBuildCost(mass, getValue(row, 'weightedBuildMaterials/metals'))
if metalsCost > 0 then
table.insert(buildCosts, formatResource(metalsCost, 'metals', true))
end
local noblesCost = calculateBuildCost(mass, getValue(row, 'weightedBuildMaterials/nobleMetals'))
if noblesCost > 0 then
table.insert(buildCosts, formatResource(noblesCost, 'nobles', true))
end
local fissilesCost = calculateBuildCost(mass, getValue(row, 'weightedBuildMaterials/fissiles'))
if fissilesCost > 0 then
table.insert(buildCosts, formatResource(fissilesCost, 'fissiles', true))
end
local exoticsCost = calculateBuildCost(mass, getValue(row, 'weightedBuildMaterials/exotics'))
if exoticsCost > 0 then
table.insert(buildCosts, formatResource(exoticsCost, 'exotics', true))
end
local antimatterCost = calculateBuildCost(mass, getValue(row, 'weightbuildmaterials/antimatter'))
if antimatterCost > 0 then
table.insert(buildCosts, formatResource(antimatterCost, 'antimatter', true))
end
local buildTime = getValue(row, 'buildTime_Days')
if buildTime and buildTime > 0 then
table.insert(buildCosts, formatResource(buildTime, 'days', true))
end
returnString = returnString .. '\n| ' .. table.concat(buildCosts, ' ')
returnString = returnString .. '<br> Mass: ' .. mass .. ' tons'
-- Monthly upkeep column
local upkeepCosts = {}
local moneyUpkeep = getValue(row, 'supportMaterials_month/money')
if isGreaterThanZero(moneyUpkeep) then
table.insert(upkeepCosts, formatResource(moneyUpkeep, 'money', false))
end
local waterUpkeep = getValue(row, 'supportMaterials_month/water')
if isGreaterThanZero(waterUpkeep) then
table.insert(upkeepCosts, formatResource(waterUpkeep, 'water', false))
end
local volatilesUpkeep = getValue(row, 'supportMaterials_month/volatiles')
if isGreaterThanZero(volatilesUpkeep) then
table.insert(upkeepCosts, formatResource(volatilesUpkeep, 'volatiles', false))
end
local metalsUpkeep = getValue(row, 'supportMaterials_month/metals')
if isGreaterThanZero(metalsUpkeep) then
table.insert(upkeepCosts, formatResource(metalsUpkeep, 'metals', false))
end
local noblesUpkeep = getValue(row, 'supportMaterials_month/nobleMetals')
if isGreaterThanZero(noblesUpkeep) then
table.insert(upkeepCosts, formatResource(noblesUpkeep, 'nobles', false))
end
local fissilesUpkeep = getValue(row, 'supportMaterials_month/fissiles')
if isGreaterThanZero(fissilesUpkeep) then
table.insert(upkeepCosts, formatResource(fissilesUpkeep, 'fissiles', false))
end
local boostUpkeep = getValue(row, 'supportMaterials_month/boost')
if isGreaterThanZero(boostUpkeep) then
table.insert(upkeepCosts, formatResource(boostUpkeep, 'boost', false))
end
local powerConsumption = getValue(row, 'power')
if isLessThanZero(powerConsumption) then
table.insert(upkeepCosts, formatResource(-powerConsumption, 'powerconsumption', false))
end
local mcConsumption = getValue(row, 'missionControl')
if isLessThanZero(mcConsumption) then
table.insert(upkeepCosts, formatResource(-mcConsumption, 'mc', false))
end
local crew = getValue(row, 'crew')
if isGreaterThanZero(crew) then
table.insert(upkeepCosts, formatResource(crew, 'crew', false))
end
returnString = returnString .. '\n| ' .. table.concat(upkeepCosts, ' ')
-- Monthly income column
local incomeItems = {}
local moneyIncome = getValue(row, 'incomeMoney_month')
if isGreaterThanZero(moneyIncome) then
table.insert(incomeItems, formatResource(moneyIncome, 'money', false))
end
local influenceIncome = getValue(row, 'incomeInfluence_month')
if isGreaterThanZero(influenceIncome) then
table.insert(incomeItems, formatResource(influenceIncome, 'influence', false))
end
local opsIncome = getValue(row, 'incomeOps_month')
if isGreaterThanZero(opsIncome) then
table.insert(incomeItems, formatResource(opsIncome, 'ops', false))
end
local researchIncome = getValue(row, 'incomeResearch_month')
if isGreaterThanZero(researchIncome) then
table.insert(incomeItems, formatResource(researchIncome, 'research', false))
end
local projectsIncome = getValue(row, 'incomeProjects')
if isGreaterThanZero(projectsIncome) then
table.insert(incomeItems, formatResource(projectsIncome, 'projects', false))
end
local volatilesIncome = getValue(row, 'incomeVolatiles_month')
if isGreaterThanZero(volatilesIncome) then
table.insert(incomeItems, formatResource(volatilesIncome, 'volatiles', false))
end
local metalsIncome = getValue(row, 'incomeMetals_month')
if isGreaterThanZero(metalsIncome) then
table.insert(incomeItems, formatResource(metalsIncome, 'metals', false))
end
local noblesIncome = getValue(row, 'incomeNobles_month')
if isGreaterThanZero(noblesIncome) then
table.insert(incomeItems, formatResource(noblesIncome, 'nobles', false))
end
local fissilesIncome = getValue(row, 'incomeFissiles_month')
if isGreaterThanZero(fissilesIncome) then
table.insert(incomeItems, formatResource(fissilesIncome, 'fissiles', false))
end
local exoticsIncome = getValue(row, 'incomeExotics_month')
if isGreaterThanZero(exoticsIncome) then
table.insert(incomeItems, formatResource(exoticsIncome, 'exotics', false))
end
local antimatterIncome = getValue(row, 'incomeAntimatter_month')
if isGreaterThanZero(antimatterIncome) then
table.insert(incomeItems, formatResource(antimatterIncome, 'antimatter', false))
end
local powerProduction = getValue(row, 'power')
if isGreaterThanZero(powerProduction) then
table.insert(incomeItems, formatResource(powerProduction, 'power', false))
end
local mcProduction = getValue(row, 'missionControl')
if isGreaterThanZero(mcProduction) then
table.insert(incomeItems, formatResource(mcProduction, 'mc', false))
end
local miningModifier = getValue(row, 'miningModifier')
if isGreaterThanZero(miningModifier) then
table.insert(incomeItems, tostring(miningModifier) .. " × Hab Site Resources")
end
returnString = returnString .. '\n| ' .. table.concat(incomeItems, ' ')
-- Special column
local specialRules = formatSpecialRules(row)
returnString = returnString .. '\n| style="text-align:left;" | ' .. (specialRules ~= '' and specialRules or '')
returnString = returnString .. '\n|-'
end
end
returnString = returnString .. '\n|}'
return returnString
end
-- Convenience functions
function HabModuleLister.ListRestrictedHabModules(frame)
return HabModuleLister.ListHabModules(frame.args)
end
function HabModuleLister.ListHumanHabModules()
return HabModuleLister.ListHabModules({'Not Match,alienModule,true'})
end
function HabModuleLister.ListAlienHabModules()
return HabModuleLister.ListHabModules({'Match,alienModule,true'})
end
-- Replace your existing GetModuleTableRow function with this:
function HabModuleLister.GetModuleTableRow(frame)
local moduleName = frame.args[1] or frame.args['module'] or ''
local showBuildCost = frame.args[2] == 'true' or frame.args['buildcost'] == 'true'
local showUpkeep = frame.args[3] == 'true' or frame.args['upkeep'] == 'true'
local showIncome = frame.args[4] == 'true' or frame.args['income'] == 'true'
local showSpecial = frame.args[5] == 'true' or frame.args['special'] == 'true'
local format = frame.args[6] or frame.args['format'] or 'table' -- Add this line
if moduleName == '' then
return "Error: No module name provided"
end
-- Find the module by name
local rowID, row = nil, nil
-- Search through all modules
for id, moduleRow in pairs(HabModuleData) do
local currentModuleName = HabModuleNames['TIHabModuleTemplate.displayName.' .. id] or id
local friendlyName = getValue(moduleRow, 'friendlyName') or currentModuleName
if currentModuleName == moduleName or friendlyName == moduleName or id == moduleName then
rowID, row = id, moduleRow
break
end
end
if not row then
return "Module not found: " .. moduleName
end
-- Process this single module using the same logic as ListHabModules
-- Build cost column
local mass = getValue(row, 'baseMass_tons', 0)
local buildCosts = {}
local waterCost = calculateBuildCost(mass, getValue(row, 'weightedBuildMaterials/water'))
if waterCost > 0 then table.insert(buildCosts, formatResource(waterCost, 'water', true)) end
local volatilesCost = calculateBuildCost(mass, getValue(row, 'weightedBuildMaterials/volatiles'))
if volatilesCost > 0 then table.insert(buildCosts, formatResource(volatilesCost, 'volatiles', true)) end
local metalsCost = calculateBuildCost(mass, getValue(row, 'weightedBuildMaterials/metals'))
if metalsCost > 0 then table.insert(buildCosts, formatResource(metalsCost, 'metals', true)) end
local noblesCost = calculateBuildCost(mass, getValue(row, 'weightedBuildMaterials/nobleMetals'))
if noblesCost > 0 then table.insert(buildCosts, formatResource(noblesCost, 'nobles', true)) end
local fissilesCost = calculateBuildCost(mass, getValue(row, 'weightedBuildMaterials/fissiles'))
if fissilesCost > 0 then table.insert(buildCosts, formatResource(fissilesCost, 'fissiles', true)) end
local exoticsCost = calculateBuildCost(mass, getValue(row, 'weightedBuildMaterials/exotics'))
if exoticsCost > 0 then table.insert(buildCosts, formatResource(exoticsCost, 'exotics', true)) end
local antimatterCost = calculateBuildCost(mass, getValue(row, 'weightbuildmaterials/antimatter'))
if antimatterCost > 0 then table.insert(buildCosts, formatResource(antimatterCost, 'antimatter', true)) end
local buildTime = getValue(row, 'buildTime_Days')
if buildTime and buildTime > 0 then table.insert(buildCosts, formatResource(buildTime, 'days', true)) end
local buildCostString = table.concat(buildCosts, ' ')
-- Monthly upkeep column
local upkeepCosts = {}
local moneyUpkeep = getValue(row, 'supportMaterials_month/money')
if isGreaterThanZero(moneyUpkeep) then table.insert(upkeepCosts, formatResource(moneyUpkeep, 'money', false)) end
local waterUpkeep = getValue(row, 'supportMaterials_month/water')
if isGreaterThanZero(waterUpkeep) then table.insert(upkeepCosts, formatResource(waterUpkeep, 'water', false)) end
local volatilesUpkeep = getValue(row, 'supportMaterials_month/volatiles')
if isGreaterThanZero(volatilesUpkeep) then table.insert(upkeepCosts, formatResource(volatilesUpkeep, 'volatiles', false)) end
local metalsUpkeep = getValue(row, 'supportMaterials_month/metals')
if isGreaterThanZero(metalsUpkeep) then table.insert(upkeepCosts, formatResource(metalsUpkeep, 'metals', false)) end
local noblesUpkeep = getValue(row, 'supportMaterials_month/nobleMetals')
if isGreaterThanZero(noblesUpkeep) then table.insert(upkeepCosts, formatResource(noblesUpkeep, 'nobles', false)) end
local fissilesUpkeep = getValue(row, 'supportMaterials_month/fissiles')
if isGreaterThanZero(fissilesUpkeep) then table.insert(upkeepCosts, formatResource(fissilesUpkeep, 'fissiles', false)) end
local boostUpkeep = getValue(row, 'supportMaterials_month/boost')
if isGreaterThanZero(boostUpkeep) then table.insert(upkeepCosts, formatResource(boostUpkeep, 'boost', false)) end
local powerConsumption = getValue(row, 'power')
if isLessThanZero(powerConsumption) then table.insert(upkeepCosts, formatResource(-powerConsumption, 'powerconsumption', false)) end
local mcConsumption = getValue(row, 'missionControl')
if isLessThanZero(mcConsumption) then table.insert(upkeepCosts, formatResource(-mcConsumption, 'mc', false)) end
local crew = getValue(row, 'crew')
if isGreaterThanZero(crew) then table.insert(upkeepCosts, formatResource(crew, 'crew', false)) end
local upkeepString = table.concat(upkeepCosts, ' ')
-- Monthly income column
local incomeItems = {}
local moneyIncome = getValue(row, 'incomeMoney_month')
if isGreaterThanZero(moneyIncome) then table.insert(incomeItems, formatResource(moneyIncome, 'money', false)) end
local influenceIncome = getValue(row, 'incomeInfluence_month')
if isGreaterThanZero(influenceIncome) then table.insert(incomeItems, formatResource(influenceIncome, 'influence', false)) end
local opsIncome = getValue(row, 'incomeOps_month')
if isGreaterThanZero(opsIncome) then table.insert(incomeItems, formatResource(opsIncome, 'ops', false)) end
local researchIncome = getValue(row, 'incomeResearch_month')
if isGreaterThanZero(researchIncome) then table.insert(incomeItems, formatResource(researchIncome, 'research', false)) end
local projectsIncome = getValue(row, 'incomeProjects')
if isGreaterThanZero(projectsIncome) then table.insert(incomeItems, formatResource(projectsIncome, 'projects', false)) end
local volatilesIncome = getValue(row, 'incomeVolatiles_month')
if isGreaterThanZero(volatilesIncome) then table.insert(incomeItems, formatResource(volatilesIncome, 'volatiles', false)) end
local metalsIncome = getValue(row, 'incomeMetals_month')
if isGreaterThanZero(metalsIncome) then table.insert(incomeItems, formatResource(metalsIncome, 'metals', false)) end
local noblesIncome = getValue(row, 'incomeNobles_month')
if isGreaterThanZero(noblesIncome) then table.insert(incomeItems, formatResource(noblesIncome, 'nobles', false)) end
local fissilesIncome = getValue(row, 'incomeFissiles_month')
if isGreaterThanZero(fissilesIncome) then table.insert(incomeItems, formatResource(fissilesIncome, 'fissiles', false)) end
local exoticsIncome = getValue(row, 'incomeExotics_month')
if isGreaterThanZero(exoticsIncome) then table.insert(incomeItems, formatResource(exoticsIncome, 'exotics', false)) end
local antimatterIncome = getValue(row, 'incomeAntimatter_month')
if isGreaterThanZero(antimatterIncome) then table.insert(incomeItems, formatResource(antimatterIncome, 'antimatter', false)) end
local powerProduction = getValue(row, 'power')
if isGreaterThanZero(powerProduction) then table.insert(incomeItems, formatResource(powerProduction, 'power', false)) end
local mcProduction = getValue(row, 'missionControl')
if isGreaterThanZero(mcProduction) then table.insert(incomeItems, formatResource(mcProduction, 'mc', false)) end
local miningModifier = getValue(row, 'miningModifier')
if isGreaterThanZero(miningModifier) then table.insert(incomeItems, tostring(miningModifier) .. " × Hab Site Resources") end
local incomeString = table.concat(incomeItems, ' ')
-- Special column
local specialRules = formatSpecialRules(row)
local specialString = specialRules ~= '' and specialRules or ''
if format == 'list' or format == 'singlecell' then
-- Format for single cell with labels
local lines = {}
if showBuildCost and buildCostString ~= '' then
table.insert(lines, "'''Build cost''':<br> " .. buildCostString)
end
if showUpkeep and upkeepString ~= '' then
table.insert(lines, "<br>'''Monthly expenses''':<br> " .. upkeepString)
end
if showIncome and incomeString ~= '' then
table.insert(lines, "<br>'''Monthly income''':<br> " .. incomeString)
end
if showSpecial and specialString ~= '' then
table.insert(lines, "<br>'''Special Rule''':<br> " .. specialString)
end
return table.concat(lines, "<br>")
else
-- Original table format
local columns = {}
if showBuildCost then table.insert(columns, buildCostString) end
if showUpkeep then table.insert(columns, upkeepString) end
if showIncome then table.insert(columns, incomeString) end
if showSpecial then table.insert(columns, specialString) end
local result = table.concat(columns, "\n|")
return mw.getCurrentFrame():preprocess(result)
end
end
--endregion
return HabModuleLister


