The sea stacks off the west coves all lean on each other — pull one pillar out and the arch above it stays standing only if something else carries the load. Your race code wants the same architecture. Right now, seven lessons into the Coastal Race build, you have timing logic here, array lookups there, and Print calls scattered like barnacles. Inkbeard has dragged worse codebases to the bottom of the channel for less. Today you extract the load-bearing functions into ONE shared module — RaceHelpers — and import it from any file that needs it. You already learned what a module is with Barnaby back in the North Jungle (verse-modules and creating-custom-modules); this lesson turns that knowledge into the SOLID layering habit every later zone quietly depends on.
What you will build
The RaceHelpers module — the capstone piece every subsequent West Coves lesson imports:
FormatTime— turns raw seconds into aM:SSrace-clock string (the countdown lesson and the finish-line ticker both call it).SafeIndex— a fallback-returning array lookup, so checkpoint-name lookups never fail (builds directly onarray-bounds-are-failable, lesson 6).RaceLog— one shared logging door with a[CoastalRace]prefix (upgrading the rawPrinthabit fromprint-debugging-in-verse, lesson 1).
Write these once, import them everywhere. When the storm-timeout lesson, the spawn-monitor lesson, and the state-machine capstone all need a race clock, they call the SAME FormatTime — and a formatting tweak is a one-line change in one file.
Walkthrough
Step 1 — declare the module
A Verse module is a named bundle of code. Functions marked <public> inside it are callable from any file that imports the module with using { RaceHelpers }. Two rules from Barnaby's jungle lessons still rule here:
<public>or it doesn't exist — a module function without<public>is internal to the module. Importers can't see it.- No
@editableinside a module — modules are not devices; they have no Details panel. Device references get passed in as parameters.
Step 2 — the failable math inside FormatTime
Quotient[X, Y] and Mod[X, Y] are <decides> functions from /Verse.org/Verse — they can fail (divide by zero), so Verse makes you call them with square brackets inside a failure context. We use the multi-condition if: block: if both succeed, we format the clock; if not, the fallback "0:00" survives.
Step 3 — the complete file
Here is the whole thing: the module, then a demo device that imports it and exercises all three helpers. Note the demo unwraps the ?agent from TriggeredEvent before using it — the same optional-unwrapping muscle you built in lesson 4.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Verse }
using { /UnrealEngine.com/Temporary/Diagnostics }
# RaceHelpers — the shared toolbox every Coastal Race lesson imports.
# In your project, keep this module in its own file (RaceHelpers.verse).
RaceHelpers := module:
# Turns raw seconds into a "M:SS" race-clock string.
# 754 seconds -> "12:34".
FormatTime<public>(TotalSeconds : int) : string =
var Clock : string = "0:00"
if:
Minutes := Quotient[TotalSeconds, 60]
Seconds := Mod[TotalSeconds, 60]
then:
if (Seconds < 10):
set Clock = "{Minutes}:0{Seconds}"
else:
set Clock = "{Minutes}:{Seconds}"
Clock
# Safe array lookup: hands back a fallback instead of failing.
SafeIndex<public>(Names : []string, Index : int, Fallback : string) : string =
if (Found := Names[Index]):
Found
else:
Fallback
# One shared logging door — every race message swims through here.
RaceLog<public>(Message : string) : void =
Print("[CoastalRace] {Message}")
# Import our own module so its helpers are in scope below.
using { RaceHelpers }
sea_stack_helper_demo := class(creative_device):
# Wire this to a checkpoint pressure plate in the Details panel.
@editable
CheckpointPlate : trigger_device = trigger_device{}
# Named gates along the channel — indexed by checkpoint number.
CheckpointNames : []string = array{"Dock Launch", "Sea-Stack Slalom", "Kraken Channel", "Shore Sprint"}
var CheckpointsPassed : int = 0
OnBegin<override>()<suspends>:void =
# Prove all three helpers work the moment the island starts.
RaceLog("Helpers online. A 754-second run reads as {FormatTime(754)}")
CheckpointPlate.TriggeredEvent.Subscribe(OnCheckpointPassed)
# TriggeredEvent hands us ?agent — unwrap before using it.
OnCheckpointPassed(Agent : ?agent):void =
if (Racer := Agent?):
GateName := SafeIndex(CheckpointNames, CheckpointsPassed, "Uncharted Gate")
RaceLog("Checkpoint cleared: {GateName}")
set CheckpointsPassed += 1
What each part does
| Piece | Why it matters |
|---|---|
RaceHelpers := module: |
Declares the shared toolbox. In your project, put it in its own file so every device file can import it. |
FormatTime<public>(...) |
<public> makes it visible to importers. Quotient[754, 60] → 12, Mod[754, 60] → 34 → "12:34". |
if: ... then: |
The failure context both <decides> calls need. If either fails, Clock keeps its "0:00" default. |
SafeIndex(...) |
Names[Index] is failable; the if catches the out-of-bounds case and returns Fallback instead. |
RaceLog(...) |
One prefix, one place. When you later swap Print for a HUD message, you edit ONE function. |
using { RaceHelpers } |
The import. Without this line, FormatTime is an unknown identifier — even in the same project. |
OnCheckpointPassed(Agent : ?agent) |
TriggeredEvent is listenable(?agent); unwrap with if (Racer := Agent?) before use. |
Drop the file into Verse Explorer, build, place the device, wire a trigger to CheckpointPlate, and step on it: the log reads [CoastalRace] Checkpoint cleared: Dock Launch, then Sea-Stack Slalom, and past the end of the array — Uncharted Gate. No crash. That's SafeIndex earning its keep.
Common patterns
Pattern 1 — a second device imports the same module
The whole point of a shared module is that other files import it too. Here a lap counter pulls in its own helper module — in a real project the module sits in its own file, and every consumer file writes its own using { lap_helpers } line (imports do NOT carry over between files).
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# In a real project this module lives in its own file —
# any .verse file in the project can then `using { lap_helpers }`.
lap_helpers := module:
AnnounceLap<public>(LapNumber : int, LapTotal : int) : void =
Print("Lap {LapNumber} of {LapTotal} — pull harder, sailor!")
using { lap_helpers }
lap_counter_device := class(creative_device):
@editable
LapPlate : trigger_device = trigger_device{}
LapTotal : int = 3
var CurrentLap : int = 1
OnBegin<override>()<suspends>:void =
LapPlate.TriggeredEvent.Subscribe(OnLapLine)
OnLapLine(Agent : ?agent):void =
if (Racer := Agent?):
AnnounceLap(CurrentLap, LapTotal)
set CurrentLap += 1
Pattern 2 — modules hold shared constants too
Remember RaceConfig from the constants lesson (lesson 3)? A module is exactly where those tuning values belong — one source of truth the whole race reads.
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# Modules hold shared CONSTANTS too — one source of truth for tuning.
race_config := module:
LapCount<public> : int = 3
MinRacers<public> : int = 2
StormTimeoutSeconds<public> : int = 300
using { race_config }
config_reader_device := class(creative_device):
OnBegin<override>()<suspends>:void =
Print("Race rules: {LapCount} laps, at least {MinRacers} racers")
Print("Storm closes the channel after {StormTimeoutSeconds}s")
Where this goes next
RaceHelpers is now a permanent pillar of the Coastal Race capstone — every subsequent West Coves lesson imports it:
countdown-timer-loop(lesson 9) callsFormatTimefor the 3-2-1-GO broadcast clock.launching-logic-with-spawn(lesson 10) and the concurrency lessons log every monitor task throughRaceLog.- The checkpoint lessons lean on
SafeIndexevery time a racer advances through the gate array. - The
west-coves-coastal-racecapstone (lesson 23) assembles all of it — and because every phase logged through the same door and read the same clock, debugging the full race is aRaceLoggrep instead of a kraken hunt.
Extract shared logic early, import it everywhere: that is the layering habit the East Volcano's advanced systems and the Deeps' scene-graph components will assume you already have. Inkbeard approves — grudgingly, which is his warmest setting.