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.

1917 lines
50 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Laser Rifle & Shield combo
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "in_buttons.h"
  9. #include "takedamageinfo.h"
  10. #include "weapon_csbase.h"
  11. #include "ammodef.h"
  12. #include "cs_gamerules.h"
  13. #define ALLOW_WEAPON_SPREAD_DISPLAY 0
  14. #if defined( CLIENT_DLL )
  15. #include "vgui/ISurface.h"
  16. #include "vgui_controls/Controls.h"
  17. #include "c_cs_player.h"
  18. #include "hud_crosshair.h"
  19. #include "c_te_effect_dispatch.h"
  20. #include "c_te_legacytempents.h"
  21. extern IVModelInfoClient* modelinfo;
  22. #else
  23. #include "cs_player.h"
  24. #include "te_effect_dispatch.h"
  25. #include "KeyValues.h"
  26. #include "cs_ammodef.h"
  27. extern IVModelInfo* modelinfo;
  28. #endif
  29. ConVar weapon_accuracy_model( "weapon_accuracy_model", "2", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY | FCVAR_ARCHIVE );
  30. // ----------------------------------------------------------------------------- //
  31. // Global functions.
  32. // ----------------------------------------------------------------------------- //
  33. struct WeaponAliasTranslationInfoStruct
  34. {
  35. const char* alias;
  36. const char* translatedAlias;
  37. };
  38. static const WeaponAliasTranslationInfoStruct s_WeaponAliasTranslationInfo[] =
  39. {
  40. { "cv47", "ak47" },
  41. { "defender", "galil" },
  42. { "krieg552", "sg552" },
  43. { "magnum", "awp" },
  44. { "d3au1", "g3sg1" },
  45. { "clarion", "famas" },
  46. { "bullpup", "aug" },
  47. { "krieg550", "sg550" },
  48. { "9x19mm", "glock" },
  49. { "km45", "usp" },
  50. { "228compact", "p228" },
  51. { "nighthawk", "deagle" },
  52. { "elites", "elite" },
  53. { "fn57", "fiveseven" },
  54. { "12gauge", "m3" },
  55. { "autoshotgun", "xm1014" },
  56. { "mp", "tmp" },
  57. { "smg", "mp5navy" },
  58. { "mp5", "mp5navy" },
  59. { "c90", "p90" },
  60. { "vest", "kevlar" },
  61. { "vesthelm", "assaultsuit" },
  62. { "smokegrenade", "sgren" },
  63. { "smokegrenade", "sgren" },
  64. { "nvgs", "nightvision" },
  65. { "", "" } // this needs to be last
  66. };
  67. struct WeaponAliasInfo
  68. {
  69. CSWeaponID id;
  70. const char* alias;
  71. };
  72. WeaponAliasInfo s_weaponAliasInfo[] =
  73. {
  74. { WEAPON_P228, "p228" },
  75. { WEAPON_GLOCK, "glock" },
  76. { WEAPON_SCOUT, "scout" },
  77. { WEAPON_XM1014, "xm1014" },
  78. { WEAPON_MAC10, "mac10" },
  79. { WEAPON_AUG, "aug" },
  80. { WEAPON_ELITE, "elite" },
  81. { WEAPON_FIVESEVEN, "fiveseven" },
  82. { WEAPON_UMP45, "ump45" },
  83. { WEAPON_SG550, "sg550" },
  84. { WEAPON_GALIL, "galil" },
  85. { WEAPON_FAMAS, "famas" },
  86. { WEAPON_USP, "usp" },
  87. { WEAPON_AWP, "awp" },
  88. { WEAPON_MP5NAVY, "mp5navy" },
  89. { WEAPON_M249, "m249" },
  90. { WEAPON_M3, "m3" },
  91. { WEAPON_M4A1, "m4a1" },
  92. { WEAPON_TMP, "tmp" },
  93. { WEAPON_G3SG1, "g3sg1" },
  94. { WEAPON_DEAGLE, "deagle" },
  95. { WEAPON_SG552, "sg552" },
  96. { WEAPON_AK47, "ak47" },
  97. { WEAPON_P90, "p90" },
  98. { WEAPON_KNIFE, "knife" },
  99. { WEAPON_C4, "c4" },
  100. { WEAPON_FLASHBANG, "flashbang" },
  101. { WEAPON_SMOKEGRENADE, "smokegrenade" },
  102. { WEAPON_SMOKEGRENADE, "sgren" },
  103. { WEAPON_HEGRENADE, "hegrenade" },
  104. { WEAPON_HEGRENADE, "hegren" },
  105. // not sure any of these are needed
  106. { WEAPON_SHIELDGUN, "shield" },
  107. { WEAPON_SHIELDGUN, "shieldgun" },
  108. { WEAPON_KEVLAR, "kevlar" },
  109. { WEAPON_ASSAULTSUIT, "assaultsuit" },
  110. { WEAPON_NVG, "nightvision" },
  111. { WEAPON_NVG, "nvg" },
  112. { WEAPON_NONE, "none" },
  113. };
  114. bool IsAmmoType( int iAmmoType, const char *pAmmoName )
  115. {
  116. return GetAmmoDef()->Index( pAmmoName ) == iAmmoType;
  117. }
  118. //--------------------------------------------------------------------------------------------------------
  119. //
  120. // Given an alias, return the translated alias.
  121. //
  122. const char * GetTranslatedWeaponAlias( const char *szAlias )
  123. {
  124. for ( int i = 0; i < ARRAYSIZE(s_WeaponAliasTranslationInfo); ++i )
  125. {
  126. if ( Q_stricmp(s_WeaponAliasTranslationInfo[i].alias, szAlias) == 0 )
  127. {
  128. return s_WeaponAliasTranslationInfo[i].translatedAlias;
  129. }
  130. }
  131. return szAlias;
  132. }
  133. //--------------------------------------------------------------------------------------------------------
  134. //
  135. // Given a translated alias, return the alias.
  136. //
  137. const char * GetWeaponAliasFromTranslated(const char *translatedAlias)
  138. {
  139. int i = 0;
  140. const WeaponAliasTranslationInfoStruct *info = &(s_WeaponAliasTranslationInfo[i]);
  141. while (info->alias[0] != 0)
  142. {
  143. if (Q_stricmp(translatedAlias, info->translatedAlias) == 0)
  144. {
  145. return info->alias;
  146. }
  147. info = &(s_WeaponAliasTranslationInfo[++i]);
  148. }
  149. return translatedAlias;
  150. }
  151. //--------------------------------------------------------------------------------------------------------
  152. //
  153. // Given an alias, return the associated weapon ID
  154. //
  155. CSWeaponID AliasToWeaponID( const char *szAlias )
  156. {
  157. if ( szAlias )
  158. {
  159. for ( int i=0; i < ARRAYSIZE(s_weaponAliasInfo); ++i)
  160. {
  161. if ( Q_stricmp( s_weaponAliasInfo[i].alias, szAlias) == 0 )
  162. return s_weaponAliasInfo[i].id;
  163. }
  164. }
  165. return WEAPON_NONE;
  166. }
  167. //--------------------------------------------------------------------------------------------------------
  168. //
  169. // Given a weapon ID, return its alias
  170. //
  171. const char *WeaponIDToAlias( int id )
  172. {
  173. for ( int i=0; i < ARRAYSIZE(s_weaponAliasInfo); ++i)
  174. {
  175. if ( s_weaponAliasInfo[i].id == id )
  176. return s_weaponAliasInfo[i].alias;
  177. }
  178. return NULL;
  179. }
  180. //--------------------------------------------------------------------------------------------------------
  181. //
  182. // Return true if given weapon ID is a primary weapon
  183. //
  184. bool IsPrimaryWeapon( CSWeaponID id )
  185. {
  186. const CCSWeaponInfo* pWeaponInfo = GetWeaponInfo( id );
  187. if ( pWeaponInfo )
  188. {
  189. return pWeaponInfo->iSlot == WEAPON_SLOT_RIFLE;
  190. }
  191. return false;
  192. }
  193. //--------------------------------------------------------------------------------------------------------
  194. //
  195. // Return true if given weapon ID is a secondary weapon
  196. //
  197. bool IsSecondaryWeapon( CSWeaponID id )
  198. {
  199. const CCSWeaponInfo* pWeaponInfo = GetWeaponInfo( id );
  200. if ( pWeaponInfo )
  201. return pWeaponInfo->iSlot == WEAPON_SLOT_PISTOL;
  202. return false;
  203. }
  204. #ifdef CLIENT_DLL
  205. int GetShellForAmmoType( const char *ammoname )
  206. {
  207. if ( !Q_strcmp( BULLET_PLAYER_762MM, ammoname ) )
  208. return CS_SHELL_762NATO;
  209. if ( !Q_strcmp( BULLET_PLAYER_556MM, ammoname ) )
  210. return CS_SHELL_556;
  211. if ( !Q_strcmp( BULLET_PLAYER_338MAG, ammoname ) )
  212. return CS_SHELL_338MAG;
  213. if ( !Q_strcmp( BULLET_PLAYER_BUCKSHOT, ammoname ) )
  214. return CS_SHELL_12GAUGE;
  215. if ( !Q_strcmp( BULLET_PLAYER_57MM, ammoname ) )
  216. return CS_SHELL_57;
  217. // default 9 mm
  218. return CS_SHELL_9MM;
  219. }
  220. #endif
  221. // ----------------------------------------------------------------------------- //
  222. // CWeaponCSBase tables.
  223. // ----------------------------------------------------------------------------- //
  224. IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCSBase, DT_WeaponCSBase )
  225. BEGIN_NETWORK_TABLE( CWeaponCSBase, DT_WeaponCSBase )
  226. #if !defined( CLIENT_DLL )
  227. SendPropInt( SENDINFO( m_weaponMode ), 1, SPROP_UNSIGNED ),
  228. SendPropFloat(SENDINFO(m_fAccuracyPenalty) ),
  229. // world weapon models have no aminations
  230. SendPropExclude( "DT_AnimTimeMustBeFirst", "m_flAnimTime" ),
  231. SendPropExclude( "DT_BaseAnimating", "m_nSequence" ),
  232. // SendPropExclude( "DT_LocalActiveWeaponData", "m_flTimeWeaponIdle" ),
  233. #else
  234. RecvPropInt( RECVINFO( m_weaponMode ) ),
  235. RecvPropFloat( RECVINFO(m_fAccuracyPenalty)),
  236. #endif
  237. END_NETWORK_TABLE()
  238. #if defined(CLIENT_DLL)
  239. BEGIN_PREDICTION_DATA( CWeaponCSBase )
  240. DEFINE_PRED_FIELD( m_flTimeWeaponIdle, FIELD_FLOAT, FTYPEDESC_OVERRIDE | FTYPEDESC_NOERRORCHECK ),
  241. DEFINE_PRED_FIELD( m_flNextPrimaryAttack, FIELD_FLOAT, FTYPEDESC_OVERRIDE | FTYPEDESC_NOERRORCHECK ),
  242. DEFINE_PRED_FIELD( m_flNextSecondaryAttack, FIELD_FLOAT, FTYPEDESC_OVERRIDE | FTYPEDESC_NOERRORCHECK ),
  243. DEFINE_PRED_FIELD( m_bDelayFire, FIELD_BOOLEAN, 0 ),
  244. DEFINE_PRED_FIELD( m_flAccuracy, FIELD_FLOAT, 0 ),
  245. DEFINE_PRED_FIELD( m_weaponMode, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  246. DEFINE_PRED_FIELD_TOL( m_fAccuracyPenalty, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, 0.00005f ),
  247. END_PREDICTION_DATA()
  248. #endif
  249. LINK_ENTITY_TO_CLASS( weapon_cs_base, CWeaponCSBase );
  250. #ifdef GAME_DLL
  251. BEGIN_DATADESC( CWeaponCSBase )
  252. //DEFINE_FUNCTION( DefaultTouch ),
  253. DEFINE_THINKFUNC( FallThink )
  254. END_DATADESC()
  255. #endif
  256. #if defined( CLIENT_DLL )
  257. ConVar cl_crosshaircolor( "cl_crosshaircolor", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Set crosshair color: 0=green, 1=red, 2=blue, 3=yellow, 4=cyan, 5=custom" );
  258. ConVar cl_dynamiccrosshair( "cl_dynamiccrosshair", "1", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Enables dynamic crosshair; 0=off, 1=normal behavior (based on actual weapon accuracy), 2=legacy simulated dynamic behavior, 3=legacy simulated static behavior" );
  259. ConVar cl_crosshairspreadscale( "cl_crosshairspreadscale", "0.3", FCVAR_CLIENTDLL | FCVAR_ARCHIVE);
  260. ConVar cl_scalecrosshair( "cl_scalecrosshair", "1", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Enable crosshair scaling (deprecated)" );
  261. ConVar cl_crosshairscale( "cl_crosshairscale", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Crosshair scaling factor (deprecated)" );
  262. ConVar cl_crosshairalpha( "cl_crosshairalpha", "200", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
  263. ConVar cl_crosshairusealpha( "cl_crosshairusealpha", "1", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
  264. ConVar cl_crosshairsize( "cl_crosshairsize", "5", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
  265. ConVar cl_crosshairthickness( "cl_crosshairthickness", "0.5", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
  266. ConVar cl_crosshairdot( "cl_crosshairdot", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
  267. ConVar cl_crosshaircolor_r( "cl_crosshaircolor_r", "50", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
  268. ConVar cl_crosshaircolor_g( "cl_crosshaircolor_g", "250", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
  269. ConVar cl_crosshaircolor_b( "cl_crosshaircolor_b", "50", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
  270. #if ALLOW_WEAPON_SPREAD_DISPLAY
  271. ConVar weapon_debug_spread_show( "weapon_debug_spread_show", "0", FCVAR_CLIENTDLL | FCVAR_DEVELOPMENTONLY, "Enables display of weapon accuracy; 1: show accuracy box, 2: show box with recoil offset" );
  272. ConVar weapon_debug_spread_gap( "weapon_debug_spread_gap", "0.67", FCVAR_CLIENTDLL | FCVAR_DEVELOPMENTONLY );
  273. #endif
  274. // [paquin] make sure crosshair scales independent of frame rate
  275. // unless legacy cvar is set
  276. ConVar cl_legacy_crosshair_recoil( "cl_legacy_crosshair_recoil", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Enable legacy framerate dependent crosshair recoil");
  277. // use old scaling behavior
  278. ConVar cl_legacy_crosshair_scale( "cl_legacy_crosshair_scale", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Enable legacy crosshair scaling");
  279. void DrawCrosshairRect( int x0, int y0, int x1, int y1, bool bAdditive )
  280. {
  281. if ( bAdditive )
  282. {
  283. vgui::surface()->DrawTexturedRect( x0, y0, x1, y1 );
  284. }
  285. else
  286. {
  287. // Alpha-blended crosshair
  288. vgui::surface()->DrawFilledRect( x0, y0, x1, y1 );
  289. }
  290. }
  291. #endif
  292. // must be included after the above macros
  293. #ifndef CLIENT_DLL
  294. #include "cs_bot.h"
  295. #endif
  296. // ----------------------------------------------------------------------------- //
  297. // CWeaponCSBase implementation.
  298. // ----------------------------------------------------------------------------- //
  299. CWeaponCSBase::CWeaponCSBase()
  300. {
  301. SetPredictionEligible( true );
  302. m_bDelayFire = true;
  303. m_nextPrevOwnerTouchTime = 0.0;
  304. m_prevOwner = NULL;
  305. AddSolidFlags( FSOLID_TRIGGER ); // Nothing collides with these but it gets touches.
  306. #ifdef CLIENT_DLL
  307. m_iCrosshairTextureID = 0;
  308. #else
  309. m_iDefaultExtraAmmo = 0;
  310. #endif
  311. m_fAccuracyPenalty = 0.0f;
  312. m_weaponMode = Primary_Mode;
  313. }
  314. #ifndef CLIENT_DLL
  315. bool CWeaponCSBase::KeyValue( const char *szKeyName, const char *szValue )
  316. {
  317. if ( !BaseClass::KeyValue( szKeyName, szValue ) )
  318. {
  319. if ( FStrEq( szKeyName, "ammo" ) )
  320. {
  321. int bullets = atoi( szValue );
  322. if ( bullets < 0 )
  323. return false;
  324. m_iDefaultExtraAmmo = bullets;
  325. return true;
  326. }
  327. }
  328. return false;
  329. }
  330. #endif
  331. bool CWeaponCSBase::IsPredicted() const
  332. {
  333. return true;
  334. }
  335. bool CWeaponCSBase::IsPistol() const
  336. {
  337. return GetCSWpnData().m_WeaponType == WEAPONTYPE_PISTOL;
  338. }
  339. bool CWeaponCSBase::IsFullAuto() const
  340. {
  341. return GetCSWpnData().m_bFullAuto;
  342. }
  343. bool CWeaponCSBase::PlayEmptySound()
  344. {
  345. //MIKETODO: certain weapons should override this to make it empty:
  346. // C4
  347. // Flashbang
  348. // HE Grenade
  349. // Smoke grenade
  350. CPASAttenuationFilter filter( this );
  351. filter.UsePredictionRules();
  352. if ( IsPistol() )
  353. {
  354. EmitSound( filter, entindex(), "Default.ClipEmpty_Pistol" );
  355. }
  356. else
  357. {
  358. EmitSound( filter, entindex(), "Default.ClipEmpty_Rifle" );
  359. }
  360. return 0;
  361. }
  362. CCSPlayer* CWeaponCSBase::GetPlayerOwner() const
  363. {
  364. return dynamic_cast< CCSPlayer* >( GetOwner() );
  365. }
  366. //=============================================================================
  367. // HPE_BEGIN:
  368. //=============================================================================
  369. //[dwenger] Accessors for the prior owner list
  370. void CWeaponCSBase::AddToPriorOwnerList(CCSPlayer* pPlayer)
  371. {
  372. if ( !IsAPriorOwner( pPlayer ) )
  373. {
  374. // Add player to prior owner list
  375. m_PriorOwners.AddToTail( pPlayer );
  376. }
  377. }
  378. bool CWeaponCSBase::IsAPriorOwner(CCSPlayer* pPlayer)
  379. {
  380. return (m_PriorOwners.Find( pPlayer ) != -1);
  381. }
  382. //=============================================================================
  383. // HPE_END
  384. //=============================================================================
  385. void CWeaponCSBase::SecondaryAttack( void )
  386. {
  387. #ifndef CLIENT_DLL
  388. CCSPlayer *pPlayer = GetPlayerOwner();
  389. if ( !pPlayer )
  390. return;
  391. if ( pPlayer->HasShield() == false )
  392. BaseClass::SecondaryAttack();
  393. else
  394. {
  395. pPlayer->SetShieldDrawnState( !pPlayer->IsShieldDrawn() );
  396. if ( pPlayer->IsShieldDrawn() )
  397. SendWeaponAnim( ACT_SHIELD_UP );
  398. else
  399. SendWeaponAnim( ACT_SHIELD_DOWN );
  400. m_flNextSecondaryAttack = gpGlobals->curtime + 0.4;
  401. m_flNextPrimaryAttack = gpGlobals->curtime + 0.4;
  402. }
  403. #endif
  404. }
  405. bool CWeaponCSBase::SendWeaponAnim( int iActivity )
  406. {
  407. #ifdef CS_SHIELD_ENABLED
  408. CCSPlayer *pPlayer = GetPlayerOwner();
  409. if ( pPlayer && pPlayer->HasShield() )
  410. {
  411. CBaseViewModel *vm = pPlayer->GetViewModel( 1 );
  412. if ( vm == NULL )
  413. return false;
  414. vm->SetWeaponModel( SHIELD_VIEW_MODEL, this );
  415. int idealSequence = vm->SelectWeightedSequence( (Activity)iActivity );
  416. if ( idealSequence >= 0 )
  417. {
  418. vm->SendViewModelMatchingSequence( idealSequence );
  419. }
  420. }
  421. #endif
  422. return BaseClass::SendWeaponAnim( iActivity );
  423. }
  424. void CWeaponCSBase::ItemPostFrame()
  425. {
  426. CCSPlayer *pPlayer = GetPlayerOwner();
  427. if ( !pPlayer )
  428. return;
  429. UpdateAccuracyPenalty();
  430. UpdateShieldState();
  431. if ((m_bInReload) && (pPlayer->m_flNextAttack <= gpGlobals->curtime))
  432. {
  433. // complete the reload.
  434. int j = MIN( GetMaxClip1() - m_iClip1, pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) );
  435. // Add them to the clip
  436. m_iClip1 += j;
  437. pPlayer->RemoveAmmo( j, m_iPrimaryAmmoType );
  438. m_bInReload = false;
  439. }
  440. if ((pPlayer->m_nButtons & IN_ATTACK2) && (m_flNextSecondaryAttack <= gpGlobals->curtime))
  441. {
  442. if ( pPlayer->HasShield() )
  443. CWeaponCSBase::SecondaryAttack();
  444. else
  445. SecondaryAttack();
  446. pPlayer->m_nButtons &= ~IN_ATTACK2;
  447. }
  448. else if ((pPlayer->m_nButtons & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime ))
  449. {
  450. if ( CSGameRules()->IsFreezePeriod() ) // Can't shoot during the freeze period
  451. return;
  452. if ( pPlayer->m_bIsDefusing )
  453. return;
  454. if ( pPlayer->State_Get() != STATE_ACTIVE )
  455. return;
  456. if ( pPlayer->IsShieldDrawn() )
  457. return;
  458. // we have to reset the FireOnEmpty flag before we can fire on an empty clip
  459. if ( m_iClip1 == 0 && !m_bFireOnEmpty )
  460. return;
  461. // don't repeat fire if this is not a full auto weapon
  462. if ( pPlayer->m_iShotsFired > 0 && !IsFullAuto() )
  463. return;
  464. #if !defined(CLIENT_DLL)
  465. // allow the bots to react to the gunfire
  466. if ( GetCSWpnData().m_WeaponType != WEAPONTYPE_GRENADE )
  467. {
  468. IGameEvent * event = gameeventmanager->CreateEvent( (HasAmmo()) ? "weapon_fire" : "weapon_fire_on_empty" );
  469. if( event )
  470. {
  471. const char *weaponName = STRING( m_iClassname );
  472. if ( strncmp( weaponName, "weapon_", 7 ) == 0 )
  473. {
  474. weaponName += 7;
  475. }
  476. event->SetInt( "userid", pPlayer->GetUserID() );
  477. event->SetString( "weapon", weaponName );
  478. gameeventmanager->FireEvent( event );
  479. }
  480. }
  481. #endif
  482. PrimaryAttack();
  483. }
  484. else if ( pPlayer->m_nButtons & IN_RELOAD && GetMaxClip1() != WEAPON_NOCLIP && !m_bInReload && m_flNextPrimaryAttack < gpGlobals->curtime)
  485. {
  486. // reload when reload is pressed, or if no buttons are down and weapon is empty.
  487. //MIKETODO: add code for shields...
  488. //if ( !FBitSet( m_iWeaponState, WPNSTATE_SHIELD_DRAWN ) )
  489. if ( !pPlayer->IsShieldDrawn() )
  490. {
  491. if ( Reload() )
  492. {
  493. #ifndef CLIENT_DLL
  494. // allow the bots to react to the reload
  495. IGameEvent * event = gameeventmanager->CreateEvent( "weapon_reload" );
  496. if( event )
  497. {
  498. event->SetInt( "userid", pPlayer->GetUserID() );
  499. gameeventmanager->FireEvent( event );
  500. }
  501. #endif
  502. }
  503. }
  504. }
  505. else if ( !(pPlayer->m_nButtons & (IN_ATTACK|IN_ATTACK2) ) )
  506. {
  507. if ( weapon_accuracy_model.GetInt() == 2 )
  508. {
  509. // Fire button not down -- reset the shots fired count
  510. if ( pPlayer->m_iShotsFired > 0 && ( !IsFullAuto() || m_iClip1 == 0 ) )
  511. {
  512. pPlayer->m_iShotsFired = 0;
  513. }
  514. }
  515. // The following code prevents the player from tapping the firebutton repeatedly
  516. // to simulate full auto and retaining the single shot accuracy of single fire
  517. if ( m_bDelayFire )
  518. {
  519. m_bDelayFire = false;
  520. if (pPlayer->m_iShotsFired > 15)
  521. pPlayer->m_iShotsFired = 15;
  522. m_flDecreaseShotsFired = gpGlobals->curtime + 0.4;
  523. }
  524. m_bFireOnEmpty = true;
  525. // if it's a pistol then set the shots fired to 0 after the player releases a button
  526. if ( IsPistol() )
  527. {
  528. pPlayer->m_iShotsFired = 0;
  529. }
  530. else
  531. {
  532. if ( (pPlayer->m_iShotsFired > 0) && (m_flDecreaseShotsFired < gpGlobals->curtime) )
  533. {
  534. m_flDecreaseShotsFired = gpGlobals->curtime + 0.0225;
  535. pPlayer->m_iShotsFired--;
  536. }
  537. }
  538. if ( (!IsUseable() && m_flNextPrimaryAttack < gpGlobals->curtime) )
  539. {
  540. // Intentionally blank -- used to switch weapons here
  541. }
  542. else
  543. {
  544. // weapon is useable. Reload if empty and weapon has waited as long as it has to after firing
  545. if ( m_iClip1 == 0 && !(GetWeaponFlags() & ITEM_FLAG_NOAUTORELOAD) && m_flNextPrimaryAttack < gpGlobals->curtime )
  546. {
  547. Reload();
  548. return;
  549. }
  550. }
  551. WeaponIdle( );
  552. return;
  553. }
  554. }
  555. void CWeaponCSBase::ItemBusyFrame()
  556. {
  557. UpdateAccuracyPenalty();
  558. BaseClass::ItemBusyFrame();
  559. }
  560. float CWeaponCSBase::GetInaccuracy() const
  561. {
  562. CCSPlayer *pPlayer = GetPlayerOwner();
  563. if ( !pPlayer )
  564. return 0.0f;
  565. const CCSWeaponInfo& weaponInfo = GetCSWpnData();
  566. float fMaxSpeed = GetMaxSpeed();
  567. if ( fMaxSpeed == 0.0f )
  568. fMaxSpeed = GetCSWpnData().m_flMaxSpeed;
  569. return m_fAccuracyPenalty +
  570. RemapValClamped(pPlayer->GetAbsVelocity().Length2D(),
  571. fMaxSpeed * CS_PLAYER_SPEED_DUCK_MODIFIER,
  572. fMaxSpeed * 0.95f, // max out at 95% of run speed to avoid jitter near max speed
  573. 0.0f, weaponInfo.m_fInaccuracyMove[m_weaponMode]);
  574. }
  575. float CWeaponCSBase::GetSpread() const
  576. {
  577. if ( weapon_accuracy_model.GetInt() == 1 )
  578. return 0.0f;
  579. return GetCSWpnData().m_fSpread[m_weaponMode];
  580. }
  581. float CWeaponCSBase::GetMaxSpeed() const
  582. {
  583. // The weapon should have set this in its constructor.
  584. float flRet = GetCSWpnData().m_flMaxSpeed;
  585. Assert( flRet > 1 );
  586. return flRet;
  587. }
  588. const CCSWeaponInfo &CWeaponCSBase::GetCSWpnData() const
  589. {
  590. const FileWeaponInfo_t *pWeaponInfo = &GetWpnData();
  591. const CCSWeaponInfo *pCSInfo;
  592. #ifdef _DEBUG
  593. pCSInfo = dynamic_cast< const CCSWeaponInfo* >( pWeaponInfo );
  594. Assert( pCSInfo );
  595. #else
  596. pCSInfo = static_cast< const CCSWeaponInfo* >( pWeaponInfo );
  597. #endif
  598. return *pCSInfo;
  599. }
  600. //-----------------------------------------------------------------------------
  601. // Purpose:
  602. //-----------------------------------------------------------------------------
  603. const char *CWeaponCSBase::GetViewModel( int /*viewmodelindex = 0 -- this is ignored in the base class here*/ ) const
  604. {
  605. CCSPlayer *pOwner = GetPlayerOwner();
  606. if ( pOwner == NULL )
  607. return BaseClass::GetViewModel();
  608. if ( pOwner->HasShield() && GetCSWpnData().m_bCanUseWithShield )
  609. return GetCSWpnData().m_szShieldViewModel;
  610. else
  611. return GetWpnData().szViewModel;
  612. return BaseClass::GetViewModel();
  613. }
  614. void CWeaponCSBase::Precache( void )
  615. {
  616. BaseClass::Precache();
  617. #ifdef CS_SHIELD_ENABLED
  618. if ( GetCSWpnData().m_bCanUseWithShield )
  619. {
  620. PrecacheModel( GetCSWpnData().m_szShieldViewModel );
  621. }
  622. #endif
  623. PrecacheScriptSound( "Default.ClipEmpty_Pistol" );
  624. PrecacheScriptSound( "Default.ClipEmpty_Rifle" );
  625. PrecacheScriptSound( "Default.Zoom" );
  626. }
  627. Activity CWeaponCSBase::GetDeployActivity( void )
  628. {
  629. return ACT_VM_DRAW;
  630. }
  631. bool CWeaponCSBase::DefaultDeploy( char *szViewModel, char *szWeaponModel, int iActivity, char *szAnimExt )
  632. {
  633. // Msg( "deploy %s at %f\n", GetClassname(), gpGlobals->curtime );
  634. CCSPlayer *pOwner = GetPlayerOwner();
  635. if ( !pOwner )
  636. {
  637. return false;
  638. }
  639. pOwner->SetAnimationExtension( szAnimExt );
  640. SetViewModel();
  641. SendWeaponAnim( GetDeployActivity() );
  642. pOwner->SetNextAttack( gpGlobals->curtime + SequenceDuration() );
  643. m_flNextPrimaryAttack = gpGlobals->curtime;
  644. m_flNextSecondaryAttack = gpGlobals->curtime;
  645. SetWeaponVisible( true );
  646. pOwner->SetShieldDrawnState( false );
  647. if ( pOwner->HasShield() == true )
  648. SetWeaponModelIndex( SHIELD_WORLD_MODEL);
  649. else
  650. SetWeaponModelIndex( szWeaponModel );
  651. return true;
  652. }
  653. void CWeaponCSBase::UpdateShieldState( void )
  654. {
  655. //empty by default.
  656. CCSPlayer *pOwner = GetPlayerOwner();
  657. if ( pOwner == NULL )
  658. return;
  659. //ADRIANTODO
  660. //Make the hitbox set switches here!!!
  661. if ( pOwner->HasShield() == false )
  662. {
  663. pOwner->SetShieldDrawnState( false );
  664. //pOwner->SetHitBoxSet( 0 );
  665. return;
  666. }
  667. else
  668. {
  669. //pOwner->SetHitBoxSet( 1 );
  670. }
  671. }
  672. void CWeaponCSBase::SetWeaponModelIndex( const char *pName )
  673. {
  674. m_iWorldModelIndex = modelinfo->GetModelIndex( pName );
  675. }
  676. bool CWeaponCSBase::CanBeSelected( void )
  677. {
  678. if ( !VisibleInWeaponSelection() )
  679. return false;
  680. return true;
  681. }
  682. bool CWeaponCSBase::CanDeploy( void )
  683. {
  684. CCSPlayer *pPlayer = GetPlayerOwner();
  685. if ( !pPlayer )
  686. return false;
  687. if ( pPlayer->HasShield() && GetCSWpnData().m_bCanUseWithShield == false )
  688. return false;
  689. return BaseClass::CanDeploy();
  690. }
  691. float CWeaponCSBase::CalculateNextAttackTime( float fCycleTime )
  692. {
  693. float fCurAttack = m_flNextPrimaryAttack;
  694. float fDeltaAttack = gpGlobals->curtime - fCurAttack;
  695. if ( fDeltaAttack < 0 || fDeltaAttack > gpGlobals->interval_per_tick )
  696. {
  697. fCurAttack = gpGlobals->curtime;
  698. }
  699. m_flNextSecondaryAttack = m_flNextPrimaryAttack = fCurAttack + fCycleTime;
  700. return fCurAttack;
  701. }
  702. bool CWeaponCSBase::Holster( CBaseCombatWeapon *pSwitchingTo )
  703. {
  704. CCSPlayer *pPlayer = GetPlayerOwner();
  705. if ( !pPlayer )
  706. return false;
  707. if ( pPlayer )
  708. pPlayer->SetFOV( pPlayer, 0 ); // reset the default FOV.
  709. if ( pPlayer )
  710. pPlayer->SetShieldDrawnState( false );
  711. return BaseClass::Holster( pSwitchingTo );
  712. }
  713. bool CWeaponCSBase::Deploy()
  714. {
  715. CCSPlayer *pPlayer = GetPlayerOwner();
  716. #ifdef CLIENT_DLL
  717. m_iAlpha = 80;
  718. if ( pPlayer )
  719. {
  720. pPlayer->m_iLastZoom = 0;
  721. pPlayer->SetFOV( pPlayer, 0 );
  722. }
  723. #else
  724. m_flDecreaseShotsFired = gpGlobals->curtime;
  725. if ( pPlayer )
  726. {
  727. pPlayer->m_iShotsFired = 0;
  728. pPlayer->m_bResumeZoom = false;
  729. pPlayer->m_iLastZoom = 0;
  730. pPlayer->SetFOV( pPlayer, 0 );
  731. }
  732. #endif
  733. m_fAccuracyPenalty = 0.0f;
  734. return BaseClass::Deploy();
  735. }
  736. #ifndef CLIENT_DLL
  737. bool CWeaponCSBase::IsRemoveable()
  738. {
  739. if ( BaseClass::IsRemoveable() == true )
  740. {
  741. if ( m_nextPrevOwnerTouchTime > gpGlobals->curtime )
  742. {
  743. return false;
  744. }
  745. }
  746. return BaseClass::IsRemoveable();
  747. }
  748. #endif
  749. void CWeaponCSBase::Drop(const Vector &vecVelocity)
  750. {
  751. #ifdef CLIENT_DLL
  752. BaseClass::Drop(vecVelocity);
  753. return;
  754. #else
  755. // Once somebody drops a gun, it's fair game for removal when/if
  756. // a game_weapon_manager does a cleanup on surplus weapons in the
  757. // world.
  758. SetRemoveable( true );
  759. StopAnimation();
  760. StopFollowingEntity( );
  761. SetMoveType( MOVETYPE_FLYGRAVITY );
  762. // clear follow stuff, setup for collision
  763. SetGravity(1.0);
  764. m_iState = WEAPON_NOT_CARRIED;
  765. RemoveEffects( EF_NODRAW );
  766. FallInit();
  767. SetGroundEntity( NULL );
  768. m_bInReload = false; // stop reloading
  769. SetThink( NULL );
  770. m_nextPrevOwnerTouchTime = gpGlobals->curtime + 0.8f;
  771. m_prevOwner = GetPlayerOwner();
  772. SetTouch(&CWeaponCSBase::DefaultTouch);
  773. IPhysicsObject *pObj = VPhysicsGetObject();
  774. if ( pObj != NULL )
  775. {
  776. AngularImpulse angImp( 200, 200, 200 );
  777. pObj->AddVelocity( &vecVelocity, &angImp );
  778. }
  779. else
  780. {
  781. SetAbsVelocity( vecVelocity );
  782. }
  783. SetNextThink( gpGlobals->curtime );
  784. SetOwnerEntity( NULL );
  785. SetOwner( NULL );
  786. #endif
  787. }
  788. // whats going on here is that if the player drops this weapon, they shouldn't take it back themselves
  789. // for a little while. But if they throw it at someone else, the other player should get it immediately.
  790. void CWeaponCSBase::DefaultTouch(CBaseEntity *pOther)
  791. {
  792. if ((m_prevOwner != NULL) && (pOther == m_prevOwner) && (gpGlobals->curtime < m_nextPrevOwnerTouchTime))
  793. {
  794. return;
  795. }
  796. BaseClass::DefaultTouch(pOther);
  797. }
  798. #if defined( CLIENT_DLL )
  799. //-----------------------------------------------------------------------------
  800. // Purpose: Draw the weapon's crosshair
  801. //-----------------------------------------------------------------------------
  802. void CWeaponCSBase::DrawCrosshair()
  803. {
  804. if ( !crosshair.GetInt() )
  805. return;
  806. CHudCrosshair *pCrosshair = GET_HUDELEMENT( CHudCrosshair );
  807. if ( !pCrosshair )
  808. return;
  809. // clear crosshair
  810. pCrosshair->SetCrosshair( 0, Color( 255, 255, 255, 255 ) );
  811. CCSPlayer* pPlayer = (CCSPlayer*)C_BasePlayer::GetLocalPlayer();
  812. if ( !pPlayer )
  813. return;
  814. // localplayer must be owner if not in Spec mode
  815. Assert( (pPlayer == GetPlayerOwner()) || ( pPlayer->GetObserverMode()==OBS_MODE_IN_EYE) );
  816. // Draw the targeting zone around the pCrosshair
  817. if ( pPlayer->IsInVGuiInputMode() )
  818. return;
  819. int r, g, b;
  820. switch ( cl_crosshaircolor.GetInt() )
  821. {
  822. case 0 : r = 50; g = 250; b = 50; break;
  823. case 1 : r = 250; g = 50; b = 50; break;
  824. case 2 : r = 50; g = 50; b = 250; break;
  825. case 3 : r = 250; g = 250; b = 50; break;
  826. case 4 : r = 50; g = 250; b = 250; break;
  827. case 5 :
  828. r = cl_crosshaircolor_r.GetInt();
  829. g = cl_crosshaircolor_g.GetInt();
  830. b = cl_crosshaircolor_b.GetInt();
  831. break;
  832. default : r = 50; g = 250; b = 50; break;
  833. }
  834. // if user is using nightvision, make the crosshair red.
  835. if (pPlayer->m_bNightVisionOn)
  836. {
  837. r = 250;
  838. g = 50;
  839. b = 50;
  840. }
  841. int alpha = clamp( cl_crosshairalpha.GetInt(), 0, 255 );
  842. vgui::surface()->DrawSetColor( r, g, b, alpha );
  843. if ( !m_iCrosshairTextureID )
  844. {
  845. CHudTexture *pTexture = gHUD.GetIcon( "whiteAdditive" );
  846. if ( pTexture )
  847. {
  848. m_iCrosshairTextureID = pTexture->textureId;
  849. }
  850. }
  851. bool bAdditive = !cl_crosshairusealpha.GetBool() && !pPlayer->m_bNightVisionOn;
  852. if ( bAdditive )
  853. {
  854. vgui::surface()->DrawSetColor( r, g, b, 200 );
  855. vgui::surface()->DrawSetTexture( m_iCrosshairTextureID );
  856. }
  857. if ( pPlayer->HasShield() && pPlayer->IsShieldDrawn() == true )
  858. return;
  859. // no crosshair for sniper rifles
  860. bool bCrosshairVisible = crosshair.GetBool() && GetCSWpnData().m_WeaponType != WEAPONTYPE_SNIPER_RIFLE;
  861. if ( !bCrosshairVisible
  862. #if ALLOW_WEAPON_SPREAD_DISPLAY
  863. && !weapon_debug_spread_show.GetBool()
  864. #endif
  865. )
  866. return;
  867. float fHalfFov = DEG2RAD(pPlayer->GetFOV()) * 0.5f;
  868. int iCrosshairDistance;
  869. int iBarSize = RoundFloatToInt(YRES(cl_crosshairsize.GetFloat()));
  870. int iBarThickness = MAX( 1, RoundFloatToInt(YRES(cl_crosshairthickness.GetFloat())));
  871. switch ( cl_dynamiccrosshair.GetInt() )
  872. {
  873. case 0:
  874. default:
  875. {
  876. // static crosshair
  877. float fSpread = (GetCSWpnData().m_fSpread[m_weaponMode] + GetCSWpnData().m_fInaccuracyStand[m_weaponMode]) * 320.0f / tanf(fHalfFov);
  878. iCrosshairDistance = MAX( 0, RoundFloatToInt( YRES( fSpread * cl_crosshairspreadscale.GetFloat() ) ) );
  879. }
  880. break;
  881. case 1:
  882. {
  883. float fSpread = (GetInaccuracy() + GetSpread()) * 320.0f / tanf(fHalfFov);
  884. iCrosshairDistance = MAX( 0, RoundFloatToInt( YRES( fSpread * cl_crosshairspreadscale.GetFloat() ) ) );
  885. }
  886. break;
  887. case 2:
  888. case 3:
  889. {
  890. float fCrosshairDistanceGoal = GetCSWpnData().m_iCrosshairMinDistance; // The minimum distance the crosshair can achieve...
  891. // legacy dynamic crosshair
  892. if ( cl_dynamiccrosshair.GetInt() == 2 )
  893. {
  894. if ( !( pPlayer->GetFlags() & FL_ONGROUND ) )
  895. fCrosshairDistanceGoal *= 2.0f;
  896. else if ( pPlayer->GetFlags() & FL_DUCKING )
  897. fCrosshairDistanceGoal *= 0.5f;
  898. else if ( pPlayer->GetAbsVelocity().Length() > 100 )
  899. fCrosshairDistanceGoal *= 1.5f;
  900. }
  901. // [jpaquin] changed to only bump up the crosshair size if the player is still shooting or is spectating someone else
  902. int iDeltaDistance = GetCSWpnData().m_iCrosshairDeltaDistance; // Amount by which the crosshair expands when shooting (per frame)
  903. if ( pPlayer->m_iShotsFired > m_iAmmoLastCheck && (pPlayer->m_nButtons & (IN_ATTACK|IN_ATTACK2)) )
  904. fCrosshairDistanceGoal += iDeltaDistance;
  905. m_iAmmoLastCheck = pPlayer->m_iShotsFired;
  906. if ( m_flCrosshairDistance > fCrosshairDistanceGoal )
  907. {
  908. // [jpaquin] if we're not in legacy crosshair mode, use an exponential decay function so
  909. // that the crosshair shrinks at the same rate regardless of the frame rate
  910. if ( !cl_legacy_crosshair_recoil.GetBool() )
  911. {
  912. // .44888 on the next line makes the decay very close to what old method produces at 100fps.
  913. m_flCrosshairDistance = Lerp(expf(-gpGlobals->frametime / 0.44888f), fCrosshairDistanceGoal, m_flCrosshairDistance);
  914. }
  915. else
  916. {
  917. m_flCrosshairDistance -= 0.1f + m_flCrosshairDistance * 0.013;
  918. }
  919. }
  920. // clamp max crosshair expansion
  921. m_flCrosshairDistance = clamp(m_flCrosshairDistance, fCrosshairDistanceGoal, 25.0f);
  922. if ( cl_legacy_crosshair_scale.GetBool() )
  923. {
  924. //scale bar size to the resolution
  925. int crosshairScale = cl_crosshairscale.GetInt();
  926. if ( crosshairScale < 1 )
  927. {
  928. if ( ScreenHeight() <= 600 )
  929. {
  930. crosshairScale = 600;
  931. }
  932. else if ( ScreenHeight() <= 768 )
  933. {
  934. crosshairScale = 768;
  935. }
  936. else
  937. {
  938. crosshairScale = 1200;
  939. }
  940. }
  941. float scale;
  942. if( cl_scalecrosshair.GetBool() == false )
  943. {
  944. scale = 1.0f;
  945. }
  946. else
  947. {
  948. scale = (float)ScreenHeight() / (float)crosshairScale;
  949. }
  950. // calculate the inner distance of the crosshair in current screen units
  951. iCrosshairDistance = (int)ceil( m_flCrosshairDistance * scale );
  952. iBarSize = XRES(5); // + (iCrosshairDistance - fCrosshairDistanceGoal) / 2;
  953. iBarSize = MAX( 1, (int)( (float)iBarSize * scale ) );
  954. iBarThickness = MAX( 1, (int)floor( scale + 0.5f ) );
  955. }
  956. else
  957. {
  958. iCrosshairDistance = RoundFloatToInt(m_flCrosshairDistance * ScreenHeight() / 1200.0f);
  959. }
  960. }
  961. break;
  962. }
  963. int iCenterX = ScreenWidth() / 2;
  964. int iCenterY = ScreenHeight() / 2;
  965. if ( bCrosshairVisible )
  966. {
  967. // draw horizontal crosshair lines
  968. int iInnerLeft = iCenterX - iCrosshairDistance - iBarThickness / 2;
  969. int iInnerRight = iInnerLeft + 2 * iCrosshairDistance + iBarThickness;
  970. int iOuterLeft = iInnerLeft - iBarSize;
  971. int iOuterRight = iInnerRight + iBarSize;
  972. int y0 = iCenterY - iBarThickness / 2;
  973. int y1 = y0 + iBarThickness;
  974. DrawCrosshairRect( iOuterLeft, y0, iInnerLeft, y1, bAdditive );
  975. DrawCrosshairRect( iInnerRight, y0, iOuterRight, y1, bAdditive );
  976. // draw vertical crosshair lines
  977. int iInnerTop = iCenterY - iCrosshairDistance - iBarThickness / 2;
  978. int iInnerBottom = iInnerTop + 2 * iCrosshairDistance + iBarThickness;
  979. int iOuterTop = iInnerTop - iBarSize;
  980. int iOuterBottom = iInnerBottom + iBarSize;
  981. int x0 = iCenterX - iBarThickness / 2;
  982. int x1 = x0 + iBarThickness;
  983. DrawCrosshairRect( x0, iOuterTop, x1, iInnerTop, bAdditive );
  984. DrawCrosshairRect( x0, iInnerBottom, x1, iOuterBottom, bAdditive );
  985. // draw dot
  986. if ( cl_crosshairdot.GetBool() )
  987. {
  988. int x0 = iCenterX - iBarThickness / 2;
  989. int x1 = x0 + iBarThickness;
  990. int y0 = iCenterY - iBarThickness / 2;
  991. int y1 = y0 + iBarThickness;
  992. DrawCrosshairRect( x0, y0, x1, y1, bAdditive );
  993. }
  994. }
  995. #if ALLOW_WEAPON_SPREAD_DISPLAY
  996. // show accuracy brackets
  997. if ( weapon_debug_spread_show.GetInt() == 1 || weapon_debug_spread_show.GetInt() == 2 )
  998. {
  999. if ( weapon_debug_spread_show.GetInt() == 2 )
  1000. {
  1001. const QAngle& punchAngles = pPlayer->GetPunchAngle();
  1002. Vector vecDirShooting;
  1003. AngleVectors( punchAngles, &vecDirShooting );
  1004. float iOffsetX = RoundFloatToInt(YRES(vecDirShooting.y * 320.0f / tanf(fHalfFov)));
  1005. float iOffsetY = RoundFloatToInt(YRES(vecDirShooting.z * 320.0f / tanf(fHalfFov)));
  1006. iCenterX -= iOffsetX;
  1007. iCenterY -= iOffsetY;
  1008. }
  1009. // colors
  1010. r = 250;
  1011. g = 250;
  1012. b = 50;
  1013. vgui::surface()->DrawSetColor( r, g, b, alpha );
  1014. int iBarThickness = MAX( 1, RoundFloatToInt(YRES(cl_crosshairthickness.GetFloat())));
  1015. float fSpreadDistance = (GetInaccuracy() + GetSpread()) * 320.0f / tanf(fHalfFov);
  1016. int iSpreadDistance = RoundFloatToInt(YRES(fSpreadDistance));
  1017. // draw vertical spread lines
  1018. int iInnerLeft = iCenterX - iSpreadDistance;
  1019. int iInnerRight = iCenterX + iSpreadDistance;
  1020. int iOuterLeft = iInnerLeft - iBarThickness;
  1021. int iOuterRight = iInnerRight + iBarThickness;
  1022. int iInnerTop = iCenterY - iSpreadDistance;
  1023. int iInnerBottom = iCenterY + iSpreadDistance;
  1024. int iOuterTop = iInnerTop - iBarThickness;
  1025. int iOuterBottom = iInnerBottom + iBarThickness;
  1026. int iGap = RoundFloatToInt(weapon_debug_spread_gap.GetFloat() * iSpreadDistance);
  1027. // draw horizontal lines
  1028. DrawCrosshairRect( iOuterLeft, iOuterTop, iCenterX - iGap, iInnerTop, bAdditive );
  1029. DrawCrosshairRect( iCenterX + iGap, iOuterTop, iOuterRight, iInnerTop, bAdditive );
  1030. DrawCrosshairRect( iOuterLeft, iInnerBottom, iCenterX - iGap, iOuterBottom, bAdditive );
  1031. DrawCrosshairRect( iCenterX + iGap, iInnerBottom, iOuterRight, iOuterBottom, bAdditive );
  1032. // draw vertical lines
  1033. DrawCrosshairRect( iOuterLeft, iOuterTop, iInnerLeft, iCenterY - iGap, bAdditive );
  1034. DrawCrosshairRect( iOuterLeft, iCenterY + iGap, iInnerLeft, iOuterBottom, bAdditive );
  1035. DrawCrosshairRect( iInnerRight, iOuterTop, iOuterRight, iCenterY - iGap, bAdditive );
  1036. DrawCrosshairRect( iInnerRight, iCenterY + iGap, iOuterRight, iOuterBottom, bAdditive );
  1037. }
  1038. #endif
  1039. }
  1040. void CWeaponCSBase::OnDataChanged( DataUpdateType_t type )
  1041. {
  1042. BaseClass::OnDataChanged( type );
  1043. if ( GetPredictable() && !ShouldPredict() )
  1044. ShutdownPredictable();
  1045. }
  1046. bool CWeaponCSBase::ShouldPredict()
  1047. {
  1048. if ( GetOwner() && GetOwner() == C_BasePlayer::GetLocalPlayer() )
  1049. return true;
  1050. return BaseClass::ShouldPredict();
  1051. }
  1052. void CWeaponCSBase::ProcessMuzzleFlashEvent()
  1053. {
  1054. // This is handled from the player's animstate, so it can match up to the beginning of the fire animation
  1055. }
  1056. bool CWeaponCSBase::OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options )
  1057. {
  1058. if( event == 5001 )
  1059. {
  1060. C_CSPlayer *pPlayer = ToCSPlayer( GetOwner() );
  1061. if( pPlayer && pPlayer->GetFOV() < pPlayer->GetDefaultFOV() && HideViewModelWhenZoomed() )
  1062. return true;
  1063. CEffectData data;
  1064. data.m_fFlags = 0;
  1065. data.m_hEntity = pViewModel->GetRefEHandle();
  1066. data.m_nAttachmentIndex = 1;
  1067. data.m_flScale = GetCSWpnData().m_flMuzzleScale;
  1068. switch( GetMuzzleFlashStyle() )
  1069. {
  1070. case CS_MUZZLEFLASH_NONE:
  1071. break;
  1072. case CS_MUZZLEFLASH_X:
  1073. {
  1074. DispatchEffect( "CS_MuzzleFlash_X", data );
  1075. }
  1076. break;
  1077. case CS_MUZZLEFLASH_NORM:
  1078. default:
  1079. {
  1080. DispatchEffect( "CS_MuzzleFlash", data );
  1081. }
  1082. break;
  1083. }
  1084. return true;
  1085. }
  1086. return BaseClass::OnFireEvent( pViewModel, origin, angles, event, options );
  1087. }
  1088. int CWeaponCSBase::GetMuzzleFlashStyle( void )
  1089. {
  1090. return GetCSWpnData().m_iMuzzleFlashStyle;
  1091. }
  1092. int CWeaponCSBase::GetMuzzleAttachment( void )
  1093. {
  1094. return LookupAttachment( "muzzle_flash" );
  1095. }
  1096. #else
  1097. //-----------------------------------------------------------------------------
  1098. // Purpose: Get the accuracy derived from weapon and player, and return it
  1099. //-----------------------------------------------------------------------------
  1100. const Vector& CWeaponCSBase::GetBulletSpread()
  1101. {
  1102. static Vector cone = VECTOR_CONE_8DEGREES;
  1103. return cone;
  1104. }
  1105. //-----------------------------------------------------------------------------
  1106. // Purpose: Match the anim speed to the weapon speed while crouching
  1107. //-----------------------------------------------------------------------------
  1108. float CWeaponCSBase::GetDefaultAnimSpeed()
  1109. {
  1110. return 1.0;
  1111. }
  1112. //-----------------------------------------------------------------------------
  1113. // Purpose: Draw the laser rifle effect
  1114. //-----------------------------------------------------------------------------
  1115. void CWeaponCSBase::BulletWasFired( const Vector &vecStart, const Vector &vecEnd )
  1116. {
  1117. }
  1118. bool CWeaponCSBase::ShouldRemoveOnRoundRestart()
  1119. {
  1120. if ( GetPlayerOwner() )
  1121. return false;
  1122. else
  1123. return true;
  1124. }
  1125. //=============================================================================
  1126. // HPE_BEGIN:
  1127. // [dwenger] Handle round restart processing for the weapon.
  1128. //=============================================================================
  1129. void CWeaponCSBase::OnRoundRestart()
  1130. {
  1131. // Clear out the list of prior owners
  1132. m_PriorOwners.RemoveAll();
  1133. }
  1134. //=============================================================================
  1135. // HPE_END
  1136. //=============================================================================
  1137. //=========================================================
  1138. // Materialize - make a CWeaponCSBase visible and tangible
  1139. //=========================================================
  1140. void CWeaponCSBase::Materialize()
  1141. {
  1142. if ( IsEffectActive( EF_NODRAW ) )
  1143. {
  1144. // changing from invisible state to visible.
  1145. RemoveEffects( EF_NODRAW );
  1146. DoMuzzleFlash();
  1147. }
  1148. AddSolidFlags( FSOLID_TRIGGER );
  1149. //SetTouch( &CWeaponCSBase::DefaultTouch );
  1150. SetThink( NULL );
  1151. }
  1152. //=========================================================
  1153. // AttemptToMaterialize - the item is trying to rematerialize,
  1154. // should it do so now or wait longer?
  1155. //=========================================================
  1156. void CWeaponCSBase::AttemptToMaterialize()
  1157. {
  1158. float time = g_pGameRules->FlWeaponTryRespawn( this );
  1159. if ( time == 0 )
  1160. {
  1161. Materialize();
  1162. return;
  1163. }
  1164. SetNextThink( gpGlobals->curtime + time );
  1165. }
  1166. //=========================================================
  1167. // CheckRespawn - a player is taking this weapon, should
  1168. // it respawn?
  1169. //=========================================================
  1170. void CWeaponCSBase::CheckRespawn()
  1171. {
  1172. //GOOSEMAN : Do not respawn weapons!
  1173. return;
  1174. }
  1175. //=========================================================
  1176. // Respawn- this item is already in the world, but it is
  1177. // invisible and intangible. Make it visible and tangible.
  1178. //=========================================================
  1179. CBaseEntity* CWeaponCSBase::Respawn()
  1180. {
  1181. // make a copy of this weapon that is invisible and inaccessible to players (no touch function). The weapon spawn/respawn code
  1182. // will decide when to make the weapon visible and touchable.
  1183. CBaseEntity *pNewWeapon = CBaseEntity::Create( GetClassname(), g_pGameRules->VecWeaponRespawnSpot( this ), GetAbsAngles(), GetOwner() );
  1184. if ( pNewWeapon )
  1185. {
  1186. pNewWeapon->AddEffects( EF_NODRAW );// invisible for now
  1187. pNewWeapon->SetTouch( NULL );// no touch
  1188. pNewWeapon->SetThink( &CWeaponCSBase::AttemptToMaterialize );
  1189. UTIL_DropToFloor( this, MASK_SOLID );
  1190. // not a typo! We want to know when the weapon the player just picked up should respawn! This new entity we created is the replacement,
  1191. // but when it should respawn is based on conditions belonging to the weapon that was taken.
  1192. pNewWeapon->SetNextThink( gpGlobals->curtime + g_pGameRules->FlWeaponRespawnTime( this ) );
  1193. }
  1194. else
  1195. {
  1196. Msg( "Respawn failed to create %s!\n", GetClassname() );
  1197. }
  1198. return pNewWeapon;
  1199. }
  1200. //-----------------------------------------------------------------------------
  1201. // Purpose:
  1202. //-----------------------------------------------------------------------------
  1203. void CWeaponCSBase::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  1204. {
  1205. CBasePlayer *pPlayer = ToBasePlayer( pActivator );
  1206. if ( pPlayer )
  1207. {
  1208. m_OnPlayerUse.FireOutput( pActivator, pCaller );
  1209. }
  1210. }
  1211. bool CWeaponCSBase::Reload()
  1212. {
  1213. CCSPlayer *pPlayer = GetPlayerOwner();
  1214. if ( !pPlayer )
  1215. return false;
  1216. pPlayer->m_iShotsFired = 0;
  1217. bool retval = BaseClass::Reload();
  1218. return retval;
  1219. }
  1220. void CWeaponCSBase::Spawn()
  1221. {
  1222. BaseClass::Spawn();
  1223. // Override the bloat that our base class sets as it's a little bit bigger than we want.
  1224. // If it's too big, you drop a weapon and its box is so big that you're still touching it
  1225. // when it falls and you pick it up again right away.
  1226. CollisionProp()->UseTriggerBounds( true, 30 );
  1227. // Set this here to allow players to shoot dropped weapons
  1228. SetCollisionGroup( COLLISION_GROUP_WEAPON );
  1229. SetExtraAmmoCount( m_iDefaultExtraAmmo ); //Start with no additional ammo
  1230. m_nextPrevOwnerTouchTime = 0.0;
  1231. m_prevOwner = NULL;
  1232. //=============================================================================
  1233. // HPE_BEGIN:
  1234. //=============================================================================
  1235. // [tj] initialize donor of this weapon
  1236. m_donor = NULL;
  1237. m_donated = false;
  1238. m_weaponMode = Primary_Mode;
  1239. //=============================================================================
  1240. // HPE_END
  1241. //=============================================================================
  1242. }
  1243. bool CWeaponCSBase::DefaultReload( int iClipSize1, int iClipSize2, int iActivity )
  1244. {
  1245. if ( BaseClass::DefaultReload( iClipSize1, iClipSize2, iActivity ) )
  1246. {
  1247. SendReloadEvents();
  1248. return true;
  1249. }
  1250. else
  1251. {
  1252. return false;
  1253. }
  1254. }
  1255. void CWeaponCSBase::SendReloadEvents()
  1256. {
  1257. CCSPlayer *pPlayer = dynamic_cast< CCSPlayer* >( GetOwner() );
  1258. if ( !pPlayer )
  1259. return;
  1260. // Send a message to any clients that have this entity to play the reload.
  1261. CPASFilter filter( pPlayer->GetAbsOrigin() );
  1262. filter.RemoveRecipient( pPlayer );
  1263. UserMessageBegin( filter, "ReloadEffect" );
  1264. WRITE_SHORT( pPlayer->entindex() );
  1265. MessageEnd();
  1266. // Make the player play his reload animation.
  1267. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_RELOAD );
  1268. }
  1269. #endif
  1270. bool CWeaponCSBase::DefaultPistolReload()
  1271. {
  1272. CCSPlayer *pPlayer = GetPlayerOwner();
  1273. if ( !pPlayer )
  1274. return false;
  1275. if (pPlayer->GetAmmoCount( GetPrimaryAmmoType() ) <= 0)
  1276. return true;
  1277. if ( !DefaultReload( GetCSWpnData().iDefaultClip1, 0, ACT_VM_RELOAD ) )
  1278. return false;
  1279. pPlayer->m_iShotsFired = 0;
  1280. return true;
  1281. }
  1282. bool CWeaponCSBase::IsUseable()
  1283. {
  1284. CCSPlayer *pPlayer = GetPlayerOwner();
  1285. if ( !pPlayer )
  1286. return false;
  1287. if ( Clip1() <= 0 )
  1288. {
  1289. if ( pPlayer->GetAmmoCount( GetPrimaryAmmoType() ) <= 0 && GetMaxClip1() != -1 )
  1290. {
  1291. // clip is empty (or nonexistant) and the player has no more ammo of this type.
  1292. return false;
  1293. }
  1294. }
  1295. return true;
  1296. }
  1297. #if defined( CLIENT_DLL )
  1298. float g_lateralBob = 0;
  1299. float g_verticalBob = 0;
  1300. static ConVar cl_bobcycle( "cl_bobcycle","0.8", FCVAR_CHEAT );
  1301. static ConVar cl_bob( "cl_bob","0.002", FCVAR_CHEAT );
  1302. static ConVar cl_bobup( "cl_bobup","0.5", FCVAR_CHEAT );
  1303. //-----------------------------------------------------------------------------
  1304. // Purpose:
  1305. // Output : float
  1306. //-----------------------------------------------------------------------------
  1307. float CWeaponCSBase::CalcViewmodelBob( void )
  1308. {
  1309. static float bobtime;
  1310. static float lastbobtime;
  1311. static float lastspeed;
  1312. float cycle;
  1313. CBasePlayer *player = ToBasePlayer( GetOwner() );
  1314. //Assert( player );
  1315. //NOTENOTE: For now, let this cycle continue when in the air, because it snaps badly without it
  1316. if ( ( !gpGlobals->frametime ) ||
  1317. ( player == NULL ) ||
  1318. ( cl_bobcycle.GetFloat() <= 0.0f ) ||
  1319. ( cl_bobup.GetFloat() <= 0.0f ) ||
  1320. ( cl_bobup.GetFloat() >= 1.0f ) )
  1321. {
  1322. //NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!)
  1323. return 0.0f;// just use old value
  1324. }
  1325. //Find the speed of the player
  1326. float speed = player->GetLocalVelocity().Length2D();
  1327. float flmaxSpeedDelta = MAX( 0, (gpGlobals->curtime - lastbobtime) * 320.0f );
  1328. // don't allow too big speed changes
  1329. speed = clamp( speed, lastspeed-flmaxSpeedDelta, lastspeed+flmaxSpeedDelta );
  1330. speed = clamp( speed, -320, 320 );
  1331. lastspeed = speed;
  1332. //FIXME: This maximum speed value must come from the server.
  1333. // MaxSpeed() is not sufficient for dealing with sprinting - jdw
  1334. float bob_offset = RemapVal( speed, 0, 320, 0.0f, 1.0f );
  1335. bobtime += ( gpGlobals->curtime - lastbobtime ) * bob_offset;
  1336. lastbobtime = gpGlobals->curtime;
  1337. //Calculate the vertical bob
  1338. cycle = bobtime - (int)(bobtime/cl_bobcycle.GetFloat())*cl_bobcycle.GetFloat();
  1339. cycle /= cl_bobcycle.GetFloat();
  1340. if ( cycle < cl_bobup.GetFloat() )
  1341. {
  1342. cycle = M_PI * cycle / cl_bobup.GetFloat();
  1343. }
  1344. else
  1345. {
  1346. cycle = M_PI + M_PI*(cycle-cl_bobup.GetFloat())/(1.0 - cl_bobup.GetFloat());
  1347. }
  1348. g_verticalBob = speed*0.005f;
  1349. g_verticalBob = g_verticalBob*0.3 + g_verticalBob*0.7*sin(cycle);
  1350. g_verticalBob = clamp( g_verticalBob, -7.0f, 4.0f );
  1351. //Calculate the lateral bob
  1352. cycle = bobtime - (int)(bobtime/cl_bobcycle.GetFloat()*2)*cl_bobcycle.GetFloat()*2;
  1353. cycle /= cl_bobcycle.GetFloat()*2;
  1354. if ( cycle < cl_bobup.GetFloat() )
  1355. {
  1356. cycle = M_PI * cycle / cl_bobup.GetFloat();
  1357. }
  1358. else
  1359. {
  1360. cycle = M_PI + M_PI*(cycle-cl_bobup.GetFloat())/(1.0 - cl_bobup.GetFloat());
  1361. }
  1362. g_lateralBob = speed*0.005f;
  1363. g_lateralBob = g_lateralBob*0.3 + g_lateralBob*0.7*sin(cycle);
  1364. g_lateralBob = clamp( g_lateralBob, -7.0f, 4.0f );
  1365. //NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!)
  1366. return 0.0f;
  1367. }
  1368. //-----------------------------------------------------------------------------
  1369. // Purpose:
  1370. // Input : &origin -
  1371. // &angles -
  1372. // viewmodelindex -
  1373. //-----------------------------------------------------------------------------
  1374. void CWeaponCSBase::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles )
  1375. {
  1376. Vector forward, right;
  1377. AngleVectors( angles, &forward, &right, NULL );
  1378. CalcViewmodelBob();
  1379. // Apply bob, but scaled down to 40%
  1380. VectorMA( origin, g_verticalBob * 0.4f, forward, origin );
  1381. // Z bob a bit more
  1382. origin[2] += g_verticalBob * 0.1f;
  1383. // bob the angles
  1384. angles[ ROLL ] += g_verticalBob * 0.5f;
  1385. angles[ PITCH ] -= g_verticalBob * 0.4f;
  1386. angles[ YAW ] -= g_lateralBob * 0.3f;
  1387. // VectorMA( origin, g_lateralBob * 0.2f, right, origin );
  1388. }
  1389. #else
  1390. void CWeaponCSBase::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles )
  1391. {
  1392. }
  1393. float CWeaponCSBase::CalcViewmodelBob( void )
  1394. {
  1395. return 0.0f;
  1396. }
  1397. #endif
  1398. #ifndef CLIENT_DLL
  1399. bool CWeaponCSBase::PhysicsSplash( const Vector &centerPoint, const Vector &normal, float rawSpeed, float scaledSpeed )
  1400. {
  1401. if ( rawSpeed > 20 )
  1402. {
  1403. float size = 4.0f;
  1404. if ( !IsPistol() )
  1405. size += 2.0f;
  1406. // adjust splash size based on speed
  1407. size += RemapValClamped( rawSpeed, 0, 400, 0, 3 );
  1408. CEffectData data;
  1409. data.m_vOrigin = centerPoint;
  1410. data.m_vNormal = normal;
  1411. data.m_flScale = random->RandomFloat( size, size + 1.0f );
  1412. if ( GetWaterType() & CONTENTS_SLIME )
  1413. {
  1414. data.m_fFlags |= FX_WATER_IN_SLIME;
  1415. }
  1416. DispatchEffect( "gunshotsplash", data );
  1417. return true;
  1418. }
  1419. return false;
  1420. }
  1421. #endif // !CLIENT_DLL
  1422. //-----------------------------------------------------------------------------
  1423. // Purpose:
  1424. // Input : *pPicker -
  1425. //-----------------------------------------------------------------------------
  1426. void CWeaponCSBase::OnPickedUp( CBaseCombatCharacter *pNewOwner )
  1427. {
  1428. #if !defined( CLIENT_DLL )
  1429. RemoveEffects( EF_ITEM_BLINK );
  1430. if( pNewOwner->IsPlayer() && pNewOwner->IsAlive() )
  1431. {
  1432. m_OnPlayerPickup.FireOutput(pNewOwner, this);
  1433. // Play the pickup sound for 1st-person observers
  1434. CRecipientFilter filter;
  1435. for ( int i=0; i<gpGlobals->maxClients; ++i )
  1436. {
  1437. CBasePlayer *player = UTIL_PlayerByIndex(i);
  1438. if ( player && !player->IsAlive() && player->GetObserverMode() == OBS_MODE_IN_EYE )
  1439. {
  1440. filter.AddRecipient( player );
  1441. }
  1442. }
  1443. if ( filter.GetRecipientCount() )
  1444. {
  1445. CBaseEntity::EmitSound( filter, pNewOwner->entindex(), "Player.PickupWeapon" );
  1446. }
  1447. // Robin: We don't want to delete weapons the player has picked up, so
  1448. // clear the name of the weapon. This prevents wildcards that are meant
  1449. // to find NPCs finding weapons dropped by the NPCs as well.
  1450. SetName( NULL_STRING );
  1451. }
  1452. // Someone picked me up, so make it so that I can't be removed.
  1453. SetRemoveable( false );
  1454. #endif
  1455. }
  1456. void CWeaponCSBase::UpdateAccuracyPenalty()
  1457. {
  1458. CCSPlayer *pPlayer = GetPlayerOwner();
  1459. if ( !pPlayer )
  1460. return;
  1461. const CCSWeaponInfo& weaponInfo = GetCSWpnData();
  1462. float fNewPenalty = 0.0f;
  1463. // on ladder?
  1464. if ( pPlayer->GetMoveType() == MOVETYPE_LADDER )
  1465. {
  1466. fNewPenalty += weaponInfo.m_fInaccuracyStand[m_weaponMode] + weaponInfo.m_fInaccuracyLadder[m_weaponMode];
  1467. }
  1468. // in the air?
  1469. // else if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
  1470. // {
  1471. // fNewPenalty += weaponInfo.m_fInaccuracyStand[m_weaponMode] + weaponInfo.m_fInaccuracyJump[m_weaponMode];
  1472. // }
  1473. else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING) )
  1474. {
  1475. fNewPenalty += weaponInfo.m_fInaccuracyCrouch[m_weaponMode];
  1476. }
  1477. else
  1478. {
  1479. fNewPenalty += weaponInfo.m_fInaccuracyStand[m_weaponMode];
  1480. }
  1481. if ( m_bInReload )
  1482. {
  1483. fNewPenalty += weaponInfo.m_fInaccuracyReload;
  1484. }
  1485. if ( fNewPenalty > m_fAccuracyPenalty )
  1486. {
  1487. m_fAccuracyPenalty = fNewPenalty;
  1488. }
  1489. else
  1490. {
  1491. float fDecayFactor;
  1492. if ( pPlayer->GetMoveType() == MOVETYPE_LADDER )
  1493. {
  1494. fDecayFactor = logf(10.0f) / weaponInfo.m_fRecoveryTimeStand;
  1495. }
  1496. else if ( !FBitSet(pPlayer->GetFlags(), FL_ONGROUND) ) // in air
  1497. {
  1498. // enforce a large recovery speed penalty (300%) for players in the air; this helps to provide
  1499. // comparable in-air accuracy to the old weapon model
  1500. fDecayFactor = logf(10.0f) / (weaponInfo.m_fRecoveryTimeCrouch * 3.0f);
  1501. }
  1502. else if ( FBitSet(pPlayer->GetFlags(), FL_DUCKING) )
  1503. {
  1504. fDecayFactor = logf(10.0f) / weaponInfo.m_fRecoveryTimeCrouch;
  1505. }
  1506. else
  1507. {
  1508. fDecayFactor = logf(10.0f) / weaponInfo.m_fRecoveryTimeStand;
  1509. }
  1510. m_fAccuracyPenalty = Lerp(expf(TICK_INTERVAL * -fDecayFactor), fNewPenalty, (float)m_fAccuracyPenalty);
  1511. }
  1512. }
  1513. const float kJumpVelocity = sqrtf(2.0f * 800.0f * 57.0f); // see CCSGameMovement::CheckJumpButton()
  1514. void CWeaponCSBase::OnJump( float fImpulse )
  1515. {
  1516. m_fAccuracyPenalty += GetCSWpnData().m_fInaccuracyJump[m_weaponMode] * fImpulse / kJumpVelocity;
  1517. }
  1518. void CWeaponCSBase::OnLand( float fVelocity )
  1519. {
  1520. float fPenalty = GetCSWpnData().m_fInaccuracyLand[m_weaponMode] * fVelocity / kJumpVelocity;
  1521. m_fAccuracyPenalty += fPenalty;
  1522. /*
  1523. // this bit of code is only if we want to punch the player view on all landings
  1524. CCSPlayer *pPlayer = GetPlayerOwner();
  1525. if ( !pPlayer )
  1526. return;
  1527. QAngle angle = pPlayer->GetPunchAngle();
  1528. float fVKick = RAD2DEG(asinf(fPenalty)) * 0.4f;
  1529. float fHKick = SharedRandomFloat("LandPunchAngleYaw", -1.0f, +1.0f) * fVKick * 0.1f;
  1530. angle.x += fVKick; // pitch
  1531. angle.y += fHKick; // yaw
  1532. pPlayer->SetPunchAngle( angle );
  1533. */
  1534. }