Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2747 lines
83 KiB

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