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