aboutsummaryrefslogtreecommitdiff
path: root/sp/src/game/server/hl2/vehicle_airboat.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /sp/src/game/server/hl2/vehicle_airboat.cpp
parentMark some more files as text. (diff)
downloadsource-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.cpp4202
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 );
+ }
+}
+