aboutsummaryrefslogtreecommitdiff
path: root/Northstar.CustomServers/scripts/vscripts/mp/_revive.gnut
diff options
context:
space:
mode:
Diffstat (limited to 'Northstar.CustomServers/scripts/vscripts/mp/_revive.gnut')
-rw-r--r--Northstar.CustomServers/scripts/vscripts/mp/_revive.gnut352
1 files changed, 352 insertions, 0 deletions
diff --git a/Northstar.CustomServers/scripts/vscripts/mp/_revive.gnut b/Northstar.CustomServers/scripts/vscripts/mp/_revive.gnut
new file mode 100644
index 00000000..b2f5c467
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/mp/_revive.gnut
@@ -0,0 +1,352 @@
+untyped
+
+
+global function Revive_Init
+
+global function PlayerRevivesOrBleedsOut
+global function DeathPackage_PlayerRevive
+global function ShouldRevivePlayer
+
+const float REVIVE_BLEED_OUT_TIME = 15.0
+global const float REVIVE_DEATH_TIME = 2.0
+const float REVIVE_DIST_OUTER = 135.0
+const float REVIVE_DIST_INNER = 120.0
+
+struct
+{
+ table fakePlayers
+} file
+
+function Revive_Init()
+{
+ if ( !ReviveEnabled() )
+ return
+
+ RegisterSignal( "KillReviveNag" )
+ RegisterSignal( "DoneBleedingOut" )
+ RegisterSignal( "ReviveSucceeded" )
+ RegisterSignal( "ReviveFailed" )
+ RegisterSignal( "ForceBleedOut" )
+
+ AddCallback_OnClientDisconnected( ReviveOnClientDisconnect )
+}
+
+void function PlayerRevivesOrBleedsOut( entity player )
+{
+ player.EndSignal( "OnDestroy" )
+ player.EndSignal( "ForceBleedOut" )
+ svGlobal.levelEnt.EndSignal( "RoundEnd" )
+
+ table e = { revived = false }
+ //thread PlayerReviveVONag( player, 0.5 )
+
+ OnThreadEnd(
+ function() : ( player, e )
+ {
+ if ( !IsValid( player ) )
+ return
+
+ player.Signal( "KillReviveNag" )
+ player.Signal( "DoneBleedingOut" )
+ player.nv.reviveBleedingOut = -1.0 //-1 means off
+
+ if ( e.revived )
+ {
+ player.Signal( "ReviveSucceeded")
+ thread PlayerStandsBackUp( player )
+ }
+ else
+ {
+ file.fakePlayers[ player ].Destroy()
+ player.Signal( "ReviveFailed" )
+ DecideRespawnPlayer( player )
+ }
+ }
+ )
+
+ wait( REVIVE_DEATH_TIME )
+ player.StartObserverMode( OBS_MODE_DEATHCAM )
+
+ ForceRespawnIfEntireTeamIsDead( player )
+
+ float endTime = Time() + REVIVE_BLEED_OUT_TIME
+ player.nv.reviveBleedingOut = endTime
+
+ bool reviving = false
+ float doneReviveTime = Time() + 100
+
+ float distOuterSqr = pow( REVIVE_DIST_OUTER, 2 )
+ float distInnerSqr = pow( REVIVE_DIST_INNER, 2 )
+
+ while ( true )
+ {
+ array<entity> healers = Revive_GetAvailablePlayerHealers( player )
+
+ //we were reviving but aren't anymore - set revive to false.
+ if ( reviving && !FriendlyIsReviving( healers, player, distOuterSqr ) )
+ {
+ //thread PlayerReviveVONag( player )
+ reviving = false
+ player.nv.reviveHealedTime = -1.0 //-1 means off
+ }
+ //we were not reviving but now we are? set the new revive done time.
+ else if ( !reviving && FriendlyIsReviving( healers, player, distInnerSqr ) )
+ {
+ player.Signal( "KillReviveNag" )
+ doneReviveTime = Time() + REVIVE_TIME_TO_REVIVE
+ player.nv.reviveHealedTime = doneReviveTime
+ reviving = true
+ }
+
+ //are we done reviving? then set the value and return
+ if ( reviving && Time() > doneReviveTime )
+ {
+ e.revived = true
+ return
+ }
+
+ //we didn't make it
+ if ( !reviving && Time() > endTime )
+ return
+
+ wait 0.2
+ }
+}
+
+void function ForceRespawnIfEntireTeamIsDead( entity player )
+{
+ int playerTeam = player.GetTeam()
+ array<entity> victimTeamMembers = GetPlayerArrayOfTeam( playerTeam )
+ foreach ( member in victimTeamMembers )
+ {
+ if ( member.p.isReviving || IsAlive( member ) )
+ return
+ }
+ foreach ( member in victimTeamMembers )
+ {
+ if ( player != member && member.p.isReviving == false )
+ member.Signal( "ForceBleedOut" )
+ }
+ MessageToTeam( GetOtherTeam( playerTeam ), eEventNotifications.EnemyTeamEliminated )
+ player.Signal( "ForceBleedOut" )
+}
+
+void function PlayerReviveVONag( entity player, float delay = 0.5 )
+{
+ player.EndSignal( "OnDestroy" )
+ player.EndSignal( "KillReviveNag" )
+
+ OnThreadEnd(
+ function() : ( player )
+ {
+ if ( IsValid( player ) )
+ StopSoundOnEntity( player, "diag_coop_bleedout_help" )
+ }
+ )
+
+ if ( delay > 0 )
+ wait delay
+
+ while ( true )
+ {
+ float time = EmitSoundOnEntity( player, "diag_coop_bleedout_help" )
+ wait time
+
+ wait RandomFloatRange( 10, 15 )
+ }
+}
+
+bool function FriendlyIsReviving( array<entity> healers, entity player, float distSqr )
+{
+ vector origin = player.GetOrigin()
+
+ foreach ( friend in healers )
+ {
+ if ( !IsAlive( friend ) )
+ continue
+
+ if ( DistanceSqr( friend.GetOrigin(), origin ) < distSqr )
+ return true
+ }
+
+ return false
+}
+
+array<entity> function Revive_GetAvailablePlayerHealers( entity player )
+{
+ int team = player.GetTeam()
+ array<entity> players = GetPlayerArrayOfTeam( team )
+ array<entity> playersCanRevive = []
+ foreach ( player in players )
+ {
+ if ( !IsAlive( player ) )
+ continue
+
+ playersCanRevive.append( player )
+ }
+
+ return playersCanRevive
+}
+
+bool function ShouldRevivePlayer( entity player, var damageInfo )
+{
+ if ( !ReviveEnabled() )
+ return false
+
+ if ( !GamePlaying() )
+ return false
+
+ if ( player.ContextAction_IsMeleeExecution() )
+ return false
+
+ if ( player.IsTitan() )
+ return false
+
+ int source = DamageInfo_GetDamageSourceIdentifier( damageInfo )
+
+ if ( source == eDamageSourceId.fall ||
+ source == eDamageSourceId.submerged ||
+ source == eDamageSourceId.outOfBounds ||
+ source == eDamageSourceId.indoor_inferno )
+ return false
+
+ return true
+}
+
+entity function SpawnFakePlayer( entity player, int team, vector origin, vector angles, asset weaponModel, asset model )
+{
+ float fadeDist = 10000.0
+ int solidType = 0// 0 = no collision, 2 = bounding box, 6 = use vPhysics, 8 = hitboxes only
+
+ entity fakePlayer = CreatePropDynamic( model, origin, angles, solidType, fadeDist )
+ if ( !( player in file.fakePlayers ) )
+ {
+ file.fakePlayers[ player ] <- null
+ }
+ file.fakePlayers[ player ] = fakePlayer
+
+ thread FakePlayerTrack( fakePlayer, player )
+
+ if ( weaponModel != $"" )
+ {
+ entity gun = CreatePropDynamic( weaponModel, origin, angles, 0, fadeDist )
+ gun.SetParent( fakePlayer, "PROPGUN" )
+ }
+
+ return fakePlayer
+}
+
+void function FakePlayerTrack( entity fakePlayer, entity player )
+{
+ fakePlayer.EndSignal( "OnDestroy" )
+ player.EndSignal( "OnDestroy" )
+ vector lastPlayerOrg = Vector( 0.0, 0.0, 0.0 )
+
+ while ( true )
+ {
+ if ( player.GetOrigin() == lastPlayerOrg )
+ player.SetVelocity( Vector( 0.0, 0.0, 0.0 ) )
+ lastPlayerOrg = player.GetOrigin()
+
+ fakePlayer.SetOrigin( player.GetOrigin() )
+ WaitFrame()
+ }
+}
+
+void function DeathPackage_PlayerRevive( entity player )
+{
+ player.kv.VisibilityFlags = ENTITY_VISIBLE_TO_NOBODY
+
+ vector deathOrg = player.GetOrigin()
+
+ vector mins = Vector( -16.0, -16.0, 0.0 )
+ vector maxs = Vector( 16.0, 16.0, 72.0 )
+ TraceResults result = TraceHull( deathOrg + Vector( 0.0, 0.0, 8.0 ), deathOrg + Vector( 0.0, 0.0, -16000.0 ), mins, maxs, player, ( TRACE_MASK_PLAYERSOLID_BRUSHONLY | TRACE_MASK_BLOCKLOS ), TRACE_COLLISION_GROUP_NONE )
+
+ player.SetVelocity( Vector( 0.0, 0.0, 0.0 ) )
+ thread ReviveLerpToOrigin( player, deathOrg, result.endPos )
+
+ entity activeWeapon = player.GetActiveWeapon()
+
+ asset weaponModel = activeWeapon == null ? $"" : activeWeapon.GetModelName()
+
+ entity fakePlayer = SpawnFakePlayer( player, player.GetTeam(), deathOrg, player.GetAngles(), weaponModel, player.GetModelName() )
+ fakePlayer.Anim_Play( "pt_wounded_drag_zinger_A_idle" )
+ player.Anim_Play( "pt_wounded_drag_zinger_A_idle" )
+}
+
+void function ReviveLerpToOrigin( entity player, vector deathOrg, vector endPos )
+{
+ player.EndSignal( "DoneBleedingOut" )
+ player.EndSignal( "OnDestroy" )
+
+ entity mover = CreateScriptMover()
+
+ OnThreadEnd(
+ function() : ( player, mover )
+ {
+ if ( IsValid( player ) )
+ player.ClearParent()
+
+ if ( IsValid( mover ) )
+ mover.Destroy()
+ }
+ )
+
+ mover.SetOrigin( deathOrg )
+ player.SetOrigin( deathOrg )
+ player.SetParent( mover )
+
+ float moveTime = GraphCapped( deathOrg.z - endPos.z, 0.0, 768.0, 0.1, 2.0 )
+
+ mover.NonPhysicsMoveTo( endPos, moveTime, moveTime, 0.0 )
+ wait( moveTime )
+ player.ClearParent()
+
+ while ( true )
+ {
+ player.SetOrigin( endPos )
+ WaitFrame()
+ }
+}
+
+void function PlayerStandsBackUp( entity player )
+{
+ player.EndSignal( "OnDestroy" )
+ svGlobal.levelEnt.EndSignal( "RoundEnd" )
+
+ entity fakePlayer = expect entity( file.fakePlayers[ player ] )
+ file.fakePlayers[ player ] = null
+
+ player.p.isReviving = true
+
+ OnThreadEnd(
+ function() : ( player, fakePlayer )
+ {
+ if ( IsValid( fakePlayer ) )
+ fakePlayer.Destroy()
+
+ if ( IsValid( player ) )
+ player.p.isReviving = false
+ }
+ )
+
+ fakePlayer.Anim_Play( "CQB_knockback_pain_react" )
+ fakePlayer.Anim_SetInitialTime( 2.0 )
+ wait( 1.5 )
+
+ var settings = player.GetPlayerSettings()
+ player.SetPlayerSettings( "spectator" )
+ player.SetPlayerSettings( settings )
+ player.RespawnPlayer( null )
+}
+
+void function ReviveOnClientDisconnect( entity player )
+{
+ if ( player in file.fakePlayers )
+ {
+ if ( IsValid( file.fakePlayers[ player ] ) )
+ file.fakePlayers[ player ].Destroy()
+ delete file.fakePlayers[ player ]
+ }
+} \ No newline at end of file