From 85e1bc6f1cfcd968f71e8c97cb058083f8305b7f Mon Sep 17 00:00:00 2001 From: siimav Date: Tue, 23 Jan 2024 02:07:48 +0200 Subject: [PATCH] Add on-pad vessel repairs --- GameData/RP-1/Resources/KCT_repair.truecolor | Bin 0 -> 387 bytes Source/RP0/Harmony/FlightInputHandler.cs | 22 +++++ Source/RP0/ModIntegrations/TFInterop.cs | 70 ++++++++++++++ Source/RP0/RP0.csproj | 45 ++++----- .../LaunchComplex/LaunchComplex.cs | 39 ++++++-- .../Projects/ISpaceCenterProject.cs | 3 +- .../RP0/SpaceCenter/Projects/LCOpsProject.cs | 16 +++- .../Projects/ReconRolloutProject.cs | 4 +- .../Projects/VesselRepairProject.cs | 89 ++++++++++++++++++ Source/RP0/SpaceCenter/SCMEvents.cs | 36 ++++++- .../RP0/SpaceCenter/SpaceCenterManagement.cs | 77 +++++++++++++-- Source/RP0/UI/KCT/GUI_BuildList.cs | 61 ++++++++++-- Source/RP0/Utilities/Formula.cs | 5 + Source/RP0/Utilities/KCTUtilities.cs | 2 +- 14 files changed, 404 insertions(+), 65 deletions(-) create mode 100644 GameData/RP-1/Resources/KCT_repair.truecolor create mode 100644 Source/RP0/Harmony/FlightInputHandler.cs create mode 100644 Source/RP0/ModIntegrations/TFInterop.cs create mode 100644 Source/RP0/SpaceCenter/Projects/VesselRepairProject.cs diff --git a/GameData/RP-1/Resources/KCT_repair.truecolor b/GameData/RP-1/Resources/KCT_repair.truecolor new file mode 100644 index 0000000000000000000000000000000000000000..185036e13647cbc0bb36f3895376294c5dd83cd7 GIT binary patch literal 387 zcmV-}0et?6P)&>jjLxfr;n^^a3vap1?p86G1Z#M9>Qu z=n*s&p22iuO*^tHe$dcU-&b#HD5g^Rk2+)K)`68#fGOYx=oOg(a1Qi=?hk^R&$YGQ z)Ly9Xs2A#|darha*8t3^uL-grL+X{^YCtH0v+ADutS*N7noV8)2FPbYeM*pgAE=w^ zuKF=LfNdk zq;40r1|$N8MetPZq;pbu2PL4g-fz}fYRrB|Jr4e3o+3^#q29GLN63--pibs`P5GOu hv>jmlzcH%u#xGc(Bq9?t1(N^(002ovPDHLkV1j=Fp@#qf literal 0 HcmV?d00001 diff --git a/Source/RP0/Harmony/FlightInputHandler.cs b/Source/RP0/Harmony/FlightInputHandler.cs new file mode 100644 index 00000000000..fcaa0fd8e46 --- /dev/null +++ b/Source/RP0/Harmony/FlightInputHandler.cs @@ -0,0 +1,22 @@ +using HarmonyLib; + +namespace RP0.Harmony +{ + [HarmonyPatch(typeof(FlightInputHandler))] + internal class PatchFlightInputHandler + { + /// + /// Makes sure that throttle stays at 0 when repairs were done and vessel comes off rails. + /// + [HarmonyPostfix] + [HarmonyPatch("SetLaunchCtrlState")] + internal static void Postfix_SetLaunchCtrlState() + { + bool b = SpaceCenterManagement.Instance?.DoingVesselRepair ?? false; + if (b) + { + FlightInputHandler.state.mainThrottle = 0; + } + } + } +} diff --git a/Source/RP0/ModIntegrations/TFInterop.cs b/Source/RP0/ModIntegrations/TFInterop.cs new file mode 100644 index 00000000000..d72b3e67543 --- /dev/null +++ b/Source/RP0/ModIntegrations/TFInterop.cs @@ -0,0 +1,70 @@ +using System; +using System.Linq; +using System.Reflection; + +namespace RP0 +{ + public static class TFInterop + { + private static bool? _isTestFlightInstalled = null; + private static bool? _hasSupportForReset = null; + private static Assembly _assembly; + private static MethodInfo _miGetVesselStatus; + private static MethodInfo _miResetAllFailuresOnVessel; + private static MethodInfo _miResetAllRunTimesOnVessel; + + public static bool IsTestFlightInstalled + { + get + { + EnsureReflectionInitialized(); + return _isTestFlightInstalled.Value; + } + } + public static bool HasSupportForReset + { + get + { + EnsureReflectionInitialized(); + return _hasSupportForReset.Value; + } + } + + public static bool VesselHasFailedParts(Vessel v) + { + if (v == null) return false; + + EnsureReflectionInitialized(); + var res = (int)_miGetVesselStatus.Invoke(null, new object[] { v }); + // 0 = OK, 1 = Has failure, -1 = Could not find TestFlight Core on Part + return res > 0; + } + + public static void ResetAllFailures(Vessel v) + { + if (v == null) return; + + EnsureReflectionInitialized(); + _miResetAllFailuresOnVessel.Invoke(null, new object[] { v }); + _miResetAllRunTimesOnVessel.Invoke(null, new object[] { v }); + } + + private static void EnsureReflectionInitialized() + { + if (_isTestFlightInstalled.HasValue) return; + + _assembly = AssemblyLoader.loadedAssemblies.FirstOrDefault((AssemblyLoader.LoadedAssembly la) => string.Equals(la.name, "TestFlightCore", StringComparison.OrdinalIgnoreCase))?.assembly; + _isTestFlightInstalled = _assembly != null; + _hasSupportForReset = false; + + if (_isTestFlightInstalled.Value) + { + var type = _assembly.GetType("TestFlightCore.TestFlightInterface"); + _miGetVesselStatus = type.GetMethod("GetVesselStatus", BindingFlags.Public | BindingFlags.Static); + _miResetAllFailuresOnVessel = type.GetMethod("ResetAllFailuresOnVessel", BindingFlags.Public | BindingFlags.Static); + _miResetAllRunTimesOnVessel = type.GetMethod("ResetAllRunTimesOnVessel", BindingFlags.Public | BindingFlags.Static); + _hasSupportForReset = _miResetAllRunTimesOnVessel != null; + } + } + } +} diff --git a/Source/RP0/RP0.csproj b/Source/RP0/RP0.csproj index b8d7162b7c4..c6f3ccdacf0 100644 --- a/Source/RP0/RP0.csproj +++ b/Source/RP0/RP0.csproj @@ -38,7 +38,10 @@ + + + @@ -283,7 +286,6 @@ False - ..\..\ContractConfigurator\source\ContractConfigurator\bin\Release\ContractConfigurator.dll False @@ -291,16 +293,13 @@ False - ..\..\..\..\..\..\Games\R112\KSP_x64_Data\Managed\Ionic.Zip.dll False False - ..\..\..\..\..\..\..\temp\dlls\Kerbalism112.dll False - ..\..\..\..\..\..\Games\R112\GameData\KSPCommunityFixes\Plugins\KSPCommunityFixes.dll False @@ -310,7 +309,9 @@ False - ..\..\..\..\..\..\..\Games\R112\GameData\ROUtils\Plugins\ROUtils.dll + False + + False @@ -322,51 +323,37 @@ False - - False + False - - False - ..\..\..\KSP DLL\1.12.3\UnityEngine.AssetBundleModule.dll + False - - False + False - - False - ..\..\..\..\..\..\Games\R112\KSP_x64_Data\Managed\UnityEngine.ImageConversionModule.dll + False - - False + False - - False + False - - False + False - - False - ..\..\..\..\..\..\Games\R112\KSP_x64_Data\Managed\UnityEngine.ScreenCaptureModule.dll + False - - False + False False - - False - ..\..\ContractConfigurator\source\ContractConfigurator\bin\Release\UnityEngine.UIModule.dll + False diff --git a/Source/RP0/SpaceCenter/LaunchComplex/LaunchComplex.cs b/Source/RP0/SpaceCenter/LaunchComplex/LaunchComplex.cs index 34a466c9ec1..2329b09ae9c 100644 --- a/Source/RP0/SpaceCenter/LaunchComplex/LaunchComplex.cs +++ b/Source/RP0/SpaceCenter/LaunchComplex/LaunchComplex.cs @@ -1,7 +1,8 @@ -using System; +using ROUtils.DataTypes; +using System; +using System.Collections.Generic; using UniLinq; using UnityEngine; -using ROUtils.DataTypes; namespace RP0 { @@ -44,6 +45,8 @@ public class LaunchComplex : IConfigNode public PersistentObservableList PadConstructions = new PersistentObservableList(); [Persistent] public PersistentObservableList Recon_Rollout = new PersistentObservableList(); + [Persistent] + public PersistentObservableList VesselRepairs = new PersistentObservableList(); private double _rate; private double _rateHRCapped; @@ -142,6 +145,7 @@ void AddListeners() BuildList.Updated += updated; Warehouse.Updated += updated; Recon_Rollout.Updated += lcpUpdated; + VesselRepairs.Updated += lcpUpdated; } #endregion @@ -207,10 +211,12 @@ public int LaunchPadCount public bool IsEmpty => LCType == LaunchComplexType.Hangar && BuildList.Count == 0 && Warehouse.Count == 0 && Engineers == 0 && LCData.StartingHangar.Compare(this); - public bool IsActive => BuildList.Count > 0 || Recon_Rollout.Any(rr => !rr.IsComplete()); - public bool CanDismantle => BuildList.Count == 0 && Warehouse.Count == 0 && !Recon_Rollout.Any(r => r.RRType != ReconRolloutProject.RolloutReconType.Reconditioning); - public bool CanModifyButton => BuildList.Count == 0 && Warehouse.Count == 0 && Recon_Rollout.Count == 0; - public bool CanModifyReal => Recon_Rollout.Count == 0; + public bool IsActive => BuildList.Count > 0 || GetAllLCOps().Any(op => !op.IsComplete()); + public bool CanDismantle => BuildList.Count == 0 && Warehouse.Count == 0 && + !Recon_Rollout.Any(r => r.RRType != ReconRolloutProject.RolloutReconType.Reconditioning) && + VesselRepairs.Count == 0; + public bool CanModifyButton => BuildList.Count == 0 && Warehouse.Count == 0 && Recon_Rollout.Count == 0 && VesselRepairs.Count == 0; + public bool CanModifyReal => Recon_Rollout.Count == 0 && VesselRepairs.Count == 0; public bool CanIntegrate => ProjectBPTotal == 0d; private double _projectBPTotal = -1d; @@ -219,7 +225,7 @@ public int LaunchPadCount public double RecalculateProjectBP() { _projectBPTotal = 0d; - foreach (var r in Recon_Rollout) + foreach (var r in GetAllLCOps()) { if (!r.IsBlocking || r.IsComplete()) continue; @@ -250,8 +256,8 @@ public void RecalculateBuildRates() foreach (var vp in BuildList) vp.UpdateBuildRate(); - foreach (var rr in Recon_Rollout) - rr.UpdateBuildRate(); + foreach (var op in GetAllLCOps()) + op.UpdateBuildRate(); RecalculateProjectBP(); @@ -418,7 +424,7 @@ public void Load(ConfigNode node) } } - foreach (var rr in Recon_Rollout) + foreach (var rr in GetAllLCOps()) rr.LC = this; foreach (var vp in BuildList) @@ -447,6 +453,19 @@ public void PostLoad(LCSpaceCenter ksc) } } + public IEnumerable GetAllLCOps() + { + foreach (var item in Recon_Rollout) + { + yield return item; + } + + foreach (var item in VesselRepairs) + { + yield return item; + } + } + private void CalculateAndSetRates() { _strategyRateMultiplier = CurrencyUtils.Rate(LCType == LaunchComplexType.Pad ? TransactionReasonsRP0.RateIntegrationVAB : TransactionReasonsRP0.RateIntegrationSPH); diff --git a/Source/RP0/SpaceCenter/Projects/ISpaceCenterProject.cs b/Source/RP0/SpaceCenter/Projects/ISpaceCenterProject.cs index e4ccc2d5040..0466f6bf2e9 100644 --- a/Source/RP0/SpaceCenter/Projects/ISpaceCenterProject.cs +++ b/Source/RP0/SpaceCenter/Projects/ISpaceCenterProject.cs @@ -9,7 +9,8 @@ public enum ProjectType Reconditioning, KSC, AirLaunch, - Crew + Crew, + VesselRepair }; public interface ISpaceCenterProject diff --git a/Source/RP0/SpaceCenter/Projects/LCOpsProject.cs b/Source/RP0/SpaceCenter/Projects/LCOpsProject.cs index 516c3420d53..02fb9164d35 100644 --- a/Source/RP0/SpaceCenter/Projects/LCOpsProject.cs +++ b/Source/RP0/SpaceCenter/Projects/LCOpsProject.cs @@ -18,10 +18,12 @@ public abstract class LCOpsProject : ConfigNodePersistenceBase, ISpaceCenterProj private bool _wasComplete; protected double _buildRate = -1; + public Guid AssociatedIdAsGuid => new Guid(associatedID); + public abstract TransactionReasonsRP0 TransactionReason { get; } protected abstract TransactionReasonsRP0 transactionReasonTime { get; } - public VesselProject AssociatedVP => KCTUtilities.FindVPByID(LC, new Guid(associatedID)); + public VesselProject AssociatedVP => KCTUtilities.FindVPByID(LC, AssociatedIdAsGuid); protected LaunchComplex _lc = null; public LaunchComplex LC @@ -42,6 +44,14 @@ public LaunchComplex LC break; } } + else if (this is VesselRepairProject vr) + { + if (lc.VesselRepairs.Contains(vr)) + { + _lc = lc; + break; + } + } } } } @@ -239,7 +249,7 @@ public double GetTimeLeftEstAll() public static double GetTotalBlockingProjectTime(LaunchComplex lc) { - foreach (var r in lc.Recon_Rollout) + foreach (var r in lc.GetAllLCOps()) { if (r.IsBlocking && !r.IsComplete()) AddLCP(r); @@ -288,7 +298,7 @@ public static LCOpsProject GetFirstCompleting(LaunchComplex lc) LCOpsProject lcp = null; // The blocking LCP with the lowest time left // doesn't have to worry about build rate changing - foreach (var r in lc.Recon_Rollout) + foreach (var r in lc.GetAllLCOps()) { if (r.IsComplete()) continue; diff --git a/Source/RP0/SpaceCenter/Projects/ReconRolloutProject.cs b/Source/RP0/SpaceCenter/Projects/ReconRolloutProject.cs index 81d613741bc..c577180ef75 100644 --- a/Source/RP0/SpaceCenter/Projects/ReconRolloutProject.cs +++ b/Source/RP0/SpaceCenter/Projects/ReconRolloutProject.cs @@ -7,8 +7,8 @@ public class ReconRolloutProject : LCOpsProject { public enum RolloutReconType { Reconditioning, Rollout, Rollback, Recovery, None, AirlaunchMount, AirlaunchUnmount }; - public override string Name => KSP.Localization.Localizer.Format("#rp0_LCOps_Type_" + RRType.ToString()) - ; + public override string Name => KSP.Localization.Localizer.Format("#rp0_LCOps_Type_" + RRType.ToString()); + [Persistent] public string launchPadID = "LaunchPad"; [Persistent] diff --git a/Source/RP0/SpaceCenter/Projects/VesselRepairProject.cs b/Source/RP0/SpaceCenter/Projects/VesselRepairProject.cs new file mode 100644 index 00000000000..5c7acce0b6b --- /dev/null +++ b/Source/RP0/SpaceCenter/Projects/VesselRepairProject.cs @@ -0,0 +1,89 @@ +using RealFuels; + +namespace RP0 +{ + public class VesselRepairProject : LCOpsProject + { + public override string Name => "Repair vessel"; + + [Persistent] + public string launchPadID = "LaunchPad"; + [Persistent] + public string shipName; + + public override TransactionReasonsRP0 TransactionReason => TransactionReasonsRP0.RocketRollout; + + protected override TransactionReasonsRP0 transactionReasonTime => TransactionReasonsRP0.RateRollout; + + public override ProjectType GetProjectType() => ProjectType.VesselRepair; + + public VesselRepairProject() : base() + { + } + + /// + /// + /// + /// + /// + public VesselRepairProject(Vessel vessel, string launchSite, LaunchComplex lc) + { + associatedID = vessel.id.ToString(); + launchPadID = launchSite; + RP0Debug.Log("New VesselRepair at launchsite: " + launchPadID); + progress = 0; + _lc = lc; + + mass = vessel.GetTotalMass(); + shipName = vessel.vesselName; + try + { + var vp = new VesselProject(vessel, ProjectType.VAB); + isHumanRated = vp.humanRated; + BP = Formula.GetVesselRepairBP(vp); + vesselBP = vp.buildPoints; + } + catch + { + RP0Debug.Log("Error while determining BP for VesselRepair"); + } + } + + public bool ApplyRepairs() + { + Vessel v = FlightGlobals.FindVessel(AssociatedIdAsGuid); + if (v != null) + { + if (!v.loaded) return false; + + v.ctrlState.mainThrottle = 0; + TFInterop.ResetAllFailures(v); + SpaceCenterManagement.Instance.DoingVesselRepair = true; + + try + { + ResetIgnitionsOnVessel(v); + } + catch + { + // Might be using older RF version that is missing the ResetIgnitions method + } + + return true; + } + else + { + RP0Debug.LogError($"Failed to find vessel {associatedID} to reset failures on"); + return false; + } + } + + private static void ResetIgnitionsOnVessel(Vessel v) + { + foreach (var merf in v.FindPartModulesImplementing()) + { + merf.ResetIgnitions(); + } + } + } +} diff --git a/Source/RP0/SpaceCenter/SCMEvents.cs b/Source/RP0/SpaceCenter/SCMEvents.cs index a6f1dd49fe1..aafa4c103ba 100644 --- a/Source/RP0/SpaceCenter/SCMEvents.cs +++ b/Source/RP0/SpaceCenter/SCMEvents.cs @@ -2,12 +2,14 @@ using ROUtils.DataTypes; using ToolbarControl_NS; using ROUtils; +using UnityEngine.SceneManagement; namespace RP0 { public class SCMEvents : HostedSingleton { new public static SCMEvents Instance { get; private set; } + public static bool SceneChangeInProgress { get; private set; } public SCMEvents(SingletonHost host) : base(host) { @@ -71,6 +73,7 @@ public void SubscribeToEvents() GameEvents.onGameStateLoad.Add(OnGameStateLoad); GameEvents.OnPartPurchased.Add(OnPartPurchased); GameEvents.Modifiers.OnCurrencyModified.Add(OnCurrenciesModified); + GameEvents.onFlightGlobalsRemoveVessel.Add(OnRemoveVessel); // Flight GameEvents.onVesselSituationChange.Add(VesselSituationChange); @@ -81,7 +84,6 @@ public void SubscribeToEvents() GameEvents.StageManager.OnGUIStageRemoved.Add(StageCountChangedEvent); GameEvents.StageManager.OnGUIStageSequenceModified.Add(StagingOrderChangedEvent); GameEvents.StageManager.OnPartUpdateStageability.Add(PartStageabilityChangedEvent); - GameEvents.onEditorStarted.Add(OnEditorStarted); // Space Center GameEvents.OnKSCFacilityUpgraded.Add(FacilityUpgradedEvent); @@ -100,11 +102,9 @@ public void SubscribeToEvents() GameEvents.onGUIMissionControlDespawn.Add(ExitSCSubsceneAndShow); GameEvents.onGUIRnDComplexDespawn.Add(ExitSCSubsceneAndShow); GameEvents.onGUIKSPediaDespawn.Add(RestoreAllGUIs); - } - private void OnEditorStarted() - { - KCTUtilities.HandleEditorButton(); + GameEvents.onGameSceneLoadRequested.Add(OnGameSceneLoadRequested); + SceneManager.sceneLoaded += OnSceneLoaded; } public void CreateEvents() @@ -131,6 +131,20 @@ public void CreateEvents() ApplyGlobalEffectiveCostMultiplier = new EventData, IEnumerable, Dictionary>("ApplyGlobalEffectiveCostMultiplier"); } + private void OnGameSceneLoadRequested(GameScenes data) + { + SceneChangeInProgress = true; + } + + private void OnSceneLoaded(Scene scene, LoadSceneMode mode) + { + if (mode != LoadSceneMode.Additive && + HighLogic.GetLoadedGameSceneFromBuildIndex(scene.buildIndex) != GameScenes.LOADINGBUFFER) + { + SceneChangeInProgress = false; + } + } + public void HideAllGUIs() { KCT_GUI.BackupUIState(); @@ -203,6 +217,18 @@ private void OnCurrenciesModified(CurrencyModifierQuery query) KCTUtilities.ProcessSciPointTotalChange(changeDelta); } + private void OnRemoveVessel(Vessel v) + { + if (SceneChangeInProgress) return; + + RP0Debug.Log($"OnRemoveVessel: {v.vesselName}"); + VesselRepairProject r = SpaceCenterManagement.Instance.FindRepairForVessel(v); + if (r != null) + { + r.LC.VesselRepairs.Remove(r); + } + } + private void ShipModifiedEvent(ShipConstruct vessel) { SpaceCenterManagement.Instance.IsEditorRecalcuationRequired = true; diff --git a/Source/RP0/SpaceCenter/SpaceCenterManagement.cs b/Source/RP0/SpaceCenter/SpaceCenterManagement.cs index 702cf5d4035..694ef0bb0f2 100644 --- a/Source/RP0/SpaceCenter/SpaceCenterManagement.cs +++ b/Source/RP0/SpaceCenter/SpaceCenterManagement.cs @@ -144,6 +144,8 @@ public static void ClearVesselEditMode() #region Fields + public bool DoingVesselRepair; + public bool MergingAvailable; public List MergedVessels = new List(); @@ -755,7 +757,7 @@ private void PopUpVesselError(List errored) for (int i = 0; i < currentLC.Recon_Rollout.Count; i++) { ReconRolloutProject rr = currentLC.Recon_Rollout[i]; - if (rr.associatedID == vp.shipID.ToString()) + if (rr.AssociatedIdAsGuid == vp.shipID) { currentLC.Recon_Rollout.Remove(rr); i--; @@ -845,6 +847,20 @@ public LaunchComplex FindLCFromID(Guid guid) return LC(guid); } + public VesselRepairProject FindRepairForVessel(Vessel v) + { + foreach (var ksc in KSCs) + { + foreach (var lc in ksc.LaunchComplexes) + { + var r = lc.VesselRepairs.Find(r => r.AssociatedIdAsGuid == v.id); + if (r != null) return r; + } + } + + return null; + } + #endregion #region KSCSwitcher section @@ -1152,7 +1168,7 @@ private void ProcessNewFlight() FlightGlobals.ActiveVessel.vesselName = vp.shipName; } if (vesselLC == null) vesselLC = ActiveSC.ActiveLC; - if (vesselLC.Recon_Rollout.FirstOrDefault(r => r.associatedID == vp.shipID.ToString()) is ReconRolloutProject rollout) + if (vesselLC.Recon_Rollout.FirstOrDefault(r => r.AssociatedIdAsGuid == vp.shipID) is ReconRolloutProject rollout) vesselLC.Recon_Rollout.Remove(rollout); LaunchedVessel = new VesselProject(); @@ -1631,13 +1647,24 @@ public void ProgressBuildTime(double UTDiff) //Reset the associated launchpad id when rollback completes Profiler.BeginSample("RP0ProgressBuildTime.ReconRollout.FindVPesselByID"); if (rr.RRType == ReconRolloutProject.RolloutReconType.Rollback && rr.IsComplete() - && KCTUtilities.FindVPByID(rr.LC, new Guid(rr.associatedID)) is VesselProject vp) + && KCTUtilities.FindVPByID(rr.LC, rr.AssociatedIdAsGuid) is VesselProject vp) { vp.launchSiteIndex = -1; } Profiler.EndSample(); } + for (int i = currentLC.VesselRepairs.Count; i-- > 0;) + { + var vr = currentLC.VesselRepairs[i]; + vr.IncrementProgress(UTDiff); + if (vr.IsComplete() && HighLogic.LoadedSceneIsFlight && + vr.ApplyRepairs()) + { + currentLC.VesselRepairs.Remove(vr); + } + } + currentLC.Recon_Rollout.RemoveAll(rr => rr.RRType != ReconRolloutProject.RolloutReconType.Rollout && rr.RRType != ReconRolloutProject.RolloutReconType.AirlaunchMount && rr.IsComplete()); } @@ -1720,6 +1747,34 @@ private void DoNormalRecovery() _recoverCallback.Invoke(); } + private void QueueRepairFailures() + { + KCT_Preset_General settings = PresetManager.Instance.ActivePreset.GeneralSettings; + if (settings.BuildTimes) + { + var dataModule = FlightGlobals.ActiveVessel.vesselModules.Find(vm => vm is KCTVesselTracker) as KCTVesselTracker; + if (dataModule != null && dataModule.Data.FacilityBuiltIn == EditorFacility.VAB) + { + string launchSite = FlightDriver.LaunchSiteName; + LaunchComplex lc = Instance.FindLCFromID(dataModule.Data.LCID); + if (lc != null) + { + if (lc.LCType == LaunchComplexType.Pad && lc.ActiveLPInstance != null + && (launchSite == "LaunchPad" || lc.LaunchPads.Find(p => p.name == launchSite) == null)) + { + launchSite = lc.ActiveLPInstance.name; + } + var proj = new VesselRepairProject(FlightGlobals.ActiveVessel, launchSite, lc); + lc.VesselRepairs.Add(proj); + } + } + } + else + { + // Do immediately? + } + } + private void RecoveryChoiceTS() { if (!(_trackingStation != null && _trackingStation.SelectedVessel is Vessel selectedVessel)) @@ -1751,10 +1806,11 @@ private void RecoveryChoiceFlight() return; } - bool isSPHAllowed = KCTUtilities.IsSphRecoveryAvailable(FlightGlobals.ActiveVessel); - bool isVABAllowed = KCTUtilities.IsVabRecoveryAvailable(FlightGlobals.ActiveVessel); + Vessel v = FlightGlobals.ActiveVessel; + bool isSPHAllowed = KCTUtilities.IsSphRecoveryAvailable(v); + bool isVABAllowed = KCTUtilities.IsVabRecoveryAvailable(v); var options = new List(); - if (!FlightGlobals.ActiveVessel.isEVA) + if (!v.isEVA) { string nodeTitle = ResearchAndDevelopment.GetTechnologyTitle(Database.SettingsSC.VABRecoveryTech); string techLimitText = string.IsNullOrEmpty(nodeTitle) ? string.Empty : @@ -1777,6 +1833,15 @@ private void RecoveryChoiceFlight() { tooltipText = "Vessel will be scrapped and the total value of recovered parts will be refunded." }); + + if (TFInterop.HasSupportForReset && v.GetVesselBuiltAt() != EditorFacility.SPH && + TFInterop.VesselHasFailedParts(v) && FindRepairForVessel(v) == null) + { + options.Add(new DialogGUIButtonWithTooltip("Repair failures", QueueRepairFailures) + { + tooltipText = "All failures will be repaired without having to leave the flight scene." + }); + } } else { diff --git a/Source/RP0/UI/KCT/GUI_BuildList.cs b/Source/RP0/UI/KCT/GUI_BuildList.cs index dc9a278a648..98d8e53dedc 100644 --- a/Source/RP0/UI/KCT/GUI_BuildList.cs +++ b/Source/RP0/UI/KCT/GUI_BuildList.cs @@ -27,7 +27,7 @@ public enum RenameType { None, Vessel, Pad, LaunchComplex }; private static GUIStyle _redText, _yellowText, _greenText, _blobText, _yellowButton, _redButton, _greenButton; private static GUIContent _settingsTexture, _planeTexture, _rocketTexture, _techTexture, _constructTexture, - _reconTexture, _rolloutTexture, _rollbackTexture, _airlaunchTexture, _recoveryTexture, _hangarTexture; + _reconTexture, _rolloutTexture, _rollbackTexture, _airlaunchTexture, _recoveryTexture, _hangarTexture, _repairTexture; private const int _width1 = 120; private const int _width2 = 100; private const int _butW = 20; @@ -114,6 +114,7 @@ public static void InitBuildListVars() _rolloutTexture = new GUIContent(GameDatabase.Instance.GetTexture("RP-1/Resources/KCT_rollout16", false)); _settingsTexture = new GUIContent(GameDatabase.Instance.GetTexture("RP-1/Resources/KCT_settings16", false)); _techTexture = new GUIContent(GameDatabase.Instance.GetTexture("RP-1/Resources/KCT_tech16", false)); + _repairTexture = new GUIContent(GameDatabase.Instance.GetTexture("RP-1/Resources/KCT_repair", false)); } public static void DrawBuildListWindow(int windowID) @@ -139,19 +140,19 @@ public static void DrawBuildListWindow(int windowID) } else if (reconRoll.RRType == ReconRolloutProject.RolloutReconType.Rollout) { - VesselProject associated = reconRoll.LC.Warehouse.FirstOrDefault(vp => vp.shipID.ToString() == reconRoll.associatedID); + VesselProject associated = reconRoll.LC.Warehouse.FirstOrDefault(vp => vp.shipID == reconRoll.AssociatedIdAsGuid); txt = $"{associated.shipName} Rollout"; locTxt = reconRoll.launchPadID; } else if (reconRoll.RRType == ReconRolloutProject.RolloutReconType.Rollback) { - VesselProject associated = reconRoll.LC.Warehouse.FirstOrDefault(vp => vp.shipID.ToString() == reconRoll.associatedID); + VesselProject associated = reconRoll.LC.Warehouse.FirstOrDefault(vp => vp.shipID == reconRoll.AssociatedIdAsGuid); txt = $"{associated.shipName} Rollback"; locTxt = reconRoll.launchPadID; } else if (reconRoll.RRType == ReconRolloutProject.RolloutReconType.Recovery) { - VesselProject associated = reconRoll.LC.Warehouse.FirstOrDefault(vp => vp.shipID.ToString() == reconRoll.associatedID); + VesselProject associated = reconRoll.LC.Warehouse.FirstOrDefault(vp => vp.shipID == reconRoll.AssociatedIdAsGuid); txt = $"{associated.shipName} Recovery"; locTxt = associated.LC.Name; } @@ -237,12 +238,12 @@ public static void DrawBuildListWindow(int windowID) } else if (reconRoll.RRType == ReconRolloutProject.RolloutReconType.Rollout) { - VesselProject associated = reconRoll.LC.Warehouse.FirstOrDefault(vp => vp.shipID.ToString() == reconRoll.associatedID); + VesselProject associated = reconRoll.LC.Warehouse.FirstOrDefault(vp => vp.shipID == reconRoll.AssociatedIdAsGuid); txt += $"{associated.shipName} rollout at {reconRoll.launchPadID}"; } else if (reconRoll.RRType == ReconRolloutProject.RolloutReconType.Rollback) { - VesselProject associated = reconRoll.LC.Warehouse.FirstOrDefault(vp => vp.shipID.ToString() == reconRoll.associatedID); + VesselProject associated = reconRoll.LC.Warehouse.FirstOrDefault(vp => vp.shipID == reconRoll.AssociatedIdAsGuid); txt += $"{associated.shipName} rollback at {reconRoll.launchPadID}"; } else @@ -631,6 +632,7 @@ private static void RenderCombinedList() _allItems.Add(b); } _allItems.AddRange(l.Recon_Rollout); + _allItems.AddRange(l.VesselRepairs); } accTime = 0d; foreach (var c in k.Constructions) @@ -689,6 +691,10 @@ private static void RenderCombinedList() else GUILayout.Label(r.GetItemName()); } + else if (t is VesselRepairProject) + { + GUILayout.Label(t.GetItemName()); + } else if (t is VesselProject b) GUILayout.Label($"{b.LC.Name}: {b.GetItemName()}"); else if (t is ConstructionProject constr) @@ -822,6 +828,9 @@ private static GUIContent GetTypeIcon(ISpaceCenterProject b) case ProjectType.TechNode: return _techTexture; + + case ProjectType.VesselRepair: + return _repairTexture; } return _constructTexture; @@ -841,7 +850,10 @@ private static void RenderBuildList() _scrollPos = GUILayout.BeginScrollView(_scrollPos, GUILayout.Height(375)); if (activeLC.LCType == LaunchComplexType.Pad) + { + RenderRepairs(); RenderRollouts(); + } RenderVesselsBeingBuilt(activeLC); RenderWarehouse(); @@ -867,14 +879,47 @@ private static void RenderRollouts() { GUILayout.BeginHorizontal(); double tLeft = reconditioning.GetTimeLeft(); - if (!HighLogic.LoadedSceneIsEditor && reconditioning.GetBuildRate() > 0 && GUILayout.Button(new GUIContent("Warp To", $"√ Gain/Loss:\n{SpaceCenterManagement.Instance.GetBudgetDelta(tLeft):N0}"), GUILayout.Width((_butW + 4) * 3))) + if (!HighLogic.LoadedSceneIsEditor && reconditioning.GetBuildRate() > 0 && + GUILayout.Button(new GUIContent("Warp To", $"√ Gain/Loss:\n{SpaceCenterManagement.Instance.GetBudgetDelta(tLeft):N0}"), GUILayout.Width((_butW + 4) * 3))) { KCTWarpController.Create(reconditioning); } DrawTypeIcon(reconditioning); GUILayout.Label($"Reconditioning: {reconditioning.launchPadID}"); GUILayout.Label($"{reconditioning.GetFractionComplete():P2}", GetLabelRightAlignStyle(), GUILayout.Width(_width1 / 2)); - GUILayout.Label(RP0DTUtils.GetColonFormattedTimeWithTooltip(tLeft, "recon"+reconditioning.launchPadID), GetLabelRightAlignStyle(), GUILayout.Width(_width2)); + GUILayout.Label(RP0DTUtils.GetColonFormattedTimeWithTooltip(tLeft, "recon" + reconditioning.launchPadID), GetLabelRightAlignStyle(), GUILayout.Width(_width2)); + + GUILayout.EndHorizontal(); + } + } + + private static void RenderRepairs() + { + LaunchComplex activeLC = SpaceCenterManagement.EditorShipEditingMode ? SpaceCenterManagement.Instance.EditedVessel.LC : SpaceCenterManagement.Instance.ActiveSC.ActiveLC; + foreach (VesselRepairProject repair in activeLC.VesselRepairs) + { + GUILayout.BeginHorizontal(); + double tLeft = repair.GetTimeLeft(); + if (!HighLogic.LoadedSceneIsEditor && repair.GetBuildRate() > 0 && + GUILayout.Button(new GUIContent("Warp To", $"√ Gain/Loss:\n{SpaceCenterManagement.Instance.GetBudgetDelta(tLeft):N0}"), GUILayout.Width((_butW + 4) * 3))) + { + KCTWarpController.Create(repair); + } + + if (GUILayout.Button("X", GUILayout.Width(_butW))) + { + DialogGUIBase[] options = new DialogGUIBase[2]; + options[0] = new DialogGUIButton("Yes", () => activeLC.VesselRepairs.Remove(repair)); + options[1] = new DialogGUIButton("No", () => { }); + MultiOptionDialog diag = new MultiOptionDialog("scrapVesselPopup", $"Are you sure you want to cancel this repair?", + "Cancel Repair", null, options: options); + PopupDialog.SpawnPopupDialog(new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), diag, false, HighLogic.UISkin).HideGUIsWhilePopup(); + } + + DrawTypeIcon(repair); + GUILayout.Label($"Repair: {repair.shipName}"); + GUILayout.Label($"{repair.GetFractionComplete():P2}", GetLabelRightAlignStyle(), GUILayout.Width(_width1 / 2)); + GUILayout.Label(RP0DTUtils.GetColonFormattedTimeWithTooltip(tLeft, "repair" + repair.associatedID), GetLabelRightAlignStyle(), GUILayout.Width(_width2)); GUILayout.EndHorizontal(); } diff --git a/Source/RP0/Utilities/Formula.cs b/Source/RP0/Utilities/Formula.cs index 2f6ef2a3065..4f0bf86d8ca 100644 --- a/Source/RP0/Utilities/Formula.cs +++ b/Source/RP0/Utilities/Formula.cs @@ -163,6 +163,11 @@ public static double GetReconditioningBP(VesselProject vessel) return vessel.buildPoints * 0.01d + Math.Max(1, vessel.GetTotalMass() - 20d) * 2000d; } + public static double GetVesselRepairBP(VesselProject vessel) + { + return GetRolloutBP(vessel) / 7.5; + } + public static double GetRecoveryBPSPH(VesselProject vessel) { double costDeltaHighPow; diff --git a/Source/RP0/Utilities/KCTUtilities.cs b/Source/RP0/Utilities/KCTUtilities.cs index 1b551882df3..435fa569fef 100644 --- a/Source/RP0/Utilities/KCTUtilities.cs +++ b/Source/RP0/Utilities/KCTUtilities.cs @@ -667,7 +667,7 @@ public static ISpaceCenterProject GetNextThingToFinish() continue; foreach (ISpaceCenterProject vp in LC.BuildList) _checkTime(vp, ref shortestTime, ref thing); - foreach (ISpaceCenterProject rr in LC.Recon_Rollout) + foreach (ISpaceCenterProject rr in LC.GetAllLCOps()) _checkTime(rr, ref shortestTime, ref thing); } foreach (ISpaceCenterProject ub in KSC.Constructions)