mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-17 14:07:48 +00:00
1
This commit is contained in:
@@ -0,0 +1,931 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Client side implementation of the airboat.
|
||||
//
|
||||
// - Dampens motion of driver's view to reduce nausea.
|
||||
// - Autocenters driver's view after a period of inactivity.
|
||||
// - Controls headlights.
|
||||
// - Controls curve parameters for pitch/roll blending.
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "c_prop_vehicle.h"
|
||||
#include "datacache/imdlcache.h"
|
||||
#include "flashlighteffect.h"
|
||||
#include "movevars_shared.h"
|
||||
#include "ammodef.h"
|
||||
#include "SpriteTrail.h"
|
||||
#include "beamdraw.h"
|
||||
#include "enginesprite.h"
|
||||
#include "fx_quad.h"
|
||||
#include "fx.h"
|
||||
#include "fx_water.h"
|
||||
#include "engine/ivdebugoverlay.h"
|
||||
#include "view.h"
|
||||
#include "clienteffectprecachesystem.h"
|
||||
#include "c_basehlplayer.h"
|
||||
#include "vgui_controls/Controls.h"
|
||||
#include "vgui/ISurface.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
ConVar r_AirboatViewBlendTo( "r_AirboatViewBlendTo", "1", FCVAR_CHEAT );
|
||||
ConVar r_AirboatViewBlendToScale( "r_AirboatViewBlendToScale", "0.03", FCVAR_CHEAT );
|
||||
ConVar r_AirboatViewBlendToTime( "r_AirboatViewBlendToTime", "1.5", FCVAR_CHEAT );
|
||||
|
||||
ConVar cl_draw_airboat_wake( "cl_draw_airboat_wake", "1", FCVAR_CHEAT );
|
||||
|
||||
// Curve parameters for pitch/roll blending.
|
||||
// NOTE: Must restart (or create a new airboat) after changing these cvars!
|
||||
ConVar r_AirboatRollCurveZero( "r_AirboatRollCurveZero", "90.0", FCVAR_CHEAT ); // Roll less than this is clamped to zero.
|
||||
ConVar r_AirboatRollCurveLinear( "r_AirboatRollCurveLinear", "120.0", FCVAR_CHEAT ); // Roll greater than this is mapped directly.
|
||||
// Spline in between.
|
||||
|
||||
ConVar r_AirboatPitchCurveZero( "r_AirboatPitchCurveZero", "25.0", FCVAR_CHEAT ); // Pitch less than this is clamped to zero.
|
||||
ConVar r_AirboatPitchCurveLinear( "r_AirboatPitchCurveLinear", "60.0", FCVAR_CHEAT ); // Pitch greater than this is mapped directly.
|
||||
// Spline in between.
|
||||
|
||||
ConVar airboat_joy_response_move( "airboat_joy_response_move", "1" ); // Quadratic steering response
|
||||
|
||||
|
||||
#define AIRBOAT_DELTA_LENGTH_MAX 12.0f // 1 foot
|
||||
#define AIRBOAT_FRAMETIME_MIN 1e-6
|
||||
|
||||
#define HEADLIGHT_DISTANCE 1000
|
||||
|
||||
#define MAX_WAKE_POINTS 16
|
||||
#define WAKE_POINT_MASK (MAX_WAKE_POINTS-1)
|
||||
|
||||
#define WAKE_LIFETIME 0.5f
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Client-side Airboat Class
|
||||
//
|
||||
class C_PropAirboat : public C_PropVehicleDriveable
|
||||
{
|
||||
DECLARE_CLASS( C_PropAirboat, C_PropVehicleDriveable );
|
||||
|
||||
public:
|
||||
|
||||
DECLARE_CLIENTCLASS();
|
||||
DECLARE_INTERPOLATION();
|
||||
DECLARE_DATADESC();
|
||||
|
||||
C_PropAirboat();
|
||||
~C_PropAirboat();
|
||||
|
||||
public:
|
||||
|
||||
// C_BaseEntity
|
||||
virtual void Simulate();
|
||||
|
||||
// IClientVehicle
|
||||
virtual void UpdateViewAngles( C_BasePlayer *pLocalPlayer, CUserCmd *pCmd );
|
||||
virtual void OnEnteredVehicle( C_BasePlayer *pPlayer );
|
||||
virtual int GetPrimaryAmmoType() const;
|
||||
virtual int GetPrimaryAmmoClip() const;
|
||||
virtual bool PrimaryAmmoUsesClips() const;
|
||||
virtual int GetPrimaryAmmoCount() const;
|
||||
virtual int GetJoystickResponseCurve() const;
|
||||
|
||||
int DrawModel( int flags );
|
||||
|
||||
// Draws crosshair in the forward direction of the boat
|
||||
void DrawHudElements( );
|
||||
|
||||
private:
|
||||
|
||||
void DrawPropWake( Vector origin, float speed );
|
||||
void DrawPontoonSplash( Vector position, Vector direction, float speed );
|
||||
void DrawPontoonWake( Vector startPos, Vector wakeDir, float wakeLength, float speed);
|
||||
|
||||
void DampenEyePosition( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles );
|
||||
void DampenForwardMotion( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles, float flFrameTime );
|
||||
void DampenUpMotion( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles, float flFrameTime );
|
||||
void ComputePDControllerCoefficients( float *pCoefficientsOut, float flFrequency, float flDampening, float flDeltaTime );
|
||||
|
||||
void UpdateHeadlight( void );
|
||||
void UpdateWake( void );
|
||||
int DrawWake( void );
|
||||
void DrawSegment( const BeamSeg_t &beamSeg, const Vector &vNormal );
|
||||
|
||||
TrailPoint_t *GetTrailPoint( int n )
|
||||
{
|
||||
int nIndex = (n + m_nFirstStep) & WAKE_POINT_MASK;
|
||||
return &m_vecSteps[nIndex];
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Vector m_vecLastEyePos;
|
||||
Vector m_vecLastEyeTarget;
|
||||
Vector m_vecEyeSpeed;
|
||||
Vector m_vecTargetSpeed;
|
||||
|
||||
float m_flViewAngleDeltaTime;
|
||||
|
||||
bool m_bHeadlightIsOn;
|
||||
int m_nAmmoCount;
|
||||
CHeadlightEffect *m_pHeadlight;
|
||||
|
||||
int m_nExactWaterLevel;
|
||||
|
||||
TrailPoint_t m_vecSteps[MAX_WAKE_POINTS];
|
||||
int m_nFirstStep;
|
||||
int m_nStepCount;
|
||||
float m_flUpdateTime;
|
||||
|
||||
TimedEvent m_SplashTime;
|
||||
CMeshBuilder m_Mesh;
|
||||
|
||||
Vector m_vecPhysVelocity;
|
||||
};
|
||||
|
||||
IMPLEMENT_CLIENTCLASS_DT( C_PropAirboat, DT_PropAirboat, CPropAirboat )
|
||||
RecvPropBool( RECVINFO( m_bHeadlightIsOn ) ),
|
||||
RecvPropInt( RECVINFO( m_nAmmoCount ) ),
|
||||
RecvPropInt( RECVINFO( m_nExactWaterLevel ) ),
|
||||
RecvPropInt( RECVINFO( m_nWaterLevel ) ),
|
||||
RecvPropVector( RECVINFO( m_vecPhysVelocity ) ),
|
||||
END_RECV_TABLE()
|
||||
|
||||
|
||||
BEGIN_DATADESC( C_PropAirboat )
|
||||
DEFINE_FIELD( m_vecLastEyePos, FIELD_POSITION_VECTOR ),
|
||||
DEFINE_FIELD( m_vecLastEyeTarget, FIELD_POSITION_VECTOR ),
|
||||
DEFINE_FIELD( m_vecEyeSpeed, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( m_vecTargetSpeed, FIELD_VECTOR ),
|
||||
//DEFINE_FIELD( m_flViewAngleDeltaTime, FIELD_FLOAT ),
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Constructor
|
||||
//-----------------------------------------------------------------------------
|
||||
C_PropAirboat::C_PropAirboat()
|
||||
{
|
||||
m_vecEyeSpeed.Init();
|
||||
m_flViewAngleDeltaTime = 0.0f;
|
||||
m_pHeadlight = NULL;
|
||||
|
||||
m_ViewSmoothingData.flPitchCurveZero = r_AirboatPitchCurveZero.GetFloat();
|
||||
m_ViewSmoothingData.flPitchCurveLinear = r_AirboatPitchCurveLinear.GetFloat();
|
||||
m_ViewSmoothingData.flRollCurveZero = r_AirboatRollCurveZero.GetFloat();
|
||||
m_ViewSmoothingData.flRollCurveLinear = r_AirboatRollCurveLinear.GetFloat();
|
||||
|
||||
m_ViewSmoothingData.rollLockData.flLockInterval = 1.5;
|
||||
m_ViewSmoothingData.rollLockData.flUnlockBlendInterval = 1.0;
|
||||
|
||||
m_ViewSmoothingData.pitchLockData.flLockInterval = 1.5;
|
||||
m_ViewSmoothingData.pitchLockData.flUnlockBlendInterval = 1.0;
|
||||
|
||||
m_nFirstStep = 0;
|
||||
m_nStepCount = 0;
|
||||
m_SplashTime.Init( 60 );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Deconstructor
|
||||
//-----------------------------------------------------------------------------
|
||||
C_PropAirboat::~C_PropAirboat()
|
||||
{
|
||||
if (m_pHeadlight)
|
||||
{
|
||||
delete m_pHeadlight;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Draws the ammo for the airboat
|
||||
//-----------------------------------------------------------------------------
|
||||
int C_PropAirboat::GetPrimaryAmmoType() const
|
||||
{
|
||||
if ( m_nAmmoCount < 0 )
|
||||
return -1;
|
||||
|
||||
int nAmmoType = GetAmmoDef()->Index( "AirboatGun" );
|
||||
return nAmmoType;
|
||||
}
|
||||
|
||||
int C_PropAirboat::GetPrimaryAmmoCount() const
|
||||
{
|
||||
return m_nAmmoCount;
|
||||
}
|
||||
|
||||
bool C_PropAirboat::PrimaryAmmoUsesClips() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int C_PropAirboat::GetPrimaryAmmoClip() const
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// The airboat prefers a more peppy response curve for joystick control.
|
||||
//-----------------------------------------------------------------------------
|
||||
int C_PropAirboat::GetJoystickResponseCurve() const
|
||||
{
|
||||
return airboat_joy_response_move.GetInt();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Draws crosshair in the forward direction of the boat
|
||||
//-----------------------------------------------------------------------------
|
||||
void C_PropAirboat::DrawHudElements( )
|
||||
{
|
||||
BaseClass::DrawHudElements();
|
||||
|
||||
MDLCACHE_CRITICAL_SECTION();
|
||||
|
||||
CHudTexture *pIcon = gHUD.GetIcon( IsX360() ? "crosshair_default" : "plushair" );
|
||||
if ( pIcon != NULL )
|
||||
{
|
||||
float x, y;
|
||||
Vector screen;
|
||||
|
||||
int vx, vy, vw, vh;
|
||||
vgui::surface()->GetFullscreenViewport( vx, vy, vw, vh );
|
||||
float screenWidth = vw;
|
||||
float screenHeight = vh;
|
||||
|
||||
x = screenWidth/2;
|
||||
y = screenHeight/2;
|
||||
|
||||
int eyeAttachmentIndex = LookupAttachment( "vehicle_driver_eyes" );
|
||||
Vector vehicleEyeOrigin;
|
||||
QAngle vehicleEyeAngles;
|
||||
GetAttachment( eyeAttachmentIndex, vehicleEyeOrigin, vehicleEyeAngles );
|
||||
|
||||
// Only worry about yaw.
|
||||
vehicleEyeAngles.x = vehicleEyeAngles.z = 0.0f;
|
||||
|
||||
Vector vecForward;
|
||||
AngleVectors( vehicleEyeAngles, &vecForward );
|
||||
VectorMA( vehicleEyeOrigin, 100.0f, vecForward, vehicleEyeOrigin );
|
||||
|
||||
ScreenTransform( vehicleEyeOrigin, screen );
|
||||
x += 0.5 * screen[0] * screenWidth + 0.5;
|
||||
y -= 0.5 * screen[1] * screenHeight + 0.5;
|
||||
|
||||
x -= pIcon->Width() / 2;
|
||||
y -= pIcon->Height() / 2;
|
||||
|
||||
pIcon->DrawSelf( x, y, gHUD.m_clrNormal );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Blend view angles.
|
||||
//-----------------------------------------------------------------------------
|
||||
void C_PropAirboat::UpdateViewAngles( C_BasePlayer *pLocalPlayer, CUserCmd *pCmd )
|
||||
{
|
||||
if ( r_AirboatViewBlendTo.GetInt() )
|
||||
{
|
||||
//
|
||||
// Autocenter the view after a period of no mouse movement while throttling.
|
||||
//
|
||||
bool bResetViewAngleTime = false;
|
||||
|
||||
if ( ( pCmd->mousedx != 0 || pCmd->mousedy != 0 ) || ( fabsf( m_flThrottle ) < 0.01f ) )
|
||||
{
|
||||
if ( IsX360() )
|
||||
{
|
||||
// Only reset this if there isn't an autoaim target!
|
||||
C_BaseHLPlayer *pLocalHLPlayer = (C_BaseHLPlayer *)pLocalPlayer;
|
||||
if ( pLocalHLPlayer )
|
||||
{
|
||||
// Get the autoaim target.
|
||||
CBaseEntity *pTarget = pLocalHLPlayer->m_HL2Local.m_hAutoAimTarget.Get();
|
||||
|
||||
if( !pTarget )
|
||||
{
|
||||
bResetViewAngleTime = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bResetViewAngleTime = true;
|
||||
}
|
||||
}
|
||||
|
||||
if( bResetViewAngleTime )
|
||||
{
|
||||
m_flViewAngleDeltaTime = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flViewAngleDeltaTime += gpGlobals->frametime;
|
||||
}
|
||||
|
||||
if ( m_flViewAngleDeltaTime > r_AirboatViewBlendToTime.GetFloat() )
|
||||
{
|
||||
// Blend the view angles.
|
||||
int eyeAttachmentIndex = LookupAttachment( "vehicle_driver_eyes" );
|
||||
Vector vehicleEyeOrigin;
|
||||
QAngle vehicleEyeAngles;
|
||||
GetAttachmentLocal( eyeAttachmentIndex, vehicleEyeOrigin, vehicleEyeAngles );
|
||||
|
||||
QAngle outAngles;
|
||||
InterpolateAngles( pCmd->viewangles, vehicleEyeAngles, outAngles, r_AirboatViewBlendToScale.GetFloat() );
|
||||
pCmd->viewangles = outAngles;
|
||||
}
|
||||
}
|
||||
|
||||
BaseClass::UpdateViewAngles( pLocalPlayer, pCmd );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void C_PropAirboat::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 C_PropAirboat::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 C_PropAirboat::DampenForwardMotion( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles, float flFrameTime )
|
||||
{
|
||||
// vecVehicleEyePos = real eye position this frame
|
||||
|
||||
// m_vecLastEyePos = eye position last frame
|
||||
// m_vecEyeSpeed = eye speed last frame
|
||||
// vecPredEyePos = predicted eye position this frame (assuming no acceleration - it will get that from the pd controller).
|
||||
// vecPredEyeSpeed = predicted eye speed
|
||||
Vector vecPredEyePos = m_vecLastEyePos + m_vecEyeSpeed * flFrameTime;
|
||||
Vector vecPredEyeSpeed = m_vecEyeSpeed;
|
||||
|
||||
// m_vecLastEyeTarget = real eye position last frame (used for speed calculation).
|
||||
// Calculate the approximate speed based on the current vehicle eye position and the eye position last frame.
|
||||
Vector vecVehicleEyeSpeed = ( vecVehicleEyePos - m_vecLastEyeTarget ) / flFrameTime;
|
||||
m_vecLastEyeTarget = vecVehicleEyePos;
|
||||
if (vecVehicleEyeSpeed.Length() == 0.0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate the delta between the predicted eye position and speed and the current eye position and speed.
|
||||
Vector vecDeltaSpeed = vecVehicleEyeSpeed - vecPredEyeSpeed;
|
||||
Vector vecDeltaPos = vecVehicleEyePos - vecPredEyePos;
|
||||
|
||||
// Forward vector.
|
||||
Vector vecForward;
|
||||
AngleVectors( vecVehicleEyeAngles, &vecForward );
|
||||
|
||||
float flDeltaLength = vecDeltaPos.Length();
|
||||
if ( flDeltaLength > AIRBOAT_DELTA_LENGTH_MAX )
|
||||
{
|
||||
// Clamp.
|
||||
float flDelta = flDeltaLength - AIRBOAT_DELTA_LENGTH_MAX;
|
||||
if ( flDelta > 40.0f )
|
||||
{
|
||||
// This part is a bit of a hack to get rid of large deltas (at level load, etc.).
|
||||
m_vecLastEyePos = vecVehicleEyePos;
|
||||
m_vecEyeSpeed = vecVehicleEyeSpeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Position clamp.
|
||||
float flRatio = AIRBOAT_DELTA_LENGTH_MAX / flDeltaLength;
|
||||
vecDeltaPos *= flRatio;
|
||||
Vector vecForwardOffset = vecForward * ( vecForward.Dot( vecDeltaPos ) );
|
||||
vecVehicleEyePos -= vecForwardOffset;
|
||||
m_vecLastEyePos = vecVehicleEyePos;
|
||||
|
||||
// Speed clamp.
|
||||
vecDeltaSpeed *= flRatio;
|
||||
float flCoefficients[2];
|
||||
ComputePDControllerCoefficients( flCoefficients, r_AirboatViewDampenFreq.GetFloat(), r_AirboatViewDampenDamp.GetFloat(), flFrameTime );
|
||||
m_vecEyeSpeed += ( ( flCoefficients[0] * vecDeltaPos + flCoefficients[1] * vecDeltaSpeed ) * flFrameTime );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Generate an updated (dampening) speed for use in next frames position prediction.
|
||||
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 = vecPredEyePos;
|
||||
|
||||
// Move eye forward/backward.
|
||||
Vector vecForwardOffset = vecForward * ( vecForward.Dot( vecDeltaPos ) );
|
||||
vecVehicleEyePos -= vecForwardOffset;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void C_PropAirboat::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 C_PropAirboat::OnEnteredVehicle( C_BasePlayer *pPlayer )
|
||||
{
|
||||
int eyeAttachmentIndex = LookupAttachment( "vehicle_driver_eyes" );
|
||||
Vector vehicleEyeOrigin;
|
||||
QAngle vehicleEyeAngles;
|
||||
GetAttachment( eyeAttachmentIndex, vehicleEyeOrigin, vehicleEyeAngles );
|
||||
|
||||
m_vecLastEyeTarget = vehicleEyeOrigin;
|
||||
m_vecLastEyePos = vehicleEyeOrigin;
|
||||
m_vecEyeSpeed = vec3_origin;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void C_PropAirboat::Simulate()
|
||||
{
|
||||
UpdateHeadlight();
|
||||
UpdateWake();
|
||||
|
||||
BaseClass::Simulate();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Creates, destroys, and updates the headlight effect as needed.
|
||||
//-----------------------------------------------------------------------------
|
||||
void C_PropAirboat::UpdateHeadlight()
|
||||
{
|
||||
if (m_bHeadlightIsOn)
|
||||
{
|
||||
if (!m_pHeadlight)
|
||||
{
|
||||
// Turned on the headlight; create it.
|
||||
m_pHeadlight = new CHeadlightEffect();
|
||||
|
||||
if (!m_pHeadlight)
|
||||
return;
|
||||
|
||||
m_pHeadlight->TurnOn();
|
||||
}
|
||||
|
||||
// The headlight is emitted from an attachment point so that it can move
|
||||
// as we turn the handlebars.
|
||||
int nHeadlightIndex = LookupAttachment( "vehicle_headlight" );
|
||||
|
||||
Vector vecLightPos;
|
||||
QAngle angLightDir;
|
||||
GetAttachment(nHeadlightIndex, vecLightPos, angLightDir);
|
||||
|
||||
Vector vecLightDir, vecLightRight, vecLightUp;
|
||||
AngleVectors( angLightDir, &vecLightDir, &vecLightRight, &vecLightUp );
|
||||
|
||||
// Update the light with the new position and direction.
|
||||
m_pHeadlight->UpdateLight( vecLightPos, vecLightDir, vecLightRight, vecLightUp, HEADLIGHT_DISTANCE );
|
||||
}
|
||||
else if (m_pHeadlight)
|
||||
{
|
||||
// Turned off the headlight; delete it.
|
||||
delete m_pHeadlight;
|
||||
m_pHeadlight = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void C_PropAirboat::UpdateWake( void )
|
||||
{
|
||||
if ( gpGlobals->frametime <= 0.0f )
|
||||
return;
|
||||
|
||||
// Can't update too quickly
|
||||
if ( m_flUpdateTime > gpGlobals->curtime )
|
||||
return;
|
||||
|
||||
Vector screenPos = GetRenderOrigin();
|
||||
screenPos.z = m_nExactWaterLevel;
|
||||
|
||||
TrailPoint_t *pLast = m_nStepCount ? GetTrailPoint( m_nStepCount-1 ) : NULL;
|
||||
if ( ( pLast == NULL ) || ( pLast->m_vecScreenPos.DistToSqr( screenPos ) > 4.0f ) )
|
||||
{
|
||||
// If we're over our limit, steal the last point and put it up front
|
||||
if ( m_nStepCount >= MAX_WAKE_POINTS )
|
||||
{
|
||||
--m_nStepCount;
|
||||
++m_nFirstStep;
|
||||
}
|
||||
|
||||
// Save off its screen position, not its world position
|
||||
TrailPoint_t *pNewPoint = GetTrailPoint( m_nStepCount );
|
||||
pNewPoint->m_vecScreenPos = screenPos + Vector( 0, 0, 2 );
|
||||
pNewPoint->m_flDieTime = gpGlobals->curtime + WAKE_LIFETIME;
|
||||
pNewPoint->m_flWidthVariance = random->RandomFloat( -16, 16 );
|
||||
|
||||
if ( pLast )
|
||||
{
|
||||
pNewPoint->m_flTexCoord = pLast->m_flTexCoord + pLast->m_vecScreenPos.DistTo( screenPos );
|
||||
pNewPoint->m_flTexCoord = fmod( pNewPoint->m_flTexCoord, 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
pNewPoint->m_flTexCoord = 0.0f;
|
||||
}
|
||||
|
||||
++m_nStepCount;
|
||||
}
|
||||
|
||||
// Don't update again for a bit
|
||||
m_flUpdateTime = gpGlobals->curtime + ( 0.5f / (float) MAX_WAKE_POINTS );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &beamSeg -
|
||||
//-----------------------------------------------------------------------------
|
||||
void C_PropAirboat::DrawSegment( const BeamSeg_t &beamSeg, const Vector &vNormal )
|
||||
{
|
||||
// Build the endpoints.
|
||||
Vector vPoint1, vPoint2;
|
||||
VectorMA( beamSeg.m_vPos, beamSeg.m_flWidth*0.5f, vNormal, vPoint1 );
|
||||
VectorMA( beamSeg.m_vPos, -beamSeg.m_flWidth*0.5f, vNormal, vPoint2 );
|
||||
|
||||
// Specify the points.
|
||||
m_Mesh.Position3fv( vPoint1.Base() );
|
||||
m_Mesh.Color4f( VectorExpand( beamSeg.m_vColor ), beamSeg.m_flAlpha );
|
||||
m_Mesh.TexCoord2f( 0, 0, beamSeg.m_flTexCoord );
|
||||
m_Mesh.TexCoord2f( 1, 0, beamSeg.m_flTexCoord );
|
||||
m_Mesh.AdvanceVertex();
|
||||
|
||||
m_Mesh.Position3fv( vPoint2.Base() );
|
||||
m_Mesh.Color4f( VectorExpand( beamSeg.m_vColor ), beamSeg.m_flAlpha );
|
||||
m_Mesh.TexCoord2f( 0, 1, beamSeg.m_flTexCoord );
|
||||
m_Mesh.TexCoord2f( 1, 1, beamSeg.m_flTexCoord );
|
||||
m_Mesh.AdvanceVertex();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : position -
|
||||
//-----------------------------------------------------------------------------
|
||||
void C_PropAirboat::DrawPontoonSplash( Vector origin, Vector direction, float speed )
|
||||
{
|
||||
Vector offset;
|
||||
|
||||
CSmartPtr<CSplashParticle> pSimple = CSplashParticle::Create( "splish" );
|
||||
pSimple->SetSortOrigin( origin );
|
||||
|
||||
SimpleParticle *pParticle;
|
||||
|
||||
Vector color = Vector( 0.8f, 0.8f, 0.75f );
|
||||
float colorRamp;
|
||||
|
||||
float flScale = RemapVal( speed, 64, 256, 0.75f, 1.0f );
|
||||
|
||||
PMaterialHandle hMaterial;
|
||||
|
||||
float tempDelta = gpGlobals->frametime;
|
||||
|
||||
while( m_SplashTime.NextEvent( tempDelta ) )
|
||||
{
|
||||
if ( random->RandomInt( 0, 1 ) )
|
||||
{
|
||||
hMaterial = ParticleMgr()->GetPMaterial( "effects/splash1" );
|
||||
}
|
||||
else
|
||||
{
|
||||
hMaterial = ParticleMgr()->GetPMaterial( "effects/splash2" );
|
||||
}
|
||||
|
||||
offset = RandomVector( -8.0f * flScale, 8.0f * flScale );
|
||||
offset[2] = 0.0f;
|
||||
offset += origin;
|
||||
|
||||
pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), hMaterial, offset );
|
||||
|
||||
if ( pParticle == NULL )
|
||||
continue;
|
||||
|
||||
pParticle->m_flLifetime = 0.0f;
|
||||
pParticle->m_flDieTime = 0.25f;
|
||||
|
||||
pParticle->m_vecVelocity.Random( -0.4f, 0.4f );
|
||||
pParticle->m_vecVelocity += (direction*5.0f+Vector(0,0,1));
|
||||
|
||||
VectorNormalize( pParticle->m_vecVelocity );
|
||||
|
||||
pParticle->m_vecVelocity *= speed + random->RandomFloat( -128.0f, 128.0f );
|
||||
|
||||
colorRamp = random->RandomFloat( 0.75f, 1.25f );
|
||||
|
||||
pParticle->m_uchColor[0] = MIN( 1.0f, color[0] * colorRamp ) * 255.0f;
|
||||
pParticle->m_uchColor[1] = MIN( 1.0f, color[1] * colorRamp ) * 255.0f;
|
||||
pParticle->m_uchColor[2] = MIN( 1.0f, color[2] * colorRamp ) * 255.0f;
|
||||
|
||||
pParticle->m_uchStartSize = random->RandomFloat( 8, 16 ) * flScale;
|
||||
pParticle->m_uchEndSize = pParticle->m_uchStartSize * 2;
|
||||
|
||||
pParticle->m_uchStartAlpha = 255;
|
||||
pParticle->m_uchEndAlpha = 0;
|
||||
|
||||
pParticle->m_flRoll = random->RandomInt( 0, 360 );
|
||||
pParticle->m_flRollDelta = random->RandomFloat( -4.0f, 4.0f );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : Vector startPos -
|
||||
// wakeDir -
|
||||
// wakeLength -
|
||||
//-----------------------------------------------------------------------------
|
||||
void C_PropAirboat::DrawPontoonWake( Vector startPos, Vector wakeDir, float wakeLength, float speed )
|
||||
{
|
||||
#define WAKE_STEPS 6
|
||||
|
||||
Vector wakeStep = wakeDir * ( wakeLength / (float) WAKE_STEPS );
|
||||
Vector origin;
|
||||
float scale;
|
||||
|
||||
IMaterial *pMaterial = materials->FindMaterial( "effects/splashwake1", NULL, false );
|
||||
CMatRenderContextPtr pRenderContext( materials );
|
||||
IMesh* pMesh = pRenderContext->GetDynamicMesh( 0, 0, 0, pMaterial );
|
||||
|
||||
CMeshBuilder meshBuilder;
|
||||
meshBuilder.Begin( pMesh, MATERIAL_QUADS, WAKE_STEPS );
|
||||
|
||||
for ( int i = 0; i < WAKE_STEPS; i++ )
|
||||
{
|
||||
origin = startPos + ( wakeStep * i );
|
||||
origin[0] += random->RandomFloat( -4.0f, 4.0f );
|
||||
origin[1] += random->RandomFloat( -4.0f, 4.0f );
|
||||
origin[2] = m_nExactWaterLevel + 2.0f;
|
||||
|
||||
float scaleRange = RemapVal( i, 0, WAKE_STEPS-1, 32, 64 );
|
||||
scale = scaleRange + ( 8.0f * sin( gpGlobals->curtime * 5 * i ) );
|
||||
|
||||
float alpha = RemapValClamped( speed, 128, 600, 0.05f, 0.25f );
|
||||
float color[4] = { 1.0f, 1.0f, 1.0f, alpha };
|
||||
|
||||
// Needs to be time based so it'll freeze when the game is frozen
|
||||
float yaw = random->RandomFloat( 0, 360 );
|
||||
|
||||
Vector rRight = ( Vector(1,0,0) * cos( DEG2RAD( yaw ) ) ) - ( Vector(0,1,0) * sin( DEG2RAD( yaw ) ) );
|
||||
Vector rUp = ( Vector(1,0,0) * cos( DEG2RAD( yaw+90.0f ) ) ) - ( Vector(0,1,0) * sin( DEG2RAD( yaw+90.0f ) ) );
|
||||
|
||||
Vector point;
|
||||
meshBuilder.Color4fv (color);
|
||||
meshBuilder.TexCoord2f (0, 0, 1);
|
||||
VectorMA (origin, -scale, rRight, point);
|
||||
VectorMA (point, -scale, rUp, point);
|
||||
meshBuilder.Position3fv (point.Base());
|
||||
meshBuilder.AdvanceVertex();
|
||||
|
||||
meshBuilder.Color4fv (color);
|
||||
meshBuilder.TexCoord2f (0, 0, 0);
|
||||
VectorMA (origin, scale, rRight, point);
|
||||
VectorMA (point, -scale, rUp, point);
|
||||
meshBuilder.Position3fv (point.Base());
|
||||
meshBuilder.AdvanceVertex();
|
||||
|
||||
meshBuilder.Color4fv (color);
|
||||
meshBuilder.TexCoord2f (0, 1, 0);
|
||||
VectorMA (origin, scale, rRight, point);
|
||||
VectorMA (point, scale, rUp, point);
|
||||
meshBuilder.Position3fv (point.Base());
|
||||
meshBuilder.AdvanceVertex();
|
||||
|
||||
meshBuilder.Color4fv (color);
|
||||
meshBuilder.TexCoord2f (0, 1, 1);
|
||||
VectorMA (origin, -scale, rRight, point);
|
||||
VectorMA (point, scale, rUp, point);
|
||||
meshBuilder.Position3fv (point.Base());
|
||||
meshBuilder.AdvanceVertex();
|
||||
}
|
||||
|
||||
meshBuilder.End();
|
||||
pMesh->Draw();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Output : int
|
||||
//-----------------------------------------------------------------------------
|
||||
int C_PropAirboat::DrawWake( void )
|
||||
{
|
||||
if ( cl_draw_airboat_wake.GetBool() == false )
|
||||
return 0;
|
||||
|
||||
// Make sure we're in water...
|
||||
if ( GetWaterLevel() == 0 )
|
||||
return 0;
|
||||
|
||||
//FIXME: For now, we don't draw slime this way
|
||||
if ( GetWaterLevel() == 2 )
|
||||
return 0;
|
||||
|
||||
bool bDriven = ( GetPassenger( VEHICLE_ROLE_DRIVER ) != NULL );
|
||||
|
||||
Vector vehicleDir = m_vecPhysVelocity;
|
||||
float vehicleSpeed = VectorNormalize( vehicleDir );
|
||||
|
||||
Vector vecPontoonFrontLeft;
|
||||
Vector vecPontoonFrontRight;
|
||||
Vector vecPontoonRearLeft;
|
||||
Vector vecPontoonRearRight;
|
||||
Vector vecSplashPoint;
|
||||
|
||||
QAngle fooAngles;
|
||||
|
||||
//FIXME: This lookup should be cached off
|
||||
// Get all attachments
|
||||
GetAttachment( LookupAttachment( "raytrace_fl" ), vecPontoonFrontLeft, fooAngles );
|
||||
GetAttachment( LookupAttachment( "raytrace_fr" ), vecPontoonFrontRight, fooAngles );
|
||||
GetAttachment( LookupAttachment( "raytrace_rl" ), vecPontoonRearLeft, fooAngles );
|
||||
GetAttachment( LookupAttachment( "raytrace_rr" ), vecPontoonRearRight, fooAngles );
|
||||
GetAttachment( LookupAttachment( "splash_pt" ), vecSplashPoint, fooAngles );
|
||||
|
||||
// Find the direction of the pontoons
|
||||
Vector vecLeftWakeDir = ( vecPontoonRearLeft - vecPontoonFrontLeft );
|
||||
Vector vecRightWakeDir = ( vecPontoonRearRight - vecPontoonFrontRight );
|
||||
|
||||
// Find the pontoon's size
|
||||
float flWakeLeftLength = VectorNormalize( vecLeftWakeDir );
|
||||
float flWakeRightLength = VectorNormalize( vecRightWakeDir );
|
||||
|
||||
vecPontoonFrontLeft.z = m_nExactWaterLevel;
|
||||
vecPontoonFrontRight.z = m_nExactWaterLevel;
|
||||
|
||||
if ( bDriven && vehicleSpeed > 128.0f )
|
||||
{
|
||||
DrawPontoonWake( vecPontoonFrontLeft, vecLeftWakeDir, flWakeLeftLength, vehicleSpeed );
|
||||
DrawPontoonWake( vecPontoonFrontRight, vecRightWakeDir, flWakeRightLength, vehicleSpeed );
|
||||
|
||||
Vector vecSplashDir;
|
||||
Vector vForward;
|
||||
GetVectors( &vForward, NULL, NULL );
|
||||
|
||||
if ( m_vecPhysVelocity.x < -64.0f )
|
||||
{
|
||||
vecSplashDir = vecLeftWakeDir - vForward;
|
||||
VectorNormalize( vecSplashDir );
|
||||
|
||||
// Don't do this if we're going backwards
|
||||
if ( m_vecPhysVelocity.y > 0.0f )
|
||||
{
|
||||
DrawPontoonSplash( vecPontoonFrontLeft + ( vecLeftWakeDir * 1.0f ), vecSplashDir, m_vecPhysVelocity.y );
|
||||
}
|
||||
}
|
||||
else if ( m_vecPhysVelocity.x > 64.0f )
|
||||
{
|
||||
vecSplashDir = vecRightWakeDir + vForward;
|
||||
VectorNormalize( vecSplashDir );
|
||||
|
||||
// Don't do this if we're going backwards
|
||||
if ( m_vecPhysVelocity.y > 0.0f )
|
||||
{
|
||||
DrawPontoonSplash( vecPontoonFrontRight + ( vecRightWakeDir * 1.0f ), vecSplashDir, m_vecPhysVelocity.y );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Must have at least one point
|
||||
if ( m_nStepCount <= 1 )
|
||||
return 1;
|
||||
|
||||
IMaterial *pMaterial = materials->FindMaterial( "effects/splashwake4", 0);
|
||||
|
||||
//Bind the material
|
||||
CMatRenderContextPtr pRenderContext( materials );
|
||||
IMesh *pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, pMaterial );
|
||||
|
||||
m_Mesh.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, (m_nStepCount-1) * 2 );
|
||||
|
||||
TrailPoint_t *pLast = GetTrailPoint( m_nStepCount - 1 );
|
||||
|
||||
TrailPoint_t currentPoint;
|
||||
currentPoint.m_flDieTime = gpGlobals->curtime + 0.5f;
|
||||
currentPoint.m_vecScreenPos = GetAbsOrigin();
|
||||
currentPoint.m_vecScreenPos[2] = m_nExactWaterLevel + 16;
|
||||
currentPoint.m_flTexCoord = pLast->m_flTexCoord + currentPoint.m_vecScreenPos.DistTo(pLast->m_vecScreenPos);
|
||||
currentPoint.m_flTexCoord = fmod( currentPoint.m_flTexCoord, 1 );
|
||||
currentPoint.m_flWidthVariance = 0.0f;
|
||||
|
||||
TrailPoint_t *pPrevPoint = NULL;
|
||||
|
||||
Vector segDir, normal;
|
||||
|
||||
for ( int i = 0; i <= m_nStepCount; ++i )
|
||||
{
|
||||
// This makes it so that we're always drawing to the current location
|
||||
TrailPoint_t *pPoint = (i != m_nStepCount) ? GetTrailPoint(i) : ¤tPoint;
|
||||
|
||||
float flLifePerc = RemapValClamped( ( pPoint->m_flDieTime - gpGlobals->curtime ), 0, WAKE_LIFETIME, 0.0f, 1.0f );
|
||||
|
||||
BeamSeg_t curSeg;
|
||||
curSeg.m_vColor.x = curSeg.m_vColor.y = curSeg.m_vColor.z = 1.0f;
|
||||
|
||||
float flAlphaFade = flLifePerc;
|
||||
float alpha = RemapValClamped( fabs( m_vecPhysVelocity.y ), 128, 600, 0.0f, 1.0f );
|
||||
|
||||
curSeg.m_flAlpha = 0.25f;
|
||||
curSeg.m_flAlpha *= flAlphaFade * alpha;
|
||||
|
||||
curSeg.m_vPos = pPoint->m_vecScreenPos;
|
||||
|
||||
float widthBase = SimpleSplineRemapVal( fabs( m_vecPhysVelocity.y ), 128, 600, 32, 48 );
|
||||
|
||||
curSeg.m_flWidth = Lerp( flLifePerc, widthBase*6, widthBase );
|
||||
curSeg.m_flWidth += pPoint->m_flWidthVariance;
|
||||
|
||||
if ( curSeg.m_flWidth < 0.0f )
|
||||
{
|
||||
curSeg.m_flWidth = 0.0f;
|
||||
}
|
||||
|
||||
curSeg.m_flTexCoord = pPoint->m_flTexCoord;
|
||||
|
||||
if ( pPrevPoint != NULL )
|
||||
{
|
||||
segDir = ( pPrevPoint->m_vecScreenPos - pPoint->m_vecScreenPos );
|
||||
VectorNormalize( segDir );
|
||||
|
||||
normal = CrossProduct( segDir, Vector( 0, 0, -1 ) );
|
||||
|
||||
DrawSegment( curSeg, normal );
|
||||
}
|
||||
|
||||
pPrevPoint = pPoint;
|
||||
}
|
||||
|
||||
m_Mesh.End();
|
||||
pMesh->Draw();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : flags -
|
||||
// Output : int
|
||||
//-----------------------------------------------------------------------------
|
||||
int C_PropAirboat::DrawModel( int flags )
|
||||
{
|
||||
if ( BaseClass::DrawModel( flags ) == false )
|
||||
return 0;
|
||||
|
||||
if ( !m_bReadyToDraw )
|
||||
return 0;
|
||||
|
||||
return DrawWake();
|
||||
}
|
||||
Reference in New Issue
Block a user