using Car_simulation.Core.Physics; namespace Car_simulation.Core.Components { public class Drivetrain : ICarComponent { public Engine Engine { get; private set; } public WheelSystem WheelSystem { get; private set; } private int _currentGear = 1; public float[] GearRatios { get; set; } = { 3.8f, 2.5f, 1.8f, 1.3f, 1.0f, 0.8f, 0.65f }; public float FinalDriveRatio { get; set; } = 4.0f; public float Efficiency { get; set; } = 0.95f; public float ClutchEngagement { get; set; } = 0f; public float MaxClutchTorque { get; set; } = 400f; public float ClutchStiffness { get; set; } = 50f; public float ClutchTorque { get; private set; } public float TransmittedPower { get; private set; } public float ClutchSlipRatio { get; private set; } public Drivetrain(Engine engine, WheelSystem wheelSystem) { Engine = engine; WheelSystem = wheelSystem; } public void Update(float deltaTime) { if (ClutchEngagement <= 0.01f || TotalRatio == 0) { ClutchTorque = 0; TransmittedPower = 0; ClutchSlipRatio = 1f; return; } float expectedWheelOmega = Engine.AngularVelocity / TotalRatio; float actualWheelOmega = WheelSystem.AngularVelocity; float omegaDifference = actualWheelOmega - expectedWheelOmega; float maxClutchTorque = MaxClutchTorque * ClutchEngagement; float desiredTorque = -omegaDifference * ClutchStiffness; desiredTorque = Math.Clamp(desiredTorque, -maxClutchTorque, maxClutchTorque); if (desiredTorque > 0) { float engineTorque = Engine.GetTorqueOutput() * Engine.GetActualThrottle(); float maxEngineTorqueAtWheels = engineTorque * TotalRatio * Efficiency; desiredTorque = Math.Min(desiredTorque, maxEngineTorqueAtWheels); } ClutchTorque = desiredTorque; float energyTransferred = 0f; if (omegaDifference > 0.01f) // Wheels → Engine { float power = ClutchTorque * Engine.AngularVelocity; energyTransferred = power * deltaTime; float wheelEnergyLoss = Math.Abs(energyTransferred); float engineEnergyGain = wheelEnergyLoss * Efficiency; WheelSystem.TotalEnergy -= wheelEnergyLoss; Engine.FlywheelEnergy += engineEnergyGain; } else if (omegaDifference < -0.01f) // Engine → Wheels { float power = -ClutchTorque * Engine.AngularVelocity; energyTransferred = power * deltaTime; float engineEnergyLoss = Math.Abs(energyTransferred); float wheelEnergyGain = engineEnergyLoss * Efficiency; Engine.FlywheelEnergy -= engineEnergyLoss; WheelSystem.TotalEnergy += wheelEnergyGain; } TransmittedPower = energyTransferred / deltaTime; if (maxClutchTorque > 0) { float torqueRatio = Math.Abs(ClutchTorque) / maxClutchTorque; ClutchSlipRatio = torqueRatio; } else { ClutchSlipRatio = 1f; } } public float GearRatio => GetCurrentGearRatio(); public float TotalRatio => GearRatio * FinalDriveRatio; private float GetCurrentGearRatio() { if (_currentGear == 0) return 0f; if (_currentGear == -1) return -3.5f; if (_currentGear > 0 && _currentGear <= GearRatios.Length) return GearRatios[_currentGear - 1]; return 0f; } public float GetSpeedDifferenceRPM() { float expectedWheelOmega = Engine.AngularVelocity / TotalRatio; float actualWheelOmega = WheelSystem.AngularVelocity; return (actualWheelOmega - expectedWheelOmega) * PhysicsUtil.RAD_PER_SEC_TO_RPM; } public string GetCurrentGearName() { return _currentGear switch { -1 => "R", 0 => "N", _ => _currentGear.ToString() }; } public float GetClutchSlipPercent() => ClutchSlipRatio * 100f; public void GearUp() { if (_currentGear < GearRatios.Length) _currentGear++; } public void GearDown() { if (_currentGear > 1) _currentGear--; } } }