diff --git a/Source/RP0/Avionics/ModuleAvionics.cs b/Source/RP0/Avionics/ModuleAvionics.cs index 3bf87eb6fe7..fea79f7fa63 100644 --- a/Source/RP0/Avionics/ModuleAvionics.cs +++ b/Source/RP0/Avionics/ModuleAvionics.cs @@ -3,6 +3,7 @@ using UniLinq; using System.Reflection; using UnityEngine; +using RealAntennas; namespace RP0 { @@ -32,6 +33,9 @@ public class ModuleAvionics : PartModule [KSPField(isPersistant = true)] public bool systemEnabled = true; + [KSPField(isPersistant = true, guiActive = true, guiName = "Permanently disabled", groupName = PAWGroup, groupDisplayName = PAWGroup)] + public bool dead = false; + [KSPField] public string techRequired = ""; @@ -52,7 +56,7 @@ public class ModuleAvionics : PartModule protected virtual float GetInternalMassLimit() => massLimit; protected virtual float GetEnabledkW() => enabledkW; protected virtual float GetDisabledkW() => disabledkW; - protected virtual bool GetToggleable() => toggleable; + protected virtual bool GetToggleable() => !dead && toggleable; internal bool TechAvailable => string.IsNullOrEmpty(techRequired) || HighLogic.CurrentGame == null || HighLogic.CurrentGame.Mode == Game.Modes.SANDBOX || ResearchAndDevelopment.GetTechnologyState(techRequired) == RDTech.State.Available; /// @@ -63,7 +67,7 @@ public float CurrentMassLimit { get { - if (currentlyEnabled && TechAvailable && !IsLockedByInterplanetary) + if (!dead && currentlyEnabled && TechAvailable && !IsLockedByInterplanetary) return GetInternalMassLimit(); else return 0f; @@ -103,14 +107,18 @@ protected void InitializeResourceRate() res.rate = 0; // Disable the CommandModule consuming electric charge on init. } - public float PowerDraw(bool onRails = false) => - part.protoModuleCrew?.Count > 0 || (systemEnabled && !(GetToggleable() && onRails)) ? - GetEnabledkW() : GetDisabledkW(); + public float GetPowerDraw(bool onRails = false) + { + if (dead) return 0; + + return part.protoModuleCrew?.Count > 0 || (systemEnabled && !(GetToggleable() && onRails)) ? + GetEnabledkW() : GetDisabledkW(); + } protected void UpdateRate(bool onRails = false) { currentlyEnabled = systemEnabled && !(GetToggleable() && onRails); - float currentKW = PowerDraw(onRails); + float currentKW = GetPowerDraw(onRails); ecConsumption = new KeyValuePair(ecName, -currentKW); currentWatts = currentKW * 1000; // If requested, let Kerbalism handle power draw, else handle via ModuleCommand resourceHandler. @@ -126,15 +134,18 @@ private ModuleResource FindCommandChargeResource() => protected virtual void SetActionsAndGui() { - var toggleAble = GetToggleable(); - Events[nameof(ToggleEvent)].guiActive = toggleAble; - Events[nameof(ToggleEvent)].guiActiveEditor = toggleAble; + bool toggleable = GetToggleable(); + Events[nameof(ToggleEvent)].guiActive = toggleable; + Events[nameof(ToggleEvent)].guiActiveEditor = toggleable; Events[nameof(ToggleEvent)].guiName = (systemEnabled ? "Shutdown" : "Activate") + " Avionics"; - Events[nameof(ToggleEvent)].active = toggleAble; - Actions[nameof(ActivateAction)].active = (!systemEnabled || HighLogic.LoadedSceneIsEditor) && toggleAble; - Actions[nameof(ShutdownAction)].active = (systemEnabled || HighLogic.LoadedSceneIsEditor) && toggleAble; - Actions[nameof(ToggleAction)].active = toggleAble; - Fields[nameof(currentWatts)].guiActive = toggleAble; + Events[nameof(ToggleEvent)].active = toggleable; + Actions[nameof(ActivateAction)].active = (!systemEnabled || HighLogic.LoadedSceneIsEditor) && toggleable; + Actions[nameof(ShutdownAction)].active = (systemEnabled || HighLogic.LoadedSceneIsEditor) && toggleable; + Actions[nameof(ToggleAction)].active = toggleable; + Fields[nameof(currentWatts)].guiActive = toggleable; + Actions[nameof(KillAction)].active = !dead; + Events[nameof(KillEvent)].guiActive = !dead; + Events[nameof(KillEvent)].active = !dead; } protected void StageActivated(int stage) @@ -166,6 +177,30 @@ protected IEnumerator CheckRenameDebris() } } + /// + /// Even after disabling ModuleCommand, the vessel will still have comms and be controllable through Kerbalism. + /// If all the antenna PMs are disabled then RA will make sure that the issue is fixed in a permanent way. + /// + protected void KillAntennasIfLastAvionicsDead() + { + bool anyAlive = false; + List avionicsModules = part.vessel.FindPartModulesImplementing(); + foreach (ModuleAvionics am in avionicsModules) + { + anyAlive |= !am.dead; + } + + if (!anyAlive) + { + List antennaModules = part.vessel.FindPartModulesImplementing(); + foreach (ModuleRealAntenna mra in antennaModules) + { + if (mra.Condition != AntennaCondition.Disabled) + mra.PermanentShutdownAction(null); + } + } + } + #endregion #region Overrides and callbacks @@ -181,6 +216,11 @@ public override void OnAwake() // OnStartFinished() instead of OnStart(), to let ModuleCommand configure itself first. public override void OnStartFinished(StartState _) { + if (dead) + { + DisableModuleCommand(); + } + InitializeResourceRate(); if (!GetToggleable()) toggleable = false; @@ -215,7 +255,7 @@ private void RailChange(Vessel v, bool onRails) // Too many ways to exit a scene, so always write the Disabled power draw public override void OnSave(ConfigNode node) { - node.SetValue(nameof(currentWatts), PowerDraw(true) * 1000); + node.SetValue(nameof(currentWatts), GetPowerDraw(true) * 1000); } protected void OnDestroy() @@ -256,6 +296,18 @@ public override string GetInfo() return retStr; } + protected void DisableModuleCommand() + { + ModuleCommand command = part.FindModuleImplementing(); + if (command != null) + { + command.enabled = false; + command.isEnabled = false; + command.moduleIsEnabled = false; + part.isControlSource = Vessel.ControlLevel.NONE; + } + } + #endregion #region Actions and Events @@ -264,12 +316,12 @@ public override string GetInfo() public void ToggleEvent() { systemEnabled = !systemEnabled; - if (part.protoModuleCrew?.Count > 0) + if (!dead && part.protoModuleCrew?.Count > 0) { systemEnabled = true; ScreenMessages.PostScreenMessage("Cannot shut down avionics while crewed"); } - else if (!GetToggleable()) + else if (!dead && !GetToggleable()) { systemEnabled = true; ScreenMessages.PostScreenMessage("Cannot shut down avionics on this part"); @@ -299,6 +351,26 @@ public void ActivateAction(KSPActionParam _) systemEnabled = false; ToggleEvent(); } + + [KSPEvent(guiActive = true, guiActiveEditor = false, guiName = "Disable Avionics permanently", groupName = PAWGroup)] + public void KillEvent() + { + var options = new DialogGUIBase[] { + new DialogGUIButton("Yes", () => KillAction(null)), + new DialogGUIButton("No", () => {}) + }; + var dialog = new MultiOptionDialog("ConfirmDisableAvionics", "Are you sure you want to permanently disable the avionics unit? Doing this will prevent avionics from consuming power but it will no longer provide any control either.", "Disable Avionics", HighLogic.UISkin, 300, options); + PopupDialog.SpawnPopupDialog(dialog, true, HighLogic.UISkin); + } + + [KSPAction("Disable Avionics permanently")] + public void KillAction(KSPActionParam _) + { + dead = true; + DisableModuleCommand(); + KillAntennasIfLastAvionicsDead(); + ToggleEvent(); + } #endregion #region Kerbalism diff --git a/Source/RP0/Avionics/ModuleProceduralAvionics.cs b/Source/RP0/Avionics/ModuleProceduralAvionics.cs index e4606daa05c..e95e8e5d6e9 100644 --- a/Source/RP0/Avionics/ModuleProceduralAvionics.cs +++ b/Source/RP0/Avionics/ModuleProceduralAvionics.cs @@ -139,7 +139,7 @@ internal float GetShieldedAvionicsMass(float controllableMass) private static float GetPolynomial(float value, float exponent, float constant, float factor) => (Mathf.Pow(value, exponent) + constant) * factor; private static float GetInversePolynomial(float value, float exponent, float constant, float factor) => Mathf.Pow(value / factor - constant, 1 / exponent); - protected override bool GetToggleable() => CurrentProceduralAvionicsTechNode.disabledPowerFactor > 0; + protected override bool GetToggleable() => !dead && CurrentProceduralAvionicsTechNode.disabledPowerFactor > 0; protected override string GetTonnageString() => "This part can be configured to allow control of vessels up to any mass."; diff --git a/Source/RP0/Harmony/Vessel.cs b/Source/RP0/Harmony/Vessel.cs new file mode 100644 index 00000000000..02fbd757e5c --- /dev/null +++ b/Source/RP0/Harmony/Vessel.cs @@ -0,0 +1,45 @@ +using CommNet; +using HarmonyLib; + +namespace RP0.Harmony +{ + [HarmonyPatch(typeof(Vessel))] + internal class PatchVessel + { + [HarmonyPatch("GetControlLevel")] + internal static bool Prefix_GetControlLevel(Vessel __instance, ref Vessel.ControlLevel __result) + { + if (__instance.isEVA && __instance.crew.Count > 0) + { + if (!__instance.crew[0].inactive) + { + __result = Vessel.ControlLevel.PARTIAL_MANNED; + return false; + } + __result = Vessel.ControlLevel.NONE; + return false; + } + + if (__instance.connection != null && CommNetScenario.Instance != null && CommNetScenario.CommNetEnabled) + { + __result = __instance.connection.GetControlLevel(); + if (__result == Vessel.ControlLevel.NONE) return false; + } + + var controlLevel = Vessel.ControlLevel.NONE; + int count = __instance.parts.Count; + while (count-- > 0) + { + Part part = __instance.parts[count]; + if (part.isControlSource > controlLevel) + { + controlLevel = part.isControlSource; + } + } + + if (controlLevel < __result) __result = controlLevel; + + return false; + } + } +} diff --git a/Source/RP0/Properties/AssemblyInfo.cs b/Source/RP0/Properties/AssemblyInfo.cs index ef8fdc5a63d..7f26d07c72a 100644 --- a/Source/RP0/Properties/AssemblyInfo.cs +++ b/Source/RP0/Properties/AssemblyInfo.cs @@ -45,7 +45,7 @@ [assembly: KSPAssemblyDependency("ModularFlightIntegrator", 1, 0)] [assembly: KSPAssemblyDependency("RealFuels", 15, 7)] -[assembly: KSPAssemblyDependency("RealAntennas", 2, 4)] +[assembly: KSPAssemblyDependency("RealAntennas", 2, 6)] [assembly: KSPAssemblyDependency("ROUtils", 1, 0, 1)] [assembly: KSPAssemblyDependency("ClickThroughBlocker", 1, 8)] [assembly: KSPAssemblyDependency("ContractConfigurator", 2, 6)] diff --git a/Source/RP0/RP0.csproj b/Source/RP0/RP0.csproj index cd560a76ac6..2beebf37349 100644 --- a/Source/RP0/RP0.csproj +++ b/Source/RP0/RP0.csproj @@ -49,6 +49,7 @@ +