Showcase · Shooter

How the shooter is made

A range you have to move around: forty cells on two floors, a gallery of targets that get back up, two that run, a bunker you shoot into through a window, and mines that come looking for you. Fifty-seven things, three short scripts, and no shooter code anywhere in the engine — everything on this page is a rule or a script in one document.

A prototype arena on two floors: a catwalk down one side, a gallery of targets on the far wall, breakable cover, crates and barrels
The document, drawn by the software rasteriser the tests use — a picture of the file, not a screenshot of a good moment.

The gun is two properties

The player's body is a blueprint called marksman with hp: 100 and ammo: 30, and the pistol hangs on its hand socket. What makes the pistol a weapon and not a prop is nothing but its properties — damage: 25 and range: 60, read when the trigger is pulled. Ammo lives on the player rather than the gun, which is what lets an ammo box hand rounds to whoever walks in.

A target is one rule

The basic gallery target has hp: 100 and a single rule, and it is the shape almost every rule in the game takes — on damaged, when hp <= 0:

on: damaged   when: hp <= 0
do: spawn shard   ·  spawn shard   ·  score 1
    emit "target down"  ·  despawn

The two shards are the pieces left behind; the emit is the line on everybody's HUD. despawn comes last because a rule stops there — anything after it would be writing to a corpse.

Getting back up, without a script

The poppers do something the basic target cannot: they return. The trick is that dying is not despawn but deactivate for six seconds — and returned is an event, so coming back can have a rule of its own:

on: damaged   when: hp <= 0
do: spawn shard  ·  score 2  ·  emit "popper down"
    deactivate self, 6 seconds

on: returned
do: heal self 999  ·  emit "popper up"

Heal by 999 rather than "set hp to full" because heal clamps — it is the document's way of saying whatever full is. Two rows in a panel, and the range never empties.

The runner is on a clock

Two targets sweep the gallery, and their script is the first multiplayer lesson in the file. Where a runner is, is computed from world.time — the one number every browser in the room agrees about — rather than accumulated frame by frame:

const PACE = 0.42
let phase = 0

function onSpawn() {
  // Staggered by where it starts, so two runners are not one thick runner.
  phase = self.x * 0.21
}

function onTick() {
  self.x = self.get('centre') + Math.sin(world.time * PACE + phase) * self.get('span')
}

Four browsers draw this in the same place without anybody sending anything, and a runner that was shot down rejoins the sweep where the sweep is now — not where it happened to die. The centre and span are properties on the blueprint, so the route moves without touching the code.

The thing that comes for you

A script can measure a distance and take health off; it cannot cast a ray, so it cannot shoot back. What it can do is come to you — so the mine circles its patch of the pit until somebody walks inside its reach, closes the distance, and goes off in their face. Three numbers make it fair rather than annoying: it flies at 3.6 — above head height and above every crate, so what it costs you is the open ground rather than the cover; it reloads for seven seconds, so being caught once is a hit and not a grinder; and at 40 hp against a 25-damage pistol it is two shots to bring down, which is why it scores 8 while a gallery target scores 1.

const RING = 5      // its circuit, when nobody is near
const HEIGHT = 3.6  // above the cover, so cover still works
const AGGRO = 11    // how far it notices
const STING = 2     // how close it has to get
const HURT = 18
const RELOAD = 7

function onTick(dt) {
  const player = getEntityByName('player')
  const reach = player ? self.flatDistanceTo(player) : 999

  if (player && world.time >= ready && reach < AGGRO) {
    // close the distance...
    if (reach < STING) {
      player.damage(HURT)
      self.emit('a mine went off')
      ready = world.time + RELOAD
    }
  }
  // ...otherwise, back to the circuit, eased rather than snapped.
}

Its death is still just a rule — rubble, a thud, score 8, deactivate for twelve seconds, and the returned heal puts it back on patrol.

Cover that fights back, pickups that meter themselves

The barrels are the range's one joke, and it is a rule: on damaged, when hp <= 0 — damage other by 20. other is whoever set the rule off, so shooting a barrel you are standing next to is a decision you made.

The ammo box and the stim use the same deactivate trick as the poppers, pointed the other way: walking in adds ammo +12 (or heals 40), and the box deactivates for fifteen seconds (the stim, twenty-five). That one number is the whole supply economy — no cooldown system, no spawner, just a thing that is briefly not there.

What the scoring says

A still target scores 1, a popper 2, a mine 8. The prices are the difficulty curve stated out loud — and the host decides what a score means, which is what lets the same document be a practice range in freeplay and a match with a winner. The level does not know. It just says what happened.