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.

2553 lines
68 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "c_cs_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" //HOOK_COMMAND
  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 "c_physicsprop.h"
  26. #include "props_shared.h"
  27. #include "obstacle_pushaway.h"
  28. #include "death_pose.h"
  29. #include "effect_dispatch_data.h" //for water ripple / splash effect
  30. #include "c_te_effect_dispatch.h" //ditto
  31. #include "c_te_legacytempents.h"
  32. #include "cs_gamerules.h"
  33. #include "fx_cs_blood.h"
  34. #include "c_cs_playerresource.h"
  35. #include "c_team.h"
  36. #include "history_resource.h"
  37. #include "ragdoll_shared.h"
  38. #include "collisionutils.h"
  39. // NVNT - haptics system for spectating
  40. #include "haptics/haptic_utils.h"
  41. #include "steam/steam_api.h"
  42. #include "cs_blackmarket.h" // for vest/helmet prices
  43. #if defined( CCSPlayer )
  44. #undef CCSPlayer
  45. #endif
  46. #include "materialsystem/imesh.h" //for materials->FindMaterial
  47. #include "iviewrender.h" //for view->
  48. #include "iviewrender_beams.h" // flashlight beam
  49. //=============================================================================
  50. // HPE_BEGIN:
  51. // [menglish] Adding and externing variables needed for the freezecam
  52. //=============================================================================
  53. static Vector WALL_MIN(-WALL_OFFSET,-WALL_OFFSET,-WALL_OFFSET);
  54. static Vector WALL_MAX(WALL_OFFSET,WALL_OFFSET,WALL_OFFSET);
  55. extern ConVar spec_freeze_time;
  56. extern ConVar spec_freeze_traveltime;
  57. extern ConVar spec_freeze_distance_min;
  58. extern ConVar spec_freeze_distance_max;
  59. //=============================================================================
  60. // HPE_END
  61. //=============================================================================
  62. ConVar cl_left_hand_ik( "cl_left_hand_ik", "0", 0, "Attach player's left hand to rifle with IK." );
  63. ConVar cl_ragdoll_physics_enable( "cl_ragdoll_physics_enable", "1", 0, "Enable/disable ragdoll physics." );
  64. ConVar cl_minmodels( "cl_minmodels", "0", 0, "Uses one player model for each team." );
  65. ConVar cl_min_ct( "cl_min_ct", "1", 0, "Controls which CT model is used when cl_minmodels is set.", true, 1, true, 4 );
  66. ConVar cl_min_t( "cl_min_t", "1", 0, "Controls which Terrorist model is used when cl_minmodels is set.", true, 1, true, 4 );
  67. const float CycleLatchTolerance = 0.15; // amount we can diverge from the server's cycle before we're corrected
  68. extern ConVar mp_playerid_delay;
  69. extern ConVar mp_playerid_hold;
  70. extern ConVar sv_allowminmodels;
  71. class CAddonInfo
  72. {
  73. public:
  74. const char *m_pAttachmentName;
  75. const char *m_pWeaponClassName; // The addon uses the w_ model from this weapon.
  76. const char *m_pModelName; //If this is present, will use this model instead of looking up the weapon
  77. const char *m_pHolsterName;
  78. };
  79. // These must follow the ADDON_ ordering.
  80. CAddonInfo g_AddonInfo[] =
  81. {
  82. { "grenade0", "weapon_flashbang", 0, 0 },
  83. { "grenade1", "weapon_flashbang", 0, 0 },
  84. { "grenade2", "weapon_hegrenade", 0, 0 },
  85. { "grenade3", "weapon_smokegrenade", 0, 0 },
  86. { "c4", "weapon_c4", 0, 0 },
  87. { "defusekit", 0, "models/weapons/w_defuser.mdl", 0 },
  88. { "primary", 0, 0, 0 }, // Primary addon model is looked up based on m_iPrimaryAddon
  89. { "pistol", 0, 0, 0 }, // Pistol addon model is looked up based on m_iSecondaryAddon
  90. { "eholster", 0, "models/weapons/w_eq_eholster_elite.mdl", "models/weapons/w_eq_eholster.mdl" },
  91. };
  92. // -------------------------------------------------------------------------------- //
  93. // Player animation event. Sent to the client when a player fires, jumps, reloads, etc..
  94. // -------------------------------------------------------------------------------- //
  95. class C_TEPlayerAnimEvent : public C_BaseTempEntity
  96. {
  97. public:
  98. DECLARE_CLASS( C_TEPlayerAnimEvent, C_BaseTempEntity );
  99. DECLARE_CLIENTCLASS();
  100. virtual void PostDataUpdate( DataUpdateType_t updateType )
  101. {
  102. // Create the effect.
  103. C_CSPlayer *pPlayer = dynamic_cast< C_CSPlayer* >( m_hPlayer.Get() );
  104. if ( pPlayer && !pPlayer->IsDormant() )
  105. {
  106. pPlayer->DoAnimationEvent( (PlayerAnimEvent_t)m_iEvent.Get(), m_nData );
  107. }
  108. }
  109. public:
  110. CNetworkHandle( CBasePlayer, m_hPlayer );
  111. CNetworkVar( int, m_iEvent );
  112. CNetworkVar( int, m_nData );
  113. };
  114. IMPLEMENT_CLIENTCLASS_EVENT( C_TEPlayerAnimEvent, DT_TEPlayerAnimEvent, CTEPlayerAnimEvent );
  115. BEGIN_RECV_TABLE_NOBASE( C_TEPlayerAnimEvent, DT_TEPlayerAnimEvent )
  116. RecvPropEHandle( RECVINFO( m_hPlayer ) ),
  117. RecvPropInt( RECVINFO( m_iEvent ) ),
  118. RecvPropInt( RECVINFO( m_nData ) )
  119. END_RECV_TABLE()
  120. BEGIN_PREDICTION_DATA( C_CSPlayer )
  121. #ifdef CS_SHIELD_ENABLED
  122. DEFINE_PRED_FIELD( m_bShieldDrawn, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  123. #endif
  124. DEFINE_PRED_FIELD_TOL( m_flStamina, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, 0.1f ),
  125. DEFINE_PRED_FIELD( m_flCycle, FIELD_FLOAT, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE | FTYPEDESC_NOERRORCHECK ),
  126. DEFINE_PRED_FIELD( m_iShotsFired, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  127. DEFINE_PRED_FIELD( m_iDirection, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  128. DEFINE_PRED_FIELD( m_bResumeZoom, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  129. DEFINE_PRED_FIELD( m_iLastZoom, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  130. END_PREDICTION_DATA()
  131. vgui::IImage* GetDefaultAvatarImage( C_BasePlayer *pPlayer )
  132. {
  133. vgui::IImage* result = NULL;
  134. switch ( pPlayer ? pPlayer->GetTeamNumber() : TEAM_MAXCOUNT )
  135. {
  136. case TEAM_TERRORIST:
  137. result = vgui::scheme()->GetImage( CSTRIKE_DEFAULT_T_AVATAR, true );
  138. break;
  139. case TEAM_CT:
  140. result = vgui::scheme()->GetImage( CSTRIKE_DEFAULT_CT_AVATAR, true );
  141. break;
  142. default:
  143. result = vgui::scheme()->GetImage( CSTRIKE_DEFAULT_AVATAR, true );
  144. break;
  145. }
  146. return result;
  147. }
  148. // ----------------------------------------------------------------------------- //
  149. // Client ragdoll entity.
  150. // ----------------------------------------------------------------------------- //
  151. float g_flDieTranslucentTime = 0.6;
  152. class C_CSRagdoll : public C_BaseAnimatingOverlay
  153. {
  154. public:
  155. DECLARE_CLASS( C_CSRagdoll, C_BaseAnimatingOverlay );
  156. DECLARE_CLIENTCLASS();
  157. C_CSRagdoll();
  158. ~C_CSRagdoll();
  159. virtual void OnDataChanged( DataUpdateType_t type );
  160. int GetPlayerEntIndex() const;
  161. IRagdoll* GetIRagdoll() const;
  162. bool GetRagdollInitBoneArrays( matrix3x4_t *pDeltaBones0, matrix3x4_t *pDeltaBones1, matrix3x4_t *pCurrentBones, float boneDt ) OVERRIDE;
  163. void ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName );
  164. virtual void ComputeFxBlend();
  165. virtual bool IsTransparent();
  166. bool IsInitialized() { return m_bInitialized; }
  167. // fading ragdolls don't cast shadows
  168. virtual ShadowType_t ShadowCastType()
  169. {
  170. if ( m_flRagdollSinkStart == -1 )
  171. return BaseClass::ShadowCastType();
  172. return SHADOWS_NONE;
  173. }
  174. virtual void ValidateModelIndex( void );
  175. private:
  176. C_CSRagdoll( const C_CSRagdoll & ) {}
  177. void Interp_Copy( C_BaseAnimatingOverlay *pSourceEntity );
  178. void CreateLowViolenceRagdoll( void );
  179. void CreateCSRagdoll( void );
  180. private:
  181. EHANDLE m_hPlayer;
  182. CNetworkVector( m_vecRagdollVelocity );
  183. CNetworkVector( m_vecRagdollOrigin );
  184. CNetworkVar(int, m_iDeathPose );
  185. CNetworkVar(int, m_iDeathFrame );
  186. float m_flRagdollSinkStart;
  187. bool m_bInitialized;
  188. bool m_bCreatedWhilePlaybackSkipping;
  189. };
  190. IMPLEMENT_CLIENTCLASS_DT_NOBASE( C_CSRagdoll, DT_CSRagdoll, CCSRagdoll )
  191. RecvPropVector( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ) ),
  192. RecvPropVector( RECVINFO(m_vecRagdollOrigin) ),
  193. RecvPropEHandle( RECVINFO( m_hPlayer ) ),
  194. RecvPropInt( RECVINFO( m_nModelIndex ) ),
  195. RecvPropInt( RECVINFO(m_nForceBone) ),
  196. RecvPropVector( RECVINFO(m_vecForce) ),
  197. RecvPropVector( RECVINFO( m_vecRagdollVelocity ) ),
  198. RecvPropInt( RECVINFO(m_iDeathPose) ),
  199. RecvPropInt( RECVINFO(m_iDeathFrame) ),
  200. RecvPropInt(RECVINFO(m_iTeamNum)),
  201. RecvPropInt( RECVINFO(m_bClientSideAnimation)),
  202. END_RECV_TABLE()
  203. C_CSRagdoll::C_CSRagdoll()
  204. {
  205. m_flRagdollSinkStart = -1;
  206. m_bInitialized = false;
  207. m_bCreatedWhilePlaybackSkipping = engine->IsSkippingPlayback();
  208. }
  209. C_CSRagdoll::~C_CSRagdoll()
  210. {
  211. PhysCleanupFrictionSounds( this );
  212. }
  213. bool C_CSRagdoll::GetRagdollInitBoneArrays( matrix3x4_t *pDeltaBones0, matrix3x4_t *pDeltaBones1, matrix3x4_t *pCurrentBones, float boneDt )
  214. {
  215. // otherwise use the death pose to set up the ragdoll
  216. ForceSetupBonesAtTime( pDeltaBones0, gpGlobals->curtime - boneDt );
  217. GetRagdollCurSequenceWithDeathPose( this, pDeltaBones1, gpGlobals->curtime, m_iDeathPose, m_iDeathFrame );
  218. return SetupBones( pCurrentBones, MAXSTUDIOBONES, BONE_USED_BY_ANYTHING, gpGlobals->curtime );
  219. }
  220. void C_CSRagdoll::Interp_Copy( C_BaseAnimatingOverlay *pSourceEntity )
  221. {
  222. if ( !pSourceEntity )
  223. return;
  224. VarMapping_t *pSrc = pSourceEntity->GetVarMapping();
  225. VarMapping_t *pDest = GetVarMapping();
  226. // Find all the VarMapEntry_t's that represent the same variable.
  227. for ( int i = 0; i < pDest->m_Entries.Count(); i++ )
  228. {
  229. VarMapEntry_t *pDestEntry = &pDest->m_Entries[i];
  230. for ( int j=0; j < pSrc->m_Entries.Count(); j++ )
  231. {
  232. VarMapEntry_t *pSrcEntry = &pSrc->m_Entries[j];
  233. if ( !Q_strcmp( pSrcEntry->watcher->GetDebugName(),
  234. pDestEntry->watcher->GetDebugName() ) )
  235. {
  236. pDestEntry->watcher->Copy( pSrcEntry->watcher );
  237. break;
  238. }
  239. }
  240. }
  241. }
  242. void C_CSRagdoll::ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName )
  243. {
  244. IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
  245. if( !pPhysicsObject )
  246. return;
  247. Vector dir = pTrace->endpos - pTrace->startpos;
  248. if ( iDamageType == DMG_BLAST )
  249. {
  250. dir *= 4000; // adjust impact strenght
  251. // apply force at object mass center
  252. pPhysicsObject->ApplyForceCenter( dir );
  253. }
  254. else
  255. {
  256. Vector hitpos;
  257. VectorMA( pTrace->startpos, pTrace->fraction, dir, hitpos );
  258. VectorNormalize( dir );
  259. dir *= 4000; // adjust impact strenght
  260. // apply force where we hit it
  261. pPhysicsObject->ApplyForceOffset( dir, hitpos );
  262. // Blood spray!
  263. FX_CS_BloodSpray( hitpos, dir, 10 );
  264. }
  265. m_pRagdoll->ResetRagdollSleepAfterTime();
  266. }
  267. void C_CSRagdoll::ValidateModelIndex( void )
  268. {
  269. if ( sv_allowminmodels.GetBool() && cl_minmodels.GetBool() )
  270. {
  271. if ( GetTeamNumber() == TEAM_CT )
  272. {
  273. int index = cl_min_ct.GetInt() - 1;
  274. if ( index >= 0 && index < CTPlayerModels.Count() )
  275. {
  276. m_nModelIndex = modelinfo->GetModelIndex(CTPlayerModels[index]);
  277. }
  278. }
  279. else if ( GetTeamNumber() == TEAM_TERRORIST )
  280. {
  281. int index = cl_min_t.GetInt() - 1;
  282. if ( index >= 0 && index < TerroristPlayerModels.Count() )
  283. {
  284. m_nModelIndex = modelinfo->GetModelIndex(TerroristPlayerModels[index]);
  285. }
  286. }
  287. }
  288. BaseClass::ValidateModelIndex();
  289. }
  290. void C_CSRagdoll::CreateLowViolenceRagdoll( void )
  291. {
  292. // Just play a death animation.
  293. // Find a death anim to play.
  294. int iMinDeathAnim = 9999, iMaxDeathAnim = -9999;
  295. for ( int iAnim=1; iAnim < 100; iAnim++ )
  296. {
  297. char str[512];
  298. Q_snprintf( str, sizeof( str ), "death%d", iAnim );
  299. if ( LookupSequence( str ) == -1 )
  300. break;
  301. iMinDeathAnim = MIN( iMinDeathAnim, iAnim );
  302. iMaxDeathAnim = MAX( iMaxDeathAnim, iAnim );
  303. }
  304. if ( iMinDeathAnim == 9999 )
  305. {
  306. CreateCSRagdoll();
  307. return;
  308. }
  309. SetNetworkOrigin( m_vecRagdollOrigin );
  310. SetAbsOrigin( m_vecRagdollOrigin );
  311. SetAbsVelocity( m_vecRagdollVelocity );
  312. C_CSPlayer *pPlayer = dynamic_cast< C_CSPlayer* >( m_hPlayer.Get() );
  313. if ( pPlayer )
  314. {
  315. if ( !pPlayer->IsDormant() )
  316. {
  317. // move my current model instance to the ragdoll's so decals are preserved.
  318. pPlayer->SnatchModelInstance( this );
  319. }
  320. SetAbsAngles( pPlayer->GetRenderAngles() );
  321. SetNetworkAngles( pPlayer->GetRenderAngles() );
  322. }
  323. int iDeathAnim = RandomInt( iMinDeathAnim, iMaxDeathAnim );
  324. char str[512];
  325. Q_snprintf( str, sizeof( str ), "death%d", iDeathAnim );
  326. SetSequence( LookupSequence( str ) );
  327. ForceClientSideAnimationOn();
  328. Interp_Reset( GetVarMapping() );
  329. }
  330. void C_CSRagdoll::CreateCSRagdoll()
  331. {
  332. // First, initialize all our data. If we have the player's entity on our client,
  333. // then we can make ourselves start out exactly where the player is.
  334. C_CSPlayer *pPlayer = dynamic_cast< C_CSPlayer* >( m_hPlayer.Get() );
  335. // mark this to prevent model changes from overwriting the death sequence with the server sequence
  336. SetReceivedSequence();
  337. if ( pPlayer && !pPlayer->IsDormant() )
  338. {
  339. // move my current model instance to the ragdoll's so decals are preserved.
  340. pPlayer->SnatchModelInstance( this );
  341. VarMapping_t *varMap = GetVarMapping();
  342. // Copy all the interpolated vars from the player entity.
  343. // The entity uses the interpolated history to get bone velocity.
  344. bool bRemotePlayer = (pPlayer != C_BasePlayer::GetLocalPlayer());
  345. if ( bRemotePlayer )
  346. {
  347. Interp_Copy( pPlayer );
  348. SetAbsAngles( pPlayer->GetRenderAngles() );
  349. GetRotationInterpolator().Reset();
  350. m_flAnimTime = pPlayer->m_flAnimTime;
  351. SetSequence( pPlayer->GetSequence() );
  352. m_flPlaybackRate = pPlayer->GetPlaybackRate();
  353. }
  354. else
  355. {
  356. // This is the local player, so set them in a default
  357. // pose and slam their velocity, angles and origin
  358. SetAbsOrigin( m_vecRagdollOrigin );
  359. SetAbsAngles( pPlayer->GetRenderAngles() );
  360. SetAbsVelocity( m_vecRagdollVelocity );
  361. int iSeq = LookupSequence( "walk_lower" );
  362. if ( iSeq == -1 )
  363. {
  364. Assert( false ); // missing walk_lower?
  365. iSeq = 0;
  366. }
  367. SetSequence( iSeq ); // walk_lower, basic pose
  368. SetCycle( 0.0 );
  369. // go ahead and set these on the player in case the code below decides to set up bones using
  370. // that entity instead of this one. The local player may not have valid animation
  371. pPlayer->SetSequence( iSeq ); // walk_lower, basic pose
  372. pPlayer->SetCycle( 0.0 );
  373. Interp_Reset( varMap );
  374. }
  375. }
  376. else
  377. {
  378. // overwrite network origin so later interpolation will
  379. // use this position
  380. SetNetworkOrigin( m_vecRagdollOrigin );
  381. SetAbsOrigin( m_vecRagdollOrigin );
  382. SetAbsVelocity( m_vecRagdollVelocity );
  383. Interp_Reset( GetVarMapping() );
  384. }
  385. // Turn it into a ragdoll.
  386. if ( cl_ragdoll_physics_enable.GetInt() )
  387. {
  388. // Make us a ragdoll..
  389. m_nRenderFX = kRenderFxRagdoll;
  390. matrix3x4_t boneDelta0[MAXSTUDIOBONES];
  391. matrix3x4_t boneDelta1[MAXSTUDIOBONES];
  392. matrix3x4_t currentBones[MAXSTUDIOBONES];
  393. const float boneDt = 0.05f;
  394. //=============================================================================
  395. // [pfreese], [tj]
  396. // There are visual problems with the attempted blending of the
  397. // death pose animations in C_CSRagdoll::GetRagdollInitBoneArrays. The version
  398. // in C_BasePlayer::GetRagdollInitBoneArrays doesn't attempt to blend death
  399. // poses, so if the player is relevant, use that one regardless of whether the
  400. // player is the local one or not.
  401. //=============================================================================
  402. if ( pPlayer && !pPlayer->IsDormant() )
  403. {
  404. pPlayer->GetRagdollInitBoneArrays( boneDelta0, boneDelta1, currentBones, boneDt );
  405. }
  406. else
  407. {
  408. GetRagdollInitBoneArrays( boneDelta0, boneDelta1, currentBones, boneDt );
  409. }
  410. InitAsClientRagdoll( boneDelta0, boneDelta1, currentBones, boneDt );
  411. m_flRagdollSinkStart = -1;
  412. }
  413. else
  414. {
  415. m_flRagdollSinkStart = gpGlobals->curtime;
  416. DestroyShadow();
  417. ClientLeafSystem()->SetRenderGroup( GetRenderHandle(), RENDER_GROUP_TRANSLUCENT_ENTITY );
  418. }
  419. m_bInitialized = true;
  420. }
  421. void C_CSRagdoll::ComputeFxBlend( void )
  422. {
  423. if ( m_flRagdollSinkStart == -1 )
  424. {
  425. BaseClass::ComputeFxBlend();
  426. }
  427. else
  428. {
  429. float elapsed = gpGlobals->curtime - m_flRagdollSinkStart;
  430. float flVal = RemapVal( elapsed, 0, g_flDieTranslucentTime, 255, 0 );
  431. flVal = clamp( flVal, 0, 255 );
  432. m_nRenderFXBlend = (int)flVal;
  433. #ifdef _DEBUG
  434. m_nFXComputeFrame = gpGlobals->framecount;
  435. #endif
  436. }
  437. }
  438. bool C_CSRagdoll::IsTransparent( void )
  439. {
  440. if ( m_flRagdollSinkStart == -1 )
  441. {
  442. return BaseClass::IsTransparent();
  443. }
  444. else
  445. {
  446. return true;
  447. }
  448. }
  449. void C_CSRagdoll::OnDataChanged( DataUpdateType_t type )
  450. {
  451. BaseClass::OnDataChanged( type );
  452. if ( type == DATA_UPDATE_CREATED )
  453. {
  454. // Prevent replays from creating ragdolls on the first frame of playback after skipping through playback.
  455. // If a player died (leaving a ragdoll) previous to the first frame of replay playback,
  456. // their ragdoll wasn't yet initialized because OnDataChanged events are queued but not processed
  457. // until the first render.
  458. if ( engine->IsPlayingDemo() && m_bCreatedWhilePlaybackSkipping )
  459. {
  460. Release();
  461. return;
  462. }
  463. if ( g_RagdollLVManager.IsLowViolence() )
  464. {
  465. CreateLowViolenceRagdoll();
  466. }
  467. else
  468. {
  469. CreateCSRagdoll();
  470. }
  471. }
  472. else
  473. {
  474. if ( !cl_ragdoll_physics_enable.GetInt() )
  475. {
  476. // Don't let it set us back to a ragdoll with data from the server.
  477. m_nRenderFX = kRenderFxNone;
  478. }
  479. }
  480. }
  481. IRagdoll* C_CSRagdoll::GetIRagdoll() const
  482. {
  483. return m_pRagdoll;
  484. }
  485. //-----------------------------------------------------------------------------
  486. // Purpose: Called when the player toggles nightvision
  487. // Input : *pData - the int value of the nightvision state
  488. // *pStruct - the player
  489. // *pOut -
  490. //-----------------------------------------------------------------------------
  491. void RecvProxy_NightVision( const CRecvProxyData *pData, void *pStruct, void *pOut )
  492. {
  493. C_CSPlayer *pPlayerData = (C_CSPlayer *) pStruct;
  494. bool bNightVisionOn = ( pData->m_Value.m_Int > 0 );
  495. if ( pPlayerData->m_bNightVisionOn != bNightVisionOn )
  496. {
  497. if ( bNightVisionOn )
  498. pPlayerData->m_flNightVisionAlpha = 1;
  499. }
  500. pPlayerData->m_bNightVisionOn = bNightVisionOn;
  501. }
  502. void RecvProxy_FlashTime( const CRecvProxyData *pData, void *pStruct, void *pOut )
  503. {
  504. C_CSPlayer *pPlayerData = (C_CSPlayer *) pStruct;
  505. if( pPlayerData != C_BasePlayer::GetLocalPlayer() )
  506. return;
  507. if ( (pPlayerData->m_flFlashDuration != pData->m_Value.m_Float) && pData->m_Value.m_Float > 0 )
  508. {
  509. pPlayerData->m_flFlashAlpha = 1;
  510. }
  511. pPlayerData->m_flFlashDuration = pData->m_Value.m_Float;
  512. pPlayerData->m_flFlashBangTime = gpGlobals->curtime + pPlayerData->m_flFlashDuration;
  513. }
  514. void RecvProxy_HasDefuser( const CRecvProxyData *pData, void *pStruct, void *pOut )
  515. {
  516. C_CSPlayer *pPlayerData = (C_CSPlayer *)pStruct;
  517. if (pPlayerData == NULL)
  518. {
  519. return;
  520. }
  521. bool drawIcon = false;
  522. if (pData->m_Value.m_Int == 0)
  523. {
  524. pPlayerData->RemoveDefuser();
  525. }
  526. else
  527. {
  528. if (pPlayerData->HasDefuser() == false)
  529. {
  530. drawIcon = true;
  531. }
  532. pPlayerData->GiveDefuser();
  533. }
  534. if (pPlayerData->IsLocalPlayer() && drawIcon)
  535. {
  536. // add to pickup history
  537. CHudHistoryResource *pHudHR = GET_HUDELEMENT( CHudHistoryResource );
  538. if ( pHudHR )
  539. {
  540. pHudHR->AddToHistory(HISTSLOT_ITEM, "defuser_pickup");
  541. }
  542. }
  543. }
  544. void C_CSPlayer::RecvProxy_CycleLatch( const CRecvProxyData *pData, void *pStruct, void *pOut )
  545. {
  546. // This receive proxy looks to see if the server's value is close enough to what we think it should
  547. // be. We've been running the same code; this is an error correction for changes we didn't simulate
  548. // while they were out of PVS.
  549. C_CSPlayer *pPlayer = (C_CSPlayer *)pStruct;
  550. if( pPlayer->IsLocalPlayer() )
  551. return; // Don't need to fixup ourselves.
  552. float incomingCycle = (float)(pData->m_Value.m_Int) / 16; // Came in as 4 bit fixed point
  553. float currentCycle = pPlayer->GetCycle();
  554. bool closeEnough = fabs(currentCycle - incomingCycle) < CycleLatchTolerance;
  555. if( fabs(currentCycle - incomingCycle) > (1 - CycleLatchTolerance) )
  556. {
  557. closeEnough = true;// Handle wrapping around 1->0
  558. }
  559. if( !closeEnough )
  560. {
  561. // Server disagrees too greatly. Correct our value.
  562. if ( pPlayer && pPlayer->GetTeam() )
  563. {
  564. DevMsg( 2, "%s %s(%d): Cycle latch wants to correct %.2f in to %.2f.\n",
  565. pPlayer->GetTeam()->Get_Name(), pPlayer->GetPlayerName(), pPlayer->entindex(), currentCycle, incomingCycle );
  566. }
  567. pPlayer->SetServerIntendedCycle( incomingCycle );
  568. }
  569. }
  570. void __MsgFunc_ReloadEffect( bf_read &msg )
  571. {
  572. int iPlayer = msg.ReadShort();
  573. C_CSPlayer *pPlayer = dynamic_cast< C_CSPlayer* >( C_BaseEntity::Instance( iPlayer ) );
  574. if ( pPlayer )
  575. pPlayer->PlayReloadEffect();
  576. }
  577. USER_MESSAGE_REGISTER( ReloadEffect );
  578. BEGIN_RECV_TABLE_NOBASE( C_CSPlayer, DT_CSLocalPlayerExclusive )
  579. RecvPropFloat( RECVINFO(m_flStamina) ),
  580. RecvPropInt( RECVINFO( m_iDirection ) ),
  581. RecvPropInt( RECVINFO( m_iShotsFired ) ),
  582. RecvPropFloat( RECVINFO( m_flVelocityModifier ) ),
  583. RecvPropVector( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ) ),
  584. //=============================================================================
  585. // HPE_BEGIN:
  586. // [tj]Set up the receive table for per-client domination data
  587. //=============================================================================
  588. RecvPropArray3( RECVINFO_ARRAY( m_bPlayerDominated ), RecvPropBool( RECVINFO( m_bPlayerDominated[0] ) ) ),
  589. RecvPropArray3( RECVINFO_ARRAY( m_bPlayerDominatingMe ), RecvPropBool( RECVINFO( m_bPlayerDominatingMe[0] ) ) )
  590. //=============================================================================
  591. // HPE_END
  592. //=============================================================================
  593. END_RECV_TABLE()
  594. BEGIN_RECV_TABLE_NOBASE( C_CSPlayer, DT_CSNonLocalPlayerExclusive )
  595. RecvPropVector( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ) ),
  596. END_RECV_TABLE()
  597. IMPLEMENT_CLIENTCLASS_DT( C_CSPlayer, DT_CSPlayer, CCSPlayer )
  598. RecvPropDataTable( "cslocaldata", 0, 0, &REFERENCE_RECV_TABLE(DT_CSLocalPlayerExclusive) ),
  599. RecvPropDataTable( "csnonlocaldata", 0, 0, &REFERENCE_RECV_TABLE(DT_CSNonLocalPlayerExclusive) ),
  600. RecvPropInt( RECVINFO( m_iAddonBits ) ),
  601. RecvPropInt( RECVINFO( m_iPrimaryAddon ) ),
  602. RecvPropInt( RECVINFO( m_iSecondaryAddon ) ),
  603. RecvPropInt( RECVINFO( m_iThrowGrenadeCounter ) ),
  604. RecvPropInt( RECVINFO( m_iPlayerState ) ),
  605. RecvPropInt( RECVINFO( m_iAccount ) ),
  606. RecvPropInt( RECVINFO( m_bInBombZone ) ),
  607. RecvPropInt( RECVINFO( m_bInBuyZone ) ),
  608. RecvPropInt( RECVINFO( m_iClass ) ),
  609. RecvPropInt( RECVINFO( m_ArmorValue ) ),
  610. RecvPropFloat( RECVINFO( m_angEyeAngles[0] ) ),
  611. RecvPropFloat( RECVINFO( m_angEyeAngles[1] ) ),
  612. RecvPropFloat( RECVINFO( m_flStamina ) ),
  613. RecvPropInt( RECVINFO( m_bHasDefuser ), 0, RecvProxy_HasDefuser ),
  614. RecvPropInt( RECVINFO( m_bNightVisionOn), 0, RecvProxy_NightVision ),
  615. RecvPropBool( RECVINFO( m_bHasNightVision ) ),
  616. //=============================================================================
  617. // HPE_BEGIN:
  618. // [dwenger] Added for fun-fact support
  619. //=============================================================================
  620. //RecvPropBool( RECVINFO( m_bPickedUpDefuser ) ),
  621. //RecvPropBool( RECVINFO( m_bDefusedWithPickedUpKit ) ),
  622. //=============================================================================
  623. // HPE_END
  624. //=============================================================================
  625. RecvPropBool( RECVINFO( m_bInHostageRescueZone ) ),
  626. RecvPropInt( RECVINFO( m_ArmorValue ) ),
  627. RecvPropBool( RECVINFO( m_bIsDefusing ) ),
  628. RecvPropBool( RECVINFO( m_bResumeZoom ) ),
  629. RecvPropInt( RECVINFO( m_iLastZoom ) ),
  630. #ifdef CS_SHIELD_ENABLED
  631. RecvPropBool( RECVINFO( m_bHasShield ) ),
  632. RecvPropBool( RECVINFO( m_bShieldDrawn ) ),
  633. #endif
  634. RecvPropInt( RECVINFO( m_bHasHelmet ) ),
  635. RecvPropVector( RECVINFO( m_vecRagdollVelocity ) ),
  636. RecvPropFloat( RECVINFO( m_flFlashDuration ), 0, RecvProxy_FlashTime ),
  637. RecvPropFloat( RECVINFO( m_flFlashMaxAlpha)),
  638. RecvPropInt( RECVINFO( m_iProgressBarDuration ) ),
  639. RecvPropFloat( RECVINFO( m_flProgressBarStartTime ) ),
  640. RecvPropEHandle( RECVINFO( m_hRagdoll ) ),
  641. RecvPropInt( RECVINFO( m_cycleLatch ), 0, &C_CSPlayer::RecvProxy_CycleLatch ),
  642. END_RECV_TABLE()
  643. C_CSPlayer::C_CSPlayer() :
  644. m_iv_angEyeAngles( "C_CSPlayer::m_iv_angEyeAngles" )
  645. {
  646. m_PlayerAnimState = CreatePlayerAnimState( this, this, LEGANIM_9WAY, true );
  647. m_angEyeAngles.Init();
  648. AddVar( &m_angEyeAngles, &m_iv_angEyeAngles, LATCH_SIMULATION_VAR );
  649. m_iLastAddonBits = m_iAddonBits = 0;
  650. m_iLastPrimaryAddon = m_iLastSecondaryAddon = WEAPON_NONE;
  651. m_iProgressBarDuration = 0;
  652. m_flProgressBarStartTime = 0.0f;
  653. m_ArmorValue = 0;
  654. m_bHasHelmet = false;
  655. m_iIDEntIndex = 0;
  656. m_delayTargetIDTimer.Reset();
  657. m_iOldIDEntIndex = 0;
  658. m_holdTargetIDTimer.Reset();
  659. m_iDirection = 0;
  660. m_Activity = ACT_IDLE;
  661. m_pFlashlightBeam = NULL;
  662. m_fNextThinkPushAway = 0.0f;
  663. m_serverIntendedCycle = -1.0f;
  664. view->SetScreenOverlayMaterial( NULL );
  665. m_bPlayingFreezeCamSound = false;
  666. }
  667. C_CSPlayer::~C_CSPlayer()
  668. {
  669. RemoveAddonModels();
  670. ReleaseFlashlight();
  671. m_PlayerAnimState->Release();
  672. }
  673. bool C_CSPlayer::HasDefuser() const
  674. {
  675. return m_bHasDefuser;
  676. }
  677. void C_CSPlayer::GiveDefuser()
  678. {
  679. m_bHasDefuser = true;
  680. }
  681. void C_CSPlayer::RemoveDefuser()
  682. {
  683. m_bHasDefuser = false;
  684. }
  685. bool C_CSPlayer::HasNightVision() const
  686. {
  687. return m_bHasNightVision;
  688. }
  689. bool C_CSPlayer::IsVIP() const
  690. {
  691. C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources();
  692. if ( !pCSPR )
  693. return false;
  694. return pCSPR->IsVIP( entindex() );
  695. }
  696. C_CSPlayer* C_CSPlayer::GetLocalCSPlayer()
  697. {
  698. return (C_CSPlayer*)C_BasePlayer::GetLocalPlayer();
  699. }
  700. CSPlayerState C_CSPlayer::State_Get() const
  701. {
  702. return m_iPlayerState;
  703. }
  704. float C_CSPlayer::GetMinFOV() const
  705. {
  706. // Min FOV for AWP.
  707. return 10;
  708. }
  709. int C_CSPlayer::GetAccount() const
  710. {
  711. return m_iAccount;
  712. }
  713. int C_CSPlayer::PlayerClass() const
  714. {
  715. return m_iClass;
  716. }
  717. bool C_CSPlayer::IsInBuyZone()
  718. {
  719. return m_bInBuyZone;
  720. }
  721. bool C_CSPlayer::CanShowTeamMenu() const
  722. {
  723. return true;
  724. }
  725. int C_CSPlayer::ArmorValue() const
  726. {
  727. return m_ArmorValue;
  728. }
  729. bool C_CSPlayer::HasHelmet() const
  730. {
  731. return m_bHasHelmet;
  732. }
  733. int C_CSPlayer::GetCurrentAssaultSuitPrice()
  734. {
  735. // WARNING: This price logic also exists in CCSPlayer::AttemptToBuyAssaultSuit
  736. // and must be kept in sync if changes are made.
  737. int fullArmor = ArmorValue() >= 100 ? 1 : 0;
  738. if ( fullArmor && !HasHelmet() )
  739. {
  740. return HELMET_PRICE;
  741. }
  742. else if ( !fullArmor && HasHelmet() )
  743. {
  744. return KEVLAR_PRICE;
  745. }
  746. else
  747. {
  748. // NOTE: This applies to the case where you already have both
  749. // as well as the case where you have neither. In the case
  750. // where you have both, the item should still have a price
  751. // and become disabled when you have little or no money left.
  752. return ASSAULTSUIT_PRICE;
  753. }
  754. }
  755. const QAngle& C_CSPlayer::GetRenderAngles()
  756. {
  757. if ( IsRagdoll() )
  758. {
  759. return vec3_angle;
  760. }
  761. else
  762. {
  763. return m_PlayerAnimState->GetRenderAngles();
  764. }
  765. }
  766. float g_flFattenAmt = 4;
  767. void C_CSPlayer::GetShadowRenderBounds( Vector &mins, Vector &maxs, ShadowType_t shadowType )
  768. {
  769. if ( shadowType == SHADOWS_SIMPLE )
  770. {
  771. // Don't let the render bounds change when we're using blobby shadows, or else the shadow
  772. // will pop and stretch.
  773. mins = CollisionProp()->OBBMins();
  774. maxs = CollisionProp()->OBBMaxs();
  775. }
  776. else
  777. {
  778. GetRenderBounds( mins, maxs );
  779. // We do this because the normal bbox calculations don't take pose params into account, and
  780. // the rotation of the guy's upper torso can place his gun a ways out of his bbox, and
  781. // the shadow will get cut off as he rotates.
  782. //
  783. // Thus, we give it some padding here.
  784. mins -= Vector( g_flFattenAmt, g_flFattenAmt, 0 );
  785. maxs += Vector( g_flFattenAmt, g_flFattenAmt, 0 );
  786. }
  787. }
  788. void C_CSPlayer::GetRenderBounds( Vector& theMins, Vector& theMaxs )
  789. {
  790. // TODO POSTSHIP - this hack/fix goes hand-in-hand with a fix in CalcSequenceBoundingBoxes in utils/studiomdl/simplify.cpp.
  791. // When we enable the fix in CalcSequenceBoundingBoxes, we can get rid of this.
  792. //
  793. // What we're doing right here is making sure it only uses the bbox for our lower-body sequences since,
  794. // with the current animations and the bug in CalcSequenceBoundingBoxes, are WAY bigger than they need to be.
  795. C_BaseAnimating::GetRenderBounds( theMins, theMaxs );
  796. // If we're ducking, we should reduce the render height by the difference in standing and ducking heights.
  797. // This prevents shadows from drawing above ducking players etc.
  798. if ( GetFlags() & FL_DUCKING )
  799. {
  800. theMaxs.z -= 18.5f;
  801. }
  802. }
  803. bool C_CSPlayer::GetShadowCastDirection( Vector *pDirection, ShadowType_t shadowType ) const
  804. {
  805. if ( shadowType == SHADOWS_SIMPLE )
  806. {
  807. // Blobby shadows should sit directly underneath us.
  808. pDirection->Init( 0, 0, -1 );
  809. return true;
  810. }
  811. else
  812. {
  813. return BaseClass::GetShadowCastDirection( pDirection, shadowType );
  814. }
  815. }
  816. void C_CSPlayer::VPhysicsUpdate( IPhysicsObject *pPhysics )
  817. {
  818. BaseClass::VPhysicsUpdate( pPhysics );
  819. }
  820. int C_CSPlayer::GetIDTarget() const
  821. {
  822. if ( !m_delayTargetIDTimer.IsElapsed() )
  823. return 0;
  824. if ( m_iIDEntIndex )
  825. {
  826. return m_iIDEntIndex;
  827. }
  828. if ( m_iOldIDEntIndex && !m_holdTargetIDTimer.IsElapsed() )
  829. {
  830. return m_iOldIDEntIndex;
  831. }
  832. return 0;
  833. }
  834. void InitializeAddonModelFromWeapon( CWeaponCSBase *weapon, C_BreakableProp *addon )
  835. {
  836. if ( !weapon )
  837. {
  838. return;
  839. }
  840. const CCSWeaponInfo& weaponInfo = weapon->GetCSWpnData();
  841. if ( weaponInfo.m_szAddonModel[0] == 0 )
  842. {
  843. addon->InitializeAsClientEntity( weaponInfo.szWorldModel, RENDER_GROUP_OPAQUE_ENTITY );
  844. }
  845. else
  846. {
  847. addon->InitializeAsClientEntity( weaponInfo.m_szAddonModel, RENDER_GROUP_OPAQUE_ENTITY );
  848. }
  849. }
  850. void C_CSPlayer::CreateAddonModel( int i )
  851. {
  852. COMPILE_TIME_ASSERT( (sizeof( g_AddonInfo ) / sizeof( g_AddonInfo[0] )) == NUM_ADDON_BITS );
  853. // Create the model entity.
  854. CAddonInfo *pAddonInfo = &g_AddonInfo[i];
  855. int iAttachment = LookupAttachment( pAddonInfo->m_pAttachmentName );
  856. if ( iAttachment <= 0 )
  857. return;
  858. C_BreakableProp *pEnt = new C_BreakableProp;
  859. int addonType = (1<<i);
  860. if ( addonType == ADDON_PISTOL || addonType == ADDON_PRIMARY )
  861. {
  862. CCSWeaponInfo *weaponInfo = GetWeaponInfo( (CSWeaponID)((addonType == ADDON_PRIMARY) ? m_iPrimaryAddon.Get() : m_iSecondaryAddon.Get()) );
  863. if ( !weaponInfo )
  864. {
  865. Warning( "C_CSPlayer::CreateAddonModel: Unable to get weapon info.\n" );
  866. pEnt->Release();
  867. return;
  868. }
  869. if ( weaponInfo->m_szAddonModel[0] == 0 )
  870. {
  871. pEnt->InitializeAsClientEntity( weaponInfo->szWorldModel, RENDER_GROUP_OPAQUE_ENTITY );
  872. }
  873. else
  874. {
  875. pEnt->InitializeAsClientEntity( weaponInfo->m_szAddonModel, RENDER_GROUP_OPAQUE_ENTITY );
  876. }
  877. }
  878. else if( pAddonInfo->m_pModelName )
  879. {
  880. if ( addonType == ADDON_PISTOL2 && !(m_iAddonBits & ADDON_PISTOL ) )
  881. {
  882. pEnt->InitializeAsClientEntity( pAddonInfo->m_pHolsterName, RENDER_GROUP_OPAQUE_ENTITY );
  883. }
  884. else
  885. {
  886. pEnt->InitializeAsClientEntity( pAddonInfo->m_pModelName, RENDER_GROUP_OPAQUE_ENTITY );
  887. }
  888. }
  889. else
  890. {
  891. WEAPON_FILE_INFO_HANDLE hWpnInfo = LookupWeaponInfoSlot( pAddonInfo->m_pWeaponClassName );
  892. if ( hWpnInfo == GetInvalidWeaponInfoHandle() )
  893. {
  894. Assert( false );
  895. return;
  896. }
  897. CCSWeaponInfo *pWeaponInfo = dynamic_cast< CCSWeaponInfo* >( GetFileWeaponInfoFromHandle( hWpnInfo ) );
  898. if ( pWeaponInfo )
  899. {
  900. if ( pWeaponInfo->m_szAddonModel[0] == 0 )
  901. pEnt->InitializeAsClientEntity( pWeaponInfo->szWorldModel, RENDER_GROUP_OPAQUE_ENTITY );
  902. else
  903. pEnt->InitializeAsClientEntity( pWeaponInfo->m_szAddonModel, RENDER_GROUP_OPAQUE_ENTITY );
  904. }
  905. else
  906. {
  907. pEnt->Release();
  908. Warning( "C_CSPlayer::CreateAddonModel: Unable to get weapon info for %s.\n", pAddonInfo->m_pWeaponClassName );
  909. return;
  910. }
  911. }
  912. if ( Q_strcmp( pAddonInfo->m_pAttachmentName, "c4" ) )
  913. {
  914. // fade out all attached models except C4
  915. pEnt->SetFadeMinMax( 400, 500 );
  916. }
  917. // Create the addon.
  918. CAddonModel *pAddon = &m_AddonModels[m_AddonModels.AddToTail()];
  919. pAddon->m_hEnt = pEnt;
  920. pAddon->m_iAddon = i;
  921. pAddon->m_iAttachmentPoint = iAttachment;
  922. pEnt->SetParent( this, pAddon->m_iAttachmentPoint );
  923. pEnt->SetLocalOrigin( Vector( 0, 0, 0 ) );
  924. pEnt->SetLocalAngles( QAngle( 0, 0, 0 ) );
  925. if ( IsLocalPlayer() )
  926. {
  927. pEnt->SetSolid( SOLID_NONE );
  928. pEnt->RemoveEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID );
  929. }
  930. }
  931. void C_CSPlayer::UpdateAddonModels()
  932. {
  933. int iCurAddonBits = m_iAddonBits;
  934. // Don't put addon models on the local player unless in third person.
  935. if ( IsLocalPlayer() && !C_BasePlayer::ShouldDrawLocalPlayer() )
  936. iCurAddonBits = 0;
  937. // If the local player is observing this entity in first-person mode, get rid of its addons.
  938. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  939. if ( pPlayer && pPlayer->GetObserverMode() == OBS_MODE_IN_EYE && pPlayer->GetObserverTarget() == this )
  940. iCurAddonBits = 0;
  941. // Any changes to the attachments we should have?
  942. if ( m_iLastAddonBits == iCurAddonBits &&
  943. m_iLastPrimaryAddon == m_iPrimaryAddon &&
  944. m_iLastSecondaryAddon == m_iSecondaryAddon )
  945. {
  946. return;
  947. }
  948. bool rebuildPistol2Addon = false;
  949. if ( m_iSecondaryAddon == WEAPON_ELITE && ((m_iLastAddonBits ^ iCurAddonBits) & ADDON_PISTOL) != 0 )
  950. {
  951. rebuildPistol2Addon = true;
  952. }
  953. m_iLastAddonBits = iCurAddonBits;
  954. m_iLastPrimaryAddon = m_iPrimaryAddon;
  955. m_iLastSecondaryAddon = m_iSecondaryAddon;
  956. // Get rid of any old models.
  957. int i,iNext;
  958. for ( i=m_AddonModels.Head(); i != m_AddonModels.InvalidIndex(); i = iNext )
  959. {
  960. iNext = m_AddonModels.Next( i );
  961. CAddonModel *pModel = &m_AddonModels[i];
  962. int addonBit = 1<<pModel->m_iAddon;
  963. if ( !( iCurAddonBits & addonBit ) || (rebuildPistol2Addon && addonBit == ADDON_PISTOL2) )
  964. {
  965. if ( pModel->m_hEnt.Get() )
  966. pModel->m_hEnt->Release();
  967. m_AddonModels.Remove( i );
  968. }
  969. }
  970. // Figure out which models we have now.
  971. int curModelBits = 0;
  972. FOR_EACH_LL( m_AddonModels, j )
  973. {
  974. curModelBits |= (1<<m_AddonModels[j].m_iAddon);
  975. }
  976. // Add any new models.
  977. for ( i=0; i < NUM_ADDON_BITS; i++ )
  978. {
  979. if ( (iCurAddonBits & (1<<i)) && !( curModelBits & (1<<i) ) )
  980. {
  981. // Ok, we're supposed to have this one.
  982. CreateAddonModel( i );
  983. }
  984. }
  985. }
  986. void C_CSPlayer::RemoveAddonModels()
  987. {
  988. m_iAddonBits = 0;
  989. UpdateAddonModels();
  990. }
  991. void C_CSPlayer::NotifyShouldTransmit( ShouldTransmitState_t state )
  992. {
  993. // Remove all addon models if we go out of the PVS.
  994. if ( state == SHOULDTRANSMIT_END )
  995. {
  996. RemoveAddonModels();
  997. if( m_pFlashlightBeam != NULL )
  998. {
  999. ReleaseFlashlight();
  1000. }
  1001. }
  1002. BaseClass::NotifyShouldTransmit( state );
  1003. }
  1004. void C_CSPlayer::UpdateSoundEvents()
  1005. {
  1006. int iNext;
  1007. for ( int i=m_SoundEvents.Head(); i != m_SoundEvents.InvalidIndex(); i = iNext )
  1008. {
  1009. iNext = m_SoundEvents.Next( i );
  1010. CCSSoundEvent *pEvent = &m_SoundEvents[i];
  1011. if ( gpGlobals->curtime >= pEvent->m_flEventTime )
  1012. {
  1013. CLocalPlayerFilter filter;
  1014. EmitSound( filter, GetSoundSourceIndex(), STRING( pEvent->m_SoundName ) );
  1015. m_SoundEvents.Remove( i );
  1016. }
  1017. }
  1018. }
  1019. //-----------------------------------------------------------------------------
  1020. void C_CSPlayer::UpdateMinModels( void )
  1021. {
  1022. int modelIndex = m_nModelIndex;
  1023. // cl_minmodels convar dependent on sv_allowminmodels convar
  1024. if ( !IsVIP() && sv_allowminmodels.GetBool() && cl_minmodels.GetBool() && !IsLocalPlayer() )
  1025. {
  1026. if ( GetTeamNumber() == TEAM_CT )
  1027. {
  1028. int index = cl_min_ct.GetInt() - 1;
  1029. if ( index >= 0 && index < CTPlayerModels.Count() )
  1030. {
  1031. modelIndex = modelinfo->GetModelIndex( CTPlayerModels[index] );
  1032. }
  1033. }
  1034. else if ( GetTeamNumber() == TEAM_TERRORIST )
  1035. {
  1036. int index = cl_min_t.GetInt() - 1;
  1037. if ( index >= 0 && index < TerroristPlayerModels.Count() )
  1038. {
  1039. modelIndex = modelinfo->GetModelIndex( TerroristPlayerModels[index] );
  1040. }
  1041. }
  1042. }
  1043. SetModelByIndex( modelIndex );
  1044. }
  1045. // NVNT gate for spectating.
  1046. static bool inSpectating_Haptics = false;
  1047. //-----------------------------------------------------------------------------
  1048. void C_CSPlayer::ClientThink()
  1049. {
  1050. BaseClass::ClientThink();
  1051. UpdateSoundEvents();
  1052. UpdateAddonModels();
  1053. UpdateIDTarget();
  1054. if ( gpGlobals->curtime >= m_fNextThinkPushAway )
  1055. {
  1056. PerformObstaclePushaway( this );
  1057. m_fNextThinkPushAway = gpGlobals->curtime + PUSHAWAY_THINK_INTERVAL;
  1058. }
  1059. // NVNT - check for spectating forces
  1060. if ( IsLocalPlayer() )
  1061. {
  1062. if ( GetTeamNumber() == TEAM_SPECTATOR || !this->IsAlive() || GetLocalOrInEyeCSPlayer() != this )
  1063. {
  1064. if (!inSpectating_Haptics)
  1065. {
  1066. if ( haptics )
  1067. haptics->SetNavigationClass("spectate");
  1068. inSpectating_Haptics = true;
  1069. }
  1070. }
  1071. else
  1072. {
  1073. if (inSpectating_Haptics)
  1074. {
  1075. if ( haptics )
  1076. haptics->SetNavigationClass("on_foot");
  1077. inSpectating_Haptics = false;
  1078. }
  1079. }
  1080. if ( m_iObserverMode == OBS_MODE_FREEZECAM )
  1081. {
  1082. //=============================================================================
  1083. // HPE_BEGIN:
  1084. // [Forrest] Added sv_disablefreezecam check
  1085. //=============================================================================
  1086. static ConVarRef sv_disablefreezecam( "sv_disablefreezecam" );
  1087. if ( !m_bPlayingFreezeCamSound && !cl_disablefreezecam.GetBool() && !sv_disablefreezecam.GetBool() )
  1088. //=============================================================================
  1089. // HPE_END
  1090. //=============================================================================
  1091. {
  1092. // Play sound
  1093. m_bPlayingFreezeCamSound = true;
  1094. CLocalPlayerFilter filter;
  1095. EmitSound_t ep;
  1096. ep.m_nChannel = CHAN_VOICE;
  1097. ep.m_pSoundName = "UI/freeze_cam.wav";
  1098. ep.m_flVolume = VOL_NORM;
  1099. ep.m_SoundLevel = SNDLVL_NORM;
  1100. ep.m_bEmitCloseCaption = false;
  1101. EmitSound( filter, GetSoundSourceIndex(), ep );
  1102. }
  1103. }
  1104. else
  1105. {
  1106. m_bPlayingFreezeCamSound = false;
  1107. }
  1108. }
  1109. }
  1110. void C_CSPlayer::OnDataChanged( DataUpdateType_t type )
  1111. {
  1112. BaseClass::OnDataChanged( type );
  1113. if ( type == DATA_UPDATE_CREATED )
  1114. {
  1115. SetNextClientThink( CLIENT_THINK_ALWAYS );
  1116. if ( IsLocalPlayer() )
  1117. {
  1118. if ( CSGameRules() && CSGameRules()->IsBlackMarket() )
  1119. {
  1120. CSGameRules()->m_pPrices = NULL;
  1121. CSGameRules()->m_StringTableBlackMarket = NULL;
  1122. CSGameRules()->GetBlackMarketPriceList();
  1123. CSGameRules()->SetBlackMarketPrices( false );
  1124. }
  1125. }
  1126. }
  1127. UpdateVisibility();
  1128. }
  1129. void C_CSPlayer::ValidateModelIndex( void )
  1130. {
  1131. UpdateMinModels();
  1132. }
  1133. void C_CSPlayer::PostDataUpdate( DataUpdateType_t updateType )
  1134. {
  1135. // C_BaseEntity assumes we're networking the entity's angles, so pretend that it
  1136. // networked the same value we already have.
  1137. SetNetworkAngles( GetLocalAngles() );
  1138. BaseClass::PostDataUpdate( updateType );
  1139. }
  1140. //-----------------------------------------------------------------------------
  1141. // Purpose:
  1142. // Output : Returns true on success, false on failure.
  1143. //-----------------------------------------------------------------------------
  1144. bool C_CSPlayer::Interpolate( float currentTime )
  1145. {
  1146. if ( !BaseClass::Interpolate( currentTime ) )
  1147. return false;
  1148. if ( CSGameRules()->IsFreezePeriod() )
  1149. {
  1150. // don't interpolate players position during freeze period
  1151. SetAbsOrigin( GetNetworkOrigin() );
  1152. }
  1153. return true;
  1154. }
  1155. int C_CSPlayer::GetMaxHealth() const
  1156. {
  1157. return 100;
  1158. }
  1159. //-----------------------------------------------------------------------------
  1160. // Purpose: Return the local player, or the player being spectated in-eye
  1161. //-----------------------------------------------------------------------------
  1162. C_CSPlayer* GetLocalOrInEyeCSPlayer( void )
  1163. {
  1164. C_CSPlayer *player = C_CSPlayer::GetLocalCSPlayer();
  1165. if( player && player->GetObserverMode() == OBS_MODE_IN_EYE )
  1166. {
  1167. C_BaseEntity *target = player->GetObserverTarget();
  1168. if( target && target->IsPlayer() )
  1169. {
  1170. return ToCSPlayer( target );
  1171. }
  1172. }
  1173. return player;
  1174. }
  1175. #define MAX_FLASHBANG_OPACITY 75.0f
  1176. //-----------------------------------------------------------------------------
  1177. // Purpose: Update this client's targetid entity
  1178. //-----------------------------------------------------------------------------
  1179. void C_CSPlayer::UpdateIDTarget()
  1180. {
  1181. if ( !IsLocalPlayer() )
  1182. return;
  1183. // Clear old target and find a new one
  1184. m_iIDEntIndex = 0;
  1185. // don't show IDs if mp_playerid == 2
  1186. if ( mp_playerid.GetInt() == 2 )
  1187. return;
  1188. // don't show IDs if mp_fadetoblack is on
  1189. if ( mp_fadetoblack.GetBool() && !IsAlive() )
  1190. return;
  1191. // don't show IDs in chase spec mode
  1192. if ( GetObserverMode() == OBS_MODE_CHASE ||
  1193. GetObserverMode() == OBS_MODE_DEATHCAM )
  1194. return;
  1195. //Check how much of a screen fade we have.
  1196. //if it's more than 75 then we can't see what's going on so we don't display the id.
  1197. byte color[4];
  1198. bool blend;
  1199. vieweffects->GetFadeParams( &color[0], &color[1], &color[2], &color[3], &blend );
  1200. if ( color[3] > MAX_FLASHBANG_OPACITY && ( IsAlive() || GetObserverMode() == OBS_MODE_IN_EYE ) )
  1201. return;
  1202. trace_t tr;
  1203. Vector vecStart, vecEnd;
  1204. VectorMA( MainViewOrigin(), 2500, MainViewForward(), vecEnd );
  1205. VectorMA( MainViewOrigin(), 10, MainViewForward(), vecStart );
  1206. UTIL_TraceLine( vecStart, vecEnd, MASK_VISIBLE_AND_NPCS, GetLocalOrInEyeCSPlayer(), COLLISION_GROUP_NONE, &tr );
  1207. if ( !tr.startsolid && !tr.DidHitNonWorldEntity() )
  1208. {
  1209. CTraceFilterSimple filter( GetLocalOrInEyeCSPlayer(), COLLISION_GROUP_NONE );
  1210. // Check for player hitboxes extending outside their collision bounds
  1211. const float rayExtension = 40.0f;
  1212. UTIL_ClipTraceToPlayers(vecStart, vecEnd + MainViewForward() * rayExtension, MASK_SOLID|CONTENTS_HITBOX, &filter, &tr );
  1213. }
  1214. if ( !tr.startsolid && tr.DidHitNonWorldEntity() )
  1215. {
  1216. C_BaseEntity *pEntity = tr.m_pEnt;
  1217. if ( pEntity && (pEntity != this) )
  1218. {
  1219. if ( mp_playerid.GetInt() == 1 ) // only show team names
  1220. {
  1221. if ( pEntity->GetTeamNumber() != GetTeamNumber() )
  1222. {
  1223. return;
  1224. }
  1225. }
  1226. //Adrian: If there's a smoke cloud in my way, don't display the name
  1227. //We check this AFTER we found a player, just so we don't go thru this for nothing.
  1228. for ( int i = 0; i < m_SmokeGrenades.Count(); i++ )
  1229. {
  1230. C_BaseParticleEntity *pSmokeGrenade = (C_BaseParticleEntity*)m_SmokeGrenades.Element( i );
  1231. if ( pSmokeGrenade )
  1232. {
  1233. float flHit1, flHit2;
  1234. float flRadius = ( SMOKEGRENADE_PARTICLERADIUS * NUM_PARTICLES_PER_DIMENSION + 1 ) * 0.5f;
  1235. Vector vPos = pSmokeGrenade->GetAbsOrigin();
  1236. /*debugoverlay->AddBoxOverlay( pSmokeGrenade->GetAbsOrigin(), Vector( flRadius, flRadius, flRadius ),
  1237. Vector( -flRadius, -flRadius, -flRadius ), QAngle( 0, 0, 0 ), 255, 0, 0, 255, 0.2 );*/
  1238. if ( IntersectInfiniteRayWithSphere( MainViewOrigin(), MainViewForward(), vPos, flRadius, &flHit1, &flHit2 ) )
  1239. {
  1240. return;
  1241. }
  1242. }
  1243. }
  1244. if ( !GetIDTarget() && ( !m_iOldIDEntIndex || m_holdTargetIDTimer.IsElapsed() ) )
  1245. {
  1246. // track when we first mouse over the target
  1247. m_delayTargetIDTimer.Start( mp_playerid_delay.GetFloat() );
  1248. }
  1249. m_iIDEntIndex = pEntity->entindex();
  1250. m_iOldIDEntIndex = m_iIDEntIndex;
  1251. m_holdTargetIDTimer.Start( mp_playerid_hold.GetFloat() );
  1252. }
  1253. }
  1254. }
  1255. //-----------------------------------------------------------------------------
  1256. // Purpose: Input handling
  1257. //-----------------------------------------------------------------------------
  1258. bool C_CSPlayer::CreateMove( float flInputSampleTime, CUserCmd *pCmd )
  1259. {
  1260. // Bleh... we will wind up needing to access bones for attachments in here.
  1261. C_BaseAnimating::AutoAllowBoneAccess boneaccess( true, true );
  1262. return BaseClass::CreateMove( flInputSampleTime, pCmd );
  1263. }
  1264. //-----------------------------------------------------------------------------
  1265. // Purpose: Flash this entity on the radar
  1266. //-----------------------------------------------------------------------------
  1267. bool C_CSPlayer::IsInHostageRescueZone()
  1268. {
  1269. return m_bInHostageRescueZone;
  1270. }
  1271. CWeaponCSBase* C_CSPlayer::GetActiveCSWeapon() const
  1272. {
  1273. return dynamic_cast< CWeaponCSBase* >( GetActiveWeapon() );
  1274. }
  1275. CWeaponCSBase* C_CSPlayer::GetCSWeapon( CSWeaponID id ) const
  1276. {
  1277. for (int i=0;i<MAX_WEAPONS;i++)
  1278. {
  1279. CBaseCombatWeapon *weapon = GetWeapon( i );
  1280. if ( weapon )
  1281. {
  1282. CWeaponCSBase *csWeapon = dynamic_cast< CWeaponCSBase * >( weapon );
  1283. if ( csWeapon )
  1284. {
  1285. if ( id == csWeapon->GetWeaponID() )
  1286. {
  1287. return csWeapon;
  1288. }
  1289. }
  1290. }
  1291. }
  1292. return NULL;
  1293. }
  1294. //REMOVEME
  1295. /*
  1296. void C_CSPlayer::SetFireAnimation( PLAYER_ANIM playerAnim )
  1297. {
  1298. Activity idealActivity = ACT_WALK;
  1299. // Figure out stuff about the current state.
  1300. float speed = GetAbsVelocity().Length2D();
  1301. bool isMoving = ( speed != 0.0f ) ? true : false;
  1302. bool isDucked = ( GetFlags() & FL_DUCKING ) ? true : false;
  1303. bool isStillJumping = false; //!( GetFlags() & FL_ONGROUND );
  1304. bool isRunning = false;
  1305. if ( speed > ARBITRARY_RUN_SPEED )
  1306. {
  1307. isRunning = true;
  1308. }
  1309. // Now figure out what to do based on the current state and the new state.
  1310. switch ( playerAnim )
  1311. {
  1312. default:
  1313. case PLAYER_RELOAD:
  1314. case PLAYER_ATTACK1:
  1315. case PLAYER_IDLE:
  1316. case PLAYER_WALK:
  1317. // Are we still jumping?
  1318. // If so, keep playing the jump animation.
  1319. if ( !isStillJumping )
  1320. {
  1321. idealActivity = ACT_WALK;
  1322. if ( isDucked )
  1323. {
  1324. idealActivity = !isMoving ? ACT_CROUCHIDLE : ACT_RUN_CROUCH;
  1325. }
  1326. else
  1327. {
  1328. if ( isRunning )
  1329. {
  1330. idealActivity = ACT_RUN;
  1331. }
  1332. else
  1333. {
  1334. idealActivity = isMoving ? ACT_WALK : ACT_IDLE;
  1335. }
  1336. }
  1337. // Allow body yaw to override for standing and turning in place
  1338. idealActivity = m_PlayerAnimState.BodyYawTranslateActivity( idealActivity );
  1339. }
  1340. break;
  1341. case PLAYER_JUMP:
  1342. idealActivity = ACT_HOP;
  1343. break;
  1344. case PLAYER_DIE:
  1345. // Uses Ragdoll now???
  1346. idealActivity = ACT_DIESIMPLE;
  1347. break;
  1348. // FIXME: Use overlays for reload, start/leave aiming, attacking
  1349. case PLAYER_START_AIMING:
  1350. case PLAYER_LEAVE_AIMING:
  1351. idealActivity = ACT_WALK;
  1352. break;
  1353. }
  1354. CWeaponCSBase *pWeapon = GetActiveCSWeapon();
  1355. if ( pWeapon )
  1356. {
  1357. Activity aWeaponActivity = idealActivity;
  1358. if ( playerAnim == PLAYER_ATTACK1 )
  1359. {
  1360. switch ( idealActivity )
  1361. {
  1362. case ACT_WALK:
  1363. default:
  1364. aWeaponActivity = ACT_PLAYER_WALK_FIRE;
  1365. break;
  1366. case ACT_RUN:
  1367. aWeaponActivity = ACT_PLAYER_RUN_FIRE;
  1368. break;
  1369. case ACT_IDLE:
  1370. aWeaponActivity = ACT_PLAYER_IDLE_FIRE;
  1371. break;
  1372. case ACT_CROUCHIDLE:
  1373. aWeaponActivity = ACT_PLAYER_CROUCH_FIRE;
  1374. break;
  1375. case ACT_RUN_CROUCH:
  1376. aWeaponActivity = ACT_PLAYER_CROUCH_WALK_FIRE;
  1377. break;
  1378. }
  1379. }
  1380. m_PlayerAnimState.SetWeaponLayerSequence( pWeapon->GetCSWpnData().m_szAnimExtension, aWeaponActivity );
  1381. }
  1382. }
  1383. */
  1384. ShadowType_t C_CSPlayer::ShadowCastType( void )
  1385. {
  1386. if ( !IsVisible() )
  1387. return SHADOWS_NONE;
  1388. return SHADOWS_RENDER_TO_TEXTURE_DYNAMIC;
  1389. }
  1390. //-----------------------------------------------------------------------------
  1391. // Purpose: Returns whether or not we can switch to the given weapon.
  1392. // Input : pWeapon -
  1393. //-----------------------------------------------------------------------------
  1394. bool C_CSPlayer::Weapon_CanSwitchTo( CBaseCombatWeapon *pWeapon )
  1395. {
  1396. if ( !pWeapon->CanDeploy() )
  1397. return false;
  1398. if ( GetActiveWeapon() )
  1399. {
  1400. if ( !GetActiveWeapon()->CanHolster() )
  1401. return false;
  1402. }
  1403. return true;
  1404. }
  1405. void C_CSPlayer::UpdateClientSideAnimation()
  1406. {
  1407. // We do this in a different order than the base class.
  1408. // We need our cycle to be valid for when we call the playeranimstate update code,
  1409. // or else it'll synchronize the upper body anims with the wrong cycle.
  1410. if ( GetSequence() != -1 )
  1411. {
  1412. // move frame forward
  1413. FrameAdvance( 0.0f ); // 0 means to use the time we last advanced instead of a constant
  1414. }
  1415. // Update the animation data. It does the local check here so this works when using
  1416. // a third-person camera (and we don't have valid player angles).
  1417. if ( this == C_CSPlayer::GetLocalCSPlayer() )
  1418. m_PlayerAnimState->Update( EyeAngles()[YAW], m_angEyeAngles[PITCH] );
  1419. else
  1420. m_PlayerAnimState->Update( m_angEyeAngles[YAW], m_angEyeAngles[PITCH] );
  1421. if ( GetSequence() != -1 )
  1422. {
  1423. // latch old values
  1424. OnLatchInterpolatedVariables( LATCH_ANIMATION_VAR );
  1425. }
  1426. }
  1427. float g_flMuzzleFlashScale=1;
  1428. void C_CSPlayer::ProcessMuzzleFlashEvent()
  1429. {
  1430. CBasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
  1431. // Reenable when the weapons have muzzle flash attachments in the right spot.
  1432. if ( this == pLocalPlayer )
  1433. return; // don't show own world muzzle flashs in for localplayer
  1434. if ( pLocalPlayer && pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE )
  1435. {
  1436. // also don't show in 1st person spec mode
  1437. if ( pLocalPlayer->GetObserverTarget() == this )
  1438. return;
  1439. }
  1440. CWeaponCSBase *pWeapon = GetActiveCSWeapon();
  1441. if ( !pWeapon )
  1442. return;
  1443. bool hasMuzzleFlash = (pWeapon->GetMuzzleFlashStyle() != CS_MUZZLEFLASH_NONE);
  1444. Vector vector;
  1445. QAngle angles;
  1446. int iAttachment = LookupAttachment( "muzzle_flash" );
  1447. if ( iAttachment >= 0 )
  1448. {
  1449. bool bFoundAttachment = GetAttachment( iAttachment, vector, angles );
  1450. // If we have an attachment, then stick a light on it.
  1451. if ( bFoundAttachment )
  1452. {
  1453. if ( hasMuzzleFlash )
  1454. {
  1455. dlight_t *el = effects->CL_AllocDlight( LIGHT_INDEX_MUZZLEFLASH + index );
  1456. el->origin = vector;
  1457. el->radius = 70;
  1458. el->decay = el->radius / 0.05f;
  1459. el->die = gpGlobals->curtime + 0.05f;
  1460. el->color.r = 255;
  1461. el->color.g = 192;
  1462. el->color.b = 64;
  1463. el->color.exponent = 5;
  1464. }
  1465. int shellType = GetShellForAmmoType( pWeapon->GetCSWpnData().szAmmo1 );
  1466. QAngle playerAngle = EyeAngles();
  1467. Vector vForward, vRight, vUp;
  1468. AngleVectors( playerAngle, &vForward, &vRight, &vUp );
  1469. QAngle angVelocity;
  1470. Vector vVel = vRight * 100 + vUp * 20;
  1471. VectorAngles( vVel, angVelocity );
  1472. if ( pWeapon->GetMaxClip1() > 0 )
  1473. {
  1474. tempents->CSEjectBrass( vector, angVelocity, 120, shellType, this );
  1475. }
  1476. }
  1477. }
  1478. if ( hasMuzzleFlash )
  1479. {
  1480. iAttachment = pWeapon->GetMuzzleAttachment();
  1481. if ( iAttachment > 0 )
  1482. {
  1483. float flScale = pWeapon->GetCSWpnData().m_flMuzzleScale;
  1484. flScale *= 0.75;
  1485. FX_MuzzleEffectAttached( flScale, pWeapon->GetRefEHandle(), iAttachment, NULL, false );
  1486. }
  1487. }
  1488. }
  1489. const QAngle& C_CSPlayer::EyeAngles()
  1490. {
  1491. if ( IsLocalPlayer() && !g_nKillCamMode )
  1492. {
  1493. return BaseClass::EyeAngles();
  1494. }
  1495. else
  1496. {
  1497. return m_angEyeAngles;
  1498. }
  1499. }
  1500. bool C_CSPlayer::ShouldDraw( void )
  1501. {
  1502. // If we're dead, our ragdoll will be drawn for us instead.
  1503. if ( !IsAlive() )
  1504. return false;
  1505. if( GetTeamNumber() == TEAM_SPECTATOR )
  1506. return false;
  1507. if( IsLocalPlayer() )
  1508. {
  1509. if ( IsRagdoll() )
  1510. return true;
  1511. }
  1512. return BaseClass::ShouldDraw();
  1513. }
  1514. bool FindWeaponAttachmentBone( C_BaseCombatWeapon *pWeapon, int &iWeaponBone )
  1515. {
  1516. if ( !pWeapon )
  1517. return false;
  1518. CStudioHdr *pHdr = pWeapon->GetModelPtr();
  1519. if ( !pHdr )
  1520. return false;
  1521. for ( iWeaponBone=0; iWeaponBone < pHdr->numbones(); iWeaponBone++ )
  1522. {
  1523. if ( stricmp( pHdr->pBone( iWeaponBone )->pszName(), "L_Hand_Attach" ) == 0 )
  1524. break;
  1525. }
  1526. return iWeaponBone != pHdr->numbones();
  1527. }
  1528. bool FindMyAttachmentBone( C_BaseAnimating *pModel, int &iBone, CStudioHdr *pHdr )
  1529. {
  1530. if ( !pHdr )
  1531. return false;
  1532. for ( iBone=0; iBone < pHdr->numbones(); iBone++ )
  1533. {
  1534. if ( stricmp( pHdr->pBone( iBone )->pszName(), "Valvebiped.Bip01_L_Hand" ) == 0 )
  1535. break;
  1536. }
  1537. return iBone != pHdr->numbones();
  1538. }
  1539. inline bool IsBoneChildOf( CStudioHdr *pHdr, int iBone, int iParent )
  1540. {
  1541. if ( iBone == iParent )
  1542. return false;
  1543. while ( iBone != -1 )
  1544. {
  1545. if ( iBone == iParent )
  1546. return true;
  1547. iBone = pHdr->pBone( iBone )->parent;
  1548. }
  1549. return false;
  1550. }
  1551. void ApplyDifferenceTransformToChildren(
  1552. C_BaseAnimating *pModel,
  1553. const matrix3x4_t &mSource,
  1554. const matrix3x4_t &mDest,
  1555. int iParentBone )
  1556. {
  1557. CStudioHdr *pHdr = pModel->GetModelPtr();
  1558. if ( !pHdr )
  1559. return;
  1560. // Build a matrix to go from mOriginalHand to mHand.
  1561. // ( mDest * Inverse( mSource ) ) * mSource = mDest
  1562. matrix3x4_t mSourceInverse, mToDest;
  1563. MatrixInvert( mSource, mSourceInverse );
  1564. ConcatTransforms( mDest, mSourceInverse, mToDest );
  1565. // Now multiply iMyBone and all its children by mToWeaponBone.
  1566. for ( int i=0; i < pHdr->numbones(); i++ )
  1567. {
  1568. if ( IsBoneChildOf( pHdr, i, iParentBone ) )
  1569. {
  1570. matrix3x4_t &mCur = pModel->GetBoneForWrite( i );
  1571. matrix3x4_t mNew;
  1572. ConcatTransforms( mToDest, mCur, mNew );
  1573. mCur = mNew;
  1574. }
  1575. }
  1576. }
  1577. void GetCorrectionMatrices(
  1578. const matrix3x4_t &mShoulder,
  1579. const matrix3x4_t &mElbow,
  1580. const matrix3x4_t &mHand,
  1581. matrix3x4_t &mShoulderCorrection,
  1582. matrix3x4_t &mElbowCorrection
  1583. )
  1584. {
  1585. extern void Studio_AlignIKMatrix( matrix3x4_t &mMat, const Vector &vAlignTo );
  1586. // Get the positions of each node so we can get the direction vectors.
  1587. Vector vShoulder, vElbow, vHand;
  1588. MatrixPosition( mShoulder, vShoulder );
  1589. MatrixPosition( mElbow, vElbow );
  1590. MatrixPosition( mHand, vHand );
  1591. // Get rid of the translation.
  1592. matrix3x4_t mOriginalShoulder = mShoulder;
  1593. matrix3x4_t mOriginalElbow = mElbow;
  1594. MatrixSetColumn( Vector( 0, 0, 0 ), 3, mOriginalShoulder );
  1595. MatrixSetColumn( Vector( 0, 0, 0 ), 3, mOriginalElbow );
  1596. // Let the IK code align them like it would if we did IK on the joint.
  1597. matrix3x4_t mAlignedShoulder = mOriginalShoulder;
  1598. matrix3x4_t mAlignedElbow = mOriginalElbow;
  1599. Studio_AlignIKMatrix( mAlignedShoulder, vElbow-vShoulder );
  1600. Studio_AlignIKMatrix( mAlignedElbow, vHand-vElbow );
  1601. // Figure out the transformation from the aligned bones to the original ones.
  1602. matrix3x4_t mInvAlignedShoulder, mInvAlignedElbow;
  1603. MatrixInvert( mAlignedShoulder, mInvAlignedShoulder );
  1604. MatrixInvert( mAlignedElbow, mInvAlignedElbow );
  1605. ConcatTransforms( mInvAlignedShoulder, mOriginalShoulder, mShoulderCorrection );
  1606. ConcatTransforms( mInvAlignedElbow, mOriginalElbow, mElbowCorrection );
  1607. }
  1608. void C_CSPlayer::BuildTransformations( CStudioHdr *pHdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed )
  1609. {
  1610. // First, setup our model's transformations like normal.
  1611. BaseClass::BuildTransformations( pHdr, pos, q, cameraTransform, boneMask, boneComputed );
  1612. if ( IsLocalPlayer() && !C_BasePlayer::ShouldDrawLocalPlayer() )
  1613. return;
  1614. if ( !cl_left_hand_ik.GetInt() )
  1615. return;
  1616. // If our current weapon has a bone named L_Hand_Attach, then we attach the player's
  1617. // left hand (Valvebiped.Bip01_L_Hand) to it.
  1618. C_BaseCombatWeapon *pWeapon = GetActiveWeapon();
  1619. if ( !pWeapon )
  1620. return;
  1621. // Have the weapon setup its bones.
  1622. pWeapon->SetupBones( NULL, 0, BONE_USED_BY_ANYTHING, gpGlobals->curtime );
  1623. int iWeaponBone = 0;
  1624. if ( FindWeaponAttachmentBone( pWeapon, iWeaponBone ) )
  1625. {
  1626. int iMyBone = 0;
  1627. if ( FindMyAttachmentBone( this, iMyBone, pHdr ) )
  1628. {
  1629. int iHand = iMyBone;
  1630. int iElbow = pHdr->pBone( iHand )->parent;
  1631. int iShoulder = pHdr->pBone( iElbow )->parent;
  1632. matrix3x4_t *pBones = &GetBoneForWrite( 0 );
  1633. // Store off the original hand position.
  1634. matrix3x4_t mSource = pBones[iHand];
  1635. // Figure out the rotation offset from the current shoulder and elbow bone rotations
  1636. // and what the IK code's alignment code is going to produce, because we'll have to
  1637. // re-apply that offset after the IK runs.
  1638. matrix3x4_t mShoulderCorrection, mElbowCorrection;
  1639. GetCorrectionMatrices( pBones[iShoulder], pBones[iElbow], pBones[iHand], mShoulderCorrection, mElbowCorrection );
  1640. // Do the IK solution.
  1641. Vector vHandTarget;
  1642. MatrixPosition( pWeapon->GetBone( iWeaponBone ), vHandTarget );
  1643. Studio_SolveIK( iShoulder, iElbow, iHand, vHandTarget, pBones );
  1644. // Now reapply the rotation correction.
  1645. matrix3x4_t mTempShoulder = pBones[iShoulder];
  1646. matrix3x4_t mTempElbow = pBones[iElbow];
  1647. ConcatTransforms( mTempShoulder, mShoulderCorrection, pBones[iShoulder] );
  1648. ConcatTransforms( mTempElbow, mElbowCorrection, pBones[iElbow] );
  1649. // Now apply the transformation on the hand to the fingers.
  1650. matrix3x4_t &mDest = GetBoneForWrite( iHand );
  1651. ApplyDifferenceTransformToChildren( this, mSource, mDest, iHand );
  1652. }
  1653. }
  1654. }
  1655. C_BaseAnimating * C_CSPlayer::BecomeRagdollOnClient()
  1656. {
  1657. return NULL;
  1658. }
  1659. IRagdoll* C_CSPlayer::GetRepresentativeRagdoll() const
  1660. {
  1661. if ( m_hRagdoll.Get() )
  1662. {
  1663. C_CSRagdoll *pRagdoll = (C_CSRagdoll*)m_hRagdoll.Get();
  1664. return pRagdoll->GetIRagdoll();
  1665. }
  1666. else
  1667. {
  1668. return NULL;
  1669. }
  1670. }
  1671. void C_CSPlayer::PlayReloadEffect()
  1672. {
  1673. // Only play the effect for other players.
  1674. if ( this == C_CSPlayer::GetLocalCSPlayer() )
  1675. {
  1676. Assert( false ); // We shouldn't have been sent this message.
  1677. return;
  1678. }
  1679. // Get the view model for our current gun.
  1680. CWeaponCSBase *pWeapon = GetActiveCSWeapon();
  1681. if ( !pWeapon )
  1682. return;
  1683. // The weapon needs two models, world and view, but can only cache one. Synthesize the other.
  1684. const CCSWeaponInfo &info = pWeapon->GetCSWpnData();
  1685. const model_t *pModel = modelinfo->GetModel( modelinfo->GetModelIndex( info.szViewModel ) );
  1686. if ( !pModel )
  1687. return;
  1688. CStudioHdr studioHdr( modelinfo->GetStudiomodel( pModel ), mdlcache );
  1689. if ( !studioHdr.IsValid() )
  1690. return;
  1691. // Find the reload animation.
  1692. for ( int iSeq=0; iSeq < studioHdr.GetNumSeq(); iSeq++ )
  1693. {
  1694. mstudioseqdesc_t *pSeq = &studioHdr.pSeqdesc( iSeq );
  1695. if ( pSeq->activity == ACT_VM_RELOAD )
  1696. {
  1697. float poseParameters[MAXSTUDIOPOSEPARAM];
  1698. memset( poseParameters, 0, sizeof( poseParameters ) );
  1699. float cyclesPerSecond = Studio_CPS( &studioHdr, *pSeq, iSeq, poseParameters );
  1700. // Now read out all the sound events with their timing
  1701. for ( int iEvent=0; iEvent < pSeq->numevents; iEvent++ )
  1702. {
  1703. mstudioevent_t *pEvent = pSeq->pEvent( iEvent );
  1704. if ( pEvent->event == CL_EVENT_SOUND )
  1705. {
  1706. CCSSoundEvent event;
  1707. event.m_SoundName = pEvent->options;
  1708. event.m_flEventTime = gpGlobals->curtime + pEvent->cycle / cyclesPerSecond;
  1709. m_SoundEvents.AddToTail( event );
  1710. }
  1711. }
  1712. break;
  1713. }
  1714. }
  1715. }
  1716. void C_CSPlayer::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
  1717. {
  1718. if ( event == PLAYERANIMEVENT_THROW_GRENADE )
  1719. {
  1720. // Let the server handle this event. It will update m_iThrowGrenadeCounter and the client will
  1721. // pick up the event in CCSPlayerAnimState.
  1722. }
  1723. else
  1724. {
  1725. m_PlayerAnimState->DoAnimationEvent( event, nData );
  1726. }
  1727. }
  1728. void C_CSPlayer::FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options )
  1729. {
  1730. if( event == 7001 )
  1731. {
  1732. bool bInWater = ( enginetrace->GetPointContents(origin) & CONTENTS_WATER );
  1733. //Msg( "run event ( %d )\n", bInWater ? 1 : 0 );
  1734. if( bInWater )
  1735. {
  1736. //run splash
  1737. CEffectData data;
  1738. //trace up from foot position to the water surface
  1739. trace_t tr;
  1740. Vector vecTrace(0,0,1024);
  1741. UTIL_TraceLine( origin, origin + vecTrace, MASK_WATER, NULL, COLLISION_GROUP_NONE, &tr );
  1742. if ( tr.fractionleftsolid )
  1743. {
  1744. data.m_vOrigin = origin + (vecTrace * tr.fractionleftsolid);
  1745. }
  1746. else
  1747. {
  1748. data.m_vOrigin = origin;
  1749. }
  1750. data.m_vNormal = Vector( 0,0,1 );
  1751. data.m_flScale = random->RandomFloat( 4.0f, 5.0f );
  1752. DispatchEffect( "watersplash", data );
  1753. }
  1754. }
  1755. else if( event == 7002 )
  1756. {
  1757. bool bInWater = ( enginetrace->GetPointContents(origin) & CONTENTS_WATER );
  1758. //Msg( "walk event ( %d )\n", bInWater ? 1 : 0 );
  1759. if( bInWater )
  1760. {
  1761. //walk ripple
  1762. CEffectData data;
  1763. //trace up from foot position to the water surface
  1764. trace_t tr;
  1765. Vector vecTrace(0,0,1024);
  1766. UTIL_TraceLine( origin, origin + vecTrace, MASK_WATER, NULL, COLLISION_GROUP_NONE, &tr );
  1767. if ( tr.fractionleftsolid )
  1768. {
  1769. data.m_vOrigin = origin + (vecTrace * tr.fractionleftsolid);
  1770. }
  1771. else
  1772. {
  1773. data.m_vOrigin = origin;
  1774. }
  1775. data.m_vNormal = Vector( 0,0,1 );
  1776. data.m_flScale = random->RandomFloat( 4.0f, 7.0f );
  1777. DispatchEffect( "waterripple", data );
  1778. }
  1779. }
  1780. else
  1781. BaseClass::FireEvent( origin, angles, event, options );
  1782. }
  1783. void C_CSPlayer::SetActivity( Activity eActivity )
  1784. {
  1785. m_Activity = eActivity;
  1786. }
  1787. Activity C_CSPlayer::GetActivity() const
  1788. {
  1789. return m_Activity;
  1790. }
  1791. const Vector& C_CSPlayer::GetRenderOrigin( void )
  1792. {
  1793. if ( m_hRagdoll.Get() )
  1794. {
  1795. C_CSRagdoll *pRagdoll = (C_CSRagdoll*)m_hRagdoll.Get();
  1796. if ( pRagdoll->IsInitialized() )
  1797. return pRagdoll->GetRenderOrigin();
  1798. }
  1799. return BaseClass::GetRenderOrigin();
  1800. }
  1801. void C_CSPlayer::Simulate( void )
  1802. {
  1803. if( this != C_BasePlayer::GetLocalPlayer() )
  1804. {
  1805. if ( IsEffectActive( EF_DIMLIGHT ) )
  1806. {
  1807. QAngle eyeAngles = EyeAngles();
  1808. Vector vForward;
  1809. AngleVectors( eyeAngles, &vForward );
  1810. int iAttachment = LookupAttachment( "muzzle_flash" );
  1811. if ( iAttachment < 0 )
  1812. return;
  1813. Vector vecOrigin;
  1814. QAngle dummy;
  1815. GetAttachment( iAttachment, vecOrigin, dummy );
  1816. trace_t tr;
  1817. UTIL_TraceLine( vecOrigin, vecOrigin + (vForward * 200), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
  1818. if( !m_pFlashlightBeam )
  1819. {
  1820. BeamInfo_t beamInfo;
  1821. beamInfo.m_nType = TE_BEAMPOINTS;
  1822. beamInfo.m_vecStart = tr.startpos;
  1823. beamInfo.m_vecEnd = tr.endpos;
  1824. beamInfo.m_pszModelName = "sprites/glow01.vmt";
  1825. beamInfo.m_pszHaloName = "sprites/glow01.vmt";
  1826. beamInfo.m_flHaloScale = 3.0;
  1827. beamInfo.m_flWidth = 8.0f;
  1828. beamInfo.m_flEndWidth = 35.0f;
  1829. beamInfo.m_flFadeLength = 300.0f;
  1830. beamInfo.m_flAmplitude = 0;
  1831. beamInfo.m_flBrightness = 60.0;
  1832. beamInfo.m_flSpeed = 0.0f;
  1833. beamInfo.m_nStartFrame = 0.0;
  1834. beamInfo.m_flFrameRate = 0.0;
  1835. beamInfo.m_flRed = 255.0;
  1836. beamInfo.m_flGreen = 255.0;
  1837. beamInfo.m_flBlue = 255.0;
  1838. beamInfo.m_nSegments = 8;
  1839. beamInfo.m_bRenderable = true;
  1840. beamInfo.m_flLife = 0.5;
  1841. beamInfo.m_nFlags = FBEAM_FOREVER | FBEAM_ONLYNOISEONCE | FBEAM_NOTILE | FBEAM_HALOBEAM;
  1842. m_pFlashlightBeam = beams->CreateBeamPoints( beamInfo );
  1843. }
  1844. if( m_pFlashlightBeam )
  1845. {
  1846. BeamInfo_t beamInfo;
  1847. beamInfo.m_vecStart = tr.startpos;
  1848. beamInfo.m_vecEnd = tr.endpos;
  1849. beamInfo.m_flRed = 255.0;
  1850. beamInfo.m_flGreen = 255.0;
  1851. beamInfo.m_flBlue = 255.0;
  1852. beams->UpdateBeamInfo( m_pFlashlightBeam, beamInfo );
  1853. dlight_t *el = effects->CL_AllocDlight( 0 );
  1854. el->origin = tr.endpos;
  1855. el->radius = 50;
  1856. el->color.r = 200;
  1857. el->color.g = 200;
  1858. el->color.b = 200;
  1859. el->die = gpGlobals->curtime + 0.1;
  1860. }
  1861. }
  1862. else if ( m_pFlashlightBeam )
  1863. {
  1864. ReleaseFlashlight();
  1865. }
  1866. }
  1867. BaseClass::Simulate();
  1868. }
  1869. void C_CSPlayer::ReleaseFlashlight( void )
  1870. {
  1871. if( m_pFlashlightBeam )
  1872. {
  1873. m_pFlashlightBeam->flags = 0;
  1874. m_pFlashlightBeam->die = gpGlobals->curtime - 1;
  1875. m_pFlashlightBeam = NULL;
  1876. }
  1877. }
  1878. bool C_CSPlayer::HasC4( void )
  1879. {
  1880. if( this == C_CSPlayer::GetLocalPlayer() )
  1881. {
  1882. return Weapon_OwnsThisType( "weapon_c4" );
  1883. }
  1884. else
  1885. {
  1886. C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources();
  1887. return pCSPR->HasC4( entindex() );
  1888. }
  1889. }
  1890. void C_CSPlayer::ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName )
  1891. {
  1892. static ConVar *violence_hblood = cvar->FindVar( "violence_hblood" );
  1893. if ( violence_hblood && !violence_hblood->GetBool() )
  1894. return;
  1895. BaseClass::ImpactTrace( pTrace, iDamageType, pCustomImpactName );
  1896. }
  1897. //-----------------------------------------------------------------------------
  1898. void C_CSPlayer::CalcObserverView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov )
  1899. {
  1900. /**
  1901. * TODO: Fix this!
  1902. // CS:S standing eyeheight is above the collision volume, so we need to pull it
  1903. // down when we go into close quarters.
  1904. float maxEyeHeightAboveBounds = VEC_VIEW_SCALED( this ).z - VEC_HULL_MAX_SCALED( this ).z;
  1905. if ( GetObserverMode() == OBS_MODE_IN_EYE &&
  1906. maxEyeHeightAboveBounds > 0.0f &&
  1907. GetObserverTarget() &&
  1908. GetObserverTarget()->IsPlayer() )
  1909. {
  1910. const float eyeClearance = 12.0f; // eye pos must be this far below the ceiling
  1911. C_CSPlayer *target = ToCSPlayer( GetObserverTarget() );
  1912. Vector offset = eyeOrigin - GetAbsOrigin();
  1913. Vector vHullMin = VEC_HULL_MIN_SCALED( this );
  1914. vHullMin.z = 0.0f;
  1915. Vector vHullMax = VEC_HULL_MAX_SCALED( this );
  1916. Vector start = GetAbsOrigin();
  1917. start.z += vHullMax.z;
  1918. Vector end = start;
  1919. end.z += eyeClearance + VEC_VIEW_SCALED( this ).z - vHullMax_SCALED( this ).z;
  1920. vHullMax.z = 0.0f;
  1921. Vector fudge( 1, 1, 0 );
  1922. vHullMin += fudge;
  1923. vHullMax -= fudge;
  1924. trace_t trace;
  1925. Ray_t ray;
  1926. ray.Init( start, end, vHullMin, vHullMax );
  1927. UTIL_TraceRay( ray, MASK_PLAYERSOLID, target, COLLISION_GROUP_PLAYER_MOVEMENT, &trace );
  1928. if ( trace.fraction < 1.0f )
  1929. {
  1930. float est = start.z + trace.fraction * (end.z - start.z) - GetAbsOrigin().z - eyeClearance;
  1931. if ( ( target->GetFlags() & FL_DUCKING ) == 0 && !target->GetFallVelocity() && !target->IsDucked() )
  1932. {
  1933. offset.z = est;
  1934. }
  1935. else
  1936. {
  1937. offset.z = MIN( est, offset.z );
  1938. }
  1939. eyeOrigin.z = GetAbsOrigin().z + offset.z;
  1940. }
  1941. }
  1942. */
  1943. BaseClass::CalcObserverView( eyeOrigin, eyeAngles, fov );
  1944. }
  1945. //=============================================================================
  1946. // HPE_BEGIN:
  1947. //=============================================================================
  1948. // [tj] checks if this player has another given player on their Steam friends list.
  1949. bool C_CSPlayer::HasPlayerAsFriend(C_CSPlayer* player)
  1950. {
  1951. if (!steamapicontext || !steamapicontext->SteamFriends() || !steamapicontext->SteamUtils() || !player)
  1952. {
  1953. return false;
  1954. }
  1955. player_info_t pi;
  1956. if ( !engine->GetPlayerInfo( player->entindex(), &pi ) )
  1957. {
  1958. return false;
  1959. }
  1960. if ( !pi.friendsID )
  1961. {
  1962. return false;
  1963. }
  1964. // check and see if they're on the local player's friends list
  1965. CSteamID steamID( pi.friendsID, 1, steamapicontext->SteamUtils()->GetConnectedUniverse(), k_EAccountTypeIndividual );
  1966. return steamapicontext->SteamFriends()->HasFriend( steamID, k_EFriendFlagImmediate);
  1967. }
  1968. // [menglish] Returns whether this player is dominating or is being dominated by the specified player
  1969. bool C_CSPlayer::IsPlayerDominated( int iPlayerIndex )
  1970. {
  1971. return m_bPlayerDominated.Get( iPlayerIndex );
  1972. }
  1973. bool C_CSPlayer::IsPlayerDominatingMe( int iPlayerIndex )
  1974. {
  1975. return m_bPlayerDominatingMe.Get( iPlayerIndex );
  1976. }
  1977. // helper interpolation functions
  1978. namespace Interpolators
  1979. {
  1980. inline float Linear( float t ) { return t; }
  1981. inline float SmoothStep( float t )
  1982. {
  1983. t = 3 * t * t - 2.0f * t * t * t;
  1984. return t;
  1985. }
  1986. inline float SmoothStep2( float t )
  1987. {
  1988. return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f);
  1989. }
  1990. inline float SmoothStepStart( float t )
  1991. {
  1992. t = 0.5f * t;
  1993. t = 3 * t * t - 2.0f * t * t * t;
  1994. t = t* 2.0f;
  1995. return t;
  1996. }
  1997. inline float SmoothStepEnd( float t )
  1998. {
  1999. t = 0.5f * t + 0.5f;
  2000. t = 3 * t * t - 2.0f * t * t * t;
  2001. t = (t - 0.5f) * 2.0f;
  2002. return t;
  2003. }
  2004. }
  2005. //-----------------------------------------------------------------------------
  2006. // Purpose: Calculate the view for the player while he's in freeze frame observer mode
  2007. //-----------------------------------------------------------------------------
  2008. void C_CSPlayer::CalcFreezeCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov )
  2009. {
  2010. C_BaseEntity *pTarget = GetObserverTarget();
  2011. //=============================================================================
  2012. // HPE_BEGIN:
  2013. // [Forrest] Added sv_disablefreezecam check
  2014. //=============================================================================
  2015. static ConVarRef sv_disablefreezecam( "sv_disablefreezecam" );
  2016. if ( !pTarget || cl_disablefreezecam.GetBool() || sv_disablefreezecam.GetBool() )
  2017. //=============================================================================
  2018. // HPE_END
  2019. //=============================================================================
  2020. {
  2021. return CalcDeathCamView( eyeOrigin, eyeAngles, fov );
  2022. }
  2023. // pick a zoom camera target
  2024. Vector vLookAt = pTarget->GetObserverCamOrigin(); // Returns ragdoll origin if they're ragdolled
  2025. vLookAt += GetChaseCamViewOffset( pTarget );
  2026. // look over ragdoll, not through
  2027. if ( !pTarget->IsAlive() )
  2028. vLookAt.z += pTarget->GetBaseAnimating() ? VEC_DEAD_VIEWHEIGHT_SCALED( pTarget->GetBaseAnimating() ).z : VEC_DEAD_VIEWHEIGHT.z;
  2029. // Figure out a view position in front of the target
  2030. Vector vEyeOnPlane = eyeOrigin;
  2031. vEyeOnPlane.z = vLookAt.z;
  2032. Vector vToTarget = vLookAt - vEyeOnPlane;
  2033. VectorNormalize( vToTarget );
  2034. // goal position of camera is pulled away from target by m_flFreezeFrameDistance
  2035. Vector vTargetPos = vLookAt - (vToTarget * m_flFreezeFrameDistance);
  2036. // Now trace out from the target, so that we're put in front of any walls
  2037. trace_t trace;
  2038. C_BaseEntity::PushEnableAbsRecomputations( false ); // HACK don't recompute positions while doing RayTrace
  2039. UTIL_TraceHull( vLookAt, vTargetPos, WALL_MIN, WALL_MAX, MASK_SOLID, pTarget, COLLISION_GROUP_NONE, &trace );
  2040. C_BaseEntity::PopEnableAbsRecomputations();
  2041. if ( trace.fraction < 1.0 )
  2042. {
  2043. // The camera's going to be really close to the target. So we don't end up
  2044. // looking at someone's chest, aim close freezecams at the target's eyes.
  2045. vTargetPos = trace.endpos;
  2046. // To stop all close in views looking up at character's chins, move the view up.
  2047. vTargetPos.z += fabs(vLookAt.z - vTargetPos.z) * 0.85;
  2048. C_BaseEntity::PushEnableAbsRecomputations( false ); // HACK don't recompute positions while doing RayTrace
  2049. UTIL_TraceHull( vLookAt, vTargetPos, WALL_MIN, WALL_MAX, MASK_SOLID, pTarget, COLLISION_GROUP_NONE, &trace );
  2050. C_BaseEntity::PopEnableAbsRecomputations();
  2051. vTargetPos = trace.endpos;
  2052. }
  2053. // Look directly at the target
  2054. vToTarget = vLookAt - vTargetPos;
  2055. VectorNormalize( vToTarget );
  2056. VectorAngles( vToTarget, eyeAngles );
  2057. float fCurTime = gpGlobals->curtime - m_flFreezeFrameStartTime;
  2058. float fInterpolant = clamp( fCurTime / spec_freeze_traveltime.GetFloat(), 0.0f, 1.0f );
  2059. fInterpolant = Interpolators::SmoothStepEnd( fInterpolant );
  2060. // move the eye toward our killer
  2061. VectorLerp( m_vecFreezeFrameStart, vTargetPos, fInterpolant, eyeOrigin );
  2062. if ( fCurTime >= spec_freeze_traveltime.GetFloat() && !m_bSentFreezeFrame )
  2063. {
  2064. IGameEvent *pEvent = gameeventmanager->CreateEvent( "freezecam_started" );
  2065. if ( pEvent )
  2066. {
  2067. gameeventmanager->FireEventClientSide( pEvent );
  2068. }
  2069. m_bSentFreezeFrame = true;
  2070. view->FreezeFrame( spec_freeze_time.GetFloat() );
  2071. }
  2072. }
  2073. float C_CSPlayer::GetDeathCamInterpolationTime()
  2074. {
  2075. static ConVarRef sv_disablefreezecam( "sv_disablefreezecam" );
  2076. if ( cl_disablefreezecam.GetBool() || sv_disablefreezecam.GetBool() || !GetObserverTarget() )
  2077. return spec_freeze_time.GetFloat();
  2078. else
  2079. return CS_DEATH_ANIMATION_TIME;
  2080. }
  2081. //=============================================================================
  2082. // HPE_END
  2083. //=============================================================================