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.

427 lines
12 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_gamerules.h"
  10. #include "KeyValues.h"
  11. #include "cs_bot.h"
  12. // memdbgon must be the last include file in a .cpp file!!!
  13. #include "tier0/memdbgon.h"
  14. //--------------------------------------------------------------------------------------------------------------
  15. /**
  16. * Checks if the bot can hear the event
  17. */
  18. void CCSBot::OnAudibleEvent( IGameEvent *event, CBasePlayer *player, float range, PriorityType priority, bool isHostile, bool isFootstep, const Vector *actualOrigin )
  19. {
  20. /// @todo Listen to non-player sounds
  21. if (player == NULL)
  22. return;
  23. // don't pay attention to noise that friends make
  24. if (!IsEnemy( player ))
  25. return;
  26. Vector playerOrigin = GetCentroid( player );
  27. Vector myOrigin = GetCentroid( this );
  28. // If the event occurs far from the triggering player, it may override the origin
  29. if ( actualOrigin )
  30. {
  31. playerOrigin = *actualOrigin;
  32. }
  33. // check if noise is close enough for us to hear
  34. const Vector *newNoisePosition = &playerOrigin;
  35. float newNoiseDist = (myOrigin - *newNoisePosition).Length();
  36. if (newNoiseDist < range)
  37. {
  38. // we heard the sound
  39. if ((IsLocalPlayerWatchingMe() && cv_bot_debug.GetInt() == 3) || cv_bot_debug.GetInt() == 4)
  40. {
  41. PrintIfWatched( "Heard noise (%s from %s, pri %s, time %3.1f)\n",
  42. (FStrEq( "weapon_fire", event->GetName() )) ? "Weapon fire " : "",
  43. (player) ? player->GetPlayerName() : "NULL",
  44. (priority == PRIORITY_HIGH) ? "HIGH" : ((priority == PRIORITY_MEDIUM) ? "MEDIUM" : "LOW"),
  45. gpGlobals->curtime );
  46. }
  47. // should we pay attention to it
  48. // if noise timestamp is zero, there is no prior noise
  49. if (m_noiseTimestamp > 0.0f)
  50. {
  51. // only overwrite recent sound if we are louder (closer), or more important - if old noise was long ago, its faded
  52. const float shortTermMemoryTime = 3.0f;
  53. if (gpGlobals->curtime - m_noiseTimestamp < shortTermMemoryTime)
  54. {
  55. // prior noise is more important - ignore new one
  56. if (priority < m_noisePriority)
  57. return;
  58. float oldNoiseDist = (myOrigin - m_noisePosition).Length();
  59. if (newNoiseDist >= oldNoiseDist)
  60. return;
  61. }
  62. }
  63. // find the area in which the noise occured
  64. /// @todo Better handle when noise occurs off the nav mesh
  65. /// @todo Make sure noise area is not through a wall or ceiling from source of noise
  66. /// @todo Change GetNavTravelTime to better deal with NULL destination areas
  67. CNavArea *noiseArea = TheNavMesh->GetNearestNavArea( *newNoisePosition );
  68. if (noiseArea == NULL)
  69. {
  70. PrintIfWatched( " *** Noise occurred off the nav mesh - ignoring!\n" );
  71. return;
  72. }
  73. m_noiseArea = noiseArea;
  74. // remember noise priority
  75. m_noisePriority = priority;
  76. // randomize noise position in the area a bit - hearing isn't very accurate
  77. // the closer the noise is, the more accurate our placement
  78. /// @todo Make sure not to pick a position on the opposite side of ourselves.
  79. const float maxErrorRadius = 400.0f;
  80. const float maxHearingRange = 2000.0f;
  81. float errorRadius = maxErrorRadius * newNoiseDist/maxHearingRange;
  82. m_noisePosition.x = newNoisePosition->x + RandomFloat( -errorRadius, errorRadius );
  83. m_noisePosition.y = newNoisePosition->y + RandomFloat( -errorRadius, errorRadius );
  84. // note the *travel distance* to the noise
  85. m_noiseTravelDistance = GetTravelDistanceToPlayer( (CCSPlayer *)player );
  86. // make sure noise position remains in the same area
  87. m_noiseArea->GetClosestPointOnArea( m_noisePosition, &m_noisePosition );
  88. // note when we heard the noise
  89. m_noiseTimestamp = gpGlobals->curtime;
  90. // if we hear a nearby enemy, become alert
  91. const float nearbyNoiseRange = 1000.0f;
  92. if (m_noiseTravelDistance < nearbyNoiseRange && m_noiseTravelDistance > 0.0f)
  93. {
  94. BecomeAlert();
  95. }
  96. }
  97. }
  98. //--------------------------------------------------------------------------------------------------------------
  99. void CCSBot::OnHEGrenadeDetonate( IGameEvent *event )
  100. {
  101. if ( !IsAlive() )
  102. return;
  103. // don't react to our own events
  104. CBasePlayer *player = UTIL_PlayerByUserId( event->GetInt( "userid" ) );
  105. if ( player == this )
  106. return;
  107. OnAudibleEvent( event, player, 99999.0f, PRIORITY_HIGH, true ); // hegrenade_detonate
  108. }
  109. //--------------------------------------------------------------------------------------------------------------
  110. void CCSBot::OnFlashbangDetonate( IGameEvent *event )
  111. {
  112. if ( !IsAlive() )
  113. return;
  114. // don't react to our own events
  115. CBasePlayer *player = UTIL_PlayerByUserId( event->GetInt( "userid" ) );
  116. if ( player == this )
  117. return;
  118. OnAudibleEvent( event, player, 1000.0f, PRIORITY_LOW, true ); // flashbang_detonate
  119. }
  120. //--------------------------------------------------------------------------------------------------------------
  121. void CCSBot::OnSmokeGrenadeDetonate( IGameEvent *event )
  122. {
  123. if ( !IsAlive() )
  124. return;
  125. // don't react to our own events
  126. CBasePlayer *player = UTIL_PlayerByUserId( event->GetInt( "userid" ) );
  127. if ( player == this )
  128. return;
  129. OnAudibleEvent( event, player, 1000.0f, PRIORITY_LOW, true ); // smokegrenade_detonate
  130. }
  131. //--------------------------------------------------------------------------------------------------------------
  132. void CCSBot::OnGrenadeBounce( IGameEvent *event )
  133. {
  134. if ( !IsAlive() )
  135. return;
  136. // don't react to our own events
  137. CBasePlayer *player = UTIL_PlayerByUserId( event->GetInt( "userid" ) );
  138. if ( player == this )
  139. return;
  140. OnAudibleEvent( event, player, 500.0f, PRIORITY_LOW, true ); // grenade_bounce
  141. }
  142. //--------------------------------------------------------------------------------------------------------------
  143. void CCSBot::OnBulletImpact( IGameEvent *event )
  144. {
  145. if ( !IsAlive() )
  146. return;
  147. // don't react to our own events
  148. CBasePlayer *player = UTIL_PlayerByUserId( event->GetInt( "userid" ) );
  149. if ( player == this )
  150. return;
  151. // Construct an origin for the sound, since it can be far from the originating player
  152. Vector actualOrigin;
  153. actualOrigin.x = event->GetFloat( "x", 0.0f );
  154. actualOrigin.y = event->GetFloat( "y", 0.0f );
  155. actualOrigin.z = event->GetFloat( "z", 0.0f );
  156. /// @todo Ignoring bullet impact events for now - we dont want bots to look directly at them!
  157. //OnAudibleEvent( event, player, 1100.0f, PRIORITY_MEDIUM, true, false, &actualOrigin ); // bullet_impact
  158. }
  159. //--------------------------------------------------------------------------------------------------------------
  160. void CCSBot::OnBreakProp( IGameEvent *event )
  161. {
  162. if ( !IsAlive() )
  163. return;
  164. // don't react to our own events
  165. CBasePlayer *player = UTIL_PlayerByUserId( event->GetInt( "userid" ) );
  166. if ( player == this )
  167. return;
  168. OnAudibleEvent( event, player, 1100.0f, PRIORITY_MEDIUM, true ); // break_prop
  169. }
  170. //--------------------------------------------------------------------------------------------------------------
  171. void CCSBot::OnBreakBreakable( IGameEvent *event )
  172. {
  173. if ( !IsAlive() )
  174. return;
  175. // don't react to our own events
  176. CBasePlayer *player = UTIL_PlayerByUserId( event->GetInt( "userid" ) );
  177. if ( player == this )
  178. return;
  179. OnAudibleEvent( event, player, 1100.0f, PRIORITY_MEDIUM, true ); // break_glass
  180. }
  181. //--------------------------------------------------------------------------------------------------------------
  182. void CCSBot::OnDoorMoving( IGameEvent *event )
  183. {
  184. if ( !IsAlive() )
  185. return;
  186. // don't react to our own events
  187. CBasePlayer *player = UTIL_PlayerByUserId( event->GetInt( "userid" ) );
  188. if ( player == this )
  189. return;
  190. OnAudibleEvent( event, player, 1100.0f, PRIORITY_MEDIUM, false ); // door_moving
  191. }
  192. //--------------------------------------------------------------------------------------------------------------
  193. void CCSBot::OnHostageFollows( IGameEvent *event )
  194. {
  195. if ( !IsAlive() )
  196. return;
  197. // don't react to our own events
  198. CBasePlayer *player = UTIL_PlayerByUserId( event->GetInt( "userid" ) );
  199. if ( player == this )
  200. return;
  201. // player_follows needs a player
  202. if (player == NULL)
  203. return;
  204. // don't pay attention to noise that friends make
  205. if (!IsEnemy( player ))
  206. return;
  207. Vector playerOrigin = GetCentroid( player );
  208. Vector myOrigin = GetCentroid( this );
  209. const float range = 1200.0f;
  210. // this is here so T's not only act on the noise, but look at it, too
  211. if (GetTeamNumber() == TEAM_TERRORIST)
  212. {
  213. // make sure we can hear the noise
  214. if ((playerOrigin - myOrigin).IsLengthGreaterThan( range ))
  215. return;
  216. // tell our teammates that the hostages are being taken
  217. GetChatter()->HostagesBeingTaken();
  218. // only move if we hear them being rescued and can't see any hostages
  219. if (GetGameState()->GetNearestVisibleFreeHostage() == NULL)
  220. {
  221. // since we are guarding the hostages, presumably we know where they are
  222. // if we're close enough to "hear" this event, either go to where the event occured,
  223. // or head for an escape zone to head them off
  224. if (GetTask() != CCSBot::GUARD_HOSTAGE_RESCUE_ZONE)
  225. {
  226. //const float headOffChance = 33.3f;
  227. if (true) // || RandomFloat( 0, 100 ) < headOffChance)
  228. {
  229. // head them off at a rescue zone
  230. if (GuardRandomZone())
  231. {
  232. SetTask( CCSBot::GUARD_HOSTAGE_RESCUE_ZONE );
  233. SetDisposition( CCSBot::OPPORTUNITY_FIRE );
  234. PrintIfWatched( "Trying to beat them to an escape zone!\n" );
  235. }
  236. }
  237. else
  238. {
  239. SetTask( SEEK_AND_DESTROY );
  240. StandUp();
  241. Run();
  242. MoveTo( playerOrigin, FASTEST_ROUTE );
  243. }
  244. }
  245. }
  246. }
  247. else
  248. {
  249. // CT's don't care about this noise
  250. return;
  251. }
  252. OnAudibleEvent( event, player, range, PRIORITY_MEDIUM, false ); // hostage_follows
  253. }
  254. //--------------------------------------------------------------------------------------------------------------
  255. void CCSBot::OnRoundEnd( IGameEvent *event )
  256. {
  257. // Morale adjustments happen even for dead players
  258. int winner = event->GetInt( "winner" );
  259. switch ( winner )
  260. {
  261. case WINNER_TER:
  262. if (GetTeamNumber() == TEAM_CT)
  263. {
  264. DecreaseMorale();
  265. }
  266. else
  267. {
  268. IncreaseMorale();
  269. }
  270. break;
  271. case WINNER_CT:
  272. if (GetTeamNumber() == TEAM_CT)
  273. {
  274. IncreaseMorale();
  275. }
  276. else
  277. {
  278. DecreaseMorale();
  279. }
  280. break;
  281. default:
  282. break;
  283. }
  284. m_gameState.OnRoundEnd( event );
  285. if ( !IsAlive() )
  286. return;
  287. if ( event->GetInt( "winner" ) == WINNER_TER )
  288. {
  289. if (GetTeamNumber() == TEAM_TERRORIST)
  290. GetChatter()->CelebrateWin();
  291. }
  292. else if ( event->GetInt( "winner" ) == WINNER_CT )
  293. {
  294. if (GetTeamNumber() == TEAM_CT)
  295. GetChatter()->CelebrateWin();
  296. }
  297. }
  298. //--------------------------------------------------------------------------------------------------------------
  299. void CCSBot::OnRoundStart( IGameEvent *event )
  300. {
  301. m_gameState.OnRoundStart( event );
  302. }
  303. //--------------------------------------------------------------------------------------------------------------
  304. void CCSBot::OnHostageRescuedAll( IGameEvent *event )
  305. {
  306. m_gameState.OnHostageRescuedAll( event );
  307. }
  308. //--------------------------------------------------------------------------------------------------------------
  309. void CCSBot::OnNavBlocked( IGameEvent *event )
  310. {
  311. if ( event->GetBool( "blocked" ) )
  312. {
  313. unsigned int areaID = event->GetInt( "area" );
  314. if ( areaID )
  315. {
  316. // An area was blocked off. Reset our path if it has this area on it.
  317. for( int i=0; i<m_pathLength; ++i )
  318. {
  319. const ConnectInfo *info = &m_path[ i ];
  320. if ( info->area && info->area->GetID() == areaID )
  321. {
  322. DestroyPath();
  323. return;
  324. }
  325. }
  326. }
  327. }
  328. }
  329. //--------------------------------------------------------------------------------------------------------------
  330. /**
  331. * Invoked when bot enters a nav area
  332. */
  333. void CCSBot::OnEnteredNavArea( CNavArea *newArea )
  334. {
  335. // assume that we "clear" an area of enemies when we enter it
  336. newArea->SetClearedTimestamp( GetTeamNumber()-1 );
  337. // if we just entered a 'stop' area, set the flag
  338. if ( newArea->GetAttributes() & NAV_MESH_STOP )
  339. {
  340. m_isStopping = true;
  341. }
  342. /// @todo Flag these areas as spawn areas during load
  343. if (IsAtEnemySpawn())
  344. {
  345. m_hasVisitedEnemySpawn = true;
  346. }
  347. }