Overview
The advanced_storm_controller_device solves the classic BR problem: how do I keep players from camping the edges while also scripting dramatic pacing? Drop the device on your island, configure up to 50 phases in the editor (size, speed, damage, beacon targets), then use Verse to decide when the storm starts, when it stops, and what happens at the end of each phase.
Key use-cases:
- Delayed start — hold the storm until a puzzle is solved or a boss is defeated, then call
GenerateStorm(). - Emergency reset — call
DestroyStorm()to wipe the storm if a round ends early or a special event fires. - Phase-gated events — subscribe to
PhaseEndedEventto spawn a new wave of enemies, open a vault, or announce the next safe zone every time a phase finishes closing.
Editor prerequisite: Set Generate Storm On Game Start to No in the device's Details panel whenever you plan to call
GenerateStorm()from Verse. If you leave it on Yes, the storm starts automatically and a secondGenerateStorm()call is ignored.
API Reference
advanced_storm_controller_device
Used to control a Battle Royale-style storm with up to 50 phases. Like
basic_storm_controller_devices, you can use this storm to keep players inside a playable area, but unlike thebasic_storm_controller_device, this device generates multiple storm phases. When used in conjunction withadvanced_storm_beacon_devices, you can customize each phase of the storm by applying one or more beacons a
Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse). Inherited members are merged from storm_controller_device.
advanced_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: Boss-Triggered Storm with Phase Announcements
A player defeats a boss (simulated here by a trigger_device the designer wires to the boss's elimination output). The moment that trigger fires, the storm spins up. After each phase finishes closing, a item_granter_device hands every surviving player a shield potion as a reward for lasting another ring. If the round ends early (a second trigger fires), the storm is destroyed immediately.
using { /Fortnite.com/Devices }
using { /Fortnite.com/Game }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# Localized helper — needed any time we pass text to a message param
PhaseCompleteText<localizes>(S : string) : message = "{S}"
boss_storm_manager_device := class(creative_device):
# Wire this to the boss elimination trigger in the editor
@editable
BossTrigger : trigger_device = trigger_device{}
# Wire this to a second trigger that fires when the round ends early
@editable
EarlyEndTrigger : trigger_device = trigger_device{}
# The advanced storm controller placed on the island
# Make sure "Generate Storm On Game Start" = No in its Details panel
@editable
StormController : advanced_storm_controller_device = advanced_storm_controller_device{}
# An item granter set to give a small shield potion
@editable
PhaseRewardGranter : item_granter_device = item_granter_device{}
# Track how many phases have completed
var PhasesCompleted : int = 0
OnBegin<override>()<suspends> : void =
# Subscribe to the boss trigger — when it fires, start the storm
BossTrigger.TriggeredEvent.Subscribe(OnBossDefeated)
# Subscribe to the early-end trigger — destroy the storm if round ends
EarlyEndTrigger.TriggeredEvent.Subscribe(OnEarlyEnd)
# Subscribe to PhaseEndedEvent so we can reward players each ring
StormController.PhaseEndedEvent.Subscribe(OnPhaseEnded)
# Called when the boss elimination trigger fires
# trigger_device.TriggeredEvent is listenable(?agent), so we receive ?agent
OnBossDefeated(Agent : ?agent) : void =
# Start the multi-phase storm
StormController.GenerateStorm()
# Called when the early-end trigger fires
OnEarlyEnd(Agent : ?agent) : void =
# Tear down the storm immediately
StormController.DestroyStorm()
# Called automatically each time a storm phase finishes closing
# PhaseEndedEvent is listenable(tuple()), so the handler takes no arguments
OnPhaseEnded() : void =
set PhasesCompleted = PhasesCompleted + 1
# Grant a reward to every player via the item granter
# (item_granter_device.GrantItem() grants to all players when no agent is specified
# — configure the granter's "Grant To" option to "All Players" in the editor)
PhaseRewardGranter.GrantItem()
Line-by-line explanation
| Lines | What's happening |
|---|---|
@editable StormController |
Exposes the device slot so you drag-and-drop the placed advanced_storm_controller_device in the editor. Without @editable the field is invisible to the editor and always null. |
BossTrigger.TriggeredEvent.Subscribe(OnBossDefeated) |
Wires the boss trigger's event to our handler. The handler signature must match listenable(?agent) — one ?agent param. |
StormController.PhaseEndedEvent.Subscribe(OnPhaseEnded) |
PhaseEndedEvent is listenable(tuple()) — the handler takes no arguments (an empty tuple). |
StormController.GenerateStorm() |
Starts the storm sequence from phase 1. Only works if Generate Storm On Game Start is No. |
StormController.DestroyStorm() |
Immediately removes the storm from the island — safe to call at any time. |
set PhasesCompleted = PhasesCompleted + 1 |
Verse requires set to mutate a var. Forgetting set is a compile error. |
Common patterns
Pattern 1 — Delayed storm start after a countdown
Use Sleep() to hold the storm for 60 seconds, giving players time to loot before the ring closes.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
countdown_storm_device := class(creative_device):
# Place your advanced_storm_controller_device and set
# "Generate Storm On Game Start" = No
@editable
StormController : advanced_storm_controller_device = advanced_storm_controller_device{}
OnBegin<override>()<suspends> : void =
# Give players 60 seconds of free-roam before the storm starts
Sleep(60.0)
StormController.GenerateStorm()
Pattern 2 — Destroy the storm when the last player reaches a goal zone
A capture_area_device signals when the final player captures the zone; the storm is immediately destroyed as a victory reward.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
victory_storm_device := class(creative_device):
@editable
StormController : advanced_storm_controller_device = advanced_storm_controller_device{}
# A capture area placed at the extraction/goal zone
@editable
GoalZone : capture_area_device = capture_area_device{}
OnBegin<override>()<suspends> : void =
# Storm is already running (Generate Storm On Game Start = Yes in editor)
# Wait for someone to capture the goal zone, then destroy the storm
GoalZone.TeamReachesTargetEvent.Subscribe(OnGoalCaptured)
OnGoalCaptured(TeamIndex : int) : void =
# Remove the storm — the safe zone is now the whole map
StormController.DestroyStorm()
Pattern 3 — React to every phase end to escalate difficulty
Each time a phase finishes closing, spawn an extra guard wave by triggering a guard_spawner_device.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
escalating_storm_device := class(creative_device):
@editable
StormController : advanced_storm_controller_device = advanced_storm_controller_device{}
# A guard spawner configured to spawn one wave per activation
@editable
GuardSpawner : guard_spawner_device = guard_spawner_device{}
var PhaseCount : int = 0
OnBegin<override>()<suspends> : void =
StormController.PhaseEndedEvent.Subscribe(OnPhaseEnded)
# Storm started via editor (Generate Storm On Game Start = Yes)
# PhaseEndedEvent is listenable(tuple()) — handler takes no params
OnPhaseEnded() : void =
set PhaseCount = PhaseCount + 1
# Spawn a guard wave after every phase
GuardSpawner.Spawn()
Gotchas
1. GenerateStorm is silently ignored if the editor option is wrong
If Generate Storm On Game Start is left as Yes, calling GenerateStorm() from Verse does nothing — the storm already started. Always set it to No when you want Verse to control the start.
2. PhaseEndedEvent is listenable(tuple()) — zero-argument handler
This trips up creators who expect an agent parameter. The handler must take no arguments:
# CORRECT
OnPhaseEnded() : void = ...
# WRONG — won't compile
OnPhaseEnded(A : agent) : void = ...
3. @editable is mandatory — bare identifiers fail
You cannot write advanced_storm_controller_device{}.GenerateStorm() inline. Every device reference must be an @editable field on your creative_device class and wired in the editor, or the runtime reference is invalid.
4. DestroyStorm is permanent for that round
Calling DestroyStorm() removes the storm entirely. There is no "pause" — if you want to restart it, call GenerateStorm() again (which restarts from phase 1). Plan your game flow accordingly.
5. Phase count is editor-only
The number of phases (up to 50) and each phase's size/speed/damage are configured in the device's Details panel and via advanced_storm_beacon_devices — you cannot set them from Verse at runtime. Verse only controls when the storm runs and reacts to phase transitions.
6. message vs string
If you ever pass text to a UI or notification device alongside storm events, remember Verse does not accept raw string where message is expected. Declare a localizes helper:
MyLabel<localizes>(S : string) : message = "{S}"
There is no StringToMessage function.