aboutsummaryrefslogtreecommitdiff
path: root/Northstar.CustomServers
diff options
context:
space:
mode:
authorBobTheBob <32057864+BobTheBob9@users.noreply.github.com>2021-06-23 05:06:17 +0100
committerBobTheBob <32057864+BobTheBob9@users.noreply.github.com>2021-06-23 05:06:17 +0100
commit8d4c71f7779120298773c9232dcdaa0fa0063786 (patch)
treeba87b4e511de66161faabd7ba8ffa3efcab08974 /Northstar.CustomServers
parent207facbc402f5639cbcd31f079214351ef605cf2 (diff)
downloadNorthstarMods-8d4c71f7779120298773c9232dcdaa0fa0063786.tar.gz
NorthstarMods-8d4c71f7779120298773c9232dcdaa0fa0063786.zip
lts and powerup rewrite, more gamestate changes
Diffstat (limited to 'Northstar.CustomServers')
-rw-r--r--Northstar.CustomServers/scripts/vscripts/_powerup.gnut119
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ctf.nut4
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_lts.nut185
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_speedball.nut14
-rw-r--r--Northstar.CustomServers/scripts/vscripts/mp/_base_gametype_mp.gnut27
-rw-r--r--Northstar.CustomServers/scripts/vscripts/mp/_gamestate_mp.nut113
6 files changed, 201 insertions, 261 deletions
diff --git a/Northstar.CustomServers/scripts/vscripts/_powerup.gnut b/Northstar.CustomServers/scripts/vscripts/_powerup.gnut
index 40984d0db..a154ddb6f 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 e710a9118..f34907307 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 18cf97359..e8231aad8 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 9c70cfb90..91ebf8c69 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 244d323ea..8069b4a57 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 2fa24e9da..21d529e4d 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 )
{
}