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.

522 lines
14 KiB

  1. // NextBotCombatCharacter.cpp
  2. // Next generation bot system
  3. // Author: Michael Booth, April 2005
  4. // Copyright (c) 2005 Turtle Rock Studios, Inc. - All Rights Reserved
  5. #include "cbase.h"
  6. #include "team.h"
  7. #include "CRagdollMagnet.h"
  8. #include "NextBot.h"
  9. #include "NextBotLocomotionInterface.h"
  10. #include "NextBotBodyInterface.h"
  11. #ifdef TERROR
  12. #include "TerrorGamerules.h"
  13. #endif
  14. #include "datacache/imdlcache.h"
  15. #include "EntityFlame.h"
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include "tier0/memdbgon.h"
  18. ConVar NextBotStop( "nb_stop", "0", FCVAR_CHEAT | FCVAR_REPLICATED, "Stop all NextBots" );
  19. //--------------------------------------------------------------------------------------------------------
  20. class CSendBotCommand
  21. {
  22. public:
  23. CSendBotCommand( const char *command )
  24. {
  25. m_command = command;
  26. }
  27. bool operator() ( INextBot *bot )
  28. {
  29. bot->OnCommandString( m_command );
  30. return true;
  31. }
  32. const char *m_command;
  33. };
  34. CON_COMMAND_F( nb_command, "Sends a command string to all bots", FCVAR_CHEAT )
  35. {
  36. if ( args.ArgC() <= 1 )
  37. {
  38. Msg( "Missing command string" );
  39. return;
  40. }
  41. CSendBotCommand sendCmd( args.ArgS() );
  42. TheNextBots().ForEachBot( sendCmd );
  43. }
  44. //-----------------------------------------------------------------------------------------------------
  45. BEGIN_DATADESC( NextBotCombatCharacter )
  46. DEFINE_THINKFUNC( DoThink ),
  47. END_DATADESC()
  48. //-----------------------------------------------------------------------------------------------------
  49. IMPLEMENT_SERVERCLASS_ST( NextBotCombatCharacter, DT_NextBot )
  50. END_SEND_TABLE()
  51. //-----------------------------------------------------------------------------------------------------
  52. NextBotDestroyer::NextBotDestroyer( int team )
  53. {
  54. m_team = team;
  55. }
  56. //-----------------------------------------------------------------------------------------------------
  57. bool NextBotDestroyer::operator() ( INextBot *bot )
  58. {
  59. if ( m_team == TEAM_ANY || bot->GetEntity()->GetTeamNumber() == m_team )
  60. {
  61. // players need to be kicked, not deleted
  62. if ( bot->GetEntity()->IsPlayer() )
  63. {
  64. CBasePlayer *player = dynamic_cast< CBasePlayer * >( bot->GetEntity() );
  65. engine->ServerCommand( UTIL_VarArgs( "kick \"%s\"\n", player->GetPlayerName() ) );
  66. }
  67. else
  68. {
  69. UTIL_Remove( bot->GetEntity() );
  70. }
  71. }
  72. return true;
  73. }
  74. //-----------------------------------------------------------------------------------------------------
  75. CON_COMMAND_F( nb_delete_all, "Delete all non-player NextBot entities.", FCVAR_CHEAT )
  76. {
  77. // Listenserver host or rcon access only!
  78. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  79. return;
  80. CTeam *team = NULL;
  81. if ( args.ArgC() == 2 )
  82. {
  83. const char *teamName = args[1];
  84. for( int i=0; i < g_Teams.Count(); ++i )
  85. {
  86. if ( FStrEq( teamName, g_Teams[i]->GetName() ) )
  87. {
  88. // delete all bots on this team
  89. team = g_Teams[i];
  90. break;
  91. }
  92. }
  93. if ( team == NULL )
  94. {
  95. Msg( "Invalid team '%s'\n", teamName );
  96. return;
  97. }
  98. }
  99. // delete all bots on all teams
  100. NextBotDestroyer destroyer( team ? team->GetTeamNumber() : TEAM_ANY );
  101. TheNextBots().ForEachBot( destroyer );
  102. }
  103. //-----------------------------------------------------------------------------------------------------
  104. class NextBotApproacher
  105. {
  106. public:
  107. NextBotApproacher( void )
  108. {
  109. CBasePlayer *player = UTIL_GetListenServerHost();
  110. if ( player )
  111. {
  112. Vector forward;
  113. player->EyeVectors( &forward );
  114. trace_t result;
  115. unsigned int mask = MASK_BLOCKLOS_AND_NPCS|CONTENTS_IGNORE_NODRAW_OPAQUE | CONTENTS_GRATE | CONTENTS_WINDOW;
  116. UTIL_TraceLine( player->EyePosition(), player->EyePosition() + 999999.9f * forward, mask, player, COLLISION_GROUP_NONE, &result );
  117. if ( result.DidHit() )
  118. {
  119. NDebugOverlay::Cross3D( result.endpos, 5, 0, 255, 0, true, 10.0f );
  120. m_isGoalValid = true;
  121. m_goal = result.endpos;
  122. }
  123. else
  124. {
  125. m_isGoalValid = false;
  126. }
  127. }
  128. }
  129. bool operator() ( INextBot *bot )
  130. {
  131. if ( TheNextBots().IsDebugFilterMatch( bot ) )
  132. {
  133. bot->OnCommandApproach( m_goal );
  134. }
  135. return true;
  136. }
  137. bool m_isGoalValid;
  138. Vector m_goal;
  139. };
  140. CON_COMMAND_F( nb_move_to_cursor, "Tell all NextBots to move to the cursor position", FCVAR_CHEAT )
  141. {
  142. // Listenserver host or rcon access only!
  143. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  144. return;
  145. NextBotApproacher approach;
  146. TheNextBots().ForEachBot( approach );
  147. }
  148. //----------------------------------------------------------------------------------------------------------
  149. //----------------------------------------------------------------------------------------------------------
  150. bool IgnoreActorsTraceFilterFunction( IHandleEntity *pServerEntity, int contentsMask )
  151. {
  152. CBaseEntity *entity = EntityFromEntityHandle( pServerEntity );
  153. return ( entity->MyNextBotPointer() == NULL && !entity->IsPlayer() );
  154. }
  155. //----------------------------------------------------------------------------------------------------------
  156. //----------------------------------------------------------------------------------------------------------
  157. bool VisionTraceFilterFunction( IHandleEntity *pServerEntity, int contentsMask )
  158. {
  159. // Honor BlockLOS also to allow seeing through partially-broken doors
  160. CBaseEntity *entity = EntityFromEntityHandle( pServerEntity );
  161. return ( entity->MyNextBotPointer() == NULL && !entity->IsPlayer() && entity->BlocksLOS() );
  162. }
  163. //----------------------------------------------------------------------------------------------------------
  164. //----------------------------------------------------------------------------------------------------------
  165. NextBotCombatCharacter::NextBotCombatCharacter( void )
  166. {
  167. m_lastAttacker = NULL;
  168. m_didModelChange = false;
  169. }
  170. //----------------------------------------------------------------------------------------------------------
  171. void NextBotCombatCharacter::Spawn( void )
  172. {
  173. BaseClass::Spawn();
  174. // reset bot components
  175. Reset();
  176. SetSolid( SOLID_BBOX );
  177. AddSolidFlags( FSOLID_NOT_STANDABLE );
  178. SetMoveType( MOVETYPE_CUSTOM );
  179. SetCollisionGroup( COLLISION_GROUP_PLAYER );
  180. m_iMaxHealth = m_iHealth;
  181. m_takedamage = DAMAGE_YES;
  182. MDLCACHE_CRITICAL_SECTION();
  183. InitBoneControllers( );
  184. // set up think callback
  185. SetThink( &NextBotCombatCharacter::DoThink );
  186. SetNextThink( gpGlobals->curtime );
  187. m_lastAttacker = NULL;
  188. }
  189. bool NextBotCombatCharacter::IsAreaTraversable( const CNavArea *area ) const
  190. {
  191. if ( !area )
  192. return false;
  193. ILocomotion *mover = GetLocomotionInterface();
  194. if ( mover && !mover->IsAreaTraversable( area ) )
  195. return false;
  196. return BaseClass::IsAreaTraversable( area );
  197. }
  198. //----------------------------------------------------------------------------------------------------------
  199. void NextBotCombatCharacter::DoThink( void )
  200. {
  201. VPROF_BUDGET( "NextBotCombatCharacter::DoThink", "NextBot" );
  202. SetNextThink( gpGlobals->curtime );
  203. if ( BeginUpdate() )
  204. {
  205. // emit model change event
  206. if ( m_didModelChange )
  207. {
  208. m_didModelChange = false;
  209. OnModelChanged();
  210. // propagate model change into NextBot event responders
  211. for ( INextBotEventResponder *sub = FirstContainedResponder(); sub; sub = NextContainedResponder( sub ) )
  212. {
  213. sub->OnModelChanged();
  214. }
  215. }
  216. UpdateLastKnownArea();
  217. // update bot components
  218. if ( !NextBotStop.GetBool() && (GetFlags() & FL_FROZEN) == 0 )
  219. {
  220. Update();
  221. }
  222. EndUpdate();
  223. }
  224. }
  225. //----------------------------------------------------------------------------------------------------------
  226. void NextBotCombatCharacter::Touch( CBaseEntity *other )
  227. {
  228. if ( ShouldTouch( other ) )
  229. {
  230. // propagate touch into NextBot event responders
  231. trace_t result;
  232. result = GetTouchTrace();
  233. // OnContact refers to *physical* contact, not triggers or other non-physical entities
  234. if ( result.DidHit() || other->MyCombatCharacterPointer() != NULL )
  235. {
  236. OnContact( other, &result );
  237. }
  238. }
  239. BaseClass::Touch( other );
  240. }
  241. //----------------------------------------------------------------------------------------------------------
  242. void NextBotCombatCharacter::SetModel( const char *szModelName )
  243. {
  244. // actually change the model
  245. BaseClass::SetModel( szModelName );
  246. // need to do a lazy-check because precache system also invokes this
  247. m_didModelChange = true;
  248. }
  249. //----------------------------------------------------------------------------------------------------------
  250. void NextBotCombatCharacter::Ignite( float flFlameLifetime, bool bNPCOnly, float flSize, bool bCalledByLevelDesigner )
  251. {
  252. BaseClass::Ignite( flFlameLifetime, bNPCOnly, flSize, bCalledByLevelDesigner );
  253. // propagate event to components
  254. OnIgnite();
  255. }
  256. //----------------------------------------------------------------------------------------------------------
  257. void NextBotCombatCharacter::Ignite( float flFlameLifetime, CBaseEntity *pAttacker )
  258. {
  259. if ( IsOnFire() )
  260. return;
  261. // BaseClass::Ignite stuff, plus SetAttacker on the flame, so our attacker gets credit
  262. CEntityFlame *pFlame = CEntityFlame::Create( this, flFlameLifetime );
  263. if ( pFlame )
  264. {
  265. pFlame->SetAttacker( pAttacker );
  266. AddFlag( FL_ONFIRE );
  267. SetEffectEntity( pFlame );
  268. }
  269. m_OnIgnite.FireOutput( this, this );
  270. // propagate event to components
  271. OnIgnite();
  272. }
  273. //----------------------------------------------------------------------------------------------------------
  274. int NextBotCombatCharacter::OnTakeDamage_Alive( const CTakeDamageInfo &info )
  275. {
  276. // track our last attacker
  277. if ( info.GetAttacker() && info.GetAttacker()->MyCombatCharacterPointer() )
  278. {
  279. m_lastAttacker = info.GetAttacker()->MyCombatCharacterPointer();
  280. }
  281. // propagate event to components
  282. OnInjured( info );
  283. return CBaseCombatCharacter::OnTakeDamage_Alive( info );
  284. }
  285. //----------------------------------------------------------------------------------------------------------
  286. int NextBotCombatCharacter::OnTakeDamage_Dying( const CTakeDamageInfo &info )
  287. {
  288. // track our last attacker
  289. if ( info.GetAttacker()->MyCombatCharacterPointer() )
  290. {
  291. m_lastAttacker = info.GetAttacker()->MyCombatCharacterPointer();
  292. }
  293. // propagate event to components
  294. OnInjured( info );
  295. return CBaseCombatCharacter::OnTakeDamage_Dying( info );
  296. }
  297. //----------------------------------------------------------------------------------------------------------
  298. /**
  299. * Can't use CBaseCombatCharacter's Event_Killed because it will immediately ragdoll us
  300. */
  301. static int g_DeathStartEvent = 0;
  302. void NextBotCombatCharacter::Event_Killed( const CTakeDamageInfo &info )
  303. {
  304. // track our last attacker
  305. if ( info.GetAttacker() && info.GetAttacker()->MyCombatCharacterPointer() )
  306. {
  307. m_lastAttacker = info.GetAttacker()->MyCombatCharacterPointer();
  308. }
  309. // propagate event to my components
  310. OnKilled( info );
  311. // Advance life state to dying
  312. m_lifeState = LIFE_DYING;
  313. #ifdef TERROR
  314. /*
  315. * TODO: Make this game-generic
  316. */
  317. // Create the death event just like players do.
  318. TerrorGameRules()->DeathNoticeForEntity( this, info );
  319. // Infected specific event
  320. TerrorGameRules()->DeathNoticeForInfected( this, info );
  321. #endif
  322. if ( GetOwnerEntity() != NULL )
  323. {
  324. GetOwnerEntity()->DeathNotice( this );
  325. }
  326. // inform the other bots
  327. TheNextBots().OnKilled( this, info );
  328. }
  329. //----------------------------------------------------------------------------------------------------------
  330. void NextBotCombatCharacter::PerformCustomPhysics( Vector *pNewPosition, Vector *pNewVelocity, QAngle *pNewAngles, QAngle *pNewAngVelocity )
  331. {
  332. ILocomotion *mover = GetLocomotionInterface();
  333. if ( mover )
  334. {
  335. // hack to keep ground entity from being NULL'd when Z velocity is positive
  336. SetGroundEntity( mover->GetGround() );
  337. }
  338. }
  339. //----------------------------------------------------------------------------------------------------------
  340. bool NextBotCombatCharacter::BecomeRagdoll( const CTakeDamageInfo &info, const Vector &forceVector )
  341. {
  342. // See if there's a ragdoll magnet that should influence our force.
  343. Vector adjustedForceVector = forceVector;
  344. CRagdollMagnet *magnet = CRagdollMagnet::FindBestMagnet( this );
  345. if ( magnet )
  346. {
  347. adjustedForceVector += magnet->GetForceVector( this );
  348. }
  349. // clear the deceased's sound channels.(may have been firing or reloading when killed)
  350. EmitSound( "BaseCombatCharacter.StopWeaponSounds" );
  351. return BaseClass::BecomeRagdoll( info, adjustedForceVector );
  352. }
  353. //----------------------------------------------------------------------------------------------------------
  354. void NextBotCombatCharacter::HandleAnimEvent( animevent_t *event )
  355. {
  356. // propagate event to components
  357. OnAnimationEvent( event );
  358. }
  359. //----------------------------------------------------------------------------------------------------------
  360. /**
  361. * Propagate event into NextBot event responders
  362. */
  363. void NextBotCombatCharacter::OnNavAreaChanged( CNavArea *enteredArea, CNavArea *leftArea )
  364. {
  365. INextBotEventResponder::OnNavAreaChanged( enteredArea, leftArea );
  366. BaseClass::OnNavAreaChanged( enteredArea, leftArea );
  367. }
  368. //----------------------------------------------------------------------------------------------------------
  369. Vector NextBotCombatCharacter::EyePosition( void )
  370. {
  371. if ( GetBodyInterface() )
  372. {
  373. return GetBodyInterface()->GetEyePosition();
  374. }
  375. return BaseClass::EyePosition();
  376. }
  377. //----------------------------------------------------------------------------------------------------------
  378. /**
  379. * Return true if this object can be +used by the bot
  380. */
  381. bool NextBotCombatCharacter::IsUseableEntity( CBaseEntity *entity, unsigned int requiredCaps )
  382. {
  383. if ( entity )
  384. {
  385. int caps = entity->ObjectCaps();
  386. if ( caps & (FCAP_IMPULSE_USE|FCAP_CONTINUOUS_USE|FCAP_ONOFF_USE|FCAP_DIRECTIONAL_USE) )
  387. {
  388. if ( (caps & requiredCaps) == requiredCaps )
  389. {
  390. return true;
  391. }
  392. }
  393. }
  394. return false;
  395. }
  396. //----------------------------------------------------------------------------------------------------------
  397. void NextBotCombatCharacter::UseEntity( CBaseEntity *entity, USE_TYPE useType )
  398. {
  399. if ( IsUseableEntity( entity ) )
  400. {
  401. variant_t emptyVariant;
  402. entity->AcceptInput( "Use", this, this, emptyVariant, useType );
  403. }
  404. }