//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "c_prop_vehicle.h" #include "hud.h" #include #include #include "view.h" #include "engine/ivdebugoverlay.h" #include "movevars_shared.h" #include "iviewrender.h" #include "vgui/ISurface.h" #include "client_virtualreality.h" #include "../hud_crosshair.h" #include "sourcevr/isourcevirtualreality.h" // NVNT haptic utils #include "haptics/haptic_utils.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" int ScreenTransform( const Vector& point, Vector& screen ); extern ConVar default_fov; extern ConVar joy_response_move_vehicle; IMPLEMENT_CLIENTCLASS_DT(C_PropVehicleDriveable, DT_PropVehicleDriveable, CPropVehicleDriveable) RecvPropEHandle( RECVINFO(m_hPlayer) ), RecvPropInt( RECVINFO( m_nSpeed ) ), RecvPropInt( RECVINFO( m_nRPM ) ), RecvPropFloat( RECVINFO( m_flThrottle ) ), RecvPropInt( RECVINFO( m_nBoostTimeLeft ) ), RecvPropInt( RECVINFO( m_nHasBoost ) ), RecvPropInt( RECVINFO( m_nScannerDisabledWeapons ) ), RecvPropInt( RECVINFO( m_nScannerDisabledVehicle ) ), RecvPropInt( RECVINFO( m_bEnterAnimOn ) ), RecvPropInt( RECVINFO( m_bExitAnimOn ) ), RecvPropInt( RECVINFO( m_bUnableToFire ) ), RecvPropVector( RECVINFO( m_vecEyeExitEndpoint ) ), RecvPropBool( RECVINFO( m_bHasGun ) ), RecvPropVector( RECVINFO( m_vecGunCrosshair ) ), END_RECV_TABLE() BEGIN_DATADESC( C_PropVehicleDriveable ) DEFINE_EMBEDDED( m_ViewSmoothingData ), END_DATADESC() ConVar r_VehicleViewClamp( "r_VehicleViewClamp", "1", FCVAR_CHEAT ); #define ROLL_CURVE_ZERO 20 // roll less than this is clamped to zero #define ROLL_CURVE_LINEAR 90 // roll greater than this is copied out #define PITCH_CURVE_ZERO 10 // pitch less than this is clamped to zero #define PITCH_CURVE_LINEAR 45 // pitch greater than this is copied out // spline in between //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- C_PropVehicleDriveable::C_PropVehicleDriveable() : m_iv_vecGunCrosshair( "C_PropVehicleDriveable::m_iv_vecGunCrosshair" ) { m_hPrevPlayer = NULL; memset( &m_ViewSmoothingData, 0, sizeof( m_ViewSmoothingData ) ); m_ViewSmoothingData.pVehicle = this; m_ViewSmoothingData.bClampEyeAngles = true; m_ViewSmoothingData.bDampenEyePosition = true; m_ViewSmoothingData.flPitchCurveZero = PITCH_CURVE_ZERO; m_ViewSmoothingData.flPitchCurveLinear = PITCH_CURVE_LINEAR; m_ViewSmoothingData.flRollCurveZero = ROLL_CURVE_ZERO; m_ViewSmoothingData.flRollCurveLinear = ROLL_CURVE_LINEAR; m_ViewSmoothingData.flFOV = m_flFOV = default_fov.GetFloat(); AddVar( &m_vecGunCrosshair, &m_iv_vecGunCrosshair, LATCH_SIMULATION_VAR ); } //----------------------------------------------------------------------------- // Purpose: De-constructor //----------------------------------------------------------------------------- C_PropVehicleDriveable::~C_PropVehicleDriveable() { } //----------------------------------------------------------------------------- // By default all driveable vehicles use the curve defined by the convar. //----------------------------------------------------------------------------- int C_PropVehicleDriveable::GetJoystickResponseCurve() const { return joy_response_move_vehicle.GetInt(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- C_BaseCombatCharacter *C_PropVehicleDriveable::GetPassenger( int nRole ) { if ( nRole == VEHICLE_ROLE_DRIVER ) return m_hPlayer.Get(); return NULL; } //----------------------------------------------------------------------------- // Returns the role of the passenger //----------------------------------------------------------------------------- int C_PropVehicleDriveable::GetPassengerRole( C_BaseCombatCharacter *pPassenger ) { if ( m_hPlayer.Get() == pPassenger ) return VEHICLE_ROLE_DRIVER; return VEHICLE_ROLE_NONE; } //----------------------------------------------------------------------------- // Purpose: // Input : updateType - //----------------------------------------------------------------------------- void C_PropVehicleDriveable::OnPreDataChanged( DataUpdateType_t updateType ) { BaseClass::OnPreDataChanged( updateType ); m_hPrevPlayer = m_hPlayer; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_PropVehicleDriveable::OnDataChanged( DataUpdateType_t updateType ) { BaseClass::OnDataChanged( updateType ); if ( m_hPlayer && !m_hPrevPlayer ) { OnEnteredVehicle( m_hPlayer ); SetNextClientThink( CLIENT_THINK_ALWAYS ); g_ClientVirtualReality.AlignTorsoAndViewToWeapon(); } else if ( !m_hPlayer && m_hPrevPlayer ) { // NVNT notify haptics system of navigation exit OnExitedVehicle( m_hPrevPlayer ); // They have just exited the vehicle. // Sometimes we never reach the end of our exit anim, such as if the // animation doesn't have fadeout 0 specified in the QC, so we fail to // catch it in VehicleViewSmoothing. Catch it here instead. m_ViewSmoothingData.bWasRunningAnim = false; SetNextClientThink( CLIENT_THINK_NEVER ); } } //----------------------------------------------------------------------------- // Should this object cast render-to-texture shadows? //----------------------------------------------------------------------------- ShadowType_t C_PropVehicleDriveable::ShadowCastType() { CStudioHdr *pStudioHdr = GetModelPtr(); if ( !pStudioHdr ) return SHADOWS_NONE; if ( IsEffectActive(EF_NODRAW | EF_NOSHADOW) ) return SHADOWS_NONE; // Always use render-to-texture. We'll always the dirty bits in our think function return SHADOWS_RENDER_TO_TEXTURE; } //----------------------------------------------------------------------------- // Mark the shadow as dirty while the vehicle is being driven //----------------------------------------------------------------------------- void C_PropVehicleDriveable::ClientThink( void ) { // The vehicle is always dirty owing to pose parameters while it's being driven. g_pClientShadowMgr->MarkRenderToTextureShadowDirty( GetShadowHandle() ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_PropVehicleDriveable::DampenEyePosition( Vector &vecVehicleEyePos, QAngle &vecVehicleEyeAngles ) { } //----------------------------------------------------------------------------- // Purpose: Modify the player view/camera while in a vehicle //----------------------------------------------------------------------------- void C_PropVehicleDriveable::GetVehicleViewPosition( int nRole, Vector *pAbsOrigin, QAngle *pAbsAngles, float *pFOV /*=NULL*/ ) { SharedVehicleViewSmoothing( m_hPlayer, pAbsOrigin, pAbsAngles, m_bEnterAnimOn, m_bExitAnimOn, m_vecEyeExitEndpoint, &m_ViewSmoothingData, pFOV ); } //----------------------------------------------------------------------------- // Futzes with the clip planes //----------------------------------------------------------------------------- void C_PropVehicleDriveable::GetVehicleClipPlanes( float &flZNear, float &flZFar ) const { // FIXME: Need something a better long-term, this fixes the buggy. flZNear = 6; } //----------------------------------------------------------------------------- // Renders hud elements //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Simply used to return intensity value based upon current timer passed in //----------------------------------------------------------------------------- int GetFlashColorIntensity( int LowIntensity, int HighIntensity, bool Dimming, int Increment, int Timer ) { if ( Dimming ) return ( HighIntensity - Timer * Increment ); else return ( LowIntensity + Timer * Increment ); } #define TRIANGULATED_CROSSHAIR 1 void C_PropVehicleDriveable::DrawHudElements( ) { CHudTexture *pIcon; int iIconX, iIconY; if (m_bHasGun) { // draw crosshairs for vehicle gun pIcon = gHUD.GetIcon( "gunhair" ); if ( pIcon != NULL ) { float x, y; if( UseVR() ) { C_BasePlayer *pPlayer = (C_BasePlayer *)GetPassenger( VEHICLE_ROLE_DRIVER ); Vector vecStart, vecDirection; pPlayer->EyePositionAndVectors( &vecStart, &vecDirection, NULL, NULL ); Vector vecEnd = vecStart + vecDirection * MAX_TRACE_LENGTH; trace_t tr; UTIL_TraceLine( vecStart, vecEnd, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); Vector screen; screen.Init(); ScreenTransform(tr.endpos, screen); int vx, vy, vw, vh; vgui::surface()->GetFullscreenViewport( vx, vy, vw, vh ); float screenWidth = vw; float screenHeight = vh; x = 0.5f * ( 1.0f + screen[0] ) * screenWidth + 0.5f; y = 0.5f * ( 1.0f - screen[1] ) * screenHeight + 0.5f; } else { Vector screen; x = ScreenWidth()/2; y = ScreenHeight()/2; #if TRIANGULATED_CROSSHAIR ScreenTransform( m_vecGunCrosshair, screen ); x += 0.5 * screen[0] * ScreenWidth() + 0.5; y -= 0.5 * screen[1] * ScreenHeight() + 0.5; #endif } x -= pIcon->Width() / 2; y -= pIcon->Height() / 2; Color clr = ( m_bUnableToFire ) ? gHUD.m_clrCaution : gHUD.m_clrNormal; pIcon->DrawSelf( x, y, clr ); } if ( m_nScannerDisabledWeapons ) { // Draw icons for scanners "weps disabled" pIcon = gHUD.GetIcon( "dmg_bio" ); if ( pIcon ) { iIconY = 467 - pIcon->Height() / 2; iIconX = 385; if ( !m_bScannerWepIcon ) { pIcon->DrawSelf( XRES(iIconX), YRES(iIconY), Color( 0, 0, 255, 255 ) ); m_bScannerWepIcon = true; m_iScannerWepFlashTimer = 0; m_bScannerWepDim = true; } else { pIcon->DrawSelf( XRES(iIconX), YRES(iIconY), Color( 0, 0, GetFlashColorIntensity(55, 255, m_bScannerWepDim, 10, m_iScannerWepFlashTimer), 255 ) ); m_iScannerWepFlashTimer++; m_iScannerWepFlashTimer %= 20; if(!m_iScannerWepFlashTimer) m_bScannerWepDim ^= 1; } } } } if ( m_nScannerDisabledVehicle ) { // Draw icons for scanners "vehicle disabled" pIcon = gHUD.GetIcon( "dmg_bio" ); if ( pIcon ) { iIconY = 467 - pIcon->Height() / 2; iIconX = 410; if ( !m_bScannerVehicleIcon ) { pIcon->DrawSelf( XRES(iIconX), YRES(iIconY), Color( 0, 0, 255, 255 ) ); m_bScannerVehicleIcon = true; m_iScannerVehicleFlashTimer = 0; m_bScannerVehicleDim = true; } else { pIcon->DrawSelf( XRES(iIconX), YRES(iIconY), Color( 0, 0, GetFlashColorIntensity(55, 255, m_bScannerVehicleDim, 10, m_iScannerVehicleFlashTimer), 255 ) ); m_iScannerVehicleFlashTimer++; m_iScannerVehicleFlashTimer %= 20; if(!m_iScannerVehicleFlashTimer) m_bScannerVehicleDim ^= 1; } } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_PropVehicleDriveable::RestrictView( float *pYawBounds, float *pPitchBounds, float *pRollBounds, QAngle &vecViewAngles ) { int eyeAttachmentIndex = LookupAttachment( "vehicle_driver_eyes" ); Vector vehicleEyeOrigin; QAngle vehicleEyeAngles; GetAttachmentLocal( eyeAttachmentIndex, vehicleEyeOrigin, vehicleEyeAngles ); // Limit the yaw. if ( pYawBounds ) { float flAngleDiff = AngleDiff( vecViewAngles.y, vehicleEyeAngles.y ); flAngleDiff = clamp( flAngleDiff, pYawBounds[0], pYawBounds[1] ); vecViewAngles.y = vehicleEyeAngles.y + flAngleDiff; } // Limit the pitch. if ( pPitchBounds ) { float flAngleDiff = AngleDiff( vecViewAngles.x, vehicleEyeAngles.x ); flAngleDiff = clamp( flAngleDiff, pPitchBounds[0], pPitchBounds[1] ); vecViewAngles.x = vehicleEyeAngles.x + flAngleDiff; } // Limit the roll. if ( pRollBounds ) { float flAngleDiff = AngleDiff( vecViewAngles.z, vehicleEyeAngles.z ); flAngleDiff = clamp( flAngleDiff, pRollBounds[0], pRollBounds[1] ); vecViewAngles.z = vehicleEyeAngles.z + flAngleDiff; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_PropVehicleDriveable::UpdateViewAngles( C_BasePlayer *pLocalPlayer, CUserCmd *pCmd ) { if ( r_VehicleViewClamp.GetInt() ) { float pitchBounds[2] = { -85.0f, 25.0f }; RestrictView( NULL, pitchBounds, NULL, pCmd->viewangles ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_PropVehicleDriveable::OnEnteredVehicle( C_BaseCombatCharacter *pPassenger ) { #if defined( WIN32 ) && !defined( _X360 ) // NVNT notify haptics system of navigation change HapticsEnteredVehicle(this,pPassenger); #endif } // NVNT - added function void C_PropVehicleDriveable::OnExitedVehicle( C_BaseCombatCharacter *pPassenger ) { #if defined( WIN32 ) && !defined( _X360 ) // NVNT notify haptics system of navigation exit HapticsExitedVehicle(this,pPassenger); #endif }