Terrain#
This page shows how to give cells their behaviour: what blocks walking or sight, what a door does, what a trap does, and how a spell reacts to what it hits.
What a cell contains#
A cell holds, in this order: the player, creatures, floor hazards, spell fields, traps, then the terrain itself. Every occupant behaves through its components: small named behaviours such as blocksMovement or flammable. A component describes what something does, never what it is called. A web is flammable; it is not "a web that burns".
Terrain components live in public/config/world.json, under terrain. Every terrain must list its components. An empty list is open, see-through floor. Creature families take theirs from creatures.json, and hazards from the hazards section of creature-rules.json.
Components#
| Component | Parameters | What it does |
|---|---|---|
blocksMovement | none | Nobody can walk in. |
blocksSight | none | Sight stops behind the cell. |
dampensSound | factor, 0 to 100 | Keeps that percentage of a noise passing through. |
flammable | none | Fire can destroy it. |
slowsMovement | cost, 2 to 10 | A step costs that many steps for a creature. |
consumedOnTrigger | none | Used up when it triggers. |
regenerates | duration, 1 to 20 | The creature regenerates; fire stops it for that many turns. |
onEnter, onStay | actions | Actions on whoever enters, or stays each turn. |
interact | actions, optional reach and priority | Actions the player triggers by hand. |
Water declares only blocksMovement: you cannot cross it, but you can see the far bank. Walls add blocksSight.
Goal: a floor type that slows creatures and burns away.
[{ "component": "flammable" }, { "component": "slowsMovement", "cost": 2 }]
A creature crossing the cell spends two steps on it, and a fire spell that reaches it removes it.
Hazards are declared the same way. A frost hazard freezes whoever stays on its cell:
{ "components": [{ "component": "onStay", "actions": [{ "kind": "status", "status": "frozen", "turns": 2 }] }] }
Advanced: cell actions
onEnter and onStay accept hurt, burn, status (burning, frozen or slowed, for 1 to 50 turns), destroy and suppressRegeneration. The full list with parameters is in the catalogue.
Doors#
A closed door blocks movement and sight, muffles sound, and has an interact component that opens it into another terrain.
Goal: a closed door that the player opens from the next cell.
[{ "component": "blocksMovement" }, { "component": "blocksSight" }, { "component": "interact", "reach": "adjacent", "actions": [{ "kind": "operateDoor", "operation": "open", "tile": "doorOpen" }] }]
Interacting with the door turns it into doorOpen, which blocks neither walking nor sight. It costs one turn and makes a small noise.
tilemust exist in the terrain list and differ from the door itself.reach: "adjacent"means the door is on a neighbouring cell. The default,self, targets the cell under the player.- A locked door uses
forceDoorinstead. It also needs adoorForcingblock inworld.json.
The game places doors when it generates floors. This extract from world.json sets a 25% chance on the first floor, growing by 2.5% per floor:
{ "doorGeneration": { "tile": "door" }, "balance": { "doorChance": 0.25, "doorGrowth": 0.025 } }
Doors only go on narrow room thresholds, and every room keeps at least one free entrance. Add lockedTile and lockedDoorChance to turn some of them into locked doors.
Devices#
A device is something the player places, such as a trap. Devices are declared in devices.json. A device sets its glyph, its trigger and its components. The trigger is enter, turn or adjacent.
Goal: a proximity mine that slows anyone next to it for three turns.
{
"proximity": {
"name": "TACTICS_SYSTEM_TRAP",
"glyph": "^", "color": "#90e9ec",
"trigger": "adjacent", "charges": 3, "turns": 3,
"components": [{"component": "onStay", "actions": [
{"kind": "status", "status": "slowed", "turns": 3}
]}]
}
}
Each turn, anyone on the eight cells around the device is slowed. Every victim uses one of its three charges, and the device is spent when they run out.
- The player's own devices hit the player too.
enterreadsonEnter;turnandadjacentreadonStay.allowedTerrainlimits where it can be placed.
Interactions with spells#
An interaction says what happens when a spell of some element touches an occupant that has a component. Interactions live in magic.json, under magic.interactions.
Goal: fire destroys anything flammable.
{ "effect": { "element": "fire" }, "component": "flammable", "actions": [{ "kind": "destroy" }] }
A fire spell that lands on a web removes the web. Any element declared in magic.elements can have such a rule. Interactions add reactions on top of the spell; they do not replace it, and they do not wound.
See Magic for elements.
Named fields#
A named field is a lasting zone that a spell leaves behind. Declare it in magic.json under magic.namedFields, then reference it from the field action.
{
"opaque-air": {
"name": "MAGIC_SYSTEM_AIR", "glyph": "◇", "color": "#a9baaf",
"power": 0, "elements": [],
"components": [{ "component": "blocksSight" }]
}
}
The field shows as a grey diamond and blocks sight until it expires.
Fields accept blocksMovement, blocksSight, dampensSound, flammable, slowsMovement, consumedOnTrigger, onEnter and onStay. They refuse interact and regenerates.
If the game refuses your cells at startup, see Troubleshooting.