Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

461 lines
13 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Revive
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================
  7. #include "cbase.h"
  8. #include "tf_revive.h"
  9. #include "tf_gamerules.h"
  10. #ifdef CLIENT_DLL
  11. #include "tf_hud_target_id.h"
  12. #include "view.h"
  13. #include "tf_hud_mediccallers.h"
  14. #else
  15. #include "tf_gamestats.h"
  16. #include "particle_parse.h"
  17. #include "world.h"
  18. #include "collisionutils.h"
  19. #include "triggers.h"
  20. #endif // CLIENT_DLL
  21. // memdbgon must be the last include file in a .cpp file!!!
  22. #include "tier0/memdbgon.h"
  23. #define MARKER_MODEL "models/props_mvm/mvm_revive_tombstone.mdl"
  24. static const int REVIVE_EASY_LIMIT = 4;
  25. static const int REVIVE_MEDIUM_LIMIT = 8;
  26. #ifdef GAME_DLL
  27. #ifdef STAGING_ONLY
  28. CON_COMMAND_F ( tf_test_revive_spawnmarker, "Crude way to spawn a marker for testing", FCVAR_CHEAT )
  29. {
  30. CBasePlayer *pLocalPlayer = UTIL_PlayerByIndex( 1 );
  31. if ( !pLocalPlayer )
  32. return;
  33. CTFPlayer *pPlayer = ToTFPlayer( pLocalPlayer );
  34. if ( !pPlayer )
  35. return;
  36. CTFReviveMarker::Create( pPlayer );
  37. }
  38. #endif // STAGING_ONLY
  39. extern void HandleRageGain( CTFPlayer *pPlayer, unsigned int iRequiredBuffFlags, float flDamage, float fInverseRageGainScale );
  40. #else
  41. extern void AddMedicCaller( C_BaseEntity *pEntity, float flDuration, Vector &vecOffset, bool bAutoCaller = false );
  42. #endif // GAME_DLL
  43. //-----------------------------------------------------------------------------
  44. //
  45. //-----------------------------------------------------------------------------
  46. IMPLEMENT_NETWORKCLASS_ALIASED( TFReviveMarker, DT_TFReviveMarker )
  47. BEGIN_NETWORK_TABLE( CTFReviveMarker, DT_TFReviveMarker )
  48. #ifdef GAME_DLL
  49. SendPropEHandle( SENDINFO( m_hOwner ) ),
  50. SendPropInt( SENDINFO( m_iHealth ), -1, SPROP_VARINT | SPROP_CHANGES_OFTEN ),
  51. SendPropInt( SENDINFO( m_iMaxHealth ), -1, SPROP_VARINT ),
  52. SendPropInt( SENDINFO( m_nRevives ), -1, SPROP_VARINT | SPROP_UNSIGNED ),
  53. #else
  54. RecvPropEHandle( RECVINFO( m_hOwner ) ),
  55. RecvPropInt( RECVINFO( m_iHealth ) ),
  56. RecvPropInt( RECVINFO( m_iMaxHealth ) ),
  57. RecvPropInt( RECVINFO( m_nRevives ) ),
  58. #endif
  59. END_NETWORK_TABLE()
  60. LINK_ENTITY_TO_CLASS( entity_revive_marker, CTFReviveMarker );
  61. PRECACHE_REGISTER( entity_revive_marker );
  62. BEGIN_DATADESC( CTFReviveMarker )
  63. #ifdef GAME_DLL
  64. DEFINE_THINKFUNC( ReviveThink ),
  65. #endif // GAME_DLL
  66. END_DATADESC()
  67. //-----------------------------------------------------------------------------
  68. //
  69. //-----------------------------------------------------------------------------
  70. CTFReviveMarker::CTFReviveMarker()
  71. {
  72. #ifdef GAME_DLL
  73. m_flHealAccumulator = 0.f;
  74. m_flLastHealTime = 0.f;
  75. m_bOwnerPromptedToRevive = false;
  76. m_bOnGround = false;
  77. #else
  78. m_iMaxHealth = 1;
  79. m_bCalledForMedic = false;
  80. #endif // GAME_DLL
  81. m_nRevives = 0;
  82. UseClientSideAnimation();
  83. }
  84. //-----------------------------------------------------------------------------
  85. // Purpose:
  86. //-----------------------------------------------------------------------------
  87. void CTFReviveMarker::Precache()
  88. {
  89. BaseClass::Precache();
  90. PrecacheModel( MARKER_MODEL );
  91. PrecacheScriptSound( "MVM.PlayerRevived" );
  92. PrecacheParticleSystem( "speech_revivecall" );
  93. PrecacheParticleSystem( "speech_revivecall_medium" );
  94. PrecacheParticleSystem( "speech_revivecall_hard" );
  95. }
  96. //-----------------------------------------------------------------------------
  97. // Purpose:
  98. //-----------------------------------------------------------------------------
  99. void CTFReviveMarker::Spawn( void )
  100. {
  101. Precache();
  102. BaseClass::Spawn();
  103. SetHealth( 1 );
  104. SetModel( MARKER_MODEL );
  105. SetSolid( SOLID_BBOX );
  106. SetSolidFlags( FSOLID_TRIGGER );
  107. SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  108. SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
  109. // SetCollisionBounds( VEC_HULL_MIN, VEC_HULL_MAX );
  110. SetBlocksLOS( false );
  111. AddEffects( EF_NOSHADOW );
  112. ResetSequence( LookupSequence( "idle" ) );
  113. #ifdef GAME_DLL
  114. m_takedamage = DAMAGE_NO;
  115. SetThink( &CTFReviveMarker::ReviveThink );
  116. SetNextThink( gpGlobals->curtime );
  117. #endif // GAME_DLL
  118. }
  119. //-----------------------------------------------------------------------------
  120. // Purpose:
  121. // Input : collisionGroup -
  122. // Output : Returns true on success, false on failure.
  123. //-----------------------------------------------------------------------------
  124. bool CTFReviveMarker::ShouldCollide( int collisionGroup, int contentsMask ) const
  125. {
  126. if ( collisionGroup == COLLISION_GROUP_PLAYER_MOVEMENT )
  127. return false;
  128. if ( collisionGroup == COLLISION_GROUP_PROJECTILE )
  129. return false;
  130. if ( collisionGroup == TFCOLLISION_GROUP_ROCKETS )
  131. return false;
  132. return BaseClass::ShouldCollide( collisionGroup, contentsMask );
  133. }
  134. #ifdef CLIENT_DLL
  135. //-----------------------------------------------------------------------------
  136. // Purpose:
  137. //-----------------------------------------------------------------------------
  138. void CTFReviveMarker::OnDataChanged( DataUpdateType_t updateType )
  139. {
  140. // Call for medic once the server's set maxhealth
  141. if ( !m_bCalledForMedic && m_iMaxHealth > 1 )
  142. {
  143. MedicCallerType nType = CALLER_TYPE_REVIVE_EASY;
  144. if ( m_nRevives >= REVIVE_EASY_LIMIT && m_nRevives < REVIVE_MEDIUM_LIMIT )
  145. {
  146. nType = CALLER_TYPE_REVIVE_MEDIUM;
  147. }
  148. else if ( m_nRevives >= REVIVE_MEDIUM_LIMIT )
  149. {
  150. nType = CALLER_TYPE_REVIVE_HARD;
  151. }
  152. Vector vecPos;
  153. if ( GetAttachmentLocal( LookupAttachment( "mediccall" ), vecPos ) )
  154. {
  155. CTFMedicCallerPanel::AddMedicCaller( this, 5.0, vecPos, nType );
  156. }
  157. m_bCalledForMedic = true;
  158. }
  159. BaseClass::OnDataChanged( updateType );
  160. }
  161. #endif // CLIENT_DLL
  162. #ifdef GAME_DLL
  163. //-----------------------------------------------------------------------------
  164. // Purpose:
  165. //-----------------------------------------------------------------------------
  166. CTFReviveMarker *CTFReviveMarker::Create( CTFPlayer *pOwner )
  167. {
  168. if ( pOwner )
  169. {
  170. CTFReviveMarker *pMarker = static_cast< CTFReviveMarker* >( CBaseEntity::Create( "entity_revive_marker", pOwner->GetAbsOrigin() + Vector( 0, 0, 50 ), pOwner->GetAbsAngles() ) );
  171. if ( pMarker )
  172. {
  173. pMarker->SetOwner( pOwner );
  174. pMarker->ChangeTeam( pOwner->GetTeamNumber() );
  175. return pMarker;
  176. }
  177. }
  178. return NULL;
  179. }
  180. //-----------------------------------------------------------------------------
  181. //
  182. //-----------------------------------------------------------------------------
  183. int CTFReviveMarker::UpdateTransmitState( void )
  184. {
  185. return SetTransmitState( FL_EDICT_FULLCHECK );
  186. }
  187. //-----------------------------------------------------------------------------
  188. //
  189. //-----------------------------------------------------------------------------
  190. int CTFReviveMarker::ShouldTransmit( const CCheckTransmitInfo *pInfo )
  191. {
  192. return FL_EDICT_ALWAYS;
  193. }
  194. //-----------------------------------------------------------------------------
  195. // Purpose:
  196. //-----------------------------------------------------------------------------
  197. void CTFReviveMarker::ReviveThink( void )
  198. {
  199. if ( !m_hOwner || !InSameTeam( m_hOwner ) )
  200. {
  201. UTIL_Remove( this );
  202. return;
  203. }
  204. if ( !GetMaxHealth() )
  205. {
  206. // Set health of marker based on class, and number of previous revives
  207. float flHealth = m_hOwner->GetMaxHealth() / 2;
  208. Assert( flHealth > 0.f );
  209. PlayerStats_t *pPlayerStats = CTF_GameStats.FindPlayerStats( m_hOwner );
  210. if ( pPlayerStats )
  211. {
  212. m_nRevives.Set( pPlayerStats->statsCurrentRound.m_iStat[TFSTAT_REVIVED] );
  213. flHealth += ( (float)m_nRevives * 10.f );
  214. }
  215. SetMaxHealth( flHealth );
  216. }
  217. // At rest?
  218. if ( !m_bOnGround && ( GetFlags() & FL_ONGROUND ) )
  219. {
  220. SetMoveType( MOVETYPE_NONE );
  221. m_bOnGround = true;
  222. // See if we've in a trigger_hurt
  223. for ( int i = 0; i < ITriggerHurtAutoList::AutoList().Count(); i++ )
  224. {
  225. CTriggerHurt *pTrigger = static_cast<CTriggerHurt*>( ITriggerHurtAutoList::AutoList()[i] );
  226. if ( !pTrigger->m_bDisabled )
  227. {
  228. Vector vecMins, vecMaxs;
  229. pTrigger->GetCollideable()->WorldSpaceSurroundingBounds( &vecMins, &vecMaxs );
  230. if ( IsPointInBox( GetCollideable()->GetCollisionOrigin(), vecMins, vecMaxs ) )
  231. {
  232. UTIL_Remove( this );
  233. return;
  234. }
  235. }
  236. }
  237. // Different particle based on difficulty of this revive
  238. const char *pszParticle = NULL;
  239. if ( m_nRevives < REVIVE_EASY_LIMIT )
  240. {
  241. pszParticle = "speech_revivecall";
  242. }
  243. else if ( m_nRevives < REVIVE_MEDIUM_LIMIT )
  244. {
  245. pszParticle = "speech_revivecall_medium";
  246. }
  247. else
  248. {
  249. pszParticle = "speech_revivecall_hard";
  250. }
  251. // DispatchParticleEffect( pszParticle, GetAbsOrigin() + Vector( 0, 0, 80 ), vec3_angle );
  252. DispatchParticleEffect( pszParticle, PATTACH_POINT_FOLLOW, this, "mediccall" );
  253. EmitSound( "Medic.AutoCallerAnnounce" );
  254. }
  255. // Close revive prompt if no longer being revived
  256. if ( HasOwnerBeenPrompted() && !IsReviveInProgress() )
  257. {
  258. IGameEvent *event = gameeventmanager->CreateEvent( "revive_player_stopped" );
  259. if ( event )
  260. {
  261. event->SetInt( "entindex", m_hOwner->entindex() );
  262. gameeventmanager->FireEvent( event );
  263. SetOwnerHasBeenPrompted( false );
  264. }
  265. }
  266. SetNextThink( gpGlobals->curtime + 0.1f );
  267. }
  268. #endif // GAME_DLL
  269. //-----------------------------------------------------------------------------
  270. //
  271. //-----------------------------------------------------------------------------
  272. void CTFReviveMarker::SetOwner( CTFPlayer *pPlayer )
  273. {
  274. #ifdef GAME_DLL
  275. if ( !pPlayer )
  276. return;
  277. m_hOwner = pPlayer;
  278. ChangeTeam( m_hOwner->GetTeamNumber() );
  279. // Determine bodygroup based on class
  280. SetBodygroup( 1, m_hOwner->GetPlayerClass()->GetClassIndex() - 1 );
  281. SetAbsAngles( m_hOwner->GetAbsAngles() );
  282. #endif // GAME_DLL
  283. }
  284. #ifdef GAME_DLL
  285. //-----------------------------------------------------------------------------
  286. //
  287. //-----------------------------------------------------------------------------
  288. void CTFReviveMarker::AddMarkerHealth( float flAmount )
  289. {
  290. CTFPlayer *pReviver = GetReviver();
  291. if ( !pReviver )
  292. return;
  293. CTFPlayer *pOwner = GetOwner();
  294. if ( !pOwner )
  295. return;
  296. if ( !GetMaxHealth() )
  297. return;
  298. HandleRageGain( pReviver, kRageBuffFlag_OnHeal, flAmount * 2, 1.f );
  299. m_flHealAccumulator += flAmount;
  300. if ( m_flHealAccumulator >= 1.f )
  301. {
  302. float flHealthToAdd = floor( m_flHealAccumulator );
  303. m_flHealAccumulator -= flHealthToAdd;
  304. m_iHealth += flHealthToAdd;
  305. m_flLastHealTime = gpGlobals->curtime;
  306. }
  307. if ( m_iHealth >= GetMaxHealth() )
  308. {
  309. ReviveOwner();
  310. // Give points
  311. CTF_GameStats.Event_PlayerAwardBonusPoints( pReviver, pOwner, 50 );
  312. }
  313. }
  314. //-----------------------------------------------------------------------------
  315. //
  316. //-----------------------------------------------------------------------------
  317. bool CTFReviveMarker::IsReviveInProgress( void )
  318. {
  319. float flTimeSinceHeal = gpGlobals->curtime - m_flLastHealTime;
  320. return ( m_flLastHealTime && flTimeSinceHeal <= 2.f );
  321. }
  322. //-----------------------------------------------------------------------------
  323. // Returns true if the player was spawned at their marker
  324. //-----------------------------------------------------------------------------
  325. bool CTFReviveMarker::ReviveOwner( void )
  326. {
  327. if ( !m_hOwner )
  328. return false;
  329. m_hOwner->ForceRespawn();
  330. // Increment stat
  331. CTF_GameStats.Event_PlayerRevived( m_hOwner );
  332. // If the medic's gone, or dead, stay in the spawn room
  333. if ( !m_pReviver || !m_pReviver->IsAlive() )
  334. return false;
  335. // See if their marker is clear
  336. Vector vecTeleportPos = GetAbsOrigin();
  337. trace_t tr;
  338. CTraceFilterIgnoreTeammatesAndTeamObjects filter( m_hOwner, COLLISION_GROUP_NONE, m_hOwner->GetTeamNumber() );
  339. UTIL_TraceHull( vecTeleportPos, vecTeleportPos, VEC_HULL_MIN_SCALED( m_hOwner ), VEC_HULL_MAX_SCALED( m_hOwner ), ( MASK_SOLID | CONTENTS_PLAYERCLIP ), &filter, &tr );
  340. // If not, try the medic's location
  341. if ( tr.fraction < 1.f )
  342. {
  343. if ( !m_pReviver )
  344. // They'll appear in their spawn room.
  345. return false;
  346. vecTeleportPos = m_pReviver->GetAbsOrigin();
  347. }
  348. else
  349. {
  350. // Use the angles that were stored when the marker was spawned
  351. m_hOwner->SetAbsAngles( GetAbsAngles() );
  352. }
  353. // Magic
  354. color32 fadeColor = { 50, 50, 50, 200 };
  355. UTIL_ScreenFade( m_hOwner, fadeColor, 0.5, 0.4, FFADE_IN );
  356. m_hOwner->Teleport( &vecTeleportPos, &m_hOwner->GetAbsAngles(), &vec3_origin );
  357. m_hOwner->EmitSound( "MVM.PlayerRevived" );
  358. if ( m_pReviver )
  359. {
  360. IGameEvent *event = gameeventmanager->CreateEvent( "revive_player_complete" );
  361. if ( event )
  362. {
  363. event->SetInt( "entindex", m_pReviver->entindex() );
  364. gameeventmanager->FireEvent( event );
  365. }
  366. }
  367. m_hOwner->SpeakConceptIfAllowed( MP_CONCEPT_RESURRECTED );
  368. return true;
  369. }
  370. //-----------------------------------------------------------------------------
  371. //
  372. //-----------------------------------------------------------------------------
  373. void CTFReviveMarker::PromptOwner( void )
  374. {
  375. if ( !m_hOwner )
  376. {
  377. UTIL_Remove( this );
  378. return;
  379. }
  380. if ( HasOwnerBeenPrompted() )
  381. return;
  382. IGameEvent *event = gameeventmanager->CreateEvent( "revive_player_notify" );
  383. if ( event )
  384. {
  385. event->SetInt( "entindex", m_hOwner->entindex() );
  386. event->SetInt( "marker_entindex", entindex() );
  387. gameeventmanager->FireEvent( event );
  388. SetOwnerHasBeenPrompted( true );
  389. }
  390. }
  391. #endif // GAME_DLL