Module:NationLister
From Terra Invicta Official Wiki
Documentation for this module may be created at Module:NationLister/doc
---
---Reads the Nation data files and presents them in readable formats, with additional calculated statistics.
---
---@module NationLister
local NationLister = {}
--region Dependencies
local IconModule = require('Module:Icon')
local TVModule = require('Module:TV')
local RegionData = mw.loadData('Module:CSVReader/Region')
local NationData = mw.loadData('Module:CSVReader/Nation')
local StartTimeData = mw.loadData('Module:CSVReader/StartTime')
local ClaimData = mw.loadData('Module:CSVReader/Claim')
local RegionArmyData = mw.loadData('Module:RegionLister/Armies')
local RegionNames = mw.loadData('Module:ENReader/Region')
local NationNames = mw.loadData('Module:ENReader/Nation')
--endregion
--region Private constants
local floor = math.floor
local mlog = math.log
local mmin = math.min
local mmax = math.max
local round = function(num, decimals)
local mult = 10^(decimals or 0)
return floor(num * mult + 0.5) / mult
end
local ssub = string.sub
--endregion
--region Private member variables
--none
--endregion
--region Private methods
--endregion
--region Public methods
function NationLister.calculateCPCost(GDP)
return (GDP / 1000000000) ^ 0.6 / 2
end
function NationLister.calculateNumControlPoints(GDP)
return mmin(6, mmax(1, round((GDP / 1000000000) ^ 0.18 / 1.09)))
end
---@return monthly research output for nation if unrest = 0 and there are no armies.
function NationLister.calculateIP(GDP)
return (GDP / 1000000000) ^ 0.35
end
---@return monthly research output for nation if cohesion = 5 and unrest = 0.
function NationLister.calculateResearch(pop, gdppc, education, democracy, numControlPoints)
--float population_Millions = this.population_Millions;
--float perCapitaGDP = this.perCapitaGDP;
--return (population_Millions * ((perCapitaGDP <= 30000f) ? Mathf.Pow(this.perCapitaGDP / 15000f, 0.6f) : (1.5157166f + 0.90942997f * (Mathf.Log(this.perCapitaGDP / 15000f) - 0.6931472f))) * (this.education * Mathf.Min(this.education, 12f)) * Mathf.Pow(Mathf.Max(this.democracy, 1f), 0.16666667f) * 0.0075f + Mathf.Min(this.population / 5000f, (float)this.numControlPoints + this.education + this.democracy / 2f)) * (1.25f - Mathf.Abs(this.cohesion - 5f) / 10f) * (1f - this.unrest * this.unrest * 0.01f) * (1f + this.adviserScienceBonus);
local gdppcMultiplier = 0
if gdppc <= 30000 then
gdppcMultiplier = (gdppc / 15000) ^ 0.6
else
gdppcMultiplier = 1.5157166 + 0.90942997 * (mlog(gdppc / 15000) - 0.6931472)
end
local educationMultiplier = education * mmin(education, 12)
local democracyMultiplier = mmax(democracy, 1) ^ 0.16666667
local baseValue = mmin(pop / 5000, numControlPoints + education + democracy / 2)
return (pop / 1000000 * gdppcMultiplier * educationMultiplier * democracyMultiplier * 0.0075 + baseValue) * 1.25
end
---@return monthly spoils profit for nation if unrest = 0 and there are no armies and 100% of the IP is put into spoils.
function NationLister.calculateSpoils(IP, democracy, numResourceRegions)
return (5 * IP + 5 * numResourceRegions + 2.5 * (10 - democracy)) * IP
end
function NationLister.ListPresentNations()
return NationLister.ListNations('2022')
end
function NationLister.List2026Nations()
return NationLister.ListNations('2026')
end
function NationLister.ListFutureNations()
return NationLister.ListNations('2070')
end
---@param campaignYear The start year of the campaign to show nations for. Defaults to 2022 if not specified
---@return table
function NationLister.ListNations(campaignYear)
if not campaignYear then
campaignYear = '2022'
end
local frame = mw.getCurrentFrame()
local returnString = '{| class="wikitable sortable mw-collapsible" style="text-align: center;'
returnString = returnString .. '\n|-'
returnString = returnString .. '\n! rowspan=2 | Nation<br>Alternate Name<br>(# Regions)'
returnString = returnString .. '\n! rowspan=2 | [[GDP]]<br>' .. IconModule.Icon({args={'cp'}}) .. '<br>' .. IconModule.Icon({args={'ip', bgcolor='DarkSlateGray'}})
returnString = returnString .. '\n! rowspan=2 | ' .. IconModule.Icon({args={'pop'}}) .. '<br>(Mns)'
returnString = returnString .. '\n! colspan=2 | ' .. IconModule.Icon({args={'research', bgcolor='DarkSlateGray'}}) .. ' per'
returnString = returnString .. '\n! colspan=2 | ' .. IconModule.Icon({args={'spoils'}}) .. ' per'
returnString = returnString .. '\n! rowspan=2 | ' .. IconModule.Icon({args={'inequality', bgcolor='DarkSlateGray'}}) .. '<br>' .. IconModule.Icon({args={'sustainability'}})
returnString = returnString .. '\n! rowspan=2 | ' .. IconModule.Icon({args={'democracy', bgcolor='DarkSlateGray'}})
returnString = returnString .. '\n! rowspan=2 | ' .. IconModule.Icon({args={'education'}})
returnString = returnString .. '\n! rowspan=2 | ' .. IconModule.Icon({args={'miltech'}}) .. '<br>' .. IconModule.Icon({args={'army'}}) .. '<br>' .. IconModule.Icon({args={'navy'}}) .. '<br>' .. IconModule.Icon({args={'found military'}})
returnString = returnString .. '\n! rowspan=2 | ' .. IconModule.Icon({args={'nuke'}}) .. '<br>' .. IconModule.Icon({args={'develop atomic bomb'}})
returnString = returnString .. '\n! rowspan=2 | ' .. IconModule.Icon({args={'boost'}}) .. '<br>' .. IconModule.Icon({args={'mc'}}) .. '<br>' .. IconModule.Icon({args={'money'}}) .. '<br>' .. IconModule.Icon({args={'found space program'}})
returnString = returnString .. '\n! rowspan=2 | ' .. IconModule.Icon({args={'cohesion', bgcolor='DarkSlateGray'}}) .. '<br>' .. IconModule.Icon({args={'unrest', bgcolor='DarkSlateGray'}})
returnString = returnString .. '\n|-'
returnString = returnString .. '\n! Month'
returnString = returnString .. '\n! ' .. IconModule.Icon({args={'cp'}})
returnString = returnString .. '\n! Month'
returnString = returnString .. '\n! ' .. IconModule.Icon({args={'cp'}})
returnString = returnString .. '\n|-'
for rowID, row in pairs(NationData) do
if ((string.sub(rowID,1,4) == campaignYear) or (campaignYear == '2022' and string.sub(rowID,1,2) ~= '20')) then
local GDP = tonumber(row['initialGDP'] or 0) or 0
startTimeDataName = campaignYear .. 'Start'
if startTimeDataName == '2022Start' then
startTimeDataName = 'ModernDayStart'
end
GDP = GDP * (tonumber(StartTimeData[startTimeDataName]['globalStartingGDPScaling'] or 1) or 1)
local education = tonumber(row['education'] or 0) or 0
local regionCount = 0
local regionPopulationInMns = 0
local totalRegionGDPShares = 0
local regionBoost = 0
local regionMC = 0
local regionMCCap = 0
local regionResource = 0
local regionArmy = 0
local regionNavy = 0
local claimList = ClaimData[1][rowID]
if claimList then
for i, regionID in ipairs(claimList) do
if ClaimData[3][rowID][regionID]['initialOwner'] == 'true' and RegionData[regionID] then
regionCount = regionCount + 1
regionPopulationInMns = regionPopulationInMns + (tonumber(RegionData[regionID]['population_Millions']) or 0)
local regionMultiplier = 1
if RegionData[regionID]['oilResource'] == 'true' or RegionData[regionID]['mining'] == 'true' or RegionData[regionID]['coreEco'] == 'true' then
regionMultiplier = 1.25
elseif ClaimData[3][rowID][regionID]['initialColony'] == 'true' then
regionMultiplier = 0.5
end
totalRegionGDPShares = totalRegionGDPShares + (tonumber(RegionData[regionID]['population_Millions']) or 0) * regionMultiplier
regionBoost = regionBoost + (tonumber(RegionData[regionID]['boostPerYear_tons']) or 0)
regionMC = regionMC + (tonumber(RegionData[regionID]['missionControl']) or 0)
if RegionData[regionID]['oilResource'] == 'true' or RegionData[regionID]['mining'] == 'true' then
regionResource = regionResource + 1
end
if RegionArmyData[regionID] then
regionArmy = regionArmy + 1
if RegionArmyData[regionID] == 'Naval' then
regionNavy = regionNavy + 1
end
end
end
end
for i, regionID in ipairs(claimList) do
if ClaimData[3][rowID][regionID]['initialOwner'] == 'true' and RegionData[regionID] then
local regionMultiplier = 1
if RegionData[regionID]['oilResource'] == 'true' or RegionData[regionID]['mining'] == 'true' or RegionData[regionID]['coreEco'] == 'true' then
regionMultiplier = 1.25
elseif ClaimData[3][rowID][regionID]['initialColony'] == 'true' then
regionMultiplier = 0.5
end
local regionShare = (tonumber(RegionData[regionID]['population_Millions']) or 0) * regionMultiplier
local regionGDP = GDP * regionShare / totalRegionGDPShares
regionMCCap = regionMCCap + 1 + floor(regionGDP / 1000000000 / mmax(200,300 - education * 6))
end
end
end
-- Convert boost to monthly decatons, and round to 2 decimals.
regionBoost = round(regionBoost / 120, 2)
local unionFlag = ''
if row['unionFlagResource'] and row['unionFlagResource'] ~= '' then
unionFlag = IconModule.Icon({args={ssub(rowID,-3) .. 'U',size='25px'}})
end
returnString = returnString .. '\n|' .. IconModule.Icon({args={ssub(rowID,-3),size='25px'}}) .. '[[' .. ssub(rowID,-3) .. '|' .. (NationNames['TINationTemplate.displayName.' .. ssub(rowID,-3)] or rowID) .. ']]<br>' .. unionFlag .. (NationNames['TINationTemplate.unionDisplayName.' .. ssub(rowID,-3)] or '') .. '<br>(' .. tostring(regionCount) .. ' Initial Regions)'
if regionCount > 0 then
-- This nation actually exists at the start of the scenario.
local CPCost = NationLister.calculateCPCost(GDP)
local IP = NationLister.calculateIP(GDP)
returnString = returnString .. '\n| ' .. round(GDP / 1000000000) .. '<br>' .. TVModule.TV({args={'cp', round(CPCost, 1), 'true'}}) .. '<br>' .. TVModule.TV({args={'ip', round(IP, 2), 'true'}})
returnString = returnString .. '\n| ' .. round(regionPopulationInMns, 2)
local numControlPoints = NationLister.calculateNumControlPoints(GDP)
local gdppc = GDP / regionPopulationInMns / 1000000
local democracy = tonumber(row['democracy'] or 0) or 0
local researchOutput = NationLister.calculateResearch(regionPopulationInMns * 1000000, gdppc, education, democracy, numControlPoints)
returnString = returnString .. '\n| ' .. round(researchOutput, 1)
returnString = returnString .. '\n| ' .. round(researchOutput / CPCost, 1)
local spoilsOutput = NationLister.calculateSpoils(IP, democracy, regionResource)
returnString = returnString .. '\n| ' .. round(spoilsOutput, 1)
returnString = returnString .. '\n| ' .. round(spoilsOutput / CPCost, 1)
returnString = returnString .. '\n| ' .. TVModule.TV({args={'inequality', row['inequality'], 'true'}})
local pollutionMultiplier = tonumber(row['greenEconomy'])
local sustainability = ''
if pollutionMultiplier == 0 then
sustainability = '10'
elseif (1/pollutionMultiplier) > 9.99 then
sustainability = '9.99+'
elseif (1/pollutionMultiplier) > 9.9 then
sustainability = '9.9+'
else
sustainability = tostring(floor(1/pollutionMultiplier * 100 + 0.5) / 100)
end
returnString = returnString .. '<br>' .. TVModule.TV({args={'sustainability', sustainability, 'true'}})
returnString = returnString .. '\n| ' .. democracy
returnString = returnString .. '\n| ' .. education
-- Null value for found military actually implies it has been founded.
local foundMilitaryIPs = tonumber(row['foundMilitaryIPs'] or 1) or 1
local manfSortValue = regionArmy * 10000 + (tonumber(row['miltech'] or 0) or 0) * 100 + regionNavy + foundMilitaryIPs * 1000000
returnString = returnString .. '\n| data-sort-value=' .. manfSortValue .. '|'
returnString = returnString .. TVModule.TV({args={'miltech', row['miltech'], 'true'}})
if regionArmy > 0 then
returnString = returnString .. '<br>' .. TVModule.TV({args={'army', regionArmy, 'true'}})
end
if regionNavy > 0 then
returnString = returnString .. '<br>' .. TVModule.TV({args={'navy', regionNavy, 'true'}})
end
returnString = returnString .. '<br>' .. (foundMilitaryIPs * 100) .. '%' .. IconModule.Icon({args={'found military'}})
local nukes = tonumber(row['nuclearWeapons'] or 0) or 0
returnString = returnString .. '\n| ' .. TVModule.TV({args={'nuke', nukes, 'true'}})
if nukes == 0 and row['nuclearProgramIPs'] then
returnString = returnString .. '<br>' .. (tonumber(row['nuclearProgramIPs']) * 100) .. '%' .. IconModule.Icon({args={'develop atomic bomb'}})
end
local funding = tonumber(row['spaceFunding_year']) or 0
-- Convert funding to monthly, and round to 2 decimals.
funding = round(funding / 12,2)
local initSpaceIPs = tonumber(row['initSpaceIPs'] or 0) or 0
local bmfsSortValue = regionBoost * 10000 + regionMC * 100 + funding + initSpaceIPs / 100
returnString = returnString .. '\n| data-sort-value=' .. bmfsSortValue .. '|'
if regionBoost > 0 then
returnString = returnString .. TVModule.TV({args={'boost', regionBoost, 'true'}}) .. '<br>'
end
returnString = returnString .. TVModule.TV({args={'mc', tostring(regionMC) .. '/' .. tostring(regionMCCap), 'true'}}) .. '<br>'
if funding > 0 then
returnString = returnString .. TVModule.TV({args={'money', funding, 'true'}}) .. '<br>'
end
returnString = returnString .. (initSpaceIPs * 100) .. '%' .. IconModule.Icon({args={'found space program'}})
returnString = returnString .. '\n| ' .. TVModule.TV({args={'cohesion', row['cohesion'], 'true'}}) .. '<br>' .. TVModule.TV({args={'unrest', row['unrest'], 'true'}})
end
returnString = returnString .. '\n|-'
end
end
returnString = returnString .. '\n|}'
return returnString
end
--endregion
return NationLister


