· 5 months ago · May 07, 2025, 12:00 PM
1// Move these outside the Program class as top-level methods
2public static Vector3D GetNaturalGravity(IMyRemoteControl rc)
3{
4 return rc.GetTotalGravity();
5}
6
7public static double GetAltitude(IMyRemoteControl rc)
8{
9 Vector3D planetCenter;
10 return rc.TryGetPlanetPosition(out planetCenter) ?
11 Vector3D.Distance(rc.GetPosition(), planetCenter) - rc.GetRadius() : 0;
12}
13
14public static Vector3D GetVelocity(IMyRemoteControl rc)
15{
16 return rc.GetShipVelocities().LinearVelocity;
17}
18
19// Enum for script states
20enum DropPodState
21{
22 Idle, // Waiting for GOAL
23 AwaitingLaunch, // GOAL set, waiting for LAUNCH
24 Initializing, // Finding blocks, loading settings
25 AwaitingPhysicalRelease, // LAUNCH command received, waiting for manual detach ('release' argument)
26 Ascending, // Getting out of gravity
27 HighAltitudeCruise, // Navigating to target at high altitude
28 Descending, // Reducing altitude
29 Landing, // Controlled touchdown
30 Finished, // Mission complete
31 Error // Something went wrong
32}
33
34// Struct to hold settings - Defined only ONCE
35public struct DropPodSettings
36{
37 public double AscentGravityThreshold;
38 public double MinCruiseAltitude;
39 public double DescentAltitude;
40 public double ParachuteAltitude;
41 public double LandingSpeedThreshold;
42 public double FinalLandingAltitude;
43 public double ThrustMultiplier;
44 public double GyroMultiplier; // New setting
45
46 // Default values
47 public DropPodSettings(bool isDefault = true)
48 {
49 AscentGravityThreshold = 0.1;
50 MinCruiseAltitude = 40000; // meters from planet center or surface approx.
51 DescentAltitude = 3000; // meters above ground (RC.GetAltitude())
52 ParachuteAltitude = 800; // meters above ground
53 LandingSpeedThreshold = 5; // m/s
54 FinalLandingAltitude = 3; // meters above ground (allow slight height before landing gear touch)
55 ThrustMultiplier = 0.8;
56 GyroMultiplier = 5.0; // Default gyro sensitivity
57 }
58}
59
60
61DropPodState currentState = DropPodState.Idle;
62Vector3D targetGPS;
63// Store state data for persistence (CurrentState, TargetGPS, etc.)
64string storedState = "";
65
66// --- Block Lists (populated in InitializeBlocks) ---
67IMyRemoteControl remoteControl;
68List<IMyThrust> upThrusters = new List<IMyThrust>();
69List<IMyThrust> downThrusters = new List<IMyThrust>();
70List<IMyThrust> forwardThrusters = new List<IMyThrust>();
71List<IMyThrust> backwardThrusters = new List<IMyThrust>();
72List<IMyThrust> leftThrusters = new List<IMyThrust>();
73List<IMyThrust> rightThrusters = new List<IMyThrust>();
74List<IMyGyro> gyros = new List<IMyGyro>();
75List<IMyParachute> parachutes = new List<IMyParachute>();
76// Add your release mechanism block reference here (Connector/Merge Block)
77// IMyShipConnector releaseConnector;
78
79// --- Configuration Settings (Loaded from Custom Data) ---
80DropPodSettings settings = new DropPodSettings();
81
82bool parachutesAvailable = false;
83bool launched = false; // Flag to indicate if the pod has been released (via 'release' argument)
84
85
86// --- Constructor ---
87public Program()
88{
89 // Set update frequency
90 Runtime.UpdateFrequency = UpdateFrequency.Update10; // Start lower
91
92 // Load state from Storage
93 LoadState();
94
95 // Load settings from Custom Data (or print defaults if empty)
96 LoadSettings();
97
98 // Initial echo
99 Echo("Script loaded. Current State: " + currentState.ToString());
100 EchoSettings();
101
102 // If not in Idle, try to ensure blocks are found and settings are loaded
103 if (currentState != DropPodState.Idle)
104 {
105 // Re-initialize blocks if needed (e.g., after game load)
106 InitializeBlocks(); // This just finds blocks, doesn't change state
107 if (remoteControl == null && currentState != DropPodState.Error)
108 {
109 SetState(DropPodState.Error, "Failed to find Remote Control block on load.");
110 }
111 }
112 else
113 {
114 // In Idle, prompt user
115 Echo("Send 'GOAL GPS:...' to set target.");
116 }
117}
118
119// --- Save/Load State ---
120void SaveState()
121{
122 // Save current state and target GPS to Storage
123 storedState = currentState.ToString() + "|" + (targetGPS != Vector3D.Zero ? targetGPS.ToString() : "");
124 Storage = storedState;
125}
126
127void LoadState()
128{
129 if (!string.IsNullOrWhiteSpace(Storage))
130 {
131 string[] parts = Storage.Split('|');
132 if (parts.Length > 0) Enum.TryParse(parts[0], out currentState);
133 if (parts.Length > 1 && !string.IsNullOrWhiteSpace(parts[1]))
134 {
135 Vector3D.TryParse(parts[1], out targetGPS);
136 }
137 Echo("Loaded state: " + currentState.ToString());
138 if (targetGPS != Vector3D.Zero) Echo("Loaded target: " + targetGPS.ToString());
139
140 // Adjust update frequency immediately based on loaded state if necessary
141 switch (currentState)
142 {
143 case DropPodState.Ascending:
144 case DropPodState.HighAltitudeCruise:
145 case DropPodState.Descending:
146 case DropPodState.Landing:
147 Runtime.UpdateFrequency = UpdateFrequency.Update1;
148 break;
149 default:
150 Runtime.UpdateFrequency = UpdateFrequency.Update10;
151 break;
152 }
153 }
154 else
155 {
156 currentState = DropPodState.Idle;
157 Echo("No saved state found. Starting in Idle.");
158 }
159}
160
161// --- Custom Data Settings Handling ---
162void LoadSettings()
163{
164 if (string.IsNullOrWhiteSpace(Me.CustomData))
165 {
166 Echo("Custom Data is empty. Printing default settings.");
167 PrintDefaultSettings();
168 // Use default settings instance already created
169 }
170 else
171 {
172 Echo("Loading settings from Custom Data...");
173 settings = ParseSettings(Me.CustomData);
174 Echo("Settings loaded.");
175 }
176}
177
178void PrintDefaultSettings()
179{
180 StringBuilder sb = new StringBuilder();
181 sb.AppendLine("# Drop Pod Script Settings");
182 sb.AppendLine("# Edit these values to customize behavior.");
183 sb.AppendLine("# Lines starting with # are comments.");
184 sb.AppendLine("# Use key=value format.");
185 sb.AppendLine("");
186 sb.AppendLine($"AscentGravityThreshold={settings.AscentGravityThreshold}");
187 sb.AppendLine($"MinCruiseAltitude={settings.MinCruiseAltitude}");
188 sb.AppendLine($"DescentAltitude={settings.DescentAltitude}");
189 sb.AppendLine($"ParachuteAltitude={settings.ParachuteAltitude}");
190 sb.AppendLine($"LandingSpeedThreshold={settings.LandingSpeedThreshold}");
191 sb.AppendLine($"FinalLandingAltitude={settings.FinalLandingAltitude}");
192 sb.AppendLine($"ThrustMultiplier={settings.ThrustMultiplier}");
193 sb.AppendLine($"GyroMultiplier={settings.GyroMultiplier}"); // Added setting for gyro sensitivity
194 // Add other settings here as needed
195 Me.CustomData = sb.ToString();
196}
197
198DropPodSettings ParseSettings(string customData)
199{
200 DropPodSettings loadedSettings = new DropPodSettings();
201 var lines = customData.Split('\n');
202 foreach (var line in lines)
203 {
204 var trimmedLine = line.Trim();
205 if (trimmedLine.StartsWith("#") || string.IsNullOrWhiteSpace(trimmedLine)) continue;
206
207 var parts = trimmedLine.Split('=');
208 if (parts.Length != 2) continue;
209
210 string key = parts[0].Trim();
211 string value = parts[1].Trim();
212
213 // Parse specific settings
214 double doubleVal;
215 // Using basic TryParse as Globalization is not available
216 if (double.TryParse(value, out doubleVal))
217 {
218 switch (key)
219 {
220 case "AscentGravityThreshold": loadedSettings.AscentGravityThreshold = doubleVal; break;
221 case "MinCruiseAltitude": loadedSettings.MinCruiseAltitude = doubleVal; break;
222 case "DescentAltitude": loadedSettings.DescentAltitude = doubleVal; break;
223 case "ParachuteAltitude": loadedSettings.ParachuteAltitude = doubleVal; break;
224 case "LandingSpeedThreshold": loadedSettings.LandingSpeedThreshold = doubleVal; break;
225 case "FinalLandingAltitude": loadedSettings.FinalLandingAltitude = doubleVal; break;
226 case "ThrustMultiplier": loadedSettings.ThrustMultiplier = doubleVal; break;
227 case "GyroMultiplier": loadedSettings.GyroMultiplier = doubleVal; break;
228 // Add cases for other double settings
229 }
230 }
231 // Add parsing for other types (int, bool, string) if needed
232 }
233 return loadedSettings;
234}
235
236void EchoSettings()
237{
238 Echo("--- Settings ---");
239 Echo($"Ascent Gravity: {settings.AscentGravityThreshold:F2} m/s²");
240 Echo($"Min Cruise Alt: {settings.MinCruiseAltitude:F0} m");
241 Echo($"Descent Alt: {settings.DescentAltitude:F0} m");
242 Echo($"Parachute Alt: {settings.ParachuteAltitude:F0} m");
243 Echo($"Landing Speed: {settings.LandingSpeedThreshold:F1} m/s");
244 Echo($"Final Land Alt: {settings.FinalLandingAltitude:F0} m");
245 Echo($"Thrust Multi: {settings.ThrustMultiplier:F2}");
246 Echo($"Gyro Multi: {settings.GyroMultiplier:F2}");
247 Echo("----------------");
248}
249
250
251// --- Main Method ---
252public void Main(string argument, UpdateType updateSource)
253{
254 Echo("State: " + currentState.ToString());
255 Echo("Target: " + (targetGPS != Vector3D.Zero ? targetGPS.ToString() : "None"));
256 Echo("Time: " + Runtime.TimeSinceLastRun.TotalSeconds.ToString("F3") + "s"); // Echo last run time
257
258 // Handle arguments based on current state
259 if (argument.Length > 0)
260 {
261 string[] args = argument.Split(' ');
262 string command = args[0].ToUpper();
263
264 switch (currentState)
265 {
266 case DropPodState.Idle:
267 if (command == "GOAL" && args.Length > 1)
268 {
269 string gpsString = argument.Substring("GOAL ".Length).Trim();
270 Vector3D parsedGps;
271 if (TryParseGps(gpsString, out parsedGps))
272 {
273 targetGPS = parsedGps;
274 SetState(DropPodState.AwaitingLaunch);
275 Echo($"Target set to {targetGPS.ToString()}. Send 'LAUNCH' to begin.");
276 }
277 else
278 {
279 Echo("Invalid GPS format. Use 'GOAL GPS:Name:X:Y:Z:'.");
280 }
281 }
282 else { Echo("Send 'GOAL GPS:...' to set target."); }
283 break;
284
285 case DropPodState.AwaitingLaunch:
286 if (command == "LAUNCH")
287 {
288 SetState(DropPodState.Initializing);
289 Echo("Launch command received. Initializing...");
290 }
291 else if (command == "GOAL" && args.Length > 1) // Allow changing target
292 {
293 string gpsString = argument.Substring("GOAL ".Length).Trim();
294 Vector3D parsedGps;
295 if (TryParseGps(gpsString, out parsedGps))
296 {
297 targetGPS = parsedGps;
298 Echo($"Target updated to {targetGPS.ToString()}. Send 'LAUNCH' to begin.");
299 }
300 else
301 {
302 Echo("Invalid GPS format. Use 'GOAL GPS:Name:X:Y:Z:'.");
303 }
304 }
305 else { Echo("Target set. Send 'LAUNCH' to begin."); }
306 break;
307
308 case DropPodState.AwaitingPhysicalRelease:
309 if (command == "RELEASE" || command == "LAUNCHED") // Argument to signal manual detachment
310 {
311 launched = true; // Flag set
312 SetState(DropPodState.Ascending);
313 Echo("Release confirmed. Initiating ascent.");
314 }
315 else { Echo("Waiting for physical release. Disconnect and send 'RELEASE' argument."); }
316 break;
317
318 case DropPodState.Finished:
319 case DropPodState.Error:
320 if (command == "RESET")
321 {
322 targetGPS = Vector3D.Zero; // Clear target
323 launched = false;
324 SetState(DropPodState.Idle);
325 Echo("Script reset. Send 'GOAL GPS:...' to start again.");
326 }
327 break;
328
329 default:
330 // Ignore arguments in active flight states unless specifically handled (e.g., abort?)
331 Echo("Flight in progress. Ignoring command.");
332 break;
333 }
334 }
335
336
337 // State Machine Logic (called every update tick if frequency > None)
338 switch (currentState)
339 {
340 case DropPodState.Initializing:
341 HandleInitializing();
342 break;
343 case DropPodState.AwaitingPhysicalRelease:
344 // Just waiting for the 'release' argument
345 break;
346 case DropPodState.Ascending:
347 HandleAscending();
348 break;
349 case DropPodState.HighAltitudeCruise:
350 HandleHighAltitudeCruise();
351 break;
352 case DropPodState.Descending:
353 HandleDescending();
354 break;
355 case DropPodState.Landing:
356 HandleLanding();
357 break;
358 case DropPodState.Finished:
359 // Do nothing, waiting for reset
360 break;
361 case DropPodState.Error:
362 // Display error, waiting for reset
363 break;
364 }
365
366 // Save state at the end of Main
367 SaveState();
368}
369
370// --- State Handler Methods ---
371
372void SetState(DropPodState newState, string errorMessage = null)
373{
374 currentState = newState;
375 Echo("Transitioned to State: " + currentState.ToString());
376
377 // Actions on state entry
378 switch (newState)
379 {
380 case DropPodState.Idle:
381 case DropPodState.AwaitingLaunch:
382 Runtime.UpdateFrequency = UpdateFrequency.Update10; // Low frequency while waiting
383 SetThrusterOverrides(0);
384 SetGyroOverride(false);
385 // remoteControl?.SetAutoPilotEnabled(false); // Commenting out if RC methods are an issue
386 break;
387 case DropPodState.Initializing:
388 Runtime.UpdateFrequency = UpdateFrequency.Update10; // Still low freq
389 InitializeBlocks(); // Find blocks
390 LoadSettings(); // Reload settings just in case Custom Data was changed
391 break;
392 case DropPodState.AwaitingPhysicalRelease:
393 Runtime.UpdateFrequency = UpdateFrequency.Update10; // Low freq
394 SetThrusterOverrides(0); // Ensure everything is off before release
395 SetGyroOverride(false);
396 // remoteControl?.SetAutoPilotEnabled(false); // Commenting out if RC methods are an issue
397 Echo("Ready for release. Disconnect the pod and send 'RELEASE' argument.");
398 break;
399 case DropPodState.Ascending:
400 Runtime.UpdateFrequency = UpdateFrequency.Update1; // High frequency for flight
401 SetGyroOverride(true); // Enable gyro override for orientation control
402 // remoteControl?.SetAutoPilotEnabled(false); // Commenting out if RC methods are an issue
403 break;
404 case DropPodState.HighAltitudeCruise:
405 Runtime.UpdateFrequency = UpdateFrequency.Update1; // High frequency for navigation/monitoring
406 SetGyroOverride(false); // Let RC handle orientation
407 // RC autopilot setup is done in HandleHighAltitudeCruise
408 break;
409 case DropPodState.Descending:
410 Runtime.UpdateFrequency = UpdateFrequency.Update1; // High frequency for descent control
411 // remoteControl?.SetAutoPilotEnabled(false); // Commenting out if RC methods are an issue
412 SetGyroOverride(true); // Manual gyro control for descent orientation
413 break;
414 case DropPodState.Landing:
415 Runtime.UpdateFrequency = UpdateFrequency.Update1; // Critical frequency for soft landing
416 // Gyros already enabled
417 break;
418 case DropPodState.Finished:
419 Runtime.UpdateFrequency = UpdateFrequency.None; // Stop script execution
420 SetThrusterOverrides(0);
421 SetGyroOverride(false);
422 // remoteControl?.SetAutoPilotEnabled(false); // Commenting out if RC methods are an issue
423 Echo("Mission Complete!");
424 break;
425 case DropPodState.Error:
426 Runtime.UpdateFrequency = UpdateFrequency.None; // Stop script execution
427 SetThrusterOverrides(0);
428 SetGyroOverride(false);
429 // remoteControl?.SetAutoPilotEnabled(false); // Commenting out if RC methods are an issue
430 Echo("Error: " + errorMessage);
431 break;
432 }
433 SaveState(); // Save state change
434}
435
436void HandleInitializing()
437{
438 Echo("Initializing...");
439 // Blocks already found in SetState.
440 // Settings already loaded in SetState.
441
442 if (remoteControl == null)
443 {
444 SetState(DropPodState.Error, "No Remote Control block found.");
445 return;
446 }
447 if (upThrusters.Count == 0)
448 {
449 SetState(DropPodState.Error, "No 'Up' thrusters found. (Thrusters pointing down relative to grid)");
450 return;
451 }
452 // Warnings for missing directional thrusters can be echoed but don't need to be fatal errors
453
454 parachutesAvailable = parachutes.Count > 0;
455 if (parachutesAvailable) Echo("Parachutes found."); else Echo("No parachutes found. Relying on thrusters for landing.");
456
457 // Check if a target was actually set before launching
458 if (targetGPS == Vector3D.Zero)
459 {
460 SetState(DropPodState.Error, "No target GPS set. Use 'GOAL GPS:...' first.");
461 return;
462 }
463
464 Echo("Initialization complete. Ready for physical release.");
465 SetState(DropPodState.AwaitingPhysicalRelease);
466}
467
468// Replace your HandleAscending method
469void HandleAscending()
470{
471 if (remoteControl == null) { SetState(DropPodState.Error, "Remote Control lost during ascent."); return; }
472
473 Vector3D gravity = remoteControl.GetNaturalGravity();
474 double gravityMagnitude = gravity.Length();
475 Vector3D currentPos = remoteControl.GetPosition();
476 double currentAltitude = remoteControl.GetAltitude();
477
478 if (gravityMagnitude < settings.AscentGravityThreshold && currentAltitude > settings.MinCruiseAltitude / 2)
479 {
480 SetThrusterOverrides(0);
481 SetState(DropPodState.HighAltitudeCruise);
482 Echo("Reached space altitude. Initiating cruise.");
483 return;
484 }
485
486 // Orient grid straight up
487 if (gravityMagnitude > 0.01)
488 {
489 Vector3D antiGravity = -Vector3D.Normalize(gravity);
490 AlignGridToVector(antiGravity, settings.GyroMultiplier);
491 }
492 else
493 {
494 SetGyroOverride(true, Vector3D.Zero);
495 }
496
497 SetThrusterOverrides((float)settings.ThrustMultiplier, Base6Directions.Direction.Up);
498 Echo($"Ascending. Gravity: {gravityMagnitude:F2} m/s². Altitude: {currentAltitude:F0} m");
499}
500
501// Update HandleHighAltitudeCruise method
502void HandleHighAltitudeCruise()
503{
504 if (remoteControl == null) { SetState(DropPodState.Error, "Remote Control lost during cruise."); return; }
505
506 Vector3D currentPos = remoteControl.GetPosition();
507 Vector3D targetPos = targetGPS;
508 Vector3D planetCenter;
509 double currentAltitude = GetAltitude(remoteControl);
510
511 // Get planet center for orbital navigation
512 if (!remoteControl.TryGetPlanetPosition(out planetCenter))
513 {
514 SetState(DropPodState.Error, "Cannot determine planet position.");
515 return;
516 }
517
518 // Calculate positions relative to planet center
519 Vector3D currentPosFromCenter = currentPos - planetCenter;
520 Vector3D targetPosFromCenter = targetPos - planetCenter;
521
522 // Calculate desired orbit position (above target at cruise altitude)
523 double desiredOrbitRadius = Math.Max(settings.MinCruiseAltitude, currentPosFromCenter.Length());
524 Vector3D desiredOrbitPos = planetCenter + Vector3D.Normalize(targetPosFromCenter) * desiredOrbitRadius;
525
526 // Calculate if we're roughly above the target
527 double angleToTarget = Math.Acos(
528 Vector3D.Dot(Vector3D.Normalize(currentPosFromCenter), Vector3D.Normalize(targetPosFromCenter))
529 );
530
531 // Convert to degrees for readable comparison
532 double angleToTargetDegrees = angleToTarget * 180 / Math.PI;
533
534 // If we're above target (within 5 degrees) and at appropriate altitude, start descent
535 if (angleToTargetDegrees < 5 && currentAltitude < settings.MinCruiseAltitude * 1.1)
536 {
537 SetState(DropPodState.Descending);
538 Echo($"Above target. Starting descent from {currentAltitude:F0}m");
539 return;
540 }
541
542 // Otherwise, continue orbital movement
543 Vector3D velocityDir = remoteControl.GetShipVelocities().LinearVelocity;
544 Vector3D gravity = GetNaturalGravity(remoteControl);
545
546 // Calculate desired movement direction (tangent to orbit)
547 Vector3D orbitNormal = Vector3D.Cross(currentPosFromCenter, targetPosFromCenter);
548 if (orbitNormal.LengthSquared() > 0.1)
549 {
550 Vector3D desiredDirection = Vector3D.Cross(Vector3D.Normalize(currentPosFromCenter), Vector3D.Normalize(orbitNormal));
551
552 // Align to orbital direction
553 AlignGridToVector(desiredDirection, settings.GyroMultiplier);
554
555 // Apply thrust to maintain orbit
556 double gravityMagnitude = gravity.Length();
557 float thrustLevel = (float)Math.Min(1.0, gravityMagnitude / 9.81);
558 SetThrusterOverrides(thrustLevel, Base6Directions.Direction.Forward);
559 }
560
561 Echo($"Orbiting. Altitude: {currentAltitude:F0}m, Angle to target: {angleToTargetDegrees:F1}°");
562}
563
564void HandleDescending()
565{
566 if (remoteControl == null) { SetState(DropPodState.Error, "Remote Control lost during descent."); return; }
567 if (downThrusters.Count == 0 && !parachutesAvailable) { SetState(DropPodState.Error, "No downward thrusters or parachutes for descent."); return; }
568
569
570 Vector3D gravity = remoteControl.GetNaturalGravity();
571 Vector3D currentPos = remoteControl.GetPosition();
572 // If GetVelocity is causing errors, you might need to remove or comment out lines using velocity.
573 Vector3D velocity = remoteControl.GetVelocity();
574 // If GetAltitude is causing errors, you might need to use a different altitude check.
575 double altitudeAboveGround = remoteControl.GetAltitude();
576 double verticalSpeed = Vector3D.Dot(velocity, Vector3D.Normalize(gravity)); // Positive if moving down
577
578 // Orient grid for descent - point "Down" thrusters towards gravity
579 Vector3D gravityDirection = Vector3D.Normalize(gravity); // Desired 'Down' direction for the grid
580 AlignGridToVector(gravityDirection, settings.GyroMultiplier); // Use gyros to align grid.WorldMatrix.Down with gravityDirection
581
582 // Parachute Deployment
583 bool areChutesDeployed = parachutes.Count > 0 && parachutes.Any(chute => chute.Status == DoorStatus.Open); // Check if ANY chute is open
584 if (parachutesAvailable && altitudeAboveGround < settings.ParachuteAltitude && !areChutesDeployed)
585 {
586 DeployParachutes();
587 // Reduce/kill thrusters if parachutes are main braking
588 SetThrusterOverrides(0); // For now, kill all thrusters on chute deploy
589 Echo($"Deploying parachutes at {altitudeAboveGround:F0} m.");
590 // Transition to Landing immediately as chutes take over primary braking
591 SetState(DropPodState.Landing);
592 return; // Exit this frame's descent logic to start landing logic
593 }
594
595 // If not using chutes or above chute altitude, control descent speed with thrusters
596 if (!areChutesDeployed && downThrusters.Count > 0)
597 {
598 // Simple vertical speed control (P controller)
599 double targetVerticalSpeed = 10; // Start with a faster descent speed (m/s)
600 if (altitudeAboveGround < settings.ParachuteAltitude * 1.5) targetVerticalSpeed = settings.LandingSpeedThreshold * 2; // Slow down as we get closer
601
602 double speedError = targetVerticalSpeed - verticalSpeed; // Positive error means too slow (need more push down)
603 double thrustMagnitude = MathHelper.Clamp(speedError * 0.2 + gravity.Length() / 9.81f, 0, 1); // P control + scaled gravity compensation
604 // Scale gravity comp by ~Earth gravity to normalize
605
606 // Apply thrust opposite the gravity vector ('Up' relative to planet) using 'Down' thrusters on grid
607 SetThrusterOverrides((float)thrustMagnitude, Base6Directions.Direction.Down); // Down thrusters push Up
608
609 // Basic horizontal braking: Kill horizontal velocity as we descend
610 Vector3D gravityDirection = Vector3D.Normalize(gravity);
611 Vector3D horizontalVelocity = velocity - verticalSpeed * gravityDirection;
612 // Needs SetThrustersInDirection helper or rely on prior RC accuracy
613 }
614
615
616 // Transition to Landing when close enough
617 if (altitudeAboveGround < settings.ParachuteAltitude * 1.2 || areChutesDeployed) // Enter landing phase when close or chutes deployed
618 {
619 SetState(DropPodState.Landing);
620 Echo($"Entering landing phase at {altitudeAboveGround:F0} m.");
621 return;
622 }
623
624 // If GetAltitude and GetVelocity are causing errors, you might need to remove or change this Echo line.
625 Echo($"Descending. Altitude (RC): {altitudeAboveGround:F0} m. Vertical Speed: {verticalSpeed:F2} m/s");
626}
627
628void HandleLanding()
629{
630 if (remoteControl == null) { SetState(DropPodState.Error, "Remote Control lost during landing."); return; }
631 if (downThrusters.Count == 0 && !parachutesAvailable) { SetState(DropPodState.Error, "No downward thrusters or parachutes for landing."); return; }
632
633
634 Vector3D gravity = remoteControl.GetNaturalGravity();
635 Vector3D currentPos = remoteControl.GetPosition();
636 // If GetVelocity is causing errors, you might need to remove or comment out lines using velocity.
637 Vector3D velocity = remoteControl.GetVelocity();
638 // If GetAltitude is causing errors, you might need to use a different altitude check.
639 double altitudeAboveGround = remoteControl.GetAltitude();
640 double verticalSpeed = Vector3D.Dot(velocity, Vector3D.Normalize(gravity)); // Positive if moving down
641
642 // Orient grid (continue pointing 'Down' thrusters towards gravity)
643 AlignGridToVector(Vector3D.Normalize(gravity), settings.GyroMultiplier);
644
645 // Check for landing success (low speed, low altitude)
646 if (Math.Abs(verticalSpeed) < settings.LandingSpeedThreshold && altitudeAboveGround < settings.FinalLandingAltitude)
647 {
648 SetState(DropPodState.Finished);
649 return;
650 }
651
652 // Landing Thrust Control (if not relying solely on parachutes)
653 bool areChutesDeployed = parachutes.Count > 0 && parachutes.Any(chute => chute.Status == DoorStatus.Open);
654 if (!areChutesDeployed && downThrusters.Count > 0) // If no chutes, or they failed, or below chute range
655 {
656 // PID-like control for vertical speed aiming for -LANDING_SPEED_THRESHOLD (up)
657 // This is a simplified P controller
658 double targetVerticalVelocity = -settings.LandingSpeedThreshold; // Target velocity UP
659 double speedError = targetVerticalVelocity - verticalSpeed; // Positive error means too slow (need more push up)
660 double verticalThrust = MathHelper.Clamp(speedError * 1.0 + gravity.Length() / 9.81f, 0, 1); // P control + scaled gravity compensation
661
662 SetThrusterOverrides((float)verticalThrust, Base6Directions.Direction.Down); // Use down thrusters to push up
663
664 // Horizontal braking: Kill horizontal velocity
665 Vector3D gravityDirection = Vector3D.Normalize(gravity);
666 Vector3D horizontalVelocity = velocity - verticalSpeed * gravityDirection;
667 // Needs SetThrustersInDirection helper or rely on prior RC accuracy
668 }
669 else // If parachutes are deployed, maybe just use thrusters for fine-tuning/horizontal control
670 {
671 // Use directional thrusters for horizontal movement to zero out horizontal velocity
672 Vector3D gravityDirection = Vector3D.Normalize(gravity);
673 Vector3D horizontalVelocity = velocity - verticalSpeed * gravityDirection;
674 // Needs SetThrustersInDirection helper
675 }
676
677 // If GetAltitude and GetVelocity are causing errors, you might need to remove or change this Echo line.
678 Echo($"Landing. Altitude (RC): {altitudeAboveGround:F0} m. Vertical Speed: {verticalSpeed:F2} m/s. Total Speed: {velocity.Length():F2} m/s");
679}
680
681void HandleFinished()
682{
683 Echo("Drop pod landed safely.");
684 // Could add logic here to turn off components, lock landing gear, etc.
685}
686
687void HandleError()
688{
689 // Error message already displayed by SetState
690}
691
692
693// --- Helper Methods ---
694
695void InitializeBlocks()
696{
697 // If GetBlockOfType is causing errors, this is a core API issue.
698 // You might need to use GridTerminalSystem.SearchBlocksOfName or GetBlocksOfType
699 // with type checks as an alternative, but GetBlockOfType<T>() should work.
700 remoteControl = GridTerminalSystem.GetBlockOfType<IMyRemoteControl>();
701
702 // Clear lists before repopulating
703 upThrusters.Clear();
704 downThrusters.Clear();
705 forwardThrusters.Clear();
706 backwardThrusters.Clear();
707 leftThrusters.Clear();
708 rightThrusters.Clear();
709 gyros.Clear();
710 parachutes.Clear();
711
712 // Find and group thrusters by their thrust direction relative to the grid's forward (+Z)
713 // Thruster's WorldMatrix.Backward is the direction the *thrust* is applied
714 // Block's Orientation.TransformDirection(Base6Directions.Direction.Backward) gives
715 // the direction the thruster is POINTING relative to the grid's local axes.
716 // Thrust is applied opposite the direction the nozzle is pointing.
717 // Grid +Y is Up, -Y is Down, +X is Right, -X is Left, +Z is Forward, -Z is Backward by convention.
718
719 var allThrusters = new List<IMyThrust>();
720 // If GetBlocksOfType is causing errors, this is a core API issue.
721 GridTerminalSystem.GetBlocksOfType(allThrusters);
722
723 foreach (var thruster in allThrusters)
724 {
725 // Direction the thruster is POINTING relative to the grid's axes
726 Base6Directions.Direction thrusterLocalPointingDirection = thruster.Orientation.TransformDirection(Base6Directions.Direction.Backward);
727
728 // Thrust is applied in the opposite direction
729 Base6Directions.Direction thrustDirection = Base6Directions.GetOppositeDirection(thrusterLocalPointingDirection);
730
731 switch (thrustDirection)
732 {
733 case Base6Directions.Direction.Up: upThrusters.Add(thruster); break;
734 case Base6Directions.Direction.Down: downThrusters.Add(thruster); break;
735 case Base6Directions.Direction.Forward: forwardThrusters.Add(thruster); break;
736 case Base6Directions.Direction.Backward: backwardThrusters.Add(thruster); break;
737 case Base6Directions.Direction.Left: leftThrusters.Add(thruster); break;
738 case Base6Directions.Direction.Right: rightThrusters.Add(thruster); break;
739 }
740 }
741
742 // If GetBlocksOfType is causing errors for Gyros and Parachutes, it's a core API issue.
743 GridTerminalSystem.GetBlocksOfType(gyros);
744 GridTerminalSystem.GetBlocksOfType(parachutes);
745 // Find your release block here...
746 // releaseConnector = GridTerminalSystem.GetBlockOfType<IMyShipConnector>("MyConnectorName"); // Example
747
748 Echo($"Found: RC: {remoteControl != null}, Gyros: {gyros.Count}, Chutes: {parachutes.Count}");
749 Echo($"Thrusters: U:{upThrusters.Count} D:{downThrusters.Count} F:{forwardThrusters.Count} B:{backwardThrusters.Count} L:{leftThrusters.Count} R:{rightThrusters.Count}");
750}
751
752// Sets thrust override for thrusters in a specific direction relative to the grid
753// Note: This method takes float percentage, ensure values passed are cast or are floats.
754void SetThrusterOverrides(float percentage, Base6Directions.Direction? direction = null)
755{
756 percentage = MathHelper.Clamp(percentage, 0f, 1f); // Ensure percentage is valid
757
758 Action<List<IMyThrust>> setOverride = (list) =>
759 {
760 foreach (var thruster in list) thruster.ThrustOverridePercentage = percentage;
761 };
762
763 if (direction == null) // Apply to all thrusters if direction is null
764 {
765 setOverride(upThrusters);
766 setOverride(downThrusters);
767 setOverride(forwardThrusters);
768 setOverride(backwardThrusters);
769 setOverride(leftThrusters);
770 setOverride(rightThrusters);
771 }
772 else
773 {
774 switch (direction.Value)
775 {
776 case Base6Directions.Direction.Up: setOverride(upThrusters); break;
777 case Base6Directions.Direction.Down: setOverride(downThrusters); break;
778 case Base6Directions.Direction.Forward: setOverride(forwardThrusters); break;
779 case Base6Directions.Direction.Backward: setOverride(backwardThrusters); break;
780 case Base6Directions.Direction.Left: setOverride(leftThrusters); break;
781 case Base6Directions.Direction.Right: setOverride(rightThrusters); break;
782 }
783 }
784}
785
786// Attempts to apply thrust towards a given world direction using relevant thrusters
787// This is a simplified approach and might not provide precise control without more math
788void SetThrustersInDirection(Vector3D worldDirection, float strength)
789{
790 if (remoteControl == null) return;
791
792 strength = MathHelper.Clamp(strength, 0f, 1f);
793
794 // Convert world direction to grid coordinates
795 // Construct a MatrixD from the Matrix3x3 rotation - this should work.
796 // If new MatrixD(Matrix3x3) is causing errors, this is a core API issue.
797 MatrixD worldToGridRotation = MatrixD.Transpose(new MatrixD(remoteControl.WorldMatrix.Rotation));
798 Vector3D gridDirection = Vector3D.TransformNormal(worldDirection, worldToGridRotation);
799
800 // Apply thrust component-wise (simplified)
801 // Apply strength * absolute value of component to the thrusters in that grid direction
802 SetThrusterOverrides((float)(strength * Math.Max(0, gridDirection.Y)), Base6Directions.Direction.Up); // Push up in grid +Y
803 SetThrusterOverrides((float)(strength * Math.Max(0, -gridDirection.Y)), Base6Directions.Direction.Down); // Push down in grid -Y
804 SetThrusterOverrides((float)(strength * Math.Max(0, gridDirection.Z)), Base6Directions.Direction.Forward); // Push forward in grid +Z
805 SetThrusterOverrides((float)(strength * Math.Max(0, -gridDirection.Z)), Base6Directions.Direction.Backward); // Push backward in grid -Z
806 SetThrusterOverrides((float)(strength * Math.Max(0, -gridDirection.X)), Base6Directions.Direction.Left); // Push left in grid -X
807 SetThrusterOverrides((float)(strength * Math.Max(0, gridDirection.X)), Base6Directions.Direction.Right); // Push right in grid +X
808}
809
810
811void SetGyroOverride(bool enable, Vector3D? pitchYawRollRates = null)
812{
813 foreach (var gyro in gyros)
814 {
815 // If GyroOverride, Pitch, Yaw, Roll are causing errors, this is a core API issue.
816 gyro.GyroOverride = enable;
817 if (enable && pitchYawRollRates.HasValue)
818 {
819 // Gyro override takes radians per second
820 gyro.Pitch = (float)pitchYawRollRates.Value.X;
821 gyro.Yaw = (float)pitchYawRollRates.Value.Y;
822 gyro.Roll = (float)pitchYawRollRates.Value.Z;
823 }
824 else if (enable && !pitchYawRollRates.HasValue)
825 {
826 // Set rates to 0 if overriding without specific rates
827 gyro.Pitch = 0;
828 gyro.Yaw = 0;
829 gyro.Roll = 0;
830 }
831 }
832}
833
834// Aligns the grid's WorldMatrix.Up vector to a desiredWorldUp vector
835// Applies angular velocity using gyros proportional to the alignment error
836void AlignGridToVector(Vector3D desiredDirection, double multiplier)
837{
838 if (gyros.Count == 0 || remoteControl == null) return;
839
840 Matrix worldMatrix = remoteControl.WorldMatrix;
841 Vector3D forward = worldMatrix.Forward;
842
843 // Calculate rotation
844 double angle = Math.Acos(Vector3D.Dot(forward, desiredDirection));
845 Vector3D rotationAxis = Vector3D.Cross(forward, desiredDirection);
846
847 if (rotationAxis.LengthSquared() < 0.001)
848 {
849 SetGyroOverride(true, Vector3D.Zero);
850 return;
851 }
852
853 rotationAxis = Vector3D.Normalize(rotationAxis);
854
855 // Calculate angular velocity
856 Vector3D worldAngularVelocity = rotationAxis * angle * multiplier;
857
858 // Convert to local space
859 Vector3D localAngularVelocity = Vector3D.TransformNormal(worldAngularVelocity, Matrix.Transpose(worldMatrix));
860
861 Vector3D gyroRates = new Vector3D(
862 -localAngularVelocity.X,
863 localAngularVelocity.Y,
864 -localAngularVelocity.Z
865 );
866
867 SetGyroOverride(true, gyroRates);
868}
869
870
871void DeployParachutes()
872{
873 foreach (var chute in parachutes)
874 {
875 // If OpenDoor is causing errors, this is a core API issue.
876 chute.OpenDoor();
877 // chute.AutoDeploy = true; // Could use auto deploy as well, but manual is more controllable
878 }
879}
880
881bool TryParseGps(string gpsString, out Vector3D coords)
882{
883 coords = Vector3D.Zero;
884 if (!gpsString.StartsWith("GPS:", StringComparison.OrdinalIgnoreCase)) return false;
885
886 string[] parts = gpsString.Substring(4).Split(':');
887 if (parts.Length != 4) return false; // Name, X, Y, Z
888
889 double x = 0.0, y = 0.0, z = 0.0; // Initialized variables
890
891 // Using basic TryParse as Globalization is not available
892 // Note: This version might be sensitive to regional number formats (comma vs dot)
893 // If double.TryParse is causing errors here, and the CultureInfo version didn't work,
894 // it's a fundamental issue with double parsing in your environment.
895 if (double.TryParse(parts[1], out x) &&
896 double.TryParse(parts[2], out y) &&
897 double.TryParse(parts[3], out z))
898 {
899 coords = new Vector3D(x, y, z);
900 return true;
901 }
902
903 return false;
904}