mirror of
https://github.com/Gigaslav/HL2Overcharged.git
synced 2026-09-15 20:12:37 +03:00
2363 lines
71 KiB
C++
2363 lines
71 KiB
C++
//========= 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"
|
|
#include "OverCharged/Entity_WeaponDetach.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
|
|
|
|
ConVar oc_airboat_checking_sphere_radius("oc_airboat_checking_sphere_radius", "23", FCVAR_ARCHIVE);
|
|
ConVar oc_airboat_has_gun("oc_airboat_has_gun", "0");
|
|
ConVar sk_airboat_max_ammo("sk_airboat_max_ammo", "100");
|
|
ConVar sk_airboat_recharge_rate("sk_airboat_recharge_rate", "15");
|
|
ConVar sk_airboat_drain_rate("sk_airboat_drain_rate", "10");
|
|
ConVar hud_airboathint_numentries("hud_airboathint_numentries", "10", FCVAR_NONE);
|
|
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;
|
|
|
|
// Tree Trigger
|
|
/*class CAirboatEntity : public CBaseEntity
|
|
{
|
|
DECLARE_CLASS(CAirboatEntity, CBaseEntity);
|
|
public:
|
|
//void Touch(CBaseEntity *pOther);
|
|
Vector Min;
|
|
Vector Max;
|
|
Vector Pos;
|
|
static CAirboatEntity *TriggerCreate(CBaseEntity *pOwner, const Vector &position);// , const Vector &min, const Vector &max);
|
|
};
|
|
LINK_ENTITY_TO_CLASS(airboat_entity, CAirboatEntity);
|
|
|
|
CAirboatEntity *CAirboatEntity::TriggerCreate(CBaseEntity *pOwner, const Vector &position)//, const Vector &min, const Vector &max)
|
|
{
|
|
CAirboatEntity *pTrigger = CREATE_ENTITY(CAirboatEntity, "airboat_entity");
|
|
pTrigger->SetLocalOrigin(position);
|
|
pTrigger->SetCollisionGroup(COLLISION_GROUP_PLAYER);
|
|
pTrigger->SetSolid(SOLID_BBOX);
|
|
pTrigger->AddSolidFlags(FSOLID_TRIGGER | FSOLID_NOT_SOLID);
|
|
pTrigger->SetMoveType(MOVETYPE_NONE);
|
|
pTrigger->SetOwnerEntity(pOwner);
|
|
|
|
UTIL_SetSize(pTrigger, Vector(-13, -13, -13), Vector(13, 13, 13));
|
|
NDebugOverlay::Box(position, Vector(-13, -13, -13), Vector(13, 13, 13), 0, 255, 0, true, 10.1f);
|
|
//pTrigger->Min = min;
|
|
//pTrigger->Max = max;
|
|
//pTrigger->Pos = position;
|
|
return pTrigger;
|
|
}*/
|
|
|
|
|
|
|
|
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);
|
|
int GetTracerAttachment(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 void Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value);
|
|
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);
|
|
|
|
CBaseCombatWeapon *pWeapon;
|
|
CBaseEntity *pDriver;
|
|
CBasePlayer *pPlayerDriver;
|
|
CBasePlayer *pPlayer;
|
|
/*CEntityWeaponDetach *pWDetachA = NULL;
|
|
~CPropAirboat()
|
|
{
|
|
if (pWDetachA)
|
|
{
|
|
delete pWDetachA;
|
|
pWDetachA = NULL;
|
|
}
|
|
};*/
|
|
private:
|
|
|
|
bool HasTakenAirboatGun;
|
|
|
|
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:
|
|
//CAirboatEntity *m_pTrigger;
|
|
|
|
|
|
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 foundedPlayer;
|
|
int count;
|
|
bool canTakeOffTheGun;*/
|
|
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;
|
|
|
|
bool HasGunS;
|
|
bool repeathasgun;
|
|
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(HasTakenAirboatGun, FIELD_BOOLEAN),
|
|
DEFINE_FIELD(repeathasgun, FIELD_BOOLEAN),
|
|
//DEFINE_FIELD(pWDetachA, FIELD_EHANDLE),
|
|
/*DEFINE_FIELD(foundedPlayer, FIELD_BOOLEAN),
|
|
DEFINE_FIELD(count, FIELD_INTEGER),
|
|
DEFINE_FIELD(canTakeOffTheGun, FIELD_BOOLEAN),*/
|
|
// 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");
|
|
|
|
PrecacheParticleSystem("abgun_normal2");
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CPropAirboat::Spawn(void)
|
|
{
|
|
if (oc_airboat_has_gun.GetInt())
|
|
{
|
|
inputdata_t inputdata;
|
|
inputdata.value.SetBool(true);
|
|
InputEnableGun(inputdata);
|
|
}
|
|
|
|
/*if (pWDetachA == NULL)
|
|
{
|
|
pWDetachA = new CEntityWeaponDetach(this, "gun", Vector(-13, -13, -13), Vector(13, 13, 13));
|
|
pWDetachA->SetOwnerEntity(this);
|
|
pWDetachA->FollowEntity(this);
|
|
|
|
}
|
|
else
|
|
{
|
|
delete pWDetachA;
|
|
pWDetachA = new CEntityWeaponDetach(this, "gun", Vector(-13, -13, -13), Vector(13, 13, 13));
|
|
pWDetachA->SetOwnerEntity(this);
|
|
pWDetachA->FollowEntity(this);
|
|
}*/
|
|
//if (pWDetachA == NULL)
|
|
//pWDetachA = new CEntityWeaponDetach(this, "gun", Vector(-13, -13, -13), Vector(13, 13, 13));
|
|
/*if (pWDetachA == NULL)
|
|
{
|
|
pWDetachA = pWDetachA->entCreate(this, GetAbsOrigin(), Vector(-13, -13, -13), Vector(13, 13, 13));// , Vector(-23, -23, -23), Vector(23, 23, 23));
|
|
pWDetachA->SetParent(this);
|
|
}*/
|
|
|
|
/*int attachment1 = LookupAttachment("gun");
|
|
Vector vecOrigin2;
|
|
GetAttachment(attachment1, vecOrigin2);
|
|
Vector triggerPosition, vForward, vRight, vUp;
|
|
|
|
AngleVectors(GetAbsAngles(), &vForward, &vRight, &vUp);
|
|
triggerPosition = GetAbsOrigin() + (vForward * 64) + (vUp * 64);
|
|
m_pTrigger = CAirboatEntity::TriggerCreate(this, triggerPosition);// , Vector(-23, -23, -23), Vector(23, 23, 23));
|
|
m_pTrigger->SetParent(this->GetDriver());
|
|
UTIL_SetSize(m_pTrigger, Vector(-13, -13, -13), Vector(13, 13, 13));*/
|
|
|
|
m_nAmmoCount = m_bHasGun ? 0 : -1;
|
|
m_hAvoidSphere = CreateHelicopterAvoidanceSphere(this, 0, 50.0f, false);
|
|
m_flLastImpactEffectTime = -1;
|
|
m_iNumberOfEntries = 0;
|
|
HasGunS = false;
|
|
repeathasgun = m_bHasGun;
|
|
// 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);
|
|
}
|
|
HasTakenAirboatGun = false;
|
|
|
|
if (m_bHasGun)
|
|
m_bAllowDetach = true;
|
|
|
|
/*foundedPlayer = false;
|
|
count = 0;
|
|
canTakeOffTheGun = false;*/
|
|
//DoOnceAirboat = true;
|
|
//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();
|
|
if (m_bHasGun)
|
|
m_bAllowDetach = true;
|
|
SetBodygroup(AIRBOAT_BODYGROUP_GUN, m_bHasGun);
|
|
repeathasgun = 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;
|
|
}
|
|
|
|
|
|
ConVar oc_test_sphere_x("oc_test_sphere_x", "0");
|
|
ConVar oc_test_sphere_y("oc_test_sphere_y", "0");
|
|
ConVar oc_test_sphere_z("oc_test_sphere_z", "0");
|
|
|
|
int count = 0;
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Manages animation and sound state.
|
|
//-----------------------------------------------------------------------------
|
|
void CPropAirboat::Think(void)
|
|
{
|
|
BaseClass::Think();
|
|
|
|
//if (m_bHasGun)
|
|
{
|
|
/*if (pWDetachA)
|
|
{
|
|
pWDetachA->SetOwnerEntity(this);
|
|
pWDetachA->LegacyThink(this, "weapon_airboatgun", "gun", "Press 'use' button to mount back the cannon", "Press 'use' button to tear off the cannon", oc_airboat_checking_sphere_radius.GetFloat(), HasTakenAirboatGun);
|
|
}
|
|
else
|
|
{
|
|
/*if (gEntList.FindEntityByClassname(this, "ent_weapon_detach") != NULL)
|
|
delete gEntList.FindEntityByClassname(this, "ent_weapon_detach");*/
|
|
//if (gEntList.FindEntityByClassname(this, "ent_weapon_detach") == NULL)
|
|
/*{
|
|
pWDetachA = new CEntityWeaponDetach(this, "gun", Vector(-13, -13, -13), Vector(13, 13, 13));
|
|
}
|
|
}*/
|
|
}
|
|
|
|
/*if (m_bAllowDetach)
|
|
CheckAttachedGun("gun", oc_airboat_checking_sphere_radius.GetFloat(), true);*/
|
|
|
|
//if (m_bAllowDetach && CheckAttachedGun("gun", oc_airboat_checking_sphere_radius.GetFloat(), true))
|
|
// GunText(oc_airboat_checking_sphere_radius.GetFloat(), m_bHasGun, "Press 'use' button to mount back the cannon", "Press 'use' button to pick up the cannon");
|
|
|
|
// BJ: Goddamn localisation strings
|
|
if (m_bAllowDetach && CheckAttachedGun("gun", oc_airboat_checking_sphere_radius.GetFloat(), true))
|
|
GunText(oc_airboat_checking_sphere_radius.GetFloat(), m_bHasGun, "#OVR_MountAirboatGun", "#OVR_DismountAirboatGun");
|
|
|
|
//Removing decals
|
|
if (GetWaterLevel() > 1)
|
|
GetBaseAnimating()->RemoveAllDecals();
|
|
|
|
// 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();
|
|
|
|
}
|
|
|
|
void CPropAirboat::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value)
|
|
{
|
|
CBasePlayer *pPlayer = ToBasePlayer(pActivator);
|
|
|
|
if (pPlayer == NULL)
|
|
return;
|
|
|
|
if (!CheckAttachedGun("gun", oc_airboat_checking_sphere_radius.GetFloat(), true))
|
|
return BaseClass::Use(pActivator, pCaller, useType, value);
|
|
else
|
|
m_bCanDetach = m_bAllowDetach;
|
|
|
|
if (m_bCanDetach)
|
|
{
|
|
|
|
if (pPlayer->GetActiveWeapon())
|
|
pPlayer->Weapon_SetLast(pPlayer->GetActiveWeapon());
|
|
|
|
int attachment = LookupAttachment("gun");
|
|
//int tau = LookupAttachment("taucannon");
|
|
if ((attachment) && repeathasgun)
|
|
{
|
|
Vector vecOrigin, vecOrigin2;// = this->WorldSpaceCenter();
|
|
QAngle angDir, angDir2;
|
|
|
|
GetAttachment(attachment, vecOrigin, angDir);
|
|
|
|
repeathasgun = true;
|
|
if (/*pWDetachA != NULL && pWDetachA->canTakeOffTheGun && */(!HasTakenAirboatGun) && !pPlayer->HasNamedPlayerItem("weapon_airboatgun"))
|
|
{
|
|
pWeapon = pPlayer->GetActiveWeapon();
|
|
HasTakenAirboatGun = true;
|
|
pPlayer->Weapon_SetLast(pWeapon);
|
|
pPlayer->GiveNamedItem("weapon_airboatgun");
|
|
|
|
SetBodygroup(1, false);
|
|
m_bHasGun = false;
|
|
|
|
EmitSound("Jeep.GaussDeCharge");
|
|
|
|
//Easy fix
|
|
GetBaseAnimating()->RemoveAllDecals();
|
|
|
|
//m_bGunHasBeenCutOff = true;
|
|
//HasGunS = true;
|
|
//pPlayer->Weapon_Switch((CBaseCombatWeapon*)pPlayer->GiveNamedItem("weapon_airboatgun"));
|
|
HasGunS = false;
|
|
return;
|
|
}
|
|
else if (/*((tr2.endpos.DistTo(vecStart) > 20) && (tr2.endpos.DistTo(vecStart) <= 58 ))*//*pWDetachA != NULL && pWDetachA->canTakeOffTheGun && */(HasTakenAirboatGun) && (FClassnameIs(pPlayer->GetActiveWeapon(), "weapon_airboatgun")))
|
|
{
|
|
if (cvar->FindVar("oc_weapon_holster")->GetInt())
|
|
{
|
|
cvar->FindVar("oc_weapon_holster")->SetValue(0);
|
|
HasGunS = true;
|
|
}
|
|
|
|
HasTakenAirboatGun = false;
|
|
|
|
//pPlayer->SwitchToNextBestWeapon(pPlayer->GetActiveWeapon());
|
|
|
|
UTIL_Remove(pPlayer->GetActiveWeapon());
|
|
//pPlayer->Weapon_SlotOccupied(pPlayer->GetActiveWeapon());
|
|
//pPlayer->Weapon_GetLast();
|
|
|
|
SetBodygroup(1, true);
|
|
pPlayer->SwitchToNextBestWeapon(pPlayer->Weapon_GetLast());
|
|
//if (FClassnameIs(pPlayer->Weapon_GetLast(), "weapon_airboatgun"))
|
|
//pPlayer->SwitchToNextBestWeapon(pPlayer->Weapon_GetLast());
|
|
/*else
|
|
pPlayer->Weapon_Switch(pPlayer->Weapon_GetLast());*/
|
|
|
|
//(pPlayer->Weapon_GetSlot(0));//random->RandomInt(1, 3)));
|
|
|
|
|
|
m_bHasGun = true;
|
|
g_pEffects->Sparks(vecOrigin);
|
|
EmitSound("Jeep.GaussReCharge");
|
|
//m_bGunHasBeenCutOff = true;
|
|
|
|
if (HasGunS)
|
|
cvar->FindVar("oc_weapon_holster")->SetValue(1);
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Fall back and get in the vehicle instead, skip giving ammo
|
|
return BaseClass::Use(pActivator, pCaller, useType, value);
|
|
}
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
// 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";
|
|
}
|
|
|
|
int CPropAirboat::GetTracerAttachment(void)
|
|
{
|
|
int iAttachment = LookupAttachment("muzzle");
|
|
if (!iAttachment)
|
|
iAttachment = 1;
|
|
return iAttachment;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
// 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();
|
|
|
|
if (!cvar->FindVar("oc_particle_muzzleflashes")->GetInt())
|
|
{
|
|
CEffectData data;
|
|
data.m_nAttachmentIndex = 1;
|
|
data.m_nEntIndex = this->entindex();
|
|
DispatchEffect("AirboatMuzzleFlash", data);
|
|
}
|
|
else
|
|
{
|
|
CEffectData data;
|
|
data.m_nAttachmentIndex = 1;
|
|
data.m_nEntIndex = this->entindex();
|
|
DispatchEffect("AirboatGunMuzzleFlashParticle", data);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
/*float Radius;// = 110;
|
|
Radius = random->RandomInt(150, 250);
|
|
CPASFilter filter(vecGunPosition);
|
|
te->DynamicLight(filter, 0.0, &vecGunPosition, 0, random->RandomInt(95, 125), random->RandomInt(155, 195), 3, Radius, random->RandomInt(0.018, 0.23), Radius / 0.5);//OverCharged
|
|
DispatchParticleEffect("abgun_normal2", PATTACH_POINT_FOLLOW, this, "muzzle", false);*/
|
|
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);
|
|
}
|
|
}
|
|
|