⚔️ Combat Simulator

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}

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

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}
}

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.

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.