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.

807 lines
20 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. // nav_pathfind.h
  9. // Path-finding mechanisms using the Navigation Mesh
  10. // Author: Michael S. Booth ([email protected]), January 2003
  11. #ifndef _NAV_PATHFIND_H_
  12. #define _NAV_PATHFIND_H_
  13. #include "tier0/vprof.h"
  14. #include "mathlib/ssemath.h"
  15. #include "nav_area.h"
  16. extern int g_DebugPathfindCounter;
  17. //-------------------------------------------------------------------------------------------------------------------
  18. /**
  19. * Used when building a path to determine the kind of path to build
  20. */
  21. enum RouteType
  22. {
  23. DEFAULT_ROUTE,
  24. FASTEST_ROUTE,
  25. SAFEST_ROUTE,
  26. RETREAT_ROUTE,
  27. };
  28. //--------------------------------------------------------------------------------------------------------------
  29. /**
  30. * Functor used with NavAreaBuildPath()
  31. */
  32. class ShortestPathCost
  33. {
  34. public:
  35. float operator() ( CNavArea *area, CNavArea *fromArea, const CNavLadder *ladder, const CFuncElevator *elevator, float length )
  36. {
  37. if ( fromArea == NULL )
  38. {
  39. // first area in path, no cost
  40. return 0.0f;
  41. }
  42. else
  43. {
  44. // compute distance traveled along path so far
  45. float dist;
  46. if ( ladder )
  47. {
  48. dist = ladder->m_length;
  49. }
  50. else if ( length > 0.0 )
  51. {
  52. dist = length;
  53. }
  54. else
  55. {
  56. dist = ( area->GetCenter() - fromArea->GetCenter() ).Length();
  57. }
  58. float cost = dist + fromArea->GetCostSoFar();
  59. // if this is a "crouch" area, add penalty
  60. if ( area->GetAttributes() & NAV_MESH_CROUCH )
  61. {
  62. const float crouchPenalty = 20.0f; // 10
  63. cost += crouchPenalty * dist;
  64. }
  65. // if this is a "jump" area, add penalty
  66. if ( area->GetAttributes() & NAV_MESH_JUMP )
  67. {
  68. const float jumpPenalty = 5.0f;
  69. cost += jumpPenalty * dist;
  70. }
  71. // if this is a 'damaging' area (Fire for example), add penalty
  72. if ( area->IsDamaging() )
  73. {
  74. const float damagingPenalty = 100.0f;
  75. cost += damagingPenalty * dist;
  76. }
  77. return cost;
  78. }
  79. }
  80. };
  81. //--------------------------------------------------------------------------------------------------------------
  82. /**
  83. * Find path from startArea to goalArea via an A* search, using supplied cost heuristic.
  84. * If cost functor returns -1 for an area, that area is considered a dead end.
  85. * This doesn't actually build a path, but the path is defined by following parent
  86. * pointers back from goalArea to startArea.
  87. * If 'closestArea' is non-NULL, the closest area to the goal is returned (useful if the path fails).
  88. * If 'goalArea' is NULL, will compute a path as close as possible to 'goalPos'.
  89. * If 'goalPos' is NULL, will use the center of 'goalArea' as the goal position.
  90. * If 'maxPathLength' is nonzero, path building will stop when this length is reached.
  91. * Returns true if a path exists.
  92. */
  93. #define IGNORE_NAV_BLOCKERS true
  94. template< typename CostFunctor >
  95. bool NavAreaBuildPath( CNavArea *startArea, CNavArea *goalArea, const Vector *goalPos, CostFunctor &costFunc, CNavArea **closestArea = NULL, float maxPathLength = 0.0f, int teamID = TEAM_ANY, bool ignoreNavBlockers = false )
  96. {
  97. VPROF_BUDGET( "NavAreaBuildPath", "NextBotSpiky" );
  98. SNPROF("NavAreaBuildPath");
  99. if ( closestArea )
  100. {
  101. *closestArea = startArea;
  102. }
  103. bool isDebug = ( g_DebugPathfindCounter-- > 0 );
  104. if (startArea == NULL)
  105. return false;
  106. if (goalArea != NULL && goalArea->IsBlocked( teamID, ignoreNavBlockers ))
  107. goalArea = NULL;
  108. if (goalArea == NULL && goalPos == NULL)
  109. return false;
  110. startArea->SetParent( NULL );
  111. // if we are already in the goal area, build trivial path
  112. if (startArea == goalArea)
  113. {
  114. goalArea->SetParent( NULL );
  115. return true;
  116. }
  117. // determine actual goal position
  118. Vector actualGoalPos = (goalPos) ? *goalPos : goalArea->GetCenter();
  119. // start search
  120. CNavArea::ClearSearchLists();
  121. // compute estimate of path length
  122. /// @todo Cost might work as "manhattan distance"
  123. startArea->SetTotalCost( (startArea->GetCenter() - actualGoalPos).Length() );
  124. float initCost = costFunc( startArea, NULL, NULL, NULL, -1.0f );
  125. if (initCost < 0.0f)
  126. return false;
  127. startArea->SetCostSoFar( initCost );
  128. startArea->SetPathLengthSoFar( 0.0 );
  129. startArea->AddToOpenList();
  130. // keep track of the area we visit that is closest to the goal
  131. if (closestArea)
  132. *closestArea = startArea;
  133. float closestAreaDist = startArea->GetTotalCost();
  134. // do A* search
  135. while( !CNavArea::IsOpenListEmpty() )
  136. {
  137. // get next area to check
  138. CNavArea *area = CNavArea::PopOpenList();
  139. if ( isDebug )
  140. {
  141. area->DrawFilled( 0, 255, 0, 128, 30.0f );
  142. }
  143. // don't consider blocked areas
  144. if ( area->IsBlocked( teamID, ignoreNavBlockers ) )
  145. continue;
  146. // check if we have found the goal area or position
  147. if (area == goalArea || (goalArea == NULL && goalPos && area->Contains( *goalPos )))
  148. {
  149. if (closestArea)
  150. {
  151. *closestArea = area;
  152. }
  153. return true;
  154. }
  155. // search adjacent areas
  156. enum SearchType
  157. {
  158. SEARCH_FLOOR, SEARCH_LADDERS, SEARCH_ELEVATORS
  159. };
  160. SearchType searchWhere = SEARCH_FLOOR;
  161. int searchIndex = 0;
  162. int dir = NORTH;
  163. const NavConnectVector *floorList = area->GetAdjacentAreas( NORTH );
  164. bool ladderUp = true;
  165. const NavLadderConnectVector *ladderList = NULL;
  166. enum { AHEAD = 0, LEFT, RIGHT, BEHIND, NUM_TOP_DIRECTIONS };
  167. int ladderTopDir = AHEAD;
  168. bool bHaveMaxPathLength = ( maxPathLength > 0.0f );
  169. float length = -1;
  170. while( true )
  171. {
  172. CNavArea *newArea = NULL;
  173. NavTraverseType how;
  174. const CNavLadder *ladder = NULL;
  175. const CFuncElevator *elevator = NULL;
  176. //
  177. // Get next adjacent area - either on floor or via ladder
  178. //
  179. if ( searchWhere == SEARCH_FLOOR )
  180. {
  181. // if exhausted adjacent connections in current direction, begin checking next direction
  182. if ( searchIndex >= floorList->Count() )
  183. {
  184. ++dir;
  185. if ( dir == NUM_DIRECTIONS )
  186. {
  187. // checked all directions on floor - check ladders next
  188. searchWhere = SEARCH_LADDERS;
  189. ladderList = area->GetLadders( CNavLadder::LADDER_UP );
  190. searchIndex = 0;
  191. ladderTopDir = AHEAD;
  192. }
  193. else
  194. {
  195. // start next direction
  196. floorList = area->GetAdjacentAreas( (NavDirType)dir );
  197. searchIndex = 0;
  198. }
  199. continue;
  200. }
  201. const NavConnect &floorConnect = floorList->Element( searchIndex );
  202. newArea = floorConnect.area;
  203. length = floorConnect.length;
  204. how = (NavTraverseType)dir;
  205. ++searchIndex;
  206. if ( IsGameConsole() && searchIndex < floorList->Count() )
  207. {
  208. PREFETCH360( floorList->Element( searchIndex ).area, 0 );
  209. }
  210. }
  211. else if ( searchWhere == SEARCH_LADDERS )
  212. {
  213. if ( searchIndex >= ladderList->Count() )
  214. {
  215. if ( !ladderUp )
  216. {
  217. // checked both ladder directions - check elevators next
  218. searchWhere = SEARCH_ELEVATORS;
  219. searchIndex = 0;
  220. ladder = NULL;
  221. }
  222. else
  223. {
  224. // check down ladders
  225. ladderUp = false;
  226. ladderList = area->GetLadders( CNavLadder::LADDER_DOWN );
  227. searchIndex = 0;
  228. }
  229. continue;
  230. }
  231. if ( ladderUp )
  232. {
  233. ladder = ladderList->Element( searchIndex ).ladder;
  234. // do not use BEHIND connection, as its very hard to get to when going up a ladder
  235. if ( ladderTopDir == AHEAD )
  236. {
  237. newArea = ladder->m_topForwardArea;
  238. }
  239. else if ( ladderTopDir == LEFT )
  240. {
  241. newArea = ladder->m_topLeftArea;
  242. }
  243. else if ( ladderTopDir == RIGHT )
  244. {
  245. newArea = ladder->m_topRightArea;
  246. }
  247. else
  248. {
  249. ++searchIndex;
  250. ladderTopDir = AHEAD;
  251. continue;
  252. }
  253. how = GO_LADDER_UP;
  254. ++ladderTopDir;
  255. }
  256. else
  257. {
  258. newArea = ladderList->Element( searchIndex ).ladder->m_bottomArea;
  259. how = GO_LADDER_DOWN;
  260. ladder = ladderList->Element(searchIndex).ladder;
  261. ++searchIndex;
  262. }
  263. if ( newArea == NULL )
  264. continue;
  265. length = -1.0f;
  266. }
  267. else // if ( searchWhere == SEARCH_ELEVATORS )
  268. {
  269. elevator = NULL;
  270. break;
  271. }
  272. // don't backtrack
  273. if ( newArea == area )
  274. continue;
  275. // don't consider blocked areas
  276. if ( newArea->IsBlocked( teamID, ignoreNavBlockers ) )
  277. continue;
  278. float newCostSoFar = costFunc( newArea, area, ladder, elevator, length );
  279. // check if cost functor says this area is a dead-end
  280. if ( newCostSoFar < 0.0f )
  281. continue;
  282. // stop if path length limit reached
  283. if ( bHaveMaxPathLength )
  284. {
  285. // keep track of path length so far
  286. float deltaLength = ( newArea->GetCenter() - area->GetCenter() ).Length();
  287. float newLengthSoFar = area->GetPathLengthSoFar() + deltaLength;
  288. if ( newLengthSoFar > maxPathLength )
  289. continue;
  290. newArea->SetPathLengthSoFar( newLengthSoFar );
  291. }
  292. if ( ( newArea->IsOpen() || newArea->IsClosed() ) && newArea->GetCostSoFar() <= newCostSoFar )
  293. {
  294. // this is a worse path - skip it
  295. continue;
  296. }
  297. else
  298. {
  299. // compute estimate of distance left to go
  300. float distSq = ( newArea->GetCenter() - actualGoalPos ).LengthSqr();
  301. float newCostRemaining = ( distSq > 0.0 ) ? FastSqrt( distSq ) : 0.0 ;
  302. // track closest area to goal in case path fails
  303. if ( closestArea && newCostRemaining < closestAreaDist )
  304. {
  305. *closestArea = newArea;
  306. closestAreaDist = newCostRemaining;
  307. }
  308. newArea->SetCostSoFar( newCostSoFar );
  309. newArea->SetTotalCost( newCostSoFar + newCostRemaining );
  310. if ( newArea->IsClosed() )
  311. {
  312. newArea->RemoveFromClosedList();
  313. }
  314. if ( newArea->IsOpen() )
  315. {
  316. // area already on open list, update the list order to keep costs sorted
  317. newArea->UpdateOnOpenList();
  318. }
  319. else
  320. {
  321. newArea->AddToOpenList();
  322. }
  323. newArea->SetParent( area, how );
  324. }
  325. }
  326. // we have searched this area
  327. area->AddToClosedList();
  328. }
  329. return false;
  330. }
  331. //--------------------------------------------------------------------------------------------------------------
  332. /**
  333. * Compute distance between two areas. Return -1 if can't reach 'endArea' from 'startArea'.
  334. */
  335. template< typename CostFunctor >
  336. float NavAreaTravelDistance( CNavArea *startArea, CNavArea *endArea, CostFunctor &costFunc, float maxPathLength = 0.0f )
  337. {
  338. if (startArea == NULL)
  339. return -1.0f;
  340. if (endArea == NULL)
  341. return -1.0f;
  342. if (startArea == endArea)
  343. return 0.0f;
  344. // compute path between areas using given cost heuristic
  345. if (NavAreaBuildPath( startArea, endArea, NULL, costFunc, NULL, maxPathLength ) == false)
  346. return -1.0f;
  347. // compute distance along path
  348. float distance = 0.0f;
  349. for( CNavArea *area = endArea; area->GetParent(); area = area->GetParent() )
  350. {
  351. distance += (area->GetCenter() - area->GetParent()->GetCenter()).Length();
  352. }
  353. return distance;
  354. }
  355. //--------------------------------------------------------------------------------------------------------------
  356. /**
  357. * Do a breadth-first search, invoking functor on each area.
  358. * If functor returns 'true', continue searching from this area.
  359. * If functor returns 'false', the area's adjacent areas are not explored (dead end).
  360. * If 'maxRange' is 0 or less, no range check is done (all areas will be examined).
  361. *
  362. * NOTE: Returns all areas that overlap range, even partially
  363. *
  364. * @todo Use ladder connections
  365. */
  366. // helper function
  367. inline void AddAreaToOpenList( CNavArea *area, CNavArea *parent, const Vector &startPos, float maxRange )
  368. {
  369. if (area == NULL)
  370. return;
  371. if (!area->IsMarked())
  372. {
  373. area->Mark();
  374. area->SetTotalCost( 0.0f );
  375. area->SetParent( parent );
  376. if (maxRange > 0.0f)
  377. {
  378. // make sure this area overlaps range
  379. Vector closePos;
  380. area->GetClosestPointOnArea( startPos, &closePos );
  381. if ((closePos - startPos).AsVector2D().IsLengthLessThan( maxRange ))
  382. {
  383. // compute approximate distance along path to limit travel range, too
  384. float distAlong = parent->GetCostSoFar();
  385. distAlong += (area->GetCenter() - parent->GetCenter()).Length();
  386. area->SetCostSoFar( distAlong );
  387. // allow for some fudge due to large size areas
  388. if (distAlong <= 1.5f * maxRange)
  389. area->AddToOpenList();
  390. }
  391. }
  392. else
  393. {
  394. // infinite range
  395. area->AddToOpenList();
  396. }
  397. }
  398. }
  399. /****************************************************************
  400. * DEPRECATED: Use filter-based SearchSurroundingAreas below
  401. ****************************************************************/
  402. #define INCLUDE_INCOMING_CONNECTIONS 0x1
  403. #define INCLUDE_BLOCKED_AREAS 0x2
  404. #define EXCLUDE_OUTGOING_CONNECTIONS 0x4
  405. #define EXCLUDE_ELEVATORS 0x8
  406. template < typename Functor >
  407. void SearchSurroundingAreas( CNavArea *startArea, const Vector &startPos, Functor &func, float maxRange = -1.0f, unsigned int options = 0, int teamID = TEAM_ANY )
  408. {
  409. if (startArea == NULL)
  410. return;
  411. CNavArea::MakeNewMarker();
  412. CNavArea::ClearSearchLists();
  413. startArea->AddToOpenList();
  414. startArea->SetTotalCost( 0.0f );
  415. startArea->SetCostSoFar( 0.0f );
  416. startArea->SetParent( NULL );
  417. startArea->Mark();
  418. while( !CNavArea::IsOpenListEmpty() )
  419. {
  420. // get next area to check
  421. CNavArea *area = CNavArea::PopOpenList();
  422. // don't use blocked areas
  423. if ( area->IsBlocked( teamID ) && !(options & INCLUDE_BLOCKED_AREAS) )
  424. continue;
  425. // invoke functor on area
  426. if (func( area ))
  427. {
  428. // explore adjacent floor areas
  429. for( int dir=0; dir<NUM_DIRECTIONS; ++dir )
  430. {
  431. int count = area->GetAdjacentCount( (NavDirType)dir );
  432. for( int i=0; i<count; ++i )
  433. {
  434. CNavArea *adjArea = area->GetAdjacentArea( (NavDirType)dir, i );
  435. if ( options & EXCLUDE_OUTGOING_CONNECTIONS )
  436. {
  437. if ( !adjArea->IsConnected( area, NUM_DIRECTIONS ) )
  438. {
  439. continue; // skip this outgoing connection
  440. }
  441. }
  442. AddAreaToOpenList( adjArea, area, startPos, maxRange );
  443. }
  444. }
  445. // potentially include areas that connect TO this area via a one-way link
  446. if (options & INCLUDE_INCOMING_CONNECTIONS)
  447. {
  448. for( int dir=0; dir<NUM_DIRECTIONS; ++dir )
  449. {
  450. const NavConnectVector *list = area->GetIncomingConnections( (NavDirType)dir );
  451. FOR_EACH_VEC( (*list), it )
  452. {
  453. NavConnect connect = (*list)[ it ];
  454. AddAreaToOpenList( connect.area, area, startPos, maxRange );
  455. }
  456. }
  457. }
  458. // explore adjacent areas connected by ladders
  459. // check up ladders
  460. const NavLadderConnectVector *ladderList = area->GetLadders( CNavLadder::LADDER_UP );
  461. if (ladderList)
  462. {
  463. FOR_EACH_VEC( (*ladderList), it )
  464. {
  465. const CNavLadder *ladder = (*ladderList)[ it ].ladder;
  466. // do not use BEHIND connection, as its very hard to get to when going up a ladder
  467. AddAreaToOpenList( ladder->m_topForwardArea, area, startPos, maxRange );
  468. AddAreaToOpenList( ladder->m_topLeftArea, area, startPos, maxRange );
  469. AddAreaToOpenList( ladder->m_topRightArea, area, startPos, maxRange );
  470. }
  471. }
  472. // check down ladders
  473. ladderList = area->GetLadders( CNavLadder::LADDER_DOWN );
  474. if (ladderList)
  475. {
  476. FOR_EACH_VEC( (*ladderList), it )
  477. {
  478. const CNavLadder *ladder = (*ladderList)[ it ].ladder;
  479. AddAreaToOpenList( ladder->m_bottomArea, area, startPos, maxRange );
  480. }
  481. }
  482. }
  483. }
  484. }
  485. //--------------------------------------------------------------------------------------------------------------
  486. /**
  487. * Derive your own custom search functor from this interface method for use with SearchSurroundingAreas below.
  488. */
  489. class ISearchSurroundingAreasFunctor
  490. {
  491. public:
  492. virtual ~ISearchSurroundingAreasFunctor() { }
  493. /**
  494. * Perform user-defined action on area.
  495. * Return 'false' to end the search (ie: you found what you were looking for)
  496. */
  497. virtual bool operator() ( CNavArea *area, CNavArea *priorArea, float travelDistanceSoFar ) = 0;
  498. // return true if 'adjArea' should be included in the ongoing search
  499. virtual bool ShouldSearch( CNavArea *adjArea, CNavArea *currentArea, float travelDistanceSoFar )
  500. {
  501. return !adjArea->IsBlocked( TEAM_ANY );
  502. }
  503. /**
  504. * Collect adjacent areas to continue the search by calling 'IncludeInSearch' on each
  505. */
  506. virtual void IterateAdjacentAreas( CNavArea *area, CNavArea *priorArea, float travelDistanceSoFar )
  507. {
  508. // search adjacent outgoing connections
  509. for( int dir=0; dir<NUM_DIRECTIONS; ++dir )
  510. {
  511. int count = area->GetAdjacentCount( (NavDirType)dir );
  512. for( int i=0; i<count; ++i )
  513. {
  514. CNavArea *adjArea = area->GetAdjacentArea( (NavDirType)dir, i );
  515. if ( ShouldSearch( adjArea, area, travelDistanceSoFar ) )
  516. {
  517. IncludeInSearch( adjArea, area );
  518. }
  519. }
  520. }
  521. }
  522. // Invoked after the search has completed
  523. virtual void PostSearch( void ) { }
  524. // consider 'area' in upcoming search steps
  525. void IncludeInSearch( CNavArea *area, CNavArea *priorArea )
  526. {
  527. if ( area == NULL )
  528. return;
  529. if ( !area->IsMarked() )
  530. {
  531. area->Mark();
  532. area->SetTotalCost( 0.0f );
  533. area->SetParent( priorArea );
  534. // compute approximate travel distance from start area of search
  535. if ( priorArea )
  536. {
  537. float distAlong = priorArea->GetCostSoFar();
  538. distAlong += ( area->GetCenter() - priorArea->GetCenter() ).Length();
  539. area->SetCostSoFar( distAlong );
  540. }
  541. else
  542. {
  543. area->SetCostSoFar( 0.0f );
  544. }
  545. // adding an area to the open list also marks it
  546. area->AddToOpenList();
  547. }
  548. }
  549. };
  550. /**
  551. * Do a breadth-first search starting from 'startArea' and continuing outward based on
  552. * adjacent areas that pass the given filter
  553. */
  554. inline void SearchSurroundingAreas( CNavArea *startArea, ISearchSurroundingAreasFunctor &func )
  555. {
  556. if ( startArea )
  557. {
  558. CNavArea::MakeNewMarker();
  559. CNavArea::ClearSearchLists();
  560. startArea->AddToOpenList();
  561. startArea->SetTotalCost( 0.0f );
  562. startArea->SetCostSoFar( 0.0f );
  563. startArea->SetParent( NULL );
  564. startArea->Mark();
  565. CUtlVector< CNavArea * > adjVector;
  566. while( !CNavArea::IsOpenListEmpty() )
  567. {
  568. // get next area to check
  569. CNavArea *area = CNavArea::PopOpenList();
  570. if ( func( area, area->GetParent(), area->GetCostSoFar() ) )
  571. {
  572. func.IterateAdjacentAreas( area, area->GetParent(), area->GetCostSoFar() );
  573. }
  574. else
  575. {
  576. // search aborted
  577. break;
  578. }
  579. }
  580. }
  581. func.PostSearch();
  582. }
  583. //--------------------------------------------------------------------------------------------------------------
  584. /**
  585. * Fuctor that returns lowest cost for farthest away areas
  586. * For use with FindMinimumCostArea()
  587. */
  588. class FarAwayFunctor
  589. {
  590. public:
  591. float operator() ( CNavArea *area, CNavArea *fromArea, const CNavLadder *ladder )
  592. {
  593. if (area == fromArea)
  594. return 9999999.9f;
  595. return 1.0f/(fromArea->GetCenter() - area->GetCenter()).Length();
  596. }
  597. };
  598. /**
  599. * Fuctor that returns lowest cost for areas farthest from given position
  600. * For use with FindMinimumCostArea()
  601. */
  602. class FarAwayFromPositionFunctor
  603. {
  604. public:
  605. FarAwayFromPositionFunctor( const Vector &pos ) : m_pos( pos )
  606. {
  607. }
  608. float operator() ( CNavArea *area, CNavArea *fromArea, const CNavLadder *ladder )
  609. {
  610. return 1.0f/(m_pos - area->GetCenter()).Length();
  611. }
  612. private:
  613. const Vector &m_pos;
  614. };
  615. /**
  616. * Pick a low-cost area of "decent" size
  617. */
  618. template< typename CostFunctor >
  619. CNavArea *FindMinimumCostArea( CNavArea *startArea, CostFunctor &costFunc )
  620. {
  621. const float minSize = 150.0f;
  622. // collect N low-cost areas of a decent size
  623. enum { NUM_CHEAP_AREAS = 32 };
  624. struct
  625. {
  626. CNavArea *area;
  627. float cost;
  628. }
  629. cheapAreaSet[ NUM_CHEAP_AREAS ];
  630. int cheapAreaSetCount = 0;
  631. FOR_EACH_VEC( TheNavAreas, iter )
  632. {
  633. CNavArea *area = TheNavAreas[iter];
  634. // skip the small areas
  635. if ( area->GetSizeX() < minSize || area->GetSizeY() < minSize)
  636. continue;
  637. // compute cost of this area
  638. // HPE_FIX[pfreese]: changed this to only pass three parameters, in accord with the two functors above
  639. float cost = costFunc( area, startArea, NULL );
  640. if (cheapAreaSetCount < NUM_CHEAP_AREAS)
  641. {
  642. cheapAreaSet[ cheapAreaSetCount ].area = area;
  643. cheapAreaSet[ cheapAreaSetCount++ ].cost = cost;
  644. }
  645. else
  646. {
  647. // replace most expensive cost if this is cheaper
  648. int expensive = 0;
  649. for( int i=1; i<NUM_CHEAP_AREAS; ++i )
  650. if (cheapAreaSet[i].cost > cheapAreaSet[expensive].cost)
  651. expensive = i;
  652. if (cheapAreaSet[expensive].cost > cost)
  653. {
  654. cheapAreaSet[expensive].area = area;
  655. cheapAreaSet[expensive].cost = cost;
  656. }
  657. }
  658. }
  659. if (cheapAreaSetCount)
  660. {
  661. // pick one of the areas at random
  662. return cheapAreaSet[ RandomInt( 0, cheapAreaSetCount-1 ) ].area;
  663. }
  664. else
  665. {
  666. // degenerate case - no decent sized areas - pick a random area
  667. int numAreas = TheNavAreas.Count();
  668. int which = RandomInt( 0, numAreas-1 );
  669. FOR_EACH_VEC( TheNavAreas, iter )
  670. {
  671. if (which-- == 0)
  672. return TheNavAreas[iter];
  673. }
  674. }
  675. return cheapAreaSet[ RandomInt( 0, cheapAreaSetCount-1 ) ].area;
  676. }
  677. #endif // _NAV_PATHFIND_H_