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.

743 lines
22 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Base combat character with no AI
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. // $NoKeywords: $
  8. //=============================================================================//
  9. #include "cbase.h"
  10. #include "ai_network.h"
  11. #include "ai_node.h"
  12. #include "ai_basenpc.h"
  13. #include "ai_link.h"
  14. #include "ai_navigator.h"
  15. #include "world.h"
  16. #include "ai_moveprobe.h"
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include "tier0/memdbgon.h"
  19. ConVar ai_no_node_cache( "ai_no_node_cache", "0" );
  20. extern float MOVE_HEIGHT_EPSILON;
  21. //-----------------------------------------------------------------------------
  22. //-----------------------------------------------------------------------------
  23. // For now we just have one AINetwork called "BigNet". At some
  24. // later point we will probabaly have multiple AINetworkds per level
  25. CAI_Network* g_pBigAINet;
  26. //-----------------------------------------------------------------------------
  27. abstract_class INodeListFilter
  28. {
  29. public:
  30. virtual bool NodeIsValid( CAI_Node &node ) = 0;
  31. virtual float NodeDistanceSqr( CAI_Node &node ) = 0;
  32. };
  33. //-------------------------------------
  34. // Purpose: Filters nodes for an NPC
  35. //-------------------------------------
  36. class CNodeFilter : public INodeListFilter
  37. {
  38. public:
  39. CNodeFilter( CAI_BaseNPC *pNPC, const Vector &pos ) : m_pNPC(pNPC), m_pos(pos)
  40. {
  41. if ( m_pNPC )
  42. m_capabilities = m_pNPC->CapabilitiesGet();
  43. }
  44. CNodeFilter( const Vector &pos ) : m_pNPC(NULL), m_pos(pos)
  45. {
  46. }
  47. virtual bool NodeIsValid( CAI_Node &node )
  48. {
  49. if ( node.GetType() == NODE_DELETED )
  50. return false;
  51. if ( !m_pNPC )
  52. return true;
  53. if ( m_pNPC->GetNavType() == NAV_FLY && node.GetType() != NODE_AIR )
  54. return false;
  55. // Check that node is of proper type for this NPC's navigation ability
  56. if ((node.GetType() == NODE_AIR && !(m_capabilities & bits_CAP_MOVE_FLY)) ||
  57. (node.GetType() == NODE_GROUND && !(m_capabilities & bits_CAP_MOVE_GROUND)) )
  58. return false;
  59. if ( m_pNPC->IsUnusableNode( node.GetId(), node.GetHint() ) )
  60. return false;
  61. return true;
  62. }
  63. virtual float NodeDistanceSqr( CAI_Node &node )
  64. {
  65. // UNDONE: This call to Position() really seems excessive here. What is the real
  66. // error % relative to 800 units (MAX_NODE_LINK_DIST) ?
  67. if ( m_pNPC )
  68. return (node.GetPosition(m_pNPC->GetHullType()) - m_pos).LengthSqr();
  69. else
  70. return (node.GetOrigin() - m_pos).LengthSqr();
  71. }
  72. const Vector &m_pos;
  73. CAI_BaseNPC *m_pNPC;
  74. int m_capabilities; // cache this
  75. };
  76. //-----------------------------------------------------------------------------
  77. // CAI_Network
  78. //-----------------------------------------------------------------------------
  79. // for searching for the nearest node
  80. // PERFORMANCE: Tune this number
  81. #define MAX_NEAR_NODES 10 // Trace to 10 nodes at most
  82. //-----------------------------------------------------------------------------
  83. CAI_Network::CAI_Network()
  84. {
  85. m_iNumNodes = 0; // Number of nodes in this network
  86. m_pAInode = NULL; // Array of all nodes in this network
  87. m_iNearestCacheNext = NEARNODE_CACHE_SIZE - 1;
  88. // Force empty node caches to be rebuild
  89. for (int node=0;node<NEARNODE_CACHE_SIZE;node++)
  90. {
  91. m_NearestCache[node].hull = HULL_NONE;
  92. m_NearestCache[node].expiration = FLT_MIN;
  93. }
  94. #ifdef AI_NODE_TREE
  95. m_pNodeTree = NULL;
  96. #endif
  97. }
  98. //-----------------------------------------------------------------------------
  99. CAI_Network::~CAI_Network()
  100. {
  101. #ifdef AI_NODE_TREE
  102. if ( m_pNodeTree )
  103. {
  104. engine->DestroySpatialPartition( m_pNodeTree );
  105. m_pNodeTree = NULL;
  106. }
  107. #endif
  108. if ( m_pAInode )
  109. {
  110. for ( int node = 0; node < m_iNumNodes; node++ )
  111. {
  112. CAI_Node *pNode = m_pAInode[node];
  113. Assert( pNode && pNode->m_iID == node );
  114. for ( int link = 0; link < pNode->NumLinks(); link++ )
  115. {
  116. CAI_Link *pLink = pNode->m_Links[link];
  117. if ( pLink )
  118. {
  119. int destID = pLink->DestNodeID( node );
  120. Assert( destID > node && destID < m_iNumNodes );
  121. if ( destID > node && destID < m_iNumNodes )
  122. {
  123. CAI_Node *pDestNode = m_pAInode[destID];
  124. Assert( pDestNode );
  125. for ( int destLink = 0; destLink < pDestNode->NumLinks(); destLink++ )
  126. {
  127. if ( pDestNode->m_Links[destLink] == pLink )
  128. {
  129. pDestNode->m_Links[destLink] = NULL;
  130. }
  131. }
  132. }
  133. delete pLink;
  134. }
  135. }
  136. delete pNode;
  137. }
  138. }
  139. delete[] m_pAInode;
  140. m_pAInode = NULL;
  141. }
  142. //-----------------------------------------------------------------------------
  143. // Purpose: Given an bitString and float array of size array_size, return the
  144. // index of the smallest number in the array whose it is set
  145. //-----------------------------------------------------------------------------
  146. int CAI_Network::FindBSSmallest(CVarBitVec *bitString, float *float_array, int array_size)
  147. {
  148. int winIndex = -1;
  149. float winSize = FLT_MAX;
  150. for (int i=0;i<array_size;i++)
  151. {
  152. if (bitString->IsBitSet(i) && (float_array[i]<winSize))
  153. {
  154. winIndex = i;
  155. winSize = float_array[i];
  156. }
  157. }
  158. return winIndex;
  159. }
  160. //-----------------------------------------------------------------------------
  161. // Purpose: Build a list of nearby nodes sorted by distance
  162. // Input : &list -
  163. // maxListCount -
  164. // *pFilter -
  165. // Output : int - count of list
  166. //-----------------------------------------------------------------------------
  167. int CAI_Network::ListNodesInBox( CNodeList &list, int maxListCount, const Vector &mins, const Vector &maxs, INodeListFilter *pFilter )
  168. {
  169. CNodeList result;
  170. result.SetLessFunc( CNodeList::RevIsLowerPriority );
  171. // NOTE: maxListCount must be > 0 or this will crash
  172. bool full = false;
  173. float flClosest = 1000000.0 * 1000000;
  174. int closest = 0;
  175. // UNDONE: Store the nodes in a tree and query the tree instead of the entire list!!!
  176. for ( int node = 0; node < m_iNumNodes; node++ )
  177. {
  178. CAI_Node *pNode = m_pAInode[node];
  179. const Vector &origin = pNode->GetOrigin();
  180. // in box?
  181. if ( origin.x < mins.x || origin.x > maxs.x ||
  182. origin.y < mins.y || origin.y > maxs.y ||
  183. origin.z < mins.z || origin.z > maxs.z )
  184. continue;
  185. if ( !pFilter->NodeIsValid(*pNode) )
  186. continue;
  187. float flDist = pFilter->NodeDistanceSqr(*pNode);
  188. if ( flDist < flClosest )
  189. {
  190. closest = node;
  191. flClosest = flDist;
  192. }
  193. if ( !full || (flDist < result.ElementAtHead().dist) )
  194. {
  195. if ( full )
  196. result.RemoveAtHead();
  197. result.Insert( AI_NearNode_t(node, flDist) );
  198. full = (result.Count() == maxListCount);
  199. }
  200. }
  201. list.RemoveAll();
  202. while ( result.Count() )
  203. {
  204. list.Insert( result.ElementAtHead() );
  205. result.RemoveAtHead();
  206. }
  207. return list.Count();
  208. }
  209. //-----------------------------------------------------------------------------
  210. // Purpose: Return ID of node nearest of vecOrigin for pNPC with the given
  211. // tolerance distance. If a route is required to get to the node
  212. // node_route is set.
  213. //-----------------------------------------------------------------------------
  214. int CAI_Network::NearestNodeToPoint( CAI_BaseNPC *pNPC, const Vector &vecOrigin, bool bCheckVisibility, INearestNodeFilter *pFilter )
  215. {
  216. AI_PROFILE_SCOPE( CAI_Network_NearestNodeToNPCAtPoint );
  217. // --------------------------------
  218. // Check if network has no nodes
  219. // --------------------------------
  220. if (m_iNumNodes == 0)
  221. return NO_NODE;
  222. // ----------------------------------------------------------------
  223. // First check cached nearest node positions
  224. // ----------------------------------------------------------------
  225. int cachePos;
  226. int cachedNode = GetCachedNearestNode( vecOrigin, pNPC, &cachePos );
  227. if ( cachedNode != NO_NODE )
  228. {
  229. if ( bCheckVisibility )
  230. {
  231. trace_t tr;
  232. Vector vTestLoc = ( pNPC ) ?
  233. m_pAInode[cachedNode]->GetPosition(pNPC->GetHullType()) + pNPC->GetViewOffset() :
  234. m_pAInode[cachedNode]->GetOrigin();
  235. CTraceFilterNav traceFilter( pNPC, true, pNPC, COLLISION_GROUP_NONE );
  236. AI_TraceLine ( vecOrigin, vTestLoc, pNPC ? pNPC->GetAITraceMask_BrushOnly() : MASK_NPCSOLID_BRUSHONLY, &traceFilter, &tr );
  237. if ( tr.fraction != 1.0 )
  238. cachedNode = NO_NODE;
  239. }
  240. if ( cachedNode != NO_NODE && ( !pFilter || pFilter->IsValid( m_pAInode[cachedNode] ) ) )
  241. {
  242. m_NearestCache[cachePos].expiration = gpGlobals->curtime + NEARNODE_CACHE_LIFE;
  243. return cachedNode;
  244. }
  245. }
  246. // ---------------------------------------------------------------
  247. // First get nodes distances and eliminate those that are beyond
  248. // the maximum allowed distance for local movements
  249. // ---------------------------------------------------------------
  250. CNodeFilter filter( pNPC, vecOrigin );
  251. #ifdef AI_PERF_MON
  252. m_nPerfStatNN++;
  253. #endif
  254. AI_NearNode_t *pBuffer = (AI_NearNode_t *)stackalloc( sizeof(AI_NearNode_t) * MAX_NEAR_NODES );
  255. CNodeList list( pBuffer, MAX_NEAR_NODES );
  256. // OPTIMIZE: If not flying, this box should be smaller in Z (2 * height?)
  257. Vector ext(MAX_NODE_LINK_DIST, MAX_NODE_LINK_DIST, MAX_NODE_LINK_DIST);
  258. // If the NPC can fly, check further
  259. if ( pNPC && ( pNPC->CapabilitiesGet() & bits_CAP_MOVE_FLY ) )
  260. {
  261. ext.Init( MAX_AIR_NODE_LINK_DIST, MAX_AIR_NODE_LINK_DIST, MAX_AIR_NODE_LINK_DIST );
  262. }
  263. ListNodesInBox( list, MAX_NEAR_NODES, vecOrigin - ext, vecOrigin + ext, &filter );
  264. // --------------------------------------------------------------
  265. // Now find a reachable node searching the close nodes first
  266. // --------------------------------------------------------------
  267. //int smallestVisibleID = NO_NODE;
  268. for( ;list.Count(); list.RemoveAtHead() )
  269. {
  270. int smallest = list.ElementAtHead().nodeIndex;
  271. // Check not already rejected above
  272. if ( smallest == cachedNode )
  273. continue;
  274. // Check that this node is usable by the current hull size
  275. if ( pNPC && !pNPC->GetNavigator()->CanFitAtNode(smallest))
  276. continue;
  277. if ( bCheckVisibility )
  278. {
  279. trace_t tr;
  280. Vector vTestLoc = ( pNPC ) ?
  281. m_pAInode[smallest]->GetPosition(pNPC->GetHullType()) + pNPC->GetNodeViewOffset() :
  282. m_pAInode[smallest]->GetOrigin();
  283. Vector vecVisOrigin = vecOrigin + Vector(0,0,1);
  284. CTraceFilterNav traceFilter( pNPC, true, pNPC, COLLISION_GROUP_NONE );
  285. AI_TraceLine ( vecVisOrigin, vTestLoc, pNPC ? pNPC->GetAITraceMask_BrushOnly() : MASK_NPCSOLID_BRUSHONLY, &traceFilter, &tr );
  286. if ( tr.fraction != 1.0 )
  287. continue;
  288. }
  289. if ( pFilter )
  290. {
  291. if ( !pFilter->IsValid( m_pAInode[smallest] ) )
  292. {
  293. if ( !pFilter->ShouldContinue() )
  294. break;
  295. continue;
  296. }
  297. }
  298. SetCachedNearestNode( vecOrigin, smallest, (pNPC) ? pNPC->GetHullType() : HULL_NONE );
  299. return smallest;
  300. }
  301. // Store inability to reach in cache for later use
  302. SetCachedNearestNode( vecOrigin, NO_NODE, (pNPC) ? pNPC->GetHullType() : HULL_NONE );
  303. return NO_NODE;
  304. }
  305. //-----------------------------------------------------------------------------
  306. // Purpose: Find the nearest node to an entity without regard to how whether
  307. // the node can be reached
  308. //-----------------------------------------------------------------------------
  309. int CAI_Network::NearestNodeToPoint(const Vector &vPosition, bool bCheckVisibility )
  310. {
  311. return NearestNodeToPoint( NULL, vPosition, bCheckVisibility );
  312. }
  313. //-----------------------------------------------------------------------------
  314. // Purpose: Check nearest node cache for checkPos and return cached nearest
  315. // node if it exists in the cache. Doesn't care about reachability,
  316. // only if the node is visible
  317. //-----------------------------------------------------------------------------
  318. int CAI_Network::GetCachedNode(const Vector &checkPos, Hull_t nHull, int *pCachePos )
  319. {
  320. if ( ai_no_node_cache.GetBool() )
  321. return NOT_CACHED;
  322. // Walk from newest to oldest.
  323. int iNewest = m_iNearestCacheNext + 1;
  324. for ( int i = 0; i < NEARNODE_CACHE_SIZE; i++ )
  325. {
  326. int iCurrent = ( iNewest + i ) % NEARNODE_CACHE_SIZE;
  327. if ( m_NearestCache[iCurrent].hull == nHull && m_NearestCache[iCurrent].expiration > gpGlobals->curtime )
  328. {
  329. if ( (m_NearestCache[iCurrent].vTestPosition - checkPos).LengthSqr() < Square(24.0) )
  330. {
  331. if ( pCachePos )
  332. *pCachePos = iCurrent;
  333. return m_NearestCache[iCurrent].node;
  334. }
  335. }
  336. }
  337. if ( pCachePos )
  338. *pCachePos = -1;
  339. return NOT_CACHED;
  340. }
  341. //-----------------------------------------------------------------------------
  342. int CAI_Network::GetCachedNearestNode(const Vector &checkPos, CAI_BaseNPC *pNPC, int *pCachePos )
  343. {
  344. if ( pNPC )
  345. {
  346. CNodeFilter filter( pNPC, checkPos );
  347. int nodeID = GetCachedNode( checkPos, pNPC->GetHullType(), pCachePos );
  348. if ( nodeID >= 0 )
  349. {
  350. if ( filter.NodeIsValid( *m_pAInode[nodeID] ) && pNPC->GetNavigator()->CanFitAtNode(nodeID) )
  351. return nodeID;
  352. }
  353. }
  354. return NO_NODE;
  355. }
  356. //-----------------------------------------------------------------------------
  357. // Purpose: Update nearest node cache with new data
  358. // if nHull == HULL_NONE, reachability of this node wasn't checked
  359. //-----------------------------------------------------------------------------
  360. void CAI_Network::SetCachedNearestNode(const Vector &checkPos, int nodeID, Hull_t nHull)
  361. {
  362. if ( ai_no_node_cache.GetBool() )
  363. return;
  364. m_NearestCache[m_iNearestCacheNext].vTestPosition = checkPos;
  365. m_NearestCache[m_iNearestCacheNext].node = nodeID;
  366. m_NearestCache[m_iNearestCacheNext].hull = nHull;
  367. m_NearestCache[m_iNearestCacheNext].expiration = gpGlobals->curtime + NEARNODE_CACHE_LIFE;
  368. m_iNearestCacheNext--;
  369. if ( m_iNearestCacheNext < 0 )
  370. {
  371. m_iNearestCacheNext = NEARNODE_CACHE_SIZE - 1;
  372. }
  373. }
  374. //-----------------------------------------------------------------------------
  375. Vector CAI_Network::GetNodePosition( Hull_t hull, int nodeID )
  376. {
  377. if ( !m_pAInode )
  378. {
  379. Assert( 0 );
  380. return vec3_origin;
  381. }
  382. if ( ( nodeID < 0 ) || ( nodeID > m_iNumNodes ) )
  383. {
  384. Assert( 0 );
  385. return vec3_origin;
  386. }
  387. return m_pAInode[nodeID]->GetPosition( hull );
  388. }
  389. //-----------------------------------------------------------------------------
  390. Vector CAI_Network::GetNodePosition( CBaseCombatCharacter *pNPC, int nodeID )
  391. {
  392. if ( pNPC == NULL )
  393. {
  394. Assert( 0 );
  395. return vec3_origin;
  396. }
  397. return GetNodePosition( pNPC->GetHullType(), nodeID );
  398. }
  399. //-----------------------------------------------------------------------------
  400. float CAI_Network::GetNodeYaw( int nodeID )
  401. {
  402. if ( !m_pAInode )
  403. {
  404. Assert( 0 );
  405. return 0.0f;
  406. }
  407. if ( ( nodeID < 0 ) || ( nodeID > m_iNumNodes ) )
  408. {
  409. Assert( 0 );
  410. return 0.0f;
  411. }
  412. return m_pAInode[nodeID]->GetYaw();
  413. }
  414. //-----------------------------------------------------------------------------
  415. // Purpose: Adds an ainode to the network
  416. //-----------------------------------------------------------------------------
  417. CAI_Node *CAI_Network::AddNode( const Vector &origin, float yaw )
  418. {
  419. // ---------------------------------------------------------------------
  420. // When loaded from a file will know the number of nodes from
  421. // the start. Otherwise init MAX_NODES of them if not initialized
  422. // ---------------------------------------------------------------------
  423. if (!m_pAInode || !m_pAInode[0])
  424. {
  425. if ( m_pAInode )
  426. delete [] m_pAInode;
  427. m_pAInode = new CAI_Node*[MAX_NODES];
  428. }
  429. if (m_iNumNodes >= MAX_NODES)
  430. {
  431. DevMsg( "ERROR: too many nodes in map, deleting last node.\n" );
  432. m_iNumNodes--;
  433. }
  434. m_pAInode[m_iNumNodes] = new CAI_Node( m_iNumNodes, origin, yaw );
  435. #ifdef AI_NODE_TREE
  436. if ( !m_pNodeTree )
  437. {
  438. Vector worldMins, worldMaxs;
  439. GetWorldEntity()->GetWorldBounds( worldMins, worldMaxs );
  440. m_pNodeTree = engine->CreateSpatialPartition( worldMins, worldMaxs );
  441. }
  442. m_pNodeTree->CreateHandle( (IHandleEntity *)m_iNumNodes, PARTITION_NODE, origin, origin ); // ignore result for now as we'll never move or remove
  443. #endif
  444. m_iNumNodes++;
  445. return m_pAInode[m_iNumNodes-1];
  446. };
  447. //-----------------------------------------------------------------------------
  448. //-----------------------------------------------------------------------------
  449. CAI_Link *CAI_Network::CreateLink( int srcID, int destID, CAI_DynamicLink *pDynamicLink )
  450. {
  451. CAI_Node *pSrcNode = GetNode( srcID );
  452. CAI_Node *pDestNode = GetNode( destID );
  453. Assert( pSrcNode && pDestNode && pSrcNode != pDestNode );
  454. if ( !pSrcNode || !pDestNode )
  455. {
  456. DevMsg( "Attempted to create link to node that doesn't exist\n" );
  457. return NULL;
  458. }
  459. if ( pSrcNode == pDestNode )
  460. {
  461. DevMsg( "Attempted to link a node to itself\n" );
  462. return NULL;
  463. }
  464. if ( pSrcNode->NumLinks() == AI_MAX_NODE_LINKS )
  465. {
  466. DevMsg( "Node %d has too many links\n", srcID );
  467. return NULL;
  468. }
  469. if ( pDestNode->NumLinks() == AI_MAX_NODE_LINKS )
  470. {
  471. DevMsg( "Node %d has too many links\n", destID );
  472. return NULL;
  473. }
  474. CAI_Link *pLink = new CAI_Link;
  475. pLink->m_iSrcID = srcID;
  476. pLink->m_iDestID = destID;
  477. pLink->m_pDynamicLink = pDynamicLink;
  478. pSrcNode->AddLink(pLink);
  479. pDestNode->AddLink(pLink);
  480. return pLink;
  481. }
  482. //-----------------------------------------------------------------------------
  483. // Purpose: Returns true is two nodes are connected by the network graph
  484. //-----------------------------------------------------------------------------
  485. bool CAI_Network::IsConnected(int srcID, int destID)
  486. {
  487. if (srcID > m_iNumNodes || destID > m_iNumNodes)
  488. {
  489. DevMsg("IsConnected called with invalid node IDs!\n");
  490. return false;
  491. }
  492. if ( srcID == destID )
  493. return true;
  494. int srcZone = m_pAInode[srcID]->GetZone();
  495. int destZone = m_pAInode[destID]->GetZone();
  496. if ( srcZone == AI_NODE_ZONE_SOLO || destZone == AI_NODE_ZONE_SOLO )
  497. return false;
  498. if ( srcZone == AI_NODE_ZONE_UNIVERSAL || destZone == AI_NODE_ZONE_UNIVERSAL ) // only happens in WC edit case
  499. return true;
  500. #ifdef DEBUG
  501. if ( srcZone == AI_NODE_ZONE_UNKNOWN || destZone == AI_NODE_ZONE_UNKNOWN )
  502. {
  503. DevMsg( "Warning: Node found in unknown zone\n" );
  504. return true;
  505. }
  506. #endif
  507. return ( srcZone == destZone );
  508. }
  509. //-----------------------------------------------------------------------------
  510. IterationRetval_t CAI_Network::EnumElement( IHandleEntity *pHandleEntity )
  511. {
  512. #ifdef AI_NODE_TREE
  513. m_GatheredNodes.AddToTail( (int)pHandleEntity );
  514. #endif
  515. return ITERATION_CONTINUE;
  516. }
  517. ConVar ai_nav_debug_experimental_pathing( "ai_nav_debug_experimental_pathing", "0", FCVAR_NONE, "Draw paths tried during search for bodysnatcher pathing" );
  518. // Experimental: starting at a given nav-node, walk the network to try to find a node at least
  519. // mindist away from a point yet no more than maxdist. Returns NULL on failure. Recursive.
  520. // supply squares of min, max distance.
  521. // This algorithm only looks at routes with monotonically increasing distance -- it'll fail to
  522. // find any that involve switchbacks, but on the other hand this avoids needing any additional
  523. // statekeeping to follow cycles. It does tend to hit the same node twice from different origins.
  524. // @TODO: replace 1/sqrt with hardware reciprocal square root
  525. struct AiNetwork_FindNode_linkpair
  526. {
  527. CAI_Node *node; ///< destination node
  528. float distFromStartSq; ///< dot product
  529. inline AiNetwork_FindNode_linkpair(CAI_Node *_node, float _dist) : node(_node), distFromStartSq(_dist) {};
  530. };
  531. CAI_Node *CAI_Network::FindNodeDistanceAwayFromStart( CAI_Node * RESTRICT pStartNode, const Vector &point, float minDistSq, float maxDistSq, const Hull_t hulltype, const Capability_t movetype, const IPathingNodeValidator &validator ) RESTRICT
  532. {
  533. VPROF("CAI_Network::FindNodeDistanceAwayFromStart()");
  534. AssertMsg( pStartNode, "FindNodeDistanceAwayFromStart called with NULL start\n" );
  535. if ( pStartNode == NULL )
  536. return NULL;
  537. // get origin of start point and squared distance to origin
  538. Vector pointToHere = pStartNode->GetOrigin() - point;
  539. float distSq = pointToHere.LengthSqr();
  540. if ( distSq >= minDistSq )
  541. {
  542. if ( distSq <= maxDistSq )
  543. {
  544. if ( validator.Validate( pStartNode, this ) )
  545. return pStartNode; // this node is good
  546. // else try to path beyond this
  547. }
  548. else
  549. {
  550. return NULL; // we've gone too far.
  551. }
  552. }
  553. // okay, we need to traverse a little deeper. build a list of
  554. // node links that go in the correct direction, sorted so that
  555. // the correctiest ones are first
  556. // this is an icky O(n^2) algorithm
  557. const int maxlinks = pStartNode->NumLinks();
  558. CUtlVector<AiNetwork_FindNode_linkpair> linkIds( 0, maxlinks );
  559. for ( int i = 0 ; i < maxlinks ; ++i )
  560. {
  561. // can this hull go through this link
  562. CAI_Link * RESTRICT link = pStartNode->GetLinkByIndex(i);
  563. if ( link->m_iAcceptedMoveTypes[hulltype] & movetype )
  564. { // yes, we can move through this node
  565. // find where to add it into the list
  566. CAI_Node *RESTRICT destNode = this->GetNode(link->DestNodeID( pStartNode->GetId() )); //this->GetNode(link->m_iDestID);
  567. Assert(destNode);
  568. float distToDestSq = (destNode->GetOrigin() - point).LengthSqr();
  569. // only follow links that face away from the start position (this is what prevents infinite loops)
  570. if ( distToDestSq > distSq )
  571. {
  572. // walk through list from beginning to end and bail out when
  573. // finding one further away so can insert before
  574. // thus our vector will be sorted in descending order of distance
  575. // from origin
  576. int j;
  577. for ( j = 0 ; (j < linkIds.Count()) && (distToDestSq < linkIds[j].distFromStartSq) ; ++j );
  578. linkIds.InsertBefore( j, AiNetwork_FindNode_linkpair( destNode, distToDestSq ) );
  579. }
  580. }
  581. }
  582. // now try each of the nodes we have accumulated into this vector and recursively call
  583. // into any that may match
  584. for ( int i = 0 ; i < linkIds.Count() ; ++i )
  585. {
  586. CAI_Node *retval = FindNodeDistanceAwayFromStart( linkIds[i].node, point, minDistSq, maxDistSq, hulltype, movetype, validator );
  587. if ( ai_nav_debug_experimental_pathing.GetBool() )
  588. {
  589. if ( retval )
  590. {
  591. NDebugOverlay::HorzArrow( pStartNode->GetOrigin(), linkIds[i].node->GetOrigin(), 4, 0, 255, 255, 255, true, ai_nav_debug_experimental_pathing.GetFloat() );
  592. return retval;
  593. }
  594. else
  595. {
  596. NDebugOverlay::HorzArrow( pStartNode->GetOrigin(), linkIds[i].node->GetOrigin(),4, 255, 128, 0, 255, true, ai_nav_debug_experimental_pathing.GetFloat() );
  597. }
  598. }
  599. else
  600. {
  601. if ( retval )
  602. return retval;
  603. }
  604. }
  605. // didn't find anything
  606. return NULL;
  607. }
  608. //=============================================================================