Downloads
Both also come with the game, in Primacy_Data\StreamingAssets\ModKit\, and the Mods window opens them for you.
Making mods for Primacy
A mod is a folder of plain text files: spreadsheets (CSV) you can edit in Excel or any text editor, a few JSON files, PNG pictures and small Lua scripts. There is nothing to install and nothing to compile. You put the folder in your mods folder, switch it on in the game's Mods window, and the game uses it from its next start. When it works, you publish it to the Steam Workshop from the same window.
What a mod can change: every country's start figures, cities (rename, change, remove, add), country names, flags and colours, brand-new countries, who owns each province, province names, tech nodes, Situation Room events, Government policies, new starts (scenarios), icons, fonts, translations, and rules written in Lua. What it cannot change: province shapes, rivers, coasts, terrain, the satellite picture, the tutorial, and game code (.dll files cannot be loaded).
Your first mod, step by step#
- Start the game. On the main menu click Mods.
- Click Example mod. The game copies a working example into your mods folder (
Documents\My Games\Primacy\Mods\example-mod). It appears in the list, switched off. - Click Open mods folder. Copy the folder
example-modand rename the copy, for examplemy-first-mod. - Open
my-first-mod\mod.jsonin a text editor (Notepad works). Change"id"to"my-first-mod"and"name"to a name you like. The id must be unique: two mods with the same id cannot run together. - Make one change. For example open
tables\countries.csvand change Poland's unemployment:POL,=0.08,means "set it to 8%". - Back in the game, close the Mods window and open it again (it reads the folder again). Switch your mod on and click Apply & restart (hold the button). The game restarts its session with your mod.
- Open Mods again. Your mod's row says Running and how many values it changed. If something is wrong it says how many problems its files have; click Mods log to see each one with its file and line.
- Start a new game as Poland and look at unemployment. That is a mod.
Documents\My Games\Primacy\ModDump and opens that folder. Everything in this guide that says "find it in the reference" is in there.Where everything is#
| What | Where |
|---|---|
| Your mods (making and testing) | Documents\My Games\Primacy\Mods\<your-mod>\. If your Documents folder is in OneDrive, it is under OneDrive. The Mods window's Open mods folder always opens the right one. |
| Mods from the Workshop | Steam keeps them in its own folder (steamapps\workshop\content\…). You do not need to touch them: subscribe on Steam and they appear in the Mods window. |
| The reference (every id, code and name) | The Mods window's Reference files writes it to Documents\My Games\Primacy\ModDump\ (from the game as it runs now, your mods included). An unmodded copy ships with the game: in Steam, right-click Primacy, Manage → Browse local files, then Primacy_Data\StreamingAssets\ModKit\reference\. |
| The example mod and this guide | Primacy_Data\StreamingAssets\ModKit\ in the game's folder (Example mod and Modding guide in the Mods window open them). |
| The mods log | Documents\My Games\Primacy\mods.log, new each time the game starts. It lists every problem in every mod (file and line), every conflict, what each mod changed, and everything your scripts print. Mods log in the Mods window opens it. |
| Your mod list and language | Kept by the game; change them only in the Mods window. |
Finding ids and names#
Every key a mod file uses comes from a file in the reference folder. Open them in Excel (Data → From Text/CSV, choose UTF-8) or a text editor and search with Ctrl+F.
| You need | Reference file | What is in it |
|---|---|---|
| A country's code (POL, RUS…) | lists\countries.csv | code, name now, the name the game's data uses, kind (Sovereign, Dependency…), whether it is on the map, provinces, capital, population. 250+ countries. |
| Country figure column names | tables\countries.csv | The first line lists every column; each row is a country with its start values. See Country figures for what each column means. |
| A city's key (POL.lodz) | cities\cities.csv | Every city (about 49,500): key, name, lat, lon, population, capital. Search the name, copy the key from the first column. |
| A province id (UKR-1234) | map\provinces.csv | Every province (about 4,200): id, name, owner. Search the province's name or filter by owner. |
| A tech node id | tech\nodes.csv | Every node (625): id, name, cost, year, weight, odds, and where it sits: field, branch, era. Search the name you see in the game's tech tree. |
| An event id | events\ids.txt | Every Situation Room event: id | family | tone | title. |
| A policy id | policies\ids.txt | Every Government policy: id | ministry | name | range and unit (or "Choice"). |
| Words for levers, ministries, voter groups, traits, laws, event families… | lists\names.txt | Every word a mod file may use, grouped by where it goes. |
| Equipment rows and weapon type names | military\equipment.csv, lists\weapon-types.csv | Every country's stock (key, country, type, in service, stored, on order, category); every one of the 4,512 weapon type names with category, year and maker. |
| Weapon type keys, stats, categories, shapes | militaryweapon-types.csv, listsweapon-categories.csv, listsweapon-shapes-schools.csv | Every weapon type with its key, generation, ratings, cost, year, maker, shape and the countries that build it; the category ids and what their three ratings mean; the 3D shapes and design schools. |
| Aircraft and warship numbers | militaryir-stats.csv, military
aval-stats.csv | Every role and generation of aircraft (fighter.1 .. drone.6); every hull class of warship. |
| Air base keys | military\air-bases.csv | Every airfield squadrons can use: key, name, kind, lat, lon, runway, country. |
| Naval base keys, navies | military\naval-bases.csv, military\navies.csv | Every naval base; every navy's ships by class. |
| Airport keys, seaport ids | transport\airports.csv, transport\seaports.csv | Every civil airport (about 2,600); every seaport on the sea-trade routes. |
| Icon keys | pictures\icons.txt | Every icon key the game draws (hud.economy, hud.bolt…). |
| Texts to translate | lang\en\seen.csv | Every fixed text of the game (windows, hover explanations, events, policies), ready to fill in. |
tech\nodes.csv: the id is in the first column of the same row (for example "Mobile Broadband" → comp.net.mobile-broadband).Tools: the Mods window, the console, the log#
The Mods window (main menu → Mods)
- The list: every installed mod with its picture, version, tags and whether it changes gameplay. Its state line says Running (and how many values it changed), "On after a restart", "Off", "Cannot run: …" with the reason, or the first error in its mod.json.
- The switch turns a mod on or off. Up / Down set the order (the top mod applies first, a lower mod wins over it).
- Publish (local mods) uploads to the Workshop; Page (Workshop mods) opens its Workshop page.
- Language: English and every language an installed mod offers.
- Example mod, Modding guide, Reference files, Mods log, Open mods folder, Open Workshop.
- Apply & restart (hold): the game's session starts again with the new list. It is only active when your list differs from what runs now.
The console (press the ` key, left of 1)
| Command | Does |
|---|---|
mods | Lists every installed mod: on, off, blocked (and why), running. |
mods check | Reads every mod folder again and prints every problem: mod.json errors, warnings, and every error and conflict of the running mods with file and line. |
mods log | The last 60 lines of the mods log (scripts' print and log lines too). |
mods folder | Prints where your local mods folder is. |
modsdump | Same as Reference files: writes the reference to ModDump. |
modsreload | Restarts the session with your mod files as they are on disk now: edit, reload, look, repeat. |
The mods log
14:02:11 Running, in this order: Example mod (example-mod v1.0), My mod (my-mod v0.1); language en
14:02:12 ERROR my-mod: cities/cities.csv line 4: no key "POL.lods" in cities (write "new:POL.lods" to add it)
14:02:12 warn conflict countries POL unemployment: set by example-mod, my-mod; my-mod wins
14:02:12 my-mod: 12 values changed, 1 problems
14:02:12 Applied in 640 ms
14:30:05 [my-mod] January 2026: Poland's debt is 61%
mod.json#
Every mod folder needs a mod.json at its top. Without it the folder is not a mod.
{
"id": "polish-golden-age",
"name": "Polish Golden Age",
"version": "2.0",
"game": "0.1+",
"description": "Poland's rise: a richer start, the Amber Coast and new events.",
"tags": ["Scenario", "Countries", "Events"],
"gameplay": true,
"loadAfter": ["real-balance-2025"],
"needs": [],
"dlc": []
}
| Field | Required | What it does |
|---|---|---|
id | yes | The mod's unique name: 3 to 64 characters, only small letters a-z, digits, - and _. Saves and other mods refer to it, so never change it after you publish. |
name | yes | The name players see. |
version | no (1.0) | Your version, numbers with dots ("1.2", "2.0.3"). Raise it when you publish an update. |
game | no | The game versions it works with: "0.1+" means 0.1 or newer; "0.1" means 0.1.x only; empty means any. A mismatch is a warning, not an error. The game's version is at the bottom left of the main menu. |
description | no | One line shown in the Mods window and used as the Workshop description when you first publish. |
tags | no | Workshop tags. Only these exact words count: Scenario, Balance, Overhaul, Graphics, Translation, Fixes, Map, Countries, Cities, Economy, Society, Military, Tech, Policies, Events, Scripts, Space DLC. |
gameplay | no (true) | false marks a looks-only mod (flags, icons, fonts, translations). A looks-only mod can be added to or removed from an existing save; a gameplay mod cannot. Any mod switches achievements off. |
loadAfter | no | Ids of mods this one must apply after, when they are on (so it wins over them). Use it when you change another mod's results. |
needs | no | Ids of mods this one cannot run without. If one is missing or off, this mod is blocked and the Mods window says why. |
dlc | no | ["space"] if the mod changes Space DLC content. Players without the pack see it blocked. A mod can never unlock a DLC. |
"loadafter") is an error, so a typo never silently does nothing. JSON needs double quotes around names and texts.Other files at the top of a mod
preview.png(or .jpg): the mod's picture in the Mods window and on the Workshop. 512×512 or 1024×1024, under 1 MB.workshop.txt: written by the game when you first publish; it holds your Workshop item number. Keep it: it makes your next upload update the same Workshop page.
The folder
my-mod/ mod.json required preview.png optional picture tables/countries.csv country start figures → Country figures cities/cities.csv cities → Cities map/countries.csv country names, new countries → Country names map/owners.csv who owns which province → Who owns the land map/provinces.csv province names → Province names tech/nodes.csv tech nodes → Tech tree events/*.json Situation Room events → Events policies/*.json Government policies → Policies scenario/start.json a start of its own → Scenarios military/equipment.csv weapons each country has → Equipment military/weapon-types.csv each weapon type's stats, new types → Weapon types military/air-stats.csv aircraft numbers by role and generation → Aircraft numbers military/naval-stats.csv warship numbers by hull class → Warship numbers military/air-bases.csv airfields for squadrons → Air bases military/naval-bases.csv naval bases → Naval bases and navies military/navies.csv ships by class per navy → Naval bases and navies transport/airports.csv civil airports → Airports and seaports transport/seaports.csv seaports → Airports and seaports diplomacy/organisations.csv the UN, NATO, the IMF... and new ones → Organisations diplomacy/*.csv trade deals, sanctions, treaties, disputes, bases... → Diplomacy military/warheads.csv nuclear stockpiles → Diplomacy and warheads military/navies.csv (see above) scripts/*.lua rules in Lua → Lua scripts pictures/flags/POL.png a country's flag → Flags and icons pictures/icons/<key>.png an icon → Flags and icons fonts/*.ttf, *.otf letters the game lacks → Fonts lang/<code>/language.json + *.csv a translation → Translations
Every part is optional except mod.json. A file in a folder the game knows but with a name it does not (for example tables/countrys.csv) is reported in the log, so a typo is found at once.
CSV and JSON rules#
CSV (tables)
- The first line that is not a comment is the header: column names, separated by commas.
- One row per line. Cells are separated by commas. Spaces around a cell are ignored.
- A cell with a comma in it goes in double quotes:
"Union of Soviet Socialist Republics, the". A quote inside: write it twice"". - Comments: a line starting with
#, orspace # spaceafter a cell:POL,=0.2 # Poland. A colour like#a8322fis a value, not a comment. - Decimals use a dot:
0.25, never0,25. - Save as UTF-8. In Excel: File → Save As → CSV UTF-8. Excel in some languages saves with semicolons: open the file in Notepad and check it uses commas.
- Empty lines are ignored. A cell left empty keeps the game's value.
JSON (events, policies, scenarios, mod.json)
- Names and texts in double quotes:
"name": "Amber fund". - Numbers without quotes:
"cost": 0.5.true,false,nullwithout quotes. - Lists in
[ ], objects in{ }, items separated by commas. - Allowed in Primacy (not in strict JSON): comments
// …and/* … */, and a comma after the last item. - A line break inside a text:
\n. A quote inside a text:\". - Errors give the line: "line 12: ':' missing after "name"".
How tables change values#
A table file does not replace the game's table: it lists only the rows and columns you change. The first column is the key (a country code, a city key, a node id…); the other columns are any of that table's columns, in any order, as few as you like.
| Cell | Meaning | Example | Result (game value 0.20) |
|---|---|---|---|
=x or x | set to x | =0.25 | 0.25 |
*x | multiply by x | *1.1 | 0.22 |
+x | add x | +0.05 | 0.25 |
+-x | subtract x | +-0.05 | 0.15 |
| (empty) | keep the value | 0.20 | |
yes / no | for yes/no columns (capital, dormant) | yes | |
| text | for text columns (names): set it; = optional | =Leningrad | |
+A B / -A B | for list columns (country codes, kinds): add these items / take them out; a plain list replaces the whole list. Adds and removals from several mods all apply (no conflict) | +SWE FIN |
- Remove a row (cities only): put
removein the second cell:UKR.pripyat,remove. - Add a row (cities only): start the key with
new:and fill every needed column:new:POL.amber-port,Amber Port,54.40,18.20,120000,no,3. *and+only work on numbers; text can only be set.- A wrong line is reported and skipped; the rest of the file still applies.
Order, conflicts, restarts and saves#
When a mod applies
Once, while the game starts (before the map, borders, cities and names are built). Changing the mod list or the language therefore needs Apply & restart; a running game never mixes two mod lists. modsreload in the console does the same after you edited files.
Order
Mods apply from the top of the list to the bottom, moved only as far as each one's loadAfter needs. A later mod's = wins over an earlier one; * and + stack: VAT 23% with mod A =0.25 then mod B *0.8 gives 20%.
Conflicts
A value two mods both set with = is a conflict. It is not an error: the lower mod wins, and the Mods window and the log say which value, which mods and who won.
Saves
- A save remembers its mods and their order.
- A save loads only with the same gameplay mods in the same order. Otherwise the game shows which mods it needs, offers Subscribe to all (Workshop mods) and Use this save's mods (switches your list and loads the save).
- Looks-only mods (
"gameplay": false) can be added or removed at any time. - A newer version of the same mod is allowed: Workshop mods update themselves. Keep your updates compatible with saves made with older versions.
Country figures#
Every number of every country at the start of a game: people, health, schools, safety, the economy, the budget and the forces. The key column is iso3, the country code (see lists\countries.csv). A country you change gets these values from day one; the economy, the forces and everything else are built from them.
iso3,population,unemployment,health.beds,militarySpending
POL,*1.05,=0.04,,
HUN,,,+0.5,
ISR,,,,=0.06
| Column | What it is |
|---|---|
dormant | yes = not on the map at the start (a nation that can be released later); no = on the map. |
population | People at the start. |
urbanShare | Share of people living in towns and cities (0.6 = 60%). |
ages.children | Share of people under working age. |
ages.working | Share of people of working age. |
ages.retired | Share of people of retirement age. The three ages add up to 1. |
education.basic | Share of adults with primary school or less. |
education.skilled | Share of adults with secondary or vocational school. |
education.expert | Share of adults with university. The three add up to 1. |
birthRate | Births per 1,000 people per year. |
deathRate | Deaths per 1,000 people per year. |
lifeExpectancy | Years a baby born now would live on average. |
participation | Share of the working-age population in the labour force. |
unemployment | Share of the labour force without work (0.05 = 5%). |
health.spending | Current health spending as a share of GDP (0.12 = 12%), public and private. |
health.stateShare | Share of the health spending the state pays (the rest is households and insurance). |
health.doctors | Doctors per 1,000 people. |
health.nurses | Nurses and midwives per 1,000 people. |
health.beds | Hospital beds per 1,000 people. |
health.infantDeaths | Babies dying before their first birthday, per 1,000 born. |
schooling.spending | Public education spending as a share of GDP (0.05 = 5%); private schooling adds about 15% on top. |
schooling.primary | Gross enrolment ratios: pupils of any age over the children of that school age (can pass 1). |
schooling.secondary | Gross enrolment ratio in secondary school (can pass 1). |
schooling.tertiary | Gross enrolment ratio in universities and colleges (can pass 1). |
schooling.literacy | Share of adults who can read and write. |
schooling.learningYears | Learning-adjusted years of school (World Bank Human Capital Index): years at school x how much is learnt. |
safety.spending | Public order & safety spending (police, fire crews, courts, prisons) as a share of GDP (0.018 = 1.8%). |
safety.murders | Murders (intentional homicides) per 100,000 people a year. |
safety.assaults | Recorded assaults per 100,000 people a year. |
safety.robberies | Recorded robberies per 100,000 people a year. |
safety.thefts | Recorded thefts and burglaries per 100,000 people a year. |
safety.drugOffences | Recorded drug offences per 100,000 people a year. |
safety.frauds | Recorded frauds per 100,000 people a year. |
safety.police | Police officers per 100,000 people. |
safety.prisoners | Prisoners per 100,000 people. |
safety.organisedCrime | Organised crime 0..1 (Global Organized Crime Index criminality, 1-10 scaled). |
safety.guns | Civilian guns per 100 people. |
safety.fireDeaths | Deaths from fire, heat and hot substances per 100,000 people a year. |
safety.burnedKm2 | Land burnt by wildfires in an average year, km2. |
safety.policing | Policing style: 0 community, 1 standard, 2 iron fist. |
safety.gunLaw | Gun ownership: 0 strict, 1 licensed, 2 open. |
safety.drugLaw | Drug policy: 0 prohibition, 1 decriminalised, 2 legal. |
safety.sentencing | Sentencing: 0 rehabilitation, 1 standard, 2 punitive. |
leisure.tourismIn | What foreign visitors spend here a year, as a share of GDP (0.05 = 5%). |
leisure.tourismOut | What residents spend on trips abroad a year, as a share of GDP. |
leisure.arrivals | Foreign visitor arrivals a year per resident. |
leisure.householdShare | Household consumption as a share of GDP. |
gdpUsd | Nominal GDP in US dollars. |
gdpPppUsd | GDP at purchasing-power parity (international dollars). |
priceLevel | Price level against the USA (the PPP ratio): India ~0.3, Switzerland ~1.4. |
gdpShares.agriculture | Share of GDP from farming, forests and fishing. |
gdpShares.industry | Share of GDP from industry, mining, energy and building. |
gdpShares.services | Share of GDP from services. The three add up to 1. |
employmentShares.agriculture | Share of jobs in farming. |
employmentShares.industry | Share of jobs in industry. |
employmentShares.services | Share of jobs in services. The three add up to 1. |
inflation | Inflation per year (0.03 = 3%). |
infrastructure | Roads, rail, ports, grid: 0 none.. 1 excellent. |
cityLevel | How developed the country's cities look at the start: 0 Informal, 1 Industrial, 2 Emerging, 3 Advanced, 4 Futuristic, 5 Sci-fi (fractions mix the two tiers). Big cities and the capital sit a little above it, small towns below. A city can have its own level in cities/cities.csv. |
cityLevelOverride | Used by the game's own data tools; leave it. |
stateOwnership | Share of the economy the state owns at the start (per-sector shares come later). |
planning | How much of investment follows the central plan instead of profit: 0 none.. 1 full planning. |
revenueShare | Government revenue as a share of GDP. |
debtShare | Government debt as a share of GDP (1 = 100%). |
budgetBalance | General government budget balance in 2024 as a share of GDP: net lending (+ surplus) or borrowing (- deficit). |
interestShare | Net interest the general government paid in 2024, a share of GDP; -1 = unknown (the model estimates it from the debt). |
fxReserves | Total foreign reserves incl. gold at the end of 2024, in US dollars; -1 = unknown (the model assumes three months of imports). |
militarySpending | Military spending as a share of GDP (0.02 = 2%). |
armedForces | Armed forces personnel, active and paramilitary (IISS via the World Bank). |
reservists | Military reservists: not under arms in peacetime, called up when needed. |
airForce.fighters | Fighters and interceptors (air superiority; generation 4 and up are multirole). |
airForce.attack | Attack and strike aircraft (bombers not included). |
airForce.bombers | Strategic bombers. |
airForce.attackHelicopters | Attack helicopters. |
airForce.helicopters | Every military helicopter, attack ones included. |
airForce.tankers | Air-to-air refuelling tankers. |
airForce.specialMission | Special mission: AWACS, reconnaissance, electronic warfare, maritime patrol. |
airForce.transports | Transport aircraft. |
Cities#
Rename a city, change its population, make it the capital, set how developed it looks, remove it, or add a new one anywhere on land. The game builds everything else from the list: buildings and night lights, map labels, offices and homes; the city's owner is the province it stands in.
key,name,lat,lon,population,capital,level
RUS.saint-petersburg,=Leningrad,,,,,
RUS.volgograd,=Stalingrad,,,*1.5,,=5
POL.lodz,remove
new:POL.amber-port,Amber Port,54.40,18.20,120000,no,3
| Column | Meaning |
|---|---|
key | The city: its country's code, a dot, and its name in small plain letters with - for spaces (accents removed): "Łódź" → POL.lodz, "Saint Petersburg" → RUS.saint-petersburg. A second city of the same name in the same country gets -2, -3. Always copy keys from cities\cities.csv in the reference. For a new city you choose the key: country code + dot + any name, e.g. new:POL.amber-port. The code decides which country's data the city starts with; its owner is still the province it stands in. |
name | The name shown everywhere (labels, windows, news). |
lat, lon | Position in degrees (lat −90..90, north positive; lon −180..180, east positive), as in Google Maps' "lat, lon". Needed for a new city; changing them for an existing city moves it. |
population | People in the city. It sets how big the city is drawn and how high its label ranks. |
capital | yes / no. One capital per country: making a city the capital takes the title from the old one. |
level | How developed it looks at the start, 0 to 5: 0 Informal, 1 Industrial, 2 Emerging, 3 Advanced, 4 Futuristic, 5 Sci-fi (fractions mix two looks). Empty: the game decides from the country. |
- Removed city: its buildings and label go; its people stay in the province's figures. A stadium, holy site, station or airport it had serves the nearest city of the same country.
- New city: needs name, lat, lon and population. Its people come from its province (the province's total stays the same). Buildings grow by themselves.
- Adding or removing cities needs a new game (saves remember cities by their place in the list). Renames also work with old saves.
- A name with letters the game's fonts lack (Москва, 東京) works: the letters come from a fallback font.
Country names and new countries#
iso3,name,short,adjective,flag,colour
RUS,Union of Soviet Socialist Republics,Soviet Union,Soviet,flags/SOV.png,#a8322f
KUR,Kurdistan,Kurdistan,Kurdish,flags/KUR.png,#c8a040
| Column | Meaning |
|---|---|
iso3 | A code from lists\countries.csv renames that country. A code the game does not know creates a brand-new country with that code. |
name | The new name shown everywhere: the map, every window, news, Messages, diplomacy. Must not be another country's name. Needed for a new country. |
short, adjective | The short name and the adjective ("Soviet Union", "Soviet"); scripts can read them (country(code).short). |
flag | A picture in your mod: flags/SOV.png or pictures/flags/SOV.png. PNG or JPG, 3:2 like the game's flags (for example 300×200). |
colour | The map colour: #rrggbb (from any colour picker), e.g. #a8322f. |
- A renamed country keeps all its figures; the game's own data still finds it by its old name.
- A new country starts with nothing: give it land in
map/owners.csv. It builds its economy from the provinces it gets, like a state born from a revolt. - Rows apply before
owners.csv, so a new country can receive land in the same mod.
Who owns the land#
from,to
UKR,RUS # every province of Ukraine to Russia
BLR,RUS
IRQ-2201,KUR # one province to the new country Kurdistan
from: a country code (every province it owns now) or a province id (one province). Province ids are inmap\provinces.csv(columnid, with its name and owner).to: a country code, including a new country made inmap/countries.csv.- Moves run top to bottom, before day 1, with the same annexation wars use: people, farms, industry and cities go with the land.
- A country left with no land stays in the game (it can come back), like one that lost a war.
- Province shapes never change: you can only give whole provinces.
Province names#
id,name
RUS-2155,=Leningrad Oblast
The id comes from map\provinces.csv in the reference. Only the name can change; the owner column there is for information (owners change in map/owners.csv). Renames work with old saves.
Tech tree#
id,name,cost,year,weight,odds
comp.net.mobile-broadband,,*0.9,,,
enrg.fusion.compact-q1,=Compact fusion,,=2040,,
Finding the id: open tech\nodes.csv in the reference and search the node's name as the tech tree shows it. The first column is the id. The columns field, branch and era tell you where it sits (for example "Compute & AI", "comp.net", "The Digital Age").
| Column | Meaning |
|---|---|
id | The node, e.g. comp.net.mobile-broadband (field.branch.name). |
name | The name shown in the tree. |
cost | Research points it takes (above 0). *0.5 halves it. |
year | Its plausible year (1800..2300). Before that year, while no country has it yet, research on it is slower the earlier you are: 1 year early ≈ 0.77× speed, 5 years ≈ 0.29×, 10 years ≈ 0.12×. Once any country has it, or from that year on, it goes at full speed. 0 = no such brake (things already invented when the game starts). |
weight | How much the node counts: in a country's overall technology level, and in what it costs to catch up on it when others already have it (higher = counts more, costs more to catch up). |
odds | Only for risky breakthroughs (a gamble): the chance one attempt succeeds, 0..1; each failed try adds 0.2, up to 0.95. 0 for ordinary nodes. |
field, branch, era | Where it sits in the tree (for information: they cannot change). |
Situation Room events#
Any number of .json files in events/. Each has "add" (new events), "change" (the game's or another mod's events), or both.
How events are picked
Each month the game weighs every event that may happen to a country: its weight formula gives a number (0 = cannot happen now). One event is picked with chances in proportion to the weights (an "Opportunity" counts ×1.25). An event does not come back to the same country before its cooldown (months); the player does not get two events of the same family within 75 days. Typical weights: 0.02 (rare) to 0.5 (likely).
{
"add": [ {
"id": "pga.vistula_boom",
"family": "Economy", "tone": "Opportunity", "scope": "Player", "place": "BigCity", "other": "None",
"countries": ["POL"],
"cooldown": 48, "days": 7, "big": false, "chainOnly": false,
"weight": "0.04 + ramp(gdpPerHead, 15000, 40000) * 0.06 * (atWar ? 0.5 : 1)",
"title": "Factories flock to the Vistula",
"text": "Three carmakers want to build near {place}. They ask for {money:0.2} in tax breaks.",
"facts": [ ["Jobs", "{num:900-4000}"], ["Asked for", "{money:0.2}"] ],
"voices": [ ["Economy minister", "Say yes before Hungary does."], ["Finance minister", "Every factory wants a gift."] ],
"choices": [
{ "name": "Tax breaks", "about": "Pay to win them", "fx": "cost:0.2 gdp+0.3@24 mood:Business+3@12",
"riskP": 0.2, "riskText": "One carmaker takes the money and leaves.", "riskFx": "ap-3@6",
"echoP": 0.5, "echoMonths": 12, "echoEvent": "pga.vistula_second_wave", "echoText": "A second wave of investors calls." },
{ "name": "Let them come", "about": "No money from the state", "fx": "gdp+0.1@24", "hold": false, "notes": ["Slower"] }
],
"none": "mood:Business-2@6",
"noneText": "Nobody decided. The carmakers went to Hungary.",
"worldNews": "{country} lands three car factories."
} ],
"change": {
"volcano": { "off": true },
"hostages_abroad": { "weightMul": 0.5, "cooldown": 60 },
"quake": { "title": "The ground shakes in {place}", "days": 5 }
}
}
Every field of an added event
| Field | Required | Meaning |
|---|---|---|
id | yes | Unique id. Start it with your mod's short name (pga.) so it never clashes with the game's or another mod's (all ids: events\ids.txt). |
family | no (Society) | Disaster, Economy, Politics, Society, Security, Diplomacy, Military, Science, World. |
tone | no (Either) | Crisis, Opportunity, Either: how the dossier is coloured. |
scope | no (Both) | Player (only the player's country gets it), Ai (only others: world news), Both. |
place | no (Capital) | Where it happens ({place}, and where the camera flies): Capital, BigCity, FarmProvince, Port, BorderWithOther, Province, EvHazard, None. |
other | no (None) | Another country it is about ({other}): None, Neighbour, HostileNeighbour, NeighbourAtWar, Ally, AllyAtWar, Rival, BigPower, Buyer, Victim. |
countries | no | Only these countries can get it (codes). Leave out for every country. |
cooldown | no (48) | Months before it can come again to the same country. |
days | no (7) | Days the player has to decide (the clock on the dossier). |
weight | no (0.02) | A number or a formula in quotes. |
title, text | yes | The dossier's headline and story. Templates like {place} are filled in. |
facts | no | A list of ["label", "value"] pairs shown as figures on the dossier. |
voices | no | A list of ["who", "what they say"] pairs: the advisers. |
choices | yes (1–4) | The options; see below. |
none, noneText | no | Effects and the line when the deadline passes with no choice. |
big | no | true: a big crisis that pauses the game. |
chainOnly | no | true: never rolled by itself; only opened by another event's echo or by a script. |
worldNews | no | The world news line when it happens to another country. |
Every field of a choice
| Field | Meaning |
|---|---|
name, about | The button's title and the line under it. |
fx | Effect words separated by spaces: what the choice does. |
riskP, riskText, riskFx | The chance (0..1) it goes wrong, what the player is told, and the extra effects then. |
echoP, echoMonths, echoEvent, echoText | A follow-up: chance (0..1), after how many months, the event id it opens (yours or the game's), or only a line in Messages. |
hold | true: the player must hold the button (for choices that cannot be undone). |
notes | Extra chips on the card, a list of short texts ("Deaths −40%"). |
Changing an existing event ("change")
| Field | Meaning |
|---|---|
"off": true | The event never happens. |
"weightMul": 0.5 | Multiplies its weight (0.5 = half as likely). |
"weight": "formula" | Replaces its weight with your formula. |
"cooldown", "days", "title", "text" | Replace those. |
Text templates#
| Write | Becomes |
|---|---|
{country} | The country's name. |
{place}, {province} | The place (city or province, from place), the province. |
{other} | The other country (from other). |
{capital}, {year} | The capital's name, the year. |
{money:1.2} | 1.2% of the country's GDP, in dollars ("$9.8 B"). |
{pop:0.4} | 0.4% of the place's people, as a number. |
{num:900-4000}, {pct:20-60} | A number / a percentage in that range (the same each time the dossier is shown). |
Effect words#
Effects are short words separated by spaces: cost:0.2 gdp+0.3@24 mood:Business+3@12. @n at the end means "for n months" (the effect fades over that time); without it, it is immediate or permanent. All numbers use a dot for decimals.
| Write | Does | Example |
|---|---|---|
ap±n@m | Approval points of the player's government, fading over m months. | ap+7@4 |
mood:Group±n@m | A voter group's mood. Groups (no spaces): Workers, Business, Retirees, Families, Experts, Publicstaff, Rural, Young, Savers, Patients, Soldiers, Faithful, Minorities. | mood:Rural-3@8 |
cost:x | A one-off cost: x% of GDP from the budget (Emergencies). | cost:0.9 |
gain:x | A one-off windfall: x% of GDP. | gain:0.5 |
yearly:x@m | x% of GDP a year for m months (a cost; negative = income). | yearly:0.4@24 |
gdp±x@m | The country's output, % for m months. | gdp-0.4@3 |
local±x@m | Output of the event's province only, %. | local-3@6 |
farm±x@m / farmw±x@m | Harvests in the province / the whole country, %. | farmw-8@12 |
res±x@m | Research speed, %. | res+5@24 |
mort±x@m | Deaths, %. | mort+2@6 |
inv±x@m | How attractive the country is to investors, %. | inv-5@12 |
army±x@m | Army fighting quality, %. | army-15@2 |
inf±n | Diplomatic influence points (the player's). | inf+20 |
rel±n:who@m | Opinion of others towards the country; who = Other, Neighbours, World, Allies, Donors; fades over m months (default 12). | rel+6:donors@12 |
trust±n:who | Trust of others, same who. | trust-4:world |
trait:name±n | The leader's reputation: Steady, Iron, Caring, Gambler, Builder, Hawk. | trait:caring+1 |
mark:kind@days | A mark on the map at the event's place for that many days: Rubble, Flood, Ash, Cyclone, Evacuation, Riot, Smoke. | mark:rubble@365 |
pol:id=v / pol:id*v | Signs a Government policy (ids in policies\ids.txt) at value v, or its value now times v. It waits and phases in like any policy. The player's country only. | pol:incb=20 |
law:Name=option | Signs an economy law option (names in lists\names.txt, "Laws"). | law:RentControl=1 |
med:what±n@m | War medicine: Beds, Deaths, Trains, Fatigue, Backlog, Pension, Abroad, Supply (%). | med:beds+20@3 |
strike:n | Settles a staff strike: 0 nurses, 1 doctors, 2 teachers. | strike:0 |
Chance formulas#
A formula is a text in quotes. It is worked out for each country each month; the result is the event's weight (0 or less = it cannot happen).
What you can write
- Numbers:
0.04,2.5e-3. + - * /and brackets (dividing by 0 gives 0).- Comparisons:
< > <= >= == !=(1 true, 0 false). - Logic:
&&and,||or,!not,true,false. a ? b : c: b if a is true, else c.ramp(x, from, to): 0 below from, 1 at to and above, a straight line between.clamp(x, lo, hi),min(a, b),max(a, b),abs(x),pow(a, b),if(c, a, b).
Examples
"0.03" always the same
"atWar ? 0.2 : 0" only at war
"ramp(unemployment, 0.06, 0.15) * 0.4" grows with joblessness
"0.05 + (approval < 35) * 0.1" likelier when unpopular
"coastal && month >= 6 && month <= 9 ? 0.1 : 0" summer, on the coastValues a formula can read (the country being weighed)
| Name | Meaning and unit |
|---|---|
unemployment | Share of the labour force without work (0.05 = 5%). |
inflation | Price rise per year (0.03 = 3%). |
growth | Economic growth in percent (2.5 = 2.5%). |
debtShare | State debt as a share of GDP (1 = 100%). |
gdp, gdpPerHead | GDP in dollars; GDP per person in dollars. |
approval | The player's approval, 0..100 (other countries: 50). |
foodPrice | Food price level: 1 normal, higher when inflation is high. |
research | Research effort, about 0.002 (poor) to 0.04 (rich). |
atWar, neighbourAtWar | 1 if the country / a neighbour is at war, else 0. |
exporter, bigCountry, coastal | 1 or 0: a big exporter; a big country; has a coast. |
soldiersMood, workersMood, youngMood, expertsMood, businessMood | The player's voter group moods, −100..100 (0 for other countries). |
tension | Tension between faiths, about 0 (calm) to 1. |
weariness | Weariness of a campaign against armed groups (0 none). |
corruption | 0..1. |
terrorThreat, campaigning | Threat from armed groups 0..1; 1 if the country fights a campaign against them. |
month, year | The date: month 1..12, the year. |
lat | The capital's latitude (negative = southern hemisphere). |
player | 1 if this is the player's country. |
Government policies#
Change the game's policies, or add new sliders. A new policy appears in its ministry in the Government window, is signed like any other, waits, then phases in. Ids: policies\ids.txt.
{
"change": {
"incb": { "max": 60, "wait": 2, "name": "Income tax (flat)", "about": "One rate for every worker." }
},
"add": [ {
"id": "pga.amber_fund",
"ministry": "Industry",
"name": "Amber Coast fund",
"about": "State money that makes the coast attractive to investors.",
"examples": "Like Ireland's 1990s grants",
"unit": "%", "min": 0, "max": 2, "step": 0.1, "default": 0,
"wait": 3, "ramp": 12, "stage1": "Parliament", "stage2": "Money flows",
"levers": [ { "stat": "InvestmentAttractiveness", "perUnit": 1.5 },
{ "stat": "Corruption", "perUnit": 0.5 } ],
"forecast": "gdp .02 .05 bal -.03 -.01",
"upBetter": ["Investors come to the coast"], "upWorse": ["Costs the budget"],
"downBetter": ["Saves money"], "downWorse": ["Fewer new firms"],
"whoUp": "Business +", "whoDown": "Business -"
} ]
}
Changing a policy
min, max, step (slider range), wait (months until it starts), ramp (months until full effect), name, about, examples.
Every field of a new policy
| Field | Meaning |
|---|---|
id | Unique id; start it with your mod's short name. |
ministry | Treasury, CentralBank, Labour, Industry, Energy, Health, Education, Safety, Leisure, Defence, Faith, Sport, Transport, Housing (full list: lists\names.txt). |
name, about, examples | What the card shows. |
unit, min, max, step, default | The slider: its unit text, range, step, and the value every country starts with. |
wait, ramp, stage1, stage2 | Months until it starts and until full effect, and the names of the two stages. |
levers | What it does. Each lever moves one economy figure of the country by perUnit % for each unit of the slider: perUnit 1.5 at slider 2 means +3%. Negative perUnit lowers it. The lever names (stat) are in lists\names.txt under "Policy levers": SectorOutput, LaborProductivity, InputUse, LandYield, ExtractionRate, Recovery, ConstructionCost, BuildTime, MaintenanceCost, TradeCost, TariffRevenue, ExportPrice, ResearchSpeed, TechDiffusion, BirthRate, Mortality, MigrationPull, EducationCoverage, Happiness, Pollution, Corruption, Stability, InterestRate, TaxCollection, InvestmentAttractiveness, AccidentChance, SpoilageRate. |
forecast | What the card forecasts per unit, as pairs of low and high: keys gdp (% of GDP), unemp (points), infl (points), bal (budget, % of GDP), debt (% of GDP), le (life expectancy, years), sch (school years), appr (approval points). "gdp .02 .05 bal -.03 -.01". |
upBetter, upWorse, downBetter, downWorse | Lists of short lines: what is better and worse when it goes up or down. |
whoUp, whoDown | Which voter groups like it: "Business +, Workers -". |
Scenarios#
A scenario is a start of its own. Combine it with map/countries.csv and map/owners.csv for a different world on day one. It shows on the nation pick's GAME SETUP strip. If two running mods have one, the game merges them setting by setting: a setting a mod leaves out keeps what a mod above it gave (a scenario without year keeps another mod's year), a setting both give is the lower mod's (shown as a conflict), and their wars add up.
{
"name": "The Soviet Union returns",
"description": "Russia has taken back the Soviet republics. Poland and Finland stand alone.",
"keepGameWars": false,
"wars": [ {
"attacker": "RUS", "defender": "POL", "since": "2024-12-20",
"held": [ [ [23.5,52.1], [24.2,52.1], [24.2,51.6], [23.5,51.6] ] ],
"fronts": [ { "name": "Brest", "lon": 23.7, "lat": 52.1,
"attackerGroup": "Western group", "attackerDivisions": 6, "attackerArtillery": 1,
"defenderGroup": "Eastern command", "defenderDivisions": 4, "defenderArtillery": 1 } ]
} ]
}
| Field | Meaning |
|---|---|
name, description | Shown on GAME SETUP. |
keepGameWars | true (default): the game's own wars of the day (Russia–Ukraine) start too; false: only yours. |
year | An earlier start: the world's year, 1946 .. 2023 (leave it out for the game's 2024). The clock starts on 1 January of the next year ("year": 1985 → 1 January 1986). Every weapon type that entered service after that year leaves the arsenals: each country's stock of it becomes the newest type of the same kind in service by then (its own older versions first, then its family, its design school, its maker), with the same numbers; orders of it are dropped; a kind that did not exist yet (drones in 1960) is removed. Weapon-tree steps of later years are not known to anyone and must be developed again, and the programmes and production lines of 2024 follow the same year. The tech tree too: every technology first made after that year is forgotten by every country, and each field's level falls by what those technologies gave, so economies start weaker (most in electronics, energy, medicine) and grow back as they research them again. Dates of play are shown on the scenario's calendar (the game counts on its own calendar inside, so every system runs as usual); dates of the 2024 data's history keep their real years. Countries, borders and populations stay those of 2024 (change them with the other files), and content tied to dates (5G, new cables, sport tournaments, space missions) comes on its own schedule, that many years early. |
wars[].attacker, defender | Country codes (renamed or new countries too). |
wars[].since | The date the war began, YYYY-MM-DD (it can be before the game starts). |
wars[].held | Areas the attacker already holds: a list of rings; each ring is at least 3 points [lon, lat] in order around the area. |
wars[].fronts | Where the forces stand: a point on the line, and each side's divisions (10,000 troops each) and artillery divisions. Armies appear a few kilometres behind it on each side. |
An earlier start needs only the year (and usually no wars of today):
{ "name": "Cold War, 1985", "description": "The world of 1985.", "keepGameWars": false, "year": 1985 }
Wars start with the "Start with wars" option on GAME SETUP; off means a world at peace.
Armies and squadrons#
A country's forces at the start are built from its figures and its equipment, so you change them in two files:
| To change | Where | Example |
|---|---|---|
| How many soldiers (armies are formed from them) | tables/countries.csv: armedForces (active), reservists | POL,=300000,=350000 |
| How many aircraft of each role (squadrons are formed from them, at the air bases the country holds) | tables/countries.csv: airForce.fighters, airForce.attack, airForce.bombers, airForce.attackHelicopters, airForce.helicopters, airForce.tankers, airForce.specialMission, airForce.transports | POL,,,=96 |
| Which tanks, jets, ships' weapons, drones they have | military/equipment.csv | see Equipment |
| What a tank, jet, ship or drone can do; new types of your own | military/weapon-types.csv | see Weapon types |
| How far, fast and hard each kind of aircraft flies and fights | military/air-stats.csv | see Aircraft numbers |
| Warships' speed, missiles, sensors, armour | military/naval-stats.csv | see Warship numbers |
| Where squadrons can be | military/air-bases.csv | see Air bases |
| Ships | military/navies.csv, military/naval-bases.csv | see Naval bases and navies |
| Wars and fronts on day one | scenario/start.json | see Scenarios |
A brand-new country made by a mod has no row in tables/countries.csv, so it starts with no forces of its own; it raises them in play like any country. Squadrons only form at air bases inside the country's land on day one.
Equipment#
Every country's weapons at the start: how many of each type it has in service, in storage and on order. Armies, squadrons and fleets take their weapons from this list.
key,country,type,active,stored,ordered
POL.k2-black-panther,,,*1.5,,
UKR.t-64bv,remove
new:POL.leopard-2a7v,POL,Leopard 2A7V,120,0,0
| Column | Meaning |
|---|---|
key | The country's code, a dot, and the type's name in plain letters (POL.k2-black-panther); -2 for a second row of the same type. Copy keys from military\equipment.csv. For a new row choose any key starting with the country code. |
country | The owner's code (needed for a new row). |
type | The weapon type's exact name from lists\weapon-types.csv (e.g. Leopard 2A7V, F-35A Lightning II). Needed for a new row. A type your mod adds in military/weapon-types.csv works too (by its name), and so does a weapon-types key (leopard-2a7v). |
active, stored, ordered | In service; in storage (can be brought back); ordered and still to come. |
category | For information (tank, fighter, frigate…): it comes with the type. |
Weapon types: stats and new types#
Every tank, armoured vehicle, gun, air-defence system, aircraft, helicopter, drone and warship in the game is a weapon type: 4,512 of them, from the T-55 to designs of the 2060s. This file changes what each type can do, and adds new types of your own: a new bomber, fighter, tank or frigate.
key,name,category,generation,rating1,rating2,rating3,cost,year,maker,school,model,family,builtBy
su-57,,,=5,,=15,,,,,,,,
tu-160,=Tu-160 White Swan,,,,*1.5,,,,,,,,
leopard-2a7v,,,,=12,,,=18,,,,,,
new:tu-260,Tu-260 Storm,bomber,5.2,20,30,10,450,2031,RUS,RUS,Bomber,Tu-260,RUS
new:pzl-hawk,PZL Hawk,multirole,4.4,5,10,0,70,2029,POL,EURO,Fighter,Hawk,POL
The first three lines change the game's own types (Su-57 one generation step up and better weapons; the Tu-160 renamed and its weapons rating half again higher; the Leopard 2A7V better protected and dearer). The last two add new aircraft that Russia and Poland can build from the first day.
| Column | Meaning |
|---|---|
key | The type's name in plain letters: lower case, every run of other characters turned into one - (Su-57 → su-57, F-16C/D Block 50 → f-16c-d-block-50). A second type with the same plain name gets -2. Copy keys from military\weapon-types.csv. For a new type write new: and any key not taken. |
name | Its name everywhere in the game. Names must all differ: saves, military/equipment.csv and the arms market find a type by its name. You may rename the game's types; a renamed type is still found by its old name, so older saves and your equipment rows keep working. |
category | What it is: an id from lists\weapon-categories.csv (mbt, ifv, spg, sam_long, fighter, multirole, attack, bomber, attack_heli, male_drone, ucav, ddg, ffg, ssn…). Needed for a new type. The game's own types keep their category (armies, squadrons and fleets are built round it): to turn a tank into something else, add a new type. |
generation | The most important number: how modern it is. Aircraft and ships 1 .. 6, where for aircraft 1 = Gen 2, 2 = Gen 3, 3 = Gen 4, 4 = Gen 4.5, 5 = Gen 5, 6 = Gen 6 (an F-16C/D Block 50 is 3.2, an Su-57 4.7). Ships: 1 = 1950s, 2 = 1960s-70s, 3 = 1980s, 4 = 1990s-2000s, 5 = 2010s, 6 = newest. Armour and guns 0.2 .. 5 (a T-55 about 1, a Leopard 2A7V 3.4). Decimals are fine. The game allows 0.2 .. 7. |
rating1, rating2, rating3 | How much better (or worse) it is than an average type of its generation, in % from -100 to 100 (the game's own types stay within -20 .. +20). Each category has its own three ratings: the ratings column names them (e.g. Sensors/Weapons/Range for fighters). See the table below for what each one does. |
ratings | For information: the names of the three ratings for its category. |
cost | The price of one, in US$ million (a Leopard 2A7V 15, an F-35A about 80, a destroyer about 2,000). Building, buying and selling use it. |
year | The year it entered service (1900 .. 2300): shown in windows, and of two types of the same generation a country builds and fields the newer. |
maker | The 3-letter code of the country that designed it (SUN is the Soviet Union for old Soviet types). |
school | The design school it belongs to: US, EURO, RUS, CHN, ISR, KOR, TUR, IND, JPN, IRN, PAK, BRA, UKR, Other (lists\weapon-shapes-schools.csv). Empty for a new type: Other. |
model | The 3D shape drawn on the map: Tank, TrackedIfv, WheeledApc, Fighter, Strike, Bomber, BigJet, Helicopter, Drone, Destroyer, Frigate, Submarine… (all in lists\weapon-shapes-schools.csv). Mods cannot add new 3D models. Empty for a new type: the usual shape of its category. |
family | The family it belongs to (T-72, F-16): shown in windows. Empty for a new type: its name. |
builtBy | The countries whose factories make it from the start: 3-letter codes separated by spaces (RUS BLR). They can order it on their production lines and sell it to others on the arms market. Empty: nobody builds it (it exists only where military/equipment.csv puts it). |
What the three ratings do
| Category | rating1 | rating2 | rating3 |
|---|---|---|---|
| Tanks, IFVs, APCs, MRAPs, recce | Protection | Firepower | Mobility: half of it is added to the army's march speed (+20 = 10% faster) |
| Guns and rocket launchers | Range | Firepower | Mobility (as above) |
| Air defence (SAMs, AA guns) | Reach | Firepower | Mobility |
| Fighters, multirole, attack, bombers | Sensors | Weapons: multiplies the squadron's ground attack (+30 = 30% harder strikes) | Range: multiplies its combat radius (+10 = 10% further) |
| Helicopters | Protection | Weapons | Range |
| Drones | Sensors | Weapons | Endurance |
| Warships and submarines | Defence | Strike | Endurance: multiplies its days at sea (+25 = a quarter longer) |
| Support (trucks, tankers, transports, AEW) | Capacity | Sensors | Range |
All three also count together as the type's worth: 1 + (rating1 + rating2 + rating3) / 300. An army's fighting power is multiplied by its equipment's worth (three ratings of +30 = 30% stronger), and the AI compares types by it.
How a type's generation becomes real numbers
- Aircraft: a squadron flies with the numbers of its role at its aircraft's generation from military/air-stats.csv, taken between the two nearest rows (generation 4.6 = 60% of the way from the Gen 4.5 row to the Gen 5 row). Then rating2 and rating3 above. The role comes from the category:
fighter→ fighter;multirole→ fighter or strike;attack→ strike;bomber→ bomber;male_drone,ucav→ drone. - Ships: a ship has the numbers of its hull class from military/naval-stats.csv at its generation rounded (4.6 → 5). The hull comes from the category:
cvcarrier,lhdassault ship,lpdlanding ship,cgcruiser,ddgdestroyer,ffgfrigate,corvettecorvette,ssn/ssgnattack submarine,ssk/midgetdiesel submarine,ssbnmissile submarine,mcmminesweeper. - Armies: an army's generation is the average of its equipment's. Each step of generation over its country's starting level makes it markedly stronger, and its worth (above) multiplies that.
Giving your new type to countries
- Add the type here with
new:(name, category, generation, ratings, cost, year at least). - Put it in service: add rows to
military/equipment.csvusing its name as the type:
Squadrons, armies and fleets take it from their country's stock at the start (newest first).key,country,type,active,stored,ordered new:RUS.tu-260,RUS,Tu-260 Storm,16,0,8 - Let countries build it: write their codes in
builtBy. Others can then buy it from them on the arms market.
New types come after the game's 4,512 and cannot be removed (rows of this file cannot be removed at all: set a type's equipment rows to 0 instead). A save that holds a mod's type needs that mod to be on: without it, those holdings are dropped on loading and the log says how many.
Aircraft numbers#
How far, how fast and how hard every kind of aircraft flies and fights, by role and generation: 30 rows, fighter.1 (a Gen 2 fighter) to drone.6. The roles are fighter, strike (strike fighters and attack aircraft), bomber, helicopter (attack helicopters) and drone (MALE and combat drones). Generation 1 .. 6 = Gen 2, 3, 4, 4.5, 5, 6.
key,radius,speed,stealth,radar,missileRange,missiles
fighter.5,*1.2,,=0.7,,,
fighter.6,,=1300,,=260,=200,=8
bomber.4,=4000,,,,,
| Column | Meaning |
|---|---|
key | Role, a dot, generation 1 .. 6. |
role, generation, low | For information (a helicopter flies low, under long-range radar). |
radius | Combat radius in km: how far it strikes and comes back (at least 20). It can move to another base up to 2.5 times that (helicopters 3 times). |
speed | Cruise speed in km/h (at least 50). |
airToAir | Air-to-air power of one aircraft (a fighter 1; bombers 0). |
groundAttack | Ground-attack power of one aircraft (a strike fighter 1, a bomber 6; each generation adds 10% on top). |
stealth | 0 .. 0.95: how much it shrinks the distance radars see it at (0.6 = seen at 40% of the usual range). |
radar | How far its own radar sees an aircraft without stealth, in km. |
missileRange | Reach of its long-range air-to-air missiles in km (0 = guns and short-range missiles only). |
missiles | Long-range missiles one aircraft carries. |
endurance | Hours it can stay in the air on internal fuel (at least 0.2). A patrol stays on station for what the trip out and back leaves. |
standoff | How far from its target it releases its weapons, in km (0 = it must fly over the target; bombers of Gen 4.5 and up fire cruise missiles from 300 km). |
squadron | The most aircraft in a squadron of this role (1 .. 60). One number per role: set it on any row of the role and it counts for all its generations. |
runway | The shortest runway it needs, in metres (0 = any field). |
altitude | Cruise altitude in km above the ground. |
size | How big it looks to radar and the eye (a fighter 1, a bomber 3; at least 0.05). |
agility | How well it turns and dodges (a fighter 1, a bomber 0.25). |
hitEase | How easy it is to hit (a fighter 1, a bomber 1.4; at least 0.1). |
hoursOver | Hours it stays over its target. |
turnaround | Hours on the ground between sorties (re-arming, refuelling, repairs). |
sortiesPerDay | The most sorties one aircraft flies a day (0.1 .. 12). |
Changes count for every country. A squadron flying a mix of types averages the numbers of their generations, then applies their weapons and range ratings (Weapon types).
Air bases#
key,name,kind,lat,lon,runway,country
POL.lask-air-base,=Lask 32nd Tactical Air Base,,,,=3200,
new:POL.amber-coast-air-base,Amber Coast Air Base,military,54.45,18.30,2800,POL
POL.copernicus-wroclaw-airport,remove
| Column | Meaning |
|---|---|
key | Country code + dot + the base's name in plain letters. Copy from military\air-bases.csv. |
name | Shown on the map and in windows. |
kind | military or civil (a civil airfield holds only a few squadrons). |
lat, lon | Where it is, in degrees. |
runway | Longest runway in metres (500..6000): longer runways take heavier aircraft and more squadrons. |
country | The code of the country the base belongs to in the data. In play, a base serves whoever holds its ground. |
Squadrons are placed at the start at the bases each country holds; a country with aircraft but no base has nowhere to fly from. Air bases and civil airports are separate lists: this one is for military flying, airports are for passengers and the economy.
Airports and seaports#
# transport/airports.csv
key,name,lat,lon,country,runways,passengers,military,iata
POL.warsaw-chopin-airport,=Warsaw Okecie,,,,,*1.2,,
new:POL.amber-coast-airport,Amber Coast Airport,54.38,18.47,POL,2,4.5,no,AMB
# transport/seaports.csv
id,name,depth,containers,bulk,oil,lng
gdansk,,=18,*1.5,,,
| Airport column | Meaning |
|---|---|
key | Country code + dot + the airport's name in plain letters. Copy from transport\airports.csv. |
name, lat, lon, country | Its name, position and country. A new airport's province is the one it stands in. |
runways | 1..6. |
passengers | Millions a year at the start. |
military | yes: also used as a military airfield. |
iata | Its three-letter code (shown in windows; optional). |
province | For information (where it stands). |
| Seaport column | Meaning |
|---|---|
id | The port's id (the sea-trade routes run between these). Seaports can be changed but not added or removed. |
name | Its name. |
depth | Water depth at its berths in metres (up to 40): deeper takes bigger ships. |
containers, bulk, oil, lng | Traffic a year at the start: containers in millions of TEU; bulk, oil and LNG in millions of tonnes. The berths are built to carry it. |
Organisations#
The international organisations of the start: the UN and its Security Council, NATO, the EU, the IMF, the WTO, OPEC+, BRICS and the rest (27 rows, keys in diplomacy\organisations.csv of the reference). Change their members and rules, remove them, or found new ones.
key,name,kinds,voting,members,suspended,mutual,vetoes
NATO,,,,+UKR GEO,,,
EU,,,,-HUN,,,
IMF,remove
new:WARSAW,Warsaw Pact,defence arms,consensus,RUS POL CZE SVK HUN ROU BGR,,yes,
new:COMECON,Council for Mutual Economic Assistance,trade money,consensus,RUS POL CZE SVK HUN ROU BGR CUB VNM MNG,,no,
| Column | Meaning |
|---|---|
key | Its short code (UN, UNSC, NATO, EU, IMF…). A new one: new: + a code of your own, letters and digits (no | or :). |
name | Its full name, shown in the Diplomacy window. |
kinds | What it does, a list of: defence and arms (members vote to deploy battlegroups and hold exercises), trade (lower tariffs between members), money (members vote on loans to a member in trouble), move (free movement of people), politics (votes to condemn, suspend and readmit). |
voting | How it decides: consensus or unanimity (one no blocks), majority, weighted (by the size of the economy), quotas (by money paid in), veto (a majority, but the vetoes members can block). |
members | 3-letter country codes (lists\countries.csv), the order kept. +SWE FIN adds members, -HUN takes one out, a plain list replaces them all. |
suspended | Members barred from its votes and benefits for now (they stay members). |
mutual | yes: an attack on one is an attack on all, as in NATO. Members become allies and join each other's defensive wars. |
vetoes | Members whose no blocks a vote: the Security Council's five permanent members. |
- The game's own keep their own rules. The UN needs two thirds; the Security Council's resolutions need 9 votes and no veto; the IMF lends to members deep in debt; OPEC+ cuts and raises oil output; NATO and the EU need every member. Your changes to members, name, kinds and voting still apply.
- A new organisation works by its columns. A
defenceorganisation withmutualyesis an alliance; atradeone lowers tariffs between members; amoneyone can vote loans. - Removing one removes what it does. Without the UN there are no General Assembly votes and no UN sanctions; without the Security Council no resolutions or UN peacekeepers; without the IMF no bailouts; without NATO no NATO battlegroups on the eastern flank.
- Trade agreements between countries (free trade deals) and defence treaties between two countries are not in this table.
The diplomacy of the start#
Besides the organisations, eight more files hold the world's diplomacy on day one. Each is the game's own list row by row (see diplomacy\ in the reference): change a row, remove it (key,remove), or add one (new:key,...). Codes are 3-letter country codes; a sender or treaty party may also be an organisation (EU, UN, NATO). List columns take +codes and -codes.
| File | Columns | Example |
|---|---|---|
trade-deals.csv | name, type (FTA, CU, EIA, PTA), parties, year | new:comecon-trade,COMECON trade,PTA,RUS POL CZE HUN,1949 |
sanctions.csv | sender, target, packages (travel arms tech finance energy full), since | EU.RUS,remove |
treaties.csv | party, with, kind (mutual, guarantee, umbrella), year, note | new:RUS.MNG.mutual,RUS,MNG,mutual,1966,Soviet troops in Mongolia |
disputes.csv | claimant, holder, area, provinces, severity (1 dormant .. 3 armed) | new:TCD.LBY-1,TCD,LBY,The Aouzou Strip,,3 |
recognition.csv | country, kind (recognised_by / not_recognised_by), countries | TWN,,,=KOR SAU ZAF PAN |
no-relations.csv | a, b, since (a year or never) | new:ISR.RUS,ISR,RUS,1967 |
bases.csv | user, host, name, lat, lon, kind (air naval army mixed radar), personnel | new:RUS.wunsdorf,RUS,DDR,Wünsdorf,52.17,13.47,mixed,380000 |
history.csv | a, b, value (-50..50), years it lasts, text | new:IRN.IRQ,IRN,IRQ,-35,40,The war since 1980 |
Warheads
military/warheads.csv: the nine nuclear states' stockpiles on day one: key,tactical,strategic,deployedTactical,deployedStrategic,perYear,cap,shownLow,shownHigh. The Soviet Union of 1985: RUS,=29000,=10200,=8000,=9900,=1500,=40000,=33000,=45000.
A start before 2024
A scenario with a year also trims what did not exist yet, by itself: weapons and drones of later years, named warships commissioned later, modern coastal missile batteries, SAMs and aircraft beyond the year's generation, space missions, launch sites, observatories and satellites of later years, and NATO's eastern battlegroups (2017). Wars in scenario/start.json take real dates of that year ("since": "1980-09-22"), and scripts see the shown year (date().year is 1986 in a 1985 world).
Flags and icons#
pictures/flags/POL.png: replaces a country's flag, named by its code. PNG or JPG, 3:2 (e.g. 300×200).pictures/icons/<key>.png: replaces an icon. Keys are inpictures\icons.txt(e.g.hud.economy.png). Draw it white on a transparent background, square, 128×128: the game colours it like its own icons.- At most 4096×4096. A mod with only pictures should be
"gameplay": false, so players can add it to old saves.
Fonts#
The game's fonts cover the letters of its own text. Letters they lack (a Cyrillic city name, a Chinese translation) come from a fallback font, looked for in this order: .ttf / .otf files in your mod's fonts/ folder, then the fonts Windows has (Arial for Latin, Greek and Cyrillic; Microsoft YaHei, Yu Gothic, Malgun Gothic for Chinese, Japanese, Korean; Nirmala and Leelawadee for Indian scripts and Thai). The map labels copy the letters they need into their own atlas. Ship a font only if Windows' fonts do not have your letters, and only fonts you may share.
Translations#
my-translation/
mod.json "gameplay": false, "tags": ["Translation"]
lang/de/language.json { "name": "Deutsch" }
lang/de/menu.csv
lang/de/events.csv (any number of .csv files)
# lang/de/menu.csv
source,text
New game,Neues Spiel
Load game,Spiel laden
"Mods apply from top to bottom. Any mod turns achievements off.","Mods gelten von oben nach unten. Jeder Mod schaltet Erfolge ab."
- The folder name is the language code you choose (
de,pl,pt-br);language.jsongives the name players pick in the Mods window. sourceis the English text exactly as the game has it, capitals and punctuation included;textis yours. Put a cell with a comma in double quotes.\nis a line break.- The full list of the game's texts:
lang\en\seen.csvin the reference (windows, buttons, hover explanations, events, policies). Copy it, fill the second column, and remove rows you do not translate. - Window captions show in capitals, but you translate them as written in the list.
- Players pick the language in the Mods window; it applies after Apply & restart.
- Texts the game builds with numbers inside ("12 provinces") stay English for now; everything fixed is translated.
Lua scripts: how they run#
- Every
.luafile in your mod'sscripts/folder runs once when the game starts (after the mods applied). Files run in name order; they share one set of global variables per mod. Each mod has its own: mods cannot see each other's variables. - That first run should only set things up: define functions and register handlers with
on(event, function). The game calls a handler when its event happens (a new month, a war…), never every frame. - Global variables keep their values between calls for the whole session. They are not saved: after loading a save, scripts start fresh. Work out what you need from the game (
country(),date()) rather than counting in variables when it must survive a load. - Scripts read the game and ask it to do things through the functions below; they cannot reach anything else (no files, no internet, no other programs).
-- scripts/rules.lua
local warned = false -- a variable of this file
on("new_month", function(e) -- runs every new month
local pl = country("POL") -- a table of Poland's figures
if pl.debtShare > 0.6 and not warned then
message("Poland's debt", string.format("Debt is %.0f%% of GDP.", pl.debtShare * 100))
warned = true
end
end)
The Lua language (what scripts can use)#
Primacy runs standard Lua syntax with the parts below. If you know any Lua (Roblox, WoW, Garry's Mod), this is the same.
Values
nil nothing
true, false
42, 3.5, 1e6 numbers (all decimal)
"text", 'text', [[long
text]]
{ 1, 2, 3 } a list (index from 1)
{ name = "POL", n = 5 } a table with fields
function(x) return x endVariables
local x = 1 -- this file / this block only
count = 0 -- global (this mod's scripts)
local a, b = 1, 2
t.name = "Poland" -- set a field
t["name"] -- read a field
#t -- length of a list or textOperators
+ - * / % ^ arithmetic (% remainder, ^ power)
.. join texts: "a" .. 1
== ~= < > <= >= compare (~= is "not equal")
and or not logic
x = x or 0 default valueControl
if a then ... elseif b then ... else ... end
while cond do ... end
repeat ... until cond
for i = 1, 10 do ... end -- 1..10
for i = 10, 1, -1 do ... end -- step
for i, v in ipairs(list) do ... end
for k, v in pairs(tbl) do ... end
break
return valueFunctions
local function add(a, b) return a + b end
local sq = function(x) return x * x end
local t = { n = 2 }
function t:twice(x) return self.n * x end
print(t:twice(21)) -- 42
-- a function can return several values:
local function mm(a, b) return a, b endLibraries
math.floor math.ceil math.abs math.sqrt
math.min math.max math.random math.huge math.pi
string.len string.upper string.lower
string.sub(s, i, j) string.rep(s, n)
string.find(s, part)
string.format("%s %d %.1f %%", "a", 5, 2.25)
table.insert(t, v) table.insert(t, pos, v)
table.remove(t) table.concat(t, ", ")
type(v) tostring(v) tonumber(s)
pairs ipairs next error assert printio, os, load, require, dofile, debug, coroutines, metatables, goto, varargs (...). Text methods work on literals and variables: ("x"):upper(), s:sub(1, 3).Game events#
Register a handler: on("new_year", function(e) ... end). The handler gets one argument, e, a table with the event's details (read them as e.year, e.attacker…). You may register several handlers for the same event; they run in the order registered.
| Event | When | Fields of e |
|---|---|---|
game_start | A new game reaches its first day (not a loaded save), after the start-date wars began and diplomacy is up. | none (e is nil) |
new_month | The first day of every month. | e.year, e.month (1..12) |
new_year | 1 January (right after new_month). | e.year, e.month (1) |
war_declared | Any war begins (also the start-date wars). | e.attacker, e.defender (country codes) |
peace | Any war ends. | e.a, e.b (country codes) |
new_day | Every day at midnight. | e.year, e.month, e.day |
city_captured | A city changes hands in a war. | e.city, e.from, e.to, e.capital |
province_captured | A province comes under the other side's control. | e.province (id), e.name, e.from, e.to |
ceasefire | A war freezes into a ceasefire. | e.a, e.b |
truce_broken | A ceasefire breaks and fighting resumes. | e.a, e.b, e.breaker |
surrendered | A country surrenders (its terms dictated, or the whole country taken). | e.winner, e.loser |
strike | An air, missile, nuclear or chemical strike lands (the first of each attacker, target and kind a day). | e.by, e.target, e.kind (air, missile, nuclear, chemical), e.lat, e.lon |
treaty_signed, treaty_ended | An agreement is signed, or ends. | e.kind (Alliance, FreeTrade, NonAggression...), e.a, e.b |
joined_org, left_org | A country joins or leaves an organisation. | e.org (key), e.name, e.country |
sanctions_changed | Sanctions between two countries change. | e.sender, e.target, e.packages (now in force; "" = none) |
vote_decided | A vote ends in the UN, the Security Council or a bloc. | e.org, e.motion, e.sponsor, e.target, e.passed |
state_visit | A state visit ends. | e.host, e.guest, e.outcome, e.headline |
government_changed | A country's government changes (election, coup, a victor's puppet). | e.country, e.by |
event_opened, event_decided | A Situation Room event opens, or is answered. | e.id, e.country, e.place; decided: e.choice (1, 2... or "none"), e.risked |
economy_alert | A crisis notice: recession, default, bank run... | e.country, e.kind, e.value |
weapon_developed | A weapon-tree step or an arms programme is finished. | e.country, e.weapon, e.step or e.programme |
arms_delivered | An arms order is delivered. | e.country, e.weapon, e.count |
space_news | A space launch, arrival, landing, discovery or loss. | e.country, e.kind, e.title, e.mission, e.body, e.good |
asteroid_impact | An asteroid or debris hits the Earth. | e.lat, e.lon, e.megatons, e.ocean |
Every function a script can call#
Countries are always named by their code (a text like "POL"); codes are in lists\countries.csv. A new country made by a mod uses the code you gave it.
country(code) → table or nil
The country's figures right now. Returns nil if there is no such country (check it: local c = country("XYZ") if not c then return end).
| Field | Meaning |
|---|---|
code, name, short, adjective | Its code, name now, short name, adjective (from a mod; empty if none). |
capital | Its capital's name. |
gdp, gdpPerHead | GDP in dollars; per person. |
population | People. |
unemployment, inflation, debtShare | Shares: 0.05 = 5%. |
provinces | How many provinces it holds. |
atWar | true if it is at war with anyone. |
player | true if it is the player's country. |
approval | The government's approval 0..100 (the player's country only; nil for others). |
local de = country("DEU")
print(de.name, de.gdp, de.unemployment * 100 .. "%")
countries() → list of codes
Every country that holds land now. Loop over it with ipairs:
for _, code in ipairs(countries()) do
local c = country(code)
if c.debtShare > 1.2 then log(c.name .. " owes more than its GDP") end
end
player() → code
The player's country code (nil before the player picked one).
date() → table
date().year, date().month, date().day: the game's date.
at_war(a, b) → true/false
Whether two countries are at war with each other: if at_war("RUS", "UKR") then ... end.
wars() → list
Every war: a list of tables { a = code, b = code }.
for _, w in ipairs(wars()) do log(w.a .. " fights " .. w.b) end
province(id) → table or nil
{ id, name, owner } of a province (ids in map\provinces.csv): province("UKR-1234").owner.
provinces(code) → list of ids
Every province a country holds now.
city(name [, code]) → table or nil
The first city with that name (in that country, if you give a code): { name, country, population, lat, lon, capital }. city("Gdansk", "POL").population.
fire_event(id, code) → true/false
Opens a Situation Room event for a country now: the game's (ids in events\ids.txt) or your own (give it "chainOnly": true if it should only ever come from your script). Returns false when it cannot happen there.
message(title [, text])
Shows a card to the player and keeps it in Messages, with your mod's name on it.
sign_policy(id, value) → true, false or a reason
The player's government signs a policy change (ids and ranges in policies\ids.txt). It waits and phases in like one the player signs. Returns a text saying why when it cannot be signed now ("The economy is not running yet.").
declare_war(a, b) → true/false
Country a declares war on country b, with diplomacy's normal rules and consequences.
make_peace(a, b) → true/false
Ends the war between a and b; each keeps the ground it holds. False when they are not at war.
add_sanction(sender, target, packages), lift_sanction(sender, target [, packages])
Imposes or lifts sanctions: packages are any of travel arms tech finance energy full ("arms tech"). Without packages, lift_sanction lifts them all.
sign_treaty(kind, a, b [, years]), end_treaty(kind, a, b) → true/false
Signs or ends an agreement: Alliance, FreeTrade, NonAggression, Guarantee, Basing, VisaFree, Research... (the treaty kinds of the Talks tab).
join_org(org, code), leave_org(org, code) → true/false
Takes a country into an organisation, or out of it at once (join_org("NATO", "POL")). Keys as in diplomacy/organisations.csv.
add_opinion(a, b, value, text [, years])
What a thinks of b: a grievance (below 0) or a friendship (-100..100), with its reason, fading over the years given (10 if none).
give_province(id, code) → true/false
Hands a province (ids: map\provinces.csv) to a country, with its people, cities and industry, like a ceded one.
launch_mission(code, kind, world [, name]) → true or the reason it cannot
Orders a space mission the way the game plans any: launch_mission("RUS", "Outpost", "earth", "Mir"). Kinds: Flyby, Orbiter, Entry, Lander, Rover, Aerial, Sample, CrewFlyby, Crewed, Outpost, Solar, Telescope, Impactor, Relay. Real missions with their own dates go in space/missions.csv (the game's list, row by row; a new row's dates are in the scenario's years).
log(text) and print(...)
Write a line to the mods log (Mods log in the Mods window; mods log in the console), with your mod's id in front. Use them to see what your script does.
on(event, function)
Registers a handler (see Game events). Unknown event names are an error.
Worked examples#
A yearly report on the richest countries
on("new_year", function(e)
local list = {}
for _, code in ipairs(countries()) do
table.insert(list, country(code))
end
-- find the top three by GDP (simple selection)
for n = 1, 3 do
local best, bestI = nil, 0
for i, c in ipairs(list) do
if not best or c.gdp > best.gdp then best, bestI = c, i end
end
if best then
log(string.format("%d. %s: $%.0f B", n, best.name, best.gdp / 1e9))
table.remove(list, bestI)
end
end
end)
Rally when the player is attacked
on("war_declared", function(e)
if e.defender == player() then
local me = country(player())
message("The nation rallies", me.name .. " is attacked by " .. country(e.attacker).name .. ".")
fire_event("pga.rally_round_the_flag", player())
end
end)
A crisis that grows with debt, once
local fired = {}
on("new_month", function(e)
for _, code in ipairs(countries()) do
local c = country(code)
if c.debtShare > 1.5 and not fired[code] then
fired[code] = true
fire_event("pga.debt_crisis", code)
end
end
end)
A war that starts in 2027
on("new_month", function(e)
if e.year == 2027 and e.month == 3 and not at_war("CHN", "TWN") then
declare_war("CHN", "TWN")
end
end)
Limits and errors#
- Each call (the first run, each handler call) may take up to 200,000 steps. A script that runs longer is stopped with "the script ran too long", and the game goes on. Loop over countries freely; avoid loops in loops over thousands of things.
- Calls may nest 120 deep (recursion). Deeper is stopped.
- An error stops only that call. It goes to the mods log with the file and line:
rules.lua:12: a number is needed, not nil. The handler stays registered and runs again next time. - Common errors: "cannot index nil 'c'" (a function returned nil: check it), "cannot call nil 'contry'" (a typo in a function name), "'end' expected to close 'if'" (a missing
end), "cannot compare nil with number" (a field that does not exist).
Publish on the Workshop#
- In the Mods window, find your local mod and click Publish (it says Update after the first time).
- Title (from mod.json's name), description, what changed in this version, and who can see it: Only me (test first), Friends, Link only, Everyone.
- Hold Publish. The upload shows its progress. The first time, Steam may ask you to accept the Workshop agreement on the item's page: until you do, the item stays hidden.
- The game writes
workshop.txtinto your folder; keep it so later uploads update the same page. - The tags come from mod.json's
tags(only the Workshop's tags are sent), and "Space DLC" when the mod lists the Space pack.
Players subscribe on Steam; the game shows the mod in their Mods window, switched off, after Steam downloads it.
Every error message, and what to do#
| Message | Means | Fix |
|---|---|---|
| mod.json line n: … | A JSON syntax error. | Check quotes, commas and brackets on that line. |
| "id" is missing / id … may only use a-z… | mod.json's id. | 3–64 small letters, digits, - or _. |
| unknown field "x" | A misspelt mod.json field. | Use the names in mod.json. |
| needs the mod "x" | A mod in needs is missing or off. | Subscribe and switch it on. |
| needs the space DLC | The player does not own the pack. | — |
| two mods use the id "x" | Two folders share an id. | Change one id. |
| line 1: the first column must be "key" | A table's key column is wrong. | Use the key column of that table. |
| … has no column "x" | A misspelt column. | Copy the header from the reference. |
| no key "x" in … | The row's key does not exist. | Copy the key from the reference; to add a city write new:. |
| "x" is not a number | A number cell with a comma or text. | Use a dot: 0.25. |
| text can only be set (=) | * or + on a text column. | Write the new text. |
| rows of … cannot be removed / does not take new rows | remove or new: where the table does not allow it. | Only cities take them. |
| a new city needs … | A new city lacks name, lat, lon or population. | Fill them. |
| this column is for information only / owners change in map/owners.csv | You changed an information column. | Leave it empty. |
| no table reads this file | A file name the game does not know. | Check the file name and folder. |
| cannot read the effect "x" | An effect word is wrong. | See Effect words. |
| weight: unknown value "x" | A formula name is wrong. | See Chance formulas. |
| no lever "x" | A policy lever name is wrong. | See lists\names.txt. |
| no country "x" | A code that does not exist. | See lists\countries.csv; new countries go in map/countries.csv first. |
| no province "x" | A province id that does not exist. | See map\provinces.csv. |
| flag "x": no such file in the mod | The flag path is wrong. | Paths start at your mod folder: flags/SOV.png. |
| conflict … set by a, b; b wins | Two mods set the same value (not an error). | Order them, or use loadAfter. |
Rules and limits#
- Changing the mod list or the language restarts the game's session.
- Any mod switches achievements off.
- Mods cannot unlock DLC.
- Nothing changes the world at once mid-game: mod policies phase in; scripts act through the game's own systems (events, policies, diplomacy).
- No code: .dll files are never loaded. Lua does that job.
- Not moddable: province shapes, rivers, coasts, terrain, the satellite picture and adding new tech nodes. The tutorial only starts with no mods running (it teaches the game as it ships).
- Do not publish other people's work (art, fonts, music) without their permission, or real-world hate content.
Questions#
My mod does nothing.
Is it switched on and did you Apply & restart? Open the Mods window: does its row say Running and "N values changed"? If 0 values: open the mods log, every skipped line is there with its reason.
Excel changed my file.
Excel may turn codes into dates or use semicolons. Save as "CSV UTF-8" and check the file in Notepad. Or edit CSVs in a text editor.
Where do I see what a value is now?
Reference files writes every table as the running game has it, your mods included. Compare with the shipped reference (unmodded).
Can my mod change another mod?
Yes: list it in loadAfter (so yours applies after) and change the same rows; needs if yours cannot work without it.
How do I test quickly?
Edit your files, then type modsreload in the console: the session restarts with the files as they are now.
Can a script remember something across saves?
No: scripts start fresh after a load. Work it out from the game's state (the date, a country's figures) each time.
Does my mod slow the game?
Tables apply once at start (a city mod adds about half a second). Scripts run only on their events, with a step limit.
Wishlist on Steam
