//------------------------------------------------------------------------
void CVehicleMovementStdBoat::UpdateRunSound(const float deltaTime)
{
	Vec3 localAccel(ZERO);
	m_measureSpeedTimer+=deltaTime;

	if (m_measureSpeedTimer > 0.25f)
	{
		const Vec3& v = m_physStatus[k_mainThread].v;
		Vec3 accel = (v - m_lastMeasuredVel) * (1.f/m_measureSpeedTimer);
		localAccel = accel * m_physStatus[k_mainThread].q;

		m_lastMeasuredVel = v;
		m_measureSpeedTimer = 0.f;
	}

	if (m_pVehicle->IsProbablyDistant())
		return;

  // rpm dropdown for waves
  if (m_rpmPitchDir != 0)    
  { 
    float speed = (m_rpmPitchDir > 0) ? 0.1f : -0.8f; // quick down, slow up
    m_waveSoundPitch += deltaTime * speed;

    if (m_waveSoundPitch < -m_waveSoundAmount) // dropdown amount
    {
      m_waveSoundPitch = -m_waveSoundAmount;
      m_rpmPitchDir = 1;
    }      
    else if (m_waveSoundPitch > 0.f)
    {
      m_waveSoundPitch = 0.f;
      m_rpmPitchDir = 0;
    }
  }

  if (m_rpmPitchSpeed>0.f)
  {    
    const float maxPedal = (!m_inWater) ? 1.f : Boosting() ? 0.8f : 0.7f;

    // pitch rpm with pedal          
    float pedal = GetEnginePedal();
    pedal = sgnnz(pedal)*max(ms_engineSoundIdleRatio, min(maxPedal, abs(pedal))); // clamp "pedal" to [0.2..0.7] range

    float delta = pedal - m_rpmScaleSgn;
    m_rpmScaleSgn = max(-1.f, min(1.f, m_rpmScaleSgn + sgn(delta)*min(abs(delta), m_rpmPitchSpeed*deltaTime)));

    // skip transition around 0 when on pedal (sounds more realistic)
    if (abs(GetEnginePedal()) > 0.001f && abs(delta) > 0.001f && sgn(m_rpmScaleSgn) != sgn(delta) && abs(m_rpmScaleSgn) <= 0.3f)
      m_rpmScaleSgn = sgn(delta)*0.3f;

    // for normal driving, rpm is clamped at max defined by sound dept
    m_rpmScale = abs(m_rpmScaleSgn);
    m_rpmScale = min(1.f, max(ms_engineSoundIdleRatio, m_rpmScale + m_waveSoundPitch));

		m_pIEntityAudioProxy->SetRtpcValue(m_audioControlIDs[eSID_VehicleRPM], m_rpmScale);
  }
}
//////////////////////////////////////////////////////////////////////////
// NOTE: This function must be thread-safe. Before adding stuff contact MarcoC.
void CVehicleMovementTank::ProcessMovement(const float deltaTime)
{ 
	FUNCTION_PROFILER( gEnv->pSystem, PROFILE_GAME );

	m_netActionSync.UpdateObject(this);

	CryAutoCriticalSection lk(m_lock);

	CVehicleMovementBase::ProcessMovement(deltaTime);

	if (!(m_actorId && m_isEnginePowered))
	{
		IPhysicalEntity* pPhysics = GetPhysics();

		if (m_latFriction != 1.3f)
			SetLatFriction(1.3f);

		if (m_axleFriction != m_axleFrictionMax)
			UpdateAxleFriction(0.f, false, deltaTime);

		m_action.bHandBrake = 1;
		m_action.pedal = 0;
		m_action.steer = 0;
		pPhysics->Action(&m_action, 1);
		return;
	}

	IPhysicalEntity* pPhysics = GetPhysics();
	MARK_UNUSED m_action.clutch;

	Matrix34 worldTM( m_PhysPos.q );
	worldTM.AddTranslation( m_PhysPos.pos );

	const Matrix34 invWTM = worldTM.GetInvertedFast();

	Vec3 localVel = invWTM.TransformVector(m_PhysDyn.v);
	Vec3 localW = invWTM.TransformVector(m_PhysDyn.w);
	float speed = m_PhysDyn.v.len();
	float speedRatio = min(1.f, speed/m_maxSpeed);

	float actionPedal = abs(m_movementAction.power) > 0.001f ? m_movementAction.power : 0.f;        

	// tank specific:
	// avoid steering input around 0.5 (ask Anton)
	float actionSteer = m_movementAction.rotateYaw;
	float absSteer = abs(actionSteer);
	float steerSpeed = (absSteer < 0.01f && abs(m_currSteer) > 0.01f) ? m_steerSpeedRelax : m_steerSpeed;

	if (steerSpeed == 0.f)
	{
		m_currSteer =	(float)sgn(actionSteer);
	}
	else
	{ 
		if (m_movementAction.isAI)
		{
			m_currSteer = actionSteer;
		}
		else
		{
			m_currSteer += min(abs(actionSteer-m_currSteer), deltaTime*steerSpeed) * sgn(actionSteer-m_currSteer);        
		}
	}
	Limit(m_currSteer, -m_steerLimit, m_steerLimit);  

	if (abs(m_currSteer) > 0.0001f) 
	{
		// if steering, apply full throttle to have enough turn power    
		actionPedal = (float)sgn(actionPedal);

		if (actionPedal == 0.f) 
		{
			// allow steering-on-teh-spot only above maxReverseSpeed (to avoid sudden reverse of controls)
			const float maxReverseSpeed = -1.5f;
			actionPedal = max(0.f, min(1.f, 1.f-(localVel.y/maxReverseSpeed)));

			// todo
			float steerLim = 0.8f;
			Limit(m_currSteer, -steerLim*m_steerLimit, steerLim*m_steerLimit);
		}
	}

	if (!pPhysics->GetStatus(&m_vehicleStatus))
		return;

	int currGear = m_vehicleStatus.iCurGear - 1; // indexing for convenience: -1,0,1,2,..

	UpdateAxleFriction(m_movementAction.power, true, deltaTime);
	UpdateSuspension(deltaTime);   	

	float absPedal = abs(actionPedal);  

	// pedal ramping   
	if (m_pedalSpeed == 0.f)
		m_currPedal = actionPedal;
	else
	{
		m_currPedal += deltaTime * m_pedalSpeed * sgn(actionPedal - m_currPedal);  
		m_currPedal = clamp_tpl(m_currPedal, -absPedal, absPedal);
	}

	// only apply pedal after threshold is exceeded
	if (currGear == 0 && fabs_tpl(m_currPedal) < m_pedalThreshold) 
		m_action.pedal = 0;
	else
		m_action.pedal = m_currPedal;

	// change pedal amount based on damages
	float damageMul = 0.0f;
	{
		if (m_movementAction.isAI)
		{
			damageMul = 1.0f - 0.30f * m_damage; 
			m_action.pedal *= damageMul;
		}
		else
		{
			// request from Sten: damage shouldn't affect reversing so much.
			float effectiveDamage = m_damage;
			if(m_action.pedal < -0.1f)
				effectiveDamage = 0.4f * m_damage;

			m_action.pedal *= GetWheelCondition();
			damageMul = 1.0f - 0.7f*effectiveDamage; 
			m_action.pedal *= damageMul;
		}
	}

	// reverse steering value for backward driving
	float effSteer = m_currSteer * sgn(actionPedal);   

	// update lateral friction  
	float latSlipMinGoal = 0.f;
	float latFricMinGoal = m_latFricMin;

	if (abs(effSteer) > 0.01f && !m_movementAction.brake)
	{
		latSlipMinGoal = m_latSlipMin;

		// use steering friction, but not when countersteering
		if (sgn(effSteer) != sgn(localW.z))
			latFricMinGoal = m_latFricMinSteer;
	}

	Interpolate(m_currentSlipMin, latSlipMinGoal, 3.f, deltaTime);   

	if (latFricMinGoal < m_currentFricMin)
		m_currentFricMin = latFricMinGoal;
	else
		Interpolate(m_currentFricMin, latFricMinGoal, 3.f, deltaTime);

	float fractionSpeed = min(1.f, max(0.f, m_avgLateralSlip-m_currentSlipMin) / (m_latSlipMax-m_currentSlipMin));
	float latFric = fractionSpeed * (m_latFricMax-m_currentFricMin) + m_currentFricMin;

	if ( m_movementAction.brake && m_movementAction.isAI )
	{
		// it is natural for ai, apply differnt friction value while handbreaking
		latFric = m_latFricMax;
	}

	if (latFric != m_latFriction)
	{ 
		SetLatFriction(latFric);    
	}      

	const static float maxSteer = gf_PI/4.f; // fix maxsteer, shouldn't change  
	m_action.steer = m_currSteer * maxSteer;  

	if (m_steeringImpulseMin > 0.f && m_wheelContactsLeft != 0 && m_wheelContactsRight != 0)
	{  
		const float maxW = 0.3f*gf_PI;
		float steer = abs(m_currSteer)>0.001f ? m_currSteer : 0.f;    
		float desired = steer * maxW; 
		float curr = -localW.z;
		float err = desired - curr; // err>0 means correction to right 
		Limit(err, -maxW, maxW);

		if (abs(err) > 0.01f)
		{ 
			float amount = m_steeringImpulseMin + speedRatio*(m_steeringImpulseMax-m_steeringImpulseMin);

			// bigger correction for relaxing
			if (desired == 0.f || (desired*curr>0 && abs(desired)<abs(curr))) 
				amount = m_steeringImpulseRelaxMin + speedRatio*(m_steeringImpulseRelaxMax-m_steeringImpulseRelaxMin);

			float corr = -err * amount * m_PhysDyn.mass * deltaTime;

			pe_action_impulse imp;
			imp.iApplyTime = 0;      
			imp.angImpulse = worldTM.GetColumn2() * corr;
			pPhysics->Action(&imp, THREAD_SAFE);
		}    
	}

	m_action.bHandBrake = (m_movementAction.brake) ? 1 : 0;	

	if (currGear > 0 && m_vehicleStatus.iCurGear < m_currentGear) 
	{
		// when shifted down, disengage clutch immediately to avoid power/speed dropdown
		m_action.clutch = 1.f;
	}

	pPhysics->Action(&m_action, 1);

	if (Boosting())  
		ApplyBoost(speed, 1.2f*m_maxSpeed*GetWheelCondition()*damageMul, m_boostStrength, deltaTime);  

	if (m_wheelContacts <= 1 && speed > 5.f)
	{
		ApplyAirDamp(DEG2RAD(20.f), DEG2RAD(10.f), deltaTime, THREAD_SAFE);
		UpdateGravity(-9.81f * 1.4f);
	}

	if (m_netActionSync.PublishActions( CNetworkMovementStdWheeled(this) ))
		CHANGED_NETWORK_STATE(m_pVehicle,  eEA_GameClientDynamic );
}
//------------------------------------------------------------------------
void CVehicleMovementHelicopter::ProcessActionsLift(float deltaTime)
{
	Ang3 angles = m_pEntity->GetWorldAngles();
	//	const float& currentPitch = angles.x;
	const float &currentRoll = angles.y;

	float boost = Boosting() ? m_boostMult : 1.0f;

	float workingUpDirZ = max(0.90f, min(1.0f, m_workingUpDir.z));
	float pitchRatio = (workingUpDirZ - 0.95f) / 0.05f;
	float liftAction = 0.0f;

	if(pitchRatio > -0.0001f)
		liftAction = m_liftAction * sinf(pitchRatio * gf_PI * 0.5f);

	float fwdAction = 0.0f;

	if(pitchRatio < -0.0001f)
		fwdAction = m_liftAction * cosf(pitchRatio * gf_PI * 0.5f);

	if(m_liftAction < 0.0f && pitchRatio < 0.0f)
	{
		liftAction = m_liftAction * (-max(-1.0f, pitchRatio) * 0.5f);
		fwdAction = 0.0f;
	}

	m_noHoveringTimer = 0.0f;

	float gravity;
	pe_simulation_params paramsSim;

	if(GetPhysics()->GetParams(&paramsSim))
		gravity = abs(paramsSim.gravity.z);
	else
		gravity = 9.8f;

	// hovering force

	m_control.impulse += Vec3(m_workingUpDir.x, m_workingUpDir.y, min(1.0f, m_workingUpDir.z)) * gravity * (boost);
	m_control.impulse += m_workingUpDir * m_enginePower * gravity * liftAction * boost;

	// forward force

	m_control.impulse += Vec3(m_workingUpDir.x, m_workingUpDir.y, 0.0f) * gravity * m_enginePower * fwdAction;

	if(abs(m_desiredRoll) > 0.0001)
	{
		float side = 1.0f;

		if(currentRoll < 0.0f)
			side = -1.0f;
		else if(currentRoll > 0.0f)
			side = 1.0f;
		else
			side = 0.0f;

		m_control.impulse += m_currentLeftDir * m_enginePower * (m_liftAction + (m_desiredRoll * side)) * boost;
	}

	// force damping

	float horizDamp = m_velDamp;
	float vertDamp = 1.0f;

	if(Boosting())
		horizDamp *= 0.25f;

	float turbulenceMult = 1.0f - min(m_turbulenceMultMax, m_turbulence);
	m_control.impulse.x -= m_PhysDyn.v.x * horizDamp;
	m_control.impulse.y -= m_PhysDyn.v.y * horizDamp;
	m_control.impulse.z -= m_PhysDyn.v.z * vertDamp;

#if ENABLE_VEHICLE_DEBUG
	IActor *pActor = m_pActorSystem->GetActor(m_actorId);

	int profile = g_pGameCVars->v_profileMovement;

	if((profile == 1 && pActor && pActor->IsClient()) || profile == 2)
	{
		IRenderer *pRenderer = gEnv->pRenderer;
		float color[4] = {1,1,1,1};

		Ang3 localAngles = m_pEntity->GetWorldAngles();

		pRenderer->Draw2dLabel(5.0f, 350.0f, 1.5f, color, false, "pitchRatio: %f,  liftAction: %f,  fwdAction: %f", pitchRatio, liftAction, fwdAction);
	}

#endif
}
//------------------------------------------------------------------------
void CVehicleMovementHelicopter::ProcessActions(const float deltaTime)
{
	FUNCTION_PROFILER(GetISystem(), PROFILE_GAME);

	UpdateDamages(deltaTime);
	UpdateEngine(deltaTime);

	m_velDamp = 0.0f;

	m_playerControls.ProcessActions(deltaTime);

	Limit(m_forwardAction, -1.0f, 1.0f);
	Limit(m_strafeAction, -1.0f, 1.0f);

	m_actionYaw = 0.0f;

	Matrix33 tm(m_PhysPos.q);
	Ang3 angles = Ang3::GetAnglesXYZ(tm);
	Vec3 worldPos =  m_PhysPos.pos;

	// +ve pitch means nose up
	const float &currentPitch = angles.x;
	// +ve roll means to the left
	const float &currentRoll = angles.y;
	// +ve direction mean rotation anti-clockwise about the z axis - 0 means along y
	float currentDir = angles.z;

	float pitchDeg = RAD2DEG(currentPitch);

	if(m_maxPitchAngleMov != 0.0f && pitchDeg >= (m_maxPitchAngleMov * 0.5f))
	{
		float mult = pitchDeg / (m_maxPitchAngleMov);

		if(mult > 1.0f && m_desiredPitch < 0.0f)
		{
			m_desiredPitch *= 0.0f;
			m_actionPitch *= 0.0f;
			m_desiredPitch += 0.5f * mult;
		}
		else if(m_desiredPitch < 0.0f)
		{
			m_desiredPitch *= (1.0f - mult);
			m_desiredPitch += 0.05f;
		}
	}
	else if(m_maxPitchAngleMov != 0.0f && pitchDeg <= (-m_maxPitchAngleMov * 0.5f))
	{
		float mult = abs(pitchDeg) / (m_maxPitchAngleMov);

		if(mult > 1.0f && m_desiredPitch > 0.0f)
		{
			m_desiredPitch *= 0.0f;
			m_actionPitch *= 0.0f;
			m_desiredPitch += 0.5f * mult;
		}
		else if(m_desiredPitch > 0.0f)
		{
			m_desiredPitch *= (1.0f - mult);
			m_desiredPitch -= 0.05f;
		}
	}

	if(m_pInvertPitchVar->GetIVal() == 0)
		m_desiredPitch *= -1.0f;

	Vec3 currentVel = m_PhysDyn.v;
	Vec3 currentVel2D = currentVel;
	currentVel2D.z = 0.0f;

	if(currentRoll >= DEG2RAD(m_maxRollAngle * 0.5f) && m_desiredRoll > 0.001f)
	{
		float r = currentRoll / DEG2RAD(m_maxRollAngle);
		r = min(1.0f, r * 1.0f);
		r = 1.0f - r;
		m_desiredRoll *= r;
		m_desiredRoll = min(1.0f, m_desiredRoll);
	}
	else if(currentRoll <= DEG2RAD(-m_maxRollAngle * 0.5f) && m_desiredRoll < 0.001f)
	{
		float r = abs(currentRoll) / DEG2RAD(m_maxRollAngle);
		r = min(1.0f, r * 1.0f);
		r = 1.0f - r;
		m_desiredRoll *= r;
		m_desiredRoll = max(-1.0f, m_desiredRoll);
	}

	Vec3 currentFwdDir2D = m_currentFwdDir;
	currentFwdDir2D.z = 0.0f;
	currentFwdDir2D.NormalizeSafe();

	Vec3 currentLeftDir2D(-currentFwdDir2D.y, currentFwdDir2D.x, 0.0f);

	currentVel2D.z = 0.0f;

	float currentHeight = worldPos.z;
	float currentFwdSpeed = currentVel.Dot(currentFwdDir2D);

	ProcessActions_AdjustActions(deltaTime);

	float inputMult = m_basicSpeedFraction;

	// desired things
	float turnDecreaseScale = m_yawDecreaseWithSpeed / (m_yawDecreaseWithSpeed + fabs(currentFwdSpeed));

	Vec3 desired_vel2D =
		currentFwdDir2D * m_forwardAction * m_maxFwdSpeed * inputMult +
		currentLeftDir2D * m_strafeAction * m_maxLeftSpeed * inputMult;

	// calculate the angle changes

	Vec3 desiredVelChange2D = desired_vel2D - currentVel2D;

	float desiredTiltAngle = m_tiltPerVelDifference * desiredVelChange2D.GetLength();
	Limit(desiredTiltAngle, -m_maxTiltAngle, m_maxTiltAngle);

	float goal = abs(m_desiredPitch) + abs(m_desiredRoll);
	goal *= 1.5f;
	Interpolate(m_playerAcceleration, goal, 0.25f, deltaTime);
	Limit(m_playerAcceleration, 0.0f, 5.0f);

	if(!iszero(m_desiredPitch))
	{
		m_actionPitch -= m_desiredPitch * m_pitchInputConst;
		Limit(m_actionPitch, -m_maxYawRate, m_maxYawRate);
	}

	m_actionRoll += m_pitchActionPerTilt * m_desiredRoll * (m_playerAcceleration + 1.0f);
	Limit(m_actionRoll, -10.0f, 10.0f);
	Limit(m_actionPitch, -10.0f, 10.0f);

	float relaxRollTolerance = 0.0f;

	if(!iszero(m_turnAction) || abs(m_PhysDyn.w.z) > DEG2RAD(10.0f))
	{
		m_actionYaw += -m_turnAction * m_yawInputConst * GetDamageMult();

		float side = 0.0f;

		if(abs(m_turnAction) > 0.01f)
			side = min(1.0f, max(-1.0f, m_turnAction));

		float roll = DEG2RAD(m_extraRollForTurn * side) - (currentRoll);
		m_actionRoll += max(0.0f, abs(roll)) * side * m_rollForTurnForce;

		float pitchComp = abs(currentPitch) / DEG2RAD(2.50f);

		if(pitchComp > 1.0f)
			roll *= pitchComp;

		roll *= max(1.0f, abs(m_PhysDyn.w.z));

		m_actionRoll += roll;

		Limit(m_actionYaw, -m_maxYawRate, m_maxYawRate);
	}

	m_desiredDir = currentDir;
	m_lastDir = currentDir;

	float boost = Boosting() ? m_boostMult : 1.0f;

	if(m_pAltitudeLimitVar)
	{
		float altitudeLimit = m_pAltitudeLimitVar->GetFVal();

		if(!iszero(altitudeLimit))
		{
			float altitudeLowerOffset;

			if(m_pAltitudeLimitLowerOffsetVar)
			{
				float r = 1.0f - min(1.0f, max(0.0f, m_pAltitudeLimitLowerOffsetVar->GetFVal()));
				altitudeLowerOffset = r * altitudeLimit;
			}
			else
				altitudeLowerOffset = altitudeLimit;

			float mult = 1.0f;

			if(currentHeight >= altitudeLimit)
			{
				if(m_liftAction > 0.f)
				{
					mult = 0.0f;
				}
			}
			else if(currentHeight >= altitudeLowerOffset)
			{
				float zone = altitudeLimit - altitudeLowerOffset;
				mult = (altitudeLimit - currentHeight) / (zone);
			}

			m_liftAction *= mult;

			if(currentPitch > DEG2RAD(0.0f))
			{
				if(m_forwardAction > 0.0f)
					m_forwardAction *= mult;

				if(m_actionPitch > 0.0f)
				{
					m_actionPitch *= mult;
					m_actionPitch += -currentPitch;
				}
			}

			m_desiredHeight = min(altitudeLowerOffset, currentHeight);
		}
	}
	else
	{
		m_desiredHeight = currentHeight;
	}

	ProcessActionsLift(deltaTime);

	if(m_pStabilizeVTOL)
	{
		float stabilizeTime = m_pStabilizeVTOL->GetFVal();

		if(stabilizeTime > 0.0f)
		{
			if(m_relaxTimer < 6.0f)
				m_relaxTimer += deltaTime;
			else
			{
				float r = currentRoll - relaxRollTolerance;
				r = min(1.0f, max(-1.0f, r));

				m_actionRoll += -r * m_relaxForce * (m_relaxTimer / 6.0f);
			}
		}
	}

	if(m_netActionSync.PublishActions(CNetworkMovementHelicopter(this)))
		CHANGED_NETWORK_STATE(m_pVehicle, eEA_GameClientDynamic);
}
//------------------------------------------------------------------------
void CVehicleMovementHelicopter::Update(const float deltaTime)
{
	FUNCTION_PROFILER(GetISystem(), PROFILE_GAME);

	CVehicleMovementBase::Update(deltaTime);

	CryAutoCriticalSection lk(m_lock);

	if(m_isTouchingGround)
	{
		m_timeOnTheGround += deltaTime;
		m_isTouchingGround = false;
	}
	else
	{
		m_timeOnTheGround = 0.0f;
	}

	// ai specific sound matter

	if(m_soundMasterVolume != m_vehicleVolume)
	{
		float vol = m_soundMasterVolume;

		if(m_vehicleVolume == 0)
			CVehicleMovementBase::SetSoundMasterVolume(m_vehicleVolume);
		else if(vol < m_vehicleVolume)
		{
			vol +=deltaTime;

			if(vol > m_vehicleVolume)
				vol = m_vehicleVolume;

			CVehicleMovementBase::SetSoundMasterVolume(vol);
		}
		else if(vol > m_vehicleVolume)
		{
			vol -=deltaTime;

			if(vol < m_vehicleVolume)
				vol = m_vehicleVolume;

			CVehicleMovementBase::SetSoundMasterVolume(vol);
		}
	}

	// update animation

	if(m_isEngineGoingOff)
	{
		if(m_enginePower > 0.0f)
		{
			UpdateEngine(deltaTime);
		}
		else
		{
			m_enginePower = 0.0f;
		}
	}

	SetAnimationSpeed(eVMA_Engine, (m_enginePower / m_enginePowerMax));

#if ENABLE_VEHICLE_DEBUG
	IActor *pActor = m_pActorSystem->GetActor(m_actorId);

	int profile = g_pGameCVars->v_profileMovement;

	if((profile == 1 && pActor && pActor->IsClient()) || profile == 2)
	{
		IRenderer *pRenderer = gEnv->pRenderer;
		float color[4] = {1,1,1,1};

		Ang3 localAngles = m_pEntity->GetWorldAngles();

		m_mass = m_statusDyn.mass;
		Vec3 &velocity = m_statusDyn.v;
		Vec3 &angVelocity = m_statusDyn.w;

		pRenderer->Draw2dLabel(5.0f,   0.0f, 2.0f, color, false, "Helicopter movement");
		Vec3 i;
		i = m_control.impulse.GetNormalizedSafe();
		pRenderer->Draw2dLabel(5.0f,  85.0f, 1.5f, color, false, "impulse: %f, %f, %f (%f, %f, %f)", m_control.impulse.x, m_control.impulse.y, m_control.impulse.z, i.x, i.y, i.z);
		pRenderer->Draw2dLabel(5.0f, 100.0f, 1.5f, color, false, "angImpulse: %f, %f, %f", m_control.angImpulse.x, m_control.angImpulse.y, m_control.angImpulse.z);
		i = velocity.GetNormalizedSafe();
		pRenderer->Draw2dLabel(5.0f, 115.0f, 1.5f, color, false, "velocity: %f, %f, %f (%f) (%f, %f, %f)", velocity.x, velocity.y, velocity.z, velocity.GetLength(), i.x, i.y, i.z);
		pRenderer->Draw2dLabel(5.0f, 130.0f, 1.5f, color, false, "angular velocity: %f, %f, %f", RAD2DEG(angVelocity.x), RAD2DEG(angVelocity.y), RAD2DEG(angVelocity.z));
		pRenderer->Draw2dLabel(5.0f, 160.0f, 1.5f, color, false, "angles: %f, %f, %f (%f, %f, %f)", RAD2DEG(localAngles.x), localAngles.y, localAngles.z, RAD2DEG(localAngles.x), RAD2DEG(localAngles.y), RAD2DEG(localAngles.z));
		pRenderer->Draw2dLabel(5.0f, 175.0f, 1.5f, color, false, "m_rpmScale: %f, damage: %f, damageActual: %f, turbulence: %f", m_rpmScale, m_damage, m_damageActual, m_turbulence);
		pRenderer->Draw2dLabel(5.0f, 190.0f, 1.5f, color, false, "m_turnAction: %f, actionYaw: %f, targetRotation: %f, %f, %f", m_turnAction, m_actionYaw, RAD2DEG(m_rotateTarget.x), RAD2DEG(m_rotateTarget.y), RAD2DEG(m_rotateTarget.z));
		pRenderer->Draw2dLabel(5.0f, 220.0f, 1.5f, color, false, "lift: %f, engineForce: %f, hoveringPower: %f, desiredHeight: %f, boost: %d, fwdAction: %f", m_liftAction, m_engineForce, m_hoveringPower, m_desiredHeight, Boosting(), m_forwardAction);
		pRenderer->Draw2dLabel(5.0f, 235.0f, 1.5f, color, false, "pitchAction:  %f, rollAction:  %f", m_actionPitch, m_actionRoll);
		pRenderer->Draw2dLabel(5.0f, 250.0f, 1.5f, color, false, "desiredPitch: %f, desiredRoll: %f", m_desiredPitch, m_desiredRoll);

		Vec3 direction = m_pEntity->GetWorldTM().GetColumn(1);
		pRenderer->Draw2dLabel(5.0f, 270.0f, 1.5f, color, false, "fwd direction: %.2f, %.2f, %.2f", direction.x, direction.y, direction.z);
		pRenderer->Draw2dLabel(5.0f, 285.0f, 1.5f, color, false, "workingUpDir:  %.2f, %.2f, %.2f", m_workingUpDir.x, m_workingUpDir.y, m_workingUpDir.z);
		pRenderer->Draw2dLabel(5.0f, 300.0f, 1.5f, color, false, "accel:  %f", m_playerAcceleration);
	}

#endif
}
//------------------------------------------------------------------------
void CVehicleMovementVTOL::ProcessActions(const float deltaTime)
{
	FUNCTION_PROFILER( GetISystem(), PROFILE_GAME );

	UpdateDamages(deltaTime);
	UpdateEngine(deltaTime);

	m_velDamp = 0.25f;

	m_playerControls.ProcessActions(deltaTime);

	Limit(m_forwardAction, -1.0f, 1.0f);
	Limit(m_strafeAction, -1.0f, 1.0f);

	m_actionYaw = 0.0f;

	Vec3 worldPos = m_pEntity->GetWorldPos();

	IPhysicalEntity* pPhysics = GetPhysics();

	// get the current state

	// roll pitch + yaw

	Matrix34 worldTM = m_pRotorPart ? m_pRotorPart->GetWorldTM() : m_pEntity->GetWorldTM();
//	if (m_pRotorPart)
//		worldTM = m_pRotorPart->GetWorldTM();
//	else
//		worldTM = m_pEntity->GetWorldTM();

	Vec3 specialPos = worldTM.GetTranslation();
	Ang3 angles = Ang3::GetAnglesXYZ(Matrix33(worldTM));

	Matrix33 tm;
	tm.SetRotationXYZ((angles));

	// +ve pitch means nose up
	const float& currentPitch = angles.x;
	// +ve roll means to the left
	const float& currentRoll = angles.y;
	// +ve direction mean rotation anti-clockwise about the z axis - 0 means along y
	float currentDir = angles.z;

	const float maxPitchAngle = 60.0f;
	
	float pitchDeg = RAD2DEG(currentPitch);
	if (pitchDeg >= (maxPitchAngle * 0.75f))
	{
		float mult = pitchDeg / (maxPitchAngle);
		
		if (mult > 1.0f && m_desiredPitch < 0.0f)
		{
			m_desiredPitch *= 0.0f;
			m_actionPitch *= 0.0f;
			m_desiredPitch += 0.2f * mult;
		}
		else if (m_desiredPitch < 0.0f)
		{
			m_desiredPitch *= (1.0f - mult);
			m_desiredPitch += 0.05f;
		}
	}
	else if (pitchDeg <= (-maxPitchAngle * 0.75f))
	{
		float mult = abs(pitchDeg) / (maxPitchAngle);

		if (mult > 1.0f && m_desiredPitch > 0.0f)
		{
			m_desiredPitch *= 0.0f;
			m_actionPitch *= 0.0f;
			m_desiredPitch += 0.2f * mult;
		}
		else if (m_desiredPitch > 0.0f)
		{
			m_desiredPitch *= (1.0f - mult);
			m_desiredPitch -= 0.05f;
		}
	}

	if (currentRoll >= DEG2RAD(m_maxRollAngle * 0.7f) && m_desiredRoll > 0.001f)
	{
		float r = currentRoll / DEG2RAD(m_maxRollAngle);
		r = min(1.0f, r * 1.0f);
		r = 1.0f - r;
		m_desiredRoll *= r;
		m_desiredRoll = min(1.0f, m_desiredRoll);
	}
	else if (currentRoll <= DEG2RAD(-m_maxRollAngle * 0.7f) && m_desiredRoll < 0.001f)
	{
		float r = abs(currentRoll) / DEG2RAD(m_maxRollAngle);
		r = min(1.0f, r * 1.0f);
		r = 1.0f - r;
		m_desiredRoll *= r;
		m_desiredRoll = max(-1.0f, m_desiredRoll);
	}

	Vec3 currentFwdDir2D = m_currentFwdDir;
	currentFwdDir2D.z = 0.0f;
	currentFwdDir2D.NormalizeSafe();

	Vec3 currentLeftDir2D(-currentFwdDir2D.y, currentFwdDir2D.x, 0.0f);

	Vec3 currentVel = m_PhysDyn.v;
	Vec3 currentVel2D = currentVel;
	currentVel2D.z = 0.0f;

	float currentHeight = worldPos.z;
	float currentFwdSpeed = currentVel.Dot(currentFwdDir2D);

	ProcessActions_AdjustActions(deltaTime);

	float inputMult = m_basicSpeedFraction;

	// desired things
	float turnDecreaseScale = m_yawDecreaseWithSpeed / (m_yawDecreaseWithSpeed + fabs(currentFwdSpeed));

	Vec3 desired_vel2D = 
		currentFwdDir2D * m_forwardAction * m_maxFwdSpeed * inputMult + 
		currentLeftDir2D * m_strafeAction * m_maxLeftSpeed * inputMult;

	// calculate the angle changes

	Vec3 desiredVelChange2D = desired_vel2D - currentVel2D;

	float desiredTiltAngle = m_tiltPerVelDifference * desiredVelChange2D.GetLength();
	Limit(desiredTiltAngle, -m_maxTiltAngle, m_maxTiltAngle);

	float goal = abs(m_desiredPitch) + abs(m_desiredRoll);
	goal *= 1.5f;
	Interpolate(m_playerAcceleration, goal, 0.25f, deltaTime);
	Limit(m_playerAcceleration, 0.0f, 5.0f);

	//static float g_angleLift = 4.0f;

	if (abs(m_liftAction) > 0.001f && abs(m_forwardAction) < 0.001)
	{
//		float pitch = RAD2DEG(currentPitch);

		if (m_liftPitchAngle < 0.0f && m_liftAction > 0.0f)
			m_liftPitchAngle = 0.0f;
		else if (m_liftPitchAngle > 0.0f && m_liftAction < 0.0f)
			m_liftPitchAngle = 0.0f;

		Interpolate(m_liftPitchAngle, 1.25f * m_liftAction, 0.75f, deltaTime);

		if (m_liftPitchAngle < 1.0f && m_liftPitchAngle > -1.0f)
			m_desiredPitch += 0.05f * m_liftAction;
	}
	else if (m_liftAction < 0.001f && abs(m_liftPitchAngle) > 0.001)
	{
		Interpolate(m_liftPitchAngle, 0.0f, 1.0f, deltaTime);
		m_desiredPitch += 0.05f * -m_liftPitchAngle;
	}

	/* todo
	else if (m_liftAction < -0.001f)
	{
		m_desiredPitch += min(0.0f, (DEG2RAD(-5.0f) - currentPitch)) * 0.5f * m_liftAction;
	}*/

	if (!iszero(m_desiredPitch))
	{
		m_actionPitch -= m_desiredPitch * m_pitchInputConst;
		Limit(m_actionPitch, -m_maxYawRate, m_maxYawRate);
	}

	float rollAccel = 1.0f;
	if (abs(currentRoll + m_desiredRoll) < abs(currentRoll))
		rollAccel *= 1.25f;

	m_actionRoll += m_pitchActionPerTilt * m_desiredRoll * rollAccel * (m_playerAcceleration + 1.0f);
	Limit(m_actionRoll, -10.0f, 10.0f);
	Limit(m_actionPitch, -10.0f, 10.0f);

	// roll as we turn
	if (!m_strafeAction)
	{
		m_actionYaw += m_yawPerRoll * currentRoll;
	}

	if (abs(m_strafeAction) > 0.001f)
	{
		float side = 0.0f;
		side = min(1.0f, max(-1.0f, m_strafeAction));

		float roll = DEG2RAD(m_extraRollForTurn * 0.25f * side) - (currentRoll);
		m_actionRoll += max(0.0f, abs(roll)) * side * 1.0f;
	}

	float relaxRollTolerance = 0.0f;

	if (abs(m_turnAction) > 0.01f || abs(m_PhysDyn.w.z) > DEG2RAD(3.0f))
	{
		m_actionYaw += -m_turnAction * m_yawInputConst * GetDamageMult();

		float side = 0.0f;
		if (abs(m_turnAction) > 0.01f)
			side = min(1.0f, max(-1.0f, m_turnAction));

		float roll = DEG2RAD(m_extraRollForTurn * side) - (currentRoll);
		m_actionRoll += max(0.0f, abs(roll)) * side * m_rollForTurnForce;

		roll *= max(1.0f, abs(m_PhysDyn.w.z));

		m_actionRoll += roll;

		Limit(m_actionYaw, -m_maxYawRate, m_maxYawRate);
	}

	m_desiredDir = currentDir;
	m_lastDir = currentDir;

	float boost = Boosting() ? m_boostMult : 1.0f;
	float liftActionMax = 1.0f;

	if (m_pAltitudeLimitVar)
	{
		float altitudeLimit = m_pAltitudeLimitVar->GetFVal();

		if (!iszero(altitudeLimit))
		{
			float altitudeLowerOffset;

			if (m_pAltitudeLimitLowerOffsetVar)
			{
				float r = 1.0f - min(1.0f, max(0.0f, m_pAltitudeLimitLowerOffsetVar->GetFVal()));
				altitudeLowerOffset = r * altitudeLimit;
			}
			else
				altitudeLowerOffset = altitudeLimit;

			float mult = 1.0f;

			if (currentHeight >= altitudeLimit)
			{
				if (m_liftAction > 0.f)
				{
					mult = 0.0f;
				}
			}
			else if (currentHeight >= altitudeLowerOffset)
			{
				float zone = altitudeLimit - altitudeLowerOffset;
				mult = (altitudeLimit - currentHeight) / (zone);
			}

			m_liftAction *= mult;

			if (currentPitch > DEG2RAD(0.0f))
			{
				if (m_forwardAction > 0.0f)
					m_forwardAction *= mult;

				if (m_actionPitch > 0.0f)
				{
					m_actionPitch *= mult;
					m_actionPitch += -currentPitch;
				}
			}

			m_desiredHeight = min(altitudeLowerOffset, currentHeight);
		}
	}
	else
	{
		m_desiredHeight = currentHeight;
	}

	if (abs(m_liftAction) > 0.001f)
	{
		m_liftAction = min(liftActionMax, max(-0.2f, m_liftAction));

		m_hoveringPower = (m_powerInputConst * m_liftAction) * boost;
		m_noHoveringTimer = 0.0f;
	}
	else if (!m_isTouchingGround)
	{
		if (m_noHoveringTimer <= 0.0f)
		{
			float gravity;

			pe_simulation_params paramsSim;
			if (pPhysics->GetParams(&paramsSim))
				gravity = abs(paramsSim.gravity.z);
			else
				gravity = 9.2f;

			float upDirZ = m_workingUpDir.z;

			if (abs(m_forwardAction) > 0.01 && upDirZ > 0.0f)
				upDirZ = 1.0f;
			else if (upDirZ > 0.8f)
				upDirZ = 1.0f;

			float upPower = upDirZ;
			upPower -= min(1.0f, abs(m_forwardAction) * abs(angles.x));

			float turbulenceMult = 1.0f - min(m_turbulenceMultMax, m_turbulence);
			Vec3& impulse = m_control.impulse;
			impulse += Vec3(0.0f, 0.0f, upPower) * gravity * turbulenceMult * GetDamageMult();
			impulse.z -= m_PhysDyn.v.z * turbulenceMult;
		}
		else
		{
			m_noHoveringTimer -= deltaTime;
		}
	}

	if (m_pStabilizeVTOL)
	{
		float stabilizeTime = m_pStabilizeVTOL->GetFVal();

		if (stabilizeTime > 0.0f)
		{
			if (m_relaxTimer < 6.0f)
				m_relaxTimer += deltaTime;
			else
			{
				float r = currentRoll - relaxRollTolerance;
				r = min(1.0f, max(-1.0f, r));

				m_actionRoll += -r * m_relaxForce * (m_relaxTimer / 6.0f);
			}

		}
	}

	if (m_netActionSync.PublishActions( CNetworkMovementHelicopter(this) ))
		m_pVehicle->GetGameObject()->ChangedNetworkState(eEA_GameClientDynamic);
}
//------------------------------------------------------------------------
void CVehicleMovementVTOL::PreProcessMovement(const float deltaTime)
{
	CVehicleMovementHelicopter::PreProcessMovement(deltaTime);

	if (abs(m_forwardAction) > 0.0f && m_timeOnTheGround <= 0.01f)
		SetHorizontalMode(1.0f);
	else
		SetHorizontalMode(0.0f);

	if (m_forwardAction > 0.0f && m_timeOnTheGround <= 0.01f)
	{
		m_wingsTimer += deltaTime;
		m_wingsTimer = min(m_wingsTimer, m_timeUntilWingsRotate);
	}
	else
	{
		m_wingsTimer -= deltaTime * 0.65f;
		m_wingsTimer = max(m_wingsTimer, 0.0f);
	}

	Interpolate(m_wingsAnimTime, 1.0f - (m_wingsTimer / m_timeUntilWingsRotate), m_wingsSpeed, deltaTime);

	if (!m_isVTOLMovement)
		return;
	
	IPhysicalEntity* pPhysics = GetPhysics();
	assert(pPhysics);

	float gravity;
	pe_simulation_params paramsSim;
	if (pPhysics->GetParams(&paramsSim))
		gravity = abs(paramsSim.gravity.z);
	else
		gravity = 9.2f;

	float vertical = 1.0f - m_horizontal;

	//m_engineForce = max(1.0f, gravity * vertical) * m_enginePower * max(0.25f, vertical);
	m_engineForce = 0.0f;
	m_engineForce += gravity * vertical * m_enginePower;
	m_engineForce += m_horizontal * m_enginePower;

	Matrix33 tm( m_PhysPos.q);
	Ang3 angles = Ang3::GetAnglesXYZ(tm);

 	m_workingUpDir = m_engineUpDir; //Vec3(0.0f, 0.0f, 1.0f);
	
	m_workingUpDir += (vertical * m_rotorDiskTiltScale * Vec3(angles.y, -angles.x, 0.0f));
	m_workingUpDir += (m_horizontal * m_rotorDiskTiltScale * Vec3(0.0f, 0.0f, angles.z));

	m_workingUpDir = tm * m_workingUpDir;
	m_workingUpDir.z += 0.25f;
	m_workingUpDir.NormalizeSafe();

	float strafe = m_strafeAction * m_strafeForce;

	if (m_noHoveringTimer <= 0.0f)
	{
		Vec3 forwardImpulse;

		float turbulenceMult = 1.0f - min(m_turbulenceMultMax, m_turbulence);

		forwardImpulse = m_currentFwdDir * m_enginePower * m_horizFwdForce * m_horizontal
			* (m_forwardAction + (Boosting() * m_boostForce)) * GetDamageMult() * turbulenceMult;

		if (m_forwardAction < 0.0f)
			forwardImpulse *= m_forwardInverseMult;

		forwardImpulse += m_currentUpDir * m_liftAction * m_enginePower * gravity;
		Vec3 fakeLeftDir = tm * Vec3(-1.0f, 0.0f, 0.0f);
		fakeLeftDir.z = 0.0f;
		forwardImpulse += fakeLeftDir * -strafe * m_enginePower * m_horizLeftForce * turbulenceMult;

 		float horizDamp = 0.25f;
		static float vertDamp = 0.0f;

		if ( m_movementAction.isAI )
			horizDamp *= abs(m_turnAction * 4.0f) + 1.0f;
		else
			horizDamp = m_velDamp;

 		m_control.impulse += forwardImpulse;
		m_control.impulse.x -= m_PhysDyn.v.x * horizDamp * turbulenceMult;
		m_control.impulse.y -= m_PhysDyn.v.y * horizDamp * turbulenceMult;
		m_control.impulse.z -= m_PhysDyn.v.z * vertDamp * turbulenceMult;
	}

	m_workingUpDir.z += 0.45f * m_liftAction;
	m_workingUpDir.NormalizeSafe();


	return;
}
//////////////////////////////////////////////////////////////////////////
// NOTE: This function must be thread-safe. Before adding stuff contact MarcoC.
void CVehicleMovementStdBoat::ProcessMovement(const float deltaTime)
{  
  FUNCTION_PROFILER( GetISystem(), PROFILE_GAME );

  static const float fWaterLevelMaxDiff = 0.15f; // max allowed height difference between propeller center and water level
  static const float fSubmergedMin = 0.01f;
  static const float fMinSpeedForTurn = 0.5f; // min speed so that turning becomes possible
  
  if (m_bNetSync)
    m_netActionSync.UpdateObject(this);

  CryAutoCriticalSection lk(m_lock);

  CVehicleMovementBase::ProcessMovement(deltaTime);

  IEntity* pEntity = m_pVehicle->GetEntity();
  IPhysicalEntity* pPhysics = pEntity->GetPhysics(); 
  SVehiclePhysicsStatus* physStatus = &m_physStatus[k_physicsThread];
  assert(pPhysics);

  float frameTime = min(deltaTime, 0.1f); 

  if (abs(m_movementAction.power) < 0.001f)
    m_movementAction.power = 0.f;
  if (abs(m_movementAction.rotateYaw) < 0.001f)
    m_movementAction.rotateYaw = 0.f;

  Matrix34 wTM( physStatus->q );
  wTM.AddTranslation( physStatus->pos );

  Matrix34 wTMInv = wTM.GetInvertedFast();
    
  Vec3 localVel = wTMInv.TransformVector( physStatus->v );
  Vec3 localW = wTMInv.TransformVector( physStatus->w );   

  const Vec3 xAxis = wTM.GetColumn0();
  const Vec3 yAxis = wTM.GetColumn1();
  const Vec3 zAxis = wTM.GetColumn2();
  
  // check if propeller is in water
  Vec3 worldPropPos = wTM * m_pushOffset;  
  float waterLevelWorld = gEnv->p3DEngine->GetWaterLevel( &worldPropPos );
  float fWaterLevelDiff = worldPropPos.z - waterLevelWorld;  
  
  bool submerged = physStatus->submergedFraction > fSubmergedMin;
  m_inWater = submerged && fWaterLevelDiff < fWaterLevelMaxDiff;
    
  float speed = physStatus->v.len2() > 0.001f ? physStatus->v.len() : 0.f;    
  float speedRatio = min(1.f, speed/(m_maxSpeed*m_factorMaxSpeed));  
  float absPedal = abs(m_movementAction.power);
  float absSteer = abs(m_movementAction.rotateYaw);
 
  // wave stuff 
  float waveFreq = 1.f;
  waveFreq += 3.f*speedRatio;

  float waveTimerPrev = m_waveTimer;
  m_waveTimer += frameTime*waveFreq;

  // new randomized amount for this oscillation
  if (m_waveTimer >= gf_PI && waveTimerPrev < gf_PI) 
    m_waveRandomMult = cry_random(0.0f, 1.0f);  
  
  if (m_waveTimer >= 2*gf_PI)  
    m_waveTimer -= 2*gf_PI;    

  float kx = m_waveIdleStrength.x*(m_waveRandomMult+0.3f) * (1.f-speedRatio + m_waveSpeedMult*speedRatio);
  float ky = m_waveIdleStrength.y * (1.f - 0.5f*absPedal - 0.5f*absSteer);

  Vec3 waveLoc = m_massOffset;
  waveLoc.y += speedRatio*min(0.f, m_pushOffset.y-m_massOffset.y);
  waveLoc = wTM * waveLoc;

  bool visible = m_pVehicle->GetGameObject()->IsProbablyVisible();
  bool doWave = visible && submerged && physStatus->submergedFraction < 0.99f;
    
  if (doWave && !m_isEnginePowered)
    m_pVehicle->NeedsUpdate(IVehicle::eVUF_AwakePhysics);
  
  if (m_isEnginePowered || (visible && !m_pVehicle->IsProbablyDistant()))
  {
    if (doWave && (m_isEnginePowered || g_pGameCVars->v_rockBoats))
    { 
      pe_action_impulse waveImp;
      waveImp.angImpulse.x = Boosting() ? 0.f : sinf(m_waveTimer) * frameTime * m_Inertia.x * kx;
      
      if (isneg(waveImp.angImpulse.x))
        waveImp.angImpulse.x *= (1.f - min(1.f, 2.f*speedRatio)); // less amplitude for negative impulse      

      waveImp.angImpulse.y = sinf(m_waveTimer-0.5f*gf_PI) * frameTime * m_Inertia.y * ky;  
      waveImp.angImpulse.z = 0.f;
      waveImp.angImpulse = wTM.TransformVector(waveImp.angImpulse);
      waveImp.point = waveLoc;
      if (!m_movementAction.isAI)
	      pPhysics->Action(&waveImp, 1);      
    }
  }
  // ~wave stuff 

	if (!m_isEnginePowered)
		return;

  pe_action_impulse linearImp, angularImp, dampImp, liftImp; 
  float turnAccel = 0, turnAccelNorm = 0;

	if (m_inWater)
	{     
    // Lateral damping
    if (m_lateralDamping>0.f)
    {
    		pe_action_impulse impulse;
    		impulse.impulse = - physStatus->mass * xAxis * (localVel.x * (frameTime * m_lateralDamping)/(1.f + frameTime*m_lateralDamping));
    		pPhysics->Action(&impulse, 1);
    }
    // optional lifting (catamarans)
    if (m_velLift > 0.f)
    {
      if (localVel.y > m_velLift && !IsLifted())
        Lift(true);
      else if (localVel.y < m_velLift && IsLifted())
        Lift(false);
    }

    if (Boosting() && IsLifted())
    {
      // additional lift force      
      liftImp.impulse = Vec3(0,0,physStatus->mass*frameTime*(localVel.y/(m_velMax*m_factorMaxSpeed))*3.f);
      liftImp.point = wTM * m_massOffset;
      pPhysics->Action(&liftImp, 1);
    }
    
    // apply driving force         
    float a = m_movementAction.power;

    if (sgn(a)*sgn(localVel.y) > 0)
    {
      // reduce acceleration with increasing speed
      float ratio = (localVel.y > 0.f) ? localVel.y/(m_velMax*m_factorMaxSpeed) : -localVel.y/(m_velMaxReverse*m_factorMaxSpeed);      
      a = (ratio>1.f) ? 0.f : sgn(a)*min(abs(a), 1.f-((1.f-m_accelVelMax)*sqr(ratio))); 
    }
    
    if (a != 0)
    {
      if (sgn(a) * sgn(localVel.y) < 0) // "braking"
        a *= m_accelCoeff;    
      else
        a = max(a, -m_pedalLimitReverse);

      Vec3 pushDir(FORWARD_DIRECTION);                
      
      // apply force downwards a bit for more realistic response  
      if (a > 0)
        pushDir = Quat_tpl<float>::CreateRotationAA( DEG2RAD(m_pushTilt), Vec3(-1,0,0) ) * pushDir;

      pushDir = wTM.TransformVector( pushDir );  
      linearImp.impulse = pushDir * physStatus->mass * a * m_accel * m_factorAccel * frameTime;

      linearImp.point = m_pushOffset;
      linearImp.point.x = m_massOffset.x;
      linearImp.point = wTM * linearImp.point;
			pPhysics->Action(&linearImp, 1);
    } 
    
    float roll = (float)__fsel(zAxis.z - 0.2f, xAxis.z / (frameTime + frameTime*frameTime), 0.f);		// Roll damping (with a exp. time constant of 1 sec)

    // apply steering           
    if (m_movementAction.rotateYaw != 0)
    { 
      if (abs(localVel.y) < fMinSpeedForTurn){ // if forward speed too small, no turning possible
        turnAccel = 0; 
      }
      else 
      {
        int iDir = m_movementAction.power != 0.f ? sgn(m_movementAction.power) : sgn(localVel.y);
        turnAccelNorm = m_movementAction.rotateYaw * iDir * max(1.f, m_turnVelocityMult * speedRatio);    

        // steering and current w in same direction?
        int sgnSteerW = sgn(m_movementAction.rotateYaw) * iDir * sgn(-localW.z);

        if (sgnSteerW < 0)
        { 
          // "braking"
          turnAccelNorm *= m_turnAccelCoeff; 
        }
        else 
        {    
          // reduce turn vel towards max
          float maxRatio = 1.f - 0.15f*min(1.f, abs(localW.z)/m_turnRateMax);
          turnAccelNorm = sgn(turnAccelNorm) * min(abs(turnAccelNorm), maxRatio);
        }

        turnAccel = turnAccelNorm * m_turnAccel;
        //roll = 0.2f * turnAccel; // slight roll        
      }        
    }   

		// Use the centripetal acceleration to determine the amount of roll
		float centripetalAccel = clamp_tpl(speed * localW.z, -10.f, +10.f);
		roll -= (1.f - 2.f*fabsf(xAxis.z)) * m_rollAccel * centripetalAccel;

		// Always damp rotation!
		turnAccel += localW.z * m_turnDamping;
    
    if (turnAccel != 0)
    {
      Vec3& angImp = angularImp.angImpulse; 
      
      angImp.x = 0.f;
      angImp.y = roll * frameTime * m_Inertia.y;
      angImp.z = -turnAccel * frameTime * m_Inertia.z;      
      
      angImp = wTM.TransformVector( angImp );
      pPhysics->Action(&angularImp, 1);
    }   
    
    if (abs(localVel.x) > 0.01f)  
    { 
      // lateral force         
      Vec3& cornerForce = dampImp.impulse;
      
      cornerForce.x = -localVel.x * m_cornerForceCoeff * physStatus->mass * frameTime;
      cornerForce.y = 0.f;
      cornerForce.z = 0.f;
      
      if (m_cornerTilt != 0)
        cornerForce = Quat_tpl<float>::CreateRotationAA( sgn(localVel.x)*DEG2RAD(m_cornerTilt), Vec3(0,1,0) ) * cornerForce;

      dampImp.impulse = wTM.TransformVector(cornerForce);

      dampImp.point = m_cornerOffset;
      dampImp.point.x = m_massOffset.x;
      dampImp.point = wTM.TransformPoint( dampImp.point );
      pPhysics->Action(&dampImp, 1);         
    }  
  }

  EjectionTest(deltaTime);
  
	if (!m_pVehicle->GetStatus().doingNetPrediction)
	{
		if (m_bNetSync && m_netActionSync.PublishActions( CNetworkMovementStdBoat(this) ))
			CHANGED_NETWORK_STATE(m_pVehicle, CNetworkMovementStdBoat::CONTROLLED_ASPECT );
	}
}
//------------------------------------------------------------------------
void CVehicleMovementStdBoat::Update(const float deltaTime)
{
	CVehicleMovementBase::Update(deltaTime);

	SetAnimationSpeed(eVMA_Engine, abs(m_rpmScaleSgn));
	if (m_inWater)
	{ 
		SetSoundParam(eSID_Run, "slip", 0.2f*abs(m_localSpeed.x)); 
	}

#if ENABLE_VEHICLE_DEBUG
	if (IsProfilingMovement() && g_pGameCVars->v_profileMovement != 2)
	{
		IEntity* pEntity = m_pVehicle->GetEntity();
		const Matrix34& wTM = pEntity->GetWorldTM();  
		Matrix34 wTMInv = wTM.GetInvertedFast();
		
		const SVehiclePhysicsStatus* physStatus = &m_physStatus[k_mainThread];
		Vec3 localW = physStatus->q * physStatus->w;

		float speed = physStatus->v.len2() > 0.001f ? physStatus->v.len() : 0.f;    
		float speedRatio = min(1.f, speed/(m_maxSpeed*m_factorMaxSpeed));  
		float absPedal = abs(m_movementAction.power);
		float absSteer = abs(m_movementAction.rotateYaw);
		
		static const float fSubmergedMin = 0.01f;
		static const float fWaterLevelMaxDiff = 0.15f; // max allowed height difference between propeller center and water level

		Vec3 worldPropPos = wTM * m_pushOffset;  
		float waterLevelWorld = gEnv->p3DEngine->GetWaterLevel( &worldPropPos );
		float fWaterLevelDiff = worldPropPos.z - waterLevelWorld;  

		// wave stuff 
		float waveFreq = 1.f;
		waveFreq += 3.f*speedRatio;

		float kx = m_waveIdleStrength.x*(m_waveRandomMult+0.3f) * (1.f-speedRatio + m_waveSpeedMult*speedRatio);
		float ky = m_waveIdleStrength.y * (1.f - 0.5f*absPedal - 0.5f*absSteer);
		Vec3 waveLoc = m_massOffset;
		waveLoc.y += speedRatio*min(0.f, m_pushOffset.y-m_massOffset.y);
		waveLoc = wTM * waveLoc;

		IRenderer* pRenderer = gEnv->pRenderer;
		static float color[4] = {1,1,1,1};    
		float colorRed[4] = {1,0,0,1};
		float colorGreen[4] = {0,1,0,1};
		float y=50.f, step1=15.f, step2=20.f, size1=1.3f, size2=1.5f;

		pRenderer->Draw2dLabel(5.0f,   y, size2, color, false, "Boat movement");
		pRenderer->Draw2dLabel(5.0f,  y+=step2, size1, color, false, "Speed: %.1f (%.1f km/h)", speed, speed*3.6f);
		pRenderer->Draw2dLabel(5.0f,  y+=step1, size1, color, false, "LocalW.z norm: %.2f", abs(localW.z)/m_turnRateMax);
		if (m_velLift > 0.f)
		{
			pRenderer->Draw2dLabel(5.0f,  y+=step2, size1, m_lifted ? colorGreen : color, false, m_lifted ? "Lifted" : "not lifted");
			//pRenderer->Draw2dLabel(5.0f,  y+=step2, size1, color, false, "Impulse lift: %.0f", liftImp.impulse.len());               
		}    
		pRenderer->Draw2dLabel(5.0f,  y+=step1, size1, physStatus->submergedFraction > fSubmergedMin ? color : colorRed, false, "Submerged: %.2f", physStatus->submergedFraction);
		pRenderer->Draw2dLabel(5.0f,  y+=step1, size1, fWaterLevelDiff < fWaterLevelMaxDiff ? color : colorRed, false, "WaterLevel: %.2f (max: %.2f)", fWaterLevelDiff, fWaterLevelMaxDiff);

		pRenderer->Draw2dLabel(5.0f,  y+=step2, size2, color, false, "Driver input");
		pRenderer->Draw2dLabel(5.0f,  y+=step2, size1, color, false, "power: %.2f", m_movementAction.power);
		pRenderer->Draw2dLabel(5.0f,  y+=step1, size1, color, false, "steer: %.2f", m_movementAction.rotateYaw); 

		pRenderer->Draw2dLabel(5.0f,  y+=step2, size2, color, false, "Propelling");
		//pRenderer->Draw2dLabel(5.0f,  y+=step2, size1, color, false, "turnAccel (norm/real): %.2f / %.2f", turnAccelNorm, turnAccel);         
		//pRenderer->Draw2dLabel(5.0f,  y+=step1, size1, color, false, "Impulse acc: %.0f", linearImp.impulse.len());         
		//pRenderer->Draw2dLabel(5.0f,  y+=step1, size1, color, false, "Impulse steer/damp: %.0f", angularImp.angImpulse.len()); 
		//pRenderer->Draw2dLabel(5.0f,  y+=step1, size1, color, false, "Impulse corner: %.0f", dampImp.impulse.len());

		pRenderer->Draw2dLabel(5.0f,  y+=step2, size2, color, false, "Waves");
		pRenderer->Draw2dLabel(5.0f,  y+=step2, size1, color, false, "timer: %.1f", m_waveTimer); 
		pRenderer->Draw2dLabel(5.0f,  y+=step1, size1, color, false, "frequency: %.2f", waveFreq); 
		pRenderer->Draw2dLabel(5.0f,  y+=step1, size1, color, false, "random: %.2f", m_waveRandomMult); 
		pRenderer->Draw2dLabel(5.0f,  y+=step1, size1, color, false, "kX: %.2f", kx);     
		pRenderer->Draw2dLabel(5.0f,  y+=step1, size1, color, false, "kY: %.2f", ky); 

		if (Boosting())
			pRenderer->Draw2dLabel(5.0f,  y+=step1, size1, color, false, "Boost: %.2f", m_boostCounter);

		IRenderAuxGeom* pGeom = pRenderer->GetIRenderAuxGeom();
		ColorB colorB(0,255,0,255);

		pRenderer->DrawLabel(worldPropPos, 1.3f, "WL: %.2f", waterLevelWorld);

		pGeom->DrawSphere(worldPropPos, 0.15f, colorB);
		pGeom->DrawSphere(waveLoc, 0.25f, colorB);
		pGeom->DrawLine(waveLoc, colorB, waveLoc+Vec3(0,0,2), colorB);

		// impulses
		//DrawImpulse(linearImp, Vec3(0,0,1), 3.f/deltaTime, ColorB(255,0,0,255));
		//DrawImpulse(angularImp, Vec3(0,0,1), 2.f/deltaTime, ColorB(128,0,0,255));          
		//DrawImpulse(liftImp, Vec3(0,0,6), 2.f/deltaTime, ColorB(0,0,255,255));
	}
#endif
}
//------------------------------------------------------------------------
void CVehicleMovementStdBoat::UpdateRunSound(const float deltaTime)
{
	Vec3 localAccel(ZERO);
	m_measureSpeedTimer+=deltaTime;

	if(m_measureSpeedTimer > 0.25f)
	{
		Vec3 accel = (m_statusDyn.v - m_lastMeasuredVel) * (1.f/m_measureSpeedTimer);
		Matrix33 worldTM(!m_statusPos.q);
		localAccel = worldTM * accel;

		m_lastMeasuredVel = m_statusDyn.v;
		m_measureSpeedTimer = 0.f;
	}

	if(m_pVehicle->IsProbablyDistant())
		return;

	float soundSpeedRatio = ms_engineSoundIdleRatio + (1.f-ms_engineSoundIdleRatio) * m_speedRatio;

	SetSoundParam(eSID_Run, "speed", soundSpeedRatio);
	SetSoundParam(eSID_Ambience, "speed", soundSpeedRatio);
	//SetSoundParam(eSID_Run, "boost", Boosting() ? 1.f : 0.f);

	float acceleration = min(1.f, abs(localAccel.y) / m_accel*max(1.f, m_accelCoeff));

	if(acceleration > 0.5f)
	{
		if(ISound *pSound = GetOrPlaySound(eSID_Acceleration, 2.f))
			SetSoundParam(pSound, "acceleration", acceleration);
	}

	float damage = GetSoundDamage();

	if(damage > 0.1f)
	{
		if(ISound *pSound = GetOrPlaySound(eSID_Damage, 5.f, m_enginePos))
			SetSoundParam(pSound, "damage", damage);
	}

	// rpm dropdown for waves
	if(m_rpmPitchDir != 0)
	{
		float speed = (m_rpmPitchDir > 0) ? 0.1f : -0.8f; // quick down, slow up
		m_waveSoundPitch += deltaTime * speed;

		if(m_waveSoundPitch < -m_waveSoundAmount)  // dropdown amount
		{
			m_waveSoundPitch = -m_waveSoundAmount;
			m_rpmPitchDir = 1;
		}
		else if(m_waveSoundPitch > 0.f)
		{
			m_waveSoundPitch = 0.f;
			m_rpmPitchDir = 0;
		}
	}

	if(m_rpmPitchSpeed>0.f)
	{
		const float maxPedal = (!m_inWater) ? 1.f : Boosting() ? 0.8f : 0.7f;

		// pitch rpm with pedal
		float pedal = GetEnginePedal();
		pedal = sgnnz(pedal)*max(ms_engineSoundIdleRatio, min(maxPedal, abs(pedal))); // clamp "pedal" to [0.2..0.7] range

		float delta = pedal - m_rpmScaleSgn;
		m_rpmScaleSgn = max(-1.f, min(1.f, m_rpmScaleSgn + sgn(delta)*min(abs(delta), m_rpmPitchSpeed*deltaTime)));

		// skip transition around 0 when on pedal (sounds more realistic)
		if(abs(GetEnginePedal()) > 0.001f && abs(delta) > 0.001f && sgn(m_rpmScaleSgn) != sgn(delta) && abs(m_rpmScaleSgn) <= 0.3f)
			m_rpmScaleSgn = sgn(delta)*0.3f;

		// for normal driving, rpm is clamped at max defined by sound dept
		m_rpmScale = abs(m_rpmScaleSgn);
		m_rpmScale = min(1.f, max(ms_engineSoundIdleRatio, m_rpmScale + m_waveSoundPitch));

		SetSoundParam(eSID_Run, "rpm_scale", m_rpmScale);
		SetSoundParam(eSID_Ambience, "rpm_scale", m_rpmScale);
	}
}