aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_fastball.gnut1
-rw-r--r--Northstar.CustomServers/mod.json5
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/_powerup.gnut4
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/_xp.gnut5
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_coliseum.nut93
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_cp.nut40
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut2
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ffa.nut3
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_lts.nut1
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_speedball.nut1
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/lobby/_private_lobby.gnut4
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp.nut3
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut5
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut53
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/levels/_lf_maps_shared.gnut2
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_lf_deck.nut1
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_wargames.nut423
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut10
18 files changed, 594 insertions, 62 deletions
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_fastball.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_fastball.gnut
index 00c19310..6dabb655 100644
--- a/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_fastball.gnut
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_fastball.gnut
@@ -22,6 +22,7 @@ void function GamemodeFastball_Init()
// implementing intro in a different file because it'll likely be big
ClassicMP_SetCustomIntro( GamemodeFastballIntroSetup, 14.5 ) // bit of a guess number
+ ClassicMP_ForceDisableEpilogue( true )
AddCallback_EntitiesDidLoad( SpawnPanelsForLevel )
AddCallback_GameStateEnter( eGameState.Prematch, ResetPanels )
SetTimeoutWinnerDecisionFunc( FastballDecideWinner )
diff --git a/Northstar.CustomServers/mod.json b/Northstar.CustomServers/mod.json
index 9f9b405b..980a5efd 100644
--- a/Northstar.CustomServers/mod.json
+++ b/Northstar.CustomServers/mod.json
@@ -26,6 +26,11 @@
{
"Name": "ns_private_match_last_map",
"DefaultValue": "mp_forwardbase_kodai"
+ },
+
+ {
+ "Name": "ns_private_match_override_maxplayers",
+ "DefaultValue": "1"
}
],
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/_powerup.gnut b/Northstar.CustomServers/mod/scripts/vscripts/_powerup.gnut
index 5fb0778c..5756bb53 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/_powerup.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/_powerup.gnut
@@ -48,7 +48,8 @@ void function PowerupSpawnerThink( entity spawnpoint, PowerUp powerupDef )
powerup.SetOrigin( base.GetOrigin() + powerupDef.modelOffset )
powerup.SetAngles( base.GetAngles() + powerupDef.modelAngles )
powerup.SetValueForModelKey( powerupDef.model )
-
+ powerup.s.powerupRef <- powerupDef.itemRef // this needs to be done before dispatchspawn since OnPowerupCollected will run as soon as we call dispatchspawn if there's a player on battery as it spawns
+
DispatchSpawn( powerup )
// unless i'm doing something really dumb, this all has to be done after dispatchspawn to get the powerup to not have gravity
@@ -57,7 +58,6 @@ void function PowerupSpawnerThink( entity spawnpoint, PowerUp powerupDef )
powerup.SetAngles( base.GetAngles() + powerupDef.modelAngles )
powerup.SetModel( powerupDef.model )
- powerup.s.powerupRef <- powerupDef.itemRef
PickupGlow glow = CreatePickupGlow( powerup, powerupDef.glowColor.x.tointeger(), powerupDef.glowColor.y.tointeger(), powerupDef.glowColor.z.tointeger() )
glow.glowFX.SetOrigin( spawnpoint.GetOrigin() ) // want the glow to be parented to the powerup, but have the position of the spawnpoint
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/_xp.gnut b/Northstar.CustomServers/mod/scripts/vscripts/_xp.gnut
index 0be171d1..6db3b9a7 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/_xp.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/_xp.gnut
@@ -38,8 +38,9 @@ void function HandleXPGainForScoreEvent( entity player, ScoreEvent event )
xpValue = weaponXp
else if ( xpValue < titanXp )
xpValue = titanXp
-
- if ( ShouldTrackXPForWeapon( player.GetActiveWeapon().GetWeaponClassName() ) )
+
+ entity weapon = player.GetActiveWeapon()
+ if ( IsValid( weapon ) && ShouldTrackXPForWeapon( weapon.GetWeaponClassName() ) )
AddWeaponXP( player, xpValue )
// if we specifically gain titan xp, then give titan xp no matter what, otherwise only give it when we're in a titan
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_coliseum.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_coliseum.nut
index e0664b1e..88a95fe4 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_coliseum.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_coliseum.nut
@@ -36,11 +36,14 @@ void function GamemodeColiseum_Init()
ClassicMP_SetCustomIntro( ClassicMP_DefaultNoIntro_Setup, ClassicMP_DefaultNoIntro_GetLength() )
AddCallback_GameStateEnter( eGameState.Prematch, ShowColiseumIntroScreen )
AddCallback_OnPlayerRespawned( GivePlayerColiseumLoadout )
+
+ ClassicMP_SetEpilogue( SetupColiseumEpilogue )
}
// stub function referenced in sh_gamemodes_mp
void function GamemodeColiseum_CustomIntro( entity player )
-{}
+{
+}
void function ShowColiseumIntroScreen()
{
@@ -56,7 +59,6 @@ void function ShowColiseumIntroScreenThreaded()
foreach ( entity player in GetPlayerArray() )
{
-
array<entity> otherTeam = GetPlayerArrayOfTeam( GetOtherTeam( player.GetTeam() ) )
int winstreak = 0
@@ -106,6 +108,7 @@ void function GivePlayerColiseumLoadout( entity player )
coliseumLoadout.primary = GetColiseumItem( "primary" )
coliseumLoadout.primaryMods = [ GetColiseumItem( "primary_attachment" ), GetColiseumItem( "primary_mod1" ), GetColiseumItem( "primary_mod2" ), GetColiseumItem( "primary_mod3" ) ]
+ coliseumLoadout.primaryAttachments = [] // will likely crash if we dont do this
coliseumLoadout.secondary = GetColiseumItem( "secondary" )
coliseumLoadout.secondaryMods = [ GetColiseumItem( "secondary_mod1" ), GetColiseumItem( "secondary_mod2" ), GetColiseumItem( "secondary_mod3" ) ]
@@ -129,4 +132,88 @@ string function GetColiseumItem( string name )
return expect string ( GetCurrentPlaylistVar( "coliseum_" + name ) )
}
-// todo this needs the outro: unsure what anims it uses \ No newline at end of file
+void function SetupColiseumEpilogue()
+{
+ AddCallback_GameStateEnter( eGameState.Epilogue, RunColiseumOutro )
+}
+
+void function RunColiseumOutro()
+{
+ entity outroAnimPoint = GetEnt( "intermission" )
+ array<entity> winningPlayers = GetPlayerArrayOfTeam( GetWinningTeam() )
+
+ if ( GetPlayerArray().len() > 0 && IsValid( outroAnimPoint ) && GetWinningTeam() != -1 && winningPlayers.len() != 0 ) // this will fail if we don't have players or a spot to do it
+ thread RunColiseumOutroThreaded( outroAnimPoint.GetOrigin(), winningPlayers[ 0 ] )
+ else
+ SetGameState( eGameState.Postmatch )
+}
+
+void function RunColiseumOutroThreaded( vector point, entity winningPlayer )
+{
+ OnThreadEnd( function() : ()
+ {
+ SetGameState( eGameState.Postmatch )
+ })
+
+ winningPlayer.EndSignal( "OnDestroy" )
+
+ // pick winner and loser anims
+ int numLost = GameRules_GetTeamScore( GetOtherTeam( GetWinningTeam() ) )
+ int animIndex = RandomInt( OUTROANIMS_WINNER[ numLost ].len() )
+ string winnerAnim = OUTROANIMS_WINNER[ numLost ][ animIndex ]
+ string loserAnim = OUTROANIMS_LOSER[ numLost ][ animIndex ]
+
+ foreach ( entity player in GetPlayerArray() )
+ {
+ if ( !IsAlive( player ) )
+ player.RespawnPlayer( null )
+
+ AddCinematicFlag( player, CE_FLAG_HIDE_MAIN_HUD )
+ ScreenFadeFromBlack( player, 0.5 )
+ player.SetOrigin( point )
+ player.SetNameVisibleToEnemy( false )
+ player.SetNameVisibleToFriendly( false )
+ // for some reason this just doesn't use the mp music system, so have to manually play this
+ // odd game
+ EmitSoundOnEntityOnlyToPlayer( player, player, "music_mp_speedball_game_win" )
+
+ FirstPersonSequenceStruct outroSequence
+ outroSequence.thirdPersonCameraAttachments = [ "VDU" ]
+ outroSequence.blendTime = 0.25
+ outroSequence.attachment = "ref"
+ outroSequence.enablePlanting = true
+ outroSequence.playerPushable = false
+
+ // for when we kill any active weapons if not needed for anim
+ entity playerWeapon = player.GetActiveWeapon()
+
+ if ( player.GetTeam() == GetWinningTeam() )
+ {
+ if ( IsValid( playerWeapon ) )
+ playerWeapon.Destroy()
+
+ outroSequence.thirdPersonAnim = winnerAnim
+ outroSequence.noParent = true
+ outroSequence.gravity = true
+ thread FirstPersonSequence( outroSequence, player )
+ }
+ else
+ {
+ // need weapon for this anim in particular
+ if ( IsValid( playerWeapon ) && loserAnim != "pt_coliseum_loser_gunkick" )
+ playerWeapon.Destroy()
+
+ outroSequence.thirdPersonAnim = loserAnim
+ outroSequence.useAnimatedRefAttachment = true
+ thread FirstPersonSequence( outroSequence, player, winningPlayer )
+ }
+ }
+
+ // all outro anims should be the same length ideally
+ wait winningPlayer.GetSequenceDuration( winnerAnim ) - 0.75
+
+ foreach ( entity player in GetPlayerArray() )
+ ScreenFadeToBlackForever( player, 0.75 )
+
+ wait 0.75
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_cp.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_cp.nut
index 41f5fedb..4ea25fa7 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_cp.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_cp.nut
@@ -34,10 +34,46 @@ void function GamemodeCP_Init()
void function RateSpawnpoints_CP( int checkClass, array<entity> spawnpoints, int team, entity player )
{
- // check hardpoints
+ if ( HasSwitchedSides() )
+ team = GetOtherTeam( team )
+ // check hardpoints, determine which ones we own
array<entity> startSpawns = SpawnPoints_GetPilotStart( team )
- array<entity> enemyStartSpawns = SpawnPoints_GetPilotStart( GetOtherTeam( team ) )
+ vector averageFriendlySpawns
+
+ // average out startspawn positions
+ foreach ( entity spawnpoint in startSpawns )
+ averageFriendlySpawns += spawnpoint.GetOrigin()
+
+ averageFriendlySpawns /= startSpawns.len()
+
+ entity friendlyHardpoint // determine our furthest out hardpoint
+ foreach ( entity hardpoint in HARDPOINTS )
+ {
+ if ( hardpoint.GetTeam() == player.GetTeam() && GetGlobalNetFloat( "objective" + hardpoint.kv.hardpointGroup + "Progress" ) >= 0.95 )
+ {
+ if ( IsValid( friendlyHardpoint ) )
+ {
+ if ( Distance2D( averageFriendlySpawns, hardpoint.GetOrigin() ) > Distance2D( averageFriendlySpawns, friendlyHardpoint.GetOrigin() ) )
+ friendlyHardpoint = hardpoint
+ }
+ else
+ friendlyHardpoint = hardpoint
+ }
+ }
+
+ vector ratingPos
+ if ( IsValid( friendlyHardpoint ) )
+ ratingPos = friendlyHardpoint.GetOrigin()
+ else
+ ratingPos = averageFriendlySpawns
+
+ foreach ( entity spawnpoint in spawnpoints )
+ {
+ // idk about magic number here really
+ float rating = 1.0 - ( Distance2D( spawnpoint.GetOrigin(), ratingPos ) / 1000.0 )
+ spawnpoint.CalculateRating( checkClass, player.GetTeam(), rating, rating )
+ }
}
void function SpawnHardpoints()
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut
index 3213827d..7f879c69 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut
@@ -134,7 +134,7 @@ bool function VerifyCTFSpawnpoint( entity spawnpoint, int team )
{
// ensure spawnpoints aren't too close to enemy base
- if ( HasSwitchedSides() && spawnpoint.GetTeam() >= TEAM_IMC )
+ if ( HasSwitchedSides() )
team = GetOtherTeam( team )
array<entity> startSpawns = SpawnPoints_GetPilotStart( team )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ffa.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ffa.nut
index 85b4aefb..6a8b3ea4 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ffa.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ffa.nut
@@ -12,6 +12,7 @@ void function OnPlayerKilled( entity victim, entity attacker, var damageInfo )
if ( victim != attacker && victim.IsPlayer() && attacker.IsPlayer() && GetGameState() == eGameState.Playing )
{
AddTeamScore( attacker.GetTeam(), 1 )
- attacker.AddToPlayerGameStat( PGS_SCORE, 1 )
+ // why isn't this PGS_SCORE? odd game
+ attacker.AddToPlayerGameStat( PGS_ASSAULT_SCORE, 1 )
}
} \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_lts.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_lts.nut
index 194db8a0..5de78b53 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_lts.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_lts.nut
@@ -30,6 +30,7 @@ void function GamemodeLts_Init()
TrackTitanDamageInPlayerGameStat( PGS_ASSAULT_SCORE )
ClassicMP_SetCustomIntro( ClassicMP_DefaultNoIntro_Setup, ClassicMP_DefaultNoIntro_GetLength() )
+ ClassicMP_ForceDisableEpilogue( true )
AddCallback_GameStateEnter( eGameState.Playing, WaitForThirtySecondsLeft )
}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_speedball.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_speedball.nut
index 4532fb97..abc9013a 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_speedball.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_speedball.nut
@@ -26,6 +26,7 @@ void function GamemodeSpeedball_Init()
SetTimeoutWinnerDecisionFunc( TimeoutCheckFlagHolder )
ClassicMP_SetCustomIntro( ClassicMP_DefaultNoIntro_Setup, ClassicMP_DefaultNoIntro_GetLength() )
+ ClassicMP_ForceDisableEpilogue( true )
}
void function CreateFlag( entity flagSpawn )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/lobby/_private_lobby.gnut b/Northstar.CustomServers/mod/scripts/vscripts/lobby/_private_lobby.gnut
index 6dd04809..7011de08 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/lobby/_private_lobby.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/lobby/_private_lobby.gnut
@@ -124,6 +124,10 @@ void function StartMatch()
WaitFrame()
}
+ // do this before setting playlist
+ if ( GetConVarBool( "ns_private_match_override_maxplayers" ) )
+ SetPlaylistVarOverride( "max_players", GetCurrentPlaylistVarString( "max_players", "16" ) )
+
try
{
// todo: not every gamemode uses the same playlist as their name! need some code to resolve these manually
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp.nut
index 66bb3d6a..2baf119c 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp.nut
@@ -30,6 +30,7 @@ struct {
float customIntroLength
bool epilogueForceDisabled = false
+ bool shouldRunEpilogueInRoundBasedMode = false
void functionref() epilogueSetupFunc
} file
@@ -117,5 +118,5 @@ bool function GetClassicMPMode()
bool function ClassicMP_ShouldRunEpilogue()
{
// note: there is a run_evac playlist var, but it's unused, and default 0, so use a new one
- return !file.epilogueForceDisabled && GetClassicMPMode() && !IsRoundBased() && GetCurrentPlaylistVarInt( "run_epilogue", 1 ) == 1
+ return !file.epilogueForceDisabled && GetClassicMPMode() && GetCurrentPlaylistVarInt( "run_epilogue", 1 ) == 1
} \ No newline at end of file
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 02c312be..b6b0aa10 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
@@ -221,7 +221,7 @@ void function SpawnPlayerIntoDropship( entity player )
ClassicMP_OnIntroFinished() // set intro as finished
// wait for intro timer to be fully done
- wait( Time() - ( file.introStartTime + DROPSHIP_INTRO_LENGTH ) )
+ wait ( file.introStartTime + DROPSHIP_INTRO_LENGTH ) - Time()
player.MovementDisable() // disable all movement but let them look around still
player.ConsumeDoubleJump() // movementdisable doesn't prevent double jumps
@@ -234,6 +234,5 @@ void function SpawnPlayerIntoDropship( entity player )
player.EnableWeaponViewModel()
RemoveCinematicFlag( player, CE_FLAG_CLASSIC_MP_SPAWNING )
- if ( GetServerVar( "switchedSides" ) != 1 )
- TryGameModeAnnouncement( player )
+ TryGameModeAnnouncement( player )
} \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut
index 0eef7f00..bf21b492 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut
@@ -63,7 +63,6 @@ void function PIN_GameStart()
AddCallback_GameStateEnter( eGameState.WaitingForCustomStart, GameStateEnter_WaitingForCustomStart )
AddCallback_GameStateEnter( eGameState.WaitingForPlayers, GameStateEnter_WaitingForPlayers )
AddCallback_OnClientConnected( WaitingForPlayers_ClientConnected )
- AddCallback_OnClientDisconnected( WaitingForPlayers_ClientDisconnected )
AddCallback_GameStateEnter( eGameState.PickLoadout, GameStateEnter_PickLoadout )
AddCallback_GameStateEnter( eGameState.Prematch, GameStateEnter_Prematch )
@@ -120,27 +119,20 @@ void function GameStateEnter_WaitingForPlayers()
foreach ( entity player in GetPlayerArray() )
WaitingForPlayers_ClientConnected( player )
- thread WaitForPlayers( GetPendingClientsCount() + file.numPlayersFullyConnected ) // like 90% sure there should be a way to get number of loading clients on server but idk it
+ thread WaitForPlayers() // like 90% sure there should be a way to get number of loading clients on server but idk it
}
-void function WaitForPlayers( int wantedNum )
+void function WaitForPlayers( )
{
// note: atm if someone disconnects as this happens the game will just wait forever
- print( "WaitForPlayers(): " + wantedNum + " players" )
- float endTime = Time() + 120.0
+ float endTime = Time() + 30.0
- while ( endTime > Time() )
- {
- if ( file.numPlayersFullyConnected >= wantedNum )
- break
-
+ while ( GetPendingClientsCount() != 0 && endTime > Time() )
WaitFrame()
- }
print( "done waiting!" )
wait 1.0 // bit nicer
-
if ( file.usePickLoadoutScreen )
SetGameState( eGameState.PickLoadout )
else
@@ -151,16 +143,8 @@ void function WaitingForPlayers_ClientConnected( entity player )
{
if ( GetGameState() == eGameState.WaitingForPlayers )
ScreenFadeToBlackForever( player, 0.0 )
-
- file.numPlayersFullyConnected++
}
-void function WaitingForPlayers_ClientDisconnected( entity player )
-{
- file.numPlayersFullyConnected--
-}
-
-
// eGameState.PickLoadout
void function GameStateEnter_PickLoadout()
{
@@ -238,7 +222,10 @@ void function GameStateEnter_Playing_Threaded()
if ( file.timeoutWinnerDecisionFunc != null )
winningTeam = file.timeoutWinnerDecisionFunc()
else
- winningTeam = GameScore_GetWinningTeam()
+ winningTeam = GetWinningTeam()
+
+ if ( winningTeam == -1 )
+ winningTeam = TEAM_UNASSIGNED
if ( file.switchSidesBased && !file.hasSwitchedSides && !IsRoundBased() ) // in roundbased modes, we handle this in setwinner
SetGameState( eGameState.SwitchingSides )
@@ -269,6 +256,8 @@ void function GameStateEnter_WinnerDetermined_Threaded()
{
// do win announcement
int winningTeam = GetWinningTeam()
+ if ( winningTeam == -1 )
+ winningTeam = TEAM_UNASSIGNED
foreach ( entity player in GetPlayerArray() )
{
@@ -277,15 +266,15 @@ void function GameStateEnter_WinnerDetermined_Threaded()
announcementSubstr = player.GetTeam() == winningTeam ? file.announceRoundWinnerWinningSubstr : file.announceRoundWinnerLosingSubstr
if ( IsRoundBased() )
- Remote_CallFunction_NonReplay( player, "ServerCallback_AnnounceRoundWinner", GetWinningTeam(), announcementSubstr, ROUND_WINNING_KILL_REPLAY_SCREEN_FADE_TIME, GameRules_GetTeamScore2( TEAM_MILITIA ), GameRules_GetTeamScore2( TEAM_IMC ) )
+ Remote_CallFunction_NonReplay( player, "ServerCallback_AnnounceRoundWinner", winningTeam, announcementSubstr, ROUND_WINNING_KILL_REPLAY_SCREEN_FADE_TIME, GameRules_GetTeamScore2( TEAM_MILITIA ), GameRules_GetTeamScore2( TEAM_IMC ) )
else
- Remote_CallFunction_NonReplay( player, "ServerCallback_AnnounceWinner", GetWinningTeam(), announcementSubstr, ROUND_WINNING_KILL_REPLAY_SCREEN_FADE_TIME )
+ Remote_CallFunction_NonReplay( player, "ServerCallback_AnnounceWinner", winningTeam, announcementSubstr, ROUND_WINNING_KILL_REPLAY_SCREEN_FADE_TIME )
}
WaitFrame() // wait a frame so other scripts can setup killreplay stuff
entity replayAttacker = file.roundWinningKillReplayAttacker
- bool doReplay = Replay_IsEnabled() && !ClassicMP_ShouldRunEpilogue() && IsRoundWinningKillReplayEnabled() && IsValid( replayAttacker )
+ bool doReplay = Replay_IsEnabled() && IsRoundWinningKillReplayEnabled() && IsValid( replayAttacker )
&& Time() - file.roundWinningKillReplayTime <= ROUND_WINNING_KILL_REPLAY_LENGTH_OF_REPLAY
float replayLength = 2.0 // extra delay if no replay
@@ -336,11 +325,23 @@ void function GameStateEnter_WinnerDetermined_Threaded()
int roundsPlayed = expect int ( GetServerVar( "roundsPlayed" ) )
SetServerVar( "roundsPlayed", roundsPlayed + 1 )
- float highestScore = max( GameRules_GetTeamScore( TEAM_IMC ), GameRules_GetTeamScore( TEAM_MILITIA ) )
+ int winningTeam = GetWinningTeam()
+ if ( winningTeam == -1 )
+ winningTeam = TEAM_UNASSIGNED
+
+ int highestScore = GameRules_GetTeamScore( winningTeam )
int roundScoreLimit = GameMode_GetRoundScoreLimit( GAMETYPE )
if ( highestScore >= roundScoreLimit )
- SetGameState( eGameState.Postmatch )
+ {
+ if ( ClassicMP_ShouldRunEpilogue() )
+ {
+ ClassicMP_SetupEpilogue()
+ SetGameState( eGameState.Epilogue )
+ }
+ else
+ SetGameState( eGameState.Postmatch )
+ }
else if ( file.switchSidesBased && !file.hasSwitchedSides && highestScore >= ( roundScoreLimit.tofloat() / 2.0 ) ) // round up
SetGameState( eGameState.SwitchingSides ) // note: switchingsides will handle setting to pickloadout and prematch by itself
else if ( file.usePickLoadoutScreen )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/_lf_maps_shared.gnut b/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/_lf_maps_shared.gnut
index 695f096d..91b17840 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/_lf_maps_shared.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/_lf_maps_shared.gnut
@@ -5,5 +5,5 @@ void function SetupLiveFireMaps()
{
Riff_ForceTitanAvailability( eTitanAvailability.Never )
ClassicMP_SetLevelIntro( ClassicMP_DefaultNoIntro_Setup, ClassicMP_DefaultNoIntro_GetLength() )
- ClassicMP_ForceDisableEpilogue( true )
+ // ClassicMP_ForceDisableEpilogue( true ) // don't do this because evac handles this now
} \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_lf_deck.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_lf_deck.nut
index 398b2fc5..dca30fe9 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_lf_deck.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_lf_deck.nut
@@ -2,5 +2,6 @@ global function CodeCallback_MapInit
void function CodeCallback_MapInit()
{
+ FlagClear( "Disable_Marvins" )
SetupLiveFireMaps()
} \ 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 3a46eba8..58a4be02 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_wargames.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/levels/mp_wargames.nut
@@ -2,7 +2,12 @@ untyped
global function CodeCallback_MapInit
struct {
- bool introStartTime
+ float introStartTime
+ entity militiaPod
+ entity imcPod
+
+ vector militiaPodFXEyePos
+ vector imcPodFXEyePos
} file
void function CodeCallback_MapInit()
@@ -14,32 +19,426 @@ void function CodeCallback_MapInit()
SetEvacSpaceNode( GetEnt( "end_spacenode" ) )
- // currently disabled: intro
- // if ( !IsFFAGame() )
- // ClassicMP_SetLevelIntro( WargamesIntroSetup, 25.0 )
+ // dissolve effects
+ AddDeathCallback( "player", WargamesDissolveDeadEntity )
+ AddDeathCallback( "npc_soldier", WargamesDissolveDeadEntity )
+ AddDeathCallback( "npc_spectre", WargamesDissolveDeadEntity )
+ AddDeathCallback( "npc_pilot_elite", WargamesDissolveDeadEntity )
+ AddDeathCallback( "npc_marvin", WargamesDissolveDeadEntity )
+
+ FlagClear( "Disable_Marvins" )
+
+ // currently disabled until finished: intro
+ if ( !IsFFAGame() )
+ ClassicMP_SetLevelIntro( WargamesIntroSetup, 20.0 )
}
-// intro stuff
+// dissolve effects
+void function WargamesDissolveDeadEntity( entity deadEnt, var damageInfo )
+{
+ if ( deadEnt.IsPlayer() || GamePlayingOrSuddenDeath() || GetGameState() == eGameState.Epilogue )
+ {
+ deadEnt.Dissolve( ENTITY_DISSOLVE_CHAR, < 0, 0, 0 >, 0 )
+ EmitSoundAtPosition( TEAM_UNASSIGNED, deadEnt.GetOrigin(), "Object_Dissolve" )
+ }
+}
+
+// intro stuff:
void function WargamesIntroSetup()
{
- AddCallback_OnClientConnected( WargamesIntro_OnClientConnected )
- AddCallback_OnClientDisconnected( WargamesIntro_OnClientDisconnected )
+ PrecacheParticleSystem( FX_POD_SCREEN_IN )
+ PrecacheParticleSystem( $"P_pod_scan_laser_FP" )
+ PrecacheParticleSystem( $"P_pod_Dlight_console1" )
+ PrecacheParticleSystem( $"P_pod_Dlight_console2" )
+ PrecacheParticleSystem( $"P_pod_door_glow_FP" )
+ PrecacheModel( $"models/titans/ogre/ogreposeopen.mdl" )
+
+ file.militiaPod = GetEnt( "training_pod" )
+ file.imcPod = GetEnt( "training_pod_imc" )
+
+ AddCallback_OnClientConnected( WargamesIntro_AddPlayer )
AddCallback_GameStateEnter( eGameState.Prematch, OnPrematchStart )
}
-void function WargamesIntro_OnClientConnected( entity player )
+void function WargamesIntro_AddPlayer( entity player )
{
+ if ( GetGameState() != eGameState.Prematch )
+ return
+
+ thread PlayerWatchesWargamesIntro( player )
+}
+void function OnPrematchStart()
+{
+ ClassicMP_OnIntroStarted()
+ file.introStartTime = Time()
+
+ // set up shared objects
+ // this breaks glowlights, not sure why
+ //file.imcPod.RenderWithViewModels( true )
+ //file.militiaPod.RenderWithViewModels( true )
+
+ PodFXLights( file.imcPod )
+ PodFXLights( file.militiaPod )
+
+ FirstPersonSequenceStruct openPodSequence
+ openPodSequence.thirdPersonAnimIdle = "trainingpod_doors_open_idle"
+ thread FirstPersonSequence( openPodSequence, file.imcPod )
+ thread FirstPersonSequence( openPodSequence, file.militiaPod )
+
+ // militia titans/marvins
+ entity militiaOgre = CreatePropDynamic( $"models/titans/ogre/ogreposeopen.mdl", < -2060, 2856, -1412.5 >, < 0, 0, 0 > )
+
+ entity militiaOgreMarvin1 = CreateMarvin( TEAM_UNASSIGNED, < -2113, 2911, -1412 >, < 0, 20, 0 > )
+ DispatchSpawn( militiaOgreMarvin1 )
+ thread PlayAnim( militiaOgreMarvin1, "mv_idle_weld" )
+
+ entity militiaOgreMarvin2 = CreateMarvin( TEAM_UNASSIGNED, < -2040, 2788, -1412 >, < 0, 140, 0 > )
+ DispatchSpawn( militiaOgreMarvin2 )
+ thread PlayAnim( militiaOgreMarvin2, "mv_idle_weld" )
+
+ entity militiaOgreMarvin3 = CreateMarvin( TEAM_UNASSIGNED, < -2116, 2868, -1458 >, < 0, 127, 0 > )
+ DispatchSpawn( militiaOgreMarvin3 )
+ thread PlayAnim( militiaOgreMarvin3, "mv_turret_repair_A_idle" )
+
+ entity militiaIon = CreatePropDynamic( $"models/titans/medium/titan_medium_ajax.mdl", < -1809.98, 2790.39, -1409 >, < 0, 80, 0 > )
+ thread PlayAnim( militiaIon, "at_titan_activation_wargames_intro" )
+
+ entity militiaPilot = CreateElitePilot( TEAM_UNASSIGNED, < 0, 0, 0 >, < 0, 0, 0 > )
+ DispatchSpawn( militiaPilot )
+ militiaPilot.SetParent( militiaIon, "HIJACK" )
+ militiaPilot.MarkAsNonMovingAttachment()
+ militiaPilot.Anim_ScriptedPlay( "pt_titan_activation_pilot" )
+ militiaPilot.Anim_EnableUseAnimatedRefAttachmentInsteadOfRootMotion()
+
+ entity militiaMarvinChillin = CreateMarvin( TEAM_UNASSIGNED, < -1786, 3060, -1412 >, < 0, -120, 0 > )
+ DispatchSpawn( militiaMarvinChillin )
+ thread PlayAnim( militiaMarvinChillin, "mv_idle_unarmed" )
+
+
+ // imc grunts
+ entity imcGrunt1 = CreatePropDynamic( $"models/humans/grunts/imc_grunt_rifle.mdl", < -2915, 2867, -1788 >, < 0, -137, 0 > )
+ thread PlayAnim( imcGrunt1, "pt_console_idle" )
+
+ entity imcGrunt2 = CreatePropDynamic( $"models/humans/grunts/imc_grunt_rifle.mdl", < -2870, 2746, -1786 >, < 0, -167, 0 > )
+ thread PlayAnim( imcGrunt2, "pt_console_idle" )
+ imcGrunt2.Anim_SetInitialTime( 2.0 )
+
+ entity imcGrunt3 = CreatePropDynamic( $"models/humans/grunts/imc_grunt_rifle.mdl", < -3037, 2909, -1786 >, < 0, -60, 0 > )
+ thread PlayAnim( imcGrunt3, "pt_console_idle" )
+ imcGrunt3.Anim_SetInitialTime( 4.0 )
+
+ entity imcGrunt4 = CreatePropDynamic( $"models/humans/grunts/imc_grunt_rifle.mdl", < -3281, 2941, -1790 >, < 0, 138, 0 > )
+ thread PlayAnim( imcGrunt4, "pt_console_idle" )
+ imcGrunt4.Anim_SetInitialTime( 6.0 )
+
+ // launch players into intro
+ foreach ( entity player in GetPlayerArray() )
+ thread PlayerWatchesWargamesIntro( player )
+
+ // 7 seconds of nothing until we start the pod sequence
+ wait 7.0
+
+ FirstPersonSequenceStruct podCloseSequence
+ podCloseSequence.thirdPersonAnim = "trainingpod_doors_close"
+ podCloseSequence.thirdPersonAnimIdle = "trainingpod_doors_close_idle"
+ thread FirstPersonSequence( podCloseSequence, file.imcPod )
+ thread FirstPersonSequence( podCloseSequence, file.militiaPod )
+
+ wait 7.0
+ thread PodBootFXThread( file.imcPod )
+ thread PodBootFXThread( file.militiaPod )
+
+ wait 6.0
+ ClassicMP_OnIntroFinished()
+
+ // make sure we stop using viewmodels for these otherwise everyone can see them in the floor 24/7
+ file.imcPod.RenderWithViewModels( false )
+ file.militiaPod.RenderWithViewModels( false )
+
+ //PodFXCleanup( file.imcPod )
+ //PodFXCleanup( file.militiaPod )
+
+ // cleanup intro objects
+ militiaOgre.Destroy()
+ militiaIon.Destroy()
+ militiaPilot.Destroy()
+ militiaOgreMarvin1.Destroy()
+ militiaOgreMarvin2.Destroy()
+ militiaOgreMarvin3.Destroy()
+ militiaMarvinChillin.Destroy()
+
+ imcGrunt1.Destroy()
+ imcGrunt2.Destroy()
+ imcGrunt3.Destroy()
+ imcGrunt4.Destroy()
}
-void function WargamesIntro_OnClientDisconnected( entity player )
+void function PlayerWatchesWargamesIntro( entity player )
{
+ if ( IsAlive( player ) )
+ player.Die()
+ OnThreadEnd( function() : ( player )
+ {
+ if ( IsValid( player ) )
+ {
+ RemoveCinematicFlag( player, CE_FLAG_CLASSIC_MP_SPAWNING )
+ player.kv.VisibilityFlags = ENTITY_VISIBLE_TO_EVERYONE
+ ClearPlayerAnimViewEntity( player )
+ player.EnableWeaponViewModel()
+ player.ClearParent()
+ player.UnforceStand()
+ player.MovementEnable()
+ Remote_CallFunction_NonReplay( player, "ServerCallback_ClearFactionLeaderIntro" )
+ }
+ })
+
+ // we need to wait a frame if we killed ourselves to spawn into this, so just easier to do it all the time to remove any weirdness
+ WaitFrame()
+
+ player.EndSignal( "OnDestroy" )
+ player.EndSignal( "OnDeath" )
+
+ int factionTeam = ConvertPlayerFactionToIMCOrMilitiaTeam( player )
+ entity playerPod
+ if ( factionTeam == TEAM_IMC )
+ playerPod = file.imcPod
+ else
+ playerPod = file.militiaPod
+
+ // setup player
+ int podAttachId = playerPod.LookupAttachment( "REF" )
+ player.SetOrigin( playerPod.GetAttachmentOrigin( podAttachId ) )
+ player.SetAngles( playerPod.GetAttachmentAngles( podAttachId ) )
+ player.RespawnPlayer( null )
+ player.SetParent( playerPod, "REF" )
+ player.ForceStand()
+
+ if ( !HasAnimEvent( player.GetFirstPersonProxy(), "PlaySound_SimPod_DoorShut" ) )
+ AddAnimEvent( player.GetFirstPersonProxy(), "PlaySound_SimPod_DoorShut", PlaySound_SimPod_DoorShut )
+
+ AddCinematicFlag( player, CE_FLAG_CLASSIC_MP_SPAWNING )
+ player.kv.VisibilityFlags = ENTITY_VISIBLE_TO_OWNER
+ TrainingPod_ViewConeLock_PodClosed( player )
+ player.DisableWeaponViewModel()
+ player.MovementDisable()
+
+ // spawn faction leader
+ // no clue why client subtracts 4.5 from the time we give this, so just add it here instead
+ if ( factionTeam == TEAM_IMC )
+ Remote_CallFunction_NonReplay( player, "ServerCallback_SpawnIMCFactionLeaderForIntro", file.introStartTime + 4.5, playerPod.GetEncodedEHandle() )
+ else
+ Remote_CallFunction_NonReplay( player, "ServerCallback_SpawnMilitiaFactionLeaderForIntro", file.introStartTime + 4.5, playerPod.GetEncodedEHandle() )
+
+ // idle pod sequence
+ FirstPersonSequenceStruct podIdleSequence
+ podIdleSequence.firstPersonAnimIdle = "ptpov_trainingpod_idle"
+ podIdleSequence.renderWithViewModels = true
+ podIdleSequence.attachment = "REF"
+ thread FirstPersonSequence( podIdleSequence, player, playerPod )
+
+ ScreenFadeFromBlack( player, max( 0.0, ( file.introStartTime + 0.5 ) - Time() ), max( 0.0, ( file.introStartTime + 0.5 ) - Time() ) )
+
+ // also get eye positions for fx here
+ if ( file.imcPodFXEyePos == < 0, 0, 0 > && factionTeam == TEAM_IMC )
+ file.imcPodFXEyePos = player.EyePosition()
+ else if ( file.militiaPodFXEyePos == < 0, 0, 0 > && factionTeam == TEAM_MILITIA )
+ file.militiaPodFXEyePos = player.EyePosition()
+
+ // 7 seconds of nothing before we start the pod sequence
+ wait ( file.introStartTime + 7.0 ) - Time()
+
+ FirstPersonSequenceStruct podCloseSequence
+ podCloseSequence.firstPersonAnim = "ptpov_trainingpod_doors_close"
+ podCloseSequence.renderWithViewModels = true
+ podCloseSequence.attachment = "REF"
+ podCloseSequence.viewConeFunction = TrainingPod_ViewConeLock_SemiStrict
+ podCloseSequence.setInitialTime = Time() - ( file.introStartTime + 7.0 )
+ waitthread FirstPersonSequence( podCloseSequence, player, playerPod )
+
+ // boot sequence
+ EmitSoundOnEntityOnlyToPlayer( player, player, "NPE_Scr_SimPod_PowerUp" )
+ TrainingPod_ViewConeLock_PodClosed( player )
+
+ // 10 seconds of starting pod before we run effects and spawn players
+ // note, this is cool because it waits for a specific time, so we can have a blocking call directly before it just fine
+ wait ( file.introStartTime + 15.5 ) - Time()
+ Remote_CallFunction_NonReplay( player, "ServerCallback_PlayPodTransitionScreenFX" )
+
+ // need to wait no matter what the delay is here so fx will sync up
+ wait 3.5
+
+ entity spawnpoint = FindSpawnPoint( player, false, true )
+ spawnpoint.s.lastUsedTime = Time()
+ player.SetOrigin( spawnpoint.GetOrigin() )
+ player.SetAngles( spawnpoint.GetAngles() )
+
+ thread DelayedGamemodeAnnouncement( player )
}
-void function OnPrematchStart()
+void function DelayedGamemodeAnnouncement( entity player )
{
- ClassicMP_OnIntroStarted()
- file.introStartTime = Time()
+ wait 1.0
+ TryGameModeAnnouncement( player )
+}
+
+void function PlaySound_SimPod_DoorShut( entity playerFirstPersonProxy ) // stolen from sp_training
+{
+ entity player = playerFirstPersonProxy.GetOwner()
+ if ( !IsValid( player ) )
+ return
+
+ EmitSoundOnEntityOnlyToPlayer( player, player, "NPE_Scr_SimPod_DoorShut" )
+}
+
+// intro viewcones
+void function TrainingPod_ViewConeLock_PodOpen( entity player )
+{
+ player.PlayerCone_FromAnim()
+ player.PlayerCone_SetMinYaw( -25 )
+ player.PlayerCone_SetMaxYaw( 25 )
+ player.PlayerCone_SetMinPitch( -30 )
+ player.PlayerCone_SetMaxPitch( 35 )
+}
+
+void function TrainingPod_ViewConeLock_PodClosed( entity player )
+{
+ player.PlayerCone_FromAnim()
+ player.PlayerCone_SetMinYaw( -25 )
+ player.PlayerCone_SetMaxYaw( 25 )
+ player.PlayerCone_SetMinPitch( -30 )
+ player.PlayerCone_SetMaxPitch( 30 )
+}
+
+void function TrainingPod_ViewConeLock_SemiStrict( entity player )
+{
+ player.PlayerCone_FromAnim()
+ player.PlayerCone_SetMinYaw( -10 )
+ player.PlayerCone_SetMaxYaw( 10 )
+ player.PlayerCone_SetMinPitch( -10 )
+ player.PlayerCone_SetMaxPitch( 10 )
+}
+
+// intro pod fx
+// here be dragons etc hell code probably
+void function PodFXLights( entity pod )
+{
+ // dlights
+ pod.s.podLightFXHandles <- []
+ pod.s.podLightFXHandles.append( PlayLoopFXOnEntity( $"P_pod_Dlight_console1", pod, "light_console1" ) )
+ pod.s.podLightFXHandles.append( PlayLoopFXOnEntity( $"P_pod_Dlight_console2", pod, "light_console2" ) )
+}
+
+void function PodFXLasers( entity pod )
+{
+ entity leftEmitter = CreateScriptMover( pod.GetOrigin() )
+ pod.s.leftLaserEmitter <- leftEmitter
+ entity rightEmitter = CreateScriptMover( pod.GetOrigin() )
+ pod.s.rightLaserEmitter <- rightEmitter
+
+ thread PodFXLaserSweep( leftEmitter, pod, pod == file.imcPod ? file.imcPodFXEyePos : file.militiaPodFXEyePos, "fx_laser_L" )
+ thread PodFXLaserSweep( rightEmitter, pod, pod == file.imcPod ? file.imcPodFXEyePos : file.militiaPodFXEyePos, "fx_laser_R" )
+}
+
+void function PodFXLaserSweep( entity emitter, entity pod, vector eyePos, string attachment )
+{
+ // setup emitter attachments
+ emitter.SetOrigin( < 5, 5, 5 > )
+ emitter.SetParent( pod, attachment )
+
+ float sweepTime = RandomFloatRange( 2.9, 3.15 )
+
+ vector centerAng = VectorToAngles( ( eyePos + < 0, 0, 7 > ) - emitter.GetOrigin() )
+ vector topAng = centerAng + < -270, 0, 0 >
+ vector bottomAng = centerAng + < -90, 0, 0 >
+
+ emitter.s.fxHandle <- PlayLoopFXOnEntity( $"P_pod_scan_laser_FP", emitter )
+
+ float finalCenterTime = sweepTime * 0.15
+ float bigSweepTime = ( sweepTime - finalCenterTime ) / 2
+
+ emitter.SetAbsAngles( topAng )
+ emitter.NonPhysicsRotateTo( topAng, bigSweepTime, 0.0, bigSweepTime * 0.2 )
+ wait bigSweepTime - 0.1
+
+ emitter.NonPhysicsRotateTo( bottomAng, bigSweepTime, 0.0, bigSweepTime * 0.2 )
+ wait bigSweepTime
+
+ emitter.NonPhysicsRotateTo( centerAng, finalCenterTime, 0.0, finalCenterTime * 0.2 )
+}
+
+void function PodFXGlowLights( entity pod )
+{
+ // see sp_training:5533 (TrainingPod_GlowLightsArraySetup)
+ array< array< string > > glowLightGroups = [
+ [ "fx_glow_L_door012", "fx_glow_R_door014" ],
+ [ "fx_glow_L_door013", "fx_glow_R_door013" ],
+ [ "fx_glow_L_door014", "fx_glow_R_door012" ],
+ [ "fx_glow_L_door011", "fx_glow_R_door011" ],
+ [ "fx_glow_L_door09", "fx_glow_R_door09" ],
+ [ "fx_glow_L_door010", "fx_glow_R_door010" ],
+ [ "fx_glow_L_door07", "fx_glow_R_door07" ],
+ [ "fx_glow_L_door08", "fx_glow_R_door08" ],
+ [ "fx_glow_L_door05", "fx_glow_R_door05" ],
+ [ "fx_glow_L_door06", "fx_glow_R_door06" ],
+ [ "fx_glow_L_door03", "fx_glow_R_door03" ],
+ [ "fx_glow_L_door04", "fx_glow_R_door04" ],
+ [ "fx_glow_L_door01", "fx_glow_R_door01" ],
+ [ "fx_glow_L_door02", "fx_glow_R_door02" ]
+ ]
+
+ pod.s.podGlowLightFXHandles <- []
+
+ foreach ( array<string> group in glowLightGroups )
+ {
+ foreach ( string attachName in group )
+ pod.s.podGlowLightFXHandles.append( PlayLoopFXOnEntity( $"P_pod_door_glow_FP", pod, attachName ) )
+
+ wait 0.1
+ }
+}
+
+void function PodBootFXThread( entity pod )
+{
+ PodFXGlowLights( pod )
+ PodFXLasers( pod )
+}
+
+void function PodFXCleanup( entity pod )
+{
+ foreach ( entity handle in pod.s.podLightFXHandles )
+ {
+ if ( IsValid_ThisFrame( handle ) )
+ {
+ handle.SetStopType( "DestroyImmediately" )
+ handle.ClearParent()
+ handle.Destroy()
+ }
+ }
+
+ pod.s.podLightFXHandles = []
+
+ foreach ( entity handle in pod.s.podGlowLightFXHandles )
+ {
+ if ( IsValid_ThisFrame( handle ) )
+ {
+ handle.SetStopType( "DestroyImmediately" )
+ handle.ClearParent()
+ handle.Destroy()
+ }
+ }
+
+ pod.s.podGlowLightFXHandles = []
+
+ pod.s.leftLaserEmitter.s.fxHandle.SetStopType( "DestroyImmediately" )
+ pod.s.leftLaserEmitter.s.fxHandle.ClearParent()
+ pod.s.leftLaserEmitter.s.fxHandle.Destroy()
+ pod.s.leftLaserEmitter.Destroy()
+
+ pod.s.rightLaserEmitter.s.fxHandle.SetStopType( "DestroyImmediately" )
+ pod.s.rightLaserEmitter.s.fxHandle.ClearParent()
+ pod.s.rightLaserEmitter.s.fxHandle.Destroy()
+ pod.s.rightLaserEmitter.Destroy()
} \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut
index 18026f17..88f1e04c 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut
@@ -302,15 +302,9 @@ bool function IsSpawnpointValid( entity spawnpoint, int team )
void function RateSpawnpoints_Generic( int checkClass, array<entity> spawnpoints, int team, entity player )
{
- // generic spawns v2
- array<entity> startSpawns = checkClass == TD_TITAN ? SpawnPoints_GetTitanStart( team ) : SpawnPoints_GetPilotStart( team )
+ // i'm not a fan of this func, but i really don't have a better way to do this rn, and it's surprisingly good with los checks implemented now
- // realistically, this should spawn people fairly spread out across the map, preferring being closer to their startspawns
- // should spawn like, near fights, but not in them
-
-
- // using old func for tests rq
- // calculate ratings for preferred nodes
+ // calculate ratings for preferred nodes
// this tries to prefer nodes with more teammates, then activity on them
// todo: in the future it might be good to have this prefer nodes with enemies up to a limit of some sort
// especially in ffa modes i could deffo see this falling apart a bit rn