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.

862 lines
24 KiB

  1. // NextBotPath.h
  2. // Encapsulate and manipulate a path through the world
  3. // Author: Michael Booth, February 2006
  4. //========= Copyright Valve Corporation, All rights reserved. ============//
  5. #ifndef _NEXT_BOT_PATH_H_
  6. #define _NEXT_BOT_PATH_H_
  7. #include "NextBotInterface.h"
  8. #include "tier0/vprof.h"
  9. #define PATH_NO_LENGTH_LIMIT 0.0f // non-default argument value for Path::Compute()
  10. #define PATH_TRUNCATE_INCOMPLETE_PATH false // non-default argument value for Path::Compute()
  11. class INextBot;
  12. class CNavArea;
  13. class CNavLadder;
  14. //---------------------------------------------------------------------------------------------------------------
  15. /**
  16. * The interface for pathfinding costs.
  17. * TODO: Replace all template cost functors with this interface, so we can virtualize and derive from them.
  18. */
  19. class IPathCost
  20. {
  21. public:
  22. virtual float operator()( CNavArea *area, CNavArea *fromArea, const CNavLadder *ladder, const CFuncElevator *elevator, float length ) const = 0;
  23. };
  24. //---------------------------------------------------------------------------------------------------------------
  25. /**
  26. * The interface for selecting a goal area during "open goal" pathfinding
  27. */
  28. class IPathOpenGoalSelector
  29. {
  30. public:
  31. // compare "newArea" to "currentGoal" and return the area that is the better goal area
  32. virtual CNavArea *operator() ( CNavArea *currentGoal, CNavArea *newArea ) const = 0;
  33. };
  34. //---------------------------------------------------------------------------------------------------------------
  35. /**
  36. * A Path through the world.
  37. * Not only does this encapsulate a path to get from point A to point B,
  38. * but also the selecting the decision algorithm for how to build that path.
  39. */
  40. class Path
  41. {
  42. public:
  43. Path( void );
  44. virtual ~Path() { }
  45. enum SegmentType
  46. {
  47. ON_GROUND,
  48. DROP_DOWN,
  49. CLIMB_UP,
  50. JUMP_OVER_GAP,
  51. LADDER_UP,
  52. LADDER_DOWN,
  53. NUM_SEGMENT_TYPES
  54. };
  55. // @todo Allow custom Segment classes for different kinds of paths
  56. struct Segment
  57. {
  58. CNavArea *area; // the area along the path
  59. NavTraverseType how; // how to enter this area from the previous one
  60. Vector pos; // our movement goal position at this point in the path
  61. const CNavLadder *ladder; // if "how" refers to a ladder, this is it
  62. SegmentType type; // how to traverse this segment of the path
  63. Vector forward; // unit vector along segment
  64. float length; // length of this segment
  65. float distanceFromStart; // distance of this node from the start of the path
  66. float curvature; // how much the path 'curves' at this point in the XY plane (0 = none, 1 = 180 degree doubleback)
  67. Vector m_portalCenter; // position of center of 'portal' between previous area and this area
  68. float m_portalHalfWidth; // half width of 'portal'
  69. };
  70. virtual float GetLength( void ) const; // return length of path from start to finish
  71. virtual const Vector &GetPosition( float distanceFromStart, const Segment *start = NULL ) const; // return a position on the path at the given distance from the path start
  72. virtual const Vector &GetClosestPosition( const Vector &pos, const Segment *start = NULL, float alongLimit = 0.0f ) const; // return the closest point on the path to the given position
  73. virtual const Vector &GetStartPosition( void ) const; // return the position where this path starts
  74. virtual const Vector &GetEndPosition( void ) const; // return the position where this path ends
  75. virtual CBaseCombatCharacter *GetSubject( void ) const; // return the actor this path leads to, or NULL if there is no subject
  76. virtual const Path::Segment *GetCurrentGoal( void ) const; // return current goal along the path we are trying to reach
  77. virtual float GetAge( void ) const; // return "age" of this path (time since it was built)
  78. enum SeekType
  79. {
  80. SEEK_ENTIRE_PATH, // search the entire path length
  81. SEEK_AHEAD, // search from current cursor position forward toward end of path
  82. SEEK_BEHIND // search from current cursor position backward toward path start
  83. };
  84. virtual void MoveCursorToClosestPosition( const Vector &pos, SeekType type = SEEK_ENTIRE_PATH, float alongLimit = 0.0f ) const; // Set cursor position to closest point on path to given position
  85. enum MoveCursorType
  86. {
  87. PATH_ABSOLUTE_DISTANCE,
  88. PATH_RELATIVE_DISTANCE
  89. };
  90. virtual void MoveCursorToStart( void ); // set seek cursor to start of path
  91. virtual void MoveCursorToEnd( void ); // set seek cursor to end of path
  92. virtual void MoveCursor( float value, MoveCursorType type = PATH_ABSOLUTE_DISTANCE ); // change seek cursor position
  93. virtual float GetCursorPosition( void ) const; // return position of seek cursor (distance along path)
  94. struct Data
  95. {
  96. Vector pos; // the position along the path
  97. Vector forward; // unit vector along path direction
  98. float curvature; // how much the path 'curves' at this point in the XY plane (0 = none, 1 = 180 degree doubleback)
  99. const Segment *segmentPrior; // the segment just before this position
  100. };
  101. virtual const Data &GetCursorData( void ) const; // return path state at the current cursor position
  102. virtual bool IsValid( void ) const;
  103. virtual void Invalidate( void ); // make path invalid (clear it)
  104. virtual void Draw( const Path::Segment *start = NULL ) const; // draw the path for debugging
  105. virtual void DrawInterpolated( float from, float to ); // draw the path for debugging - MODIFIES cursor position
  106. virtual const Segment *FirstSegment( void ) const; // return first segment of path
  107. virtual const Segment *NextSegment( const Segment *currentSegment ) const; // return next segment of path, given current one
  108. virtual const Segment *PriorSegment( const Segment *currentSegment ) const; // return previous segment of path, given current one
  109. virtual const Segment *LastSegment( void ) const; // return last segment of path
  110. enum ResultType
  111. {
  112. COMPLETE_PATH,
  113. PARTIAL_PATH,
  114. NO_PATH
  115. };
  116. virtual void OnPathChanged( INextBot *bot, ResultType result ) { } // invoked when the path is (re)computed (path is valid at the time of this call)
  117. virtual void Copy( INextBot *bot, const Path &path ); // Replace this path with the given path's data
  118. //-----------------------------------------------------------------------------------------------------------------
  119. /**
  120. * Compute shortest path from bot to given actor via A* algorithm.
  121. * If returns true, path was found to the subject.
  122. * If returns false, path may either be invalid (use IsValid() to check), or valid but
  123. * doesn't reach all the way to the subject.
  124. */
  125. template< typename CostFunctor >
  126. bool Compute( INextBot *bot, CBaseCombatCharacter *subject, CostFunctor &costFunc, float maxPathLength = 0.0f, bool includeGoalIfPathFails = true )
  127. {
  128. VPROF_BUDGET( "Path::Compute(subject)", "NextBot" );
  129. Invalidate();
  130. m_subject = subject;
  131. const Vector &start = bot->GetPosition();
  132. CNavArea *startArea = bot->GetEntity()->GetLastKnownArea();
  133. if ( !startArea )
  134. {
  135. OnPathChanged( bot, NO_PATH );
  136. return false;
  137. }
  138. CNavArea *subjectArea = subject->GetLastKnownArea();
  139. if ( !subjectArea )
  140. {
  141. OnPathChanged( bot, NO_PATH );
  142. return false;
  143. }
  144. Vector subjectPos = subject->GetAbsOrigin();
  145. // if we are already in the subject area, build trivial path
  146. if ( startArea == subjectArea )
  147. {
  148. BuildTrivialPath( bot, subjectPos );
  149. return true;
  150. }
  151. //
  152. // Compute shortest path to subject
  153. //
  154. CNavArea *closestArea = NULL;
  155. bool pathResult = NavAreaBuildPath( startArea, subjectArea, &subjectPos, costFunc, &closestArea, maxPathLength, bot->GetEntity()->GetTeamNumber() );
  156. // Failed?
  157. if ( closestArea == NULL )
  158. return false;
  159. //
  160. // Build actual path by following parent links back from goal area
  161. //
  162. // get count
  163. int count = 0;
  164. CNavArea *area;
  165. for( area = closestArea; area; area = area->GetParent() )
  166. {
  167. ++count;
  168. if ( area == startArea )
  169. {
  170. // startArea can be re-evaluated during the pathfind and given a parent...
  171. break;
  172. }
  173. if ( count >= MAX_PATH_SEGMENTS-1 ) // save room for endpoint
  174. break;
  175. }
  176. if ( count == 1 )
  177. {
  178. BuildTrivialPath( bot, subjectPos );
  179. return pathResult;
  180. }
  181. // assemble path
  182. m_segmentCount = count;
  183. for( area = closestArea; count && area; area = area->GetParent() )
  184. {
  185. --count;
  186. m_path[ count ].area = area;
  187. m_path[ count ].how = area->GetParentHow();
  188. m_path[ count ].type = ON_GROUND;
  189. }
  190. if ( pathResult || includeGoalIfPathFails )
  191. {
  192. // append actual subject position
  193. m_path[ m_segmentCount ].area = closestArea;
  194. m_path[ m_segmentCount ].pos = subjectPos;
  195. m_path[ m_segmentCount ].ladder = NULL;
  196. m_path[ m_segmentCount ].how = NUM_TRAVERSE_TYPES;
  197. m_path[ m_segmentCount ].type = ON_GROUND;
  198. ++m_segmentCount;
  199. }
  200. // compute path positions
  201. if ( ComputePathDetails( bot, start ) == false )
  202. {
  203. Invalidate();
  204. OnPathChanged( bot, NO_PATH );
  205. return false;
  206. }
  207. // remove redundant nodes and clean up path
  208. Optimize( bot );
  209. PostProcess();
  210. OnPathChanged( bot, pathResult ? COMPLETE_PATH : PARTIAL_PATH );
  211. return pathResult;
  212. }
  213. //-----------------------------------------------------------------------------------------------------------------
  214. /**
  215. * Compute shortest path from bot to 'goal' via A* algorithm.
  216. * If returns true, path was found to the goal position.
  217. * If returns false, path may either be invalid (use IsValid() to check), or valid but
  218. * doesn't reach all the way to the goal.
  219. */
  220. template< typename CostFunctor >
  221. bool Compute( INextBot *bot, const Vector &goal, CostFunctor &costFunc, float maxPathLength = 0.0f, bool includeGoalIfPathFails = true )
  222. {
  223. VPROF_BUDGET( "Path::Compute(goal)", "NextBotSpiky" );
  224. Invalidate();
  225. const Vector &start = bot->GetPosition();
  226. CNavArea *startArea = bot->GetEntity()->GetLastKnownArea();
  227. if ( !startArea )
  228. {
  229. OnPathChanged( bot, NO_PATH );
  230. return false;
  231. }
  232. // check line-of-sight to the goal position when finding it's nav area
  233. const float maxDistanceToArea = 200.0f;
  234. CNavArea *goalArea = TheNavMesh->GetNearestNavArea( goal, true, maxDistanceToArea, true );
  235. // if we are already in the goal area, build trivial path
  236. if ( startArea == goalArea )
  237. {
  238. BuildTrivialPath( bot, goal );
  239. return true;
  240. }
  241. // make sure path end position is on the ground
  242. Vector pathEndPosition = goal;
  243. if ( goalArea )
  244. {
  245. pathEndPosition.z = goalArea->GetZ( pathEndPosition );
  246. }
  247. else
  248. {
  249. TheNavMesh->GetGroundHeight( pathEndPosition, &pathEndPosition.z );
  250. }
  251. //
  252. // Compute shortest path to goal
  253. //
  254. CNavArea *closestArea = NULL;
  255. bool pathResult = NavAreaBuildPath( startArea, goalArea, &goal, costFunc, &closestArea, maxPathLength, bot->GetEntity()->GetTeamNumber() );
  256. // Failed?
  257. if ( closestArea == NULL )
  258. return false;
  259. //
  260. // Build actual path by following parent links back from goal area
  261. //
  262. // get count
  263. int count = 0;
  264. CNavArea *area;
  265. for( area = closestArea; area; area = area->GetParent() )
  266. {
  267. ++count;
  268. if ( area == startArea )
  269. {
  270. // startArea can be re-evaluated during the pathfind and given a parent...
  271. break;
  272. }
  273. if ( count >= MAX_PATH_SEGMENTS-1 ) // save room for endpoint
  274. break;
  275. }
  276. if ( count == 1 )
  277. {
  278. BuildTrivialPath( bot, goal );
  279. return pathResult;
  280. }
  281. // assemble path
  282. m_segmentCount = count;
  283. for( area = closestArea; count && area; area = area->GetParent() )
  284. {
  285. --count;
  286. m_path[ count ].area = area;
  287. m_path[ count ].how = area->GetParentHow();
  288. m_path[ count ].type = ON_GROUND;
  289. }
  290. if ( pathResult || includeGoalIfPathFails )
  291. {
  292. // append actual goal position
  293. m_path[ m_segmentCount ].area = closestArea;
  294. m_path[ m_segmentCount ].pos = pathEndPosition;
  295. m_path[ m_segmentCount ].ladder = NULL;
  296. m_path[ m_segmentCount ].how = NUM_TRAVERSE_TYPES;
  297. m_path[ m_segmentCount ].type = ON_GROUND;
  298. ++m_segmentCount;
  299. }
  300. // compute path positions
  301. if ( ComputePathDetails( bot, start ) == false )
  302. {
  303. Invalidate();
  304. OnPathChanged( bot, NO_PATH );
  305. return false;
  306. }
  307. // remove redundant nodes and clean up path
  308. Optimize( bot );
  309. PostProcess();
  310. OnPathChanged( bot, pathResult ? COMPLETE_PATH : PARTIAL_PATH );
  311. return pathResult;
  312. }
  313. //-----------------------------------------------------------------------------------------------------------------
  314. /**
  315. * Build a path from bot's current location to an undetermined goal area
  316. * that minimizes the given cost along the final path and meets the
  317. * goal criteria.
  318. */
  319. virtual bool ComputeWithOpenGoal( INextBot *bot, const IPathCost &costFunc, const IPathOpenGoalSelector &goalSelector, float maxSearchRadius = 0.0f )
  320. {
  321. VPROF_BUDGET( "ComputeWithOpenGoal", "NextBot" );
  322. int teamID = bot->GetEntity()->GetTeamNumber();
  323. CNavArea *startArea = bot->GetEntity()->GetLastKnownArea();
  324. if ( startArea == NULL )
  325. return NULL;
  326. startArea->SetParent( NULL );
  327. // start search
  328. CNavArea::ClearSearchLists();
  329. float initCost = costFunc( startArea, NULL, NULL, NULL, -1.0f );
  330. if ( initCost < 0.0f )
  331. return NULL;
  332. startArea->SetTotalCost( initCost );
  333. startArea->AddToOpenList();
  334. // find our goal as we search
  335. CNavArea *goalArea = NULL;
  336. //
  337. // Dijkstra's algorithm (since we don't know our goal).
  338. //
  339. while( !CNavArea::IsOpenListEmpty() )
  340. {
  341. // get next area to check
  342. CNavArea *area = CNavArea::PopOpenList();
  343. area->AddToClosedList();
  344. // don't consider blocked areas
  345. if ( area->IsBlocked( teamID ) )
  346. continue;
  347. // build adjacent area array
  348. CollectAdjacentAreas( area );
  349. // search adjacent areas
  350. for( int i=0; i<m_adjAreaIndex; ++i )
  351. {
  352. CNavArea *newArea = m_adjAreaVector[ i ].area;
  353. // only visit each area once
  354. if ( newArea->IsClosed() )
  355. continue;
  356. // don't consider blocked areas
  357. if ( newArea->IsBlocked( teamID ) )
  358. continue;
  359. // don't use this area if it is out of range
  360. if ( maxSearchRadius > 0.0f && ( newArea->GetCenter() - bot->GetEntity()->GetAbsOrigin() ).IsLengthGreaterThan( maxSearchRadius ) )
  361. continue;
  362. // determine cost of traversing this area
  363. float newCost = costFunc( newArea, area, m_adjAreaVector[ i ].ladder, NULL, -1.0f );
  364. // don't use adjacent area if cost functor says it is a dead-end
  365. if ( newCost < 0.0f )
  366. continue;
  367. if ( newArea->IsOpen() && newArea->GetTotalCost() <= newCost )
  368. {
  369. // we have already visited this area, and it has a better path
  370. continue;
  371. }
  372. else
  373. {
  374. // whether this area has been visited or not, we now have a better path to it
  375. newArea->SetParent( area, m_adjAreaVector[ i ].how );
  376. newArea->SetTotalCost( newCost );
  377. // use 'cost so far' to hold cumulative cost
  378. newArea->SetCostSoFar( newCost );
  379. // tricky bit here - relying on OpenList being sorted by cost
  380. if ( newArea->IsOpen() )
  381. {
  382. // area already on open list, update the list order to keep costs sorted
  383. newArea->UpdateOnOpenList();
  384. }
  385. else
  386. {
  387. newArea->AddToOpenList();
  388. }
  389. // keep track of best goal so far
  390. goalArea = goalSelector( goalArea, newArea );
  391. }
  392. }
  393. }
  394. if ( goalArea )
  395. {
  396. // compile the path details into a usable path
  397. AssemblePrecomputedPath( bot, goalArea->GetCenter(), goalArea );
  398. return true;
  399. }
  400. // all adjacent areas are likely too far away
  401. return false;
  402. }
  403. //-----------------------------------------------------------------------------------------------------------------
  404. /**
  405. * Given the last area in a path with valid parent pointers,
  406. * construct the actual path.
  407. */
  408. void AssemblePrecomputedPath( INextBot *bot, const Vector &goal, CNavArea *endArea )
  409. {
  410. VPROF_BUDGET( "AssemblePrecomputedPath", "NextBot" );
  411. const Vector &start = bot->GetPosition();
  412. // get count
  413. int count = 0;
  414. CNavArea *area;
  415. for( area = endArea; area; area = area->GetParent() )
  416. {
  417. ++count;
  418. }
  419. // save room for endpoint
  420. if ( count > MAX_PATH_SEGMENTS-1 )
  421. {
  422. count = MAX_PATH_SEGMENTS-1;
  423. }
  424. else if ( count == 0 )
  425. {
  426. return;
  427. }
  428. if ( count == 1 )
  429. {
  430. BuildTrivialPath( bot, goal );
  431. return;
  432. }
  433. // assemble path
  434. m_segmentCount = count;
  435. for( area = endArea; count && area; area = area->GetParent() )
  436. {
  437. --count;
  438. m_path[ count ].area = area;
  439. m_path[ count ].how = area->GetParentHow();
  440. m_path[ count ].type = ON_GROUND;
  441. }
  442. // append actual goal position
  443. m_path[ m_segmentCount ].area = endArea;
  444. m_path[ m_segmentCount ].pos = goal;
  445. m_path[ m_segmentCount ].ladder = NULL;
  446. m_path[ m_segmentCount ].how = NUM_TRAVERSE_TYPES;
  447. m_path[ m_segmentCount ].type = ON_GROUND;
  448. ++m_segmentCount;
  449. // compute path positions
  450. if ( ComputePathDetails( bot, start ) == false )
  451. {
  452. Invalidate();
  453. OnPathChanged( bot, NO_PATH );
  454. return;
  455. }
  456. // remove redundant nodes and clean up path
  457. Optimize( bot );
  458. PostProcess();
  459. OnPathChanged( bot, COMPLETE_PATH );
  460. }
  461. /**
  462. * Utility function for when start and goal are in the same area
  463. */
  464. bool BuildTrivialPath( INextBot *bot, const Vector &goal );
  465. /**
  466. * Determine exactly where the path goes between the given two areas
  467. * on the path. Return this point in 'crossPos'.
  468. */
  469. virtual void ComputeAreaCrossing( INextBot *bot, const CNavArea *from, const Vector &fromPos, const CNavArea *to, NavDirType dir, Vector *crossPos ) const;
  470. private:
  471. enum { MAX_PATH_SEGMENTS = 256 };
  472. Segment m_path[ MAX_PATH_SEGMENTS ];
  473. int m_segmentCount;
  474. bool ComputePathDetails( INextBot *bot, const Vector &start ); // determine actual path positions
  475. void Optimize( INextBot *bot );
  476. void PostProcess( void );
  477. int FindNextOccludedNode( INextBot *bot, int anchor ); // used by Optimize()
  478. void InsertSegment( Segment newSegment, int i ); // insert new segment at index i
  479. mutable Vector m_pathPos; // used by GetPosition()
  480. mutable Vector m_closePos; // used by GetClosestPosition()
  481. mutable float m_cursorPos; // current cursor position (distance along path)
  482. mutable Data m_cursorData; // used by GetCursorData()
  483. mutable bool m_isCursorDataDirty;
  484. IntervalTimer m_ageTimer; // how old is this path?
  485. CHandle< CBaseCombatCharacter > m_subject; // the subject this path leads to
  486. /**
  487. * Build a vector of adjacent areas reachable from the given area
  488. */
  489. void CollectAdjacentAreas( CNavArea *area )
  490. {
  491. m_adjAreaIndex = 0;
  492. const NavConnectVector &adjNorth = *area->GetAdjacentAreas( NORTH );
  493. FOR_EACH_VEC( adjNorth, it )
  494. {
  495. if ( m_adjAreaIndex >= MAX_ADJ_AREAS )
  496. break;
  497. m_adjAreaVector[ m_adjAreaIndex ].area = adjNorth[ it ].area;
  498. m_adjAreaVector[ m_adjAreaIndex ].how = GO_NORTH;
  499. m_adjAreaVector[ m_adjAreaIndex ].ladder = NULL;
  500. ++m_adjAreaIndex;
  501. }
  502. const NavConnectVector &adjSouth = *area->GetAdjacentAreas( SOUTH );
  503. FOR_EACH_VEC( adjSouth, it )
  504. {
  505. if ( m_adjAreaIndex >= MAX_ADJ_AREAS )
  506. break;
  507. m_adjAreaVector[ m_adjAreaIndex ].area = adjSouth[ it ].area;
  508. m_adjAreaVector[ m_adjAreaIndex ].how = GO_SOUTH;
  509. m_adjAreaVector[ m_adjAreaIndex ].ladder = NULL;
  510. ++m_adjAreaIndex;
  511. }
  512. const NavConnectVector &adjWest = *area->GetAdjacentAreas( WEST );
  513. FOR_EACH_VEC( adjWest, it )
  514. {
  515. if ( m_adjAreaIndex >= MAX_ADJ_AREAS )
  516. break;
  517. m_adjAreaVector[ m_adjAreaIndex ].area = adjWest[ it ].area;
  518. m_adjAreaVector[ m_adjAreaIndex ].how = GO_WEST;
  519. m_adjAreaVector[ m_adjAreaIndex ].ladder = NULL;
  520. ++m_adjAreaIndex;
  521. }
  522. const NavConnectVector &adjEast = *area->GetAdjacentAreas( EAST );
  523. FOR_EACH_VEC( adjEast, it )
  524. {
  525. if ( m_adjAreaIndex >= MAX_ADJ_AREAS )
  526. break;
  527. m_adjAreaVector[ m_adjAreaIndex ].area = adjEast[ it ].area;
  528. m_adjAreaVector[ m_adjAreaIndex ].how = GO_EAST;
  529. m_adjAreaVector[ m_adjAreaIndex ].ladder = NULL;
  530. ++m_adjAreaIndex;
  531. }
  532. const NavLadderConnectVector &adjUpLadder = *area->GetLadders( CNavLadder::LADDER_UP );
  533. FOR_EACH_VEC( adjUpLadder, it )
  534. {
  535. CNavLadder *ladder = adjUpLadder[ it ].ladder;
  536. if ( ladder->m_topForwardArea && m_adjAreaIndex < MAX_ADJ_AREAS )
  537. {
  538. m_adjAreaVector[ m_adjAreaIndex ].area = ladder->m_topForwardArea;
  539. m_adjAreaVector[ m_adjAreaIndex ].how = GO_LADDER_UP;
  540. m_adjAreaVector[ m_adjAreaIndex ].ladder = ladder;
  541. ++m_adjAreaIndex;
  542. }
  543. if ( ladder->m_topLeftArea && m_adjAreaIndex < MAX_ADJ_AREAS )
  544. {
  545. m_adjAreaVector[ m_adjAreaIndex ].area = ladder->m_topLeftArea;
  546. m_adjAreaVector[ m_adjAreaIndex ].how = GO_LADDER_UP;
  547. m_adjAreaVector[ m_adjAreaIndex ].ladder = ladder;
  548. ++m_adjAreaIndex;
  549. }
  550. if ( ladder->m_topRightArea && m_adjAreaIndex < MAX_ADJ_AREAS )
  551. {
  552. m_adjAreaVector[ m_adjAreaIndex ].area = ladder->m_topRightArea;
  553. m_adjAreaVector[ m_adjAreaIndex ].how = GO_LADDER_UP;
  554. m_adjAreaVector[ m_adjAreaIndex ].ladder = ladder;
  555. ++m_adjAreaIndex;
  556. }
  557. }
  558. const NavLadderConnectVector &adjDownLadder = *area->GetLadders( CNavLadder::LADDER_DOWN );
  559. FOR_EACH_VEC( adjDownLadder, it )
  560. {
  561. CNavLadder *ladder = adjDownLadder[ it ].ladder;
  562. if ( m_adjAreaIndex >= MAX_ADJ_AREAS )
  563. break;
  564. if ( ladder->m_bottomArea )
  565. {
  566. m_adjAreaVector[ m_adjAreaIndex ].area = ladder->m_bottomArea;
  567. m_adjAreaVector[ m_adjAreaIndex ].how = GO_LADDER_DOWN;
  568. m_adjAreaVector[ m_adjAreaIndex ].ladder = ladder;
  569. ++m_adjAreaIndex;
  570. }
  571. }
  572. }
  573. enum { MAX_ADJ_AREAS = 64 };
  574. struct AdjInfo
  575. {
  576. CNavArea *area;
  577. CNavLadder *ladder;
  578. NavTraverseType how;
  579. };
  580. AdjInfo m_adjAreaVector[ MAX_ADJ_AREAS ];
  581. int m_adjAreaIndex;
  582. };
  583. inline float Path::GetLength( void ) const
  584. {
  585. if (m_segmentCount <= 0)
  586. {
  587. return 0.0f;
  588. }
  589. return m_path[ m_segmentCount-1 ].distanceFromStart;
  590. }
  591. inline bool Path::IsValid( void ) const
  592. {
  593. return (m_segmentCount > 0);
  594. }
  595. inline void Path::Invalidate( void )
  596. {
  597. m_segmentCount = 0;
  598. m_cursorPos = 0.0f;
  599. m_cursorData.pos = vec3_origin;
  600. m_cursorData.forward = Vector( 1.0f, 0, 0 );
  601. m_cursorData.curvature = 0.0f;
  602. m_cursorData.segmentPrior = NULL;
  603. m_isCursorDataDirty = true;
  604. m_subject = NULL;
  605. }
  606. inline const Path::Segment *Path::FirstSegment( void ) const
  607. {
  608. return (IsValid()) ? &m_path[0] : NULL;
  609. }
  610. inline const Path::Segment *Path::NextSegment( const Segment *currentSegment ) const
  611. {
  612. if (currentSegment == NULL || !IsValid())
  613. return NULL;
  614. int i = currentSegment - m_path;
  615. if (i < 0 || i >= m_segmentCount-1)
  616. {
  617. return NULL;
  618. }
  619. return &m_path[ i+1 ];
  620. }
  621. inline const Path::Segment *Path::PriorSegment( const Segment *currentSegment ) const
  622. {
  623. if (currentSegment == NULL || !IsValid())
  624. return NULL;
  625. int i = currentSegment - m_path;
  626. if (i < 1 || i >= m_segmentCount)
  627. {
  628. return NULL;
  629. }
  630. return &m_path[ i-1 ];
  631. }
  632. inline const Path::Segment *Path::LastSegment( void ) const
  633. {
  634. return ( IsValid() ) ? &m_path[ m_segmentCount-1 ] : NULL;
  635. }
  636. inline const Vector &Path::GetStartPosition( void ) const
  637. {
  638. return ( IsValid() ) ? m_path[ 0 ].pos : vec3_origin;
  639. }
  640. inline const Vector &Path::GetEndPosition( void ) const
  641. {
  642. return ( IsValid() ) ? m_path[ m_segmentCount-1 ].pos : vec3_origin;
  643. }
  644. inline CBaseCombatCharacter *Path::GetSubject( void ) const
  645. {
  646. return m_subject;
  647. }
  648. inline void Path::MoveCursorToStart( void )
  649. {
  650. m_cursorPos = 0.0f;
  651. m_isCursorDataDirty = true;
  652. }
  653. inline void Path::MoveCursorToEnd( void )
  654. {
  655. m_cursorPos = GetLength();
  656. m_isCursorDataDirty = true;
  657. }
  658. inline void Path::MoveCursor( float value, MoveCursorType type )
  659. {
  660. if ( type == PATH_ABSOLUTE_DISTANCE )
  661. {
  662. m_cursorPos = value;
  663. }
  664. else // relative distance
  665. {
  666. m_cursorPos += value;
  667. }
  668. if ( m_cursorPos < 0.0f )
  669. {
  670. m_cursorPos = 0.0f;
  671. }
  672. else if ( m_cursorPos > GetLength() )
  673. {
  674. m_cursorPos = GetLength();
  675. }
  676. m_isCursorDataDirty = true;
  677. }
  678. inline float Path::GetCursorPosition( void ) const
  679. {
  680. return m_cursorPos;
  681. }
  682. inline const Path::Segment *Path::GetCurrentGoal( void ) const
  683. {
  684. return NULL;
  685. }
  686. inline float Path::GetAge( void ) const
  687. {
  688. return m_ageTimer.GetElapsedTime();
  689. }
  690. #endif // _NEXT_BOT_PATH_H_