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.

767 lines
19 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Encapsulation of the current scenario/game state. Allows each bot imperfect knowledge.
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. // Author: Michael S. Booth ([email protected]), 2003
  8. #include "cbase.h"
  9. #include "KeyValues.h"
  10. #include "cs_bot.h"
  11. #include "cs_gamestate.h"
  12. #include "cs_simple_hostage.h"
  13. // memdbgon must be the last include file in a .cpp file!!!
  14. #include "tier0/memdbgon.h"
  15. //--------------------------------------------------------------------------------------------------------------
  16. CSGameState::CSGameState( CCSBot *owner )
  17. {
  18. m_owner = owner;
  19. m_isRoundOver = false;
  20. m_bombState = MOVING;
  21. m_lastSawBomber.Invalidate();
  22. m_lastSawLooseBomb.Invalidate();
  23. m_isPlantedBombPosKnown = false;
  24. m_plantedBombsite = UNKNOWN;
  25. m_bombsiteCount = 0;
  26. m_bombsiteSearchIndex = 0;
  27. for( int i=0; i<MAX_HOSTAGES; ++i )
  28. {
  29. m_hostage[i].hostage = NULL;
  30. m_hostage[i].isValid = false;
  31. m_hostage[i].isAlive = false;
  32. m_hostage[i].isFree = true;
  33. m_hostage[i].knownPos = Vector( 0, 0, 0 );
  34. }
  35. }
  36. //--------------------------------------------------------------------------------------------------------------
  37. /**
  38. * Reset at round start
  39. */
  40. void CSGameState::Reset( void )
  41. {
  42. m_isRoundOver = false;
  43. // bomb -----------------------------------------------------------------------
  44. m_bombState = MOVING;
  45. m_lastSawBomber.Invalidate();
  46. m_lastSawLooseBomb.Invalidate();
  47. m_isPlantedBombPosKnown = false;
  48. m_plantedBombsite = UNKNOWN;
  49. m_bombsiteCount = TheCSBots()->GetZoneCount();
  50. int i;
  51. for( i=0; i<m_bombsiteCount; ++i )
  52. {
  53. m_isBombsiteClear[i] = false;
  54. m_bombsiteSearchOrder[i] = i;
  55. }
  56. // shuffle the bombsite search order
  57. // allows T's to plant at random site, and TEAM_CT's to search in a random order
  58. // NOTE: VS6 std::random_shuffle() doesn't work well with an array of two elements (most maps)
  59. for( i=0; i < m_bombsiteCount; ++i )
  60. {
  61. int swap = m_bombsiteSearchOrder[i];
  62. int rnd = RandomInt( i, m_bombsiteCount-1 );
  63. m_bombsiteSearchOrder[i] = m_bombsiteSearchOrder[ rnd ];
  64. m_bombsiteSearchOrder[ rnd ] = swap;
  65. }
  66. m_bombsiteSearchIndex = 0;
  67. // hostage ---------------------------------------------------------------------
  68. InitializeHostageInfo();
  69. }
  70. //--------------------------------------------------------------------------------------------------------------
  71. /**
  72. * Update game state based on events we have received
  73. */
  74. void CSGameState::OnHostageRescuedAll( IGameEvent *event )
  75. {
  76. m_allHostagesRescued = true;
  77. }
  78. //--------------------------------------------------------------------------------------------------------------
  79. /**
  80. * Update game state based on events we have received
  81. */
  82. void CSGameState::OnRoundEnd( IGameEvent *event )
  83. {
  84. m_isRoundOver = true;
  85. }
  86. //--------------------------------------------------------------------------------------------------------------
  87. /**
  88. * Update game state based on events we have received
  89. */
  90. void CSGameState::OnRoundStart( IGameEvent *event )
  91. {
  92. Reset();
  93. }
  94. //--------------------------------------------------------------------------------------------------------------
  95. /**
  96. * Update game state based on events we have received
  97. */
  98. void CSGameState::OnBombPlanted( IGameEvent *event )
  99. {
  100. // change state - the event is announced to everyone
  101. SetBombState( PLANTED );
  102. CBasePlayer *plantingPlayer = UTIL_PlayerByUserId( event->GetInt( "userid" ) );
  103. // Terrorists always know where the bomb is
  104. if (m_owner->GetTeamNumber() == TEAM_TERRORIST && plantingPlayer)
  105. {
  106. UpdatePlantedBomb( plantingPlayer->GetAbsOrigin() );
  107. }
  108. }
  109. //--------------------------------------------------------------------------------------------------------------
  110. /**
  111. * Update game state based on events we have received
  112. */
  113. void CSGameState::OnBombDefused( IGameEvent *event )
  114. {
  115. // change state - the event is announced to everyone
  116. SetBombState( DEFUSED );
  117. }
  118. //--------------------------------------------------------------------------------------------------------------
  119. /**
  120. * Update game state based on events we have received
  121. */
  122. void CSGameState::OnBombExploded( IGameEvent *event )
  123. {
  124. // change state - the event is announced to everyone
  125. SetBombState( EXPLODED );
  126. }
  127. //--------------------------------------------------------------------------------------------------------------
  128. /**
  129. * True if round has been won or lost (but not yet reset)
  130. */
  131. bool CSGameState::IsRoundOver( void ) const
  132. {
  133. return m_isRoundOver;
  134. }
  135. //--------------------------------------------------------------------------------------------------------------
  136. void CSGameState::SetBombState( BombState state )
  137. {
  138. // if state changed, reset "last seen" timestamps
  139. if (m_bombState != state)
  140. {
  141. m_bombState = state;
  142. }
  143. }
  144. //--------------------------------------------------------------------------------------------------------------
  145. void CSGameState::UpdateLooseBomb( const Vector &pos )
  146. {
  147. m_looseBombPos = pos;
  148. m_lastSawLooseBomb.Reset();
  149. // we saw the loose bomb, update our state
  150. SetBombState( LOOSE );
  151. }
  152. //--------------------------------------------------------------------------------------------------------------
  153. float CSGameState::TimeSinceLastSawLooseBomb( void ) const
  154. {
  155. return m_lastSawLooseBomb.GetElapsedTime();
  156. }
  157. //--------------------------------------------------------------------------------------------------------------
  158. bool CSGameState::IsLooseBombLocationKnown( void ) const
  159. {
  160. if (m_bombState != LOOSE)
  161. return false;
  162. return (m_lastSawLooseBomb.HasStarted()) ? true : false;
  163. }
  164. //--------------------------------------------------------------------------------------------------------------
  165. void CSGameState::UpdateBomber( const Vector &pos )
  166. {
  167. m_bomberPos = pos;
  168. m_lastSawBomber.Reset();
  169. // we saw the bomber, update our state
  170. SetBombState( MOVING );
  171. }
  172. //--------------------------------------------------------------------------------------------------------------
  173. float CSGameState::TimeSinceLastSawBomber( void ) const
  174. {
  175. return m_lastSawBomber.GetElapsedTime();
  176. }
  177. //--------------------------------------------------------------------------------------------------------------
  178. bool CSGameState::IsPlantedBombLocationKnown( void ) const
  179. {
  180. if (m_bombState != PLANTED)
  181. return false;
  182. return m_isPlantedBombPosKnown;
  183. }
  184. //--------------------------------------------------------------------------------------------------------------
  185. /**
  186. * Return the zone index of the planted bombsite, or UNKNOWN
  187. */
  188. int CSGameState::GetPlantedBombsite( void ) const
  189. {
  190. if (m_bombState != PLANTED)
  191. return UNKNOWN;
  192. return m_plantedBombsite;
  193. }
  194. //--------------------------------------------------------------------------------------------------------------
  195. /**
  196. * Return true if we are currently in the bombsite where the bomb is planted
  197. */
  198. bool CSGameState::IsAtPlantedBombsite( void ) const
  199. {
  200. if (m_bombState != PLANTED)
  201. return false;
  202. Vector myOrigin = GetCentroid( m_owner );
  203. const CCSBotManager::Zone *zone = TheCSBots()->GetClosestZone( myOrigin );
  204. if (zone)
  205. {
  206. return (m_plantedBombsite == zone->m_index);
  207. }
  208. return false;
  209. }
  210. //--------------------------------------------------------------------------------------------------------------
  211. /**
  212. * Return the zone index of the next bombsite to search
  213. */
  214. int CSGameState::GetNextBombsiteToSearch( void )
  215. {
  216. if (m_bombsiteCount <= 0)
  217. return 0;
  218. int i;
  219. // return next non-cleared bombsite index
  220. for( i=m_bombsiteSearchIndex; i<m_bombsiteCount; ++i )
  221. {
  222. int z = m_bombsiteSearchOrder[i];
  223. if (!m_isBombsiteClear[z])
  224. {
  225. m_bombsiteSearchIndex = i;
  226. return z;
  227. }
  228. }
  229. // all the bombsites are clear, someone must have been mistaken - start search over
  230. for( i=0; i<m_bombsiteCount; ++i )
  231. m_isBombsiteClear[i] = false;
  232. m_bombsiteSearchIndex = 0;
  233. return GetNextBombsiteToSearch();
  234. }
  235. //--------------------------------------------------------------------------------------------------------------
  236. /**
  237. * Returns position of bomb in its various states (moving, loose, planted),
  238. * or NULL if we don't know where the bomb is
  239. */
  240. const Vector *CSGameState::GetBombPosition( void ) const
  241. {
  242. switch( m_bombState )
  243. {
  244. case MOVING:
  245. {
  246. if (!m_lastSawBomber.HasStarted())
  247. return NULL;
  248. return &m_bomberPos;
  249. }
  250. case LOOSE:
  251. {
  252. if (IsLooseBombLocationKnown())
  253. return &m_looseBombPos;
  254. return NULL;
  255. }
  256. case PLANTED:
  257. {
  258. if (IsPlantedBombLocationKnown())
  259. return &m_plantedBombPos;
  260. return NULL;
  261. }
  262. }
  263. return NULL;
  264. }
  265. //--------------------------------------------------------------------------------------------------------------
  266. /**
  267. * We see the planted bomb at 'pos'
  268. */
  269. void CSGameState::UpdatePlantedBomb( const Vector &pos )
  270. {
  271. const CCSBotManager::Zone *zone = TheCSBots()->GetClosestZone( pos );
  272. if (zone == NULL)
  273. {
  274. CONSOLE_ECHO( "ERROR: Bomb planted outside of a zone!\n" );
  275. m_plantedBombsite = UNKNOWN;
  276. }
  277. else
  278. {
  279. m_plantedBombsite = zone->m_index;
  280. }
  281. m_plantedBombPos = pos;
  282. m_isPlantedBombPosKnown = true;
  283. SetBombState( PLANTED );
  284. }
  285. //--------------------------------------------------------------------------------------------------------------
  286. /**
  287. * Someone told us where the bomb is planted
  288. */
  289. void CSGameState::MarkBombsiteAsPlanted( int zoneIndex )
  290. {
  291. m_plantedBombsite = zoneIndex;
  292. SetBombState( PLANTED );
  293. }
  294. //--------------------------------------------------------------------------------------------------------------
  295. /**
  296. * Someone told us a bombsite is clear
  297. */
  298. void CSGameState::ClearBombsite( int zoneIndex )
  299. {
  300. if (zoneIndex >= 0 && zoneIndex < m_bombsiteCount)
  301. m_isBombsiteClear[ zoneIndex ] = true;
  302. }
  303. //--------------------------------------------------------------------------------------------------------------
  304. bool CSGameState::IsBombsiteClear( int zoneIndex ) const
  305. {
  306. if (zoneIndex >= 0 && zoneIndex < m_bombsiteCount)
  307. return m_isBombsiteClear[ zoneIndex ];
  308. return false;
  309. }
  310. //--------------------------------------------------------------------------------------------------------------
  311. /**
  312. * Initialize our knowledge of the number and location of hostages
  313. */
  314. void CSGameState::InitializeHostageInfo( void )
  315. {
  316. m_hostageCount = 0;
  317. m_allHostagesRescued = false;
  318. m_haveSomeHostagesBeenTaken = false;
  319. for( int i=0; i<g_Hostages.Count(); ++i )
  320. {
  321. m_hostage[ m_hostageCount ].hostage = g_Hostages[i];
  322. m_hostage[ m_hostageCount ].knownPos = g_Hostages[i]->GetAbsOrigin();
  323. m_hostage[ m_hostageCount ].isValid = true;
  324. m_hostage[ m_hostageCount ].isAlive = true;
  325. m_hostage[ m_hostageCount ].isFree = true;
  326. ++m_hostageCount;
  327. }
  328. }
  329. //--------------------------------------------------------------------------------------------------------------
  330. /**
  331. * Return the closest free and live hostage
  332. * If we are a CT this information is perfect.
  333. * Otherwise, this is based on our individual memory of the game state.
  334. * If NULL is returned, we don't think there are any hostages left, or we dont know where they are.
  335. * NOTE: a T can remember a hostage who has died. knowPos will be filled in, but NULL will be
  336. * returned, since CHostages get deleted when they die.
  337. */
  338. CHostage *CSGameState::GetNearestFreeHostage( Vector *knowPos ) const
  339. {
  340. if (m_owner == NULL)
  341. return NULL;
  342. CNavArea *startArea = m_owner->GetLastKnownArea();
  343. if (startArea == NULL)
  344. return NULL;
  345. CHostage *close = NULL;
  346. Vector closePos( 0, 0, 0 );
  347. float closeDistance = 9999999999.9f;
  348. for( int i=0; i<m_hostageCount; ++i )
  349. {
  350. CHostage *hostage = m_hostage[i].hostage;
  351. Vector hostagePos;
  352. if (m_owner->GetTeamNumber() == TEAM_CT)
  353. {
  354. // we know exactly where the hostages are, and if they are alive
  355. if (!m_hostage[i].hostage || !m_hostage[i].hostage->IsValid())
  356. continue;
  357. if (m_hostage[i].hostage->IsFollowingSomeone())
  358. continue;
  359. hostagePos = m_hostage[i].hostage->GetAbsOrigin();
  360. }
  361. else
  362. {
  363. // use our memory of where we think the hostages are
  364. if (m_hostage[i].isValid == false)
  365. continue;
  366. hostagePos = m_hostage[i].knownPos;
  367. }
  368. CNavArea *hostageArea = TheNavMesh->GetNearestNavArea( hostagePos );
  369. if (hostageArea)
  370. {
  371. ShortestPathCost cost;
  372. float travelDistance = NavAreaTravelDistance( startArea, hostageArea, cost );
  373. if (travelDistance >= 0.0f && travelDistance < closeDistance)
  374. {
  375. closeDistance = travelDistance;
  376. closePos = hostagePos;
  377. close = hostage;
  378. }
  379. }
  380. }
  381. // return where we think the hostage is
  382. if (knowPos && close)
  383. *knowPos = closePos;
  384. return close;
  385. }
  386. //--------------------------------------------------------------------------------------------------------------
  387. /**
  388. * Return the location of a "free" hostage, or NULL if we dont know of any
  389. */
  390. const Vector *CSGameState::GetRandomFreeHostagePosition( void ) const
  391. {
  392. if (m_owner == NULL)
  393. return NULL;
  394. static Vector freePos[ MAX_HOSTAGES ];
  395. int freeCount = 0;
  396. for( int i=0; i<m_hostageCount; ++i )
  397. {
  398. const HostageInfo *info = &m_hostage[i];
  399. if (m_owner->GetTeamNumber() == TEAM_CT)
  400. {
  401. // we know exactly where the hostages are, and if they are alive
  402. if (!info->hostage || !info->hostage->IsAlive())
  403. continue;
  404. // escorted hostages are not "free"
  405. if (info->hostage->IsFollowingSomeone())
  406. continue;
  407. freePos[ freeCount++ ] = info->hostage->GetAbsOrigin();
  408. }
  409. else
  410. {
  411. // use our memory of where we think the hostages are
  412. if (info->isValid == false)
  413. continue;
  414. freePos[ freeCount++ ] = info->knownPos;
  415. }
  416. }
  417. if (freeCount)
  418. {
  419. return &freePos[ RandomInt( 0, freeCount-1 ) ];
  420. }
  421. return NULL;
  422. }
  423. //--------------------------------------------------------------------------------------------------------------
  424. /**
  425. * If we can see any of the positions where we think a hostage is, validate it
  426. * Return status of any changes (a hostage died or was moved)
  427. */
  428. unsigned char CSGameState::ValidateHostagePositions( void )
  429. {
  430. // limit how often we validate
  431. if (!m_validateInterval.IsElapsed())
  432. return NO_CHANGE;
  433. const float validateInterval = 0.5f;
  434. m_validateInterval.Start( validateInterval );
  435. // check the status of hostages
  436. unsigned char status = NO_CHANGE;
  437. int i;
  438. int startValidCount = 0;
  439. for( i=0; i<m_hostageCount; ++i )
  440. if (m_hostage[i].isValid)
  441. ++startValidCount;
  442. for( i=0; i<m_hostageCount; ++i )
  443. {
  444. HostageInfo *info = &m_hostage[i];
  445. if (!info->hostage )
  446. continue;
  447. // if we can see a hostage, update our knowledge of it
  448. Vector pos = info->hostage->GetAbsOrigin() + Vector( 0, 0, HalfHumanHeight );
  449. if (m_owner->IsVisible( pos, CHECK_FOV ))
  450. {
  451. if (info->hostage->IsAlive())
  452. {
  453. // live hostage
  454. // if hostage is being escorted by a CT, we don't "see" it, we see the CT
  455. if (info->hostage->IsFollowingSomeone())
  456. {
  457. info->isValid = false;
  458. }
  459. else
  460. {
  461. info->knownPos = info->hostage->GetAbsOrigin();
  462. info->isValid = true;
  463. }
  464. }
  465. else
  466. {
  467. // dead hostage
  468. // if we thought it was alive, this is news to us
  469. if (info->isAlive)
  470. status |= HOSTAGE_DIED;
  471. info->isAlive = false;
  472. info->isValid = false;
  473. }
  474. continue;
  475. }
  476. // if we dont know where this hostage is, nothing to validate
  477. if (!info->isValid)
  478. continue;
  479. // can't directly see this hostage
  480. // check line of sight to where we think this hostage is, to see if we noticed that is has moved
  481. pos = info->knownPos + Vector( 0, 0, HalfHumanHeight );
  482. if (m_owner->IsVisible( pos, CHECK_FOV ))
  483. {
  484. // we can see where we thought the hostage was - verify it is still there and alive
  485. if (!info->hostage->IsValid())
  486. {
  487. // since we have line of sight to an invalid hostage, it must be dead
  488. // discovered that hostage has been killed
  489. status |= HOSTAGE_DIED;
  490. info->isAlive = false;
  491. info->isValid = false;
  492. continue;
  493. }
  494. if (info->hostage->IsFollowingSomeone())
  495. {
  496. // discovered the hostage has been taken
  497. status |= HOSTAGE_GONE;
  498. info->isValid = false;
  499. continue;
  500. }
  501. const float tolerance = 50.0f;
  502. if ((info->hostage->GetAbsOrigin() - info->knownPos).IsLengthGreaterThan( tolerance ))
  503. {
  504. // discovered that hostage has been moved
  505. status |= HOSTAGE_GONE;
  506. info->isValid = false;
  507. continue;
  508. }
  509. }
  510. }
  511. int endValidCount = 0;
  512. for( i=0; i<m_hostageCount; ++i )
  513. if (m_hostage[i].isValid)
  514. ++endValidCount;
  515. if (endValidCount == 0 && startValidCount > 0)
  516. {
  517. // we discovered all the hostages are gone
  518. status &= ~HOSTAGE_GONE;
  519. status |= HOSTAGES_ALL_GONE;
  520. }
  521. return status;
  522. }
  523. //--------------------------------------------------------------------------------------------------------------
  524. /**
  525. * Return the nearest visible free hostage
  526. * Since we can actually see any hostage we return, we know its actual position
  527. */
  528. CHostage *CSGameState::GetNearestVisibleFreeHostage( void ) const
  529. {
  530. CHostage *close = NULL;
  531. float closeRangeSq = 999999999.9f;
  532. float rangeSq;
  533. Vector pos;
  534. Vector myOrigin = GetCentroid( m_owner );
  535. for( int i=0; i<m_hostageCount; ++i )
  536. {
  537. const HostageInfo *info = &m_hostage[i];
  538. if ( !info->hostage )
  539. continue;
  540. // if the hostage is dead or rescued, its not free
  541. if (!info->hostage->IsAlive())
  542. continue;
  543. // if this hostage is following someone, its not free
  544. if (info->hostage->IsFollowingSomeone())
  545. continue;
  546. /// @todo Use travel distance here
  547. pos = info->hostage->GetAbsOrigin();
  548. rangeSq = (pos - myOrigin).LengthSqr();
  549. if (rangeSq < closeRangeSq)
  550. {
  551. if (!m_owner->IsVisible( pos ))
  552. continue;
  553. close = info->hostage;
  554. closeRangeSq = rangeSq;
  555. }
  556. }
  557. return close;
  558. }
  559. //--------------------------------------------------------------------------------------------------------------
  560. /**
  561. * Return true if there are no free hostages
  562. */
  563. bool CSGameState::AreAllHostagesBeingRescued( void ) const
  564. {
  565. // if the hostages have all been rescued, they are not being rescued any longer
  566. if (m_allHostagesRescued)
  567. return false;
  568. bool isAllDead = true;
  569. for( int i=0; i<m_hostageCount; ++i )
  570. {
  571. const HostageInfo *info = &m_hostage[i];
  572. if (m_owner->GetTeamNumber() == TEAM_CT)
  573. {
  574. // CT's have perfect knowledge via their radar
  575. if (info->hostage && info->hostage->IsValid())
  576. {
  577. if (!info->hostage->IsFollowingSomeone())
  578. return false;
  579. isAllDead = false;
  580. }
  581. }
  582. else
  583. {
  584. if (info->isValid && info->isAlive)
  585. return false;
  586. if (info->isAlive)
  587. isAllDead = false;
  588. }
  589. }
  590. // if all of the remaining hostages are dead, they arent being rescued
  591. if (isAllDead)
  592. return false;
  593. return true;
  594. }
  595. //--------------------------------------------------------------------------------------------------------------
  596. /**
  597. * All hostages have been rescued or are dead
  598. */
  599. bool CSGameState::AreAllHostagesGone( void ) const
  600. {
  601. if (m_allHostagesRescued)
  602. return true;
  603. // do we know that all the hostages are dead
  604. for( int i=0; i<m_hostageCount; ++i )
  605. {
  606. const HostageInfo *info = &m_hostage[i];
  607. if (m_owner->GetTeamNumber() == TEAM_CT)
  608. {
  609. // CT's have perfect knowledge via their radar
  610. if (info->hostage && info->hostage->IsAlive())
  611. return false;
  612. }
  613. else
  614. {
  615. if (info->isValid && info->isAlive)
  616. return false;
  617. }
  618. }
  619. return true;
  620. }
  621. //--------------------------------------------------------------------------------------------------------------
  622. /**
  623. * Someone told us all the hostages are gone
  624. */
  625. void CSGameState::AllHostagesGone( void )
  626. {
  627. for( int i=0; i<m_hostageCount; ++i )
  628. m_hostage[i].isValid = false;
  629. }