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.

1208 lines
30 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. // nav_path.cpp
  9. // Encapsulation of a path through space
  10. // Author: Michael S. Booth ([email protected]), November 2003
  11. #include "cbase.h"
  12. #include "cs_gamerules.h"
  13. #include "cs_player.h"
  14. #include "nav_mesh.h"
  15. #include "cs_nav_path.h"
  16. #include "bot_util.h"
  17. #include "improv_locomotor.h"
  18. // memdbgon must be the last include file in a .cpp file!!!
  19. #include "tier0/memdbgon.h"
  20. #ifdef _WIN32
  21. #pragma warning (disable:4701) // disable warning that variable *may* not be initialized
  22. #endif
  23. #define DrawLine( from, to, duration, red, green, blue ) NDebugOverlay::Line( from, to, red, green, blue, true, 0.1f )
  24. //--------------------------------------------------------------------------------------------------------------
  25. /**
  26. * Determine actual path positions
  27. */
  28. bool CCSNavPath::ComputePathPositions( void )
  29. {
  30. if (m_segmentCount == 0)
  31. return false;
  32. // start in first area's center
  33. m_path[0].pos = m_path[0].area->GetCenter();
  34. m_path[0].ladder = NULL;
  35. m_path[0].how = NUM_TRAVERSE_TYPES;
  36. for( int i=1; i<m_segmentCount; ++i )
  37. {
  38. const PathSegment *from = &m_path[ i-1 ];
  39. PathSegment *to = &m_path[ i ];
  40. if (to->how <= GO_WEST) // walk along the floor to the next area
  41. {
  42. to->ladder = NULL;
  43. // compute next point, keeping path as straight as possible
  44. from->area->ComputeClosestPointInPortal( to->area, (NavDirType)to->how, from->pos, &to->pos );
  45. // move goal position into the goal area a bit
  46. const float stepInDist = 5.0f; // how far to "step into" an area - must be less than min area size
  47. AddDirectionVector( &to->pos, (NavDirType)to->how, stepInDist );
  48. // we need to walk out of "from" area, so keep Z where we can reach it
  49. to->pos.z = from->area->GetZ( to->pos );
  50. // if this is a "jump down" connection, we must insert an additional point on the path
  51. if (to->area->IsConnected( from->area, NUM_DIRECTIONS ) == false)
  52. {
  53. // this is a "jump down" link
  54. // compute direction of path just prior to "jump down"
  55. Vector2D dir;
  56. DirectionToVector2D( (NavDirType)to->how, &dir );
  57. // shift top of "jump down" out a bit to "get over the ledge"
  58. const float pushDist = 25.0f;
  59. to->pos.x += pushDist * dir.x;
  60. to->pos.y += pushDist * dir.y;
  61. // insert a duplicate node to represent the bottom of the fall
  62. if (m_segmentCount < MAX_PATH_SEGMENTS-1)
  63. {
  64. // copy nodes down
  65. for( int j=m_segmentCount; j>i; --j )
  66. m_path[j] = m_path[j-1];
  67. // path is one node longer
  68. ++m_segmentCount;
  69. // move index ahead into the new node we just duplicated
  70. ++i;
  71. m_path[i].pos.x = to->pos.x + pushDist * dir.x;
  72. m_path[i].pos.y = to->pos.y + pushDist * dir.y;
  73. // put this one at the bottom of the fall
  74. m_path[i].pos.z = to->area->GetZ( m_path[i].pos );
  75. }
  76. }
  77. }
  78. else if (to->how == GO_LADDER_UP) // to get to next area, must go up a ladder
  79. {
  80. // find our ladder
  81. const NavLadderConnectVector *ladders = from->area->GetLadders( CNavLadder::LADDER_UP );
  82. int it;
  83. for( it=0; it<ladders->Count(); ++it )
  84. {
  85. CNavLadder *ladder = (*ladders)[ it ].ladder;
  86. // can't use "behind" area when ascending...
  87. if (ladder->m_topForwardArea == to->area ||
  88. ladder->m_topLeftArea == to->area ||
  89. ladder->m_topRightArea == to->area)
  90. {
  91. to->ladder = ladder;
  92. to->pos = ladder->m_bottom + ladder->GetNormal() * 2.0f * HalfHumanWidth;
  93. break;
  94. }
  95. }
  96. if (it == ladders->Count())
  97. {
  98. //PrintIfWatched( "ERROR: Can't find ladder in path\n" );
  99. return false;
  100. }
  101. }
  102. else if (to->how == GO_LADDER_DOWN) // to get to next area, must go down a ladder
  103. {
  104. // find our ladder
  105. const NavLadderConnectVector *ladders = from->area->GetLadders( CNavLadder::LADDER_DOWN );
  106. int it;
  107. for( it=0; it<ladders->Count(); ++it )
  108. {
  109. CNavLadder *ladder = (*ladders)[ it ].ladder;
  110. if (ladder->m_bottomArea == to->area)
  111. {
  112. to->ladder = ladder;
  113. to->pos = ladder->m_top;
  114. to->pos = ladder->m_top - ladder->GetNormal() * 2.0f * HalfHumanWidth;
  115. break;
  116. }
  117. }
  118. if (it == ladders->Count())
  119. {
  120. //PrintIfWatched( "ERROR: Can't find ladder in path\n" );
  121. return false;
  122. }
  123. }
  124. }
  125. return true;
  126. }
  127. //--------------------------------------------------------------------------------------------------------------
  128. /**
  129. * Return true if position is at the end of the path
  130. */
  131. bool CCSNavPath::IsAtEnd( const Vector &pos ) const
  132. {
  133. if (!IsValid())
  134. return false;
  135. const float epsilon = 20.0f;
  136. return (pos - GetEndpoint()).IsLengthLessThan( epsilon );
  137. }
  138. //--------------------------------------------------------------------------------------------------------------
  139. /**
  140. * Return length of path from start to finish
  141. */
  142. float CCSNavPath::GetLength( void ) const
  143. {
  144. float length = 0.0f;
  145. for( int i=1; i<GetSegmentCount(); ++i )
  146. {
  147. length += (m_path[i].pos - m_path[i-1].pos).Length();
  148. }
  149. return length;
  150. }
  151. //--------------------------------------------------------------------------------------------------------------
  152. /**
  153. * Return point a given distance along the path - if distance is out of path bounds, point is clamped to start/end
  154. * @todo Be careful of returning "positions" along one-way drops, ladders, etc.
  155. */
  156. bool CCSNavPath::GetPointAlongPath( float distAlong, Vector *pointOnPath ) const
  157. {
  158. if (!IsValid() || pointOnPath == NULL)
  159. return false;
  160. if (distAlong <= 0.0f)
  161. {
  162. *pointOnPath = m_path[0].pos;
  163. return true;
  164. }
  165. float lengthSoFar = 0.0f;
  166. float segmentLength;
  167. Vector dir;
  168. for( int i=1; i<GetSegmentCount(); ++i )
  169. {
  170. dir = m_path[i].pos - m_path[i-1].pos;
  171. segmentLength = dir.Length();
  172. if (segmentLength + lengthSoFar >= distAlong)
  173. {
  174. // desired point is on this segment of the path
  175. float delta = distAlong - lengthSoFar;
  176. float t = delta / segmentLength;
  177. *pointOnPath = m_path[i].pos + t * dir;
  178. return true;
  179. }
  180. lengthSoFar += segmentLength;
  181. }
  182. *pointOnPath = m_path[ GetSegmentCount()-1 ].pos;
  183. return true;
  184. }
  185. //--------------------------------------------------------------------------------------------------------------
  186. /**
  187. * Return the node index closest to the given distance along the path without going over - returns (-1) if error
  188. */
  189. int CCSNavPath::GetSegmentIndexAlongPath( float distAlong ) const
  190. {
  191. if (!IsValid())
  192. return -1;
  193. if (distAlong <= 0.0f)
  194. {
  195. return 0;
  196. }
  197. float lengthSoFar = 0.0f;
  198. Vector dir;
  199. for( int i=1; i<GetSegmentCount(); ++i )
  200. {
  201. lengthSoFar += (m_path[i].pos - m_path[i-1].pos).Length();
  202. if (lengthSoFar > distAlong)
  203. {
  204. return i-1;
  205. }
  206. }
  207. return GetSegmentCount()-1;
  208. }
  209. //--------------------------------------------------------------------------------------------------------------
  210. /**
  211. * Compute closest point on path to given point
  212. * NOTE: This does not do line-of-sight tests, so closest point may be thru the floor, etc
  213. */
  214. bool CCSNavPath::FindClosestPointOnPath( const Vector *worldPos, int startIndex, int endIndex, Vector *close ) const
  215. {
  216. if (!IsValid() || close == NULL)
  217. return false;
  218. Vector along, toWorldPos;
  219. Vector pos;
  220. const Vector *from, *to;
  221. float length;
  222. float closeLength;
  223. float closeDistSq = 9999999999.9;
  224. float distSq;
  225. for( int i=startIndex; i<=endIndex; ++i )
  226. {
  227. from = &m_path[i-1].pos;
  228. to = &m_path[i].pos;
  229. // compute ray along this path segment
  230. along = *to - *from;
  231. // make it a unit vector along the path
  232. length = along.NormalizeInPlace();
  233. // compute vector from start of segment to our point
  234. toWorldPos = *worldPos - *from;
  235. // find distance of closest point on ray
  236. closeLength = DotProduct( toWorldPos, along );
  237. // constrain point to be on path segment
  238. if (closeLength <= 0.0f)
  239. pos = *from;
  240. else if (closeLength >= length)
  241. pos = *to;
  242. else
  243. pos = *from + closeLength * along;
  244. distSq = (pos - *worldPos).LengthSqr();
  245. // keep the closest point so far
  246. if (distSq < closeDistSq)
  247. {
  248. closeDistSq = distSq;
  249. *close = pos;
  250. }
  251. }
  252. return true;
  253. }
  254. //--------------------------------------------------------------------------------------------------------------
  255. /**
  256. * Build trivial path when start and goal are in the same nav area
  257. */
  258. bool CCSNavPath::BuildTrivialPath( const Vector &start, const Vector &goal )
  259. {
  260. m_segmentCount = 0;
  261. CNavArea *startArea = TheNavMesh->GetNearestNavArea( start );
  262. if (startArea == NULL)
  263. return false;
  264. CNavArea *goalArea = TheNavMesh->GetNearestNavArea( goal );
  265. if (goalArea == NULL)
  266. return false;
  267. m_segmentCount = 2;
  268. m_path[0].area = startArea;
  269. m_path[0].pos.x = start.x;
  270. m_path[0].pos.y = start.y;
  271. m_path[0].pos.z = startArea->GetZ( start );
  272. m_path[0].ladder = NULL;
  273. m_path[0].how = NUM_TRAVERSE_TYPES;
  274. m_path[1].area = goalArea;
  275. m_path[1].pos.x = goal.x;
  276. m_path[1].pos.y = goal.y;
  277. m_path[1].pos.z = goalArea->GetZ( goal );
  278. m_path[1].ladder = NULL;
  279. m_path[1].how = NUM_TRAVERSE_TYPES;
  280. return true;
  281. }
  282. //--------------------------------------------------------------------------------------------------------------
  283. /**
  284. * Draw the path for debugging.
  285. */
  286. void CCSNavPath::Draw( const Vector &color )
  287. {
  288. if (!IsValid())
  289. return;
  290. for( int i=1; i<m_segmentCount; ++i )
  291. {
  292. DrawLine( m_path[i-1].pos + Vector( 0, 0, HalfHumanHeight ),
  293. m_path[i].pos + Vector( 0, 0, HalfHumanHeight ), 2, 255 * color.x, 255 * color.y, 255 * color.z );
  294. }
  295. }
  296. //--------------------------------------------------------------------------------------------------------------
  297. /**
  298. * Check line of sight from 'anchor' node on path to subsequent nodes until
  299. * we find a node that can't been seen from 'anchor'.
  300. */
  301. int CCSNavPath::FindNextOccludedNode( int anchor )
  302. {
  303. for( int i=anchor+1; i<m_segmentCount; ++i )
  304. {
  305. // don't remove ladder nodes
  306. if (m_path[i].ladder)
  307. return i;
  308. if (!IsWalkableTraceLineClear( m_path[ anchor ].pos, m_path[ i ].pos ))
  309. {
  310. // cant see this node from anchor node
  311. return i;
  312. }
  313. Vector anchorPlusHalf = m_path[ anchor ].pos + Vector( 0, 0, HalfHumanHeight );
  314. Vector iPlusHalf = m_path[ i ].pos +Vector( 0, 0, HalfHumanHeight );
  315. if (!IsWalkableTraceLineClear( anchorPlusHalf, iPlusHalf) )
  316. {
  317. // cant see this node from anchor node
  318. return i;
  319. }
  320. Vector anchorPlusFull = m_path[ anchor ].pos + Vector( 0, 0, HumanHeight );
  321. Vector iPlusFull = m_path[ i ].pos + Vector( 0, 0, HumanHeight );
  322. if (!IsWalkableTraceLineClear( anchorPlusFull, iPlusFull ))
  323. {
  324. // cant see this node from anchor node
  325. return i;
  326. }
  327. }
  328. return m_segmentCount;
  329. }
  330. //--------------------------------------------------------------------------------------------------------------
  331. /**
  332. * Smooth out path, removing redundant nodes
  333. */
  334. void CCSNavPath::Optimize( void )
  335. {
  336. // DONT USE THIS: Optimizing the path results in cutting thru obstacles
  337. return;
  338. if (m_segmentCount < 3)
  339. return;
  340. int anchor = 0;
  341. while( anchor < m_segmentCount )
  342. {
  343. int occluded = FindNextOccludedNode( anchor );
  344. int nextAnchor = occluded-1;
  345. if (nextAnchor > anchor)
  346. {
  347. // remove redundant nodes between anchor and nextAnchor
  348. int removeCount = nextAnchor - anchor - 1;
  349. if (removeCount > 0)
  350. {
  351. for( int i=nextAnchor; i<m_segmentCount; ++i )
  352. {
  353. m_path[i-removeCount] = m_path[i];
  354. }
  355. m_segmentCount -= removeCount;
  356. }
  357. }
  358. ++anchor;
  359. }
  360. }
  361. //--------------------------------------------------------------------------------------------------------------
  362. //--------------------------------------------------------------------------------------------------------------
  363. /**
  364. * Constructor
  365. */
  366. CNavPathFollower::CNavPathFollower( void )
  367. {
  368. m_improv = NULL;
  369. m_path = NULL;
  370. m_segmentIndex = 0;
  371. m_isLadderStarted = false;
  372. m_isDebug = false;
  373. }
  374. void CNavPathFollower::Reset( void )
  375. {
  376. m_segmentIndex = 1;
  377. m_isLadderStarted = false;
  378. m_stuckMonitor.Reset();
  379. }
  380. //--------------------------------------------------------------------------------------------------------------
  381. /**
  382. * Move improv along path
  383. */
  384. void CNavPathFollower::Update( float deltaT, bool avoidObstacles )
  385. {
  386. if (m_path == NULL || m_path->IsValid() == false)
  387. return;
  388. const CCSNavPath::PathSegment *node = (*m_path)[ m_segmentIndex ];
  389. if (node == NULL)
  390. {
  391. m_improv->OnMoveToFailure( m_path->GetEndpoint(), CImprovLocomotor::FAIL_INVALID_PATH );
  392. m_path->Invalidate();
  393. return;
  394. }
  395. // handle ladders
  396. /*
  397. if (node->ladder)
  398. {
  399. const Vector *approachPos = NULL;
  400. const Vector *departPos = NULL;
  401. if (m_segmentIndex)
  402. approachPos = &(*m_path)[ m_segmentIndex-1 ]->pos;
  403. if (m_segmentIndex < m_path->GetSegmentCount()-1)
  404. departPos = &(*m_path)[ m_segmentIndex+1 ]->pos;
  405. if (!m_isLadderStarted)
  406. {
  407. // set up ladder movement
  408. m_improv->StartLadder( node->ladder, node->how, approachPos, departPos );
  409. m_isLadderStarted = true;
  410. }
  411. // move improv along ladder
  412. if (m_improv->TraverseLadder( node->ladder, node->how, approachPos, departPos, deltaT ))
  413. {
  414. // completed ladder
  415. ++m_segmentIndex;
  416. }
  417. return;
  418. }
  419. */
  420. // reset ladder init flag
  421. m_isLadderStarted = false;
  422. //
  423. // Check if we reached the end of the path
  424. //
  425. const float closeRange = 20.0f;
  426. if ((m_improv->GetFeet() - node->pos).IsLengthLessThan( closeRange ))
  427. {
  428. ++m_segmentIndex;
  429. if (m_segmentIndex >= m_path->GetSegmentCount())
  430. {
  431. m_improv->OnMoveToSuccess( m_path->GetEndpoint() );
  432. m_path->Invalidate();
  433. return;
  434. }
  435. }
  436. m_goal = node->pos;
  437. const float aheadRange = 300.0f;
  438. m_segmentIndex = FindPathPoint( aheadRange, &m_goal, &m_behindIndex );
  439. if (m_segmentIndex >= m_path->GetSegmentCount())
  440. m_segmentIndex = m_path->GetSegmentCount()-1;
  441. bool isApproachingJumpArea = false;
  442. //
  443. // Crouching
  444. //
  445. if (!m_improv->IsUsingLadder())
  446. {
  447. // because hostage crouching is not really supported by the engine,
  448. // if we are standing in a crouch area, we must crouch to avoid collisions
  449. if (m_improv->GetLastKnownArea() &&
  450. m_improv->GetLastKnownArea()->GetAttributes() & NAV_MESH_CROUCH &&
  451. !(m_improv->GetLastKnownArea()->GetAttributes() & NAV_MESH_JUMP))
  452. {
  453. m_improv->Crouch();
  454. }
  455. // if we are approaching a crouch area, crouch
  456. // if there are no crouch areas coming up, stand
  457. const float crouchRange = 50.0f;
  458. bool didCrouch = false;
  459. for( int i=m_segmentIndex; i<m_path->GetSegmentCount(); ++i )
  460. {
  461. const CNavArea *to = (*m_path)[i]->area;
  462. // if there is a jump area on the way to the crouch area, don't crouch as it messes up the jump
  463. if (to->GetAttributes() & NAV_MESH_JUMP)
  464. {
  465. isApproachingJumpArea = true;
  466. break;
  467. }
  468. Vector close;
  469. to->GetClosestPointOnArea( m_improv->GetCentroid(), &close );
  470. if ((close - m_improv->GetFeet()).AsVector2D().IsLengthGreaterThan( crouchRange ))
  471. break;
  472. if (to->GetAttributes() & NAV_MESH_CROUCH)
  473. {
  474. m_improv->Crouch();
  475. didCrouch = true;
  476. break;
  477. }
  478. }
  479. if (!didCrouch && !m_improv->IsJumping())
  480. {
  481. // no crouch areas coming up
  482. m_improv->StandUp();
  483. }
  484. } // end crouching logic
  485. if (m_isDebug)
  486. {
  487. m_path->Draw();
  488. UTIL_DrawBeamPoints( m_improv->GetCentroid(), m_goal + Vector( 0, 0, StepHeight ), 1, 255, 0, 255 );
  489. UTIL_DrawBeamPoints( m_goal + Vector( 0, 0, StepHeight ), m_improv->GetCentroid(), 1, 255, 0, 255 );
  490. }
  491. // check if improv becomes stuck
  492. m_stuckMonitor.Update( m_improv );
  493. // if improv has been stuck for too long, give up
  494. const float giveUpTime = 2.0f;
  495. if (m_stuckMonitor.GetDuration() > giveUpTime)
  496. {
  497. m_improv->OnMoveToFailure( m_path->GetEndpoint(), CImprovLocomotor::FAIL_STUCK );
  498. m_path->Invalidate();
  499. return;
  500. }
  501. // if our goal is high above us, we must have fallen
  502. if (m_goal.z - m_improv->GetFeet().z > JumpCrouchHeight)
  503. {
  504. const float closeRange = 75.0f;
  505. Vector2D to( m_improv->GetFeet().x - m_goal.x, m_improv->GetFeet().y - m_goal.y );
  506. if (to.IsLengthLessThan( closeRange ))
  507. {
  508. // we can't reach the goal position
  509. // check if we can reach the next node, in case this was a "jump down" situation
  510. const CCSNavPath::PathSegment *nextNode = (*m_path)[ m_behindIndex+1 ];
  511. if (m_behindIndex >=0 && nextNode)
  512. {
  513. if (nextNode->pos.z - m_improv->GetFeet().z > JumpCrouchHeight)
  514. {
  515. // the next node is too high, too - we really did fall of the path
  516. m_improv->OnMoveToFailure( m_path->GetEndpoint(), CImprovLocomotor::FAIL_FELL_OFF );
  517. m_path->Invalidate();
  518. return;
  519. }
  520. }
  521. else
  522. {
  523. // fell trying to get to the last node in the path
  524. m_improv->OnMoveToFailure( m_path->GetEndpoint(), CImprovLocomotor::FAIL_FELL_OFF );
  525. m_path->Invalidate();
  526. return;
  527. }
  528. }
  529. }
  530. // avoid small obstacles
  531. if (avoidObstacles && !isApproachingJumpArea && !m_improv->IsJumping() && m_segmentIndex < m_path->GetSegmentCount()-1)
  532. {
  533. FeelerReflexAdjustment( &m_goal );
  534. // currently, this is only used for hostages, and their collision physics stinks
  535. // do more feeler checks to avoid short obstacles
  536. /*
  537. const float inc = 0.25f;
  538. for( float t = 0.5f; t < 1.0f; t += inc )
  539. {
  540. FeelerReflexAdjustment( &m_goal, t * StepHeight );
  541. }
  542. */
  543. }
  544. // move improv along path
  545. m_improv->TrackPath( m_goal, deltaT );
  546. }
  547. //--------------------------------------------------------------------------------------------------------------
  548. /**
  549. * Return the closest point to our current position on our current path
  550. * If "local" is true, only check the portion of the path surrounding m_pathIndex.
  551. */
  552. int CNavPathFollower::FindOurPositionOnPath( Vector *close, bool local ) const
  553. {
  554. if (!m_path->IsValid())
  555. return -1;
  556. Vector along, toFeet;
  557. Vector feet = m_improv->GetFeet();
  558. Vector eyes = m_improv->GetEyes();
  559. Vector pos;
  560. const Vector *from, *to;
  561. float length;
  562. float closeLength;
  563. float closeDistSq = 9999999999.9;
  564. int closeIndex = -1;
  565. float distSq;
  566. int start, end;
  567. if (local)
  568. {
  569. start = m_segmentIndex - 3;
  570. if (start < 1)
  571. start = 1;
  572. end = m_segmentIndex + 3;
  573. if (end > m_path->GetSegmentCount())
  574. end = m_path->GetSegmentCount();
  575. }
  576. else
  577. {
  578. start = 1;
  579. end = m_path->GetSegmentCount();
  580. }
  581. for( int i=start; i<end; ++i )
  582. {
  583. from = &(*m_path)[i-1]->pos;
  584. to = &(*m_path)[i]->pos;
  585. // compute ray along this path segment
  586. along = *to - *from;
  587. // make it a unit vector along the path
  588. length = along.NormalizeInPlace();
  589. // compute vector from start of segment to our point
  590. toFeet = feet - *from;
  591. // find distance of closest point on ray
  592. closeLength = DotProduct( toFeet, along );
  593. // constrain point to be on path segment
  594. if (closeLength <= 0.0f)
  595. pos = *from;
  596. else if (closeLength >= length)
  597. pos = *to;
  598. else
  599. pos = *from + closeLength * along;
  600. distSq = (pos - feet).LengthSqr();
  601. // keep the closest point so far
  602. if (distSq < closeDistSq)
  603. {
  604. // don't use points we cant see
  605. Vector probe = pos + Vector( 0, 0, HalfHumanHeight );
  606. if (!IsWalkableTraceLineClear( eyes, probe, WALK_THRU_DOORS | WALK_THRU_BREAKABLES ))
  607. continue;
  608. // don't use points we cant reach
  609. //if (!IsStraightLinePathWalkable( &pos ))
  610. // continue;
  611. closeDistSq = distSq;
  612. if (close)
  613. *close = pos;
  614. closeIndex = i-1;
  615. }
  616. }
  617. return closeIndex;
  618. }
  619. //--------------------------------------------------------------------------------------------------------------
  620. /**
  621. * Compute a point a fixed distance ahead along our path.
  622. * Returns path index just after point.
  623. */
  624. int CNavPathFollower::FindPathPoint( float aheadRange, Vector *point, int *prevIndex )
  625. {
  626. // find path index just past aheadRange
  627. int afterIndex;
  628. // finds the closest point on local area of path, and returns the path index just prior to it
  629. Vector close;
  630. int startIndex = FindOurPositionOnPath( &close, true );
  631. if (prevIndex)
  632. *prevIndex = startIndex;
  633. if (startIndex <= 0)
  634. {
  635. // went off the end of the path
  636. // or next point in path is unwalkable (ie: jump-down)
  637. // keep same point
  638. return m_segmentIndex;
  639. }
  640. // if we are crouching, just follow the path exactly
  641. if (m_improv->IsCrouching())
  642. {
  643. // we want to move to the immediately next point along the path from where we are now
  644. int index = startIndex+1;
  645. if (index >= m_path->GetSegmentCount())
  646. index = m_path->GetSegmentCount()-1;
  647. *point = (*m_path)[ index ]->pos;
  648. // if we are very close to the next point in the path, skip ahead to the next one to avoid wiggling
  649. // we must do a 2D check here, in case the goal point is floating in space due to jump down, etc
  650. const float closeEpsilon = 20.0f; // 10
  651. while ((*point - close).AsVector2D().IsLengthLessThan( closeEpsilon ))
  652. {
  653. ++index;
  654. if (index >= m_path->GetSegmentCount())
  655. {
  656. index = m_path->GetSegmentCount()-1;
  657. break;
  658. }
  659. *point = (*m_path)[ index ]->pos;
  660. }
  661. return index;
  662. }
  663. // make sure we use a node a minimum distance ahead of us, to avoid wiggling
  664. while (startIndex < m_path->GetSegmentCount()-1)
  665. {
  666. Vector pos = (*m_path)[ startIndex+1 ]->pos;
  667. // we must do a 2D check here, in case the goal point is floating in space due to jump down, etc
  668. const float closeEpsilon = 20.0f;
  669. if ((pos - close).AsVector2D().IsLengthLessThan( closeEpsilon ))
  670. {
  671. ++startIndex;
  672. }
  673. else
  674. {
  675. break;
  676. }
  677. }
  678. // if we hit a ladder or jump area, must stop (dont use ladder behind us)
  679. if (startIndex > m_segmentIndex && startIndex < m_path->GetSegmentCount() &&
  680. ((*m_path)[ startIndex ]->ladder || (*m_path)[ startIndex ]->area->GetAttributes() & NAV_MESH_JUMP))
  681. {
  682. *point = (*m_path)[ startIndex ]->pos;
  683. return startIndex;
  684. }
  685. // we need the point just *ahead* of us
  686. ++startIndex;
  687. if (startIndex >= m_path->GetSegmentCount())
  688. startIndex = m_path->GetSegmentCount()-1;
  689. // if we hit a ladder or jump area, must stop
  690. if (startIndex < m_path->GetSegmentCount() &&
  691. ((*m_path)[ startIndex ]->ladder || (*m_path)[ startIndex ]->area->GetAttributes() & NAV_MESH_JUMP))
  692. {
  693. *point = (*m_path)[ startIndex ]->pos;
  694. return startIndex;
  695. }
  696. // note direction of path segment we are standing on
  697. Vector initDir = (*m_path)[ startIndex ]->pos - (*m_path)[ startIndex-1 ]->pos;
  698. initDir.NormalizeInPlace();
  699. Vector feet = m_improv->GetFeet();
  700. Vector eyes = m_improv->GetEyes();
  701. float rangeSoFar = 0;
  702. // this flag is true if our ahead point is visible
  703. bool visible = true;
  704. Vector prevDir = initDir;
  705. // step along the path until we pass aheadRange
  706. bool isCorner = false;
  707. int i;
  708. for( i=startIndex; i<m_path->GetSegmentCount(); ++i )
  709. {
  710. Vector pos = (*m_path)[i]->pos;
  711. Vector to = pos - (*m_path)[i-1]->pos;
  712. Vector dir = to;
  713. dir.NormalizeInPlace();
  714. // don't allow path to double-back from our starting direction (going upstairs, down curved passages, etc)
  715. if (DotProduct( dir, initDir ) < 0.0f) // -0.25f
  716. {
  717. --i;
  718. break;
  719. }
  720. // if the path turns a corner, we want to move towards the corner, not into the wall/stairs/etc
  721. if (DotProduct( dir, prevDir ) < 0.5f)
  722. {
  723. isCorner = true;
  724. --i;
  725. break;
  726. }
  727. prevDir = dir;
  728. // don't use points we cant see
  729. Vector probe = pos + Vector( 0, 0, HalfHumanHeight );
  730. if (!IsWalkableTraceLineClear( eyes, probe, WALK_THRU_BREAKABLES ))
  731. {
  732. // presumably, the previous point is visible, so we will interpolate
  733. visible = false;
  734. break;
  735. }
  736. // if we encounter a ladder or jump area, we must stop
  737. if (i < m_path->GetSegmentCount() &&
  738. ((*m_path)[ i ]->ladder || (*m_path)[ i ]->area->GetAttributes() & NAV_MESH_JUMP))
  739. break;
  740. // Check straight-line path from our current position to this position
  741. // Test for un-jumpable height change, or unrecoverable fall
  742. //if (!IsStraightLinePathWalkable( &pos ))
  743. //{
  744. // --i;
  745. // break;
  746. //}
  747. Vector along = (i == startIndex) ? (pos - feet) : (pos - (*m_path)[i-1]->pos);
  748. rangeSoFar += along.Length2D();
  749. // stop if we have gone farther than aheadRange
  750. if (rangeSoFar >= aheadRange)
  751. break;
  752. }
  753. if (i < startIndex)
  754. afterIndex = startIndex;
  755. else if (i < m_path->GetSegmentCount())
  756. afterIndex = i;
  757. else
  758. afterIndex = m_path->GetSegmentCount()-1;
  759. // compute point on the path at aheadRange
  760. if (afterIndex == 0)
  761. {
  762. *point = (*m_path)[0]->pos;
  763. }
  764. else
  765. {
  766. // interpolate point along path segment
  767. const Vector *afterPoint = &(*m_path)[ afterIndex ]->pos;
  768. const Vector *beforePoint = &(*m_path)[ afterIndex-1 ]->pos;
  769. Vector to = *afterPoint - *beforePoint;
  770. float length = to.Length2D();
  771. float t = 1.0f - ((rangeSoFar - aheadRange) / length);
  772. if (t < 0.0f)
  773. t = 0.0f;
  774. else if (t > 1.0f)
  775. t = 1.0f;
  776. *point = *beforePoint + t * to;
  777. // if afterPoint wasn't visible, slide point backwards towards beforePoint until it is
  778. if (!visible)
  779. {
  780. const float sightStepSize = 25.0f;
  781. float dt = sightStepSize / length;
  782. Vector probe = *point + Vector( 0, 0, HalfHumanHeight );
  783. while( t > 0.0f && !IsWalkableTraceLineClear( eyes, probe, WALK_THRU_BREAKABLES ) )
  784. {
  785. t -= dt;
  786. *point = *beforePoint + t * to;
  787. }
  788. if (t <= 0.0f)
  789. *point = *beforePoint;
  790. }
  791. }
  792. // if position found is too close to us, or behind us, force it farther down the path so we don't stop and wiggle
  793. if (!isCorner)
  794. {
  795. const float epsilon = 50.0f;
  796. Vector2D toPoint;
  797. Vector2D centroid( m_improv->GetCentroid().x, m_improv->GetCentroid().y );
  798. toPoint.x = point->x - centroid.x;
  799. toPoint.y = point->y - centroid.y;
  800. if (DotProduct2D( toPoint, initDir.AsVector2D() ) < 0.0f || toPoint.IsLengthLessThan( epsilon ))
  801. {
  802. int i;
  803. for( i=startIndex; i<m_path->GetSegmentCount(); ++i )
  804. {
  805. toPoint.x = (*m_path)[i]->pos.x - centroid.x;
  806. toPoint.y = (*m_path)[i]->pos.y - centroid.y;
  807. if ((*m_path)[i]->ladder || (*m_path)[i]->area->GetAttributes() & NAV_MESH_JUMP || toPoint.IsLengthGreaterThan( epsilon ))
  808. {
  809. *point = (*m_path)[i]->pos;
  810. startIndex = i;
  811. break;
  812. }
  813. }
  814. if (i == m_path->GetSegmentCount())
  815. {
  816. *point = m_path->GetEndpoint();
  817. startIndex = m_path->GetSegmentCount()-1;
  818. }
  819. }
  820. }
  821. // m_pathIndex should always be the next point on the path, even if we're not moving directly towards it
  822. if (startIndex < m_path->GetSegmentCount())
  823. return startIndex;
  824. return m_path->GetSegmentCount()-1;
  825. }
  826. //--------------------------------------------------------------------------------------------------------------
  827. /**
  828. * Do reflex avoidance movements if our "feelers" are touched
  829. * @todo Parameterize feeler spacing
  830. */
  831. void CNavPathFollower::FeelerReflexAdjustment( Vector *goalPosition, float height )
  832. {
  833. // if we are in a "precise" area, do not do feeler adjustments
  834. if (m_improv->GetLastKnownArea() && m_improv->GetLastKnownArea()->GetAttributes() & NAV_MESH_PRECISE)
  835. return;
  836. // use the direction towards the goal
  837. Vector dir = *goalPosition - m_improv->GetFeet();
  838. dir.z = 0.0f;
  839. dir.NormalizeInPlace();
  840. Vector lat( -dir.y, dir.x, 0.0f );
  841. const float feelerOffset = (m_improv->IsCrouching()) ? 15.0f : 20.0f; // 15, 20
  842. const float feelerLengthRun = 50.0f; // 100 - too long for tight hallways (cs_747)
  843. const float feelerLengthWalk = 30.0f;
  844. const float feelerHeight = (height > 0.0f) ? height : StepHeight + 0.1f; // if obstacle is lower than StepHeight, we'll walk right over it
  845. float feelerLength = (m_improv->IsRunning()) ? feelerLengthRun : feelerLengthWalk;
  846. feelerLength = (m_improv->IsCrouching()) ? 20.0f : feelerLength;
  847. //
  848. // Feelers must follow floor slope
  849. //
  850. float ground;
  851. Vector normal;
  852. if (m_improv->GetSimpleGroundHeightWithFloor( m_improv->GetEyes(), &ground, &normal ) == false)
  853. return;
  854. // get forward vector along floor
  855. dir = CrossProduct( lat, normal );
  856. // correct the sideways vector
  857. lat = CrossProduct( dir, normal );
  858. Vector feet = m_improv->GetFeet();
  859. feet.z += feelerHeight;
  860. Vector from = feet + feelerOffset * lat;
  861. Vector to = from + feelerLength * dir;
  862. bool leftClear = IsWalkableTraceLineClear( from, to, WALK_THRU_DOORS | WALK_THRU_BREAKABLES );
  863. // draw debug beams
  864. if (m_isDebug)
  865. {
  866. if (leftClear)
  867. UTIL_DrawBeamPoints( from, to, 1, 0, 255, 0 );
  868. else
  869. UTIL_DrawBeamPoints( from, to, 1, 255, 0, 0 );
  870. }
  871. from = feet - feelerOffset * lat;
  872. to = from + feelerLength * dir;
  873. bool rightClear = IsWalkableTraceLineClear( from, to, WALK_THRU_DOORS | WALK_THRU_BREAKABLES );
  874. // draw debug beams
  875. if (m_isDebug)
  876. {
  877. if (rightClear)
  878. UTIL_DrawBeamPoints( from, to, 1, 0, 255, 0 );
  879. else
  880. UTIL_DrawBeamPoints( from, to, 1, 255, 0, 0 );
  881. }
  882. const float avoidRange = (m_improv->IsCrouching()) ? 150.0f : 300.0f;
  883. if (!rightClear)
  884. {
  885. if (leftClear)
  886. {
  887. // right hit, left clear - veer left
  888. *goalPosition = *goalPosition + avoidRange * lat;
  889. //*goalPosition = m_improv->GetFeet() + avoidRange * lat;
  890. //m_improv->StrafeLeft();
  891. }
  892. }
  893. else if (!leftClear)
  894. {
  895. // right clear, left hit - veer right
  896. *goalPosition = *goalPosition - avoidRange * lat;
  897. //*goalPosition = m_improv->GetFeet() - avoidRange * lat;
  898. //m_improv->StrafeRight();
  899. }
  900. }
  901. //--------------------------------------------------------------------------------------------------------------
  902. /**
  903. * Reset the stuck-checker.
  904. */
  905. CStuckMonitor::CStuckMonitor( void )
  906. {
  907. m_isStuck = false;
  908. m_avgVelIndex = 0;
  909. m_avgVelCount = 0;
  910. }
  911. /**
  912. * Reset the stuck-checker.
  913. */
  914. void CStuckMonitor::Reset( void )
  915. {
  916. m_isStuck = false;
  917. m_avgVelIndex = 0;
  918. m_avgVelCount = 0;
  919. }
  920. //--------------------------------------------------------------------------------------------------------------
  921. /**
  922. * Test if the improv has become stuck
  923. */
  924. void CStuckMonitor::Update( CImprovLocomotor *improv )
  925. {
  926. if (m_isStuck)
  927. {
  928. // improv is stuck - see if it has moved far enough to be considered unstuck
  929. const float unstuckRange = 75.0f;
  930. if ((improv->GetCentroid() - m_stuckSpot).IsLengthGreaterThan( unstuckRange ))
  931. {
  932. // no longer stuck
  933. Reset();
  934. //PrintIfWatched( "UN-STUCK\n" );
  935. }
  936. }
  937. else
  938. {
  939. // check if improv has become stuck
  940. // compute average velocity over a short period (for stuck check)
  941. Vector vel = improv->GetCentroid() - m_lastCentroid;
  942. // if we are jumping, ignore Z
  943. //if (improv->IsJumping())
  944. // vel.z = 0.0f;
  945. // ignore Z unless we are on a ladder (which is only Z)
  946. if (!improv->IsUsingLadder())
  947. vel.z = 0.0f;
  948. // cannot be Length2D, or will break ladder movement (they are only Z)
  949. float moveDist = vel.Length();
  950. float deltaT = gpGlobals->curtime - m_lastTime;
  951. if (deltaT <= 0.0f)
  952. return;
  953. m_lastTime = gpGlobals->curtime;
  954. // compute current velocity
  955. m_avgVel[ m_avgVelIndex++ ] = moveDist/deltaT;
  956. if (m_avgVelIndex == MAX_VEL_SAMPLES)
  957. m_avgVelIndex = 0;
  958. if (m_avgVelCount < MAX_VEL_SAMPLES)
  959. {
  960. ++m_avgVelCount;
  961. }
  962. else
  963. {
  964. // we have enough samples to know if we're stuck
  965. float avgVel = 0.0f;
  966. for( int t=0; t<m_avgVelCount; ++t )
  967. avgVel += m_avgVel[t];
  968. avgVel /= m_avgVelCount;
  969. // cannot make this velocity too high, or actors will get "stuck" when going down ladders
  970. float stuckVel = (improv->IsUsingLadder()) ? 10.0f : 20.0f;
  971. if (avgVel < stuckVel)
  972. {
  973. // note when and where we initially become stuck
  974. m_stuckTimer.Start();
  975. m_stuckSpot = improv->GetCentroid();
  976. m_isStuck = true;
  977. }
  978. }
  979. }
  980. // always need to track this
  981. m_lastCentroid = improv->GetCentroid();
  982. }