Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

346 lines
7.8 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. // Author: Michael S. Booth ([email protected]), 2003
  8. #include "cbase.h"
  9. #include "cs_bot.h"
  10. // memdbgon must be the last include file in a .cpp file!!!
  11. #include "tier0/memdbgon.h"
  12. extern int gmsgBotVoice;
  13. //--------------------------------------------------------------------------------------------------------------
  14. /**
  15. * Returns true if the radio message is an order to do something
  16. * NOTE: "Report in" is not considered a "command" because it doesnt ask the bot to go somewhere, or change its mind
  17. */
  18. bool CCSBot::IsRadioCommand( RadioType event ) const
  19. {
  20. if (event == RADIO_AFFIRMATIVE ||
  21. event == RADIO_NEGATIVE ||
  22. event == RADIO_ENEMY_SPOTTED ||
  23. event == RADIO_SECTOR_CLEAR ||
  24. event == RADIO_REPORTING_IN ||
  25. event == RADIO_REPORT_IN_TEAM ||
  26. event == RADIO_ENEMY_DOWN ||
  27. event == RADIO_INVALID )
  28. return false;
  29. return true;
  30. }
  31. //--------------------------------------------------------------------------------------------------------------
  32. /**
  33. * Respond to radio commands from HUMAN players
  34. */
  35. void CCSBot::RespondToRadioCommands( void )
  36. {
  37. // bots use the chatter system to respond to each other
  38. if (m_radioSubject != NULL && m_radioSubject->IsPlayer())
  39. {
  40. CCSPlayer *player = m_radioSubject;
  41. if (player->IsBot())
  42. {
  43. m_lastRadioCommand = RADIO_INVALID;
  44. return;
  45. }
  46. }
  47. if (m_lastRadioCommand == RADIO_INVALID)
  48. return;
  49. // a human player has issued a radio command
  50. GetChatter()->ResetRadioSilenceDuration();
  51. // if we are doing something important, ignore the radio
  52. // unless it is a "report in" request - we can do that while we continue to do other things
  53. /// @todo Create "uninterruptable" flag
  54. if (m_lastRadioCommand != RADIO_REPORT_IN_TEAM)
  55. {
  56. if (IsBusy())
  57. {
  58. // consume command
  59. m_lastRadioCommand = RADIO_INVALID;
  60. return;
  61. }
  62. }
  63. // wait for reaction time before responding
  64. // delay needs to be long enough for the radio message we're responding to to finish
  65. float respondTime = 1.0f + 2.0f * GetProfile()->GetReactionTime();
  66. if (IsRogue())
  67. respondTime += 2.0f;
  68. if (gpGlobals->curtime - m_lastRadioRecievedTimestamp < respondTime)
  69. return;
  70. // rogues won't follow commands, unless already following the player
  71. if (!IsFollowing() && IsRogue())
  72. {
  73. if (IsRadioCommand( m_lastRadioCommand ))
  74. {
  75. GetChatter()->Negative();
  76. }
  77. // consume command
  78. m_lastRadioCommand = RADIO_INVALID;
  79. return;
  80. }
  81. CCSPlayer *player = m_radioSubject;
  82. if (player == NULL)
  83. return;
  84. // respond to command
  85. bool canDo = false;
  86. const float inhibitAutoFollowDuration = 60.0f;
  87. switch( m_lastRadioCommand )
  88. {
  89. case RADIO_REPORT_IN_TEAM:
  90. {
  91. GetChatter()->ReportingIn();
  92. break;
  93. }
  94. case RADIO_FOLLOW_ME:
  95. case RADIO_COVER_ME:
  96. case RADIO_STICK_TOGETHER_TEAM:
  97. case RADIO_REGROUP_TEAM:
  98. {
  99. if (!IsFollowing())
  100. {
  101. Follow( player );
  102. player->AllowAutoFollow();
  103. canDo = true;
  104. }
  105. break;
  106. }
  107. case RADIO_ENEMY_SPOTTED:
  108. case RADIO_NEED_BACKUP:
  109. case RADIO_TAKING_FIRE:
  110. if (!IsFollowing())
  111. {
  112. Follow( player );
  113. GetChatter()->Say( "OnMyWay" );
  114. player->AllowAutoFollow();
  115. canDo = false;
  116. }
  117. break;
  118. case RADIO_TEAM_FALL_BACK:
  119. {
  120. if (TryToRetreat())
  121. canDo = true;
  122. break;
  123. }
  124. case RADIO_HOLD_THIS_POSITION:
  125. {
  126. // find the leader's area
  127. SetTask( HOLD_POSITION );
  128. StopFollowing();
  129. player->InhibitAutoFollow( inhibitAutoFollowDuration );
  130. Hide( TheNavMesh->GetNearestNavArea( m_radioPosition ) );
  131. canDo = true;
  132. break;
  133. }
  134. case RADIO_GO_GO_GO:
  135. case RADIO_STORM_THE_FRONT:
  136. StopFollowing();
  137. Hunt();
  138. canDo = true;
  139. player->InhibitAutoFollow( inhibitAutoFollowDuration );
  140. break;
  141. case RADIO_GET_OUT_OF_THERE:
  142. if (TheCSBots()->IsBombPlanted())
  143. {
  144. EscapeFromBomb();
  145. player->InhibitAutoFollow( inhibitAutoFollowDuration );
  146. canDo = true;
  147. }
  148. break;
  149. case RADIO_SECTOR_CLEAR:
  150. {
  151. // if this is a defusal scenario, and the bomb is planted,
  152. // and a human player cleared a bombsite, check it off our list too
  153. if (TheCSBots()->GetScenario() == CCSBotManager::SCENARIO_DEFUSE_BOMB)
  154. {
  155. if (GetTeamNumber() == TEAM_CT && TheCSBots()->IsBombPlanted())
  156. {
  157. const CCSBotManager::Zone *zone = TheCSBots()->GetClosestZone( player );
  158. if (zone)
  159. {
  160. GetGameState()->ClearBombsite( zone->m_index );
  161. // if we are huting for the planted bomb, re-select bombsite
  162. if (GetTask() == FIND_TICKING_BOMB)
  163. Idle();
  164. canDo = true;
  165. }
  166. }
  167. }
  168. break;
  169. }
  170. default:
  171. // ignore all other radio commands for now
  172. return;
  173. }
  174. if (canDo)
  175. {
  176. // affirmative
  177. GetChatter()->Affirmative();
  178. // if we agreed to follow a new command, put away our grenade
  179. if (IsRadioCommand( m_lastRadioCommand ) && IsUsingGrenade())
  180. {
  181. EquipBestWeapon();
  182. }
  183. }
  184. // consume command
  185. m_lastRadioCommand = RADIO_INVALID;
  186. }
  187. //--------------------------------------------------------------------------------------------------------------
  188. /**
  189. * Decide if we should move to help the player, return true if we will
  190. */
  191. bool CCSBot::RespondToHelpRequest( CCSPlayer *them, Place place, float maxRange )
  192. {
  193. if (IsRogue())
  194. return false;
  195. // if we're busy, ignore
  196. if (IsBusy())
  197. return false;
  198. Vector themOrigin = GetCentroid( them );
  199. // if we are too far away, ignore
  200. if (maxRange > 0.0f)
  201. {
  202. // compute actual travel distance
  203. PathCost cost(this);
  204. float travelDistance = NavAreaTravelDistance( m_lastKnownArea, TheNavMesh->GetNearestNavArea( themOrigin ), cost );
  205. if (travelDistance < 0.0f)
  206. return false;
  207. if (travelDistance > maxRange)
  208. return false;
  209. }
  210. if (place == UNDEFINED_PLACE)
  211. {
  212. // if we have no "place" identifier, go directly to them
  213. // if we are already there, ignore
  214. float rangeSq = (them->GetAbsOrigin() - GetAbsOrigin()).LengthSqr();
  215. const float close = 750.0f * 750.0f;
  216. if (rangeSq < close)
  217. return true;
  218. MoveTo( themOrigin, FASTEST_ROUTE );
  219. }
  220. else
  221. {
  222. // if we are already there, ignore
  223. if (GetPlace() == place)
  224. return true;
  225. // go to where help is needed
  226. const Vector *pos = GetRandomSpotAtPlace( place );
  227. if (pos)
  228. {
  229. MoveTo( *pos, FASTEST_ROUTE );
  230. }
  231. else
  232. {
  233. MoveTo( themOrigin, FASTEST_ROUTE );
  234. }
  235. }
  236. // acknowledge
  237. GetChatter()->Say( "OnMyWay" );
  238. return true;
  239. }
  240. //--------------------------------------------------------------------------------------------------------------
  241. /**
  242. * Send a radio message
  243. */
  244. void CCSBot::SendRadioMessage( RadioType event )
  245. {
  246. // make sure this is a radio event
  247. if (event <= RADIO_START_1 || event >= RADIO_END)
  248. return;
  249. PrintIfWatched( "%3.1f: SendRadioMessage( %s )\n", gpGlobals->curtime, RadioEventName[ event ] );
  250. // note the time the message was sent
  251. TheCSBots()->SetRadioMessageTimestamp( event, GetTeamNumber() );
  252. m_lastRadioSentTimestamp = gpGlobals->curtime;
  253. char slot[2];
  254. slot[1] = '\000';
  255. if (event > RADIO_START_1 && event < RADIO_START_2)
  256. {
  257. HandleMenu_Radio1( event - RADIO_START_1 );
  258. }
  259. else if (event > RADIO_START_2 && event < RADIO_START_3)
  260. {
  261. HandleMenu_Radio2( event - RADIO_START_2 );
  262. }
  263. else
  264. {
  265. HandleMenu_Radio3( event - RADIO_START_3 );
  266. }
  267. }
  268. //--------------------------------------------------------------------------------------------------------------
  269. /**
  270. * Send voice chatter. Also sends the entindex and duration for voice feedback.
  271. */
  272. void CCSBot::SpeakAudio( const char *voiceFilename, float duration, int pitch )
  273. {
  274. if( !IsAlive() )
  275. return;
  276. if ( IsObserver() )
  277. return;
  278. CRecipientFilter filter;
  279. ConstructRadioFilter( filter );
  280. UserMessageBegin ( filter, "RawAudio" );
  281. WRITE_BYTE( pitch );
  282. WRITE_BYTE( entindex() );
  283. WRITE_FLOAT( duration );
  284. WRITE_STRING( voiceFilename );
  285. MessageEnd();
  286. GetChatter()->ResetRadioSilenceDuration();
  287. m_voiceEndTimestamp = gpGlobals->curtime + duration;
  288. }