Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2270 lines
74 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include "cbase.h"
  8. #include "ndebugoverlay.h"
  9. #include "ai_pathfinder.h"
  10. #include "ai_basenpc.h"
  11. #include "ai_node.h"
  12. #include "ai_network.h"
  13. #include "ai_waypoint.h"
  14. #include "ai_link.h"
  15. #include "ai_routedist.h"
  16. #include "ai_moveprobe.h"
  17. #include "ai_dynamiclink.h"
  18. #include "ai_localnavigator.h"
  19. #include "ai_hint.h"
  20. #include "bitstring.h"
  21. //@todo: bad dependency!
  22. #include "ai_navigator.h"
  23. // memdbgon must be the last include file in a .cpp file!!!
  24. #include "tier0/memdbgon.h"
  25. #if defined( OSX )
  26. #pragma GCC diagnostic ignored "-Wenum-compare" // comparison of two values with different enumeration types (Capability_t and RouteBuildFlags_e)
  27. #endif
  28. #define NUM_NPC_DEBUG_OVERLAYS 50
  29. const float MAX_LOCAL_NAV_DIST_GROUND[2] = { (50*12), (25*12) };
  30. const float MAX_LOCAL_NAV_DIST_FLY[2] = { (750*12), (750*12) };
  31. //-----------------------------------------------------------------------------
  32. // CAI_Pathfinder
  33. //
  34. BEGIN_SIMPLE_DATADESC( CAI_Pathfinder )
  35. // m_TriDebugOverlay
  36. // m_bIgnoreStaleLinks
  37. DEFINE_FIELD( m_flLastStaleLinkCheckTime, FIELD_TIME ),
  38. // m_pNetwork
  39. END_DATADESC()
  40. //-----------------------------------------------------------------------------
  41. // Compute move type bits to nav type
  42. //-----------------------------------------------------------------------------
  43. Navigation_t MoveBitsToNavType( int fBits )
  44. {
  45. switch (fBits)
  46. {
  47. case bits_CAP_MOVE_GROUND:
  48. return NAV_GROUND;
  49. case bits_CAP_MOVE_FLY:
  50. return NAV_FLY;
  51. case bits_CAP_MOVE_CLIMB:
  52. return NAV_CLIMB;
  53. case bits_CAP_MOVE_JUMP:
  54. return NAV_JUMP;
  55. case bits_CAP_MOVE_CRAWL:
  56. return NAV_CRAWL;
  57. default:
  58. // This will only happen if more than one bit is set
  59. return NAV_NONE;
  60. }
  61. }
  62. int NavTypeToMoveBits( Navigation_t nNavType )
  63. {
  64. switch (nNavType)
  65. {
  66. case NAV_GROUND:
  67. return bits_CAP_MOVE_GROUND;
  68. case NAV_FLY:
  69. return bits_CAP_MOVE_FLY;
  70. case NAV_CLIMB:
  71. return bits_CAP_MOVE_CLIMB;
  72. case NAV_JUMP:
  73. return bits_CAP_MOVE_JUMP;
  74. case NAV_CRAWL:
  75. return bits_CAP_MOVE_CRAWL;
  76. default:
  77. return 0;
  78. }
  79. }
  80. //-----------------------------------------------------------------------------
  81. void CAI_Pathfinder::Init( CAI_Network *pNetwork )
  82. {
  83. Assert( pNetwork );
  84. m_pNetwork = pNetwork;
  85. }
  86. //-----------------------------------------------------------------------------
  87. //
  88. //-----------------------------------------------------------------------------
  89. bool CAI_Pathfinder::UseStrongOptimizations()
  90. {
  91. if ( !AIStrongOpt() )
  92. {
  93. return false;
  94. }
  95. #ifdef HL2_DLL
  96. if( GetOuter()->Classify() == CLASS_PLAYER_ALLY_VITAL )
  97. {
  98. return false;
  99. }
  100. #endif//HL2_DLL
  101. return true;
  102. }
  103. //-----------------------------------------------------------------------------
  104. // Computes the link type
  105. //-----------------------------------------------------------------------------
  106. Navigation_t CAI_Pathfinder::ComputeWaypointType( bool *pWantsPreciseMovement, CAI_Node **ppNodes, int parentID, int destID )
  107. {
  108. Navigation_t navType = NAV_NONE;
  109. *pWantsPreciseMovement = false;
  110. CAI_Node *pNode = ppNodes[parentID];
  111. for (int link=0; link < pNode->NumLinks();link++)
  112. {
  113. CAI_Link *pLink = pNode->GetLinkByIndex(link);
  114. if ( pLink->DestNodeID(parentID) != destID )
  115. continue;
  116. // BRJ 10/1/02
  117. // FIXME: pNPC->CapabilitiesGet() is actually the mechanism by which fliers
  118. // filter out the bitfields in the waypoint type (most importantly, bits_MOVE_CAP_GROUND)
  119. // that would cause the waypoint distance to be computed in a 2D, as opposed to 3D fashion
  120. // This is a super-scary weak link if you ask me.
  121. int linkMoveTypeBits = pLink->m_iAcceptedMoveTypes[GetHullType()];
  122. int moveTypeBits = ( linkMoveTypeBits & CapabilitiesGet());
  123. if ( !moveTypeBits && linkMoveTypeBits == bits_CAP_MOVE_JUMP )
  124. {
  125. Assert( pNode->GetHint() && pNode->GetHint()->HintType() == HINT_JUMP_OVERRIDE );
  126. ppNodes[destID]->Lock(0.3);
  127. moveTypeBits = linkMoveTypeBits;
  128. }
  129. *pWantsPreciseMovement = ( pLink->m_LinkInfo & bits_LINK_PRECISE_MOVEMENT ) != 0;
  130. Navigation_t linkType = NAV_NONE;
  131. if ( IsPowerOfTwo( moveTypeBits & bits_CAP_MOVE_GROUP ) )
  132. {
  133. linkType = MoveBitsToNavType( moveTypeBits );
  134. }
  135. else
  136. {
  137. // NOTE: Hack for nodes which say they are jump-capable + crawl-capable
  138. if ( navType != NAV_NONE )
  139. {
  140. // This will only trigger if the links disagree about their nav type
  141. Assert( NavTypeToMoveBits( navType ) & moveTypeBits );
  142. linkType = navType;
  143. }
  144. else
  145. {
  146. // This logic only works assuming crawl nodes are the only
  147. // type of node that can overlap with other nodes
  148. Assert( moveTypeBits & bits_CAP_MOVE_CRAWL );
  149. moveTypeBits = ( CapabilitiesGet() & bits_CAP_MOVE_CRAWL ) ?
  150. ( moveTypeBits & (~bits_CAP_MOVE_GROUP ) | bits_CAP_MOVE_CRAWL ) :
  151. ( moveTypeBits & (~bits_CAP_MOVE_CRAWL) );
  152. linkType = MoveBitsToNavType( moveTypeBits );
  153. }
  154. }
  155. // This will only trigger if the links disagree about their nav type
  156. Assert( (navType == NAV_NONE) || (navType == linkType) );
  157. navType = linkType;
  158. break;
  159. }
  160. // @TODO (toml 10-15-02): one would not expect to come out of the above logic
  161. // with NAV_NONE. However, if a graph is newly built, it can contain malformed
  162. // links that are referred to by the destination node, not the source node.
  163. // This has to be fixed
  164. if ( navType == NAV_NONE )
  165. {
  166. pNode = ppNodes[destID];
  167. for (int link=0; link < pNode->NumLinks();link++)
  168. {
  169. if (pNode->GetLinkByIndex(link)->DestNodeID(parentID) == destID)
  170. {
  171. int npcMoveBits = CapabilitiesGet();
  172. int nodeMoveBits = pNode->GetLinkByIndex(link)->m_iAcceptedMoveTypes[GetHullType()];
  173. int moveTypeBits = ( npcMoveBits & nodeMoveBits );
  174. Navigation_t linkType = MoveBitsToNavType( moveTypeBits );
  175. Assert( (navType == NAV_NONE) || (navType == linkType) );
  176. navType = linkType;
  177. DevMsg( "Note: Strange link found between nodes in AI node graph\n" );
  178. break;
  179. }
  180. }
  181. }
  182. AssertMsg( navType != NAV_NONE, "Pathfinder appears to have output a path with consecutive nodes thate are not actually connected\n" );
  183. return navType;
  184. }
  185. //-----------------------------------------------------------------------------
  186. // Purpose: Given an array of parentID's and endID, contruct a linked
  187. // list of waypoints through those parents
  188. //-----------------------------------------------------------------------------
  189. AI_Waypoint_t* CAI_Pathfinder::MakeRouteFromParents( int *parentArray, int endID )
  190. {
  191. AI_Waypoint_t *pOldWaypoint = NULL;
  192. AI_Waypoint_t *pNewWaypoint = NULL;
  193. int currentID = endID;
  194. CAI_Node **pAInode = GetNetwork()->AccessNodes();
  195. int nNextWaypointFlags = 0;
  196. while (currentID != NO_NODE)
  197. {
  198. // Try to link it to the previous waypoint
  199. int prevID = parentArray[currentID];
  200. int destID;
  201. if (prevID != NO_NODE)
  202. {
  203. destID = prevID;
  204. }
  205. else
  206. {
  207. // If we have no previous node, then use the next node
  208. if ( !pOldWaypoint )
  209. return NULL;
  210. destID = pOldWaypoint->iNodeID;
  211. }
  212. bool bWantsPreciseMovement;
  213. Navigation_t waypointType = ComputeWaypointType( &bWantsPreciseMovement, pAInode, currentID, destID );
  214. // BRJ 10/1/02
  215. // FIXME: It appears potentially possible for us to compute waypoints
  216. // here which the NPC is not capable of traversing (because
  217. // pNPC->CapabilitiesGet() in ComputeWaypointType() above filters it out).
  218. // It's also possible if none of the lines have an appropriate DestNodeID.
  219. // Um, shouldn't such a waypoint not be allowed?!?!?
  220. Assert( waypointType != NAV_NONE );
  221. int nWaypointFlags = bits_WP_TO_NODE | nNextWaypointFlags;
  222. if ( bWantsPreciseMovement )
  223. {
  224. nWaypointFlags |= bits_WP_DONT_SIMPLIFY | bits_WP_PRECISE_MOVEMENT;
  225. nNextWaypointFlags = bits_WP_DONT_SIMPLIFY | bits_WP_PRECISE_MOVEMENT;
  226. }
  227. else
  228. {
  229. nNextWaypointFlags = 0;
  230. }
  231. pNewWaypoint = new AI_Waypoint_t( pAInode[currentID]->GetPosition( GetHullType() ),
  232. pAInode[currentID]->GetYaw(), waypointType, nWaypointFlags, currentID );
  233. // Link it up...
  234. pNewWaypoint->SetNext( pOldWaypoint );
  235. pOldWaypoint = pNewWaypoint;
  236. currentID = prevID;
  237. }
  238. return pOldWaypoint;
  239. }
  240. //------------------------------------------------------------------------------
  241. // Purpose : Test if stale link is no longer stale
  242. //------------------------------------------------------------------------------
  243. bool CAI_Pathfinder::IsLinkStillStale(int moveType, CAI_Link *nodeLink)
  244. {
  245. if ( m_bIgnoreStaleLinks )
  246. return false;
  247. if ( !(nodeLink->m_LinkInfo & bits_LINK_STALE_SUGGESTED ) )
  248. return false;
  249. if ( gpGlobals->curtime < nodeLink->m_timeStaleExpires )
  250. return true;
  251. // NPC should only check one stale link per think
  252. if (gpGlobals->curtime == m_flLastStaleLinkCheckTime)
  253. {
  254. return true;
  255. }
  256. else
  257. {
  258. m_flLastStaleLinkCheckTime = gpGlobals->curtime;
  259. }
  260. // Test movement, if suceeds, clear the stale bit
  261. if (CheckStaleRoute(GetNetwork()->GetNode(nodeLink->m_iSrcID)->GetPosition(GetHullType()),
  262. GetNetwork()->GetNode(nodeLink->m_iDestID)->GetPosition(GetHullType()), moveType))
  263. {
  264. nodeLink->m_LinkInfo &= ~bits_LINK_STALE_SUGGESTED;
  265. return false;
  266. }
  267. nodeLink->m_timeStaleExpires = gpGlobals->curtime + 1.0;
  268. return true;
  269. }
  270. //-----------------------------------------------------------------------------
  271. //-----------------------------------------------------------------------------
  272. int CAI_Pathfinder::NearestNodeToNPC()
  273. {
  274. return GetNetwork()->NearestNodeToPoint( GetOuter(), GetAbsOrigin() );
  275. }
  276. //-----------------------------------------------------------------------------
  277. //-----------------------------------------------------------------------------
  278. int CAI_Pathfinder::NearestNodeToPoint( const Vector &vecOrigin )
  279. {
  280. return GetNetwork()->NearestNodeToPoint( GetOuter(), vecOrigin );
  281. }
  282. //-----------------------------------------------------------------------------
  283. // Purpose: Build a path between two nodes
  284. //-----------------------------------------------------------------------------
  285. static float s_pDangerDistFactor[3] = { 2048.0f, 4096.0f, 8192.0f };
  286. AI_Waypoint_t *CAI_Pathfinder::FindBestPath(int startID, int endID)
  287. {
  288. AI_PROFILE_SCOPE( CAI_Pathfinder_FindBestPath );
  289. if ( !GetNetwork()->NumNodes() )
  290. return NULL;
  291. #ifdef AI_PERF_MON
  292. m_nPerfStatPB++;
  293. #endif
  294. int nNodes = GetNetwork()->NumNodes();
  295. CAI_Node **pAInode = GetNetwork()->AccessNodes();
  296. CVarBitVec openBS(nNodes);
  297. CVarBitVec closeBS(nNodes);
  298. // ------------- INITIALIZE ------------------------
  299. float* nodeG = (float *)stackalloc( nNodes * sizeof(float) );
  300. float* nodeH = (float *)stackalloc( nNodes * sizeof(float) );
  301. float* nodeF = (float *)stackalloc( nNodes * sizeof(float) );
  302. int* nodeP = (int *)stackalloc( nNodes * sizeof(int) ); // Node parent
  303. for (int node=0;node<nNodes;node++)
  304. {
  305. nodeG[node] = FLT_MAX;
  306. nodeP[node] = -1;
  307. }
  308. nodeG[startID] = 0;
  309. nodeH[startID] = 0.1*(pAInode[startID]->GetPosition(GetHullType())-pAInode[endID]->GetPosition(GetHullType())).Length(); // Don't want to over estimate
  310. nodeF[startID] = nodeG[startID] + nodeH[startID];
  311. openBS.Set(startID);
  312. closeBS.Set( startID );
  313. // --------------- FIND BEST PATH ------------------
  314. while (!openBS.IsAllClear())
  315. {
  316. int smallestID = CAI_Network::FindBSSmallest(&openBS,nodeF,nNodes);
  317. openBS.Clear(smallestID);
  318. CAI_Node *pSmallestNode = pAInode[smallestID];
  319. if (GetOuter()->IsUnusableNode(smallestID, pSmallestNode->GetHint()))
  320. continue;
  321. if (smallestID == endID)
  322. {
  323. AI_Waypoint_t* route = MakeRouteFromParents(&nodeP[0], endID);
  324. return route;
  325. }
  326. // Check this if the node is immediately in the path after the startNode
  327. // that it isn't blocked
  328. for (int link=0; link < pSmallestNode->NumLinks();link++)
  329. {
  330. CAI_Link *nodeLink = pSmallestNode->GetLinkByIndex(link);
  331. if (!IsLinkUsable(nodeLink,smallestID))
  332. continue;
  333. // FIXME: the cost function should take into account Node costs (danger, flanking, etc).
  334. int moveType = nodeLink->m_iAcceptedMoveTypes[GetHullType()] & CapabilitiesGet();
  335. int testID = nodeLink->DestNodeID(smallestID);
  336. Vector r1 = pSmallestNode->GetPosition(GetHullType());
  337. Vector r2 = pAInode[testID]->GetPosition(GetHullType());
  338. float dist = GetOuter()->GetNavigator()->MovementCost( moveType, r1, r2 ); // MovementCost takes ref parameters!!
  339. if ( dist == FLT_MAX )
  340. continue;
  341. if ( nodeLink->m_LinkInfo & bits_PREFER_AVOID )
  342. {
  343. dist += 512.0f;
  344. }
  345. if ( nodeLink->m_nDangerCount > 0 )
  346. {
  347. if ( nodeLink->m_nDangerCount > 3 )
  348. continue;
  349. dist += s_pDangerDistFactor[ nodeLink->m_nDangerCount - 1 ];
  350. }
  351. float new_g = nodeG[smallestID] + dist;
  352. if ( !closeBS.IsBitSet(testID) || (new_g < nodeG[testID]) )
  353. {
  354. nodeP[testID] = smallestID;
  355. nodeG[testID] = new_g;
  356. nodeH[testID] = (pAInode[testID]->GetPosition(GetHullType())-pAInode[endID]->GetPosition(GetHullType())).Length();
  357. nodeF[testID] = nodeG[testID] + nodeH[testID];
  358. closeBS.Set( testID );
  359. openBS.Set( testID );
  360. }
  361. }
  362. }
  363. return NULL;
  364. }
  365. //-----------------------------------------------------------------------------
  366. // Purpose: Find a short random path of at least pathLength distance. If
  367. // vDirection is given random path will expand in the given direction,
  368. // and then attempt to go generally straight
  369. //-----------------------------------------------------------------------------
  370. AI_Waypoint_t* CAI_Pathfinder::FindShortRandomPath(int startID, float minPathLength, const Vector &directionIn)
  371. {
  372. int pNeighbor[AI_MAX_NODE_LINKS];
  373. int pStaleNeighbor[AI_MAX_NODE_LINKS];
  374. int numNeighbors = 1; // The start node
  375. int numStaleNeighbors = 0;
  376. int neighborID = NO_NODE;
  377. int nNodes = GetNetwork()->NumNodes();
  378. CAI_Node **pAInode = GetNetwork()->AccessNodes();
  379. if ( !nNodes )
  380. return NULL;
  381. MARK_TASK_EXPENSIVE();
  382. int *nodeParent = (int *)stackalloc( sizeof(int) * nNodes );
  383. CVarBitVec closeBS(nNodes);
  384. Vector vDirection = directionIn;
  385. // ------------------------------------------
  386. // Bail immediately if node has no neighbors
  387. // ------------------------------------------
  388. if (pAInode[startID]->NumLinks() == 0)
  389. {
  390. return NULL;
  391. }
  392. // ------------- INITIALIZE ------------------------
  393. nodeParent[startID] = NO_NODE;
  394. pNeighbor[0] = startID;
  395. // --------------- FIND PATH ---------------------------------------------------------------
  396. // Quit when path is long enough, and I've run out of neighbors unless I'm on a climb node
  397. // in which case I'm not allowed to stop
  398. // -----------------------------------------------------------------------------------------
  399. float pathLength = 0;
  400. int nSearchCount = 0;
  401. while ( (pathLength < minPathLength) ||
  402. (neighborID != NO_NODE && pAInode[neighborID]->GetType() == NODE_CLIMB))
  403. {
  404. nSearchCount++;
  405. // If no neighbors try circling back to last node
  406. if (neighborID != NO_NODE &&
  407. numNeighbors == 0 &&
  408. numStaleNeighbors == 0 )
  409. {
  410. // If we dead ended on a climb node we've failed as we
  411. // aren't allowed to stop on a climb node
  412. if (pAInode[neighborID]->GetType() == NODE_CLIMB)
  413. {
  414. // If no neighbors exist we've failed.
  415. return NULL;
  416. }
  417. // Otherwise accept this path to a dead end
  418. else
  419. {
  420. AI_Waypoint_t* route = MakeRouteFromParents(&nodeParent[0], neighborID);
  421. return route;
  422. }
  423. }
  424. // ----------------------
  425. // Pick a neighbor
  426. // ----------------------
  427. int lastID = neighborID;
  428. // If vDirection is non-zero attempt to expand close to current direction
  429. if (vDirection != vec3_origin)
  430. {
  431. float bestDot = -1;
  432. Vector vLastPos;
  433. if (lastID == NO_NODE)
  434. {
  435. vLastPos = GetLocalOrigin();
  436. }
  437. else
  438. {
  439. vLastPos = pAInode[lastID]->GetOrigin();
  440. }
  441. // If no neighbors, try using a stale one
  442. if (numNeighbors == 0)
  443. {
  444. neighborID = pStaleNeighbor[random->RandomInt(0,numStaleNeighbors-1)];
  445. }
  446. else
  447. {
  448. for (int i=0;i<numNeighbors;i++)
  449. {
  450. Vector nodeDir = vLastPos - pAInode[pNeighbor[i]]->GetOrigin();
  451. VectorNormalize(nodeDir);
  452. float fDotPr = DotProduct(vDirection,nodeDir);
  453. if (fDotPr > bestDot)
  454. {
  455. bestDot = fDotPr;
  456. neighborID = pNeighbor[i];
  457. }
  458. }
  459. }
  460. if (neighborID != NO_NODE)
  461. {
  462. vDirection = vLastPos - pAInode[neighborID]->GetOrigin();
  463. VectorNormalize(vDirection);
  464. }
  465. }
  466. // Pick random neighbor
  467. else if (numNeighbors != 0)
  468. {
  469. neighborID = pNeighbor[random->RandomInt(0,numNeighbors-1)];
  470. }
  471. // If no neighbors, try using a stale one
  472. else
  473. {
  474. neighborID = pStaleNeighbor[random->RandomInt(0,numStaleNeighbors-1)];
  475. }
  476. // BUGBUG: This routine is totally hosed!
  477. if ( neighborID < 0 )
  478. return NULL;
  479. // Set previous nodes parent
  480. nodeParent[neighborID] = lastID;
  481. closeBS.Set(neighborID);
  482. // Add the new length
  483. if (lastID != NO_NODE)
  484. {
  485. pathLength += (pAInode[lastID]->GetOrigin() - pAInode[neighborID]->GetOrigin()).Length();
  486. }
  487. // If path is long enough or we've hit a maximum number of search nodes,
  488. // we're done unless we've ended on a climb node
  489. if ((pathLength >= minPathLength || nSearchCount > 20) &&
  490. pAInode[neighborID]->GetType() != NODE_CLIMB)
  491. {
  492. return MakeRouteFromParents(&nodeParent[0], neighborID);
  493. }
  494. // Clear neighbors
  495. numNeighbors = 0;
  496. numStaleNeighbors = 0;
  497. // Now add in new neighbors, pick links in different order ever time
  498. pAInode[neighborID]->ShuffleLinks();
  499. for (int link=0; link < pAInode[neighborID]->NumLinks();link++)
  500. {
  501. if ( numStaleNeighbors == ARRAYSIZE(pStaleNeighbor) )
  502. {
  503. AssertMsg( 0, "Array overflow" );
  504. return NULL;
  505. }
  506. if ( numNeighbors == ARRAYSIZE(pStaleNeighbor) )
  507. {
  508. AssertMsg( 0, "Array overflow" );
  509. return NULL;
  510. }
  511. CAI_Link* nodeLink = pAInode[neighborID]->GetShuffeledLink(link);
  512. int testID = nodeLink->DestNodeID(neighborID);
  513. // --------------------------------------------------------------------------
  514. // Don't loop
  515. // --------------------------------------------------------------------------
  516. if (closeBS.IsBitSet(testID))
  517. {
  518. continue;
  519. }
  520. // --------------------------------------------------------------------------
  521. // Don't go back to the node I just visited
  522. // --------------------------------------------------------------------------
  523. if (testID == lastID)
  524. {
  525. continue;
  526. }
  527. // --------------------------------------------------------------------------
  528. // Make sure link is valid
  529. // --------------------------------------------------------------------------
  530. if (!IsLinkUsable(nodeLink,neighborID))
  531. {
  532. continue;
  533. }
  534. // --------------------------------------------------------------------------
  535. // If its a stale node add to stale list
  536. // --------------------------------------------------------------------------
  537. if (pAInode[testID]->IsLocked())
  538. {
  539. pStaleNeighbor[numStaleNeighbors]=testID;
  540. numStaleNeighbors++;
  541. }
  542. // --------------------------------------
  543. // Add to list of non-stale neighbors
  544. // --------------------------------------
  545. else
  546. {
  547. pNeighbor[numNeighbors]=testID;
  548. numNeighbors++;
  549. }
  550. }
  551. }
  552. // Failed to get a path of full length, but return what we have
  553. return MakeRouteFromParents(&nodeParent[0], neighborID);
  554. }
  555. //------------------------------------------------------------------------------
  556. // Purpose : Returns true is link us usable by the given NPC from the
  557. // startID node.
  558. //------------------------------------------------------------------------------
  559. bool CAI_Pathfinder::IsLinkUsable(CAI_Link *pLink, int startID)
  560. {
  561. // --------------------------------------------------------------------------
  562. // Skip if link turned off
  563. // --------------------------------------------------------------------------
  564. if (pLink->m_LinkInfo & ( bits_LINK_OFF | bits_LINK_ASW_BASHABLE ) )
  565. {
  566. CAI_DynamicLink *pDynamicLink = pLink->m_pDynamicLink;
  567. if ( !pDynamicLink || pDynamicLink->m_strAllowUse == NULL_STRING )
  568. return false;
  569. const char *pszAllowUse = STRING( pDynamicLink->m_strAllowUse );
  570. if ( pDynamicLink->m_bInvertAllow )
  571. {
  572. // Exlude only the specified entity name or classname
  573. if ( GetOuter()->NameMatches(pszAllowUse) || GetOuter()->ClassMatches( pszAllowUse ) )
  574. return false;
  575. }
  576. else
  577. {
  578. // Exclude everything but the allowed entity name or classname
  579. if ( !GetOuter()->NameMatches( pszAllowUse) && !GetOuter()->ClassMatches( pszAllowUse ) )
  580. return false;
  581. }
  582. }
  583. // --------------------------------------------------------------------------
  584. // Get the destination nodeID
  585. // --------------------------------------------------------------------------
  586. int endID = pLink->DestNodeID(startID);
  587. // --------------------------------------------------------------------------
  588. // Make sure I have the ability to do the type of movement specified by the link
  589. // --------------------------------------------------------------------------
  590. int linkMoveTypes = pLink->m_iAcceptedMoveTypes[GetHullType()];
  591. int moveType = ( linkMoveTypes & CapabilitiesGet() );
  592. CAI_Node *pStartNode,*pEndNode;
  593. pStartNode = GetNetwork()->GetNode(startID);
  594. pEndNode = GetNetwork()->GetNode(endID);
  595. if ( (linkMoveTypes & bits_CAP_MOVE_JUMP) && !moveType )
  596. {
  597. CAI_Hint *pStartHint = pStartNode->GetHint();
  598. CAI_Hint *pEndHint = pEndNode->GetHint();
  599. if ( pStartHint && pEndHint )
  600. {
  601. if ( pStartHint->HintType() == HINT_JUMP_OVERRIDE &&
  602. pEndHint->HintType() == HINT_JUMP_OVERRIDE &&
  603. ( ( ( pStartHint->GetSpawnFlags() | pEndHint->GetSpawnFlags() ) & SF_ALLOW_JUMP_UP ) || pStartHint->GetAbsOrigin().z > pEndHint->GetAbsOrigin().z ) )
  604. {
  605. if ( !pStartNode->IsLocked() )
  606. {
  607. if ( pStartHint->GetTargetNode() == -1 || pStartHint->GetTargetNode() == endID )
  608. moveType = bits_CAP_MOVE_JUMP;
  609. }
  610. }
  611. }
  612. }
  613. if (!moveType)
  614. {
  615. return false;
  616. }
  617. // --------------------------------------------------------------------------
  618. // Check if NPC has a reason not to use the desintion node
  619. // --------------------------------------------------------------------------
  620. if (GetOuter()->IsUnusableNode(endID, pEndNode->GetHint()))
  621. {
  622. return false;
  623. }
  624. // --------------------------------------------------------------------------
  625. // If a jump make sure the jump is within NPC's legal parameters for jumping
  626. // --------------------------------------------------------------------------
  627. if (moveType == bits_CAP_MOVE_JUMP)
  628. {
  629. if (!GetOuter()->IsJumpLegal(pStartNode->GetPosition(GetHullType()),
  630. pEndNode->GetPosition(GetHullType()),
  631. pEndNode->GetPosition(GetHullType())))
  632. {
  633. return false;
  634. }
  635. }
  636. // --------------------------------------------------------------------------
  637. // If an NPC suggested that this link is stale and I haven't checked it yet
  638. // I should make sure the link is still valid before proceeding
  639. // --------------------------------------------------------------------------
  640. if (pLink->m_LinkInfo & bits_LINK_STALE_SUGGESTED)
  641. {
  642. if (IsLinkStillStale(moveType, pLink))
  643. {
  644. return false;
  645. }
  646. }
  647. return true;
  648. }
  649. //-----------------------------------------------------------------------------
  650. static int NPCBuildFlags( CAI_BaseNPC *pNPC, const Vector &vecOrigin )
  651. {
  652. // If vecOrigin the the npc's position and npc is climbing only climb nodes allowed
  653. if (pNPC->GetLocalOrigin() == vecOrigin && pNPC->GetNavType() == NAV_CLIMB)
  654. {
  655. return bits_BUILD_CLIMB;
  656. }
  657. else if (pNPC->CapabilitiesGet() & bits_CAP_MOVE_FLY)
  658. {
  659. return bits_BUILD_FLY | bits_BUILD_GIVEWAY;
  660. }
  661. else if (pNPC->CapabilitiesGet() & bits_CAP_MOVE_GROUND)
  662. {
  663. int buildFlags = bits_BUILD_GROUND | bits_BUILD_GIVEWAY;
  664. if (pNPC->CapabilitiesGet() & bits_CAP_MOVE_JUMP)
  665. {
  666. buildFlags |= bits_BUILD_JUMP;
  667. }
  668. if (pNPC->CapabilitiesGet() & bits_CAP_MOVE_CRAWL)
  669. {
  670. buildFlags |= bits_BUILD_CRAWL;
  671. }
  672. return buildFlags;
  673. }
  674. return 0;
  675. }
  676. //-----------------------------------------------------------------------------
  677. // Creates a node waypoint
  678. //-----------------------------------------------------------------------------
  679. AI_Waypoint_t* CAI_Pathfinder::CreateNodeWaypoint( Hull_t hullType, int nodeID, int nodeFlags )
  680. {
  681. CAI_Node *pNode = GetNetwork()->GetNode(nodeID);
  682. Navigation_t navType;
  683. switch(pNode->GetType())
  684. {
  685. case NODE_CLIMB:
  686. navType = NAV_CLIMB;
  687. break;
  688. case NODE_AIR:
  689. navType = NAV_FLY;
  690. break;
  691. default:
  692. navType = NAV_GROUND;
  693. break;
  694. }
  695. return new AI_Waypoint_t( pNode->GetPosition(hullType), pNode->GetYaw(), navType, ( bits_WP_TO_NODE | nodeFlags) , nodeID );
  696. }
  697. //-----------------------------------------------------------------------------
  698. // Purpose: Returns a route to a node for the given npc with the given
  699. // build flags
  700. //-----------------------------------------------------------------------------
  701. AI_Waypoint_t* CAI_Pathfinder::RouteToNode(const Vector &vecOrigin, int buildFlags, int nodeID, float goalTolerance)
  702. {
  703. AI_PROFILE_SCOPE( CAI_Pathfinder_RouteToNode );
  704. buildFlags |= NPCBuildFlags( GetOuter(), vecOrigin );
  705. buildFlags &= ~bits_BUILD_GET_CLOSE;
  706. // Check if vecOrigin is already at the smallest node
  707. // FIXME: an equals check is a bit sloppy, this should be a tolerance
  708. const Vector &vecNodePosition = GetNetwork()->GetNode(nodeID)->GetPosition(GetHullType());
  709. if (vecOrigin == vecNodePosition)
  710. {
  711. return CreateNodeWaypoint( GetHullType(), nodeID, bits_WP_TO_GOAL );
  712. }
  713. // Otherwise try to build a local route to the node
  714. AI_Waypoint_t *pResult = BuildLocalRoute(vecOrigin,
  715. vecNodePosition, NULL, bits_WP_TO_NODE, nodeID, buildFlags, goalTolerance);
  716. if ( pResult )
  717. pResult->iNodeID = nodeID;
  718. return pResult;
  719. }
  720. //-----------------------------------------------------------------------------
  721. // Purpose: Returns a route to a node for the given npc with the given
  722. // build flags
  723. //-----------------------------------------------------------------------------
  724. AI_Waypoint_t* CAI_Pathfinder::RouteFromNode(const Vector &vecOrigin, int buildFlags, int nodeID, float goalTolerance)
  725. {
  726. AI_PROFILE_SCOPE( CAI_Pathfinder_RouteFromNode );
  727. buildFlags |= NPCBuildFlags( GetOuter(), vecOrigin );
  728. buildFlags |= bits_BUILD_GET_CLOSE;
  729. // Check if vecOrigin is already at the smallest node
  730. // FIXME: an equals check is a bit sloppy, this should be a tolerance
  731. CAI_Node *pNode = GetNetwork()->GetNode(nodeID);
  732. const Vector &vecNodePosition = pNode->GetPosition(GetHullType());
  733. if (vecOrigin == vecNodePosition)
  734. {
  735. return CreateNodeWaypoint( GetHullType(), nodeID, bits_WP_TO_GOAL );
  736. }
  737. // Otherwise try to build a local route from the node
  738. AI_Waypoint_t* pResult = BuildLocalRoute( vecNodePosition,
  739. vecOrigin, NULL, bits_WP_TO_GOAL, NO_NODE, buildFlags, goalTolerance);
  740. // Handle case of target hanging over edge near climb dismount
  741. if ( !pResult &&
  742. pNode->GetType() == NODE_CLIMB &&
  743. ( vecOrigin - vecNodePosition ).Length2DSqr() < 32.0*32.0 &&
  744. GetOuter()->GetMoveProbe()->CheckStandPosition(vecNodePosition, GetOuter()->GetAITraceMask_BrushOnly() ) )
  745. {
  746. pResult = new AI_Waypoint_t( vecOrigin, 0, NAV_GROUND, bits_WP_TO_GOAL, nodeID );
  747. }
  748. return pResult;
  749. }
  750. //-----------------------------------------------------------------------------
  751. // Builds a simple route (no triangulation, no making way)
  752. //-----------------------------------------------------------------------------
  753. AI_Waypoint_t *CAI_Pathfinder::BuildSimpleRoute( Navigation_t navType, const Vector &vStart,
  754. const Vector &vEnd, const CBaseEntity *pTarget, int endFlags, int nodeID,
  755. int nodeTargetType, float flYaw )
  756. {
  757. Assert( navType == NAV_JUMP || navType == NAV_CLIMB || navType == NAV_CRAWL ); // this is what this here function is for
  758. // Only allowed to jump to ground nodes
  759. if ((nodeID == NO_NODE) || (GetNetwork()->GetNode(nodeID)->GetType() == nodeTargetType) )
  760. {
  761. AIMoveTrace_t moveTrace;
  762. GetOuter()->GetMoveProbe()->MoveLimit( navType, vStart, vEnd, GetOuter()->GetAITraceMask(), pTarget, &moveTrace );
  763. // If I was able to make the move, or the vEnd is the
  764. // goal and I'm within tolerance, just move to vEnd
  765. if (!IsMoveBlocked(moveTrace))
  766. {
  767. // It worked so return a route of length one to the endpoint
  768. return new AI_Waypoint_t( vEnd, flYaw, navType, endFlags, nodeID );
  769. }
  770. }
  771. return NULL;
  772. }
  773. //-----------------------------------------------------------------------------
  774. // Builds a complex route (triangulation, making way)
  775. //-----------------------------------------------------------------------------
  776. AI_Waypoint_t *CAI_Pathfinder::BuildComplexRoute( Navigation_t navType, const Vector &vStart,
  777. const Vector &vEnd, const CBaseEntity *pTarget, int endFlags, int nodeID,
  778. int buildFlags, float flYaw, float goalTolerance, float maxLocalNavDistance )
  779. {
  780. AI_PROFILE_SCOPE( CAI_Pathfinder_BuildComplexRoute );
  781. float flTotalDist = ComputePathDistance( navType, vStart, vEnd );
  782. if ( flTotalDist < 0.0625 )
  783. {
  784. return new AI_Waypoint_t( vEnd, flYaw, navType, endFlags, nodeID );
  785. }
  786. unsigned int collideFlags = (buildFlags & bits_BUILD_IGNORE_NPCS) ? GetOuter()->GetAITraceMask_BrushOnly() : GetOuter()->GetAITraceMask();
  787. bool bCheckGround = (GetOuter()->CapabilitiesGet() & bits_CAP_SKIP_NAV_GROUND_CHECK) ? false : true;
  788. if ( flTotalDist <= maxLocalNavDistance || ( buildFlags & bits_BUILD_UNLIMITED_DISTANCE ) )
  789. {
  790. AIMoveTrace_t moveTrace;
  791. AI_PROFILE_SCOPE_BEGIN( CAI_Pathfinder_BuildComplexRoute_Direct );
  792. GetOuter()->GetMoveProbe()->MoveLimit( navType, vStart, vEnd, collideFlags, pTarget, (bCheckGround) ? 100 : 0, &moveTrace);
  793. // If I was able to make the move...
  794. if (!IsMoveBlocked(moveTrace))
  795. {
  796. // It worked so return a route of length one to the endpoint
  797. return new AI_Waypoint_t( vEnd, flYaw, navType, endFlags, nodeID );
  798. }
  799. // ...or the vEnd is thegoal and I'm within tolerance, just move to vEnd
  800. if ( (buildFlags & bits_BUILD_GET_CLOSE) &&
  801. (endFlags & bits_WP_TO_GOAL) &&
  802. moveTrace.flDistObstructed <= goalTolerance )
  803. {
  804. return new AI_Waypoint_t( vEnd, flYaw, navType, endFlags, nodeID );
  805. }
  806. AI_PROFILE_SCOPE_END();
  807. // -------------------------------------------------------------------
  808. // Try to triangulate if requested
  809. // -------------------------------------------------------------------
  810. AI_PROFILE_SCOPE_BEGIN( CAI_Pathfinder_BuildComplexRoute_Triangulate );
  811. if (buildFlags & bits_BUILD_TRIANG)
  812. {
  813. if ( !UseStrongOptimizations() || ( GetOuter()->GetState() == NPC_STATE_SCRIPT || GetOuter()->IsCurSchedule( SCHED_SCENE_GENERIC, false ) ) )
  814. {
  815. float flTotalDist = ComputePathDistance( navType, vStart, vEnd );
  816. AI_Waypoint_t *triangRoute = BuildTriangulationRoute(vStart, vEnd, pTarget,
  817. endFlags, nodeID, flYaw, flTotalDist - moveTrace.flDistObstructed, navType);
  818. if (triangRoute)
  819. {
  820. return triangRoute;
  821. }
  822. }
  823. }
  824. AI_PROFILE_SCOPE_END();
  825. // -------------------------------------------------------------------
  826. // Try to giveway if requested
  827. // -------------------------------------------------------------------
  828. if (moveTrace.fStatus == AIMR_BLOCKED_NPC && (buildFlags & bits_BUILD_GIVEWAY))
  829. {
  830. // If I can't get there even ignoring NPCs, don't bother to request a giveway
  831. AIMoveTrace_t moveTrace2;
  832. GetOuter()->GetMoveProbe()->MoveLimit( navType, vStart, vEnd, GetOuter()->GetAITraceMask_BrushOnly(), pTarget, (bCheckGround) ? 100 : 0, &moveTrace2 );
  833. if (!IsMoveBlocked(moveTrace2))
  834. {
  835. // If I can clear the way return a route of length one to the target location
  836. if ( CanGiveWay(vStart, vEnd, moveTrace.pObstruction) )
  837. {
  838. return new AI_Waypoint_t( vEnd, flYaw, navType, endFlags, nodeID );
  839. }
  840. }
  841. }
  842. }
  843. return NULL;
  844. }
  845. //-----------------------------------------------------------------------------
  846. // Purpose: Attempts to build a crawl route between vStart
  847. // and vEnd, ignoring entity pTarget
  848. // Input :
  849. // Output : Returns a route if successful or NULL if no local route was possible
  850. //-----------------------------------------------------------------------------
  851. AI_Waypoint_t *CAI_Pathfinder::BuildCrawlRoute(const Vector &vStart, const Vector &vEnd,
  852. const CBaseEntity *pTarget, int endFlags, int nodeID, int buildFlags, float flYaw, float goalTolerance)
  853. {
  854. // Only allowed to jump to ground nodes
  855. //return BuildSimpleRoute( NAV_CRAWL, vStart, vEnd, pTarget,
  856. // endFlags, nodeID, NODE_GROUND, flYaw );
  857. return BuildComplexRoute( NAV_CRAWL, vStart, vEnd, pTarget,
  858. endFlags, nodeID, buildFlags, flYaw, goalTolerance, MAX_LOCAL_NAV_DIST_GROUND[UseStrongOptimizations()] );
  859. }
  860. //-----------------------------------------------------------------------------
  861. // Purpose: Attempts to build a jump route between vStart
  862. // and vEnd, ignoring entity pTarget
  863. // Input :
  864. // Output : Returns a route if successful or NULL if no local route was possible
  865. //-----------------------------------------------------------------------------
  866. AI_Waypoint_t *CAI_Pathfinder::BuildJumpRoute(const Vector &vStart, const Vector &vEnd,
  867. const CBaseEntity *pTarget, int endFlags, int nodeID, int buildFlags, float flYaw)
  868. {
  869. Vector vecDiff = vStart - vEnd;
  870. if( fabs(vecDiff.z) <= 24.0f && vecDiff.Length2D() <= Square(600.0f) )
  871. return NULL;
  872. // Only allowed to jump to ground nodes
  873. return BuildSimpleRoute( NAV_JUMP, vStart, vEnd, pTarget,
  874. endFlags, nodeID, NODE_GROUND, flYaw );
  875. }
  876. //-----------------------------------------------------------------------------
  877. // Purpose: Attempts to build a climb route between vStart
  878. // and vEnd, ignoring entity pTarget
  879. // Input :
  880. // Output : Returns a route if successful or NULL if no climb route was possible
  881. //-----------------------------------------------------------------------------
  882. AI_Waypoint_t *CAI_Pathfinder::BuildClimbRoute(const Vector &vStart, const Vector &vEnd, const CBaseEntity *pTarget, int endFlags, int nodeID, int buildFlags, float flYaw)
  883. {
  884. // Only allowed to climb to climb nodes
  885. return BuildSimpleRoute( NAV_CLIMB, vStart, vEnd, pTarget,
  886. endFlags, nodeID, NODE_CLIMB, flYaw );
  887. }
  888. //-----------------------------------------------------------------------------
  889. // Purpose: Attempts to build a ground route between vStart
  890. // and vEnd, ignoring entity pTarget the the given tolerance
  891. // Input :
  892. // Output : Returns a route if successful or NULL if no ground route was possible
  893. //-----------------------------------------------------------------------------
  894. AI_Waypoint_t *CAI_Pathfinder::BuildGroundRoute(const Vector &vStart, const Vector &vEnd,
  895. const CBaseEntity *pTarget, int endFlags, int nodeID, int buildFlags, float flYaw, float goalTolerance)
  896. {
  897. return BuildComplexRoute( NAV_GROUND, vStart, vEnd, pTarget,
  898. endFlags, nodeID, buildFlags, flYaw, goalTolerance, MAX_LOCAL_NAV_DIST_GROUND[UseStrongOptimizations()] );
  899. }
  900. //-----------------------------------------------------------------------------
  901. // Purpose: Attempts to build a fly route between vStart
  902. // and vEnd, ignoring entity pTarget the the given tolerance
  903. // Input :
  904. // Output : Returns a route if successful or NULL if no ground route was possible
  905. //-----------------------------------------------------------------------------
  906. AI_Waypoint_t *CAI_Pathfinder::BuildFlyRoute(const Vector &vStart, const Vector &vEnd,
  907. const CBaseEntity *pTarget, int endFlags, int nodeID, int buildFlags, float flYaw, float goalTolerance)
  908. {
  909. return BuildComplexRoute( NAV_FLY, vStart, vEnd, pTarget,
  910. endFlags, nodeID, buildFlags, flYaw, goalTolerance, MAX_LOCAL_NAV_DIST_FLY[UseStrongOptimizations()] );
  911. }
  912. //-----------------------------------------------------------------------------
  913. // Purpose: Attempts to build a route between vStart and vEnd, requesting the
  914. // pNPCBlocker to get out of the way
  915. // Input :
  916. // Output : Returns a route if successful or NULL if giveway failed
  917. //-----------------------------------------------------------------------------
  918. bool CAI_Pathfinder::CanGiveWay( const Vector& vStart, const Vector& vEnd, CBaseEntity *pBlocker)
  919. {
  920. // FIXME: make this a CAI_BaseNPC member function
  921. CAI_BaseNPC *pNPCBlocker = pBlocker->MyNPCPointer();
  922. if (pNPCBlocker && pNPCBlocker->edict())
  923. {
  924. Disposition_t eDispBlockerToMe = pNPCBlocker->IRelationType( GetOuter() );
  925. if ( ( eDispBlockerToMe == D_LI ) || ( eDispBlockerToMe == D_NU ) )
  926. {
  927. return true;
  928. }
  929. return false;
  930. // FIXME: this is called in route creation, not navigation. It shouldn't actually make
  931. // anyone get out of their way, just see if they'll honor the request.
  932. // things like locked doors, enemies and such should refuse, all others shouldn't.
  933. // things like breakables should know who is trying to break them, though a door hidden behind
  934. // some boxes shouldn't be known to the AI even though a route should connect through them but
  935. // be turned off.
  936. /*
  937. Vector moveDir = (vEnd - vStart).Normalize();
  938. Vector blockerDir = (pNPCBlocker->GetLocalOrigin() - vStart);
  939. float blockerDist = DotProduct(moveDir,blockerDir);
  940. Vector blockPos = vStart + (moveDir*blockerDist);
  941. if (pNPCBlocker->RequestGiveWay ( m_owner->GetLocalOrigin(), blockPos, moveDir, m_owner->m_eHull))
  942. {
  943. return true;
  944. }
  945. */
  946. }
  947. return false;
  948. }
  949. //-----------------------------------------------------------------------------
  950. // Purpose: Attempts to build a triangulation route between vStart
  951. // and vEnd, ignoring entity pTarget the the given tolerance and
  952. // triangulating around a blocking object at blockDist
  953. // Input :
  954. // Output : Returns a route if successful or NULL if no local route was possible
  955. //-----------------------------------------------------------------------------
  956. AI_Waypoint_t *CAI_Pathfinder::BuildTriangulationRoute(
  957. const Vector &vStart, // from where
  958. const Vector &vEnd, // to where
  959. const CBaseEntity *pTarget, // an entity I can ignore
  960. int endFlags, // add these WP flags to the last waypoint
  961. int nodeID, // node id for the last waypoint
  962. float flYaw, // ideal yaw for the last waypoint
  963. float flDistToBlocker,// how far away is the obstruction from the start?
  964. Navigation_t navType)
  965. {
  966. AI_PROFILE_SCOPE( CAI_Pathfinder_BuildTriangulationRoute );
  967. Vector vApex;
  968. if (!Triangulate(navType, vStart, vEnd, flDistToBlocker, pTarget, &vApex ))
  969. return NULL;
  970. //-----------------------------------------------------------------------------
  971. // it worked, create a route
  972. //-----------------------------------------------------------------------------
  973. AI_Waypoint_t *pWayPoint2 = new AI_Waypoint_t( vEnd, flYaw, navType, endFlags, nodeID );
  974. // FIXME: Compute a reasonable yaw here
  975. AI_Waypoint_t *waypoint1 = new AI_Waypoint_t( vApex, 0, navType, bits_WP_TO_DETOUR, NO_NODE );
  976. waypoint1->SetNext(pWayPoint2);
  977. return waypoint1;
  978. }
  979. //-----------------------------------------------------------------------------
  980. // Purpose: Get the next node (with wrapping) around a circularly wound path
  981. // Input : nLastNode - The starting node
  982. // nDirection - Direction we're moving
  983. // nNumNodes - Total nodes in the chain
  984. //-----------------------------------------------------------------------------
  985. inline int GetNextPoint( int nLastNode, int nDirection, int nNumNodes )
  986. {
  987. int nNextNode = nLastNode + nDirection;
  988. if ( nNextNode > (nNumNodes-1) )
  989. nNextNode = 0;
  990. else if ( nNextNode < 0 )
  991. nNextNode = (nNumNodes-1);
  992. return nNextNode;
  993. }
  994. //-----------------------------------------------------------------------------
  995. // Purpose: Attempt to wind a route through a series of node points in a specified direction.
  996. // Input : *vecCorners - Points to test between
  997. // nNumCorners - Number of points to test
  998. // &vecStart - Starting position
  999. // &vecEnd - Ending position
  1000. // Output : Route through the points
  1001. //-----------------------------------------------------------------------------
  1002. AI_Waypoint_t *CAI_Pathfinder::BuildRouteThroughPoints( Vector *vecPoints, int nNumPoints, int nDirection, int nStartIndex, int nEndIndex, Navigation_t navType, CBaseEntity *pTarget )
  1003. {
  1004. AIMoveTrace_t endTrace;
  1005. endTrace.fStatus = AIMR_OK;
  1006. CAI_MoveProbe *pMoveProbe = GetOuter()->GetMoveProbe();
  1007. AI_Waypoint_t *pFirstRoute = NULL;
  1008. AI_Waypoint_t *pHeadRoute = NULL;
  1009. int nCurIndex = nStartIndex;
  1010. int nNextIndex;
  1011. // FIXME: Must be able to move to the first position (these needs some parameterization)
  1012. pMoveProbe->MoveLimit( navType, GetOuter()->GetAbsOrigin(), vecPoints[nStartIndex], GetOuter()->GetAITraceMask(), pTarget, &endTrace );
  1013. if ( IsMoveBlocked( endTrace ) )
  1014. {
  1015. // NDebugOverlay::HorzArrow( GetOuter()->GetAbsOrigin(), vecPoints[nStartIndex], 8.0f, 255, 0, 0, 0, true, 4.0f );
  1016. return NULL;
  1017. }
  1018. // NDebugOverlay::HorzArrow( GetOuter()->GetAbsOrigin(), vecPoints[nStartIndex], 8.0f, 0, 255, 0, 0, true, 4.0f );
  1019. int nRunAwayCount = 0;
  1020. while ( nRunAwayCount++ < nNumPoints )
  1021. {
  1022. // Advance our index in the specified direction
  1023. nNextIndex = GetNextPoint( nCurIndex, nDirection, nNumPoints );
  1024. // Try and build a local route between the current and next point
  1025. pMoveProbe->MoveLimit( navType, vecPoints[nCurIndex], vecPoints[nNextIndex], GetOuter()->GetAITraceMask(), pTarget, &endTrace );
  1026. if ( IsMoveBlocked( endTrace ) )
  1027. {
  1028. // TODO: Triangulate here if we failed?
  1029. // We failed, so give up
  1030. if ( pHeadRoute )
  1031. {
  1032. DeleteAll( pHeadRoute );
  1033. }
  1034. // NDebugOverlay::HorzArrow( vecPoints[nCurIndex], vecPoints[nNextIndex], 8.0f, 255, 0, 0, 0, true, 4.0f );
  1035. return NULL;
  1036. }
  1037. // NDebugOverlay::HorzArrow( vecPoints[nCurIndex], vecPoints[nNextIndex], 8.0f, 0, 255, 0, 0, true, 4.0f );
  1038. if ( pHeadRoute == NULL )
  1039. {
  1040. // Start a new route head
  1041. pFirstRoute = pHeadRoute = new AI_Waypoint_t( vecPoints[nCurIndex], 0.0f, navType, bits_WP_TO_DETOUR, NO_NODE );
  1042. }
  1043. else
  1044. {
  1045. // Link a new waypoint into the path
  1046. AI_Waypoint_t *pNewNode = new AI_Waypoint_t( vecPoints[nCurIndex], 0.0f, navType, bits_WP_TO_DETOUR|bits_WP_DONT_SIMPLIFY, NO_NODE );
  1047. pHeadRoute->SetNext( pNewNode );
  1048. pHeadRoute = pNewNode;
  1049. }
  1050. // See if we're done
  1051. if ( nNextIndex == nEndIndex )
  1052. {
  1053. AI_Waypoint_t *pNewNode = new AI_Waypoint_t( vecPoints[nEndIndex], 0.0f, navType, bits_WP_TO_DETOUR, NO_NODE );
  1054. pHeadRoute->SetNext( pNewNode );
  1055. pHeadRoute = pNewNode;
  1056. break;
  1057. }
  1058. // Advance one node
  1059. nCurIndex = nNextIndex;
  1060. }
  1061. return pFirstRoute;
  1062. }
  1063. //-----------------------------------------------------------------------------
  1064. // Purpose: Find the closest point in a list of points, to a specified position
  1065. // Input : &vecPosition - Position to test against
  1066. // *vecPoints - List of vectors we'll check
  1067. // nNumPoints - Number of points in the list
  1068. // Output : Index to the closest point in the list
  1069. //-----------------------------------------------------------------------------
  1070. inline int ClosestPointToPosition( const Vector &vecPosition, Vector *vecPoints, int nNumPoints )
  1071. {
  1072. int nBestNode = -1;
  1073. float flBestDistSqr = FLT_MAX;
  1074. float flDistSqr;
  1075. for ( int i = 0; i < nNumPoints; i++ )
  1076. {
  1077. flDistSqr = ( vecPoints[i] - vecPosition ).LengthSqr();
  1078. if ( flDistSqr < flBestDistSqr )
  1079. {
  1080. flBestDistSqr = flDistSqr;
  1081. nBestNode = i;
  1082. }
  1083. }
  1084. return nBestNode;
  1085. }
  1086. //-----------------------------------------------------------------------------
  1087. // Purpose: Find which winding through a circular list is shortest in physical distance travelled
  1088. // Input : &vecStart - Where we started from
  1089. // nStartPoint - Starting index into the points
  1090. // nEndPoint - Ending index into the points
  1091. // nNumPoints - Number of points in the list
  1092. // *vecPoints - List of vectors making up a list of points
  1093. //-----------------------------------------------------------------------------
  1094. inline int ShortestDirectionThroughPoints( const Vector &vecStart, int nStartPoint, int nEndPoint, Vector *vecPoints, int nNumPoints )
  1095. {
  1096. const int nClockwise = 1;
  1097. const int nCounterClockwise = -1;
  1098. // Find the quickest direction around the object
  1099. int nCurPoint = nStartPoint;
  1100. int nNextPoint = GetNextPoint( nStartPoint, 1, nNumPoints );
  1101. float flStartDistSqr = ( vecStart - vecPoints[nStartPoint] ).LengthSqr();
  1102. float flDistanceSqr = flStartDistSqr;
  1103. // Try going clockwise first
  1104. for ( int i = 0; i < nNumPoints; i++ )
  1105. {
  1106. flDistanceSqr += ( vecPoints[nCurPoint] - vecPoints[nNextPoint] ).LengthSqr();
  1107. if ( nNextPoint == nEndPoint )
  1108. break;
  1109. nNextPoint = GetNextPoint( nNextPoint, 1, nNumPoints );
  1110. }
  1111. // Save this to test against
  1112. float flBestDistanceSqr = flDistanceSqr;
  1113. // Start from the beginning again
  1114. flDistanceSqr = flStartDistSqr;
  1115. nCurPoint = nStartPoint;
  1116. nNextPoint = GetNextPoint( nStartPoint, -1, nNumPoints );
  1117. // Now go the other way and see if it's shorter to do so
  1118. for ( int i = 0; i < nNumPoints; i++ )
  1119. {
  1120. flDistanceSqr += ( vecPoints[nCurPoint] - vecPoints[nNextPoint] ).LengthSqr();
  1121. // We've gone over our maximum so we can't be shorter
  1122. if ( flDistanceSqr > flBestDistanceSqr )
  1123. break;
  1124. // We hit the end, we're shorter
  1125. if ( nNextPoint == nEndPoint )
  1126. return nCounterClockwise;
  1127. nNextPoint = GetNextPoint( nNextPoint, -1, nNumPoints );
  1128. }
  1129. return nClockwise;
  1130. }
  1131. //-----------------------------------------------------------------------------
  1132. // Purpose: Attempt to build an avoidance route around an object using its OBB
  1133. // Currently this function is meant for NPCs moving around a vehicle,
  1134. // and is very specialized as such
  1135. //
  1136. // Output : Returns a route if successful or NULL if no local route was possible
  1137. //-----------------------------------------------------------------------------
  1138. AI_Waypoint_t *CAI_Pathfinder::BuildOBBAvoidanceRoute( const Vector &vStart, const Vector &vEnd,
  1139. const CBaseEntity *pObstruction, // obstruction to avoid
  1140. const CBaseEntity *pTarget, // target to ignore
  1141. Navigation_t navType )
  1142. {
  1143. AI_PROFILE_SCOPE( CAI_Pathfinder_BuildOBBAvoidanceRoute );
  1144. // If the point we're navigating to is within our OBB, then fail
  1145. // TODO: We could potentially also just try to get as near as possible
  1146. if ( pObstruction->CollisionProp()->IsPointInBounds( vEnd ) )
  1147. return NULL;
  1148. // Find out how much we'll need to inflate the collision bounds to let us move past
  1149. Vector vecSize = pObstruction->CollisionProp()->OBBSize();
  1150. float flWidth = GetOuter()->GetHullWidth() * 0.5f;
  1151. float flWidthPercX = ( flWidth / vecSize.x );
  1152. float flWidthPercY = ( flWidth / vecSize.y );
  1153. // Find the points around the object, bloating it by our hull width
  1154. // The ordering of these corners wind clockwise around the object, starting at the top left
  1155. Vector vecPoints[4];
  1156. pObstruction->CollisionProp()->NormalizedToWorldSpace( Vector( -flWidthPercX, 1+flWidthPercY, 0.25f ), &vecPoints[0] );
  1157. pObstruction->CollisionProp()->NormalizedToWorldSpace( Vector( 1+flWidthPercX, 1+flWidthPercY, 0.25f ), &vecPoints[1] );
  1158. pObstruction->CollisionProp()->NormalizedToWorldSpace( Vector( 1+flWidthPercX, -flWidthPercY, 0.25f ), &vecPoints[2] );
  1159. pObstruction->CollisionProp()->NormalizedToWorldSpace( Vector( -flWidthPercX, -flWidthPercY, 0.25f ), &vecPoints[3] );
  1160. // Find the two points nearest our goals
  1161. int nStartPoint = ClosestPointToPosition( vStart, vecPoints, ARRAYSIZE( vecPoints ) );
  1162. int nEndPoint = ClosestPointToPosition( vEnd, vecPoints, ARRAYSIZE( vecPoints ) );
  1163. // We won't be able to build a route if we're moving no distance between points
  1164. if ( nStartPoint == nEndPoint )
  1165. return NULL;
  1166. // Find the shortest path around this wound polygon (direction is how to step through array)
  1167. int nDirection = ShortestDirectionThroughPoints( vStart, nStartPoint, nEndPoint, vecPoints, ARRAYSIZE( vecPoints ) );
  1168. // Attempt to build a route in our direction
  1169. AI_Waypoint_t *pRoute = BuildRouteThroughPoints( vecPoints, ARRAYSIZE(vecPoints), nDirection, nStartPoint, nEndPoint, navType, (CBaseEntity *) pTarget );
  1170. if ( pRoute == NULL )
  1171. {
  1172. // Failed that way, so try the opposite
  1173. pRoute = BuildRouteThroughPoints( vecPoints, ARRAYSIZE(vecPoints), (-nDirection), nStartPoint, nEndPoint, navType, (CBaseEntity *) pTarget );
  1174. if ( pRoute == NULL )
  1175. return NULL;
  1176. }
  1177. return pRoute;
  1178. }
  1179. //-----------------------------------------------------------------------------
  1180. // Purpose: Attempts to build a local route (not using nodes) between vStart
  1181. // and vEnd, ignoring entity pTarget the the given tolerance
  1182. // Input :
  1183. // Output : Returns a route if successful or NULL if no local route was possible
  1184. //-----------------------------------------------------------------------------
  1185. AI_Waypoint_t *CAI_Pathfinder::BuildLocalRoute(const Vector &vStart, const Vector &vEnd, const CBaseEntity *pTarget, int endFlags, int nodeID, int buildFlags, float goalTolerance)
  1186. {
  1187. AI_PROFILE_SCOPE( CAI_Pathfinder_BuildLocalRoute );
  1188. // Get waypoint yaw
  1189. float flYaw;
  1190. if (nodeID != NO_NODE)
  1191. {
  1192. flYaw = GetNetwork()->GetNode(nodeID)->GetYaw();
  1193. }
  1194. else
  1195. {
  1196. flYaw = 0;
  1197. }
  1198. // Try a ground route if requested
  1199. if (buildFlags & bits_BUILD_GROUND)
  1200. {
  1201. AI_Waypoint_t *groundRoute = BuildGroundRoute(vStart,vEnd,pTarget,endFlags,nodeID,buildFlags,flYaw,goalTolerance);
  1202. if (groundRoute)
  1203. {
  1204. return groundRoute;
  1205. }
  1206. }
  1207. // Try a fly route if requested
  1208. if ( buildFlags & bits_BUILD_FLY )
  1209. {
  1210. AI_Waypoint_t *flyRoute = BuildFlyRoute(vStart,vEnd,pTarget,endFlags,nodeID,buildFlags,flYaw,goalTolerance);
  1211. if (flyRoute)
  1212. {
  1213. return flyRoute;
  1214. }
  1215. }
  1216. // Try a crawl route if NPC can crawl and requested
  1217. if ((buildFlags & bits_BUILD_CRAWL) && (CapabilitiesGet() & bits_CAP_MOVE_CRAWL))
  1218. {
  1219. AI_Waypoint_t *crawlRoute = BuildCrawlRoute(vStart,vEnd,pTarget,endFlags,nodeID,buildFlags,flYaw,goalTolerance);
  1220. if (crawlRoute)
  1221. {
  1222. return crawlRoute;
  1223. }
  1224. }
  1225. // Try a jump route if NPC can jump and requested
  1226. if ((buildFlags & bits_BUILD_JUMP) && (CapabilitiesGet() & bits_CAP_MOVE_JUMP))
  1227. {
  1228. AI_Waypoint_t *jumpRoute = BuildJumpRoute(vStart,vEnd,pTarget,endFlags,nodeID,buildFlags,flYaw);
  1229. if (jumpRoute)
  1230. {
  1231. return jumpRoute;
  1232. }
  1233. }
  1234. // Try a climb route if NPC can climb and requested
  1235. if ((buildFlags & bits_BUILD_CLIMB) && (CapabilitiesGet() & bits_CAP_MOVE_CLIMB))
  1236. {
  1237. AI_Waypoint_t *climbRoute = BuildClimbRoute(vStart,vEnd,pTarget,endFlags,nodeID,buildFlags,flYaw);
  1238. if (climbRoute)
  1239. {
  1240. return climbRoute;
  1241. }
  1242. }
  1243. // Everything failed so return a NULL route
  1244. return NULL;
  1245. }
  1246. //-----------------------------------------------------------------------------
  1247. // Purpose: Builds a route to the given vecGoal using either local movement
  1248. // or nodes
  1249. //-----------------------------------------------------------------------------
  1250. ConVar ai_no_local_paths( "ai_no_local_paths", "0" );
  1251. AI_Waypoint_t *CAI_Pathfinder::BuildRoute( const Vector &vStart, const Vector &vEnd,
  1252. CBaseEntity *pTarget, float goalTolerance, Navigation_t curNavType, int nBuildFlags )
  1253. {
  1254. Assert( ( nBuildFlags & ( bits_BUILD_GROUND | bits_BUILD_JUMP | bits_BUILD_FLY | bits_BUILD_CLIMB |
  1255. bits_BUILD_CRAWL | bits_BUILD_GIVEWAY | bits_BUILD_TRIANG | bits_BUILD_IGNORE_NPCS | bits_BUILD_COLLIDE_NPCS ) ) == 0 );
  1256. nBuildFlags &= ~( bits_BUILD_GROUND | bits_BUILD_JUMP | bits_BUILD_FLY | bits_BUILD_CLIMB |
  1257. bits_BUILD_CRAWL | bits_BUILD_GIVEWAY | bits_BUILD_TRIANG | bits_BUILD_IGNORE_NPCS | bits_BUILD_COLLIDE_NPCS );
  1258. bool bTryLocal = !ai_no_local_paths.GetBool() && ( ( nBuildFlags & bits_BUILD_NO_LOCAL_NAV ) == 0 );
  1259. // Set up build flags
  1260. if (curNavType == NAV_CLIMB)
  1261. {
  1262. // if I'm climbing, then only allow routes that are also climb routes
  1263. nBuildFlags |= bits_BUILD_CLIMB;
  1264. bTryLocal = false;
  1265. }
  1266. else if ( (CapabilitiesGet() & bits_CAP_MOVE_FLY) || (CapabilitiesGet() & bits_CAP_MOVE_SWIM) )
  1267. {
  1268. nBuildFlags |= (bits_BUILD_FLY | bits_BUILD_GIVEWAY | bits_BUILD_TRIANG);
  1269. }
  1270. else if (CapabilitiesGet() & bits_CAP_MOVE_GROUND)
  1271. {
  1272. nBuildFlags |= (bits_BUILD_GROUND | bits_BUILD_GIVEWAY | bits_BUILD_TRIANG);
  1273. if ( CapabilitiesGet() & bits_CAP_MOVE_JUMP )
  1274. {
  1275. nBuildFlags |= bits_BUILD_JUMP;
  1276. }
  1277. if ( CapabilitiesGet() & bits_CAP_MOVE_CRAWL )
  1278. {
  1279. nBuildFlags |= bits_BUILD_CRAWL;
  1280. }
  1281. }
  1282. AI_Waypoint_t *pResult = NULL;
  1283. // First try a local route
  1284. if ( bTryLocal && CanUseLocalNavigation() )
  1285. {
  1286. int nLocalBuildFlags = nBuildFlags;
  1287. if ( CapabilitiesGet() & bits_CAP_NO_LOCAL_NAV_CRAWL )
  1288. {
  1289. nLocalBuildFlags &= ~bits_BUILD_CRAWL;
  1290. }
  1291. pResult = BuildLocalRoute(vStart, vEnd, pTarget,
  1292. bits_WP_TO_GOAL, NO_NODE,
  1293. nLocalBuildFlags, goalTolerance);
  1294. }
  1295. // If the fails, try a node route
  1296. if ( !pResult )
  1297. {
  1298. pResult = BuildNodeRoute( vStart, vEnd, nBuildFlags, goalTolerance );
  1299. }
  1300. m_bIgnoreStaleLinks = false;
  1301. return pResult;
  1302. }
  1303. void CAI_Pathfinder::UnlockRouteNodes( AI_Waypoint_t *pPath )
  1304. {
  1305. CAI_Node *pNode;
  1306. while ( pPath )
  1307. {
  1308. if ( pPath->iNodeID != NO_NODE && ( pNode = GetNetwork()->GetNode(pPath->iNodeID) ) != NULL && pNode->IsLocked() )
  1309. pNode->Unlock();
  1310. pPath = pPath->GetNext();
  1311. }
  1312. }
  1313. //-----------------------------------------------------------------------------
  1314. // Purpose: Attempts to build a radial route around the given center position
  1315. // over a given arc size
  1316. //
  1317. // Input : vStartPos - where route should start from
  1318. // vCenterPos - the center of the arc
  1319. // vGoalPos - ultimate goal position
  1320. // flRadius - radius of the arc
  1321. // flArc - how long should the path be (in degrees)
  1322. // bClockwise - the direction we are heading
  1323. // Output : The route
  1324. //-----------------------------------------------------------------------------
  1325. AI_Waypoint_t *CAI_Pathfinder::BuildRadialRoute( const Vector &vStartPos, const Vector &vCenterPos, const Vector &vGoalPos, float flRadius, float flArc, float flStepDist, bool bClockwise, float goalTolerance, bool bAirRoute /*= false*/ )
  1326. {
  1327. MARK_TASK_EXPENSIVE();
  1328. // ------------------------------------------------------------------------------
  1329. // Make sure we have a minimum distance between nodes. For the given
  1330. // radius, calculate the angular step necessary for this distance.
  1331. // IMPORTANT: flStepDist must be large enough that given the
  1332. // NPC's movment speed that it can come to a stop
  1333. // ------------------------------------------------------------------------------
  1334. float flAngleStep = 2.0f * atan((0.5f*flStepDist)/flRadius);
  1335. // Flip direction if clockwise
  1336. if ( bClockwise )
  1337. {
  1338. flArc *= -1;
  1339. flAngleStep *= -1;
  1340. }
  1341. // Calculate the start angle on the arc in world coordinates
  1342. Vector vStartDir = ( vStartPos - vCenterPos );
  1343. VectorNormalize( vStartDir );
  1344. // Get our control angles
  1345. float flStartAngle = DEG2RAD(UTIL_VecToYaw(vStartDir));
  1346. float flEndAngle = flStartAngle + DEG2RAD(flArc);
  1347. // Offset set our first node by one arc step so NPC doesn't run perpendicular to the arc when starting a different radius
  1348. flStartAngle += flAngleStep;
  1349. AI_Waypoint_t* pHeadRoute = NULL; // Pointer to the beginning of the route chains
  1350. AI_Waypoint_t* pNextRoute = NULL; // Next leg of the route
  1351. AI_Waypoint_t* pLastRoute = NULL; // The last route chain added to the head
  1352. Vector vLastPos = vStartPos; // Last position along the arc in worldspace
  1353. int fRouteBits = ( bAirRoute ) ? bits_BUILD_FLY : bits_BUILD_GROUND; // Whether this is an air route or not
  1354. float flCurAngle = flStartAngle; // Starting angle
  1355. Vector vNextPos;
  1356. // Make sure that we've got somewhere to go. This generally means your trying to walk too small an arc.
  1357. Assert( ( bClockwise && flCurAngle > flEndAngle ) || ( !bClockwise && flCurAngle < flEndAngle ) );
  1358. // Start iterating through our arc
  1359. while( 1 )
  1360. {
  1361. // See if we've ended our run
  1362. if ( ( bClockwise && flCurAngle <= flEndAngle ) || ( !bClockwise && flCurAngle >= flEndAngle ) )
  1363. break;
  1364. // Get our next position along the arc
  1365. vNextPos = vCenterPos;
  1366. vNextPos.x += flRadius * cos( flCurAngle );
  1367. vNextPos.y += flRadius * sin( flCurAngle );
  1368. // Build a route from the last position to the current one
  1369. pNextRoute = BuildLocalRoute( vLastPos, vNextPos, NULL, NULL, NO_NODE, fRouteBits, goalTolerance);
  1370. // If we can't find a route, we failed
  1371. if ( pNextRoute == NULL )
  1372. return NULL;
  1373. // Don't simplify the route (otherwise we'll cut corners where we don't want to!
  1374. pNextRoute->ModifyFlags( bits_WP_DONT_SIMPLIFY, true );
  1375. if ( pHeadRoute )
  1376. {
  1377. // Tack the routes together
  1378. AddWaypointLists( pHeadRoute, pNextRoute );
  1379. }
  1380. else
  1381. {
  1382. // Otherwise we're now the previous route
  1383. pHeadRoute = pNextRoute;
  1384. }
  1385. // Move our position
  1386. vLastPos = vNextPos;
  1387. pLastRoute = pNextRoute;
  1388. // Move our current angle
  1389. flCurAngle += flAngleStep;
  1390. }
  1391. // NOTE: We could also simply build a local route with no curve, but it's unlikely that's what was intended by the caller
  1392. if ( pHeadRoute == NULL )
  1393. return NULL;
  1394. // Append a path to the final position
  1395. pLastRoute = BuildLocalRoute( vLastPos, vGoalPos, NULL, NULL, NO_NODE, bAirRoute ? bits_BUILD_FLY : bits_BUILD_GROUND, goalTolerance );
  1396. if ( pLastRoute == NULL )
  1397. return NULL;
  1398. // Allow us to simplify the last leg of the route
  1399. pLastRoute->ModifyFlags( bits_WP_DONT_SIMPLIFY, false );
  1400. pLastRoute->ModifyFlags( bits_WP_TO_GOAL, true );
  1401. // Add them together
  1402. AddWaypointLists( pHeadRoute, pLastRoute );
  1403. // Give back the complete route
  1404. return pHeadRoute;
  1405. }
  1406. //-----------------------------------------------------------------------------
  1407. // Checks a stale navtype route
  1408. //-----------------------------------------------------------------------------
  1409. bool CAI_Pathfinder::CheckStaleNavTypeRoute( Navigation_t navType, const Vector &vStart, const Vector &vEnd )
  1410. {
  1411. AIMoveTrace_t moveTrace;
  1412. GetOuter()->GetMoveProbe()->MoveLimit( navType, vStart, vEnd, GetOuter()->GetAITraceMask(), NULL, 100, AIMLF_IGNORE_TRANSIENTS, &moveTrace);
  1413. // Is the direct route clear?
  1414. if (!IsMoveBlocked(moveTrace))
  1415. {
  1416. return true;
  1417. }
  1418. // Next try to triangulate
  1419. // FIXME: Since blocked dist is an unreliable number, this computation is bogus
  1420. Vector vecDelta;
  1421. VectorSubtract( vEnd, vStart, vecDelta );
  1422. float flTotalDist = vecDelta.Length();
  1423. Vector vApex;
  1424. if (Triangulate( navType, vStart, vEnd, flTotalDist - moveTrace.flDistObstructed, NULL, &vApex ))
  1425. {
  1426. return true;
  1427. }
  1428. // Try a giveway request, if I can get there ignoring NPCs
  1429. if ( moveTrace.pObstruction && moveTrace.pObstruction->MyNPCPointer() )
  1430. {
  1431. GetOuter()->GetMoveProbe()->MoveLimit( navType, vStart, vEnd, GetOuter()->GetAITraceMask_BrushOnly(), NULL, &moveTrace);
  1432. if (!IsMoveBlocked(moveTrace))
  1433. {
  1434. return true;
  1435. }
  1436. }
  1437. return false;
  1438. }
  1439. //-----------------------------------------------------------------------------
  1440. // Purpose: Checks if a local route (not using nodes) between vStart
  1441. // and vEnd exists using the moveType
  1442. // Input :
  1443. // Output : Returns a route if sucessful or NULL if no local route was possible
  1444. //-----------------------------------------------------------------------------
  1445. bool CAI_Pathfinder::CheckStaleRoute(const Vector &vStart, const Vector &vEnd, int moveTypes)
  1446. {
  1447. AI_PROFILE_SCOPE( CAI_Pathfinder_CheckStaleRoute );
  1448. // -------------------------------------------------------------------
  1449. // First try to go there directly
  1450. // -------------------------------------------------------------------
  1451. if (moveTypes & bits_CAP_MOVE_GROUND)
  1452. {
  1453. if (CheckStaleNavTypeRoute( NAV_GROUND, vStart, vEnd ))
  1454. return true;
  1455. }
  1456. // -------------------------------------------------------------------
  1457. // First try to go there directly
  1458. // -------------------------------------------------------------------
  1459. if (moveTypes & bits_CAP_MOVE_FLY)
  1460. {
  1461. if (CheckStaleNavTypeRoute( NAV_FLY, vStart, vEnd ))
  1462. return true;
  1463. }
  1464. // --------------------------------------------------------------
  1465. // Try to jump if we can jump to a node
  1466. // --------------------------------------------------------------
  1467. if (moveTypes & bits_CAP_MOVE_JUMP)
  1468. {
  1469. AIMoveTrace_t moveTrace;
  1470. GetOuter()->GetMoveProbe()->MoveLimit( NAV_JUMP, vStart, vEnd, GetOuter()->GetAITraceMask(), NULL, &moveTrace);
  1471. if (!IsMoveBlocked(moveTrace))
  1472. {
  1473. return true;
  1474. }
  1475. else
  1476. {
  1477. // Can't tell jump up from jump down at this point
  1478. GetOuter()->GetMoveProbe()->MoveLimit( NAV_JUMP, vEnd, vStart, GetOuter()->GetAITraceMask(), NULL, &moveTrace);
  1479. if (!IsMoveBlocked(moveTrace))
  1480. return true;
  1481. }
  1482. }
  1483. // --------------------------------------------------------------
  1484. // Try to climb if we can climb to a node
  1485. // --------------------------------------------------------------
  1486. if (moveTypes & bits_CAP_MOVE_CLIMB)
  1487. {
  1488. AIMoveTrace_t moveTrace;
  1489. GetOuter()->GetMoveProbe()->MoveLimit( NAV_CLIMB, vStart, vEnd, GetOuter()->GetAITraceMask(), NULL, &moveTrace);
  1490. if (!IsMoveBlocked(moveTrace))
  1491. {
  1492. return true;
  1493. }
  1494. }
  1495. // Man do we suck! Couldn't get there by any route
  1496. return false;
  1497. }
  1498. //-----------------------------------------------------------------------------
  1499. #define MAX_NODE_TRIES 4
  1500. #define MAX_TRIANGULATIONS 2
  1501. class CPathfindNearestNodeFilter : public INearestNodeFilter
  1502. {
  1503. public:
  1504. CPathfindNearestNodeFilter( CAI_Pathfinder *pPathfinder, const Vector &vGoal, bool bToNode, int buildFlags, float goalTolerance, bool bAvoidObstacles )
  1505. : m_pPathfinder( pPathfinder ),
  1506. m_nTries(0),
  1507. m_vGoal( vGoal ),
  1508. m_bToNode( bToNode ),
  1509. m_goalTolerance( goalTolerance ),
  1510. m_moveTypes( buildFlags & ( bits_BUILD_GROUND | bits_BUILD_FLY | bits_BUILD_JUMP | bits_BUILD_CLIMB | bits_BUILD_CRAWL ) ),
  1511. m_bAvoidObstacles( bAvoidObstacles ),
  1512. m_pRoute( NULL )
  1513. {
  1514. COMPILE_TIME_ASSERT( bits_BUILD_GROUND == bits_CAP_MOVE_GROUND && bits_BUILD_FLY == bits_CAP_MOVE_FLY && bits_BUILD_JUMP == bits_CAP_MOVE_JUMP && bits_BUILD_CLIMB == bits_CAP_MOVE_CLIMB && bits_BUILD_CRAWL == bits_CAP_MOVE_CRAWL );
  1515. }
  1516. bool IsValid( CAI_Node *pNode )
  1517. {
  1518. int nStaleLinks = 0;
  1519. if ( !m_pPathfinder->m_bIgnoreStaleLinks )
  1520. {
  1521. int hull = m_pPathfinder->GetOuter()->GetHullType();
  1522. for ( int i = 0; i < pNode->NumLinks(); i++ )
  1523. {
  1524. CAI_Link *pLink = pNode->GetLinkByIndex( i );
  1525. if ( pLink->m_LinkInfo & ( bits_LINK_STALE_SUGGESTED | ( bits_LINK_OFF | bits_LINK_ASW_BASHABLE ) ) )
  1526. {
  1527. nStaleLinks++;
  1528. }
  1529. else if ( ( pLink->m_iAcceptedMoveTypes[hull] & m_moveTypes ) == 0 )
  1530. {
  1531. nStaleLinks++;
  1532. }
  1533. }
  1534. }
  1535. if ( nStaleLinks && nStaleLinks == pNode->NumLinks() )
  1536. return false;
  1537. if ( m_bAvoidObstacles )
  1538. {
  1539. if ( CAI_LocalNavigator::IsSegmentBlockedByGlobalObstacles( pNode->GetOrigin(), m_vGoal ) )
  1540. return false;
  1541. }
  1542. int buildFlags = ( m_nTries < MAX_TRIANGULATIONS ) ? ( bits_BUILD_IGNORE_NPCS | bits_BUILD_TRIANG ) : bits_BUILD_IGNORE_NPCS;
  1543. if ( m_bToNode )
  1544. m_pRoute = m_pPathfinder->RouteToNode( m_vGoal, buildFlags, pNode->GetId(), m_goalTolerance );
  1545. else
  1546. m_pRoute = m_pPathfinder->RouteFromNode( m_vGoal, buildFlags, pNode->GetId(), m_goalTolerance );
  1547. m_nTries++;
  1548. return ( m_pRoute != NULL );
  1549. }
  1550. bool ShouldContinue()
  1551. {
  1552. return ( !m_pRoute && m_nTries < MAX_NODE_TRIES );
  1553. }
  1554. CAI_Pathfinder *m_pPathfinder;
  1555. int m_nTries;
  1556. Vector m_vGoal;
  1557. bool m_bToNode;
  1558. float m_goalTolerance;
  1559. int m_moveTypes;
  1560. bool m_bAvoidObstacles;
  1561. AI_Waypoint_t * m_pRoute;
  1562. };
  1563. AI_Waypoint_t *CAI_Pathfinder::BuildNearestNodeRoute( const Vector &vGoal, bool bToNode, int buildFlags, float goalTolerance, int *pNearestNode )
  1564. {
  1565. AI_PROFILE_SCOPE( CAI_Pathfinder_BuildNearestNodeRoute );
  1566. CPathfindNearestNodeFilter filter( this, vGoal, bToNode, buildFlags, goalTolerance, true );
  1567. *pNearestNode = GetNetwork()->NearestNodeToPoint( GetOuter(), vGoal, true, &filter );
  1568. return filter.m_pRoute;
  1569. }
  1570. //-----------------------------------------------------------------------------
  1571. // Purpose: Attemps to build a node route between vStart and vEnd
  1572. // Input :
  1573. // Output : Returns a route if sucessful or NULL if no node route was possible
  1574. //-----------------------------------------------------------------------------
  1575. AI_Waypoint_t *CAI_Pathfinder::BuildNodeRoute(const Vector &vStart, const Vector &vEnd, int buildFlags, float goalTolerance)
  1576. {
  1577. AI_PROFILE_SCOPE( CAI_Pathfinder_BuildNodeRoute );
  1578. // ----------------------------------------------------------------------
  1579. // Make sure network has nodes
  1580. // ----------------------------------------------------------------------
  1581. if (GetNetwork()->NumNodes() == 0)
  1582. return NULL;
  1583. // ----------------------------------------------------------------------
  1584. // Find the nearest source node
  1585. // ----------------------------------------------------------------------
  1586. int srcID;
  1587. AI_Waypoint_t *srcRoute = BuildNearestNodeRoute( vStart, true, buildFlags, goalTolerance, &srcID );
  1588. if ( !srcRoute )
  1589. {
  1590. DbgNavMsg1( GetOuter(), "Node pathfind failed, no route to source %d\n", srcID );
  1591. return NULL;
  1592. }
  1593. // ----------------------------------------------------------------------
  1594. // Find the nearest destination node
  1595. // ----------------------------------------------------------------------
  1596. int destID;
  1597. AI_Waypoint_t *destRoute = BuildNearestNodeRoute( vEnd, false, buildFlags, goalTolerance, &destID );
  1598. if ( !destRoute )
  1599. {
  1600. DeleteAll( srcRoute );
  1601. DbgNavMsg1( GetOuter(), "Node pathfind failed, no route to dest %d\n", destID );
  1602. return NULL;
  1603. }
  1604. // ----------------------------------------------------------------------
  1605. // If source and destination are the same, we can bypass finding a route
  1606. // ----------------------------------------------------------------------
  1607. if (destID == srcID)
  1608. {
  1609. AddWaypointLists(srcRoute,destRoute);
  1610. DbgNavMsg( GetOuter(), "Node pathfind succeeded: dest == source\n");
  1611. return srcRoute;
  1612. }
  1613. // If nodes are not connected by network graph, no route is possible
  1614. if (!GetNetwork()->IsConnected(srcID, destID))
  1615. return NULL;
  1616. AI_Waypoint_t *path = FindBestPath(srcID, destID);
  1617. if (!path)
  1618. {
  1619. DeleteAll(srcRoute);
  1620. DeleteAll(destRoute);
  1621. DbgNavMsg2( GetOuter(), "Node pathfind failed, no route between %d and %d\n", srcID, destID );
  1622. return NULL;
  1623. }
  1624. // Now put all the pieces together to form our route
  1625. AddWaypointLists(srcRoute,path);
  1626. AddWaypointLists(srcRoute,destRoute);
  1627. DbgNavMsg( GetOuter(), "Node pathfind succeeded\n");
  1628. return srcRoute;
  1629. }
  1630. //-----------------------------------------------------------------------------
  1631. // Test the triangulation route...
  1632. //-----------------------------------------------------------------------------
  1633. #ifdef _WIN32
  1634. #pragma warning (disable:4701)
  1635. #endif
  1636. bool CAI_Pathfinder::TestTriangulationRoute( Navigation_t navType, const Vector& vecStart,
  1637. const Vector &vecApex, const Vector &vecEnd, const CBaseEntity *pTargetEnt, AIMoveTrace_t *pStartTrace )
  1638. {
  1639. AIMoveTrace_t endTrace;
  1640. endTrace.fStatus = AIMR_OK; // just to make the debug overlay code easy
  1641. // Check the triangulation
  1642. CAI_MoveProbe *pMoveProbe = GetOuter()->GetMoveProbe();
  1643. bool bPathClear = false;
  1644. // See if we can get from the start point to the triangle apex
  1645. if ( pMoveProbe->MoveLimit(navType, vecStart, vecApex, GetOuter()->GetAITraceMask(), pTargetEnt, pStartTrace ) )
  1646. {
  1647. // Ok, we got from the start to the triangle apex, now try
  1648. // the triangle apex to the end
  1649. if ( pMoveProbe->MoveLimit(navType, vecApex, vecEnd, GetOuter()->GetAITraceMask(), pTargetEnt, &endTrace ) )
  1650. {
  1651. bPathClear = true;
  1652. }
  1653. }
  1654. // Debug mode: display the tested path...
  1655. if (GetOuter()->m_debugOverlays & OVERLAY_NPC_TRIANGULATE_BIT)
  1656. m_TriDebugOverlay.AddTriOverlayLines( vecStart, vecApex, vecEnd, *pStartTrace, endTrace, bPathClear);
  1657. return bPathClear;
  1658. }
  1659. #ifdef _WIN32
  1660. #pragma warning (default:4701)
  1661. #endif
  1662. //-----------------------------------------------------------------------------
  1663. // Purpose: tries to overcome local obstacles by triangulating a path around them.
  1664. // Input : flDist is is how far the obstruction that we are trying
  1665. // to triangulate around is from the npc
  1666. // Output :
  1667. //-----------------------------------------------------------------------------
  1668. // FIXME: this has no concept that the blocker may not be exactly along the vecStart, vecEnd vector.
  1669. // FIXME: it should take a position (and size?) to avoid
  1670. // FIXME: this does the position checks in the same order as GiveWay() so they tend to fight each other when both are active
  1671. #define MAX_TRIAGULATION_DIST (32*12)
  1672. bool CAI_Pathfinder::Triangulate( Navigation_t navType, const Vector &vecStart, const Vector &vecEndIn,
  1673. float flDistToBlocker, const CBaseEntity *pTargetEnt, Vector *pApex )
  1674. {
  1675. if ( GetOuter()->IsFlaggedEfficient() )
  1676. return false;
  1677. Assert( pApex );
  1678. AI_PROFILE_SCOPE( CAI_Pathfinder_Triangulate );
  1679. Vector vecForward, vecUp, vecPerpendicular;
  1680. VectorSubtract( vecEndIn, vecStart, vecForward );
  1681. float flTotalDist = VectorNormalize( vecForward );
  1682. Vector vecEnd;
  1683. // If we're walking, then don't try to triangulate over large distances
  1684. if ( navType != NAV_FLY && flTotalDist > MAX_TRIAGULATION_DIST)
  1685. {
  1686. vecEnd = vecForward * MAX_TRIAGULATION_DIST;
  1687. flTotalDist = MAX_TRIAGULATION_DIST;
  1688. if ( !GetOuter()->GetMoveProbe()->MoveLimit(navType, vecEnd, vecEndIn, GetOuter()->GetAITraceMask(), pTargetEnt) )
  1689. {
  1690. return false;
  1691. }
  1692. }
  1693. else
  1694. vecEnd = vecEndIn;
  1695. // Compute a direction vector perpendicular to the desired motion direction
  1696. if ( 1.0f - fabs(vecForward.z) > 1e-3 )
  1697. {
  1698. vecUp.Init( 0, 0, 1 );
  1699. CrossProduct( vecForward, vecUp, vecPerpendicular ); // Orthogonal to facing
  1700. }
  1701. else
  1702. {
  1703. vecUp.Init( 0, 1, 0 );
  1704. vecPerpendicular.Init( 1, 0, 0 );
  1705. }
  1706. // Grab the size of the navigation bounding box
  1707. float sizeX = 0.5f * NAI_Hull::Length(GetHullType());
  1708. float sizeZ = 0.5f * NAI_Hull::Height(GetHullType());
  1709. // start checking right about where the object is, picking two equidistant
  1710. // starting points, one on the left, one on the right. As we progress
  1711. // through the loop, we'll push these away from the obstacle, hoping to
  1712. // find a way around on either side. m_vecSize.x is added to the ApexDist
  1713. // in order to help select an apex point that insures that the NPC is
  1714. // sufficiently past the obstacle before trying to turn back onto its original course.
  1715. if (GetOuter()->m_debugOverlays & OVERLAY_NPC_TRIANGULATE_BIT)
  1716. {
  1717. m_TriDebugOverlay.FadeTriOverlayLines();
  1718. }
  1719. float flApexDist = flDistToBlocker + sizeX;
  1720. if (flApexDist > flTotalDist)
  1721. {
  1722. flApexDist = flTotalDist;
  1723. }
  1724. // Compute triangulation apex points (NAV_FLY attempts vertical triangulation too)
  1725. Vector vecDelta[2];
  1726. Vector vecApex[4];
  1727. float pApexDist[4];
  1728. Vector vecCenter;
  1729. int nNumToTest = 2;
  1730. VectorMultiply( vecPerpendicular, sizeX, vecDelta[0] );
  1731. VectorMA( vecStart, flApexDist, vecForward, vecCenter );
  1732. VectorSubtract( vecCenter, vecDelta[0], vecApex[0] );
  1733. VectorAdd( vecCenter, vecDelta[0], vecApex[1] );
  1734. vecDelta[0] *= 2.0f;
  1735. pApexDist[0] = pApexDist[1] = flApexDist;
  1736. if (navType == NAV_FLY)
  1737. {
  1738. VectorMultiply( vecUp, 3.0f * sizeZ, vecDelta[1] );
  1739. VectorSubtract( vecCenter, vecDelta[1], vecApex[2] );
  1740. VectorAdd( vecCenter, vecDelta[1], vecApex[3] );
  1741. pApexDist[2] = pApexDist[3] = flApexDist;
  1742. nNumToTest = 4;
  1743. }
  1744. AIMoveTrace_t moveTrace;
  1745. for (int i = 0; i < 2; ++i )
  1746. {
  1747. // NOTE: Do reverse order so fliers try to move over the top first
  1748. for (int j = nNumToTest; --j >= 0; )
  1749. {
  1750. if (TestTriangulationRoute(navType, vecStart, vecApex[j], vecEnd, pTargetEnt, &moveTrace))
  1751. {
  1752. *pApex = vecApex[j];
  1753. return true;
  1754. }
  1755. // Here, the starting half of the triangle was blocked. Lets
  1756. // pull back the apex toward the start...
  1757. if (IsMoveBlocked(moveTrace))
  1758. {
  1759. Vector vecStartToObstruction;
  1760. VectorSubtract( moveTrace.vEndPosition, vecStart, vecStartToObstruction );
  1761. float flDistToObstruction = DotProduct( vecStartToObstruction, vecForward );
  1762. float flNewApexDist = pApexDist[j];
  1763. if (pApexDist[j] > flDistToObstruction)
  1764. flNewApexDist = flDistToObstruction;
  1765. VectorMA( vecApex[j], flNewApexDist - pApexDist[j], vecForward, vecApex[j] );
  1766. pApexDist[j] = flNewApexDist;
  1767. }
  1768. // NOTE: This has to occur *after* the code above because
  1769. // the above code uses vecApex for some distance computations
  1770. if (j & 0x1)
  1771. vecApex[j] += vecDelta[j >> 1];
  1772. else
  1773. vecApex[j] -= vecDelta[j >> 1];
  1774. }
  1775. }
  1776. return false;
  1777. }
  1778. //-----------------------------------------------------------------------------
  1779. // Purpose: Triangulation debugging
  1780. //-----------------------------------------------------------------------------
  1781. void CAI_Pathfinder::DrawDebugGeometryOverlays(int npcDebugOverlays)
  1782. {
  1783. m_TriDebugOverlay.Draw(npcDebugOverlays);
  1784. }
  1785. void CAI_Pathfinder::CTriDebugOverlay::Draw(int npcDebugOverlays)
  1786. {
  1787. if (m_debugTriOverlayLine)
  1788. {
  1789. if ( npcDebugOverlays & OVERLAY_NPC_TRIANGULATE_BIT)
  1790. {
  1791. for (int i=0;i<NUM_NPC_DEBUG_OVERLAYS;i++)
  1792. {
  1793. if (m_debugTriOverlayLine[i]->draw)
  1794. {
  1795. NDebugOverlay::Line(m_debugTriOverlayLine[i]->origin,
  1796. m_debugTriOverlayLine[i]->dest,
  1797. m_debugTriOverlayLine[i]->r,
  1798. m_debugTriOverlayLine[i]->g,
  1799. m_debugTriOverlayLine[i]->b,
  1800. m_debugTriOverlayLine[i]->noDepthTest,
  1801. 0);
  1802. }
  1803. }
  1804. }
  1805. else
  1806. {
  1807. ClearTriOverlayLines();
  1808. }
  1809. }
  1810. }
  1811. void CAI_Pathfinder::CTriDebugOverlay::AddTriOverlayLines( const Vector &vecStart, const Vector &vecApex, const Vector &vecEnd, const AIMoveTrace_t &startTrace, const AIMoveTrace_t &endTrace, bool bPathClear )
  1812. {
  1813. static unsigned char s_TriangulationColor[2][3] =
  1814. {
  1815. { 255, 0, 0 },
  1816. { 0, 255, 0 }
  1817. };
  1818. unsigned char *c = s_TriangulationColor[bPathClear];
  1819. AddTriOverlayLine(vecStart, vecApex, c[0],c[1],c[2], false);
  1820. AddTriOverlayLine(vecApex, vecEnd, c[0],c[1],c[2], false);
  1821. // If we've blocked, draw an X where we were blocked...
  1822. if (IsMoveBlocked(startTrace.fStatus))
  1823. {
  1824. Vector pt1, pt2;
  1825. pt1 = pt2 = startTrace.vEndPosition;
  1826. pt1.x -= 10; pt1.y -= 10;
  1827. pt2.x += 10; pt2.y += 10;
  1828. AddTriOverlayLine(pt1, pt2, c[0],c[1],c[2], false);
  1829. pt1.x += 20;
  1830. pt2.x -= 20;
  1831. AddTriOverlayLine(pt1, pt2, c[0],c[1],c[2], false);
  1832. }
  1833. if (IsMoveBlocked(endTrace.fStatus))
  1834. {
  1835. Vector pt1, pt2;
  1836. pt1 = pt2 = endTrace.vEndPosition;
  1837. pt1.x -= 10; pt1.y -= 10;
  1838. pt2.x += 10; pt2.y += 10;
  1839. AddTriOverlayLine(pt1, pt2, c[0],c[1],c[2], false);
  1840. pt1.x += 20;
  1841. pt2.x -= 20;
  1842. AddTriOverlayLine(pt1, pt2, c[0],c[1],c[2], false);
  1843. }
  1844. }
  1845. void CAI_Pathfinder::CTriDebugOverlay::ClearTriOverlayLines(void)
  1846. {
  1847. if (m_debugTriOverlayLine)
  1848. {
  1849. for (int i=0;i<NUM_NPC_DEBUG_OVERLAYS;i++)
  1850. {
  1851. m_debugTriOverlayLine[i]->draw = false;
  1852. }
  1853. }
  1854. }
  1855. void CAI_Pathfinder::CTriDebugOverlay::FadeTriOverlayLines(void)
  1856. {
  1857. if (m_debugTriOverlayLine)
  1858. {
  1859. for (int i=0;i<NUM_NPC_DEBUG_OVERLAYS;i++)
  1860. {
  1861. m_debugTriOverlayLine[i]->r *= 0.5;
  1862. m_debugTriOverlayLine[i]->g *= 0.5;
  1863. m_debugTriOverlayLine[i]->b *= 0.5;
  1864. }
  1865. }
  1866. }
  1867. void CAI_Pathfinder::CTriDebugOverlay::AddTriOverlayLine(const Vector &origin, const Vector &dest, int r, int g, int b, bool noDepthTest)
  1868. {
  1869. if (!m_debugTriOverlayLine)
  1870. {
  1871. m_debugTriOverlayLine = new OverlayLine_t*[NUM_NPC_DEBUG_OVERLAYS];
  1872. for (int i=0;i<NUM_NPC_DEBUG_OVERLAYS;i++)
  1873. {
  1874. m_debugTriOverlayLine[i] = new OverlayLine_t;
  1875. }
  1876. }
  1877. static int overCounter = 0;
  1878. if (overCounter >= NUM_NPC_DEBUG_OVERLAYS)
  1879. {
  1880. overCounter = 0;
  1881. }
  1882. m_debugTriOverlayLine[overCounter]->origin = origin;
  1883. m_debugTriOverlayLine[overCounter]->dest = dest;
  1884. m_debugTriOverlayLine[overCounter]->r = r;
  1885. m_debugTriOverlayLine[overCounter]->g = g;
  1886. m_debugTriOverlayLine[overCounter]->b = b;
  1887. m_debugTriOverlayLine[overCounter]->noDepthTest = noDepthTest;
  1888. m_debugTriOverlayLine[overCounter]->draw = true;
  1889. overCounter++;
  1890. }
  1891. //-----------------------------------------------------------------------------