public static void CreateMissionProfile(Vessel vessel, FlightRecording recording) { var profile = MissionProfile.CreateFromRecording(vessel, recording); // Make the profile-name unique to use it as a key: profile.profileName = MissionController.GetUniqueProfileName(profile.profileName); MissionController.missionProfiles.Add(profile.profileName, profile); Log.Warning("saved new mission profile '" + profile.profileName + "'" + " Total of " + MissionController.missionProfiles.Count + " missions saved"); }
public static void CreateMissionProfile(Vessel vessel, FlightRecording recording) { var profile = MissionProfile.CreateFromRecording(vessel, recording); // Make the profile-name unique to use it as a key: profile.profileName = MissionController.GetUniqueProfileName(profile.profileName); MissionController.missionProfiles.Add(profile.profileName, profile); Debug.Log("[KSTS] saved new mission profile '" + profile.profileName + "'"); }
public static void ChangeMissionProfileName(string name, string newName) { MissionProfile profile = null; if (!MissionController.missionProfiles.TryGetValue(name, out profile)) { return; } MissionController.missionProfiles.Remove(name); profile.profileName = MissionController.GetUniqueProfileName(newName); MissionController.missionProfiles.Add(profile.profileName, profile); }
public override void OnSave(ConfigNode node) { try { FlightRecoorder.SaveRecordings(node); MissionController.SaveMissions(node); } catch (Exception e) { Debug.LogError("[KSTS] OnSave(): " + e.ToString()); } }
public override void OnLoad(ConfigNode node) { try { FlightRecorder.LoadRecordings(node); MissionController.LoadMissions(node); GUI.Reset(); } catch (Exception e) { Debug.LogError("[KSTS] OnLoad(): " + e.ToString()); } }
// Is called when this Addon is first loaded to initializes all values (eg registration of event-handlers and creation // of original-stats library). public void Awake() { try { FlightRecoorder.Initialize(); MissionController.Initialize(); // Build dictionary of all parts for easier access: if (KSTS.partDictionary == null) { KSTS.partDictionary = new Dictionary <string, AvailablePart>(); foreach (AvailablePart part in PartLoader.LoadedPartsList) { if (KSTS.partDictionary.ContainsKey(part.name.ToString())) { Debug.LogError("[KSTS] duplicate part-name '" + part.name.ToString() + "'"); continue; } KSTS.partDictionary.Add(part.name.ToString(), part); } } // Build a dictionay of all resources for easier access: if (KSTS.resourceDictionary == null) { KSTS.resourceDictionary = new Dictionary <string, PartResourceDefinition>(); foreach (PartResourceDefinition resourceDefinition in PartResourceLibrary.Instance.resourceDefinitions) { KSTS.resourceDictionary.Add(resourceDefinition.name.ToString(), resourceDefinition); } } // Invoke the timer-function every second to run background-code: if (!IsInvoking("Timer")) { InvokeRepeating("Timer", 1, 1); } // Execute the following code only once: if (KSTS.initialized) { return; } DontDestroyOnLoad(this); KSTS.initialized = true; } catch (Exception e) { Debug.LogError("[KSTS] Awake(): " + e.ToString()); } }
public static bool Display() { currentCost = 0; var ready = DisplayInner(); var launch = DisplayFooter(currentCost, ready); if (launch) { // Start the mission: MissionController.StartMission(Mission.CreateConstruction(shipName, payloadShipSelector.payload.template, targetVesselSelector.targetVessel, missionProfileSelector.selectedProfile, crewTransferSelector.crewToDeliver, flagSelector.flagURL, constructionTime)); KSTS.AddFunds(-currentCost); Reset(); return(true); } return(false); }
public static bool Display() { currentCost = 0; var ready = DisplayInner(); var launch = DisplayFooter(currentCost, ready); if (launch) { // Start the mission: MissionController.StartMission(Mission.CreateDeployment(shipName, payloadShipSelector.payload.template, orbitEditor.GetOrbit(), missionProfileSelector.selectedProfile, crewTransferSelector.crewToDeliver, flagSelector.flagURL)); KSTS.AddFunds(-currentCost); Reset(); return(true); } return(false); }
public void Timer() { try { // Don't update while not in game: if (HighLogic.LoadedScene == GameScenes.MAINMENU || HighLogic.LoadedScene == GameScenes.CREDITS || HighLogic.LoadedScene == GameScenes.SETTINGS) { return; } // Call all background-jobs: FlightRecorder.Timer(); MissionController.Timer(); } catch (Exception e) { Debug.LogError("[KSTS] Timer(): " + e.ToString()); } }
public static bool Display() { currentCost = 0; var ready = DisplayInner(); var launch = DisplayFooter(currentCost, ready); if (launch) { MissionController.StartMission(Mission.CreateTransport( targetVesselSelector.targetVessel, missionProfileSelector.selectedProfile, payloadResourceSelector.selectedResources, payloadResourceSelector.selectedCrewTransfers )); KSTS.AddFunds(-currentCost); Reset(); return(true); } return(false); }
// Closes the active recording for the given vessel and creates a mission-profile from it: public static void SaveRecording(Vessel vessel) { try { FlightRecording recording; if (!FlightRecorder.flightRecordings.TryGetValue(vessel.id.ToString(), out recording)) { return; } if (!recording.CanFinish()) { return; } MissionController.CreateMissionProfile(vessel, recording); FlightRecorder.flightRecordings.Remove(vessel.id.ToString()); } catch (Exception e) { Debug.LogError("[KSTS] SaveRecording(): " + e.ToString()); } }
public override void OnLoad(ConfigNode node) { Log.Warning("KSTS: OnLoad"); try { FlightRecorder.LoadRecordings(node); MissionController.LoadMissions(node); if (node.HasValue("useKACifAvailable")) { MissionController.useKACifAvailable = bool.Parse(node.GetValue("useKACifAvailable")); } if (node.HasValue("useStockAlarmClock")) { MissionController.useStockAlarmClock = bool.Parse(node.GetValue("useStockAlarmClock")); } GUI.Reset(); } catch (Exception e) { Debug.LogError("OnLoad(): " + e.ToString()); } }
public static bool Display() { currentCost = 0; bool ready = DisplayInner(); bool launch = DisplayFooter(currentCost, ready); if (launch) { if (!GUIOrbitEditor.CheckOrbitClear(orbitEditor.GetOrbit())) { // The selected orbit is used by another vessel, abort: ScreenMessages.PostScreenMessage("Selected orbit already in use by another vessel, aborting mission!"); } else { // The orbit is clear, start the mission: MissionController.StartMission(Mission.CreateDeployment(shipName, payloadShipSelector.payload.template, orbitEditor.GetOrbit(), missionProfileSelector.selectedProfile, crewTransferSelector.crewToDeliver, flagSelector.flagURL)); KSTS.AddFunds(-currentCost); Reset(); return(true); } } return(false); }
// Shows a list of all available crew-members which the player can choose to transport and returns true, if the selection is valid: public bool DisplayList() { var targetCrewCapacity = 0; if (targetVessel != null) { targetCrewCapacity = TargetVessel.GetCrewCapacity(targetVessel); } else if (targetTemplate != null) { targetCrewCapacity = targetTemplate.GetCrewCapacity(); } if (missionProfile.crewCapacity == 0 && missionProfile.missionType == MissionProfileType.TRANSPORT) // We only care about the seats on the transport-vessel during transport-missions. { GUILayout.Label("There are no available seats in the selected mission-profile."); return(true); } else if (targetCrewCapacity == 0) // If the target has no seats, we can't transport anyone. { GUILayout.Label("The selected target-vessel can not hold any crew-members."); return(true); } else { // Target-vessel summary: var targetOverload = false; string headline; if (targetVessel != null) // Existing vessel (in- & outboud transfers possible) { // Display capacity and transfer deltas: var targetVesselCrew = TargetVessel.GetCrew(targetVessel); if (targetVesselCrew.Count + crewToDeliver.Count - crewToCollect.Count > targetCrewCapacity) { targetOverload = true; } headline = "<b>" + targetVessel.vesselName + ":</b> " + targetVesselCrew.Count.ToString() + "/" + targetCrewCapacity.ToString(); var transfers = " inbound: " + crewToDeliver.Count.ToString("+#;-#;0") + ", outbound: " + (-crewToCollect.Count).ToString("+#;-#;0"); if (targetOverload) { transfers = "<color=#FF0000>" + transfers + "</color>"; } GUILayout.Label(headline + transfers); // Display Crew that is stationed on the target vessel: foreach (var kerbonaut in targetVesselCrew) { var details = " <b>" + kerbonaut.name + "</b> (Level " + kerbonaut.experienceLevel.ToString() + " " + kerbonaut.trait + ")"; if (missionProfile.oneWayMission || MissionController.GetKerbonautsMission(kerbonaut.name) != null || missionProfile.missionType != MissionProfileType.TRANSPORT) { GUILayout.Label(" • " + details); // Do not transport kerbals, which are flagged for another mission or there isn't even a return-trip or transport happening } else { var selected = GUILayout.Toggle(crewToCollect.Contains(kerbonaut.name), details); if (selected && !crewToCollect.Contains(kerbonaut.name)) { crewToCollect.Add(kerbonaut.name); } else if (!selected && crewToCollect.Contains(kerbonaut.name)) { crewToCollect.Remove(kerbonaut.name); } } } GUILayout.Label(""); } else if (targetTemplate != null) // New vessel (only inbound transfers possible) { // Display capacity: if (crewToDeliver.Count > targetCrewCapacity) { targetOverload = true; } headline = "<b>" + targetTemplate.template.shipName + ":</b> "; var seats = crewToDeliver.Count.ToString() + " / " + targetCrewCapacity.ToString() + " seat"; if (targetCrewCapacity != 1) { seats += "s"; } if (targetOverload) { seats = "<color=#FF0000>" + seats + "</color>"; } GUILayout.Label(headline + seats); } // Display Transport-vessel summary, if this is a transport-mission: var transportOutboundOverload = false; var transportInboundOverload = false; if (missionProfile.missionType == MissionProfileType.TRANSPORT) { if (crewToDeliver.Count > missionProfile.crewCapacity) { transportOutboundOverload = true; } if (crewToCollect.Count > missionProfile.crewCapacity) { transportInboundOverload = true; } headline = "<b>" + missionProfile.vesselName + ":</b> "; var outbound = "outbound: " + crewToDeliver.Count.ToString() + "/" + missionProfile.crewCapacity.ToString(); if (transportOutboundOverload) { outbound = "<color=#FF0000>" + outbound + "</color>"; } var inbound = ""; if (!missionProfile.oneWayMission) { inbound = ", inbound: " + crewToCollect.Count.ToString() + "/" + missionProfile.crewCapacity.ToString(); if (transportInboundOverload) { inbound = "<color=#FF0000>" + inbound + "</color>"; } } else { inbound += ", inbound: -"; } GUILayout.Label(headline + outbound + inbound); } // Display crew-rowster: foreach (var kerbonaut in GetCrewRoster()) { var details = " <b>" + kerbonaut.name + "</b> (Level " + kerbonaut.experienceLevel.ToString() + " " + kerbonaut.trait.ToString() + ")"; if (MissionController.GetKerbonautsMission(kerbonaut.name) != null) { GUILayout.Label(" • " + details); // Do not transport kerbals, which are flagged for another mission } else { var selected = GUILayout.Toggle(crewToDeliver.Contains(kerbonaut.name), details); if (selected && !crewToDeliver.Contains(kerbonaut.name)) { crewToDeliver.Add(kerbonaut.name); } else if (!selected && crewToDeliver.Contains(kerbonaut.name)) { crewToDeliver.Remove(kerbonaut.name); } } } // Check if the selection is valid (it neither overloads the target nor the transport): if (!targetOverload && !transportOutboundOverload && !transportInboundOverload) { return(true); } return(false); } }
public static void Display() { if (!initialized) { Initialize(); } Vessel vessel = FlightGlobals.ActiveVessel; FlightRecording recording = null; if (vessel) { recording = FlightRecoorder.GetFlightRecording(vessel); } if (!vessel || recording == null) { Reset(); // Show list of recorded profiles: missionProfileSelector.DisplayList(); if (missionProfileSelector.selectedProfile != null) { if (missionProfileSelector.selectedProfile != lastSelectedProfile) { // The selecte profile was switched: lastSelectedProfile = missionProfileSelector.selectedProfile; newMissionProfileName = missionProfileSelector.selectedProfile.profileName; } GUILayout.BeginHorizontal(); GUILayout.Label("<size=14><b>Profile name:</b></size>", new GUIStyle(GUI.labelStyle) { stretchWidth = false }); newMissionProfileName = GUILayout.TextField(newMissionProfileName, new GUIStyle(GUI.textFieldStyle) { stretchWidth = true }); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); if (GUILayout.Button("Save Profile", GUI.buttonStyle)) { MissionController.ChangeMissionProfileName(missionProfileSelector.selectedProfile.profileName, newMissionProfileName); missionProfileSelector = new GUIMissionProfileSelector(); // Deselect & Reset } if (GUILayout.Button("Delete Profile", GUI.buttonStyle)) { MissionController.DeleteMissionProfile(missionProfileSelector.selectedProfile.profileName); missionProfileSelector = new GUIMissionProfileSelector(); // Deselect & Reset } GUILayout.EndHorizontal(); } } else { // During the recording, allow the player to change the name of the new flight-profile: GUILayout.BeginHorizontal(); GUILayout.Label("<size=14><b>Profile name:</b></size>", new GUIStyle(GUI.labelStyle) { stretchWidth = false }); if (recording.status != FlightRecordingStatus.PRELAUNCH) { recording.profileName = GUILayout.TextField(recording.profileName, new GUIStyle(GUI.textFieldStyle) { stretchWidth = true }); } else { GUILayout.Label(recording.profileName, new GUIStyle(GUI.labelStyle) { stretchWidth = true }); } GUILayout.EndHorizontal(); // Display all Information about the current recording: GUILayout.BeginScrollView(new Vector2(0, 0), new GUIStyle(GUI.scrollStyle) { stretchHeight = true }); List <KeyValuePair <string, string> > displayAttributes = recording.GetDisplayAttributes(); foreach (KeyValuePair <string, string> displayAttribute in displayAttributes) { GUILayout.BeginHorizontal(); GUILayout.Label("<b>" + displayAttribute.Key + "</b>"); GUILayout.Label(displayAttribute.Value + " ", new GUIStyle(GUI.labelStyle) { alignment = TextAnchor.MiddleRight }); GUILayout.EndHorizontal(); } GUILayout.EndScrollView(); // Display payload selector: if (recording.status == FlightRecordingStatus.ASCENDING || recording.status == FlightRecordingStatus.PRELAUNCH) { GUILayout.BeginHorizontal(); GUILayout.Label("<size=14><b>Mission Type:</b></size>"); string[] missionTypeStrings = new string[] { "Deploy", "Transport" }; selectedMissionTypeTab = GUILayout.Toolbar(selectedMissionTypeTab, missionTypeStrings); GUILayout.EndHorizontal(); scrollPos = GUILayout.BeginScrollView(scrollPos, GUI.scrollStyle, GUILayout.Height(210), GUILayout.MaxHeight(210)); if (selectedMissionTypeTab == 0) { // Show all deployable payloads: if (!recording.CanPerformMission(MissionProfileType.DEPLOY)) { GUILayout.Label("Deployment missions can only be performed if the vessel has detachable parts which haven't been used during the flight (no resource consumption, inactive, uncrewed, etc)."); } else { // Show all detachable subassemblies: foreach (PayloadAssembly payloadAssembly in recording.GetPayloadAssemblies()) { GUILayout.BeginHorizontal(); if (GUILayout.Toggle(selectedPayloadAssemblyIds.Contains(payloadAssembly.id), "<b>" + payloadAssembly.name + "</b>")) { if (!selectedPayloadAssemblyIds.Contains(payloadAssembly.id)) { selectedPayloadAssemblyIds.Add(payloadAssembly.id); } } else if (selectedPayloadAssemblyIds.Contains(payloadAssembly.id)) { selectedPayloadAssemblyIds.Remove(payloadAssembly.id); } GUILayout.Label(payloadAssembly.partCount.ToString() + " parts, " + payloadAssembly.mass.ToString("#,##0.00 t") + " ", new GUIStyle(GUI.labelStyle) { alignment = TextAnchor.MiddleRight }); GUILayout.EndHorizontal(); } } } else { if (!recording.CanPerformMission(MissionProfileType.TRANSPORT)) { GUILayout.Label("Transport missions can only be performed with vessels which have at least one docking port as well as RCS thrusters."); } else { // Show all payload-resources: double totalPayloadMass = 0; foreach (PayloadResource payloadResource in recording.GetPayloadResources()) { double selectedAmount = 0; selectedPayloadDeploymentResources.TryGetValue(payloadResource.name, out selectedAmount); GUILayout.BeginHorizontal(); GUILayout.Label("<b>" + payloadResource.name + "</b>"); GUILayout.Label(((selectedAmount / payloadResource.amount) * 100).ToString("0.00") + "% (" + selectedAmount.ToString("#,##0.00") + " / " + payloadResource.amount.ToString("#,##0.00") + "): " + (selectedAmount * payloadResource.mass).ToString("#,##0.00 t") + " ", new GUIStyle(GUI.labelStyle) { alignment = TextAnchor.MiddleRight }); GUILayout.EndHorizontal(); selectedAmount = GUILayout.HorizontalSlider((float)selectedAmount, 0, (float)payloadResource.amount); if (selectedAmount < 0) { selectedAmount = 0; } if (selectedAmount > payloadResource.amount) { selectedAmount = payloadResource.amount; } if (payloadResource.amount - selectedAmount < 0.01) { selectedAmount = payloadResource.amount; } totalPayloadMass += selectedAmount * payloadResource.mass; if (selectedPayloadDeploymentResources.ContainsKey(payloadResource.name)) { selectedPayloadDeploymentResources[payloadResource.name] = selectedAmount; } else { selectedPayloadDeploymentResources.Add(payloadResource.name, selectedAmount); } } GUILayout.BeginHorizontal(); GUILayout.Label("<b>Total Payload</b>"); GUILayout.Label(totalPayloadMass.ToString("#,##0.00 t "), new GUIStyle(GUI.labelStyle) { alignment = TextAnchor.MiddleRight }); GUILayout.EndHorizontal(); } } GUILayout.EndScrollView(); } // Bottom pane with action-buttons: GUILayout.BeginHorizontal(); if (recording.status == FlightRecordingStatus.PRELAUNCH && GUILayout.Button("Record", GUI.buttonStyle)) { // Start Recording: FlightRecoorder.StartRecording(vessel); } if (recording.CanDeploy() && GUILayout.Button("Release Payload", GUI.buttonStyle)) { if (selectedMissionTypeTab == 0) { List <PayloadAssembly> payloadAssemblies = recording.GetPayloadAssemblies(); List <PayloadAssembly> selectedPayloadAssemblies = new List <PayloadAssembly>(); foreach (PayloadAssembly payloadAssembly in recording.GetPayloadAssemblies()) { if (selectedPayloadAssemblyIds.Contains(payloadAssembly.id)) { selectedPayloadAssemblies.Add(payloadAssembly); } } if (selectedPayloadAssemblies.Count > 0) { recording.DeployPayloadAssembly(selectedPayloadAssemblies); } } else { recording.DeployPayloadResources(selectedPayloadDeploymentResources); } } if (recording.CanFinish() && GUILayout.Button("Stop & Save", GUI.buttonStyle)) { // Stop recording and create a mission-profile: FlightRecoorder.SaveRecording(vessel); } if (recording.status != FlightRecordingStatus.PRELAUNCH && GUILayout.Button("Abort", GUI.buttonStyle)) { // Cancel runnig recording: FlightRecoorder.CancelRecording(vessel); } GUILayout.EndHorizontal(); } }
// Is called when this Addon is first loaded to initializes all values (eg registration of event-handlers and creation // of original-stats library). public void Awake() { try { FlightRecorder.Initialize(); MissionController.Initialize(); // Build dictionary of all parts for easier access: if (KSTS.partDictionary == null) { KSTS.partDictionary = new Dictionary <string, AvailablePart>(); foreach (var part in PartLoader.LoadedPartsList) { if (KSTS.partDictionary.ContainsKey(part.name.ToString())) { Debug.LogError("duplicate part-name '" + part.name.ToString() + "'"); continue; } KSTS.partDictionary.Add(part.name.ToString(), part); } } // Build a dictionay of all resources for easier access: if (KSTS.resourceDictionary == null) { KSTS.resourceDictionary = new Dictionary <string, PartResourceDefinition>(); foreach (var resourceDefinition in PartResourceLibrary.Instance.resourceDefinitions) { KSTS.resourceDictionary.Add(resourceDefinition.name.ToString(), resourceDefinition); } } // Invoke the timer-function every second to run background-code: if (!IsInvoking("Timer")) { InvokeRepeating("Timer", 1, 1); } // In case the Stage Recovery Mod is installed, add lists and handlers to track the separation and recovery of stages: if (StageRecoveryAPI.StageRecoveryAvailable && KSTS.stageParentDictionary == null) { Log.Warning("detected stage recovery mod"); stageParentDictionary = new Dictionary <string, string>(); StageRecoveryAPI.AddRecoverySuccessEvent((vessel, array, str) => { if (StageRecoveryAPI.StageRecoveryEnabled) { FlightRecorder.OnStageRecovered(vessel.id.ToString(), array[1]); } }); GameEvents.onStageSeparation.Add(new EventData <EventReport> .OnEvent(this.onStageSeparation)); GameEvents.onVesselWasModified.Add(new EventData <Vessel> .OnEvent(this.onVesselModified)); } // Execute the following code only once: if (KSTS.initialized) { return; } DontDestroyOnLoad(this); KSTS.initialized = true; } catch (Exception e) { Debug.LogError("Awake(): " + e.ToString()); } }
// Is called when this Addon is first loaded to initializes all values (eg registration of event-handlers and creation // of original-stats library). public void Awake() { try { FlightRecorder.Initialize(); MissionController.Initialize(); // Build dictionary of all parts for easier access: if (KSTS.partDictionary == null) { KSTS.partDictionary = new Dictionary <string, AvailablePart>(); foreach (AvailablePart part in PartLoader.LoadedPartsList) { if (KSTS.partDictionary.ContainsKey(part.name.ToString())) { Debug.LogError("[KSTS] duplicate part-name '" + part.name.ToString() + "'"); continue; } KSTS.partDictionary.Add(part.name.ToString(), part); } } // Build a dictionay of all resources for easier access: if (KSTS.resourceDictionary == null) { KSTS.resourceDictionary = new Dictionary <string, PartResourceDefinition>(); foreach (PartResourceDefinition resourceDefinition in PartResourceLibrary.Instance.resourceDefinitions) { KSTS.resourceDictionary.Add(resourceDefinition.name.ToString(), resourceDefinition); } } // Invoke the timer-function every second to run background-code: if (!IsInvoking("Timer")) { InvokeRepeating("Timer", 1, 1); } parentDictionary = new Dictionary <string, string>(); if (StageRecoveryAPI.StageRecoveryAvailable) { StageRecoveryAPI.AddRecoverySuccessEvent((vessel, array, str) => { if (StageRecoveryAPI.StageRecoveryEnabled) { StageRecovered?.Invoke(this, new StageRecoveredEventArgs { Vessel = vessel, FundsRecovered = array[1] }); } }); GameEvents.onStageSeparation.Add(new EventData <EventReport> .OnEvent(this.onStageSeparation)); GameEvents.onVesselWasModified.Add(new EventData <Vessel> .OnEvent(this.onVesselModified)); } // Execute the following code only once: if (KSTS.initialized) { return; } DontDestroyOnLoad(this); KSTS.initialized = true; } catch (Exception e) { Debug.LogError("[KSTS] Awake(): " + e.ToString()); } }
public override void OnSave(ConfigNode node) { try { /* * When transporting an available kerbal to a ship or recovering one from orbit, we manipulate the (unloaded) vessel's crew * as well as the roster-status of the kerbal. This is apparently not expected by KSP's core functionality, because there * seems to be a secret list of roster-status which is enforced when the game is safed: * * [WRN 15:16:14.678] [ProtoCrewMember Warning]: Crewmember Sierina Kerman found inside a part but status is set as missing. Vessel must have failed to save earlier. Restoring assigned status. * [WRN 15:17:42.913] [ProtoCrewMember Warning]: Crewmember Sierina Kerman found assigned but no vessels reference him. Sierina Kerman set as missing. * * Afterwards these kerbals would be lost for the player, which is why we have to use the workaround below to revert these * changes and make sure each kerbal has the correct status. This effectively disables the "missing" status as these kerbals * will always respawn, but I haven't seen a valid use-case for this thus far, so it is probably fine. */ if (HighLogic.CurrentGame.CrewRoster.Count > 0 && FlightGlobals.Vessels.Count > 0) { // Build a list of all Kerbals which are assigned to vessels: List <string> vesselCrewNames = new List <string>(); foreach (Vessel vessel in FlightGlobals.Vessels) { foreach (ProtoCrewMember crewMember in TargetVessel.GetCrew(vessel)) { vesselCrewNames.Add(crewMember.name); } } // Build a list of all kerbals which we could have manipulated: List <ProtoCrewMember> kerbals = new List <ProtoCrewMember>(); foreach (ProtoCrewMember kerbal in HighLogic.CurrentGame.CrewRoster.Kerbals(ProtoCrewMember.KerbalType.Crew)) { kerbals.Add(kerbal); } foreach (ProtoCrewMember kerbal in HighLogic.CurrentGame.CrewRoster.Kerbals(ProtoCrewMember.KerbalType.Tourist)) { kerbals.Add(kerbal); } // Check those kerbals against our vessel-list and maybe restore their correct status: foreach (ProtoCrewMember kerbal in kerbals) { if (kerbal.rosterStatus == ProtoCrewMember.RosterStatus.Dead) { continue; } if (vesselCrewNames.Contains(kerbal.name) && kerbal.rosterStatus != ProtoCrewMember.RosterStatus.Assigned) { Debug.Log("[KSTS] setting kerbal " + kerbal.name + " from " + kerbal.rosterStatus.ToString() + " to Assigned (see code for more info)"); kerbal.rosterStatus = ProtoCrewMember.RosterStatus.Assigned; } else if (!vesselCrewNames.Contains(kerbal.name) && kerbal.rosterStatus != ProtoCrewMember.RosterStatus.Available) { Debug.Log("[KSTS] setting kerbal " + kerbal.name + " from " + kerbal.rosterStatus.ToString() + " to Available (see code for more info)"); kerbal.rosterStatus = ProtoCrewMember.RosterStatus.Available; } } } FlightRecorder.SaveRecordings(node); MissionController.SaveMissions(node); } catch (Exception e) { Debug.LogError("[KSTS] OnSave(): " + e.ToString()); } }