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.

469 lines
12 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "ai_localnavigator.h"
  8. #include "ai_basenpc.h"
  9. #include "ai_planesolver.h"
  10. #include "ai_moveprobe.h"
  11. #include "ai_motor.h"
  12. // memdbgon must be the last include file in a .cpp file!!!
  13. #include "tier0/memdbgon.h"
  14. ConVar ai_debug_directnavprobe("ai_debug_directnavprobe", "0");
  15. const float TIME_DELAY_FULL_DIRECT_PROBE[2] = { 0.25, 0.35 };
  16. //-----------------------------------------------------------------------------
  17. BEGIN_SIMPLE_DATADESC(CAI_LocalNavigator)
  18. // m_fLastWasClear (not saved)
  19. // m_LastMoveGoal (not saved)
  20. // m_FullDirectTimer (not saved)
  21. // m_pPlaneSolver (not saved)
  22. // m_pMoveProbe (not saved)
  23. END_DATADESC();
  24. //-------------------------------------
  25. CAI_LocalNavigator::CAI_LocalNavigator(CAI_BaseNPC *pOuter) : CAI_Component( pOuter )
  26. {
  27. m_pMoveProbe = NULL;
  28. m_pPlaneSolver = new CAI_PlaneSolver( pOuter );
  29. m_fLastWasClear = false;
  30. memset( &m_LastMoveGoal, 0, sizeof(m_LastMoveGoal) );
  31. }
  32. //-------------------------------------
  33. CAI_LocalNavigator::~CAI_LocalNavigator()
  34. {
  35. delete m_pPlaneSolver;
  36. }
  37. //-------------------------------------
  38. void CAI_LocalNavigator::Init( IAI_MovementSink *pMovementServices )
  39. {
  40. CAI_ProxyMovementSink::Init( pMovementServices );
  41. m_pMoveProbe = GetOuter()->GetMoveProbe(); // @TODO (toml 03-30-03): this is a "bad" way to grab this pointer. Components should have an explcit "init" phase.
  42. }
  43. //-------------------------------------
  44. void CAI_LocalNavigator::ResetMoveCalculations()
  45. {
  46. m_FullDirectTimer.Force();
  47. m_pPlaneSolver->Reset();
  48. }
  49. //-------------------------------------
  50. void CAI_LocalNavigator::AddObstacle( const Vector &pos, float radius, AI_MoveSuggType_t type )
  51. {
  52. m_pPlaneSolver->AddObstacle( pos, radius, NULL, type );
  53. }
  54. //-------------------------------------
  55. bool CAI_LocalNavigator::HaveObstacles()
  56. {
  57. return m_pPlaneSolver->HaveObstacles();
  58. }
  59. Obstacle_t CAI_LocalNavigator::AddGlobalObstacle( const Vector &pos, float radius, AI_MoveSuggType_t type )
  60. {
  61. return CAI_PlaneSolver::AddGlobalObstacle( pos, radius, NULL, type );
  62. }
  63. void CAI_LocalNavigator::RemoveGlobalObstacle( Obstacle_t hObstacle )
  64. {
  65. CAI_PlaneSolver::RemoveGlobalObstacle( hObstacle );
  66. }
  67. void CAI_LocalNavigator::RemoveGlobalObstacles( void )
  68. {
  69. CAI_PlaneSolver::RemoveGlobalObstacles();
  70. }
  71. bool CAI_LocalNavigator::IsSegmentBlockedByGlobalObstacles( const Vector &vecStart, const Vector &vecEnd )
  72. {
  73. return CAI_PlaneSolver::IsSegmentBlockedByGlobalObstacles( vecStart, vecEnd );
  74. }
  75. //-------------------------------------
  76. bool CAI_LocalNavigator::MoveCalcDirect( AILocalMoveGoal_t *pMoveGoal, bool bOnlyCurThink, float *pDistClear, AIMoveResult_t *pResult )
  77. {
  78. AI_PROFILE_SCOPE(CAI_LocalNavigator_MoveCalcDirect);
  79. bool bRetVal = false;
  80. if ( pMoveGoal->speed )
  81. {
  82. CAI_Motor *pMotor = GetOuter()->GetMotor();
  83. float minCheckDist = pMotor->MinCheckDist();
  84. float probeDist = m_pPlaneSolver->CalcProbeDist( pMoveGoal->speed ); // having this match steering allows one fewer traces
  85. float checkDist = MAX( minCheckDist, probeDist );
  86. float checkStepDist = MAX( 16.0, probeDist * 0.5 );
  87. if ( pMoveGoal->flags & ( AILMG_TARGET_IS_TRANSITION | AILMG_TARGET_IS_GOAL ) )
  88. {
  89. // clamp checkDist to be no farther than MAX distance to goal
  90. checkDist = MIN( checkDist, pMoveGoal->maxDist );
  91. }
  92. if ( checkDist <= 0.0 )
  93. {
  94. *pResult = AIMR_OK;
  95. return true;
  96. }
  97. float moveThisInterval = pMotor->CalcIntervalMove();
  98. bool bExpectingArrival = (moveThisInterval >= checkDist);
  99. if ( !m_FullDirectTimer.Expired() )
  100. {
  101. if ( !m_fLastWasClear ||
  102. ( !VectorsAreEqual(pMoveGoal->target, m_LastMoveGoal.target, 0.1) ||
  103. !VectorsAreEqual(pMoveGoal->dir, m_LastMoveGoal.dir, 0.1) ) ||
  104. bExpectingArrival )
  105. {
  106. m_FullDirectTimer.Force();
  107. }
  108. }
  109. if ( bOnlyCurThink ) // Outer code claims to have done a validation (probably a simplify operation)
  110. {
  111. m_FullDirectTimer.Set( TIME_DELAY_FULL_DIRECT_PROBE[AIStrongOpt()] );
  112. }
  113. // First, check the probable move for this cycle
  114. bool bTraceClear = true;
  115. Vector testPos;
  116. if ( !bExpectingArrival )
  117. {
  118. testPos = GetLocalOrigin() + pMoveGoal->dir * moveThisInterval;
  119. bTraceClear = GetMoveProbe()->MoveLimit( pMoveGoal->navType, GetLocalOrigin(), testPos,
  120. GetOuter()->GetAITraceMask(), pMoveGoal->pMoveTarget,
  121. 100.0,
  122. ( pMoveGoal->navType == NAV_GROUND ) ? AIMLF_2D : AIMLF_DEFAULT,
  123. &pMoveGoal->directTrace );
  124. if ( !bTraceClear )
  125. {
  126. // Adjust probe top match expected probe dist (relied on later in process)
  127. pMoveGoal->directTrace.flDistObstructed = (checkDist - moveThisInterval) + pMoveGoal->directTrace.flDistObstructed;
  128. }
  129. if ( !IsRetail() && ai_debug_directnavprobe.GetBool() )
  130. {
  131. if ( !bTraceClear )
  132. {
  133. DevMsg( GetOuter(), "Close obstruction %f\n", checkDist - pMoveGoal->directTrace.flDistObstructed );
  134. NDebugOverlay::Line( WorldSpaceCenter(), Vector( testPos.x, testPos.y, WorldSpaceCenter().z ), 255, 0, 0, false, 0.1 );
  135. if ( pMoveGoal->directTrace.pObstruction )
  136. NDebugOverlay::Line( WorldSpaceCenter(), pMoveGoal->directTrace.pObstruction->WorldSpaceCenter(), 255, 0, 255, false, 0.1 );
  137. }
  138. else
  139. {
  140. NDebugOverlay::Line( WorldSpaceCenter(), Vector( testPos.x, testPos.y, WorldSpaceCenter().z ), 0, 255, 0, false, 0.1 );
  141. }
  142. }
  143. pMoveGoal->thinkTrace = pMoveGoal->directTrace;
  144. }
  145. // Now project out for future obstructions
  146. if ( bTraceClear )
  147. {
  148. if ( m_FullDirectTimer.Expired() )
  149. {
  150. testPos = GetLocalOrigin() + pMoveGoal->dir * checkDist;
  151. float checkStepPct = (checkStepDist / checkDist) * 100.0;
  152. if ( checkStepPct > 100.0 )
  153. checkStepPct = 100.0;
  154. bTraceClear = GetMoveProbe()->MoveLimit( pMoveGoal->navType, GetLocalOrigin(), testPos,
  155. GetOuter()->GetAITraceMask(), pMoveGoal->pMoveTarget,
  156. checkStepPct,
  157. ( pMoveGoal->navType == NAV_GROUND ) ? AIMLF_2D : AIMLF_DEFAULT,
  158. &pMoveGoal->directTrace );
  159. if ( bExpectingArrival )
  160. pMoveGoal->thinkTrace = pMoveGoal->directTrace;
  161. if (ai_debug_directnavprobe.GetBool() )
  162. {
  163. if ( !bTraceClear )
  164. {
  165. NDebugOverlay::Line( GetOuter()->EyePosition(), Vector( testPos.x, testPos.y, GetOuter()->EyePosition().z ), 255, 0, 0, false, 0.1 );
  166. DevMsg( GetOuter(), "Obstruction %f\n", checkDist - pMoveGoal->directTrace.flDistObstructed );
  167. }
  168. else
  169. {
  170. NDebugOverlay::Line( GetOuter()->EyePosition(), Vector( testPos.x, testPos.y, GetOuter()->EyePosition().z ), 0, 255, 0, false, 0.1 );
  171. DevMsg( GetOuter(), "No obstruction\n" );
  172. }
  173. }
  174. }
  175. else
  176. {
  177. if ( ai_debug_directnavprobe.GetBool() )
  178. DevMsg( GetOuter(), "No obstruction (Near probe only)\n" );
  179. }
  180. }
  181. pMoveGoal->bHasTraced = true;
  182. float distClear = checkDist - pMoveGoal->directTrace.flDistObstructed;
  183. if (distClear < 0.001)
  184. distClear = 0;
  185. if ( bTraceClear )
  186. {
  187. *pResult = AIMR_OK;
  188. bRetVal = true;
  189. m_fLastWasClear = true;
  190. }
  191. else if ( ( pMoveGoal->flags & ( AILMG_TARGET_IS_TRANSITION | AILMG_TARGET_IS_GOAL ) ) &&
  192. pMoveGoal->maxDist < distClear )
  193. {
  194. *pResult = AIMR_OK;
  195. bRetVal = true;
  196. m_fLastWasClear = true;
  197. }
  198. else
  199. {
  200. *pDistClear = distClear;
  201. m_fLastWasClear = false;
  202. }
  203. }
  204. else
  205. {
  206. // Should never end up in this function with speed of zero. Probably an activity problem.
  207. *pResult = AIMR_ILLEGAL;
  208. bRetVal = true;
  209. }
  210. m_LastMoveGoal = *pMoveGoal;
  211. if ( bRetVal && m_FullDirectTimer.Expired() )
  212. m_FullDirectTimer.Set( TIME_DELAY_FULL_DIRECT_PROBE[AIStrongOpt()] );
  213. return bRetVal;
  214. }
  215. //-------------------------------------
  216. ConVar ai_no_steer( "ai_no_steer", "0" );
  217. bool CAI_LocalNavigator::MoveCalcSteer( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult )
  218. {
  219. if ( (pMoveGoal->flags & AILMG_NO_STEER) )
  220. return false;
  221. if ( ai_no_steer.GetBool() )
  222. return false;
  223. if ( GetOuter()->IsFlaggedEfficient() )
  224. return false;
  225. AI_PROFILE_SCOPE(CAI_Motor_MoveCalcSteer);
  226. Vector moveSolution;
  227. if ( m_pPlaneSolver->Solve( *pMoveGoal, distClear, &moveSolution ) )
  228. {
  229. if ( moveSolution != pMoveGoal->dir )
  230. {
  231. float dot = moveSolution.AsVector2D().Dot( pMoveGoal->dir.AsVector2D() );
  232. const float COS_HALF_30 = 0.966;
  233. if ( dot > COS_HALF_30 )
  234. {
  235. float probeDist = m_pPlaneSolver->CalcProbeDist( pMoveGoal->speed );
  236. if ( pMoveGoal->maxDist < probeDist * 0.33333 && distClear > probeDist * 0.6666)
  237. {
  238. // A waypoint is coming up, but there's probably time to steer
  239. // away after hitting it
  240. *pResult = AIMR_OK;
  241. return true;
  242. }
  243. }
  244. pMoveGoal->facing = pMoveGoal->dir = moveSolution;
  245. }
  246. *pResult = AIMR_OK;
  247. return true;
  248. }
  249. return false;
  250. }
  251. //-------------------------------------
  252. bool CAI_LocalNavigator::MoveCalcStop( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult )
  253. {
  254. if (distClear < pMoveGoal->maxDist)
  255. {
  256. if ( distClear < 0.1 )
  257. {
  258. DebugNoteMovementFailure();
  259. *pResult = AIMR_ILLEGAL;
  260. }
  261. else
  262. {
  263. pMoveGoal->maxDist = distClear;
  264. *pResult = AIMR_OK;
  265. }
  266. return true;
  267. }
  268. *pResult = AIMR_OK;
  269. return true;
  270. }
  271. //-------------------------------------
  272. #ifdef DEBUG
  273. #define SetSolveCookie() pMoveGoal->solveCookie = __LINE__;
  274. #else
  275. #define SetSolveCookie() ((void)0)
  276. #endif
  277. AIMoveResult_t CAI_LocalNavigator::MoveCalcRaw( AILocalMoveGoal_t *pMoveGoal, bool bOnlyCurThink )
  278. {
  279. AI_PROFILE_SCOPE(CAI_Motor_MoveCalc);
  280. AIMoveResult_t result = AIMR_OK; // Assume success
  281. AIMoveTrace_t directTrace;
  282. float distClear;
  283. // --------------------------------------------------
  284. bool bDirectClear = MoveCalcDirect( pMoveGoal, bOnlyCurThink, &distClear, &result);
  285. if ( OnCalcBaseMove( pMoveGoal, distClear, &result ) )
  286. {
  287. SetSolveCookie();
  288. return DbgResult( result );
  289. }
  290. bool bShouldSteer = ( !(pMoveGoal->flags & AILMG_NO_STEER) && ( !bDirectClear || HaveObstacles() ) );
  291. if ( bDirectClear && !bShouldSteer )
  292. {
  293. SetSolveCookie();
  294. return DbgResult( result );
  295. }
  296. // --------------------------------------------------
  297. if ( bShouldSteer )
  298. {
  299. if ( !bDirectClear )
  300. {
  301. if ( OnObstructionPreSteer( pMoveGoal, distClear, &result ) )
  302. {
  303. SetSolveCookie();
  304. return DbgResult( result );
  305. }
  306. }
  307. if ( MoveCalcSteer( pMoveGoal, distClear, &result ) )
  308. {
  309. SetSolveCookie();
  310. return DbgResult( result );
  311. }
  312. }
  313. if ( OnFailedSteer( pMoveGoal, distClear, &result ) )
  314. {
  315. SetSolveCookie();
  316. return DbgResult( result );
  317. }
  318. // --------------------------------------------------
  319. if ( OnFailedLocalNavigation( pMoveGoal, distClear, &result ) )
  320. {
  321. SetSolveCookie();
  322. return DbgResult( result );
  323. }
  324. if ( distClear < GetOuter()->GetMotor()->MinStoppingDist() )
  325. {
  326. if ( OnInsufficientStopDist( pMoveGoal, distClear, &result ) )
  327. {
  328. SetSolveCookie();
  329. return DbgResult( result );
  330. }
  331. if ( MoveCalcStop( pMoveGoal, distClear, &result) )
  332. {
  333. SetSolveCookie();
  334. return DbgResult( result );
  335. }
  336. }
  337. // A hopeful result... may get in trouble at next waypoint and obstruction is still there
  338. if ( distClear > pMoveGoal->curExpectedDist )
  339. {
  340. SetSolveCookie();
  341. return DbgResult( AIMR_OK );
  342. }
  343. // --------------------------------------------------
  344. DebugNoteMovementFailure();
  345. SetSolveCookie();
  346. return DbgResult( IsMoveBlocked( pMoveGoal->directTrace.fStatus ) ? pMoveGoal->directTrace.fStatus : AIMR_ILLEGAL );
  347. }
  348. //-------------------------------------
  349. AIMoveResult_t CAI_LocalNavigator::MoveCalc( AILocalMoveGoal_t *pMoveGoal, bool bPreviouslyValidated )
  350. {
  351. bool bOnlyCurThink = ( bPreviouslyValidated && !HaveObstacles() );
  352. AIMoveResult_t result = MoveCalcRaw( pMoveGoal, bOnlyCurThink );
  353. if ( pMoveGoal->curExpectedDist > pMoveGoal->maxDist )
  354. pMoveGoal->curExpectedDist = pMoveGoal->maxDist;
  355. // If success, try to dampen really fast turning movement
  356. if ( result == AIMR_OK)
  357. {
  358. float interval = GetOuter()->GetMotor()->GetMoveInterval();
  359. float currentYaw = UTIL_AngleMod( GetLocalAngles().y );
  360. float goalYaw;
  361. float deltaYaw;
  362. float speed;
  363. float clampedYaw;
  364. // Clamp yaw
  365. goalYaw = UTIL_VecToYaw( pMoveGoal->facing );
  366. deltaYaw = fabs( UTIL_AngleDiff( goalYaw, currentYaw ) );
  367. if ( deltaYaw > 15 )
  368. {
  369. speed = deltaYaw * 4.0; // i.e., any maneuver takes a quarter a second
  370. clampedYaw = AI_ClampYaw( speed, currentYaw, goalYaw, interval );
  371. if ( clampedYaw != goalYaw )
  372. {
  373. pMoveGoal->facing = UTIL_YawToVector( clampedYaw );
  374. }
  375. }
  376. }
  377. return result;
  378. }
  379. //-----------------------------------------------------------------------------