# Your first scenario: Crossing Drive

Day 1 of the [training path](TRAINING.md). By the end of it you have a scenario a trainee can drive. A car sets
off on a town route with a 50 km/h limit, a stop line, a pedestrian crossing and a destination. The HUD shows
speed, the limit and the next objective, and the scenario emits an event at every moment that matters. Day 2
scores those events.

- **Part 1, the world** (about 4 hours): the ground, the road, the car, the stop line, the crossing, the pedestrian,
  the buildings and the destination.
- **Part 2, the rules** (about 3 hours): what the scripts judge and emit, how to change them, and a self-test that
  drives the route the way a careful driver would.

The finished project is the **Crossing Drive** sample. Install it from the launcher's **Samples** tab and keep it
open beside yours. The Linux build has no launcher: install the sample on Windows and copy the folder across, as
[Start here](START-HERE.md), "On Linux", says. Every block below is copied from it, less two things that only
change its look: the sample's parts each name a `material` (gloss paint, glass, rubber, matte walls, in
`scenes/materials/`), and it carries scenery (windows, roofs, kerbs, street lamps and trees, tagged `scenery`, none
of them solid). Your scene drives the same without them; copy them across if you want the sample's look. The sample
also carries day 2's two files, `journal.json` and `training/crossing-drive.scorecard.json`. Leave them out for now.

## Before you start

- Talos is installed and you have played a sample ([Start here](START-HERE.md)).
- **Two ways to make each step.** Every step is written both ways. Use either, or both.
  - **Ask your assistant.** If you connected an AI assistant ([Start here](START-HERE.md), "Connect your AI"), each
    step gives a prompt for it. It works through the Talos tools, checks its own work with the critic, and you
    watch the change arrive in the editor.
  - **By hand.** Edit the project's files in any text editor. A scene is JSON. When you save it, the editor shows
    **Scene changed on disk**: press **Reload**. Each step shows the JSON to add. A block with one entity per line
    pastes into the `entities` list as it is. The file may mix that style with one value per line.
- **Check each step, with or without the editor.** From a terminal in the project folder:
  - `argus validate scenes/main.scene.json` reads the scene as the engine will. It says `ok, N entities`, or what is
    wrong and where. Each step gives its N.
  - `argus run . --headless --frames 600` runs ten seconds with no window. It exits 0, or prints the script error.
  - The command is `argus` on Linux. On Windows it is `argus.exe` in the engine's `bin` folder:
    `& "$env:LOCALAPPDATA\Argus\engines\<version>\bin\argus.exe" validate scenes\main.scene.json`.
- **Expected in every run:** `no navmesh (nav-undeclared: ...)`. This scenario needs no navigation. A run also counts
  one entity more than the file has: the road builds its surface as an entity of its own, `route-surface`.
- **Positions** are in metres. +Y is up, and the car drives toward -Z. The route runs from z = 150 down to
  z = -140, then turns left.
- **Play** is **F5**, and **Shift+Esc** stops. Play runs the scene as it is, unsaved edits included.

## Part 1: the world

### 1. Create the project

In the launcher: **Projects** ▸ **New project**. Choose **3D** at the right of the Template heading, the **Empty**
template, and the name **My Crossing Drive**, then press **Create**. The project opens in the editor with three
entities: `cam` (a fixed camera), `floor` (100 m of grass) and `spawn` (where the player starts).

Open `scenes/main.scene.json` in a text editor. That file is the scene, and every step below changes it.

**Check:** `ok, 3 entities`.

### 2. Make room, and a sun

The route is about 400 m long, so the floor grows to 420 m square. It also gets the tag `ground`. A sun gives the
scene shadows and a direction to its light. Without one, the sky alone lights it.

> **Ask:** "Name the scene `crossing-drive`. Make the floor 420 m square, keep it 0.5 m thick, and add the tag
> `ground` to it. Add a warm directional sun, high and from the front left, after the floor."

**By hand:**

1. In `meta`, set `"name": "crossing-drive"`. Every run prints this name.
2. Replace the `floor` entity with this one, and add the `sun` right after it. The order of entities does not change
   what a scene does, but it is part of a run's fingerprint (the self-test's `digest`), so keep the order the guide
   gives.

```json
{"id": "floor", "name": "floor", "tags": ["static", "ground"], "transform": {"position": [0, -0.25, 0], "rotation": [0, 0, 0, 1], "scale": [420, 0.5, 420]}, "components": [{"type": "mesh", "asset": "cube", "color": "#587A42"}, {"type": "collider", "shape": "box", "size": [1, 1, 1]}, {"type": "rigidbody", "kind": "static"}]},
{"id": "sun", "name": "sun", "tags": [], "transform": {"position": [0, 40, 0], "rotation": [-0.38, 0.2, 0.08, 0.9], "scale": [1, 1, 1]}, "components": [{"type": "light", "kind": "directional", "color": "#FFF4E0", "intensity": 1}]}
```

**You should see** the grass reach the horizon, and shadows under things from now on. **Check:** `ok, 4 entities`.

### 3. The road

A road is a `road` component that reads its shape from a small file: the points along its centre line, its width and
its painted lines. The engine builds the surface, its collider and a centre line that scripts can read.

> **Ask:** "Add a road called `route`, 8 m wide with edge and centre lines, in `roads/route.road.json`, along these
> points: [0,0,170], [0,0,40], [0,0,-50], [-5,0,-85], [-20,0,-112], [-45,0,-130], [-80,0,-139], [-135,0,-141]."

**By hand:** create the folder `roads` in the project folder (beside `scenes`), and in it `route.road.json`:

```json
{
 "widthM": 8.0,
 "points": [
  [
   0,
   0,
   170
  ],
  [
   0,
   0,
   40
  ],
  [
   0,
   0,
   -50
  ],
  [
   -5,
   0,
   -85
  ],
  [
   -20,
   0,
   -112
  ],
  [
   -45,
   0,
   -130
  ],
  [
   -80,
   0,
   -139
  ],
  [
   -135,
   0,
   -141
  ]
 ],
 "surface": {
  "finish": {
   "preset": "asphalt",
   "grain": 0.1
  }
 },
 "markings": {
  "edge": {},
  "centre": {}
 }
}
```

Then add this entity to the scene's `entities`. `source` is the file's path from the project folder.
`material.friction` is the grip of the road's surface: 1 is dry asphalt.

```json
{"id": "route", "name": "route", "tags": ["ground"], "transform": {"position": [0, 0, 0], "rotation": [0, 0, 0, 1], "scale": [1, 1, 1]}, "components": [{"type": "road", "source": "roads/route.road.json", "material": {"friction": 1.0}}]}
```

**You should see** a grey road with white lines running away from the camera. Select `route`, and Details shows its
width and **Edit points**, which lets you drag the points in the viewport. **Check:** `ok, 5 entities`.
**If the road is missing**, the file has a mistake, and `argus validate` names it: `road-source-invalid` with the key
it did not expect. A run only warns and goes on without the road, so validate is the check to use.

### 4. The car

The car is a box with the engine's `vehicle` component and four wheels. The `sedan` preset sets its mass, engine,
brakes, tyres and steering ([Vehicles](VEHICLES.md)). The engine drives it from the actions `throttle`, `brake` and
`steer`, so the scene declares those keys. The car's script, `behaviors/drive-rules`, films it with a chase camera
and runs the rules. You add the script file now and read it in part 2.

> **Ask:** "Replace the player spawn with a car: the `sedan` vehicle preset on a 1.8 x 1.45 x 4.7 m box body
> at [0, 0.05, 150], with a blue body, a dark cabin and four cylinder wheels. Tag it `player` and `trainee`.
> Bind W/S or Up/Down to throttle, A/D or Left/Right to steer, Space to handbrake, and the pad's triggers and
> left stick. Then copy `scenes/behaviors/drive-rules.ts` from the Crossing Drive sample and attach it to the car
> with the props `limitKmh` 50, `stopLine` `stop-line`, `pedestrian` `pedestrian-1` and `autoHazard` true.
> Copy the sample's `meta.strings` too."

**By hand:**

1. Delete the `spawn` entity: the car becomes the player.
2. Make the camera a chase camera: in the `cam` entity, set the camera's `"kind"` to `"third-person"`. The car's
   script points it at the car.

```json
{"id": "cam", "name": "camera", "tags": [], "transform": {"position": [0, 3, 8], "rotation": [0, 0, 0, 1], "scale": [1, 1, 1]}, "components": [{"type": "camera", "kind": "third-person", "fov": 60, "primary": true}]}
```

3. In the scene's `meta`, add `input` and replace the empty `strings` with these (the HUD's words):

```json
"input": {
  "contexts": {
    "gameplay": {
      "throttle": {
        "kind": "axis1",
        "keys": ["S", "W", "Down", "Up"],
        "gamepad": "right-trigger"
      },
      "brake": {
        "kind": "button",
        "keys": [],
        "gamepad": "left-trigger"
      },
      "steer": {
        "kind": "axis1",
        "keys": ["A", "D", "Left", "Right"],
        "gamepad": "left-stick",
        "deadzone": 0.15
      },
      "handbrake": {
        "kind": "button",
        "keys": ["Space"],
        "gamepad": "right-bumper"
      }
    }
  },
  "active": "gameplay"
},
"strings": {
  "objective.stop": "Stop fully at the stop line",
  "objective.crossing": "Watch the crossing ahead",
  "objective.destination": "Drive on to the green post",
  "objective.arrived": "Arrived: run complete",
  "hint.start": "W to drive, S to brake, A/D to steer",
  "result.stopped": "Stopped at the line",
  "result.not-stopped": "Did not stop at the line"
}
```

   - An `axis1` action lists its keys in pairs: the key for -1, then the key for +1. So `throttle` is S (-1) and W (+1)
     for one pair, and Down and Up for the other.
   - S at -1 brakes, and reverses once the car has stopped. That is why `brake` binds no key of its own, only the
     pad's left trigger.
4. In `assets`, beside `cube`, add the meshes the car, the pedestrian and the post use. `triangles` is each mesh's
   size, which the scene's budget counts.

```json
"cylinder": {"kind": "glb", "path": "builtin:cylinder", "triangles": 96},
"capsule": {"kind": "glb", "path": "builtin:capsule", "triangles": 816},
"sphere": {"kind": "glb", "path": "builtin:sphere", "triangles": 1024}
```

5. Add the car and its parts to `entities`. Each part has `"parent": "car"`, so its position is relative to the car.
   The vehicle finds its wheels by the `wheel` component on its children, and tells front from rear by their z.
   The car's `name` is the trainee's, which the instructor console shows; the scripts and the scorecard use its
   `id`, `car`.

```json
{"id": "car", "name": "Sam Taylor", "tags": ["player", "trainee"], "transform": {"position": [0, 0.05, 150], "rotation": [0, 0, 0, 1], "scale": [1, 1, 1]}, "components": [{"type": "collider", "shape": "box", "size": [1.8, 1.45, 4.7], "center": [0, 0.87, 0]}, {"type": "rigidbody", "kind": "dynamic"}, {"type": "vehicle", "preset": "sedan"}, {"type": "script", "module": "behaviors/drive-rules", "props": {"limitKmh": 50, "stopLine": "stop-line", "pedestrian": "pedestrian-1", "autoHazard": true}}]},
{"id": "car-body", "name": "car-body", "tags": [], "transform": {"position": [0, 0.75, 0], "rotation": [0, 0, 0, 1], "scale": [1.8, 0.75, 4.6]}, "components": [{"type": "mesh", "asset": "cube", "color": "#1F4FA8"}], "parent": "car"},
{"id": "car-cabin", "name": "car-cabin", "tags": [], "transform": {"position": [0, 1.35, 0.2], "rotation": [0, 0, 0, 1], "scale": [1.6, 0.55, 2.3]}, "components": [{"type": "mesh", "asset": "cube", "color": "#1A1E24"}], "parent": "car"},
{"id": "car-wheel-fl", "name": "car-wheel-fl", "tags": [], "transform": {"position": [-0.775, 0.32, -1.4], "rotation": [0, 0, 0.7071, 0.7071], "scale": [0.64, 0.22, 0.64]}, "components": [{"type": "mesh", "asset": "cylinder", "color": "#151515"}, {"type": "wheel"}], "parent": "car"},
{"id": "car-wheel-fr", "name": "car-wheel-fr", "tags": [], "transform": {"position": [0.775, 0.32, -1.4], "rotation": [0, 0, 0.7071, 0.7071], "scale": [0.64, 0.22, 0.64]}, "components": [{"type": "mesh", "asset": "cylinder", "color": "#151515"}, {"type": "wheel"}], "parent": "car"},
{"id": "car-wheel-rl", "name": "car-wheel-rl", "tags": [], "transform": {"position": [-0.775, 0.32, 1.4], "rotation": [0, 0, 0.7071, 0.7071], "scale": [0.64, 0.22, 0.64]}, "components": [{"type": "mesh", "asset": "cylinder", "color": "#151515"}, {"type": "wheel"}], "parent": "car"},
{"id": "car-wheel-rr", "name": "car-wheel-rr", "tags": [], "transform": {"position": [0.775, 0.32, 1.4], "rotation": [0, 0, 0.7071, 0.7071], "scale": [0.64, 0.22, 0.64]}, "components": [{"type": "mesh", "asset": "cylinder", "color": "#151515"}, {"type": "wheel"}], "parent": "car"}
```

6. Copy `scenes/behaviors/drive-rules.ts` from the sample into your project's `scenes/behaviors/` folder (make
   the folder).

**You should see**, after F5, the camera behind the car. W drives, S brakes, and A and D steer. The HUD shows Speed,
Limit and Time, and "Stop fully at the stop line". **Check:** `ok, 11 entities`, and the ten-second run exits 0.
Without a display, step 14's self-test is the proof that the car drives.
**If the car does not move**, check the `input` block: every action needs at least one key or pad binding, and
`active` must name the context.

### 5. The stop line

Three things: white paint across the road, a sign, and a **trigger volume**. A trigger volume is a box collider
marked `isTrigger`, so it is not solid, and a `trigger` component that names the event it raises. When the car's
front enters it, the car's script hears `stop-line` and judges the stop.

> **Ask:** "At z = 40, add a white stop line across the road, a red stop sign on a grey post on the right, and an
> invisible trigger volume `stop-line` across the road that raises the trigger event `stop-line`."

**By hand:** add to `entities`:

```json
{"id": "stop-line-paint", "name": "stop-line-paint", "tags": [], "transform": {"position": [0, 0.03, 40.0], "rotation": [0, 0, 0, 1], "scale": [7.4, 0.02, 0.45]}, "components": [{"type": "mesh", "asset": "cube", "color": "#E8E6DF", "colliderMode": "none"}]},
{"id": "stop-sign-post", "name": "stop-sign-post", "tags": [], "transform": {"position": [5.2, 1.25, 41.0], "rotation": [0, 0, 0, 1], "scale": [0.08, 2.5, 0.08]}, "components": [{"type": "mesh", "asset": "cylinder", "color": "#8A8B8E"}]},
{"id": "stop-sign", "name": "stop-sign", "tags": [], "transform": {"position": [5.2, 2.75, 41.0], "rotation": [0, 0, 0, 1], "scale": [0.8, 0.8, 0.06]}, "components": [{"type": "mesh", "asset": "cube", "color": "#B0141A"}]},
{"id": "stop-line", "name": "stop line", "tags": ["volume"], "transform": {"position": [0, 1.5, 40.0], "rotation": [0, 0, 0, 1], "scale": [1, 1, 1]}, "components": [{"type": "collider", "shape": "box", "size": [10.0, 3, 0.5], "isTrigger": true}, {"type": "trigger", "event": "stop-line"}]}
```

**You should see** the line and the sign. Drive over the line and the objective changes to "Watch the crossing
ahead". **Check:** `ok, 15 entities`.

A mesh that nothing moves collides as its own shape even with no `collider` (the run counts these as
`mesh colliders`), so the car can bump the post. The paint says `"colliderMode": "none"`: it is painted on, and the
car must not bump over it. Only what part 2 tags as an obstacle counts as a collision.

### 6. The crossing

The crossing is six white stripes and a pavement on each side, at z = -20. Two volumes go with it. The
**hazard zone**, 32 m before the crossing, is where the pedestrian is sent out. The **crossing** volume tells the
rules that the car has reached the crossing.

> **Ask:** "At z = -20, add a pedestrian crossing: six white stripes 0.6 m wide and 3 m long across the road,
> and a 3 m wide grey pavement on each side, 24 m long. Add trigger volumes `crossing` at z = -20 and
> `hazard-zone` at z = 12, each across the road and raising the event of its own name."

**By hand:** add to `entities`:

```json
{"id": "crossing-stripe-1", "name": "crossing-stripe-1", "tags": [], "transform": {"position": [-3.0, 0.03, -20.0], "rotation": [0, 0, 0, 1], "scale": [0.6, 0.02, 3.0]}, "components": [{"type": "mesh", "asset": "cube", "color": "#E8E6DF", "colliderMode": "none"}]},
{"id": "crossing-stripe-2", "name": "crossing-stripe-2", "tags": [], "transform": {"position": [-1.8, 0.03, -20.0], "rotation": [0, 0, 0, 1], "scale": [0.6, 0.02, 3.0]}, "components": [{"type": "mesh", "asset": "cube", "color": "#E8E6DF", "colliderMode": "none"}]},
{"id": "crossing-stripe-3", "name": "crossing-stripe-3", "tags": [], "transform": {"position": [-0.6, 0.03, -20.0], "rotation": [0, 0, 0, 1], "scale": [0.6, 0.02, 3.0]}, "components": [{"type": "mesh", "asset": "cube", "color": "#E8E6DF", "colliderMode": "none"}]},
{"id": "crossing-stripe-4", "name": "crossing-stripe-4", "tags": [], "transform": {"position": [0.6, 0.03, -20.0], "rotation": [0, 0, 0, 1], "scale": [0.6, 0.02, 3.0]}, "components": [{"type": "mesh", "asset": "cube", "color": "#E8E6DF", "colliderMode": "none"}]},
{"id": "crossing-stripe-5", "name": "crossing-stripe-5", "tags": [], "transform": {"position": [1.8, 0.03, -20.0], "rotation": [0, 0, 0, 1], "scale": [0.6, 0.02, 3.0]}, "components": [{"type": "mesh", "asset": "cube", "color": "#E8E6DF", "colliderMode": "none"}]},
{"id": "crossing-stripe-6", "name": "crossing-stripe-6", "tags": [], "transform": {"position": [3.0, 0.03, -20.0], "rotation": [0, 0, 0, 1], "scale": [0.6, 0.02, 3.0]}, "components": [{"type": "mesh", "asset": "cube", "color": "#E8E6DF", "colliderMode": "none"}]},
{"id": "pavement-left", "name": "pavement-left", "tags": [], "transform": {"position": [-6.0, 0.06, -20.0], "rotation": [0, 0, 0, 1], "scale": [3.0, 0.12, 24.0]}, "components": [{"type": "mesh", "asset": "cube", "color": "#B4AFA6"}]},
{"id": "pavement-right", "name": "pavement-right", "tags": [], "transform": {"position": [6.0, 0.06, -20.0], "rotation": [0, 0, 0, 1], "scale": [3.0, 0.12, 24.0]}, "components": [{"type": "mesh", "asset": "cube", "color": "#B4AFA6"}]},
{"id": "crossing", "name": "crossing", "tags": ["volume"], "transform": {"position": [0, 1.5, -20.0], "rotation": [0, 0, 0, 1], "scale": [1, 1, 1]}, "components": [{"type": "collider", "shape": "box", "size": [10.0, 3, 3], "isTrigger": true}, {"type": "trigger", "event": "crossing"}]},
{"id": "hazard-zone", "name": "hazard zone", "tags": ["volume"], "transform": {"position": [0, 1.5, 12.0], "rotation": [0, 0, 0, 1], "scale": [1, 1, 1]}, "components": [{"type": "collider", "shape": "box", "size": [10.0, 3, 2], "isTrigger": true}, {"type": "trigger", "event": "hazard-zone"}]}
```

**Check:** `ok, 25 entities`.

### 7. The pedestrian

The pedestrian waits on the right pavement. The parent entity carries the collider and the script, and the two
children are what you see: a capsule body and a head. Its collider is a trigger too, so the car passes through
rather than stopping dead. The touch is reported as a collision in part 2. Its script, `behaviors/pedestrian`, walks
it across when the event `send-pedestrian` arrives.

> **Ask:** "Copy `scenes/behaviors/pedestrian.ts` from the Crossing Drive sample. Add a pedestrian
> `pedestrian-1` (tag `pedestrian`) on the right pavement at [6.2, 0, -20.8], facing across the road: a kinematic
> body with a 0.6 x 1.8 x 0.6 trigger box that raises `pedestrian-contact`, an orange capsule body and a head.
> Attach `behaviors/pedestrian` with `to` [-6.2, 0, -20.8], `speed` 1.4 and `watch` `car`."

**By hand:** copy `scenes/behaviors/pedestrian.ts` from the sample, then add to `entities`:

```json
{"id": "pedestrian-1", "name": "pedestrian", "tags": ["pedestrian"], "transform": {"position": [6.2, 0, -20.8], "rotation": [0, 0.7071, 0, 0.7071], "scale": [1, 1, 1]}, "components": [{"type": "collider", "shape": "box", "size": [0.6, 1.8, 0.6], "center": [0, 0.9, 0], "isTrigger": true}, {"type": "trigger", "event": "pedestrian-contact"}, {"type": "rigidbody", "kind": "kinematic"}, {"type": "script", "module": "behaviors/pedestrian", "props": {"to": [-6.2, 0, -20.8], "speed": 1.4, "watch": "car"}}]},
{"id": "pedestrian-1-body", "name": "pedestrian-1-body", "tags": [], "transform": {"position": [0, 0.8, 0], "rotation": [0, 0, 0, 1], "scale": [0.45, 0.6, 0.3]}, "components": [{"type": "mesh", "asset": "capsule", "color": "#F28C0F"}], "parent": "pedestrian-1"},
{"id": "pedestrian-1-head", "name": "pedestrian-1-head", "tags": [], "transform": {"position": [0, 1.6, 0], "rotation": [0, 0, 0, 1], "scale": [0.24, 0.26, 0.24]}, "components": [{"type": "mesh", "asset": "sphere", "color": "#C68E6A"}], "parent": "pedestrian-1"}
```

The pedestrian moves, so it has a `kinematic` rigid body: a body the script moves and physics does not push. The
volumes and the buildings stand still and need none.

**You should see** the pedestrian step out as you approach the crossing, and walk to the far pavement.
**Check:** `ok, 28 entities`.

### 8. The destination and the street

The destination is a green post and one more volume, near the end of the road. The buildings make the drive read as a
street. They are solid, and part 2 counts driving into one as a collision.

> **Ask:** "Near the end of the road, add a 4 m tall green post at [-118, 2, -135] and a trigger volume
> `destination` across the road at [-115, 1.5, -140.4] raising `destination`. Then put ten box buildings along
> the street, at least 8 m from the road's centre line, solid, tagged `static` and `building`."

**By hand:** add to `entities`:

```json
{"id": "destination-post", "name": "destination-post", "tags": [], "transform": {"position": [-118, 2, -135.0], "rotation": [0, 0, 0, 1], "scale": [0.3, 4, 0.3]}, "components": [{"type": "mesh", "asset": "cylinder", "color": "#2EA043"}]},
{"id": "destination", "name": "destination", "tags": ["volume"], "transform": {"position": [-115, 1.5, -140.4], "rotation": [0, 0, 0, 1], "scale": [1, 1, 1]}, "components": [{"type": "collider", "shape": "box", "size": [4, 3, 12.0], "isTrigger": true}, {"type": "trigger", "event": "destination"}]}
```

and the buildings:

```json
{"id": "building-1", "name": "building-1", "tags": ["static", "building"], "transform": {"position": [-13, 3.44, 120], "rotation": [0, 0, 0, 1], "scale": [10, 7, 12]}, "components": [{"type": "mesh", "asset": "cube", "color": "#B5654A"}, {"type": "collider", "shape": "box", "size": [1, 1, 1]}]},
{"id": "building-2", "name": "building-2", "tags": ["static", "building"], "transform": {"position": [13, 2.94, 104], "rotation": [0, 0, 0, 1], "scale": [9, 6, 14]}, "components": [{"type": "mesh", "asset": "cube", "color": "#D8CCB2"}, {"type": "collider", "shape": "box", "size": [1, 1, 1]}]},
{"id": "building-3", "name": "building-3", "tags": ["static", "building"], "transform": {"position": [-14, 3.94, 78], "rotation": [0, 0, 0, 1], "scale": [11, 8, 10]}, "components": [{"type": "mesh", "asset": "cube", "color": "#8E959E"}, {"type": "collider", "shape": "box", "size": [1, 1, 1]}]},
{"id": "building-4", "name": "building-4", "tags": ["static", "building"], "transform": {"position": [14, 2.94, 62], "rotation": [0, 0, 0, 1], "scale": [10, 6, 12]}, "components": [{"type": "mesh", "asset": "cube", "color": "#C9A77C"}, {"type": "collider", "shape": "box", "size": [1, 1, 1]}]},
{"id": "building-5", "name": "building-5", "tags": ["static", "building"], "transform": {"position": [-13, 2.94, 6], "rotation": [0, 0, 0, 1], "scale": [9, 6, 12]}, "components": [{"type": "mesh", "asset": "cube", "color": "#6F7F8C"}, {"type": "collider", "shape": "box", "size": [1, 1, 1]}]},
{"id": "building-6", "name": "building-6", "tags": ["static", "building"], "transform": {"position": [14, 4.44, -2], "rotation": [0, 0, 0, 1], "scale": [10, 9, 10]}, "components": [{"type": "mesh", "asset": "cube", "color": "#A9876B"}, {"type": "collider", "shape": "box", "size": [1, 1, 1]}]},
{"id": "building-7", "name": "building-7", "tags": ["static", "building"], "transform": {"position": [-14, 3.44, -44], "rotation": [0, 0, 0, 1], "scale": [10, 7, 14]}, "components": [{"type": "mesh", "asset": "cube", "color": "#C7C2B8"}, {"type": "collider", "shape": "box", "size": [1, 1, 1]}]},
{"id": "building-8", "name": "building-8", "tags": ["static", "building"], "transform": {"position": [15, 2.94, -50], "rotation": [0, 0, 0, 1], "scale": [9, 6, 12]}, "components": [{"type": "mesh", "asset": "cube", "color": "#7E6A5C"}, {"type": "collider", "shape": "box", "size": [1, 1, 1]}]},
{"id": "building-9", "name": "building-9", "tags": ["static", "building"], "transform": {"position": [-32, 3.44, -92], "rotation": [0, 0, 0, 1], "scale": [12, 7, 10]}, "components": [{"type": "mesh", "asset": "cube", "color": "#B9B4A6"}, {"type": "collider", "shape": "box", "size": [1, 1, 1]}]},
{"id": "building-10", "name": "building-10", "tags": ["static", "building"], "transform": {"position": [-62, 3.94, -116], "rotation": [0, 0, 0, 1], "scale": [10, 8, 10]}, "components": [{"type": "mesh", "asset": "cube", "color": "#94A3AE"}, {"type": "collider", "shape": "box", "size": [1, 1, 1]}]}
```

**You should see**, at the green post, "Arrived: run complete". Under it are whether you stopped at the line, how
many times you went over the limit, and how many collisions you had.

That is the whole world. **Check:** `ok, 40 entities` (a run says 41: the road's surface). Save it (**Ctrl+S** in
the editor, if you made changes there).

## Part 2: the rules

Part 1 copied two scripts. This part reads them, so you can change them and write your own.

### 9. How a behaviour works

A behaviour is a TypeScript module whose default export has hooks. The engine calls them:

| Hook | When |
|---|---|
| `init(ctx)` | once, when the entity is set up (again after a hot reload, with `ctx.state` kept) |
| `update(ctx, dt)` | every tick, 60 a second; `dt` is 1/60 s |
| `onTrigger(ctx, t)` | the entity enters or leaves a trigger volume, **as either side**: the car hears the volumes it drives into, and a volume's own script (the pedestrian's) hears what enters it |
| `onCollision(ctx, c)` | the entity's body starts touching another solid body; `c.other` is that entity |
| `onEvent(ctx, ev)` | any behaviour emitted an event (`ctx.events.emit`): `ev.name`, `ev.data` |

What the two scripts use, and what it is:

| Member | What |
|---|---|
| `t.event`, `t.entering`, `t.volume`, `t.other` | in `onTrigger`: the volume's event name, entering (or leaving), the volume, and what entered it |
| `ctx.entity` | this entity: `.id`, `.tags`, `.distanceTo(other)`, `.moveToward(point, metres)` |
| `ctx.vehicle` | its vehicle: `.speed` (m/s), `.throttle` (-1 to 1), `.brake` (0 to 1) |
| `ctx.world.find(id)` | another entity, by id (or `null`) |
| `ctx.events.emit(name, data)` | sends an event to every behaviour, and to day 2's journal |
| `ctx.log(name, data)` | writes a line to the log: a headless run prints it |
| `ctx.hud.objective(key)`, `ctx.hud.prompt(key)`, `ctx.hud.set(field, value, options)` | the HUD's objective line, its prompt, and a named field with a label and unit |
| `ctx.camera.follow(entity)`, `ctx.camera.set("third-person", {...})` | the chase camera and its distance, height and lag |
| `ctx.props`, `ctx.state` | the script component's props, and this instance's own state, kept across hot reloads and in recordings |

Every member is typed. Your code editor completes them from the types in the engine's `sdk` folder, and the docs
site's Script API pages describe each one. Save a script while Play runs, and the change is live at once.

### 10. The car's rules: `drive-rules.ts`

```ts
import type { Behavior, Ctx } from "@argus/api"

/**
 * Crossing Drive: the rules of the run, on the trainee's car (the scene's player entity).
 *
 * The engine drives the car itself (`vehicle` with `preset: "sedan"`, docs/VEHICLES.md): W / S or the triggers,
 * A / D or the left stick. This script only
 * - films it (a chase camera) and shows speed, the limit, the time and the next objective on the HUD,
 * - judges the stop: the slowest the car went over the last `STOP_WINDOW_M` metres before the stop line,
 * - sends the pedestrian out when the car enters the hazard zone (`autoHazard`; an instructor can send it instead
 *   with the bus event `send-pedestrian`),
 * - and emits one bus event at each moment that matters, so a scorecard, a recording or a test can read the run:
 *
 * | Event | Data | When |
 * |---|---|---|
 * | `speed-over` | `{ subject, speed_kmh, limit_kmh }` | the speed goes over the limit (once until it is back under) |
 * | `brake-applied` | `{ subject, speed_kmh }` | the brake goes on (once per press) |
 * | `stop-line` | `{ subject, stopped, min_kmh, speed_kmh }` | the car's front reaches the stop line |
 * | `crossing-entered` | `{ subject }` | the car reaches the pedestrian crossing |
 * | `trainee-collision` | `{ subject, other, speed_kmh }` | the car hits an entity tagged building, pedestrian or traffic |
 * | `destination-reached` | `{ subject, seconds }` | the car reaches the green post |
 *
 * Each is also written to the log, so a headless run prints it. The three places on the route also emit
 * `goal-reached` (`stop-line`, `crossing`, `destination`): the self-test checks them.
 * Change the numbers below, or the props in the scene, and press Play again.
 */

const KMH = 3.6
/** Slower than this counts as stopped, km/h. */
const STOPPED_KMH = 1
/** The stop is judged over this many metres before the line, measured from the car's front. */
const STOP_WINDOW_M = 10
/** Half the car's length, m: its front bumper is this far ahead of its centre (the collider is 4.7 m long). */
const HALF_LENGTH = 2.35
/** After a `speed-over`, the speed must fall this far under the limit before another one counts, km/h. */
const REARM_KMH = 2
/** Hitting an entity with one of these tags is a collision. Tag anything else the trainee must not hit. */
const OBSTACLES = ["building", "pedestrian", "traffic"]

interface State {
  /** The clock starts on the first touch of the throttle, not at Play. */
  started: boolean
  seconds: number
  /** The slowest the car went inside the stop window, km/h. */
  approachMinKmh: number
  over: boolean
  overCount: number
  /** The brake was held last tick. */
  braking: boolean
  collisions: number
  /** The places already reached, so each is reported once. */
  reached: string[]
  stopped: boolean | null
}

interface Props {
  /** The speed limit, km/h. */
  limitKmh?: number
  /** The stop-line volume's entity id. */
  stopLine?: string
  /** The pedestrian sent out at the hazard zone. */
  pedestrian?: string
  /** Send the pedestrian out when the car enters the hazard zone (false: only an instructor does). */
  autoHazard?: boolean
}

type C = Ctx<State, Props>

/** Emits a scenario event, and logs it: a headless run (`argus run . --headless --self-test ...`) prints each one. */
function report(ctx: C, name: string, data: Record<string, unknown>) {
  ctx.events.emit(name, data)
  ctx.log(name, data)
}

/** True the first time `place` is reached. The three places on the route also count as goals. */
function once(ctx: C, place: string, goal = true): boolean {
  if (ctx.state.reached.includes(place)) return false
  ctx.state.reached.push(place)
  if (goal) ctx.events.emit("goal-reached", { goal: place })
  return true
}

/** Braking: the brake held, or the throttle pulled back (S) while still rolling forward. */
function braking(ctx: C): boolean {
  const v = ctx.vehicle
  return v !== null && (v.brake > 0.1 || (v.throttle < -0.1 && v.speed > 0.5))
}

function kmh(ctx: C): number {
  return Math.abs(ctx.vehicle?.speed ?? 0) * KMH
}

function collided(ctx: C, other: string) {
  ctx.state.collisions += 1
  report(ctx, "trainee-collision", { subject: ctx.entity.id, other, speed_kmh: round1(kmh(ctx)) })
}

function round1(x: number): number {
  return Math.round(x * 10) / 10
}

export default {
  init(ctx) {
    const s = ctx.state
    // `init` runs again after a hot reload with `state` kept: fill only what is missing.
    if (typeof s.seconds !== "number") {
      s.started = false
      s.seconds = 0
      s.approachMinKmh = Infinity
      s.over = false
      s.overCount = 0
      s.braking = false
      s.collisions = 0
      s.reached = []
      s.stopped = null
    }
    ctx.camera.follow(ctx.entity)
    ctx.camera.set("third-person", { distance: 8, pitch: -0.22, eyeHeight: 1.6, recenterDelay: 0, lookAhead: 0.3 })
    ctx.hud.objective(s.reached.includes("stop-line") ? "objective.destination" : "objective.stop")
    ctx.hud.set("limit", ctx.props.limitKmh ?? 50, { label: "Limit", unit: "km/h" })
    if (!s.started) ctx.hud.prompt("hint.start")
  },

  update(ctx, dt) {
    const s = ctx.state
    const v = ctx.vehicle
    if (v === null) return
    const limit = ctx.props.limitKmh ?? 50
    const speed = kmh(ctx)
    const done = s.reached.includes("destination")

    if (!s.started && Math.abs(v.throttle) > 0.05) {
      s.started = true
      ctx.hud.prompt(null)
    }
    if (s.started && !done) s.seconds += dt

    // The stop: the slowest speed while the car's front is within STOP_WINDOW_M of the line.
    if (!s.reached.includes("stop-line")) {
      const line = ctx.world.find(ctx.props.stopLine ?? "stop-line")
      if (line !== null && ctx.entity.distanceTo(line) - HALF_LENGTH <= STOP_WINDOW_M) {
        s.approachMinKmh = Math.min(s.approachMinKmh, speed)
      }
    }

    // The brake: one event per press, so a reaction time can be measured from the pedestrian stepping out.
    const brake = braking(ctx)
    if (brake && !s.braking) report(ctx, "brake-applied", { subject: ctx.entity.id, speed_kmh: round1(speed) })
    s.braking = brake

    // Over the limit: once per excursion.
    if (!s.over && speed > limit) {
      s.over = true
      s.overCount += 1
      report(ctx, "speed-over", { subject: ctx.entity.id, speed_kmh: round1(speed), limit_kmh: limit })
    } else if (s.over && speed < limit - REARM_KMH) {
      s.over = false
    }

    ctx.hud.set("speed", Math.round(speed), { label: "Speed", unit: "km/h" })
    ctx.hud.set("time", s.seconds, { label: "Time", unit: "s", decimals: 1 })
  },

  onTrigger(ctx, t) {
    if (!t.entering) return
    const s = ctx.state
    switch (t.event) {
      case "stop-line":
        if (!once(ctx, "stop-line")) return
        s.stopped = s.approachMinKmh < STOPPED_KMH
        // min_kmh is what the stop is judged on (the slowest in the window); speed_kmh is the speed crossing the line.
        report(ctx, "stop-line", {
          subject: ctx.entity.id,
          stopped: s.stopped,
          min_kmh: round1(Number.isFinite(s.approachMinKmh) ? s.approachMinKmh : kmh(ctx)),
          speed_kmh: round1(kmh(ctx)),
        })
        ctx.hud.objective("objective.crossing")
        return
      case "hazard-zone":
        if (ctx.props.autoHazard !== false && once(ctx, "hazard-zone", false)) {
          ctx.events.emit("send-pedestrian", { pedestrian: ctx.props.pedestrian ?? "pedestrian-1" })
        }
        return
      case "crossing":
        if (!once(ctx, "crossing")) return
        report(ctx, "crossing-entered", { subject: ctx.entity.id })
        ctx.hud.objective("objective.destination")
        return
      case "destination":
        if (!once(ctx, "destination")) return
        report(ctx, "destination-reached", { subject: ctx.entity.id, seconds: round1(s.seconds) })
        ctx.hud.objective("objective.arrived")
        // A HUD text is a key into meta.strings, never the text itself: one key per state, numbers as numbers.
        ctx.hud.set("stop", s.stopped ? "result.stopped" : "result.not-stopped")
        ctx.hud.set("over", s.overCount, { label: "Times over the limit" })
        ctx.hud.set("collisions", s.collisions, { label: "Collisions" })
        return
      case "pedestrian-contact":
        collided(ctx, t.volume.id)
        return
    }
  },

  onCollision(ctx, c) {
    // Only what is tagged as an obstacle counts. The car touches the floor and the road's surface (an entity the
    // road builds, `route-surface`) all the time, and that is driving, not a collision.
    if (OBSTACLES.some((tag) => c.other.tags.includes(tag))) collided(ctx, c.other.id)
  },
} satisfies Behavior<State, Props>
```

Things to notice:

- **Nothing reads the wall clock.** Time is the sum of `dt`. That is why a run plays back the same.
- **Each place reports once** (`once`), and the three places on the route also emit `goal-reached`, which the
  self-test counts.
- **The stop is judged over a window**: the slowest speed while the car's front is within 10 m of the line.
- **Only tagged obstacles are collisions.** The car touches the floor and the road's surface all the time, which is
  driving.
- **Every event is also logged** (`report`), so a run in a terminal prints it.
- **The HUD takes keys, not text.** `ctx.hud.objective("objective.stop")` shows the string `objective.stop` from
  `meta.strings`, and `ctx.hud.set("speed", 42, { label: "Speed", unit: "km/h" })` shows a number with its words.
  A text you build at run time would show as `[[the text]]`. The critic warns about it (`missing-string`).

### 11. The pedestrian: `pedestrian.ts`

```ts
import type { Behavior, Ctx, Vec3 } from "@argus/api"

/**
 * Crossing Drive: a pedestrian who steps out across the road when told to.
 *
 * It waits on the pavement until the bus event `send-pedestrian` arrives. The trainee's car sends it on entering
 * the hazard zone; an instructor or a test can send it at any time with `{ pedestrian: "<this entity's id>" }`, or
 * with no data for every pedestrian. Then it walks to `to` at `speed` m/s and emits
 * `pedestrian-stepped-out` `{ pedestrian, distance_m }`, the distance being from the car it `watch`es.
 * The event is also written to the log, so a headless run prints it. It goes once, and stops where it is if the car
 * touches it (the car reports that as `trainee-collision`).
 *
 * As it walks, its limbs swing: children whose ids end in `-leg-l`, `-leg-r`, `-arm-l` and `-arm-r` turn about
 * the hip and the shoulder, a stride every `STRIDE_M` metres. A pedestrian without them glides.
 */

/** Metres walked per stride, left foot to left foot. */
const STRIDE_M = 1.5
/** How far the legs and the arms swing each way, radians. */
const LEG_SWING = 0.42
const ARM_SWING = 0.32
/** A limb turns about this share of its half-length above its centre: near the top of the capsule. */
const PIVOT = 0.8
/** Seconds the limbs take to come to rest when it stops. */
const SETTLE_S = 0.3
/** Each limb: its id's ending, its swing, and which way it swings (an arm against the leg on its side). */
const LIMBS: [string, number, number][] = [
  ["-leg-l", LEG_SWING, 1],
  ["-leg-r", LEG_SWING, -1],
  ["-arm-l", ARM_SWING, -1],
  ["-arm-r", ARM_SWING, 1],
]

interface State {
  walking: boolean
  gone: boolean
  /** Metres walked: the stride's phase. */
  walked: number
  /** How much of the swing shows: 1 while walking, easing to 0 when it stops. */
  swing: number
}

interface Props {
  /** Where it walks to: the far pavement. */
  to?: Vec3
  /** Walking speed, m/s. */
  speed?: number
  /** The car the reported distance is measured from. */
  watch?: string
}

type C = Ctx<State, Props>

/** Turns each limb about its top (about X; positive swings it forward, toward -Z), keeping the top where it is. */
function pose(ctx: C, phase: number, amount: number) {
  for (const limb of ctx.entity.children) {
    const kind = LIMBS.find(([end]) => limb.id.endsWith(end))
    if (kind === undefined) continue
    const angle = kind[1] * kind[2] * Math.sin(phase) * amount
    const was = 2 * Math.atan2(limb.rotation[0], limb.rotation[3])
    const d = PIVOT * limb.scale[1]
    const [x, y, z] = limb.position
    const topY = y + d * Math.cos(was)
    const topZ = z + d * Math.sin(was)
    limb.position = [x, topY - d * Math.cos(angle), topZ - d * Math.sin(angle)]
    limb.rotation = [Math.sin(angle / 2), 0, 0, Math.cos(angle / 2)]
  }
}

export default {
  init(ctx) {
    if (typeof ctx.state.walking !== "boolean") {
      ctx.state.walking = false
      ctx.state.gone = false
    }
    if (typeof ctx.state.walked !== "number") {
      ctx.state.walked = 0
      ctx.state.swing = 0
    }
  },

  update(ctx, dt) {
    const s = ctx.state
    if (s.walking) {
      const to = ctx.props.to ?? [-6, 0, 0]
      const step = (ctx.props.speed ?? 1.4) * dt
      if (ctx.entity.moveToward(to, step)) s.walking = false
      s.walked += step
      s.swing = 1
    } else if (s.swing > 0) {
      s.swing = Math.max(0, s.swing - dt / SETTLE_S)
    } else {
      return
    }
    pose(ctx, (s.walked / STRIDE_M) * 2 * Math.PI, s.swing)
  },

  onEvent(ctx, ev) {
    if (ev.name !== "send-pedestrian" || ctx.state.gone) return
    // From a script: `{ pedestrian }`. Injected by an instructor: the session command's `target`, if it names one.
    const data = ev.data as { pedestrian?: string; target?: string | null } | null
    const who = data?.pedestrian ?? (typeof data?.target === "string" && data.target !== "world" ? data.target : undefined)
    if (who !== undefined && who !== ctx.entity.id) return
    ctx.state.gone = true
    ctx.state.walking = true
    const car = ctx.world.find(ctx.props.watch ?? "car")
    const distance = car === null ? -1 : Math.round(car.distanceTo(ctx.entity) * 10) / 10
    const stepped = { pedestrian: ctx.entity.id, distance_m: distance }
    ctx.events.emit("pedestrian-stepped-out", stepped)
    ctx.log("pedestrian-stepped-out", stepped)
  },

  onTrigger(ctx, t) {
    // Touched by the trainee's car: stop where it is.
    if (t.entering && t.event === "pedestrian-contact" && t.other.tags.includes("trainee")) ctx.state.walking = false
  },
} satisfies Behavior<State, Props>
```

The car sends `send-pedestrian` when it enters the hazard zone. On day 2 the instructor's console sends the same
event, so the hazard can come whenever the instructor chooses. Set the car's `autoHazard` prop to `false` and only
the instructor sends it.

### 12. The events

These are the scenario's output. Day 2 declares them, scores them and marks them on the review timeline.

| Event | Data | When |
|---|---|---|
| `speed-over` | `subject, speed_kmh, limit_kmh` | the speed goes over the limit (once until it is back under) |
| `brake-applied` | `subject, speed_kmh` | the brake goes on (once per press) |
| `stop-line` | `subject, stopped, min_kmh, speed_kmh` | the car's front reaches the line; `min_kmh` is the slowest it went in the last 10 m, `stopped` is true if that was under 1 km/h, and `speed_kmh` is its speed crossing the line |
| `pedestrian-stepped-out` | `pedestrian, distance_m` | the pedestrian starts across; the distance is from the car |
| `crossing-entered` | `subject` | the car reaches the crossing |
| `trainee-collision` | `subject, other, speed_kmh` | the car hits an entity tagged `building`, `pedestrian` or `traffic` |
| `destination-reached` | `subject, seconds` | the car reaches the green post |

`subject` is the trainee's car. A field named after a role (`subject`, `other`, `pedestrian`) is an entity id: on
day 2 the journal links the event to that entity.

**To see them:** every event is also logged, so step 14's self-test prints the run line by line, each line starting
with the entity's id:

```text
[car] stop-line {"min_kmh":0,"speed_kmh":19.2,"stopped":true,"subject":"car"}
[pedestrian-1] pedestrian-stepped-out {"distance_m":36.4,"pedestrian":"pedestrian-1"}
```

### 13. Change it

| To | Change |
|---|---|
| Lower the limit | the car's `limitKmh` prop |
| Make the stop stricter | `STOPPED_KMH` or `STOP_WINDOW_M` at the top of `drive-rules.ts` |
| Bring the pedestrian out later | move `hazard-zone` toward the crossing |
| Change the route | the `points` in `roads/route.road.json` (move the stop line, crossing and destination with it, then re-tune the self-test) |
| Drive something else | the car's `vehicle` preset: `suv`, `pickup` or `van` ([Vehicles](VEHICLES.md)) |
| Count more things as collisions | give them the tag `building`, `pedestrian` or `traffic`, or add a tag to `OBSTACLES` in `drive-rules.ts` |

> **Ask:** "Move the stop line 20 m further on, and make the pedestrian walk at 2 m/s." Your assistant moves the
> paint, the sign and the volume together, and changes the pedestrian's `speed` prop.

### 14. A self-test

A self-test is a script of inputs the engine plays by itself, headless, in fixed ticks ([Self-test](SELF-TEST.md)).
This one drives like a careful trainee:

- under the limit;
- a full stop just short of the stop line;
- a brake as the pedestrian steps out, and a wait while they cross;
- then on to the green post.

Each `drive` step follows the road at a throttle for a number of ticks, each `axis` step holds the throttle (a negative
value brakes), and each `action` step holds a button, `brake` or `handbrake`. End a brake just before the car stops and
hold the handbrake while it waits: a brake or a negative throttle held at a standstill reverses. The numbers are tuned
to this route: change the route or move the volumes, and tune them again.

**What it proves:** the route can be driven end to end, every rule fires (the lines it prints), and no script fails.
It passes when the stop line, the crossing and the green post are reached with no script error. **It does not judge
the drive:** a run that misses the stop still passes. Judging is day 2's scorecard. Every export carries the
self-test, so it also checks a training PC.

> **Ask:** "Write a self-test `crossing-drive` that drives the route carefully, stops at the line, brakes for the
> pedestrian, and expects the three goals. Run it, and tune it until the stop line says stopped."
> (Your assistant writes it with `behaviour.write` and runs it with `critic.selftest`.)

**By hand:** create `selftest/crossing-drive.json` in the project folder (beside `scenes/`):

```json
{
  "selfTest": 1,
  "description": "A careful drive: under the limit, a full stop short of the stop line, a brake as the pedestrian steps out, a wait while they cross, then on to the green post. Passes when the stop line, the crossing and the green post are reached with no script errors. Its timings are this route's: move the route or the volumes and re-tune them.",
  "maxFrames": 4200,
  "steps": [
    {
      "frames": 30
    },
    {
      "drive": "road",
      "road": "route",
      "throttle": 0.25,
      "lookAheadM": 12,
      "frames": 600
    },
    {
      "drive": "road",
      "road": "route",
      "throttle": 0.0,
      "lookAheadM": 12,
      "frames": 105
    },
    {
      "action": "brake",
      "frames": 82
    },
    {
      "action": "handbrake",
      "frames": 128
    },
    {
      "drive": "road",
      "road": "route",
      "throttle": 0.25,
      "lookAheadM": 12,
      "frames": 360
    },
    {
      "drive": "road",
      "road": "route",
      "throttle": 0.0,
      "lookAheadM": 12,
      "frames": 115
    },
    {
      "axis": "throttle",
      "value": [
        -0.3,
        0
      ],
      "frames": 183
    },
    {
      "action": "handbrake",
      "frames": 297
    },
    {
      "drive": "road",
      "road": "route",
      "throttle": 0.25,
      "lookAheadM": 12,
      "frames": 500
    },
    {
      "axis": "throttle",
      "value": [
        -0.4,
        0
      ],
      "frames": 30
    },
    {
      "drive": "road",
      "road": "route",
      "throttle": 0.15,
      "lookAheadM": 12,
      "frames": 220
    },
    {
      "axis": "throttle",
      "value": [
        -0.4,
        0
      ],
      "frames": 30
    },
    {
      "drive": "road",
      "road": "route",
      "throttle": 0.15,
      "lookAheadM": 12,
      "frames": 220
    },
    {
      "axis": "throttle",
      "value": [
        -0.4,
        0
      ],
      "frames": 30
    },
    {
      "drive": "road",
      "road": "route",
      "throttle": 0.15,
      "lookAheadM": 12,
      "frames": 220
    },
    {
      "axis": "throttle",
      "value": [
        -0.4,
        0
      ],
      "frames": 30
    },
    {
      "drive": "road",
      "road": "route",
      "throttle": 0.15,
      "lookAheadM": 12,
      "frames": 160
    },
    {
      "action": "brake",
      "frames": 80
    },
    {
      "action": "handbrake",
      "frames": 70
    },
    {
      "expect": "goals",
      "min": 3,
      "timeout": 10
    },
    {
      "capture": "arrived"
    }
  ],
  "expect": {
    "scriptErrors": 0
  }
}
```

and run it from a terminal in the project folder:

```sh
argus run . --headless --self-test crossing-drive
```

**You should see** the run, then the verdict:

```text
[car] brake-applied {"speed_kmh":47.2,"subject":"car"}
[car] stop-line {"min_kmh":0,"speed_kmh":19.2,"stopped":true,"subject":"car"}
argus: self-test: goal reached: "stop-line" at frame 1257
[pedestrian-1] pedestrian-stepped-out {"distance_m":36.4,"pedestrian":"pedestrian-1"}
[car] brake-applied {"speed_kmh":39.9,"subject":"car"}
[car] crossing-entered {"subject":"car"}
argus: self-test: goal reached: "crossing" at frame 2454
...
[car] destination-reached {"seconds":58.7,"subject":"car"}
argus: self-test: goal reached: "destination" at frame 3552
argus: self-test passed: ...self-test.json
```

The report, `self-test.json`, goes to the engine's log folder (`%LOCALAPPDATA%\Argus\logs` on Windows,
`~/.local/state/argus/logs` on Linux). Set `ARGUS_SELF_TEST_OUT` to a file path to put it elsewhere. A failed run
names the step that failed and the goals it reached. Each `drive` step's `distanceM` and `s` in the report say how
far it got, which is what you tune.

## Done

You have a scenario a trainee can drive, with its rules in two short scripts, its events listed, and a self-test.
Compare it with the sample: your `scenes/main.scene.json` should match it entity for entity, in the same order. Then
go on to day 2: [Events, metrics and scorecards](TRAINING-EVENTS-SCORECARDS.md).

