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.

943 lines
25 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "weapon_csbase.h"
  8. #include "decals.h"
  9. #include "cs_gamerules.h"
  10. #include "weapon_c4.h"
  11. #include "in_buttons.h"
  12. #include "datacache/imdlcache.h"
  13. #ifdef CLIENT_DLL
  14. #include "c_cs_player.h"
  15. #else
  16. #include "cs_player.h"
  17. #include "soundent.h"
  18. #include "bot/cs_bot.h"
  19. #include "KeyValues.h"
  20. #include "triggers.h"
  21. #include "cs_gamestats.h"
  22. #endif
  23. #include "cs_playeranimstate.h"
  24. #include "basecombatweapon_shared.h"
  25. #include "util_shared.h"
  26. #include "takedamageinfo.h"
  27. #include "effect_dispatch_data.h"
  28. #include "engine/ivdebugoverlay.h"
  29. #include "obstacle_pushaway.h"
  30. #include "props_shared.h"
  31. ConVar sv_showimpacts("sv_showimpacts", "0", FCVAR_REPLICATED, "Shows client (red) and server (blue) bullet impact point (1=both, 2=client-only, 3=server-only)" );
  32. ConVar sv_showplayerhitboxes( "sv_showplayerhitboxes", "0", FCVAR_REPLICATED, "Show lag compensated hitboxes for the specified player index whenever a player fires." );
  33. #define CS_MASK_SHOOT (MASK_SOLID|CONTENTS_DEBRIS)
  34. void DispatchEffect( const char *pName, const CEffectData &data );
  35. #ifdef _DEBUG
  36. // This is some extra code to collect weapon accuracy stats:
  37. struct bulletdata_s
  38. {
  39. float timedelta; // time delta since first shot of this round
  40. float derivation; // derivation for first shoot view angle
  41. int count;
  42. };
  43. #define STATS_MAX_BULLETS 50
  44. static bulletdata_s s_bullet_stats[STATS_MAX_BULLETS];
  45. Vector s_firstImpact = Vector(0,0,0);
  46. float s_firstTime = 0;
  47. float s_LastTime = 0;
  48. int s_bulletCount = 0;
  49. void ResetBulletStats()
  50. {
  51. s_firstTime = 0;
  52. s_LastTime = 0;
  53. s_bulletCount = 0;
  54. s_firstImpact = Vector(0,0,0);
  55. Q_memset( s_bullet_stats, 0, sizeof(s_bullet_stats) );
  56. }
  57. void PrintBulletStats()
  58. {
  59. for (int i=0; i<STATS_MAX_BULLETS; i++ )
  60. {
  61. if (s_bullet_stats[i].count == 0)
  62. break;
  63. Msg("%3i;%3i;%.4f;%.4f\n", i, s_bullet_stats[i].count,
  64. s_bullet_stats[i].timedelta, s_bullet_stats[i].derivation );
  65. }
  66. }
  67. void AddBulletStat( float time, float dist, Vector &impact )
  68. {
  69. if ( time > s_LastTime + 2.0f )
  70. {
  71. // time delta since last shoot is bigger than 2 seconds, start new row
  72. s_LastTime = s_firstTime = time;
  73. s_bulletCount = 0;
  74. s_firstImpact = impact;
  75. }
  76. else
  77. {
  78. s_LastTime = time;
  79. s_bulletCount++;
  80. }
  81. if ( s_bulletCount >= STATS_MAX_BULLETS )
  82. s_bulletCount = STATS_MAX_BULLETS -1;
  83. if ( dist < 1 )
  84. dist = 1;
  85. int i = s_bulletCount;
  86. float offset = VectorLength( s_firstImpact - impact );
  87. float timedelta = time - s_firstTime;
  88. float derivation = offset / dist;
  89. float weight = (float)s_bullet_stats[i].count/(float)(s_bullet_stats[i].count+1);
  90. s_bullet_stats[i].timedelta *= weight;
  91. s_bullet_stats[i].timedelta += (1.0f-weight) * timedelta;
  92. s_bullet_stats[i].derivation *= weight;
  93. s_bullet_stats[i].derivation += (1.0f-weight) * derivation;
  94. s_bullet_stats[i].count++;
  95. }
  96. CON_COMMAND( stats_bullets_reset, "Reset bullet stats")
  97. {
  98. ResetBulletStats();
  99. }
  100. CON_COMMAND( stats_bullets_print, "Print bullet stats")
  101. {
  102. PrintBulletStats();
  103. }
  104. #endif
  105. float CCSPlayer::GetPlayerMaxSpeed()
  106. {
  107. if ( GetMoveType() == MOVETYPE_NONE )
  108. {
  109. return CS_PLAYER_SPEED_STOPPED;
  110. }
  111. if ( IsObserver() )
  112. {
  113. // Player gets speed bonus in observer mode
  114. return CS_PLAYER_SPEED_OBSERVER;
  115. }
  116. bool bValidMoveState = ( State_Get() == STATE_ACTIVE || State_Get() == STATE_OBSERVER_MODE );
  117. if ( !bValidMoveState || m_bIsDefusing || CSGameRules()->IsFreezePeriod() )
  118. {
  119. // Player should not move during the freeze period
  120. return CS_PLAYER_SPEED_STOPPED;
  121. }
  122. float speed = BaseClass::GetPlayerMaxSpeed();
  123. if ( IsVIP() == true ) // VIP is slow due to the armour he's wearing
  124. {
  125. speed = MIN(speed, CS_PLAYER_SPEED_VIP);
  126. }
  127. else
  128. {
  129. CWeaponCSBase *pWeapon = dynamic_cast<CWeaponCSBase*>( GetActiveWeapon() );
  130. if ( pWeapon )
  131. {
  132. if ( HasShield() && IsShieldDrawn() )
  133. {
  134. speed = MIN(speed, CS_PLAYER_SPEED_SHIELD);
  135. }
  136. else
  137. {
  138. speed = MIN(speed, pWeapon->GetMaxSpeed());
  139. }
  140. }
  141. }
  142. return speed;
  143. }
  144. void CCSPlayer::GetBulletTypeParameters(
  145. int iBulletType,
  146. float &fPenetrationPower,
  147. float &flPenetrationDistance )
  148. {
  149. //MIKETODO: make ammo types come from a script file.
  150. if ( IsAmmoType( iBulletType, BULLET_PLAYER_50AE ) )
  151. {
  152. fPenetrationPower = 30;
  153. flPenetrationDistance = 1000.0;
  154. }
  155. else if ( IsAmmoType( iBulletType, BULLET_PLAYER_762MM ) )
  156. {
  157. fPenetrationPower = 39;
  158. flPenetrationDistance = 5000.0;
  159. }
  160. else if ( IsAmmoType( iBulletType, BULLET_PLAYER_556MM ) ||
  161. IsAmmoType( iBulletType, BULLET_PLAYER_556MM_BOX ) )
  162. {
  163. fPenetrationPower = 35;
  164. flPenetrationDistance = 4000.0;
  165. }
  166. else if ( IsAmmoType( iBulletType, BULLET_PLAYER_338MAG ) )
  167. {
  168. fPenetrationPower = 45;
  169. flPenetrationDistance = 8000.0;
  170. }
  171. else if ( IsAmmoType( iBulletType, BULLET_PLAYER_9MM ) )
  172. {
  173. fPenetrationPower = 21;
  174. flPenetrationDistance = 800.0;
  175. }
  176. else if ( IsAmmoType( iBulletType, BULLET_PLAYER_BUCKSHOT ) )
  177. {
  178. fPenetrationPower = 0;
  179. flPenetrationDistance = 0.0;
  180. }
  181. else if ( IsAmmoType( iBulletType, BULLET_PLAYER_45ACP ) )
  182. {
  183. fPenetrationPower = 15;
  184. flPenetrationDistance = 500.0;
  185. }
  186. else if ( IsAmmoType( iBulletType, BULLET_PLAYER_357SIG ) )
  187. {
  188. fPenetrationPower = 25;
  189. flPenetrationDistance = 800.0;
  190. }
  191. else if ( IsAmmoType( iBulletType, BULLET_PLAYER_57MM ) )
  192. {
  193. fPenetrationPower = 30;
  194. flPenetrationDistance = 2000.0;
  195. }
  196. else
  197. {
  198. // What kind of ammo is this?
  199. Assert( false );
  200. fPenetrationPower = 0;
  201. flPenetrationDistance = 0.0;
  202. }
  203. }
  204. static void GetMaterialParameters( int iMaterial, float &flPenetrationModifier, float &flDamageModifier )
  205. {
  206. switch ( iMaterial )
  207. {
  208. case CHAR_TEX_METAL :
  209. flPenetrationModifier = 0.5; // If we hit metal, reduce the thickness of the brush we can't penetrate
  210. flDamageModifier = 0.3;
  211. break;
  212. case CHAR_TEX_DIRT :
  213. flPenetrationModifier = 0.5;
  214. flDamageModifier = 0.3;
  215. break;
  216. case CHAR_TEX_CONCRETE :
  217. flPenetrationModifier = 0.4;
  218. flDamageModifier = 0.25;
  219. break;
  220. case CHAR_TEX_GRATE :
  221. flPenetrationModifier = 1.0;
  222. flDamageModifier = 0.99;
  223. break;
  224. case CHAR_TEX_VENT :
  225. flPenetrationModifier = 0.5;
  226. flDamageModifier = 0.45;
  227. break;
  228. case CHAR_TEX_TILE :
  229. flPenetrationModifier = 0.65;
  230. flDamageModifier = 0.3;
  231. break;
  232. case CHAR_TEX_COMPUTER :
  233. flPenetrationModifier = 0.4;
  234. flDamageModifier = 0.45;
  235. break;
  236. case CHAR_TEX_WOOD :
  237. flPenetrationModifier = 1.0;
  238. flDamageModifier = 0.6;
  239. break;
  240. default :
  241. flPenetrationModifier = 1.0;
  242. flDamageModifier = 0.5;
  243. break;
  244. }
  245. Assert( flPenetrationModifier > 0 );
  246. Assert( flDamageModifier < 1.0f ); // Less than 1.0f for avoiding infinite loops
  247. }
  248. static bool TraceToExit(Vector &start, Vector &dir, Vector &end, float flStepSize, float flMaxDistance )
  249. {
  250. float flDistance = 0;
  251. Vector last = start;
  252. while ( flDistance <= flMaxDistance )
  253. {
  254. flDistance += flStepSize;
  255. end = start + flDistance *dir;
  256. if ( (UTIL_PointContents ( end ) & MASK_SOLID) == 0 )
  257. {
  258. // found first free point
  259. return true;
  260. }
  261. }
  262. return false;
  263. }
  264. inline void UTIL_TraceLineIgnoreTwoEntities( const Vector& vecAbsStart, const Vector& vecAbsEnd, unsigned int mask,
  265. const IHandleEntity *ignore, const IHandleEntity *ignore2, int collisionGroup, trace_t *ptr )
  266. {
  267. Ray_t ray;
  268. ray.Init( vecAbsStart, vecAbsEnd );
  269. CTraceFilterSkipTwoEntities traceFilter( ignore, ignore2, collisionGroup );
  270. enginetrace->TraceRay( ray, mask, &traceFilter, ptr );
  271. if( r_visualizetraces.GetBool() )
  272. {
  273. DebugDrawLine( ptr->startpos, ptr->endpos, 255, 0, 0, true, -1.0f );
  274. }
  275. }
  276. void CCSPlayer::FireBullet(
  277. Vector vecSrc, // shooting postion
  278. const QAngle &shootAngles, //shooting angle
  279. float flDistance, // max distance
  280. int iPenetration, // how many obstacles can be penetrated
  281. int iBulletType, // ammo type
  282. int iDamage, // base damage
  283. float flRangeModifier, // damage range modifier
  284. CBaseEntity *pevAttacker, // shooter
  285. bool bDoEffects,
  286. float xSpread, float ySpread
  287. )
  288. {
  289. float fCurrentDamage = iDamage; // damage of the bullet at it's current trajectory
  290. float flCurrentDistance = 0.0; //distance that the bullet has traveled so far
  291. Vector vecDirShooting, vecRight, vecUp;
  292. AngleVectors( shootAngles, &vecDirShooting, &vecRight, &vecUp );
  293. // MIKETODO: put all the ammo parameters into a script file and allow for CS-specific params.
  294. float flPenetrationPower = 0; // thickness of a wall that this bullet can penetrate
  295. float flPenetrationDistance = 0; // distance at which the bullet is capable of penetrating a wall
  296. float flDamageModifier = 0.5; // default modification of bullets power after they go through a wall.
  297. float flPenetrationModifier = 1.f;
  298. GetBulletTypeParameters( iBulletType, flPenetrationPower, flPenetrationDistance );
  299. if ( !pevAttacker )
  300. pevAttacker = this; // the default attacker is ourselves
  301. // add the spray
  302. Vector vecDir = vecDirShooting + xSpread * vecRight + ySpread * vecUp;
  303. VectorNormalize( vecDir );
  304. //Adrian: visualize server/client player positions
  305. //This is used to show where the lag compesator thinks the player should be at.
  306. #if 0
  307. for ( int k = 1; k <= gpGlobals->maxClients; k++ )
  308. {
  309. CBasePlayer *clientClass = (CBasePlayer *)CBaseEntity::Instance( k );
  310. if ( clientClass == NULL )
  311. continue;
  312. if ( k == entindex() )
  313. continue;
  314. #ifdef CLIENT_DLL
  315. debugoverlay->AddBoxOverlay( clientClass->GetAbsOrigin(), clientClass->WorldAlignMins(), clientClass->WorldAlignMaxs(), QAngle( 0, 0, 0), 255,0,0,127, 4 );
  316. #else
  317. NDebugOverlay::Box( clientClass->GetAbsOrigin(), clientClass->WorldAlignMins(), clientClass->WorldAlignMaxs(), 0,0,255,127, 4 );
  318. #endif
  319. }
  320. #endif
  321. //=============================================================================
  322. // HPE_BEGIN:
  323. //=============================================================================
  324. #ifndef CLIENT_DLL
  325. // [pfreese] Track number player entities killed with this bullet
  326. int iPenetrationKills = 0;
  327. // [menglish] Increment the shots fired for this player
  328. CCS_GameStats.Event_ShotFired( this, GetActiveWeapon() );
  329. #endif
  330. //=============================================================================
  331. // HPE_END
  332. //=============================================================================
  333. bool bFirstHit = true;
  334. CBasePlayer *lastPlayerHit = NULL;
  335. if( sv_showplayerhitboxes.GetInt() > 0 )
  336. {
  337. CBasePlayer *lagPlayer = UTIL_PlayerByIndex( sv_showplayerhitboxes.GetInt() );
  338. if( lagPlayer )
  339. {
  340. #ifdef CLIENT_DLL
  341. lagPlayer->DrawClientHitboxes(4, true);
  342. #else
  343. lagPlayer->DrawServerHitboxes(4, true);
  344. #endif
  345. }
  346. }
  347. MDLCACHE_CRITICAL_SECTION();
  348. while ( fCurrentDamage > 0 )
  349. {
  350. Vector vecEnd = vecSrc + vecDir * flDistance;
  351. trace_t tr; // main enter bullet trace
  352. UTIL_TraceLineIgnoreTwoEntities( vecSrc, vecEnd, CS_MASK_SHOOT|CONTENTS_HITBOX, this, lastPlayerHit, COLLISION_GROUP_NONE, &tr );
  353. {
  354. CTraceFilterSkipTwoEntities filter( this, lastPlayerHit, COLLISION_GROUP_NONE );
  355. // Check for player hitboxes extending outside their collision bounds
  356. const float rayExtension = 40.0f;
  357. UTIL_ClipTraceToPlayers( vecSrc, vecEnd + vecDir * rayExtension, CS_MASK_SHOOT|CONTENTS_HITBOX, &filter, &tr );
  358. }
  359. lastPlayerHit = ToBasePlayer(tr.m_pEnt);
  360. if ( tr.fraction == 1.0f )
  361. break; // we didn't hit anything, stop tracing shoot
  362. #ifdef _DEBUG
  363. if ( bFirstHit )
  364. AddBulletStat( gpGlobals->realtime, VectorLength( vecSrc-tr.endpos), tr.endpos );
  365. #endif
  366. bFirstHit = false;
  367. #ifndef CLIENT_DLL
  368. //
  369. // Propogate a bullet impact event
  370. // @todo Add this for shotgun pellets (which dont go thru here)
  371. //
  372. IGameEvent * event = gameeventmanager->CreateEvent( "bullet_impact" );
  373. if ( event )
  374. {
  375. event->SetInt( "userid", GetUserID() );
  376. event->SetFloat( "x", tr.endpos.x );
  377. event->SetFloat( "y", tr.endpos.y );
  378. event->SetFloat( "z", tr.endpos.z );
  379. gameeventmanager->FireEvent( event );
  380. }
  381. #endif
  382. /************* MATERIAL DETECTION ***********/
  383. surfacedata_t *pSurfaceData = physprops->GetSurfaceData( tr.surface.surfaceProps );
  384. int iEnterMaterial = pSurfaceData->game.material;
  385. GetMaterialParameters( iEnterMaterial, flPenetrationModifier, flDamageModifier );
  386. bool hitGrate = tr.contents & CONTENTS_GRATE;
  387. // since some railings in de_inferno are CONTENTS_GRATE but CHAR_TEX_CONCRETE, we'll trust the
  388. // CONTENTS_GRATE and use a high damage modifier.
  389. if ( hitGrate )
  390. {
  391. // If we're a concrete grate (TOOLS/TOOLSINVISIBLE texture) allow more penetrating power.
  392. flPenetrationModifier = 1.0f;
  393. flDamageModifier = 0.99f;
  394. }
  395. #ifdef CLIENT_DLL
  396. if ( sv_showimpacts.GetInt() == 1 || sv_showimpacts.GetInt() == 2 )
  397. {
  398. // draw red client impact markers
  399. debugoverlay->AddBoxOverlay( tr.endpos, Vector(-2,-2,-2), Vector(2,2,2), QAngle( 0, 0, 0), 255,0,0,127, 4 );
  400. if ( tr.m_pEnt && tr.m_pEnt->IsPlayer() )
  401. {
  402. C_BasePlayer *player = ToBasePlayer( tr.m_pEnt );
  403. player->DrawClientHitboxes( 4, true );
  404. }
  405. }
  406. #else
  407. if ( sv_showimpacts.GetInt() == 1 || sv_showimpacts.GetInt() == 3 )
  408. {
  409. // draw blue server impact markers
  410. NDebugOverlay::Box( tr.endpos, Vector(-2,-2,-2), Vector(2,2,2), 0,0,255,127, 4 );
  411. if ( tr.m_pEnt && tr.m_pEnt->IsPlayer() )
  412. {
  413. CBasePlayer *player = ToBasePlayer( tr.m_pEnt );
  414. player->DrawServerHitboxes( 4, true );
  415. }
  416. }
  417. #endif
  418. //calculate the damage based on the distance the bullet travelled.
  419. flCurrentDistance += tr.fraction * flDistance;
  420. fCurrentDamage *= pow (flRangeModifier, (flCurrentDistance / 500));
  421. // check if we reach penetration distance, no more penetrations after that
  422. if (flCurrentDistance > flPenetrationDistance && iPenetration > 0)
  423. iPenetration = 0;
  424. #ifndef CLIENT_DLL
  425. // This just keeps track of sounds for AIs (it doesn't play anything).
  426. CSoundEnt::InsertSound( SOUND_BULLET_IMPACT, tr.endpos, 400, 0.2f, this );
  427. #endif
  428. int iDamageType = DMG_BULLET | DMG_NEVERGIB;
  429. if( bDoEffects )
  430. {
  431. // See if the bullet ended up underwater + started out of the water
  432. if ( enginetrace->GetPointContents( tr.endpos ) & (CONTENTS_WATER|CONTENTS_SLIME) )
  433. {
  434. trace_t waterTrace;
  435. UTIL_TraceLine( vecSrc, tr.endpos, (MASK_SHOT|CONTENTS_WATER|CONTENTS_SLIME), this, COLLISION_GROUP_NONE, &waterTrace );
  436. if( waterTrace.allsolid != 1 )
  437. {
  438. CEffectData data;
  439. data.m_vOrigin = waterTrace.endpos;
  440. data.m_vNormal = waterTrace.plane.normal;
  441. data.m_flScale = random->RandomFloat( 8, 12 );
  442. if ( waterTrace.contents & CONTENTS_SLIME )
  443. {
  444. data.m_fFlags |= FX_WATER_IN_SLIME;
  445. }
  446. DispatchEffect( "gunshotsplash", data );
  447. }
  448. }
  449. else
  450. {
  451. //Do Regular hit effects
  452. // Don't decal nodraw surfaces
  453. if ( !( tr.surface.flags & (SURF_SKY|SURF_NODRAW|SURF_HINT|SURF_SKIP) ) )
  454. {
  455. CBaseEntity *pEntity = tr.m_pEnt;
  456. if ( !( !friendlyfire.GetBool() && pEntity && pEntity->GetTeamNumber() == GetTeamNumber() ) )
  457. {
  458. UTIL_ImpactTrace( &tr, iDamageType );
  459. }
  460. }
  461. }
  462. } // bDoEffects
  463. // add damage to entity that we hit
  464. #ifndef CLIENT_DLL
  465. ClearMultiDamage();
  466. //=============================================================================
  467. // HPE_BEGIN:
  468. // [pfreese] Check if enemy players were killed by this bullet, and if so,
  469. // add them to the iPenetrationKills count
  470. //=============================================================================
  471. CBaseEntity *pEntity = tr.m_pEnt;
  472. CTakeDamageInfo info( pevAttacker, pevAttacker, fCurrentDamage, iDamageType );
  473. CalculateBulletDamageForce( &info, iBulletType, vecDir, tr.endpos );
  474. pEntity->DispatchTraceAttack( info, vecDir, &tr );
  475. bool bWasAlive = pEntity->IsAlive();
  476. TraceAttackToTriggers( info, tr.startpos, tr.endpos, vecDir );
  477. ApplyMultiDamage();
  478. if (bWasAlive && !pEntity->IsAlive() && pEntity->IsPlayer() && pEntity->GetTeamNumber() != GetTeamNumber())
  479. {
  480. ++iPenetrationKills;
  481. }
  482. //=============================================================================
  483. // HPE_END
  484. //=============================================================================
  485. #endif
  486. // check if bullet can penetrate another entity
  487. if ( iPenetration == 0 && !hitGrate )
  488. break; // no, stop
  489. // If we hit a grate with iPenetration == 0, stop on the next thing we hit
  490. if ( iPenetration < 0 )
  491. break;
  492. Vector penetrationEnd;
  493. // try to penetrate object, maximum penetration is 128 inch
  494. if ( !TraceToExit( tr.endpos, vecDir, penetrationEnd, 24, 128 ) )
  495. break;
  496. // find exact penetration exit
  497. trace_t exitTr;
  498. UTIL_TraceLine( penetrationEnd, tr.endpos, CS_MASK_SHOOT|CONTENTS_HITBOX, NULL, &exitTr );
  499. if( exitTr.m_pEnt != tr.m_pEnt && exitTr.m_pEnt != NULL )
  500. {
  501. // something was blocking, trace again
  502. UTIL_TraceLine( penetrationEnd, tr.endpos, CS_MASK_SHOOT|CONTENTS_HITBOX, exitTr.m_pEnt, COLLISION_GROUP_NONE, &exitTr );
  503. }
  504. // get material at exit point
  505. pSurfaceData = physprops->GetSurfaceData( exitTr.surface.surfaceProps );
  506. int iExitMaterial = pSurfaceData->game.material;
  507. hitGrate = hitGrate && ( exitTr.contents & CONTENTS_GRATE );
  508. // if enter & exit point is wood or metal we assume this is
  509. // a hollow crate or barrel and give a penetration bonus
  510. if ( iEnterMaterial == iExitMaterial )
  511. {
  512. if( iExitMaterial == CHAR_TEX_WOOD ||
  513. iExitMaterial == CHAR_TEX_METAL )
  514. {
  515. flPenetrationModifier *= 2;
  516. }
  517. }
  518. float flTraceDistance = VectorLength( exitTr.endpos - tr.endpos );
  519. // check if bullet has enough power to penetrate this distance for this material
  520. if ( flTraceDistance > ( flPenetrationPower * flPenetrationModifier ) )
  521. break; // bullet hasn't enough power to penetrate this distance
  522. // penetration was successful
  523. // bullet did penetrate object, exit Decal
  524. if ( bDoEffects )
  525. {
  526. UTIL_ImpactTrace( &exitTr, iDamageType );
  527. }
  528. //setup new start end parameters for successive trace
  529. flPenetrationPower -= flTraceDistance / flPenetrationModifier;
  530. flCurrentDistance += flTraceDistance;
  531. // NDebugOverlay::Box( exitTr.endpos, Vector(-2,-2,-2), Vector(2,2,2), 0,255,0,127, 8 );
  532. vecSrc = exitTr.endpos;
  533. flDistance = (flDistance - flCurrentDistance) * 0.5;
  534. // reduce damage power each time we hit something other than a grate
  535. fCurrentDamage *= flDamageModifier;
  536. // reduce penetration counter
  537. iPenetration--;
  538. }
  539. #ifndef CLIENT_DLL
  540. //=============================================================================
  541. // HPE_BEGIN:
  542. // [pfreese] If we killed at least two enemies with a single bullet, award the
  543. // TWO_WITH_ONE_SHOT achievement
  544. //=============================================================================
  545. if (iPenetrationKills >= 2)
  546. {
  547. AwardAchievement(CSKillTwoWithOneShot);
  548. }
  549. //=============================================================================
  550. // HPE_END
  551. //=============================================================================
  552. #endif
  553. }
  554. void CCSPlayer::UpdateStepSound( surfacedata_t *psurface, const Vector &vecOrigin, const Vector &vecVelocity )
  555. {
  556. float speedSqr = vecVelocity.AsVector2D().LengthSqr();
  557. // the fastest walk is 135 ( scout ), see CCSGameMovement::CheckParameters()
  558. if ( speedSqr < 150.0 * 150.0 )
  559. return; // player is not running, no footsteps
  560. BaseClass::UpdateStepSound( psurface, vecOrigin, vecVelocity );
  561. }
  562. // GOOSEMAN : Kick the view..
  563. void CCSPlayer::KickBack( float up_base, float lateral_base, float up_modifier, float lateral_modifier, float up_max, float lateral_max, int direction_change )
  564. {
  565. float flKickUp;
  566. float flKickLateral;
  567. if (m_iShotsFired == 1) // This is the first round fired
  568. {
  569. flKickUp = up_base;
  570. flKickLateral = lateral_base;
  571. }
  572. else
  573. {
  574. flKickUp = up_base + m_iShotsFired*up_modifier;
  575. flKickLateral = lateral_base + m_iShotsFired*lateral_modifier;
  576. }
  577. QAngle angle = GetPunchAngle();
  578. angle.x -= flKickUp;
  579. if ( angle.x < -1 * up_max )
  580. angle.x = -1 * up_max;
  581. if ( m_iDirection == 1 )
  582. {
  583. angle.y += flKickLateral;
  584. if (angle.y > lateral_max)
  585. angle.y = lateral_max;
  586. }
  587. else
  588. {
  589. angle.y -= flKickLateral;
  590. if ( angle.y < -1 * lateral_max )
  591. angle.y = -1 * lateral_max;
  592. }
  593. if ( !SharedRandomInt( "KickBack", 0, direction_change ) )
  594. m_iDirection = 1 - m_iDirection;
  595. SetPunchAngle( angle );
  596. }
  597. bool CCSPlayer::CanMove() const
  598. {
  599. // When we're in intro camera mode, it's important to return false here
  600. // so our physics object doesn't fall out of the world.
  601. if ( GetMoveType() == MOVETYPE_NONE )
  602. return false;
  603. if ( IsObserver() )
  604. return true; // observers can move all the time
  605. bool bValidMoveState = (State_Get() == STATE_ACTIVE || State_Get() == STATE_OBSERVER_MODE);
  606. if ( m_bIsDefusing || !bValidMoveState || CSGameRules()->IsFreezePeriod() )
  607. {
  608. return false;
  609. }
  610. else
  611. {
  612. // Can't move while planting C4.
  613. CC4 *pC4 = dynamic_cast< CC4* >( GetActiveWeapon() );
  614. if ( pC4 && pC4->m_bStartedArming )
  615. return false;
  616. return true;
  617. }
  618. }
  619. void CCSPlayer::OnJump( float fImpulse )
  620. {
  621. CWeaponCSBase* pActiveWeapon = GetActiveCSWeapon();
  622. if ( pActiveWeapon != NULL )
  623. pActiveWeapon->OnJump(fImpulse);
  624. }
  625. void CCSPlayer::OnLand( float fVelocity )
  626. {
  627. CWeaponCSBase* pActiveWeapon = GetActiveCSWeapon();
  628. if ( pActiveWeapon != NULL )
  629. pActiveWeapon->OnLand(fVelocity);
  630. }
  631. //-------------------------------------------------------------------------------------------------------------------------------
  632. /**
  633. * Track the last time we were on a ladder, along with the ladder's normal and where we
  634. * were grabbing it, so we don't reach behind us and grab it again as we are trying to
  635. * dismount.
  636. */
  637. void CCSPlayer::SurpressLadderChecks( const Vector& pos, const Vector& normal )
  638. {
  639. m_ladderSurpressionTimer.Start( 1.0f );
  640. m_lastLadderPos = pos;
  641. m_lastLadderNormal = normal;
  642. }
  643. //-------------------------------------------------------------------------------------------------------------------------------
  644. /**
  645. * Prevent us from re-grabbing the same ladder we were just on:
  646. * - if the timer is elapsed, let us grab again
  647. * - if the normal is different, let us grab
  648. * - if the 2D pos is very different, let us grab, since it's probably a different ladder
  649. */
  650. bool CCSPlayer::CanGrabLadder( const Vector& pos, const Vector& normal )
  651. {
  652. if ( m_ladderSurpressionTimer.GetRemainingTime() <= 0.0f )
  653. {
  654. return true;
  655. }
  656. const float MaxDist = 64.0f;
  657. if ( pos.AsVector2D().DistToSqr( m_lastLadderPos.AsVector2D() ) < MaxDist * MaxDist )
  658. {
  659. return false;
  660. }
  661. if ( normal != m_lastLadderNormal )
  662. {
  663. return true;
  664. }
  665. return false;
  666. }
  667. void CCSPlayer::SetAnimation( PLAYER_ANIM playerAnim )
  668. {
  669. // In CS, its CPlayerAnimState object manages ALL the animation state.
  670. return;
  671. }
  672. CWeaponCSBase* CCSPlayer::CSAnim_GetActiveWeapon()
  673. {
  674. return GetActiveCSWeapon();
  675. }
  676. bool CCSPlayer::CSAnim_CanMove()
  677. {
  678. return CanMove();
  679. }
  680. //--------------------------------------------------------------------------------------------------------------
  681. #define MATERIAL_NAME_LENGTH 16
  682. #ifdef GAME_DLL
  683. class CFootstepControl : public CBaseTrigger
  684. {
  685. public:
  686. DECLARE_CLASS( CFootstepControl, CBaseTrigger );
  687. DECLARE_DATADESC();
  688. DECLARE_SERVERCLASS();
  689. virtual int UpdateTransmitState( void );
  690. virtual void Spawn( void );
  691. CNetworkVar( string_t, m_source );
  692. CNetworkVar( string_t, m_destination );
  693. };
  694. LINK_ENTITY_TO_CLASS( func_footstep_control, CFootstepControl );
  695. BEGIN_DATADESC( CFootstepControl )
  696. DEFINE_KEYFIELD( m_source, FIELD_STRING, "Source" ),
  697. DEFINE_KEYFIELD( m_destination, FIELD_STRING, "Destination" ),
  698. END_DATADESC()
  699. IMPLEMENT_SERVERCLASS_ST( CFootstepControl, DT_FootstepControl )
  700. SendPropStringT( SENDINFO(m_source) ),
  701. SendPropStringT( SENDINFO(m_destination) ),
  702. END_SEND_TABLE()
  703. int CFootstepControl::UpdateTransmitState( void )
  704. {
  705. return SetTransmitState( FL_EDICT_ALWAYS );
  706. }
  707. void CFootstepControl::Spawn( void )
  708. {
  709. InitTrigger();
  710. }
  711. #else
  712. //--------------------------------------------------------------------------------------------------------------
  713. class C_FootstepControl : public C_BaseEntity
  714. {
  715. public:
  716. DECLARE_CLASS( C_FootstepControl, C_BaseEntity );
  717. DECLARE_CLIENTCLASS();
  718. C_FootstepControl( void );
  719. ~C_FootstepControl();
  720. char m_source[MATERIAL_NAME_LENGTH];
  721. char m_destination[MATERIAL_NAME_LENGTH];
  722. };
  723. IMPLEMENT_CLIENTCLASS_DT(C_FootstepControl, DT_FootstepControl, CFootstepControl)
  724. RecvPropString( RECVINFO(m_source) ),
  725. RecvPropString( RECVINFO(m_destination) ),
  726. END_RECV_TABLE()
  727. CUtlVector< C_FootstepControl * > s_footstepControllers;
  728. C_FootstepControl::C_FootstepControl( void )
  729. {
  730. s_footstepControllers.AddToTail( this );
  731. }
  732. C_FootstepControl::~C_FootstepControl()
  733. {
  734. s_footstepControllers.FindAndRemove( this );
  735. }
  736. surfacedata_t * CCSPlayer::GetFootstepSurface( const Vector &origin, const char *surfaceName )
  737. {
  738. for ( int i=0; i<s_footstepControllers.Count(); ++i )
  739. {
  740. C_FootstepControl *control = s_footstepControllers[i];
  741. if ( FStrEq( control->m_source, surfaceName ) )
  742. {
  743. if ( control->CollisionProp()->IsPointInBounds( origin ) )
  744. {
  745. return physprops->GetSurfaceData( physprops->GetSurfaceIndex( control->m_destination ) );
  746. }
  747. }
  748. }
  749. return physprops->GetSurfaceData( physprops->GetSurfaceIndex( surfaceName ) );
  750. }
  751. #endif