STUGPlay Free

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):

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:

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:

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:


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:

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:

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:

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:

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:

Custom Assets

Files you add in the editor are bundled into the map’s assets/ folder and loaded when the map starts:

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:

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:

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:

  1. As initial map properties — set them in the map editor to configure the entity at creation time (e.g., damping=0.8 on a dynamic_object).
  2. 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.

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

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>; ...

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!”.