Team Fortress 2 Source Code as on 22/4/2020
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.

1736 lines
58 KiB

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