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.

2101 lines
64 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "vehicle_base.h"
  8. #include "engine/IEngineSound.h"
  9. #include "in_buttons.h"
  10. #include "ammodef.h"
  11. #include "IEffects.h"
  12. #include "beam_shared.h"
  13. #include "weapon_gauss.h"
  14. #include "soundenvelope.h"
  15. #include "decals.h"
  16. #include "soundent.h"
  17. #include "te_effect_dispatch.h"
  18. #include "physics_saverestore.h"
  19. #include "movevars_shared.h"
  20. #include "npc_attackchopper.h"
  21. #include "weapon_rpg.h"
  22. #include "vphysics/constraints.h"
  23. #include "world.h"
  24. #include "rumble_shared.h"
  25. // NVNT for airboat weapon fire
  26. #include "haptics/haptic_utils.h"
  27. // memdbgon must be the last include file in a .cpp file!!!
  28. #include "tier0/memdbgon.h"
  29. extern ConVar sv_vehicle_autoaim_scale;
  30. #define VEHICLE_HITBOX_DRIVER 1
  31. //
  32. // Body groups.
  33. //
  34. #define AIRBOAT_BODYGROUP_GUN 1
  35. #define AIRBOAT_BODYGROUP_PROP 2
  36. #define AIRBOAT_BODYGROUP_BLUR 3
  37. #define AIRBOAT_LOCK_SPEED 10 // Airboat must be going slower than this for player to enter or exit, in in/sec
  38. #define AIRBOAT_DELTA_LENGTH_MAX 12.0f // 1 foot
  39. #define AIRBOAT_FRAMETIME_MIN 1e-6
  40. #define AIRBOAT_SPLASH_RIPPLE 0
  41. #define AIRBOAT_SPLASH_SPRAY 1
  42. #define AIRBOAT_SPLASH_RIPPLE_SIZE 20.0f
  43. //
  44. // Pose parameters.
  45. //
  46. #define AIRBOAT_GUN_YAW "vehicle_weapon_yaw"
  47. #define AIRBOAT_GUN_PITCH "vehicle_weapon_pitch"
  48. #define AIRBOAT_FRAME_FLEX_LEFT "Frame_Flex_L"
  49. #define AIRBOAT_FRAME_FLEX_RIGHT "Frame_Flex_R"
  50. #define CANNON_MAX_UP_PITCH 60.0f
  51. #define CANNON_MAX_DOWN_PITCH 30.0f
  52. #define CANNON_MAX_RIGHT_YAW 165.0f
  53. #define CANNON_MAX_LEFT_YAW 75.0f
  54. #define CANNON_HEAVY_SHOT_INTERVAL 0.2f
  55. #define CANNON_SHAKE_INTERVAL 1.0f
  56. static ConVar sk_airboat_max_ammo("sk_airboat_max_ammo", "100" );
  57. static ConVar sk_airboat_recharge_rate("sk_airboat_recharge_rate", "15" );
  58. static ConVar sk_airboat_drain_rate("sk_airboat_drain_rate", "10" );
  59. static ConVar hud_airboathint_numentries( "hud_airboathint_numentries", "10", FCVAR_NONE );
  60. static ConVar airboat_fatal_stress( "airboat_fatal_stress", "5000", FCVAR_NONE, "Amount of stress in kg that would kill the airboat driver." );
  61. extern ConVar autoaim_max_dist;
  62. class CPropAirboat : public CPropVehicleDriveable
  63. {
  64. DECLARE_CLASS( CPropAirboat, CPropVehicleDriveable );
  65. public:
  66. DECLARE_SERVERCLASS();
  67. DECLARE_DATADESC();
  68. // CPropVehicle
  69. virtual void ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMoveData );
  70. virtual void DriveVehicle( float flFrameTime, CUserCmd *ucmd, int iButtonsDown, int iButtonsReleased );
  71. void DampenEyePosition( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles );
  72. bool ShouldThink() { return true; }
  73. // CBaseEntity
  74. void Think(void);
  75. void Precache( void );
  76. void Spawn( void );
  77. virtual void OnRestore();
  78. virtual void Activate();
  79. virtual void UpdateOnRemove();
  80. virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() | FCAP_USE_IN_RADIUS; };
  81. virtual void DoMuzzleFlash( void );
  82. virtual void StopLoopingSounds();
  83. // position to shoot at
  84. virtual Vector BodyTarget( const Vector &posSrc, bool bNoisy );
  85. virtual Vector GetSmoothedVelocity( void );
  86. virtual void EnterVehicle( CBaseCombatCharacter *pPlayer );
  87. virtual bool AllowBlockedExit( CBaseCombatCharacter *pPlayer, int nRole ) { return false; }
  88. virtual void PreExitVehicle( CBaseCombatCharacter *pPlayer, int nRole );
  89. virtual void ExitVehicle( int nRole );
  90. void ComputePDControllerCoefficients( float *pCoefficientsOut, float flFrequency, float flDampening, float flDeltaTime );
  91. void DampenForwardMotion( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles, float flFrameTime );
  92. void DampenUpMotion( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles, float flFrameTime );
  93. virtual void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
  94. virtual int OnTakeDamage( const CTakeDamageInfo &info );
  95. void VPhysicsUpdate( IPhysicsObject *pPhysics );
  96. // Scraping noises for the various things we drive on.
  97. virtual void VPhysicsFriction( IPhysicsObject *pObject, float energy, int surfaceProps, int surfacePropsHit );
  98. bool HeadlightIsOn( void ) { return m_bHeadlightIsOn; }
  99. void HeadlightTurnOn( void );
  100. void HeadlightTurnOff( void );
  101. virtual bool ShouldDrawWaterImpacts( void );
  102. bool ShouldForceExit() { return m_bForcedExit; }
  103. void ClearForcedExit() { m_bForcedExit = false; }
  104. // Input handlers.
  105. void InputWake( inputdata_t &inputdata );
  106. void InputExitVehicle( inputdata_t &inputdata );
  107. void InputEnableGun( inputdata_t &inputdata );
  108. void InputStartRotorWashForces( inputdata_t &inputdata );
  109. void InputStopRotorWashForces( inputdata_t &inputdata );
  110. // Allows the shooter to change the impact effect of his bullets
  111. virtual void DoImpactEffect( trace_t &tr, int nDamageType );
  112. // Airboat passengers do not directly receive damage from blasts or radiation damage
  113. virtual bool PassengerShouldReceiveDamage( CTakeDamageInfo &info )
  114. {
  115. if ( info.GetDamageType() & DMG_VEHICLE )
  116. return true;
  117. return (info.GetDamageType() & (DMG_RADIATION|DMG_BLAST|DMG_CRUSH) ) == 0;
  118. }
  119. const char *GetTracerType( void );
  120. private:
  121. void CreateAntiFlipConstraint();
  122. void ApplyStressDamage( IPhysicsObject *pPhysics );
  123. float CalculatePhysicsStressDamage( vphysics_objectstress_t *pStressOut, IPhysicsObject *pPhysics );
  124. void CreateDangerSounds( void );
  125. void FireGun( );
  126. void UpdateSplashEffects( void );
  127. void CreateSplash( int nSplashType );
  128. // Purpose: Aim Gun at a target
  129. void AimGunAt( const Vector &endPos, float flInterval );
  130. // Purpose: Returns the direction the gun is currently aiming at
  131. void GetGunAimDirection( Vector *resultDir );
  132. // Recharges the ammo based on speed
  133. void RechargeAmmo();
  134. // Removes the ammo...
  135. void RemoveAmmo( float flAmmoAmount );
  136. // Purpose:
  137. void ComputeAimPoint( Vector *pVecAimPoint );
  138. // Do the right thing for the gun
  139. void UpdateGunState( CUserCmd *ucmd );
  140. // Sound management
  141. void CreateSounds();
  142. void UpdateSound();
  143. void UpdateWeaponSound();
  144. void UpdateEngineSound( CSoundEnvelopeController &controller, float speedRatio );
  145. void UpdateFanSound( CSoundEnvelopeController &controller, float speedRatio );
  146. void UpdateWaterSound( CSoundEnvelopeController &controller, float speedRatio );
  147. void UpdatePropeller();
  148. void UpdateGauge();
  149. void CreatePlayerBlocker();
  150. void DestroyPlayerBlocker();
  151. void EnablePlayerBlocker( bool bEnable );
  152. private:
  153. enum
  154. {
  155. GUN_STATE_IDLE = 0,
  156. GUN_STATE_FIRING,
  157. };
  158. Vector m_vecLastEyePos;
  159. Vector m_vecLastEyeTarget;
  160. Vector m_vecEyeSpeed;
  161. //float m_flHandbrakeTime; // handbrake after the fact to keep vehicles from rolling
  162. //bool m_bInitialHandbrake;
  163. bool m_bForcedExit;
  164. int m_nGunRefAttachment;
  165. int m_nGunBarrelAttachment;
  166. float m_aimYaw;
  167. float m_aimPitch;
  168. float m_flChargeRemainder;
  169. float m_flDrainRemainder;
  170. int m_nGunState;
  171. float m_flNextHeavyShotTime;
  172. float m_flNextGunShakeTime;
  173. CNetworkVar( int, m_nAmmoCount );
  174. CNetworkVar( bool, m_bHeadlightIsOn );
  175. EHANDLE m_hAvoidSphere;
  176. int m_nSplashAttachment;
  177. float m_flPrevThrottle; // Throttle during last think. Used for detecting state changes.
  178. float m_flSpinRate; // Current rate of spin of propeller: 0 = min, 1.0 = max
  179. float m_flTargetSpinRate; // Target rate of spin of propeller: 0 = min, 1.0 = max
  180. float m_flPropTime; // Time to turn on/off the prop.
  181. float m_flBlurTime; // Time to turn on/off the blur.
  182. CSoundPatch *m_pFanSound;
  183. CSoundPatch *m_pFanMaxSpeedSound;
  184. CSoundPatch *m_pEngineSound;
  185. CSoundPatch *m_pWaterFastSound;
  186. CSoundPatch *m_pWaterStoppedSound;
  187. CSoundPatch *m_pGunFiringSound;
  188. float m_flEngineIdleTime; // Time to start playing the engine's idle sound.
  189. float m_flEngineDuckTime; // Time to reduce the volume of the engine's idle sound.
  190. bool m_bFadeOutFan; // Fade out fan sound after cruising at max speed for a while.
  191. int m_nPrevWaterLevel; // Used for detecting transitions into/out of water.
  192. float m_flWaterStoppedPitchTime; // Time to pitch shift the water stopped sound.
  193. float m_flLastImpactEffectTime;
  194. int m_iNumberOfEntries;
  195. IPhysicsConstraint *m_pAntiFlipConstraint; // A ragdoll constraint that prevents us from flipping.
  196. CHandle<CEntityBlocker> m_hPlayerBlocker;
  197. CNetworkVar( Vector, m_vecPhysVelocity );
  198. CNetworkVar( int, m_nExactWaterLevel );
  199. IMPLEMENT_NETWORK_VAR_FOR_DERIVED( m_nWaterLevel );
  200. };
  201. IMPLEMENT_SERVERCLASS_ST( CPropAirboat, DT_PropAirboat )
  202. SendPropBool( SENDINFO( m_bHeadlightIsOn ) ),
  203. SendPropInt( SENDINFO( m_nAmmoCount ), 9 ),
  204. SendPropInt( SENDINFO( m_nExactWaterLevel ) ),
  205. SendPropInt( SENDINFO( m_nWaterLevel ) ),
  206. SendPropVector( SENDINFO( m_vecPhysVelocity ) ),
  207. END_SEND_TABLE();
  208. LINK_ENTITY_TO_CLASS( prop_vehicle_airboat, CPropAirboat );
  209. BEGIN_DATADESC( CPropAirboat )
  210. DEFINE_FIELD( m_vecLastEyePos, FIELD_POSITION_VECTOR ),
  211. DEFINE_FIELD( m_vecLastEyeTarget, FIELD_POSITION_VECTOR ),
  212. DEFINE_FIELD( m_vecEyeSpeed, FIELD_VECTOR ),
  213. // DEFINE_FIELD( m_flHandbrakeTime, FIELD_TIME ),
  214. // DEFINE_FIELD( m_bInitialHandbrake,FIELD_BOOLEAN ),
  215. // DEFINE_FIELD( m_nGunRefAttachment, FIELD_INTEGER ),
  216. // DEFINE_FIELD( m_nGunBarrelAttachment, FIELD_INTEGER ),
  217. DEFINE_FIELD( m_aimYaw, FIELD_FLOAT ),
  218. DEFINE_FIELD( m_aimPitch, FIELD_FLOAT ),
  219. DEFINE_FIELD( m_flChargeRemainder, FIELD_FLOAT ),
  220. DEFINE_FIELD( m_flDrainRemainder, FIELD_FLOAT ),
  221. DEFINE_FIELD( m_nGunState, FIELD_INTEGER ),
  222. DEFINE_FIELD( m_flNextHeavyShotTime, FIELD_TIME ),
  223. DEFINE_FIELD( m_flNextGunShakeTime, FIELD_TIME ),
  224. DEFINE_FIELD( m_nAmmoCount, FIELD_INTEGER ),
  225. DEFINE_FIELD( m_bHeadlightIsOn, FIELD_BOOLEAN ),
  226. DEFINE_FIELD( m_hAvoidSphere, FIELD_EHANDLE ),
  227. // DEFINE_FIELD( m_nSplashAttachment, FIELD_INTEGER ),
  228. DEFINE_FIELD( m_hPlayerBlocker, FIELD_EHANDLE ),
  229. DEFINE_FIELD( m_vecPhysVelocity, FIELD_VECTOR ),
  230. DEFINE_FIELD( m_nExactWaterLevel, FIELD_INTEGER ),
  231. DEFINE_FIELD( m_flPrevThrottle, FIELD_FLOAT ),
  232. DEFINE_FIELD( m_flSpinRate, FIELD_FLOAT ),
  233. DEFINE_FIELD( m_flTargetSpinRate, FIELD_FLOAT ),
  234. DEFINE_FIELD( m_flPropTime, FIELD_TIME ),
  235. DEFINE_FIELD( m_flBlurTime, FIELD_TIME ),
  236. DEFINE_FIELD( m_bForcedExit, FIELD_BOOLEAN ),
  237. DEFINE_SOUNDPATCH( m_pFanSound ),
  238. DEFINE_SOUNDPATCH( m_pFanMaxSpeedSound ),
  239. DEFINE_SOUNDPATCH( m_pEngineSound ),
  240. DEFINE_SOUNDPATCH( m_pWaterFastSound ),
  241. DEFINE_SOUNDPATCH( m_pWaterStoppedSound ),
  242. DEFINE_SOUNDPATCH( m_pGunFiringSound ),
  243. DEFINE_PHYSPTR( m_pAntiFlipConstraint ),
  244. DEFINE_FIELD( m_flEngineIdleTime, FIELD_TIME ),
  245. DEFINE_FIELD( m_flEngineDuckTime, FIELD_TIME ),
  246. DEFINE_FIELD( m_bFadeOutFan, FIELD_BOOLEAN ),
  247. DEFINE_FIELD( m_nPrevWaterLevel, FIELD_INTEGER ),
  248. DEFINE_FIELD( m_flWaterStoppedPitchTime, FIELD_TIME ),
  249. DEFINE_FIELD( m_flLastImpactEffectTime, FIELD_TIME ),
  250. DEFINE_FIELD( m_iNumberOfEntries, FIELD_INTEGER ),
  251. DEFINE_INPUTFUNC( FIELD_BOOLEAN, "EnableGun", InputEnableGun ),
  252. DEFINE_INPUTFUNC( FIELD_VOID, "StartRotorWashForces", InputStartRotorWashForces ),
  253. DEFINE_INPUTFUNC( FIELD_VOID, "StopRotorWashForces", InputStopRotorWashForces ),
  254. DEFINE_INPUTFUNC( FIELD_VOID, "ExitVehicle", InputExitVehicle ),
  255. DEFINE_INPUTFUNC( FIELD_VOID, "Wake", InputWake ),
  256. END_DATADESC()
  257. //-----------------------------------------------------------------------------
  258. // Purpose:
  259. //-----------------------------------------------------------------------------
  260. void CPropAirboat::Precache( void )
  261. {
  262. BaseClass::Precache();
  263. PrecacheScriptSound( "Airboat_engine_stop" );
  264. PrecacheScriptSound( "Airboat_engine_start" );
  265. PrecacheScriptSound( "Airboat.FireGunHeavy" );
  266. PrecacheScriptSound( "Airboat.FireGunRevDown");
  267. PrecacheScriptSound( "Airboat_engine_idle" );
  268. PrecacheScriptSound( "Airboat_engine_fullthrottle" );
  269. PrecacheScriptSound( "Airboat_fan_idle" );
  270. PrecacheScriptSound( "Airboat_fan_fullthrottle" );
  271. PrecacheScriptSound( "Airboat_water_stopped" );
  272. PrecacheScriptSound( "Airboat_water_fast" );
  273. PrecacheScriptSound( "Airboat_impact_splash" );
  274. PrecacheScriptSound( "Airboat_impact_hard" );
  275. PrecacheScriptSound( "Airboat_headlight_on" );
  276. PrecacheScriptSound( "Airboat_headlight_off" );
  277. PrecacheScriptSound( "Airboat.FireGunLoop" );
  278. PrecacheMaterial( "effects/splashwake1" );
  279. PrecacheMaterial( "effects/splashwake4" );
  280. }
  281. //-----------------------------------------------------------------------------
  282. // Purpose:
  283. //-----------------------------------------------------------------------------
  284. void CPropAirboat::Spawn( void )
  285. {
  286. m_nAmmoCount = m_bHasGun ? 0 : -1;
  287. m_hAvoidSphere = CreateHelicopterAvoidanceSphere( this, 0, 50.0f, false );
  288. m_flLastImpactEffectTime = -1;
  289. m_iNumberOfEntries = 0;
  290. // Setup vehicle as a ray-cast airboat.
  291. SetVehicleType( VEHICLE_TYPE_AIRBOAT_RAYCAST );
  292. SetCollisionGroup( COLLISION_GROUP_VEHICLE );
  293. BaseClass::Spawn();
  294. AddSolidFlags( FSOLID_NOT_STANDABLE );
  295. SetAnimatedEveryTick( true );
  296. // Handbrake data.
  297. //m_flHandbrakeTime = gpGlobals->curtime + 0.1;
  298. //m_bInitialHandbrake = false;
  299. m_VehiclePhysics.SetHasBrakePedal( false );
  300. m_flMinimumSpeedToEnterExit = AIRBOAT_LOCK_SPEED;
  301. m_takedamage = DAMAGE_EVENTS_ONLY;
  302. SetBodygroup(AIRBOAT_BODYGROUP_GUN, m_bHasGun);
  303. SetBodygroup(AIRBOAT_BODYGROUP_PROP, true);
  304. SetPoseParameter( AIRBOAT_GUN_YAW, 0 );
  305. SetPoseParameter( AIRBOAT_GUN_PITCH, 0 );
  306. SetPoseParameter( AIRBOAT_FRAME_FLEX_LEFT, 0 );
  307. SetPoseParameter( AIRBOAT_FRAME_FLEX_RIGHT, 0 );
  308. m_aimYaw = 0;
  309. m_aimPitch = 0;
  310. m_bUnableToFire = true;
  311. m_nGunState = GUN_STATE_IDLE;
  312. SetPoseParameter( "Steer_Shock", 0.0f );
  313. // Get the physics object so we can adjust the buoyancy.
  314. IPhysicsObject *pPhysAirboat = VPhysicsGetObject();
  315. if ( pPhysAirboat )
  316. {
  317. pPhysAirboat->SetBuoyancyRatio( 0.0f );
  318. PhysSetGameFlags( pPhysAirboat, FVPHYSICS_HEAVY_OBJECT );
  319. }
  320. //CreateAntiFlipConstraint();
  321. }
  322. //-----------------------------------------------------------------------------
  323. // Purpose: Create a ragdoll constraint that prevents us from flipping.
  324. //-----------------------------------------------------------------------------
  325. void CPropAirboat::CreateAntiFlipConstraint()
  326. {
  327. constraint_ragdollparams_t ragdoll;
  328. ragdoll.Defaults();
  329. // Don't prevent the boat from moving, just flipping.
  330. ragdoll.onlyAngularLimits = true;
  331. // Put the ragdoll constraint in the space of the airboat.
  332. SetIdentityMatrix( ragdoll.constraintToAttached );
  333. BuildObjectRelativeXform( g_PhysWorldObject, VPhysicsGetObject(), ragdoll.constraintToReference );
  334. ragdoll.axes[0].minRotation = -100;
  335. ragdoll.axes[0].maxRotation = 100;
  336. ragdoll.axes[1].minRotation = -100;
  337. ragdoll.axes[1].maxRotation = 100;
  338. ragdoll.axes[2].minRotation = -180;
  339. ragdoll.axes[2].maxRotation = 180;
  340. m_pAntiFlipConstraint = physenv->CreateRagdollConstraint( g_PhysWorldObject, VPhysicsGetObject(), NULL, ragdoll );
  341. //NDebugOverlay::Cross3DOriented( ragdoll.constraintToReference, 128, 255, true, 100 );
  342. }
  343. //-----------------------------------------------------------------------------
  344. // Attachment indices
  345. //-----------------------------------------------------------------------------
  346. void CPropAirboat::UpdateOnRemove()
  347. {
  348. BaseClass::UpdateOnRemove();
  349. if ( m_hAvoidSphere )
  350. {
  351. UTIL_Remove( m_hAvoidSphere );
  352. m_hAvoidSphere = NULL;
  353. }
  354. }
  355. //-----------------------------------------------------------------------------
  356. // Attachment indices
  357. //-----------------------------------------------------------------------------
  358. void CPropAirboat::Activate()
  359. {
  360. BaseClass::Activate();
  361. m_nGunRefAttachment = LookupAttachment( "gun" );
  362. m_nGunBarrelAttachment = LookupAttachment( "muzzle" );
  363. m_nSplashAttachment = LookupAttachment( "splash_pt" );
  364. CreateSounds();
  365. CBaseServerVehicle *pServerVehicle = dynamic_cast<CBaseServerVehicle *>(GetServerVehicle());
  366. if ( pServerVehicle )
  367. {
  368. if( pServerVehicle->GetPassenger() )
  369. {
  370. // If a boat comes back from a save game with a driver, make sure the engine rumble starts up.
  371. pServerVehicle->StartEngineRumble();
  372. }
  373. }
  374. //CreatePlayerBlocker();
  375. //EnablePlayerBlocker( true );
  376. }
  377. void CPropAirboat::CreatePlayerBlocker()
  378. {
  379. Assert( m_hPlayerBlocker == NULL );
  380. DestroyPlayerBlocker();
  381. m_hPlayerBlocker = CEntityBlocker::Create( GetAbsOrigin(), Vector( -84, -32, 0 ), Vector( 54, 32, 84 ), this, false );
  382. if ( m_hPlayerBlocker != NULL )
  383. {
  384. m_hPlayerBlocker->SetParent( this );
  385. m_hPlayerBlocker->SetLocalOrigin( vec3_origin );
  386. m_hPlayerBlocker->SetLocalAngles( vec3_angle );
  387. m_hPlayerBlocker->SetCollisionGroup( COLLISION_GROUP_PLAYER );
  388. m_hPlayerBlocker->AddSolidFlags( FSOLID_NOT_SOLID );
  389. }
  390. }
  391. //-----------------------------------------------------------------------------
  392. // Purpose:
  393. //-----------------------------------------------------------------------------
  394. void CPropAirboat::DestroyPlayerBlocker()
  395. {
  396. if ( m_hPlayerBlocker != NULL )
  397. {
  398. UTIL_Remove( m_hPlayerBlocker );
  399. }
  400. m_hPlayerBlocker = NULL;
  401. }
  402. //-----------------------------------------------------------------------------
  403. // Purpose:
  404. // Input : bEnable -
  405. //-----------------------------------------------------------------------------
  406. void CPropAirboat::EnablePlayerBlocker( bool bEnable )
  407. {
  408. if ( m_hPlayerBlocker != NULL )
  409. {
  410. if ( bEnable )
  411. {
  412. m_hPlayerBlocker->RemoveSolidFlags( FSOLID_NOT_SOLID );
  413. }
  414. else
  415. {
  416. m_hPlayerBlocker->AddSolidFlags( FSOLID_NOT_SOLID );
  417. }
  418. }
  419. }
  420. //-----------------------------------------------------------------------------
  421. // Update the weapon sounds
  422. //-----------------------------------------------------------------------------
  423. #define MIN_CHARGE_SOUND 0.4f
  424. #define MIN_PITCH_CHANGE ( MIN_CHARGE_SOUND + ( ( 1.0f - MIN_CHARGE_SOUND ) / 3.0f ) )
  425. #define VOLUME_CHANGE_TIME 0.5f
  426. void CPropAirboat::UpdateWeaponSound()
  427. {
  428. if ( HasGun() )
  429. {
  430. CSoundEnvelopeController *pController = &CSoundEnvelopeController::GetController();
  431. float flVolume = pController->SoundGetVolume( m_pGunFiringSound );
  432. if ( (m_nGunState == GUN_STATE_IDLE) || (m_nAmmoCount == 0) )
  433. {
  434. if ( flVolume != 0.0f )
  435. {
  436. pController->SoundChangeVolume( m_pGunFiringSound, 0.0f, 0.01f );
  437. }
  438. }
  439. else
  440. {
  441. if ( flVolume != 1.0f )
  442. {
  443. pController->SoundChangeVolume( m_pGunFiringSound, 1.0f, 0.01f );
  444. }
  445. }
  446. }
  447. }
  448. //-----------------------------------------------------------------------------
  449. // Purpose: Force the player to exit the vehicle.
  450. //-----------------------------------------------------------------------------
  451. void CPropAirboat::InputExitVehicle( inputdata_t &inputdata )
  452. {
  453. m_bForcedExit = true;
  454. }
  455. //-----------------------------------------------------------------------------
  456. // Purpose: Force the airboat to wake up. This was needed to fix a last-minute
  457. // bug for the XBox -- the airboat didn't fall with the platform
  458. // in d1_canals_10b.
  459. //-----------------------------------------------------------------------------
  460. void CPropAirboat::InputWake( inputdata_t &inputdata )
  461. {
  462. VPhysicsGetObject()->Wake();
  463. }
  464. //-----------------------------------------------------------------------------
  465. // Purpose: Input handler to enable or disable the airboat's mounted gun.
  466. //-----------------------------------------------------------------------------
  467. void CPropAirboat::InputEnableGun( inputdata_t &inputdata )
  468. {
  469. m_bHasGun = inputdata.value.Bool();
  470. SetBodygroup(AIRBOAT_BODYGROUP_GUN, m_bHasGun);
  471. // When enabling the gun, give full ammo
  472. if ( m_bHasGun )
  473. {
  474. m_nAmmoCount = sk_airboat_max_ammo.GetInt();
  475. }
  476. }
  477. //-----------------------------------------------------------------------------
  478. // Purpose: Input handler to enable or disable the airboat's mounted gun.
  479. //-----------------------------------------------------------------------------
  480. void CPropAirboat::InputStartRotorWashForces( inputdata_t &inputdata )
  481. {
  482. RemoveEFlags( EFL_NO_ROTORWASH_PUSH );
  483. }
  484. //-----------------------------------------------------------------------------
  485. // Purpose: Input handler to enable or disable the airboat's mounted gun.
  486. //-----------------------------------------------------------------------------
  487. void CPropAirboat::InputStopRotorWashForces( inputdata_t &inputdata )
  488. {
  489. AddEFlags( EFL_NO_ROTORWASH_PUSH );
  490. }
  491. //-----------------------------------------------------------------------------
  492. // Creating vphysics
  493. //-----------------------------------------------------------------------------
  494. void CPropAirboat::OnRestore()
  495. {
  496. BaseClass::OnRestore();
  497. IPhysicsObject *pPhysAirboat = VPhysicsGetObject();
  498. if ( pPhysAirboat )
  499. {
  500. pPhysAirboat->SetBuoyancyRatio( 0.0f );
  501. PhysSetGameFlags( pPhysAirboat, FVPHYSICS_HEAVY_OBJECT );
  502. }
  503. // If the player's in the vehicle, NPCs should ignore it
  504. if ( GetDriver() )
  505. {
  506. SetNavIgnore();
  507. }
  508. }
  509. //-----------------------------------------------------------------------------
  510. // Used for navigation
  511. //-----------------------------------------------------------------------------
  512. void CPropAirboat::EnterVehicle( CBaseCombatCharacter *pPlayer )
  513. {
  514. BaseClass::EnterVehicle( pPlayer );
  515. //EnablePlayerBlocker( false );
  516. // NPCs like manhacks should try to hit us
  517. SetNavIgnore();
  518. // Play the engine start sound.
  519. float flDuration;
  520. EmitSound( "Airboat_engine_start", 0.0, &flDuration );
  521. m_VehiclePhysics.TurnOn();
  522. // Start playing the engine's idle sound as the startup sound finishes.
  523. m_flEngineIdleTime = gpGlobals->curtime + flDuration - 0.1;
  524. }
  525. //-----------------------------------------------------------------------------
  526. // Purpose: Called when exiting, just before playing the exit animation.
  527. //-----------------------------------------------------------------------------
  528. void CPropAirboat::PreExitVehicle( CBaseCombatCharacter *pPlayer, int nRole )
  529. {
  530. if ( HeadlightIsOn() )
  531. {
  532. HeadlightTurnOff();
  533. }
  534. // Stop shooting.
  535. m_nGunState = GUN_STATE_IDLE;
  536. CBaseEntity *pDriver = GetDriver();
  537. CBasePlayer *pPlayerDriver;
  538. if( pDriver && pDriver->IsPlayer() )
  539. {
  540. pPlayerDriver = dynamic_cast<CBasePlayer*>(pDriver);
  541. if( pPlayerDriver )
  542. {
  543. pPlayerDriver->RumbleEffect( RUMBLE_AIRBOAT_GUN, 0, RUMBLE_FLAG_STOP );
  544. }
  545. }
  546. BaseClass::PreExitVehicle( pPlayer, nRole );
  547. }
  548. //-----------------------------------------------------------------------------
  549. // Purpose: Called when exiting, after completing the exit animation.
  550. // Input : iRole -
  551. //-----------------------------------------------------------------------------
  552. void CPropAirboat::ExitVehicle( int nRole )
  553. {
  554. CBaseEntity *pDriver = GetDriver();
  555. //EnablePlayerBlocker( true );
  556. BaseClass::ExitVehicle( nRole );
  557. if (!pDriver)
  558. return;
  559. #if 0
  560. // On ORANGE BOX this is causing a big blank box to show up, which is worse
  561. // than the HUD hint persisting for a little while, so don't do it. (sjb)
  562. // clear the hint
  563. UTIL_HudHintText( pDriver, "" );
  564. #endif
  565. // NPCs like manhacks should try to avoid us again
  566. ClearNavIgnore();
  567. // Play the engine shutoff sound.
  568. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  569. CPASAttenuationFilter filter( this );
  570. EmitSound_t ep;
  571. ep.m_nChannel = CHAN_BODY;
  572. ep.m_pSoundName = "Airboat_engine_stop";
  573. ep.m_flVolume = controller.SoundGetVolume( m_pEngineSound );
  574. ep.m_SoundLevel = SNDLVL_NORM;
  575. ep.m_nPitch = controller.SoundGetPitch( m_pEngineSound );
  576. EmitSound( filter, entindex(), ep );
  577. m_VehiclePhysics.TurnOff();
  578. // Shut off the airboat sounds.
  579. controller.SoundChangeVolume( m_pEngineSound, 0.0, 0.0 );
  580. controller.SoundChangeVolume( m_pFanSound, 0.0, 0.0 );
  581. controller.SoundChangeVolume( m_pFanMaxSpeedSound, 0.0, 0.0 );
  582. controller.SoundChangeVolume( m_pWaterStoppedSound, 0.0, 0.0 );
  583. controller.SoundChangeVolume( m_pWaterFastSound, 0.0, 0.0 );
  584. controller.SoundChangeVolume( m_pGunFiringSound, 0.0, 0.0 );
  585. }
  586. //-----------------------------------------------------------------------------
  587. // Purpose:
  588. //-----------------------------------------------------------------------------
  589. void CPropAirboat::HeadlightTurnOn( void )
  590. {
  591. EmitSound( "Airboat_headlight_on" );
  592. m_bHeadlightIsOn = true;
  593. }
  594. //-----------------------------------------------------------------------------
  595. // Purpose:
  596. //-----------------------------------------------------------------------------
  597. void CPropAirboat::HeadlightTurnOff( void )
  598. {
  599. EmitSound( "Airboat_headlight_off" );
  600. m_bHeadlightIsOn = false;
  601. }
  602. //-----------------------------------------------------------------------------
  603. // position to shoot at
  604. //-----------------------------------------------------------------------------
  605. Vector CPropAirboat::BodyTarget( const Vector &posSrc, bool bNoisy )
  606. {
  607. Vector vecPosition;
  608. QAngle angles;
  609. if ( GetServerVehicle()->GetPassenger() )
  610. {
  611. // FIXME: Reconcile this with other functions that store a cached version of the results here?
  612. GetServerVehicle()->GetVehicleViewPosition( VEHICLE_ROLE_DRIVER, &vecPosition, &angles );
  613. }
  614. else
  615. {
  616. vecPosition = WorldSpaceCenter();
  617. }
  618. return vecPosition;
  619. }
  620. //-----------------------------------------------------------------------------
  621. // Smoothed velocity
  622. //-----------------------------------------------------------------------------
  623. #define SMOOTHED_MIN_VELOCITY 75.0f
  624. #define SMOOTHED_MAX_VELOCITY 150.0f
  625. Vector CPropAirboat::GetSmoothedVelocity( void )
  626. {
  627. // If we're going too slow, return the forward direction as the velocity
  628. // for NPC prediction purposes
  629. Vector vecSmoothedVelocity = BaseClass::GetSmoothedVelocity();
  630. float flSpeed = vecSmoothedVelocity.Length();
  631. if ( flSpeed >= SMOOTHED_MAX_VELOCITY )
  632. return vecSmoothedVelocity;
  633. Vector vecForward;
  634. GetVectors( &vecForward, NULL, NULL );
  635. vecForward *= MAX( flSpeed, 1.0f );
  636. if ( flSpeed <= SMOOTHED_MIN_VELOCITY )
  637. return vecForward;
  638. float flBlend = SimpleSplineRemapVal( flSpeed, SMOOTHED_MIN_VELOCITY, SMOOTHED_MAX_VELOCITY, 0.0f, 1.0f );
  639. VectorLerp( vecForward, vecSmoothedVelocity, flBlend, vecSmoothedVelocity );
  640. return vecSmoothedVelocity;
  641. }
  642. //-----------------------------------------------------------------------------
  643. // Purpose:
  644. //-----------------------------------------------------------------------------
  645. void CPropAirboat::TraceAttack( const CTakeDamageInfo &inputInfo, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  646. {
  647. CTakeDamageInfo info = inputInfo;
  648. if ( ptr->hitbox != VEHICLE_HITBOX_DRIVER )
  649. {
  650. if ( inputInfo.GetDamageType() & DMG_BULLET )
  651. {
  652. info.ScaleDamage( 0.0001 );
  653. }
  654. }
  655. BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator );
  656. }
  657. //-----------------------------------------------------------------------------
  658. // Purpose:
  659. // Input : &info -
  660. // &vecEnd -
  661. // *pTraceFilter -
  662. // *pVecTracerDest -
  663. // Output : Returns true on success, false on failure.
  664. //-----------------------------------------------------------------------------
  665. bool CPropAirboat::ShouldDrawWaterImpacts( void )
  666. {
  667. // The airboat spits out so much crap that we need to do cheaper versions
  668. // of the impact effects. Also, we need to do less of them.
  669. if ( m_flLastImpactEffectTime >= gpGlobals->curtime )
  670. return false;
  671. m_flLastImpactEffectTime = gpGlobals->curtime + 0.05f;
  672. return true;
  673. }
  674. //-----------------------------------------------------------------------------
  675. // Allows the shooter to change the impact effect of his bullets
  676. //-----------------------------------------------------------------------------
  677. void CPropAirboat::DoImpactEffect( trace_t &tr, int nDamageType )
  678. {
  679. // The airboat spits out so much crap that we need to do cheaper versions
  680. // of the impact effects. Also, we need to do less of them.
  681. if ( m_flLastImpactEffectTime == gpGlobals->curtime )
  682. return;
  683. // Randomly drop out
  684. if ( random->RandomInt( 0, 5 ) )
  685. return;
  686. m_flLastImpactEffectTime = gpGlobals->curtime;
  687. UTIL_ImpactTrace( &tr, nDamageType, "AirboatGunImpact" );
  688. }
  689. //-----------------------------------------------------------------------------
  690. // Purpose:
  691. //-----------------------------------------------------------------------------
  692. int CPropAirboat::OnTakeDamage( const CTakeDamageInfo &info )
  693. {
  694. // Do scaled up physics damage to the airboat
  695. CTakeDamageInfo physDmg = info;
  696. physDmg.ScaleDamage( 5 );
  697. if ( physDmg.GetDamageType() & DMG_BLAST )
  698. {
  699. physDmg.SetDamageForce( info.GetDamageForce() * 10 );
  700. }
  701. VPhysicsTakeDamage( physDmg );
  702. // Check to do damage to driver
  703. if ( m_hPlayer != NULL )
  704. {
  705. // Don't pass along physics damage
  706. if ( info.GetDamageType() & (DMG_CRUSH|DMG_RADIATION) )
  707. return 0;
  708. // Take the damage (strip out the DMG_BLAST)
  709. CTakeDamageInfo playerDmg = info;
  710. // Mark that we're passing it to the player so the base player accepts the damage
  711. playerDmg.SetDamageType( info.GetDamageType() | DMG_VEHICLE );
  712. // Deal the damage to the passenger
  713. m_hPlayer->TakeDamage( playerDmg );
  714. }
  715. return 0;
  716. }
  717. //-----------------------------------------------------------------------------
  718. // Scraping noises for the various things we drive on.
  719. //-----------------------------------------------------------------------------
  720. void CPropAirboat::VPhysicsFriction( IPhysicsObject *pObject, float energy, int surfaceProps, int surfacePropsHit )
  721. {
  722. // don't make noise for hidden/invisible/sky materials
  723. const surfacedata_t *phit = physprops->GetSurfaceData( surfacePropsHit );
  724. const surfacedata_t *pprops = physprops->GetSurfaceData( surfaceProps );
  725. if ( phit->game.material == 'X' || pprops->game.material == 'X' )
  726. return;
  727. // FIXME: Make different scraping sounds here
  728. float flVolume = 0.3f;
  729. surfacedata_t *psurf = physprops->GetSurfaceData( surfaceProps );
  730. const char *pSoundName = physprops->GetString( psurf->sounds.scrapeRough );
  731. PhysFrictionSound( this, pObject, pSoundName, psurf->soundhandles.scrapeRough, flVolume );
  732. }
  733. //-----------------------------------------------------------------------------
  734. // Purpose: Aim Gun at a target position.
  735. //-----------------------------------------------------------------------------
  736. // This fixes an optimizer bug that was causing targetYaw and targetPitch to
  737. // always be reported as clamped, thus disabling the gun. Ack!
  738. #pragma optimize("", off)
  739. void CPropAirboat::AimGunAt( const Vector &aimPos, float flInterval )
  740. {
  741. matrix3x4_t gunMatrix;
  742. GetAttachment( m_nGunRefAttachment, gunMatrix );
  743. // transform the target position into gun space
  744. Vector localTargetPosition;
  745. VectorITransform( aimPos, gunMatrix, localTargetPosition );
  746. VectorNormalize( localTargetPosition );
  747. m_bUnableToFire = false;
  748. m_vecGunCrosshair = aimPos;
  749. // do a look at in gun space (essentially a delta-lookat)
  750. QAngle localTargetAngles;
  751. VectorAngles( localTargetPosition, localTargetAngles );
  752. // convert to +/- 180 degrees
  753. localTargetAngles.x = UTIL_AngleDiff( localTargetAngles.x, 0 );
  754. localTargetAngles.y = UTIL_AngleDiff( localTargetAngles.y, 0 );
  755. float targetYaw = m_aimYaw + localTargetAngles.y;
  756. float targetPitch = m_aimPitch + localTargetAngles.x;
  757. // Constrain our angles
  758. float newTargetYaw = clamp( targetYaw, -CANNON_MAX_RIGHT_YAW, CANNON_MAX_LEFT_YAW );
  759. float newTargetPitch = clamp( targetPitch, -CANNON_MAX_UP_PITCH, CANNON_MAX_DOWN_PITCH );
  760. // If the angles have been clamped, we're looking outside of our valid range
  761. if ( ( newTargetYaw != targetYaw ) || ( newTargetPitch != targetPitch ) )
  762. {
  763. m_bUnableToFire = true;
  764. }
  765. targetYaw = newTargetYaw;
  766. targetPitch = newTargetPitch;
  767. m_aimYaw = targetYaw;
  768. m_aimPitch = targetPitch;
  769. SetPoseParameter( AIRBOAT_GUN_YAW, m_aimYaw);
  770. SetPoseParameter( AIRBOAT_GUN_PITCH, m_aimPitch );
  771. InvalidateBoneCache();
  772. // read back to avoid drift when hitting limits
  773. // as long as the velocity is less than the delta between the limit and 180, this is fine.
  774. m_aimPitch = GetPoseParameter( AIRBOAT_GUN_PITCH );
  775. m_aimYaw = GetPoseParameter( AIRBOAT_GUN_YAW );
  776. }
  777. #pragma optimize("", on)
  778. //-----------------------------------------------------------------------------
  779. // Removes the ammo...
  780. //-----------------------------------------------------------------------------
  781. void CPropAirboat::RemoveAmmo( float flAmmoAmount )
  782. {
  783. m_flDrainRemainder += flAmmoAmount;
  784. int nAmmoToRemove = (int)m_flDrainRemainder;
  785. m_flDrainRemainder -= nAmmoToRemove;
  786. m_nAmmoCount -= nAmmoToRemove;
  787. if ( m_nAmmoCount < 0 )
  788. {
  789. m_nAmmoCount = 0;
  790. m_flDrainRemainder = 0.0f;
  791. }
  792. }
  793. //-----------------------------------------------------------------------------
  794. // Recharges the ammo...
  795. //-----------------------------------------------------------------------------
  796. void CPropAirboat::RechargeAmmo(void)
  797. {
  798. if ( !m_bHasGun )
  799. {
  800. m_nAmmoCount = -1;
  801. return;
  802. }
  803. int nMaxAmmo = sk_airboat_max_ammo.GetInt();
  804. if ( m_nAmmoCount == nMaxAmmo )
  805. return;
  806. float flRechargeRate = sk_airboat_recharge_rate.GetInt();
  807. float flChargeAmount = flRechargeRate * gpGlobals->frametime;
  808. if ( m_flDrainRemainder != 0.0f )
  809. {
  810. if ( m_flDrainRemainder >= flChargeAmount )
  811. {
  812. m_flDrainRemainder -= flChargeAmount;
  813. return;
  814. }
  815. else
  816. {
  817. flChargeAmount -= m_flDrainRemainder;
  818. m_flDrainRemainder = 0.0f;
  819. }
  820. }
  821. m_flChargeRemainder += flChargeAmount;
  822. int nAmmoToAdd = (int)m_flChargeRemainder;
  823. m_flChargeRemainder -= nAmmoToAdd;
  824. m_nAmmoCount += nAmmoToAdd;
  825. if ( m_nAmmoCount > nMaxAmmo )
  826. {
  827. m_nAmmoCount = nMaxAmmo;
  828. m_flChargeRemainder = 0.0f;
  829. }
  830. }
  831. //-----------------------------------------------------------------------------
  832. // Purpose:
  833. //-----------------------------------------------------------------------------
  834. void CPropAirboat::ComputeAimPoint( Vector *pVecAimPoint )
  835. {
  836. Vector vecEyeDirection;
  837. if( g_pGameRules->GetAutoAimMode() == AUTOAIM_ON_CONSOLE )
  838. {
  839. // Use autoaim as the eye dir.
  840. autoaim_params_t params;
  841. params.m_fScale = AUTOAIM_SCALE_DEFAULT * sv_vehicle_autoaim_scale.GetFloat();
  842. params.m_fMaxDist = autoaim_max_dist.GetFloat();
  843. m_hPlayer->GetAutoaimVector( params );
  844. vecEyeDirection = params.m_vecAutoAimDir;
  845. }
  846. else
  847. {
  848. m_hPlayer->EyeVectors( &vecEyeDirection, NULL, NULL );
  849. }
  850. Vector vecEndPos;
  851. VectorMA( m_hPlayer->EyePosition(), MAX_TRACE_LENGTH, vecEyeDirection, vecEndPos );
  852. trace_t trace;
  853. UTIL_TraceLine( m_hPlayer->EyePosition(), vecEndPos, MASK_SHOT, this, COLLISION_GROUP_NONE, &trace );
  854. *pVecAimPoint = trace.endpos;
  855. }
  856. //-----------------------------------------------------------------------------
  857. // Purpose: Manages animation and sound state.
  858. //-----------------------------------------------------------------------------
  859. void CPropAirboat::Think(void)
  860. {
  861. BaseClass::Think();
  862. // set handbrake after physics sim settles down
  863. // if ( gpGlobals->curtime < m_flHandbrakeTime )
  864. // {
  865. // SetNextThink( gpGlobals->curtime );
  866. // }
  867. // else if ( !m_bInitialHandbrake ) // after initial timer expires, set the handbrake
  868. // {
  869. // m_bInitialHandbrake = true;
  870. // m_VehiclePhysics.SetHandbrake( true );
  871. // m_VehiclePhysics.Think();
  872. // }
  873. // Find the vertical extents of the boat
  874. Vector startPos, endPos;
  875. CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 1.0f ), &startPos );
  876. CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 0.0f ), &endPos );
  877. // Look for water along that volume.
  878. // Make a very vertically thin box and sweep it along the ray.
  879. Vector vecMins = CollisionProp()->OBBMins();
  880. Vector vecMaxs = CollisionProp()->OBBMaxs();
  881. vecMins.z = -0.1f;
  882. vecMaxs.z = 0.1f;
  883. trace_t tr;
  884. UTIL_TraceHull( startPos, endPos, vecMins, vecMaxs, (CONTENTS_WATER|CONTENTS_SLIME), this, COLLISION_GROUP_NONE, &tr );
  885. // If we hit something, then save off the info
  886. if ( tr.fraction != 1.0f )
  887. {
  888. m_nExactWaterLevel = tr.endpos.z;
  889. // Classify what we're in
  890. if ( tr.contents & CONTENTS_SLIME )
  891. {
  892. // We fake this value to mean type, instead of level
  893. SetWaterLevel( 2 );
  894. }
  895. else
  896. {
  897. // This simply signifies water
  898. SetWaterLevel( 1 );
  899. }
  900. }
  901. else
  902. {
  903. // Not in water
  904. SetWaterLevel( 0 );
  905. }
  906. StudioFrameAdvance();
  907. // If the enter or exit animation has finished, tell the server vehicle
  908. if ( IsSequenceFinished() && ( m_bEnterAnimOn || m_bExitAnimOn ) )
  909. {
  910. // The first few time we get into the jeep, print the jeep help
  911. if ( m_iNumberOfEntries < hud_airboathint_numentries.GetInt() && !m_bExitAnimOn )
  912. {
  913. UTIL_HudHintText( m_hPlayer, "#Valve_Hint_BoatKeys" );
  914. m_iNumberOfEntries++;
  915. }
  916. GetServerVehicle()->HandleEntryExitFinish( m_bExitAnimOn, false );
  917. // Start the vehicle's idle animation
  918. ResetSequence(LookupSequence("propeller_spin1"));
  919. ResetClientsideFrame();
  920. }
  921. // FIXME: Slam the crosshair every think -- if we don't do this it disappears randomly, never to return.
  922. if ( ( m_hPlayer.Get() != NULL ) && !( m_bEnterAnimOn || m_bExitAnimOn ) )
  923. {
  924. m_hPlayer->m_Local.m_iHideHUD &= ~HIDEHUD_VEHICLE_CROSSHAIR;
  925. }
  926. // Aim the gun
  927. if ( HasGun() && m_hPlayer.Get() && !m_bEnterAnimOn && !m_bExitAnimOn )
  928. {
  929. Vector vecAimPoint;
  930. ComputeAimPoint( &vecAimPoint );
  931. AimGunAt( vecAimPoint, gpGlobals->frametime );
  932. }
  933. if ( ShouldForceExit() )
  934. {
  935. ClearForcedExit();
  936. m_hPlayer->LeaveVehicle();
  937. }
  938. if ( HasGun() && ( m_nGunState == GUN_STATE_IDLE ) )
  939. {
  940. RechargeAmmo();
  941. }
  942. UpdateSound();
  943. UpdatePropeller();
  944. UpdateGauge();
  945. }
  946. //-----------------------------------------------------------------------------
  947. // Purpose:
  948. //-----------------------------------------------------------------------------
  949. void CPropAirboat::UpdatePropeller()
  950. {
  951. if ((m_bExitAnimOn) || (m_bEnterAnimOn))
  952. return;
  953. #define SPIN_RATE_MED 0.2
  954. #define SPIN_RATE_HIGH 0.6
  955. // Determine target spin rate from throttle.
  956. float flTargetSpinRate = m_flThrottle;
  957. if ((flTargetSpinRate == 0) && (m_hPlayer))
  958. {
  959. // Always keep the fan moving a little when we have a driver.
  960. flTargetSpinRate = 0.2;
  961. }
  962. // Save the current spin rate to determine state transitions.
  963. float flPrevSpinRate = m_flSpinRate;
  964. // Determine new spin rate,
  965. if (m_flSpinRate < flTargetSpinRate)
  966. {
  967. if (flTargetSpinRate > 0)
  968. {
  969. m_flSpinRate += gpGlobals->frametime * 1.0;
  970. }
  971. else
  972. {
  973. m_flSpinRate += gpGlobals->frametime * 0.4;
  974. }
  975. if (m_flSpinRate > flTargetSpinRate)
  976. {
  977. m_flSpinRate = flTargetSpinRate;
  978. }
  979. }
  980. else if (m_flSpinRate > flTargetSpinRate)
  981. {
  982. m_flSpinRate -= gpGlobals->frametime * 0.4;
  983. if (m_flSpinRate < flTargetSpinRate)
  984. {
  985. m_flSpinRate = flTargetSpinRate;
  986. }
  987. }
  988. // Update prop & blur based on new spin rate.
  989. if (fabs(m_flSpinRate) > SPIN_RATE_HIGH)
  990. {
  991. if (fabs(flPrevSpinRate) <= SPIN_RATE_HIGH)
  992. {
  993. SetBodygroup(AIRBOAT_BODYGROUP_PROP, false);
  994. SetBodygroup(AIRBOAT_BODYGROUP_BLUR, true);
  995. SetSequence(LookupSequence("propeller_spin1"));
  996. }
  997. }
  998. else if (fabs(m_flSpinRate) > SPIN_RATE_MED)
  999. {
  1000. if ((fabs(flPrevSpinRate) <= SPIN_RATE_MED) || (fabs(flPrevSpinRate) > SPIN_RATE_HIGH))
  1001. {
  1002. SetBodygroup(AIRBOAT_BODYGROUP_PROP, true);
  1003. SetBodygroup(AIRBOAT_BODYGROUP_BLUR, true);
  1004. SetSequence(LookupSequence("propeller_spin1"));
  1005. }
  1006. }
  1007. else
  1008. {
  1009. if (fabs(flPrevSpinRate) > SPIN_RATE_MED)
  1010. {
  1011. SetBodygroup(AIRBOAT_BODYGROUP_PROP, true);
  1012. SetBodygroup(AIRBOAT_BODYGROUP_BLUR, false);
  1013. SetSequence(LookupSequence("propeller_spin1"));
  1014. }
  1015. }
  1016. SetPlaybackRate( m_flSpinRate );
  1017. m_flPrevThrottle = m_flThrottle;
  1018. }
  1019. //-----------------------------------------------------------------------------
  1020. // Purpose: Updates the speedometer.
  1021. //-----------------------------------------------------------------------------
  1022. void CPropAirboat::UpdateGauge()
  1023. {
  1024. CFourWheelVehiclePhysics *pPhysics = GetPhysics();
  1025. int speed = pPhysics->GetSpeed();
  1026. int maxSpeed = pPhysics->GetMaxSpeed();
  1027. float speedRatio = clamp( (float)speed / (float)maxSpeed, 0, 1 );
  1028. SetPoseParameter( "Gauge", speedRatio );
  1029. }
  1030. //-----------------------------------------------------------------------------
  1031. // Purpose:
  1032. //-----------------------------------------------------------------------------
  1033. void CPropAirboat::CreateSounds()
  1034. {
  1035. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  1036. CPASAttenuationFilter filter( this );
  1037. if (!m_pEngineSound)
  1038. {
  1039. m_pEngineSound = controller.SoundCreate( filter, entindex(), "Airboat_engine_idle" );
  1040. controller.Play( m_pEngineSound, 0, 100 );
  1041. }
  1042. if (!m_pFanSound)
  1043. {
  1044. m_pFanSound = controller.SoundCreate( filter, entindex(), "Airboat_fan_idle" );
  1045. controller.Play( m_pFanSound, 0, 100 );
  1046. }
  1047. if (!m_pFanMaxSpeedSound)
  1048. {
  1049. m_pFanMaxSpeedSound = controller.SoundCreate( filter, entindex(), "Airboat_fan_fullthrottle" );
  1050. controller.Play( m_pFanMaxSpeedSound, 0, 100 );
  1051. }
  1052. if (!m_pWaterStoppedSound)
  1053. {
  1054. m_pWaterStoppedSound = controller.SoundCreate( filter, entindex(), "Airboat_water_stopped" );
  1055. controller.Play( m_pWaterStoppedSound, 0, 100 );
  1056. }
  1057. if (!m_pWaterFastSound)
  1058. {
  1059. m_pWaterFastSound = controller.SoundCreate( filter, entindex(), "Airboat_water_fast" );
  1060. controller.Play( m_pWaterFastSound, 0, 100 );
  1061. }
  1062. if (!m_pGunFiringSound)
  1063. {
  1064. m_pGunFiringSound = controller.SoundCreate( filter, entindex(), "Airboat.FireGunLoop" );
  1065. controller.Play( m_pGunFiringSound, 0, 100 );
  1066. }
  1067. }
  1068. //-----------------------------------------------------------------------------
  1069. // Purpose:
  1070. //-----------------------------------------------------------------------------
  1071. void CPropAirboat::StopLoopingSounds()
  1072. {
  1073. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  1074. controller.SoundDestroy( m_pEngineSound );
  1075. m_pEngineSound = NULL;
  1076. controller.SoundDestroy( m_pFanSound );
  1077. m_pFanSound = NULL;
  1078. controller.SoundDestroy( m_pFanMaxSpeedSound );
  1079. m_pFanMaxSpeedSound = NULL;
  1080. controller.SoundDestroy( m_pWaterStoppedSound );
  1081. m_pWaterStoppedSound = NULL;
  1082. controller.SoundDestroy( m_pWaterFastSound );
  1083. m_pWaterFastSound = NULL;
  1084. controller.SoundDestroy( m_pGunFiringSound );
  1085. m_pGunFiringSound = NULL;
  1086. BaseClass::StopLoopingSounds();
  1087. }
  1088. //-----------------------------------------------------------------------------
  1089. // Purpose: Manage the state of the engine sound.
  1090. //-----------------------------------------------------------------------------
  1091. void CPropAirboat::UpdateEngineSound( CSoundEnvelopeController &controller, float speedRatio )
  1092. {
  1093. #define ENGINE_MIN_VOLUME 0.22
  1094. #define ENGINE_MAX_VOLUME 0.62
  1095. #define ENGINE_MIN_PITCH 80
  1096. #define ENGINE_MAX_PITCH 140
  1097. #define ENGINE_DUCK_TIME 4.0
  1098. if ( controller.SoundGetVolume(m_pEngineSound ) == 0 )
  1099. {
  1100. if ( gpGlobals->curtime > m_flEngineIdleTime )
  1101. {
  1102. // If we've finished playing the engine start sound, start playing the idle sound.
  1103. controller.Play( m_pEngineSound, ENGINE_MAX_VOLUME, 100 );
  1104. // Ramp down the engine idle sound over time so that we can ramp it back up again based on speed.
  1105. controller.SoundChangeVolume( m_pEngineSound, ENGINE_MIN_VOLUME, ENGINE_DUCK_TIME );
  1106. controller.SoundChangePitch( m_pEngineSound, ENGINE_MIN_PITCH, ENGINE_DUCK_TIME );
  1107. // Reduce the volume of the engine idle sound after our ears get 'used' to it.
  1108. m_flEngineDuckTime = gpGlobals->curtime + ENGINE_DUCK_TIME;
  1109. }
  1110. }
  1111. else if ( gpGlobals->curtime > m_flEngineDuckTime )
  1112. {
  1113. controller.SoundChangeVolume( m_pEngineSound, RemapValClamped(speedRatio, 0, 1.0, ENGINE_MIN_VOLUME, ENGINE_MAX_VOLUME ), 0.0 );
  1114. controller.SoundChangePitch( m_pEngineSound, RemapValClamped( speedRatio, 0, 1.0, ENGINE_MIN_PITCH, ENGINE_MAX_PITCH ), 0 );
  1115. }
  1116. }
  1117. //-----------------------------------------------------------------------------
  1118. // Purpose:
  1119. //-----------------------------------------------------------------------------
  1120. void CPropAirboat::UpdateFanSound( CSoundEnvelopeController &controller, float speedRatio )
  1121. {
  1122. #define FAN_MIN_VOLUME 0.0
  1123. #define FAN_MAX_VOLUME 0.82
  1124. #define FAN_DUCK_VOLUME 0.22
  1125. #define FAN_CHANGE_VOLUME_TIME 1.0 // seconds over which to change the volume
  1126. #define FAN_DUCK_TIME 2.0 // seconds over which to duck the fan sound
  1127. // Manage the state of the fan sound.
  1128. if (speedRatio >= 0.8)
  1129. {
  1130. // Crossfade between a 'max speed' fan sound and the normal fan sound.
  1131. controller.SoundChangeVolume( m_pFanSound, RemapValClamped( speedRatio, 0.8, 1.0, FAN_MAX_VOLUME, FAN_MIN_VOLUME ), FAN_CHANGE_VOLUME_TIME );
  1132. controller.SoundChangeVolume( m_pFanMaxSpeedSound, RemapValClamped( speedRatio, 0.8, 1.0, FAN_MIN_VOLUME, FAN_MAX_VOLUME ), FAN_CHANGE_VOLUME_TIME );
  1133. if (!m_bFadeOutFan)
  1134. {
  1135. m_bFadeOutFan = true;
  1136. controller.SoundChangeVolume( m_pFanSound, FAN_DUCK_VOLUME, FAN_DUCK_TIME );
  1137. }
  1138. }
  1139. else
  1140. {
  1141. m_bFadeOutFan = false;
  1142. controller.SoundChangeVolume( m_pFanSound, RemapValClamped( fabs(m_flThrottle), 0, 1.0, FAN_MIN_VOLUME, FAN_MAX_VOLUME ), 0.25 );
  1143. controller.SoundChangeVolume( m_pFanMaxSpeedSound, 0.0, 0.0 );
  1144. }
  1145. controller.SoundChangePitch( m_pFanSound, 100 * (fabs(m_flThrottle) + 0.2), 0.25 );
  1146. }
  1147. //-----------------------------------------------------------------------------
  1148. // Purpose:
  1149. //-----------------------------------------------------------------------------
  1150. void CPropAirboat::UpdateWaterSound( CSoundEnvelopeController &controller, float speedRatio )
  1151. {
  1152. int nWaterLevel = GetWaterLevel();
  1153. // Manage the state of the water stopped sound (gentle lapping at the pontoons).
  1154. if ( nWaterLevel == 0 )
  1155. {
  1156. controller.SoundChangeVolume(m_pWaterStoppedSound, 0.0, 0.0);
  1157. }
  1158. else
  1159. {
  1160. if ( m_nPrevWaterLevel == 0 )
  1161. {
  1162. Vector vecVelocityWorld;
  1163. GetVelocity( &vecVelocityWorld, NULL );
  1164. if ( ( fabs( vecVelocityWorld.x ) > 400 ) || ( fabs( vecVelocityWorld.y ) > 400 ) || ( fabs( vecVelocityWorld.z ) > 400 ) )
  1165. {
  1166. // Landed in the water. Play a splash sound.
  1167. EmitSound( "Airboat_impact_splash" );
  1168. if ( fabs( vecVelocityWorld.z ) > 200 )
  1169. {
  1170. // Landed hard in the water. Play a smack sound.
  1171. EmitSound( "Airboat_impact_hard" );
  1172. }
  1173. }
  1174. }
  1175. if (speedRatio <= 0.1)
  1176. {
  1177. if (!controller.SoundGetVolume(m_pWaterStoppedSound))
  1178. {
  1179. // Fade in the water stopped sound over 2 seconds.
  1180. controller.SoundChangeVolume(m_pWaterStoppedSound, 1.0, 2.0);
  1181. m_flWaterStoppedPitchTime = gpGlobals->curtime + random->RandomFloat(1.0, 3.0);
  1182. }
  1183. else if (gpGlobals->curtime > m_flWaterStoppedPitchTime)
  1184. {
  1185. controller.SoundChangeVolume(m_pWaterStoppedSound, random->RandomFloat(0.2, 1.0), random->RandomFloat(1.0, 3.0));
  1186. controller.SoundChangePitch(m_pWaterStoppedSound, random->RandomFloat(90, 110), random->RandomFloat(1.0, 3.0));
  1187. m_flWaterStoppedPitchTime = gpGlobals->curtime + random->RandomFloat(2.0, 4.0);
  1188. }
  1189. }
  1190. else
  1191. {
  1192. if (controller.SoundGetVolume(m_pWaterStoppedSound))
  1193. {
  1194. // Fade out the water stopped sound over 1 second.
  1195. controller.SoundChangeVolume(m_pWaterStoppedSound, 0.0, 1.0);
  1196. }
  1197. }
  1198. }
  1199. // Manage the state of the water fast sound (water hissing under the pontoons).
  1200. if ( nWaterLevel == 0 )
  1201. {
  1202. controller.SoundChangeVolume(m_pWaterFastSound, 0.0, 0.0);
  1203. }
  1204. else
  1205. {
  1206. controller.SoundChangeVolume( m_pWaterFastSound, speedRatio, 0.0 );
  1207. }
  1208. m_nPrevWaterLevel = nWaterLevel;
  1209. }
  1210. //-----------------------------------------------------------------------------
  1211. // Purpose:
  1212. //-----------------------------------------------------------------------------
  1213. void CPropAirboat::UpdateSound()
  1214. {
  1215. if (!GetDriver())
  1216. return;
  1217. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  1218. // Sample the data that we need for sounds.
  1219. CFourWheelVehiclePhysics *pPhysics = GetPhysics();
  1220. int speed = pPhysics->GetSpeed();
  1221. int maxSpeed = pPhysics->GetMaxSpeed();
  1222. float speedRatio = clamp((float)speed / (float)maxSpeed, 0, 1);
  1223. //Msg("speedRatio=%f\n", speedRatio);
  1224. UpdateWeaponSound();
  1225. UpdateEngineSound( controller, speedRatio );
  1226. UpdateFanSound( controller, speedRatio );
  1227. UpdateWaterSound( controller, speedRatio );
  1228. }
  1229. //-----------------------------------------------------------------------------
  1230. // Purpose:
  1231. //-----------------------------------------------------------------------------
  1232. void CPropAirboat::UpdateSplashEffects( void )
  1233. {
  1234. // Splash effects.
  1235. CreateSplash( AIRBOAT_SPLASH_RIPPLE );
  1236. // CreateSplash( AIRBOAT_SPLASH_SPRAY );
  1237. }
  1238. //-----------------------------------------------------------------------------
  1239. // Purpose:
  1240. //-----------------------------------------------------------------------------
  1241. const char *CPropAirboat::GetTracerType( void )
  1242. {
  1243. if ( gpGlobals->curtime >= m_flNextHeavyShotTime )
  1244. return "AirboatGunHeavyTracer";
  1245. return "AirboatGunTracer";
  1246. }
  1247. //-----------------------------------------------------------------------------
  1248. // Purpose:
  1249. //-----------------------------------------------------------------------------
  1250. void CPropAirboat::DoMuzzleFlash( void )
  1251. {
  1252. CEffectData data;
  1253. data.m_nEntIndex = entindex();
  1254. data.m_nAttachmentIndex = m_nGunBarrelAttachment;
  1255. data.m_flScale = 1.0f;
  1256. DispatchEffect( "AirboatMuzzleFlash", data );
  1257. BaseClass::DoMuzzleFlash();
  1258. }
  1259. //-----------------------------------------------------------------------------
  1260. // Purpose:
  1261. //-----------------------------------------------------------------------------
  1262. #define GUN_WINDUP_TIME 1.5f
  1263. // NVNT Convar for airboat gun magnitude
  1264. ConVar hap_airboat_gun_mag("hap_airboat_gun_mag", "3", 0);
  1265. void CPropAirboat::FireGun( )
  1266. {
  1267. // Get the gun position.
  1268. Vector vecGunPosition;
  1269. Vector vecForward;
  1270. GetAttachment( m_nGunBarrelAttachment, vecGunPosition, &vecForward );
  1271. // NOTE: For the airboat, unable to fire really means the aim is clamped
  1272. Vector vecAimPoint;
  1273. if ( !m_bUnableToFire )
  1274. {
  1275. // Trace from eyes and see what we hit.
  1276. ComputeAimPoint( &vecAimPoint );
  1277. }
  1278. else
  1279. {
  1280. // We hit the clamp; just fire whichever way the gun is facing
  1281. VectorMA( vecGunPosition, 1000.0f, vecForward, vecAimPoint );
  1282. }
  1283. // Get a ray from the gun to the target.
  1284. Vector vecRay = vecAimPoint - vecGunPosition;
  1285. VectorNormalize( vecRay );
  1286. /*
  1287. // Get the aiming direction
  1288. Vector vecRay;
  1289. AngleVectors( vecGunAngles, &vecRay );
  1290. VectorNormalize( vecRay );
  1291. */
  1292. CAmmoDef *pAmmoDef = GetAmmoDef();
  1293. int ammoType = pAmmoDef->Index( "AirboatGun" );
  1294. #if defined( WIN32 ) && !defined( _X360 )
  1295. // NVNT punch the players haptics by the magnitude cvar each round fired
  1296. HapticPunch(m_hPlayer,0,0,hap_airboat_gun_mag.GetFloat());
  1297. #endif
  1298. FireBulletsInfo_t info;
  1299. info.m_vecSrc = vecGunPosition;
  1300. info.m_vecDirShooting = vecRay;
  1301. info.m_flDistance = 4096;
  1302. info.m_iAmmoType = ammoType;
  1303. info.m_nFlags = FIRE_BULLETS_TEMPORARY_DANGER_SOUND;
  1304. if ( gpGlobals->curtime >= m_flNextHeavyShotTime )
  1305. {
  1306. info.m_iShots = 1;
  1307. info.m_vecSpread = VECTOR_CONE_PRECALCULATED;
  1308. info.m_flDamageForceScale = 1000.0f;
  1309. }
  1310. else
  1311. {
  1312. info.m_iShots = 2;
  1313. info.m_vecSpread = VECTOR_CONE_5DEGREES;
  1314. }
  1315. FireBullets( info );
  1316. CBaseEntity *pDriver = GetDriver();
  1317. CBasePlayer *pPlayerDriver;
  1318. if( pDriver && pDriver->IsPlayer() )
  1319. {
  1320. pPlayerDriver = dynamic_cast<CBasePlayer*>(pDriver);
  1321. if( pPlayerDriver )
  1322. {
  1323. pPlayerDriver->RumbleEffect( RUMBLE_AIRBOAT_GUN, 0, RUMBLE_FLAG_LOOP|RUMBLE_FLAG_ONLYONE );
  1324. }
  1325. }
  1326. DoMuzzleFlash();
  1327. // NOTE: This must occur after FireBullets
  1328. if ( gpGlobals->curtime >= m_flNextHeavyShotTime )
  1329. {
  1330. m_flNextHeavyShotTime = gpGlobals->curtime + CANNON_HEAVY_SHOT_INTERVAL;
  1331. }
  1332. if ( gpGlobals->curtime >= m_flNextGunShakeTime )
  1333. {
  1334. UTIL_ScreenShakeObject( this, WorldSpaceCenter(), 0.2, 250.0, CANNON_SHAKE_INTERVAL, 250, SHAKE_START );
  1335. m_flNextGunShakeTime = gpGlobals->curtime + 0.5 * CANNON_SHAKE_INTERVAL;
  1336. }
  1337. // Specifically kill APC missiles in the cone. But we're going to totally cheat
  1338. // because it's hard to hit them when they are close.
  1339. // Use the player's eye position as the center of the cone.
  1340. if ( !m_hPlayer )
  1341. return;
  1342. Vector vecEyeDirection, vecEyePosition;
  1343. if ( !m_bUnableToFire )
  1344. {
  1345. if ( IsX360() )
  1346. {
  1347. GetAttachment( m_nGunBarrelAttachment, vecEyePosition, &vecEyeDirection );
  1348. }
  1349. else
  1350. {
  1351. vecEyePosition = m_hPlayer->EyePosition();
  1352. m_hPlayer->EyeVectors( &vecEyeDirection, NULL, NULL );
  1353. }
  1354. }
  1355. else
  1356. {
  1357. vecEyePosition = vecGunPosition;
  1358. vecEyeDirection = vecRay;
  1359. }
  1360. CAPCMissile *pEnt = FindAPCMissileInCone( vecEyePosition, vecEyeDirection, 2.5f );
  1361. if ( pEnt && (pEnt->GetHealth() > 0) )
  1362. {
  1363. CTakeDamageInfo info( this, this, 1, DMG_AIRBOAT );
  1364. CalculateBulletDamageForce( &info, ammoType, vecRay, pEnt->WorldSpaceCenter() );
  1365. pEnt->TakeDamage( info );
  1366. Vector vecVelocity = pEnt->GetAbsVelocity();
  1367. // Pick a vector perpendicular to the vecRay which will push it away from the airboat
  1368. Vector vecPerp;
  1369. CrossProduct( Vector( 0, 0, 1 ), vecRay, vecPerp );
  1370. vecPerp.z = 0.0f;
  1371. if ( VectorNormalize( vecPerp ) > 1e-3 )
  1372. {
  1373. Vector vecCurrentDir;
  1374. GetVectors( &vecCurrentDir, NULL, NULL );
  1375. if ( DotProduct( vecPerp, vecCurrentDir ) > 0.0f )
  1376. {
  1377. vecPerp *= -1.0f;
  1378. }
  1379. vecPerp *= random->RandomFloat( 15, 25 );
  1380. vecVelocity += vecPerp;
  1381. pEnt->SetAbsVelocity( vecVelocity );
  1382. // pEnt->DisableGuiding();
  1383. }
  1384. }
  1385. }
  1386. //-----------------------------------------------------------------------------
  1387. // Purpose:
  1388. //-----------------------------------------------------------------------------
  1389. #define FIRING_DISCHARGE_RATE (1.0f / 3.0f)
  1390. void CPropAirboat::UpdateGunState( CUserCmd *ucmd )
  1391. {
  1392. bool bStopRumble = false;
  1393. if ( ucmd->buttons & IN_ATTACK )
  1394. {
  1395. if ( m_nGunState == GUN_STATE_IDLE )
  1396. {
  1397. // AddGestureSequence( LookupSequence( "fire_gun" ) );
  1398. m_nGunState = GUN_STATE_FIRING;
  1399. }
  1400. if ( m_nAmmoCount > 0 )
  1401. {
  1402. RemoveAmmo( FIRING_DISCHARGE_RATE );
  1403. FireGun( );
  1404. if ( m_nAmmoCount == 0 )
  1405. {
  1406. EmitSound( "Airboat.FireGunRevDown" );
  1407. bStopRumble = true;
  1408. // RemoveAllGestures();
  1409. }
  1410. }
  1411. }
  1412. else
  1413. {
  1414. if ( m_nGunState != GUN_STATE_IDLE )
  1415. {
  1416. if ( m_nAmmoCount != 0 )
  1417. {
  1418. EmitSound( "Airboat.FireGunRevDown" );
  1419. bStopRumble = true;
  1420. // RemoveAllGestures();
  1421. }
  1422. m_nGunState = GUN_STATE_IDLE;
  1423. }
  1424. }
  1425. if( bStopRumble )
  1426. {
  1427. CBaseEntity *pDriver = GetDriver();
  1428. CBasePlayer *pPlayerDriver;
  1429. if( pDriver && pDriver->IsPlayer() )
  1430. {
  1431. pPlayerDriver = dynamic_cast<CBasePlayer*>(pDriver);
  1432. if( pPlayerDriver )
  1433. {
  1434. pPlayerDriver->RumbleEffect( RUMBLE_AIRBOAT_GUN, 0, RUMBLE_FLAG_STOP );
  1435. }
  1436. }
  1437. }
  1438. }
  1439. //-----------------------------------------------------------------------------
  1440. // Purpose:
  1441. //-----------------------------------------------------------------------------
  1442. void CPropAirboat::DriveVehicle( float flFrameTime, CUserCmd *ucmd, int iButtonsDown, int iButtonsReleased )
  1443. {
  1444. if ( ucmd->impulse == 100 )
  1445. {
  1446. if (HeadlightIsOn())
  1447. {
  1448. HeadlightTurnOff();
  1449. }
  1450. else
  1451. {
  1452. HeadlightTurnOn();
  1453. }
  1454. }
  1455. // Fire gun.
  1456. if ( HasGun() )
  1457. {
  1458. UpdateGunState( ucmd );
  1459. }
  1460. m_VehiclePhysics.UpdateDriverControls( ucmd, TICK_INTERVAL );
  1461. // Create splashes.
  1462. UpdateSplashEffects();
  1463. // Save this data.
  1464. m_flThrottle = m_VehiclePhysics.GetThrottle();
  1465. m_nSpeed = m_VehiclePhysics.GetSpeed();
  1466. m_nRPM = m_VehiclePhysics.GetRPM();
  1467. }
  1468. //-----------------------------------------------------------------------------
  1469. // Purpose:
  1470. // Input : *pPlayer -
  1471. // *pMoveData -
  1472. //-----------------------------------------------------------------------------
  1473. void CPropAirboat::ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMoveData )
  1474. {
  1475. BaseClass::ProcessMovement( pPlayer, pMoveData );
  1476. if ( gpGlobals->frametime != 0 )
  1477. {
  1478. // Create danger sounds in front of the vehicle.
  1479. CreateDangerSounds();
  1480. // Play a sound around us to make NPCs pay attention to us
  1481. if ( m_VehiclePhysics.GetThrottle() > 0 )
  1482. {
  1483. CSoundEnt::InsertSound( SOUND_PLAYER_VEHICLE, pPlayer->GetAbsOrigin(), 3500, 0.1f, pPlayer, SOUNDENT_CHANNEL_REPEATED_PHYSICS_DANGER );
  1484. }
  1485. }
  1486. Vector vecVelocityWorld;
  1487. GetVelocity( &vecVelocityWorld, NULL );
  1488. Vector vecVelocityLocal;
  1489. WorldToEntitySpace( GetAbsOrigin() + vecVelocityWorld, &vecVelocityLocal );
  1490. m_vecPhysVelocity = vecVelocityLocal;
  1491. }
  1492. //-----------------------------------------------------------------------------
  1493. // Purpose: Create danger sounds in front of the vehicle.
  1494. //-----------------------------------------------------------------------------
  1495. void CPropAirboat::CreateDangerSounds( void )
  1496. {
  1497. QAngle vehicleAngles = GetLocalAngles();
  1498. Vector vecStart = GetAbsOrigin();
  1499. Vector vecDir, vecRight;
  1500. GetVectors( &vecDir, &vecRight, NULL );
  1501. const float soundDuration = 0.25;
  1502. float speed = m_VehiclePhysics.GetHLSpeed();
  1503. // Make danger sounds ahead of the vehicle
  1504. if ( fabs(speed) > 120 )
  1505. {
  1506. Vector vecSpot;
  1507. float steering = m_VehiclePhysics.GetSteering();
  1508. if ( steering != 0 )
  1509. {
  1510. if ( speed > 0 )
  1511. {
  1512. vecDir += vecRight * steering * 0.5;
  1513. }
  1514. else
  1515. {
  1516. vecDir -= vecRight * steering * 0.5;
  1517. }
  1518. VectorNormalize(vecDir);
  1519. }
  1520. const float radius = speed * 0.4;
  1521. // 0.7 seconds ahead
  1522. vecSpot = vecStart + vecDir * (speed * 0.7f);
  1523. CSoundEnt::InsertSound( SOUND_DANGER, vecSpot, radius, soundDuration, this, SOUNDENT_CHANNEL_REPEATED_DANGER );
  1524. CSoundEnt::InsertSound( SOUND_PHYSICS_DANGER, vecSpot, radius, soundDuration, this, SOUNDENT_CHANNEL_REPEATED_PHYSICS_DANGER );
  1525. //NDebugOverlay::Box(vecSpot, Vector(-radius,-radius,-radius),Vector(radius,radius,radius), 255, 0, 255, 0, soundDuration);
  1526. #if 0
  1527. // put sounds a bit to left and right but slightly closer to vehicle to make
  1528. // a "cone" of sound in front of it.
  1529. trace_t tr;
  1530. vecSpot = vecStart + vecDir * (speed * 0.5f) - vecRight * speed * 0.5;
  1531. UTIL_TraceLine( vecStart, vecSpot, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
  1532. CSoundEnt::InsertSound( SOUND_DANGER, vecSpot, 400, soundDuration, this, 1 );
  1533. vecSpot = vecStart + vecDir * (speed * 0.5f) + vecRight * speed * 0.5;
  1534. UTIL_TraceLine( vecStart, vecSpot, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
  1535. CSoundEnt::InsertSound( SOUND_DANGER, vecSpot, 400, soundDuration, this, 2);
  1536. #endif
  1537. }
  1538. }
  1539. //-----------------------------------------------------------------------------
  1540. // Purpose:
  1541. //-----------------------------------------------------------------------------
  1542. void CPropAirboat::DampenEyePosition( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles )
  1543. {
  1544. // Get the frametime. (Check to see if enough time has passed to warrent dampening).
  1545. float flFrameTime = gpGlobals->frametime;
  1546. if ( flFrameTime < AIRBOAT_FRAMETIME_MIN )
  1547. {
  1548. vecVehicleEyePos = m_vecLastEyePos;
  1549. DampenUpMotion( vecVehicleEyePos, vecVehicleEyeAngles, 0.0f );
  1550. return;
  1551. }
  1552. // Keep static the sideways motion.
  1553. // Dampen forward/backward motion.
  1554. DampenForwardMotion( vecVehicleEyePos, vecVehicleEyeAngles, flFrameTime );
  1555. // Blend up/down motion.
  1556. DampenUpMotion( vecVehicleEyePos, vecVehicleEyeAngles, flFrameTime );
  1557. }
  1558. //-----------------------------------------------------------------------------
  1559. // Use the controller as follows:
  1560. // speed += ( pCoefficientsOut[0] * ( targetPos - currentPos ) + pCoefficientsOut[1] * ( targetSpeed - currentSpeed ) ) * flDeltaTime;
  1561. //-----------------------------------------------------------------------------
  1562. void CPropAirboat::ComputePDControllerCoefficients( float *pCoefficientsOut,
  1563. float flFrequency, float flDampening,
  1564. float flDeltaTime )
  1565. {
  1566. float flKs = 9.0f * flFrequency * flFrequency;
  1567. float flKd = 4.5f * flFrequency * flDampening;
  1568. float flScale = 1.0f / ( 1.0f + flKd * flDeltaTime + flKs * flDeltaTime * flDeltaTime );
  1569. pCoefficientsOut[0] = flKs * flScale;
  1570. pCoefficientsOut[1] = ( flKd + flKs * flDeltaTime ) * flScale;
  1571. }
  1572. //-----------------------------------------------------------------------------
  1573. // Purpose:
  1574. //-----------------------------------------------------------------------------
  1575. void CPropAirboat::DampenForwardMotion( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles, float flFrameTime )
  1576. {
  1577. // Get forward vector.
  1578. Vector vecForward;
  1579. AngleVectors( vecVehicleEyeAngles, &vecForward);
  1580. // Simulate the eye position forward based on the data from last frame
  1581. // (assumes no acceleration - it will get that from the "spring").
  1582. Vector vecCurrentEyePos = m_vecLastEyePos + m_vecEyeSpeed * flFrameTime;
  1583. // Calculate target speed based on the current vehicle eye position and the last vehicle eye position and frametime.
  1584. Vector vecVehicleEyeSpeed = ( vecVehicleEyePos - m_vecLastEyeTarget ) / flFrameTime;
  1585. m_vecLastEyeTarget = vecVehicleEyePos;
  1586. // Calculate the speed and position deltas.
  1587. Vector vecDeltaSpeed = vecVehicleEyeSpeed - m_vecEyeSpeed;
  1588. Vector vecDeltaPos = vecVehicleEyePos - vecCurrentEyePos;
  1589. // Clamp.
  1590. if ( vecDeltaPos.Length() > AIRBOAT_DELTA_LENGTH_MAX )
  1591. {
  1592. float flSign = vecForward.Dot( vecVehicleEyeSpeed ) >= 0.0f ? -1.0f : 1.0f;
  1593. vecVehicleEyePos += flSign * ( vecForward * AIRBOAT_DELTA_LENGTH_MAX );
  1594. m_vecLastEyePos = vecVehicleEyePos;
  1595. m_vecEyeSpeed = vecVehicleEyeSpeed;
  1596. return;
  1597. }
  1598. // Generate an updated (dampening) speed for use in next frames position extrapolation.
  1599. float flCoefficients[2];
  1600. ComputePDControllerCoefficients( flCoefficients, r_AirboatViewDampenFreq.GetFloat(), r_AirboatViewDampenDamp.GetFloat(), flFrameTime );
  1601. m_vecEyeSpeed += ( ( flCoefficients[0] * vecDeltaPos + flCoefficients[1] * vecDeltaSpeed ) * flFrameTime );
  1602. // Save off data for next frame.
  1603. m_vecLastEyePos = vecCurrentEyePos;
  1604. // Move eye forward/backward.
  1605. Vector vecForwardOffset = vecForward * ( vecForward.Dot( vecDeltaPos ) );
  1606. vecVehicleEyePos -= vecForwardOffset;
  1607. }
  1608. //-----------------------------------------------------------------------------
  1609. // Purpose:
  1610. //-----------------------------------------------------------------------------
  1611. void CPropAirboat::DampenUpMotion( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles, float flFrameTime )
  1612. {
  1613. // Get up vector.
  1614. Vector vecUp;
  1615. AngleVectors( vecVehicleEyeAngles, NULL, NULL, &vecUp );
  1616. vecUp.z = clamp( vecUp.z, 0.0f, vecUp.z );
  1617. vecVehicleEyePos.z += r_AirboatViewZHeight.GetFloat() * vecUp.z;
  1618. // NOTE: Should probably use some damped equation here.
  1619. }
  1620. //-----------------------------------------------------------------------------
  1621. // Purpose:
  1622. //-----------------------------------------------------------------------------
  1623. void CPropAirboat::CreateSplash( int nSplashType )
  1624. {
  1625. if ( GetWaterLevel( ) == 0 )
  1626. return;
  1627. Vector vecSplashPoint;
  1628. Vector vecForward, vecUp;
  1629. GetAttachment( m_nSplashAttachment, vecSplashPoint, &vecForward, &vecUp, NULL );
  1630. CEffectData data;
  1631. data.m_fFlags = 0;
  1632. data.m_vOrigin = vecSplashPoint;
  1633. if ( GetWaterType() & CONTENTS_SLIME )
  1634. {
  1635. data.m_fFlags |= FX_WATER_IN_SLIME;
  1636. }
  1637. switch ( nSplashType )
  1638. {
  1639. case AIRBOAT_SPLASH_SPRAY:
  1640. {
  1641. Vector vecSplashDir;
  1642. vecSplashDir = ( vecForward + vecUp ) * 0.5f;
  1643. VectorNormalize( vecSplashDir );
  1644. data.m_vNormal = vecSplashDir;
  1645. data.m_flScale = 10.0f + random->RandomFloat( 0, 10.0f * 0.25 );
  1646. //DispatchEffect( "waterripple", data );
  1647. DispatchEffect( "watersplash", data );
  1648. }
  1649. case AIRBOAT_SPLASH_RIPPLE:
  1650. {
  1651. /*
  1652. Vector vecSplashDir;
  1653. vecSplashDir = vecUp;
  1654. data.m_vNormal = vecSplashDir;
  1655. data.m_flScale = AIRBOAT_SPLASH_RIPPLE_SIZE + random->RandomFloat( 0, AIRBOAT_SPLASH_RIPPLE_SIZE * 0.25 );
  1656. DispatchEffect( "waterripple", data );
  1657. */
  1658. }
  1659. default:
  1660. {
  1661. return;
  1662. }
  1663. }
  1664. }
  1665. //-----------------------------------------------------------------------------
  1666. // Purpose: Overloaded to calculate stress damage.
  1667. //-----------------------------------------------------------------------------
  1668. void CPropAirboat::VPhysicsUpdate( IPhysicsObject *pPhysics )
  1669. {
  1670. BaseClass::VPhysicsUpdate( pPhysics );
  1671. if ( airboat_fatal_stress.GetFloat() > 0 )
  1672. {
  1673. ApplyStressDamage( pPhysics );
  1674. }
  1675. }
  1676. //-----------------------------------------------------------------------------
  1677. // Purpose: Returns the damage that should be dealt to the driver due to
  1678. // stress (vphysics objects exerting pressure on us).
  1679. //-----------------------------------------------------------------------------
  1680. float CPropAirboat::CalculatePhysicsStressDamage( vphysics_objectstress_t *pStressOut, IPhysicsObject *pPhysics )
  1681. {
  1682. vphysics_objectstress_t stressOut;
  1683. CalculateObjectStress( pPhysics, this, &stressOut );
  1684. //if ( ( stressOut.exertedStress > 100 ) || ( stressOut.receivedStress > 100 ) )
  1685. // Msg( "stress: %f %d %f\n", stressOut.exertedStress, stressOut.hasNonStaticStress, stressOut.receivedStress );
  1686. // Make sure the stress isn't from being stuck inside some static object.
  1687. // If we're being crushed by more than the fatal stress amount, kill the driver.
  1688. if ( stressOut.hasNonStaticStress && ( stressOut.receivedStress > airboat_fatal_stress.GetFloat() ) )
  1689. {
  1690. // if stuck, don't do this!
  1691. if ( !(pPhysics->GetGameFlags() & FVPHYSICS_PENETRATING) )
  1692. {
  1693. // Kill the driver!
  1694. return 1000;
  1695. }
  1696. }
  1697. return 0;
  1698. }
  1699. //-----------------------------------------------------------------------------
  1700. // Purpose: Applies stress damage to the player/driver.
  1701. //-----------------------------------------------------------------------------
  1702. void CPropAirboat::ApplyStressDamage( IPhysicsObject *pPhysics )
  1703. {
  1704. vphysics_objectstress_t stressOut;
  1705. float damage = CalculatePhysicsStressDamage( &stressOut, pPhysics );
  1706. if ( ( damage > 0 ) && ( m_hPlayer != NULL ) )
  1707. {
  1708. CTakeDamageInfo dmgInfo( GetWorldEntity(), GetWorldEntity(), vec3_origin, vec3_origin, damage, DMG_CRUSH );
  1709. dmgInfo.SetDamageForce( Vector( 0, 0, -stressOut.receivedStress * GetCurrentGravity() * gpGlobals->frametime ) );
  1710. dmgInfo.SetDamagePosition( GetAbsOrigin() );
  1711. m_hPlayer->TakeDamage( dmgInfo );
  1712. }
  1713. }