Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1458 lines
49 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: A moving vehicle that is used as a battering ram
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "fourwheelvehiclephysics.h"
  9. #include "engine/IEngineSound.h"
  10. #include "soundenvelope.h"
  11. #include "in_buttons.h"
  12. #include "player.h"
  13. #include "IEffects.h"
  14. #include "physics_saverestore.h"
  15. #include "vehicle_base.h"
  16. #include "isaverestore.h"
  17. #include "movevars_shared.h"
  18. #include "te_effect_dispatch.h"
  19. #include "particle_parse.h"
  20. // memdbgon must be the last include file in a .cpp file!!!
  21. #include "tier0/memdbgon.h"
  22. #define STICK_EXTENTS 400.0f
  23. #define DUST_SPEED 5 // speed at which dust starts
  24. #define REAR_AXLE 1 // indexes of axlex
  25. #define FRONT_AXLE 0
  26. #define MAX_GUAGE_SPEED 100.0 // 100 mph is max speed shown on guage
  27. #define BRAKE_MAX_VALUE 1.0f
  28. #define BRAKE_BACK_FORWARD_SCALAR 2.0f
  29. ConVar r_vehicleBrakeRate( "r_vehicleBrakeRate", "1.5", FCVAR_CHEAT );
  30. ConVar xbox_throttlebias("xbox_throttlebias", "100", FCVAR_ARCHIVE );
  31. ConVar xbox_throttlespoof("xbox_throttlespoof", "200", FCVAR_ARCHIVE );
  32. ConVar xbox_autothrottle("xbox_autothrottle", "1", FCVAR_ARCHIVE );
  33. ConVar xbox_steering_deadzone( "xbox_steering_deadzone", "0.0" );
  34. // remaps an angular variable to a 3 band function:
  35. // 0 <= t < start : f(t) = 0
  36. // start <= t <= end : f(t) = end * spline(( t-start) / (end-start) ) // s curve between clamped and linear
  37. // end < t : f(t) = t
  38. float RemapAngleRange( float startInterval, float endInterval, float value )
  39. {
  40. // Fixup the roll
  41. value = AngleNormalize( value );
  42. float absAngle = fabs(value);
  43. // beneath cutoff?
  44. if ( absAngle < startInterval )
  45. {
  46. value = 0;
  47. }
  48. // in spline range?
  49. else if ( absAngle <= endInterval )
  50. {
  51. float newAngle = SimpleSpline( (absAngle - startInterval) / (endInterval-startInterval) ) * endInterval;
  52. // grab the sign from the initial value
  53. if ( value < 0 )
  54. {
  55. newAngle *= -1;
  56. }
  57. value = newAngle;
  58. }
  59. // else leave it alone, in linear range
  60. return value;
  61. }
  62. enum vehicle_pose_params
  63. {
  64. VEH_FL_WHEEL_HEIGHT=0,
  65. VEH_FR_WHEEL_HEIGHT,
  66. VEH_RL_WHEEL_HEIGHT,
  67. VEH_RR_WHEEL_HEIGHT,
  68. VEH_FL_WHEEL_SPIN,
  69. VEH_FR_WHEEL_SPIN,
  70. VEH_RL_WHEEL_SPIN,
  71. VEH_RR_WHEEL_SPIN,
  72. VEH_STEER,
  73. VEH_ACTION,
  74. VEH_SPEEDO,
  75. };
  76. BEGIN_DATADESC_NO_BASE( CFourWheelVehiclePhysics )
  77. // These two are reset every time
  78. // DEFINE_FIELD( m_pOuter, FIELD_EHANDLE ),
  79. // m_pOuterServerVehicle;
  80. // Quiet down classcheck
  81. // DEFINE_FIELD( m_controls, vehicle_controlparams_t ),
  82. // Controls
  83. DEFINE_FIELD( m_controls.throttle, FIELD_FLOAT ),
  84. DEFINE_FIELD( m_controls.steering, FIELD_FLOAT ),
  85. DEFINE_FIELD( m_controls.brake, FIELD_FLOAT ),
  86. DEFINE_FIELD( m_controls.boost, FIELD_FLOAT ),
  87. DEFINE_FIELD( m_controls.handbrake, FIELD_BOOLEAN ),
  88. DEFINE_FIELD( m_controls.handbrakeLeft, FIELD_BOOLEAN ),
  89. DEFINE_FIELD( m_controls.handbrakeRight, FIELD_BOOLEAN ),
  90. DEFINE_FIELD( m_controls.brakepedal, FIELD_BOOLEAN ),
  91. DEFINE_FIELD( m_controls.bHasBrakePedal, FIELD_BOOLEAN ),
  92. // This has to be handled by the containing class owing to 'owner' issues
  93. // DEFINE_PHYSPTR( m_pVehicle ),
  94. DEFINE_FIELD( m_nSpeed, FIELD_INTEGER ),
  95. DEFINE_FIELD( m_nLastSpeed, FIELD_INTEGER ),
  96. DEFINE_FIELD( m_nRPM, FIELD_INTEGER ),
  97. DEFINE_FIELD( m_fLastBoost, FIELD_FLOAT ),
  98. DEFINE_FIELD( m_nBoostTimeLeft, FIELD_INTEGER ),
  99. DEFINE_FIELD( m_nHasBoost, FIELD_INTEGER ),
  100. DEFINE_FIELD( m_maxThrottle, FIELD_FLOAT ),
  101. DEFINE_FIELD( m_flMaxRevThrottle, FIELD_FLOAT ),
  102. DEFINE_FIELD( m_flMaxSpeed, FIELD_FLOAT ),
  103. DEFINE_FIELD( m_actionSpeed, FIELD_FLOAT ),
  104. // This has to be handled by the containing class owing to 'owner' issues
  105. // DEFINE_PHYSPTR_ARRAY( m_pWheels ),
  106. DEFINE_FIELD( m_wheelCount, FIELD_INTEGER ),
  107. DEFINE_ARRAY( m_wheelPosition, FIELD_VECTOR, 4 ),
  108. DEFINE_ARRAY( m_wheelRotation, FIELD_VECTOR, 4 ),
  109. DEFINE_ARRAY( m_wheelBaseHeight, FIELD_FLOAT, 4 ),
  110. DEFINE_ARRAY( m_wheelTotalHeight, FIELD_FLOAT, 4 ),
  111. DEFINE_ARRAY( m_poseParameters, FIELD_INTEGER, 12 ),
  112. DEFINE_FIELD( m_actionValue, FIELD_FLOAT ),
  113. DEFINE_KEYFIELD( m_actionScale, FIELD_FLOAT, "actionScale" ),
  114. DEFINE_FIELD( m_debugRadius, FIELD_FLOAT ),
  115. DEFINE_FIELD( m_throttleRate, FIELD_FLOAT ),
  116. DEFINE_FIELD( m_throttleStartTime, FIELD_FLOAT ),
  117. DEFINE_FIELD( m_throttleActiveTime, FIELD_FLOAT ),
  118. DEFINE_FIELD( m_turboTimer, FIELD_FLOAT ),
  119. DEFINE_FIELD( m_flVehicleVolume, FIELD_FLOAT ),
  120. DEFINE_FIELD( m_bIsOn, FIELD_BOOLEAN ),
  121. DEFINE_FIELD( m_bLastThrottle, FIELD_BOOLEAN ),
  122. DEFINE_FIELD( m_bLastBoost, FIELD_BOOLEAN ),
  123. DEFINE_FIELD( m_bLastSkid, FIELD_BOOLEAN ),
  124. END_DATADESC()
  125. //-----------------------------------------------------------------------------
  126. // Constructor
  127. //-----------------------------------------------------------------------------
  128. CFourWheelVehiclePhysics::CFourWheelVehiclePhysics( CBaseAnimating *pOuter )
  129. {
  130. m_flVehicleVolume = 0.5;
  131. m_pOuter = NULL;
  132. m_pOuterServerVehicle = NULL;
  133. m_flMaxSpeed = 30;
  134. }
  135. //-----------------------------------------------------------------------------
  136. // Destructor
  137. //-----------------------------------------------------------------------------
  138. CFourWheelVehiclePhysics::~CFourWheelVehiclePhysics ()
  139. {
  140. physenv->DestroyVehicleController( m_pVehicle );
  141. }
  142. //-----------------------------------------------------------------------------
  143. // A couple wrapper methods to perform common operations
  144. //-----------------------------------------------------------------------------
  145. inline int CFourWheelVehiclePhysics::LookupPoseParameter( const char *szName )
  146. {
  147. return m_pOuter->LookupPoseParameter( szName );
  148. }
  149. inline float CFourWheelVehiclePhysics::GetPoseParameter( int iParameter )
  150. {
  151. return m_pOuter->GetPoseParameter( iParameter );
  152. }
  153. inline float CFourWheelVehiclePhysics::SetPoseParameter( int iParameter, float flValue )
  154. {
  155. Assert(IsFinite(flValue));
  156. return m_pOuter->SetPoseParameter( iParameter, flValue );
  157. }
  158. inline bool CFourWheelVehiclePhysics::GetAttachment( const char *szName, Vector &origin, QAngle &angles )
  159. {
  160. return m_pOuter->GetAttachment( szName, origin, angles );
  161. }
  162. //-----------------------------------------------------------------------------
  163. // Methods related to spawn
  164. //-----------------------------------------------------------------------------
  165. void CFourWheelVehiclePhysics::InitializePoseParameters()
  166. {
  167. m_poseParameters[VEH_FL_WHEEL_HEIGHT] = LookupPoseParameter( "vehicle_wheel_fl_height" );
  168. m_poseParameters[VEH_FR_WHEEL_HEIGHT] = LookupPoseParameter( "vehicle_wheel_fr_height" );
  169. m_poseParameters[VEH_RL_WHEEL_HEIGHT] = LookupPoseParameter( "vehicle_wheel_rl_height" );
  170. m_poseParameters[VEH_RR_WHEEL_HEIGHT] = LookupPoseParameter( "vehicle_wheel_rr_height" );
  171. m_poseParameters[VEH_FL_WHEEL_SPIN] = LookupPoseParameter( "vehicle_wheel_fl_spin" );
  172. m_poseParameters[VEH_FR_WHEEL_SPIN] = LookupPoseParameter( "vehicle_wheel_fr_spin" );
  173. m_poseParameters[VEH_RL_WHEEL_SPIN] = LookupPoseParameter( "vehicle_wheel_rl_spin" );
  174. m_poseParameters[VEH_RR_WHEEL_SPIN] = LookupPoseParameter( "vehicle_wheel_rr_spin" );
  175. m_poseParameters[VEH_STEER] = LookupPoseParameter( "vehicle_steer" );
  176. m_poseParameters[VEH_ACTION] = LookupPoseParameter( "vehicle_action" );
  177. m_poseParameters[VEH_SPEEDO] = LookupPoseParameter( "vehicle_guage" );
  178. // move the wheels to a neutral position
  179. SetPoseParameter( m_poseParameters[VEH_SPEEDO], 0 );
  180. SetPoseParameter( m_poseParameters[VEH_STEER], 0 );
  181. SetPoseParameter( m_poseParameters[VEH_FL_WHEEL_HEIGHT], 0 );
  182. SetPoseParameter( m_poseParameters[VEH_FR_WHEEL_HEIGHT], 0 );
  183. SetPoseParameter( m_poseParameters[VEH_RL_WHEEL_HEIGHT], 0 );
  184. SetPoseParameter( m_poseParameters[VEH_RR_WHEEL_HEIGHT], 0 );
  185. m_pOuter->InvalidateBoneCache();
  186. }
  187. //-----------------------------------------------------------------------------
  188. // Purpose: Parses the vehicle's script
  189. //-----------------------------------------------------------------------------
  190. bool CFourWheelVehiclePhysics::ParseVehicleScript( const char *pScriptName, solid_t &solid, vehicleparams_t &vehicle)
  191. {
  192. // Physics keeps a cache of these to share among spawns of vehicles or flush for debugging
  193. PhysFindOrAddVehicleScript( pScriptName, &vehicle, NULL );
  194. m_debugRadius = vehicle.axles[0].wheels.radius;
  195. CalcWheelData( vehicle );
  196. PhysModelParseSolid( solid, m_pOuter, m_pOuter->GetModelIndex() );
  197. // Allow the script to shift the center of mass
  198. if ( vehicle.body.massCenterOverride != vec3_origin )
  199. {
  200. solid.massCenterOverride = vehicle.body.massCenterOverride;
  201. solid.params.massCenterOverride = &solid.massCenterOverride;
  202. }
  203. // allow script to change the mass of the vehicle body
  204. if ( vehicle.body.massOverride > 0 )
  205. {
  206. solid.params.mass = vehicle.body.massOverride;
  207. }
  208. return true;
  209. }
  210. void CFourWheelVehiclePhysics::CalcWheelData( vehicleparams_t &vehicle )
  211. {
  212. const char *pWheelAttachments[4] = { "wheel_fl", "wheel_fr", "wheel_rl", "wheel_rr" };
  213. Vector left, right;
  214. QAngle dummy;
  215. SetPoseParameter( m_poseParameters[VEH_FL_WHEEL_HEIGHT], 0 );
  216. SetPoseParameter( m_poseParameters[VEH_FR_WHEEL_HEIGHT], 0 );
  217. SetPoseParameter( m_poseParameters[VEH_RL_WHEEL_HEIGHT], 0 );
  218. SetPoseParameter( m_poseParameters[VEH_RR_WHEEL_HEIGHT], 0 );
  219. m_pOuter->InvalidateBoneCache();
  220. if ( GetAttachment( "wheel_fl", left, dummy ) && GetAttachment( "wheel_fr", right, dummy ) )
  221. {
  222. VectorITransform( left, m_pOuter->EntityToWorldTransform(), left );
  223. VectorITransform( right, m_pOuter->EntityToWorldTransform(), right );
  224. Vector center = (left + right) * 0.5;
  225. vehicle.axles[0].offset = center;
  226. vehicle.axles[0].wheelOffset = right - center;
  227. // Cache the base height of the wheels in body space
  228. m_wheelBaseHeight[0] = left.z;
  229. m_wheelBaseHeight[1] = right.z;
  230. }
  231. if ( GetAttachment( "wheel_rl", left, dummy ) && GetAttachment( "wheel_rr", right, dummy ) )
  232. {
  233. VectorITransform( left, m_pOuter->EntityToWorldTransform(), left );
  234. VectorITransform( right, m_pOuter->EntityToWorldTransform(), right );
  235. Vector center = (left + right) * 0.5;
  236. vehicle.axles[1].offset = center;
  237. vehicle.axles[1].wheelOffset = right - center;
  238. // Cache the base height of the wheels in body space
  239. m_wheelBaseHeight[2] = left.z;
  240. m_wheelBaseHeight[3] = right.z;
  241. }
  242. SetPoseParameter( m_poseParameters[VEH_FL_WHEEL_HEIGHT], 1 );
  243. SetPoseParameter( m_poseParameters[VEH_FR_WHEEL_HEIGHT], 1 );
  244. SetPoseParameter( m_poseParameters[VEH_RL_WHEEL_HEIGHT], 1 );
  245. SetPoseParameter( m_poseParameters[VEH_RR_WHEEL_HEIGHT], 1 );
  246. m_pOuter->InvalidateBoneCache();
  247. if ( GetAttachment( "wheel_fl", left, dummy ) && GetAttachment( "wheel_fr", right, dummy ) )
  248. {
  249. VectorITransform( left, m_pOuter->EntityToWorldTransform(), left );
  250. VectorITransform( right, m_pOuter->EntityToWorldTransform(), right );
  251. // Cache the height range of the wheels in body space
  252. m_wheelTotalHeight[0] = m_wheelBaseHeight[0] - left.z;
  253. m_wheelTotalHeight[1] = m_wheelBaseHeight[1] - right.z;
  254. vehicle.axles[0].wheels.springAdditionalLength = m_wheelTotalHeight[0];
  255. }
  256. if ( GetAttachment( "wheel_rl", left, dummy ) && GetAttachment( "wheel_rr", right, dummy ) )
  257. {
  258. VectorITransform( left, m_pOuter->EntityToWorldTransform(), left );
  259. VectorITransform( right, m_pOuter->EntityToWorldTransform(), right );
  260. // Cache the height range of the wheels in body space
  261. m_wheelTotalHeight[2] = m_wheelBaseHeight[0] - left.z;
  262. m_wheelTotalHeight[3] = m_wheelBaseHeight[1] - right.z;
  263. vehicle.axles[1].wheels.springAdditionalLength = m_wheelTotalHeight[2];
  264. }
  265. for ( int i = 0; i < 4; i++ )
  266. {
  267. if ( m_wheelTotalHeight[i] == 0.0f )
  268. {
  269. DevWarning("Vehicle %s has invalid wheel attachment for %s - no movement\n", STRING(m_pOuter->GetModelName()), pWheelAttachments[i]);
  270. m_wheelTotalHeight[i] = 1.0f;
  271. }
  272. }
  273. SetPoseParameter( m_poseParameters[VEH_FL_WHEEL_HEIGHT], 0 );
  274. SetPoseParameter( m_poseParameters[VEH_FR_WHEEL_HEIGHT], 0 );
  275. SetPoseParameter( m_poseParameters[VEH_RL_WHEEL_HEIGHT], 0 );
  276. SetPoseParameter( m_poseParameters[VEH_RR_WHEEL_HEIGHT], 0 );
  277. m_pOuter->InvalidateBoneCache();
  278. // Get raytrace offsets if they exist.
  279. if ( GetAttachment( "raytrace_fl", left, dummy ) && GetAttachment( "raytrace_fr", right, dummy ) )
  280. {
  281. VectorITransform( left, m_pOuter->EntityToWorldTransform(), left );
  282. VectorITransform( right, m_pOuter->EntityToWorldTransform(), right );
  283. Vector center = ( left + right ) * 0.5;
  284. vehicle.axles[0].raytraceCenterOffset = center;
  285. vehicle.axles[0].raytraceOffset = right - center;
  286. }
  287. if ( GetAttachment( "raytrace_rl", left, dummy ) && GetAttachment( "raytrace_rr", right, dummy ) )
  288. {
  289. VectorITransform( left, m_pOuter->EntityToWorldTransform(), left );
  290. VectorITransform( right, m_pOuter->EntityToWorldTransform(), right );
  291. Vector center = ( left + right ) * 0.5;
  292. vehicle.axles[1].raytraceCenterOffset = center;
  293. vehicle.axles[1].raytraceOffset = right - center;
  294. }
  295. }
  296. //-----------------------------------------------------------------------------
  297. // Precaches the effect
  298. //-----------------------------------------------------------------------------
  299. void CFourWheelVehiclePhysics::Precache()
  300. {
  301. PrecacheEffect( "WheelDust" );
  302. }
  303. //-----------------------------------------------------------------------------
  304. // Spawns the vehicle
  305. //-----------------------------------------------------------------------------
  306. void CFourWheelVehiclePhysics::Spawn( )
  307. {
  308. Assert( m_pOuter );
  309. m_actionValue = 0;
  310. m_actionSpeed = 0;
  311. m_bIsOn = false;
  312. m_controls.handbrake = false;
  313. m_controls.handbrakeLeft = false;
  314. m_controls.handbrakeRight = false;
  315. m_controls.bHasBrakePedal = true;
  316. m_controls.bAnalogSteering = false;
  317. SetMaxThrottle( 1.0 );
  318. SetMaxReverseThrottle( -1.0f );
  319. InitializePoseParameters();
  320. }
  321. //-----------------------------------------------------------------------------
  322. // Purpose: Initializes the vehicle physics
  323. // Called by our outer vehicle in it's Spawn()
  324. //-----------------------------------------------------------------------------
  325. bool CFourWheelVehiclePhysics::Initialize( const char *pVehicleScript, unsigned int nVehicleType )
  326. {
  327. // Ok, turn on the simulation now
  328. // FIXME: Disabling collisions here is necessary because we seem to be
  329. // getting a one-frame collision between the old + new collision models
  330. if ( m_pOuter->VPhysicsGetObject() )
  331. {
  332. m_pOuter->VPhysicsGetObject()->EnableCollisions(false);
  333. }
  334. m_pOuter->VPhysicsDestroyObject();
  335. // Create the vphysics model + teleport it into position
  336. solid_t solid;
  337. vehicleparams_t vehicle;
  338. if (!ParseVehicleScript( pVehicleScript, solid, vehicle ))
  339. {
  340. UTIL_Remove(m_pOuter);
  341. return false;
  342. }
  343. // NOTE: this needs to be greater than your max framerate (so zero is still instant)
  344. m_throttleRate = 10000.0;
  345. if ( vehicle.engine.throttleTime > 0 )
  346. {
  347. m_throttleRate = 1.0 / vehicle.engine.throttleTime;
  348. }
  349. m_flMaxSpeed = vehicle.engine.maxSpeed;
  350. IPhysicsObject *pBody = m_pOuter->VPhysicsInitNormal( SOLID_VPHYSICS, 0, false, &solid );
  351. PhysSetGameFlags( pBody, FVPHYSICS_NO_SELF_COLLISIONS | FVPHYSICS_MULTIOBJECT_ENTITY );
  352. m_pVehicle = physenv->CreateVehicleController( pBody, vehicle, nVehicleType, physgametrace );
  353. m_wheelCount = m_pVehicle->GetWheelCount();
  354. for ( int i = 0; i < m_wheelCount; i++ )
  355. {
  356. m_pWheels[i] = m_pVehicle->GetWheel( i );
  357. }
  358. return true;
  359. }
  360. //-----------------------------------------------------------------------------
  361. // Various steering parameters
  362. //-----------------------------------------------------------------------------
  363. void CFourWheelVehiclePhysics::SetThrottle( float flThrottle )
  364. {
  365. m_controls.throttle = flThrottle;
  366. }
  367. //-----------------------------------------------------------------------------
  368. // Purpose:
  369. //-----------------------------------------------------------------------------
  370. void CFourWheelVehiclePhysics::SetMaxThrottle( float flMaxThrottle )
  371. {
  372. m_maxThrottle = flMaxThrottle;
  373. }
  374. //-----------------------------------------------------------------------------
  375. // Purpose:
  376. //-----------------------------------------------------------------------------
  377. void CFourWheelVehiclePhysics::SetMaxReverseThrottle( float flMaxThrottle )
  378. {
  379. m_flMaxRevThrottle = flMaxThrottle;
  380. }
  381. //-----------------------------------------------------------------------------
  382. // Purpose:
  383. //-----------------------------------------------------------------------------
  384. void CFourWheelVehiclePhysics::SetSteering( float flSteering, float flSteeringRate )
  385. {
  386. if ( !flSteeringRate )
  387. {
  388. m_controls.steering = flSteering;
  389. }
  390. else
  391. {
  392. m_controls.steering = Approach( flSteering, m_controls.steering, flSteeringRate );
  393. }
  394. }
  395. //-----------------------------------------------------------------------------
  396. // Purpose:
  397. //-----------------------------------------------------------------------------
  398. void CFourWheelVehiclePhysics::SetSteeringDegrees( float flDegrees )
  399. {
  400. vehicleparams_t &vehicleParams = m_pVehicle->GetVehicleParamsForChange();
  401. vehicleParams.steering.degreesSlow = flDegrees;
  402. vehicleParams.steering.degreesFast = flDegrees;
  403. }
  404. //-----------------------------------------------------------------------------
  405. // Purpose:
  406. //-----------------------------------------------------------------------------
  407. void CFourWheelVehiclePhysics::SetAction( float flAction )
  408. {
  409. m_actionSpeed = flAction;
  410. }
  411. //-----------------------------------------------------------------------------
  412. // Purpose:
  413. //-----------------------------------------------------------------------------
  414. void CFourWheelVehiclePhysics::TurnOn( )
  415. {
  416. if ( IsEngineDisabled() )
  417. return;
  418. if ( !m_bIsOn )
  419. {
  420. m_pOuterServerVehicle->SoundStart();
  421. m_bIsOn = true;
  422. }
  423. }
  424. //-----------------------------------------------------------------------------
  425. // Purpose:
  426. //-----------------------------------------------------------------------------
  427. void CFourWheelVehiclePhysics::TurnOff( )
  428. {
  429. ResetControls();
  430. if ( m_bIsOn )
  431. {
  432. m_pOuterServerVehicle->SoundShutdown();
  433. m_bIsOn = false;
  434. }
  435. }
  436. //-----------------------------------------------------------------------------
  437. // Purpose:
  438. //-----------------------------------------------------------------------------
  439. void CFourWheelVehiclePhysics::SetBoost( float flBoost )
  440. {
  441. if ( !IsEngineDisabled() )
  442. {
  443. m_controls.boost = flBoost;
  444. }
  445. }
  446. //------------------------------------------------------
  447. // UpdateBooster - Calls UpdateBooster() in the vphysics
  448. // code to allow the timer to be updated
  449. //
  450. // Returns: false if timer has expired (can use again and
  451. // can stop think
  452. // true if timer still running
  453. //------------------------------------------------------
  454. bool CFourWheelVehiclePhysics::UpdateBooster( void )
  455. {
  456. float retval = m_pVehicle->UpdateBooster(gpGlobals->frametime );
  457. return ( retval > 0 );
  458. }
  459. //-----------------------------------------------------------------------------
  460. // Purpose:
  461. //-----------------------------------------------------------------------------
  462. void CFourWheelVehiclePhysics::SetHasBrakePedal( bool bHasBrakePedal )
  463. {
  464. m_controls.bHasBrakePedal = bHasBrakePedal;
  465. }
  466. //-----------------------------------------------------------------------------
  467. // Teleport
  468. //-----------------------------------------------------------------------------
  469. void CFourWheelVehiclePhysics::Teleport( matrix3x4_t& relativeTransform )
  470. {
  471. // We basically just have to make sure the wheels are in the right place
  472. // after teleportation occurs
  473. for ( int i = 0; i < m_wheelCount; i++ )
  474. {
  475. matrix3x4_t matrix, newMatrix;
  476. m_pWheels[i]->GetPositionMatrix( &matrix );
  477. ConcatTransforms( relativeTransform, matrix, newMatrix );
  478. m_pWheels[i]->SetPositionMatrix( newMatrix, true );
  479. }
  480. // Wake the vehicle back up after a teleport
  481. if ( m_pOuterServerVehicle && m_pOuterServerVehicle->GetFourWheelVehicle() )
  482. {
  483. IPhysicsObject *pObj = m_pOuterServerVehicle->GetFourWheelVehicle()->VPhysicsGetObject();
  484. if ( pObj )
  485. {
  486. pObj->Wake();
  487. }
  488. }
  489. }
  490. #if 1
  491. // For the #if 0 debug code below!
  492. #define HL2IVP_FACTOR METERS_PER_INCH
  493. #define IVP2HL(x) (float)(x * (1.0f/HL2IVP_FACTOR))
  494. #define HL2IVP(x) (double)(x * HL2IVP_FACTOR)
  495. #endif
  496. //-----------------------------------------------------------------------------
  497. // Debugging methods
  498. //-----------------------------------------------------------------------------
  499. void CFourWheelVehiclePhysics::DrawDebugGeometryOverlays()
  500. {
  501. for ( int iWheel = 0; iWheel < m_wheelCount; iWheel++ )
  502. {
  503. IPhysicsObject *pWheel = m_pVehicle->GetWheel( iWheel );
  504. float radius = pWheel->GetSphereRadius();
  505. Vector vecPos;
  506. QAngle vecRot;
  507. pWheel->GetPosition( &vecPos, &vecRot );
  508. // draw the physics object position/orientation
  509. NDebugOverlay::Sphere( vecPos, vecRot, radius, 0, 255, 0, 0, false, 0 );
  510. // draw the animation position/orientation
  511. NDebugOverlay::Sphere(m_wheelPosition[iWheel], m_wheelRotation[iWheel], radius, 255, 255, 0, 0, false, 0);
  512. }
  513. // Render vehicle data.
  514. IPhysicsObject *pBody = m_pOuter->VPhysicsGetObject();
  515. if ( pBody )
  516. {
  517. const vehicleparams_t vehicleParams = m_pVehicle->GetVehicleParams();
  518. // Draw a red cube as the "center" of the vehicle.
  519. Vector vecBodyPosition;
  520. QAngle angBodyDirection;
  521. pBody->GetPosition( &vecBodyPosition, &angBodyDirection );
  522. NDebugOverlay::BoxAngles( vecBodyPosition, Vector( -5, -5, -5 ), Vector( 5, 5, 5 ), angBodyDirection, 255, 0, 0, 0 ,0 );
  523. matrix3x4_t matrix;
  524. AngleMatrix( angBodyDirection, vecBodyPosition, matrix );
  525. // Draw green cubes at axle centers.
  526. Vector vecAxlePositions[2], vecAxlePositionsHL[2];
  527. vecAxlePositions[0] = vehicleParams.axles[0].offset;
  528. vecAxlePositions[1] = vehicleParams.axles[1].offset;
  529. VectorTransform( vecAxlePositions[0], matrix, vecAxlePositionsHL[0] );
  530. VectorTransform( vecAxlePositions[1], matrix, vecAxlePositionsHL[1] );
  531. NDebugOverlay::BoxAngles( vecAxlePositionsHL[0], Vector( -3, -3, -3 ), Vector( 3, 3, 3 ), angBodyDirection, 0, 255, 0, 0 ,0 );
  532. NDebugOverlay::BoxAngles( vecAxlePositionsHL[1], Vector( -3, -3, -3 ), Vector( 3, 3, 3 ), angBodyDirection, 0, 255, 0, 0 ,0 );
  533. // Draw wheel raycasts in yellow
  534. vehicle_debugcarsystem_t debugCarSystem;
  535. m_pVehicle->GetCarSystemDebugData( debugCarSystem );
  536. for ( int iWheel = 0; iWheel < 4; ++iWheel )
  537. {
  538. Vector vecStart, vecEnd, vecImpact;
  539. // Hack for now.
  540. float tmpY = IVP2HL( debugCarSystem.vecWheelRaycasts[iWheel][0].z );
  541. vecStart.z = -IVP2HL( debugCarSystem.vecWheelRaycasts[iWheel][0].y );
  542. vecStart.y = tmpY;
  543. vecStart.x = IVP2HL( debugCarSystem.vecWheelRaycasts[iWheel][0].x );
  544. tmpY = IVP2HL( debugCarSystem.vecWheelRaycasts[iWheel][1].z );
  545. vecEnd.z = -IVP2HL( debugCarSystem.vecWheelRaycasts[iWheel][1].y );
  546. vecEnd.y = tmpY;
  547. vecEnd.x = IVP2HL( debugCarSystem.vecWheelRaycasts[iWheel][1].x );
  548. tmpY = IVP2HL( debugCarSystem.vecWheelRaycastImpacts[iWheel].z );
  549. vecImpact.z = -IVP2HL( debugCarSystem.vecWheelRaycastImpacts[iWheel].y );
  550. vecImpact.y = tmpY;
  551. vecImpact.x = IVP2HL( debugCarSystem.vecWheelRaycastImpacts[iWheel].x );
  552. NDebugOverlay::BoxAngles( vecStart, Vector( -1 , -1, -1 ), Vector( 1, 1, 1 ), angBodyDirection, 0, 255, 0, 0, 0 );
  553. NDebugOverlay::Line( vecStart, vecEnd, 255, 255, 0, true, 0 );
  554. NDebugOverlay::BoxAngles( vecEnd, Vector( -1, -1, -1 ), Vector( 1, 1, 1 ), angBodyDirection, 255, 0, 0, 0, 0 );
  555. NDebugOverlay::BoxAngles( vecImpact, Vector( -0.5f , -0.5f, -0.5f ), Vector( 0.5f, 0.5f, 0.5f ), angBodyDirection, 0, 0, 255, 0, 0 );
  556. DebugDrawContactPoints( m_pVehicle->GetWheel(iWheel) );
  557. }
  558. }
  559. }
  560. int CFourWheelVehiclePhysics::DrawDebugTextOverlays( int nOffset )
  561. {
  562. const vehicle_operatingparams_t &params = m_pVehicle->GetOperatingParams();
  563. char tempstr[512];
  564. Q_snprintf( tempstr,sizeof(tempstr), "Speed %.1f T/S/B (%.0f/%.0f/%.1f)", params.speed, m_controls.throttle, m_controls.steering, m_controls.brake );
  565. m_pOuter->EntityText( nOffset, tempstr, 0 );
  566. nOffset++;
  567. Msg( "%s", tempstr );
  568. Q_snprintf( tempstr,sizeof(tempstr), "Gear: %d, RPM %4d", params.gear, (int)params.engineRPM );
  569. m_pOuter->EntityText( nOffset, tempstr, 0 );
  570. nOffset++;
  571. Msg( " %s\n", tempstr );
  572. return nOffset;
  573. }
  574. //----------------------------------------------------
  575. // Place dust at vector passed in
  576. //----------------------------------------------------
  577. void CFourWheelVehiclePhysics::PlaceWheelDust( int wheelIndex, bool ignoreSpeed )
  578. {
  579. // New vehicles handle this deeper into the base class
  580. if ( hl2_episodic.GetBool() )
  581. return;
  582. // Old dust
  583. Vector vecPos, vecVel;
  584. m_pVehicle->GetWheelContactPoint( wheelIndex, &vecPos, NULL );
  585. vecVel.Random( -1.0f, 1.0f );
  586. vecVel.z = random->RandomFloat( 0.3f, 1.0f );
  587. VectorNormalize( vecVel );
  588. // Higher speeds make larger dust clouds
  589. float flSize;
  590. if ( ignoreSpeed )
  591. {
  592. flSize = 1.0f;
  593. }
  594. else
  595. {
  596. flSize = RemapValClamped( m_nSpeed, DUST_SPEED, m_flMaxSpeed, 0.0f, 1.0f );
  597. }
  598. if ( flSize )
  599. {
  600. CEffectData data;
  601. data.m_vOrigin = vecPos;
  602. data.m_vNormal = vecVel;
  603. data.m_flScale = flSize;
  604. DispatchEffect( "WheelDust", data );
  605. }
  606. }
  607. //-----------------------------------------------------------------------------
  608. // Frame-based updating
  609. //-----------------------------------------------------------------------------
  610. bool CFourWheelVehiclePhysics::Think()
  611. {
  612. if (!m_pVehicle)
  613. return false;
  614. // Update sound + physics state
  615. const vehicle_operatingparams_t &carState = m_pVehicle->GetOperatingParams();
  616. const vehicleparams_t &vehicleData = m_pVehicle->GetVehicleParams();
  617. // Set save data.
  618. float carSpeed = fabs( INS2MPH( carState.speed ) );
  619. m_nLastSpeed = m_nSpeed;
  620. m_nSpeed = ( int )carSpeed;
  621. m_nRPM = ( int )carState.engineRPM;
  622. m_nHasBoost = vehicleData.engine.boostDelay; // if we have any boost delay, vehicle has boost ability
  623. m_pVehicle->Update( gpGlobals->frametime, m_controls);
  624. // boost sounds
  625. if( IsBoosting() && !m_bLastBoost )
  626. {
  627. m_bLastBoost = true;
  628. m_turboTimer = gpGlobals->curtime + 2.75f; // min duration for turbo sound
  629. }
  630. else if( !IsBoosting() && m_bLastBoost )
  631. {
  632. if ( gpGlobals->curtime >= m_turboTimer )
  633. {
  634. m_bLastBoost = false;
  635. }
  636. }
  637. m_fLastBoost = carState.boostDelay;
  638. m_nBoostTimeLeft = carState.boostTimeLeft;
  639. // UNDONE: Use skid info from the physics system?
  640. // Only check wheels if we're not being carried by a dropship
  641. if ( m_pOuter->VPhysicsGetObject() && !m_pOuter->VPhysicsGetObject()->GetShadowController() )
  642. {
  643. const float skidFactor = 0.15f;
  644. const float minSpeed = DEFAULT_SKID_THRESHOLD / skidFactor;
  645. // we have to slide at least 15% of our speed at higher speeds to make the skid sound (otherwise it can be too frequent)
  646. float skidThreshold = m_bLastSkid ? DEFAULT_SKID_THRESHOLD : (carState.speed * 0.15f);
  647. if ( skidThreshold < DEFAULT_SKID_THRESHOLD )
  648. {
  649. // otherwise, ramp in the skid threshold to avoid the sound at really low speeds unless really skidding
  650. skidThreshold = RemapValClamped( fabs(carState.speed), 0, minSpeed, DEFAULT_SKID_THRESHOLD*8, DEFAULT_SKID_THRESHOLD );
  651. }
  652. // check for skidding, if we're skidding, need to play the sound
  653. if ( carState.skidSpeed > skidThreshold && m_bIsOn )
  654. {
  655. if ( !m_bLastSkid ) // only play sound once
  656. {
  657. m_bLastSkid = true;
  658. CPASAttenuationFilter filter( m_pOuter );
  659. m_pOuterServerVehicle->PlaySound( VS_SKID_FRICTION_NORMAL );
  660. }
  661. // kick up dust from the wheels while skidding
  662. for ( int i = 0; i < 4; i++ )
  663. {
  664. PlaceWheelDust( i, true );
  665. }
  666. }
  667. else if ( m_bLastSkid == true )
  668. {
  669. m_bLastSkid = false;
  670. m_pOuterServerVehicle->StopSound( VS_SKID_FRICTION_NORMAL );
  671. }
  672. // toss dust up from the wheels of the vehicle if we're moving fast enough
  673. if ( m_nSpeed >= DUST_SPEED && vehicleData.steering.dustCloud && m_bIsOn )
  674. {
  675. for ( int i = 0; i < 4; i++ )
  676. {
  677. PlaceWheelDust( i );
  678. }
  679. }
  680. }
  681. // Make the steering wheel match the input, with a little dampening.
  682. #define STEER_DAMPING 0.8
  683. float flSteer = GetPoseParameter( m_poseParameters[VEH_STEER] );
  684. float flPhysicsSteer = carState.steeringAngle / vehicleData.steering.degreesSlow;
  685. SetPoseParameter( m_poseParameters[VEH_STEER], (STEER_DAMPING * flSteer) + ((1 - STEER_DAMPING) * flPhysicsSteer) );
  686. m_actionValue += m_actionSpeed * m_actionScale * gpGlobals->frametime;
  687. SetPoseParameter( m_poseParameters[VEH_ACTION], m_actionValue );
  688. // setup speedometer
  689. if ( m_bIsOn == true )
  690. {
  691. float displaySpeed = m_nSpeed / MAX_GUAGE_SPEED;
  692. SetPoseParameter( m_poseParameters[VEH_SPEEDO], displaySpeed );
  693. }
  694. return m_bIsOn;
  695. }
  696. //-----------------------------------------------------------------------------
  697. // Purpose:
  698. //-----------------------------------------------------------------------------
  699. bool CFourWheelVehiclePhysics::VPhysicsUpdate( IPhysicsObject *pPhysics )
  700. {
  701. // must be a wheel
  702. if ( pPhysics == m_pOuter->VPhysicsGetObject() )
  703. return true;
  704. // This is here so we can make the pose parameters of the wheels
  705. // reflect their current physics state
  706. for ( int i = 0; i < m_wheelCount; i++ )
  707. {
  708. if ( pPhysics == m_pWheels[i] )
  709. {
  710. Vector tmp;
  711. pPhysics->GetPosition( &m_wheelPosition[i], &m_wheelRotation[i] );
  712. // transform the wheel into body space
  713. VectorITransform( m_wheelPosition[i], m_pOuter->EntityToWorldTransform(), tmp );
  714. SetPoseParameter( m_poseParameters[VEH_FL_WHEEL_HEIGHT + i], (m_wheelBaseHeight[i] - tmp.z) / m_wheelTotalHeight[i] );
  715. SetPoseParameter( m_poseParameters[VEH_FL_WHEEL_SPIN + i], -m_wheelRotation[i].z );
  716. return false;
  717. }
  718. }
  719. return false;
  720. }
  721. //-----------------------------------------------------------------------------
  722. // Shared code to compute the vehicle view position
  723. //-----------------------------------------------------------------------------
  724. void CFourWheelVehiclePhysics::GetVehicleViewPosition( const char *pViewAttachment, float flPitchFactor, Vector *pAbsOrigin, QAngle *pAbsAngles )
  725. {
  726. matrix3x4_t vehicleEyePosToWorld;
  727. Vector vehicleEyeOrigin;
  728. QAngle vehicleEyeAngles;
  729. GetAttachment( pViewAttachment, vehicleEyeOrigin, vehicleEyeAngles );
  730. AngleMatrix( vehicleEyeAngles, vehicleEyePosToWorld );
  731. #ifdef HL2_DLL
  732. // View dampening.
  733. if ( r_VehicleViewDampen.GetInt() )
  734. {
  735. m_pOuterServerVehicle->GetFourWheelVehicle()->DampenEyePosition( vehicleEyeOrigin, vehicleEyeAngles );
  736. }
  737. #endif
  738. // Compute the relative rotation between the unperterbed eye attachment + the eye angles
  739. matrix3x4_t cameraToWorld;
  740. AngleMatrix( *pAbsAngles, cameraToWorld );
  741. matrix3x4_t worldToEyePos;
  742. MatrixInvert( vehicleEyePosToWorld, worldToEyePos );
  743. matrix3x4_t vehicleCameraToEyePos;
  744. ConcatTransforms( worldToEyePos, cameraToWorld, vehicleCameraToEyePos );
  745. // Now perterb the attachment point
  746. vehicleEyeAngles.x = RemapAngleRange( PITCH_CURVE_ZERO * flPitchFactor, PITCH_CURVE_LINEAR, vehicleEyeAngles.x );
  747. vehicleEyeAngles.z = RemapAngleRange( ROLL_CURVE_ZERO * flPitchFactor, ROLL_CURVE_LINEAR, vehicleEyeAngles.z );
  748. AngleMatrix( vehicleEyeAngles, vehicleEyeOrigin, vehicleEyePosToWorld );
  749. // Now treat the relative eye angles as being relative to this new, perterbed view position...
  750. matrix3x4_t newCameraToWorld;
  751. ConcatTransforms( vehicleEyePosToWorld, vehicleCameraToEyePos, newCameraToWorld );
  752. // output new view abs angles
  753. MatrixAngles( newCameraToWorld, *pAbsAngles );
  754. // UNDONE: *pOrigin would already be correct in single player if the HandleView() on the server ran after vphysics
  755. MatrixGetColumn( newCameraToWorld, 3, *pAbsOrigin );
  756. }
  757. //-----------------------------------------------------------------------------
  758. // Control initialization
  759. //-----------------------------------------------------------------------------
  760. void CFourWheelVehiclePhysics::ResetControls()
  761. {
  762. m_controls.handbrake = true;
  763. m_controls.handbrakeLeft = false;
  764. m_controls.handbrakeRight = false;
  765. m_controls.boost = 0;
  766. m_controls.brake = 0.0f;
  767. m_controls.throttle = 0;
  768. m_controls.steering = 0;
  769. }
  770. void CFourWheelVehiclePhysics::ReleaseHandbrake()
  771. {
  772. m_controls.handbrake = false;
  773. }
  774. void CFourWheelVehiclePhysics::SetHandbrake( bool bBrake )
  775. {
  776. m_controls.handbrake = bBrake;
  777. }
  778. //-----------------------------------------------------------------------------
  779. // Purpose:
  780. //-----------------------------------------------------------------------------
  781. void CFourWheelVehiclePhysics::EnableMotion( void )
  782. {
  783. for( int iWheel = 0; iWheel < m_wheelCount; ++iWheel )
  784. {
  785. m_pWheels[iWheel]->EnableMotion( true );
  786. }
  787. }
  788. //-----------------------------------------------------------------------------
  789. // Purpose:
  790. //-----------------------------------------------------------------------------
  791. void CFourWheelVehiclePhysics::DisableMotion( void )
  792. {
  793. Vector vecZero( 0.0f, 0.0f, 0.0f );
  794. AngularImpulse angNone( 0.0f, 0.0f, 0.0f );
  795. for( int iWheel = 0; iWheel < m_wheelCount; ++iWheel )
  796. {
  797. m_pWheels[iWheel]->SetVelocity( &vecZero, &angNone );
  798. m_pWheels[iWheel]->EnableMotion( false );
  799. }
  800. }
  801. float CFourWheelVehiclePhysics::GetHLSpeed() const
  802. {
  803. const vehicle_operatingparams_t &carState = m_pVehicle->GetOperatingParams();
  804. return carState.speed;
  805. }
  806. float CFourWheelVehiclePhysics::GetSteering() const
  807. {
  808. return m_controls.steering;
  809. }
  810. float CFourWheelVehiclePhysics::GetSteeringDegrees() const
  811. {
  812. const vehicleparams_t vehicleParams = m_pVehicle->GetVehicleParams();
  813. return vehicleParams.steering.degreesSlow;
  814. }
  815. //-----------------------------------------------------------------------------
  816. // Purpose:
  817. //-----------------------------------------------------------------------------
  818. void CFourWheelVehiclePhysics::SteeringRest( float carSpeed, const vehicleparams_t &vehicleData )
  819. {
  820. float flSteeringRate = RemapValClamped( carSpeed, vehicleData.steering.speedSlow, vehicleData.steering.speedFast,
  821. vehicleData.steering.steeringRestRateSlow, vehicleData.steering.steeringRestRateFast );
  822. m_controls.steering = Approach(0, m_controls.steering, flSteeringRate * gpGlobals->frametime );
  823. }
  824. //-----------------------------------------------------------------------------
  825. // Purpose:
  826. //-----------------------------------------------------------------------------
  827. void CFourWheelVehiclePhysics::SteeringTurn( float carSpeed, const vehicleparams_t &vehicleData, bool bTurnLeft, bool bBrake, bool bThrottle )
  828. {
  829. float flTargetSteering = bTurnLeft ? -1.0f : 1.0f;
  830. // steering speeds are stored in MPH
  831. float flSteeringRestRate = RemapValClamped( carSpeed, vehicleData.steering.speedSlow, vehicleData.steering.speedFast,
  832. vehicleData.steering.steeringRestRateSlow, vehicleData.steering.steeringRestRateFast );
  833. float carSpeedIns = MPH2INS(carSpeed);
  834. // engine speeds are stored in in/s
  835. if ( carSpeedIns > vehicleData.engine.maxSpeed )
  836. {
  837. flSteeringRestRate = RemapValClamped( carSpeedIns, vehicleData.engine.maxSpeed, vehicleData.engine.boostMaxSpeed, vehicleData.steering.steeringRestRateFast, vehicleData.steering.steeringRestRateFast*0.5f );
  838. }
  839. const vehicle_operatingparams_t &carState = m_pVehicle->GetOperatingParams();
  840. bool bIsBoosting = carState.isTorqueBoosting;
  841. // if you're recovering from a boost and still going faster than max, use the boost steering values
  842. bool bIsBoostRecover = (carState.boostTimeLeft == 100 || carState.boostTimeLeft == 0) ? false : true;
  843. float boostMinSpeed = vehicleData.engine.maxSpeed * vehicleData.engine.autobrakeSpeedGain;
  844. if ( !bIsBoosting && bIsBoostRecover && carSpeedIns > boostMinSpeed )
  845. {
  846. bIsBoosting = true;
  847. }
  848. if ( bIsBoosting )
  849. {
  850. flSteeringRestRate *= vehicleData.steering.boostSteeringRestRateFactor;
  851. }
  852. else if ( bThrottle )
  853. {
  854. flSteeringRestRate *= vehicleData.steering.throttleSteeringRestRateFactor;
  855. }
  856. float flSteeringRate = RemapValClamped( carSpeed, vehicleData.steering.speedSlow, vehicleData.steering.speedFast,
  857. vehicleData.steering.steeringRateSlow, vehicleData.steering.steeringRateFast );
  858. if ( fabs(flSteeringRate) < flSteeringRestRate )
  859. {
  860. if ( Sign(flTargetSteering) != Sign(m_controls.steering) )
  861. {
  862. flSteeringRate = flSteeringRestRate;
  863. }
  864. }
  865. if ( bIsBoosting )
  866. {
  867. flSteeringRate *= vehicleData.steering.boostSteeringRateFactor;
  868. }
  869. else if ( bBrake )
  870. {
  871. flSteeringRate *= vehicleData.steering.brakeSteeringRateFactor;
  872. }
  873. flSteeringRate *= gpGlobals->frametime;
  874. m_controls.steering = Approach( flTargetSteering, m_controls.steering, flSteeringRate );
  875. m_controls.bAnalogSteering = false;
  876. }
  877. //-----------------------------------------------------------------------------
  878. // Purpose:
  879. //-----------------------------------------------------------------------------
  880. void CFourWheelVehiclePhysics::SteeringTurnAnalog( float carSpeed, const vehicleparams_t &vehicleData, float sidemove )
  881. {
  882. // OLD Code
  883. #if 0
  884. float flSteeringRate = STEERING_BASE_RATE;
  885. float factor = clamp( fabs( sidemove ) / STICK_EXTENTS, 0.0f, 1.0f );
  886. factor *= 30;
  887. flSteeringRate *= log( factor );
  888. flSteeringRate *= gpGlobals->frametime;
  889. SetSteering( sidemove < 0.0f ? -1 : 1, flSteeringRate );
  890. #else
  891. // This is tested with gamepads with analog sticks. It gives full analog control allowing the player to hold shallow turns.
  892. float steering = ( sidemove / STICK_EXTENTS );
  893. float flSign = ( steering > 0 ) ? 1.0f : -1.0f;
  894. float flSteerAdj = RemapValClamped( fabs( steering ), xbox_steering_deadzone.GetFloat(), 1.0f, 0.0f, 1.0f );
  895. float flSteeringRate = RemapValClamped( carSpeed, vehicleData.steering.speedSlow, vehicleData.steering.speedFast,
  896. vehicleData.steering.steeringRateSlow, vehicleData.steering.steeringRateFast );
  897. flSteeringRate *= vehicleData.steering.throttleSteeringRestRateFactor;
  898. m_controls.bAnalogSteering = true;
  899. SetSteering( flSign * flSteerAdj, flSteeringRate * gpGlobals->frametime );
  900. #endif
  901. }
  902. //-----------------------------------------------------------------------------
  903. // Methods related to actually driving the vehicle
  904. //-----------------------------------------------------------------------------
  905. void CFourWheelVehiclePhysics::UpdateDriverControls( CUserCmd *cmd, float flFrameTime )
  906. {
  907. const float SPEED_THROTTLE_AS_BRAKE = 2.0f;
  908. int nButtons = cmd->buttons;
  909. // Get vehicle data.
  910. const vehicle_operatingparams_t &carState = m_pVehicle->GetOperatingParams();
  911. const vehicleparams_t &vehicleData = m_pVehicle->GetVehicleParams();
  912. // Get current speed in miles/hour.
  913. float flCarSign = 0.0f;
  914. if (carState.speed >= SPEED_THROTTLE_AS_BRAKE)
  915. {
  916. flCarSign = 1.0f;
  917. }
  918. else if ( carState.speed <= -SPEED_THROTTLE_AS_BRAKE )
  919. {
  920. flCarSign = -1.0f;
  921. }
  922. float carSpeed = fabs(INS2MPH(carState.speed));
  923. // If going forward and turning hard, keep the throttle applied.
  924. if( xbox_autothrottle.GetBool() && cmd->forwardmove > 0.0f )
  925. {
  926. if( carSpeed > GetMaxSpeed() * 0.75 )
  927. {
  928. if( fabs(cmd->sidemove) > cmd->forwardmove )
  929. {
  930. cmd->forwardmove = STICK_EXTENTS;
  931. }
  932. }
  933. }
  934. //Msg("F: %4.1f \tS: %4.1f!\tSTEER: %3.1f\n", cmd->forwardmove, cmd->sidemove, carState.steeringAngle);
  935. // If changing direction, use default "return to zero" speed to more quickly transition.
  936. if ( ( nButtons & IN_MOVELEFT ) || ( nButtons & IN_MOVERIGHT ) )
  937. {
  938. bool bTurnLeft = ( (nButtons & IN_MOVELEFT) != 0 );
  939. bool bBrake = ((nButtons & IN_BACK) != 0);
  940. bool bThrottleDown = ( (nButtons & IN_FORWARD) != 0 ) && !bBrake;
  941. SteeringTurn( carSpeed, vehicleData, bTurnLeft, bBrake, bThrottleDown );
  942. }
  943. else if ( cmd->sidemove != 0.0f )
  944. {
  945. SteeringTurnAnalog( carSpeed, vehicleData, cmd->sidemove );
  946. }
  947. else
  948. {
  949. SteeringRest( carSpeed, vehicleData );
  950. }
  951. // Set vehicle control inputs.
  952. m_controls.boost = 0;
  953. m_controls.handbrake = false;
  954. m_controls.handbrakeLeft = false;
  955. m_controls.handbrakeRight = false;
  956. m_controls.brakepedal = false;
  957. bool bThrottle;
  958. //-------------------------------------------------------------------------
  959. // Analog throttle biasing - This code gives the player a bit of control stick
  960. // 'slop' in the opposite direction that they are driving. If a player is
  961. // driving forward and makes a hard turn in which the stick actually goes
  962. // below neutral (toward reverse), this code continues to propel the car
  963. // forward unless the player makes a significant motion towards reverse.
  964. // (The inverse is true when driving in reverse and the stick is moved slightly forward)
  965. //-------------------------------------------------------------------------
  966. CBaseEntity *pDriver = m_pOuterServerVehicle->GetDriver();
  967. CBasePlayer *pPlayerDriver;
  968. float flBiasThreshold = xbox_throttlebias.GetFloat();
  969. if( pDriver && pDriver->IsPlayer() )
  970. {
  971. pPlayerDriver = dynamic_cast<CBasePlayer*>(pDriver);
  972. if( cmd->forwardmove == 0.0f && (fabs(cmd->sidemove) < 200.0f) )
  973. {
  974. // If the stick goes neutral, clear out the bias. When the bias is neutral, it will begin biasing
  975. // in whichever direction the user next presses the analog stick.
  976. pPlayerDriver->SetVehicleAnalogControlBias( VEHICLE_ANALOG_BIAS_NONE );
  977. }
  978. else if( cmd->forwardmove > 0.0f)
  979. {
  980. if( pPlayerDriver->GetVehicleAnalogControlBias() == VEHICLE_ANALOG_BIAS_REVERSE )
  981. {
  982. // Player is pushing forward, but the controller is currently biased for reverse driving.
  983. // Must pass a threshold to be accepted as forward input. Otherwise we just spoof a reduced reverse input
  984. // to keep the car moving in the direction the player probably expects.
  985. if( cmd->forwardmove < flBiasThreshold )
  986. {
  987. cmd->forwardmove = -xbox_throttlespoof.GetFloat();
  988. }
  989. else
  990. {
  991. // Passed the threshold. Allow the direction change to occur.
  992. pPlayerDriver->SetVehicleAnalogControlBias( VEHICLE_ANALOG_BIAS_FORWARD );
  993. }
  994. }
  995. else if( pPlayerDriver->GetVehicleAnalogControlBias() == VEHICLE_ANALOG_BIAS_NONE )
  996. {
  997. pPlayerDriver->SetVehicleAnalogControlBias( VEHICLE_ANALOG_BIAS_FORWARD );
  998. }
  999. }
  1000. else if( cmd->forwardmove < 0.0f )
  1001. {
  1002. if( pPlayerDriver->GetVehicleAnalogControlBias() == VEHICLE_ANALOG_BIAS_FORWARD )
  1003. {
  1004. // Inverse of above logic
  1005. if( cmd->forwardmove > -flBiasThreshold )
  1006. {
  1007. cmd->forwardmove = xbox_throttlespoof.GetFloat();
  1008. }
  1009. else
  1010. {
  1011. pPlayerDriver->SetVehicleAnalogControlBias( VEHICLE_ANALOG_BIAS_REVERSE );
  1012. }
  1013. }
  1014. else if( pPlayerDriver->GetVehicleAnalogControlBias() == VEHICLE_ANALOG_BIAS_NONE )
  1015. {
  1016. pPlayerDriver->SetVehicleAnalogControlBias( VEHICLE_ANALOG_BIAS_REVERSE );
  1017. }
  1018. }
  1019. }
  1020. //=========================
  1021. // analog control
  1022. //=========================
  1023. if( cmd->forwardmove > 0.0f )
  1024. {
  1025. float flAnalogThrottle = cmd->forwardmove / STICK_EXTENTS;
  1026. flAnalogThrottle = clamp( flAnalogThrottle, 0.25f, 1.0f );
  1027. bThrottle = true;
  1028. if ( m_controls.throttle < 0 )
  1029. {
  1030. m_controls.throttle = 0;
  1031. }
  1032. float flMaxThrottle = MAX( 0.1, m_maxThrottle );
  1033. if ( m_controls.steering != 0 )
  1034. {
  1035. float flThrottleReduce = 0;
  1036. // ramp this in, don't just start at the slow speed reduction (helps accelerate from a stop)
  1037. if ( carSpeed < vehicleData.steering.speedSlow )
  1038. {
  1039. flThrottleReduce = RemapValClamped( carSpeed, 0, vehicleData.steering.speedSlow,
  1040. 0, vehicleData.steering.turnThrottleReduceSlow );
  1041. }
  1042. else
  1043. {
  1044. flThrottleReduce = RemapValClamped( carSpeed, vehicleData.steering.speedSlow, vehicleData.steering.speedFast,
  1045. vehicleData.steering.turnThrottleReduceSlow, vehicleData.steering.turnThrottleReduceFast );
  1046. }
  1047. float limit = 1.0f - (flThrottleReduce * fabs(m_controls.steering));
  1048. if ( limit < 0 )
  1049. limit = 0;
  1050. flMaxThrottle = MIN( flMaxThrottle, limit );
  1051. }
  1052. m_controls.throttle = Approach( flMaxThrottle * flAnalogThrottle, m_controls.throttle, flFrameTime * m_throttleRate );
  1053. // Apply the brake.
  1054. if ( ( flCarSign < 0.0f ) && m_controls.bHasBrakePedal )
  1055. {
  1056. m_controls.brake = Approach( BRAKE_MAX_VALUE, m_controls.brake, flFrameTime * r_vehicleBrakeRate.GetFloat() * BRAKE_BACK_FORWARD_SCALAR );
  1057. m_controls.brakepedal = true;
  1058. m_controls.throttle = 0.0f;
  1059. bThrottle = false;
  1060. }
  1061. else
  1062. {
  1063. m_controls.brake = 0.0f;
  1064. }
  1065. }
  1066. else if( cmd->forwardmove < 0.0f )
  1067. {
  1068. float flAnalogBrake = fabs(cmd->forwardmove / STICK_EXTENTS);
  1069. flAnalogBrake = clamp( flAnalogBrake, 0.25f, 1.0f );
  1070. bThrottle = true;
  1071. if ( m_controls.throttle > 0 )
  1072. {
  1073. m_controls.throttle = 0;
  1074. }
  1075. float flMaxThrottle = MIN( -0.1, m_flMaxRevThrottle );
  1076. m_controls.throttle = Approach( flMaxThrottle * flAnalogBrake, m_controls.throttle, flFrameTime * m_throttleRate );
  1077. // Apply the brake.
  1078. if ( ( flCarSign > 0.0f ) && m_controls.bHasBrakePedal )
  1079. {
  1080. m_controls.brake = Approach( BRAKE_MAX_VALUE, m_controls.brake, flFrameTime * r_vehicleBrakeRate.GetFloat() );
  1081. m_controls.brakepedal = true;
  1082. m_controls.throttle = 0.0f;
  1083. bThrottle = false;
  1084. }
  1085. else
  1086. {
  1087. m_controls.brake = 0.0f;
  1088. }
  1089. }
  1090. // digital control
  1091. else if ( nButtons & IN_FORWARD )
  1092. {
  1093. bThrottle = true;
  1094. if ( m_controls.throttle < 0 )
  1095. {
  1096. m_controls.throttle = 0;
  1097. }
  1098. float flMaxThrottle = MAX( 0.1, m_maxThrottle );
  1099. if ( m_controls.steering != 0 )
  1100. {
  1101. float flThrottleReduce = 0;
  1102. // ramp this in, don't just start at the slow speed reduction (helps accelerate from a stop)
  1103. if ( carSpeed < vehicleData.steering.speedSlow )
  1104. {
  1105. flThrottleReduce = RemapValClamped( carSpeed, 0, vehicleData.steering.speedSlow,
  1106. 0, vehicleData.steering.turnThrottleReduceSlow );
  1107. }
  1108. else
  1109. {
  1110. flThrottleReduce = RemapValClamped( carSpeed, vehicleData.steering.speedSlow, vehicleData.steering.speedFast,
  1111. vehicleData.steering.turnThrottleReduceSlow, vehicleData.steering.turnThrottleReduceFast );
  1112. }
  1113. float limit = 1.0f - (flThrottleReduce * fabs(m_controls.steering));
  1114. if ( limit < 0 )
  1115. limit = 0;
  1116. flMaxThrottle = MIN( flMaxThrottle, limit );
  1117. }
  1118. m_controls.throttle = Approach( flMaxThrottle, m_controls.throttle, flFrameTime * m_throttleRate );
  1119. // Apply the brake.
  1120. if ( ( flCarSign < 0.0f ) && m_controls.bHasBrakePedal )
  1121. {
  1122. m_controls.brake = Approach( BRAKE_MAX_VALUE, m_controls.brake, flFrameTime * r_vehicleBrakeRate.GetFloat() * BRAKE_BACK_FORWARD_SCALAR );
  1123. m_controls.brakepedal = true;
  1124. m_controls.throttle = 0.0f;
  1125. bThrottle = false;
  1126. }
  1127. else
  1128. {
  1129. m_controls.brake = 0.0f;
  1130. }
  1131. }
  1132. else if ( nButtons & IN_BACK )
  1133. {
  1134. bThrottle = true;
  1135. if ( m_controls.throttle > 0 )
  1136. {
  1137. m_controls.throttle = 0;
  1138. }
  1139. float flMaxThrottle = MIN( -0.1, m_flMaxRevThrottle );
  1140. m_controls.throttle = Approach( flMaxThrottle, m_controls.throttle, flFrameTime * m_throttleRate );
  1141. // Apply the brake.
  1142. if ( ( flCarSign > 0.0f ) && m_controls.bHasBrakePedal )
  1143. {
  1144. m_controls.brake = Approach( BRAKE_MAX_VALUE, m_controls.brake, flFrameTime * r_vehicleBrakeRate.GetFloat() );
  1145. m_controls.brakepedal = true;
  1146. m_controls.throttle = 0.0f;
  1147. bThrottle = false;
  1148. }
  1149. else
  1150. {
  1151. m_controls.brake = 0.0f;
  1152. }
  1153. }
  1154. else
  1155. {
  1156. bThrottle = false;
  1157. m_controls.throttle = 0;
  1158. m_controls.brake = 0.0f;
  1159. }
  1160. if ( ( nButtons & IN_SPEED ) && !IsEngineDisabled() && bThrottle )
  1161. {
  1162. m_controls.boost = 1.0f;
  1163. }
  1164. // Using has brakepedal for handbrake as well.
  1165. if ( ( nButtons & IN_JUMP ) && m_controls.bHasBrakePedal )
  1166. {
  1167. m_controls.handbrake = true;
  1168. if ( cmd->sidemove < -100 )
  1169. {
  1170. m_controls.handbrakeLeft = true;
  1171. }
  1172. else if ( cmd->sidemove > 100 )
  1173. {
  1174. m_controls.handbrakeRight = true;
  1175. }
  1176. // Prevent playing of the engine revup when we're braking
  1177. bThrottle = false;
  1178. }
  1179. if ( IsEngineDisabled() )
  1180. {
  1181. m_controls.throttle = 0.0f;
  1182. m_controls.handbrake = true;
  1183. bThrottle = false;
  1184. }
  1185. // throttle sounds
  1186. // If we dropped a bunch of speed, restart the throttle
  1187. if ( bThrottle && (m_nLastSpeed > m_nSpeed && (m_nLastSpeed - m_nSpeed > 10)) )
  1188. {
  1189. m_bLastThrottle = false;
  1190. }
  1191. // throttle down now but not before??? (or we're braking)
  1192. if ( !m_controls.handbrake && !m_controls.brakepedal && bThrottle && !m_bLastThrottle )
  1193. {
  1194. m_throttleStartTime = gpGlobals->curtime; // need to track how long throttle is down
  1195. m_bLastThrottle = true;
  1196. }
  1197. // throttle up now but not before??
  1198. else if ( !bThrottle && m_bLastThrottle && IsEngineDisabled() == false )
  1199. {
  1200. m_throttleActiveTime = gpGlobals->curtime - m_throttleStartTime;
  1201. m_bLastThrottle = false;
  1202. }
  1203. float flSpeedPercentage = clamp( m_nSpeed / m_flMaxSpeed, 0, 1 );
  1204. vbs_sound_update_t params;
  1205. params.Defaults();
  1206. params.bReverse = (m_controls.throttle < 0);
  1207. params.bThrottleDown = bThrottle;
  1208. params.bTurbo = IsBoosting();
  1209. params.bVehicleInWater = m_pOuterServerVehicle->IsVehicleBodyInWater();
  1210. params.flCurrentSpeedFraction = flSpeedPercentage;
  1211. params.flFrameTime = flFrameTime;
  1212. params.flWorldSpaceSpeed = carState.speed;
  1213. m_pOuterServerVehicle->SoundUpdate( params );
  1214. }
  1215. //-----------------------------------------------------------------------------
  1216. // Purpose:
  1217. //-----------------------------------------------------------------------------
  1218. bool CFourWheelVehiclePhysics::IsBoosting( void )
  1219. {
  1220. const vehicleparams_t *pVehicleParams = &m_pVehicle->GetVehicleParams();
  1221. const vehicle_operatingparams_t *pVehicleOperating = &m_pVehicle->GetOperatingParams();
  1222. if ( pVehicleParams && pVehicleOperating )
  1223. {
  1224. if ( ( pVehicleOperating->boostDelay - pVehicleParams->engine.boostDelay ) > 0.0f )
  1225. return true;
  1226. }
  1227. return false;
  1228. }
  1229. //-----------------------------------------------------------------------------
  1230. // Purpose:
  1231. //-----------------------------------------------------------------------------
  1232. void CFourWheelVehiclePhysics::SetDisableEngine( bool bDisable )
  1233. {
  1234. // Set the engine state.
  1235. m_pVehicle->SetEngineDisabled( bDisable );
  1236. }
  1237. static int AddPhysToList( IPhysicsObject **pList, int listMax, int count, IPhysicsObject *pPhys )
  1238. {
  1239. if ( pPhys )
  1240. {
  1241. if ( count < listMax )
  1242. {
  1243. pList[count] = pPhys;
  1244. count++;
  1245. }
  1246. }
  1247. return count;
  1248. }
  1249. int CFourWheelVehiclePhysics::VPhysicsGetObjectList( IPhysicsObject **pList, int listMax )
  1250. {
  1251. int count = 0;
  1252. // add the body
  1253. count = AddPhysToList( pList, listMax, count, m_pOuter->VPhysicsGetObject() );
  1254. for ( int i = 0; i < 4; i++ )
  1255. {
  1256. count = AddPhysToList( pList, listMax, count, m_pWheels[i] );
  1257. }
  1258. return count;
  1259. }