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.

2780 lines
75 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "cbase.h"
  7. #include "c_dod_player.h"
  8. #include "c_user_message_register.h"
  9. #include "view.h"
  10. #include "iclientvehicle.h"
  11. #include "ivieweffects.h"
  12. #include "input.h"
  13. #include "IEffects.h"
  14. #include "fx.h"
  15. #include "c_basetempentity.h"
  16. #include "hud_macros.h"
  17. #include "engine/ivdebugoverlay.h"
  18. #include "smoke_fog_overlay.h"
  19. #include "bone_setup.h"
  20. #include "in_buttons.h"
  21. #include "r_efx.h"
  22. #include "dlight.h"
  23. #include "shake.h"
  24. #include "cl_animevent.h"
  25. #include "fx_dod_blood.h"
  26. #include "effect_dispatch_data.h" //for water ripple / splash effect
  27. #include "c_te_effect_dispatch.h" //ditto
  28. #include "dod_gamerules.h"
  29. #include <igameevents.h>
  30. #include "physpropclientside.h"
  31. #include "obstacle_pushaway.h"
  32. #include "prediction.h"
  33. #include "viewangleanim.h"
  34. #include "soundenvelope.h"
  35. #include "weapon_dodbipodgun.h"
  36. #include "c_dod_basegrenade.h"
  37. #include "dod_weapon_parse.h"
  38. #include "view_scene.h"
  39. #include "dod_headiconmanager.h"
  40. #include "c_world.h"
  41. #include "c_dod_bombtarget.h"
  42. #include "toolframework/itoolframework.h"
  43. #include "toolframework_client.h"
  44. #include "c_team.h"
  45. #include "collisionutils.h"
  46. #include "weapon_dodsniper.h"
  47. // NVNT - haptics system for spectating and grenades
  48. #include "haptics/haptic_utils.h"
  49. // NVNT - for grenade effects
  50. #include "weapon_dodbasegrenade.h"
  51. // NVNT - for planting bomb effect
  52. #include "weapon_dodbasebomb.h"
  53. #if defined( CDODPlayer )
  54. #undef CDODPlayer
  55. #endif
  56. #include "iviewrender_beams.h" // flashlight beam
  57. #include "materialsystem/imesh.h" //for materials->FindMaterial
  58. #include "iviewrender.h" //for view->
  59. ConVar cl_ragdoll_physics_enable( "cl_ragdoll_physics_enable", "1", 0, "Enable/disable ragdoll physics." );
  60. ConVar cl_autoreload( "cl_autoreload", "1", FCVAR_USERINFO | FCVAR_ARCHIVE, "Set to 1 to auto reload your weapon when it is empty" );
  61. ConVar cl_autorezoom( "cl_autorezoom", "1", FCVAR_USERINFO | FCVAR_ARCHIVE, "When set to 1, sniper rifles and bazooka weapons will automatically raise after each shot" );
  62. #include "tier0/memdbgon.h"
  63. //======================================================
  64. //
  65. // Cold Breath Emitter - for DOD players.
  66. //
  67. class ColdBreathEmitter : public CSimpleEmitter
  68. {
  69. public:
  70. ColdBreathEmitter( const char *pDebugName ) : CSimpleEmitter( pDebugName ) {}
  71. static ColdBreathEmitter *Create( const char *pDebugName )
  72. {
  73. return new ColdBreathEmitter( pDebugName );
  74. }
  75. void UpdateVelocity( SimpleParticle *pParticle, float timeDelta )
  76. {
  77. // Float up when lifetime is half gone.
  78. pParticle->m_vecVelocity[2] -= ( 8.0f * timeDelta );
  79. // FIXME: optimize this....
  80. pParticle->m_vecVelocity *= ExponentialDecay( 0.9, 0.03, timeDelta );
  81. }
  82. virtual float UpdateRoll( SimpleParticle *pParticle, float timeDelta )
  83. {
  84. pParticle->m_flRoll += pParticle->m_flRollDelta * timeDelta;
  85. pParticle->m_flRollDelta += pParticle->m_flRollDelta * ( timeDelta * -2.0f );
  86. //Cap the minimum roll
  87. if ( fabs( pParticle->m_flRollDelta ) < 0.5f )
  88. {
  89. pParticle->m_flRollDelta = ( pParticle->m_flRollDelta > 0.0f ) ? 0.5f : -0.5f;
  90. }
  91. return pParticle->m_flRoll;
  92. }
  93. private:
  94. ColdBreathEmitter( const ColdBreathEmitter & );
  95. };
  96. void RecvProxy_StunTime( const CRecvProxyData *pData, void *pStruct, void *pOut )
  97. {
  98. C_DODPlayer *pPlayerData = (C_DODPlayer *) pStruct;
  99. if( pPlayerData != C_BasePlayer::GetLocalPlayer() )
  100. return;
  101. if ( (pPlayerData->m_flStunDuration != pData->m_Value.m_Float) && pData->m_Value.m_Float > 0 )
  102. {
  103. pPlayerData->m_flStunAlpha = 1;
  104. }
  105. pPlayerData->m_flStunDuration = pData->m_Value.m_Float;
  106. pPlayerData->m_flStunEffectTime = gpGlobals->curtime + pPlayerData->m_flStunDuration;
  107. }
  108. // -------------------------------------------------------------------------------- //
  109. // Player animation event. Sent to the client when a player fires, jumps, reloads, etc..
  110. // -------------------------------------------------------------------------------- //
  111. class C_TEPlayerAnimEvent : public C_BaseTempEntity
  112. {
  113. public:
  114. DECLARE_CLASS( C_TEPlayerAnimEvent, C_BaseTempEntity );
  115. DECLARE_CLIENTCLASS();
  116. virtual void PostDataUpdate( DataUpdateType_t updateType )
  117. {
  118. // Create the effect.
  119. C_DODPlayer *pPlayer = dynamic_cast< C_DODPlayer* >( m_hPlayer.Get() );
  120. if ( pPlayer && !pPlayer->IsDormant() )
  121. {
  122. pPlayer->DoAnimationEvent( (PlayerAnimEvent_t)m_iEvent.Get(), m_nData );
  123. }
  124. }
  125. public:
  126. CNetworkHandle( CBasePlayer, m_hPlayer );
  127. CNetworkVar( int, m_iEvent );
  128. CNetworkVar( int, m_nData );
  129. };
  130. IMPLEMENT_CLIENTCLASS_EVENT( C_TEPlayerAnimEvent, DT_TEPlayerAnimEvent, CTEPlayerAnimEvent );
  131. BEGIN_RECV_TABLE_NOBASE( C_TEPlayerAnimEvent, DT_TEPlayerAnimEvent )
  132. RecvPropEHandle( RECVINFO( m_hPlayer ) ),
  133. RecvPropInt( RECVINFO( m_iEvent ) ),
  134. RecvPropInt( RECVINFO( m_nData ) )
  135. END_RECV_TABLE()
  136. void RecvProxy_CapAreaIndex( const CRecvProxyData *pData, void *pStruct, void *pOut )
  137. {
  138. CDODPlayerShared *pShared = ( CDODPlayerShared *)pStruct;
  139. int iCapAreaIndex = pData->m_Value.m_Int;
  140. if ( iCapAreaIndex != pShared->GetCPIndex() )
  141. {
  142. pShared->SetCPIndex( iCapAreaIndex );
  143. }
  144. }
  145. // ------------------------------------------------------------------------------------------ //
  146. // Data tables.
  147. // ------------------------------------------------------------------------------------------ //
  148. // CDODPlayerShared Data Tables
  149. //=============================
  150. // specific to the local player ( ideally should not be in CDODPlayerShared! )
  151. BEGIN_RECV_TABLE_NOBASE( CDODPlayerShared, DT_DODSharedLocalPlayerExclusive )
  152. RecvPropInt( RECVINFO( m_iPlayerClass ) ),
  153. RecvPropInt( RECVINFO( m_iDesiredPlayerClass ) ),
  154. RecvPropFloat( RECVINFO( m_flDeployedYawLimitLeft ) ),
  155. RecvPropFloat( RECVINFO( m_flDeployedYawLimitRight ) ),
  156. RecvPropInt( RECVINFO( m_iCPIndex ), 0, RecvProxy_CapAreaIndex ),
  157. RecvPropArray3( RECVINFO_ARRAY( m_bPlayerDominated ), RecvPropBool( RECVINFO( m_bPlayerDominated[0] ) ) ),
  158. RecvPropArray3( RECVINFO_ARRAY( m_bPlayerDominatingMe ), RecvPropBool( RECVINFO( m_bPlayerDominatingMe[0] ) ) ),
  159. END_RECV_TABLE()
  160. // main table
  161. BEGIN_RECV_TABLE_NOBASE( CDODPlayerShared, DT_DODPlayerShared )
  162. RecvPropFloat( RECVINFO( m_flStamina ) ),
  163. RecvPropTime( RECVINFO( m_flSlowedUntilTime ) ),
  164. RecvPropBool( RECVINFO( m_bProne ) ),
  165. RecvPropBool( RECVINFO( m_bIsSprinting ) ),
  166. RecvPropTime( RECVINFO( m_flGoProneTime ) ),
  167. RecvPropTime( RECVINFO( m_flUnProneTime ) ),
  168. RecvPropTime( RECVINFO( m_flDeployChangeTime ) ),
  169. RecvPropFloat( RECVINFO( m_flDeployedHeight ) ),
  170. RecvPropBool( RECVINFO( m_bPlanting ) ),
  171. RecvPropBool( RECVINFO( m_bDefusing ) ),
  172. RecvPropDataTable( "dodsharedlocaldata", 0, 0, &REFERENCE_RECV_TABLE(DT_DODSharedLocalPlayerExclusive) ),
  173. END_RECV_TABLE()
  174. // C_DODPlayer Data Tables
  175. //=========================
  176. // specific to the local player
  177. BEGIN_RECV_TABLE_NOBASE( C_DODPlayer, DT_DODLocalPlayerExclusive )
  178. RecvPropVector( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ) ),
  179. RecvPropFloat( RECVINFO( m_flStunDuration ), 0, RecvProxy_StunTime ),
  180. RecvPropFloat( RECVINFO( m_flStunMaxAlpha)),
  181. RecvPropInt( RECVINFO( m_iProgressBarDuration ) ),
  182. RecvPropFloat( RECVINFO( m_flProgressBarStartTime ) ),
  183. END_RECV_TABLE()
  184. // all players except the local player
  185. BEGIN_RECV_TABLE_NOBASE( C_DODPlayer, DT_DODNonLocalPlayerExclusive )
  186. RecvPropVector( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ) ),
  187. END_RECV_TABLE()
  188. // main table
  189. IMPLEMENT_CLIENTCLASS_DT( C_DODPlayer, DT_DODPlayer, CDODPlayer )
  190. RecvPropDataTable( RECVINFO_DT( m_Shared ), 0, &REFERENCE_RECV_TABLE( DT_DODPlayerShared ) ),
  191. RecvPropDataTable( "dodlocaldata", 0, 0, &REFERENCE_RECV_TABLE(DT_DODLocalPlayerExclusive) ),
  192. RecvPropDataTable( "dodnonlocaldata", 0, 0, &REFERENCE_RECV_TABLE(DT_DODNonLocalPlayerExclusive) ),
  193. RecvPropFloat( RECVINFO( m_angEyeAngles[0] ) ),
  194. RecvPropFloat( RECVINFO( m_angEyeAngles[1] ) ),
  195. RecvPropEHandle( RECVINFO( m_hRagdoll ) ),
  196. RecvPropBool( RECVINFO( m_bSpawnInterpCounter ) ),
  197. RecvPropInt( RECVINFO( m_iAchievementAwardsMask ) ),
  198. END_RECV_TABLE()
  199. // ------------------------------------------------------------------------------------------ //
  200. // Prediction tables.
  201. // ------------------------------------------------------------------------------------------ //
  202. BEGIN_PREDICTION_DATA_NO_BASE( CDODPlayerShared )
  203. DEFINE_PRED_FIELD( m_bProne, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  204. DEFINE_PRED_FIELD( m_flStamina, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
  205. DEFINE_PRED_FIELD( m_bIsSprinting, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  206. DEFINE_PRED_FIELD( m_flGoProneTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
  207. DEFINE_PRED_FIELD( m_flUnProneTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
  208. DEFINE_PRED_FIELD( m_flDeployChangeTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
  209. DEFINE_PRED_FIELD( m_flDeployedHeight, FIELD_FLOAT, FTYPEDESC_INSENDTABLE )
  210. END_PREDICTION_DATA()
  211. BEGIN_PREDICTION_DATA( C_DODPlayer )
  212. DEFINE_PRED_TYPEDESCRIPTION( m_Shared, CDODPlayerShared ),
  213. DEFINE_PRED_FIELD( m_flCycle, FIELD_FLOAT, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE | FTYPEDESC_NOERRORCHECK ),
  214. DEFINE_PRED_FIELD( m_nSequence, FIELD_INTEGER, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE | FTYPEDESC_NOERRORCHECK ),
  215. END_PREDICTION_DATA()
  216. // ----------------------------------------------------------------------------- //
  217. // Client ragdoll entity.
  218. // ----------------------------------------------------------------------------- //
  219. ConVar cl_low_violence( "cl_low_violence", "0" );
  220. ConVar cl_ragdoll_fade_time( "cl_ragdoll_fade_time", "15", FCVAR_CLIENTDLL );
  221. ConVar cl_ragdoll_pronecheck_distance( "cl_ragdoll_pronecheck_distance", "64", FCVAR_GAMEDLL );
  222. class C_DODRagdoll : public C_BaseAnimatingOverlay
  223. {
  224. public:
  225. DECLARE_CLASS( C_DODRagdoll, C_BaseAnimatingOverlay );
  226. DECLARE_CLIENTCLASS();
  227. C_DODRagdoll();
  228. ~C_DODRagdoll();
  229. virtual void OnDataChanged( DataUpdateType_t type );
  230. int GetPlayerEntIndex() const;
  231. IRagdoll* GetIRagdoll() const;
  232. void ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName );
  233. void ClientThink( void );
  234. void StartFadeOut( float fDelay );
  235. bool IsRagdollVisible();
  236. private:
  237. C_DODRagdoll( const C_DODRagdoll & ) {}
  238. void Interp_Copy( C_BaseAnimatingOverlay *pSourceEntity );
  239. void CreateLowViolenceRagdoll();
  240. void CreateDODRagdoll();
  241. private:
  242. EHANDLE m_hPlayer;
  243. CNetworkVector( m_vecRagdollVelocity );
  244. CNetworkVector( m_vecRagdollOrigin );
  245. float m_fDeathTime;
  246. bool m_bFadingOut;
  247. };
  248. IMPLEMENT_CLIENTCLASS_DT_NOBASE( C_DODRagdoll, DT_DODRagdoll, CDODRagdoll )
  249. RecvPropVector( RECVINFO(m_vecRagdollOrigin) ),
  250. RecvPropEHandle( RECVINFO( m_hPlayer ) ),
  251. RecvPropInt( RECVINFO( m_nModelIndex ) ),
  252. RecvPropInt( RECVINFO(m_nForceBone) ),
  253. RecvPropVector( RECVINFO(m_vecForce) ),
  254. RecvPropVector( RECVINFO( m_vecRagdollVelocity ) )
  255. END_RECV_TABLE()
  256. C_DODRagdoll::C_DODRagdoll()
  257. {
  258. m_fDeathTime = -1;
  259. m_bFadingOut = false;
  260. }
  261. C_DODRagdoll::~C_DODRagdoll()
  262. {
  263. PhysCleanupFrictionSounds( this );
  264. }
  265. void C_DODRagdoll::Interp_Copy( C_BaseAnimatingOverlay *pSourceEntity )
  266. {
  267. if ( !pSourceEntity )
  268. return;
  269. VarMapping_t *pSrc = pSourceEntity->GetVarMapping();
  270. VarMapping_t *pDest = GetVarMapping();
  271. // Find all the VarMapEntry_t's that represent the same variable.
  272. for ( int i = 0; i < pDest->m_Entries.Count(); i++ )
  273. {
  274. VarMapEntry_t *pDestEntry = &pDest->m_Entries[i];
  275. for ( int j=0; j < pSrc->m_Entries.Count(); j++ )
  276. {
  277. VarMapEntry_t *pSrcEntry = &pSrc->m_Entries[j];
  278. if ( !Q_strcmp( pSrcEntry->watcher->GetDebugName(),
  279. pDestEntry->watcher->GetDebugName() ) )
  280. {
  281. pDestEntry->watcher->Copy( pSrcEntry->watcher );
  282. break;
  283. }
  284. }
  285. }
  286. }
  287. void C_DODRagdoll::ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName )
  288. {
  289. IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
  290. if( !pPhysicsObject )
  291. return;
  292. Vector dir = pTrace->endpos - pTrace->startpos;
  293. if ( iDamageType == DMG_BLAST )
  294. {
  295. dir *= 4000; // adjust impact strength
  296. // apply force at object mass center
  297. pPhysicsObject->ApplyForceCenter( dir );
  298. }
  299. else
  300. {
  301. Vector hitpos;
  302. VectorMA( pTrace->startpos, pTrace->fraction, dir, hitpos );
  303. VectorNormalize( dir );
  304. dir *= 4000; // adjust impact strength
  305. // apply force where we hit it
  306. pPhysicsObject->ApplyForceOffset( dir, hitpos );
  307. // Blood spray!
  308. FX_DOD_BloodSpray( hitpos, dir, 10 );
  309. }
  310. m_pRagdoll->ResetRagdollSleepAfterTime();
  311. }
  312. void C_DODRagdoll::CreateLowViolenceRagdoll()
  313. {
  314. // Just play a death animation.
  315. // Find a death anim to play.
  316. int iMinDeathAnim = 9999, iMaxDeathAnim = -9999;
  317. for ( int iAnim=1; iAnim < 100; iAnim++ )
  318. {
  319. char str[512];
  320. Q_snprintf( str, sizeof( str ), "death%d", iAnim );
  321. if ( LookupSequence( str ) == -1 )
  322. break;
  323. iMinDeathAnim = MIN( iMinDeathAnim, iAnim );
  324. iMaxDeathAnim = MAX( iMaxDeathAnim, iAnim );
  325. }
  326. if ( iMinDeathAnim == 9999 )
  327. {
  328. CreateDODRagdoll();
  329. }
  330. else
  331. {
  332. int iDeathAnim = RandomInt( iMinDeathAnim, iMaxDeathAnim );
  333. char str[512];
  334. Q_snprintf( str, sizeof( str ), "death%d", iDeathAnim );
  335. SetSequence( LookupSequence( str ) );
  336. ForceClientSideAnimationOn();
  337. SetNetworkOrigin( m_vecRagdollOrigin );
  338. SetAbsOrigin( m_vecRagdollOrigin );
  339. SetAbsVelocity( m_vecRagdollVelocity );
  340. C_DODPlayer *pPlayer = dynamic_cast< C_DODPlayer* >( m_hPlayer.Get() );
  341. if ( pPlayer && !pPlayer->IsDormant() )
  342. {
  343. // move my current model instance to the ragdoll's so decals are preserved.
  344. pPlayer->SnatchModelInstance( this );
  345. SetAbsAngles( pPlayer->GetRenderAngles() );
  346. SetNetworkAngles( pPlayer->GetRenderAngles() );
  347. }
  348. Interp_Reset( GetVarMapping() );
  349. }
  350. }
  351. void C_DODRagdoll::CreateDODRagdoll()
  352. {
  353. // First, initialize all our data. If we have the player's entity on our client,
  354. // then we can make ourselves start out exactly where the player is.
  355. C_DODPlayer *pPlayer = dynamic_cast< C_DODPlayer* >( m_hPlayer.Get() );
  356. #ifdef _DEBUG
  357. DevMsg( 2, "CreateDODRagdoll %d %d\n", gpGlobals->framecount, pPlayer ? pPlayer->entindex() : 0 );
  358. #endif
  359. if ( pPlayer && !pPlayer->IsDormant() )
  360. {
  361. // move my current model instance to the ragdoll's so decals are preserved.
  362. pPlayer->SnatchModelInstance( this );
  363. VarMapping_t *varMap = GetVarMapping();
  364. // Copy all the interpolated vars from the player entity.
  365. // The entity uses the interpolated history to get bone velocity.
  366. if ( !pPlayer->IsLocalPlayer() && pPlayer->dod_IsInterpolationEnabled() )
  367. {
  368. Interp_Copy( pPlayer );
  369. SetAbsAngles( pPlayer->GetRenderAngles() );
  370. GetRotationInterpolator().Reset();
  371. m_flAnimTime = pPlayer->m_flAnimTime;
  372. SetSequence( pPlayer->GetSequence() );
  373. m_flPlaybackRate = pPlayer->GetPlaybackRate();
  374. }
  375. else
  376. {
  377. // This is the local player, so set them in a default
  378. // pose and slam their velocity, angles and origin
  379. SetAbsOrigin( m_vecRagdollOrigin );
  380. SetAbsAngles( pPlayer->GetRenderAngles() );
  381. SetAbsVelocity( m_vecRagdollVelocity );
  382. int iSeq = LookupSequence( "RagdollSpawn" ); // hax, find a neutral standing pose
  383. if ( iSeq == -1 )
  384. {
  385. Assert( false ); // missing look_idle?
  386. iSeq = 0;
  387. }
  388. SetSequence( iSeq ); // look_idle, basic pose
  389. SetCycle( 0.0 );
  390. Interp_Reset( varMap );
  391. }
  392. m_nBody = pPlayer->GetBody();
  393. }
  394. else
  395. {
  396. // overwrite network origin so later interpolation will
  397. // use this position
  398. SetNetworkOrigin( m_vecRagdollOrigin );
  399. SetAbsOrigin( m_vecRagdollOrigin );
  400. SetAbsVelocity( m_vecRagdollVelocity );
  401. Interp_Reset( GetVarMapping() );
  402. }
  403. SetModelIndex( m_nModelIndex );
  404. // Turn it into a ragdoll.
  405. if ( cl_ragdoll_physics_enable.GetInt() )
  406. {
  407. // Make us a ragdoll..
  408. m_nRenderFX = kRenderFxRagdoll;
  409. matrix3x4_t boneDelta0[MAXSTUDIOBONES];
  410. matrix3x4_t boneDelta1[MAXSTUDIOBONES];
  411. matrix3x4_t currentBones[MAXSTUDIOBONES];
  412. const float boneDt = 0.05f;
  413. if ( pPlayer && pPlayer == C_BasePlayer::GetLocalPlayer() )
  414. {
  415. pPlayer->GetRagdollInitBoneArrays( boneDelta0, boneDelta1, currentBones, boneDt );
  416. }
  417. else
  418. {
  419. GetRagdollInitBoneArrays( boneDelta0, boneDelta1, currentBones, boneDt );
  420. }
  421. InitAsClientRagdoll( boneDelta0, boneDelta1, currentBones, boneDt );
  422. }
  423. else
  424. {
  425. ClientLeafSystem()->SetRenderGroup( GetRenderHandle(), RENDER_GROUP_TRANSLUCENT_ENTITY );
  426. }
  427. // Fade out the ragdoll in a while
  428. StartFadeOut( cl_ragdoll_fade_time.GetFloat() );
  429. SetNextClientThink( gpGlobals->curtime + 5.0f );
  430. }
  431. void C_DODRagdoll::OnDataChanged( DataUpdateType_t type )
  432. {
  433. BaseClass::OnDataChanged( type );
  434. if ( type == DATA_UPDATE_CREATED )
  435. {
  436. if ( cl_low_violence.GetInt() )
  437. {
  438. CreateLowViolenceRagdoll();
  439. }
  440. else
  441. {
  442. CreateDODRagdoll();
  443. }
  444. }
  445. else
  446. {
  447. if ( !cl_ragdoll_physics_enable.GetInt() )
  448. {
  449. // Don't let it set us back to a ragdoll with data from the server.
  450. m_nRenderFX = kRenderFxNone;
  451. }
  452. }
  453. }
  454. IRagdoll* C_DODRagdoll::GetIRagdoll() const
  455. {
  456. return m_pRagdoll;
  457. }
  458. bool C_DODRagdoll::IsRagdollVisible()
  459. {
  460. Vector vMins = Vector(-1,-1,-1); //WorldAlignMins();
  461. Vector vMaxs = Vector(1,1,1); //WorldAlignMaxs();
  462. Vector origin = GetAbsOrigin();
  463. if( !engine->IsBoxInViewCluster( vMins + origin, vMaxs + origin) )
  464. {
  465. return false;
  466. }
  467. else if( engine->CullBox( vMins + origin, vMaxs + origin ) )
  468. {
  469. return false;
  470. }
  471. return true;
  472. }
  473. void C_DODRagdoll::ClientThink( void )
  474. {
  475. SetNextClientThink( CLIENT_THINK_ALWAYS );
  476. if ( m_bFadingOut == true )
  477. {
  478. int iAlpha = GetRenderColor().a;
  479. int iFadeSpeed = 600.0f;
  480. iAlpha = MAX( iAlpha - ( iFadeSpeed * gpGlobals->frametime ), 0 );
  481. SetRenderMode( kRenderTransAlpha );
  482. SetRenderColorA( iAlpha );
  483. if ( iAlpha == 0 )
  484. {
  485. //Release();
  486. AddEffects( EF_NODRAW );
  487. }
  488. return;
  489. }
  490. for( int iClient = 1; iClient <= gpGlobals->maxClients; ++iClient )
  491. {
  492. C_DODPlayer *pEnt = static_cast< C_DODPlayer *> ( UTIL_PlayerByIndex( iClient ) );
  493. if(!pEnt || !pEnt->IsPlayer())
  494. continue;
  495. if ( m_hPlayer == NULL )
  496. continue;
  497. if ( pEnt->entindex() == m_hPlayer->entindex() )
  498. continue;
  499. if ( pEnt->GetHealth() <= 0 )
  500. continue;
  501. if ( pEnt->m_Shared.IsProne() == false )
  502. continue;
  503. Vector vTargetOrigin = pEnt->GetAbsOrigin();
  504. Vector vMyOrigin = GetAbsOrigin();
  505. Vector vDir = vTargetOrigin - vMyOrigin;
  506. if ( vDir.Length() > cl_ragdoll_pronecheck_distance.GetInt() )
  507. continue;
  508. SetNextClientThink( CLIENT_THINK_ALWAYS );
  509. m_bFadingOut = true;
  510. return;
  511. }
  512. // if the player is looking at us, delay the fade
  513. if ( IsRagdollVisible() )
  514. {
  515. StartFadeOut( 5.0 );
  516. return;
  517. }
  518. if ( m_fDeathTime > gpGlobals->curtime )
  519. return;
  520. //Release(); // Die
  521. AddEffects( EF_NODRAW );
  522. }
  523. void C_DODRagdoll::StartFadeOut( float fDelay )
  524. {
  525. m_fDeathTime = gpGlobals->curtime + fDelay;
  526. SetNextClientThink( CLIENT_THINK_ALWAYS );
  527. }
  528. // ------------------------------------------------------------------------------------------ //
  529. // C_DODPlayer implementation.
  530. // ------------------------------------------------------------------------------------------ //
  531. C_DODPlayer::C_DODPlayer() :
  532. m_iv_angEyeAngles( "C_DODPlayer::m_iv_angEyeAngles" )
  533. {
  534. m_PlayerAnimState = CreatePlayerAnimState( this );
  535. m_Shared.Init( this );
  536. m_flPitchRecoilAccumulator = 0.0;
  537. m_flYawRecoilAccumulator = 0.0;
  538. m_flRecoilTimeRemaining = 0.0;
  539. m_iProgressBarDuration = 0;
  540. m_flProgressBarStartTime = 0.0f;
  541. AddVar( &m_angEyeAngles, &m_iv_angEyeAngles, LATCH_SIMULATION_VAR );
  542. m_flProneViewOffset = 0.0;
  543. m_bProneSwayingRight = true;
  544. m_iIDEntIndex = 0;
  545. m_Hints.Init( this, NUM_HINTS, g_pszHintMessages );
  546. m_pFlashlightBeam = NULL;
  547. m_fNextThinkPushAway = 0.0f;
  548. // Cold breath.
  549. m_bColdBreathOn = false;
  550. m_flColdBreathTimeStart = 0.0f;
  551. m_flColdBreathTimeEnd = 0.0f;
  552. m_hColdBreathEmitter = NULL;
  553. m_hColdBreathMaterial = INVALID_MATERIAL_HANDLE;
  554. m_flHideHeadIconUntilTime = 0.0f;
  555. m_iAchievementAwardsMask = 0;
  556. m_pHeadIconMaterial = NULL;
  557. }
  558. C_DODPlayer::~C_DODPlayer()
  559. {
  560. m_PlayerAnimState->Release();
  561. ReleaseFlashlight();
  562. // Kill the stamina sound!
  563. if ( m_pStaminaSound )
  564. {
  565. CSoundEnvelopeController::GetController().SoundDestroy( m_pStaminaSound );
  566. m_pStaminaSound = NULL;
  567. }
  568. // Cold breath.
  569. DestroyColdBreathEmitter();
  570. }
  571. C_DODPlayer* C_DODPlayer::GetLocalDODPlayer()
  572. {
  573. return ToDODPlayer( C_BasePlayer::GetLocalPlayer() );
  574. }
  575. IRagdoll* C_DODPlayer::GetRepresentativeRagdoll() const
  576. {
  577. if ( m_hRagdoll.Get() )
  578. {
  579. C_DODRagdoll *pRagdoll = (C_DODRagdoll*)m_hRagdoll.Get();
  580. return pRagdoll->GetIRagdoll();
  581. }
  582. else
  583. {
  584. return NULL;
  585. }
  586. }
  587. const QAngle& C_DODPlayer::GetRenderAngles()
  588. {
  589. if ( IsRagdoll() )
  590. {
  591. return vec3_angle;
  592. }
  593. else
  594. {
  595. return m_PlayerAnimState->GetRenderAngles();
  596. }
  597. }
  598. void C_DODPlayer::UpdateClientSideAnimation()
  599. {
  600. // Update the animation data. It does the local check here so this works when using
  601. // a third-person camera (and we don't have valid player angles).
  602. if ( this == C_DODPlayer::GetLocalDODPlayer() )
  603. m_PlayerAnimState->Update( EyeAngles()[YAW], m_angEyeAngles[PITCH] );
  604. else
  605. m_PlayerAnimState->Update( m_angEyeAngles[YAW], m_angEyeAngles[PITCH] );
  606. BaseClass::UpdateClientSideAnimation();
  607. }
  608. ConVar dod_playachievementsound( "dod_playachievementsound", "1", FCVAR_ARCHIVE );
  609. void C_DODPlayer::OnAchievementAchieved( int iAchievement )
  610. {
  611. // don't draw the head icon for a length of time after showing the particle effect
  612. m_flHideHeadIconUntilTime = gpGlobals->curtime + 2.5;
  613. if ( dod_playachievementsound.GetBool() )
  614. {
  615. EmitSound( "Achievement.Earned" );
  616. }
  617. BaseClass::OnAchievementAchieved( iAchievement );
  618. }
  619. int C_DODPlayer::DrawModel( int flags )
  620. {
  621. int nRetval = BaseClass::DrawModel( flags );
  622. if ( nRetval != 0 )
  623. {
  624. // register to draw the head icon, unless we're hiding it due to the "achieved" particle effect
  625. if ( gpGlobals->curtime > m_flHideHeadIconUntilTime )
  626. {
  627. HeadIconManager()->PlayerDrawn( this );
  628. }
  629. }
  630. return nRetval;
  631. }
  632. void C_DODPlayer::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
  633. {
  634. m_PlayerAnimState->DoAnimationEvent( event, nData );
  635. }
  636. DODPlayerState C_DODPlayer::State_Get() const
  637. {
  638. return m_iPlayerState;
  639. }
  640. bool C_DODPlayer::CanShowClassMenu( void )
  641. {
  642. return ( GetTeamNumber() == TEAM_ALLIES || GetTeamNumber() == TEAM_AXIS );
  643. }
  644. void C_DODPlayer::DoRecoil( int iWpnID, float flWpnRecoil )
  645. {
  646. float flPitchRecoil = flWpnRecoil;
  647. float flYawRecoil = flPitchRecoil / 4;
  648. if( iWpnID == WEAPON_BAR )
  649. flYawRecoil = MIN( flYawRecoil, 1.3 );
  650. if ( m_Shared.IsInMGDeploy() )
  651. {
  652. flPitchRecoil = 0.0;
  653. flYawRecoil = 0.0;
  654. }
  655. else if ( m_Shared.IsProne() &&
  656. iWpnID != WEAPON_30CAL &&
  657. iWpnID != WEAPON_MG42 ) //minor hackage
  658. {
  659. flPitchRecoil = flPitchRecoil / 4;
  660. flYawRecoil = flYawRecoil / 4;
  661. }
  662. else if ( m_Shared.IsDucking() )
  663. {
  664. flPitchRecoil = flPitchRecoil / 2;
  665. flYawRecoil = flYawRecoil / 2;
  666. }
  667. SetRecoilAmount( flPitchRecoil, flYawRecoil );
  668. }
  669. //Set the amount of pitch and yaw recoil we want to do over the next RECOIL_DURATION seconds
  670. void C_DODPlayer::SetRecoilAmount( float flPitchRecoil, float flYawRecoil )
  671. {
  672. //Slam the values, abandon previous recoils
  673. m_flPitchRecoilAccumulator = flPitchRecoil;
  674. flYawRecoil = flYawRecoil * random->RandomFloat( 0.8, 1.1 );
  675. if( random->RandomInt( 0,1 ) <= 0 )
  676. m_flYawRecoilAccumulator = flYawRecoil;
  677. else
  678. m_flYawRecoilAccumulator = -flYawRecoil;
  679. m_flRecoilTimeRemaining = RECOIL_DURATION;
  680. }
  681. //Get the amount of recoil we should do this frame
  682. void C_DODPlayer::GetRecoilToAddThisFrame( float &flPitchRecoil, float &flYawRecoil )
  683. {
  684. if( m_flRecoilTimeRemaining <= 0 )
  685. {
  686. flPitchRecoil = 0.0;
  687. flYawRecoil = 0.0;
  688. return;
  689. }
  690. float flRemaining = MIN( m_flRecoilTimeRemaining, gpGlobals->frametime );
  691. float flRecoilProportion = ( flRemaining / RECOIL_DURATION );
  692. flPitchRecoil = m_flPitchRecoilAccumulator * flRecoilProportion;
  693. flYawRecoil = m_flYawRecoilAccumulator * flRecoilProportion;
  694. m_flRecoilTimeRemaining -= gpGlobals->frametime;
  695. }
  696. //-----------------------------------------------------------------------------
  697. // Purpose: Input handling
  698. //-----------------------------------------------------------------------------
  699. bool C_DODPlayer::CreateMove( float flInputSampleTime, CUserCmd *pCmd )
  700. {
  701. // Lock view if deployed
  702. if( m_Shared.IsInMGDeploy() )
  703. {
  704. m_Shared.ClampDeployedAngles( &pCmd->viewangles );
  705. }
  706. //if we're prone and moving, do some sway
  707. if( m_Shared.IsProne() && IsAlive() )
  708. {
  709. float flSpeed = GetAbsVelocity().Length();
  710. float flSwayAmount = PRONE_SWAY_AMOUNT * gpGlobals->frametime;
  711. if( flSpeed > 10 )
  712. {
  713. if (m_flProneViewOffset >= PRONE_MAX_SWAY)
  714. {
  715. m_bProneSwayingRight = false;
  716. }
  717. else if (m_flProneViewOffset <= -PRONE_MAX_SWAY)
  718. {
  719. m_bProneSwayingRight = true;
  720. }
  721. if (m_bProneSwayingRight)
  722. {
  723. pCmd->viewangles[YAW] += flSwayAmount;
  724. m_flProneViewOffset += flSwayAmount;
  725. }
  726. else
  727. {
  728. pCmd->viewangles[YAW] -= flSwayAmount;
  729. m_flProneViewOffset -= flSwayAmount;
  730. }
  731. }
  732. else
  733. {
  734. // Return to 0 prone sway offset gradually
  735. //Quick Checks to make sure it isn't bigger or smaller than our sway amount
  736. if ( (m_flProneViewOffset < 0.0 && m_flProneViewOffset > -flSwayAmount) ||
  737. (m_flProneViewOffset > 0.0 && m_flProneViewOffset < flSwayAmount) )
  738. {
  739. m_flProneViewOffset = 0.0;
  740. }
  741. if (m_flProneViewOffset > 0.0)
  742. {
  743. pCmd->viewangles[YAW] -= flSwayAmount;
  744. m_flProneViewOffset -= flSwayAmount;
  745. }
  746. else if (m_flProneViewOffset < 0.0)
  747. {
  748. pCmd->viewangles[YAW] += flSwayAmount;
  749. m_flProneViewOffset += flSwayAmount;
  750. }
  751. }
  752. }
  753. bool bResult = BaseClass::CreateMove( flInputSampleTime, pCmd );
  754. AvoidPlayers( pCmd );
  755. return bResult;
  756. }
  757. // How fast to avoid collisions with center of other object, in units per second
  758. #define AVOID_SPEED 1000.0f
  759. ConVar cl_avoidspeed( "cl_avoidspeed", "1000.0f", FCVAR_CLIENTDLL );
  760. extern ConVar cl_forwardspeed;
  761. extern ConVar cl_backspeed;
  762. extern ConVar cl_sidespeed;
  763. bool C_DODPlayer::ShouldDraw( void )
  764. {
  765. if( IsDormant() )
  766. return false;
  767. // If we're dead, our ragdoll will be drawn for us instead.
  768. if ( !IsAlive() )
  769. return false;
  770. if( GetTeamNumber() == TEAM_SPECTATOR )
  771. return false;
  772. if( IsLocalPlayer() )
  773. {
  774. if ( IsRagdoll() )
  775. return true;
  776. }
  777. return BaseClass::ShouldDraw();
  778. }
  779. //-----------------------------------------------------------------------------
  780. // Deal with visibility
  781. //-----------------------------------------------------------------------------
  782. void C_DODPlayer::GetToolRecordingState( KeyValues *msg )
  783. {
  784. #ifndef _XBOX
  785. BaseClass::GetToolRecordingState( msg );
  786. BaseEntityRecordingState_t *pBaseEntityState = (BaseEntityRecordingState_t*)msg->GetPtr( "baseentity" );
  787. if ( IsLocalPlayer() )
  788. {
  789. pBaseEntityState->m_bVisible = !IsDormant() && IsAlive() && ( GetTeamNumber() != TEAM_SPECTATOR ) &&
  790. ( GetRenderMode() != kRenderNone ) && (GetObserverMode() != OBS_MODE_DEATHCAM) && !IsEffectActive(EF_NODRAW);
  791. }
  792. #endif
  793. }
  794. CWeaponDODBase* C_DODPlayer::GetActiveDODWeapon() const
  795. {
  796. C_BaseCombatWeapon *pWpn = GetActiveWeapon();
  797. if ( !pWpn )
  798. return NULL;
  799. return dynamic_cast< CWeaponDODBase* >( pWpn );
  800. }
  801. C_BaseAnimating * C_DODPlayer::BecomeRagdollOnClient()
  802. {
  803. return NULL;
  804. }
  805. void C_DODPlayer::FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options )
  806. {
  807. if( event == 7002 )
  808. {
  809. if( this == C_BasePlayer::GetLocalPlayer() )
  810. return;
  811. CWeaponDODBase *pWeapon = GetActiveDODWeapon();
  812. if ( !pWeapon )
  813. return;
  814. int iAttachment = 2;
  815. Vector vecOrigin;
  816. QAngle angAngles;
  817. if( pWeapon->GetAttachment( iAttachment, vecOrigin, angAngles ) )
  818. {
  819. int shellType = atoi(options);
  820. CEffectData data;
  821. data.m_nHitBox = shellType;
  822. data.m_vOrigin = vecOrigin;
  823. data.m_vAngles = angAngles;
  824. DispatchEffect( "DOD_EjectBrass", data );
  825. }
  826. }
  827. else
  828. BaseClass::FireEvent( origin, angles, event, options );
  829. /*
  830. // MATTTODO: water footstep effects
  831. if( event == 7001 )
  832. {
  833. bool bInWater = ( enginetrace->GetPointContents(origin) & CONTENTS_WATER );
  834. if( bInWater )
  835. {
  836. //run splash
  837. CEffectData data;
  838. //trace up from foot position to the water surface
  839. trace_t tr;
  840. Vector vecTrace(0,0,1024);
  841. UTIL_TraceLine( origin, origin + vecTrace, MASK_WATER, NULL, COLLISION_GROUP_NONE, &tr );
  842. if ( tr.fractionleftsolid )
  843. {
  844. data.m_vOrigin = origin + (vecTrace * tr.fractionleftsolid);
  845. }
  846. else
  847. {
  848. data.m_vOrigin = origin;
  849. }
  850. data.m_vNormal = Vector( 0,0,1 );
  851. data.m_flScale = random->RandomFloat( 4.0f, 5.0f );
  852. DispatchEffect( "watersplash", data );
  853. }
  854. }
  855. else if( event == 7002 )
  856. {
  857. bool bInWater = ( enginetrace->GetPointContents(origin) & CONTENTS_WATER );
  858. if( bInWater )
  859. {
  860. //walk ripple
  861. CEffectData data;
  862. //trace up from foot position to the water surface
  863. trace_t tr;
  864. Vector vecTrace(0,0,1024);
  865. UTIL_TraceLine( origin, origin + vecTrace, MASK_WATER, NULL, COLLISION_GROUP_NONE, &tr );
  866. if ( tr.fractionleftsolid )
  867. {
  868. data.m_vOrigin = origin + (vecTrace * tr.fractionleftsolid);
  869. }
  870. else
  871. {
  872. data.m_vOrigin = origin;
  873. }
  874. data.m_vNormal = Vector( 0,0,1 );
  875. data.m_flScale = random->RandomFloat( 4.0f, 7.0f );
  876. DispatchEffect( "waterripple", data );
  877. }
  878. }
  879. */
  880. }
  881. // NVNT gate for spectating.
  882. static bool inSpectating_Haptics = false;
  883. // NVNT check grenade things ( -- this is here to avoid modificaions to the grenade class -- )
  884. static bool s_holdingGrenade = false;
  885. static bool s_grenadePinPulled = false;
  886. static bool s_grenadeArmed = false;
  887. static bool s_bombPlanting = false;
  888. void C_DODPlayer::ClientThink()
  889. {
  890. BaseClass::ClientThink();
  891. if ( gpGlobals->curtime >= m_fNextThinkPushAway )
  892. {
  893. PerformObstaclePushaway( this );
  894. m_fNextThinkPushAway = gpGlobals->curtime + PUSHAWAY_THINK_INTERVAL;
  895. }
  896. if ( IsLocalPlayer() )
  897. {
  898. UpdateIDTarget();
  899. StaminaSoundThink();
  900. // Recoil
  901. QAngle viewangles;
  902. engine->GetViewAngles( viewangles );
  903. float flYawRecoil;
  904. float flPitchRecoil;
  905. GetRecoilToAddThisFrame( flPitchRecoil, flYawRecoil );
  906. // Recoil
  907. if( flPitchRecoil > 0 )
  908. {
  909. //add the recoil pitch
  910. viewangles[PITCH] -= flPitchRecoil;
  911. viewangles[YAW] += flYawRecoil;
  912. }
  913. // Sniper sway
  914. if( m_Shared.IsSniperZoomed() && GetFOV() <= 20 )
  915. {
  916. //multiply by frametime to balance for framerate changes
  917. float x = gpGlobals->frametime * cos( gpGlobals->curtime );
  918. float y = gpGlobals->frametime * 2 * cos( 2 * gpGlobals->curtime );
  919. float scale;
  920. if( m_Shared.IsDucking() ) //duck
  921. scale = ZOOM_SWAY_DUCKING;
  922. else if( m_Shared.IsProne() )
  923. scale = ZOOM_SWAY_PRONE;
  924. else //standing
  925. scale = ZOOM_SWAY_STANDING;
  926. if( GetAbsVelocity().Length() > 10 )
  927. scale += ZOOM_SWAY_MOVING_PENALTY;
  928. viewangles[PITCH] += y * scale;
  929. viewangles[YAW] += x * scale;
  930. }
  931. engine->SetViewAngles( viewangles );
  932. // NVNT check spectator nav.
  933. if( ( ( GetTeamNumber() == TEAM_SPECTATOR ) || ( !this->IsAlive() ) ) ) {
  934. if(!inSpectating_Haptics)
  935. {
  936. if ( haptics )
  937. haptics->SetNavigationClass("spectate");
  938. inSpectating_Haptics = true;
  939. }
  940. }else{
  941. if(inSpectating_Haptics) {
  942. if ( haptics )
  943. haptics->SetNavigationClass("on_foot");
  944. inSpectating_Haptics = false;
  945. }
  946. }
  947. // NVNT check grenade things ( -- this is here to avoid modificaions to the grenade class -- )
  948. C_WeaponDODBaseGrenade *heldGrenade = dynamic_cast<C_WeaponDODBaseGrenade*>(GetActiveDODWeapon());
  949. if(heldGrenade)
  950. {
  951. if(!s_holdingGrenade)
  952. {
  953. s_holdingGrenade = true;
  954. }
  955. bool pinPulled = heldGrenade->m_bPinPulled;
  956. if(pinPulled != s_grenadePinPulled) {
  957. if(pinPulled)
  958. {
  959. if ( haptics )
  960. haptics->ProcessHapticEvent(3, "Weapons", heldGrenade->GetClassname(), "PinPulled");
  961. }else {
  962. if ( haptics )
  963. haptics->ProcessHapticEvent(3, "Weapons", heldGrenade->GetClassname(), "PinReplaced");
  964. }
  965. s_grenadePinPulled = pinPulled;
  966. }
  967. bool grenadeArmed = heldGrenade->m_bArmed;
  968. if(grenadeArmed != s_grenadeArmed) {
  969. if(grenadeArmed)
  970. {
  971. if ( haptics )
  972. haptics->ProcessHapticEvent(3, "Weapons", heldGrenade->GetClassname(), "Armed");
  973. }else {
  974. if ( haptics )
  975. haptics->ProcessHapticEvent(3, "Weapons", heldGrenade->GetClassname(), "Unarmed");
  976. }
  977. s_grenadeArmed = grenadeArmed;
  978. }
  979. }else{
  980. if( s_holdingGrenade )
  981. {
  982. if(s_grenadeArmed && s_grenadePinPulled) {
  983. if ( haptics )
  984. haptics->ProcessHapticEvent(3, "Weapons", "Grenades", "Thrown");
  985. }
  986. s_holdingGrenade = false;
  987. s_grenadeArmed = false;
  988. s_grenadePinPulled = false;
  989. }
  990. C_DODBaseBombWeapon *heldBomb = dynamic_cast<C_DODBaseBombWeapon*>(GetActiveDODWeapon());
  991. if(heldBomb) {
  992. bool isPlanting = heldBomb->IsPlanting();
  993. if(isPlanting!=s_bombPlanting) {
  994. if(isPlanting) {
  995. if(!s_bombPlanting) {
  996. s_bombPlanting = true;
  997. if ( haptics )
  998. haptics->ProcessHapticEvent(3, "Weapons", heldBomb->GetClassname(), "Plant");
  999. }
  1000. }else{
  1001. if(s_bombPlanting) {
  1002. if ( haptics )
  1003. haptics->ProcessHapticEvent(3, "Weapons", heldBomb->GetClassname(), "StopPlant");
  1004. }
  1005. }
  1006. }
  1007. }else if(s_bombPlanting) {
  1008. s_bombPlanting = false;
  1009. if ( haptics )
  1010. haptics->ProcessHapticEvent(3, "Weapons", "Bomb", "Complete");
  1011. }
  1012. }
  1013. }
  1014. else
  1015. {
  1016. // Cold breath.
  1017. UpdateColdBreath();
  1018. }
  1019. }
  1020. // Start or stop the stamina breathing sound if necessary
  1021. void C_DODPlayer::StaminaSoundThink( void )
  1022. {
  1023. if ( m_bPlayingLowStaminaSound )
  1024. {
  1025. if ( !IsAlive() || m_Shared.GetStamina() >= LOW_STAMINA_THRESHOLD )
  1026. {
  1027. // stop the sprint sound
  1028. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  1029. controller.SoundFadeOut( m_pStaminaSound, 1.0, true );
  1030. // SoundFadeOut will destroy this sound, so we will have to create another one
  1031. // if we go below the threshold again soon
  1032. m_pStaminaSound = NULL;
  1033. m_bPlayingLowStaminaSound = false;
  1034. }
  1035. }
  1036. else
  1037. {
  1038. if ( IsAlive() && m_Shared.GetStamina() < LOW_STAMINA_THRESHOLD )
  1039. {
  1040. // we are alive and have low stamina
  1041. CLocalPlayerFilter filter;
  1042. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  1043. if ( !m_pStaminaSound )
  1044. m_pStaminaSound = controller.SoundCreate( filter, entindex(), "Player.Sprint" );
  1045. controller.Play( m_pStaminaSound, 0.0, 100 );
  1046. controller.SoundChangeVolume( m_pStaminaSound, 1.0, 2.0 );
  1047. m_bPlayingLowStaminaSound = true;
  1048. }
  1049. }
  1050. }
  1051. void C_DODPlayer::OnDataChanged( DataUpdateType_t type )
  1052. {
  1053. BaseClass::OnDataChanged( type );
  1054. if ( type == DATA_UPDATE_CREATED )
  1055. {
  1056. SetNextClientThink( CLIENT_THINK_ALWAYS );
  1057. }
  1058. UpdateVisibility();
  1059. }
  1060. void C_DODPlayer::PostDataUpdate( DataUpdateType_t updateType )
  1061. {
  1062. // C_BaseEntity assumes we're networking the entity's angles, so pretend that it
  1063. // networked the same value we already have.
  1064. SetNetworkAngles( GetLocalAngles() );
  1065. BaseClass::PostDataUpdate( updateType );
  1066. if( m_bSpawnInterpCounter != m_bSpawnInterpCounterCache )
  1067. {
  1068. if ( IsLocalPlayer() )
  1069. {
  1070. LocalPlayerRespawn();
  1071. }
  1072. m_bSpawnInterpCounterCache = m_bSpawnInterpCounter.m_Value;
  1073. }
  1074. }
  1075. // Called every time the player respawns
  1076. void C_DODPlayer::LocalPlayerRespawn( void )
  1077. {
  1078. MoveToLastReceivedPosition( true );
  1079. ResetLatched();
  1080. ResetToneMapping(1.0);
  1081. m_Shared.m_bForceProneChange = true;
  1082. m_flLastRespawnTime = gpGlobals->curtime;
  1083. }
  1084. class C_FadingPhysPropClientside : public C_PhysPropClientside
  1085. {
  1086. public:
  1087. DECLARE_CLASS( C_FadingPhysPropClientside, C_PhysPropClientside );
  1088. // if we wake, extend fade time
  1089. virtual void ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName )
  1090. {
  1091. // If we haven't started fading
  1092. if( GetRenderColor().a >= 255 )
  1093. {
  1094. // delay the fade
  1095. StartFadeOut( 10.0 );
  1096. // register the impact
  1097. BaseClass::ImpactTrace( pTrace, iDamageType, pCustomImpactName );
  1098. }
  1099. }
  1100. };
  1101. void C_DODPlayer::PopHelmet( Vector vecDir, Vector vecForceOrigin, int iModel )
  1102. {
  1103. if ( IsDormant() )
  1104. return; // We can't see them anyway, just bail
  1105. C_FadingPhysPropClientside *pEntity = new C_FadingPhysPropClientside();
  1106. if ( !pEntity )
  1107. return;
  1108. const model_t *model = modelinfo->GetModel( iModel );
  1109. if ( !model )
  1110. {
  1111. DevMsg("CTempEnts::PhysicsProp: model index %i not found\n", iModel );
  1112. return;
  1113. }
  1114. Vector vecHead;
  1115. QAngle angHeadAngles;
  1116. {
  1117. C_BaseAnimating::AutoAllowBoneAccess boneaccess( true, false );
  1118. int iAttachment = LookupAttachment( "head" );
  1119. GetAttachment( iAttachment, vecHead, angHeadAngles ); //attachment 1 is the head attachment
  1120. }
  1121. pEntity->SetModelName( modelinfo->GetModelName(model) );
  1122. pEntity->SetAbsOrigin( vecHead );
  1123. pEntity->SetAbsAngles( angHeadAngles );
  1124. pEntity->SetPhysicsMode( PHYSICS_MULTIPLAYER_CLIENTSIDE );
  1125. if ( !pEntity->Initialize() )
  1126. {
  1127. pEntity->Release();
  1128. return;
  1129. }
  1130. IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject();
  1131. if( pPhysicsObject )
  1132. {
  1133. #ifdef DEBUG
  1134. if( vecForceOrigin == vec3_origin )
  1135. {
  1136. vecForceOrigin = GetAbsOrigin();
  1137. }
  1138. #endif
  1139. Vector vecForce = vecDir;
  1140. Vector vecOffset = vecForceOrigin - pEntity->GetAbsOrigin();
  1141. pPhysicsObject->ApplyForceOffset( vecForce, vecOffset );
  1142. }
  1143. else
  1144. {
  1145. // failed to create a physics object
  1146. pEntity->Release();
  1147. return;
  1148. }
  1149. pEntity->StartFadeOut( 10.0 );
  1150. }
  1151. void C_DODPlayer::ReceiveMessage( int classID, bf_read &msg )
  1152. {
  1153. if ( classID != GetClientClass()->m_ClassID )
  1154. {
  1155. // message is for subclass
  1156. BaseClass::ReceiveMessage( classID, msg );
  1157. return;
  1158. }
  1159. int messageType = msg.ReadByte();
  1160. switch( messageType )
  1161. {
  1162. case DOD_PLAYER_POP_HELMET:
  1163. {
  1164. Vector vecDir, vecForceOffset;
  1165. msg.ReadBitVec3Coord( vecDir );
  1166. msg.ReadBitVec3Coord( vecForceOffset );
  1167. int model = msg.ReadShort();
  1168. PopHelmet( vecDir, vecForceOffset, model );
  1169. }
  1170. break;
  1171. case DOD_PLAYER_REMOVE_DECALS:
  1172. {
  1173. RemoveAllDecals();
  1174. }
  1175. break;
  1176. default:
  1177. break;
  1178. }
  1179. }
  1180. //-----------------------------------------------------------------------------
  1181. // Purpose: Update this client's target entity
  1182. //-----------------------------------------------------------------------------
  1183. void C_DODPlayer::UpdateIDTarget()
  1184. {
  1185. Assert( IsLocalPlayer() );
  1186. // Clear old target and find a new one
  1187. m_iIDEntIndex = 0;
  1188. // don't show IDs in chase spec mode
  1189. if ( GetObserverMode() == OBS_MODE_CHASE ||
  1190. GetObserverMode() == OBS_MODE_DEATHCAM )
  1191. return;
  1192. trace_t tr;
  1193. Vector vecStart, vecEnd;
  1194. VectorMA( MainViewOrigin(), 1500, MainViewForward(), vecEnd );
  1195. VectorMA( MainViewOrigin(), 10, MainViewForward(), vecStart );
  1196. UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  1197. C_BaseEntity *pEntity = NULL;
  1198. if ( !tr.startsolid && tr.DidHitNonWorldEntity() )
  1199. {
  1200. pEntity = tr.m_pEnt;
  1201. if ( pEntity && (pEntity != this) )
  1202. {
  1203. m_iIDEntIndex = pEntity->entindex();
  1204. }
  1205. }
  1206. // if we haven't done the weapon hint, and this entity is a weapon,
  1207. // show the weapon hint
  1208. if ( m_Hints.HasPlayedHint( HINT_PICK_UP_WEAPON ) == false && pEntity )
  1209. {
  1210. Vector vecDist = vecStart - tr.endpos;
  1211. // if m_iIDEntIndex is a CWeaponDODBase, show pick up hint
  1212. CWeaponDODBase *pWpn = dynamic_cast<CWeaponDODBase *>( pEntity );
  1213. if ( pWpn && vecDist.Length() < 100 )
  1214. {
  1215. HintMessage( HINT_PICK_UP_WEAPON );
  1216. }
  1217. }
  1218. }
  1219. bool C_DODPlayer::ShouldAutoReload( void )
  1220. {
  1221. return cl_autoreload.GetBool();
  1222. }
  1223. bool C_DODPlayer::ShouldAutoRezoom( void )
  1224. {
  1225. return cl_autorezoom.GetBool();
  1226. }
  1227. void C_DODPlayer::CheckGrenadeHint( Vector vecGrenadeOrigin )
  1228. {
  1229. if ( m_Hints.HasPlayedHint( HINT_PICK_UP_GRENADE ) == false )
  1230. {
  1231. // if its within 500 units
  1232. float flDist = ( vecGrenadeOrigin - GetAbsOrigin() ).Length2D();
  1233. if ( flDist < 500 )
  1234. {
  1235. m_Hints.HintMessage( HINT_PICK_UP_GRENADE );
  1236. }
  1237. }
  1238. }
  1239. void C_DODPlayer::CheckBombTargetPlantHint( void )
  1240. {
  1241. if ( m_Hints.HasPlayedHint( HINT_BOMB_TARGET ) == false )
  1242. {
  1243. m_Hints.HintMessage( HINT_BOMB_TARGET );
  1244. }
  1245. }
  1246. void C_DODPlayer::CheckBombTargetDefuseHint( void )
  1247. {
  1248. if ( m_Hints.HasPlayedHint( HINT_DEFUSE_BOMB ) == false )
  1249. {
  1250. m_Hints.HintMessage( HINT_DEFUSE_BOMB );
  1251. }
  1252. }
  1253. void C_DODPlayer::LowerWeapon( void )
  1254. {
  1255. m_bWeaponLowered = true;
  1256. }
  1257. void C_DODPlayer::RaiseWeapon( void )
  1258. {
  1259. m_bWeaponLowered = false;
  1260. }
  1261. bool C_DODPlayer::IsWeaponLowered( void )
  1262. {
  1263. if ( GetMoveType() == MOVETYPE_LADDER )
  1264. return true;
  1265. CWeaponDODBase *pWeapon = GetActiveDODWeapon();
  1266. if ( !pWeapon )
  1267. return false;
  1268. // Lower when underwater ( except if its melee )
  1269. if ( GetWaterLevel() > 2 && pWeapon->GetDODWpnData().m_WeaponType != WPN_TYPE_MELEE )
  1270. return true;
  1271. if ( m_Shared.IsProne() && GetAbsVelocity().LengthSqr() > 1 )
  1272. return true;
  1273. if ( m_Shared.IsGoingProne() || m_Shared.IsGettingUpFromProne() )
  1274. return true;
  1275. if ( m_Shared.IsJumping() )
  1276. return true;
  1277. if ( m_Shared.IsDefusing() )
  1278. return true;
  1279. // Lower losing team's weapons in bonus round
  1280. int state = DODGameRules()->State_Get();
  1281. if ( state == STATE_ALLIES_WIN && GetTeamNumber() == TEAM_AXIS )
  1282. return true;
  1283. if ( state == STATE_AXIS_WIN && GetTeamNumber() == TEAM_ALLIES )
  1284. return true;
  1285. if ( m_Shared.IsBazookaDeployed() )
  1286. return false;
  1287. Vector vel = GetAbsVelocity();
  1288. if ( vel.Length2D() < 50 )
  1289. return false;
  1290. if ( m_nButtons & IN_SPEED && ( m_nButtons & IN_FORWARD ) &&
  1291. m_Shared.GetStamina() >= 5 &&
  1292. !m_Shared.IsDucking() )
  1293. return true;
  1294. return m_bWeaponLowered;
  1295. }
  1296. // Shadows
  1297. ConVar cl_blobbyshadows( "cl_blobbyshadows", "0", FCVAR_CLIENTDLL );
  1298. ShadowType_t C_DODPlayer::ShadowCastType( void )
  1299. {
  1300. if ( !IsVisible() )
  1301. return SHADOWS_NONE;
  1302. C_DODPlayer *pLocalPlayer = C_DODPlayer::GetLocalDODPlayer();
  1303. // if we're first person spectating this player
  1304. if ( pLocalPlayer &&
  1305. pLocalPlayer->GetObserverTarget() == this &&
  1306. pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE )
  1307. {
  1308. return SHADOWS_NONE;
  1309. }
  1310. if( cl_blobbyshadows.GetBool() )
  1311. return SHADOWS_SIMPLE;
  1312. return SHADOWS_RENDER_TO_TEXTURE_DYNAMIC;
  1313. }
  1314. float g_flFattenAmt = 4;
  1315. void C_DODPlayer::GetShadowRenderBounds( Vector &mins, Vector &maxs, ShadowType_t shadowType )
  1316. {
  1317. if ( shadowType == SHADOWS_SIMPLE )
  1318. {
  1319. // Don't let the render bounds change when we're using blobby shadows, or else the shadow
  1320. // will pop and stretch.
  1321. mins = CollisionProp()->OBBMins();
  1322. maxs = CollisionProp()->OBBMaxs();
  1323. }
  1324. else
  1325. {
  1326. GetRenderBounds( mins, maxs );
  1327. // We do this because the normal bbox calculations don't take pose params into account, and
  1328. // the rotation of the guy's upper torso can place his gun a ways out of his bbox, and
  1329. // the shadow will get cut off as he rotates.
  1330. //
  1331. // Thus, we give it some padding here.
  1332. mins -= Vector( g_flFattenAmt, g_flFattenAmt, 0 );
  1333. maxs += Vector( g_flFattenAmt, g_flFattenAmt, 0 );
  1334. }
  1335. }
  1336. void C_DODPlayer::GetRenderBounds( Vector& theMins, Vector& theMaxs )
  1337. {
  1338. // TODO POSTSHIP - this hack/fix goes hand-in-hand with a fix in CalcSequenceBoundingBoxes in utils/studiomdl/simplify.cpp.
  1339. // When we enable the fix in CalcSequenceBoundingBoxes, we can get rid of this.
  1340. //
  1341. // What we're doing right here is making sure it only uses the bbox for our lower-body sequences since,
  1342. // with the current animations and the bug in CalcSequenceBoundingBoxes, are WAY bigger than they need to be.
  1343. C_BaseAnimating::GetRenderBounds( theMins, theMaxs );
  1344. }
  1345. bool C_DODPlayer::GetShadowCastDirection( Vector *pDirection, ShadowType_t shadowType ) const
  1346. {
  1347. if ( shadowType == SHADOWS_SIMPLE )
  1348. {
  1349. // Blobby shadows should sit directly underneath us.
  1350. pDirection->Init( 0, 0, -1 );
  1351. return true;
  1352. }
  1353. else
  1354. {
  1355. return BaseClass::GetShadowCastDirection( pDirection, shadowType );
  1356. }
  1357. }
  1358. ConVar cl_muzzleflash_dlight_3rd( "cl_muzzleflash_dlight_3rd", "1" );
  1359. void C_DODPlayer::ProcessMuzzleFlashEvent()
  1360. {
  1361. CBasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
  1362. bool bInToolRecordingMode = ToolsEnabled() && clienttools->IsInRecordingMode();
  1363. // Reenable when the weapons have muzzle flash attachments in the right spot.
  1364. if ( this == pLocalPlayer && !bInToolRecordingMode )
  1365. return; // don't show own world muzzle flashs in for localplayer
  1366. if ( pLocalPlayer && pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE )
  1367. {
  1368. // also don't show in 1st person spec mode
  1369. if ( pLocalPlayer->GetObserverTarget() == this )
  1370. return;
  1371. }
  1372. CWeaponDODBase *pWeapon = GetActiveDODWeapon();
  1373. if ( !pWeapon )
  1374. return;
  1375. int nModelIndex = pWeapon->GetModelIndex();
  1376. int nWorldModelIndex = pWeapon->GetWorldModelIndex();
  1377. if ( bInToolRecordingMode && nModelIndex != nWorldModelIndex )
  1378. {
  1379. pWeapon->SetModelIndex( nWorldModelIndex );
  1380. }
  1381. Vector vecOrigin;
  1382. QAngle angAngles;
  1383. //MATTTODO - use string names of the weapon
  1384. const static int iMuzzleFlashAttachment = 1;
  1385. const static int iEjectBrassAttachment = 2;
  1386. // If we have an attachment, then stick a light on it.
  1387. if ( cl_muzzleflash_dlight_3rd.GetBool() && pWeapon->GetAttachment( iMuzzleFlashAttachment, vecOrigin, angAngles ) )
  1388. {
  1389. // Muzzleflash light
  1390. dlight_t *el = effects->CL_AllocDlight( LIGHT_INDEX_MUZZLEFLASH );
  1391. el->origin = vecOrigin;
  1392. el->radius = 70;
  1393. if ( pWeapon->GetDODWpnData().m_WeaponType == WPN_TYPE_SNIPER )
  1394. el->radius = 150;
  1395. el->decay = el->radius / 0.05f;
  1396. el->die = gpGlobals->curtime + 0.05f;
  1397. el->color.r = 255;
  1398. el->color.g = 192;
  1399. el->color.b = 64;
  1400. el->color.exponent = 5;
  1401. if ( bInToolRecordingMode )
  1402. {
  1403. Color clr( el->color.r, el->color.g, el->color.b );
  1404. KeyValues *msg = new KeyValues( "TempEntity" );
  1405. msg->SetInt( "te", TE_DYNAMIC_LIGHT );
  1406. msg->SetString( "name", "TE_DynamicLight" );
  1407. msg->SetFloat( "time", gpGlobals->curtime );
  1408. msg->SetFloat( "duration", el->die );
  1409. msg->SetFloat( "originx", el->origin.x );
  1410. msg->SetFloat( "originy", el->origin.y );
  1411. msg->SetFloat( "originz", el->origin.z );
  1412. msg->SetFloat( "radius", el->radius );
  1413. msg->SetFloat( "decay", el->decay );
  1414. msg->SetColor( "color", clr );
  1415. msg->SetInt( "exponent", el->color.exponent );
  1416. msg->SetInt( "lightindex", LIGHT_INDEX_MUZZLEFLASH );
  1417. ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
  1418. msg->deleteThis();
  1419. }
  1420. }
  1421. const char *pszMuzzleFlashEffect = NULL;
  1422. switch( pWeapon->GetDODWpnData().m_iMuzzleFlashType )
  1423. {
  1424. case DOD_MUZZLEFLASH_PISTOL:
  1425. pszMuzzleFlashEffect = "muzzle_pistols";
  1426. break;
  1427. case DOD_MUZZLEFLASH_AUTO:
  1428. pszMuzzleFlashEffect = "muzzle_fullyautomatic";
  1429. break;
  1430. case DOD_MUZZLEFLASH_RIFLE:
  1431. pszMuzzleFlashEffect = "muzzle_rifles";
  1432. break;
  1433. case DOD_MUZZLEFLASH_ROCKET:
  1434. pszMuzzleFlashEffect = "muzzle_rockets";
  1435. break;
  1436. case DOD_MUZZLEFLASH_MG42:
  1437. pszMuzzleFlashEffect = "muzzle_mg42";
  1438. break;
  1439. default:
  1440. break;
  1441. }
  1442. if ( pszMuzzleFlashEffect )
  1443. {
  1444. DispatchParticleEffect( pszMuzzleFlashEffect, PATTACH_POINT_FOLLOW, pWeapon, 1 );
  1445. }
  1446. if( pWeapon->ShouldAutoEjectBrass() )
  1447. {
  1448. // shell eject
  1449. if( pWeapon->GetAttachment( iEjectBrassAttachment, vecOrigin, angAngles ) )
  1450. {
  1451. int shellType = pWeapon->GetEjectBrassShellType();
  1452. CEffectData data;
  1453. data.m_nHitBox = shellType;
  1454. data.m_vOrigin = vecOrigin;
  1455. data.m_vAngles = angAngles;
  1456. DispatchEffect( "DOD_EjectBrass", data );
  1457. }
  1458. }
  1459. if ( bInToolRecordingMode && nModelIndex != nWorldModelIndex )
  1460. {
  1461. pWeapon->SetModelIndex( nModelIndex );
  1462. }
  1463. }
  1464. void C_DODPlayer::NotifyShouldTransmit( ShouldTransmitState_t state )
  1465. {
  1466. // Remove all effects if we go out of the PVS.
  1467. if ( state == SHOULDTRANSMIT_END )
  1468. {
  1469. if( m_pFlashlightBeam != NULL )
  1470. {
  1471. ReleaseFlashlight();
  1472. }
  1473. }
  1474. BaseClass::NotifyShouldTransmit( state );
  1475. }
  1476. void C_DODPlayer::Simulate( void )
  1477. {
  1478. if( this != C_BasePlayer::GetLocalPlayer() )
  1479. {
  1480. if ( IsEffectActive( EF_DIMLIGHT ) )
  1481. {
  1482. QAngle eyeAngles = m_angEyeAngles;
  1483. Vector vForward;
  1484. AngleVectors( eyeAngles, &vForward );
  1485. int iAttachment = LookupAttachment( "anim_attachment_RH" );
  1486. Vector vecOrigin;
  1487. QAngle dummy;
  1488. GetAttachment( iAttachment, vecOrigin, dummy );
  1489. trace_t tr;
  1490. UTIL_TraceLine( vecOrigin, vecOrigin + (vForward * 200), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
  1491. if( !m_pFlashlightBeam )
  1492. {
  1493. BeamInfo_t beamInfo;
  1494. beamInfo.m_nType = TE_BEAMPOINTS;
  1495. beamInfo.m_vecStart = tr.startpos;
  1496. beamInfo.m_vecEnd = tr.endpos;
  1497. beamInfo.m_pszModelName = "sprites/glow01.vmt";
  1498. beamInfo.m_pszHaloName = "sprites/glow01.vmt";
  1499. beamInfo.m_flHaloScale = 3.0;
  1500. beamInfo.m_flWidth = 8.0f;
  1501. beamInfo.m_flEndWidth = 35.0f;
  1502. beamInfo.m_flFadeLength = 300.0f;
  1503. beamInfo.m_flAmplitude = 0;
  1504. beamInfo.m_flBrightness = 60.0;
  1505. beamInfo.m_flSpeed = 0.0f;
  1506. beamInfo.m_nStartFrame = 0.0;
  1507. beamInfo.m_flFrameRate = 0.0;
  1508. beamInfo.m_flRed = 255.0;
  1509. beamInfo.m_flGreen = 255.0;
  1510. beamInfo.m_flBlue = 255.0;
  1511. beamInfo.m_nSegments = 8;
  1512. beamInfo.m_bRenderable = true;
  1513. beamInfo.m_flLife = 0.5;
  1514. beamInfo.m_nFlags = FBEAM_FOREVER | FBEAM_ONLYNOISEONCE | FBEAM_NOTILE | FBEAM_HALOBEAM;
  1515. m_pFlashlightBeam = beams->CreateBeamPoints( beamInfo );
  1516. }
  1517. if( m_pFlashlightBeam )
  1518. {
  1519. BeamInfo_t beamInfo;
  1520. beamInfo.m_vecStart = tr.startpos;
  1521. beamInfo.m_vecEnd = tr.endpos;
  1522. beamInfo.m_flRed = 255.0;
  1523. beamInfo.m_flGreen = 255.0;
  1524. beamInfo.m_flBlue = 255.0;
  1525. beams->UpdateBeamInfo( m_pFlashlightBeam, beamInfo );
  1526. dlight_t *el = effects->CL_AllocDlight( 0 );
  1527. el->origin = tr.endpos;
  1528. el->radius = 50;
  1529. el->color.r = 200;
  1530. el->color.g = 200;
  1531. el->color.b = 200;
  1532. el->die = gpGlobals->curtime + 0.1;
  1533. }
  1534. }
  1535. else if ( m_pFlashlightBeam )
  1536. {
  1537. ReleaseFlashlight();
  1538. }
  1539. }
  1540. BaseClass::Simulate();
  1541. }
  1542. void C_DODPlayer::ReleaseFlashlight( void )
  1543. {
  1544. if( m_pFlashlightBeam )
  1545. {
  1546. m_pFlashlightBeam->flags = 0;
  1547. m_pFlashlightBeam->die = gpGlobals->curtime - 1;
  1548. m_pFlashlightBeam = NULL;
  1549. }
  1550. }
  1551. bool C_DODPlayer::SetFOV( CBaseEntity *pRequester, int FOV, float zoomRate /* = 0.0f */ )
  1552. {
  1553. /*
  1554. if( FOV < 30 )
  1555. {
  1556. // fade in
  1557. ScreenFade_t sf;
  1558. memset( &sf, 0, sizeof( sf ) );
  1559. sf.a = 255;
  1560. sf.r = 0;
  1561. sf.g = 0;
  1562. sf.b = 0;
  1563. sf.duration = (unsigned short)((float)(1<<SCREENFADE_FRACBITS) * 2.5f );
  1564. sf.fadeFlags = FFADE_IN;
  1565. vieweffects->Fade( sf );
  1566. }
  1567. else
  1568. {
  1569. //cancel the fade if its active
  1570. ScreenFade_t sf;
  1571. memset( &sf, 0, sizeof( sf ) );
  1572. sf.fadeFlags = FFADE_IN | FFADE_PURGE;
  1573. vieweffects->Fade( sf );
  1574. }
  1575. */
  1576. return true;
  1577. }
  1578. void C_DODPlayer::CalcObserverView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov )
  1579. {
  1580. if( GetObserverMode() == OBS_MODE_DEATHCAM )
  1581. {
  1582. CalcDODDeathCamView( eyeOrigin, eyeAngles, fov );
  1583. }
  1584. else
  1585. BaseClass::CalcObserverView( eyeOrigin, eyeAngles, fov );
  1586. }
  1587. static Vector WALL_MIN(-WALL_OFFSET,-WALL_OFFSET,-WALL_OFFSET);
  1588. static Vector WALL_MAX(WALL_OFFSET,WALL_OFFSET,WALL_OFFSET);
  1589. void C_DODPlayer::CalcDODDeathCamView(Vector& eyeOrigin, QAngle& eyeAngles, float& fov)
  1590. {
  1591. CBaseEntity * killer = GetObserverTarget();
  1592. //float interpolation = ( gpGlobals->curtime - m_flDeathTime ) / DEATH_ANIMATION_TIME;
  1593. // Interpolate very quickly to the killer and follow
  1594. float interpolation = ( gpGlobals->curtime - m_flDeathTime ) / 0.2f;
  1595. interpolation = clamp( interpolation, 0.0f, 1.0f );
  1596. m_flObserverChaseDistance += gpGlobals->frametime*48.0f;
  1597. m_flObserverChaseDistance = clamp( m_flObserverChaseDistance, CHASE_CAM_DISTANCE_MIN, CHASE_CAM_DISTANCE_MAX );
  1598. QAngle aForward = eyeAngles = EyeAngles();
  1599. Vector origin = EyePosition();
  1600. IRagdoll *pRagdoll = GetRepresentativeRagdoll();
  1601. if ( pRagdoll )
  1602. {
  1603. origin = pRagdoll->GetRagdollOrigin();
  1604. origin.z += VEC_DEAD_VIEWHEIGHT_SCALED( this ).z; // look over ragdoll, not through
  1605. }
  1606. if ( killer && (killer != this) )
  1607. {
  1608. Vector vecKiller = killer->GetAbsOrigin();
  1609. C_DODPlayer *player = ToDODPlayer( killer );
  1610. if ( player && player->IsAlive() )
  1611. {
  1612. if ( player->m_Shared.IsProne() )
  1613. {
  1614. VectorAdd( vecKiller, VEC_PRONE_VIEW_SCALED( this ), vecKiller );
  1615. }
  1616. else if( player->GetFlags() & FL_DUCKING )
  1617. {
  1618. VectorAdd( vecKiller, VEC_DUCK_VIEW_SCALED( this ), vecKiller );
  1619. }
  1620. else
  1621. {
  1622. VectorAdd( vecKiller, VEC_VIEW_SCALED( this ), vecKiller );
  1623. }
  1624. }
  1625. Vector vecToKiller = vecKiller - origin;
  1626. QAngle aKiller;
  1627. VectorAngles( vecToKiller, aKiller );
  1628. InterpolateAngles( aForward, aKiller, eyeAngles, interpolation );
  1629. }
  1630. Vector vForward; AngleVectors( eyeAngles, &vForward );
  1631. VectorNormalize( vForward );
  1632. VectorMA( origin, -m_flObserverChaseDistance, vForward, eyeOrigin );
  1633. trace_t trace; // clip against world
  1634. C_BaseEntity::PushEnableAbsRecomputations( false ); // HACK don't recompute positions while doing RayTrace
  1635. UTIL_TraceHull( origin, eyeOrigin, WALL_MIN, WALL_MAX, MASK_SOLID, this, COLLISION_GROUP_NONE, &trace );
  1636. C_BaseEntity::PopEnableAbsRecomputations();
  1637. if (trace.fraction < 1.0)
  1638. {
  1639. eyeOrigin = trace.endpos;
  1640. m_flObserverChaseDistance = VectorLength(origin - eyeOrigin);
  1641. }
  1642. fov = GetFOV();
  1643. }
  1644. void C_DODPlayer::CalcChaseCamView(Vector& eyeOrigin, QAngle& eyeAngles, float& fov)
  1645. {
  1646. C_BaseEntity *target = GetObserverTarget();
  1647. if ( !target )
  1648. {
  1649. // just copy a save in-map position
  1650. VectorCopy( EyePosition(), eyeOrigin );
  1651. VectorCopy( EyeAngles(), eyeAngles );
  1652. return;
  1653. };
  1654. Vector forward, viewpoint;
  1655. // GetRenderOrigin() returns ragdoll pos if player is ragdolled
  1656. Vector origin = target->GetRenderOrigin();
  1657. C_DODPlayer *player = ToDODPlayer( target );
  1658. if ( player && player->IsAlive() )
  1659. {
  1660. if ( player->m_Shared.IsProne() )
  1661. {
  1662. VectorAdd( origin, VEC_PRONE_VIEW_SCALED( this ), origin );
  1663. }
  1664. else if( player->GetFlags() & FL_DUCKING )
  1665. {
  1666. VectorAdd( origin, VEC_DUCK_VIEW_SCALED( this ), origin );
  1667. }
  1668. else
  1669. {
  1670. VectorAdd( origin, VEC_VIEW_SCALED( this ), origin );
  1671. }
  1672. }
  1673. else
  1674. {
  1675. // assume it's the players ragdoll
  1676. VectorAdd( origin, VEC_DEAD_VIEWHEIGHT_SCALED( this ), origin );
  1677. }
  1678. QAngle viewangles;
  1679. if ( IsLocalPlayer() )
  1680. {
  1681. engine->GetViewAngles( viewangles );
  1682. }
  1683. else
  1684. {
  1685. viewangles = EyeAngles();
  1686. }
  1687. m_flObserverChaseDistance += gpGlobals->frametime*48.0f;
  1688. m_flObserverChaseDistance = clamp( m_flObserverChaseDistance, CHASE_CAM_DISTANCE_MIN, CHASE_CAM_DISTANCE_MAX );
  1689. AngleVectors( viewangles, &forward );
  1690. VectorNormalize( forward );
  1691. VectorMA(origin, -m_flObserverChaseDistance, forward, viewpoint );
  1692. trace_t trace;
  1693. C_BaseEntity::PushEnableAbsRecomputations( false ); // HACK don't recompute positions while doing RayTrace
  1694. UTIL_TraceHull( origin, viewpoint, WALL_MIN, WALL_MAX, MASK_SOLID, target, COLLISION_GROUP_NONE, &trace );
  1695. C_BaseEntity::PopEnableAbsRecomputations();
  1696. if (trace.fraction < 1.0)
  1697. {
  1698. viewpoint = trace.endpos;
  1699. m_flObserverChaseDistance = VectorLength(origin - eyeOrigin);
  1700. }
  1701. VectorCopy( viewangles, eyeAngles );
  1702. VectorCopy( viewpoint, eyeOrigin );
  1703. fov = GetFOV();
  1704. }
  1705. extern ConVar spec_freeze_traveltime;
  1706. extern ConVar spec_freeze_time;
  1707. extern ConVar cl_dod_freezecam;
  1708. //-----------------------------------------------------------------------------
  1709. // Purpose: Calculate the view for the player while he's in freeze frame observer mode
  1710. //-----------------------------------------------------------------------------
  1711. void C_DODPlayer::CalcFreezeCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov )
  1712. {
  1713. C_BaseEntity *pTarget = GetObserverTarget();
  1714. if ( !pTarget || !cl_dod_freezecam.GetBool() )
  1715. {
  1716. CalcDeathCamView( eyeOrigin, eyeAngles, fov );
  1717. return;
  1718. }
  1719. // Zoom towards our target
  1720. float flCurTime = (gpGlobals->curtime - m_flFreezeFrameStartTime);
  1721. float flBlendPerc = clamp( flCurTime / spec_freeze_traveltime.GetFloat(), 0, 1 );
  1722. flBlendPerc = SimpleSpline( flBlendPerc );
  1723. // Find the position we would like to be lookin at
  1724. Vector vecCamDesired = pTarget->GetObserverCamOrigin(); // Returns ragdoll origin if they're ragdolled
  1725. VectorAdd( vecCamDesired, GetChaseCamViewOffset( pTarget ), vecCamDesired );
  1726. Vector vecCamTarget = vecCamDesired;
  1727. if ( !pTarget->IsAlive() )
  1728. {
  1729. vecCamTarget.z += pTarget->GetBaseAnimating() ? VEC_DEAD_VIEWHEIGHT_SCALED( pTarget->GetBaseAnimating() ).z : VEC_DEAD_VIEWHEIGHT.z; // look over ragdoll, not through
  1730. }
  1731. // Figure out a view position in front of the target
  1732. Vector vecEyeOnPlane = eyeOrigin;
  1733. vecEyeOnPlane.z = vecCamTarget.z;
  1734. Vector vecTargetPos = vecCamTarget;
  1735. Vector vecToTarget = vecTargetPos - vecEyeOnPlane;
  1736. VectorNormalize( vecToTarget );
  1737. // Stop a few units away from the target, and shift up to be at the same height
  1738. vecTargetPos = vecCamTarget - (vecToTarget * m_flFreezeFrameDistance);
  1739. float flEyePosZ = pTarget->EyePosition().z;
  1740. vecTargetPos.z = flEyePosZ + m_flFreezeZOffset;
  1741. // Now trace out from the target, so that we're put in front of any walls
  1742. trace_t trace;
  1743. C_BaseEntity::PushEnableAbsRecomputations( false ); // HACK don't recompute positions while doing RayTrace
  1744. UTIL_TraceLine( vecCamTarget, vecTargetPos, MASK_SOLID, pTarget, COLLISION_GROUP_NONE, &trace );
  1745. C_BaseEntity::PopEnableAbsRecomputations();
  1746. if (trace.fraction < 1.0 )
  1747. {
  1748. // The camera's going to be really close to the target. So we don't end up
  1749. // looking at someone's chest, aim close freezecams at the target's eyes.
  1750. vecTargetPos = trace.endpos;
  1751. vecCamTarget = vecCamDesired;
  1752. // To stop all close in views looking up at character's chins, move the view up.
  1753. vecTargetPos.z += fabs(vecCamTarget.z - vecTargetPos.z) * 0.85;
  1754. C_BaseEntity::PushEnableAbsRecomputations( false ); // HACK don't recompute positions while doing RayTrace
  1755. UTIL_TraceLine( vecCamTarget, vecTargetPos, MASK_SOLID, pTarget, COLLISION_GROUP_NONE, &trace );
  1756. C_BaseEntity::PopEnableAbsRecomputations();
  1757. vecTargetPos = trace.endpos;
  1758. }
  1759. // Look directly at the target
  1760. vecToTarget = vecCamTarget - vecTargetPos;
  1761. VectorNormalize( vecToTarget );
  1762. VectorAngles( vecToTarget, eyeAngles );
  1763. VectorLerp( m_vecFreezeFrameStart, vecTargetPos, flBlendPerc, eyeOrigin );
  1764. if ( flCurTime >= spec_freeze_traveltime.GetFloat() && !m_bSentFreezeFrame )
  1765. {
  1766. IGameEvent *pEvent = gameeventmanager->CreateEvent( "freezecam_started" );
  1767. if ( pEvent )
  1768. {
  1769. gameeventmanager->FireEventClientSide( pEvent );
  1770. }
  1771. m_bSentFreezeFrame = true;
  1772. view->FreezeFrame( spec_freeze_time.GetFloat() );
  1773. }
  1774. }
  1775. const Vector& C_DODPlayer::GetRenderOrigin( void )
  1776. {
  1777. if ( !IsAlive() && m_hRagdoll.Get() )
  1778. return m_hRagdoll.Get()->GetRenderOrigin();
  1779. return BaseClass::GetRenderOrigin();
  1780. }
  1781. //-----------------------------------------------------------------------------
  1782. // Purpose:
  1783. //-----------------------------------------------------------------------------
  1784. Vector C_DODPlayer::GetChaseCamViewOffset( CBaseEntity *target )
  1785. {
  1786. C_DODPlayer *pPlayer = ToDODPlayer( target );
  1787. if ( pPlayer && pPlayer->IsAlive() )
  1788. {
  1789. if ( pPlayer->m_Shared.IsProne() )
  1790. {
  1791. return VEC_PRONE_VIEW;
  1792. }
  1793. }
  1794. return BaseClass::GetChaseCamViewOffset( target );
  1795. }
  1796. const QAngle& C_DODPlayer::EyeAngles()
  1797. {
  1798. if ( IsLocalPlayer() && g_nKillCamMode == OBS_MODE_NONE )
  1799. {
  1800. return BaseClass::EyeAngles();
  1801. }
  1802. else
  1803. {
  1804. return m_angEyeAngles;
  1805. }
  1806. }
  1807. // Cold breath defines.
  1808. #define COLDBREATH_EMIT_MIN 2.0f
  1809. #define COLDBREATH_EMIT_MAX 3.0f
  1810. #define COLDBREATH_EMIT_SCALE 0.35f
  1811. #define COLDBREATH_PARTICLE_LIFE_MIN 0.25f
  1812. #define COLDBREATH_PARTICLE_LIFE_MAX 1.0f
  1813. #define COLDBREATH_PARTICLE_LIFE_SCALE 0.75
  1814. #define COLDBREATH_PARTICLE_SIZE_MIN 1.0f
  1815. #define COLDBREATH_PARTICLE_SIZE_MAX 4.0f
  1816. #define COLDBREATH_PARTICLE_SIZE_SCALE 1.1f
  1817. #define COLDBREATH_PARTICLE_COUNT 1
  1818. #define COLDBREATH_DURATION_MIN 0.0f
  1819. #define COLDBREATH_DURATION_MAX 1.0f
  1820. #define COLDBREATH_ALPHA_MIN 0.0f
  1821. #define COLDBREATH_ALPHA_MAX 0.3f
  1822. #define COLDBREATH_ENDSCALE_MIN 0.1f
  1823. #define COLDBREATH_ENDSCALE_MAX 0.4f
  1824. static ConVar cl_coldbreath_forcestamina( "cl_coldbreath_forcestamina", "0", FCVAR_CHEAT );
  1825. static ConVar cl_coldbreath_enable( "cl_coldbreath_enable", "1" );
  1826. //-----------------------------------------------------------------------------
  1827. // Purpose: Create the emitter of cold breath particles
  1828. //-----------------------------------------------------------------------------
  1829. bool C_DODPlayer::CreateColdBreathEmitter( void )
  1830. {
  1831. // Check to see if we are in a cold breath scenario.
  1832. if ( !GetClientWorldEntity()->m_bColdWorld )
  1833. return false;
  1834. // Set cold breath to true.
  1835. m_bColdBreathOn = true;
  1836. // Create a cold breath emitter if one doesn't already exist.
  1837. if ( !m_hColdBreathEmitter )
  1838. {
  1839. m_hColdBreathEmitter = ColdBreathEmitter::Create( "ColdBreath" );
  1840. if ( !m_hColdBreathEmitter )
  1841. return false;
  1842. // Get the particle material.
  1843. m_hColdBreathMaterial = m_hColdBreathEmitter->GetPMaterial( "sprites/frostbreath" );
  1844. Assert( m_hColdBreathMaterial != INVALID_MATERIAL_HANDLE );
  1845. // Cache off the head attachment for setting up cold breath.
  1846. m_iHeadAttach = LookupAttachment( "head" );
  1847. }
  1848. return true;
  1849. }
  1850. //-----------------------------------------------------------------------------
  1851. // Purpose: Destroy the cold breath emitter
  1852. //-----------------------------------------------------------------------------
  1853. void C_DODPlayer::DestroyColdBreathEmitter( void )
  1854. {
  1855. #if 0
  1856. if ( m_hColdBreathEmitter.IsValid() )
  1857. {
  1858. UTIL_Remove( m_hColdBreathEmitter );
  1859. }
  1860. #endif
  1861. }
  1862. //-----------------------------------------------------------------------------
  1863. // Purpose:
  1864. //-----------------------------------------------------------------------------
  1865. void C_DODPlayer::UpdateColdBreath( void )
  1866. {
  1867. if ( !cl_coldbreath_enable.GetBool() )
  1868. return;
  1869. // Check to see if the cold breath emitter has been created.
  1870. if ( !m_hColdBreathEmitter.IsValid() )
  1871. {
  1872. if ( !CreateColdBreathEmitter() )
  1873. return;
  1874. }
  1875. // Cold breath updates.
  1876. if ( !m_bColdBreathOn )
  1877. return;
  1878. // Don't emit breath if we are dead.
  1879. if ( !IsAlive() || IsDormant() )
  1880. return;
  1881. // Check player speed, do emit cold breath when moving quickly.
  1882. float flSpeed = GetAbsVelocity().Length();
  1883. if ( flSpeed > 60.0f )
  1884. return;
  1885. if ( m_flColdBreathTimeStart < gpGlobals->curtime )
  1886. {
  1887. // Spawn cold breath particles.
  1888. EmitColdBreathParticles();
  1889. // Update the timer.
  1890. if ( m_flColdBreathTimeEnd < gpGlobals->curtime )
  1891. {
  1892. // Check stamina and modify the time accordingly.
  1893. if ( m_Shared.m_flStamina < LOW_STAMINA_THRESHOLD || cl_coldbreath_forcestamina.GetBool() )
  1894. {
  1895. m_flColdBreathTimeStart = gpGlobals->curtime + RandomFloat( COLDBREATH_EMIT_MIN * COLDBREATH_EMIT_SCALE, COLDBREATH_EMIT_MAX * COLDBREATH_EMIT_SCALE );
  1896. float flDuration = RandomFloat( COLDBREATH_DURATION_MIN, COLDBREATH_DURATION_MAX );
  1897. m_flColdBreathTimeEnd = m_flColdBreathTimeStart + flDuration;
  1898. }
  1899. else
  1900. {
  1901. m_flColdBreathTimeStart = gpGlobals->curtime + RandomFloat( COLDBREATH_EMIT_MIN, COLDBREATH_EMIT_MAX );
  1902. float flDuration = RandomFloat( COLDBREATH_DURATION_MIN, COLDBREATH_DURATION_MAX );
  1903. m_flColdBreathTimeEnd = m_flColdBreathTimeStart + flDuration;
  1904. }
  1905. }
  1906. }
  1907. }
  1908. //-----------------------------------------------------------------------------
  1909. // Purpose:
  1910. //-----------------------------------------------------------------------------
  1911. void C_DODPlayer::CalculateIKLocks( float currentTime )
  1912. {
  1913. if (!m_pIk)
  1914. return;
  1915. int targetCount = m_pIk->m_target.Count();
  1916. if ( targetCount == 0 )
  1917. return;
  1918. // In TF, we might be attaching a player's view to a walking model that's using IK. If we are, it can
  1919. // get in here during the view setup code, and it's not normally supposed to be able to access the spatial
  1920. // partition that early in the rendering loop. So we allow access right here for that special case.
  1921. SpatialPartitionListMask_t curSuppressed = partition->GetSuppressedLists();
  1922. partition->SuppressLists( PARTITION_ALL_CLIENT_EDICTS, false );
  1923. CBaseEntity::PushEnableAbsRecomputations( false );
  1924. for (int i = 0; i < targetCount; i++)
  1925. {
  1926. trace_t trace;
  1927. CIKTarget *pTarget = &m_pIk->m_target[i];
  1928. if (!pTarget->IsActive())
  1929. continue;
  1930. switch( pTarget->type)
  1931. {
  1932. case IK_GROUND:
  1933. {
  1934. pTarget->SetPos( Vector( pTarget->est.pos.x, pTarget->est.pos.y, GetRenderOrigin().z ));
  1935. pTarget->SetAngles( GetRenderAngles() );
  1936. }
  1937. break;
  1938. case IK_ATTACHMENT:
  1939. {
  1940. C_BaseEntity *pEntity = NULL;
  1941. float flDist = pTarget->est.radius;
  1942. // FIXME: make entity finding sticky!
  1943. // FIXME: what should the radius check be?
  1944. for ( CEntitySphereQuery sphere( pTarget->est.pos, 64 ); ( pEntity = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() )
  1945. {
  1946. C_BaseAnimating *pAnim = pEntity->GetBaseAnimating( );
  1947. if (!pAnim)
  1948. continue;
  1949. int iAttachment = pAnim->LookupAttachment( pTarget->offset.pAttachmentName );
  1950. if (iAttachment <= 0)
  1951. continue;
  1952. Vector origin;
  1953. QAngle angles;
  1954. pAnim->GetAttachment( iAttachment, origin, angles );
  1955. // debugoverlay->AddBoxOverlay( origin, Vector( -1, -1, -1 ), Vector( 1, 1, 1 ), QAngle( 0, 0, 0 ), 255, 0, 0, 0, 0 );
  1956. float d = (pTarget->est.pos - origin).Length();
  1957. if ( d >= flDist)
  1958. continue;
  1959. flDist = d;
  1960. pTarget->SetPos( origin );
  1961. pTarget->SetAngles( angles );
  1962. // debugoverlay->AddBoxOverlay( pTarget->est.pos, Vector( -pTarget->est.radius, -pTarget->est.radius, -pTarget->est.radius ), Vector( pTarget->est.radius, pTarget->est.radius, pTarget->est.radius), QAngle( 0, 0, 0 ), 0, 255, 0, 0, 0 );
  1963. }
  1964. if (flDist >= pTarget->est.radius)
  1965. {
  1966. // debugoverlay->AddBoxOverlay( pTarget->est.pos, Vector( -pTarget->est.radius, -pTarget->est.radius, -pTarget->est.radius ), Vector( pTarget->est.radius, pTarget->est.radius, pTarget->est.radius), QAngle( 0, 0, 0 ), 0, 0, 255, 0, 0 );
  1967. // no solution, disable ik rule
  1968. pTarget->IKFailed( );
  1969. }
  1970. }
  1971. break;
  1972. }
  1973. }
  1974. CBaseEntity::PopEnableAbsRecomputations();
  1975. partition->SuppressLists( curSuppressed, true );
  1976. }
  1977. //-----------------------------------------------------------------------------
  1978. // Purpose:
  1979. //-----------------------------------------------------------------------------
  1980. void C_DODPlayer::EmitColdBreathParticles( void )
  1981. {
  1982. // Get the position to emit from - look into caching this off we are doing redundant work in the case
  1983. // of allies (see dod_headiconmanager.cpp).
  1984. Vector vecOrigin;
  1985. QAngle vecAngle;
  1986. GetAttachment( m_iHeadAttach, vecOrigin, vecAngle );
  1987. Vector vecForward, vecRight, vecUp;
  1988. AngleVectors( vecAngle, &vecUp, &vecForward, &vecRight );
  1989. vecOrigin += ( vecForward * 8.0f );
  1990. SimpleParticle *pParticle = static_cast<SimpleParticle*>( m_hColdBreathEmitter->AddParticle( sizeof( SimpleParticle ),m_hColdBreathMaterial, vecOrigin ) );
  1991. if ( pParticle )
  1992. {
  1993. pParticle->m_flLifetime = 0.0f;
  1994. pParticle->m_flDieTime = RandomFloat( COLDBREATH_PARTICLE_LIFE_MIN, COLDBREATH_PARTICLE_LIFE_MAX );
  1995. if ( m_Shared.m_flStamina < LOW_STAMINA_THRESHOLD || cl_coldbreath_forcestamina.GetBool() )
  1996. {
  1997. pParticle->m_flDieTime *= COLDBREATH_PARTICLE_LIFE_SCALE;
  1998. }
  1999. // Add just a little movement.
  2000. if ( m_Shared.m_flStamina < LOW_STAMINA_THRESHOLD || cl_coldbreath_forcestamina.GetBool() )
  2001. {
  2002. pParticle->m_vecVelocity = ( vecForward * RandomFloat( 10.0f, 30.0f ) ) + ( vecRight * RandomFloat( -2.0f, 2.0f ) ) +
  2003. ( vecUp * RandomFloat( 0.0f, 0.5f ) );
  2004. }
  2005. else
  2006. {
  2007. pParticle->m_vecVelocity = ( vecForward * RandomFloat( 10.0f, 20.0f ) ) + ( vecRight * RandomFloat( -2.0f, 2.0f ) ) +
  2008. ( vecUp * RandomFloat( 0.0f, 1.5f ) );
  2009. }
  2010. pParticle->m_uchColor[0] = 200;
  2011. pParticle->m_uchColor[1] = 200;
  2012. pParticle->m_uchColor[2] = 210;
  2013. float flParticleSize = RandomFloat( COLDBREATH_PARTICLE_SIZE_MIN, COLDBREATH_PARTICLE_SIZE_MAX );
  2014. float flParticleScale = RandomFloat( COLDBREATH_ENDSCALE_MIN, COLDBREATH_ENDSCALE_MAX );
  2015. if ( m_Shared.m_flStamina < LOW_STAMINA_THRESHOLD || cl_coldbreath_forcestamina.GetBool() )
  2016. {
  2017. pParticle->m_uchEndSize = flParticleSize * COLDBREATH_PARTICLE_SIZE_SCALE;
  2018. flParticleScale *= COLDBREATH_PARTICLE_SIZE_SCALE;
  2019. }
  2020. else
  2021. {
  2022. pParticle->m_uchEndSize = flParticleSize;
  2023. }
  2024. pParticle->m_uchStartSize = ( flParticleSize * flParticleScale );
  2025. float flAlpha = RandomFloat( COLDBREATH_ALPHA_MIN, COLDBREATH_ALPHA_MAX );
  2026. pParticle->m_uchStartAlpha = flAlpha * 255;
  2027. pParticle->m_uchEndAlpha = 0;
  2028. pParticle->m_flRoll = RandomInt( 0, 360 );
  2029. pParticle->m_flRollDelta = RandomFloat( 0.0f, 1.25f );
  2030. }
  2031. }
  2032. void C_DODPlayer::ComputeWorldSpaceSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs )
  2033. {
  2034. m_Shared.ComputeWorldSpaceSurroundingBox( pVecWorldMins, pVecWorldMaxs );
  2035. }
  2036. //-----------------------------------------------------------------------------
  2037. // Purpose: Try to steer away from any players and objects we might interpenetrate
  2038. //-----------------------------------------------------------------------------
  2039. #define DOD_AVOID_MAX_RADIUS_SQR 5184.0f // Based on player extents and max buildable extents.
  2040. #define DOD_OO_AVOID_MAX_RADIUS_SQR 0.00019f
  2041. #define DOD_MAX_SEPARATION_FORCE 256
  2042. extern ConVar cl_forwardspeed;
  2043. extern ConVar cl_backspeed;
  2044. extern ConVar cl_sidespeed;
  2045. void C_DODPlayer::AvoidPlayers( CUserCmd *pCmd )
  2046. {
  2047. // Don't test if the player is dead.
  2048. if ( IsAlive() == false )
  2049. return;
  2050. C_Team *pTeam = ( C_Team * )GetTeam();
  2051. if ( !pTeam )
  2052. return;
  2053. // Up vector.
  2054. static Vector vecUp( 0.0f, 0.0f, 1.0f );
  2055. Vector vecDODPlayerCenter = GetAbsOrigin();
  2056. Vector vecDODPlayerMin = GetPlayerMins();
  2057. Vector vecDODPlayerMax = GetPlayerMaxs();
  2058. float flZHeight = vecDODPlayerMax.z - vecDODPlayerMin.z;
  2059. vecDODPlayerCenter.z += 0.5f * flZHeight;
  2060. VectorAdd( vecDODPlayerMin, vecDODPlayerCenter, vecDODPlayerMin );
  2061. VectorAdd( vecDODPlayerMax, vecDODPlayerCenter, vecDODPlayerMax );
  2062. // Find an intersecting player or object.
  2063. int nAvoidPlayerCount = 0;
  2064. C_DODPlayer *pAvoidPlayerList[MAX_PLAYERS];
  2065. C_DODPlayer *pIntersectPlayer = NULL;
  2066. float flAvoidRadius = 0.0f;
  2067. Vector vecAvoidCenter, vecAvoidMin, vecAvoidMax;
  2068. for ( int i = 0; i < pTeam->GetNumPlayers(); ++i )
  2069. {
  2070. C_DODPlayer *pAvoidPlayer = static_cast< C_DODPlayer * >( pTeam->GetPlayer( i ) );
  2071. if ( pAvoidPlayer == NULL )
  2072. continue;
  2073. // Is the avoid player me?
  2074. if ( pAvoidPlayer == this )
  2075. continue;
  2076. // Save as list to check against for objects.
  2077. pAvoidPlayerList[nAvoidPlayerCount] = pAvoidPlayer;
  2078. ++nAvoidPlayerCount;
  2079. // Check to see if the avoid player is dormant.
  2080. if ( pAvoidPlayer->IsDormant() )
  2081. continue;
  2082. // Is the avoid player solid?
  2083. if ( pAvoidPlayer->IsSolidFlagSet( FSOLID_NOT_SOLID ) )
  2084. continue;
  2085. Vector t1, t2;
  2086. vecAvoidCenter = pAvoidPlayer->GetAbsOrigin();
  2087. vecAvoidMin = pAvoidPlayer->GetPlayerMins();
  2088. vecAvoidMax = pAvoidPlayer->GetPlayerMaxs();
  2089. flZHeight = vecAvoidMax.z - vecAvoidMin.z;
  2090. vecAvoidCenter.z += 0.5f * flZHeight;
  2091. VectorAdd( vecAvoidMin, vecAvoidCenter, vecAvoidMin );
  2092. VectorAdd( vecAvoidMax, vecAvoidCenter, vecAvoidMax );
  2093. if ( IsBoxIntersectingBox( vecDODPlayerMin, vecDODPlayerMax, vecAvoidMin, vecAvoidMax ) )
  2094. {
  2095. // Need to avoid this player.
  2096. if ( !pIntersectPlayer )
  2097. {
  2098. pIntersectPlayer = pAvoidPlayer;
  2099. break;
  2100. }
  2101. }
  2102. }
  2103. // Anything to avoid?
  2104. if ( !pIntersectPlayer )
  2105. {
  2106. return;
  2107. }
  2108. // Calculate the push strength and direction.
  2109. Vector vecDelta;
  2110. // Avoid a player - they have precedence.
  2111. if ( pIntersectPlayer )
  2112. {
  2113. VectorSubtract( pIntersectPlayer->WorldSpaceCenter(), vecDODPlayerCenter, vecDelta );
  2114. Vector vRad = pIntersectPlayer->WorldAlignMaxs() - pIntersectPlayer->WorldAlignMins();
  2115. vRad.z = 0;
  2116. flAvoidRadius = vRad.Length();
  2117. }
  2118. float flPushStrength = RemapValClamped( vecDelta.Length(), flAvoidRadius, 0, 0, DOD_MAX_SEPARATION_FORCE ); //flPushScale;
  2119. //Msg( "PushScale = %f\n", flPushStrength );
  2120. // Check to see if we have enough push strength to make a difference.
  2121. if ( flPushStrength < 0.01f )
  2122. return;
  2123. Vector vecPush;
  2124. if ( GetAbsVelocity().Length2DSqr() > 0.1f )
  2125. {
  2126. Vector vecVelocity = GetAbsVelocity();
  2127. vecVelocity.z = 0.0f;
  2128. CrossProduct( vecUp, vecVelocity, vecPush );
  2129. VectorNormalize( vecPush );
  2130. }
  2131. else
  2132. {
  2133. // We are not moving, but we're still intersecting.
  2134. QAngle angView = pCmd->viewangles;
  2135. angView.x = 0.0f;
  2136. AngleVectors( angView, NULL, &vecPush, NULL );
  2137. }
  2138. // Move away from the other player/object.
  2139. Vector vecSeparationVelocity;
  2140. if ( vecDelta.Dot( vecPush ) < 0 )
  2141. {
  2142. vecSeparationVelocity = vecPush * flPushStrength;
  2143. }
  2144. else
  2145. {
  2146. vecSeparationVelocity = vecPush * -flPushStrength;
  2147. }
  2148. // Don't allow the max push speed to be greater than the max player speed.
  2149. float flMaxPlayerSpeed = MaxSpeed();
  2150. float flCropFraction = 1.33333333f;
  2151. if ( ( GetFlags() & FL_DUCKING ) && ( GetGroundEntity() != NULL ) )
  2152. {
  2153. flMaxPlayerSpeed *= flCropFraction;
  2154. }
  2155. float flMaxPlayerSpeedSqr = flMaxPlayerSpeed * flMaxPlayerSpeed;
  2156. if ( vecSeparationVelocity.LengthSqr() > flMaxPlayerSpeedSqr )
  2157. {
  2158. vecSeparationVelocity.NormalizeInPlace();
  2159. VectorScale( vecSeparationVelocity, flMaxPlayerSpeed, vecSeparationVelocity );
  2160. }
  2161. QAngle vAngles = pCmd->viewangles;
  2162. vAngles.x = 0;
  2163. Vector currentdir;
  2164. Vector rightdir;
  2165. AngleVectors( vAngles, &currentdir, &rightdir, NULL );
  2166. Vector vDirection = vecSeparationVelocity;
  2167. VectorNormalize( vDirection );
  2168. float fwd = currentdir.Dot( vDirection );
  2169. float rt = rightdir.Dot( vDirection );
  2170. float forward = fwd * flPushStrength;
  2171. float side = rt * flPushStrength;
  2172. //Msg( "fwd: %f - rt: %f - forward: %f - side: %f\n", fwd, rt, forward, side );
  2173. pCmd->forwardmove += forward;
  2174. pCmd->sidemove += side;
  2175. // Clamp the move to within legal limits, preserving direction. This is a little
  2176. // complicated because we have different limits for forward, back, and side
  2177. //Msg( "PRECLAMP: forwardmove=%f, sidemove=%f\n", pCmd->forwardmove, pCmd->sidemove );
  2178. float flForwardScale = 1.0f;
  2179. if ( pCmd->forwardmove > fabs( cl_forwardspeed.GetFloat() ) )
  2180. {
  2181. flForwardScale = fabs( cl_forwardspeed.GetFloat() ) / pCmd->forwardmove;
  2182. }
  2183. else if ( pCmd->forwardmove < -fabs( cl_backspeed.GetFloat() ) )
  2184. {
  2185. flForwardScale = fabs( cl_backspeed.GetFloat() ) / fabs( pCmd->forwardmove );
  2186. }
  2187. float flSideScale = 1.0f;
  2188. if ( fabs( pCmd->sidemove ) > fabs( cl_sidespeed.GetFloat() ) )
  2189. {
  2190. flSideScale = fabs( cl_sidespeed.GetFloat() ) / fabs( pCmd->sidemove );
  2191. }
  2192. float flScale = MIN( flForwardScale, flSideScale );
  2193. pCmd->forwardmove *= flScale;
  2194. pCmd->sidemove *= flScale;
  2195. //Msg( "Pforwardmove=%f, sidemove=%f\n", pCmd->forwardmove, pCmd->sidemove );
  2196. }
  2197. //-----------------------------------------------------------------------------
  2198. // Purpose: Returns whether this player is the nemesis of the local player
  2199. //-----------------------------------------------------------------------------
  2200. bool C_DODPlayer::IsNemesisOfLocalPlayer()
  2201. {
  2202. C_DODPlayer *pLocalPlayer = C_DODPlayer::GetLocalDODPlayer();
  2203. if ( pLocalPlayer )
  2204. {
  2205. // return whether this player is dominating the local player
  2206. return m_Shared.IsPlayerDominated( pLocalPlayer->entindex() );
  2207. }
  2208. return false;
  2209. }
  2210. //-----------------------------------------------------------------------------
  2211. // Purpose: Returns whether we should show the nemesis icon for this player
  2212. //-----------------------------------------------------------------------------
  2213. bool C_DODPlayer::ShouldShowNemesisIcon()
  2214. {
  2215. /*
  2216. // we should show the nemesis effect on this player if he is the nemesis of the local player,
  2217. // and is not dead, cloaked or disguised
  2218. if ( IsNemesisOfLocalPlayer() && g_DODPR && g_PR->IsConnected( entindex() ) )
  2219. {
  2220. if ( IsAlive() )
  2221. return true;
  2222. }
  2223. */
  2224. return false;
  2225. }
  2226. int C_DODPlayer::GetActiveAchievementAward( void )
  2227. {
  2228. int iAward = ACHIEVEMENT_AWARDS_NONE;
  2229. int iClassBit = m_Shared.PlayerClass() + 1;
  2230. if ( m_iAchievementAwardsMask & (1<<ACHIEVEMENT_AWARDS_ALL_PACK_1) )
  2231. {
  2232. iAward = ACHIEVEMENT_AWARDS_ALL_PACK_1;
  2233. }
  2234. else if ( m_iAchievementAwardsMask & ( 1<<iClassBit ) )
  2235. {
  2236. iAward = iClassBit;
  2237. }
  2238. return iAward;
  2239. }
  2240. IMaterial *C_DODPlayer::GetHeadIconMaterial( void )
  2241. {
  2242. const char *pszMaterial = "";
  2243. int iAchievementAward = GetActiveAchievementAward();
  2244. if ( iAchievementAward >= 0 && iAchievementAward < NUM_ACHIEVEMENT_AWARDS )
  2245. {
  2246. switch ( GetTeamNumber() )
  2247. {
  2248. case TEAM_ALLIES:
  2249. pszMaterial = g_pszAchievementAwardMaterials_Allies[iAchievementAward];
  2250. break;
  2251. case TEAM_AXIS:
  2252. pszMaterial = g_pszAchievementAwardMaterials_Axis[iAchievementAward];
  2253. break;
  2254. default:
  2255. break;
  2256. }
  2257. }
  2258. IMaterial *pMaterial = NULL;
  2259. if ( pszMaterial )
  2260. {
  2261. pMaterial = materials->FindMaterial( pszMaterial, TEXTURE_GROUP_VGUI );
  2262. }
  2263. // clear the old one if its different
  2264. if ( m_pHeadIconMaterial != pMaterial )
  2265. {
  2266. if ( m_pHeadIconMaterial )
  2267. {
  2268. m_pHeadIconMaterial->DecrementReferenceCount();
  2269. }
  2270. m_pHeadIconMaterial = pMaterial;
  2271. m_pHeadIconMaterial->IncrementReferenceCount();
  2272. }
  2273. return m_pHeadIconMaterial;
  2274. }