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.

533 lines
14 KiB

  1. // NextBotInterface.cpp
  2. // Implentation of system methods for NextBot interface
  3. // Author: Michael Booth, May 2006
  4. // Copyright (c) 2006 Turtle Rock Studios, Inc. - All Rights Reserved
  5. #include "cbase.h"
  6. #include "props.h"
  7. #include "fmtstr.h"
  8. #include "team.h"
  9. #include "NextBotInterface.h"
  10. #include "NextBotBodyInterface.h"
  11. #include "NextBotManager.h"
  12. #include "tier0/vprof.h"
  13. // memdbgon must be the last include file in a .cpp file!!!
  14. #include "tier0/memdbgon.h"
  15. // development only, off by default for 360
  16. ConVar NextBotDebugHistory( "nb_debug_history", IsX360() ? "0" : "1", FCVAR_CHEAT, "If true, each bot keeps a history of debug output in memory" );
  17. //----------------------------------------------------------------------------------------------------------------
  18. INextBot::INextBot( void ) : m_debugHistory( MAX_NEXTBOT_DEBUG_HISTORY, 0 ) // CUtlVector: grow to max length, alloc 0 initially
  19. {
  20. m_tickLastUpdate = -999;
  21. m_id = -1;
  22. m_componentList = NULL;
  23. m_debugDisplayLine = 0;
  24. m_immobileTimer.Invalidate();
  25. m_immobileCheckTimer.Invalidate();
  26. m_immobileAnchor = vec3_origin;
  27. m_currentPath = NULL;
  28. // register with the manager
  29. m_id = TheNextBots().Register( this );
  30. }
  31. //----------------------------------------------------------------------------------------------------------------
  32. INextBot::~INextBot()
  33. {
  34. ResetDebugHistory();
  35. // tell the manager we're gone
  36. TheNextBots().UnRegister( this );
  37. if ( m_baseLocomotion )
  38. delete m_baseLocomotion;
  39. if ( m_baseBody )
  40. delete m_baseBody;
  41. if ( m_baseIntention )
  42. delete m_baseIntention;
  43. if ( m_baseVision )
  44. delete m_baseVision;
  45. }
  46. //----------------------------------------------------------------------------------------------------------------
  47. void INextBot::Reset( void )
  48. {
  49. m_tickLastUpdate = -999;
  50. m_debugType = 0;
  51. m_debugDisplayLine = 0;
  52. m_immobileTimer.Invalidate();
  53. m_immobileCheckTimer.Invalidate();
  54. m_immobileAnchor = vec3_origin;
  55. for( INextBotComponent *comp = m_componentList; comp; comp = comp->m_nextComponent )
  56. {
  57. comp->Reset();
  58. }
  59. }
  60. //----------------------------------------------------------------------------------------------------------------
  61. void INextBot::ResetDebugHistory( void )
  62. {
  63. for ( int i=0; i<m_debugHistory.Count(); ++i )
  64. {
  65. delete m_debugHistory[i];
  66. }
  67. m_debugHistory.RemoveAll();
  68. }
  69. //----------------------------------------------------------------------------------------------------------------
  70. bool INextBot::BeginUpdate()
  71. {
  72. if ( TheNextBots().ShouldUpdate( this ) )
  73. {
  74. TheNextBots().NotifyBeginUpdate( this );
  75. return true;
  76. }
  77. return false;
  78. }
  79. //----------------------------------------------------------------------------------------------------------------
  80. void INextBot::EndUpdate( void )
  81. {
  82. TheNextBots().NotifyEndUpdate( this );
  83. }
  84. //----------------------------------------------------------------------------------------------------------------
  85. void INextBot::Update( void )
  86. {
  87. VPROF_BUDGET( "INextBot::Update", "NextBot" );
  88. m_debugDisplayLine = 0;
  89. if ( IsDebugging( NEXTBOT_DEBUG_ALL ) )
  90. {
  91. CFmtStr msg;
  92. DisplayDebugText( msg.sprintf( "#%d", GetEntity()->entindex() ) );
  93. }
  94. UpdateImmobileStatus();
  95. // update all components
  96. for( INextBotComponent *comp = m_componentList; comp; comp = comp->m_nextComponent )
  97. {
  98. if ( comp->ComputeUpdateInterval() )
  99. {
  100. comp->Update();
  101. }
  102. }
  103. }
  104. //----------------------------------------------------------------------------------------------------------------
  105. void INextBot::Upkeep( void )
  106. {
  107. // do upkeep for all components
  108. for( INextBotComponent *comp = m_componentList; comp; comp = comp->m_nextComponent )
  109. {
  110. comp->Upkeep();
  111. }
  112. }
  113. //----------------------------------------------------------------------------------------------------------------
  114. bool INextBot::SetPosition( const Vector &pos )
  115. {
  116. IBody *body = GetBodyInterface();
  117. if (body)
  118. {
  119. return body->SetPosition( pos );
  120. }
  121. // fall back to setting raw entity position
  122. GetEntity()->SetAbsOrigin( pos );
  123. return true;
  124. }
  125. //----------------------------------------------------------------------------------------------------------------
  126. const Vector &INextBot::GetPosition( void ) const
  127. {
  128. return const_cast< INextBot * >( this )->GetEntity()->GetAbsOrigin();
  129. }
  130. //----------------------------------------------------------------------------------------------------------------
  131. /**
  132. * Return true if given actor is our enemy
  133. */
  134. bool INextBot::IsEnemy( const CBaseEntity *them ) const
  135. {
  136. if ( them == NULL )
  137. return false;
  138. // this is not strictly correct, as spectators are not enemies
  139. return const_cast< INextBot * >( this )->GetEntity()->GetTeamNumber() != them->GetTeamNumber();
  140. }
  141. //----------------------------------------------------------------------------------------------------------------
  142. /**
  143. * Return true if given actor is our friend
  144. */
  145. bool INextBot::IsFriend( const CBaseEntity *them ) const
  146. {
  147. if ( them == NULL )
  148. return false;
  149. return const_cast< INextBot * >( this )->GetEntity()->GetTeamNumber() == them->GetTeamNumber();
  150. }
  151. //----------------------------------------------------------------------------------------------------------------
  152. /**
  153. * Return true if 'them' is actually me
  154. */
  155. bool INextBot::IsSelf( const CBaseEntity *them ) const
  156. {
  157. if ( them == NULL )
  158. return false;
  159. return const_cast< INextBot * >( this )->GetEntity()->entindex() == them->entindex();
  160. }
  161. //----------------------------------------------------------------------------------------------------------------
  162. /**
  163. * Components call this to register themselves with the bot that contains them
  164. */
  165. void INextBot::RegisterComponent( INextBotComponent *comp )
  166. {
  167. // add to head of singly linked list
  168. comp->m_nextComponent = m_componentList;
  169. m_componentList = comp;
  170. }
  171. //----------------------------------------------------------------------------------------------------------------
  172. bool INextBot::IsRangeLessThan( CBaseEntity *subject, float range ) const
  173. {
  174. Vector botPos;
  175. CBaseEntity *bot = const_cast< INextBot * >( this )->GetEntity();
  176. if ( !bot || !subject )
  177. return 0.0f;
  178. bot->CollisionProp()->CalcNearestPoint( subject->WorldSpaceCenter(), &botPos );
  179. float computedRange = subject->CollisionProp()->CalcDistanceFromPoint( botPos );
  180. return computedRange < range;
  181. }
  182. //----------------------------------------------------------------------------------------------------------------
  183. bool INextBot::IsRangeLessThan( const Vector &pos, float range ) const
  184. {
  185. Vector to = pos - GetPosition();
  186. return to.IsLengthLessThan( range );
  187. }
  188. //----------------------------------------------------------------------------------------------------------------
  189. bool INextBot::IsRangeGreaterThan( CBaseEntity *subject, float range ) const
  190. {
  191. Vector botPos;
  192. CBaseEntity *bot = const_cast< INextBot * >( this )->GetEntity();
  193. if ( !bot || !subject )
  194. return 0.0f;
  195. bot->CollisionProp()->CalcNearestPoint( subject->WorldSpaceCenter(), &botPos );
  196. float computedRange = subject->CollisionProp()->CalcDistanceFromPoint( botPos );
  197. return computedRange > range;
  198. }
  199. //----------------------------------------------------------------------------------------------------------------
  200. bool INextBot::IsRangeGreaterThan( const Vector &pos, float range ) const
  201. {
  202. Vector to = pos - GetPosition();
  203. return to.IsLengthGreaterThan( range );
  204. }
  205. //----------------------------------------------------------------------------------------------------------------
  206. float INextBot::GetRangeTo( CBaseEntity *subject ) const
  207. {
  208. Vector botPos;
  209. CBaseEntity *bot = const_cast< INextBot * >( this )->GetEntity();
  210. if ( !bot || !subject )
  211. return 0.0f;
  212. bot->CollisionProp()->CalcNearestPoint( subject->WorldSpaceCenter(), &botPos );
  213. float computedRange = subject->CollisionProp()->CalcDistanceFromPoint( botPos );
  214. return computedRange;
  215. }
  216. //----------------------------------------------------------------------------------------------------------------
  217. float INextBot::GetRangeTo( const Vector &pos ) const
  218. {
  219. Vector to = pos - GetPosition();
  220. return to.Length();
  221. }
  222. //----------------------------------------------------------------------------------------------------------------
  223. float INextBot::GetRangeSquaredTo( CBaseEntity *subject ) const
  224. {
  225. Vector botPos;
  226. CBaseEntity *bot = const_cast< INextBot * >( this )->GetEntity();
  227. if ( !bot || !subject )
  228. return 0.0f;
  229. bot->CollisionProp()->CalcNearestPoint( subject->WorldSpaceCenter(), &botPos );
  230. float computedRange = subject->CollisionProp()->CalcDistanceFromPoint( botPos );
  231. return computedRange * computedRange;
  232. }
  233. //----------------------------------------------------------------------------------------------------------------
  234. float INextBot::GetRangeSquaredTo( const Vector &pos ) const
  235. {
  236. Vector to = pos - GetPosition();
  237. return to.LengthSqr();
  238. }
  239. //----------------------------------------------------------------------------------------------------------------
  240. bool INextBot::IsDebugging( unsigned int type ) const
  241. {
  242. if ( TheNextBots().IsDebugging( type ) )
  243. {
  244. return TheNextBots().IsDebugFilterMatch( this );
  245. }
  246. return false;
  247. }
  248. //----------------------------------------------------------------------------------------------------------------
  249. /**
  250. * Return the name of this bot for debugging purposes
  251. */
  252. const char *INextBot::GetDebugIdentifier( void ) const
  253. {
  254. const int nameSize = 256;
  255. static char name[ nameSize ];
  256. Q_snprintf( name, nameSize, "%s(#%d)", const_cast< INextBot * >( this )->GetEntity()->GetClassname(), const_cast< INextBot * >( this )->GetEntity()->entindex() );
  257. return name;
  258. }
  259. //----------------------------------------------------------------------------------------------------------------
  260. /**
  261. * Return true if we match the given debug symbol
  262. */
  263. bool INextBot::IsDebugFilterMatch( const char *name ) const
  264. {
  265. // compare debug identifier
  266. if ( !Q_strnicmp( name, GetDebugIdentifier(), Q_strlen( name ) ) )
  267. {
  268. return true;
  269. }
  270. // compare team name
  271. CTeam *team = GetEntity()->GetTeam();
  272. if ( team && !Q_strnicmp( name, team->GetName(), Q_strlen( name ) ) )
  273. {
  274. return true;
  275. }
  276. return false;
  277. }
  278. //----------------------------------------------------------------------------------------------------------------
  279. /**
  280. * There are some things we never want to climb on
  281. */
  282. bool INextBot::IsAbleToClimbOnto( const CBaseEntity *object ) const
  283. {
  284. if ( object == NULL || !const_cast<CBaseEntity *>(object)->IsAIWalkable() )
  285. {
  286. return false;
  287. }
  288. // never climb onto doors
  289. if ( FClassnameIs( const_cast< CBaseEntity * >( object ), "prop_door*" ) || FClassnameIs( const_cast< CBaseEntity * >( object ), "func_door*" ) )
  290. {
  291. return false;
  292. }
  293. // ok to climb on this object
  294. return true;
  295. }
  296. //----------------------------------------------------------------------------------------------------------------
  297. /**
  298. * Can we break this object
  299. */
  300. bool INextBot::IsAbleToBreak( const CBaseEntity *object ) const
  301. {
  302. if ( object && object->m_takedamage == DAMAGE_YES )
  303. {
  304. if ( FClassnameIs( const_cast< CBaseEntity * >( object ), "func_breakable" ) &&
  305. object->GetHealth() )
  306. {
  307. return true;
  308. }
  309. if ( FClassnameIs( const_cast< CBaseEntity * >( object ), "func_breakable_surf" ) )
  310. {
  311. return true;
  312. }
  313. if ( dynamic_cast< const CBreakableProp * >( object ) != NULL )
  314. {
  315. return true;
  316. }
  317. }
  318. return false;
  319. }
  320. //----------------------------------------------------------------------------------------------------------
  321. void INextBot::DisplayDebugText( const char *text ) const
  322. {
  323. const_cast< INextBot * >( this )->GetEntity()->EntityText( m_debugDisplayLine++, text, 0.1 );
  324. }
  325. //--------------------------------------------------------------------------------------------------------
  326. void INextBot::DebugConColorMsg( NextBotDebugType debugType, const Color &color, const char *fmt, ... )
  327. {
  328. bool isDataFormatted = false;
  329. va_list argptr;
  330. char data[ MAX_NEXTBOT_DEBUG_LINE_LENGTH ];
  331. if ( developer.GetBool() && IsDebugging( debugType ) )
  332. {
  333. va_start(argptr, fmt);
  334. Q_vsnprintf(data, sizeof( data ), fmt, argptr);
  335. va_end(argptr);
  336. isDataFormatted = true;
  337. ConColorMsg( color, "%s", data );
  338. }
  339. if ( !NextBotDebugHistory.GetBool() )
  340. {
  341. if ( m_debugHistory.Count() )
  342. {
  343. ResetDebugHistory();
  344. }
  345. return;
  346. }
  347. // Don't bother with event data - it's spammy enough to overshadow everything else.
  348. if ( debugType == NEXTBOT_EVENTS )
  349. return;
  350. if ( !isDataFormatted )
  351. {
  352. va_start(argptr, fmt);
  353. Q_vsnprintf(data, sizeof( data ), fmt, argptr);
  354. va_end(argptr);
  355. isDataFormatted = true;
  356. }
  357. int lastLine = m_debugHistory.Count() - 1;
  358. if ( lastLine >= 0 )
  359. {
  360. NextBotDebugLineType *line = m_debugHistory[lastLine];
  361. if ( line->debugType == debugType && V_strstr( line->data, "\n" ) == NULL )
  362. {
  363. // append onto previous line
  364. V_strncat( line->data, data, MAX_NEXTBOT_DEBUG_LINE_LENGTH );
  365. return;
  366. }
  367. }
  368. // Prune out an old line if needed, keeping a pointer to re-use the memory
  369. NextBotDebugLineType *line = NULL;
  370. if ( m_debugHistory.Count() == MAX_NEXTBOT_DEBUG_HISTORY )
  371. {
  372. line = m_debugHistory[0];
  373. m_debugHistory.Remove( 0 );
  374. }
  375. // Add to debug history
  376. if ( !line )
  377. {
  378. line = new NextBotDebugLineType;
  379. }
  380. line->debugType = debugType;
  381. V_strncpy( line->data, data, MAX_NEXTBOT_DEBUG_LINE_LENGTH );
  382. m_debugHistory.AddToTail( line );
  383. }
  384. //--------------------------------------------------------------------------------------------------------
  385. // build a vector of debug history of the given types
  386. void INextBot::GetDebugHistory( unsigned int type, CUtlVector< const NextBotDebugLineType * > *lines ) const
  387. {
  388. if ( !lines )
  389. return;
  390. lines->RemoveAll();
  391. for ( int i=0; i<m_debugHistory.Count(); ++i )
  392. {
  393. NextBotDebugLineType *line = m_debugHistory[i];
  394. if ( line->debugType & type )
  395. {
  396. lines->AddToTail( line );
  397. }
  398. }
  399. }
  400. //--------------------------------------------------------------------------------------------------------
  401. void INextBot::UpdateImmobileStatus( void )
  402. {
  403. if ( m_immobileCheckTimer.IsElapsed() )
  404. {
  405. m_immobileCheckTimer.Start( 1.0f );
  406. // if we haven't moved farther than this in 1 second, we're immobile
  407. if ( ( GetEntity()->GetAbsOrigin() - m_immobileAnchor ).IsLengthGreaterThan( GetImmobileSpeedThreshold() ) )
  408. {
  409. // moved far enough, not immobile
  410. m_immobileAnchor = GetEntity()->GetAbsOrigin();
  411. m_immobileTimer.Invalidate();
  412. }
  413. else
  414. {
  415. // haven't escaped our anchor - we are immobile
  416. if ( !m_immobileTimer.HasStarted() )
  417. {
  418. m_immobileTimer.Start();
  419. }
  420. }
  421. }
  422. }