Overview
A map in Verse is an immutable associative container that maps keys to values. You declare one with map{ Key => Value, … } and read from it with MyMap[Key] — a failable expression that succeeds when the key exists and fails (without crashing) when it doesn't. A var map can be reassigned entirely to add or remove entries.
Maps shine whenever you need to:
- Remember per-player state ("has this agent already looted chest 3?")
- Translate a trigger or zone ID into a human-readable name or reward tier
- Store configuration tables (delay times, score values) keyed by a string label
On our sun-drenched pirate dock the map is the ship's manifest: every trigger_device represents a chest on the dock, and the map records which player last opened each one so we can disable repeat triggers and fire a cinematic reveal only for the first looter.
API Reference
trigger_device
Used to relay events to other linked devices.
Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse). Inherited members are merged from trigger_base_device.
trigger_device<public> := class<concrete><final>(trigger_base_device):
Events (subscribe a handler to react):
| Event | Signature | Description |
|---|---|---|
TriggeredEvent |
TriggeredEvent<public>:listenable(?agent) |
Signaled when an agent triggers this device. Sends the agent that used this device. Returns false if no agent triggered the action (ex: it was triggered through code). |
Methods (call these to make the device act):
| Method | Signature | Description |
|---|---|---|
Trigger |
Trigger<public>(Agent:agent):void |
Triggers this device with Agent being passed as the agent that triggered the action. Use an agent reference when this device is setup to require one (for instance, you want to trigger the device only with a particular agent. |
Trigger |
Trigger<public>():void |
Triggers this device, causing it to activate its TriggeredEvent event. |
Enable |
Enable<public>():void |
Enables this device. |
Disable |
Disable<public>():void |
Disables this device. |
SetMaxTriggerCount |
SetMaxTriggerCount<public>(MaxCount:int):void |
Sets the maximum amount of times this device can trigger. * 0 can be used to indicate no limit on trigger count. * MaxCount is clamped between [0,20]. |
GetMaxTriggerCount |
GetMaxTriggerCount<public>()<transacts>:int |
Gets the maximum amount of times this device can trigger. * 0 indicates no limit on trigger count. |
GetTriggerCountRemaining |
GetTriggerCountRemaining<public>()<transacts>:int |
Returns the number of times that this device can still be triggered before hitting GetMaxTriggerCount. Returns 0 if GetMaxTriggerCount is unlimited. |
SetResetDelay |
SetResetDelay<public>(Time:float):void |
Sets the time (in seconds) after triggering, before the device can be triggered again (if MaxTrigger count allows). |
GetResetDelay |
GetResetDelay<public>()<transacts>:float |
Gets the time (in seconds) before the device can be triggered again (if MaxTrigger count allows). |
SetTransmitDelay |
SetTransmitDelay<public>(Time:float):void |
Sets the time (in seconds) which must pass after triggering, before this device informs other external devices that it has been triggered. |
GetTransmitDelay |
GetTransmitDelay<public>()<transacts>:float |
Gets the time (in seconds) which must pass after triggering, before this device informs other external devices that it has been triggered. |
player
Full public surface, resolved verbatim from the live Epic digest (Verse.digest.verse). Inherited members are merged from agent.
player<native><public> := class<unique><persistent><module_scoped_var_weak_map_key><epic_internal>(agent):
vector3
3-dimensional vector with
floatcomponents.
Full public surface, resolved verbatim from the live Epic digest (Verse.digest.verse).
vector3<native><public> := struct<concrete><computes><persistable><uht_comparable>:
Walkthrough
Scenario: Three treasure chests sit on a cel-shaded dock. Each chest is a trigger_device. When a player steps on a trigger the Verse device:
- Looks up whether that player already looted this chest.
- If not, records the player in the map, disables the trigger so no one else can loot it, and fires a cinematic.
- If yes, re-enables the trigger with a short reset delay (the chest respawns after 30 s).
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Native }
# Localized helper — trigger_device.Trigger() needs no message param,
# but we keep this around for any future UI text.
ChestLabel<localizes>(S : string) : message = "{S}"
pirate_dock_loot_manager := class(creative_device):
# ── Editable device references ──────────────────────────────────
@editable ChestA : trigger_device = trigger_device{}
@editable ChestB : trigger_device = trigger_device{}
@editable ChestC : trigger_device = trigger_device{}
@editable CinematicReveal : cinematic_sequence_device = cinematic_sequence_device{}
# ── Internal state ───────────────────────────────────────────────
# Maps a chest index (0/1/2) to the agent who looted it.
# We use ?agent so an empty chest slot is represented by false.
var LootedBy : [int]agent = map{}
# ── Lifecycle ────────────────────────────────────────────────────
OnBegin<override>()<suspends> : void =
# Each chest may only fire once before we manually re-enable it.
ChestA.SetMaxTriggerCount(1)
ChestB.SetMaxTriggerCount(1)
ChestC.SetMaxTriggerCount(1)
# Give each chest a 30-second respawn window.
ChestA.SetResetDelay(30.0)
ChestB.SetResetDelay(30.0)
ChestC.SetResetDelay(30.0)
# Subscribe handlers — each closure captures its chest index.
ChestA.TriggeredEvent.Subscribe(OnChestATriggered)
ChestB.TriggeredEvent.Subscribe(OnChestBTriggered)
ChestC.TriggeredEvent.Subscribe(OnChestCTriggered)
# ── Event handlers ───────────────────────────────────────────────
OnChestATriggered(MaybeAgent : ?agent) : void =
HandleChestLooted(0, MaybeAgent)
OnChestBTriggered(MaybeAgent : ?agent) : void =
HandleChestLooted(1, MaybeAgent)
OnChestCTriggered(MaybeAgent : ?agent) : void =
HandleChestLooted(2, MaybeAgent)
# ── Core logic ───────────────────────────────────────────────────
HandleChestLooted(ChestIndex : int, MaybeAgent : ?agent) : void =
# Unwrap the optional agent — TriggeredEvent sends ?agent.
if (Looter := MaybeAgent?):
# MAP LOOKUP: has this chest already been claimed?
if (LootedBy[ChestIndex]):
# Key exists → chest was already looted by someone.
# Re-enable with a fresh trigger count so it can respawn.
EnableChestByIndex(ChestIndex)
else:
# Key absent → first loot! Record the player.
set LootedBy = ConcatenateMaps(LootedBy, map{ ChestIndex => Looter })
# Play the cinematic reveal for this player.
CinematicReveal.Play(Looter)
# Disable the chest so it can't be triggered again
# until the reset delay expires.
DisableChestByIndex(ChestIndex)
EnableChestByIndex(Index : int) : void =
if (Index = 0):
ChestA.Enable()
ChestA.SetMaxTriggerCount(1)
else if (Index = 1):
ChestB.Enable()
ChestB.SetMaxTriggerCount(1)
else if (Index = 2):
ChestC.Enable()
ChestC.SetMaxTriggerCount(1)
DisableChestByIndex(Index : int) : void =
if (Index = 0):
ChestA.Disable()
else if (Index = 1):
ChestB.Disable()
else if (Index = 2):
ChestC.Disable()```
### Line-by-line highlights
| Lines | What's happening |
|---|---|
| `var LootedBy : [int]agent = map{}` | Declares a mutable map keyed by `int` (chest index) whose values are `agent`. Starts empty. |
| `ChestA.SetMaxTriggerCount(1)` | Limits the trigger to one fire before it needs to be manually re-enabled. |
| `ChestA.SetResetDelay(30.0)` | After the trigger fires, it waits 30 s before it can fire again (if re-enabled). |
| `ChestA.TriggeredEvent.Subscribe(OnChestATriggered)` | Registers our handler; the event sends `?agent`. |
| `if (Looter := MaybeAgent?)` | Unwraps `?agent` — mandatory before using it as `agent`. |
| `if (LootedBy[ChestIndex])` | **Map lookup** — succeeds when the key exists (chest already looted), fails silently when absent. |
| `set LootedBy = LootedBy.Concat(map{ ChestIndex => Looter })` | Immutable-map pattern: build a new map with the extra entry and reassign the `var`. |
| `CinematicReveal.Play(Looter)` | Fires the cinematic sequence for the specific player who opened the chest. |
## Common patterns
### Pattern 1 — Zone-name lookup: translate a trigger index into a dock location name
Use a `[int]string` map as a static config table. Look up the zone name when a trigger fires and use it to gate a score device.
```verse
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Native }
dock_zone_announcer := class(creative_device):
@editable ZoneTrigger : trigger_device = trigger_device{}
@editable ScoreDevice : score_manager_device = score_manager_device{}
# Static map: trigger index → zone label
ZoneNames : [int]string = map{
0 => "Crow's Nest",
1 => "Gangplank",
2 => "Lagoon Cove",
3 => "Clifftop Cannon"
}
OnBegin<override>()<suspends> : void =
ZoneTrigger.SetMaxTriggerCount(0) # 0 = unlimited triggers
ZoneTrigger.SetTransmitDelay(0.5) # half-second broadcast delay
ZoneTrigger.TriggeredEvent.Subscribe(OnZoneEntered)
OnZoneEntered(MaybeAgent : ?agent) : void =
if (A := MaybeAgent?):
# MAP LOOKUP — get the zone name for index 2 (Lagoon Cove)
if (ZoneName := ZoneNames[2]):
# Zone found — award points via score device
ScoreDevice.Activate(A)
# If the key were absent the if-block is simply skipped.
Pattern 2 — Delay config map: different reset delays per chest rarity
Store per-rarity reset delays in a [string]float map and apply them at runtime using SetResetDelay.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Native }
rarity_delay_configurator := class(creative_device):
@editable CommonChest : trigger_device = trigger_device{}
@editable RareChest : trigger_device = trigger_device{}
@editable LegendaryChest : trigger_device = trigger_device{}
# MAP: rarity string → respawn delay in seconds
RespawnDelays : [string]float = map{
"common" => 10.0,
"rare" => 20.0,
"legendary" => 45.0
}
OnBegin<override>()<suspends> : void =
# Apply delays from the map — lookup each rarity key.
if (D := RespawnDelays["common"]):
CommonChest.SetResetDelay(D)
CommonChest.SetMaxTriggerCount(0) # unlimited
if (D := RespawnDelays["rare"]):
RareChest.SetResetDelay(D)
RareChest.SetMaxTriggerCount(3) # only 3 loots per match
if (D := RespawnDelays["legendary"]):
LegendaryChest.SetResetDelay(D)
LegendaryChest.SetMaxTriggerCount(1) # one-time legendary
# Subscribe to legendary chest to fire a cinematic
LegendaryChest.TriggeredEvent.Subscribe(OnLegendaryLooted)
OnLegendaryLooted(MaybeAgent : ?agent) : void =
# Disable the legendary chest immediately after first loot
LegendaryChest.Disable()
Pattern 3 — Trigger-count inspector: read current map state and GetTriggerCountRemaining
Query the map to decide whether to re-enable a trigger, and use GetTriggerCountRemaining to confirm capacity before calling Trigger() programmatically.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Native }
chest_inspector := class(creative_device):
@editable WatchedChest : trigger_device = trigger_device{}
@editable ResetTrigger : trigger_device = trigger_device{}
# Tracks how many times each chest index has been looted this round.
var LootCount : [int]int = map{}
OnBegin<override>()<suspends> : void =
WatchedChest.SetMaxTriggerCount(5) # up to 5 loots
WatchedChest.SetResetDelay(5.0)
WatchedChest.TriggeredEvent.Subscribe(OnChestHit)
ResetTrigger.TriggeredEvent.Subscribe(OnResetRequested)
OnChestHit(MaybeAgent : ?agent) : void =
# Increment loot count for chest 0 in the map.
var NewCount : int = 1
if (Existing := LootCount[0]):
set NewCount = Existing + 1
set LootCount = LootCount.Concat(map{ 0 => NewCount })
OnResetRequested(MaybeAgent : ?agent) : void =
# Use GetTriggerCountRemaining to decide whether to fire programmatically.
Remaining := WatchedChest.GetTriggerCountRemaining()
if (Remaining > 0):
# Still has capacity — fire it via code (no agent needed here).
WatchedChest.Trigger()
else:
# Exhausted — re-enable and reset the count map.
WatchedChest.Enable()
WatchedChest.SetMaxTriggerCount(5)
set LootCount = map{}
Gotchas
1. Map lookup is failable — always wrap in if
MyMap[Key] is a failable expression. Calling it outside an if or if block is a compile error. Always write:
if (Value := MyMap[Key]):
# use Value
If the key is absent the block is simply skipped — no exception, no crash.
2. Maps are immutable — reassign the whole var to "add" entries
Verse maps are value types. You cannot mutate them in place. The idiomatic pattern is:
set MyMap = MyMap.Concat(map{ NewKey => NewValue })
If you forget var on the field declaration the compiler will refuse the set.
3. TriggeredEvent sends ?agent, not agent
The event signature is listenable(?agent). Your handler must accept ?agent and unwrap it:
OnTriggered(MaybeAgent : ?agent) : void =
if (A := MaybeAgent?):
# A is now a usable agent
Passing a handler typed (agent):void will not compile.
4. SetMaxTriggerCount is clamped to [0, 20]; 0 means unlimited
Passing 21 silently clamps to 20. Pass 0 when you want infinite triggers. Check the current cap with GetMaxTriggerCount() before logic that depends on it.
5. message vs string — localized text parameters
Any API that accepts message (e.g. future UI calls) will not accept a raw string. Declare a localizes helper:
MyLabel<localizes>(S : string) : message = "{S}"
There is no StringToMessage function in Verse.
6. int and float do not auto-convert
SetResetDelay takes float. Passing an int literal without .0 is a type error. Write 30.0, not 30.