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.

732 lines
18 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 "cs_nav_path.h"
  11. // memdbgon must be the last include file in a .cpp file!!!
  12. #include "tier0/memdbgon.h"
  13. //--------------------------------------------------------------------------------------------------------------
  14. /**
  15. * This method is the ONLY legal way to change a bot's current state
  16. */
  17. void CCSBot::SetState( BotState *state )
  18. {
  19. PrintIfWatched( "%s: SetState: %s -> %s\n", GetPlayerName(), (m_state) ? m_state->GetName() : "NULL", state->GetName() );
  20. /*
  21. if ( IsDefusingBomb() )
  22. {
  23. const Vector *bombPos = GetGameState()->GetBombPosition();
  24. if ( bombPos != NULL )
  25. {
  26. if ( TheCSBots()->GetBombDefuser() == this )
  27. {
  28. if ( TheCSBots()->IsBombPlanted() )
  29. {
  30. Msg( "Bot %s is switching from defusing the bomb to %s\n",
  31. GetPlayerName(), state->GetName() );
  32. }
  33. }
  34. }
  35. }
  36. */
  37. // if we changed state from within the special Attack state, we are no longer attacking
  38. if (m_isAttacking)
  39. StopAttacking();
  40. if (m_state)
  41. m_state->OnExit( this );
  42. state->OnEnter( this );
  43. m_state = state;
  44. m_stateTimestamp = gpGlobals->curtime;
  45. }
  46. //--------------------------------------------------------------------------------------------------------------
  47. void CCSBot::Idle( void )
  48. {
  49. SetTask( SEEK_AND_DESTROY );
  50. SetState( &m_idleState );
  51. }
  52. //--------------------------------------------------------------------------------------------------------------
  53. void CCSBot::EscapeFromBomb( void )
  54. {
  55. SetTask( ESCAPE_FROM_BOMB );
  56. SetState( &m_escapeFromBombState );
  57. }
  58. //--------------------------------------------------------------------------------------------------------------
  59. void CCSBot::EscapeFromFlames( void )
  60. {
  61. SetTask( ESCAPE_FROM_FLAMES );
  62. SetState( &m_escapeFromFlamesState );
  63. }
  64. //--------------------------------------------------------------------------------------------------------------
  65. void CCSBot::Follow( CCSPlayer *player )
  66. {
  67. if (player == NULL)
  68. return;
  69. // note when we began following
  70. if (!m_isFollowing || m_leader != player)
  71. m_followTimestamp = gpGlobals->curtime;
  72. m_isFollowing = true;
  73. m_leader = player;
  74. SetTask( FOLLOW );
  75. m_followState.SetLeader( player );
  76. SetState( &m_followState );
  77. }
  78. //--------------------------------------------------------------------------------------------------------------
  79. /**
  80. * Continue following our leader after finishing what we were doing
  81. */
  82. void CCSBot::ContinueFollowing( void )
  83. {
  84. SetTask( FOLLOW );
  85. m_followState.SetLeader( m_leader );
  86. SetState( &m_followState );
  87. }
  88. //--------------------------------------------------------------------------------------------------------------
  89. /**
  90. * Stop following
  91. */
  92. void CCSBot::StopFollowing( void )
  93. {
  94. m_isFollowing = false;
  95. m_leader = NULL;
  96. m_allowAutoFollowTime = gpGlobals->curtime + 10.0f;
  97. }
  98. //--------------------------------------------------------------------------------------------------------------
  99. /**
  100. * Begin process of rescuing hostages
  101. */
  102. void CCSBot::RescueHostages( void )
  103. {
  104. SetTask( RESCUE_HOSTAGES );
  105. }
  106. //--------------------------------------------------------------------------------------------------------------
  107. /**
  108. * Use the entity
  109. */
  110. void CCSBot::UseEntity( CBaseEntity *entity )
  111. {
  112. m_useEntityState.SetEntity( entity );
  113. SetState( &m_useEntityState );
  114. }
  115. //--------------------------------------------------------------------------------------------------------------
  116. /**
  117. * Open the door.
  118. * This assumes the bot is directly in front of the door with no obstructions.
  119. * NOTE: This state is special, like Attack, in that it suspends the current behavior and returns to it when done.
  120. */
  121. void CCSBot::OpenDoor( CBaseEntity *door )
  122. {
  123. m_openDoorState.SetDoor( door );
  124. m_isOpeningDoor = true;
  125. m_openDoorState.OnEnter( this );
  126. }
  127. //--------------------------------------------------------------------------------------------------------------
  128. /**
  129. * DEPRECATED: Use TryToHide() instead.
  130. * Move to a hiding place.
  131. * If 'searchFromArea' is non-NULL, hiding spots are looked for from that area first.
  132. */
  133. void CCSBot::Hide( CNavArea *searchFromArea, float duration, float hideRange, bool holdPosition )
  134. {
  135. if ( !TheCSBots()->AllowedToDoExpensiveBotOperationThisFrame() )
  136. return;
  137. DestroyPath();
  138. CNavArea *source;
  139. Vector sourcePos;
  140. if (searchFromArea)
  141. {
  142. source = searchFromArea;
  143. sourcePos = searchFromArea->GetCenter();
  144. }
  145. else
  146. {
  147. source = m_lastKnownArea;
  148. sourcePos = GetCentroid( this );
  149. }
  150. if (source == NULL)
  151. {
  152. PrintIfWatched( "Hide from area is NULL.\n" );
  153. Idle();
  154. return;
  155. }
  156. m_hideState.SetSearchArea( source );
  157. m_hideState.SetSearchRange( hideRange );
  158. m_hideState.SetDuration( duration );
  159. m_hideState.SetHoldPosition( holdPosition );
  160. // search around source area for a good hiding spot
  161. Vector useSpot;
  162. const Vector *pos = FindNearbyHidingSpot( this, sourcePos, hideRange, IsSniper() );
  163. if (pos == NULL)
  164. {
  165. PrintIfWatched( "No available hiding spots.\n" );
  166. // hide at our current position
  167. useSpot = GetCentroid( this );
  168. }
  169. else
  170. {
  171. useSpot = *pos;
  172. }
  173. m_hideState.SetHidingSpot( useSpot );
  174. // build a path to our new hiding spot
  175. if (ComputePath( useSpot, FASTEST_ROUTE ) == false)
  176. {
  177. PrintIfWatched( "Can't pathfind to hiding spot\n" );
  178. Idle();
  179. return;
  180. }
  181. SetState( &m_hideState );
  182. }
  183. //--------------------------------------------------------------------------------------------------------------
  184. /**
  185. * Move to the given hiding place
  186. */
  187. void CCSBot::Hide( const Vector &hidingSpot, float duration, bool holdPosition )
  188. {
  189. CNavArea *hideArea = TheNavMesh->GetNearestNavArea( hidingSpot );
  190. if (hideArea == NULL)
  191. {
  192. PrintIfWatched( "Hiding spot off nav mesh\n" );
  193. Idle();
  194. return;
  195. }
  196. DestroyPath();
  197. m_hideState.SetSearchArea( hideArea );
  198. m_hideState.SetSearchRange( 750.0f );
  199. m_hideState.SetDuration( duration );
  200. m_hideState.SetHoldPosition( holdPosition );
  201. m_hideState.SetHidingSpot( hidingSpot );
  202. // build a path to our new hiding spot
  203. if (ComputePath( hidingSpot, FASTEST_ROUTE ) == false)
  204. {
  205. PrintIfWatched( "Can't pathfind to hiding spot\n" );
  206. Idle();
  207. return;
  208. }
  209. SetState( &m_hideState );
  210. }
  211. //--------------------------------------------------------------------------------------------------------------
  212. /**
  213. * Try to hide nearby. Return true if hiding, false if can't hide here.
  214. * If 'searchFromArea' is non-NULL, hiding spots are looked for from that area first.
  215. */
  216. bool CCSBot::TryToHide( CNavArea *searchFromArea, float duration, float hideRange, bool holdPosition, bool useNearest, const Vector *pStartPosOverride /*= NULL*/ )
  217. {
  218. CNavArea *source;
  219. Vector sourcePos;
  220. if (searchFromArea)
  221. {
  222. source = searchFromArea;
  223. sourcePos = searchFromArea->GetCenter();
  224. }
  225. else
  226. {
  227. source = m_lastKnownArea;
  228. sourcePos = GetCentroid( this );
  229. }
  230. // Optionally force the starting position instead of using the center of an area...
  231. if ( pStartPosOverride )
  232. sourcePos = *pStartPosOverride;
  233. if (source == NULL)
  234. {
  235. PrintIfWatched( "Hide from area is NULL.\n" );
  236. return false;
  237. }
  238. m_hideState.SetSearchArea( source );
  239. m_hideState.SetSearchRange( hideRange );
  240. m_hideState.SetDuration( duration );
  241. m_hideState.SetHoldPosition( holdPosition );
  242. // search around source area for a good hiding spot
  243. const Vector *pos = FindNearbyHidingSpot( this, sourcePos, hideRange, IsSniper(), useNearest );
  244. if (pos == NULL)
  245. {
  246. PrintIfWatched( "No available hiding spots.\n" );
  247. return false;
  248. }
  249. m_hideState.SetHidingSpot( *pos );
  250. // build a path to our new hiding spot
  251. if (ComputePath( *pos, FASTEST_ROUTE ) == false)
  252. {
  253. PrintIfWatched( "Can't pathfind to hiding spot\n" );
  254. return false;
  255. }
  256. SetState( &m_hideState );
  257. return true;
  258. }
  259. //--------------------------------------------------------------------------------------------------------------
  260. /**
  261. * Retreat to a nearby hiding spot, away from enemies
  262. */
  263. bool CCSBot::TryToRetreat( float maxRange, float duration )
  264. {
  265. // We don't want heavies to retreat
  266. if ( CSGameRules() && CSGameRules()->IsPlayingCooperativeGametype() && HasHeavyArmor() )
  267. return false;
  268. const Vector *spot = FindNearbyRetreatSpot( this, maxRange );
  269. if (spot)
  270. {
  271. // ignore enemies for a second to give us time to hide
  272. // reaching our hiding spot clears our disposition
  273. IgnoreEnemies( 10.0f );
  274. if (duration < 0.0f)
  275. {
  276. duration = RandomFloat( 3.0f, 15.0f );
  277. }
  278. StandUp();
  279. Run();
  280. Hide( *spot, duration );
  281. PrintIfWatched( "Retreating to a safe spot!\n" );
  282. return true;
  283. }
  284. return false;
  285. }
  286. //--------------------------------------------------------------------------------------------------------------
  287. void CCSBot::Hunt( void )
  288. {
  289. SetState( &m_huntState );
  290. }
  291. //--------------------------------------------------------------------------------------------------------------
  292. /**
  293. * Attack our the given victim
  294. * NOTE: Attacking does not change our task.
  295. */
  296. void CCSBot::Attack( CCSPlayer *victim )
  297. {
  298. if (victim == NULL)
  299. return;
  300. // zombies never attack
  301. if (cv_bot_zombie.GetBool())
  302. return;
  303. // cannot attack if we are reloading
  304. if (IsReloading())
  305. return;
  306. // change enemy
  307. SetBotEnemy( victim );
  308. //
  309. // Do not "re-enter" the attack state if we are already attacking
  310. //
  311. if (IsAttacking())
  312. return;
  313. if (IsUsingGrenade())
  314. {
  315. // throw towards their feet
  316. ThrowGrenade( victim->GetAbsOrigin() );
  317. return;
  318. }
  319. // if we are currently hiding, increase our chances of crouching and holding position
  320. if (IsAtHidingSpot())
  321. m_attackState.SetCrouchAndHold( (RandomFloat( 0.0f, 100.0f ) < 60.0f) ? true : false );
  322. else
  323. m_attackState.SetCrouchAndHold( false );
  324. //SetState( &m_attackState );
  325. //PrintIfWatched( "ATTACK BEGIN (reaction time = %g (+ update time), surprise time = %g, attack delay = %g)\n",
  326. // GetProfile()->GetReactionTime(), m_surpriseDelay, GetProfile()->GetAttackDelay() );
  327. m_isAttacking = true;
  328. m_attackState.OnEnter( this );
  329. Vector victimOrigin = GetCentroid( victim );
  330. // cheat a bit and give the bot the initial location of its victim
  331. m_lastEnemyPosition = victimOrigin;
  332. m_lastSawEnemyTimestamp = gpGlobals->curtime;
  333. m_aimFocus = GetProfile()->GetAimFocusInitial();
  334. PickNewAimSpot();
  335. // forget any look at targets we have
  336. ClearLookAt();
  337. }
  338. //--------------------------------------------------------------------------------------------------------------
  339. /**
  340. * Exit the Attack state
  341. */
  342. void CCSBot::StopAttacking( void )
  343. {
  344. PrintIfWatched( "ATTACK END\n" );
  345. m_attackState.OnExit( this );
  346. m_isAttacking = false;
  347. // if we are following someone, go to the Idle state after the attack to decide whether we still want to follow
  348. if (IsFollowing())
  349. {
  350. Idle();
  351. }
  352. }
  353. //--------------------------------------------------------------------------------------------------------------
  354. bool CCSBot::IsAttacking( void ) const
  355. {
  356. return m_isAttacking;
  357. }
  358. //--------------------------------------------------------------------------------------------------------------
  359. /**
  360. * Return true if we are escaping from the bomb
  361. */
  362. bool CCSBot::IsEscapingFromBomb( void ) const
  363. {
  364. if (m_state == static_cast<const BotState *>( &m_escapeFromBombState ))
  365. return true;
  366. return false;
  367. }
  368. //--------------------------------------------------------------------------------------------------------------
  369. /**
  370. * Return true if we are escaping from a field of flames
  371. */
  372. bool CCSBot::IsEscapingFromFlames( void ) const
  373. {
  374. if ( m_state == static_cast< const BotState * >( &m_escapeFromFlamesState ) )
  375. return true;
  376. return false;
  377. }
  378. //--------------------------------------------------------------------------------------------------------------
  379. /**
  380. * Return true if we are defusing the bomb
  381. */
  382. bool CCSBot::IsDefusingBomb( void ) const
  383. {
  384. if (m_state == static_cast<const BotState *>( &m_defuseBombState ))
  385. return true;
  386. return false;
  387. }
  388. //--------------------------------------------------------------------------------------------------------------
  389. /**
  390. * Return true if we are defusing the bomb
  391. */
  392. bool CCSBot::IsPickingupHostage( void ) const
  393. {
  394. if (m_state == static_cast<const BotState *>( &m_pickupHostageState ))
  395. return true;
  396. return false;
  397. }
  398. //--------------------------------------------------------------------------------------------------------------
  399. /**
  400. * Return true if we are hiding
  401. */
  402. bool CCSBot::IsHiding( void ) const
  403. {
  404. if (m_state == static_cast<const BotState *>( &m_hideState ))
  405. return true;
  406. return false;
  407. }
  408. //--------------------------------------------------------------------------------------------------------------
  409. /**
  410. * Return true if we are hiding and at our hiding spot
  411. */
  412. bool CCSBot::IsAtHidingSpot( void ) const
  413. {
  414. if (!IsHiding())
  415. return false;
  416. return m_hideState.IsAtSpot();
  417. }
  418. //--------------------------------------------------------------------------------------------------------------
  419. /**
  420. * Return number of seconds we have been at our current hiding spot
  421. */
  422. float CCSBot::GetHidingTime( void ) const
  423. {
  424. if (IsHiding())
  425. {
  426. return m_hideState.GetHideTime();
  427. }
  428. return 0.0f;
  429. }
  430. //--------------------------------------------------------------------------------------------------------------
  431. /**
  432. * Return true if we are huting
  433. */
  434. bool CCSBot::IsHunting( void ) const
  435. {
  436. if (m_state == static_cast<const BotState *>( &m_huntState ))
  437. return true;
  438. return false;
  439. }
  440. //--------------------------------------------------------------------------------------------------------------
  441. /**
  442. * Return true if we are in the MoveTo state
  443. */
  444. bool CCSBot::IsMovingTo( void ) const
  445. {
  446. if (m_state == static_cast<const BotState *>( &m_moveToState ))
  447. return true;
  448. return false;
  449. }
  450. //--------------------------------------------------------------------------------------------------------------
  451. /**
  452. * Return true if we are buying
  453. */
  454. bool CCSBot::IsBuying( void ) const
  455. {
  456. if (m_state == static_cast<const BotState *>( &m_buyState ))
  457. return true;
  458. return false;
  459. }
  460. //--------------------------------------------------------------------------------------------------------------
  461. bool CCSBot::IsInvestigatingNoise( void ) const
  462. {
  463. if (m_state == static_cast<const BotState *>( &m_investigateNoiseState ))
  464. return true;
  465. return false;
  466. }
  467. //--------------------------------------------------------------------------------------------------------------
  468. bool CCSBot::IsIdling( void ) const
  469. {
  470. if ( m_state == static_cast< const BotState * >( &m_idleState ) )
  471. return true;
  472. return false;
  473. }
  474. //--------------------------------------------------------------------------------------------------------------
  475. /**
  476. * Move to potentially distant position
  477. */
  478. void CCSBot::MoveTo( const Vector &pos, RouteType route )
  479. {
  480. m_moveToState.SetGoalPosition( pos );
  481. m_moveToState.SetRouteType( route );
  482. SetState( &m_moveToState );
  483. }
  484. //--------------------------------------------------------------------------------------------------------------
  485. void CCSBot::PlantBomb( void )
  486. {
  487. SetState( &m_plantBombState );
  488. }
  489. //--------------------------------------------------------------------------------------------------------------
  490. /**
  491. * Bomb has been dropped - go get it
  492. */
  493. void CCSBot::FetchBomb( void )
  494. {
  495. SetState( &m_fetchBombState );
  496. }
  497. //--------------------------------------------------------------------------------------------------------------
  498. void CCSBot::DefuseBomb( void )
  499. {
  500. SetState( &m_defuseBombState );
  501. }
  502. void CCSBot::PickupHostage( CBaseEntity *entity )
  503. {
  504. m_pickupHostageState.SetEntity( entity );
  505. SetState( &m_pickupHostageState );
  506. }
  507. //--------------------------------------------------------------------------------------------------------------
  508. /**
  509. * Investigate recent enemy noise
  510. */
  511. void CCSBot::InvestigateNoise( void )
  512. {
  513. SetState( &m_investigateNoiseState );
  514. }
  515. //--------------------------------------------------------------------------------------------------------------
  516. void CCSBot::Buy( void )
  517. {
  518. SetState( &m_buyState );
  519. }
  520. //--------------------------------------------------------------------------------------------------------------
  521. /**
  522. * Move to a hiding spot and wait for initial encounter with enemy team.
  523. * Return false if can't do this behavior (ie: no hiding spots available).
  524. */
  525. bool CCSBot::MoveToInitialEncounter( void )
  526. {
  527. int myTeam = GetTeamNumber();
  528. int enemyTeam = OtherTeam( myTeam );
  529. // build a path to an enemy spawn point
  530. CBaseEntity *enemySpawn = TheCSBots()->GetRandomSpawn( enemyTeam );
  531. if (enemySpawn == NULL)
  532. {
  533. PrintIfWatched( "MoveToInitialEncounter: No enemy spawn points?\n" );
  534. return false;
  535. }
  536. if ( !TheCSBots()->AllowedToDoExpensiveBotOperationThisFrame() )
  537. return false;
  538. TheCSBots()->OnExpensiveBotOperation();
  539. // build a path from us to the enemy spawn
  540. CCSNavPath path;
  541. PathCost cost( this, FASTEST_ROUTE );
  542. path.Compute( WorldSpaceCenter(), enemySpawn->GetAbsOrigin(), cost );
  543. if (!path.IsValid())
  544. {
  545. PrintIfWatched( "MoveToInitialEncounter: Pathfind failed.\n" );
  546. return false;
  547. }
  548. // find battlefront area where teams will first meet along this path
  549. int i;
  550. for( i=0; i<path.GetSegmentCount(); ++i )
  551. {
  552. if (path[i]->area->GetEarliestOccupyTime( myTeam ) > path[i]->area->GetEarliestOccupyTime( enemyTeam ))
  553. {
  554. break;
  555. }
  556. }
  557. if (i == path.GetSegmentCount())
  558. {
  559. PrintIfWatched( "MoveToInitialEncounter: Can't find battlefront!\n" );
  560. return false;
  561. }
  562. /// @todo Remove this evil side-effect
  563. SetInitialEncounterArea( path[i]->area );
  564. // find a hiding spot on our side of the battlefront that has LOS to it
  565. const float maxRange = 1500.0f;
  566. const HidingSpot *spot = FindInitialEncounterSpot( this, path[i]->area->GetCenter(), path[i]->area->GetEarliestOccupyTime( enemyTeam ), maxRange, IsSniper() );
  567. if (spot == NULL)
  568. {
  569. PrintIfWatched( "MoveToInitialEncounter: Can't find a hiding spot\n" );
  570. return false;
  571. }
  572. float timeToWait = path[i]->area->GetEarliestOccupyTime( enemyTeam ) - spot->GetArea()->GetEarliestOccupyTime( myTeam );
  573. float minWaitTime = 4.0f * GetProfile()->GetAggression() + 3.0f;
  574. if (timeToWait < minWaitTime)
  575. {
  576. timeToWait = minWaitTime;
  577. }
  578. Hide( spot->GetPosition(), timeToWait );
  579. return true;
  580. }