aboutsummaryrefslogtreecommitdiff
path: root/Northstar.CustomServers/mod/scripts/vscripts/mp
diff options
context:
space:
mode:
authorBobTheBob <32057864+BobTheBob9@users.noreply.github.com>2022-03-03 20:54:59 +0000
committerBobTheBob <32057864+BobTheBob9@users.noreply.github.com>2022-03-03 20:54:59 +0000
commit81809dfba9a8329f07d68d1fd66133161234effa (patch)
tree94c6571b4be52dbd03435ffee478a41e8c062922 /Northstar.CustomServers/mod/scripts/vscripts/mp
parentfd0c66f5c84b7b6c349c3362c3e6df555c393aa5 (diff)
downloadNorthstarMods-81809dfba9a8329f07d68d1fd66133161234effa.tar.gz
NorthstarMods-81809dfba9a8329f07d68d1fd66133161234effa.zip
spectator refactor and private match spectator support
Diffstat (limited to 'Northstar.CustomServers/mod/scripts/vscripts/mp')
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype_mp.gnut142
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut7
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_no_intro.gnut14
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut4
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_spectator.gnut223
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_wargames.nut7
6 files changed, 261 insertions, 136 deletions
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype_mp.gnut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype_mp.gnut
index 453e35ec..d22f2627 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype_mp.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_base_gametype_mp.gnut
@@ -31,12 +31,6 @@ struct {
void function BaseGametype_Init_MPSP()
{
AddSpawnCallback( "info_intermission", SetIntermissionCamera )
- AddCallback_EntitiesDidLoad( SetSpecCams )
-
- RegisterSignal( "ObserverTargetChanged" )
- AddClientCommandCallback( "spec_next", ClientCommandCallback_spec_next )
- AddClientCommandCallback( "spec_prev", ClientCommandCallback_spec_prev )
- AddClientCommandCallback( "spec_mode", ClientCommandCallback_spec_mode )
AddPostDamageCallback( "player", AddToTitanDamageStat )
AddPostDamageCallback( "npc_titan", AddToTitanDamageStat )
@@ -50,19 +44,6 @@ void function SetIntermissionCamera( entity camera )
file.intermissionCamera = camera
}
-void function SetSpecCams()
-{
- // spec cams are called spec_cam1,2,3 etc by default, so this is the easiest way to get them imo
- int camNum = 1
- entity lastCam = null
- do {
- lastCam = GetEnt( "spec_cam" + camNum++ )
-
- if ( lastCam != null )
- file.specCams.append( lastCam )
- } while ( lastCam != null )
-}
-
void function CodeCallback_OnClientConnectionStarted( entity player )
{
// not a real player?
@@ -173,6 +154,17 @@ void function CodeCallback_OnClientConnectionCompleted( entity player )
svGlobal.levelEnt.Signal( "PlayerDidSpawn", { player = player } )
+ if ( GetConVarBool( "ns_allow_spectators" ) )
+ {
+ if ( IsPrivateMatchSpectator( player ) )
+ {
+ InitialisePrivateMatchSpectatorPlayer( player )
+ return
+ }
+ }
+ else
+ player.SetPersistentVar( "privateMatchState", 0 )
+
// handle spawning late joiners
if ( GetGameState() == eGameState.Playing )
{
@@ -492,118 +484,6 @@ void function RespawnAsTitan( entity player, bool manualPosition = false )
}
-// spectator stuff
-
-void function PlayerBecomesSpectator( entity player )
-{
- player.StartObserverMode( OBS_MODE_CHASE )
- player.StopPhysics()
-
- player.EndSignal( "OnRespawned" )
- player.EndSignal( "OnDestroy" )
- player.EndSignal( "PlayerRespawnStarted" )
-
- int targetIndex = 0
-
- OnThreadEnd( function() : ( player )
- {
- if ( IsValid( player ) )
- player.StopObserverMode()
- })
-
- while ( true )
- {
- table result = player.WaitSignal( "ObserverTargetChanged" )
-
- array<entity> targets
-
- targets.append( file.intermissionCamera )
- foreach( entity cam in file.specCams )
- targets.append( cam )
-
- array<entity> targetPlayers
- if ( IsFFAGame() )
- targetPlayers = GetPlayerArray_Alive()
- else
- targetPlayers = GetPlayerArrayOfTeam_Alive( player.GetTeam() )
-
- foreach( entity player in targetPlayers )
- targets.append( player )
-
- if ( result.next )
- targetIndex = ( targetIndex + 1 ) % targets.len()
- else
- {
- if ( targetIndex == 0 )
- targetIndex = ( targets.len() - 1 )
- else
- targetIndex--
- }
-
- if ( targetIndex >= targets.len() )
- targetIndex = 0
-
- entity target = targets[ targetIndex ]
-
- player.StopObserverMode()
- if ( player.IsWatchingSpecReplay() )
- player.SetSpecReplayDelay( 0.0 ) // clear spectator replay
-
- if ( target.IsPlayer() )
- {
- try
- {
- player.SetObserverTarget( target )
- player.StartObserverMode( OBS_MODE_CHASE )
- }
- catch ( ex ) {}
- }
- else
- {
- player.SetObserverModeStaticPosition( target.GetOrigin() )
- player.SetObserverModeStaticAngles( target.GetAngles() )
- player.StartObserverMode( OBS_MODE_STATIC )
- }
- player.StopPhysics()
- }
-}
-
-bool function ClientCommandCallback_spec_next( entity player, array<string> args )
-{
- if ( player.GetObserverMode() == OBS_MODE_CHASE || player.GetObserverMode() == OBS_MODE_STATIC || player.GetObserverMode() == OBS_MODE_IN_EYE )
- player.Signal( "ObserverTargetChanged", { next = true } )
-
- return true
-}
-
-bool function ClientCommandCallback_spec_prev( entity player, array<string> args )
-{
- if ( player.GetObserverMode() == OBS_MODE_CHASE || player.GetObserverMode() == OBS_MODE_STATIC || player.GetObserverMode() == OBS_MODE_IN_EYE )
- player.Signal( "ObserverTargetChanged", { next = false } )
-
- return true
-}
-
-bool function ClientCommandCallback_spec_mode( entity player, array<string> args )
-{
- // currently unsure how this actually gets called on client, works through console and has references in client.dll tho
- if ( player.GetObserverMode() == OBS_MODE_CHASE )
- {
- // set to first person spectate
- player.SetSpecReplayDelay( FIRST_PERSON_SPECTATOR_DELAY )
- player.SetViewEntity( player.GetObserverTarget(), true )
- player.StartObserverMode( OBS_MODE_IN_EYE )
- }
- else if ( player.GetObserverMode() == OBS_MODE_IN_EYE )
- {
- // set to third person spectate
- player.SetSpecReplayDelay( 0.0 )
- player.StartObserverMode( OBS_MODE_CHASE )
- }
-
- return true
-}
-
void function TryGameModeAnnouncement( entity player ) // only putting this here because it's here in gametype_sp lol
{
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut
index 1e19abd3..80fae331 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut
@@ -112,7 +112,12 @@ void function OnPrematchStart()
}
foreach ( entity player in GetPlayerArray() )
- thread SpawnPlayerIntoDropship( player )
+ {
+ if ( !IsPrivateMatchSpectator( player ) )
+ thread SpawnPlayerIntoDropship( player )
+ else
+ RespawnPrivateMatchSpectator( player )
+ }
thread EndIntroWhenFinished()
}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_no_intro.gnut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_no_intro.gnut
index 106f867b..7901e3a2 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_no_intro.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_no_intro.gnut
@@ -38,8 +38,12 @@ void function ClassicMP_DefaultNoIntro_Start()
foreach ( entity player in GetPlayerArray() )
{
- player.UnfreezeControlsOnServer()
- RemoveCinematicFlag( player, CE_FLAG_CLASSIC_MP_SPAWNING )
+ if ( !IsPrivateMatchSpectator( player ) )
+ {
+ player.UnfreezeControlsOnServer()
+ RemoveCinematicFlag( player, CE_FLAG_CLASSIC_MP_SPAWNING )
+ }
+
TryGameModeAnnouncement( player )
}
}
@@ -52,6 +56,12 @@ void function ClassicMP_DefaultNoIntro_SpawnPlayer( entity player )
if ( GetGameState() != eGameState.Prematch )
return
+ if ( IsPrivateMatchSpectator( player ) ) // private match spectators use custom spawn logic
+ {
+ RespawnPrivateMatchSpectator( player )
+ return
+ }
+
if ( IsAlive( player ) )
player.Die()
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut
index aa14477a..7e9943c3 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut
@@ -198,7 +198,9 @@ void function StartGameWithoutClassicMP()
foreach ( entity player in GetPlayerArray() )
{
- RespawnAsPilot( player )
+ if ( !IsPrivateMatchSpectator( player ) )
+ RespawnAsPilot( player )
+
ScreenFadeFromBlack( player, 0 )
}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_spectator.gnut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_spectator.gnut
new file mode 100644
index 00000000..aa2fc108
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_spectator.gnut
@@ -0,0 +1,223 @@
+global function Spectator_Init
+
+// stuff called by _base_gametype_mp and such
+global function InitialisePrivateMatchSpectatorPlayer
+global function PlayerBecomesSpectator
+global function RespawnPrivateMatchSpectator
+
+// custom spectator state functions
+// yes, GM_SetSpectatorFunc does exist in vanilla and serves roughly the same purpose, but using custom funcs here seemed better
+global function Spectator_SetDefaultSpectatorFunc
+global function Spectator_SetCustomSpectatorFunc
+global function Spectator_ClearCustomSpectatorFunc
+
+// helper funcs
+global function HACKCleanupStaticObserverStuff
+
+global typedef SpectatorFunc void functionref( entity player )
+
+struct {
+ array<entity> staticSpecCams
+ SpectatorFunc defaultSpectatorFunc
+ SpectatorFunc nextSpectatorFunc = null
+
+ int newestFuncIndex = 0 // used to track which players have finished the most recent spectator func
+} file
+
+void function Spectator_Init()
+{
+ Spectator_SetDefaultSpectatorFunc( SpectatorFunc_Default )
+
+ AddCallback_EntitiesDidLoad( SetStaticSpecCams )
+
+ RegisterSignal( "ObserverTargetChanged" )
+ RegisterSignal( "SpectatorFuncChanged" )
+ AddClientCommandCallback( "spec_next", ClientCommandCallback_spec_next )
+ AddClientCommandCallback( "spec_prev", ClientCommandCallback_spec_prev )
+ AddClientCommandCallback( "spec_mode", ClientCommandCallback_spec_mode )
+}
+
+void function SetStaticSpecCams()
+{
+ // spec cams are called spec_cam1,2,3 etc by default, so this is the easiest way to get them imo
+ int camNum = 1
+ entity lastCam = null
+ do {
+ lastCam = GetEnt( "spec_cam" + camNum++ )
+
+ if ( IsValid( lastCam ) )
+ file.staticSpecCams.append( lastCam )
+ } while ( IsValid( lastCam ) )
+}
+
+void function Spectator_SetDefaultSpectatorFunc( SpectatorFunc func )
+{
+ file.defaultSpectatorFunc = func
+}
+
+// sets the current spectator func, stopping any currently running spectator funcs to start this one
+void function Spectator_SetCustomSpectatorFunc( SpectatorFunc func )
+{
+ file.nextSpectatorFunc = func
+ svGlobal.levelEnt.Signal( "SpectatorFuncChanged" ) // spectator funcs need to listen to this manually
+ file.newestFuncIndex++
+}
+
+void function Spectator_ClearCustomSpectatorFunc()
+{
+ Spectator_SetCustomSpectatorFunc( null )
+}
+
+void function HACKCleanupStaticObserverStuff( entity player )
+{
+ // this may look like horrible awful pointless code at first glance, and while it is horrible and awful, it's not pointless
+ // 3.402823466E38 is 0xFFFF7F7F in memory, which is the value the game uses to determine whether the current static observer pos/angles are valid ( i.e. 0xFFFF7F7F = invalid/not initialised )
+ // in my experience, not cleaning this up after setting static angles will break OBS_MODE_CHASE-ing non-player entities which is bad for custom spectator funcs
+ // this is 100% way lower level than what script stuff should usually be doing, but it's needed here
+ // i sure do hope this won't break in normal use :clueless:
+ player.SetObserverModeStaticPosition( < 3.402823466e38, 3.402823466e38, 3.402823466e38 > )
+ player.SetObserverModeStaticAngles( < 3.402823466e38, 3.402823466e38, 3.402823466e38 > )
+}
+
+void function InitialisePrivateMatchSpectatorPlayer( entity player )
+{
+ thread PlayerBecomesSpectator( player )
+}
+
+// this should be called when intros respawn players normally to handle fades and stuff
+void function RespawnPrivateMatchSpectator( entity player )
+{
+ ScreenFadeFromBlack( player, 0.5, 0.5 )
+}
+
+void function PlayerBecomesSpectator( entity player )
+{
+ player.StopPhysics()
+
+ player.EndSignal( "OnRespawned" )
+ player.EndSignal( "OnDestroy" )
+ player.EndSignal( "PlayerRespawnStarted" )
+
+ OnThreadEnd( function() : ( player )
+ {
+ if ( IsValid( player ) )
+ player.StopObserverMode()
+ })
+
+ // keeps track of the most recent func this player has completed
+ // this is to ensure that custom spectator funcs are only run once per player even before being cleared
+ int funcIndex = 0
+
+ while ( true )
+ {
+ SpectatorFunc nextSpectatorFunc = file.defaultSpectatorFunc
+ if ( file.nextSpectatorFunc != null && funcIndex != file.newestFuncIndex )
+ nextSpectatorFunc = file.nextSpectatorFunc
+
+ waitthread nextSpectatorFunc( player )
+ funcIndex = file.newestFuncIndex // assuming this will be set before file.newestFuncIndex increments when the spectator func is ended by SpectatorFuncChanged
+ // surely this will not end up being false in practice :clueless:
+
+ // cleanup
+ player.StopObserverMode()
+ HACKCleanupStaticObserverStuff( player ) // un-initialise static observer positions/angles
+
+ WaitFrame() // always wait at least a frame in case an observer func exits immediately to prevent stuff locking up
+ }
+}
+
+void function SpectatorFunc_Default( entity player )
+{
+ svGlobal.levelEnt.EndSignal( "SpectatorFuncChanged" )
+ int targetIndex
+
+ table result = { next = false }
+
+ while ( true )
+ {
+ array<entity> targets
+ targets.extend( file.staticSpecCams )
+
+ if ( IsFFAGame() )
+ targets.extend( GetPlayerArray_Alive() )
+ else
+ targets.extend( GetPlayerArrayOfTeam_Alive( player.GetTeam() ) )
+
+ if ( targets.len() > 0 )
+ {
+ if ( result.next )
+ targetIndex = ( targetIndex + 1 ) % targets.len()
+ else
+ {
+ if ( targetIndex == 0 )
+ targetIndex = ( targets.len() - 1 )
+ else
+ targetIndex--
+ }
+
+ if ( targetIndex >= targets.len() )
+ targetIndex = 0
+
+ entity target = targets[ targetIndex ]
+
+ player.StopObserverMode()
+ if ( player.IsWatchingSpecReplay() )
+ player.SetSpecReplayDelay( 0.0 ) // clear spectator replay
+
+ if ( target.IsPlayer() )
+ {
+ try
+ {
+ player.SetObserverTarget( target )
+ player.StartObserverMode( OBS_MODE_CHASE )
+ }
+ catch ( ex ) { }
+ }
+ else
+ {
+ player.SetObserverModeStaticPosition( target.GetOrigin() )
+ player.SetObserverModeStaticAngles( target.GetAngles() )
+ player.StartObserverMode( OBS_MODE_STATIC )
+ }
+ }
+
+ player.StopPhysics()
+ result = player.WaitSignal( "ObserverTargetChanged" )
+ }
+}
+
+bool function ClientCommandCallback_spec_next( entity player, array<string> args )
+{
+ if ( player.GetObserverMode() == OBS_MODE_CHASE || player.GetObserverMode() == OBS_MODE_STATIC || player.GetObserverMode() == OBS_MODE_IN_EYE )
+ player.Signal( "ObserverTargetChanged", { next = true } )
+
+ return true
+}
+
+bool function ClientCommandCallback_spec_prev( entity player, array<string> args )
+{
+ if ( player.GetObserverMode() == OBS_MODE_CHASE || player.GetObserverMode() == OBS_MODE_STATIC || player.GetObserverMode() == OBS_MODE_IN_EYE )
+ player.Signal( "ObserverTargetChanged", { next = false } )
+
+ return true
+}
+
+bool function ClientCommandCallback_spec_mode( entity player, array<string> args )
+{
+ // currently unsure how this actually gets called on client, works through console and has references in client.dll tho
+ if ( player.GetObserverMode() == OBS_MODE_CHASE )
+ {
+ // set to first person spectate
+ player.SetSpecReplayDelay( FIRST_PERSON_SPECTATOR_DELAY )
+ player.SetViewEntity( player.GetObserverTarget(), true )
+ player.StartObserverMode( OBS_MODE_IN_EYE )
+ }
+ else if ( player.GetObserverMode() == OBS_MODE_IN_EYE )
+ {
+ // set to third person spectate
+ player.SetSpecReplayDelay( 0.0 )
+ player.StartObserverMode( OBS_MODE_CHASE )
+ }
+
+ return true
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_wargames.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_wargames.nut
index bd0f2d62..5af01346 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_wargames.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_wargames.nut
@@ -182,7 +182,12 @@ void function OnPrematchStart()
// launch players into intro
foreach ( entity player in GetPlayerArray() )
- thread PlayerWatchesWargamesIntro( player )
+ {
+ if ( !IsPrivateMatchSpectator( player ) )
+ thread PlayerWatchesWargamesIntro( player )
+ else
+ RespawnPrivateMatchSpectator( player )
+ }
// 7 seconds of nothing until we start the pod sequence
wait 7.0