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.

537 lines
14 KiB

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