You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2101 lines
64 KiB
2101 lines
64 KiB
//========= 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 ); |
|
} |
|
} |
|
|
|
|