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.

1396 lines
32 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "dod_gamerules.h"
  8. #include "takedamageinfo.h"
  9. #include "dod_shareddefs.h"
  10. #include "effect_dispatch_data.h"
  11. #include "weapon_dodbase.h"
  12. #include "weapon_dodbipodgun.h"
  13. #include "weapon_dodbaserpg.h"
  14. #include "weapon_dodsniper.h"
  15. #include "movevars_shared.h"
  16. #include "engine/IEngineSound.h"
  17. #include "SoundEmitterSystem/isoundemittersystembase.h"
  18. #include "engine/ivdebugoverlay.h"
  19. #include "obstacle_pushaway.h"
  20. #include "props_shared.h"
  21. #include "decals.h"
  22. #include "util_shared.h"
  23. #ifdef CLIENT_DLL
  24. #include "c_dod_player.h"
  25. #include "prediction.h"
  26. #include "clientmode_dod.h"
  27. #include "vgui_controls/AnimationController.h"
  28. #define CRecipientFilter C_RecipientFilter
  29. #else
  30. #include "dod_player.h"
  31. #endif
  32. ConVar dod_bonusround( "dod_bonusround", "1", FCVAR_REPLICATED, "If true, the winners of the round can attack in the intermission." );
  33. ConVar sv_showimpacts("sv_showimpacts", "0", FCVAR_REPLICATED | FCVAR_CHEAT, "Shows client (red) and server (blue) bullet impact point" );
  34. void DispatchEffect( const char *pName, const CEffectData &data );
  35. bool CDODPlayer::CanMove( void ) const
  36. {
  37. bool bValidMoveState = (State_Get() == STATE_ACTIVE || State_Get() == STATE_OBSERVER_MODE);
  38. if ( !bValidMoveState )
  39. {
  40. return false;
  41. }
  42. return true;
  43. }
  44. // BUG! This is not called on the client at respawn, only first spawn!
  45. void CDODPlayer::SharedSpawn()
  46. {
  47. BaseClass::SharedSpawn();
  48. // Reset the animation state or we will animate to standing
  49. // when we spawn
  50. m_Shared.SetJumping( false );
  51. m_flMinNextStepSoundTime = gpGlobals->curtime;
  52. m_bPlayingProneMoveSound = false;
  53. }
  54. float GetDensityFromMaterial( surfacedata_t *pSurfaceData )
  55. {
  56. float flMaterialMod = 1.0f;
  57. Assert( pSurfaceData );
  58. // material mod is how many points of damage it costs to go through
  59. // 1 unit of the material
  60. switch( pSurfaceData->game.material )
  61. {
  62. //super soft
  63. // case CHAR_TEX_LEAVES:
  64. // flMaterialMod = 1.2f;
  65. // break;
  66. case CHAR_TEX_FLESH:
  67. flMaterialMod = 1.35f;
  68. break;
  69. //soft
  70. // case CHAR_TEX_STUCCO:
  71. // case CHAR_TEX_SNOW:
  72. case CHAR_TEX_GLASS:
  73. case CHAR_TEX_WOOD:
  74. case CHAR_TEX_TILE:
  75. flMaterialMod = 1.8f;
  76. break;
  77. //hard
  78. // case CHAR_TEX_SKY:
  79. // case CHAR_TEX_ROCK:
  80. // case CHAR_TEX_SAND:
  81. case CHAR_TEX_CONCRETE:
  82. case CHAR_TEX_DIRT: // "sand"
  83. flMaterialMod = 6.6f;
  84. break;
  85. //really hard
  86. // case CHAR_TEX_HEAVYMETAL:
  87. case CHAR_TEX_GRATE:
  88. case CHAR_TEX_METAL:
  89. flMaterialMod = 13.5f;
  90. break;
  91. case 'X': // invisible collision material
  92. flMaterialMod = 0.1f;
  93. break;
  94. //medium
  95. // case CHAR_TEX_BRICK:
  96. // case CHAR_TEX_GRAVEL:
  97. // case CHAR_TEX_GRASS:
  98. default:
  99. #ifndef CLIENT_DLL
  100. AssertMsg( 0, UTIL_VarArgs( "Material has unknown materialmod - '%c' \n", pSurfaceData->game.material ) );
  101. #endif
  102. flMaterialMod = 5.0f;
  103. break;
  104. }
  105. Assert( flMaterialMod > 0 );
  106. return flMaterialMod;
  107. }
  108. static bool TraceToExit( const Vector &start,
  109. const Vector &dir,
  110. Vector &end,
  111. const float flStepSize,
  112. const float flMaxDistance )
  113. {
  114. float flDistance = 0;
  115. Vector last = start;
  116. while ( flDistance < flMaxDistance )
  117. {
  118. flDistance += flStepSize;
  119. // no point in tracing past the max distance.
  120. // if this check fails, we save ourselves a traceline later
  121. if ( flDistance > flMaxDistance )
  122. {
  123. flDistance = flMaxDistance;
  124. }
  125. end = start + flDistance * dir;
  126. // point contents fails to return proper contents inside a func_detail brush, eg the dod_flash
  127. // stairs
  128. //int contents = UTIL_PointContents( end );
  129. trace_t tr;
  130. UTIL_TraceLine( end, end, MASK_SOLID | CONTENTS_HITBOX, NULL, &tr );
  131. //if ( (UTIL_PointContents ( end ) & MASK_SOLID) == 0 )
  132. if ( !tr.startsolid )
  133. {
  134. // found first free point
  135. return true;
  136. }
  137. }
  138. return false;
  139. }
  140. #include "ammodef.h"
  141. #define NEW_HITBOX_GROUP_CODE 1
  142. #undef ARM_PENETRATION
  143. #ifndef CLIENT_DLL
  144. #include "ndebugoverlay.h"
  145. #endif
  146. void CDODPlayer::FireBullets( const FireBulletsInfo_t &info )
  147. {
  148. trace_t tr;
  149. trace_t reverseTr; //Used to find exit points
  150. static int iMaxPenetrations = 6;
  151. int iPenetrations = 0;
  152. float flDamage = info.m_flDamage; //Remaining damage in the bullet
  153. Vector vecSrc = info.m_vecSrc;
  154. Vector vecEnd = vecSrc + info.m_vecDirShooting * info.m_flDistance;
  155. static int iTraceMask = ( ( MASK_SOLID | CONTENTS_DEBRIS | CONTENTS_HITBOX | CONTENTS_PRONE_HELPER ) & ~CONTENTS_GRATE );
  156. CBaseEntity *pLastHitEntity = this; // start with us so we don't trace ourselves
  157. int iDamageType = GetAmmoDef()->DamageType( info.m_iAmmoType );
  158. int iCollisionGroup = COLLISION_GROUP_NONE;
  159. #ifdef GAME_DLL
  160. int iNumHeadshots = 0;
  161. #endif
  162. while ( flDamage > 0 && iPenetrations < iMaxPenetrations )
  163. {
  164. //DevMsg( 2, "penetration: %d, starting dmg: %.1f\n", iPenetrations, flDamage );
  165. CBaseEntity *pPreviousHit = pLastHitEntity;
  166. // skip the shooter always
  167. CTraceFilterSkipTwoEntities ignoreShooterAndPrevious( this, pPreviousHit, iCollisionGroup );
  168. UTIL_TraceLine( vecSrc, vecEnd, iTraceMask, &ignoreShooterAndPrevious, &tr );
  169. const float rayExtension = 40.0f;
  170. UTIL_ClipTraceToPlayers( vecSrc, vecEnd + info.m_vecDirShooting * rayExtension, iTraceMask, &ignoreShooterAndPrevious, &tr );
  171. if ( tr.fraction == 1.0f )
  172. break; // we didn't hit anything, stop tracing shoot
  173. // New hitbox code that uses hitbox groups instead of trying to trace
  174. // through the player
  175. if ( tr.m_pEnt && tr.m_pEnt->IsPlayer() )
  176. {
  177. switch( tr.hitgroup )
  178. {
  179. #ifdef GAME_DLL
  180. case HITGROUP_HEAD:
  181. {
  182. if ( tr.m_pEnt->GetTeamNumber() != GetTeamNumber() )
  183. {
  184. iNumHeadshots++;
  185. }
  186. }
  187. break;
  188. #endif
  189. case HITGROUP_LEFTARM:
  190. case HITGROUP_RIGHTARM:
  191. {
  192. //DevMsg( 2, "Hit arms, tracing against alt hitboxes.. \n" );
  193. CDODPlayer *pPlayer = ToDODPlayer( tr.m_pEnt );
  194. // set hitbox set to "dod_no_arms"
  195. pPlayer->SetHitboxSet( 1 );
  196. trace_t newTr;
  197. // re-fire the trace
  198. UTIL_TraceLine( vecSrc, vecEnd, iTraceMask, &ignoreShooterAndPrevious, &newTr );
  199. // if we hit the same player in the chest
  200. if ( tr.m_pEnt == newTr.m_pEnt )
  201. {
  202. //DevMsg( 2, ".. and we hit the chest.\n" );
  203. Assert( tr.hitgroup != newTr.hitgroup ); // If we hit this, hitbox sets are broken
  204. // use that damage instead
  205. tr = newTr;
  206. }
  207. // set hitboxes back to "dod"
  208. pPlayer->SetHitboxSet( 0 );
  209. }
  210. break;
  211. default:
  212. break;
  213. }
  214. }
  215. pLastHitEntity = tr.m_pEnt;
  216. if ( sv_showimpacts.GetBool() )
  217. {
  218. #ifdef CLIENT_DLL
  219. // draw red client impact markers
  220. debugoverlay->AddBoxOverlay( tr.endpos, Vector(-1,-1,-1), Vector(1,1,1), QAngle(0,0,0), 255, 0, 0, 127, 4 );
  221. if ( tr.m_pEnt && tr.m_pEnt->IsPlayer() )
  222. {
  223. C_BasePlayer *player = ToBasePlayer( tr.m_pEnt );
  224. player->DrawClientHitboxes( 4, true );
  225. }
  226. #else
  227. // draw blue server impact markers
  228. NDebugOverlay::Box( tr.endpos, Vector(-1,-1,-1), Vector(1,1,1), 0,0,255,127, 4 );
  229. if ( tr.m_pEnt && tr.m_pEnt->IsPlayer() )
  230. {
  231. CBasePlayer *player = ToBasePlayer( tr.m_pEnt );
  232. player->DrawServerHitboxes( 4, true );
  233. }
  234. #endif
  235. }
  236. #ifdef CLIENT_DLL
  237. // See if the bullet ended up underwater + started out of the water
  238. if ( enginetrace->GetPointContents( tr.endpos ) & (CONTENTS_WATER|CONTENTS_SLIME) )
  239. {
  240. trace_t waterTrace;
  241. UTIL_TraceLine( vecSrc, tr.endpos, (MASK_SHOT|CONTENTS_WATER|CONTENTS_SLIME), this, iCollisionGroup, &waterTrace );
  242. if( waterTrace.allsolid != 1 )
  243. {
  244. CEffectData data;
  245. data.m_vOrigin = waterTrace.endpos;
  246. data.m_vNormal = waterTrace.plane.normal;
  247. data.m_flScale = random->RandomFloat( 8, 12 );
  248. if ( waterTrace.contents & CONTENTS_SLIME )
  249. {
  250. data.m_fFlags |= FX_WATER_IN_SLIME;
  251. }
  252. DispatchEffect( "gunshotsplash", data );
  253. }
  254. }
  255. else
  256. {
  257. //Do Regular hit effects
  258. // Don't decal nodraw surfaces
  259. if ( !( tr.surface.flags & (SURF_SKY|SURF_NODRAW|SURF_HINT|SURF_SKIP) ) )
  260. {
  261. CBaseEntity *pEntity = tr.m_pEnt;
  262. if ( !( !friendlyfire.GetBool() && pEntity && pEntity->GetTeamNumber() == GetTeamNumber() ) )
  263. {
  264. UTIL_ImpactTrace( &tr, iDamageType );
  265. }
  266. }
  267. }
  268. #endif
  269. // Get surface where the bullet entered ( if it had different surfaces on enter and exit )
  270. surfacedata_t *pSurfaceData = physprops->GetSurfaceData( tr.surface.surfaceProps );
  271. Assert( pSurfaceData );
  272. float flMaterialMod = GetDensityFromMaterial(pSurfaceData);
  273. if ( iDamageType & DMG_MACHINEGUN )
  274. {
  275. flMaterialMod *= 0.65;
  276. }
  277. // try to penetrate object
  278. Vector penetrationEnd;
  279. float flMaxDistance = flDamage / flMaterialMod;
  280. #ifndef CLIENT_DLL
  281. ClearMultiDamage();
  282. float flActualDamage = flDamage;
  283. CTakeDamageInfo dmgInfo( info.m_pAttacker, info.m_pAttacker, flActualDamage, iDamageType );
  284. CalculateBulletDamageForce( &dmgInfo, info.m_iAmmoType, info.m_vecDirShooting, tr.endpos );
  285. tr.m_pEnt->DispatchTraceAttack( dmgInfo, info.m_vecDirShooting, &tr );
  286. DevMsg( 2, "Giving damage ( %.1f ) to entity of type %s\n", flActualDamage, tr.m_pEnt->GetClassname() );
  287. TraceAttackToTriggers( dmgInfo, tr.startpos, tr.endpos, info.m_vecDirShooting );
  288. #endif
  289. int stepsize = 16;
  290. // displacement always stops the bullet
  291. if ( tr.IsDispSurface() )
  292. {
  293. DevMsg( 2, "bullet was stopped by displacement\n" );
  294. ApplyMultiDamage();
  295. break;
  296. }
  297. // trace through the solid to find the exit point and how much material we went through
  298. if ( !TraceToExit( tr.endpos, info.m_vecDirShooting, penetrationEnd, stepsize, flMaxDistance ) )
  299. {
  300. DevMsg( 2, "bullet was stopped\n" );
  301. ApplyMultiDamage();
  302. break;
  303. }
  304. // find exact penetration exit
  305. CTraceFilterSimple ignoreShooter( this, iCollisionGroup );
  306. UTIL_TraceLine( penetrationEnd, tr.endpos, iTraceMask, &ignoreShooter, &reverseTr );
  307. // Now we can apply the damage, after we have traced the entity
  308. // so it doesn't break or die before we have a change to test against it
  309. #ifndef CLIENT_DLL
  310. ApplyMultiDamage();
  311. #endif
  312. // Continue looking for the exit point
  313. if( reverseTr.m_pEnt != tr.m_pEnt && reverseTr.m_pEnt != NULL )
  314. {
  315. // something was blocking, trace again
  316. CTraceFilterSkipTwoEntities ignoreShooterAndBlocker( this, reverseTr.m_pEnt, iCollisionGroup );
  317. UTIL_TraceLine( penetrationEnd, tr.endpos, iTraceMask, &ignoreShooterAndBlocker, &reverseTr );
  318. }
  319. if ( sv_showimpacts.GetBool() )
  320. {
  321. debugoverlay->AddLineOverlay( penetrationEnd, reverseTr.endpos, 255, 0, 0, true, 3.0 );
  322. }
  323. // penetration was successful
  324. #ifdef CLIENT_DLL
  325. // bullet did penetrate object, exit Decal
  326. if ( !( reverseTr.surface.flags & (SURF_SKY|SURF_NODRAW|SURF_HINT|SURF_SKIP) ) )
  327. {
  328. CBaseEntity *pEntity = reverseTr.m_pEnt;
  329. if ( !( !friendlyfire.GetBool() && pEntity && pEntity->GetTeamNumber() == GetTeamNumber() ) )
  330. {
  331. UTIL_ImpactTrace( &reverseTr, iDamageType );
  332. }
  333. }
  334. #endif
  335. //setup new start end parameters for successive trace
  336. // New start point is our last exit point
  337. vecSrc = reverseTr.endpos + /* 1.0 * */ info.m_vecDirShooting;
  338. // Reduce bullet damage by material and distanced travelled through that material
  339. // if it is < 0 we won't go through the loop again
  340. float flTraceDistance = VectorLength( reverseTr.endpos - tr.endpos );
  341. flDamage -= flMaterialMod * flTraceDistance;
  342. if( flDamage > 0 )
  343. {
  344. DevMsg( 2, "Completed penetration, new damage is %.1f\n", flDamage );
  345. }
  346. else
  347. {
  348. DevMsg( 2, "bullet was stopped\n" );
  349. }
  350. iPenetrations++;
  351. }
  352. #ifdef GAME_DLL
  353. HandleHeadshotAchievement( iNumHeadshots );
  354. #endif
  355. }
  356. void CDODPlayer::SetSprinting( bool bIsSprinting )
  357. {
  358. m_Shared.SetSprinting( bIsSprinting );
  359. }
  360. bool CDODPlayer::IsSprinting( void )
  361. {
  362. float flVelSqr = GetAbsVelocity().LengthSqr();
  363. return m_Shared.m_bIsSprinting && ( flVelSqr > 0.5f );
  364. }
  365. bool CDODPlayer::CanAttack( void )
  366. {
  367. if ( IsSprinting() )
  368. return false;
  369. if ( GetMoveType() == MOVETYPE_LADDER )
  370. return false;
  371. if ( m_Shared.IsJumping() )
  372. return false;
  373. if ( m_Shared.IsDefusing() )
  374. return false;
  375. // cannot attack while prone moving. except if you have a bazooka
  376. if ( m_Shared.IsProne() && GetAbsVelocity().LengthSqr() > 1 )
  377. {
  378. return false;
  379. }
  380. if( m_Shared.IsGoingProne() || m_Shared.IsGettingUpFromProne() )
  381. {
  382. return false;
  383. }
  384. CDODGameRules *rules = DODGameRules();
  385. Assert( rules );
  386. DODRoundState state = rules->State_Get();
  387. if ( dod_bonusround.GetBool() )
  388. {
  389. if ( GetTeamNumber() == TEAM_ALLIES )
  390. {
  391. return ( state == STATE_RND_RUNNING || state == STATE_ALLIES_WIN );
  392. }
  393. else
  394. {
  395. return ( state == STATE_RND_RUNNING || state == STATE_AXIS_WIN );
  396. }
  397. }
  398. else
  399. return ( state == STATE_RND_RUNNING );
  400. }
  401. void CDODPlayer::SetAnimation( PLAYER_ANIM playerAnim )
  402. {
  403. // In DoD, its CPlayerAnimState object manages ALL the animation state.
  404. return;
  405. }
  406. //-----------------------------------------------------------------------------
  407. // Purpose:
  408. // Input :
  409. // Output : const Vector
  410. //-----------------------------------------------------------------------------
  411. const Vector CDODPlayer::GetPlayerMins( void ) const
  412. {
  413. if ( IsObserver() )
  414. {
  415. return VEC_OBS_HULL_MIN;
  416. }
  417. else
  418. {
  419. if ( GetFlags() & FL_DUCKING )
  420. {
  421. return VEC_DUCK_HULL_MIN;
  422. }
  423. else if ( m_Shared.IsProne() )
  424. {
  425. return VEC_PRONE_HULL_MIN;
  426. }
  427. else
  428. {
  429. return VEC_HULL_MIN;
  430. }
  431. }
  432. }
  433. //-----------------------------------------------------------------------------
  434. // Purpose:
  435. // Input :
  436. // Output : const Vector
  437. //-----------------------------------------------------------------------------
  438. const Vector CDODPlayer::GetPlayerMaxs( void ) const
  439. {
  440. if ( IsObserver() )
  441. {
  442. return VEC_OBS_HULL_MAX_SCALED( this );
  443. }
  444. else
  445. {
  446. if ( GetFlags() & FL_DUCKING )
  447. {
  448. return VEC_DUCK_HULL_MAX_SCALED( this );
  449. }
  450. else if ( m_Shared.IsProne() )
  451. {
  452. return VEC_PRONE_HULL_MAX_SCALED( this );
  453. }
  454. else
  455. {
  456. return VEC_HULL_MAX_SCALED( this );
  457. }
  458. }
  459. }
  460. //-----------------------------------------------------------------------------
  461. // Purpose:
  462. // Input : collisionGroup -
  463. // Output : Returns true on success, false on failure.
  464. //-----------------------------------------------------------------------------
  465. bool CDODPlayer::ShouldCollide( int collisionGroup, int contentsMask ) const
  466. {
  467. if ( collisionGroup == COLLISION_GROUP_PLAYER_MOVEMENT || collisionGroup == COLLISION_GROUP_PROJECTILE )
  468. {
  469. switch( GetTeamNumber() )
  470. {
  471. case TEAM_ALLIES:
  472. if ( !( contentsMask & CONTENTS_TEAM2 ) )
  473. return false;
  474. break;
  475. case TEAM_AXIS:
  476. if ( !( contentsMask & CONTENTS_TEAM1 ) )
  477. return false;
  478. break;
  479. }
  480. }
  481. return BaseClass::ShouldCollide( collisionGroup, contentsMask );
  482. }
  483. // --------------------------------------------------------------------------------------------------- //
  484. // CDODPlayerShared implementation.
  485. // --------------------------------------------------------------------------------------------------- //
  486. CDODPlayerShared::CDODPlayerShared()
  487. {
  488. m_bProne = false;
  489. m_bForceProneChange = false;
  490. m_flNextProneCheck = 0;
  491. m_flSlowedUntilTime = 0;
  492. m_flUnProneTime = 0;
  493. m_flGoProneTime = 0;
  494. m_flDeployedHeight = STANDING_DEPLOY_HEIGHT;
  495. m_flDeployChangeTime = gpGlobals->curtime;
  496. SetDesiredPlayerClass( PLAYERCLASS_UNDEFINED );
  497. m_flLastViewAnimationTime = gpGlobals->curtime;
  498. m_pViewOffsetAnim = NULL;
  499. }
  500. CDODPlayerShared::~CDODPlayerShared()
  501. {
  502. if ( m_pViewOffsetAnim )
  503. {
  504. delete m_pViewOffsetAnim;
  505. m_pViewOffsetAnim = NULL;
  506. }
  507. }
  508. void CDODPlayerShared::Init( CDODPlayer *pPlayer )
  509. {
  510. m_pOuter = pPlayer;
  511. }
  512. bool CDODPlayerShared::IsDucking( void ) const
  513. {
  514. return !!( m_pOuter->GetFlags() & FL_DUCKING );
  515. }
  516. bool CDODPlayerShared::IsProne() const
  517. {
  518. return m_bProne;
  519. }
  520. bool CDODPlayerShared::IsGettingUpFromProne() const
  521. {
  522. return ( m_flUnProneTime > 0 );
  523. }
  524. bool CDODPlayerShared::IsGoingProne() const
  525. {
  526. return ( m_flGoProneTime > 0 );
  527. }
  528. void CDODPlayerShared::SetSprinting( bool bSprinting )
  529. {
  530. if ( bSprinting && !m_bIsSprinting )
  531. {
  532. StartSprinting();
  533. // only one penalty per key press
  534. if ( m_bGaveSprintPenalty == false )
  535. {
  536. m_flStamina -= INITIAL_SPRINT_STAMINA_PENALTY;
  537. m_bGaveSprintPenalty = true;
  538. }
  539. }
  540. else if ( !bSprinting && m_bIsSprinting )
  541. {
  542. StopSprinting();
  543. }
  544. }
  545. // this is reset when we let go of the sprint key
  546. void CDODPlayerShared::ResetSprintPenalty( void )
  547. {
  548. m_bGaveSprintPenalty = false;
  549. }
  550. void CDODPlayerShared::StartSprinting( void )
  551. {
  552. m_bIsSprinting = true;
  553. #ifndef CLIENT_DLL
  554. m_pOuter->RemoveHintTimer( HINT_USE_SPRINT );
  555. #endif
  556. }
  557. void CDODPlayerShared::StopSprinting( void )
  558. {
  559. m_bIsSprinting = false;
  560. }
  561. void CDODPlayerShared::SetProne( bool bProne, bool bNoAnimation /* = false */ )
  562. {
  563. m_bProne = bProne;
  564. if ( bNoAnimation )
  565. {
  566. m_flGoProneTime = 0;
  567. m_flUnProneTime = 0;
  568. // cancel the view animation!
  569. m_bForceProneChange = true;
  570. }
  571. if ( !bProne /*&& IsSniperZoomed()*/ ) // forceunzoom for going prone is in StartGoingProne
  572. {
  573. ForceUnzoom();
  574. }
  575. }
  576. void CDODPlayerShared::SetJumping( bool bJumping )
  577. {
  578. m_bJumping = bJumping;
  579. if ( IsSniperZoomed() )
  580. {
  581. ForceUnzoom();
  582. }
  583. }
  584. void CDODPlayerShared::ForceUnzoom( void )
  585. {
  586. CWeaponDODBase *pWeapon = GetActiveDODWeapon();
  587. if( pWeapon && ( pWeapon->GetDODWpnData().m_WeaponType & WPN_MASK_GUN ) )
  588. {
  589. CDODSniperWeapon *pSniper = dynamic_cast<CDODSniperWeapon *>(pWeapon);
  590. if ( pSniper )
  591. {
  592. pSniper->ZoomOut();
  593. }
  594. }
  595. }
  596. bool CDODPlayerShared::IsBazookaDeployed( void ) const
  597. {
  598. CWeaponDODBase *pWeapon = GetActiveDODWeapon();
  599. if( pWeapon && pWeapon->GetDODWpnData().m_WeaponType == WPN_TYPE_BAZOOKA )
  600. {
  601. CDODBaseRocketWeapon *pBazooka = (CDODBaseRocketWeapon *)pWeapon;
  602. return pBazooka->IsDeployed() && !pBazooka->m_bInReload;
  603. }
  604. return false;
  605. }
  606. bool CDODPlayerShared::IsBazookaOnlyDeployed( void ) const
  607. {
  608. CWeaponDODBase *pWeapon = GetActiveDODWeapon();
  609. if( pWeapon && pWeapon->GetDODWpnData().m_WeaponType == WPN_TYPE_BAZOOKA )
  610. {
  611. CDODBaseRocketWeapon *pBazooka = (CDODBaseRocketWeapon *)pWeapon;
  612. return pBazooka->IsDeployed();
  613. }
  614. return false;
  615. }
  616. bool CDODPlayerShared::IsSniperZoomed( void ) const
  617. {
  618. CWeaponDODBase *pWeapon = GetActiveDODWeapon();
  619. if( pWeapon && ( pWeapon->GetDODWpnData().m_WeaponType & WPN_MASK_GUN ) )
  620. {
  621. CWeaponDODBaseGun *pGun = (CWeaponDODBaseGun *)pWeapon;
  622. Assert( pGun );
  623. return pGun->IsSniperZoomed();
  624. }
  625. return false;
  626. }
  627. bool CDODPlayerShared::IsInMGDeploy( void ) const
  628. {
  629. CWeaponDODBase *pWeapon = GetActiveDODWeapon();
  630. if( pWeapon && pWeapon->GetDODWpnData().m_WeaponType == WPN_TYPE_MG )
  631. {
  632. CDODBipodWeapon *pMG = dynamic_cast<CDODBipodWeapon *>( pWeapon );
  633. Assert( pMG );
  634. return pMG->IsDeployed();
  635. }
  636. return false;
  637. }
  638. bool CDODPlayerShared::IsProneDeployed( void ) const
  639. {
  640. return ( IsProne() && IsInMGDeploy() );
  641. }
  642. bool CDODPlayerShared::IsSandbagDeployed( void ) const
  643. {
  644. return ( !IsProne() && IsInMGDeploy() );
  645. }
  646. void CDODPlayerShared::SetDesiredPlayerClass( int playerclass )
  647. {
  648. m_iDesiredPlayerClass = playerclass;
  649. }
  650. int CDODPlayerShared::DesiredPlayerClass( void )
  651. {
  652. return m_iDesiredPlayerClass;
  653. }
  654. void CDODPlayerShared::SetPlayerClass( int playerclass )
  655. {
  656. m_iPlayerClass = playerclass;
  657. }
  658. int CDODPlayerShared::PlayerClass( void )
  659. {
  660. return m_iPlayerClass;
  661. }
  662. void CDODPlayerShared::SetStamina( float flStamina )
  663. {
  664. m_flStamina = clamp( flStamina, 0, 100 );
  665. }
  666. CWeaponDODBase* CDODPlayerShared::GetActiveDODWeapon() const
  667. {
  668. CBaseCombatWeapon *pWeapon = m_pOuter->GetActiveWeapon();
  669. if ( pWeapon )
  670. {
  671. Assert( dynamic_cast< CWeaponDODBase* >( pWeapon ) == static_cast< CWeaponDODBase* >( pWeapon ) );
  672. return static_cast< CWeaponDODBase* >( pWeapon );
  673. }
  674. else
  675. {
  676. return NULL;
  677. }
  678. }
  679. void CDODPlayerShared::SetDeployed( bool bDeployed, float flHeight /* = -1 */ )
  680. {
  681. if( gpGlobals->curtime - m_flDeployChangeTime < 0.2 )
  682. {
  683. Assert(0);
  684. }
  685. m_flDeployChangeTime = gpGlobals->curtime;
  686. m_vecDeployedAngles = m_pOuter->EyeAngles();
  687. if( flHeight > 0 )
  688. {
  689. m_flDeployedHeight = flHeight;
  690. }
  691. else
  692. {
  693. m_flDeployedHeight = m_pOuter->GetViewOffset()[2];
  694. }
  695. }
  696. QAngle CDODPlayerShared::GetDeployedAngles( void ) const
  697. {
  698. return m_vecDeployedAngles;
  699. }
  700. void CDODPlayerShared::SetDeployedYawLimits( float flLeftYaw, float flRightYaw )
  701. {
  702. m_flDeployedYawLimitLeft = flLeftYaw;
  703. m_flDeployedYawLimitRight = -flRightYaw;
  704. m_vecDeployedAngles = m_pOuter->EyeAngles();
  705. }
  706. void CDODPlayerShared::ClampDeployedAngles( QAngle *vecTestAngles )
  707. {
  708. Assert( vecTestAngles );
  709. // Clamp Pitch
  710. vecTestAngles->x = clamp( vecTestAngles->x, MAX_DEPLOY_PITCH, MIN_DEPLOY_PITCH );
  711. // Clamp Yaw - do a bit more work as yaw will wrap around and cause problems
  712. float flDeployedYawCenter = GetDeployedAngles().y;
  713. float flDelta = AngleNormalize( vecTestAngles->y - flDeployedYawCenter );
  714. if( flDelta < m_flDeployedYawLimitRight )
  715. {
  716. vecTestAngles->y = flDeployedYawCenter + m_flDeployedYawLimitRight;
  717. }
  718. else if( flDelta > m_flDeployedYawLimitLeft )
  719. {
  720. vecTestAngles->y = flDeployedYawCenter + m_flDeployedYawLimitLeft;
  721. }
  722. /*
  723. Msg( "delta %.1f ( left %.1f, right %.1f ) ( %.1f -> %.1f )\n",
  724. flDelta,
  725. flDeployedYawCenter + m_flDeployedYawLimitLeft,
  726. flDeployedYawCenter + m_flDeployedYawLimitRight,
  727. before,
  728. vecTestAngles->y );
  729. */
  730. }
  731. float CDODPlayerShared::GetDeployedHeight( void ) const
  732. {
  733. return m_flDeployedHeight;
  734. }
  735. float CDODPlayerShared::GetSlowedTime( void ) const
  736. {
  737. return m_flSlowedUntilTime;
  738. }
  739. void CDODPlayerShared::SetSlowedTime( float t )
  740. {
  741. m_flSlowedUntilTime = gpGlobals->curtime + t;
  742. }
  743. void CDODPlayerShared::StartGoingProne( void )
  744. {
  745. // make the prone sound
  746. CPASFilter filter( m_pOuter->GetAbsOrigin() );
  747. filter.UsePredictionRules();
  748. m_pOuter->EmitSound( filter, m_pOuter->entindex(), "Player.GoProne" );
  749. // slow to prone speed
  750. m_flGoProneTime = gpGlobals->curtime + TIME_TO_PRONE;
  751. m_flUnProneTime = 0.0f; //reset
  752. if ( IsSniperZoomed() )
  753. ForceUnzoom();
  754. }
  755. void CDODPlayerShared::StandUpFromProne( void )
  756. {
  757. // make the prone sound
  758. CPASFilter filter( m_pOuter->GetAbsOrigin() );
  759. filter.UsePredictionRules();
  760. m_pOuter->EmitSound( filter, m_pOuter->entindex(), "Player.UnProne" );
  761. // speed up to target speed
  762. m_flUnProneTime = gpGlobals->curtime + TIME_TO_PRONE;
  763. m_flGoProneTime = 0.0f; //reset
  764. }
  765. bool CDODPlayerShared::CanChangePosition( void )
  766. {
  767. if ( IsInMGDeploy() )
  768. return false;
  769. if ( IsGettingUpFromProne() )
  770. return false;
  771. if ( IsGoingProne() )
  772. return false;
  773. return true;
  774. }
  775. void CDODPlayer::UpdateStepSound( surfacedata_t *psurface, const Vector &vecOrigin, const Vector &vecVelocity )
  776. {
  777. Vector knee;
  778. Vector feet;
  779. float height;
  780. int fLadder;
  781. if ( m_flStepSoundTime > 0 )
  782. {
  783. m_flStepSoundTime -= 1000.0f * gpGlobals->frametime;
  784. if ( m_flStepSoundTime < 0 )
  785. {
  786. m_flStepSoundTime = 0;
  787. }
  788. }
  789. if ( m_flStepSoundTime > 0 )
  790. return;
  791. if ( GetFlags() & (FL_FROZEN|FL_ATCONTROLS))
  792. return;
  793. if ( GetMoveType() == MOVETYPE_NOCLIP || GetMoveType() == MOVETYPE_OBSERVER )
  794. return;
  795. if ( !sv_footsteps.GetFloat() )
  796. return;
  797. float speed = VectorLength( vecVelocity );
  798. float groundspeed = Vector2DLength( vecVelocity.AsVector2D() );
  799. // determine if we are on a ladder
  800. fLadder = ( GetMoveType() == MOVETYPE_LADDER );
  801. float flDuck;
  802. if ( ( GetFlags() & FL_DUCKING) || fLadder )
  803. {
  804. flDuck = 100;
  805. }
  806. else
  807. {
  808. flDuck = 0;
  809. }
  810. static float flMinProneSpeed = 10.0f;
  811. static float flMinSpeed = 70.0f;
  812. static float flRunSpeed = 110.0f;
  813. bool onground = ( GetFlags() & FL_ONGROUND );
  814. bool movingalongground = ( groundspeed > 0.0f );
  815. bool moving_fast_enough = ( speed >= flMinSpeed );
  816. // always play a step sound if we are moving faster than
  817. // To hear step sounds you must be either on a ladder or moving along the ground AND
  818. // You must be moving fast enough
  819. CheckProneMoveSound( groundspeed, onground );
  820. if ( !moving_fast_enough || !(fLadder || ( onground && movingalongground )) )
  821. {
  822. return;
  823. }
  824. bool bWalking = ( speed < flRunSpeed ); // or ducking!
  825. VectorCopy( vecOrigin, knee );
  826. VectorCopy( vecOrigin, feet );
  827. height = GetPlayerMaxs()[ 2 ] - GetPlayerMins()[ 2 ];
  828. knee[2] = vecOrigin[2] + 0.2 * height;
  829. float flVol;
  830. // find out what we're stepping in or on...
  831. if ( fLadder )
  832. {
  833. psurface = physprops->GetSurfaceData( physprops->GetSurfaceIndex( "ladder" ) );
  834. flVol = 1.0;
  835. m_flStepSoundTime = 350;
  836. }
  837. else if ( enginetrace->GetPointContents( knee ) & MASK_WATER )
  838. {
  839. static int iSkipStep = 0;
  840. if ( iSkipStep == 0 )
  841. {
  842. iSkipStep++;
  843. return;
  844. }
  845. if ( iSkipStep++ == 3 )
  846. {
  847. iSkipStep = 0;
  848. }
  849. psurface = physprops->GetSurfaceData( physprops->GetSurfaceIndex( "wade" ) );
  850. flVol = 0.65;
  851. m_flStepSoundTime = 600;
  852. }
  853. else if ( enginetrace->GetPointContents( feet ) & MASK_WATER )
  854. {
  855. psurface = physprops->GetSurfaceData( physprops->GetSurfaceIndex( "water" ) );
  856. flVol = bWalking ? 0.2 : 0.5;
  857. m_flStepSoundTime = bWalking ? 400 : 300;
  858. }
  859. else
  860. {
  861. if ( !psurface )
  862. return;
  863. if ( bWalking )
  864. {
  865. m_flStepSoundTime = 400;
  866. }
  867. else
  868. {
  869. if ( speed > 200 )
  870. {
  871. int speeddiff = PLAYER_SPEED_SPRINT - PLAYER_SPEED_RUN;
  872. int diff = speed - PLAYER_SPEED_RUN;
  873. float percent = (float)diff / (float)speeddiff;
  874. m_flStepSoundTime = 300.0f - 30.0f * percent;
  875. }
  876. else
  877. {
  878. m_flStepSoundTime = 400;
  879. }
  880. }
  881. switch ( psurface->game.material )
  882. {
  883. default:
  884. case CHAR_TEX_CONCRETE:
  885. flVol = bWalking ? 0.2 : 0.5;
  886. break;
  887. case CHAR_TEX_METAL:
  888. flVol = bWalking ? 0.2 : 0.5;
  889. break;
  890. case CHAR_TEX_DIRT:
  891. flVol = bWalking ? 0.25 : 0.55;
  892. break;
  893. case CHAR_TEX_VENT:
  894. flVol = bWalking ? 0.4 : 0.7;
  895. break;
  896. case CHAR_TEX_GRATE:
  897. flVol = bWalking ? 0.2 : 0.5;
  898. break;
  899. case CHAR_TEX_TILE:
  900. flVol = bWalking ? 0.2 : 0.5;
  901. break;
  902. case CHAR_TEX_SLOSH:
  903. flVol = bWalking ? 0.2 : 0.5;
  904. break;
  905. }
  906. }
  907. m_flStepSoundTime += flDuck; // slower step time if ducking
  908. if ( GetFlags() & FL_DUCKING )
  909. {
  910. flVol *= 0.65;
  911. }
  912. // protect us from prediction errors a little bit
  913. if ( m_flMinNextStepSoundTime > gpGlobals->curtime )
  914. {
  915. return;
  916. }
  917. m_flMinNextStepSoundTime = gpGlobals->curtime + 0.1f;
  918. PlayStepSound( feet, psurface, flVol, false );
  919. }
  920. void CDODPlayer::CheckProneMoveSound( int groundspeed, bool onground )
  921. {
  922. #ifdef CLIENT_DLL
  923. bool bShouldPlay = (groundspeed > 10) && (onground == true) && m_Shared.IsProne() && IsAlive();
  924. if ( m_bPlayingProneMoveSound && !bShouldPlay )
  925. {
  926. StopSound( "Player.MoveProne" );
  927. m_bPlayingProneMoveSound= false;
  928. }
  929. else if ( !m_bPlayingProneMoveSound && bShouldPlay )
  930. {
  931. CRecipientFilter filter;
  932. filter.AddRecipientsByPAS( WorldSpaceCenter() );
  933. EmitSound( filter, entindex(), "Player.MoveProne" );
  934. m_bPlayingProneMoveSound = true;
  935. }
  936. #endif
  937. }
  938. //-----------------------------------------------------------------------------
  939. // Purpose:
  940. // Input : step -
  941. // fvol -
  942. // force - force sound to play
  943. //-----------------------------------------------------------------------------
  944. void CDODPlayer::PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force )
  945. {
  946. if ( gpGlobals->maxClients > 1 && !sv_footsteps.GetFloat() )
  947. return;
  948. #if defined( CLIENT_DLL )
  949. // during prediction play footstep sounds only once
  950. if ( prediction->InPrediction() && !prediction->IsFirstTimePredicted() )
  951. return;
  952. #endif
  953. if ( !psurface )
  954. return;
  955. unsigned short stepSoundName = m_Local.m_nStepside ? psurface->sounds.stepleft : psurface->sounds.stepright;
  956. m_Local.m_nStepside = !m_Local.m_nStepside;
  957. if ( !stepSoundName )
  958. return;
  959. IPhysicsSurfaceProps *physprops = MoveHelper( )->GetSurfaceProps();
  960. const char *pSoundName = physprops->GetString( stepSoundName );
  961. CSoundParameters params;
  962. // we don't always know the model, so go by team
  963. char *pModelNameForGender = DOD_PLAYERMODEL_AXIS_RIFLEMAN;
  964. if( GetTeamNumber() == TEAM_ALLIES )
  965. pModelNameForGender = DOD_PLAYERMODEL_US_RIFLEMAN;
  966. if ( !CBaseEntity::GetParametersForSound( pSoundName, params, pModelNameForGender ) )
  967. return;
  968. CRecipientFilter filter;
  969. filter.AddRecipientsByPAS( vecOrigin );
  970. #ifndef CLIENT_DLL
  971. // im MP, server removed all players in origins PVS, these players
  972. // generate the footsteps clientside
  973. if ( gpGlobals->maxClients > 1 )
  974. filter.RemoveRecipientsByPVS( vecOrigin );
  975. #endif
  976. EmitSound_t ep;
  977. ep.m_nChannel = params.channel;
  978. ep.m_pSoundName = params.soundname;
  979. ep.m_flVolume = fvol;
  980. ep.m_SoundLevel = params.soundlevel;
  981. ep.m_nFlags = 0;
  982. ep.m_nPitch = params.pitch;
  983. ep.m_pOrigin = &vecOrigin;
  984. EmitSound( filter, entindex(), ep );
  985. }
  986. Activity CDODPlayer::TranslateActivity( Activity baseAct, bool *pRequired /* = NULL */ )
  987. {
  988. Activity translated = baseAct;
  989. if ( GetActiveWeapon() )
  990. {
  991. translated = GetActiveWeapon()->ActivityOverride( baseAct, pRequired );
  992. }
  993. else if (pRequired)
  994. {
  995. *pRequired = false;
  996. }
  997. return translated;
  998. }
  999. void CDODPlayerShared::SetCPIndex( int index )
  1000. {
  1001. #ifdef CLIENT_DLL
  1002. if ( m_pOuter->IsLocalPlayer() )
  1003. {
  1004. if ( index == -1 )
  1005. {
  1006. // just left an area
  1007. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "ObjectiveIconShrink" );
  1008. }
  1009. else
  1010. {
  1011. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "ObjectiveIconGrow" );
  1012. }
  1013. }
  1014. #endif
  1015. m_iCPIndex = index;
  1016. }
  1017. void CDODPlayerShared::SetLastViewAnimTime( float flTime )
  1018. {
  1019. m_flLastViewAnimationTime = flTime;
  1020. }
  1021. float CDODPlayerShared::GetLastViewAnimTime( void )
  1022. {
  1023. return m_flLastViewAnimationTime;
  1024. }
  1025. void CDODPlayerShared::ResetViewOffsetAnimation( void )
  1026. {
  1027. if ( m_pViewOffsetAnim )
  1028. {
  1029. //cancel it!
  1030. m_pViewOffsetAnim->Reset();
  1031. }
  1032. }
  1033. void CDODPlayerShared::ViewOffsetAnimation( Vector vecDest, float flTime, ViewAnimationType type )
  1034. {
  1035. if ( !m_pViewOffsetAnim )
  1036. {
  1037. m_pViewOffsetAnim = CViewOffsetAnimation::CreateViewOffsetAnim( m_pOuter );
  1038. }
  1039. Assert( m_pViewOffsetAnim );
  1040. if ( m_pViewOffsetAnim )
  1041. {
  1042. m_pViewOffsetAnim->StartAnimation( m_pOuter->GetViewOffset(), vecDest, flTime, type );
  1043. }
  1044. }
  1045. void CDODPlayerShared::ViewAnimThink( void )
  1046. {
  1047. // Check for the flag that will reset our view animations
  1048. // when the player respawns
  1049. if ( m_bForceProneChange )
  1050. {
  1051. ResetViewOffsetAnimation();
  1052. m_pOuter->SetViewOffset( VEC_VIEW_SCALED( m_pOuter ) );
  1053. m_bForceProneChange = false;
  1054. }
  1055. if ( m_pViewOffsetAnim )
  1056. {
  1057. m_pViewOffsetAnim->Think();
  1058. }
  1059. }
  1060. void CDODPlayerShared::ComputeWorldSpaceSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs )
  1061. {
  1062. Vector org = m_pOuter->GetAbsOrigin();
  1063. if ( IsProne() )
  1064. {
  1065. static Vector vecProneMin(-44, -44, 0 );
  1066. static Vector vecProneMax(44, 44, 24 );
  1067. VectorAdd( vecProneMin, org, *pVecWorldMins );
  1068. VectorAdd( vecProneMax, org, *pVecWorldMaxs );
  1069. }
  1070. else
  1071. {
  1072. static Vector vecMin(-32, -32, 0 );
  1073. static Vector vecMax(32, 32, 72 );
  1074. VectorAdd( vecMin, org, *pVecWorldMins );
  1075. VectorAdd( vecMax, org, *pVecWorldMaxs );
  1076. }
  1077. }
  1078. //-----------------------------------------------------------------------------
  1079. // Purpose: Sets whether this player is dominating the specified other player
  1080. //-----------------------------------------------------------------------------
  1081. void CDODPlayerShared::SetPlayerDominated( CDODPlayer *pPlayer, bool bDominated )
  1082. {
  1083. int iPlayerIndex = pPlayer->entindex();
  1084. m_bPlayerDominated.Set( iPlayerIndex, bDominated );
  1085. pPlayer->m_Shared.SetPlayerDominatingMe( m_pOuter, bDominated );
  1086. }
  1087. //-----------------------------------------------------------------------------
  1088. // Purpose: Sets whether this player is being dominated by the other player
  1089. //-----------------------------------------------------------------------------
  1090. void CDODPlayerShared::SetPlayerDominatingMe( CDODPlayer *pPlayer, bool bDominated )
  1091. {
  1092. int iPlayerIndex = pPlayer->entindex();
  1093. m_bPlayerDominatingMe.Set( iPlayerIndex, bDominated );
  1094. }
  1095. //-----------------------------------------------------------------------------
  1096. // Purpose: Returns whether this player is dominating the specified other player
  1097. //-----------------------------------------------------------------------------
  1098. bool CDODPlayerShared::IsPlayerDominated( int iPlayerIndex )
  1099. {
  1100. #ifdef CLIENT_DLL
  1101. // On the client, we only have data for the local player.
  1102. // As a result, it's only valid to ask for dominations related to the local player
  1103. C_DODPlayer *pLocalPlayer = C_DODPlayer::GetLocalDODPlayer();
  1104. if ( !pLocalPlayer )
  1105. return false;
  1106. Assert( m_pOuter->IsLocalPlayer() || pLocalPlayer->entindex() == iPlayerIndex );
  1107. if ( m_pOuter->IsLocalPlayer() )
  1108. return m_bPlayerDominated.Get( iPlayerIndex );
  1109. return pLocalPlayer->m_Shared.IsPlayerDominatingMe( m_pOuter->entindex() );
  1110. #else
  1111. // Server has all the data.
  1112. return m_bPlayerDominated.Get( iPlayerIndex );
  1113. #endif
  1114. }
  1115. //-----------------------------------------------------------------------------
  1116. // Purpose:
  1117. //-----------------------------------------------------------------------------
  1118. bool CDODPlayerShared::IsPlayerDominatingMe( int iPlayerIndex )
  1119. {
  1120. return m_bPlayerDominatingMe.Get( iPlayerIndex );
  1121. }