Overview
The vending_machine_device solves the classic game-design problem of controlled item distribution. Without Verse you can place a vending machine and configure its items in the Details panel, but you cannot react to purchases, trigger the machine from code, or toggle it based on game state. Verse unlocks all of that.
Reach for this device when you need:
- A shop that only opens after players complete an objective (e.g., defeat a boss).
- An auto-dispenser that fires
SpawnItemthe moment a player steps on a pressure plate. - A rotating display that calls
CycleToNextItemon a timer to keep players guessing. - An event hook (
ItemSpawnedEvent) to award bonus XP, update a scoreboard, or trigger a cinematic every time someone grabs an item.
API Reference
vending_machine_device
Holds and spawns items, with an optional cost for each item. Can hold up to three items, and
agents can cycle between these by hitting the machine with their pickaxe.
Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).
vending_machine_device<public> := class<concrete><final>(creative_device_base):
Events (subscribe a handler to react):
| Event | Signature | Description |
|---|---|---|
ItemSpawnedEvent |
ItemSpawnedEvent<public>:listenable(agent) |
Signaled when an item is spawned from this device. Sends the agent that used this device. |
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. |
CycleToNextItem |
CycleToNextItem<public>():void |
Cycles the screen to show the next item. |
SpawnItem |
SpawnItem<public>():void |
Spawns an item. |
Walkthrough
Scenario: The Boss-Loot Vault
A vault vending machine is disabled at game start. When a player steps on a trigger plate (presumably after defeating a boss), the machine enables, automatically spawns a free legendary item, and then logs every subsequent pickup via ItemSpawnedEvent. A second trigger cycles the machine to the next item so latecomers get a different reward.
Place in your level:
- One
vending_machine_device(configure 2–3 items in its Details panel; set costs to 0 for a free vault) - One
trigger_devicewired to nothing — Verse handles it - One second
trigger_devicefor cycling
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# Localised message helper
boss_vault_pickup<localizes>(Name : string) : message = "Player {Name} grabbed loot from the Boss Vault!"
boss_vault_manager := class(creative_device):
# Wire this to your vending machine in the Details panel
@editable
Vault : vending_machine_device = vending_machine_device{}
# Trigger a player steps on after defeating the boss
@editable
BossDefeatedTrigger : trigger_device = trigger_device{}
# Trigger that cycles to the next item (e.g., a second plate nearby)
@editable
CycleTrigger : trigger_device = trigger_device{}
Logger : debug_draw = debug_draw{}
OnBegin<override>()<suspends> : void =
# Vault is locked until the boss is beaten
Vault.Disable()
# Subscribe to both triggers and to the pickup event
BossDefeatedTrigger.TriggeredEvent.Subscribe(OnBossDefeated)
CycleTrigger.TriggeredEvent.Subscribe(OnCycleRequested)
Vault.ItemSpawnedEvent.Subscribe(OnItemPickedUp)
# Called when the boss-defeated trigger fires
OnBossDefeated(Agent : ?agent) : void =
# Open the vault and immediately dispense the first item
Vault.Enable()
Vault.SpawnItem()
# Called when the cycle trigger fires — rotate to the next item
OnCycleRequested(Agent : ?agent) : void =
Vault.CycleToNextItem()
# Called every time a player picks up an item from the machine
OnItemPickedUp(Agent : agent) : void =
# ItemSpawnedEvent sends a concrete `agent`, not `?agent`
if (Player := player[Agent]):
Print("A player grabbed loot from the Boss Vault!")
else:
Print("An agent grabbed loot from the Boss Vault!")```
### Line-by-line explanation
| Lines | What's happening |
|---|---|
| `@editable Vault` | Exposes the vending machine slot in the Details panel so you can wire the placed device. |
| `Vault.Disable()` | Locks the machine at game start — players cannot interact with it. |
| `BossDefeatedTrigger.TriggeredEvent.Subscribe(OnBossDefeated)` | Registers our handler; the trigger fires when a player steps on the plate. |
| `Vault.ItemSpawnedEvent.Subscribe(OnItemPickedUp)` | Registers our pickup listener. Note: this event sends a concrete `agent`, **not** `?agent`. |
| `Vault.Enable()` + `Vault.SpawnItem()` | Opens the vault and immediately ejects the currently displayed item. |
| `Vault.CycleToNextItem()` | Advances the machine's display to the next configured item. |
| `if (Player := player[Agent])` | Safely casts `agent` to `player` — not all agents are players (NPCs count too). |
---
## Common patterns
### Pattern 1 — Timed auto-dispenser (SpawnItem on a loop)
A supply-drop machine that ejects an item every 30 seconds while the game is in its final phase.
```verse
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Simulation/Tags }
supply_drop_manager := class(creative_device):
@editable
SupplyMachine : vending_machine_device = vending_machine_device{}
# How many seconds between automatic drops
@editable
DropIntervalSeconds : float = 30.0
OnBegin<override>()<suspends> : void =
# Wait for the signal to start (e.g., another device enables this one)
# then loop forever, spawning an item every interval
loop:
Sleep(DropIntervalSeconds)
SupplyMachine.SpawnItem()
Key point: SpawnItem() ejects whatever item is currently displayed on the machine — pair it with CycleToNextItem() before the call if you want to rotate through all three slots.
Pattern 2 — Cycling display driven by a button
A shop kiosk where players press a button to browse items before committing to a purchase. Each button press advances the display by one slot.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
shop_browser := class(creative_device):
@editable
ShopMachine : vending_machine_device = vending_machine_device{}
# A button_device placed next to the vending machine
@editable
BrowseButton : button_device = button_device{}
OnBegin<override>()<suspends> : void =
BrowseButton.InteractedWithEvent.Subscribe(OnBrowsePressed)
OnBrowsePressed(Agent : agent) : void =
# Rotate the displayed item each time the button is pressed
ShopMachine.CycleToNextItem()
Pattern 3 — Pickup tracker using ItemSpawnedEvent
Count how many items the vending machine has dispensed this match and disable it after a cap is reached (e.g., only 5 free weapons per game).
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
limited_supply_manager := class(creative_device):
@editable
LimitedMachine : vending_machine_device = vending_machine_device{}
# Maximum items this machine may dispense per match
@editable
MaxDispenses : int = 5
var DispenseCount : int = 0
Logger : log = log{Channel := log_verse{}}
OnBegin<override>()<suspends> : void =
LimitedMachine.ItemSpawnedEvent.Subscribe(OnItemSpawned)
OnItemSpawned(Agent : agent) : void =
set DispenseCount = DispenseCount + 1
Logger.Print("Dispense {DispenseCount} of {MaxDispenses}")
if (DispenseCount >= MaxDispenses):
# Shut the machine down — supply exhausted
LimitedMachine.Disable()
Note: ItemSpawnedEvent sends a concrete agent (not ?agent), so no unwrapping is needed in the handler signature.
Gotchas
1. ItemSpawnedEvent sends agent, not ?agent
Unlike many Creative events (e.g., trigger_device.TriggeredEvent) which send ?agent, ItemSpawnedEvent delivers a concrete agent. Your handler must be OnItemSpawned(Agent : agent) : void, not (Agent : ?agent). Mixing these up causes a compile error.
2. SpawnItem() uses the currently displayed item
SpawnItem() ejects whichever item the machine's display is currently showing. If you want to dispense a specific slot, call CycleToNextItem() the right number of times first — there is no SpawnItemAtIndex() API.
3. Disabled machines ignore SpawnItem() silently
Calling SpawnItem() on a disabled machine does nothing and produces no error. Always call Enable() before SpawnItem() if you previously disabled the machine.
4. @editable fields are mandatory for placed devices
You cannot write vending_machine_device{}.SpawnItem() inline. Every placed device must be declared as an @editable field and wired in the Details panel. A bare device literal is a default-constructed object with no connection to the placed instance.
5. CycleToNextItem() wraps around
The machine cycles through its configured items (up to 3) and wraps back to slot 1 after the last. If you call it more times than there are items, you'll land back on a previous item — track the cycle count yourself if the displayed item matters.
6. No built-in way to read the current item
The API has no GetCurrentItem() or GetItemCount() method. If your logic depends on which item is displayed, maintain your own counter variable and sync it with your CycleToNextItem() calls.