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.

881 lines
23 KiB

  1. // NextBotPlayerBody.cpp
  2. // Implementation of Body interface for CBasePlayer-derived classes
  3. // Author: Michael Booth, October 2006
  4. //========= Copyright Valve Corporation, All rights reserved. ============//
  5. #include "cbase.h"
  6. #include "NextBot.h"
  7. #include "NextBotPlayerBody.h"
  8. #include "NextBotPlayer.h"
  9. // memdbgon must be the last include file in a .cpp file!!!
  10. #include "tier0/memdbgon.h"
  11. ConVar nb_saccade_time( "nb_saccade_time", "0.1", FCVAR_CHEAT );
  12. ConVar nb_saccade_speed( "nb_saccade_speed", "1000", FCVAR_CHEAT );
  13. ConVar nb_head_aim_steady_max_rate( "nb_head_aim_steady_max_rate", "100", FCVAR_CHEAT );
  14. ConVar nb_head_aim_settle_duration( "nb_head_aim_settle_duration", "0.3", FCVAR_CHEAT );
  15. ConVar nb_head_aim_resettle_angle( "nb_head_aim_resettle_angle", "100", FCVAR_CHEAT, "After rotating through this angle, the bot pauses to 'recenter' its virtual mouse on its virtual mousepad" );
  16. ConVar nb_head_aim_resettle_time( "nb_head_aim_resettle_time", "0.3", FCVAR_CHEAT, "How long the bot pauses to 'recenter' its virtual mouse on its virtual mousepad" );
  17. //-----------------------------------------------------------------------------------------------
  18. /**
  19. * A useful reply for IBody::AimHeadTowards. When the
  20. * head is aiming on target, press the fire button.
  21. */
  22. void PressFireButtonReply::OnSuccess( INextBot *bot )
  23. {
  24. INextBotPlayerInput *playerInput = dynamic_cast< INextBotPlayerInput * >( bot->GetEntity() );
  25. if ( playerInput )
  26. {
  27. playerInput->PressFireButton();
  28. }
  29. }
  30. //-----------------------------------------------------------------------------------------------
  31. /**
  32. * A useful reply for IBody::AimHeadTowards. When the
  33. * head is aiming on target, press the alternate fire button.
  34. */
  35. void PressAltFireButtonReply::OnSuccess( INextBot *bot )
  36. {
  37. INextBotPlayerInput *playerInput = dynamic_cast< INextBotPlayerInput * >( bot->GetEntity() );
  38. if ( playerInput )
  39. {
  40. playerInput->PressMeleeButton();
  41. }
  42. }
  43. //-----------------------------------------------------------------------------------------------
  44. /**
  45. * A useful reply for IBody::AimHeadTowards. When the
  46. * head is aiming on target, press the jump button.
  47. */
  48. void PressJumpButtonReply::OnSuccess( INextBot *bot )
  49. {
  50. INextBotPlayerInput *playerInput = dynamic_cast< INextBotPlayerInput * >( bot->GetEntity() );
  51. if ( playerInput )
  52. {
  53. playerInput->PressJumpButton();
  54. }
  55. }
  56. //-----------------------------------------------------------------------------------------------
  57. //-----------------------------------------------------------------------------------------------
  58. PlayerBody::PlayerBody( INextBot *bot ) : IBody( bot )
  59. {
  60. m_player = static_cast< CBasePlayer * >( bot->GetEntity() );
  61. }
  62. //-----------------------------------------------------------------------------------------------
  63. PlayerBody::~PlayerBody()
  64. {
  65. }
  66. //-----------------------------------------------------------------------------------------------
  67. /**
  68. * reset to initial state
  69. */
  70. void PlayerBody::Reset( void )
  71. {
  72. m_posture = STAND;
  73. m_lookAtPos = vec3_origin;
  74. m_lookAtSubject = NULL;
  75. m_lookAtReplyWhenAimed = NULL;
  76. m_lookAtVelocity = vec3_origin;
  77. m_lookAtExpireTimer.Invalidate();
  78. m_lookAtPriority = BORING;
  79. m_lookAtExpireTimer.Invalidate();
  80. m_lookAtDurationTimer.Invalidate();
  81. m_isSightedIn = false;
  82. m_hasBeenSightedIn = false;
  83. m_headSteadyTimer.Invalidate();
  84. m_priorAngles = vec3_angle;
  85. m_anchorRepositionTimer.Invalidate();
  86. m_anchorForward = vec3_origin;
  87. }
  88. ConVar bot_mimic( "bot_mimic", "0", 0, "Bot uses usercmd of player by index." );
  89. //-----------------------------------------------------------------------------------------------
  90. /**
  91. * Update internal state.
  92. * Do this every tick to keep head aims smooth and accurate
  93. */
  94. void PlayerBody::Upkeep( void )
  95. {
  96. // If mimicking the player, don't modify the view angles.
  97. static ConVarRef bot_mimic( "bot_mimic" );
  98. if ( bot_mimic.IsValid() && bot_mimic.GetBool() )
  99. return;
  100. const float deltaT = gpGlobals->frametime;
  101. if ( deltaT < 0.00001f )
  102. {
  103. return;
  104. }
  105. CBasePlayer *player = ( CBasePlayer * )GetBot()->GetEntity();
  106. // get current view angles
  107. QAngle currentAngles = player->EyeAngles() + player->GetPunchAngle();
  108. // track when our head is "steady"
  109. bool isSteady = true;
  110. float actualPitchRate = AngleDiff( currentAngles.x, m_priorAngles.x );
  111. if ( abs( actualPitchRate ) > nb_head_aim_steady_max_rate.GetFloat() * deltaT )
  112. {
  113. isSteady = false;
  114. }
  115. else
  116. {
  117. float actualYawRate = AngleDiff( currentAngles.y, m_priorAngles.y );
  118. if ( abs( actualYawRate ) > nb_head_aim_steady_max_rate.GetFloat() * deltaT )
  119. {
  120. isSteady = false;
  121. }
  122. }
  123. if ( isSteady )
  124. {
  125. if ( !m_headSteadyTimer.HasStarted() )
  126. {
  127. m_headSteadyTimer.Start();
  128. }
  129. }
  130. else
  131. {
  132. m_headSteadyTimer.Invalidate();
  133. }
  134. if ( GetBot()->IsDebugging( NEXTBOT_LOOK_AT ) )
  135. {
  136. if ( IsHeadSteady() )
  137. {
  138. const float maxTime = 3.0f;
  139. float t = GetHeadSteadyDuration() / maxTime;
  140. t = clamp( t, 0.f, 1.0f );
  141. NDebugOverlay::Circle( player->EyePosition(), t * 10.0f, 0, 255, 0, 255, true, 2.0f * deltaT );
  142. }
  143. }
  144. m_priorAngles = currentAngles;
  145. // if our current look-at has expired, don't change our aim further
  146. if ( m_hasBeenSightedIn && m_lookAtExpireTimer.IsElapsed() )
  147. {
  148. return;
  149. }
  150. // simulate limited range of mouse movements
  151. // compute the angle change from "center"
  152. const Vector &forward = GetViewVector();
  153. float deltaAngle = RAD2DEG( acos( DotProduct( forward, m_anchorForward ) ) );
  154. if ( deltaAngle > nb_head_aim_resettle_angle.GetFloat() )
  155. {
  156. // time to recenter our 'virtual mouse'
  157. m_anchorRepositionTimer.Start( RandomFloat( 0.9f, 1.1f ) * nb_head_aim_resettle_time.GetFloat() );
  158. m_anchorForward = forward;
  159. return;
  160. }
  161. // if we're currently recentering our "virtual mouse", wait
  162. if ( m_anchorRepositionTimer.HasStarted() && !m_anchorRepositionTimer.IsElapsed() )
  163. {
  164. return;
  165. }
  166. m_anchorRepositionTimer.Invalidate();
  167. // if we have a subject, update lookat point
  168. CBaseEntity *subject = m_lookAtSubject;
  169. if ( subject )
  170. {
  171. if ( m_lookAtTrackingTimer.IsElapsed() )
  172. {
  173. // update subject tracking by periodically estimating linear aim velocity, allowing for "slop" between updates
  174. Vector desiredLookAtPos;
  175. if ( subject->MyCombatCharacterPointer() )
  176. {
  177. desiredLookAtPos = GetBot()->GetIntentionInterface()->SelectTargetPoint( GetBot(), subject->MyCombatCharacterPointer() );
  178. }
  179. else
  180. {
  181. desiredLookAtPos = subject->WorldSpaceCenter();
  182. }
  183. desiredLookAtPos += GetHeadAimSubjectLeadTime() * subject->GetAbsVelocity();
  184. Vector errorVector = desiredLookAtPos - m_lookAtPos;
  185. float error = errorVector.NormalizeInPlace();
  186. float trackingInterval = GetHeadAimTrackingInterval();
  187. if ( trackingInterval < deltaT )
  188. {
  189. trackingInterval = deltaT;
  190. }
  191. float errorVel = error / trackingInterval;
  192. m_lookAtVelocity = ( errorVel * errorVector ) + subject->GetAbsVelocity();
  193. m_lookAtTrackingTimer.Start( RandomFloat( 0.8f, 1.2f ) * trackingInterval );
  194. }
  195. m_lookAtPos += deltaT * m_lookAtVelocity;
  196. }
  197. // aim view towards last look at point
  198. Vector to = m_lookAtPos - GetEyePosition();
  199. to.NormalizeInPlace();
  200. QAngle desiredAngles;
  201. VectorAngles( to, desiredAngles );
  202. QAngle angles;
  203. if ( GetBot()->IsDebugging( NEXTBOT_LOOK_AT ) )
  204. {
  205. NDebugOverlay::Line( GetEyePosition(), GetEyePosition() + 100.0f * forward, 255, 255, 0, false, 2.0f * deltaT );
  206. float thickness = isSteady ? 2.0f : 3.0f;
  207. int r = m_isSightedIn ? 255 : 0;
  208. int g = subject ? 255 : 0;
  209. NDebugOverlay::HorzArrow( GetEyePosition(), m_lookAtPos, thickness, r, g, 255, 255, false, 2.0f * deltaT );
  210. }
  211. const float onTargetTolerance = 0.98f;
  212. float dot = DotProduct( forward, to );
  213. if ( dot > onTargetTolerance )
  214. {
  215. // on target
  216. m_isSightedIn = true;
  217. if ( !m_hasBeenSightedIn )
  218. {
  219. m_hasBeenSightedIn = true;
  220. if ( GetBot()->IsDebugging( NEXTBOT_LOOK_AT ) )
  221. {
  222. ConColorMsg( Color( 255, 100, 0, 255 ), "%3.2f: %s Look At SIGHTED IN\n",
  223. gpGlobals->curtime,
  224. m_player->GetPlayerName() );
  225. }
  226. }
  227. if ( m_lookAtReplyWhenAimed )
  228. {
  229. m_lookAtReplyWhenAimed->OnSuccess( GetBot() );
  230. m_lookAtReplyWhenAimed = NULL;
  231. }
  232. }
  233. else
  234. {
  235. // off target
  236. m_isSightedIn = false;
  237. }
  238. // rotate view at a rate proportional to how far we have to turn
  239. // max rate if we need to turn around
  240. // want first derivative continuity of rate as our aim hits to avoid pop
  241. float approachRate = GetMaxHeadAngularVelocity();
  242. const float easeOut = 0.7f;
  243. if ( dot > easeOut )
  244. {
  245. float t = RemapVal( dot, easeOut, 1.0f, 1.0f, 0.02f );
  246. const float halfPI = 1.57f;
  247. approachRate *= sin( halfPI * t );
  248. }
  249. const float easeInTime = 0.25f;
  250. if ( m_lookAtDurationTimer.GetElapsedTime() < easeInTime )
  251. {
  252. approachRate *= m_lookAtDurationTimer.GetElapsedTime() / easeInTime;
  253. }
  254. angles.y = ApproachAngle( desiredAngles.y, currentAngles.y, approachRate * deltaT );
  255. angles.x = ApproachAngle( desiredAngles.x, currentAngles.x, 0.5f * approachRate * deltaT );
  256. angles.z = 0.0f;
  257. // back out "punch angle"
  258. angles -= player->GetPunchAngle();
  259. angles.x = AngleNormalize( angles.x );
  260. angles.y = AngleNormalize( angles.y );
  261. player->SnapEyeAngles( angles );
  262. }
  263. //-----------------------------------------------------------------------------------------------
  264. bool PlayerBody::SetPosition( const Vector &pos )
  265. {
  266. m_player->SetAbsOrigin( pos );
  267. return true;
  268. }
  269. //-----------------------------------------------------------------------------------------------
  270. /**
  271. * Return the eye position of the bot in world coordinates
  272. */
  273. const Vector &PlayerBody::GetEyePosition( void ) const
  274. {
  275. m_eyePos = m_player->EyePosition();
  276. return m_eyePos;
  277. }
  278. CBaseEntity *PlayerBody::GetEntity( void )
  279. {
  280. return m_player;
  281. }
  282. //-----------------------------------------------------------------------------------------------
  283. /**
  284. * Return the view unit direction vector in world coordinates
  285. */
  286. const Vector &PlayerBody::GetViewVector( void ) const
  287. {
  288. m_player->EyeVectors( &m_viewVector );
  289. return m_viewVector;
  290. }
  291. //-----------------------------------------------------------------------------------------------
  292. /**
  293. * Aim the bot's head towards the given goal
  294. */
  295. void PlayerBody::AimHeadTowards( const Vector &lookAtPos, LookAtPriorityType priority, float duration, INextBotReply *replyWhenAimed, const char *reason )
  296. {
  297. if ( duration <= 0.0f )
  298. {
  299. duration = 0.1f;
  300. }
  301. // don't spaz our aim around
  302. if ( m_lookAtPriority == priority )
  303. {
  304. if ( !IsHeadSteady() || GetHeadSteadyDuration() < nb_head_aim_settle_duration.GetFloat() )
  305. {
  306. // we're still finishing a look-at at the same priority
  307. if ( replyWhenAimed )
  308. {
  309. replyWhenAimed->OnFail( GetBot(), INextBotReply::DENIED );
  310. }
  311. if ( GetBot()->IsDebugging( NEXTBOT_LOOK_AT ) )
  312. {
  313. ConColorMsg( Color( 255, 0, 0, 255 ), "%3.2f: %s Look At '%s' rejected - previous aim not %s\n",
  314. gpGlobals->curtime,
  315. m_player->GetPlayerName(),
  316. reason,
  317. IsHeadSteady() ? "settled long enough" : "head-steady" );
  318. }
  319. return;
  320. }
  321. }
  322. // don't short-circuit if "sighted in" to avoid rapid view jitter
  323. if ( m_lookAtPriority > priority && !m_lookAtExpireTimer.IsElapsed() )
  324. {
  325. // higher priority lookat still ongoing
  326. if ( replyWhenAimed )
  327. {
  328. replyWhenAimed->OnFail( GetBot(), INextBotReply::DENIED );
  329. }
  330. if ( GetBot()->IsDebugging( NEXTBOT_LOOK_AT ) )
  331. {
  332. ConColorMsg( Color( 255, 0, 0, 255 ), "%3.2f: %s Look At '%s' rejected - higher priority aim in progress\n",
  333. gpGlobals->curtime,
  334. m_player->GetPlayerName(),
  335. reason );
  336. }
  337. return;
  338. }
  339. if ( m_lookAtReplyWhenAimed )
  340. {
  341. // in-process aim was interrupted
  342. m_lookAtReplyWhenAimed->OnFail( GetBot(), INextBotReply::INTERRUPTED );
  343. }
  344. m_lookAtReplyWhenAimed = replyWhenAimed;
  345. m_lookAtExpireTimer.Start( duration );
  346. // if given the same point, just update priority
  347. const float epsilon = 1.0f;
  348. if ( ( m_lookAtPos - lookAtPos ).IsLengthLessThan( epsilon ) )
  349. {
  350. m_lookAtPriority = priority;
  351. return;
  352. }
  353. // new look-at point
  354. m_lookAtPos = lookAtPos;
  355. m_lookAtSubject = NULL;
  356. m_lookAtPriority = priority;
  357. m_lookAtDurationTimer.Start();
  358. // do NOT clear this here, or continuous calls to AimHeadTowards will keep IsHeadAimingOnTarget returning false all of the time
  359. // m_isSightedIn = false;
  360. m_hasBeenSightedIn = false;
  361. if ( GetBot()->IsDebugging( NEXTBOT_LOOK_AT ) )
  362. {
  363. NDebugOverlay::Cross3D( lookAtPos, 2.0f, 255, 255, 100, true, 2.0f * duration );
  364. const char *priName = "";
  365. switch( priority )
  366. {
  367. case BORING: priName = "BORING"; break;
  368. case INTERESTING: priName = "INTERESTING"; break;
  369. case IMPORTANT: priName = "IMPORTANT"; break;
  370. case CRITICAL: priName = "CRITICAL"; break;
  371. }
  372. ConColorMsg( Color( 255, 100, 0, 255 ), "%3.2f: %s Look At ( %g, %g, %g ) for %3.2f s, Pri = %s, Reason = %s\n",
  373. gpGlobals->curtime,
  374. m_player->GetPlayerName(),
  375. lookAtPos.x, lookAtPos.y, lookAtPos.z,
  376. duration,
  377. priName,
  378. ( reason ) ? reason : "" );
  379. }
  380. }
  381. //-----------------------------------------------------------------------------------------------
  382. /**
  383. * Aim the bot's head towards the given goal
  384. */
  385. void PlayerBody::AimHeadTowards( CBaseEntity *subject, LookAtPriorityType priority, float duration, INextBotReply *replyWhenAimed, const char *reason )
  386. {
  387. if ( duration <= 0.0f )
  388. {
  389. duration = 0.1f;
  390. }
  391. if ( subject == NULL )
  392. {
  393. return;
  394. }
  395. // don't spaz our aim around
  396. if ( m_lookAtPriority == priority )
  397. {
  398. if ( !IsHeadSteady() || GetHeadSteadyDuration() < nb_head_aim_settle_duration.GetFloat() )
  399. {
  400. // we're still finishing a look-at at the same priority
  401. if ( replyWhenAimed )
  402. {
  403. replyWhenAimed->OnFail( GetBot(), INextBotReply::DENIED );
  404. }
  405. if ( GetBot()->IsDebugging( NEXTBOT_LOOK_AT ) )
  406. {
  407. ConColorMsg( Color( 255, 0, 0, 255 ), "%3.2f: %s Look At '%s' rejected - previous aim not %s\n",
  408. gpGlobals->curtime,
  409. m_player->GetPlayerName(),
  410. reason,
  411. IsHeadSteady() ? "head-steady" : "settled long enough" );
  412. }
  413. return;
  414. }
  415. }
  416. // don't short-circuit if "sighted in" to avoid rapid view jitter
  417. if ( m_lookAtPriority > priority && !m_lookAtExpireTimer.IsElapsed() )
  418. {
  419. // higher priority lookat still ongoing
  420. if ( replyWhenAimed )
  421. {
  422. replyWhenAimed->OnFail( GetBot(), INextBotReply::DENIED );
  423. }
  424. if ( GetBot()->IsDebugging( NEXTBOT_LOOK_AT ) )
  425. {
  426. ConColorMsg( Color( 255, 0, 0, 255 ), "%3.2f: %s Look At '%s' rejected - higher priority aim in progress\n",
  427. gpGlobals->curtime,
  428. m_player->GetPlayerName(),
  429. reason );
  430. }
  431. return;
  432. }
  433. if ( m_lookAtReplyWhenAimed )
  434. {
  435. // in-process aim was interrupted
  436. m_lookAtReplyWhenAimed->OnFail( GetBot(), INextBotReply::INTERRUPTED );
  437. }
  438. m_lookAtReplyWhenAimed = replyWhenAimed;
  439. m_lookAtExpireTimer.Start( duration );
  440. // if given the same subject, just update priority
  441. if ( subject == m_lookAtSubject )
  442. {
  443. m_lookAtPriority = priority;
  444. return;
  445. }
  446. // new subject
  447. m_lookAtSubject = subject;
  448. #ifdef REFACTOR_FOR_CLIENT_SIDE_EYE_TRACKING
  449. CBasePlayer *pMyPlayer = static_cast< CBasePlayer * >( GetEntity() );
  450. if ( subject->IsPlayer() )
  451. {
  452. // looking at a player, look at their eye position
  453. TerrorPlayer *pMyTarget = ToTerrorPlayer( subject );
  454. m_lookAtPos = subject->EyePosition();
  455. if(pMyPlayer)
  456. {
  457. pMyPlayer->SetLookatPlayer( pMyTarget );
  458. }
  459. }
  460. else
  461. {
  462. // not looking at a player
  463. m_lookAtPos = subject->WorldSpaceCenter();
  464. if(pMyPlayer)
  465. {
  466. pMyPlayer->SetLookatPlayer( NULL );
  467. }
  468. }
  469. #endif
  470. m_lookAtPriority = priority;
  471. m_lookAtDurationTimer.Start();
  472. // do NOT clear this here, or continuous calls to AimHeadTowards will keep IsHeadAimingOnTarget returning false all of the time
  473. // m_isSightedIn = false;
  474. m_hasBeenSightedIn = false;
  475. if ( GetBot()->IsDebugging( NEXTBOT_LOOK_AT ) )
  476. {
  477. NDebugOverlay::Cross3D( m_lookAtPos, 2.0f, 100, 100, 100, true, duration );
  478. const char *priName = "";
  479. switch( priority )
  480. {
  481. case BORING: priName = "BORING"; break;
  482. case INTERESTING: priName = "INTERESTING"; break;
  483. case IMPORTANT: priName = "IMPORTANT"; break;
  484. case CRITICAL: priName = "CRITICAL"; break;
  485. }
  486. ConColorMsg( Color( 255, 100, 0, 255 ), "%3.2f: %s Look At subject %s for %3.2f s, Pri = %s, Reason = %s\n",
  487. gpGlobals->curtime,
  488. m_player->GetPlayerName(),
  489. subject->GetClassname(),
  490. duration,
  491. priName,
  492. ( reason ) ? reason : "" );
  493. }
  494. }
  495. //-----------------------------------------------------------------------------------------------
  496. /**
  497. * Return true if head is not rapidly turning to look somewhere else
  498. */
  499. bool PlayerBody::IsHeadSteady( void ) const
  500. {
  501. return m_headSteadyTimer.HasStarted();
  502. }
  503. //-----------------------------------------------------------------------------------------------
  504. /**
  505. * Return the duration that the bot's head has been on-target
  506. */
  507. float PlayerBody::GetHeadSteadyDuration( void ) const
  508. {
  509. // return ( IsHeadAimingOnTarget() ) ? m_headSteadyTimer.GetElapsedTime() : 0.0f;
  510. return m_headSteadyTimer.HasStarted() ? m_headSteadyTimer.GetElapsedTime() : 0.0f;
  511. }
  512. //-----------------------------------------------------------------------------------------------
  513. // Clear out currently pending replyWhenAimed callback
  514. void PlayerBody::ClearPendingAimReply( void )
  515. {
  516. m_lookAtReplyWhenAimed = NULL;
  517. }
  518. //-----------------------------------------------------------------------------------------------
  519. float PlayerBody::GetMaxHeadAngularVelocity( void ) const
  520. {
  521. return nb_saccade_speed.GetFloat();
  522. }
  523. //-----------------------------------------------------------------------------------------------
  524. bool PlayerBody::StartActivity( Activity act, unsigned int flags )
  525. {
  526. // player animation state is controlled on the client
  527. return false;
  528. }
  529. //-----------------------------------------------------------------------------------------------
  530. /**
  531. * Return currently animating activity
  532. */
  533. Activity PlayerBody::GetActivity( void ) const
  534. {
  535. return ACT_INVALID;
  536. }
  537. //-----------------------------------------------------------------------------------------------
  538. /**
  539. * Return true if currently animating activity matches the given one
  540. */
  541. bool PlayerBody::IsActivity( Activity act ) const
  542. {
  543. return false;
  544. }
  545. //-----------------------------------------------------------------------------------------------
  546. /**
  547. * Return true if currently animating activity has any of the given flags
  548. */
  549. bool PlayerBody::HasActivityType( unsigned int flags ) const
  550. {
  551. return false;
  552. }
  553. //-----------------------------------------------------------------------------------------------
  554. /**
  555. * Request a posture change
  556. */
  557. void PlayerBody::SetDesiredPosture( PostureType posture )
  558. {
  559. m_posture = posture;
  560. }
  561. //-----------------------------------------------------------------------------------------------
  562. /**
  563. * Get posture body is trying to assume
  564. */
  565. IBody::PostureType PlayerBody::GetDesiredPosture( void ) const
  566. {
  567. return m_posture;
  568. }
  569. //-----------------------------------------------------------------------------------------------
  570. /**
  571. * Return true if body is trying to assume this posture
  572. */
  573. bool PlayerBody::IsDesiredPosture( PostureType posture ) const
  574. {
  575. return ( posture == m_posture );
  576. }
  577. //-----------------------------------------------------------------------------------------------
  578. /**
  579. * Return true if body's actual posture matches its desired posture
  580. */
  581. bool PlayerBody::IsInDesiredPosture( void ) const
  582. {
  583. return true;
  584. }
  585. //-----------------------------------------------------------------------------------------------
  586. /**
  587. * Return body's current actual posture
  588. */
  589. IBody::PostureType PlayerBody::GetActualPosture( void ) const
  590. {
  591. return m_posture;
  592. }
  593. //-----------------------------------------------------------------------------------------------
  594. /**
  595. * Return true if body is actually in the given posture
  596. */
  597. bool PlayerBody::IsActualPosture( PostureType posture ) const
  598. {
  599. return ( posture == m_posture );
  600. }
  601. //-----------------------------------------------------------------------------------------------
  602. /**
  603. * Return true if body's current posture allows it to move around the world
  604. */
  605. bool PlayerBody::IsPostureMobile( void ) const
  606. {
  607. return true;
  608. }
  609. //-----------------------------------------------------------------------------------------------
  610. /**
  611. * Return true if body's posture is in the process of changing to new posture
  612. */
  613. bool PlayerBody::IsPostureChanging( void ) const
  614. {
  615. return false;
  616. }
  617. //-----------------------------------------------------------------------------------------------
  618. /**
  619. * Arousal level change
  620. */
  621. void PlayerBody::SetArousal( ArousalType arousal )
  622. {
  623. m_arousal = arousal;
  624. }
  625. //-----------------------------------------------------------------------------------------------
  626. /**
  627. * Get arousal level
  628. */
  629. IBody::ArousalType PlayerBody::GetArousal( void ) const
  630. {
  631. return m_arousal;
  632. }
  633. //-----------------------------------------------------------------------------------------------
  634. /**
  635. * Return true if body is at this arousal level
  636. */
  637. bool PlayerBody::IsArousal( ArousalType arousal ) const
  638. {
  639. return ( arousal == m_arousal );
  640. }
  641. //-----------------------------------------------------------------------------------------------
  642. /**
  643. * Width of bot's collision hull in XY plane
  644. */
  645. float PlayerBody::GetHullWidth( void ) const
  646. {
  647. return VEC_HULL_MAX_SCALED( m_player ).x - VEC_HULL_MIN_SCALED( m_player ).x;
  648. }
  649. //-----------------------------------------------------------------------------------------------
  650. /**
  651. * Height of bot's current collision hull based on posture
  652. */
  653. float PlayerBody::GetHullHeight( void ) const
  654. {
  655. if ( m_posture == CROUCH )
  656. {
  657. return GetCrouchHullHeight();
  658. }
  659. return GetStandHullHeight();
  660. }
  661. //-----------------------------------------------------------------------------------------------
  662. /**
  663. * Height of bot's collision hull when standing
  664. */
  665. float PlayerBody::GetStandHullHeight( void ) const
  666. {
  667. return VEC_HULL_MAX_SCALED( m_player ).z - VEC_HULL_MIN_SCALED( m_player ).z;
  668. }
  669. //-----------------------------------------------------------------------------------------------
  670. /**
  671. * Height of bot's collision hull when crouched
  672. */
  673. float PlayerBody::GetCrouchHullHeight( void ) const
  674. {
  675. return VEC_DUCK_HULL_MAX_SCALED( m_player ).z - VEC_DUCK_HULL_MIN_SCALED( m_player ).z;
  676. }
  677. //-----------------------------------------------------------------------------------------------
  678. /**
  679. * Return current collision hull minimums based on actual body posture
  680. */
  681. const Vector &PlayerBody::GetHullMins( void ) const
  682. {
  683. if ( m_posture == CROUCH )
  684. {
  685. m_hullMins = VEC_DUCK_HULL_MIN_SCALED( m_player );
  686. }
  687. else
  688. {
  689. m_hullMins = VEC_HULL_MIN_SCALED( m_player );
  690. }
  691. return m_hullMins;
  692. }
  693. //-----------------------------------------------------------------------------------------------
  694. /**
  695. * Return current collision hull maximums based on actual body posture
  696. */
  697. const Vector &PlayerBody::GetHullMaxs( void ) const
  698. {
  699. if ( m_posture == CROUCH )
  700. {
  701. m_hullMaxs = VEC_DUCK_HULL_MAX_SCALED( m_player );
  702. }
  703. else
  704. {
  705. m_hullMaxs = VEC_HULL_MAX_SCALED( m_player );
  706. }
  707. return m_hullMaxs;
  708. }
  709. //-----------------------------------------------------------------------------------------------
  710. /**
  711. * Return the bot's collision mask (hack until we get a general hull trace abstraction here or in the locomotion interface)
  712. */
  713. unsigned int PlayerBody::GetSolidMask( void ) const
  714. {
  715. return ( m_player ) ? m_player->PlayerSolidMask() : MASK_PLAYERSOLID;
  716. }