QUESTS API in Fortnite Creative/UEFN! (Experimental Tutorial)
This tutorial demonstrates how to build a complete Quest Management system using Fortnite's experimental Verse Quests API. The creator walks through importing assets, defining custom quest classes inheriting from basic_quest, implementing progress-tracking watchers for trigger and elimination events, and wiring up UI buttons to dynamically add, remove, and scale quests. The architecture evolves from a single-session manager to a per-player instance system with automatic cleanup on disconnect, covering playtime, trigger-based, and elimination quest variants.
This is VerseIsland's own written breakdown of a community tutorial video — see the credit section below for the original creator and link.
1. Intro, Asset Import & Module Setup (0:00–1:32)
In this part: How to prepare visual assets and scaffold the Verse device file with correct progression module imports.
- Import a square icon texture into the project; right-click > Texture Source Actions > Resize To Power Of Two (128x128 recommended).
- Create a new Verse Script using the Verse Device template, naming it quests_manager_device.
- Add module imports: /Verse.org/Progression, /UnrealEngine.com/Progression, /Verse.org/Assets, /Verse.org/Simulation, /UnrealEngine.com/Temporary/Diagnostics, /Fortnite.com/Devices.
2. Editable Configuration & Device Wiring (1:32–2:52)
In this part: Declaring @editable variables to expose devices and settings in the UEFN Details panel.
- Declare @editable arrays and single-device references: TriggerDevices ([]trigger_device), GoldGranter (item_granter_device), XPAccolade (accolades_device).
- Expose UI control devices: AddMyQuestButton, RemoveMyQuestButton, IncreaseRequiredCountButton, DecreaseRequiredCountButton (all button_device).
- Add float RequiredCountStep and quest_collection variable for runtime state management.
3. Custom Quest & Objective Class Definition (2:52–5:46)
In this part: Structuring a custom quest by inheriting from basic_quest and overriding metadata and objectives.
- Define my_quest<public> := class(basic_quest): and override Icon, Name, Description, ShortDescription.
- Assign localizable message variables (MyQuestName, MyQuestDescription, MyQuestShortDescription) to overrides.
- Create my_quest_objective<public> := class(progress_quest_objective): linked via Objective<override> in the parent class.
- Set RequiredCount (e.g., 5.0) and note entitlement_quest_reward structure for future reward mapping.
4. Progress Tracking Watcher Implementation (5:46–9:47)
In this part: Building async watcher coroutines to monitor device events and update quest progress safely.
- Implement Watch<public>(TriggerDevices, Collection, Quest)<suspends>:void= loop.
- Iterate TriggerDevices, spawning WatchDevice coroutine per trigger.
- Inside WatchDevice: loop awaiting TriggerDevice.TriggeredEvent.Await().
- Validate contributor status using IsContributor extension, then call SetProgress(Progress + 1.0).
5. Contributor Validation Extension (9:47–11:37)
In this part: Creating an extension method to safely verify if an agent contributed to a quest membership.
- Define (InAgent: agent).IsContributor<public>(Contributors : []quest_participant)<decides><reads>:void=.
- Iterate Contributors, cast to quest_participant, compare Participant.Agent to InAgent.
- Return matched participant or fail safely to prevent phantom progress updates.
6. Manager Initialization & Event Binding (11:37–13:12)
In this part: Instantiating quest objects in OnBegin and subscribing to UI and lifecycle events.
- Declare var MaybeMyQuest : ?my_quest = false.
- In OnBegin<override>(), instantiate MyQuestInstance := my_quest{} and wrap in option: set MaybeMyQuest = option{MyQuestInstance}.
- Subscribe InteractedWithEvent for all four buttons.
- Subscribe MyQuestInstance.CompleteEvent.Subscribe(OnMyQuestComplete).
7. Quest Completion & Reward Routing (13:12–14:32)
In this part: Handling quest completion by querying collections, extracting players, and dispatching rewards.
- Define OnMyQuestComplete(CompletedQuest : quest):void=.
- Query Memberships := Collection.Quests[CompletedQuest].
- Loop memberships, verify Membership.Info.Receives == true.
- Extract Player via agent_quest_participant[Membership.Participant] and player[Participant.Agent].
- Execute GoldGranter.GrantItem(Player) and XPAccolade.Award(Player).
8. Quest Join/Leave Logic & Participant Lookup (14:32–20:04)
In this part: Managing active participation states, preventing duplicates, and cleaning up abandoned quests.
- OnAddMyQuestButtonPressed extracts player, calls AddMyQuest(Player).
- AddMyQuest checks GetStoredParticipant; if absent, calls Collection.JoinQuest(MyQuest, Participant, quest_participant_info{}).
- Triggers objective tracking via spawn{Objective.Watch(TriggerDevices, Collection, MyQuest)}.
- RemoveMyQuest fetches stored participant and calls Collection.AbandonQuest(MyQuest, StoredParticipant).
- Implement GetStoredParticipant helper using .first expression to match Player.Agent.
9. Per-Player Instance Refactor & Cleanup (20:04–23:49)
In this part: Scaling architecture from single-instance to per-player maps with automatic disconnect cleanup.
- Replace MaybeMyQuest with PlayerQuests : [player]my_quest = map{}.
- Conditional instantiation: if player not in map, create new my_quest_instance, register in map, subscribe CompleteEvent locally.
- Build RemoveMapData utility: iterate map, filter target player key, reassign PlayerQuests.
- Subscribe GetPlayspace().PlayerRemovedEvent() in OnBegin to auto-invoke RemoveMapData on disconnect.
- Acknowledge session-only scope and lack of persistent lobby support [LIMITATION].
- Note that unremoved quests persist as non-functional ghost entries across sessions [LIMITATION].
- Mention anticipated integration with upcoming Discover navigation updates [FUTURE].
10. Variant Quests & Runtime Testing (26:44–30:22)
In this part: Adapting the core pattern for playtime and elimination quests, plus multi-quest routing.
- Playtime variant: infinite loop with Sleep(1.0), calling SetProgress(Progress + 1.0) until threshold met.
- Elimination variant: integrate elimination_manager_device, await EliminationEvent.Await(), validate contributor, update progress.
- Deploy separate maps for playtime/elimination tracking; subscribe unified OnQuestComplete handler.
- Runtime test: activate quests via touchpad, observe HUD tracker, verify concurrent progress and reward grants.
- Note that Epic's future persistence features may impact current architecture [FUTURE].
Code shown in the video
These are excerpts of the creator's OWN on-screen Verse code, captured from the video for reference — they are the creator's code, not VerseIsland's.
At 0:05 — compiled successfully
PlaytimeQuestName<message> : message = "Playtime Quest"
PlaytimeQuestDescription<message> : message = "Play for a certain amount of time!"
playtime_quest<public> := class(basic quest):
var Name<override> : message = PlaytimeQuestName
var Objective<override> : quest objective = playtime_quest_objective:
RequiredCount := 30.0
playtime_quest_objective := class(progress_quest_objective):
Sleep(1.0)
SetProgress(Progress + 1.0)
At 1:30 — compiled successfully
quests_manager_device.verse
wtwoboss@fortnite.com/VerseQuestsAPI > V quests_manager_device.verse > ...
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Verse.org/Progression }
using { /UnrealEngine.com/Progression }
quests_manager_device := class(creative_device):
OnBegin<override>()<suspends>:void=
()
At 1:39 — compiled successfully
quests_manager_device.verse using { /Verse.org/Progression } quests_manager_device := class(creative_device):
At 1:53 — compiled successfully
quests_manager_device.verse
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Verse.org/Progression }
using { /UnrealEngine.com/Progression }
using { /Verse.org/Assets }
quests_manager_device := class(creative_device):
@editable
TriggerDevices : []trigger_device = array{}
@editable
GoldGranter : item_granter_device = item_granter_device{}
@editable
XPAccolade : accolades_device = accolades_device{}
@editable
AddMyQuestButton : button_device = button_device{}
@editable
RemoveMyQuestButton : button_device = button_device{}
@editable
IncreaseRequiredCountButton : button_device = button_device{}
@editable
DecreaseRequiredCountButton : button_device = button_device{}
RequiredCountStep : float = 1.0
var Collection : quest_collection = quest_collection{}
At 2:37 — compiled successfully
quests_manager_device := class(creative_device): @editable AddMyQuestButton : button_device = button_device{} @editable RemoveMyQuestButton : button_device = button_device{} @editable IncreaseRequiredCountButton : button_device = button_device{} @editable DecreaseRequiredCountButton : button_device = button_device{} RequiredCountStep : float = 1.0 var Collection : quest_collection = quest_collection{} var MaybeMyQuest : ?my_quest = false OnBegin<override>()<suspends>:void= MyQuestInstance := my_quest{} set MaybeMyQuest = opti vote_option_device vote_option_device {CastVoteEvent:listenab... vote_option_interface reboot_card_purchase_options reboot_card_purchase_options {CostToPurcha... overlord_spire_device {ActivationDistance:... hero_chest_device {OpenEvent:listenable(?a... post_process_device {BlendingInCompleteEve... hive_stash_device {OpenEvent:listenable(?a... down_but_not_out_device {AgentDownedEvent:...}
At 3:17 — compiled successfully
entitlement_quest_reward progress_quest_objective GetProgress IsComplete SetProgress SetRequiredCount var Progress var RequiredCount basic_quest has_icon has_description
At 3:25 — compiled successfully
Assets<public> := module: has_icon<native><public> := interface: @editable var Icon<public>:texture Colors<public> := module: color<native><public> := struct<concrete><computes><persistable><uht_comparable>:
At 3:30 — could not be verified to compile as shown (on-screen capture may be partial)
Presentation<public> := module:
description_component<native><public> := class<final_super>(component, has_description):
var Name<override>:message = external {}
var Description<override>:message = external {}
var ShortDescription<override>:message = external {}
has_description<native><public> := interface:
@editable
# The display name of this entity.
var Name<public>:message
@editable
# The full description of this entity.
var Description<public>:message
@editable
# A shortened description of this entity.
var ShortDescription<public>:message
At 3:54 — compiled successfully
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
MyQuestName<localizes> : message = "My Quest"
MyQuestDescription<localizes> : message = "Walk on 5 triggers to complete!"
MyQuestShortDescription<localizes> : message = "Triggers Triggered"
my_quest<public> := class(basic_quest):
var Icon<override>
@editable
TriggerDevices :
@editable
GoldGranter : item_granter_device = item_granter_device{}
@editable
XPAccolade : accolades_device = accolades_device{}
@editable
AddMyQuestButton : button_device = button_device{}
@editable
RemoveMyQuestButton : button_device = button_device{}
At 4:00 — compiled successfully
quests_manager_device.verse, UnrealEngine.digest.verse, using { /Fortnite.com/Devices }, MyQuestName<localizes> : message = "My Quest", my_quest<public> := class(basic_quest):, var Icon<override> : texture = starIcon, SetRemainingInteractDurationForAgent
At 4:18 — compiled successfully
#TRIGGER QUEST
MyQuestName<localizes> : message = "My Quest"
MyQuestDescription<localizes> : message = "Walk on 5 triggers to complete!"
my_quest<public> := class(basic_quest):
var Objective<override> : quest_objective = my_quest_objective:
my_quest_objective := class(progress_quest_objective):
Watch<public>(TriggerDevices : []trigger_device, Collection : quest_collection, Quest : quest)<suspends>:void=
#PLAYTIME QUEST
At 4:30 — compiled successfully
quests_manager_device.verse, using { /Fortnite.com/Devices }, MyQuestName<localizes> : message = "My Quest", my_quest<public> := class(basic_quest):, var Icon<override> : texture = starIcon, var Name<override> : message = MyQuestName, var Description<override> : message = MyQuestDescription, var ShortDescription<override> : message = MyQuestShortDescription, var Objective<override> : quest, quests_manager_device := class(creative_device):, basic_quest, quest_reward, bank_vault_device, quest_objective
At 4:41 — compiled successfully
quests_manager_device.verse
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Verse.org/Progression }
using { /UnrealEngine.com/Progression }
using { /Verse.org/Assets }
MyQuestName<localizes> : message = "My Quest"
MyQuestDescription<localizes> : message = "Walk on 5 triggers to complete!"
MyQuestShortDescription<localizes> : message = "Triggers Triggered"
my_quest<public> := class(basic_quest):
var Icon<override> : texture = starIcon
var Name<override> : message = MyQuestName
var Description<override> : message = MyQuestDescription
var ShortDescription<override> : message = MyQuestShortDescription
var Objective<override> : quest_objective = my_quest_objective:
Icon := starIcon
Name := MyQuestName
Description := MyQuestDescription
ShortDescription := MyQuestShortDescription
RequiredCount := 5.0
quests_manager_device := class(creative_device):
At 5:29 — compiled successfully
sharpshooter_quest<public> := class(basic_quest):
var Objective<override>:quest_objective = progress_quest_objective:
Icon := MyIcon
Name := SharpshooterQuestName
Description := SharpshooterQuestDescription
ShortDescription := SharpshooterQuestShortDescription
RequiredCount := 50.0
var Rewards<override>:[]quest_reward = array:
entitlement_quest_reward:
Icon := MyIcon
Name := SharpshooterRewardName
Description := SharpshooterRewardDescription
ShortDescription := SharpshooterRewardShortDescription
Entitlement := SniperSkin
Quantity := 1
At 5:58 — compiled successfully
my_quest<public> := class(basic_quest):
var Objective<override> : quest_objective = my_quest_objective:
Name := MyQuestName
Description := MyQuestDescription
ShortDescription := MyQuestShortDescription
RequiredCount := 5.0
my_quest_objective := class(progress_quest_objective):
Watch<public>(TriggerDevices : []trigger_device, Collection : quest_collection, Quest : quest)<suspends>:void=
spawn { WatchDevice(TriggerDevice , Collection, Quest)}
WatchDevice(TriggerDevice : trigger_device, Collection : quest_collection, Quest : quest)<suspends>:void=
loop:
MaybeAgent := TriggerDevice.TriggeredEvent.Await()
Agent := MaybeAgent?
Memberships := Collection.Quests[Quest]
Contributors := GetContributors(Memberships)
Agent.IsContributor[Contributors]
then:
SetProgress(Progress + 1.0)
#PLAYTIME QUEST
PlaytimeQuestName<localizes> : message = "Playtime Quest"
PlaytimeQuestDescription<localizes> : message = "Play for a certain amount of time!"
PlaytimeQuestShortDescription<localizes> : message = "Time pased"
playtime_quest<public> := class(basic_quest):
var Icon<override> : texture = starIcon
var Name<override> : message = PlaytimeQuestName
var Description<override> : message = PlaytimeQuestDescription
var ShortDescription<override> : message = PlaytimeQuestShortDescription
var Objective<override> : quest_objective = playtime_quest_objective:
At 6:14 — compiled successfully
my_quest<public> := class(basic_quest):
var Icon<override> : texture = starIcon
var Name<override> : message = MyQuestName
var Description<override> : message = MyQuestDescription
var ShortDescription<override> : message = MyQuestShortDescription
var Objective<override> : quest_objective = my_quest_objective:
Icon := starIcon
Name := MyQuestName
Description := MyQuestDescription
ShortDescription := MyQuestShortDescription
RequiredCount := 5.0
my_quest_objective := class(progress_quest_objective):
Watch<public>(TriggerDevices : []trigger_device, )
quests_manager_device := class(creative_device):
@editable
TriggerDevices : []trigger_device = array{}
@editable
GoldGranter : item_granter_device = item_granter_device{}
@editable
XPAccolade : accolades_device = accolades_device{}
@editable
AddMyQuestButton : button_device = button_device{}
At 7:00 — compiled successfully
my_quest<public> := class(basic_quest):
var Icon<override> : texture = starIcon
my_quest_objective := class(progress_quest_objective):
Watch<public>(TriggerDevices : []trigger_device, Collection : quest_collection, Quest : quest)<suspends>:void=
MaybeAgent := TriggerDevice.TriggeredEvent.Await()
quests_manager_device := class(creative_device):
TriggerDevices : []trigger_device = array{}
At 7:30 — compiled successfully
my_quest_objective := class(progress_quest_objective):
Watch<public>(TriggerDevices : []trigger_device, Collection : quest_collection, Quest : quest)<suspends>:void=
for(TriggerDevice : TriggerDevices):
spawn { WatchDevice(TriggerDevice , Collection, Quest)}
WatchDevice(TriggerDevice : trigger_device, Collection : quest_collection, Quest : quest)<suspends>:void=
loop:
MaybeAgent := T
if-then
if-then-else
At 8:30 — compiled successfully
my_quest<object> : class(basic_quest): var Objective<override> : quest_objective = my_quest_objective: my_quest_objective : class(progress_quest_objective): Watch<public>(TriggerDevices : []trigger_device, Collection : quest_collection, Quest : quest)<suspends>:void= WatchDevice(TriggerDevice : trigger_device, Collection : quest_collection, Quest : quest)<suspends>:void= MaybeAgent := TriggerDevice.TriggeredEvent.Await() Agent := MaybeAgent? Memberships := Collection.Quests[Quest] Contributors := GetContributors(Memberships) Agent.IsContributor[Contributors] SetProgress(Progress + 1.0) quests_manager_device : class(creative_device): TriggerDevices : []trigger_device = array{}
At 8:35 — compiled successfully
my_quest<public> := class(basic_quest): var Objective<override> : quest_objective = my_quest_objective: ... my_quest_objective := class(progress_quest_objective): ... SetProgress(Progress + 1.0)
At 9:00 — compiled successfully
my_quest_objective := class(progress_quest_objective): WatchDevice(TriggerDevice : trigger_device, Collection : quest_collection, Quest : quest)<suspends>:void= loop: MaybeAgent := TriggerDevice.TriggeredEvent.Await() if: Agent := MaybeAgent? Memberships := Collection.Quests[Quest] Contributors := GetContributors(Memberships) Agent.IsContributor[Contributors] then: SetProgress(Progress + 1.0) (InAgent: agent).IsContributor<public>(Contributors : []quest_participant)<decides><reads>:void= first RemoveFirstElement[ElementToRemove:t] ReplaceFirstElement[ElementToReplace:t, El... gameplay_camera_first_person_device FindCreativeObjectsWithTag(__dupe__unname... @editable GoldGranter : item_granter_device = item_granter_device{} @editable XPAccolade : accolades_device = accolades_device{} @editable AddMyQuestButton : button_device = button_device{} @editable RemoveMyQuestButton : button_device = button_device{}
At 9:30 — compiled successfully
my_quest_objective := class(progress_quest_objective): WatchDevice(TriggerDevice : trigger_device, Collection : quest_collection, Quest : quest)<suspends>:void= ... (InAgent: agent).IsContributor<public>(Contributors : []quest_participant)<decides><reads>:void= ... Participant.Agent = InAgent ... quests_manager_device := class(creative_device): ... @editable TriggerDevices : []trigger_device = array{} ... @editable GoldGranter : item_granter_device = item_granter_device{} ... @editable XPAccolade : accolades_device = accolades_device{} ... @editable AddMyQuestButton : button_device = button_device{} ... @editable RemoveMyQuestButton : button_device = button_device{}
At 10:00 — compiled successfully
using { /Fortnite.com/Devices } using { /Verse.org/Simulation } ... MyQuestName<localizes> : message = "My Quest" ... my_quest<public> := class(basic quest): ... var Icon<override> : texture = starIcon ... my_quest_objective := class(progress_quest_objective): ... Watch<public>(TriggerDevices : []trigger_device, Collection : quest_collection, Quest : quest)<suspends>:void=
At 11:27 — compiled successfully
quests_manager_device := class(creative_device): TriggerDevices : []trigger_device = array{} GoldGranter : item_granter_device = item_granter_device{} XPAccolade : accolades_device = accolades_device{} AddMyQuestButton : button_device = button_device{} RemoveMyQuestButton : button_device = button_device{} IncreaseRequiredCountButton : button_device = button_device{} DecreaseRequiredCountButton : button_device = button_device{} RequiredCountStep : float = 1.0 var Collection : quest_collection = quest_collection{} var MaybeMyQuest : ?my_quest = false OnBegin<override>()<suspends>:void= AddMyQuestButton.InteractedWithEvent.Subscribe(OnAddMyQuestButtonPressed) RemoveMyQuestButton.InteractedWithEvent.Subscribe(OnRemoveMyQuestButtonPressed)
At 12:00 — compiled successfully
quests_manager_device : class(creative_device):
DecreaseRequiredCountButton : button_device = button_device{}
var Collection : quest_collection = quest_collection{}
OnBegin<override>()<suspends>:void=
MyQuestInstance := my_quest{}
AddMyQuestButton.InteractedWithEvent.Subscribe(OnAddMyQuestButtonPressed)
MyQuestInstance.CompleteEvent.Subscribe(OnMyQuestComplete)
OnMyQuestComplete( CompletedQuest : quest):void=
if(Memberships := Collection.Quests[CompletedQuest]):
At 12:30 — compiled successfully
quests_manager_device := class(creative_device): var Collection : quest_collection = quest_collection{} var MaybeMyQuest : ?my_quest = false OnBegin<override>()<suspends>:void= ... OnMyQuestComplete( CompletedQuest : quest):void= if(Memberships := Collection.Quests[CompletedQuest]): for: Membership : Memberships Membership.Info.Receives = true IsContributor[Contributors:[]quest_partic...
At 13:00 — compiled successfully
quests_manager_device := class(creative_device):
var Collection : quest_collection = quest_collection{}
var MaybeMyQuest : ?my_quest = false
OnBegin<override>()<suspends>:void=
MyQuestInstance := my_quest{}
set MaybeMyQuest = option{MyQuestInstance}
AddMyQuestButton.InteractedWithEvent.Subscribe(OnAddMyQuestButtonPressed)
RemoveMyQuestButton.InteractedWithEvent.Subscribe(OnRemoveMyQuestButtonPressed)
IncreaseRequiredCountButton.InteractedWithEvent.Subscribe(OnIncreaseRequiredCount)
DecreaseRequiredCountButton.InteractedWithEvent.Subscribe(OnDecreaseRequiredCount)
MyQuestInstance.CompleteEvent.Subscribe(OnMyQuestComplete)
OnMyQuestComplete( CompletedQuest : quest):void=
if(Memberships := Collection.Quests[CompletedQuest]):
for:
Membership : Memberships
Membership.Info.Receives = true
Participant := agent_quest_participant[Membership.Participant]
Player := player[Participant.Agent]
do
GoldGranter.GrantItem(Player)
XPAccolade.Award
Award(Agent:agent)
TestAwardEvent
At 13:03 — compiled successfully
quests_manager_device := class(creative_device): AddMyQuestButton : button_device = button_device{} RemoveMyQuestButton : button_device = button_device{} OnBegin<override>()<suspends>:void= OnMyQuestComplete( CompletedQuest : quest):void=
At 13:23 — compiled successfully
quests_manager_device := class(creative_device):
OnMyQuestComplete( CompletedQuest : quest):void=
OnAddMyQuestButtonPressed(Agent : agent):void=
if(Player := player[Agent]):
AddMyQuest(Player)
AddMyQuest(Player : player):void=
if(MyQuest := MaybeMyQuest?):
if(not GetStoredParticipant[Player, MyQuest]):
Participant := agent quest participant{Agent := Player}
Collection.JoinQuest( MyQuest, Participant, quest_participant_info{} )
if(Objective := my_quest_objective[MyQuest.Objective]):
spawn[Objective.Watch(TriggerDevices, Collection, MyQuest)]
OnRemoveMyQuestButtonPressed(Agent : agent):void=
if(Player := player[Agent]):
RemoveMyQuest(Player)
RemoveMyQuest(Player : player):void=
if(MyQuest := MaybeMyQuest?):
if(StoredParticipant := GetStoredParticipant[Player, MyQuest]):
Collection.AbandonQuest(MyQuest, StoredParticipant)
GetStoredParticipant(Player:player, Quest:quest)<decides><reads>:agent_quest_participant=
Memberships := Collection.Quests[Quest]
first:
Membership : Memberships
Membership.Info.Contributes = true
Participant := agent quest participant[Membership.Participant]
Participant.Agent = Player
do { Participant }
At 13:30 — compiled successfully
howtwoboss@fortnite.com/VerseQuestsAPI > V quests_manager_device.verse > quests_manager_device
quests_manager_device := class(creative_device):
OnBegin<override>()<suspends>:void=
MyQuestInstance.CompleteEvent.Subscribe(OnMyQuestComplete)
OnMyQuestComplete(CompletedQuest : quest):void=
if(Memberships := Collection.Quests[CompletedQuest]):
for:
Membership : Memberships
Membership.Info.Receives = true
Participant := agent_quest_participant[Membership.Participant]
Player := player[Participant.Agent]
do:
GoldGranter.GrantItem(Player)
XPAccolade.Award(Player)
OnAddMyQuestButtonPressed(Agent : agent):void=
if(Player := player[Agent]):
AddMyQuest(Player)
OnRemoveMyQuestButtonPressed(Agent : agent):void=
if(Player := player[Agent]):
RemoveMyQuest(Player)
At 14:16 — compiled successfully
quests_manager_device.verse DecreaseRequiredCountButton.InteractedWithEvent.Subscribe(OnDecreaseRequiredCount) MyQuestInstance.CompleteEvent.Subscribe(OnMyQuestComplete) OnMyQuestComplete( CompletedQuest : quest):void= if(Memberships := Collection.Quests[CompletedQuest]): GoldGranter.GrantItem(Player) XPAccolade.Award(Player) OnAddMyQuestButtonPressed(Agent : agent):void= AddMyQuest(Player) AddMyQuest(Player : player):void= Collection.JoinQuest( MyQuest, Participant, quest_participant_info{} ) if(Objective := my_quest_objective[MyQuest.Objective]):
At 15:18 — compiled successfully
quests_manager_device := class(creative_device): OnMyQuestComplete( CompletedQuest : quest):void= ... AddMyQuest(Player : player):void= ... if(MyQuest := MaybeMyQuest?): ... if(Objective := my_quest_objective[MyQuest.Objective]): ... spawn{Objective.Watch(TriggerDevices, Collection, MyQuest)} ... MyQuest: my_quest
At 15:43 — compiled successfully
/howtwoboss@fortnite.com/VerseQuestsAPI > V quests_manager_device.verse
110 OnAddMyQuestButtonPressed(Agent : agent):void=
111 if(Player := player[Agent]):
112 AddMyQuest(Player)
114 AddMyQuest(Player : player):void=
115 if(MyQuest := MaybeMyQuest?):
116 if(not GetStoredParticipant[Player, MyQuest]):
117 Participant := agent_quest_participant{Agent := Player}
119 Collection.JoinQuest( MyQuest, Participant, quest_participant_info{} )
125 OnRemoveMyQuestButtonPressed(Agent : agent):void=
126 if(Player := player[Agent]):
127 RemoveMyQuest(Player)
129 RemoveMyQuest(Player : player):void=
130 if(MyQuest := MaybeMyQuest?):
131 if{StoredParticipant }
136 GetStoredParticipant(Player:player, Quest:quest)<decides><reads>:agent_quest_participant=
137 Memberships := Collection.Quests[Quest]
At 16:00 — compiled successfully
quests_manager_device ::= class(creative_device): OnMyQuestComplete ... GoldGranter.GrantItem(Player) OnAddMyQuestButtonPressed ... AddMyQuest OnRemoveMyQuestButtonPressed ... RemoveMyQuest GetStoredParticipant
At 16:14 — compiled successfully
quests_manager_device := class(creative_device): @editable IncreaseRequiredCountButton : button_device = button_device{} @editable DecreaseRequiredCountButton : button_device = button_device{} RequiredCountStep : float = 1.0 var Collection : quest_collection = quest_collection{} var MaybeMyQuest : ?my_quest = false OnBegin<override>()<suspends>:void= MyQuestInstance := my_quest{} set MaybeMyQuest = option{MyQuestInstance} AddMyQuestButton.InteractedWithEvent.Subscribe(OnAddMyQuestButtonPressed) RemoveMyQuestButton.InteractedWithEvent.Subscribe(OnRemoveMyQuestButtonPressed) IncreaseRequiredCountButton.InteractedWithEvent.Subscribe(OnIncreaseRequiredCount) DecreaseRequiredCountButton.InteractedWithEvent.Subscribe(OnDecreaseRequiredCount) MyQuestInstance.CompleteEvent.Subscribe(OnMyQuestComplete) OnMyQuestComplete( CompletedQuest : quest):void= if(Memberships := Collection.Quests[CompletedQuest]): for: Membership : Memberships Membership.Info.Receives = true Participant := agent quest_participant[Membership.Participant] Player := player[Participant.Agent] do: GoldGranter.GrantItem(Player) XPAccolade.Award(Player)
At 16:27 — compiled successfully
quests_manager_device := class(creative_device):
AddMyQuest(Player : player):void=
OnRemoveMyQuestButtonPressed(Agent : agent):void=
RemoveMyQuest(Player : player):void=
OnIncreaseRequiredCount(Agent : agent):void=
if(MyQuest := MaybeMyQuest?):
if(MyQuestObjective := my_quest_objective[MyQuest.Objective]):
MyQuestObjective.SetRequiredCount(MyQuestObjective.RequiredCount + RequiredCountStep )
OnDecreaseRequiredCount(Agent : agent):void=
GetStoredParticipant(Player:player, Quest:quest)<decides><reads>:agent_quest_participant=
Memberships := Collection.Quests[Quest]
At 16:30 — compiled successfully
quests_manager_device := class(creative_device):
OnIncreaseRequiredCount(Agent : agent):void=
if(MyQuest := MaybeMyQuest?):
MyQuestObjective.SetRequiredCount(MyQuestObjective.RequiredCount + RequiredCountStep)
(/UnrealEngine.com/Progression/progress_quest_objective:)SetRequiredCount<final><native><public>(Amount:float)
At 17:00 — compiled successfully
quests_manager_device := class(creative_device): OnRemoveMyQuestButtonPressed(Agent : agent):void= RemoveMyQuest(Player : player):void= OnIncreaseRequiredCount(Agent : agent):void= OnDecreaseRequiredCount(Agent : agent):void= MyQuestObjective.SetRequiredCount(MyQuestObjective.RequiredCount - RequiredCountStep ) GetStoredParticipant(Player:player, Quest:quest)<decides><reads>:agent_quest_participant=
At 20:34 — compiled successfully
quests_manager_device := class(creative_device): @editable GoldGranter : item_granter_device = item_granter_device{} @editable XPAccolade : accolades_device = accolades_device{} @editable AddMyQuestButton : button_device = button_device{} var MaybeMyQuest : ?my_quest = [false OnBegin<override>()<suspends>:void= MyQuestInstance := my_quest{} set MaybeMyQuest = option{MyQuestInstance} AddMyQuestButton.InteractedWithEvent.Subscribe(OnAddMyQuestButtonPressed) OnMyQuestComplete( CompletedQuest : quest):void=
At 21:00 — compiled successfully
quests_manager_device.verse
OnMyQuestComplete( CompletedQuest : quest):void=
if(Memberships := Collection.Quests[CompletedQuest]):
for:
Membership : Memberships
Membership.Info.Receives = true
Participant := agent_quest_participant[Membership.Participant]
Player := player[Participant.Agent]
do:
GoldGranter.GrantItem(Player)
XPAccolade.Award(Player)
OnAddMyQuestButtonPressed(Agent : agent):void=
if(Player := player[Agent]):
AddMyQuest(Player)
AddMyQuest(Player : player):void=
if(not PlayerQuests[Player])
if(not GetStoredParticipant[Player, MyQuest]):
Participant := agent_quest_participant{Agent := Player}
Collection.JoinQuest( MyQuest, Participant, quest_participant_info{} )
if(Objective := my_quest_objective[MyQuest.Objective]):
spawn{Objective.Watch(TriggerDevices, Collection, MyQuest)}
OnRemoveMyQuestButtonPressed(Agent : agent):void=
RemoveMyQuest(Player : player):void=
if(MyQuest := MaybeMyQuest?):
if(StoredParticipant := GetStoredParticipant[Player, MyQuest]):
Collection.AbandonQuest(MyQuest, StoredParticipant)
At 21:24 — compiled successfully
quests_manager_device := class(creative_device):
OnMyQuestComplete( CompletedQuest : quest):void=
AddMyQuest(Player : player):void=
if(set PlayerQuests[Player] = MyQuestInstance):
MyQuestInstance.CompleteEvent.Subscribe(OnMyQuestComplete)
At 21:30 — compiled successfully
quests_manager_device := class(creative_device):
OnMyQuestComplete( CompletedQuest : quest):void=
OnAddMyQuestButtonPressed(Agent : agent):void=
AddMyQuest(Player : player):void=
if(not PlayerQuests[Player]):
MyQuestInstance := my_quest{}
At 22:00 — compiled successfully
quests_manager_device.verse > quests_manager_device > RemoveMyQuest, quests_manager_device := class(creative_device):, AddMyQuest(Player : player):void=, OnRemoveMyQuestButtonPressed(Agent : agent):void=, RemoveMyQuest(Player : player):void=, if(MyQuest := PlayerQuests[Player]):, if(StoredParticipant := GetStoredParticipant[Player, MyQuest]):, Collection.AbandonQuest(MyQuest, StoredParticipant), RemoveMyQuest(Player:player), OnIncreaseRequiredCount(Agent : agent):void=, GetStoredParticipant(Player:player, Quest:quest)<decides><reads>:agent_quest_participant=
At 22:04 — compiled successfully
quests_manager_device.verse... quests_manager_device := class(creative_device):... AddMyQuest(Player : player):void=... OnRemoveMyQuestButtonPressed(Agent : agent):void=... RemoveMyQuest(Player : player):void=... RemoveMapData(Player)... GetStoredParticipant(Player:player, Quest:quest)<decides><reads>:agent_quest_participant=
At 22:24 — compiled successfully
quests_manager_device.verse class(creative_device): OnDecreaseRequiredCount(Agent : agent):void= GetStoredParticipant(Player:player, Quest:quest)<decides><reads>:agent_quest_participant= RemoveMapData(Player : player):void= var NewMap : [player]my_quest = map{} for(Key -> Value : PlayerQuests, Key <> Player): if(set NewMap[Key] = Value): set PlayerQuests = NewMap
At 23:00 — compiled successfully
quests_manager_device.verse | quests_manager_device.verse | UnrealEngine.digest.verse | quests_manager_device := class(creative_device): | @editable | IncreaseRequiredCountButton : button_device = button_device{} | DecreaseRequiredCountButton : button_device = button_device{} | RequiredCountStep : float = 1.0 | var Collection : quest_collection = quest_collection{} | var PlayerQuests : [player]my_quest = map{} | OnBegin<override>()<suspends>:void= | GetPlayspace().PlayerRemovedEvent().Subscribe(RemoveMapData)
At 23:30 — compiled successfully
quests_manager_device := class(creative_device): OnBegin<override>()<suspends>:void= OnMyQuestComplete( CompletedQuest : quest):void= GoldGranter.GrantItem(Player) AddMyQuest(Player : player):void= MyQuestInstance.CompleteEvent.Subscribe(OnMyQuestComplete) Collection.JoinQuest( MyQuest, Participant, quest_participant_info{ } )
At 24:00 — compiled successfully
quests_manager_device.verse
At 27:28 — compiled successfully
elimination_quest<public> := class(basic_quest):, Watch<public>(EliminationManager:elimination_manager_device, Collection:quest_collection, Quest:quest)<suspends>:void, MaybeEliminator := EliminationManager.EliminationEvent.Await(), Eliminator.IsContributor[Contributors], SetProgress(Progress + 1.0), quests_manager_device := class(creative_device):
At 28:00 — compiled successfully
quests_manager_device := class(creative_device):
OnBegin<override>()<suspends>:void=
AddEliminationQuestButton.InteractedWithEvent.Subscribe(OnAddEliminationQuestButtonPressed)
RemoveEliminationQuestButton.InteractedWithEvent.Subscribe(OnRemoveEliminationQuestButtonPressed)
#quest complete
OnQuestComplete( CompletedQuest : quest):void=
if(Memberships := Collection.Queries[CompletedQuest]):
for: Membership : Memberships
Membership.Info.Receives = true
Participant := agent quest participant[Membership.Participant]
Player := player[Participant.Agent]
do:
GoldGranter.GrantItem(Player)
XPAccolade.Award(Player)
#TRIGGER QUEST STUFF
OnAddMyQuestButtonPressed(Agent : agent):void=
if(Player := player[Agent]):
AddMyQuest(Player)
AddMyQuest(Player : player):void=
if(not PlayerQuests[Player]):
MyQuestInstance := my_quest{}
if(set PlayerQuests[Player] = MyQuestInstance):
MyQuestInstance.CompleteEvent.Subscribe(OnQuestComplete)
Participant := agent quest participant{Agent := Player}
At 28:29 — compiled successfully
quests_manager_device.verse, RemoveMapData(Player : player):void=, RemovePlaytimeMapData(Player : player):void=, RemoveEliminationMapData(Player : player):void=, OnPlayerRemoved(Player : player):void=
At 28:38 — compiled successfully
Things mentioned in the video
Names and features the video mentions, as spoken or shown on screen (not yet independently verified against the current API):
| Mentioned | Timestamp | Context |
|---|---|---|
| basic_quest | 2:52 | Parent class for custom quests. Shown in my_quest<public> := class(basic_quest). |
| progress_quest_objective | 4:13 | Base class for objective tracking. Shown in my_quest_objective<public> := class(progress_quest_objective). |
| quest_collection | 1:53 | State container for active quests/memberships. Shown as var Collection : quest_collection = quest_collection{}. |
| trigger_device | 1:53 | Input device for walk-on tracking. Shown as @editable TriggerDevices : []trigger_device = array{}. |
| item_granter_device | 1:53 | Reward dispatcher for items. Shown as @editable GoldGranter : item_granter_device = item_granter_device{}. |
| accolades_device | 1:53 | Reward dispatcher for XP. Shown as @editable XPAccolade : accolades_device = accolades_device{}. |
| button_device | 1:53 | UI interaction input. Shown for Add/Remove/Increase/Decrease buttons. |
| creative_device | 1:30 | Base class for rules devices. Shown in quests_manager_device := class(creative_device): |
| entitlement_quest_reward | 5:29 | Reference structure for item rewards. Spoken as standard Epic reward type, though custom logic used. |
| quest_reward | 3:25 | Abstract base for quest rewards. Shown in autocomplete/API surface. |
| quest_participant_info | 13:23 | Metadata passed when joining a quest. Shown in Collection.JoinQuest(..., quest_participant_info{}). |
| agent_quest_participant | 13:00 | Type casting for extracting participant data. Shown in Participant := agent_quest_participant[Membership.Participant]. |
| quest_membership | 13:00 | Record of player-quest association. Shown in Collection.Quests[CompletedQuest] loop. |
| quest_participant | 9:00 | Interface for contributor validation. Shown in IsContributor parameter type. |
| elimination_manager_device | 27:28 | Combat tracking device. Shown in elimination_quest Watch signature. |
| card_spawner | 47:15 | Enemy spawner for combat testing. Shown in editor config notes. |
| fort_playspace | 23:00 | Session container. Shown via GetPlayspace().PlayerRemovedEvent(). |
| GetPlayspace() | 23:00 | Method to access session state. Shown in OnBegin subscription. |
| PlayerRemovedEvent | 23:00 | Lifecycle event for disconnect cleanup. Shown subscribed in OnBegin. |
| Option | 13:00 | Nullable wrapper type. Shown in set MaybeMyQuest = option{MyQuestInstance}. |
| ?my_quest | 13:00 | Optional type declaration. Shown in var MaybeMyQuest : ?my_quest = false. |
| [player]my_quest | 21:00 | Map type for per-player instances. Shown in var PlayerQuests : [player]my_quest = map{}. |
| starIcon | 3:54 | Texture asset assigned to quest icon. Shown as var Icon<override> : texture = starIcon. |
| MyQuestName | 3:54 | Localizable message variable. Shown as MyQuestName<localizes> : message = "My Quest". |
| MyQuestDescription | 3:54 | Localizable message variable. Shown as MyQuestDescription<localizes> : message = "Walk on 5 triggers...". |
| MyQuestShortDescription | 3:54 | Localizable message variable. Shown as MyQuestShortDescription<localizes> : message = "Triggers Triggered". |
| PlaytimeQuestName | 0:05 | Localizable message variable. Shown as PlaytimeQuestName<localizes> : message = "Playtime Quest". |
| SharpshooterQuestName | 5:29 | Localizable message variable. Shown in sharpshooter_quest example. |
| SniperSkin | 5:29 | Entitlement ID string. Shown as Entitlement := SniperSkin. |
| SetRequiredCount | 16:27 | Method to modify objective thresholds. Shown as MyQuestObjective.SetRequiredCount(...). |
| GrantItem | 13:00 | Method on item_granter_device. Shown as GoldGranter.GrantItem(Player). |
| Award | 13:00 | Method on accolades_device. Shown as XPAccolade.Award(Player). |
| JoinQuest | 13:23 | Collection method to enroll player. Shown as Collection.JoinQuest(MyQuest, Participant, ...). |
| AbandonQuest | 13:23 | Collection method to remove player. Shown as Collection.AbandonQuest(MyQuest, StoredParticipant). |
| CompleteEvent | 12:00 | Lifecycle event fired on quest finish. Shown as MyQuestInstance.CompleteEvent.Subscribe(...). |
| InteractedWithEvent | 11:27 | Button press event. Shown subscribed for all four UI buttons. |
| TriggeredEvent | 7:30 | Device activation event. Shown as TriggerDevice.TriggeredEvent.Await(). |
| EliminationEvent | 27:28 | Combat kill event. Shown as EliminationManager.EliminationEvent.Await(). |
| Watch | 4:18 | Async watcher method signature. Shown as Watch<public>(TriggerDevices, Collection, Quest)<suspends>:void= |
| IsContributor | 9:00 | Extension method for validation. Shown as (InAgent: agent).IsContributor<public>(Contributors : []quest_participant)... |
| GetStoredParticipant | 13:23 | Helper function for lookup. Shown as GetStoredParticipant(Player:player, Quest:quest)... |
| RemoveMapData | 22:24 | Cleanup utility function. Shown iterating and filtering PlayerQuests map. |
| OnBegin | 1:30 | Device initialization override. Shown as OnBegin<override>()<suspends>:void= |
| /Verse.org/Progression | 1:30 | Module import for quest APIs. Shown in using block. |
| /UnrealEngine.com/Progression | 1:30 | Module import for progression types. Shown in using block. |
| /Verse.org/Assets | 1:30 | Module import for textures/icons. Shown in using block. |
| /Verse.org/Simulation | 1:30 | Module import for simulation primitives. Shown in using block. |
| /UnrealEngine.com/Temporary/Diagnostics | 1:30 | Module import for debug tools. Shown in using block. |
| /Fortnite.com/Devices | 1:30 | Module import for creative devices. Shown in using block. |
| sleep | 26:44 | Used in playtime quest loop. Shown as Sleep(1.0). |
| spawn | 7:30 | Launches async watcher coroutine. Shown as spawn { WatchDevice(...) }. |
| loop | 7:30 | Core iteration construct for awaiting events. Shown as loop: |
| first | 15:43 | Used in .first expression to find participant. Shown as first: Membership : Memberships |
| map | 21:00 | Used for per-player instance storage. Shown as var PlayerQuests : [player]my_quest = map{}. |
| localizes | 3:54 | Attribute for localizable messages. Shown as MyQuestName<localizes> : message = ... |
| quest_objective | 4:30 | Base type for objectives. Shown as var Objective<override> : quest_objective = my_quest_objective: |
Want to go further?
- Building a Session-Scoped Quest Manager: From Single-Instance to Per-Player Maps
- Mastering Async Watchers in Verse: Awaiting Events Without Blocking the Simulation
- Safe Progress Tracking: Validating Contributors Before Updating Quest State
- Dynamic Difficulty Scaling: Modifying RequiredCounts at Runtime via Button Inputs
- Lifecycle Management: Cleaning Up Orphaned Quest Data on Player Disconnect
Credit
This breakdown is based on a video by Howtwoboss — watch the original for the full walkthrough.
Get the complete code — free
You've read the full walkthrough. The complete, copy-paste-ready Verse solution is free for members — sign in to unlock it.
Free with your BrainDead.TV / BrainDeadGuild Discord account. The walkthrough above is always free.
Turn this into a guided course
Add This tutorial demonstrates how to build a complete Quest Management system using Fortnite's experimental Verse Quests API. The creator walks through importing a to your free study plan — we'll suggest related pages and stitch the lot into one compile-checked, self-guided lesson with worked examples and quizzes.
Original tutorial generated by Verse Island from the Verse/UEFN knowledge base, with references to the Epic Games sources above. Code is validated against the knowledge base.