text: //========= Copyright Valve Corporation, All rights reserved. ============//
  //
  // Purpose: 
  //
  // $NoKeywords: $
  //=============================================================================//
  
  
  #include "cbase.h"
  #include "c_prop_vehicle.h"
  #include "hud.h"		
  #include <vgui_controls/Controls.h>
  #include <Color.h>
  #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 "headtrack/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
  }
  
  