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.

1742 lines
58 KiB

  1. //====== Copyright � 1996-2005, Valve Corporation, All rights reserved. =======
  2. //
  3. // Purpose: Behavior for NPCs riding in cars (with boys)
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "ai_playerally.h"
  8. #include "ai_motor.h"
  9. #include "bone_setup.h"
  10. #include "vehicle_base.h"
  11. #include "entityblocker.h"
  12. #include "ai_behavior_passenger.h"
  13. #include "ai_pathfinder.h"
  14. #include "ai_network.h"
  15. #include "ai_node.h"
  16. #include "ai_moveprobe.h"
  17. #include "env_debughistory.h"
  18. // NOTE: This has to be the last file included!
  19. #include "tier0/memdbgon.h"
  20. // Custom activities
  21. int ACT_PASSENGER_IDLE;
  22. int ACT_PASSENGER_RANGE_ATTACK1;
  23. ConVar passenger_debug_transition( "passenger_debug_transition", "0" );
  24. ConVar passenger_impact_response_threshold( "passenger_impact_response_threshold", "-350.0" );
  25. #define ORIGIN_KEYNAME "origin"
  26. #define ANGLES_KEYNAME "angles"
  27. BEGIN_DATADESC( CAI_PassengerBehavior )
  28. DEFINE_EMBEDDED( m_vehicleState ),
  29. DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
  30. DEFINE_FIELD( m_PassengerIntent, FIELD_INTEGER ),
  31. DEFINE_FIELD( m_PassengerState, FIELD_INTEGER ),
  32. DEFINE_FIELD( m_hVehicle, FIELD_EHANDLE ),
  33. DEFINE_FIELD( m_hBlocker, FIELD_EHANDLE ),
  34. DEFINE_FIELD( m_vecTargetPosition, FIELD_POSITION_VECTOR ),
  35. DEFINE_FIELD( m_vecTargetAngles, FIELD_VECTOR ),
  36. DEFINE_FIELD( m_flOriginStartFrame, FIELD_FLOAT ),
  37. DEFINE_FIELD( m_flOriginEndFrame, FIELD_FLOAT ),
  38. DEFINE_FIELD( m_flAnglesStartFrame, FIELD_FLOAT ),
  39. DEFINE_FIELD( m_flAnglesEndFrame, FIELD_FLOAT ),
  40. DEFINE_FIELD( m_nTransitionSequence,FIELD_INTEGER ),
  41. END_DATADESC();
  42. BEGIN_SIMPLE_DATADESC( passengerVehicleState_t )
  43. DEFINE_FIELD( m_bWasBoosting, FIELD_BOOLEAN ),
  44. DEFINE_FIELD( m_bWasOverturned, FIELD_BOOLEAN ),
  45. DEFINE_FIELD( m_vecLastLocalVelocity, FIELD_VECTOR ),
  46. DEFINE_FIELD( m_vecDeltaVelocity, FIELD_VECTOR ),
  47. DEFINE_FIELD( m_vecLastAngles, FIELD_VECTOR ),
  48. DEFINE_FIELD( m_flNextWarningTime, FIELD_TIME ),
  49. DEFINE_FIELD( m_flLastSpeedSqr, FIELD_FLOAT ),
  50. DEFINE_FIELD( m_bPlayerInVehicle, FIELD_BOOLEAN ),
  51. END_DATADESC();
  52. //-----------------------------------------------------------------------------
  53. // Constructor
  54. //-----------------------------------------------------------------------------
  55. CAI_PassengerBehavior::CAI_PassengerBehavior( void ) :
  56. m_bEnabled( false ),
  57. m_hVehicle( NULL ),
  58. m_PassengerState( PASSENGER_STATE_OUTSIDE ),
  59. m_PassengerIntent( PASSENGER_INTENT_NONE ),
  60. m_nTransitionSequence( -1 )
  61. {
  62. }
  63. //-----------------------------------------------------------------------------
  64. // Purpose: Enables the behavior to run
  65. //-----------------------------------------------------------------------------
  66. void CAI_PassengerBehavior::Enable( CPropJeepEpisodic *pVehicle, bool bImmediateEnter /*= false*/ )
  67. {
  68. if ( m_bEnabled && m_hVehicle.Get() )
  69. return;
  70. m_bEnabled = true;
  71. m_hVehicle = pVehicle;
  72. SetPassengerState( PASSENGER_STATE_OUTSIDE );
  73. // Init our starting information about the vehicle
  74. InitVehicleState();
  75. }
  76. void CAI_PassengerBehavior::OnRestore()
  77. {
  78. if ( m_bEnabled && !m_hVehicle.Get() )
  79. {
  80. Disable();
  81. }
  82. BaseClass::OnRestore();
  83. }
  84. //-----------------------------------------------------------------------------
  85. // Purpose: Stops the behavior from being run
  86. //-----------------------------------------------------------------------------
  87. void CAI_PassengerBehavior::Disable( void )
  88. {
  89. m_bEnabled = false;
  90. m_hVehicle = NULL;
  91. }
  92. //-----------------------------------------------------------------------------
  93. // Purpose: Starts the process of entering a vehicle
  94. //-----------------------------------------------------------------------------
  95. void CAI_PassengerBehavior::EnterVehicle( void )
  96. {
  97. // If we're already in the vehicle, simply cancel out our intents
  98. if ( GetPassengerState() == PASSENGER_STATE_INSIDE || GetPassengerState() == PASSENGER_STATE_ENTERING )
  99. {
  100. // Clear out an exit
  101. if ( m_PassengerIntent == PASSENGER_INTENT_EXIT )
  102. {
  103. m_PassengerIntent = PASSENGER_INTENT_NONE;
  104. ClearCondition( COND_PASSENGER_ENTERING );
  105. ClearCondition( COND_PASSENGER_EXITING );
  106. }
  107. return;
  108. }
  109. // Update our internal state
  110. m_PassengerIntent = PASSENGER_INTENT_ENTER;
  111. // Otherwise set this condition and go!
  112. SetCondition( COND_PASSENGER_ENTERING );
  113. }
  114. //-----------------------------------------------------------------------------
  115. // Purpose: Starts the process of exiting a vehicle
  116. //-----------------------------------------------------------------------------
  117. void CAI_PassengerBehavior::ExitVehicle( void )
  118. {
  119. // Must be in the seat
  120. if ( GetPassengerState() == PASSENGER_STATE_OUTSIDE || GetPassengerState() == PASSENGER_STATE_EXITING )
  121. {
  122. // Clear out an entrance
  123. if ( m_PassengerIntent == PASSENGER_INTENT_ENTER )
  124. {
  125. m_PassengerIntent = PASSENGER_INTENT_NONE;
  126. SetCondition( COND_PASSENGER_CANCEL_ENTER );
  127. ClearCondition( COND_PASSENGER_ENTERING );
  128. ClearCondition( COND_PASSENGER_EXITING );
  129. }
  130. return;
  131. }
  132. // Update our internal state
  133. m_PassengerIntent = PASSENGER_INTENT_EXIT;
  134. //
  135. // Everything below this point will still attempt to exit the vehicle, once able
  136. //
  137. // Must have a valid vehicle
  138. if ( m_hVehicle == NULL )
  139. return;
  140. // Cannot exit while we're upside down
  141. if ( m_hVehicle->IsOverturned() )
  142. return;
  143. // Interrupt what we're doing
  144. SetCondition( COND_PASSENGER_EXITING );
  145. }
  146. //-----------------------------------------------------------------------------
  147. // Purpose: FIXME - This should move into something a bit more flexible
  148. //-----------------------------------------------------------------------------
  149. void CAI_PassengerBehavior::AddPhysicsPush( float force )
  150. {
  151. /*
  152. // Kick the vehicle so the player knows we've arrived
  153. Vector impulse = m_hVehicle->GetAbsOrigin() - GetOuter()->GetAbsOrigin();
  154. VectorNormalize( impulse );
  155. impulse.z = -0.75;
  156. VectorNormalize( impulse );
  157. Vector vecForce = impulse * force;
  158. m_hVehicle->VPhysicsGetObject()->ApplyForceOffset( vecForce, GetOuter()->GetAbsOrigin() );
  159. */
  160. Vector vecDir;
  161. IPhysicsObject *pObject = GetOuter()->VPhysicsGetObject();
  162. Vector vecVelocity;
  163. pObject->GetVelocity( &vecVelocity, NULL );
  164. GetOuter()->GetVectors( NULL, NULL, &vecDir );
  165. vecDir.Negate();
  166. Vector vecForce = vecDir * force;
  167. m_hVehicle->VPhysicsGetObject()->ApplyForceOffset( vecForce, GetOuter()->GetAbsOrigin() );
  168. }
  169. //-----------------------------------------------------------------------------
  170. // Purpose:
  171. // Output : Returns true on success, false on failure.
  172. //-----------------------------------------------------------------------------
  173. bool CAI_PassengerBehavior::IsPassengerHostile( void )
  174. {
  175. CBaseEntity *pPlayer = AI_GetSinglePlayer();
  176. // If the player hates or fears the passenger, they're hostile
  177. if ( GetOuter()->IRelationType( pPlayer ) == D_HT || GetOuter()->IRelationType( pPlayer ) == D_FR )
  178. return true;
  179. return false;
  180. }
  181. //-----------------------------------------------------------------------------
  182. // Purpose:
  183. //-----------------------------------------------------------------------------
  184. void CAI_PassengerBehavior::InitVehicleState( void )
  185. {
  186. // Set the player's state
  187. CBasePlayer *pPlayer = AI_GetSinglePlayer();
  188. m_vehicleState.m_bPlayerInVehicle = ( pPlayer && pPlayer->IsInAVehicle() && pPlayer->GetServerVehicle() == m_hVehicle->GetServerVehicle() );
  189. // Update our vehicle state so we don't confuse our previous velocity on the first frame!
  190. m_vehicleState.m_bWasBoosting = false;
  191. m_vehicleState.m_bWasOverturned = false;
  192. m_vehicleState.m_flNextWarningTime = 0.0f;
  193. m_vehicleState.m_vecDeltaVelocity = vec3_origin;
  194. m_vehicleState.m_flNextWarningTime = gpGlobals->curtime;
  195. m_vehicleState.m_vecLastAngles = m_hVehicle->GetAbsAngles();
  196. Vector localVelocity;
  197. GetLocalVehicleVelocity( &m_vehicleState.m_vecLastLocalVelocity );
  198. m_vehicleState.m_flLastSpeedSqr = localVelocity.LengthSqr();
  199. }
  200. //-----------------------------------------------------------------------------
  201. // Purpose: Puts the NPC in hierarchy with the vehicle and makes them intangible
  202. //-----------------------------------------------------------------------------
  203. void CAI_PassengerBehavior::FinishEnterVehicle( void )
  204. {
  205. if ( m_hVehicle == NULL )
  206. return;
  207. // Get the ultimate position we want to be in
  208. Vector vecFinalPos;
  209. QAngle vecFinalAngles;
  210. GetEntryTarget( &vecFinalPos, &vecFinalAngles );
  211. // Make sure we're exactly where we need to be
  212. GetOuter()->SetLocalOrigin( vecFinalPos );
  213. GetOuter()->SetLocalAngles( vecFinalAngles );
  214. GetOuter()->SetMoveType( MOVETYPE_NONE );
  215. GetOuter()->GetMotor()->SetYawLocked( true );
  216. // We're now riding inside the vehicle
  217. SetPassengerState( PASSENGER_STATE_INSIDE );
  218. // If we've not been told to leave immediately, we're done
  219. if ( m_PassengerIntent == PASSENGER_INTENT_ENTER )
  220. {
  221. m_PassengerIntent = PASSENGER_INTENT_NONE;
  222. }
  223. // Tell the vehicle we've succeeded
  224. m_hVehicle->NPC_FinishedEnterVehicle( GetOuter(), (IsPassengerHostile()==false) );
  225. }
  226. //-----------------------------------------------------------------------------
  227. // Purpose: Removes the NPC from the car
  228. //-----------------------------------------------------------------------------
  229. void CAI_PassengerBehavior::FinishExitVehicle( void )
  230. {
  231. if ( m_hVehicle == NULL )
  232. return;
  233. // Destroy the blocker
  234. if ( m_hBlocker != NULL )
  235. {
  236. UTIL_Remove( m_hBlocker );
  237. m_hBlocker = NULL;
  238. }
  239. // To do this, we need to be very sure we're in a good spot
  240. GetOuter()->SetCondition( COND_PROVOKED );
  241. GetOuter()->SetMoveType( MOVETYPE_STEP );
  242. GetOuter()->RemoveFlag( FL_FLY );
  243. GetOuter()->GetMotor()->SetYawLocked( false );
  244. // Re-enable the physical collisions for this NPC
  245. IPhysicsObject *pPhysObj = GetOuter()->VPhysicsGetObject();
  246. if ( pPhysObj != NULL )
  247. {
  248. pPhysObj->EnableCollisions( true );
  249. }
  250. m_hVehicle->NPC_RemovePassenger( GetOuter() );
  251. m_hVehicle->NPC_FinishedExitVehicle( GetOuter(), (IsPassengerHostile()==false) );
  252. SetPassengerState( PASSENGER_STATE_OUTSIDE );
  253. // Stop our custom move sequence
  254. GetOuter()->m_iszSceneCustomMoveSeq = NULL_STRING;
  255. // If we've not been told to enter immediately, we're done
  256. if ( m_PassengerIntent == PASSENGER_INTENT_EXIT )
  257. {
  258. m_PassengerIntent = PASSENGER_INTENT_NONE;
  259. Disable();
  260. }
  261. }
  262. //-----------------------------------------------------------------------------
  263. // Purpose: Build our custom interrupt cases for the behavior
  264. //-----------------------------------------------------------------------------
  265. void CAI_PassengerBehavior::BuildScheduleTestBits( void )
  266. {
  267. // Always interrupt when we need to get in or out
  268. if ( GetPassengerState() == PASSENGER_STATE_OUTSIDE || GetPassengerState() == PASSENGER_STATE_INSIDE )
  269. {
  270. GetOuter()->SetCustomInterruptCondition( GetClassScheduleIdSpace()->ConditionLocalToGlobal( COND_PASSENGER_ENTERING ) );
  271. GetOuter()->SetCustomInterruptCondition( GetClassScheduleIdSpace()->ConditionLocalToGlobal( COND_PASSENGER_EXITING ) );
  272. GetOuter()->SetCustomInterruptCondition( GetClassScheduleIdSpace()->ConditionLocalToGlobal( COND_PASSENGER_CANCEL_ENTER ) );
  273. }
  274. BaseClass::BuildScheduleTestBits();
  275. }
  276. //-----------------------------------------------------------------------------
  277. // Purpose: Dictates whether or not the behavior is active and working
  278. // Output : Returns true on success, false on failure.
  279. //-----------------------------------------------------------------------------
  280. bool CAI_PassengerBehavior::CanSelectSchedule( void )
  281. {
  282. return m_bEnabled;
  283. }
  284. //-----------------------------------------------------------------------------
  285. // Purpose:
  286. // Output : Returns true on success, false on failure.
  287. //-----------------------------------------------------------------------------
  288. bool CAI_PassengerBehavior::CanExitVehicle( void )
  289. {
  290. // Vehicle must not be overturned
  291. if ( m_hVehicle->IsOverturned() )
  292. return false;
  293. // Vehicle must be at rest
  294. Vector vecVelocity;
  295. m_hVehicle->GetVelocity( &vecVelocity, NULL );
  296. if ( vecVelocity.LengthSqr() > Square( 8.0f ) )
  297. return false;
  298. return true;
  299. }
  300. //-----------------------------------------------------------------------------
  301. // Purpose: Handles passengers deciding to enter or exit the vehicle
  302. // Output : int
  303. //-----------------------------------------------------------------------------
  304. int CAI_PassengerBehavior::SelectTransitionSchedule( void )
  305. {
  306. // Handle our various states
  307. if ( GetPassengerState() == PASSENGER_STATE_INSIDE )
  308. {
  309. // Exiting schedule
  310. if ( HasCondition( COND_PASSENGER_EXITING ) || m_PassengerIntent == PASSENGER_INTENT_EXIT )
  311. {
  312. if ( CanExitVehicle( ) )
  313. {
  314. ClearCondition( COND_PASSENGER_EXITING );
  315. return SCHED_PASSENGER_EXIT_VEHICLE;
  316. }
  317. }
  318. }
  319. else if ( GetPassengerState() == PASSENGER_STATE_ENTERING || GetPassengerState() == PASSENGER_STATE_EXITING )
  320. {
  321. // The following code attempts to fix up a passenger being interrupted mid-transition
  322. Warning( "SelectSchedule() called on transitioning passenger!\n" );
  323. ADD_DEBUG_HISTORY( HISTORY_AI_DECISIONS, UTIL_VarArgs( "%s(%d): SelectSchedule() called on transitioning passenger!\n", GetOuter()->GetDebugName(), GetOuter()->entindex() ) );
  324. Assert( 0 );
  325. // Correct this issue immediately
  326. if ( GetPassengerState() == PASSENGER_STATE_EXITING )
  327. {
  328. // Force them out of the vehicle to where they want to be
  329. // The teleport function is overridden for passengers, meaning that they will clean up properly when called to do so
  330. GetOuter()->Teleport( &m_vecTargetPosition, &m_vecTargetAngles, NULL );
  331. }
  332. else if ( GetPassengerState() == PASSENGER_STATE_ENTERING )
  333. {
  334. // Force them into the proper position
  335. GetOuter()->SetLocalOrigin( m_vecTargetPosition );
  336. GetOuter()->SetLocalAngles( m_vecTargetAngles );
  337. FinishEnterVehicle();
  338. }
  339. // Stop playing our animation
  340. SetActivity( ACT_RESET );
  341. }
  342. return SCHED_NONE;
  343. }
  344. //-----------------------------------------------------------------------------
  345. // Purpose: Overrides the schedule selection
  346. // Output : int - Schedule to play
  347. //-----------------------------------------------------------------------------
  348. int CAI_PassengerBehavior::SelectSchedule( void )
  349. {
  350. // Protect from this rare occurrence happening
  351. if ( m_hVehicle == NULL )
  352. {
  353. Assert( m_hVehicle != NULL );
  354. Warning( "Entity %s running passenger behavior without a valid vehicle!\n", GetName() );
  355. Disable();
  356. return BaseClass::SelectSchedule();
  357. }
  358. // See if we're transitioning in / out of the vehicle
  359. int nSchedule = SelectTransitionSchedule();
  360. if ( nSchedule != SCHED_NONE )
  361. return nSchedule;
  362. return SCHED_PASSENGER_IDLE;
  363. }
  364. //-----------------------------------------------------------------------------
  365. // Purpose:
  366. //-----------------------------------------------------------------------------
  367. int CAI_PassengerBehavior::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode )
  368. {
  369. switch( failedTask )
  370. {
  371. // For now, just sit back down
  372. case TASK_PASSENGER_DETACH_FROM_VEHICLE:
  373. return SCHED_PASSENGER_IDLE;
  374. break;
  375. }
  376. return BaseClass::SelectFailSchedule( failedSchedule, failedTask, taskFailCode );
  377. }
  378. //-----------------------------------------------------------------------------
  379. // Purpose: Finds a ground position at a given location with some delta up and down to check
  380. // Input : &in - position to check at
  381. // delta - amount of distance up and down to check
  382. // *out - ground position
  383. // Output : Returns true on success, false on failure.
  384. //-----------------------------------------------------------------------------
  385. bool CAI_PassengerBehavior::FindGroundAtPosition( const Vector &in, float flUpDelta, float flDownDelta, Vector *out )
  386. {
  387. Vector startPos = in + Vector( 0, 0, flUpDelta ); // Look up by delta
  388. Vector endPos = in - Vector( 0, 0, flDownDelta ); // Look down by delta
  389. Vector hullMin = GetOuter()->GetHullMins();
  390. Vector hullMax = GetOuter()->GetHullMaxs();
  391. // Ignore ourself and the vehicle we're referencing
  392. CTraceFilterVehicleTransition ignoreFilter( m_hVehicle, GetOuter(), COLLISION_GROUP_NONE );
  393. trace_t tr;
  394. UTIL_TraceHull( startPos, endPos, hullMin, hullMax, GetOuter()->GetAITraceMask(), &ignoreFilter, &tr );
  395. // Must not have ended up in solid space
  396. if ( tr.allsolid )
  397. {
  398. // Debug
  399. if ( passenger_debug_transition.GetBool() )
  400. {
  401. NDebugOverlay::SweptBox( tr.startpos, tr.endpos, hullMin, hullMax, vec3_angle, 255, 255, 0, 255, 1.0f );
  402. }
  403. return false;
  404. }
  405. // Must have ended up with feet on the ground
  406. if ( tr.DidHitWorld() || ( tr.m_pEnt && tr.m_pEnt->IsStandable() ) )
  407. {
  408. // Debug
  409. if ( passenger_debug_transition.GetBool() )
  410. {
  411. NDebugOverlay::SweptBox( tr.startpos, tr.endpos, hullMin, hullMax, vec3_angle, 0, 255, 0, 255, 1.0f );
  412. }
  413. *out = tr.endpos;
  414. return true;
  415. }
  416. // Ended up in the air
  417. if ( passenger_debug_transition.GetBool() )
  418. {
  419. NDebugOverlay::SweptBox( tr.startpos, tr.endpos, hullMin, hullMax, vec3_angle, 255, 0, 0, 255, 1.0f );
  420. }
  421. return false;
  422. }
  423. //-----------------------------------------------------------------------------
  424. // Purpose: Attempt to verify that a test position is on the node graph
  425. //-----------------------------------------------------------------------------
  426. bool CAI_PassengerBehavior::PointIsNavigable( const Vector &vecTargetPos )
  427. {
  428. // Attempt to local move between this point and the nearest node, ignoring anything but world architecture
  429. AIMoveTrace_t moveTrace;
  430. int iNearestNode = GetOuter()->GetPathfinder()->NearestNodeToPoint( vecTargetPos );
  431. if ( iNearestNode != NO_NODE )
  432. {
  433. // Try a movement trace between the test position and the node
  434. GetOuter()->GetMoveProbe()->MoveLimit( NAV_GROUND,
  435. g_pBigAINet->GetNodePosition(GetOuter()->GetHullType(), iNearestNode ),
  436. vecTargetPos,
  437. MASK_SOLID_BRUSHONLY,
  438. NULL,
  439. 0,
  440. &moveTrace );
  441. // See if we got close enough to call it arrived
  442. if ( ( moveTrace.vEndPosition - vecTargetPos ).LengthSqr() < Square( GetHullWidth() ) &&
  443. GetOuter()->GetMoveProbe()->CheckStandPosition( moveTrace.vEndPosition, MASK_SOLID_BRUSHONLY ) )
  444. {
  445. // NDebugOverlay::HorzArrow( vecTargetPos, moveTrace.vEndPosition, 8.0f, 255, 0, 0, 16, true, 4.0f );
  446. // NDebugOverlay::HorzArrow( vecTargetPos, g_pBigAINet->GetNodePosition(GetOuter()->GetHullType(), iNearestNode ), 8.0f, 255, 255, 0, 16, true, 4.0f );
  447. return true;
  448. }
  449. }
  450. return false;
  451. }
  452. //-----------------------------------------------------------------------------
  453. // Purpose: Gets the exit point for the passenger (on the ground)
  454. // Input : &vecOut - position the entity should be at when finished exiting
  455. // Output : Returns true on success, false on failure.
  456. //-----------------------------------------------------------------------------
  457. bool CAI_PassengerBehavior::GetExitPoint( int nSequence, Vector *vecExitPoint, QAngle *vecExitAngles )
  458. {
  459. bool bSucceeded = true;
  460. // Get the delta to the final position as will be dictated by this animation's auto movement
  461. Vector vecDeltaPos;
  462. QAngle vecDeltaAngles;
  463. GetOuter()->GetSequenceMovement( nSequence, 0.0f, 1.0f, vecDeltaPos, vecDeltaAngles );
  464. // Rotate the delta position by our starting angles
  465. Vector vecRotPos = vecDeltaPos;
  466. VectorRotate( vecRotPos, GetOuter()->GetAbsAngles(), vecDeltaPos );
  467. if ( vecExitPoint != NULL )
  468. {
  469. float flDownDelta = 64.0f;
  470. float flUpDelta = 16.0f;
  471. Vector vecGroundPos;
  472. bool bFoundGround = FindGroundAtPosition( GetOuter()->GetAbsOrigin() + vecDeltaPos, flUpDelta, flDownDelta, &vecGroundPos );
  473. if ( bFoundGround )
  474. {
  475. if ( PointIsNavigable( vecGroundPos ) == false )
  476. {
  477. bSucceeded = false;
  478. }
  479. }
  480. else
  481. {
  482. bSucceeded = false;
  483. }
  484. *vecExitPoint = vecGroundPos;
  485. }
  486. if ( vecExitAngles != NULL )
  487. {
  488. QAngle newAngles = GetOuter()->GetAbsAngles() + vecDeltaAngles;
  489. newAngles.x = UTIL_AngleMod( newAngles.x );
  490. newAngles.y = UTIL_AngleMod( newAngles.y );
  491. newAngles.z = UTIL_AngleMod( newAngles.z );
  492. *vecExitAngles = newAngles;
  493. }
  494. return bSucceeded;
  495. }
  496. //-----------------------------------------------------------------------------
  497. // Purpose: Reserve our entry point
  498. // Output : Returns true on success, false on failure.
  499. //-----------------------------------------------------------------------------
  500. bool CAI_PassengerBehavior::ReserveEntryPoint( VehicleSeatQuery_e eSeatSearchType )
  501. {
  502. // FIXME: Move all this logic into the NPC_EnterVehicle function?
  503. // Find any seat to get into
  504. int nSeatID = m_hVehicle->GetServerVehicle()->NPC_GetAvailableSeat( GetOuter(), GetRoleName(), eSeatSearchType );
  505. if ( nSeatID != VEHICLE_SEAT_INVALID )
  506. return m_hVehicle->NPC_AddPassenger( GetOuter(), GetRoleName(), nSeatID );
  507. return false;
  508. }
  509. //-----------------------------------------------------------------------------
  510. // Purpose: Determines whether the NPC can move between a start and end position of a transition
  511. // Input : &vecStartPos - start position
  512. // &vecEndPos - end position
  513. // Output : Returns true on success, false on failure.
  514. //-----------------------------------------------------------------------------
  515. bool CAI_PassengerBehavior::IsValidTransitionPoint( const Vector &vecStartPos, const Vector &vecEndPos )
  516. {
  517. // Now sweep a hull through space to see if we can validly exit there
  518. Vector vecHullMins = GetOuter()->GetHullMins() + Vector( 0, 0, GetOuter()->StepHeight()*2.0f );
  519. Vector vecHullMaxs = GetOuter()->GetHullMaxs() - Vector( 0, 0, GetOuter()->StepHeight() );
  520. trace_t tr;
  521. CTraceFilterVehicleTransition skipFilter( GetOuter(), m_hVehicle, COLLISION_GROUP_NONE );
  522. UTIL_TraceHull( vecStartPos, vecEndPos, vecHullMins, vecHullMaxs, GetOuter()->GetAITraceMask(), &skipFilter, &tr );
  523. // If we're blocked, we can't get out there
  524. if ( tr.fraction < 1.0f || tr.allsolid )
  525. {
  526. if ( passenger_debug_transition.GetBool() )
  527. {
  528. NDebugOverlay::SweptBox( vecStartPos, vecEndPos, vecHullMins, vecHullMaxs, vec3_angle, 255, 0, 0, 64, 2.0f );
  529. }
  530. return false;
  531. }
  532. return true;
  533. }
  534. //-----------------------------------------------------------------------------
  535. // Purpose: Find the proper sequence to use (weighted by priority or distance from current position)
  536. // to enter the vehicle.
  537. // Input : bNearest - Use distance as the criteria for a "best" sequence. Otherwise the order of the
  538. // seats is their priority.
  539. // Output : int - sequence index
  540. //-----------------------------------------------------------------------------
  541. int CAI_PassengerBehavior::FindEntrySequence( bool bNearest /*= false*/ )
  542. {
  543. // Get a list of all our animations
  544. const PassengerSeatAnims_t *pEntryAnims = m_hVehicle->GetServerVehicle()->NPC_GetPassengerSeatAnims( GetOuter(), PASSENGER_SEAT_ENTRY );
  545. if ( pEntryAnims == NULL )
  546. return -1;
  547. // Get the ultimate position we'll end up at
  548. Vector vecStartPos, vecEndPos;
  549. if ( m_hVehicle->GetServerVehicle()->NPC_GetPassengerSeatPosition( GetOuter(), &vecEndPos, NULL ) == false )
  550. return -1;
  551. const CPassengerSeatTransition *pTransition;
  552. Vector vecSeatDir;
  553. float flNearestDist = FLT_MAX;
  554. float flSeatDist;
  555. int nNearestSequence = -1;
  556. int nSequence;
  557. // Test each animation (sorted by priority) for the best match
  558. for ( int i = 0; i < pEntryAnims->Count(); i++ )
  559. {
  560. // Find the activity for this animation name
  561. pTransition = &pEntryAnims->Element(i);
  562. nSequence = GetOuter()->LookupSequence( STRING( pTransition->GetAnimationName() ) );
  563. if ( nSequence == -1 )
  564. continue;
  565. // Test this entry for validity
  566. if ( GetEntryPoint( nSequence, &vecStartPos ) == false )
  567. continue;
  568. // Check to see if we can use this
  569. if ( IsValidTransitionPoint( vecStartPos, vecEndPos ) )
  570. {
  571. // If we're just looking for the first, we're done
  572. if ( bNearest == false )
  573. return nSequence;
  574. // Otherwise distance is the deciding factor
  575. vecSeatDir = ( vecStartPos - GetOuter()->GetAbsOrigin() );
  576. flSeatDist = VectorNormalize( vecSeatDir );
  577. // Closer, take it
  578. if ( flSeatDist < flNearestDist )
  579. {
  580. flNearestDist = flSeatDist;
  581. nNearestSequence = nSequence;
  582. }
  583. }
  584. }
  585. return nNearestSequence;
  586. }
  587. //-----------------------------------------------------------------------------
  588. // Purpose:
  589. // Output : Returns true on success, false on failure.
  590. //-----------------------------------------------------------------------------
  591. int CAI_PassengerBehavior::FindExitSequence( void )
  592. {
  593. // Get a list of all our animations
  594. const PassengerSeatAnims_t *pExitAnims = m_hVehicle->GetServerVehicle()->NPC_GetPassengerSeatAnims( GetOuter(), PASSENGER_SEAT_EXIT );
  595. if ( pExitAnims == NULL )
  596. return -1;
  597. // Get the ultimate position we'll end up at
  598. Vector vecStartPos, vecEndPos;
  599. if ( m_hVehicle->GetServerVehicle()->NPC_GetPassengerSeatPosition( GetOuter(), &vecStartPos, NULL ) == false )
  600. return -1;
  601. // Test each animation (sorted by priority) for the best match
  602. for ( int i = 0; i < pExitAnims->Count(); i++ )
  603. {
  604. // Find the activity for this animation name
  605. int nSequence = GetOuter()->LookupSequence( STRING( pExitAnims->Element(i).GetAnimationName() ) );
  606. if ( nSequence == -1 )
  607. continue;
  608. // Test this entry for validity
  609. if ( GetExitPoint( nSequence, &vecEndPos ) == false )
  610. continue;
  611. // Check to see if we can use this
  612. if ( IsValidTransitionPoint( vecStartPos, vecEndPos ) )
  613. return nSequence;
  614. }
  615. return -1;
  616. }
  617. //-----------------------------------------------------------------------------
  618. // Purpose: Reserve our exit point so nothing moves into it while we're moving
  619. // Output : Returns true on success, false on failure.
  620. //-----------------------------------------------------------------------------
  621. bool CAI_PassengerBehavior::ReserveExitPoint( void )
  622. {
  623. // Cannot exit while we're upside down
  624. // FIXME: This is probably redundant!
  625. if ( m_hVehicle->IsOverturned() )
  626. return false;
  627. // Find the exit activity to use
  628. int nSequence = FindExitSequence();
  629. if ( nSequence == -1 )
  630. return false;
  631. // Get the exit position
  632. Vector vecGroundPos;
  633. if ( GetExitPoint( nSequence, &vecGroundPos, &m_vecTargetAngles ) == false )
  634. return false;
  635. // We have to do this specially because the activities are not named
  636. SetTransitionSequence( nSequence );
  637. // Reserve this space
  638. Vector hullMin = GetOuter()->GetHullMins();
  639. Vector hullMax = GetOuter()->GetHullMaxs();
  640. m_hBlocker = CEntityBlocker::Create( vecGroundPos, hullMin, hullMax, GetOuter(), true );
  641. // Save this destination position so we can interpolate towards it
  642. m_vecTargetPosition = vecGroundPos;
  643. // Pitch and roll must be zero when we finish!
  644. m_vecTargetAngles.x = m_vecTargetAngles.z = 0.0f;
  645. if ( passenger_debug_transition.GetBool() )
  646. {
  647. Vector vecForward;
  648. AngleVectors( m_vecTargetAngles, &vecForward, NULL, NULL );
  649. Vector vecArrowEnd = m_vecTargetPosition + ( vecForward * 64.0f );
  650. NDebugOverlay::HorzArrow( m_vecTargetPosition, vecArrowEnd, 8.0f, 255, 255, 0, 64, true, 4.0f );
  651. }
  652. return true;
  653. }
  654. //-----------------------------------------------------------------------------
  655. // Purpose: Find the exact point we'd like to start our animation from to enter
  656. // the vehicle.
  657. //-----------------------------------------------------------------------------
  658. bool CAI_PassengerBehavior::GetEntryPoint( int nSequence, Vector *vecEntryPoint, QAngle *vecEntryAngles )
  659. {
  660. bool bSucceeded = true;
  661. // Get the delta to the final position as will be dictated by this animation's auto movement
  662. Vector vecDeltaPos;
  663. QAngle vecDeltaAngles;
  664. GetOuter()->GetSequenceMovement( nSequence, 1.0f, 0.0f, vecDeltaPos, vecDeltaAngles );
  665. // Get the final position we're trying to end up at
  666. Vector vecTargetPos;
  667. QAngle vecTargetAngles;
  668. GetEntryTarget( &vecTargetPos, &vecTargetAngles );
  669. // Rotate it to match
  670. Vector vecPreDelta = vecDeltaPos;
  671. VectorRotate( vecPreDelta, vecTargetAngles, vecDeltaPos );
  672. // Offset this into the proper worldspace position
  673. vecTargetPos = vecTargetPos + vecDeltaPos;
  674. // Output the position, if requested
  675. if ( vecEntryPoint != NULL )
  676. {
  677. m_hVehicle->EntityToWorldSpace( vecTargetPos, vecEntryPoint );
  678. // Trace down to the ground to see where we'll stand
  679. Vector vecGroundPos;
  680. if ( FindGroundAtPosition( (*vecEntryPoint), 16.0f, 64.0f, &vecGroundPos ) == false )
  681. {
  682. // We failed
  683. if ( passenger_debug_transition.GetBool() )
  684. {
  685. NDebugOverlay::SweptBox( (*vecEntryPoint), vecGroundPos, GetOuter()->GetHullMins(), GetOuter()->GetHullMaxs(), vec3_angle, 255, 0, 0, 64, 2.0f );
  686. }
  687. // The floor could not be found
  688. bSucceeded = false;
  689. }
  690. // Take this position
  691. *vecEntryPoint = vecGroundPos;
  692. }
  693. // Output the angles, if requested
  694. if ( vecEntryAngles != NULL )
  695. {
  696. // Add our delta angles to find what angles to start at
  697. *vecEntryAngles = vecTargetAngles;
  698. vecEntryAngles->y = UTIL_AngleMod( vecTargetAngles.y + vecDeltaAngles.y );
  699. //Transform those angles to worldspace
  700. matrix3x4_t angToParent, angToWorld;
  701. AngleMatrix( (*vecEntryAngles), angToParent );
  702. ConcatTransforms( m_hVehicle->EntityToWorldTransform(), angToParent, angToWorld );
  703. MatrixAngles( angToWorld, (*vecEntryAngles) );
  704. }
  705. // Debug info
  706. if ( passenger_debug_transition.GetBool() && vecEntryPoint && vecEntryAngles )
  707. {
  708. NDebugOverlay::Axis( *vecEntryPoint, vecTargetAngles, 16, true, 4.0f );
  709. NDebugOverlay::Cross3D( *vecEntryPoint, 4, 255, 255, 0, true, 4.0f );
  710. if ( vecEntryAngles != NULL )
  711. {
  712. Vector vecForward;
  713. AngleVectors( (*vecEntryAngles), &vecForward, NULL, NULL );
  714. Vector vecArrowEnd = (*vecEntryPoint ) + ( vecForward * 64.0f );
  715. NDebugOverlay::HorzArrow( (*vecEntryPoint), vecArrowEnd, 8.0f, 0, 255, 0, 64, true, 4.0f );
  716. }
  717. }
  718. return bSucceeded;
  719. }
  720. //-----------------------------------------------------------------------------
  721. // Purpose: Do the low-level work to detach us from our vehicle
  722. //-----------------------------------------------------------------------------
  723. void CAI_PassengerBehavior::DetachFromVehicle( void )
  724. {
  725. // Detach from the parent
  726. GetOuter()->SetParent( NULL );
  727. GetOuter()->SetMoveType( MOVETYPE_STEP );
  728. GetOuter()->AddFlag( FL_FLY );
  729. GetOuter()->SetGroundEntity( NULL );
  730. GetOuter()->SetCollisionGroup( COLLISION_GROUP_NPC );
  731. m_hVehicle->RemovePhysicsChild( GetOuter() );
  732. }
  733. //-----------------------------------------------------------------------------
  734. // Purpose:
  735. //-----------------------------------------------------------------------------
  736. void CAI_PassengerBehavior::AttachToVehicle( void )
  737. {
  738. // Parent to the vehicle
  739. GetOuter()->ClearForceCrouch();
  740. GetOuter()->SetParent( m_hVehicle );
  741. GetOuter()->AddFlag( FL_FLY );
  742. GetOuter()->SetGroundEntity( NULL );
  743. GetOuter()->SetCollisionGroup( COLLISION_GROUP_IN_VEHICLE );
  744. // Turn off physical interactions while we're in the vehicle
  745. IPhysicsObject *pPhysObj = GetOuter()->VPhysicsGetObject();
  746. if ( pPhysObj != NULL )
  747. {
  748. pPhysObj->EnableCollisions( false );
  749. }
  750. // Set our destination target
  751. GetEntryTarget( &m_vecTargetPosition, &m_vecTargetAngles );
  752. // Get physics messages from our attached physics object
  753. m_hVehicle->AddPhysicsChild( GetOuter() );
  754. }
  755. //-----------------------------------------------------------------------------
  756. // Purpose: Handle task starting
  757. //-----------------------------------------------------------------------------
  758. void CAI_PassengerBehavior::StartTask( const Task_t *pTask )
  759. {
  760. switch ( pTask->iTask )
  761. {
  762. case TASK_PASSENGER_ENTER_VEHICLE:
  763. {
  764. // You must have set your entrance animation before this point!
  765. Assert( m_nTransitionSequence != -1 );
  766. // Start us playing the correct sequence
  767. GetOuter()->SetIdealActivity( ACT_SCRIPT_CUSTOM_MOVE );
  768. SetPassengerState( PASSENGER_STATE_ENTERING );
  769. // Overlaying any gestures will mess us up, so don't allow it
  770. GetOuter()->RemoveAllGestures();
  771. }
  772. break;
  773. case TASK_PASSENGER_EXIT_VEHICLE:
  774. {
  775. // You must have set your entrance animation before this point!
  776. Assert( m_nTransitionSequence != -1 );
  777. // Start us playing the correct sequence
  778. GetOuter()->SetIdealActivity( ACT_SCRIPT_CUSTOM_MOVE );
  779. // Overlaying any gestures will mess us up, so don't allow it
  780. GetOuter()->RemoveAllGestures();
  781. }
  782. break;
  783. case TASK_PASSENGER_ATTACH_TO_VEHICLE:
  784. {
  785. AttachToVehicle();
  786. TaskComplete();
  787. }
  788. break;
  789. case TASK_PASSENGER_DETACH_FROM_VEHICLE:
  790. {
  791. // Place an entity blocker where we're going to go
  792. if ( ReserveExitPoint() == false )
  793. {
  794. OnExitVehicleFailed();
  795. TaskFail("Failed to find valid exit point\n");
  796. return;
  797. }
  798. // Physically detach from the vehicle
  799. DetachFromVehicle();
  800. // Mark that we're now disembarking
  801. SetPassengerState( PASSENGER_STATE_EXITING );
  802. TaskComplete();
  803. }
  804. break;
  805. case TASK_PASSENGER_SET_IDEAL_ENTRY_YAW:
  806. {
  807. // Get the ideal facing to enter the vehicle
  808. QAngle vecEntryAngles;
  809. GetEntryPoint( m_nTransitionSequence, NULL, &vecEntryAngles );
  810. GetOuter()->GetMotor()->SetIdealYaw( vecEntryAngles.y );
  811. TaskComplete();
  812. return;
  813. }
  814. break;
  815. default:
  816. BaseClass::StartTask( pTask );
  817. break;
  818. }
  819. }
  820. //-----------------------------------------------------------------------------
  821. // Purpose: Handle task running
  822. //-----------------------------------------------------------------------------
  823. void CAI_PassengerBehavior::RunTask( const Task_t *pTask )
  824. {
  825. switch ( pTask->iTask )
  826. {
  827. case TASK_PASSENGER_ENTER_VEHICLE:
  828. {
  829. // Correct for angular/spatial deviation
  830. Assert( GetSequence() == m_nTransitionSequence );
  831. if ( GetSequence() != m_nTransitionSequence )
  832. {
  833. Warning("Corrected entrance animation on vehicle enter!\n");
  834. GetOuter()->SetIdealActivity( ACT_SCRIPT_CUSTOM_MOVE );
  835. GetOuter()->GetNavigator()->ClearGoal();
  836. SetTransitionSequence( m_nTransitionSequence );
  837. }
  838. bool corrected = DoTransitionMovement();
  839. // We must be done with the animation and in the correct position
  840. if ( corrected == false )
  841. {
  842. FinishEnterVehicle();
  843. TaskComplete();
  844. }
  845. }
  846. break;
  847. case TASK_PASSENGER_EXIT_VEHICLE:
  848. {
  849. // Correct for angular/spatial deviation
  850. Assert( GetSequence() == m_nTransitionSequence );
  851. if ( GetSequence() != m_nTransitionSequence )
  852. {
  853. Warning("Corrected exit animation on vehicle exit!\n");
  854. GetOuter()->SetIdealActivity( ACT_SCRIPT_CUSTOM_MOVE );
  855. GetOuter()->GetNavigator()->ClearGoal();
  856. SetTransitionSequence( m_nTransitionSequence );
  857. }
  858. // Correct for angular/spatial deviation
  859. bool corrected = DoTransitionMovement();
  860. // We must be done with the animation and in the correct position
  861. if ( corrected == false )
  862. {
  863. FinishExitVehicle();
  864. TaskComplete();
  865. }
  866. }
  867. break;
  868. default:
  869. BaseClass::RunTask( pTask );
  870. break;
  871. }
  872. }
  873. //-----------------------------------------------------------------------------
  874. // Purpose: Find the blend amounts for position and angles, given a point in
  875. // time within a sequence
  876. //-----------------------------------------------------------------------------
  877. bool CAI_PassengerBehavior::GetSequenceBlendAmount( float flCycle, float *posBlend, float *angBlend )
  878. {
  879. // Find positional blend, if requested
  880. if ( posBlend != NULL )
  881. {
  882. float flFrac = RemapValClamped( flCycle, m_flOriginStartFrame, m_flOriginEndFrame, 0.0f, 1.0f );
  883. (*posBlend) = SimpleSpline( flFrac );
  884. }
  885. // Find angular blend, if requested
  886. if ( angBlend != NULL )
  887. {
  888. float flFrac = RemapValClamped( flCycle, m_flAnglesStartFrame, m_flAnglesEndFrame, 0.0f, 1.0f );
  889. (*angBlend) = SimpleSpline( flFrac );
  890. }
  891. return true;
  892. }
  893. //-----------------------------------------------------------------------------
  894. // Purpose: Returns the target destination for the entry animation
  895. //-----------------------------------------------------------------------------
  896. void CAI_PassengerBehavior::GetEntryTarget( Vector *vecOrigin, QAngle *vecAngles )
  897. {
  898. // Get the ultimate position we'll end up at
  899. m_hVehicle->GetServerVehicle()->NPC_GetPassengerSeatPositionLocal( GetOuter(), vecOrigin, vecAngles );
  900. }
  901. //-----------------------------------------------------------------------------
  902. // Purpose: Returns the ideal position to be in to end up at the target at the
  903. // end of the animation.
  904. //-----------------------------------------------------------------------------
  905. void CAI_PassengerBehavior::GetTransitionAnimationIdeal( float flCycle, const Vector &vecTargetPos, const QAngle &vecTargetAngles, Vector *idealOrigin, QAngle *idealAngles )
  906. {
  907. // Get the position in time working backwards from our goal
  908. Vector vecDeltaPos;
  909. QAngle vecDeltaAngles;
  910. GetOuter()->GetSequenceMovement( GetSequence(), 1.0f, flCycle, vecDeltaPos, vecDeltaAngles );
  911. // Rotate the delta by our local angles
  912. Vector vecPreDelta = vecDeltaPos;
  913. VectorRotate( vecPreDelta, vecTargetAngles, vecDeltaPos );
  914. // Ideal origin
  915. if ( idealOrigin != NULL )
  916. {
  917. *idealOrigin = ( vecTargetPos + vecDeltaPos );
  918. }
  919. // Ideal angles
  920. if ( idealAngles != NULL )
  921. {
  922. (*idealAngles).x = anglemod( vecTargetAngles.x + vecDeltaAngles.x );
  923. (*idealAngles).y = anglemod( vecTargetAngles.y + vecDeltaAngles.y );
  924. (*idealAngles).z = anglemod( vecTargetAngles.z + vecDeltaAngles.z );
  925. }
  926. }
  927. //-----------------------------------------------------------------------------
  928. // FIXME: This is basically a complete duplication of GetIntervalMovement
  929. // which doesn't remove the x and z components of the angles. This
  930. // should be consolidated to not replicate so much code! -- jdw
  931. //-----------------------------------------------------------------------------
  932. bool CAI_PassengerBehavior::LocalIntervalMovement( float flInterval, bool &bMoveSeqFinished, Vector &newPosition, QAngle &newAngles )
  933. {
  934. CStudioHdr *pstudiohdr = GetOuter()->GetModelPtr();
  935. if ( pstudiohdr == NULL )
  936. return false;
  937. // Get our next cycle point
  938. float flNextCycle = GetNextCycleForInterval( GetSequence(), flInterval );
  939. // Fix-up loops
  940. if ( ( GetOuter()->SequenceLoops() == false ) && flNextCycle > 1.0f )
  941. {
  942. flInterval = GetOuter()->GetCycle() / ( GetOuter()->GetSequenceCycleRate( GetSequence() ) * GetOuter()->GetPlaybackRate() );
  943. flNextCycle = 1.0f;
  944. bMoveSeqFinished = true;
  945. }
  946. else
  947. {
  948. bMoveSeqFinished = false;
  949. }
  950. Vector deltaPos;
  951. QAngle deltaAngles;
  952. // Find the delta position and delta angles for this sequence
  953. if ( Studio_SeqMovement( pstudiohdr, GetOuter()->GetSequence(), GetOuter()->GetCycle(), flNextCycle, GetOuter()->GetPoseParameterArray(), deltaPos, deltaAngles ))
  954. {
  955. Vector vecPreDelta = deltaPos;
  956. VectorRotate( vecPreDelta, GetOuter()->GetLocalAngles(), deltaPos );
  957. newPosition = GetLocalOrigin() + deltaPos;
  958. newAngles = GetLocalAngles() + deltaAngles;
  959. return true;
  960. }
  961. else
  962. {
  963. newPosition = GetLocalOrigin();
  964. newAngles = GetLocalAngles();
  965. return false;
  966. }
  967. return false;
  968. }
  969. //-----------------------------------------------------------------------------
  970. // Purpose: Get the next cycle point in a sequence for a given interval
  971. //-----------------------------------------------------------------------------
  972. float CAI_PassengerBehavior::GetNextCycleForInterval( int nSequence, float flInterval )
  973. {
  974. return GetOuter()->GetCycle() + flInterval * GetOuter()->GetSequenceCycleRate( GetSequence() ) * GetOuter()->GetPlaybackRate();
  975. }
  976. //-----------------------------------------------------------------------------
  977. // Purpose: Draw debug information for the transitional movement
  978. //-----------------------------------------------------------------------------
  979. void CAI_PassengerBehavior::DrawDebugTransitionInfo( const Vector &vecIdealPos, const QAngle &vecIdealAngles, const Vector &vecAnimPos, const QAngle &vecAnimAngles )
  980. {
  981. // Debug info
  982. if ( GetPassengerState() == PASSENGER_STATE_ENTERING )
  983. {
  984. // Green - Ideal location
  985. Vector foo;
  986. m_hVehicle->EntityToWorldSpace( vecIdealPos, &foo );
  987. NDebugOverlay::Cross3D( foo, 2, 0, 255, 0, true, 0.1f );
  988. NDebugOverlay::Axis( foo, vecIdealAngles, 8, true, 0.1f );
  989. // Blue - Actual location
  990. m_hVehicle->EntityToWorldSpace( vecAnimPos, &foo );
  991. NDebugOverlay::Cross3D( foo, 2, 0, 0, 255, true, 0.1f );
  992. NDebugOverlay::Axis( foo, vecAnimAngles, 8, true, 0.1f );
  993. }
  994. else
  995. {
  996. // Green - Ideal location
  997. NDebugOverlay::Cross3D( vecIdealPos, 4, 0, 255, 0, true, 0.1f );
  998. NDebugOverlay::Axis( vecIdealPos, vecIdealAngles, 8, true, 0.1f );
  999. // Blue - Actual location
  1000. NDebugOverlay::Cross3D( vecAnimPos, 2, 0, 0, 255, true, 0.1f );
  1001. NDebugOverlay::Axis( vecAnimPos, vecAnimAngles, 8, true, 0.1f );
  1002. }
  1003. }
  1004. //-----------------------------------------------------------------------------
  1005. // Purpose: Local movement to enter or exit the vehicle
  1006. // Output : Returns true on success, false on failure.
  1007. //-----------------------------------------------------------------------------
  1008. bool CAI_PassengerBehavior::DoTransitionMovement( void )
  1009. {
  1010. // Get our animation's extrapolated end position
  1011. Vector vecAnimPos;
  1012. QAngle vecAnimAngles;
  1013. float flInterval = GetOuter()->GetAnimTimeInterval();
  1014. bool bSequenceFinished;
  1015. // Get the position we're moving to for this frame with our animation's motion
  1016. if ( LocalIntervalMovement( flInterval, bSequenceFinished, vecAnimPos, vecAnimAngles ) )
  1017. {
  1018. // Get the position we'd ideally be in
  1019. Vector vecIdealPos;
  1020. QAngle vecIdealAngles;
  1021. float flNextCycle = GetNextCycleForInterval( GetOuter()->GetSequence(), flInterval );
  1022. flNextCycle = clamp( flNextCycle, 0.0f, 1.0f );
  1023. GetTransitionAnimationIdeal( flNextCycle, m_vecTargetPosition, m_vecTargetAngles, &vecIdealPos, &vecIdealAngles );
  1024. // Get the amount of error to blend out
  1025. float flPosBlend = 1.0f;
  1026. float flAngBlend = 1.0f;
  1027. GetSequenceBlendAmount( flNextCycle, &flPosBlend, &flAngBlend );
  1028. // Find the error between our position and our ideal
  1029. Vector vecDelta = ( vecIdealPos - vecAnimPos ) * flPosBlend;
  1030. QAngle vecDeltaAngles;
  1031. vecDeltaAngles.x = AngleDiff( vecIdealAngles.x, vecAnimAngles.x ) * flAngBlend;
  1032. vecDeltaAngles.y = AngleDiff( vecIdealAngles.y, vecAnimAngles.y ) * flAngBlend;
  1033. vecDeltaAngles.z = AngleDiff( vecIdealAngles.z, vecAnimAngles.z ) * flAngBlend;
  1034. // Factor in the error
  1035. GetOuter()->SetLocalOrigin( vecAnimPos + vecDelta );
  1036. GetOuter()->SetLocalAngles( vecAnimAngles + vecDeltaAngles );
  1037. // Draw our debug information
  1038. if ( passenger_debug_transition.GetBool() )
  1039. {
  1040. DrawDebugTransitionInfo( vecIdealPos, vecIdealAngles, vecAnimPos, vecAnimAngles );
  1041. }
  1042. // We're done moving
  1043. if ( bSequenceFinished )
  1044. return false;
  1045. // We're still correcting out the error
  1046. return true;
  1047. }
  1048. // There was no movement in the animation
  1049. return false;
  1050. }
  1051. //-----------------------------------------------------------------------------
  1052. // Purpose: Translate normal schedules into vehicle schedules
  1053. //-----------------------------------------------------------------------------
  1054. int CAI_PassengerBehavior::TranslateSchedule( int scheduleType )
  1055. {
  1056. if ( GetPassengerState() == PASSENGER_STATE_INSIDE )
  1057. {
  1058. // Always be seated when riding in the car!
  1059. if ( scheduleType == SCHED_IDLE_STAND )
  1060. return SCHED_PASSENGER_IDLE;
  1061. }
  1062. return BaseClass::TranslateSchedule( scheduleType );
  1063. }
  1064. //-----------------------------------------------------------------------------
  1065. // Purpose: Returns the velocity of the vehicle with respect to its orientation
  1066. //-----------------------------------------------------------------------------
  1067. void CAI_PassengerBehavior::GetLocalVehicleVelocity( Vector *pOut )
  1068. {
  1069. Vector velocity;
  1070. m_hVehicle->GetVelocity( &velocity, NULL );
  1071. m_hVehicle->WorldToEntitySpace( m_hVehicle->GetAbsOrigin() + velocity, pOut );
  1072. }
  1073. //-----------------------------------------------------------------------------
  1074. // Purpose: Gather conditions we can comment on or react to while riding in the vehicle
  1075. //-----------------------------------------------------------------------------
  1076. void CAI_PassengerBehavior::GatherVehicleStateConditions( void )
  1077. {
  1078. // Must have a vehicle to bother with this
  1079. if ( m_hVehicle == NULL )
  1080. return;
  1081. // Clear out transient conditions
  1082. ClearCondition( COND_PASSENGER_HARD_IMPACT );
  1083. ClearCondition( COND_PASSENGER_ERRATIC_DRIVING );
  1084. ClearCondition( COND_PASSENGER_JOSTLE_SMALL );
  1085. ClearCondition( COND_PASSENGER_VEHICLE_STARTED );
  1086. ClearCondition( COND_PASSENGER_VEHICLE_STOPPED );
  1087. ClearCondition( COND_PASSENGER_PLAYER_ENTERED_VEHICLE );
  1088. ClearCondition( COND_PASSENGER_PLAYER_EXITED_VEHICLE );
  1089. CBasePlayer *pPlayer = AI_GetSinglePlayer();
  1090. if ( pPlayer )
  1091. {
  1092. if ( pPlayer->IsInAVehicle() && pPlayer->GetVehicle() == m_hVehicle->GetServerVehicle() )
  1093. {
  1094. if ( m_vehicleState.m_bPlayerInVehicle == false )
  1095. {
  1096. SetCondition( COND_PASSENGER_PLAYER_ENTERED_VEHICLE );
  1097. m_vehicleState.m_bPlayerInVehicle = true;
  1098. }
  1099. }
  1100. else
  1101. {
  1102. if ( m_vehicleState.m_bPlayerInVehicle )
  1103. {
  1104. SetCondition( COND_PASSENGER_PLAYER_EXITED_VEHICLE );
  1105. m_vehicleState.m_bPlayerInVehicle = false;
  1106. }
  1107. }
  1108. }
  1109. // Get the vehicle's boost state
  1110. if ( m_hVehicle->m_nBoostTimeLeft < 100.0f )
  1111. {
  1112. if ( m_vehicleState.m_bWasBoosting == false )
  1113. {
  1114. m_vehicleState.m_bWasBoosting = true;
  1115. }
  1116. }
  1117. else
  1118. {
  1119. m_vehicleState.m_bWasBoosting = false;
  1120. }
  1121. // Detect being overturned
  1122. if ( m_hVehicle->IsOverturned() )
  1123. {
  1124. SetCondition( COND_PASSENGER_OVERTURNED );
  1125. if ( m_vehicleState.m_bWasOverturned == false )
  1126. {
  1127. m_vehicleState.m_bWasOverturned = true;
  1128. }
  1129. }
  1130. else
  1131. {
  1132. ClearCondition( COND_PASSENGER_OVERTURNED );
  1133. m_vehicleState.m_bWasOverturned = false;
  1134. }
  1135. // Get our local velocity
  1136. Vector localVelocity;
  1137. GetLocalVehicleVelocity( &localVelocity );
  1138. Vector deltaVelocity = ( localVelocity - m_vehicleState.m_vecLastLocalVelocity );
  1139. // Detect a sudden stop!
  1140. if ( deltaVelocity.y < passenger_impact_response_threshold.GetFloat() )
  1141. {
  1142. SetCondition( COND_PASSENGER_HARD_IMPACT );
  1143. }
  1144. else if ( fabs( deltaVelocity.x ) > 200.0f || fabs( deltaVelocity.z ) > 75.0f )
  1145. {
  1146. // The X axis represents lateral movement and the Z axis represents vertical movement{
  1147. SetCondition( COND_PASSENGER_ERRATIC_DRIVING );
  1148. }
  1149. else if ( fabs( deltaVelocity.x ) > 50.0f || fabs( deltaVelocity.z ) > 25.0f )
  1150. {
  1151. // Lightly jostled
  1152. SetCondition( COND_PASSENGER_JOSTLE_SMALL );
  1153. }
  1154. // Get our speed
  1155. float flSpeedSqr = localVelocity.LengthSqr();
  1156. // See if we've crossed over the threshold between movement to stillness
  1157. if ( m_vehicleState.m_flLastSpeedSqr > STOPPED_VELOCITY_THRESHOLD_SQR && flSpeedSqr < STOPPED_VELOCITY_THRESHOLD_SQR )
  1158. {
  1159. SetCondition( COND_PASSENGER_VEHICLE_STOPPED );
  1160. }
  1161. else if ( m_vehicleState.m_flLastSpeedSqr < STARTED_VELOCITY_THRESHOLD_SQR && flSpeedSqr > STARTED_VELOCITY_THRESHOLD_SQR )
  1162. {
  1163. // See if we've crossed over the threshold between stillness to movement
  1164. SetCondition( COND_PASSENGER_VEHICLE_STARTED );
  1165. }
  1166. // Save this as our last speed
  1167. m_vehicleState.m_flLastSpeedSqr = flSpeedSqr;
  1168. // Find our delta velocity from the last frame
  1169. m_vehicleState.m_vecDeltaVelocity = ( localVelocity - m_vehicleState.m_vecLastLocalVelocity );
  1170. m_vehicleState.m_vecLastLocalVelocity = localVelocity;
  1171. // Get our angular velocity
  1172. Vector vecVelocity;
  1173. AngularImpulse angVelocty;
  1174. m_hVehicle->GetVelocity( &vecVelocity, &angVelocty );
  1175. QAngle angVel( angVelocty.x, angVelocty.y, angVelocty.z );
  1176. // Blend this into the old values
  1177. m_vehicleState.m_vecLastAngles = ( m_vehicleState.m_vecLastAngles * 0.2f ) + ( angVel * 0.8f );
  1178. }
  1179. //-----------------------------------------------------------------------------
  1180. // Purpose: Do some pre-schedule clean-up
  1181. //-----------------------------------------------------------------------------
  1182. void CAI_PassengerBehavior::PrescheduleThink( void )
  1183. {
  1184. BaseClass::PrescheduleThink();
  1185. // If we're outside the vehicle, we need to turn this behavior off immediately
  1186. if ( GetPassengerState() == PASSENGER_STATE_OUTSIDE && HasCondition( COND_PASSENGER_CANCEL_ENTER ) )
  1187. {
  1188. // Clear out our passenger intent
  1189. m_PassengerIntent = PASSENGER_INTENT_NONE;
  1190. ClearCondition( COND_PASSENGER_CANCEL_ENTER );
  1191. // Stop pathfinding
  1192. GetOuter()->GetNavigator()->ClearGoal();
  1193. // We're outside and have no intent to enter, so we're done
  1194. Disable();
  1195. // This must be stomped to cause our behavior to relinquish control
  1196. GetOuter()->ClearSchedule("Passenger enter canceled");
  1197. }
  1198. #ifdef DEBUG
  1199. if ( GetPassengerState() == PASSENGER_STATE_INSIDE )
  1200. {
  1201. Vector vecSeatOrigin;
  1202. QAngle vecSeatAngles;
  1203. if ( m_hVehicle && m_hVehicle->GetServerVehicle()->NPC_GetPassengerSeatPositionLocal( GetOuter(), &vecSeatOrigin, &vecSeatAngles ) )
  1204. {
  1205. if ( ( GetLocalOrigin() - vecSeatOrigin ).LengthSqr() > Square( 0.1f ) )
  1206. {
  1207. Warning( "Passenger has strayed from seat position!\n" );
  1208. // GetOuter()->SetLocalOrigin( vecSeatOrigin );
  1209. // GetOuter()->SetLocalAngles( vecSeatAngles );
  1210. }
  1211. }
  1212. else
  1213. {
  1214. Warning( "Passenger is in vehicle without a valid seat position! -- EJECTED\n" );
  1215. GetOuter()->SetParent( NULL );
  1216. Disable();
  1217. return;
  1218. }
  1219. }
  1220. #endif // DEBUG
  1221. }
  1222. //-----------------------------------------------------------------------------
  1223. // Purpose: Gather conditions for our use in making decisions
  1224. //-----------------------------------------------------------------------------
  1225. void CAI_PassengerBehavior::GatherConditions( void )
  1226. {
  1227. if ( IsEnabled() == false )
  1228. return BaseClass::GatherConditions();
  1229. // Sense the state of the car
  1230. GatherVehicleStateConditions();
  1231. BaseClass::GatherConditions();
  1232. }
  1233. //-----------------------------------------------------------------------------
  1234. // Purpose:
  1235. //-----------------------------------------------------------------------------
  1236. void CAI_PassengerBehavior::ModifyOrAppendCriteria( AI_CriteriaSet& criteriaSet )
  1237. {
  1238. if ( m_hVehicle == NULL )
  1239. return;
  1240. // Mark whether we're overturned or not
  1241. bool bOverturned = m_hVehicle->IsOverturned();
  1242. criteriaSet.AppendCriteria( "vehicle_overturned", bOverturned ? "1" : "0" );
  1243. // Denote whether we're in the vehicle or not
  1244. bool bInsideVehicle = ( GetPassengerState() == PASSENGER_STATE_INSIDE );
  1245. criteriaSet.AppendCriteria( "vehicle_inside", bInsideVehicle ? "1" : "0" );
  1246. // Note what angle we're at (extreme or normal)
  1247. Vector vecUp( 0.0f, 0.0f, 1.0f );
  1248. Vector vecVehicleUp;
  1249. m_hVehicle->GetVectors( NULL, NULL, &vecVehicleUp );
  1250. float flVehicleUp = DotProduct( vecVehicleUp, vecUp );
  1251. criteriaSet.AppendCriteria( "vehicle_tilt", UTIL_VarArgs( "%.2f", flVehicleUp ) );
  1252. // Set the vehicle's speed (necessary for certain types of movement judgments)
  1253. float flVehicleSpeed = sqrt( m_vehicleState.m_flLastSpeedSqr );
  1254. criteriaSet.AppendCriteria( "vehicle_speed", UTIL_VarArgs( "%f", flVehicleSpeed ) );
  1255. // Whether or not the passenger is currently able to enter the vehicle (only accounts for locking really)
  1256. bool bCanExitVehicle = ( m_hVehicle->NPC_CanExitVehicle( GetOuter(), true ) );
  1257. criteriaSet.AppendCriteria( "vehicle_can_exit", bCanExitVehicle ? "1" : "0" );
  1258. // Whether or not the passenger is currently able to exit the vehicle (only accounts for locking really)
  1259. bool bCanEnterVehicle = ( m_hVehicle->NPC_CanEnterVehicle( GetOuter(), true ) );
  1260. criteriaSet.AppendCriteria( "vehicle_can_enter", bCanEnterVehicle ? "1" : "0" );
  1261. }
  1262. //-----------------------------------------------------------------------------
  1263. // Purpose: Cache off our frame numbers from the sequence keyvalue blocks
  1264. //-----------------------------------------------------------------------------
  1265. void CAI_PassengerBehavior::CacheBlendTargets( void )
  1266. {
  1267. // Get the keyvalues for this sequence
  1268. KeyValues *seqValues = GetOuter()->GetSequenceKeyValues( m_nTransitionSequence );
  1269. KeyValues::AutoDelete autodelete_key( seqValues );
  1270. if ( seqValues == NULL )
  1271. {
  1272. Assert( 0 );
  1273. return;
  1274. }
  1275. // Get the entry/exit subkeys
  1276. KeyValues *blendValues = seqValues->FindKey( "entryexit_blend" );
  1277. if ( blendValues == NULL )
  1278. {
  1279. Assert( 0 );
  1280. return;
  1281. }
  1282. // Find our frame range on this sequence
  1283. int nMaxFrames = Studio_MaxFrame( GetOuter()->GetModelPtr(), m_nTransitionSequence, GetOuter()->GetPoseParameterArray() );
  1284. // Find a key by this name
  1285. KeyValues *subKeys = blendValues->FindKey( ORIGIN_KEYNAME );
  1286. if ( subKeys )
  1287. {
  1288. // Retrieve our frame numbers
  1289. m_flOriginStartFrame = subKeys->GetFloat( "startframe", 0.0f );
  1290. m_flOriginEndFrame = subKeys->GetFloat( "endframe", nMaxFrames );
  1291. // Convert to normalized values
  1292. m_flOriginStartFrame = RemapValClamped( m_flOriginStartFrame, 0, nMaxFrames, 0.0f, 1.0f );
  1293. m_flOriginEndFrame = RemapValClamped( m_flOriginEndFrame, 0, nMaxFrames, 0.0f, 1.0f );
  1294. }
  1295. // Find a key by this name
  1296. subKeys = blendValues->FindKey( ANGLES_KEYNAME );
  1297. if ( subKeys )
  1298. {
  1299. // Retrieve our frame numbers
  1300. m_flAnglesStartFrame = subKeys->GetFloat( "startframe", 0.0f );
  1301. m_flAnglesEndFrame = subKeys->GetFloat( "endframe", nMaxFrames );
  1302. // Convert to normalized values
  1303. m_flAnglesStartFrame = RemapValClamped( m_flAnglesStartFrame, 0, nMaxFrames, 0.0f, 1.0f );
  1304. m_flAnglesEndFrame = RemapValClamped( m_flAnglesEndFrame, 0, nMaxFrames, 0.0f, 1.0f );
  1305. }
  1306. }
  1307. //-----------------------------------------------------------------------------
  1308. // Purpose:
  1309. //-----------------------------------------------------------------------------
  1310. void CAI_PassengerBehavior::SetTransitionSequence( int nSequence )
  1311. {
  1312. // We need to use the ACT_SCRIPT_CUSTOM_MOVE scenario for this type of custom anim
  1313. m_nTransitionSequence = nSequence;
  1314. GetOuter()->m_iszSceneCustomMoveSeq = AllocPooledString( GetOuter()->GetSequenceName( m_nTransitionSequence ) );
  1315. // Cache off our blending information at this point
  1316. CacheBlendTargets();
  1317. }
  1318. //-----------------------------------------------------------------------------
  1319. // Purpose:
  1320. //-----------------------------------------------------------------------------
  1321. bool CAI_PassengerBehavior::SpeakIfAllowed( AIConcept_t concept, const char *modifiers /*= NULL*/, bool bRespondingToPlayer /*= false*/, char *pszOutResponseChosen /*= NULL*/, size_t bufsize /*= 0*/ )
  1322. {
  1323. // FIXME: Store this cast off?
  1324. CAI_PlayerAlly *pAlly = dynamic_cast<CAI_PlayerAlly *>(GetOuter());
  1325. if ( pAlly != NULL )
  1326. return pAlly->SpeakIfAllowed( concept, modifiers, bRespondingToPlayer, pszOutResponseChosen, bufsize );
  1327. return false;
  1328. }
  1329. //-----------------------------------------------------------------------------
  1330. // Purpose: Forces us to begin a dynamic scripted scene
  1331. // Input : *lpszInteractionName - Name of the sequence we'll play
  1332. // *pOther -
  1333. // Output : Returns true on success, false on failure.
  1334. //-----------------------------------------------------------------------------
  1335. bool CAI_PassengerBehavior::ForceVehicleInteraction( const char *lpszInteractionName, CBaseCombatCharacter *pOther )
  1336. {
  1337. // Don't do this unless we're sitting in the cabin of the vehicle!
  1338. if ( GetPassengerState() != PASSENGER_STATE_INSIDE )
  1339. return false;
  1340. // Set a sequence and fire it off!
  1341. GetOuter()->m_iszSceneCustomMoveSeq = AllocPooledString( lpszInteractionName );
  1342. GetOuter()->SetIdealActivity( ACT_SCRIPT_CUSTOM_MOVE );
  1343. // Slam our schedule (very unsafe!)
  1344. GetOuter()->SetSchedule( SCHED_PASSENGER_PLAY_SCRIPTED_ANIM );
  1345. return true;
  1346. }
  1347. //-----------------------------------------------------------------------------
  1348. // Purpose: Fix up teleport event when in the vehicle
  1349. //-----------------------------------------------------------------------------
  1350. void CAI_PassengerBehavior::Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity, bool bUseSlowHighAccuracyContacts )
  1351. {
  1352. //First, safely remove me from the vehicle
  1353. if ( GetPassengerState() != PASSENGER_STATE_OUTSIDE )
  1354. {
  1355. // Detach from the vehicle
  1356. DetachFromVehicle();
  1357. FinishExitVehicle();
  1358. // Turn the behavior off
  1359. GetOuter()->ClearSchedule( "ai_behavior_passenger: teleport while in vehicle" );
  1360. Disable();
  1361. }
  1362. //Then allow the teleportation
  1363. BaseClass::Teleport( newPosition, newAngles, newVelocity, bUseSlowHighAccuracyContacts );
  1364. }
  1365. //-----------------------------------------------------------------------------
  1366. // Purpose: We override this function because it can completely wreak havoc if
  1367. // we're in the middle of a transition
  1368. //-----------------------------------------------------------------------------
  1369. void CAI_PassengerBehavior::ClearSchedule( const char *szReason )
  1370. {
  1371. // Cannot do this while we're transitioning, but it's also a bug because the code that called it was probably relying on this to work!
  1372. if ( GetPassengerState() == PASSENGER_STATE_ENTERING || GetPassengerState() == PASSENGER_STATE_EXITING )
  1373. {
  1374. Warning("ClearSchedule rejected due to transitioning passenger: %s\n", szReason );
  1375. return;
  1376. }
  1377. // TODO: Even this will probably need more crafting depending on what we're doing in the vehicle
  1378. // Otherwise allow it
  1379. GetOuter()->ClearSchedule( szReason );
  1380. }
  1381. //-----------------------------------------------------------------------------
  1382. // Purpose: Dictate the terms for being interrupted by scripted schedules or scenes
  1383. //-----------------------------------------------------------------------------
  1384. bool CAI_PassengerBehavior::IsInterruptable( void )
  1385. {
  1386. // NOTE: We should never be interrupted this way when in a car. This would effectively makes us go comatose if we
  1387. // start a FACETO, MOVETO, or SEQUENCE command from a VCD.
  1388. return false;
  1389. }
  1390. //-----------------------------------------------------------------------------
  1391. // Purpose:
  1392. //-----------------------------------------------------------------------------
  1393. void CAI_PassengerBehavior::CancelEnterVehicle( void )
  1394. {
  1395. // Stop!
  1396. if ( GetPassengerState() == PASSENGER_STATE_OUTSIDE )
  1397. {
  1398. SetCondition( COND_PASSENGER_CANCEL_ENTER );
  1399. }
  1400. }
  1401. // ----------------------------------------------
  1402. // Custom AI declarations
  1403. // ----------------------------------------------
  1404. AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER( CAI_PassengerBehavior )
  1405. {
  1406. DECLARE_ACTIVITY( ACT_PASSENGER_IDLE )
  1407. DECLARE_ACTIVITY( ACT_PASSENGER_RANGE_ATTACK1 )
  1408. DECLARE_CONDITION( COND_PASSENGER_HARD_IMPACT )
  1409. DECLARE_CONDITION( COND_PASSENGER_ENTERING )
  1410. DECLARE_CONDITION( COND_PASSENGER_EXITING )
  1411. DECLARE_CONDITION( COND_PASSENGER_VEHICLE_STARTED )
  1412. DECLARE_CONDITION( COND_PASSENGER_VEHICLE_STOPPED )
  1413. DECLARE_CONDITION( COND_PASSENGER_OVERTURNED )
  1414. DECLARE_CONDITION( COND_PASSENGER_CANCEL_ENTER )
  1415. DECLARE_CONDITION( COND_PASSENGER_ERRATIC_DRIVING )
  1416. DECLARE_CONDITION( COND_PASSENGER_PLAYER_ENTERED_VEHICLE )
  1417. DECLARE_CONDITION( COND_PASSENGER_PLAYER_EXITED_VEHICLE )
  1418. DECLARE_CONDITION( COND_PASSENGER_JOSTLE_SMALL )
  1419. DECLARE_TASK( TASK_PASSENGER_ENTER_VEHICLE )
  1420. DECLARE_TASK( TASK_PASSENGER_EXIT_VEHICLE )
  1421. DECLARE_TASK( TASK_PASSENGER_ATTACH_TO_VEHICLE )
  1422. DECLARE_TASK( TASK_PASSENGER_DETACH_FROM_VEHICLE )
  1423. DECLARE_TASK( TASK_PASSENGER_SET_IDEAL_ENTRY_YAW )
  1424. // FIXME: Move to companion
  1425. DEFINE_SCHEDULE
  1426. (
  1427. SCHED_PASSENGER_ENTER_VEHICLE,
  1428. " Tasks"
  1429. " TASK_PASSENGER_SET_IDEAL_ENTRY_YAW 0"
  1430. " TASK_FACE_IDEAL 0"
  1431. " TASK_PASSENGER_ATTACH_TO_VEHICLE 0"
  1432. " TASK_PASSENGER_ENTER_VEHICLE 0"
  1433. ""
  1434. " Interrupts"
  1435. " COND_NO_CUSTOM_INTERRUPTS"
  1436. )
  1437. DEFINE_SCHEDULE
  1438. (
  1439. SCHED_PASSENGER_EXIT_VEHICLE,
  1440. " Tasks"
  1441. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_PASSENGER_IDLE"
  1442. " TASK_PASSENGER_DETACH_FROM_VEHICLE 0"
  1443. " TASK_WAIT 0.1" // We must wait one tick for us to start being updated
  1444. " TASK_PASSENGER_EXIT_VEHICLE 0"
  1445. ""
  1446. " Interrupts"
  1447. " COND_NO_CUSTOM_INTERRUPTS"
  1448. " COND_TASK_FAILED"
  1449. )
  1450. DEFINE_SCHEDULE
  1451. (
  1452. SCHED_PASSENGER_IDLE,
  1453. " Tasks"
  1454. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  1455. " TASK_WAIT 2"
  1456. ""
  1457. " Interrupts"
  1458. " COND_PROVOKED"
  1459. " COND_NEW_ENEMY"
  1460. " COND_CAN_RANGE_ATTACK1"
  1461. " COND_CAN_MELEE_ATTACK1"
  1462. " COND_PASSENGER_EXITING"
  1463. " COND_HEAR_DANGER"
  1464. )
  1465. DEFINE_SCHEDULE
  1466. (
  1467. SCHED_PASSENGER_PLAY_SCRIPTED_ANIM,
  1468. " Tasks"
  1469. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_SCRIPT_CUSTOM_MOVE"
  1470. ""
  1471. " Interrupts"
  1472. " COND_PASSENGER_HARD_IMPACT"
  1473. )
  1474. AI_END_CUSTOM_SCHEDULE_PROVIDER()
  1475. }