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.

1047 lines
28 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "animation.h" // for NOMOTION
  9. #include "ai_motor.h"
  10. #include "ai_navigator.h"
  11. #include "ai_basenpc.h"
  12. #include "ai_localnavigator.h"
  13. #include "ai_moveprobe.h"
  14. #include "saverestore_utlvector.h"
  15. // memdbgon must be the last include file in a .cpp file!!!
  16. #include "tier0/memdbgon.h"
  17. #ifdef DEBUG
  18. ConVar ai_draw_motor_movement( "ai_draw_motor_movement","0" );
  19. #endif
  20. extern float GetFloorZ(const Vector &origin);
  21. //-----------------------------------------------------------------------------
  22. // Use these functions to set breakpoints to find out where movement is failing
  23. #ifdef DEBUG
  24. void DebugNoteMovementFailure()
  25. {
  26. }
  27. // a place to put breakpoints
  28. #pragma warning(push)
  29. #pragma warning(disable:4189)
  30. AIMoveResult_t DbgResult( AIMoveResult_t result )
  31. {
  32. if ( result < AIMR_OK )
  33. {
  34. int breakHere = 1;
  35. }
  36. switch ( result )
  37. {
  38. case AIMR_BLOCKED_ENTITY:
  39. return AIMR_BLOCKED_ENTITY;
  40. case AIMR_BLOCKED_WORLD:
  41. return AIMR_BLOCKED_WORLD;
  42. case AIMR_BLOCKED_NPC:
  43. return AIMR_BLOCKED_NPC;
  44. case AIMR_ILLEGAL:
  45. return AIMR_ILLEGAL;
  46. case AIMR_OK:
  47. return AIMR_OK;
  48. case AIMR_CHANGE_TYPE:
  49. return AIMR_CHANGE_TYPE;
  50. };
  51. return AIMR_ILLEGAL;
  52. };
  53. #endif
  54. //-----------------------------------------------------------------------------
  55. //
  56. // class CAI_Motor
  57. //
  58. BEGIN_SIMPLE_DATADESC( CAI_Motor )
  59. // m_flMoveInterval (think transient)
  60. DEFINE_FIELD( m_IdealYaw, FIELD_FLOAT ),
  61. DEFINE_FIELD( m_YawSpeed, FIELD_FLOAT ),
  62. DEFINE_FIELD( m_vecVelocity, FIELD_VECTOR ),
  63. DEFINE_FIELD( m_vecAngularVelocity, FIELD_VECTOR ),
  64. DEFINE_FIELD( m_nDismountSequence, FIELD_INTEGER ),
  65. DEFINE_FIELD( m_vecDismount, FIELD_VECTOR ),
  66. DEFINE_UTLVECTOR( m_facingQueue, FIELD_EMBEDDED ),
  67. DEFINE_FIELD( m_bYawLocked, FIELD_BOOLEAN ),
  68. // m_pMoveProbe
  69. END_DATADESC()
  70. //-----------------------------------------------------------------------------
  71. CAI_Motor::CAI_Motor(CAI_BaseNPC *pOuter)
  72. : CAI_Component( pOuter )
  73. {
  74. m_flMoveInterval = 0;
  75. m_IdealYaw = 0;
  76. m_YawSpeed = 0;
  77. m_vecVelocity = Vector( 0, 0, 0 );
  78. m_pMoveProbe = NULL;
  79. m_bYawLocked = false;
  80. }
  81. //-----------------------------------------------------------------------------
  82. CAI_Motor::~CAI_Motor()
  83. {
  84. }
  85. //-----------------------------------------------------------------------------
  86. void CAI_Motor::Init( IAI_MovementSink *pMovementServices )
  87. {
  88. CAI_ProxyMovementSink::Init( pMovementServices );
  89. m_pMoveProbe = GetOuter()->GetMoveProbe(); // @TODO (toml 03-30-03): this is a "bad" way to grab this pointer. Components should have an explcit "init" phase.
  90. }
  91. //-----------------------------------------------------------------------------
  92. // Step iteratively toward a destination position
  93. //-----------------------------------------------------------------------------
  94. AIMotorMoveResult_t CAI_Motor::MoveGroundStep( const Vector &newPos, CBaseEntity *pMoveTarget, float yaw, bool bAsFarAsCan, bool bTestZ, AIMoveTrace_t *pTraceResult )
  95. {
  96. // By definition, this will produce different results than GroundMoveLimit()
  97. // because there's no guarantee that it will step exactly one step
  98. // See how far toward the new position we can step...
  99. // But don't actually test for ground geometric validity;
  100. // if it isn't valid, there's not much we can do about it
  101. AIMoveTrace_t moveTrace;
  102. unsigned testFlags = AITGM_IGNORE_FLOOR;
  103. char *pchHackBoolToInt = (char*)(&bTestZ);
  104. if ( *pchHackBoolToInt == 2 )
  105. {
  106. testFlags |= AITGM_CRAWL_LARGE_STEPS;
  107. }
  108. else
  109. {
  110. if ( !bTestZ )
  111. testFlags |= AITGM_2D;
  112. }
  113. #ifdef DEBUG
  114. if ( ai_draw_motor_movement.GetBool() )
  115. testFlags |= AITGM_DRAW_RESULTS;
  116. #endif
  117. GetMoveProbe()->TestGroundMove( GetLocalOrigin(), newPos, GetOuter()->GetAITraceMask(), testFlags, &moveTrace );
  118. if ( pTraceResult )
  119. {
  120. *pTraceResult = moveTrace;
  121. }
  122. bool bHitTarget = (moveTrace.pObstruction && (pMoveTarget == moveTrace.pObstruction ));
  123. // Move forward either if there was no obstruction or if we're told to
  124. // move as far as we can, regardless
  125. bool bIsBlocked = IsMoveBlocked(moveTrace.fStatus);
  126. if ( !bIsBlocked || bAsFarAsCan || bHitTarget )
  127. {
  128. #ifdef DEBUG
  129. if ( GetMoveProbe()->CheckStandPosition( GetLocalOrigin(), GetOuter()->GetAITraceMask() ) && !GetMoveProbe()->CheckStandPosition( moveTrace.vEndPosition, GetOuter()->GetAITraceMask() ) )
  130. {
  131. DevMsg( 2, "Warning: AI motor probably given invalid instructions\n" );
  132. }
  133. #endif
  134. // The true argument here causes it to touch all triggers
  135. // in the volume swept from the previous position to the current position
  136. UTIL_SetOrigin(GetOuter(), moveTrace.vEndPosition, true);
  137. // check to see if our ground entity has changed
  138. // NOTE: This is to detect changes in ground entity as the movement code has optimized out
  139. // ground checks. So now we have to do a simple recheck to make sure we detect when we've
  140. // stepped onto a new entity.
  141. if ( GetOuter()->GetFlags() & FL_ONGROUND )
  142. {
  143. GetOuter()->PhysicsStepRecheckGround();
  144. }
  145. // skip tiny steps, but notify the shadow object of any large steps
  146. if ( moveTrace.flStepUpDistance > 0.1f )
  147. {
  148. float height = clamp( moveTrace.flStepUpDistance, 0, StepHeight() );
  149. IPhysicsObject *pPhysicsObject = GetOuter()->VPhysicsGetObject();
  150. if ( pPhysicsObject )
  151. {
  152. IPhysicsShadowController *pShadow = pPhysicsObject->GetShadowController();
  153. if ( pShadow )
  154. {
  155. pShadow->StepUp( height );
  156. }
  157. }
  158. }
  159. if ( yaw != -1 )
  160. {
  161. QAngle angles = GetLocalAngles();
  162. angles.y = yaw;
  163. SetLocalAngles( angles );
  164. }
  165. if ( bHitTarget )
  166. return AIM_PARTIAL_HIT_TARGET;
  167. if ( !bIsBlocked )
  168. return AIM_SUCCESS;
  169. if ( moveTrace.fStatus == AIMR_BLOCKED_NPC )
  170. return AIM_PARTIAL_HIT_NPC;
  171. return AIM_PARTIAL_HIT_WORLD;
  172. }
  173. return AIM_FAILED;
  174. }
  175. //-----------------------------------------------------------------------------
  176. // Purpose: Motion for climbing
  177. // Input :
  178. // Output : Returns bits (MoveStatus_b) regarding the move status
  179. //-----------------------------------------------------------------------------
  180. void CAI_Motor::MoveClimbStart( const Vector &climbDest, const Vector &climbDir, float climbDist, float yaw )
  181. {
  182. // @Note (toml 06-11-02): the following code is somewhat suspect. It
  183. // originated in CAI_BaseNPC::MoveClimb() from early June 2002
  184. // At the very least, state should be restored to original, not
  185. // slammed.
  186. //
  187. // -----Original Message-----
  188. // From: Jay Stelly
  189. // Sent: Monday, June 10, 2002 3:57 PM
  190. // To: Tom Leonard
  191. // Subject: RE:
  192. //
  193. // yes.
  194. //
  195. // Also, there is some subtlety to using movetype. I think in
  196. // general we want to keep things in MOVETYPE_STEP because it
  197. // implies a bunch of things in the external game physics
  198. // simulator. There is a flag FL_FLY we use to
  199. // disable gravity on MOVETYPE_STEP characters.
  200. //
  201. // > -----Original Message-----
  202. // > From: Tom Leonard
  203. // > Sent: Monday, June 10, 2002 3:55 PM
  204. // > To: Jay Stelly
  205. // > Subject:
  206. // >
  207. // > Should I worry at all that the following highlighted bits of
  208. // > code are not reciprocal for all state, and furthermore, stomp
  209. // > other state?
  210. if ( fabsf( climbDir.z ) < .1 )
  211. {
  212. SetActivity( GetNavigator()->GetMovementActivity() );
  213. }
  214. else
  215. {
  216. SetActivity( (climbDir.z > -0.01 ) ? ACT_CLIMB_UP : ACT_CLIMB_DOWN );
  217. }
  218. m_nDismountSequence = SelectWeightedSequence( ACT_CLIMB_DISMOUNT );
  219. if (m_nDismountSequence != ACT_INVALID)
  220. {
  221. GetOuter()->GetSequenceLinearMotion( m_nDismountSequence, &m_vecDismount );
  222. }
  223. else
  224. {
  225. m_vecDismount.Init();
  226. }
  227. GetOuter()->AddFlag( FL_FLY ); // No gravity
  228. SetSolid( SOLID_BBOX );
  229. SetGravity( 0.0 );
  230. SetGroundEntity( NULL );
  231. }
  232. AIMoveResult_t CAI_Motor::MoveClimbExecute( const Vector &climbDest, const Vector &climbDir, float climbDist, float yaw, int climbNodesLeft )
  233. {
  234. if ( fabsf( climbDir.z ) > .1 )
  235. {
  236. if ( GetActivity() != ACT_CLIMB_DISMOUNT )
  237. {
  238. Activity desiredActivity = (climbDir.z > -0.01 ) ? ACT_CLIMB_UP : ACT_CLIMB_DOWN;
  239. if ( GetActivity() != desiredActivity )
  240. {
  241. SetActivity( desiredActivity );
  242. }
  243. }
  244. if ( GetActivity() != ACT_CLIMB_UP && GetActivity() != ACT_CLIMB_DOWN && GetActivity() != ACT_CLIMB_DISMOUNT )
  245. {
  246. DevMsg( "Climber not in a climb activity!\n" );
  247. return AIMR_ILLEGAL;
  248. }
  249. if (m_nDismountSequence != ACT_INVALID)
  250. {
  251. if (GetActivity() == ACT_CLIMB_UP )
  252. {
  253. if (climbNodesLeft <= 2 && climbDist < fabs( m_vecDismount.z ))
  254. {
  255. // fixme: No other way to force m_nIdealSequence?
  256. GetOuter()->SetActivity( ACT_CLIMB_DISMOUNT );
  257. GetOuter()->SetCycle( GetOuter()->GetMovementFrame( m_vecDismount.z - climbDist ) );
  258. }
  259. }
  260. }
  261. }
  262. float climbSpeed = GetOuter()->GetInstantaneousVelocity();
  263. if (m_nDismountSequence != ACT_INVALID)
  264. {
  265. // catch situations where the climb mount/dismount finished before reaching goal
  266. climbSpeed = MAX( climbSpeed, 30.0 );
  267. }
  268. else
  269. {
  270. // FIXME: assume if they don't have a dismount animation then they probably don't really support climbing.
  271. climbSpeed = 100.0;
  272. }
  273. SetSmoothedVelocity( climbDir * climbSpeed );
  274. if ( climbDist < climbSpeed * GetMoveInterval() )
  275. {
  276. if (climbDist <= 1e-2)
  277. climbDist = 0;
  278. const float climbTime = climbDist / climbSpeed;
  279. SetMoveInterval( GetMoveInterval() - climbTime );
  280. SetLocalOrigin( climbDest );
  281. return AIMR_CHANGE_TYPE;
  282. }
  283. else
  284. {
  285. SetMoveInterval( 0 );
  286. }
  287. // --------------------------------------------
  288. // Turn to face the climb
  289. // --------------------------------------------
  290. SetIdealYawAndUpdate( yaw );
  291. return AIMR_OK;
  292. }
  293. void CAI_Motor::MoveClimbStop()
  294. {
  295. if ( GetNavigator()->GetMovementActivity() > ACT_RESET )
  296. SetActivity( GetNavigator()->GetMovementActivity() );
  297. else
  298. SetActivity( ACT_IDLE );
  299. GetOuter()->RemoveFlag( FL_FLY );
  300. SetSmoothedVelocity( vec3_origin );
  301. SetGravity( 1.0 );
  302. }
  303. //-----------------------------------------------------------------------------
  304. // Purpose: Motion for jumping
  305. // Input :
  306. // Output : Returns bits (MoveStatus_b) regarding the move status
  307. //-----------------------------------------------------------------------------
  308. void CAI_Motor::MoveJumpStart( const Vector &velocity )
  309. {
  310. // take the npc off the ground and throw them in the air
  311. SetSmoothedVelocity( velocity );
  312. SetGravity( GetOuter()->GetJumpGravity() );
  313. SetGroundEntity( NULL );
  314. SetActivity( ACT_JUMP );
  315. SetIdealYawAndUpdate( velocity );
  316. }
  317. int CAI_Motor::MoveJumpExecute( )
  318. {
  319. // needs to detect being hit
  320. UpdateYaw( );
  321. if (GetOuter()->GetActivity() == ACT_JUMP && GetOuter()->IsActivityFinished())
  322. {
  323. SetActivity( ACT_GLIDE );
  324. }
  325. // use all the time
  326. SetMoveInterval( 0 );
  327. return AIMR_OK;
  328. }
  329. AIMoveResult_t CAI_Motor::MoveJumpStop()
  330. {
  331. SetSmoothedVelocity( Vector(0,0,0) );
  332. if (GetOuter()->GetActivity() == ACT_GLIDE)
  333. {
  334. float flTime = GetOuter()->GetGroundChangeTime();
  335. GetOuter()->AddStepDiscontinuity( flTime, GetAbsOrigin(), GetAbsAngles() );
  336. if ( SelectWeightedSequence( ACT_LAND ) == ACT_INVALID )
  337. return AIMR_CHANGE_TYPE;
  338. SetActivity( ACT_LAND );
  339. // FIXME: find out why the client doesn't interpolate immediatly after sequence change
  340. // GetOuter()->SetCycle( flTime - gpGlobals->curtime );
  341. }
  342. if (GetOuter()->GetActivity() != ACT_LAND || GetOuter()->IsActivityFinished())
  343. {
  344. return AIMR_CHANGE_TYPE;
  345. }
  346. SetMoveInterval( 0 );
  347. SetGravity( 1.0f );
  348. return AIMR_OK;
  349. }
  350. //-----------------------------------------------------------------------------
  351. float CAI_Motor::GetIdealSpeed() const
  352. {
  353. return GetOuter()->GetIdealSpeed();
  354. }
  355. float CAI_Motor::GetIdealAccel() const
  356. {
  357. return GetOuter()->GetIdealAccel();
  358. }
  359. //-----------------------------------------------------------------------------
  360. // how far will I go?
  361. float CAI_Motor::MinStoppingDist( float flMinResult )
  362. {
  363. // FIXME: should this be a constant rate or a constant time like it is now?
  364. float flDecelRate = GetIdealAccel();
  365. if (flDecelRate > 0.0)
  366. {
  367. // assuming linear deceleration, how long till my V hits 0?
  368. float t = GetCurSpeed() / flDecelRate;
  369. // and how far will I travel? (V * t - 1/2 A t^2)
  370. float flDist = GetCurSpeed() * t - 0.5 * flDecelRate * t * t;
  371. // this should always be some reasonable non-zero distance
  372. if (flDist > flMinResult)
  373. return flDist;
  374. return flMinResult;
  375. }
  376. return flMinResult;
  377. }
  378. //-----------------------------------------------------------------------------
  379. // Purpose: how fast should I be going ideally
  380. //-----------------------------------------------------------------------------
  381. float CAI_Motor::IdealVelocity( void )
  382. {
  383. // FIXME: this should be a per-entity setting so run speeds are not based on animation speeds
  384. return GetIdealSpeed() * GetPlaybackRate();
  385. }
  386. //-----------------------------------------------------------------------------
  387. void CAI_Motor::ResetMoveCalculations()
  388. {
  389. }
  390. //-----------------------------------------------------------------------------
  391. void CAI_Motor::MoveStart()
  392. {
  393. }
  394. //-----------------------------------------------------------------------------
  395. void CAI_Motor::MoveStop()
  396. {
  397. memset( &m_vecVelocity, 0, sizeof(m_vecVelocity) );
  398. GetOuter()->GetLocalNavigator()->ResetMoveCalculations();
  399. }
  400. //-----------------------------------------------------------------------------
  401. void CAI_Motor::MovePaused()
  402. {
  403. }
  404. //-----------------------------------------------------------------------------
  405. // Purpose: what linear accel/decel rate do I need to hit V1 in d distance?
  406. //-----------------------------------------------------------------------------
  407. float DeltaV( float v0, float v1, float d )
  408. {
  409. return 0.5 * (v1 * v1 - v0 * v0 ) / d;
  410. }
  411. //-----------------------------------------------------------------------------
  412. float CAI_Motor::CalcIntervalMove()
  413. {
  414. // assuming linear acceleration, how far will I travel?
  415. return 0.5 * (GetCurSpeed() + GetIdealSpeed()) * GetMoveInterval();
  416. }
  417. //-----------------------------------------------------------------------------
  418. // Purpose: Move the npc to the next location on its route.
  419. // Input : vecDir - Normalized vector indicating the direction of movement.
  420. // flDistance - distance to move
  421. // flInterval - Time interval for this movement.
  422. // flGoalDistance - target distance
  423. // flGoalVelocity - target velocity
  424. //-----------------------------------------------------------------------------
  425. AIMotorMoveResult_t CAI_Motor::MoveGroundExecute( const AILocalMoveGoal_t &move, AIMoveTrace_t *pTraceResult )
  426. {
  427. // --------------------------------------------
  428. // turn in the direction of movement
  429. // --------------------------------------------
  430. MoveFacing( move );
  431. // --------------------------------------------
  432. return MoveGroundExecuteWalk( move, GetIdealSpeed(), CalcIntervalMove(), pTraceResult );
  433. }
  434. AIMotorMoveResult_t CAI_Motor::MoveGroundExecuteWalk( const AILocalMoveGoal_t &move, float speed, float dist, AIMoveTrace_t *pTraceResult )
  435. {
  436. bool bReachingLocalGoal = ( dist > move.maxDist );
  437. // can I move farther in this interval than I'm supposed to?
  438. if ( bReachingLocalGoal )
  439. {
  440. if ( !(move.flags & AILMG_CONSUME_INTERVAL) )
  441. {
  442. // only use a portion of the time interval
  443. SetMoveInterval( GetMoveInterval() * (1 - move.maxDist / dist) );
  444. }
  445. else
  446. SetMoveInterval( 0 );
  447. dist = move.maxDist;
  448. }
  449. else
  450. {
  451. // use all the time
  452. SetMoveInterval( 0 );
  453. }
  454. SetMoveVel( move.dir * speed );
  455. // --------------------------------------------
  456. // walk the distance
  457. // --------------------------------------------
  458. AIMotorMoveResult_t result = AIM_SUCCESS;
  459. if ( dist > 0.0 )
  460. {
  461. Vector vecFrom = GetLocalOrigin();
  462. Vector vecTo = vecFrom + move.dir * dist;
  463. if ( move.navType == NAV_CRAWL )
  464. {
  465. char *pchHackBoolToInt = (char*)(&bReachingLocalGoal);
  466. *pchHackBoolToInt = 2;
  467. }
  468. result = MoveGroundStep( vecTo, move.pMoveTarget, -1, true, bReachingLocalGoal, pTraceResult );
  469. if ( result == AIM_FAILED )
  470. MoveStop();
  471. }
  472. else if ( !OnMoveStalled( move ) )
  473. {
  474. result = AIM_FAILED;
  475. }
  476. return result;
  477. }
  478. //-----------------------------------------------------------------------------
  479. // Purpose: Move the npc to the next location on its route.
  480. // Input : pTargetEnt -
  481. // vecDir - Normalized vector indicating the direction of movement.
  482. // flInterval - Time interval for this movement.
  483. //-----------------------------------------------------------------------------
  484. AIMotorMoveResult_t CAI_Motor::MoveFlyExecute( const AILocalMoveGoal_t &move, AIMoveTrace_t *pTraceResult )
  485. {
  486. // turn in the direction of movement
  487. MoveFacing( move );
  488. // calc accel/decel rates
  489. float flNewSpeed = GetIdealSpeed();
  490. SetMoveVel( move.dir * flNewSpeed );
  491. float flTotal = 0.5 * (GetCurSpeed() + flNewSpeed) * GetMoveInterval();
  492. float distance = move.maxDist;
  493. // can I move farther in this interval than I'm supposed to?
  494. if (flTotal > distance)
  495. {
  496. // only use a portion of the time interval
  497. SetMoveInterval( GetMoveInterval() * (1 - distance / flTotal) );
  498. flTotal = distance;
  499. }
  500. else
  501. {
  502. // use all the time
  503. SetMoveInterval( 0 );
  504. }
  505. Vector vecStart, vecEnd;
  506. vecStart = GetLocalOrigin();
  507. VectorMA( vecStart, flTotal, move.dir, vecEnd );
  508. AIMoveTrace_t moveTrace;
  509. GetMoveProbe()->MoveLimit( NAV_FLY, vecStart, vecEnd, GetOuter()->GetAITraceMask(), NULL, &moveTrace );
  510. if ( pTraceResult )
  511. *pTraceResult = moveTrace;
  512. // Check for total blockage
  513. if (fabs(moveTrace.flDistObstructed - flTotal) <= 1e-1)
  514. {
  515. // But if we bumped into our target, then we succeeded!
  516. if ( move.pMoveTarget && (moveTrace.pObstruction == move.pMoveTarget) )
  517. return AIM_PARTIAL_HIT_TARGET;
  518. return AIM_FAILED;
  519. }
  520. // The true argument here causes it to touch all triggers
  521. // in the volume swept from the previous position to the current position
  522. UTIL_SetOrigin(GetOuter(), moveTrace.vEndPosition, true);
  523. return (IsMoveBlocked(moveTrace.fStatus)) ? AIM_PARTIAL_HIT_WORLD : AIM_SUCCESS;
  524. }
  525. //-----------------------------------------------------------------------------
  526. // Purpose: turn in the direction of movement
  527. // Output :
  528. //-----------------------------------------------------------------------------
  529. void CAI_Motor::MoveFacing( const AILocalMoveGoal_t &move )
  530. {
  531. if ( GetOuter()->OverrideMoveFacing( move, GetMoveInterval() ) )
  532. return;
  533. // required movement direction
  534. float flMoveYaw = UTIL_VecToYaw( move.dir );
  535. int nSequence = GetSequence();
  536. float fSequenceMoveYaw = GetSequenceMoveYaw( nSequence );
  537. if ( fSequenceMoveYaw == NOMOTION )
  538. {
  539. fSequenceMoveYaw = 0;
  540. }
  541. if (!HasPoseParameter( nSequence, GetOuter()->LookupPoseMoveYaw() ))
  542. {
  543. SetIdealYawAndUpdate( UTIL_AngleMod( flMoveYaw - fSequenceMoveYaw ) );
  544. }
  545. else
  546. {
  547. // FIXME: move this up to navigator so that path goals can ignore these overrides.
  548. Vector dir;
  549. float flInfluence = GetFacingDirection( dir );
  550. dir = move.facing * (1 - flInfluence) + dir * flInfluence;
  551. VectorNormalize( dir );
  552. // ideal facing direction
  553. float idealYaw = UTIL_AngleMod( UTIL_VecToYaw( dir ) );
  554. // FIXME: facing has important max velocity issues
  555. SetIdealYawAndUpdate( idealYaw );
  556. // find movement direction to compensate for not being turned far enough
  557. float flDiff = UTIL_AngleDiff( flMoveYaw, GetLocalAngles().y );
  558. SetPoseParameter( GetOuter()->LookupPoseMoveYaw(), flDiff );
  559. /*
  560. if ((GetOuter()->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT))
  561. {
  562. DevMsg( "move %.1f : diff %.1f : ideal %.1f\n", flMoveYaw, flDiff, m_IdealYaw );
  563. }
  564. */
  565. }
  566. }
  567. //-----------------------------------------------------------------------------
  568. // Purpose: Set the ideal yaw and run the current or specified timestep
  569. // worth of rotation.
  570. //-----------------------------------------------------------------------------
  571. void CAI_Motor::SetIdealYawAndUpdate( float idealYaw, float yawSpeed)
  572. {
  573. SetIdealYaw( idealYaw );
  574. if (yawSpeed == AI_CALC_YAW_SPEED)
  575. RecalculateYawSpeed();
  576. else if (yawSpeed != AI_KEEP_YAW_SPEED)
  577. SetYawSpeed( yawSpeed );
  578. UpdateYaw(-1);
  579. }
  580. //-----------------------------------------------------------------------------
  581. void CAI_Motor::RecalculateYawSpeed()
  582. {
  583. SetYawSpeed( CalcYawSpeed() );
  584. }
  585. //-----------------------------------------------------------------------------
  586. float AI_ClampYaw( float yawSpeedPerSec, float current, float target, float time )
  587. {
  588. if (current != target)
  589. {
  590. float speed = yawSpeedPerSec * time;
  591. float move = target - current;
  592. if (target > current)
  593. {
  594. if (move >= 180)
  595. move = move - 360;
  596. }
  597. else
  598. {
  599. if (move <= -180)
  600. move = move + 360;
  601. }
  602. if (move > 0)
  603. {// turning to the npc's left
  604. if (move > speed)
  605. move = speed;
  606. }
  607. else
  608. {// turning to the npc's right
  609. if (move < -speed)
  610. move = -speed;
  611. }
  612. return UTIL_AngleMod(current + move);
  613. }
  614. return target;
  615. }
  616. //-----------------------------------------------------------------------------
  617. // Purpose: Turns a npc towards its ideal yaw.
  618. // Input : yawSpeed - Yaw speed in degrees per 1/10th of a second.
  619. // flInterval - Time interval to turn, -1 uses time since last think.
  620. // Output : Returns the number of degrees turned.
  621. //-----------------------------------------------------------------------------
  622. void CAI_Motor::UpdateYaw( int yawSpeed )
  623. {
  624. // Don't do this if our yaw is locked
  625. if ( IsYawLocked() )
  626. return;
  627. GetOuter()->SetUpdatedYaw();
  628. float ideal, current, newYaw;
  629. if ( yawSpeed == -1 )
  630. yawSpeed = GetYawSpeed();
  631. // NOTE: GetIdealYaw() will never exactly be reached because UTIL_AngleMod
  632. // also truncates the angle to 16 bits of resolution. So lets truncate it here.
  633. current = UTIL_AngleMod( GetLocalAngles().y );
  634. ideal = UTIL_AngleMod( GetIdealYaw() );
  635. // FIXME: this needs a proper interval
  636. float dt = MIN( 0.2, gpGlobals->curtime - GetLastThink() );
  637. newYaw = AI_ClampYaw( (float)yawSpeed * 10.0, current, ideal, dt );
  638. if (newYaw != current)
  639. {
  640. QAngle angles = GetLocalAngles();
  641. angles.y = newYaw;
  642. SetLocalAngles( angles );
  643. }
  644. }
  645. //=========================================================
  646. // DeltaIdealYaw - returns the difference ( in degrees ) between
  647. // npc's current yaw and ideal_yaw
  648. //
  649. // Positive result is left turn, negative is right turn
  650. //=========================================================
  651. float CAI_Motor::DeltaIdealYaw ( void )
  652. {
  653. float flCurrentYaw;
  654. flCurrentYaw = UTIL_AngleMod( GetLocalAngles().y );
  655. if ( flCurrentYaw == GetIdealYaw() )
  656. {
  657. return 0;
  658. }
  659. return UTIL_AngleDiff( GetIdealYaw(), flCurrentYaw );
  660. }
  661. //-----------------------------------------------------------------------------
  662. void CAI_Motor::SetIdealYawToTarget( const Vector &target, float noise, float offset )
  663. {
  664. float base = CalcIdealYaw( target );
  665. base += offset;
  666. if ( noise > 0 )
  667. {
  668. noise *= 0.5;
  669. base += random->RandomFloat( -noise, noise );
  670. if ( base < 0 )
  671. base += 360;
  672. else if ( base >= 360 )
  673. base -= 360;
  674. }
  675. SetIdealYaw( base );
  676. }
  677. //-----------------------------------------------------------------------------
  678. void CAI_Motor::SetIdealYawToTargetAndUpdate( const Vector &target, float yawSpeed )
  679. {
  680. SetIdealYawAndUpdate( CalcIdealYaw( target ), yawSpeed );
  681. }
  682. //-----------------------------------------------------------------------------
  683. // Purpose: Keep track of multiple objects that the npc is interested in facing
  684. //-----------------------------------------------------------------------------
  685. void CAI_Motor::AddFacingTarget( CBaseEntity *pTarget, float flImportance, float flDuration, float flRamp )
  686. {
  687. m_facingQueue.Add( pTarget, flImportance, flDuration, flRamp );
  688. }
  689. void CAI_Motor::AddFacingTarget( const Vector &vecPosition, float flImportance, float flDuration, float flRamp )
  690. {
  691. m_facingQueue.Add( vecPosition, flImportance, flDuration, flRamp );
  692. }
  693. void CAI_Motor::AddFacingTarget( CBaseEntity *pTarget, const Vector &vecPosition, float flImportance, float flDuration, float flRamp )
  694. {
  695. m_facingQueue.Add( pTarget, vecPosition, flImportance, flDuration, flRamp );
  696. }
  697. float CAI_Motor::GetFacingDirection( Vector &vecDir )
  698. {
  699. float flTotalInterest = 0.0;
  700. vecDir = Vector( 0, 0, 0 );
  701. int i;
  702. // clean up facing targets
  703. for (i = 0; i < m_facingQueue.Count();)
  704. {
  705. if (!m_facingQueue[i].IsActive())
  706. {
  707. m_facingQueue.Remove( i );
  708. }
  709. else
  710. {
  711. i++;
  712. }
  713. }
  714. for (i = 0; i < m_facingQueue.Count(); i++)
  715. {
  716. float flInterest = m_facingQueue[i].Interest( );
  717. Vector tmp = m_facingQueue[i].GetPosition() - GetAbsOrigin();
  718. // NDebugOverlay::Line( m_facingQueue[i].GetPosition(), GetAbsOrigin(), 255, 0, 0, false, 0.1 );
  719. VectorNormalize( tmp );
  720. vecDir = vecDir * (1 - flInterest) + tmp * flInterest;
  721. flTotalInterest = (1 - (1 - flTotalInterest) * (1 - flInterest));
  722. VectorNormalize( vecDir );
  723. }
  724. return flTotalInterest;
  725. }
  726. //-----------------------------------------------------------------------------
  727. AIMoveResult_t CAI_Motor::MoveNormalExecute( const AILocalMoveGoal_t &move )
  728. {
  729. AI_PROFILE_SCOPE(CAI_Motor_MoveNormalExecute);
  730. // --------------------------------
  731. AIMotorMoveResult_t fMotorResult;
  732. AIMoveTrace_t moveTrace;
  733. if ( move.navType == NAV_GROUND || move.navType == NAV_CRAWL )
  734. {
  735. fMotorResult = MoveGroundExecute( move, &moveTrace );
  736. }
  737. else
  738. {
  739. Assert( move.navType == NAV_FLY );
  740. fMotorResult = MoveFlyExecute( move, &moveTrace );
  741. }
  742. static AIMoveResult_t moveResults[] =
  743. {
  744. AIMR_ILLEGAL, // AIM_FAILED
  745. AIMR_OK, // AIM_SUCCESS
  746. AIMR_BLOCKED_NPC, // AIM_PARTIAL_HIT_NPC
  747. AIMR_BLOCKED_WORLD, // AIM_PARTIAL_HIT_WORLD
  748. AIMR_BLOCKED_WORLD, // AIM_PARTIAL_HIT_TARGET
  749. };
  750. Assert( ARRAYSIZE( moveResults ) == AIM_NUM_RESULTS && fMotorResult >= 0 && fMotorResult <= ARRAYSIZE( moveResults ) );
  751. AIMoveResult_t result = moveResults[fMotorResult];
  752. if ( result != AIMR_OK )
  753. {
  754. OnMoveExecuteFailed( move, moveTrace, fMotorResult, &result );
  755. SetMoveInterval( 0 ); // always consume interval on failure, even if overridden by OnMoveExecuteFailed()
  756. }
  757. return DbgResult( result );
  758. }
  759. //-----------------------------------------------------------------------------
  760. // Purpose: Look ahead my stopping distance, or at least my hull width
  761. //-----------------------------------------------------------------------------
  762. float CAI_Motor::MinCheckDist( void )
  763. {
  764. // Take the groundspeed into account
  765. float flMoveDist = GetMoveInterval() * GetIdealSpeed();
  766. float flMinDist = MAX( MinStoppingDist(), flMoveDist);
  767. if ( flMinDist < GetHullWidth() )
  768. flMinDist = GetHullWidth();
  769. return flMinDist;
  770. }
  771. //-----------------------------------------------------------------------------
  772. CAI_Navigator *CAI_Motor::GetNavigator( void )
  773. {
  774. return GetOuter()->GetNavigator();
  775. }
  776. int CAI_Motor::SelectWeightedSequence ( Activity activity )
  777. {
  778. return GetOuter()->SelectWeightedSequence ( activity );
  779. }
  780. float CAI_Motor::GetSequenceGroundSpeed( int iSequence )
  781. {
  782. return GetOuter()->GetSequenceGroundSpeed( iSequence );
  783. }
  784. //-----------------------------------------------------------------------------
  785. void CAI_Motor::SetSmoothedVelocity(const Vector &vecVelocity)
  786. {
  787. GetOuter()->SetAbsVelocity(vecVelocity);
  788. }
  789. Vector CAI_Motor::GetSmoothedVelocity()
  790. {
  791. return GetOuter()->GetSmoothedVelocity();
  792. }
  793. float CAI_Motor::StepHeight() const
  794. {
  795. return GetOuter()->StepHeight();
  796. }
  797. bool CAI_Motor::CanStandOn( CBaseEntity *pSurface ) const
  798. {
  799. return GetOuter()->CanStandOn( pSurface );
  800. }
  801. float CAI_Motor::CalcIdealYaw( const Vector &vecTarget )
  802. {
  803. return GetOuter()->CalcIdealYaw( vecTarget );
  804. }
  805. float CAI_Motor::SetBoneController( int iController, float flValue )
  806. {
  807. return GetOuter()->SetBoneController( iController, flValue );
  808. }
  809. float CAI_Motor::GetSequenceMoveYaw( int iSequence )
  810. {
  811. return GetOuter()->GetSequenceMoveYaw( iSequence );
  812. }
  813. void CAI_Motor::SetPlaybackRate( float flRate )
  814. {
  815. return GetOuter()->SetPlaybackRate( flRate );
  816. }
  817. float CAI_Motor::GetPlaybackRate() const
  818. {
  819. return GetOuter()->GetPlaybackRate();
  820. }
  821. float CAI_Motor::SetPoseParameter( const char *szName, float flValue )
  822. {
  823. return GetOuter()->SetPoseParameter( szName, flValue );
  824. }
  825. float CAI_Motor::GetPoseParameter( const char *szName )
  826. {
  827. return GetOuter()->GetPoseParameter( szName );
  828. }
  829. bool CAI_Motor::HasPoseParameter( int iSequence, const char *szName )
  830. {
  831. return GetOuter()->HasPoseParameter( iSequence, szName );
  832. }
  833. float CAI_Motor::SetPoseParameter( int iParameter, float flValue )
  834. {
  835. return GetOuter()->SetPoseParameter( iParameter, flValue );
  836. }
  837. bool CAI_Motor::HasPoseParameter( int iSequence, int iParameter )
  838. {
  839. return GetOuter()->HasPoseParameter( iSequence, iParameter );
  840. }
  841. void CAI_Motor::SetMoveType( MoveType_t val, MoveCollide_t moveCollide )
  842. {
  843. GetOuter()->SetMoveType( val, moveCollide );
  844. }
  845. //=============================================================================