Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Move these outside the Program class as top-level methods
- public static Vector3D GetNaturalGravity(IMyRemoteControl rc)
- {
- return rc.GetTotalGravity();
- }
- public static double GetAltitude(IMyRemoteControl rc)
- {
- Vector3D planetCenter;
- return rc.TryGetPlanetPosition(out planetCenter) ?
- Vector3D.Distance(rc.GetPosition(), planetCenter) - rc.GetRadius() : 0;
- }
- public static Vector3D GetVelocity(IMyRemoteControl rc)
- {
- return rc.GetShipVelocities().LinearVelocity;
- }
- // Enum for script states
- enum DropPodState
- {
- Idle, // Waiting for GOAL
- AwaitingLaunch, // GOAL set, waiting for LAUNCH
- Initializing, // Finding blocks, loading settings
- AwaitingPhysicalRelease, // LAUNCH command received, waiting for manual detach ('release' argument)
- Ascending, // Getting out of gravity
- HighAltitudeCruise, // Navigating to target at high altitude
- Descending, // Reducing altitude
- Landing, // Controlled touchdown
- Finished, // Mission complete
- Error // Something went wrong
- }
- // Struct to hold settings - Defined only ONCE
- public struct DropPodSettings
- {
- public double AscentGravityThreshold;
- public double MinCruiseAltitude;
- public double DescentAltitude;
- public double ParachuteAltitude;
- public double LandingSpeedThreshold;
- public double FinalLandingAltitude;
- public double ThrustMultiplier;
- public double GyroMultiplier; // New setting
- // Default values
- public DropPodSettings(bool isDefault = true)
- {
- AscentGravityThreshold = 0.1;
- MinCruiseAltitude = 40000; // meters from planet center or surface approx.
- DescentAltitude = 3000; // meters above ground (RC.GetAltitude())
- ParachuteAltitude = 800; // meters above ground
- LandingSpeedThreshold = 5; // m/s
- FinalLandingAltitude = 3; // meters above ground (allow slight height before landing gear touch)
- ThrustMultiplier = 0.8;
- GyroMultiplier = 5.0; // Default gyro sensitivity
- }
- }
- DropPodState currentState = DropPodState.Idle;
- Vector3D targetGPS;
- // Store state data for persistence (CurrentState, TargetGPS, etc.)
- string storedState = "";
- // --- Block Lists (populated in InitializeBlocks) ---
- IMyRemoteControl remoteControl;
- List<IMyThrust> upThrusters = new List<IMyThrust>();
- List<IMyThrust> downThrusters = new List<IMyThrust>();
- List<IMyThrust> forwardThrusters = new List<IMyThrust>();
- List<IMyThrust> backwardThrusters = new List<IMyThrust>();
- List<IMyThrust> leftThrusters = new List<IMyThrust>();
- List<IMyThrust> rightThrusters = new List<IMyThrust>();
- List<IMyGyro> gyros = new List<IMyGyro>();
- List<IMyParachute> parachutes = new List<IMyParachute>();
- // Add your release mechanism block reference here (Connector/Merge Block)
- // IMyShipConnector releaseConnector;
- // --- Configuration Settings (Loaded from Custom Data) ---
- DropPodSettings settings = new DropPodSettings();
- bool parachutesAvailable = false;
- bool launched = false; // Flag to indicate if the pod has been released (via 'release' argument)
- // --- Constructor ---
- public Program()
- {
- // Set update frequency
- Runtime.UpdateFrequency = UpdateFrequency.Update10; // Start lower
- // Load state from Storage
- LoadState();
- // Load settings from Custom Data (or print defaults if empty)
- LoadSettings();
- // Initial echo
- Echo("Script loaded. Current State: " + currentState.ToString());
- EchoSettings();
- // If not in Idle, try to ensure blocks are found and settings are loaded
- if (currentState != DropPodState.Idle)
- {
- // Re-initialize blocks if needed (e.g., after game load)
- InitializeBlocks(); // This just finds blocks, doesn't change state
- if (remoteControl == null && currentState != DropPodState.Error)
- {
- SetState(DropPodState.Error, "Failed to find Remote Control block on load.");
- }
- }
- else
- {
- // In Idle, prompt user
- Echo("Send 'GOAL GPS:...' to set target.");
- }
- }
- // --- Save/Load State ---
- void SaveState()
- {
- // Save current state and target GPS to Storage
- storedState = currentState.ToString() + "|" + (targetGPS != Vector3D.Zero ? targetGPS.ToString() : "");
- Storage = storedState;
- }
- void LoadState()
- {
- if (!string.IsNullOrWhiteSpace(Storage))
- {
- string[] parts = Storage.Split('|');
- if (parts.Length > 0) Enum.TryParse(parts[0], out currentState);
- if (parts.Length > 1 && !string.IsNullOrWhiteSpace(parts[1]))
- {
- Vector3D.TryParse(parts[1], out targetGPS);
- }
- Echo("Loaded state: " + currentState.ToString());
- if (targetGPS != Vector3D.Zero) Echo("Loaded target: " + targetGPS.ToString());
- // Adjust update frequency immediately based on loaded state if necessary
- switch (currentState)
- {
- case DropPodState.Ascending:
- case DropPodState.HighAltitudeCruise:
- case DropPodState.Descending:
- case DropPodState.Landing:
- Runtime.UpdateFrequency = UpdateFrequency.Update1;
- break;
- default:
- Runtime.UpdateFrequency = UpdateFrequency.Update10;
- break;
- }
- }
- else
- {
- currentState = DropPodState.Idle;
- Echo("No saved state found. Starting in Idle.");
- }
- }
- // --- Custom Data Settings Handling ---
- void LoadSettings()
- {
- if (string.IsNullOrWhiteSpace(Me.CustomData))
- {
- Echo("Custom Data is empty. Printing default settings.");
- PrintDefaultSettings();
- // Use default settings instance already created
- }
- else
- {
- Echo("Loading settings from Custom Data...");
- settings = ParseSettings(Me.CustomData);
- Echo("Settings loaded.");
- }
- }
- void PrintDefaultSettings()
- {
- StringBuilder sb = new StringBuilder();
- sb.AppendLine("# Drop Pod Script Settings");
- sb.AppendLine("# Edit these values to customize behavior.");
- sb.AppendLine("# Lines starting with # are comments.");
- sb.AppendLine("# Use key=value format.");
- sb.AppendLine("");
- sb.AppendLine($"AscentGravityThreshold={settings.AscentGravityThreshold}");
- sb.AppendLine($"MinCruiseAltitude={settings.MinCruiseAltitude}");
- sb.AppendLine($"DescentAltitude={settings.DescentAltitude}");
- sb.AppendLine($"ParachuteAltitude={settings.ParachuteAltitude}");
- sb.AppendLine($"LandingSpeedThreshold={settings.LandingSpeedThreshold}");
- sb.AppendLine($"FinalLandingAltitude={settings.FinalLandingAltitude}");
- sb.AppendLine($"ThrustMultiplier={settings.ThrustMultiplier}");
- sb.AppendLine($"GyroMultiplier={settings.GyroMultiplier}"); // Added setting for gyro sensitivity
- // Add other settings here as needed
- Me.CustomData = sb.ToString();
- }
- DropPodSettings ParseSettings(string customData)
- {
- DropPodSettings loadedSettings = new DropPodSettings();
- var lines = customData.Split('\n');
- foreach (var line in lines)
- {
- var trimmedLine = line.Trim();
- if (trimmedLine.StartsWith("#") || string.IsNullOrWhiteSpace(trimmedLine)) continue;
- var parts = trimmedLine.Split('=');
- if (parts.Length != 2) continue;
- string key = parts[0].Trim();
- string value = parts[1].Trim();
- // Parse specific settings
- double doubleVal;
- // Using basic TryParse as Globalization is not available
- if (double.TryParse(value, out doubleVal))
- {
- switch (key)
- {
- case "AscentGravityThreshold": loadedSettings.AscentGravityThreshold = doubleVal; break;
- case "MinCruiseAltitude": loadedSettings.MinCruiseAltitude = doubleVal; break;
- case "DescentAltitude": loadedSettings.DescentAltitude = doubleVal; break;
- case "ParachuteAltitude": loadedSettings.ParachuteAltitude = doubleVal; break;
- case "LandingSpeedThreshold": loadedSettings.LandingSpeedThreshold = doubleVal; break;
- case "FinalLandingAltitude": loadedSettings.FinalLandingAltitude = doubleVal; break;
- case "ThrustMultiplier": loadedSettings.ThrustMultiplier = doubleVal; break;
- case "GyroMultiplier": loadedSettings.GyroMultiplier = doubleVal; break;
- // Add cases for other double settings
- }
- }
- // Add parsing for other types (int, bool, string) if needed
- }
- return loadedSettings;
- }
- void EchoSettings()
- {
- Echo("--- Settings ---");
- Echo($"Ascent Gravity: {settings.AscentGravityThreshold:F2} m/s²");
- Echo($"Min Cruise Alt: {settings.MinCruiseAltitude:F0} m");
- Echo($"Descent Alt: {settings.DescentAltitude:F0} m");
- Echo($"Parachute Alt: {settings.ParachuteAltitude:F0} m");
- Echo($"Landing Speed: {settings.LandingSpeedThreshold:F1} m/s");
- Echo($"Final Land Alt: {settings.FinalLandingAltitude:F0} m");
- Echo($"Thrust Multi: {settings.ThrustMultiplier:F2}");
- Echo($"Gyro Multi: {settings.GyroMultiplier:F2}");
- Echo("----------------");
- }
- // --- Main Method ---
- public void Main(string argument, UpdateType updateSource)
- {
- Echo("State: " + currentState.ToString());
- Echo("Target: " + (targetGPS != Vector3D.Zero ? targetGPS.ToString() : "None"));
- Echo("Time: " + Runtime.TimeSinceLastRun.TotalSeconds.ToString("F3") + "s"); // Echo last run time
- // Handle arguments based on current state
- if (argument.Length > 0)
- {
- string[] args = argument.Split(' ');
- string command = args[0].ToUpper();
- switch (currentState)
- {
- case DropPodState.Idle:
- if (command == "GOAL" && args.Length > 1)
- {
- string gpsString = argument.Substring("GOAL ".Length).Trim();
- Vector3D parsedGps;
- if (TryParseGps(gpsString, out parsedGps))
- {
- targetGPS = parsedGps;
- SetState(DropPodState.AwaitingLaunch);
- Echo($"Target set to {targetGPS.ToString()}. Send 'LAUNCH' to begin.");
- }
- else
- {
- Echo("Invalid GPS format. Use 'GOAL GPS:Name:X:Y:Z:'.");
- }
- }
- else { Echo("Send 'GOAL GPS:...' to set target."); }
- break;
- case DropPodState.AwaitingLaunch:
- if (command == "LAUNCH")
- {
- SetState(DropPodState.Initializing);
- Echo("Launch command received. Initializing...");
- }
- else if (command == "GOAL" && args.Length > 1) // Allow changing target
- {
- string gpsString = argument.Substring("GOAL ".Length).Trim();
- Vector3D parsedGps;
- if (TryParseGps(gpsString, out parsedGps))
- {
- targetGPS = parsedGps;
- Echo($"Target updated to {targetGPS.ToString()}. Send 'LAUNCH' to begin.");
- }
- else
- {
- Echo("Invalid GPS format. Use 'GOAL GPS:Name:X:Y:Z:'.");
- }
- }
- else { Echo("Target set. Send 'LAUNCH' to begin."); }
- break;
- case DropPodState.AwaitingPhysicalRelease:
- if (command == "RELEASE" || command == "LAUNCHED") // Argument to signal manual detachment
- {
- launched = true; // Flag set
- SetState(DropPodState.Ascending);
- Echo("Release confirmed. Initiating ascent.");
- }
- else { Echo("Waiting for physical release. Disconnect and send 'RELEASE' argument."); }
- break;
- case DropPodState.Finished:
- case DropPodState.Error:
- if (command == "RESET")
- {
- targetGPS = Vector3D.Zero; // Clear target
- launched = false;
- SetState(DropPodState.Idle);
- Echo("Script reset. Send 'GOAL GPS:...' to start again.");
- }
- break;
- default:
- // Ignore arguments in active flight states unless specifically handled (e.g., abort?)
- Echo("Flight in progress. Ignoring command.");
- break;
- }
- }
- // State Machine Logic (called every update tick if frequency > None)
- switch (currentState)
- {
- case DropPodState.Initializing:
- HandleInitializing();
- break;
- case DropPodState.AwaitingPhysicalRelease:
- // Just waiting for the 'release' argument
- break;
- case DropPodState.Ascending:
- HandleAscending();
- break;
- case DropPodState.HighAltitudeCruise:
- HandleHighAltitudeCruise();
- break;
- case DropPodState.Descending:
- HandleDescending();
- break;
- case DropPodState.Landing:
- HandleLanding();
- break;
- case DropPodState.Finished:
- // Do nothing, waiting for reset
- break;
- case DropPodState.Error:
- // Display error, waiting for reset
- break;
- }
- // Save state at the end of Main
- SaveState();
- }
- // --- State Handler Methods ---
- void SetState(DropPodState newState, string errorMessage = null)
- {
- currentState = newState;
- Echo("Transitioned to State: " + currentState.ToString());
- // Actions on state entry
- switch (newState)
- {
- case DropPodState.Idle:
- case DropPodState.AwaitingLaunch:
- Runtime.UpdateFrequency = UpdateFrequency.Update10; // Low frequency while waiting
- SetThrusterOverrides(0);
- SetGyroOverride(false);
- // remoteControl?.SetAutoPilotEnabled(false); // Commenting out if RC methods are an issue
- break;
- case DropPodState.Initializing:
- Runtime.UpdateFrequency = UpdateFrequency.Update10; // Still low freq
- InitializeBlocks(); // Find blocks
- LoadSettings(); // Reload settings just in case Custom Data was changed
- break;
- case DropPodState.AwaitingPhysicalRelease:
- Runtime.UpdateFrequency = UpdateFrequency.Update10; // Low freq
- SetThrusterOverrides(0); // Ensure everything is off before release
- SetGyroOverride(false);
- // remoteControl?.SetAutoPilotEnabled(false); // Commenting out if RC methods are an issue
- Echo("Ready for release. Disconnect the pod and send 'RELEASE' argument.");
- break;
- case DropPodState.Ascending:
- Runtime.UpdateFrequency = UpdateFrequency.Update1; // High frequency for flight
- SetGyroOverride(true); // Enable gyro override for orientation control
- // remoteControl?.SetAutoPilotEnabled(false); // Commenting out if RC methods are an issue
- break;
- case DropPodState.HighAltitudeCruise:
- Runtime.UpdateFrequency = UpdateFrequency.Update1; // High frequency for navigation/monitoring
- SetGyroOverride(false); // Let RC handle orientation
- // RC autopilot setup is done in HandleHighAltitudeCruise
- break;
- case DropPodState.Descending:
- Runtime.UpdateFrequency = UpdateFrequency.Update1; // High frequency for descent control
- // remoteControl?.SetAutoPilotEnabled(false); // Commenting out if RC methods are an issue
- SetGyroOverride(true); // Manual gyro control for descent orientation
- break;
- case DropPodState.Landing:
- Runtime.UpdateFrequency = UpdateFrequency.Update1; // Critical frequency for soft landing
- // Gyros already enabled
- break;
- case DropPodState.Finished:
- Runtime.UpdateFrequency = UpdateFrequency.None; // Stop script execution
- SetThrusterOverrides(0);
- SetGyroOverride(false);
- // remoteControl?.SetAutoPilotEnabled(false); // Commenting out if RC methods are an issue
- Echo("Mission Complete!");
- break;
- case DropPodState.Error:
- Runtime.UpdateFrequency = UpdateFrequency.None; // Stop script execution
- SetThrusterOverrides(0);
- SetGyroOverride(false);
- // remoteControl?.SetAutoPilotEnabled(false); // Commenting out if RC methods are an issue
- Echo("Error: " + errorMessage);
- break;
- }
- SaveState(); // Save state change
- }
- void HandleInitializing()
- {
- Echo("Initializing...");
- // Blocks already found in SetState.
- // Settings already loaded in SetState.
- if (remoteControl == null)
- {
- SetState(DropPodState.Error, "No Remote Control block found.");
- return;
- }
- if (upThrusters.Count == 0)
- {
- SetState(DropPodState.Error, "No 'Up' thrusters found. (Thrusters pointing down relative to grid)");
- return;
- }
- // Warnings for missing directional thrusters can be echoed but don't need to be fatal errors
- parachutesAvailable = parachutes.Count > 0;
- if (parachutesAvailable) Echo("Parachutes found."); else Echo("No parachutes found. Relying on thrusters for landing.");
- // Check if a target was actually set before launching
- if (targetGPS == Vector3D.Zero)
- {
- SetState(DropPodState.Error, "No target GPS set. Use 'GOAL GPS:...' first.");
- return;
- }
- Echo("Initialization complete. Ready for physical release.");
- SetState(DropPodState.AwaitingPhysicalRelease);
- }
- // Replace your HandleAscending method
- void HandleAscending()
- {
- if (remoteControl == null) { SetState(DropPodState.Error, "Remote Control lost during ascent."); return; }
- Vector3D gravity = remoteControl.GetNaturalGravity();
- double gravityMagnitude = gravity.Length();
- Vector3D currentPos = remoteControl.GetPosition();
- double currentAltitude = remoteControl.GetAltitude();
- if (gravityMagnitude < settings.AscentGravityThreshold && currentAltitude > settings.MinCruiseAltitude / 2)
- {
- SetThrusterOverrides(0);
- SetState(DropPodState.HighAltitudeCruise);
- Echo("Reached space altitude. Initiating cruise.");
- return;
- }
- // Orient grid straight up
- if (gravityMagnitude > 0.01)
- {
- Vector3D antiGravity = -Vector3D.Normalize(gravity);
- AlignGridToVector(antiGravity, settings.GyroMultiplier);
- }
- else
- {
- SetGyroOverride(true, Vector3D.Zero);
- }
- SetThrusterOverrides((float)settings.ThrustMultiplier, Base6Directions.Direction.Up);
- Echo($"Ascending. Gravity: {gravityMagnitude:F2} m/s². Altitude: {currentAltitude:F0} m");
- }
- // Update HandleHighAltitudeCruise method
- void HandleHighAltitudeCruise()
- {
- if (remoteControl == null) { SetState(DropPodState.Error, "Remote Control lost during cruise."); return; }
- Vector3D currentPos = remoteControl.GetPosition();
- Vector3D targetPos = targetGPS;
- Vector3D planetCenter;
- double currentAltitude = GetAltitude(remoteControl);
- // Get planet center for orbital navigation
- if (!remoteControl.TryGetPlanetPosition(out planetCenter))
- {
- SetState(DropPodState.Error, "Cannot determine planet position.");
- return;
- }
- // Calculate positions relative to planet center
- Vector3D currentPosFromCenter = currentPos - planetCenter;
- Vector3D targetPosFromCenter = targetPos - planetCenter;
- // Calculate desired orbit position (above target at cruise altitude)
- double desiredOrbitRadius = Math.Max(settings.MinCruiseAltitude, currentPosFromCenter.Length());
- Vector3D desiredOrbitPos = planetCenter + Vector3D.Normalize(targetPosFromCenter) * desiredOrbitRadius;
- // Calculate if we're roughly above the target
- double angleToTarget = Math.Acos(
- Vector3D.Dot(Vector3D.Normalize(currentPosFromCenter), Vector3D.Normalize(targetPosFromCenter))
- );
- // Convert to degrees for readable comparison
- double angleToTargetDegrees = angleToTarget * 180 / Math.PI;
- // If we're above target (within 5 degrees) and at appropriate altitude, start descent
- if (angleToTargetDegrees < 5 && currentAltitude < settings.MinCruiseAltitude * 1.1)
- {
- SetState(DropPodState.Descending);
- Echo($"Above target. Starting descent from {currentAltitude:F0}m");
- return;
- }
- // Otherwise, continue orbital movement
- Vector3D velocityDir = remoteControl.GetShipVelocities().LinearVelocity;
- Vector3D gravity = GetNaturalGravity(remoteControl);
- // Calculate desired movement direction (tangent to orbit)
- Vector3D orbitNormal = Vector3D.Cross(currentPosFromCenter, targetPosFromCenter);
- if (orbitNormal.LengthSquared() > 0.1)
- {
- Vector3D desiredDirection = Vector3D.Cross(Vector3D.Normalize(currentPosFromCenter), Vector3D.Normalize(orbitNormal));
- // Align to orbital direction
- AlignGridToVector(desiredDirection, settings.GyroMultiplier);
- // Apply thrust to maintain orbit
- double gravityMagnitude = gravity.Length();
- float thrustLevel = (float)Math.Min(1.0, gravityMagnitude / 9.81);
- SetThrusterOverrides(thrustLevel, Base6Directions.Direction.Forward);
- }
- Echo($"Orbiting. Altitude: {currentAltitude:F0}m, Angle to target: {angleToTargetDegrees:F1}°");
- }
- void HandleDescending()
- {
- if (remoteControl == null) { SetState(DropPodState.Error, "Remote Control lost during descent."); return; }
- if (downThrusters.Count == 0 && !parachutesAvailable) { SetState(DropPodState.Error, "No downward thrusters or parachutes for descent."); return; }
- Vector3D gravity = remoteControl.GetNaturalGravity();
- Vector3D currentPos = remoteControl.GetPosition();
- // If GetVelocity is causing errors, you might need to remove or comment out lines using velocity.
- Vector3D velocity = remoteControl.GetVelocity();
- // If GetAltitude is causing errors, you might need to use a different altitude check.
- double altitudeAboveGround = remoteControl.GetAltitude();
- double verticalSpeed = Vector3D.Dot(velocity, Vector3D.Normalize(gravity)); // Positive if moving down
- // Orient grid for descent - point "Down" thrusters towards gravity
- Vector3D gravityDirection = Vector3D.Normalize(gravity); // Desired 'Down' direction for the grid
- AlignGridToVector(gravityDirection, settings.GyroMultiplier); // Use gyros to align grid.WorldMatrix.Down with gravityDirection
- // Parachute Deployment
- bool areChutesDeployed = parachutes.Count > 0 && parachutes.Any(chute => chute.Status == DoorStatus.Open); // Check if ANY chute is open
- if (parachutesAvailable && altitudeAboveGround < settings.ParachuteAltitude && !areChutesDeployed)
- {
- DeployParachutes();
- // Reduce/kill thrusters if parachutes are main braking
- SetThrusterOverrides(0); // For now, kill all thrusters on chute deploy
- Echo($"Deploying parachutes at {altitudeAboveGround:F0} m.");
- // Transition to Landing immediately as chutes take over primary braking
- SetState(DropPodState.Landing);
- return; // Exit this frame's descent logic to start landing logic
- }
- // If not using chutes or above chute altitude, control descent speed with thrusters
- if (!areChutesDeployed && downThrusters.Count > 0)
- {
- // Simple vertical speed control (P controller)
- double targetVerticalSpeed = 10; // Start with a faster descent speed (m/s)
- if (altitudeAboveGround < settings.ParachuteAltitude * 1.5) targetVerticalSpeed = settings.LandingSpeedThreshold * 2; // Slow down as we get closer
- double speedError = targetVerticalSpeed - verticalSpeed; // Positive error means too slow (need more push down)
- double thrustMagnitude = MathHelper.Clamp(speedError * 0.2 + gravity.Length() / 9.81f, 0, 1); // P control + scaled gravity compensation
- // Scale gravity comp by ~Earth gravity to normalize
- // Apply thrust opposite the gravity vector ('Up' relative to planet) using 'Down' thrusters on grid
- SetThrusterOverrides((float)thrustMagnitude, Base6Directions.Direction.Down); // Down thrusters push Up
- // Basic horizontal braking: Kill horizontal velocity as we descend
- Vector3D gravityDirection = Vector3D.Normalize(gravity);
- Vector3D horizontalVelocity = velocity - verticalSpeed * gravityDirection;
- // Needs SetThrustersInDirection helper or rely on prior RC accuracy
- }
- // Transition to Landing when close enough
- if (altitudeAboveGround < settings.ParachuteAltitude * 1.2 || areChutesDeployed) // Enter landing phase when close or chutes deployed
- {
- SetState(DropPodState.Landing);
- Echo($"Entering landing phase at {altitudeAboveGround:F0} m.");
- return;
- }
- // If GetAltitude and GetVelocity are causing errors, you might need to remove or change this Echo line.
- Echo($"Descending. Altitude (RC): {altitudeAboveGround:F0} m. Vertical Speed: {verticalSpeed:F2} m/s");
- }
- void HandleLanding()
- {
- if (remoteControl == null) { SetState(DropPodState.Error, "Remote Control lost during landing."); return; }
- if (downThrusters.Count == 0 && !parachutesAvailable) { SetState(DropPodState.Error, "No downward thrusters or parachutes for landing."); return; }
- Vector3D gravity = remoteControl.GetNaturalGravity();
- Vector3D currentPos = remoteControl.GetPosition();
- // If GetVelocity is causing errors, you might need to remove or comment out lines using velocity.
- Vector3D velocity = remoteControl.GetVelocity();
- // If GetAltitude is causing errors, you might need to use a different altitude check.
- double altitudeAboveGround = remoteControl.GetAltitude();
- double verticalSpeed = Vector3D.Dot(velocity, Vector3D.Normalize(gravity)); // Positive if moving down
- // Orient grid (continue pointing 'Down' thrusters towards gravity)
- AlignGridToVector(Vector3D.Normalize(gravity), settings.GyroMultiplier);
- // Check for landing success (low speed, low altitude)
- if (Math.Abs(verticalSpeed) < settings.LandingSpeedThreshold && altitudeAboveGround < settings.FinalLandingAltitude)
- {
- SetState(DropPodState.Finished);
- return;
- }
- // Landing Thrust Control (if not relying solely on parachutes)
- bool areChutesDeployed = parachutes.Count > 0 && parachutes.Any(chute => chute.Status == DoorStatus.Open);
- if (!areChutesDeployed && downThrusters.Count > 0) // If no chutes, or they failed, or below chute range
- {
- // PID-like control for vertical speed aiming for -LANDING_SPEED_THRESHOLD (up)
- // This is a simplified P controller
- double targetVerticalVelocity = -settings.LandingSpeedThreshold; // Target velocity UP
- double speedError = targetVerticalVelocity - verticalSpeed; // Positive error means too slow (need more push up)
- double verticalThrust = MathHelper.Clamp(speedError * 1.0 + gravity.Length() / 9.81f, 0, 1); // P control + scaled gravity compensation
- SetThrusterOverrides((float)verticalThrust, Base6Directions.Direction.Down); // Use down thrusters to push up
- // Horizontal braking: Kill horizontal velocity
- Vector3D gravityDirection = Vector3D.Normalize(gravity);
- Vector3D horizontalVelocity = velocity - verticalSpeed * gravityDirection;
- // Needs SetThrustersInDirection helper or rely on prior RC accuracy
- }
- else // If parachutes are deployed, maybe just use thrusters for fine-tuning/horizontal control
- {
- // Use directional thrusters for horizontal movement to zero out horizontal velocity
- Vector3D gravityDirection = Vector3D.Normalize(gravity);
- Vector3D horizontalVelocity = velocity - verticalSpeed * gravityDirection;
- // Needs SetThrustersInDirection helper
- }
- // If GetAltitude and GetVelocity are causing errors, you might need to remove or change this Echo line.
- Echo($"Landing. Altitude (RC): {altitudeAboveGround:F0} m. Vertical Speed: {verticalSpeed:F2} m/s. Total Speed: {velocity.Length():F2} m/s");
- }
- void HandleFinished()
- {
- Echo("Drop pod landed safely.");
- // Could add logic here to turn off components, lock landing gear, etc.
- }
- void HandleError()
- {
- // Error message already displayed by SetState
- }
- // --- Helper Methods ---
- void InitializeBlocks()
- {
- // If GetBlockOfType is causing errors, this is a core API issue.
- // You might need to use GridTerminalSystem.SearchBlocksOfName or GetBlocksOfType
- // with type checks as an alternative, but GetBlockOfType<T>() should work.
- remoteControl = GridTerminalSystem.GetBlockOfType<IMyRemoteControl>();
- // Clear lists before repopulating
- upThrusters.Clear();
- downThrusters.Clear();
- forwardThrusters.Clear();
- backwardThrusters.Clear();
- leftThrusters.Clear();
- rightThrusters.Clear();
- gyros.Clear();
- parachutes.Clear();
- // Find and group thrusters by their thrust direction relative to the grid's forward (+Z)
- // Thruster's WorldMatrix.Backward is the direction the *thrust* is applied
- // Block's Orientation.TransformDirection(Base6Directions.Direction.Backward) gives
- // the direction the thruster is POINTING relative to the grid's local axes.
- // Thrust is applied opposite the direction the nozzle is pointing.
- // Grid +Y is Up, -Y is Down, +X is Right, -X is Left, +Z is Forward, -Z is Backward by convention.
- var allThrusters = new List<IMyThrust>();
- // If GetBlocksOfType is causing errors, this is a core API issue.
- GridTerminalSystem.GetBlocksOfType(allThrusters);
- foreach (var thruster in allThrusters)
- {
- // Direction the thruster is POINTING relative to the grid's axes
- Base6Directions.Direction thrusterLocalPointingDirection = thruster.Orientation.TransformDirection(Base6Directions.Direction.Backward);
- // Thrust is applied in the opposite direction
- Base6Directions.Direction thrustDirection = Base6Directions.GetOppositeDirection(thrusterLocalPointingDirection);
- switch (thrustDirection)
- {
- case Base6Directions.Direction.Up: upThrusters.Add(thruster); break;
- case Base6Directions.Direction.Down: downThrusters.Add(thruster); break;
- case Base6Directions.Direction.Forward: forwardThrusters.Add(thruster); break;
- case Base6Directions.Direction.Backward: backwardThrusters.Add(thruster); break;
- case Base6Directions.Direction.Left: leftThrusters.Add(thruster); break;
- case Base6Directions.Direction.Right: rightThrusters.Add(thruster); break;
- }
- }
- // If GetBlocksOfType is causing errors for Gyros and Parachutes, it's a core API issue.
- GridTerminalSystem.GetBlocksOfType(gyros);
- GridTerminalSystem.GetBlocksOfType(parachutes);
- // Find your release block here...
- // releaseConnector = GridTerminalSystem.GetBlockOfType<IMyShipConnector>("MyConnectorName"); // Example
- Echo($"Found: RC: {remoteControl != null}, Gyros: {gyros.Count}, Chutes: {parachutes.Count}");
- Echo($"Thrusters: U:{upThrusters.Count} D:{downThrusters.Count} F:{forwardThrusters.Count} B:{backwardThrusters.Count} L:{leftThrusters.Count} R:{rightThrusters.Count}");
- }
- // Sets thrust override for thrusters in a specific direction relative to the grid
- // Note: This method takes float percentage, ensure values passed are cast or are floats.
- void SetThrusterOverrides(float percentage, Base6Directions.Direction? direction = null)
- {
- percentage = MathHelper.Clamp(percentage, 0f, 1f); // Ensure percentage is valid
- Action<List<IMyThrust>> setOverride = (list) =>
- {
- foreach (var thruster in list) thruster.ThrustOverridePercentage = percentage;
- };
- if (direction == null) // Apply to all thrusters if direction is null
- {
- setOverride(upThrusters);
- setOverride(downThrusters);
- setOverride(forwardThrusters);
- setOverride(backwardThrusters);
- setOverride(leftThrusters);
- setOverride(rightThrusters);
- }
- else
- {
- switch (direction.Value)
- {
- case Base6Directions.Direction.Up: setOverride(upThrusters); break;
- case Base6Directions.Direction.Down: setOverride(downThrusters); break;
- case Base6Directions.Direction.Forward: setOverride(forwardThrusters); break;
- case Base6Directions.Direction.Backward: setOverride(backwardThrusters); break;
- case Base6Directions.Direction.Left: setOverride(leftThrusters); break;
- case Base6Directions.Direction.Right: setOverride(rightThrusters); break;
- }
- }
- }
- // Attempts to apply thrust towards a given world direction using relevant thrusters
- // This is a simplified approach and might not provide precise control without more math
- void SetThrustersInDirection(Vector3D worldDirection, float strength)
- {
- if (remoteControl == null) return;
- strength = MathHelper.Clamp(strength, 0f, 1f);
- // Convert world direction to grid coordinates
- // Construct a MatrixD from the Matrix3x3 rotation - this should work.
- // If new MatrixD(Matrix3x3) is causing errors, this is a core API issue.
- MatrixD worldToGridRotation = MatrixD.Transpose(new MatrixD(remoteControl.WorldMatrix.Rotation));
- Vector3D gridDirection = Vector3D.TransformNormal(worldDirection, worldToGridRotation);
- // Apply thrust component-wise (simplified)
- // Apply strength * absolute value of component to the thrusters in that grid direction
- SetThrusterOverrides((float)(strength * Math.Max(0, gridDirection.Y)), Base6Directions.Direction.Up); // Push up in grid +Y
- SetThrusterOverrides((float)(strength * Math.Max(0, -gridDirection.Y)), Base6Directions.Direction.Down); // Push down in grid -Y
- SetThrusterOverrides((float)(strength * Math.Max(0, gridDirection.Z)), Base6Directions.Direction.Forward); // Push forward in grid +Z
- SetThrusterOverrides((float)(strength * Math.Max(0, -gridDirection.Z)), Base6Directions.Direction.Backward); // Push backward in grid -Z
- SetThrusterOverrides((float)(strength * Math.Max(0, -gridDirection.X)), Base6Directions.Direction.Left); // Push left in grid -X
- SetThrusterOverrides((float)(strength * Math.Max(0, gridDirection.X)), Base6Directions.Direction.Right); // Push right in grid +X
- }
- void SetGyroOverride(bool enable, Vector3D? pitchYawRollRates = null)
- {
- foreach (var gyro in gyros)
- {
- // If GyroOverride, Pitch, Yaw, Roll are causing errors, this is a core API issue.
- gyro.GyroOverride = enable;
- if (enable && pitchYawRollRates.HasValue)
- {
- // Gyro override takes radians per second
- gyro.Pitch = (float)pitchYawRollRates.Value.X;
- gyro.Yaw = (float)pitchYawRollRates.Value.Y;
- gyro.Roll = (float)pitchYawRollRates.Value.Z;
- }
- else if (enable && !pitchYawRollRates.HasValue)
- {
- // Set rates to 0 if overriding without specific rates
- gyro.Pitch = 0;
- gyro.Yaw = 0;
- gyro.Roll = 0;
- }
- }
- }
- // Aligns the grid's WorldMatrix.Up vector to a desiredWorldUp vector
- // Applies angular velocity using gyros proportional to the alignment error
- void AlignGridToVector(Vector3D desiredDirection, double multiplier)
- {
- if (gyros.Count == 0 || remoteControl == null) return;
- Matrix worldMatrix = remoteControl.WorldMatrix;
- Vector3D forward = worldMatrix.Forward;
- // Calculate rotation
- double angle = Math.Acos(Vector3D.Dot(forward, desiredDirection));
- Vector3D rotationAxis = Vector3D.Cross(forward, desiredDirection);
- if (rotationAxis.LengthSquared() < 0.001)
- {
- SetGyroOverride(true, Vector3D.Zero);
- return;
- }
- rotationAxis = Vector3D.Normalize(rotationAxis);
- // Calculate angular velocity
- Vector3D worldAngularVelocity = rotationAxis * angle * multiplier;
- // Convert to local space
- Vector3D localAngularVelocity = Vector3D.TransformNormal(worldAngularVelocity, Matrix.Transpose(worldMatrix));
- Vector3D gyroRates = new Vector3D(
- -localAngularVelocity.X,
- localAngularVelocity.Y,
- -localAngularVelocity.Z
- );
- SetGyroOverride(true, gyroRates);
- }
- void DeployParachutes()
- {
- foreach (var chute in parachutes)
- {
- // If OpenDoor is causing errors, this is a core API issue.
- chute.OpenDoor();
- // chute.AutoDeploy = true; // Could use auto deploy as well, but manual is more controllable
- }
- }
- bool TryParseGps(string gpsString, out Vector3D coords)
- {
- coords = Vector3D.Zero;
- if (!gpsString.StartsWith("GPS:", StringComparison.OrdinalIgnoreCase)) return false;
- string[] parts = gpsString.Substring(4).Split(':');
- if (parts.Length != 4) return false; // Name, X, Y, Z
- double x = 0.0, y = 0.0, z = 0.0; // Initialized variables
- // Using basic TryParse as Globalization is not available
- // Note: This version might be sensitive to regional number formats (comma vs dot)
- // If double.TryParse is causing errors here, and the CultureInfo version didn't work,
- // it's a fundamental issue with double parsing in your environment.
- if (double.TryParse(parts[1], out x) &&
- double.TryParse(parts[2], out y) &&
- double.TryParse(parts[3], out z))
- {
- coords = new Vector3D(x, y, z);
- return true;
- }
- return false;
- }
Advertisement
Add Comment
Please, Sign In to add comment