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.

527 lines
15 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "game.h"
  9. #include "ndebugoverlay.h"
  10. #include "ai_basenpc.h"
  11. #include "ai_hull.h"
  12. #include "ai_node.h"
  13. #include "ai_motor.h"
  14. #include "ai_navigator.h"
  15. #include "ai_hint.h"
  16. #include "scripted.h"
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include "tier0/memdbgon.h"
  19. //=============================================================================
  20. // PATHING & HIGHER LEVEL MOVEMENT
  21. //-----------------------------------------------------------------------------
  22. //-----------------------------------------------------------------------------
  23. // Purpose: Static debug function to force all selected npcs to go to the
  24. // given node
  25. // Input :
  26. // Output :
  27. //-----------------------------------------------------------------------------
  28. void CAI_BaseNPC::ForceSelectedGo(CBaseEntity *pPlayer, const Vector &targetPos, const Vector &traceDir, bool bRun)
  29. {
  30. CAI_BaseNPC *npc = gEntList.NextEntByClass( (CAI_BaseNPC *)NULL );
  31. for ( ; npc; npc = gEntList.NextEntByClass(npc) )
  32. {
  33. if ( ( npc->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT ) == 0 )
  34. continue;
  35. // If a behavior is active, we need to stop running it
  36. npc->SetPrimaryBehavior( NULL );
  37. Vector chasePosition = targetPos;
  38. npc->TranslateNavGoal( pPlayer, chasePosition );
  39. // It it legal to drop me here
  40. Vector vUpBit = chasePosition;
  41. vUpBit.z += 1;
  42. trace_t tr;
  43. AI_TraceHull( chasePosition, vUpBit, npc->GetHullMins(),
  44. npc->GetHullMaxs(), npc->GetAITraceMask(), npc, COLLISION_GROUP_NONE, &tr );
  45. if (tr.startsolid || tr.fraction != 1.0 )
  46. {
  47. NDebugOverlay::BoxAngles(chasePosition, npc->GetHullMins(),
  48. npc->GetHullMaxs(), npc->GetAbsAngles(), 255,0,0,20,0.5);
  49. }
  50. npc->m_vecLastPosition = chasePosition;
  51. if (npc->m_hCine != NULL)
  52. {
  53. npc->ExitScriptedSequence();
  54. }
  55. npc->SetSchedule( bRun ? SCHED_FORCED_GO_RUN : SCHED_FORCED_GO );
  56. npc->m_flMoveWaitFinished = gpGlobals->curtime;
  57. }
  58. }
  59. //-----------------------------------------------------------------------------
  60. // Purpose: Static debug function to make all selected npcs run around
  61. // Input :
  62. // Output :
  63. //-----------------------------------------------------------------------------
  64. void CAI_BaseNPC::ForceSelectedGoRandom(void)
  65. {
  66. CAI_BaseNPC *npc = gEntList.NextEntByClass( (CAI_BaseNPC *)NULL );
  67. while (npc)
  68. {
  69. if (npc->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT)
  70. {
  71. // If a behavior is active, we need to stop running it
  72. npc->SetPrimaryBehavior( NULL );
  73. npc->SetSchedule( SCHED_RUN_RANDOM );
  74. npc->GetNavigator()->SetMovementActivity(ACT_RUN);
  75. }
  76. npc = gEntList.NextEntByClass(npc);
  77. }
  78. }
  79. //-----------------------------------------------------------------------------
  80. //-----------------------------------------------------------------------------
  81. bool CAI_BaseNPC::ScheduledMoveToGoalEntity( int scheduleType, CBaseEntity *pGoalEntity, Activity movementActivity )
  82. {
  83. // If a behavior is active, we need to stop running it
  84. SetPrimaryBehavior( NULL );
  85. if ( m_NPCState == NPC_STATE_NONE )
  86. {
  87. // More than likely being grabbed before first think. Set ideal state to prevent schedule stomp
  88. m_NPCState = m_IdealNPCState;
  89. }
  90. SetSchedule( scheduleType );
  91. SetGoalEnt( pGoalEntity );
  92. // HACKHACK: Call through TranslateNavGoal to fixup this goal position
  93. // UNDONE: Remove this and have NPCs that need this functionality fix up paths in the
  94. // movement system instead of when they are specified.
  95. AI_NavGoal_t goal(pGoalEntity->GetAbsOrigin(), movementActivity, AIN_DEF_TOLERANCE, AIN_YAW_TO_DEST);
  96. TranslateNavGoal( pGoalEntity, goal.dest );
  97. return GetNavigator()->SetGoal( goal );
  98. }
  99. //-----------------------------------------------------------------------------
  100. //-----------------------------------------------------------------------------
  101. bool CAI_BaseNPC::ScheduledFollowPath( int scheduleType, CBaseEntity *pPathStart, Activity movementActivity )
  102. {
  103. // If a behavior is active, we need to stop running it
  104. SetPrimaryBehavior( NULL );
  105. if ( m_NPCState == NPC_STATE_NONE )
  106. {
  107. // More than likely being grabbed before first think. Set ideal state to prevent schedule stomp
  108. m_NPCState = m_IdealNPCState;
  109. }
  110. SetSchedule( scheduleType );
  111. SetGoalEnt( pPathStart );
  112. // HACKHACK: Call through TranslateNavGoal to fixup this goal position
  113. AI_NavGoal_t goal(GOALTYPE_PATHCORNER, pPathStart->GetLocalOrigin(), movementActivity, AIN_DEF_TOLERANCE, AIN_YAW_TO_DEST);
  114. TranslateNavGoal( pPathStart, goal.dest );
  115. return GetNavigator()->SetGoal( goal );
  116. }
  117. //-----------------------------------------------------------------------------
  118. // Purpose:
  119. // Input :
  120. // Output :
  121. //-----------------------------------------------------------------------------
  122. bool CAI_BaseNPC::IsMoving( void )
  123. {
  124. return GetNavigator()->IsGoalSet();
  125. }
  126. //-----------------------------------------------------------------------------
  127. bool CAI_BaseNPC::IsCurTaskContinuousMove()
  128. {
  129. const Task_t* pTask = GetTask();
  130. // This bit of logic strikes me funny, but the case does exist. (sjb)
  131. if( !pTask )
  132. return true;
  133. switch( pTask->iTask )
  134. {
  135. case TASK_WAIT_FOR_MOVEMENT:
  136. case TASK_MOVE_TO_TARGET_RANGE:
  137. case TASK_MOVE_TO_GOAL_RANGE:
  138. case TASK_WEAPON_RUN_PATH:
  139. case TASK_PLAY_SCENE:
  140. case TASK_RUN_PATH_TIMED:
  141. case TASK_WALK_PATH_TIMED:
  142. case TASK_RUN_PATH_FOR_UNITS:
  143. case TASK_WALK_PATH_FOR_UNITS:
  144. case TASK_RUN_PATH_FLEE:
  145. case TASK_WALK_PATH_WITHIN_DIST:
  146. case TASK_RUN_PATH_WITHIN_DIST:
  147. return true;
  148. break;
  149. default:
  150. return false;
  151. break;
  152. }
  153. }
  154. //-----------------------------------------------------------------------------
  155. // Purpose: Used to specify that the NPC has a reason not to use the a navigation node
  156. // Input :
  157. // Output :
  158. //-----------------------------------------------------------------------------
  159. bool CAI_BaseNPC::IsUnusableNode(int iNodeID, CAI_Hint *pHint)
  160. {
  161. if ( m_bHintGroupNavLimiting && m_strHintGroup != NULL_STRING && STRING(m_strHintGroup)[0] != 0 )
  162. {
  163. if (!pHint || pHint->GetGroup() != GetHintGroup())
  164. {
  165. return true;
  166. }
  167. }
  168. return false;
  169. }
  170. //-----------------------------------------------------------------------------
  171. // Purpose: Checks the validity of the given route's goaltype
  172. // Input :
  173. // Output :
  174. //-----------------------------------------------------------------------------
  175. bool CAI_BaseNPC::ValidateNavGoal()
  176. {
  177. if (GetNavigator()->GetGoalType() == GOALTYPE_COVER)
  178. {
  179. // Check if this location will block my enemy's line of sight to me
  180. if (GetEnemy())
  181. {
  182. Activity nCoverActivity = GetCoverActivity( GetHintNode() );
  183. Vector vCoverLocation = GetNavigator()->GetGoalPos();
  184. // For now we have to drop the node to the floor so we can
  185. // get an accurate postion of the NPC. Should change once Ken checks in
  186. float floorZ = GetFloorZ(vCoverLocation);
  187. vCoverLocation.z = floorZ;
  188. Vector vEyePos = vCoverLocation + EyeOffset(nCoverActivity);
  189. if (!IsCoverPosition( GetEnemy()->EyePosition(), vEyePos ) )
  190. {
  191. TaskFail(FAIL_BAD_PATH_GOAL);
  192. return false;
  193. }
  194. }
  195. }
  196. return true;
  197. }
  198. //-----------------------------------------------------------------------------
  199. // Purpose:
  200. // Input :
  201. // Output :
  202. //-----------------------------------------------------------------------------
  203. float CAI_BaseNPC::OpenDoorAndWait( CBaseEntity *pDoor )
  204. {
  205. float flTravelTime = 0;
  206. //DevMsg( 2, "A door. ");
  207. if (pDoor && !pDoor->IsLockedByMaster())
  208. {
  209. pDoor->Use(this, this, USE_ON, 0.0);
  210. flTravelTime = pDoor->GetMoveDoneTime();
  211. if ( pDoor->GetEntityName() != NULL_STRING )
  212. {
  213. CBaseEntity *pTarget = NULL;
  214. for (;;)
  215. {
  216. pTarget = gEntList.FindEntityByName( pTarget, pDoor->GetEntityName() );
  217. if ( pTarget != pDoor )
  218. {
  219. if ( !pTarget )
  220. break;
  221. if ( FClassnameIs( pTarget, pDoor->GetClassname() ) )
  222. {
  223. pTarget->Use(this, this, USE_ON, 0.0);
  224. }
  225. }
  226. }
  227. }
  228. }
  229. return gpGlobals->curtime + flTravelTime;
  230. }
  231. //-----------------------------------------------------------------------------
  232. bool CAI_BaseNPC::CanStandOn( CBaseEntity *pSurface ) const
  233. {
  234. if ( !pSurface->IsAIWalkable() )
  235. {
  236. return false;
  237. }
  238. CAI_Navigator *pNavigator = const_cast<CAI_Navigator *>(GetNavigator());
  239. if ( pNavigator->IsGoalActive() &&
  240. pSurface == pNavigator->GetGoalTarget() )
  241. return false;
  242. return BaseClass::CanStandOn( pSurface );
  243. }
  244. //-----------------------------------------------------------------------------
  245. bool CAI_BaseNPC::IsJumpLegal( const Vector &startPos, const Vector &apex, const Vector &endPos,
  246. float maxUp, float maxDown, float maxDist ) const
  247. {
  248. if ((endPos.z - startPos.z) > maxUp + 0.1)
  249. return false;
  250. if ((startPos.z - endPos.z) > maxDown + 0.1)
  251. return false;
  252. if ((apex.z - startPos.z) > maxUp * 1.25 )
  253. return false;
  254. float dist = (startPos - endPos).Length();
  255. if ( dist > maxDist + 0.1)
  256. return false;
  257. return true;
  258. }
  259. //-----------------------------------------------------------------------------
  260. // Purpose: Returns true if a reasonable jumping distance
  261. // Input :
  262. // Output :
  263. //-----------------------------------------------------------------------------
  264. bool CAI_BaseNPC::IsJumpLegal( const Vector &startPos, const Vector &apex, const Vector &endPos ) const
  265. {
  266. const float MAX_JUMP_RISE = 80.0f;
  267. const float MAX_JUMP_DISTANCE = 250.0f;
  268. const float MAX_JUMP_DROP = 192.0f;
  269. return IsJumpLegal( startPos, apex, endPos, MAX_JUMP_RISE, MAX_JUMP_DROP, MAX_JUMP_DISTANCE );
  270. }
  271. //-----------------------------------------------------------------------------
  272. // Purpose: Returns a throw velocity from start to end position
  273. // Input :
  274. // Output :
  275. //-----------------------------------------------------------------------------
  276. Vector CAI_BaseNPC::CalcThrowVelocity(const Vector &startPos, const Vector &endPos, float fGravity, float fArcSize)
  277. {
  278. // Get the height I have to throw to get to the target
  279. float stepHeight = endPos.z - startPos.z;
  280. float throwHeight = 0;
  281. // -----------------------------------------------------------------
  282. // Now calcluate the distance to a point halfway between our current
  283. // and target position. (the apex of our throwing arc)
  284. // -----------------------------------------------------------------
  285. Vector targetDir2D = endPos - startPos;
  286. targetDir2D.z = 0;
  287. float distance = VectorNormalize(targetDir2D);
  288. // If jumping up we want to throw a bit higher than the height diff
  289. if (stepHeight > 0)
  290. {
  291. throwHeight = stepHeight + fArcSize;
  292. }
  293. else
  294. {
  295. throwHeight = fArcSize;
  296. }
  297. // Make sure that I at least catch some air
  298. if (throwHeight < fArcSize)
  299. {
  300. throwHeight = fArcSize;
  301. }
  302. // -------------------------------------------------------------
  303. // calculate the vertical and horizontal launch velocities
  304. // -------------------------------------------------------------
  305. float velVert = (float)sqrt(2.0f*fGravity*throwHeight);
  306. float divisor = velVert;
  307. divisor += (float)sqrt((2.0f*(-fGravity)*(stepHeight-throwHeight)));
  308. float velHorz = (distance * fGravity)/divisor;
  309. // -----------------------------------------------------------
  310. // Make the horizontal throw vector and add vertical component
  311. // -----------------------------------------------------------
  312. Vector throwVel = targetDir2D * velHorz;
  313. throwVel.z = velVert;
  314. return throwVel;
  315. }
  316. bool CAI_BaseNPC::ShouldMoveWait()
  317. {
  318. return (m_flMoveWaitFinished > gpGlobals->curtime);
  319. }
  320. float CAI_BaseNPC::GetStepDownMultiplier() const
  321. {
  322. return m_pNavigator->GetStepDownMultiplier();
  323. }
  324. //-----------------------------------------------------------------------------
  325. // Purpose: execute any movement this sequence may have
  326. // Output :
  327. //-----------------------------------------------------------------------------
  328. bool CAI_BaseNPC::AutoMovement( CBaseEntity *pTarget, AIMoveTrace_t *pTraceResult )
  329. {
  330. return AutoMovement( GetAnimTimeInterval(), pTarget, pTraceResult );
  331. }
  332. //-----------------------------------------------------------------------------
  333. // Purpose:
  334. // Input : flInterval -
  335. // -
  336. // *pTraceResult -
  337. // Output : Returns true on success, false on failure.
  338. //-----------------------------------------------------------------------------
  339. bool CAI_BaseNPC::AutoMovement( float flInterval, CBaseEntity *pTarget, AIMoveTrace_t *pTraceResult )
  340. {
  341. bool ignored;
  342. Vector newPos;
  343. QAngle newAngles;
  344. if (flInterval <= 0.0)
  345. return true;
  346. m_ScheduleState.bTaskRanAutomovement = true;
  347. if (GetIntervalMovement( flInterval, ignored, newPos, newAngles ))
  348. {
  349. // DevMsg( "%.2f : (%.1f) %.1f %.1f %.1f\n", gpGlobals->curtime, (newPos - GetLocalOrigin()).Length(), newPos.x, newPos.y, newAngles.y );
  350. if ( m_hCine )
  351. {
  352. m_hCine->ModifyScriptedAutoMovement( &newPos );
  353. }
  354. if (GetMoveType() == MOVETYPE_STEP)
  355. {
  356. if (!(GetFlags() & FL_FLY))
  357. {
  358. if ( !pTarget )
  359. {
  360. pTarget = GetNavTargetEntity();
  361. }
  362. // allow NPCs to adjust the automatic movement
  363. if ( ModifyAutoMovement( newPos ) )
  364. {
  365. // Set our motor's speed here
  366. Vector vecOriginalPosition = GetAbsOrigin();
  367. bool bResult = false;
  368. if (!TaskIsComplete())
  369. {
  370. bResult = ( GetMotor()->MoveGroundStep( newPos, pTarget, newAngles.y, false, true, pTraceResult ) == AIM_SUCCESS );
  371. }
  372. Vector change = GetAbsOrigin() - vecOriginalPosition;
  373. if (flInterval != 0)
  374. {
  375. change /= flInterval;
  376. }
  377. GetMotor()->SetMoveVel(change);
  378. return bResult;
  379. }
  380. return ( GetMotor()->MoveGroundStep( newPos, pTarget, newAngles.y, false, true, pTraceResult ) == AIM_SUCCESS );
  381. }
  382. else
  383. {
  384. // FIXME: here's no direct interface to a fly motor, plus this needs to support a state where going through the world is okay.
  385. // FIXME: add callbacks into the script system for validation
  386. // FIXME: add function on scripts to force only legal movements
  387. // FIXME: GetIntervalMovement deals in Local space, nor global. Currently now way to communicate that through these interfaces.
  388. SetLocalOrigin( newPos );
  389. SetLocalAngles( newAngles );
  390. return true;
  391. }
  392. }
  393. else if (GetMoveType() == MOVETYPE_FLY)
  394. {
  395. Vector dist = newPos - GetLocalOrigin();
  396. VectorScale( dist, 1.0 / flInterval, dist );
  397. SetLocalVelocity( dist );
  398. return true;
  399. }
  400. }
  401. return false;
  402. }
  403. //-----------------------------------------------------------------------------
  404. // Purpose: return max 1/10 second rate of turning
  405. // Input :
  406. // Output :
  407. //-----------------------------------------------------------------------------
  408. float CAI_BaseNPC::MaxYawSpeed( void )
  409. {
  410. return 45;
  411. }
  412. //-----------------------------------------------------------------------------
  413. // Returns the estimate in seconds before we reach our nav goal.
  414. // -1 means we don't know / haven't calculated it yet.
  415. //-----------------------------------------------------------------------------
  416. float CAI_BaseNPC::GetTimeToNavGoal()
  417. {
  418. float flDist = GetNavigator()->BuildAndGetPathDistToGoal();
  419. if ( flDist < 0 )
  420. {
  421. return -1.0f;
  422. }
  423. float flSpeed = GetIdealSpeed();
  424. // FIXME: needs to consider stopping time!
  425. if (flSpeed > 0 && flDist > 0)
  426. {
  427. return flDist / flSpeed;
  428. }
  429. return 0.0;
  430. }
  431. //=============================================================================