Hey everyone, I need help on how to make a tower defense game in UEFN and I trying to make it like “Hero City Tower Defense,” which it’s created by vysena, but it’s different then that. So I got 30 trigger devices, 31 tracker devices which is 30 trackers for EliminateEnemies and 1 tracker for MainRound, 1 timer device for 30 second readyup, 3 end game devices which is 1 is EndRoundDevice to end the game at round 30, 1 is Exit Button from TitleScreenDialog, and 1 is End Game for game over, 30 cinematic sequence devices, and etc. So is there something that I did wrong here? So I’ll share my two verse scripts for you to look at, and let me know if you have any thoughts, tips, and advice, and I wish I can show you the whole video example, but it would aloud the upload being too big.
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
@editable
Rounds : \[\]cinematic_sequence_device = array{
\# 30 placeholder entries for assignment in UEFN
cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{},
cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{},
cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{},
cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{},
cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{},
cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{}
}
\# 30 levels prerounds
@editable
Levels : \[\]trigger_device = array{
\# 30 placeholder entries for assignment in UEFN
trigger_device{}, trigger_device{}, trigger_device{}, trigger_device{}, trigger_device{},
trigger_device{}, trigger_device{}, trigger_device{}, trigger_device{}, trigger_device{},
trigger_device{}, trigger_device{}, trigger_device{}, trigger_device{}, trigger_device{},
trigger_device{}, trigger_device{}, trigger_device{}, trigger_device{}, trigger_device{},
trigger_device{}, trigger_device{}, trigger_device{}, trigger_device{}, trigger_device{},
trigger_device{}, trigger_device{}, trigger_device{}, trigger_device{}, trigger_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{}
@editable
MainRound : tracker_device = tracker_device{}
@editable
EliminateEnemies : \[\]tracker_device = array{}
\# Stats
@editable
PointsStat : stat_creator_device = stat_creator_device{}
@editable
CoinsStat : stat_creator_device = stat_creator_device{}
@editable
ReadyUpTimer : 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
var EliminationCount : int = 0
var SpawnLimitsPerRound : \[\]spawn_limits = array{}
ReadyDuration : float = 30.0 # seconds (30 second)
var Players : \[\]player = array{}
var RoundActive : logic = false
var IsRoundActive : logic = false
var DialogShown : logic = false
var RoundStarted : logic = false
var DialogSubscription : ?cancelable = false
var CurrentRoundIndex : int = 0
var AgentsInZone : \[agent\]logic = map{}
var PlayerDialogSubscriptions : \[player\]cancelable = map{}
var ActiveDialogPlayer : ?player = false
OnBegin<override>()<suspends>:void=
\# Initialize spawn limits for each round
set SpawnLimitsPerRound = array{
spawn_limits{FastNPC := 30, MediumNPC := 10, StrongNPC := 3, DurationMinutes := 4.0}, # Round 1
spawn_limits{FastNPC := 60, MediumNPC := 20, StrongNPC := 5, DurationMinutes := 8.0}, # Round 2
spawn_limits{FastNPC := 100, MediumNPC := 30, StrongNPC := 8, DurationMinutes := 12.0}, # Round 3
spawn_limits{FastNPC := 140, MediumNPC := 40, StrongNPC := 10, DurationMinutes := 16.0}, # Round 4
spawn_limits{FastNPC := 180, MediumNPC := 50, StrongNPC := 12, DurationMinutes := 20.0}, # Round 5
spawn_limits{FastNPC := 230, MediumNPC := 60, StrongNPC := 15,DurationMinutes := 24.0}, # Round 6
spawn_limits{FastNPC := 270, MediumNPC := 75, StrongNPC := 18, DurationMinutes := 28.0}, # Round 7
spawn_limits{FastNPC := 315, MediumNPC := 90, StrongNPC := 20, DurationMinutes := 32.0}, # Round 8
spawn_limits{FastNPC := 360, MediumNPC := 105, StrongNPC := 22, DurationMinutes := 36.0}, # Round 9
spawn_limits{FastNPC := 400, MediumNPC := 120, StrongNPC := 24, DurationMinutes := 40.0}, # Round 10
spawn_limits{FastNPC := 440, MediumNPC := 130, StrongNPC := 26, DurationMinutes := 44.0}, # Round 11
spawn_limits{FastNPC := 480, MediumNPC := 140, StrongNPC := 28, DurationMinutes := 48.0}, # Round 12
spawn_limits{FastNPC := 520, MediumNPC := 150, StrongNPC := 30, DurationMinutes := 52.0}, # Round 13
spawn_limits{FastNPC := 560, MediumNPC := 160, StrongNPC := 32, DurationMinutes := 56.0}, # Round 14
spawn_limits{FastNPC := 600, MediumNPC := 175, StrongNPC := 35, DurationMinutes := 60.0}, # Round 15
spawn_limits{FastNPC := 640, MediumNPC := 190, StrongNPC := 38, DurationMinutes := 64.0}, # Round 16
spawn_limits{FastNPC := 680, MediumNPC := 205, StrongNPC := 40, DurationMinutes := 68.0}, # Round 17
spawn_limits{FastNPC := 720, MediumNPC := 220, StrongNPC := 42, DurationMinutes := 72.0}, # Round 18
spawn_limits{FastNPC := 760, MediumNPC := 235, StrongNPC := 44, DurationMinutes := 76.0}, # Round 19
spawn_limits{FastNPC := 800, MediumNPC := 250, StrongNPC := 46, DurationMinutes := 80.0}, # Round 20
spawn_limits{FastNPC := 840, MediumNPC := 265, StrongNPC := 48, DurationMinutes := 84.0}, # Round 21
spawn_limits{FastNPC := 880, MediumNPC := 280, StrongNPC := 50, DurationMinutes := 88.0}, # Round 22
spawn_limits{FastNPC := 920, MediumNPC := 295, StrongNPC := 52, DurationMinutes := 92.0}, # Round 23
spawn_limits{FastNPC := 960, MediumNPC := 310, StrongNPC := 54, DurationMinutes := 96.0}, # Round 24
spawn_limits{FastNPC := 1000, MediumNPC := 325, StrongNPC := 56, DurationMinutes := 100.0}, # Round 25
spawn_limits{FastNPC := 1040, MediumNPC := 340, StrongNPC := 58, DurationMinutes := 104.0}, # Round 26
spawn_limits{FastNPC := 1080, MediumNPC := 355, StrongNPC := 60, DurationMinutes := 108.0}, # Round 27
spawn_limits{FastNPC := 1120, MediumNPC := 370, StrongNPC := 62, DurationMinutes := 112.0}, # Round 28
spawn_limits{FastNPC := 1160, MediumNPC := 385, StrongNPC := 64, DurationMinutes := 116.0}, # Round 29
spawn_limits{FastNPC := 1200, MediumNPC := 400, StrongNPC := 66, DurationMinutes := 120.0} # Round 30
}
\# Save player list
set Players = GetPlayspace().GetPlayers()
\# Start: disable all spawners (idle)
FastNPCSpawner.Disable()
MediumNPCSpawner.Disable()
StrongNPCSpawner.Disable()
ReadyUpTimer.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)
\# Subscribe to ReadyUpTimer's SuccessEvent (fires when timer ends)
ReadyUpTimer.SuccessEvent.Subscribe(OnReadyUpTimerExpired)
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
if (ButtonIndex = 0):
\# When ready-up begins, keep round/spawners/timer disabled
FastNPCSpawner.Disable()
MediumNPCSpawner.Disable()
StrongNPCSpawner.Disable()
for (Cinematic : Rounds):
Cinematic.Stop()
ReadyUpTimer.Enable()
ReadyUpTimer.Start()
\# Optionally, show a UI message to notify the ready up countdown has started
\# Enable and show MainRound + EliminateEnemies for all players
for (Player : GetPlayspace().GetPlayers()):
MainRound.Assign(Player)
MainRound.SetTitleText(StringToMessage("Complete Rounds"))
MainRound.SetDescriptionText(StringToMessage("Complete all 30 rounds and defend the item from aliens."))
MainRound.SetValue(0) # Start at round 1
\# 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()
\# Reset camera for all players
for (Player : GetPlayspace().GetPlayers()):
FixedPointCamera.Disable()
TowerDefenseDialog.Hide(Player)
set PlayerDialogSubscriptions = map{}
\# Proceed with starting the game...
\# Subscribe trigger events for Levels
for (LevelTrigger : Levels):
LevelTrigger.TriggeredEvent.Subscribe(OnLevelTriggered)
OnLevelTriggered(Agent : ?agent): void =
\# Each time any Level trigger fires, update trackers
set CurrentRound += 1
MainRound.SetValue(CurrentRound)
\# Subscribe trigger events for Levels
for (LevelTrigger : Levels):
LevelTrigger.TriggeredEvent.Subscribe(OnLevelTriggered)
Print("Level trigger activated, updated round trackers.")
\# Completely resets all triggers and cinematics, then enables/plays just one of each
ActivateSingleRound(RoundNumber : int):void =
\# Disable ALL triggers
for (Trigger : Levels):
Trigger.Disable()
\# Stop ALL cinematics
for (Cine : Rounds):
Cine.Stop()
\# Enable the trigger for just this round if index is valid
if (RoundNumber > 0 and RoundNumber <= Levels.Length):
if (Level := Levels\[RoundNumber - 1\]):
Level.Enable()
\# Play the cinematic for just this round if index is valid
if (RoundNumber > 0 and RoundNumber <= Rounds.Length):
if (Cine := Rounds\[RoundNumber - 1\]):
Cine.Play()
\# Advances to the next round. Only one trigger/cinematic is ever active at a time (for round 1..30)
AdvanceRound():void =
if (CurrentRound < 30):
set CurrentRound += 1
ActivateSingleRound(CurrentRound)
EnableRoundDevices(RoundIndex:int):void =
\# Enable the trigger for this round
if (RoundIndex >= 0 and RoundIndex < Levels.Length):
if (Trigger := Levels\[RoundIndex\]):
Trigger.Enable()
\# Play the cinematic for this round
if (RoundIndex >= 0 and RoundIndex < Rounds.Length):
if (Cinematic := Rounds\[RoundIndex\]):
Cinematic.Play()
OnEliminateEnemiesComplete(Agent:agent):void =
for (Player : GetPlayspace().GetPlayers()):
for (Tracker : EliminateEnemies):
Tracker.SetValue(0)
Tracker.Assign(Player)
Tracker.Remove(Player)
Tracker.SetTitleText(StringToMessage("Alien Eliminations"))
Tracker.SetDescriptionText(StringToMessage("Eliminate 43 aliens from reaching the item"))
Tracker.CompleteEvent.Subscribe(OnEliminateEnemiesComplete)
Print("Tracker completed for agent!")
for (Tracker : EliminateEnemies):
Tracker.CompleteEvent.Subscribe(OnEliminateEnemiesComplete)
StartNewRound(): void =
\# Disable previous round if needed
\# Enable new round devices
EnableRoundDevices(CurrentRound - 1)
OnReadyUpTimerExpired(Agent: ?agent): void =
\# After 30 seconds, enable round timer and spawners
FastNPCSpawner.Enable()
MediumNPCSpawner.Enable()
StrongNPCSpawner.Enable()
for (Cinematic : Rounds):
Cinematic.Play()
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 (RoundActive = false):
set RoundActive = true
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.")
StringToMessage<localizes>(InString: string): message = "{InString}"
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 }
# Struct to hold spawn limits for each NPC type per round
spawn_limits := struct:
FastNPC : int
MediumNPC : int
StrongNPC : int
DurationMinutes : float
# Manages round-based gameplay similar to COD Zombies
round_system_manager := class(creative_device):
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 }
# Struct to hold spawn limits for each NPC type per round
spawn_limits := struct:
FastNPC : int
MediumNPC : int
StrongNPC : int
DurationMinutes : float
# Manages round-based gameplay similar to COD Zombies
round_system_manager := class(creative_device):
# Editable device references
# 30 levels prerounds
@editable
Levels : []trigger_device = array{
# 30 placeholder entries for assignment in UEFN
trigger_device{}, trigger_device{}, trigger_device{}, trigger_device{}, trigger_device{},
trigger_device{}, trigger_device{}, trigger_device{}, trigger_device{}, trigger_device{},
trigger_device{}, trigger_device{}, trigger_device{}, trigger_device{}, trigger_device{},
trigger_device{}, trigger_device{}, trigger_device{}, trigger_device{}, trigger_device{},
trigger_device{}, trigger_device{}, trigger_device{}, trigger_device{}, trigger_device{},
trigger_device{}, trigger_device{}, trigger_device{}, trigger_device{}, trigger_device{}
}
@editable
ReadyUpTimer : timer_device = timer_device{}
@editable
MainRound : tracker_device = tracker_device{}
@editable
EliminateEnemies : []tracker_device = array{}
@editable
EndRoundDevice : end_game_device = end_game_device{}
@editable
Rounds : []cinematic_sequence_device = array{
# 30 placeholder entries for assignment in UEFN
cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{},
cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{},
cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{},
cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{},
cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{},
cinematic_sequence_device{}, cinematic_sequence_device{}, cinematic_sequence_device{}, 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
TowerDefenseDialog : popup_dialog_device = popup_dialog_device{}
# Internal state
var CurrentRound : int = 0
var EliminationCount : int = 0
var SpawnLimitsPerRound : []spawn_limits = array{}
var LevelTriggered : []logic = array{}
var IsRoundActive : logic = false
var Players : []player = array{}
ReadyDuration : float = 30.0 # seconds (30 second)
# Initialize the round system
OnBegin<override>()<suspends> : void =
# Initialize spawn limits for each round
set SpawnLimitsPerRound = array{
spawn_limits{FastNPC := 30, MediumNPC := 10, StrongNPC := 4, DurationMinutes := 4.0}, # Round 1
spawn_limits{FastNPC := 60, MediumNPC := 20, StrongNPC := 5, DurationMinutes := 8.0}, # Round 2
spawn_limits{FastNPC := 100, MediumNPC := 30, StrongNPC := 8, DurationMinutes := 12.0}, # Round 3
spawn_limits{FastNPC := 140, MediumNPC := 40, StrongNPC := 10, DurationMinutes := 16.0}, # Round 4
spawn_limits{FastNPC := 180, MediumNPC := 50, StrongNPC := 12, DurationMinutes := 20.0}, # Round 5
spawn_limits{FastNPC := 230, MediumNPC := 60, StrongNPC := 15,DurationMinutes := 24.0}, # Round 6
spawn_limits{FastNPC := 270, MediumNPC := 75, StrongNPC := 18, DurationMinutes := 28.0}, # Round 7
spawn_limits{FastNPC := 315, MediumNPC := 90, StrongNPC := 20, DurationMinutes := 32.0}, # Round 8
spawn_limits{FastNPC := 360, MediumNPC := 105, StrongNPC := 22, DurationMinutes := 36.0}, # Round 9
spawn_limits{FastNPC := 400, MediumNPC := 120, StrongNPC := 24, DurationMinutes := 40.0}, # Round 10
spawn_limits{FastNPC := 440, MediumNPC := 130, StrongNPC := 26, DurationMinutes := 44.0}, # Round 11
spawn_limits{FastNPC := 480, MediumNPC := 140, StrongNPC := 28, DurationMinutes := 48.0}, # Round 12
spawn_limits{FastNPC := 520, MediumNPC := 150, StrongNPC := 30, DurationMinutes := 52.0}, # Round 13
spawn_limits{FastNPC := 560, MediumNPC := 160, StrongNPC := 32, DurationMinutes := 56.0}, # Round 14
spawn_limits{FastNPC := 600, MediumNPC := 175, StrongNPC := 35, DurationMinutes := 60.0}, # Round 15
spawn_limits{FastNPC := 640, MediumNPC := 190, StrongNPC := 38, DurationMinutes := 64.0}, # Round 16
spawn_limits{FastNPC := 680, MediumNPC := 205, StrongNPC := 40, DurationMinutes := 68.0}, # Round 17
spawn_limits{FastNPC := 720, MediumNPC := 220, StrongNPC := 42, DurationMinutes := 72.0}, # Round 18
spawn_limits{FastNPC := 760, MediumNPC := 235, StrongNPC := 44, DurationMinutes := 76.0}, # Round 19
spawn_limits{FastNPC := 800, MediumNPC := 250, StrongNPC := 46, DurationMinutes := 80.0}, # Round 20
spawn_limits{FastNPC := 840, MediumNPC := 265, StrongNPC := 48, DurationMinutes := 84.0}, # Round 21
spawn_limits{FastNPC := 880, MediumNPC := 280, StrongNPC := 50, DurationMinutes := 88.0}, # Round 22
spawn_limits{FastNPC := 920, MediumNPC := 295, StrongNPC := 52, DurationMinutes := 92.0}, # Round 23
spawn_limits{FastNPC := 960, MediumNPC := 310, StrongNPC := 54, DurationMinutes := 96.0}, # Round 24
spawn_limits{FastNPC := 1000, MediumNPC := 325, StrongNPC := 56, DurationMinutes := 100.0}, # Round 25
spawn_limits{FastNPC := 1040, MediumNPC := 340, StrongNPC := 58, DurationMinutes := 104.0}, # Round 26
spawn_limits{FastNPC := 1080, MediumNPC := 355, StrongNPC := 60, DurationMinutes := 108.0}, # Round 27
spawn_limits{FastNPC := 1120, MediumNPC := 370, StrongNPC := 62, DurationMinutes := 112.0}, # Round 28
spawn_limits{FastNPC := 1160, MediumNPC := 385, StrongNPC := 64, DurationMinutes := 116.0}, # Round 29
spawn_limits{FastNPC := 1200, MediumNPC := 400, StrongNPC := 66, DurationMinutes := 120.0} # Round 30
}
# Save player list
set Players = GetPlayspace().GetPlayers()
# Initialize first round
EnableCurrentLevel()
PlayCurrentRoundCinematic()
ActivateSingleRound(CurrentRound)
# Initial setup - disable all spawners and timers
FastNPCSpawner.Disable()
MediumNPCSpawner.Disable()
StrongNPCSpawner.Disable()
ReadyUpTimer.Disable()
# Subscribe to device events
ReadyUpTimer.SuccessEvent.Subscribe(OnReadyUpTimerComplete)
# Start with ready up for round 1
ReadyUpTimer.Enable()
ReadyUpTimer.Start() # Start Ready Up at game start
OnReadyUpTimerComplete(Agent:?agent):void =
# When ready up ends, update spawner limits and start round timer
UpdateSpawnerLimits()
OnReadyUpEnded(Agent : ?agent) : void =
# Subscribe to ReadyUpTimer
ReadyUpTimer.SuccessEvent.Subscribe(OnReadyUpEnded)
ReadyUpTimer.Start() # Start the 30s timer
# Enable and show MainRound + EliminateEnemies for all players
for (Player : GetPlayspace().GetPlayers()):
MainRound.Assign(Player)
MainRound.SetTitleText(StringToMessage("Complete Rounds"))
MainRound.SetDescriptionText(StringToMessage("Complete all 30 rounds and defend the item from aliens."))
MainRound.SetValue(0) # Start at round 1
for (Player : GetPlayspace().GetPlayers()):
MainRound.Remove(Player)
OnLevelTriggered(Agent : ?agent): void =
# Each time any Level trigger fires, update trackers
set CurrentRound += 1
MainRound.SetValue(CurrentRound)
# Subscribe trigger events for Levels
for (LevelTrigger : Levels):
LevelTrigger.TriggeredEvent.Subscribe(OnLevelTriggered)
Print("Level trigger activated, updated round trackers.")
# Completely resets all triggers and cinematics, then enables/plays just one of each
ActivateSingleRound(RoundNumber : int):void =
# Disable ALL triggers
for (Trigger : Levels):
Trigger.Disable()
# Stop ALL cinematics
for (Cine : Rounds):
Cine.Stop()
# Enable the trigger for just this round if index is valid
if (RoundNumber > 0 and RoundNumber <= Levels.Length):
if (Level := Levels[RoundNumber - 1]):
Level.Enable()
# Play the cinematic for just this round if index is valid
if (RoundNumber > 0 and RoundNumber <= Rounds.Length):
if (Cine := Rounds[RoundNumber - 1]):
Cine.Play()
# Advances to the next round. Only one trigger/cinematic is ever active at a time (for round 1..30)
AdvanceRound():void =
if (CurrentRound < 30):
set CurrentRound += 1
ActivateSingleRound(CurrentRound)
# Enable only the current round's level trigger, disable others
EnableCurrentLevel():void =
for (Index -> Level : Levels):
if (Index = CurrentRound - 1):
Level.Enable()
else:
Level.Disable()
# Play the cinematic for the current round if it exists
PlayCurrentRoundCinematic():void =
if (CurrentRound - 1 >= 0 and CurrentRound - 1 < Rounds.Length):
if (CurrentCinematic := Rounds[CurrentRound - 1]):
CurrentCinematic.Play()
EnableRoundDevices(RoundIndex:int):void =
# Enable the trigger for this round
if (RoundIndex >= 0 and RoundIndex < Levels.Length):
if (Trigger := Levels[RoundIndex]):
Trigger.Enable()
# Play the cinematic for this round
if (RoundIndex >= 0 and RoundIndex < Rounds.Length):
if (Cinematic := Rounds[RoundIndex]):
Cinematic.Play()
OnEliminateEnemiesComplete(Agent:agent):void =
for (Player : GetPlayspace().GetPlayers()):
for (Tracker : EliminateEnemies):
Tracker.SetValue(0)
Tracker.Assign(Player)
Tracker.Remove(Player)
Tracker.SetTitleText(StringToMessage("Alien Eliminations"))
Tracker.SetDescriptionText(StringToMessage("Eliminate 43 aliens from reaching the item"))
Tracker.CompleteEvent.Subscribe(OnEliminateEnemiesComplete)
Print("Tracker completed for agent!")
for (Tracker : EliminateEnemies):
Tracker.CompleteEvent.Subscribe(OnEliminateEnemiesComplete)
# Handle ready up phase completion
OnReadyUpComplete(Agent : ?agent) : void =
if (IsRoundActive = false):
StartRound()
EndRound()
# Call this when starting a new round
StartNewRound():void =
if (CurrentRound < 30):
set CurrentRound += 1
EnableCurrentLevel()
PlayCurrentRoundCinematic()
# Start a new round
StartRound() : void =
set IsRoundActive = true
set CurrentRound += 1
# Update round tracker
MainRound.SetValue(CurrentRound)
# 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
for (Cinematic : Rounds):
Cinematic.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.")
# Spawn enemies scaled to current round
SpawnEnemiesForRound() : void =
# Calculate spawn counts based on round
var FastCount : int = 30 + CurrentRound
var MediumCount : int = 10 + CurrentRound
var StrongCount : int = 0
if (CurrentRound >= 3):
set StrongCount = CurrentRound - 1
# Spawn enemies
for (Index := 10..FastCount):
FastNPCSpawner.Spawn()
for (Index := 5..MediumCount):
MediumNPCSpawner.Spawn()
for (Index := 2..StrongCount):
StrongNPCSpawner.Spawn()
OnDialogButtonPressed(Agent: agent, ButtonIndex: int): void =
if (ButtonIndex = 0):
# When ready-up begins, keep round/spawners/timer disabled
FastNPCSpawner.Disable()
MediumNPCSpawner.Disable()
StrongNPCSpawner.Disable()
for (Cinematic : Rounds):
Cinematic.Stop()
# Set durations to avoid incorrect settings
ReadyUpTimer.SetMaxDuration(30.0) # 30 seconds ready-up
ReadyUpTimer.Enable()
ReadyUpTimer.Start()
OnReadyUpTimerExpired(Agent: ?agent): void =
# After 30 seconds, enable round timer and spawners
FastNPCSpawner.Enable()
MediumNPCSpawner.Enable()
StrongNPCSpawner.Enable()
for (Cinematic : Rounds):
Cinematic.Play()
set IsRoundActive = true
set CurrentRound += 1
MainRound.SetValue(CurrentRound)
# Update round tracker and reset eliminations for all players
set CurrentRound += 1
for (Player : GetPlayspace().GetPlayers()):
MainRound.SetValue(CurrentRound)
# 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 the game if this is Level 30
if (CurrentRound = 30):
RoundPlayers := GetPlayspace().GetPlayers()
if (Players.Length > 0):
if (FirstPlayer := Players[0]):
EndRoundDevice.Activate(FirstPlayer)
else:
# Start ready up phase for next round
# Reset timers and start ReadyUp again
ReadyUpTimer.Reset()
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()
# Update spawner limits based on current round
UpdateSpawnerLimits():void =
if (CurrentRound <= SpawnLimitsPerRound.Length):
if (Limits := SpawnLimitsPerRound[CurrentRound - 1]):
# Enable/disable spawners based on limits
if (Limits.FastNPC > 0):
FastNPCSpawner.Enable()
else:
FastNPCSpawner.Disable()
if (Limits.MediumNPC > 0):
MediumNPCSpawner.Enable()
else:
MediumNPCSpawner.Disable()
if (Limits.StrongNPC > 0):
StrongNPCSpawner.Enable()
else:
StrongNPCSpawner.Disable()
StringToMessage<localizes>(InString: string): message = "{InString}"