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.

870 lines
24 KiB

  1. // NextBotPath.h
  2. // Encapsulate and manipulate a path through the world
  3. // Author: Michael Booth, February 2006
  4. // Copyright (c) 2006 Turtle Rock Studios, Inc. - 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. //
  157. // Build actual path by following parent links back from subject area
  158. //
  159. // get count
  160. int count = 0;
  161. CNavArea *area;
  162. for( area = closestArea; area; area = area->GetParent() )
  163. {
  164. ++count;
  165. if ( area == startArea )
  166. {
  167. // startArea can be re-evaluated during the pathfind and given a parent...
  168. break;
  169. }
  170. }
  171. // save room for endpoint
  172. if ( count > MAX_PATH_SEGMENTS-1 )
  173. {
  174. count = MAX_PATH_SEGMENTS-1;
  175. }
  176. else if ( count == 0 )
  177. {
  178. return false;
  179. }
  180. if ( count == 1 )
  181. {
  182. BuildTrivialPath( bot, subjectPos );
  183. return pathResult;
  184. }
  185. // assemble path
  186. m_segmentCount = count;
  187. for( area = closestArea; count && area; area = area->GetParent() )
  188. {
  189. --count;
  190. m_path[ count ].area = area;
  191. m_path[ count ].how = area->GetParentHow();
  192. m_path[ count ].type = ON_GROUND;
  193. }
  194. if ( pathResult || includeGoalIfPathFails )
  195. {
  196. // append actual subject position
  197. m_path[ m_segmentCount ].area = closestArea;
  198. m_path[ m_segmentCount ].pos = subjectPos;
  199. m_path[ m_segmentCount ].ladder = NULL;
  200. m_path[ m_segmentCount ].how = NUM_TRAVERSE_TYPES;
  201. m_path[ m_segmentCount ].type = ON_GROUND;
  202. ++m_segmentCount;
  203. }
  204. // compute path positions
  205. if ( ComputePathDetails( bot, start ) == false )
  206. {
  207. Invalidate();
  208. OnPathChanged( bot, NO_PATH );
  209. return false;
  210. }
  211. // remove redundant nodes and clean up path
  212. Optimize( bot );
  213. PostProcess();
  214. OnPathChanged( bot, pathResult ? COMPLETE_PATH : PARTIAL_PATH );
  215. return pathResult;
  216. }
  217. //-----------------------------------------------------------------------------------------------------------------
  218. /**
  219. * Compute shortest path from bot to 'goal' via A* algorithm.
  220. * If returns true, path was found to the goal position.
  221. * If returns false, path may either be invalid (use IsValid() to check), or valid but
  222. * doesn't reach all the way to the goal.
  223. */
  224. template< typename CostFunctor >
  225. bool Compute( INextBot *bot, const Vector &goal, CostFunctor &costFunc, float maxPathLength = 0.0f, bool includeGoalIfPathFails = true )
  226. {
  227. VPROF_BUDGET( "Path::Compute(goal)", "NextBotSpiky" );
  228. Invalidate();
  229. const Vector &start = bot->GetPosition();
  230. CNavArea *startArea = bot->GetEntity()->GetLastKnownArea();
  231. if ( !startArea )
  232. {
  233. OnPathChanged( bot, NO_PATH );
  234. return false;
  235. }
  236. // check line-of-sight to the goal position when finding it's nav area
  237. const float maxDistanceToArea = 200.0f;
  238. CNavArea *goalArea = TheNavMesh->GetNearestNavArea( goal, true, maxDistanceToArea, true );
  239. // if we are already in the goal area, build trivial path
  240. if ( startArea == goalArea )
  241. {
  242. BuildTrivialPath( bot, goal );
  243. return true;
  244. }
  245. // make sure path end position is on the ground
  246. Vector pathEndPosition = goal;
  247. if ( goalArea )
  248. {
  249. pathEndPosition.z = goalArea->GetZ( pathEndPosition );
  250. }
  251. else
  252. {
  253. TheNavMesh->GetGroundHeight( pathEndPosition, &pathEndPosition.z );
  254. }
  255. //
  256. // Compute shortest path to goal
  257. //
  258. CNavArea *closestArea = NULL;
  259. bool pathResult = NavAreaBuildPath( startArea, goalArea, &goal, costFunc, &closestArea, maxPathLength, bot->GetEntity()->GetTeamNumber() );
  260. //
  261. // Build actual path by following parent links back from goal area
  262. //
  263. // get count
  264. int count = 0;
  265. CNavArea *area;
  266. for( area = closestArea; area; area = area->GetParent() )
  267. {
  268. ++count;
  269. if ( area == startArea )
  270. {
  271. // startArea can be re-evaluated during the pathfind and given a parent...
  272. break;
  273. }
  274. }
  275. // save room for endpoint
  276. if ( count > MAX_PATH_SEGMENTS-1 )
  277. {
  278. count = MAX_PATH_SEGMENTS-1;
  279. }
  280. else if ( count == 0 )
  281. {
  282. return false;
  283. }
  284. if ( count == 1 )
  285. {
  286. BuildTrivialPath( bot, goal );
  287. return pathResult;
  288. }
  289. // assemble path
  290. m_segmentCount = count;
  291. for( area = closestArea; count && area; area = area->GetParent() )
  292. {
  293. --count;
  294. m_path[ count ].area = area;
  295. m_path[ count ].how = area->GetParentHow();
  296. m_path[ count ].type = ON_GROUND;
  297. }
  298. if ( pathResult || includeGoalIfPathFails )
  299. {
  300. // append actual goal position
  301. m_path[ m_segmentCount ].area = closestArea;
  302. m_path[ m_segmentCount ].pos = pathEndPosition;
  303. m_path[ m_segmentCount ].ladder = NULL;
  304. m_path[ m_segmentCount ].how = NUM_TRAVERSE_TYPES;
  305. m_path[ m_segmentCount ].type = ON_GROUND;
  306. ++m_segmentCount;
  307. }
  308. // compute path positions
  309. if ( ComputePathDetails( bot, start ) == false )
  310. {
  311. Invalidate();
  312. OnPathChanged( bot, NO_PATH );
  313. return false;
  314. }
  315. // remove redundant nodes and clean up path
  316. Optimize( bot );
  317. PostProcess();
  318. OnPathChanged( bot, pathResult ? COMPLETE_PATH : PARTIAL_PATH );
  319. return pathResult;
  320. }
  321. //-----------------------------------------------------------------------------------------------------------------
  322. /**
  323. * Build a path from bot's current location to an undetermined goal area
  324. * that minimizes the given cost along the final path and meets the
  325. * goal criteria.
  326. */
  327. virtual bool ComputeWithOpenGoal( INextBot *bot, const IPathCost &costFunc, const IPathOpenGoalSelector &goalSelector, float maxSearchRadius = 0.0f )
  328. {
  329. VPROF_BUDGET( "ComputeWithOpenGoal", "NextBot" );
  330. int teamID = bot->GetEntity()->GetTeamNumber();
  331. CNavArea *startArea = bot->GetEntity()->GetLastKnownArea();
  332. if ( startArea == NULL )
  333. return NULL;
  334. startArea->SetParent( NULL );
  335. // start search
  336. CNavArea::ClearSearchLists();
  337. float initCost = costFunc( startArea, NULL, NULL, NULL, -1.0f );
  338. if ( initCost < 0.0f )
  339. return NULL;
  340. startArea->SetTotalCost( initCost );
  341. startArea->AddToOpenList();
  342. // find our goal as we search
  343. CNavArea *goalArea = NULL;
  344. //
  345. // Dijkstra's algorithm (since we don't know our goal).
  346. //
  347. while( !CNavArea::IsOpenListEmpty() )
  348. {
  349. // get next area to check
  350. CNavArea *area = CNavArea::PopOpenList();
  351. area->AddToClosedList();
  352. // don't consider blocked areas
  353. if ( area->IsBlocked( teamID ) )
  354. continue;
  355. // build adjacent area array
  356. CollectAdjacentAreas( area );
  357. // search adjacent areas
  358. for( int i=0; i<m_adjAreaIndex; ++i )
  359. {
  360. CNavArea *newArea = m_adjAreaVector[ i ].area;
  361. // only visit each area once
  362. if ( newArea->IsClosed() )
  363. continue;
  364. // don't consider blocked areas
  365. if ( newArea->IsBlocked( teamID ) )
  366. continue;
  367. // don't use this area if it is out of range
  368. if ( maxSearchRadius > 0.0f && ( newArea->GetCenter() - bot->GetEntity()->GetAbsOrigin() ).IsLengthGreaterThan( maxSearchRadius ) )
  369. continue;
  370. // determine cost of traversing this area
  371. float newCost = costFunc( newArea, area, m_adjAreaVector[ i ].ladder, NULL, -1.0f );
  372. // don't use adjacent area if cost functor says it is a dead-end
  373. if ( newCost < 0.0f )
  374. continue;
  375. if ( newArea->IsOpen() && newArea->GetTotalCost() <= newCost )
  376. {
  377. // we have already visited this area, and it has a better path
  378. continue;
  379. }
  380. else
  381. {
  382. // whether this area has been visited or not, we now have a better path to it
  383. newArea->SetParent( area, m_adjAreaVector[ i ].how );
  384. newArea->SetTotalCost( newCost );
  385. // use 'cost so far' to hold cumulative cost
  386. newArea->SetCostSoFar( newCost );
  387. // tricky bit here - relying on OpenList being sorted by cost
  388. if ( newArea->IsOpen() )
  389. {
  390. // area already on open list, update the list order to keep costs sorted
  391. newArea->UpdateOnOpenList();
  392. }
  393. else
  394. {
  395. newArea->AddToOpenList();
  396. }
  397. // keep track of best goal so far
  398. goalArea = goalSelector( goalArea, newArea );
  399. }
  400. }
  401. }
  402. if ( goalArea )
  403. {
  404. // compile the path details into a usable path
  405. AssemblePrecomputedPath( bot, goalArea->GetCenter(), goalArea );
  406. return true;
  407. }
  408. // all adjacent areas are likely too far away
  409. return false;
  410. }
  411. //-----------------------------------------------------------------------------------------------------------------
  412. /**
  413. * Given the last area in a path with valid parent pointers,
  414. * construct the actual path.
  415. */
  416. void AssemblePrecomputedPath( INextBot *bot, const Vector &goal, CNavArea *endArea )
  417. {
  418. VPROF_BUDGET( "AssemblePrecomputedPath", "NextBot" );
  419. const Vector &start = bot->GetPosition();
  420. // get count
  421. int count = 0;
  422. CNavArea *area;
  423. for( area = endArea; area; area = area->GetParent() )
  424. {
  425. ++count;
  426. }
  427. // save room for endpoint
  428. if ( count > MAX_PATH_SEGMENTS-1 )
  429. {
  430. count = MAX_PATH_SEGMENTS-1;
  431. }
  432. else if ( count == 0 )
  433. {
  434. return;
  435. }
  436. if ( count == 1 )
  437. {
  438. BuildTrivialPath( bot, goal );
  439. return;
  440. }
  441. // assemble path
  442. m_segmentCount = count;
  443. for( area = endArea; count && area; area = area->GetParent() )
  444. {
  445. --count;
  446. m_path[ count ].area = area;
  447. m_path[ count ].how = area->GetParentHow();
  448. m_path[ count ].type = ON_GROUND;
  449. }
  450. // append actual goal position
  451. m_path[ m_segmentCount ].area = endArea;
  452. m_path[ m_segmentCount ].pos = goal;
  453. m_path[ m_segmentCount ].ladder = NULL;
  454. m_path[ m_segmentCount ].how = NUM_TRAVERSE_TYPES;
  455. m_path[ m_segmentCount ].type = ON_GROUND;
  456. ++m_segmentCount;
  457. // compute path positions
  458. if ( ComputePathDetails( bot, start ) == false )
  459. {
  460. Invalidate();
  461. OnPathChanged( bot, NO_PATH );
  462. return;
  463. }
  464. // remove redundant nodes and clean up path
  465. Optimize( bot );
  466. PostProcess();
  467. OnPathChanged( bot, COMPLETE_PATH );
  468. }
  469. /**
  470. * Utility function for when start and goal are in the same area
  471. */
  472. bool BuildTrivialPath( INextBot *bot, const Vector &goal );
  473. /**
  474. * Determine exactly where the path goes between the given two areas
  475. * on the path. Return this point in 'crossPos'.
  476. */
  477. virtual void ComputeAreaCrossing( INextBot *bot, const CNavArea *from, const Vector &fromPos, const CNavArea *to, NavDirType dir, Vector *crossPos ) const;
  478. private:
  479. enum { MAX_PATH_SEGMENTS = 256 };
  480. Segment m_path[ MAX_PATH_SEGMENTS ];
  481. int m_segmentCount;
  482. bool ComputePathDetails( INextBot *bot, const Vector &start ); // determine actual path positions
  483. void Optimize( INextBot *bot );
  484. void PostProcess( void );
  485. int FindNextOccludedNode( INextBot *bot, int anchor ); // used by Optimize()
  486. void InsertSegment( Segment newSegment, int i ); // insert new segment at index i
  487. mutable Vector m_pathPos; // used by GetPosition()
  488. mutable Vector m_closePos; // used by GetClosestPosition()
  489. mutable float m_cursorPos; // current cursor position (distance along path)
  490. mutable Data m_cursorData; // used by GetCursorData()
  491. mutable bool m_isCursorDataDirty;
  492. IntervalTimer m_ageTimer; // how old is this path?
  493. CHandle< CBaseCombatCharacter > m_subject; // the subject this path leads to
  494. /**
  495. * Build a vector of adjacent areas reachable from the given area
  496. */
  497. void CollectAdjacentAreas( CNavArea *area )
  498. {
  499. m_adjAreaIndex = 0;
  500. const NavConnectVector &adjNorth = *area->GetAdjacentAreas( NORTH );
  501. FOR_EACH_VEC( adjNorth, it )
  502. {
  503. if ( m_adjAreaIndex >= MAX_ADJ_AREAS )
  504. break;
  505. m_adjAreaVector[ m_adjAreaIndex ].area = adjNorth[ it ].area;
  506. m_adjAreaVector[ m_adjAreaIndex ].how = GO_NORTH;
  507. m_adjAreaVector[ m_adjAreaIndex ].ladder = NULL;
  508. ++m_adjAreaIndex;
  509. }
  510. const NavConnectVector &adjSouth = *area->GetAdjacentAreas( SOUTH );
  511. FOR_EACH_VEC( adjSouth, it )
  512. {
  513. if ( m_adjAreaIndex >= MAX_ADJ_AREAS )
  514. break;
  515. m_adjAreaVector[ m_adjAreaIndex ].area = adjSouth[ it ].area;
  516. m_adjAreaVector[ m_adjAreaIndex ].how = GO_SOUTH;
  517. m_adjAreaVector[ m_adjAreaIndex ].ladder = NULL;
  518. ++m_adjAreaIndex;
  519. }
  520. const NavConnectVector &adjWest = *area->GetAdjacentAreas( WEST );
  521. FOR_EACH_VEC( adjWest, it )
  522. {
  523. if ( m_adjAreaIndex >= MAX_ADJ_AREAS )
  524. break;
  525. m_adjAreaVector[ m_adjAreaIndex ].area = adjWest[ it ].area;
  526. m_adjAreaVector[ m_adjAreaIndex ].how = GO_WEST;
  527. m_adjAreaVector[ m_adjAreaIndex ].ladder = NULL;
  528. ++m_adjAreaIndex;
  529. }
  530. const NavConnectVector &adjEast = *area->GetAdjacentAreas( EAST );
  531. FOR_EACH_VEC( adjEast, it )
  532. {
  533. if ( m_adjAreaIndex >= MAX_ADJ_AREAS )
  534. break;
  535. m_adjAreaVector[ m_adjAreaIndex ].area = adjEast[ it ].area;
  536. m_adjAreaVector[ m_adjAreaIndex ].how = GO_EAST;
  537. m_adjAreaVector[ m_adjAreaIndex ].ladder = NULL;
  538. ++m_adjAreaIndex;
  539. }
  540. const NavLadderConnectVector &adjUpLadder = *area->GetLadders( CNavLadder::LADDER_UP );
  541. FOR_EACH_VEC( adjUpLadder, it )
  542. {
  543. CNavLadder *ladder = adjUpLadder[ it ].ladder;
  544. if ( ladder->m_topForwardArea && m_adjAreaIndex < MAX_ADJ_AREAS )
  545. {
  546. m_adjAreaVector[ m_adjAreaIndex ].area = ladder->m_topForwardArea;
  547. m_adjAreaVector[ m_adjAreaIndex ].how = GO_LADDER_UP;
  548. m_adjAreaVector[ m_adjAreaIndex ].ladder = ladder;
  549. ++m_adjAreaIndex;
  550. }
  551. if ( ladder->m_topLeftArea && m_adjAreaIndex < MAX_ADJ_AREAS )
  552. {
  553. m_adjAreaVector[ m_adjAreaIndex ].area = ladder->m_topLeftArea;
  554. m_adjAreaVector[ m_adjAreaIndex ].how = GO_LADDER_UP;
  555. m_adjAreaVector[ m_adjAreaIndex ].ladder = ladder;
  556. ++m_adjAreaIndex;
  557. }
  558. if ( ladder->m_topRightArea && m_adjAreaIndex < MAX_ADJ_AREAS )
  559. {
  560. m_adjAreaVector[ m_adjAreaIndex ].area = ladder->m_topRightArea;
  561. m_adjAreaVector[ m_adjAreaIndex ].how = GO_LADDER_UP;
  562. m_adjAreaVector[ m_adjAreaIndex ].ladder = ladder;
  563. ++m_adjAreaIndex;
  564. }
  565. }
  566. const NavLadderConnectVector &adjDownLadder = *area->GetLadders( CNavLadder::LADDER_DOWN );
  567. FOR_EACH_VEC( adjDownLadder, it )
  568. {
  569. CNavLadder *ladder = adjDownLadder[ it ].ladder;
  570. if ( m_adjAreaIndex >= MAX_ADJ_AREAS )
  571. break;
  572. if ( ladder->m_bottomArea )
  573. {
  574. m_adjAreaVector[ m_adjAreaIndex ].area = ladder->m_bottomArea;
  575. m_adjAreaVector[ m_adjAreaIndex ].how = GO_LADDER_DOWN;
  576. m_adjAreaVector[ m_adjAreaIndex ].ladder = ladder;
  577. ++m_adjAreaIndex;
  578. }
  579. }
  580. }
  581. enum { MAX_ADJ_AREAS = 64 };
  582. struct AdjInfo
  583. {
  584. CNavArea *area;
  585. CNavLadder *ladder;
  586. NavTraverseType how;
  587. };
  588. AdjInfo m_adjAreaVector[ MAX_ADJ_AREAS ];
  589. int m_adjAreaIndex;
  590. };
  591. inline float Path::GetLength( void ) const
  592. {
  593. if (m_segmentCount <= 0)
  594. {
  595. return 0.0f;
  596. }
  597. return m_path[ m_segmentCount-1 ].distanceFromStart;
  598. }
  599. inline bool Path::IsValid( void ) const
  600. {
  601. return (m_segmentCount > 0);
  602. }
  603. inline void Path::Invalidate( void )
  604. {
  605. m_segmentCount = 0;
  606. m_cursorPos = 0.0f;
  607. m_cursorData.pos = vec3_origin;
  608. m_cursorData.forward = Vector( 1.0f, 0, 0 );
  609. m_cursorData.curvature = 0.0f;
  610. m_cursorData.segmentPrior = NULL;
  611. m_isCursorDataDirty = true;
  612. m_subject = NULL;
  613. }
  614. inline const Path::Segment *Path::FirstSegment( void ) const
  615. {
  616. return (IsValid()) ? &m_path[0] : NULL;
  617. }
  618. inline const Path::Segment *Path::NextSegment( const Segment *currentSegment ) const
  619. {
  620. if (currentSegment == NULL || !IsValid())
  621. return NULL;
  622. int i = currentSegment - m_path;
  623. if (i < 0 || i >= m_segmentCount-1)
  624. {
  625. return NULL;
  626. }
  627. return &m_path[ i+1 ];
  628. }
  629. inline const Path::Segment *Path::PriorSegment( const Segment *currentSegment ) const
  630. {
  631. if (currentSegment == NULL || !IsValid())
  632. return NULL;
  633. int i = currentSegment - m_path;
  634. if (i < 1 || i >= m_segmentCount)
  635. {
  636. return NULL;
  637. }
  638. return &m_path[ i-1 ];
  639. }
  640. inline const Path::Segment *Path::LastSegment( void ) const
  641. {
  642. return ( IsValid() ) ? &m_path[ m_segmentCount-1 ] : NULL;
  643. }
  644. inline const Vector &Path::GetStartPosition( void ) const
  645. {
  646. return ( IsValid() ) ? m_path[ 0 ].pos : vec3_origin;
  647. }
  648. inline const Vector &Path::GetEndPosition( void ) const
  649. {
  650. return ( IsValid() ) ? m_path[ m_segmentCount-1 ].pos : vec3_origin;
  651. }
  652. inline CBaseCombatCharacter *Path::GetSubject( void ) const
  653. {
  654. return m_subject;
  655. }
  656. inline void Path::MoveCursorToStart( void )
  657. {
  658. m_cursorPos = 0.0f;
  659. m_isCursorDataDirty = true;
  660. }
  661. inline void Path::MoveCursorToEnd( void )
  662. {
  663. m_cursorPos = GetLength();
  664. m_isCursorDataDirty = true;
  665. }
  666. inline void Path::MoveCursor( float value, MoveCursorType type )
  667. {
  668. if ( type == PATH_ABSOLUTE_DISTANCE )
  669. {
  670. m_cursorPos = value;
  671. }
  672. else // relative distance
  673. {
  674. m_cursorPos += value;
  675. }
  676. if ( m_cursorPos < 0.0f )
  677. {
  678. m_cursorPos = 0.0f;
  679. }
  680. else if ( m_cursorPos > GetLength() )
  681. {
  682. m_cursorPos = GetLength();
  683. }
  684. m_isCursorDataDirty = true;
  685. }
  686. inline float Path::GetCursorPosition( void ) const
  687. {
  688. return m_cursorPos;
  689. }
  690. inline const Path::Segment *Path::GetCurrentGoal( void ) const
  691. {
  692. return NULL;
  693. }
  694. inline float Path::GetAge( void ) const
  695. {
  696. return m_ageTimer.GetElapsedTime();
  697. }
  698. #endif // _NEXT_BOT_PATH_H_