← Back to path Download .md

πŸ¦‘ West Coves

Teen 23 lessons Β· ~184 min Β· Generated 2026-07-04

REAL UEFN island for west-coves must be a water-course archipelago: a sheltered lobby lagoon (Marooned Town dock) with player spawn pads + a matchmaking-portal return link to AweShucks Town, then a looped boat channel threading mangrove gates, sea-stack pillars, Kraken's Cove and

Learning Outcomes

Lesson 1: Shout into the Void: print/log debugging

Objectives

Student can instrument any Verse script with Print statements and read the output log to trace game flow and diagnose bugs.

πŸ” Builds on: north-jungle: verse language fundamentals (first-script, functions)

🧩 Your capstone piece: The RaceDebug logger used in every later step (toggleable debug overlay of race state)

Quiz

  1. How many arguments does the message part of Print() accept?
    • A. As many comma-separated values as you like
    • B. Exactly one string
    • C. One string per line
    • D. A string and a number
  2. What does Print("Total = {3 + 4}") write to the log?
    • A. Total = {3 + 4}
    • B. Total = 34
    • C. Total = 7
    • D. Total = 3 + 4
  3. Why is GetPlayerUI called with square brackets β€” GetPlayerUI[] β€” instead of parentheses?
    • A. It returns a logic value
    • B. It is fallible and must be used in a failure context
    • C. It is a transacts function
    • D. Square brackets are optional styling
  4. Which call actually places a widget onto a player's screen?
    • A. Print(Widget)
    • B. UI.AddWidget(Canvas)
    • C. Widget.Show()
    • D. GetPlayspace().DisplayUI()
  5. How do you build a debug output string in Verse?
    • A. "Count = " + Count
    • B. String interpolation: "Count = {Count}"
    • C. Concat("Count = ", Count)
    • D. Print("Count = ", Count)
Answer key
  1. B β€” Print takes a single string; combine values using string interpolation, not extra arguments.
  2. C β€” The braces evaluate the expression before converting to text, so {3 + 4} becomes 7.
  3. B β€” GetPlayerUI[] can fail, so it must be bound inside a failure context such as an if condition.
  4. B β€” After binding UI via GetPlayerUI[], you call UI.AddWidget(Canvas) to display it.
  5. B β€” Verse has no string + concatenation for this; use interpolation with curly braces.

Recap

One step closer to Coastal Race β€” The RaceDebug logger used in every later step (toggleable debug overlay of race state)

Sources

Lesson 2: Readable Code Is Debuggable Code: naming conventions

Objectives

Student can apply Verse naming conventions (PascalCase functions, descriptive vars, _device suffixes) so bugs are visible on read.

🧩 Your capstone piece: Naming standard for the whole Coastal Race codebase (RaceConfig, CheckpointGate, OnRacerEliminated)

Quiz

  1. Which casing convention does Verse use for class names?
    • A. PascalCase (e.g., VaultDoorController)
    • B. snake_case (e.g., vault_door_controller)
    • C. SCREAMING_SNAKE_CASE (e.g., VAULT_DOOR_CONTROLLER)
    • D. camelCase (e.g., vaultDoorController)
  2. You have an @editable field that holds a trigger_device. Which name best follows Verse conventions?
    • A. pressure_plate
    • B. pressureplate
    • C. PressurePlate
    • D. PRESSURE_PLATE
  3. What is the recommended file-naming pattern for a Verse file containing a creative_device class that manages scoring?
    • A. ScoreManager.verse
    • B. score_manager_controller.verse
    • C. scoremanager.verse
    • D. Score_Manager_Controller.verse
  4. Why should numeric fields include their unit in the name (e.g., DurationSeconds instead of Duration)?
    • A. The Verse compiler requires units in field names to type-check numbers
    • B. Verse has no unit type system, so the name is the only documentation of what unit is expected
    • C. UEFN's Details panel automatically converts values based on the unit suffix
    • D. It is required by the @editable specifier
  5. You want to display a message to a player using a hud_message_device. You try passing a raw string literal but the compiler rejects it. What must you do instead?
    • A. Cast the string with StringToMessage()
    • B. Declare a function with the <localizes> specifier that returns a message, then call it
    • C. Wrap the string in double curly braces: {{"my text"}}
    • D. Use Print() instead, which accepts raw strings
Answer key
  1. B β€” Verse class names use snake_case, matching the standard library (creative_device, trigger_device, barrier_device). Fields, methods, and local variables use PascalCase.
  2. C β€” @editable fields (and all non-class identifiers) use PascalCase. The name also appears verbatim in the UEFN Details panel, so a clear PascalCase name is important for designers.
  3. B β€” File names follow snake_case with a role suffix: [system]_[role].verse. For a creative_device the role suffix is _controller, giving score_manager_controller.verse.
  4. B β€” Verse does not have a built-in unit type system. A field named Duration is ambiguous β€” seconds? milliseconds? frames? Including the unit in the name (DurationSeconds) is the only way to make the expectation clear to callers and designers.
  5. B β€” Wherever a message type is required you must provide a value produced by a <localizes> function. There is no StringToMessage() in Verse. Declare e.g. MyMsg<localizes>(S:string):message = "{S}" and pass MyMsg("your text").

Recap

One step closer to Coastal Race β€” Naming standard for the whole Coastal Race codebase (RaceConfig, CheckpointGate, OnRacerEliminated)

Sources

Lesson 3: Crow's Nest Constants: immutable bindings

Objectives

Student can declare constants and immutable data bindings and explain why config values should never be mutable.

🧩 Your capstone piece: RaceConfig constants: LapCount, StormTimeoutSeconds, MinRacers, CheckpointCount

Quiz

  1. Which syntax is valid for declaring a constant at class scope in Verse?
    • A. RaceTime : float = 90.0
    • B. RaceTime := 90.0
    • C. var RaceTime : float = 90.0
    • D. const RaceTime = 90.0
  2. A timer_device's SuccessEvent sends which type to its handler?
    • A. agent
    • B. ?agent
    • C. player
    • D. void
  3. You want to add 10 seconds to a player's remaining race time. Which pair of timer_device methods do you use?
    • A. GetActiveDuration then SetActiveDuration
    • B. Resume then Pause
    • C. Start then Complete
    • D. Save then Load
  4. Why is `GracePeriodTime : float = 10.0` better than writing `10.0` directly in a SetActiveDuration call?
    • A. It makes the code run faster at runtime.
    • B. It gives the value a meaningful name, making intent clear and changes a one-line edit.
    • C. It prevents the compiler from optimising the value away.
    • D. Constants are required by SetActiveDuration β€” literals are not accepted.
  5. You write `Count := 3` inside a function body. What is the type of Count?
    • A. float, because Verse defaults to float
    • B. int, inferred from the integer literal 3
    • C. This is a compile error β€” you must write Count : int = 3
    • D. string, because := always creates a string
Answer key
  1. A β€” At class scope you must provide an explicit type annotation: `Identifier : type = value`. The shorthand `:=` (inferred type) is only valid inside a function body. `var` declares a mutable variable, not a constant. Verse has no `const` keyword.
  2. B β€” SuccessEvent is typed `listenable(?agent)` β€” it sends an optional agent. You must unwrap it with `if (A := MaybeAgent?):` before using the agent value.
  3. A β€” GetActiveDuration(Agent) reads the current remaining seconds; SetActiveDuration(NewTime, Agent) writes the updated value. Together they let you add or subtract time from a live countdown.
  4. B β€” Constants are a readability and maintainability tool. A named constant documents what the number means and ensures that if you need to change it, you only edit one place. SetActiveDuration accepts any float expression β€” literals work too, but named constants are best practice.
  5. B β€” Inside a function, `:=` lets Verse infer the type from the initializer. The literal `3` is an integer literal, so Count is inferred as int. The explicit form `Count : int = 3` is equivalent and also valid.

Recap

One step closer to Coastal Race β€” RaceConfig constants: LapCount, StormTimeoutSeconds, MinRacers, CheckpointCount

Sources

Lesson 4: Maybe Treasure, Maybe Nothing: returning and unwrapping option types

Objectives

Student can return ?t from a function and safely unwrap it with if-then binding and the ? operator.

πŸ” Builds on: north-jungle: verse-optionals fundamentals

🧩 Your capstone piece: FindWinner()?player β€” the safe race-winner lookup (companions: verse-optionals PASS, sug-handling-optional-types PASS, sug-safe-optional-value-access PASS)

Quiz

  1. How do you write the type 'maybe an int' in Verse?
    • A. int?
    • B. ?int
    • C. option<int>
    • D. maybe int
  2. What value represents an empty option in Verse?
    • A. nil
    • B. null
    • C. false
    • D. 0
  3. Which idiom correctly unwraps an option named Maybe?
    • A. if (X := Maybe?):
    • B. X = Maybe.value
    • C. if (Maybe != nil):
    • D. X := Maybe!
  4. Why must option unwrapping happen inside an `if` or `for`?
    • A. For performance reasons
    • B. Because `?` is a failable expression needing a failure context
    • C. It doesn't; you can unwrap anywhere
    • D. Because options are async
  5. How does FindBuoyIndex signal 'no match'?
    • A. return nil
    • B. return -1
    • C. return false
    • D. throw an error
Answer key
  1. B β€” A leading `?` marks an option type, so `?int` means 'an int or nothing'.
  2. C β€” An empty option is `false`; there is no `nil` or `null` in Verse.
  3. A β€” `if (X := Maybe?):` uses the query operator in a failure context, binding X only when a value is present.
  4. B β€” The query operator `?` can fail (empty option), so it may only appear in a failure context like an `if`/`for` condition.
  5. C β€” It returns `false`, the empty option, so the caller can handle the missing case.

Recap

One step closer to Coastal Race β€” FindWinner()?player β€” the safe race-winner lookup (companions: verse-optionals PASS, sug-handling-optional-types PASS, sug-safe-optional-value-access PASS)

Sources

Lesson 5: The Skull Brig: failure contexts and the <decides> operator

Objectives

Student can write failable functions with <decides><transacts> and call them inside if-contexts so failures roll back instead of crashing.

🧩 Your capstone piece: Failable checkpoint validation: ValidateCheckpoint[Racer, Index]<decides> gates lap progress (FAILS compile 2 err β€” rewrite; also compile-gate verse-failure-contexts which has no compile key; passing failure-context-required + failure-decides back it)

Quiz

  1. Why is the `[]` bracket syntax used when calling `IsEnabled`?
    • A. It indexes an array element
    • B. It marks a fallible `<decides>` call that must appear in a failure context
    • C. It converts the return value to a logic
    • D. It calls the function asynchronously
  2. What happens when a `<decides>` expression fails inside an `if` condition?
    • A. The program crashes
    • B. An exception is thrown
    • C. Execution jumps to `else:` and speculative state is rolled back
    • D. The function returns false
  3. Why must `GetPlayerUI[Player]` be bound inside an `if` condition?
    • A. Because it is `<suspends>`
    • B. Because it is `<decides>` and can fail
    • C. Because it returns a string
    • D. Because it modifies persistent state
  4. How do you subscribe to a trigger device's fired event?
    • A. ActivationTrigger.TriggeredEvent().Subscribe(OnActivated)
    • B. ActivationTrigger.TriggeredEvent.Subscribe(OnActivated)
    • C. ActivationTrigger.Subscribe(TriggeredEvent)
    • D. Subscribe(ActivationTrigger.TriggeredEvent)
  5. What is the correct way to build an error message that includes a variable in Verse?
    • A. "Error: " + Text
    • B. Print("Error:", Text)
    • C. "Error: {Text}" using interpolation
    • D. concat("Error: ", Text)
Answer key
  1. B β€” `[]` invokes a `<decides>` (fallible) function; it may only appear inside a failure context like an `if` condition.
  2. C β€” Failure is control flow: on failure Verse skips to `else:` and automatically rolls back any speculative changes.
  3. B β€” `GetPlayerUI` is a `<decides>` function, so it must be called with `[]` inside a failure context, e.g. `if (UI := GetPlayerUI[Player]):`.
  4. B β€” Device events subscribe WITHOUT parentheses on the event field: `ActivationTrigger.TriggeredEvent.Subscribe(OnActivated)`.
  5. C β€” Verse has no string `+` concatenation; use interpolation like `"Error: {Text}"`.

Recap

One step closer to Coastal Race β€” Failable checkpoint validation: ValidateCheckpoint[Racer, Index]<decides> gates lap progress (FAILS compile 2 err β€” rewrite; also compile-gate verse-failure-contexts which has no compile key; passing failure-context-required + failure-decides back it)

Sources

Lesson 6: Sunken Deck Salvage: array indexing is failable

Objectives

Student can index arrays inside failure contexts and never write an out-of-bounds crash again.

πŸ” Builds on: north-jungle: arrays fundamentals (sum-an-array)

🧩 Your capstone piece: Safe Checkpoints[Index] lookups as racers advance through the gate array

Quiz

  1. What happens in Verse when you write `MyArray[5]` and the array only has 3 elements?
    • A. The expression fails, and if it is inside an `if`, the else branch runs
    • B. Verse throws a runtime exception and halts the device
    • C. Verse returns a default zero value for the element type
    • D. The expression succeeds but returns an invalid reference
  2. Which of the following is the correct way to safely read the first element of `MyArray` in Verse?
    • A. First := MyArray[0]
    • B. if (First := MyArray[0]): # use First
    • C. First := MyArray.Get(0)
    • D. try { First := MyArray[0] } catch {}
  3. You want to iterate over every element in `Triggers : []trigger_device` and call `Disable()` on each. Which approach is safest and most idiomatic?
    • A. for (I : 0..Triggers.Length - 1): Triggers[I].Disable()
    • B. for (Trigger : Triggers): Trigger.Disable()
    • C. var I : int = 0; loop: if (T := Triggers[I]): T.Disable(); set I += 1
    • D. Triggers[0].Disable(); Triggers[1].Disable() # hardcoded
  4. You have an `@editable` array of `item_granter_device` but your device does nothing at runtime. What is the most likely cause?
    • A. Arrays cannot be marked @editable in Verse
    • B. You forgot to add elements to the array in the UEFN editor, so it is empty
    • C. item_granter_device is not a valid array element type
    • D. You must call MyArray.Initialize() before using it
  5. Inside a single `if` condition, you chain two failable array accesses: `if (A := Arr[I], B := Arr[I+1]):`. What happens if only the second access fails?
    • A. The body runs with A valid and B set to a default value
    • B. Only the line using B is skipped; A is still usable in the body
    • C. The entire if body is skipped because the whole condition fails
    • D. A compile error is raised because chaining failable expressions is not allowed
Answer key
  1. A β€” Array indexing is a failable expression in Verse. An out-of-bounds index causes the expression to fail, which is handled by the surrounding failure context (if/else, for, etc.) β€” no crash, no exception.
  2. B β€” Square-bracket indexing is failable and must appear inside a failure context such as an `if` expression. A bare assignment outside a failure context is a compile error. Verse has no `try/catch` and no `.Get()` method on arrays.
  3. B β€” `for (Trigger : Triggers)` iterates elements directly and is always bounds-safe β€” it simply produces no iterations on an empty array. The range-based approach in option A can fail if the array is empty (Length - 1 = -1).
  4. B β€” @editable arrays default to empty (`array{}`). If you do not wire up elements in the UEFN editor, every index access fails silently. Always check `Length > 0` in OnBegin during development.
  5. C β€” In Verse, a failure anywhere in a chained `if` condition causes the entire condition to fail, and the whole body is skipped. Partial success within a single condition chain is not possible.

Recap

One step closer to Coastal Race β€” Safe Checkpoints[Index] lookups as racers advance through the gate array

Sources

Lesson 7: Racer Roster: adding and removing from arrays

Objectives

Student can grow, shrink, and rebuild arrays (concat, slice-around-index) to maintain a live roster.

🧩 Your capstone piece: The live Racers array: add on join, remove on elimination/leave

Quiz

  1. You call `MyArray.RemoveAllElements(0)` but the array still contains zeros afterward. What went wrong?
    • A. You forgot to assign the result back: `set MyArray = MyArray.RemoveAllElements(0)`
    • B. RemoveAllElements only removes the first occurrence, not all of them
    • C. RemoveAllElements requires a failure context like an `if` expression
    • D. You must call `MyArray.Clear()` first
  2. Which syntax correctly appends a single `player` value `P` to a `var Roster : []player`?
    • A. set Roster += P
    • B. set Roster += array{P}
    • C. Roster.Add(P)
    • D. set Roster = Roster.Append(P)
  3. Why must `Remove[From, To]` be called inside an `if` expression?
    • A. Because it returns a tuple, not an array
    • B. Because it is a suspending function that requires an async context
    • C. Because it is failable β€” it fails when the index range is out of bounds
    • D. Because it mutates the array in place and needs a transaction
  4. You declare `var Bag := array{}` and get a compile error. What is the fix?
    • A. Use curly braces: `var Bag := {}`
    • B. Add a type annotation: `var Bag : []int = array{}`
    • C. Initialize with at least two elements
    • D. Use `var Bag := array{0}.Remove[0,0]`
  5. A `trigger_device` TriggeredEvent hands your handler `(Agent : ?agent)`. Before you can call `player[Agent]`, what must you do?
    • A. Cast Agent with `(player)Agent`
    • B. Call `Agent.GetPlayer()`
    • C. Unwrap the option with `if (A := Agent?)` and then use A
    • D. Nothing β€” ?agent and agent are interchangeable
Answer key
  1. A β€” Verse arrays are value types. RemoveAllElements returns a NEW array with the elements removed β€” it does not mutate the original. You must write `set MyArray = MyArray.RemoveAllElements(0)` to keep the change.
  2. B β€” The `+=` operator on arrays is concatenation of two arrays. You must wrap the single item in `array{P}` to make it a one-element array before concatenating.
  3. C β€” `Remove` uses square-bracket call syntax (`[]`) which signals a failable expression in Verse. If the indices are out of range it fails, so it must live inside a failure context such as `if (Result := MyArray.Remove[0, 0])`.
  4. B β€” When you create an empty array Verse cannot infer the element type from the literal alone. You must provide an explicit type annotation such as `var Bag : []int = array{}` so the compiler knows what kind of array it is.
  5. C β€” The event delivers an *optional* agent (`?agent`). You must unwrap it with `if (A := Agent?):` to get a concrete `agent` value before you can attempt the `player[A]` downcast.

Recap

One step closer to Coastal Race β€” The live Racers array: add on join, remove on elimination/leave

Sources

Lesson 8: Sea-Stack Pillars: your shared helper module

Objectives

Student can extract reusable functions into a module and import it from any file β€” the SOLID layering habit every later zone uses.

πŸ” Builds on: north-jungle: verse-modules + creating-custom-modules

🧩 Your capstone piece: RaceHelpers module (FormatTime, SafeIndex, Logging) imported by every subsequent lesson (content/verse/shared-helper-module.json compile-PASSED; backed by using-a-module + function-as-helper, both PASS)

Quiz

  1. A device file calls FormatTime(90) but fails to compile: "Unknown identifier FormatTime". The RaceHelpers module (with FormatTime<public>) lives in another file in the same project. What fixes the build?
    • A. Add `using { RaceHelpers }` at the top of THIS file β€” every file that calls a module's functions needs its own import line.
    • B. Add `using { /Fortnite.com/Devices }` β€” device imports also bring in project modules.
    • C. Copy FormatTime into the device class β€” module functions can only be called inside their own file.
    • D. Rename the call to RaceHelpers.FormatTime(90) β€” qualified calls never need a using directive.
  2. Why must helper functions inside a module carry the <public> specifier?
    • A. Without <public>, the function is internal to the module β€” files that import the module cannot see or call it.
    • B. <public> makes the function show up as an @editable field in the Details panel.
    • C. <public> is required for any function that takes parameters.
    • D. It marks the function as thread-safe for spawn{} calls.
  3. Inside FormatTime, why are Quotient and Mod called with square brackets inside an `if:` block?
    • A. Both are <decides> (failable) functions β€” square brackets mark the failable call, and it must happen in a failure context like if.
    • B. Square brackets are how Verse passes two arguments to math functions.
    • C. The if: block is only needed because the function returns a string.
    • D. Quotient and Mod are asynchronous, so they need a suspending context.
  4. You want the module to own the checkpoint pressure plate directly, so you add `@editable Plate : trigger_device = trigger_device{}` inside the module block. What happens?
    • A. Compile error β€” modules are not devices and cannot hold @editable fields; pass placed devices to module functions as parameters instead.
    • B. It compiles, and the plate appears in every importing device's Details panel.
    • C. It compiles, but only the first device to import the module can wire it.
    • D. It works only if the module is also marked <public>.
  5. The demo prints `RaceLog("A 754-second run reads as {FormatTime(754)}")`. What does FormatTime(754) return?
    • A. "754:00" β€” the function just appends ":00" to the input.
    • B. "12:34" β€” Quotient[754, 60] gives 12 minutes and Mod[754, 60] gives 34 seconds.
    • C. "12:3" β€” Mod returns 3 because it truncates.
    • D. "0:00" β€” 754 is out of range for the race clock.
Answer key
  1. A β€” A `using { RaceHelpers }` directive only applies to the file it is written in. The module being <public> and present in the project is not enough β€” each consumer file must import it. (Fully qualified paths can also work, but the module still has to be resolvable from that file's scope; the standard fix is the using line.)
  2. A β€” Access control in Verse modules defaults to internal. Only members marked <public> are visible to code outside the module, so shared helpers must be <public> to be usable by importers.
  3. A β€” Quotient<native><public>(X:int, Y:int)<computes><decides>:int and Mod share the same failable signature β€” division can fail (e.g. divide by zero). Failable invocations use [] and must run in a failure context; the if: block provides it, and the "0:00" default covers the failure branch.
  4. A β€” @editable belongs to creative_device classes, which have a Details panel in UEFN. A module has no placed instance to edit, so the field is rejected. The shared-module pattern is: devices own the @editable references and hand them to module helpers as parameters.
  5. B β€” Quotient[754, 60] is the integer quotient 12, and Mod[754, 60] is the remainder 34. Seconds (34) is not below 10, so the else branch formats "{Minutes}:{Seconds}" = "12:34" β€” exactly the example in the code comment.

Recap

One step closer to Coastal Race β€” RaceHelpers module (FormatTime, SafeIndex, Logging) imported by every subsequent lesson (content/verse/shared-helper-module.json compile-PASSED; backed by using-a-module + function-as-helper, both PASS)

Sources

Lesson 9: Async 101: Sleep, loop, and the countdown

Objectives

Student can write <suspends> functions using Sleep() in loops to build countdowns and repeating timers without blocking the game.

πŸ” Builds on: south-shores: hud_message_device for the countdown display

🧩 Your capstone piece: The 3-2-1-GO race countdown broadcast to all racers (companions loop-with-sleep, sleep-timer, sug-implementing-asynchronous-timers-with-sleep all PASS)

Quiz

  1. Which `timer_device` event fires when the countdown reaches zero without being manually completed?
    • A. SuccessEvent
    • B. FailureEvent
    • C. StartUrgencyModeEvent
    • D. ResetEvent
  2. Your handler is subscribed to `SuccessEvent`. What is the correct function signature?
    • A. OnSuccess(Agent : agent) : void
    • B. OnSuccess(Agent : ?agent) : void
    • C. OnSuccess() : void
    • D. OnSuccess(Agent : player) : void
  3. You want to add 20 seconds to a player's running countdown. Which method do you call?
    • A. RaceTimer.AddTime(20.0, Agent)
    • B. RaceTimer.SetActiveDuration(RaceTimer.GetActiveDuration(Agent) + 20.0, Agent)
    • C. RaceTimer.Resume(Agent)
    • D. RaceTimer.ResetForAll(Agent)
  4. You call `RaceTimer.Disable()` and then `RaceTimer.Start()`. What happens?
    • A. The timer starts normally
    • B. The timer starts but immediately pauses
    • C. The Start call is silently ignored because the device is disabled
    • D. A compile error is thrown
  5. After calling `RaceTimer.ResetForAll()`, what is the timer's state?
    • A. Running from the base duration
    • B. Stopped at the base duration, waiting for a new Start call
    • C. Paused at whatever time was remaining
    • D. Completed and firing SuccessEvent
Answer key
  1. B β€” `FailureEvent` is signaled when the timer runs out naturally. `SuccessEvent` fires when `Complete()` or `CompleteForAll()` is called explicitly. There is no `ResetEvent`.
  2. B β€” `SuccessEvent` is typed `listenable(?agent)`, so the handler must accept `?agent` (optional agent). You then unwrap it with `if (A := Agent?):` before using agent-specific APIs.
  3. B β€” `SetActiveDuration` sets the remaining time to a specific value. To add time, read the current value with `GetActiveDuration`, add your bonus, and pass the result back to `SetActiveDuration`.
  4. C β€” While a device is disabled it does not receive signals, including method calls like `Start`. You must call `Enable()` before `Start()` for the timer to begin.
  5. B β€” `ResetForAll` rewinds the timer to its configured base duration AND stops it. It does NOT restart automatically β€” you must call `Start()` or `StartForAll()` again to begin a new countdown.

Recap

One step closer to Coastal Race β€” The 3-2-1-GO race countdown broadcast to all racers (companions loop-with-sleep, sleep-timer, sug-implementing-asynchronous-timers-with-sleep all PASS)

Sources

Lesson 10: Marooned Town: fire-and-forget with spawn

Objectives

Student can launch independent async tasks with spawn{} and knows when spawn is right vs. awaiting inline.

🧩 Your capstone piece: One spawned per-racer monitor task (position/checkpoint watcher) per player at race start

Quiz

  1. Why must `UpdatePlayerUIAsync` be marked `<suspends>`?
    • A. Because it is editable
    • B. Because it calls `Sleep`, which yields execution
    • C. Because it returns a logic
    • D. Because all event handlers must be `<suspends>`
  2. How is `GetRandomFloat` correctly called given its signature `GetRandomFloat(Low:float, High:float)<transacts>:float`?
    • A. With brackets: `GetRandomFloat[1.0, 3.0]` because it's fallible
    • B. With parens, storing the returned float: `Delay := GetRandomFloat(1.0, 3.0)`
    • C. Inside an `if` condition only
    • D. By spawning it
  3. What does the `spawn` expression do?
    • A. Freezes the game until the task completes
    • B. Starts an async task that lives independently of its creating scope
    • C. Creates a new player
    • D. Forces a function to be `<decides>`
  4. Why does the `OnTriggered` handler use `spawn` instead of calling the async function directly?
    • A. To make the code shorter
    • B. So the handler returns immediately and doesn't block while the task sleeps
    • C. Because trigger handlers cannot call functions
    • D. To roll back the trigger
  5. Which call actually performs the visible UI update in this device?
    • A. `Print(...)`
    • B. `Sleep(Delay)`
    • C. `SuccessMessage.Show(TriggeringAgent)`
    • D. `spawn{...}`
Answer key
  1. B β€” Any function that awaits or calls a `<suspends>` API like `Sleep` must itself be `<suspends>`.
  2. B β€” It is `<transacts>`, not `<decides>`, and returns a plain `float`, so call it with `()` and capture the value.
  3. B β€” `spawn` is Verse's unstructured concurrency escape hatch β€” it launches an async expression that runs independently.
  4. B β€” `OnTriggered` is not `<suspends>`; spawning the slow work lets the handler return instantly while the task runs in the background.
  5. C β€” `hud_message_device.Show(Agent)` displays the configured HUD message to that player β€” the concrete UI update.

Recap

One step closer to Coastal Race β€” One spawned per-racer monitor task (position/checkpoint watcher) per player at race start

Sources

Lesson 11: All Boats Together: sync waits for everyone

Objectives

Student can run tasks in parallel with sync{} and use all results once every branch completes.

🧩 Your capstone piece: The synced start gate: all racers' ready-checks complete before GO (companion sync-await-all + sync, both PASS)

Quiz

  1. What does the `sync` expression return when all its tasks complete?
    • A. Nothing (void)
    • B. A tuple of each subexpression's result, in order
    • C. Only the first task's result
    • D. A logic value indicating success
  2. If three tasks under `sync` sleep for 3.0, 2.0, and 1.0 seconds, roughly how long does the block take?
    • A. 6.0 seconds
    • B. 1.0 second
    • C. 3.0 seconds
    • D. 2.0 seconds
  3. Why must `sync` be used inside a `<suspends>` context like `OnBegin`?
    • A. Because it modifies persistent state
    • B. Because it awaits async work that may span simulation updates
    • C. Because it fails as control flow
    • D. Because it returns a logic value
  4. How do you access the second result from `Results := sync: ...`?
    • A. Results[1]
    • B. Results.1
    • C. Results(1)
    • D. Results.Get(1)
  5. What is the defining behavior of `sync` regarding completion?
    • A. It continues as soon as the first task finishes
    • B. It waits for ALL subexpressions to complete before continuing
    • C. It cancels slower tasks once one finishes
    • D. It runs tasks one at a time
Answer key
  1. B β€” `sync` returns a tuple containing each subexpression's result in source order, accessible via Results(0), Results(1), etc.
  2. C β€” Tasks run in parallel, so total time equals the slowest one (3.0s), not the sum (6.0s).
  3. B β€” `sync` runs async expressions that can take time across simulation updates, so it requires an async (<suspends>) context.
  4. C β€” Tuple elements are accessed with parentheses and a zero-based index, so Results(1) is the second element.
  5. B β€” `sync` is a barrier: it only proceeds after every single subexpression has completed.

Recap

One step closer to Coastal Race β€” The synced start gate: all racers' ready-checks complete before GO (companion sync-await-all + sync, both PASS)

Sources

Lesson 12: Kraken's Cove: race β€” first task wins, losers cancelled

Objectives

Student can use race{} to run competing tasks where the first to finish cancels the rest β€” the timeout pattern.

🧩 Your capstone piece: Storm-vs-finish-line: race{AwaitAllFinished(), StormTimer()} ends the race either way (FAILS compile 1 err β€” rewrite first; central to zone theme)

Quiz

  1. What does a `race` expression do when its fastest branch completes?
    • A. Runs the remaining branches to completion
    • B. Returns the winner's value and cancels the other branches
    • C. Restarts all branches
    • D. Returns an array of all branch results
  2. Why must `GetPlayerUI[Player]` be called inside an `if (...)` condition?
    • A. It is async and needs <suspends>
    • B. It is fallible (uses [] brackets) and may only run in a failure context
    • C. It modifies persistent state
    • D. It returns a logic that must be toggled
  3. Why is each race branch wrapped in a `block:` ending in a string?
    • A. To make the branch async
    • B. So the block's final expression becomes the branch's value used as the race result
    • C. Because race requires exactly two branches
    • D. To cancel the branch early
  4. What type must `text_block.SetText` receive?
    • A. string
    • B. message
    • C. text_block
    • D. logic
  5. What does the `<localizes>` specifier on `StringToMessage` enable it to return?
    • A. A logic value
    • B. A message from a string, suitable for UI text
    • C. A canvas widget
    • D. An awaitable event
Answer key
  1. B β€” `race` takes the first branch to finish, uses its result, and cancels every losing branch automatically.
  2. B β€” The `[]` brackets mark it fallible, so it can only appear in a failure context such as an `if` condition.
  3. B β€” A block evaluates to its last expression, so ending each branch in a string label lets `race` return which trigger won.
  4. B β€” `SetText` takes a `message`, which is why we use a `<localizes>` helper to convert our string.
  5. B β€” `<localizes>` functions produce `message` values, letting us turn a plain string into text the UI accepts.

Recap

One step closer to Coastal Race β€” Storm-vs-finish-line: race{AwaitAllFinished(), StormTimer()} ends the race either way (FAILS compile 1 err β€” rewrite first; central to zone theme)

Sources

Lesson 13: First Across the Line: rush keeps the losers running

Objectives

Student can choose between race and rush β€” rush returns the first result but lets remaining tasks continue.

🧩 Your capstone piece: Winner detection: rush over per-racer finish awaits crowns first place while others keep racing for podium spots

Quiz

  1. What distinguishes `rush` from `race` in Verse?
    • A. rush cancels slower subexpressions; race does not
    • B. rush continues when the fastest finishes but does NOT cancel slower tasks
    • C. rush runs expressions sequentially
    • D. rush can only contain one subexpression
  2. How many async subexpressions does a `rush` block require?
    • A. Exactly one
    • B. Two or more
    • C. Exactly three
    • D. Any number including zero
  3. Why must `rush` appear inside a `<suspends>` context such as `OnBegin`?
    • A. Because it allocates memory
    • B. Because its subexpressions are async and need a suspendable context
    • C. Because it returns a logic value
    • D. Because creative_device requires it
  4. What is the correct way to obtain a player's HUD in the grounding?
    • A. GetPlayerUI(Player)
    • B. Player.GetPlayerUI()
    • C. GetPlayerUI[Player] inside an if
    • D. Player.GetCanvas()
  5. Which method adds a widget to a player's HUD?
    • A. PlayerUI.AddWidget(Widget)
    • B. PlayerUI.GetCanvas()
    • C. PlayerUI.Show(Widget)
    • D. Widget.Attach(PlayerUI)
Answer key
  1. B β€” `rush` proceeds when the fastest subexpression completes but lets the remaining tasks keep running. `race` cancels the losers.
  2. B β€” Like sync and race, rush runs a block of two or more async expressions concurrently.
  3. B β€” rush schedules async work, so it can only run where suspension is allowed β€” a `<suspends>` function.
  4. C β€” `GetPlayerUI` is `<transacts><decides>`, so it's called with brackets in a failure context and the result is bound.
  5. A β€” `AddWidget<native><public>(Widget:widget):void` adds the widget using default slot configuration.

Recap

One step closer to Coastal Race β€” Winner detection: rush over per-racer finish awaits crowns first place while others keep racing for podium spots

Sources

Lesson 14: The Timing Toolbox: spawn/sync/race/loop together

Objectives

Student can combine all four concurrency primitives into one game-timing structure and pick the right tool per job.

🧩 Your capstone piece: The full race-loop skeleton: loop rounds, sync starts, race timeouts, spawn monitors (FAILS compile 5 err β€” the zone's worst-broken load-bearing article; intermediate sibling verse-concurrency-async-race-state-machines PASSES as backup)

Quiz

  1. Which keyword starts an async task in the background so the current function can return immediately?
    • A. race
    • B. sync
    • C. spawn
    • D. loop
  2. What does a `race` expression do when one of its branches finishes?
    • A. Waits for all branches to finish
    • B. Cancels the remaining branches
    • C. Restarts the slower branches
    • D. Runs them sequentially instead
  3. Why must `Sleep`, `race`, and `spawn`-launched awaits live inside `OnBegin`?
    • A. Because OnBegin is the only public function
    • B. Because they need a <suspends> (async) context
    • C. Because OnBegin runs faster
    • D. Because @editable requires it
  4. How do you correctly reassign a `var` in Verse?
    • A. Remaining -= 1
    • B. Remaining--
    • C. set Remaining = Remaining - 1
    • D. Remaining = Remaining - 1
  5. Why is `GetPlayerUI[Agent]` called inside an `if` rather than as a plain statement?
    • A. It always returns void
    • B. It is fallible (uses []) and needs a failure context
    • C. It is a device event
    • D. It requires a loop
Answer key
  1. C β€” `spawn` launches an asynchronous expression and lets the calling code continue right away.
  2. B β€” `race` keeps the result of the first branch to finish and cancels the others.
  3. B β€” These are async operations that require a `<suspends>` context, which `OnBegin<override>()<suspends>` provides.
  4. C β€” Verse uses `set X = ...`. There is no `--` or `-=` operator, and bare `X = value` on a var is an error.
  5. B β€” The `[]` brackets mean it's a `<decides>`/fallible call, which may only appear in a failure context like an `if` condition.

Recap

One step closer to Coastal Race β€” The full race-loop skeleton: loop rounds, sync starts, race timeouts, spawn monitors (FAILS compile 5 err β€” the zone's worst-broken load-bearing article; intermediate sibling verse-concurrency-async-race-state-machines PASSES as backup)

Sources

Lesson 15: Mermaid Lagoon: await events, never poll

Objectives

Student can subscribe to and await device/custom events instead of loop-polling state, and can define a custom event(t) signal.

πŸ” Builds on: north-jungle: verse-events-custom (event bus)

🧩 Your capstone piece: RaceStartedEvent / RacerFinishedEvent custom signals gluing the phases together (content/verse compile-PASSED; companions subscribe-to-event, await-an-event, custom-event-signal all PASS)

Quiz

  1. You have this poll loop watching a var: loop: if (DoorOpened?): break Sleep(0.1) What is the correct event-driven replacement?
    • A. Declare DoorOpenedEvent : event() = event(){}, call DoorOpenedEvent.Signal() where the var used to be set, and replace the whole loop with DoorOpenedEvent.Await()
    • B. Shrink the Sleep to 0.01 so the loop reacts faster
    • C. Replace Sleep(0.1) with Sleep(0.0) so the loop checks every frame
    • D. Wrap the loop in spawn{} so it polls on a background task instead
  2. RacerFinishedEvent is declared as event(agent). What does RacerFinishedEvent.Await() evaluate to?
    • A. The agent payload passed to the matching Signal call
    • B. A logic value indicating whether the event has ever fired
    • C. Nothing - Await() on an event always returns void
    • D. A subscription handle you must cancel later
  3. Why does the finish-line handler unwrap before signalling? OnFinishLineCrossed(MaybeRacer : ?agent) : void = if (Racer := MaybeRacer?): RacerFinishedEvent.Signal(Racer)
    • A. trigger_device.TriggeredEvent sends ?agent because a non-agent cause can trip the trigger, but RacerFinishedEvent carries a plain agent - so the optional must be proven before Signal
    • B. Signal() can only be called inside an if expression
    • C. Unwrapping makes the event fire faster
    • D. ?agent and agent are interchangeable; the unwrap is purely stylistic
  4. In the signal hub, OnBegin does RacerFinishedEvent.Await() while OnRacerFinished is Subscribed to the same event. What is the behavioural difference?
    • A. Await resumes its task once, on the NEXT Signal only; Subscribe's handler runs on EVERY Signal from then on
    • B. They are identical - Subscribe is just the non-suspending spelling of Await
    • C. Subscribe only works on device events, so the custom-event Subscribe is ignored
    • D. Await blocks the whole island until the event fires; Subscribe does not
  5. The signal hub declares RaceStartedEvent : event() = event(){}. Which using-import does the event(t) class require?
    • A. using { /Fortnite.com/Devices }
    • B. using { /Verse.org/Verse }
    • C. using { /UnrealEngine.com/Temporary/Diagnostics }
    • D. using { /Verse.org/Simulation }
Answer key
  1. A β€” Polling is replaced by signalling: the code that used to `set DoorOpened = true` now calls `Signal()`, and the waiting task replaces its entire loop with a single `Await()`. Shrinking the Sleep or moving the loop to another task still polls β€” it just changes where the waste happens.
  2. A β€” `event(t).Await()` suspends until the next `Signal(Payload)` and returns that payload β€” so `Winner := RacerFinishedEvent.Await()` binds the finishing agent directly.
  3. A β€” `TriggeredEvent` is a `listenable(?agent)` β€” the payload is optional. Our custom event demands a real `agent`, so the handler unwraps with `if (Racer := MaybeRacer?)` and only signals when a racer actually crossed.
  4. A β€” An awaiter is a one-shot: the suspended task resumes on the next `Signal` and moves on (here, catching the FIRST finisher). A subscriber is persistent: its handler runs for every occurrence (here, congratulating EVERY finisher). Both patterns coexist on one event.
  5. B β€” The article calls this out in Step 1: `event()` needs `using { /Verse.org/Verse }`. Devices come from /Fortnite.com/Devices, Print from Diagnostics, and Sleep from Simulation β€” none of those bring in `event(t)`.

Recap

One step closer to Coastal Race β€” RaceStartedEvent / RacerFinishedEvent custom signals gluing the phases together (content/verse compile-PASSED; companions subscribe-to-event, await-an-event, custom-event-signal all PASS)

Sources

Lesson 16: Mangrove Channel Gates: trigger_device as checkpoint sensor

Objectives

Student can wire trigger_device TriggeredEvent into Verse with agent payloads to detect exactly who passed a gate.

πŸ” Builds on: center-village: trigger wiring + button-event bridge patterns

🧩 Your capstone piece: Physical checkpoint sensors along the race channel feeding CheckpointPassed(Racer, Index)

Quiz

  1. What type does the trigger_device's TriggeredEvent hand to your subscribed handler?
    • A. agent
    • B. ?agent (an optional agent)
    • C. player
    • D. fort_character
  2. Why must you declare the trigger as an @editable field inside your creative_device class?
    • A. To make it run faster
    • B. So you can bind it to the placed device and call its methods; a bare Device.Method() fails
    • C. Because TriggeredEvent only works on editable fields
    • D. It is optional and only for readability
  3. What value passed to SetMaxTriggerCount means 'no limit'?
    • A. 20
    • B. -1
    • C. 0
    • D. 100
  4. What does GetTriggerCountRemaining() return when the device's max trigger count is unlimited?
    • A. The number of players in the game
    • B. 20
    • C. 0
    • D. An error
  5. Which call fires the device's TriggeredEvent from code while attaching a specific player?
    • A. Trigger()
    • B. Trigger(Agent)
    • C. Enable()
    • D. SetTransmitDelay(Agent)
Answer key
  1. B β€” TriggeredEvent is listenable(?agent) because the device can also be fired from code with no agent, so you must unwrap the option before using it.
  2. B β€” Calling a method on a placed device requires an @editable field you point at the device in the Details panel; otherwise you get 'Unknown identifier'.
  3. C β€” 0 indicates unlimited triggers. The argument is clamped between 0 and 20.
  4. C β€” Per the API, it returns 0 when GetMaxTriggerCount is unlimited, so don't treat 0 as 'empty' in that configuration.
  5. B β€” Trigger(Agent:agent) fires the event and passes that agent along; the agentless Trigger() fires with no agent.

Recap

One step closer to Coastal Race β€” Physical checkpoint sensors along the race channel feeding CheckpointPassed(Racer, Index)

Sources

Lesson 17: Firefly Cove at Dusk: PlayerAddedEvent and the join lifecycle

Objectives

Student can handle players joining mid-session (PlayerAddedEvent + GetPlayspace().GetPlayers()) and initialize per-player state.

πŸ” Builds on: south-shores: player spawn pad basics

🧩 Your capstone piece: The race lobby: joiners registered into the Racers roster + assigned a boat dock (content/verse compile-PASSED β€” pin URL was wrongly flagged broken; sibling detect-player-leave also PASSES for the leave path)

Quiz

  1. Why must OnBegin ALSO iterate GetPlayspace().GetPlayers() instead of relying on PlayerAddedEvent alone?
    • A. PlayerAddedEvent only fires for players who join AFTER the subscription β€” players already in the playspace when OnBegin runs never trigger it.
    • B. GetPlayers() is required to activate PlayerAddedEvent; the event stays dormant until the list is read once.
    • C. PlayerAddedEvent fires for AI participants too, so the iteration filters those out.
    • D. Iterating GetPlayers() is only needed in multiplayer playtests, never in published islands.
  2. Which is the correct way to subscribe to the playspace join event?
    • A. GetPlayspace().PlayerAddedEvent().Subscribe(OnPlayerJoined)
    • B. GetPlayspace().PlayerAddedEvent.Subscribe(OnPlayerJoined)
    • C. GetPlayspace().Subscribe(PlayerAddedEvent, OnPlayerJoined)
    • D. PlayerAddedEvent(GetPlayspace()).Subscribe(OnPlayerJoined)
  3. In OnBegin, why does the registrar subscribe to PlayerAddedEvent BEFORE sweeping GetPlayers()?
    • A. So a player joining between the two steps is caught by the event β€” the worst case becomes a duplicate registration, which the idempotent RegisterRacer skips.
    • B. Because Verse requires all Subscribe calls to appear before any for loop in OnBegin.
    • C. Because GetPlayers() returns an empty array until at least one subscription exists.
    • D. The order does not matter; both orders behave identically.
  4. The lobby spawn pad's SpawnedEvent delivers an agent, but the Racers roster stores player values. What is the correct narrowing step?
    • A. if (SpawnedPlayer := player[SpawnedAgent]): β€” a failable cast that succeeds only when the agent is a human player.
    • B. SpawnedPlayer := SpawnedAgent as player β€” an unchecked cast.
    • C. SpawnedPlayer := player(SpawnedAgent) β€” a constructor call.
    • D. No narrowing is needed; agent and player are interchangeable types.
  5. A player joins Firefly Cove five minutes after the match started. Which line does the registrar print first?
    • A. "Racer already registered - skipping."
    • B. "A new racer drifts into Firefly Cove."
    • C. "Welcome to the Coastal Race! Your boat awaits."
    • D. Nothing β€” mid-session joiners are only caught by the OnBegin sweep.
Answer key
  1. A β€” The event is not retroactive. Anyone standing on the island before your OnBegin subscribed will never fire PlayerAddedEvent, so per-player initialization must also sweep the current GetPlayers() list. The two halves together cover the full join lifecycle.
  2. A β€” PlayerAddedEvent is a function that RETURNS a listenable(player) β€” signature PlayerAddedEvent<public>():listenable(player) β€” so it needs the () call before .Subscribe. Device events like SpawnedEvent are plain fields and take .Subscribe directly.
  3. A β€” Sweep-then-subscribe leaves a gap where a joiner is missed by BOTH paths. Subscribe-then-sweep means an unlucky joiner might be delivered twice instead β€” and a duplicate is harmless because RegisterRacer checks DockAssignments before registering.
  4. A β€” player[Agent] is Verse's failable type cast: inside a failure context it succeeds for human players and fails for other agents (like AI). That keeps NPCs out of a player-typed roster and map.
  5. B β€” A mid-session joiner arrives via the PlayerAddedEvent path: OnPlayerJoined prints "A new racer drifts into Firefly Cove." and then calls RegisterRacer. The welcome banner is a HUD message device, not a Print, and the OnBegin sweep only covers players present before the device started.

Recap

One step closer to Coastal Race β€” The race lobby: joiners registered into the Racers roster + assigned a boat dock (content/verse compile-PASSED β€” pin URL was wrongly flagged broken; sibling detect-player-leave also PASSES for the leave path)

Sources

Lesson 18: Hidden Cove Ambush: elimination_result and EliminatedEvent

Objectives

Student can subscribe to fort_character EliminatedEvent, read elimination_result (who/by-whom), and drive game logic from eliminations.

🧩 Your capstone piece: DNF handling: eliminated racers leave the roster, feed the kill-ticker, become spectators (content/verse compile-PASSED; support tracking-player-health-shields + elimination-feed-device PASS; elimination-manager-device FAILS 1 err β€” fix as supporting ref)

Quiz

  1. You have an `elimination_result` named `Result` and want to credit the eliminator's score ONLY when a real player dealt the blow. Which code is correct?
    • A. if (Killer := Result.EliminatingCharacter?, KillerAgent := Killer.GetAgent[]) { ScoreDevice.Activate(KillerAgent) }
    • B. ScoreDevice.Activate(Result.EliminatingCharacter.GetAgent())
    • C. if (Result.EliminatingCharacter = true) { ScoreDevice.Activate(Result.EliminatingCharacter) }
    • D. ScoreDevice.Activate(Result.EliminatedCharacter.GetAgent[])
  2. What payload does `fort_character.EliminatedEvent()` deliver to its subscribers?
    • A. An `elimination_result` struct carrying `EliminatedCharacter : fort_character` and `EliminatingCharacter : ?fort_character`
    • B. The `agent` that was eliminated
    • C. A `tuple(fort_character, fort_character)` of victim and killer
    • D. Nothing - it is a plain signal with no payload
  3. Why is `EliminatingCharacter` typed as `?fort_character` (an option) instead of plain `fort_character`?
    • A. Because not every elimination has an author - storm damage, fall damage, and other environmental deaths have no eliminating character
    • B. Because the eliminator might have left the game before the event fires
    • C. Because options are faster to copy than interface references
    • D. Because Verse requires all struct fields to be optional
  4. Your race mode respawns eliminated players for a second lap. Your elimination watcher stops firing for respawned players. Why?
    • A. A respawned player gets a NEW fort_character instance - the old subscription is attached to the eliminated one, so you must call GetFortCharacter[] and subscribe again
    • B. EliminatedEvent can only ever fire once per game
    • C. The playspace cancels all subscriptions when any player is eliminated
    • D. Respawned characters are invulnerable and cannot be eliminated
  5. This line fails to compile: `Character.EliminatedEvent.Subscribe(OnEliminated)`. Why?
    • A. Subscribe requires a suspending function as its argument
    • B. `EliminatedEvent` is a function, not a property - you must CALL it with `()` to get the listenable: `Character.EliminatedEvent().Subscribe(OnEliminated)`
    • C. You can only Subscribe to device events, never character events
    • D. EliminatedEvent must be awaited before it can be subscribed
Answer key
  1. A β€” `EliminatingCharacter` is `?fort_character` - an option. The `?` postfix unwrap inside a failure context (`if`) succeeds only when an eliminator exists, and `GetAgent[]` is failable too, so both belong in the same `if`. Environmental eliminations (storm, fall damage) leave the option empty and the whole condition fails - no score awarded. Option 3 activates for the VICTIM'S agent, which is the classic scoring bug.
  2. A β€” The digest signature is `EliminatedEvent<public>():listenable(elimination_result)`. The `elimination_result` struct (from `/Fortnite.com/Game`) always contains the eliminated `fort_character`, plus an OPTIONAL eliminating character that is false for environmental eliminations.
  3. A β€” The digest comment says it directly: `EliminatingCharacter` will be false when the character was eliminated through non-character actions, such as environmental damage. The option type forces you to handle the no-eliminator case at compile time.
  4. A β€” A `fort_character` is one incarnation of a player. Elimination ends that incarnation; respawning creates a fresh `fort_character`. Any per-character subscription must be re-established on the new character - a common pattern is a loop that re-fetches `GetFortCharacter[]` after each elimination, or subscribing from a `SpawnedEvent` handler.
  5. B β€” The digest signature is `EliminatedEvent<public>():listenable(elimination_result)` - note the `()`. Unlike device events such as a trigger's `TriggeredEvent`, which are plain properties, `EliminatedEvent` is a function that RETURNS a listenable. Call it first, then `Subscribe(...)` or `Await()` the result.

Recap

One step closer to Coastal Race β€” DNF handling: eliminated racers leave the roster, feed the kill-ticker, become spectators (content/verse compile-PASSED; support tracking-player-health-shields + elimination-feed-device PASS; elimination-manager-device FAILS 1 err β€” fix as supporting ref)

Sources

Lesson 19: Treasure Cove Phases: the game state machine

Objectives

Student can model game flow as an enum-state machine with async phase functions and event-driven transitions (Lobby→Countdown→Racing→Podium→Reset).

πŸ” Builds on: south-shores: enum-with-data-pattern (state seed)

🧩 Your capstone piece: The Coastal Race master state machine orchestrating every prior piece (content/verse compile-PASSED; verse-concurrency-async-race-state-machines PASSES as the deep companion; sug-implementing-game-state-management-with-verse-classes FAILS 1 err β€” optional fix)

Quiz

  1. In the Coastal Race transition table, which event moves Racing β†’ Podium, and which moves Racing β†’ Reset?
    • A. RacerFinished (finish line crossed) β†’ Podium; the storm timeout elapsing first β†’ Reset.
    • B. AllReady β†’ Podium; RacerFinished β†’ Reset.
    • C. The storm timeout β†’ Podium; RacerFinished β†’ Reset.
    • D. Both transitions fire on RacerFinished β€” the machine picks randomly.
  2. Why does each phase function return a `race_phase` instead of setting `Phase` itself?
    • A. So the dispatcher loop is the ONLY place the state changes β€” one `set Phase = ...` line, making every transition traceable.
    • B. Because Verse forbids `set` inside `<suspends>` functions.
    • C. Because enums cannot be assigned outside `OnBegin`.
    • D. It is purely stylistic; scattering `set Phase` across handlers works just as safely.
  3. A latecomer presses the ready button while the Racing phase is running. What happens?
    • A. Nothing β€” AllReady.Signal() fires, but no task is Awaiting it during Racing, so the signal is simply dropped.
    • B. The machine instantly transitions back to Countdown.
    • C. The game crashes because Signal() without an Await is a runtime error.
    • D. The signal is queued and consumed the next time the Lobby phase runs.
  4. In Pattern 2, why must you `set LastWinner = MaybeAgent` BEFORE calling `RacerFinished.Signal()`?
    • A. The awaiting phase resumes as soon as Signal() fires β€” signaling first would let the podium phase read stale (or empty) winner data.
    • B. Verse requires all `set` statements to precede method calls in a function body.
    • C. Signal() clears every var on the device, so the write would be lost.
    • D. The order does not matter; Await always waits one extra frame before resuming.
  5. Predict the output: when RunCountdown() runs, what does the log show?
    • A. [Cove] 3... then [Cove] 2... then [Cove] 1... then [Cove] GO! β€” it prints the count, sleeps one second, decrements, and breaks when the count reaches 0.
    • B. [Cove] GO! immediately, then 3... 2... 1... afterwards.
    • C. [Cove] 3... [Cove] 2... [Cove] 1... [Cove] 0... [Cove] GO! β€” it prints zero before breaking.
    • D. Nothing β€” Countdown exits on an event signal, not a timer.
Answer key
  1. A β€” RunRacing races two arms: one Awaits RacerFinished (signaled when the finish-line trigger fires) and returns the tag for Podium; the other Sleeps for StormTimeoutSeconds and returns the tag for Reset. Whichever completes first decides the next phase; the losing arm is cancelled.
  2. A β€” Funneling every transition through the dispatcher's single `set Phase = ...` means there is exactly one place state changes. Phase functions stay pure 'run, wait, recommend next phase' units β€” which is what makes the machine easy to debug and extend.
  3. A β€” Custom event() signals only wake tasks that are currently Awaiting. Since only RunLobby Awaits AllReady, an out-of-phase press evaporates harmlessly β€” the state machine gets transition guards for free, with no `if` checks in the handlers.
  4. A β€” Signal() wakes the Awaiting task immediately. The payload var is the data riding along with the transition, so it must be written before the transition fires β€” stash first, signal second.
  5. A β€” RunCountdown starts Count at 3, and each loop iteration prints the current Count, Sleeps 1.0 second, decrements, and breaks once Count <= 0. Zero is never printed because the break happens before another print, and 'GO!' prints only after the loop exits.

Recap

One step closer to Coastal Race β€” The Coastal Race master state machine orchestrating every prior piece (content/verse compile-PASSED; verse-concurrency-async-race-state-machines PASSES as the deep companion; sug-implementing-game-state-management-with-verse-classes FAILS 1 err β€” optional fix)

Sources

Lesson 20: The Rusty Barnacle: Inkbeard's NPC behaviour state machine

Objectives

Student can author npc_behavior Verse with an internal state machine (idle/patrol/chase) and attach it to an NPC spawner.

πŸ” Builds on: west-coves L19 state machine + north-jungle classes

🧩 Your capstone piece: The Kraken hazard NPC: patrols the channel, chases and eliminates straggler racers (support: npc-behavior, npc-spawner-device, verse-npc-custom-behavior, verse-npc-guard-patrol all PASS; spawn-npc FAILS 1 err)

Quiz

  1. You want Captain Crow's alert timer to last 20 seconds for a specific player who stepped on the dock. Which call is correct?
    • A. AlertTimer.SetActiveDuration(20.0, A) where A is the unwrapped agent
    • B. AlertTimer.SetActiveDuration(20, A) where A is the unwrapped agent
    • C. AlertTimer.Start(20.0)
    • D. AlertTimer.SetMaxDuration(20.0)
  2. Why must you call AlertTimer.Enable() before AlertTimer.Start()?
    • A. Enable() allocates memory for the timer object
    • B. Start() silently does nothing on a disabled timer
    • C. Enable() sets the timer duration
    • D. Start() requires an agent parameter when the timer is disabled
  3. What does passing 0 to DockTrigger.SetMaxTriggerCount(0) do?
    • A. Disables the trigger immediately
    • B. Sets the trigger to fire exactly once
    • C. Removes the trigger limit so it can fire unlimited times
    • D. Resets the trigger count to its default value
  4. You subclass npc_behavior and call Focus.MaintainFocus(LagoonLocation) directly in OnBegin without a race block. What happens?
    • A. The NPC looks at the location for one tick, then continues
    • B. The NPC's OnBegin suspends at that line forever, blocking all subsequent logic
    • C. MaintainFocus returns immediately because it has no <suspends> effect
    • D. The compiler rejects the call because npc_behavior cannot use focus_interface
  5. StartUrgencyModeEvent on timer_device fires when:
    • A. The timer is first enabled
    • B. The timer is paused
    • C. The timer enters its configured urgency (last-seconds warning) window
    • D. The timer's SuccessEvent has already fired
Answer key
  1. A β€” SetActiveDuration takes a float and an agent. You must unwrap the ?agent first (if A := MaybeAgent?:) and pass 20.0 (float), not 20 (int), since Verse does not auto-convert int to float.
  2. B β€” Calling Start() on a disabled timer_device silently fails β€” the timer never begins. You must Enable() the device first to allow it to receive signals.
  3. C β€” Per the API docs, 0 indicates no limit on trigger count β€” the trigger can fire unlimited times, which is what you want for a repeating patrol cycle.
  4. B β€” MaintainFocus has the <suspends> effect and never returns on its own. Calling it bare in OnBegin freezes the coroutine at that line indefinitely. Always wrap it in a race block with a Sleep or event that can cancel it.
  5. C β€” StartUrgencyModeEvent signals when the timer enters Urgency Mode β€” the configurable last-seconds warning window set in the UEFN device properties. It fires before SuccessEvent or FailureEvent.

Recap

One step closer to Coastal Race β€” The Kraken hazard NPC: patrols the channel, chases and eliminates straggler racers (support: npc-behavior, npc-spawner-device, verse-npc-custom-behavior, verse-npc-guard-patrol all PASS; spawn-npc FAILS 1 err)

Sources

Lesson 21: Crystal Grotto Lens: first-person camera for spectators

Objectives

Student can push/pop a gameplay_camera_first_person_device onto an agent's camera stack from Verse to change perspective at runtime.

🧩 Your capstone piece: Eliminated racers get a first-person spectator cam following the race leader; camera arc step toward Sequencer/control-rig in later zones

Quiz

  1. What method pushes the first-person camera onto a single specific player's camera stack?
    • A. AddTo(Agent)
    • B. AddToAll()
    • C. Enable()
    • D. RemoveFrom(Agent)
  2. A `trigger_device.TriggeredEvent` handler receives which parameter type?
    • A. agent
    • B. ?agent
    • C. player
    • D. void
  3. You want to prevent the first-person camera from being activated at all during a cutscene. What is the correct sequence of calls?
    • A. Disable(), then RemoveFromAll()
    • B. RemoveFromAll(), then Disable()
    • C. RemoveFrom(Agent), then AddToAll()
    • D. Enable(), then RemoveFromAll()
  4. After calling `RemoveFrom(Agent)`, what happens to that player's view?
    • A. The screen goes black until a new camera is manually assigned
    • B. The engine restores the next camera down in that player's camera stack
    • C. All cameras are removed and the player sees the default sky
    • D. The device must call Enable() again before any camera works
  5. Which statement about `gameplay_camera_first_person_device` events is correct?
    • A. It fires a CameraActivatedEvent whenever AddTo is called
    • B. It fires a PlayerEnteredFPSEvent you can subscribe to
    • C. It has no events β€” it is purely imperative and you drive it from other devices' events
    • D. It inherits TriggeredEvent from gameplay_camera_device
Answer key
  1. A β€” `AddTo(Agent)` targets one agent and pushes the camera to the top of that player's stack, making it their active camera. `AddToAll()` does the same for every player at once.
  2. B β€” `TriggeredEvent` is a `listenable(?agent)`, so the handler receives an optional agent `?agent`. You must unwrap it with `if (Agent := MaybeAgent?):` before passing it to camera methods.
  3. B β€” You should call `RemoveFromAll()` first to cleanly pop the camera from any player stacks it's currently on, then `Disable()` to lock the device so nothing can push it again.
  4. B β€” `RemoveFrom` pops this device from the stack and the engine automatically makes the next camera in the stack active β€” typically the default third-person camera. No manual reassignment is needed.
  5. C β€” `gameplay_camera_first_person_device` exposes no events of its own. You react to events from other devices (triggers, buttons, timers) and call the camera's methods (`AddTo`, `RemoveFrom`, etc.) in those handlers.

Recap

One step closer to Coastal Race β€” Eliminated racers get a first-person spectator cam following the race leader; camera arc step toward Sequencer/control-rig in later zones

Sources

Lesson 22: Checkpoint Gate Prefab: async behaviour in Scene Graph components

Objectives

Student can write a custom component whose OnSimulate runs async logic (pulse animation + trigger await) and package the gate entity as a reusable prefab.

πŸ” Builds on: south-shores/the-deeps scene-graph fundamentals: entities-components, scene-graph-add-component

🧩 Your capstone piece: The CheckpointGate prefab (glow pulse, pass-through detection) instanced along the course β€” and exported for the-deeps to import (FAILS compile 5 err β€” rewrite; carries the whole async arc into components)

Quiz

  1. In the Scene Graph, what is an entity best described as?
    • A. A component that always glows
    • B. A container that holds components
    • C. A player's UI widget
    • D. A replacement for Verse
  2. Why is GetPlayerUI called with square brackets in the code?
    • A. It concatenates strings
    • B. It is a <suspends> function
    • C. It is fallible and must sit in a failure context like an if
    • D. Brackets are required for all functions
  3. What happens to a child entity when you move its parent in the Scene Graph?
    • A. Nothing, they are independent
    • B. The child moves with the parent
    • C. The child is deleted
    • D. The parent is deleted
  4. Which type is used for the LanternOn flag?
    • A. bool
    • B. logic
    • C. int
    • D. string
  5. How do you correctly reassign the var LanternOn to true?
    • A. LanternOn = true
    • B. set LanternOn = true
    • C. LanternOn := true
    • D. LanternOn += true
Answer key
  1. B β€” An entity is just a container; components (mesh, light, etc.) give it its powers.
  2. C β€” GetPlayerUI[Player] is fallible, so it is bound inside an `if (UI := GetPlayerUI[Player])`.
  3. B β€” Parenting means moving the parent drags every child along, like a LEGO tower.
  4. B β€” Verse's boolean type is `logic` (true/false), never `bool`.
  5. B β€” Mutable variables are reassigned with `set X = value`.

Recap

One step closer to Coastal Race β€” The CheckpointGate prefab (glow pulse, pass-through detection) instanced along the course β€” and exported for the-deeps to import (FAILS compile 5 err β€” rewrite; carries the whole async arc into components)

Sources

Lesson 23: CAPSTONE β€” Coastal Race: ship it

Objectives

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.

πŸ” 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)

Quiz

  1. Your build-review checklist maps each Coastal Race mechanic to the lesson that taught it. Which row is WRONG?
    • A. Storm timeout β†’ race-expressions: race{} cancels the losing branch
    • B. Eliminated racers spectate β†’ elimination-event + first-person-spectator-camera
    • C. Persistent career wins β†’ countdown-timer-loop: Sleep() loops keep the count alive
    • D. Ordered gates β†’ trigger-device-checkpoints + failure contexts for validation
  2. The boat dock code subscribes OnBoatSpawned(Boat:fort_vehicle) to SpawnedEvent. Which using{} line is required to make it compile, and what error do you get without it?
    • A. using { /Fortnite.com/Vehicles } β€” without it: Unknown identifier `fort_vehicle`
    • B. using { /Fortnite.com/Devices } β€” without it: Unknown identifier `SpawnedEvent`
    • C. using { /Verse.org/Vehicles } β€” without it: Ambiguous identifier `fort_vehicle`
    • D. No extra import β€” fort_vehicle ships with /Fortnite.com/Devices
  3. In DoRacing, the code runs race: { AwaitAllFinished(), StormCountdown() }. Every racer crosses the finish line before the storm timeout elapses. What happens?
    • A. AwaitAllFinished completes and StormCountdown is cancelled mid-Sleep β€” the round ends immediately
    • B. Both branches always run to completion; the storm message still shows later
    • C. StormCountdown wins because Sleep-based branches have scheduler priority
    • D. The race expression restarts StormCountdown for the next round automatically
  4. Which declaration makes each player's win count survive across game sessions?
    • A. A module-scoped `var CoastalRaceWins:weak_map(player, int) = map{}`
    • B. A `var Wins:[player]int = map{}` member inside coastal_race_device
    • C. A local `var Wins:int = 0` inside DoPodium
    • D. The RaceConfig module constant `Wins<public>:int = 0`
  5. A fifth player is in the lobby but the pier only has four Boat Spawners in BoatDocks. During DoCountdown, what happens to racer number five?
    • A. The failable `BoatDocks[Index]` inside the if simply fails for index 4 β€” no boat, no crash; they wait for the next round
    • B. The script crashes with an index-out-of-range runtime error
    • C. The loop wraps around and assigns them to BoatDocks[0], sharing a boat
    • D. DoCountdown blocks forever waiting for a fifth dock to appear
Answer key
  1. C β€” Persistence has nothing to do with Sleep loops β€” career wins survive sessions because `CoastalRaceWins` is a module-scoped `var weak_map(player, int)`, the persistable pattern from East Volcano's tycoon-state lesson. The other three rows map correctly.
  2. A β€” `vehicle_spawner_boat_device` itself comes from /Fortnite.com/Devices, but the `fort_vehicle` type that `SpawnedEvent` sends lives in /Fortnite.com/Vehicles. Omitting that using{} produces `Script error 3506: Unknown identifier 'fort_vehicle'` β€” the classic boat-race compile trap.
  3. A β€” race{} runs its branches concurrently and cancels the rest the moment one completes. When the last finisher makes AwaitAllFinished return, the storm branch is cancelled inside its Sleep β€” the storm announcement never fires. If the timeout elapsed first, the finish-await would be the one cancelled.
  4. A β€” UEFN persists module-scoped var weak_map(player, t) declarations per player, per island, across sessions β€” int is a persistable type, so the map survives. Class members and locals reset every session, and module constants are immutable shared config, not per-player state.
  5. A β€” Array indexing in Verse is failable. `if (Dock := BoatDocks[Index])` fails safely when Index is beyond the array, so the extra racer just gets no boat this round β€” exactly the article's 'a fifth racer at a four-dock pier simply waits' behavior.

Recap

One step closer to Coastal Race β€” 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)

Sources

Generated by Verse Island on 2026-07-04.