Map Object Reference
This document covers every object type you can place in a STUG map and the properties each one supports. Maps are .smf files (zip archives containing a project.json and optional assets like a node graph).
Creating and Testing Maps
Maps are created in the map editor, available at /editor on the game site. The editor saves maps as .smf files and publishes them, where they become available to play in-game through custom games (Or normal games after approval on the STUG Discord.)
Conventions
Unless a property says otherwise:
| Convention | Value |
|---|---|
| Distances & sizes | Pixels |
| Positions | x/y in pixels from the map’s top-left; +Y is down (screen coordinates) |
Force/impulse inputs (applyForce, applyImpulse) and pushAngle |
+Y is up (math coordinates): 0=right, 90=up, 180=left, 270=down |
| Rotations | Degrees in map properties and setProperty; a few internal values (e.g. turretRotationLimit) are radians and are marked as such |
| Time | Seconds for gameplay timing (fuseTime, respawnTime, interval, wire delays); milliseconds for client visual timing (maxLifetime, animateInterval, reloadTime); marked per property |
| Ticks | The server simulates 15 ticks per second, so 1 tick ≈ 67ms. Properties measured in ticks (e.g. damageInterval) are marked as such |
| Teams | 0 and 1 for the two teams, -1 for neutral/all |
Map-Level Properties
These are set on the map project itself, not on individual objects.
| Property | Type | Default | Description |
|---|---|---|---|
brightness |
number | 1.4 |
Ambient light brightness. 0.0 makes the map very dark. |
lightColor |
string | #c4b4a8 |
Ambient light color as a hex string. |
disableVision |
boolean | false |
Turns fog of war off for this map: everyone sees everything. |
visionRange |
number | 1250 |
How far (px) each player sees. Clamped to 200–20000. |
visionWallsBlock |
boolean | false |
Solid walls and objects block line of sight (see blocksVision on walls/objects to exempt one). |
visionTeamShared |
boolean | false |
Teammates’ vision counts as yours. |
visionTeammatesVisible |
boolean | false |
true makes teammates always visible, even out of range. By default they are culled like enemies. |
Vision properties override the server defaults for the duration of the map; a server admin’s /vision command overrides both.
The map’s width and height are part of the project settings in the editor, not properties. Other custom map-level properties are stored and accessible to gamemode code but have no built-in effect.
Common Properties
These properties can be set on any object type.
| Property | Type | Description |
|---|---|---|
gamemodes |
string | Comma-separated list of gamemode types this object should appear in. If omitted, the object loads in all gamemodes. Example: conquest,frontline. |
zIndex |
number | Render depth override. Auto-calculated from the object’s layer if not set. Higher values render on top. |
Object Types
static_wall
Solid wall with collision. Blocks movement and projectiles.
Object-level fields (set on the object itself, not in properties):
- Position, size, rotation
- Tile/texture, tileset
- Polygon vertices (for non-rectangular shapes)
- Tint color
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
disableCollisions |
boolean | false |
Removes all collision. The wall becomes purely visual. |
disableHitboxCollision |
boolean | false |
Projectiles pass through but vehicles still collide. |
blocksVision |
boolean | true |
Set false so the wall never blocks line of sight when the server runs fog of war in walls mode (a low fence you can see over). |
alpha |
number | 1 |
Transparency (0 = invisible, 1 = opaque). Set via shapeStyle in the editor. |
repeating |
boolean | false |
Tile the texture across the shape. Set via shapeStyle in the editor. |
tilePosition |
object | Offset for repeating textures {x, y}. |
|
tileScale |
object | Scale for repeating textures {x, y}. |
Objects without a key default to static_wall with disableCollisions: true (decoration only).
I/O Inputs:
| Input | Description |
|---|---|
setAlpha(value) |
Changes the wall’s transparency on clients (e.g., wall1:setAlpha:0:0.5). |
setTint(value) |
Changes the wall’s color tint on clients. Value is a hex string #RRGGBB. |
When disabled via I/O, a wall’s collision is removed (passthrough) and clients render it semi-transparent; enabling restores it. See Enable/Disable Behavior Per Type.
spawn_protection
Invisible barrier that blocks projectiles from one team’s spawn area. Vehicles can pass through. Only collides with the hitbox collision group (projectiles), not players.
Object-level fields: Same as static_wall (position, size, rotation, tile, polygon, tint).
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
team |
number | Team that this spawn protection belongs to. Projectiles from the same team pass through. |
static_terrain
Terrain zone that affects vehicles driving over it. Uses sensor collision (vehicles enter/exit the zone but aren’t blocked).
Object-level fields: Same as static_wall (position, size, rotation, tile, polygon, tint).
Properties:
| Property | Type | Description |
|---|---|---|
terrainType |
string | Required. The terrain effect to apply. See values below. |
Terrain types:
| Value | Effect |
|---|---|
water |
Increases damping (slows vehicles). Wheeled vehicles are less affected than tracked. |
ice |
Reduces engine/turn force and greatly reduces friction. Vehicles slide and drift. |
mud |
Heavy slowdown with severely reduced engine and turning power. Tracked vehicles handle mud better than wheeled. |
sand |
Moderate slowdown with mild engine/turn reduction. Less punishing than water or mud. Tracked vehicles are less affected. |
road |
Reduced friction for faster movement. Wheeled vehicles benefit more than tracked. |
pit |
Vehicles that stop moving over a pit fall in and are destroyed. |
Properties — Physics Overrides:
Any terrain instance can override its type’s default physics values. If omitted, the terrain type’s built-in values apply.
| Property | Type | Default | Description |
|---|---|---|---|
damping |
number | per terrain type | Override body damping. |
angularDamping |
number | per terrain type | Override angular damping. |
engineForceMod |
number | per terrain type | Override engine force multiplier (0-1). |
turnForceMod |
number | per terrain type | Override turn force multiplier (0-1). |
sideFrictionMod |
number | per terrain type | Override side friction multiplier (0-1). |
Properties — Damage:
| Property | Type | Default | Description |
|---|---|---|---|
damage |
number | Damage dealt to vehicles inside the zone. Applied once on enter; repeats if damageInterval is set. |
|
damageInterval |
number (ticks) | Ticks between repeated damage (15 ticks = 1 second). E.g. 15 deals damage once per second. If omitted, damage is one-shot on enter only. |
I/O Inputs:
| Input | Description |
|---|---|
setTerrainType(value) |
Changes the terrain type at runtime (e.g. water, mud), updating both server state and clients. |
setDamage(value) |
Changes the per-interval damage the terrain deals to vehicles on it. |
I/O Outputs:
| Output | Fires When |
|---|---|
onVehicleEnter |
A vehicle enters the terrain area (activator = the vehicle). |
onVehicleLeave |
A vehicle leaves the terrain area (activator = the vehicle). |
When disabled via I/O, the terrain stops detecting overlaps (no effects, no damage).
entity_hitbox
Not a real entity. Defines a reusable collision shape that dynamic_object entities can reference by name. Set the object’s name to an identifier, then reference it from a dynamic object’s hitbox property.
Supports polygon shapes. Rectangle hitboxes use the dynamic object’s own dimensions.
entity_spawn
Spawn point for players. Not rendered in-game.
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
team |
number | Required. | Team this spawn belongs to. 0 or 1 for team spawns, -1 or negative for random/FFA spawns. |
type |
string | If set, marks this as a “special” spawn accessible by gamemodes via teamData[team].specialSpawns[type]. Used for gamemode-specific spawn logic. |
Spawn points are shuffled each round. The object’s x, y, and rotation determine where and which direction the player faces.
I/O Inputs: (only available on spawn points with a name set)
| Input | Description |
|---|---|
setTeam(value) |
Reassigns this spawn point to a different team at runtime. Moves it between teamData[team].spawns (or specialSpawns[type] if a type is set) so future spawns use the new team. |
I/O Outputs: (only available on spawn points with a name set)
| Output | Fires When |
|---|---|
onPlayerSpawned |
A player spawns at this specific spawn point (activator = the spawned vehicle). |
Only named spawn points participate in Entity I/O — a backing logic entity is created for them at map load. Unnamed spawn points work normally but cannot be wired.
entity_flag
Flag for Capture the Flag mode. Only created when the active gamemode is capture_the_flag.
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
team |
number | Required. | The team this flag belongs to. Enemy players pick it up; friendly players return it. |
Place one flag per team. Players grab the enemy flag and bring it to their own capture point to score.
I/O Inputs:
| Input | Description |
|---|---|
drop |
Forces the current carrier to drop the flag (it stays where dropped until returned or picked up). No-op if no one is carrying it. |
returnFlag |
Immediately returns the flag to its home position. |
I/O Outputs:
| Output | Fires When |
|---|---|
onPickedUp |
An enemy player picks up the flag (activator = the carrier). |
onDropped |
The flag is dropped — carrier killed, left the field, or via the drop input (activator = the last carrier). |
onReturned |
A friendly player returns a dropped flag to base (activator = the returning player). |
onCaptured |
The flag is captured at the carrier’s capture point (activator = the carrier). |
onReset |
The flag resets to its home position (after a capture, timeout, or returnFlag). |
entity_capture_point
Capture zone for Conquest, Frontline, and CTF gamemodes. Sorted left-to-right by x position and auto-indexed.
Defaults to gamemodes: conquest,frontline if no gamemodes property is set.
Object-level fields:
name— Display name shown in the HUD.
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
team |
number | -1 |
Starting owner team. -1 for neutral. |
radius |
number | 300 |
Capture radius in pixels. Vehicles inside this radius contest/capture the point. Also used as the flag delivery radius in CTF. |
captureSpeed |
number | 1 |
Multiplier on how fast the point is captured and decapped. 2 = twice as fast, 0.5 = half speed. Scales all vehicle types equally, preserving the light/medium/heavy balance. Works in every gamemode that uses capture points. |
pointValue |
number | 1 |
(Conquest only) Score awarded to the owning team each time this point ticks while fully held. Lets you make high-value objectives. |
scoreInterval |
number | 7500 |
(Conquest only) Milliseconds between score awards while the point is held. Lower = scores more often. The interval restarts whenever the point is lost and recaptured. |
I/O Inputs:
| Input | Description |
|---|---|
resetCapture |
Resets capture percentage to 0 and team to neutral (-1). |
setTeam(value) |
Sets the owning team and capture percentage (1 = fully captured, -1 = reset). |
setProgress(value) |
Sets capture progress directly. Clamped to 0–1. |
lock |
Freezes the point — capture progress can’t change and contesting vehicles are ignored until unlocked. |
unlock |
Re-enables normal capturing after a lock. |
I/O Outputs:
| Output | Fires When |
|---|---|
onCaptured |
A team fully captures the point. |
onStartCapture |
An enemy team begins capturing the point (activator = the first capturing vehicle). |
onContested |
Two or more teams are present in the radius at once (activator = null). |
onNeutralized |
An owned point is decapped down to 0% (activator = the decapping vehicle). |
vehicle
Spawns any vehicle type on the map. Supports optional AI control and full tankData customization via map properties.
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
team |
number | -1 |
Team the vehicle belongs to. -1 for neutral. |
tankType |
string | medium_tank |
Vehicle type to spawn. Any valid vehicle type from vehicle_list.js. |
enableAI |
boolean | false |
Attaches bot AI that auto-targets enemies. |
range |
number | 500 |
Maximum AI firing range in pixels (only applies when AI is enabled). |
skill |
number | 1 |
AI accuracy from 0 (very inaccurate) to 1 (perfect aim with lead prediction). |
disableMovement |
boolean | false |
Prevents the vehicle from moving. |
disableRoaming |
boolean | false |
Prevents autonomous AI roaming. The AI stays in place until it acquires a target, then pursues normally. |
perfectLeadAi |
boolean | false |
Enables perfect projectile lead prediction for AI. |
disableFiring |
boolean | false |
Prevents the vehicle from shooting. |
disableOutlines |
boolean | false |
Hides the selection outline around the vehicle. |
health |
number | If set, the vehicle becomes destructible with this much health. Auto-repair is disabled. If omitted, the vehicle is invincible (default). | |
ignoreTerrainShot |
boolean | false |
Allows projectiles to fire through terrain the vehicle is inside of. |
scale |
number | 1 |
Size multiplier. Values > 1 make the vehicle larger, < 1 smaller. Affects physics and rendering. |
skin |
string | Skin key from skin_list.js (e.g. skin-olive, skin-woodland). Applies the texture cosmetic to the vehicle. |
|
grantAbilitiesOnKill |
boolean | false |
When true, killing this AI vehicle grants the attacker killstreak ability progress (same as killing a player). Only relevant for destructible AI vehicles. |
TankData overrides — These properties override the vehicle’s base stats from vehicle_list.js. They can be set as initial map properties or changed at runtime via setProperty:
| Property | Type | Description |
|---|---|---|
engineForce |
number | Forward engine power. |
engineForceWithTurn |
number | Engine power while turning. |
engineBoostForce |
number | Boost engine power. |
engineBoostTime |
number | Boost duration (ms). |
engineBoostAngularDamping |
number | Angular damping during boost. |
turnForce |
number | Stationary turn power. |
turnForceWithEngine |
number | Turn power while moving. |
mass |
number | Vehicle mass (also updates physics body mass). |
reverseMod |
number | Reverse speed multiplier. |
reloadTime |
number | Time between shots (ms). |
damageMod |
number | Outgoing damage multiplier. |
damagePenMod |
number | Penetration damage multiplier. |
fireForce |
number | Recoil force when firing. |
barrelLength |
number | Barrel length in pixels. |
armorMod |
number | Front armor multiplier. |
armorModSide |
number | Side armor multiplier. |
armorModRear |
number | Rear armor multiplier. |
armorPenMod |
number | Armor penetration multiplier. |
turretSpeed |
number | Turret rotation speed. |
turretRotationLimit |
number | Max turret rotation angle (radians). |
Vehicles also support the base entity fields (damping, angularDamping, velocityX, velocityY, angularVelocity) as initial map properties — see setProperty — Base Entity Fields below.
I/O Inputs:
| Input | Description |
|---|---|
repair(value) |
Heals the vehicle by value HP, capped at its max health. No-op if dead. |
setLoaded(value) |
Sets the loaded/reloaded state. Truthy values mark the weapon ready to fire immediately. |
fire |
Fires the vehicle’s weapon. |
I/O Outputs:
| Output | Fires When |
|---|---|
onKilled |
Vehicle is killed |
onDamaged |
Vehicle takes damage but is not killed |
onFired |
The vehicle fires its weapon (activator = the vehicle) |
onReloaded |
The vehicle finishes reloading and is ready to fire again |
onPlayerEntered |
A player takes control of this vehicle via switchPlayerTo (activator = the entering player) |
onPlayerExited |
A player relinquishes control via ejectPlayer |
AI I/O — the following inputs and outputs are available on AI-controlled vehicles: map-placed vehicles with enableAI, turrets, and AI units spawned by gamemodes or killstreaks (such as zombies and the colossus).
AI Outputs:
| Output | Activator | Fires When |
|---|---|---|
onTargetAcquired |
New target entity | AI acquires a new target (or switches to a different one) |
onTargetLost |
Previous target entity | AI loses its target (e.g. target died or was cleared) |
AI Inputs — Targeting:
| Input | Value | Description |
|---|---|---|
setTarget(value) |
Entity name or activator |
Force AI to target a specific entity. Locks the target so AI won’t override it. Empty value clears the target. |
clearTarget |
— | Clears the current target and unlocks targeting so AI resumes normal scanning. |
lockTarget |
— | Prevents AI from switching away from the current target. |
unlockTarget |
— | Allows AI to resume normal target scanning. |
getTarget |
— | Fires onTarget output with the current target as activator. |
AI Inputs — Movement:
| Input | Value | Description |
|---|---|---|
goToPosition(value) |
"x,y" or "x y" |
Pathfind to world coordinates. Ignored if movement is disabled. |
goToEntity(value) |
Entity name or activator |
Pathfind to an entity’s current position. |
setMovement(value) |
true / false |
Enable or disable all AI movement (roaming + pursuit). Disabling clears the current path. |
setRoaming(value) |
true / false |
Enable or disable autonomous roaming. When disabled, the AI stays in place until it has a target, then pursues normally. Also settable via disableRoaming map property. |
AI Inputs — Firing & Stats:
| Input | Value | Description |
|---|---|---|
setFiring(value) |
true / false |
Enable or disable AI firing. Unlike disable, the AI still thinks, tracks, and moves. |
setRange(value) |
Number (pixels) | Change AI view/firing range. |
setSkill(value) |
Number 0–1 | Change AI accuracy. Clamped to [0, 1]. |
turret
Backwards-compatible alias for vehicle with turret-specific defaults. Equivalent to a vehicle with enableAI: true, disableMovement: true, perfectLeadAi: true, ignoreTerrainShot: true, and tankType: turret.
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
team |
number | Required. | Team the turret fights for. |
tankType |
string | turret |
Vehicle type to use. Can be turret, turret_artillery, or any valid vehicle type. |
range |
number | 500 |
Maximum firing range in pixels. |
disableFiring |
boolean | false |
Prevents the turret from shooting. Useful for decorative or gamemode-toggled turrets. |
disableOutlines |
boolean | false |
Hides the selection outline around the turret. |
health |
number | If set, the turret becomes destructible with this much health. Auto-repair is disabled. If omitted, the turret is invincible (default). | |
skill |
number | 1 |
AI accuracy from 0 (very inaccurate) to 1 (perfect aim with lead prediction). |
disableMovement |
boolean | true |
Can be set to false to allow the turret to move. |
disableRoaming |
boolean | false |
Prevents autonomous AI roaming. The AI stays in place until it acquires a target, then pursues normally. |
perfectLeadAi |
boolean | true |
Can be set to false to disable perfect lead prediction. |
grantAbilitiesOnKill |
boolean | false |
When true, killing this turret grants the attacker killstreak ability progress. Only relevant for destructible turrets (with health set). |
Turrets also support all tankData override properties and all I/O inputs/outputs (including AI I/O) listed under vehicle. When disabled via I/O, a turret stops firing and thinking entirely; enabling resumes the AI.
dynamic_object
Interactive object with physics. Used for destructible props, pickups, ball game objects, and more.
Object-level fields:
- Position, size, rotation
- Tile/texture, tileset
- Tint color
Properties — Physics:
| Property | Type | Default | Description |
|---|---|---|---|
mass |
number | 0 |
Physics mass. 0 = static (immovable). Positive values make the object pushable. |
damping |
number | 0.5 |
Linear velocity damping. 0 = no drag (frictionless), 1 = maximum drag (stops instantly). Controls how quickly the object decelerates. |
angularDamping |
number | 0.5 |
Angular velocity damping. 0 = no rotational drag, 1 = stops rotating instantly. |
velocityX |
number | 0 |
Initial horizontal velocity. |
velocityY |
number | 0 |
Initial vertical velocity. |
angularVelocity |
number | 0 |
Initial rotational velocity. |
fixedRotation |
boolean | false |
Locks the body’s rotation — the object can move but never spins. Useful for carts, platforms, sliding doors. |
fixedX |
boolean | false |
Locks horizontal movement — the object can only move vertically. |
fixedY |
boolean | false |
Locks vertical movement — the object can only move horizontally. |
collisionResponse |
boolean | true |
When false, the body passes through other bodies but still generates contact events (onCollisionEnter/onCollisionLeave). |
shape |
string | rectangle |
Collision shape: rectangle, circle, or polygon. |
radius |
number | 32 |
Radius when shape is circle. |
sizeOffset |
number | 0 |
Shrinks the rectangle collision shape by this many pixels. |
hitbox |
string | Name of an entity_hitbox object to use for collision shape. Overrides shape. |
|
polygonScale |
number | 1 |
Scale multiplier for polygon vertices. |
material |
string | standard |
Physics material. Available: standard, ball. |
isSensor |
boolean | false |
Makes the shape a sensor (detects overlap but no physical collision). |
disablePlayerCollision |
boolean | false |
Vehicles pass through this object. |
disableHitboxCollision |
boolean | false |
Projectiles pass through this object. |
blocksVision |
boolean | true |
Set false so the object never blocks line of sight when the server runs fog of war in walls mode. |
Properties — Damage & Health:
| Property | Type | Default | Description |
|---|---|---|---|
team |
number | -1 |
Team affiliation. Same-team shots don’t deal damage. |
health |
number | If set, the object is destructible with this much health. If omitted, the object ignores projectile damage entirely. | |
canRunOver |
boolean | false |
Vehicles can destroy this object by driving over it. Heavier vehicles crush it instantly; lighter ones deal 5 damage per hit. |
disableDestructionOnDeath |
boolean | false |
When destroyed, remove the physics body but keep the entity (for visual death effects). Only applies when health or canRunOver is set. |
despawnTime |
number | Auto-remove the object after this many seconds. | |
hitEffect |
string | Set to vehicle to show the vehicle-hit sparks effect when shot. |
Properties — Respawn:
| Property | Type | Default | Description |
|---|---|---|---|
respawnTime |
number | Seconds before the object respawns after being destroyed or picked up. If omitted, the object does not respawn. |
Properties — Push Zone:
| Property | Type | Default | Description |
|---|---|---|---|
pushForce |
number | Force magnitude applied each tick to bodies inside the zone. Use with isSensor: true. |
|
pushAngle |
number (degrees) | entity rotation | Fixed push direction. If omitted, force pushes in the direction the object is rotated. Directions: 0=right, 90=up, 180=left, 270=down. |
Properties — Pickup:
Setting pickupType turns the object into a consumable pickup.
| Property | Type | Default | Description |
|---|---|---|---|
pickupType |
string | Pickup category. health restores vehicle health. Other values are handled by gamemode hooks (onPickup). |
|
pickupAmount |
number | Amount to restore/grant. For health, this is HP restored. |
|
pickupRadius |
number | 40 |
Detection radius for pickup collection. |
pickupPlayersOnly |
boolean | false |
Only human players can collect (bots cannot). |
Properties — Visual (client-side):
| Property | Type | Default | Description |
|---|---|---|---|
alpha |
number | 1 |
Transparency. |
frameX |
number | Starting X frame index for spritesheet textures. | |
frameY |
number | Starting Y frame index for spritesheet textures. | |
fadeFrameX |
number | Frame to show during fade/death animation. | |
animateMovement |
boolean | false |
Advance animation frames based on movement speed. |
animateDamage |
boolean | false |
Advance animation frames based on damage taken. |
animateInterval |
number (ms) | Override the tileset’s default animation interval. Lower values = faster animation. | |
animateStartFrame |
number | 0 |
Starting frame index into the tileset animation sequence. |
animateStartPaused |
boolean | false |
Start with the tileset animation paused. Use with playAnimation IO input to trigger playback. |
animatePingPong |
boolean | false |
Reverse animation direction at each end of the sequence instead of looping. |
animatePlayOnce |
boolean | false |
Play the animation once and pause on the last frame. Use playAnimation IO input to replay. |
effect |
string | Visual effect. burning adds a fire particle effect. |
|
enableFireOnDamaged |
boolean | false |
Show fire effect when health is low. |
enableShadow |
boolean | false |
Render a drop shadow beneath the object. |
enableArrowPointer |
boolean | false |
Show an off-screen arrow indicator pointing to this object. |
parallaxX |
number | Horizontal parallax multiplier (for background layers). | |
parallaxY |
number | Vertical parallax multiplier (for background layers). | |
zOffset |
number | Additional z-index offset for fine-tuning render order. |
Properties — Gamemode:
| Property | Type | Description |
|---|---|---|
objectType |
string | Classification tag read by gamemodes. Tank Ball uses ball and goal. Accessible via server.mapDataServer.dynamicObjects[objectType]. |
I/O Inputs:
| Input | Description |
|---|---|
setAlpha(value) |
Changes the object’s transparency on clients |
setFrame(value) |
Set tileset animation to a specific frame index (into tileset.animate.sequence). Updates texture immediately. |
playAnimation |
Resume time-based animation cycling (resets frame timer) |
pauseAnimation |
Freeze animation on the current frame |
setAnimationInterval(value) |
Override the tileset’s animation interval in milliseconds. Pass 0 to reset to the tileset default. |
setTint(value) |
Changes the object’s color tint on clients. Value is a hex string #RRGGBB. |
teleport(value) |
Moves the object to world coordinates. Value is "x y" (space-separated). Zeroes velocity. |
respawn |
Removes the object and queues it to respawn at its original map position. Requires the object to have a respawn map source. |
setPushForce(value) |
Changes the push-zone force magnitude at runtime (for sensor push zones). |
setPushAngle(value) |
Changes the push-zone direction at runtime (degrees: 0=right, 90=up, 180=left, 270=down). |
I/O Outputs:
| Output | Fires When |
|---|---|
onDestroyed |
Health reaches 0 and entity is killed |
onDamaged |
Entity takes damage but is not killed |
onPickedUp |
A vehicle picks up the object (pickup type objects) |
onTriggerEnter |
A vehicle enters a sensor object’s collision area |
onTriggerLeave |
A vehicle leaves a sensor object’s collision area |
onRespawned |
The object respawns (via respawnTime) — fires on the newly created entity. |
When disabled via I/O, the object’s collisions, pickups, push zones, and sensor detection all stop, and clients render it semi-transparent.
constraint
Physics constraint connecting two named entities. Place the constraint object in the editor at the desired pivot/anchor position — its world coordinates automatically become the default local pivot on each connected body.
A constraint is a logic entity (no physics body, no rendering). It supports Entity I/O: enabling creates the physics constraint, disabling removes it. Use startDisabled to defer creation until triggered.
Properties:
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
constraintType |
string | Yes | — | Constraint type: hinge, rope, slider, weld, or gear. |
bodyA |
string | Yes | — | name of the first connected entity. |
bodyB |
string | Yes | — | name of the second connected entity. |
maxForce |
number | No | Infinity | Maximum force the constraint can apply. |
collideConnected |
boolean | No | false |
Whether the two connected bodies can collide with each other. Important: Set to true if one body is a sensor that needs to detect the other. |
stiffness |
number | No | — | Spring stiffness for all constraint equations (higher = more rigid). |
relaxation |
number | No | — | Number of time steps to relax the constraint (higher = softer). |
Position-as-Pivot: For hinge, rope, and slider constraints, the constraint entity’s position in the editor is converted to body-local coordinates on both bodyA and bodyB. This means you place the constraint at the exact world position where you want the pivot/anchor, and the system computes the correct local offsets automatically. Explicit pivot/anchor properties always override this default.
Type-specific properties:
hinge — Two objects connected at a pivot point, free to rotate (like a door hinge).
| Property | Type | Default | Description |
|---|---|---|---|
pivotAX |
number | from position | Pivot point X on bodyA (local coordinates). |
pivotAY |
number | from position | Pivot point Y on bodyA. |
pivotBX |
number | from position | Pivot point X on bodyB. |
pivotBY |
number | from position | Pivot point Y on bodyB. |
rope — Holds two objects at a fixed distance apart (like a rope or rigid rod).
| Property | Type | Default | Description |
|---|---|---|---|
distance |
number | auto | Distance between anchors. Auto-calculated from positions if omitted. |
anchorAX |
number | from position | Anchor point X on bodyA. |
anchorAY |
number | from position | Anchor point Y on bodyA. |
anchorBX |
number | from position | Anchor point X on bodyB. |
anchorBY |
number | from position | Anchor point Y on bodyB. |
slider — One object slides along a track on another (like a drawer or piston).
| Property | Type | Default | Description |
|---|---|---|---|
anchorAX |
number | from position | Anchor point X on bodyA. |
anchorAY |
number | from position | Anchor point Y on bodyA. |
anchorBX |
number | from position | Anchor point X on bodyB. |
anchorBY |
number | from position | Anchor point Y on bodyB. |
axisAX |
number | 1 |
Slide axis X direction on bodyA. |
axisAY |
number | 0 |
Slide axis Y direction on bodyA. |
upperLimit |
number | — | Maximum slide distance in the positive axis direction. |
lowerLimit |
number | — | Maximum slide distance in the negative axis direction. |
disableRotationalLock |
boolean | false |
Allow the sliding object to rotate freely. |
weld — Glues two objects together rigidly (no relative movement at all).
| Property | Type | Default | Description |
|---|---|---|---|
offsetBX |
number | auto | Offset X of bodyB relative to bodyA. Auto-computed from current positions if omitted. |
offsetBY |
number | auto | Offset Y of bodyB. |
angleB |
number | auto | Angle of bodyB relative to bodyA (degrees). Auto-computed if omitted. |
Setting offsetBX = 0 and offsetBY = 0 forces both objects to share the same position.
gear — Links the rotation of two objects at a ratio (like interlocking gears).
| Property | Type | Default | Description |
|---|---|---|---|
ratio |
number | 1 |
Gear ratio. 2 means B rotates twice for every rotation of A. |
angle |
number | 0 |
Initial angle offset between the two gears. |
maxTorque |
number | — | Maximum torque the gear link can transmit. |
I/O Behavior:
| Input | Description |
|---|---|
enable |
Creates the physics constraint (if not already active) |
disable |
Removes the physics constraint |
toggle |
Toggles the constraint on/off |
setMaxForce(value) |
Sets the maximum force on the active constraint at runtime. No-op if the constraint isn’t currently active. |
setStiffness(value) |
Sets the spring stiffness on all of the active constraint’s equations. No-op if the constraint isn’t currently active. |
If either bodyA or bodyB is destroyed, the constraint is automatically cleaned up.
Examples:
Hinged gate — A gate that swings open when pushed:
# Place gate_post (dynamic_object, mass=0) and gate_door (dynamic_object, mass=50)
# Place a constraint object at the left edge of gate_door (the hinge point)
# hinge1 (constraint, constraintType=hinge, bodyA=gate_door, bodyB=gate_post)
Rope bridge — Two anchors connected by a rope:
# anchor_left (dynamic_object, mass=0), anchor_right (dynamic_object, mass=0)
# rope1 (constraint, constraintType=rope, bodyA=anchor_left, bodyB=anchor_right, distance=300)
Toggle constraint via I/O — A bridge that can be cut:
# bridge_constraint (constraint, constraintType=rope, bodyA=plank, bodyB=anchor)
# switch1 (dynamic_object, named "switch1", health=10)
switch1.onDestroyed -> bridge_constraint:disable:0
Push zone attached to an object:
# cart (dynamic_object, mass=20), push_zone (dynamic_object, isSensor=true, mass=1, pushForce=300)
# weld1 (constraint, constraintType=weld, bodyA=push_zone, bodyB=cart, offsetBX=0, offsetBY=0, collideConnected=true)
nuke
Expanding area-damage ring that kills everything in its radius. Originates at the placed position and grows outward. Entities at the center are killed/exploded; those farther out are killed and knocked back.
When team is -1 (default), damages everything. When set to a team number, friendly entities are skipped.
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
team |
number | -1 |
Team affiliation. -1 damages all entities. When set, skips same-team entities. |
size |
number | 1000 |
Maximum ring radius in pixels. |
I/O Inputs:
| Input | Description |
|---|---|
detonate |
Instantly expands the ring to its maximum size (skips the gradual growth). |
cancel |
Removes the nuke immediately without firing onDetonated. |
I/O Outputs:
| Output | Fires When |
|---|---|
onDetonated |
The ring reaches its maximum size (full detonation), including via the detonate input. |
onRemoved |
Nuke entity is removed (after ring reaches max size or via remove/cancel input). |
Placement: Via spawn_template + spawner. The nuke starts expanding immediately on creation.
landmine
Proximity mine that explodes when an enemy vehicle drives over it. Has a physics body that can be pushed.
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
team |
number | -1 |
Team affiliation. Only enemy vehicles trigger the mine. -1 targets all teams. |
lifetime |
number | 120 (seconds) |
Time before the mine auto-removes. Overrides the default 2-minute timeout. |
disableTimeout |
boolean | false |
If true, the mine never auto-removes. |
I/O Inputs:
| Input | Description |
|---|---|
detonate |
Force the mine to explode immediately. |
damage |
Also triggers explosion (built-in damage input calls explode()). |
arm |
Arms the mine so it explodes on enemy proximity (the default state). |
disarm |
Disarms the mine — enemies can drive over it without triggering it. Can still be detonated via detonate/damage. |
I/O Outputs:
| Output | Fires When |
|---|---|
onExploded |
Mine explodes (from proximity, detonate, or damage input). |
Placement: Direct placement or via spawn_template + spawner.
grenade
Timed explosive with a fuse. Has a physics body that bounces and rolls. Supports a smoke subtype that spawns a smoke cloud instead of a single explosion.
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
team |
number | -1 |
Team affiliation. Explosion damage only hits enemy entities. |
fuseTime |
number | 4 (seconds) |
Time before detonation. Overrides the default 4-second fuse. |
subtype |
string | (none) | Set to smoke for a smoke grenade that spawns multiple smoke artillery shells on detonation. |
I/O Inputs:
| Input | Description |
|---|---|
detonate |
Force the grenade to explode immediately. |
damage |
Also triggers explosion (built-in damage input calls explode()). |
setFuseTime(value) |
Resets the fuse to explode value seconds from now. |
I/O Outputs:
| Output | Fires When |
|---|---|
onExploded |
Grenade explodes (from fuse timer, detonate, or damage input). |
Placement: Via spawn_template + spawner.
artillery
Delayed explosion at a point. A shell marker appears and explodes after a fuse delay. No physics body — purely positional.
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
team |
number | -1 |
Team affiliation. Explosion damage only hits enemy entities. |
fuseTime |
number | 4 (seconds) |
Time before detonation. Overrides the default 4-second fuse. |
I/O Inputs:
| Input | Description |
|---|---|
detonate |
Force the shell to explode immediately. |
setFuseTime(value) |
Resets the fuse to explode value seconds from now. |
I/O Outputs:
| Output | Fires When |
|---|---|
onExploded |
Shell explodes (from fuse timer or detonate input). |
Placement: Via spawn_template + spawner.
airstrike
Scripted bombing run that drops a series of bombs along a line. Bombs are dropped perpendicular to the entity’s rotation at the placed position. Starts bombing immediately when placed via the map loader.
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
team |
number | -1 |
Team affiliation. Bomb damage only hits enemy entities. |
bombCount |
number | 5 |
Number of bombs to drop. |
I/O Inputs:
| Input | Description |
|---|---|
startBombing |
Begins (or restarts) the bombing run. Resets bombCount to 5 if the previous run finished. |
stopBombing |
Stops dropping bombs by zeroing the remaining bomb count. |
Use kill or remove to cancel the bombing run and remove the entity entirely.
I/O Outputs:
| Output | Fires When |
|---|---|
onBombingComplete |
All bombs have been dropped. Entity remains for 10 seconds then auto-removes. |
Placement: Via spawn_template + spawner. Position and rotation determine where and in which direction bombs are dropped.
The bombs themselves are internal entities — they are not a placeable object type and have no Entity I/O. To react to the run, wire the airstrike’s own outputs.
wall_deployable
Destructible barrier wall with a physics body. Can be destroyed by taking damage.
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
team |
number | -1 |
Team affiliation. |
health |
number | 100 |
Hit points before the wall is destroyed. |
lifetime |
number | 180 (seconds) |
Time before the wall auto-removes. Overrides the default 3-minute timeout. |
disableTimeout |
boolean | false |
If true, the wall never auto-removes. |
I/O Inputs:
| Input | Description |
|---|---|
damage(value) |
Deal damage to the wall. Destroys it when health reaches 0. |
I/O Outputs:
| Output | Fires When |
|---|---|
onExploded |
Wall is destroyed (health reaches 0). |
Placement: Direct placement or via spawn_template + spawner. The wall’s width and height from the map object determine its visual size; the collision shape uses full width and quarter height.
light
Point light source. Rendered client-side only.
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
color |
string | Light color as hex #RRGGBB. |
|
brightness |
number | Light intensity/brightness. | |
pattern |
string | (none) | Brightness pattern string. Each character a–z maps to a brightness level: a=0%, m≈48%, z=100% of the base brightness. Loops continuously. If omitted, the light is static. |
patternSpeed |
number | 10 |
Steps per second through the pattern string. |
The light’s position, width, and height come from the object placement.
I/O Inputs:
| Input | Description |
|---|---|
setColor(value) |
Changes the light color at runtime. Value is a hex string #RRGGBB. |
setBrightness(value) |
Changes the light intensity at runtime. |
setPattern(value) |
Changes the brightness pattern string at runtime (see Pattern presets below). |
Pattern presets:
z— Full brightness steadymmnmmommommnonmmonqnmmo— Candle flickermmamammmmammamamaaamammma— Fluorescent flickeraaaaaaaazzzzzzzz— Slow pulseabcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba— Smooth ramp
effect_emitter
Spawns visual effects (explosions, smoke, fire, debris, sparks) at a map position. Supports continuous emission at an interval and/or on-demand firing via Entity I/O. No physics body — purely visual.
Object-level fields:
- Tile/texture, tileset — If a texture is assigned to the object in the editor, particles use that custom image instead of a built-in effect type. Custom asset particles fade out over their lifetime.
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
effectType |
string | (none) | Built-in effect type: explosion, smoke, fire, debris, debris_cloud, spark, scorch, water_splash, impact_terrain, impact_tank, wreck_explosion, boost_particle, shell_smoke. Not needed when using a custom asset texture. |
effectWidth |
number | 64 |
Width of each effect in pixels. |
effectHeight |
number | 64 |
Height of each effect in pixels. |
interval |
number | (none) | Seconds between automatic spawns. If omitted, only fires via I/O fire input. |
count |
number | 1 |
Number of effects per fire (1–20, clamped). |
spread |
number | 0 |
Random position offset radius in pixels from emitter center. |
velocityX |
number | 0 |
Base horizontal velocity. |
velocityY |
number | 0 |
Base vertical velocity. |
velocitySpread |
number | 0 |
Random velocity variation added to both axes (± range). |
tint |
string | (none) | Hex color tint, e.g. #FF0000. |
alpha |
number | (none) | Opacity override (0–1). |
maxLifetime |
number | 500 |
Effect lifetime in ms. For built-in effectType, overrides the type’s default. For custom asset particles, this is the fade-out duration. |
damping |
number | 1 |
Per-frame velocity multiplier. Values < 1 slow particles over time (e.g., 0.95 for gradual deceleration). 1 = no damping. |
gravityX |
number | 0 |
Constant horizontal acceleration applied each frame. |
gravityY |
number | 0 |
Constant vertical acceleration applied each frame (positive = down). |
rotationSpeed |
number | 0 |
Angular velocity — how fast particles spin (radians per frame unit). |
scaleStart |
number | (none) | Starting scale of particles. Used with scaleEnd to interpolate size over lifetime. |
scaleEnd |
number | (none) | Ending scale of particles. Both scaleStart and scaleEnd must be set for interpolation. |
I/O Inputs:
| Input | Description |
|---|---|
fire |
Spawn effect(s) immediately (one burst of count effects). Fires onFired output. |
setTint(value) |
Changes the particle tint at runtime. Value is a hex string, e.g. #FF0000. |
setInterval(value) |
Changes the auto-emit interval (seconds) at runtime. |
setScale(value) |
Multiplies the current scaleStart/scaleEnd by value (compounds on repeated calls). |
I/O Outputs:
| Output | Fires When |
|---|---|
onFired |
Each time effect(s) are spawned via fire input (not interval auto-fire). |
Behavior:
- Continuous mode (
intervalset): Client auto-fires at the interval rate while entity is enabled. Respects enable/disable. Purely client-side (no network traffic). - Triggered mode (
intervalnot set): Only fires when server deliversfireinput via I/O wiring. - Both: An emitter can have
intervalAND respond tofireinput. - startDisabled: Continuous emitters start silent; triggered emitters ignore
firewhile disabled. - Custom asset vs effectType: If the object has a tile/texture assigned in the editor, particles use that image (fading out over
maxLifetime). IfeffectTypeis set instead, particles use the built-in effect animation. Custom asset takes priority when both are set.
Examples:
Continuous smoke:
# smoke_emitter (effect_emitter, effectType=smoke, interval=1, effectWidth=32, effectHeight=32)
Explosion triggered by I/O:
# button1 (dynamic_object, named "button1", health=10)
onDestroyed: boom:fire:0
# boom (effect_emitter, named "boom", effectType=explosion, effectWidth=256, effectHeight=256)
onFired: relay1:fireOutput:0
Custom image particles (e.g. leaves, sparks, custom debris):
# Assign a leaf texture to the effect_emitter object in the editor
# leaf_emitter (effect_emitter, interval=0.5, count=3, spread=64, effectWidth=16, effectHeight=16,
# velocityY=0.3, velocitySpread=0.2, maxLifetime=2000)
Red-tinted smoke burst:
# red_smoke (effect_emitter, named "red_smoke", effectType=smoke, count=5, spread=50, tint=#FF0000)
# Triggered via fire input from another entity.
sound_emitter
Plays positional audio at a map position. Supports looping ambient sounds, interval-based repetition, and on-demand firing via Entity I/O. No physics body or visible sprite — audio only.
Sounds can reference built-in game sounds (e.g. burning, tank_engine, beep) or custom audio files bundled in the map zip’s assets/ folder (e.g. campfire.ogg). Custom sounds must be .ogg, .mp3, or .wav format.
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
sound |
string | required | Sound name: a built-in sound key (e.g. burning, tank_engine) OR a custom sound filename from the map zip (e.g. campfire.ogg). |
volume |
number | 1 |
Playback volume (0–1). |
loop |
boolean | false |
Loop the sound continuously while enabled. |
interval |
number | (none) | Seconds between automatic plays. If omitted with loop=false, only fires via I/O. |
maxDistance |
number | 1200 |
Maximum distance in pixels for positional audio falloff. |
pitch |
number | 1 |
Playback rate/pitch multiplier. 0.5 = half speed/octave down, 2 = double speed/octave up. Clamped 0.25–4. |
pitchVariance |
number | 0 |
Random pitch variation (± range). E.g. 0.1 means pitch varies ±0.1 each play for natural variation. |
fadein |
boolean | false |
Enable 600ms fade-in on play. |
I/O Inputs:
| Input | Description |
|---|---|
fire |
Play the sound once immediately. Fires onFired output. |
stop |
Stop any currently playing sound. |
setVolume(value) |
Changes the playback volume (0–1). Takes effect on the next play. |
setPitch(value) |
Changes the pitch/playback-rate multiplier. Takes effect on the next play. |
setSound(value) |
Changes which sound is played (built-in key or custom filename). Takes effect on the next play. |
I/O Outputs:
| Output | Fires When |
|---|---|
onFired |
Each time sound is played via fire input (not interval/loop auto-play). |
Behavior:
- Loop mode (
loop=true): Sound starts playing when entity is enabled. When the sound naturally ends, it re-triggers automatically. Stops when disabled. - Interval mode (
intervalset): Client auto-fires at the interval rate while entity is enabled. Each play is a one-shot. Purely client-side timing. - Triggered mode (no loop, no interval): Only plays when server delivers
fireinput via I/O wiring. - Enable/Disable: Enabling a looping emitter starts its sound; disabling stops it. Interval emitters stop ticking when disabled. Triggered emitters ignore
firewhile disabled. - Positional audio: Volume fades with distance from the listener, and cuts off beyond
maxDistance. Sound is culled when the listener is on a different z-level. - Custom sounds: Place audio files (
.ogg,.mp3,.wav) in the map zip’sassets/folder. Reference them by filename (e.g.campfire.ogg). Custom sounds are loaded as Howl instances when the map loads. - setProperty: All properties are read at play-time, so runtime
setPropertychanges (volume, sound, pitch, etc.) take effect on the next play.
Examples:
Ambient campfire (looping custom sound):
# campfire_sound (sound_emitter, sound=campfire.ogg, loop=true, volume=0.8, maxDistance=600)
# Place campfire.ogg in the map zip's assets/ folder.
Alarm triggered by I/O (built-in sound):
# button1 (dynamic_object, named "button1", health=10)
onDestroyed: alarm:fire:0
# alarm (sound_emitter, named "alarm", sound=beep, volume=1, maxDistance=2000)
onFired: relay1:fireOutput:0
Interval-based machinery hum:
# machine_hum (sound_emitter, sound=tank_engine, interval=3, volume=0.5, pitch=0.7, pitchVariance=0.05)
spawn_template
Data-only object that defines WHAT to spawn. No entity is created at map load time. Must have a name set in the editor so that spawner entities can reference it.
The template’s objectType property determines the type of entity created (e.g., dynamic_object, vehicle, turret). All properties from the template are passed through to the spawned entity.
Object-level fields:
name— Required. Identifier referenced by spawner’stemplateproperty.- Position, size, rotation, tile/texture, tileset, tint — carried over to spawned entities (position is overridden by the spawner’s location).
Properties:
| Property | Type | Required | Description |
|---|---|---|---|
objectType |
string | Yes | The entity type to create. Must match a valid map object key: dynamic_object, vehicle, turret, light, static_wall, nuke, landmine, grenade, artillery, airstrike, wall_deployable, etc. |
All other properties are passed through to the spawned entity. For example, a template with objectType: vehicle can include tankType, enableAI, health, etc.
spawner
Logic entity that spawns entities at runtime via Entity I/O. Defines WHERE and WHEN to spawn. Triggered by I/O inputs, typically wired from other entities (buttons, triggers, timers).
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
template |
string | Required. | Name of a spawn_template object to use. |
maxActive |
number | 0 |
Maximum simultaneous alive entities. 0 = unlimited. |
I/O Inputs:
| Input | Description |
|---|---|
spawn |
Creates one entity from the template at the spawner’s position. Blocked if maxActive is reached. |
killSpawned |
Removes all currently tracked spawned entities. |
removeSpawned |
Removes all tracked spawned entities and clears the tracking list (frees up maxActive slots immediately). |
setTemplate(value) |
Changes the spawn_template name used for future spawns. |
setMaxActive(value) |
Changes the maximum simultaneous alive count at runtime. |
I/O Outputs:
| Output | Fires When |
|---|---|
onSpawned |
After successfully creating an entity (activator = spawned entity). |
onAllKilled |
When all tracked spawned entities have been killed (bodies may still exist). |
onAllRemoved |
When all tracked spawned entities have been fully removed from the world. |
onMaxReached |
When spawn is blocked because maxActive limit is reached. |
Spawned entities inherit all properties from the template (hitbox, health, I/O outputs, etc.) but do NOT get an ioName (avoids name collisions when spawning multiples). The spawner’s position overrides the template’s position. If a spawned dynamic_object has respawnTime, it respawns at the spawner’s position.
Examples:
Spawn a destructible crate when a button is destroyed:
# crate_template (spawn_template, named "crate_template")
# objectType: dynamic_object, health: 50, asset: crate.webp
# button1 (dynamic_object, named "button1", health: 10)
onDestroyed: spawner1:spawn:0
# spawner1 (spawner, template=crate_template)
Spawn AI tanks in waves with a limit:
# tank_template (spawn_template, named "tank_template")
# objectType: vehicle, tankType: panthur, enableAI: true, health: 200
# wave_spawner (spawner, named "wave_spawner", template=tank_template, maxActive=3)
onAllKilled: wave_timer:resetTimer:0
onMaxReached: wave_timer:disable:0
# wave_timer (logic_timer, named "wave_timer", interval=5, oscillate=true)
onTimer: wave_spawner:spawn:0
Layers
Objects are assigned to layers in the editor. Layers affect rendering order:
- Each layer has a
zIndexvalue. - Objects without an explicit
zIndexproperty get one calculated from their layer:-10000 + (layerZIndex * 10). dynamic_objectentities default tozIndex: -5if no explicit value is set.
Custom Assets
Files you add in the editor are bundled into the map’s assets/ folder and loaded when the map starts:
- Images —
.webp,.png, or.jpg. Assign them to objects in the editor like any texture. They’re used by walls and dynamic objects, byeffect_emitteras custom particle images, and bygame_events’setDialogueImageas dialogue portraits (the portrait image must also be used by some map object, or it won’t be loaded). - Sounds —
.ogg,.mp3, or.wav. Reference them by filename from asound_emitter’ssoundproperty (e.g.campfire.ogg). - Scripts —
.scriptfiles are loaded automatically when the map loads. See Map Scripts.
Keep assets small — everything in the zip is downloaded by every player before the map starts.
AI Pathfinding (Node Graphs)
AI vehicles (bots, enableAI vehicles, AI inputs like goToPosition) navigate using a node graph: a grid over the map with one node every 64 pixels, where each node is either walkable or blocked. Walls and pit terrain are blocked; everything else is walkable.
You don’t create this by hand — if the map has no node graph, the server generates one automatically when the map loads and stores it in the map file (assets/nodegraph.ain).
Things to keep in mind when building for AI:
- Corridors need to be comfortably wider than the 64px node spacing, or AI may not find a path through them.
- AI can’t path across
pitterrain, but other terrain types (water, mud, ice) are walkable — they just slow vehicles down. - Areas fully enclosed by walls are unreachable to AI even if a player could be spawned there.
Gamemode Filtering
Any object can include a gamemodes property to restrict which gamemodes it appears in:
gamemodes: conquest,frontline,battle
The object is skipped if the current gamemode is not in the list. If the property is omitted, the object loads in all gamemodes.
Capture points default to gamemodes: conquest,frontline if not explicitly set.
What Your Map Needs Per Gamemode
Every map needs entity_spawn points: team spawns (team 0 and 1) for team modes, and negative-team spawns for FFA-style modes. On top of that:
| Gamemode | Required map objects |
|---|---|
conquest, frontline |
One or more entity_capture_point (sorted left-to-right by x). |
capture_the_flag |
One entity_flag per team. Each flag automatically gets a capture/delivery point at its position — you don’t place those separately. |
tank_ball |
One entity_spawn per team with type: ball (where the ball spawns), and a goal per team: dynamic_object with objectType: goal. The ball itself is created by the gamemode. |
battle, war, team_deathmatch, free_for_all, zombies, hunted |
Spawn points only. |
If you’re building for one specific gamemode, set gamemodes on objects that shouldn’t appear elsewhere.
Note that gamemodes can adjust map objects as they load (for example, swapping team assignments), so an object may behave slightly differently across modes. For gamemode development, see GAMEMODES.md.
Entity I/O System
An input/output wiring system that lets map makers connect entity events to actions on other entities. Named entities can fire outputs (events) that trigger inputs (actions) on target entities, with optional delays.
Wire Format
Add output properties to any named entity:
onDestroyed: door1:enable:0; relay_alarm:fireOutput:0.5
onDamaged: counter1:increment:0
onTriggerEnter: gate1:disable:0
onDestroyed: turret1:setTeam:0:1
Format: targetName:inputName:delayInSeconds:value (semicolon-separated for multiple). Delay 0 = immediate; if omitted, defaults to 0. The value segment is optional and is passed to inputs that accept a parameter (e.g., setTeam, damage, setHealth, setAlpha). Values may contain colons (e.g., events:addScore:0:0:5 passes 0:5 as the value). Use \; to include a literal semicolon in a value, and \\ for a literal backslash.
The special target name activator delivers the input to whichever entity triggered the output, instead of a fixed named entity. This lets you affect the entity that caused an event without knowing its name:
# Rotate any vehicle that enters a sensor
onTriggerEnter: activator:setProperty:0:rotation=180
# Damage the entity that triggered the zone
onTriggerEnter: activator:damage:0:50
# Disable the entity that fired the output
onDestroyed: activator:disable:0
The activator is preserved through delays, so activator:setProperty:2:x=0 still targets the original triggering entity after the 2-second delay.
The special target name self delivers the input to the entity whose output is firing (the source entity). This lets an entity act on itself in response to its own events:
# Sensor disables itself after first trigger
onTriggerEnter: self:disable:0
# Timer sets its own property each tick
onTimer: self:setProperty:0:lastFired=true
# Object kills itself 5 seconds after being damaged
onDamaged: self:kill:5
self is preserved through delays, just like activator. Note: self refers to the entity that owns the output, while activator refers to the entity that triggered it (e.g., the vehicle that entered a sensor).
Wire Value Expressions
Wire values can contain {expression} syntax that resolves dynamically at delivery time, referencing entity map properties and performing inline arithmetic.
Syntax:
| Form | Example Wire Value | Resolves To |
|---|---|---|
| Property ref | {math1.currentValue} |
Entity math1’s mapProperties.currentValue |
| Activator ref | {activator.health} |
Activator entity’s health |
| Self ref | {self.health} |
Source entity’s health (the entity firing the output) |
| Arithmetic | {math1.currentValue * 2} |
Numeric result |
| String concat | {a.label + b.label} |
Concatenated string |
| Mixed | {activator.health + self.team} |
Combined result |
| Embedded in text | Score: {math1.currentValue} |
"Score: 42" |
| Number literal | {a.x + 5} |
Numeric result |
NOTE: It can only perform these expressions on a max of two values, for more it will need to be chained with other map objects.
Operands: entityName.propertyName (looks up mapProperties), activator.propertyName (looks up the triggering entity), self.propertyName (looks up the source entity firing the output), or a number literal.
Activator properties (same as logic_compare):
| Property | Resolves To |
|---|---|
activator.health |
netData.health |
activator.alive |
netData.alive |
activator.team |
team |
activator.type |
type (e.g., vehicle, object) |
activator.subtype |
subtype (e.g., actor) |
activator.tankType |
netData.tankType |
activator.speed |
Magnitude of body.velocity |
activator.aimAngle |
Turret target angle in radians (where the player is aiming) |
activator.turretAngle |
Current turret rotation in radians (absolute) |
activator.pingX |
X coordinate of last ping location |
activator.pingY |
Y coordinate of last ping location |
activator.name |
netData.ioName (entity I/O name) |
activator.playerName |
netData.name (player display name, if applicable) |
activator.mapProperty:key |
netData.mapProperties[key] |
If the activator is null (e.g., onRoundStart), activator references resolve to empty string.
Self properties (same as activator — self refers to the entity firing the output):
| Property | Resolves To |
|---|---|
self.health |
netData.health |
self.alive |
netData.alive |
self.team |
team |
self.type |
type |
self.subtype |
subtype |
self.tankType |
netData.tankType |
self.speed |
Magnitude of body.velocity |
self.aimAngle |
Turret target angle in radians (where the player is aiming) |
self.turretAngle |
Current turret rotation in radians (absolute) |
self.pingX |
X coordinate of last ping location |
self.pingY |
Y coordinate of last ping location |
self.name |
netData.ioName (entity I/O name) |
self.playerName |
netData.name (player display name, if applicable) |
self.mapProperty:key |
netData.mapProperties[key] |
If the source is null, self references resolve to empty string.
Operators: +, -, *, /, %
Rules:
+performs numeric addition when both operands are numeric, string concatenation otherwise.- Division or modulo by zero resolves to empty string.
- A single whole-value expression
{expr}preserves the raw type (number stays number). Embedded in text, the result is always a string. - Missing entity or property resolves to empty string
''. - Invalid expressions (no dot, not a number) return the raw text including braces (e.g.,
{foo}). - Expressions are resolved at delivery time, so delayed wires get current values when they fire (not when queued).
Examples:
Announce a dynamic score:
# math1 (logic_math, named "math1", initialValue=0)
# events (game_events, named "events")
onResult: events:announce:0:Score: {math1.currentValue}
Pass a computed value to setProperty:
onTimer: obj1:setProperty:0:alpha={math1.currentValue / 100}
Announce which tank type was killed:
# events (game_events, named "events")
onPlayerKilled: events:announce:0:{activator.tankType} was destroyed!
Set a math node to the activator’s team:
# sensor (dynamic_object, isSensor=true)
onTriggerEnter: math1:setValue:0:{activator.team}
Built-in Inputs (All Entities)
| Input | Description |
|---|---|
enable |
Sets the entity to enabled, fires onEnabled, calls type-specific enable behavior |
disable |
Sets the entity to disabled, fires onDisabled, calls type-specific disable behavior |
toggle |
Toggles enabled state, fires onEnabled or onDisabled |
kill |
Destroys the entity (calls kill(), falls back to remove()) |
remove |
Removes the entity from the world (always calls remove()) |
damage(value) |
Deals damage to the entity (e.g., target:damage:0:50) |
setHealth(value) |
Sets entity health to the given value |
setTeam(value) |
Sets entity team to the given value (0, 1, etc.) |
setProperty(key=value) |
Sets a map property on the entity. Value is key=value format (e.g., target:setProperty:0:health=500). Numbers and booleans are auto-parsed. Also works when targeting a spawn_template by name to modify future spawns. Setting base entity fields (x, y, rotation, width, height) applies the change to the actual entity state and physics body — see below. Setting a known output name (e.g., onDestroyed=door1:enable:0) dynamically updates the entity’s I/O wiring — see below. |
clearProperty(key) |
Removes a map property from the entity (e.g., target:clearProperty:0:health). The property is deleted from mapProperties and the client is notified. |
debug(value) |
Sends a chat message [Debug] entityName: value for I/O troubleshooting. Works regardless of enabled state. Wire value expressions resolve before reaching the handler (e.g., math1:debug:0:{math1.currentValue} shows the resolved value). |
switchPlayerTo |
Switches the activator’s controlling client to control this entity. The activator must have a currentPlayer (be player-controlled). Saves the client’s original vehicle so ejectPlayer can return them later. No-op if the activator has no controlling client. |
ejectPlayer |
Returns this entity’s controlling client to their original vehicle. If the original vehicle is dead or removed, the current vehicle is killed instead. Clears the saved original vehicle reference. No-op if this entity has no controlling client. |
applyForce(x y) |
Applies a continuous force to the entity’s physics body. Value is "forceX forceY" (space-separated). Y follows game coordinates (positive = up). No-op without a body. |
applyImpulse(x y) |
Applies an instant velocity change to the entity’s physics body. Value is "impulseX impulseY" (space-separated). Y follows game coordinates. No-op without a body. |
setProperty — Base Entity Fields
These keys apply directly to the entity’s state (not just stored in mapProperties). They work in two ways:
- As initial map properties — set them in the map editor to configure the entity at creation time (e.g.,
damping=0.8on adynamic_object). - At runtime via
setProperty— change them dynamically through I/O wiring.
Both paths go through the same code, so every property listed here works identically whether set initially or changed later.
Physics body fields (require a physics body — no-op on entities without one):
| Key | Effect |
|---|---|
x |
Moves the entity horizontally (updates physics body position) |
y |
Moves the entity vertically (updates physics body position) |
rotation |
Rotates the entity (value in degrees, converted to radians internally) |
width |
Changes entity width |
height |
Changes entity height |
velocityX |
Sets horizontal velocity on the physics body |
velocityY |
Sets vertical velocity on the physics body |
angularVelocity |
Sets rotational velocity on the physics body |
mass |
Sets physics body mass and updates mass properties |
damping |
Sets linear damping (0 = no drag, 1 = max drag). Controls deceleration. |
angularDamping |
Sets angular damping (0 = no rotational drag, 1 = max). Controls rotational slowdown. |
fixedRotation |
Boolean. Locks the body’s rotation — the object can still move but will never spin. Useful for carts, platforms, sliding doors. |
fixedX |
Boolean. Locks horizontal movement — the object can only move vertically. Useful for elevator-style objects. |
fixedY |
Boolean. Locks vertical movement — the object can only move horizontally. Useful for rail-mounted sliding objects. |
collisionResponse |
Boolean. When false, the body passes through other bodies instead of colliding, but still generates contact events (onCollisionEnter/onCollisionLeave). Useful for detection zones with mass, ghost objects, or pass-through triggers. |
TankData fields (only apply to vehicles/turrets with tankData):
| Key | Effect |
|---|---|
engineForce |
Forward engine power |
engineForceWithTurn |
Engine power while turning |
engineBoostForce |
Boost engine power |
engineBoostTime |
Boost duration (ms). Also updates the internal boost timer. |
engineBoostAngularDamping |
Angular damping during boost |
turnForce |
Stationary turn power |
turnForceWithEngine |
Turn power while moving |
mass |
Vehicle mass (updates both tankData and physics body) |
reverseMod |
Reverse speed multiplier |
reloadTime |
Time between shots (ms). Also updates the netData reload time. |
damageMod |
Outgoing damage multiplier |
damagePenMod |
Penetration damage multiplier |
fireForce |
Recoil force when firing |
barrelLength |
Barrel length in pixels |
armorMod |
Front armor multiplier |
armorModSide |
Side armor multiplier |
armorModRear |
Rear armor multiplier |
armorPenMod |
Armor penetration multiplier |
turretSpeed |
Turret rotation speed |
turretRotationLimit |
Max turret rotation angle (radians) |
Combined with wire value expressions, this lets you move entities dynamically at runtime:
# Move a gate to a computed position
timer1.onTimer -> gate1:setProperty:0:x={math1.currentValue}
# Rotate a turret based on a counter
counter1.onTarget -> platform1:setProperty:0:rotation=180
# Launch an object sideways when triggered
sensor.onTriggerEnter -> ball:setProperty:0:velocityX=200
All other keys (e.g., health, pickupAmount, alpha) are stored in mapProperties as before and sent to the client via RPC.
setProperty — Dynamic Outputs
When setProperty targets a known output name (e.g., onDestroyed, onTimer, onTriggerEnter), it updates the entity’s I/O wiring instead of sending an RPC. The value uses the standard wire format:
# Wire a sensor to disable a gate at runtime
timer1.onTimer -> sensor1:setProperty:0:onTriggerEnter=gate1:disable:0
# Replace an entity's onDestroyed output
relay1.onFireOutput -> barrel1:setProperty:0:onDestroyed=relay2:fireOutput:0; counter1:increment:0
# Clear an output (set to empty)
button1.onTimer -> turret1:setProperty:0:onFired=
This fully replaces the output for that key. The value is stored in mapProperties and also parsed into the entity’s live ioOutputs. Works via scripts and regular wire deliveries.
Built-in Outputs (All Entities)
| Output | Fires When |
|---|---|
onEnabled |
Entity is enabled via enable or toggle input |
onDisabled |
Entity is disabled via disable or toggle input |
onRemoved |
Entity is removed from the world (any cause: kill, remove, despawn, etc.) |
onCollisionEnter |
A physics body begins contact with this entity. The activator is the other entity. Fires for any entity with a physics body (vehicles, dynamic objects, etc.). |
onCollisionLeave |
A physics body ends contact with this entity. The activator is the other entity. |
Key Event Outputs
These outputs fire on the player’s currently controlled entity when the player presses or releases a key. Each key has a Down and Up variant. The entity itself is the activator.
onKey*Downfires fromkeydown, exceptonKeyFireDownwhich fires from thefiresocket event (mouse click / fire key) to avoid double-firing.onKey*Upfires fromkeyupfor all keys (including Fire).
| Down Output | Up Output | Key | Description |
|---|---|---|---|
onKeyForwardDown |
onKeyForwardUp |
1 | Forward |
onKeyReverseDown |
onKeyReverseUp |
2 | Reverse |
onKeyTurnLeftDown |
onKeyTurnLeftUp |
3 | Turn Left |
onKeyTurnRightDown |
onKeyTurnRightUp |
4 | Turn Right |
onKeyHandbrakeDown |
onKeyHandbrakeUp |
5 | Handbrake |
onKeyBoostDown |
onKeyBoostUp |
6 | Boost |
onKeyFireDown |
onKeyFireUp |
7 | Fire |
onKeyRepairDown |
onKeyRepairUp |
8 | Repair |
onKeyPingLocationDown |
onKeyPingLocationUp |
9 | Ping Location |
onKeyShowEmotesDown |
onKeyShowEmotesUp |
10 | Show Emotes |
onKeyShowScoreboardDown |
onKeyShowScoreboardUp |
11 | Show Scoreboard |
onKeyKillstreak1Down |
onKeyKillstreak1Up |
12 | Killstreak 1 |
onKeyKillstreak2Down |
onKeyKillstreak2Up |
13 | Killstreak 2 |
onKeyKillstreak3Down |
onKeyKillstreak3Up |
14 | Killstreak 3 |
onKeyAllChatDown |
onKeyAllChatUp |
15 | All Chat |
onKeyTeamChatDown |
onKeyTeamChatUp |
16 | Team Chat |
onKeySquadChatDown |
onKeySquadChatUp |
17 | Squad Chat |
onKeySpecialDown |
onKeySpecialUp |
18 | Special |
Key State Properties
You can read the current pressed state of any key via wire value expressions using key.<KeyName>. Returns true if the key is currently held down, false otherwise.
| Property | Key | Example |
|---|---|---|
key.Forward |
1 | {self.key.Forward}, {activator.key.Forward} |
key.Reverse |
2 | {activator.key.Reverse} |
key.TurnLeft |
3 | {activator.key.TurnLeft} |
key.TurnRight |
4 | {activator.key.TurnRight} |
key.Handbrake |
5 | {activator.key.Handbrake} |
key.Boost |
6 | {activator.key.Boost} |
key.Fire |
7 | {activator.key.Fire} |
key.Repair |
8 | {activator.key.Repair} |
key.PingLocation |
9 | {activator.key.PingLocation} |
key.ShowEmotes |
10 | {activator.key.ShowEmotes} |
key.ShowScoreboard |
11 | {activator.key.ShowScoreboard} |
key.Killstreak1 |
12 | {activator.key.Killstreak1} |
key.Killstreak2 |
13 | {activator.key.Killstreak2} |
key.Killstreak3 |
14 | {activator.key.Killstreak3} |
key.AllChat |
15 | {activator.key.AllChat} |
key.TeamChat |
16 | {activator.key.TeamChat} |
key.SquadChat |
17 | {activator.key.SquadChat} |
key.Special |
18 | {activator.key.Special} |
Example — Player enters trigger, switches to turret, fire triggers cannon, handbrake ejects:
# Trigger zone switches player to the turret
sensor_zone.onTriggerEnter -> turret1:switchPlayerTo:0
# When player fires while controlling the turret, fire the cannon effect
turret1.onKeyFireDown -> cannon_fx:fire:0
# When player presses handbrake, eject back to original vehicle
turret1.onKeyHandbrakeDown -> turret1:ejectPlayer:0
# Conditional logic using key state property
turret1.onKeyFireDown -> math1:test:0:{self.key.Boost}
Built-in Properties
| Property | Description |
|---|---|
startDisabled |
If truthy, entity starts with I/O disabled (won’t fire outputs) |
Disabled Entities
A disabled entity will not fire any outputs, except onEnabled and onDisabled. Inputs can still be delivered to disabled entities.
Disabling also triggers type-specific behavior — see Enable/Disable Behavior Per Type below.
Enable/Disable Behavior Per Type
| Entity Type | What Happens When Disabled |
|---|---|
static_object (wall) |
Collision masks set to 0 (passthrough), client shows semi-transparent (alpha 0.3) |
static_object (terrain) |
Collision masks set to 0 (terrain sensor stops detecting overlaps) |
dynamic_object |
Collision masks set to 0 (disables collisions, pickups, push, sensors), client shows semi-transparent (alpha 0.3) |
capture_point |
Capture tick and client proximity detection are skipped (capture progress freezes) |
turret (vehicle) |
AI firing disabled (disableFiring), botThink is skipped entirely |
light |
Light becomes invisible on client |
When re-enabled, all effects are reversed (collision masks restored, alpha restored, AI resumes, etc.).
Entity-Specific I/O
Each object type’s own inputs and outputs are documented in its section above — see the I/O Inputs / I/O Outputs tables under static_wall, static_terrain, entity_spawn, entity_flag, entity_capture_point, vehicle (including AI I/O, shared by turret), dynamic_object, constraint, nuke, landmine, grenade, artillery, airstrike, wall_deployable, light, effect_emitter, sound_emitter, and spawner.
Logic Entities
Non-visual entities that provide routing and control flow for I/O wiring. Place them in the map editor with the following object keys.
logic_relay
Simple signal forwarding node. Useful for fan-out or adding delays. The activator is passed through, so downstream entities receive the original activator (e.g. the vehicle that entered a trigger zone).
| Inputs | Outputs |
|---|---|
fireOutput — triggers the relay |
onFireOutput — fires when triggered (passes activator through) |
logic_timer
Fires on an interval. Starts automatically when the round begins.
| Property | Description |
|---|---|
interval |
Time in seconds between fires (default: 1) |
oscillate |
If truthy, repeats indefinitely; otherwise fires once |
| Inputs | Outputs |
|---|---|
fireOutput — immediate fire |
onTimer — fires each interval |
resetTimer — resets accumulator, re-enables, and unpauses |
|
setInterval — change the interval at runtime (seconds, > 0) |
|
pause — pause the timer (accumulator held) |
|
resume — resume a paused timer |
logic_counter
Counts up/down and fires when a target count is reached.
| Property | Description |
|---|---|
target |
Count to reach before firing onTarget (default: 1) |
startValue |
Starting count value (default: 0) |
| Inputs | Outputs |
|---|---|
increment — adds 1 to count |
onTarget — fires when count >= target |
decrement — subtracts 1 from count |
onReset — fires when reset |
reset — resets count to startValue |
|
setTarget — change the target count at runtime |
|
setValue — set the current count directly |
logic_branch
Boolean gate that routes signals based on its current value.
| Property | Description |
|---|---|
initialValue |
Starting boolean value (default: false) |
| Inputs | Outputs |
|---|---|
setTrue — sets value to true |
onTrue — fires when test is called and value is true |
setFalse — sets value to false |
onFalse — fires when test is called and value is false |
setValue — toggles value |
|
test — evaluates and fires onTrue or onFalse |
logic_compare
Conditional gate that tests a property of the activator entity against a configured value, then routes the signal to onTrue or onFalse. The activator passes through.
| Property | Type | Default | Description |
|---|---|---|---|
property |
string | (required) | Activator property to test (see table below) |
operator |
string | eq |
Comparison operator: eq, neq, lt, gt, lte, gte |
compareValue |
any | (required) | Value to compare against (auto-parsed: "75" → 75, "true" → true) |
Supported properties:
| Property | Resolves To | Type |
|---|---|---|
health |
activator.netData.health |
number |
alive |
activator.netData.alive |
boolean |
team |
activator.team |
number |
type |
activator.type |
string |
subtype |
activator.subtype |
string |
tankType |
activator.netData.tankType |
string |
speed |
Magnitude of activator.body.velocity |
number |
name |
activator.netData.ioName |
string |
playerName |
activator.netData.name (player display name) |
string |
mapProperty:<key> |
activator.netData.mapProperties[key] |
any |
| Inputs | Outputs |
|---|---|
test — evaluate condition against activator |
onTrue — fires when condition passes |
setOperator — change operator at runtime |
onFalse — fires when condition fails or no activator |
setCompareValue — change compare value at runtime |
|
setTestProperty — change tested property at runtime |
Examples:
Only heal players below 50 HP:
# sensor (dynamic_object, isSensor=true, named "sensor")
onTriggerEnter: health_check:test:0
# health_check (logic_compare, property=health, operator=lt, compareValue=50)
onTrue: heal_station:enable:0
Team-specific door:
# door_sensor (dynamic_object, isSensor=true, named "door_sensor")
onTriggerEnter: team_check:test:0
# team_check (logic_compare, property=team, operator=eq, compareValue=0)
onTrue: door1:disable:0
Vehicle type gate:
# entry_sensor (dynamic_object, isSensor=true, named "entry_sensor")
onTriggerEnter: type_check:test:0
# type_check (logic_compare, property=tankType, operator=eq, compareValue=panthur)
onTrue: special_door:disable:0
onFalse: denied_relay:fireOutput:0
logic_and
Two-input AND gate. Tracks whether inputA and inputB have each been received at least once. When both are satisfied, fires onTrue. For 3+ input AND logic, chain multiple logic_and gates.
| Property | Type | Default | Description |
|---|---|---|---|
autoReset |
boolean | false |
If true, automatically resets both inputs after firing onTrue (allows re-triggering) |
| Inputs | Outputs |
|---|---|
inputA — sets input A to true, checks gate |
onTrue — fires when both inputs received |
clearA — sets input A to false |
onFalse — fires when test called but not all inputs received |
toggleA — toggles input A, checks gate |
onReset — fires when reset is called |
inputB — sets input B to true, checks gate |
|
clearB — sets input B to false |
|
toggleB — toggles input B, checks gate |
|
test — read-only check, does not mark any input |
|
reset — clears both received flags |
Examples:
Open a gate only after both switches are destroyed:
# switch1 (dynamic_object, named "switch1", health=10)
onDestroyed: gate_and:inputA:0
# switch2 (dynamic_object, named "switch2", health=10)
onDestroyed: gate_and:inputB:0
# gate_and (logic_and, named "gate_and")
onTrue: gate1:kill:0
Enable turret only after delay AND sensor triggered (with autoReset):
# warmup_timer (logic_timer, interval=10, oscillate=false)
onTimer: turret_gate:inputA:0
# sensor (dynamic_object, isSensor=true)
onTriggerEnter: turret_gate:inputB:0
# turret_gate (logic_and, autoReset=true)
onTrue: turret1:enable:0
Three-input AND via chaining:
# switch1.onDestroyed → and1:inputA:0
# switch2.onDestroyed → and1:inputB:0
# switch3.onDestroyed → and2:inputB:0
# and1 (logic_and)
onTrue: and2:inputA:0
# and2 (logic_and)
onTrue: final_door:kill:0
logic_math
Accumulator-style arithmetic gate. Stores an internal numeric value that can be modified by input operations, then optionally pushes the result to a target entity’s property.
| Property | Type | Default | Description |
|---|---|---|---|
initialValue |
number | 0 |
Starting value of the accumulator |
value |
number | 1 |
Default operand used when the wire doesn’t supply one |
target |
string | (none) | Entity name to deliver result to via setProperty |
targetProperty |
string | (none) | Property name on target entity to set |
min |
number | (none) | Clamp minimum. Fires onMin when reached |
max |
number | (none) | Clamp maximum. Fires onMax when reached |
| Inputs | Outputs |
|---|---|
add(v) — accumulator += v |
onResult — fires after every operation |
subtract(v) — accumulator -= v |
onMin — fires when value clamped to min |
multiply(v) — accumulator *= v |
onMax — fires when value clamped to max |
divide(v) — accumulator /= v (no-op if v=0) |
|
modulo(v) — accumulator %= v (no-op if v=0) |
|
pow(v) — accumulator = accumulator^v |
|
setValue(v) — accumulator = v |
|
round — Math.round(accumulator) |
|
floor — Math.floor(accumulator) |
|
ceil — Math.ceil(accumulator) |
|
abs — Math.abs(accumulator) |
|
negate — accumulator = -accumulator |
|
reset — reset to initialValue |
|
getResult — fire onResult without modifying value |
For all arithmetic inputs (add–pow), the operand resolves as: wire value if provided, else the value property (default 1). The current value is always exposed as mapProperties.currentValue so logic_compare can read it via mapProperty:currentValue.
Examples:
Brightness dimmer (trigger zones control a light):
# math1 (logic_math, initialValue=1.4, target=my_light, targetProperty=brightness, min=0, max=2)
# zone_up (dynamic_object, isSensor=true) onTriggerEnter: math1:add:0:0.1
# zone_down (dynamic_object, isSensor=true) onTriggerEnter: math1:subtract:0:0.1
Countdown to explosion:
# countdown (logic_math, initialValue=10, value=1, min=0)
# timer1 onTimer: countdown:subtract:0
# countdown onMin: relay_explode:fireOutput:0
Score multiplier:
# multiplier (logic_math, initialValue=100, value=2)
# trigger onTriggerEnter: multiplier:multiply:0
logic_random
Random number generator. Generates random values on demand and stores the result in currentValue (accessible in expressions via {random1.currentValue}). Follows the same currentValue pattern as logic_math.
Properties:
| Property | Default | Description |
|---|---|---|
min |
0 |
Minimum value (inclusive) |
max |
1 |
Maximum value (inclusive for rollInt, exclusive for roll) |
initialValue |
0 |
Value before any roll, restored by reset |
Inputs:
| Input | Description |
|---|---|
roll |
Generate random float in [min, max), store in currentValue, fire onResult |
rollInt |
Generate random integer in [min, max] (inclusive), store in currentValue, fire onResult |
reset |
Restore currentValue to initialValue, fire onResult |
getResult |
Fire onResult without rolling (reads current stored value) |
Outputs:
| Output | Description |
|---|---|
onResult |
Fired after every roll, rollInt, reset, or getResult |
Runtime setProperty: min=<value>, max=<value>
Examples:
Random spawn delay (1–5 seconds):
# rng (logic_random, min=1, max=5)
# timer onTimer: rng:roll:0
# rng onResult: delay_timer:setProperty:0:interval={rng.currentValue}
Dice roll (1–6):
# dice (logic_random, min=1, max=6)
# button (dynamic_object, health=10) onDestroyed: dice:rollInt:0
Random branch (50/50 coin flip):
# coin (logic_random, min=0, max=1)
# trigger onTriggerEnter: coin:rollInt:0
# coin onResult: branch:setValue:0:{coin.currentValue}
game_events
Bridges game lifecycle and gamemode events into the Entity I/O system. Place one or more in a map to react to round starts, player kills, nuke detonations, and more. Multiple instances are supported (all fire independently). Invisible to clients.
Inputs:
| Input | Description |
|---|---|
endRound |
Force end the current round (guarded by ioEnabled) |
setRoundTime(value) |
Set remaining round time in seconds, broadcasts updated roundState |
announce(value) |
Broadcast text announcement to all players (guarded by ioEnabled) |
addScore(value) |
Add to a team’s score. Format: team:amount (e.g., 0:1) (guarded by ioEnabled) |
setTeamScore(value) |
Set a team’s score to an absolute value. Format: team:amount (e.g., 0:5) (guarded by ioEnabled) |
sendChat(value) |
Send a chat message to all players (guarded by ioEnabled) |
announceActivator(value) |
Broadcast a text announcement to the activator’s player only (guarded by ioEnabled) |
setDialogueImage(value) |
Set portrait image for subsequent dialogue. Value is a filename from the map’s assets (e.g., commander.webp). Empty value reverts to default Shelly portrait Important: This image must be added as a map object somewhere in the map for this to work at all. |
showDialogue(value) |
Show dialogue overlay to all players. Uses custom portrait if setDialogueImage was called, otherwise shows Shelly. Supports <imp> tags for gold highlighting |
showDialogueActivator(value) |
Show dialogue to the activator’s player only. Uses custom portrait if setDialogueImage was called |
hideDialogue |
Hide dialogue for all players |
hideDialogueActivator |
Hide dialogue for the activator’s player only |
pause |
Pause physics & entity ticking (singleplayer only). Shows PAUSED overlay with continue prompt |
unpause |
Resume game after pause |
Universal Outputs (fired in all gamemodes):
| Output | Fires When | Activator |
|---|---|---|
onRoundStart |
Round begins playing | null |
onRoundEnd |
Round ends | null |
onGameStart |
Game first starts (round 0) | null |
onGameEnd |
Game ends | null |
onLobbyStart |
Map loaded during lobby phase | null |
onPlayerSpawned |
Any player vehicle spawns | vehicle entity |
onPlayerKilled |
Any player killed | victim vehicle entity |
onPlayerJoined |
A client joins the game | player entity (or null) |
onPlayerLeft |
A client disconnects from the game | null |
onContinue |
Player presses spacebar/double-tap during pause | player entity |
Gamemode-Specific Outputs:
| Output | Gamemodes | Fires When |
|---|---|---|
onNukeWarning |
conquest, frontline | All capture points taken, nuke countdown begins |
onNukeCancelled |
conquest, frontline | Capture point recaptured, nuke stopped |
onNukeDetonated |
conquest, frontline | Nuke fires |
onTeamEliminated |
battle, war | Last player on a team killed |
onRingClosing |
battle, war | Battle ring starts closing |
onGoalScored |
tank_ball | Ball enters a goal |
onWaveStarted |
zombies | New zombie wave begins |
onTargetSelected |
hunted | New hunted target chosen |
constraint
Physics constraint between two named bodies. See the constraint object type section above for full documentation. Supports enable/disable/toggle to create or remove the constraint at runtime.
spawner
Spawns entities at runtime from a spawn_template, with properties, inputs, and outputs documented in the spawner object type section above.
Examples
Destroy a barrier when two switches are hit:
# switch1 (dynamic_object, named "switch1")
onDestroyed: counter1:increment:0
# switch2 (dynamic_object, named "switch2")
onDestroyed: counter1:increment:0
# counter1 (logic_counter, target=2)
onTarget: barrier1:kill:0
Open a gate when a vehicle enters a trigger zone:
# trigger_zone (dynamic_object, isSensor=true, named "trigger_zone")
onTriggerEnter: gate1:disable:0
onTriggerLeave: gate1:enable:1.5
Delayed alarm relay:
# alarm_trigger (dynamic_object, named "alarm_trigger")
onDestroyed: relay1:fireOutput:0
# relay1 (logic_relay, named "relay1")
onFireOutput: siren1:enable:0; light1:enable:0; door1:kill:2
Passthrough wall controlled by a trigger zone:
# trigger_zone (dynamic_object, isSensor=true, named "trigger_zone")
onTriggerEnter: wall1:enable:0
# wall1 (static_object wall, named "wall1", startDisabled=true)
# Starts disabled (no collisions, semi-transparent). Becomes solid when a vehicle enters the trigger.
Switch turret team when a capture point is taken:
# point_a (capture_point, named "point_a")
onCaptured: turret1:setTeam:0:1
# turret1 (vehicle turret, named "turret1")
# Team changes to 1 (including its AI) when point_a is captured.
Change a dynamic object’s pickup amount at runtime:
# boost_switch (dynamic_object, named "boost_switch")
onDestroyed: health_kit:setProperty:0:pickupAmount=200
# health_kit (dynamic_object, named "health_kit", pickupType=health, pickupAmount=50)
# Pickup amount increases from 50 to 200 when the switch is destroyed.
Trigger a relay when the round starts:
# events (game_events, named "events")
onRoundStart: relay1:fireOutput:0
# relay1 (logic_relay, named "relay1")
onFireOutput: door1:enable:0; alarm1:enable:0
Announce a message when a nuke is incoming:
# events (game_events, named "events")
onNukeWarning: announcer:announce:0:NUKE INCOMING
# announcer (game_events, named "announcer")
Modify a spawn template so future spawns have more health:
# upgrade_trigger (dynamic_object, isSensor=true, named "upgrade_trigger")
onTriggerEnter: tank_template:setProperty:0:health=500
# tank_template (spawn_template, named "tank_template")
# objectType: vehicle, tankType: panthur, enableAI: true, health: 200
# Future spawns from this template will have 500 health instead of 200.
Map Scripts (.script files)
Script files provide a centralized, plain-text way to define Entity I/O wiring, logic entities, and simple scripting. Instead of spreading dozens of wire strings across individual map object property panels, you can write everything in one readable .script file.
Scripts are syntactic sugar over Entity I/O — they parse into the same wire connections and entity definitions that already exist. Scripts are server-side only (no client changes needed).
All .script files are loaded automatically when the map loads, as long as the script is added to the map as an asset. Multiple script files are supported.
Entity Blocks
Declare entities inline instead of placing them in the editor:
entity <name> <type> {
<key> = <value>
...
}
<name>— the entity’s ioName (registered in entityNameCache)<type>— any entity type supported by the map loader (see below)- Properties inside
{}arekey = valuepairs (one per line), auto-parsed as numbers/booleans/strings - The fields
x,y,width,height,rotation,tile,textureFile,tileset, andtintare treated as object-level fields; all other properties go into the entity’s property bag - Empty braces
{}are valid (no properties) - Braces can be on the same line or next line
Supported entity types: Any type the map loader handles — dynamic_object, vehicle, turret, static_wall, spawn_protection, static_terrain, light, effect_emitter, sound_emitter, logic_relay, logic_timer, logic_counter, logic_branch, logic_compare, logic_and, logic_math, logic_random, spawner, game_events, constraint, nuke, landmine, grenade, artillery, airstrike, wall_deployable
Player spawn points (entity_spawn) and hitbox definitions (entity_hitbox) cannot be declared in scripts — they’re processed when the editor layers load, so place them in the editor.
Examples:
entity timer1 logic_timer {
interval = 5
oscillate = true
}
entity score_math logic_math {
initialValue = 0
min = 0
max = 100
target = hud_display
targetProperty = score
}
entity events1 game_events {}
# Physical entities work too — create a sensor zone from a script
entity trigger dynamic_object {
x = 3000
y = 3000
width = 200
height = 200
isSensor = true
}
Wire Statements
Connect entity outputs to inputs:
<source>.<output> -> <target>:<input>:<delay>:<value>; ...
- Left side:
entityName.outputName - Right side: standard wire format (semicolon-separated for multiple targets)
- Wire values support
{expression}syntax (see Wire Value Expressions above) - Wires defined in scripts are additive — they merge with any wires defined in map object properties
Examples:
switch1.onDestroyed -> counter1:increment:0
counter1.onTarget -> barrier1:kill:0
# Multi-target wire
timer1.onTimer -> spawner1:spawn:0; alarm:fire:0
# Wire with expression value
sensor.onTriggerEnter -> events1:announce:0:Score: {score_math.currentValue}
Command Statements
Bare target:input:delay:value lines deliver an input directly when the map loads, without needing a source entity or output event. Useful for one-time setup like starting disabled, triggering an initial spawn, or sending a debug message.
# Disable a gate at map load
gate1:disable:0
# Spawn an initial wave after 3 seconds
spawner1:spawn:3
# Multiple commands on one line
relay1:fireOutput:0; counter1:reset:0
Commands support the same format as wire targets — including delays, values, and semicolon-separated multiples.
Comments
Lines starting with # are comments. Inline comments also work on entity and wire lines (but # inside wire values after -> is preserved).
# This is a comment
entity relay1 logic_relay {} # Inline comment
Interaction with Map Object Wires
Script wires are applied after all map object wires. When both a map object property and a script define wires for the same output, the script wires are appended (not replaced). This lets you augment editor-placed wiring with script-based wiring.
Full Example
# Wave spawner system
entity wave_timer logic_timer {
interval = 10
oscillate = true
}
entity kill_counter logic_counter {
target = 3
startValue = 0
}
# When timer fires, spawn a tank
wave_timer.onTimer -> spawner1:spawn:0
# Track kills, restart timer when wave cleared
spawner1.onAllKilled -> kill_counter:increment:0
kill_counter.onTarget -> wave_timer:resetTimer:0; kill_counter:reset:0
kill_counter.onTarget -> events1:announce:0:Wave cleared!
This creates two logic entities (wave_timer and kill_counter) and wires them together with existing map-placed entities (spawner1, events1). The wave timer triggers spawning every 10 seconds, and when all spawned entities are killed, the counter increments. After 3 kills, it resets the timer and announces “Wave cleared!”.