Reference Devices compiles

basic_storm_controller_device: Scripted Storms That Close In on Cue

The `basic_storm_controller_device` lets you summon and dismiss a Battle Royale-style storm entirely from Verse — no manual timeline required. Whether you want the storm to kick in only after a boss is defeated, reset between rounds, or chain into a cinematic the moment it finishes closing, this device gives you the hooks to do it. It exposes two action methods (`GenerateStorm` / `DestroyStorm`) and one event (`PhaseEndedEvent`) that together cover every scripted-storm scenario you'll encounter.

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

Overview

The basic_storm_controller_device is a single-phase storm controller — it shrinks the safe zone once, from its starting radius down to the configured end radius, over the configured duration. It lives in the storm_controller_device inheritance chain alongside the multi-phase advanced_storm_controller_device.

When to reach for it:

  • You need a storm that closes in once (survival rounds, timed challenges, last-player-standing modes).
  • You want to delay the storm's start until a scripted moment (e.g., after a wave of enemies is cleared).
  • You need to reset the storm between rounds without restarting the whole session.
  • You want to react the instant the storm finishes closing (trigger a cinematic, open a loot vault, end the round).

Key constraint: If you call GenerateStorm() from Verse, you must set the device's Generate Storm On Game Start option to No in the Details panel. Leaving it on Yes means the storm starts automatically and your Verse call may conflict with it.

API Reference

basic_storm_controller_device

A simplified storm device that provides a way to create a single-phase storm and control its basic behaviors. To control multiple phases of the storm see advanced_storm_controller_device.

Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse). Inherited members are merged from storm_controller_device.

basic_storm_controller_device<public> := class<concrete><final>(storm_controller_device):

Events (subscribe a handler to react):

Event Signature Description
PhaseEndedEvent PhaseEndedEvent<public>:listenable(tuple()) Signaled when storm resizing ends. Use this with the On Finish Behavior option for better controls.

Methods (call these to make the device act):

Method Signature Description
GenerateStorm GenerateStorm<public>():void Generates the storm. Generate Storm On Game Start must be set to No if you choose to use GenerateStorm.
DestroyStorm DestroyStorm<public>():void Destroys the storm.

storm_controller_device

Base class for various specialized storm devices. See also: * basic_storm_controller_device * advanced_storm_controller_device

Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).

storm_controller_device<public> := class<abstract><epic_internal>(creative_device_base):

Events (subscribe a handler to react):

Event Signature Description
PhaseEndedEvent PhaseEndedEvent<public>:listenable(tuple()) Signaled when storm resizing ends. Use this with the On Finish Behavior option for better controls.

Methods (call these to make the device act):

Method Signature Description
GenerateStorm GenerateStorm<public>():void Generates the storm. Generate Storm On Game Start must be set to No if you choose to use GenerateStorm.
DestroyStorm DestroyStorm<public>():void Destroys the storm.

Walkthrough

Scenario: The Vault Opens When the Storm Closes In

A survival island has a locked vault in the center. The storm doesn't start until all enemies are eliminated. The moment the storm finishes closing, the vault door barrier device is disabled, rewarding survivors who made it to the safe zone.

UEFN setup:

  1. Place a basic_storm_controller_device. In Details → set Generate Storm On Game Start = No.
  2. Place a barrier_device in front of the vault door.
  3. Place a trigger_device that players activate to signal "enemies cleared" (or wire it to a guard spawner's AllGuardsEliminatedEvent — here we use a trigger for simplicity).
  4. Create a new Verse device, wire all three as @editable fields.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }

# vault_storm_manager_device
# - StormController: basic_storm_controller_device (Generate Storm On Game Start = No)
# - VaultBarrier:    barrier_device (starts Enabled)
# - EnemiesClearedTrigger: trigger_device (players activate when all enemies are down)
vault_storm_manager_device := class(creative_device):

    # Wire this to your basic_storm_controller_device in the Details panel.
    @editable
    StormController : basic_storm_controller_device = basic_storm_controller_device{}

    # Wire this to the barrier_device blocking the vault entrance.
    @editable
    VaultBarrier : barrier_device = barrier_device{}

    # Wire this to a trigger_device players activate to signal enemies are cleared.
    @editable
    EnemiesClearedTrigger : trigger_device = trigger_device{}

    OnBegin<override>()<suspends> : void =
        # Step 1 — wait for the "enemies cleared" signal before starting the storm.
        EnemiesClearedTrigger.TriggeredEvent.Await()

        # Step 2 — generate the storm now that enemies are gone.
        StormController.GenerateStorm()

        # Step 3 — block here until the storm finishes closing.
        # PhaseEndedEvent fires a tuple(), so we Await it directly.
        StormController.PhaseEndedEvent.Await()

        # Step 4 — storm has fully closed in; open the vault!
        VaultBarrier.Disable()

Line-by-line breakdown:

Line What it does
@editable StormController Exposes the storm device slot in the Details panel so you can wire the placed device.
@editable VaultBarrier Exposes the barrier device slot.
@editable EnemiesClearedTrigger Exposes the trigger slot.
EnemiesClearedTrigger.TriggeredEvent.Await() Suspends until a player fires the trigger — storm stays off until then.
StormController.GenerateStorm() Kicks off the single storm phase. Requires Generate Storm On Game Start = No in Details.
StormController.PhaseEndedEvent.Await() Suspends until the storm finishes shrinking (the phase ends).
VaultBarrier.Disable() Removes the vault barrier, rewarding players who reached the safe zone.

Note: PhaseEndedEvent is a listenable(tuple()). Calling .Await() on it returns the empty tuple () — you don't need to capture it.


Common patterns

Pattern 1 — Subscribe to PhaseEndedEvent for multi-listener setups

When multiple systems need to react to the storm ending (e.g., spawn loot and play a sound and update a HUD), use Subscribe instead of Await so every handler fires independently.

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

# storm_phase_subscriber_device
# Demonstrates subscribing a handler to PhaseEndedEvent.
storm_phase_subscriber_device := class(creative_device):

    @editable
    StormController : basic_storm_controller_device = basic_storm_controller_device{}

    @editable
    LootSpawner : item_spawner_device = item_spawner_device{}

    # Called once at game start — wire up the subscription.
    OnBegin<override>()<suspends> : void =
        # Subscribe: OnStormPhaseDone is called every time PhaseEndedEvent fires.
        StormController.PhaseEndedEvent.Subscribe(OnStormPhaseDone)

        # Generate the storm immediately at game start
        # (Details panel: Generate Storm On Game Start = No).
        StormController.GenerateStorm()

    # Handler — PhaseEndedEvent is listenable(tuple()), so the handler takes ().
    OnStormPhaseDone() : void =
        # Spawn bonus loot at the center safe zone the moment the storm locks in.
        LootSpawner.SpawnItem()

Why this matters: Subscribe lets you attach multiple independent handlers to the same event without chaining Await calls in a single coroutine. Each handler runs in its own context when the event fires.


Pattern 2 — DestroyStorm to reset between rounds

In a multi-round survival game you want to clear the storm at the end of each round and regenerate it for the next one.

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

# storm_round_manager_device
# Runs two storm rounds back-to-back, resetting between them.
storm_round_manager_device := class(creative_device):

    @editable
    StormController : basic_storm_controller_device = basic_storm_controller_device{}

    # A trigger players hit to advance to the next round.
    @editable
    NextRoundTrigger : trigger_device = trigger_device{}

    OnBegin<override>()<suspends> : void =
        RunRound(1)
        RunRound(2)

    # Encapsulates a single storm round.
    RunRound(RoundNumber : int)<suspends> : void =
        # Generate the storm for this round.
        StormController.GenerateStorm()

        # Wait for the storm phase to fully close.
        StormController.PhaseEndedEvent.Await()

        # Round over — wait for the host to signal the next round.
        NextRoundTrigger.TriggeredEvent.Await()

        # Tear down the storm so it can be regenerated cleanly.
        StormController.DestroyStorm()

Key point: Always call DestroyStorm() before calling GenerateStorm() again. Calling GenerateStorm() while a storm already exists produces undefined behavior — the device expects a clean state.


Pattern 3 — Delayed storm with a countdown trigger

Give players a grace period before the storm appears, then generate it on demand.

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

# delayed_storm_device
# Waits 30 seconds after game start, then generates the storm.
delayed_storm_device := class(creative_device):

    @editable
    StormController : basic_storm_controller_device = basic_storm_controller_device{}

    # A countdown timer device set to 30 seconds.
    @editable
    GracePeriodTimer : timer_device = timer_device{}

    OnBegin<override>()<suspends> : void =
        # Start the visible countdown so players know the storm is coming.
        GracePeriodTimer.Start()

        # Block until the timer fires its completion event.
        GracePeriodTimer.SuccessEvent.Await()

        # Grace period over — unleash the storm.
        StormController.GenerateStorm()

        # React when the storm finishes closing.
        StormController.PhaseEndedEvent.Await()

        # Storm fully closed — destroy it to clean up.
        StormController.DestroyStorm()

Gotchas

1. Generate Storm On Game Start must be No when calling GenerateStorm()

This is the single most common mistake. If the device's Details panel still has Generate Storm On Game Start = Yes, the storm fires at game start and your GenerateStorm() call may produce a double-storm or be silently ignored. Always flip it to No before writing any Verse that calls GenerateStorm().

2. PhaseEndedEvent is listenable(tuple()) — not listenable(?agent)

Unlike player-triggered events, PhaseEndedEvent carries no payload. When you Subscribe, your handler signature must be HandlerName() : void = (zero parameters). Giving it an Agent parameter will cause a compile error.

# CORRECT
OnPhaseDone() : void =
    # do stuff

# WRONG — tuple() carries no agent
OnPhaseDone(Agent : ?agent) : void =
    # compile error

3. Call DestroyStorm() before regenerating

The device does not auto-reset. If you call GenerateStorm() while a storm is already active (even a fully-closed one), behavior is undefined. Always pair each GenerateStorm() with a subsequent DestroyStorm() before the next round.

4. @editable is mandatory — you cannot instantiate the device in Verse

basic_storm_controller_device is a <concrete><final> class but it's a placed device — it must exist in the UEFN level and be wired via an @editable field. You cannot write basic_storm_controller_device{} as a meaningful runtime object; the {} default is only a placeholder until the editor wires the real placed instance.

5. Moving the device mid-storm has no effect on the current phase

The inherited TeleportTo / MoveTo methods reposition the device, but existing storms do not retarget — only a newly generated storm will use the new position. If you need a dynamic storm center, call DestroyStorm(), move the device, then call GenerateStorm() again.

6. No intfloat auto-conversion in timer/duration values

If you compute a delay duration in Verse and pass it to any method expecting float, make sure you produce a float literal or cast explicitly. 30 is an int; 30.0 is a float. Mixing them causes a compile error.

Device Settings & Options

The basic_storm_controller_device User Options panel in the UEFN editor — every setting you can tune.

Basic Storm Controller Device settings and options panel in the UEFN editor — Generate Storm On Game Start, Initial Radius, End Radius, Damage, Operat Summ
Basic Storm Controller Device — User Options (1 of 2) in the UEFN editor: Generate Storm On Game Start, Initial Radius, End Radius, Damage, Operat Summ
Basic Storm Controller Device settings and options panel in the UEFN editor — Generate Storm On Game Start, Initial Radius, End Radius, Damage, Operat Summ
Basic Storm Controller Device — User Options (2 of 2) in the UEFN editor: Generate Storm On Game Start, Initial Radius, End Radius, Damage, Operat Summ
⚙️ Settings on this device (5)

Setting names read from the panel above — may be partial; the screenshots are the source of truth.

Generate Storm On Game Start
Initial Radius
End Radius
Damage
Operat Summ

Guides & scripts that use basic_storm_controller_device

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

Build your own lesson with basic_storm_controller_device

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 →