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.

777 lines
19 KiB

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