/* Returns time at which this course will complete */ public double CompletionTime() { double start, length; if (Started) { start = startTime; } else { start = KSPUtils.GetUT(); } length = GetTime(); return(start + length); }
public bool NautHasTrainingForPart(ProtoCrewMember pcm, string partName) { TrainingDatabase.SynonymReplace(partName, out partName); FlightLog.Entry ent = pcm.careerLog.Last(); if (ent == null) { return(false); } bool lacksMission = IsMissionTrainingEnabled; for (int i = pcm.careerLog.Entries.Count; i-- > 0;) { FlightLog.Entry e = pcm.careerLog.Entries[i]; if (lacksMission) { if (string.IsNullOrEmpty(e.type) || string.IsNullOrEmpty(e.target)) { continue; } if (e.type == TrainingType_Mission && e.target == partName) { double exp = GetExpiration(pcm.name, e); lacksMission = exp == 0d || exp < KSPUtils.GetUT(); } } else { if (string.IsNullOrEmpty(e.type) || string.IsNullOrEmpty(e.target)) { continue; } if (e.type == TrainingType_Proficiency && e.target == partName) { return(true); } } } return(false); }
public void Start() { double ut = KSPUtils.GetUT(); if (NextUpdate > ut + UpdateInterval) { // KRASH has a bad habit of not reverting state properly when exiting sims. // This means that the updateInterval could end up years into the future. NextUpdate = ut + 5; } onKctTechQueuedEvent = GameEvents.FindEvent <EventData <RDTech> >("OnKctTechQueued"); if (onKctTechQueuedEvent != null) { onKctTechQueuedEvent.Add(AddCoursesForTechNode); } StartCoroutine(CreateCoursesRoutine()); StartCoroutine(EnsureActiveCrewInSimulationRoutine()); }
public void Update() { if (HighLogic.CurrentGame == null || HighLogic.CurrentGame.CrewRoster == null) { return; } if (_isFirstLoad) { _isFirstLoad = false; ProcessFirstLoad(); } if (HighLogic.LoadedSceneIsFlight && _flightLogUpdateCounter++ >= FlightLogUpdateInterval) { _flightLogUpdateCounter = 0; UpdateFlightLog(); } double time = KSPUtils.GetUT(); if (NextUpdate < time) { // Ensure that CrewHandler updates happen at predictable times so that accurate KAC alarms can be set. do { NextUpdate += UpdateInterval; }while (NextUpdate < time); ProcessRetirements(time); ProcessCourses(time); } if (_inAC) { FixAstronauComplexUI(); } }
private void VesselRecoveryProcessing(ProtoVessel v, MissionRecoveryDialog mrDialog, float data) { Debug.Log("[RP-0] - Vessel recovery processing"); var retirementChanges = new List <string>(); var inactivity = new List <string>(); double UT = KSPUtils.GetUT(); // normally we would use v.missionTime, but that doesn't seem to update // when you're not actually controlling the vessel double elapsedTime = UT - v.launchTime; Debug.Log($"[RP-0] mission elapsedTime: {KSPUtil.PrintDateDeltaCompact(elapsedTime, true, true)}"); // When flight duration was too short, mission training should not be set as expired. // This can happen when an on-the-pad failure occurs and the vessel is recovered. // We could perhaps override this if they're not actually in flight // (if the user didn't recover right from the pad I think this is a fair assumption) if (elapsedTime < Settings.minFlightDurationSecondsForTrainingExpire) { Debug.Log($"[RP-0] - mission time too short for crew to be inactive (elapsed time was {elapsedTime}, settings set for {Settings.minFlightDurationSecondsForTrainingExpire})"); return; } var validStatuses = new List <string> { FlightLog.EntryType.Flight.ToString(), Situation_FlightHigh, FlightLog.EntryType.Suborbit.ToString(), FlightLog.EntryType.Orbit.ToString(), FlightLog.EntryType.ExitVessel.ToString(), FlightLog.EntryType.Land.ToString(), FlightLog.EntryType.Flyby.ToString() }; foreach (ProtoCrewMember pcm in v.GetVesselCrew()) { Debug.Log("[RP-0] - Found ProtoCrewMember: " + pcm.displayName); var allFlightsDict = new Dictionary <string, int>(); int curFlight = pcm.careerLog.Last().flight; double inactivityMult = 0; double retirementMult = 0; foreach (FlightLog.Entry e in pcm.careerLog.Entries) { if (e.type == "Nationality") { continue; } if (e.type == TrainingType_Mission) { SetExpiration(pcm.name, e, KSPUtils.GetUT()); } if (validStatuses.Contains(e.type)) { int situationCount; var key = $"{e.target}-{e.type}"; if (allFlightsDict.ContainsKey(key)) { situationCount = allFlightsDict[key]; allFlightsDict[key] = ++situationCount; } else { situationCount = 1; allFlightsDict.Add(key, situationCount); } if (e.flight != curFlight) { continue; } if (TryGetBestSituationMatch(e.target, e.type, "Retire", out double situationMult)) { double countMult = 1 + Math.Pow(situationCount - 1, Settings.retireOffsetFlightNumPow); retirementMult += situationMult / countMult; } if (TryGetBestSituationMatch(e.target, e.type, "Inactive", out double inactivMult)) { inactivityMult += inactivMult; } } } Debug.Log("[RP-0] retirementMult: " + retirementMult); Debug.Log("[RP-0] inactivityMult: " + inactivityMult); double acMult = ScenarioUpgradeableFacilities.GetFacilityLevel(SpaceCenterFacility.AstronautComplex) + 1; Debug.Log("[RP-0] AC multiplier: " + acMult); if (KerbalRetireTimes.TryGetValue(pcm.name, out double retTime)) { double stupidityPenalty = UtilMath.Lerp(Settings.retireOffsetStupidMin, Settings.retireOffsetStupidMax, pcm.stupidity); Debug.Log($"[RP-0] stupidityPenalty for {pcm.stupidity}: {stupidityPenalty}"); double retireOffset = retirementMult * 86400 * Settings.retireOffsetBaseMult / stupidityPenalty; if (retireOffset > 0) { KerbalRetireIncreases.TryGetValue(pcm.name, out double retIncreaseTotal); retIncreaseTotal += retireOffset; if (retIncreaseTotal > Settings.retireIncreaseCap) { // Cap the total retirement increase at a specific number of years retireOffset -= retIncreaseTotal - Settings.retireIncreaseCap; retIncreaseTotal = Settings.retireIncreaseCap; } KerbalRetireIncreases[pcm.name] = retIncreaseTotal; string sRetireOffset = KSPUtil.PrintDateDelta(retireOffset, false, false); Debug.Log("[RP-0] retire date increased by: " + sRetireOffset); retTime += retireOffset; KerbalRetireTimes[pcm.name] = retTime; retirementChanges.Add($"\n{pcm.name}, +{sRetireOffset}, no earlier than {KSPUtil.PrintDate(retTime, false)}"); } } inactivityMult = Math.Max(1, inactivityMult); double elapsedTimeDays = elapsedTime / 86400; double inactiveTimeDays = Math.Pow(Math.Max(Settings.inactivityMinFlightDurationDays, elapsedTimeDays), Settings.inactivityFlightDurationExponent) * Math.Min(Settings.inactivityMaxSituationMult, inactivityMult) / acMult; double inactiveTime = inactiveTimeDays * 86400; Debug.Log("[RP-0] inactive for: " + KSPUtil.PrintDateDeltaCompact(inactiveTime, true, false)); pcm.SetInactive(inactiveTime, false); inactivity.Add($"\n{pcm.name}, until {KSPUtil.PrintDate(inactiveTime + UT, true, false)}"); } if (inactivity.Count > 0) { StringBuilder sb = new StringBuilder(); sb.Append("The following crew members will be on leave:"); foreach (string s in inactivity) { sb.Append(s); } if (RetirementEnabled && retirementChanges.Count > 0) { sb.Append("\n\nThe following retirement changes have occurred:"); foreach (string s in retirementChanges) { sb.Append(s); } } PopupDialog.SpawnPopupDialog(new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), "CrewUpdateNotification", "Crew Updates", sb.ToString(), "OK", true, HighLogic.UISkin); } }
public void CompleteCourse() { //assign rewards to all kerbals and set them to free if (Completed) { foreach (ProtoCrewMember student in Students) { if (student == null) { continue; } if (ExpireLog != null) { foreach (ConfigNode.Value v in ExpireLog.values) { for (int i = student.careerLog.Count; i-- > 0;) { FlightLog.Entry e = student.careerLog.Entries[i]; if (TrainingExpiration.Compare(v.value, e)) { e.type = "expired_" + e.type; CrewHandler.Instance.RemoveExpiration(student.name, v.value); break; } } } } if (RewardLog != null) { if (student.flightLog.Count > 0) { student.ArchiveFlightLog(); } TrainingExpiration exp = null; if (expiration > 0d) { exp = new TrainingExpiration(); exp.PcmName = student.name; exp.Expiration = expiration; if (expirationUseStupid) { exp.Expiration *= UtilMath.Lerp(CrewHandler.Settings.trainingProficiencyStupidMin, CrewHandler.Settings.trainingProficiencyStupidMax, student.stupidity); } exp.Expiration += KSPUtils.GetUT(); } bool prevMissionsAlreadyExpired = false; foreach (ConfigNode.Value v in RewardLog.values) { string[] s = v.value.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); string trainingType = s[0]; string trainingTarget = s.Length == 1 ? null : s[1]; if (!prevMissionsAlreadyExpired && trainingType == CrewHandler.TrainingType_Mission) { // Expire any previous mission trainings because only 1 should be active at a time for (int i = student.careerLog.Count; i-- > 0;) { FlightLog.Entry e = student.careerLog.Entries[i]; if (e.type == CrewHandler.TrainingType_Mission) { e.type = "expired_" + e.type; CrewHandler.Instance.RemoveExpiration(student.name, v.value); student.ArchiveFlightLog(); prevMissionsAlreadyExpired = true; } } } student.flightLog.AddEntry(trainingType, trainingTarget); student.ArchiveFlightLog(); if (expiration > 0d) { exp.Entries.Add(v.value); } } if (expiration > 0d) { CrewHandler.Instance.AddExpiration(exp); } } if (rewardXP != 0) { student.ExtraExperience += rewardXP; } } } foreach (ProtoCrewMember student in Students) { student.inactive = false; } //fire an event }