diff --git a/GameData/RealAntennas/KSPCommunityFixes.cfg b/GameData/RealAntennas/KSPCommunityFixes.cfg new file mode 100644 index 0000000..f5784d7 --- /dev/null +++ b/GameData/RealAntennas/KSPCommunityFixes.cfg @@ -0,0 +1,4 @@ +@KSP_COMMUNITY_FIXES:FOR[RealAntennas] +{ + @ModUpgradePipeline = true +} diff --git a/src/RealAntennasProject/ModuleRealAntenna.cs b/src/RealAntennasProject/ModuleRealAntenna.cs index d0fc912..1cca000 100644 --- a/src/RealAntennasProject/ModuleRealAntenna.cs +++ b/src/RealAntennasProject/ModuleRealAntenna.cs @@ -6,14 +6,27 @@ namespace RealAntennas { + [Flags] + public enum AntennaCondition + { + Enabled = 1 << 0, + Disabled = 1 << 1, + PermanentShutdown = 1 << 2, + Broken = 1 << 3 + } + public class ModuleRealAntenna : ModuleDataTransmitter, IPartCostModifier, IPartMassModifier { private const string PAWGroup = "RealAntennas"; private const string PAWGroupPlanner = "Antenna Planning"; - [KSPField(guiActiveEditor = true, guiName = "Antenna", isPersistant = true, groupName = PAWGroup, groupDisplayName = PAWGroup), + + [KSPField(guiActiveEditor = true, guiName = "Antenna", groupName = PAWGroup, groupDisplayName = PAWGroup), UI_Toggle(disabledText = "Disabled", enabledText = "Enabled", scene = UI_Scene.Editor)] public bool _enabled = true; + [KSPField(guiActiveEditor = false, guiActive = true, guiName = "Condition", isPersistant = true, groupName = PAWGroup, groupDisplayName = PAWGroup)] + public AntennaCondition Condition = AntennaCondition.Enabled; + [KSPField(guiActive = true, guiActiveEditor = true, guiName = "Gain", guiUnits = " dBi", guiFormat = "F1", groupName = PAWGroup, groupDisplayName = PAWGroup)] public float Gain; // Physical directionality, measured in dBi @@ -136,6 +149,8 @@ public override void OnStart(StartState state) Fields[nameof(_enabled)].uiControlEditor.onFieldChanged = OnAntennaEnableChange; (Fields[nameof(TxPower)].uiControlEditor as UI_FloatRange).maxValue = MaxTxPower; + _enabled = Condition != AntennaCondition.Disabled; + actualMaxTechLevel = maxTechLevel; // maxTechLevel value can come from applied PartUpgrades int maxLvlFromParams = HighLogic.CurrentGame.Parameters.CustomParams().MaxTechLevel; if (HighLogic.CurrentGame.Mode != Game.Modes.CAREER) @@ -164,13 +179,13 @@ public override void OnStart(StartState state) ConfigBandOptions(); SetupIdlePower(); RecalculateFields(); - SetFieldVisibility(_enabled); + SetFieldVisibility(); ApplyTLColoring(); if (HighLogic.LoadedSceneIsFlight) { - isEnabled = _enabled; - if (_enabled) + isEnabled = Condition != AntennaCondition.Disabled; + if (isEnabled) GameEvents.OnGameSettingsApplied.Add(ApplyGameSettings); } else if (HighLogic.LoadedSceneIsEditor) @@ -181,10 +196,10 @@ public override void OnStart(StartState state) private void SetupIdlePower() { - if (HighLogic.LoadedSceneIsFlight && _enabled) + if (HighLogic.LoadedSceneIsFlight && Condition != AntennaCondition.Disabled) { var electricCharge = resHandler.inputResources.First(x => x.id == PartResourceLibrary.ElectricityHashcode); - electricCharge.rate = (Kerbalism.Kerbalism.KerbalismAssembly is null) ? RAAntenna.IdlePowerDraw : 0; + electricCharge.rate = Condition != AntennaCondition.PermanentShutdown && (Kerbalism.Kerbalism.KerbalismAssembly is null) ? RAAntenna.IdlePowerDraw : 0; string err = ""; resHandler.UpdateModuleResourceInputs(ref err, 1, 1, false, false); } @@ -192,7 +207,7 @@ private void SetupIdlePower() public void FixedUpdate() { - if (HighLogic.LoadedSceneIsFlight && _enabled) + if (HighLogic.LoadedSceneIsFlight && Condition != AntennaCondition.Disabled && Condition != AntennaCondition.PermanentShutdown) { RAAntenna.AMWTemp = (AMWTemp > 0) ? AMWTemp : Convert.ToSingle(part.temperature); //part.AddThermalFlux(req / Time.fixedDeltaTime); @@ -231,16 +246,24 @@ private void SetupBaseFields() if (Fields[nameof(powerText)] is BaseField bf) bf.guiActive = bf.guiActiveEditor = false; // "Antenna Rating" } - private void SetFieldVisibility(bool en) + private void SetFieldVisibility() { - Fields[nameof(Gain)].guiActiveEditor = Fields[nameof(Gain)].guiActive = en; - Fields[nameof(TxPower)].guiActiveEditor = Fields[nameof(TxPower)].guiActive = en; - Fields[nameof(TechLevel)].guiActiveEditor = Fields[nameof(TechLevel)].guiActive = en; - Fields[nameof(RFBand)].guiActiveEditor = Fields[nameof(RFBand)].guiActive = en; - Fields[nameof(sActivePowerConsumed)].guiActiveEditor = Fields[nameof(sActivePowerConsumed)].guiActive = en; - Fields[nameof(sIdlePowerConsumed)].guiActiveEditor = Fields[nameof(sIdlePowerConsumed)].guiActive = en; - Fields[nameof(sAntennaTarget)].guiActive = en; + bool showFields = Condition != AntennaCondition.Disabled && Condition != AntennaCondition.PermanentShutdown; + Fields[nameof(Gain)].guiActiveEditor = Fields[nameof(Gain)].guiActive = showFields; + Fields[nameof(TxPower)].guiActiveEditor = Fields[nameof(TxPower)].guiActive = showFields; + Fields[nameof(TechLevel)].guiActiveEditor = Fields[nameof(TechLevel)].guiActive = showFields; + Fields[nameof(RFBand)].guiActiveEditor = Fields[nameof(RFBand)].guiActive = showFields; + Fields[nameof(sActivePowerConsumed)].guiActiveEditor = Fields[nameof(sActivePowerConsumed)].guiActive = showFields; + Fields[nameof(sIdlePowerConsumed)].guiActiveEditor = Fields[nameof(sIdlePowerConsumed)].guiActive = showFields; + Fields[nameof(sAntennaTarget)].guiActive = showFields; Fields[nameof(plannerActiveTxTime)].guiActiveEditor = Kerbalism.Kerbalism.KerbalismAssembly is System.Reflection.Assembly; + Actions[nameof(PermanentShutdownAction)].active = showFields; + Events[nameof(PermanentShutdownEvent)].guiActive = showFields; + Events[nameof(PermanentShutdownEvent)].active = showFields; + Events[nameof(AntennaPlanningGUI)].active = showFields; + Events[nameof(AntennaPlanningGUI)].guiActive = showFields; + Events[nameof(DebugAntenna)].active = showFields; + Events[nameof(DebugAntenna)].guiActive = showFields; } private void SetupUICallbacks() @@ -263,7 +286,12 @@ private void UpdateMaxTechLevelInUI() } private void OnPlannerActiveTxTimeChanged(BaseField field, object obj) => RecalculatePlannerECConsumption(); - private void OnAntennaEnableChange(BaseField field, object obj) { SetFieldVisibility(_enabled); RecalculatePlannerECConsumption(); } + private void OnAntennaEnableChange(BaseField field, object obj) + { + Condition = _enabled ? AntennaCondition.Enabled : AntennaCondition.Disabled; + SetFieldVisibility(); + RecalculatePlannerECConsumption(); + } private void OnRFBandChange(BaseField f, object obj) => RecalculateFields(); private void OnTxPowerChange(BaseField f, object obj) => RecalculateFields(); private void OnTechLevelChange(BaseField f, object obj) // obj is the OLD value @@ -277,6 +305,29 @@ private void OnTechLevelChange(BaseField f, object obj) // obj is the OLD va private void OnTechLevelChangeSymmetry(BaseField f, object obj) => ConfigBandOptions(); + [KSPEvent(guiActive = true, guiActiveEditor = false, guiName = "Disable antenna permanently", groupName = PAWGroup)] + public void PermanentShutdownEvent() + { + var options = new DialogGUIBase[] { + new DialogGUIButton("Yes", () => PermanentShutdownAction(null)), + new DialogGUIButton("No", () => {}) + }; + var dialog = new MultiOptionDialog("ConfirmDisableAntenna", "Are you sure you want to permanently disable the antenna? Doing this will prevent it from consuming power but the operation is irreversible.", "Disable antenna", HighLogic.UISkin, 300, options); + PopupDialog.SpawnPopupDialog(dialog, true, HighLogic.UISkin); + } + + [KSPAction("Disable antenna permanently")] + public void PermanentShutdownAction(KSPActionParam _) + { + _enabled = false; + Condition = AntennaCondition.PermanentShutdown; + SetFieldVisibility(); + SetupIdlePower(); + GameEvents.onVesselWasModified.Fire(vessel); // Need to notify RACommNetVessel about disabling antennas + RACommNetScenario scen = RACommNetScenario.Instance as RACommNetScenario; + scen?.Network?.ResetNetwork(); + } + private void ApplyGameSettings() { StockRateModifier = HighLogic.CurrentGame.Parameters.CustomParams().StockRateModifier; @@ -343,7 +394,7 @@ public override string GetInfo() return res; } - public override bool CanComm() => base.CanComm() && (!Deployable || Deployed); + public override bool CanComm() => Condition == AntennaCondition.Enabled && (!Deployable || Deployed) && base.CanComm(); public override string ToString() => RAAntenna.ToString(); @@ -419,9 +470,9 @@ private IEnumerator StockScienceFixer() #region Cost and Mass Modifiers public float GetModuleCost(float defaultCost, ModifierStagingSituation sit) => - _enabled ? RAAntenna.TechLevelInfo.BaseCost + (RAAntenna.TechLevelInfo.CostPerWatt * RATools.LinearScale(TxPower)/1000) : 0; + Condition != AntennaCondition.Disabled ? RAAntenna.TechLevelInfo.BaseCost + (RAAntenna.TechLevelInfo.CostPerWatt * RATools.LinearScale(TxPower)/1000) : 0; public float GetModuleMass(float defaultMass, ModifierStagingSituation sit) => - _enabled && applyMassModifier ? (RAAntenna.TechLevelInfo.BaseMass + (RAAntenna.TechLevelInfo.MassPerWatt * RATools.LinearScale(TxPower) / 1000)) / 1000 : 0; + Condition != AntennaCondition.Disabled && applyMassModifier ? (RAAntenna.TechLevelInfo.BaseMass + (RAAntenna.TechLevelInfo.MassPerWatt * RATools.LinearScale(TxPower) / 1000)) / 1000 : 0; public ModifierChangeWhen GetModuleCostChangeWhen() => ModifierChangeWhen.FIXED; public ModifierChangeWhen GetModuleMassChangeWhen() => ModifierChangeWhen.FIXED; #endregion @@ -435,8 +486,9 @@ public string PlannerUpdate(List> resources, Celest } private void RecalculatePlannerECConsumption() { + bool consumesPower = Condition != AntennaCondition.Disabled && Condition != AntennaCondition.PermanentShutdown; // RAAntenna.IdlePowerDraw is in kW (ec/s), PowerDrawLinear is in mW - double ec = _enabled ? RAAntenna.IdlePowerDraw + (RAAntenna.PowerDrawLinear * 1e-6 * plannerActiveTxTime) : 0; + double ec = consumesPower ? RAAntenna.IdlePowerDraw + (RAAntenna.PowerDrawLinear * 1e-6 * plannerActiveTxTime) : 0; plannerECConsumption = new KeyValuePair("ElectricCharge", -ec); } @@ -455,7 +507,7 @@ public virtual bool Validate(out string validationError, out bool canBeResolved, costToResolve = 0; techToResolve = string.Empty; - if (!_enabled || techLevel <= actualMaxTechLevel) return true; + if (Condition == AntennaCondition.Disabled || techLevel <= actualMaxTechLevel) return true; PartUpgradeHandler.Upgrade upgd = GetUpgradeForTL(techLevel); if (PartUpgradeManager.Handler.IsAvailableToUnlock(upgd.name)) diff --git a/src/RealAntennasProject/Properties/AssemblyInfo.cs b/src/RealAntennasProject/Properties/AssemblyInfo.cs index 38988fc..b0ec8f3 100644 --- a/src/RealAntennasProject/Properties/AssemblyInfo.cs +++ b/src/RealAntennasProject/Properties/AssemblyInfo.cs @@ -32,7 +32,7 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.4.0.0")] +[assembly: AssemblyVersion("2.6.0.0")] #if CIBUILD [assembly: AssemblyFileVersion("@MAJOR@.@MINOR@.@PATCH@.@BUILD@")] [assembly: KSPAssembly("RealAntennas", @MAJOR@, @MINOR@)] diff --git a/src/RealAntennasProject/RACommNetVessel.cs b/src/RealAntennasProject/RACommNetVessel.cs index 322dab3..b08150c 100644 --- a/src/RealAntennasProject/RACommNetVessel.cs +++ b/src/RealAntennasProject/RACommNetVessel.cs @@ -175,7 +175,7 @@ protected List DiscoverAntennas() { foreach (ModuleRealAntenna ant in Vessel.FindPartModulesImplementing().ToList()) { - if (ant._enabled) + if (ant.Condition == AntennaCondition.Enabled) { ant.RAAntenna.ParentNode = Comm; if (DeployedLoaded(ant.part)) antennaList.Add(ant.RAAntenna); @@ -192,7 +192,10 @@ protected List DiscoverAntennas() foreach (ProtoPartModuleSnapshot snap in part.modules.Where(x => x.moduleName == ModuleRealAntenna.ModuleName)) { bool _enabled = true; - snap.moduleValues.TryGetValue(nameof(ModuleRealAntenna._enabled), ref _enabled); + string sState = null; + if (snap.moduleValues.TryGetValue(nameof(ModuleRealAntenna.Condition), ref sState)) + _enabled = sState == AntennaCondition.Enabled.ToString(); + // Doesn't get the correct PartModule if multiple, but the only impact is the name, which defaults to the part anyway. if (_enabled && part.partInfo.partPrefab.FindModuleImplementing() is ModuleRealAntenna mra && mra.CanCommUnloaded(snap)) { diff --git a/src/RealAntennasProject/RealAntennas.csproj b/src/RealAntennasProject/RealAntennas.csproj index 2822697..e19f7cb 100644 --- a/src/RealAntennasProject/RealAntennas.csproj +++ b/src/RealAntennasProject/RealAntennas.csproj @@ -88,6 +88,7 @@ + diff --git a/src/RealAntennasProject/UpgradeScripts/v2_6_AntennaState.cs b/src/RealAntennasProject/UpgradeScripts/v2_6_AntennaState.cs new file mode 100644 index 0000000..8558b15 --- /dev/null +++ b/src/RealAntennasProject/UpgradeScripts/v2_6_AntennaState.cs @@ -0,0 +1,65 @@ +using SaveUpgradePipeline; +using System; + +namespace RealAntennas.UpgradeScripts +{ + [UpgradeModule(LoadContext.SFS | LoadContext.Craft, sfsNodeUrl = "GAME/FLIGHTSTATE/VESSEL/PART", craftNodeUrl = "PART")] + public class v2_6_AntennaState : UpgradeScript + { + public override string Name => "RealAntennas Antenna Condition upgrader"; + public override string Description => "Update crafts to use new condition enum"; + public override Version EarliestCompatibleVersion => new Version(1, 0, 0); + public override Version TargetVersion => new Version(2, 6, 0); + + public override TestResult OnTest(ConfigNode node, LoadContext loadContext, ref string nodeName) + { + nodeName = NodeUtil.GetPartNodeNameValue(node, loadContext); + if (node.GetNode("MODULE", "name", nameof(ModuleRealAntenna)) is ConfigNode mecNode) + return TestResult.Upgradeable; + return TestResult.Pass; + } + + public override void OnUpgrade(ConfigNode node, LoadContext loadContext, ConfigNode parentNode) + { + var pmNode = node.GetNode("MODULE", "name", nameof(ModuleRealAntenna)); + var v = pmNode.GetValue("_enabled"); + var newState = string.Equals(v, "false", StringComparison.OrdinalIgnoreCase) ? AntennaCondition.Disabled : AntennaCondition.Enabled; + pmNode.AddValue(nameof(ModuleRealAntenna.Condition), newState.ToString()); + pmNode.RemoveValue("_enabled"); + } + } + + public class v2_6_AntennaState_KCTBase : v2_6_AntennaState + { + public override string Name { get => $"{base.Name} KCT-{nodeUrlSFS}"; } + public override TestResult OnTest(ConfigNode node, LoadContext loadContext, ref string nodeName) => + loadContext == LoadContext.Craft ? TestResult.Pass : base.OnTest(node, loadContext, ref nodeName); + } + + [UpgradeModule(LoadContext.SFS, sfsNodeUrl = "GAME/SCENARIO/KSC/VABList/KCTVessel/ShipNode/PART")] + public class v2_6_AntennaState_KCT1 : v2_6_AntennaState_KCTBase { } + + [UpgradeModule(LoadContext.SFS, sfsNodeUrl = "GAME/SCENARIO/KSC/SPHList/KCTVessel/ShipNode/PART")] + public class v2_6_AntennaState_KCT2 : v2_6_AntennaState_KCTBase { } + + [UpgradeModule(LoadContext.SFS, sfsNodeUrl = "GAME/SCENARIO/KSC/VABWarehouse/KCTVessel/ShipNode/PART")] + public class v2_6_AntennaState_KCT3 : v2_6_AntennaState_KCTBase { } + + [UpgradeModule(LoadContext.SFS, sfsNodeUrl = "GAME/SCENARIO/KSC/SPHWarehouse/KCTVessel/ShipNode/PART")] + public class v2_6_AntennaState_KCT4 : v2_6_AntennaState_KCTBase { } + + [UpgradeModule(LoadContext.SFS, sfsNodeUrl = "GAME/SCENARIO/KSC/VABPlans/KCTVessel/ShipNode/PART")] + public class v2_6_AntennaState_KCT5 : v2_6_AntennaState_KCTBase { } + + [UpgradeModule(LoadContext.SFS, sfsNodeUrl = "GAME/SCENARIO/KSC/SPHPlans/KCTVessel/ShipNode/PART")] + public class v2_6_AntennaState_KCT6 : v2_6_AntennaState_KCTBase { } + + [UpgradeModule(LoadContext.SFS, sfsNodeUrl = "GAME/SCENARIO/KSC/LaunchComplex/BuildList/KCTVessel/ShipNode/PART")] + public class v2_6_AntennaState_KCT7 : v2_6_AntennaState_KCTBase { } + + [UpgradeModule(LoadContext.SFS, sfsNodeUrl = "GAME/SCENARIO/KSC/LaunchComplex/Warehouse/KCTVessel/ShipNode/PART")] + public class v2_6_AntennaState_KCT8 : v2_6_AntennaState_KCTBase { } + + [UpgradeModule(LoadContext.SFS, sfsNodeUrl = "GAME/SCENARIO/KSC/LaunchComplex/Plans/KCTVessel/ShipNode/PART")] + public class v2_6_AntennaState_KCT9 : v2_6_AntennaState_KCTBase { } +}