Battle Configuration JSON Reference (Schema 2)
This reference is for users who want to save, share, or edit configurations beyond the GUI Builder. Only the fields documented here are within the published support scope. Valid JSON syntax does not guarantee that a value or combination is supported.
Minimal configuration
{
"schema": 2,
"init": {
"seed": 1,
"gap": 1,
"tier_factor": 10000,
"threshold": 0
},
"mode": 1,
"wall": null,
"left": {
"level": 25,
"formation": 0,
"troops": [
{"type": "infantry", "tier": 5, "count": 1}
],
"bonuses": {}
},
"right": {
"level": 25,
"formation": 0,
"troops": [
{"type": "infantry", "tier": 1, "count": 1}
],
"bonuses": {}
}
}
Top-level fields are schema, init, mode, wall, left, and right.
Schema 2 requires init to include seed, gap, tier_factor, and
threshold; each side requires level, formation, troops, and bonuses.
wall may be null. Numeric fields must be JSON integers, not strings,
decimals, or booleans.
An optional "description": "..." may be added as a note. It is metadata and
does not change the battle.
Global parameters
| Field | Range | Purpose |
|---|---|---|
schema |
2 |
Selects the current configuration format. |
init.seed |
1–1000 | Starting point for the battle's random sequence. Keep it fixed when replaying a setup. |
init.gap |
1–100 | Step parameter for the random sequence. Keep it fixed with the seed when comparing setups. |
init.tier_factor |
1–10000 | Factor used when adjusting tier-count differences; 10000 is the UI default. |
init.threshold |
0–100 | Percentage threshold used to determine the result at the time limit; default 0. |
mode |
1 |
Only mode 1 is currently in the supported contract. |
wall |
null or object |
Use null to disable the wall; see the wall section below. |
left, right |
object | The two sides participating in the battle. |
Seed and gap are not combat bonuses. Keep them unchanged when comparing configurations; changing them may change the random sequence used.
Side configuration
| Field | Range / type | Purpose |
|---|---|---|
level |
1–75 | Side level. The runtime must contain corresponding level data. |
formation |
0–5 | Formation index. The UI displays the corresponding English in-game name; JSON stores this number. |
troops |
troop array | Troop entries with a count greater than zero; an empty array is allowed. |
bonuses |
object | Side-wide default bonuses. Omitted channels default to zero. |
kind_bonuses |
optional object | Sparse overrides by troop kind; only specified channels replace side defaults. |
advantages |
optional object | Counter values for Infantry, Cavalry, and Ranged; separate from bonuses. |
heroes |
optional array | Up to five hero records per side, subject to supported combinations. |
pets |
optional array | Up to five pet records per side, subject to runtime validation. |
hero_ultra |
optional, 0–100000 | Raw hero-ultra parameter; defaults to zero. Use only with a supported hero/skill setup. |
Troop entry
{"type": "infantry", "tier": 1, "count": 1000}
type:infantry,ranged,cavalry, orsiege.tier: integer from 1 to 5.count: positive integer. Total troop count is limited to 20 million per side.- Each
type/tierpair may appear only once. Omit a troop entry when it is not used; an entry with count zero is invalid.
Standard bonuses
These values use raw units. The convention is 100 raw equals a 1% modifier.
Limits are input bounds, not recommended values.
| Channel | Raw limit | Field meaning |
|---|---|---|
attack, defence, hp |
0–1000000 | Side attack, defence, and HP. |
reduce_attack, reduce_defence, reduce_hp |
0–1000000 | Corresponding reduction channels. |
curse_attack, curse_defence, curse_hp |
0–1000000 | Curse channels affecting the opposing same-kind troops. |
add_damage, reduce_damage |
0–100000 | Additional-damage and damage-reduction channels. |
magic |
0–100000 | Side magic channel. |
Example:
"bonuses": {
"attack": 5000,
"hp": 10000,
"reduce_damage": 2500
}
Omitted channels use zero. Only listed channel names are supported; an unrecognized key is not treated as a comment.
Overrides by troop kind
kind_bonuses maps a troop kind to an object containing only the channels to
override:
"bonuses": {
"attack": 1000,
"hp": 2000
},
"kind_bonuses": {
"infantry": {"attack": 5000},
"ranged": {"hp": 0}
}
In this example Infantry uses attack 5000 and inherits HP 2000; Ranged inherits
attack 1000 and uses HP 0; kinds without an entry inherit both side-wide values.
An override replaces a value; it is not added to the side bonus. An empty
{} entry inherits all channels. Channel names and limits match the standard
bonus table.
Counter advantages (advantages)
advantages is a separate parameter and is not part of bonuses or
kind_bonuses. The documented cycle is Infantry counters Ranged, Ranged counters
Cavalry, and Cavalry counters Infantry. The object accepts infantry,
cavalry, and ranged; each value is an integer from 0 to 100000 raw.
"advantages": {
"infantry": 5000,
"cavalry": 5000,
"ranged": 5000
}
Siege has no advantages key in this contract. Each side has three inputs under
Advantages (counters) in the GUI Builder. Blank omits the key (the core's
default is 0); entering 0 preserves an explicit zero. 100 raw corresponds to
1%. Imports retain sparse values, including zero; clearing a loaded input removes
that particular key from the exported configuration.
Heroes and pets
"heroes": [{"id": 1, "rank": 1, "star": 5}],
"pets": [{"id": 26, "level": 1}],
"hero_ultra": 0
- Hero:
id,rank(1–255),star(1–5); at most five records per side and at most four heroes of one troop kind. The UI selector lists IDs1, 2, 3, 5, 7, 8, 9, 16, 54; this is not a complete roster. - Pet:
id,level(1–10); at most five records. The selector lists13, 17, 19, 24, 26, 28, 29, 30, 31, 32, 36, 41, 43, 48, 49, 50, 58, 59, 67. - With a wall enabled, pet selections in the GUI are limited to
19, 24, 26, 28, 29, 30, 31, 32, 41, 43, 50. - The resource list and slot limits do not guarantee every combination. Unsupported resources, effects, or combinations are rejected by the runtime.
Wall and defence force
When configured, the wall object has this form:
"wall": {
"defence": 1,
"maximum": 0,
"force": [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
"health": 10000,
"attributes": {"attack": 5000}
}
defence,maximum: non-negative integers within the u32 contract.force: exactly 12 integers in this order: Trap0 T1–T4, Tower T1–T4, Trap2 T1–T4. Total defence count is limited to 20 million.health: optional, 0–1000000; separate WallHealth parameter, default 10000.attributes: optional attributes shared by defence units. Supportsattack,defence,hp,reduce_attack,reduce_defence,reduce_hp,curse_attack,curse_defence, andcurse_hpfrom 0 to 1000000; andadd_damage,reduce_damagefrom 0 to 100000.magicand per-category overrides are not supported.
Set "wall": null when there is no wall. Wall mode is determined from the troop
configuration; there is no wall-mode field to set.
Import, export, and validation
Resource names are display labels from the verified English catalog. JSON keeps IDs, indices and force order; names are not input keys or new mechanic support.
- Import battle JSON file loads a schema 2 file, fills the fields recognized by the builder, and retains advanced fields in Raw JSON. Input that cannot be represented intact is rejected before replacing the configuration; use Raw JSON to submit it unchanged for core validation.
- Export JSON downloads the active configuration. In Raw JSON mode, it exports the current editor content; in GUI mode, it exports the form values while retaining imported advanced fields without GUI controls.
- Invalid JSON syntax, missing required fields, invalid numeric types, unsupported keys, duplicate troop type/tier entries, or combinations outside the supported scope may be rejected. Use the runtime error to identify the field to correct.
- When editing Raw JSON, keep fields you do not understand rather than removing them arbitrarily.
Army Profile, Battle Preset and library backup files
Library files use a version 1 envelope, separate from combat JSON schema 2.
Envelopes are never sent to /api/run. Import to library accepts an envelope
or schema 2 battle; Import battle JSON file loads a schema 2 battle directly
into the builder.
Valid Army Profile example:
{
"format": "combat-simulator-army-profile",
"version": 1,
"profile": {
"name": "Infantry army",
"notes": "",
"army": {
"level": 25,
"formation": 0,
"troops": [{"type": "infantry", "tier": 5, "count": 1000}],
"bonuses": {},
"advantages": {"infantry": 0}
}
}
}
profile.army is the complete object for one side, without a left/right
wrapper, init or wall. Optional army fields use the same side contract above.
A Battle Preset has format: "combat-simulator-battle-preset", version: 1, and
a preset object containing name, notes and battle. preset.battle is the
complete schema 2 configuration, including wall and both armies. Exporting the
current battle still produces schema 2 alone, without library metadata.
A library backup has format: "combat-simulator-library", version: 1, and
armies/presets arrays. Every record contains id, name, notes,
created_at/updated_at (timestamp strings), and either army or battle.
Imports assign new IDs, suffix colliding names and retain backup timestamps;
existing records are kept. Names must be unique within each kind, ignoring case,
and contain at most 100 characters; notes are strings of at most 2000 characters
and may be empty.
The whole document is checked before writing. Unsupported versions, duplicate JSON keys, decimal/exponent numeric tokens, inexact browser integers and data the builder cannot represent are rejected. Advanced combat fields are retained; the core still validates mechanics at run time. The library is browser-local for this site address; see the guide for backup and storage recovery.
Results
The UI presents the winner, time/tick, weighted loss, and casualties by troop kind/tier. Wall defence losses are shown separately when applicable. Playback shows recorded ticks and available state observations. Weighted loss is a simulator loss metric; it is not documented as Might or KVK score.