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.

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