CSV and JSON Reference
CSV and JSON Reference is the complete technical schema for every data file Imperium Sine Fine reads — every column of every CSV, and every field of every JSON structure. See Desktop Modding Manual for the conceptual side of modding: folder structure, override rules, and how to actually get a mod running.
All CSV files use a semicolon (;) as the column separator. Lines beginning with // or ; are treated as comments and ignored. The row ID is always in column 0, and is always an integer.
CSV File Reference
data_troops.csv
Defines all troop types. Each row is one troop. Columns 0–20.
| Col | Name | Type | Description |
|---|---|---|---|
| 0 | Troop set | int | Faction troop group (0 = default, used when no faction-specific set exists). |
| 1 | Name | string | Display name. |
| 2 | Troop id | int | Row key — the troop's position (1–8) within its set. |
| 3 | Melee combat | float | Base Melee Value (MV). |
| 4 | Ranged combat | float | Base Ranged Value (RV). |
| 5 | Armour | float | Base Armour Value (AV). |
| 6 | Shock value | float | Base Shock Value (SV). |
| 7 | Resolve value | float | Base Resolve Value (RSV). |
| 8 | Hit points | int | Base hit points. |
| 9 | Training | int | Training value used in the Battle Honours XP formula, not recruitment cost. |
| 10 | Movement | int | Base movement points per cycle. |
| 11 | Sight | int | Vision radius in hexes. |
| 12 | Description | string | In-game flavour text. |
| 13 | Manpower | float | Manpower cost to recruit one troop. |
| 14 | Melee weapons | float | Melee weapon equipment cost. |
| 15 | Ranged weapons | float | Ranged weapon equipment cost. |
| 16 | Armour equipment | float | Armour equipment cost. |
| 17 | Horses | float | Horse resource cost. |
| 18 | Support equipment | float | Support equipment cost. |
| 19 | Grain consumption | float | Grain consumed per cycle. |
| 20 | Abilities | string | Comma-separated ability tags (e.g. Footmen, Cavalry, Aquaphobia). |
See Troop Types for the generic (Troop Set 0) roster in full, and any individual faction page for its own troop set.
data_factions.csv
Defines the 14 major factions. Columns 0–23.
| Col | Name | Type | Description |
|---|---|---|---|
| 0 | Faction id | int | Row key. |
| 1 | Race id | int | The race this faction belongs to. |
| 2 | Colour id | int | Map colour. |
| 3 | Troop set | int | Which data_troops.csv troop set this faction uses. |
| 4 | Focus set | string | Focus tree filename (without .json), or GENERIC_FOCUS_TREE_ID. |
| 5 | Name | string | Display name. |
| 6 | Threat distance | int | AI: hex range for detecting enemy threats. |
| 7 | Exercise distance | int | AI: hex range for army training decisions. |
| 8 | Patrol distance | int | AI: hex range for patrol army response. |
| 9 | Main troop type | int | Preferred troop type id for Army Composition Bonus. |
| 10 | Main troop percentage | float | Target fraction (0.0–1.0) of that troop type for maximum ACB. |
| 11 | Favourite terrain type | int | AI: preferred terrain id for expansion. |
| 12 | Favourite tech 1 | int | AI: first technology priority id. |
| 13 | Research modifier | float | Not used (legacy column). Set to 0. |
| 14 | Manpower modifier | float | Modifier to manpower collection. |
| 15 | Horses modifier | float | Modifier to horse collection — see Resources. |
| 16 | Organisation modifier | float | Modifier to army organisation. |
| 17 | Exercise modifier | float | Modifier to exercise effectiveness. |
| 18 | New farm distance | int | AI: maximum hex distance for establishing new farms. |
| 19–21 | Personality 1–3 | int | Three personality ids defining AI behaviour. |
| 22 | Favourite strategy | int | AI: preferred combat strategy id. |
| 23 | Reinforce | int | AI: whether the faction actively reinforces armies (1 = yes, 0 = no). |
data_terrain.csv
Defines every terrain type. Columns 0–14.
| Col | Name | Type | Description |
|---|---|---|---|
| 0 | id | int | Row key. |
| 1 | name | string | Display name. |
| 2 | popCap | int | Maximum population a single hex of this terrain can support. |
| 3 | horses | int | Horses produced per hex per cycle. |
| 4 | iron | int | Iron produced per hex per cycle. |
| 5 | leather | int | Leather produced per hex per cycle. |
| 6 | wood | int | Wood produced per hex per cycle. |
| 7 | stone | int | Stone produced per hex per cycle. |
| 8 | grain | int | Grain produced per hex per cycle. |
| 9 | moveTimeIn | int | Cycles for an army to enter this hex. −1 = impassable. |
| 10 | moveTimeOut | int | Cycles for an army to exit this hex. −1 = impassable. |
| 11 | defensive bonus | float | Combat defensive multiplier for a defending army (1.00 = no bonus). |
| 12 | terrainValue | int | Strategic value used in AI calculations. |
| 13 | supportWear | float | Equipment wear rate for an army stationary on this terrain. |
| 14 | Description | string | In-game flavour text. |
See Terrain for the full player-facing reference table built from this file.
data_technologies.csv
Defines all researchable technologies. Columns 0–4.
| Col | Name | Type | Description |
|---|---|---|---|
| 0 | id | int | Row key. |
| 1 | race | int | Race that can research this technology (0 = all races). |
| 2 | name | string | Display name. |
| 3 | cost | int | Research cost in cycles. |
| 4 | description | string | In-game description shown in the Research panel. |
data_equipment.csv
Defines the four manufactured equipment types. Columns 0–8.
| Col | Name | Type | Description |
|---|---|---|---|
| 0 | id | int | Row key. |
| 1 | name | string | Display name. |
| 2 | manpower | int | Manpower cost per unit produced. |
| 3 | horses | int | Horses cost. |
| 4 | iron | int | Iron cost. |
| 5 | leather | int | Leather cost. |
| 6 | wood | int | Wood cost. |
| 7 | workshopCapacity | int | Workshop slots consumed per unit produced. |
| 8 | description | string | In-game description. |
data_projects.csv
Defines all army and settlement projects. Columns 0–8.
| Col | Name | Type | Description |
|---|---|---|---|
| 0 | id | int | Row key. |
| 1 | name | string | Display name. |
| 2 | type | int | Project category: 1 = settlement project, 2 = army project. |
| 3 | infantry demand | int | Manpower required to start the project. |
| 4 | project time | int | Base time in cycles to complete. |
| 5 | distance limit | int | Minimum hex distance from the nearest settlement or outpost (0 = no restriction). |
| 6 | tech modifier | float | Technology modifier applied to completion time. |
| 7 | equipment wear | float | Equipment wear rate during the project. |
| 8 | stone cost | int | Stone resource cost to initiate the project. |
See Projects for the full player-facing reference built from this file.
data_skills.csv
Defines leader skills for Generals and Governors. Columns 0–7.
| Col | Name | Type | Description |
|---|---|---|---|
| 0 | id | int | Row key. |
| 1 | leaderType | int | Leader type this skill applies to: 2 = Governor, 3 = General. |
| 2 | name | string | Display name. |
| 3–7 | level1–level5 | float | Effect value at each of the five skill levels. |
See Leader Skills for the full player-facing reference.
data_strategies.csv
Defines the combat strategies available to armies. Columns 0–23.
| Col | Name | Type | Description |
|---|---|---|---|
| 0 | Strategy id | int | Row key. |
| 1 | Name | string | Display name. |
| 2–12 | Combat modifiers | float | Multipliers against each opposing strategy, in fixed order: Garrison, Rush Enemy, Shield Wall, Phalanx, Archer Fire, Feigned Retreat, Flanking Manoeuvres, Cavalry Charge, Oblique Order, Siege Warfare, Skirmish. |
| 13 | xpLevel | int | Minimum General XP level required. |
| 14 | tech2 | int | Minimum level of Archery (technology 2) required. |
| 15 | tech5 | int | Minimum level of Field Manoeuvres (technology 5) required. |
| 16 | tech12 | int | Minimum level of Marching (technology 12) required. |
| 17 | tech13 | int | Minimum level of Military Academy (technology 13) required. |
| 18 | troop4Percentage | float | Minimum fraction of troop type 4 (Marksmen) required. |
| 19 | cavalryPercentage | float | Minimum fraction of cavalry required. |
See Combat Strategies for the full player-facing reference.
data_province_development.csv
Defines the ten Province Development Levels. Columns 0–17.
| Col | Name | Type | Description |
|---|---|---|---|
| 0 | Development id | int | Row key. |
| 1 | Name | string | Display name. |
| 2 | development level | int | The PDL number (1–10) this row represents. |
| 3 | workshopMaximum | int | Maximum workshops a settlement at this level can build. |
| 4 | basePopulationCap | int | Base population cap contribution (flat). |
| 5 | baseCapitalCap | int | Base capital population cap contribution (flat). |
| 6 | capTerrainModifier | float | Terrain Modifier applied to a hex's population cap contribution — see Province Development. |
| 7 | upgradePopulation | int | Population required to upgrade to the next level. −1 = no further upgrade possible (Imperial Hub). |
| 8 | upgradeHexes | int | Controlled hexes required to unlock the next upgrade. |
| 9 | upgradeCycles | int | Time in cycles required to complete the upgrade. |
| 10 | upgradeStoneCost | int | Stone cost for the upgrade. |
| 11 | horsesModifier | float | Resource modifier for horse production. |
| 12 | resourcesModifier | float | General resource production modifier. |
| 13 | expansionProjectTime | float | Multiplier on expansion project time. |
| 14 | settlementProjectTime | float | Multiplier on settlement project time. |
| 15 | armyProjectTime | float | Multiplier on army project time. |
| 16 | movementCostModifier | float | Multiplier on army movement cost through this hex. |
| 17 | workshopCapacity | float | Workshop capacity modifier. |
See Province Development for the full player-facing reference built from this file.
data_names.csv
Defines name pools for settlements, armies, and leaders, per faction. Columns 0–2.
| Col | Name | Type | Description |
|---|---|---|---|
| 0 | Faction id | int | Faction the name belongs to — matches data_factions.csv column 0. |
| 1 | Type | int | Name category: 1 = settlement, 2 = army, 3 = leader. |
| 2 | Name | string | The name string itself. |
Mods load names additively on top of vanilla — there's no override mechanism here, new names are simply added to the pool.
data_personality.csv
Defines AI personality profiles, referenced by data_factions.csv columns 19–21. Columns 0–10.
| Col | Name | Type | Description |
|---|---|---|---|
| 0 | Personality id | int | Row key. |
| 1 | Name | string | Display name. |
| 2 | scoreWarLimit | int | AI decision threshold. |
| 3 | relationRaceModifier | float | Multiplier applied to the Race Modifier described on Relation Values. |
| 4 | relationNegativeModifier | float | Multiplier applied to negative relation modifiers. |
| 5 | relationNegativeNorm | float | Baseline adjustment for negative relations. |
| 6 | acceptNeutral | int | AI decision threshold for accepting Neutral-status requests. |
| 7 | acceptTreaty | int | AI decision threshold for accepting treaty upgrade requests. |
| 8 | relationCalmBorder | int | Modifier to the Calm Border mechanic described on Relation Values. |
| 9 | relationBorderTension | int | Modifier to the Border Tension mechanic described on Relation Values. |
| 10 | militaryStyle | int | AI decision category affecting army composition and strategy choices. |
Event JSON Reference
Event files are JSON arrays — each element is one event object. Place files in the events\ folder.
Event Object Fields
| Field | Type | Required | Description |
|---|---|---|---|
| id | int | Yes | Unique event ID. Vanilla uses 100001–199999 (general), 1001–1999 (army), 2001–2999 (settlement). Mods should use IDs above 300,000. |
| title | string | Yes | Short title shown in the event popup. |
| description | string | Yes | Narrative text of the event. |
| targetType | string | Yes | Who the event targets: "EMPIRE", "ARMY", or "SETTLEMENT". |
| triggerConditions | array | No | Conditions that must all be true for the event to fire. Empty = always eligible. |
| triggerChance | float | Yes | Probability the event fires when all conditions are met. |
| triggerCooldownCycles | int | Yes | Minimum cycles between firings of this event. |
| tags | string array | No | Descriptive tags for filtering — common values include MINOR, MAJOR, GOOD_EVENT, BAD_EVENT, ECONOMY, LEADER, CRIME, IMPERIAL_COURT. |
| options | array | Yes | The player choice(s) presented. |
Condition Object
{ "type": "CONDITION_TYPE", "targetId": 0, "value": 0 }
| Type | targetId | value | Description |
|---|---|---|---|
| ARMY_HAS_GENERAL | — | — | Army has an undamaged general assigned. |
| ARMY_IS_COMBAT | — | — | Army is currently in combat. |
| EMPIRE_AT_WAR | — | — | Empire is at war with at least one other major faction. |
| EMPIRE_HAS_ADVISOR | — | — | Empire has at least one advisor in the Imperial Court. |
| EMPIRE_HAS_EMPEROR | — | — | Empire has an emperor in the Imperial Court. |
| EMPIRE_HAS_HEIR | — | — | Empire has an heir in the Imperial Court. |
| EMPIRE_HAS_REBELLION | — | — | Empire is a major faction with at least one settlement controlled by its connected rebel faction. |
| EMPIRE_MAX_LOYALTY | — | loyalty | Combined empire loyalty is at most this value. |
| EMPIRE_MIN_LOYALTY | — | loyalty | Combined empire loyalty is at least this value. |
| EMPIRE_NO_HEIR | — | — | Empire has no heir in the Imperial Court. |
| EMPIRE_NOT_AT_WAR | — | — | Empire is at peace with every other major faction. |
| EVENT_HAS_FIRED | — | event id | The specified event has already triggered. |
| EVENT_NOT_FIRED | — | event id | The specified event has not yet triggered. |
| GENERAL_HAS_TRAIT | trait id | — | Army's general has this trait. |
| GENERAL_WITHOUT_TRAIT | trait id | — | Army's general does not have this trait. |
| GOVERNOR_HAS_TRAIT | trait id | — | Settlement's governor has this trait. |
| GOVERNOR_WITHOUT_TRAIT | trait id | — | Settlement's governor does not have this trait. |
| IS_EMPIRE_ID | faction id | — | Fires only for this faction. |
| IS_NOT_EMPIRE_ID | faction id | — | Does not fire for this faction. |
| IS_MIN_CYCLE | — | min cycle | Total cycles played must exceed this value. |
| IS_MONTH | — | month (1–12) | Only fires in this calendar month. |
| IS_NOT_MONTH | — | month (1–12) | Does not fire in this month. |
| IS_PLAYER_EMPIRE | — | — | Fires only for the human player's empire. |
| IS_SUMMER | — | — | True for months 5–9. |
| IS_WINTER | — | — | True for months 11, 12, 1, 2, 3. |
| SETTLEMENT_HAS_GOVERNOR | — | — | Settlement has an undamaged governor assigned. |
| SETTLEMENT_IS_CAPITAL | — | — | Settlement is the empire's capital. |
| SETTLEMENT_IS_COMBAT | — | — | Settlement is currently under siege. |
| SETTLEMENT_LESS_WORKSHOPS | — | count | Settlement has fewer than this many workshops. |
| SETTLEMENT_MAX_LOYALTY | — | loyalty | Settlement loyalty is at most this value. |
| SETTLEMENT_MIN_LOYALTY | — | loyalty | Settlement loyalty is at least this value. |
| SETTLEMENT_MORE_WORKSHOPS | — | count | Settlement has more than this many workshops. |
| SETTLEMENT_NEED_GARRISON | — | — | Settlement garrison is below its wanted amount. |
| SETTLEMENT_NOT_CAPITAL | — | — | Settlement is not the empire's capital. |
| SETTLEMENT_WORKSHOP_POSSIBLE | — | count | Settlement's current workshops plus this value must still be at or below the maximum allowed. |
Option Object
{
"text": "Description of what happens.",
"optionConditions": [ ... ],
"effects": [ ... ],
"nextEventId": -1
}
| Field | Type | Required | Description |
|---|---|---|---|
| text | string | Yes | Button text shown to the player. |
| optionConditions | array | No | Conditions (same vocabulary as the Condition Object above) that must all pass for this option to be offered. Lets a single event present different choices to different empires — a poor empire might see an option a wealthy one never does. |
| effects | array | Yes | Effects applied when this option is chosen. |
| nextEventId | int | No | Event ID to fire immediately after this option resolves, chaining two events into one continuous story beat. Omit or use −1 for none. |
Effect Object
{
"type": "EFFECT_TYPE",
"targetId": 0,
"value": 0,
"stringData": "",
"excludeCapital": false,
"filter": "",
"fallback": "",
"race": null,
"raiseWanted": null,
"strength": 0
}
Many effect names follow a consistent pattern worth knowing up front: SETTLEMENT_* effects act on a single settlement (either one chosen at random, or a specific one via the NAMED_* family), while EMPIRE_* effects act across every settlement the empire owns at once. Scope also determines how a value is read:
| Prefix | Scope | Value Type |
|---|---|---|
| EMPIRE_* | Every settlement the empire owns | Percentages |
| CAPITAL_* | The capital only | Percentages |
| SETTLEMENT_* | One randomly chosen settlement (respects filter/excludeCapital) | Flat values only |
| NAMED_* | One specific settlement by targetId (fallback required) | Either, depending on the effect |
Optional fields — apply to the scope families above, ignored by effects that don't use them:
| Field | Type | Description |
|---|---|---|
| excludeCapital | bool | EMPIRE_* and SETTLEMENT_* scope: skip the capital. Default false. |
| filter | string | Restricts which settlements qualify: coastal, has-harbour, non-core, forest-adjacent, open-terrain, rough-terrain, borders-enemy, or race:{id}. Also doubles as the ALL/NAMED scope selector for ARMY_ADD_TRAINING and LEADER_GRANT_XP, and as the city selector for CREATE_DIPLOMATIC_REQUEST. An unrecognised filter matches everything rather than nothing. |
| fallback | string | Required on every NAMED_* effect. Uses the same filter vocabulary above, applied if the named settlement doesn't exist in the current world — e.g. a random map, or a scenario that omitted it. A NAMED_* effect must degrade gracefully rather than silently doing nothing. |
| race | int | Optional race filter for population and soul effects. Omit for all races. |
| raiseWanted | bool | *_CHANGE_GARRISON only. Also raises the wanted garrison by the same amount so new troops don't drain away again. Omit or true to raise; explicit false opts out. |
| strength | int | *_SPAWN_ARMY/*_SPAWN_FLEET only. Troop-count multiplier over the baseline raised unit. 0 or 1 = baseline, 3 = triple. The baseline raised army is deliberately weak, so a spawn intended as a genuine reward usually wants 2–3. |
Empire — Leaders and Court
| Type | targetId | value | Description |
|---|---|---|---|
| EMPIRE_NEW_HEIR | — | — | Generates a new heir. Requires the empire to currently have none. |
| EMPEROR_DAMAGE / EMPEROR_SICKNESS | — | cycles | Emperor incapacitated / sick. |
| HEIR_DAMAGE / HEIR_SICKNESS | — | cycles | Heir incapacitated / sick. |
| GENERAL_DAMAGE / GENERAL_SICKNESS | — | cycles | General incapacitated / sick. |
| GENERAL_DEATH | — | — | General dies; a replacement is appointed automatically. |
| GENERAL_CHANGE_XP | — | ±xp | Changes the general's XP (never below 0). |
| GOVERNOR_DAMAGE / GOVERNOR_SICKNESS | — | cycles | Governor incapacitated / sick. |
| GOVERNOR_DEATH | — | — | Governor dies; a replacement is appointed automatically. |
| GOVERNOR_CHANGE_XP | — | ±xp | Changes the governor's XP (never below 0). |
| LEADER_GRANT_XP | army id when filter is NAMED | xp | Grants flat XP. StringData: ARMY, FLEET, or GOVERNOR. filter: ALL (default) or NAMED. |
Empire — Stock, Research, and Loyalty
| Type | stringData | value | Description |
|---|---|---|---|
| EMPIRE_CHANGE_EQUIPMENT | GES type | ±amount | Credits the Global Equipment Stock immediately. Despite the name, this covers everything the GES holds — SUPPORT_EQUIPMENT, ARMOUR, MELEE_WEAPONS, RANGED_WEAPONS, and MANPOWER. This is the right effect for most equipment grants — see Resource Grants below. |
| EMPIRE_CHANGE_EQUIPMENTS | — | ±amount | Same as above, applied to every GES type at once. |
| EMPIRE_CHANGE_LOYALTY | — | ±loyalty | Empire-wide loyalty. |
| GRANT_IMI | — | ±amount | Imperial Influence, clamped to IMI_CAPACITY. Overflow is discarded. |
| ADVANCE_RESEARCH | — | percent or −cycles | Positive value = percent of remaining research completed; negative value = that many cycles advanced. Floored at 1 cycle remaining — never completes research outright. |
Empire / Capital / Settlement Scope
| Type | stringData | value | Description |
|---|---|---|---|
| EMPIRE_CHANGE_POPULATION | — | ±percent | Population change in every settlement. race is optional. |
| EMPIRE_ADD_POPULATION | — | ±amount | Flat population added to every settlement. race is optional. |
| EMPIRE_CHANGE_RESOURCE | resource | ±amount | Stone, Horses, or Grain only — see Resource Grants below. |
| EMPIRE_CHANGE_GARRISON | — | ±amount | Garrison change across every settlement. raiseWanted defaults to true. |
| EMPIRE_ADVANCE_PROJECT | category | cycles | category: SETTLEMENT, EXPANSION, or ARMY. Floored at 1 cycle remaining. |
| CAPITAL_CHANGE_POPULATION | — | ±percent | Capital population. |
| CAPITAL_CHANGE_RESOURCE | resource | ±amount | Stone, Horses, or Grain only. |
| CAPITAL_CHANGE_GARRISON | — | ±amount | Capital garrison. |
| CAPITAL_INCREASE_DEV | — | levels | Raises capital development level. |
| CAPITAL_SPAWN_ARMY | — | training level | Spawns an army at the capital. strength multiplies troop count. |
| SETTLEMENT_ADD_POPULATION | — | amount | Flat population in one random settlement. |
| SETTLEMENT_CHANGE_POPULATION | — | ±percent | One random settlement. (CHANGE_SETTLEMENT_POPULATION is kept as a deprecated loading alias.) |
| SETTLEMENT_SPAWN_ARMY | — | training level | Army in one random settlement. strength multiplies troop count. |
| SETTLEMENT_SPAWN_FLEET | — | count | Fleet in one random settlement. strength multiplies crews. Always raises a War Galley hull — hullType isn't honoured. |
| SETTLEMENT_ADVANCE_PROJECT | category | cycles | One random settlement. |
| SETTLEMENT_ADD_WORKSHOP / SETTLEMENT_REMOVE_WORKSHOP | — | count | Adjusts workshop count (settlement already in scope). |
| SETTLEMENT_CHANGE_GARRISON | — | ±troops | Garrison (settlement already in scope). If the result exceeds the wanted garrison, that target increases in steps of 50, unless raiseWanted is set to false. |
| SETTLEMENT_CHANGE_LOYALTY | — | ±loyalty | Loyalty (settlement already in scope). |
| SETTLEMENT_CHANGE_RESOURCE | resource | ±amount | One resource in the settlement. StringData: MANPOWER, HORSES, IRON, LEATHER, or WOOD. |
| SETTLEMENT_CHANGE_RESOURCES | — | ±amount | All resource types at once, in this one settlement — despite the similarly-named EMPIRE_CHANGE_RESOURCE, this does not apply empire-wide. |
| SETTLEMENT_REBELLION | — | minimum garrison | See below for the full description of this effect. |
| SETTLEMENT_RESET_COOLDOWN | — | event id | Resets every settlement in the empire's cooldown for the specified event. |
| SETTLEMENT_ADD_SOULS | — | amount | Adds souls to an Ende settlement — see Population and Special Races. filter sets scope. |
SETTLEMENT_REBELLION in full: the settlement leaves the empire and joins its corresponding rebel faction. A disloyal or corrupt governor stays in power; otherwise a new one is assigned. Garrison is topped up to the effect value if below it. Every remaining city in the empire loses 6 loyalty. If the rebel faction previously held no assets, it inherits the empire's technology, its diplomacy resets toward every major faction (and theirs toward it), and it receives starting resources: 250 Manpower, 100 Horses, 500 Support Equipment, 250 Armour, 250 Melee Weapons, 100 Ranged Weapons.
Named Settlement (targetId = settlement id, fallback required)
| Type | value | Description |
|---|---|---|
| NAMED_SETTLEMENT_CHANGE_POPULATION | ±percent | Population. |
| NAMED_SETTLEMENT_CHANGE_RESOURCE | ±amount | stringData = resource. |
| NAMED_SETTLEMENT_CHANGE_GARRISON | ±amount | Garrison. raiseWanted defaults to true. |
| NAMED_SETTLEMENT_CHANGE_LOYALTY | ±loyalty | Loyalty. |
| NAMED_SETTLEMENT_CHANGE_DEVELOPMENT | ±levels | Development level. |
| NAMED_SETTLEMENT_ADVANCE_PROJECT | cycles | stringData = category. |
| NAMED_SPAWN_ARMY | training level | strength multiplies troop count. |
| NAMED_SPAWN_FLEET | — | strength multiplies crews. |
| NAMED_GRANT_OUTPOST | outpost id | Grants an outpost. |
Army
| Type | targetId | value | Description |
|---|---|---|---|
| ARMY_CHANGE_TROOPS | troop type id | ±count | Adds or removes troops (never below 0). StringData: INFANTRY, HEAVY_INFANTRY, MARKSMEN, CAVALRY, HEAVY_CAVALRY, or SIEGE_ENGINE. |
| ARMY_ADD_TRAINING | army id when filter is NAMED | levels | Adds training toward the equivalent XP threshold for the named tier — see Leader Age and Experience. filter: ALL (default) or NAMED. Clamped to maximum training; overflow is discarded, not banked. |
Diplomatic
| Type | targetId | value | Description |
|---|---|---|---|
| CHANGE_OPINION_EMPIRE | target empire id | ±opinion | Changes diplomatic opinion toward another empire. Requires the two empires to have translated each other's language — see Languages and Translation. Omitting targetId applies to every empire the actor has met. |
| CHANGE_STANDING_RELATION | other empire id | ±steps | Moves standing one or more rungs along the Hatred/Disliked/Trustworthy ladder — see Standing Relations below. StringData sets direction: TOWARD_TARGET (default) changes your own standing toward them; FROM_TARGET changes their standing toward you. |
| DECLARE_WAR | target empire id | — | Declares war on the player's behalf. Allies of the target join automatically. |
| WARN_EMPIRE | target empire id | — | Sends a warning report to the target. stringData is an i18n key resolved through the string table; when absent, falls back to the firing event's own title and description. |
| UNLOCK_DIPLOMATIC_ACTION | — | action id | Permanently unlocks a diplomatic action for the empire. Currently meaningful for action 13 (Demand Return of Cities) and 14 (Demand Reparation) — both locked by default. |
| CREATE_DIPLOMATIC_REQUEST | target empire id | — | Sends a demand directly — no diplomatic slot, no Imperial Influence cost, no research pause. stringData: demand-return-of-cities or demand-reparation. filter: optional city selector (most-isolated, highest-heritage-population, or lowest-loyalty — the default). See the Focus Tree JSON Reference section for how this is used from a focus. |
Spirits and Misc
| Type | stringData | Description |
|---|---|---|
| GRANT_SPIRIT | spirit id | Grants an empire spirit. |
| REMOVE_SPIRIT | spirit id | Removes an empire spirit. |
| DISABLE_TUTORIAL_EVENTS | — | Turns off remaining tutorial events. |
RACE_SET_TERRAIN_LOYALTY (stringData "race:condition") is reserved but not implemented — it's blocked on a planned refactor of the racial terrain-loyalty table. Use the keyed spirit modifier RACE_TERRAIN_LOYALTY_ instead, covered further down this page.
Resource Grants and the Monthly Cycle
Settlements collect resources through the month up to the production phase, where Iron, Leather, and Wood are zeroed out, and Stone, Horses, and produced equipment ship to the Global Equipment Stock. This matters for how a grant effect actually pays out:
- EMPIRE_CHANGE_EQUIPMENT credits the GES immediately — use it for anything the player should be able to spend right away, including Manpower, despite the effect's name.
- *_CHANGE_RESOURCE effects are restricted to Stone, Horses, and Grain specifically, because these are the only three resources that survive the monthly sweep. A grant of Iron, Leather, or Wood is worth whatever gets consumed before month end and nothing after — the same effect would pay out very differently depending on which day of the month it happened to fire.
- Settlement-level grants of Stone, Grain, and Horses are safe but delayed — they land locally right away, and reach the central stock at month end.
Standing Relations (the Hatred/Trustworthy Ladder)
Most diplomatic relation values, covered on Relation Values, are reactions to a specific event and decay back toward zero over time. Standing Relations work differently — Hatred, Disliked, and Trustworthy express a settled opinion rather than a recent grievance, and are mutually exclusive: an empire occupies exactly one rung of a four-step ladder toward any other empire.
| Rung | Relation Value | Meaning |
|---|---|---|
| Hatred | −225 | Permanent enmity |
| Disliked | −75 | Standing distrust |
| (neutral) | 0 | No standing opinion — none of the three is set |
| Trustworthy | +75 | Proven reliable |
CHANGE_STANDING_RELATION moves along this ladder rather than setting a value directly. A step past either end simply does nothing. Because the rungs are unevenly spaced, climbing out of a hole is worth considerably more than gilding an existing friendship: Hatred to Disliked is a +150 swing, Disliked to neutral +75, neutral to Trustworthy +75.
Direction matters. stringData selects which side of the relationship changes:
- TOWARD_TARGET (the default) changes your own empire's standing toward the target — and the AI reads this directly for its own diplomatic vetoes. An AI empire will not offer peace, open borders, friendship, or an alliance to anyone it holds at Disliked or Hatred, regardless of what the overall relation total happens to say.
- FROM_TARGET changes the target's standing toward you instead — reputation rather than policy. This is what the Return Cities diplomatic action uses.
Two vanilla diplomatic actions step this ladder up by one: Return Cities, and accepting a Demand Return of Cities or Demand Reparation request. Standing can also step down automatically, without any scripted effect at all — see Relation Values for how the base game's own conquest-reaction system does this.
Complete Event Examples
Simple Example
A minor event with a single condition and a single effect:
[
{
"id": 300001,
"title": "A Legendary Sword",
"description": "A blacksmith has forged a blade of extraordinary quality...",
"targetType": "ARMY",
"triggerConditions": [
{ "type": "ARMY_HAS_GENERAL" },
{ "type": "IS_SUMMER" },
{ "type": "EVENT_NOT_FIRED", "value": 300001 }
],
"triggerChance": 1.0,
"triggerCooldownCycles": 960,
"tags": ["MINOR", "LEADER", "GOOD_EVENT"],
"options": [
{
"text": "Present it to the general.",
"effects": [
{ "type": "GENERAL_CHANGE_XP", "value": 50 }
]
},
{
"text": "Sell it and equip the levies instead.",
"optionConditions": [
{ "type": "EMPIRE_MAX_LOYALTY", "value": 50 }
],
"effects": [
{ "type": "EMPIRE_CHANGE_EQUIPMENT", "stringData": "MELEE_WEAPONS", "value": 40 }
]
}
]
}
]
The second option only appears when empire loyalty is at or below 50 — a realm doing well never sees the choice to sell an heirloom. This demonstrates optionConditions: the same event can offer genuinely different choices depending on the empire's own circumstances.
Complex Example
A major empire-wide event demonstrating several fields together: filter restricting an effect to specific settlements, excludeCapital protecting the capital from a penalty, fallback covering a NAMED_* effect when the target doesn't exist in a given scenario, and strength scaling a spawned army beyond its baseline size:
[
{
"id": 300050,
"title": "The Coastal Uprising",
"description": "Discontent has been building in your coastal territories for months. A fleet of raiders has landed, and your harbour towns are in open revolt...",
"targetType": "EMPIRE",
"triggerConditions": [
{ "type": "EMPIRE_MAX_LOYALTY", "value": 30 },
{ "type": "IS_MIN_CYCLE", "value": 2000 },
{ "type": "EVENT_NOT_FIRED", "value": 300050 }
],
"triggerChance": 0.15,
"triggerCooldownCycles": 2000,
"tags": ["MAJOR", "BAD_EVENT", "CRIME"],
"options": [
{
"text": "Crack down hard. Reinforce every coastal settlement and make an example of the ringleaders at the capital.",
"effects": [
{
"type": "EMPIRE_CHANGE_GARRISON",
"value": 150,
"filter": "coastal",
"excludeCapital": true,
"raiseWanted": true
},
{
"type": "NAMED_SPAWN_ARMY",
"targetId": 4001,
"fallback": "coastal",
"strength": 3
},
{
"type": "SETTLEMENT_CHANGE_LOYALTY",
"value": -10
},
{
"type": "EMPIRE_CHANGE_LOYALTY",
"value": 3,
"excludeCapital": false
}
]
},
{
"text": "Negotiate. Grant concessions to the coastal towns and let the matter die down quietly.",
"effects": [
{
"type": "EMPIRE_CHANGE_RESOURCE",
"stringData": "GRAIN",
"value": -500,
"filter": "coastal"
},
{
"type": "EMPIRE_CHANGE_LOYALTY",
"value": 8,
"filter": "coastal"
}
]
}
]
}
]
A few things worth pointing out in the first option above:
- filter: "coastal" on EMPIRE_CHANGE_GARRISON means only coastal settlements get reinforced — inland cities are untouched.
- excludeCapital: true on that same effect protects the capital from the garrison change entirely, while the empire-wide loyalty boost right below it deliberately sets excludeCapital: false to make sure the capital does benefit from that one.
- NAMED_SPAWN_ARMY targets a specific settlement ID (4001) — if that settlement doesn't exist in whatever scenario the mod is running in, fallback: "coastal" means the game picks any coastal settlement instead, so the effect never silently fails.
- strength: 3 triples the size of that spawned army beyond its baseline.
Focus Tree JSON Reference
Focus trees are wired into the game already, sitting ahead of a future expansion that hasn't been announced yet — think of this section as a bit of a spoiler. Without that content, the focus panel never opens and the AI never selects a focus tree at all; a focus tree in a mod simply lies dormant for every player until it does. Build with this section in mind, but don't expect it to do anything visible yet.
Place focus files in the focus\ folder. A focus file is either a bare JSON array of focus nodes, or an object with optional AI disposition metadata:
{
"aiAxes": [ ... ],
"focuses": [ ... ]
}
Both forms load correctly. Use the object form only if you want to declare AI disposition — see below.
Focus Node Fields
Only id is strictly required, but a node with no name and no effects does nothing.
| Field | Type | Description |
|---|---|---|
| id | int | Required. Unique focus ID within the tree. |
| label | string | Design code such as "A.2a". Used in logs and as a display fallback; never shown as the primary name. |
| name | string | Display name. |
| description | string | Shown in the focus tooltip. |
| iconId | int | Image id from the game's icon table — not a file path. |
| branch | string | Grouping tag: "A"–"F", "R" (root), "RA" (racial), "DY", "W", "REP". Branches A–D are the ones affected by the FOCUS_TIME_BRANCH_* spirit modifiers. |
| timeRequired | string | Either a tier ("T3") or a literal cycle count ("840"). A string, not an int — see Focus Duration below. |
| requirements | array | Conditions that must all pass for the focus to be selectable. Prerequisites live here too — see Requirement Object below. |
| visibleWhen | array | Conditions that must all pass for the node to be drawn at all. Omit for always-visible. |
| mutuallyExclusiveWith | int array | Sibling focus ids. Completing any of them permanently locks this one out — an XOR choice. |
| effects | array | Effects applied on completion. Same vocabulary as events — see the Event JSON Reference above. |
| alternateEffects | array | Used instead of effects when alternateWhen passes. |
| alternateWhen | object | A single requirement object. When it passes, the alternate payload is used instead of the default one. |
| alternateSpirit | string | Spirit granted instead of grantSpirit when alternateWhen passes. |
| alternateDescription | string | Tooltip text for the alternate case, so the description never promises something the player won't actually get. |
| grantSpirit | string | Spirit id granted on completion. |
| replacesSpirit | string | Spirit id silently revoked first — the chain-upgrade mechanism for a focus that supersedes an earlier one. |
| spiritByHeld | object | Map of heldSpiritId → spiritIdToGrant. Checked in order; first match wins, no match grants nothing. Built for convergence nodes where two different paths meet at the same focus. |
| removeSpiritOnTier | int | If set, grantSpirit is auto-revoked the moment any focus of this tier or higher completes. |
| repeatable | object | See Repeatable Focuses below. |
| warnings | array | See Warnings below. |
| aiWeight, aiTags, aiRequirements, aiTrough | — | See AI Selection Fields below. |
| displayX, displayY | int | Grid column and row on the focus canvas. Layout hints only — no gameplay logic reads them. |
| redeclareCostImi | int | Marks this focus re-declarable: the cost in Imperial Influence to swap to it after an exclusive choice was already made. |
| redeclareLoyaltyPenalty | int | Empire loyalty lost when re-declaring. |
| redeclareFreezeCycles | int | Cycles during which no new focus can be started after re-declaring. |
Focus Duration
timeRequired is a string — either a tier name or an explicit cycle count:
| Tier | Cycles |
|---|---|
| "T0" | 120 |
| "T1" | 240 |
| "T2" | 300 |
| "T3" | 360 |
| "T4" | 420 |
| "T5" | 540 |
| "T6" | 660 |
A literal number as a string, such as "840", sets that many cycles directly. An unrecognised tier falls back to 360 cycles and logs a warning. The final duration is then scaled by the empire's FOCUS_TIME_ALL and FOCUS_TIME_BRANCH_A–_D spirit modifiers, floored at 0.50× of the base — no combination of modifiers can push a focus below half its authored length.
Requirement Object
{ "type": "REQUIREMENT_TYPE", "targetId": 0, "targetIds": [], "value": 0, "stringData": "" }
The same object is used by requirements, visibleWhen, aiRequirements, and alternateWhen.
Prerequisites are just requirements — there's no separate requiredFocusIds field. Use FOCUS_COMPLETED for a single prerequisite, or FOCUS_COMPLETED_COUNT_MIN for "any of these" or "any N of these."
| Type | targetId | value | Description |
|---|---|---|---|
| FOCUS_COMPLETED | focus id | — | That focus has been completed. |
| FOCUS_COMPLETED_COUNT_MIN | — | how many | targetIds lists the candidates. value: 1 means "any one of them." |
| OWNS_SETTLEMENT | settlement id | — | Empire owns that settlement. |
| SETTLEMENT_DEVELOPMENT_MIN / _MAX | settlement id | dev level | Settlement at least / at most this development level. |
| PLAYER_RESOURCE_MIN | resource id | min amount | Empire holds at least this much of the resource. |
| HAS_GENERAL | — | — | At least one army has a general. |
| HAS_GENERAL_WITH_SKILL | skill id | min level | Any general has that skill at this level. |
| HAS_GOVERNOR | — | — | At least one settlement has a governor. |
| NOT_AT_WAR / IS_AT_WAR | — | — | War state. |
| TECHNOLOGY_RESEARCHED | tech id | min level | Technology at this level (0 is treated as 1). |
| RACE_IS | race id | — | Empire's race matches. |
| IS_COASTAL_EMPIRE | — | — | At least one settlement borders water. |
| IS_LANDLOCKED_OR_ROUGH | — | — | Not coastal, or at least 40% of controlled hexes are hills, mountains, broken ground, forest, or dark forest. |
| RACE_HAS_NO_CRIME | — | — | The empire's race has a base crime rate of zero — see Crime and Provincial Order. |
| RACE_CANNOT_ASSIMILATE | — | — | The empire's race cannot assimilate other populations — see Assimilation. |
| EMPIRE_HOLDS_SPIRIT | — | — | stringData = spirit id the empire must currently hold. |
| EMPIRE_LOST_SETTLEMENT | — | min lost | Empire has lost at least this many settlements to conquest. Counts real empire-vs-empire conquest only — enables failure-triggered focuses. |
| EMPIRE_WRONGED_BY | other empire id | — | That empire has declared war on, or taken a settlement from, this empire within the last 20 years. |
| WORLD_STATE | — | threshold | stringData holds the predicate — see below. |
WORLD_STATE predicates. stringData is "PREDICATE:X" or "PREDICATE:X:Y", where X and Y are faction ids or the literal SELF (the empire being evaluated):
| stringData | value | Meaning |
|---|---|---|
| "WAR_BETWEEN:X:Y" | — | X and Y are currently at war with each other. |
| "HOLDS_FOUNDED_BY:X:Y" | count | X holds at least value settlements originally founded by Y. |
| "HOLDS_SETTLEMENTS_MIN:X" | count | X holds at least value settlements in total. |
Unknown predicates log a warning and evaluate as false.
Repeatable Focuses
"repeatable": {
"cooldownCycles": 480,
"costEscalation": 60,
"maxRepeats": 5,
"effectCap": 3
}
| Field | Meaning |
|---|---|
| cooldownCycles | Cycles before the focus becomes selectable again. 0 = immediately. |
| costEscalation | Cycles added to the duration per previous completion. |
| maxRepeats | Hard cap on completions. 0 = unlimited. |
| effectCap | Completions after which perCompletion spirit deltas (see the Spirit JSON Reference section) stop applying. |
Effects cap while cost does not — deliberately. A long campaign has something to keep spending time on, without the reward inflating indefinitely.
Warnings
Fires a notification to another empire at a point in a focus's lifecycle — the mechanism behind something like "the Storm Palace has begun moving envoys among the harbours."
"warnings": [
{
"targetEmpireId": 4,
"messageId": "warn.mymod.fleet_gathering",
"trigger": "50"
}
]
| Field | Meaning |
|---|---|
| targetEmpireId | Faction id to warn. Takes precedence over targetGroup. |
| targetGroup | Used when targetEmpireId is absent. Only "all-met" (every empire this one has established communications with) is implemented — other values log a warning and are skipped. |
| messageId | i18n key for the warning text. Add the key in your mod's i18n/strings.properties file. |
| trigger | "start", "50" (halfway), or "completion". |
AI Selection Fields
| Field | Type | Meaning |
|---|---|---|
| aiWeight | int | Selection weight. Default 50. Only set it on genuine decision points. |
| aiTags | string array | Tags compared against the empire's rolled disposition. A tag matching the empire's disposition multiplies weight by 2.0; a tag belonging to a declared axis but not matching multiplies by 0.4. Tags belonging to no declared axis are simply ignored — safe to use for readability. |
| aiRequirements | array | Hard gates for the AI only. Checked at selection and re-checked at completion — if a gate has since failed, DECLARE_WAR effects are skipped while every other effect still applies. |
| aiTrough | bool | Marks a focus whose own effects are net negative but which unlocks something better. Such a node is scored purely on what it leads to, so the AI doesn't avoid it forever. |
One caveat worth knowing: the AI's pre-combat "should I attack" check doesn't read targeted combat bonuses such as ARMY_COMBAT_VS_EMPIRE_<id>. An AI holding a grudge bonus therefore fights better when it meets that empire, without being any more inclined to actually seek them out. If a tree depends on the AI going after a specific rival, that intent belongs in aiWeight on the war-declaring focus, gated with aiRequirements (e.g. EMPIRE_WRONGED_BY) — weight is the only lever the selector actually reads.
AI Axes (Disposition)
Declared once per tree, at the top level of the object form. Each axis is rolled once per empire at first evaluation and then fixed for the rest of the campaign — this is what makes two AI empires running the same tree play differently from each other.
"aiAxes": [
{
"id": "course",
"source": "faction",
"options": [
{ "tag": "governance", "weight": 30, "matchPlayStyle": 1 },
{ "tag": "economy", "weight": 35 },
{ "tag": "military", "weight": 25, "matchPlayStyle": 3 }
]
}
]
| Field | Meaning |
|---|---|
| id | Axis name. Only used as a key. |
| source | "literal" (plain weights), "faction" (weights plus a +50 bonus when matchPlayStyle matches the empire's AI play style), or "race" (per-race weights). |
| options[].tag | The tag matched against aiTags on focus nodes. |
| options[].weight | Roll weight. Defaults: 50 for literal/race, 25 for faction. |
| options[].matchPlayStyle | source: "faction" only. Play-style id that grants the roll bonus. |
| options[].weightByRace | source: "race" only. Map of race id to weight; unlisted races default to 50. |
Diplomatic Demands from a Focus
CREATE_DIPLOMATIC_REQUEST (see the Event JSON Reference section above) lets a focus send an ultimatum without paying the usual diplomatic cost. It names one settlement, chosen by the filter selector:
| Selector | Picks |
|---|---|
| most-isolated | The candidate furthest from the target's capital. |
| highest-heritage-population | The candidate with the most population of the demanding empire's race. |
| lowest-loyalty | The least loyal candidate. This is the default. |
The target's capital is never a candidate. For demand-return-of-cities, candidates are further restricted to settlements the demanding empire originally founded — so in a scenario, set founderId on every settlement that lore says belonged to the demanding realm, regardless of who actually holds it at game start. Get this wrong and the request silently finds no candidate and never fires.
The target may accept (the settlement transfers), refuse, or ignore the demand. Refusing or ignoring grants the sender WAR_UNRESTRICTED against the refuser — free, penalty-free, truce-ignoring war — so refusal is never actually free either. Omitting targetId on a demand-reparation sends one request to every empire that currently qualifies under EMPIRE_WRONGED_BY.
Focus Tree Naming
The file generic_focus.json replaces the built-in generic focus tree, which every faction uses unless it names a different tree in data_factions.csv column 4. Its tree id is GENERIC_FOCUS_TREE.
Any other filename creates a new tree, whose id is simply the filename without .json. To assign it to a faction, put that same string in column 4 of data_factions.csv.
Focus Node Example
{
"id": 5001,
"label": "M.2a",
"name": "The Long Watch",
"description": "Border forts are manned year-round, at a cost the treasury feels.",
"branch": "A",
"timeRequired": "T3",
"requirements": [
{ "type": "FOCUS_COMPLETED", "targetId": 5000 },
{ "type": "IS_LANDLOCKED_OR_ROUGH" }
],
"mutuallyExclusiveWith": [5002],
"effects": [
{
"type": "EMPIRE_CHANGE_GARRISON",
"value": 50,
"filter": "borders-enemy",
"raiseWanted": true
}
],
"grantSpirit": "MYMOD_LONG_WATCH",
"aiWeight": 60,
"aiTags": ["defensive"],
"displayX": 2,
"displayY": 4
}
Spirit JSON Reference
Spirit files are individual JSON objects — one file, one spirit. Place files in the spirits\ folder; the filename (without .json) becomes the spirit's ID. Every faction's empire spirit is defined this way — see any faction page for a worked example of a real spirit's effects.
Spirit Fields
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Unique spirit ID. Must match the filename (without .json). |
| name | string | Yes | Display name. |
| description | string | Yes | Shown in the Imperial Court panel. |
| rawModifiers | object | No | Map of modifier name to float value. Plain modifiers and keyed modifiers both go in this same object — see below. |
| perCompletion | object | No | Incremental modifier deltas applied each time a repeatable focus granting this spirit completes. (Only relevant once focus trees exist — see the note below.) |
| onGainEffects | array | No | Event effects applied when the empire gains this spirit. |
| onLossEffects | array | No | Event effects applied when the empire loses this spirit. |
| isAdoptableManually | boolean | Yes | Whether the player can adopt this spirit voluntarily. Faction and government spirits are usually false. |
rawModifiers Keys — Plain Modifiers
All default to 0.0 unless noted. Sign convention: for anything named *_TIME, *_COST, *_WEAR, *_CONSUMPTION, or LOCAL_CRIME, negative is always better for the empire holding the modifier. For everything else, positive is better. There's exactly one deliberate exception to this rule, flagged in its own row below.
Empire, research, and diplomacy:
| Key | Description |
|---|---|
| IMI_GENERATION | Imperial Influence generated per cycle (base 0.2). |
| IMI_CAPACITY | Maximum stored Imperial Influence (base 100). The ceiling, not the rate. |
| DIPLOMATIC_RELATIONS | Flat bonus/malus to relations with all empires. |
| DIPLOMATIC_SPEED | Diplomacy completion speed. |
| DIPLOMATIC_COST | Imperial Influence cost of diplomatic actions. Negative is cheaper. |
| RESEARCH_TIME | Research duration. Negative is faster. (Renamed from RESEARCH_SPEED, which still loads as an alias so older spirit files keep working.) |
| RESEARCH_SETTLEMENT_PENALTY | Reduces the per-settlement research penalty described on Technology Tree. That penalty compounds, so blunting it matters more than the base rate once an empire passes six or seven cities. |
| GES_MANPOWER_CAPACITY | Global manpower stock capacity — see Global Equipment Stock. |
| GES_HORSES_CAPACITY | Global horses stock capacity. |
| GES_EQUIPMENT_CAPACITY | Global equipment stock capacity. |
| GES_GRAIN_CAPACITY | Global grain stock capacity. |
| BUILD_CHURCHES | Non-zero enables the Build Churches project — see Outposts. |
| GROW_FOREST | Non-zero enables the Grow Forest project — see Projects. |
| SEA_FARING | Non-zero lets armies enter open ocean hexes — see Movement. |
Settlement (applies to every settlement in the empire):
| Key | Description |
|---|---|
| LOCAL_LOYALTY | Flat loyalty per settlement — see Loyalty. |
| LOCAL_CRIME | Multiplies the derived crime rate. Negative reduces crime — see Crime and Provincial Order. |
| LOCAL_CITY_DEFENCE | Modifier to garrison combat values. |
| LOCAL_EXPANSION_TIME | Modifier to hex expansion project time. |
| LOCAL_PROJECT_TIME | Modifier to settlement project time. |
| LOCAL_WALL_TIME | Modifier to the Build Walls project's duration. |
| LOCAL_MANPOWER_COLLECTION | Modifier to recruits drawn from each population group. |
| LOCAL_RESOURCE_COLLECTION | Modifier to Manpower, Horses, Iron, Leather, and Wood collection. Excludes Stone. |
| LOCAL_STONE_COLLECTION | Modifier to Stone output specifically. |
| LOCAL_GRAIN_COLLECTION | Modifier to daily grain collection. |
| LOCAL_RESOURCE_CHURCHES | Non-zero grants bonus resources from Churches. |
| LOCAL_PRODUCTIVITY | Modifier to workshop capacity. |
| LOCAL_WORKSHOP_SLOTS | Flat addition to the workshop slot formula. |
| LOCAL_WORKSHOP_STAFFING | Modifier to workers required per workshop. Negative means workshops need fewer people — see Production and Equipment. |
| LOCAL_POPULATION_GROWTH | Modifier to birth rate. Does not affect resource income, which derives from hexes rather than population — see Population Growth. |
| LOCAL_ASSIMILATION | Modifier to assimilation rate — see Assimilation. |
| LOCAL_MIGRATION_SPEED | Modifier to the rate of gentle forced migration between settlements — see Migration. |
Army:
| Key | Description |
|---|---|
| ARMY_MV / ARMY_AV / ARMY_RV / ARMY_SV / ARMY_RSV | Melee, Armour, Ranged, Shock, and Resolve values. |
| ARMY_CR | Combat Readiness. Also raises the minimum allowed Combat Readiness. |
| ARMY_CR_AT_WAR | Additional Combat Readiness modifier applying only while the empire is at war. |
| ARMY_CAP | Flat extra armies beyond the Military Academy limit — see Armies. |
| ARMY_PROJECT_TIME | Modifier to army project duration. |
| ARMY_MOVEMENT_SPEED | Modifier to army movement rate. |
| ARMY_EQUIPMENT_WEAR | Modifier to equipment wear while marching. |
| ARMY_GRAIN_CONSUMPTION | Modifier to army grain demand. Negative means an army that eats less. |
| ARMY_EMBARK_COST | Modifier to embark/disembark cost — see Movement. |
| ARMY_EXERCISE | Modifier to Exercise Army effectiveness. |
| ARMY_SIGHT | Modifier to sight range, capped at 4. Only matters with Fog of War on. |
| ARMY_ORGANISATION_CHURCHES | Non-zero means armies near Churches lose less organisation. |
| ENEMY_SUPPLY_IN_TERRITORY | Equipment wear inflicted on enemy armies resting inside the holder's own territory. The one deliberate exception to the sign convention above — positive is better here, because the value describes damage done to somebody else, not a cost paid by the holder. |
Naval: Fleets are Army objects internally, so the ARMY_* modifiers above already apply to them — this separate NAVAL_* set exists so a naval-flavoured spirit doesn't silently buff every land army too. These are wired into existing calculations ahead of a future expansion that hasn't been announced yet. Naval combat and fleets aren't part of the base game — don't build a mod expecting these to do anything meaningful until that content actually ships.
| Key | Description |
|---|---|
| NAVAL_MV / NAVAL_AV / NAVAL_RV / NAVAL_SV / NAVAL_RSV | Combat values applied only to fleets. |
| NAVAL_MOVEMENT_SPEED | Modifier to fleet movement rate. |
| FLEET_CAP | Extra fleets beyond one per coastal settlement. |
| RAISE_FLEET_TIME | Modifier to the Raise Fleet project's duration. |
| SIEGE_SUPPORT | Modifier to a fleet's naval bombardment support to an ongoing land siege — see Sieges. |
Leaders, heirs, and race:
| Key | Description |
|---|---|
| LEADER_XP_GAIN | Modifier to XP gain for army, fleet, and governor leaders — see Leader Age and Experience. |
| HEIR_QUALITY | Flat skill levels on generated heirs. |
| RACE_ENABLE_ASSIMILATION | Grants assimilation to a race that cannot normally assimilate (such as the Ende), at this rate. Removed entirely rather than merely reduced when the spirit is lost — see Population and Special Races. |
| SOUL_CAPTURE | Multiplies the Ende kill-to-troop conversion rate. Affects combat only — it does not affect population growth. |
Conquest-conditional: applied realm-wide the instant a settlement is captured, depending on whether the empire it was taken from had wronged the conqueror within the last 20 years.
| Key | Description |
|---|---|
| LOYALTY_ON_UNJUSTIFIED_CONQUEST | Loyalty change when taking a settlement from an empire that hasn't wronged the conqueror. Negative is a penalty. |
| LOYALTY_ON_JUSTIFIED_CONQUEST | Loyalty change when taking a settlement from an empire that has. Positive is a reward. |
Focus system: read only by the focus manager — see the Focus Tree JSON Reference section above for the same forward-looking caveat as naval content.
| Key | Description |
|---|---|
| FOCUS_TIME_ALL | Modifier to all focus durations. |
| FOCUS_TIME_BRANCH_A … _D | Per-branch focus duration, matching a node's own branch field. |
rawModifiers Keys — Keyed Modifiers
Some modifiers need a key as well as a value — a specific terrain, project, or empire. These go in the same rawModifiers object as the plain modifiers above, with the key appended to the modifier name after an underscore. There's no separate schema for this, and unknown names are simply ignored rather than erroring — safe to author even if you're not entirely sure of an ID.
"rawModifiers": {
"ARMY_TERRAIN_COMBAT_140": 0.15,
"REDUCE_BUILDING_COST_115": -0.20
}
| Prefix | Key Is | Description |
|---|---|---|
| ARMY_TERRAIN_COMBAT_ | terrain id | Combat modifier in that terrain only — see Terrain for terrain IDs. |
| ARMY_COMBAT_VS_EMPIRE_ | faction id | Combat modifier against that empire specifically. See the AI caveat under Focus Tree JSON Reference — the AI's own attack decision doesn't read this. |
| OUTPOST_BUILD_TIME_ | project id | Outpost project duration. Keyed by the project, not the outpost type — see Outposts. |
| REDUCE_BUILDING_COST_ | project id | Permanent Stone-cost reduction for that project. |
| RACE_TERRAIN_LOYALTY_ | condition | Adjusts a race's terrain loyalty reaction. Conditions: no-forest, forest-adjacent, open-terrain — see Loyalty. |
| RACE_BUILDING_LOYALTY_ | condition | Adjusts a race's per-building loyalty reaction. Conditions: marketplace, city-wall. |
| LOCAL_LOYALTY_AT_RECRUIT_LEVEL_ | recruit level (0/1/2) | Loyalty applied only at that recruitment setting — see Population Growth for recruit levels. |
| WAR_UNRESTRICTED_ | faction id | Non-zero: declaring war on that empire costs no Imperial Influence, incurs no reputation penalty, and ignores the usual post-war waiting period — see Diplomatic Actions. |
| DEFECTION_PRESSURE_ | faction id | Lowers the relation threshold at which that empire's AI yields to diplomatic demands. (Ties into the focus-driven demand system — see Diplomatic Demands from a Focus above.) |
perCompletion — Repeatable Focus Scaling
Only relevant once focus trees exist — see the note at the top of that section above.
When a spirit is granted by a repeatable focus, perCompletion adds its deltas again on every completion, until the focus's own effectCap is reached.
{
"id": "MYMOD_LEDGER_OF_DEBTS",
"name": "The Ledger of Debts",
"description": "Each account settled makes the next easier to open.",
"rawModifiers": { "ARMY_CR": 0.01 },
"perCompletion": { "ARMY_CR": 0.01 },
"isAdoptableManually": false
}
Base modifiers apply once, when the spirit is first granted. perCompletion then stacks further deltas on top per completion. Keyed modifier names work here too.
Spirit Naming Conventions
- Faction spirits:
FACTION_<id>.json(e.g. FACTION_1.json for faction id 1) - Government spirits:
GOVERNMENT_<TYPE>_<LEVEL>.json - Custom spirits: any ID — available as national spirits for events and focus effects
Spirit Example
{
"id": "SPIRIT_IRON_DISCIPLINE",
"name": "Iron Discipline",
"description": "Your armies are drilled beyond endurance. They fight harder but resent it.",
"rawModifiers": {
"ARMY_MV": 1.5,
"ARMY_RSV": 2.0,
"LOCAL_LOYALTY": -1.0
},
"onGainEffects": [],
"onLossEffects": [],
"isAdoptableManually": true
}
