//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #include "cbase.h" #include "c_vehicle_jeep.h" #include "movevars_shared.h" #include "view.h" #include "flashlighteffect.h" #include "c_baseplayer.h" #include "c_te_effect_dispatch.h" #include "fx.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" extern ConVar default_fov; ConVar r_JeepViewBlendTo( "r_JeepViewBlendTo", "1", FCVAR_CHEAT ); ConVar r_JeepViewBlendToScale( "r_JeepViewBlendToScale", "0.03", FCVAR_CHEAT ); ConVar r_JeepViewBlendToTime( "r_JeepViewBlendToTime", "1.5", FCVAR_CHEAT ); #define JEEP_DELTA_LENGTH_MAX 12.0f // 1 foot #define JEEP_FRAMETIME_MIN 1e-6 #define JEEP_HEADLIGHT_DISTANCE 1000 IMPLEMENT_CLIENTCLASS_DT( C_PropJeep, DT_PropJeep, CPropJeep ) RecvPropBool( RECVINFO( m_bHeadlightIsOn ) ), END_RECV_TABLE() //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- C_PropJeep::C_PropJeep() { m_vecEyeSpeed.Init(); m_flViewAngleDeltaTime = 0.0f; m_pHeadlight = NULL; ConVarRef r_JeepFOV( "r_JeepFOV" ); m_ViewSmoothingData.flFOV = r_JeepFOV.GetFloat(); } //----------------------------------------------------------------------------- // Purpose: Deconstructor //----------------------------------------------------------------------------- C_PropJeep::~C_PropJeep() { if ( m_pHeadlight ) { delete m_pHeadlight; } } void C_PropJeep::Simulate( void ) { // The dim light is the flashlight. if ( m_bHeadlightIsOn ) { if ( m_pHeadlight == NULL ) { // Turned on the headlight; create it. m_pHeadlight = new CHeadlightEffect; if ( m_pHeadlight == NULL ) return; m_pHeadlight->TurnOn(); } QAngle vAngle; Vector vVector; Vector vecForward, vecRight, vecUp; int iAttachment = LookupAttachment( "headlight" ); if ( iAttachment != INVALID_PARTICLE_ATTACHMENT ) { GetAttachment( iAttachment, vVector, vAngle ); AngleVectors( vAngle, &vecForward, &vecRight, &vecUp ); m_pHeadlight->UpdateLight( vVector, vecForward, vecRight, vecUp, JEEP_HEADLIGHT_DISTANCE ); } } else if ( m_pHeadlight ) { // Turned off the flashlight; delete it. delete m_pHeadlight; m_pHeadlight = NULL; } BaseClass::Simulate(); } //----------------------------------------------------------------------------- // Purpose: Blend view angles. //----------------------------------------------------------------------------- void C_PropJeep::UpdateViewAngles( C_BasePlayer *pLocalPlayer, CUserCmd *pCmd ) { if ( r_JeepViewBlendTo.GetInt() ) { // Check to see if the mouse has been touched in a bit or that we are not throttling. if ( ( pCmd->mousedx != 0 || pCmd->mousedy != 0 ) || ( fabsf( m_flThrottle ) < 0.01f ) ) { m_flViewAngleDeltaTime = 0.0f; } else { m_flViewAngleDeltaTime += gpGlobals->frametime; } if ( m_flViewAngleDeltaTime > r_JeepViewBlendToTime.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_JeepViewBlendToScale.GetFloat() ); pCmd->viewangles = outAngles; } } BaseClass::UpdateViewAngles( pLocalPlayer, pCmd ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_PropJeep::DampenEyePosition( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles ) { #ifdef HL2_CLIENT_DLL // Get the frametime. (Check to see if enough time has passed to warrent dampening). float flFrameTime = gpGlobals->frametime; if ( flFrameTime < JEEP_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 ); #endif } //----------------------------------------------------------------------------- // Use the controller as follows: // speed += ( pCoefficientsOut[0] * ( targetPos - currentPos ) + pCoefficientsOut[1] * ( targetSpeed - currentSpeed ) ) * flDeltaTime; //----------------------------------------------------------------------------- void C_PropJeep::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_PropJeep::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 > JEEP_DELTA_LENGTH_MAX ) { // Clamp. float flDelta = flDeltaLength - JEEP_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 = JEEP_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_JeepViewDampenFreq.GetFloat(), r_JeepViewDampenDamp.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_JeepViewDampenFreq.GetFloat(), r_JeepViewDampenDamp.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_PropJeep::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_JeepViewZHeight.GetFloat() * vecUp.z; // NOTE: Should probably use some damped equation here. } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_PropJeep::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: // Input : &data - //----------------------------------------------------------------------------- void WheelDustCallback( const CEffectData &data ) { CSmartPtr pSimple = CSimpleEmitter::Create( "dust" ); pSimple->SetSortOrigin( data.m_vOrigin ); pSimple->SetNearClip( 32, 64 ); SimpleParticle *pParticle; Vector offset; //FIXME: Better sampling area offset = data.m_vOrigin + ( data.m_vNormal * data.m_flScale ); //Find area ambient light color and use it to tint smoke Vector worldLight = WorldGetLightForPoint( offset, true ); //Throw puffs offset.Random( -(data.m_flScale*16.0f), data.m_flScale*16.0f ); offset.z = 0.0f; offset += data.m_vOrigin + ( data.m_vNormal * data.m_flScale ); pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof(SimpleParticle), g_Mat_DustPuff[0], offset ); if ( pParticle != NULL ) { pParticle->m_flLifetime = 0.0f; pParticle->m_flDieTime = random->RandomFloat( 0.25f, 0.5f ); pParticle->m_vecVelocity = RandomVector( -1.0f, 1.0f ); VectorNormalize( pParticle->m_vecVelocity ); pParticle->m_vecVelocity[2] += random->RandomFloat( 16.0f, 32.0f ) * (data.m_flScale*2.0f); int color = random->RandomInt( 100, 150 ); pParticle->m_uchColor[0] = 16 + ( worldLight[0] * (float) color ); pParticle->m_uchColor[1] = 8 + ( worldLight[1] * (float) color ); pParticle->m_uchColor[2] = ( worldLight[2] * (float) color ); pParticle->m_uchStartAlpha = random->RandomInt( 64.0f*data.m_flScale, 128.0f*data.m_flScale ); pParticle->m_uchEndAlpha = 0; pParticle->m_uchStartSize = random->RandomInt( 16, 24 ) * data.m_flScale; pParticle->m_uchEndSize = random->RandomInt( 32, 48 ) * data.m_flScale; pParticle->m_flRoll = random->RandomInt( 0, 360 ); pParticle->m_flRollDelta = random->RandomFloat( -2.0f, 2.0f ); } } DECLARE_CLIENT_EFFECT( "WheelDust", WheelDustCallback );