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
@editablefields 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 baredisguise_device.ApplyDisguise(...)fails with Unknown identifier. MyText<localizes>is the pattern for building amessagefrom a string; we keep it ready in case you want to show HUD text. There is noStringToMessage.- In
OnBegin, we subscribe handlers to four events. Subscriptions belong here; the handlers are methods at class scope. OnSteppedOnPlatereceives?agentfrom the trigger. We unwrap withif (A := Agent?, Player := player[A]):— the second clause casts the agent to aplayer, 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 withnotso we only apply when they're not already disguised.OnDropPressedcomes from the button, which hands a plainagent(not?agent), so we cast straight toplayerand callRemoveDisguise.OnDisguiseBrokengets atuple(player, ?agent).Result(0)is the victim;Result(1)is the optional breaker. We unwrap the breaker and callRemoveAnyDisguiseto 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
ApplyDisguisecan 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 withIsDisguiseAppliedor react inApplyDisguiseEvent.- Break is not the same as Remove.
RemoveDisguiseEventfires when you callRemoveDisguiseor when a new disguise replaces it. It does not fire when the disguise is broken by damage — that'sBreakDisguiseEvent. Handle them separately. playervsagent. Every disguise method takesplayer. Triggers give you?agent, buttons give youagent. Always cast withplayer[A]inside anifbefore calling — the cast can fail for non-player agents (like NPCs).- The break event's second element is optional.
tuple(player, ?agent)— the?agentis empty if the break wasn't caused by another agent (e.g., entering a no-disguise zone). Always unwrap withif (Breaker := Result(1)?):before using it. IsDisguiseApplied/IsAnyDisguiseAppliedare<decides>. Call them inside anif (...)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. CallRemoveDisguise/RemoveAnyDisguiseexplicitly if you need a clean slate. messageneeds localization. If you push disguise status to a HUD message device, build the text with a<localizes>helper likeMyText(S:string):message = "{S}"; you cannot pass a raw string.