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 placedtrigger_devices.DockPlateis the one players step on;HornRelayis linked to your sound/cinematic and gets fired only when checks pass.OnBeginsubscribesOnDockSteppedto the plate'sTriggeredEvent.TriggeredEventis alistenable(?agent), so our handler must take(Agent : ?agent).OnDockSteppeduses anif: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 thethenbranch.HornRelay.Trigger(SteppingAgent)— this realtrigger_devicemethod relays the confirmed event to everything linked to the horn relay.ValidateCharacteris our failure-decides function.Agent.GetFortCharacter[]andChar.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. WritingValidateCharacter(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 — anif:condition, another<decides>function, or afor/caseguard. Calling it at plain statement level fails to compile. TriggeredEventgives?agent, and it can befalse. If the device was triggered from code viaTrigger()(no agent), unwrappingAgent?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
logicfor validation. Returning a boolean and thenif (MyBool = true)throws away Verse's whole failure model. Model requirements as failable expressions and combine withand/or/not. Trigger(Agent)vsTrigger(). 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.