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.

2137 lines
69 KiB

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