Overview
The supply_drop_spawner_device spawns an aerial supply crate — the classic balloon-and-parachute loot box — and lets you customize and control it from Verse. It solves a very common island-design problem: getting loot to players at a dramatic, controlled moment (after a king-of-the-hill capture, when a boss dies, at the start of a round, etc.) instead of relying on natural world spawns.
Reach for it when you want to:
- Spawn loot on cue — call
Spawn()(orSpawn(Agent)to set the owning team) from any game event. - Gate the loot —
Lock()andUnlock()decide whether players can open the crate. - React to the crate's lifecycle — subscribe to
LandingEvent,BalloonPoppedEvent,OpenedEvent, andDestroyCrateEventto drive scoring, VFX, or follow-up events. - Add drama —
DestroyBalloon()cuts the parachute so the crate freefalls, andOpen(Agent)force-opens it regardless of lock state. - Clean up —
DestroySpawnedDrops()removes any drops this device spawned.
Unlike the item spawner, this device gives you the whole crate experience: balloon, descent, landing, and the moment of opening — each one a hook you can script.
API Reference
supply_drop_spawner_device
Used to spawn and configure an aerial supply drop that can provide players with customized weapons/supplies.
Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).
supply_drop_spawner_device<public> := class<concrete><final>(creative_device_base):
Events (subscribe a handler to react):
| Event | Signature | Description |
|---|---|---|
BalloonPoppedEvent |
BalloonPoppedEvent<public>:listenable(?agent) |
Signaled when the balloon on the supply crate is popped. Sends the ?agent that popped the balloon. If no agent popped the balloon returns false. |
OpenedEvent |
OpenedEvent<public>:listenable(agent) |
Signaled when the supply crate is opened. Sends the agent that opened the crate. |
LandingEvent |
LandingEvent<public>:listenable(tuple()) |
Signaled when the supply crate lands for the first time. |
DestroyCrateEvent |
DestroyCrateEvent<public>:listenable(?agent) |
Signaled when the supply crate is destroyed. Sends the destroying agent. If no agent destroyed the crate then false is returned. |
Methods (call these to make the device act):
| Method | Signature | Description |
|---|---|---|
Spawn |
Spawn<public>(Agent:agent):void |
Spawns a supply drop provided one hasn't already spawned. Owning Team is set to Agent's team. |
Spawn |
Spawn<public>():void |
Spawns a supply drop provided one hasn't already spawned. |
DestroyBalloon |
DestroyBalloon<public>():void |
Destroys the balloon and causes the supply crate to freefall. |
Open |
Open<public>(Agent:agent):void |
Opens the supply crate, ignoring the locked or unlocked state. Agent acts as the instigator of the open action. |
Lock |
Lock<public>():void |
Locks the supply crate so agents cannot open it. |
Unlock |
Unlock<public>():void |
Unlocks the supply crate so agents can open it. |
DestroySpawnedDrops |
DestroySpawnedDrops<public>():void |
Destroys supply drops spawned by this device. |
Walkthrough
Let's build a "reward drop" scenario. When a player steps on a capture trigger, we spawn a supply drop for that player's team. The crate starts locked so nobody can grab it mid-air. The moment it lands, we unlock it. When a player opens it, we lock it again so it can't be looted twice, and we destroy the spawned drops to clear the area.
supply_drop_game := class(creative_device):
# Drag your Supply Drop Spawner device here in the Details panel.
@editable
DropSpawner : supply_drop_spawner_device = supply_drop_spawner_device{}
# A trigger the player steps on to call in the drop.
@editable
CaptureTrigger : trigger_device = trigger_device{}
OnBegin<override>()<suspends>:void =
# React to the player stepping on the capture trigger.
CaptureTrigger.TriggeredEvent.Subscribe(OnCaptured)
# React to the crate's lifecycle.
DropSpawner.LandingEvent.Subscribe(OnLanded)
DropSpawner.OpenedEvent.Subscribe(OnOpened)
DropSpawner.BalloonPoppedEvent.Subscribe(OnBalloonPopped)
# TriggeredEvent hands us a ?agent — unwrap it before use.
OnCaptured(MaybeAgent : ?agent) : void =
if (Player := MaybeAgent?):
# Spawn the drop owned by the capturing player's team.
DropSpawner.Spawn(Player)
# Lock it so it can't be opened while still in the air.
DropSpawner.Lock()
# LandingEvent carries an empty tuple() — no agent.
OnLanded() : void =
# Now that it's on the ground, let players open it.
DropSpawner.Unlock()
# OpenedEvent hands us a plain agent (already unwrapped).
OnOpened(Agent : agent) : void =
# Lock it again so it can't be re-opened, then clean up.
DropSpawner.Lock()
DropSpawner.DestroySpawnedDrops()
# BalloonPoppedEvent hands a ?agent (could be a stray bullet).
OnBalloonPopped(MaybeAgent : ?agent) : void =
if (Popper := MaybeAgent?):
# A player shot the balloon — let it freefall the rest of the way.
DropSpawner.DestroyBalloon()
Line by line:
- The two
@editablefields are how Verse reaches placed devices. You drop the real devices into these slots in the Details panel; a baresupply_drop_spawner_device{}default is just a placeholder until you assign one. OnBeginis where all subscriptions happen. EverySubscribecall wires one of the device's events to a method on this class.OnCapturedreceives a?agentbecauseTriggeredEventislistenable(?agent). We unwrap withif (Player := MaybeAgent?):before using it.DropSpawner.Spawn(Player)uses the team-aware overload so the crate belongs to the capturing player's team.OnLandedtakes no parameter —LandingEventislistenable(tuple()), an empty payload. WeUnlock()here so the crate is now openable.OnOpenedgets a plainagent(not optional) becauseOpenedEventislistenable(agent)— no unwrap needed. WeLock()andDestroySpawnedDrops()to prevent double-looting.OnBalloonPoppedgets a?agent; once a player pops the balloon weDestroyBalloon()for a fast freefall finish.
Common patterns
Force-open a crate from another event
Use Open(Agent) to crack the crate open regardless of lock state — handy when a separate "detonate" button should blow it open for whoever pressed it.
force_open_drop := class(creative_device):
@editable
DropSpawner : supply_drop_spawner_device = supply_drop_spawner_device{}
@editable
OpenButton : button_device = button_device{}
OnBegin<override>()<suspends>:void =
OpenButton.InteractedWithEvent.Subscribe(OnButtonPressed)
OnButtonPressed(Agent : agent) : void =
# Open ignores lock/unlock state; Agent is the instigator.
DropSpawner.Open(Agent)
React to the crate being destroyed and respawn it
DestroyCrateEvent fires when the crate is gone — a great place to award points and call in a fresh drop.
respawning_drop := class(creative_device):
@editable
DropSpawner : supply_drop_spawner_device = supply_drop_spawner_device{}
@editable
ScoreManager : score_manager_device = score_manager_device{}
OnBegin<override>()<suspends>:void =
DropSpawner.DestroyCrateEvent.Subscribe(OnCrateDestroyed)
# Kick off the first drop when the game starts.
DropSpawner.Spawn()
OnCrateDestroyed(MaybeAgent : ?agent) : void =
if (Destroyer := MaybeAgent?):
# Reward whoever destroyed the crate.
ScoreManager.Activate(Destroyer)
# Bring in a replacement drop.
DropSpawner.Spawn()
A timed drop on a delay
Spawn a no-team drop after a delay using Sleep, then clean up old drops first so only one exists.
timed_drop := class(creative_device):
@editable
DropSpawner : supply_drop_spawner_device = supply_drop_spawner_device{}
OnBegin<override>()<suspends>:void =
# Clear anything left over, then wait and spawn.
DropSpawner.DestroySpawnedDrops()
Sleep(30.0)
DropSpawner.Spawn()
DropSpawner.Unlock()
Gotchas
LandingEventhas no agent. It islistenable(tuple()), so its handler must take no parameters (OnLanded() : void). WritingOnLanded(A : agent)will not match the subscription signature.OpenedEventisagent, the others are?agent.OpenedEventhands you a ready-to-useagent.BalloonPoppedEventandDestroyCrateEventhand a?agentbecause a non-agent (a stray bullet, the environment) might have caused it — always unwrap withif (X := Maybe?):.Spawn()is no-op if a drop already exists. The device won't spawn a second crate while one is live. CallDestroySpawnedDrops()first if you want a guaranteed fresh drop.Open(Agent)ignores lock state.Lock()only blocks players from manually opening the crate —Open(Agent)will force it open anyway. Don't rely onLock()to stop a scriptedOpen.- You must declare the device as an
@editablefield. CallingDropSpawner.Spawn()only works becauseDropSpawneris a field referencing a placed device. A bare local of typesupply_drop_spawner_device{}will compile but do nothing in the world. - Two
Spawnoverloads.Spawn(Agent)sets the owning team to the agent's team;Spawn()spawns with no owning team. Pick deliberately based on whether team ownership matters for your loot rules.