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.

4280 lines
120 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. // @TODO (toml 06-26-02): The entry points in this file need to be organized
  7. //=============================================================================//
  8. #include "cbase.h"
  9. #include <float.h> // for FLT_MAX
  10. #include "animation.h" // for NOMOTION
  11. #include "collisionutils.h"
  12. #include "ndebugoverlay.h"
  13. #include "isaverestore.h"
  14. #include "saverestore_utlvector.h"
  15. #include "ai_navigator.h"
  16. #include "ai_node.h"
  17. #include "ai_route.h"
  18. #include "ai_routedist.h"
  19. #include "ai_waypoint.h"
  20. #include "ai_pathfinder.h"
  21. #include "ai_link.h"
  22. #include "ai_memory.h"
  23. #include "ai_motor.h"
  24. #include "ai_localnavigator.h"
  25. #include "ai_moveprobe.h"
  26. #include "ai_hint.h"
  27. #include "BasePropDoor.h"
  28. #include "props.h"
  29. #include "physics_npc_solver.h"
  30. // memdbgon must be the last include file in a .cpp file!!!
  31. #include "tier0/memdbgon.h"
  32. const char * g_ppszGoalTypes[] =
  33. {
  34. "GOALTYPE_NONE",
  35. "GOALTYPE_TARGETENT",
  36. "GOALTYPE_ENEMY",
  37. "GOALTYPE_PATHCORNER",
  38. "GOALTYPE_LOCATION",
  39. "GOALTYPE_LOCATION_NEAREST_NODE",
  40. "GOALTYPE_FLANK",
  41. "GOALTYPE_COVER",
  42. };
  43. #define AIGetGoalTypeText(type) (g_ppszGoalTypes[(type)])
  44. ConVar ai_vehicle_avoidance("ai_vehicle_avoidance", "1", FCVAR_CHEAT );
  45. #ifdef DEBUG_AI_NAVIGATION
  46. ConVar ai_debug_nav("ai_debug_nav", "0");
  47. #endif
  48. #ifdef DEBUG
  49. ConVar ai_test_nav_failure_handling("ai_test_nav_failure_handling", "0");
  50. int g_PathFailureCounter;
  51. int g_MoveFailureCounter;
  52. #define ShouldTestFailPath() ( ai_test_nav_failure_handling.GetBool() && g_PathFailureCounter++ % 20 == 0 )
  53. #define ShouldTestFailMove() ( ai_test_nav_failure_handling.GetBool() && g_MoveFailureCounter++ % 100 == 0 )
  54. #else
  55. #define ShouldTestFailPath() (0)
  56. #define ShouldTestFailMove() (0)
  57. #endif
  58. //-----------------------------------------------------------------------------
  59. #ifdef DEBUG
  60. bool g_fTestSteering = 0;
  61. #endif
  62. //-----------------------------------------------------------------------------
  63. class CAI_NavInHintGroupFilter : public INearestNodeFilter
  64. {
  65. public:
  66. CAI_NavInHintGroupFilter( string_t iszGroup = NULL_STRING ) :
  67. m_iszGroup( iszGroup )
  68. {
  69. }
  70. bool IsValid( CAI_Node *pNode )
  71. {
  72. if ( !pNode->GetHint() )
  73. {
  74. return false;
  75. }
  76. if ( pNode->GetHint()->GetGroup() != m_iszGroup )
  77. {
  78. return false;
  79. }
  80. return true;
  81. }
  82. bool ShouldContinue()
  83. {
  84. return true;
  85. }
  86. string_t m_iszGroup;
  87. };
  88. //-----------------------------------------------------------------------------
  89. const Vector AIN_NO_DEST( FLT_MAX, FLT_MAX, FLT_MAX );
  90. #define NavVecToString(v) ((v == AIN_NO_DEST) ? "AIN_NO_DEST" : VecToString(v))
  91. #define FLIER_CUT_CORNER_DIST 16 // 8 means the npc's bounding box is contained without the box of the node in WC
  92. #define NAV_STOP_MOVING_TOLERANCE 6 // Goal tolerance for TASK_STOP_MOVING stopping paths
  93. //-----------------------------------------------------------------------------
  94. // class CAI_Navigator
  95. //-----------------------------------------------------------------------------
  96. BEGIN_SIMPLE_DATADESC( CAI_Navigator )
  97. DEFINE_FIELD( m_navType, FIELD_INTEGER ),
  98. // m_pMotor
  99. // m_pMoveProbe
  100. // m_pLocalNavigator
  101. // m_pAINetwork
  102. DEFINE_EMBEDDEDBYREF( m_pPath ),
  103. // m_pClippedWaypoints (not saved)
  104. // m_flTimeClipped (not saved)
  105. // m_PreviousMoveActivity (not saved)
  106. // m_PreviousArrivalActivity (not saved)
  107. DEFINE_FIELD( m_bValidateActivitySpeed, FIELD_BOOLEAN ),
  108. DEFINE_FIELD( m_bCalledStartMove, FIELD_BOOLEAN ),
  109. DEFINE_FIELD( m_fNavComplete, FIELD_BOOLEAN ),
  110. DEFINE_FIELD( m_bNotOnNetwork, FIELD_BOOLEAN ),
  111. DEFINE_FIELD( m_bLastNavFailed, FIELD_BOOLEAN ),
  112. DEFINE_FIELD( m_flNextSimplifyTime, FIELD_TIME ),
  113. DEFINE_FIELD( m_bForcedSimplify, FIELD_BOOLEAN ),
  114. DEFINE_FIELD( m_flLastSuccessfulSimplifyTime, FIELD_TIME ),
  115. DEFINE_FIELD( m_flTimeLastAvoidanceTriangulate, FIELD_TIME ),
  116. DEFINE_FIELD( m_timePathRebuildMax, FIELD_FLOAT ),
  117. DEFINE_FIELD( m_timePathRebuildDelay, FIELD_FLOAT ),
  118. DEFINE_FIELD( m_timePathRebuildFail, FIELD_TIME ),
  119. DEFINE_FIELD( m_timePathRebuildNext, FIELD_TIME ),
  120. DEFINE_FIELD( m_fRememberStaleNodes, FIELD_BOOLEAN ),
  121. DEFINE_FIELD( m_bNoPathcornerPathfinds, FIELD_BOOLEAN ),
  122. // m_fPeerMoveWait (think transient)
  123. // m_hPeerWaitingOn (peer move fields do not need to be saved, tied to current schedule and path, which are not saved)
  124. // m_PeerWaitMoveTimer (ibid)
  125. // m_PeerWaitClearTimer(ibid)
  126. // m_NextSidestepTimer (ibid)
  127. DEFINE_FIELD( m_hBigStepGroundEnt, FIELD_EHANDLE ),
  128. DEFINE_FIELD( m_hLastBlockingEnt, FIELD_EHANDLE ),
  129. // m_vPosBeginFailedSteer (reset on load)
  130. // m_timeBeginFailedSteer (reset on load)
  131. // m_nNavFailCounter (reset on load)
  132. // m_flLastNavFailTime (reset on load)
  133. END_DATADESC()
  134. //-----------------------------------------------------------------------------
  135. CAI_Navigator::CAI_Navigator(CAI_BaseNPC *pOuter)
  136. : BaseClass(pOuter)
  137. {
  138. m_pPath = new CAI_Path;
  139. m_pAINetwork = NULL;
  140. m_bNotOnNetwork = false;
  141. m_flNextSimplifyTime = 0;
  142. m_flLastSuccessfulSimplifyTime = -1;
  143. m_pClippedWaypoints = new CAI_WaypointList;
  144. m_flTimeClipped = -1;
  145. m_bValidateActivitySpeed = true;
  146. m_bCalledStartMove = false;
  147. // ----------------------------
  148. m_navType = NAV_GROUND;
  149. m_fNavComplete = false;
  150. m_bLastNavFailed = false;
  151. // ----------------------------
  152. m_PeerWaitMoveTimer.Set(0.25); // 2 thinks
  153. m_PeerWaitClearTimer.Set(3.0);
  154. m_NextSidestepTimer.Set(5.0);
  155. m_vPosBeginFailedSteer = vec3_invalid;
  156. m_timeBeginFailedSteer = FLT_MAX;
  157. m_flTimeLastAvoidanceTriangulate = -1;
  158. // ----------------------------
  159. m_bNoPathcornerPathfinds = false;
  160. m_fRememberStaleNodes = true;
  161. m_pMotor = NULL;
  162. m_pMoveProbe = NULL;
  163. m_pLocalNavigator = NULL;
  164. m_nNavFailCounter = 0;
  165. m_flLastNavFailTime = -1;
  166. }
  167. //-----------------------------------------------------------------------------
  168. void CAI_Navigator::Init( CAI_Network *pNetwork )
  169. {
  170. m_pMotor = GetOuter()->GetMotor();
  171. m_pMoveProbe = GetOuter()->GetMoveProbe();
  172. m_pLocalNavigator = GetOuter()->GetLocalNavigator();
  173. m_pAINetwork = pNetwork;
  174. }
  175. //-----------------------------------------------------------------------------
  176. CAI_Navigator::~CAI_Navigator()
  177. {
  178. delete m_pPath;
  179. m_pClippedWaypoints->RemoveAll();
  180. delete m_pClippedWaypoints;
  181. }
  182. //-----------------------------------------------------------------------------
  183. const short AI_NAVIGATOR_SAVE_VERSION = 1;
  184. void CAI_Navigator::Save( ISave &save )
  185. {
  186. save.WriteShort( &AI_NAVIGATOR_SAVE_VERSION );
  187. CUtlVector<AI_Waypoint_t> minPathArray;
  188. AI_Waypoint_t *pCurWaypoint = GetPath()->GetCurWaypoint();
  189. if ( pCurWaypoint )
  190. {
  191. if ( ( pCurWaypoint->NavType() == NAV_CLIMB || pCurWaypoint->NavType() == NAV_JUMP ) )
  192. {
  193. CAI_WaypointList minCompletionPath;
  194. if ( GetStoppingPath( &minCompletionPath ) && !minCompletionPath.IsEmpty() )
  195. {
  196. AI_Waypoint_t *pCurrent = minCompletionPath.GetLast();
  197. while ( pCurrent )
  198. {
  199. minPathArray.AddToTail( *pCurrent );
  200. pCurrent = pCurrent->GetPrev();
  201. }
  202. minCompletionPath.RemoveAll();
  203. }
  204. }
  205. }
  206. SaveUtlVector( &save, &minPathArray, FIELD_EMBEDDED );
  207. }
  208. //-----------------------------------------------------------------------------
  209. void CAI_Navigator::Restore( IRestore &restore )
  210. {
  211. short version = restore.ReadShort();
  212. if ( version != AI_NAVIGATOR_SAVE_VERSION )
  213. return;
  214. CUtlVector<AI_Waypoint_t> minPathArray;
  215. RestoreUtlVector( &restore, &minPathArray, FIELD_EMBEDDED );
  216. if ( minPathArray.Count() )
  217. {
  218. for ( int i = 0; i < minPathArray.Count(); i++ )
  219. {
  220. m_pClippedWaypoints->PrependWaypoint( minPathArray[i].GetPos(), minPathArray[i].NavType(), ( minPathArray[i].Flags() & ~bits_WP_TO_PATHCORNER ), minPathArray[i].flYaw );
  221. }
  222. m_flTimeClipped = gpGlobals->curtime + 1000; // time passes between restore and onrestore
  223. }
  224. }
  225. //-----------------------------------------------------------------------------
  226. bool CAI_Navigator::ActivityIsLocomotive( Activity activity )
  227. {
  228. // FIXME: should be calling HasMovement() for a sequence
  229. return ( activity > ACT_IDLE );
  230. }
  231. //-----------------------------------------------------------------------------
  232. CAI_Pathfinder *CAI_Navigator::GetPathfinder()
  233. {
  234. return GetOuter()->GetPathfinder();
  235. }
  236. //-----------------------------------------------------------------------------
  237. const CAI_Pathfinder *CAI_Navigator::GetPathfinder() const
  238. {
  239. return GetOuter()->GetPathfinder();
  240. }
  241. //-----------------------------------------------------------------------------
  242. CBaseEntity *CAI_Navigator::GetNavTargetEntity()
  243. {
  244. if ( GetGoalType() == GOALTYPE_ENEMY || GetGoalType() == GOALTYPE_TARGETENT )
  245. return GetOuter()->GetNavTargetEntity();
  246. return GetPath()->GetTarget();
  247. }
  248. //-----------------------------------------------------------------------------
  249. void CAI_Navigator::TaskMovementComplete()
  250. {
  251. GetOuter()->TaskMovementComplete();
  252. }
  253. //-----------------------------------------------------------------------------
  254. float CAI_Navigator::MaxYawSpeed()
  255. {
  256. return GetOuter()->MaxYawSpeed();
  257. }
  258. //-----------------------------------------------------------------------------
  259. void CAI_Navigator::SetSpeed( float fl )
  260. {
  261. GetOuter()->m_flSpeed = fl;
  262. }
  263. //-----------------------------------------------------------------------------
  264. bool CAI_Navigator::FindPath( const AI_NavGoal_t &goal, unsigned flags )
  265. {
  266. CAI_Path *pPath = GetPath();
  267. MARK_TASK_EXPENSIVE();
  268. // Clear out previous state
  269. if ( flags & AIN_CLEAR_PREVIOUS_STATE )
  270. pPath->Clear();
  271. else if ( flags & AIN_CLEAR_TARGET )
  272. pPath->ClearTarget();
  273. // Set the activity
  274. if ( goal.activity != AIN_DEF_ACTIVITY )
  275. pPath->SetMovementActivity( goal.activity );
  276. else if ( pPath->GetMovementActivity() == ACT_INVALID )
  277. pPath->SetMovementActivity( ( GetOuter()->GetState() == NPC_STATE_COMBAT ) ? ACT_RUN : ACT_WALK );
  278. // Set the tolerance
  279. if ( goal.tolerance == AIN_HULL_TOLERANCE )
  280. pPath->SetGoalTolerance( GetHullWidth() );
  281. else if ( goal.tolerance != AIN_DEF_TOLERANCE )
  282. pPath->SetGoalTolerance( goal.tolerance );
  283. else if (pPath->GetGoalTolerance() == 0 )
  284. pPath->SetGoalTolerance( GetOuter()->GetDefaultNavGoalTolerance() );
  285. if (pPath->GetGoalTolerance() < 0.1 )
  286. DevMsg( GetOuter(), "Suspicious navigation goal tolerance specified\n");
  287. pPath->SetWaypointTolerance( GetHullWidth() * 0.5 );
  288. pPath->SetGoalType( GOALTYPE_NONE ); // avoids a spurious warning about setting the goal type twice
  289. pPath->SetGoalType( goal.type );
  290. pPath->SetGoalFlags( goal.flags );
  291. CBaseEntity *pPathTarget = goal.pTarget;
  292. if ((goal.type == GOALTYPE_TARGETENT) || (goal.type == GOALTYPE_ENEMY))
  293. {
  294. // Guarantee that the path target
  295. if (goal.type == GOALTYPE_TARGETENT)
  296. pPathTarget = GetTarget();
  297. else
  298. pPathTarget = GetEnemy();
  299. Assert( goal.pTarget == AIN_DEF_TARGET || goal.pTarget == pPathTarget );
  300. // Set the goal offset
  301. if ( goal.dest != AIN_NO_DEST )
  302. pPath->SetTargetOffset( goal.dest );
  303. // We're not setting the goal position here because
  304. // it's going to be computed + set in DoFindPath.
  305. }
  306. else
  307. {
  308. // When our goaltype is position based, we have to set
  309. // the goal position here because it won't get set during DoFindPath().
  310. if ( goal.dest != AIN_NO_DEST )
  311. pPath->ResetGoalPosition( goal.dest );
  312. else if ( goal.destNode != AIN_NO_NODE )
  313. pPath->ResetGoalPosition( GetNodePos( goal.destNode ) );
  314. }
  315. if ( pPathTarget > AIN_DEF_TARGET )
  316. {
  317. pPath->SetTarget( pPathTarget );
  318. }
  319. pPath->ClearWaypoints();
  320. bool result = FindPath( ( flags & AIN_NO_PATH_TASK_FAIL ) == 0 );
  321. if ( result == false )
  322. {
  323. if ( flags & AIN_DISCARD_IF_FAIL )
  324. pPath->Clear();
  325. else
  326. pPath->SetGoalType( GOALTYPE_NONE );
  327. }
  328. else
  329. {
  330. if ( goal.arrivalActivity != AIN_DEF_ACTIVITY && goal.arrivalActivity > ACT_RESET )
  331. {
  332. pPath->SetArrivalActivity( goal.arrivalActivity );
  333. }
  334. else if ( goal.arrivalSequence != -1 )
  335. {
  336. pPath->SetArrivalSequence( goal.arrivalSequence );
  337. }
  338. // Initialize goal facing direction
  339. // FIXME: This is a poor way to initialize the arrival direction, apps calling SetGoal()
  340. // should do this themselves, and/or it should be part of AI_NavGoal_t
  341. // FIXME: A number of calls to SetGoal() building routes to their enemy but don't set the flags!
  342. if (goal.type == GOALTYPE_ENEMY)
  343. {
  344. pPath->SetGoalDirection( pPathTarget );
  345. pPath->SetGoalSpeed( pPathTarget );
  346. }
  347. else
  348. {
  349. pPath->SetGoalDirection( pPath->ActualGoalPosition() - GetAbsOrigin() );
  350. }
  351. }
  352. return result;
  353. }
  354. ConVar ai_navigator_generate_spikes( "ai_navigator_generate_spikes", "0" );
  355. ConVar ai_navigator_generate_spikes_strength( "ai_navigator_generate_spikes_strength", "8" );
  356. //-----------------------------------------------------------------------------
  357. bool CAI_Navigator::SetGoal( const AI_NavGoal_t &goal, unsigned flags )
  358. {
  359. // Queue this up if we're in the middle of a frame
  360. if ( PostFrameNavigationSystem()->IsGameFrameRunning() )
  361. {
  362. // Send off the query for queuing
  363. PostFrameNavigationSystem()->EnqueueEntityNavigationQuery( GetOuter(), CreateFunctor( this, &CAI_Navigator::SetGoal, RefToVal( goal ), flags ) );
  364. // Complete immediately if we're waiting on that
  365. // FIXME: This will probably cause a lot of subtle little nuisances...
  366. if ( ( flags & AIN_NO_PATH_TASK_FAIL ) == 0 || GetOuter()->IsCurTaskContinuousMove() )
  367. {
  368. TaskComplete();
  369. }
  370. // For now, always succeed -- we need to deal with failures on the next frame
  371. return true;
  372. }
  373. CAI_Path *pPath = GetPath();
  374. OnNewGoal();
  375. // Clear out previous state
  376. if ( flags & AIN_CLEAR_PREVIOUS_STATE )
  377. {
  378. ClearPath();
  379. }
  380. if ( GetOuter()->IsCurTaskContinuousMove() || ai_post_frame_navigation.GetBool() )
  381. {
  382. flags |= AIN_NO_PATH_TASK_FAIL;
  383. }
  384. bool result = FindPath( goal, flags );
  385. if ( result == false )
  386. {
  387. DbgNavMsg( GetOuter(), "Failed to pathfind to nav goal:\n" );
  388. DbgNavMsg1( GetOuter(), " Type: %s\n", AIGetGoalTypeText( goal.type) );
  389. DbgNavMsg1( GetOuter(), " Dest: %s\n", NavVecToString( goal.dest ) );
  390. DbgNavMsg1( GetOuter(), " Dest node: %p\n", goal.destNode );
  391. DbgNavMsg1( GetOuter(), " Target: %p\n", goal.pTarget );
  392. if ( flags & AIN_DISCARD_IF_FAIL )
  393. ClearPath();
  394. }
  395. else
  396. {
  397. DbgNavMsg( GetOuter(), "New goal set:\n" );
  398. DbgNavMsg1( GetOuter(), " Type: %s\n", AIGetGoalTypeText( goal.type) );
  399. DbgNavMsg1( GetOuter(), " Dest: %s\n", NavVecToString( goal.dest ) );
  400. DbgNavMsg1( GetOuter(), " Dest node: %p\n", goal.destNode );
  401. DbgNavMsg1( GetOuter(), " Target: %p\n", goal.pTarget );
  402. DbgNavMsg1( GetOuter(), " Tolerance: %.1f\n", GetPath()->GetGoalTolerance() );
  403. DbgNavMsg1( GetOuter(), " Waypoint tol: %.1f\n", GetPath()->GetWaypointTolerance() );
  404. DbgNavMsg1( GetOuter(), " Activity: %s\n", GetOuter()->GetActivityName(GetPath()->GetMovementActivity()) );
  405. DbgNavMsg1( GetOuter(), " Arrival act: %s\n", GetOuter()->GetActivityName(GetPath()->GetArrivalActivity()) );
  406. DbgNavMsg1( GetOuter(), " Arrival seq: %d\n", GetPath()->GetArrivalSequence() );
  407. DbgNavMsg1( GetOuter(), " Goal dir: %s\n", NavVecToString( GetPath()->GetGoalDirection(GetAbsOrigin())) );
  408. // Set our ideal yaw. This has to be done *after* finding the path,
  409. // because the goal position may not be set until then
  410. if ( goal.flags & AIN_YAW_TO_DEST )
  411. {
  412. DbgNavMsg( GetOuter(), " Yaw to dest\n" );
  413. GetMotor()->SetIdealYawToTarget( pPath->ActualGoalPosition() );
  414. }
  415. SimplifyPath( true, goal.maxInitialSimplificationDist );
  416. }
  417. return result;
  418. }
  419. //-----------------------------------------------------------------------------
  420. // Change the target of the path
  421. //-----------------------------------------------------------------------------
  422. bool CAI_Navigator::SetGoalTarget( CBaseEntity *pEntity, const Vector &offset )
  423. {
  424. OnNewGoal();
  425. CAI_Path *pPath = GetPath();
  426. pPath->SetTargetOffset( offset );
  427. pPath->SetTarget( pEntity );
  428. pPath->ClearWaypoints();
  429. return FindPath( !GetOuter()->IsCurTaskContinuousMove() );
  430. }
  431. //-----------------------------------------------------------------------------
  432. bool CAI_Navigator::SetRadialGoal( const Vector &destination, const Vector &center, float radius, float arc, float stepDist, bool bClockwise, bool bAirRoute)
  433. {
  434. DbgNavMsg( GetOuter(), "Set radial goal\n" );
  435. OnNewGoal();
  436. GetPath()->SetGoalType(GOALTYPE_LOCATION);
  437. GetPath()->SetWaypoints( GetPathfinder()->BuildRadialRoute( GetLocalOrigin(), center, destination, radius, arc, stepDist, bClockwise, GetPath()->GetGoalTolerance(), bAirRoute ), true);
  438. GetPath()->SetGoalTolerance( GetOuter()->GetDefaultNavGoalTolerance() );
  439. return IsGoalActive();
  440. }
  441. //-----------------------------------------------------------------------------
  442. bool CAI_Navigator::SetRandomGoal( const Vector &from, float minPathLength, const Vector &dir )
  443. {
  444. DbgNavMsg( GetOuter(), "Set random goal\n" );
  445. OnNewGoal();
  446. if ( GetNetwork()->NumNodes() <= 0 )
  447. return false;
  448. INearestNodeFilter *pFilter = NULL;
  449. CAI_NavInHintGroupFilter filter;
  450. if ( GetOuter()->GetHintGroup() != NULL_STRING )
  451. {
  452. filter.m_iszGroup = GetOuter()->GetHintGroup();
  453. pFilter = &filter;
  454. }
  455. int fromNodeID = GetNetwork()->NearestNodeToPoint( GetOuter(), from, true, pFilter );
  456. if (fromNodeID == NO_NODE)
  457. return false;
  458. AI_Waypoint_t* pRoute = GetPathfinder()->FindShortRandomPath( fromNodeID, minPathLength, dir );
  459. if (!pRoute)
  460. return false;
  461. GetPath()->SetGoalType(GOALTYPE_LOCATION);
  462. GetPath()->SetWaypoints(pRoute);
  463. GetPath()->SetLastNodeAsGoal();
  464. GetPath()->SetGoalTolerance( GetOuter()->GetDefaultNavGoalTolerance() );
  465. SimplifyPath( true );
  466. return true;
  467. }
  468. //-----------------------------------------------------------------------------
  469. bool CAI_Navigator::SetDirectGoal( const Vector &goalPos, Navigation_t navType )
  470. {
  471. DbgNavMsg( GetOuter(), "Set direct goal\n" );
  472. OnNewGoal();
  473. ClearPath();
  474. GetPath()->SetGoalType( GOALTYPE_LOCATION );
  475. GetPath()->SetWaypoints( new AI_Waypoint_t( goalPos, 0, navType, bits_WP_TO_GOAL, NO_NODE ) );
  476. GetPath()->SetGoalTolerance( GetOuter()->GetDefaultNavGoalTolerance() );
  477. GetPath()->SetGoalPosition( goalPos );
  478. return true;
  479. }
  480. //-----------------------------------------------------------------------------
  481. // Placeholder implementation for wander goals: cast a few random vectors and
  482. // accept the first one that still lies on the navmesh.
  483. // Side effect: vector goal of navigator is set.
  484. // Returns: true on goal set, false otherwise.
  485. static bool SetWanderGoalByRandomVector(CAI_Navigator *pNav, float minRadius, float maxRadius, int numTries)
  486. {
  487. while (--numTries >= 0)
  488. {
  489. float dist = random->RandomFloat( minRadius, maxRadius );
  490. Vector dir = UTIL_YawToVector( random->RandomFloat( 0, 359.99 ) );
  491. if ( pNav->SetVectorGoal( dir, dist, minRadius ) )
  492. return true;
  493. }
  494. return false;
  495. }
  496. bool CAI_Navigator::SetWanderGoal( float minRadius, float maxRadius )
  497. {
  498. // @Note (toml 11-07-02): this is a bogus placeholder implementation!!!
  499. //
  500. // First try using a random setvector goal, and then try SetRandomGoal().
  501. // Except, if we have a hint group, first try SetRandomGoal() (which
  502. // respects hint groups) and then fall back on the setvector.
  503. if( !GetOuter()->GetHintGroup() )
  504. {
  505. return ( SetWanderGoalByRandomVector( this, minRadius, maxRadius, 5 ) ||
  506. SetRandomGoal( 1 ) );
  507. }
  508. else
  509. {
  510. return ( SetRandomGoal(1) ||
  511. SetWanderGoalByRandomVector( this, minRadius, maxRadius, 5 ) );
  512. }
  513. }
  514. //-----------------------------------------------------------------------------
  515. void CAI_Navigator::CalculateDeflection( const Vector &start, const Vector &dir, const Vector &normal, Vector *pResult )
  516. {
  517. Vector temp;
  518. CrossProduct( dir, normal, temp );
  519. CrossProduct( normal, temp, *pResult );
  520. VectorNormalize( *pResult );
  521. }
  522. //-----------------------------------------------------------------------------
  523. bool CAI_Navigator::SetVectorGoal( const Vector &dir, float targetDist, float minDist, bool fShouldDeflect )
  524. {
  525. DbgNavMsg( GetOuter(), "Set vector goal\n" );
  526. Vector result;
  527. if ( FindVectorGoal( &result, dir, targetDist, minDist, fShouldDeflect ) )
  528. return SetGoal( result );
  529. return false;
  530. }
  531. //-----------------------------------------------------------------------------
  532. bool CAI_Navigator::SetVectorGoalFromTarget( const Vector &goalPos, float minDist, bool fShouldDeflect )
  533. {
  534. Vector vDir = goalPos;
  535. float dist = ComputePathDirection( GetNavType(), GetLocalOrigin(), goalPos, &vDir );
  536. return SetVectorGoal( vDir, dist, minDist, fShouldDeflect );
  537. }
  538. //-----------------------------------------------------------------------------
  539. bool CAI_Navigator::FindVectorGoal( Vector *pResult, const Vector &dir, float targetDist, float minDist, bool fShouldDeflect )
  540. {
  541. AIMoveTrace_t moveTrace;
  542. float distAchieved = 0;
  543. MARK_TASK_EXPENSIVE();
  544. Vector testLoc = GetLocalOrigin() + ( dir * targetDist );
  545. GetMoveProbe()->MoveLimit( GetNavType(), GetLocalOrigin(), testLoc, GetOuter()->GetAITraceMask(), NULL, &moveTrace );
  546. if ( moveTrace.fStatus != AIMR_OK )
  547. {
  548. distAchieved = targetDist - moveTrace.flDistObstructed;
  549. if ( fShouldDeflect && moveTrace.vHitNormal != vec3_origin )
  550. {
  551. Vector vecDeflect;
  552. Vector vecNormal = moveTrace.vHitNormal;
  553. if ( GetNavType() == NAV_GROUND )
  554. vecNormal.z = 0;
  555. CalculateDeflection( moveTrace.vEndPosition, dir, vecNormal, &vecDeflect );
  556. testLoc = moveTrace.vEndPosition + ( vecDeflect * ( targetDist - distAchieved ) );
  557. Vector temp = moveTrace.vEndPosition;
  558. GetMoveProbe()->MoveLimit( GetNavType(), temp, testLoc, GetOuter()->GetAITraceMask(), NULL, &moveTrace );
  559. distAchieved += ( targetDist - distAchieved ) - moveTrace.flDistObstructed;
  560. }
  561. if ( distAchieved < minDist + 0.01 )
  562. return false;
  563. }
  564. *pResult = moveTrace.vEndPosition;
  565. return true;
  566. }
  567. //-----------------------------------------------------------------------------
  568. bool CAI_Navigator::SetRandomGoal( float minPathLength, const Vector &dir )
  569. {
  570. return SetRandomGoal( GetLocalOrigin(), minPathLength, dir );
  571. }
  572. //-----------------------------------------------------------------------------
  573. bool CAI_Navigator::PrependLocalAvoidance( float distObstacle, const AIMoveTrace_t &directTrace )
  574. {
  575. if ( AIStrongOpt() )
  576. return false;
  577. if ( GetOuter()->IsFlaggedEfficient() )
  578. return false;
  579. if ( m_flTimeLastAvoidanceTriangulate >= gpGlobals->curtime )
  580. return false; // Only triangulate once per think at most
  581. m_flTimeLastAvoidanceTriangulate = gpGlobals->curtime;
  582. AI_PROFILE_SCOPE(CAI_Navigator_PrependLocalAvoidance);
  583. AI_Waypoint_t *pAvoidanceRoute = NULL;
  584. Vector vStart = GetLocalOrigin();
  585. if ( distObstacle < GetHullWidth() * 0.5 )
  586. {
  587. AIMoveTrace_t backawayTrace;
  588. Vector vTestBackaway = GetCurWaypointPos() - GetLocalOrigin();
  589. VectorNormalize( vTestBackaway );
  590. vTestBackaway *= -GetHullWidth();
  591. vTestBackaway += GetLocalOrigin();
  592. int flags = ( GetNavType() == NAV_GROUND ) ? AIMLF_2D : AIMLF_DEFAULT;
  593. if ( GetMoveProbe()->MoveLimit( GetNavType(), GetLocalOrigin(), vTestBackaway,
  594. GetOuter()->GetAITraceMask(), GetNavTargetEntity(),
  595. 100.0,
  596. flags, &backawayTrace ) )
  597. {
  598. vStart = backawayTrace.vEndPosition;
  599. pAvoidanceRoute = new AI_Waypoint_t( vStart, 0, GetNavType(), bits_WP_TO_DETOUR, NO_NODE );
  600. }
  601. }
  602. AI_Waypoint_t *pTriangulation = GetPathfinder()->BuildTriangulationRoute(
  603. vStart,
  604. GetCurWaypointPos(),
  605. GetNavTargetEntity(),
  606. bits_WP_TO_DETOUR,
  607. NO_NODE,
  608. 0.0,
  609. distObstacle,
  610. GetNavType() );
  611. if ( !pTriangulation )
  612. {
  613. delete pAvoidanceRoute;
  614. return false;
  615. }
  616. if ( pAvoidanceRoute )
  617. pAvoidanceRoute->SetNext( pTriangulation );
  618. else
  619. pAvoidanceRoute = pTriangulation;
  620. // @TODO (toml 02-04-04): it would be better to do this on each triangulation test to
  621. // improve the odds of success. however, difficult in current structure
  622. float moveThisInterval = GetMotor()->CalcIntervalMove();
  623. Vector dir = pAvoidanceRoute->GetPos() - GetLocalOrigin();
  624. float dist = VectorNormalize( dir );
  625. Vector testPos;
  626. if ( dist > moveThisInterval )
  627. {
  628. dist = moveThisInterval;
  629. testPos = GetLocalOrigin() + dir * dist;
  630. }
  631. else
  632. {
  633. testPos = pAvoidanceRoute->GetPos();
  634. }
  635. int flags = ( GetNavType() == NAV_GROUND ) ? AIMLF_2D : AIMLF_DEFAULT;
  636. if ( !GetMoveProbe()->MoveLimit( GetNavType(), GetLocalOrigin(), testPos,
  637. GetOuter()->GetAITraceMask(), GetNavTargetEntity(),
  638. 100.0,
  639. flags ) )
  640. {
  641. DeleteAll( pAvoidanceRoute );
  642. return false;
  643. }
  644. // FIXME: should the route get simplified?
  645. DbgNavMsg( GetOuter(), "Adding triangulation\n" );
  646. GetPath()->PrependWaypoints( pAvoidanceRoute );
  647. return true;
  648. }
  649. //-----------------------------------------------------------------------------
  650. void CAI_Navigator::PrependWaypoint( const Vector &newPoint, Navigation_t navType, unsigned waypointFlags )
  651. {
  652. GetPath()->PrependWaypoint( newPoint, navType, waypointFlags );
  653. }
  654. //-----------------------------------------------------------------------------
  655. const Vector &CAI_Navigator::GetGoalPos() const
  656. {
  657. return GetPath()->BaseGoalPosition();
  658. }
  659. //-----------------------------------------------------------------------------
  660. CBaseEntity *CAI_Navigator::GetGoalTarget()
  661. {
  662. return GetPath()->GetTarget();
  663. }
  664. //-----------------------------------------------------------------------------
  665. float CAI_Navigator::GetGoalTolerance() const
  666. {
  667. return GetPath()->GetGoalTolerance();
  668. }
  669. //-----------------------------------------------------------------------------
  670. void CAI_Navigator::SetGoalTolerance(float tolerance)
  671. {
  672. GetPath()->SetGoalTolerance(tolerance);
  673. }
  674. //-----------------------------------------------------------------------------
  675. bool CAI_Navigator::RefindPathToGoal( bool fSignalTaskStatus, bool bDontIgnoreBadLinks )
  676. {
  677. return FindPath( fSignalTaskStatus, bDontIgnoreBadLinks );
  678. }
  679. //-----------------------------------------------------------------------------
  680. bool CAI_Navigator::UpdateGoalPos( const Vector &goalPos )
  681. {
  682. // Queue this up if we're in the middle of a frame
  683. if ( PostFrameNavigationSystem()->IsGameFrameRunning() )
  684. {
  685. // Send off the query for queuing
  686. PostFrameNavigationSystem()->EnqueueEntityNavigationQuery( GetOuter(), CreateFunctor( this, &CAI_Navigator::UpdateGoalPos, RefToVal( goalPos ) ) );
  687. // For now, always succeed -- we need to deal with failures on the next frame
  688. return true;
  689. }
  690. DbgNavMsg( GetOuter(), "Updating goal pos\n" );
  691. if ( GetNavType() == NAV_JUMP )
  692. {
  693. DevMsg( "Updating goal pos while jumping!\n" );
  694. AssertOnce( 0 );
  695. return false;
  696. }
  697. // FindPath should be finding the goal position if the goal type is
  698. // one of these two... We could just ignore the suggested position
  699. // in these two cases I suppose!
  700. Assert( (GetPath()->GoalType() != GOALTYPE_ENEMY) && (GetPath()->GoalType() != GOALTYPE_TARGETENT) );
  701. GetPath()->ResetGoalPosition( goalPos );
  702. if ( FindPath( !GetOuter()->IsCurTaskContinuousMove() ) )
  703. {
  704. SimplifyPath( true );
  705. return true;
  706. }
  707. return false;
  708. }
  709. //-----------------------------------------------------------------------------
  710. Activity CAI_Navigator::SetMovementActivity(Activity activity)
  711. {
  712. return GetPath()->SetMovementActivity( activity );
  713. }
  714. //-----------------------------------------------------------------------------
  715. void CAI_Navigator::StopMoving( bool bImmediate )
  716. {
  717. DbgNavMsg1( GetOuter(), "CAI_Navigator::StopMoving( %d )\n", bImmediate );
  718. if ( IsGoalSet() )
  719. {
  720. if ( bImmediate || !SetGoalFromStoppingPath() )
  721. {
  722. OnNavComplete();
  723. }
  724. }
  725. else
  726. ClearGoal();
  727. }
  728. //-----------------------------------------------------------------------------
  729. bool CAI_Navigator::ClearGoal()
  730. {
  731. DbgNavMsg( GetOuter(), "CAI_Navigator::ClearGoal()\n" );
  732. ClearPath();
  733. OnNewGoal();
  734. return true;
  735. }
  736. //-----------------------------------------------------------------------------
  737. int CAI_Navigator::GetMovementSequence( )
  738. {
  739. int sequence = GetPath()->GetMovementSequence( );
  740. if (sequence == ACT_INVALID)
  741. {
  742. Activity activity = GetPath()->GetMovementActivity();
  743. Assert( activity != ACT_INVALID );
  744. sequence = GetOuter()->SelectWeightedSequence( GetOuter()->TranslateActivity( activity ) );
  745. if ( sequence == ACT_INVALID )
  746. {
  747. DevMsg( GetOuter(), "No appropriate sequence for movement activity %s (%d)\n", GetOuter()->GetActivityName( GetPath()->GetArrivalActivity() ), GetPath()->GetArrivalActivity() );
  748. if ( activity == ACT_SCRIPT_CUSTOM_MOVE )
  749. {
  750. sequence = GetOuter()->GetScriptCustomMoveSequence();
  751. }
  752. else
  753. {
  754. sequence = GetOuter()->SelectWeightedSequence( GetOuter()->TranslateActivity( ACT_WALK ) );
  755. }
  756. }
  757. Assert( sequence != ACT_INVALID );
  758. GetPath()->SetMovementSequence( sequence );
  759. }
  760. return sequence;
  761. }
  762. //-----------------------------------------------------------------------------
  763. void CAI_Navigator::SetMovementSequence( int sequence )
  764. {
  765. GetPath()->SetMovementSequence( sequence );
  766. }
  767. //-----------------------------------------------------------------------------
  768. Activity CAI_Navigator::GetMovementActivity() const
  769. {
  770. return GetPath()->GetMovementActivity();
  771. }
  772. //-----------------------------------------------------------------------------
  773. void CAI_Navigator::SetArrivalActivity(Activity activity)
  774. {
  775. GetPath()->SetArrivalActivity(activity);
  776. }
  777. //-----------------------------------------------------------------------------
  778. int CAI_Navigator::GetArrivalSequence( int curSequence )
  779. {
  780. int sequence = GetPath()->GetArrivalSequence( );
  781. if (sequence == ACT_INVALID)
  782. {
  783. Activity activity = GetOuter()->GetStoppedActivity();
  784. Assert( activity != ACT_INVALID );
  785. if (activity == ACT_INVALID)
  786. {
  787. activity = ACT_IDLE;
  788. }
  789. sequence = GetOuter()->SelectWeightedSequence( GetOuter()->TranslateActivity( activity ), curSequence );
  790. if ( sequence == ACT_INVALID )
  791. {
  792. DevMsg( GetOuter(), "No appropriate sequence for arrival activity %s (%d)\n", GetOuter()->GetActivityName( GetPath()->GetArrivalActivity() ), GetPath()->GetArrivalActivity() );
  793. sequence = GetOuter()->SelectWeightedSequence( GetOuter()->TranslateActivity( ACT_IDLE ), curSequence );
  794. }
  795. Assert( sequence != ACT_INVALID );
  796. GetPath()->SetArrivalSequence( sequence );
  797. }
  798. return sequence;
  799. }
  800. //-----------------------------------------------------------------------------
  801. void CAI_Navigator::SetArrivalSequence( int sequence )
  802. {
  803. GetPath()->SetArrivalActivity( ACT_INVALID );
  804. GetPath()->SetArrivalSequence( sequence );
  805. }
  806. //-----------------------------------------------------------------------------
  807. Activity CAI_Navigator::GetArrivalActivity( ) const
  808. {
  809. return GetPath()->GetArrivalActivity( );
  810. }
  811. //-----------------------------------------------------------------------------
  812. void CAI_Navigator::SetArrivalDirection( const Vector &goalDirection )
  813. {
  814. GetPath()->SetGoalDirection( goalDirection );
  815. }
  816. //-----------------------------------------------------------------------------
  817. void CAI_Navigator::SetArrivalDirection( const QAngle &goalAngle )
  818. {
  819. Vector goalDirection;
  820. AngleVectors( goalAngle, &goalDirection );
  821. GetPath()->SetGoalDirection( goalDirection );
  822. }
  823. //-----------------------------------------------------------------------------
  824. void CAI_Navigator::SetArrivalDirection( CBaseEntity * pTarget )
  825. {
  826. GetPath()->SetGoalDirection( pTarget );
  827. }
  828. //-----------------------------------------------------------------------------
  829. Vector CAI_Navigator::GetArrivalDirection( )
  830. {
  831. return GetPath()->GetGoalDirection( GetAbsOrigin() );
  832. }
  833. //-----------------------------------------------------------------------------
  834. void CAI_Navigator::SetArrivalSpeed( float flSpeed )
  835. {
  836. GetPath()->SetGoalSpeed( flSpeed );
  837. }
  838. //-----------------------------------------------------------------------------
  839. float CAI_Navigator::GetArrivalSpeed( void )
  840. {
  841. float flSpeed = GetPath()->GetGoalSpeed( GetAbsOrigin() );
  842. if (flSpeed >= 0.0)
  843. {
  844. return flSpeed;
  845. }
  846. int sequence = GetArrivalSequence( ACT_INVALID );
  847. if (sequence != ACT_INVALID)
  848. {
  849. flSpeed = GetOuter()->GetEntryVelocity( sequence );
  850. SetArrivalSpeed( flSpeed );
  851. }
  852. else
  853. {
  854. flSpeed = 0.0;
  855. }
  856. return flSpeed;
  857. }
  858. //-----------------------------------------------------------------------------
  859. void CAI_Navigator::SetArrivalDistance( float flDistance )
  860. {
  861. GetPath()->SetGoalStoppingDistance( flDistance );
  862. }
  863. //-----------------------------------------------------------------------------
  864. float CAI_Navigator::GetArrivalDistance() const
  865. {
  866. return GetPath()->GetGoalStoppingDistance();
  867. }
  868. //-----------------------------------------------------------------------------
  869. const Vector &CAI_Navigator::GetCurWaypointPos() const
  870. {
  871. return GetPath()->CurWaypointPos();
  872. }
  873. //-----------------------------------------------------------------------------
  874. int CAI_Navigator::GetCurWaypointFlags() const
  875. {
  876. return GetPath()->CurWaypointFlags();
  877. }
  878. //-----------------------------------------------------------------------------
  879. GoalType_t CAI_Navigator::GetGoalType() const
  880. {
  881. return GetPath()->GoalType();
  882. }
  883. //-----------------------------------------------------------------------------
  884. int CAI_Navigator::GetGoalFlags() const
  885. {
  886. return GetPath()->GoalFlags();
  887. }
  888. //-----------------------------------------------------------------------------
  889. bool CAI_Navigator::CurWaypointIsGoal() const
  890. {
  891. return GetPath()->CurWaypointIsGoal();
  892. }
  893. bool CAI_Navigator::CurWaypointRequiresPreciseMovement() const
  894. {
  895. return ( GetPath()->CurWaypointFlags() & bits_WP_PRECISE_MOVEMENT ) != 0;
  896. }
  897. //-----------------------------------------------------------------------------
  898. bool CAI_Navigator::IsGoalSet() const
  899. {
  900. return ( GetPath()->GoalType() != GOALTYPE_NONE );
  901. }
  902. //-----------------------------------------------------------------------------
  903. bool CAI_Navigator::IsGoalActive() const
  904. {
  905. return ( GetPath() && !( const_cast<CAI_Path *>(GetPath())->IsEmpty() ) );
  906. }
  907. //-----------------------------------------------------------------------------
  908. bool CAI_Navigator::GetPointAlongPath( Vector *pResult, float distance, bool fReducibleOnly )
  909. {
  910. if ( !GetPath()->GetCurWaypoint() )
  911. return false;
  912. AI_Waypoint_t *pCurWaypoint = GetPath()->GetCurWaypoint();
  913. AI_Waypoint_t *pEndPoint = pCurWaypoint;
  914. float distRemaining = distance;
  915. float distToNext;
  916. Vector vPosPrev = GetLocalOrigin();
  917. while ( pEndPoint->GetNext() )
  918. {
  919. distToNext = ComputePathDistance( GetNavType(), vPosPrev, pEndPoint->GetPos() );
  920. if ( distToNext > distRemaining)
  921. break;
  922. distRemaining -= distToNext;
  923. vPosPrev = pEndPoint->GetPos();
  924. if ( fReducibleOnly && !pEndPoint->IsReducible() )
  925. break;
  926. pEndPoint = pEndPoint->GetNext();
  927. }
  928. Vector &result = *pResult;
  929. float distToEnd = ComputePathDistance( GetNavType(), vPosPrev, pEndPoint->GetPos() );
  930. if ( distToEnd - distRemaining < 0.1 )
  931. {
  932. result = pEndPoint->GetPos();
  933. }
  934. else
  935. {
  936. result = pEndPoint->GetPos() - vPosPrev;
  937. VectorNormalize( result );
  938. result *= distRemaining;
  939. result += vPosPrev;
  940. }
  941. return true;
  942. }
  943. //-----------------------------------------------------------------------------
  944. float CAI_Navigator::GetPathDistanceToGoal()
  945. {
  946. return GetPath()->GetPathDistanceToGoal(GetAbsOrigin());
  947. }
  948. //-----------------------------------------------------------------------------
  949. float CAI_Navigator::GetPathTimeToGoal()
  950. {
  951. if ( GetOuter()->m_flGroundSpeed )
  952. return (GetPathDistanceToGoal() / GetOuter()->m_flGroundSpeed);
  953. return 0;
  954. }
  955. //-----------------------------------------------------------------------------
  956. AI_PathNode_t CAI_Navigator::GetNearestNode()
  957. {
  958. #if !defined( GNUC )
  959. COMPILE_TIME_ASSERT( ((int)AIN_NO_NODE == (int)NO_NODE) );
  960. #endif
  961. return (AI_PathNode_t)(intp)( GetPathfinder()->NearestNodeToNPC() );
  962. }
  963. //-----------------------------------------------------------------------------
  964. Vector CAI_Navigator::GetNodePos( AI_PathNode_t node )
  965. {
  966. return GetNetwork()->GetNode(size_cast<int>( (intp)node ) )->GetPosition(GetHullType());
  967. }
  968. //-----------------------------------------------------------------------------
  969. void CAI_Navigator::OnScheduleChange()
  970. {
  971. DbgNavMsg( GetOuter(), "Schedule change\n" );
  972. }
  973. //-----------------------------------------------------------------------------
  974. void CAI_Navigator::OnClearPath(void)
  975. {
  976. }
  977. //-----------------------------------------------------------------------------
  978. void CAI_Navigator::OnNewGoal()
  979. {
  980. DbgNavMsg( GetOuter(), "New Goal\n" );
  981. ResetCalculations();
  982. m_fNavComplete = true;
  983. }
  984. //-----------------------------------------------------------------------------
  985. void CAI_Navigator::OnNavComplete()
  986. {
  987. DbgNavMsg( GetOuter(), "Nav complete\n" );
  988. ResetCalculations();
  989. TaskMovementComplete();
  990. m_fNavComplete = true;
  991. }
  992. //-----------------------------------------------------------------------------
  993. void CAI_Navigator::OnNavFailed( bool bMovement )
  994. {
  995. DbgNavMsg( GetOuter(), "Nav failed\n" );
  996. if ( bMovement )
  997. GetOuter()->OnMovementFailed();
  998. #ifdef DEBUG
  999. if ( CurWaypointIsGoal() )
  1000. {
  1001. float flWaypointDist = ComputePathDistance( GetNavType(), GetLocalOrigin(), GetPath()->ActualGoalPosition() );
  1002. if ( flWaypointDist < GetGoalTolerance() + 0.1 )
  1003. {
  1004. DevMsg( "Nav failed but NPC was within goal tolerance?\n" );
  1005. }
  1006. }
  1007. #endif
  1008. ResetCalculations();
  1009. m_fNavComplete = true;
  1010. m_bLastNavFailed = true;
  1011. }
  1012. //-----------------------------------------------------------------------------
  1013. void CAI_Navigator::OnNavFailed( AI_TaskFailureCode_t code, bool bMovement )
  1014. {
  1015. if ( GetOuter()->ShouldFailNav( bMovement ) )
  1016. {
  1017. OnNavFailed( bMovement );
  1018. DbgNavMsg( GetOuter(), "CAI_Navigator::OnNavFailed calling SetActivity(GetStoppedActivity)");
  1019. SetActivity( GetOuter()->GetStoppedActivity() );
  1020. TaskFail(code);
  1021. }
  1022. else
  1023. {
  1024. m_nNavFailCounter++;
  1025. m_flLastNavFailTime = gpGlobals->curtime;
  1026. if ( GetOuter()->ShouldBruteForceFailedNav() )
  1027. {
  1028. if (bMovement)
  1029. {
  1030. m_timeBeginFailedSteer = FLT_MAX;
  1031. // if failing, turn off collisions with the object
  1032. CBaseEntity *pBlocker = GetBlockingEntity();
  1033. // FIXME: change this to only be for MOVETYPE_VPHYSICS?
  1034. if (pBlocker && !pBlocker->IsWorld() && !pBlocker->IsPlayer() && !FClassnameIs( pBlocker, "func_tracktrain" ))
  1035. {
  1036. //pBlocker->DrawBBoxOverlay( 2.0f );
  1037. if (NPCPhysics_CreateSolver( GetOuter(), pBlocker, true, 10.0f ) != NULL)
  1038. {
  1039. ClearNavFailCounter();
  1040. }
  1041. }
  1042. // if still failing, try jumping forward through the route
  1043. if (GetNavFailCounter() > 0)
  1044. {
  1045. if (TeleportAlongPath())
  1046. {
  1047. ClearNavFailCounter();
  1048. }
  1049. }
  1050. }
  1051. else
  1052. {
  1053. CBaseEntity *pBlocker = GetMoveProbe()->GetBlockingEntity();
  1054. if (pBlocker)
  1055. {
  1056. //pBlocker->DrawBBoxOverlay( 2.0f );
  1057. if (NPCPhysics_CreateSolver( GetOuter(), pBlocker, true, 10.0f ) != NULL)
  1058. {
  1059. ClearNavFailCounter();
  1060. }
  1061. }
  1062. }
  1063. }
  1064. }
  1065. }
  1066. //-----------------------------------------------------------------------------
  1067. void CAI_Navigator::OnNavFailed( const char *pszGeneralFailText, bool bMovement )
  1068. {
  1069. OnNavFailed( MakeFailCode( pszGeneralFailText ), bMovement );
  1070. }
  1071. //-----------------------------------------------------------------------------
  1072. int CAI_Navigator::GetNavFailCounter() const
  1073. {
  1074. return m_nNavFailCounter;
  1075. }
  1076. //-----------------------------------------------------------------------------
  1077. void CAI_Navigator::ClearNavFailCounter()
  1078. {
  1079. m_nNavFailCounter = 0;
  1080. }
  1081. //-----------------------------------------------------------------------------
  1082. float CAI_Navigator::GetLastNavFailTime() const
  1083. {
  1084. return m_flLastNavFailTime;
  1085. }
  1086. //-----------------------------------------------------------------------------
  1087. bool CAI_Navigator::TeleportAlongPath()
  1088. {
  1089. while (GetPath()->GetCurWaypoint())
  1090. {
  1091. Vector vecStart;
  1092. Vector vTestPoint;
  1093. vecStart = GetPath()->CurWaypointPos();
  1094. AdvancePath();
  1095. GetOuter()->GetMoveProbe()->FloorPoint( vecStart, GetOuter()->GetAITraceMask(), GetOuter()->StepHeight(), -64, &vTestPoint );
  1096. if ( CanFitAtPosition( vTestPoint, GetOuter()->GetAITraceMask(), false, false ) )
  1097. {
  1098. if ( GetOuter()->GetMoveProbe()->CheckStandPosition( vTestPoint, GetOuter()->GetAITraceMask() ) )
  1099. {
  1100. GetOuter()->Teleport( &vTestPoint, NULL, NULL );
  1101. // clear ground entity, let normal fall code reestablish what the npc is now standing on
  1102. GetOuter()->SetGroundEntity( NULL );
  1103. GetOuter()->PhysicsTouchTriggers( &vTestPoint );
  1104. return true;
  1105. }
  1106. }
  1107. if (CurWaypointIsGoal())
  1108. break;
  1109. }
  1110. return false;
  1111. }
  1112. //-----------------------------------------------------------------------------
  1113. void CAI_Navigator::ResetCalculations()
  1114. {
  1115. m_hPeerWaitingOn = NULL;
  1116. m_PeerWaitMoveTimer.Force();
  1117. m_PeerWaitClearTimer.Force();
  1118. m_hBigStepGroundEnt = NULL;
  1119. m_NextSidestepTimer.Force();
  1120. m_bCalledStartMove = false;
  1121. m_vPosBeginFailedSteer = vec3_invalid;
  1122. m_timeBeginFailedSteer = FLT_MAX;
  1123. m_flLastSuccessfulSimplifyTime = -1;
  1124. GetLocalNavigator()->ResetMoveCalculations();
  1125. GetMotor()->ResetMoveCalculations();
  1126. GetMoveProbe()->ClearBlockingEntity();
  1127. m_nNavFailCounter = 0;
  1128. m_flLastNavFailTime = -1;
  1129. }
  1130. //-----------------------------------------------------------------------------
  1131. // Purpose: Sets navigation type, maintaining any necessary invariants
  1132. //-----------------------------------------------------------------------------
  1133. void CAI_Navigator::SetNavType( Navigation_t navType )
  1134. {
  1135. m_navType = navType;
  1136. }
  1137. //-----------------------------------------------------------------------------
  1138. AIMoveResult_t CAI_Navigator::MoveCrawl()
  1139. {
  1140. if (!PreMove())
  1141. return AIMR_ILLEGAL;
  1142. if ( ShouldTestFailMove() )
  1143. return AIMR_ILLEGAL;
  1144. // --------------------------------
  1145. AIMoveResult_t result = AIMR_ILLEGAL;
  1146. if ( MoveUpdateWaypoint( &result ) )
  1147. return result;
  1148. // --------------------------------
  1149. // Set activity to be the Navigation activity
  1150. float preMoveSpeed = GetIdealSpeed();
  1151. Activity preMoveActivity = GetActivity();
  1152. int nPreMoveSequence = GetOuter()->GetSequence(); // this is an unfortunate necessity to ensure setting back the activity picks the right one if it had been sticky
  1153. Vector vStart = GetAbsOrigin();
  1154. // --------------------------------
  1155. // FIXME: only need since IdealSpeed isn't based on movement activity but immediate activity!
  1156. SetActivity( GetMovementActivity() );
  1157. if ( m_bValidateActivitySpeed && GetIdealSpeed() <= 0.0f )
  1158. {
  1159. if ( GetActivity() == ACT_TRANSITION )
  1160. return AIMR_OK;
  1161. DevMsg( "%s moving with speed <= 0 (%s)\n", GetEntClassname(), GetOuter()->GetSequenceName( GetSequence() ) );
  1162. }
  1163. // --------------------------------
  1164. if ( GetNavType() != NAV_CRAWL )
  1165. {
  1166. DbgNavMsg( GetOuter(), "Crawl start\n" );
  1167. }
  1168. SetNavType( NAV_CRAWL );
  1169. AILocalMoveGoal_t move;
  1170. MoveCalcBaseGoal( &move );
  1171. result = MoveEnact( move );
  1172. // --------------------------------
  1173. // If we didn't actually move, but it was a success (i.e., blocked but within tolerance), make sure no visual artifact
  1174. // FIXME: only needed because of the above slamming of SetActivity(), which is only needed
  1175. // because GetIdealSpeed() looks at the current activity instead of the movement activity.
  1176. if ( result == AIMR_OK && preMoveSpeed < 0.01 )
  1177. {
  1178. if ( ( GetAbsOrigin() - vStart ).Length() < 0.01 )
  1179. {
  1180. GetOuter()->SetSequence( nPreMoveSequence );
  1181. SetActivity( preMoveActivity );
  1182. }
  1183. }
  1184. // --------------------------------
  1185. return result;
  1186. }
  1187. //-----------------------------------------------------------------------------
  1188. AIMoveResult_t CAI_Navigator::MoveClimb()
  1189. {
  1190. // --------------------------------------------------
  1191. // CLIMB START
  1192. // --------------------------------------------------
  1193. const Vector &climbDest = GetPath()->CurWaypointPos();
  1194. Vector climbDir = climbDest - GetLocalOrigin();
  1195. float climbDist = VectorNormalize( climbDir );
  1196. if ( GetNavType() != NAV_CLIMB )
  1197. {
  1198. DbgNavMsg( GetOuter(), "Climb start\n" );
  1199. GetMotor()->MoveClimbStart( climbDest, climbDir, climbDist, GetPath()->CurWaypointYaw() );
  1200. }
  1201. SetNavType( NAV_CLIMB );
  1202. // Look for a block by another NPC, and attempt to recover
  1203. AIMoveTrace_t moveTrace;
  1204. if ( climbDist > 0.01 &&
  1205. !GetMoveProbe()->MoveLimit( NAV_CLIMB, GetLocalOrigin(), GetLocalOrigin() + ( climbDir * MIN(0.1,climbDist - 0.005) ), GetOuter()->GetAITraceMask(), GetNavTargetEntity(), &moveTrace ) )
  1206. {
  1207. CAI_BaseNPC *pOther = ( moveTrace.pObstruction ) ? moveTrace.pObstruction->MyNPCPointer() : NULL;
  1208. if ( pOther )
  1209. {
  1210. bool bAbort = false;
  1211. if ( !pOther->IsMoving() )
  1212. bAbort = true;
  1213. else if ( pOther->GetNavType() == NAV_CLIMB && climbDir.z <= 0.01 )
  1214. {
  1215. const Vector &otherClimbDest = pOther->GetNavigator()->GetPath()->CurWaypointPos();
  1216. Vector otherClimbDir = otherClimbDest - pOther->GetLocalOrigin();
  1217. VectorNormalize( otherClimbDir );
  1218. if ( otherClimbDir.Dot( climbDir ) < 0 )
  1219. {
  1220. bAbort = true;
  1221. if ( pOther->GetNavigator()->GetStoppingPath( m_pClippedWaypoints ) )
  1222. {
  1223. m_flTimeClipped = gpGlobals->curtime;
  1224. SetNavType(NAV_GROUND); // end of clipped will be on ground
  1225. SetGravity( 1.0 );
  1226. if ( RefindPathToGoal( false ) )
  1227. {
  1228. bAbort = false;
  1229. }
  1230. SetGravity( 0.0 );
  1231. SetNavType(NAV_CLIMB);
  1232. }
  1233. }
  1234. }
  1235. if ( bAbort )
  1236. {
  1237. DbgNavMsg( GetOuter(), "Climb fail\n" );
  1238. GetMotor()->MoveClimbStop();
  1239. SetNavType(NAV_GROUND);
  1240. return AIMR_BLOCKED_NPC;
  1241. }
  1242. }
  1243. }
  1244. // count NAV_CLIMB nodes remaining
  1245. int climbNodesLeft = 0;
  1246. AI_Waypoint_t *pWaypoint = GetPath()->GetCurWaypoint();
  1247. while (pWaypoint && pWaypoint->NavType() == NAV_CLIMB)
  1248. {
  1249. ++climbNodesLeft;
  1250. pWaypoint = pWaypoint->GetNext();
  1251. }
  1252. AIMoveResult_t result = GetMotor()->MoveClimbExecute( climbDest, climbDir, climbDist, GetPath()->CurWaypointYaw(), climbNodesLeft );
  1253. if ( result == AIMR_CHANGE_TYPE )
  1254. {
  1255. if ( GetPath()->GetCurWaypoint()->GetNext() )
  1256. AdvancePath();
  1257. else
  1258. OnNavComplete();
  1259. if ( !GetPath()->GetCurWaypoint() || !GetPath()->GetCurWaypoint()->GetNext() || !(GetPath()->CurWaypointNavType() == NAV_CLIMB))
  1260. {
  1261. DbgNavMsg( GetOuter(), "Climb stop\n" );
  1262. GetMotor()->MoveClimbStop();
  1263. SetNavType(NAV_GROUND);
  1264. }
  1265. }
  1266. else if ( result != AIMR_OK )
  1267. {
  1268. DbgNavMsg( GetOuter(), "Climb fail (2)\n" );
  1269. GetMotor()->MoveClimbStop();
  1270. SetNavType(NAV_GROUND);
  1271. return result;
  1272. }
  1273. return result;
  1274. }
  1275. //-----------------------------------------------------------------------------
  1276. AIMoveResult_t CAI_Navigator::MoveJump()
  1277. {
  1278. // --------------------------------------------------
  1279. // JUMPING
  1280. // --------------------------------------------------
  1281. if ( (GetNavType() != NAV_JUMP) && (GetEntFlags() & FL_ONGROUND) )
  1282. {
  1283. // --------------------------------------------------
  1284. // Now check if I can actually jump this distance?
  1285. // --------------------------------------------------
  1286. AIMoveTrace_t moveTrace;
  1287. GetMoveProbe()->MoveLimit( NAV_JUMP, GetLocalOrigin(), GetPath()->CurWaypointPos(),
  1288. GetOuter()->GetAITraceMask(), GetNavTargetEntity(), &moveTrace );
  1289. if ( IsMoveBlocked( moveTrace ) )
  1290. {
  1291. return moveTrace.fStatus;
  1292. }
  1293. SetNavType(NAV_JUMP);
  1294. DbgNavMsg( GetOuter(), "Jump start\n" );
  1295. GetMotor()->MoveJumpStart( moveTrace.vJumpVelocity );
  1296. }
  1297. // --------------------------------------------------
  1298. // LANDING (from jump)
  1299. // --------------------------------------------------
  1300. else if (GetNavType() == NAV_JUMP && (GetEntFlags() & FL_ONGROUND))
  1301. {
  1302. // DevMsg( "jump to %f %f %f landed %f %f %f", GetPath()->CurWaypointPos().x, GetPath()->CurWaypointPos().y, GetPath()->CurWaypointPos().z, GetLocalOrigin().x, GetLocalOrigin().y, GetLocalOrigin().z );
  1303. DbgNavMsg( GetOuter(), "Jump stop\n" );
  1304. AIMoveResult_t result = GetMotor()->MoveJumpStop( );
  1305. if (result == AIMR_CHANGE_TYPE)
  1306. {
  1307. SetNavType(NAV_GROUND);
  1308. // --------------------------------------------------
  1309. // If I've jumped to my goal I'm done
  1310. // --------------------------------------------------
  1311. if (CurWaypointIsGoal())
  1312. {
  1313. OnNavComplete();
  1314. return AIMR_OK;
  1315. }
  1316. // --------------------------------------------------
  1317. // Otherwise advance my route and walk
  1318. // --------------------------------------------------
  1319. else
  1320. {
  1321. AdvancePath();
  1322. return AIMR_CHANGE_TYPE;
  1323. }
  1324. }
  1325. return AIMR_OK;
  1326. }
  1327. // --------------------------------------------------
  1328. // IN-AIR (from jump)
  1329. // --------------------------------------------------
  1330. else
  1331. {
  1332. GetMotor()->MoveJumpExecute( );
  1333. }
  1334. return AIMR_OK;
  1335. }
  1336. //-----------------------------------------------------------------------------
  1337. void CAI_Navigator::MoveCalcBaseGoal( AILocalMoveGoal_t *pMoveGoal )
  1338. {
  1339. AI_PROFILE_SCOPE( CAI_Navigator_MoveCalcBaseGoal );
  1340. pMoveGoal->navType = GetNavType();
  1341. pMoveGoal->target = GetPath()->CurWaypointPos();
  1342. pMoveGoal->maxDist = ComputePathDirection( GetNavType(), GetLocalOrigin(), pMoveGoal->target, &pMoveGoal->dir );
  1343. pMoveGoal->facing = pMoveGoal->dir;
  1344. pMoveGoal->speed = GetMotor()->GetSequenceGroundSpeed( GetMovementSequence() );
  1345. pMoveGoal->curExpectedDist = pMoveGoal->speed * GetMotor()->GetMoveInterval();
  1346. pMoveGoal->pMoveTarget = GetNavTargetEntity();
  1347. if ( pMoveGoal->curExpectedDist > pMoveGoal->maxDist )
  1348. pMoveGoal->curExpectedDist = pMoveGoal->maxDist;
  1349. if ( GetPath()->CurWaypointIsGoal())
  1350. {
  1351. pMoveGoal->flags |= AILMG_TARGET_IS_GOAL;
  1352. }
  1353. else
  1354. {
  1355. AI_Waypoint_t *pCurWaypoint = GetPath()->GetCurWaypoint();
  1356. if ( pCurWaypoint->GetNext() && pCurWaypoint->GetNext()->NavType() != pCurWaypoint->NavType() )
  1357. pMoveGoal->flags |= AILMG_TARGET_IS_TRANSITION;
  1358. }
  1359. const Task_t *pCurTask = GetOuter()->GetTask();
  1360. if ( pCurTask && pCurTask->iTask == TASK_STOP_MOVING )
  1361. {
  1362. // If we're running stop moving, don't steer or run avoidance paths
  1363. // This stops the NPC wiggling around as they attempt to reach a stopping
  1364. // path that's pushed right up against geometry. (Tracker #48656)
  1365. pMoveGoal->flags |= ( AILMG_NO_STEER | AILMG_NO_AVOIDANCE_PATHS );
  1366. }
  1367. pMoveGoal->pPath = GetPath();
  1368. }
  1369. //-----------------------------------------------------------------------------
  1370. bool CAI_Navigator::OnCalcBaseMove( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult )
  1371. {
  1372. if ( GetOuter()->OnCalcBaseMove( pMoveGoal, distClear, pResult ) )
  1373. {
  1374. DebugNoteMovementFailureIfBlocked( *pResult );
  1375. return true;
  1376. }
  1377. return false;
  1378. }
  1379. //-----------------------------------------------------------------------------
  1380. bool CAI_Navigator::OnObstructionPreSteer( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult )
  1381. {
  1382. bool fTargetIsGoal = ( ( pMoveGoal->flags & AILMG_TARGET_IS_GOAL ) != 0 );
  1383. bool fShouldAttemptHit = false;
  1384. bool fShouldAdvancePath = false;
  1385. float tolerance = 0;
  1386. if ( fTargetIsGoal )
  1387. {
  1388. fShouldAttemptHit = true;
  1389. tolerance = GetPath()->GetGoalTolerance();
  1390. }
  1391. else if ( !( pMoveGoal->flags & AILMG_TARGET_IS_TRANSITION ) )
  1392. {
  1393. fShouldAttemptHit = true;
  1394. fShouldAdvancePath = true;
  1395. tolerance = GetPath()->GetWaypointTolerance();
  1396. // If the immediate goal is close, and the clearance brings into tolerance,
  1397. // just try and move on
  1398. if ( pMoveGoal->maxDist < 4*12 && pMoveGoal->maxDist - distClear < tolerance )
  1399. tolerance = pMoveGoal->maxDist + 1;
  1400. }
  1401. if ( fShouldAttemptHit )
  1402. {
  1403. if ( distClear > pMoveGoal->maxDist )
  1404. {
  1405. #ifdef PHYSICS_NPC_SHADOW_DISCREPENCY
  1406. if ( distClear < AI_EPS_CASTS ) // needed because vphysics can pull us back up to this far
  1407. {
  1408. DebugNoteMovementFailure();
  1409. *pResult = pMoveGoal->directTrace.fStatus;
  1410. pMoveGoal->maxDist = 0;
  1411. return true;
  1412. }
  1413. #endif
  1414. *pResult = AIMR_OK;
  1415. return true;
  1416. }
  1417. #ifdef PHYSICS_NPC_SHADOW_DISCREPENCY
  1418. if ( pMoveGoal->maxDist + AI_EPS_CASTS < tolerance )
  1419. #else
  1420. if ( pMoveGoal->maxDist < tolerance )
  1421. #endif
  1422. {
  1423. if ( !fTargetIsGoal ||
  1424. ( pMoveGoal->directTrace.fStatus != AIMR_BLOCKED_NPC ) ||
  1425. ( !((CAI_BaseNPC *)pMoveGoal->directTrace.pObstruction)->IsMoving() ) )
  1426. {
  1427. pMoveGoal->maxDist = distClear;
  1428. *pResult = AIMR_OK;
  1429. if ( fShouldAdvancePath )
  1430. {
  1431. AdvancePath();
  1432. }
  1433. else if ( distClear < 0.025 )
  1434. {
  1435. *pResult = pMoveGoal->directTrace.fStatus;
  1436. }
  1437. return true;
  1438. }
  1439. }
  1440. }
  1441. #ifdef HL2_EPISODIC
  1442. // Build an avoidance path around a vehicle
  1443. if ( ai_vehicle_avoidance.GetBool() && pMoveGoal->directTrace.pObstruction != NULL && pMoveGoal->directTrace.pObstruction->GetServerVehicle() != NULL )
  1444. {
  1445. //FIXME: This should change into a flag which forces an OBB route to be formed around the entity in question!
  1446. AI_Waypoint_t *pOBB = GetPathfinder()->BuildOBBAvoidanceRoute( GetOuter()->GetAbsOrigin(),
  1447. GetGoalPos(),
  1448. pMoveGoal->directTrace.pObstruction,
  1449. GetNavTargetEntity(),
  1450. GetNavType() );
  1451. // See if we need to complete this navigation
  1452. if ( pOBB == NULL )
  1453. {
  1454. /*
  1455. if ( GetOuter()->ShouldFailNav( true ) == false )
  1456. {
  1457. // Create a physics solver to allow us to pass
  1458. NPCPhysics_CreateSolver( GetOuter(), pMoveGoal->directTrace.pObstruction, true, 5.0f );
  1459. return true;
  1460. }
  1461. */
  1462. }
  1463. else
  1464. {
  1465. // Otherwise we have a clear path to move around
  1466. GetPath()->PrependWaypoints( pOBB );
  1467. return true;
  1468. }
  1469. }
  1470. #endif // HL2_EPISODIC
  1471. // Allow the NPC to override this behavior. Above logic takes priority
  1472. if ( GetOuter()->OnObstructionPreSteer( pMoveGoal, distClear, pResult ) )
  1473. {
  1474. DebugNoteMovementFailureIfBlocked( *pResult );
  1475. return true;
  1476. }
  1477. if ( !m_hBigStepGroundEnt.Get() &&
  1478. pMoveGoal->directTrace.pObstruction &&
  1479. distClear < GetHullWidth() &&
  1480. pMoveGoal->directTrace.pObstruction == GetOuter()->GetGroundEntity() &&
  1481. ( pMoveGoal->directTrace.pObstruction->IsPlayer() ||
  1482. dynamic_cast<CPhysicsProp *>( pMoveGoal->directTrace.pObstruction ) ) )
  1483. {
  1484. m_hBigStepGroundEnt = pMoveGoal->directTrace.pObstruction;
  1485. *pResult = AIMR_CHANGE_TYPE;
  1486. return true;
  1487. }
  1488. return false;
  1489. }
  1490. //-----------------------------------------------------------------------------
  1491. bool CAI_Navigator::OnInsufficientStopDist( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult )
  1492. {
  1493. // Allow the NPC to override this behavior
  1494. if ( GetOuter()->OnInsufficientStopDist( pMoveGoal, distClear, pResult ))
  1495. {
  1496. DebugNoteMovementFailureIfBlocked( *pResult );
  1497. return true;
  1498. }
  1499. #ifdef PHYSICS_NPC_SHADOW_DISCREPENCY
  1500. if ( distClear < AI_EPS_CASTS ) // needed because vphysics can pull us back up to this far
  1501. {
  1502. DebugNoteMovementFailure();
  1503. *pResult = pMoveGoal->directTrace.fStatus;
  1504. pMoveGoal->maxDist = 0;
  1505. return true;
  1506. }
  1507. #endif
  1508. if ( !IsMovingOutOfWay( *pMoveGoal, distClear ) )
  1509. {
  1510. float goalDist = ComputePathDistance( GetNavType(), GetAbsOrigin(), GetPath()->ActualGoalPosition() );
  1511. if ( goalDist < GetGoalTolerance() + 0.01 )
  1512. {
  1513. pMoveGoal->maxDist = distClear;
  1514. pMoveGoal->flags |= AILMG_CONSUME_INTERVAL;
  1515. OnNavComplete();
  1516. *pResult = AIMR_OK;
  1517. return true;
  1518. }
  1519. if ( m_NextSidestepTimer.Expired() )
  1520. {
  1521. // Try bumping to side
  1522. m_NextSidestepTimer.Reset();
  1523. AIMoveTrace_t moveTrace;
  1524. Vector vDeflection;
  1525. CalculateDeflection( GetLocalOrigin(), pMoveGoal->dir, pMoveGoal->directTrace.vHitNormal, &vDeflection );
  1526. for ( int i = 1; i > -2; i -= 2 )
  1527. {
  1528. Vector testLoc = GetLocalOrigin() + ( vDeflection * GetHullWidth() * 2.0) * i;
  1529. GetMoveProbe()->MoveLimit( GetNavType(), GetLocalOrigin(), testLoc, GetOuter()->GetAITraceMask(), NULL, &moveTrace );
  1530. if ( moveTrace.fStatus == AIMR_OK )
  1531. {
  1532. Vector vNewWaypoint = moveTrace.vEndPosition;
  1533. GetMoveProbe()->MoveLimit( GetNavType(), vNewWaypoint, pMoveGoal->target, GetOuter()->GetAITraceMask_BrushOnly(), NULL, &moveTrace );
  1534. if ( moveTrace.fStatus == AIMR_OK )
  1535. {
  1536. PrependWaypoint( vNewWaypoint, GetNavType(), bits_WP_TO_DETOUR );
  1537. *pResult = AIMR_CHANGE_TYPE;
  1538. return true;
  1539. }
  1540. }
  1541. }
  1542. }
  1543. if ( distClear < 1.0 )
  1544. {
  1545. // too close, nothing happening, I'm screwed
  1546. DebugNoteMovementFailure();
  1547. *pResult = pMoveGoal->directTrace.fStatus;
  1548. pMoveGoal->maxDist = 0;
  1549. return true;
  1550. }
  1551. return false;
  1552. }
  1553. *pResult = AIMR_OK;
  1554. pMoveGoal->maxDist = distClear;
  1555. pMoveGoal->flags |= AILMG_CONSUME_INTERVAL;
  1556. return true;
  1557. }
  1558. //-----------------------------------------------------------------------------
  1559. bool CAI_Navigator::OnFailedSteer( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult )
  1560. {
  1561. // Allow the NPC to override this behavior
  1562. if ( GetOuter()->OnFailedSteer( pMoveGoal, distClear, pResult ))
  1563. {
  1564. DebugNoteMovementFailureIfBlocked( *pResult );
  1565. return true;
  1566. }
  1567. if ( pMoveGoal->flags & AILMG_TARGET_IS_GOAL )
  1568. {
  1569. if ( distClear >= GetPathDistToGoal() )
  1570. {
  1571. *pResult = AIMR_OK;
  1572. return true;
  1573. }
  1574. if ( distClear > pMoveGoal->maxDist - GetPath()->GetGoalTolerance() )
  1575. {
  1576. Assert( CurWaypointIsGoal() && fabs(pMoveGoal->maxDist - GetPathDistToCurWaypoint()) < 0.01 );
  1577. if ( pMoveGoal->maxDist > distClear )
  1578. pMoveGoal->maxDist = distClear;
  1579. if ( distClear < 0.125 )
  1580. OnNavComplete();
  1581. pMoveGoal->flags |= AILMG_CONSUME_INTERVAL;
  1582. *pResult = AIMR_OK;
  1583. return true;
  1584. }
  1585. }
  1586. if ( !( pMoveGoal->flags & AILMG_TARGET_IS_TRANSITION ) )
  1587. {
  1588. float distToWaypoint = GetPathDistToCurWaypoint();
  1589. float halfHull = GetHullWidth() * 0.5;
  1590. if ( distToWaypoint < halfHull )
  1591. {
  1592. if ( distClear > distToWaypoint + halfHull )
  1593. {
  1594. *pResult = AIMR_OK;
  1595. return true;
  1596. }
  1597. }
  1598. }
  1599. #if 0
  1600. if ( pMoveGoal->directTrace.pObstruction->MyNPCPointer() &&
  1601. !GetOuter()->IsUsingSmallHull() &&
  1602. GetOuter()->SetHullSizeSmall() )
  1603. {
  1604. *pResult = AIMR_CHANGE_TYPE;
  1605. return true;
  1606. }
  1607. #endif
  1608. if ( !TestingSteering() && pMoveGoal->directTrace.fStatus == AIMR_BLOCKED_NPC && pMoveGoal->directTrace.vHitNormal != vec3_origin )
  1609. {
  1610. AIMoveTrace_t moveTrace;
  1611. Vector vDeflection;
  1612. CalculateDeflection( GetLocalOrigin(), pMoveGoal->dir, pMoveGoal->directTrace.vHitNormal, &vDeflection );
  1613. if ( pMoveGoal->dir.AsVector2D().Dot( vDeflection.AsVector2D() ) > 0.7 )
  1614. {
  1615. Vector testLoc = GetLocalOrigin() + ( vDeflection * pMoveGoal->curExpectedDist );
  1616. GetMoveProbe()->MoveLimit( GetNavType(), GetLocalOrigin(), testLoc, GetOuter()->GetAITraceMask(), NULL, &moveTrace );
  1617. if ( moveTrace.fStatus == AIMR_OK )
  1618. {
  1619. pMoveGoal->dir = vDeflection;
  1620. pMoveGoal->maxDist = pMoveGoal->curExpectedDist;
  1621. *pResult = AIMR_OK;
  1622. return true;
  1623. }
  1624. }
  1625. }
  1626. // If fail steer more than once after a second with no measurable progres, fail completely
  1627. // This usually means a sucessful triangulation was not actually a valid avoidance.
  1628. const float MOVE_TOLERANCE = 12.0;
  1629. const float TIME_TOLERANCE = 1.0;
  1630. if ( m_vPosBeginFailedSteer == vec3_invalid || ( m_vPosBeginFailedSteer - GetAbsOrigin() ).LengthSqr() > Square(MOVE_TOLERANCE) )
  1631. {
  1632. m_vPosBeginFailedSteer = GetAbsOrigin();
  1633. m_timeBeginFailedSteer = gpGlobals->curtime;
  1634. }
  1635. else if ( GetNavType() == NAV_GROUND &&
  1636. gpGlobals->curtime - m_timeBeginFailedSteer > TIME_TOLERANCE &&
  1637. GetOuter()->m_flGroundSpeed * TIME_TOLERANCE > MOVE_TOLERANCE )
  1638. {
  1639. *pResult = AIMR_ILLEGAL;
  1640. return true;
  1641. }
  1642. if ( !(pMoveGoal->flags & AILMG_NO_AVOIDANCE_PATHS) && distClear < pMoveGoal->maxDist && !TestingSteering() )
  1643. {
  1644. if ( PrependLocalAvoidance( distClear, pMoveGoal->directTrace ) )
  1645. {
  1646. *pResult = AIMR_CHANGE_TYPE;
  1647. return true;
  1648. }
  1649. }
  1650. return false;
  1651. }
  1652. //-----------------------------------------------------------------------------
  1653. bool CAI_Navigator::OnFailedLocalNavigation( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult )
  1654. {
  1655. // Allow the NPC to override this behavior
  1656. if ( GetOuter()->OnFailedLocalNavigation( pMoveGoal, distClear, pResult ))
  1657. {
  1658. DebugNoteMovementFailureIfBlocked( *pResult );
  1659. return true;
  1660. }
  1661. if ( DelayNavigationFailure( pMoveGoal->directTrace ) )
  1662. {
  1663. *pResult = AIMR_OK;
  1664. pMoveGoal->maxDist = distClear;
  1665. pMoveGoal->flags |= AILMG_CONSUME_INTERVAL;
  1666. return true;
  1667. }
  1668. return false;
  1669. }
  1670. //-----------------------------------------------------------------------------
  1671. bool CAI_Navigator::DelayNavigationFailure( const AIMoveTrace_t &trace )
  1672. {
  1673. // This code only handles the case of a group of AIs in close proximity, preparing
  1674. // to move mostly as a group, but on slightly different think schedules. Without
  1675. // this patience factor, in the middle or at the rear might fail just because it
  1676. // happened to have its think function called a half cycle before the one
  1677. // in front of it.
  1678. CAI_BaseNPC *pBlocker = trace.pObstruction ? trace.pObstruction->MyNPCPointer() : NULL;
  1679. Assert( m_fPeerMoveWait == false || pBlocker == m_hPeerWaitingOn ); // expected to be cleared each frame, and never call this function twice
  1680. if ( !m_fPeerMoveWait || pBlocker != m_hPeerWaitingOn )
  1681. {
  1682. if ( pBlocker )
  1683. {
  1684. if ( m_hPeerWaitingOn != pBlocker || m_PeerWaitClearTimer.Expired() )
  1685. {
  1686. m_fPeerMoveWait = true;
  1687. m_hPeerWaitingOn = pBlocker;
  1688. m_PeerWaitMoveTimer.Reset();
  1689. m_PeerWaitClearTimer.Reset();
  1690. if ( pBlocker->GetGroundEntity() == GetOuter() )
  1691. {
  1692. trace_t bumpTrace;
  1693. pBlocker->GetMoveProbe()->TraceHull( pBlocker->GetAbsOrigin(),
  1694. pBlocker->GetAbsOrigin() + Vector(0,0,2.0),
  1695. GetOuter()->GetAITraceMask(),
  1696. &bumpTrace );
  1697. if ( bumpTrace.fraction == 1.0 )
  1698. {
  1699. UTIL_SetOrigin(pBlocker, bumpTrace.endpos, true);
  1700. }
  1701. }
  1702. }
  1703. else if ( m_hPeerWaitingOn == pBlocker && !m_PeerWaitMoveTimer.Expired() )
  1704. {
  1705. m_fPeerMoveWait = true;
  1706. }
  1707. }
  1708. }
  1709. return m_fPeerMoveWait;
  1710. }
  1711. //-----------------------------------------------------------------------------
  1712. // @TODO (toml 11-12-02): right now, physics can pull the box back pretty far even though a hull
  1713. // trace said we could make the move. Jay is looking into it. For now, if the NPC physics shadow
  1714. // is active, allow for a bugger tolerance
  1715. extern ConVar npc_vphysics;
  1716. bool test_it = false;
  1717. bool CAI_Navigator::MoveUpdateWaypoint( AIMoveResult_t *pResult )
  1718. {
  1719. // Note that goal & waypoint tolerances are handled in progress blockage cases (e.g., OnObstructionPreSteer)
  1720. AI_Waypoint_t *pCurWaypoint = GetPath()->GetCurWaypoint();
  1721. float waypointDist = ComputePathDistance( GetNavType(), GetLocalOrigin(), pCurWaypoint->GetPos() );
  1722. bool bIsGoal = CurWaypointIsGoal();
  1723. float tolerance = ( npc_vphysics.GetBool() ) ? 0.25 : 0.0625;
  1724. bool fHit = false;
  1725. if ( waypointDist <= tolerance )
  1726. {
  1727. if ( test_it )
  1728. {
  1729. if ( pCurWaypoint->GetNext() && pCurWaypoint->GetNext()->NavType() != pCurWaypoint->NavType() )
  1730. {
  1731. if ( waypointDist < 0.001 )
  1732. fHit = true;
  1733. }
  1734. else
  1735. fHit = true;
  1736. }
  1737. else
  1738. fHit = true;
  1739. }
  1740. if ( fHit )
  1741. {
  1742. if ( bIsGoal )
  1743. {
  1744. OnNavComplete();
  1745. *pResult = AIMR_OK;
  1746. }
  1747. else
  1748. {
  1749. AdvancePath();
  1750. *pResult = AIMR_CHANGE_TYPE;
  1751. }
  1752. return true;
  1753. }
  1754. return false;
  1755. }
  1756. //-----------------------------------------------------------------------------
  1757. bool CAI_Navigator::OnMoveStalled( const AILocalMoveGoal_t &move )
  1758. {
  1759. DbgNavMsg(GetOuter(), "CAI_Navigator::OnMoveStalled calling SetActivity(GetStoppedActivity)");
  1760. SetActivity( GetOuter()->GetStoppedActivity() );
  1761. return true;
  1762. }
  1763. //-----------------------------------------------------------------------------
  1764. bool CAI_Navigator::OnMoveExecuteFailed( const AILocalMoveGoal_t &move, const AIMoveTrace_t &trace, AIMotorMoveResult_t fMotorResult, AIMoveResult_t *pResult )
  1765. {
  1766. // Allow the NPC to override this behavior
  1767. if ( GetOuter()->OnMoveExecuteFailed( move, trace, fMotorResult, pResult ))
  1768. {
  1769. DebugNoteMovementFailureIfBlocked( *pResult );
  1770. return true;
  1771. }
  1772. if ( !m_hBigStepGroundEnt.Get() &&
  1773. trace.pObstruction &&
  1774. trace.flTotalDist - trace.flDistObstructed < GetHullWidth() &&
  1775. trace.pObstruction == GetOuter()->GetGroundEntity() &&
  1776. ( trace.pObstruction->IsPlayer() ||
  1777. dynamic_cast<CPhysicsProp *>( trace.pObstruction ) ) )
  1778. {
  1779. m_hBigStepGroundEnt = trace.pObstruction;
  1780. *pResult = AIMR_CHANGE_TYPE;
  1781. return true;
  1782. }
  1783. if ( fMotorResult == AIM_PARTIAL_HIT_TARGET )
  1784. {
  1785. OnNavComplete();
  1786. *pResult = AIMR_OK;
  1787. }
  1788. else if ( fMotorResult == AIM_PARTIAL_HIT_NPC && DelayNavigationFailure( trace ) )
  1789. {
  1790. *pResult = AIMR_OK;
  1791. }
  1792. return true;
  1793. }
  1794. //-----------------------------------------------------------------------------
  1795. bool CAI_Navigator::OnMoveBlocked( AIMoveResult_t *pResult )
  1796. {
  1797. if ( *pResult == AIMR_BLOCKED_NPC &&
  1798. GetPath()->GetCurWaypoint() &&
  1799. ( GetPath()->GetCurWaypoint()->Flags() & bits_WP_TO_DOOR ) )
  1800. {
  1801. CBasePropDoor *pDoor = (CBasePropDoor *)(CBaseEntity *)GetPath()->GetCurWaypoint()->GetEHandleData();
  1802. if (pDoor != NULL)
  1803. {
  1804. GetOuter()->OpenPropDoorBegin( pDoor );
  1805. *pResult = AIMR_OK;
  1806. return true;
  1807. }
  1808. }
  1809. // Allow the NPC to override this behavior
  1810. if ( GetOuter()->OnMoveBlocked( pResult ))
  1811. return true;
  1812. float flWaypointDist;
  1813. if ( !GetPath()->CurWaypointIsGoal() && GetPath()->GetCurWaypoint()->IsReducible() )
  1814. {
  1815. flWaypointDist = ComputePathDistance( GetNavType(), GetLocalOrigin(), GetCurWaypointPos() );
  1816. if ( flWaypointDist < GetHullWidth() )
  1817. {
  1818. AdvancePath();
  1819. *pResult = AIMR_CHANGE_TYPE;
  1820. }
  1821. }
  1822. DbgNavMsg(GetOuter(), "CAI_Navigator::OnMoveBlocked calling SetActivity(GetStoppedActivity)");
  1823. SetActivity( GetOuter()->GetStoppedActivity() );
  1824. const float EPS = 0.1;
  1825. flWaypointDist = ComputePathDistance( GetNavType(), GetLocalOrigin(), GetPath()->ActualGoalPosition() );
  1826. if ( flWaypointDist < GetGoalTolerance() + EPS )
  1827. {
  1828. OnNavComplete();
  1829. *pResult = AIMR_OK;
  1830. return true;
  1831. }
  1832. return false;
  1833. }
  1834. //-------------------------------------
  1835. AIMoveResult_t CAI_Navigator::MoveEnact( const AILocalMoveGoal_t &baseMove )
  1836. {
  1837. AIMoveResult_t result = AIMR_ILLEGAL;
  1838. AILocalMoveGoal_t move = baseMove;
  1839. result = GetLocalNavigator()->MoveCalc( &move, ( m_flLastSuccessfulSimplifyTime == gpGlobals->curtime ) );
  1840. if ( result != AIMR_OK )
  1841. m_hLastBlockingEnt = move.directTrace.pObstruction;
  1842. else
  1843. {
  1844. m_hLastBlockingEnt = NULL;
  1845. GetMoveProbe()->ClearBlockingEntity();
  1846. }
  1847. if ( result == AIMR_OK && !m_fNavComplete )
  1848. {
  1849. Assert( GetPath()->GetCurWaypoint() );
  1850. result = GetMotor()->MoveNormalExecute( move );
  1851. }
  1852. else if ( result != AIMR_CHANGE_TYPE )
  1853. {
  1854. GetMotor()->MoveStop();
  1855. }
  1856. if ( IsMoveBlocked( result ) )
  1857. {
  1858. OnMoveBlocked( &result );
  1859. }
  1860. return result;
  1861. }
  1862. //-----------------------------------------------------------------------------
  1863. AIMoveResult_t CAI_Navigator::MoveNormal()
  1864. {
  1865. if (!PreMove())
  1866. return AIMR_ILLEGAL;
  1867. if ( ShouldTestFailMove() )
  1868. return AIMR_ILLEGAL;
  1869. // --------------------------------
  1870. AIMoveResult_t result = AIMR_ILLEGAL;
  1871. if ( MoveUpdateWaypoint( &result ) )
  1872. return result;
  1873. // --------------------------------
  1874. // Set activity to be the Navigation activity
  1875. float preMoveSpeed = GetIdealSpeed();
  1876. Activity preMoveActivity = GetActivity();
  1877. int nPreMoveSequence = GetOuter()->GetSequence(); // this is an unfortunate necessity to ensure setting back the activity picks the right one if it had been sticky
  1878. Vector vStart = GetAbsOrigin();
  1879. // --------------------------------
  1880. // FIXME: only need since IdealSpeed isn't based on movement activity but immediate activity!
  1881. SetActivity( GetMovementActivity() );
  1882. if ( m_bValidateActivitySpeed && GetIdealSpeed() <= 0.0f )
  1883. {
  1884. if ( GetActivity() == ACT_TRANSITION )
  1885. return AIMR_OK;
  1886. DevMsg( "%s moving with speed <= 0 (%s)\n", GetEntClassname(), GetOuter()->GetSequenceName( GetSequence() ) );
  1887. }
  1888. // --------------------------------
  1889. AILocalMoveGoal_t move;
  1890. MoveCalcBaseGoal( &move );
  1891. result = MoveEnact( move );
  1892. // --------------------------------
  1893. // If we didn't actually move, but it was a success (i.e., blocked but within tolerance), make sure no visual artifact
  1894. // FIXME: only needed because of the above slamming of SetActivity(), which is only needed
  1895. // because GetIdealSpeed() looks at the current activity instead of the movement activity.
  1896. if ( result == AIMR_OK && preMoveSpeed < 0.01 )
  1897. {
  1898. if ( ( GetAbsOrigin() - vStart ).Length() < 0.01 )
  1899. {
  1900. DbgNavMsg2(GetOuter(), "CAI_Navigator::MoveNormal calling SetSequence(%s) and SetActivity(%s)", GetOuter()->GetSequenceName(nPreMoveSequence), GetOuter()->GetActivityName(preMoveActivity));
  1901. GetOuter()->SetSequence( nPreMoveSequence );
  1902. SetActivity( preMoveActivity );
  1903. }
  1904. }
  1905. // --------------------------------
  1906. return result;
  1907. }
  1908. //-----------------------------------------------------------------------------
  1909. bool CAI_Navigator::PreMove()
  1910. {
  1911. Navigation_t goalType = GetPath()->CurWaypointNavType();
  1912. Navigation_t curType = GetNavType();
  1913. m_fPeerMoveWait = false;
  1914. if ( goalType == NAV_GROUND && curType != NAV_GROUND )
  1915. {
  1916. // Warn for things other than crawling
  1917. if ( curType != NAV_CRAWL )
  1918. DevMsg( "Warning: %s(%s) appears to have wrong nav type in CAI_Navigator::MoveGround()\n", GetOuter()->GetClassname(), STRING( GetOuter()->GetEntityName() ) );
  1919. switch ( curType )
  1920. {
  1921. case NAV_CLIMB:
  1922. {
  1923. GetMotor()->MoveClimbStop();
  1924. break;
  1925. }
  1926. case NAV_JUMP:
  1927. {
  1928. GetMotor()->MoveJumpStop();
  1929. break;
  1930. }
  1931. }
  1932. SetNavType( NAV_GROUND );
  1933. }
  1934. else if ( goalType == NAV_FLY && curType != NAV_FLY )
  1935. {
  1936. AssertMsg( 0, ( "GetNavType() == NAV_FLY" ) );
  1937. return false;
  1938. }
  1939. // --------------------------------
  1940. Assert( GetMotor()->GetMoveInterval() > 0 );
  1941. // --------------------------------
  1942. SimplifyPath();
  1943. return true;
  1944. }
  1945. //--------------------------------------------------------------------------------------------
  1946. bool CAI_Navigator::IsMovingOutOfWay( const AILocalMoveGoal_t &moveGoal, float distClear )
  1947. {
  1948. // FIXME: We can make this work for regular entities; although the
  1949. // original code was simply looking @ NPCs. I'm reproducing that code now
  1950. // although I want to make it work for both.
  1951. CAI_BaseNPC *pBlocker = moveGoal.directTrace.pObstruction ? moveGoal.directTrace.pObstruction->MyNPCPointer() : NULL;
  1952. // if it's the world, it ain't moving
  1953. if (!pBlocker)
  1954. return false;
  1955. // if they're doing something, assume it'll work out
  1956. if (pBlocker->IsMoving())
  1957. {
  1958. if ( distClear > moveGoal.curExpectedDist * 0.75 )
  1959. return true;
  1960. Vector2D velBlocker = pBlocker->GetMotor()->GetCurVel().AsVector2D();
  1961. Vector2D velBlockerNorm = velBlocker;
  1962. Vector2DNormalize( velBlockerNorm );
  1963. float dot = moveGoal.dir.AsVector2D().Dot( velBlockerNorm );
  1964. if (dot > -0.25 )
  1965. {
  1966. return true;
  1967. }
  1968. }
  1969. return false;
  1970. }
  1971. //-----------------------------------------------------------------------------
  1972. // Purpose: Return true if the navigator should apply movement to the NPC
  1973. //-----------------------------------------------------------------------------
  1974. bool CAI_Navigator::ShouldMove( bool bHasAGoal )
  1975. {
  1976. return ( bHasAGoal && ActivityIsLocomotive( GetMovementActivity() ) );
  1977. }
  1978. //-----------------------------------------------------------------------------
  1979. // Purpose: Move towards the next waypoint on my route
  1980. // Input :
  1981. // Output :
  1982. //-----------------------------------------------------------------------------
  1983. enum AINavResult_t
  1984. {
  1985. AINR_OK,
  1986. AINR_NO_GOAL,
  1987. AINR_NO_ROUTE,
  1988. AINR_BLOCKED,
  1989. AINR_ILLEGAL
  1990. };
  1991. bool CAI_Navigator::Move( float flInterval )
  1992. {
  1993. if (flInterval > 1.0)
  1994. {
  1995. // Bound interval so we don't get ludicrous motion when debugging
  1996. // or when framerate drops catostrophically
  1997. flInterval = 1.0;
  1998. }
  1999. if ( GetOuter()->OverrideMove( flInterval ) )
  2000. return true; // assume override move handles stopping issues
  2001. // UNDONE: Figure out how much of the timestep was consumed by movement
  2002. // this frame and restart the movement/schedule engine if necessary
  2003. bool bHasGoal = GetGoalType() != GOALTYPE_NONE;
  2004. bool bIsTurning = HasMemory( bits_MEMORY_TURNING );
  2005. if ( bHasGoal )
  2006. {
  2007. if ( bIsTurning )
  2008. {
  2009. if ( gpGlobals->curtime - GetPath()->GetStartTime() > 5 )
  2010. {
  2011. Forget( bits_MEMORY_TURNING );
  2012. bIsTurning = false;
  2013. DbgNavMsg( GetOuter(), "NPC appears stuck turning. Proceeding.\n" );
  2014. }
  2015. }
  2016. if ( ActivityIsLocomotive( m_PreviousMoveActivity ) && !ActivityIsLocomotive( GetMovementActivity() ) )
  2017. {
  2018. SetMovementActivity( GetOuter()->TranslateActivity( m_PreviousMoveActivity ) );
  2019. }
  2020. }
  2021. else
  2022. {
  2023. m_PreviousMoveActivity = ACT_RESET;
  2024. m_PreviousArrivalActivity = ACT_RESET;
  2025. }
  2026. bool fShouldMove = ShouldMove( bHasGoal );
  2027. if ( !fShouldMove )
  2028. {
  2029. // @TODO (toml 10-30-02): the climb part of this is unacceptable, but needed until navigation can handle commencing
  2030. // a navigation while in the middle of a climb
  2031. if ( GetNavType() == NAV_CLIMB )
  2032. {
  2033. GetMotor()->MoveClimbStop();
  2034. SetNavType( NAV_GROUND );
  2035. }
  2036. GetMotor()->MoveStop();
  2037. AssertMsg( TaskIsRunning() || TaskIsComplete(), ("Schedule stalled!!\n") );
  2038. return false;
  2039. }
  2040. AINavResult_t result = AINR_OK;
  2041. GetMotor()->SetMoveInterval( flInterval );
  2042. // ---------------------------------------------------------------------
  2043. // Move should never happen if I don't have a movement goal or route
  2044. // ---------------------------------------------------------------------
  2045. if ( GetPath()->GoalType() == GOALTYPE_NONE )
  2046. {
  2047. DevWarning( "Move requested with no route!\n" );
  2048. result = AINR_NO_GOAL;
  2049. }
  2050. else if (!GetPath()->GetCurWaypoint())
  2051. {
  2052. DevWarning( "Move goal with no route!\n" );
  2053. GetPath()->Clear();
  2054. result = AINR_NO_ROUTE;
  2055. }
  2056. if ( result == AINR_OK )
  2057. {
  2058. // ---------------------------------------------------------------------
  2059. // If I've been asked to wait, let's wait
  2060. // ---------------------------------------------------------------------
  2061. if ( GetOuter()->ShouldMoveWait() )
  2062. {
  2063. GetMotor()->MovePaused();
  2064. return false;
  2065. }
  2066. int nLoopCount = 0;
  2067. bool bMoved = false;
  2068. AIMoveResult_t moveResult = AIMR_CHANGE_TYPE;
  2069. m_fNavComplete = false;
  2070. while ( moveResult >= AIMR_OK && !m_fNavComplete )
  2071. {
  2072. if ( GetMotor()->GetMoveInterval() <= 0 )
  2073. {
  2074. moveResult = AIMR_OK;
  2075. break;
  2076. }
  2077. // TODO: move higher up the call chain?
  2078. if ( !m_bCalledStartMove )
  2079. {
  2080. GetMotor()->MoveStart();
  2081. m_bCalledStartMove = true;
  2082. }
  2083. if ( m_hBigStepGroundEnt && m_hBigStepGroundEnt != GetOuter()->GetGroundEntity() )
  2084. m_hBigStepGroundEnt = NULL;
  2085. switch (GetPath()->CurWaypointNavType())
  2086. {
  2087. case NAV_CRAWL:
  2088. moveResult = MoveCrawl();
  2089. break;
  2090. case NAV_CLIMB:
  2091. moveResult = MoveClimb();
  2092. break;
  2093. case NAV_JUMP:
  2094. moveResult = MoveJump();
  2095. break;
  2096. case NAV_GROUND:
  2097. case NAV_FLY:
  2098. moveResult = MoveNormal();
  2099. break;
  2100. default:
  2101. DevMsg( "Bogus route move type!");
  2102. moveResult = AIMR_ILLEGAL;
  2103. break;
  2104. }
  2105. if ( moveResult == AIMR_OK )
  2106. bMoved = true;
  2107. ++nLoopCount;
  2108. if ( nLoopCount > 16 )
  2109. {
  2110. DevMsg( "ERROR: %s navigation not terminating. Possibly bad cyclical solving?\n", GetOuter()->GetDebugName() );
  2111. moveResult = AIMR_ILLEGAL;
  2112. switch (GetPath()->CurWaypointNavType())
  2113. {
  2114. case NAV_GROUND:
  2115. case NAV_FLY:
  2116. case NAV_CRAWL:
  2117. OnMoveBlocked( &moveResult );
  2118. break;
  2119. }
  2120. break;
  2121. }
  2122. }
  2123. // --------------------------------------------
  2124. // Update move status
  2125. // --------------------------------------------
  2126. if ( IsMoveBlocked( moveResult ) )
  2127. {
  2128. bool bRecovered = false;
  2129. if (moveResult != AIMR_BLOCKED_NPC || GetNavType() == NAV_CLIMB || GetNavType() == NAV_JUMP || GetPath()->CurWaypointNavType() == NAV_JUMP )
  2130. {
  2131. if ( MarkCurWaypointFailedLink() )
  2132. {
  2133. AI_Waypoint_t *pSavedWaypoints = GetPath()->GetCurWaypoint();
  2134. if ( pSavedWaypoints )
  2135. {
  2136. GetPath()->SetWaypoints( NULL );
  2137. if ( RefindPathToGoal( false, true ) )
  2138. {
  2139. DeleteAll( pSavedWaypoints );
  2140. bRecovered = true;
  2141. }
  2142. else
  2143. GetPath()->SetWaypoints( pSavedWaypoints );
  2144. }
  2145. }
  2146. }
  2147. if ( !bRecovered )
  2148. {
  2149. OnNavFailed( ( moveResult == AIMR_ILLEGAL ) ? FAIL_NO_ROUTE_ILLEGAL : FAIL_NO_ROUTE_BLOCKED, true );
  2150. }
  2151. }
  2152. return bMoved;
  2153. }
  2154. static AI_TaskFailureCode_t failures[] =
  2155. {
  2156. NO_TASK_FAILURE, // AINR_OK (never should happen)
  2157. FAIL_NO_ROUTE_GOAL, // AINR_NO_GOAL
  2158. FAIL_NO_ROUTE, // AINR_NO_ROUTE
  2159. FAIL_NO_ROUTE_BLOCKED, // AINR_BLOCKED
  2160. FAIL_NO_ROUTE_ILLEGAL // AINR_ILLEGAL
  2161. };
  2162. OnNavFailed( failures[result], false );
  2163. return false;
  2164. }
  2165. //-----------------------------------------------------------------------------
  2166. // Purpose: Returns yaw speed based on what they're doing.
  2167. //-----------------------------------------------------------------------------
  2168. float CAI_Navigator::CalcYawSpeed( void )
  2169. {
  2170. // Allow the NPC to override this behavior
  2171. float flNPCYaw = GetOuter()->CalcYawSpeed();
  2172. if (flNPCYaw >= 0.0f)
  2173. return flNPCYaw;
  2174. float maxYaw = MaxYawSpeed();
  2175. //return maxYaw;
  2176. if( IsGoalSet() && GetIdealSpeed() != 0.0)
  2177. {
  2178. // ---------------------------------------------------
  2179. // If not moving to a waypoint use a base turing speed
  2180. // ---------------------------------------------------
  2181. if (!GetPath()->GetCurWaypoint())
  2182. {
  2183. return maxYaw;
  2184. }
  2185. // --------------------------------------------------------------
  2186. // If moving towards a waypoint, set the turn speed based on the
  2187. // distance of the waypoint and my forward velocity
  2188. // --------------------------------------------------------------
  2189. if (GetIdealSpeed() > 0)
  2190. {
  2191. // -----------------------------------------------------------------
  2192. // Get the projection of npc's heading direction on the waypoint dir
  2193. // -----------------------------------------------------------------
  2194. float waypointDist = (GetPath()->CurWaypointPos() - GetLocalOrigin()).Length();
  2195. // If waypoint is close, aim for the waypoint
  2196. if (waypointDist < 100)
  2197. {
  2198. float scale = 1 + (0.01*(100 - waypointDist));
  2199. return (maxYaw * scale);
  2200. }
  2201. }
  2202. }
  2203. return maxYaw;
  2204. }
  2205. //-----------------------------------------------------------------------------
  2206. float CAI_Navigator::GetStepDownMultiplier()
  2207. {
  2208. if ( m_hBigStepGroundEnt )
  2209. {
  2210. if ( !m_hBigStepGroundEnt->IsPlayer() )
  2211. return 2.6;
  2212. else
  2213. return 10.0;
  2214. }
  2215. return 1.0;
  2216. }
  2217. //-----------------------------------------------------------------------------
  2218. // Purpose: Attempts to advance the route to the next waypoint, triangulating
  2219. // around entities that are in the way
  2220. // Input :
  2221. // Output :
  2222. //-----------------------------------------------------------------------------
  2223. void CAI_Navigator::AdvancePath()
  2224. {
  2225. DbgNavMsg( GetOuter(), "Advancing path\n" );
  2226. AI_Waypoint_t *pCurWaypoint = GetPath()->GetCurWaypoint();
  2227. bool bPassingPathcorner = ( ( pCurWaypoint->Flags() & bits_WP_TO_PATHCORNER ) != 0 );
  2228. if ( bPassingPathcorner )
  2229. {
  2230. Assert( !pCurWaypoint->GetNext() || (pCurWaypoint->GetNext()->Flags() & (bits_WP_TO_PATHCORNER | bits_WP_TO_GOAL )) == (bits_WP_TO_PATHCORNER | bits_WP_TO_GOAL ));
  2231. CBaseEntity *pEntity = pCurWaypoint->hPathCorner;
  2232. if ( pEntity )
  2233. {
  2234. variant_t emptyVariant;
  2235. pEntity->AcceptInput( "InPass", GetOuter(), pEntity, emptyVariant, 0 );
  2236. }
  2237. }
  2238. if ( GetPath()->CurWaypointIsGoal() )
  2239. return;
  2240. if ( pCurWaypoint->Flags() & bits_WP_TO_DOOR )
  2241. {
  2242. CBasePropDoor *pDoor = static_cast<CBasePropDoor *>( pCurWaypoint->GetEHandleData().Get() );
  2243. if (pDoor != NULL)
  2244. {
  2245. GetOuter()->OpenPropDoorBegin(pDoor);
  2246. }
  2247. else
  2248. {
  2249. DevMsg("%s trying to open a door that has been deleted!\n", GetOuter()->GetDebugName());
  2250. }
  2251. }
  2252. GetPath()->Advance();
  2253. // If we've just passed a path_corner, advance m_pGoalEnt
  2254. if ( bPassingPathcorner )
  2255. {
  2256. pCurWaypoint = GetPath()->GetCurWaypoint();
  2257. if ( pCurWaypoint )
  2258. {
  2259. Assert( (pCurWaypoint->Flags() & (bits_WP_TO_PATHCORNER | bits_WP_TO_GOAL )) == (bits_WP_TO_PATHCORNER | bits_WP_TO_GOAL ));
  2260. SetGoalEnt( pCurWaypoint->hPathCorner );
  2261. DoFindPathToPathcorner( pCurWaypoint->hPathCorner );
  2262. }
  2263. }
  2264. }
  2265. //-----------------------------------------------------------------------------
  2266. // Purpose:
  2267. //-----------------------------------------------------------------------------
  2268. #ifdef DEBUG
  2269. ConVar ai_disable_path_simplification( "ai_disable_path_simplification","0" );
  2270. #define IsSimplifyPathDisabled() ai_disable_path_simplification.GetBool()
  2271. #else
  2272. #define IsSimplifyPathDisabled() false
  2273. #endif
  2274. const float MIN_ANGLE_COS_SIMPLIFY = 0.766; // 40 deg left or right
  2275. bool CAI_Navigator::ShouldAttemptSimplifyTo( const Vector &pos )
  2276. {
  2277. if ( m_bForcedSimplify )
  2278. return true;
  2279. Vector vecToPos = ( pos - GetLocalOrigin() );
  2280. vecToPos.z = 0;
  2281. VectorNormalize( vecToPos );
  2282. Vector vecCurrentDirectionOfMovement = ( GetCurWaypointPos() - GetLocalOrigin() );
  2283. vecCurrentDirectionOfMovement.z = 0;
  2284. VectorNormalize( vecCurrentDirectionOfMovement );
  2285. float dot = vecCurrentDirectionOfMovement.AsVector2D().Dot( vecToPos.AsVector2D() );
  2286. return ( m_bForcedSimplify || dot > MIN_ANGLE_COS_SIMPLIFY );
  2287. }
  2288. //-------------------------------------
  2289. bool CAI_Navigator::ShouldSimplifyTo( bool passedDetour, const Vector &pos )
  2290. {
  2291. int flags = AIMLF_QUICK_REJECT;
  2292. #ifndef NPCS_BLOCK_SIMPLIFICATION
  2293. if ( !passedDetour )
  2294. flags |= AIMLF_IGNORE_TRANSIENTS;
  2295. #endif
  2296. AIMoveTrace_t moveTrace;
  2297. GetMoveProbe()->MoveLimit( GetNavType(),
  2298. GetLocalOrigin(), pos, GetOuter()->GetAITraceMask(),
  2299. GetPath()->GetTarget(), 100, flags, &moveTrace );
  2300. return !IsMoveBlocked(moveTrace);
  2301. }
  2302. //-------------------------------------
  2303. void CAI_Navigator::SimplifyPathInsertSimplification( AI_Waypoint_t *pSegmentStart, const Vector &point )
  2304. {
  2305. if ( point != pSegmentStart->GetPos() )
  2306. {
  2307. AI_Waypoint_t *pNextWaypoint = pSegmentStart->GetNext();
  2308. Assert( pNextWaypoint );
  2309. Assert( pSegmentStart->NavType() == pNextWaypoint->NavType() );
  2310. AI_Waypoint_t *pNewWaypoint = new AI_Waypoint_t( point, 0, pSegmentStart->NavType(), 0, NO_NODE );
  2311. while ( GetPath()->GetCurWaypoint() != pNextWaypoint )
  2312. {
  2313. Assert( GetPath()->GetCurWaypoint()->IsReducible() );
  2314. GetPath()->Advance();
  2315. }
  2316. pNewWaypoint->SetNext( pNextWaypoint );
  2317. GetPath()->SetWaypoints( pNewWaypoint );
  2318. }
  2319. else
  2320. {
  2321. while ( GetPath()->GetCurWaypoint() != pSegmentStart )
  2322. {
  2323. Assert( GetPath()->GetCurWaypoint()->IsReducible() );
  2324. GetPath()->Advance();
  2325. }
  2326. }
  2327. }
  2328. //-------------------------------------
  2329. bool CAI_Navigator::SimplifyPathForwardScan( const CAI_Navigator::SimplifyForwardScanParams &params,
  2330. AI_Waypoint_t *pCurWaypoint, const Vector &curPoint,
  2331. float distRemaining, bool skipTest, bool passedDetour, int *pTestCount )
  2332. {
  2333. AI_Waypoint_t *pNextWaypoint = pCurWaypoint->GetNext();
  2334. if ( !passedDetour )
  2335. passedDetour = ( ( pCurWaypoint->Flags() & bits_WP_TO_DETOUR) != 0 );
  2336. if ( distRemaining > 0)
  2337. {
  2338. if ( pCurWaypoint->IsReducible() )
  2339. {
  2340. // Walk out to test point, or next waypoint
  2341. AI_Waypoint_t *pRecursionWaypoint;
  2342. Vector nextPoint;
  2343. float distToNext = ComputePathDistance( GetNavType(), curPoint, pNextWaypoint->GetPos() );
  2344. if (distToNext < params.increment * 1.10 )
  2345. {
  2346. nextPoint = pNextWaypoint->GetPos();
  2347. distRemaining -= distToNext;
  2348. pRecursionWaypoint = pNextWaypoint;
  2349. }
  2350. else
  2351. {
  2352. Vector offset = pNextWaypoint->GetPos() - pCurWaypoint->GetPos();
  2353. VectorNormalize( offset );
  2354. offset *= params.increment;
  2355. nextPoint = curPoint + offset;
  2356. distRemaining -= params.increment;
  2357. pRecursionWaypoint = pCurWaypoint;
  2358. }
  2359. bool skipTestNext = ( ComputePathDistance( GetNavType(), GetLocalOrigin(), nextPoint ) > params.radius + 0.1 );
  2360. if ( SimplifyPathForwardScan( params, pRecursionWaypoint, nextPoint, distRemaining, skipTestNext, passedDetour, pTestCount ) )
  2361. return true;
  2362. }
  2363. }
  2364. if ( !skipTest && *pTestCount < params.maxSamples && ShouldAttemptSimplifyTo( curPoint ) )
  2365. {
  2366. (*pTestCount)++;
  2367. if ( ShouldSimplifyTo( passedDetour, curPoint ) )
  2368. {
  2369. SimplifyPathInsertSimplification( pCurWaypoint, curPoint );
  2370. return true;
  2371. }
  2372. }
  2373. return false;
  2374. }
  2375. //-------------------------------------
  2376. bool CAI_Navigator::SimplifyPathForwardScan( const CAI_Navigator::SimplifyForwardScanParams &params )
  2377. {
  2378. AI_Waypoint_t *pCurWaypoint = GetPath()->GetCurWaypoint();
  2379. float distRemaining = params.scanDist - GetPathDistToCurWaypoint();
  2380. int testCount = 0;
  2381. if ( distRemaining < 0.1 )
  2382. return false;
  2383. if ( SimplifyPathForwardScan( params, pCurWaypoint, pCurWaypoint->GetPos(), distRemaining, true, false, &testCount ) )
  2384. return true;
  2385. return false;
  2386. }
  2387. //-------------------------------------
  2388. bool CAI_Navigator::SimplifyPathForward( float maxDist )
  2389. {
  2390. AI_Waypoint_t *pCurWaypoint = GetPath()->GetCurWaypoint();
  2391. AI_Waypoint_t *pNextWaypoint = pCurWaypoint->GetNext();
  2392. if ( !pNextWaypoint )
  2393. return false;
  2394. AI_PROFILE_SCOPE(CAI_Navigator_SimplifyPathForward);
  2395. static SimplifyForwardScanParams fullScanParams =
  2396. {
  2397. 32 * 12, // Distance to move out path
  2398. 12 * 12, // Radius within which a point must be to be valid
  2399. 3 * 12, // Increment to move out on
  2400. 4, // maximum number of point samples
  2401. };
  2402. SimplifyForwardScanParams scanParams = fullScanParams;
  2403. if ( maxDist > fullScanParams.radius )
  2404. {
  2405. float ratio = (maxDist / fullScanParams.radius);
  2406. fullScanParams.radius = maxDist;
  2407. fullScanParams.scanDist *= ratio;
  2408. fullScanParams.increment *= ratio;
  2409. }
  2410. if ( SimplifyPathForwardScan( scanParams ) )
  2411. return true;
  2412. if ( ShouldAttemptSimplifyTo( pNextWaypoint->GetPos() ) &&
  2413. ComputePathDistance( GetNavType(), GetLocalOrigin(), pNextWaypoint->GetPos() ) < scanParams.scanDist &&
  2414. ShouldSimplifyTo( ( ( pCurWaypoint->Flags() & bits_WP_TO_DETOUR ) != 0 ), pNextWaypoint->GetPos() ) ) // @TODO (toml 04-25-03): need to handle this better. this is here because forward scan may look out so far that a close obvious solution is skipped (due to test limiting)
  2415. {
  2416. delete pCurWaypoint;
  2417. GetPath()->SetWaypoints(pNextWaypoint);
  2418. return true;
  2419. }
  2420. return false;
  2421. }
  2422. //-------------------------------------
  2423. bool CAI_Navigator::SimplifyPathBacktrack()
  2424. {
  2425. AI_PROFILE_SCOPE(CAI_Navigator_SimplifyPathBacktrack);
  2426. AI_Waypoint_t *pCurWaypoint = GetPath()->GetCurWaypoint();
  2427. AI_Waypoint_t *pNextWaypoint = pCurWaypoint->GetNext();
  2428. // ------------------------------------------------------------------------
  2429. // If both waypoints are ground waypoints and my path sends me back tracking
  2430. // more than 24 inches, try to aim for (roughly) the nearest point on the line
  2431. // connecting the first two waypoints
  2432. // ------------------------------------------------------------------------
  2433. if (pCurWaypoint->GetNext() &&
  2434. (pNextWaypoint->Flags() & bits_WP_TO_NODE) &&
  2435. (pNextWaypoint->NavType() == NAV_GROUND) &&
  2436. (pCurWaypoint->NavType() == NAV_GROUND) &&
  2437. (pCurWaypoint->Flags() & bits_WP_TO_NODE) )
  2438. {
  2439. Vector firstToMe = (GetLocalOrigin() - pCurWaypoint->GetPos());
  2440. Vector firstToNext = pNextWaypoint->GetPos() - pCurWaypoint->GetPos();
  2441. VectorNormalize(firstToNext);
  2442. firstToMe.z = 0;
  2443. firstToNext.z = 0;
  2444. float firstDist = firstToMe.Length();
  2445. float firstProj = DotProduct(firstToMe,firstToNext);
  2446. float goalTolerance = GetPath()->GetGoalTolerance();
  2447. if (firstProj>0.5*firstDist)
  2448. {
  2449. Vector targetPos = pCurWaypoint->GetPos() + (firstProj * firstToNext);
  2450. // Only use a local or jump move
  2451. int buildFlags = 0;
  2452. if (CapabilitiesGet() & bits_CAP_MOVE_GROUND)
  2453. buildFlags |= bits_BUILD_GROUND;
  2454. if (CapabilitiesGet() & bits_CAP_MOVE_JUMP)
  2455. buildFlags |= bits_BUILD_JUMP;
  2456. // Make sure I can get to the new point
  2457. AI_Waypoint_t *route1 = GetPathfinder()->BuildLocalRoute(GetLocalOrigin(), targetPos, GetPath()->GetTarget(), bits_WP_TO_DETOUR, NO_NODE, buildFlags, goalTolerance);
  2458. if (!route1)
  2459. return false;
  2460. // Make sure the new point gets me to the target location
  2461. AI_Waypoint_t *route2 = GetPathfinder()->BuildLocalRoute(targetPos, pNextWaypoint->GetPos(), GetPath()->GetTarget(), bits_WP_TO_DETOUR, NO_NODE, buildFlags, goalTolerance);
  2462. if (!route2)
  2463. {
  2464. DeleteAll(route1);
  2465. return false;
  2466. }
  2467. // Add the two route together
  2468. AddWaypointLists(route1,route2);
  2469. // Now add the rest of the old route to the new one
  2470. AddWaypointLists(route1,pNextWaypoint->GetNext());
  2471. // Now advance the route linked list, putting the finished waypoints back in the waypoint pool
  2472. AI_Waypoint_t *freeMe = pCurWaypoint->GetNext();
  2473. delete pCurWaypoint;
  2474. delete freeMe;
  2475. GetPath()->SetWaypoints(route1);
  2476. return true;
  2477. }
  2478. }
  2479. return false;
  2480. }
  2481. //-------------------------------------
  2482. bool CAI_Navigator::SimplifyPathQuick()
  2483. {
  2484. AI_PROFILE_SCOPE(CAI_Navigator_SimplifyPathQuick);
  2485. static SimplifyForwardScanParams quickScanParams[2] =
  2486. {
  2487. {
  2488. (12.0 * 12.0) - 0.1, // Distance to move out path
  2489. 12 * 12, // Radius within which a point must be to be valid
  2490. 0.5 * 12, // Increment to move out on
  2491. 1, // maximum number of point samples
  2492. },
  2493. // Strong optimization version
  2494. {
  2495. (6.0 * 12.0) - 0.1, // Distance to move out path
  2496. 8 * 12, // Radius within which a point must be to be valid
  2497. 1.0 * 12, // Increment to move out on
  2498. 1, // maximum number of point samples
  2499. }
  2500. };
  2501. if ( SimplifyPathForwardScan( quickScanParams[AIStrongOpt()] ) )
  2502. return true;
  2503. return false;
  2504. }
  2505. //-------------------------------------
  2506. // Second entry is the strong opt version
  2507. const float ROUTE_SIMPLIFY_TIME_DELAY[2] = { 0.5, 1.0f };
  2508. const float NO_PVS_ROUTE_SIMPLIFY_TIME_DELAY[2] = { 1.0, 2.0f };
  2509. const float QUICK_SIMPLIFY_TIME_DELAY[2] = { FLT_MIN, 0.3f };
  2510. int g_iFrameLastSimplified;
  2511. bool CAI_Navigator::SimplifyPath( bool bFirstForPath, float scanDist )
  2512. {
  2513. AI_PROFILE_SCOPE(CAI_Navigator_SimplifyPath);
  2514. if ( TestingSteering() || IsSimplifyPathDisabled() )
  2515. return false;
  2516. bool bInPVS = GetOuter()->HasCondition( COND_IN_PVS );
  2517. bool bRetVal = false;
  2518. Navigation_t navType = GetOuter()->GetNavType();
  2519. if (navType == NAV_GROUND || navType == NAV_FLY)
  2520. {
  2521. AI_Waypoint_t *pCurWaypoint = GetPath()->GetCurWaypoint();
  2522. if ( !pCurWaypoint || !pCurWaypoint->IsReducible() )
  2523. return false;
  2524. //-----------------------------
  2525. bool bFullSimplify;
  2526. bFullSimplify = ( m_flNextSimplifyTime <= gpGlobals->curtime );
  2527. if ( bFirstForPath && !bFullSimplify )
  2528. {
  2529. bFullSimplify = bInPVS;
  2530. }
  2531. if ( AIStrongOpt() && bFullSimplify )
  2532. {
  2533. if ( g_iFrameLastSimplified != gpGlobals->framecount )
  2534. {
  2535. g_iFrameLastSimplified = gpGlobals->framecount;
  2536. }
  2537. else
  2538. {
  2539. bFullSimplify = false;
  2540. }
  2541. }
  2542. m_bForcedSimplify = bFirstForPath;
  2543. //-----------------------------
  2544. if ( bFullSimplify )
  2545. {
  2546. float simplifyDelay = ( bInPVS ) ? ROUTE_SIMPLIFY_TIME_DELAY[AIStrongOpt()] : NO_PVS_ROUTE_SIMPLIFY_TIME_DELAY[AIStrongOpt()];
  2547. if ( GetOuter()->GetMoveEfficiency() > AIME_NORMAL )
  2548. simplifyDelay *= 2;
  2549. m_flNextSimplifyTime = gpGlobals->curtime + simplifyDelay;
  2550. if ( SimplifyPathForward( scanDist ) )
  2551. bRetVal = true;
  2552. else if ( SimplifyPathBacktrack() )
  2553. bRetVal = true;
  2554. else if ( SimplifyPathQuick() )
  2555. bRetVal = true;
  2556. }
  2557. else if ( bFirstForPath || ( bInPVS && GetOuter()->GetMoveEfficiency() == AIME_NORMAL ) )
  2558. {
  2559. if ( !AIStrongOpt() || gpGlobals->curtime - m_flLastSuccessfulSimplifyTime > QUICK_SIMPLIFY_TIME_DELAY[AIStrongOpt()] )
  2560. {
  2561. if ( SimplifyPathQuick() )
  2562. bRetVal = true;
  2563. }
  2564. }
  2565. }
  2566. if ( bRetVal )
  2567. {
  2568. m_flLastSuccessfulSimplifyTime = gpGlobals->curtime;
  2569. DbgNavMsg( GetOuter(), "Simplified path\n" );
  2570. }
  2571. return bRetVal;
  2572. }
  2573. //-----------------------------------------------------------------------------
  2574. AI_NavPathProgress_t CAI_Navigator::ProgressFlyPath( const AI_ProgressFlyPathParams_t &params )
  2575. {
  2576. if ( IsGoalActive() )
  2577. {
  2578. float waypointDist = ( GetCurWaypointPos() - GetLocalOrigin() ).Length();
  2579. if ( CurWaypointIsGoal() )
  2580. {
  2581. float tolerance = MAX( params.goalTolerance, GetPath()->GetGoalTolerance() );
  2582. if ( waypointDist <= tolerance )
  2583. return AINPP_COMPLETE;
  2584. }
  2585. else
  2586. {
  2587. bool bIsStrictWaypoint = ( (GetPath()->CurWaypointFlags() & (bits_WP_TO_PATHCORNER|bits_WP_DONT_SIMPLIFY) ) != 0 );
  2588. float tolerance = (bIsStrictWaypoint) ? params.strictPointTolerance : params.waypointTolerance;
  2589. if ( waypointDist <= tolerance )
  2590. {
  2591. trace_t tr;
  2592. AI_TraceLine( GetAbsOrigin(), GetPath()->GetCurWaypoint()->GetNext()->GetPos(), GetOuter()->GetAITraceMask(), GetOuter(), COLLISION_GROUP_NONE, &tr );
  2593. if ( tr.fraction == 1.0f )
  2594. {
  2595. AdvancePath();
  2596. return AINPP_ADVANCED;
  2597. }
  2598. }
  2599. if ( SimplifyFlyPath( params ) )
  2600. return AINPP_ADVANCED;
  2601. }
  2602. }
  2603. return AINPP_NO_CHANGE;
  2604. }
  2605. //-----------------------------------------------------------------------------
  2606. void CAI_Navigator::SimplifyFlyPath( unsigned collisionMask, const CBaseEntity *pTarget,
  2607. float strictPointTolerance, float blockTolerance,
  2608. AI_NpcBlockHandling_t blockHandling)
  2609. {
  2610. AI_ProgressFlyPathParams_t params( collisionMask, strictPointTolerance, blockTolerance,
  2611. 0, 0, blockHandling );
  2612. params.SetCurrent( pTarget );
  2613. SimplifyFlyPath( params );
  2614. }
  2615. //-----------------------------------------------------------------------------
  2616. #define FLY_ROUTE_SIMPLIFY_TIME_DELAY 0.3
  2617. #define FLY_ROUTE_SIMPLIFY_LOOK_DIST (12.0*12.0)
  2618. bool CAI_Navigator::SimplifyFlyPath( const AI_ProgressFlyPathParams_t &params )
  2619. {
  2620. if ( !GetPath()->GetCurWaypoint() )
  2621. return false;
  2622. if ( m_flNextSimplifyTime > gpGlobals->curtime)
  2623. return false;
  2624. m_flNextSimplifyTime = gpGlobals->curtime + FLY_ROUTE_SIMPLIFY_TIME_DELAY;
  2625. if ( params.bTrySimplify && SimplifyPathForward( FLY_ROUTE_SIMPLIFY_LOOK_DIST ) )
  2626. return true;
  2627. // don't shorten path_corners
  2628. bool bIsStrictWaypoint = ( !params.bTrySimplify || ( (GetPath()->CurWaypointFlags() & (bits_WP_TO_PATHCORNER|bits_WP_DONT_SIMPLIFY) ) != 0 ) );
  2629. Vector dir = GetCurWaypointPos() - GetLocalOrigin();
  2630. float length = VectorNormalize( dir );
  2631. if ( !bIsStrictWaypoint || length < params.strictPointTolerance )
  2632. {
  2633. // FIXME: This seems strange... Why should this condition ever be met?
  2634. // Don't advance your waypoint if you don't have one!
  2635. if (GetPath()->CurWaypointIsGoal())
  2636. return false;
  2637. AIMoveTrace_t moveTrace;
  2638. GetMoveProbe()->MoveLimit( NAV_FLY, GetLocalOrigin(), GetPath()->NextWaypointPos(),
  2639. params.collisionMask, params.pTarget, &moveTrace);
  2640. if ( moveTrace.flDistObstructed - params.blockTolerance < 0.01 ||
  2641. ( ( params.blockHandling == AISF_IGNORE) && ( moveTrace.fStatus == AIMR_BLOCKED_NPC ) ) )
  2642. {
  2643. AdvancePath();
  2644. return true;
  2645. }
  2646. else if ( moveTrace.pObstruction && params.blockHandling == AISF_AVOID )
  2647. {
  2648. PrependLocalAvoidance( params.blockTolerance - moveTrace.flDistObstructed, moveTrace );
  2649. }
  2650. }
  2651. return false;
  2652. }
  2653. //-----------------------------------------------------------------------------
  2654. // Purpose:
  2655. // Input :
  2656. // Output :
  2657. //-----------------------------------------------------------------------------
  2658. float CAI_Navigator::MovementCost( int moveType, Vector &vecStart, Vector &vecEnd )
  2659. {
  2660. float flCost = vecStart.DistTo( vecEnd );
  2661. if ( moveType == bits_CAP_MOVE_JUMP || moveType == bits_CAP_MOVE_CLIMB )
  2662. {
  2663. flCost *= 2.0;
  2664. }
  2665. // Allow the NPC to override the movement cost
  2666. GetOuter()->MovementCost( moveType, vecStart, vecEnd, &flCost );
  2667. return flCost;
  2668. }
  2669. //-----------------------------------------------------------------------------
  2670. // Purpose: Will the entities hull fit at the given node
  2671. // Input :
  2672. // Output : Returns true if hull fits at node
  2673. //-----------------------------------------------------------------------------
  2674. bool CAI_Navigator::CanFitAtNode(int nodeNum, unsigned int collisionMask )
  2675. {
  2676. // Make sure I have a network
  2677. if (!GetNetwork())
  2678. {
  2679. DevMsg("CanFitAtNode() called with no network!\n");
  2680. return false;
  2681. }
  2682. CAI_Node* pTestNode = GetNetwork()->GetNode(nodeNum);
  2683. Vector startPos = pTestNode->GetPosition(GetHullType());
  2684. // -------------------------------------------------------------------
  2685. // Check ground nodes for standable bottom
  2686. // -------------------------------------------------------------------
  2687. if (pTestNode->GetType() == NODE_GROUND)
  2688. {
  2689. if (!GetMoveProbe()->CheckStandPosition( startPos, collisionMask ))
  2690. {
  2691. return false;
  2692. }
  2693. }
  2694. // -------------------------------------------------------------------
  2695. // Check climb exit nodes for standable bottom
  2696. // -------------------------------------------------------------------
  2697. if ((pTestNode->GetType() == NODE_CLIMB) &&
  2698. (pTestNode->m_eNodeInfo & (bits_NODE_CLIMB_BOTTOM | bits_NODE_CLIMB_EXIT )))
  2699. {
  2700. if (!GetMoveProbe()->CheckStandPosition( startPos, collisionMask ))
  2701. {
  2702. return false;
  2703. }
  2704. }
  2705. // -------------------------------------------------------------------
  2706. // Check that hull fits at position of node
  2707. // -------------------------------------------------------------------
  2708. if (!CanFitAtPosition( startPos, collisionMask ))
  2709. {
  2710. startPos.z += GetOuter()->StepHeight();
  2711. if (!CanFitAtPosition( startPos, collisionMask ))
  2712. return false;
  2713. }
  2714. return true;
  2715. }
  2716. //-----------------------------------------------------------------------------
  2717. // Purpose: Returns true if NPC's hull fits in the given spot with the
  2718. // given collision mask
  2719. // Input :
  2720. // Output :
  2721. //-----------------------------------------------------------------------------
  2722. bool CAI_Navigator::CanFitAtPosition( const Vector &vStartPos, unsigned int collisionMask, bool bIgnoreTransients, bool bAllowPlayerAvoid )
  2723. {
  2724. CTraceFilterNav traceFilter( const_cast<CAI_BaseNPC *>(GetOuter()), bIgnoreTransients, GetOuter(), COLLISION_GROUP_NONE, bAllowPlayerAvoid );
  2725. Vector vEndPos = vStartPos;
  2726. vEndPos.z += 0.01;
  2727. trace_t tr;
  2728. AI_TraceHull( vStartPos, vEndPos,
  2729. GetHullMins(), GetHullMaxs(),
  2730. collisionMask,
  2731. &traceFilter,
  2732. &tr );
  2733. if (tr.startsolid)
  2734. {
  2735. return false;
  2736. }
  2737. return true;
  2738. }
  2739. //-----------------------------------------------------------------------------
  2740. float CAI_Navigator::GetPathDistToCurWaypoint() const
  2741. {
  2742. return ( GetPath()->GetCurWaypoint() ) ?
  2743. ComputePathDistance( GetNavType(), GetLocalOrigin(), GetPath()->CurWaypointPos() ) :
  2744. 0;
  2745. }
  2746. //-----------------------------------------------------------------------------
  2747. // Computes the distance to our goal, rebuilding waypoint distances if necessary.
  2748. // Returns -1 if we still don't have a valid path length after rebuilding.
  2749. //
  2750. // NOTE: this should really be part of GetPathDistToGoal below, but I didn't
  2751. // want to affect OnFailedSteer this close to shipping! (dvs: 8/16/07)
  2752. //-----------------------------------------------------------------------------
  2753. float CAI_Navigator::BuildAndGetPathDistToGoal()
  2754. {
  2755. if ( !GetPath() )
  2756. return -1;
  2757. // Make sure it's fresh.
  2758. GetPath()->GetPathLength();
  2759. if ( GetPath()->GetCurWaypoint() && ( GetPath()->GetCurWaypoint()->flPathDistGoal >= 0 ) )
  2760. return GetPathDistToGoal();
  2761. return -1;
  2762. }
  2763. // FIXME: this ignores the fact that flPathDistGoal might be -1, yielding nonsensical results.
  2764. // See BuildAndGetPathDistToGoal above.
  2765. float CAI_Navigator::GetPathDistToGoal() const
  2766. {
  2767. return ( GetPath()->GetCurWaypoint() ) ?
  2768. ( GetPathDistToCurWaypoint() + GetPath()->GetCurWaypoint()->flPathDistGoal ) :
  2769. 0;
  2770. }
  2771. //-----------------------------------------------------------------------------
  2772. // Purpose: Attempts to build a route
  2773. // Input :
  2774. // Output : True if successful / false if fail
  2775. //-----------------------------------------------------------------------------
  2776. bool CAI_Navigator::FindPath( bool fSignalTaskStatus, bool bDontIgnoreBadLinks )
  2777. {
  2778. // Test to see if we're resolving spiking problems via threading
  2779. if ( ai_navigator_generate_spikes.GetBool() )
  2780. {
  2781. unsigned int nLargeCount = (INT_MAX>>(ai_navigator_generate_spikes_strength.GetInt()));
  2782. while ( nLargeCount-- ) {}
  2783. }
  2784. bool bRetrying = (HasMemory(bits_MEMORY_PATH_FAILED) && m_timePathRebuildMax != 0 );
  2785. if ( bRetrying )
  2786. {
  2787. // If I've passed by fail time, fail this task
  2788. if (m_timePathRebuildFail < gpGlobals->curtime)
  2789. {
  2790. if ( fSignalTaskStatus )
  2791. OnNavFailed( FAIL_NO_ROUTE );
  2792. else
  2793. OnNavFailed();
  2794. return false;
  2795. }
  2796. else if ( m_timePathRebuildNext > gpGlobals->curtime )
  2797. {
  2798. return false;
  2799. }
  2800. }
  2801. bool bFindResult = DoFindPath();
  2802. if ( !bDontIgnoreBadLinks && !bFindResult && GetOuter()->IsNavigationUrgent() )
  2803. {
  2804. GetPathfinder()->SetIgnoreBadLinks();
  2805. bFindResult = DoFindPath();
  2806. }
  2807. if (bFindResult)
  2808. {
  2809. Forget(bits_MEMORY_PATH_FAILED);
  2810. if (fSignalTaskStatus)
  2811. {
  2812. TaskComplete();
  2813. }
  2814. return true;
  2815. }
  2816. if (m_timePathRebuildMax == 0)
  2817. {
  2818. if ( fSignalTaskStatus )
  2819. OnNavFailed( FAIL_NO_ROUTE );
  2820. else
  2821. OnNavFailed();
  2822. return false;
  2823. }
  2824. else
  2825. {
  2826. if ( !bRetrying )
  2827. {
  2828. Remember(bits_MEMORY_PATH_FAILED);
  2829. m_timePathRebuildFail = gpGlobals->curtime + m_timePathRebuildMax;
  2830. }
  2831. m_timePathRebuildNext = gpGlobals->curtime + m_timePathRebuildDelay;
  2832. return false;
  2833. }
  2834. return true;
  2835. }
  2836. //-----------------------------------------------------------------------------
  2837. // Purpose: Called when route fails. Marks last link on which that failure
  2838. // occured as stale so when then next node route is build that link
  2839. // will be explictly checked for blockages
  2840. // Input :
  2841. // Output :
  2842. //-----------------------------------------------------------------------------
  2843. bool CAI_Navigator::MarkCurWaypointFailedLink( void )
  2844. {
  2845. if ( TestingSteering() )
  2846. return false;
  2847. if ( !m_fRememberStaleNodes )
  2848. return false;
  2849. // Prevent a crash in release
  2850. if( !GetPath() || !GetPath()->GetCurWaypoint() )
  2851. return false;
  2852. bool didMark = false;
  2853. int startID = GetPath()->GetLastNodeReached();
  2854. int endID = GetPath()->GetCurWaypoint()->iNodeID;
  2855. if ( endID != NO_NODE )
  2856. {
  2857. bool bBlockAll = false;
  2858. if ( m_hLastBlockingEnt != NULL &&
  2859. !m_hLastBlockingEnt->IsPlayer() && !m_hLastBlockingEnt->IsNPC() &&
  2860. m_hLastBlockingEnt->GetMoveType() == MOVETYPE_VPHYSICS &&
  2861. m_hLastBlockingEnt->VPhysicsGetObject() &&
  2862. ( !m_hLastBlockingEnt->VPhysicsGetObject()->IsMoveable() ||
  2863. m_hLastBlockingEnt->VPhysicsGetObject()->GetMass() > 200 ) )
  2864. {
  2865. // Make sure it's a "large" object
  2866. // - One dimension is >40
  2867. // - Other 2 dimensions are >30
  2868. CCollisionProperty *pCollisionProp = m_hLastBlockingEnt->CollisionProp();
  2869. bool bFoundLarge = false;
  2870. bool bFoundSmall = false;
  2871. Vector vecSize = pCollisionProp->OBBMaxs() - pCollisionProp->OBBMins();
  2872. for ( int i = 0; i < 3; i++ )
  2873. {
  2874. if ( vecSize[i] > 40 )
  2875. {
  2876. bFoundLarge = true;
  2877. }
  2878. if ( vecSize[i] < 30 )
  2879. {
  2880. bFoundSmall = true;
  2881. break;
  2882. }
  2883. }
  2884. if ( bFoundLarge && !bFoundSmall )
  2885. {
  2886. Vector vStartPos = GetNetwork()->GetNode( endID )->GetPosition( GetHullType() );
  2887. Vector vEndPos = vStartPos;
  2888. vEndPos.z += 0.01;
  2889. trace_t tr;
  2890. UTIL_TraceModel( vStartPos, vEndPos, GetHullMins(), GetHullMaxs(), m_hLastBlockingEnt, COLLISION_GROUP_NONE, &tr );
  2891. if ( tr.startsolid )
  2892. bBlockAll = true;
  2893. }
  2894. }
  2895. if ( bBlockAll )
  2896. {
  2897. CAI_Node *pDestNode = GetNetwork()->GetNode( endID );
  2898. for ( int i = 0; i < pDestNode->NumLinks(); i++ )
  2899. {
  2900. CAI_Link *pLink = pDestNode->GetLinkByIndex( i );
  2901. pLink->m_LinkInfo |= bits_LINK_STALE_SUGGESTED;
  2902. pLink->m_timeStaleExpires = gpGlobals->curtime + 4.0;
  2903. didMark = true;
  2904. }
  2905. }
  2906. else if ( startID != NO_NODE )
  2907. {
  2908. // Find link and mark it as stale
  2909. CAI_Node *pNode = GetNetwork()->GetNode(startID);
  2910. CAI_Link *pLink = pNode->GetLink( endID );
  2911. if ( pLink )
  2912. {
  2913. pLink->m_LinkInfo |= bits_LINK_STALE_SUGGESTED;
  2914. pLink->m_timeStaleExpires = gpGlobals->curtime + 4.0;
  2915. didMark = true;
  2916. }
  2917. }
  2918. }
  2919. return didMark;
  2920. }
  2921. //-----------------------------------------------------------------------------
  2922. // Purpose: Builds a route to the given vecGoal using either local movement
  2923. // or nodes
  2924. // Input :
  2925. // Output : True is route was found, false otherwise
  2926. //-----------------------------------------------------------------------------
  2927. bool CAI_Navigator::DoFindPathToPos( )
  2928. {
  2929. CAI_Path * pPath = GetPath();
  2930. CAI_Pathfinder *pPathfinder = GetPathfinder();
  2931. const Vector & actualGoalPos = pPath->ActualGoalPosition();
  2932. CBaseEntity * pTarget = pPath->GetTarget();
  2933. float tolerance = pPath->GetGoalTolerance();
  2934. Vector origin;
  2935. if ( gpGlobals->curtime - m_flTimeClipped > 0.11 || m_bLastNavFailed )
  2936. m_pClippedWaypoints->RemoveAll();
  2937. if ( m_pClippedWaypoints->IsEmpty() )
  2938. origin = GetLocalOrigin();
  2939. else
  2940. {
  2941. AI_Waypoint_t *pLastClipped = m_pClippedWaypoints->GetLast();
  2942. origin = pLastClipped->GetPos();
  2943. }
  2944. m_bLastNavFailed = false;
  2945. pPath->ClearWaypoints();
  2946. int nBuildFlags = 0;
  2947. if ( GetGoalFlags() & AIN_LOCAL_SUCCEEED_ON_WITHIN_TOLERANCE )
  2948. {
  2949. nBuildFlags |= bits_BUILD_GET_CLOSE;
  2950. }
  2951. if ( GetGoalFlags() & AIN_NO_LOCAL_NAVIGATION )
  2952. {
  2953. nBuildFlags |= bits_BUILD_NO_LOCAL_NAV;
  2954. }
  2955. if ( GetGoalFlags() & AIN_UNLIMITED_DISTANCE )
  2956. {
  2957. nBuildFlags |= bits_BUILD_UNLIMITED_DISTANCE;
  2958. }
  2959. AI_Waypoint_t *pFirstWaypoint = pPathfinder->BuildRoute( origin, actualGoalPos, pTarget, tolerance, GetNavType(), nBuildFlags );
  2960. if (!pFirstWaypoint)
  2961. {
  2962. // Sorry no path
  2963. return false;
  2964. }
  2965. pPath->SetWaypoints( pFirstWaypoint );
  2966. if ( ShouldTestFailPath() )
  2967. {
  2968. pPath->ClearWaypoints();
  2969. return false;
  2970. }
  2971. if ( !m_pClippedWaypoints->IsEmpty() )
  2972. {
  2973. AI_Waypoint_t *pFirstClipped = m_pClippedWaypoints->GetFirst();
  2974. m_pClippedWaypoints->Set( NULL );
  2975. pFirstClipped->ModifyFlags( bits_WP_DONT_SIMPLIFY, true );
  2976. pPath->PrependWaypoints( pFirstClipped );
  2977. pFirstWaypoint = pFirstClipped;
  2978. }
  2979. if ( pFirstWaypoint->IsReducible() && pFirstWaypoint->GetNext() && pFirstWaypoint->GetNext()->NavType() == GetNavType() &&
  2980. ShouldOptimizeInitialPathSegment( pFirstWaypoint ) )
  2981. {
  2982. // If we're seemingly beyond the waypoint, and our hull is over the line, move on
  2983. const float EPS = 0.1;
  2984. Vector vClosest;
  2985. CalcClosestPointOnLineSegment( origin,
  2986. pFirstWaypoint->GetPos(), pFirstWaypoint->GetNext()->GetPos(),
  2987. vClosest );
  2988. if ( ( pFirstWaypoint->GetPos() - vClosest ).Length() > EPS &&
  2989. ( origin - vClosest ).Length() < GetHullWidth() * 0.5 )
  2990. {
  2991. pPath->Advance();
  2992. }
  2993. }
  2994. return true;
  2995. }
  2996. //-----------------------------------------------------------------------------
  2997. CBaseEntity *CAI_Navigator::GetNextPathcorner( CBaseEntity *pPathCorner )
  2998. {
  2999. CBaseEntity *pNextPathCorner = NULL;
  3000. Assert( pPathCorner );
  3001. if ( pPathCorner )
  3002. {
  3003. pNextPathCorner = pPathCorner->GetNextTarget();
  3004. CAI_Hint *pHint;
  3005. if ( !pNextPathCorner && ( pHint = dynamic_cast<CAI_Hint *>( pPathCorner ) ) != NULL )
  3006. {
  3007. int targetNode = pHint->GetTargetNode();
  3008. if ( targetNode != NO_NODE )
  3009. {
  3010. CAI_Node *pTestNode = GetNetwork()->GetNode(targetNode);
  3011. pNextPathCorner = pTestNode->GetHint();
  3012. }
  3013. }
  3014. }
  3015. return pNextPathCorner;
  3016. }
  3017. //-----------------------------------------------------------------------------
  3018. bool CAI_Navigator::DoFindPathToPathcorner( CBaseEntity *pPathCorner )
  3019. {
  3020. // UNDONE: This is broken
  3021. // UNDONE: Remove this and change the pathing code to be able to refresh itself and support
  3022. // circular paths, etc.
  3023. bool returnCode = false;
  3024. Assert( GetPath()->GoalType() == GOALTYPE_PATHCORNER );
  3025. // NPC is on a path_corner loop
  3026. if ( pPathCorner != NULL )
  3027. {
  3028. // Find path to first pathcorner
  3029. if ( ( GetGoalFlags() & AIN_NO_PATHCORNER_PATHFINDING ) || m_bNoPathcornerPathfinds )
  3030. {
  3031. // HACKHACK: If the starting path_corner has a speed, copy that to the entity
  3032. if ( pPathCorner->m_flSpeed != 0 )
  3033. SetSpeed( pPathCorner->m_flSpeed );
  3034. GetPath()->ClearWaypoints();
  3035. AI_Waypoint_t *pRoute = new AI_Waypoint_t( pPathCorner->GetLocalOrigin(), 0, GetNavType(), bits_WP_TO_PATHCORNER, NO_NODE );
  3036. pRoute->hPathCorner = pPathCorner;
  3037. AI_Waypoint_t *pLast = pRoute;
  3038. pPathCorner = GetNextPathcorner(pPathCorner);
  3039. if ( pPathCorner )
  3040. {
  3041. pLast = new AI_Waypoint_t( pPathCorner->GetLocalOrigin(), 0, GetNavType(), bits_WP_TO_PATHCORNER, NO_NODE );
  3042. pLast->hPathCorner = pPathCorner;
  3043. pRoute->SetNext(pLast);
  3044. }
  3045. pLast->ModifyFlags( bits_WP_TO_GOAL, true );
  3046. GetPath()->SetWaypoints( pRoute, true );
  3047. returnCode = true;
  3048. }
  3049. else
  3050. {
  3051. Vector initPos = pPathCorner->GetLocalOrigin();
  3052. TranslateNavGoal( pPathCorner, initPos );
  3053. GetPath()->ResetGoalPosition( initPos );
  3054. float tolerance = GetPath()->GetGoalTolerance();
  3055. float outerTolerance = GetOuter()->GetDefaultNavGoalTolerance();
  3056. if ( outerTolerance > tolerance )
  3057. {
  3058. GetPath()->SetGoalTolerance( outerTolerance );
  3059. tolerance = outerTolerance;
  3060. }
  3061. if ( ( returnCode = DoFindPathToPos() ) != false )
  3062. {
  3063. // HACKHACK: If the starting path_corner has a speed, copy that to the entity
  3064. if ( pPathCorner->m_flSpeed != 0 )
  3065. {
  3066. SetSpeed( pPathCorner->m_flSpeed );
  3067. }
  3068. AI_Waypoint_t *lastWaypoint = GetPath()->GetGoalWaypoint();
  3069. Assert(lastWaypoint);
  3070. lastWaypoint->ModifyFlags( bits_WP_TO_PATHCORNER, true );
  3071. lastWaypoint->hPathCorner = pPathCorner;
  3072. pPathCorner = GetNextPathcorner(pPathCorner); // first already accounted for in pathfind
  3073. if ( pPathCorner )
  3074. {
  3075. // Place a dummy node in that will be used to signal the next pathfind, also prevents
  3076. // animation system from decellerating when approaching a pathcorner
  3077. lastWaypoint->ModifyFlags( bits_WP_TO_GOAL, false );
  3078. // BRJ 10/4/02
  3079. // FIXME: I'm not certain about the navtype here
  3080. AI_Waypoint_t *curWaypoint = new AI_Waypoint_t( pPathCorner->GetLocalOrigin(), 0, GetNavType(), (bits_WP_TO_PATHCORNER | bits_WP_TO_GOAL), NO_NODE );
  3081. Vector waypointPos = curWaypoint->GetPos();
  3082. TranslateNavGoal( pPathCorner, waypointPos );
  3083. curWaypoint->SetPos( waypointPos );
  3084. GetPath()->SetGoalTolerance( tolerance );
  3085. curWaypoint->hPathCorner = pPathCorner;
  3086. lastWaypoint->SetNext(curWaypoint);
  3087. GetPath()->ResetGoalPosition( curWaypoint->GetPos() );
  3088. }
  3089. }
  3090. }
  3091. }
  3092. return returnCode;
  3093. }
  3094. //-----------------------------------------------------------------------------
  3095. // Purpose: Attemps to find a route
  3096. // Input :
  3097. // Output :
  3098. //-----------------------------------------------------------------------------
  3099. bool CAI_Navigator::DoFindPath( void )
  3100. {
  3101. AI_PROFILE_SCOPE(CAI_Navigator_DoFindPath);
  3102. DbgNavMsg( GetOuter(), "Finding new path\n" );
  3103. GetPath()->ClearWaypoints();
  3104. bool returnCode;
  3105. returnCode = false;
  3106. switch( GetPath()->GoalType() )
  3107. {
  3108. case GOALTYPE_PATHCORNER:
  3109. {
  3110. returnCode = DoFindPathToPathcorner( GetGoalEnt() );
  3111. }
  3112. break;
  3113. case GOALTYPE_ENEMY:
  3114. {
  3115. // NOTE: This is going to set the goal position, which was *not*
  3116. // set in SetGoal for this movement type
  3117. CBaseEntity *pEnemy = GetPath()->GetTarget();
  3118. if (pEnemy)
  3119. {
  3120. Assert( pEnemy == GetEnemy() );
  3121. Vector newPos = GetEnemyLKP();
  3122. float tolerance = GetPath()->GetGoalTolerance();
  3123. float outerTolerance = GetOuter()->GetDefaultNavGoalTolerance();
  3124. if ( outerTolerance > tolerance )
  3125. {
  3126. GetPath()->SetGoalTolerance( outerTolerance );
  3127. tolerance = outerTolerance;
  3128. }
  3129. TranslateNavGoal( pEnemy, newPos );
  3130. // NOTE: Calling reset here because this can get called
  3131. // any time we have to update our goal position
  3132. GetPath()->ResetGoalPosition( newPos );
  3133. GetPath()->SetGoalTolerance( tolerance );
  3134. returnCode = DoFindPathToPos();
  3135. }
  3136. }
  3137. break;
  3138. case GOALTYPE_LOCATION:
  3139. case GOALTYPE_FLANK:
  3140. case GOALTYPE_COVER:
  3141. returnCode = DoFindPathToPos();
  3142. break;
  3143. case GOALTYPE_LOCATION_NEAREST_NODE:
  3144. {
  3145. int myNodeID;
  3146. int destNodeID;
  3147. returnCode = false;
  3148. myNodeID = GetPathfinder()->NearestNodeToNPC();
  3149. if (myNodeID != NO_NODE)
  3150. {
  3151. destNodeID = GetNetwork()->NearestNodeToPoint( GetPath()->ActualGoalPosition(), false );
  3152. if (destNodeID != NO_NODE)
  3153. {
  3154. AI_Waypoint_t *pRoute = GetPathfinder()->FindBestPath( myNodeID, destNodeID );
  3155. if ( pRoute != NULL )
  3156. {
  3157. GetPath()->SetWaypoints( pRoute );
  3158. GetPath()->SetLastNodeAsGoal(true);
  3159. returnCode = true;
  3160. }
  3161. }
  3162. }
  3163. }
  3164. break;
  3165. case GOALTYPE_TARGETENT:
  3166. {
  3167. // NOTE: This is going to set the goal position, which was *not*
  3168. // set in SetGoal for this movement type
  3169. CBaseEntity *pTarget = GetPath()->GetTarget();
  3170. if ( pTarget )
  3171. {
  3172. Assert( pTarget == GetTarget() );
  3173. // NOTE: Calling reset here because this can get called
  3174. // any time we have to update our goal position
  3175. Vector initPos = pTarget->GetAbsOrigin();
  3176. TranslateNavGoal( pTarget, initPos );
  3177. GetPath()->ResetGoalPosition( initPos );
  3178. returnCode = DoFindPathToPos();
  3179. }
  3180. }
  3181. break;
  3182. }
  3183. return returnCode;
  3184. }
  3185. //-----------------------------------------------------------------------------
  3186. void CAI_Navigator::ClearPath( void )
  3187. {
  3188. OnClearPath();
  3189. m_timePathRebuildMax = 0; // How long to try rebuilding path before failing task
  3190. m_timePathRebuildFail = 0; // Current global time when should fail building path
  3191. m_timePathRebuildNext = 0; // Global time to try rebuilding again
  3192. m_timePathRebuildDelay = 0; // How long to wait before trying to rebuild again
  3193. Forget( bits_MEMORY_PATH_FAILED );
  3194. AI_Waypoint_t *pWaypoint = GetPath()->GetCurWaypoint();
  3195. if ( pWaypoint )
  3196. {
  3197. SaveStoppingPath();
  3198. m_PreviousMoveActivity = GetMovementActivity();
  3199. m_PreviousArrivalActivity = GetArrivalActivity();
  3200. if( m_pClippedWaypoints && m_pClippedWaypoints->GetFirst() )
  3201. {
  3202. Assert( m_PreviousMoveActivity > ACT_RESET );
  3203. }
  3204. while ( pWaypoint )
  3205. {
  3206. if ( pWaypoint->iNodeID != NO_NODE )
  3207. {
  3208. CAI_Node *pNode = GetNetwork()->GetNode(pWaypoint->iNodeID);
  3209. if ( pNode )
  3210. {
  3211. if ( pNode->IsLocked() )
  3212. pNode->Unlock();
  3213. }
  3214. }
  3215. pWaypoint = pWaypoint->GetNext();
  3216. }
  3217. }
  3218. GetPath()->Clear();
  3219. }
  3220. //-----------------------------------------------------------------------------
  3221. bool CAI_Navigator::GetStoppingPath( CAI_WaypointList * pClippedWaypoints )
  3222. {
  3223. pClippedWaypoints->RemoveAll();
  3224. AI_Waypoint_t *pCurWaypoint = GetPath()->GetCurWaypoint();
  3225. if ( pCurWaypoint )
  3226. {
  3227. bool bMustCompleteCurrent = ( pCurWaypoint->NavType() == NAV_CLIMB || pCurWaypoint->NavType() == NAV_JUMP );
  3228. float distRemaining = GetMotor()->MinStoppingDist( 0 );
  3229. if ( bMustCompleteCurrent )
  3230. {
  3231. float distToCurrent = ComputePathDistance( pCurWaypoint->NavType(), GetLocalOrigin(), pCurWaypoint->GetPos() );
  3232. if ( pCurWaypoint->NavType() == NAV_CLIMB )
  3233. {
  3234. if ( pCurWaypoint->GetNext() && pCurWaypoint->GetNext()->NavType() == NAV_CLIMB )
  3235. distToCurrent += ComputePathDistance( NAV_CLIMB, pCurWaypoint->GetPos(), pCurWaypoint->GetNext()->GetPos() );
  3236. distToCurrent += GetHullWidth() * 2.0;
  3237. }
  3238. if ( distToCurrent > distRemaining )
  3239. distRemaining = distToCurrent;
  3240. }
  3241. if ( bMustCompleteCurrent || distRemaining > 0.1 )
  3242. {
  3243. Vector vPosPrev = GetLocalOrigin();
  3244. AI_Waypoint_t *pNextPoint = pCurWaypoint;
  3245. AI_Waypoint_t *pSavedWaypoints = NULL;
  3246. AI_Waypoint_t *pLastSavedWaypoint = NULL;
  3247. AI_Waypoint_t *pNewWaypoint;
  3248. while ( distRemaining > 0.01 && pNextPoint )
  3249. {
  3250. if ( ( pNextPoint->NavType() == NAV_CLIMB || pNextPoint->NavType() == NAV_JUMP ) &&
  3251. !bMustCompleteCurrent )
  3252. {
  3253. break;
  3254. }
  3255. #if PARANOID_NAV_CHECK_ON_MOMENTUM
  3256. if ( !CanFitAtPosition( pNextPoint->GetPos(), GetAITraceMask() ) )
  3257. {
  3258. break;
  3259. }
  3260. #endif
  3261. if ( pNextPoint->NavType() != NAV_CLIMB || !pNextPoint->GetNext() || pNextPoint->GetNext()->NavType() != NAV_CLIMB )
  3262. bMustCompleteCurrent = false;
  3263. float distToNext = ComputePathDistance( pNextPoint->NavType(), vPosPrev, pNextPoint->GetPos() );
  3264. if ( distToNext <= distRemaining + 0.01 )
  3265. {
  3266. pNewWaypoint = new AI_Waypoint_t(*pNextPoint);
  3267. if ( pNewWaypoint->Flags() & bits_WP_TO_PATHCORNER )
  3268. {
  3269. pNewWaypoint->ModifyFlags( bits_WP_TO_PATHCORNER, false );
  3270. pNewWaypoint->hPathCorner = NULL;
  3271. }
  3272. pNewWaypoint->ModifyFlags( bits_WP_TO_GOAL | bits_WP_TO_NODE, false );
  3273. pNewWaypoint->iNodeID = NO_NODE;
  3274. if ( pLastSavedWaypoint )
  3275. pLastSavedWaypoint->SetNext( pNewWaypoint );
  3276. else
  3277. pSavedWaypoints = pNewWaypoint;
  3278. pLastSavedWaypoint = pNewWaypoint;
  3279. vPosPrev = pNextPoint->GetPos();
  3280. // NDebugOverlay::Cross3D( vPosPrev, 16, 255, 255, 0, false, 10.0f );
  3281. pNextPoint = pNextPoint->GetNext();
  3282. distRemaining -= distToNext;
  3283. }
  3284. else
  3285. {
  3286. Assert( !( pNextPoint->NavType() == NAV_CLIMB || pNextPoint->NavType() == NAV_JUMP ) );
  3287. Vector remainder = pNextPoint->GetPos() - vPosPrev;
  3288. VectorNormalize( remainder );
  3289. float yaw = UTIL_VecToYaw( remainder );
  3290. remainder *= distRemaining;
  3291. remainder += vPosPrev;
  3292. AIMoveTrace_t trace;
  3293. if ( GetMoveProbe()->MoveLimit( pNextPoint->NavType(), vPosPrev, remainder, GetOuter()->GetAITraceMask(), NULL, 100, AIMLF_DEFAULT | AIMLF_2D, &trace ) )
  3294. {
  3295. pNewWaypoint = new AI_Waypoint_t( trace.vEndPosition, yaw, pNextPoint->NavType(), bits_WP_TO_GOAL, 0);
  3296. if ( pLastSavedWaypoint )
  3297. pLastSavedWaypoint->SetNext( pNewWaypoint );
  3298. else
  3299. pSavedWaypoints = pNewWaypoint;
  3300. }
  3301. distRemaining = 0;
  3302. }
  3303. }
  3304. if ( pSavedWaypoints )
  3305. {
  3306. pClippedWaypoints->Set( pSavedWaypoints );
  3307. return true;
  3308. }
  3309. }
  3310. }
  3311. return false;
  3312. }
  3313. //-----------------------------------------------------------------------------
  3314. void CAI_Navigator::IgnoreStoppingPath( void )
  3315. {
  3316. if( m_pClippedWaypoints && m_pClippedWaypoints->GetFirst() )
  3317. {
  3318. AI_Waypoint_t *pWaypoint = m_pClippedWaypoints->GetFirst();
  3319. if( pWaypoint->NavType() != NAV_JUMP && pWaypoint->NavType() != NAV_CLIMB )
  3320. {
  3321. m_pClippedWaypoints->RemoveAll();
  3322. }
  3323. }
  3324. }
  3325. //-----------------------------------------------------------------------------
  3326. ConVar ai_use_clipped_paths( "ai_use_clipped_paths", "1" );
  3327. void CAI_Navigator::SaveStoppingPath( void )
  3328. {
  3329. m_flTimeClipped = -1;
  3330. m_pClippedWaypoints->RemoveAll();
  3331. AI_Waypoint_t *pCurWaypoint = GetPath()->GetCurWaypoint();
  3332. if ( pCurWaypoint )
  3333. {
  3334. if ( ( pCurWaypoint->NavType() == NAV_CLIMB || pCurWaypoint->NavType() == NAV_JUMP ) || ai_use_clipped_paths.GetBool() )
  3335. {
  3336. if ( GetStoppingPath( m_pClippedWaypoints ) )
  3337. m_flTimeClipped = gpGlobals->curtime;
  3338. }
  3339. }
  3340. }
  3341. //-----------------------------------------------------------------------------
  3342. bool CAI_Navigator::SetGoalFromStoppingPath()
  3343. {
  3344. if ( m_pClippedWaypoints && m_pClippedWaypoints->IsEmpty() )
  3345. SaveStoppingPath();
  3346. if ( m_pClippedWaypoints && !m_pClippedWaypoints->IsEmpty() )
  3347. {
  3348. if ( m_PreviousMoveActivity <= ACT_RESET && GetMovementActivity() <= ACT_RESET )
  3349. {
  3350. m_pClippedWaypoints->RemoveAll();
  3351. DevWarning( 2, "%s has a stopping path and no valid. Movement activity: %s (prev %s)\n", GetOuter()->GetDebugName(), ActivityList_NameForIndex(GetMovementActivity()), ActivityList_NameForIndex(m_PreviousMoveActivity) );
  3352. return false;
  3353. }
  3354. if ( ( m_pClippedWaypoints->GetFirst()->NavType() == NAV_CLIMB || m_pClippedWaypoints->GetFirst()->NavType() == NAV_JUMP ) )
  3355. {
  3356. const Task_t *pCurTask = GetOuter()->GetTask();
  3357. if ( pCurTask && pCurTask->iTask == TASK_STOP_MOVING )
  3358. {
  3359. // Clipped paths are used for 2 reasons: Prepending movement that must be finished in the case of climbing/jumping,
  3360. // and bringing an NPC to a stop. In the second case, we should never be starting climb or jump movement.
  3361. m_pClippedWaypoints->RemoveAll();
  3362. return false;
  3363. }
  3364. }
  3365. if ( !GetPath()->IsEmpty() )
  3366. GetPath()->ClearWaypoints();
  3367. GetPath()->SetWaypoints( m_pClippedWaypoints->GetFirst(), true );
  3368. m_pClippedWaypoints->Set( NULL );
  3369. GetPath()->SetGoalType( GOALTYPE_NONE );
  3370. GetPath()->SetGoalType( GOALTYPE_LOCATION );
  3371. GetPath()->SetGoalTolerance( NAV_STOP_MOVING_TOLERANCE );
  3372. Assert( GetPath()->GetCurWaypoint() );
  3373. Assert( m_PreviousMoveActivity != ACT_INVALID );
  3374. if ( m_PreviousMoveActivity != ACT_RESET )
  3375. GetPath()->SetMovementActivity( m_PreviousMoveActivity );
  3376. if ( m_PreviousArrivalActivity != ACT_RESET )
  3377. GetPath()->SetArrivalActivity( m_PreviousArrivalActivity );
  3378. return true;
  3379. }
  3380. return false;
  3381. }
  3382. //-----------------------------------------------------------------------------
  3383. static Vector GetRouteColor(Navigation_t navType, int waypointFlags)
  3384. {
  3385. if (navType == NAV_JUMP)
  3386. {
  3387. return Vector(255,0,0);
  3388. }
  3389. if (waypointFlags & bits_WP_TO_GOAL)
  3390. {
  3391. return Vector(200,0,255);
  3392. }
  3393. if (waypointFlags & bits_WP_TO_DETOUR)
  3394. {
  3395. return Vector(0,200,255);
  3396. }
  3397. if (waypointFlags & bits_WP_TO_NODE)
  3398. {
  3399. return Vector(0,0,255);
  3400. }
  3401. else //if (waypointBits & bits_WP_TO_PATHCORNER)
  3402. {
  3403. return Vector(0,255,150);
  3404. }
  3405. }
  3406. //-----------------------------------------------------------------------------
  3407. // Returns a color for debugging purposes
  3408. //-----------------------------------------------------------------------------
  3409. static Vector GetWaypointColor(Navigation_t navType)
  3410. {
  3411. switch( navType )
  3412. {
  3413. case NAV_JUMP:
  3414. return Vector(255,90,90);
  3415. case NAV_GROUND:
  3416. return Vector(0,255,255);
  3417. case NAV_CLIMB:
  3418. return Vector(90,255,255);
  3419. case NAV_FLY:
  3420. return Vector(90,90,255);
  3421. case NAV_CRAWL:
  3422. return Vector(255,255,0);
  3423. default:
  3424. return Vector(255,0,0);
  3425. }
  3426. }
  3427. //-----------------------------------------------------------------------------
  3428. void CAI_Navigator::DrawDebugRouteOverlay(void)
  3429. {
  3430. AI_Waypoint_t *waypoint = GetPath()->GetCurWaypoint();
  3431. if (waypoint)
  3432. {
  3433. Vector RGB = GetRouteColor(waypoint->NavType(), waypoint->Flags());
  3434. NDebugOverlay::Line(GetLocalOrigin(), waypoint->GetPos(), RGB[0],RGB[1],RGB[2], true,0);
  3435. }
  3436. while (waypoint)
  3437. {
  3438. Vector RGB = GetWaypointColor(waypoint->NavType());
  3439. NDebugOverlay::Box(waypoint->GetPos(), Vector(-3,-3,-3),Vector(3,3,3), RGB[0],RGB[1],RGB[2], true,0);
  3440. if ( waypoint->Flags() & bits_WP_PRECISE_MOVEMENT )
  3441. {
  3442. NDebugOverlay::Box(waypoint->GetPos(), Vector(-6,-6,-6),Vector(6,6,6), 255, 0, 0, true,0);
  3443. }
  3444. if ( waypoint->Flags() & bits_WP_DONT_SIMPLIFY )
  3445. {
  3446. NDebugOverlay::Box(waypoint->GetPos(), Vector(-9,-9,-9),Vector(9,9,9), 255, 0, 255, true,0);
  3447. }
  3448. if (waypoint->GetNext())
  3449. {
  3450. Vector RGB = GetRouteColor(waypoint->GetNext()->NavType(), waypoint->GetNext()->Flags());
  3451. NDebugOverlay::Line(waypoint->GetPos(), waypoint->GetNext()->GetPos(),RGB[0],RGB[1],RGB[2], true,0);
  3452. }
  3453. waypoint = waypoint->GetNext();
  3454. }
  3455. if ( GetPath()->GoalType() != GOALTYPE_NONE )
  3456. {
  3457. Vector vecGoalPos = GetPath()->ActualGoalPosition();
  3458. Vector vecGoalDir = GetPath()->GetGoalDirection( GetOuter()->GetAbsOrigin() );
  3459. NDebugOverlay::Line( vecGoalPos, vecGoalPos + vecGoalDir * 32, 0,0,255, true, 2.0 );
  3460. float flYaw = UTIL_VecToYaw( vecGoalDir );
  3461. NDebugOverlay::Text( vecGoalPos, CFmtStr("yaw: %f", flYaw), true, 1 );
  3462. }
  3463. }
  3464. //-----------------------------------------------------------------------------