CAPSTONE — Coastal Race: ship it

In this lesson you'll learn to

  • ✓Student assembles all 22 lessons into a complete playable boat-race mode with lobby, synced start, checkpoints, storm timeout, eliminations, kraken NPC, podium, and persistent win tracking.

Student assembles all 22 lessons into a complete playable boat-race mode with lobby, synced start, checkpoints, storm timeout, eliminations, kraken NPC, podium, and persistent win tracking.

📖 Reference & full walkthrough: CAPSTONE — Coastal Race: ship it

🔁 Builds on: north-jungle modules, south-shores HUD module + SpawnProp, center-village round-logic pattern, east-volcano persistable [player]int pattern

🧩 Your capstone piece: The whole minigame (multi-step project article + data/learning-paths/path-west-coves.json record; also fix vehicle-spawner-boat-device FAIL 1 err — the boats! — and lean on race-checkpoint-device + race-manager-device + timer-device, all PASS, as device-track sidebars)


Overview

You race boats around a course of buoys in the coves — and the whole time, a storm is closing on the water. Finish your laps before it shuts the course down, or be the racer who got farthest when it did.

That's the game. Here's the lesson, and it's the capstone idea itself: a capstone is not a from-scratch build. You compose what earlier lessons already taught and add the smallest possible amount of new glue. This mode reuses the volcano's shrinking safe-zone whole — not a line of it rebuilt — runs the race as the same phase machine you've now seen in four worlds, and moves everything on signals instead of polling. Four small modules, one job each:

File Its one job
cr_types.verse the race's shared vocabulary: phases, the per-racer record, the start/finish/timeout signals
cr_race_manager.verse the rules: count laps in checkpoint order, run the phase machine, decide the winner
cr_storm.verse the storm — built entirely by reusing the volcano zone; it only turns "caught outside" into a race penalty
cr_mode_device.verse the wiring: boat docks, buoy checkpoints, the storm marker, the HUD

The dependency arrow points one way — device → rules → vocabulary — so the rules read (and test) on their own.

Reuse the whole mechanic — add zero new storm logic

This is the heart of the lesson. The shrinking, closing boundary — its timer, its scale, its check for who's outside — already exists in the volcano zone. You don't copy any of it. Your entire storm layer is this translator:

storm_bridge := class(vs_safe_zone.zone_damage_sink):
    Consequences : race_consequence_sink
    Consequence : storm_consequence = storm_consequence.Slow

    OnPlayerOutsideZone<override>(Player : player) : void =
        Consequences.OnCaughtInStorm(Player, Consequence)

When the zone reports a racer caught outside, the bridge turns that into a race penalty — slow the boat, or sweep it back to the start. That's composition: you ride a module's public surface and re-derive nothing inside it. The same mechanic served as a lava boundary there and serves as a race storm here, byte-for-byte unchanged — which is exactly why building modules with clean public surfaces pays off an island later.

The race is a state machine (fifth world)

By now you could write this in your sleep — and that's the point:

case (Blackboard.Phase):
    race_phase.Lobby =>
        Triggers.StartEvent.Await()
        set Blackboard.Phase = race_phase.Countdown
    race_phase.Racing =>
        RunRacing(Blackboard, Triggers)
        set Blackboard.Phase = race_phase.Finished

Each phase is one case arm inside a loop. The interesting phase is Racing, which runs three things at once and ends the moment any one of them finishes:

race:
    RunClock(Blackboard)
    WaitFinish(Blackboard, Triggers)
    WaitTimeout(Blackboard, Triggers)

race: runs every leg together and returns with the first to complete. The clock leg never completes on its own — it just ticks the stopwatch — so the round ends either when a racer finishes or when the storm closes the water, whichever comes first. One race: expression is the mode's entire ending logic.

Count laps in one place, in order

if (Rec.NextCheckpoint = Idx):
    Last := Config.CheckpointCount - 1
    if (Idx >= Last):
        set Rec.LapsComplete += 1
        set Rec.NextCheckpoint = 0

A crossing only counts if it's the buoy the racer was due to cross next — nobody cuts the course. The last buoy is the finish line: cross it in order and the lap ticks over. Laps are counted here and nowhere else; every buoy on the course just forwards its crossing to this one authority. That single-owner rule is what keeps a multi-device mode honest — when a lap count looks wrong, there is exactly one function to read.

Signals, not polling

FinishEvent<public> : event(agent) = event(agent){}

Your own event(agent) is await-only — the race machine Awaits it. The device layer Subscribes the real hardware events (a button press, a checkpoint trigger) and Signals your event from inside those handlers. The round advances the instant something happens, with no per-tick "did anyone finish yet?" loop anywhere in the mode.

No division — rank by whole numbers

If the storm ends the round before anyone finishes, you still need a winner — the racer who got farthest:

Score := Rec.LapsComplete * 1000 + Rec.NextCheckpoint

Laps first, then progress within the current lap: one multiply and one add, never a division. Whole-number ranking keeps every comparison exact and side-steps the rounding corners of dividing floats. (The jungle rally ranks with a comparison chain instead — two mode, same discipline: rank by ordering, never by arithmetic that can lie.)

Build it in the editor

Place, and link in the mode device's Details panel:

  • two boat spawners as docks;
  • four trigger devices as buoys, linked in course order — the last is the finish line;
  • a button to start, a player spawner as the start dock, a HUD message device, and an End Game device;
  • the storm's penalty devices: a player movement settings device for the "slow" penalty and a teleporter for the "reset";
  • one mesh for the storm marker — the reused zone attaches to it in code.

Press start, round the buoys, and try to finish before the storm closes the coves. Then let the storm win a round on purpose, and watch the timeout crown whoever was ahead.

What carries forward

  • Compose, don't rebuild. The whole storm is one bridge class riding another module's public surface. Clean surfaces are why.
  • race: is an ending machine. Run the clock, the finish, and the timeout together; the first to complete ends the round. No flags, no polling.
  • One authority per fact — laps counted in one function, forwarded to by every buoy.
  • Your events are awaited; hardware events are subscribed. The device layer translates between the two worlds, once.
  • Rank without division — whole-number keys or comparison chains, both exact, both instant.
🔒

Keep going — free

You've read the intro. The rest of this lesson is free for members. Sign in to continue and track your progress.

Sign in free to continue this lesson
Pass the quiz above to chart this quest in your Journal.

Sources

/guides/west-coves-coastal-race
Source

Biloxi Studios original lesson

Guided course

Add this lesson to your free study plan.

🧭 The Keeper’s log

Quest complete? Chart your next heading from the 🦑 West Coves expedition.

⛵ Take me there