Ejemplo n.º 1
0
 public override void OnLoad(ConfigNode node)
 {
     try
     {
         FlightRecorder.LoadRecordings(node);
         MissionController.LoadMissions(node);
         GUI.Reset();
     }
     catch (Exception e)
     {
         Debug.LogError("[KSTS] OnLoad(): " + e.ToString());
     }
 }
Ejemplo n.º 2
0
        public static void LoadRecordings(ConfigNode node)
        {
            FlightRecorder.flightRecordings.Clear();
            ConfigNode flightRecorderNode = node.GetNode("FlightRecorder");

            if (flightRecorderNode == null)
            {
                return;
            }

            foreach (ConfigNode flightRecordingNode in flightRecorderNode.GetNodes())
            {
                FlightRecorder.flightRecordings.Add(flightRecordingNode.name, FlightRecording.CreateFromConfigNode(flightRecordingNode));
            }

            FlightRecorder.CollectGarbage(); // Might not work as expected in KSP 1.2, so we added this also to the timer-function.
        }
Ejemplo n.º 3
0
        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());
            }
        }
Ejemplo n.º 4
0
        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());
            }
        }
Ejemplo n.º 5
0
        // 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:
                FlightRecorder.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() != FlightRecorder.timerVesselId)
                {
                    // The vessel has changed, reset all variables from the last timer-tick:
                    FlightRecorder.timerVesselId = vessel.id.ToString();
                    FlightRecorder.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 (!FlightRecorder.timerPartResources.ContainsKey(partId))
                        {
                            FlightRecorder.timerPartResources.Add(partId, new Dictionary <string, double>());
                        }
                        double lastAmount;
                        if (!FlightRecorder.timerPartResources[partId].TryGetValue(resourceId, out lastAmount))
                        {
                            FlightRecorder.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());
            }
        }
Ejemplo n.º 6
0
        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());
            }
        }
Ejemplo n.º 7
0
        // 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());
            }
        }
Ejemplo n.º 8
0
        public static void Display()
        {
            if (!initialized)
            {
                Initialize();
            }
            Vessel          vessel    = FlightGlobals.ActiveVessel;
            FlightRecording recording = null;

            if (vessel)
            {
                recording = FlightRecorder.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:
                            bool selectionChanged = false;
                            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);
                                        selectionChanged = true;
                                    }
                                }
                                else
                                {
                                    if (selectedPayloadAssemblyIds.Contains(payloadAssembly.id))
                                    {
                                        selectedPayloadAssemblyIds.Remove(payloadAssembly.id);
                                        selectionChanged = true;
                                    }
                                }
                                GUILayout.Label(payloadAssembly.partCount.ToString() + " part" + (payloadAssembly.partCount != 1 ? "s" : "") + ", " + payloadAssembly.mass.ToString("#,##0.00 t") + "   ", new GUIStyle(GUI.labelStyle)
                                {
                                    alignment = TextAnchor.MiddleRight
                                });
                                GUILayout.EndHorizontal();
                            }

                            // Highlight all selected assemblies (to make sure these don't cancel each other out, we first turn all off and then switch the selected ones on):
                            if (selectionChanged)
                            {
                                foreach (PayloadAssembly payloadAssembly in recording.GetPayloadAssemblies())
                                {
                                    payloadAssembly.Highlight(false);
                                }
                                foreach (PayloadAssembly payloadAssembly in recording.GetPayloadAssemblies())
                                {
                                    if (selectedPayloadAssemblyIds.Contains(payloadAssembly.id))
                                    {
                                        payloadAssembly.Highlight(true);
                                    }
                                }
                            }
                        }
                    }
                    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:
                    FlightRecorder.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:
                    FlightRecorder.SaveRecording(vessel);
                }

                if (recording.status != FlightRecordingStatus.PRELAUNCH && GUILayout.Button("Abort", GUI.buttonStyle))
                {
                    // Cancel runnig recording:
                    FlightRecorder.CancelRecording(vessel);
                }
                GUILayout.EndHorizontal();
            }
        }
Ejemplo n.º 9
0
        // 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());
            }
        }