From d3b04b382fc71544b25d703c06556f61806d79ac Mon Sep 17 00:00:00 2001 From: siimav Date: Tue, 11 Jun 2024 01:22:43 +0300 Subject: [PATCH] Integrate with the RP-1 config validation feature --- .../RealAntennasCommNetParams.cfg | 20 +-- GameData/RealAntennas/RealismOverhaul.cfg | 20 +-- src/RealAntennasProject/ModuleRealAntenna.cs | 126 +++++++++++++++++- src/RealAntennasProject/TechLevelInfo.cs | 17 ++- src/RealAntennasProject/Tools.cs | 14 ++ 5 files changed, 168 insertions(+), 29 deletions(-) diff --git a/GameData/RealAntennas/RealAntennasCommNetParams.cfg b/GameData/RealAntennas/RealAntennasCommNetParams.cfg index 46bd264..eba23ea 100644 --- a/GameData/RealAntennas/RealAntennasCommNetParams.cfg +++ b/GameData/RealAntennas/RealAntennasCommNetParams.cfg @@ -99,7 +99,7 @@ RealAntennasCommNetParams } TechLevelInfo { - name = TL0 + name = commsTL0 Level = 0 Description = WW2-era PowerEfficiency = 0.0555 @@ -116,7 +116,7 @@ RealAntennasCommNetParams } TechLevelInfo { - name = TL1 + name = commsTL1 Level = 1 Description = Lunar Range Comms, 1956: 26m antenna 1958 PowerEfficiency = 0.0769 @@ -133,7 +133,7 @@ RealAntennasCommNetParams } TechLevelInfo { - name = TL2 + name = commsTL2 Level = 2 Description = Digital Comms, 1959-1960 PowerEfficiency = 0.1 @@ -150,7 +150,7 @@ RealAntennasCommNetParams } TechLevelInfo { - name = TL3 + name = commsTL3 Level = 3 Description = Interplanetary Comms, 1961-1963: Maser 1962, S/C noise reduction 1961 (300-3000K=10dB noise temp) PowerEfficiency = 0.1304 @@ -167,7 +167,7 @@ RealAntennasCommNetParams } TechLevelInfo { - name = TL4 + name = commsTL4 Level = 4 Description = Improved Comms, 1964-1966: 64m Antenna 1967 PowerEfficiency = 0.1667 @@ -184,7 +184,7 @@ RealAntennasCommNetParams } TechLevelInfo { - name = TL5 + name = commsTL5 Level = 5 Description = Advanced Comms, 1967-1971: Noise reduction 1968, block coding 1969 PowerEfficiency = 0.2222 @@ -201,7 +201,7 @@ RealAntennasCommNetParams } TechLevelInfo { - name = TL6 + name = commsTL6 Level = 6 Description = Deep Space Comms, 1971-1974: Antenna improvements 1971-1972, Convolutional coding ~1973 PowerEfficiency = 0.25 @@ -218,7 +218,7 @@ RealAntennasCommNetParams } TechLevelInfo { - name = TL7 + name = commsTL7 Level = 7 Description = High Data Rate Comms, 1976-1980: X-Band ~1975, concatenated coding, MW noise reduction ~1980 PowerEfficiency = 0.3 @@ -235,7 +235,7 @@ RealAntennasCommNetParams } TechLevelInfo { - name = TL8 + name = commsTL8 Level = 8 Description = Massive Scale Comms, 1986-1997: 70m antennas 1988 PowerEfficiency = 0.3724 @@ -252,7 +252,7 @@ RealAntennasCommNetParams } TechLevelInfo { - name = TL9 + name = commsTL9 Level = 9 Description = Efficient Comms, 1998-2008: Super-cooled maser & feed 1995, Ka-band 2004 PowerEfficiency = 0.4397 diff --git a/GameData/RealAntennas/RealismOverhaul.cfg b/GameData/RealAntennas/RealismOverhaul.cfg index 80f48c4..62a37da 100644 --- a/GameData/RealAntennas/RealismOverhaul.cfg +++ b/GameData/RealAntennas/RealismOverhaul.cfg @@ -153,7 +153,7 @@ !TechLevelInfo,* {} TechLevelInfo { - name = TL0 + name = commsTL0 Level = 0 Description = WW2-era PowerEfficiency = 0.0555 @@ -170,7 +170,7 @@ } TechLevelInfo { - name = TL1 + name = commsTL1 Level = 1 Description = Lunar Range Comms, 1956: 26m antenna 1958 PowerEfficiency = 0.0769 @@ -187,7 +187,7 @@ } TechLevelInfo { - name = TL2 + name = commsTL2 Level = 2 Description = Digital Comms, 1959-1960 PowerEfficiency = 0.1 @@ -204,7 +204,7 @@ } TechLevelInfo { - name = TL3 + name = commsTL3 Level = 3 Description = Interplanetary Comms, 1961-1963: Maser 1962, S/C noise reduction 1961 (300-3000K=10dB noise temp) PowerEfficiency = 0.1304 @@ -221,7 +221,7 @@ } TechLevelInfo { - name = TL4 + name = commsTL4 Level = 4 Description = Improved Comms, 1964-1966: 64m Antenna 1967 PowerEfficiency = 0.1667 @@ -238,7 +238,7 @@ } TechLevelInfo { - name = TL5 + name = commsTL5 Level = 5 Description = Advanced Comms, 1967-1971: Noise reduction 1968, block coding 1969 PowerEfficiency = 0.2222 @@ -255,7 +255,7 @@ } TechLevelInfo { - name = TL6 + name = commsTL6 Level = 6 Description = Deep Space Comms, 1971-1974: Antenna improvements 1971-1972, Convolutional coding ~1973 PowerEfficiency = 0.25 @@ -272,7 +272,7 @@ } TechLevelInfo { - name = TL7 + name = commsTL7 Level = 7 Description = High Data Rate Comms, 1976-1980: X-Band ~1975, concatenated coding, MW noise reduction ~1980 PowerEfficiency = 0.3 @@ -289,7 +289,7 @@ } TechLevelInfo { - name = TL8 + name = commsTL8 Level = 8 Description = Massive Scale Comms, 1986-1997: 70m antennas 1988 PowerEfficiency = 0.3724 @@ -306,7 +306,7 @@ } TechLevelInfo { - name = TL9 + name = commsTL9 Level = 9 Description = Efficient Comms, 1998-2008: Super-cooled maser & feed 1995, Ka-band 2004 PowerEfficiency = 0.4397 diff --git a/src/RealAntennasProject/ModuleRealAntenna.cs b/src/RealAntennasProject/ModuleRealAntenna.cs index 7fef1d4..6bf760d 100644 --- a/src/RealAntennasProject/ModuleRealAntenna.cs +++ b/src/RealAntennasProject/ModuleRealAntenna.cs @@ -11,7 +11,7 @@ public class ModuleRealAntenna : ModuleDataTransmitter, IPartCostModifier, IPart private const string PAWGroup = "RealAntennas"; private const string PAWGroupPlanner = "Antenna Planning"; [KSPField(guiActiveEditor = true, guiName = "Antenna", isPersistant = true, groupName = PAWGroup, groupDisplayName = PAWGroup), - UI_Toggle(disabledText = "Disabled", enabledText = "Enabled", scene =UI_Scene.Editor)] + UI_Toggle(disabledText = "Disabled", enabledText = "Enabled", scene = UI_Scene.Editor)] public bool _enabled = true; [KSPField(guiActive = true, guiActiveEditor = true, guiName = "Gain", guiUnits = " dBi", guiFormat = "F1", groupName = PAWGroup, groupDisplayName = PAWGroup)] @@ -70,6 +70,7 @@ public class ModuleRealAntenna : ModuleDataTransmitter, IPartCostModifier, IPart public static float InactivePowerConsumptionMult = 0.1f; private float DefaultPacketInterval = 1.0f; private bool scienceMonitorActive = false; + private int actualMaxTechLevel = 0; public float PowerDraw => RATools.LogScale(PowerDrawLinear); public float PowerDrawLinear => RATools.LinearScale(TxPower) / RAAntenna.PowerEfficiency; @@ -117,6 +118,7 @@ public override void OnSave(ConfigNode node) public void OnDestroy() { GameEvents.OnGameSettingsApplied.Remove(ApplyGameSettings); + GameEvents.OnPartUpgradePurchased.Remove(OnPartUpgradePurchased); } public void Configure(ConfigNode node) @@ -134,14 +136,19 @@ public override void OnStart(StartState state) Fields[nameof(_enabled)].uiControlEditor.onFieldChanged = OnAntennaEnableChange; (Fields[nameof(TxPower)].uiControlEditor as UI_FloatRange).maxValue = MaxTxPower; - if (HighLogic.CurrentGame.Mode != Game.Modes.CAREER) maxTechLevel = HighLogic.CurrentGame.Parameters.CustomParams().MaxTechLevel; - if (Fields[nameof(TechLevel)].uiControlEditor is UI_FloatRange fr) + actualMaxTechLevel = maxTechLevel; // maxTechLevel value can come from applied PartUpgrades + int maxLvlFromParams = HighLogic.CurrentGame.Parameters.CustomParams().MaxTechLevel; + if (HighLogic.CurrentGame.Mode != Game.Modes.CAREER) { - fr.maxValue = maxTechLevel; - if (fr.maxValue == fr.minValue) - fr.maxValue += 0.001f; + maxTechLevel = actualMaxTechLevel = maxLvlFromParams; } - if (TechLevel < 0) TechLevel = maxTechLevel; + else if (RATools.RP1Found) + { + // With RP-1 present, always allow selecting all TLs but validate the user choice on vessel getting built + maxTechLevel = maxLvlFromParams; + } + UpdateMaxTechLevelInUI(); + if (TechLevel < 0) TechLevel = actualMaxTechLevel; RAAntenna.Name = part.partInfo.title; if (!RAAntenna.CanTarget) @@ -158,6 +165,7 @@ public override void OnStart(StartState state) SetupIdlePower(); RecalculateFields(); SetFieldVisibility(_enabled); + ApplyTLColoring(); if (HighLogic.LoadedSceneIsFlight) { @@ -165,6 +173,10 @@ public override void OnStart(StartState state) if (_enabled) GameEvents.OnGameSettingsApplied.Add(ApplyGameSettings); } + else if (HighLogic.LoadedSceneIsEditor) + { + GameEvents.OnPartUpgradePurchased.Add(OnPartUpgradePurchased); + } } private void SetupIdlePower() @@ -240,17 +252,29 @@ private void SetupUICallbacks() Fields[nameof(plannerActiveTxTime)].uiControlEditor.onFieldChanged += OnPlannerActiveTxTimeChanged; } + private void UpdateMaxTechLevelInUI() + { + if (Fields[nameof(TechLevel)].uiControlEditor is UI_FloatRange fr) + { + fr.maxValue = maxTechLevel; + if (fr.maxValue == fr.minValue) + fr.maxValue += 0.001f; + } + } + private void OnPlannerActiveTxTimeChanged(BaseField field, object obj) => RecalculatePlannerECConsumption(); private void OnAntennaEnableChange(BaseField field, object obj) { SetFieldVisibility(_enabled); 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 { + ApplyTLColoring(); string oldBand = RFBand; ConfigBandOptions(); RecalculateFields(); if (!oldBand.Equals(RFBand)) MonoUtilities.RefreshPartContextWindow(part); } + private void OnTechLevelChangeSymmetry(BaseField f, object obj) => ConfigBandOptions(); private void ApplyGameSettings() @@ -258,6 +282,32 @@ private void ApplyGameSettings() StockRateModifier = HighLogic.CurrentGame.Parameters.CustomParams().StockRateModifier; } + /// + /// Handles TL PartUpgrade getting purchased in Editor scene + /// + /// + private void OnPartUpgradePurchased(PartUpgradeHandler.Upgrade upgd) + { + var tlInf = TechLevelInfo.GetTechLevel(upgd.name); + if (tlInf != null && tlInf.Level > actualMaxTechLevel) + { + actualMaxTechLevel = tlInf.Level; + if (!RATools.RP1Found) maxTechLevel = actualMaxTechLevel; + UpdateMaxTechLevelInUI(); + ApplyTLColoring(); + } + } + + private void ApplyTLColoring() + { + if (HighLogic.LoadedSceneIsEditor) + { + BaseField f = Fields[nameof(TechLevel)]; + f.guiFormat = techLevel > actualMaxTechLevel ? "''#''" : "N0"; + f.guiName = techLevel > actualMaxTechLevel ? "Tech Level" : "Tech Level"; + } + } + private void ConfigBandOptions() { List availableBands = new List(); @@ -389,5 +439,67 @@ private void RecalculatePlannerECConsumption() double ec = _enabled ? RAAntenna.IdlePowerDraw + (RAAntenna.PowerDrawLinear * 1e-6 * plannerActiveTxTime) : 0; plannerECConsumption = new KeyValuePair("ElectricCharge", -ec); } + + #region RP-1 integration + /// + /// Called from RP-1 VesselBuildValidator + /// + /// + /// + /// + /// + public virtual bool Validate(out string validationError, out bool canBeResolved, out float costToResolve, out string techToResolve) + { + validationError = null; + canBeResolved = false; + costToResolve = 0; + techToResolve = string.Empty; + + if (!_enabled || techLevel <= actualMaxTechLevel) return true; + + PartUpgradeHandler.Upgrade upgd = GetUpgradeForTL(techLevel); + if (PartUpgradeManager.Handler.IsAvailableToUnlock(upgd.name)) + { + canBeResolved = true; + costToResolve = upgd.entryCost; + validationError = $"purchase {upgd.title}"; + } + else + { + techToResolve = upgd.techRequired; + validationError = $"unlock tech {ResearchAndDevelopment.GetTechnologyTitle(upgd.techRequired)}"; + } + + return false; + } + + /// + /// Called from RP-1 VesselBuildValidator + /// + /// + public virtual bool ResolveValidationError() + { + PartUpgradeHandler.Upgrade upgd = GetUpgradeForTL(techLevel); + if (upgd == null) + return false; + return PurchaseConfig(upgd); + } + + private static bool PurchaseConfig(PartUpgradeHandler.Upgrade upgd) + { + CurrencyModifierQuery cmq = CurrencyModifierQuery.RunQuery(TransactionReasons.RnDPartPurchase, -upgd.entryCost, 0, 0); + if (!cmq.CanAfford()) + return false; + PartUpgradeManager.Handler.SetUnlocked(upgd.name, true); + GameEvents.OnPartUpgradePurchased.Fire(upgd); + return true; + } + + private static PartUpgradeHandler.Upgrade GetUpgradeForTL(int techLevel) + { + TechLevelInfo tlInf = TechLevelInfo.GetTechLevel(techLevel); + return PartUpgradeManager.Handler.GetUpgrade(tlInf.name); + } + #endregion } } \ No newline at end of file diff --git a/src/RealAntennasProject/TechLevelInfo.cs b/src/RealAntennasProject/TechLevelInfo.cs index 8b3bf1f..6b106a7 100644 --- a/src/RealAntennasProject/TechLevelInfo.cs +++ b/src/RealAntennasProject/TechLevelInfo.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using UniLinq; using UnityEngine; namespace RealAntennas @@ -48,8 +49,7 @@ public static void Init(ConfigNode config) public static TechLevelInfo GetTechLevel(int i) { - if (!initialized) - Init(GameDatabase.Instance.GetConfigNode("RealAntennas/RealAntennasCommNetParams/RealAntennasCommNetParams")); + EnsureInitialized(); i = Mathf.Clamp(i, 0, MaxTL); if (All.TryGetValue(i, out TechLevelInfo info)) { @@ -57,5 +57,18 @@ public static TechLevelInfo GetTechLevel(int i) } return All[0]; } + + public static TechLevelInfo GetTechLevel(string name) + { + EnsureInitialized(); + // Note: consider a separate dictionary if the lookups start getting called from hot paths + return All.Values.FirstOrDefault(inf => inf.name == name); + } + + private static void EnsureInitialized() + { + if (!initialized) + Init(GameDatabase.Instance.GetConfigNode("RealAntennas/RealAntennasCommNetParams/RealAntennasCommNetParams")); + } } } diff --git a/src/RealAntennasProject/Tools.cs b/src/RealAntennasProject/Tools.cs index 83a8ac2..2ab5172 100644 --- a/src/RealAntennasProject/Tools.cs +++ b/src/RealAntennasProject/Tools.cs @@ -8,6 +8,20 @@ namespace RealAntennas { public class RATools : object { + private static bool? _RP1Found = null; + public static bool RP1Found + { + get + { + if (!_RP1Found.HasValue) + { + var assembly = AssemblyLoader.loadedAssemblies.FirstOrDefault(a => a.assembly.GetName().Name == "RP0")?.assembly; + _RP1Found = assembly != null; + } + return _RP1Found.Value; + } + } + public static double LinearScale(double x) => math.pow(10, x / 10); public static float LinearScale(float x) => math.pow(10, x / 10); public static double LogScale(double x) => 10 * math.log10(x);