diff options
Diffstat (limited to 'Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut')
-rw-r--r-- | Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut | 734 |
1 files changed, 734 insertions, 0 deletions
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut new file mode 100644 index 000000000..197ac5e90 --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut @@ -0,0 +1,734 @@ +untyped + +global function PIN_GameStart +global function SetGameState +global function GameState_EntitiesDidLoad +global function WaittillGameStateOrHigher +global function AddCallback_OnRoundEndCleanup + +global function SetShouldUsePickLoadoutScreen +global function SetSwitchSidesBased +global function SetSuddenDeathBased +global function SetShouldUseRoundWinningKillReplay +global function SetRoundWinningKillReplayKillClasses +global function SetRoundWinningKillReplayAttacker +global function SetWinner +global function SetTimeoutWinnerDecisionFunc +global function AddTeamScore + +global function GameState_GetTimeLimitOverride +global function IsRoundBasedGameOver +global function ShouldRunEvac +global function GiveTitanToPlayer +global function GetTimeLimit_ForGameMode + +struct { + // used for togglable parts of gamestate + bool usePickLoadoutScreen + bool switchSidesBased + bool suddenDeathBased + int functionref() timeoutWinnerDecisionFunc + + // for waitingforplayers + int numPlayersFullyConnected + + bool hasSwitchedSides + + int announceRoundWinnerWinningSubstr + int announceRoundWinnerLosingSubstr + + bool roundWinningKillReplayTrackPilotKills = true + bool roundWinningKillReplayTrackTitanKills = false + + float roundWinningKillReplayTime + entity roundWinningKillReplayVictim + entity roundWinningKillReplayAttacker + int roundWinningKillReplayMethodOfDeath + float roundWinningKillReplayTimeOfDeath + float roundWinningKillReplayHealthFrac + + array<void functionref()> roundEndCleanupCallbacks +} file + +void function PIN_GameStart() +{ + // todo: using the pin telemetry function here, weird and was done veeery early on before i knew how this all worked, should use a different one + + // called from InitGameState + //FlagInit( "ReadyToStartMatch" ) + + SetServerVar( "switchedSides", 0 ) + SetServerVar( "winningTeam", -1 ) + + 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 ) + AddCallback_GameStateEnter( eGameState.Playing, GameStateEnter_Playing ) + AddCallback_GameStateEnter( eGameState.WinnerDetermined, GameStateEnter_WinnerDetermined ) + AddCallback_GameStateEnter( eGameState.SwitchingSides, GameStateEnter_SwitchingSides ) + AddCallback_GameStateEnter( eGameState.SuddenDeath, GameStateEnter_SuddenDeath ) + AddCallback_GameStateEnter( eGameState.Postmatch, GameStateEnter_Postmatch ) + + AddCallback_OnPlayerKilled( OnPlayerKilled ) + AddDeathCallback( "npc_titan", OnTitanKilled ) + + RegisterSignal( "CleanUpEntitiesForRoundEnd" ) +} + +void function SetGameState( int newState ) +{ + SetServerVar( "gameStateChangeTime", Time() ) + SetServerVar( "gameState", newState ) + svGlobal.levelEnt.Signal( "GameStateChanged" ) + + // added in AddCallback_GameStateEnter + foreach ( callbackFunc in svGlobal.gameStateEnterCallbacks[ newState ] ) + callbackFunc() +} + +void function GameState_EntitiesDidLoad() +{ + // nothing of importance to put here, this is referenced in _gamestate though so need it +} + +void function WaittillGameStateOrHigher( int gameState ) +{ + while ( GetGameState() < gameState ) + svGlobal.levelEnt.WaitSignal( "GameStateChanged" ) +} + + +// logic for individual gamestates: + + +// eGameState.WaitingForCustomStart +void function GameStateEnter_WaitingForCustomStart() +{ + // unused in release, comments indicate this was supposed to be used for an e3 demo + // perhaps games in this demo were manually started by an employee? no clue really +} + + +// eGameState.WaitingForPlayers +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 +} + +void function WaitForPlayers( int wantedNum ) +{ + // note: atm if someone disconnects as this happens the game will just wait forever + print( "WaitForPlayers(): " + wantedNum + " players" ) + float endTime = Time() + 120.0 + + while ( endTime > Time() ) + { + if ( file.numPlayersFullyConnected >= wantedNum ) + break + + WaitFrame() + } + + print( "done waiting!" ) + + wait 1.0 // bit nicer + + if ( file.usePickLoadoutScreen ) + SetGameState( eGameState.PickLoadout ) + else + SetGameState( eGameState.Prematch ) +} + +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() +{ + thread GameStateEnter_PickLoadout_Threaded() +} + +void function GameStateEnter_PickLoadout_Threaded() +{ + float pickloadoutLength = 20.0 // may need tweaking + SetServerVar( "minPickLoadOutTime", Time() + pickloadoutLength ) + + // titan selection menu can change minPickLoadOutTime so we need to wait manually until we hit the time + while ( Time() < GetServerVar( "minPickLoadOutTime" ) ) + WaitFrame() + + SetGameState( eGameState.Prematch ) +} + + +// eGameState.Prematch +void function GameStateEnter_Prematch() +{ + int timeLimit = GameMode_GetTimeLimit( GAMETYPE ) * 60 + if ( file.switchSidesBased ) + timeLimit /= 2 // endtime is half of total per side + + SetServerVar( "gameEndTime", Time() + timeLimit + ClassicMP_GetIntroLength() ) + SetServerVar( "roundEndTime", Time() + ClassicMP_GetIntroLength() + GameMode_GetRoundTimeLimit( GAMETYPE ) * 60 ) +} + + +// eGameState.Playing +void function GameStateEnter_Playing() +{ + thread GameStateEnter_Playing_Threaded() +} + +void function GameStateEnter_Playing_Threaded() +{ + WaitFrame() // ensure timelimits are all properly set + + while ( GetGameState() == eGameState.Playing ) + { + // could cache these, but what if we update it midgame? + float endTime + if ( IsRoundBased() ) + endTime = expect float( GetServerVar( "roundEndTime" ) ) + else + endTime = expect float( GetServerVar( "gameEndTime" ) ) + + // time's up! + if ( Time() >= endTime ) + { + int winningTeam + if ( file.timeoutWinnerDecisionFunc != null ) + winningTeam = file.timeoutWinnerDecisionFunc() + else + winningTeam = GameScore_GetWinningTeam() + + if ( file.switchSidesBased && !file.hasSwitchedSides && !IsRoundBased() ) // in roundbased modes, we handle this in setwinner + SetGameState( eGameState.SwitchingSides ) + else if ( file.suddenDeathBased && winningTeam == TEAM_UNASSIGNED ) // suddendeath if we draw and suddendeath is enabled and haven't switched sides + SetGameState( eGameState.SuddenDeath ) + else + SetWinner( winningTeam ) + } + + WaitFrame() + } +} + + +// eGameState.WinnerDetermined +// these are likely innacurate +const float ROUND_END_FADE_KILLREPLAY = 1.0 +const float ROUND_END_DELAY_KILLREPLAY = 3.0 +const float ROUND_END_FADE_NOKILLREPLAY = 8.0 +const float ROUND_END_DELAY_NOKILLREPLAY = 10.0 + +void function GameStateEnter_WinnerDetermined() +{ + thread GameStateEnter_WinnerDetermined_Threaded() +} + +void function GameStateEnter_WinnerDetermined_Threaded() +{ + bool killcamsWereEnabled = KillcamsEnabled() + if ( killcamsWereEnabled ) // dont want killcams to interrupt stuff + SetKillcamsEnabled( false ) + + WaitFrame() // wait a frame so other scripts can setup killreplay stuff + + entity replayAttacker = file.roundWinningKillReplayAttacker + bool doReplay = Replay_IsEnabled() && !( !IsRoundBased() && Evac_IsEnabled() ) && IsRoundWinningKillReplayEnabled() && IsValid( replayAttacker ) + && Time() - file.roundWinningKillReplayTime <= ROUND_WINNING_KILL_REPLAY_LENGTH_OF_REPLAY + + float replayLength = 2.0 // extra delay if no replay + if ( doReplay ) + { + replayLength = ROUND_WINNING_KILL_REPLAY_LENGTH_OF_REPLAY + if ( "respawnTime" in replayAttacker.s && Time() - replayAttacker.s.respawnTime < replayLength ) + replayLength += Time() - expect float ( replayAttacker.s.respawnTime ) + + SetServerVar( "roundWinningKillReplayEntHealthFrac", file.roundWinningKillReplayHealthFrac ) + } + + foreach ( entity player in GetPlayerArray() ) + thread PlayerWatchesRoundWinningKillReplay( player, doReplay, replayLength ) + + wait ROUND_WINNING_KILL_REPLAY_SCREEN_FADE_TIME + CleanUpEntitiesForRoundEnd() // fade should be done by this point, so cleanup stuff now when people won't see + wait replayLength + + WaitFrame() // prevent a race condition with PlayerWatchesRoundWinningKillReplay + file.roundWinningKillReplayAttacker = null // clear this + + if ( killcamsWereEnabled ) + SetKillcamsEnabled( true ) + + if ( IsRoundBased() ) + { + svGlobal.levelEnt.Signal( "RoundEnd" ) + int roundsPlayed = expect int ( GetServerVar( "roundsPlayed" ) ) + SetServerVar( "roundsPlayed", roundsPlayed + 1 ) + + float highestScore = max( GameRules_GetTeamScore( TEAM_IMC ), GameRules_GetTeamScore( TEAM_MILITIA ) ) + int roundScoreLimit = GameMode_GetRoundScoreLimit( GAMETYPE ) + + if ( highestScore >= roundScoreLimit ) + 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 ) + SetGameState( eGameState.PickLoadout ) + else + SetGameState ( eGameState.Prematch ) + } + else + { + if ( Evac_IsEnabled() ) + SetGameState( eGameState.Epilogue ) + else + SetGameState( eGameState.Postmatch ) + } +} + +void function PlayerWatchesRoundWinningKillReplay( entity player, bool doReplay, float replayLength ) +{ + player.FreezeControlsOnServer() + + int winningTeam = GetWinningTeam() + int announcementSubstr + if ( winningTeam != TEAM_UNASSIGNED ) + announcementSubstr = player.GetTeam() == winningTeam ? file.announceRoundWinnerWinningSubstr : file.announceRoundWinnerLosingSubstr + + if ( IsRoundBased() ) + 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", winningTeam, announcementSubstr, ROUND_WINNING_KILL_REPLAY_SCREEN_FADE_TIME ) + + if ( IsRoundBased() || !Evac_IsEnabled() ) // if we're doing evac, then no fades or killreplay + { + ScreenFadeToBlackForever( player, ROUND_WINNING_KILL_REPLAY_SCREEN_FADE_TIME ) + wait ROUND_WINNING_KILL_REPLAY_SCREEN_FADE_TIME + + if ( doReplay ) + { + player.SetPredictionEnabled( false ) // prediction fucks with replays + + entity attacker = file.roundWinningKillReplayAttacker + player.SetKillReplayDelay( Time() - replayLength, THIRD_PERSON_KILL_REPLAY_ALWAYS ) + player.SetKillReplayInflictorEHandle( attacker.GetEncodedEHandle() ) + player.SetKillReplayVictim( file.roundWinningKillReplayVictim ) + player.SetViewIndex( attacker.GetIndexForEntity() ) + player.SetIsReplayRoundWinning( true ) + + if ( replayLength >= ROUND_WINNING_KILL_REPLAY_LENGTH_OF_REPLAY - 0.5 ) // only do fade if close to full length replay + { + // this doesn't work because fades don't work on players that are in a replay, unsure how official servers do this + wait replayLength - 2.0 + ScreenFadeToBlackForever( player, 2.0 ) + + wait 2.0 + } + else + wait replayLength + } + else + wait replayLength // this will just be extra delay if no replay + + player.SetPredictionEnabled( true ) + player.ClearReplayDelay() + player.ClearViewEntity() + player.UnfreezeControlsOnServer() + } +} + + +// eGameState.SwitchingSides +void function GameStateEnter_SwitchingSides() +{ + thread GameStateEnter_SwitchingSides_Threaded() +} + +void function GameStateEnter_SwitchingSides_Threaded() +{ + bool killcamsWereEnabled = KillcamsEnabled() + if ( killcamsWereEnabled ) // dont want killcams to interrupt stuff + SetKillcamsEnabled( false ) + + WaitFrame() // wait a frame so callbacks can set killreplay info + + entity replayAttacker = file.roundWinningKillReplayAttacker + bool doReplay = Replay_IsEnabled() && IsRoundWinningKillReplayEnabled() && IsValid( replayAttacker ) && !IsRoundBased() // for roundbased modes, we've already done the replay + && Time() - file.roundWinningKillReplayTime <= SWITCHING_SIDES_DELAY + + float replayLength = SWITCHING_SIDES_DELAY_REPLAY // extra delay if no replay + if ( doReplay ) + { + replayLength = SWITCHING_SIDES_DELAY + if ( "respawnTime" in replayAttacker.s && Time() - replayAttacker.s.respawnTime < replayLength ) + replayLength += Time() - expect float ( replayAttacker.s.respawnTime ) + + SetServerVar( "roundWinningKillReplayEntHealthFrac", file.roundWinningKillReplayHealthFrac ) + } + + foreach ( entity player in GetPlayerArray() ) + thread PlayerWatchesSwitchingSidesKillReplay( player, doReplay, replayLength ) + + wait SWITCHING_SIDES_DELAY_REPLAY + CleanUpEntitiesForRoundEnd() // fade should be done by this point, so cleanup stuff now when people won't see + wait replayLength + + if ( killcamsWereEnabled ) + SetKillcamsEnabled( true ) + + file.hasSwitchedSides = true + svGlobal.levelEnt.Signal( "RoundEnd" ) // might be good to get a new signal for this? not 100% necessary tho i think + SetServerVar( "switchedSides", 1 ) + file.roundWinningKillReplayAttacker = null // reset this after replay + + if ( file.usePickLoadoutScreen ) + SetGameState( eGameState.PickLoadout ) + else + SetGameState ( eGameState.Prematch ) +} + +void function PlayerWatchesSwitchingSidesKillReplay( entity player, bool doReplay, float replayLength ) +{ + player.FreezeControlsOnServer() + + ScreenFadeToBlackForever( player, SWITCHING_SIDES_DELAY_REPLAY ) // automatically cleared + wait SWITCHING_SIDES_DELAY_REPLAY + + if ( doReplay ) + { + player.SetPredictionEnabled( false ) // prediction fucks with replays + + // delay seems weird for switchingsides? ends literally the frame the flag is collected + + entity attacker = file.roundWinningKillReplayAttacker + player.SetKillReplayDelay( Time() - replayLength, THIRD_PERSON_KILL_REPLAY_ALWAYS ) + player.SetKillReplayInflictorEHandle( attacker.GetEncodedEHandle() ) + player.SetKillReplayVictim( file.roundWinningKillReplayVictim ) + player.SetViewIndex( attacker.GetIndexForEntity() ) + player.SetIsReplayRoundWinning( true ) + + if ( replayLength >= SWITCHING_SIDES_DELAY - 0.5 ) // only do fade if close to full length replay + { + // this doesn't work because fades don't work on players that are in a replay, unsure how official servers do this + wait replayLength - ROUND_WINNING_KILL_REPLAY_SCREEN_FADE_TIME + ScreenFadeToBlackForever( player, ROUND_WINNING_KILL_REPLAY_SCREEN_FADE_TIME ) + + wait ROUND_WINNING_KILL_REPLAY_SCREEN_FADE_TIME + } + else + wait replayLength + } + else + wait SWITCHING_SIDES_DELAY_REPLAY // extra delay if no replay + + player.SetPredictionEnabled( true ) + player.ClearReplayDelay() + player.ClearViewEntity() + player.UnfreezeControlsOnServer() +} + + +// eGameState.SuddenDeath +void function GameStateEnter_SuddenDeath() +{ + // disable respawns, suddendeath calling is done on a kill callback + SetRespawnsEnabled( false ) +} + + +// eGameState.Postmatch +void function GameStateEnter_Postmatch() +{ + foreach ( entity player in GetPlayerArray() ) + { + player.FreezeControlsOnServer() + thread ForceFadeToBlack( player ) + } + + thread GameStateEnter_Postmatch_Threaded() +} + +void function GameStateEnter_Postmatch_Threaded() +{ + wait GAME_POSTMATCH_LENGTH + + GameRules_EndMatch() +} + +void function ForceFadeToBlack( entity player ) +{ + // todo: check if this is still necessary + player.EndSignal( "OnDestroy" ) + + // hack until i figure out what deathcam stuff is causing fadetoblacks to be cleared + while ( true ) + { + WaitFrame() + ScreenFadeToBlackForever( player, 0.0 ) + } +} + + +// shared across multiple gamestates + +void function OnPlayerKilled( entity victim, entity attacker, var damageInfo ) +{ + if ( !GamePlayingOrSuddenDeath() ) + return + + // set round winning killreplay info here if we're tracking pilot kills + // todo: make this not count environmental deaths like falls, unsure how to prevent this + if ( file.roundWinningKillReplayTrackPilotKills && victim != attacker && attacker != svGlobal.worldspawn && IsValid( attacker ) ) + { + file.roundWinningKillReplayTime = Time() + file.roundWinningKillReplayVictim = victim + file.roundWinningKillReplayAttacker = attacker + file.roundWinningKillReplayMethodOfDeath = DamageInfo_GetDamageSourceIdentifier( damageInfo ) + file.roundWinningKillReplayTimeOfDeath = Time() + file.roundWinningKillReplayHealthFrac = GetHealthFrac( attacker ) + } + + // note: pilotstitans is just win if enemy team runs out of either pilots or titans + if ( IsPilotEliminationBased() || GetGameState() == eGameState.SuddenDeath ) + { + if ( GetPlayerArrayOfTeam_Alive( victim.GetTeam() ).len() == 0 ) + { + // for ffa we need to manually get the last team alive + if ( IsFFAGame() ) + { + array<int> teamsWithLivingPlayers + foreach ( entity player in GetPlayerArray_Alive() ) + { + if ( !teamsWithLivingPlayers.contains( player.GetTeam() ) ) + teamsWithLivingPlayers.append( player.GetTeam() ) + } + + if ( teamsWithLivingPlayers.len() == 1 ) + SetWinner( teamsWithLivingPlayers[ 0 ], "#GAMEMODE_ENEMY_PILOTS_ELIMINATED", "#GAMEMODE_FRIENDLY_PILOTS_ELIMINATED" ) + else if ( teamsWithLivingPlayers.len() == 0 ) // failsafe: only team was the dead one + SetWinner( TEAM_UNASSIGNED, "#GAMEMODE_ENEMY_PILOTS_ELIMINATED", "#GAMEMODE_FRIENDLY_PILOTS_ELIMINATED" ) // this is fine in ffa + } + else + SetWinner( GetOtherTeam( victim.GetTeam() ), "#GAMEMODE_ENEMY_PILOTS_ELIMINATED", "#GAMEMODE_FRIENDLY_PILOTS_ELIMINATED" ) + } + } + + if ( ( Riff_EliminationMode() == eEliminationMode.Titans || Riff_EliminationMode() == eEliminationMode.PilotsTitans ) && victim.IsTitan() ) // need an extra check for this + OnTitanKilled( victim, damageInfo ) +} + +void function OnTitanKilled( entity victim, var damageInfo ) +{ + if ( !GamePlayingOrSuddenDeath() ) + return + + // set round winning killreplay info here if we're tracking titan kills + // todo: make this not count environmental deaths like falls, unsure how to prevent this + entity attacker = DamageInfo_GetAttacker( damageInfo ) + if ( file.roundWinningKillReplayTrackTitanKills && victim != attacker && attacker != svGlobal.worldspawn && IsValid( attacker ) ) + { + file.roundWinningKillReplayTime = Time() + file.roundWinningKillReplayVictim = victim + file.roundWinningKillReplayAttacker = attacker + file.roundWinningKillReplayMethodOfDeath = DamageInfo_GetDamageSourceIdentifier( damageInfo ) + file.roundWinningKillReplayTimeOfDeath = Time() + file.roundWinningKillReplayHealthFrac = GetHealthFrac( attacker ) + } + + // note: pilotstitans is just win if enemy team runs out of either pilots or titans + if ( IsTitanEliminationBased() ) + { + int livingTitans + foreach ( entity titan in GetTitanArrayOfTeam( victim.GetTeam() ) ) + livingTitans++ + + if ( livingTitans == 0 ) + { + // for ffa we need to manually get the last team alive + if ( IsFFAGame() ) + { + array<int> teamsWithLivingTitans + foreach ( entity titan in GetTitanArray() ) + { + if ( !teamsWithLivingTitans.contains( titan.GetTeam() ) ) + teamsWithLivingTitans.append( titan.GetTeam() ) + } + + if ( teamsWithLivingTitans.len() == 1 ) + SetWinner( teamsWithLivingTitans[ 0 ], "#GAMEMODE_ENEMY_TITANS_DESTROYED", "#GAMEMODE_FRIENDLY_TITANS_DESTROYED" ) + else if ( teamsWithLivingTitans.len() == 0 ) // failsafe: only team was the dead one + SetWinner( TEAM_UNASSIGNED, "#GAMEMODE_ENEMY_TITANS_DESTROYED", "#GAMEMODE_FRIENDLY_TITANS_DESTROYED" ) // this is fine in ffa + } + else + SetWinner( GetOtherTeam( victim.GetTeam() ), "#GAMEMODE_ENEMY_TITANS_DESTROYED", "#GAMEMODE_FRIENDLY_TITANS_DESTROYED" ) + } + } +} + +void function AddCallback_OnRoundEndCleanup( void functionref() callback ) +{ + file.roundEndCleanupCallbacks.append( callback ) +} + +void function CleanUpEntitiesForRoundEnd() +{ + // this function should clean up any and all entities that need to be removed between rounds, ideally at a point where it isn't noticable to players + SetPlayerDeathsHidden( true ) // hide death sounds and such so people won't notice they're dying + + foreach ( entity player in GetPlayerArray() ) + { + ClearTitanAvailable( player ) + + if ( IsAlive( player ) ) + player.Die() + + if ( IsAlive( player.GetPetTitan() ) ) + player.GetPetTitan().Destroy() + } + + foreach ( entity npc in GetNPCArray() ) + if ( IsAlive( npc ) ) + npc.Die() // need this because getnpcarray includes the pettitans we just killed at this point + + // allow other scripts to clean stuff up too + svGlobal.levelEnt.Signal( "CleanUpEntitiesForRoundEnd" ) + foreach ( void functionref() callback in file.roundEndCleanupCallbacks ) + callback() + + SetPlayerDeathsHidden( false ) +} + + + +// stuff for gamemodes to call + +void function SetShouldUsePickLoadoutScreen( bool shouldUse ) +{ + file.usePickLoadoutScreen = shouldUse +} + +void function SetSwitchSidesBased( bool switchSides ) +{ + file.switchSidesBased = switchSides +} + +void function SetSuddenDeathBased( bool suddenDeathBased ) +{ + file.suddenDeathBased = suddenDeathBased +} + +void function SetShouldUseRoundWinningKillReplay( bool shouldUse ) +{ + SetServerVar( "roundWinningKillReplayEnabled", shouldUse ) +} + +void function SetRoundWinningKillReplayKillClasses( bool pilot, bool titan ) +{ + file.roundWinningKillReplayTrackPilotKills = pilot + file.roundWinningKillReplayTrackTitanKills = titan // player kills in titans should get tracked anyway, might be worth renaming this +} + +void function SetRoundWinningKillReplayAttacker( entity attacker ) +{ + file.roundWinningKillReplayTime = Time() + file.roundWinningKillReplayHealthFrac = GetHealthFrac( attacker ) + file.roundWinningKillReplayAttacker = attacker +} + +void function SetWinner( int team, string winningReason = "", string losingReason = "" ) +{ + SetServerVar( "winningTeam", team ) + + if ( winningReason.len() == 0 ) + file.announceRoundWinnerWinningSubstr = 0 + else + file.announceRoundWinnerWinningSubstr = GetStringID( winningReason ) + + if ( losingReason.len() == 0 ) + file.announceRoundWinnerLosingSubstr = 0 + else + file.announceRoundWinnerLosingSubstr = GetStringID( losingReason ) + + if ( IsRoundBased() ) + { + if ( team != TEAM_UNASSIGNED ) + { + GameRules_SetTeamScore( team, GameRules_GetTeamScore( team ) + 1 ) + GameRules_SetTeamScore2( team, GameRules_GetTeamScore2( team ) + 1 ) + } + + SetGameState( eGameState.WinnerDetermined ) + } + else + SetGameState( eGameState.WinnerDetermined ) +} + +void function AddTeamScore( int team, int amount ) +{ + GameRules_SetTeamScore( team, GameRules_GetTeamScore( team ) + amount ) + GameRules_SetTeamScore2( team, GameRules_GetTeamScore2( team ) + amount ) + + int scoreLimit + if ( IsRoundBased() ) + scoreLimit = GameMode_GetRoundScoreLimit( GAMETYPE ) + else + scoreLimit = GameMode_GetScoreLimit( GAMETYPE ) + + int score = GameRules_GetTeamScore( team ) + if ( score >= scoreLimit || GetGameState() == eGameState.SuddenDeath ) + SetWinner( team ) + else if ( ( file.switchSidesBased && !file.hasSwitchedSides ) && score >= ( scoreLimit.tofloat() / 2.0 ) ) + SetGameState( eGameState.SwitchingSides ) +} + +void function SetTimeoutWinnerDecisionFunc( int functionref() callback ) +{ + file.timeoutWinnerDecisionFunc = callback +} + +// idk + +float function GameState_GetTimeLimitOverride() +{ + return 100 +} + +bool function IsRoundBasedGameOver() +{ + return false +} + +bool function ShouldRunEvac() +{ + return true +} + +void function GiveTitanToPlayer( entity player ) +{ + +} + +float function GetTimeLimit_ForGameMode() +{ + return 100.0 +}
\ No newline at end of file |