Hey everyone, do you all know what UEFN devices did vysena use for swisscom hero city tower defense? I’m trying to make a round system for a project I’m working, it’s called 20ALIENZ TOWER DEFENSE, it’s where players have to be on the turrets to shot the enemies before reaching the precious item and they have to survive 30 rounds. So I’ll share two verse codes and I share the video example of what I’m trying to do and what went wrong, and let me if y’all have any helpful thoughts, tips, and advices. It would be really helpful, because I’ve tried to make it exactly like swisscom hero city tower defense and COD zombie wave system, it’s going wrong.
tower_defense_manager script:
using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /UnrealEngine.com/Temporary/UI }
using { /Fortnite.com/UI }
# Device to manage tower defense rounds and settings
tower_defense_manager := class(creative_device):
\# Dialogs & Round infrastructure
@editable
TowerDefenseDialog : popup_dialog_device = popup_dialog_device{}
@editable
GateSequencer : cinematic_sequence_device = cinematic_sequence_device{}
\# NPC Spawners
@editable
FastNPCSpawner : npc_spawner_device = npc_spawner_device{}
@editable
MediumNPCSpawner : npc_spawner_device = npc_spawner_device{}
@editable
StrongNPCSpawner : npc_spawner_device = npc_spawner_device{}
\# Stats
@editable
PointsStat : stat_creator_device = stat_creator_device{}
@editable
CoinsStat : stat_creator_device = stat_creator_device{}
@editable
RoundTimer : timer_device = timer_device{}
@editable
FixedPointCamera : gameplay_camera_fixed_point_device = gameplay_camera_fixed_point_device{}
@editable
ThirdPersonControls : gameplay_controls_third_person_device = gameplay_controls_third_person_device{}
@editable
Objective : objective_device = objective_device{}
@editable
Player1Spawner : player_spawner_device = player_spawner_device{}
@editable
Player2Spawner : player_spawner_device = player_spawner_device{}
@editable
Player3Spawner : player_spawner_device = player_spawner_device{}
@editable
Player4Spawner : player_spawner_device = player_spawner_device{}
\# Round timer and logic
var CurrentRound : int = 1
TotalRounds : int = 30
RoundDuration : float = 300.0 # seconds (5 min)
var RoundActive : logic = false
var IsRoundActive : logic = false
var DialogShown : logic = false
var RoundStarted : logic = false
var DialogSubscription : ?cancelable = false
\# Array of round settings devices for 30 rounds
var RoundSettings : \[\]round_settings_device = array{}
var CurrentRoundIndex : int = 0
var AgentsInZone : \[agent\]logic = map{}
var PlayerDialogSubscriptions : \[player\]cancelable = map{}
var ActiveDialogPlayer : ?player = false
OnBegin<override>()<suspends>:void=
\# Start: disable all spawners (idle)
FastNPCSpawner.Disable()
MediumNPCSpawner.Disable()
StrongNPCSpawner.Disable()
\# Subscribe to the 1Player spawner's spawn event
Player1Spawner.SpawnedEvent.Subscribe(OnPlayer1Spawned)
Player2Spawner.SpawnedEvent.Subscribe(OnOtherPlayerSpawned)
Player3Spawner.SpawnedEvent.Subscribe(OnOtherPlayerSpawned)
Player4Spawner.SpawnedEvent.Subscribe(OnOtherPlayerSpawned)
\# Subscribe to dialog button interaction
TowerDefenseDialog.RespondingButtonEvent.Subscribe(OnTowerDefenseButtonPressed)
\# Cancel and hide timer at game start—do this FIRST no matter what!
Self.RoundTimer.Reset()
Self.RoundTimer.Disable()
set Self.DialogSubscription = option{Self.TowerDefenseDialog.RespondingButtonEvent.Subscribe(Self.OnDialogButtonPressed)}
\# Notice: No call to Self.TowerDefenseDialog.Show() or Enable() here
OnOtherPlayerSpawned(Player: agent): void =
Print("Other player spawned, dialog not shown and not subscribed.")
OnPlayer1Spawned(Player: agent): void =
\# Show dialog and subscribe only for the player spawned from 1Player spawner
if (FirstPlayer := player\[Player\]):
TowerDefenseDialog.Show(FirstPlayer)
Subscription := TowerDefenseDialog.RespondingButtonEvent.Subscribe(OnDialogButtonPressed)
if (set PlayerDialogSubscriptions\[FirstPlayer\] = Subscription) {}
set ActiveDialogPlayer = option{FirstPlayer}
Print("Waiting for round start...")
OnDialogButtonPressed(Agent:agent, ButtonIndex:int):void =
if (ActivePlayer := ActiveDialogPlayer?, Agent = ActivePlayer, ButtonIndex = 0):
if (ButtonIndex = 0 and Self.RoundStarted = false):
set Self.RoundStarted = true
if (DialogSub := Self.DialogSubscription?):
DialogSub.Cancel()
set Self.DialogSubscription = false
\# Dismiss dialog and clean up only for the active player
\# Only the 1Player spawner player can trigger start logic!
if (Subscription := PlayerDialogSubscriptions\[ActivePlayer\]):
Subscription.Cancel()
TowerDefenseDialog.Hide(ActivePlayer)
set PlayerDialogSubscriptions = map{}
set ActiveDialogPlayer = false
Print("Game Started!")
\# Proceed with starting the game...
\# Switch camera back to Fortnite's default third-person view
Self.FixedPointCamera.Disable()
Self.ThirdPersonControls.Enable()
Self.RoundTimer.Enable()
Self.RoundTimer.Reset()
Self.RoundTimer.Start()
\# Reset camera for all players
for (Player : GetPlayspace().GetPlayers()):
FixedPointCamera.Disable()
spawn{HandleTimerExpiration()}
TowerDefenseDialog.Hide(Player)
set PlayerDialogSubscriptions = map{}
\# Proceed with starting the game...
\# Create 30 round settings devices and store them in the array
set RoundSettings = for (Index := 0..29):
round_settings_device{}
\# Example: Configure each round's settings
for (RoundIndex -> RoundDevice : RoundSettings):
\# Set up round-specific settings here
\# For example, you could enable/disable certain features per round
\# or configure round-specific rules
Print("Configured round {RoundIndex + 1}")
\# Example function to get a specific round's settings
GetRoundDevice(RoundIndex : int) : void =
if (RoundDevice := RoundSettings\[RoundIndex\]):
RoundDevice
\# Call this when a round ends
HandleRoundEnd() : void =
for (Player : GetPlayspace().GetPlayers()):
if (RoundDevice := RoundSettings\[CurrentRoundIndex\]):
Players := GetPlayspace().GetPlayers()
if (FirstPlayer := Players\[0\]):
RoundDevice.EndRound(FirstPlayer) # Pass agent type!
HandleTimerExpiration()<suspends>:void =
Self.RoundTimer.SuccessEvent.Await()
Self.RoundTimer.Reset()
Self.RoundTimer.Disable()
\# Implementation for timer expiration handling
Print("Timer expired")
Players := Self.GetPlayspace().GetPlayers()
if (Players.Length > 0):
\# Start the configured round timer (automatically shows on player HUD)
Self.RoundTimer.Start()
\# Wait for the timer to complete
Self.RoundTimer.SuccessEvent.Await()
\# After the timer finishes, end the round for all players
else:
Print("Can't find player")
OnTowerDefenseButtonPressed(Data:tuple(agent, int)):void=
ButtonIndex := Data(1)
case(ButtonIndex):
\# 0: Start round, enable all spawners, move players, keep dialog shown
0 => HandleStartRound()
\_ => Print("Unknown button pressed")
HandleStartRound():void=
if (CurrentRound > TotalRounds):
Print("All rounds complete.")
return
if (RoundActive = false):
set RoundActive = true
FastNPCSpawner.Enable()
MediumNPCSpawner.Enable()
StrongNPCSpawner.Enable()
FastNPCSpawner.Spawn()
MediumNPCSpawner.Spawn()
StrongNPCSpawner.Spawn()
GateSequencer.Play()
Print("Round {CurrentRound} started.")
\# Move all players to new location & keep dialog shown
for (Player : GetPlayspace().GetPlayers()):
if (Character := Player.GetFortCharacter\[\]):
TargetLocation := vector3{X := 1000.0, Y := 1000.0, Z := 200.0}
if (Character.TeleportTo\[TargetLocation, rotation{}\]):
Print("Moved player to target location at round start.")
\# Start timer for this round
spawn{RoundTimerLoop()}
\# Each round lasts 5 min, up to 30 rounds; stops spawners & activates EndRound each time.
RoundTimerLoop()<suspends>:void=
Print("Started round timer for {RoundDuration} seconds.")
Sleep(RoundDuration)
set RoundActive = false
\# Stop spawners
FastNPCSpawner.Disable()
MediumNPCSpawner.Disable()
StrongNPCSpawner.Disable()
Print("Round {CurrentRound} ended.")
for (Player : GetPlayspace().GetPlayers()):
if (CurrentRoundIndex = 29):
\# RoundSettings: notify round end
for (Player : GetPlayspace().GetPlayers()):
if (RoundDevice := RoundSettings\[CurrentRoundIndex\]):
RoundDevice.EndRound(Player) # Pass Player (agent)
set CurrentRound += 1
round_system_manager script:
using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /UnrealEngine.com/Temporary/UI }
using { /Fortnite.com/UI }
# Manages round-based gameplay similar to COD Zombies
round_system_manager := class(creative_device):
\# Editable device references
@editable
Levels : \[\]trigger_device = array{}
@editable
ReadyUpTimer : timer_device = timer_device{}
@editable
MainRound : tracker_device = tracker_device{}
@editable
EliminateEnemies : tracker_device = tracker_device{}
@editable
EndRoundDevice : end_game_device = end_game_device{}
@editable
Rounds : \[\]round_settings_device = array{}
@editable
GateSequencer : cinematic_sequence_device = cinematic_sequence_device{}
@editable
FastNPCSpawner : npc_spawner_device = npc_spawner_device{}
@editable
MediumNPCSpawner : npc_spawner_device = npc_spawner_device{}
@editable
StrongNPCSpawner : npc_spawner_device = npc_spawner_device{}
@editable
RoundTimer : timer_device = timer_device{}
\# Internal state
var CurrentRound : int = 0
var IsRoundActive : logic = false
\# Initialize the round system
OnBegin<override>()<suspends> : void =
\# Initial setup - disable all spawners and timers
FastNPCSpawner.Disable()
MediumNPCSpawner.Disable()
StrongNPCSpawner.Disable()
RoundTimer.Reset()
RoundTimer.Disable()
\# Subscribe to device events
RoundTimer.SuccessEvent.Subscribe(OnRoundTimerComplete)
ReadyUpTimer.SuccessEvent.Subscribe(OnReadyUpComplete)
EliminateEnemies.CompleteEvent.Subscribe(OnAllEnemiesEliminated)
\# Start first round
StartNextRound()
\# Handle ready up phase completion
OnReadyUpComplete(Agent : ?agent) : void =
if (IsRoundActive = false):
StartRound()
\# Start a new round
StartRound() : void =
set IsRoundActive = true
set CurrentRound += 1
\# Update round tracker
MainRound.SetValue(CurrentRound)
\# Start round timer
RoundTimer.Start()
\# Spawn enemies based on round
SpawnEnemiesForRound()
\# Enable and spawn NPCs
FastNPCSpawner.Enable()
MediumNPCSpawner.Enable()
StrongNPCSpawner.Enable()
FastNPCSpawner.Spawn()
MediumNPCSpawner.Spawn()
StrongNPCSpawner.Spawn()
\# Play round start cinematic
GateSequencer.Play()
\# Teleport players to start position
for (Player : GetPlayspace().GetPlayers()):
if (Character := Player.GetFortCharacter\[\]):
TargetLocation := vector3{X := 1000.0, Y := 1000.0, Z := 200.0}
if (Character.TeleportTo\[TargetLocation, rotation{}\]):
Print("Moved player to target location at round start.")
\# Start round timer
RoundTimer.Enable()
RoundTimer.Reset()
RoundTimer.Start()
\# Spawn enemies scaled to current round
SpawnEnemiesForRound() : void =
\# Calculate spawn counts based on round
var FastCount : int = 50 + CurrentRound
var MediumCount : int = 10 + CurrentRound
var StrongCount : int = 0
if (CurrentRound >= 5):
set StrongCount = CurrentRound - 1
\# Spawn enemies
for (Index := 50..FastCount):
FastNPCSpawner.Spawn()
for (Index := 10..MediumCount):
MediumNPCSpawner.Spawn()
for (Index := 1..StrongCount):
StrongNPCSpawner.Spawn()
\# Handle round timer completion
OnRoundTimerComplete(Agent : ?agent) : void =
if (IsRoundActive = true):
EndRound()
\# Handle all enemies eliminated
OnAllEnemiesEliminated(Agent : agent) : void =
if (IsRoundActive = true):
EndRound()
\# End the current round
EndRound() : void =
set IsRoundActive = false
\# Stop all spawners
FastNPCSpawner.DespawnAll(false)
MediumNPCSpawner.DespawnAll(false)
StrongNPCSpawner.DespawnAll(false)
\# Check if game should end
if (CurrentRound >= Rounds.Length):
EndGame()
\# Disable all spawners
FastNPCSpawner.Disable()
MediumNPCSpawner.Disable()
StrongNPCSpawner.Disable()
\# End round for all players
for (Player : GetPlayspace().GetPlayers()):
if (RoundDevice := Rounds\[CurrentRound - 1\]):
RoundDevice.EndRound(Player)
\# Check if game should end
if (CurrentRound >= 30):
if (FirstPlayer := GetPlayspace().GetPlayers()\[0\]):
EndRoundDevice.Activate(FirstPlayer)
else:
\# Start ready up phase for next round
ReadyUpTimer.Start()
\# End the game
EndGame() : void =
\# Activate end game device for the first player
if (FirstPlayer := GetPlayspace().GetPlayers()\[0\]):
EndRoundDevice.Activate(FirstPlayer)
\# Start next round
StartNextRound() : void =
ReadyUpTimer.Start()