diff --git a/Flags/Kethane 1.png b/Flags/Kethane 1.png new file mode 100644 index 0000000..4dc151e Binary files /dev/null and b/Flags/Kethane 1.png differ diff --git a/Parts/kethane_1m_converter/part.cfg b/Parts/kethane_1m_converter/part.cfg index ee49df2..c7fe66d 100644 --- a/Parts/kethane_1m_converter/part.cfg +++ b/Parts/kethane_1m_converter/part.cfg @@ -47,40 +47,44 @@ fuelCrossFeed = True MODULE { name = KethaneConverter - SourceResource = Kethane - TargetResource = LiquidFuel - ConversionEfficiency = 0.97 - SourceConsumption = 2.25 - PowerConsumption = 6 + Label = Rocket Fuel + InputRates + { + Kethane = 2.25 + ElectricCharge = 6 + } + OutputRatios + { + LiquidFuel = 0.99 + Oxidizer = 0.99 + } } MODULE { name = KethaneConverter - SourceResource = Kethane - TargetResource = Oxidizer - ConversionEfficiency = 1.01 - SourceConsumption = 2.75 - PowerConsumption = 4 + InputRates + { + Kethane = 1.5 + ElectricCharge = 8 + } + OutputRatios + { + MonoPropellant = 0.3 + } } MODULE { name = KethaneConverter - SourceResource = Kethane - TargetResource = MonoPropellant - ConversionEfficiency = 0.3 - SourceConsumption = 1.5 - PowerConsumption = 8 -} - -MODULE -{ - name = KethaneConverter - SourceResource = Kethane - TargetResource = XenonGas - ConversionEfficiency = 0.4 - SourceConsumption = 1.5 - PowerConsumption = 3 + InputRates + { + Kethane = 1.5 + ElectricCharge = 3 + } + OutputRatios + { + XenonGas = 0.4 + } } } diff --git a/Parts/kethane_2m_converter/part.cfg b/Parts/kethane_2m_converter/part.cfg index 8cf4290..f37a447 100644 --- a/Parts/kethane_2m_converter/part.cfg +++ b/Parts/kethane_2m_converter/part.cfg @@ -51,44 +51,60 @@ MODULE MODULE { name = KethaneConverter - SourceResource = Kethane - TargetResource = LiquidFuel - ConversionEfficiency = 1.03 - SourceConsumption = 6.75 - PowerConsumption = 12 HeatProduction = 600 + InputRates + { + Kethane = 6.75 + ElectricCharge = 12 + } + OutputRatios + { + LiquidFuel = 1.03 + } } MODULE { name = KethaneConverter - SourceResource = Kethane - TargetResource = Oxidizer - ConversionEfficiency = 0.99 - SourceConsumption = 8.25 - PowerConsumption = 8 HeatProduction = 800 + InputRates + { + Kethane = 8.25 + ElectricCharge = 8 + } + OutputRatios + { + Oxidizer = 0.99 + } } MODULE { name = KethaneConverter - SourceResource = Kethane - TargetResource = MonoPropellant - ConversionEfficiency = 0.85 - SourceConsumption = 3 - PowerConsumption = 10 HeatProduction = 1200 + InputRates + { + Kethane = 3 + ElectricCharge = 10 + } + OutputRatios + { + MonoPropellant = 0.85 + } } MODULE { name = KethaneConverter - SourceResource = Kethane - TargetResource = XenonGas - ConversionEfficiency = 0.25 - SourceConsumption = 2 - PowerConsumption = 8 HeatProduction = 300 + InputRates + { + Kethane = 2 + ElectricCharge = 8 + } + OutputRatios + { + XenonGas = 0.25 + } } } diff --git a/Parts/kethane_highGain/part.cfg b/Parts/kethane_highGain/part.cfg index 44ebdd4..15ed157 100644 --- a/Parts/kethane_highGain/part.cfg +++ b/Parts/kethane_highGain/part.cfg @@ -42,6 +42,10 @@ MODULE DetectingPeriod = 1.5 DetectingHeight = 250000 PowerConsumption = 0.8 + Resource + { + Name = Kethane + } } MODULE diff --git a/Parts/kethane_sensor_1m/part.cfg b/Parts/kethane_sensor_1m/part.cfg index eb3e452..1cc8fb3 100644 --- a/Parts/kethane_sensor_1m/part.cfg +++ b/Parts/kethane_sensor_1m/part.cfg @@ -45,6 +45,10 @@ MODULE { DetectingPeriod = 0.9 DetectingHeight = 1200000 PowerConsumption = 2.5 + Resource + { + Name = Kethane + } } MODULE diff --git a/Plugin/InstallChecker.cs b/Plugin/InstallChecker.cs index bde00d4..2686dd2 100644 --- a/Plugin/InstallChecker.cs +++ b/Plugin/InstallChecker.cs @@ -6,7 +6,7 @@ namespace Kethane { - [KSPAddon(KSPAddon.Startup.MainMenu, true)] + [KSPAddonFixed(KSPAddon.Startup.MainMenu, true, typeof(InstallChecker))] internal class InstallChecker : MonoBehaviour { protected void Start() diff --git a/Plugin/KSPAddonFixed.cs b/Plugin/KSPAddonFixed.cs new file mode 100644 index 0000000..591b4d0 --- /dev/null +++ b/Plugin/KSPAddonFixed.cs @@ -0,0 +1,37 @@ +using System; + +namespace Kethane +{ + /// + /// KSPAddon with equality checking using an additional type parameter. Fixes the issue where AddonLoader prevents multiple start-once addons with the same start scene. + /// + public class KSPAddonFixed : KSPAddon, IEquatable + { + private readonly Type type; + + public KSPAddonFixed(KSPAddon.Startup startup, bool once, Type type) + : base(startup, once) + { + this.type = type; + } + + public override bool Equals(object obj) + { + if (obj.GetType() != this.GetType()) { return false; } + return Equals((KSPAddonFixed)obj); + } + + public bool Equals(KSPAddonFixed other) + { + if (this.once != other.once) { return false; } + if (this.startup != other.startup) { return false; } + if (this.type != other.type) { return false; } + return true; + } + + public override int GetHashCode() + { + return this.startup.GetHashCode() ^ this.once.GetHashCode() ^ this.type.GetHashCode(); + } + } +} diff --git a/Plugin/Kethane.csproj b/Plugin/Kethane.csproj index bb3bdc5..4850b43 100644 --- a/Plugin/Kethane.csproj +++ b/Plugin/Kethane.csproj @@ -68,6 +68,7 @@ + diff --git a/Plugin/KethaneConverter.cs b/Plugin/KethaneConverter.cs index 280b473..af1595f 100644 --- a/Plugin/KethaneConverter.cs +++ b/Plugin/KethaneConverter.cs @@ -8,20 +8,26 @@ namespace Kethane { public class KethaneConverter : PartModule { - [KSPField(isPersistant = false)] - public string SourceResource; - - [KSPField(isPersistant = false)] - public string TargetResource; + private struct ResourceRate + { + public String Resource { get; private set; } + public double Rate { get; private set; } - [KSPField(isPersistant = false)] - public float ConversionEfficiency; + public ResourceRate(String resource, double rate) + : this() + { + Resource = resource; + Rate = rate; + } - [KSPField(isPersistant = false)] - public float SourceConsumption; + public static ResourceRate operator *(ResourceRate rate, double multiplier) + { + return new ResourceRate(rate.Resource, rate.Rate * multiplier); + } + } [KSPField(isPersistant = false)] - public float PowerConsumption; + public String Label; [KSPField(isPersistant = false)] public float HeatProduction; @@ -29,6 +35,11 @@ public class KethaneConverter : PartModule [KSPField(isPersistant = true)] public bool IsEnabled; + public ConfigNode config; + + private ResourceRate[] inputRates; + private ResourceRate[] outputRates; + [KSPEvent(guiActive = true, guiName = "Activate Converter", active = true)] public void ActivateConverter() { @@ -61,14 +72,48 @@ public void ToggleConverterAction(KSPActionParam param) public override string GetInfo() { - return String.Format("{0}:\n- Conversion Efficiency: {1:P0}\n- {4} Consumption: {2:F1}L/s\n- Power Consumption: {3:F1}/s", TargetResource, ConversionEfficiency, SourceConsumption, PowerConsumption, SourceResource); + return String.Format("{0} Converter:\n> Inputs:\n", Label) + String.Join("\n", inputRates.Select(r => String.Format(" - {0}: {1:N2}/s", r.Resource, r.Rate)).ToArray()) + "\n> Outputs:\n" + String.Join("\n", outputRates.Select(r => String.Format(" - {0}: {1:N2}/s", r.Resource, r.Rate)).ToArray()) + "\n"; + } + + public override void OnLoad(ConfigNode config) + { + if (this.config == null) + { + this.config = new ConfigNode(); + config.CopyTo(this.config); + } + + loadConfig(); + } + + private void loadConfig() + { + var definitions = PartResourceLibrary.Instance.resourceDefinitions; + + inputRates = loadRates(config.GetNode("InputRates")).ToArray(); + var inputMassRate = inputRates.Sum(p => p.Rate * definitions[p.Resource].density); + + outputRates = loadRates(config.GetNode("OutputRatios")).Select(r => r * (inputMassRate / definitions[r.Resource].density)).GroupBy(r => r.Resource).Select(g => new ResourceRate(g.Key, g.Sum(r => r.Rate))).Concat(loadRates(config.GetNode("OutputRates"))).ToArray(); + + if (Label == null) + { + Label = String.Join("/", outputRates.Select(r => r.Resource).ToArray()); + } + } + + private static IEnumerable loadRates(ConfigNode config) + { + return (config ?? new ConfigNode()).values.Cast().Where(v => PartResourceLibrary.Instance.resourceDefinitions.Any(d => d.name == v.name)).Select(v => new ResourceRate(v.name, Misc.Parse(v.value, 0.0))).Where(r => r.Rate > 0); } public override void OnStart(PartModule.StartState state) { - Actions["ActivateConverterAction"].guiName = Events["ActivateConverter"].guiName = String.Format("Activate {0} Converter", TargetResource); - Actions["DeactivateConverterAction"].guiName = Events["DeactivateConverter"].guiName = String.Format("Deactivate {0} Converter", TargetResource); - Actions["ToggleConverterAction"].guiName = String.Format("Toggle {0} Converter", TargetResource); + loadConfig(); + + Actions["ActivateConverterAction"].guiName = Events["ActivateConverter"].guiName = String.Format("Activate {0} Converter", Label); + Actions["DeactivateConverterAction"].guiName = Events["DeactivateConverter"].guiName = String.Format("Deactivate {0} Converter", Label); + Actions["ToggleConverterAction"].guiName = String.Format("Toggle {0} Converter", Label); + if (state == StartState.Editor) { return; } this.part.force_activate(); } @@ -83,24 +128,8 @@ public override void OnFixedUpdate() { if (!IsEnabled) { return; } - var conversionRatio = PartResourceLibrary.Instance.GetDefinition(SourceResource).density / PartResourceLibrary.Instance.GetDefinition(TargetResource).density; - - double requestedSpace = SourceConsumption * conversionRatio * ConversionEfficiency * TimeWarp.fixedDeltaTime; - double requestedSource = SourceConsumption * TimeWarp.fixedDeltaTime; - double requestedEnergy = PowerConsumption * TimeWarp.fixedDeltaTime; - - double availableSpace = 0; - var listPartResource = Misc.GetConnectedResources(this.part, TargetResource); - if (listPartResource.Count > 0) - availableSpace = listPartResource.Max(r => r.maxAmount - r.amount); - var availableSource = Misc.GetConnectedResources(this.part, SourceResource).Max(r => r.amount); - var availableEnergy = Misc.GetConnectedResources(this.part, "ElectricCharge").Max(r => r.amount); - - var spaceRatio = availableSpace / requestedSpace; - var sourceRatio = availableSource / requestedSource; - var energyRatio = availableEnergy / requestedEnergy; - - var ratio = Math.Min(Math.Min(Math.Min(spaceRatio, sourceRatio), energyRatio), 1); + var rates = outputRates.Select(r => r * -1).Concat(inputRates).Select(r => r * TimeWarp.fixedDeltaTime).ToArray(); + var ratio = rates.Select(r => Misc.GetConnectedResources(this.part, r.Resource).Select(c => r.Rate > 0 ? c.amount : c.maxAmount - c.amount).DefaultIfEmpty().Max() / Math.Abs(r.Rate)).Prepend(1).Min(); var heatsink = this.part.Modules.OfType().SingleOrDefault(); if (heatsink != null) @@ -109,19 +138,10 @@ public override void OnFixedUpdate() ratio *= heatsink.AddHeat(heatRequest) / heatRequest; } - requestedSource *= ratio; - - var drawnSource = this.part.RequestResource(SourceResource, requestedSource); - - ratio *= drawnSource / requestedSource; - requestedEnergy *= ratio; - - var drawnEnergy = this.part.RequestResource("ElectricCharge", requestedEnergy); - - ratio *= drawnEnergy / requestedEnergy; - requestedSpace *= ratio; - - this.part.RequestResource(TargetResource, -requestedSpace); + foreach (var rate in rates) + { + this.part.RequestResource(rate.Resource, rate.Rate * ratio); + } } } } diff --git a/Plugin/KethaneData.cs b/Plugin/KethaneData.cs index d64da21..92fadaf 100644 --- a/Plugin/KethaneData.cs +++ b/Plugin/KethaneData.cs @@ -17,6 +17,10 @@ public static KethaneData Current if (!game.scenarios.Any(p => p.moduleName == typeof(KethaneData).Name)) { var proto = game.AddProtoScenarioModule(typeof(KethaneData), GameScenes.FLIGHT, GameScenes.TRACKSTATION); + if (proto.targetScenes.Contains(HighLogic.LoadedScene)) + { + proto.Load(ScenarioRunner.fetch); + } } return game.scenarios.Select(s => s.moduleRef).OfType().SingleOrDefault(); diff --git a/Plugin/KethaneExtractorAnimatorLanded.cs b/Plugin/KethaneExtractorAnimatorLanded.cs index b2df454..6f4c500 100644 --- a/Plugin/KethaneExtractorAnimatorLanded.cs +++ b/Plugin/KethaneExtractorAnimatorLanded.cs @@ -6,7 +6,6 @@ public class KethaneExtractorAnimatorLanded : PartModule, IExtractorAnimator public ExtractorState CurrentState { get; private set; } public void Deploy() { CurrentState = ExtractorState.Deployed; } public void Retract() { CurrentState = ExtractorState.Retracted; } - public bool CanExtract { get { return vessel.LandedOrSplashed; } } public KethaneExtractorAnimatorLanded() { diff --git a/Plugin/KethaneWetMassIndicator.cs b/Plugin/KethaneWetMassIndicator.cs index 9ef0cf2..705a2a7 100644 --- a/Plugin/KethaneWetMassIndicator.cs +++ b/Plugin/KethaneWetMassIndicator.cs @@ -5,9 +5,12 @@ namespace Kethane { public class KethaneWetMassIndicator : PartModule { + [KSPField(isPersistant = false)] + public String Label; + public override string GetInfo() { - return String.Format("Wet Mass: {0}", (float)this.part.Resources.Cast().Sum(r => r.maxAmount * PartResourceLibrary.Instance.GetDefinition(r.resourceName).density) + this.part.mass); + return String.Format("{0}: {1}", Label ?? "Wet Mass", (float)this.part.Resources.Cast().Sum(r => r.maxAmount * PartResourceLibrary.Instance.GetDefinition(r.resourceName).density) + this.part.mass); } } } diff --git a/Plugin/MapOverlay.cs b/Plugin/MapOverlay.cs index 4bf7883..daf6a87 100644 --- a/Plugin/MapOverlay.cs +++ b/Plugin/MapOverlay.cs @@ -213,24 +213,25 @@ public void RefreshCellColor(GeodesicGrid.Cell cell, CelestialBody body) { if (body != this.body) { return; } var colors = mesh.colors32; - refreshCellColor(cell, body, colors); + refreshCellColor(cell, body, colors, KethaneData.Current); mesh.colors32 = colors; } private void refreshCellColors() { var colors = new Color32[mesh.vertexCount]; + var data = KethaneData.Current; foreach (var cell in grid) { - refreshCellColor(cell, body, colors); + refreshCellColor(cell, body, colors, data); } mesh.colors32 = colors; } - private void refreshCellColor(GeodesicGrid.Cell cell, CelestialBody body, Color32[] colors) + private void refreshCellColor(GeodesicGrid.Cell cell, CelestialBody body, Color32[] colors, KethaneData data) { - var deposit = KethaneData.Current.GetCellDeposit(resource.Resource, body, cell); - var scanned = KethaneData.Current.Scans[resource.Resource][body.name][cell]; + var deposit = data.GetCellDeposit(resource.Resource, body, cell); + var scanned = data.Scans[resource.Resource][body.name][cell]; var color = (revealAll ? deposit != null : scanned) ? getDepositColor(resource, deposit) : colorUnknown; setCellColor(cell, color, colors); } diff --git a/Plugin/Misc.cs b/Plugin/Misc.cs index 4d95c37..ca9eca1 100644 --- a/Plugin/Misc.cs +++ b/Plugin/Misc.cs @@ -90,6 +90,16 @@ public static float Parse(string s, float defaultValue) return value; } + public static double Parse(string s, double defaultValue) + { + double value; + if (!double.TryParse(s, out value)) + { + value = defaultValue; + } + return value; + } + public static int Parse(string s, int defaultValue) { int value; diff --git a/Plugin/Properties/AssemblyInfo.cs b/Plugin/Properties/AssemblyInfo.cs index 0811b1e..c748168 100644 --- a/Plugin/Properties/AssemblyInfo.cs +++ b/Plugin/Properties/AssemblyInfo.cs @@ -33,4 +33,4 @@ // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyInformationalVersion("0.7.5")] \ No newline at end of file +[assembly: AssemblyInformationalVersion("0.7.6")] \ No newline at end of file diff --git a/Plugin/ScaledSpaceFix.cs b/Plugin/ScaledSpaceFix.cs index 1a71470..410cff2 100644 --- a/Plugin/ScaledSpaceFix.cs +++ b/Plugin/ScaledSpaceFix.cs @@ -8,6 +8,7 @@ internal class ScaledSpaceFix : MonoBehaviour { public void Start() { + if (ScaledSpace.Instance == null || ScaledSpace.Instance.scaledSpaceTransforms == null) { return; } ScaledSpace.Instance.scaledSpaceTransforms.RemoveAll(t => t == null); if (HighLogic.LoadedScene == GameScenes.MAINMENU) { diff --git a/Plugin/SettingsManager.cs b/Plugin/SettingsManager.cs index a71179d..d8b3f8f 100644 --- a/Plugin/SettingsManager.cs +++ b/Plugin/SettingsManager.cs @@ -2,7 +2,7 @@ namespace Kethane { - [KSPAddon(KSPAddon.Startup.Instantly, true)] + [KSPAddonFixed(KSPAddon.Startup.Instantly, true, typeof(SettingsManager))] internal class SettingsManager : MonoBehaviour { private static ConfigNode node;