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.

1033 lines
28 KiB

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