๐ง Center Village (Free)
Teen
19 lessons ยท
~152 min ยท Generated 2026-07-04
The REAL AweShucks Town UEFN island needs: TERRAIN/BUILD โ central town plaza (CTF arena footprint with two flag bases), island market street, map-room building (Hall of Badges wall inside), Jitterbean coffee shop, five themed portal booths ringing the plaza (flamingo/bear/dragon
Learning Outcomes
- AweShucks Town Hub โ the Island That Launches the Island
Lesson 1: Town Plaza Wiring: the Trigger Device is Your Event Glue
Objectives
- Student can wire device-to-device events in the editor and Subscribe to TriggeredEvent in Verse to react to anything happening in the world.
Student can wire device-to-device events in the editor and Subscribe to TriggeredEvent in Verse to react to anything happening in the world.
๐ Builds on: south-shores: OnBegin + Subscribe pattern from the first-device lessons
๐งฉ Your capstone piece: Plaza gate triggers that arm each portal booth and fire the hub event bus
Quiz
- What type does the trigger_device's TriggeredEvent hand to your subscribed handler?
- A. agent
- B. ?agent (an optional agent)
- C. player
- D. fort_character
- Why must you declare the trigger as an @editable field inside your creative_device class?
- A. To make it run faster
- B. So you can bind it to the placed device and call its methods; a bare Device.Method() fails
- C. Because TriggeredEvent only works on editable fields
- D. It is optional and only for readability
- What value passed to SetMaxTriggerCount means 'no limit'?
- What does GetTriggerCountRemaining() return when the device's max trigger count is unlimited?
- A. The number of players in the game
- B. 20
- C. 0
- D. An error
- Which call fires the device's TriggeredEvent from code while attaching a specific player?
- A. Trigger()
- B. Trigger(Agent)
- C. Enable()
- D. SetTransmitDelay(Agent)
Answer key
- B โ TriggeredEvent is listenable(?agent) because the device can also be fired from code with no agent, so you must unwrap the option before using it.
- B โ Calling a method on a placed device requires an @editable field you point at the device in the Details panel; otherwise you get 'Unknown identifier'.
- C โ 0 indicates unlimited triggers. The argument is clamped between 0 and 20.
- C โ Per the API, it returns 0 when GetMaxTriggerCount is unlimited, so don't treat 0 as 'empty' in that configuration.
- B โ Trigger(Agent:agent) fires the event and passes that agent along; the agentless Trigger() fires with no agent.
Recap
One step closer to AweShucks Town Hub โ the Island That Launches the Island โ Plaza gate triggers that arm each portal booth and fire the hub event bus
Sources
Lesson 2: Booth Buttons: Bridging Device Interactions into Verse
Objectives
- Student can subscribe to button InteractedWithEvent, read the interacting agent, and route it into their own functions.
Student can subscribe to button InteractedWithEvent, read the interacting agent, and route it into their own functions.
๐ Builds on: south-shores: one-responsibility-per-function (clean handler functions)
๐งฉ Your capstone piece: The interact button at each of the 5 zone-portal booths
Quiz
- Which method subscribes a handler to a Button device interaction?
- A. Button.InteractedWithEvent.Subscribe(Handler)
- B. Button.InteractedWithEvent().Subscribe(Handler)
- C. Button.OnInteract(Handler)
- D. Subscribe(Button.InteractedWithEvent, Handler)
- Why must `GetPlayerUI[InPlayer]` be called inside an `if` condition?
- A. It is slow
- B. It is a <decides> (fallible) call using [] brackets, which are only legal in a failure context
- C. It returns a string
- D. It requires <suspends>
- Why can't you pass the `agent` directly to `GetPlayerUI`?
- A. Agents don't exist in Verse
- B. GetPlayerUI is deprecated
- C. An agent may be AI; UI only exists for players, so you must cast with player[Agent] first
- D. Agents are strings
- What is the correct comparison operator for equality in Verse?
- A. ==
- B. =
- C. ===
- D. .equals
- How do you reassign a value to a `var` in Verse?
- A. Count = Count + 1
- B. set Count = Count + 1
- C. Count += 1
- D. let Count = Count + 1
Answer key
- A โ Device events are accessed as fields and subscribed to with `.Subscribe` directly โ no parentheses on the event name.
- B โ The [] brackets mark a fallible <decides> call, which may only appear in a failure context such as an `if`.
- C โ Agents can be players or AI. UI is per-player, so you cast the agent to a player before requesting UI.
- B โ Verse uses `=` for comparison and `<>` for inequality; `==` and `!=` are not Verse.
- B โ Mutable variables are reassigned with the `set` keyword; `+=` and `++` do not exist in Verse.
Recap
One step closer to AweShucks Town Hub โ the Island That Launches the Island โ The interact button at each of the 5 zone-portal booths
Sources
- /guides/sug-subscribing-to-button-device-interaction-events-in-verse
Lesson 3: Professor AweShucks Speaks: HUD Messages for Player Guidance
Objectives
- Student can show timed, targeted HUD messages from Verse to guide players through the hub.
Student can show timed, targeted HUD messages from Verse to guide players through the hub.
๐ Builds on: south-shores: string interpolation lesson for dynamic message text
๐งฉ Your capstone piece: Welcome sequence + per-booth travel prompts narrated by Professor AweShucks
Quiz
- You want to show a HUD message to every player at once. Which method do you call?
- A. Show()
- B. Show(Agent)
- C. SetText()
- D. ClearAllMessages()
- What does passing `DisplayTime := 0.0` to `Show(Agent, Message, ?DisplayTime := 0.0)` do?
- A. Hides the message immediately
- B. Uses the device's default display time
- C. Displays the message persistently until manually hidden
- D. Causes a compile error โ 0.0 is invalid
- A `trigger_device.TriggeredEvent` handler receives `(Agent : ?agent)`. You want to call `MyHUD.Show(Agent)`. What must you do first?
- A. Cast Agent to agent using `agent(Agent)`
- B. Unwrap the option with `if (A := Agent?):` and pass `A`
- C. Nothing โ ?agent and agent are interchangeable
- D. Call `Show()` instead since the agent is optional
- Which of the following correctly passes text to `SetText`?
- A. MyHUD.SetText("Vault Opened!")
- B. MyHUD.SetText(StringToMessage("Vault Opened!"))
- C. MyHUD.SetText(VaultMsg("Vault Opened!")) where VaultMsg<localizes>(S:string):message = "{S}"
- D. MyHUD.SetText(message{"Vault Opened!"})
- You call `Show()` while a message is already visible. What happens?
- A. The new message is queued and plays after the current one finishes
- B. The new message replaces the currently active message immediately
- C. A compile error is thrown
- D. The call is silently ignored
Answer key
- A โ `Show()` (no arguments) broadcasts the currently set message to all players affected by the device. `Show(Agent)` targets a single player, `SetText` only sets the text without displaying it, and `ClearAllMessages` removes messages.
- C โ A `DisplayTime` of `0.0` means the message stays on screen indefinitely. You must call `Hide()`, `Hide(Agent)`, or `ClearAllMessages()` to remove it.
- B โ `Show(Agent:agent)` requires a non-optional `agent`. You must unwrap the `?agent` with `if (A := Agent?):` before passing it. Passing `?agent` directly is a compile error.
- C โ `SetText` expects a `message` type. You must declare a `<localizes>` function to convert a string into a `message`. There is no `StringToMessage` function, and raw string literals are not `message` values.
- B โ According to the API, `Show()` 'will replace any previously active message.' If you need sequential messages, sleep between calls or track timing with `GetDisplayTime()`.
Recap
One step closer to AweShucks Town Hub โ the Island That Launches the Island โ Welcome sequence + per-booth travel prompts narrated by Professor AweShucks
Sources
- /guides/hud-message-device
Lesson 4: Island Market Ambience: Song Sync and Musical Stings
Objectives
- Student can drive the song_sync_device from Verse to layer district ambience and fire a sting on game events.
Student can drive the song_sync_device from Verse to layer district ambience and fire a sting on game events.
๐ Builds on: south-shores: Patchwork/audio device patterns (drum, omega-synth)
๐งฉ Your capstone piece: Market ambience loop + portal-unlock sting + round-start music cue
Quiz
- Which two methods does the song_sync_device expose to Verse?
- A. Play and Stop
- B. Enable and Disable
- C. Start and Restart
- D. Show and Hide
- Why must you declare the device as an @editable field instead of calling song_sync_device{}.Enable() directly?
- A. Because Enable is private
- B. Because a bare literal isn't linked to the placed device in the level
- C. Because Verse forbids literals
- D. Because Enable needs an agent parameter
- What is the correct handler signature for a trigger_device's TriggeredEvent?
- A. (Agent : agent)
- B. (Agent : ?agent)
- C. (Player : player)
- D. ()
- How would you cleanly restart the synchronized timeline from a known point?
- A. Call Enable() twice
- B. Call Disable() then Enable()
- C. Call Restart()
- D. Re-subscribe the event
- Where should you put the code that subscribes event handlers to a button or plate?
- A. In a top-level function
- B. In OnBegin<override>()<suspends>:void
- C. In the class field list
- D. In the handler method itself
Answer key
- B โ song_sync_device inherits only Enable() and Disable() from its patchwork_device base โ these turn the shared timeline on and off.
- B โ An @editable field lets you assign the actual placed device in the Details panel; a bare literal points to nothing in the world.
- B โ TriggeredEvent hands an optional agent, so the handler takes (Agent : ?agent) and you unwrap it with if (P := Agent?):.
- B โ There is no Restart method; calling Disable() then Enable() stops and re-starts the shared timeline.
- B โ Subscriptions are wired up in OnBegin, where the device is live and the event handler methods exist at class scope.
Recap
One step closer to AweShucks Town Hub โ the Island That Launches the Island โ Market ambience loop + portal-unlock sting + round-start music cue
Sources
Lesson 5: Map Room Guards: AI Patrol Paths and NPC Routes
Objectives
- Student can lay patrol path nodes, assign an NPC guard, and start/stop/redirect patrols from Verse.
Student can lay patrol path nodes, assign an NPC guard, and start/stop/redirect patrols from Verse.
๐ Builds on: verse-npc-guard-patrol (compile-passed companion script, re-zoned here)
๐งฉ Your capstone piece: Town guards patrolling the plaza and market; guards pause when the hub round launches
Quiz
- What signature does a handler for NodeReachedEvent need?
- A. (Agent : agent) โ the event is listenable(agent)
- B. (MaybeAgent : ?agent) โ you must unwrap it
- C. () โ patrol events pass no data
- D. (Node : ai_patrol_path_device)
- Which method commands an already-patrolling guard onto its alternate route?
- A. Enable()
- B. Assign(Patroller)
- C. GoToNextPatrolGroup(Patroller)
- D. Disable()
- Why must you declare the patrol device as an @editable field?
- A. To localize its messages
- B. Because a bare Device.Method() call fails with 'Unknown identifier' โ you can only call methods on a placed device instance held in a field
- C. To make events fire faster
- D. It is optional; you can call methods globally
- What agent should you pass to Assign(Patroller)?
- A. Any player agent
- B. A guard agent produced by the guard_spawner_device (e.g. from SpawnedEvent or GetAgents())
- C. The creative_device itself
- D. A vector3 location
- In the walkthrough, what happens right after GuardSpawner.SpawnedEvent fires?
- A. The node calls Disable() immediately
- B. OnGuardSpawned assigns the guard to GatePatrolNode, then calls GoToNextPatrolGroup on it
- C. ArrivalLight.Begin() fires before anything else
- D. The guard is teleported to the gate node
Answer key
- A โ Patrol path events are listenable(agent), so the handler receives a non-optional agent directly โ no ? unwrap needed.
- C โ GoToNextPatrolGroup tells the patroller to follow the Next Patrol Path Group instead of the default group.
- B โ Placed devices must be referenced through an @editable field of a creative_device; otherwise the identifier is unknown.
- B โ Assign expects an AI agent created by the guard spawner; player agents won't patrol.
- B โ OnGuardSpawned calls GatePatrolNode.Assign(Guard) then GatePatrolNode.GoToNextPatrolGroup(Guard) to push the fresh guard onto its alert route.
Recap
One step closer to AweShucks Town Hub โ the Island That Launches the Island โ Town guards patrolling the plaza and market; guards pause when the hub round launches
Sources
- /guides/ai-patrol-path-device
Lesson 6: Score and Time: Wiring score_manager and timer from Verse
Objectives
- Student can award score via score_manager_device and start/pause/reset a timer_device from Verse as one cooperating system.
Student can award score via score_manager_device and start/pause/reset a timer_device from Verse as one cooperating system.
๐งฉ Your capstone piece: Lobby countdown timer + town-coin score used by the quest and the CTF event
Quiz
- Which method actually grants points to a player?
- A. SetScoreAward
- B. Increment
- C. Activate
- D. GetScoreAward
- How does a handler for the score manager's ScoreOutputEvent receive the player?
- A. As a ?agent that must be unwrapped with Agent?
- B. As a plain agent, no unwrap needed
- C. As a player_ui
- D. As a string
- When should you call the agent overloads like Activate(Player) instead of Activate()?
- A. Never; they are deprecated
- B. Only in OnBegin
- C. When the device's Activating Team option targets a specific team, so the agent's team is checked
- D. Only when reading scores
- What does GetScoreAward() return?
- A. The player's total lifetime score
- B. The number of points the next activation will award
- C. An optional agent
- D. Nothing; it has void return
- Why must Scorer be declared as an @editable field?
- A. So it prints to the log
- B. So it can be localized
- C. So Verse can reference the placed device in the level and call its methods
- D. So it auto-converts int to float
Answer key
- C โ SetScoreAward/Increment/Decrement/SetToAgentScore only configure the pending amount; Activate (or Activate(Agent)) is what pays it out.
- B โ ScoreOutputEvent is listenable(agent), so the handler gets a plain agent. Only optional listenable(?agent) events (like a trigger's TriggeredEvent) require unwrapping.
- C โ With a specific Activating Team set, the agent's team determines whether they may affect the device, so the agent overloads are required.
- B โ GetScoreAward()<transacts>:int returns the score the next Activate will grant. GetCurrentScore(Agent) returns a player's running total.
- C โ Device methods can only be called through an @editable field bound to a placed device; a bare constructor instance references nothing in the level.
Recap
One step closer to AweShucks Town Hub โ the Island That Launches the Island โ Lobby countdown timer + town-coin score used by the quest and the CTF event
Sources
- /guides/score-manager-device
Lesson 7: The Hub Heartbeat: Custom Round Logic in Verse
Objectives
- Student can orchestrate a start/play/win/reset game loop in Verse that coordinates score, timer, and spawns.
Student can orchestrate a start/play/win/reset game loop in Verse that coordinates score, timer, and spawns.
๐ Builds on: south-shores: enum-with-data-pattern game-phase state machine module
๐งฉ Your capstone piece: The hub lobby loop: WAITING -> READY -> LAUNCH -> RESET state machine
Quiz
- Why is `LoserFlags` keyed on `session` rather than being a plain `var` on the device?
- A. Plain device vars are wiped on round reset; a session-keyed weak_map persists across rounds in the same match
- B. Session keys are faster to look up
- C. Plain vars cannot store logic values
- D. It is the only way to store a map in Verse
- Why must `LoserFlags[Session]` appear inside an `if`/`for` or a `<decides>` body?
- A. Because it returns a logic that must be negated
- B. Because `[]` is a fallible index operation that may only run in a failure context
- C. Because weak_maps are asynchronous
- D. Because Session is optional
- What is the correct Verse boolean type used for the loser flag?
- A. bool
- B. boolean
- C. logic
- D. flag
- How do you test a `logic` value named `Flag`?
- A. if (Flag == true):
- B. if (Flag?):
- C. if (Flag = true):
- D. if (not Flag):
- Which real player-UI calls actually put the warning on screen?
- A. GetSession() and AddWidget
- B. GetPlayerUI[] to get the UI, then AddWidget with a canvas
- C. Print and Sleep
- D. Subscribe and Cancel
Answer key
- A โ State on the device is unreliable across a round reset; keying a weak_map on the session lets the flag survive between rounds of the same match.
- B โ Square-bracket index access is fallible and can only be used where failure is handled โ a condition or a <decides> function body.
- C โ Verse has no `bool`; its boolean type is `logic` with values `true`/`false`, tested with `?`.
- B โ You interrogate a logic with `?`: `if (Flag?):` succeeds when it is true. `==` is not Verse and `= true` is wrong.
- B โ `GetPlayerUI[Player]` returns the player's UI; you build a canvas (containing a text_block) and call `AddWidget` to display it.
Recap
One step closer to AweShucks Town Hub โ the Island That Launches the Island โ The hub lobby loop: WAITING -> READY -> LAUNCH -> RESET state machine
Sources
- /guides/custom-round-logic-using-verse
Lesson 8: Teams and Classes: Organizing a Multiplayer Town
Objectives
- Student can configure teams and classes, use class_selector devices, and read a player's team from Verse.
Student can configure teams and classes, use class_selector devices, and read a player's team from Verse.
๐งฉ Your capstone piece: Red/Blue plaza teams + Explorer/Builder classes for the town CTF event
Quiz
- How do you reach the object that tracks all teams from a Verse device?
- A. GetPlayspace().GetTeamCollection()
- B. GetTeams().Collection()
- C. GetWorld().Teams()
- D. GetPlayspace().TeamList()
- Why is GetTeam[Player] called with square brackets inside an if?
- A. Because it returns an array
- B. Because it is fallible and may fail to find a team
- C. Because it is a suspends function
- D. Because square brackets speed it up
- Which operator does Verse use to compare two values for equality?
- What does GetTeams() return?
- A. A single team
- B. The number of teams as an int
- C. An array of all teams
- D. A map of players to teams
- Which event fires when a new player enters the playspace?
- A. PlayerAddedEvent()
- B. SpawnedEvent
- C. OnPlayerJoin
- D. PlayerEnteredEvent
Answer key
- A โ GetPlayspace() returns the fort_playspace, and GetTeamCollection() returns its fort_team_collection.
- B โ GetTeam[] is a fallible (<decides>) call, so it must appear in a failure context like an if condition using [] brackets.
- B โ Verse uses a single = for equality comparison; == is not valid Verse.
- C โ GetTeams() returns an array of teams; you can read .Length or loop over it.
- A โ PlayerAddedEvent() is a listenable(player) that signals when a human player joins the fort_playspace.
Recap
One step closer to AweShucks Town Hub โ the Island That Launches the Island โ Red/Blue plaza teams + Explorer/Builder classes for the town CTF event
Sources
- /guides/using-teams-and-classes-to-organize-your-game
Lesson 9: Lobby Spawns and Loadouts: player_spawner + team_settings_and_inventory
Objectives
- Student can place per-team spawn pads and grant team loadouts/inventory rules so players always spawn correctly equipped.
Student can place per-team spawn pads and grant team loadouts/inventory rules so players always spawn correctly equipped.
๐ Builds on: the-deeps reference: team-settings-and-inventory-device (compile-passed, reused here)
๐งฉ Your capstone piece: Team spawn pads flanking the plaza + starter loadout for the CTF event
Quiz
- What is the correct way to reference a placed `player_spawner_device` in your Verse class so you can call its methods?
- A. Declare it as an `@editable` field and wire it up in the UEFN Details panel
- B. Call `player_spawner_device{}` inline wherever you need it
- C. Import it with `using { /Fortnite.com/Devices/player_spawner_device }`
- D. Use `GetDevice[player_spawner_device]` at runtime
- What type does `SpawnedEvent` send to its subscriber handler?
- A. `?agent` โ an optional agent that must be unwrapped
- B. `player` โ the specific player who spawned
- C. `agent` โ a non-optional agent
- D. `fort_character` โ the character that was spawned
- You call `ArenaSpawner.SpawnPlayer(Player)` but the player doesn't move. The spawner is enabled and valid for their team. What is the most likely cause?
- A. You forgot to call `ArenaSpawner.Spawn()` first
- B. The device's `ShouldRespawnAlivePlayers` setting is false and the player is currently alive
- C. `SpawnPlayer` only works inside a `<suspends>` function
- D. You must subscribe to `SpawnedEvent` before calling `SpawnPlayer`
- Which of the following correctly disables a spawner, waits 10 seconds, then re-enables it?
- A. `MySpawner.Disable() Sleep(10.0) MySpawner.Enable()` in a plain (non-suspends) method
- B. `MySpawner.Disable()` then `MySpawner.Enable()` with `Sleep(10.0)` between them inside an `OnBegin<override>()<suspends>` body
- C. `MySpawner.SetEnabled(false)` then `MySpawner.SetEnabled(true)` after a timer
- D. `Disable(MySpawner)` then `Enable(MySpawner)` after `Wait(10)`
- You want to grant a weapon to every player the moment they spawn from a pad. Which approach is correct?
- A. Poll `GetPlayspace().GetPlayers()` every frame and check their location
- B. Subscribe to `SpawnedEvent` on the spawner and call your granter logic inside the handler
- C. Override `OnPlayerSpawn` in your creative_device class
- D. Call `SpawnPlayer` and pass a callback lambda as the second argument
Answer key
- A โ Verse devices must be declared as `@editable` fields inside your `creative_device` class and assigned in the UEFN Details panel. A bare `player_spawner_device{}` creates a disconnected default instance that has no placed counterpart and cannot affect the game world.
- C โ `SpawnedEvent` is typed `listenable(agent)`, so its handler receives a plain `agent` value โ no optional unwrap needed. If you need player-specific APIs, cast with `if (P := player[SpawnedAgent]):`.
- B โ When `ShouldRespawnAlivePlayers` is false (the default), `SpawnPlayer` has no effect on a living player โ it only takes effect on their next death. Enable the setting in the device's Details panel to force-move alive players immediately.
- B โ `Sleep` is a suspending call and can only be used in a `<suspends>` context like `OnBegin`. The correct API calls are `MySpawner.Disable()` and `MySpawner.Enable()` โ there is no `SetEnabled` or free-function `Disable`/`Enable`.
- B โ `SpawnedEvent` fires every time an agent spawns from that device and delivers the agent to your handler. This is the idiomatic, event-driven way to react to spawns โ no polling or overrides required, and `SpawnPlayer` takes only a `player` argument with no callback.
Recap
One step closer to AweShucks Town Hub โ the Island That Launches the Island โ Team spawn pads flanking the plaza + starter loadout for the CTF event
Sources
- /guides/player-spawner-device
Lesson 10: Tour the Town: Quests with the Tracker Device
Objectives
- Student can define a tracked quest (visit all 5 portal booths), update progress from Verse, and grant a completion reward.
Student can define a tracked quest (visit all 5 portal booths), update progress from Verse, and grant a completion reward.
๐ Builds on: north-jungle: verse-maps lesson pattern for per-player visited-booth map
๐งฉ Your capstone piece: 'Tour the Town' onboarding quest that walks new players past every zone booth
Quiz
- Which method fires automatically when a tracked value reaches the target, and what type does its handler receive?
- A. CompleteEvent, which sends a plain `agent`
- B. CompleteEvent, which sends `?agent`
- C. TriggeredEvent, which sends a plain `agent`
- D. OnComplete, which sends `int`
- You want to display 'Collect Crystals' as the tracker's HUD title. Which call is correct?
- A. ObjectiveTracker.SetTitleText("Collect Crystals")
- B. ObjectiveTracker.SetTitleText(MyTitle("Collect Crystals")) where MyTitle<localizes>(S:string):message = "{S}"
- C. ObjectiveTracker.SetTitleText(StringToMessage("Collect Crystals"))
- D. ObjectiveTracker.SetTitle("Collect Crystals")
- What must be true for `Save(Agent)`, `Load(Agent)`, and `ClearPersistence(Agent)` to have any effect?
- A. The tracker must be assigned to the agent before calling them
- B. The tracker's **Use Persistence** property must be set to **Use** in the device's editor settings
- C. You must call `LoadForAll()` first
- D. The agent must have reached the target value
- How do you correctly check whether an agent currently has the tracker active?
- A. if (ObjectiveTracker.IsActive(A) = true):
- B. if (ObjectiveTracker.IsActive[A]):
- C. ObjectiveTracker.IsActive(A)
- D. if (ObjectiveTracker.IsActive(A)?):
- A player joins mid-game after `AssignToAll()` was already called. What must you do to give them the tracker?
- A. Nothing โ `AssignToAll` automatically covers future joiners
- B. Call `AssignToAll()` again to refresh all assignments
- C. Subscribe to a join event and call `Assign(Agent)` for the new player
- D. Call `LoadForAll()` which implicitly assigns the tracker
Answer key
- A โ `CompleteEvent : listenable(agent)` sends a plain, already-unwrapped `agent` to its handler โ no optional unwrapping needed, unlike `trigger_device.TriggeredEvent` which sends `?agent`.
- B โ `SetTitleText` requires a `message` value. There is no `StringToMessage` function in Verse. You must declare a localizer helper with `<localizes>` and pass its result.
- B โ Persistence methods are silently no-ops unless the tracker's **Use Persistence** option is enabled in the UEFN device panel. The Verse calls alone are not sufficient.
- B โ `IsActive` carries `<decides>`, making it a failable expression. It must be called with bracket syntax inside an `if` expression: `if (ObjectiveTracker.IsActive[A]):`. Calling it outside an `if` is a compile error.
- C โ `AssignToAll()` only covers agents present at the moment it is called. For late joiners you must detect the join (e.g., via a player_spawner_device event) and explicitly call `Assign(Agent)` for each new player.
Recap
One step closer to AweShucks Town Hub โ the Island That Launches the Island โ 'Tour the Town' onboarding quest that walks new players past every zone booth
Sources
Lesson 11: Plaza Skirmish: a Capture-the-Flag Mini-Mode
Objectives
- Student can assemble teams, classes, spawns, score, timer, and round logic into one playable CTF event.
Student can assemble teams, classes, spawns, score, timer, and round logic into one playable CTF event.
๐ Builds on: lessons 6-9 of this zone + south-shores Shell-Hunt project structure
๐งฉ Your capstone piece: The while-you-wait plaza minigame that runs between hub launches
Quiz
- In classic CTF rules, why can't a whole team just blindly rush the enemy flag?
- A. The enemy flag is too heavy to carry with a full team
- B. A capture only counts if YOUR own flag is home, so someone must defend
- C. Rushing disables your respawns
- D. The map physically locks until both flags are dropped
- Which device IS the flag in a UEFN Capture the Flag build?
- A. Item Granter Device
- B. Capture Area Device
- C. Capture Item Spawner Device
- D. Trigger Device
- How does the Verse device decide which TEAM to credit for a capture?
- A. It reads a team number stored on the flag device
- B. It calls GetPlayspace().GetTeamCollection().GetTeam[Scorer] on the capturing agent
- C. It checks which spawn pad the player last used
- D. Teams aren't tracked in Verse; only the device options know
- Why is the map-set written as `if (set Scores[ScoringTeam] = NewScore) {}`?
- A. To run the assignment on a background thread
- B. Because setting a map entry is a failable expression and must be handled
- C. To delay scoring by one frame
- D. It's a stylistic choice with no functional reason
- What type is `Scores` declared as in `capture_the_flag_device`?
- A. var Scores : [agent]int = map{}
- B. var Scores : [team]int = map{}
- C. var Scores : []int = array{}
- D. var Scores : [string]int = map{}
Answer key
- B โ Classic rules require your flag to be at base for a capture to score, which forces a defense/offense split and creates the tug-of-war.
- C โ capture_item_spawner_device spawns the carryable flag and fires the pickup/capture/drop/return events. A Capture Area is for zones, not carryables.
- B โ The team collection's GetTeam[] resolves an agent to its team โ the canonical team-mode lookup. It's failable, so it's guarded with if.
- B โ Mutating a map entry is failable in Verse, so it's wrapped in an if (...) {} to satisfy the failure context.
- B โ Scores is keyed directly by the team object (`[team]int`), avoiding magic numbers or string keys.
Recap
One step closer to AweShucks Town Hub โ the Island That Launches the Island โ The while-you-wait plaza minigame that runs between hub launches
Sources
- /guides/game-mode-capture-the-flag
Lesson 12: The Hall of Badges: Cross-Zone Persistence with [player] Maps
Objectives
- Student can store per-player progress in a weak_map([player], int) marked <persistable>, version it, and read zone badges earned on OTHER islands.
Student can store per-player progress in a weak_map([player], int) marked <persistable>, version it, and read zone badges earned on OTHER islands.
๐ Builds on: east-volcano: persisting-a-tycoon-state-with-persistable pattern (compile-passed grounding)
๐งฉ Your capstone piece: The IslandProgress badge profile that gates paid-zone portals and lights the Hall of Badges
Quiz
- A returning player joins with a save stamped Version = 1 while CurrentSaveVersion = 2. What does the EnsureProfile / MigrateProgress pair do with their badges?
- A. EnsureProfile detects Version < CurrentSaveVersion and rebuilds the record via MigrateProgress: the new Version is stamped, v2-specific changes are applied (retroactive TownCoins), and every badge is preserved through the copy-constructor.
- B. The old record is discarded and replaced with a fresh island_progress{}, so the player starts over with zero badges.
- C. Nothing โ the engine automatically rewrites the Version field, so no migration code is ever needed.
- D. The save fails to load at all, because a persistable class can only ever be read at the exact version it was written.
- Why is the island_progress class declared class<final><persistable> rather than just class<persistable>?
- A. <final> is required on a persistable class โ the engine must know the exact concrete shape it is saving, so persistable classes cannot be subclassed.
- B. <final> makes the class immutable at runtime, which is what makes persistence possible.
- C. <final> is optional and only added for performance.
- D. <final> marks the class as the last version of the schema, disabling future migrations.
- To add 50 TownCoins to a stored profile, why does the code build a whole new island_progress with the copy-constructor instead of writing set Profile.TownCoins += 50?
- A. Stored persistable records cannot be mutated field-by-field โ you replace the map entry with a fresh record, overriding the changed field and copying the rest via the <constructor> so no other field is silently reset.
- B. It is purely a style choice; in-place mutation works the same way.
- C. Because TownCoins is declared <final> and can never change after creation.
- D. Because weak_map entries are read-only once the engine has saved them.
- Why do EnsureProfile and GrantBadge carry <decides><transacts>, forcing callers to invoke them as EnsureProfile[Player]?
- A. weak_map reads and writes are failable โ the key may be absent โ so the accessors run in a failure context and callers must handle success/failure with [] inside an if.
- B. <decides> makes the function run asynchronously so saves never block the game thread.
- C. It is required for any function that takes a player parameter.
- D. The specifiers make the weak_map automatically persistable.
- Which declaration correctly fills the blank so island_progress can be saved by the engine?
island_progress := class<final>____:
Version:int = 0
ShellBadge:logic = false
- A. <persistable>
- B. <unique>
- C. <concrete>
- D. <transacts>
Answer key
- A โ Migration is opt-in code YOU write: EnsureProfile compares the stored Version against CurrentSaveVersion and, when it is older, replaces the record with MigrateProgress(Existing) โ a fresh record that stamps the new Version, applies any semantic changes, and copies every surviving field through MakeIslandProgress<constructor>. Old badges are never lost because the constructor copies them.
- A โ Verse requires <final> on persistable classes: the saved layout must be a fixed, concrete shape, so subclassing is forbidden. Every field must also itself be a persistable type.
- A โ The persistence idiom is copy-on-write: set IslandProgressMap[Player] = island_progress: TownCoins := Src.TownCoins + 50, then MakeIslandProgress<constructor>(Src) fills in every other field. Skipping the constructor (or forgetting a field inside it) is how updates silently zero unrelated progress.
- A โ IslandProgressMap[Player] and set IslandProgressMap[Player] = ... can both fail (missing key, invalid player), so the accessors are <decides><transacts> and the whole body is one transactional failure context. Callers write if (EnsureProfile[Player]): โ a failed lookup rolls back cleanly instead of crashing.
- A โ The class must be marked <persistable> (alongside <final>) for the engine to save it per player in a weak_map. <unique>, <concrete>, and <transacts> are not what makes a class savable.
Recap
One step closer to AweShucks Town Hub โ the Island That Launches the Island โ The IslandProgress badge profile that gates paid-zone portals and lights the Hall of Badges
Sources
- /guides/cross-zone-progress-persistence
Lesson 13: Badge-Gated Doors: Conditional Buttons and Unlock Logic
Objectives
- Student can gate interactions with conditional_button_device and a Verse gatekeeper module that checks the persistence profile.
Student can gate interactions with conditional_button_device and a Verse gatekeeper module that checks the persistence profile.
๐ Builds on: north-jungle: verse-modules / shared-helper-module (import the gatekeeper as a module)
๐งฉ Your capstone piece: portal_gatekeeper module: free booths always open, paid-zone booths require the previous badge
Quiz
- A booth charges 5 town coins AND requires the Shell Hunt badge. Which check belongs in the conditional_button_device and which belongs in Verse โ and why?
- A. Coins in the device (it counts key items natively and fires NotEnoughItemsEvent); badge in Verse (badges live in a persistable weak_map profile the device cannot read).
- B. Both in the device โ conditional buttons can be configured to check any saved value.
- C. Both in Verse โ devices should never perform gameplay checks.
- D. Badge in the device (badges are items); coins in Verse (currency needs code).
- Why does `CanEnter[Player, RequiredBadge]` always succeed for a free booth?
- A. Free booths pass RequiredBadge = 0, so the `if (RequiredBadge > 0)` body never runs and no failable check executes.
- B. Because EnsureProfile grants every badge to new players.
- C. Because the conditional button is disabled at free booths.
- D. It doesn't โ free booths must call a separate CanEnterFree function.
- In GrantBadge, why rebuild the profile with `MakeIslandProgress<constructor>(Src)` instead of mutating the stored record's Badges field directly?
- A. Persistable records in a weak_map cannot be mutated in place โ you store a fresh record that overrides the changed field and copies the rest via the constructor, so no other field is silently reset.
- B. The constructor is faster than direct mutation.
- C. weak_map entries are read-only after the first write.
- D. It is purely stylistic; `set Src.Badges += array{Badge}` would compile fine.
- The gatekeeper lives in its own file. What must each booth script write to use it?
- A. using { PortalGatekeeper } โ importing the module by name, the same technique as importing CodewoodKit in the North Jungle.
- B. include "PortalGatekeeper.verse" at the top of the file.
- C. Nothing โ all Verse files automatically share every declaration.
- D. @import(PortalGatekeeper) above the class declaration.
- In `portal_booth_device.OnBegin`, which two events does `BoothButton` subscribe to, and what does each one represent?
- A. ActivatedEvent (the device's own item check passed) and NotEnoughItemsEvent (the item check failed).
- B. ActivatedEvent (the badge check passed) and NotEnoughItemsEvent (the badge check failed).
- C. TriggeredEvent and ActivatedEvent, both fired only after the Verse badge check.
- D. OnBeginEvent and NotEnoughItemsEvent.
Answer key
- A โ The device's native job is counting key items in the player's inventory right now โ it verifies the coin fee and fires NotEnoughItemsEvent for free. The badge is account-level progress stored in a persistable weak_map profile; no device panel can read that, so the badge lookup must be Verse. Each check sits where its data lives.
- A โ CanEnter only delegates to the failable HasBadge when RequiredBadge is positive. With RequiredBadge = 0 the guard is skipped, nothing can fail, and the decides function succeeds โ which is exactly the 'free booths always open' rule expressed in one line.
- A โ You can't mutate a field of a stored persistable record. The copy-constructor idiom builds a new island_progress overriding only Badges while the constructor copies every other field (like Version) โ the standard pattern that keeps a one-field update from zeroing the rest of the save.
- A โ Verse modules are imported with a using expression naming the module: `using { PortalGatekeeper }`. After that its public members (CanEnter, GrantBadge, the badge ids) are available โ the exact skill built in the North Jungle module lessons, now powering the hub.
- A โ OnBegin subscribes OnBoothActivated to ActivatedEvent and OnMissingItems to NotEnoughItemsEvent. ActivatedEvent fires only after the device's own item check already passed โ that's the moment Verse layers the badge check on top. NotEnoughItemsEvent fires when the item check failed, giving the 'can't afford the fee' case for free.
Recap
One step closer to AweShucks Town Hub โ the Island That Launches the Island โ portal_gatekeeper module: free booths always open, paid-zone booths require the previous badge
Sources
- /guides/portal-gating-conditional-button
Lesson 14: Building the Portal Gate: Scene Graph Entities and Prefabs
Objectives
- Student can build the portal gate as an entity hierarchy with mesh components and save it as a reusable prefab stamped at all 5 booths.
Student can build the portal gate as an entity hierarchy with mesh components and save it as a reusable prefab stamped at all 5 booths.
๐ Builds on: verse-scene-graph-intro + hierarchy-transforms (compile-passed set)
๐งฉ Your capstone piece: The portal-gate prefab reused at every zone booth
Quiz
- Why is GetComponent written with square brackets (GetComponent[mesh_component]) and placed inside an if:?
- A. Because square brackets are required syntax for every method call in Verse
- B. Because it returns an array of components that must be looped over
- C. Because getting a component can fail โ the entity might not have one of that kind โ so it must be guarded
- D. Because it modifies the entity, and modifications always need square brackets
- You want a custom component to run an ongoing loop that waits and repeats forever. Which lifetime method should hold that logic?
- A. OnSimulate, because it is <suspends> and is where loops and waits live
- B. OnBeginSimulation, because it runs first at startup
- C. OnAddedToScene, because the component is freshly in the scene
- D. OnEndSimulation, because the loop should keep going until shutdown
- When you build a new component in code, why must you write Entity := Entity (telling it which entity it belongs to)?
- A. It is optional naming; Verse picks the nearest entity if you omit it
- B. It tells the component to copy itself onto every child entity
- C. It links the component to the simulation entity at the root
- D. A component is born attached to exactly one entity for life โ components cannot be moved between parents
- To make an entity's shape vanish temporarily without deleting it, the example calls Mesh.Disable(). What's the advantage of disabling over deleting?
- A. Disabling frees more memory than deleting the entity
- B. The mesh can be turned back on later with Enable(), so the thing can reappear
- C. Deleting a mesh_component is not allowed in Verse
- D. Disabling automatically removes the entity from the tree
- Fill in the blank: to define a component you can bolt onto an entity, you write my_component := class<____>(component):. What goes in the blank?
- A. final_super
- B. suspends
- C. override
- D. editable
Answer key
- C โ Square brackets mark a verb that might fail (the <decides> effect); an entity may lack that component type. The if: safely skips when there's no match. Brackets are not a universal call syntax โ round brackets mark calls that can't fail.
- A โ OnSimulate is the <suspends> main-logic method built for loops, waits, and ongoing behavior. OnBeginSimulation is for instant must-finish-now setup, not for suspending loops.
- D โ The lesson notes components cannot be moved between parents, so each is born attached to one entity for life โ hence you must specify its entity at construction. Verse does not auto-pick or copy it across children.
- B โ Disable()/Enable() flip a component off and on without destroying it, so the object can reappear later โ the basis of the blink-on/off loop. Deleting would be permanent and removing it from the tree is unrelated.
- A โ <final_super> is the required tag every time you make a component intended for an entity โ it promises the class sits directly on top of component. <suspends> and <override> are effects/specifiers for methods, and @editable is an attribute for vars, not a class tag.
Recap
One step closer to AweShucks Town Hub โ the Island That Launches the Island โ The portal-gate prefab reused at every zone booth
Sources
- /guides/verse-scene-graph-entities-components
Lesson 15: The Portal Beacon: a Custom Verse Component
Objectives
- Student can write a custom component (portal_beacon_component) attached to the gate prefab that animates/glows when the player's badge unlocks that zone.
Student can write a custom component (portal_beacon_component) attached to the gate prefab that animates/glows when the player's badge unlocks that zone.
๐ Builds on: lesson 12 IslandProgress module + south-shores MoveTo/ease animation arc
๐งฉ Your capstone piece: portal_beacon_component: per-player unlock glow + idle pulse animation on each gate
Quiz
- Why must a call like `Entity.GetComponent[light_component]` be wrapped in an `if` (or used with `or`)?
- A. Because GetComponent is a failable lookup that may not find the component
- B. Because light_component is a device
- C. Because GetComponent runs asynchronously
- D. Because Verse forbids square brackets outside loops
- What does a `light_component` depend on to know where it shines?
- A. A trigger_device on the same entity
- B. A transform_component on the entity
- C. The creative_device's OnBegin
- D. A material asset
- How do you reference a placed Scene Graph entity so Verse can act on it?
- A. Call entity{} directly in OnBegin
- B. Declare an @editable entity field on a creative_device and assign it in the Details panel
- C. Use StringToMessage to name it
- D. It happens automatically for every entity in the level
- Which method returns the direct child entities of an entity?
- A. GetComponents()
- B. GetLocalTransform()
- C. GetEntities()
- D. Enable()
- Why do the transform examples write `20.0` and `100.0` instead of `20` and `100`?
- A. Style preference only
- B. Verse does not auto-convert int to float, and translation vectors are float
- C. Integers are not allowed in Scene Graph at all
- D. Because SetLocalTransform rejects positive numbers
Answer key
- A โ The `[]` form is a failable query โ the entity might not have that component โ so it must appear in a failure context like an `if`.
- B โ The API notes that a transform_component on the entity positions the light.
- B โ Like any placed thing, an entity must be held in an @editable field on a creative_device to be referenced from Verse.
- C โ GetEntities() returns the []entity direct children; GetComponents() returns the []component on the entity itself.
- B โ Vector translations are float components; Verse won't implicitly convert an int, so float literals are required.
Recap
One step closer to AweShucks Town Hub โ the Island That Launches the Island โ portal_beacon_component: per-player unlock glow + idle pulse animation on each gate
Sources
- /guides/scene-graph-add-component
Lesson 16: The Gateway: Matchmaking Portal Device
Objectives
- Student can configure a matchmaking_portal_device with a linked island code, enable/disable it from Verse, and send players to another published island.
Student can configure a matchmaking_portal_device with a linked island code, enable/disable it from Verse, and send players to another published island.
๐ Builds on: lesson 13 portal_gatekeeper module
๐งฉ Your capstone piece: The five live portals โ one per zone capstone island โ enabled by the gatekeeper
Quiz
- Which two methods does `matchmaking_portal_device` expose in its Verse API?
- A. Enable() and Disable()
- B. Open() and Close()
- C. Activate() and Deactivate()
- D. Start() and Stop()
- You want to call `Sleep(20.0)` inside a method triggered by a `trigger_device` event. What must you do?
- A. Mark the event handler itself as `<suspends>`
- B. Use `spawn { }` to launch a `<suspends>` coroutine from the handler
- C. Call `Sleep` directly โ event handlers support suspending automatically
- D. Replace `Sleep` with a `timer_device`; Verse Sleep cannot be used here
- Where do you configure which island the Matchmaking Portal sends players to?
- A. By calling a `SetDestination()` method in Verse at runtime
- B. In the device's Details panel in the UEFN editor โ it cannot be changed via Verse
- C. By passing a string island ID to `Enable()`
- D. Through a linked `island_selector_device` referenced in Verse
- You declare `@editable EscapePortal : matchmaking_portal_device = matchmaking_portal_device{}` but forget to assign the placed device in the UEFN Details panel. What happens when `EscapePortal.Enable()` is called?
- A. A compile error is thrown because the field has no real device
- B. The call targets the default placeholder instance and the in-world portal is unaffected
- C. UEFN automatically finds the nearest portal in the level
- D. A runtime exception crashes the Verse device
- Which of the following correctly subscribes to a `trigger_device` event and handles the optional agent parameter?
- A. `Plate.TriggeredEvent.Subscribe(OnStep)` with handler `OnStep(Agent : agent) : void`
- B. `Plate.TriggeredEvent.Subscribe(OnStep)` with handler `OnStep(Agent : ?agent) : void`
- C. `Plate.TriggeredEvent.Await()` inside `OnBegin` with no handler method
- D. `Plate.OnTriggered.Subscribe(OnStep)` with handler `OnStep() : void`
Answer key
- A โ The device's full API surface is `Enable():void` and `Disable():void`. There are no Open/Close or Activate/Deactivate methods.
- B โ Event handler callbacks cannot be `<suspends>`. You must `spawn` a separate coroutine that is marked `<suspends>` and contains the `Sleep` call.
- B โ The destination island is an editor-only property set in the Details panel. The Verse API only controls whether the portal is enabled or disabled โ there is no runtime destination API.
- B โ The default value `matchmaking_portal_device{}` is a valid compile-time placeholder. Calls succeed but target that placeholder, not the placed device โ so the in-world portal never opens or closes.
- B โ `trigger_device.TriggeredEvent` is a `listenable(?agent)`, so the handler must accept `(Agent : ?agent)`. Using `agent` instead of `?agent` causes a type mismatch compile error.
Recap
One step closer to AweShucks Town Hub โ the Island That Launches the Island โ The five live portals โ one per zone capstone island โ enabled by the gatekeeper
Sources
- /guides/matchmaking-portal-device
Lesson 17: Ship It: Island Settings, Matchmaking Queues, and Publishing
Objectives
- Student can configure island settings, set matchmaking queue controls, and publish through Creator Portal so their hub is a real joinable Fortnite island.
Student can configure island settings, set matchmaking queue controls, and publish through Creator Portal so their hub is a real joinable Fortnite island.
๐ Builds on: grounding: configure-the-island-settings, matchmaking-portal-device, using-creator-portal (crawled Epic docs)
๐งฉ Your capstone piece: The published AweShucks Town island code that all portals and the site link to
Quiz
- Pre-publish checklist: your published hub's queue never fills and matchmaking seems dead. Which island settings, left at careless values, are the usual culprits?
- A. Island Matchmaking Privacy set to Private, Teams = Custom with Team Size at Dynamic, Minimum Players set too high, Overtime Player Target unreachable, and over-long Queue durations
- B. Spawn Immunity Time, Respawn Type, Fall Damage, Glider Redeploy, and Starting Health
- C. Only Max Players โ no other island setting can affect matchmaking
- D. The island's thumbnail and description, which matchmaking uses to rank islands
- What happens during the Queue Overtime Duration phase of matchmaking?
- A. The queue stops trying for Max Players and instead tries to start the game once the Overtime Player Target is reached
- B. Players already in the match get overtime bonus points
- C. The queue doubles Max Players to fill the island faster
- D. The game extends the current round by the overtime duration
- You call RoundSettings.EnableMatchmaking() in Verse but nothing changes in your playtest session. What is the most likely reason?
- A. EnableMatchmaking only applies to published games that have matchmaking turned on in the Island Settings
- B. EnableMatchmaking must be called before OnBegin runs
- C. The round_settings_device must be hidden before matchmaking calls work
- D. EnableMatchmaking requires an agent parameter to identify the host
- In the Creator Portal publishing flow, what is a private version code for?
- A. Playtesting your island yourself across platforms โ it is unmoderated and only you can use it; a Public Release is created from it later
- B. Sharing the island publicly with a select group of up to 100 players
- C. A backup code Epic uses to restore your island if the release fails review
- D. The final island code players use once the island passes moderation
- What is the recommended Teams setting for a social hub like AweShucks Town, and why?
- A. Free for All, because a social hub has no sides
- B. Custom with Team Size set to Dynamic, for flexible parties
- C. Squads, so friends always land together
- D. Solo, to prevent any player interaction
Answer key
- A โ Privacy = Private restricts joins to the host's party; Teams = Custom with Team Size left at Dynamic disables matchmaking outright per Epic's island settings docs; an unreachable Minimum Players cancels queues; and bad Overtime Player Target / queue duration values leave players stuck waiting. Cosmetic and combat settings never block matchmaking.
- A โ The queue runs in two phases: during Queue Main Duration it attempts a full match at Max Players; when that expires, the overtime phase tries to reach the Overtime Player Target instead. If even Minimum Players cannot be met, the queue cancels.
- A โ The digest documents this directly: EnableMatchmaking/DisableMatchmaking 'only applies to published games that have matchmaking turned on in the Island settings.' Verse controls when the gate opens, but Island Settings decide whether the gate exists โ and it only matters on a published island.
- A โ Clicking PUBLISH in UEFN creates a private version code โ not moderated, usable only by you, ideal for validating queue settings on different platforms. In the Creator Portal you then Create New Release, select that private version, pass ratings and review, and only then does the public island code go live.
- A โ The Island Settings table recommends Free for All for a hub-safe value, noting a hub has no sides. Teams = Custom with Team Size at Dynamic actually disables matchmaking entirely.
Recap
One step closer to AweShucks Town Hub โ the Island That Launches the Island โ The published AweShucks Town island code that all portals and the site link to
Sources
- /guides/publish-your-hub-island
Lesson 18: Capstone: AweShucks Town โ the Hub That Wires the Island Together
Objectives
- Student assembles every lesson into the full hub island: lobby loop, CTF event, quest, badge persistence, gated scene-graph portals, and live matchmaking portals โ then publishes it.
Student assembles every lesson into the full hub island: lobby loop, CTF event, quest, badge persistence, gated scene-graph portals, and live matchmaking portals โ then publishes it.
๐ Builds on: south-shores game_phase enum module + SpawnProp helper; north-jungle shared_helpers module + custom event bus; IslandProgress persistence schema shared island-wide
๐งฉ Your capstone piece: The whole capstone โ final integration and publish walkthrough
Quiz
- FINAL BOSS: a bug report says 'a paid portal opens for a badge-less player.' Which module and function do you inspect first?
- A. portal_gatekeeper.CheckAccess โ every booth's open-or-refuse decision flows through this one function
- B. ShellHuntKit.PhaseLabel โ it controls which portals are visible in each phase
- C. CodewoodKit.ScoreLine โ it formats the badge total shown at the booth
- D. IslandProgress.AddCoins โ coins are what unlock paid portals
- Why does OnBegin call Disable() on every matchmaking_portal_device before anything else?
- A. Enable/Disable is device-wide, so portals must start closed and only open after the gatekeeper verifies an interacting player
- B. Matchmaking portals crash if they are enabled before OnBegin finishes
- C. Disable() clears the portal's matchmaking queue from the previous session
- D. It is required before Subscribe can be called on the booth buttons
- In IslandProgress.AddCoins, why is a whole new island_badges instance built instead of writing `set Badges.TownCoins += Amount`?
- A. Persistable class values are immutable snapshots โ you persist a change by writing a NEW instance into the weak_map
- B. Verse forbids the += operator on int fields
- C. Rebuilding is faster than mutating a field in place
- D. weak_map entries can only be written once per session
- OnSouthBooth passes `false` where OnJungleBooth passes `option{IslandProgress.zone_badge.ShellHunt}`. What does that express?
- A. South Shores is a free booth (empty option = no badge required), while the jungle booth requires the Shell Hunt badge
- B. The south portal is disabled while the jungle portal is enabled
- C. `false` makes CheckAccess always refuse entry to South Shores
- D. It selects which team is allowed to use each portal
- What is RunLobbyRound's phase order, driven by ShellHuntKit.game_phase?
- A. Waiting -> Ready -> Launch -> Reset
- B. Ready -> Waiting -> Launch -> Reset
- C. Waiting -> Launch -> Ready -> Reset
- D. Launch -> Ready -> Waiting -> Reset
Answer key
- A โ The hub's design rule is that NO booth decides its own access: TryOpenPortal always asks portal_gatekeeper.CheckAccess(Player, Required). If a paid portal opened without a badge, either CheckAccess's logic regressed or the booth passed `false` (no requirement) instead of `option{zone_badge...}` โ both live in that one call path.
- A โ The digest surface for matchmaking_portal_device is just Enable()/Disable() โ there is no per-player variant. Starting every portal closed and opening only after portal_gatekeeper.CheckAccess passes is what makes the gate real; leave one enabled at start and badge-less players walk straight through.
- A โ Persistence follows the weak_map write: BadgeMap[Player] holds an immutable persistable value, so the update pattern is read the old snapshot, rebuild it with the changed field, and `set BadgeMap[Player] = NewValue`. That successful set IS the save.
- A โ TryOpenPortal takes `?IslandProgress.zone_badge`. The empty option (`false`) means 'no requirement' and CheckAccess returns true for everyone โ the free on-ramp. A filled option names the badge CheckAccess must find in the player's persistent island_badges profile.
- A โ RunLobbyRound calls SetPhase(Waiting) while it waits for MinPlayers, then SetPhase(Ready) while the LobbyTimer counts down, then SetPhase(Launch) to run the CTF round, and finally SetPhase(Reset) before looping again โ matching the enum's declared order.
Recap
One step closer to AweShucks Town Hub โ the Island That Launches the Island โ The whole capstone โ final integration and publish walkthrough
Sources
- /guides/awe-shucks-town-hub
Lesson 19: Reuse Your Modules Across Islands (What Epic Doesn't Teach)
Objectives
- Student can package the UI kit / save system / helper modules built in earlier zones and reuse them in a DIFFERENT island project โ building more advanced games from proven parts.
Student can package the UI kit / save system / helper modules built in earlier zones and reuse them in a DIFFERENT island project โ building more advanced games from proven parts.
๐ Builds on: south-shores ShellHuntKit; north-jungle CodewoodKit + UI widgets; east-volcano persistence save module
๐งฉ Your capstone piece: The hub island itself imports every zone kit โ proof the whole curriculum compounds
Quiz
- What must be true about a module's access specifiers for another island project to import it cleanly?
- A. The module itself AND every member the importing code calls must be marked <public>; anything without it stays internal and is invisible to the importer.
- B. Only the module declaration needs <public>; its members are exported automatically.
- C. Access specifiers only matter within one file โ a copied module is always fully visible.
- D. Every member must be marked <override> so the new project can replace it.
- Why must a portable module NEVER contain @editable device fields?
- A. Modules are not placed devices โ they have no Details panel to wire, so device references must enter as function parameters supplied by the importing island.
- B. @editable is a paid UEFN feature that not all projects have enabled.
- C. @editable fields are automatically deleted when a file is copied between projects.
- D. Modules can hold @editable fields as long as they are also marked <public>.
- You copied ShellHuntKit.verse into a new project. Which single line brings its exports into scope in another Verse file there?
- A. using { ShellHuntKit }
- B. import ShellHuntKit from "ShellHuntKit.verse"
- C. include <ShellHuntKit>
- D. module { ShellHuntKit }
- VolcanoSaveKit stores zone_save records in a weak_map(player, zone_save). How do you update ONE field of a player's persistable record?
- A. Rebuild a whole new record carrying the unchanged fields plus the new value, then write it back into the map โ persistable values are snapshots.
- B. Assign directly to the field: SaveMap[Player].BestScore = Score.
- C. Call SaveMap.Update(Player, Score) โ weak_map has a built-in field updater.
- D. Delete the player's entry and let the engine regenerate it with the new value.
- Why does CodewoodKit include its own `using { /Verse.org/Simulation }` line instead of relying on the host device file's imports?
- A. A module must carry its own dependencies โ the article warns never to assume the host file already imported what the module body needs.
- B. using lines are only valid inside module blocks, never at file scope.
- C. It is purely stylistic; the import would resolve fine even if removed.
- D. Simulation must be imported twice per file or the compiler throws a duplicate-symbol warning.
Answer key
- A โ Access specifiers are checked per declaration. A `<public>` module with a non-public member compiles fine at home, but the importing island gets a compile error the moment it calls the hidden member. Export the module and each member it exposes.
- A โ Only classes that are actually placed in a level (creative_device subclasses) surface @editable in the editor. A module has no instance in the level, so a device reference inside it can never be wired. Pass the device in as a parameter โ the new island supplies its own hardware.
- A โ `using { ShellHuntKit }` imports the module by name โ the same syntax you use for Epic's own modules like /Fortnite.com/Devices. After that line, its public members are callable unqualified (or fully qualified as ShellHuntKit.Member).
- A โ Persistable class values are immutable snapshots. RecordScore reads the existing record, constructs a fresh zone_save copying Version and applying the new BestScore, and writes the whole record back with `set SaveMap[Player] = ...` inside a failure context.
- A โ Rule 4 of the export checklist: if the module body calls Print or touches player, the module imports its own dependencies. The next island that carries the file may not import Simulation at all in its host file, so the kit cannot lean on it.
Recap
One step closer to AweShucks Town Hub โ the Island That Launches the Island โ The hub island itself imports every zone kit โ proof the whole curriculum compounds
Sources
- /guides/reuse-modules-across-islands
Generated by Verse Island on 2026-07-04.