Modding Guide
TinyGenerals is data-driven — game rules, factions, and visuals are defined in JSON files and sprite sheets. This guide covers the format and structure you need to create custom content.
Rulesets
Section titled “Rulesets”A ruleset is a single JSON file that defines all game rules: terrain, units, buildings, factions, combat, and vision. The default ruleset is “Classic TinyGenerals.”
Ruleset Structure
Section titled “Ruleset Structure”{ "id": "my-ruleset", "name": "My Custom Ruleset", "faction_mode": "optional", "faction_selection": { "required": false, "default_behavior": "prompt", "allow_mirror_matches": true, "auto_balance": false }, "factions": { ... }, "terrain_types": { ... }, "overlay_types": { ... }, "unit_types": { ... }, "building_types": { ... }, "vision_rules": { ... }, "combat_rules": { ... }}Faction Mode
Section titled “Faction Mode”| Value | Behavior |
|---|---|
"none" | No factions. All players use the shared unit_types. |
"optional" | Players may pick a faction for unique units. |
"required" | Every player must select a faction before the game starts. |
Shared vs Faction Units
Section titled “Shared vs Faction Units”The top-level unit_types defines generic fallback units used only when no faction is selected. In faction games, each faction’s own unit_types completely replaces any shared unit with the same key:
{ "unit_types": { "INFANTRY": { "name": "Infantry", "role": "INFANTRY", "health": 10, ... }, "VEHICLE": { "name": "Vehicle", "role": "ARMOR", "health": 15, ... } }, "factions": { "ALLIANCE": { "unit_types": { "INFANTRY": { "name": "Militia", "role": "INFANTRY", "health": 10, ... }, "VEHICLE": { "name": "Battle Tank", "role": "ARMOR", "health": 18, ... }, "HOWITZER": { "name": "Howitzer", "role": "RANGED", "health": 10, ... } } } }}An Alliance player gets Militia, Battle Tank, and Howitzer — never the generic Infantry or Vehicle.
Unit Archetypes (Roles & Tiers)
Section titled “Unit Archetypes (Roles & Tiers)”Maps are faction-agnostic — the map creator doesn’t know which factions will play. Spawn points use archetypes (a role + power tier) that resolve to the correct faction unit at game start.
Every unit definition includes a role field:
"HOWITZER": { "name": "Howitzer", "role": "RANGED", "health": 10, "min_attack_range": 2, "max_attack_range": 4, ...}| Role | Description | Current units |
|---|---|---|
INFANTRY | Foot soldiers, can capture buildings | Militia, Trooper, Shock Trooper |
ARMOR | Heavy armored vehicles (tanks) | Battle Tank, Assault Tank |
RANGED | Long-range indirect fire (artillery) | Howitzer, Rocket Battery |
ANTI_ARMOR | (Reserved) Anti-tank / tank destroyers | — |
RECON | (Reserved) Fast scouts with high vision | — |
VEHICLE | (Reserved) Light vehicles (APCs, IFVs) | — |
ENGINEER | (Reserved) Builders, repair, field fortifications | — |
AIR_DEFENSE | (Reserved) Anti-air units | — |
FIGHTER | (Reserved) Air superiority aircraft | — |
BOMBER | (Reserved) Ground attack aircraft | — |
NAVAL | (Reserved) Surface warships | — |
Units in the same role can have different power tiers:
"SHOCK_TROOPER": { "name": "Shock Trooper", "role": "INFANTRY", "tier": 2, "move_points": 4, "allow_move_after_attack": true, ...}| Tier | Meaning | Example |
|---|---|---|
| 1 (default) | Basic unit | Trooper |
| 2 | Advanced variant | Shock Trooper |
| 3 | Elite variant | (future) |
Units without a tier field are tier 1.
Spawn Resolution
Section titled “Spawn Resolution”When a game starts, each spawn point resolves like this:
- Direct match — If the spawn value is a unit ID (e.g.
"HOWITZER"), use it directly - Role + tier scan — Find a faction unit with matching
roleandtier - Tier fallback — No match at requested tier? Try the next lower tier, down to T1
- Last resort — No unit matches the role at all? Spawn the faction’s Infantry
Example: A map spawn is set to INFANTRY_2 (Infantry, tier 2):
| Faction | Has T2 infantry? | Result |
|---|---|---|
| Federation | Yes (Shock Trooper) | Spawns Shock Trooper |
| Alliance | No | Falls back to T1 → Spawns Militia |
Using Archetypes in the Map Editor
Section titled “Using Archetypes in the Map Editor”In the map editor’s Spawns mode:
- Select a player slot (P1–P8)
- Pick a role — Infantry, Armor, or Artillery
- Pick a tier — T1, T2, or T3
- Click hexes to place spawn points
The spawn marker shows the role letter and tier (e.g. I for Infantry T1, R2 for Artillery T2).
Creating a Faction
Section titled “Creating a Faction”A faction lives inside the ruleset’s factions block. Here’s the minimal structure:
"MY_FACTION": { "id": "MY_FACTION", "name": "The Ironclads", "description": "Heavy defensive faction with powerful armor.", "version": "1.0.0", "difficulty": "intermediate", "playstyle_tags": ["defensive", "armor-heavy"], "emblem": { "type": "emoji", "value": "🛡️" }, "asset_pack": "default", "unlock_requirements": { "default_unlocked": true, "premium_only": false, "required_trophies": [] }, "preview": { "tagline": "Unbreakable line", "showcase_units": ["INFANTRY", "HEAVY_TANK"] }, "unit_types": { "INFANTRY": { "name": "Garrison", "role": "INFANTRY", "symbol": "G", "health": 12, "move_points": 2, "attack": 3, "soft_attack": 3, "hard_attack": 2, "target_type": "SOFT", "vision_range": 2, "can_traverse_all": true, "can_capture": true, "cost": 150, "min_attack_range": 1, "max_attack_range": 1, "max_attacks_per_turn": 1, "allow_attack_after_move": true } }, "building_types": {}, "bonuses": { "starting_gold_bonus": 0, "income_multiplier": 1.0, "unit_cost_multiplier": 1.0, "vision_range_bonus": 0 }}Unit Definition Fields
Section titled “Unit Definition Fields”| Field | Type | Description |
|---|---|---|
name | string | Display name (also used for sprite lookup) |
role | string | Archetype — see roles table above |
tier | int | Power tier within role (omit or 0 for tier 1) |
symbol | string | 1-2 character fallback when no sprite is available |
health | int | Maximum hit points |
move_points | int | Movement points per turn |
attack | int | Legacy attack value |
soft_attack | int | Damage vs soft targets (infantry) |
hard_attack | int | Damage vs hard targets (vehicles) |
target_type | string | "SOFT" or "HARD" — what this unit counts as when defending |
vision_range | int | How far this unit can see (in hexes) |
can_traverse_all | bool | Can cross mountains and other impassable-to-vehicles terrain |
can_capture | bool | Can capture buildings |
cost | int | Gold cost to produce |
min_attack_range | int | Minimum attack range (1 = adjacent) |
max_attack_range | int | Maximum attack range (1 = melee only) |
attack_cost | int | Movement points consumed per attack (0 = free) |
max_attacks_per_turn | int | How many times this unit can attack per turn |
allow_move_after_attack | bool | Can move after attacking |
allow_attack_after_move | bool | Can attack after moving |
Faction Checklist
Section titled “Faction Checklist”- Include at least one unit with
"role": "INFANTRY"— this is the last-resort spawn fallback - Cover the core roles (
INFANTRY,ARMOR,RANGED) so maps with varied archetypes work - Give each unit a unique
symbol(1-2 characters) for the fallback renderer - Faction bonuses are multipliers —
1.0means no change from baseline
Themes
Section titled “Themes”Themes control the visual appearance: terrain tiles, unit sprites, building graphics, and animations. The game ships with a default theme and falls back to colored shapes for any missing assets.
Theme Structure
Section titled “Theme Structure”A theme is a directory containing a manifest and sprite subdirectories:
my-theme/ theme.json ← Manifest (declares assets and animations) terrain/ GRASS_1.png GRASS_2.png FOREST_1.png ... units/ MILITIA.png BATTLE_TANK.png HOWITZER.png ... buildings/ BASE.png FACTORY.png ...Unit Sprite Naming
Section titled “Unit Sprite Naming”Sprites are matched by the unit’s name field (uppercased, spaces → underscores):
| Unit name | Sprite filename |
|---|---|
| Militia | MILITIA.png |
| Battle Tank | BATTLE_TANK.png |
| Shock Trooper | SHOCK_TROOPER.png |
If the name-based sprite isn’t found, the game tries the unit type ID (e.g. INFANTRY.png). If neither exists, it renders a colored circle with the unit’s symbol text.
For detailed sprite dimensions and hex geometry, see the Graphics Guide.
Quick Reference
Section titled “Quick Reference”Current Archetype Map
Section titled “Current Archetype Map”| Role | Alliance T1 | Alliance T2 | Federation T1 | Federation T2 |
|---|---|---|---|---|
| Infantry | Militia | — | Trooper | Shock Trooper |
| Armor | Battle Tank | — | Assault Tank | — |
| Artillery | Howitzer | — | Rocket Battery | — |
Spawn Resolution Flow
Section titled “Spawn Resolution Flow”Map spawn: "RANGED_2" (Artillery, tier 2) │ ├─ Faction has RANGED tier 2? → Yes → Spawn it │ → No → Try tier 1 │ ├─ Faction has RANGED tier 1? → Yes → Spawn it │ → No → Spawn Infantry T1 │ └─ DoneAs new units and factions are added, the archetype system picks them up automatically — existing maps don’t need updates.