Counter Strike : Global Offensive Source Code
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.

1457 lines
37 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. // Author: Michael S. Booth ([email protected]), 2003
  8. #include "cbase.h"
  9. #include "cs_bot.h"
  10. #include "basecsgrenade_projectile.h"
  11. #include "../../../shared/cstrike15/cs_gamerules.h"
  12. // memdbgon must be the last include file in a .cpp file!!!
  13. #include "tier0/memdbgon.h"
  14. //--------------------------------------------------------------------------------------------------------------
  15. /**
  16. * Fire our active weapon towards our current enemy
  17. * NOTE: Aiming our weapon is handled in RunBotUpkeep()
  18. */
  19. void CCSBot::FireWeaponAtEnemy( void )
  20. {
  21. if (cv_bot_dont_shoot.GetBool())
  22. {
  23. return;
  24. }
  25. CBasePlayer *enemy = GetBotEnemy();
  26. if (enemy == NULL)
  27. {
  28. return;
  29. }
  30. Vector myOrigin = GetCentroid( this );
  31. if (IsUsingSniperRifle())
  32. {
  33. // if we're using a sniper rifle, don't fire until we are standing still, are zoomed in, and not rapidly moving our view
  34. if (!IsNotMoving() || IsWaitingForZoom() || !HasViewBeenSteady( GetProfile()->GetReactionTime() ) )
  35. {
  36. return;
  37. }
  38. }
  39. if (gpGlobals->curtime > m_fireWeaponTimestamp &&
  40. GetTimeSinceAcquiredCurrentEnemy() >= GetProfile()->GetAttackDelay() &&
  41. !IsSurprised())
  42. {
  43. if (!(IsRecognizedEnemyProtectedByShield() && IsPlayerFacingMe( enemy )) && // don't shoot at enemies behind shields
  44. !IsReloading() &&
  45. !IsActiveWeaponClipEmpty() &&
  46. //gpGlobals->curtime > m_reacquireTimestamp &&
  47. IsEnemyVisible())
  48. {
  49. // we have a clear shot - pull trigger if we are aiming at enemy
  50. Vector toAimSpot = m_targetSpot - EyePosition();
  51. float rangeToEnemy = toAimSpot.NormalizeInPlace();
  52. if ( IsUsingSniperRifle() )
  53. {
  54. // check our accuracy versus our target distance
  55. float fProjectedSpread = rangeToEnemy * GetActiveCSWeapon()->GetInaccuracy();
  56. float fRequiredSpread = IsUsing( WEAPON_AWP ) ? 50.0f : 25.0f; // AWP will kill with any hit
  57. if ( fProjectedSpread > fRequiredSpread )
  58. return;
  59. }
  60. // get actual view direction vector
  61. Vector aimDir = GetViewVector();
  62. float onTarget = DotProduct( toAimSpot, aimDir );
  63. // aim more precisely with a sniper rifle
  64. // because rifles' bullets spray, don't have to be very precise
  65. const float halfSize = (IsUsingSniperRifle()) ? HalfHumanWidth : 2.0f * HalfHumanWidth;
  66. // aiming tolerance depends on how close the target is - closer targets subtend larger angles
  67. float aimTolerance = (float)cos( atan( halfSize / rangeToEnemy ) );
  68. if (onTarget > aimTolerance)
  69. {
  70. bool doAttack;
  71. // if friendly fire is on, don't fire if a teammate is blocking our line of fire
  72. if (TheCSBots()->AllowFriendlyFireDamage())
  73. {
  74. if (IsFriendInLineOfFire())
  75. doAttack = false;
  76. else
  77. doAttack = true;
  78. }
  79. else
  80. {
  81. // fire freely
  82. doAttack = true;
  83. }
  84. if (doAttack)
  85. {
  86. // if we are using a knife, only swing it if we're close
  87. if (IsUsingKnife())
  88. {
  89. const float knifeRange = 75.0f; // 50
  90. if (rangeToEnemy < knifeRange)
  91. {
  92. // since we've given ourselves away - run!
  93. ForceRun( 5.0f );
  94. // if our prey is facing away, backstab him!
  95. if (!IsPlayerFacingMe( enemy ))
  96. {
  97. SecondaryAttack();
  98. }
  99. else
  100. {
  101. // randomly choose primary and secondary attacks with knife
  102. const float knifeStabChance = 33.3f;
  103. if (RandomFloat( 0, 100 ) < knifeStabChance)
  104. SecondaryAttack();
  105. else
  106. PrimaryAttack();
  107. }
  108. }
  109. }
  110. else
  111. {
  112. PrimaryAttack();
  113. }
  114. }
  115. if (IsUsingPistol())
  116. {
  117. // high-skill bots fire their pistols quickly at close range
  118. const float closePistolRange = 360.0f;
  119. if (GetProfile()->GetSkill() > 0.75f && rangeToEnemy < closePistolRange)
  120. {
  121. // fire as fast as possible
  122. m_fireWeaponTimestamp = 0.0f;
  123. }
  124. else
  125. {
  126. // fire somewhat quickly
  127. m_fireWeaponTimestamp = RandomFloat( 0.15f, 0.4f );
  128. }
  129. }
  130. else // not using a pistol
  131. {
  132. const float sprayRange = 400.0f;
  133. if (GetProfile()->GetSkill() < 0.5f || rangeToEnemy < sprayRange || IsUsingMachinegun())
  134. {
  135. // spray 'n pray if enemy is close, or we're not that good, or we're using the big machinegun
  136. m_fireWeaponTimestamp = 0.0f;
  137. }
  138. else
  139. {
  140. const float distantTargetRange = 800.0f;
  141. if (!IsUsingSniperRifle() && rangeToEnemy > distantTargetRange)
  142. {
  143. // if very far away, fire slowly for better accuracy
  144. m_fireWeaponTimestamp = RandomFloat( 0.3f, 0.7f );
  145. }
  146. else
  147. {
  148. // fire short bursts for accuracy
  149. m_fireWeaponTimestamp = RandomFloat( 0.15f, 0.25f ); // 0.15, 0.5
  150. }
  151. }
  152. }
  153. // subtract system latency
  154. m_fireWeaponTimestamp -= g_BotUpdateInterval;
  155. m_fireWeaponTimestamp += gpGlobals->curtime;
  156. }
  157. }
  158. }
  159. }
  160. //--------------------------------------------------------------------------------------------------------------
  161. /**
  162. * Set the current aim offset
  163. */
  164. void CCSBot::PickNewAimSpot()
  165. {
  166. // aim at enemy, if he's still alive
  167. if (m_enemy != NULL && m_enemy->IsAlive())
  168. {
  169. Vector enemyOrigin = GetCentroid( m_enemy );
  170. if ( IsEnemyVisible() )
  171. {
  172. //
  173. // Enemy is visible - determine which part of him to shoot at
  174. //
  175. const float sharpshooter = 0.8f;
  176. VisiblePartType aimAtPart;
  177. if (IsUsingMachinegun())
  178. {
  179. // spray the big machinegun at the enemy's gut
  180. aimAtPart = GUT;
  181. }
  182. else if (IsUsing( WEAPON_AWP ) || IsUsingShotgun())
  183. {
  184. // these weapons are best aimed at the chest
  185. aimAtPart = GUT;
  186. }
  187. else if (GetProfile()->GetSkill() > 0.5f && IsActiveWeaponRecoilHigh() )
  188. {
  189. // sprayin' and prayin' - aim at the gut since we're not going to be accurate
  190. aimAtPart = GUT;
  191. }
  192. else if (GetProfile()->GetSkill() < sharpshooter)
  193. {
  194. // low skill bots don't go for headshots
  195. aimAtPart = GUT;
  196. }
  197. else
  198. {
  199. // high skill - aim for the head
  200. aimAtPart = HEAD;
  201. }
  202. if (IsEnemyPartVisible( aimAtPart ))
  203. {
  204. m_targetSpot = GetPartPosition( GetBotEnemy(), aimAtPart );
  205. }
  206. else
  207. {
  208. // desired part is blocked - aim at whatever part is visible
  209. if (IsEnemyPartVisible( GUT ))
  210. {
  211. m_targetSpot = GetPartPosition( GetBotEnemy(), GUT );
  212. }
  213. else if (IsEnemyPartVisible( HEAD ))
  214. {
  215. m_targetSpot = GetPartPosition( GetBotEnemy(), HEAD );
  216. }
  217. else if (IsEnemyPartVisible( LEFT_SIDE ))
  218. {
  219. m_targetSpot = GetPartPosition( GetBotEnemy(), LEFT_SIDE );
  220. }
  221. else if (IsEnemyPartVisible( RIGHT_SIDE ))
  222. {
  223. m_targetSpot = GetPartPosition( GetBotEnemy(), RIGHT_SIDE );
  224. }
  225. else // FEET
  226. {
  227. m_targetSpot = GetPartPosition( GetBotEnemy(), FEET );
  228. }
  229. }
  230. // temp test
  231. m_targetSpot = GetPartPosition( GetBotEnemy(), GUT );
  232. m_targetSpotVelocity = m_enemy->GetAbsVelocity();
  233. m_targetSpotTime = gpGlobals->curtime;
  234. }
  235. else
  236. {
  237. // aim where we last saw enemy - but bend the ray so we don't point directly into walls
  238. // if we put this back, make sure you only bend the ray ONCE and keep the bent spot - don't continually recompute
  239. //BendLineOfSight( m_eyePosition, m_lastEnemyPosition, &m_aimSpot );
  240. m_targetSpot = m_lastEnemyPosition;
  241. m_targetSpotVelocity.Zero();
  242. }
  243. // decay old aim focus angle
  244. m_aimFocus *= expf(logf(GetProfile()->GetAimFocusDecay()) * m_aimFocusInterval);
  245. // calculate current aim focus maxima
  246. Vector toTarget = m_targetSpot - EyePositionConst();
  247. QAngle aimGoal;
  248. VectorAngles( toTarget, aimGoal );
  249. QAngle viewAngles = EyeAngles();
  250. float deltaYaw = AngleDistance(viewAngles.y, aimGoal.y);
  251. float deltaPitch = AngleDistance(viewAngles.x, aimGoal.x);
  252. float fAngleOffset = sqrtf(deltaYaw * deltaYaw + deltaPitch * deltaPitch);
  253. float fNewMaxFocus = MIN(60.0f, fAngleOffset) * GetProfile()->GetAimFocusOffsetScale();
  254. m_aimFocus = MAX(m_aimFocus, fNewMaxFocus);
  255. float fTheta = RandomFloat(0.0f, 2.0f * M_PI);
  256. float fRadius = RandomFloat(0.0f, m_aimFocus);
  257. m_aimError[YAW] = fRadius * cosf(fTheta);
  258. m_aimError[PITCH] = fRadius * sinf(fTheta);
  259. }
  260. // define time when aim offset will automatically be updated
  261. m_aimFocusInterval = GetProfile()->GetAimFocusInterval();
  262. m_aimFocusInterval *= RandomFloat(0.8f, 1.2f); // add some randomness
  263. m_aimFocusNextUpdate = gpGlobals->curtime + m_aimFocusInterval;
  264. }
  265. //--------------------------------------------------------------------------------------------------------------
  266. /**
  267. * Wiggle aim error based on GetProfile()->GetSkill()
  268. */
  269. void CCSBot::UpdateAimPrediction( void )
  270. {
  271. float fTimeSinceAimSpot = gpGlobals->curtime - m_targetSpotTime;
  272. m_targetSpotPredicted = m_targetSpot + fTimeSinceAimSpot * m_targetSpotVelocity;
  273. Vector toTarget = m_targetSpotPredicted - EyePositionConst();
  274. VectorAngles( toTarget, m_aimGoal );
  275. if ( m_aimGoal[PITCH] > 180.0f )
  276. m_aimGoal[PITCH] -= 360.f;
  277. m_aimGoal += m_aimError;
  278. // adjust aim angle for recoil
  279. float fSkill = GetProfile()->GetSkill(); // 0.0 .. 1.0
  280. float fPunchAngleCorrectionFactor = 1.0f + (1.f - fSkill) * .8f * SlowNoise( 6.f );
  281. const QAngle &punchAngles = GetAimPunchAngle();
  282. m_aimGoal -= punchAngles * fPunchAngleCorrectionFactor;
  283. m_aimGoal[PITCH] = clamp(m_aimGoal[PITCH], -89.0f, +89.0f);
  284. }
  285. //--------------------------------------------------------------------------------------------------------------
  286. /**
  287. * Change our zoom level to be appropriate for the given range.
  288. * Return true if the zoom level changed.
  289. */
  290. bool CCSBot::AdjustZoom( float range )
  291. {
  292. bool adjustZoom = false;
  293. if (IsUsingSniperRifle())
  294. {
  295. const float sniperZoomRange = 150.0f; // NOTE: This must be less than sniperMinRange in AttackState
  296. const float sniperFarZoomRange = 1500.0f;
  297. // if range is too close, don't zoom
  298. if (range <= sniperZoomRange)
  299. {
  300. // zoom out
  301. if (GetZoomLevel() != NO_ZOOM)
  302. {
  303. adjustZoom = true;
  304. }
  305. }
  306. else if (range < sniperFarZoomRange)
  307. {
  308. // maintain low zoom
  309. if (GetZoomLevel() != LOW_ZOOM)
  310. {
  311. adjustZoom = true;
  312. }
  313. }
  314. else
  315. {
  316. // maintain high zoom
  317. if (GetZoomLevel() != HIGH_ZOOM)
  318. {
  319. adjustZoom = true;
  320. }
  321. }
  322. }
  323. else
  324. {
  325. // zoom out
  326. if (GetZoomLevel() != NO_ZOOM)
  327. {
  328. adjustZoom = true;
  329. }
  330. }
  331. if (adjustZoom)
  332. {
  333. SecondaryAttack();
  334. // pause after zoom to allow "eyes" to refocus
  335. // m_zoomTimer.Start( 0.25f + (1.0f - GetProfile()->GetSkill()) );
  336. m_zoomTimer.Start( 0.25f );
  337. }
  338. return adjustZoom;
  339. }
  340. //--------------------------------------------------------------------------------------------------------------
  341. /**
  342. * Returns true if using the specific weapon
  343. */
  344. bool CCSBot::IsUsing( CSWeaponID weaponID ) const
  345. {
  346. CWeaponCSBase *weapon = GetActiveCSWeapon();
  347. if (weapon == NULL)
  348. return false;
  349. if (weapon->IsA( weaponID ))
  350. return true;
  351. return false;
  352. }
  353. //--------------------------------------------------------------------------------------------------------------
  354. /**
  355. * Returns true if we are using a weapon with a removable silencer
  356. */
  357. bool CCSBot::DoesActiveWeaponHaveRemoveableSilencer( void ) const
  358. {
  359. CWeaponCSBase *weapon = GetActiveCSWeapon();
  360. if (weapon == NULL)
  361. return false;
  362. if ( weapon->IsA( WEAPON_USP ) )
  363. return true;
  364. return false;
  365. }
  366. //--------------------------------------------------------------------------------------------------------------
  367. /**
  368. * Return true if we are using a sniper rifle
  369. */
  370. bool CCSBot::IsUsingSniperRifle( void ) const
  371. {
  372. CWeaponCSBase *weapon = GetActiveCSWeapon();
  373. if (weapon && IsSniperRifle( weapon ))
  374. return true;
  375. return false;
  376. }
  377. //--------------------------------------------------------------------------------------------------------------
  378. /**
  379. * Return true if we have a sniper rifle in our inventory
  380. */
  381. bool CCSBot::IsSniper( void ) const
  382. {
  383. CWeaponCSBase *weapon = static_cast<CWeaponCSBase *>( Weapon_GetSlot( WEAPON_SLOT_RIFLE ) );
  384. if (weapon && IsSniperRifle( weapon ))
  385. return true;
  386. return false;
  387. }
  388. //--------------------------------------------------------------------------------------------------------------
  389. /**
  390. * Return true if we are actively sniping (moving to sniper spot or settled in)
  391. */
  392. bool CCSBot::IsSniping( void ) const
  393. {
  394. if (GetTask() == MOVE_TO_SNIPER_SPOT || GetTask() == SNIPING)
  395. return true;
  396. return false;
  397. }
  398. //--------------------------------------------------------------------------------------------------------------
  399. /**
  400. * Return true if we are using a shotgun
  401. */
  402. bool CCSBot::IsUsingShotgun( void ) const
  403. {
  404. CWeaponCSBase *weapon = GetActiveCSWeapon();
  405. if (weapon == NULL)
  406. return false;
  407. return weapon->IsKindOf(WEAPONTYPE_SHOTGUN);
  408. }
  409. //--------------------------------------------------------------------------------------------------------------
  410. /**
  411. * Returns true if using the big 'ol machinegun
  412. */
  413. bool CCSBot::IsUsingMachinegun( void ) const
  414. {
  415. CWeaponCSBase *weapon = GetActiveCSWeapon();
  416. if (weapon && weapon->IsA( WEAPON_M249 ))
  417. return true;
  418. return false;
  419. }
  420. //--------------------------------------------------------------------------------------------------------------
  421. /**
  422. * Return true if primary weapon doesn't exist or is totally out of ammo
  423. */
  424. bool CCSBot::IsPrimaryWeaponEmpty( void ) const
  425. {
  426. CWeaponCSBase *weapon = static_cast<CWeaponCSBase *>( Weapon_GetSlot( WEAPON_SLOT_RIFLE ) );
  427. if (weapon == NULL)
  428. return true;
  429. // check if gun has any ammo left
  430. if (weapon->HasAnyAmmo())
  431. return false;
  432. return true;
  433. }
  434. //--------------------------------------------------------------------------------------------------------------
  435. /**
  436. * Return true if pistol doesn't exist or is totally out of ammo
  437. */
  438. bool CCSBot::IsPistolEmpty( void ) const
  439. {
  440. CWeaponCSBase *weapon = static_cast<CWeaponCSBase *>( Weapon_GetSlot( WEAPON_SLOT_PISTOL ) );
  441. if (weapon == NULL)
  442. return true;
  443. // check if gun has any ammo left
  444. if (weapon->HasAnyAmmo())
  445. return false;
  446. return true;
  447. }
  448. //--------------------------------------------------------------------------------------------------------------
  449. /**
  450. * Equip the given item
  451. */
  452. bool CCSBot::DoEquip( CWeaponCSBase *weapon )
  453. {
  454. if (weapon == NULL)
  455. return false;
  456. // check if weapon has any ammo left
  457. if (!weapon->HasAnyAmmo())
  458. return false;
  459. // equip it
  460. SelectItem( weapon->GetClassname() );
  461. m_equipTimer.Start();
  462. return true;
  463. }
  464. // throttle how often equipping is allowed
  465. const float minEquipInterval = 5.0f;
  466. //--------------------------------------------------------------------------------------------------------------
  467. /**
  468. * Equip the best weapon we are carrying that has ammo
  469. */
  470. void CCSBot::EquipBestWeapon( bool mustEquip )
  471. {
  472. // throttle how often equipping is allowed
  473. if (!mustEquip && m_equipTimer.GetElapsedTime() < minEquipInterval)
  474. return;
  475. CCSBotManager *ctrl = static_cast<CCSBotManager *>( TheBots );
  476. CWeaponCSBase *primary = static_cast<CWeaponCSBase *>( Weapon_GetSlot( WEAPON_SLOT_RIFLE ) );
  477. if (primary)
  478. {
  479. CSWeaponType weaponClass = primary->GetWeaponType();
  480. if ((ctrl->AllowShotguns() && weaponClass == WEAPONTYPE_SHOTGUN) ||
  481. (ctrl->AllowMachineGuns() && weaponClass == WEAPONTYPE_MACHINEGUN) ||
  482. (ctrl->AllowRifles() && weaponClass == WEAPONTYPE_RIFLE) ||
  483. (ctrl->AllowShotguns() && weaponClass == WEAPONTYPE_SHOTGUN) ||
  484. (ctrl->AllowSnipers() && weaponClass == WEAPONTYPE_SNIPER_RIFLE) ||
  485. (ctrl->AllowSubMachineGuns() && weaponClass == WEAPONTYPE_SUBMACHINEGUN))
  486. {
  487. if (DoEquip( primary ))
  488. return;
  489. }
  490. }
  491. if (ctrl->AllowPistols())
  492. {
  493. if (DoEquip( static_cast<CWeaponCSBase *>( Weapon_GetSlot( WEAPON_SLOT_PISTOL ) ) ))
  494. return;
  495. }
  496. // always have a knife
  497. EquipKnife();
  498. }
  499. //--------------------------------------------------------------------------------------------------------------
  500. /**
  501. * Equip our pistol
  502. */
  503. void CCSBot::EquipPistol( void )
  504. {
  505. // throttle how often equipping is allowed
  506. if (m_equipTimer.GetElapsedTime() < minEquipInterval)
  507. return;
  508. if (TheCSBots()->AllowPistols() && !IsUsingPistol())
  509. {
  510. CWeaponCSBase *pistol = static_cast<CWeaponCSBase *>( Weapon_GetSlot( WEAPON_SLOT_PISTOL ) );
  511. DoEquip( pistol );
  512. }
  513. }
  514. //--------------------------------------------------------------------------------------------------------------
  515. /**
  516. * Equip the knife
  517. */
  518. void CCSBot::EquipKnife( void )
  519. {
  520. if (!IsUsingKnife())
  521. {
  522. SelectItem( "weapon_knife" );
  523. }
  524. }
  525. //--------------------------------------------------------------------------------------------------------------
  526. /**
  527. * Return true if we have a grenade in our inventory
  528. */
  529. bool CCSBot::HasGrenade( void ) const
  530. {
  531. CWeaponCSBase *grenade = static_cast<CWeaponCSBase *>( Weapon_GetSlot( WEAPON_SLOT_GRENADES ) );
  532. return (grenade) ? true : false;
  533. }
  534. //--------------------------------------------------------------------------------------------------------------
  535. /**
  536. * Equip a grenade, return false if we cant
  537. */
  538. bool CCSBot::EquipGrenade( bool noSmoke )
  539. {
  540. // snipers don't use grenades
  541. if (IsSniper())
  542. return false;
  543. if ( CSGameRules()->IsPlayingOffline() && CSGameRules()->GetCustomBotDifficulty() == CUSTOM_BOT_DIFFICULTY_DUMB )
  544. {
  545. // For Offline games: Bots in dumb mode should not toss grenades
  546. return false;
  547. }
  548. if (IsUsingGrenade())
  549. return true;
  550. if (HasGrenade())
  551. {
  552. CWeaponCSBase *grenade = static_cast<CWeaponCSBase *>( Weapon_GetSlot( WEAPON_SLOT_GRENADES ) );
  553. if (noSmoke && grenade->IsA( WEAPON_SMOKEGRENADE ))
  554. return false;
  555. SelectItem( grenade->GetClassname() );
  556. return true;
  557. }
  558. return false;
  559. }
  560. //--------------------------------------------------------------------------------------------------------------
  561. /**
  562. * Returns true if we have knife equipped
  563. */
  564. bool CCSBot::IsUsingKnife( void ) const
  565. {
  566. CWeaponCSBase *weapon = GetActiveCSWeapon();
  567. if (weapon && (weapon->IsA( WEAPON_KNIFE ) || weapon->IsA( WEAPON_KNIFE_GG )))
  568. return true;
  569. return false;
  570. }
  571. //--------------------------------------------------------------------------------------------------------------
  572. /**
  573. * Returns true if we have pistol equipped
  574. */
  575. bool CCSBot::IsUsingPistol( void ) const
  576. {
  577. CWeaponCSBase *weapon = GetActiveCSWeapon();
  578. if (weapon && weapon->IsPistol())
  579. return true;
  580. return false;
  581. }
  582. //--------------------------------------------------------------------------------------------------------------
  583. /**
  584. * Returns true if we have a grenade equipped
  585. */
  586. bool CCSBot::IsUsingGrenade( void ) const
  587. {
  588. CWeaponCSBase *weapon = GetActiveCSWeapon();
  589. if (!weapon)
  590. return false;
  591. if (weapon->IsA( WEAPON_FLASHBANG ) ||
  592. weapon->IsA( WEAPON_SMOKEGRENADE ) ||
  593. weapon->IsA( WEAPON_HEGRENADE ) ||
  594. weapon->IsA( WEAPON_MOLOTOV ) ||
  595. weapon->IsA( WEAPON_INCGRENADE ) ||
  596. weapon->IsA( WEAPON_DECOY ) ||
  597. weapon->IsA( WEAPON_TAGRENADE ) )
  598. return true;
  599. return false;
  600. }
  601. //--------------------------------------------------------------------------------------------------------------
  602. /**
  603. * Begin the process of throwing the grenade
  604. */
  605. void CCSBot::ThrowGrenade( const Vector &target )
  606. {
  607. if (IsUsingGrenade() && m_grenadeTossState == NOT_THROWING && !IsOnLadder())
  608. {
  609. m_grenadeTossState = START_THROW;
  610. m_tossGrenadeTimer.Start( 2.0f ); // wait up to two seconds for teammates to clear out of the way
  611. Vector bend = target;
  612. BendLineOfSight( target, GetLastKnownEnemyPosition(), &bend );
  613. const float angleTolerance = 3.0f;
  614. SetLookAt( "GrenadeThrowBend", bend, PRIORITY_UNINTERRUPTABLE, 4.0f, false, angleTolerance );
  615. if ( !CSGameRules()->IsPlayingCooperativeGametype() )
  616. Wait( RandomFloat( 2.0f, 4.0f ) );
  617. if (cv_bot_debug.GetBool() && IsLocalPlayerWatchingMe())
  618. {
  619. NDebugOverlay::Cross3D( bend, 25.0f, 125, 255, 125, true, 300.0f );
  620. }
  621. PrintIfWatched( "%3.2f: Grenade: START_THROW\n", gpGlobals->curtime );
  622. }
  623. }
  624. //--------------------------------------------------------------------------------------------------------------
  625. /**
  626. * Returns true if our weapon can attack
  627. */
  628. bool CCSBot::CanActiveWeaponFire( void ) const
  629. {
  630. return ( GetActiveWeapon() && GetActiveWeapon()->m_flNextPrimaryAttack <= gpGlobals->curtime );
  631. }
  632. //--------------------------------------------------------------------------------------------------------------
  633. /**
  634. * Find spot to throw grenade ahead of us and "around the corner" along our path
  635. */
  636. bool CCSBot::FindGrenadeTossPathTarget( Vector *pos )
  637. {
  638. if (!HasPath())
  639. return false;
  640. // find farthest point we can see on the path
  641. int i;
  642. for( i=m_pathIndex; i<m_pathLength; ++i )
  643. {
  644. if (!FVisible( m_path[i].pos + Vector( 0, 0, HalfHumanHeight ) ))
  645. break;
  646. }
  647. if (i == m_pathIndex)
  648. return false;
  649. // find exact spot where we lose sight
  650. Vector dir = m_path[i].pos - m_path[i-1].pos;
  651. float length = dir.NormalizeInPlace();
  652. const float inc = 25.0f;
  653. Vector p;
  654. Vector visibleSpot = m_path[i-1].pos;
  655. for( float t = 0.0f; t<length; t += inc )
  656. {
  657. p = m_path[i-1].pos + t * dir;
  658. p.z += HalfHumanHeight;
  659. if (!FVisible( p ))
  660. break;
  661. visibleSpot = p;
  662. }
  663. // massage the location a bit
  664. visibleSpot.z += 10.0f;
  665. const float bufferRange = 50.0f;
  666. trace_t result;
  667. Vector check;
  668. // check +X
  669. check = visibleSpot + Vector( 999.9f, 0, 0 );
  670. UTIL_TraceLine( visibleSpot, check, MASK_PLAYERSOLID, this, COLLISION_GROUP_NONE, &result );
  671. if (result.fraction < 1.0f)
  672. {
  673. float range = result.endpos.x - visibleSpot.x;
  674. if (range < bufferRange)
  675. {
  676. visibleSpot.x = result.endpos.x - bufferRange;
  677. }
  678. }
  679. // check -X
  680. check = visibleSpot + Vector( -999.9f, 0, 0 );
  681. UTIL_TraceLine( visibleSpot, check, MASK_PLAYERSOLID, this, COLLISION_GROUP_NONE, &result );
  682. if (result.fraction < 1.0f)
  683. {
  684. float range = visibleSpot.x - result.endpos.x;
  685. if (range < bufferRange)
  686. {
  687. visibleSpot.x = result.endpos.x + bufferRange;
  688. }
  689. }
  690. // check +Y
  691. check = visibleSpot + Vector( 0, 999.9f, 0 );
  692. UTIL_TraceLine( visibleSpot, check, MASK_PLAYERSOLID, this, COLLISION_GROUP_NONE, &result );
  693. if (result.fraction < 1.0f)
  694. {
  695. float range = result.endpos.y - visibleSpot.y;
  696. if (range < bufferRange)
  697. {
  698. visibleSpot.y = result.endpos.y - bufferRange;
  699. }
  700. }
  701. // check -Y
  702. check = visibleSpot + Vector( 0, -999.9f, 0 );
  703. UTIL_TraceLine( visibleSpot, check, MASK_PLAYERSOLID, this, COLLISION_GROUP_NONE, &result );
  704. if (result.fraction < 1.0f)
  705. {
  706. float range = visibleSpot.y - result.endpos.y;
  707. if (range < bufferRange)
  708. {
  709. visibleSpot.y = result.endpos.y + bufferRange;
  710. }
  711. }
  712. *pos = visibleSpot;
  713. return true;
  714. }
  715. //--------------------------------------------------------------------------------------------------------------
  716. /**
  717. * Look for grenade throw targets and throw the grenade
  718. */
  719. void CCSBot::LookForGrenadeTargets( void )
  720. {
  721. if (!IsUsingGrenade() || IsThrowingGrenade())
  722. {
  723. return;
  724. }
  725. const CNavArea *tossArea = GetInitialEncounterArea();
  726. if (tossArea == NULL)
  727. {
  728. return;
  729. }
  730. int enemyTeam = OtherTeam( GetTeamNumber() );
  731. // check if we should put our grenade away
  732. if (tossArea->GetEarliestOccupyTime( enemyTeam ) > gpGlobals->curtime)
  733. {
  734. EquipBestWeapon( MUST_EQUIP );
  735. return;
  736. }
  737. // throw grenades at initial encounter area
  738. Vector tossTarget = Vector( 0, 0, 0 );
  739. if (!tossArea->IsVisible( EyePosition(), &tossTarget ))
  740. {
  741. return;
  742. }
  743. CWeaponCSBase *weapon = GetActiveCSWeapon();
  744. if (weapon && weapon->IsA( WEAPON_SMOKEGRENADE ))
  745. {
  746. // don't worry so much about smokes
  747. ThrowGrenade( tossTarget );
  748. PrintIfWatched( "Throwing smoke grenade!" );
  749. SetInitialEncounterArea( NULL );
  750. return;
  751. }
  752. else // explosive and flashbang grenades
  753. {
  754. // initial encounter area is visible, wait to throw until timing is right
  755. const float leadTime = 1.5f;
  756. float enemyTime = tossArea->GetEarliestOccupyTime( enemyTeam );
  757. if (enemyTime - TheCSBots()->GetElapsedRoundTime() > leadTime)
  758. {
  759. // don't throw yet
  760. return;
  761. }
  762. Vector to = tossTarget - EyePosition();
  763. float range = to.Length();
  764. const float slope = 0.2f; // 0.25f;
  765. float tossHeight = slope * range;
  766. trace_t result;
  767. CTraceFilterNoNPCsOrPlayer traceFilter( this, COLLISION_GROUP_NONE );
  768. const float heightInc = tossHeight / 10.0f;
  769. Vector target;
  770. float safeSpace = tossHeight / 2.0f;
  771. // Build a box to sweep along the ray when looking for obstacles
  772. const Vector& eyePosition = EyePosition();
  773. Vector mins = VEC_HULL_MIN;
  774. Vector maxs = VEC_HULL_MAX;
  775. mins.z = 0;
  776. maxs.z = heightInc;
  777. // find low and high bounds of toss window
  778. float low = 0.0f;
  779. float high = tossHeight + safeSpace;
  780. bool gotLow = false;
  781. float lastH = 0.0f;
  782. for( float h = 0.0f; h < 3.0f * tossHeight; h += heightInc )
  783. {
  784. target = tossTarget + Vector( 0, 0, h );
  785. // make sure toss line is clear
  786. QAngle angles( 0, 0, 0 );
  787. Ray_t ray;
  788. ray.Init( eyePosition, target, mins, maxs );
  789. enginetrace->TraceRay( ray, MASK_VISIBLE_AND_NPCS | CONTENTS_GRATE, &traceFilter, &result );
  790. if (result.fraction == 1.0f)
  791. {
  792. //NDebugOverlay::SweptBox( eyePosition, target, mins, maxs, angles, 0, 0, 255, 40, 10.0f );
  793. // line is clear
  794. if (!gotLow)
  795. {
  796. low = h;
  797. gotLow = true;
  798. }
  799. }
  800. else
  801. {
  802. //NDebugOverlay::SweptBox( eyePosition, target, mins, maxs, angles, 255, 0, 0, 5, 10.0f );
  803. // line is blocked
  804. if (gotLow)
  805. {
  806. high = lastH;
  807. break;
  808. }
  809. }
  810. lastH = h;
  811. }
  812. if (gotLow)
  813. {
  814. // throw grenade into toss window
  815. if (tossHeight < low)
  816. {
  817. if (low + safeSpace > high)
  818. {
  819. // narrow window
  820. tossHeight = (high + low)/2.0f;
  821. }
  822. else
  823. {
  824. tossHeight = low + safeSpace;
  825. }
  826. }
  827. else if (tossHeight > high - safeSpace)
  828. {
  829. if (high - safeSpace < low)
  830. {
  831. // narrow window
  832. tossHeight = (high + low)/2.0f;
  833. }
  834. else
  835. {
  836. tossHeight = high - safeSpace;
  837. }
  838. }
  839. ThrowGrenade( tossTarget + Vector( 0, 0, tossHeight ) );
  840. SetInitialEncounterArea( NULL );
  841. return;
  842. }
  843. }
  844. }
  845. //--------------------------------------------------------------------------------------------------------------
  846. class FOVClearOfFriends
  847. {
  848. public:
  849. FOVClearOfFriends( CCSBot *me )
  850. {
  851. m_me = me;
  852. }
  853. bool operator() ( CBasePlayer *player )
  854. {
  855. if (player == m_me || !player->IsAlive())
  856. return true;
  857. if (m_me->InSameTeam( player ))
  858. {
  859. Vector to = player->EyePosition() - m_me->EyePosition();
  860. to.NormalizeInPlace();
  861. Vector forward;
  862. m_me->EyeVectors( &forward );
  863. if (DotProduct( to, forward ) > 0.95f)
  864. {
  865. if (m_me->IsVisible( (CCSPlayer *)player ))
  866. {
  867. // we see a friend in our FOV
  868. return false;
  869. }
  870. }
  871. }
  872. return true;
  873. }
  874. CCSBot *m_me;
  875. };
  876. //--------------------------------------------------------------------------------------------------------------
  877. /**
  878. * Process the grenade throw state machine
  879. */
  880. void CCSBot::UpdateGrenadeThrow( void )
  881. {
  882. switch( m_grenadeTossState )
  883. {
  884. case START_THROW:
  885. {
  886. if (m_tossGrenadeTimer.IsElapsed())
  887. {
  888. // something prevented the throw - give up
  889. EquipBestWeapon( MUST_EQUIP );
  890. ClearLookAt();
  891. m_grenadeTossState = NOT_THROWING;
  892. PrintIfWatched( "%3.2f: Grenade: THROW FAILED\n", gpGlobals->curtime );
  893. return;
  894. }
  895. if (m_lookAtSpotState == LOOK_AT_SPOT)
  896. {
  897. // don't throw if there are friends ahead of us
  898. FOVClearOfFriends fovClear( this );
  899. if (ForEachPlayer( fovClear ))
  900. {
  901. m_grenadeTossState = FINISH_THROW;
  902. float flGrenadeTossDelay = CSGameRules()->IsPlayingCooperativeGametype() ? 0.01f : 1.0f;
  903. m_tossGrenadeTimer.Start( flGrenadeTossDelay );
  904. PrintIfWatched( "%3.2f: Grenade: FINISH_THROW\n", gpGlobals->curtime );
  905. }
  906. else
  907. {
  908. PrintIfWatched( "%3.2f: Grenade: Friend is in the way...\n", gpGlobals->curtime );
  909. }
  910. }
  911. // hold in the trigger and be ready to throw
  912. PrimaryAttack();
  913. break;
  914. }
  915. case FINISH_THROW:
  916. {
  917. // throw the grenade and hold our aiming line for a moment
  918. if (m_tossGrenadeTimer.IsElapsed())
  919. {
  920. ClearLookAt();
  921. m_grenadeTossState = NOT_THROWING;
  922. PrintIfWatched( "%3.2f: Grenade: THROW COMPLETE\n", gpGlobals->curtime );
  923. }
  924. break;
  925. }
  926. default:
  927. {
  928. if (IsUsingGrenade())
  929. {
  930. // pull the pin
  931. PrimaryAttack();
  932. }
  933. break;
  934. }
  935. }
  936. }
  937. //--------------------------------------------------------------------------------------------------------------
  938. class GrenadeResponse
  939. {
  940. public:
  941. GrenadeResponse( CCSBot *me )
  942. {
  943. m_me = me;
  944. }
  945. bool operator() ( ActiveGrenade *ag ) const
  946. {
  947. const float retreatRange = 300.0f;
  948. const float hideTime = 1.0f;
  949. // do we see this grenade
  950. if (m_me->IsVisible( ag->GetPosition(), CHECK_FOV, (CBaseEntity *)ag->GetEntity() ))
  951. {
  952. // we see it
  953. if (ag->IsSmoke())
  954. {
  955. // ignore smokes
  956. return true;
  957. }
  958. Vector velDir = ag->GetEntity()->GetAbsVelocity();
  959. float grenadeSpeed = velDir.NormalizeInPlace();
  960. const float atRestSpeed = 50.0f;
  961. const float aboutToBlow = 0.5f;
  962. if (ag->IsFlashbang() && ag->GetEntity()->m_flDetonateTime - gpGlobals->curtime < aboutToBlow)
  963. {
  964. // turn away from flashbangs about to explode
  965. QAngle eyeAngles = m_me->EyeAngles();
  966. float yaw = RandomFloat( 100.0f, 135.0f );
  967. eyeAngles.y += (RandomFloat( -1.0f, 1.0f ) < 0.0f) ? (-yaw) : yaw;
  968. Vector forward;
  969. AngleVectors( eyeAngles, &forward );
  970. Vector away = m_me->EyePosition() - 1000.0f * forward;
  971. const float duration = 2.0f;
  972. m_me->ClearLookAt();
  973. m_me->SetLookAt( "Avoid Flashbang", away, PRIORITY_UNINTERRUPTABLE, duration );
  974. m_me->StopAiming();
  975. return false;
  976. }
  977. // flee from grenades if close by or thrown towards us
  978. const float throwDangerRange = 750.0f;
  979. const float nearDangerRange = 300.0f;
  980. Vector to = ag->GetPosition() - m_me->GetAbsOrigin();
  981. float range = to.NormalizeInPlace();
  982. if (range > throwDangerRange)
  983. {
  984. return true;
  985. }
  986. if (grenadeSpeed > atRestSpeed)
  987. {
  988. // grenade is moving
  989. if (DotProduct( to, velDir ) >= -0.5f)
  990. {
  991. // going away from us
  992. return true;
  993. }
  994. m_me->PrintIfWatched( "Retreating from a grenade thrown towards me!\n" );
  995. }
  996. else if (range < nearDangerRange)
  997. {
  998. // grenade has come to rest near us
  999. m_me->PrintIfWatched( "Retreating from a grenade that landed near me!\n" );
  1000. }
  1001. // retreat!
  1002. m_me->TryToRetreat( retreatRange, hideTime );
  1003. return false;
  1004. }
  1005. return true;
  1006. }
  1007. CCSBot *m_me;
  1008. };
  1009. /**
  1010. * React to enemy grenades we see
  1011. */
  1012. void CCSBot::AvoidEnemyGrenades( void )
  1013. {
  1014. // low skill bots dont avoid grenades
  1015. if (GetProfile()->GetSkill() < 0.5)
  1016. {
  1017. return;
  1018. }
  1019. if (IsAvoidingGrenade())
  1020. {
  1021. // already avoiding one
  1022. return;
  1023. }
  1024. // low skill bots don't avoid grenades
  1025. if (GetProfile()->GetSkill() < 0.6f)
  1026. {
  1027. return;
  1028. }
  1029. GrenadeResponse respond( this );
  1030. if (TheBots->ForEachGrenade( respond ) == false)
  1031. {
  1032. const float avoidTime = 4.0f;
  1033. m_isAvoidingGrenade.Start( avoidTime );
  1034. }
  1035. }
  1036. //--------------------------------------------------------------------------------------------------------------
  1037. /**
  1038. * Reload our weapon if we must
  1039. */
  1040. void CCSBot::ReloadCheck( void )
  1041. {
  1042. const float safeReloadWaitTime = 3.0f;
  1043. const float reloadAmmoRatio = 0.6f;
  1044. // don't bother to reload if there are no enemies left
  1045. if (GetEnemiesRemaining() == 0)
  1046. return;
  1047. if (IsDefusingBomb() || IsReloading())
  1048. return;
  1049. if (IsActiveWeaponClipEmpty())
  1050. {
  1051. // high-skill players switch to pistol instead of reloading during combat
  1052. if (GetProfile()->GetSkill() > 0.5f && IsAttacking())
  1053. {
  1054. if (!GetActiveCSWeapon()->IsPistol() && !IsPistolEmpty())
  1055. {
  1056. // switch to pistol instead of reloading
  1057. EquipPistol();
  1058. return;
  1059. }
  1060. }
  1061. }
  1062. else if (GetTimeSinceLastSawEnemy() > safeReloadWaitTime && GetActiveWeaponAmmoRatio() <= reloadAmmoRatio)
  1063. {
  1064. // high-skill players use all their ammo and switch to pistol instead of reloading during combat
  1065. if (GetProfile()->GetSkill() > 0.5f && IsAttacking())
  1066. return;
  1067. }
  1068. else
  1069. {
  1070. // do not need to reload
  1071. return;
  1072. }
  1073. // don't reload the AWP until it is totally out of ammo
  1074. if (IsUsing( WEAPON_AWP ) && !IsActiveWeaponClipEmpty())
  1075. return;
  1076. Reload();
  1077. // move to cover to reload if there are enemies nearby
  1078. if (GetNearbyEnemyCount())
  1079. {
  1080. // avoid enemies while reloading (above 0.75 skill always hide to reload)
  1081. const float hideChance = 25.0f + 100.0f * GetProfile()->GetSkill();
  1082. if (!IsHiding() && RandomFloat( 0, 100 ) < hideChance)
  1083. {
  1084. const float safeTime = 5.0f;
  1085. if (GetTimeSinceLastSawEnemy() < safeTime)
  1086. {
  1087. PrintIfWatched( "Retreating to a safe spot to reload!\n" );
  1088. const Vector *spot = FindNearbyRetreatSpot( this, 1000.0f );
  1089. if (spot)
  1090. {
  1091. // ignore enemies for a second to give us time to hide
  1092. // reaching our hiding spot clears our disposition
  1093. IgnoreEnemies( 10.0f );
  1094. Run();
  1095. StandUp();
  1096. Hide( *spot, 0.0f );
  1097. }
  1098. }
  1099. }
  1100. }
  1101. }
  1102. //--------------------------------------------------------------------------------------------------------------
  1103. /**
  1104. * Silence/unsilence our weapon if we must
  1105. */
  1106. void CCSBot::SilencerCheck( void )
  1107. {
  1108. const float safeSilencerWaitTime = 3.5f; // longer than reload check because reloading should take precedence
  1109. if (IsDefusingBomb() || IsReloading() || IsAttacking())
  1110. return;
  1111. if (!DoesActiveWeaponHaveRemoveableSilencer())
  1112. return;
  1113. if (GetTimeSinceLastSawEnemy() < safeSilencerWaitTime)
  1114. return;
  1115. // don't touch the silencer if there are enemies nearby
  1116. if (GetNearbyEnemyCount() == 0)
  1117. {
  1118. CWeaponCSBase *weapon = GetActiveCSWeapon();
  1119. if (weapon == NULL)
  1120. return;
  1121. bool isSilencerOn = weapon->IsSilenced();
  1122. if ( weapon->m_flNextSecondaryAttack >= gpGlobals->curtime )
  1123. return;
  1124. // equip silencer if we want to and we don't have a shield.
  1125. if ( isSilencerOn != (GetProfile()->PrefersSilencer() || GetProfile()->GetSkill() > 0.7f) && !HasShield() )
  1126. {
  1127. PrintIfWatched( "%s silencer!\n", (isSilencerOn) ? "Unequipping" : "Equipping" );
  1128. weapon->SecondaryAttack();
  1129. }
  1130. }
  1131. }
  1132. //--------------------------------------------------------------------------------------------------------------
  1133. /**
  1134. * Invoked when in contact with a CBaseCombatWeapon
  1135. */
  1136. bool CCSBot::BumpWeapon( CBaseCombatWeapon *pWeapon )
  1137. {
  1138. CWeaponCSBase *droppedGun = dynamic_cast< CWeaponCSBase* >( pWeapon );
  1139. // right now we only care about primary weapons on the ground
  1140. if ( droppedGun && droppedGun->GetSlot() == WEAPON_SLOT_RIFLE )
  1141. {
  1142. CWeaponCSBase *myGun = dynamic_cast< CWeaponCSBase* >( Weapon_GetSlot( WEAPON_SLOT_RIFLE ) );
  1143. // if the gun on the ground is the same one we have, dont bother
  1144. if ( myGun && droppedGun->GetCSWeaponID() != myGun->GetCSWeaponID() )
  1145. {
  1146. // if we don't have a weapon preference, give up
  1147. if ( GetProfile()->HasPrimaryPreference() )
  1148. {
  1149. // don't change weapons if we've seen enemies recently
  1150. const float safeTime = 2.5f;
  1151. if ( GetTimeSinceLastSawEnemy() >= safeTime )
  1152. {
  1153. // we have a primary weapon - drop it if the one on the ground is better
  1154. for( int i = 0; i < GetProfile()->GetWeaponPreferenceCount(); ++i )
  1155. {
  1156. CSWeaponID prefID = GetProfile()->GetWeaponPreference( i );
  1157. if (!IsPrimaryWeapon( prefID ))
  1158. continue;
  1159. // if the gun we are using is more desirable, give up
  1160. if ( prefID == myGun->GetCSWeaponID() )
  1161. break;
  1162. if ( prefID == droppedGun->GetCSWeaponID() )
  1163. {
  1164. // the gun on the ground is better than the one we have - drop our gun
  1165. DropWeaponSlot( WEAPON_SLOT_RIFLE );
  1166. break;
  1167. }
  1168. }
  1169. }
  1170. }
  1171. }
  1172. }
  1173. return BaseClass::BumpWeapon( droppedGun );
  1174. }
  1175. //--------------------------------------------------------------------------------------------------------------
  1176. /**
  1177. * Return true if a friend is in our weapon's way
  1178. * @todo Check more rays for safety.
  1179. */
  1180. bool CCSBot::IsFriendInLineOfFire( void )
  1181. {
  1182. // compute the unit vector along our view
  1183. Vector aimDir = GetViewVector();
  1184. // trace the bullet's path
  1185. trace_t result;
  1186. UTIL_TraceLine( EyePosition(), EyePosition() + 10000.0f * aimDir, MASK_PLAYERSOLID, this, COLLISION_GROUP_NONE, &result );
  1187. if (result.DidHitNonWorldEntity())
  1188. {
  1189. CBaseEntity *victim = result.m_pEnt;
  1190. if (victim && victim->IsPlayer() && victim->IsAlive())
  1191. {
  1192. CBasePlayer *player = static_cast<CBasePlayer *>( victim );
  1193. if ( !IsOtherEnemy( player->entindex() ) )
  1194. return true;
  1195. }
  1196. }
  1197. return false;
  1198. }
  1199. //--------------------------------------------------------------------------------------------------------------
  1200. /**
  1201. * Return line-of-sight distance to obstacle along weapon fire ray
  1202. * @todo Re-use this computation with IsFriendInLineOfFire()
  1203. */
  1204. float CCSBot::ComputeWeaponSightRange( void )
  1205. {
  1206. // compute the unit vector along our view
  1207. Vector aimDir = GetViewVector();
  1208. // trace the bullet's path
  1209. trace_t result;
  1210. UTIL_TraceLine( EyePosition(), EyePosition() + 10000.0f * aimDir, MASK_PLAYERSOLID, this, COLLISION_GROUP_NONE, &result );
  1211. return (EyePosition() - result.endpos).Length();
  1212. }
  1213. //--------------------------------------------------------------------------------------------------------------
  1214. /**
  1215. * Return true if the given player just fired their weapon
  1216. */
  1217. bool CCSBot::DidPlayerJustFireWeapon( const CCSPlayer *player ) const
  1218. {
  1219. // if this player has just fired his weapon, we notice him
  1220. CWeaponCSBase *weapon = player->GetActiveCSWeapon();
  1221. return (weapon && !weapon->IsSilenced() && weapon->m_flNextPrimaryAttack > gpGlobals->curtime);
  1222. }