diff options
| author | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:31:46 -0800 |
|---|---|---|
| committer | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:46:31 -0800 |
| commit | f56bb35301836e56582a575a75864392a0177875 (patch) | |
| tree | de61ddd39de3e7df52759711950b4c288592f0dc /sp/src/game/server/hl2/vehicle_airboat.cpp | |
| parent | Mark some more files as text. (diff) | |
| download | source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip | |
Fix line endings. WHAMMY.
Diffstat (limited to 'sp/src/game/server/hl2/vehicle_airboat.cpp')
| -rw-r--r-- | sp/src/game/server/hl2/vehicle_airboat.cpp | 4202 |
1 files changed, 2101 insertions, 2101 deletions
diff --git a/sp/src/game/server/hl2/vehicle_airboat.cpp b/sp/src/game/server/hl2/vehicle_airboat.cpp index adc9259b..c8a49ddb 100644 --- a/sp/src/game/server/hl2/vehicle_airboat.cpp +++ b/sp/src/game/server/hl2/vehicle_airboat.cpp @@ -1,2101 +1,2101 @@ -//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#include "cbase.h"
-#include "vehicle_base.h"
-#include "engine/IEngineSound.h"
-#include "in_buttons.h"
-#include "ammodef.h"
-#include "IEffects.h"
-#include "beam_shared.h"
-#include "weapon_gauss.h"
-#include "soundenvelope.h"
-#include "decals.h"
-#include "soundent.h"
-#include "te_effect_dispatch.h"
-#include "physics_saverestore.h"
-#include "movevars_shared.h"
-#include "npc_attackchopper.h"
-#include "weapon_rpg.h"
-#include "vphysics/constraints.h"
-#include "world.h"
-#include "rumble_shared.h"
-// NVNT for airboat weapon fire
-#include "haptics/haptic_utils.h"
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-extern ConVar sv_vehicle_autoaim_scale;
-
-#define VEHICLE_HITBOX_DRIVER 1
-
-//
-// Body groups.
-//
-#define AIRBOAT_BODYGROUP_GUN 1
-#define AIRBOAT_BODYGROUP_PROP 2
-#define AIRBOAT_BODYGROUP_BLUR 3
-
-#define AIRBOAT_LOCK_SPEED 10 // Airboat must be going slower than this for player to enter or exit, in in/sec
-
-#define AIRBOAT_DELTA_LENGTH_MAX 12.0f // 1 foot
-#define AIRBOAT_FRAMETIME_MIN 1e-6
-
-#define AIRBOAT_SPLASH_RIPPLE 0
-#define AIRBOAT_SPLASH_SPRAY 1
-#define AIRBOAT_SPLASH_RIPPLE_SIZE 20.0f
-
-//
-// Pose parameters.
-//
-#define AIRBOAT_GUN_YAW "vehicle_weapon_yaw"
-#define AIRBOAT_GUN_PITCH "vehicle_weapon_pitch"
-#define AIRBOAT_FRAME_FLEX_LEFT "Frame_Flex_L"
-#define AIRBOAT_FRAME_FLEX_RIGHT "Frame_Flex_R"
-
-#define CANNON_MAX_UP_PITCH 60.0f
-#define CANNON_MAX_DOWN_PITCH 30.0f
-#define CANNON_MAX_RIGHT_YAW 165.0f
-#define CANNON_MAX_LEFT_YAW 75.0f
-
-#define CANNON_HEAVY_SHOT_INTERVAL 0.2f
-#define CANNON_SHAKE_INTERVAL 1.0f
-
-static ConVar sk_airboat_max_ammo("sk_airboat_max_ammo", "100" );
-static ConVar sk_airboat_recharge_rate("sk_airboat_recharge_rate", "15" );
-static ConVar sk_airboat_drain_rate("sk_airboat_drain_rate", "10" );
-static ConVar hud_airboathint_numentries( "hud_airboathint_numentries", "10", FCVAR_NONE );
-static ConVar airboat_fatal_stress( "airboat_fatal_stress", "5000", FCVAR_NONE, "Amount of stress in kg that would kill the airboat driver." );
-
-extern ConVar autoaim_max_dist;
-
-class CPropAirboat : public CPropVehicleDriveable
-{
- DECLARE_CLASS( CPropAirboat, CPropVehicleDriveable );
-
-public:
-
- DECLARE_SERVERCLASS();
- DECLARE_DATADESC();
-
- // CPropVehicle
- virtual void ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMoveData );
- virtual void DriveVehicle( float flFrameTime, CUserCmd *ucmd, int iButtonsDown, int iButtonsReleased );
- void DampenEyePosition( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles );
- bool ShouldThink() { return true; }
-
- // CBaseEntity
- void Think(void);
- void Precache( void );
- void Spawn( void );
- virtual void OnRestore();
- virtual void Activate();
- virtual void UpdateOnRemove();
- virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() | FCAP_USE_IN_RADIUS; };
- virtual void DoMuzzleFlash( void );
- virtual void StopLoopingSounds();
-
- // position to shoot at
- virtual Vector BodyTarget( const Vector &posSrc, bool bNoisy );
- virtual Vector GetSmoothedVelocity( void );
-
- virtual void EnterVehicle( CBaseCombatCharacter *pPlayer );
-
- virtual bool AllowBlockedExit( CBaseCombatCharacter *pPlayer, int nRole ) { return false; }
- virtual void PreExitVehicle( CBaseCombatCharacter *pPlayer, int nRole );
- virtual void ExitVehicle( int nRole );
-
- void ComputePDControllerCoefficients( float *pCoefficientsOut, float flFrequency, float flDampening, float flDeltaTime );
- void DampenForwardMotion( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles, float flFrameTime );
- void DampenUpMotion( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles, float flFrameTime );
-
- virtual void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
- virtual int OnTakeDamage( const CTakeDamageInfo &info );
-
- void VPhysicsUpdate( IPhysicsObject *pPhysics );
-
- // Scraping noises for the various things we drive on.
- virtual void VPhysicsFriction( IPhysicsObject *pObject, float energy, int surfaceProps, int surfacePropsHit );
-
- bool HeadlightIsOn( void ) { return m_bHeadlightIsOn; }
- void HeadlightTurnOn( void );
- void HeadlightTurnOff( void );
-
- virtual bool ShouldDrawWaterImpacts( void );
-
- bool ShouldForceExit() { return m_bForcedExit; }
- void ClearForcedExit() { m_bForcedExit = false; }
-
- // Input handlers.
- void InputWake( inputdata_t &inputdata );
- void InputExitVehicle( inputdata_t &inputdata );
- void InputEnableGun( inputdata_t &inputdata );
- void InputStartRotorWashForces( inputdata_t &inputdata );
- void InputStopRotorWashForces( inputdata_t &inputdata );
-
- // Allows the shooter to change the impact effect of his bullets
- virtual void DoImpactEffect( trace_t &tr, int nDamageType );
-
- // Airboat passengers do not directly receive damage from blasts or radiation damage
- virtual bool PassengerShouldReceiveDamage( CTakeDamageInfo &info )
- {
- if ( info.GetDamageType() & DMG_VEHICLE )
- return true;
-
- return (info.GetDamageType() & (DMG_RADIATION|DMG_BLAST|DMG_CRUSH) ) == 0;
- }
-
- const char *GetTracerType( void );
-
-private:
-
- void CreateAntiFlipConstraint();
-
- void ApplyStressDamage( IPhysicsObject *pPhysics );
- float CalculatePhysicsStressDamage( vphysics_objectstress_t *pStressOut, IPhysicsObject *pPhysics );
-
- void CreateDangerSounds( void );
-
- void FireGun( );
-
- void UpdateSplashEffects( void );
- void CreateSplash( int nSplashType );
-
- // Purpose: Aim Gun at a target
- void AimGunAt( const Vector &endPos, float flInterval );
-
- // Purpose: Returns the direction the gun is currently aiming at
- void GetGunAimDirection( Vector *resultDir );
-
- // Recharges the ammo based on speed
- void RechargeAmmo();
-
- // Removes the ammo...
- void RemoveAmmo( float flAmmoAmount );
-
- // Purpose:
- void ComputeAimPoint( Vector *pVecAimPoint );
-
- // Do the right thing for the gun
- void UpdateGunState( CUserCmd *ucmd );
-
- // Sound management
- void CreateSounds();
- void UpdateSound();
- void UpdateWeaponSound();
- void UpdateEngineSound( CSoundEnvelopeController &controller, float speedRatio );
- void UpdateFanSound( CSoundEnvelopeController &controller, float speedRatio );
- void UpdateWaterSound( CSoundEnvelopeController &controller, float speedRatio );
-
- void UpdatePropeller();
- void UpdateGauge();
-
- void CreatePlayerBlocker();
- void DestroyPlayerBlocker();
- void EnablePlayerBlocker( bool bEnable );
-
-private:
-
- enum
- {
- GUN_STATE_IDLE = 0,
- GUN_STATE_FIRING,
- };
-
- Vector m_vecLastEyePos;
- Vector m_vecLastEyeTarget;
- Vector m_vecEyeSpeed;
-
- //float m_flHandbrakeTime; // handbrake after the fact to keep vehicles from rolling
- //bool m_bInitialHandbrake;
-
- bool m_bForcedExit;
-
- int m_nGunRefAttachment;
- int m_nGunBarrelAttachment;
- float m_aimYaw;
- float m_aimPitch;
- float m_flChargeRemainder;
- float m_flDrainRemainder;
- int m_nGunState;
- float m_flNextHeavyShotTime;
- float m_flNextGunShakeTime;
-
- CNetworkVar( int, m_nAmmoCount );
- CNetworkVar( bool, m_bHeadlightIsOn );
- EHANDLE m_hAvoidSphere;
-
- int m_nSplashAttachment;
-
- float m_flPrevThrottle; // Throttle during last think. Used for detecting state changes.
- float m_flSpinRate; // Current rate of spin of propeller: 0 = min, 1.0 = max
- float m_flTargetSpinRate; // Target rate of spin of propeller: 0 = min, 1.0 = max
- float m_flPropTime; // Time to turn on/off the prop.
- float m_flBlurTime; // Time to turn on/off the blur.
-
- CSoundPatch *m_pFanSound;
- CSoundPatch *m_pFanMaxSpeedSound;
- CSoundPatch *m_pEngineSound;
- CSoundPatch *m_pWaterFastSound;
- CSoundPatch *m_pWaterStoppedSound;
- CSoundPatch *m_pGunFiringSound;
-
- float m_flEngineIdleTime; // Time to start playing the engine's idle sound.
- float m_flEngineDuckTime; // Time to reduce the volume of the engine's idle sound.
-
- bool m_bFadeOutFan; // Fade out fan sound after cruising at max speed for a while.
-
- int m_nPrevWaterLevel; // Used for detecting transitions into/out of water.
- float m_flWaterStoppedPitchTime; // Time to pitch shift the water stopped sound.
-
- float m_flLastImpactEffectTime;
- int m_iNumberOfEntries;
-
- IPhysicsConstraint *m_pAntiFlipConstraint; // A ragdoll constraint that prevents us from flipping.
-
- CHandle<CEntityBlocker> m_hPlayerBlocker;
-
- CNetworkVar( Vector, m_vecPhysVelocity );
-
- CNetworkVar( int, m_nExactWaterLevel );
-
- IMPLEMENT_NETWORK_VAR_FOR_DERIVED( m_nWaterLevel );
-
-};
-
-IMPLEMENT_SERVERCLASS_ST( CPropAirboat, DT_PropAirboat )
- SendPropBool( SENDINFO( m_bHeadlightIsOn ) ),
- SendPropInt( SENDINFO( m_nAmmoCount ), 9 ),
- SendPropInt( SENDINFO( m_nExactWaterLevel ) ),
- SendPropInt( SENDINFO( m_nWaterLevel ) ),
- SendPropVector( SENDINFO( m_vecPhysVelocity ) ),
-END_SEND_TABLE();
-
-LINK_ENTITY_TO_CLASS( prop_vehicle_airboat, CPropAirboat );
-
-BEGIN_DATADESC( CPropAirboat )
- DEFINE_FIELD( m_vecLastEyePos, FIELD_POSITION_VECTOR ),
- DEFINE_FIELD( m_vecLastEyeTarget, FIELD_POSITION_VECTOR ),
- DEFINE_FIELD( m_vecEyeSpeed, FIELD_VECTOR ),
-
-// DEFINE_FIELD( m_flHandbrakeTime, FIELD_TIME ),
-// DEFINE_FIELD( m_bInitialHandbrake,FIELD_BOOLEAN ),
-// DEFINE_FIELD( m_nGunRefAttachment, FIELD_INTEGER ),
-// DEFINE_FIELD( m_nGunBarrelAttachment, FIELD_INTEGER ),
- DEFINE_FIELD( m_aimYaw, FIELD_FLOAT ),
- DEFINE_FIELD( m_aimPitch, FIELD_FLOAT ),
- DEFINE_FIELD( m_flChargeRemainder, FIELD_FLOAT ),
- DEFINE_FIELD( m_flDrainRemainder, FIELD_FLOAT ),
- DEFINE_FIELD( m_nGunState, FIELD_INTEGER ),
- DEFINE_FIELD( m_flNextHeavyShotTime, FIELD_TIME ),
- DEFINE_FIELD( m_flNextGunShakeTime, FIELD_TIME ),
- DEFINE_FIELD( m_nAmmoCount, FIELD_INTEGER ),
- DEFINE_FIELD( m_bHeadlightIsOn, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_hAvoidSphere, FIELD_EHANDLE ),
-// DEFINE_FIELD( m_nSplashAttachment, FIELD_INTEGER ),
- DEFINE_FIELD( m_hPlayerBlocker, FIELD_EHANDLE ),
-
- DEFINE_FIELD( m_vecPhysVelocity, FIELD_VECTOR ),
- DEFINE_FIELD( m_nExactWaterLevel, FIELD_INTEGER ),
-
- DEFINE_FIELD( m_flPrevThrottle, FIELD_FLOAT ),
- DEFINE_FIELD( m_flSpinRate, FIELD_FLOAT ),
- DEFINE_FIELD( m_flTargetSpinRate, FIELD_FLOAT ),
- DEFINE_FIELD( m_flPropTime, FIELD_TIME ),
- DEFINE_FIELD( m_flBlurTime, FIELD_TIME ),
- DEFINE_FIELD( m_bForcedExit, FIELD_BOOLEAN ),
-
- DEFINE_SOUNDPATCH( m_pFanSound ),
- DEFINE_SOUNDPATCH( m_pFanMaxSpeedSound ),
- DEFINE_SOUNDPATCH( m_pEngineSound ),
- DEFINE_SOUNDPATCH( m_pWaterFastSound ),
- DEFINE_SOUNDPATCH( m_pWaterStoppedSound ),
- DEFINE_SOUNDPATCH( m_pGunFiringSound ),
-
- DEFINE_PHYSPTR( m_pAntiFlipConstraint ),
-
- DEFINE_FIELD( m_flEngineIdleTime, FIELD_TIME ),
- DEFINE_FIELD( m_flEngineDuckTime, FIELD_TIME ),
- DEFINE_FIELD( m_bFadeOutFan, FIELD_BOOLEAN ),
-
- DEFINE_FIELD( m_nPrevWaterLevel, FIELD_INTEGER ),
- DEFINE_FIELD( m_flWaterStoppedPitchTime, FIELD_TIME ),
-
- DEFINE_FIELD( m_flLastImpactEffectTime, FIELD_TIME ),
- DEFINE_FIELD( m_iNumberOfEntries, FIELD_INTEGER ),
-
- DEFINE_INPUTFUNC( FIELD_BOOLEAN, "EnableGun", InputEnableGun ),
- DEFINE_INPUTFUNC( FIELD_VOID, "StartRotorWashForces", InputStartRotorWashForces ),
- DEFINE_INPUTFUNC( FIELD_VOID, "StopRotorWashForces", InputStopRotorWashForces ),
- DEFINE_INPUTFUNC( FIELD_VOID, "ExitVehicle", InputExitVehicle ),
- DEFINE_INPUTFUNC( FIELD_VOID, "Wake", InputWake ),
-
-END_DATADESC()
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAirboat::Precache( void )
-{
- BaseClass::Precache();
-
- PrecacheScriptSound( "Airboat_engine_stop" );
- PrecacheScriptSound( "Airboat_engine_start" );
-
- PrecacheScriptSound( "Airboat.FireGunHeavy" );
- PrecacheScriptSound( "Airboat.FireGunRevDown");
-
- PrecacheScriptSound( "Airboat_engine_idle" );
- PrecacheScriptSound( "Airboat_engine_fullthrottle" );
- PrecacheScriptSound( "Airboat_fan_idle" );
- PrecacheScriptSound( "Airboat_fan_fullthrottle" );
- PrecacheScriptSound( "Airboat_water_stopped" );
- PrecacheScriptSound( "Airboat_water_fast" );
- PrecacheScriptSound( "Airboat_impact_splash" );
- PrecacheScriptSound( "Airboat_impact_hard" );
-
- PrecacheScriptSound( "Airboat_headlight_on" );
- PrecacheScriptSound( "Airboat_headlight_off" );
-
- PrecacheScriptSound( "Airboat.FireGunLoop" );
-
- PrecacheMaterial( "effects/splashwake1" );
- PrecacheMaterial( "effects/splashwake4" );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAirboat::Spawn( void )
-{
- m_nAmmoCount = m_bHasGun ? 0 : -1;
- m_hAvoidSphere = CreateHelicopterAvoidanceSphere( this, 0, 50.0f, false );
- m_flLastImpactEffectTime = -1;
- m_iNumberOfEntries = 0;
-
- // Setup vehicle as a ray-cast airboat.
- SetVehicleType( VEHICLE_TYPE_AIRBOAT_RAYCAST );
- SetCollisionGroup( COLLISION_GROUP_VEHICLE );
- BaseClass::Spawn();
-
- AddSolidFlags( FSOLID_NOT_STANDABLE );
- SetAnimatedEveryTick( true );
-
- // Handbrake data.
- //m_flHandbrakeTime = gpGlobals->curtime + 0.1;
- //m_bInitialHandbrake = false;
- m_VehiclePhysics.SetHasBrakePedal( false );
-
- m_flMinimumSpeedToEnterExit = AIRBOAT_LOCK_SPEED;
-
- m_takedamage = DAMAGE_EVENTS_ONLY;
-
- SetBodygroup(AIRBOAT_BODYGROUP_GUN, m_bHasGun);
- SetBodygroup(AIRBOAT_BODYGROUP_PROP, true);
-
- SetPoseParameter( AIRBOAT_GUN_YAW, 0 );
- SetPoseParameter( AIRBOAT_GUN_PITCH, 0 );
- SetPoseParameter( AIRBOAT_FRAME_FLEX_LEFT, 0 );
- SetPoseParameter( AIRBOAT_FRAME_FLEX_RIGHT, 0 );
-
- m_aimYaw = 0;
- m_aimPitch = 0;
- m_bUnableToFire = true;
- m_nGunState = GUN_STATE_IDLE;
-
- SetPoseParameter( "Steer_Shock", 0.0f );
-
- // Get the physics object so we can adjust the buoyancy.
- IPhysicsObject *pPhysAirboat = VPhysicsGetObject();
- if ( pPhysAirboat )
- {
- pPhysAirboat->SetBuoyancyRatio( 0.0f );
- PhysSetGameFlags( pPhysAirboat, FVPHYSICS_HEAVY_OBJECT );
- }
-
- //CreateAntiFlipConstraint();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Create a ragdoll constraint that prevents us from flipping.
-//-----------------------------------------------------------------------------
-void CPropAirboat::CreateAntiFlipConstraint()
-{
- constraint_ragdollparams_t ragdoll;
- ragdoll.Defaults();
-
- // Don't prevent the boat from moving, just flipping.
- ragdoll.onlyAngularLimits = true;
-
- // Put the ragdoll constraint in the space of the airboat.
- SetIdentityMatrix( ragdoll.constraintToAttached );
- BuildObjectRelativeXform( g_PhysWorldObject, VPhysicsGetObject(), ragdoll.constraintToReference );
-
- ragdoll.axes[0].minRotation = -100;
- ragdoll.axes[0].maxRotation = 100;
- ragdoll.axes[1].minRotation = -100;
- ragdoll.axes[1].maxRotation = 100;
- ragdoll.axes[2].minRotation = -180;
- ragdoll.axes[2].maxRotation = 180;
-
- m_pAntiFlipConstraint = physenv->CreateRagdollConstraint( g_PhysWorldObject, VPhysicsGetObject(), NULL, ragdoll );
-
- //NDebugOverlay::Cross3DOriented( ragdoll.constraintToReference, 128, 255, true, 100 );
-}
-
-
-//-----------------------------------------------------------------------------
-// Attachment indices
-//-----------------------------------------------------------------------------
-void CPropAirboat::UpdateOnRemove()
-{
- BaseClass::UpdateOnRemove();
-
- if ( m_hAvoidSphere )
- {
- UTIL_Remove( m_hAvoidSphere );
- m_hAvoidSphere = NULL;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Attachment indices
-//-----------------------------------------------------------------------------
-void CPropAirboat::Activate()
-{
- BaseClass::Activate();
-
- m_nGunRefAttachment = LookupAttachment( "gun" );
- m_nGunBarrelAttachment = LookupAttachment( "muzzle" );
- m_nSplashAttachment = LookupAttachment( "splash_pt" );
-
- CreateSounds();
-
- CBaseServerVehicle *pServerVehicle = dynamic_cast<CBaseServerVehicle *>(GetServerVehicle());
- if ( pServerVehicle )
- {
- if( pServerVehicle->GetPassenger() )
- {
- // If a boat comes back from a save game with a driver, make sure the engine rumble starts up.
- pServerVehicle->StartEngineRumble();
- }
- }
-
- //CreatePlayerBlocker();
- //EnablePlayerBlocker( true );
-}
-
-
-void CPropAirboat::CreatePlayerBlocker()
-{
- Assert( m_hPlayerBlocker == NULL );
- DestroyPlayerBlocker();
-
- m_hPlayerBlocker = CEntityBlocker::Create( GetAbsOrigin(), Vector( -84, -32, 0 ), Vector( 54, 32, 84 ), this, false );
- if ( m_hPlayerBlocker != NULL )
- {
- m_hPlayerBlocker->SetParent( this );
- m_hPlayerBlocker->SetLocalOrigin( vec3_origin );
- m_hPlayerBlocker->SetLocalAngles( vec3_angle );
- m_hPlayerBlocker->SetCollisionGroup( COLLISION_GROUP_PLAYER );
- m_hPlayerBlocker->AddSolidFlags( FSOLID_NOT_SOLID );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAirboat::DestroyPlayerBlocker()
-{
- if ( m_hPlayerBlocker != NULL )
- {
- UTIL_Remove( m_hPlayerBlocker );
- }
-
- m_hPlayerBlocker = NULL;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : bEnable -
-//-----------------------------------------------------------------------------
-void CPropAirboat::EnablePlayerBlocker( bool bEnable )
-{
- if ( m_hPlayerBlocker != NULL )
- {
- if ( bEnable )
- {
- m_hPlayerBlocker->RemoveSolidFlags( FSOLID_NOT_SOLID );
- }
- else
- {
- m_hPlayerBlocker->AddSolidFlags( FSOLID_NOT_SOLID );
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Update the weapon sounds
-//-----------------------------------------------------------------------------
-#define MIN_CHARGE_SOUND 0.4f
-#define MIN_PITCH_CHANGE ( MIN_CHARGE_SOUND + ( ( 1.0f - MIN_CHARGE_SOUND ) / 3.0f ) )
-#define VOLUME_CHANGE_TIME 0.5f
-
-void CPropAirboat::UpdateWeaponSound()
-{
- if ( HasGun() )
- {
- CSoundEnvelopeController *pController = &CSoundEnvelopeController::GetController();
- float flVolume = pController->SoundGetVolume( m_pGunFiringSound );
- if ( (m_nGunState == GUN_STATE_IDLE) || (m_nAmmoCount == 0) )
- {
- if ( flVolume != 0.0f )
- {
- pController->SoundChangeVolume( m_pGunFiringSound, 0.0f, 0.01f );
- }
- }
- else
- {
- if ( flVolume != 1.0f )
- {
- pController->SoundChangeVolume( m_pGunFiringSound, 1.0f, 0.01f );
- }
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Force the player to exit the vehicle.
-//-----------------------------------------------------------------------------
-void CPropAirboat::InputExitVehicle( inputdata_t &inputdata )
-{
- m_bForcedExit = true;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Force the airboat to wake up. This was needed to fix a last-minute
-// bug for the XBox -- the airboat didn't fall with the platform
-// in d1_canals_10b.
-//-----------------------------------------------------------------------------
-void CPropAirboat::InputWake( inputdata_t &inputdata )
-{
- VPhysicsGetObject()->Wake();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Input handler to enable or disable the airboat's mounted gun.
-//-----------------------------------------------------------------------------
-void CPropAirboat::InputEnableGun( inputdata_t &inputdata )
-{
- m_bHasGun = inputdata.value.Bool();
- SetBodygroup(AIRBOAT_BODYGROUP_GUN, m_bHasGun);
-
- // When enabling the gun, give full ammo
- if ( m_bHasGun )
- {
- m_nAmmoCount = sk_airboat_max_ammo.GetInt();
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Input handler to enable or disable the airboat's mounted gun.
-//-----------------------------------------------------------------------------
-void CPropAirboat::InputStartRotorWashForces( inputdata_t &inputdata )
-{
- RemoveEFlags( EFL_NO_ROTORWASH_PUSH );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Input handler to enable or disable the airboat's mounted gun.
-//-----------------------------------------------------------------------------
-void CPropAirboat::InputStopRotorWashForces( inputdata_t &inputdata )
-{
- AddEFlags( EFL_NO_ROTORWASH_PUSH );
-}
-
-
-//-----------------------------------------------------------------------------
-// Creating vphysics
-//-----------------------------------------------------------------------------
-void CPropAirboat::OnRestore()
-{
- BaseClass::OnRestore();
-
- IPhysicsObject *pPhysAirboat = VPhysicsGetObject();
- if ( pPhysAirboat )
- {
- pPhysAirboat->SetBuoyancyRatio( 0.0f );
- PhysSetGameFlags( pPhysAirboat, FVPHYSICS_HEAVY_OBJECT );
- }
-
- // If the player's in the vehicle, NPCs should ignore it
- if ( GetDriver() )
- {
- SetNavIgnore();
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Used for navigation
-//-----------------------------------------------------------------------------
-void CPropAirboat::EnterVehicle( CBaseCombatCharacter *pPlayer )
-{
- BaseClass::EnterVehicle( pPlayer );
-
- //EnablePlayerBlocker( false );
-
- // NPCs like manhacks should try to hit us
- SetNavIgnore();
-
- // Play the engine start sound.
- float flDuration;
- EmitSound( "Airboat_engine_start", 0.0, &flDuration );
- m_VehiclePhysics.TurnOn();
-
- // Start playing the engine's idle sound as the startup sound finishes.
- m_flEngineIdleTime = gpGlobals->curtime + flDuration - 0.1;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Called when exiting, just before playing the exit animation.
-//-----------------------------------------------------------------------------
-void CPropAirboat::PreExitVehicle( CBaseCombatCharacter *pPlayer, int nRole )
-{
- if ( HeadlightIsOn() )
- {
- HeadlightTurnOff();
- }
-
- // Stop shooting.
- m_nGunState = GUN_STATE_IDLE;
-
- CBaseEntity *pDriver = GetDriver();
- CBasePlayer *pPlayerDriver;
- if( pDriver && pDriver->IsPlayer() )
- {
- pPlayerDriver = dynamic_cast<CBasePlayer*>(pDriver);
- if( pPlayerDriver )
- {
- pPlayerDriver->RumbleEffect( RUMBLE_AIRBOAT_GUN, 0, RUMBLE_FLAG_STOP );
- }
- }
-
- BaseClass::PreExitVehicle( pPlayer, nRole );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Called when exiting, after completing the exit animation.
-// Input : iRole -
-//-----------------------------------------------------------------------------
-void CPropAirboat::ExitVehicle( int nRole )
-{
- CBaseEntity *pDriver = GetDriver();
-
- //EnablePlayerBlocker( true );
-
- BaseClass::ExitVehicle( nRole );
-
- if (!pDriver)
- return;
-
-#if 0
- // On ORANGE BOX this is causing a big blank box to show up, which is worse
- // than the HUD hint persisting for a little while, so don't do it. (sjb)
- // clear the hint
- UTIL_HudHintText( pDriver, "" );
-#endif
-
- // NPCs like manhacks should try to avoid us again
- ClearNavIgnore();
-
- // Play the engine shutoff sound.
- CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
- CPASAttenuationFilter filter( this );
-
- EmitSound_t ep;
- ep.m_nChannel = CHAN_BODY;
- ep.m_pSoundName = "Airboat_engine_stop";
- ep.m_flVolume = controller.SoundGetVolume( m_pEngineSound );
- ep.m_SoundLevel = SNDLVL_NORM;
- ep.m_nPitch = controller.SoundGetPitch( m_pEngineSound );
-
- EmitSound( filter, entindex(), ep );
- m_VehiclePhysics.TurnOff();
-
- // Shut off the airboat sounds.
- controller.SoundChangeVolume( m_pEngineSound, 0.0, 0.0 );
- controller.SoundChangeVolume( m_pFanSound, 0.0, 0.0 );
- controller.SoundChangeVolume( m_pFanMaxSpeedSound, 0.0, 0.0 );
- controller.SoundChangeVolume( m_pWaterStoppedSound, 0.0, 0.0 );
- controller.SoundChangeVolume( m_pWaterFastSound, 0.0, 0.0 );
- controller.SoundChangeVolume( m_pGunFiringSound, 0.0, 0.0 );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAirboat::HeadlightTurnOn( void )
-{
- EmitSound( "Airboat_headlight_on" );
- m_bHeadlightIsOn = true;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAirboat::HeadlightTurnOff( void )
-{
- EmitSound( "Airboat_headlight_off" );
- m_bHeadlightIsOn = false;
-}
-
-
-//-----------------------------------------------------------------------------
-// position to shoot at
-//-----------------------------------------------------------------------------
-Vector CPropAirboat::BodyTarget( const Vector &posSrc, bool bNoisy )
-{
- Vector vecPosition;
- QAngle angles;
- if ( GetServerVehicle()->GetPassenger() )
- {
- // FIXME: Reconcile this with other functions that store a cached version of the results here?
- GetServerVehicle()->GetVehicleViewPosition( VEHICLE_ROLE_DRIVER, &vecPosition, &angles );
- }
- else
- {
- vecPosition = WorldSpaceCenter();
- }
- return vecPosition;
-}
-
-
-//-----------------------------------------------------------------------------
-// Smoothed velocity
-//-----------------------------------------------------------------------------
-#define SMOOTHED_MIN_VELOCITY 75.0f
-#define SMOOTHED_MAX_VELOCITY 150.0f
-
-Vector CPropAirboat::GetSmoothedVelocity( void )
-{
- // If we're going too slow, return the forward direction as the velocity
- // for NPC prediction purposes
- Vector vecSmoothedVelocity = BaseClass::GetSmoothedVelocity();
- float flSpeed = vecSmoothedVelocity.Length();
- if ( flSpeed >= SMOOTHED_MAX_VELOCITY )
- return vecSmoothedVelocity;
-
- Vector vecForward;
- GetVectors( &vecForward, NULL, NULL );
- vecForward *= MAX( flSpeed, 1.0f );
- if ( flSpeed <= SMOOTHED_MIN_VELOCITY )
- return vecForward;
-
- float flBlend = SimpleSplineRemapVal( flSpeed, SMOOTHED_MIN_VELOCITY, SMOOTHED_MAX_VELOCITY, 0.0f, 1.0f );
- VectorLerp( vecForward, vecSmoothedVelocity, flBlend, vecSmoothedVelocity );
- return vecSmoothedVelocity;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAirboat::TraceAttack( const CTakeDamageInfo &inputInfo, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
-{
- CTakeDamageInfo info = inputInfo;
- if ( ptr->hitbox != VEHICLE_HITBOX_DRIVER )
- {
- if ( inputInfo.GetDamageType() & DMG_BULLET )
- {
- info.ScaleDamage( 0.0001 );
- }
- }
-
- BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &info -
-// &vecEnd -
-// *pTraceFilter -
-// *pVecTracerDest -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CPropAirboat::ShouldDrawWaterImpacts( void )
-{
- // The airboat spits out so much crap that we need to do cheaper versions
- // of the impact effects. Also, we need to do less of them.
- if ( m_flLastImpactEffectTime >= gpGlobals->curtime )
- return false;
-
- m_flLastImpactEffectTime = gpGlobals->curtime + 0.05f;
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Allows the shooter to change the impact effect of his bullets
-//-----------------------------------------------------------------------------
-void CPropAirboat::DoImpactEffect( trace_t &tr, int nDamageType )
-{
- // The airboat spits out so much crap that we need to do cheaper versions
- // of the impact effects. Also, we need to do less of them.
- if ( m_flLastImpactEffectTime == gpGlobals->curtime )
- return;
-
- // Randomly drop out
- if ( random->RandomInt( 0, 5 ) )
- return;
-
- m_flLastImpactEffectTime = gpGlobals->curtime;
- UTIL_ImpactTrace( &tr, nDamageType, "AirboatGunImpact" );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-int CPropAirboat::OnTakeDamage( const CTakeDamageInfo &info )
-{
- // Do scaled up physics damage to the airboat
- CTakeDamageInfo physDmg = info;
- physDmg.ScaleDamage( 5 );
- if ( physDmg.GetDamageType() & DMG_BLAST )
- {
- physDmg.SetDamageForce( info.GetDamageForce() * 10 );
- }
- VPhysicsTakeDamage( physDmg );
-
- // Check to do damage to driver
- if ( m_hPlayer != NULL )
- {
- // Don't pass along physics damage
- if ( info.GetDamageType() & (DMG_CRUSH|DMG_RADIATION) )
- return 0;
-
- // Take the damage (strip out the DMG_BLAST)
- CTakeDamageInfo playerDmg = info;
-
- // Mark that we're passing it to the player so the base player accepts the damage
- playerDmg.SetDamageType( info.GetDamageType() | DMG_VEHICLE );
-
- // Deal the damage to the passenger
- m_hPlayer->TakeDamage( playerDmg );
- }
-
- return 0;
-}
-
-
-//-----------------------------------------------------------------------------
-// Scraping noises for the various things we drive on.
-//-----------------------------------------------------------------------------
-void CPropAirboat::VPhysicsFriction( IPhysicsObject *pObject, float energy, int surfaceProps, int surfacePropsHit )
-{
- // don't make noise for hidden/invisible/sky materials
- const surfacedata_t *phit = physprops->GetSurfaceData( surfacePropsHit );
- const surfacedata_t *pprops = physprops->GetSurfaceData( surfaceProps );
- if ( phit->game.material == 'X' || pprops->game.material == 'X' )
- return;
-
- // FIXME: Make different scraping sounds here
- float flVolume = 0.3f;
-
- surfacedata_t *psurf = physprops->GetSurfaceData( surfaceProps );
- const char *pSoundName = physprops->GetString( psurf->sounds.scrapeRough );
-
- PhysFrictionSound( this, pObject, pSoundName, psurf->soundhandles.scrapeRough, flVolume );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Aim Gun at a target position.
-//-----------------------------------------------------------------------------
-// This fixes an optimizer bug that was causing targetYaw and targetPitch to
-// always be reported as clamped, thus disabling the gun. Ack!
-#pragma optimize("", off)
-void CPropAirboat::AimGunAt( const Vector &aimPos, float flInterval )
-{
- matrix3x4_t gunMatrix;
- GetAttachment( m_nGunRefAttachment, gunMatrix );
-
- // transform the target position into gun space
- Vector localTargetPosition;
- VectorITransform( aimPos, gunMatrix, localTargetPosition );
- VectorNormalize( localTargetPosition );
- m_bUnableToFire = false;
- m_vecGunCrosshair = aimPos;
-
- // do a look at in gun space (essentially a delta-lookat)
- QAngle localTargetAngles;
- VectorAngles( localTargetPosition, localTargetAngles );
-
- // convert to +/- 180 degrees
- localTargetAngles.x = UTIL_AngleDiff( localTargetAngles.x, 0 );
- localTargetAngles.y = UTIL_AngleDiff( localTargetAngles.y, 0 );
-
- float targetYaw = m_aimYaw + localTargetAngles.y;
- float targetPitch = m_aimPitch + localTargetAngles.x;
-
- // Constrain our angles
- float newTargetYaw = clamp( targetYaw, -CANNON_MAX_RIGHT_YAW, CANNON_MAX_LEFT_YAW );
- float newTargetPitch = clamp( targetPitch, -CANNON_MAX_UP_PITCH, CANNON_MAX_DOWN_PITCH );
-
- // If the angles have been clamped, we're looking outside of our valid range
- if ( ( newTargetYaw != targetYaw ) || ( newTargetPitch != targetPitch ) )
- {
- m_bUnableToFire = true;
- }
-
- targetYaw = newTargetYaw;
- targetPitch = newTargetPitch;
-
- m_aimYaw = targetYaw;
- m_aimPitch = targetPitch;
-
- SetPoseParameter( AIRBOAT_GUN_YAW, m_aimYaw);
- SetPoseParameter( AIRBOAT_GUN_PITCH, m_aimPitch );
-
- InvalidateBoneCache();
-
- // read back to avoid drift when hitting limits
- // as long as the velocity is less than the delta between the limit and 180, this is fine.
- m_aimPitch = GetPoseParameter( AIRBOAT_GUN_PITCH );
- m_aimYaw = GetPoseParameter( AIRBOAT_GUN_YAW );
-}
-#pragma optimize("", on)
-
-
-//-----------------------------------------------------------------------------
-// Removes the ammo...
-//-----------------------------------------------------------------------------
-void CPropAirboat::RemoveAmmo( float flAmmoAmount )
-{
- m_flDrainRemainder += flAmmoAmount;
- int nAmmoToRemove = (int)m_flDrainRemainder;
- m_flDrainRemainder -= nAmmoToRemove;
- m_nAmmoCount -= nAmmoToRemove;
- if ( m_nAmmoCount < 0 )
- {
- m_nAmmoCount = 0;
- m_flDrainRemainder = 0.0f;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Recharges the ammo...
-//-----------------------------------------------------------------------------
-void CPropAirboat::RechargeAmmo(void)
-{
- if ( !m_bHasGun )
- {
- m_nAmmoCount = -1;
- return;
- }
-
- int nMaxAmmo = sk_airboat_max_ammo.GetInt();
- if ( m_nAmmoCount == nMaxAmmo )
- return;
-
- float flRechargeRate = sk_airboat_recharge_rate.GetInt();
- float flChargeAmount = flRechargeRate * gpGlobals->frametime;
- if ( m_flDrainRemainder != 0.0f )
- {
- if ( m_flDrainRemainder >= flChargeAmount )
- {
- m_flDrainRemainder -= flChargeAmount;
- return;
- }
- else
- {
- flChargeAmount -= m_flDrainRemainder;
- m_flDrainRemainder = 0.0f;
- }
- }
-
- m_flChargeRemainder += flChargeAmount;
- int nAmmoToAdd = (int)m_flChargeRemainder;
- m_flChargeRemainder -= nAmmoToAdd;
- m_nAmmoCount += nAmmoToAdd;
- if ( m_nAmmoCount > nMaxAmmo )
- {
- m_nAmmoCount = nMaxAmmo;
- m_flChargeRemainder = 0.0f;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAirboat::ComputeAimPoint( Vector *pVecAimPoint )
-{
- Vector vecEyeDirection;
-
- if( g_pGameRules->GetAutoAimMode() == AUTOAIM_ON_CONSOLE )
- {
- // Use autoaim as the eye dir.
- autoaim_params_t params;
-
- params.m_fScale = AUTOAIM_SCALE_DEFAULT * sv_vehicle_autoaim_scale.GetFloat();
- params.m_fMaxDist = autoaim_max_dist.GetFloat();
- m_hPlayer->GetAutoaimVector( params );
-
- vecEyeDirection = params.m_vecAutoAimDir;
- }
- else
- {
- m_hPlayer->EyeVectors( &vecEyeDirection, NULL, NULL );
- }
-
- Vector vecEndPos;
- VectorMA( m_hPlayer->EyePosition(), MAX_TRACE_LENGTH, vecEyeDirection, vecEndPos );
- trace_t trace;
- UTIL_TraceLine( m_hPlayer->EyePosition(), vecEndPos, MASK_SHOT, this, COLLISION_GROUP_NONE, &trace );
- *pVecAimPoint = trace.endpos;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Manages animation and sound state.
-//-----------------------------------------------------------------------------
-void CPropAirboat::Think(void)
-{
- BaseClass::Think();
-
- // set handbrake after physics sim settles down
-// if ( gpGlobals->curtime < m_flHandbrakeTime )
-// {
-// SetNextThink( gpGlobals->curtime );
-// }
-// else if ( !m_bInitialHandbrake ) // after initial timer expires, set the handbrake
-// {
-// m_bInitialHandbrake = true;
-// m_VehiclePhysics.SetHandbrake( true );
-// m_VehiclePhysics.Think();
-// }
-
- // Find the vertical extents of the boat
- Vector startPos, endPos;
- CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 1.0f ), &startPos );
- CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 0.0f ), &endPos );
-
- // Look for water along that volume.
- // Make a very vertically thin box and sweep it along the ray.
- Vector vecMins = CollisionProp()->OBBMins();
- Vector vecMaxs = CollisionProp()->OBBMaxs();
- vecMins.z = -0.1f;
- vecMaxs.z = 0.1f;
-
- trace_t tr;
- UTIL_TraceHull( startPos, endPos, vecMins, vecMaxs, (CONTENTS_WATER|CONTENTS_SLIME), this, COLLISION_GROUP_NONE, &tr );
-
- // If we hit something, then save off the info
- if ( tr.fraction != 1.0f )
- {
- m_nExactWaterLevel = tr.endpos.z;
-
- // Classify what we're in
- if ( tr.contents & CONTENTS_SLIME )
- {
- // We fake this value to mean type, instead of level
- SetWaterLevel( 2 );
- }
- else
- {
- // This simply signifies water
- SetWaterLevel( 1 );
- }
- }
- else
- {
- // Not in water
- SetWaterLevel( 0 );
- }
-
- StudioFrameAdvance();
-
- // If the enter or exit animation has finished, tell the server vehicle
- if ( IsSequenceFinished() && ( m_bEnterAnimOn || m_bExitAnimOn ) )
- {
- // The first few time we get into the jeep, print the jeep help
- if ( m_iNumberOfEntries < hud_airboathint_numentries.GetInt() && !m_bExitAnimOn )
- {
- UTIL_HudHintText( m_hPlayer, "#Valve_Hint_BoatKeys" );
- m_iNumberOfEntries++;
- }
-
- GetServerVehicle()->HandleEntryExitFinish( m_bExitAnimOn, false );
-
- // Start the vehicle's idle animation
- ResetSequence(LookupSequence("propeller_spin1"));
- ResetClientsideFrame();
- }
-
- // FIXME: Slam the crosshair every think -- if we don't do this it disappears randomly, never to return.
- if ( ( m_hPlayer.Get() != NULL ) && !( m_bEnterAnimOn || m_bExitAnimOn ) )
- {
- m_hPlayer->m_Local.m_iHideHUD &= ~HIDEHUD_VEHICLE_CROSSHAIR;
- }
-
- // Aim the gun
- if ( HasGun() && m_hPlayer.Get() && !m_bEnterAnimOn && !m_bExitAnimOn )
- {
- Vector vecAimPoint;
- ComputeAimPoint( &vecAimPoint );
- AimGunAt( vecAimPoint, gpGlobals->frametime );
- }
-
- if ( ShouldForceExit() )
- {
- ClearForcedExit();
- m_hPlayer->LeaveVehicle();
- }
-
- if ( HasGun() && ( m_nGunState == GUN_STATE_IDLE ) )
- {
- RechargeAmmo();
- }
-
- UpdateSound();
- UpdatePropeller();
- UpdateGauge();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAirboat::UpdatePropeller()
-{
- if ((m_bExitAnimOn) || (m_bEnterAnimOn))
- return;
-
- #define SPIN_RATE_MED 0.2
- #define SPIN_RATE_HIGH 0.6
-
- // Determine target spin rate from throttle.
- float flTargetSpinRate = m_flThrottle;
- if ((flTargetSpinRate == 0) && (m_hPlayer))
- {
- // Always keep the fan moving a little when we have a driver.
- flTargetSpinRate = 0.2;
- }
-
- // Save the current spin rate to determine state transitions.
- float flPrevSpinRate = m_flSpinRate;
-
- // Determine new spin rate,
- if (m_flSpinRate < flTargetSpinRate)
- {
- if (flTargetSpinRate > 0)
- {
- m_flSpinRate += gpGlobals->frametime * 1.0;
- }
- else
- {
- m_flSpinRate += gpGlobals->frametime * 0.4;
- }
-
- if (m_flSpinRate > flTargetSpinRate)
- {
- m_flSpinRate = flTargetSpinRate;
- }
- }
- else if (m_flSpinRate > flTargetSpinRate)
- {
- m_flSpinRate -= gpGlobals->frametime * 0.4;
- if (m_flSpinRate < flTargetSpinRate)
- {
- m_flSpinRate = flTargetSpinRate;
- }
- }
-
- // Update prop & blur based on new spin rate.
- if (fabs(m_flSpinRate) > SPIN_RATE_HIGH)
- {
- if (fabs(flPrevSpinRate) <= SPIN_RATE_HIGH)
- {
- SetBodygroup(AIRBOAT_BODYGROUP_PROP, false);
- SetBodygroup(AIRBOAT_BODYGROUP_BLUR, true);
- SetSequence(LookupSequence("propeller_spin1"));
- }
- }
- else if (fabs(m_flSpinRate) > SPIN_RATE_MED)
- {
- if ((fabs(flPrevSpinRate) <= SPIN_RATE_MED) || (fabs(flPrevSpinRate) > SPIN_RATE_HIGH))
- {
- SetBodygroup(AIRBOAT_BODYGROUP_PROP, true);
- SetBodygroup(AIRBOAT_BODYGROUP_BLUR, true);
- SetSequence(LookupSequence("propeller_spin1"));
- }
- }
- else
- {
- if (fabs(flPrevSpinRate) > SPIN_RATE_MED)
- {
- SetBodygroup(AIRBOAT_BODYGROUP_PROP, true);
- SetBodygroup(AIRBOAT_BODYGROUP_BLUR, false);
- SetSequence(LookupSequence("propeller_spin1"));
- }
- }
-
- SetPlaybackRate( m_flSpinRate );
-
- m_flPrevThrottle = m_flThrottle;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Updates the speedometer.
-//-----------------------------------------------------------------------------
-void CPropAirboat::UpdateGauge()
-{
- CFourWheelVehiclePhysics *pPhysics = GetPhysics();
- int speed = pPhysics->GetSpeed();
- int maxSpeed = pPhysics->GetMaxSpeed();
- float speedRatio = clamp( (float)speed / (float)maxSpeed, 0, 1 );
-
- SetPoseParameter( "Gauge", speedRatio );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAirboat::CreateSounds()
-{
- CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
-
- CPASAttenuationFilter filter( this );
-
- if (!m_pEngineSound)
- {
- m_pEngineSound = controller.SoundCreate( filter, entindex(), "Airboat_engine_idle" );
- controller.Play( m_pEngineSound, 0, 100 );
- }
-
- if (!m_pFanSound)
- {
- m_pFanSound = controller.SoundCreate( filter, entindex(), "Airboat_fan_idle" );
- controller.Play( m_pFanSound, 0, 100 );
- }
-
- if (!m_pFanMaxSpeedSound)
- {
- m_pFanMaxSpeedSound = controller.SoundCreate( filter, entindex(), "Airboat_fan_fullthrottle" );
- controller.Play( m_pFanMaxSpeedSound, 0, 100 );
- }
-
- if (!m_pWaterStoppedSound)
- {
- m_pWaterStoppedSound = controller.SoundCreate( filter, entindex(), "Airboat_water_stopped" );
- controller.Play( m_pWaterStoppedSound, 0, 100 );
- }
-
- if (!m_pWaterFastSound)
- {
- m_pWaterFastSound = controller.SoundCreate( filter, entindex(), "Airboat_water_fast" );
- controller.Play( m_pWaterFastSound, 0, 100 );
- }
-
- if (!m_pGunFiringSound)
- {
- m_pGunFiringSound = controller.SoundCreate( filter, entindex(), "Airboat.FireGunLoop" );
- controller.Play( m_pGunFiringSound, 0, 100 );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAirboat::StopLoopingSounds()
-{
- CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
-
- controller.SoundDestroy( m_pEngineSound );
- m_pEngineSound = NULL;
-
- controller.SoundDestroy( m_pFanSound );
- m_pFanSound = NULL;
-
- controller.SoundDestroy( m_pFanMaxSpeedSound );
- m_pFanMaxSpeedSound = NULL;
-
- controller.SoundDestroy( m_pWaterStoppedSound );
- m_pWaterStoppedSound = NULL;
-
- controller.SoundDestroy( m_pWaterFastSound );
- m_pWaterFastSound = NULL;
-
- controller.SoundDestroy( m_pGunFiringSound );
- m_pGunFiringSound = NULL;
-
- BaseClass::StopLoopingSounds();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Manage the state of the engine sound.
-//-----------------------------------------------------------------------------
-void CPropAirboat::UpdateEngineSound( CSoundEnvelopeController &controller, float speedRatio )
-{
- #define ENGINE_MIN_VOLUME 0.22
- #define ENGINE_MAX_VOLUME 0.62
- #define ENGINE_MIN_PITCH 80
- #define ENGINE_MAX_PITCH 140
- #define ENGINE_DUCK_TIME 4.0
-
- if ( controller.SoundGetVolume(m_pEngineSound ) == 0 )
- {
- if ( gpGlobals->curtime > m_flEngineIdleTime )
- {
- // If we've finished playing the engine start sound, start playing the idle sound.
- controller.Play( m_pEngineSound, ENGINE_MAX_VOLUME, 100 );
-
- // Ramp down the engine idle sound over time so that we can ramp it back up again based on speed.
- controller.SoundChangeVolume( m_pEngineSound, ENGINE_MIN_VOLUME, ENGINE_DUCK_TIME );
- controller.SoundChangePitch( m_pEngineSound, ENGINE_MIN_PITCH, ENGINE_DUCK_TIME );
-
- // Reduce the volume of the engine idle sound after our ears get 'used' to it.
- m_flEngineDuckTime = gpGlobals->curtime + ENGINE_DUCK_TIME;
- }
- }
- else if ( gpGlobals->curtime > m_flEngineDuckTime )
- {
- controller.SoundChangeVolume( m_pEngineSound, RemapValClamped(speedRatio, 0, 1.0, ENGINE_MIN_VOLUME, ENGINE_MAX_VOLUME ), 0.0 );
- controller.SoundChangePitch( m_pEngineSound, RemapValClamped( speedRatio, 0, 1.0, ENGINE_MIN_PITCH, ENGINE_MAX_PITCH ), 0 );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAirboat::UpdateFanSound( CSoundEnvelopeController &controller, float speedRatio )
-{
- #define FAN_MIN_VOLUME 0.0
- #define FAN_MAX_VOLUME 0.82
- #define FAN_DUCK_VOLUME 0.22
- #define FAN_CHANGE_VOLUME_TIME 1.0 // seconds over which to change the volume
- #define FAN_DUCK_TIME 2.0 // seconds over which to duck the fan sound
-
- // Manage the state of the fan sound.
- if (speedRatio >= 0.8)
- {
- // Crossfade between a 'max speed' fan sound and the normal fan sound.
- controller.SoundChangeVolume( m_pFanSound, RemapValClamped( speedRatio, 0.8, 1.0, FAN_MAX_VOLUME, FAN_MIN_VOLUME ), FAN_CHANGE_VOLUME_TIME );
- controller.SoundChangeVolume( m_pFanMaxSpeedSound, RemapValClamped( speedRatio, 0.8, 1.0, FAN_MIN_VOLUME, FAN_MAX_VOLUME ), FAN_CHANGE_VOLUME_TIME );
-
- if (!m_bFadeOutFan)
- {
- m_bFadeOutFan = true;
- controller.SoundChangeVolume( m_pFanSound, FAN_DUCK_VOLUME, FAN_DUCK_TIME );
- }
- }
- else
- {
- m_bFadeOutFan = false;
- controller.SoundChangeVolume( m_pFanSound, RemapValClamped( fabs(m_flThrottle), 0, 1.0, FAN_MIN_VOLUME, FAN_MAX_VOLUME ), 0.25 );
- controller.SoundChangeVolume( m_pFanMaxSpeedSound, 0.0, 0.0 );
- }
-
- controller.SoundChangePitch( m_pFanSound, 100 * (fabs(m_flThrottle) + 0.2), 0.25 );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAirboat::UpdateWaterSound( CSoundEnvelopeController &controller, float speedRatio )
-{
- int nWaterLevel = GetWaterLevel();
-
- // Manage the state of the water stopped sound (gentle lapping at the pontoons).
- if ( nWaterLevel == 0 )
- {
- controller.SoundChangeVolume(m_pWaterStoppedSound, 0.0, 0.0);
- }
- else
- {
- if ( m_nPrevWaterLevel == 0 )
- {
- Vector vecVelocityWorld;
- GetVelocity( &vecVelocityWorld, NULL );
-
- if ( ( fabs( vecVelocityWorld.x ) > 400 ) || ( fabs( vecVelocityWorld.y ) > 400 ) || ( fabs( vecVelocityWorld.z ) > 400 ) )
- {
- // Landed in the water. Play a splash sound.
- EmitSound( "Airboat_impact_splash" );
-
- if ( fabs( vecVelocityWorld.z ) > 200 )
- {
- // Landed hard in the water. Play a smack sound.
- EmitSound( "Airboat_impact_hard" );
- }
- }
- }
-
- if (speedRatio <= 0.1)
- {
- if (!controller.SoundGetVolume(m_pWaterStoppedSound))
- {
- // Fade in the water stopped sound over 2 seconds.
- controller.SoundChangeVolume(m_pWaterStoppedSound, 1.0, 2.0);
- m_flWaterStoppedPitchTime = gpGlobals->curtime + random->RandomFloat(1.0, 3.0);
- }
- else if (gpGlobals->curtime > m_flWaterStoppedPitchTime)
- {
- controller.SoundChangeVolume(m_pWaterStoppedSound, random->RandomFloat(0.2, 1.0), random->RandomFloat(1.0, 3.0));
- controller.SoundChangePitch(m_pWaterStoppedSound, random->RandomFloat(90, 110), random->RandomFloat(1.0, 3.0));
- m_flWaterStoppedPitchTime = gpGlobals->curtime + random->RandomFloat(2.0, 4.0);
- }
- }
- else
- {
- if (controller.SoundGetVolume(m_pWaterStoppedSound))
- {
- // Fade out the water stopped sound over 1 second.
- controller.SoundChangeVolume(m_pWaterStoppedSound, 0.0, 1.0);
- }
- }
- }
-
- // Manage the state of the water fast sound (water hissing under the pontoons).
- if ( nWaterLevel == 0 )
- {
- controller.SoundChangeVolume(m_pWaterFastSound, 0.0, 0.0);
- }
- else
- {
- controller.SoundChangeVolume( m_pWaterFastSound, speedRatio, 0.0 );
- }
-
- m_nPrevWaterLevel = nWaterLevel;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAirboat::UpdateSound()
-{
- if (!GetDriver())
- return;
-
- CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
-
- // Sample the data that we need for sounds.
- CFourWheelVehiclePhysics *pPhysics = GetPhysics();
- int speed = pPhysics->GetSpeed();
- int maxSpeed = pPhysics->GetMaxSpeed();
- float speedRatio = clamp((float)speed / (float)maxSpeed, 0, 1);
-
- //Msg("speedRatio=%f\n", speedRatio);
-
- UpdateWeaponSound();
- UpdateEngineSound( controller, speedRatio );
- UpdateFanSound( controller, speedRatio );
- UpdateWaterSound( controller, speedRatio );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAirboat::UpdateSplashEffects( void )
-{
- // Splash effects.
- CreateSplash( AIRBOAT_SPLASH_RIPPLE );
-// CreateSplash( AIRBOAT_SPLASH_SPRAY );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-const char *CPropAirboat::GetTracerType( void )
-{
- if ( gpGlobals->curtime >= m_flNextHeavyShotTime )
- return "AirboatGunHeavyTracer";
-
- return "AirboatGunTracer";
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAirboat::DoMuzzleFlash( void )
-{
- CEffectData data;
- data.m_nEntIndex = entindex();
- data.m_nAttachmentIndex = m_nGunBarrelAttachment;
- data.m_flScale = 1.0f;
- DispatchEffect( "AirboatMuzzleFlash", data );
-
- BaseClass::DoMuzzleFlash();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-#define GUN_WINDUP_TIME 1.5f
-
-// NVNT Convar for airboat gun magnitude
-ConVar hap_airboat_gun_mag("hap_airboat_gun_mag", "3", 0);
-
-void CPropAirboat::FireGun( )
-{
- // Get the gun position.
- Vector vecGunPosition;
- Vector vecForward;
- GetAttachment( m_nGunBarrelAttachment, vecGunPosition, &vecForward );
-
- // NOTE: For the airboat, unable to fire really means the aim is clamped
- Vector vecAimPoint;
- if ( !m_bUnableToFire )
- {
- // Trace from eyes and see what we hit.
- ComputeAimPoint( &vecAimPoint );
- }
- else
- {
- // We hit the clamp; just fire whichever way the gun is facing
- VectorMA( vecGunPosition, 1000.0f, vecForward, vecAimPoint );
- }
-
- // Get a ray from the gun to the target.
- Vector vecRay = vecAimPoint - vecGunPosition;
- VectorNormalize( vecRay );
-
- /*
- // Get the aiming direction
- Vector vecRay;
- AngleVectors( vecGunAngles, &vecRay );
- VectorNormalize( vecRay );
- */
-
- CAmmoDef *pAmmoDef = GetAmmoDef();
- int ammoType = pAmmoDef->Index( "AirboatGun" );
-
-#if defined( WIN32 ) && !defined( _X360 )
- // NVNT punch the players haptics by the magnitude cvar each round fired
- HapticPunch(m_hPlayer,0,0,hap_airboat_gun_mag.GetFloat());
-#endif
-
- FireBulletsInfo_t info;
- info.m_vecSrc = vecGunPosition;
- info.m_vecDirShooting = vecRay;
- info.m_flDistance = 4096;
- info.m_iAmmoType = ammoType;
- info.m_nFlags = FIRE_BULLETS_TEMPORARY_DANGER_SOUND;
-
- if ( gpGlobals->curtime >= m_flNextHeavyShotTime )
- {
- info.m_iShots = 1;
- info.m_vecSpread = VECTOR_CONE_PRECALCULATED;
- info.m_flDamageForceScale = 1000.0f;
- }
- else
- {
- info.m_iShots = 2;
- info.m_vecSpread = VECTOR_CONE_5DEGREES;
- }
-
- FireBullets( info );
-
- CBaseEntity *pDriver = GetDriver();
- CBasePlayer *pPlayerDriver;
- if( pDriver && pDriver->IsPlayer() )
- {
- pPlayerDriver = dynamic_cast<CBasePlayer*>(pDriver);
- if( pPlayerDriver )
- {
- pPlayerDriver->RumbleEffect( RUMBLE_AIRBOAT_GUN, 0, RUMBLE_FLAG_LOOP|RUMBLE_FLAG_ONLYONE );
- }
- }
-
- DoMuzzleFlash();
-
- // NOTE: This must occur after FireBullets
- if ( gpGlobals->curtime >= m_flNextHeavyShotTime )
- {
- m_flNextHeavyShotTime = gpGlobals->curtime + CANNON_HEAVY_SHOT_INTERVAL;
- }
-
- if ( gpGlobals->curtime >= m_flNextGunShakeTime )
- {
- UTIL_ScreenShakeObject( this, WorldSpaceCenter(), 0.2, 250.0, CANNON_SHAKE_INTERVAL, 250, SHAKE_START );
- m_flNextGunShakeTime = gpGlobals->curtime + 0.5 * CANNON_SHAKE_INTERVAL;
- }
-
- // Specifically kill APC missiles in the cone. But we're going to totally cheat
- // because it's hard to hit them when they are close.
- // Use the player's eye position as the center of the cone.
- if ( !m_hPlayer )
- return;
-
- Vector vecEyeDirection, vecEyePosition;
- if ( !m_bUnableToFire )
- {
- if ( IsX360() )
- {
- GetAttachment( m_nGunBarrelAttachment, vecEyePosition, &vecEyeDirection );
- }
- else
- {
- vecEyePosition = m_hPlayer->EyePosition();
- m_hPlayer->EyeVectors( &vecEyeDirection, NULL, NULL );
- }
- }
- else
- {
- vecEyePosition = vecGunPosition;
- vecEyeDirection = vecRay;
- }
-
- CAPCMissile *pEnt = FindAPCMissileInCone( vecEyePosition, vecEyeDirection, 2.5f );
- if ( pEnt && (pEnt->GetHealth() > 0) )
- {
- CTakeDamageInfo info( this, this, 1, DMG_AIRBOAT );
- CalculateBulletDamageForce( &info, ammoType, vecRay, pEnt->WorldSpaceCenter() );
- pEnt->TakeDamage( info );
-
- Vector vecVelocity = pEnt->GetAbsVelocity();
-
- // Pick a vector perpendicular to the vecRay which will push it away from the airboat
- Vector vecPerp;
- CrossProduct( Vector( 0, 0, 1 ), vecRay, vecPerp );
- vecPerp.z = 0.0f;
- if ( VectorNormalize( vecPerp ) > 1e-3 )
- {
- Vector vecCurrentDir;
- GetVectors( &vecCurrentDir, NULL, NULL );
- if ( DotProduct( vecPerp, vecCurrentDir ) > 0.0f )
- {
- vecPerp *= -1.0f;
- }
-
- vecPerp *= random->RandomFloat( 15, 25 );
- vecVelocity += vecPerp;
- pEnt->SetAbsVelocity( vecVelocity );
-// pEnt->DisableGuiding();
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-#define FIRING_DISCHARGE_RATE (1.0f / 3.0f)
-
-void CPropAirboat::UpdateGunState( CUserCmd *ucmd )
-{
- bool bStopRumble = false;
-
- if ( ucmd->buttons & IN_ATTACK )
- {
- if ( m_nGunState == GUN_STATE_IDLE )
- {
-// AddGestureSequence( LookupSequence( "fire_gun" ) );
- m_nGunState = GUN_STATE_FIRING;
- }
-
- if ( m_nAmmoCount > 0 )
- {
- RemoveAmmo( FIRING_DISCHARGE_RATE );
- FireGun( );
-
- if ( m_nAmmoCount == 0 )
- {
- EmitSound( "Airboat.FireGunRevDown" );
- bStopRumble = true;
-// RemoveAllGestures();
- }
- }
- }
- else
- {
- if ( m_nGunState != GUN_STATE_IDLE )
- {
- if ( m_nAmmoCount != 0 )
- {
- EmitSound( "Airboat.FireGunRevDown" );
- bStopRumble = true;
-// RemoveAllGestures();
- }
- m_nGunState = GUN_STATE_IDLE;
- }
- }
-
- if( bStopRumble )
- {
- CBaseEntity *pDriver = GetDriver();
- CBasePlayer *pPlayerDriver;
- if( pDriver && pDriver->IsPlayer() )
- {
- pPlayerDriver = dynamic_cast<CBasePlayer*>(pDriver);
- if( pPlayerDriver )
- {
- pPlayerDriver->RumbleEffect( RUMBLE_AIRBOAT_GUN, 0, RUMBLE_FLAG_STOP );
- }
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAirboat::DriveVehicle( float flFrameTime, CUserCmd *ucmd, int iButtonsDown, int iButtonsReleased )
-{
- if ( ucmd->impulse == 100 )
- {
- if (HeadlightIsOn())
- {
- HeadlightTurnOff();
- }
- else
- {
- HeadlightTurnOn();
- }
- }
-
- // Fire gun.
- if ( HasGun() )
- {
- UpdateGunState( ucmd );
- }
-
- m_VehiclePhysics.UpdateDriverControls( ucmd, TICK_INTERVAL );
-
- // Create splashes.
- UpdateSplashEffects();
-
- // Save this data.
- m_flThrottle = m_VehiclePhysics.GetThrottle();
- m_nSpeed = m_VehiclePhysics.GetSpeed();
- m_nRPM = m_VehiclePhysics.GetRPM();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pPlayer -
-// *pMoveData -
-//-----------------------------------------------------------------------------
-void CPropAirboat::ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMoveData )
-{
- BaseClass::ProcessMovement( pPlayer, pMoveData );
-
- if ( gpGlobals->frametime != 0 )
- {
- // Create danger sounds in front of the vehicle.
- CreateDangerSounds();
-
- // Play a sound around us to make NPCs pay attention to us
- if ( m_VehiclePhysics.GetThrottle() > 0 )
- {
- CSoundEnt::InsertSound( SOUND_PLAYER_VEHICLE, pPlayer->GetAbsOrigin(), 3500, 0.1f, pPlayer, SOUNDENT_CHANNEL_REPEATED_PHYSICS_DANGER );
- }
- }
-
- Vector vecVelocityWorld;
- GetVelocity( &vecVelocityWorld, NULL );
- Vector vecVelocityLocal;
- WorldToEntitySpace( GetAbsOrigin() + vecVelocityWorld, &vecVelocityLocal );
-
- m_vecPhysVelocity = vecVelocityLocal;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Create danger sounds in front of the vehicle.
-//-----------------------------------------------------------------------------
-void CPropAirboat::CreateDangerSounds( void )
-{
- QAngle vehicleAngles = GetLocalAngles();
- Vector vecStart = GetAbsOrigin();
- Vector vecDir, vecRight;
-
- GetVectors( &vecDir, &vecRight, NULL );
-
- const float soundDuration = 0.25;
- float speed = m_VehiclePhysics.GetHLSpeed();
-
- // Make danger sounds ahead of the vehicle
- if ( fabs(speed) > 120 )
- {
- Vector vecSpot;
-
- float steering = m_VehiclePhysics.GetSteering();
- if ( steering != 0 )
- {
- if ( speed > 0 )
- {
- vecDir += vecRight * steering * 0.5;
- }
- else
- {
- vecDir -= vecRight * steering * 0.5;
- }
- VectorNormalize(vecDir);
- }
-
- const float radius = speed * 0.4;
-
- // 0.7 seconds ahead
- vecSpot = vecStart + vecDir * (speed * 0.7f);
- CSoundEnt::InsertSound( SOUND_DANGER, vecSpot, radius, soundDuration, this, SOUNDENT_CHANNEL_REPEATED_DANGER );
- CSoundEnt::InsertSound( SOUND_PHYSICS_DANGER, vecSpot, radius, soundDuration, this, SOUNDENT_CHANNEL_REPEATED_PHYSICS_DANGER );
- //NDebugOverlay::Box(vecSpot, Vector(-radius,-radius,-radius),Vector(radius,radius,radius), 255, 0, 255, 0, soundDuration);
-
-#if 0
- // put sounds a bit to left and right but slightly closer to vehicle to make
- // a "cone" of sound in front of it.
- trace_t tr;
- vecSpot = vecStart + vecDir * (speed * 0.5f) - vecRight * speed * 0.5;
- UTIL_TraceLine( vecStart, vecSpot, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
- CSoundEnt::InsertSound( SOUND_DANGER, vecSpot, 400, soundDuration, this, 1 );
-
- vecSpot = vecStart + vecDir * (speed * 0.5f) + vecRight * speed * 0.5;
- UTIL_TraceLine( vecStart, vecSpot, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
- CSoundEnt::InsertSound( SOUND_DANGER, vecSpot, 400, soundDuration, this, 2);
-#endif
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAirboat::DampenEyePosition( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles )
-{
- // Get the frametime. (Check to see if enough time has passed to warrent dampening).
- float flFrameTime = gpGlobals->frametime;
- if ( flFrameTime < AIRBOAT_FRAMETIME_MIN )
- {
- vecVehicleEyePos = m_vecLastEyePos;
- DampenUpMotion( vecVehicleEyePos, vecVehicleEyeAngles, 0.0f );
- return;
- }
-
- // Keep static the sideways motion.
-
- // Dampen forward/backward motion.
- DampenForwardMotion( vecVehicleEyePos, vecVehicleEyeAngles, flFrameTime );
-
- // Blend up/down motion.
- DampenUpMotion( vecVehicleEyePos, vecVehicleEyeAngles, flFrameTime );
-}
-
-
-//-----------------------------------------------------------------------------
-// Use the controller as follows:
-// speed += ( pCoefficientsOut[0] * ( targetPos - currentPos ) + pCoefficientsOut[1] * ( targetSpeed - currentSpeed ) ) * flDeltaTime;
-//-----------------------------------------------------------------------------
-void CPropAirboat::ComputePDControllerCoefficients( float *pCoefficientsOut,
- float flFrequency, float flDampening,
- float flDeltaTime )
-{
- float flKs = 9.0f * flFrequency * flFrequency;
- float flKd = 4.5f * flFrequency * flDampening;
-
- float flScale = 1.0f / ( 1.0f + flKd * flDeltaTime + flKs * flDeltaTime * flDeltaTime );
-
- pCoefficientsOut[0] = flKs * flScale;
- pCoefficientsOut[1] = ( flKd + flKs * flDeltaTime ) * flScale;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAirboat::DampenForwardMotion( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles, float flFrameTime )
-{
- // Get forward vector.
- Vector vecForward;
- AngleVectors( vecVehicleEyeAngles, &vecForward);
-
- // Simulate the eye position forward based on the data from last frame
- // (assumes no acceleration - it will get that from the "spring").
- Vector vecCurrentEyePos = m_vecLastEyePos + m_vecEyeSpeed * flFrameTime;
-
- // Calculate target speed based on the current vehicle eye position and the last vehicle eye position and frametime.
- Vector vecVehicleEyeSpeed = ( vecVehicleEyePos - m_vecLastEyeTarget ) / flFrameTime;
- m_vecLastEyeTarget = vecVehicleEyePos;
-
- // Calculate the speed and position deltas.
- Vector vecDeltaSpeed = vecVehicleEyeSpeed - m_vecEyeSpeed;
- Vector vecDeltaPos = vecVehicleEyePos - vecCurrentEyePos;
-
- // Clamp.
- if ( vecDeltaPos.Length() > AIRBOAT_DELTA_LENGTH_MAX )
- {
- float flSign = vecForward.Dot( vecVehicleEyeSpeed ) >= 0.0f ? -1.0f : 1.0f;
- vecVehicleEyePos += flSign * ( vecForward * AIRBOAT_DELTA_LENGTH_MAX );
- m_vecLastEyePos = vecVehicleEyePos;
- m_vecEyeSpeed = vecVehicleEyeSpeed;
- return;
- }
-
- // Generate an updated (dampening) speed for use in next frames position extrapolation.
- float flCoefficients[2];
- ComputePDControllerCoefficients( flCoefficients, r_AirboatViewDampenFreq.GetFloat(), r_AirboatViewDampenDamp.GetFloat(), flFrameTime );
- m_vecEyeSpeed += ( ( flCoefficients[0] * vecDeltaPos + flCoefficients[1] * vecDeltaSpeed ) * flFrameTime );
-
- // Save off data for next frame.
- m_vecLastEyePos = vecCurrentEyePos;
-
- // Move eye forward/backward.
- Vector vecForwardOffset = vecForward * ( vecForward.Dot( vecDeltaPos ) );
- vecVehicleEyePos -= vecForwardOffset;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAirboat::DampenUpMotion( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles, float flFrameTime )
-{
- // Get up vector.
- Vector vecUp;
- AngleVectors( vecVehicleEyeAngles, NULL, NULL, &vecUp );
- vecUp.z = clamp( vecUp.z, 0.0f, vecUp.z );
- vecVehicleEyePos.z += r_AirboatViewZHeight.GetFloat() * vecUp.z;
-
- // NOTE: Should probably use some damped equation here.
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPropAirboat::CreateSplash( int nSplashType )
-{
- if ( GetWaterLevel( ) == 0 )
- return;
-
- Vector vecSplashPoint;
- Vector vecForward, vecUp;
- GetAttachment( m_nSplashAttachment, vecSplashPoint, &vecForward, &vecUp, NULL );
-
- CEffectData data;
- data.m_fFlags = 0;
- data.m_vOrigin = vecSplashPoint;
- if ( GetWaterType() & CONTENTS_SLIME )
- {
- data.m_fFlags |= FX_WATER_IN_SLIME;
- }
-
- switch ( nSplashType )
- {
- case AIRBOAT_SPLASH_SPRAY:
- {
- Vector vecSplashDir;
- vecSplashDir = ( vecForward + vecUp ) * 0.5f;
- VectorNormalize( vecSplashDir );
- data.m_vNormal = vecSplashDir;
- data.m_flScale = 10.0f + random->RandomFloat( 0, 10.0f * 0.25 );
- //DispatchEffect( "waterripple", data );
- DispatchEffect( "watersplash", data );
- }
- case AIRBOAT_SPLASH_RIPPLE:
- {
- /*
- Vector vecSplashDir;
- vecSplashDir = vecUp;
- data.m_vNormal = vecSplashDir;
- data.m_flScale = AIRBOAT_SPLASH_RIPPLE_SIZE + random->RandomFloat( 0, AIRBOAT_SPLASH_RIPPLE_SIZE * 0.25 );
- DispatchEffect( "waterripple", data );
- */
- }
- default:
- {
- return;
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Overloaded to calculate stress damage.
-//-----------------------------------------------------------------------------
-void CPropAirboat::VPhysicsUpdate( IPhysicsObject *pPhysics )
-{
- BaseClass::VPhysicsUpdate( pPhysics );
-
- if ( airboat_fatal_stress.GetFloat() > 0 )
- {
- ApplyStressDamage( pPhysics );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns the damage that should be dealt to the driver due to
-// stress (vphysics objects exerting pressure on us).
-//-----------------------------------------------------------------------------
-float CPropAirboat::CalculatePhysicsStressDamage( vphysics_objectstress_t *pStressOut, IPhysicsObject *pPhysics )
-{
- vphysics_objectstress_t stressOut;
- CalculateObjectStress( pPhysics, this, &stressOut );
-
- //if ( ( stressOut.exertedStress > 100 ) || ( stressOut.receivedStress > 100 ) )
- // Msg( "stress: %f %d %f\n", stressOut.exertedStress, stressOut.hasNonStaticStress, stressOut.receivedStress );
-
- // Make sure the stress isn't from being stuck inside some static object.
- // If we're being crushed by more than the fatal stress amount, kill the driver.
- if ( stressOut.hasNonStaticStress && ( stressOut.receivedStress > airboat_fatal_stress.GetFloat() ) )
- {
- // if stuck, don't do this!
- if ( !(pPhysics->GetGameFlags() & FVPHYSICS_PENETRATING) )
- {
- // Kill the driver!
- return 1000;
- }
- }
-
- return 0;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Applies stress damage to the player/driver.
-//-----------------------------------------------------------------------------
-void CPropAirboat::ApplyStressDamage( IPhysicsObject *pPhysics )
-{
- vphysics_objectstress_t stressOut;
- float damage = CalculatePhysicsStressDamage( &stressOut, pPhysics );
- if ( ( damage > 0 ) && ( m_hPlayer != NULL ) )
- {
- CTakeDamageInfo dmgInfo( GetWorldEntity(), GetWorldEntity(), vec3_origin, vec3_origin, damage, DMG_CRUSH );
- dmgInfo.SetDamageForce( Vector( 0, 0, -stressOut.receivedStress * GetCurrentGravity() * gpGlobals->frametime ) );
- dmgInfo.SetDamagePosition( GetAbsOrigin() );
- m_hPlayer->TakeDamage( dmgInfo );
- }
-}
-
+//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "vehicle_base.h" +#include "engine/IEngineSound.h" +#include "in_buttons.h" +#include "ammodef.h" +#include "IEffects.h" +#include "beam_shared.h" +#include "weapon_gauss.h" +#include "soundenvelope.h" +#include "decals.h" +#include "soundent.h" +#include "te_effect_dispatch.h" +#include "physics_saverestore.h" +#include "movevars_shared.h" +#include "npc_attackchopper.h" +#include "weapon_rpg.h" +#include "vphysics/constraints.h" +#include "world.h" +#include "rumble_shared.h" +// NVNT for airboat weapon fire +#include "haptics/haptic_utils.h" +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern ConVar sv_vehicle_autoaim_scale; + +#define VEHICLE_HITBOX_DRIVER 1 + +// +// Body groups. +// +#define AIRBOAT_BODYGROUP_GUN 1 +#define AIRBOAT_BODYGROUP_PROP 2 +#define AIRBOAT_BODYGROUP_BLUR 3 + +#define AIRBOAT_LOCK_SPEED 10 // Airboat must be going slower than this for player to enter or exit, in in/sec + +#define AIRBOAT_DELTA_LENGTH_MAX 12.0f // 1 foot +#define AIRBOAT_FRAMETIME_MIN 1e-6 + +#define AIRBOAT_SPLASH_RIPPLE 0 +#define AIRBOAT_SPLASH_SPRAY 1 +#define AIRBOAT_SPLASH_RIPPLE_SIZE 20.0f + +// +// Pose parameters. +// +#define AIRBOAT_GUN_YAW "vehicle_weapon_yaw" +#define AIRBOAT_GUN_PITCH "vehicle_weapon_pitch" +#define AIRBOAT_FRAME_FLEX_LEFT "Frame_Flex_L" +#define AIRBOAT_FRAME_FLEX_RIGHT "Frame_Flex_R" + +#define CANNON_MAX_UP_PITCH 60.0f +#define CANNON_MAX_DOWN_PITCH 30.0f +#define CANNON_MAX_RIGHT_YAW 165.0f +#define CANNON_MAX_LEFT_YAW 75.0f + +#define CANNON_HEAVY_SHOT_INTERVAL 0.2f +#define CANNON_SHAKE_INTERVAL 1.0f + +static ConVar sk_airboat_max_ammo("sk_airboat_max_ammo", "100" ); +static ConVar sk_airboat_recharge_rate("sk_airboat_recharge_rate", "15" ); +static ConVar sk_airboat_drain_rate("sk_airboat_drain_rate", "10" ); +static ConVar hud_airboathint_numentries( "hud_airboathint_numentries", "10", FCVAR_NONE ); +static ConVar airboat_fatal_stress( "airboat_fatal_stress", "5000", FCVAR_NONE, "Amount of stress in kg that would kill the airboat driver." ); + +extern ConVar autoaim_max_dist; + +class CPropAirboat : public CPropVehicleDriveable +{ + DECLARE_CLASS( CPropAirboat, CPropVehicleDriveable ); + +public: + + DECLARE_SERVERCLASS(); + DECLARE_DATADESC(); + + // CPropVehicle + virtual void ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMoveData ); + virtual void DriveVehicle( float flFrameTime, CUserCmd *ucmd, int iButtonsDown, int iButtonsReleased ); + void DampenEyePosition( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles ); + bool ShouldThink() { return true; } + + // CBaseEntity + void Think(void); + void Precache( void ); + void Spawn( void ); + virtual void OnRestore(); + virtual void Activate(); + virtual void UpdateOnRemove(); + virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() | FCAP_USE_IN_RADIUS; }; + virtual void DoMuzzleFlash( void ); + virtual void StopLoopingSounds(); + + // position to shoot at + virtual Vector BodyTarget( const Vector &posSrc, bool bNoisy ); + virtual Vector GetSmoothedVelocity( void ); + + virtual void EnterVehicle( CBaseCombatCharacter *pPlayer ); + + virtual bool AllowBlockedExit( CBaseCombatCharacter *pPlayer, int nRole ) { return false; } + virtual void PreExitVehicle( CBaseCombatCharacter *pPlayer, int nRole ); + virtual void ExitVehicle( int nRole ); + + void ComputePDControllerCoefficients( float *pCoefficientsOut, float flFrequency, float flDampening, float flDeltaTime ); + void DampenForwardMotion( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles, float flFrameTime ); + void DampenUpMotion( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles, float flFrameTime ); + + virtual void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator ); + virtual int OnTakeDamage( const CTakeDamageInfo &info ); + + void VPhysicsUpdate( IPhysicsObject *pPhysics ); + + // Scraping noises for the various things we drive on. + virtual void VPhysicsFriction( IPhysicsObject *pObject, float energy, int surfaceProps, int surfacePropsHit ); + + bool HeadlightIsOn( void ) { return m_bHeadlightIsOn; } + void HeadlightTurnOn( void ); + void HeadlightTurnOff( void ); + + virtual bool ShouldDrawWaterImpacts( void ); + + bool ShouldForceExit() { return m_bForcedExit; } + void ClearForcedExit() { m_bForcedExit = false; } + + // Input handlers. + void InputWake( inputdata_t &inputdata ); + void InputExitVehicle( inputdata_t &inputdata ); + void InputEnableGun( inputdata_t &inputdata ); + void InputStartRotorWashForces( inputdata_t &inputdata ); + void InputStopRotorWashForces( inputdata_t &inputdata ); + + // Allows the shooter to change the impact effect of his bullets + virtual void DoImpactEffect( trace_t &tr, int nDamageType ); + + // Airboat passengers do not directly receive damage from blasts or radiation damage + virtual bool PassengerShouldReceiveDamage( CTakeDamageInfo &info ) + { + if ( info.GetDamageType() & DMG_VEHICLE ) + return true; + + return (info.GetDamageType() & (DMG_RADIATION|DMG_BLAST|DMG_CRUSH) ) == 0; + } + + const char *GetTracerType( void ); + +private: + + void CreateAntiFlipConstraint(); + + void ApplyStressDamage( IPhysicsObject *pPhysics ); + float CalculatePhysicsStressDamage( vphysics_objectstress_t *pStressOut, IPhysicsObject *pPhysics ); + + void CreateDangerSounds( void ); + + void FireGun( ); + + void UpdateSplashEffects( void ); + void CreateSplash( int nSplashType ); + + // Purpose: Aim Gun at a target + void AimGunAt( const Vector &endPos, float flInterval ); + + // Purpose: Returns the direction the gun is currently aiming at + void GetGunAimDirection( Vector *resultDir ); + + // Recharges the ammo based on speed + void RechargeAmmo(); + + // Removes the ammo... + void RemoveAmmo( float flAmmoAmount ); + + // Purpose: + void ComputeAimPoint( Vector *pVecAimPoint ); + + // Do the right thing for the gun + void UpdateGunState( CUserCmd *ucmd ); + + // Sound management + void CreateSounds(); + void UpdateSound(); + void UpdateWeaponSound(); + void UpdateEngineSound( CSoundEnvelopeController &controller, float speedRatio ); + void UpdateFanSound( CSoundEnvelopeController &controller, float speedRatio ); + void UpdateWaterSound( CSoundEnvelopeController &controller, float speedRatio ); + + void UpdatePropeller(); + void UpdateGauge(); + + void CreatePlayerBlocker(); + void DestroyPlayerBlocker(); + void EnablePlayerBlocker( bool bEnable ); + +private: + + enum + { + GUN_STATE_IDLE = 0, + GUN_STATE_FIRING, + }; + + Vector m_vecLastEyePos; + Vector m_vecLastEyeTarget; + Vector m_vecEyeSpeed; + + //float m_flHandbrakeTime; // handbrake after the fact to keep vehicles from rolling + //bool m_bInitialHandbrake; + + bool m_bForcedExit; + + int m_nGunRefAttachment; + int m_nGunBarrelAttachment; + float m_aimYaw; + float m_aimPitch; + float m_flChargeRemainder; + float m_flDrainRemainder; + int m_nGunState; + float m_flNextHeavyShotTime; + float m_flNextGunShakeTime; + + CNetworkVar( int, m_nAmmoCount ); + CNetworkVar( bool, m_bHeadlightIsOn ); + EHANDLE m_hAvoidSphere; + + int m_nSplashAttachment; + + float m_flPrevThrottle; // Throttle during last think. Used for detecting state changes. + float m_flSpinRate; // Current rate of spin of propeller: 0 = min, 1.0 = max + float m_flTargetSpinRate; // Target rate of spin of propeller: 0 = min, 1.0 = max + float m_flPropTime; // Time to turn on/off the prop. + float m_flBlurTime; // Time to turn on/off the blur. + + CSoundPatch *m_pFanSound; + CSoundPatch *m_pFanMaxSpeedSound; + CSoundPatch *m_pEngineSound; + CSoundPatch *m_pWaterFastSound; + CSoundPatch *m_pWaterStoppedSound; + CSoundPatch *m_pGunFiringSound; + + float m_flEngineIdleTime; // Time to start playing the engine's idle sound. + float m_flEngineDuckTime; // Time to reduce the volume of the engine's idle sound. + + bool m_bFadeOutFan; // Fade out fan sound after cruising at max speed for a while. + + int m_nPrevWaterLevel; // Used for detecting transitions into/out of water. + float m_flWaterStoppedPitchTime; // Time to pitch shift the water stopped sound. + + float m_flLastImpactEffectTime; + int m_iNumberOfEntries; + + IPhysicsConstraint *m_pAntiFlipConstraint; // A ragdoll constraint that prevents us from flipping. + + CHandle<CEntityBlocker> m_hPlayerBlocker; + + CNetworkVar( Vector, m_vecPhysVelocity ); + + CNetworkVar( int, m_nExactWaterLevel ); + + IMPLEMENT_NETWORK_VAR_FOR_DERIVED( m_nWaterLevel ); + +}; + +IMPLEMENT_SERVERCLASS_ST( CPropAirboat, DT_PropAirboat ) + SendPropBool( SENDINFO( m_bHeadlightIsOn ) ), + SendPropInt( SENDINFO( m_nAmmoCount ), 9 ), + SendPropInt( SENDINFO( m_nExactWaterLevel ) ), + SendPropInt( SENDINFO( m_nWaterLevel ) ), + SendPropVector( SENDINFO( m_vecPhysVelocity ) ), +END_SEND_TABLE(); + +LINK_ENTITY_TO_CLASS( prop_vehicle_airboat, CPropAirboat ); + +BEGIN_DATADESC( CPropAirboat ) + DEFINE_FIELD( m_vecLastEyePos, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( m_vecLastEyeTarget, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( m_vecEyeSpeed, FIELD_VECTOR ), + +// DEFINE_FIELD( m_flHandbrakeTime, FIELD_TIME ), +// DEFINE_FIELD( m_bInitialHandbrake,FIELD_BOOLEAN ), +// DEFINE_FIELD( m_nGunRefAttachment, FIELD_INTEGER ), +// DEFINE_FIELD( m_nGunBarrelAttachment, FIELD_INTEGER ), + DEFINE_FIELD( m_aimYaw, FIELD_FLOAT ), + DEFINE_FIELD( m_aimPitch, FIELD_FLOAT ), + DEFINE_FIELD( m_flChargeRemainder, FIELD_FLOAT ), + DEFINE_FIELD( m_flDrainRemainder, FIELD_FLOAT ), + DEFINE_FIELD( m_nGunState, FIELD_INTEGER ), + DEFINE_FIELD( m_flNextHeavyShotTime, FIELD_TIME ), + DEFINE_FIELD( m_flNextGunShakeTime, FIELD_TIME ), + DEFINE_FIELD( m_nAmmoCount, FIELD_INTEGER ), + DEFINE_FIELD( m_bHeadlightIsOn, FIELD_BOOLEAN ), + DEFINE_FIELD( m_hAvoidSphere, FIELD_EHANDLE ), +// DEFINE_FIELD( m_nSplashAttachment, FIELD_INTEGER ), + DEFINE_FIELD( m_hPlayerBlocker, FIELD_EHANDLE ), + + DEFINE_FIELD( m_vecPhysVelocity, FIELD_VECTOR ), + DEFINE_FIELD( m_nExactWaterLevel, FIELD_INTEGER ), + + DEFINE_FIELD( m_flPrevThrottle, FIELD_FLOAT ), + DEFINE_FIELD( m_flSpinRate, FIELD_FLOAT ), + DEFINE_FIELD( m_flTargetSpinRate, FIELD_FLOAT ), + DEFINE_FIELD( m_flPropTime, FIELD_TIME ), + DEFINE_FIELD( m_flBlurTime, FIELD_TIME ), + DEFINE_FIELD( m_bForcedExit, FIELD_BOOLEAN ), + + DEFINE_SOUNDPATCH( m_pFanSound ), + DEFINE_SOUNDPATCH( m_pFanMaxSpeedSound ), + DEFINE_SOUNDPATCH( m_pEngineSound ), + DEFINE_SOUNDPATCH( m_pWaterFastSound ), + DEFINE_SOUNDPATCH( m_pWaterStoppedSound ), + DEFINE_SOUNDPATCH( m_pGunFiringSound ), + + DEFINE_PHYSPTR( m_pAntiFlipConstraint ), + + DEFINE_FIELD( m_flEngineIdleTime, FIELD_TIME ), + DEFINE_FIELD( m_flEngineDuckTime, FIELD_TIME ), + DEFINE_FIELD( m_bFadeOutFan, FIELD_BOOLEAN ), + + DEFINE_FIELD( m_nPrevWaterLevel, FIELD_INTEGER ), + DEFINE_FIELD( m_flWaterStoppedPitchTime, FIELD_TIME ), + + DEFINE_FIELD( m_flLastImpactEffectTime, FIELD_TIME ), + DEFINE_FIELD( m_iNumberOfEntries, FIELD_INTEGER ), + + DEFINE_INPUTFUNC( FIELD_BOOLEAN, "EnableGun", InputEnableGun ), + DEFINE_INPUTFUNC( FIELD_VOID, "StartRotorWashForces", InputStartRotorWashForces ), + DEFINE_INPUTFUNC( FIELD_VOID, "StopRotorWashForces", InputStopRotorWashForces ), + DEFINE_INPUTFUNC( FIELD_VOID, "ExitVehicle", InputExitVehicle ), + DEFINE_INPUTFUNC( FIELD_VOID, "Wake", InputWake ), + +END_DATADESC() + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropAirboat::Precache( void ) +{ + BaseClass::Precache(); + + PrecacheScriptSound( "Airboat_engine_stop" ); + PrecacheScriptSound( "Airboat_engine_start" ); + + PrecacheScriptSound( "Airboat.FireGunHeavy" ); + PrecacheScriptSound( "Airboat.FireGunRevDown"); + + PrecacheScriptSound( "Airboat_engine_idle" ); + PrecacheScriptSound( "Airboat_engine_fullthrottle" ); + PrecacheScriptSound( "Airboat_fan_idle" ); + PrecacheScriptSound( "Airboat_fan_fullthrottle" ); + PrecacheScriptSound( "Airboat_water_stopped" ); + PrecacheScriptSound( "Airboat_water_fast" ); + PrecacheScriptSound( "Airboat_impact_splash" ); + PrecacheScriptSound( "Airboat_impact_hard" ); + + PrecacheScriptSound( "Airboat_headlight_on" ); + PrecacheScriptSound( "Airboat_headlight_off" ); + + PrecacheScriptSound( "Airboat.FireGunLoop" ); + + PrecacheMaterial( "effects/splashwake1" ); + PrecacheMaterial( "effects/splashwake4" ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropAirboat::Spawn( void ) +{ + m_nAmmoCount = m_bHasGun ? 0 : -1; + m_hAvoidSphere = CreateHelicopterAvoidanceSphere( this, 0, 50.0f, false ); + m_flLastImpactEffectTime = -1; + m_iNumberOfEntries = 0; + + // Setup vehicle as a ray-cast airboat. + SetVehicleType( VEHICLE_TYPE_AIRBOAT_RAYCAST ); + SetCollisionGroup( COLLISION_GROUP_VEHICLE ); + BaseClass::Spawn(); + + AddSolidFlags( FSOLID_NOT_STANDABLE ); + SetAnimatedEveryTick( true ); + + // Handbrake data. + //m_flHandbrakeTime = gpGlobals->curtime + 0.1; + //m_bInitialHandbrake = false; + m_VehiclePhysics.SetHasBrakePedal( false ); + + m_flMinimumSpeedToEnterExit = AIRBOAT_LOCK_SPEED; + + m_takedamage = DAMAGE_EVENTS_ONLY; + + SetBodygroup(AIRBOAT_BODYGROUP_GUN, m_bHasGun); + SetBodygroup(AIRBOAT_BODYGROUP_PROP, true); + + SetPoseParameter( AIRBOAT_GUN_YAW, 0 ); + SetPoseParameter( AIRBOAT_GUN_PITCH, 0 ); + SetPoseParameter( AIRBOAT_FRAME_FLEX_LEFT, 0 ); + SetPoseParameter( AIRBOAT_FRAME_FLEX_RIGHT, 0 ); + + m_aimYaw = 0; + m_aimPitch = 0; + m_bUnableToFire = true; + m_nGunState = GUN_STATE_IDLE; + + SetPoseParameter( "Steer_Shock", 0.0f ); + + // Get the physics object so we can adjust the buoyancy. + IPhysicsObject *pPhysAirboat = VPhysicsGetObject(); + if ( pPhysAirboat ) + { + pPhysAirboat->SetBuoyancyRatio( 0.0f ); + PhysSetGameFlags( pPhysAirboat, FVPHYSICS_HEAVY_OBJECT ); + } + + //CreateAntiFlipConstraint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Create a ragdoll constraint that prevents us from flipping. +//----------------------------------------------------------------------------- +void CPropAirboat::CreateAntiFlipConstraint() +{ + constraint_ragdollparams_t ragdoll; + ragdoll.Defaults(); + + // Don't prevent the boat from moving, just flipping. + ragdoll.onlyAngularLimits = true; + + // Put the ragdoll constraint in the space of the airboat. + SetIdentityMatrix( ragdoll.constraintToAttached ); + BuildObjectRelativeXform( g_PhysWorldObject, VPhysicsGetObject(), ragdoll.constraintToReference ); + + ragdoll.axes[0].minRotation = -100; + ragdoll.axes[0].maxRotation = 100; + ragdoll.axes[1].minRotation = -100; + ragdoll.axes[1].maxRotation = 100; + ragdoll.axes[2].minRotation = -180; + ragdoll.axes[2].maxRotation = 180; + + m_pAntiFlipConstraint = physenv->CreateRagdollConstraint( g_PhysWorldObject, VPhysicsGetObject(), NULL, ragdoll ); + + //NDebugOverlay::Cross3DOriented( ragdoll.constraintToReference, 128, 255, true, 100 ); +} + + +//----------------------------------------------------------------------------- +// Attachment indices +//----------------------------------------------------------------------------- +void CPropAirboat::UpdateOnRemove() +{ + BaseClass::UpdateOnRemove(); + + if ( m_hAvoidSphere ) + { + UTIL_Remove( m_hAvoidSphere ); + m_hAvoidSphere = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Attachment indices +//----------------------------------------------------------------------------- +void CPropAirboat::Activate() +{ + BaseClass::Activate(); + + m_nGunRefAttachment = LookupAttachment( "gun" ); + m_nGunBarrelAttachment = LookupAttachment( "muzzle" ); + m_nSplashAttachment = LookupAttachment( "splash_pt" ); + + CreateSounds(); + + CBaseServerVehicle *pServerVehicle = dynamic_cast<CBaseServerVehicle *>(GetServerVehicle()); + if ( pServerVehicle ) + { + if( pServerVehicle->GetPassenger() ) + { + // If a boat comes back from a save game with a driver, make sure the engine rumble starts up. + pServerVehicle->StartEngineRumble(); + } + } + + //CreatePlayerBlocker(); + //EnablePlayerBlocker( true ); +} + + +void CPropAirboat::CreatePlayerBlocker() +{ + Assert( m_hPlayerBlocker == NULL ); + DestroyPlayerBlocker(); + + m_hPlayerBlocker = CEntityBlocker::Create( GetAbsOrigin(), Vector( -84, -32, 0 ), Vector( 54, 32, 84 ), this, false ); + if ( m_hPlayerBlocker != NULL ) + { + m_hPlayerBlocker->SetParent( this ); + m_hPlayerBlocker->SetLocalOrigin( vec3_origin ); + m_hPlayerBlocker->SetLocalAngles( vec3_angle ); + m_hPlayerBlocker->SetCollisionGroup( COLLISION_GROUP_PLAYER ); + m_hPlayerBlocker->AddSolidFlags( FSOLID_NOT_SOLID ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropAirboat::DestroyPlayerBlocker() +{ + if ( m_hPlayerBlocker != NULL ) + { + UTIL_Remove( m_hPlayerBlocker ); + } + + m_hPlayerBlocker = NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : bEnable - +//----------------------------------------------------------------------------- +void CPropAirboat::EnablePlayerBlocker( bool bEnable ) +{ + if ( m_hPlayerBlocker != NULL ) + { + if ( bEnable ) + { + m_hPlayerBlocker->RemoveSolidFlags( FSOLID_NOT_SOLID ); + } + else + { + m_hPlayerBlocker->AddSolidFlags( FSOLID_NOT_SOLID ); + } + } +} + + +//----------------------------------------------------------------------------- +// Update the weapon sounds +//----------------------------------------------------------------------------- +#define MIN_CHARGE_SOUND 0.4f +#define MIN_PITCH_CHANGE ( MIN_CHARGE_SOUND + ( ( 1.0f - MIN_CHARGE_SOUND ) / 3.0f ) ) +#define VOLUME_CHANGE_TIME 0.5f + +void CPropAirboat::UpdateWeaponSound() +{ + if ( HasGun() ) + { + CSoundEnvelopeController *pController = &CSoundEnvelopeController::GetController(); + float flVolume = pController->SoundGetVolume( m_pGunFiringSound ); + if ( (m_nGunState == GUN_STATE_IDLE) || (m_nAmmoCount == 0) ) + { + if ( flVolume != 0.0f ) + { + pController->SoundChangeVolume( m_pGunFiringSound, 0.0f, 0.01f ); + } + } + else + { + if ( flVolume != 1.0f ) + { + pController->SoundChangeVolume( m_pGunFiringSound, 1.0f, 0.01f ); + } + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Force the player to exit the vehicle. +//----------------------------------------------------------------------------- +void CPropAirboat::InputExitVehicle( inputdata_t &inputdata ) +{ + m_bForcedExit = true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Force the airboat to wake up. This was needed to fix a last-minute +// bug for the XBox -- the airboat didn't fall with the platform +// in d1_canals_10b. +//----------------------------------------------------------------------------- +void CPropAirboat::InputWake( inputdata_t &inputdata ) +{ + VPhysicsGetObject()->Wake(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Input handler to enable or disable the airboat's mounted gun. +//----------------------------------------------------------------------------- +void CPropAirboat::InputEnableGun( inputdata_t &inputdata ) +{ + m_bHasGun = inputdata.value.Bool(); + SetBodygroup(AIRBOAT_BODYGROUP_GUN, m_bHasGun); + + // When enabling the gun, give full ammo + if ( m_bHasGun ) + { + m_nAmmoCount = sk_airboat_max_ammo.GetInt(); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Input handler to enable or disable the airboat's mounted gun. +//----------------------------------------------------------------------------- +void CPropAirboat::InputStartRotorWashForces( inputdata_t &inputdata ) +{ + RemoveEFlags( EFL_NO_ROTORWASH_PUSH ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Input handler to enable or disable the airboat's mounted gun. +//----------------------------------------------------------------------------- +void CPropAirboat::InputStopRotorWashForces( inputdata_t &inputdata ) +{ + AddEFlags( EFL_NO_ROTORWASH_PUSH ); +} + + +//----------------------------------------------------------------------------- +// Creating vphysics +//----------------------------------------------------------------------------- +void CPropAirboat::OnRestore() +{ + BaseClass::OnRestore(); + + IPhysicsObject *pPhysAirboat = VPhysicsGetObject(); + if ( pPhysAirboat ) + { + pPhysAirboat->SetBuoyancyRatio( 0.0f ); + PhysSetGameFlags( pPhysAirboat, FVPHYSICS_HEAVY_OBJECT ); + } + + // If the player's in the vehicle, NPCs should ignore it + if ( GetDriver() ) + { + SetNavIgnore(); + } +} + + +//----------------------------------------------------------------------------- +// Used for navigation +//----------------------------------------------------------------------------- +void CPropAirboat::EnterVehicle( CBaseCombatCharacter *pPlayer ) +{ + BaseClass::EnterVehicle( pPlayer ); + + //EnablePlayerBlocker( false ); + + // NPCs like manhacks should try to hit us + SetNavIgnore(); + + // Play the engine start sound. + float flDuration; + EmitSound( "Airboat_engine_start", 0.0, &flDuration ); + m_VehiclePhysics.TurnOn(); + + // Start playing the engine's idle sound as the startup sound finishes. + m_flEngineIdleTime = gpGlobals->curtime + flDuration - 0.1; +} + + +//----------------------------------------------------------------------------- +// Purpose: Called when exiting, just before playing the exit animation. +//----------------------------------------------------------------------------- +void CPropAirboat::PreExitVehicle( CBaseCombatCharacter *pPlayer, int nRole ) +{ + if ( HeadlightIsOn() ) + { + HeadlightTurnOff(); + } + + // Stop shooting. + m_nGunState = GUN_STATE_IDLE; + + CBaseEntity *pDriver = GetDriver(); + CBasePlayer *pPlayerDriver; + if( pDriver && pDriver->IsPlayer() ) + { + pPlayerDriver = dynamic_cast<CBasePlayer*>(pDriver); + if( pPlayerDriver ) + { + pPlayerDriver->RumbleEffect( RUMBLE_AIRBOAT_GUN, 0, RUMBLE_FLAG_STOP ); + } + } + + BaseClass::PreExitVehicle( pPlayer, nRole ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Called when exiting, after completing the exit animation. +// Input : iRole - +//----------------------------------------------------------------------------- +void CPropAirboat::ExitVehicle( int nRole ) +{ + CBaseEntity *pDriver = GetDriver(); + + //EnablePlayerBlocker( true ); + + BaseClass::ExitVehicle( nRole ); + + if (!pDriver) + return; + +#if 0 + // On ORANGE BOX this is causing a big blank box to show up, which is worse + // than the HUD hint persisting for a little while, so don't do it. (sjb) + // clear the hint + UTIL_HudHintText( pDriver, "" ); +#endif + + // NPCs like manhacks should try to avoid us again + ClearNavIgnore(); + + // Play the engine shutoff sound. + CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); + CPASAttenuationFilter filter( this ); + + EmitSound_t ep; + ep.m_nChannel = CHAN_BODY; + ep.m_pSoundName = "Airboat_engine_stop"; + ep.m_flVolume = controller.SoundGetVolume( m_pEngineSound ); + ep.m_SoundLevel = SNDLVL_NORM; + ep.m_nPitch = controller.SoundGetPitch( m_pEngineSound ); + + EmitSound( filter, entindex(), ep ); + m_VehiclePhysics.TurnOff(); + + // Shut off the airboat sounds. + controller.SoundChangeVolume( m_pEngineSound, 0.0, 0.0 ); + controller.SoundChangeVolume( m_pFanSound, 0.0, 0.0 ); + controller.SoundChangeVolume( m_pFanMaxSpeedSound, 0.0, 0.0 ); + controller.SoundChangeVolume( m_pWaterStoppedSound, 0.0, 0.0 ); + controller.SoundChangeVolume( m_pWaterFastSound, 0.0, 0.0 ); + controller.SoundChangeVolume( m_pGunFiringSound, 0.0, 0.0 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropAirboat::HeadlightTurnOn( void ) +{ + EmitSound( "Airboat_headlight_on" ); + m_bHeadlightIsOn = true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropAirboat::HeadlightTurnOff( void ) +{ + EmitSound( "Airboat_headlight_off" ); + m_bHeadlightIsOn = false; +} + + +//----------------------------------------------------------------------------- +// position to shoot at +//----------------------------------------------------------------------------- +Vector CPropAirboat::BodyTarget( const Vector &posSrc, bool bNoisy ) +{ + Vector vecPosition; + QAngle angles; + if ( GetServerVehicle()->GetPassenger() ) + { + // FIXME: Reconcile this with other functions that store a cached version of the results here? + GetServerVehicle()->GetVehicleViewPosition( VEHICLE_ROLE_DRIVER, &vecPosition, &angles ); + } + else + { + vecPosition = WorldSpaceCenter(); + } + return vecPosition; +} + + +//----------------------------------------------------------------------------- +// Smoothed velocity +//----------------------------------------------------------------------------- +#define SMOOTHED_MIN_VELOCITY 75.0f +#define SMOOTHED_MAX_VELOCITY 150.0f + +Vector CPropAirboat::GetSmoothedVelocity( void ) +{ + // If we're going too slow, return the forward direction as the velocity + // for NPC prediction purposes + Vector vecSmoothedVelocity = BaseClass::GetSmoothedVelocity(); + float flSpeed = vecSmoothedVelocity.Length(); + if ( flSpeed >= SMOOTHED_MAX_VELOCITY ) + return vecSmoothedVelocity; + + Vector vecForward; + GetVectors( &vecForward, NULL, NULL ); + vecForward *= MAX( flSpeed, 1.0f ); + if ( flSpeed <= SMOOTHED_MIN_VELOCITY ) + return vecForward; + + float flBlend = SimpleSplineRemapVal( flSpeed, SMOOTHED_MIN_VELOCITY, SMOOTHED_MAX_VELOCITY, 0.0f, 1.0f ); + VectorLerp( vecForward, vecSmoothedVelocity, flBlend, vecSmoothedVelocity ); + return vecSmoothedVelocity; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropAirboat::TraceAttack( const CTakeDamageInfo &inputInfo, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator ) +{ + CTakeDamageInfo info = inputInfo; + if ( ptr->hitbox != VEHICLE_HITBOX_DRIVER ) + { + if ( inputInfo.GetDamageType() & DMG_BULLET ) + { + info.ScaleDamage( 0.0001 ); + } + } + + BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &info - +// &vecEnd - +// *pTraceFilter - +// *pVecTracerDest - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CPropAirboat::ShouldDrawWaterImpacts( void ) +{ + // The airboat spits out so much crap that we need to do cheaper versions + // of the impact effects. Also, we need to do less of them. + if ( m_flLastImpactEffectTime >= gpGlobals->curtime ) + return false; + + m_flLastImpactEffectTime = gpGlobals->curtime + 0.05f; + + return true; +} + +//----------------------------------------------------------------------------- +// Allows the shooter to change the impact effect of his bullets +//----------------------------------------------------------------------------- +void CPropAirboat::DoImpactEffect( trace_t &tr, int nDamageType ) +{ + // The airboat spits out so much crap that we need to do cheaper versions + // of the impact effects. Also, we need to do less of them. + if ( m_flLastImpactEffectTime == gpGlobals->curtime ) + return; + + // Randomly drop out + if ( random->RandomInt( 0, 5 ) ) + return; + + m_flLastImpactEffectTime = gpGlobals->curtime; + UTIL_ImpactTrace( &tr, nDamageType, "AirboatGunImpact" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CPropAirboat::OnTakeDamage( const CTakeDamageInfo &info ) +{ + // Do scaled up physics damage to the airboat + CTakeDamageInfo physDmg = info; + physDmg.ScaleDamage( 5 ); + if ( physDmg.GetDamageType() & DMG_BLAST ) + { + physDmg.SetDamageForce( info.GetDamageForce() * 10 ); + } + VPhysicsTakeDamage( physDmg ); + + // Check to do damage to driver + if ( m_hPlayer != NULL ) + { + // Don't pass along physics damage + if ( info.GetDamageType() & (DMG_CRUSH|DMG_RADIATION) ) + return 0; + + // Take the damage (strip out the DMG_BLAST) + CTakeDamageInfo playerDmg = info; + + // Mark that we're passing it to the player so the base player accepts the damage + playerDmg.SetDamageType( info.GetDamageType() | DMG_VEHICLE ); + + // Deal the damage to the passenger + m_hPlayer->TakeDamage( playerDmg ); + } + + return 0; +} + + +//----------------------------------------------------------------------------- +// Scraping noises for the various things we drive on. +//----------------------------------------------------------------------------- +void CPropAirboat::VPhysicsFriction( IPhysicsObject *pObject, float energy, int surfaceProps, int surfacePropsHit ) +{ + // don't make noise for hidden/invisible/sky materials + const surfacedata_t *phit = physprops->GetSurfaceData( surfacePropsHit ); + const surfacedata_t *pprops = physprops->GetSurfaceData( surfaceProps ); + if ( phit->game.material == 'X' || pprops->game.material == 'X' ) + return; + + // FIXME: Make different scraping sounds here + float flVolume = 0.3f; + + surfacedata_t *psurf = physprops->GetSurfaceData( surfaceProps ); + const char *pSoundName = physprops->GetString( psurf->sounds.scrapeRough ); + + PhysFrictionSound( this, pObject, pSoundName, psurf->soundhandles.scrapeRough, flVolume ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Aim Gun at a target position. +//----------------------------------------------------------------------------- +// This fixes an optimizer bug that was causing targetYaw and targetPitch to +// always be reported as clamped, thus disabling the gun. Ack! +#pragma optimize("", off) +void CPropAirboat::AimGunAt( const Vector &aimPos, float flInterval ) +{ + matrix3x4_t gunMatrix; + GetAttachment( m_nGunRefAttachment, gunMatrix ); + + // transform the target position into gun space + Vector localTargetPosition; + VectorITransform( aimPos, gunMatrix, localTargetPosition ); + VectorNormalize( localTargetPosition ); + m_bUnableToFire = false; + m_vecGunCrosshair = aimPos; + + // do a look at in gun space (essentially a delta-lookat) + QAngle localTargetAngles; + VectorAngles( localTargetPosition, localTargetAngles ); + + // convert to +/- 180 degrees + localTargetAngles.x = UTIL_AngleDiff( localTargetAngles.x, 0 ); + localTargetAngles.y = UTIL_AngleDiff( localTargetAngles.y, 0 ); + + float targetYaw = m_aimYaw + localTargetAngles.y; + float targetPitch = m_aimPitch + localTargetAngles.x; + + // Constrain our angles + float newTargetYaw = clamp( targetYaw, -CANNON_MAX_RIGHT_YAW, CANNON_MAX_LEFT_YAW ); + float newTargetPitch = clamp( targetPitch, -CANNON_MAX_UP_PITCH, CANNON_MAX_DOWN_PITCH ); + + // If the angles have been clamped, we're looking outside of our valid range + if ( ( newTargetYaw != targetYaw ) || ( newTargetPitch != targetPitch ) ) + { + m_bUnableToFire = true; + } + + targetYaw = newTargetYaw; + targetPitch = newTargetPitch; + + m_aimYaw = targetYaw; + m_aimPitch = targetPitch; + + SetPoseParameter( AIRBOAT_GUN_YAW, m_aimYaw); + SetPoseParameter( AIRBOAT_GUN_PITCH, m_aimPitch ); + + InvalidateBoneCache(); + + // read back to avoid drift when hitting limits + // as long as the velocity is less than the delta between the limit and 180, this is fine. + m_aimPitch = GetPoseParameter( AIRBOAT_GUN_PITCH ); + m_aimYaw = GetPoseParameter( AIRBOAT_GUN_YAW ); +} +#pragma optimize("", on) + + +//----------------------------------------------------------------------------- +// Removes the ammo... +//----------------------------------------------------------------------------- +void CPropAirboat::RemoveAmmo( float flAmmoAmount ) +{ + m_flDrainRemainder += flAmmoAmount; + int nAmmoToRemove = (int)m_flDrainRemainder; + m_flDrainRemainder -= nAmmoToRemove; + m_nAmmoCount -= nAmmoToRemove; + if ( m_nAmmoCount < 0 ) + { + m_nAmmoCount = 0; + m_flDrainRemainder = 0.0f; + } +} + + +//----------------------------------------------------------------------------- +// Recharges the ammo... +//----------------------------------------------------------------------------- +void CPropAirboat::RechargeAmmo(void) +{ + if ( !m_bHasGun ) + { + m_nAmmoCount = -1; + return; + } + + int nMaxAmmo = sk_airboat_max_ammo.GetInt(); + if ( m_nAmmoCount == nMaxAmmo ) + return; + + float flRechargeRate = sk_airboat_recharge_rate.GetInt(); + float flChargeAmount = flRechargeRate * gpGlobals->frametime; + if ( m_flDrainRemainder != 0.0f ) + { + if ( m_flDrainRemainder >= flChargeAmount ) + { + m_flDrainRemainder -= flChargeAmount; + return; + } + else + { + flChargeAmount -= m_flDrainRemainder; + m_flDrainRemainder = 0.0f; + } + } + + m_flChargeRemainder += flChargeAmount; + int nAmmoToAdd = (int)m_flChargeRemainder; + m_flChargeRemainder -= nAmmoToAdd; + m_nAmmoCount += nAmmoToAdd; + if ( m_nAmmoCount > nMaxAmmo ) + { + m_nAmmoCount = nMaxAmmo; + m_flChargeRemainder = 0.0f; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropAirboat::ComputeAimPoint( Vector *pVecAimPoint ) +{ + Vector vecEyeDirection; + + if( g_pGameRules->GetAutoAimMode() == AUTOAIM_ON_CONSOLE ) + { + // Use autoaim as the eye dir. + autoaim_params_t params; + + params.m_fScale = AUTOAIM_SCALE_DEFAULT * sv_vehicle_autoaim_scale.GetFloat(); + params.m_fMaxDist = autoaim_max_dist.GetFloat(); + m_hPlayer->GetAutoaimVector( params ); + + vecEyeDirection = params.m_vecAutoAimDir; + } + else + { + m_hPlayer->EyeVectors( &vecEyeDirection, NULL, NULL ); + } + + Vector vecEndPos; + VectorMA( m_hPlayer->EyePosition(), MAX_TRACE_LENGTH, vecEyeDirection, vecEndPos ); + trace_t trace; + UTIL_TraceLine( m_hPlayer->EyePosition(), vecEndPos, MASK_SHOT, this, COLLISION_GROUP_NONE, &trace ); + *pVecAimPoint = trace.endpos; +} + + +//----------------------------------------------------------------------------- +// Purpose: Manages animation and sound state. +//----------------------------------------------------------------------------- +void CPropAirboat::Think(void) +{ + BaseClass::Think(); + + // set handbrake after physics sim settles down +// if ( gpGlobals->curtime < m_flHandbrakeTime ) +// { +// SetNextThink( gpGlobals->curtime ); +// } +// else if ( !m_bInitialHandbrake ) // after initial timer expires, set the handbrake +// { +// m_bInitialHandbrake = true; +// m_VehiclePhysics.SetHandbrake( true ); +// m_VehiclePhysics.Think(); +// } + + // Find the vertical extents of the boat + Vector startPos, endPos; + CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 1.0f ), &startPos ); + CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 0.0f ), &endPos ); + + // Look for water along that volume. + // Make a very vertically thin box and sweep it along the ray. + Vector vecMins = CollisionProp()->OBBMins(); + Vector vecMaxs = CollisionProp()->OBBMaxs(); + vecMins.z = -0.1f; + vecMaxs.z = 0.1f; + + trace_t tr; + UTIL_TraceHull( startPos, endPos, vecMins, vecMaxs, (CONTENTS_WATER|CONTENTS_SLIME), this, COLLISION_GROUP_NONE, &tr ); + + // If we hit something, then save off the info + if ( tr.fraction != 1.0f ) + { + m_nExactWaterLevel = tr.endpos.z; + + // Classify what we're in + if ( tr.contents & CONTENTS_SLIME ) + { + // We fake this value to mean type, instead of level + SetWaterLevel( 2 ); + } + else + { + // This simply signifies water + SetWaterLevel( 1 ); + } + } + else + { + // Not in water + SetWaterLevel( 0 ); + } + + StudioFrameAdvance(); + + // If the enter or exit animation has finished, tell the server vehicle + if ( IsSequenceFinished() && ( m_bEnterAnimOn || m_bExitAnimOn ) ) + { + // The first few time we get into the jeep, print the jeep help + if ( m_iNumberOfEntries < hud_airboathint_numentries.GetInt() && !m_bExitAnimOn ) + { + UTIL_HudHintText( m_hPlayer, "#Valve_Hint_BoatKeys" ); + m_iNumberOfEntries++; + } + + GetServerVehicle()->HandleEntryExitFinish( m_bExitAnimOn, false ); + + // Start the vehicle's idle animation + ResetSequence(LookupSequence("propeller_spin1")); + ResetClientsideFrame(); + } + + // FIXME: Slam the crosshair every think -- if we don't do this it disappears randomly, never to return. + if ( ( m_hPlayer.Get() != NULL ) && !( m_bEnterAnimOn || m_bExitAnimOn ) ) + { + m_hPlayer->m_Local.m_iHideHUD &= ~HIDEHUD_VEHICLE_CROSSHAIR; + } + + // Aim the gun + if ( HasGun() && m_hPlayer.Get() && !m_bEnterAnimOn && !m_bExitAnimOn ) + { + Vector vecAimPoint; + ComputeAimPoint( &vecAimPoint ); + AimGunAt( vecAimPoint, gpGlobals->frametime ); + } + + if ( ShouldForceExit() ) + { + ClearForcedExit(); + m_hPlayer->LeaveVehicle(); + } + + if ( HasGun() && ( m_nGunState == GUN_STATE_IDLE ) ) + { + RechargeAmmo(); + } + + UpdateSound(); + UpdatePropeller(); + UpdateGauge(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropAirboat::UpdatePropeller() +{ + if ((m_bExitAnimOn) || (m_bEnterAnimOn)) + return; + + #define SPIN_RATE_MED 0.2 + #define SPIN_RATE_HIGH 0.6 + + // Determine target spin rate from throttle. + float flTargetSpinRate = m_flThrottle; + if ((flTargetSpinRate == 0) && (m_hPlayer)) + { + // Always keep the fan moving a little when we have a driver. + flTargetSpinRate = 0.2; + } + + // Save the current spin rate to determine state transitions. + float flPrevSpinRate = m_flSpinRate; + + // Determine new spin rate, + if (m_flSpinRate < flTargetSpinRate) + { + if (flTargetSpinRate > 0) + { + m_flSpinRate += gpGlobals->frametime * 1.0; + } + else + { + m_flSpinRate += gpGlobals->frametime * 0.4; + } + + if (m_flSpinRate > flTargetSpinRate) + { + m_flSpinRate = flTargetSpinRate; + } + } + else if (m_flSpinRate > flTargetSpinRate) + { + m_flSpinRate -= gpGlobals->frametime * 0.4; + if (m_flSpinRate < flTargetSpinRate) + { + m_flSpinRate = flTargetSpinRate; + } + } + + // Update prop & blur based on new spin rate. + if (fabs(m_flSpinRate) > SPIN_RATE_HIGH) + { + if (fabs(flPrevSpinRate) <= SPIN_RATE_HIGH) + { + SetBodygroup(AIRBOAT_BODYGROUP_PROP, false); + SetBodygroup(AIRBOAT_BODYGROUP_BLUR, true); + SetSequence(LookupSequence("propeller_spin1")); + } + } + else if (fabs(m_flSpinRate) > SPIN_RATE_MED) + { + if ((fabs(flPrevSpinRate) <= SPIN_RATE_MED) || (fabs(flPrevSpinRate) > SPIN_RATE_HIGH)) + { + SetBodygroup(AIRBOAT_BODYGROUP_PROP, true); + SetBodygroup(AIRBOAT_BODYGROUP_BLUR, true); + SetSequence(LookupSequence("propeller_spin1")); + } + } + else + { + if (fabs(flPrevSpinRate) > SPIN_RATE_MED) + { + SetBodygroup(AIRBOAT_BODYGROUP_PROP, true); + SetBodygroup(AIRBOAT_BODYGROUP_BLUR, false); + SetSequence(LookupSequence("propeller_spin1")); + } + } + + SetPlaybackRate( m_flSpinRate ); + + m_flPrevThrottle = m_flThrottle; +} + + +//----------------------------------------------------------------------------- +// Purpose: Updates the speedometer. +//----------------------------------------------------------------------------- +void CPropAirboat::UpdateGauge() +{ + CFourWheelVehiclePhysics *pPhysics = GetPhysics(); + int speed = pPhysics->GetSpeed(); + int maxSpeed = pPhysics->GetMaxSpeed(); + float speedRatio = clamp( (float)speed / (float)maxSpeed, 0, 1 ); + + SetPoseParameter( "Gauge", speedRatio ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropAirboat::CreateSounds() +{ + CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); + + CPASAttenuationFilter filter( this ); + + if (!m_pEngineSound) + { + m_pEngineSound = controller.SoundCreate( filter, entindex(), "Airboat_engine_idle" ); + controller.Play( m_pEngineSound, 0, 100 ); + } + + if (!m_pFanSound) + { + m_pFanSound = controller.SoundCreate( filter, entindex(), "Airboat_fan_idle" ); + controller.Play( m_pFanSound, 0, 100 ); + } + + if (!m_pFanMaxSpeedSound) + { + m_pFanMaxSpeedSound = controller.SoundCreate( filter, entindex(), "Airboat_fan_fullthrottle" ); + controller.Play( m_pFanMaxSpeedSound, 0, 100 ); + } + + if (!m_pWaterStoppedSound) + { + m_pWaterStoppedSound = controller.SoundCreate( filter, entindex(), "Airboat_water_stopped" ); + controller.Play( m_pWaterStoppedSound, 0, 100 ); + } + + if (!m_pWaterFastSound) + { + m_pWaterFastSound = controller.SoundCreate( filter, entindex(), "Airboat_water_fast" ); + controller.Play( m_pWaterFastSound, 0, 100 ); + } + + if (!m_pGunFiringSound) + { + m_pGunFiringSound = controller.SoundCreate( filter, entindex(), "Airboat.FireGunLoop" ); + controller.Play( m_pGunFiringSound, 0, 100 ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropAirboat::StopLoopingSounds() +{ + CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); + + controller.SoundDestroy( m_pEngineSound ); + m_pEngineSound = NULL; + + controller.SoundDestroy( m_pFanSound ); + m_pFanSound = NULL; + + controller.SoundDestroy( m_pFanMaxSpeedSound ); + m_pFanMaxSpeedSound = NULL; + + controller.SoundDestroy( m_pWaterStoppedSound ); + m_pWaterStoppedSound = NULL; + + controller.SoundDestroy( m_pWaterFastSound ); + m_pWaterFastSound = NULL; + + controller.SoundDestroy( m_pGunFiringSound ); + m_pGunFiringSound = NULL; + + BaseClass::StopLoopingSounds(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Manage the state of the engine sound. +//----------------------------------------------------------------------------- +void CPropAirboat::UpdateEngineSound( CSoundEnvelopeController &controller, float speedRatio ) +{ + #define ENGINE_MIN_VOLUME 0.22 + #define ENGINE_MAX_VOLUME 0.62 + #define ENGINE_MIN_PITCH 80 + #define ENGINE_MAX_PITCH 140 + #define ENGINE_DUCK_TIME 4.0 + + if ( controller.SoundGetVolume(m_pEngineSound ) == 0 ) + { + if ( gpGlobals->curtime > m_flEngineIdleTime ) + { + // If we've finished playing the engine start sound, start playing the idle sound. + controller.Play( m_pEngineSound, ENGINE_MAX_VOLUME, 100 ); + + // Ramp down the engine idle sound over time so that we can ramp it back up again based on speed. + controller.SoundChangeVolume( m_pEngineSound, ENGINE_MIN_VOLUME, ENGINE_DUCK_TIME ); + controller.SoundChangePitch( m_pEngineSound, ENGINE_MIN_PITCH, ENGINE_DUCK_TIME ); + + // Reduce the volume of the engine idle sound after our ears get 'used' to it. + m_flEngineDuckTime = gpGlobals->curtime + ENGINE_DUCK_TIME; + } + } + else if ( gpGlobals->curtime > m_flEngineDuckTime ) + { + controller.SoundChangeVolume( m_pEngineSound, RemapValClamped(speedRatio, 0, 1.0, ENGINE_MIN_VOLUME, ENGINE_MAX_VOLUME ), 0.0 ); + controller.SoundChangePitch( m_pEngineSound, RemapValClamped( speedRatio, 0, 1.0, ENGINE_MIN_PITCH, ENGINE_MAX_PITCH ), 0 ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropAirboat::UpdateFanSound( CSoundEnvelopeController &controller, float speedRatio ) +{ + #define FAN_MIN_VOLUME 0.0 + #define FAN_MAX_VOLUME 0.82 + #define FAN_DUCK_VOLUME 0.22 + #define FAN_CHANGE_VOLUME_TIME 1.0 // seconds over which to change the volume + #define FAN_DUCK_TIME 2.0 // seconds over which to duck the fan sound + + // Manage the state of the fan sound. + if (speedRatio >= 0.8) + { + // Crossfade between a 'max speed' fan sound and the normal fan sound. + controller.SoundChangeVolume( m_pFanSound, RemapValClamped( speedRatio, 0.8, 1.0, FAN_MAX_VOLUME, FAN_MIN_VOLUME ), FAN_CHANGE_VOLUME_TIME ); + controller.SoundChangeVolume( m_pFanMaxSpeedSound, RemapValClamped( speedRatio, 0.8, 1.0, FAN_MIN_VOLUME, FAN_MAX_VOLUME ), FAN_CHANGE_VOLUME_TIME ); + + if (!m_bFadeOutFan) + { + m_bFadeOutFan = true; + controller.SoundChangeVolume( m_pFanSound, FAN_DUCK_VOLUME, FAN_DUCK_TIME ); + } + } + else + { + m_bFadeOutFan = false; + controller.SoundChangeVolume( m_pFanSound, RemapValClamped( fabs(m_flThrottle), 0, 1.0, FAN_MIN_VOLUME, FAN_MAX_VOLUME ), 0.25 ); + controller.SoundChangeVolume( m_pFanMaxSpeedSound, 0.0, 0.0 ); + } + + controller.SoundChangePitch( m_pFanSound, 100 * (fabs(m_flThrottle) + 0.2), 0.25 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropAirboat::UpdateWaterSound( CSoundEnvelopeController &controller, float speedRatio ) +{ + int nWaterLevel = GetWaterLevel(); + + // Manage the state of the water stopped sound (gentle lapping at the pontoons). + if ( nWaterLevel == 0 ) + { + controller.SoundChangeVolume(m_pWaterStoppedSound, 0.0, 0.0); + } + else + { + if ( m_nPrevWaterLevel == 0 ) + { + Vector vecVelocityWorld; + GetVelocity( &vecVelocityWorld, NULL ); + + if ( ( fabs( vecVelocityWorld.x ) > 400 ) || ( fabs( vecVelocityWorld.y ) > 400 ) || ( fabs( vecVelocityWorld.z ) > 400 ) ) + { + // Landed in the water. Play a splash sound. + EmitSound( "Airboat_impact_splash" ); + + if ( fabs( vecVelocityWorld.z ) > 200 ) + { + // Landed hard in the water. Play a smack sound. + EmitSound( "Airboat_impact_hard" ); + } + } + } + + if (speedRatio <= 0.1) + { + if (!controller.SoundGetVolume(m_pWaterStoppedSound)) + { + // Fade in the water stopped sound over 2 seconds. + controller.SoundChangeVolume(m_pWaterStoppedSound, 1.0, 2.0); + m_flWaterStoppedPitchTime = gpGlobals->curtime + random->RandomFloat(1.0, 3.0); + } + else if (gpGlobals->curtime > m_flWaterStoppedPitchTime) + { + controller.SoundChangeVolume(m_pWaterStoppedSound, random->RandomFloat(0.2, 1.0), random->RandomFloat(1.0, 3.0)); + controller.SoundChangePitch(m_pWaterStoppedSound, random->RandomFloat(90, 110), random->RandomFloat(1.0, 3.0)); + m_flWaterStoppedPitchTime = gpGlobals->curtime + random->RandomFloat(2.0, 4.0); + } + } + else + { + if (controller.SoundGetVolume(m_pWaterStoppedSound)) + { + // Fade out the water stopped sound over 1 second. + controller.SoundChangeVolume(m_pWaterStoppedSound, 0.0, 1.0); + } + } + } + + // Manage the state of the water fast sound (water hissing under the pontoons). + if ( nWaterLevel == 0 ) + { + controller.SoundChangeVolume(m_pWaterFastSound, 0.0, 0.0); + } + else + { + controller.SoundChangeVolume( m_pWaterFastSound, speedRatio, 0.0 ); + } + + m_nPrevWaterLevel = nWaterLevel; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropAirboat::UpdateSound() +{ + if (!GetDriver()) + return; + + CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); + + // Sample the data that we need for sounds. + CFourWheelVehiclePhysics *pPhysics = GetPhysics(); + int speed = pPhysics->GetSpeed(); + int maxSpeed = pPhysics->GetMaxSpeed(); + float speedRatio = clamp((float)speed / (float)maxSpeed, 0, 1); + + //Msg("speedRatio=%f\n", speedRatio); + + UpdateWeaponSound(); + UpdateEngineSound( controller, speedRatio ); + UpdateFanSound( controller, speedRatio ); + UpdateWaterSound( controller, speedRatio ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropAirboat::UpdateSplashEffects( void ) +{ + // Splash effects. + CreateSplash( AIRBOAT_SPLASH_RIPPLE ); +// CreateSplash( AIRBOAT_SPLASH_SPRAY ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char *CPropAirboat::GetTracerType( void ) +{ + if ( gpGlobals->curtime >= m_flNextHeavyShotTime ) + return "AirboatGunHeavyTracer"; + + return "AirboatGunTracer"; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropAirboat::DoMuzzleFlash( void ) +{ + CEffectData data; + data.m_nEntIndex = entindex(); + data.m_nAttachmentIndex = m_nGunBarrelAttachment; + data.m_flScale = 1.0f; + DispatchEffect( "AirboatMuzzleFlash", data ); + + BaseClass::DoMuzzleFlash(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +#define GUN_WINDUP_TIME 1.5f + +// NVNT Convar for airboat gun magnitude +ConVar hap_airboat_gun_mag("hap_airboat_gun_mag", "3", 0); + +void CPropAirboat::FireGun( ) +{ + // Get the gun position. + Vector vecGunPosition; + Vector vecForward; + GetAttachment( m_nGunBarrelAttachment, vecGunPosition, &vecForward ); + + // NOTE: For the airboat, unable to fire really means the aim is clamped + Vector vecAimPoint; + if ( !m_bUnableToFire ) + { + // Trace from eyes and see what we hit. + ComputeAimPoint( &vecAimPoint ); + } + else + { + // We hit the clamp; just fire whichever way the gun is facing + VectorMA( vecGunPosition, 1000.0f, vecForward, vecAimPoint ); + } + + // Get a ray from the gun to the target. + Vector vecRay = vecAimPoint - vecGunPosition; + VectorNormalize( vecRay ); + + /* + // Get the aiming direction + Vector vecRay; + AngleVectors( vecGunAngles, &vecRay ); + VectorNormalize( vecRay ); + */ + + CAmmoDef *pAmmoDef = GetAmmoDef(); + int ammoType = pAmmoDef->Index( "AirboatGun" ); + +#if defined( WIN32 ) && !defined( _X360 ) + // NVNT punch the players haptics by the magnitude cvar each round fired + HapticPunch(m_hPlayer,0,0,hap_airboat_gun_mag.GetFloat()); +#endif + + FireBulletsInfo_t info; + info.m_vecSrc = vecGunPosition; + info.m_vecDirShooting = vecRay; + info.m_flDistance = 4096; + info.m_iAmmoType = ammoType; + info.m_nFlags = FIRE_BULLETS_TEMPORARY_DANGER_SOUND; + + if ( gpGlobals->curtime >= m_flNextHeavyShotTime ) + { + info.m_iShots = 1; + info.m_vecSpread = VECTOR_CONE_PRECALCULATED; + info.m_flDamageForceScale = 1000.0f; + } + else + { + info.m_iShots = 2; + info.m_vecSpread = VECTOR_CONE_5DEGREES; + } + + FireBullets( info ); + + CBaseEntity *pDriver = GetDriver(); + CBasePlayer *pPlayerDriver; + if( pDriver && pDriver->IsPlayer() ) + { + pPlayerDriver = dynamic_cast<CBasePlayer*>(pDriver); + if( pPlayerDriver ) + { + pPlayerDriver->RumbleEffect( RUMBLE_AIRBOAT_GUN, 0, RUMBLE_FLAG_LOOP|RUMBLE_FLAG_ONLYONE ); + } + } + + DoMuzzleFlash(); + + // NOTE: This must occur after FireBullets + if ( gpGlobals->curtime >= m_flNextHeavyShotTime ) + { + m_flNextHeavyShotTime = gpGlobals->curtime + CANNON_HEAVY_SHOT_INTERVAL; + } + + if ( gpGlobals->curtime >= m_flNextGunShakeTime ) + { + UTIL_ScreenShakeObject( this, WorldSpaceCenter(), 0.2, 250.0, CANNON_SHAKE_INTERVAL, 250, SHAKE_START ); + m_flNextGunShakeTime = gpGlobals->curtime + 0.5 * CANNON_SHAKE_INTERVAL; + } + + // Specifically kill APC missiles in the cone. But we're going to totally cheat + // because it's hard to hit them when they are close. + // Use the player's eye position as the center of the cone. + if ( !m_hPlayer ) + return; + + Vector vecEyeDirection, vecEyePosition; + if ( !m_bUnableToFire ) + { + if ( IsX360() ) + { + GetAttachment( m_nGunBarrelAttachment, vecEyePosition, &vecEyeDirection ); + } + else + { + vecEyePosition = m_hPlayer->EyePosition(); + m_hPlayer->EyeVectors( &vecEyeDirection, NULL, NULL ); + } + } + else + { + vecEyePosition = vecGunPosition; + vecEyeDirection = vecRay; + } + + CAPCMissile *pEnt = FindAPCMissileInCone( vecEyePosition, vecEyeDirection, 2.5f ); + if ( pEnt && (pEnt->GetHealth() > 0) ) + { + CTakeDamageInfo info( this, this, 1, DMG_AIRBOAT ); + CalculateBulletDamageForce( &info, ammoType, vecRay, pEnt->WorldSpaceCenter() ); + pEnt->TakeDamage( info ); + + Vector vecVelocity = pEnt->GetAbsVelocity(); + + // Pick a vector perpendicular to the vecRay which will push it away from the airboat + Vector vecPerp; + CrossProduct( Vector( 0, 0, 1 ), vecRay, vecPerp ); + vecPerp.z = 0.0f; + if ( VectorNormalize( vecPerp ) > 1e-3 ) + { + Vector vecCurrentDir; + GetVectors( &vecCurrentDir, NULL, NULL ); + if ( DotProduct( vecPerp, vecCurrentDir ) > 0.0f ) + { + vecPerp *= -1.0f; + } + + vecPerp *= random->RandomFloat( 15, 25 ); + vecVelocity += vecPerp; + pEnt->SetAbsVelocity( vecVelocity ); +// pEnt->DisableGuiding(); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +#define FIRING_DISCHARGE_RATE (1.0f / 3.0f) + +void CPropAirboat::UpdateGunState( CUserCmd *ucmd ) +{ + bool bStopRumble = false; + + if ( ucmd->buttons & IN_ATTACK ) + { + if ( m_nGunState == GUN_STATE_IDLE ) + { +// AddGestureSequence( LookupSequence( "fire_gun" ) ); + m_nGunState = GUN_STATE_FIRING; + } + + if ( m_nAmmoCount > 0 ) + { + RemoveAmmo( FIRING_DISCHARGE_RATE ); + FireGun( ); + + if ( m_nAmmoCount == 0 ) + { + EmitSound( "Airboat.FireGunRevDown" ); + bStopRumble = true; +// RemoveAllGestures(); + } + } + } + else + { + if ( m_nGunState != GUN_STATE_IDLE ) + { + if ( m_nAmmoCount != 0 ) + { + EmitSound( "Airboat.FireGunRevDown" ); + bStopRumble = true; +// RemoveAllGestures(); + } + m_nGunState = GUN_STATE_IDLE; + } + } + + if( bStopRumble ) + { + CBaseEntity *pDriver = GetDriver(); + CBasePlayer *pPlayerDriver; + if( pDriver && pDriver->IsPlayer() ) + { + pPlayerDriver = dynamic_cast<CBasePlayer*>(pDriver); + if( pPlayerDriver ) + { + pPlayerDriver->RumbleEffect( RUMBLE_AIRBOAT_GUN, 0, RUMBLE_FLAG_STOP ); + } + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropAirboat::DriveVehicle( float flFrameTime, CUserCmd *ucmd, int iButtonsDown, int iButtonsReleased ) +{ + if ( ucmd->impulse == 100 ) + { + if (HeadlightIsOn()) + { + HeadlightTurnOff(); + } + else + { + HeadlightTurnOn(); + } + } + + // Fire gun. + if ( HasGun() ) + { + UpdateGunState( ucmd ); + } + + m_VehiclePhysics.UpdateDriverControls( ucmd, TICK_INTERVAL ); + + // Create splashes. + UpdateSplashEffects(); + + // Save this data. + m_flThrottle = m_VehiclePhysics.GetThrottle(); + m_nSpeed = m_VehiclePhysics.GetSpeed(); + m_nRPM = m_VehiclePhysics.GetRPM(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pPlayer - +// *pMoveData - +//----------------------------------------------------------------------------- +void CPropAirboat::ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMoveData ) +{ + BaseClass::ProcessMovement( pPlayer, pMoveData ); + + if ( gpGlobals->frametime != 0 ) + { + // Create danger sounds in front of the vehicle. + CreateDangerSounds(); + + // Play a sound around us to make NPCs pay attention to us + if ( m_VehiclePhysics.GetThrottle() > 0 ) + { + CSoundEnt::InsertSound( SOUND_PLAYER_VEHICLE, pPlayer->GetAbsOrigin(), 3500, 0.1f, pPlayer, SOUNDENT_CHANNEL_REPEATED_PHYSICS_DANGER ); + } + } + + Vector vecVelocityWorld; + GetVelocity( &vecVelocityWorld, NULL ); + Vector vecVelocityLocal; + WorldToEntitySpace( GetAbsOrigin() + vecVelocityWorld, &vecVelocityLocal ); + + m_vecPhysVelocity = vecVelocityLocal; +} + + +//----------------------------------------------------------------------------- +// Purpose: Create danger sounds in front of the vehicle. +//----------------------------------------------------------------------------- +void CPropAirboat::CreateDangerSounds( void ) +{ + QAngle vehicleAngles = GetLocalAngles(); + Vector vecStart = GetAbsOrigin(); + Vector vecDir, vecRight; + + GetVectors( &vecDir, &vecRight, NULL ); + + const float soundDuration = 0.25; + float speed = m_VehiclePhysics.GetHLSpeed(); + + // Make danger sounds ahead of the vehicle + if ( fabs(speed) > 120 ) + { + Vector vecSpot; + + float steering = m_VehiclePhysics.GetSteering(); + if ( steering != 0 ) + { + if ( speed > 0 ) + { + vecDir += vecRight * steering * 0.5; + } + else + { + vecDir -= vecRight * steering * 0.5; + } + VectorNormalize(vecDir); + } + + const float radius = speed * 0.4; + + // 0.7 seconds ahead + vecSpot = vecStart + vecDir * (speed * 0.7f); + CSoundEnt::InsertSound( SOUND_DANGER, vecSpot, radius, soundDuration, this, SOUNDENT_CHANNEL_REPEATED_DANGER ); + CSoundEnt::InsertSound( SOUND_PHYSICS_DANGER, vecSpot, radius, soundDuration, this, SOUNDENT_CHANNEL_REPEATED_PHYSICS_DANGER ); + //NDebugOverlay::Box(vecSpot, Vector(-radius,-radius,-radius),Vector(radius,radius,radius), 255, 0, 255, 0, soundDuration); + +#if 0 + // put sounds a bit to left and right but slightly closer to vehicle to make + // a "cone" of sound in front of it. + trace_t tr; + vecSpot = vecStart + vecDir * (speed * 0.5f) - vecRight * speed * 0.5; + UTIL_TraceLine( vecStart, vecSpot, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); + CSoundEnt::InsertSound( SOUND_DANGER, vecSpot, 400, soundDuration, this, 1 ); + + vecSpot = vecStart + vecDir * (speed * 0.5f) + vecRight * speed * 0.5; + UTIL_TraceLine( vecStart, vecSpot, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); + CSoundEnt::InsertSound( SOUND_DANGER, vecSpot, 400, soundDuration, this, 2); +#endif + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropAirboat::DampenEyePosition( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles ) +{ + // Get the frametime. (Check to see if enough time has passed to warrent dampening). + float flFrameTime = gpGlobals->frametime; + if ( flFrameTime < AIRBOAT_FRAMETIME_MIN ) + { + vecVehicleEyePos = m_vecLastEyePos; + DampenUpMotion( vecVehicleEyePos, vecVehicleEyeAngles, 0.0f ); + return; + } + + // Keep static the sideways motion. + + // Dampen forward/backward motion. + DampenForwardMotion( vecVehicleEyePos, vecVehicleEyeAngles, flFrameTime ); + + // Blend up/down motion. + DampenUpMotion( vecVehicleEyePos, vecVehicleEyeAngles, flFrameTime ); +} + + +//----------------------------------------------------------------------------- +// Use the controller as follows: +// speed += ( pCoefficientsOut[0] * ( targetPos - currentPos ) + pCoefficientsOut[1] * ( targetSpeed - currentSpeed ) ) * flDeltaTime; +//----------------------------------------------------------------------------- +void CPropAirboat::ComputePDControllerCoefficients( float *pCoefficientsOut, + float flFrequency, float flDampening, + float flDeltaTime ) +{ + float flKs = 9.0f * flFrequency * flFrequency; + float flKd = 4.5f * flFrequency * flDampening; + + float flScale = 1.0f / ( 1.0f + flKd * flDeltaTime + flKs * flDeltaTime * flDeltaTime ); + + pCoefficientsOut[0] = flKs * flScale; + pCoefficientsOut[1] = ( flKd + flKs * flDeltaTime ) * flScale; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropAirboat::DampenForwardMotion( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles, float flFrameTime ) +{ + // Get forward vector. + Vector vecForward; + AngleVectors( vecVehicleEyeAngles, &vecForward); + + // Simulate the eye position forward based on the data from last frame + // (assumes no acceleration - it will get that from the "spring"). + Vector vecCurrentEyePos = m_vecLastEyePos + m_vecEyeSpeed * flFrameTime; + + // Calculate target speed based on the current vehicle eye position and the last vehicle eye position and frametime. + Vector vecVehicleEyeSpeed = ( vecVehicleEyePos - m_vecLastEyeTarget ) / flFrameTime; + m_vecLastEyeTarget = vecVehicleEyePos; + + // Calculate the speed and position deltas. + Vector vecDeltaSpeed = vecVehicleEyeSpeed - m_vecEyeSpeed; + Vector vecDeltaPos = vecVehicleEyePos - vecCurrentEyePos; + + // Clamp. + if ( vecDeltaPos.Length() > AIRBOAT_DELTA_LENGTH_MAX ) + { + float flSign = vecForward.Dot( vecVehicleEyeSpeed ) >= 0.0f ? -1.0f : 1.0f; + vecVehicleEyePos += flSign * ( vecForward * AIRBOAT_DELTA_LENGTH_MAX ); + m_vecLastEyePos = vecVehicleEyePos; + m_vecEyeSpeed = vecVehicleEyeSpeed; + return; + } + + // Generate an updated (dampening) speed for use in next frames position extrapolation. + float flCoefficients[2]; + ComputePDControllerCoefficients( flCoefficients, r_AirboatViewDampenFreq.GetFloat(), r_AirboatViewDampenDamp.GetFloat(), flFrameTime ); + m_vecEyeSpeed += ( ( flCoefficients[0] * vecDeltaPos + flCoefficients[1] * vecDeltaSpeed ) * flFrameTime ); + + // Save off data for next frame. + m_vecLastEyePos = vecCurrentEyePos; + + // Move eye forward/backward. + Vector vecForwardOffset = vecForward * ( vecForward.Dot( vecDeltaPos ) ); + vecVehicleEyePos -= vecForwardOffset; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropAirboat::DampenUpMotion( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles, float flFrameTime ) +{ + // Get up vector. + Vector vecUp; + AngleVectors( vecVehicleEyeAngles, NULL, NULL, &vecUp ); + vecUp.z = clamp( vecUp.z, 0.0f, vecUp.z ); + vecVehicleEyePos.z += r_AirboatViewZHeight.GetFloat() * vecUp.z; + + // NOTE: Should probably use some damped equation here. +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropAirboat::CreateSplash( int nSplashType ) +{ + if ( GetWaterLevel( ) == 0 ) + return; + + Vector vecSplashPoint; + Vector vecForward, vecUp; + GetAttachment( m_nSplashAttachment, vecSplashPoint, &vecForward, &vecUp, NULL ); + + CEffectData data; + data.m_fFlags = 0; + data.m_vOrigin = vecSplashPoint; + if ( GetWaterType() & CONTENTS_SLIME ) + { + data.m_fFlags |= FX_WATER_IN_SLIME; + } + + switch ( nSplashType ) + { + case AIRBOAT_SPLASH_SPRAY: + { + Vector vecSplashDir; + vecSplashDir = ( vecForward + vecUp ) * 0.5f; + VectorNormalize( vecSplashDir ); + data.m_vNormal = vecSplashDir; + data.m_flScale = 10.0f + random->RandomFloat( 0, 10.0f * 0.25 ); + //DispatchEffect( "waterripple", data ); + DispatchEffect( "watersplash", data ); + } + case AIRBOAT_SPLASH_RIPPLE: + { + /* + Vector vecSplashDir; + vecSplashDir = vecUp; + data.m_vNormal = vecSplashDir; + data.m_flScale = AIRBOAT_SPLASH_RIPPLE_SIZE + random->RandomFloat( 0, AIRBOAT_SPLASH_RIPPLE_SIZE * 0.25 ); + DispatchEffect( "waterripple", data ); + */ + } + default: + { + return; + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Overloaded to calculate stress damage. +//----------------------------------------------------------------------------- +void CPropAirboat::VPhysicsUpdate( IPhysicsObject *pPhysics ) +{ + BaseClass::VPhysicsUpdate( pPhysics ); + + if ( airboat_fatal_stress.GetFloat() > 0 ) + { + ApplyStressDamage( pPhysics ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns the damage that should be dealt to the driver due to +// stress (vphysics objects exerting pressure on us). +//----------------------------------------------------------------------------- +float CPropAirboat::CalculatePhysicsStressDamage( vphysics_objectstress_t *pStressOut, IPhysicsObject *pPhysics ) +{ + vphysics_objectstress_t stressOut; + CalculateObjectStress( pPhysics, this, &stressOut ); + + //if ( ( stressOut.exertedStress > 100 ) || ( stressOut.receivedStress > 100 ) ) + // Msg( "stress: %f %d %f\n", stressOut.exertedStress, stressOut.hasNonStaticStress, stressOut.receivedStress ); + + // Make sure the stress isn't from being stuck inside some static object. + // If we're being crushed by more than the fatal stress amount, kill the driver. + if ( stressOut.hasNonStaticStress && ( stressOut.receivedStress > airboat_fatal_stress.GetFloat() ) ) + { + // if stuck, don't do this! + if ( !(pPhysics->GetGameFlags() & FVPHYSICS_PENETRATING) ) + { + // Kill the driver! + return 1000; + } + } + + return 0; +} + + +//----------------------------------------------------------------------------- +// Purpose: Applies stress damage to the player/driver. +//----------------------------------------------------------------------------- +void CPropAirboat::ApplyStressDamage( IPhysicsObject *pPhysics ) +{ + vphysics_objectstress_t stressOut; + float damage = CalculatePhysicsStressDamage( &stressOut, pPhysics ); + if ( ( damage > 0 ) && ( m_hPlayer != NULL ) ) + { + CTakeDamageInfo dmgInfo( GetWorldEntity(), GetWorldEntity(), vec3_origin, vec3_origin, damage, DMG_CRUSH ); + dmgInfo.SetDamageForce( Vector( 0, 0, -stressOut.receivedStress * GetCurrentGravity() * gpGlobals->frametime ) ); + dmgInfo.SetDamagePosition( GetAbsOrigin() ); + m_hPlayer->TakeDamage( dmgInfo ); + } +} + |