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.

892 lines
22 KiB

  1. // NextBotManager.cpp
  2. // Author: Michael Booth, May 2006
  3. //========= Copyright Valve Corporation, All rights reserved. ============//
  4. #include "cbase.h"
  5. #include "NextBotManager.h"
  6. #include "NextBotInterface.h"
  7. #ifdef TERROR
  8. #include "ZombieBot/Infected/Infected.h"
  9. #include "ZombieBot/Witch/Witch.h"
  10. #include "ZombieManager.h"
  11. #endif
  12. #include "SharedFunctorUtils.h"
  13. //#include "../../common/blackbox_helper.h"
  14. // memdbgon must be the last include file in a .cpp file!!!
  15. #include "tier0/memdbgon.h"
  16. extern ConVar ZombieMobMaxSize;
  17. ConVar nb_update_frequency( "nb_update_frequency", ".1", FCVAR_CHEAT );
  18. ConVar nb_update_framelimit( "nb_update_framelimit", ( IsDebug() ) ? "30" : "15", FCVAR_CHEAT );
  19. ConVar nb_update_maxslide( "nb_update_maxslide", "2", FCVAR_CHEAT );
  20. ConVar nb_update_debug( "nb_update_debug", "0", FCVAR_CHEAT );
  21. //---------------------------------------------------------------------------------------------
  22. //---------------------------------------------------------------------------------------------
  23. /**
  24. * Singleton accessor.
  25. * By returning a reference, we guarantee construction of the
  26. * instance before its first use.
  27. */
  28. NextBotManager &TheNextBots( void )
  29. {
  30. if ( NextBotManager::GetInstance() )
  31. {
  32. return *NextBotManager::GetInstance();
  33. }
  34. else
  35. {
  36. static NextBotManager manager;
  37. NextBotManager::SetInstance( &manager );
  38. return manager;
  39. }
  40. }
  41. NextBotManager* NextBotManager::sInstance = NULL;
  42. //---------------------------------------------------------------------------------------------
  43. //---------------------------------------------------------------------------------------------
  44. static const char *debugTypeName[] =
  45. {
  46. "BEHAVIOR",
  47. "LOOK_AT",
  48. "PATH",
  49. "ANIMATION",
  50. "LOCOMOTION",
  51. "VISION",
  52. "HEARING",
  53. "EVENTS",
  54. "ERRORS",
  55. NULL
  56. };
  57. static void CC_SetDebug( const CCommand &args )
  58. {
  59. if ( args.ArgC() < 2 )
  60. {
  61. Msg( "Debugging stopped\n" );
  62. TheNextBots().SetDebugTypes( NEXTBOT_DEBUG_NONE );
  63. return;
  64. }
  65. int debugType = 0;
  66. for( int i=1; i<args.ArgC(); ++i )
  67. {
  68. int type;
  69. for( type = 0; debugTypeName[ type ]; ++type )
  70. {
  71. const char *token = args[i];
  72. // special token that means "all"
  73. if ( token[0] == '*' )
  74. {
  75. debugType = NEXTBOT_DEBUG_ALL;
  76. break;
  77. }
  78. if ( !Q_strnicmp( args[i], debugTypeName[ type ], Q_strlen( args[1] ) ) )
  79. {
  80. debugType |= ( 1 << type );
  81. break;
  82. }
  83. }
  84. if ( !debugTypeName[ type ] )
  85. {
  86. Msg( "Invalid debug type '%s'\n", args[i] );
  87. }
  88. }
  89. // enable debugging
  90. TheNextBots().SetDebugTypes( ( NextBotDebugType ) debugType );
  91. }
  92. static ConCommand SetDebug( "nb_debug", CC_SetDebug, "Debug NextBots. Categories are: BEHAVIOR, LOOK_AT, PATH, ANIMATION, LOCOMOTION, VISION, HEARING, EVENTS, ERRORS.", FCVAR_CHEAT );
  93. //---------------------------------------------------------------------------------------------
  94. static void CC_SetDebugFilter( const CCommand &args )
  95. {
  96. if ( args.ArgC() < 2 )
  97. {
  98. Msg( "Debug filter cleared.\n" );
  99. TheNextBots().DebugFilterClear();
  100. return;
  101. }
  102. for( int i=1; i<args.ArgC(); ++i )
  103. {
  104. int index = Q_atoi( args[i] );
  105. if ( index > 0 )
  106. {
  107. TheNextBots().DebugFilterAdd( index );
  108. }
  109. else
  110. {
  111. TheNextBots().DebugFilterAdd( args[i] );
  112. }
  113. }
  114. }
  115. static ConCommand SetDebugFilter( "nb_debug_filter", CC_SetDebugFilter, "Add items to the NextBot debug filter. Items can be entindexes or part of the indentifier of one or more bots.", FCVAR_CHEAT );
  116. //---------------------------------------------------------------------------------------------
  117. class Selector
  118. {
  119. public:
  120. Selector( CBasePlayer *player, bool useLOS )
  121. {
  122. m_player = player;
  123. player->EyeVectors( &m_forward );
  124. m_pick = NULL;
  125. m_pickRange = 99999999999999.9f;
  126. m_useLOS = useLOS;
  127. }
  128. bool operator() ( INextBot *bot )
  129. {
  130. CBaseCombatCharacter *botEntity = bot->GetEntity();
  131. if ( botEntity->IsAlive() )
  132. {
  133. Vector to = botEntity->WorldSpaceCenter() - m_player->EyePosition();
  134. float range = to.NormalizeInPlace();
  135. if ( DotProduct( m_forward, to ) > 0.98f && range < m_pickRange )
  136. {
  137. if ( !m_useLOS || m_player->IsAbleToSee( botEntity, CBaseCombatCharacter::DISREGARD_FOV ) )
  138. {
  139. m_pick = bot;
  140. m_pickRange = range;
  141. }
  142. }
  143. }
  144. return true;
  145. }
  146. CBasePlayer *m_player;
  147. Vector m_forward;
  148. INextBot *m_pick;
  149. float m_pickRange;
  150. bool m_useLOS;
  151. };
  152. static void CC_SelectBot( const CCommand &args )
  153. {
  154. CBasePlayer *player = UTIL_GetListenServerHost();
  155. if ( player )
  156. {
  157. Selector select( player, false );
  158. TheNextBots().ForEachBot( select );
  159. TheNextBots().Select( select.m_pick );
  160. if ( select.m_pick )
  161. {
  162. NDebugOverlay::Circle( select.m_pick->GetLocomotionInterface()->GetFeet() + Vector( 0, 0, 5 ), Vector( 1, 0, 0 ), Vector( 0, -1, 0 ), 25.0f, 0, 255, 0, 255, false, 1.0f );
  163. }
  164. }
  165. }
  166. static ConCommand SelectBot( "nb_select", CC_SelectBot, "Select the bot you are aiming at for further debug operations.", FCVAR_CHEAT );
  167. //---------------------------------------------------------------------------------------------
  168. static void CC_ForceLookAt( const CCommand &args )
  169. {
  170. CBasePlayer *player = UTIL_GetListenServerHost();
  171. INextBot *pick = TheNextBots().GetSelected();
  172. if ( player && pick )
  173. {
  174. pick->GetBodyInterface()->AimHeadTowards( player, IBody::CRITICAL, 9999999.9f, NULL, "Aim forced" );
  175. }
  176. }
  177. static ConCommand ForceLookAt( "nb_force_look_at", CC_ForceLookAt, "Force selected bot to look at the local player's position", FCVAR_CHEAT );
  178. //--------------------------------------------------------------------------------------------------------
  179. void CC_WarpSelectedHere( const CCommand &args )
  180. {
  181. CBasePlayer *me = dynamic_cast< CBasePlayer * >( UTIL_GetCommandClient() );
  182. INextBot *pick = TheNextBots().GetSelected();
  183. if ( me == NULL || pick == NULL )
  184. {
  185. return;
  186. }
  187. Vector forward;
  188. me->EyeVectors( &forward );
  189. trace_t result;
  190. UTIL_TraceLine( me->EyePosition(), me->EyePosition() + 999999.9f * forward, MASK_BLOCKLOS_AND_NPCS|CONTENTS_IGNORE_NODRAW_OPAQUE, me, COLLISION_GROUP_NONE, &result );
  191. if ( result.DidHit() )
  192. {
  193. Vector spot = result.endpos + Vector( 0, 0, 10.0f );
  194. pick->GetEntity()->Teleport( &spot, &vec3_angle, &vec3_origin );
  195. }
  196. }
  197. static ConCommand WarpSelectedHere( "nb_warp_selected_here", CC_WarpSelectedHere, "Teleport the selected bot to your cursor position", FCVAR_CHEAT );
  198. //---------------------------------------------------------------------------------------------
  199. //---------------------------------------------------------------------------------------------
  200. NextBotManager::NextBotManager( void )
  201. {
  202. m_debugType = 0;
  203. m_selectedBot = NULL;
  204. m_iUpdateTickrate = 0;
  205. }
  206. //---------------------------------------------------------------------------------------------
  207. NextBotManager::~NextBotManager()
  208. {
  209. }
  210. //---------------------------------------------------------------------------------------------
  211. /**
  212. * Reset to initial state
  213. */
  214. void NextBotManager::Reset( void )
  215. {
  216. // remove the NextBots that should go away during a reset (they will unregister themselves as they go)
  217. int i = m_botList.Head();
  218. while ( i != m_botList.InvalidIndex() )
  219. {
  220. int iNext = m_botList.Next( i );
  221. if ( m_botList[i]->IsRemovedOnReset() )
  222. {
  223. UTIL_Remove( m_botList[i]->GetEntity() );
  224. //Assert( !m_botList.IsInList( i ) ); // UTIL_Remove() calls UpdateOnRemove, adds EFL_KILLME, but doesn't delete until the end of the frame
  225. }
  226. i = iNext;
  227. }
  228. m_selectedBot = NULL;
  229. }
  230. //---------------------------------------------------------------------------------------------
  231. inline bool IsDead( INextBot *pBot )
  232. {
  233. CBaseCombatCharacter *pEntity = pBot->GetEntity();
  234. if ( pEntity )
  235. {
  236. if ( pEntity->IsPlayer() && pEntity->m_lifeState == LIFE_DEAD )
  237. {
  238. return true;
  239. }
  240. if ( pEntity->IsMarkedForDeletion() )
  241. {
  242. return true;
  243. }
  244. if ( pEntity->m_pfnThink == &CBaseEntity::SUB_Remove )
  245. {
  246. return true;
  247. }
  248. }
  249. return false;
  250. }
  251. //---------------------------------------------------------------------------------------------
  252. // Debug stats for update balancing
  253. static int g_nRun;
  254. static int g_nSlid;
  255. static int g_nBlockedSlides;
  256. void NextBotManager::Update( void )
  257. {
  258. // do lightweight upkeep every tick
  259. for( int u=m_botList.Head(); u != m_botList.InvalidIndex(); u = m_botList.Next( u ) )
  260. {
  261. m_botList[ u ]->Upkeep();
  262. }
  263. // schedule full updates
  264. if ( m_botList.Count() )
  265. {
  266. static int iCurFrame = -1;
  267. if ( iCurFrame != gpGlobals->framecount )
  268. {
  269. iCurFrame = gpGlobals->framecount;
  270. m_SumFrameTime = 0;
  271. }
  272. else
  273. {
  274. // Don't run multiple ticks in a frame
  275. return;
  276. }
  277. int tickRate = TIME_TO_TICKS( nb_update_frequency.GetFloat() );
  278. if ( tickRate < 0 )
  279. {
  280. tickRate = 0;
  281. }
  282. if ( m_iUpdateTickrate != tickRate )
  283. {
  284. Msg( "NextBot tickrate changed from %d (%.3fms) to %d (%.3fms)\n", m_iUpdateTickrate, TICKS_TO_TIME( m_iUpdateTickrate ), tickRate, TICKS_TO_TIME( tickRate ) );
  285. m_iUpdateTickrate = tickRate;
  286. }
  287. int i = 0;
  288. int nScheduled = 0;
  289. int nNonResponsive = 0;
  290. int nDead = 0;
  291. if ( m_iUpdateTickrate > 0 )
  292. {
  293. INextBot *pBot;
  294. // Count dead bots, they won't update and balancing calculations should exclude them
  295. for( i = m_botList.Head(); i != m_botList.InvalidIndex(); i = m_botList.Next( i ) )
  296. {
  297. if ( IsDead( m_botList[i] ) )
  298. {
  299. nDead++;
  300. }
  301. }
  302. int nTargetToRun = ceilf( (float)( m_botList.Count() - nDead ) / (float)m_iUpdateTickrate );
  303. int curtickcount = gpGlobals->tickcount;
  304. for( i = m_botList.Head(); nTargetToRun && i != m_botList.InvalidIndex(); i = m_botList.Next( i ) )
  305. {
  306. pBot = m_botList[i];
  307. if ( pBot->IsFlaggedForUpdate() )
  308. {
  309. // Was offered a run last tick but didn't take it, push it back
  310. // Leave the flag set so that bot will run right away later, but be ignored
  311. // until then
  312. nNonResponsive++;
  313. }
  314. else
  315. {
  316. if ( curtickcount - pBot->GetTickLastUpdate() < m_iUpdateTickrate )
  317. {
  318. break;
  319. }
  320. if ( !IsDead( pBot ) )
  321. {
  322. pBot->FlagForUpdate();
  323. nTargetToRun--;
  324. nScheduled++;
  325. }
  326. }
  327. }
  328. }
  329. else
  330. {
  331. nScheduled = m_botList.Count();
  332. }
  333. if ( nb_update_debug.GetBool() )
  334. {
  335. int nIntentionalSliders = 0;
  336. if ( m_iUpdateTickrate > 0 )
  337. {
  338. for( ; i != m_botList.InvalidIndex(); i = m_botList.Next( i ) )
  339. {
  340. if ( gpGlobals->tickcount - m_botList[i]->GetTickLastUpdate() >= m_iUpdateTickrate )
  341. {
  342. nIntentionalSliders++;
  343. }
  344. }
  345. }
  346. Msg( "Frame %8d/tick %8d: %3d run of %3d, %3d sliders, %3d blocked slides, scheduled %3d for next tick, %3d intentional sliders, %d nonresponsive, %d dead\n", gpGlobals->framecount - 1, gpGlobals->tickcount - 1, g_nRun, m_botList.Count() - nDead, g_nSlid, g_nBlockedSlides, nScheduled, nIntentionalSliders, nNonResponsive, nDead );
  347. g_nRun = g_nSlid = g_nBlockedSlides = 0;
  348. }
  349. }
  350. }
  351. //---------------------------------------------------------------------------------------------
  352. bool NextBotManager::ShouldUpdate( INextBot *bot )
  353. {
  354. if ( m_iUpdateTickrate < 1 )
  355. {
  356. return true;
  357. }
  358. float frameLimit = nb_update_framelimit.GetFloat();
  359. float sumFrameTime = 0;
  360. if ( bot->IsFlaggedForUpdate() )
  361. {
  362. bot->FlagForUpdate( false );
  363. sumFrameTime = m_SumFrameTime * 1000.0;
  364. if ( frameLimit > 0.0f )
  365. {
  366. if ( sumFrameTime < frameLimit )
  367. {
  368. return true;
  369. }
  370. else if ( nb_update_debug.GetBool() )
  371. {
  372. Msg( "Frame %8d/tick %8d: frame out of budget (%.2fms > %.2fms)\n", gpGlobals->framecount, gpGlobals->tickcount, sumFrameTime, frameLimit );
  373. }
  374. }
  375. }
  376. int nTicksSlid = ( gpGlobals->tickcount - bot->GetTickLastUpdate() ) - m_iUpdateTickrate;
  377. if ( nTicksSlid >= nb_update_maxslide.GetInt() )
  378. {
  379. if ( frameLimit == 0.0 || sumFrameTime < nb_update_framelimit.GetFloat() * 2.0 )
  380. {
  381. g_nBlockedSlides++;
  382. return true;
  383. }
  384. }
  385. if ( nb_update_debug.GetBool() )
  386. {
  387. if ( nTicksSlid > 0 )
  388. {
  389. g_nSlid++;
  390. }
  391. }
  392. return false;
  393. }
  394. //---------------------------------------------------------------------------------------------
  395. void NextBotManager::NotifyBeginUpdate( INextBot *bot )
  396. {
  397. if ( nb_update_debug.GetBool() )
  398. {
  399. g_nRun++;
  400. }
  401. m_botList.Unlink( bot->GetBotId() );
  402. m_botList.LinkToTail( bot->GetBotId() );
  403. bot->SetTickLastUpdate( gpGlobals->tickcount );
  404. m_CurUpdateStartTime = Plat_FloatTime();
  405. }
  406. //---------------------------------------------------------------------------------------------
  407. void NextBotManager::NotifyEndUpdate( INextBot *bot )
  408. {
  409. // This might be a good place to detect a particular bot had spiked [3/14/2008 tom]
  410. m_SumFrameTime += Plat_FloatTime() - m_CurUpdateStartTime;
  411. }
  412. //---------------------------------------------------------------------------------------------
  413. /**
  414. * When the server has changed maps
  415. */
  416. void NextBotManager::OnMapLoaded( void )
  417. {
  418. Reset();
  419. }
  420. //---------------------------------------------------------------------------------------------
  421. /**
  422. * When the scenario restarts
  423. */
  424. void NextBotManager::OnRoundRestart( void )
  425. {
  426. Reset();
  427. }
  428. //---------------------------------------------------------------------------------------------
  429. int NextBotManager::Register( INextBot *bot )
  430. {
  431. return m_botList.AddToHead( bot );
  432. }
  433. //---------------------------------------------------------------------------------------------
  434. void NextBotManager::UnRegister( INextBot *bot )
  435. {
  436. m_botList.Remove( bot->GetBotId() );
  437. if ( bot == m_selectedBot)
  438. {
  439. // we can't access virtual methods because this is called from a destructor, so just clear it
  440. m_selectedBot = NULL;
  441. }
  442. }
  443. //--------------------------------------------------------------------------------------------------------
  444. void NextBotManager::OnBeginChangeLevel( void )
  445. {
  446. }
  447. //----------------------------------------------------------------------------------------------------------
  448. class NextBotKilledNotifyScan
  449. {
  450. public:
  451. NextBotKilledNotifyScan( CBaseCombatCharacter *victim, const CTakeDamageInfo &info )
  452. {
  453. m_victim = victim;
  454. m_info = info;
  455. }
  456. bool operator() ( INextBot *bot )
  457. {
  458. if ( bot->GetEntity()->IsAlive() && !bot->IsSelf( m_victim ) )
  459. {
  460. bot->OnOtherKilled( m_victim, m_info );
  461. }
  462. return true;
  463. }
  464. CBaseCombatCharacter *m_victim;
  465. CTakeDamageInfo m_info;
  466. };
  467. //---------------------------------------------------------------------------------------------
  468. /**
  469. * When an actor is killed. Propagate to all NextBots.
  470. */
  471. void NextBotManager::OnKilled( CBaseCombatCharacter *victim, const CTakeDamageInfo &info )
  472. {
  473. NextBotKilledNotifyScan notify( victim, info );
  474. TheNextBots().ForEachBot( notify );
  475. }
  476. //----------------------------------------------------------------------------------------------------------
  477. class NextBotSoundNotifyScan
  478. {
  479. public:
  480. NextBotSoundNotifyScan( CBaseEntity *source, const Vector &pos, KeyValues *keys ) : m_source( source ), m_pos( pos ), m_keys( keys )
  481. {
  482. }
  483. bool operator() ( INextBot *bot )
  484. {
  485. if ( bot->GetEntity()->IsAlive() && !bot->IsSelf( m_source ) )
  486. {
  487. bot->OnSound( m_source, m_pos, m_keys );
  488. }
  489. return true;
  490. }
  491. CBaseEntity *m_source;
  492. const Vector &m_pos;
  493. KeyValues *m_keys;
  494. };
  495. //---------------------------------------------------------------------------------------------
  496. /**
  497. * When an entity emits a sound
  498. */
  499. void NextBotManager::OnSound( CBaseEntity *source, const Vector &pos, KeyValues *keys )
  500. {
  501. NextBotSoundNotifyScan notify( source, pos, keys );
  502. TheNextBots().ForEachBot( notify );
  503. if ( source && IsDebugging( NEXTBOT_HEARING ) )
  504. {
  505. int r,g,b;
  506. switch( source->GetTeamNumber() )
  507. {
  508. case FIRST_GAME_TEAM: r = 0; g = 255; b = 0; break;
  509. case (FIRST_GAME_TEAM+1): r = 255; g = 0; b = 0; break;
  510. default: r = 255; g = 255; b = 0; break;
  511. }
  512. NDebugOverlay::Circle( pos, Vector( 1, 0, 0 ), Vector( 0, -1, 0 ), 5.0f, r, g, b, 255, true, 3.0f );
  513. }
  514. }
  515. //----------------------------------------------------------------------------------------------------------
  516. class NextBotResponseNotifyScan
  517. {
  518. public:
  519. NextBotResponseNotifyScan( CBaseCombatCharacter *who, AIConcept_t concept, AI_Response *response ) : m_who( who ), m_concept( concept ), m_response( response )
  520. {
  521. }
  522. bool operator() ( INextBot *bot )
  523. {
  524. if ( bot->GetEntity()->IsAlive() )
  525. {
  526. bot->OnSpokeConcept( m_who, m_concept, m_response );
  527. }
  528. return true;
  529. }
  530. CBaseCombatCharacter *m_who;
  531. AIConcept_t m_concept;
  532. AI_Response *m_response;
  533. };
  534. //---------------------------------------------------------------------------------------------
  535. /**
  536. * When an Actor speaks a concept
  537. */
  538. void NextBotManager::OnSpokeConcept( CBaseCombatCharacter *who, AIConcept_t concept, AI_Response *response )
  539. {
  540. NextBotResponseNotifyScan notify( who, concept, response );
  541. TheNextBots().ForEachBot( notify );
  542. if ( IsDebugging( NEXTBOT_HEARING ) )
  543. {
  544. // const char *who = response->GetCriteria()->GetValue( response->GetCriteria()->FindCriterionIndex( "Who" ) );
  545. // TODO: Need concept.GetStringConcept()
  546. DevMsg( "%3.2f: OnSpokeConcept( %s, %s )\n", gpGlobals->curtime, who->GetDebugName(), "concept.GetStringConcept()" );
  547. }
  548. }
  549. //----------------------------------------------------------------------------------------------------------
  550. class NextBotWeaponFiredNotifyScan
  551. {
  552. public:
  553. NextBotWeaponFiredNotifyScan( CBaseCombatCharacter *who, CBaseCombatWeapon *weapon ) : m_who( who ), m_weapon( weapon )
  554. {
  555. }
  556. bool operator() ( INextBot *bot )
  557. {
  558. if ( bot->GetEntity()->IsAlive() )
  559. {
  560. bot->OnWeaponFired( m_who, m_weapon );
  561. }
  562. return true;
  563. }
  564. CBaseCombatCharacter *m_who;
  565. CBaseCombatWeapon *m_weapon;
  566. };
  567. //---------------------------------------------------------------------------------------------
  568. /**
  569. * When someone fires a weapon
  570. */
  571. void NextBotManager::OnWeaponFired( CBaseCombatCharacter *whoFired, CBaseCombatWeapon *weapon )
  572. {
  573. NextBotWeaponFiredNotifyScan notify( whoFired, weapon );
  574. TheNextBots().ForEachBot( notify );
  575. if ( IsDebugging( NEXTBOT_EVENTS ) )
  576. {
  577. DevMsg( "%3.2f: OnWeaponFired( %s, %s )\n", gpGlobals->curtime, whoFired->GetDebugName(), weapon->GetName() );
  578. }
  579. }
  580. //---------------------------------------------------------------------------------------------
  581. /**
  582. * Add given entindex to the debug filter
  583. */
  584. void NextBotManager::DebugFilterAdd( int index )
  585. {
  586. DebugFilter filter;
  587. filter.index = index;
  588. filter.name[0] = '\000';
  589. m_debugFilterList.AddToTail( filter );
  590. }
  591. //---------------------------------------------------------------------------------------------
  592. /**
  593. * Add given name to the debug filter
  594. */
  595. void NextBotManager::DebugFilterAdd( const char *name )
  596. {
  597. DebugFilter filter;
  598. filter.index = -1;
  599. Q_strncpy( filter.name, name, DebugFilter::MAX_DEBUG_NAME_SIZE );
  600. m_debugFilterList.AddToTail( filter );
  601. }
  602. //---------------------------------------------------------------------------------------------
  603. /**
  604. * Remove given entindex from the debug filter
  605. */
  606. void NextBotManager::DebugFilterRemove( int index )
  607. {
  608. for( int i=0; i<m_debugFilterList.Count(); ++i )
  609. {
  610. if ( m_debugFilterList[i].index == index )
  611. {
  612. m_debugFilterList.Remove( i );
  613. break;
  614. }
  615. }
  616. }
  617. //---------------------------------------------------------------------------------------------
  618. /**
  619. * Remove given name from the debug filter
  620. */
  621. void NextBotManager::DebugFilterRemove( const char *name )
  622. {
  623. for( int i=0; i<m_debugFilterList.Count(); ++i )
  624. {
  625. if ( m_debugFilterList[i].name[0] != '\000' &&
  626. !Q_strnicmp( name, m_debugFilterList[i].name, MIN( Q_strlen( name ), sizeof( m_debugFilterList[i].name ) ) ) )
  627. {
  628. m_debugFilterList.Remove( i );
  629. break;
  630. }
  631. }
  632. }
  633. //---------------------------------------------------------------------------------------------
  634. /**
  635. * Clear the debug filter (remove all entries)
  636. */
  637. void NextBotManager::DebugFilterClear( void )
  638. {
  639. m_debugFilterList.RemoveAll();
  640. }
  641. //---------------------------------------------------------------------------------------------
  642. /**
  643. * Return true if the given bot matches the debug filter
  644. */
  645. bool NextBotManager::IsDebugFilterMatch( const INextBot *bot ) const
  646. {
  647. // if the filter is empty, all bots match
  648. if ( m_debugFilterList.Count() == 0 )
  649. {
  650. return true;
  651. }
  652. for( int i=0; i<m_debugFilterList.Count(); ++i )
  653. {
  654. // compare entity index
  655. if ( m_debugFilterList[i].index == const_cast< INextBot * >( bot )->GetEntity()->entindex() )
  656. {
  657. return true;
  658. }
  659. // compare debug filter
  660. if ( m_debugFilterList[i].name[0] != '\000' && bot->IsDebugFilterMatch( m_debugFilterList[i].name ) )
  661. {
  662. return true;
  663. }
  664. // compare special keyword meaning local player is looking at them
  665. if ( !Q_strnicmp( m_debugFilterList[i].name, "lookat", Q_strlen( m_debugFilterList[i].name ) ) )
  666. {
  667. CBasePlayer *watcher = UTIL_GetListenServerHost();
  668. if ( watcher )
  669. {
  670. CBaseEntity *subject = watcher->GetObserverTarget();
  671. if ( subject && bot->IsSelf( subject ) )
  672. {
  673. return true;
  674. }
  675. }
  676. }
  677. // compare special keyword meaning NextBot is selected
  678. if ( !Q_strnicmp( m_debugFilterList[i].name, "selected", Q_strlen( m_debugFilterList[i].name ) ) )
  679. {
  680. INextBot *selected = GetSelected();
  681. if ( selected && bot->IsSelf( selected->GetEntity() ) )
  682. {
  683. return true;
  684. }
  685. }
  686. }
  687. return false;
  688. }
  689. //---------------------------------------------------------------------------------------------
  690. /**
  691. * Get the bot under the given player's crosshair
  692. */
  693. INextBot *NextBotManager::GetBotUnderCrosshair( CBasePlayer *picker )
  694. {
  695. if ( !picker )
  696. return NULL;
  697. const float MaxDot = 0.7f;
  698. const float MaxRange = 4000.0f;
  699. TargetScan< CBaseCombatCharacter > scan( picker, TEAM_ANY, 1.0f - MaxDot, MaxRange );
  700. ForEachCombatCharacter( scan );
  701. CBaseCombatCharacter *target = scan.GetTarget();
  702. if ( target && target->MyNextBotPointer() )
  703. return target->MyNextBotPointer();
  704. return NULL;
  705. }
  706. #ifdef NEED_BLACK_BOX
  707. //---------------------------------------------------------------------------------------------
  708. CON_COMMAND( nb_dump_debug_history, "Dumps debug history for the bot under the cursor to the blackbox" )
  709. {
  710. if ( !NextBotDebugHistory.GetBool() )
  711. {
  712. BlackBox_Record( "bot", "nb_debug_history 0" );
  713. return;
  714. }
  715. CBasePlayer *player = UTIL_GetCommandClient();
  716. if ( !player )
  717. {
  718. player = UTIL_GetListenServerHost();
  719. }
  720. INextBot *bot = TheNextBots().GetBotUnderCrosshair( player );
  721. if ( !bot )
  722. {
  723. BlackBox_Record( "bot", "no bot under crosshairs" );
  724. return;
  725. }
  726. CUtlVector< const INextBot::NextBotDebugLineType * > lines;
  727. bot->GetDebugHistory( (NEXTBOT_DEBUG_ALL & (~NEXTBOT_EVENTS)), &lines );
  728. for ( int i=0; i<lines.Count(); ++i )
  729. {
  730. if ( IsPC() )
  731. {
  732. BlackBox_Record( "bot", "%s", lines[i]->data );
  733. }
  734. }
  735. }
  736. #endif // NEED_BLACK_BOX
  737. //---------------------------------------------------------------------------------------------
  738. void NextBotManager::CollectAllBots( CUtlVector< INextBot * > *botVector )
  739. {
  740. if ( !botVector )
  741. return;
  742. botVector->RemoveAll();
  743. for( int i=m_botList.Head(); i != m_botList.InvalidIndex(); i = m_botList.Next( i ) )
  744. {
  745. botVector->AddToTail( m_botList[i] );
  746. }
  747. }