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.

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