Overview
Clamp is a pure math function built into Verse's core library. It takes three floats — a value, a lower bound, and an upper bound — and returns the value pinched into that range:
- If
Valis belowA, you getA. - If
Valis aboveB, you getB. - If
Valis between them, you getValunchanged.
The function is robust: it handles the case where A > B by returning the median of all three arguments, and it treats NaN as greater than positive infinity, so you never get a silent bad value slipping through.
When to reach for it:
- Clamping a health or shield value before displaying it.
- Keeping a spawn-delay or reset-delay inside a legal device range (e.g.,
SetMaxTriggerCountonly accepts 0–20). - Normalising a score multiplier so it never goes below 1× or above 5×.
- Sanitising any float that comes from player input, physics, or arithmetic before passing it to a device method.
Clamp lives in /Verse.org/Verse — no extra import needed beyond the standard creative-device boilerplate.
API Reference
trigger_device
Used to relay events to other linked devices.
Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse). Inherited members are merged from trigger_base_device.
trigger_device<public> := class<concrete><final>(trigger_base_device):
Events (subscribe a handler to react):
| Event | Signature | Description |
|---|---|---|
TriggeredEvent |
TriggeredEvent<public>:listenable(?agent) |
Signaled when an agent triggers this device. Sends the agent that used this device. Returns false if no agent triggered the action (ex: it was triggered through code). |
Methods (call these to make the device act):
| Method | Signature | Description |
|---|---|---|
Trigger |
Trigger<public>(Agent:agent):void |
Triggers this device with Agent being passed as the agent that triggered the action. Use an agent reference when this device is setup to require one (for instance, you want to trigger the device only with a particular agent. |
Trigger |
Trigger<public>():void |
Triggers this device, causing it to activate its TriggeredEvent event. |
Enable |
Enable<public>():void |
Enables this device. |
Disable |
Disable<public>():void |
Disables this device. |
SetMaxTriggerCount |
SetMaxTriggerCount<public>(MaxCount:int):void |
Sets the maximum amount of times this device can trigger. * 0 can be used to indicate no limit on trigger count. * MaxCount is clamped between [0,20]. |
GetMaxTriggerCount |
GetMaxTriggerCount<public>()<transacts>:int |
Gets the maximum amount of times this device can trigger. * 0 indicates no limit on trigger count. |
GetTriggerCountRemaining |
GetTriggerCountRemaining<public>()<transacts>:int |
Returns the number of times that this device can still be triggered before hitting GetMaxTriggerCount. Returns 0 if GetMaxTriggerCount is unlimited. |
SetResetDelay |
SetResetDelay<public>(Time:float):void |
Sets the time (in seconds) after triggering, before the device can be triggered again (if MaxTrigger count allows). |
GetResetDelay |
GetResetDelay<public>()<transacts>:float |
Gets the time (in seconds) before the device can be triggered again (if MaxTrigger count allows). |
SetTransmitDelay |
SetTransmitDelay<public>(Time:float):void |
Sets the time (in seconds) which must pass after triggering, before this device informs other external devices that it has been triggered. |
GetTransmitDelay |
GetTransmitDelay<public>()<transacts>:float |
Gets the time (in seconds) which must pass after triggering, before this device informs other external devices that it has been triggered. |
player
Full public surface, resolved verbatim from the live Epic digest (Verse.digest.verse). Inherited members are merged from agent.
player<native><public> := class<unique><persistent><module_scoped_var_weak_map_key><epic_internal>(agent):
timer_device
Provides a way to keep track of the time something has taken, either for scoreboard purposes, or to trigger actions. It can be configured in several ways, either acting as a countdown to an event that is triggered at the end, or as a stopwatch for an action that needs to be completed before a set time runs out.
Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).
timer_device<public> := class<concrete><final>(creative_device_base):
Events (subscribe a handler to react):
| Event | Signature | Description |
|---|---|---|
SuccessEvent |
SuccessEvent<public>:listenable(?agent) |
Signaled when the timer completes or ends with success. Sends the agent that activated the timer, if any. |
FailureEvent |
FailureEvent<public>:listenable(?agent) |
Signaled when the timer completes or ends with failure. Sends the agent that activated the timer, if any. |
StartUrgencyModeEvent |
StartUrgencyModeEvent<public>:listenable(?agent) |
Signaled when the timer enters Urgency Mode. Sends the agent that activated the timer, if any. |
Methods (call these to make the device act):
| Method | Signature | Description |
|---|---|---|
Enable |
Enable<public>(Agent:agent):void |
Enables this device for Agent. |
Enable |
Enable<public>():void |
Enables this device. |
Disable |
Disable<public>(Agent:agent):void |
Disables this device for Agent. While disabled this device will not receive signals. |
Disable |
Disable<public>():void |
Disables this device. While disabled this device will not receive signals. |
ResetForAll |
ResetForAll<public>(Agent:agent):void |
Resets the timer back to its base time and stops it for all agents. |
ResetForAll |
ResetForAll<public>():void |
Resets the timer back to its base time and stops it for all agents. |
Start |
Start<public>(Agent:agent):void |
Starts the timer for Agent. |
Start |
Start<public>():void |
Starts the timer. |
Pause |
Pause<public>(Agent:agent):void |
Pauses the timer for Agent. |
Pause |
Pause<public>():void |
Pauses the timer. |
Resume |
Resume<public>(Agent:agent):void |
Resumes the timer for Agent. |
Resume |
Resume<public>():void |
Resumes the timer. |
Complete |
Complete<public>(Agent:agent):void |
Completes the timer for Agent. |
Complete |
Complete<public>():void |
Completes the timer. |
StartForAll |
StartForAll<public>(Agent:agent):void |
Starts the timer for all agents. |
StartForAll |
StartForAll<public>():void |
Starts the timer for all agents. |
PauseForAll |
PauseForAll<public>(Agent:agent):void |
Pauses the timer for all agents. |
PauseForAll |
PauseForAll<public>():void |
Pauses the timer for all agents. |
ResumeForAll |
ResumeForAll<public>(Agent:agent):void |
Resumes the timer for all agents. |
ResumeForAll |
ResumeForAll<public>():void |
Resumes the timer for all agents. |
CompleteForAll |
CompleteForAll<public>(Agent:agent):void |
Completes the timer for all agents. |
CompleteForAll |
CompleteForAll<public>():void |
Completes the timer for all agents. |
Save |
Save<public>(Agent:agent):void |
Saves this device's data for Agent. |
Load |
Load<public>(Agent:agent):void |
Loads this device's saved data for Agent. |
ClearPersistenceData |
ClearPersistenceData<public>(Agent:agent):void |
Clears this device's saved data for Agent. |
ClearPersistenceDataForAll |
ClearPersistenceDataForAll<public>(Agent:agent):void |
Clears this device's saved data for all agents. |
ClearPersistenceDataForAll |
ClearPersistenceDataForAll<public>():void |
Clears this device's saved data for all agents. |
SetActiveDuration |
SetActiveDuration<public>(Time:float, Agent:agent):void |
Sets the remaining time (in seconds) on the timer, if active, on Agent. |
SetActiveDuration |
SetActiveDuration<public>(Time:float):void |
Sets the remaining time (in seconds) on the timer, if active. Use this function if the timer is set to use the same time for all agent's. |
GetActiveDuration |
GetActiveDuration<public>(Agent:agent)<transacts>:float |
Returns the remaining time (in seconds) on the timer for Agent. |
GetActiveDuration |
GetActiveDuration<public>()<transacts>:float |
Returns the remaining time (in seconds) on the timer if it is set to be global. |
SetLapTime |
SetLapTime<public>(Agent:agent):void |
Sets the lap time indicator for Agent. |
SetLapTimeForAll |
SetLapTimeForAll<public>(Agent:agent):void |
Sets the lap time indicator for all agents. |
SetLapTimeForAll |
SetLapTimeForAll<public>():void |
Sets the lap time indicator for all agents. |
SetMaxDuration |
SetMaxDuration<public>(Time:float):void |
Sets the maximum duration of the timer (in seconds). |
GetMaxDuration |
GetMaxDuration<public>()<transacts>:float |
Returns the maximum duration of the timer (in seconds). |
IsStatePerAgent |
IsStatePerAgent<public>()<transacts><decides>:void |
Succeeds if this device is tracking timer state for each individual agent independently. Fails if state is being tracked globally for all agent's. |
Walkthrough
The Scenario
You have a sun-drenched pirate dock. A player sprints across the planks and steps on a pressure plate at the far end. The moment they step on it, a countdown timer starts — they have 30 seconds to reach the treasure chest trigger at the dock's tip. A second trigger at the tip completes the timer and fires a success fanfare.
The twist: you want to display how many whole seconds remain when the player finishes, clamped between 0 and 30 so a laggy network tick or a sub-zero float never produces a nonsense value. You also use Clamp to safely set the trigger's MaxTriggerCount (which the API clamps to 0–20 internally, but you clamp first so your intent is explicit).
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Verse }
# ─────────────────────────────────────────────────────────────────
# dock_race_device
# Place in your level and wire up the three @editable fields.
# ─────────────────────────────────────────────────────────────────
dock_race_device := class(creative_device):
# The pressure plate at the START of the dock.
@editable
StartPlate : trigger_device = trigger_device{}
# The trigger zone at the TIP of the dock (the finish).
@editable
FinishTrigger : trigger_device = trigger_device{}
# The countdown timer (configure it for 30 s in the Details panel).
@editable
RaceTimer : timer_device = timer_device{}
# How many seconds the race window lasts.
RaceDurationSeconds : float = 30.0
# Tracks when the race started (seconds since level load).
var RaceStartTime : float = 0.0
# ── Lifecycle ────────────────────────────────────────────────
OnBegin<override>()<suspends> : void =
# Allow the start plate to fire exactly once per race attempt.
# SetMaxTriggerCount accepts 0-20; we clamp our desired value
# to that range so the intent is self-documenting.
var DesiredMax : int = 1
StartPlate.SetMaxTriggerCount(Clamp(float[DesiredMax], 0.0, 20.0) |> int[...])
# ↑ simpler: just pass 1 directly — shown here to illustrate Clamp on the path
StartPlate.SetMaxTriggerCount(1)
# Wire events.
StartPlate.TriggeredEvent.Subscribe(OnDockStart)
FinishTrigger.TriggeredEvent.Subscribe(OnDockFinish)
RaceTimer.FailureEvent.Subscribe(OnTimerFailed)
# ── Handlers ─────────────────────────────────────────────────
# Called when the player steps on the start plate.
OnDockStart(MaybeAgent : ?agent) : void =
# Record the wall-clock start time.
set RaceStartTime = GetSimulationElapsedTime()
# Start the visual countdown for everyone.
RaceTimer.Start()
# Re-enable the finish trigger (it starts disabled in the Details panel).
FinishTrigger.Enable()
# Called when the player hits the finish trigger at the dock tip.
OnDockFinish(MaybeAgent : ?agent) : void =
# How many seconds elapsed?
var Elapsed : float = GetSimulationElapsedTime() - RaceStartTime
# Clamp elapsed to [0, RaceDurationSeconds] — guards against
# a negative delta if clocks drift, or a value beyond the window.
var ClampedElapsed : float = Clamp(Elapsed, 0.0, RaceDurationSeconds)
# Remaining time, also clamped so it can never be negative.
var Remaining : float = Clamp(RaceDurationSeconds - ClampedElapsed, 0.0, RaceDurationSeconds)
# Mark the timer as a success for the finishing agent.
if (A := MaybeAgent?):
RaceTimer.Complete(A)
else:
RaceTimer.Complete()
# Disable the finish trigger until the next race.
FinishTrigger.Disable()
# Called when the countdown hits zero before the player finishes.
OnTimerFailed(MaybeAgent : ?agent) : void =
# Reset everything so the player can try again.
RaceTimer.ResetForAll()
FinishTrigger.Disable()
StartPlate.Enable()
StartPlate.SetMaxTriggerCount(1)
Line-by-line highlights
| Line / concept | Why it matters |
|---|---|
Clamp(Elapsed, 0.0, RaceDurationSeconds) |
Core usage — pins the raw elapsed float so downstream math is always safe. |
Clamp(RaceDurationSeconds - ClampedElapsed, 0.0, RaceDurationSeconds) |
Second clamp on the result of subtraction — belt-and-suspenders against float imprecision. |
StartPlate.SetMaxTriggerCount(1) |
Limits the plate to one fire per attempt; SetMaxTriggerCount internally clamps to [0,20]. |
RaceTimer.Complete(A) |
Signals success on the timer for the specific agent, triggering any linked fanfare devices. |
RaceTimer.ResetForAll() |
Resets the countdown for everyone so the next attempt starts clean. |
if (A := MaybeAgent?): |
Safe unwrap of the ?agent the event sends — never assume an agent is present. |
Common patterns
Pattern 1 — Clamping a reset delay before applying it
You want players to be able to re-trigger the dock bell, but only after a cooldown. The cooldown comes from a variable that might have been set to an out-of-range value. Clamp it before calling SetResetDelay.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Verse }
dock_bell_device := class(creative_device):
@editable
BellTrigger : trigger_device = trigger_device{}
# Desired cooldown supplied by a designer — could be anything.
@editable
DesiredCooldownSeconds : float = 5.0
OnBegin<override>()<suspends> : void =
# SetResetDelay expects a non-negative float.
# Clamp to [0.5, 60.0] — sane dock-bell range.
var SafeCooldown : float = Clamp(DesiredCooldownSeconds, 0.5, 60.0)
BellTrigger.SetResetDelay(SafeCooldown)
# Confirm what was actually set.
var ActualDelay : float = BellTrigger.GetResetDelay()
BellTrigger.TriggeredEvent.Subscribe(OnBellRung)
OnBellRung(MaybeAgent : ?agent) : void =
# Fire a chained trigger (e.g. linked to a sound cue device).
BellTrigger.Trigger()
Pattern 2 — Clamping a transmit delay for a cinematic trigger
A cannon fires on the pirate ship. You want a transmit delay so the explosion sound device fires slightly after the visual — but the delay must stay between 0.1 s and 2.0 s regardless of what a config float says.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Verse }
cannon_fire_device := class(creative_device):
@editable
CannonTrigger : trigger_device = trigger_device{}
# Designer-supplied delay in seconds.
@editable
ExplosionDelaySeconds : float = 0.75
OnBegin<override>()<suspends> : void =
# Clamp the transmit delay to a sensible cinematic window.
var SafeDelay : float = Clamp(ExplosionDelaySeconds, 0.1, 2.0)
CannonTrigger.SetTransmitDelay(SafeDelay)
# Also cap how many times the cannon can fire per round.
# SetMaxTriggerCount clamps internally to [0,20]; we mirror that.
var MaxShots : int = 3
CannonTrigger.SetMaxTriggerCount(Clamp(float[MaxShots], 0.0, 20.0) |> int[...])
# Simpler direct call — shown both ways for illustration:
CannonTrigger.SetMaxTriggerCount(3)
CannonTrigger.TriggeredEvent.Subscribe(OnCannonFired)
OnCannonFired(MaybeAgent : ?agent) : void =
# How many shots remain? 0 means unlimited, so guard that.
var Remaining : int = CannonTrigger.GetTriggerCountRemaining()
# Clamp remaining to [0, 3] for a display-safe value.
var DisplayRemaining : int = int[Clamp(float[Remaining], 0.0, 3.0)]
Pattern 3 — Clamping timer duration before starting a lap race
A lagoon lap race lets the host set a custom duration. Clamp it before calling SetActiveDuration so the timer never gets a zero or absurdly large value.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Verse }
lagoon_lap_device := class(creative_device):
@editable
LapTimer : timer_device = timer_device{}
@editable
StartGate : trigger_device = trigger_device{}
# Host-configurable lap duration; might be 0 or 9999 by mistake.
@editable
DesiredLapSeconds : float = 45.0
OnBegin<override>()<suspends> : void =
StartGate.TriggeredEvent.Subscribe(OnRaceStart)
LapTimer.SuccessEvent.Subscribe(OnLapComplete)
LapTimer.FailureEvent.Subscribe(OnLapFailed)
OnRaceStart(MaybeAgent : ?agent) : void =
if (A := MaybeAgent?):
# Clamp the designer value to [10.0, 120.0] seconds.
var SafeDuration : float = Clamp(DesiredLapSeconds, 10.0, 120.0)
LapTimer.SetActiveDuration(SafeDuration, A)
LapTimer.Start(A)
OnLapComplete(MaybeAgent : ?agent) : void =
LapTimer.ResetForAll()
StartGate.Enable()
OnLapFailed(MaybeAgent : ?agent) : void =
LapTimer.ResetForAll()
StartGate.Enable()
Gotchas
1. Clamp works on float, not int — convert explicitly
Verse does not auto-convert between int and float. If you have an int value you want to clamp, cast it first with float[MyInt], clamp, then cast back with int[Result] (which truncates toward zero).
# WRONG — type mismatch, won't compile:
# var Safe : int = Clamp(MyIntValue, 0, 20)
# CORRECT:
var Safe : int = int[Clamp(float[MyIntValue], 0.0, 20.0)]
2. Argument order doesn't matter — but be explicit anyway
Clamp(Val, A, B) returns the median of all three, so Clamp(5.0, 10.0, 2.0) still returns 5.0 (the median). This robustness is intentional, but relying on it makes code hard to read. Always pass A as your intended lower bound and B as your upper bound.
3. NaN is treated as greater than +Inf
If Val is NaN (e.g., from a division by zero), Clamp returns B (the upper bound). This is a safe fallback, but you should still guard the inputs that could produce NaN — especially division results.
4. ?agent events must be unwrapped before use
Every event on trigger_device and timer_device sends ?agent (an optional agent). You must unwrap it before passing to agent-specific device methods:
# WRONG — ?agent is not agent:
# RaceTimer.Complete(MaybeAgent) # compile error
# CORRECT:
if (A := MaybeAgent?):
RaceTimer.Complete(A)
5. SetMaxTriggerCount clamps to [0, 20] internally — but document your intent
The device will silently clamp any value you pass. If you pass 50, you get 20. Calling Clamp yourself before the call makes the constraint visible to future readers and prevents confusion when GetMaxTriggerCount() returns a different number than you set.
6. message parameters need localised values
If you ever display remaining time through a device that takes a message parameter, you cannot pass a raw string. Declare a localised helper:
TimeLabel<localizes>(S : string) : message = "{S}"
Then pass TimeLabel("Time: 12s"). There is no StringToMessage function in Verse.