diff options
Diffstat (limited to 'Northstar.CustomServers/mod/scripts/vscripts')
16 files changed, 588 insertions, 62 deletions
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 |