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.

2679 lines
68 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_gamerules.h"
  10. #include "cs_player.h"
  11. #include "shared_util.h"
  12. #include "engine/IEngineSound.h"
  13. #include "keyvalues.h"
  14. #include "bot.h"
  15. #include "bot_util.h"
  16. #include "cs_bot.h"
  17. #include "cs_bot_chatter.h"
  18. // memdbgon must be the last include file in a .cpp file!!!
  19. #include "tier0/memdbgon.h"
  20. // Use to toggle between old bot chatter system and response rules
  21. extern ConVar bot_chatter_use_rr;
  22. /**
  23. * @todo Fix this
  24. */
  25. bool GetRandomSpotAtPlace( Place place, Vector *pPos )
  26. {
  27. int count = 0;
  28. FOR_EACH_VEC( TheNavAreas, it )
  29. {
  30. CNavArea *area = TheNavAreas[ it ];
  31. if (area->GetPlace() == place)
  32. ++count;
  33. }
  34. if (count == 0)
  35. return false;
  36. int which = RandomInt( 0, count-1 );
  37. FOR_EACH_VEC( TheNavAreas, rit )
  38. {
  39. CNavArea *area = TheNavAreas[ rit ];
  40. if (area->GetPlace() == place && which == 0)
  41. {
  42. *pPos = area->GetCenter();
  43. return true;
  44. }
  45. }
  46. return false;
  47. }
  48. //---------------------------------------------------------------------------------------------------------------
  49. /**
  50. * Transmit meme to other bots
  51. */
  52. void BotMeme::Transmit( CCSBot *sender ) const
  53. {
  54. for( int i = 1; i <= gpGlobals->maxClients; i++ )
  55. {
  56. CCSPlayer *player = static_cast<CCSPlayer *>( UTIL_PlayerByIndex( i ) );
  57. if (player == NULL)
  58. continue;
  59. // if (FNullEnt( player->pev ))
  60. // continue;
  61. // if (FStrEq( STRING( player->pev->netname ), "" ))
  62. // continue;
  63. // skip self
  64. if (sender == player)
  65. continue;
  66. // ignore dead humans
  67. if (!player->IsBot() && !player->IsAlive())
  68. continue;
  69. // ignore enemies, since we can't hear them talk
  70. if (sender && sender->IsOtherEnemy( player ))
  71. continue;
  72. // if not a bot, fail the test
  73. if (!player->IsBot())
  74. continue;
  75. CCSBot *bot = dynamic_cast<CCSBot *>( player );
  76. if ( !bot )
  77. continue;
  78. // allow bot to interpret our meme
  79. Interpret( sender, bot );
  80. }
  81. }
  82. //---------------------------------------------------------------------------------------------------------------
  83. /**
  84. * A teammate called for help - respond
  85. */
  86. void BotHelpMeme::Interpret( CCSBot *sender, CCSBot *receiver ) const
  87. {
  88. const float maxHelpRange = 3000.0f; // 2000
  89. receiver->RespondToHelpRequest( sender, m_place, maxHelpRange );
  90. }
  91. //---------------------------------------------------------------------------------------------------------------
  92. /**
  93. * A teammate reported information about a bombsite
  94. */
  95. void BotBombsiteStatusMeme::Interpret( CCSBot *sender, CCSBot *receiver ) const
  96. {
  97. // remember this bombsite's status
  98. if (m_status == CLEAR)
  99. receiver->GetGameState()->ClearBombsite( m_zoneIndex );
  100. else
  101. receiver->GetGameState()->MarkBombsiteAsPlanted( m_zoneIndex );
  102. // if we were heading to the just-cleared bombsite, pick another one to search
  103. // if our target bombsite wasn't cleared, will will continue going to it,
  104. // because GetNextBombsiteToSearch() will return the same zone (since its not cleared)
  105. // if the bomb was planted, we will head to that bombsite
  106. if (receiver->GetTask() == CCSBot::FIND_TICKING_BOMB)
  107. {
  108. receiver->Idle();
  109. receiver->GetChatter()->Affirmative();
  110. }
  111. }
  112. //---------------------------------------------------------------------------------------------------------------
  113. /**
  114. * A teammate reported information about the bomb
  115. */
  116. void BotBombStatusMeme::Interpret( CCSBot *sender, CCSBot *receiver ) const
  117. {
  118. // update our gamestate based on teammate's report
  119. switch( m_state )
  120. {
  121. case CSGameState::MOVING:
  122. receiver->GetGameState()->UpdateBomber( m_pos );
  123. // if we are hunting and see no enemies, respond
  124. if (!receiver->IsRogue() && receiver->IsHunting() && receiver->GetNearbyEnemyCount() == 0)
  125. receiver->RespondToHelpRequest( sender, TheNavMesh->GetPlace( m_pos ) );
  126. break;
  127. case CSGameState::LOOSE:
  128. receiver->GetGameState()->UpdateLooseBomb( m_pos );
  129. if (receiver->GetTask() == CCSBot::GUARD_BOMB_ZONE)
  130. {
  131. receiver->Idle();
  132. receiver->GetChatter()->Affirmative();
  133. }
  134. break;
  135. }
  136. }
  137. //---------------------------------------------------------------------------------------------------------------
  138. /**
  139. * A teammate has asked that we follow him
  140. */
  141. void BotFollowMeme::Interpret( CCSBot *sender, CCSBot *receiver ) const
  142. {
  143. if (receiver->IsRogue())
  144. return;
  145. // if we're busy, ignore
  146. if (receiver->IsBusy())
  147. return;
  148. // if we are too far away, ignore
  149. // compute actual travel distance
  150. Vector senderOrigin = GetCentroid( sender );
  151. PathCost cost( receiver );
  152. float travelDistance = NavAreaTravelDistance( receiver->GetLastKnownArea(),
  153. TheNavMesh->GetNearestNavArea( senderOrigin ),
  154. cost );
  155. if (travelDistance < 0.0f)
  156. return;
  157. const float tooFar = 1000.0f;
  158. if (travelDistance > tooFar)
  159. return;
  160. // begin following
  161. receiver->Follow( sender );
  162. // acknowledge
  163. receiver->GetChatter()->Say( "CoveringFriend" );
  164. }
  165. //---------------------------------------------------------------------------------------------------------------
  166. /**
  167. * A teammate has asked us to defend a place
  168. */
  169. void BotDefendHereMeme::Interpret( CCSBot *sender, CCSBot *receiver ) const
  170. {
  171. if (receiver->IsRogue())
  172. return;
  173. // if we're busy, ignore
  174. if (receiver->IsBusy())
  175. return;
  176. Place place = TheNavMesh->GetPlace( m_pos );
  177. if (place != UNDEFINED_PLACE)
  178. {
  179. // pick a random hiding spot in this place
  180. const Vector *spot = FindRandomHidingSpot( receiver, place, receiver->IsSniper() );
  181. if (spot)
  182. {
  183. receiver->SetTask( CCSBot::HOLD_POSITION );
  184. receiver->Hide( *spot );
  185. return;
  186. }
  187. }
  188. // hide nearby
  189. receiver->SetTask( CCSBot::HOLD_POSITION );
  190. receiver->Hide( TheNavMesh->GetNearestNavArea( m_pos ) );
  191. // acknowledge
  192. receiver->GetChatter()->Say( "Affirmative" );
  193. }
  194. //---------------------------------------------------------------------------------------------------------------
  195. /**
  196. * A teammate has asked where the bomb is planted
  197. */
  198. void BotWhereBombMeme::Interpret( CCSBot *sender, CCSBot *receiver ) const
  199. {
  200. int zone = receiver->GetGameState()->GetPlantedBombsite();
  201. if (zone != CSGameState::UNKNOWN)
  202. receiver->GetChatter()->FoundPlantedBomb( zone );
  203. }
  204. //---------------------------------------------------------------------------------------------------------------
  205. /**
  206. * A teammate has asked us to report in
  207. */
  208. void BotRequestReportMeme::Interpret( CCSBot *sender, CCSBot *receiver ) const
  209. {
  210. receiver->GetChatter()->ReportingIn();
  211. }
  212. //---------------------------------------------------------------------------------------------------------------
  213. /**
  214. * A teammate told us all the hostages are gone
  215. */
  216. void BotAllHostagesGoneMeme::Interpret( CCSBot *sender, CCSBot *receiver ) const
  217. {
  218. receiver->GetGameState()->AllHostagesGone();
  219. // acknowledge
  220. receiver->GetChatter()->Say( "Affirmative" );
  221. }
  222. //---------------------------------------------------------------------------------------------------------------
  223. /**
  224. * A teammate told us a CT is talking to a hostage
  225. */
  226. void BotHostageBeingTakenMeme::Interpret( CCSBot *sender, CCSBot *receiver ) const
  227. {
  228. receiver->GetGameState()->HostageWasTaken();
  229. // if we're busy, ignore
  230. if (receiver->IsBusy())
  231. return;
  232. receiver->Idle();
  233. // acknowledge
  234. receiver->GetChatter()->Say( "Affirmative" );
  235. }
  236. //---------------------------------------------------------------------------------------------------------------
  237. /**
  238. * A teammate heard a noise, so we shouldn't report noises for a while
  239. */
  240. void BotHeardNoiseMeme::Interpret( CCSBot *sender, CCSBot *receiver ) const
  241. {
  242. receiver->GetChatter()->FriendHeardNoise();
  243. }
  244. //---------------------------------------------------------------------------------------------------------------
  245. /**
  246. * A teammate warned about snipers, so we shouldn't warn again for awhile
  247. */
  248. void BotWarnSniperMeme::Interpret( CCSBot *sender, CCSBot *receiver ) const
  249. {
  250. receiver->GetChatter()->FriendSpottedSniper();
  251. }
  252. //---------------------------------------------------------------------------------------------------------------
  253. BotSpeakable::BotSpeakable()
  254. {
  255. m_phrase = NULL;
  256. }
  257. //---------------------------------------------------------------------------------------------------------------
  258. BotSpeakable::~BotSpeakable()
  259. {
  260. if ( m_phrase )
  261. {
  262. delete[] m_phrase;
  263. m_phrase = NULL;
  264. }
  265. }
  266. //---------------------------------------------------------------------------------------------------------------
  267. //---------------------------------------------------------------------------------------------------------------
  268. BotPhrase::BotPhrase( bool isPlace )
  269. {
  270. m_name = NULL;
  271. m_place = UNDEFINED_PLACE;
  272. m_isPlace = isPlace;
  273. m_radioEvent = RADIO_INVALID;
  274. m_isImportant = false;
  275. ClearCriteria();
  276. m_numVoiceBanks = 0;
  277. InitVoiceBank( 0 );
  278. }
  279. BotPhrase::~BotPhrase()
  280. {
  281. for( int bank=0; bank<m_voiceBank.Count(); ++bank )
  282. {
  283. for( int speakable=0; speakable<m_voiceBank[bank]->Count(); ++speakable )
  284. {
  285. delete (*m_voiceBank[bank])[speakable];
  286. }
  287. delete m_voiceBank[bank];
  288. }
  289. if ( m_name )
  290. delete [] m_name;
  291. }
  292. void BotPhrase::InitVoiceBank( int bankIndex )
  293. {
  294. while ( m_numVoiceBanks <= bankIndex )
  295. {
  296. m_count.AddToTail(0);
  297. m_index.AddToTail(0);
  298. m_voiceBank.AddToTail( new BotSpeakableVector );
  299. ++m_numVoiceBanks;
  300. }
  301. }
  302. /**
  303. * Return a random speakable - avoid repeating
  304. */
  305. char *BotPhrase::GetSpeakable( int bankIndex, float *duration ) const
  306. {
  307. if (bankIndex < 0 || bankIndex >= m_numVoiceBanks || m_count[bankIndex] == 0)
  308. {
  309. if (duration)
  310. *duration = 0.0f;
  311. return NULL;
  312. }
  313. // find phrase that meets the current criteria
  314. int start = m_index[bankIndex];
  315. while(true)
  316. {
  317. BotSpeakableVector *speakables = m_voiceBank[bankIndex];
  318. int& index = m_index[bankIndex];
  319. const BotSpeakable *speak = (*speakables)[index++];
  320. if (m_index[bankIndex] >= m_count[bankIndex])
  321. m_index[bankIndex] = 0;
  322. // check place criteria
  323. // if this speakable has a place criteria, it must match to be used
  324. // speakables with Place of ANY will match any place
  325. // speakables with a specific Place will only be used if Place matches
  326. // speakables with Place of UNDEFINED only match Place of UNDEFINED
  327. if (speak->m_place == ANY_PLACE || speak->m_place == m_placeCriteria)
  328. {
  329. // check count criteria
  330. // if this speakable has a count criteria, it must match to be used
  331. // if this speakable does not have a count criteria, we dont care what the count is set to
  332. if (speak->m_count == UNDEFINED_COUNT || speak->m_count == MIN( m_countCriteria, COUNT_MANY ))
  333. {
  334. if (duration)
  335. *duration = speak->m_duration;
  336. return speak->m_phrase;
  337. }
  338. }
  339. // check if we exhausted all speakables
  340. if (m_index[bankIndex] == start)
  341. {
  342. if (duration)
  343. *duration = 0.0f;
  344. return NULL;
  345. }
  346. }
  347. }
  348. //---------------------------------------------------------------------------------------------------------------
  349. /**
  350. * Randomly shuffle the speakable order
  351. */
  352. void BotPhrase::Randomize( void )
  353. {
  354. for ( int bank = 0; bank < m_voiceBank.Count(); ++bank )
  355. {
  356. BotSpeakableVector *speakables = m_voiceBank[bank];
  357. if ( speakables->Count() == 1 )
  358. continue;
  359. // A simple shuffle: for each array index, swap it with a random index
  360. for ( int index = 0; index < speakables->Count(); ++index )
  361. {
  362. int newIndex = RandomInt( 0, speakables->Count()-1 );
  363. BotSpeakable *speakable = (*speakables)[index];
  364. (*speakables)[index] = (*speakables)[newIndex];
  365. (*speakables)[newIndex] = speakable;
  366. }
  367. }
  368. }
  369. //---------------------------------------------------------------------------------------------------------------
  370. //---------------------------------------------------------------------------------------------------------------
  371. BotPhraseManager *TheBotPhrases = NULL;
  372. BotPhraseManager::BotPhraseManager( void )
  373. {
  374. m_placeCount = 0;
  375. }
  376. /**
  377. * Invoked when map changes
  378. */
  379. void BotPhraseManager::OnMapChange( void )
  380. {
  381. m_placeCount = 0;
  382. }
  383. /**
  384. * Removes everything from memory
  385. */
  386. void BotPhraseManager::Reset( void )
  387. {
  388. int i;
  389. // free phrase resources
  390. for( i=0; i<m_list.Count(); ++i )
  391. {
  392. delete m_list[i];
  393. }
  394. for( i=0; i<m_placeList.Count(); ++i )
  395. {
  396. delete m_placeList[i];
  397. }
  398. m_list.RemoveAll();
  399. m_placeList.RemoveAll();
  400. m_painPhrase = NULL;
  401. m_agreeWithPlanPhrase = NULL;
  402. }
  403. /**
  404. * Invoked when the round resets
  405. */
  406. void BotPhraseManager::OnRoundRestart( void )
  407. {
  408. // effectively reset all interval timers
  409. m_placeCount = 0;
  410. // shuffle all the speakables
  411. int i;
  412. for( i=0; i<m_placeList.Count(); ++i )
  413. m_placeList[i]->Randomize();
  414. for( i=0; i<m_list.Count(); ++i )
  415. m_list[i]->Randomize();
  416. }
  417. BotChatterOutputType BotPhraseManager::GetOutputType( int voiceBank ) const
  418. {
  419. if ( voiceBank >= 0 && voiceBank < m_output.Count() )
  420. {
  421. return m_output[voiceBank];
  422. }
  423. return BOT_CHATTER_RADIO;
  424. }
  425. /**
  426. * Initialize phrase system from database file
  427. */
  428. bool BotPhraseManager::Initialize( const char *filename, int bankIndex )
  429. {
  430. bool isDefault = (bankIndex == 0);
  431. FileHandle_t file = filesystem->Open( filename, "r" );
  432. if (!file)
  433. {
  434. CONSOLE_ECHO( "WARNING: Cannot access bot phrase database '%s'\n", filename );
  435. return false;
  436. }
  437. // BOTPORT: Redo file reading to avoid loading whole file into memory at once
  438. int phraseDataLength = filesystem->Size( filename );
  439. char *phraseDataFile = new char[ phraseDataLength ];
  440. int dataReadLength = filesystem->Read( phraseDataFile, phraseDataLength, file );
  441. filesystem->Close( file );
  442. if ( dataReadLength > 0 )
  443. {
  444. // NULL-terminate based on the length read in, since Read() can transform \r\n to \n and
  445. // return fewer bytes than we were expecting.
  446. phraseDataFile[ dataReadLength - 1 ] = 0;
  447. }
  448. const char *phraseData = phraseDataFile;
  449. const int RadioPathLen = 128; // wav filenames need to be shorter than this to go over the net anyway.
  450. char baseDir[RadioPathLen] = "";
  451. char compositeFilename[RadioPathLen];
  452. //
  453. // Parse the BotChatter.db into BotPhrase collections
  454. //
  455. while( true )
  456. {
  457. phraseData = SharedParse( phraseData );
  458. if (!phraseData)
  459. break;
  460. char *token = SharedGetToken();
  461. if ( !stricmp( token, "Output" ) )
  462. {
  463. // get name of this output device
  464. phraseData = SharedParse( phraseData );
  465. if (!phraseData)
  466. {
  467. CONSOLE_ECHO( "Error parsing '%s' - expected identifier\n", filename );
  468. delete [] phraseDataFile;
  469. return false;
  470. }
  471. while ( m_output.Count() <= bankIndex )
  472. {
  473. m_output.AddToTail(BOT_CHATTER_RADIO);
  474. }
  475. char *token = SharedGetToken();
  476. if ( !stricmp( token, "Voice" ) )
  477. {
  478. m_output[bankIndex] = BOT_CHATTER_VOICE;
  479. }
  480. }
  481. else if ( !stricmp( token, "BaseDir" ) )
  482. {
  483. // get name of this output device
  484. phraseData = SharedParse( phraseData );
  485. if (!phraseData)
  486. {
  487. CONSOLE_ECHO( "Error parsing '%s' - expected identifier\n", filename );
  488. delete [] phraseDataFile;
  489. return false;
  490. }
  491. char *token = SharedGetToken();
  492. Q_strncpy( baseDir, token, RadioPathLen );
  493. Q_strncat( baseDir, "\\", RadioPathLen, -1 );
  494. baseDir[RadioPathLen-1] = 0;
  495. }
  496. else if (!stricmp( token, "Place" ) || !stricmp( token, "Chatter" ))
  497. {
  498. bool isPlace = (stricmp( token, "Place" )) ? false : true;
  499. // encountered a new phrase collection
  500. BotPhrase *phrase = NULL;
  501. if ( isDefault )
  502. {
  503. phrase = new BotPhrase( isPlace );
  504. }
  505. // get name of this phrase
  506. phraseData = SharedParse( phraseData );
  507. if (!phraseData)
  508. {
  509. CONSOLE_ECHO( "Error parsing '%s' - expected identifier\n", filename );
  510. delete [] phraseDataFile;
  511. return false;
  512. }
  513. if ( isDefault )
  514. {
  515. phrase->m_name = CloneString( SharedGetToken() );
  516. phrase->m_place = (isPlace) ? TheNavMesh->NameToPlace( phrase->m_name ) : UNDEFINED_PLACE;
  517. }
  518. else // look up the existing phrase
  519. {
  520. if ( isPlace )
  521. {
  522. phrase = const_cast<BotPhrase *>(GetPlace( SharedGetToken() ));
  523. }
  524. else
  525. {
  526. phrase = const_cast<BotPhrase *>(GetPhrase( SharedGetToken() ));
  527. }
  528. if ( !phrase )
  529. {
  530. CONSOLE_ECHO( "Error parsing '%s' - phrase '%s' is invalid\n", filename, SharedGetToken() );
  531. delete [] phraseDataFile;
  532. return false;
  533. }
  534. }
  535. phrase->InitVoiceBank( bankIndex );
  536. PlaceCriteria placeCriteria = ANY_PLACE;
  537. CountCriteria countCriteria = UNDEFINED_COUNT;
  538. RadioType radioEvent = RADIO_INVALID;
  539. bool isImportant = false;
  540. // read attributes of this phrase
  541. while( true )
  542. {
  543. // get next token
  544. phraseData = SharedParse( phraseData );
  545. if (!phraseData)
  546. {
  547. CONSOLE_ECHO( "Error parsing %s - expected 'End'\n", filename );
  548. delete [] phraseDataFile;
  549. return false;
  550. }
  551. token = SharedGetToken();
  552. // check for Place criteria
  553. if (!stricmp( token, "Place" ))
  554. {
  555. phraseData = SharedParse( phraseData );
  556. if (!phraseData)
  557. {
  558. CONSOLE_ECHO( "Error parsing %s - expected Place name\n", filename );
  559. delete [] phraseDataFile;
  560. return false;
  561. }
  562. token = SharedGetToken();
  563. // update place criteria for subsequent speak lines
  564. // NOTE: this assumes places must be first in the chatter database
  565. // check for special identifiers
  566. if (!stricmp( "ANY", token ))
  567. placeCriteria = ANY_PLACE;
  568. else if (!stricmp( "UNDEFINED", token ))
  569. placeCriteria = UNDEFINED_PLACE;
  570. else
  571. placeCriteria = TheNavMesh->NameToPlace( token );
  572. continue;
  573. }
  574. // check for Count criteria
  575. if (!stricmp( token, "Count" ))
  576. {
  577. phraseData = SharedParse( phraseData );
  578. if (!phraseData)
  579. {
  580. CONSOLE_ECHO( "Error parsing %s - expected Count value\n", filename );
  581. delete [] phraseDataFile;
  582. return false;
  583. }
  584. token = SharedGetToken();
  585. // update count criteria for subsequent speak lines
  586. if (!stricmp( token, "Many" ))
  587. countCriteria = COUNT_MANY;
  588. else
  589. countCriteria = atoi( token );
  590. continue;
  591. }
  592. // check for radio equivalent
  593. if (!stricmp( token, "Radio" ))
  594. {
  595. phraseData = SharedParse( phraseData );
  596. if (!phraseData)
  597. {
  598. CONSOLE_ECHO( "Error parsing %s - expected radio event\n", filename );
  599. delete [] phraseDataFile;
  600. return false;
  601. }
  602. token = SharedGetToken();
  603. RadioType event = NameToRadioEvent( token );
  604. if (event <= RADIO_START_1 || event >= RADIO_END)
  605. {
  606. CONSOLE_ECHO( "Error parsing %s - invalid radio event '%s'\n", filename, token );
  607. delete [] phraseDataFile;
  608. return false;
  609. }
  610. radioEvent = event;
  611. continue;
  612. }
  613. // check for "important" flag
  614. if (!stricmp( token, "Important" ))
  615. {
  616. isImportant = true;
  617. continue;
  618. }
  619. // check for End delimiter
  620. if (!stricmp( token, "End" ))
  621. break;
  622. // found a phrase - add it to the collection
  623. BotSpeakable *speak = new BotSpeakable;
  624. if ( baseDir[0] )
  625. {
  626. Q_snprintf( compositeFilename, RadioPathLen, "%s%s", baseDir, token );
  627. speak->m_phrase = CloneString( compositeFilename );
  628. }
  629. else
  630. {
  631. speak->m_phrase = CloneString( token );
  632. }
  633. speak->m_place = placeCriteria;
  634. speak->m_count = countCriteria;
  635. #ifdef POSIX
  636. Q_FixSlashes( speak->m_phrase );
  637. Q_strlower( speak->m_phrase );
  638. #endif
  639. #if defined( CSTRIKE15 )
  640. // cstrike15 doesn't use bot chatter this way, the system redirects to RR, so no need to do anything other than slam this
  641. speak->m_duration = 1.0f;
  642. #else
  643. speak->m_duration = enginesound->GetSoundDuration( speak->m_phrase );
  644. if (speak->m_duration <= 0.0f)
  645. {
  646. if ( !engine->IsDedicatedServer() )
  647. {
  648. DevMsg( "Warning: Couldn't get duration of phrase '%s'\n", speak->m_phrase );
  649. }
  650. speak->m_duration = 1.0f;
  651. }
  652. #endif
  653. BotSpeakableVector * speakables = phrase->m_voiceBank[ bankIndex ];
  654. speakables->AddToTail( speak );
  655. ++phrase->m_count[ bankIndex ];
  656. }
  657. if ( isDefault )
  658. {
  659. phrase->m_radioEvent = radioEvent;
  660. phrase->m_isImportant = isImportant;
  661. }
  662. // add phrase collection to the appropriate master list
  663. if (isPlace)
  664. m_placeList.AddToTail( phrase );
  665. else
  666. m_list.AddToTail( phrase );
  667. }
  668. }
  669. delete [] phraseDataFile;
  670. m_painPhrase = GetPhrase( "Pain" );
  671. m_agreeWithPlanPhrase = GetPhrase( "AgreeWithPlan" );
  672. return true;
  673. }
  674. BotPhraseManager::~BotPhraseManager()
  675. {
  676. Reset();
  677. }
  678. /**
  679. * Given a name, return the associated phrase collection
  680. */
  681. const BotPhrase *BotPhraseManager::GetPhrase( const char *name ) const
  682. {
  683. for( int i=0; i<m_list.Count(); ++i )
  684. {
  685. if (!stricmp( m_list[i]->m_name, name ))
  686. return m_list[i];
  687. }
  688. //CONSOLE_ECHO( "GetPhrase: ERROR - Invalid phrase '%s'\n", name );
  689. return NULL;
  690. }
  691. /**
  692. * Given an id, return the associated phrase collection
  693. * @todo Store phrases in a vector to make this fast
  694. */
  695. /*
  696. const BotPhrase *BotPhraseManager::GetPhrase( unsigned int place ) const
  697. {
  698. for( BotPhraseList::const_iterator iter = m_list.begin(); iter != m_list.end(); ++iter )
  699. {
  700. const BotPhrase *phrase = *iter;
  701. if (phrase->m_place == id)
  702. return phrase;
  703. }
  704. CONSOLE_ECHO( "GetPhrase: ERROR - Invalid phrase id #%d\n", id );
  705. return NULL;
  706. }
  707. */
  708. /**
  709. * Given a name, return the associated Place phrase collection
  710. */
  711. const BotPhrase *BotPhraseManager::GetPlace( const char *name ) const
  712. {
  713. if (name == NULL)
  714. return NULL;
  715. for( int i=0; i<m_placeList.Count(); ++i )
  716. {
  717. if (!stricmp( m_placeList[i]->m_name, name ))
  718. return m_placeList[i];
  719. }
  720. return NULL;
  721. }
  722. /**
  723. * Given a name, return the associated Place phrase collection
  724. */
  725. const BotPhrase *BotPhraseManager::GetPlace( PlaceCriteria place ) const
  726. {
  727. if (place == UNDEFINED_PLACE)
  728. return NULL;
  729. for( int i=0; i<m_placeList.Count(); ++i )
  730. {
  731. if (m_placeList[i]->m_place == place)
  732. return m_placeList[i];
  733. }
  734. return NULL;
  735. }
  736. //---------------------------------------------------------------------------------------------------------------
  737. //---------------------------------------------------------------------------------------------------------------
  738. BotStatement::BotStatement( BotChatterInterface *chatter, BotStatementType type, float expireDuration )
  739. {
  740. m_chatter = chatter;
  741. m_next = NULL;
  742. m_prev = NULL;
  743. m_timestamp = gpGlobals->curtime;
  744. m_speakTimestamp = 0.0f;
  745. m_type = type;
  746. m_subject = UNDEFINED_SUBJECT;
  747. m_place = UNDEFINED_PLACE;
  748. m_meme = NULL;
  749. m_startTime = gpGlobals->curtime;
  750. m_expireTime = gpGlobals->curtime + expireDuration;
  751. m_isSpeaking = false;
  752. m_nextTime = 0.0f;
  753. m_index = -1;
  754. m_count = 0;
  755. m_conditionCount = 0;
  756. }
  757. BotStatement::~BotStatement()
  758. {
  759. if (m_meme)
  760. delete m_meme;
  761. }
  762. //---------------------------------------------------------------------------------------------------------------
  763. CCSBot *BotStatement::GetOwner( void ) const
  764. {
  765. return m_chatter->GetOwner();
  766. }
  767. //---------------------------------------------------------------------------------------------------------------
  768. /**
  769. * Attach a meme to this statement, to be transmitted to other friendly bots when spoken
  770. */
  771. void BotStatement::AttachMeme( BotMeme *meme )
  772. {
  773. m_meme = meme;
  774. }
  775. //---------------------------------------------------------------------------------------------------------------
  776. /**
  777. * Add a conditions that must be true for the statement to be spoken
  778. */
  779. void BotStatement::AddCondition( ConditionType condition )
  780. {
  781. if (m_conditionCount < MAX_BOT_CONDITIONS)
  782. m_condition[ m_conditionCount++ ] = condition;
  783. }
  784. //---------------------------------------------------------------------------------------------------------------
  785. /**
  786. * Return true if this statement is "important" and not personality chatter
  787. */
  788. bool BotStatement::IsImportant( void ) const
  789. {
  790. // if a statement contains any important phrases, it is important
  791. for( int i=0; i<m_count; ++i )
  792. {
  793. if (m_statement[i].isPhrase && m_statement[i].phrase->IsImportant())
  794. return true;
  795. // hack for now - phrases with enemy counts are important
  796. if (!m_statement[i].isPhrase && m_statement[i].context == BotStatement::CURRENT_ENEMY_COUNT)
  797. return true;
  798. }
  799. return false;
  800. }
  801. //---------------------------------------------------------------------------------------------------------------
  802. /**
  803. * Verify all attached conditions
  804. */
  805. bool BotStatement::IsValid( void ) const
  806. {
  807. for( int i=0; i<m_conditionCount; ++i )
  808. {
  809. switch( m_condition[i] )
  810. {
  811. case IS_IN_COMBAT:
  812. {
  813. // Don't speak in combat, unless we're playing a coop mission
  814. if ( !GetOwner()->IsAttacking() && !CSGameRules()->IsPlayingCoopMission() )
  815. return false;
  816. break;
  817. }
  818. /*
  819. case RADIO_SILENCE:
  820. {
  821. if (GetOwner()->GetChatter()->GetRadioSilenceDuration() < 10.0f)
  822. return false;
  823. break;
  824. }
  825. */
  826. case ENEMIES_REMAINING:
  827. {
  828. if (GetOwner()->GetEnemiesRemaining() == 0)
  829. return false;
  830. break;
  831. }
  832. }
  833. }
  834. return true;
  835. }
  836. //---------------------------------------------------------------------------------------------------------------
  837. /**
  838. * Return true if this statement is essentially the same as the given one
  839. */
  840. bool BotStatement::IsRedundant( const BotStatement *say ) const
  841. {
  842. // special cases
  843. if (GetType() == REPORT_MY_PLAN ||
  844. GetType() == REPORT_REQUEST_HELP ||
  845. GetType() == REPORT_CRITICAL_EVENT ||
  846. GetType() == REPORT_ACKNOWLEDGE)
  847. return false;
  848. // check if topics are different
  849. if (say->GetType() != GetType())
  850. return false;
  851. if (!say->HasPlace() && !HasPlace() && !say->HasSubject() && !HasSubject())
  852. {
  853. // neither has place or subject, so they are the same
  854. return true;
  855. }
  856. // check if subject matter is the same
  857. if (say->HasPlace() && HasPlace() && say->GetPlace() == GetPlace())
  858. {
  859. // talking about the same place
  860. return true;
  861. }
  862. if (say->HasSubject() && HasSubject() && say->GetSubject() == GetSubject())
  863. {
  864. // talking about the same player
  865. return true;
  866. }
  867. return false;
  868. }
  869. //---------------------------------------------------------------------------------------------------------------
  870. /**
  871. * Return true if this statement is no longer appropriate to say
  872. */
  873. bool BotStatement::IsObsolete( void ) const
  874. {
  875. // if the round is over, the only things we should say are emotes
  876. if (GetOwner()->GetGameState()->IsRoundOver())
  877. {
  878. if (m_type != REPORT_EMOTE)
  879. return true;
  880. }
  881. // If we're wanting to say "I lost him" but we've spotted another enemy,
  882. // we no longer need to report losing someone.
  883. if ( GetOwner()->GetChatter()->SeesAtLeastOneEnemy() && m_type == REPORT_ENEMY_LOST )
  884. {
  885. return true;
  886. }
  887. // check if statement lifetime has expired
  888. return (gpGlobals->curtime > m_expireTime);
  889. }
  890. //---------------------------------------------------------------------------------------------------------------
  891. /**
  892. * Possibly change what were going to say base on what teammate is saying
  893. */
  894. void BotStatement::Convert( const BotStatement *say )
  895. {
  896. if (GetType() == REPORT_MY_PLAN && say->GetType() == REPORT_MY_PLAN)
  897. {
  898. const BotPhrase *meToo = TheBotPhrases->GetAgreeWithPlanPhrase();
  899. // don't reconvert
  900. if (m_statement[0].phrase == meToo)
  901. return;
  902. // if our plans are the same, change our statement to "me too"
  903. if (m_statement[0].phrase == say->m_statement[0].phrase)
  904. {
  905. if (m_place == say->m_place)
  906. {
  907. // same plan at the same place - convert to "me too"
  908. m_statement[0].phrase = meToo;
  909. m_startTime = gpGlobals->curtime + RandomFloat( 0.5f, 1.0f );
  910. }
  911. else
  912. {
  913. // same plan at different place - wait a bit to allow others to respond "me too"
  914. m_startTime = gpGlobals->curtime + RandomFloat( 3.0f, 4.0f );
  915. }
  916. }
  917. }
  918. }
  919. //---------------------------------------------------------------------------------------------------------------
  920. void BotStatement::AppendPhrase( const BotPhrase *phrase )
  921. {
  922. if (phrase == NULL)
  923. return;
  924. if (m_count < MAX_BOT_PHRASES)
  925. {
  926. m_statement[ m_count ].isPhrase = true;
  927. m_statement[ m_count++ ].phrase = phrase;
  928. }
  929. }
  930. /**
  931. * Special phrases that depend on the context
  932. */
  933. void BotStatement::AppendPhrase( ContextType contextPhrase )
  934. {
  935. if (m_count < MAX_BOT_PHRASES)
  936. {
  937. m_statement[ m_count ].isPhrase = false;
  938. m_statement[ m_count++ ].context = contextPhrase;
  939. }
  940. }
  941. /**
  942. * Say our statement
  943. * m_index refers to the phrase currently being spoken, or -1 if we havent started yet
  944. */
  945. bool BotStatement::Update( void )
  946. {
  947. CCSBot *me = GetOwner();
  948. // if all of our teammates are dead, the only non-redundant statements are emotes
  949. if (me->GetFriendsRemaining() == 0 && GetType() != REPORT_EMOTE && !me->HasHeavyArmor())
  950. return false;
  951. if (!m_isSpeaking)
  952. {
  953. m_isSpeaking = true;
  954. m_speakTimestamp = gpGlobals->curtime;
  955. }
  956. const int nMinEnemiesToNote = CSGameRules()->IsPlayingCoopMission() ? 1 : 3;
  957. // special case - context dependent delay
  958. if (m_index >= 0 && m_statement[ m_index ].context == ACCUMULATE_ENEMIES_DELAY)
  959. {
  960. // report if we see a lot of enemies, or if enough time has passed
  961. const float reportTime = 2.0f; // 1
  962. if ( me->GetNearbyEnemyCount() > nMinEnemiesToNote || gpGlobals->curtime - m_speakTimestamp > reportTime )
  963. {
  964. // enough enemies have accumulated to expire this delay
  965. m_nextTime = 0.0f;
  966. }
  967. }
  968. if (gpGlobals->curtime > m_nextTime)
  969. {
  970. // check for end of statement
  971. if (++m_index == m_count)
  972. {
  973. // transmit any memes carried in this statement to our teammates
  974. if (m_meme)
  975. m_meme->Transmit( me );
  976. return false;
  977. }
  978. // start next part of statement
  979. float duration = 0.0f;
  980. const BotPhrase *phrase = NULL;
  981. if (m_statement[ m_index ].isPhrase)
  982. {
  983. // normal phrase
  984. phrase = m_statement[ m_index ].phrase;
  985. }
  986. else
  987. {
  988. // context-dependant phrase
  989. switch( m_statement[ m_index ].context )
  990. {
  991. case CURRENT_ENEMY_COUNT:
  992. {
  993. if ( me->HasHeavyArmor() )
  994. break;
  995. int enemyCount = me->GetNearbyEnemyCount();
  996. const int nEnemySpottedThreshold = CSGameRules()->IsPlayingCoopMission() ? 0 : 1;
  997. // if we are outnumbered, ask for help
  998. if (enemyCount-1 > me->GetNearbyFriendCount())
  999. {
  1000. phrase = TheBotPhrases->GetPhrase( "Help" );
  1001. AttachMeme( new BotHelpMeme() );
  1002. }
  1003. else if ( enemyCount > nEnemySpottedThreshold )
  1004. {
  1005. phrase = TheBotPhrases->GetPhrase( "EnemySpotted" );
  1006. phrase->SetCountCriteria( enemyCount );
  1007. }
  1008. break;
  1009. }
  1010. case REMAINING_ENEMY_COUNT:
  1011. {
  1012. if ( me->HasHeavyArmor() )
  1013. break;
  1014. static const char *speak[] =
  1015. {
  1016. "NoEnemiesLeft", "OneEnemyLeft", "TwoEnemiesLeft", "ThreeEnemiesLeft"
  1017. };
  1018. int enemyCount = me->GetEnemiesRemaining();
  1019. // dont report if there are lots of enemies left
  1020. if (enemyCount < 0 || enemyCount > nMinEnemiesToNote )
  1021. {
  1022. phrase = NULL;
  1023. }
  1024. else
  1025. {
  1026. phrase = TheBotPhrases->GetPhrase( speak[ enemyCount ] );
  1027. }
  1028. break;
  1029. }
  1030. case SHORT_DELAY:
  1031. {
  1032. m_nextTime = gpGlobals->curtime + RandomFloat( 0.1f, 0.5f );
  1033. return true;
  1034. }
  1035. case LONG_DELAY:
  1036. {
  1037. m_nextTime = gpGlobals->curtime + RandomFloat( 1.0f, 2.0f );
  1038. return true;
  1039. }
  1040. case ACCUMULATE_ENEMIES_DELAY:
  1041. {
  1042. // wait until test becomes true
  1043. m_nextTime = 99999999.9f;
  1044. return true;
  1045. }
  1046. }
  1047. }
  1048. if (phrase)
  1049. {
  1050. // if chatter system is in "standard radio" mode, send the equivalent radio command
  1051. if (me->GetChatter()->GetVerbosity() == BotChatterInterface::RADIO)
  1052. {
  1053. RadioType radioEvent = phrase->GetRadioEquivalent();
  1054. if (radioEvent == RADIO_INVALID)
  1055. {
  1056. // skip directly to the next phrase
  1057. m_nextTime = 0.0f;
  1058. }
  1059. else
  1060. {
  1061. // use the standard radio
  1062. me->GetChatter()->ResetRadioSilenceDuration();
  1063. me->SendRadioMessage( radioEvent );
  1064. duration = 2.0f;
  1065. }
  1066. }
  1067. else
  1068. {
  1069. // set place criteria
  1070. phrase->SetPlaceCriteria( m_place );
  1071. bool sayIt = true;
  1072. if (phrase->IsPlace())
  1073. {
  1074. // don't repeat the place if someone just mentioned it not too long ago
  1075. float timeSince = TheBotPhrases->GetPlaceStatementInterval( phrase->GetPlace() );
  1076. const float minRepeatTime = 20.0f; // 30
  1077. if (timeSince < minRepeatTime)
  1078. {
  1079. sayIt = false;
  1080. }
  1081. else
  1082. {
  1083. TheBotPhrases->ResetPlaceStatementInterval( phrase->GetPlace() );
  1084. }
  1085. }
  1086. if (sayIt)
  1087. {
  1088. bool bSpoke = false;
  1089. if ( bot_chatter_use_rr.GetBool() )
  1090. {
  1091. // Pipe the bot chatter through the response rules system
  1092. int nPlace = phrase->IsPlace() ? phrase->GetPlace() : phrase->GetPlaceCriteria();
  1093. const char *place = TheNavMesh->PlaceToName( nPlace );
  1094. CountCriteria count = phrase->GetCountCriteria();
  1095. AI_CriteriaSet &botCriteria = phrase->GetCriteriaSet();
  1096. if ( count != UNDEFINED_COUNT )
  1097. {
  1098. botCriteria.AppendCriteria( "count", count );
  1099. }
  1100. if ( place )
  1101. {
  1102. botCriteria.AppendCriteria( "place", place );
  1103. }
  1104. if ( CSGameRules()->IsPlayingCoopMission() )
  1105. {
  1106. botCriteria.AppendCriteria( "gamemode", "coop" );
  1107. }
  1108. if ( me->HasHeavyArmor() )
  1109. {
  1110. botCriteria.AppendCriteria( "isheavy", 1 );
  1111. }
  1112. // TODO: Need voice pitch as a criteria?
  1113. // me->GetProfile()->GetVoicePitch()
  1114. bSpoke = me->SpeakAudioResponseRules( phrase->GetName(), &botCriteria, duration + 1.0f );
  1115. }
  1116. else
  1117. {
  1118. const char *filename = phrase->GetSpeakable( me->GetProfile()->GetVoiceBank(), &duration );
  1119. // CONSOLE_ECHO( "%s: Radio( '%s' )\n", STRING( me->pev->netname ), filename );
  1120. if ( filename )
  1121. {
  1122. me->SpeakAudio( filename, duration + 1.0f, me->GetProfile()->GetVoicePitch() );
  1123. bSpoke = true;
  1124. }
  1125. }
  1126. if ( !bSpoke )
  1127. {
  1128. RadioType radioEvent = phrase->GetRadioEquivalent();
  1129. if (radioEvent == RADIO_INVALID)
  1130. {
  1131. // skip directly to the next phrase
  1132. m_nextTime = 0.0f;
  1133. }
  1134. else
  1135. {
  1136. // use the standard radio
  1137. me->SendRadioMessage( radioEvent );
  1138. me->GetChatter()->ResetRadioSilenceDuration();
  1139. duration = 2.0f;
  1140. }
  1141. }
  1142. /* BOTPORT: Wire up bot voice over IP
  1143. else if ( g_engfuncs.pfnPlayClientVoice && TheBotPhrases->GetOutputType( me->GetProfile()->GetVoiceBank() ) == BOT_CHATTER_VOICE )
  1144. {
  1145. me->GetChatter()->ResetRadioSilenceDuration();
  1146. g_engfuncs.pfnPlayClientVoice( me->entindex() - 1, filename );
  1147. }
  1148. */
  1149. }
  1150. }
  1151. const float gap = 0.1f;
  1152. m_nextTime = gpGlobals->curtime + duration + gap;
  1153. }
  1154. else
  1155. {
  1156. // skip directly to the next phrase
  1157. m_nextTime = 0.0f;
  1158. }
  1159. }
  1160. return true;
  1161. }
  1162. /**
  1163. * If this statement refers to a specific place, return that place
  1164. * Places can be implicit in the statement, or explicitly defined
  1165. */
  1166. unsigned int BotStatement::GetPlace( void ) const
  1167. {
  1168. // return any explicitly set place if we have one
  1169. if (m_place != UNDEFINED_PLACE)
  1170. return m_place;
  1171. // look for an implicit place in our statement
  1172. for( int i=0; i<m_count; ++i )
  1173. if (m_statement[i].isPhrase && m_statement[i].phrase->IsPlace())
  1174. return m_statement[i].phrase->GetPlace();
  1175. return 0;
  1176. }
  1177. /**
  1178. * Return true if this statement has an associated count
  1179. */
  1180. bool BotStatement::HasCount( void ) const
  1181. {
  1182. for( int i=0; i<m_count; ++i )
  1183. if (!m_statement[i].isPhrase && m_statement[i].context == CURRENT_ENEMY_COUNT)
  1184. return true;
  1185. return false;
  1186. }
  1187. //---------------------------------------------------------------------------------------------------------------
  1188. //---------------------------------------------------------------------------------------------------------------
  1189. CountdownTimer BotChatterInterface::m_encourageTimer;
  1190. IntervalTimer BotChatterInterface::m_radioSilenceInterval[ 2 ];
  1191. enum PitchHack
  1192. {
  1193. P_HI,
  1194. P_NORMAL,
  1195. P_LOW
  1196. };
  1197. static int nextPitch = P_HI;
  1198. BotChatterInterface::BotChatterInterface( CCSBot *me )
  1199. {
  1200. m_me = me;
  1201. m_statementList = NULL;
  1202. switch( nextPitch )
  1203. {
  1204. case P_HI:
  1205. m_pitch = RandomInt( 105, 110 );
  1206. break;
  1207. case P_NORMAL:
  1208. m_pitch = RandomInt( 95, 105 );
  1209. break;
  1210. case P_LOW:
  1211. m_pitch = RandomInt( 85, 95 );
  1212. break;
  1213. }
  1214. nextPitch = (nextPitch + 1) % 3;
  1215. Reset();
  1216. }
  1217. //---------------------------------------------------------------------------------------------------------------
  1218. BotChatterInterface::~BotChatterInterface()
  1219. {
  1220. // free pending statements
  1221. BotStatement *next;
  1222. for( BotStatement *msg = m_statementList; msg; msg = next )
  1223. {
  1224. next = msg->m_next;
  1225. delete msg;
  1226. }
  1227. }
  1228. //---------------------------------------------------------------------------------------------------------------
  1229. /**
  1230. * Reset to initial state
  1231. */
  1232. void BotChatterInterface::Reset( void )
  1233. {
  1234. BotStatement *msg, *nextMsg;
  1235. // removing pending statements - except for those about the round results
  1236. for( msg = m_statementList; msg; msg = nextMsg )
  1237. {
  1238. nextMsg = msg->m_next;
  1239. if (msg->GetType() != REPORT_ROUND_END)
  1240. RemoveStatement( msg );
  1241. }
  1242. m_seeAtLeastOneEnemy = false;
  1243. m_timeWhenSawFirstEnemy = 0.0f;
  1244. m_reportedEnemies = false;
  1245. m_requestedBombLocation = false;
  1246. ResetRadioSilenceDuration();
  1247. m_needBackupInterval.Invalidate();
  1248. m_spottedBomberInterval.Invalidate();
  1249. m_spottedLooseBombTimer.Invalidate();
  1250. m_heardNoiseTimer.Invalidate();
  1251. m_scaredInterval.Invalidate();
  1252. m_planInterval.Invalidate();
  1253. m_encourageTimer.Invalidate();
  1254. m_escortingHostageTimer.Invalidate();
  1255. m_warnSniperTimer.Invalidate();
  1256. m_heavyTauntTimer.Invalidate();
  1257. }
  1258. //---------------------------------------------------------------------------------------------------------------
  1259. /**
  1260. * Register a statement for speaking
  1261. */
  1262. void BotChatterInterface::AddStatement( BotStatement *statement, bool mustAdd )
  1263. {
  1264. // don't add statements if bot chatter is shut off
  1265. if (GetVerbosity() == OFF)
  1266. {
  1267. delete statement;
  1268. return;
  1269. }
  1270. // if we only want mission-critical radio chatter, ignore non-important phrases
  1271. if (GetVerbosity() == MINIMAL && !statement->IsImportant())
  1272. {
  1273. delete statement;
  1274. return;
  1275. }
  1276. // don't add statements if we're dead
  1277. if (!m_me->IsAlive() && !mustAdd)
  1278. {
  1279. delete statement;
  1280. return;
  1281. }
  1282. // don't add empty statements
  1283. if (statement->m_count == 0)
  1284. {
  1285. delete statement;
  1286. return;
  1287. }
  1288. // don't add statements that are redundant with something we're already waiting to say
  1289. BotStatement *s;
  1290. for( s=m_statementList; s; s = s->m_next )
  1291. {
  1292. if (statement->IsRedundant( s ))
  1293. {
  1294. m_me->PrintIfWatched( "I tried to say something I'm already saying.\n" );
  1295. delete statement;
  1296. return;
  1297. }
  1298. }
  1299. // keep statements in order of start time
  1300. // check list is empty
  1301. if (m_statementList == NULL)
  1302. {
  1303. statement->m_next = NULL;
  1304. statement->m_prev = NULL;
  1305. m_statementList = statement;
  1306. return;
  1307. }
  1308. // list has at least one statement on it
  1309. // insert into list in order
  1310. BotStatement *earlier = NULL;
  1311. for( s=m_statementList; s; s = s->m_next )
  1312. {
  1313. if (s->GetStartTime() > statement->GetStartTime())
  1314. break;
  1315. earlier = s;
  1316. }
  1317. // insert just after "earlier"
  1318. if (earlier)
  1319. {
  1320. if (earlier->m_next)
  1321. earlier->m_next->m_prev = statement;
  1322. statement->m_next = earlier->m_next;
  1323. earlier->m_next = statement;
  1324. statement->m_prev = earlier;
  1325. }
  1326. else
  1327. {
  1328. // insert at head
  1329. statement->m_prev = NULL;
  1330. statement->m_next = m_statementList;
  1331. m_statementList->m_prev = statement;
  1332. m_statementList = statement;
  1333. }
  1334. }
  1335. //---------------------------------------------------------------------------------------------------------------
  1336. /**
  1337. * Remove a statement
  1338. */
  1339. void BotChatterInterface::RemoveStatement( BotStatement *statement )
  1340. {
  1341. if (statement->m_next)
  1342. statement->m_next->m_prev = statement->m_prev;
  1343. if (statement->m_prev)
  1344. statement->m_prev->m_next = statement->m_next;
  1345. else
  1346. m_statementList = statement->m_next;
  1347. delete statement;
  1348. }
  1349. //---------------------------------------------------------------------------------------------------------------
  1350. /**
  1351. * Track nearby enemy count and report enemy activity
  1352. */
  1353. void BotChatterInterface::ReportEnemies( void )
  1354. {
  1355. if (!m_me->IsAlive())
  1356. return;
  1357. if (m_me->GetNearbyEnemyCount() == 0)
  1358. {
  1359. m_seeAtLeastOneEnemy = false;
  1360. m_reportedEnemies = false;
  1361. }
  1362. else if (!m_seeAtLeastOneEnemy)
  1363. {
  1364. m_seeAtLeastOneEnemy = true;
  1365. m_timeWhenSawFirstEnemy = gpGlobals->curtime;
  1366. }
  1367. // determine whether we should report enemy activity
  1368. if (!m_reportedEnemies && m_seeAtLeastOneEnemy)
  1369. {
  1370. // request backup if we're outnumbered
  1371. if (m_me->IsOutnumbered() && NeedBackup())
  1372. {
  1373. m_reportedEnemies = true;
  1374. return;
  1375. }
  1376. m_me->GetChatter()->EnemySpotted();
  1377. m_reportedEnemies = true;
  1378. }
  1379. }
  1380. //---------------------------------------------------------------------------------------------------------------
  1381. /**
  1382. * Invoked when we die
  1383. */
  1384. void BotChatterInterface::OnDeath( void )
  1385. {
  1386. if (IsTalking())
  1387. {
  1388. if (m_me->GetChatter()->GetVerbosity() == BotChatterInterface::MINIMAL ||
  1389. m_me->GetChatter()->GetVerbosity() == BotChatterInterface::NORMAL)
  1390. {
  1391. // we've died mid-sentance - emit a gargle of pain
  1392. const BotPhrase *pain = TheBotPhrases->GetPainPhrase();
  1393. if (pain)
  1394. {
  1395. /*
  1396. if ( g_engfuncs.pfnPlayClientVoice && TheBotPhrases->GetOutputType( m_me->GetProfile()->GetVoiceBank() ) == BOT_CHATTER_VOICE )
  1397. {
  1398. g_engfuncs.pfnPlayClientVoice( m_me->entindex() - 1, pain->GetSpeakable(m_me->GetProfile()->GetVoiceBank()) );
  1399. m_me->GetChatter()->ResetRadioSilenceDuration();
  1400. }
  1401. else
  1402. */
  1403. {
  1404. if ( bot_chatter_use_rr.GetBool() )
  1405. {
  1406. AI_CriteriaSet botCriteria;
  1407. if ( CSGameRules()->IsPlayingCoopMission() )
  1408. {
  1409. botCriteria.AppendCriteria( "gamemode", "coop" );
  1410. }
  1411. if ( m_me->HasHeavyArmor() )
  1412. {
  1413. botCriteria.AppendCriteria( "isheavy", 1 );
  1414. }
  1415. m_me->SpeakAudioResponseRules( "DeathCry", &botCriteria, 0.0f );
  1416. }
  1417. else
  1418. {
  1419. m_me->SpeakAudio( pain->GetSpeakable( m_me->GetProfile()->GetVoiceBank() ), 0.0f, m_me->GetProfile()->GetVoicePitch() );
  1420. }
  1421. }
  1422. }
  1423. }
  1424. }
  1425. // remove all of our statements
  1426. Reset();
  1427. }
  1428. //---------------------------------------------------------------------------------------------------------------
  1429. /**
  1430. * Process ongoing chatter for this bot
  1431. */
  1432. void BotChatterInterface::Update( void )
  1433. {
  1434. // report enemy activity
  1435. ReportEnemies();
  1436. // ask team to report in if we havent heard anything in awhile
  1437. if (ShouldSpeak())
  1438. {
  1439. const float longTime = 30.0f;
  1440. if (m_me->GetEnemiesRemaining() > 0 && GetRadioSilenceDuration() > longTime)
  1441. {
  1442. ReportIn();
  1443. }
  1444. }
  1445. // speak if it is our turn
  1446. BotStatement *say = GetActiveStatement();
  1447. if (say)
  1448. {
  1449. // if our statement is active, speak it
  1450. if (say->GetOwner() == m_me)
  1451. {
  1452. if (say->Update() == false)
  1453. {
  1454. // this statement is complete - destroy it
  1455. RemoveStatement( say );
  1456. }
  1457. }
  1458. }
  1459. //
  1460. // Process active statements.
  1461. // Removed expired statements, re-order statements according to their relavence and importance
  1462. // Remove redundant statements (ie: our teammates already said them)
  1463. //
  1464. const BotStatement *friendSay = GetActiveStatement();
  1465. if (friendSay && friendSay->GetOwner() == m_me)
  1466. friendSay = NULL;
  1467. BotStatement *nextSay;
  1468. for( say = m_statementList; say; say = nextSay )
  1469. {
  1470. nextSay = say->m_next;
  1471. // check statement conditions
  1472. if (!say->IsValid())
  1473. {
  1474. RemoveStatement( say );
  1475. continue;
  1476. }
  1477. // don't interrupt ourselves
  1478. if (say->IsSpeaking())
  1479. continue;
  1480. // check for obsolete statements
  1481. if (say->IsObsolete())
  1482. {
  1483. m_me->PrintIfWatched( "Statement obsolete - removing.\n" );
  1484. RemoveStatement( say );
  1485. continue;
  1486. }
  1487. // if a teammate is saying what we were going to say, dont repeat it
  1488. if (friendSay)
  1489. {
  1490. // convert what we're about to say based on what our teammate is currently saying
  1491. say->Convert( friendSay );
  1492. // don't say things our teammates have just said
  1493. if (say->IsRedundant( friendSay ))
  1494. {
  1495. // thie statement is redundant - destroy it
  1496. //m_me->PrintIfWatched( "Teammate said what I was going to say - shutting up.\n" );
  1497. m_me->PrintIfWatched( "Teammate said what I was going to say - shutting up.\n" );
  1498. RemoveStatement( say );
  1499. }
  1500. }
  1501. }
  1502. }
  1503. //---------------------------------------------------------------------------------------------------------------
  1504. /**
  1505. * Returns the statement that is being spoken, or is next to be spoken if no-one is speaking now
  1506. */
  1507. BotStatement *BotChatterInterface::GetActiveStatement( void )
  1508. {
  1509. // keep track of statement waiting longest to be spoken - it is next
  1510. BotStatement *earliest = NULL;
  1511. float earlyTime = 999999999.9f;
  1512. for( int i = 1; i <= gpGlobals->maxClients; i++ )
  1513. {
  1514. CCSPlayer *player = static_cast<CCSPlayer *>( UTIL_PlayerByIndex( i ) );
  1515. if (player == NULL)
  1516. continue;
  1517. // ignore dead humans
  1518. if (!player->IsBot() && !player->IsAlive())
  1519. continue;
  1520. // ignore enemies, since we can't hear them talk
  1521. if (player->IsOtherEnemy( m_me->entindex() ))
  1522. continue;
  1523. CCSBot *bot = dynamic_cast<CCSBot *>(player);
  1524. // if not a bot, fail the test
  1525. /// @todo Check if human is currently talking
  1526. if (!bot)
  1527. continue;
  1528. for( BotStatement *say = bot->GetChatter()->m_statementList; say; say = say->m_next )
  1529. {
  1530. // if this statement is currently being spoken, return it
  1531. if (say->IsSpeaking())
  1532. return say;
  1533. // keep track of statement that has been waiting longest to be spoken of anyone on our team
  1534. if (say->GetStartTime() < earlyTime)
  1535. {
  1536. earlyTime = say->GetTimestamp();
  1537. earliest = say;
  1538. }
  1539. }
  1540. }
  1541. // make sure it is time to start this statement
  1542. if (earliest && earliest->GetStartTime() > gpGlobals->curtime)
  1543. return NULL;
  1544. return earliest;
  1545. }
  1546. /**
  1547. * Return true if we speaking makes sense now
  1548. */
  1549. bool BotChatterInterface::ShouldSpeak( void ) const
  1550. {
  1551. // don't talk to non-existent friends
  1552. if (m_me->GetFriendsRemaining() == 0)
  1553. return false;
  1554. // if everyone is together, no need to tell them what's going on
  1555. if (m_me->GetNearbyFriendCount() == m_me->GetFriendsRemaining())
  1556. return false;
  1557. return true;
  1558. }
  1559. //---------------------------------------------------------------------------------------------------------------
  1560. float BotChatterInterface::GetRadioSilenceDuration( void )
  1561. {
  1562. return m_radioSilenceInterval[ m_me->GetTeamNumber() % 2 ].GetElapsedTime();
  1563. }
  1564. //---------------------------------------------------------------------------------------------------------------
  1565. void BotChatterInterface::ResetRadioSilenceDuration( void )
  1566. {
  1567. m_radioSilenceInterval[ m_me->GetTeamNumber() % 2 ].Reset();
  1568. }
  1569. //---------------------------------------------------------------------------------------------------------------
  1570. inline const BotPhrase *GetPlacePhrase( CCSBot *me )
  1571. {
  1572. Place place = me->GetPlace();
  1573. if (place != UNDEFINED_PLACE)
  1574. return TheBotPhrases->GetPlace( place );
  1575. return NULL;
  1576. }
  1577. inline void SayWhere( BotStatement *say, Place place )
  1578. {
  1579. say->AppendPhrase( TheBotPhrases->GetPlace( place ) );
  1580. }
  1581. /**
  1582. * Report enemy sightings
  1583. */
  1584. void BotChatterInterface::EnemySpotted( void )
  1585. {
  1586. // NOTE: This could be a few seconds out of date (enemy is in an adjacent place)
  1587. Place place = m_me->GetEnemyPlace();
  1588. BotStatement *say = new BotStatement( this, REPORT_VISIBLE_ENEMIES, 10.0f );
  1589. // where are the enemies
  1590. say->AppendPhrase( TheBotPhrases->GetPlace( place ) );
  1591. // how many are there
  1592. say->AppendPhrase( BotStatement::ACCUMULATE_ENEMIES_DELAY );
  1593. say->AppendPhrase( BotStatement::CURRENT_ENEMY_COUNT );
  1594. say->AddCondition( BotStatement::IS_IN_COMBAT );
  1595. AddStatement( say );
  1596. }
  1597. //---------------------------------------------------------------------------------------------------------------
  1598. /**
  1599. * If a friend warned of snipers, don't warn again for awhile
  1600. */
  1601. void BotChatterInterface::FriendSpottedSniper( void )
  1602. {
  1603. m_warnSniperTimer.Start( 60.0f );
  1604. }
  1605. //---------------------------------------------------------------------------------------------------------------
  1606. /**
  1607. * Warn of an enemy sniper
  1608. */
  1609. void BotChatterInterface::SpottedSniper( void )
  1610. {
  1611. if (!m_warnSniperTimer.IsElapsed())
  1612. {
  1613. return;
  1614. }
  1615. if (m_me->GetFriendsRemaining() == 0)
  1616. {
  1617. // no-one to warn
  1618. return;
  1619. }
  1620. BotStatement *say = new BotStatement( this, REPORT_INFORMATION, 10.0f );
  1621. say->AppendPhrase( TheBotPhrases->GetPhrase( "SniperWarning" ) );
  1622. say->AttachMeme( new BotWarnSniperMeme() );
  1623. AddStatement( say );
  1624. }
  1625. //---------------------------------------------------------------------------------------------------------------
  1626. void BotChatterInterface::Clear( Place place )
  1627. {
  1628. BotStatement *say = new BotStatement( this, REPORT_INFORMATION, 10.0f );
  1629. SayWhere( say, place );
  1630. say->AppendPhrase( TheBotPhrases->GetPhrase( "Clear" ) );
  1631. AddStatement( say );
  1632. }
  1633. //---------------------------------------------------------------------------------------------------------------
  1634. /**
  1635. * Request enemy activity report
  1636. */
  1637. void BotChatterInterface::ReportIn( void )
  1638. {
  1639. BotStatement *say = new BotStatement( this, REPORT_REQUEST_INFORMATION, 10.0f );
  1640. say->AppendPhrase( TheBotPhrases->GetPhrase( "RequestReport" ) );
  1641. say->AddCondition( BotStatement::RADIO_SILENCE );
  1642. say->AttachMeme( new BotRequestReportMeme() );
  1643. AddStatement( say );
  1644. }
  1645. //---------------------------------------------------------------------------------------------------------------
  1646. /**
  1647. * Report our situtation
  1648. */
  1649. void BotChatterInterface::ReportingIn( void )
  1650. {
  1651. BotStatement *say = new BotStatement( this, REPORT_INFORMATION, 10.0f );
  1652. // where are we
  1653. Place place = m_me->GetPlace();
  1654. SayWhere( say, place );
  1655. // what are we doing
  1656. switch( m_me->GetTask() )
  1657. {
  1658. case CCSBot::PLANT_BOMB:
  1659. {
  1660. m_me->GetChatter()->GoingToPlantTheBomb( UNDEFINED_PLACE );
  1661. break;
  1662. }
  1663. case CCSBot::DEFUSE_BOMB:
  1664. {
  1665. m_me->GetChatter()->Say( "DefusingBomb" );
  1666. break;
  1667. }
  1668. case CCSBot::GUARD_LOOSE_BOMB:
  1669. {
  1670. if (TheCSBots()->GetLooseBomb())
  1671. {
  1672. say->AppendPhrase( TheBotPhrases->GetPhrase( "GuardingLooseBomb" ) );
  1673. say->AttachMeme( new BotBombStatusMeme( CSGameState::LOOSE, TheCSBots()->GetLooseBomb()->GetAbsOrigin() ) );
  1674. }
  1675. break;
  1676. }
  1677. case CCSBot::GUARD_HOSTAGES:
  1678. {
  1679. m_me->GetChatter()->GuardingHostages( UNDEFINED_PLACE, !m_me->IsAtHidingSpot() );
  1680. break;
  1681. }
  1682. case CCSBot::GUARD_HOSTAGE_RESCUE_ZONE:
  1683. {
  1684. m_me->GetChatter()->GuardingHostageEscapeZone( !m_me->IsAtHidingSpot() );
  1685. break;
  1686. }
  1687. case CCSBot::COLLECT_HOSTAGES:
  1688. {
  1689. break;
  1690. }
  1691. case CCSBot::RESCUE_HOSTAGES:
  1692. {
  1693. m_me->GetChatter()->EscortingHostages();
  1694. break;
  1695. }
  1696. case CCSBot::GUARD_VIP_ESCAPE_ZONE:
  1697. {
  1698. break;
  1699. }
  1700. }
  1701. // what do we see
  1702. if (m_me->IsAttacking())
  1703. {
  1704. if (m_me->IsOutnumbered())
  1705. {
  1706. // in trouble in a firefight
  1707. say->AppendPhrase( TheBotPhrases->GetPhrase( "Help" ) );
  1708. say->AttachMeme( new BotHelpMeme( place ) );
  1709. }
  1710. else
  1711. {
  1712. // battling enemies
  1713. say->AppendPhrase( TheBotPhrases->GetPhrase( "InCombat" ) );
  1714. }
  1715. }
  1716. else
  1717. {
  1718. // not in combat, start our report a little later
  1719. say->SetStartTime( gpGlobals->curtime + 2.0f );
  1720. const float recentTime = 10.0f;
  1721. if (m_me->GetEnemyDeathTimestamp() < recentTime && m_me->GetEnemyDeathTimestamp() >= m_me->GetTimeSinceLastSawEnemy() + 0.5f)
  1722. {
  1723. // recently saw an enemy die
  1724. say->AppendPhrase( TheBotPhrases->GetPhrase( "EnemyDown" ) );
  1725. }
  1726. else if (m_me->GetTimeSinceLastSawEnemy() < recentTime)
  1727. {
  1728. // recently saw an enemy
  1729. say->AppendPhrase( TheBotPhrases->GetPhrase( "EnemySpotted" ) );
  1730. }
  1731. else
  1732. {
  1733. // haven't seen enemies
  1734. say->AppendPhrase( TheBotPhrases->GetPhrase( "Clear" ) );
  1735. }
  1736. }
  1737. AddStatement( say );
  1738. }
  1739. //---------------------------------------------------------------------------------------------------------------
  1740. bool BotChatterInterface::NeedBackup( void )
  1741. {
  1742. const float minRequestInterval = 10.0f;
  1743. if (m_needBackupInterval.IsLessThen( minRequestInterval ))
  1744. return false;
  1745. if ( m_me->HasHeavyArmor() )
  1746. return false;
  1747. m_needBackupInterval.Reset();
  1748. if (m_me->GetFriendsRemaining() == 0)
  1749. {
  1750. // we're all alone...
  1751. Scared();
  1752. return true;
  1753. }
  1754. else
  1755. {
  1756. // ask friends for help
  1757. BotStatement *say = new BotStatement( this, REPORT_REQUEST_HELP, 10.0f );
  1758. // where are we
  1759. Place place = m_me->GetPlace();
  1760. SayWhere( say, place );
  1761. say->AppendPhrase( TheBotPhrases->GetPhrase( "Help" ) );
  1762. say->AttachMeme( new BotHelpMeme( place ) );
  1763. AddStatement( say );
  1764. }
  1765. return true;
  1766. }
  1767. //---------------------------------------------------------------------------------------------------------------
  1768. void BotChatterInterface::PinnedDown( void )
  1769. {
  1770. // this is a form of "need backup"
  1771. const float minRequestInterval = 10.0f;
  1772. if (m_needBackupInterval.IsLessThen( minRequestInterval ))
  1773. return;
  1774. if ( m_me->HasHeavyArmor() )
  1775. return;
  1776. m_needBackupInterval.Reset();
  1777. BotStatement *say = new BotStatement( this, REPORT_REQUEST_HELP, 10.0f );
  1778. // where are we
  1779. Place place = m_me->GetPlace();
  1780. SayWhere( say, place );
  1781. say->AppendPhrase( TheBotPhrases->GetPhrase( "PinnedDown" ) );
  1782. say->AttachMeme( new BotHelpMeme( place ) );
  1783. say->AddCondition( BotStatement::IS_IN_COMBAT );
  1784. AddStatement( say );
  1785. }
  1786. //---------------------------------------------------------------------------------------------------------------
  1787. /**
  1788. * If a friend said that they heard something, we don't want to say something similar
  1789. * for a while.
  1790. */
  1791. void BotChatterInterface::FriendHeardNoise( void )
  1792. {
  1793. m_heardNoiseTimer.Start( 20.0f );
  1794. }
  1795. //---------------------------------------------------------------------------------------------------------------
  1796. void BotChatterInterface::HeardNoise( const Vector &pos )
  1797. {
  1798. if (TheCSBots()->IsRoundOver())
  1799. return;
  1800. if (m_heardNoiseTimer.IsElapsed())
  1801. {
  1802. // throttle frequency
  1803. m_heardNoiseTimer.Start( 20.0f );
  1804. // make rare, since many teammates may try to say this
  1805. if (RandomFloat( 0, 100 ) < 33)
  1806. {
  1807. BotStatement *say = new BotStatement( this, REPORT_INFORMATION, 5.0f );
  1808. say->AppendPhrase( TheBotPhrases->GetPhrase( "HeardNoise" ) );
  1809. say->SetPlace( TheNavMesh->GetPlace( pos ) );
  1810. say->AttachMeme( new BotHeardNoiseMeme() );
  1811. AddStatement( say );
  1812. }
  1813. }
  1814. }
  1815. //---------------------------------------------------------------------------------------------------------------
  1816. void BotChatterInterface::KilledMyEnemy( int victimID )
  1817. {
  1818. // only report if we killed the last enemy in the area
  1819. if (m_me->GetNearbyEnemyCount() <= 1)
  1820. return;
  1821. BotStatement *say = new BotStatement( this, REPORT_ENEMY_ACTION, 3.0f );
  1822. say->AppendPhrase( TheBotPhrases->GetPhrase( "KilledMyEnemy" ) );
  1823. say->SetSubject( victimID );
  1824. AddStatement( say );
  1825. }
  1826. //---------------------------------------------------------------------------------------------------------------
  1827. void BotChatterInterface::EnemiesRemaining( void )
  1828. {
  1829. // only report if we killed the last enemy in the area
  1830. if (m_me->GetNearbyEnemyCount() > 1)
  1831. return;
  1832. BotStatement *say = new BotStatement( this, REPORT_ENEMIES_REMAINING, 5.0f );
  1833. say->AppendPhrase( BotStatement::REMAINING_ENEMY_COUNT );
  1834. say->SetStartTime( gpGlobals->curtime + RandomFloat( 2.0f, 4.0f ) );
  1835. AddStatement( say );
  1836. }
  1837. //---------------------------------------------------------------------------------------------------------------
  1838. void BotChatterInterface::Affirmative( void )
  1839. {
  1840. BotStatement *say = new BotStatement( this, REPORT_ACKNOWLEDGE, 3.0f );
  1841. say->AppendPhrase( TheBotPhrases->GetPhrase( "Affirmative" ) );
  1842. AddStatement( say );
  1843. }
  1844. //---------------------------------------------------------------------------------------------------------------
  1845. void BotChatterInterface::Negative( void )
  1846. {
  1847. BotStatement *say = new BotStatement( this, REPORT_ACKNOWLEDGE, 3.0f );
  1848. say->AppendPhrase( TheBotPhrases->GetPhrase( "Negative" ) );
  1849. AddStatement( say );
  1850. }
  1851. //---------------------------------------------------------------------------------------------------------------
  1852. void BotChatterInterface::GoingToPlantTheBomb( Place place )
  1853. {
  1854. if (TheCSBots()->IsRoundOver())
  1855. return;
  1856. const float minInterval = 20.0f;
  1857. if (m_planInterval.IsLessThen( minInterval ))
  1858. return;
  1859. m_planInterval.Reset();
  1860. BotStatement *say = new BotStatement( this, REPORT_CRITICAL_EVENT, 10.0f );
  1861. say->AppendPhrase( TheBotPhrases->GetPhrase( "GoingToPlantBomb" ) );
  1862. say->SetPlace( place );
  1863. say->AttachMeme( new BotFollowMeme() );
  1864. AddStatement( say );
  1865. }
  1866. //---------------------------------------------------------------------------------------------------------------
  1867. void BotChatterInterface::PlantingTheBomb( Place place )
  1868. {
  1869. if (TheCSBots()->IsRoundOver())
  1870. return;
  1871. BotStatement *say = new BotStatement( this, REPORT_CRITICAL_EVENT, 10.0f );
  1872. say->AppendPhrase( TheBotPhrases->GetPhrase( "PlantingBomb" ) );
  1873. say->SetPlace( place );
  1874. Vector myOrigin = GetCentroid( m_me );
  1875. say->AttachMeme( new BotDefendHereMeme( myOrigin ) );
  1876. AddStatement( say );
  1877. }
  1878. //---------------------------------------------------------------------------------------------------------------
  1879. void BotChatterInterface::TheyPickedUpTheBomb( void )
  1880. {
  1881. if (TheCSBots()->IsRoundOver())
  1882. return;
  1883. // if we already know the bomb is not loose, this is old news
  1884. if (!m_me->GetGameState()->IsBombLoose())
  1885. return;
  1886. // update our gamestate - use our own position for now
  1887. const Vector &myOrigin = GetCentroid( m_me );
  1888. m_me->GetGameState()->UpdateBomber( myOrigin );
  1889. // tell our teammates
  1890. BotStatement *say = new BotStatement( this, REPORT_INFORMATION, 10.0f );
  1891. say->AppendPhrase( TheBotPhrases->GetPhrase( "TheyPickedUpTheBomb" ) );
  1892. say->AttachMeme( new BotBombStatusMeme( CSGameState::MOVING, myOrigin ) );
  1893. AddStatement( say );
  1894. }
  1895. //---------------------------------------------------------------------------------------------------------------
  1896. void BotChatterInterface::SpottedBomber( CBasePlayer *bomber )
  1897. {
  1898. const Vector &bomberOrigin = GetCentroid( bomber );
  1899. if (m_me->GetGameState()->IsBombMoving())
  1900. {
  1901. // if we knew where the bomber was, this is old news
  1902. const Vector *bomberPos = m_me->GetGameState()->GetBombPosition();
  1903. const float closeRangeSq = 1000.0f * 1000.0f;
  1904. if (bomberPos && (bomberOrigin - *bomberPos).LengthSqr() < closeRangeSq)
  1905. return;
  1906. }
  1907. // update our gamestate
  1908. m_me->GetGameState()->UpdateBomber( bomberOrigin );
  1909. // tell our teammates
  1910. BotStatement *say = new BotStatement( this, REPORT_INFORMATION, 10.0f );
  1911. // where is the bomber
  1912. Place place = TheNavMesh->GetPlace( bomberOrigin );
  1913. SayWhere( say, place );
  1914. say->AppendPhrase( TheBotPhrases->GetPhrase( "SpottedBomber" ) );
  1915. say->SetSubject( bomber->entindex() );
  1916. //say->AttachMeme( new BotHelpMeme( place ) );
  1917. say->AttachMeme( new BotBombStatusMeme( CSGameState::MOVING, bomberOrigin ) );
  1918. AddStatement( say );
  1919. }
  1920. //---------------------------------------------------------------------------------------------------------------
  1921. void BotChatterInterface::SpottedLooseBomb( CBaseEntity *bomb )
  1922. {
  1923. if (TheCSBots()->IsRoundOver())
  1924. return;
  1925. // if we already know the bomb is loose, this is old news
  1926. if (m_me->GetGameState()->IsBombLoose())
  1927. return;
  1928. // update our gamestate
  1929. m_me->GetGameState()->UpdateLooseBomb( bomb->GetAbsOrigin() );
  1930. if (m_spottedLooseBombTimer.IsElapsed())
  1931. {
  1932. // throttle frequency
  1933. m_spottedLooseBombTimer.Start( 10.0f );
  1934. // tell our teammates
  1935. BotStatement *say = new BotStatement( this, REPORT_INFORMATION, 10.0f );
  1936. // where is the bomb
  1937. Place place = TheNavMesh->GetPlace( bomb->GetAbsOrigin() );
  1938. SayWhere( say, place );
  1939. say->AppendPhrase( TheBotPhrases->GetPhrase( "SpottedLooseBomb" ) );
  1940. if (TheCSBots()->GetLooseBomb())
  1941. say->AttachMeme( new BotBombStatusMeme( CSGameState::LOOSE, bomb->GetAbsOrigin() ) );
  1942. AddStatement( say );
  1943. }
  1944. }
  1945. //---------------------------------------------------------------------------------------------------------------
  1946. void BotChatterInterface::GuardingLooseBomb( CBaseEntity *bomb )
  1947. {
  1948. // if we already know the bomb is loose, this is old news
  1949. // if (m_me->GetGameState()->IsBombLoose())
  1950. // return;
  1951. if (TheCSBots()->IsRoundOver() || !bomb)
  1952. return;
  1953. const float minInterval = 20.0f;
  1954. if (m_planInterval.IsLessThen( minInterval ))
  1955. return;
  1956. m_planInterval.Reset();
  1957. // update our gamestate
  1958. m_me->GetGameState()->UpdateLooseBomb( bomb->GetAbsOrigin() );
  1959. // tell our teammates
  1960. BotStatement *say = new BotStatement( this, REPORT_INFORMATION, 10.0f );
  1961. // where is the bomb
  1962. Place place = TheNavMesh->GetPlace( bomb->GetAbsOrigin() );
  1963. SayWhere( say, place );
  1964. say->AppendPhrase( TheBotPhrases->GetPhrase( "GuardingLooseBomb" ) );
  1965. if (TheCSBots()->GetLooseBomb())
  1966. say->AttachMeme( new BotBombStatusMeme( CSGameState::LOOSE, bomb->GetAbsOrigin() ) );
  1967. AddStatement( say );
  1968. }
  1969. //---------------------------------------------------------------------------------------------------------------
  1970. void BotChatterInterface::RequestBombLocation( void )
  1971. {
  1972. // only ask once per round
  1973. if (m_requestedBombLocation)
  1974. return;
  1975. m_requestedBombLocation = true;
  1976. // tell our teammates
  1977. BotStatement *say = new BotStatement( this, REPORT_REQUEST_INFORMATION, 10.0f );
  1978. say->AppendPhrase( TheBotPhrases->GetPhrase( "WhereIsTheBomb" ) );
  1979. say->AttachMeme( new BotWhereBombMeme() );
  1980. AddStatement( say );
  1981. }
  1982. //---------------------------------------------------------------------------------------------------------------
  1983. void BotChatterInterface::BombsiteClear( int zoneIndex )
  1984. {
  1985. const CCSBotManager::Zone *zone = TheCSBots()->GetZone( zoneIndex );
  1986. if (zone == NULL)
  1987. return;
  1988. BotStatement *say = new BotStatement( this, REPORT_INFORMATION, 10.0f );
  1989. SayWhere( say, TheNavMesh->GetPlace( zone->m_center ) );
  1990. say->AppendPhrase( TheBotPhrases->GetPhrase( "BombsiteClear" ) );
  1991. say->AttachMeme( new BotBombsiteStatusMeme( zoneIndex, BotBombsiteStatusMeme::CLEAR ) );
  1992. AddStatement( say );
  1993. }
  1994. //---------------------------------------------------------------------------------------------------------------
  1995. void BotChatterInterface::FoundPlantedBomb( int zoneIndex )
  1996. {
  1997. const CCSBotManager::Zone *zone = TheCSBots()->GetZone( zoneIndex );
  1998. if (zone == NULL)
  1999. return;
  2000. BotStatement *say = new BotStatement( this, REPORT_INFORMATION, 3.0f );
  2001. say->AppendPhrase( TheBotPhrases->GetPhrase( "PlantedBombPlace" ) );
  2002. say->SetPlace( TheNavMesh->GetPlace( zone->m_center ) );
  2003. say->AttachMeme( new BotBombsiteStatusMeme( zoneIndex, BotBombsiteStatusMeme::PLANTED ) );
  2004. AddStatement( say );
  2005. }
  2006. //---------------------------------------------------------------------------------------------------------------
  2007. void BotChatterInterface::Scared( void )
  2008. {
  2009. const float minInterval = 10.0f;
  2010. if (m_scaredInterval.IsLessThen( minInterval ))
  2011. return;
  2012. m_scaredInterval.Reset();
  2013. BotStatement *say = new BotStatement( this, REPORT_EMOTE, 1.0f );
  2014. say->AppendPhrase( TheBotPhrases->GetPhrase( "ScaredEmote" ) );
  2015. say->AddCondition( BotStatement::IS_IN_COMBAT );
  2016. AddStatement( say );
  2017. }
  2018. //---------------------------------------------------------------------------------------------------------------
  2019. void BotChatterInterface::CelebrateWin( void )
  2020. {
  2021. BotStatement *say = new BotStatement( this, REPORT_EMOTE, 15.0f );
  2022. // wait a bit before speaking
  2023. say->SetStartTime( gpGlobals->curtime + RandomFloat( 2.0f, 5.0f ) );
  2024. const float quickRound = 45.0f;
  2025. if (m_me->GetFriendsRemaining() == 0)
  2026. {
  2027. // we were the last man standing
  2028. if (TheCSBots()->GetElapsedRoundTime() < quickRound)
  2029. say->AppendPhrase( TheBotPhrases->GetPhrase( "WonRoundQuickly" ) );
  2030. else if (RandomFloat( 0.0f, 100.0f ) < 33.3f)
  2031. say->AppendPhrase( TheBotPhrases->GetPhrase( "LastManStanding" ) );
  2032. }
  2033. else
  2034. {
  2035. if (TheCSBots()->GetElapsedRoundTime() < quickRound)
  2036. {
  2037. if (RandomFloat( 0.0f, 100.0f ) < 33.3f)
  2038. say->AppendPhrase( TheBotPhrases->GetPhrase( "WonRoundQuickly" ) );
  2039. }
  2040. else if (RandomFloat( 0.0f, 100.0f ) < 10.0f)
  2041. {
  2042. say->AppendPhrase( TheBotPhrases->GetPhrase( "WonRound" ) );
  2043. }
  2044. }
  2045. AddStatement( say );
  2046. }
  2047. //---------------------------------------------------------------------------------------------------------------
  2048. void BotChatterInterface::AnnouncePlan( const char *phraseName, Place place )
  2049. {
  2050. if (TheCSBots()->IsRoundOver())
  2051. return;
  2052. BotStatement *say = new BotStatement( this, REPORT_MY_PLAN, 10.0f );
  2053. say->AppendPhrase( TheBotPhrases->GetPhrase( phraseName ) );
  2054. say->SetPlace( place );
  2055. // wait at least a short time after round start
  2056. say->SetStartTime( TheCSBots()->GetRoundStartTime() + RandomFloat( 2.0, 3.0f ) );
  2057. AddStatement( say );
  2058. }
  2059. //---------------------------------------------------------------------------------------------------------------
  2060. void BotChatterInterface::GuardingBombsite( Place place )
  2061. {
  2062. if (TheCSBots()->IsRoundOver())
  2063. return;
  2064. const float minInterval = 20.0f;
  2065. if (m_planInterval.IsLessThen( minInterval ))
  2066. return;
  2067. m_planInterval.Reset();
  2068. AnnouncePlan( "GoingToDefendBombsite", place );
  2069. }
  2070. //---------------------------------------------------------------------------------------------------------------
  2071. void BotChatterInterface::GuardingHostages( Place place, bool isPlan )
  2072. {
  2073. if (TheCSBots()->IsRoundOver())
  2074. return;
  2075. const float minInterval = 20.0f;
  2076. if (m_planInterval.IsLessThen( minInterval ))
  2077. return;
  2078. m_planInterval.Reset();
  2079. if (isPlan)
  2080. AnnouncePlan( "GoingToGuardHostages", place );
  2081. else
  2082. Say( "GuardingHostages" );
  2083. }
  2084. //---------------------------------------------------------------------------------------------------------------
  2085. void BotChatterInterface::GuardingHostageEscapeZone( bool isPlan )
  2086. {
  2087. if (TheCSBots()->IsRoundOver())
  2088. return;
  2089. const float minInterval = 20.0f;
  2090. if (m_planInterval.IsLessThen( minInterval ))
  2091. return;
  2092. m_planInterval.Reset();
  2093. if (isPlan)
  2094. AnnouncePlan( "GoingToGuardHostageEscapeZone", UNDEFINED_PLACE );
  2095. else
  2096. Say( "GuardingHostageEscapeZone" );
  2097. }
  2098. //---------------------------------------------------------------------------------------------------------------
  2099. void BotChatterInterface::HostagesBeingTaken( void )
  2100. {
  2101. if (TheCSBots()->IsRoundOver())
  2102. return;
  2103. BotStatement *say = new BotStatement( this, REPORT_INFORMATION, 3.0f );
  2104. say->AppendPhrase( TheBotPhrases->GetPhrase( "HostagesBeingTaken" ) );
  2105. say->AttachMeme( new BotHostageBeingTakenMeme() );
  2106. AddStatement( say );
  2107. }
  2108. //---------------------------------------------------------------------------------------------------------------
  2109. void BotChatterInterface::HostagesTaken( void )
  2110. {
  2111. if (TheCSBots()->IsRoundOver())
  2112. return;
  2113. BotStatement *say = new BotStatement( this, REPORT_INFORMATION, 3.0f );
  2114. say->AppendPhrase( TheBotPhrases->GetPhrase( "HostagesTaken" ) );
  2115. AddStatement( say );
  2116. }
  2117. //---------------------------------------------------------------------------------------------------------------
  2118. void BotChatterInterface::TalkingToHostages( void )
  2119. {
  2120. }
  2121. //---------------------------------------------------------------------------------------------------------------
  2122. void BotChatterInterface::EscortingHostages( void )
  2123. {
  2124. if (TheCSBots()->IsRoundOver())
  2125. return;
  2126. if (m_escortingHostageTimer.IsElapsed())
  2127. {
  2128. // throttle frequency
  2129. m_escortingHostageTimer.Start( 10.0f );
  2130. BotStatement *say = new BotStatement( this, REPORT_MY_PLAN, 5.0f );
  2131. say->AppendPhrase( TheBotPhrases->GetPhrase( "EscortingHostages" ) );
  2132. AddStatement( say );
  2133. }
  2134. }
  2135. //---------------------------------------------------------------------------------------------------------------
  2136. void BotChatterInterface::HostageDown( void )
  2137. {
  2138. if (TheCSBots()->IsRoundOver())
  2139. return;
  2140. BotStatement *say = new BotStatement( this, REPORT_INFORMATION, 3.0f );
  2141. say->AppendPhrase( TheBotPhrases->GetPhrase( "HostageDown" ) );
  2142. AddStatement( say );
  2143. }
  2144. //---------------------------------------------------------------------------------------------------------------
  2145. void BotChatterInterface::Encourage( const char *phraseName, float repeatInterval, float lifetime )
  2146. {
  2147. if (m_encourageTimer.IsElapsed())
  2148. {
  2149. Say( phraseName, lifetime );
  2150. m_encourageTimer.Start( repeatInterval );
  2151. }
  2152. }
  2153. //---------------------------------------------------------------------------------------------------------------
  2154. void BotChatterInterface::KilledFriend( void )
  2155. {
  2156. BotStatement *say = new BotStatement( this, REPORT_KILLED_FRIEND, 2.0f );
  2157. say->AppendPhrase( TheBotPhrases->GetPhrase( "KilledFriend" ) );
  2158. // give them time to react
  2159. say->SetStartTime( gpGlobals->curtime + RandomFloat( 0.5f, 1.0f ) );
  2160. AddStatement( say );
  2161. }
  2162. //---------------------------------------------------------------------------------------------------------------
  2163. void BotChatterInterface::FriendlyFire( const char *pDmgType )
  2164. {
  2165. if ( !mp_friendlyfire.GetBool() )
  2166. return;
  2167. BotStatement *say = new BotStatement( this, REPORT_FRIENDLY_FIRE, 1.0f );
  2168. AI_CriteriaSet botCriteria;
  2169. if ( pDmgType )
  2170. {
  2171. botCriteria.AppendCriteria( "damagetype", pDmgType );
  2172. }
  2173. const BotPhrase *pPhrase = TheBotPhrases->GetPhrase( "FriendlyFire" );
  2174. pPhrase->SetCriteriaSet( botCriteria );
  2175. say->AppendPhrase( pPhrase );
  2176. // give them time to react
  2177. say->SetStartTime( gpGlobals->curtime + RandomFloat( 0.3f, 0.5f ) );
  2178. AddStatement( say );
  2179. }
  2180. //---------------------------------------------------------------------------------------------------------------
  2181. void BotChatterInterface::DoPhoenixHeavyWakeTaunt( void )
  2182. {
  2183. if ( m_heavyTauntTimer.IsElapsed() )
  2184. {
  2185. m_heavyTauntTimer.Start( RandomInt( 9, 15 ) );
  2186. Say( "HeavyWakeTaunt" );
  2187. }
  2188. }