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 { FlightRecoorder.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 void LoadRecordings(ConfigNode node) { FlightRecoorder.flightRecordings.Clear(); ConfigNode flightRecorderNode = node.GetNode("FlightRecorder"); if (flightRecorderNode == null) { return; } foreach (ConfigNode flightRecordingNode in flightRecorderNode.GetNodes()) { FlightRecoorder.flightRecordings.Add(flightRecordingNode.name, FlightRecording.CreateFromConfigNode(flightRecordingNode)); } FlightRecoorder.CollectGarbage(); // Might not work as expected in KSP 1.2, so we added this also to the timer-function. }
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: FlightRecoorder.Timer(); MissionController.Timer(); } catch (Exception e) { Debug.LogError("[KSTS] Timer(): " + e.ToString()); } }
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 every second and keeps track of used parts during a flight-recording: public static void Timer() { try { // Maybe remove old, invalid running recordings: FlightRecoorder.CollectGarbage(); // Check if we are on an vessel which is recording a flight: Vessel vessel = FlightGlobals.ActiveVessel; if (!vessel) { return; } FlightRecording recording = GetFlightRecording(vessel); if (recording == null) { return; } if (vessel.id.ToString() != FlightRecoorder.timerVesselId) { // The vessel has changed, reset all variables from the last timer-tick: FlightRecoorder.timerVesselId = vessel.id.ToString(); FlightRecoorder.timerPartResources.Clear(); } // Check all parts, if something has changed which makes the part unusable for payload-deployments: foreach (Part part in vessel.parts) { if (recording.usedPartIds.Contains(part.flightID.ToString())) { continue; // Already blocked } bool blockThis = false; string partId = part.flightID.ToString(); // Check for running engines: foreach (ModuleEngines engineModule in part.FindModulesImplementing <ModuleEngines>()) { if (engineModule.GetCurrentThrust() > 0) { blockThis = true; } } foreach (ModuleEnginesFX engineModule in part.FindModulesImplementing <ModuleEnginesFX>()) { if (engineModule.GetCurrentThrust() > 0) { blockThis = true; } } // Check for resource-consumption: foreach (PartResource resource in part.Resources) { PartResourceDefinition resourceDefinition = null; string resourceId = resource.resourceName.ToString(); if (!KSTS.resourceDictionary.TryGetValue(resourceId, out resourceDefinition)) { continue; } if (resourceDefinition.density <= 0) { continue; // We only care about resources with mass, skipping electricity and such. } if (!FlightRecoorder.timerPartResources.ContainsKey(partId)) { FlightRecoorder.timerPartResources.Add(partId, new Dictionary <string, double>()); } double lastAmount; if (!FlightRecoorder.timerPartResources[partId].TryGetValue(resourceId, out lastAmount)) { FlightRecoorder.timerPartResources[partId].Add(resourceId, resource.amount); } else { if (lastAmount != resource.amount) { blockThis = true; // The amount has changed relative to the last timer-tick. } } } if (blockThis) { Debug.Log("[KSTS] marking part " + part.name.ToString() + " (" + part.flightID.ToString() + ") as used"); recording.usedPartIds.Add(part.flightID.ToString()); } } } catch (Exception e) { Debug.LogError("[KSTS] FlightRecoorder.Timer(): " + e.ToString()); } }