//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: CBaseTFPlayer functions dealing with death and reinforcement // // $Workfile: $ // $Date: $ // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "player.h" #include "tf_player.h" #include "gamerules.h" #include "basecombatweapon.h" #include "EntityList.h" #include "tf_shareddefs.h" #include "tf_team.h" #include "baseviewmodel.h" #include "tf_class_infiltrator.h" #include "in_buttons.h" #include "globals.h" int g_iNumberOfCorpses; //----------------------------------------------------------------------------- // Purpose: The player was just killed //----------------------------------------------------------------------------- void CBaseTFPlayer::Event_Killed( const CTakeDamageInfo &info ) { // TODO don't use temp entities to transmit messages CPASFilter filter( GetLocalOrigin() ); te->KillPlayerAttachments( filter, 0.0, entindex() ); // Remove the player from any vehicle they're in if ( IsInAVehicle() ) { LeaveVehicle(); } // Holster weapon immediately, to allow it to cleanup if (GetActiveWeapon()) { GetActiveWeapon()->Holster( ); } // Stop attaching sappers if ( IsAttachingSapper() ) { StopAttaching(); } // stop them touching anything AddFlag( FL_DONTTOUCH ); g_pGameRules->PlayerKilled( this, info ); ClearUseEntity(); // If I'm ragdolling due to a knockdown, don't play any animations if ( m_hRagdollShadow == NULL ) { if ( PlayerClass() != TFCLASS_INFILTRATOR ) { // Calculate death force Vector forceVector = CalcDamageForceVector( info ); BecomeRagdollOnClient( forceVector ); } else { SetAnimation( PLAYER_DIE ); } } DeathSound( info ); SetViewOffset( VEC_DEAD_VIEWHEIGHT_SCALED( this ) ); m_lifeState = LIFE_DYING; pl.deadflag = true; // Enter dying state AddSolidFlags( FSOLID_NOT_SOLID ); SetMoveType( MOVETYPE_NONE ); QAngle angles = GetLocalAngles(); angles.x = angles.z = 0; SetLocalAngles( angles ); m_takedamage = DAMAGE_NO; // clear out the suit message cache so we don't keep chattering SetSuitUpdate(NULL, false, 0); // reset FOV SetFOV( this, 0 ); // Setup for respawn m_flTimeOfDeath = gpGlobals->curtime; SetThink(TFPlayerDeathThink); SetNextThink( gpGlobals->curtime + 0.1f ); SetPowerup(POWERUP_EMP,false); // Tell the playerclass that the player died if ( GetPlayerClass() ) { GetPlayerClass()->PlayerDied( info.GetAttacker() ); } // Tell the attacker's playerclass that he killed someone if ( info.GetAttacker() && info.GetAttacker()->IsPlayer() ) { CBaseTFPlayer *pPlayerAttacker = (CBaseTFPlayer*)info.GetAttacker(); pPlayerAttacker->KilledPlayer( this ); } DropAllResourceChunks(); // Tell all teams to update their orders COrderEvent_PlayerKilled order( this ); GlobalOrderEvent( &order ); } //----------------------------------------------------------------------------- // Purpose: Think function for dead/dying players. // Play their death animation, then set up for reinforcement. //----------------------------------------------------------------------------- void CBaseTFPlayer::TFPlayerDeathThink(void) { float flForward; SetNextThink( gpGlobals->curtime + 0.1f ); if ( GetFlags() & FL_ONGROUND ) { flForward = GetAbsVelocity().Length() - 20; if (flForward <= 0) { SetAbsVelocity( vec3_origin ); } else { Vector vecNewVelocity = GetAbsVelocity(); VectorNormalize( vecNewVelocity ); vecNewVelocity *= flForward; SetAbsVelocity( vecNewVelocity ); } } StudioFrameAdvance(); if (GetModelIndex() && (!IsSequenceFinished()) && (m_lifeState == LIFE_DYING)) { m_iRespawnFrames++; if ( m_iRespawnFrames < 60 ) // animations should be no longer than this return; } // Start looping dying state SetAnimation( PLAYER_DIE ); // ROBIN: Everyone respawns immediately now. Maps will define respawns in the future. if ( (gpGlobals->curtime - m_flTimeOfDeath) < 3 ) return; m_lifeState = LIFE_RESPAWNABLE; // Respawn on button press, but not if they're checking the scores // Also respawn if they're not looking at scores, and they've been dead for over 5 seconds bool bButtonDown = (m_nButtons & ~IN_SCORE) > 0; if ( (bButtonDown || (gpGlobals->curtime - m_flTimeOfDeath) > 5 ) ) { PlayerRespawn(); } /* // Aliens become respawnable immediately, because they're waiting for a reinforcement wave. // Humans have to wait a short time. if ( (GetTeamNumber() == TEAM_HUMANS) && (gpGlobals->curtime - m_flTimeOfDeath) < 3 ) return; if ( (GetTeamNumber() == TEAM_ALIENS) && (gpGlobals->curtime - m_flTimeOfDeath) < 1 ) return; // Humans can respawn, Aliens can't (reinforcement wave for their kind) // Aliens stop thinking now and wait for the reinforcement wave if ( GetTeamNumber() == TEAM_ALIENS ) { m_lifeState = LIFE_RESPAWNABLE; SetThink( NULL ); } else { m_lifeState = LIFE_RESPAWNABLE; // Respawn on button press if ( m_nButtons & ~IN_SCORE ) { PlayerRespawn(); } } */ } //----------------------------------------------------------------------------- // Purpose: Return true if this player is ready to reinforce //----------------------------------------------------------------------------- bool CBaseTFPlayer::IsReadyToReinforce( void ) { // Only Aliens reinforce in waves, humans respawn normally if ( (GetTeamNumber() == TEAM_ALIENS) && (m_lifeState == LIFE_RESPAWNABLE) ) return true; return false; } //----------------------------------------------------------------------------- // Purpose: Bring the player back to life in a reinforcement wave //----------------------------------------------------------------------------- void CBaseTFPlayer::Reinforce( void ) { // Tell all teams to update their orders COrderEvent_PlayerRespawned order( this ); GlobalOrderEvent( &order ); StopAnimation(); IncrementInterpolationFrame(); m_flPlaybackRate = 0.0; PlayerRespawn(); }