Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4163 lines
116 KiB

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