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.

2791 lines
84 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "vcollide_parse.h"
  8. #include "vehicle_base.h"
  9. #include "npc_vehicledriver.h"
  10. #include "in_buttons.h"
  11. #include "engine/IEngineSound.h"
  12. #include "soundenvelope.h"
  13. #include "SoundEmitterSystem/isoundemittersystembase.h"
  14. #include "saverestore_utlvector.h"
  15. #include "keyvalues.h"
  16. #include "studio.h"
  17. #include "bone_setup.h"
  18. #include "collisionutils.h"
  19. #include "animation.h"
  20. #include "env_player_surface_trigger.h"
  21. #include "rumble_shared.h"
  22. #ifdef HL2_DLL
  23. #include "hl2_player.h"
  24. #endif
  25. // memdbgon must be the last include file in a .cpp file!!!
  26. #include "tier0/memdbgon.h"
  27. ConVar g_debug_vehiclesound( "g_debug_vehiclesound", "0", FCVAR_CHEAT );
  28. ConVar g_debug_vehicleexit( "g_debug_vehicleexit", "0", FCVAR_CHEAT );
  29. ConVar sv_vehicle_autoaim_scale("sv_vehicle_autoaim_scale", "8");
  30. bool ShouldVehicleIgnoreEntity( CBaseEntity *pVehicle, CBaseEntity *pCollide );
  31. #define HITBOX_SET 2
  32. //-----------------------------------------------------------------------------
  33. // Save/load
  34. //-----------------------------------------------------------------------------
  35. BEGIN_DATADESC_NO_BASE( vehicle_gear_t )
  36. DEFINE_FIELD( flMinSpeed, FIELD_FLOAT ),
  37. DEFINE_FIELD( flMaxSpeed, FIELD_FLOAT ),
  38. DEFINE_FIELD( flSpeedApproachFactor,FIELD_FLOAT ),
  39. END_DATADESC()
  40. BEGIN_DATADESC_NO_BASE( vehicle_crashsound_t )
  41. DEFINE_FIELD( flMinSpeed, FIELD_FLOAT ),
  42. DEFINE_FIELD( flMinDeltaSpeed, FIELD_FLOAT ),
  43. DEFINE_FIELD( iszCrashSound, FIELD_STRING ),
  44. DEFINE_FIELD( gearLimit, FIELD_INTEGER ),
  45. END_DATADESC()
  46. BEGIN_DATADESC_NO_BASE( vehiclesounds_t )
  47. DEFINE_AUTO_ARRAY( iszSound, FIELD_STRING ),
  48. DEFINE_UTLVECTOR( pGears, FIELD_EMBEDDED ),
  49. DEFINE_UTLVECTOR( crashSounds, FIELD_EMBEDDED ),
  50. DEFINE_AUTO_ARRAY( iszStateSounds, FIELD_STRING ),
  51. DEFINE_AUTO_ARRAY( minStateTime, FIELD_FLOAT ),
  52. END_DATADESC()
  53. BEGIN_SIMPLE_DATADESC( CPassengerInfo )
  54. DEFINE_FIELD( m_hPassenger, FIELD_EHANDLE ),
  55. DEFINE_FIELD( m_strRoleName, FIELD_STRING ),
  56. DEFINE_FIELD( m_strSeatName, FIELD_STRING ),
  57. // NOT SAVED
  58. // DEFINE_FIELD( m_nRole, FIELD_INTEGER ),
  59. // DEFINE_FIELD( m_nSeat, FIELD_INTEGER ),
  60. END_DATADESC()
  61. BEGIN_SIMPLE_DATADESC( CBaseServerVehicle )
  62. // These are reset every time by the constructor of the owning class
  63. // DEFINE_FIELD( m_pVehicle, FIELD_CLASSPTR ),
  64. // DEFINE_FIELD( m_pDrivableVehicle; ??? ),
  65. // Controls
  66. DEFINE_FIELD( m_nNPCButtons, FIELD_INTEGER ),
  67. DEFINE_FIELD( m_nPrevNPCButtons, FIELD_INTEGER ),
  68. DEFINE_FIELD( m_flTurnDegrees, FIELD_FLOAT ),
  69. DEFINE_FIELD( m_flVehicleVolume, FIELD_FLOAT ),
  70. // We're going to reparse this data from file in Precache
  71. DEFINE_EMBEDDED( m_vehicleSounds ),
  72. DEFINE_FIELD( m_iSoundGear, FIELD_INTEGER ),
  73. DEFINE_FIELD( m_flSpeedPercentage, FIELD_FLOAT ),
  74. DEFINE_SOUNDPATCH( m_pStateSound ),
  75. DEFINE_SOUNDPATCH( m_pStateSoundFade ),
  76. DEFINE_FIELD( m_soundState, FIELD_INTEGER ),
  77. DEFINE_FIELD( m_soundStateStartTime, FIELD_TIME ),
  78. DEFINE_FIELD( m_lastSpeed, FIELD_FLOAT ),
  79. // NOT SAVED
  80. // DEFINE_FIELD( m_EntryAnimations, CUtlVector ),
  81. // DEFINE_FIELD( m_ExitAnimations, CUtlVector ),
  82. // DEFINE_FIELD( m_bParsedAnimations, FIELD_BOOLEAN ),
  83. // DEFINE_UTLVECTOR( m_PassengerRoles, FIELD_EMBEDDED ),
  84. DEFINE_FIELD( m_iCurrentExitAnim, FIELD_INTEGER ),
  85. DEFINE_FIELD( m_vecCurrentExitEndPoint, FIELD_POSITION_VECTOR ),
  86. DEFINE_FIELD( m_chPreviousTextureType, FIELD_CHARACTER ),
  87. DEFINE_FIELD( m_savedViewOffset, FIELD_VECTOR ),
  88. DEFINE_FIELD( m_hExitBlocker, FIELD_EHANDLE ),
  89. DEFINE_UTLVECTOR( m_PassengerInfo, FIELD_EMBEDDED ),
  90. END_DATADESC()
  91. //-----------------------------------------------------------------------------
  92. // Purpose: Base class for drivable vehicle handling. Contain it in your
  93. // drivable vehicle.
  94. //-----------------------------------------------------------------------------
  95. CBaseServerVehicle::CBaseServerVehicle( void )
  96. {
  97. m_pVehicle = NULL;
  98. m_pDrivableVehicle = NULL;
  99. m_nNPCButtons = 0;
  100. m_nPrevNPCButtons = 0;
  101. m_flTurnDegrees = 0;
  102. m_bParsedAnimations = false;
  103. m_iCurrentExitAnim = 0;
  104. m_vecCurrentExitEndPoint = vec3_origin;
  105. m_flVehicleVolume = 0.5;
  106. m_iSoundGear = 0;
  107. m_pStateSound = NULL;
  108. m_pStateSoundFade = NULL;
  109. m_soundState = SS_NONE;
  110. m_flSpeedPercentage = 0;
  111. m_bUseLegacyExitChecks = false;
  112. m_vehicleSounds.Init();
  113. }
  114. //-----------------------------------------------------------------------------
  115. // Purpose:
  116. //-----------------------------------------------------------------------------
  117. CBaseServerVehicle::~CBaseServerVehicle( void )
  118. {
  119. SoundShutdown(0);
  120. }
  121. //-----------------------------------------------------------------------------
  122. // Purpose:
  123. //-----------------------------------------------------------------------------
  124. void CBaseServerVehicle::Precache( void )
  125. {
  126. int i;
  127. // Precache our other sounds
  128. for ( i = 0; i < VS_NUM_SOUNDS; i++ )
  129. {
  130. if ( m_vehicleSounds.iszSound[i] != NULL_STRING )
  131. {
  132. CBaseEntity::PrecacheScriptSound( STRING(m_vehicleSounds.iszSound[i]) );
  133. }
  134. }
  135. for ( i = 0; i < m_vehicleSounds.crashSounds.Count(); i++ )
  136. {
  137. if ( m_vehicleSounds.crashSounds[i].iszCrashSound != NULL_STRING )
  138. {
  139. CBaseEntity::PrecacheScriptSound( STRING(m_vehicleSounds.crashSounds[i].iszCrashSound) );
  140. }
  141. }
  142. for ( i = 0; i < SS_NUM_STATES; i++ )
  143. {
  144. if ( m_vehicleSounds.iszStateSounds[i] != NULL_STRING )
  145. {
  146. CBaseEntity::PrecacheScriptSound( STRING(m_vehicleSounds.iszStateSounds[i]) );
  147. }
  148. }
  149. }
  150. //-----------------------------------------------------------------------------
  151. // Purpose: Parses the vehicle's script for the vehicle sounds
  152. //-----------------------------------------------------------------------------
  153. bool CBaseServerVehicle::Initialize( const char *pScriptName )
  154. {
  155. // Attempt to parse our vehicle script
  156. if ( PhysFindOrAddVehicleScript( pScriptName, NULL, &m_vehicleSounds ) == false )
  157. return false;
  158. Precache();
  159. return true;
  160. }
  161. //-----------------------------------------------------------------------------
  162. // Purpose:
  163. //-----------------------------------------------------------------------------
  164. void CBaseServerVehicle::SetVehicle( CBaseEntity *pVehicle )
  165. {
  166. m_pVehicle = pVehicle;
  167. m_pDrivableVehicle = dynamic_cast<IDrivableVehicle*>(m_pVehicle);
  168. Assert( m_pDrivableVehicle );
  169. }
  170. //-----------------------------------------------------------------------------
  171. // Purpose:
  172. //-----------------------------------------------------------------------------
  173. IDrivableVehicle *CBaseServerVehicle::GetDrivableVehicle( void )
  174. {
  175. Assert( m_pDrivableVehicle );
  176. return m_pDrivableVehicle;
  177. }
  178. //-----------------------------------------------------------------------------
  179. // Purpose: Returns the driver. Unlike GetPassenger(VEHICLE_ROLE_DRIVER), it will return
  180. // the NPC driver if it has one.
  181. //-----------------------------------------------------------------------------
  182. CBaseEntity *CBaseServerVehicle::GetDriver( void )
  183. {
  184. return GetPassenger( VEHICLE_ROLE_DRIVER );
  185. }
  186. //-----------------------------------------------------------------------------
  187. // Purpose:
  188. //-----------------------------------------------------------------------------
  189. CBaseCombatCharacter *CBaseServerVehicle::GetPassenger( int nRole )
  190. {
  191. Assert( nRole == VEHICLE_ROLE_DRIVER );
  192. CBaseEntity *pDriver = GetDrivableVehicle()->GetDriver();
  193. if ( pDriver == NULL )
  194. return NULL;
  195. return pDriver->MyCombatCharacterPointer();
  196. }
  197. //-----------------------------------------------------------------------------
  198. // Purpose:
  199. //-----------------------------------------------------------------------------
  200. int CBaseServerVehicle::GetPassengerRole( CBaseCombatCharacter *pPassenger )
  201. {
  202. if ( pPassenger == GetDrivableVehicle()->GetDriver() )
  203. return VEHICLE_ROLE_DRIVER;
  204. return VEHICLE_ROLE_NONE;
  205. }
  206. //-----------------------------------------------------------------------------
  207. // Purpose: Adds a passenger to the vehicle
  208. // Input : nSeat - seat to sit in
  209. // *pPassenger - character to enter
  210. //-----------------------------------------------------------------------------
  211. bool CBaseServerVehicle::NPC_AddPassenger( CBaseCombatCharacter *pPassenger, string_t strRoleName, int nSeat )
  212. {
  213. // Players cannot yet use this code! - jdw
  214. Assert( pPassenger != NULL && pPassenger->IsPlayer() == false );
  215. if ( pPassenger == NULL || pPassenger->IsPlayer() )
  216. return false;
  217. // Find our role
  218. int nRole = FindRoleIndexByName( strRoleName );
  219. if ( nRole == -1 )
  220. return false;
  221. // Cannot evict a passenger already in this position
  222. CBaseCombatCharacter *pCurrentPassenger = NPC_GetPassengerInSeat( nRole, nSeat );
  223. if ( pCurrentPassenger == pPassenger )
  224. return true;
  225. // If we weren't the same passenger, we need to be empty
  226. if ( pCurrentPassenger != NULL )
  227. return false;
  228. // Find the seat
  229. for ( int i = 0; i < m_PassengerInfo.Count(); i++ )
  230. {
  231. if ( m_PassengerInfo[i].GetSeat() == nSeat && m_PassengerInfo[i].GetRole() == nRole )
  232. {
  233. m_PassengerInfo[i].m_hPassenger = pPassenger;
  234. return true;
  235. }
  236. }
  237. return false;
  238. }
  239. //-----------------------------------------------------------------------------
  240. // Purpose: Removes a passenger from the vehicle
  241. // Input : *pPassenger - Passenger to remove
  242. // Output : Returns true on success, false on failure.
  243. //-----------------------------------------------------------------------------
  244. bool CBaseServerVehicle::NPC_RemovePassenger( CBaseCombatCharacter *pPassenger )
  245. {
  246. // Players cannot yet use this code! - jdw
  247. Assert( pPassenger != NULL && pPassenger->IsPlayer() == false );
  248. if ( pPassenger == NULL || pPassenger->IsPlayer() )
  249. return false;
  250. // Find the seat
  251. for ( int i = 0; i < m_PassengerInfo.Count(); i++ )
  252. {
  253. if ( m_PassengerInfo[i].m_hPassenger == pPassenger )
  254. {
  255. m_PassengerInfo[i].m_hPassenger = NULL;
  256. return true;
  257. }
  258. }
  259. return false;
  260. }
  261. //-----------------------------------------------------------------------------
  262. // Purpose: Returns the attachment point index for the passenger's seat
  263. // Input : *pPassenger - Passenger in the seat
  264. // Output : int - Attachment point index for the vehicle
  265. //-----------------------------------------------------------------------------
  266. int CBaseServerVehicle::NPC_GetPassengerSeatAttachment( CBaseCombatCharacter *pPassenger )
  267. {
  268. // Get the role and seat the the supplied passenger
  269. for ( int i = 0; i < m_PassengerInfo.Count(); i++ )
  270. {
  271. // If this is the passenger, get the attachment it'll be at
  272. if ( m_PassengerInfo[i].m_hPassenger == pPassenger )
  273. {
  274. // The seat is the attachment point
  275. int nSeat = m_PassengerInfo[i].GetSeat();
  276. int nRole = m_PassengerInfo[i].GetRole();
  277. return m_PassengerRoles[nRole].m_PassengerSeats[nSeat].GetAttachmentID();
  278. }
  279. }
  280. return -1;
  281. }
  282. //-----------------------------------------------------------------------------
  283. // Purpose: Get the worldspace position and angles of the specified seat
  284. // Input : *pPassenger - Passenger's seat to use
  285. //-----------------------------------------------------------------------------
  286. bool CBaseServerVehicle::NPC_GetPassengerSeatPosition( CBaseCombatCharacter *pPassenger, Vector *vecResultPos, QAngle *vecResultAngles )
  287. {
  288. // Get our attachment point
  289. int nSeatAttachment = NPC_GetPassengerSeatAttachment( pPassenger );
  290. if ( nSeatAttachment == -1 )
  291. return false;
  292. // Figure out which entrypoint hitbox the player is in
  293. CBaseAnimating *pAnimating = dynamic_cast< CBaseAnimating * >( m_pVehicle );
  294. if ( pAnimating == NULL )
  295. return false;
  296. Vector vecPos;
  297. QAngle vecAngles;
  298. pAnimating->GetAttachment( nSeatAttachment, vecPos, vecAngles );
  299. if ( vecResultPos != NULL )
  300. {
  301. *vecResultPos = vecPos;
  302. }
  303. if ( vecResultAngles != NULL )
  304. {
  305. *vecResultAngles = vecAngles;
  306. }
  307. return true;
  308. }
  309. //-----------------------------------------------------------------------------
  310. // Purpose: Get the localspace position and angles of the specified seat
  311. // Input : *pPassenger - Passenger's seat to use
  312. //-----------------------------------------------------------------------------
  313. bool CBaseServerVehicle::NPC_GetPassengerSeatPositionLocal( CBaseCombatCharacter *pPassenger, Vector *vecResultPos, QAngle *vecResultAngles )
  314. {
  315. // Get our attachment point
  316. int nSeatAttachment = NPC_GetPassengerSeatAttachment( pPassenger );
  317. if ( nSeatAttachment == -1 )
  318. return false;
  319. // Figure out which entrypoint hitbox the player is in
  320. CBaseAnimating *pAnimating = m_pVehicle->GetBaseAnimating();
  321. if ( pAnimating == NULL )
  322. return false;
  323. Vector vecPos;
  324. QAngle vecAngles;
  325. pAnimating->InvalidateBoneCache(); // NOTE: We're moving with velocity, so we're almost always out of date
  326. pAnimating->GetAttachmentLocal( nSeatAttachment, vecPos, vecAngles );
  327. if ( vecResultPos != NULL )
  328. {
  329. *vecResultPos = vecPos;
  330. }
  331. if ( vecResultAngles != NULL )
  332. {
  333. *vecResultAngles = vecAngles;
  334. }
  335. return true;
  336. }
  337. //-----------------------------------------------------------------------------
  338. // Purpose: Retrieves a list of animations used to enter/exit the seat occupied by the passenger
  339. // Input : *pPassenger - Passenger who's seat anims to retrieve
  340. // nType - which set of animations to retrieve
  341. //-----------------------------------------------------------------------------
  342. const PassengerSeatAnims_t *CBaseServerVehicle::NPC_GetPassengerSeatAnims( CBaseCombatCharacter *pPassenger, PassengerSeatAnimType_t nType )
  343. {
  344. // Get the role and seat the the supplied passenger
  345. for ( int i = 0; i < m_PassengerInfo.Count(); i++ )
  346. {
  347. if ( m_PassengerInfo[i].m_hPassenger == pPassenger )
  348. {
  349. int nSeat = m_PassengerInfo[i].GetSeat();
  350. int nRole = m_PassengerInfo[i].GetRole();
  351. switch( nType )
  352. {
  353. case PASSENGER_SEAT_ENTRY:
  354. return &m_PassengerRoles[nRole].m_PassengerSeats[nSeat].m_EntryTransitions;
  355. break;
  356. case PASSENGER_SEAT_EXIT:
  357. return &m_PassengerRoles[nRole].m_PassengerSeats[nSeat].m_ExitTransitions;
  358. break;
  359. default:
  360. return NULL;
  361. break;
  362. }
  363. }
  364. }
  365. return NULL;
  366. }
  367. void CBaseServerVehicle::SetPassengerWeapon( bool bUseWeapon, CBaseCombatCharacter *pPassenger )
  368. {
  369. CBasePlayer *pPlayer = ToBasePlayer( pPassenger );
  370. if ( pPlayer == NULL )
  371. {
  372. return;
  373. }
  374. if ( pPassenger != NULL && pPassenger->IsPlayer() == false )
  375. {
  376. Assert( 0 );
  377. return;
  378. }
  379. CBaseCombatWeapon *pWeapon = pPlayer->GetActiveWeapon();
  380. if ( pWeapon == NULL )
  381. {
  382. Assert( 0 );
  383. return;
  384. }
  385. if ( bUseWeapon )
  386. {
  387. pPlayer->ShowCrosshair( true );
  388. pWeapon->Deploy();
  389. #if defined ( PORTAL2 )
  390. int iSeq = pWeapon->LookupSequence( "end_draw" );
  391. Assert( iSeq >= 0 );
  392. pWeapon->SendViewModelAnim( iSeq );
  393. #endif
  394. }
  395. else
  396. {
  397. pWeapon->Holster( NULL );
  398. pPlayer->ShowCrosshair( false );
  399. }
  400. }
  401. //-----------------------------------------------------------------------------
  402. // Purpose: Get and set the current driver. Use PassengerRole_t enum in shareddefs.h for adding passengers
  403. //-----------------------------------------------------------------------------
  404. void CBaseServerVehicle::SetPassenger( int nRole, CBaseCombatCharacter *pPassenger )
  405. {
  406. // Baseclass only handles vehicles with a single passenger
  407. Assert( nRole == VEHICLE_ROLE_DRIVER );
  408. if ( pPassenger != NULL && pPassenger->IsPlayer() == false )
  409. {
  410. // Use NPC_AddPassenger() for NPCs at the moment, these will all be collapsed into one system -- jdw
  411. Assert( 0 );
  412. return;
  413. }
  414. // Getting in? or out?
  415. if ( pPassenger != NULL )
  416. {
  417. CBasePlayer *pPlayer = ToBasePlayer( pPassenger );
  418. if ( pPlayer != NULL )
  419. {
  420. m_savedViewOffset = pPlayer->GetViewOffset();
  421. pPlayer->SetViewOffset( vec3_origin );
  422. if ( IsPassengerUsingStandardWeapons( nRole ) == false )
  423. {
  424. pPlayer->ShowCrosshair( false );
  425. }
  426. GetDrivableVehicle()->EnterVehicle( pPassenger );
  427. #ifdef HL2_DLL
  428. // Stop the player sprint and flashlight.
  429. CHL2_Player *pHL2Player = dynamic_cast<CHL2_Player*>( pPlayer );
  430. if ( pHL2Player )
  431. {
  432. if ( pHL2Player->IsSprinting() )
  433. {
  434. pHL2Player->StopSprinting();
  435. }
  436. if ( pHL2Player->FlashlightIsOn() )
  437. {
  438. pHL2Player->FlashlightTurnOff();
  439. }
  440. }
  441. #endif
  442. }
  443. }
  444. else
  445. {
  446. CBasePlayer *pPlayer = ToBasePlayer( GetDriver() );
  447. if ( pPlayer )
  448. {
  449. // Restore the exiting player's view offset
  450. pPlayer->SetViewOffset( m_savedViewOffset );
  451. if ( IsPassengerUsingStandardWeapons( nRole ) == false )
  452. {
  453. pPlayer->ShowCrosshair( true );
  454. }
  455. }
  456. GetDrivableVehicle()->ExitVehicle( nRole );
  457. GetDrivableVehicle()->SetVehicleEntryAnim( false );
  458. UTIL_Remove( m_hExitBlocker );
  459. }
  460. }
  461. //-----------------------------------------------------------------------------
  462. // Purpose: Get a position in *world space* inside the vehicle for the player to start at
  463. //-----------------------------------------------------------------------------
  464. void CBaseServerVehicle::GetPassengerSeatPoint( int nRole, Vector *pPoint, QAngle *pAngles )
  465. {
  466. Assert( nRole == VEHICLE_ROLE_DRIVER );
  467. CBaseAnimating *pAnimating = dynamic_cast<CBaseAnimating *>(m_pVehicle);
  468. if ( pAnimating )
  469. {
  470. char pAttachmentName[32];
  471. Q_snprintf( pAttachmentName, sizeof( pAttachmentName ), "vehicle_feet_passenger%d", nRole );
  472. int nFeetAttachmentIndex = pAnimating->LookupAttachment(pAttachmentName);
  473. int nIdleSequence = pAnimating->SelectWeightedSequence( ACT_IDLE );
  474. if ( nFeetAttachmentIndex > 0 && nIdleSequence != -1 )
  475. {
  476. // FIXME: This really wants to be a faster query than this implementation!
  477. Vector vecOrigin;
  478. QAngle vecAngles;
  479. if ( GetLocalAttachmentAtTime( nIdleSequence, nFeetAttachmentIndex, 0.0f, &vecOrigin, &vecAngles ) )
  480. {
  481. UTIL_ParentToWorldSpace( pAnimating, vecOrigin, vecAngles );
  482. if ( pPoint )
  483. {
  484. *pPoint = vecOrigin;
  485. }
  486. if ( pAngles )
  487. {
  488. *pAngles = vecAngles;
  489. }
  490. return;
  491. }
  492. }
  493. }
  494. // Couldn't find the attachment point, so just use the origin
  495. if ( pPoint )
  496. {
  497. *pPoint = m_pVehicle->GetAbsOrigin();
  498. }
  499. if ( pAngles )
  500. {
  501. *pAngles = m_pVehicle->GetAbsAngles();
  502. }
  503. }
  504. //---------------------------------------------------------------------------------
  505. // Check Exit Point for leaving vehicle.
  506. //
  507. // Input: yaw/roll from vehicle angle to check for exit
  508. // distance from origin to drop player (allows for different shaped vehicles
  509. // Output: returns true if valid location, pEndPoint
  510. // updated with actual exit point
  511. //---------------------------------------------------------------------------------
  512. bool CBaseServerVehicle::CheckExitPoint( float yaw, int distance, Vector *pEndPoint )
  513. {
  514. QAngle vehicleAngles = m_pVehicle->GetLocalAngles();
  515. Vector vecStart = m_pVehicle->GetAbsOrigin();
  516. Vector vecDir;
  517. vecStart.z += 12; // always 12" from ground
  518. vehicleAngles[YAW] += yaw;
  519. AngleVectors( vehicleAngles, NULL, &vecDir, NULL );
  520. // Vehicles are oriented along the Y axis
  521. vecDir *= -1;
  522. *pEndPoint = vecStart + vecDir * distance;
  523. trace_t tr;
  524. UTIL_TraceHull( vecStart, *pEndPoint, VEC_HULL_MIN, VEC_HULL_MAX, MASK_PLAYERSOLID, m_pVehicle, COLLISION_GROUP_NONE, &tr );
  525. if ( tr.fraction < 1.0 )
  526. return false;
  527. return true;
  528. }
  529. //-----------------------------------------------------------------------------
  530. // Purpose: Where does this passenger exit the vehicle?
  531. //-----------------------------------------------------------------------------
  532. bool CBaseServerVehicle::GetPassengerExitPoint( int nRole, Vector *pExitPoint, QAngle *pAngles )
  533. {
  534. Assert( nRole == VEHICLE_ROLE_DRIVER );
  535. // First, see if we've got an attachment point
  536. CBaseAnimating *pAnimating = dynamic_cast<CBaseAnimating *>(m_pVehicle);
  537. if ( pAnimating )
  538. {
  539. Vector vehicleExitOrigin;
  540. QAngle vehicleExitAngles;
  541. if ( pAnimating->GetAttachment( "vehicle_driver_exit", vehicleExitOrigin, vehicleExitAngles ) )
  542. {
  543. // Make sure it's clear
  544. trace_t tr;
  545. UTIL_TraceHull( vehicleExitOrigin + Vector(0, 0, 12), vehicleExitOrigin, VEC_HULL_MIN, VEC_HULL_MAX, MASK_PLAYERSOLID, m_pVehicle, COLLISION_GROUP_NONE, &tr );
  546. if ( !tr.startsolid )
  547. {
  548. *pAngles = vehicleExitAngles;
  549. *pExitPoint = tr.endpos;
  550. return true;
  551. }
  552. }
  553. }
  554. // left side
  555. if( CheckExitPoint( 90, 90, pExitPoint ) ) // angle from car, distance from origin, actual exit point
  556. return true;
  557. // right side
  558. if( CheckExitPoint( -90, 90, pExitPoint ) )
  559. return true;
  560. // front
  561. if( CheckExitPoint( 0, 100, pExitPoint ) )
  562. return true;
  563. // back
  564. if( CheckExitPoint( 180, 170, pExitPoint ) )
  565. return true;
  566. // All else failed, try popping them out the top.
  567. Vector vecWorldMins, vecWorldMaxs;
  568. m_pVehicle->CollisionProp()->WorldSpaceAABB( &vecWorldMins, &vecWorldMaxs );
  569. pExitPoint->x = (vecWorldMins.x + vecWorldMaxs.x) * 0.5f;
  570. pExitPoint->y = (vecWorldMins.y + vecWorldMaxs.y) * 0.5f;
  571. pExitPoint->z = vecWorldMaxs.z + 50.0f;
  572. // Make sure it's clear
  573. trace_t tr;
  574. UTIL_TraceHull( m_pVehicle->CollisionProp()->WorldSpaceCenter(), *pExitPoint, VEC_HULL_MIN, VEC_HULL_MAX, MASK_PLAYERSOLID, m_pVehicle, COLLISION_GROUP_NONE, &tr );
  575. if ( !tr.startsolid )
  576. {
  577. return true;
  578. }
  579. // No clear exit point available!
  580. return false;
  581. }
  582. //-----------------------------------------------------------------------------
  583. // Purpose:
  584. //-----------------------------------------------------------------------------
  585. void CBaseServerVehicle::ParseExitAnim( KeyValues *pkvExitList, bool bEscapeExit )
  586. {
  587. // Look through the entry animations list
  588. KeyValues *pkvExitAnim = pkvExitList->GetFirstSubKey();
  589. while ( pkvExitAnim )
  590. {
  591. // Add 'em to our list
  592. int iIndex = m_ExitAnimations.AddToTail();
  593. Q_strncpy( m_ExitAnimations[iIndex].szAnimName, pkvExitAnim->GetName(), sizeof(m_ExitAnimations[iIndex].szAnimName) );
  594. m_ExitAnimations[iIndex].bEscapeExit = bEscapeExit;
  595. if ( StringHasPrefixCaseSensitive( pkvExitAnim->GetString(), "upsidedown" ) )
  596. {
  597. m_ExitAnimations[iIndex].bUpright = false;
  598. }
  599. else
  600. {
  601. m_ExitAnimations[iIndex].bUpright = true;
  602. }
  603. pkvExitAnim = pkvExitAnim->GetNextKey();
  604. }
  605. }
  606. //-----------------------------------------------------------------------------
  607. // Purpose: Parse the transition information
  608. // Input : *pTransitionKeyValues - key values to parse
  609. //-----------------------------------------------------------------------------
  610. void CBaseServerVehicle::ParseNPCSeatTransition( KeyValues *pTransitionKeyValues, CPassengerSeatTransition *pTransition )
  611. {
  612. // Store it
  613. const char *lpszAnimName = pTransitionKeyValues->GetString( "animation" );
  614. pTransition->m_strAnimationName = AllocPooledString( lpszAnimName );
  615. pTransition->m_nPriority = pTransitionKeyValues->GetInt( "priority" );
  616. }
  617. //-----------------------------------------------------------------------------
  618. // Purpose: Sorting function for vehicle seat animation priorities
  619. //-----------------------------------------------------------------------------
  620. typedef CPassengerSeatTransition SortSeatPriorityType;
  621. int __cdecl SeatPrioritySort( const SortSeatPriorityType *s1, const SortSeatPriorityType *s2 )
  622. {
  623. return ( s1->GetPriority() > s2->GetPriority() );
  624. }
  625. //-----------------------------------------------------------------------------
  626. // Purpose: Parse one set of entry/exit data
  627. // Input : *pSetKeyValues - Key values for this set
  628. //-----------------------------------------------------------------------------
  629. void CBaseServerVehicle::ParseNPCPassengerSeat( KeyValues *pSetKeyValues, CPassengerSeat *pSeat )
  630. {
  631. CBaseAnimating *pAnimating = (CBaseAnimating *) m_pVehicle;
  632. // Get our attachment name
  633. const char *lpszAttachmentName = pSetKeyValues->GetString( "target_attachment" );
  634. int nAttachmentID = pAnimating->LookupAttachment( lpszAttachmentName );
  635. pSeat->m_nAttachmentID = nAttachmentID;
  636. pSeat->m_strSeatName = AllocPooledString( lpszAttachmentName );
  637. KeyValues *pKey = pSetKeyValues->GetFirstSubKey();
  638. while ( pKey != NULL )
  639. {
  640. const char *lpszName = pKey->GetName();
  641. if ( Q_stricmp( lpszName, "entry" ) == 0 )
  642. {
  643. int nIndex = pSeat->m_EntryTransitions.AddToTail();
  644. Assert( pSeat->m_EntryTransitions.IsValidIndex( nIndex ) );
  645. ParseNPCSeatTransition( pKey, &pSeat->m_EntryTransitions[nIndex] );
  646. }
  647. else if ( Q_stricmp( lpszName, "exit" ) == 0 )
  648. {
  649. int nIndex = pSeat->m_ExitTransitions.AddToTail();
  650. Assert( pSeat->m_ExitTransitions.IsValidIndex( nIndex ) );
  651. ParseNPCSeatTransition( pKey, &pSeat->m_ExitTransitions[nIndex] );
  652. }
  653. // Advance
  654. pKey = pKey->GetNextKey();
  655. }
  656. // Sort the seats based on their priority
  657. pSeat->m_EntryTransitions.Sort( SeatPrioritySort );
  658. pSeat->m_ExitTransitions.Sort( SeatPrioritySort );
  659. }
  660. //-----------------------------------------------------------------------------
  661. // Purpose: Find a passenger role (by name), or create a new one of that names
  662. // Input : strName - name of the role
  663. // : *nIndex - the index into the CUtlBuffer where this role resides
  664. // Output : CPassengerRole * - Role found or created
  665. //-----------------------------------------------------------------------------
  666. CPassengerRole *CBaseServerVehicle::FindOrCreatePassengerRole( string_t strName, int *nIndex )
  667. {
  668. // Try to find an already created container of the same name
  669. for ( int i = 0; i < m_PassengerRoles.Count(); i++ )
  670. {
  671. // If we match, return it
  672. if ( FStrEq( STRING( m_PassengerRoles[i].m_strName ), STRING( strName ) ) )
  673. {
  674. // Supply the index, if requested
  675. if ( nIndex != NULL )
  676. {
  677. *nIndex = i;
  678. }
  679. return &m_PassengerRoles[i];
  680. }
  681. }
  682. // Create a new container
  683. int nNewIndex = m_PassengerRoles.AddToTail();
  684. Assert( m_PassengerRoles.IsValidIndex( nNewIndex ) );
  685. m_PassengerRoles[nNewIndex].m_strName = strName;
  686. // Supply the index, if requested
  687. if ( nIndex != NULL )
  688. {
  689. *nIndex = nNewIndex;
  690. }
  691. return &m_PassengerRoles[nNewIndex];
  692. }
  693. ConVar g_debug_npc_vehicle_roles( "g_debug_npc_vehicle_roles", "0" );
  694. //-----------------------------------------------------------------------------
  695. // Purpose: Parse NPC entry and exit anim data
  696. // Input : *pModelKeyValues - Key values from the vehicle model
  697. //-----------------------------------------------------------------------------
  698. void CBaseServerVehicle::ParseNPCRoles( KeyValues *pkvPassengerList )
  699. {
  700. // Get the definition section
  701. if ( pkvPassengerList == NULL )
  702. return;
  703. // Get our animating class
  704. CBaseAnimating *pAnimating = dynamic_cast<CBaseAnimating *>(m_pVehicle);
  705. Assert( pAnimating != NULL );
  706. if ( pAnimating == NULL )
  707. return;
  708. // For attachment polling
  709. CStudioHdr *pStudioHdr = pAnimating->GetModelPtr();
  710. Assert( pStudioHdr != NULL );
  711. if ( pStudioHdr == NULL )
  712. return;
  713. // Parse all subkeys
  714. int nRoleIndex;
  715. KeyValues *pkvPassengerKey = pkvPassengerList->GetFirstSubKey();
  716. while ( pkvPassengerKey != NULL )
  717. {
  718. string_t strRoleName = AllocPooledString( pkvPassengerKey->GetName() );
  719. // Find or create the container
  720. CPassengerRole *pRole = FindOrCreatePassengerRole( strRoleName, &nRoleIndex );
  721. if ( pRole == NULL )
  722. continue;
  723. // Add a new role
  724. int nSeatIndex = pRole->m_PassengerSeats.AddToTail();
  725. Assert( pRole->m_PassengerSeats.IsValidIndex( nSeatIndex ) );
  726. // Parse the information
  727. ParseNPCPassengerSeat( pkvPassengerKey, &pRole->m_PassengerSeats[nSeatIndex] );
  728. // Add a matching entry into our passenger manifest
  729. int nPassengerIndex = m_PassengerInfo.AddToTail();
  730. m_PassengerInfo[nPassengerIndex].m_hPassenger = NULL;
  731. m_PassengerInfo[nPassengerIndex].m_nSeat = nSeatIndex;
  732. m_PassengerInfo[nPassengerIndex].m_nRole = nRoleIndex;
  733. // The following are used for index fix-ups after save game restoration
  734. m_PassengerInfo[nPassengerIndex].m_strRoleName = strRoleName;
  735. m_PassengerInfo[nPassengerIndex].m_strSeatName = pRole->m_PassengerSeats[nSeatIndex].m_strSeatName;
  736. // Advance to the next key
  737. pkvPassengerKey = pkvPassengerKey->GetNextKey();
  738. }
  739. // ======================================================================================================
  740. // Debug print
  741. if ( g_debug_npc_vehicle_roles.GetBool() )
  742. {
  743. Msg("Passenger Roles Parsed:\t%d\n\n", m_PassengerRoles.Count() );
  744. for ( int i = 0; i < m_PassengerRoles.Count(); i++ )
  745. {
  746. Msg("\tPassenger Role:\t%s (%d seats)\n", STRING(m_PassengerRoles[i].m_strName), m_PassengerRoles[i].m_PassengerSeats.Count() );
  747. // Iterate through all information sets under this name
  748. for ( int j = 0; j < m_PassengerRoles[i].m_PassengerSeats.Count(); j++ )
  749. {
  750. Msg("\t\tAttachment: %d\n", m_PassengerRoles[i].m_PassengerSeats[j].m_nAttachmentID );
  751. // Entries
  752. Msg("\t\tEntries:\t%d\n", m_PassengerRoles[i].m_PassengerSeats[j].m_EntryTransitions.Count() );
  753. Msg("\t\t=====================\n" );
  754. for ( int nEntry = 0; nEntry < m_PassengerRoles[i].m_PassengerSeats[j].m_EntryTransitions.Count(); nEntry++ )
  755. {
  756. Msg("\t\t\tAnimation:\t%s\t(Priority %d)\n", STRING(m_PassengerRoles[i].m_PassengerSeats[j].m_EntryTransitions[nEntry].m_strAnimationName),
  757. m_PassengerRoles[i].m_PassengerSeats[j].m_EntryTransitions[nEntry].m_nPriority );
  758. }
  759. Msg("\n");
  760. // Exits
  761. Msg("\t\tExits:\t%d\n", m_PassengerRoles[i].m_PassengerSeats[j].m_ExitTransitions.Count() );
  762. Msg("\t\t=====================\n" );
  763. for ( int nExits = 0; nExits < m_PassengerRoles[i].m_PassengerSeats[j].m_ExitTransitions.Count(); nExits++ )
  764. {
  765. Msg("\t\t\tAnimation:\t%s\t(Priority %d)\n", STRING(m_PassengerRoles[i].m_PassengerSeats[j].m_ExitTransitions[nExits].m_strAnimationName),
  766. m_PassengerRoles[i].m_PassengerSeats[j].m_ExitTransitions[nExits].m_nPriority );
  767. }
  768. }
  769. Msg("\n");
  770. }
  771. }
  772. // ======================================================================================================
  773. }
  774. //-----------------------------------------------------------------------------
  775. // Purpose: Get an attachment point at a specified time in its cycle (note: not exactly a speedy query, use sparingly!)
  776. // Input : nSequence - sequence to test
  777. // nAttachmentIndex - attachment to test
  778. // flCyclePoint - 0.0 - 1.0
  779. // Output : Returns true on success, false on failure.
  780. //-----------------------------------------------------------------------------
  781. bool CBaseServerVehicle::GetLocalAttachmentAtTime( int nQuerySequence, int nAttachmentIndex, float flCyclePoint, Vector *vecOriginOut, QAngle *vecAnglesOut )
  782. {
  783. CBaseAnimating *pAnimating = m_pVehicle->GetBaseAnimating();
  784. if ( pAnimating == NULL )
  785. return false;
  786. // TODO: It's annoying to stomp and restore this off each time when we're just going to stomp it again later, but the function
  787. // should really leave the car in an acceptable state to run this query -- jdw
  788. // Store this off for restoration later
  789. int nOldSequence = pAnimating->GetSequence();
  790. float flOldCycle = pAnimating->GetCycle();
  791. // Setup the model for the query
  792. pAnimating->SetSequence( nQuerySequence );
  793. pAnimating->SetCycle( flCyclePoint );
  794. pAnimating->InvalidateBoneCache();
  795. // Query for the point
  796. Vector vecOrigin;
  797. QAngle vecAngles;
  798. pAnimating->GetAttachmentLocal( nAttachmentIndex, vecOrigin, vecAngles );
  799. if ( vecOriginOut != NULL )
  800. {
  801. *vecOriginOut = vecOrigin;
  802. }
  803. if ( vecAnglesOut != NULL )
  804. {
  805. *vecAnglesOut = vecAngles;
  806. }
  807. // Restore the model after the query
  808. pAnimating->SetSequence( nOldSequence );
  809. pAnimating->SetCycle( flOldCycle );
  810. pAnimating->InvalidateBoneCache();
  811. return true;
  812. }
  813. //-----------------------------------------------------------------------------
  814. // Purpose: Get an attachment point at a specified time in its cycle (note: not exactly a speedy query, use sparingly!)
  815. // Input : lpszAnimName - name of the sequence to test
  816. // nAttachmentIndex - attachment to test
  817. // flCyclePoint - 0.0 - 1.0
  818. // Output : Returns true on success, false on failure.
  819. //-----------------------------------------------------------------------------
  820. bool CBaseServerVehicle::GetLocalAttachmentAtTime( const char *lpszAnimName, int nAttachmentIndex, float flCyclePoint, Vector *vecOriginOut, QAngle *vecAnglesOut )
  821. {
  822. CBaseAnimating *pAnimating = m_pVehicle->GetBaseAnimating();
  823. if ( pAnimating == NULL )
  824. return false;
  825. int nQuerySequence = pAnimating->LookupSequence( lpszAnimName );
  826. if ( nQuerySequence < 0 )
  827. return false;
  828. return GetLocalAttachmentAtTime( nQuerySequence, nAttachmentIndex, flCyclePoint, vecOriginOut, vecAnglesOut );
  829. }
  830. //-----------------------------------------------------------------------------
  831. // Purpose:
  832. //-----------------------------------------------------------------------------
  833. void CBaseServerVehicle::CacheEntryExitPoints( void )
  834. {
  835. CBaseAnimating *pAnimating = m_pVehicle->GetBaseAnimating();
  836. if ( pAnimating == NULL )
  837. return;
  838. int nAttachment = pAnimating->LookupAttachment( "vehicle_driver_eyes" );
  839. // For each exit animation, determine where the end point is and cache it
  840. for ( int i = 0; i < m_ExitAnimations.Count(); i++ )
  841. {
  842. if ( GetLocalAttachmentAtTime( m_ExitAnimations[i].szAnimName, nAttachment, 1.0f, &m_ExitAnimations[i].vecExitPointLocal, &m_ExitAnimations[i].vecExitAnglesLocal ) == false )
  843. {
  844. Warning("Exit animation %s failed to cache target points properly!\n", m_ExitAnimations[i].szAnimName );
  845. }
  846. if ( g_debug_vehicleexit.GetBool() )
  847. {
  848. Vector vecExitPoint = m_ExitAnimations[i].vecExitPointLocal;
  849. QAngle vecExitAngles = m_ExitAnimations[i].vecExitAnglesLocal;
  850. UTIL_ParentToWorldSpace( pAnimating, vecExitPoint, vecExitAngles );
  851. NDebugOverlay::Box( vecExitPoint, -Vector(8,8,8), Vector(8,8,8), 0, 255, 0, 0, 20.0f );
  852. NDebugOverlay::Axis( vecExitPoint, vecExitAngles, 8.0f, true, 20.0f );
  853. }
  854. }
  855. }
  856. //-----------------------------------------------------------------------------
  857. // Purpose:
  858. //-----------------------------------------------------------------------------
  859. void CBaseServerVehicle::ParseEntryExitAnims( void )
  860. {
  861. // Try and find the right animation to play in the model's keyvalues
  862. KeyValues *modelKeyValues = new KeyValues("");
  863. if ( modelKeyValues->LoadFromBuffer( modelinfo->GetModelName( m_pVehicle->GetModel() ), modelinfo->GetModelKeyValueText( m_pVehicle->GetModel() ) ) )
  864. {
  865. // Do we have an entry section?
  866. KeyValues *pkvEntryList = modelKeyValues->FindKey("vehicle_entry");
  867. if ( pkvEntryList )
  868. {
  869. // Look through the entry animations list
  870. KeyValues *pkvEntryAnim = pkvEntryList->GetFirstSubKey();
  871. while ( pkvEntryAnim )
  872. {
  873. // Add 'em to our list
  874. int iIndex = m_EntryAnimations.AddToTail();
  875. Q_strncpy( m_EntryAnimations[iIndex].szAnimName, pkvEntryAnim->GetName(), sizeof(m_EntryAnimations[iIndex].szAnimName) );
  876. m_EntryAnimations[iIndex].iHitboxGroup = pkvEntryAnim->GetInt();
  877. pkvEntryAnim = pkvEntryAnim->GetNextKey();
  878. }
  879. }
  880. // Do we have an exit section?
  881. KeyValues *pkvExitList = modelKeyValues->FindKey("vehicle_exit");
  882. if ( pkvExitList )
  883. {
  884. ParseExitAnim( pkvExitList, false );
  885. }
  886. // Do we have an exit section?
  887. pkvExitList = modelKeyValues->FindKey("vehicle_escape_exit");
  888. if ( pkvExitList )
  889. {
  890. ParseExitAnim( pkvExitList, true );
  891. }
  892. // Parse the NPC vehicle roles as well
  893. KeyValues *pkvPassengerList = modelKeyValues->FindKey( "vehicle_npc_passengers" );
  894. if ( pkvPassengerList )
  895. {
  896. ParseNPCRoles( pkvPassengerList );
  897. }
  898. }
  899. modelKeyValues->deleteThis();
  900. // Determine the entry and exit points for the
  901. CacheEntryExitPoints();
  902. }
  903. //-----------------------------------------------------------------------------
  904. // Purpose:
  905. //-----------------------------------------------------------------------------
  906. void CBaseServerVehicle::HandlePassengerEntry( CBaseCombatCharacter *pPassenger, bool bAllowEntryOutsideZone )
  907. {
  908. CBasePlayer *pPlayer = ToBasePlayer( pPassenger );
  909. if ( pPlayer != NULL )
  910. {
  911. // Find out which hitbox the player's eyepoint is within
  912. int iEntryAnim = GetEntryAnimForPoint( pPlayer->EyePosition() );
  913. // Get this interface for animation queries
  914. CBaseAnimating *pAnimating = dynamic_cast<CBaseAnimating *>(m_pVehicle);
  915. if ( !pAnimating )
  916. return;
  917. // Are we in an entrypoint zone?
  918. if ( iEntryAnim == ACTIVITY_NOT_AVAILABLE )
  919. {
  920. // Normal get in refuses to allow entry
  921. if ( !bAllowEntryOutsideZone )
  922. return;
  923. // We failed to find a valid entry anim, but we've got to get back in because the player's
  924. // got stuck exiting the vehicle. For now, just use the first get in anim
  925. // UNDONE: We need a better solution for this.
  926. iEntryAnim = pAnimating->LookupSequence( m_EntryAnimations[0].szAnimName );
  927. }
  928. // Check to see if this vehicle can be controlled or if it's locked
  929. if ( GetDrivableVehicle()->CanEnterVehicle( pPlayer ) )
  930. {
  931. // Make sure the passenger can get in as well
  932. if ( pPlayer->CanEnterVehicle( this, VEHICLE_ROLE_DRIVER ) )
  933. {
  934. // Setup the "enter" vehicle sequence and skip the animation if it isn't present.
  935. pAnimating->SetCycle( 0 );
  936. pAnimating->m_flAnimTime = gpGlobals->curtime;
  937. pAnimating->ResetSequence( iEntryAnim );
  938. pAnimating->ResetClientsideFrame();
  939. pAnimating->InvalidateBoneCache(); // This is necessary because we need to query attachment points this frame for blending!
  940. GetDrivableVehicle()->SetVehicleEntryAnim( true );
  941. pPlayer->GetInVehicle( this, VEHICLE_ROLE_DRIVER );
  942. }
  943. }
  944. }
  945. else
  946. {
  947. // NPCs handle transitioning themselves, they should NOT call this function
  948. Assert( 0 );
  949. }
  950. }
  951. //-----------------------------------------------------------------------------
  952. // Purpose:
  953. //-----------------------------------------------------------------------------
  954. bool CBaseServerVehicle::HandlePassengerExit( CBaseCombatCharacter *pPassenger )
  955. {
  956. CBasePlayer *pPlayer = ToBasePlayer( pPassenger );
  957. if ( pPlayer != NULL )
  958. {
  959. // Clear hud hints
  960. UTIL_HudHintText( pPlayer, "" );
  961. vbs_sound_update_t params;
  962. InitSoundParams(params);
  963. params.bExitVehicle = true;
  964. SoundState_Update( params );
  965. // Find the right exit anim to use based on available exit points.
  966. Vector vecExitPoint;
  967. bool bAllPointsBlocked;
  968. int iSequence = GetExitAnimToUse( vecExitPoint, bAllPointsBlocked );
  969. // If all exit points were blocked and this vehicle doesn't allow exiting in
  970. // these cases, bail.
  971. Vector vecNewPos = pPlayer->GetAbsOrigin();
  972. QAngle angNewAngles = pPlayer->GetAbsAngles();
  973. int nRole = GetPassengerRole( pPlayer );
  974. if ( ( bAllPointsBlocked ) || ( iSequence == ACTIVITY_NOT_AVAILABLE ) )
  975. {
  976. // Animation-driven exit points are all blocked, or we have none. Fall back to the more simple static exit points.
  977. if ( !GetPassengerExitPoint( nRole, &vecNewPos, &angNewAngles ) && !GetDrivableVehicle()->AllowBlockedExit( pPlayer, nRole ) )
  978. return false;
  979. // At this point, the player has exited the vehicle but did so without playing an animation. We need to give the vehicle a
  980. // chance to do any post-animation clean-up it may need to perform.
  981. HandleEntryExitFinish( false, true );
  982. }
  983. // Now we either have an exit sequence to play, a valid static exit position, or we don't care
  984. // whether we're blocked or not. We're getting out, one way or another.
  985. GetDrivableVehicle()->PreExitVehicle( pPlayer, nRole );
  986. if ( iSequence > ACTIVITY_NOT_AVAILABLE )
  987. {
  988. CBaseAnimating *pAnimating = dynamic_cast<CBaseAnimating *>(m_pVehicle);
  989. if ( pAnimating )
  990. {
  991. pAnimating->SetCycle( 0 );
  992. pAnimating->m_flAnimTime = gpGlobals->curtime;
  993. pAnimating->ResetSequence( iSequence );
  994. pAnimating->ResetClientsideFrame();
  995. GetDrivableVehicle()->SetVehicleExitAnim( true, vecExitPoint );
  996. // Re-deploy our weapon
  997. if ( pPlayer && pPlayer->IsAlive() )
  998. {
  999. if ( pPlayer->GetActiveWeapon() )
  1000. {
  1001. pPlayer->GetActiveWeapon()->Deploy();
  1002. pPlayer->ShowCrosshair( true );
  1003. }
  1004. }
  1005. // To prevent anything moving into the volume the player's going to occupy at the end of the exit
  1006. // NOTE: Set the player as the blocker's owner so the player is allowed to intersect it
  1007. Vector vecExitFeetPoint = vecExitPoint - VEC_VIEW;
  1008. m_hExitBlocker = CEntityBlocker::Create( vecExitFeetPoint, VEC_HULL_MIN, VEC_HULL_MAX, pPlayer, true );
  1009. // We may as well stand where we're going to get out at and stop being parented
  1010. pPlayer->SetAbsOrigin( vecExitFeetPoint );
  1011. pPlayer->SetParent( NULL );
  1012. return true;
  1013. }
  1014. }
  1015. // Couldn't find an animation, so exit immediately
  1016. pPlayer->LeaveVehicle( vecNewPos, angNewAngles );
  1017. return true;
  1018. }
  1019. else
  1020. {
  1021. // NPCs handle transitioning themselves, they should NOT call this function
  1022. Assert( 0 );
  1023. }
  1024. return false;
  1025. }
  1026. //-----------------------------------------------------------------------------
  1027. // Purpose:
  1028. //-----------------------------------------------------------------------------
  1029. int CBaseServerVehicle::GetEntryAnimForPoint( const Vector &vecEyePoint )
  1030. {
  1031. // Parse the vehicle animations the first time they get in the vehicle
  1032. if ( !m_bParsedAnimations )
  1033. {
  1034. // Load the entry/exit animations from the vehicle
  1035. ParseEntryExitAnims();
  1036. m_bParsedAnimations = true;
  1037. }
  1038. // No entry anims? Vehicles with no entry anims are always enterable.
  1039. if ( !m_EntryAnimations.Count() )
  1040. return 0;
  1041. // Figure out which entrypoint hitbox the player is in
  1042. CBaseAnimating *pAnimating = dynamic_cast<CBaseAnimating *>(m_pVehicle);
  1043. if ( !pAnimating )
  1044. return 0;
  1045. CStudioHdr *pStudioHdr = pAnimating->GetModelPtr();
  1046. if (!pStudioHdr)
  1047. return 0;
  1048. int iHitboxSet = FindHitboxSetByName( pStudioHdr, "entryboxes" );
  1049. mstudiohitboxset_t *set = pStudioHdr->pHitboxSet( iHitboxSet );
  1050. if ( !set || !set->numhitboxes )
  1051. return 0;
  1052. // Loop through the hitboxes and find out which one we're in
  1053. for ( int i = 0; i < set->numhitboxes; i++ )
  1054. {
  1055. mstudiobbox_t *pbox = set->pHitbox( i );
  1056. Vector vecPosition;
  1057. QAngle vecAngles;
  1058. pAnimating->GetBonePosition( pbox->bone, vecPosition, vecAngles );
  1059. // Build a rotation matrix from orientation
  1060. matrix3x4_t fRotateMatrix;
  1061. AngleMatrix( vecAngles, vecPosition, fRotateMatrix);
  1062. Vector localEyePoint;
  1063. VectorITransform( vecEyePoint, fRotateMatrix, localEyePoint );
  1064. if ( IsPointInBox( localEyePoint, pbox->bbmin, pbox->bbmax ) )
  1065. {
  1066. // Find the entry animation for this hitbox
  1067. int iCount = m_EntryAnimations.Count();
  1068. for ( int entry = 0; entry < iCount; entry++ )
  1069. {
  1070. if ( m_EntryAnimations[entry].iHitboxGroup == pbox->group )
  1071. {
  1072. // Get the sequence for the animation
  1073. return pAnimating->LookupSequence( m_EntryAnimations[entry].szAnimName );
  1074. }
  1075. }
  1076. }
  1077. }
  1078. // Fail
  1079. return ACTIVITY_NOT_AVAILABLE;
  1080. }
  1081. //-----------------------------------------------------------------------------
  1082. // Purpose: Find an exit animation that'll get the player to a valid position
  1083. // Input : vecEyeExitEndpoint - Returns with the final eye position after exiting.
  1084. // bAllPointsBlocked - Returns whether all exit points were found to be blocked.
  1085. // Output :
  1086. //-----------------------------------------------------------------------------
  1087. int CBaseServerVehicle::GetExitAnimToUse( Vector &vecEyeExitEndpoint, bool &bAllPointsBlocked )
  1088. {
  1089. bAllPointsBlocked = false;
  1090. // Parse the vehicle animations the first time they get in the vehicle
  1091. if ( !m_bParsedAnimations )
  1092. {
  1093. // Load the entry/exit animations from the vehicle
  1094. ParseEntryExitAnims();
  1095. m_bParsedAnimations = true;
  1096. }
  1097. // No exit anims?
  1098. if ( !m_ExitAnimations.Count() )
  1099. return ACTIVITY_NOT_AVAILABLE;
  1100. // Figure out which entrypoint hitbox the player is in
  1101. CBaseAnimating *pAnimating = dynamic_cast<CBaseAnimating *>(m_pVehicle);
  1102. if ( !pAnimating )
  1103. return ACTIVITY_NOT_AVAILABLE;
  1104. CStudioHdr *pStudioHdr = pAnimating->GetModelPtr();
  1105. if (!pStudioHdr)
  1106. return ACTIVITY_NOT_AVAILABLE;
  1107. bool bUpright = IsVehicleUpright();
  1108. // Loop through the exit animations and find one that ends in a clear position
  1109. // Also attempt to choose the animation which brings you closest to your view direction.
  1110. CBasePlayer *pPlayer = ToBasePlayer( GetDriver() );
  1111. if ( pPlayer == NULL )
  1112. return ACTIVITY_NOT_AVAILABLE;
  1113. int nRole = GetPassengerRole( pPlayer );
  1114. int nBestExitAnim = -1;
  1115. bool bBestExitIsEscapePoint = true;
  1116. Vector vecViewDirection, vecViewOrigin, vecBestExitPoint( 0, 0, 0 );
  1117. vecViewOrigin = pPlayer->EyePosition();
  1118. pPlayer->EyeVectors( &vecViewDirection );
  1119. vecViewDirection.z = 0.0f;
  1120. VectorNormalize( vecViewDirection );
  1121. float flMaxCosAngleDelta = -2.0f;
  1122. int iCount = m_ExitAnimations.Count();
  1123. for ( int i = 0; i < iCount; i++ )
  1124. {
  1125. if ( m_ExitAnimations[i].bUpright != bUpright )
  1126. continue;
  1127. // Don't use an escape point if we found a non-escape point already
  1128. if ( !bBestExitIsEscapePoint && m_ExitAnimations[i].bEscapeExit )
  1129. continue;
  1130. Vector vehicleExitOrigin;
  1131. QAngle vehicleExitAngles;
  1132. // NOTE: HL2 and Ep1 used a method that relied on the animators to place attachment points in the model which marked where
  1133. // the player would exit to. This was rendered unnecessary in Ep2, but the choreo vehicles of these older products
  1134. // did not have proper exit animations and relied on the exact queries that were happening before. For choreo vehicles,
  1135. // we now just allow them to perform those older queries to keep those products happy. - jdw
  1136. // Get the position we think we're going to end up at
  1137. if ( m_bUseLegacyExitChecks )
  1138. {
  1139. pAnimating->GetAttachment( m_ExitAnimations[i].szAnimName, vehicleExitOrigin, vehicleExitAngles );
  1140. }
  1141. else
  1142. {
  1143. vehicleExitOrigin = m_ExitAnimations[i].vecExitPointLocal;
  1144. vehicleExitAngles = m_ExitAnimations[i].vecExitAnglesLocal;
  1145. UTIL_ParentToWorldSpace( pAnimating, vehicleExitOrigin, vehicleExitAngles );
  1146. }
  1147. // Don't bother checking points which are farther from our view direction.
  1148. Vector vecDelta;
  1149. VectorSubtract( vehicleExitOrigin, vecViewOrigin, vecDelta );
  1150. vecDelta.z = 0.0f;
  1151. VectorNormalize( vecDelta );
  1152. float flCosAngleDelta = DotProduct( vecDelta, vecViewDirection );
  1153. // But always check non-escape exits if our current best exit is an escape exit.
  1154. if ( !bBestExitIsEscapePoint || m_ExitAnimations[i].bEscapeExit )
  1155. {
  1156. if ( flCosAngleDelta < flMaxCosAngleDelta )
  1157. continue;
  1158. }
  1159. // The attachment points are where the driver's eyes will end up, so we subtract the view offset
  1160. // to get the actual exit position.
  1161. vehicleExitOrigin -= VEC_VIEW;
  1162. Vector vecMove(0,0,64);
  1163. Vector vecStart = vehicleExitOrigin + vecMove;
  1164. Vector vecEnd = vehicleExitOrigin - vecMove;
  1165. // Starting at the exit point, trace a flat plane down until we hit ground
  1166. // NOTE: The hull has no vertical span because we want to test the lateral constraints against the ground, not height (yet)
  1167. trace_t tr;
  1168. UTIL_TraceHull( vecStart, vecEnd, VEC_HULL_MIN, Vector( VEC_HULL_MAX.x, VEC_HULL_MAX.y, VEC_HULL_MIN.z ), MASK_PLAYERSOLID, NULL, COLLISION_GROUP_NONE, &tr );
  1169. if ( g_debug_vehicleexit.GetBool() )
  1170. {
  1171. NDebugOverlay::SweptBox( vecStart, vecEnd, VEC_HULL_MIN, Vector( VEC_HULL_MAX.x, VEC_HULL_MAX.y, VEC_HULL_MIN.y ), vec3_angle, 255, 255, 255, 8.0f, 20.0f );
  1172. }
  1173. if ( tr.fraction < 1.0f )
  1174. {
  1175. // If we hit the ground, try to now "stand up" at that point to see if we'll fit
  1176. UTIL_TraceHull( tr.endpos, tr.endpos, VEC_HULL_MIN, VEC_HULL_MAX, MASK_PLAYERSOLID, NULL, COLLISION_GROUP_NONE, &tr );
  1177. // See if we're unable to stand at this space
  1178. if ( tr.startsolid )
  1179. {
  1180. if ( g_debug_vehicleexit.GetBool() )
  1181. {
  1182. NDebugOverlay::Box( tr.endpos, VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 0, 8.0f, 20.0f );
  1183. }
  1184. continue;
  1185. }
  1186. if ( g_debug_vehicleexit.GetBool() )
  1187. {
  1188. NDebugOverlay::Box( tr.endpos, VEC_HULL_MIN, VEC_HULL_MAX, 0, 255, 0, 8.0f, 20.0f );
  1189. }
  1190. }
  1191. else if ( tr.allsolid || ( ( tr.fraction == 1.0 ) && !GetDrivableVehicle()->AllowMidairExit( pPlayer, nRole ) ) )
  1192. {
  1193. if ( g_debug_vehicleexit.GetBool() )
  1194. {
  1195. NDebugOverlay::Box( tr.endpos, VEC_HULL_MIN, VEC_HULL_MAX, 255,0,0, 64, 10 );
  1196. }
  1197. continue;
  1198. }
  1199. // Calculate the exit endpoint & viewpoint
  1200. Vector vecExitEndPoint = tr.endpos;
  1201. // Make sure we can trace to the center of the exit point
  1202. UTIL_TraceLine( vecViewOrigin, vecExitEndPoint, MASK_PLAYERSOLID, pAnimating, COLLISION_GROUP_NONE, &tr );
  1203. if ( tr.fraction != 1.0 )
  1204. {
  1205. #ifdef HL2_EPISODIC
  1206. if ( ShouldVehicleIgnoreEntity( GetVehicleEnt(), tr.m_pEnt ) == false )
  1207. #endif //HL2_EPISODIC
  1208. {
  1209. if ( g_debug_vehicleexit.GetBool() )
  1210. {
  1211. NDebugOverlay::Line( vecViewOrigin, vecExitEndPoint, 255,0,0, true, 10 );
  1212. }
  1213. continue;
  1214. }
  1215. }
  1216. bBestExitIsEscapePoint = m_ExitAnimations[i].bEscapeExit;
  1217. vecBestExitPoint = vecExitEndPoint;
  1218. nBestExitAnim = i;
  1219. flMaxCosAngleDelta = flCosAngleDelta;
  1220. }
  1221. if ( nBestExitAnim >= 0 )
  1222. {
  1223. m_vecCurrentExitEndPoint = vecBestExitPoint;
  1224. if ( g_debug_vehicleexit.GetBool() )
  1225. {
  1226. NDebugOverlay::Cross3D( m_vecCurrentExitEndPoint, 16, 0, 255, 0, true, 10 );
  1227. NDebugOverlay::Box( m_vecCurrentExitEndPoint, VEC_HULL_MIN, VEC_HULL_MAX, 255,255,255, 8, 10 );
  1228. }
  1229. vecEyeExitEndpoint = vecBestExitPoint + VEC_VIEW;
  1230. m_iCurrentExitAnim = nBestExitAnim;
  1231. return pAnimating->LookupSequence( m_ExitAnimations[m_iCurrentExitAnim].szAnimName );
  1232. }
  1233. // Fail, all exit points were blocked.
  1234. bAllPointsBlocked = true;
  1235. return ACTIVITY_NOT_AVAILABLE;
  1236. }
  1237. //-----------------------------------------------------------------------------
  1238. // Purpose:
  1239. //-----------------------------------------------------------------------------
  1240. void CBaseServerVehicle::HandleEntryExitFinish( bool bExitAnimOn, bool bResetAnim )
  1241. {
  1242. // Parse the vehicle animations. This is needed because they may have
  1243. // saved, and loaded during exit anim, which would clear the exit anim.
  1244. if ( !m_bParsedAnimations )
  1245. {
  1246. // Load the entry/exit animations from the vehicle
  1247. ParseEntryExitAnims();
  1248. m_bParsedAnimations = true;
  1249. }
  1250. // Figure out which entrypoint hitbox the player is in
  1251. CBaseAnimating *pAnimating = m_pVehicle->GetBaseAnimating();
  1252. if ( !pAnimating )
  1253. return;
  1254. // Did the entry anim just finish?
  1255. if ( bExitAnimOn )
  1256. {
  1257. // The exit animation just finished
  1258. CBasePlayer *pPlayer = ToBasePlayer( GetDriver() );
  1259. if ( pPlayer != NULL )
  1260. {
  1261. Vector vecEyes;
  1262. QAngle vecEyeAng;
  1263. if ( m_iCurrentExitAnim >= 0 && m_iCurrentExitAnim < m_ExitAnimations.Count() )
  1264. {
  1265. // Convert our offset points to worldspace ones
  1266. vecEyes = m_ExitAnimations[m_iCurrentExitAnim].vecExitPointLocal;
  1267. vecEyeAng = m_ExitAnimations[m_iCurrentExitAnim].vecExitAnglesLocal;
  1268. UTIL_ParentToWorldSpace( pAnimating, vecEyes, vecEyeAng );
  1269. // Use the endpoint we figured out when we exited
  1270. vecEyes = m_vecCurrentExitEndPoint;
  1271. }
  1272. else
  1273. {
  1274. pAnimating->GetAttachment( "vehicle_driver_eyes", vecEyes, vecEyeAng );
  1275. }
  1276. if ( g_debug_vehicleexit.GetBool() )
  1277. {
  1278. NDebugOverlay::Box( vecEyes, -Vector(2,2,2), Vector(2,2,2), 255,0,0, 64, 10.0 );
  1279. }
  1280. // If the end point isn't clear, get back in the vehicle
  1281. /*
  1282. trace_t tr;
  1283. UTIL_TraceHull( vecEyes, vecEyes, VEC_HULL_MIN, VEC_HULL_MAX, MASK_SOLID, NULL, COLLISION_GROUP_NONE, &tr );
  1284. if ( tr.startsolid && tr.fraction < 1.0 )
  1285. {
  1286. pPlayer->LeaveVehicle( vecEyes, vecEyeAng );
  1287. m_pVehicle->Use( pPlayer, pPlayer, USE_TOGGLE, 1 );
  1288. return;
  1289. }
  1290. */
  1291. pPlayer->LeaveVehicle( vecEyes, vecEyeAng );
  1292. }
  1293. }
  1294. // Only reset the animation if we're told to
  1295. if ( bResetAnim )
  1296. {
  1297. // Start the vehicle idling again
  1298. int iSequence = pAnimating->SelectWeightedSequence( ACT_IDLE );
  1299. if ( iSequence > ACTIVITY_NOT_AVAILABLE )
  1300. {
  1301. pAnimating->SetCycle( 0 );
  1302. pAnimating->m_flAnimTime = gpGlobals->curtime;
  1303. pAnimating->ResetSequence( iSequence );
  1304. pAnimating->ResetClientsideFrame();
  1305. }
  1306. }
  1307. GetDrivableVehicle()->SetVehicleEntryAnim( false );
  1308. GetDrivableVehicle()->SetVehicleExitAnim( false, vec3_origin );
  1309. }
  1310. //-----------------------------------------------------------------------------
  1311. // Purpose: Where does the passenger see from?
  1312. //-----------------------------------------------------------------------------
  1313. void CBaseServerVehicle::GetVehicleViewPosition( int nRole, Vector *pAbsOrigin, QAngle *pAbsAngles, float *pFOV /*= NULL*/ )
  1314. {
  1315. Assert( nRole == VEHICLE_ROLE_DRIVER );
  1316. CBaseCombatCharacter *pPassenger = GetPassenger( VEHICLE_ROLE_DRIVER );
  1317. Assert( pPassenger );
  1318. CBasePlayer *pPlayer = ToBasePlayer( pPassenger );
  1319. if ( pPlayer != NULL )
  1320. {
  1321. // Call through the player to resolve the actual position (if available)
  1322. if ( pAbsOrigin != NULL )
  1323. {
  1324. *pAbsOrigin = pPlayer->EyePosition();
  1325. }
  1326. if ( pAbsAngles != NULL )
  1327. {
  1328. *pAbsAngles = pPlayer->EyeAngles();
  1329. }
  1330. if ( pFOV )
  1331. {
  1332. *pFOV = pPlayer->GetFOV();
  1333. }
  1334. }
  1335. else
  1336. {
  1337. // NPCs are not supported
  1338. Assert( 0 );
  1339. }
  1340. }
  1341. //-----------------------------------------------------------------------------
  1342. // Purpose:
  1343. //-----------------------------------------------------------------------------
  1344. void CBaseServerVehicle::SetupMove( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper *pHelper, CMoveData *move )
  1345. {
  1346. GetDrivableVehicle()->SetupMove( player, ucmd, pHelper, move );
  1347. }
  1348. //-----------------------------------------------------------------------------
  1349. // Purpose:
  1350. //-----------------------------------------------------------------------------
  1351. void CBaseServerVehicle::ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMoveData )
  1352. {
  1353. GetDrivableVehicle()->ProcessMovement( pPlayer, pMoveData );
  1354. trace_t tr;
  1355. UTIL_TraceLine( pPlayer->GetAbsOrigin(), pPlayer->GetAbsOrigin() - Vector( 0, 0, 256 ), MASK_PLAYERSOLID, GetVehicleEnt(), COLLISION_GROUP_NONE, &tr );
  1356. // If our gamematerial has changed, tell any player surface triggers that are watching
  1357. IPhysicsSurfaceProps *physprops = MoveHelper()->GetSurfaceProps();
  1358. const surfacedata_t *pSurfaceProp = physprops->GetSurfaceData( tr.surface.surfaceProps );
  1359. char cCurrGameMaterial = pSurfaceProp->game.material;
  1360. // Changed?
  1361. if ( m_chPreviousTextureType != cCurrGameMaterial )
  1362. {
  1363. CEnvPlayerSurfaceTrigger::SetPlayerSurface( pPlayer, cCurrGameMaterial );
  1364. }
  1365. m_chPreviousTextureType = cCurrGameMaterial;
  1366. }
  1367. //-----------------------------------------------------------------------------
  1368. // Purpose:
  1369. //-----------------------------------------------------------------------------
  1370. void CBaseServerVehicle::FinishMove( CBasePlayer *player, CUserCmd *ucmd, CMoveData *move )
  1371. {
  1372. GetDrivableVehicle()->FinishMove( player, ucmd, move );
  1373. }
  1374. //-----------------------------------------------------------------------------
  1375. // Purpose:
  1376. //-----------------------------------------------------------------------------
  1377. void CBaseServerVehicle::ItemPostFrame( CBasePlayer *player )
  1378. {
  1379. Assert( player == GetDriver() );
  1380. GetDrivableVehicle()->ItemPostFrame( player );
  1381. if ( player->m_afButtonPressed & IN_USE )
  1382. {
  1383. if ( GetDrivableVehicle()->CanExitVehicle(player) )
  1384. {
  1385. if ( !HandlePassengerExit( player ) && ( player != NULL ) )
  1386. {
  1387. player->PlayUseDenySound();
  1388. }
  1389. }
  1390. }
  1391. }
  1392. //-----------------------------------------------------------------------------
  1393. // Purpose:
  1394. //-----------------------------------------------------------------------------
  1395. void CBaseServerVehicle::NPC_ThrottleForward( void )
  1396. {
  1397. m_nNPCButtons |= IN_FORWARD;
  1398. m_nNPCButtons &= ~IN_BACK;
  1399. m_nNPCButtons &= ~IN_JUMP;
  1400. }
  1401. //-----------------------------------------------------------------------------
  1402. // Purpose:
  1403. //-----------------------------------------------------------------------------
  1404. void CBaseServerVehicle::NPC_ThrottleReverse( void )
  1405. {
  1406. m_nNPCButtons |= IN_BACK;
  1407. m_nNPCButtons &= ~IN_FORWARD;
  1408. m_nNPCButtons &= ~IN_JUMP;
  1409. }
  1410. //-----------------------------------------------------------------------------
  1411. // Purpose:
  1412. //-----------------------------------------------------------------------------
  1413. void CBaseServerVehicle::NPC_ThrottleCenter( void )
  1414. {
  1415. m_nNPCButtons &= ~IN_FORWARD;
  1416. m_nNPCButtons &= ~IN_BACK;
  1417. m_nNPCButtons &= ~IN_JUMP;
  1418. }
  1419. //-----------------------------------------------------------------------------
  1420. // Purpose:
  1421. //-----------------------------------------------------------------------------
  1422. void CBaseServerVehicle::NPC_Brake( void )
  1423. {
  1424. m_nNPCButtons &= ~IN_FORWARD;
  1425. m_nNPCButtons &= ~IN_BACK;
  1426. m_nNPCButtons |= IN_JUMP;
  1427. }
  1428. //-----------------------------------------------------------------------------
  1429. // Purpose:
  1430. //-----------------------------------------------------------------------------
  1431. void CBaseServerVehicle::NPC_TurnLeft( float flDegrees )
  1432. {
  1433. m_nNPCButtons |= IN_MOVELEFT;
  1434. m_nNPCButtons &= ~IN_MOVERIGHT;
  1435. m_flTurnDegrees = -flDegrees;
  1436. }
  1437. //-----------------------------------------------------------------------------
  1438. // Purpose:
  1439. //-----------------------------------------------------------------------------
  1440. void CBaseServerVehicle::NPC_TurnRight( float flDegrees )
  1441. {
  1442. m_nNPCButtons |= IN_MOVERIGHT;
  1443. m_nNPCButtons &= ~IN_MOVELEFT;
  1444. m_flTurnDegrees = flDegrees;
  1445. }
  1446. //-----------------------------------------------------------------------------
  1447. // Purpose:
  1448. //-----------------------------------------------------------------------------
  1449. void CBaseServerVehicle::NPC_TurnCenter( void )
  1450. {
  1451. m_nNPCButtons &= ~IN_MOVERIGHT;
  1452. m_nNPCButtons &= ~IN_MOVELEFT;
  1453. }
  1454. //-----------------------------------------------------------------------------
  1455. // Purpose:
  1456. //-----------------------------------------------------------------------------
  1457. void CBaseServerVehicle::NPC_PrimaryFire( void )
  1458. {
  1459. m_nNPCButtons |= IN_ATTACK;
  1460. }
  1461. //-----------------------------------------------------------------------------
  1462. // Purpose:
  1463. //-----------------------------------------------------------------------------
  1464. void CBaseServerVehicle::NPC_SecondaryFire( void )
  1465. {
  1466. m_nNPCButtons |= IN_ATTACK2;
  1467. }
  1468. //-----------------------------------------------------------------------------
  1469. // Purpose:
  1470. //-----------------------------------------------------------------------------
  1471. void CBaseServerVehicle::Weapon_PrimaryRanges( float *flMinRange, float *flMaxRange )
  1472. {
  1473. *flMinRange = 64;
  1474. *flMaxRange = 1024;
  1475. }
  1476. //-----------------------------------------------------------------------------
  1477. // Purpose:
  1478. //-----------------------------------------------------------------------------
  1479. void CBaseServerVehicle::Weapon_SecondaryRanges( float *flMinRange, float *flMaxRange )
  1480. {
  1481. *flMinRange = 64;
  1482. *flMaxRange = 1024;
  1483. }
  1484. //-----------------------------------------------------------------------------
  1485. // Purpose: Return the time at which this vehicle's primary weapon can fire again
  1486. //-----------------------------------------------------------------------------
  1487. float CBaseServerVehicle::Weapon_PrimaryCanFireAt( void )
  1488. {
  1489. return gpGlobals->curtime;
  1490. }
  1491. //-----------------------------------------------------------------------------
  1492. // Purpose: Return the time at which this vehicle's secondary weapon can fire again
  1493. //-----------------------------------------------------------------------------
  1494. float CBaseServerVehicle::Weapon_SecondaryCanFireAt( void )
  1495. {
  1496. return gpGlobals->curtime;
  1497. }
  1498. const char *pSoundStateNames[] =
  1499. {
  1500. "SS_NONE",
  1501. "SS_SHUTDOWN",
  1502. "SS_SHUTDOWN_WATER",
  1503. "SS_START_WATER",
  1504. "SS_START_IDLE",
  1505. "SS_IDLE",
  1506. "SS_GEAR_0",
  1507. "SS_GEAR_1",
  1508. "SS_GEAR_2",
  1509. "SS_GEAR_3",
  1510. "SS_GEAR_4",
  1511. "SS_SLOWDOWN",
  1512. "SS_SLOWDOWN_HIGHSPEED",
  1513. "SS_GEAR_0_RESUME",
  1514. "SS_GEAR_1_RESUME",
  1515. "SS_GEAR_2_RESUME",
  1516. "SS_GEAR_3_RESUME",
  1517. "SS_GEAR_4_RESUME",
  1518. "SS_TURBO",
  1519. "SS_REVERSE",
  1520. };
  1521. static int SoundStateIndexFromName( const char *pName )
  1522. {
  1523. for ( int i = 0; i < SS_NUM_STATES; i++ )
  1524. {
  1525. Assert( i < ARRAYSIZE(pSoundStateNames) );
  1526. if ( !strcmpi( pSoundStateNames[i], pName ) )
  1527. return i;
  1528. }
  1529. return -1;
  1530. }
  1531. static const char *SoundStateNameFromIndex( int index )
  1532. {
  1533. index = clamp(index, 0, SS_NUM_STATES-1 );
  1534. return pSoundStateNames[index];
  1535. }
  1536. void CBaseServerVehicle::PlaySound( const char *pSound )
  1537. {
  1538. if ( !pSound || !pSound[0] )
  1539. return;
  1540. if ( g_debug_vehiclesound.GetInt() )
  1541. {
  1542. Msg("Playing non-looping vehicle sound: %s\n", pSound );
  1543. }
  1544. m_pVehicle->EmitSound( pSound );
  1545. }
  1546. void CBaseServerVehicle::StopLoopingSound( float fadeTime )
  1547. {
  1548. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  1549. if ( m_pStateSoundFade )
  1550. {
  1551. controller.SoundDestroy( m_pStateSoundFade );
  1552. m_pStateSoundFade = NULL;
  1553. }
  1554. if ( m_pStateSound )
  1555. {
  1556. m_pStateSoundFade = m_pStateSound;
  1557. m_pStateSound = NULL;
  1558. controller.SoundFadeOut( m_pStateSoundFade, fadeTime, false );
  1559. }
  1560. }
  1561. void CBaseServerVehicle::PlayLoopingSound( const char *pSoundName )
  1562. {
  1563. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  1564. CPASAttenuationFilter filter( m_pVehicle );
  1565. CSoundPatch *pNewSound = NULL;
  1566. if ( pSoundName && pSoundName[0] )
  1567. {
  1568. pNewSound = controller.SoundCreate( filter, m_pVehicle->entindex(), CHAN_STATIC, pSoundName, ATTN_NORM );
  1569. }
  1570. if ( m_pStateSound && pNewSound && controller.SoundGetName( pNewSound ) == controller.SoundGetName( m_pStateSound ) )
  1571. {
  1572. // if the sound is the same, don't play this, just re-use the old one
  1573. controller.SoundDestroy( pNewSound );
  1574. pNewSound = m_pStateSound;
  1575. controller.SoundChangeVolume( pNewSound, 1.0f, 0.0f );
  1576. m_pStateSound = NULL;
  1577. }
  1578. else if ( g_debug_vehiclesound.GetInt() )
  1579. {
  1580. const char *pStopSound = m_pStateSound ? controller.SoundGetName( m_pStateSound ).ToCStr() : "NULL";
  1581. const char *pStartSound = pNewSound ? controller.SoundGetName( pNewSound ).ToCStr() : "NULL";
  1582. Msg("Stop %s, start %s\n", pStopSound, pStartSound );
  1583. }
  1584. StopLoopingSound();
  1585. m_pStateSound = pNewSound;
  1586. if ( m_pStateSound )
  1587. {
  1588. controller.Play( m_pStateSound, 1.0f, 100 );
  1589. }
  1590. }
  1591. static sound_states MapGearToState( vbs_sound_update_t &params, int gear )
  1592. {
  1593. switch( gear )
  1594. {
  1595. case 0: return params.bReverse ? SS_REVERSE : SS_GEAR_0;
  1596. case 1: return SS_GEAR_1;
  1597. case 2: return SS_GEAR_2;
  1598. case 3: return SS_GEAR_3;
  1599. default:case 4: return SS_GEAR_4;
  1600. }
  1601. }
  1602. static sound_states MapGearToMidState( vbs_sound_update_t &params, int gear )
  1603. {
  1604. switch( gear )
  1605. {
  1606. case 0: return params.bReverse ? SS_REVERSE : SS_GEAR_0_RESUME;
  1607. case 1: return SS_GEAR_1_RESUME;
  1608. case 2: return SS_GEAR_2_RESUME;
  1609. case 3: return SS_GEAR_3_RESUME;
  1610. default:case 4: return SS_GEAR_4_RESUME;
  1611. }
  1612. }
  1613. bool CBaseServerVehicle::PlayCrashSound( float speed )
  1614. {
  1615. int i;
  1616. float delta = 0;
  1617. float absSpeed = fabs(speed);
  1618. float absLastSpeed = fabs(m_lastSpeed);
  1619. if ( absLastSpeed > absSpeed )
  1620. {
  1621. delta = fabs(m_lastSpeed - speed);
  1622. }
  1623. float rumble = delta / 8.0f;
  1624. if( rumble > 60.0f )
  1625. rumble = 60.0f;
  1626. if( rumble > 5.0f )
  1627. {
  1628. if ( GetDriver() )
  1629. {
  1630. UTIL_ScreenShake( GetDriver()->GetAbsOrigin(), rumble, 150.0f, 1.0f, 240.0f, SHAKE_START_RUMBLEONLY, true );
  1631. }
  1632. }
  1633. for ( i = 0; i < m_vehicleSounds.crashSounds.Count(); i++ )
  1634. {
  1635. const vehicle_crashsound_t &crash = m_vehicleSounds.crashSounds[i];
  1636. if ( !crash.gearLimit )
  1637. continue;
  1638. if ( m_iSoundGear <= crash.gearLimit )
  1639. {
  1640. if ( delta > crash.flMinDeltaSpeed && absLastSpeed > crash.flMinSpeed )
  1641. {
  1642. PlaySound( crash.iszCrashSound.ToCStr() );
  1643. return true;
  1644. }
  1645. }
  1646. }
  1647. for ( i = m_vehicleSounds.crashSounds.Count()-1; i >= 0; --i )
  1648. {
  1649. const vehicle_crashsound_t &crash = m_vehicleSounds.crashSounds[i];
  1650. if ( delta > crash.flMinDeltaSpeed && absLastSpeed > crash.flMinSpeed )
  1651. {
  1652. PlaySound( crash.iszCrashSound.ToCStr() );
  1653. return true;
  1654. }
  1655. }
  1656. return false;
  1657. }
  1658. bool CBaseServerVehicle::CheckCrash( vbs_sound_update_t &params )
  1659. {
  1660. if ( params.bVehicleInWater )
  1661. return false;
  1662. bool bCrashed = PlayCrashSound( params.flWorldSpaceSpeed );
  1663. if ( bCrashed )
  1664. {
  1665. if ( g_debug_vehiclesound.GetInt() )
  1666. {
  1667. Msg("Crashed!: speed %.2f, lastSpeed %.2f\n", params.flWorldSpaceSpeed, m_lastSpeed );
  1668. }
  1669. }
  1670. m_lastSpeed = params.flWorldSpaceSpeed;
  1671. return bCrashed;
  1672. }
  1673. sound_states CBaseServerVehicle::SoundState_ChooseState( vbs_sound_update_t &params )
  1674. {
  1675. float timeInState = gpGlobals->curtime - m_soundStateStartTime;
  1676. bool bInStateForMinTime = timeInState > m_vehicleSounds.minStateTime[m_soundState] ? true : false;
  1677. sound_states stateOut = m_soundState;
  1678. // exit overrides everything else
  1679. if ( params.bExitVehicle )
  1680. {
  1681. switch ( m_soundState )
  1682. {
  1683. case SS_NONE:
  1684. case SS_SHUTDOWN:
  1685. case SS_SHUTDOWN_WATER:
  1686. return m_soundState;
  1687. }
  1688. return SS_SHUTDOWN;
  1689. }
  1690. // check global state in states that don't mask them
  1691. switch( m_soundState )
  1692. {
  1693. // global states masked for these states.
  1694. case SS_NONE:
  1695. case SS_START_IDLE:
  1696. case SS_SHUTDOWN:
  1697. break;
  1698. case SS_START_WATER:
  1699. case SS_SHUTDOWN_WATER:
  1700. if ( !params.bVehicleInWater )
  1701. return SS_START_IDLE;
  1702. break;
  1703. case SS_TURBO:
  1704. if ( params.bVehicleInWater )
  1705. return SS_SHUTDOWN_WATER;
  1706. if ( CheckCrash(params) )
  1707. return SS_IDLE;
  1708. break;
  1709. case SS_IDLE:
  1710. if ( params.bVehicleInWater )
  1711. return SS_SHUTDOWN_WATER;
  1712. break;
  1713. case SS_REVERSE:
  1714. case SS_GEAR_0:
  1715. case SS_GEAR_1:
  1716. case SS_GEAR_2:
  1717. case SS_GEAR_3:
  1718. case SS_GEAR_4:
  1719. case SS_SLOWDOWN:
  1720. case SS_SLOWDOWN_HIGHSPEED:
  1721. case SS_GEAR_0_RESUME:
  1722. case SS_GEAR_1_RESUME:
  1723. case SS_GEAR_2_RESUME:
  1724. case SS_GEAR_3_RESUME:
  1725. case SS_GEAR_4_RESUME:
  1726. if ( params.bVehicleInWater )
  1727. {
  1728. return SS_SHUTDOWN_WATER;
  1729. }
  1730. if ( params.bTurbo )
  1731. {
  1732. return SS_TURBO;
  1733. }
  1734. if ( CheckCrash(params) )
  1735. return SS_IDLE;
  1736. break;
  1737. }
  1738. switch( m_soundState )
  1739. {
  1740. case SS_START_IDLE:
  1741. if ( bInStateForMinTime || params.bThrottleDown )
  1742. return SS_IDLE;
  1743. break;
  1744. case SS_IDLE:
  1745. if ( bInStateForMinTime && params.bThrottleDown )
  1746. {
  1747. if ( params.bTurbo )
  1748. return SS_TURBO;
  1749. return params.bReverse ? SS_REVERSE : SS_GEAR_0;
  1750. }
  1751. break;
  1752. case SS_GEAR_0_RESUME:
  1753. case SS_GEAR_0:
  1754. if ( (bInStateForMinTime && !params.bThrottleDown) || params.bReverse )
  1755. {
  1756. return SS_IDLE;
  1757. }
  1758. if ( m_iSoundGear > 0 )
  1759. {
  1760. return SS_GEAR_1;
  1761. }
  1762. break;
  1763. case SS_GEAR_1_RESUME:
  1764. case SS_GEAR_1:
  1765. if ( bInStateForMinTime )
  1766. {
  1767. if ( !params.bThrottleDown )
  1768. return SS_SLOWDOWN;
  1769. }
  1770. if ( m_iSoundGear != 1 )
  1771. return MapGearToState( params, m_iSoundGear);
  1772. break;
  1773. case SS_GEAR_2_RESUME:
  1774. case SS_GEAR_2:
  1775. if ( bInStateForMinTime )
  1776. {
  1777. if ( !params.bThrottleDown )
  1778. return SS_SLOWDOWN;
  1779. else if ( m_iSoundGear != 2 )
  1780. return MapGearToState(params, m_iSoundGear);
  1781. }
  1782. break;
  1783. case SS_GEAR_3_RESUME:
  1784. case SS_GEAR_3:
  1785. if ( bInStateForMinTime )
  1786. {
  1787. if ( !params.bThrottleDown )
  1788. return SS_SLOWDOWN;
  1789. else if ( m_iSoundGear != 3 )
  1790. return MapGearToState(params, m_iSoundGear);
  1791. }
  1792. break;
  1793. case SS_GEAR_4_RESUME:
  1794. case SS_GEAR_4:
  1795. if ( bInStateForMinTime && !params.bThrottleDown )
  1796. {
  1797. return SS_SLOWDOWN;
  1798. }
  1799. if ( m_iSoundGear != 4 )
  1800. {
  1801. return MapGearToMidState(params, m_iSoundGear);
  1802. }
  1803. break;
  1804. case SS_REVERSE:
  1805. if ( bInStateForMinTime && !params.bReverse )
  1806. {
  1807. return SS_SLOWDOWN;
  1808. }
  1809. break;
  1810. case SS_SLOWDOWN_HIGHSPEED:
  1811. case SS_SLOWDOWN:
  1812. if ( params.bThrottleDown )
  1813. {
  1814. // map gears
  1815. return MapGearToMidState(params, m_iSoundGear);
  1816. }
  1817. if ( m_iSoundGear == 0 )
  1818. {
  1819. return SS_IDLE;
  1820. }
  1821. break;
  1822. case SS_NONE:
  1823. stateOut = params.bVehicleInWater ? SS_START_WATER : SS_START_IDLE;
  1824. break;
  1825. case SS_TURBO:
  1826. if ( bInStateForMinTime && !params.bTurbo )
  1827. {
  1828. return MapGearToMidState(params, m_iSoundGear);
  1829. }
  1830. break;
  1831. default:
  1832. break;
  1833. }
  1834. return stateOut;
  1835. }
  1836. const char *CBaseServerVehicle::StateSoundName( sound_states state )
  1837. {
  1838. return m_vehicleSounds.iszStateSounds[state].ToCStr();
  1839. }
  1840. void CBaseServerVehicle::SoundState_OnNewState( sound_states lastState )
  1841. {
  1842. if ( g_debug_vehiclesound.GetInt() )
  1843. {
  1844. int index = m_soundState;
  1845. Msg("Switched to state: %d (%s)\n", m_soundState, SoundStateNameFromIndex(index) );
  1846. }
  1847. switch ( m_soundState )
  1848. {
  1849. case SS_SHUTDOWN:
  1850. case SS_SHUTDOWN_WATER:
  1851. case SS_START_WATER:
  1852. StopLoopingSound();
  1853. PlaySound( StateSoundName(m_soundState) );
  1854. break;
  1855. case SS_IDLE:
  1856. m_lastSpeed = -1;
  1857. PlayLoopingSound( StateSoundName(m_soundState) );
  1858. break;
  1859. case SS_START_IDLE:
  1860. case SS_REVERSE:
  1861. case SS_GEAR_0:
  1862. case SS_GEAR_0_RESUME:
  1863. case SS_GEAR_1:
  1864. case SS_GEAR_1_RESUME:
  1865. case SS_GEAR_2:
  1866. case SS_GEAR_2_RESUME:
  1867. case SS_GEAR_3:
  1868. case SS_GEAR_3_RESUME:
  1869. case SS_GEAR_4:
  1870. case SS_GEAR_4_RESUME:
  1871. case SS_TURBO:
  1872. PlayLoopingSound( StateSoundName(m_soundState) );
  1873. break;
  1874. case SS_SLOWDOWN_HIGHSPEED:
  1875. case SS_SLOWDOWN:
  1876. if ( m_iSoundGear < 2 )
  1877. {
  1878. PlayLoopingSound( StateSoundName( SS_SLOWDOWN ) );
  1879. }
  1880. else
  1881. {
  1882. PlayLoopingSound( StateSoundName( SS_SLOWDOWN_HIGHSPEED ) );
  1883. }
  1884. break;
  1885. default:break;
  1886. }
  1887. m_soundStateStartTime = gpGlobals->curtime;
  1888. }
  1889. void CBaseServerVehicle::SoundState_Update( vbs_sound_update_t &params )
  1890. {
  1891. sound_states newState = SoundState_ChooseState( params );
  1892. if ( newState != m_soundState )
  1893. {
  1894. sound_states lastState = m_soundState;
  1895. m_soundState = newState;
  1896. SoundState_OnNewState( lastState );
  1897. }
  1898. switch( m_soundState )
  1899. {
  1900. case SS_SHUTDOWN:
  1901. case SS_SHUTDOWN_WATER:
  1902. case SS_START_WATER:
  1903. case SS_START_IDLE:
  1904. case SS_IDLE:
  1905. case SS_REVERSE:
  1906. case SS_GEAR_0:
  1907. case SS_GEAR_4:
  1908. case SS_SLOWDOWN_HIGHSPEED:
  1909. case SS_SLOWDOWN:
  1910. case SS_GEAR_0_RESUME:
  1911. case SS_GEAR_4_RESUME:
  1912. break;
  1913. default:break;
  1914. }
  1915. }
  1916. void CBaseServerVehicle::InitSoundParams( vbs_sound_update_t &params )
  1917. {
  1918. params.Defaults();
  1919. params.bVehicleInWater = IsVehicleBodyInWater();
  1920. }
  1921. //-----------------------------------------------------------------------------
  1922. // Purpose: Vehicle Sound Start
  1923. //-----------------------------------------------------------------------------
  1924. void CBaseServerVehicle::SoundStart()
  1925. {
  1926. StartEngineRumble();
  1927. m_soundState = SS_NONE;
  1928. vbs_sound_update_t params;
  1929. InitSoundParams(params);
  1930. SoundState_Update( params );
  1931. }
  1932. // vehicle is starting up disabled, but in some cases you still want to play a sound
  1933. // HACK: handle those here.
  1934. void CBaseServerVehicle::SoundStartDisabled()
  1935. {
  1936. m_soundState = SS_NONE;
  1937. vbs_sound_update_t params;
  1938. InitSoundParams(params);
  1939. sound_states newState = SoundState_ChooseState( params );
  1940. switch( newState )
  1941. {
  1942. case SS_START_WATER:
  1943. PlaySound( StateSoundName(newState) );
  1944. break;
  1945. }
  1946. }
  1947. //-----------------------------------------------------------------------------
  1948. // Purpose:
  1949. //-----------------------------------------------------------------------------
  1950. void CBaseServerVehicle::SoundShutdown( float flFadeTime )
  1951. {
  1952. StopEngineRumble();
  1953. // Stop any looping sounds that may be running, as the following stop sound may not exist
  1954. // and thus leave a looping sound playing after the user gets out.
  1955. for ( int i = 0; i < NUM_SOUNDS_TO_STOP_ON_EXIT; i++ )
  1956. {
  1957. StopSound( g_iSoundsToStopOnExit[i] );
  1958. }
  1959. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  1960. if ( m_pStateSoundFade )
  1961. {
  1962. controller.SoundFadeOut( m_pStateSoundFade, flFadeTime, true );
  1963. m_pStateSoundFade = NULL;
  1964. }
  1965. if ( m_pStateSound )
  1966. {
  1967. controller.SoundFadeOut( m_pStateSound, flFadeTime, true );
  1968. m_pStateSound = NULL;
  1969. }
  1970. }
  1971. //-----------------------------------------------------------------------------
  1972. // Purpose:
  1973. //-----------------------------------------------------------------------------
  1974. void CBaseServerVehicle::SoundUpdate( vbs_sound_update_t &params )
  1975. {
  1976. if ( g_debug_vehiclesound.GetInt() > 1 )
  1977. {
  1978. Msg("Throttle: %s, Reverse: %s\n", params.bThrottleDown?"on":"off", params.bReverse?"on":"off" );
  1979. }
  1980. float flCurrentSpeed = params.flCurrentSpeedFraction;
  1981. if ( g_debug_vehiclesound.GetInt() > 1 )
  1982. {
  1983. Msg("CurrentSpeed: %.3f ", flCurrentSpeed );
  1984. }
  1985. // Figure out our speed for the purposes of sound playing.
  1986. // We slow the transition down a little to make the gear changes slower.
  1987. if ( m_vehicleSounds.pGears.Count() > 0 )
  1988. {
  1989. if ( flCurrentSpeed > m_flSpeedPercentage )
  1990. {
  1991. // don't accelerate when the throttle isn't down
  1992. if ( !params.bThrottleDown )
  1993. {
  1994. flCurrentSpeed = m_flSpeedPercentage;
  1995. }
  1996. flCurrentSpeed = Approach( flCurrentSpeed, m_flSpeedPercentage, params.flFrameTime * m_vehicleSounds.pGears[m_iSoundGear].flSpeedApproachFactor );
  1997. }
  1998. }
  1999. m_flSpeedPercentage = clamp( flCurrentSpeed, 0.0f, 1.0f );
  2000. if ( g_debug_vehiclesound.GetInt() > 1 )
  2001. {
  2002. Msg("Sound Speed: %.3f\n", m_flSpeedPercentage );
  2003. }
  2004. // Only do gear changes when the throttle's down
  2005. RecalculateSoundGear( params );
  2006. SoundState_Update( params );
  2007. }
  2008. //-----------------------------------------------------------------------------
  2009. // Purpose: Play a non-gear based vehicle sound
  2010. //-----------------------------------------------------------------------------
  2011. void CBaseServerVehicle::PlaySound( vehiclesound iSound )
  2012. {
  2013. if ( m_vehicleSounds.iszSound[iSound] != NULL_STRING )
  2014. {
  2015. CPASAttenuationFilter filter( m_pVehicle );
  2016. EmitSound_t ep;
  2017. ep.m_nChannel = CHAN_VOICE;
  2018. ep.m_pSoundName = STRING(m_vehicleSounds.iszSound[iSound]);
  2019. ep.m_flVolume = m_flVehicleVolume;
  2020. ep.m_SoundLevel = SNDLVL_NORM;
  2021. CBaseEntity::EmitSound( filter, m_pVehicle->entindex(), ep );
  2022. if ( g_debug_vehiclesound.GetInt() )
  2023. {
  2024. Msg("Playing vehicle sound: %s\n", ep.m_pSoundName );
  2025. }
  2026. }
  2027. }
  2028. //-----------------------------------------------------------------------------
  2029. // Purpose: Stop a non-gear based vehicle sound
  2030. //-----------------------------------------------------------------------------
  2031. void CBaseServerVehicle::StopSound( vehiclesound iSound )
  2032. {
  2033. if ( m_vehicleSounds.iszSound[iSound] != NULL_STRING )
  2034. {
  2035. CBaseEntity::StopSound( m_pVehicle->entindex(), CHAN_VOICE, STRING(m_vehicleSounds.iszSound[iSound]) );
  2036. }
  2037. }
  2038. //-----------------------------------------------------------------------------
  2039. // Purpose: Calculate the gear we should be in based upon the vehicle's current speed
  2040. //-----------------------------------------------------------------------------
  2041. void CBaseServerVehicle::RecalculateSoundGear( vbs_sound_update_t &params )
  2042. {
  2043. int iNumGears = m_vehicleSounds.pGears.Count();
  2044. for ( int i = (iNumGears-1); i >= 0; i-- )
  2045. {
  2046. if ( m_flSpeedPercentage > m_vehicleSounds.pGears[i].flMinSpeed )
  2047. {
  2048. m_iSoundGear = i;
  2049. break;
  2050. }
  2051. }
  2052. // If we're going in reverse, we want to stay in first gear
  2053. if ( params.bReverse )
  2054. {
  2055. m_iSoundGear = 0;
  2056. }
  2057. }
  2058. //---------------------------------------------------------
  2059. //---------------------------------------------------------
  2060. void CBaseServerVehicle::StartEngineRumble()
  2061. {
  2062. return;
  2063. }
  2064. //---------------------------------------------------------
  2065. //---------------------------------------------------------
  2066. void CBaseServerVehicle::StopEngineRumble()
  2067. {
  2068. return;
  2069. }
  2070. //-----------------------------------------------------------------------------
  2071. // Purpose: Find the passenger in the given seat of the vehicle
  2072. // Input : nSeatID - seat ID to check
  2073. // Output : CBaseCombatCharacter - character in the seat
  2074. //-----------------------------------------------------------------------------
  2075. CBaseCombatCharacter *CBaseServerVehicle::NPC_GetPassengerInSeat( int nRoleID, int nSeatID )
  2076. {
  2077. // Search all passengers in the vehicle
  2078. for ( int i = 0; i < m_PassengerInfo.Count(); i++ )
  2079. {
  2080. // If the seat ID matches, return the entity in that seat
  2081. if ( m_PassengerInfo[i].GetSeat() == nSeatID && m_PassengerInfo[i].GetRole() == nRoleID )
  2082. return m_PassengerInfo[i].m_hPassenger;
  2083. }
  2084. return NULL;
  2085. }
  2086. //-----------------------------------------------------------------------------
  2087. // Purpose: Find the first available seat (ranked by priority)
  2088. // Input : nRoleID - Role index
  2089. // Output : int - Seat by index
  2090. //-----------------------------------------------------------------------------
  2091. int CBaseServerVehicle::NPC_GetAvailableSeat_Any( CBaseCombatCharacter *pPassenger, int nRoleID )
  2092. {
  2093. // Look through all available seats
  2094. for ( int i = 0; i < m_PassengerRoles[nRoleID].m_PassengerSeats.Count(); i++ )
  2095. {
  2096. // See if anyone is already in this seat
  2097. CBaseCombatCharacter *pCurrentPassenger = NPC_GetPassengerInSeat( nRoleID, i );
  2098. if ( pCurrentPassenger != NULL && pCurrentPassenger != pPassenger )
  2099. continue;
  2100. // This seat is open
  2101. return i;
  2102. }
  2103. // Nothing found
  2104. return -1;
  2105. }
  2106. //-----------------------------------------------------------------------------
  2107. // Purpose: Find the seat with the nearest entry point to the querier
  2108. // Input : *pPassenger - Terget to be nearest to
  2109. // nRoleID - Role index
  2110. // Output : int - Seat by index
  2111. //-----------------------------------------------------------------------------
  2112. int CBaseServerVehicle::NPC_GetAvailableSeat_Nearest( CBaseCombatCharacter *pPassenger, int nRoleID )
  2113. {
  2114. // Not yet implemented
  2115. Assert( 0 );
  2116. return -1;
  2117. }
  2118. //-----------------------------------------------------------------------------
  2119. // Purpose: Get a seat in the vehicle based on our role and criteria
  2120. // Input : *pPassenger - Entity attempting to find a seat
  2121. // strRoleName - Role the seat must serve
  2122. // nQueryType - Method for choosing the best seat (if multiple)
  2123. // Output : int - Seat by unique ID
  2124. //-----------------------------------------------------------------------------
  2125. int CBaseServerVehicle::NPC_GetAvailableSeat( CBaseCombatCharacter *pPassenger, string_t strRoleName, VehicleSeatQuery_e nQueryType )
  2126. {
  2127. // Parse the vehicle animations the first time they get in the vehicle
  2128. if ( m_bParsedAnimations == false )
  2129. {
  2130. // Load the entry/exit animations from the vehicle
  2131. ParseEntryExitAnims();
  2132. m_bParsedAnimations = true;
  2133. }
  2134. // Get the role index
  2135. int nRole = FindRoleIndexByName( strRoleName );
  2136. if ( m_PassengerRoles.IsValidIndex( nRole ) == false )
  2137. return -1;
  2138. switch( nQueryType )
  2139. {
  2140. case VEHICLE_SEAT_ANY:
  2141. return NPC_GetAvailableSeat_Any( pPassenger, nRole );
  2142. break;
  2143. case VEHICLE_SEAT_NEAREST:
  2144. return NPC_GetAvailableSeat_Nearest( pPassenger, nRole );
  2145. break;
  2146. default:
  2147. Assert( 0 );
  2148. break;
  2149. };
  2150. return -1;
  2151. }
  2152. //-----------------------------------------------------------------------------
  2153. // Purpose: Determine if there's an available seat of a given role name
  2154. // Input : strRoleName - name of the role
  2155. // Output : Returns true on success, false on failure.
  2156. //-----------------------------------------------------------------------------
  2157. bool CBaseServerVehicle::NPC_HasAvailableSeat( string_t strRoleName )
  2158. {
  2159. return ( NPC_GetAvailableSeat( NULL, strRoleName, VEHICLE_SEAT_ANY ) != -1 );
  2160. }
  2161. //-----------------------------------------------------------------------------
  2162. // Purpose: Find a role index by name
  2163. // Input : strRoleName - name of the role
  2164. //-----------------------------------------------------------------------------
  2165. int CBaseServerVehicle::FindRoleIndexByName( string_t strRoleName )
  2166. {
  2167. // Search through all our known roles
  2168. for ( int i = 0; i < m_PassengerRoles.Count(); i++ )
  2169. {
  2170. // Return the index if the name matches
  2171. if ( FStrEq( STRING( m_PassengerRoles[i].GetName() ), STRING( strRoleName ) ) )
  2172. return i;
  2173. }
  2174. return -1;
  2175. }
  2176. //-----------------------------------------------------------------------------
  2177. // Purpose: Find a seat index by its name
  2178. // Input : strSeatName - name of the seat
  2179. //-----------------------------------------------------------------------------
  2180. int CBaseServerVehicle::FindSeatIndexByName( int nRoleIndex, string_t strSeatName )
  2181. {
  2182. // Role must be valid
  2183. if ( m_PassengerRoles.IsValidIndex( nRoleIndex ) == false )
  2184. return -1;
  2185. // Used for attachment polling
  2186. CBaseAnimating *pAnimating = dynamic_cast<CBaseAnimating *>(GetVehicleEnt());
  2187. if ( pAnimating == NULL )
  2188. return -1;
  2189. // Get the index of the named attachment in the model
  2190. int nAttachmentID = pAnimating->LookupAttachment( STRING( strSeatName ) );
  2191. // Look through the roles for this seat attachment ID
  2192. for ( int i = 0; i < m_PassengerRoles[nRoleIndex].m_PassengerSeats.Count(); i++ )
  2193. {
  2194. // Return that index if found
  2195. if ( m_PassengerRoles[nRoleIndex].m_PassengerSeats[i].GetAttachmentID() == nAttachmentID )
  2196. return i;
  2197. }
  2198. return -1;
  2199. }
  2200. //-----------------------------------------------------------------------------
  2201. // Purpose: Called after loading a saved game
  2202. //-----------------------------------------------------------------------------
  2203. void CBaseServerVehicle::RestorePassengerInfo( void )
  2204. {
  2205. // If there is passenger information, then we have passengers in the vehicle
  2206. if ( m_PassengerInfo.Count() != 0 && m_bParsedAnimations == false )
  2207. {
  2208. // Load the entry/exit animations from the vehicle
  2209. ParseEntryExitAnims();
  2210. m_bParsedAnimations = true;
  2211. }
  2212. // FIXME: If a passenger cannot fix-up its indices, it must be removed from the vehicle!
  2213. // Fix-up every passenger with updated indices
  2214. for ( int i = 0; i < m_PassengerInfo.Count(); i++ )
  2215. {
  2216. // Fix up the role first
  2217. int nRoleIndex = FindRoleIndexByName( m_PassengerInfo[i].m_strRoleName );
  2218. if ( m_PassengerRoles.IsValidIndex( nRoleIndex ) )
  2219. {
  2220. // New role index
  2221. m_PassengerInfo[i].m_nRole = nRoleIndex;
  2222. // Then fix up the seat via attachment name
  2223. int nSeatIndex = FindSeatIndexByName( nRoleIndex, m_PassengerInfo[i].m_strSeatName );
  2224. if ( m_PassengerRoles[nRoleIndex].m_PassengerSeats.IsValidIndex( nSeatIndex ) )
  2225. {
  2226. // New seat index
  2227. m_PassengerInfo[i].m_nSeat = nSeatIndex;
  2228. }
  2229. else
  2230. {
  2231. // The seat attachment was not found. This most likely means that the seat attachment name has changed
  2232. // in the target vehicle and the NPC passenger is now stranded!
  2233. Assert( 0 );
  2234. }
  2235. }
  2236. else
  2237. {
  2238. // The role was not found. This most likely means that the role names have changed
  2239. // in the target vehicle and the NPC passenger is now stranded!
  2240. Assert( 0 );
  2241. }
  2242. }
  2243. }
  2244. void CBaseServerVehicle::ReloadScript()
  2245. {
  2246. if ( m_pDrivableVehicle )
  2247. {
  2248. string_t script = m_pDrivableVehicle->GetVehicleScriptName();
  2249. IPhysicsVehicleController *pController = GetVehicleController();
  2250. vehicleparams_t *pVehicleParams = pController ? &(pController->GetVehicleParamsForChange()) : NULL;
  2251. PhysFindOrAddVehicleScript( script.ToCStr(), pVehicleParams, &m_vehicleSounds );
  2252. if ( pController )
  2253. {
  2254. pController->VehicleDataReload();
  2255. }
  2256. }
  2257. }
  2258. //-----------------------------------------------------------------------------
  2259. // Purpose: Passes this call down into the server vehicle where the tests are done
  2260. //-----------------------------------------------------------------------------
  2261. bool CBaseServerVehicle::PassengerShouldReceiveDamage( CTakeDamageInfo &info )
  2262. {
  2263. if ( GetDrivableVehicle() )
  2264. return GetDrivableVehicle()->PassengerShouldReceiveDamage( info );
  2265. return true;
  2266. }
  2267. //===========================================================================================================
  2268. // Vehicle Sounds
  2269. //===========================================================================================================
  2270. // These are sounds that are to be automatically stopped whenever the vehicle's driver leaves it
  2271. vehiclesound g_iSoundsToStopOnExit[] =
  2272. {
  2273. VS_ENGINE2_START,
  2274. VS_ENGINE2_STOP,
  2275. };
  2276. char *vehiclesound_parsenames[VS_NUM_SOUNDS] =
  2277. {
  2278. "skid_lowfriction",
  2279. "skid_normalfriction",
  2280. "skid_highfriction",
  2281. "engine2_start",
  2282. "engine2_stop",
  2283. "misc1",
  2284. "misc2",
  2285. "misc3",
  2286. "misc4",
  2287. };
  2288. CVehicleSoundsParser::CVehicleSoundsParser( void )
  2289. {
  2290. // UNDONE: Revisit this pattern - move sub-block processing ideas into the parser architecture
  2291. m_iCurrentGear = -1;
  2292. m_iCurrentState = -1;
  2293. m_iCurrentCrashSound = -1;
  2294. }
  2295. //-----------------------------------------------------------------------------
  2296. // Purpose:
  2297. //-----------------------------------------------------------------------------
  2298. void CVehicleSoundsParser::ParseKeyValue( void *pData, const char *pKey, const char *pValue )
  2299. {
  2300. vehiclesounds_t *pSounds = (vehiclesounds_t *)pData;
  2301. // New gear?
  2302. if ( !strcmpi( pKey, "gear" ) )
  2303. {
  2304. // Create, initialize, and add a new gear to our list
  2305. int iNewGear = pSounds->pGears.AddToTail();
  2306. pSounds->pGears[iNewGear].flMaxSpeed = 0;
  2307. pSounds->pGears[iNewGear].flSpeedApproachFactor = 1.0;
  2308. // Set our min speed to the previous gear's max
  2309. if ( iNewGear == 0 )
  2310. {
  2311. // First gear, so our minspeed is 0
  2312. pSounds->pGears[iNewGear].flMinSpeed = 0;
  2313. }
  2314. else
  2315. {
  2316. pSounds->pGears[iNewGear].flMinSpeed = pSounds->pGears[iNewGear-1].flMaxSpeed;
  2317. }
  2318. // Remember which gear we're reading data from
  2319. m_iCurrentGear = iNewGear;
  2320. }
  2321. else if ( !strcmpi( pKey, "state" ) )
  2322. {
  2323. m_iCurrentState = 0;
  2324. }
  2325. else if ( !strcmpi( pKey, "crashsound" ) )
  2326. {
  2327. m_iCurrentCrashSound = pSounds->crashSounds.AddToTail();
  2328. pSounds->crashSounds[m_iCurrentCrashSound].flMinSpeed = 0;
  2329. pSounds->crashSounds[m_iCurrentCrashSound].flMinDeltaSpeed = 0;
  2330. pSounds->crashSounds[m_iCurrentCrashSound].iszCrashSound = NULL_STRING;
  2331. }
  2332. else
  2333. {
  2334. int i;
  2335. // Are we currently in a gear block?
  2336. if ( m_iCurrentGear >= 0 )
  2337. {
  2338. Assert( m_iCurrentGear < pSounds->pGears.Count() );
  2339. // Check gear keys
  2340. if ( !strcmpi( pKey, "max_speed" ) )
  2341. {
  2342. pSounds->pGears[m_iCurrentGear].flMaxSpeed = atof(pValue);
  2343. return;
  2344. }
  2345. if ( !strcmpi( pKey, "speed_approach_factor" ) )
  2346. {
  2347. pSounds->pGears[m_iCurrentGear].flSpeedApproachFactor = atof(pValue);
  2348. return;
  2349. }
  2350. }
  2351. // We're done reading a gear, so stop checking them.
  2352. m_iCurrentGear = -1;
  2353. if ( m_iCurrentState >= 0 )
  2354. {
  2355. if ( !strcmpi( pKey, "name" ) )
  2356. {
  2357. m_iCurrentState = SoundStateIndexFromName( pValue );
  2358. pSounds->iszStateSounds[m_iCurrentState] = NULL_STRING;
  2359. pSounds->minStateTime[m_iCurrentState] = 0.0f;
  2360. return;
  2361. }
  2362. else if ( !strcmpi( pKey, "sound" ) )
  2363. {
  2364. pSounds->iszStateSounds[m_iCurrentState] = AllocPooledString(pValue);
  2365. return;
  2366. }
  2367. else if ( !strcmpi( pKey, "min_time" ) )
  2368. {
  2369. pSounds->minStateTime[m_iCurrentState] = atof(pValue);
  2370. return;
  2371. }
  2372. }
  2373. //
  2374. m_iCurrentState = -1;
  2375. if ( m_iCurrentCrashSound >= 0 )
  2376. {
  2377. if ( !strcmpi( pKey, "min_speed" ) )
  2378. {
  2379. pSounds->crashSounds[m_iCurrentCrashSound].flMinSpeed = atof(pValue);
  2380. return;
  2381. }
  2382. else if ( !strcmpi( pKey, "sound" ) )
  2383. {
  2384. pSounds->crashSounds[m_iCurrentCrashSound].iszCrashSound = AllocPooledString(pValue);
  2385. return;
  2386. }
  2387. else if ( !strcmpi( pKey, "min_speed_change" ) )
  2388. {
  2389. pSounds->crashSounds[m_iCurrentCrashSound].flMinDeltaSpeed = atof(pValue);
  2390. return;
  2391. }
  2392. else if ( !strcmpi( pKey, "gear_limit" ) )
  2393. {
  2394. pSounds->crashSounds[m_iCurrentCrashSound].gearLimit = atoi(pValue);
  2395. return;
  2396. }
  2397. }
  2398. m_iCurrentCrashSound = -1;
  2399. for ( i = 0; i < VS_NUM_SOUNDS; i++ )
  2400. {
  2401. if ( !strcmpi( pKey, vehiclesound_parsenames[i] ) )
  2402. {
  2403. pSounds->iszSound[i] = AllocPooledString(pValue);
  2404. return;
  2405. }
  2406. }
  2407. }
  2408. }
  2409. //-----------------------------------------------------------------------------
  2410. // Purpose:
  2411. //-----------------------------------------------------------------------------
  2412. void CVehicleSoundsParser::SetDefaults( void *pData )
  2413. {
  2414. vehiclesounds_t *pSounds = (vehiclesounds_t *)pData;
  2415. pSounds->Init();
  2416. }