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.

1117 lines
28 KiB

  1. //========= Copyright 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_simple_hostage.h"
  10. #include "cs_gamerules.h"
  11. #include "func_breakablesurf.h"
  12. #include "obstacle_pushaway.h"
  13. #include "cs_bot.h"
  14. // memdbgon must be the last include file in a .cpp file!!!
  15. #include "tier0/memdbgon.h"
  16. LINK_ENTITY_TO_CLASS( cs_bot, CCSBot );
  17. BEGIN_DATADESC( CCSBot )
  18. END_DATADESC()
  19. //--------------------------------------------------------------------------------------------------------------
  20. /**
  21. * Return the number of bots following the given player
  22. */
  23. int GetBotFollowCount( CCSPlayer *leader )
  24. {
  25. int count = 0;
  26. for( int i=1; i <= gpGlobals->maxClients; ++i )
  27. {
  28. CBaseEntity *entity = UTIL_PlayerByIndex( i );
  29. if (entity == NULL)
  30. continue;
  31. CBasePlayer *player = static_cast<CBasePlayer *>( entity );
  32. if (!player->IsBot())
  33. continue;
  34. if (!player->IsAlive())
  35. continue;
  36. CCSBot *bot = dynamic_cast<CCSBot *>( player );
  37. if (bot && bot->GetFollowLeader() == leader)
  38. ++count;
  39. }
  40. return count;
  41. }
  42. //--------------------------------------------------------------------------------------------------------------
  43. /**
  44. * Change movement speed to walking
  45. */
  46. void CCSBot::Walk( void )
  47. {
  48. if (m_mustRunTimer.IsElapsed())
  49. {
  50. BaseClass::Walk();
  51. }
  52. else
  53. {
  54. // must run
  55. Run();
  56. }
  57. }
  58. //--------------------------------------------------------------------------------------------------------------
  59. /**
  60. * Return true if jump was started.
  61. * This is extended from the base jump to disallow jumping when in a crouch area.
  62. */
  63. bool CCSBot::Jump( bool mustJump )
  64. {
  65. // prevent jumping if we're crouched, unless we're in a crouchjump area - jump wins
  66. bool inCrouchJumpArea = (m_lastKnownArea &&
  67. (m_lastKnownArea->GetAttributes() & NAV_MESH_CROUCH) &&
  68. (m_lastKnownArea->GetAttributes() & NAV_MESH_JUMP));
  69. if ( !IsUsingLadder() && IsDucked() && !inCrouchJumpArea )
  70. {
  71. return false;
  72. }
  73. return BaseClass::Jump( mustJump );
  74. }
  75. //--------------------------------------------------------------------------------------------------------------
  76. /**
  77. * Invoked when injured by something
  78. * NOTE: We dont want to directly call Attack() here, or the bots will have super-human reaction times when injured
  79. */
  80. int CCSBot::OnTakeDamage( const CTakeDamageInfo &info )
  81. {
  82. CBaseEntity *attacker = info.GetInflictor();
  83. // getting hurt makes us alert
  84. BecomeAlert();
  85. StopWaiting();
  86. // if we were attacked by a teammate, rebuke
  87. if (attacker->IsPlayer())
  88. {
  89. CCSPlayer *player = static_cast<CCSPlayer *>( attacker );
  90. if (InSameTeam( player ) && !player->IsBot())
  91. GetChatter()->FriendlyFire();
  92. }
  93. if (attacker->IsPlayer() && IsEnemy( attacker ))
  94. {
  95. // Track previous attacker so we don't try to panic multiple times for a shotgun blast
  96. CCSPlayer *lastAttacker = m_attacker;
  97. float lastAttackedTimestamp = m_attackedTimestamp;
  98. // keep track of our last attacker
  99. m_attacker = reinterpret_cast<CCSPlayer *>( attacker );
  100. m_attackedTimestamp = gpGlobals->curtime;
  101. // no longer safe
  102. AdjustSafeTime();
  103. if ( !IsSurprised() && (m_attacker != lastAttacker || m_attackedTimestamp != lastAttackedTimestamp) )
  104. {
  105. CCSPlayer *enemy = static_cast<CCSPlayer *>( attacker );
  106. // being hurt by an enemy we can't see causes panic
  107. if (!IsVisible( enemy, CHECK_FOV ))
  108. {
  109. // if not attacking anything, look around to try to find attacker
  110. if (!IsAttacking())
  111. {
  112. Panic();
  113. }
  114. else // we are attacking
  115. {
  116. if (!IsEnemyVisible())
  117. {
  118. // can't see our current enemy, panic to acquire new attacker
  119. Panic();
  120. }
  121. }
  122. }
  123. }
  124. }
  125. // extend
  126. return BaseClass::OnTakeDamage( info );
  127. }
  128. //--------------------------------------------------------------------------------------------------------------
  129. /**
  130. * Invoked when killed
  131. */
  132. void CCSBot::Event_Killed( const CTakeDamageInfo &info )
  133. {
  134. // PrintIfWatched( "Killed( attacker = %s )\n", STRING(pevAttacker->netname) );
  135. GetChatter()->OnDeath();
  136. // increase the danger where we died
  137. const float deathDanger = 1.0f;
  138. const float deathDangerRadius = 500.0f;
  139. TheNavMesh->IncreaseDangerNearby( GetTeamNumber(), deathDanger, m_lastKnownArea, GetAbsOrigin(), deathDangerRadius );
  140. // end voice feedback
  141. m_voiceEndTimestamp = 0.0f;
  142. // extend
  143. BaseClass::Event_Killed( info );
  144. }
  145. //--------------------------------------------------------------------------------------------------------------
  146. /**
  147. * Return true if line segment intersects rectagular volume
  148. */
  149. #define HI_X 0x01
  150. #define LO_X 0x02
  151. #define HI_Y 0x04
  152. #define LO_Y 0x08
  153. #define HI_Z 0x10
  154. #define LO_Z 0x20
  155. inline bool IsIntersectingBox( const Vector& start, const Vector& end, const Vector& boxMin, const Vector& boxMax )
  156. {
  157. unsigned char startFlags = 0;
  158. unsigned char endFlags = 0;
  159. // classify start point
  160. if (start.x < boxMin.x)
  161. startFlags |= LO_X;
  162. if (start.x > boxMax.x)
  163. startFlags |= HI_X;
  164. if (start.y < boxMin.y)
  165. startFlags |= LO_Y;
  166. if (start.y > boxMax.y)
  167. startFlags |= HI_Y;
  168. if (start.z < boxMin.z)
  169. startFlags |= LO_Z;
  170. if (start.z > boxMax.z)
  171. startFlags |= HI_Z;
  172. // classify end point
  173. if (end.x < boxMin.x)
  174. endFlags |= LO_X;
  175. if (end.x > boxMax.x)
  176. endFlags |= HI_X;
  177. if (end.y < boxMin.y)
  178. endFlags |= LO_Y;
  179. if (end.y > boxMax.y)
  180. endFlags |= HI_Y;
  181. if (end.z < boxMin.z)
  182. endFlags |= LO_Z;
  183. if (end.z > boxMax.z)
  184. endFlags |= HI_Z;
  185. // trivial reject
  186. if (startFlags & endFlags)
  187. return false;
  188. /// @todo Do exact line/box intersection check
  189. return true;
  190. }
  191. extern void UTIL_DrawBox( Extent *extent, int lifetime, int red, int green, int blue );
  192. //--------------------------------------------------------------------------------------------------------------
  193. /**
  194. * When bot is touched by another entity.
  195. */
  196. void CCSBot::Touch( CBaseEntity *other )
  197. {
  198. // EXTEND
  199. BaseClass::Touch( other );
  200. // if we have touched a higher-priority player, make way
  201. /// @todo Need to account for reaction time, etc.
  202. if (other->IsPlayer())
  203. {
  204. // if we are defusing a bomb, don't move
  205. if (IsDefusingBomb())
  206. return;
  207. // if we are on a ladder, don't move
  208. if (IsUsingLadder())
  209. return;
  210. CCSPlayer *player = static_cast<CCSPlayer *>( other );
  211. // get priority of other player
  212. unsigned int otherPri = TheCSBots()->GetPlayerPriority( player );
  213. // get our priority
  214. unsigned int myPri = TheCSBots()->GetPlayerPriority( this );
  215. // if our priority is better, don't budge
  216. if (myPri < otherPri)
  217. return;
  218. // they are higher priority - make way, unless we're already making way for someone more important
  219. if (m_avoid != NULL)
  220. {
  221. unsigned int avoidPri = TheCSBots()->GetPlayerPriority( static_cast<CBasePlayer *>( static_cast<CBaseEntity *>( m_avoid ) ) );
  222. if (avoidPri < otherPri)
  223. {
  224. // ignore 'other' because we're already avoiding someone better
  225. return;
  226. }
  227. }
  228. m_avoid = other;
  229. m_avoidTimestamp = gpGlobals->curtime;
  230. }
  231. // Check for breakables we're actually touching
  232. // If we're not stuck or crouched, we don't care
  233. if ( !m_isStuck && !IsCrouching() && !IsOnLadder() )
  234. return;
  235. // See if it's breakable
  236. if ( IsBreakableEntity( other ) )
  237. {
  238. // it's breakable - try to shoot it.
  239. SetLookAt( "Breakable", other->WorldSpaceCenter(), PRIORITY_HIGH, 0.1f, false, 5.0f, true );
  240. }
  241. }
  242. //--------------------------------------------------------------------------------------------------------------
  243. /**
  244. * Return true if we are busy doing something important
  245. */
  246. bool CCSBot::IsBusy( void ) const
  247. {
  248. if (IsAttacking() ||
  249. IsBuying() ||
  250. IsDefusingBomb() ||
  251. GetTask() == PLANT_BOMB ||
  252. GetTask() == RESCUE_HOSTAGES ||
  253. IsSniping())
  254. {
  255. return true;
  256. }
  257. return false;
  258. }
  259. //--------------------------------------------------------------------------------------------------------------
  260. void CCSBot::BotDeathThink( void )
  261. {
  262. }
  263. //--------------------------------------------------------------------------------------------------------------
  264. /**
  265. * Try to join the given team
  266. */
  267. void CCSBot::TryToJoinTeam( int team )
  268. {
  269. m_desiredTeam = team;
  270. }
  271. //--------------------------------------------------------------------------------------------------------------
  272. /**
  273. * Assign given player as our current enemy to attack
  274. */
  275. void CCSBot::SetBotEnemy( CCSPlayer *enemy )
  276. {
  277. if (m_enemy != enemy)
  278. {
  279. m_enemy = enemy;
  280. m_currentEnemyAcquireTimestamp = gpGlobals->curtime;
  281. PrintIfWatched( "SetBotEnemy: %s\n", (enemy) ? enemy->GetPlayerName() : "(NULL)" );
  282. }
  283. }
  284. //--------------------------------------------------------------------------------------------------------------
  285. /**
  286. * If we are not on the navigation mesh (m_currentArea == NULL),
  287. * move towards last known area.
  288. * Return false if off mesh.
  289. */
  290. bool CCSBot::StayOnNavMesh( void )
  291. {
  292. if (m_currentArea == NULL)
  293. {
  294. // move back onto the area map
  295. // if we have no lastKnownArea, we probably started off
  296. // of the nav mesh - find the closest nav area and use it
  297. CNavArea *goalArea;
  298. if (!m_currentArea && !m_lastKnownArea)
  299. {
  300. goalArea = TheNavMesh->GetNearestNavArea( GetCentroid( this ) );
  301. PrintIfWatched( "Started off the nav mesh - moving to closest nav area...\n" );
  302. }
  303. else
  304. {
  305. goalArea = m_lastKnownArea;
  306. PrintIfWatched( "Getting out of NULL area...\n" );
  307. }
  308. if (goalArea)
  309. {
  310. Vector pos;
  311. goalArea->GetClosestPointOnArea( GetCentroid( this ), &pos );
  312. // move point into area
  313. Vector to = pos - GetCentroid( this );
  314. to.NormalizeInPlace();
  315. const float stepInDist = 5.0f; // how far to "step into" an area - must be less than min area size
  316. pos = pos + (stepInDist * to);
  317. MoveTowardsPosition( pos );
  318. }
  319. // if we're stuck, try to get un-stuck
  320. // do stuck movements last, so they override normal movement
  321. if (m_isStuck)
  322. Wiggle();
  323. return false;
  324. }
  325. return true;
  326. }
  327. //--------------------------------------------------------------------------------------------------------------
  328. /**
  329. * Return true if we will do scenario-related tasks
  330. */
  331. bool CCSBot::IsDoingScenario( void ) const
  332. {
  333. // if we are deferring to humans, and there is a live human on our team, don't do the scenario
  334. if (cv_bot_defer_to_human.GetBool())
  335. {
  336. if (UTIL_HumansOnTeam( GetTeamNumber(), IS_ALIVE ))
  337. return false;
  338. }
  339. return true;
  340. }
  341. //--------------------------------------------------------------------------------------------------------------
  342. /**
  343. * Return true if we noticed the bomb on the ground or on the radar (for T's only)
  344. */
  345. bool CCSBot::NoticeLooseBomb( void ) const
  346. {
  347. CCSBotManager *ctrl = static_cast<CCSBotManager *>( TheCSBots() );
  348. if (ctrl->GetScenario() != CCSBotManager::SCENARIO_DEFUSE_BOMB)
  349. return false;
  350. CBaseEntity *bomb = ctrl->GetLooseBomb();
  351. if (bomb)
  352. {
  353. // T's can always see bomb on their radar
  354. return true;
  355. }
  356. return false;
  357. }
  358. //--------------------------------------------------------------------------------------------------------------
  359. /**
  360. * Return true if can see the bomb lying on the ground
  361. */
  362. bool CCSBot::CanSeeLooseBomb( void ) const
  363. {
  364. CCSBotManager *ctrl = static_cast<CCSBotManager *>( TheCSBots() );
  365. if (ctrl->GetScenario() != CCSBotManager::SCENARIO_DEFUSE_BOMB)
  366. return false;
  367. CBaseEntity *bomb = ctrl->GetLooseBomb();
  368. if (bomb)
  369. {
  370. if (IsVisible( bomb->GetAbsOrigin(), CHECK_FOV ))
  371. return true;
  372. }
  373. return false;
  374. }
  375. //--------------------------------------------------------------------------------------------------------------
  376. /**
  377. * Return true if can see the planted bomb
  378. */
  379. bool CCSBot::CanSeePlantedBomb( void ) const
  380. {
  381. CCSBotManager *ctrl = static_cast<CCSBotManager *>( TheCSBots() );
  382. if (ctrl->GetScenario() != CCSBotManager::SCENARIO_DEFUSE_BOMB)
  383. return false;
  384. if (!GetGameState()->IsBombPlanted())
  385. return false;
  386. const Vector *bombPos = GetGameState()->GetBombPosition();
  387. if (bombPos && IsVisible( *bombPos, CHECK_FOV ))
  388. return true;
  389. return false;
  390. }
  391. //--------------------------------------------------------------------------------------------------------------
  392. /**
  393. * Return last enemy that hurt us
  394. */
  395. CCSPlayer *CCSBot::GetAttacker( void ) const
  396. {
  397. if (m_attacker && m_attacker->IsAlive())
  398. return m_attacker;
  399. return NULL;
  400. }
  401. //--------------------------------------------------------------------------------------------------------------
  402. /**
  403. * Immediately jump off of our ladder, if we're on one
  404. */
  405. void CCSBot::GetOffLadder( void )
  406. {
  407. if (IsUsingLadder())
  408. {
  409. Jump( MUST_JUMP );
  410. DestroyPath();
  411. }
  412. }
  413. //--------------------------------------------------------------------------------------------------------------
  414. /**
  415. * Return time when given spot was last checked
  416. */
  417. float CCSBot::GetHidingSpotCheckTimestamp( HidingSpot *spot ) const
  418. {
  419. for( int i=0; i<m_checkedHidingSpotCount; ++i )
  420. if (m_checkedHidingSpot[i].spot->GetID() == spot->GetID())
  421. return m_checkedHidingSpot[i].timestamp;
  422. return -999999.9f;
  423. }
  424. //--------------------------------------------------------------------------------------------------------------
  425. /**
  426. * Set the timestamp of the given spot to now.
  427. * If the spot is not in the set, overwrite the least recently checked spot.
  428. */
  429. void CCSBot::SetHidingSpotCheckTimestamp( HidingSpot *spot )
  430. {
  431. int leastRecent = 0;
  432. float leastRecentTime = gpGlobals->curtime + 1.0f;
  433. for( int i=0; i<m_checkedHidingSpotCount; ++i )
  434. {
  435. // if spot is in the set, just update its timestamp
  436. if (m_checkedHidingSpot[i].spot->GetID() == spot->GetID())
  437. {
  438. m_checkedHidingSpot[i].timestamp = gpGlobals->curtime;
  439. return;
  440. }
  441. // keep track of least recent spot
  442. if (m_checkedHidingSpot[i].timestamp < leastRecentTime)
  443. {
  444. leastRecentTime = m_checkedHidingSpot[i].timestamp;
  445. leastRecent = i;
  446. }
  447. }
  448. // if there is room for more spots, append this one
  449. if (m_checkedHidingSpotCount < MAX_CHECKED_SPOTS)
  450. {
  451. m_checkedHidingSpot[ m_checkedHidingSpotCount ].spot = spot;
  452. m_checkedHidingSpot[ m_checkedHidingSpotCount ].timestamp = gpGlobals->curtime;
  453. ++m_checkedHidingSpotCount;
  454. }
  455. else
  456. {
  457. // replace the least recent spot
  458. m_checkedHidingSpot[ leastRecent ].spot = spot;
  459. m_checkedHidingSpot[ leastRecent ].timestamp = gpGlobals->curtime;
  460. }
  461. }
  462. //--------------------------------------------------------------------------------------------------------------
  463. /**
  464. * Periodic check of hostage count in case we lost some
  465. */
  466. void CCSBot::UpdateHostageEscortCount( void )
  467. {
  468. const float updateInterval = 1.0f;
  469. if (m_hostageEscortCount == 0 || gpGlobals->curtime - m_hostageEscortCountTimestamp < updateInterval)
  470. return;
  471. m_hostageEscortCountTimestamp = gpGlobals->curtime;
  472. // recount the hostages in case we lost some
  473. m_hostageEscortCount = 0;
  474. for( int i=0; i<g_Hostages.Count(); ++i )
  475. {
  476. CHostage *hostage = g_Hostages[i];
  477. // skip dead or rescued hostages
  478. if ( !hostage->IsValid() || !hostage->IsAlive() )
  479. continue;
  480. // check if hostage has targeted us, and is following
  481. if ( hostage->IsFollowing( this ) )
  482. ++m_hostageEscortCount;
  483. }
  484. }
  485. //--------------------------------------------------------------------------------------------------------------
  486. /**
  487. * Return true if we are outnumbered by enemies
  488. */
  489. bool CCSBot::IsOutnumbered( void ) const
  490. {
  491. return (GetNearbyFriendCount() < GetNearbyEnemyCount()-1) ? true : false;
  492. }
  493. //--------------------------------------------------------------------------------------------------------------
  494. /**
  495. * Return number of enemies we are outnumbered by
  496. */
  497. int CCSBot::OutnumberedCount( void ) const
  498. {
  499. if (IsOutnumbered())
  500. return (GetNearbyEnemyCount()-1) - GetNearbyFriendCount();
  501. return 0;
  502. }
  503. //--------------------------------------------------------------------------------------------------------------
  504. /**
  505. * Return the closest "important" enemy for the given scenario (bomb carrier, VIP, hostage escorter)
  506. */
  507. CCSPlayer *CCSBot::GetImportantEnemy( bool checkVisibility ) const
  508. {
  509. CCSBotManager *ctrl = static_cast<CCSBotManager *>( TheCSBots() );
  510. CCSPlayer *nearEnemy = NULL;
  511. float nearDist = 999999999.9f;
  512. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  513. {
  514. CBaseEntity *entity = UTIL_PlayerByIndex( i );
  515. if (entity == NULL)
  516. continue;
  517. // if (FNullEnt( entity->pev ))
  518. // continue;
  519. // if (FStrEq( STRING( entity->pev->netname ), "" ))
  520. // continue;
  521. // is it a player?
  522. if (!entity->IsPlayer())
  523. continue;
  524. CCSPlayer *player = static_cast<CCSPlayer *>( entity );
  525. // is it alive?
  526. if (!player->IsAlive())
  527. continue;
  528. // skip friends
  529. if (InSameTeam( player ))
  530. continue;
  531. // is it "important"
  532. if (!ctrl->IsImportantPlayer( player ))
  533. continue;
  534. // is it closest?
  535. Vector d = GetAbsOrigin() - player->GetAbsOrigin();
  536. float distSq = d.x*d.x + d.y*d.y + d.z*d.z;
  537. if (distSq < nearDist)
  538. {
  539. if (checkVisibility && !IsVisible( player, CHECK_FOV ))
  540. continue;
  541. nearEnemy = player;
  542. nearDist = distSq;
  543. }
  544. }
  545. return nearEnemy;
  546. }
  547. //--------------------------------------------------------------------------------------------------------------
  548. /**
  549. * Sets our current disposition
  550. */
  551. void CCSBot::SetDisposition( DispositionType disposition )
  552. {
  553. m_disposition = disposition;
  554. if (m_disposition != IGNORE_ENEMIES)
  555. m_ignoreEnemiesTimer.Invalidate();
  556. }
  557. //--------------------------------------------------------------------------------------------------------------
  558. /**
  559. * Return our current disposition
  560. */
  561. CCSBot::DispositionType CCSBot::GetDisposition( void ) const
  562. {
  563. if (!m_ignoreEnemiesTimer.IsElapsed())
  564. return IGNORE_ENEMIES;
  565. return m_disposition;
  566. }
  567. //--------------------------------------------------------------------------------------------------------------
  568. /**
  569. * Ignore enemies for a short durationy
  570. */
  571. void CCSBot::IgnoreEnemies( float duration )
  572. {
  573. m_ignoreEnemiesTimer.Start( duration );
  574. }
  575. //--------------------------------------------------------------------------------------------------------------
  576. /**
  577. * Increase morale one step
  578. */
  579. void CCSBot::IncreaseMorale( void )
  580. {
  581. if (m_morale < EXCELLENT)
  582. m_morale = static_cast<MoraleType>( m_morale + 1 );
  583. }
  584. //--------------------------------------------------------------------------------------------------------------
  585. /**
  586. * Decrease morale one step
  587. */
  588. void CCSBot::DecreaseMorale( void )
  589. {
  590. if (m_morale > TERRIBLE)
  591. m_morale = static_cast<MoraleType>( m_morale - 1 );
  592. }
  593. //--------------------------------------------------------------------------------------------------------------
  594. /**
  595. * Return true if we are acting like a rogue (not listening to teammates, not doing scenario goals)
  596. * @todo Account for morale
  597. */
  598. bool CCSBot::IsRogue( void ) const
  599. {
  600. CCSBotManager *ctrl = static_cast<CCSBotManager *>( TheCSBots() );
  601. if (!ctrl->AllowRogues())
  602. return false;
  603. // periodically re-evaluate our rogue status
  604. if (m_rogueTimer.IsElapsed())
  605. {
  606. m_rogueTimer.Start( RandomFloat( 10.0f, 30.0f ) );
  607. // our chance of going rogue is inversely proportional to our teamwork attribute
  608. const float rogueChance = 100.0f * (1.0f - GetProfile()->GetTeamwork());
  609. m_isRogue = (RandomFloat( 0, 100 ) < rogueChance);
  610. }
  611. return m_isRogue;
  612. }
  613. //--------------------------------------------------------------------------------------------------------------
  614. /**
  615. * Return true if we are in a hurry
  616. */
  617. bool CCSBot::IsHurrying( void ) const
  618. {
  619. if (!m_hurryTimer.IsElapsed())
  620. return true;
  621. CCSBotManager *ctrl = static_cast<CCSBotManager *>( TheCSBots() );
  622. // if the bomb has been planted, we are in a hurry, CT or T (they could be defusing it!)
  623. if (ctrl->GetScenario() == CCSBotManager::SCENARIO_DEFUSE_BOMB && ctrl->IsBombPlanted())
  624. return true;
  625. // if we are a T and hostages are being rescued, we are in a hurry
  626. if (ctrl->GetScenario() == CCSBotManager::SCENARIO_RESCUE_HOSTAGES &&
  627. GetTeamNumber() == TEAM_TERRORIST &&
  628. GetGameState()->AreAllHostagesBeingRescued())
  629. return true;
  630. return false;
  631. }
  632. //--------------------------------------------------------------------------------------------------------------
  633. /**
  634. * Return true if it is the early, "safe", part of the round
  635. */
  636. bool CCSBot::IsSafe( void ) const
  637. {
  638. CCSBotManager *ctrl = static_cast<CCSBotManager *>( TheCSBots() );
  639. if (ctrl->GetElapsedRoundTime() < m_safeTime)
  640. return true;
  641. return false;
  642. }
  643. //--------------------------------------------------------------------------------------------------------------
  644. /**
  645. * Return true if it is well past the early, "safe", part of the round
  646. */
  647. bool CCSBot::IsWellPastSafe( void ) const
  648. {
  649. CCSBotManager *ctrl = static_cast<CCSBotManager *>( TheCSBots() );
  650. if (ctrl->GetElapsedRoundTime() > 2.0f * m_safeTime)
  651. return true;
  652. return false;
  653. }
  654. //--------------------------------------------------------------------------------------------------------------
  655. /**
  656. * Return true if we were in the safe time last update, but not now
  657. */
  658. bool CCSBot::IsEndOfSafeTime( void ) const
  659. {
  660. return m_wasSafe && !IsSafe();
  661. }
  662. //--------------------------------------------------------------------------------------------------------------
  663. /**
  664. * Return the amount of "safe time" we have left
  665. */
  666. float CCSBot::GetSafeTimeRemaining( void ) const
  667. {
  668. CCSBotManager *ctrl = static_cast<CCSBotManager *>( TheCSBots() );
  669. return m_safeTime - ctrl->GetElapsedRoundTime();
  670. }
  671. //--------------------------------------------------------------------------------------------------------------
  672. /**
  673. * Called when enemy seen to adjust safe time for this round
  674. */
  675. void CCSBot::AdjustSafeTime( void )
  676. {
  677. CCSBotManager *ctrl = static_cast<CCSBotManager *>( TheCSBots() );
  678. // if we spotted an enemy sooner than we thought possible, adjust our notion of "safe" time
  679. if (ctrl->GetElapsedRoundTime() < m_safeTime)
  680. {
  681. // since right now is not safe, adjust safe time to be a few seconds ago
  682. m_safeTime = ctrl->GetElapsedRoundTime() - 2.0f;
  683. }
  684. }
  685. //--------------------------------------------------------------------------------------------------------------
  686. /**
  687. * Return true if we haven't seen an enemy for "a long time"
  688. */
  689. bool CCSBot::HasNotSeenEnemyForLongTime( void ) const
  690. {
  691. const float longTime = 30.0f;
  692. return (GetTimeSinceLastSawEnemy() > longTime);
  693. }
  694. //--------------------------------------------------------------------------------------------------------------
  695. /**
  696. * Pick a random zone and hide near it
  697. */
  698. bool CCSBot::GuardRandomZone( float range )
  699. {
  700. CCSBotManager *ctrl = static_cast<CCSBotManager *>( TheCSBots() );
  701. const CCSBotManager::Zone *zone = ctrl->GetRandomZone();
  702. if (zone)
  703. {
  704. CNavArea *rescueArea = ctrl->GetRandomAreaInZone( zone );
  705. if (rescueArea)
  706. {
  707. Hide( rescueArea, -1.0f, range );
  708. return true;
  709. }
  710. }
  711. return false;
  712. }
  713. //--------------------------------------------------------------------------------------------------------------
  714. class CollectRetreatSpotsFunctor
  715. {
  716. public:
  717. CollectRetreatSpotsFunctor( CCSBot *me, float range )
  718. {
  719. m_me = me;
  720. m_count = 0;
  721. m_range = range;
  722. }
  723. enum { MAX_SPOTS = 256 };
  724. bool operator() ( CNavArea *area )
  725. {
  726. // collect all the hiding spots in this area
  727. const HidingSpotVector *pSpots = area->GetHidingSpots();
  728. FOR_EACH_VEC( (*pSpots), it )
  729. {
  730. const HidingSpot *spot = (*pSpots)[ it ];
  731. if (m_count >= MAX_SPOTS)
  732. break;
  733. // make sure hiding spot is in range
  734. if (m_range > 0.0f)
  735. if ((spot->GetPosition() - GetCentroid( m_me )).IsLengthGreaterThan( m_range ))
  736. continue;
  737. // if a Player is using this hiding spot, don't consider it
  738. if (IsSpotOccupied( m_me, spot->GetPosition() ))
  739. {
  740. // player is in hiding spot
  741. /// @todo Check if player is moving or sitting still
  742. continue;
  743. }
  744. // don't select spot if an enemy can see it
  745. if (UTIL_IsVisibleToTeam( spot->GetPosition() + Vector( 0, 0, HalfHumanHeight ), OtherTeam( m_me->GetTeamNumber() ) ))
  746. continue;
  747. // don't select spot if it is closest to an enemy
  748. CBasePlayer *owner = UTIL_GetClosestPlayer( spot->GetPosition() );
  749. if (owner && !m_me->InSameTeam( owner ))
  750. continue;
  751. m_spot[ m_count++ ] = &spot->GetPosition();
  752. }
  753. // if we've filled up, stop searching
  754. if (m_count == MAX_SPOTS)
  755. return false;
  756. return true;
  757. }
  758. CCSBot *m_me;
  759. float m_range;
  760. const Vector *m_spot[ MAX_SPOTS ];
  761. int m_count;
  762. };
  763. /**
  764. * Do a breadth-first search to find a good retreat spot.
  765. * Don't pick a spot that a Player is currently occupying.
  766. */
  767. const Vector *FindNearbyRetreatSpot( CCSBot *me, float maxRange )
  768. {
  769. CNavArea *area = me->GetLastKnownArea();
  770. if (area == NULL)
  771. return NULL;
  772. // collect spots that enemies cannot see
  773. CollectRetreatSpotsFunctor collector( me, maxRange );
  774. SearchSurroundingAreas( area, GetCentroid( me ), collector, maxRange );
  775. if (collector.m_count == 0)
  776. return NULL;
  777. // select a hiding spot at random
  778. int which = RandomInt( 0, collector.m_count-1 );
  779. return collector.m_spot[ which ];
  780. }
  781. //--------------------------------------------------------------------------------------------------------------
  782. class FarthestHostage
  783. {
  784. public:
  785. FarthestHostage( const CCSBot *me )
  786. {
  787. m_me = me;
  788. m_farRange = -1.0f;
  789. }
  790. bool operator() ( CHostage *hostage )
  791. {
  792. if (hostage->IsFollowing( m_me ))
  793. {
  794. float range = (hostage->GetAbsOrigin() - m_me->GetAbsOrigin()).Length();
  795. if (range > m_farRange)
  796. {
  797. m_farRange = range;
  798. }
  799. }
  800. return true;
  801. }
  802. const CCSBot *m_me;
  803. float m_farRange;
  804. };
  805. /**
  806. * Return euclidean distance to farthest escorted hostage.
  807. * Return -1 if no hostage is following us.
  808. */
  809. float CCSBot::GetRangeToFarthestEscortedHostage( void ) const
  810. {
  811. FarthestHostage away( this );
  812. ForEachHostage( away );
  813. return away.m_farRange;
  814. }
  815. //--------------------------------------------------------------------------------------------------------------
  816. /**
  817. * Return string describing current task
  818. * NOTE: This MUST be kept in sync with the CCSBot::TaskType enum
  819. */
  820. const char *CCSBot::GetTaskName( void ) const
  821. {
  822. static const char *name[ NUM_TASKS ] =
  823. {
  824. "SEEK_AND_DESTROY",
  825. "PLANT_BOMB",
  826. "FIND_TICKING_BOMB",
  827. "DEFUSE_BOMB",
  828. "GUARD_TICKING_BOMB",
  829. "GUARD_BOMB_DEFUSER",
  830. "GUARD_LOOSE_BOMB",
  831. "GUARD_BOMB_ZONE",
  832. "GUARD_INITIAL_ENCOUNTER",
  833. "ESCAPE_FROM_BOMB",
  834. "HOLD_POSITION",
  835. "FOLLOW",
  836. "VIP_ESCAPE",
  837. "GUARD_VIP_ESCAPE_ZONE",
  838. "COLLECT_HOSTAGES",
  839. "RESCUE_HOSTAGES",
  840. "GUARD_HOSTAGES",
  841. "GUARD_HOSTAGE_RESCUE_ZONE",
  842. "MOVE_TO_LAST_KNOWN_ENEMY_POSITION",
  843. "MOVE_TO_SNIPER_SPOT",
  844. "SNIPING",
  845. };
  846. return name[ (int)GetTask() ];
  847. }
  848. //--------------------------------------------------------------------------------------------------------------
  849. /**
  850. * Return string describing current disposition
  851. * NOTE: This MUST be kept in sync with the CCSBot::DispositionType enum
  852. */
  853. const char *CCSBot::GetDispositionName( void ) const
  854. {
  855. static const char *name[ NUM_DISPOSITIONS ] =
  856. {
  857. "ENGAGE_AND_INVESTIGATE",
  858. "OPPORTUNITY_FIRE",
  859. "SELF_DEFENSE",
  860. "IGNORE_ENEMIES"
  861. };
  862. return name[ (int)GetDisposition() ];
  863. }
  864. //--------------------------------------------------------------------------------------------------------------
  865. /**
  866. * Return string describing current morale
  867. * NOTE: This MUST be kept in sync with the CCSBot::MoraleType enum
  868. */
  869. const char *CCSBot::GetMoraleName( void ) const
  870. {
  871. static const char *name[ EXCELLENT - TERRIBLE + 1 ] =
  872. {
  873. "TERRIBLE",
  874. "BAD",
  875. "NEGATIVE",
  876. "NEUTRAL",
  877. "POSITIVE",
  878. "GOOD",
  879. "EXCELLENT"
  880. };
  881. return name[ (int)GetMorale() + 3 ];
  882. }
  883. //--------------------------------------------------------------------------------------------------------------
  884. /**
  885. * Fill in a CUserCmd with our data
  886. */
  887. void CCSBot::BuildUserCmd( CUserCmd& cmd, const QAngle& viewangles, float forwardmove, float sidemove, float upmove, int buttons, byte impulse )
  888. {
  889. Q_memset( &cmd, 0, sizeof( cmd ) );
  890. if ( !RunMimicCommand( cmd ) )
  891. {
  892. // Don't walk when ducked - it's painfully slow
  893. if ( m_Local.m_bDucked || m_Local.m_bDucking )
  894. {
  895. buttons &= ~IN_SPEED;
  896. }
  897. cmd.command_number = gpGlobals->tickcount;
  898. cmd.forwardmove = forwardmove;
  899. cmd.sidemove = sidemove;
  900. cmd.upmove = upmove;
  901. cmd.buttons = buttons;
  902. cmd.impulse = impulse;
  903. VectorCopy( viewangles, cmd.viewangles );
  904. cmd.random_seed = random->RandomInt( 0, 0x7fffffff );
  905. }
  906. }
  907. //--------------------------------------------------------------------------------------------------------------