void CVehicleSeatActionRotateTurret::MaintainPartRotationWorldSpace(EVehicleTurretRotationType eType)
{
	CVehiclePartBase* pPart   = m_rotations[eType].m_pPart;
	IVehiclePart*     pParent = pPart->GetParent();
	IActor*           pActor  = m_pSeat->GetPassengerActor();

	bool remote     = m_pSeat->GetCurrentTransition() == IVehicleSeat::eVT_RemoteUsage;
	bool worldSpace = m_rotations[eType].m_worldSpace && VehicleCVars().v_independentMountedGuns != 0;

	if (worldSpace && pParent && pActor && pActor->IsClient() && !remote)
	{
		// we want to keep the old worldspace rotation
		// therefore we're updating the local transform from it
		// NB: there is no need to clamp here, its done later

		Matrix34 localTM = pParent->GetWorldTM().GetInverted() * Matrix34(m_rotations[eType].m_prevWorldQuat);
		localTM.OrthonormalizeFast(); // precision issue

		const Matrix34 &baseTM = pPart->GetLocalBaseTM();

		if (!Matrix34::IsEquivalent(baseTM,localTM))
		{
			Ang3 anglesCurr(baseTM);
			Ang3 angles(localTM);

			if (eType == eVTRT_Pitch)
			{
				angles.y = anglesCurr.y;
				angles.z = anglesCurr.z;
			}
			else if (eType == eVTRT_Yaw)
			{
				angles.x = anglesCurr.x;
				angles.y = anglesCurr.y;
			}

			localTM.SetRotationXYZ(angles);
			localTM.SetTranslation(baseTM.GetTranslation());
			pPart->SetLocalBaseTM(localTM);

			m_pSeat->ChangedNetworkState(CVehicle::ASPECT_PART_MATRIX);
		}

#if ENABLE_VEHICLE_DEBUG
		if (VehicleCVars().v_debugdraw == eVDB_Parts)
		{
			float color[] = {1,1,1,1};
			Ang3  a(localTM), aBase(baseTM);
			gEnv->pRenderer->Draw2dLabel(200,200,1.4f,color,false,"localAng: %.1f (real: %.1f)", RAD2DEG(a.z), RAD2DEG(aBase.z));
		}
#endif
	}
}
//------------------------------------------------------------------------
void CVehicleDamageBehaviorDetachPart::Reset()
{
	if (m_detachedEntityId)
	{
		for(TDetachedStatObjs::iterator ite = m_detachedStatObjs.begin(), end = m_detachedStatObjs.end(); ite != end; ++ite)
		{
			CVehiclePartBase	*pPartBase = ite->first;

			if(IStatObj *pStatObj = ite->second)
			{
				if(pPartBase)
				{
					pPartBase->SetStatObj(pStatObj);
				}

				pStatObj->Release();
			}
		}

		m_detachedStatObjs.clear();

		if(GetISystem()->IsSerializingFile() != 1)
		{		
			IEntitySystem* pEntitySystem = gEnv->pEntitySystem;
			pEntitySystem->RemoveEntity(m_detachedEntityId, true);
		}
		
		m_detachedEntityId = 0;
	}	
}
//------------------------------------------------------------------------
void CVehicleDamageBehaviorDetachPart::OnDamageEvent(EVehicleDamageBehaviorEvent event, const SVehicleDamageBehaviorEventParams& behaviorParams)
{
	if (event == eVDBE_Repair)
		return;

	if (!m_detachedEntityId && behaviorParams.componentDamageRatio >= 1.0f)
	{
    CVehiclePartBase* pPart = (CVehiclePartBase*)m_pVehicle->GetPart(m_partName.c_str());
    if (!pPart || !pPart->GetStatObj())
      return;

		if (max(1.f-behaviorParams.randomness, pPart->GetDetachProbability()) < cry_random(0.0f, 1.0f)) 
			return;

		IEntity* pDetachedEntity = SpawnDetachedEntity();
		if (!pDetachedEntity)
			return;

		m_detachedEntityId = pDetachedEntity->GetId();

    const Matrix34& partWorldTM = pPart->GetWorldTM();
    pDetachedEntity->SetWorldTM(partWorldTM);
		
    MovePartToTheNewEntity(pDetachedEntity, pPart);
		
		SEntityPhysicalizeParams physicsParams;
		physicsParams.mass = pPart->GetMass();
		physicsParams.type = PE_RIGID;    		    
		physicsParams.nSlot = 0;
		pDetachedEntity->Physicalize(physicsParams);
  
    IPhysicalEntity* pPhysics = pDetachedEntity->GetPhysics();
    if (pPhysics)
    {
      pe_params_part params;      
      params.flagsOR = geom_collides|geom_floats;
      params.flagsColliderAND = ~geom_colltype3;
      params.flagsColliderOR = geom_colltype0;
      pPhysics->SetParams(&params);

      pe_action_add_constraint ac;
      ac.flags = constraint_inactive|constraint_ignore_buddy;
      ac.pBuddy = m_pVehicle->GetEntity()->GetPhysics();
      ac.pt[0].Set(0,0,0);
      pPhysics->Action(&ac);
			
			// after 1s, remove the constraint again
			m_pVehicle->SetTimer(-1, 1000, this);
    
		  // set the impulse
		  const Vec3& velocity = m_pVehicle->GetStatus().vel;  				  		  
		  Vec3 baseForce = m_pVehicle->GetEntity()->GetWorldTM().TransformVector(pPart->GetDetachBaseForce());
		  baseForce *= cry_random(6.0f, 10.0f);
  		
      pe_action_impulse actionImpulse;
		  actionImpulse.impulse = physicsParams.mass * (velocity + baseForce);
      actionImpulse.angImpulse = physicsParams.mass * Vec3(cry_random(-1.0f,1.0f), cry_random(-1.0f,1.0f), cry_random(-1.0f,1.0f));
      actionImpulse.iApplyTime = 1;
		  
      pPhysics->Action(&actionImpulse);		
    }

		// copy vehicle's material to new entity (fixes detaching parts from vehicles with different paints),
		// or specify the destroyed material if it exists

		IStatObj* pExternalStatObj = pPart->GetExternalGeometry(false);	// Get undamaged external geometry (if any)
		IMaterial *pMaterial = pExternalStatObj ? pExternalStatObj->GetMaterial() : m_pVehicle->GetEntity()->GetMaterial();

		if(event == eVDBE_VehicleDestroyed || event == eVDBE_Hit)
		{
			if (pExternalStatObj)
			{
				if (IStatObj* pStatObj = pPart->GetExternalGeometry(true))     // Try to get the destroyed, external geometry material
					pMaterial = pStatObj->GetMaterial();
			}
			else if (m_pVehicle->GetDestroyedMaterial())  // If there is no external geometry, try the vehicle's destroyed material
			{
				pMaterial = m_pVehicle->GetDestroyedMaterial();
			}
		}

		pDetachedEntity->SetMaterial(pMaterial);
		
		AttachParticleEffect(pDetachedEntity, m_pEffect);

		if (m_notifyMovement)
		{
			SVehicleMovementEventParams params;
			params.iValue = pPart->GetIndex();
			m_pVehicle->GetMovement()->OnEvent(IVehicleMovement::eVME_PartDetached, params);
		}
	}
}
//------------------------------------------------------------------------
void CVehicleSeatActionRotateTurret::UpdatePartRotation(EVehicleTurretRotationType eType, float frameTime)
{
	CRY_ASSERT( eType < eVTRT_NumRotationTypes );

	const float threshold = 0.01f;
	if (frameTime > 0.08f) frameTime = 0.08f;

	CVehiclePartBase* pPart   = m_rotations[eType].m_pPart;
	IVehiclePart*     pParent = pPart->GetParent();
	IActor*           pActor  = m_pSeat->GetPassengerActor();

	float rot_dir      = fsgnf(m_rotations[eType].m_action);
	float max_rotation = fabsf(m_rotations[eType].m_action);
	float rot_speed    = DEG2RAD(fabsf(m_rotations[eType].m_speed)) * GetDamageSpeedMul(pPart);

	float delta = rot_dir * rot_speed  * frameTime;
	delta += m_rotations[eType].m_aimAssist;

	delta = fmod(delta, gf_PI2);
	if (delta > gf_PI)  delta -= gf_PI2;
	if (delta < -gf_PI) delta += gf_PI2;

	Limit( delta, -max_rotation, max_rotation);

	Ang3 deltaAngles(ZERO);
	if (eType == eVTRT_Pitch)
		deltaAngles.x = delta;
	else if (eType == eVTRT_Yaw)
		deltaAngles.z = delta;
	else
		CRY_ASSERT(false && "Unknown turret rotation");

	Matrix34 tm     = pPart->GetLocalBaseTM();
	Ang3     angles = Ang3::GetAnglesXYZ(tm) + deltaAngles;

	float lerp = 0.f;
	if (eType == eVTRT_Pitch)
	{
		Vec3 yAxis = m_rotations[eVTRT_Yaw].m_pPart->GetLocalBaseTM().GetColumn1();
		yAxis.z = 0.f;
		yAxis.normalize();
		lerp = 0.5f - 0.5f * yAxis.y;
		Limit(lerp, 0.0f, 1.0f);
	}

	// clamp to limits
	if (m_rotations[eType].m_minLimitF != 0.0f || m_rotations[eType].m_maxLimit != 0.0f)
	{
		// Different clamp angles facing forwards/backwards
		float minLimit = m_rotations[eType].m_minLimitF + (m_rotations[eType].m_minLimitB - m_rotations[eType].m_minLimitF) * lerp;
		float angle    = (eType == eVTRT_Pitch) ? angles.x : angles.z;
		if (angle > m_rotations[eType].m_maxLimit || angle < minLimit)
		{
			angle                             = clamp_tpl(angle, minLimit, m_rotations[eType].m_maxLimit);
			m_rotations[eType].m_currentValue = 0.f;

			if (eType == eVTRT_Pitch)
				angles.x = angle;
			else
				angles.z = angle;
		}
	}

	m_rotations[eType].m_orientation.Set(Quat::CreateRotationXYZ(angles));
	m_rotations[eType].m_orientation.Update(frameTime);

	m_rotations[eType].m_action    = 0.0f;
	m_rotations[eType].m_aimAssist = 0.0f;

	Matrix34 newTM(m_rotations[eType].m_orientation.Get().GetNormalized());
	newTM.SetTranslation(tm.GetTranslation());
	pPart->SetLocalBaseTM(newTM);

	// store world-space rotation
	const Matrix34 &worldTM = pPart->GetWorldTM();
	m_rotations[eType].m_prevWorldQuat = Quat(worldTM);
	CRY_ASSERT(m_rotations[eType].m_prevWorldQuat.IsValid());

	// now update the turret sound based on the calculated rotation speed
	UpdateRotationSound(eType, delta, frameTime);

}
void CVehicleSeatActionRotateTurret::DoUpdate(const float frameTime)
{
	FUNCTION_PROFILER(GetISystem(), PROFILE_ACTION);

	if (gEnv->IsClient() && m_pVehicle->IsProbablyDistant() && !m_pVehicle->GetGameObject()->IsProbablyVisible())
		return;

	// AI use the aim goal to control their rotation (remote usage too)
	if (!m_aimGoal.IsZero())
	{
		UpdateAimGoal();
	}

	// now update each rotation type
	for (int i = 0; i < eVTRT_NumRotationTypes; ++i)
	{
		if (m_rotations[i].m_pPart)
		{
			MaintainPartRotationWorldSpace((EVehicleTurretRotationType)i);
		}
	}

	// Cache the helper position before applying any rotation
	IActor* pActor        = m_pSeat->GetPassengerActor();
	bool    checkRotation = (m_rotTestHelpers[0] && m_rotTestHelpers[1] && pActor);
	Vec3    oldHelperPos  = checkRotation ? m_rotTestHelpers[1]->GetWorldSpaceTranslation() : Vec3(ZERO);

	Matrix34 oldMatrices[eVTRT_NumRotationTypes];

	for (int i = 0; i < eVTRT_NumRotationTypes; ++i)
	{
		if (m_rotations[i].m_pPart)
		{
			oldMatrices[i] = m_rotations[i].m_pPart->GetLocalBaseTM();
			UpdatePartRotation((EVehicleTurretRotationType)i, frameTime);
		}
	}

	// Check for turret collisions
	if (checkRotation)
	{
		// need to test the new rotations before applying them. Sweep a sphere between the two helpers and check for collisions...
		static IPhysicalEntity* pSkipEntities[10];
		int                     numSkip = m_pVehicle->GetSkipEntities(pSkipEntities, 10);
		primitives::sphere      sphere;
		sphere.center = m_rotTestHelpers[0]->GetWorldSpaceTranslation();
		sphere.r      = m_rotTestRadius;

		geom_contact* pContact = NULL;
		Vec3          dir      = m_rotTestHelpers[1]->GetWorldSpaceTranslation() - sphere.center;
		float         hit      = gEnv->pPhysicalWorld->PrimitiveWorldIntersection(sphere.type, &sphere, dir, ent_static | ent_terrain | ent_rigid | ent_sleeping_rigid, &pContact, 0, (geom_colltype_player << rwi_colltype_bit) | rwi_stop_at_pierceable, 0, 0, 0, pSkipEntities, numSkip);
		if (hit > 0.001f && pContact)
		{
			// there was a collision. check whether the barrel is moving towards the collision point or not... if not, ignore the collision.
#if ENABLE_VEHICLE_DEBUG
			if (VehicleCVars().v_debugdraw > 0)
			{
				CPersistantDebug* pPD = CCryAction::GetCryAction()->GetPersistantDebug();
				pPD->Begin("VehicleCannon", false);

				ColorF col(1.0f, 0.0f, 0.0f, 1.0f);
				if (pContact && hit > 0.0f)
				{
					pPD->AddSphere(pContact->pt, 0.1f, col, 30.0f);
				}
			}
#endif
			Vec3 endPos  = m_rotTestHelpers[1]->GetWorldSpaceTranslation();
			Vec3 moveDir = endPos - oldHelperPos;
			Vec3 hitDir  = pContact->pt - oldHelperPos;

			if (moveDir.Dot(hitDir) > 0.0f)
			{
				// reset as though the rotation never happened.
				for (int i = 0; i < eVTRT_NumRotationTypes; ++i)
				{
					if (m_rotations[i].m_pPart)
					{
						CVehiclePartBase* pPart = m_rotations[i].m_pPart;
						pPart->SetLocalBaseTM(oldMatrices[i]);
						const Matrix34 &worldTM = pPart->GetWorldTM();
						m_rotations[i].m_prevWorldQuat = Quat(worldTM);
						m_rotations[i].m_orientation.Set(Quat(Matrix33(oldMatrices[i])));
					}
				}
			}
		}
	}

	m_aimGoalPriority = 0;
}