Reference Verse compiles

trigger_device & Failure-Decides: The Pirate Cove Cannon

The `trigger_device` is your island's universal relay switch: step on a pressure plate, fire a cannon, open a vault, start a countdown. But the real magic happens when you combine it with Verse's failure-as-control-flow philosophy — instead of checking booleans, you write *success conditions* and let Verse handle the rest. In this article we'll build a 2D cel-shaded pirate cove where a cannon fires only when the right crew member steps on the launch pad, and a second trigger keeps score of remai

Updated Examples verified on the live UEFN compiler
Watch the Knotplayer in ~90 seconds.

Overview

"failure-decides" isn't a placed device you drag into your level — it's the core Verse control-flow pattern that decides whether your island logic runs at all. In Verse, failure is control flow: instead of writing a function that returns true/false, you write a failable function marked <decides><transacts> that either succeeds (and produces a value) or fails (and rolls back cleanly). The calling code runs the success branch only if the check succeeds.

The game problem this solves: imagine a lighthouse horn on a sunny cove that should sound only when a specific player, who is still active in the match, steps on the dock plate. If you tried to model that with boolean flags you'd write brittle if (IsRight and IsAlive and ...) chains. With <decides> you write small optimistic checks and combine them with and, or, and not. Any check that fails short-circuits the whole thing — no half-applied state, because <transacts> automatically rolls back.

We pair this pattern with a real placed device — the trigger_device — because a trigger is the perfect "something happened, now decide if we act" moment. The trigger's TriggeredEvent hands us an ?agent; our failure-decides checks unwrap and validate that agent, and only then do we call the trigger's own Trigger() to relay the confirmed event onward to other linked devices (like a sound or cinematic).

Reach for this pattern whenever an action has requirements: the right agent, a living character, a matching team, an unlocked door.

API Reference

(API surface could not be resolved for this device.)

Walkthrough

Our scene: a dock plate trigger at the edge of a bright cove. When a player steps on it, we want to relay a confirmed "horn blast" only if that player passes our checks. We fire a second linked trigger_device (the horn relay) via Trigger() when the checks succeed.

using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Verse.org/Simulation }

# The dock plate trigger and the horn relay trigger are placed on the cove
# and linked in the editor's details panels.
dock_horn_controller := class(creative_device):

    @editable
    DockPlate : trigger_device = trigger_device{}

    @editable
    HornRelay : trigger_device = trigger_device{}

    OnBegin<override>()<suspends>:void =
        # Subscribe our handler to the plate's TriggeredEvent.
        DockPlate.TriggeredEvent.Subscribe(OnDockStepped)

    # Event handler: a listenable(?agent) hands us an optional agent.
    OnDockStepped(Agent : ?agent) : void =
        # This is where failure decides: if the whole check succeeds,
        # we relay the horn. If any step fails, nothing happens.
        if:
            # 1. Unwrap the optional agent (fails if it was code-triggered).
            SteppingAgent := Agent?
            # 2. Our failure-decides check must succeed.
            ValidateCharacter[SteppingAgent]
        then:
            # Only reached when every check above succeeded.
            HornRelay.Trigger(SteppingAgent)

    # A failable function: it either succeeds (producing nothing useful,
    # just 'void') or fails. Marked <decides><transacts>.
    ValidateCharacter(Agent : agent)<decides><transacts> : void =
        # GetFortCharacter is itself failable — succeeds only if the
        # agent has a character in the world.
        Char := Agent.GetFortCharacter[]
        # IsActive is failable — succeeds only if the character is alive
        # and in the world. If it fails, the whole function fails.
        Char.IsActive[]

Line by line:

  • @editable DockPlate / HornRelay — two placed trigger_devices. DockPlate is the one players step on; HornRelay is linked to your sound/cinematic and gets fired only when checks pass.
  • OnBegin subscribes OnDockStepped to the plate's TriggeredEvent. TriggeredEvent is a listenable(?agent), so our handler must take (Agent : ?agent).
  • OnDockStepped uses an if: block as a failure context. SteppingAgent := Agent? unwraps the optional (this fails if the device was triggered by code with no agent). ValidateCharacter[SteppingAgent] — note the square brackets, which invoke a failable function. Both must succeed for the then branch.
  • HornRelay.Trigger(SteppingAgent) — this real trigger_device method relays the confirmed event to everything linked to the horn relay.
  • ValidateCharacter is our failure-decides function. Agent.GetFortCharacter[] and Char.IsActive[] are both failable calls; if either fails, the function fails and (thanks to <transacts>) any tentative state is rolled back automatically.

Common patterns

Pattern 1 — Trigger() with no agent as a fallback

Sometimes the checks fail but you still want a "denied" cue. Use or to try the strict path first, then a fallback that fires a different relay with the parameterless Trigger().

using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Verse.org/Simulation }

cove_gate := class(creative_device):

    @editable
    GatePlate : trigger_device = trigger_device{}

    @editable
    DeniedBuzzer : trigger_device = trigger_device{}

    OnBegin<override>()<suspends>:void =
        GatePlate.TriggeredEvent.Subscribe(OnStepped)

    OnStepped(Agent : ?agent) : void =
        # If validation fails, fire the buzzer with no agent.
        if:
            A := Agent?
            IsLivingHero[A]
        else:
            # Reached when the checks fail — relay the denied buzzer.
            DeniedBuzzer.Trigger()

    IsLivingHero(Agent : agent)<decides><transacts> : void =
        Char := Agent.GetFortCharacter[]
        Char.IsActive[]

Pattern 2 — combining checks with and / not

Failure-decides shines when several conditions must all hold. Here the horn fires only when a character exists AND is active — and we invert a check with not.

using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Verse.org/Simulation }

clifftop_beacon := class(creative_device):

    @editable
    BeaconPlate : trigger_device = trigger_device{}

    @editable
    LightRelay : trigger_device = trigger_device{}

    OnBegin<override>()<suspends>:void =
        BeaconPlate.TriggeredEvent.Subscribe(OnBeacon)

    OnBeacon(Agent : ?agent) : void =
        if (A := Agent?; PassesAll[A]):
            LightRelay.Trigger(A)

    PassesAll(Agent : agent)<decides><transacts> : void =
        Char := Agent.GetFortCharacter[]
        # 'and' chains failable checks: BOTH must succeed.
        Char.IsActive[] and (not Char.GetHealth() <= 0.0)

Pattern 3 — a helper that produces a value on success

A <decides> function can return something. Here it returns the confirmed fort_character, which we then use before relaying the trigger.

using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Verse.org/Simulation }

shore_scanner := class(creative_device):

    @editable
    ScanPlate : trigger_device = trigger_device{}

    @editable
    RewardRelay : trigger_device = trigger_device{}

    OnBegin<override>()<suspends>:void =
        ScanPlate.TriggeredEvent.Subscribe(OnScan)

    OnScan(Agent : ?agent) : void =
        if:
            A := Agent?
            Char := ActiveCharacter[A]
        then:
            # We have a validated character in 'Char' if we need it.
            RewardRelay.Trigger(A)

    # <decides> function that returns the fort_character on success.
    ActiveCharacter(Agent : agent)<decides><transacts> : fort_character =
        Char := Agent.GetFortCharacter[]
        Char.IsActive[]
        Char

Gotchas

  • Square brackets, not parentheses, call failable functions. ValidateCharacter[A] invokes the <decides> version. Writing ValidateCharacter(A) treats it as non-failable and won't compile in the failure context.
  • You can only call a <decides> function inside a failure context — an if: condition, another <decides> function, or a for/case guard. Calling it at plain statement level fails to compile.
  • TriggeredEvent gives ?agent, and it can be false. If the device was triggered from code via Trigger() (no agent), unwrapping Agent? fails. Always unwrap before validating.
  • <transacts> means rollback is automatic. Any state your function tentatively changed is undone on failure — don't write manual cleanup for the failure path.
  • Don't return logic for validation. Returning a boolean and then if (MyBool = true) throws away Verse's whole failure model. Model requirements as failable expressions and combine with and/or/not.
  • Trigger(Agent) vs Trigger(). The agent overload matters when a downstream device is set to require a specific agent; the no-arg overload just fires the event. Pick the one your linked device expects.

Guides & scripts that use player

Step-by-step tutorials that put this object to work.

Build your own lesson with player

Generate a personalized, step-by-step lesson plan built around this object — grounded in this exact reference and our compile-verified knowledge base.

Build a lesson →