Reference Verse compiles

Inheritance in Verse: Building a Pirate Island Enemy Hierarchy

Inheritance lets you write shared logic once in a parent class, then snap specialised behaviours onto child classes — no copy-paste required. On your sun-drenched pirate lagoon island, that means one `island_enemy` base class handles health and defeat logic while `deck_guard` and `crow_nest_sniper` each add their own twist. Pair that with `trigger_device` and `creature_spawner_device` and you have a living, breathing shoreline battle in minutes.

Updated Examples verified on the live UEFN compiler
Watch the Knottrigger_device in ~90 seconds.

Overview

Inheritance in Verse is the class(ParentClass) syntax. A child class automatically gains every field and method its parent declares, and can override <override> methods to change behaviour without touching the parent. This solves a classic game-dev problem: you have a family of enemies (guards on the dock, snipers in the crow's nest, captains on the ship) that all share health, activation, and defeat logic — but each needs a unique reaction when eliminated.

Reach for inheritance when:

  • Multiple game objects share a common lifecycle (spawn → active → defeated).
  • You want polymorphic dispatch — store a heterogeneous list of enemies and call the same method on all of them.
  • You need to extend a shared base without duplicating code across devices.

Verse enforces single class inheritance (one parent class, but multiple interfaces). The parent class must not be <final> for a child to extend it.

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.

creature_spawner_device

Used to spawn one or more waves of creatures of customizable types at selected time intervals.

Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).

creature_spawner_device<public> := class<concrete><final>(creative_device_base):

Events (subscribe a handler to react):

Event Signature Description
SpawnedEvent SpawnedEvent<public>:listenable(agent) Signaled when a creature is spawned. Sends the agent creature who was spawned.
EliminatedEvent EliminatedEvent<public>:listenable(device_ai_interaction_result) Signaled when a creature is eliminated. Source is the agent that has eliminated the creature. If the creature was eliminated by a non-agent then Source is 'false'. Target is the creature that was eliminated.

Methods (call these to make the device act):

Method Signature Description
SpawnAt SpawnAt<public>(Position:(/Verse.org/SpatialMath:)vector3, ?Rotation:?(/Verse.org/SpatialMath:)rotation Spawn a creature at the given position. When Rotation is not provided, it will default to the Device's rotation. Returns the agent spawned or false if the device has reached its maximum spawn count. This function is <suspends> because it
Enable Enable<public>():void Enables this device.
Disable Disable<public>():void Disables this device.
DestroySpawner DestroySpawner<public>():void Destroys this device.
EliminateCreatures EliminateCreatures<public>():void Eliminates all creatures spawned by this device.
GetSpawnLimit GetSpawnLimit<public>()<transacts>:int Returns the spawn limit of the device.

Walkthrough

Scenario: The Lagoon Ambush

Your cel-shaded pirate lagoon has two enemy types:

  • Dock Guard — spawns at the waterline, triggers an alarm bell when eliminated.
  • Crow's Nest Sniper — spawns on the clifftop mast, disables the alarm trigger after being defeated (the sniper was jamming signals).

Both share a base class that tracks how many enemies remain and calls OnDefeated when their spawner fires EliminatedEvent. The child classes override OnDefeated to do their unique thing.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/SpatialMath }

# lagoon_ambush_manager.verse
# Base enemy behaviour — shared by every enemy type on the lagoon.
island_enemy := class:
    # The spawner that owns this enemy type.
    var Spawner : creature_spawner_device = creature_spawner_device{}
    # The trigger this enemy type interacts with on defeat.
    var LinkedTrigger : trigger_device = trigger_device{}

    # Called when any creature from Spawner is eliminated.
    # Child classes override this to add specialised logic.
    OnDefeated(Result : device_ai_interaction_result) : void =
        # Base: just fire the linked trigger so other devices react.
        LinkedTrigger.Trigger()

# Dock Guard — fires the alarm bell trigger on defeat.
dock_guard := class(island_enemy):
    # Override: also set the trigger to fire only once (alarm rings once).
    OnDefeated<override>(Result : device_ai_interaction_result) : void =
        LinkedTrigger.SetMaxTriggerCount(1)
        LinkedTrigger.Trigger()   # ring the alarm bell

# Crow's Nest Sniper — disables the trigger on defeat (jammer goes offline).
crow_nest_sniper := class(island_enemy):
    OnDefeated<override>(Result : device_ai_interaction_result) : void =
        LinkedTrigger.Enable()    # re-enable the trigger the sniper was blocking
        LinkedTrigger.Trigger()   # fire a cinematic sequence trigger

# The creative_device that wires everything together.
lagoon_ambush_manager := class(creative_device):

    # ---- Editable device references ----
    @editable
    DockGuardSpawner : creature_spawner_device = creature_spawner_device{}

    @editable
    SniperSpawner : creature_spawner_device = creature_spawner_device{}

    @editable
    AlarmTrigger : trigger_device = trigger_device{}

    @editable
    CinematicTrigger : trigger_device = trigger_device{}

    # ---- Internal enemy instances (built from the base hierarchy) ----
    var Guard : dock_guard = dock_guard{}
    var Sniper : crow_nest_sniper = crow_nest_sniper{}

    OnBegin<override>()<suspends> : void =
        # Initialize internal enemy instances with the dragged devices.
        set Guard.Spawner = DockGuardSpawner
        set Guard.LinkedTrigger = AlarmTrigger
        set Sniper.Spawner = SniperSpawner
        set Sniper.LinkedTrigger = CinematicTrigger

        # Disable the cinematic trigger until the sniper is down.
        CinematicTrigger.Disable()
        # Give the alarm a reset delay so it can ring again if re-triggered.
        AlarmTrigger.SetResetDelay(5.0)

        # Subscribe both spawners — each routes to its own OnDefeated override.
        DockGuardSpawner.EliminatedEvent.Subscribe(OnGuardEliminated)
        SniperSpawner.EliminatedEvent.Subscribe(OnSniperEliminated)

        # Spawn the dock guard at the waterline (world units).
        spawn { SpawnGuardAsync() }

    SpawnGuardAsync()<suspends> : void =
        DockGuardSpawner.SpawnAt(Position := vector3{Forward := 1200.0, Left := 0.0, Up := 0.0})

    # Handler: delegate to the guard's overridden OnDefeated.
    OnGuardEliminated(Result : device_ai_interaction_result) : void =
        Guard.OnDefeated(Result)

    # Handler: delegate to the sniper's overridden OnDefeated.
    OnSniperEliminated(Result : device_ai_interaction_result) : void =
        Sniper.OnDefeated(Result)```

**Line-by-line explanation**

| Lines | What's happening |
|---|---|
| `island_enemy := class:` | Parent class — holds the two fields every enemy needs and a default `OnDefeated`. |
| `dock_guard := class(island_enemy):` | Child inherits both fields; `<override>` replaces `OnDefeated`. |
| `crow_nest_sniper := class(island_enemy):` | Second child, different override — enables then fires a cinematic trigger. |
| `lagoon_ambush_manager := class(creative_device):` | The device that owns `@editable` references and wires subscriptions. |
| `CinematicTrigger.Disable()` | Sniper is alive → cinematic is locked. Defeat the sniper → `Enable()` + `Trigger()` plays the cutscene. |
| `AlarmTrigger.SetResetDelay(5.0)` | Alarm can ring again 5 s after the first guard dies — useful if a second wave spawns. |
| `DockGuardSpawner.SpawnAt(...)` | Spawns the guard at a fixed lagoon coordinate; `<suspends>` requires a `spawn {}` block. |
| `Guard.OnDefeated(Result)` | Polymorphic call — Verse dispatches to `dock_guard`'s override automatically. |

## Common patterns

### Pattern 1 — Query trigger state before firing (GetMaxTriggerCount / GetTriggerCountRemaining)

Before ringing the alarm a second time, check whether the trigger has uses left.

```verse
lagoon_trigger_checker := class(creative_device):

    @editable
    AlarmTrigger : trigger_device = trigger_device{}

    OnBegin<override>()<suspends> : void =
        AlarmTrigger.SetMaxTriggerCount(3)   # alarm rings at most 3 times
        AlarmTrigger.SetResetDelay(4.0)

        # Simulate a second wave arriving after 10 s.
        Sleep(10.0)
        CheckAndRingAlarm()

    CheckAndRingAlarm() : void =
        Max := AlarmTrigger.GetMaxTriggerCount()
        Remaining := AlarmTrigger.GetTriggerCountRemaining()
        # Only ring if uses remain (Remaining > 0 means a cap is set AND uses are left).
        if (Remaining > 0):
            AlarmTrigger.Trigger()

Pattern 2 — React to a creature spawning (SpawnedEvent) and set transmit delay

When a pirate spawns on the dock, delay the trigger signal so the fog-horn sound plays first.

lagoon_spawn_reactor := class(creative_device):

    @editable
    DockSpawner : creature_spawner_device = creature_spawner_device{}

    @editable
    FogHornTrigger : trigger_device = trigger_device{}

    OnBegin<override>()<suspends> : void =
        # Fog horn fires 2 s after the trigger activates — gives audio time to start.
        FogHornTrigger.SetTransmitDelay(2.0)
        DockSpawner.SpawnedEvent.Subscribe(OnPirateSpawned)
        DockSpawner.Enable()

    OnPirateSpawned(SpawnedAgent : agent) : void =
        # Fire the fog horn trigger every time a new pirate appears on the dock.
        FogHornTrigger.Trigger()

Pattern 3 — Polymorphic enemy list: call OnDefeated on all enemies at once

Store a heterogeneous array of island_enemy and iterate — the correct override fires for each.

# Reuse the island_enemy / dock_guard / crow_nest_sniper classes from the Walkthrough.
lagoon_wave_manager := class(creative_device):

    @editable
    GuardSpawner : creature_spawner_device = creature_spawner_device{}

    @editable
    SniperSpawner : creature_spawner_device = creature_spawner_device{}

    @editable
    AlarmTrigger : trigger_device = trigger_device{}

    @editable
    CinematicTrigger : trigger_device = trigger_device{}

    # Polymorphic array — holds both child types under the parent type.
    var Enemies : []island_enemy = array{}

    OnBegin<override>()<suspends> : void =
        CinematicTrigger.Disable()

        Guard := dock_guard{ Spawner = GuardSpawner, LinkedTrigger = AlarmTrigger }
        Sniper := crow_nest_sniper{ Spawner = SniperSpawner, LinkedTrigger = CinematicTrigger }
        set Enemies = array{ Guard, Sniper }

        # Subscribe each spawner's EliminatedEvent.
        GuardSpawner.EliminatedEvent.Subscribe(OnAnyEnemyEliminated)
        SniperSpawner.EliminatedEvent.Subscribe(OnAnyEnemyEliminated)

        # Wipe the whole wave after 120 s if the player hasn't cleared it.
        Sleep(120.0)
        for (E : Enemies):
            E.Spawner.EliminateCreatures()

    # Single handler — iterates the array and calls each enemy's override.
    OnAnyEnemyEliminated(Result : device_ai_interaction_result) : void =
        for (E : Enemies):
            if (Result.Target = E.Spawner):  # match by spawner identity
                E.OnDefeated(Result)

Gotchas

1. Parent class must NOT be <final>

Built-in UEFN devices like trigger_device are declared <final> — you cannot subclass them. Inheritance is for your own Verse classes (island_enemy, game_round, etc.). Use @editable fields to hold device references inside your classes instead.

2. <override> is required — and the parent method must not be <final>

If you forget <override> on a child method that has the same name as a parent method, Verse will report a compile error. Conversely, if the parent marks the method <final>, no child can override it.

3. SpawnAt is <suspends> — wrap it

creature_spawner_device.SpawnAt(...) suspends the calling coroutine. You cannot call it directly in a synchronous method. Always wrap it in spawn { MyAsyncFunc() } or call it from an <override>()<suspends> function.

4. EliminatedEvent sends device_ai_interaction_result, not ?agent

Unlike trigger_device.TriggeredEvent which sends ?agent, the creature spawner's EliminatedEvent sends a device_ai_interaction_result struct. Access .Target for the creature and .Source (which may be false if a non-agent killed it) for the killer — no optional unwrap needed on the struct itself.

5. TriggeredEvent sends ?agent — always unwrap

trigger_device.TriggeredEvent is a listenable(?agent). Your handler receives (Agent : ?agent). Unwrap before use:

OnTriggered(Agent : ?agent) : void =
    if (A := Agent?):
        # use A as a concrete agent

Skipping the unwrap causes a type error at compile time.

6. SetMaxTriggerCount clamps to [0, 20]

Passing a value outside that range is silently clamped — 0 means unlimited. Don't expect SetMaxTriggerCount(999) to give you 999 triggers; you'll get 20.

7. Single inheritance only

Verse classes support one parent class. If you need shared behaviour from two sources, factor the second source into an interface and implement it alongside your single parent class.

Guides & scripts that use trigger_device

Step-by-step tutorials that put this object to work.

Build your own lesson with trigger_device

Generate a personalized, step-by-step lesson plan built around this object — grounded in this exact reference and our compile-verified knowledge base.

Build a lesson →