Overview
The down_but_not_out_device customizes (or prevents) the down but not out player state — the middle ground between healthy and removed from game. In this state a player is crawling and helpless but can still be revived by a teammate before a timer expires.
Reach for this device when you are building cooperative experiences — a mountain-climbing rescue game, a co-op zombie defense, an extraction heist — where players should depend on each other instead of just respawning. With Verse you can:
- Force a player down on command (
Down) — great for traps, story beats, or penalties. - Instantly revive a downed player (
Revive) — e.g. when a medic reaches them or a team objective completes. - React to the whole lifecycle:
AgentDownedEvent,AgentPickedUpEvent,AgentDroppedEvent,AgentThrownEvent,AgentRevivedEvent, and the two shake-down events (ShakeDownEventfor the aggressor,ShakenDownEventfor the victim). - Enable / Disable the whole system per-phase.
The key mental model: the device doesn't cause downs by itself in your code — players get downed naturally by damage, and you can also drive it manually. Either way, the events fire and you wire up scoring, UI, and rescue logic.
API Reference
down_but_not_out_device
Used to customize (or prevent) the 'down but not out' player state between 'healthy' and 'removed from game'.
Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).
down_but_not_out_device<public> := class<concrete><final>(creative_device_base):
Events (subscribe a handler to react):
| Event | Signature | Description |
|---|---|---|
AgentDownedEvent |
AgentDownedEvent<public>:listenable(agent) |
Signaled when an agent is set to the down but not out player state. Sends the agent that was downed. |
AgentPickedUpEvent |
AgentPickedUpEvent<public>:listenable(agent) |
Signaled when an agent in the down but not out player state is picked up. Sends the agent that was picked up. |
AgentDroppedEvent |
AgentDroppedEvent<public>:listenable(agent) |
Signaled when an agent in the down but not out player state is dropped. Sends the agent that was dropped. |
AgentThrownEvent |
AgentThrownEvent<public>:listenable(agent) |
Signaled when an agent in the down but not out player state is thrown. Sends the agent that was thrown. |
AgentRevivedEvent |
AgentRevivedEvent<public>:listenable(agent) |
Signaled when an agent in the down but not out player state is revived. Sends the agent that was revived. |
ShakeDownEvent |
ShakeDownEvent<public>:listenable(agent) |
Signaled when an agent is the aggressor of a shake down. Sends the agent that is the aggressor. |
ShakenDownEvent |
ShakenDownEvent<public>:listenable(agent) |
Signaled when an agent is the victim of a shake down. Sends the agent that is the victim. |
Methods (call these to make the device act):
| Method | Signature | Description |
|---|---|---|
Enable |
Enable<public>():void |
Enables this device. |
Disable |
Disable<public>():void |
Disables this device. |
Down |
Down<public>(Agent:agent):void |
Sets the Agent to the down but not out player state. |
Revive |
Revive<public>(Agent:agent):void |
Sets the Agent to the healthy player state if they are in the down but not out player state. |
Walkthrough
Let's build a co-op rescue loop. When any player goes down, we light up a VFX beacon over the squad's rally point and bump a 'players down' counter on a progress mesh. When a teammate revives them, we clear the beacon. We also let players manually trigger a down for testing via a button-style flow, and reward the aggressor of a shake down with a score.
This example uses the device's real methods (Down, Revive, Enable) and subscribes to four of its real events.
using { /Fortnite.com }
using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# Co-op rescue manager: reacts to downs/revives and drives a beacon + progress meter.
rescue_manager_device := class(creative_device):
# The down but not out device placed in the level.
@editable
DBNO : down_but_not_out_device = down_but_not_out_device{}
# A VFX beacon we light up while at least one teammate is down.
@editable
RescueBeacon : vfx_spawner_device = vfx_spawner_device{}
# A progress mesh used as a visible 'downed players' meter.
@editable
DownedMeter : progress_based_mesh_device = progress_based_mesh_device{}
# How many players are currently in the downed state.
var DownedCount : int = 0
OnBegin<override>()<suspends>:void =
# Make sure the downed system is active for this round.
DBNO.Enable()
RescueBeacon.Disable()
# Subscribe handlers to the device's real events.
DBNO.AgentDownedEvent.Subscribe(OnAgentDowned)
DBNO.AgentRevivedEvent.Subscribe(OnAgentRevived)
DBNO.ShakeDownEvent.Subscribe(OnShakeDown)
DBNO.AgentThrownEvent.Subscribe(OnAgentThrown)
# Runs when ANY agent enters the downed state.
OnAgentDowned(Agent : agent) : void =
set DownedCount = DownedCount + 1
# Light the rescue beacon the moment the first player goes down.
if (DownedCount >= 1):
RescueBeacon.Enable()
RescueBeacon.Restart()
UpdateMeter()
# Runs when a downed agent is revived back to healthy.
OnAgentRevived(Agent : agent) : void =
set DownedCount = Max(0, DownedCount - 1)
# No more downed players? Turn the beacon off.
if (DownedCount <= 0):
RescueBeacon.Disable()
UpdateMeter()
# The aggressor of a shake down (a damaging interaction on a downed player).
OnShakeDown(Agent : agent) : void =
# Reward the aggressor by manually downing them as a 'karma' twist —
# purely a demo of calling Down() from an event.
DBNO.Down(Agent)
# A downed player was thrown — count them as freshly endangered.
OnAgentThrown(Agent : agent) : void =
UpdateMeter()
# Push the current downed count into the progress mesh (0..100).
UpdateMeter() : void =
# Scale: each downed player fills 25% of the meter, clamped.
Filled := Min(100, DownedCount * 25)
set DownedMeter.CurrentProgress = Filled * 1.0
Line by line:
- The three
@editablefields are how Verse gets a handle on placed devices. Without declaringDBNOas a field, callingDBNO.Enable()would fail with Unknown identifier. OnBeginruns once when the game starts. WeEnable()the downed system and start with the beacon off.Subscribewires each event to a method. Alistenable(agent)hands the handler anagentdirectly (not an?agent), so the parameter type isagent.OnAgentDownedincrements our own counter, lights the beacon viaEnable()/Restart(), and refreshes the meter.OnAgentReviveddecrements (clamped at 0 withMax) and disables the beacon when the squad is back up.OnShakeDowndemonstrates callingDown(Agent)from inside an event handler — the aggressor gets downed as a gameplay twist.UpdateMeterwrites afloatintoCurrentProgress; noteDownedCount * 25is anint, so we multiply by1.0to convert tofloat— Verse never auto-converts int↔float.
Common patterns
Pattern 1 — Force a player down with Down, then auto-rescue with Revive
A trap zone that knocks players down, then a friendly 'auto-medic' that revives them after a delay.
using { /Fortnite.com }
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
auto_medic_device := class(creative_device):
@editable
DBNO : down_but_not_out_device = down_but_not_out_device{}
# A trigger that, when stepped on, downs the player.
@editable
TrapTrigger : trigger_device = trigger_device{}
OnBegin<override>()<suspends>:void =
DBNO.Enable()
TrapTrigger.TriggeredEvent.Subscribe(OnTrapped)
OnTrapped(MaybeAgent : ?agent) : void =
# A trigger sends ?agent — unwrap before use.
if (Agent := MaybeAgent?):
DBNO.Down(Agent)
# Auto-rescue after a delay on a concurrent task.
spawn { RescueLater(Agent) }
RescueLater(Agent : agent)<suspends> : void =
Sleep(5.0)
DBNO.Revive(Agent)
Pattern 2 — Track pickups and drops to award a 'medic' score
React to AgentPickedUpEvent and AgentDroppedEvent to log when teammates carry downed players to safety.
using { /Fortnite.com }
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
carry_tracker_device := class(creative_device):
@editable
DBNO : down_but_not_out_device = down_but_not_out_device{}
# Counts how many downed players are currently being carried.
var BeingCarried : int = 0
OnBegin<override>()<suspends>:void =
DBNO.Enable()
DBNO.AgentPickedUpEvent.Subscribe(OnPickedUp)
DBNO.AgentDroppedEvent.Subscribe(OnDropped)
OnPickedUp(Agent : agent) : void =
set BeingCarried = BeingCarried + 1
OnDropped(Agent : agent) : void =
set BeingCarried = Max(0, BeingCarried - 1)
Pattern 3 — Punish the victim of a shake down with ShakenDownEvent
Use the victim-side event to disable the whole system briefly (e.g. a 'no revives' penalty window).
using { /Fortnite.com }
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
shakedown_penalty_device := class(creative_device):
@editable
DBNO : down_but_not_out_device = down_but_not_out_device{}
OnBegin<override>()<suspends>:void =
DBNO.Enable()
DBNO.ShakenDownEvent.Subscribe(OnShakenDown)
OnShakenDown(Victim : agent) : void =
# Briefly disable, then re-enable the downed system.
spawn { PenaltyWindow() }
PenaltyWindow()<suspends> : void =
DBNO.Disable()
Sleep(10.0)
DBNO.Enable()
Gotchas
- The events hand you a bare
agent, not?agent. Alistenable(agent)(like all the DBNO events) gives the handler(Agent : agent)directly — no unwrap needed. Only sources liketrigger_device.TriggeredEventsend?agent, which you unwrap withif (A := MaybeAgent?):. DownandRevivetake a plainagent. If you have aplayerorfort_character, pass the correspondingagent. You can check current state withfort_character.IsDownButNotOut[](from/Fortnite.com/Characters) — note it's a failable<decides>call, so use it inside anif.Reviveonly works on a downed agent. CallingReviveon a healthy player does nothing — it specifically sets a down but not out agent back to healthy.- int ↔ float never auto-converts. Writing into
CurrentProgress(afloat) from an int count requires an explicit conversion likeCount * 1.0. - You must declare the device as an
@editablefield and assign it in the editor's Details panel. A baredown_but_not_out_device{}literal is just a placeholder for the field default — the real linkage happens in UEFN. Disablestops the whole system, so any down/revive logic and events go quiet until youEnable()again. Re-enable before you expect the next down.- Localized text: if you later show a downed-player message via a
hud_message_device, themessageparam needs a<localizes>value — there is noStringToMessage.