SDK reference
A lookup list of everything a mod can declare. To learn how to do it, see the guides linked in each table.
Paths on this page use \. The game is case-insensitive.
1. Mod structure
A mod is a folder in mods\. Folders starting with . or _ are ignored. Without a mod.json, the game creates a basic one (with autoGenerated).
"Gameplay" = every player must have the same mod (README).
| Path in the mod | What it does | Gameplay |
|---|---|---|
mod.json |
The mod's manifest. See section 2 | No |
data\… |
New files, or files that replace the game's, at the same path | Yes |
scenarios\, adventures\, conquests\ |
Maps, adventures and conquests | Yes |
units\, buildings\, mapobjects\ |
Replace the game's. .xml and .pass (footprints) count; .rle and the rest don't |
Only .xml and .pass |
visuals\ |
Effects and projectiles | Only .xml |
packs\*.bfhp |
Maps (random, settlements…) | Yes |
packs\*.pak |
Packs mounted alongside the game's | Depends on their contents |
randommap\, randommapsettlements\ |
Random map shapes and city templates (Random maps, City templates) | Yes |
patches\<path> |
Patch of the game file at that path. Only .xml, .ini and .vs. See section 6 |
Depends on the path it patches |
civs\<id>.json |
Civilizations. Section 3 | Yes |
specials\*.json |
Unit specials. Section 4 | Yes |
options\*.json |
Game options. Section 5 | Yes |
events\<Event>.vs |
Event scripts. Section 7 | Yes |
loc\<language>.loc.xml, loc\<language>\*.loc.xml |
Texts added to the game's (Languages) | No |
local\<language>\<path> |
File the game sees as currentlang\<path>, only with that language |
No |
local\<language>.pak |
Language pack (also <language>_something.pak). Only the one for the game's language is mounted |
Depends on its contents |
hidden.txt |
Game files the mod hides, one path per line; the ones after [language] are only hidden with that language. For old mods that removed files by replacing the game's packs; shrink-mod.js writes it |
Yes, if it hides gameplay files |
rle.mmp.tail |
Graphics appended to the game's rle.mmp, with the fingerprint of the rle.mmp it extends. shrink-mod.js writes it. Only one active pack can have it |
No |
movies\… |
Cutscenes (PlayMovie): they are looked up in the active packs first, in load order |
No |
gameres\… |
Loose interface images (Images) | No |
| Any other path | Loose file with the same path as in the game | No |
- Forbidden extensions:
.exe .dll .asi .bat .cmd .com .scr .ps1 .vbs .js .msi .sys .cpl .lnk .reg .jar .hta .wsf .pif. With one of them, the mod doesn't load. - An extension that isn't a known one, and isn't forbidden, is allowed and never executed.
- If two mods ship the same file, a loose file beats one inside a
.pak; between two loose files, or two inside.pakfiles, the one that comes first in the load order wins. The game works that order out, nobody picks it: first what the mods ask for (a mod goes above the ones it needs inrequiresand the ones in itsloadAfter, and below the ones in itsloadBefore); then cosmetic mods go above content (maps, battles, conquest), and content above mods that change gameplay; on a tie, byid. The same mods give the same order on every computer. On each mod's card, the content manager lists the files it shares with other active mods and whose copy of each one is used. If two active mods replace the same gameplay files and neither names the other inrequires,loadAfterorloadBefore, it is shown as a problem: use only one, or a compatibility patch (a mod that ships the merged files and has both in itsloadAfter). When every file they share is cosmetic (textures, sounds, interface), the player can choose whose files are used; the choice is saved in the profile, only applies to loose files and changes neither the load order nor what counts online. To change a few values of a file that another mod may also change, a patch is better (section 6): patches add up instead of replacing the file.
Test folder <game>\mod\ (no mod needed; it beats all mods):
| Path | What it does |
|---|---|
mod\<path> |
Replaces the game file at that path (if [Mods] Override=1) |
mod\patches\<path>, mod\loc\<language>.loc.xml, mod\events\<Event>.vs, mod\civs\*.json, mod\specials\*.json |
Same as in a mod. options\ isn't read from here |
mod\_cache\ |
Generated by the game (merged texts, patched files). Don't touch it |
2. mod.json
| Field | Type | Default | What it does |
|---|---|---|---|
id |
text | folder name, lowercase with - |
The mod's identifier |
name |
text, or object per language | folder name | Name in the content manager |
version |
text | empty | Version |
authors |
list of texts | empty | Authors |
description |
text, or object per language | empty | Description |
license |
text | empty | License |
redistributable |
boolean | false |
The host may send the mod to someone who doesn't have it |
requires |
list of ids, or {"id", "version"} objects |
empty | Mods it needs. version accepts >=, <=, >, <, =, *, or a number (equal) |
incompatibleWith |
list of ids | empty | Mods it can't be enabled with |
loadAfter |
list of ids | empty | Your mod goes above those: its files, patches and texts win |
loadBefore |
list of ids | empty | Your mod goes below those |
category |
text | empty | GameBanana category (Publishing) |
tags |
list of texts | empty | GameBanana tags: players (maps) and language (campaigns, translations, audio) |
sources |
object | none | url (or page), gamebanana (number) and tags (list, added to the ones above) |
language |
object, list of objects or text | none | code, name and base (english by default). Plain text = just the code. See Languages |
commands |
object | none | {"<orders file without extension>": {"name": text or object per language}}. See Keys |
A per-language text picks the game's language; if it's missing, English; if that's missing too, the first one.
English aliases the game also reads: name, description, license, redistributable, language (with code, name).
3. civs\<id>.json
Guide: Civilizations.
| Field | Type | Default | Required | What it does |
|---|---|---|---|---|
id |
text | file name | No | Letters, numbers and _; starts with a letter; up to 24. Stored in lowercase. In scripts it's the constant as you write it, with a capital first letter (Dacia, RomeImperial) |
index |
integer 10-99 | — | Yes | The civilization's number |
base |
text | Gaul |
No | Game civilization it inherits from: Gaul, RepublicanRome, Carthage, Iberia, ImperialRome, Britain, Egypt, Germany (also Rome, Roman, Briton) |
name |
text, or object per language | the id, capitalized |
No | Name in the lobby |
letter |
one letter | the base's | No | Prefix for classes and orders |
template |
text | empty | No | Prefix of your city and village templates |
internalName |
text | the base's | No | Name the game uses to look for interface files. Also valid in race= if it's not the base's |
folder |
text | the base's | No | Name in the selection texts |
random |
boolean | true |
No | Included in the random civilization draw |
biome |
text (a game civilization) | the base | No | Terrain, trees and objects of the random map |
ai |
boolean | false |
No | The AI plays with your classes and orders (otherwise with the base's). In the draw, an AI only gets civilizations that have it |
ownAi |
boolean | false |
No | With ai: the mod ships all the AI scripts |
alias |
object | none | No | classes and commands: name in the base → name in your civilization (each value a text) |
variant |
boolean | false |
No | Variant of the base: uses its classes, GetPlayerRace returns the base and Mod_PlayerCiv the chosen one. With it, ai is true unless set |
replacesBase |
boolean | false |
No | The base doesn't show in the lobby: this civilization takes its place and its share of the random draw |
race="<id>"in a class's<properties>uses the civilization by itsidor itsinternalName.- Repeated
idorindex: the one from the mod that comes first wins; the others are ignored with a warning. - English aliases:
index,name,letter,template,internalName,folder,random,biome,ai,ownAi.
4. specials\*.json
Guide: Unit specials.
| Field | Type | Default | Required | What it does |
|---|---|---|---|---|
id |
text | — | Yes | Letters, numbers and _; starts with a letter; up to 24. Can't be a game unit special. It's the script constant (Critical) and the name in unit_specials |
bit |
integer 36-63 | — | Yes | Mask bit. Don't change it once published |
name |
text, or object per language | the id |
No | Name |
description |
text, or object per language | empty | No | Read; not shown |
icon |
text | empty | No | Read; not used |
- Repeated
idorbit: the one from the mod that comes first wins. - In
unit_specials, names are comma-separated; mod ones are case-insensitive. The game ignores the ones it doesn't know. - English aliases:
name,description,icon.
5. options\<id>.json
Guide: Game options.
| Field | Type | Default | What it does |
|---|---|---|---|
id |
text | file name | Letters, numbers, _ and -; up to 32. Stored in lowercase. It's what Mod_GetOption and Mod_GetOptionValue use |
name |
text, or object per language | the id |
Text in the lobby |
description |
text, or object per language | empty | The row's help text |
values |
list of 2 to 16 | none (= toggle) | Each value: a text, or {"id", "nombre"}. The first is 0 |
default |
boolean, number or text | false / 0 |
Toggle: true/false. List: the value's number (from 0) or its id. An id that doesn't exist gives 0 |
- A
valueswith fewer than 2 or more than 16 items ignores the whole option. - Bits: toggle 1; list of 2 values 1; of 3-4, 2; of 5-8, 3; of 9-16, 4. They're taken in alphabetical order of
id. - With a repeated
id, the one from the mod that comes first wins. - English aliases:
name,description,values,default.
6. Patches
Guide: Patches. A patch goes in patches\<path of the game file>. Only .xml, .ini and .vs. Order: from the last mod in the load order to the first, so the first one has the last word; mod\patches last.
XML
The patch's first tag is the root (usually called <patch>); its children are the operations, applied in order. Operation, tag and attribute names are case-insensitive.
| Operation | Attributes | Content | What it does |
|---|---|---|---|
<set> |
select, optional, unset, and any other attribute |
None | Changes or adds those attributes on the selected elements. unset removes attributes (separated by spaces or commas) |
<add> |
select, optional, position |
Elements or comments | Adds them. position: end (default), start, before, after |
<remove> |
select, optional |
None | Removes the selected elements |
<replace> |
select, optional |
Elements or comments | Replaces the selected elements with the content |
selectis required. You can't use<set>to set an attribute calledselect,optionalorunset.optional:1,yes,si,síortrueavoid the error if the selector finds nothing.<add>and<replace>without content give an error.- A selector with no results, or an unknown operation, is skipped with an error; the rest go on. If the patch isn't well-formed, no operation is applied.
- The patched file keeps comments and quotes; attributes are collapsed onto one line.
Selector
| Element | Syntax |
|---|---|
| Child / descendant step | /nombre, //nombre |
| Any name | * |
| With attribute equal | [@a='v'] or [@a="v"] (case-insensitive; understands & < > " ') |
| With attribute | [@a] |
| The nth | [n], from 1: the nth of those matching name and conditions, within each parent |
You can chain conditions ([@a='v'][2]). Nothing else exists (and, contains, text(), ..). Always start with / or //.
INI
Each line of the patch, under a [Seccion] (required before any line):
| Line | What it does |
|---|---|
Clave = valor |
Changes the first line with that key in the section, or adds it at the end of the section. It's written as Clave = valor |
-Clave |
Removes all lines with that key from the section, or that exact line. Error if there are none |
+Linea |
Adds a standalone line if it isn't there already |
[Seccion] |
Switches section. If it doesn't exist in the file, it's created at the end |
;… or #… |
Comment. Only valid on a whole line |
Keys and sections are case-insensitive. A comment at the end of a patch line isn't stripped.
.vs scripts
Directives start with @ in the first column; the code below each one is what gets inserted.
| Directive | Where it inserts |
|---|---|
@top |
After the first line if it's a // comment (the signature); otherwise at the start |
@bottom |
At the end of the script |
@before TEXTO |
Before the first line containing TEXTO |
@after TEXTO |
After that line |
@replace TEXTO |
In place of that line. With no code below, it deletes the line |
@end |
Closes the block; anything after it up to the next directive is ignored |
TEXTOis a literal piece of a line (case-sensitive). The first line containing it is used, and directives apply in order: they see what the earlier ones inserted.- Error (and the directive is skipped): unknown directive,
@before/@after/@replacewithoutTEXTO, a directive without code (except@replace),TEXTOnot found.
7. Script events
Guide: Scripts. File: events\<Event>.vs. Runs on every PC; with several mods, all of them run.
| Event | When | Signature (first line) |
|---|---|---|
OnGameStart |
When a game starts, after the victory scripts start. Can use Sleep |
// void |
OnObjectDie |
An object dies, before its death is processed (the first time) | // void, Obj victim |
OnObjectKill |
A blow kills an object | // void, Obj killer, Obj victim |
OnSettlementCaptured |
A settlement changes owner while the game is running. Players 1-16; 0 = no owner | // void, Obj building, int oldPlayer, int newPlayer |
OnAttackResolved |
A blow that deals damage, before it's applied | // void, Obj attacker, Obj victim, int damage |
OnPlayerDefeated |
A player (1-8) is declared defeated, once per player | // void, int player |
- Except for
OnGameStart, they run immediately and can't wait. - An event that fires inside itself is cut off at 4 levels.
- If the script fails at run time, the log records it up to 5 times per script.
- Only these event names are read. You choose the parameter names.
8. Mod_* functions
Guide: Scripts. The current API is 6; it's part of the multiplayer check.
| Function | API |
|---|---|
Mod_Log(str text) |
1 |
Mod_LogInt(str text, int value) |
1 |
Mod_IsActive(str id) : bool |
1 |
Mod_ApiVersion() : int |
1 |
Mod_SetInt(str name, int value) |
2 |
Mod_GetInt(str name) : int |
2 |
Mod_AddInt(str name, int delta) : int |
2 |
Mod_HasKey(str name) : bool |
2 |
Mod_AttackDamage() : int |
3 |
Mod_SetAttackDamage(int damage) |
3 |
Mod_HasSpecial(Unit unit, int bit) : bool |
4 |
Mod_SetSpecial(Unit unit, int bit, bool on) |
4 |
Mod_TrueRace(Obj obj) : int |
5 |
Mod_GetOption(str id) : bool |
6 |
Mod_GetOptionValue(str id) : int |
6 |
Script constants: each mod civilization's id with a capital first letter (value = its index) and each mod unit special's id (value = its bit).
9. imperivm-mod.ini
It's in the game folder. It's read at startup.
| Section | Key | Value | Default | What it does |
|---|---|---|---|---|
[Debug] |
LogFiles |
0 or 1 |
0 |
The log records every file the game opens and marks the ones that don't exist (VFS: opens … -> does not exist) |
[Debug] |
Dump |
comma-separated paths, or * |
empty | Copies each file to mod-dump\<path> exactly as the game reads it (patches and mod texts already applied), the first time it opens it. Exact name, no wildcards except * (all) |
[Debug] |
DumpAtStartup |
comma-separated paths | empty | Copies those files to mod-dump\ at startup, without waiting for the game to open them |
[Mods] |
Override |
0 or 1 |
1 |
Enables <game>\mod\<path> as a file replacement |
[Mods] |
Profile |
profile name, Original or No profile |
Original |
Profile the game starts with (No profile is "My content" in the manager). The content manager changes it; a change applies on restart |
10. Limits
| What | Limit | Source |
|---|---|---|
| Game option bits | 8 across all options of all mods | Code |
| Values in an option list | 2 to 16 | Code |
Option id |
up to 32; letters, numbers, _, - |
Code |
Civilization and unit special id |
up to 24; letters, numbers, _; starts with a letter |
Code |
Civilization index |
10 to 99 | Code |
Civilization letter |
1 character | Code |
Unit special bit |
36 to 63 | Code |
bit in Mod_HasSpecial and Mod_SetSpecial |
0 to 63 (out of range: false / does nothing) |
Code |
| Mod variable name | up to 64 characters | Code |
| Mod variables | up to 1,024; 32-bit integers | Code |
Mod_Log and Mod_LogInt |
2,000 lines per session | Code |
Mod_SetAttackDamage |
a negative value is clamped to 0 | Code |
| Event script errors in the log | 5 per script | Code |
| Nested events | 4 levels | Code |
| Patch errors per mod | the first 50 are kept | Code |
Dump |
up to 1,023 characters; DumpAtStartup, 2,047 |
Code |
<behavior> per class |
8 | Scripts |
Images, layers and states in an .ent.xml |
32, 32 and 8 | Graphics |
<anim> idx and frames per animation |
1 to 28; up to 64 (2 are sentinels: 62 rows) | Graphics |
| Sprite directions | 8 soldiers, 12 heroes and animals, 4 ships | Graphics |
| Sprite colors | 256; with player color, 192 | Graphics |
| Button / portrait | 153 x 51 / 65 x 76 | Images |
Formation BonusDamage, BonusArmor, BonusRange, BonusLevel |
-1000 to 1000 | Formations |
| Random map type name | 2 to 31 characters | Random maps |
--players, --small, --distortion of create-random-map.js |
2 to 8, 0 to 8, 0 to 0.5 | create-random-map.js |
--width of create-template.js |
0 to 4 | City templates |
The guides explain how to do each thing; this is the complete list. Back to the README.