
//////////////////////////////////////////////////////////////////////
//
//	Crytek Source code 
//	Copyright (c) Crytek 2001-2004
//
//  File: XVehicle.cpp
//  Description: Vehicle handling code
//
//  History:
//  - Created by Kirill Bulatsev
//	- Modified by Filippo De Luca
//	- February 2005: Modified by Marco Corbetta for SDK release
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "XVehicle.h"
#include <IAgent.h>

//////////////////////////////////////////////////////////////////////
#define SGN(a) ((a < 0) ? -1 : 1) 

string	CVehicle::m_sNoWeaponName("NoWeapon");

//////////////////////////////////////////////////////////////////////
CVehicle::CVehicle(CXGame *pGame):
m_fPedalSpeed(0.0f),
m_DriverID(0),
m_fPrevFwvSpeedLen(0.0f),
m_pHeadLight(NULL),
m_pHeadLightLeft(NULL),
m_pHeadLightRight(NULL),
m_pBreakLightLeft(NULL),
m_pBreakLightRight(NULL),
m_bHeadLightsOn(false),
m_bBreakLightsOn(false),
m_bAutoLights(false),
m_bPhysAwaike(false),
m_bBrakeOnNodriver(true),
m_fNoDriverTime(0.0f),
m_AngleLimitVFlag(false),
m_AngleLimitHFlag(false),
m_vWpnAng(0,0,180),
m_vWpnAngDelta(0,0,0),
m_bCrossOnScreen(false),
m_WeaponUser(0),
m_bForceHandBreak(false)
{
	m_pGame = pGame;
	m_pScriptObject=NULL;
	m_fPedalSpeed = 10.0f;
	m_fSteerSpeed = 25.0f; m_fv0MaxSteer = 40.0f; m_fkvMaxSteer = 0.0f;
	m_fv0SteerRelaxation = 0.0f; m_fkvSteerRelaxation = 5.0f;
	m_fMaxSteeringPedal = 0.015f; m_fPedalLimitSpeed = 10.0f;
	m_ScriptStream.Create(m_pGame->GetScriptSystem());
	m_Type = VHT_CAR;

	SwitchLights(0);

	m_btVelocity = 0;
	m_vEnginePos(0,0,0);

	m_fEngineHealth=100.0f;			
	m_fDeathTimer=-1;
	
	m_pGliderGravity=m_pGame->GetSystem()->GetIConsole()->GetCVar("game_GliderGravity");		
	m_pGliderBackImpulse=m_pGame->GetSystem()->GetIConsole()->GetCVar("game_GliderBackImpulse");		
	m_pGliderDamping=m_pGame->GetSystem()->GetIConsole()->GetCVar("game_GliderDamping");		
	m_pGliderStartGravity=m_pGame->GetSystem()->GetIConsole()->GetCVar("game_GliderStartGravity");		

	m_sAutoWeaponName.clear();
	m_sMountedWeaponName.clear();

	m_AngleLimitVFlag = true;
	m_AngleLimitHFlag = true;	
	m_MaxVAngle = 20;
	m_MinHAngle = -95;
	m_MaxHAngle = 95;

	m_bUpdateCamera = false;
	m_vCamStiffness[0](0,0,0); m_vCamStiffness[1](0,0,0);
	m_vCamLimits[0](1,1,1); m_vCamLimits[1](1,1,1);
	m_fCamDamping=0.8f; m_fMaxCamTimestep=0.01f; m_fCamSnapDist=0.001f; m_fCamSnapVel=0.01f;

	m_fhandbraking_value = 0;
	m_fhandbraking_value_nodriver = 0;

	m_fMaxBrakingFriction = 1.0;
	m_fMaxBrakingFrictionNoDriver = 1.0;

	m_bUsingNoDriverFriction = false;

	m_DirVelDotProduct = 0.0f;

	m_fstabilizejump = 0.0f;

	m_flastwheelrotation = 0.0f;

	m_fWaterlevelLimit = 0.0f;
}

//////////////////////////////////////////////////////////////////////
CVehicle::~CVehicle()
{
	SAFE_DELETE( m_pHeadLight );
	SAFE_DELETE( m_pHeadLightLeft );
	SAFE_DELETE( m_pHeadLightRight );
	SAFE_DELETE( m_pBreakLightLeft );
	SAFE_DELETE( m_pBreakLightRight );

	if(m_pScriptObject)
		m_pScriptObject->Release();
}

//////////////////////////////////////////////////////////////////////
bool CVehicle::Init()
{
	m_bAcceleratedLastUpdate = false;
	m_bAcceleratedFlagSetLastFrame = false;
	m_bIsBreaking = false;
	m_iPrevMoveTime = 0;
	m_fSimTime = 0;

	GetEntity()->GetScriptObject()->SetValue("type", "Vehicle");
	// [kirill] need this to use character(weapon on vehicle) and m_objects (vehicle's geometry)
	// when calculating BBox
	GetEntity()->SetFlags( ETY_FLAG_CALCBBOX_USEALL );
	
	return true;
}
 
//////////////////////////////////////////////////////////////////////
void CVehicle::Update()
{
	// [marco] remove the vehicle after some time it 
	// exploded - not in multiplayer
	float fTimeStep = m_pGame->GetSystem()->GetITimer()->GetFrameTime();
	if (m_fEngineHealth<=0)
		m_bHeadLightsOn=false;
	if (!m_pGame->IsMultiplayer())  
	{	
		if (m_fEngineHealth<=0)
		{	
			// first time set the death timer
			if (m_fDeathTimer<-0.5f) // epsilon
			{			
				float	deathTimer=m_pGame->p_deathtime->GetFVal();
				m_fDeathTimer = deathTimer;	
			}
			else
				m_fDeathTimer-=fTimeStep;

			if (m_fDeathTimer<=0.0f)
			{
				m_fDeathTimer = 0.0f;
				int nRendererFrameID = m_pGame->GetSystem()->GetIRenderer()->GetFrameID();
				if (nRendererFrameID-m_pEntity->GetDrawFrame()>150)
				{
					m_pEntity->Remove();
					return;
				}
			}
		}
	}

	UpdateLights();
	UpdateWeaponPosAngl();

	if(GetEntity()->GetPhysics())
	if(!GetEntity()->GetPhysics()->GetStatus(&pe_status_awake()))
		UpdateCamera(fTimeStep, m_pGame->IsSynchronizing());

	if ((m_Type == VHT_BOAT) )
		WakeupPhys();

	if (GetEntity()->GetPhysics())
	{
		pe_params_flags pf;
		pf.flagsOR = pef_custom_poststep | pef_fixed_damping;
		GetEntity()->GetPhysics()->SetParams(&pf);
	}
}

//////////////////////////////////////////////////////////////////////
void CVehicle::UpdatePhysics(float fTimeStep)
{
	if(!HasDriver())
		m_fNoDriverTime += fTimeStep;

	UpdateMovementStatus();

	if ((m_Type == VHT_BOAT))
		UpdateBoat(fTimeStep);
	else if ( m_Type == VHT_PARAGLIDER )
		UpdateParaglider(fTimeStep);
	// [filippo]
	else if (m_Type == VHT_CAR)
	{
		// there is no driver
		// brake the car
		IPhysicalEntity *icar = GetEntity()->GetPhysics();
		if(!icar)	// not phisycalized
		{
			m_pGame->GetSystem()->GetILog()->Log("\002 WARNING car is NOT phisycalized [ %s ]", m_pEntity->GetName());
			return;
		}
		
		if ((!HasDriver() && (m_bBrakeOnNodriver || m_fNoDriverTime>3.0f)) || m_bForceHandBreak)
		{
			//[ filippo]: when vehicles are without driver use a fake friction and save the right friction value.
			if (!m_bUsingNoDriverFriction)
			{
				pe_params_car pc;
				pc.maxBrakingFriction = m_fMaxBrakingFrictionNoDriver;
				
				icar->SetParams(&pc);

				m_bUsingNoDriverFriction = true;
			}

			pe_action_drive drive;
			drive.bHandBrake = 1;
			icar->Action(&drive);

			AdditionalPhysics(icar,fTimeStep,true);
		}
		else
		{
			//if there is no driver and we still dont handbrake automatically , release the pedal
			if (!HasDriver())
			{
				pe_action_drive drive;
				drive.pedal = 0;
				icar->Action(&drive);
			}
			// [filippo]: when vehicles have driver use restore the right friction value.
			if (m_bUsingNoDriverFriction)
			{
				pe_params_car pc;
				pc.maxBrakingFriction = m_fMaxBrakingFriction;
				
				icar->SetParams(&pc);

				m_bUsingNoDriverFriction = false;
			}

			AdditionalPhysics(icar,fTimeStep,false);
		}
	}
	
	m_pGame->ConstrainToSandbox(GetEntity());
	UpdateCamera(fTimeStep, m_pGame->IsSynchronizing());

	if (m_pGame->m_pClient && !m_pGame->m_pServer && m_pGame->m_pClient->GetPlayerId()==m_DriverID && !m_bAIDriver)
		ProcessMovement(m_cmdLastMove);
}

//////////////////////////////////////////////////////////////////////
// 0 - normal
// 1 - moving back
// 2 - turnLeft
// 3 - turnRight
// 4 - break
// 5 - break beckwards
// sets current movement type to be used to play animations for users 
// (turning/breaking/movingBack)
//////////////////////////////////////////////////////////////////////
void	CVehicle::UpdateMovementStatus()
{
	// if nobody inside - no need to update usersMovamaneStates
	if(m_UsersList.empty())
		return;

	IPhysicalEntity *pEnt=m_pEntity->GetPhysics();
	pe_status_dynamics	dyn;
	pEnt->GetStatus(&dyn);

	m_TurnState = 0;
	if(dyn.w.z > .1f)
		m_TurnState = 2;
	else if(dyn.w.z < -.1f)
		m_TurnState = 3;

	Vec3	vFwdDir = m_pEntity->GetAngles();

	Matrix44 tm;
	tm.SetIdentity();
	tm=Matrix44::CreateRotationZYX(-vFwdDir*gf_DEGTORAD)*tm; //NOTE: angles in radians and negated 
	vFwdDir = GetTransposed44(tm)*(Vec3d(0,-1,0));

	float	dot2d = vFwdDir.x*dyn.v.x + vFwdDir.y*dyn.v.y;
	float	velLen2 = dyn.v.len2();
	if( dot2d>0 && velLen2>1.0f)
		m_TurnState = 1;
	
	if(m_bIsBreaking && velLen2>3.0f)
	{
		if(m_TurnState == 1)
   			m_TurnState = 5;	
		else
   			m_TurnState = 4;
	}

	m_btVelocity = velLen2;

	m_DirVelDotProduct = dot2d;
}

//////////////////////////////////////////////////////////////////////////
//this is specific fake-physics code for the boat only		
void CVehicle::UpdateBoat(float fTimeStep)
{
	float	dTime = fTimeStep;
	m_fSimTime += fTimeStep;
	if( dTime > 0.1f )
		dTime = 0.1f ;

	pe_action_awake	aa;
	aa.bAwake=0;
	
	if (!m_pEntity->GetPhysics())
		return;	// not physicalized
	IPhysicalEntity *pEnt=m_pEntity->GetPhysics();

	float	cTime = m_fSimTime;//m_pGame->GetSystem()->GetITimer()->GetCurrTime();
	Vec3	pos;
	pe_action_impulse am;
	am.iSource = 3; // this will mark impulse as non-scheduleable in fixed timestep multiplayer
	Matrix33 m;
	Vec3		boatUp=Vec3(0.0f,0.0f,1.0f);
	Vec3		boatFwd=Vec3(0.0f,-1.0f,0.0f);
	Vec3		vert=Vec3(0.0f,0.0f,1.0f);
	Vec3		dv;
	Vec3		waterLine;
	pe_status_dynamics	dyn;
	pe_status_pos spos;
	spos.pMtx3x3 = m.data;
	pEnt->GetStatus(&spos);
	pos = spos.pos;

	//get the engine pos, from where to apply the impulse
	GetEntity()->GetHelperPosition("waterlevel",waterLine,true);	
	waterLine = m*waterLine+pos;

	pEnt->GetStatus( &dyn );
	m_btVelocity = dyn.v.len();

	am.momentum.Set(0,0,0);
	// transform into entity space
	boatUp = m*boatUp;
	boatFwd = m*boatFwd;
	dv = vert^boatUp;

	// waves
	float	waveWScale = m_pGame->b_wscalew->GetFVal();
	float	waveScale = m_pGame->b_wscale->GetFVal();
	float	waveMomentum = m_BoatParams.m_fBtWave;//m_pGame->b_wmomentum->GetFVal();
	Vec3	wave;
	wave.x = cry_cosf((pos.x+pos.y)*waveScale + cTime*waveWScale);
	wave.y = cry_sinf((pos.x+pos.y)*waveScale + cTime*waveWScale*1.21f);
	wave.z = 0.0f;

	//get the engine pos, from where to apply the impulse
	GetEntity()->GetHelperPosition("engine",m_vEnginePos,true);
	m_vEnginePos = m*m_vEnginePos+pos;
	
	float	waterLevel = (m_pGame->GetSystem()->GetI3DEngine()->GetWaterLevel( m_pEntity) - waterLine.z );//+ .6f - pos.z);

	bool inwatersave = m_btInWater;
	m_btInWater = (waterLevel>m_fWaterlevelLimit);

	//this is because we dont want the inwater state to switch between inwater/onground every 0.01 sec
	if (m_btInWater && !inwatersave)
		m_fWaterlevelLimit = -0.25f;
	else if (!m_btInWater && inwatersave)
		m_fWaterlevelLimit = 0.0f;
	
	bool	bFloating = true;
	float	coef;

	if (m_btInWater)	// is in water
	{
		coef = (1.0f + dv.len2()*5.0f)*m_BoatParams.m_fBtStand*dTime;

		am.momentum.x = -(dv.x+dyn.w.x*m_BoatParams.m_fBtDumpW)*coef;
		am.momentum.y = -(dv.y+dyn.w.y*m_BoatParams.m_fBtDumpW)*coef;

		// add waves
		wave = vert^wave;
		am.momentum.x += wave.x*waveMomentum*dTime;
		am.momentum.y += wave.y*waveMomentum*dTime;

		// do water friction
		am.momentum.z -= dyn.w.z*m_BoatParams.m_fBtDumpRot*dTime;

		// here goes speed damping - separate fwd/back and sideways
		// get forward and side components of velocity
		Vec3	projVel(dyn.v.x, dyn.v.y, 0.0f);
		Vec3	projDir(boatFwd.x, boatFwd.y, 0.0f);
		float	projVelLen = projVel.len();
		projVel.normalize();
		projDir.normalize();
		Vec3	projSide(projDir.y, -projDir.x, 0.0f);
		float	velCos = projVel.x*projDir.x + projVel.y*projDir.y;
		float	fwdVelLen = projVelLen*velCos;
		Vec3	fwdV = projDir*fwdVelLen;
		velCos = projVel.x*projSide.x + projVel.y*projSide.y;
		Vec3	sideV = projSide*projVelLen*velCos;
		am.impulse.x = -(fwdV.x*m_BoatParams.m_fBtDumpV + sideV.x*m_BoatParams.m_fBtDumpVSide)*dTime;
		am.impulse.y = -(fwdV.y*m_BoatParams.m_fBtDumpV + sideV.y*m_BoatParams.m_fBtDumpVSide)*dTime;
		am.impulse.z = -dyn.v.z*m_BoatParams.m_fBtDumpVH*dTime;

		// do tilt on speedup
		fwdVelLen = -fwdVelLen;
		float	velDiff = fwdVelLen - m_fPrevFwvSpeedLen;	
		if( fwdVelLen>0.0f )
		{
			Vec3 momentum = (-boatFwd)^Vec3(0.0f, 0.0f, m_BoatParams.m_fBtTitlSpd);
			if(velDiff>0 && velDiff/fwdVelLen>m_BoatParams.m_fBtTitlSpdA)
			{
				if(velDiff>dTime*20.0f)
					velDiff=dTime*20.0f;
				am.momentum += vectorf(momentum*velDiff);
				m_fPrevFwvTilt = velDiff;
			}
			else if( fwdVelLen>m_BoatParams.m_fBtTitlSpdMinV )
			{
				float	tilt = fwdVelLen*dTime*m_BoatParams.m_fBtTitlSpdMinVTilt;
				float	diff = m_fPrevFwvTilt - tilt;
				float	tiltSpd=.1f;
				if(diff/dTime>tiltSpd)
					tilt = m_fPrevFwvTilt - tiltSpd*dTime;
				am.momentum += vectorf(momentum*tilt);
				m_fPrevFwvTilt = tilt;
			}
		}
		m_fPrevFwvSpeedLen = fwdVelLen;
	}
	else // not in water (jumping?)
	{				
		coef = (1.0f + dv.len2()*5.0f)*m_BoatParams.m_fBtStandAir*dTime;

		bFloating = false;
		{
			// go straight
			am.momentum.x = -(dv.x+dyn.w.x*m_BoatParams.m_fBtDumpW)*coef;
			am.momentum.y = -(dv.y+dyn.w.y*m_BoatParams.m_fBtDumpW)*coef;
		}
	}

	pEnt->Action(&am);

	pe_simulation_params sp;			
	sp.gravity.zero();
	sp.gravity.z = waterLevel*m_pGame->b_float->GetFVal();
	if(!bFloating) 
	{
		//[filippo]: if m_BoatParams.m_fBoatGravity is 0 use the old sys
		sp.gravity.z = (m_BoatParams.m_fBoatGravity==0)?(sp.gravity.z*1.5f - 3):(m_BoatParams.m_fBoatGravity);
		//sp.gravity.z = sp.gravity.z*1.5f - 3; // LUC modification for better jumps out of water
	}
	else
		sp.gravity.z = sp.gravity.z*3.5f;

	pEnt->SetParams(&sp);

	// set water level
	//don't use water/float with physics
	pe_params_buoyancy pb;
	pb.waterDensity = 0.0f;
	pEnt->SetParams(&pb);
}

//////////////////////////////////////////////////////////////////////////
void CVehicle::UpdateParaglider(float fTimeStep)
{
	float	dTime = fTimeStep;
	if( dTime > 0.1f )
		dTime = 0.1f ;

	if (!HasDriver())
	{
		// do override gravity for the paraglider
		IPhysicalEntity *pEnt=m_pEntity->GetPhysics();
		pe_simulation_params sp;
		sp.gravity.zero();
		sp.gravity.z = -9.8f;
		sp.gravityFreefall=vectorf(0,0,-9.8f);
		sp.damping=m_pGliderDamping->GetFVal()*4.0f;
		sp.dampingFreefall=m_pGliderDamping->GetFVal();
		m_pEntity->GetPhysics()->SetParams(&sp);
		return;
	}

	//this is specific fake-physics code for the boat only		
	if (!m_pEntity->GetPhysics())
		return;	// not physicalized

	float	cTime = m_pGame->GetSystem()->GetITimer()->GetCurrTime();
	float	coef = m_BoatParams.m_fBtStand*dTime;
	Vec3	pos = m_pEntity->GetPos();
	Vec3	angle = m_pEntity->GetAngles();
	pe_action_impulse am;
	Matrix44	m;
	Vec3		boatUp=Vec3(0.0f,0.0f,1.0f);
	Vec3		boatFwd=Vec3(0.0f,-1.0f,0.0f);
	Vec3		vert=Vec3(0.0f,0.0f,1.0f);
	Vec3		dv;
	pe_status_dynamics	dyn;

	m_pEntity->GetPhysics()->GetStatus( &dyn );
	m_btVelocity = dyn.v.len();

	am.momentum.Set(0,0,0);
	// transform into entity space
	m.SetIdentity();
	angle.x = -DEG2RAD(angle.x);
	angle.y = -DEG2RAD(angle.y);
	angle.z = -DEG2RAD(angle.z);
	m.SetRotationZYX( angle );
	boatUp = m.TransformVectorOLD(boatUp);
	boatFwd = m.TransformVectorOLD(boatFwd);
	dv = vert^boatUp;

	// go straight
	coef = (1.0f + dv.len2()*5.0f)*m_BoatParams.m_fBtStand*dTime;
	am.momentum.x = -dv.x*coef;
	am.momentum.y = -dv.y*coef;
	am.momentum = am.momentum - dyn.w*m_BoatParams.m_fBtDumpRot*dTime;

	// here goes speed damping - separate fwd/back and sideways
	// get forvard and side component of velocity
	Vec3	projVel(dyn.v.x, dyn.v.y, 0.0f);
	Vec3	projDir(boatFwd.x, boatFwd.y, 0.0f);
	float	projVelLen = projVel.len();
	projVel.normalize();
	projDir.normalize();
	Vec3	projSide(projDir.y, -projDir.x, 0.0f);
	float	velCos = projVel.x*projDir.x + projVel.y*projDir.y;
	float	fwdVelLen = projVelLen*velCos;
	Vec3	fwdV = projDir*fwdVelLen;
	velCos = projVel.x*projSide.x + projVel.y*projSide.y;
	Vec3	sideV = projSide*projVelLen*velCos;
	am.impulse.x = -(fwdV.x*m_BoatParams.m_fBtDumpV + sideV.x*m_BoatParams.m_fBtDumpVSide)*dTime;
	am.impulse.y = -(fwdV.y*m_BoatParams.m_fBtDumpV + sideV.y*m_BoatParams.m_fBtDumpVSide)*dTime;
	am.impulse.z = -dyn.v.z*m_BoatParams.m_fBtDumpVH*dTime;

	am.impulse.x += -(fwdV.x*m_BoatParams.m_fBtDumpV + sideV.x*m_BoatParams.m_fBtDumpVSide)*dTime;
	am.impulse.y += -(fwdV.y*m_BoatParams.m_fBtDumpV + sideV.y*m_BoatParams.m_fBtDumpVSide)*dTime;

	if(fabs(boatFwd.z)<.07) // just go forward
	{
		am.impulse -= boatFwd*dTime*m_BoatParams.m_fBtSpeedV;
	}
	else if(boatFwd.z>0)	// going down
	{
		boatFwd *= 1.0f+boatFwd.z*6.0f;
		am.impulse -= boatFwd*dTime*m_BoatParams.m_fBtSpeedV;
	}
	else			// going up
	{
		if(projVelLen>m_BoatParams.m_fPgUpSpeedThrhld)	// go up if speed is high
		{
		float upscale = projVelLen;
			if( upscale>m_BoatParams.m_fPgUpSpeedThrhld+5 )
				upscale = m_BoatParams.m_fPgUpSpeedThrhld+5;
			am.impulse.z -= boatFwd.z*m_BoatParams.m_fPgBackUpRate*upscale*dTime;
		}

		if(m_btVelocity>5.0f)	// slow down
		{
			am.impulse += boatFwd.z*dyn.v*m_BoatParams.m_fPgBackUpSlowRate*dTime;
		}
		else					// keep going
		{
			am.impulse -= projDir*dTime*m_BoatParams.m_fBtSpeedV;

			Vec3 momentum = (boatFwd)^Vec3(0.0f, 0.0f, m_BoatParams.m_fBtTitlSpd)*10.0f;
			am.momentum = vectorf(momentum*dTime);
		}
	}

   // go down
	am.impulse.z -= m_BoatParams.m_fPgDownRate*dTime;

	//	am.impulse -= projDir*dTime*m_BoatParams.m_fBtSpeedV;
	m_pEntity->GetPhysics()->Action(&am);

	// do override gravity for the paraglider
	IPhysicalEntity *pEnt=m_pEntity->GetPhysics();
	pe_simulation_params sp;					
	sp.gravity.zero();
	sp.gravityFreefall.zero();
	sp.damping=m_pGliderDamping->GetFVal()*4.0f;
	sp.dampingFreefall=m_pGliderDamping->GetFVal();
	m_pEntity->GetPhysics()->SetParams(&sp);

	//don't use water/float with physics
	pe_params_buoyancy pb;
	pb.waterDensity = 0.0f;
	m_pEntity->GetPhysics()->SetParams(&pb);
}

//////////////////////////////////////////////////////////////////////////
// not-real-physics boat
void CVehicle::ProcessMovementBoat2(CXEntityProcessingCmd &cmd, float velScale  )
{	
bool	bCanBreak = (velScale>=0);

	if(velScale<0)
		velScale = 1;

	IPhysicalWorld *pWorld = m_pGame->GetSystem()->GetIPhysicalWorld();
	int iCurTime = pWorld->GetiPhysicsTime();
	float dt = (iCurTime - m_iPrevMoveTime)*pWorld->GetPhysVars()->timeGranularity;	
	m_iPrevMoveTime = iCurTime;
	if( dt>.1f )
		dt = .1f;

	if(!m_pEntity->GetPhysics())
		return;
	IPhysicalEntity *iboat = GetEntity()->GetPhysics();

	// to prevent undesired input
	if(velScale>1.0f)
		velScale = 1.0f;
	if(velScale<.1f)
		velScale = .1f;

	bool	bCanTurn	= (m_btVelocity>m_BoatParams.m_fBtSpeedTurnMin);
	float	fMovementImpuls	= m_BoatParams.m_fBtSpeedV*velScale;
	float	fTurnImpuls	= m_BoatParams.m_fBtTurn - m_btVelocity*m_BoatParams.m_fBtTurnSpeedScale;
	
	if(fTurnImpuls < m_BoatParams.m_fBtTurnMin)
		fTurnImpuls = m_BoatParams.m_fBtTurnMin;

	if( (cmd.CheckAction(ACTION_MOVEMODE) || cmd.CheckAction(ACTION_MOVEMODE_TOGGLE)) && bCanBreak )
	{
		fTurnImpuls = m_BoatParams.m_fBtTurn*2;
		bCanTurn = true;
	}

	//get the engine pos, from where to apply the impulse
	//GetEntity()->GetHelperPosition("engine",m_vEnginePos);	

	Vec3 vWaterFlowSpeed(0,0,0);
 
	// FIXME
	if (!m_btInWater)
	{
		if (m_Type!=VHT_PARAGLIDER)
			return;
	}
	else
	if (m_Type==VHT_PARAGLIDER)
		return;

	//define impulse action
	pe_status_pos spos;
	iboat->GetStatus(&spos);
	pe_action_impulse	control;
	control.iSource = 3; // this will mark impulse as non-scheduleable in fixed timestep multiplayer
	Vec3 angles = Ang3::GetAnglesXYZ(matrix3x3f(spos.q))*gf_RADTODEG;
	Vec3	turn = Vec3( 0.0f, 1.0f, 0.0f );
	Vec3 momentum;
	Vec3	tdir = angles;
	Vec3	dir;
	pe_status_dynamics	dyn;

	iboat->GetStatus( &dyn );
	Vec3 refdir(0,-1,0);
	Matrix44 tm;
	tm.SetIdentity();
	//tm.RotateMatrix_fix( angles );
	tm=Matrix44::CreateRotationZYX(-angles*gf_DEGTORAD)*tm; //NOTE: angles in radians and negated 
	tdir = GetTransposed44(tm)*(refdir);

	dir = -tdir;
	if (m_Type!=VHT_PARAGLIDER)	
	{
		dir.z = 0;
		dir.Normalize();
	}

	tdir = dir;
	tdir *= 10.0f;

	control.momentum = Vec3(0.0f, 0.0f, 0.0f);
	control.impulse = Vec3(0.0f, 0.0f, 0.0f);

	if (cmd.CheckAction(ACTION_MOVE_FORWARD))
	{
		control.impulse = dir*fMovementImpuls*dt;		
	}
	else 
	if (cmd.CheckAction(ACTION_MOVE_BACKWARD))
	{
		if(bCanBreak)
			control.impulse = -dir*fMovementImpuls*dt;
		else
			control.impulse = -dir*fMovementImpuls*.3f*dt;
	}

	if (cmd.CheckAction(ACTION_MOVE_RIGHT) && bCanTurn)
	{
		control.momentum = -Vec3(	-dir.x*m_btVelocity*m_BoatParams.m_fBtTitlTurn*dt,
								-dir.y*m_btVelocity*m_BoatParams.m_fBtTitlTurn*dt,
								fTurnImpuls*dt);
	}
	else 
	if (cmd.CheckAction(ACTION_MOVE_LEFT) && bCanTurn) 
	{
		control.momentum = Vec3(	-dir.x*m_btVelocity*m_BoatParams.m_fBtTitlTurn*dt,
									-dir.y*m_btVelocity*m_BoatParams.m_fBtTitlTurn*dt,
									fTurnImpuls*dt);
	}
	if( dyn.v*dir<0.0f )		// moving backward - switch turning direction	
	{
		control.momentum.z = -control.momentum.z;
	}

	if( cmd.CheckAction(ACTION_WALK) && bCanBreak)	// do breaking
	{
		control.impulse = -dyn.v*m_BoatParams.m_fBtSpeedV*dt*.082f;
		control.impulse.z = 0;
	}
	if( cmd.CheckAction(ACTION_MOVEMODE2) && bCanBreak)	// do backoff
	{
		if(m_btVelocity>.1)
			control.impulse = -dyn.v*m_BoatParams.m_fBtSpeedV*dt*.82f;
		else
			control.impulse = -dir*m_BoatParams.m_fBtSpeedV*dt*5.0f;
		control.impulse.z = 0;
	}
	iboat->Action(&control);
}

//////////////////////////////////////////////////////////////////////////
// not-real-physics boat
void CVehicle::ProcessMovementParaglider(CXEntityProcessingCmd &cmd )
{	

	IPhysicalWorld *pWorld = m_pGame->GetSystem()->GetIPhysicalWorld();
	int iCurTime = pWorld->GetiPhysicsTime();
	float dt = (iCurTime - m_iPrevMoveTime)*pWorld->GetPhysVars()->timeGranularity;	

	m_iPrevMoveTime = iCurTime;
	if( dt>.1f )
		dt = .1f;
	if(!m_pEntity->GetPhysics())
		return;

	float	fMovementImpuls	= m_BoatParams.m_fBtSpeedV;
	float	fTurnImpuls	= m_BoatParams.m_fBtTurn;//*m_btVelocity*.05f;
	bool	bCanTurn	= (m_btVelocity>m_BoatParams.m_fBtSpeedTurnMin);

	// get the engine pos, from where to apply the impulse
	//	GetEntity()->GetHelperPosition("engine",m_vEnginePos);	

	// define impulse action
	pe_action_impulse	control;
	Vec3 angles = m_pEntity->GetAngles();
	Vec3	turn = Vec3( 0.0f, 1.0f, 0.0f );
	Vec3 momentum;
	Vec3	tdir = angles;
	Vec3	dir;
	pe_status_dynamics	dyn;
	
	m_pEntity->GetPhysics()->GetStatus( &dyn );

	Vec3 refdir(0,-1,0);
	Matrix44 tm;
	tm.SetIdentity();
	//tm.RotateMatrix_fix( angles );
	tm=Matrix44::CreateRotationZYX(-angles*gf_DEGTORAD)*tm; //NOTE: angles in radians and negated 
	tdir = GetTransposed44(tm)*(refdir);

	dir = -tdir;
	tdir = dir;
	tdir *= 10.0f;

	control.momentum = Vec3(0.0f, 0.0f, 0.0f);
	control.impulse = Vec3(0.0f, 0.0f, 0.0f);

	//drive the glider
	IPhysicalEntity *iboat = GetEntity()->GetPhysics();

	if (cmd.CheckAction(ACTION_MOVE_FORWARD))
	{
		// go down when pressing forward
		Vec3 momentum = (-tdir)^Vec3(0.0f, 0.0f, m_BoatParams.m_fBtTitlSpd);
		control.momentum = vectorf(momentum*dt);
	}
	else 
	if (cmd.CheckAction(ACTION_MOVE_BACKWARD))
	{
		// can't fly back when using the paraglider, but rather
		// go up
		Vec3 momentum = (tdir)^Vec3(0.0f, 0.0f, m_BoatParams.m_fBtTitlSpd);
		control.momentum = vectorf(momentum*dt);
	}

	// to prevent rolling over
	bCanTurn = fabs(dyn.w.z)<.4f;

	if (cmd.CheckAction(ACTION_MOVE_RIGHT) && bCanTurn)
	{

		control.momentum = -Vec3(	-dir.x*m_btVelocity*m_BoatParams.m_fBtTitlTurn*dt,
								-dir.y*m_btVelocity*m_BoatParams.m_fBtTitlTurn*dt,
								fTurnImpuls*dt);		
	}
	else 
	if (cmd.CheckAction(ACTION_MOVE_LEFT) && bCanTurn) 
	{
		control.momentum = Vec3(	-dir.x*m_btVelocity*m_BoatParams.m_fBtTitlTurn*dt,
									-dir.y*m_btVelocity*m_BoatParams.m_fBtTitlTurn*dt,
									fTurnImpuls*dt);
	}
	iboat->Action(&control);
}

//////////////////////////////////////////////////////////////////////////
void CVehicle::ProcessMovement(CXEntityProcessingCmd &cmd)
{
	m_cmdLastMove = cmd;

	if (m_Type == VHT_BOAT)
	{
		ProcessMovementBoat2( cmd );
		return;
	}
	if (m_Type == VHT_PARAGLIDER)
	{
		ProcessMovementParaglider( cmd );
		return;
	}

	//drive the car
	IPhysicalEntity *icar = GetEntity()->GetPhysics();
	if(!icar)	// not phisycalized
	{
		m_pGame->GetSystem()->GetILog()->Log("\002 WARNING car is NOT phisycalized [ %s ]", m_pEntity->GetName());
		return;
	}

	// brakes off - will switch on if breaking
	m_bBreakLightsOn = false;

	pe_action_drive drive;
	pe_status_vehicle status;
	float maxsteer;

	icar->GetStatus(&status);

	IPhysicalWorld *pWorld = m_pGame->GetSystem()->GetIPhysicalWorld();
	int iCurTime = pWorld->GetiPhysicsTime();
	float dt = (iCurTime - m_iPrevMoveTime)*pWorld->GetPhysVars()->timeGranularity;	
	m_iPrevMoveTime = iCurTime;

	if (m_bAcceleratedLastUpdate)
		m_bAcceleratedLastUpdate = false;

	m_bIsBreaking = false; //cmd.CheckAction(ACTION_JUMP);

	if (cmd.CheckAction(ACTION_MOVE_FORWARD))
	{
		if (m_DirVelDotProduct>1.0f)
		{
			m_bIsBreaking = true;
		}
		else
		{
			drive.bHandBrake = 0;
			// use damage model here 
			// [filippo]: begin to use damage model when vehicles have half the health.
			drive.dpedal = m_fPedalSpeed*dt*min(m_fEngineHealth/50.0f,1.0f); 
		}

		if (!m_bAcceleratedFlagSetLastFrame)
		{
			m_bAcceleratedLastUpdate = true;
			m_bAcceleratedFlagSetLastFrame = true;
		}
	}
	else if (cmd.CheckAction(ACTION_MOVE_BACKWARD))
	{
		//if we are trying to brake with footbrake use in any case the handbrake

		//TODO? make this a parameter of the vehicle?
		if (m_DirVelDotProduct<-1.0f)
		{
			m_bIsBreaking = true;
		}
		else
		{
			drive.bHandBrake = 0;
			//use damage model here, [filippo]: begin to use damage model when vehicles have half the health.
			drive.dpedal = -m_fPedalSpeed*dt*min(m_fEngineHealth/50.0f,1.0f); 
			m_bBreakLightsOn = true;
		}
	}
	else
	{
		if(drive.pedal!=1){
		
			drive.bHandBrake = 0;
			drive.pedal = 0;
	
		m_bAcceleratedFlagSetLastFrame = false;
		}
	}

	if (cmd.CheckAction( ACTION_WALK) || !HasDriver() || cmd.CheckAction(ACTION_JUMP))
		m_bIsBreaking = true;
	
	drive.steer = status.steer;
	float vel = status.vel.len();
	maxsteer = m_fv0MaxSteer + m_fkvMaxSteer*vel;
	maxsteer *= (float)(gf_PI)/180.0f;
	float maxpedal = 1.0f;
	if (status.vel.len2()>sqr(m_fPedalLimitSpeed))
		maxpedal -= fabs_tpl(status.steer)/maxsteer*(1.0f-m_fMaxSteeringPedal);
	if (status.pedal>maxpedal)
		drive.pedal = maxpedal;
	else if (status.pedal<-maxpedal)
		drive.pedal = -maxpedal;

	// rotate the steering wheel (if any)
	int nSlot=m_pEntity->GetSteeringWheelSlot();
	if (nSlot>=0)
	{
		// set the correct position 
		// since the steering wheel is at pos. 0,0,0
		Vec3 vCurrAngles;
		m_pEntity->GetObjectAngles(nSlot,vCurrAngles);
		vCurrAngles.y=drive.steer*180;

		float deltaa = vCurrAngles.y-m_flastwheelrotation;

		//if the delta is above 180 deg snap it to the correct angles, otherwise could be a crazy rotating wheel in few cases.
		if (fabs(deltaa)<180)
		{
			m_flastwheelrotation += deltaa*min(dt*5.0f,1.0f);//5 degree at sec
			vCurrAngles.y = m_flastwheelrotation;
		}
		else
			m_flastwheelrotation = vCurrAngles.y;

		Vec3 vRotPointObjSpace;
		m_pEntity->GetHelperPosition("steering_pivot",vRotPointObjSpace,true);
		m_pEntity->SetObjectPos(nSlot,vRotPointObjSpace);		
		m_pEntity->SetObjectAngles(nSlot,vCurrAngles);
	}

	//get the normalized speed delta, between 0-30
	float speeddelta = vel;
	if (speeddelta > 30.0f) speeddelta = 30.0f;
	speeddelta /= 30.0f;

	//use a steerspeed between min and max velocity values
	float steerdelta = m_fSteerSpeed - m_fSteerSpeedMin;
	float	curSteerSpeed = m_fSteerSpeedMin + steerdelta * speeddelta;
	//the same per the steerscale
	float steerscaledelta = m_fsteerspeed_scale - m_fsteerspeed_scale_min;
	float sensitivitybyspeed = m_fsteerspeed_scale_min + steerscaledelta * speeddelta;

	//GetISystem()->GetILog()->Log("steerSpeed:%f,steer:%f",curSteerSpeed*sensitivitybyspeed,drive.steer);	

	//the second condition because we want steering to go from left to right quickly, so in this case we use steerrelaxation.
	if (cmd.CheckAction(ACTION_MOVE_RIGHT) && drive.steer>-0.1f)
	{
		drive.steer += curSteerSpeed*((float)(gf_PI)/180.0f)*dt*sensitivitybyspeed;
		if (drive.steer > maxsteer)
			drive.steer = maxsteer;
	} 
	//the second condition because we want steering to go from left to right quickly, so in this case we use steerrelaxation.
	else if (cmd.CheckAction(ACTION_MOVE_LEFT) && drive.steer<0.1f) 
	{
		drive.steer -= curSteerSpeed*((float)(gf_PI)/180.0f)*dt*sensitivitybyspeed;
		if (drive.steer < -maxsteer)
			drive.steer = -maxsteer;
	}
	else 
	{
		// get the delta necessary to put the steer in the center position, and change the steer in smooth way.
		float deltaa = 0-drive.steer;
		drive.steer += deltaa*min(dt*m_fv0SteerRelaxation*gf_PI/180.0f,1.0f);
	}

	pe_params_car pc;
	pc.axleFriction = m_fAxleFriction;
	icar->SetParams(&pc);


	if (m_bIsBreaking)
	{
		drive.bHandBrake = 1;	// handbrake brake always
		m_bBreakLightsOn = true;
	}

	icar->Action(&drive);	
}

//////////////////////////////////////////////////////////////////////////
void CVehicle::GetWheelStatus(int nWheel, pe_status_wheel *pStatus)
{
	if (!pStatus)
		return;
	IPhysicalEntity *icar = GetEntity()->GetPhysics();
	if (!icar)
		return;
	pStatus->iWheel=nWheel;
	icar->GetStatus(pStatus);
}

//////////////////////////////////////////////////////////////////////////
void CVehicle::SetDrivingParams(float pedalspeed,float steerspeed,float v0maxsteer,float kvmaxsteer,float v0steerrelax,float kvsteerrelax,
																float brake_vel_threshold,float brake_axle_friction,
																float steerspeedVelScale, float steerspeedMin,
																float maxSteeringPedal, float pedalLimitSpeed, 
																float fhandbrakingvalue,float fmaxbrakingfrictionnodriver,
																float fhandbrakingvaluenodriver,float fstabilizejump,
																float fsteerspeedscale, float fsteerspeedscalemin)
{
	m_fPedalSpeed = pedalspeed; m_fSteerSpeed = steerspeed; m_fv0MaxSteer = v0maxsteer; m_fkvMaxSteer = kvmaxsteer;
	m_fv0SteerRelaxation = v0steerrelax; m_fkvSteerRelaxation = kvsteerrelax;
	m_fBrakeVelThreshold = brake_vel_threshold; m_fBrakeAxleFriction = brake_axle_friction;
	m_fSteerSpeedVelSCale = steerspeedVelScale;
	m_fSteerSpeedMin = steerspeedMin;
	m_fMaxSteeringPedal = maxSteeringPedal;
	m_fPedalLimitSpeed = pedalLimitSpeed;
	m_fhandbraking_value = fhandbrakingvalue;
	m_fhandbraking_value_nodriver = fhandbrakingvaluenodriver;
	m_fstabilizejump = fstabilizejump;
	m_fMaxBrakingFrictionNoDriver = 1.0;
	m_fsteerspeed_scale = fsteerspeedscale;
	m_fsteerspeed_scale_min = fsteerspeedscalemin;

	IPhysicalEntity *icar = NULL;

	if (GetEntity())
		icar = GetEntity()->GetPhysics();

	if (icar)
	{
		pe_params_car pc;
		icar->GetParams(&pc);
		m_fAxleFriction = pc.axleFriction;

		//
		m_fMaxBrakingFriction = pc.maxBrakingFriction;

		if (fmaxbrakingfrictionnodriver<=0)// if there is no fmaxbrakingfrictionnodriver specified use the same friction in any case
			m_fMaxBrakingFrictionNoDriver = m_fMaxBrakingFriction;
		else
			m_fMaxBrakingFrictionNoDriver = fmaxbrakingfrictionnodriver;

		//and at first time set the car to nodriver friction.
		pc.maxBrakingFriction = m_fMaxBrakingFrictionNoDriver;		
		icar->SetParams(&pc);

		m_bUsingNoDriverFriction = true;
	}
}

//////////////////////////////////////////////////////////////////////////
bool CVehicle::QueryContainerInterface(ContainerInterfaceType desired_interface, void **ppInterface )
{
	if (desired_interface == CIT_IVEHICLE)
	{
		*ppInterface = (void *) this;
		return true;
	}
	else
	{
		*ppInterface = 0;
		return false;
	}
}

//////////////////////////////////////////////////////////////////////////
bool CVehicle::Write(CStream& stm,EntityCloneState *cs)
{
	if(m_pEntity->IsHidden())
		return true;

	if (!cs || cs->m_bOffSync)
	{
		unsigned char ucHealth = (int)(m_fEngineHealth*2+0.5f);
		stm.Write(ucHealth);
		if (m_pGame->UseFixedStep() && (m_Type == VHT_BOAT))
		{
			stm.Write(true);
			stm.Write(m_fPrevFwvTilt);
			stm.Write(m_fPrevFwvSpeedLen);
			stm.Write(m_fSimTime);
		}
		else
			stm.Write(false);

		if (cs && !cs->m_bLocalplayer)
		{
			stm.Write(true);
			stm.Write(m_cmdLastMove.CheckAction(ACTION_MOVE_FORWARD));
			stm.Write(m_cmdLastMove.CheckAction(ACTION_MOVE_BACKWARD));
			stm.Write(m_cmdLastMove.CheckAction(ACTION_MOVE_LEFT));
			stm.Write(m_cmdLastMove.CheckAction(ACTION_MOVE_RIGHT));
			stm.Write(m_cmdLastMove.CheckAction(ACTION_WALK));
			stm.Write(m_cmdLastMove.CheckAction(ACTION_JUMP));
		}
		else
			stm.Write(false);
	}

	IScriptSystem *pScScriptSystem=m_pGame->GetScriptSystem();
	m_ScriptStream.Attach(&stm);
	pScScriptSystem->BeginCall(GetEntity()->GetEntityClassName(),"OnWrite");
	pScScriptSystem->PushFuncParam(GetEntity()->GetScriptObject());
	pScScriptSystem->PushFuncParam(m_ScriptStream.GetScriptObject());
	pScScriptSystem->EndCall();

	stm.Write(m_bHeadLightsOn);

	return true;
}

//////////////////////////////////////////////////////////////////////////
bool CVehicle::Read(CStream& stm)
{

	if(m_pEntity->IsHidden())
		return true;

	IPhysicalEntity *icar = GetEntity()->GetPhysics();
	pe_params_flags pf;
	icar->GetParams(&pf);
	if (!(pf.flags & pef_checksum_received))
	{
		unsigned char ucHealth;
		stm.Read(ucHealth);
		m_fEngineHealth =  ucHealth*0.5f;
		bool bnz; stm.Read(bnz);
		if (bnz)
		{
			stm.Read(m_fPrevFwvTilt);
			stm.Read(m_fPrevFwvSpeedLen);
			stm.Read(m_fSimTime);
		}
		if ((m_Type == VHT_BOAT)) 
		{
			pe_status_dynamics sd;
			m_pEntity->GetPhysics()->GetStatus( &sd );
			m_btVelocity = sd.v.len();
		}
		
		stm.Read(bnz);
		if (bnz)
		{
			m_cmdLastMove.Reset();
			stm.Read(bnz); if (bnz)
				m_cmdLastMove.AddAction(ACTION_MOVE_FORWARD);
			stm.Read(bnz); if (bnz)
				m_cmdLastMove.AddAction(ACTION_MOVE_BACKWARD);
			stm.Read(bnz); if (bnz)
				m_cmdLastMove.AddAction(ACTION_MOVE_LEFT);
			stm.Read(bnz); if (bnz)
				m_cmdLastMove.AddAction(ACTION_MOVE_RIGHT);
			stm.Read(bnz); if (bnz)
				m_cmdLastMove.AddAction(ACTION_WALK);
			stm.Read(bnz); if (bnz)
				m_cmdLastMove.AddAction(ACTION_JUMP);
		}

		UpdateCamera(0,true);
	}

	IScriptSystem *pScScriptSystem=m_pGame->GetScriptSystem();
	m_ScriptStream.Attach(&stm);
	pScScriptSystem->BeginCall(GetEntity()->GetEntityClassName(),"OnRead");
	pScScriptSystem->PushFuncParam(GetEntity()->GetScriptObject());
	pScScriptSystem->PushFuncParam(m_ScriptStream.GetScriptObject());
	pScScriptSystem->EndCall();

	if(icar && m_pGame && !m_pGame->IsMultiplayer())	// not phisycalized
	{	// [Anton] - don't put vehicles on brake during network synchronization!

		if ((m_Type == VHT_BOAT) || m_Type == VHT_PARAGLIDER)
		{
			pe_action_set_velocity control;
			icar->Action(&control);
		}
		else
		{
			pe_action_drive drive;
			drive.pedal = 0;
			drive.bHandBrake = 1;
			icar->Action(&drive);
		}
	}

	stm.Read( m_bHeadLightsOn );

	return true;
}

//////////////////////////////////////////////////////////////////////////
void CVehicle::SetEngineHealth(float fEngineHealth, bool bScheduled)
{
	if (m_pGame->IsMultiplayer() && m_pGame->UseFixedStep() && !bScheduled)
	{
		if (m_pGame->IsServer())
			m_pGame->ScheduleEvent(-1, m_pEntity,fEngineHealth);
		return;
	}
	m_fEngineHealth = fEngineHealth;
}

//////////////////////////////////////////////////////////////////////////
void CVehicle::SetWaterVehicleParameters(	WATER_VEHICLE_PARAMS &wvpar, bool bFlyingVehicle)
{
	m_BoatParams = wvpar;

	if (bFlyingVehicle)
		m_Type = VHT_PARAGLIDER;
	else
		m_Type = VHT_BOAT;
}


//////////////////////////////////////////////////////////////////////////
bool	CVehicle::HasDriver()
{
	return (m_DriverID!=0);
}

//////////////////////////////////////////////////////////////////////////
void	CVehicle::WakeupPhys( )
{	
	// wake up always when visible or used - otherwise some weird stuff happen - boats don't move
	if (GetEntity()->GetPhysics())
	{
		pe_action_awake aa;
		aa.bAwake = 1;
		GetEntity()->GetPhysics()->Action(&aa);
		m_bPhysAwaike = true;
	}
}

//////////////////////////////////////////////////////////////////////////
void	CVehicle::AddUser(int entId)
{
	WakeupPhys( );
	m_UsersList.push_back(entId);
}

//////////////////////////////////////////////////////////////////////////
void	CVehicle::RemoveUser( int entId )
{
UsersList::iterator	itr = std::find(m_UsersList.begin(), m_UsersList.end(), entId );
	if( itr != m_UsersList.end() )
		m_UsersList.erase( itr );
}

//////////////////////////////////////////////////////////////////////////
void CVehicle::PreloadInstanceResources(Vec3d vPrevPortalPos, float fPrevPortalDistance, float fTime)
{
	// #pragma message( "Warning: Preloading of render resources is not implemented in " __FUNCTION__ )
	// for all objects what will be used for rendering call 
	// ICryCharInstance::PreloadResources or
	// IStatObj::PreloadResources
	// Ask Vladimir for details
}

//////////////////////////////////////////////////////////////////////////
void	CVehicle::InitHeadLight( const char* sImg, const char* sShader )
{
	SAFE_DELETE( m_pHeadLight );

	if(sImg && sImg[0])		// no texture - no light!!!
	{
		m_pHeadLight = new CDLight();
		m_pHeadLight->m_fAnimSpeed = 0;
		int nFlags2 = FT2_FORCECUBEMAP;
		m_pHeadLight->m_pLightImage = m_pGame->GetSystem()->GetIRenderer()->EF_LoadTexture(sImg, 0, nFlags2, eTT_Cubemap);
		m_pHeadLight->m_Flags = DLF_PROJECT | DLF_IGNORE_OWNER;

		if(sShader && sShader[0])
			m_pHeadLight->m_pShader = m_pGame->GetSystem()->GetIRenderer()->EF_LoadShader((char*)sShader, eSH_World);
	}
}

//////////////////////////////////////////////////////////////////////////
void	CVehicle::InitFakeLight( CDLight** pLight, const char* sShader )
{
	if (*pLight)
		delete *pLight;

	if(sShader && sShader[0])		// no shader - no light!!!
	{
	*pLight = new CDLight();
		(*pLight)->m_Flags = DLF_POINT|DLF_FAKE | DLF_IGNORE_OWNER;
		(*pLight)->m_pShader = m_pGame->GetSystem()->GetIRenderer()->EF_LoadShader((char*)sShader, eSH_World);
	}
}

//////////////////////////////////////////////////////////////////////////
void	CVehicle::UpdateLights( )
{
	if(m_fEngineHealth<1)	// no lights when destroyed
		return;

	if(m_bAutoLights && m_bAIDriver)
	{
		if(!HasDriver() )
			return;
		m_bHeadLightsOn = true;
	}

	// adding the light
	if(m_bHeadLightsOn)
	{
		if(m_pHeadLight)
		{
			m_pHeadLight->m_fLightFrustumAngle = 45;
			m_pHeadLight->m_fRadius = 50;

			m_pEntity->GetHelperPosition(m_HeadLightHelper.c_str(), m_pHeadLight->m_Origin );
			m_pHeadLight->m_ProjAngles = Vec3d(m_pEntity->GetAngles().y,m_pEntity->GetAngles().x,m_pEntity->GetAngles().z+90);
			
			m_pHeadLight->m_Color = CFColor(1.f,1.f,1.f, 1.0f);
			m_pHeadLight->m_SpecColor = CFColor(1.f,1.f,1.f);	

			m_pHeadLight->m_Flags = DLF_PROJECT | DLF_LIGHTSOURCE | DLF_IGNORE_OWNER;
			m_pGame->GetSystem()->GetI3DEngine()->AddDynamicLightSource(*m_pHeadLight, GetEntity());
		}

		// flares
		UpdateFakeLight(m_pHeadLightLeft, m_HeadLightHelperLeft.c_str());
		UpdateFakeLight(m_pHeadLightRight, m_HeadLightHelperRight.c_str());
	}

	if( !HasDriver() )
		return;
	
	// adding flares on backlights if breaking
	if(m_bBreakLightsOn)
	{
		UpdateFakeLight(m_pBreakLightLeft, m_BackLightHelperLeft.c_str());
		UpdateFakeLight(m_pBreakLightRight, m_BackLightHelperRight.c_str());
	}
}

//////////////////////////////////////////////////////////////////////////
void	CVehicle::UpdateFakeLight( CDLight* light, const char* sHelper )
{

	if( light == NULL ) return;
	light->m_fLightFrustumAngle = 30;
	light->m_fRadius = 5;

	m_pEntity->GetHelperPosition(sHelper, light->m_Origin);
	light->m_ProjAngles = Vec3d(m_pEntity->GetAngles().y,m_pEntity->GetAngles().x,m_pEntity->GetAngles().z+90);
	
	light->m_Color = CFColor(1.f,1.f,1.f, 1.0f);
	light->m_SpecColor = CFColor(1.f,1.f,1.f);

	light->m_Flags = DLF_FAKE | DLF_LIGHTSOURCE | DLF_PROJECT | DLF_IGNORE_OWNER;

	m_pGame->GetSystem()->GetI3DEngine()->AddDynamicLightSource(*light, GetEntity(), light->m_nEntityLightId);
}

//////////////////////////////////////////////////////////////////////////
void	CVehicle::SwitchLights( int light )
{
	if(light == 1)
		m_bHeadLightsOn = true;
	else if(light == 0)
		m_bHeadLightsOn = false;
	else
		m_bHeadLightsOn = !m_bHeadLightsOn;
}

//////////////////////////////////////////////////////////////////////////
float CVehicle::GetLightRadius()
{
	if(m_pHeadLight && m_bHeadLightsOn)
		return m_pHeadLight->m_fRadius;
	
	return 0;
}

//////////////////////////////////////////////////////////////////////////
void CVehicle::GetFirePosAngles(Vec3d& firePos, Vec3d& fireAngles)
{
	firePos = m_vWpnPos;
	fireAngles = m_vWpnAng;
	return;
}

//////////////////////////////////////////////////////////////////////////
const string& CVehicle::GetWeaponName( CPlayer::eInVehiclestate state )
{
	switch(state)
	{
	case CPlayer::PVS_DRIVER:
		return m_sAutoWeaponName;
	case CPlayer::PVS_GUNNER:
		return m_sMountedWeaponName;
	}
	return m_sNoWeaponName;
}

//////////////////////////////////////////////////////////////////////////
void CVehicle::SetCameraParams(Vec3 vCamStiffness[2],Vec3 vCamLimits[2],float fCamDamping,float fMaxCamTimestep,
															 float fCamSnapDist,float fCamSnapVel)
{
	m_vCamStiffness[0] = vCamStiffness[0];
	m_vCamStiffness[1] = vCamStiffness[1];
	m_vCamLimits[0] = vCamLimits[0];
	m_vCamLimits[1] = vCamLimits[1];
  m_fCamDamping = fCamDamping;
	m_fMaxCamTimestep = fMaxCamTimestep;
	m_fCamSnapDist = fCamSnapDist;
	m_fCamSnapVel = fCamSnapVel;
}

//////////////////////////////////////////////////////////////////////////
void CVehicle::ResetCamera(bool bUpdateCamera,const char *pHelperName)
{
	if (bUpdateCamera && GetEntity()->GetPhysics())
	{
		pe_status_pos sp;
		pe_status_dynamics sd;
		GetEntity()->GetPhysics()->GetStatus(&sp);
		GetEntity()->GetPhysics()->GetStatus(&sd);
		GetEntity()->GetHelperPosition(pHelperName,m_vCamHelperPos,true);	
		m_vPrevCamTarget = m_vCamPos = sp.pos + sp.q*m_vCamHelperPos;
		m_vCamVel = sd.v + (sd.w^m_vCamPos-sd.centerOfMass);
		m_bUpdateCamera = true;
	}
	else
		m_bUpdateCamera = false;
}

//////////////////////////////////////////////////////////////////////////
void CVehicle::UpdateCamera(float fTimeStep, bool bSynchronizing)
{
	if (m_bUpdateCamera)
	{
		pe_status_pos sp;
		GetEntity()->GetPhysics()->GetStatus(&sp);
		Vec3 posTarget,posTargetNew,posDelta,posDeltaLoc,posDeltaLocNorm,velTarget;
		Matrix33 R = Matrix33(sp.q);
		posTargetNew = sp.pos + R*m_vCamHelperPos;

		if (!bSynchronizing)
		{
			if (fTimeStep==0 || (velTarget = (posTargetNew-m_vPrevCamTarget)/fTimeStep).len2()>sqr(200) || 
					m_vCamStiffness[0].len2()+m_vCamStiffness[1].len2()==0)
			{
				m_vCamPos = posTargetNew;
				pe_status_dynamics sd;
				GetEntity()->GetPhysics()->GetStatus(&sd);
				m_vCamVel = sd.v + (sd.w^m_vCamPos-sd.centerOfMass);
			}
			else
			{
				float ks,fStep;
				int i,j;

				posTarget = m_vPrevCamTarget;
				do 
				{
					fStep = min(fTimeStep,m_fMaxCamTimestep);
					posTarget += velTarget*fStep;
					m_vCamPos += m_vCamVel*fStep;

					if ((posDelta = m_vCamPos-posTarget).len2()>sqr(m_fCamSnapDist))
					{
						posDeltaLocNorm = (posDeltaLoc=posDelta*R).GetNormalized();
						for(i=0,ks=0; i<3; i++)
						{
							j = isneg(posDeltaLoc[i])^1;
							if (m_vCamStiffness[j][i]==0)
								m_vCamPos -= R.GetColumn(i)*posDeltaLoc[i];
							else if (fabs_tpl(posDeltaLoc[i])>m_vCamLimits[j][i])
								m_vCamPos -= R.GetColumn(i)*(posDeltaLoc[i]+m_vCamLimits[j][i]*(j*2-1));
							ks += sqr(posDeltaLocNorm[i])*m_vCamStiffness[j][i];
						}
						//m_vCamVel -= posDelta*((ks + 2.0f*sqrt_tpl(ks)*m_fCamDamping*((posDelta*(m_vCamVel-velTarget))/posDelta.len2()))*fStep);
						m_vCamVel -= posDelta*(ks*fStep);
						m_vCamVel += (velTarget-m_vCamVel)*(m_fCamDamping*fStep);
					}
					else if ((m_vCamVel-velTarget).len2()<sqr(m_fCamSnapVel))
					{
						m_vCamPos = posTarget;
						m_vCamVel = velTarget;
					}
				}	while ((fTimeStep-=fStep)>0.0001f);
			}
			m_vPrevCamTarget = posTargetNew;
		}
		else
		{
			m_vCamPos += (posTargetNew-m_vPrevCamTarget);
			m_vPrevCamTarget = posTargetNew;
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CVehicle::WeaponState(int userId, bool shooting, int fireMode)
{
CPlayer* theShooter = GetUserInState( CPlayer::PVS_PASSENGER);
	
	// it's a passenger - has it's own weapon, don't affect vehicle's weapon
	if( theShooter )	
		return;

	if(shooting)
	{
		// use appropriate fire animation, depending on fire mode (MG/rockets)
		if(fireMode == 0)
			m_pEntity->StartAnimation(0, "default" );
	}
	else
	{
		theShooter = GetUserInState( CPlayer::PVS_GUNNER );
		if(theShooter)
			if(theShooter->GetEntity()->GetId()!=userId)	
				// there is still gunner in - don't stop ani
				return;
		ICryCharInstance *pCharacter = m_pEntity->GetCharInterface()->GetCharacter(0);
		if(pCharacter)
			pCharacter->StopAnimation(0);
	}
}

//////////////////////////////////////////////////////////////////////////
//[filippo]
void CVehicle::AdditionalPhysics(IPhysicalEntity *pcar,float fdelta,bool bforcebreaking)
{
	if (pcar==NULL || m_fEngineHealth<=0)
		return;

	pe_status_vehicle vstatus;
	pe_status_dynamics dstatus;

	pcar->GetStatus(&vstatus);
	pcar->GetStatus(&dstatus);

	float fvelmod = dstatus.v.len();

	if (vstatus.bWheelContact>1)
	{
		float handbrake;
		pe_action_set_velocity action;

		if (m_bUsingNoDriverFriction)
			handbrake = m_fhandbraking_value_nodriver;
		else
			handbrake = m_fhandbraking_value;

		if ((m_bIsBreaking||bforcebreaking||vstatus.bHandBrake) && fvelmod>0 && handbrake>0 
				&& vstatus.nActiveColliders==0) // [Anton] don't force unaccounted for excessive damping when contacting with other
																				// objects, for ex. platform in the boat puzzle
		{		

			action.v[0] = dstatus.v[0];
			action.v[1] = dstatus.v[1];
			action.v[2] = dstatus.v[2];
						
			float fnewvel = fvelmod - handbrake*fdelta;
			
			if (fnewvel<=0)
				fnewvel = 0;
			
			action.v = action.v / fvelmod * fnewvel;

			pcar->Action(&action);
		}
	}
	else if (m_fstabilizejump>0) // flying? try to stabilize the rolling		
	{
		Matrix44 tm;
		tm.SetIdentity();
		tm = Matrix44::CreateRotationZYX(-m_pEntity->GetAngles()*gf_DEGTORAD)*tm; //NOTE: angles in radians and negated 
			
		Vec3 vUpDir = GetTransposed44(tm)*Vec3d(0,0,1);
		Vec3 vFwdDir = GetTransposed44(tm)*Vec3d(0,-1,0);
		
		if (vUpDir.z > 0.5f) 
		{
			Vec3 vStabilizedangles = m_pEntity->GetAngles();
			vStabilizedangles.y = 0;

			tm.SetIdentity();
			tm = Matrix44::CreateRotationZYX(-vStabilizedangles*gf_DEGTORAD)*tm; //NOTE: angles in radians and negated 
			Vec3 vert = GetTransposed44(tm)*Vec3d(0,0,1);
				
			pe_action_impulse am;						

			float coef = m_fstabilizejump*fdelta;
			Vec3 dv = vert^vUpDir;
			coef *= 1.0f + dv.len2();

			am.momentum.x = -(dv.x+dstatus.w.x)*coef;
			am.momentum.y = -(dv.y+dstatus.w.y)*coef;
			am.momentum.z = 0;
			
			pcar->Action(&am);
		}		
	}
}

//////////////////////////////////////////////////////////////////////////
void CVehicle::SaveAIState(CStream & stm, CScriptObjectStream & scriptStream)
{

	IAIObject *pObject = m_pEntity->GetAI();
	if (pObject)		
		pObject->Save(stm);

	IScriptSystem *pScriptSystem = m_pGame->GetSystem()->GetIScriptSystem();
	HSCRIPTFUNCTION	saveOverallFunction=NULL;
	if( m_pEntity->GetScriptObject() && m_pEntity->GetScriptObject()->GetValue("OnSaveOverall", saveOverallFunction) )
	{
		pScriptSystem->BeginCall(saveOverallFunction);
		pScriptSystem->PushFuncParam(m_pEntity->GetScriptObject());
		pScriptSystem->PushFuncParam(scriptStream.GetScriptObject());
		pScriptSystem->EndCall();
	}
}

//////////////////////////////////////////////////////////////////////////
void CVehicle::LoadAIState(CStream & stm, CScriptObjectStream & scriptStream)
{

	IAIObject *pObject = m_pEntity->GetAI();
	if (pObject)
		pObject->Load(stm);

	IScriptSystem *pScriptSystem = m_pGame->GetSystem()->GetIScriptSystem();
	HSCRIPTFUNCTION	saveOverallFunction=NULL;
	if( m_pEntity->GetScriptObject() && m_pEntity->GetScriptObject()->GetValue("OnLoadOverall", saveOverallFunction) )
	{
		pScriptSystem->BeginCall(saveOverallFunction);
		pScriptSystem->PushFuncParam(m_pEntity->GetScriptObject());
		pScriptSystem->PushFuncParam(scriptStream.GetScriptObject());
		pScriptSystem->EndCall();
	}
}

//////////////////////////////////////////////////////////////////////////
void CVehicle::OnEntityNetworkUpdate( const EntityId &idViewerEntity, const Vec3d &v3dViewer, uint32 &inoutPriority, 
	EntityCloneState &inoutCloneState) const
{
	IEntity *pLocPlayerEnt = GetISystem()->GetIEntitySystem()->GetEntity(idViewerEntity);

	if(pLocPlayerEnt)		// if there is a local player
	{
		IEntityContainer *pCnt = pLocPlayerEnt->GetContainer();

		if(pCnt)
		{
			CPlayer *pLocPlayer;

			// if this vehicle has the player as passanger
			if(pCnt->QueryContainerInterface(CIT_IPLAYER,(void **)&pLocPlayer))		// and it's a player
			{
				CVehicle *pVehiclepLocPlayer = pLocPlayer->GetVehicle();

				if(pVehiclepLocPlayer && pVehiclepLocPlayer->GetEntity()->GetId()==m_pEntity->GetId())	// that is inside this vehicle
					inoutPriority += 350000; // boost the vehicle the player rides in
			}
		}
	}
}

