diff options
Diffstat (limited to 'Northstar.CustomServers')
6 files changed, 201 insertions, 261 deletions
diff --git a/Northstar.CustomServers/scripts/vscripts/_powerup.gnut b/Northstar.CustomServers/scripts/vscripts/_powerup.gnut index 40984d0d..a154ddb6 100644 --- a/Northstar.CustomServers/scripts/vscripts/_powerup.gnut +++ b/Northstar.CustomServers/scripts/vscripts/_powerup.gnut @@ -1,91 +1,84 @@ untyped global function PowerUps_Init +struct { + array<entity> powerupSpawns +} file + void function PowerUps_Init() { - SH_PowerUp_Init() - - AddCallback_EntitiesDidLoad( EntitesDidLoad ) + SH_PowerUp_Init() + + AddSpawnCallbackEditorClass( "script_ref", "script_power_up_other", AddPowerupSpawn ) + AddCallback_OnTouchHealthKit( "item_powerup", OnPowerupCollected ) } -void function EntitesDidLoad() +void function AddPowerupSpawn( entity spawnpoint ) { - array<entity> scriptRefs = GetEntArrayByClass_Expensive( "script_ref" ) - foreach ( entity ref in scriptRefs ) - if ( ref.HasKey( "powerUpType" ) ) - { - PowerUp powerup = GetPowerUpFromItemRef( expect string( ref.kv.powerUpType ) ) - - // CreatePickup is defined in mp/_pickups.gnut - // mp/_pickups.gnut is a sp-only script - // it's literally in the mp folder - // respawn PLEASE - //CreatePickup( ref, powerup.model, bool function( entity player ) { powerup.destroyFunc( player ); return true } ) - - if ( powerup.spawnFunc() ) - { - CreatePropDynamic( powerup.baseModel, ref.GetOrigin(), ref.GetAngles(), 2 ) - thread PowerUpThink( ref, powerup ) - } - } - - AddCallback_OnTouchHealthKit( "item_powerup", OnPowerUpCollected ) + file.powerupSpawns.append( spawnpoint ) } -entity function CreatePowerUp( entity spawnpoint, PowerUp powerup ) +void function RespawnPowerups() { - entity powerupEnt = CreateEntity( "item_powerup" ) - - powerupEnt.SetValueForModelKey( powerup.model ) - powerupEnt.kv.fadedist = 10000 - powerupEnt.kv.gravity = 0.000001 // really hacky, but gravity 0.0 is considered the same as 1.0, and i'm not sure how to enable/disable gravity on entities in script - - DispatchSpawn( powerupEnt ) - - powerupEnt.SetModel( powerup.model ) - powerupEnt.SetOrigin( spawnpoint.GetOrigin() + powerup.modelOffset ) - powerupEnt.SetAngles( spawnpoint.GetAngles() + powerup.modelAngles ) - - powerupEnt.s.powerUpType <- powerup.itemRef - - return powerupEnt + foreach ( entity spawnpoint in file.powerupSpawns ) + { + PowerUp powerupDef = GetPowerUpFromItemRef( expect string( spawnpoint.kv.powerUpType ) ) + thread PowerupSpawnerThink( spawnpoint, powerupDef ) + } } -void function PowerUpThink( entity spawnpoint, PowerUp powerup ) +void function PowerupSpawnerThink( entity spawnpoint, PowerUp powerupDef ) { - svGlobal.levelEnt.EndSignal( "RoundEnd" ) // should reset on round end + svGlobal.levelEnt.EndSignal( "CleanUpEntitiesForRoundEnd" ) - entity powerupEnt + entity base = CreatePropDynamic( powerupDef.baseModel, spawnpoint.GetOrigin(), spawnpoint.GetAngles(), 2 ) + OnThreadEnd( function() : ( base ) + { + base.Destroy() + }) while ( true ) { - powerupEnt = CreatePowerUp( spawnpoint, powerup ) - - OnThreadEnd( function() : ( powerupEnt, spawnpoint, powerup ) - { - // should be called on round end - print( "resetting powerup..." ) - if ( IsValid( powerupEnt ) ) - powerupEnt.Destroy() - - // recursively spawn new powerup - thread PowerUpThink( spawnpoint, powerup ) - } - ) - - // handle the glow here so we can destroy it - PickupGlow glow = CreatePickupGlow( powerupEnt, powerup.glowColor.x.tointeger(), powerup.glowColor.y.tointeger(), powerup.glowColor.z.tointeger() ) + if ( !powerupDef.spawnFunc() ) + { + // unsure if this is the best way of doing it + WaitFrame() + continue + } + + entity powerup = CreateEntity( "item_powerup" ) + + powerup.SetParent( base ) // parenting ensures that gravity isn't an issue + powerup.SetValueForModelKey( powerupDef.model ) + + DispatchSpawn( powerup ) + + // offset from parent's position/angles + powerup.SetOrigin( powerupDef.modelOffset ) + powerup.SetAngles( 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 - powerupEnt.WaitSignal( "OnDestroy" ) + OnThreadEnd( function() : ( powerup ) + { + if ( IsValid( powerup ) ) + { + powerup.Destroy() + } + }) - wait powerup.respawnDelay + powerup.WaitSignal( "OnDestroy" ) + wait powerupDef.respawnDelay } } -bool function OnPowerUpCollected( entity player, entity healthpack ) +bool function OnPowerupCollected( entity player, entity healthpack ) { - PowerUp powerup = GetPowerUpFromItemRef( expect string( healthpack.s.powerUpType ) ) + PowerUp powerup = GetPowerUpFromItemRef( expect string( healthpack.s.powerupRef ) ) if ( player.IsTitan() == powerup.titanPickup ) { diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ctf.nut b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ctf.nut index e710a911..f3490730 100644 --- a/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ctf.nut +++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ctf.nut @@ -30,7 +30,8 @@ void function CaptureTheFlag_Init() CaptureTheFlagShared_Init() SetSwitchSidesBased( true ) SetSuddenDeathBased( true ) - //SetSpawnsUseFrontline( true ) + SetShouldUseRoundWinningKillReplay( true ) + SetRoundWinningKillReplayKillClasses( false, false ) // make these fully manual AddCallback_OnClientConnected( CTFInitPlayer ) @@ -417,6 +418,7 @@ void function CaptureFlag( entity player, entity flag ) AddTeamScore( team, 1 ) AddPlayerScore( player, "FlagCapture", player ) player.AddToPlayerGameStat( PGS_ASSAULT_SCORE, 1 ) // add 1 to captures on scoreboard + SetRoundWinningKillReplayAttacker( player ) // set attacker for last cap replay array<entity> assistList if ( player.GetTeam() == TEAM_IMC ) diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_lts.nut b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_lts.nut index 18cf9735..e8231aad 100644 --- a/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_lts.nut +++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_lts.nut @@ -17,49 +17,49 @@ void function GamemodeLts_Init() SetRoundBased( true ) SetRespawnsEnabled( false ) Riff_ForceSetEliminationMode( eEliminationMode.PilotsTitans ) - SetServerVar( "roundWinningKillReplayEnabled", true ) // really ought to get a function for setting this - - AddCallback_OnPlayerKilled( OnPlayerKilled ) - AddDeathCallback( "npc_titan", OnTitanKilled ) + SetShouldUseRoundWinningKillReplay( true ) + SetRoundWinningKillReplayKillClasses( true, true ) // both titan and pilot kills are tracked AddDamageCallback( "player", OnPlayerDamaged ) AddDamageCallback( "npc_titan", OnTitanDamaged ) - AddCallback_OnPilotBecomesTitan( GamemodeLTS_RefreshHighlight ) - AddCallback_OnTitanBecomesPilot( GamemodeLTS_RefreshHighlight ) + AddCallback_OnPilotBecomesTitan( RefreshThirtySecondWallhackHighlight ) + AddCallback_OnTitanBecomesPilot( RefreshThirtySecondWallhackHighlight ) + + SetTimeoutWinnerDecisionFunc( CheckTitanHealthForDraw ) - ClassicMP_SetCustomIntro( GamemodeLTS_Intro, 0.0 ) + ClassicMP_SetCustomIntro( GamemodeLTS_Intro, 0.0 ) // dont any sorta } +// this should also probably be moved into a generic intro rather than being lts-specific void function GamemodeLTS_Intro() { - AddCallback_GameStateEnter( eGameState.Prematch, GamemodeLTS_IntroOnPrematchStart ) + AddCallback_GameStateEnter( eGameState.Prematch, LTSIntroOnPrematchStart ) } -void function GamemodeLTS_IntroOnPrematchStart() +void function LTSIntroOnPrematchStart() { ClassicMP_OnIntroStarted() - - SetGameState( eGameState.Playing ) + foreach ( entity player in GetPlayerArray() ) - thread GamemodeLTS_IntroSpawnPlayer( player ) + thread LTSIntroSpawnPlayer( player ) + + wait 2.0 // literally a guess number for how long the drop might take ClassicMP_OnIntroFinished() - SetKillcamsEnabled( true ) - file.shouldDoHighlights = false thread GamemodeLTS_PlayingThink() } -void function GamemodeLTS_IntroSpawnPlayer( entity player ) +void function LTSIntroSpawnPlayer( entity player ) { if ( IsAlive( player ) ) { player.Die() - WaitFrame() + WaitFrame() // this doesn't work for some reason but the player will die in roundend anyway so not really an issue } - - RespawnAsTitan( player, false ) + + thread RespawnAsTitan( player, false ) while ( !player.IsTitan() ) WaitFrame() @@ -69,12 +69,9 @@ void function GamemodeLTS_IntroSpawnPlayer( entity player ) void function GamemodeLTS_PlayingThink() { - WaitFrame() // due to how this is all written the prematch callbacks might not've run by the time this starts - // so we need to wait a frame to ensure they've been run so gameEndTime is set svGlobal.levelEnt.EndSignal( "RoundEnd" ) // end this on round end float endTime = expect float ( GetServerVar( "gameEndTime" ) ) - print( "ENDTIME " + endTime ) // wait until 30sec left wait endTime - 30 - Time() @@ -83,18 +80,14 @@ void function GamemodeLTS_PlayingThink() // warn there's 30 seconds left Remote_CallFunction_NonReplay( player, "ServerCallback_LTSThirtySecondWarning" ) - // do highlights - file.shouldDoHighlights = true - GamemodeLTS_RefreshHighlight( player, null ) + // do initial highlight + RefreshThirtySecondWallhackHighlight( player, null ) } - - wait endTime - Time() - thread CheckTitansForDraw() // need to thread this so we don't accidentally signal roundend in the same thread that'll be ended when we hit roundend } -void function GamemodeLTS_RefreshHighlight( entity player, entity titan ) +void function RefreshThirtySecondWallhackHighlight( entity player, entity titan ) { - if ( !file.shouldDoHighlights ) + if ( TimeSpentInCurrentState() < 30.0 ) return Highlight_SetEnemyHighlight( player, "enemy_sonar" ) // i think this needs a different effect, this works for now tho @@ -103,133 +96,59 @@ void function GamemodeLTS_RefreshHighlight( entity player, entity titan ) Highlight_SetEnemyHighlight( player.GetPetTitan(), "enemy_sonar" ) } -void function CheckTeamTitans( int team ) +int function CheckTitanHealthForDraw() { - if ( GetGameState() != eGameState.Playing ) - return - - array<entity> teamPlayers = GetPlayerArrayOfTeam( team ) - - int numLivingTitans = 0 - int numLivingPlayers = 0 - foreach ( entity player in teamPlayers ) - { - // wouldn't it be easier just to only track and increment numLivingTitans if the owner is alive? - // yes it would - // but for some reason this is not how respawn does it - if ( IsAlive( player ) ) - numLivingPlayers++ + int militiaTitans + int imcTitans - if ( IsAlive( player.GetPetTitan() ) || player.IsTitan() ) - numLivingTitans++ - } - - if ( numLivingPlayers == 0 || numLivingTitans == 0 ) - { - SetKillcamsEnabled( false ) // make sure killcams can't interrupt the round winning kill replay - //SetRoundWinningKillReplayInfo( file.lastDamageInfoVictim, file.lastDamageInfoAttacker, file.lastDamageInfoMethodOfDeath, file.lastDamageInfoTime ) - SetWinner( GetOtherTeam( team ), "#GAMEMODE_ENEMY_TITANS_DESTROYED", "#GAMEMODE_FRIENDLY_TITANS_DESTROYED" ) - } -} - -void function CheckTitansForDraw() -{ - int militiaLivingTitans - int imcLivingTitans + float militiaHealth + float imcHealth - float militiaCombinedHealth - float imcCombinedHealth - - foreach ( entity player in GetPlayerArray() ) + foreach ( entity titan in GetTitanArray() ) { - // only need to track titans for this, can assume that neither team has lost due to titan death if the round is still going - entity titan = IsAlive( player.GetPetTitan() ) ? player.GetPetTitan() : player - if ( titan.IsPlayer() && !titan.IsTitan() ) - continue - - if ( IsAlive( titan ) ) - if ( player.GetTeam() == TEAM_MILITIA ) - { - // doomed is counted as 0 health in this - militiaCombinedHealth += titan.GetTitanSoul().IsDoomed() ? 0.0 : GetHealthFrac( titan ) - militiaLivingTitans++ - } - else - { - // doomed is counted as 0 health in this - imcCombinedHealth += titan.GetTitanSoul().IsDoomed() ? 0.0 : GetHealthFrac( titan ) - imcLivingTitans++ - } + if ( titan.GetTeam() == TEAM_MILITIA ) + { + // doomed is counted as 0 health + militiaHealth += titan.GetTitanSoul().IsDoomed() ? 0.0 : GetHealthFrac( titan ) + militiaTitans++ + } + else + { + // doomed is counted as 0 health in this + imcHealth += titan.GetTitanSoul().IsDoomed() ? 0.0 : GetHealthFrac( titan ) + imcTitans++ + } } - SetKillcamsEnabled( false ) - //SetRoundWinningKillReplayInfo( null, null, 0, 0 ) // make sure we don't do a replay - - // default if both teams are equal - int winner = TEAM_UNASSIGNED - - string winnerSubstr - string loserSubstr + // note: due to how stuff is set up rn, there's actually no way to do win/loss reasons in timeout decision funcs + // as soon as there is, strings in question are "#GAMEMODE_TITAN_TITAN_ADVANTAGE" and "#GAMEMODE_TITAN_TITAN_DISADVANTAGE" - if ( militiaLivingTitans != imcLivingTitans ) // one team has a titan lead - { - winnerSubstr = "#GAMEMODE_TITAN_TITAN_ADVANTAGE" - loserSubstr = "#GAMEMODE_TITAN_TITAN_DISADVANTAGE" + if ( militiaTitans != imcTitans ) + return militiaTitans > imcTitans ? TEAM_MILITIA : TEAM_IMC + else if ( militiaHealth != imcHealth ) + return militiaHealth > imcHealth ? TEAM_MILITIA : TEAM_IMC - winner = militiaLivingTitans > imcLivingTitans ? TEAM_MILITIA : TEAM_IMC - } - else if ( militiaCombinedHealth != imcCombinedHealth ) // one team has a health lead - { - winnerSubstr = "#GAMEMODE_TITAN_DAMAGE_ADVANTAGE" - loserSubstr = "#GAMEMODE_TITAN_DAMAGE_DISADVANTAGE" - - winner = militiaCombinedHealth > imcCombinedHealth ? TEAM_MILITIA : TEAM_IMC - } - - print( "CheckTitansForDraw(): " + winner ) - SetWinner( winner, winnerSubstr, loserSubstr ) -} - -void function OnPlayerKilled( entity victim, entity attacker, var damageInfo ) -{ - file.lastDamageInfoVictim = victim - file.lastDamageInfoAttacker = DamageInfo_GetAttacker( damageInfo ) - file.lastDamageInfoMethodOfDeath = DamageInfo_GetDamageSourceIdentifier( damageInfo ) - file.lastDamageInfoTime = Time() - - if ( !victim.isSpawning ) - CheckTeamTitans( victim.GetTeam() ) -} - -void function OnTitanKilled( entity titan, var damageInfo ) -{ - file.lastDamageInfoVictim = titan.GetOwner() - file.lastDamageInfoAttacker = DamageInfo_GetAttacker( damageInfo ) - file.lastDamageInfoMethodOfDeath = DamageInfo_GetDamageSourceIdentifier( damageInfo ) - file.lastDamageInfoTime = Time() - - if ( IsPetTitan( titan ) && !titan.GetBossPlayer().isSpawning ) - CheckTeamTitans( titan.GetTeam() ) + return TEAM_UNASSIGNED } -void function AddToDamageStat( var damageInfo ) +// this should be generic, not restricted to a specific gamemode +void function AddToTitanDamageStat( entity victim, var damageInfo ) { // todo: this needs to not count selfdamage entity attacker = DamageInfo_GetAttacker( damageInfo ) float amount = DamageInfo_GetDamage( damageInfo ) - if ( attacker.IsPlayer() ) + if ( attacker.IsPlayer() && attacker != victim ) attacker.AddToPlayerGameStat( PGS_ASSAULT_SCORE, amount ) // titan damage on } void function OnPlayerDamaged( entity player, var damageInfo ) { if ( player.IsTitan() ) - AddToDamageStat( damageInfo ) + AddToTitanDamageStat( player, damageInfo ) } void function OnTitanDamaged( entity titan, var damageInfo ) { - if ( IsPetTitan( titan ) ) - AddToDamageStat( damageInfo ) + AddToTitanDamageStat( titan, damageInfo ) }
\ No newline at end of file diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_speedball.nut b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_speedball.nut index 9c70cfb9..91ebf8c6 100644 --- a/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_speedball.nut +++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_speedball.nut @@ -14,9 +14,9 @@ void function GamemodeSpeedball_Init() // gamemode settings SetRoundBased( true ) SetRespawnsEnabled( false ) + SetShouldUseRoundWinningKillReplay( true ) Riff_ForceTitanAvailability( eTitanAvailability.Never ) Riff_ForceSetEliminationMode( eEliminationMode.Pilots ) - SetServerVar( "roundWinningKillReplayEnabled", true ) // really ought to get a function for setting this AddSpawnCallbackEditorClass( "script_ref", "info_speedball_flag", CreateFlag ) @@ -59,19 +59,9 @@ void function OnPlayerKilled( entity victim, entity attacker, var damageInfo ) DropFlag() if ( victim.IsPlayer() && GetGameState() == eGameState.Playing ) - { - // this REALLY ought to be an elimationmode thing rather than gamemode-based - int livingPlayers - foreach ( entity player in GetPlayerArrayOfTeam( victim.GetTeam() ) ) - if ( IsAlive( player ) ) - livingPlayers++ - - if ( livingPlayers == 0 ) - SetWinner( GetOtherTeam( victim.GetTeam() ) ) - else if ( livingPlayers == 1 ) + if ( GetPlayerArrayOfTeam_Alive( victim.GetTeam() ).len() == 1 ) foreach ( entity player in GetPlayerArray() ) Remote_CallFunction_NonReplay( player, "ServerCallback_SPEEDBALL_LastPlayer", player.GetTeam() != victim.GetTeam() ) - } } void function GiveFlag( entity player ) diff --git a/Northstar.CustomServers/scripts/vscripts/mp/_base_gametype_mp.gnut b/Northstar.CustomServers/scripts/vscripts/mp/_base_gametype_mp.gnut index 244d323e..8069b4a5 100644 --- a/Northstar.CustomServers/scripts/vscripts/mp/_base_gametype_mp.gnut +++ b/Northstar.CustomServers/scripts/vscripts/mp/_base_gametype_mp.gnut @@ -12,6 +12,7 @@ global function TryGameModeAnnouncement global function SetKillcamsEnabled global function KillcamsEnabled +global function SetPlayerDeathsHidden global function ShouldEntTakeDamage_SPMP global function GetTitanBuildTime @@ -19,6 +20,7 @@ global function TitanPlayerHotDropsIntoLevel struct { bool killcamsEnabled = true + bool playerDeathsHidden = false entity intermissionCamera array<entity> specCams @@ -148,6 +150,10 @@ void function CodeCallback_OnClientConnectionCompleted( entity player ) PlayCurrentTeamMusicEventsOnPlayer( player ) SetCurrentTeamObjectiveForPlayer( player ) + entity skycam = GetEnt( "skybox_cam_level" ) + if ( skycam != null ) + player.SetSkyCamera( skycam ) + FinishClientScriptInitialization( player ) // Added via AddCallback_OnClientConnected @@ -304,7 +310,8 @@ void function PostDeathThread_MP( entity player, var damageInfo ) // based on ga player.SetObserverTarget( null ) } - Remote_CallFunction_NonReplay( player, "ServerCallback_YouDied", attacker.GetEncodedEHandle(), GetHealthFrac( attacker ), methodOfDeath ) + if ( !file.playerDeathsHidden ) + Remote_CallFunction_NonReplay( player, "ServerCallback_YouDied", attacker.GetEncodedEHandle(), GetHealthFrac( attacker ), methodOfDeath ) float deathcamLength = GetDeathCamLength( player ) wait deathcamLength @@ -385,15 +392,6 @@ void function EndReplayOnTime( entity player, float replayLength ) void function DecideRespawnPlayer( entity player ) { // this isn't even used atm, could likely be removed if some vanilla code didn't rely on it - - Assert( IsValid( player ), player + " is invalid!!" ) - Assert( !IsAlive( player ), player + " is already alive" ) - Assert( player.hasConnected, player + "isn't connected" ) - - if ( GetClassicMPMode() && GetGameState() < eGameState.Playing ) - return // let intro functions handle spawning if we're in classicmp and not spawned yet - - } void function RespawnAsPilot( entity player, bool manualPosition = false ) @@ -437,7 +435,7 @@ void function RespawnAsTitan( entity player, bool manualPosition = false ) vector xyOffset = RotateAroundOrigin2D( < 44, 0, 520 >, < 0, 0, 0 >, spawnpoint.GetAngles().y ) camera.SetLocalOrigin( xyOffset ) - camera.SetLocalAngles( < camera.GetAngles().x, spawnpoint.GetAngles().y, camera.GetAngles().z > ) + camera.SetLocalAngles( < camera.GetAngles().x, spawnpoint.GetAngles().y, camera.GetAngles().z > ) // this straight up just does not work lol camera.Fire( "Enable", "!activator", 0, player ) waitthread TitanHotDrop( titan, "at_hotdrop_01", spawnpoint.GetOrigin(), spawnpoint.GetAngles(), player, camera ) // do hotdrop anim @@ -474,7 +472,6 @@ void function PlayerBecomesSpectator( entity player ) foreach( entity cam in file.specCams ) targets.append( cam ) - array<entity> targetPlayers if ( IsFFAGame() ) targetPlayers = GetPlayerArray_Alive() @@ -566,9 +563,13 @@ bool function KillcamsEnabled() return file.killcamsEnabled } -// stuff to change later +void function SetPlayerDeathsHidden( bool hidden ) +{ + file.playerDeathsHidden = hidden +} +// stuff to change later bool function ShouldEntTakeDamage_SPMP( entity ent, var damageInfo ) { diff --git a/Northstar.CustomServers/scripts/vscripts/mp/_gamestate_mp.nut b/Northstar.CustomServers/scripts/vscripts/mp/_gamestate_mp.nut index 2fa24e9d..21d529e4 100644 --- a/Northstar.CustomServers/scripts/vscripts/mp/_gamestate_mp.nut +++ b/Northstar.CustomServers/scripts/vscripts/mp/_gamestate_mp.nut @@ -39,11 +39,14 @@ struct { 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() @@ -69,9 +72,10 @@ void function PIN_GameStart() AddCallback_GameStateEnter( eGameState.SuddenDeath, GameStateEnter_SuddenDeath ) AddCallback_GameStateEnter( eGameState.Postmatch, GameStateEnter_Postmatch ) - AddCallback_OnClientConnected( SetSkyCam ) // had no idea where to put this lol AddCallback_OnPlayerKilled( OnPlayerKilled ) AddDeathCallback( "npc_titan", OnTitanKilled ) + + RegisterSignal( "CleanUpEntitiesForRoundEnd" ) } void function SetGameState( int newState ) @@ -241,11 +245,16 @@ void function GameStateEnter_WinnerDetermined() 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() && Evac_IsEnabled() && IsRoundWinningKillReplayEnabled() && IsValid( replayAttacker ) - + 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 ) { @@ -256,7 +265,15 @@ void function GameStateEnter_WinnerDetermined_Threaded() foreach ( entity player in GetPlayerArray() ) thread PlayerWatchesRoundWinningKillReplay( player, doReplay, replayLength ) - wait replayLength + ROUND_WINNING_KILL_REPLAY_SCREEN_FADE_TIME + 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() ) { @@ -266,7 +283,7 @@ void function GameStateEnter_WinnerDetermined_Threaded() if ( max( GameRules_GetTeamScore( TEAM_IMC ), GameRules_GetTeamScore( TEAM_MILITIA ) ) >= GameMode_GetRoundScoreLimit( GAMETYPE ) ) SetGameState( eGameState.Postmatch ) else if ( file.switchSidesBased && !file.hasSwitchedSides ) - SetGameState( eGameState.SwitchingSides ) + SetGameState( eGameState.SwitchingSides ) // note: switchingsides will handle setting to pickloadout and prematch by itself else if ( file.usePickLoadoutScreen ) SetGameState( eGameState.PickLoadout ) else @@ -300,12 +317,6 @@ void function PlayerWatchesRoundWinningKillReplay( entity player, bool doReplay, ScreenFadeToBlackForever( player, ROUND_WINNING_KILL_REPLAY_SCREEN_FADE_TIME ) wait ROUND_WINNING_KILL_REPLAY_SCREEN_FADE_TIME - // do this after screen goes black so people can't see the titan dying - // don't use .die since that makes explosions and that - // todo: need a function specifically for cleaning up npcs and stuff on round end, this is imperfect - if ( IsAlive( player.GetPetTitan() ) ) - player.GetPetTitan().Destroy() - if ( doReplay ) { player.SetPredictionEnabled( false ) // prediction fucks with replays @@ -347,8 +358,15 @@ void function GameStateEnter_SwitchingSides() 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 <= ROUND_WINNING_KILL_REPLAY_LENGTH_OF_REPLAY float replayLength = SWITCHING_SIDES_DELAY_REPLAY // extra delay if no replay if ( doReplay ) @@ -360,9 +378,15 @@ void function GameStateEnter_SwitchingSides_Threaded() foreach ( entity player in GetPlayerArray() ) thread PlayerWatchesSwitchingSidesKillReplay( player, doReplay, replayLength ) - wait SWITCHING_SIDES_DELAY_REPLAY + 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 @@ -379,11 +403,6 @@ void function PlayerWatchesSwitchingSidesKillReplay( entity player, bool doRepla ScreenFadeToBlackForever( player, SWITCHING_SIDES_DELAY_REPLAY ) // automatically cleared wait SWITCHING_SIDES_DELAY_REPLAY - // do this after screen goes black so people can't see the titan dying - // don't use .die since that makes explosions and that - if ( IsAlive( player.GetPetTitan() ) ) - player.GetPetTitan().Destroy() - if ( doReplay ) { player.SetPredictionEnabled( false ) // prediction fucks with replays @@ -483,13 +502,6 @@ void function ForceFadeToBlack( entity player ) // shared across multiple gamestates -void function SetSkyCam( entity player ) -{ - entity skycam = GetEnt( "skybox_cam_level" ) - - if ( skycam != null ) - player.SetSkyCamera( skycam ) -} void function OnPlayerKilled( entity victim, entity attacker, var damageInfo ) { @@ -499,6 +511,7 @@ void function OnPlayerKilled( entity victim, entity attacker, var damageInfo ) // set round winning killreplay info here if no custom replaydelay if ( file.roundWinningKillReplayTrackPilotKills ) { + file.roundWinningKillReplayTime = Time() file.roundWinningKillReplayVictim = victim file.roundWinningKillReplayAttacker = attacker file.roundWinningKillReplayMethodOfDeath = DamageInfo_GetDamageSourceIdentifier( damageInfo ) @@ -532,7 +545,7 @@ void function OnPlayerKilled( entity victim, entity attacker, var damageInfo ) } if ( ( Riff_EliminationMode() == eEliminationMode.Titans || Riff_EliminationMode() == eEliminationMode.PilotsTitans ) && victim.IsTitan() ) // need an extra check for this - OnTitanKilled( victim, damageInfo ) + OnTitanKilled( victim, damageInfo ) } void function OnTitanKilled( entity victim, var damageInfo ) @@ -545,6 +558,7 @@ void function OnTitanKilled( entity victim, var damageInfo ) { entity attacker = DamageInfo_GetAttacker( damageInfo ) + file.roundWinningKillReplayTime = Time() file.roundWinningKillReplayVictim = victim file.roundWinningKillReplayAttacker = attacker file.roundWinningKillReplayMethodOfDeath = DamageInfo_GetDamageSourceIdentifier( damageInfo ) @@ -582,6 +596,36 @@ void function OnTitanKilled( entity victim, var damageInfo ) } } +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() ) + { + if ( IsAlive( player ) ) + player.Die() + + if ( IsAlive( player.GetPetTitan() ) ) + player.GetPetTitan().Destroy() + } + + foreach ( entity npc in GetNPCArray() ) + npc.Die() + + // 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 @@ -606,16 +650,17 @@ void function SetShouldUseRoundWinningKillReplay( bool shouldUse ) SetServerVar( "roundWinningKillReplayEnabled", shouldUse ) } -// is this necessary? idk really 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 target ) +void function SetRoundWinningKillReplayAttacker( entity attacker ) { - file.roundWinningKillReplayAttacker = target + file.roundWinningKillReplayTime = Time() + file.roundWinningKillReplayHealthFrac = GetHealthFrac( attacker ) + file.roundWinningKillReplayAttacker = attacker } void function SetWinner( int team, string winningReason = "", string losingReason = "" ) @@ -664,16 +709,6 @@ void function AddTeamScore( int team, int amount ) SetGameState( eGameState.SwitchingSides ) } -void function SetRoundWinningKillReplayInfo( entity victim, entity attacker, int methodOfDeath, float timeOfDeath ) // can't just pass in a damageinfo because they seem to die over time somehow -{ - file.roundWinningKillReplayVictim = victim - file.roundWinningKillReplayAttacker = attacker - file.roundWinningKillReplayMethodOfDeath = methodOfDeath - file.roundWinningKillReplayTimeOfDeath = timeOfDeath - if ( attacker != null ) - file.roundWinningKillReplayHealthFrac = GetHealthFrac( attacker ) -} - void function SetTimeoutWinnerDecisionFunc( int functionref() callback ) { file.timeoutWinnerDecisionFunc = callback @@ -696,7 +731,7 @@ bool function ShouldRunEvac() return true } -void function GiveTitanToPlayer(entity player) +void function GiveTitanToPlayer( entity player ) { } |