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.

1449 lines
48 KiB

  1. //========= Copyright 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. // Spawns the vehicle
  298. //-----------------------------------------------------------------------------
  299. void CFourWheelVehiclePhysics::Spawn( )
  300. {
  301. Assert( m_pOuter );
  302. m_actionValue = 0;
  303. m_actionSpeed = 0;
  304. m_bIsOn = false;
  305. m_controls.handbrake = false;
  306. m_controls.handbrakeLeft = false;
  307. m_controls.handbrakeRight = false;
  308. m_controls.bHasBrakePedal = true;
  309. m_controls.bAnalogSteering = false;
  310. SetMaxThrottle( 1.0 );
  311. SetMaxReverseThrottle( -1.0f );
  312. InitializePoseParameters();
  313. }
  314. //-----------------------------------------------------------------------------
  315. // Purpose: Initializes the vehicle physics
  316. // Called by our outer vehicle in it's Spawn()
  317. //-----------------------------------------------------------------------------
  318. bool CFourWheelVehiclePhysics::Initialize( const char *pVehicleScript, unsigned int nVehicleType )
  319. {
  320. // Ok, turn on the simulation now
  321. // FIXME: Disabling collisions here is necessary because we seem to be
  322. // getting a one-frame collision between the old + new collision models
  323. if ( m_pOuter->VPhysicsGetObject() )
  324. {
  325. m_pOuter->VPhysicsGetObject()->EnableCollisions(false);
  326. }
  327. m_pOuter->VPhysicsDestroyObject();
  328. // Create the vphysics model + teleport it into position
  329. solid_t solid;
  330. vehicleparams_t vehicle;
  331. if (!ParseVehicleScript( pVehicleScript, solid, vehicle ))
  332. {
  333. UTIL_Remove(m_pOuter);
  334. return false;
  335. }
  336. // NOTE: this needs to be greater than your max framerate (so zero is still instant)
  337. m_throttleRate = 10000.0;
  338. if ( vehicle.engine.throttleTime > 0 )
  339. {
  340. m_throttleRate = 1.0 / vehicle.engine.throttleTime;
  341. }
  342. m_flMaxSpeed = vehicle.engine.maxSpeed;
  343. IPhysicsObject *pBody = m_pOuter->VPhysicsInitNormal( SOLID_VPHYSICS, 0, false, &solid );
  344. PhysSetGameFlags( pBody, FVPHYSICS_NO_SELF_COLLISIONS | FVPHYSICS_MULTIOBJECT_ENTITY );
  345. m_pVehicle = physenv->CreateVehicleController( pBody, vehicle, nVehicleType, physgametrace );
  346. m_wheelCount = m_pVehicle->GetWheelCount();
  347. for ( int i = 0; i < m_wheelCount; i++ )
  348. {
  349. m_pWheels[i] = m_pVehicle->GetWheel( i );
  350. }
  351. return true;
  352. }
  353. //-----------------------------------------------------------------------------
  354. // Various steering parameters
  355. //-----------------------------------------------------------------------------
  356. void CFourWheelVehiclePhysics::SetThrottle( float flThrottle )
  357. {
  358. m_controls.throttle = flThrottle;
  359. }
  360. //-----------------------------------------------------------------------------
  361. // Purpose:
  362. //-----------------------------------------------------------------------------
  363. void CFourWheelVehiclePhysics::SetMaxThrottle( float flMaxThrottle )
  364. {
  365. m_maxThrottle = flMaxThrottle;
  366. }
  367. //-----------------------------------------------------------------------------
  368. // Purpose:
  369. //-----------------------------------------------------------------------------
  370. void CFourWheelVehiclePhysics::SetMaxReverseThrottle( float flMaxThrottle )
  371. {
  372. m_flMaxRevThrottle = flMaxThrottle;
  373. }
  374. //-----------------------------------------------------------------------------
  375. // Purpose:
  376. //-----------------------------------------------------------------------------
  377. void CFourWheelVehiclePhysics::SetSteering( float flSteering, float flSteeringRate )
  378. {
  379. if ( !flSteeringRate )
  380. {
  381. m_controls.steering = flSteering;
  382. }
  383. else
  384. {
  385. m_controls.steering = Approach( flSteering, m_controls.steering, flSteeringRate );
  386. }
  387. }
  388. //-----------------------------------------------------------------------------
  389. // Purpose:
  390. //-----------------------------------------------------------------------------
  391. void CFourWheelVehiclePhysics::SetSteeringDegrees( float flDegrees )
  392. {
  393. vehicleparams_t &vehicleParams = m_pVehicle->GetVehicleParamsForChange();
  394. vehicleParams.steering.degreesSlow = flDegrees;
  395. vehicleParams.steering.degreesFast = flDegrees;
  396. }
  397. //-----------------------------------------------------------------------------
  398. // Purpose:
  399. //-----------------------------------------------------------------------------
  400. void CFourWheelVehiclePhysics::SetAction( float flAction )
  401. {
  402. m_actionSpeed = flAction;
  403. }
  404. //-----------------------------------------------------------------------------
  405. // Purpose:
  406. //-----------------------------------------------------------------------------
  407. void CFourWheelVehiclePhysics::TurnOn( )
  408. {
  409. if ( IsEngineDisabled() )
  410. return;
  411. if ( !m_bIsOn )
  412. {
  413. m_pOuterServerVehicle->SoundStart();
  414. m_bIsOn = true;
  415. }
  416. }
  417. //-----------------------------------------------------------------------------
  418. // Purpose:
  419. //-----------------------------------------------------------------------------
  420. void CFourWheelVehiclePhysics::TurnOff( )
  421. {
  422. ResetControls();
  423. if ( m_bIsOn )
  424. {
  425. m_pOuterServerVehicle->SoundShutdown();
  426. m_bIsOn = false;
  427. }
  428. }
  429. //-----------------------------------------------------------------------------
  430. // Purpose:
  431. //-----------------------------------------------------------------------------
  432. void CFourWheelVehiclePhysics::SetBoost( float flBoost )
  433. {
  434. if ( !IsEngineDisabled() )
  435. {
  436. m_controls.boost = flBoost;
  437. }
  438. }
  439. //------------------------------------------------------
  440. // UpdateBooster - Calls UpdateBooster() in the vphysics
  441. // code to allow the timer to be updated
  442. //
  443. // Returns: false if timer has expired (can use again and
  444. // can stop think
  445. // true if timer still running
  446. //------------------------------------------------------
  447. bool CFourWheelVehiclePhysics::UpdateBooster( void )
  448. {
  449. float retval = m_pVehicle->UpdateBooster(gpGlobals->frametime );
  450. return ( retval > 0 );
  451. }
  452. //-----------------------------------------------------------------------------
  453. // Purpose:
  454. //-----------------------------------------------------------------------------
  455. void CFourWheelVehiclePhysics::SetHasBrakePedal( bool bHasBrakePedal )
  456. {
  457. m_controls.bHasBrakePedal = bHasBrakePedal;
  458. }
  459. //-----------------------------------------------------------------------------
  460. // Teleport
  461. //-----------------------------------------------------------------------------
  462. void CFourWheelVehiclePhysics::Teleport( matrix3x4_t& relativeTransform )
  463. {
  464. // We basically just have to make sure the wheels are in the right place
  465. // after teleportation occurs
  466. for ( int i = 0; i < m_wheelCount; i++ )
  467. {
  468. matrix3x4_t matrix, newMatrix;
  469. m_pWheels[i]->GetPositionMatrix( &matrix );
  470. ConcatTransforms( relativeTransform, matrix, newMatrix );
  471. m_pWheels[i]->SetPositionMatrix( newMatrix, true );
  472. }
  473. // Wake the vehicle back up after a teleport
  474. if ( m_pOuterServerVehicle && m_pOuterServerVehicle->GetFourWheelVehicle() )
  475. {
  476. IPhysicsObject *pObj = m_pOuterServerVehicle->GetFourWheelVehicle()->VPhysicsGetObject();
  477. if ( pObj )
  478. {
  479. pObj->Wake();
  480. }
  481. }
  482. }
  483. #if 1
  484. // For the #if 0 debug code below!
  485. #define HL2IVP_FACTOR METERS_PER_INCH
  486. #define IVP2HL(x) (float)(x * (1.0f/HL2IVP_FACTOR))
  487. #define HL2IVP(x) (double)(x * HL2IVP_FACTOR)
  488. #endif
  489. //-----------------------------------------------------------------------------
  490. // Debugging methods
  491. //-----------------------------------------------------------------------------
  492. void CFourWheelVehiclePhysics::DrawDebugGeometryOverlays()
  493. {
  494. for ( int iWheel = 0; iWheel < m_wheelCount; iWheel++ )
  495. {
  496. IPhysicsObject *pWheel = m_pVehicle->GetWheel( iWheel );
  497. float radius = pWheel->GetSphereRadius();
  498. Vector vecPos;
  499. QAngle vecRot;
  500. pWheel->GetPosition( &vecPos, &vecRot );
  501. // draw the physics object position/orientation
  502. NDebugOverlay::Sphere( vecPos, vecRot, radius, 0, 255, 0, 0, false, 0 );
  503. // draw the animation position/orientation
  504. NDebugOverlay::Sphere(m_wheelPosition[iWheel], m_wheelRotation[iWheel], radius, 255, 255, 0, 0, false, 0);
  505. }
  506. // Render vehicle data.
  507. IPhysicsObject *pBody = m_pOuter->VPhysicsGetObject();
  508. if ( pBody )
  509. {
  510. const vehicleparams_t vehicleParams = m_pVehicle->GetVehicleParams();
  511. // Draw a red cube as the "center" of the vehicle.
  512. Vector vecBodyPosition;
  513. QAngle angBodyDirection;
  514. pBody->GetPosition( &vecBodyPosition, &angBodyDirection );
  515. NDebugOverlay::BoxAngles( vecBodyPosition, Vector( -5, -5, -5 ), Vector( 5, 5, 5 ), angBodyDirection, 255, 0, 0, 0 ,0 );
  516. matrix3x4_t matrix;
  517. AngleMatrix( angBodyDirection, vecBodyPosition, matrix );
  518. // Draw green cubes at axle centers.
  519. Vector vecAxlePositions[2], vecAxlePositionsHL[2];
  520. vecAxlePositions[0] = vehicleParams.axles[0].offset;
  521. vecAxlePositions[1] = vehicleParams.axles[1].offset;
  522. VectorTransform( vecAxlePositions[0], matrix, vecAxlePositionsHL[0] );
  523. VectorTransform( vecAxlePositions[1], matrix, vecAxlePositionsHL[1] );
  524. NDebugOverlay::BoxAngles( vecAxlePositionsHL[0], Vector( -3, -3, -3 ), Vector( 3, 3, 3 ), angBodyDirection, 0, 255, 0, 0 ,0 );
  525. NDebugOverlay::BoxAngles( vecAxlePositionsHL[1], Vector( -3, -3, -3 ), Vector( 3, 3, 3 ), angBodyDirection, 0, 255, 0, 0 ,0 );
  526. // Draw wheel raycasts in yellow
  527. vehicle_debugcarsystem_t debugCarSystem;
  528. m_pVehicle->GetCarSystemDebugData( debugCarSystem );
  529. for ( int iWheel = 0; iWheel < 4; ++iWheel )
  530. {
  531. Vector vecStart, vecEnd, vecImpact;
  532. // Hack for now.
  533. float tmpY = IVP2HL( debugCarSystem.vecWheelRaycasts[iWheel][0].z );
  534. vecStart.z = -IVP2HL( debugCarSystem.vecWheelRaycasts[iWheel][0].y );
  535. vecStart.y = tmpY;
  536. vecStart.x = IVP2HL( debugCarSystem.vecWheelRaycasts[iWheel][0].x );
  537. tmpY = IVP2HL( debugCarSystem.vecWheelRaycasts[iWheel][1].z );
  538. vecEnd.z = -IVP2HL( debugCarSystem.vecWheelRaycasts[iWheel][1].y );
  539. vecEnd.y = tmpY;
  540. vecEnd.x = IVP2HL( debugCarSystem.vecWheelRaycasts[iWheel][1].x );
  541. tmpY = IVP2HL( debugCarSystem.vecWheelRaycastImpacts[iWheel].z );
  542. vecImpact.z = -IVP2HL( debugCarSystem.vecWheelRaycastImpacts[iWheel].y );
  543. vecImpact.y = tmpY;
  544. vecImpact.x = IVP2HL( debugCarSystem.vecWheelRaycastImpacts[iWheel].x );
  545. NDebugOverlay::BoxAngles( vecStart, Vector( -1 , -1, -1 ), Vector( 1, 1, 1 ), angBodyDirection, 0, 255, 0, 0, 0 );
  546. NDebugOverlay::Line( vecStart, vecEnd, 255, 255, 0, true, 0 );
  547. NDebugOverlay::BoxAngles( vecEnd, Vector( -1, -1, -1 ), Vector( 1, 1, 1 ), angBodyDirection, 255, 0, 0, 0, 0 );
  548. NDebugOverlay::BoxAngles( vecImpact, Vector( -0.5f , -0.5f, -0.5f ), Vector( 0.5f, 0.5f, 0.5f ), angBodyDirection, 0, 0, 255, 0, 0 );
  549. DebugDrawContactPoints( m_pVehicle->GetWheel(iWheel) );
  550. }
  551. }
  552. }
  553. int CFourWheelVehiclePhysics::DrawDebugTextOverlays( int nOffset )
  554. {
  555. const vehicle_operatingparams_t &params = m_pVehicle->GetOperatingParams();
  556. char tempstr[512];
  557. Q_snprintf( tempstr,sizeof(tempstr), "Speed %.1f T/S/B (%.0f/%.0f/%.1f)", params.speed, m_controls.throttle, m_controls.steering, m_controls.brake );
  558. m_pOuter->EntityText( nOffset, tempstr, 0 );
  559. nOffset++;
  560. Msg( "%s", tempstr );
  561. Q_snprintf( tempstr,sizeof(tempstr), "Gear: %d, RPM %4d", params.gear, (int)params.engineRPM );
  562. m_pOuter->EntityText( nOffset, tempstr, 0 );
  563. nOffset++;
  564. Msg( " %s\n", tempstr );
  565. return nOffset;
  566. }
  567. //----------------------------------------------------
  568. // Place dust at vector passed in
  569. //----------------------------------------------------
  570. void CFourWheelVehiclePhysics::PlaceWheelDust( int wheelIndex, bool ignoreSpeed )
  571. {
  572. // New vehicles handle this deeper into the base class
  573. if ( hl2_episodic.GetBool() )
  574. return;
  575. // Old dust
  576. Vector vecPos, vecVel;
  577. m_pVehicle->GetWheelContactPoint( wheelIndex, &vecPos, NULL );
  578. vecVel.Random( -1.0f, 1.0f );
  579. vecVel.z = random->RandomFloat( 0.3f, 1.0f );
  580. VectorNormalize( vecVel );
  581. // Higher speeds make larger dust clouds
  582. float flSize;
  583. if ( ignoreSpeed )
  584. {
  585. flSize = 1.0f;
  586. }
  587. else
  588. {
  589. flSize = RemapValClamped( m_nSpeed, DUST_SPEED, m_flMaxSpeed, 0.0f, 1.0f );
  590. }
  591. if ( flSize )
  592. {
  593. CEffectData data;
  594. data.m_vOrigin = vecPos;
  595. data.m_vNormal = vecVel;
  596. data.m_flScale = flSize;
  597. DispatchEffect( "WheelDust", data );
  598. }
  599. }
  600. //-----------------------------------------------------------------------------
  601. // Frame-based updating
  602. //-----------------------------------------------------------------------------
  603. bool CFourWheelVehiclePhysics::Think()
  604. {
  605. if (!m_pVehicle)
  606. return false;
  607. // Update sound + physics state
  608. const vehicle_operatingparams_t &carState = m_pVehicle->GetOperatingParams();
  609. const vehicleparams_t &vehicleData = m_pVehicle->GetVehicleParams();
  610. // Set save data.
  611. float carSpeed = fabs( INS2MPH( carState.speed ) );
  612. m_nLastSpeed = m_nSpeed;
  613. m_nSpeed = ( int )carSpeed;
  614. m_nRPM = ( int )carState.engineRPM;
  615. m_nHasBoost = vehicleData.engine.boostDelay; // if we have any boost delay, vehicle has boost ability
  616. m_pVehicle->Update( gpGlobals->frametime, m_controls);
  617. // boost sounds
  618. if( IsBoosting() && !m_bLastBoost )
  619. {
  620. m_bLastBoost = true;
  621. m_turboTimer = gpGlobals->curtime + 2.75f; // min duration for turbo sound
  622. }
  623. else if( !IsBoosting() && m_bLastBoost )
  624. {
  625. if ( gpGlobals->curtime >= m_turboTimer )
  626. {
  627. m_bLastBoost = false;
  628. }
  629. }
  630. m_fLastBoost = carState.boostDelay;
  631. m_nBoostTimeLeft = carState.boostTimeLeft;
  632. // UNDONE: Use skid info from the physics system?
  633. // Only check wheels if we're not being carried by a dropship
  634. if ( m_pOuter->VPhysicsGetObject() && !m_pOuter->VPhysicsGetObject()->GetShadowController() )
  635. {
  636. const float skidFactor = 0.15f;
  637. const float minSpeed = DEFAULT_SKID_THRESHOLD / skidFactor;
  638. // we have to slide at least 15% of our speed at higher speeds to make the skid sound (otherwise it can be too frequent)
  639. float skidThreshold = m_bLastSkid ? DEFAULT_SKID_THRESHOLD : (carState.speed * 0.15f);
  640. if ( skidThreshold < DEFAULT_SKID_THRESHOLD )
  641. {
  642. // otherwise, ramp in the skid threshold to avoid the sound at really low speeds unless really skidding
  643. skidThreshold = RemapValClamped( fabs(carState.speed), 0, minSpeed, DEFAULT_SKID_THRESHOLD*8, DEFAULT_SKID_THRESHOLD );
  644. }
  645. // check for skidding, if we're skidding, need to play the sound
  646. if ( carState.skidSpeed > skidThreshold && m_bIsOn )
  647. {
  648. if ( !m_bLastSkid ) // only play sound once
  649. {
  650. m_bLastSkid = true;
  651. CPASAttenuationFilter filter( m_pOuter );
  652. m_pOuterServerVehicle->PlaySound( VS_SKID_FRICTION_NORMAL );
  653. }
  654. // kick up dust from the wheels while skidding
  655. for ( int i = 0; i < 4; i++ )
  656. {
  657. PlaceWheelDust( i, true );
  658. }
  659. }
  660. else if ( m_bLastSkid == true )
  661. {
  662. m_bLastSkid = false;
  663. m_pOuterServerVehicle->StopSound( VS_SKID_FRICTION_NORMAL );
  664. }
  665. // toss dust up from the wheels of the vehicle if we're moving fast enough
  666. if ( m_nSpeed >= DUST_SPEED && vehicleData.steering.dustCloud && m_bIsOn )
  667. {
  668. for ( int i = 0; i < 4; i++ )
  669. {
  670. PlaceWheelDust( i );
  671. }
  672. }
  673. }
  674. // Make the steering wheel match the input, with a little dampening.
  675. #define STEER_DAMPING 0.8
  676. float flSteer = GetPoseParameter( m_poseParameters[VEH_STEER] );
  677. float flPhysicsSteer = carState.steeringAngle / vehicleData.steering.degreesSlow;
  678. SetPoseParameter( m_poseParameters[VEH_STEER], (STEER_DAMPING * flSteer) + ((1 - STEER_DAMPING) * flPhysicsSteer) );
  679. m_actionValue += m_actionSpeed * m_actionScale * gpGlobals->frametime;
  680. SetPoseParameter( m_poseParameters[VEH_ACTION], m_actionValue );
  681. // setup speedometer
  682. if ( m_bIsOn == true )
  683. {
  684. float displaySpeed = m_nSpeed / MAX_GUAGE_SPEED;
  685. SetPoseParameter( m_poseParameters[VEH_SPEEDO], displaySpeed );
  686. }
  687. return m_bIsOn;
  688. }
  689. //-----------------------------------------------------------------------------
  690. // Purpose:
  691. //-----------------------------------------------------------------------------
  692. bool CFourWheelVehiclePhysics::VPhysicsUpdate( IPhysicsObject *pPhysics )
  693. {
  694. // must be a wheel
  695. if ( pPhysics == m_pOuter->VPhysicsGetObject() )
  696. return true;
  697. // This is here so we can make the pose parameters of the wheels
  698. // reflect their current physics state
  699. for ( int i = 0; i < m_wheelCount; i++ )
  700. {
  701. if ( pPhysics == m_pWheels[i] )
  702. {
  703. Vector tmp;
  704. pPhysics->GetPosition( &m_wheelPosition[i], &m_wheelRotation[i] );
  705. // transform the wheel into body space
  706. VectorITransform( m_wheelPosition[i], m_pOuter->EntityToWorldTransform(), tmp );
  707. SetPoseParameter( m_poseParameters[VEH_FL_WHEEL_HEIGHT + i], (m_wheelBaseHeight[i] - tmp.z) / m_wheelTotalHeight[i] );
  708. SetPoseParameter( m_poseParameters[VEH_FL_WHEEL_SPIN + i], -m_wheelRotation[i].z );
  709. return false;
  710. }
  711. }
  712. return false;
  713. }
  714. //-----------------------------------------------------------------------------
  715. // Shared code to compute the vehicle view position
  716. //-----------------------------------------------------------------------------
  717. void CFourWheelVehiclePhysics::GetVehicleViewPosition( const char *pViewAttachment, float flPitchFactor, Vector *pAbsOrigin, QAngle *pAbsAngles )
  718. {
  719. matrix3x4_t vehicleEyePosToWorld;
  720. Vector vehicleEyeOrigin;
  721. QAngle vehicleEyeAngles;
  722. GetAttachment( pViewAttachment, vehicleEyeOrigin, vehicleEyeAngles );
  723. AngleMatrix( vehicleEyeAngles, vehicleEyePosToWorld );
  724. #ifdef HL2_DLL
  725. // View dampening.
  726. if ( r_VehicleViewDampen.GetInt() )
  727. {
  728. m_pOuterServerVehicle->GetFourWheelVehicle()->DampenEyePosition( vehicleEyeOrigin, vehicleEyeAngles );
  729. }
  730. #endif
  731. // Compute the relative rotation between the unperterbed eye attachment + the eye angles
  732. matrix3x4_t cameraToWorld;
  733. AngleMatrix( *pAbsAngles, cameraToWorld );
  734. matrix3x4_t worldToEyePos;
  735. MatrixInvert( vehicleEyePosToWorld, worldToEyePos );
  736. matrix3x4_t vehicleCameraToEyePos;
  737. ConcatTransforms( worldToEyePos, cameraToWorld, vehicleCameraToEyePos );
  738. // Now perterb the attachment point
  739. vehicleEyeAngles.x = RemapAngleRange( PITCH_CURVE_ZERO * flPitchFactor, PITCH_CURVE_LINEAR, vehicleEyeAngles.x );
  740. vehicleEyeAngles.z = RemapAngleRange( ROLL_CURVE_ZERO * flPitchFactor, ROLL_CURVE_LINEAR, vehicleEyeAngles.z );
  741. AngleMatrix( vehicleEyeAngles, vehicleEyeOrigin, vehicleEyePosToWorld );
  742. // Now treat the relative eye angles as being relative to this new, perterbed view position...
  743. matrix3x4_t newCameraToWorld;
  744. ConcatTransforms( vehicleEyePosToWorld, vehicleCameraToEyePos, newCameraToWorld );
  745. // output new view abs angles
  746. MatrixAngles( newCameraToWorld, *pAbsAngles );
  747. // UNDONE: *pOrigin would already be correct in single player if the HandleView() on the server ran after vphysics
  748. MatrixGetColumn( newCameraToWorld, 3, *pAbsOrigin );
  749. }
  750. //-----------------------------------------------------------------------------
  751. // Control initialization
  752. //-----------------------------------------------------------------------------
  753. void CFourWheelVehiclePhysics::ResetControls()
  754. {
  755. m_controls.handbrake = true;
  756. m_controls.handbrakeLeft = false;
  757. m_controls.handbrakeRight = false;
  758. m_controls.boost = 0;
  759. m_controls.brake = 0.0f;
  760. m_controls.throttle = 0;
  761. m_controls.steering = 0;
  762. }
  763. void CFourWheelVehiclePhysics::ReleaseHandbrake()
  764. {
  765. m_controls.handbrake = false;
  766. }
  767. void CFourWheelVehiclePhysics::SetHandbrake( bool bBrake )
  768. {
  769. m_controls.handbrake = bBrake;
  770. }
  771. //-----------------------------------------------------------------------------
  772. // Purpose:
  773. //-----------------------------------------------------------------------------
  774. void CFourWheelVehiclePhysics::EnableMotion( void )
  775. {
  776. for( int iWheel = 0; iWheel < m_wheelCount; ++iWheel )
  777. {
  778. m_pWheels[iWheel]->EnableMotion( true );
  779. }
  780. }
  781. //-----------------------------------------------------------------------------
  782. // Purpose:
  783. //-----------------------------------------------------------------------------
  784. void CFourWheelVehiclePhysics::DisableMotion( void )
  785. {
  786. Vector vecZero( 0.0f, 0.0f, 0.0f );
  787. AngularImpulse angNone( 0.0f, 0.0f, 0.0f );
  788. for( int iWheel = 0; iWheel < m_wheelCount; ++iWheel )
  789. {
  790. m_pWheels[iWheel]->SetVelocity( &vecZero, &angNone );
  791. m_pWheels[iWheel]->EnableMotion( false );
  792. }
  793. }
  794. float CFourWheelVehiclePhysics::GetHLSpeed() const
  795. {
  796. const vehicle_operatingparams_t &carState = m_pVehicle->GetOperatingParams();
  797. return carState.speed;
  798. }
  799. float CFourWheelVehiclePhysics::GetSteering() const
  800. {
  801. return m_controls.steering;
  802. }
  803. float CFourWheelVehiclePhysics::GetSteeringDegrees() const
  804. {
  805. const vehicleparams_t vehicleParams = m_pVehicle->GetVehicleParams();
  806. return vehicleParams.steering.degreesSlow;
  807. }
  808. //-----------------------------------------------------------------------------
  809. // Purpose:
  810. //-----------------------------------------------------------------------------
  811. void CFourWheelVehiclePhysics::SteeringRest( float carSpeed, const vehicleparams_t &vehicleData )
  812. {
  813. float flSteeringRate = RemapValClamped( carSpeed, vehicleData.steering.speedSlow, vehicleData.steering.speedFast,
  814. vehicleData.steering.steeringRestRateSlow, vehicleData.steering.steeringRestRateFast );
  815. m_controls.steering = Approach(0, m_controls.steering, flSteeringRate * gpGlobals->frametime );
  816. }
  817. //-----------------------------------------------------------------------------
  818. // Purpose:
  819. //-----------------------------------------------------------------------------
  820. void CFourWheelVehiclePhysics::SteeringTurn( float carSpeed, const vehicleparams_t &vehicleData, bool bTurnLeft, bool bBrake, bool bThrottle )
  821. {
  822. float flTargetSteering = bTurnLeft ? -1.0f : 1.0f;
  823. // steering speeds are stored in MPH
  824. float flSteeringRestRate = RemapValClamped( carSpeed, vehicleData.steering.speedSlow, vehicleData.steering.speedFast,
  825. vehicleData.steering.steeringRestRateSlow, vehicleData.steering.steeringRestRateFast );
  826. float carSpeedIns = MPH2INS(carSpeed);
  827. // engine speeds are stored in in/s
  828. if ( carSpeedIns > vehicleData.engine.maxSpeed )
  829. {
  830. flSteeringRestRate = RemapValClamped( carSpeedIns, vehicleData.engine.maxSpeed, vehicleData.engine.boostMaxSpeed, vehicleData.steering.steeringRestRateFast, vehicleData.steering.steeringRestRateFast*0.5f );
  831. }
  832. const vehicle_operatingparams_t &carState = m_pVehicle->GetOperatingParams();
  833. bool bIsBoosting = carState.isTorqueBoosting;
  834. // if you're recovering from a boost and still going faster than max, use the boost steering values
  835. bool bIsBoostRecover = (carState.boostTimeLeft == 100 || carState.boostTimeLeft == 0) ? false : true;
  836. float boostMinSpeed = vehicleData.engine.maxSpeed * vehicleData.engine.autobrakeSpeedGain;
  837. if ( !bIsBoosting && bIsBoostRecover && carSpeedIns > boostMinSpeed )
  838. {
  839. bIsBoosting = true;
  840. }
  841. if ( bIsBoosting )
  842. {
  843. flSteeringRestRate *= vehicleData.steering.boostSteeringRestRateFactor;
  844. }
  845. else if ( bThrottle )
  846. {
  847. flSteeringRestRate *= vehicleData.steering.throttleSteeringRestRateFactor;
  848. }
  849. float flSteeringRate = RemapValClamped( carSpeed, vehicleData.steering.speedSlow, vehicleData.steering.speedFast,
  850. vehicleData.steering.steeringRateSlow, vehicleData.steering.steeringRateFast );
  851. if ( fabs(flSteeringRate) < flSteeringRestRate )
  852. {
  853. if ( Sign(flTargetSteering) != Sign(m_controls.steering) )
  854. {
  855. flSteeringRate = flSteeringRestRate;
  856. }
  857. }
  858. if ( bIsBoosting )
  859. {
  860. flSteeringRate *= vehicleData.steering.boostSteeringRateFactor;
  861. }
  862. else if ( bBrake )
  863. {
  864. flSteeringRate *= vehicleData.steering.brakeSteeringRateFactor;
  865. }
  866. flSteeringRate *= gpGlobals->frametime;
  867. m_controls.steering = Approach( flTargetSteering, m_controls.steering, flSteeringRate );
  868. m_controls.bAnalogSteering = false;
  869. }
  870. //-----------------------------------------------------------------------------
  871. // Purpose:
  872. //-----------------------------------------------------------------------------
  873. void CFourWheelVehiclePhysics::SteeringTurnAnalog( float carSpeed, const vehicleparams_t &vehicleData, float sidemove )
  874. {
  875. // OLD Code
  876. #if 0
  877. float flSteeringRate = STEERING_BASE_RATE;
  878. float factor = clamp( fabs( sidemove ) / STICK_EXTENTS, 0.0f, 1.0f );
  879. factor *= 30;
  880. flSteeringRate *= log( factor );
  881. flSteeringRate *= gpGlobals->frametime;
  882. SetSteering( sidemove < 0.0f ? -1 : 1, flSteeringRate );
  883. #else
  884. // This is tested with gamepads with analog sticks. It gives full analog control allowing the player to hold shallow turns.
  885. float steering = ( sidemove / STICK_EXTENTS );
  886. float flSign = ( steering > 0 ) ? 1.0f : -1.0f;
  887. float flSteerAdj = RemapValClamped( fabs( steering ), xbox_steering_deadzone.GetFloat(), 1.0f, 0.0f, 1.0f );
  888. float flSteeringRate = RemapValClamped( carSpeed, vehicleData.steering.speedSlow, vehicleData.steering.speedFast,
  889. vehicleData.steering.steeringRateSlow, vehicleData.steering.steeringRateFast );
  890. flSteeringRate *= vehicleData.steering.throttleSteeringRestRateFactor;
  891. m_controls.bAnalogSteering = true;
  892. SetSteering( flSign * flSteerAdj, flSteeringRate * gpGlobals->frametime );
  893. #endif
  894. }
  895. //-----------------------------------------------------------------------------
  896. // Methods related to actually driving the vehicle
  897. //-----------------------------------------------------------------------------
  898. void CFourWheelVehiclePhysics::UpdateDriverControls( CUserCmd *cmd, float flFrameTime )
  899. {
  900. const float SPEED_THROTTLE_AS_BRAKE = 2.0f;
  901. int nButtons = cmd->buttons;
  902. // Get vehicle data.
  903. const vehicle_operatingparams_t &carState = m_pVehicle->GetOperatingParams();
  904. const vehicleparams_t &vehicleData = m_pVehicle->GetVehicleParams();
  905. // Get current speed in miles/hour.
  906. float flCarSign = 0.0f;
  907. if (carState.speed >= SPEED_THROTTLE_AS_BRAKE)
  908. {
  909. flCarSign = 1.0f;
  910. }
  911. else if ( carState.speed <= -SPEED_THROTTLE_AS_BRAKE )
  912. {
  913. flCarSign = -1.0f;
  914. }
  915. float carSpeed = fabs(INS2MPH(carState.speed));
  916. // If going forward and turning hard, keep the throttle applied.
  917. if( xbox_autothrottle.GetBool() && cmd->forwardmove > 0.0f )
  918. {
  919. if( carSpeed > GetMaxSpeed() * 0.75 )
  920. {
  921. if( fabs(cmd->sidemove) > cmd->forwardmove )
  922. {
  923. cmd->forwardmove = STICK_EXTENTS;
  924. }
  925. }
  926. }
  927. //Msg("F: %4.1f \tS: %4.1f!\tSTEER: %3.1f\n", cmd->forwardmove, cmd->sidemove, carState.steeringAngle);
  928. // If changing direction, use default "return to zero" speed to more quickly transition.
  929. if ( ( nButtons & IN_MOVELEFT ) || ( nButtons & IN_MOVERIGHT ) )
  930. {
  931. bool bTurnLeft = ( (nButtons & IN_MOVELEFT) != 0 );
  932. bool bBrake = ((nButtons & IN_BACK) != 0);
  933. bool bThrottleDown = ( (nButtons & IN_FORWARD) != 0 ) && !bBrake;
  934. SteeringTurn( carSpeed, vehicleData, bTurnLeft, bBrake, bThrottleDown );
  935. }
  936. else if ( cmd->sidemove != 0.0f )
  937. {
  938. SteeringTurnAnalog( carSpeed, vehicleData, cmd->sidemove );
  939. }
  940. else
  941. {
  942. SteeringRest( carSpeed, vehicleData );
  943. }
  944. // Set vehicle control inputs.
  945. m_controls.boost = 0;
  946. m_controls.handbrake = false;
  947. m_controls.handbrakeLeft = false;
  948. m_controls.handbrakeRight = false;
  949. m_controls.brakepedal = false;
  950. bool bThrottle;
  951. //-------------------------------------------------------------------------
  952. // Analog throttle biasing - This code gives the player a bit of control stick
  953. // 'slop' in the opposite direction that they are driving. If a player is
  954. // driving forward and makes a hard turn in which the stick actually goes
  955. // below neutral (toward reverse), this code continues to propel the car
  956. // forward unless the player makes a significant motion towards reverse.
  957. // (The inverse is true when driving in reverse and the stick is moved slightly forward)
  958. //-------------------------------------------------------------------------
  959. CBaseEntity *pDriver = m_pOuterServerVehicle->GetDriver();
  960. CBasePlayer *pPlayerDriver;
  961. float flBiasThreshold = xbox_throttlebias.GetFloat();
  962. if( pDriver && pDriver->IsPlayer() )
  963. {
  964. pPlayerDriver = dynamic_cast<CBasePlayer*>(pDriver);
  965. if( cmd->forwardmove == 0.0f && (fabs(cmd->sidemove) < 200.0f) )
  966. {
  967. // If the stick goes neutral, clear out the bias. When the bias is neutral, it will begin biasing
  968. // in whichever direction the user next presses the analog stick.
  969. pPlayerDriver->SetVehicleAnalogControlBias( VEHICLE_ANALOG_BIAS_NONE );
  970. }
  971. else if( cmd->forwardmove > 0.0f)
  972. {
  973. if( pPlayerDriver->GetVehicleAnalogControlBias() == VEHICLE_ANALOG_BIAS_REVERSE )
  974. {
  975. // Player is pushing forward, but the controller is currently biased for reverse driving.
  976. // Must pass a threshold to be accepted as forward input. Otherwise we just spoof a reduced reverse input
  977. // to keep the car moving in the direction the player probably expects.
  978. if( cmd->forwardmove < flBiasThreshold )
  979. {
  980. cmd->forwardmove = -xbox_throttlespoof.GetFloat();
  981. }
  982. else
  983. {
  984. // Passed the threshold. Allow the direction change to occur.
  985. pPlayerDriver->SetVehicleAnalogControlBias( VEHICLE_ANALOG_BIAS_FORWARD );
  986. }
  987. }
  988. else if( pPlayerDriver->GetVehicleAnalogControlBias() == VEHICLE_ANALOG_BIAS_NONE )
  989. {
  990. pPlayerDriver->SetVehicleAnalogControlBias( VEHICLE_ANALOG_BIAS_FORWARD );
  991. }
  992. }
  993. else if( cmd->forwardmove < 0.0f )
  994. {
  995. if( pPlayerDriver->GetVehicleAnalogControlBias() == VEHICLE_ANALOG_BIAS_FORWARD )
  996. {
  997. // Inverse of above logic
  998. if( cmd->forwardmove > -flBiasThreshold )
  999. {
  1000. cmd->forwardmove = xbox_throttlespoof.GetFloat();
  1001. }
  1002. else
  1003. {
  1004. pPlayerDriver->SetVehicleAnalogControlBias( VEHICLE_ANALOG_BIAS_REVERSE );
  1005. }
  1006. }
  1007. else if( pPlayerDriver->GetVehicleAnalogControlBias() == VEHICLE_ANALOG_BIAS_NONE )
  1008. {
  1009. pPlayerDriver->SetVehicleAnalogControlBias( VEHICLE_ANALOG_BIAS_REVERSE );
  1010. }
  1011. }
  1012. }
  1013. //=========================
  1014. // analog control
  1015. //=========================
  1016. if( cmd->forwardmove > 0.0f )
  1017. {
  1018. float flAnalogThrottle = cmd->forwardmove / STICK_EXTENTS;
  1019. flAnalogThrottle = clamp( flAnalogThrottle, 0.25f, 1.0f );
  1020. bThrottle = true;
  1021. if ( m_controls.throttle < 0 )
  1022. {
  1023. m_controls.throttle = 0;
  1024. }
  1025. float flMaxThrottle = MAX( 0.1, m_maxThrottle );
  1026. if ( m_controls.steering != 0 )
  1027. {
  1028. float flThrottleReduce = 0;
  1029. // ramp this in, don't just start at the slow speed reduction (helps accelerate from a stop)
  1030. if ( carSpeed < vehicleData.steering.speedSlow )
  1031. {
  1032. flThrottleReduce = RemapValClamped( carSpeed, 0, vehicleData.steering.speedSlow,
  1033. 0, vehicleData.steering.turnThrottleReduceSlow );
  1034. }
  1035. else
  1036. {
  1037. flThrottleReduce = RemapValClamped( carSpeed, vehicleData.steering.speedSlow, vehicleData.steering.speedFast,
  1038. vehicleData.steering.turnThrottleReduceSlow, vehicleData.steering.turnThrottleReduceFast );
  1039. }
  1040. float limit = 1.0f - (flThrottleReduce * fabs(m_controls.steering));
  1041. if ( limit < 0 )
  1042. limit = 0;
  1043. flMaxThrottle = MIN( flMaxThrottle, limit );
  1044. }
  1045. m_controls.throttle = Approach( flMaxThrottle * flAnalogThrottle, m_controls.throttle, flFrameTime * m_throttleRate );
  1046. // Apply the brake.
  1047. if ( ( flCarSign < 0.0f ) && m_controls.bHasBrakePedal )
  1048. {
  1049. m_controls.brake = Approach( BRAKE_MAX_VALUE, m_controls.brake, flFrameTime * r_vehicleBrakeRate.GetFloat() * BRAKE_BACK_FORWARD_SCALAR );
  1050. m_controls.brakepedal = true;
  1051. m_controls.throttle = 0.0f;
  1052. bThrottle = false;
  1053. }
  1054. else
  1055. {
  1056. m_controls.brake = 0.0f;
  1057. }
  1058. }
  1059. else if( cmd->forwardmove < 0.0f )
  1060. {
  1061. float flAnalogBrake = fabs(cmd->forwardmove / STICK_EXTENTS);
  1062. flAnalogBrake = clamp( flAnalogBrake, 0.25f, 1.0f );
  1063. bThrottle = true;
  1064. if ( m_controls.throttle > 0 )
  1065. {
  1066. m_controls.throttle = 0;
  1067. }
  1068. float flMaxThrottle = MIN( -0.1, m_flMaxRevThrottle );
  1069. m_controls.throttle = Approach( flMaxThrottle * flAnalogBrake, m_controls.throttle, flFrameTime * m_throttleRate );
  1070. // Apply the brake.
  1071. if ( ( flCarSign > 0.0f ) && m_controls.bHasBrakePedal )
  1072. {
  1073. m_controls.brake = Approach( BRAKE_MAX_VALUE, m_controls.brake, flFrameTime * r_vehicleBrakeRate.GetFloat() );
  1074. m_controls.brakepedal = true;
  1075. m_controls.throttle = 0.0f;
  1076. bThrottle = false;
  1077. }
  1078. else
  1079. {
  1080. m_controls.brake = 0.0f;
  1081. }
  1082. }
  1083. // digital control
  1084. else if ( nButtons & IN_FORWARD )
  1085. {
  1086. bThrottle = true;
  1087. if ( m_controls.throttle < 0 )
  1088. {
  1089. m_controls.throttle = 0;
  1090. }
  1091. float flMaxThrottle = MAX( 0.1, m_maxThrottle );
  1092. if ( m_controls.steering != 0 )
  1093. {
  1094. float flThrottleReduce = 0;
  1095. // ramp this in, don't just start at the slow speed reduction (helps accelerate from a stop)
  1096. if ( carSpeed < vehicleData.steering.speedSlow )
  1097. {
  1098. flThrottleReduce = RemapValClamped( carSpeed, 0, vehicleData.steering.speedSlow,
  1099. 0, vehicleData.steering.turnThrottleReduceSlow );
  1100. }
  1101. else
  1102. {
  1103. flThrottleReduce = RemapValClamped( carSpeed, vehicleData.steering.speedSlow, vehicleData.steering.speedFast,
  1104. vehicleData.steering.turnThrottleReduceSlow, vehicleData.steering.turnThrottleReduceFast );
  1105. }
  1106. float limit = 1.0f - (flThrottleReduce * fabs(m_controls.steering));
  1107. if ( limit < 0 )
  1108. limit = 0;
  1109. flMaxThrottle = MIN( flMaxThrottle, limit );
  1110. }
  1111. m_controls.throttle = Approach( flMaxThrottle, m_controls.throttle, flFrameTime * m_throttleRate );
  1112. // Apply the brake.
  1113. if ( ( flCarSign < 0.0f ) && m_controls.bHasBrakePedal )
  1114. {
  1115. m_controls.brake = Approach( BRAKE_MAX_VALUE, m_controls.brake, flFrameTime * r_vehicleBrakeRate.GetFloat() * BRAKE_BACK_FORWARD_SCALAR );
  1116. m_controls.brakepedal = true;
  1117. m_controls.throttle = 0.0f;
  1118. bThrottle = false;
  1119. }
  1120. else
  1121. {
  1122. m_controls.brake = 0.0f;
  1123. }
  1124. }
  1125. else if ( nButtons & IN_BACK )
  1126. {
  1127. bThrottle = true;
  1128. if ( m_controls.throttle > 0 )
  1129. {
  1130. m_controls.throttle = 0;
  1131. }
  1132. float flMaxThrottle = MIN( -0.1, m_flMaxRevThrottle );
  1133. m_controls.throttle = Approach( flMaxThrottle, m_controls.throttle, flFrameTime * m_throttleRate );
  1134. // Apply the brake.
  1135. if ( ( flCarSign > 0.0f ) && m_controls.bHasBrakePedal )
  1136. {
  1137. m_controls.brake = Approach( BRAKE_MAX_VALUE, m_controls.brake, flFrameTime * r_vehicleBrakeRate.GetFloat() );
  1138. m_controls.brakepedal = true;
  1139. m_controls.throttle = 0.0f;
  1140. bThrottle = false;
  1141. }
  1142. else
  1143. {
  1144. m_controls.brake = 0.0f;
  1145. }
  1146. }
  1147. else
  1148. {
  1149. bThrottle = false;
  1150. m_controls.throttle = 0;
  1151. m_controls.brake = 0.0f;
  1152. }
  1153. if ( ( nButtons & IN_SPEED ) && !IsEngineDisabled() && bThrottle )
  1154. {
  1155. m_controls.boost = 1.0f;
  1156. }
  1157. // Using has brakepedal for handbrake as well.
  1158. if ( ( nButtons & IN_JUMP ) && m_controls.bHasBrakePedal )
  1159. {
  1160. m_controls.handbrake = true;
  1161. if ( cmd->sidemove < -100 )
  1162. {
  1163. m_controls.handbrakeLeft = true;
  1164. }
  1165. else if ( cmd->sidemove > 100 )
  1166. {
  1167. m_controls.handbrakeRight = true;
  1168. }
  1169. // Prevent playing of the engine revup when we're braking
  1170. bThrottle = false;
  1171. }
  1172. if ( IsEngineDisabled() )
  1173. {
  1174. m_controls.throttle = 0.0f;
  1175. m_controls.handbrake = true;
  1176. bThrottle = false;
  1177. }
  1178. // throttle sounds
  1179. // If we dropped a bunch of speed, restart the throttle
  1180. if ( bThrottle && (m_nLastSpeed > m_nSpeed && (m_nLastSpeed - m_nSpeed > 10)) )
  1181. {
  1182. m_bLastThrottle = false;
  1183. }
  1184. // throttle down now but not before??? (or we're braking)
  1185. if ( !m_controls.handbrake && !m_controls.brakepedal && bThrottle && !m_bLastThrottle )
  1186. {
  1187. m_throttleStartTime = gpGlobals->curtime; // need to track how long throttle is down
  1188. m_bLastThrottle = true;
  1189. }
  1190. // throttle up now but not before??
  1191. else if ( !bThrottle && m_bLastThrottle && IsEngineDisabled() == false )
  1192. {
  1193. m_throttleActiveTime = gpGlobals->curtime - m_throttleStartTime;
  1194. m_bLastThrottle = false;
  1195. }
  1196. float flSpeedPercentage = clamp( m_nSpeed / m_flMaxSpeed, 0.f, 1.f );
  1197. vbs_sound_update_t params;
  1198. params.Defaults();
  1199. params.bReverse = (m_controls.throttle < 0);
  1200. params.bThrottleDown = bThrottle;
  1201. params.bTurbo = IsBoosting();
  1202. params.bVehicleInWater = m_pOuterServerVehicle->IsVehicleBodyInWater();
  1203. params.flCurrentSpeedFraction = flSpeedPercentage;
  1204. params.flFrameTime = flFrameTime;
  1205. params.flWorldSpaceSpeed = carState.speed;
  1206. m_pOuterServerVehicle->SoundUpdate( params );
  1207. }
  1208. //-----------------------------------------------------------------------------
  1209. // Purpose:
  1210. //-----------------------------------------------------------------------------
  1211. bool CFourWheelVehiclePhysics::IsBoosting( void )
  1212. {
  1213. const vehicleparams_t *pVehicleParams = &m_pVehicle->GetVehicleParams();
  1214. const vehicle_operatingparams_t *pVehicleOperating = &m_pVehicle->GetOperatingParams();
  1215. if ( pVehicleParams && pVehicleOperating )
  1216. {
  1217. if ( ( pVehicleOperating->boostDelay - pVehicleParams->engine.boostDelay ) > 0.0f )
  1218. return true;
  1219. }
  1220. return false;
  1221. }
  1222. //-----------------------------------------------------------------------------
  1223. // Purpose:
  1224. //-----------------------------------------------------------------------------
  1225. void CFourWheelVehiclePhysics::SetDisableEngine( bool bDisable )
  1226. {
  1227. // Set the engine state.
  1228. m_pVehicle->SetEngineDisabled( bDisable );
  1229. }
  1230. static int AddPhysToList( IPhysicsObject **pList, int listMax, int count, IPhysicsObject *pPhys )
  1231. {
  1232. if ( pPhys )
  1233. {
  1234. if ( count < listMax )
  1235. {
  1236. pList[count] = pPhys;
  1237. count++;
  1238. }
  1239. }
  1240. return count;
  1241. }
  1242. int CFourWheelVehiclePhysics::VPhysicsGetObjectList( IPhysicsObject **pList, int listMax )
  1243. {
  1244. int count = 0;
  1245. // add the body
  1246. count = AddPhysToList( pList, listMax, count, m_pOuter->VPhysicsGetObject() );
  1247. for ( int i = 0; i < 4; i++ )
  1248. {
  1249. count = AddPhysToList( pList, listMax, count, m_pWheels[i] );
  1250. }
  1251. return count;
  1252. }