Reference Devices

disguise_device: Spy Cosmetics That Apply and Break on Cue

The disguise_device cloaks a player in a cosmetic disguise — perfect for a spy-vs-spy infiltration mode where blending in with the crowd is your only defense. This article shows how to drive it from Verse: apply a disguise when a player steps on a plate, react when it gets blown by an enemy, and clean it up on demand.

Updated
The code on this reference page is provided as-is and did not pass the latest compile check — treat the examples as a starting point and verify in your project.
Watch the Knotdisguise_device in ~90 seconds.

Overview

The disguise_device applies a cosmetic disguise to a player. The actual disguise look — and the conditions that break it — are configured on the device in UEFN. From Verse, you control when the disguise goes on, when it comes off, and you react to it being applied, removed, or broken by an enemy.

Reach for this device when you want a hide-in-plain-sight game loop: an infiltration mode where blending into NPC crowds matters, a prop-hunt style round, or a stealth phase where taking damage blows your cover. The device exposes paired events: the non-Any variant fires only for disguises from this device, while the Any variant fires for disguises from any disguise device or disguise kit. Because it implements enableable, you can also Enable()/Disable() it to gate when disguising is allowed.

Key idea: ApplyDisguise and RemoveDisguise are methods you call, while ApplyDisguiseEvent, BreakDisguiseEvent, and RemoveDisguiseEvent are events you subscribe to to run game logic in response.

API Reference

disguise_device

Used to apply a cosmetic disguise to the player. The disguise to apply is defined on the device, as are the conditions for when the disguise breaks.

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

disguise_device<public> := class<concrete><final>(creative_device_base, enableable):

Events (subscribe a handler to react):

Event Signature Description
ApplyDisguiseEvent ApplyDisguiseEvent<public>:listenable(player) Signaled when a disguise from this device is successfully applied to player.
ApplyAnyDisguiseEvent ApplyAnyDisguiseEvent<public>:listenable(player) Signaled when any disguise is successfully applied to player. Includes disguises applied by any disguise device, or by a disguise kit.
BreakDisguiseEvent BreakDisguiseEvent<public>:listenable(tuple(player, ?agent)) Signaled when a disguise applied by this device on player is broken. The second optional agent describes who broke the disguise (the damage source if broken by damage or elimination). The conditions triggering a disguise to break are se
BreakAnyDisguiseEvent BreakAnyDisguiseEvent<public>:listenable(tuple(player, ?agent)) Signaled when any applied disguise on player is broken. Includes disguises applied by any disguise device, or by a disguise kit. The second optional agent describes who broke the disguise (the damage source if broken by damage or elimin
RemoveDisguiseEvent RemoveDisguiseEvent<public>:listenable(player) Signaled when a disguise applied by this device on player is removed. Removal can occur from calling the Remove Disguise function, or from getting replaced by another disguise. This event will not trigger if the disguise is broken.
RemoveAnyDisguiseEvent RemoveAnyDisguiseEvent<public>:listenable(player) Signaled when any applied disguise on player is removed. Includes disguises applied by any disguise device, or by a disguise kit. Removal can occur from calling the Remove Disguise function, or from getting replaced by another disguise.

Methods (call these to make the device act):

Method Signature Description
ApplyDisguise ApplyDisguise<public>(Player:player):void Applies the disguise to the provided player. If the provided player does not pass the device's filter settings, or if another disguise is already present and the device's Stomp Existing Disguise option is set to false, the disguise wi
RemoveDisguise RemoveDisguise<public>(Player:player):void Removes the disguise applied by this device, if it exists, from the provided player.
RemoveAnyDisguise RemoveAnyDisguise<public>(Player:player):void Removes any applied disguise from player. Includes disguises applied by any disguise device, or by a disguise kit.
IsDisguiseApplied IsDisguiseApplied<public>(Player:player)<transacts><decides>:void Succeeds if the provided player has a disguise applied from this device.
IsAnyDisguiseApplied IsAnyDisguiseApplied<public>(Player:player)<transacts><decides>:void Succeeds if the provided player has any disguise applied. Includes disguises applied by any disguise device, or by a disguise kit.

Walkthrough

Let's build a stealth phase. When a player steps on a trigger plate, they get disguised. While disguised they earn nothing special, but if an enemy shoots them their disguise breaks — and we punish the cover-blower by removing any disguise they might be wearing too. A button lets a player voluntarily drop their disguise.

using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporal }

# Stealth-phase disguise controller.
disguise_controller := class(creative_device):

    @editable
    DisguisePlate : trigger_device = trigger_device{}

    @editable
    DropButton : button_device = button_device{}

    @editable
    SpyDisguise : disguise_device = disguise_device{}

    MyText<localizes>(S : string) : message = "{S}"

    OnBegin<override>()<suspends> : void =
        # Step on the plate -> get disguised.
        DisguisePlate.TriggeredEvent.Subscribe(OnSteppedOnPlate)
        # Press the button -> drop your own disguise.
        DropButton.InteractedWithEvent.Subscribe(OnDropPressed)
        # React to the disguise lifecycle from THIS device.
        SpyDisguise.ApplyDisguiseEvent.Subscribe(OnDisguised)
        SpyDisguise.BreakDisguiseEvent.Subscribe(OnDisguiseBroken)
        SpyDisguise.RemoveDisguiseEvent.Subscribe(OnDisguiseRemoved)

    # trigger_device hands us (Agent : ?agent) — unwrap it.
    OnSteppedOnPlate(Agent : ?agent) : void =
        if (A := Agent?, Player := player[A]):
            # Don't double-apply if they're already disguised by us.
            if (not SpyDisguise.IsDisguiseApplied[Player]):
                SpyDisguise.ApplyDisguise(Player)

    # button_device hands us a plain agent.
    OnDropPressed(Agent : agent) : void =
        if (Player := player[Agent]):
            SpyDisguise.RemoveDisguise(Player)

    OnDisguised(Player : player) : void =
        Print("A player blended into the crowd.")

    # BreakDisguiseEvent carries (player, ?agent) — who broke it.
    OnDisguiseBroken(Result : tuple(player, ?agent)) : void =
        Victim := Result(0)
        Print("Cover blown!")
        # If we know who broke it, strip THEIR disguise as a penalty.
        if (Breaker := Result(1)?, BreakerPlayer := player[Breaker]):
            SpyDisguise.RemoveAnyDisguise(BreakerPlayer)

    OnDisguiseRemoved(Player : player) : void =
        Print("Disguise removed cleanly.")

Line by line:

  • The three @editable fields let you wire the placed trigger, button, and disguise device in the UEFN Details panel. Without these fields you cannot call the device's methods — a bare disguise_device.ApplyDisguise(...) fails with Unknown identifier.
  • MyText<localizes> is the pattern for building a message from a string; we keep it ready in case you want to show HUD text. There is no StringToMessage.
  • In OnBegin, we subscribe handlers to four events. Subscriptions belong here; the handlers are methods at class scope.
  • OnSteppedOnPlate receives ?agent from the trigger. We unwrap with if (A := Agent?, Player := player[A]): — the second clause casts the agent to a player, which is the type every disguise method wants.
  • IsDisguiseApplied[Player] is a <decides> query: it succeeds when this device already disguised the player. We negate it with not so we only apply when they're not already disguised.
  • OnDropPressed comes from the button, which hands a plain agent (not ?agent), so we cast straight to player and call RemoveDisguise.
  • OnDisguiseBroken gets a tuple(player, ?agent). Result(0) is the victim; Result(1) is the optional breaker. We unwrap the breaker and call RemoveAnyDisguise to strip whatever disguise they were hiding behind.

Common patterns

Pattern 1 — Check disguise state before granting a stealth reward. Use the <decides> query IsAnyDisguiseApplied to gate logic: only a currently-disguised player passes the checkpoint.

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

stealth_checkpoint := class(creative_device):

    @editable
    Checkpoint : trigger_device = trigger_device{}

    @editable
    Disguise : disguise_device = disguise_device{}

    @editable
    RewardGranter : item_granter_device = item_granter_device{}

    OnBegin<override>()<suspends> : void =
        Checkpoint.TriggeredEvent.Subscribe(OnReachCheckpoint)

    OnReachCheckpoint(Agent : ?agent) : void =
        if (A := Agent?, Player := player[A]):
            # Only disguised infiltrators may pass and grab the reward.
            if (Disguise.IsAnyDisguiseApplied[Player]):
                RewardGranter.GrantItem(A)
            else:
                Print("You must be disguised to pass.")

Pattern 2 — React to ANY disguise being applied, including disguise kits. Subscribe to ApplyAnyDisguiseEvent so your scoreboard counts disguises from every source, not just this device.

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

disguise_watcher := class(creative_device):

    @editable
    Disguise : disguise_device = disguise_device{}

    OnBegin<override>()<suspends> : void =
        # Fires for disguises from any device OR a disguise kit.
        Disguise.ApplyAnyDisguiseEvent.Subscribe(OnAnyDisguise)
        Disguise.RemoveAnyDisguiseEvent.Subscribe(OnAnyRemoved)

    OnAnyDisguise(Player : player) : void =
        Print("Someone, somehow, is now disguised.")

    OnAnyRemoved(Player : player) : void =
        Print("A disguise was removed (any source).")

Pattern 3 — Force-strip everyone's disguise at the end of a round. Iterate the players and call RemoveAnyDisguise so a fresh phase starts clean.

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

round_resetter := class(creative_device):

    @editable
    EndRoundButton : button_device = button_device{}

    @editable
    Disguise : disguise_device = disguise_device{}

    OnBegin<override>()<suspends> : void =
        EndRoundButton.InteractedWithEvent.Subscribe(OnEndRound)

    OnEndRound(Agent : agent) : void =
        # Strip disguises from every player in the match.
        for (Player : GetPlayspace().GetPlayers()):
            Disguise.RemoveAnyDisguise(Player)
        # Stop new disguises until next phase.
        Disguise.Disable()

Gotchas

  • ApplyDisguise can silently no-op. If the player fails the device's filter settings, or already has a disguise and Stomp Existing Disguise is off, nothing happens and no event fires. Don't assume the call succeeded — confirm with IsDisguiseApplied or react in ApplyDisguiseEvent.
  • Break is not the same as Remove. RemoveDisguiseEvent fires when you call RemoveDisguise or when a new disguise replaces it. It does not fire when the disguise is broken by damage — that's BreakDisguiseEvent. Handle them separately.
  • player vs agent. Every disguise method takes player. Triggers give you ?agent, buttons give you agent. Always cast with player[A] inside an if before calling — the cast can fail for non-player agents (like NPCs).
  • The break event's second element is optional. tuple(player, ?agent) — the ?agent is empty if the break wasn't caused by another agent (e.g., entering a no-disguise zone). Always unwrap with if (Breaker := Result(1)?): before using it.
  • IsDisguiseApplied / IsAnyDisguiseApplied are <decides>. Call them inside an if (...) with square brackets, never with parentheses as a statement. They succeed or fail; they don't return a logic value.
  • Disabling the device does NOT remove active disguises. Per the API, disguises already applied by this device stay on when you Disable() it. Call RemoveDisguise/RemoveAnyDisguise explicitly if you need a clean slate.
  • message needs localization. If you push disguise status to a HUD message device, build the text with a <localizes> helper like MyText(S:string):message = "{S}"; you cannot pass a raw string.

Guides & scripts that use disguise_device

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

Build your own lesson with disguise_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 →