Overview
The campfire_device is a healing area-of-effect device. When lit, it periodically pulses health to any agent standing within its radius. Out of the box it's a self-contained prop, but wiring it to Verse unlocks a full event-driven API:
- React to proximity — know the moment a player steps into or out of the healing zone.
- React to pulses — fire custom logic every time the campfire ticks health, or specifically when a named player receives a heal.
- Control the fire — light it, extinguish it, and add wood to extend its burn time, all from code.
- Track state changes — know when the fire is lit, put out, enabled, or disabled.
When to reach for it: Survival games (fire = safety), escort missions (campfire checkpoints), wave-defense games (healing station between rounds), or any scenario where a spatial healing zone needs scripted behavior.
API Reference
campfire_device
Used to place a campfire in the world that an
agentcan use to heal themselves.
Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).
campfire_device<public> := class<concrete><final>(creative_device_base):
Events (subscribe a handler to react):
| Event | Signature | Description |
|---|---|---|
AgentEntersEffectAreaEvent |
AgentEntersEffectAreaEvent<public>:listenable(agent) |
Signaled when an agent enters the area of effect for this device. Sends the entering agent. |
AgentExitsEffectAreaEvent |
AgentExitsEffectAreaEvent<public>:listenable(agent) |
Signaled when an agent exits the area of effect for this device. Sends the exiting agent. |
CampfirePulseEvent |
CampfirePulseEvent<public>:listenable(tuple()) |
Signaled when this device generates a pulse. |
AgentPulsedEvent |
AgentPulsedEvent<public>:listenable(agent) |
Signaled when an agent is affected by a pulse generated by this device. Sends the affected agent. |
LitEvent |
LitEvent<public>:listenable(agent) |
Signaled when this device is lit by an agent. Sends the lighting agent. |
ExtinguishedEvent |
ExtinguishedEvent<public>:listenable(agent) |
Signaled when this device is extinguished by an agent. Sends the extinguishing agent. |
EnabledEvent |
EnabledEvent<public>:listenable(tuple()) |
Signaled when this device is enabled. |
DisabledEvent |
DisabledEvent<public>:listenable(tuple()) |
Signaled when this device is disabled. |
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. |
AddWood |
AddWood<public>():void |
Adds wood to this device. |
Light |
Light<public>(Agent:agent):void |
Lights this device. |
Extinguish |
Extinguish<public>(Agent:agent):void |
Extinguishes this device. |
Walkthrough
Scenario: A survival island has a central campfire. When a player enters the healing zone the campfire logs the arrival and adds extra wood so the fire burns longer. When the campfire is extinguished (by a storm mechanic or another player) a Verse device re-lights it after 10 seconds — keeping the safe zone alive but punishing players who let it die.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# Attach this device to a campfire_device in the UEFN level.
campfire_manager := class(creative_device):
# Wire this to your campfire_device in the Details panel.
@editable
Campfire : campfire_device = campfire_device{}
# How long (seconds) to wait before re-lighting after extinguish.
@editable
RelightDelay : float = 10.0
# We need a real agent to call Light/Extinguish — store the first
# player who enters so we always have one on hand.
var CachedAgent : ?agent = false
OnBegin<override>()<suspends> : void =
# Subscribe to all the events we care about.
Campfire.AgentEntersEffectAreaEvent.Subscribe(OnPlayerEnter)
Campfire.AgentExitsEffectAreaEvent.Subscribe(OnPlayerExit)
Campfire.AgentPulsedEvent.Subscribe(OnPlayerHealed)
Campfire.LitEvent.Subscribe(OnFireLit)
Campfire.ExtinguishedEvent.Subscribe(OnFireExtinguished)
# Make sure the fire starts lit by lighting it once we have
# an agent — we'll do that reactively in OnPlayerEnter.
# For now just ensure the device is enabled.
Campfire.Enable()
# Called when any agent walks into the campfire radius.
OnPlayerEnter(Agent : agent) : void =
# Cache the agent so we can call Light/Extinguish later.
set CachedAgent = option{Agent}
# Add wood every time someone enters — keeps the fire fuelled.
Campfire.AddWood()
# Called when any agent leaves the campfire radius.
OnPlayerExit(Agent : agent) : void =
# Nothing extra needed here — just a hook for your own logic.
# Example: you could start a timer to extinguish if nobody remains.
false
# Called each time the campfire pulse heals a specific agent.
OnPlayerHealed(Agent : agent) : void =
# You could award bonus score, play a sound via another device, etc.
false
# Called when the fire is successfully lit.
OnFireLit(Agent : agent) : void =
# Great place to trigger a VFX spawner or play a sound cue.
false
# Called when the fire goes out — start the relight timer.
OnFireExtinguished(Agent : agent) : void =
spawn { RelightAfterDelay() }
# Waits RelightDelay seconds then re-lights the campfire.
RelightAfterDelay()<suspends> : void =
Sleep(RelightDelay)
# We need a valid agent to call Light().
if (A := CachedAgent?):
Campfire.Light(A)
Line-by-line breakdown:
| Lines | What's happening |
|---|---|
@editable Campfire |
Exposes the campfire slot in the Details panel so you can drag-drop your placed device. |
@editable RelightDelay |
Designers can tune the relight window without touching code. |
var CachedAgent |
Light() and Extinguish() require an agent argument — we store the first entrant so we always have one. |
Campfire.Enable() |
Ensures the device is active when the game starts. |
Campfire.AgentEntersEffectAreaEvent.Subscribe(OnPlayerEnter) |
Hooks the enter event; handler signature must match (agent):void. |
Campfire.AddWood() |
Called on every entry — each call extends burn time. |
spawn { RelightAfterDelay() } |
Launches the relight coroutine without blocking the event handler. |
Sleep(RelightDelay) |
Suspends the coroutine for the configured delay. |
if (A := CachedAgent?): |
Safely unwraps the ?agent option before passing it to Light(). |
Common patterns
Pattern 1 — Disable the campfire between rounds and re-enable it fresh
In a wave-defense game you might want the campfire off during combat and on during the rest phase.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
campfire_round_controller := class(creative_device):
@editable
Campfire : campfire_device = campfire_device{}
# Wire a trigger or button device here to signal round end.
@editable
RoundEndTrigger : trigger_device = trigger_device{}
# Duration of the rest phase in seconds.
@editable
RestPhaseDuration : float = 30.0
OnBegin<override>()<suspends> : void =
# Campfire starts disabled — players haven't earned it yet.
Campfire.Disable()
RoundEndTrigger.TriggeredEvent.Subscribe(OnRoundEnd)
OnRoundEnd(Agent : ?agent) : void =
spawn { RunRestPhase() }
RunRestPhase()<suspends> : void =
# Enable the campfire and add a big wood boost for the rest phase.
Campfire.Enable()
Campfire.AddWood()
Campfire.AddWood()
Campfire.AddWood()
# Wait out the rest phase, then disable again.
Sleep(RestPhaseDuration)
Campfire.Disable()
Key calls: Disable(), Enable(), AddWood() — three distinct methods in one flow.
Pattern 2 — Track which players are currently inside the healing zone
Useful for a game mode that awards bonus points only to players who stay near the fire.
using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Verse.org/Simulation }
using { /Verse.org/Collections }
campfire_presence_tracker := class(creative_device):
@editable
Campfire : campfire_device = campfire_device{}
# Simple counter — how many agents are currently in the zone.
var AgentsInZone : int = 0
OnBegin<override>()<suspends> : void =
Campfire.AgentEntersEffectAreaEvent.Subscribe(OnEnter)
Campfire.AgentExitsEffectAreaEvent.Subscribe(OnExit)
Campfire.CampfirePulseEvent.Subscribe(OnPulse)
OnEnter(Agent : agent) : void =
set AgentsInZone = AgentsInZone + 1
OnExit(Agent : agent) : void =
if (AgentsInZone > 0):
set AgentsInZone = AgentsInZone - 1
# CampfirePulseEvent sends tuple() — no agent, just a tick signal.
OnPulse(T : tuple()) : void =
# Every pulse, if players are present, add more wood to keep it alive.
if (AgentsInZone > 0):
Campfire.AddWood()
Key calls: AgentEntersEffectAreaEvent, AgentExitsEffectAreaEvent, CampfirePulseEvent — note that CampfirePulseEvent is listenable(tuple()) so the handler takes (T : tuple()), not an agent.
Pattern 3 — Player-controlled extinguish and relight (interactive fire mechanic)
A player interacts with a button to toggle the campfire — useful for a puzzle where fire blocks a path.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
campfire_toggle_controller := class(creative_device):
@editable
Campfire : campfire_device = campfire_device{}
# A button_device the player presses to toggle the fire.
@editable
ToggleButton : button_device = button_device{}
var FireIsLit : logic = true
OnBegin<override>()<suspends> : void =
Campfire.LitEvent.Subscribe(OnLit)
Campfire.ExtinguishedEvent.Subscribe(OnExtinguished)
ToggleButton.InteractedWithEvent.Subscribe(OnButtonPressed)
OnLit(Agent : agent) : void =
set FireIsLit = true
OnExtinguished(Agent : agent) : void =
set FireIsLit = false
OnButtonPressed(Agent : ?agent) : void =
# Unwrap the optional agent before calling Light/Extinguish.
if (A := Agent?):
if (FireIsLit?):
Campfire.Extinguish(A)
else:
Campfire.Light(A)
Key calls: Light(Agent), Extinguish(Agent), LitEvent, ExtinguishedEvent — demonstrates the full fire-state toggle loop.
Gotchas
1. Light() and Extinguish() require an agent — always have one ready
Light(Agent:agent) and Extinguish(Agent:agent) are not zero-argument calls. You must pass a valid agent. The safest pattern is to cache the first agent from AgentEntersEffectAreaEvent in a var CachedAgent : ?agent field and unwrap it with if (A := CachedAgent?): before calling either method. Calling these before any agent has entered (and before you've cached one) means you have no agent to pass — plan for that case.
2. CampfirePulseEvent sends tuple(), not an agent
The global pulse event fires on a timer regardless of who is nearby. Its handler signature is (T : tuple()) : void, not (Agent : agent) : void. If you want per-agent pulse data, subscribe to AgentPulsedEvent instead, which does send the healed agent.
3. AgentEntersEffectAreaEvent and AgentExitsEffectAreaEvent send agent, not ?agent
These two events are listenable(agent) — the handler receives a concrete agent, no optional unwrap needed. Contrast this with events on other devices (like trigger_device.TriggeredEvent) that send ?agent and require the if (A := Agent?): unwrap pattern. Don't add unnecessary unwrapping here.
4. EnabledEvent and DisabledEvent send tuple()
Like CampfirePulseEvent, the enable/disable state events carry no payload. Their handlers must be (T : tuple()) : void. A common mistake is writing (Agent : agent) : void for these — that won't compile.
5. AddWood() is additive, not absolute
Each call to AddWood() adds one unit of wood on top of whatever is already in the campfire. There is no SetWood(amount) call. If you want a large fuel boost, call AddWood() multiple times in a loop or in sequence. Calling it once per player entry is a common and effective pattern.
6. The device must be @editable — you cannot construct it in code
campfire_device is <concrete><final> and must be placed in the UEFN level editor. Declare it as an @editable field in your creative_device class and drag-drop the placed actor into the slot in the Details panel. A bare campfire_device{} default is only a placeholder — it won't refer to your placed device unless wired up in the editor.
7. spawn {} for async relight logic inside synchronous event handlers
Event handlers (like OnFireExtinguished) are synchronous — they cannot Sleep() directly. To run a timed relight, use spawn { MyCoroutine() } inside the handler to launch a concurrent task that can call Sleep() and then Light().