using UnityEngine; public class MainEngine : MonoBehaviour { [Header("References")] public FuelTank fuelTank; // Reference to the fuel tank supplying this engine public Altimeter altimeter; // Reference to the altimeter for gravity calculations [Header("Engine Settings")] public float maxThrust = 1000f; // kN public float Isp = 300f; // seconds public float throttleRampTime = 2f; // seconds to reach full throttle public float minStartupFlow = 0.05f; // minimum fraction of max flow to maintain ignition public AnimationCurve throttleCurve = AnimationCurve.Linear(0, 0, 1, 1); [Header("Efficiency Settings")] [Range(0f, 1f)] public float engineEfficiency = 1f; // e.g., reduce if engine is hot or damaged [Header("Engine Status")] public bool engineOnline = false; // true if engine is running public bool engineIgnited = false; // true if ignition completed public bool restartable = true; // if false, engine cannot restart once shut down public float throttleInput = 0f; // 0-1 input from pilot public float thrust = 0f; // kN public float fuelFlowRate = 0f; // kg/s private float effectiveThrottle = 0f; // ramped throttle for smooth startup/shutdown private float startupTimer = 0f; private bool everStarted = false; // tracks if engine has ever been started private float postIgnitionTimer = 0f; // timer for post-ignition grace period private const float postIgnitionGrace = 0.5f; // seconds to allow ramp-up after ignition private float g = 9.81f; // local gravity from altimeter void Update() { HandleEngineState(); UpdateThrottle(); // Only calculate thrust and fuel flow if engine is ignited if (engineOnline && engineIgnited) { // Update post-ignition timer if (postIgnitionTimer < postIgnitionGrace) postIgnitionTimer += Time.deltaTime; CalculateThrustAndFuelFlow(); } else { thrust = 0f; fuelFlowRate = 0f; postIgnitionTimer = 0f; } } /// /// Handles startup, shutdown, and engine cutout. /// private void HandleEngineState() { g = (float)altimeter.gh; // Update local gravity from altimeter each frame // Check if engine is offline and restartable conditions if (!engineOnline) { effectiveThrottle = Mathf.MoveTowards(effectiveThrottle, 0f, Time.deltaTime / throttleRampTime); thrust = 0f; fuelFlowRate = 0f; // Mark engine as having been started if (engineIgnited) everStarted = true; if (engineIgnited) Debug.Log("[MainEngine] Engine shutdown: engineOnline set to false."); engineIgnited = false; return; } // Prevent restart if engine is not restartable and was previously started if (!restartable && everStarted) { Debug.Log("[MainEngine] Engine shutdown: not restartable and already started."); engineOnline = false; effectiveThrottle = 0f; thrust = 0f; fuelFlowRate = 0f; engineIgnited = false; return; } // Engine is commanded online if (!engineIgnited) { // Startup delay / ignition sequence startupTimer += Time.deltaTime; if (startupTimer >= throttleRampTime) { engineIgnited = true; startupTimer = 0f; postIgnitionTimer = 0f; // reset grace period timer Debug.Log("[MainEngine] Engine ignition complete."); } else { // Slowly ramp effective throttle during startup effectiveThrottle = Mathf.Lerp(0f, throttleInput, startupTimer / throttleRampTime); } } } /// /// Smooths throttle input over time (fuel ramping) /// private void UpdateThrottle() { if (!engineOnline || !engineIgnited) return; // Smooth throttle changes to avoid instant jumps effectiveThrottle = Mathf.MoveTowards(effectiveThrottle, throttleInput, Time.deltaTime / throttleRampTime); } /// /// Calculates thrust and fuel flow based on current throttle and efficiency /// private void CalculateThrustAndFuelFlow() { // Base thrust from throttle curve and max thrust thrust = throttleCurve.Evaluate(effectiveThrottle) * maxThrust * engineEfficiency; // Fuel flow based on thrust and Isp fuelFlowRate = thrust / (Isp * g); // Request fuel from the tank if assigned if (fuelTank != null) { float fuelRequested = fuelFlowRate * Time.deltaTime; float fuelProvided = fuelTank.RequestFuel(fuelRequested); if (fuelProvided < fuelRequested) { Debug.Log($"[MainEngine] Engine flameout: insufficient fuel (requested {fuelRequested:F3}, got {fuelProvided:F3})."); engineIgnited = false; effectiveThrottle = 0f; thrust = 0f; fuelFlowRate = 0f; return; } } // Only check for flameout if engine is fully ignited and grace period has passed float nominalFlow = maxThrust / (Isp * g); if (engineIgnited && postIgnitionTimer >= postIgnitionGrace && (fuelFlowRate / nominalFlow) < minStartupFlow) { Debug.Log($"[MainEngine] Engine flameout: fuel flow below minimum ({fuelFlowRate / nominalFlow:F3} < {minStartupFlow})."); engineIgnited = false; // engine flameout effectiveThrottle = 0f; thrust = 0f; fuelFlowRate = 0f; } } /// /// Call to turn the engine on /// public void StartEngine() { if (!engineOnline) { // Prevent starting if not restartable and already started if (!restartable && everStarted) { Debug.Log("[MainEngine] StartEngine() called but engine is not restartable and has already started."); return; } Debug.Log("[MainEngine] Engine start command received."); engineOnline = true; startupTimer = 0f; } } /// /// Call to turn the engine off /// public void ShutdownEngine() { Debug.Log("[MainEngine] Engine shutdown command received."); engineOnline = false; engineIgnited = false; } public void SetThrottle(float value) { throttleInput = Mathf.Clamp01(value); } }