using { /Fortnite.com/Devices } using { /Fortnite.com/Characters } using { /Fortnite.com/Playspaces } using { /Verse.org/Simulation } using { /UnrealEngine.com/Temporary/Diagnostics } using { /UnrealEngine.com/Temporary/SpatialMath } # ----------------------------------------------------------------------------- # concert_manager — Indie Island tutorial 01: Live concert # # Runs a timed show: an intro cinematic, everyone teleported to the venue, a # list of cues (audio, lights, props, VFX, post-process looks), and everyone # teleported back when the set is over. # # Setup (Details panel): # 1. ShowStartTrigger: the trigger that starts the show. Wire the same device # into live_event_manager's StartTrigger so the countdown starts it. # 2. Add Cues in chronological order (lowest DelaySeconds first). # 3. Set ShowDurationSeconds to the REAL length of the whole set. # 4. Wire ConcertRespawner (a spawner at the venue) and DefaultSpawners # (every normal spawner). Empty fields silently do nothing. # 5. Place a prop at the venue landing spot (ConcertSpawnPoint) and one in the # world (ReturnSpawnPoint). # 6. Beacons to hide during the show go in HideBeacons. For beacons that are # managed by a loop (dropper_manager), wire that device's # HideMarkersTrigger to ShowStartTrigger and ShowMarkersTrigger to # ConcertEndTrigger instead. # ----------------------------------------------------------------------------- # One moment in the show. show_cue := class: # Seconds after the show starts that this cue fires. @editable DelaySeconds : float = 0.0 # Audio players to start / stop. One Audio Player per track, # with "Sync Player Audio" turned ON. @editable PlayAudio : []audio_player_device = array{} @editable StopAudio : []audio_player_device = array{} # Props that appear / disappear. @editable ShowProps : []creative_prop = array{} @editable HideProps : []creative_prop = array{} # Extra triggers for anything wired up in the editor (Details -> Events). @editable Triggers : []trigger_device = array{} # Post-process look for this moment. The previous look blends out first. @editable PostProcess : ?post_process_device = false # Lights on / off. Name lights by position and subject (Key_Left_Vocalist). @editable LightsOn : []customizable_light_device = array{} @editable LightsOff : []customizable_light_device = array{} # VFX on / off. @editable VFXOn : []vfx_spawner_device = array{} @editable VFXOff : []vfx_spawner_device = array{} concert_manager := class(creative_device): # Starts the show when triggered. @editable ShowStartTrigger : trigger_device = trigger_device{} # Optional button to force the show to start (for rehearsals). # Remove or hide it before you publish. @editable ForceStartButton : button_device = button_device{} # Ambient audio that stops when the show starts and resumes afterwards. @editable MuteOnShowStart : []audio_player_device = array{} # Fires when the show is over (after ShowDurationSeconds). @editable ConcertEndTrigger : trigger_device = trigger_device{} # All cues, in chronological order. @editable Cues : []show_cue = array{} # Total length of the real set, in seconds (all songs together). # The show ends on THIS, not on the last cue -- the last cue firing and the # music finishing are different moments. @editable ShowDurationSeconds : float = 600.0 # Optional: triggers wired to a Day Sequence device (night for the show, # day again afterwards). @editable DaySequenceTrigger : trigger_device = trigger_device{} @editable DaySequenceEndTrigger : trigger_device = trigger_device{} # Cinematic before the teleport (e.g. an arrival flythrough), and one that # plays at the venue before the cues. Add a streaming source to the camera, # or areas far from players will render empty. @editable IntroSequence : cinematic_sequence_device = cinematic_sequence_device{} @editable ShowSequence : cinematic_sequence_device = cinematic_sequence_device{} # Props marking where players land at the venue, and where they go back to. @editable ConcertSpawnPoint : creative_prop = creative_prop{} @editable ReturnSpawnPoint : creative_prop = creative_prop{} # Spawner at the venue, only enabled during the show, so anyone who dies # mid-show respawns at the stage. @editable ConcertRespawner : player_spawner_device = player_spawner_device{} # Your normal spawners, switched off during the show. @editable DefaultSpawners : []player_spawner_device = array{} # Beacons to hide during the show. @editable HideBeacons : []beacon_device = array{} var ShowStarted : logic = false var ActivePostProcess : ?post_process_device = false OnBegin() : void = for (Cue : Cues): if (Look := Cue.PostProcess?): Look.Disable() # A newly placed spawner is enabled by default -- switch the venue one off. ConcertRespawner.Disable() ShowStartTrigger.TriggeredEvent.Subscribe(OnShowStart) ForceStartButton.InteractedWithEvent.Subscribe(OnForceStart) OnForceStart(Agent : agent) : void = ShowStartTrigger.Trigger() OnShowStart(Agent : ?agent) : void = if (ShowStarted = false): set ShowStarted = true DaySequenceTrigger.Trigger() for (B : HideBeacons): B.Disable() # Swap spawners on both sides. ConcertRespawner.Enable() for (S : DefaultSpawners): S.Disable() for (A : MuteOnShowStart): A.Stop() spawn { RunTransitionAndShow() } RunTransitionAndShow() : void = IntroSequence.Play() IntroSequence.StoppedEvent.Await() TeleportEveryoneTo(ConcertSpawnPoint) ShowSequence.Play() RunShow() # GetFortCharacter[] fails silently for anyone mid-respawn, and that player is # skipped. The venue spawner above is what catches them. TeleportEveryoneTo(Marker : creative_prop) : void = Where := Marker.GetTransform() for (Player : GetPlayspace().GetPlayers()): if (FortChar := Player.GetFortCharacter[]): if (FortChar.TeleportTo[Position := Where.Translation, Rotation := Where.Rotation]) {} RunShow() : void = Print("[concert_manager] Show started -- {Cues.Length} cues") var ElapsedSeconds : float = 0.0 for (Cue : Cues): WaitTime := Cue.DelaySeconds - ElapsedSeconds if (WaitTime > 0.0): Sleep(WaitTime) FireCue(Cue) set ElapsedSeconds = Cue.DelaySeconds # Wait out the rest of the set, measured from ShowDurationSeconds. RemainingSeconds := ShowDurationSeconds - ElapsedSeconds if (RemainingSeconds > 0.0): Sleep(RemainingSeconds) OnShowEnd() OnShowEnd() : void = Print("[concert_manager] Show over") ConcertEndTrigger.Trigger() DaySequenceEndTrigger.Trigger() for (B : HideBeacons): B.Enable() ConcertRespawner.Disable() for (S : DefaultSpawners): S.Enable() for (A : MuteOnShowStart): A.Play() if (OldLook := ActivePostProcess?): OldLook.BlendOutForAll() set ActivePostProcess = false TeleportEveryoneTo(ReturnSpawnPoint) FireCue(Cue : show_cue) : void = for (A : Cue.PlayAudio): A.Play() for (A : Cue.StopAudio): A.Stop() for (P : Cue.ShowProps): P.Show() for (P : Cue.HideProps): P.Hide() for (T : Cue.Triggers): T.Trigger() for (L : Cue.LightsOn): L.TurnOn() for (L : Cue.LightsOff): L.TurnOff() for (V : Cue.VFXOn): V.Enable() for (V : Cue.VFXOff): V.Disable() # Blend the old look out BEFORE the new one in, or they stack. if (NewLook := Cue.PostProcess?): if (OldLook := ActivePostProcess?): OldLook.BlendOutForAll() NewLook.Enable() NewLook.BlendInForAll() set ActivePostProcess = option{NewLook}