Skip to content

Commit

Permalink
Showing 4 changed files with 222 additions and 175 deletions.
Binary file modified GameData/RP-0/Plugins/RP0.dll
Binary file not shown.
24 changes: 13 additions & 11 deletions Source/Avionics/ModuleAvionics.cs
Original file line number Diff line number Diff line change
@@ -125,11 +125,21 @@ protected void UpdateRate()
currentWatts *= 1000f;
}

protected void OnConfigurationUpdated()
{
SetActionsAndGui();
}

private void SetActionsAndGui()
{
Events["ToggleEvent"].guiName = (systemEnabled ? "Shutdown" : "Activate") + " Avionics";
Actions["ActivateAction"].active = !systemEnabled;
Actions["ShutdownAction"].active = systemEnabled;
var toggleAble = GetToggleable();
Events[nameof(ToggleEvent)].guiActive = toggleAble;
Events[nameof(ToggleEvent)].guiActiveEditor = toggleAble;
Events[nameof(ToggleEvent)].guiName = (systemEnabled ? "Shutdown" : "Activate") + " Avionics";
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;
}

protected void StageActivated(int stage)
@@ -200,14 +210,6 @@ public void Start()
}
//We want to call UpdateRate all the time to capture anything from proceduralAvionics
UpdateRate();

Fields["currentWatts"].guiActive =
Events["ToggleEvent"].guiActive =
Events["ToggleEvent"].guiActiveEditor =
Actions["ToggleAction"].active =
Actions["ActivateAction"].active =
Actions["ShutdownAction"].active = GetToggleable();

SetActionsAndGui();
}

367 changes: 206 additions & 161 deletions Source/Avionics/ModuleProceduralAvionics.cs
Original file line number Diff line number Diff line change
@@ -16,11 +16,11 @@ class ModuleProceduralAvionics : ModuleAvionics, IPartMassModifier, IPartCostMod

const string kwFormat = "{0:0.##}";
const string wFormat = "{0:0}";
const float FLOAT_ERROR_ALLOWANCE = 1.002F;
const float FLOAT_TOLERANCE = 1.00001f;

// This controls how much the current part can control (in metric tons)
[KSPField(isPersistant = true, guiName = "Tonnage", guiActive = false, guiActiveEditor = true, guiUnits = "T"),
UI_FloatEdit(scene = UI_Scene.Editor, minValue = 0f, incrementLarge = 10f, incrementSmall = 1f, incrementSlide = 0.05f, sigFigs = 3, unit = "T")]
// This controls how much the current part can control (in metric tons)
[KSPField(isPersistant = true, guiName = "Tonnage", guiActive = false, guiActiveEditor = true, guiUnits = "\u2009t"),
UI_FloatEdit(scene = UI_Scene.Editor, minValue = 0f, incrementLarge = 10f, incrementSmall = 1f, incrementSlide = 0.05f, sigFigs = 3, unit = "\u2009t")]
public float proceduralMassLimit = 0;

// We can have multiple configurations of avionics, for example:
@@ -94,29 +94,43 @@ private float GetCurrentDensity()

protected override float GetInternalMassLimit()
{
var max = GetMaximumControllableTonnage();
if (max == 0) {
//Sounds like we're not yet initiaziled, let's not change anything
//Log("no max");
return proceduralMassLimit;
}
var min = GetMinimumControllableTonnage();
bool changed = false;
if (proceduralMassLimit > (max * FLOAT_ERROR_ALLOWANCE)) {
Log("Resetting procedural mass limit to max of ", max, ", was ", proceduralMassLimit);
proceduralMassLimit = max;
changed = true;
}
if ((proceduralMassLimit * FLOAT_ERROR_ALLOWANCE) < min) {
Log("Resetting procedural mass limit to min of ", min, ", was ", proceduralMassLimit);
proceduralMassLimit = min;
changed = true;
}
if (changed) {
RefreshPartWindow();
}
return proceduralMassLimit;
}
var oldLimit = proceduralMassLimit;
ClampInternalMassLimit();
if(proceduralMassLimit != oldLimit)
{
Log("WARNING: LIMIT WAS RESET IN GET");
}
return proceduralMassLimit;
}

private void ClampInternalMassLimit()
{
var max = GetMaximumControllableTonnage();
if (max == 0)
{
//Sounds like we're not yet initiaziled, let's not change anything
//Log("no max");
return;
}
var min = GetMinimumControllableTonnage();
bool changed = false;
if (proceduralMassLimit > max * FLOAT_TOLERANCE)
{
Log("Resetting procedural mass limit to max of ", max, ", was ", proceduralMassLimit);
proceduralMassLimit = max;
changed = true;
}
if (proceduralMassLimit * FLOAT_TOLERANCE < min)
{
Log("Resetting procedural mass limit to min of ", min, ", was ", proceduralMassLimit);
proceduralMassLimit = min;
changed = true;
}
if (changed)
{
RefreshPartWindow();
}
}

protected override float GetEnabledkW()
{
@@ -182,10 +196,7 @@ public override void OnLoad(ConfigNode node)
UpdateConfigSliders();
BindUIChangeCallbacks();

UpdateMaxValues();
UpdateCurrentConfig();

SetInternalKSPFields();
AvionicsConfigChanged();

if (cachedEventData != null) {
OnPartVolumeChanged(cachedEventData);
@@ -208,39 +219,48 @@ private void BindUIChangeCallbacks()
private void MassLimitChanged(BaseField arg1, object arg2)
{
Log("Mass limit changed");
SetMinVolume();
SetInternalKSPFields();
ClampInternalMassLimit();
SetMinVolume();
RefreshDisplays();
}

private void AvionicsConfigChanged(BaseField arg1, object arg2)
{
avionicsTechLevel = ProceduralAvionicsTechManager.GetMaxUnlockedTech(avionicsConfigName);

AvionicsConfigChanged();
ResetTo100();
}

private void AvionicsConfigChanged()
{
SetMinVolume();
GetInternalMassLimit(); //reset within bounds
UpdateMaxValues();
UpdateCurrentConfig();

SetInternalKSPFields();
}


private float cachedMinVolue = float.MaxValue;
if (avionicsConfigName == oldAvionicsConfigName)
{
return;
}
Log("Setting config to ", avionicsConfigName);
currentProceduralAvionicsConfig = ProceduralAvionicsTechManager.GetProceduralAvionicsConfig(avionicsConfigName);
Log("Setting tech node to ", avionicsTechLevel);
oldAvionicsConfigName = avionicsConfigName;
SetInternalKSPFields();
ResetTo100();
ClampInternalMassLimit();
SetMinVolume(true);
UpdateMaxValues();
OnConfigurationUpdated();
RefreshDisplays();
}


private float cachedMinVolume = float.MaxValue;
public void SetMinVolume(bool forceUpdate = false)
{
Log("Setting min volume for proceduralMassLimit of ", proceduralMassLimit);
float minVolume = proceduralMassLimit / (2 * tonnageToMassRatio * maxDensityOfAvionics);
float minVolume = proceduralMassLimit / (2 * tonnageToMassRatio * maxDensityOfAvionics) * FLOAT_TOLERANCE;
if (float.IsNaN(minVolume)) {
return;
}
Log("min volume sholud be ", minVolume);
cachedMinVolue = minVolume;
Log("min volume should be ", minVolume);
cachedMinVolume = minVolume;

PartModule ppModule = null;
Type ppModuleType = null;
@@ -253,9 +273,9 @@ public void SetMinVolume(bool forceUpdate = false)
Log("Applied min volume");
}
}
//Log("minVolume: ", minVolume);
Log("minVolume: ", minVolume);
Log("Comparing against cached volume of ", cachedVolume);
if (forceUpdate || minVolume > (cachedVolume * FLOAT_ERROR_ALLOWANCE)) { // adding a buffer for floating point errors
if (forceUpdate || minVolume > cachedVolume) { // adding a buffer for floating point errors
if (!forceUpdate) {
Log("cachedVolume too low: ", cachedVolume);
}
@@ -299,33 +319,16 @@ public ModifierChangeWhen GetModuleCostChangeWhen()

#endregion

#region config loading and serialization

private void UpdateCurrentConfig()
{
if (avionicsConfigName == oldAvionicsConfigName) {
return;
}
Log("Setting config to ", avionicsConfigName);
currentProceduralAvionicsConfig =
ProceduralAvionicsTechManager.GetProceduralAvionicsConfig(avionicsConfigName);
Log("Setting tech node to ", avionicsTechLevel);
oldAvionicsConfigName = avionicsConfigName;
SetMinVolume(true);
}
#endregion

#region part attribute calculations
private float CalculateNewMass()
{
if (HighLogic.LoadedSceneIsFlight) {
if (HighLogic.LoadedSceneIsFlight || maxDensityOfAvionics > 0) {
return DoMassCalculation();
}
if (CurrentProceduralAvionicsConfig != null && CurrentProceduralAvionicsTechNode != null) {
//Standard density is 4/3s of maximum density
//Log("Current Tech node standard density: ", CurrentProceduralAvionicsTechNode.standardAvionicsDensity);
maxDensityOfAvionics = (CurrentProceduralAvionicsTechNode.standardAvionicsDensity * 4) / 3;
tonnageToMassRatio = CurrentProceduralAvionicsTechNode.tonnageToMassRatio;
Log("Not yet initialized but getmass called!?");
SetInternalKSPFields();
return DoMassCalculation();
}
else {
@@ -377,12 +380,12 @@ private float GetControllableUtilizationPercentage()
private float GetMaximumControllableTonnage()
{
var maxAvionicsMass = cachedVolume * maxDensityOfAvionics;
var maxForVolume = UtilMath.RoundToPlaces(maxAvionicsMass * tonnageToMassRatio * 2, 2);
var maxControllableTonnage = Math.Min(maxForVolume, maximumTonnage);
return maxControllableTonnage;
//Log("max for volume before trunc: ", maxAvionicsMass * tonnageToMassRatio * 2);
var maxForVolume = FloorToSliderIncrement(maxAvionicsMass * tonnageToMassRatio * 2);
return Math.Min(maxForVolume, maximumTonnage);
}

private float GetMinimumControllableTonnage()
private float GetMinimumControllableTonnage()
{
if (CurrentProceduralAvionicsTechNode != null) {
return CurrentProceduralAvionicsTechNode.minimumTonnage;
@@ -392,9 +395,12 @@ private float GetMinimumControllableTonnage()

private void ResetTo100()
{
float value = cachedVolume * maxDensityOfAvionics * tonnageToMassRatio;
Log("100% utilization calculated as ", value);
proceduralMassLimit = value;
if(cachedVolume == float.MaxValue)
{
return;
}
proceduralMassLimit = cachedVolume * maxDensityOfAvionics * tonnageToMassRatio;
Log("100% utilization calculated as ", proceduralMassLimit);
}

private void UpdateMaxValues()
@@ -405,39 +411,65 @@ private void UpdateMaxValues()
proceduralMassLimitEdit = (UI_FloatEdit)Fields["proceduralMassLimit"].uiControlEditor;
}

if (CurrentProceduralAvionicsConfig != null && CurrentProceduralAvionicsTechNode != null) {

tonnageToMassRatio = CurrentProceduralAvionicsTechNode.tonnageToMassRatio;
proceduralMassLimitEdit.maxValue = GetMaximumControllableTonnage();
proceduralMassLimitEdit.minValue = GetMinimumControllableTonnage();

// Set slide, small, and large slider increments to be (at most) 0.025%, 1%, and 10%
var procMassDelta = proceduralMassLimitEdit.maxValue - proceduralMassLimitEdit.minValue;

//we'll start at a large incerement of 1, and work up from there
int largeIncrement = 1;
while (largeIncrement < procMassDelta) {
largeIncrement *= 2;
}

float largeIncFloat = (float)largeIncrement;

// There's some weirdness going on here that makes the slider not match up with min and max values.
// Because of that, need to ensure that this difference does not get larger than FLOAT_ERROR_ALLOWANCE
// which is used for ensuring the min and max values in GetInternalMassLimit().

proceduralMassLimitEdit.incrementSlide = proceduralMassLimitEdit.minValue * (FLOAT_ERROR_ALLOWANCE - 1f);
proceduralMassLimitEdit.incrementSmall = largeIncFloat / 100;
proceduralMassLimitEdit.incrementLarge = largeIncFloat / 10;
}
else {
Log("Cannot update max value yet, CurrentProceduralAvionicsConfig is null");
proceduralMassLimitEdit.maxValue = float.MaxValue;
proceduralMassLimitEdit.minValue = 0;
}
}

private void UpdateConfigSliders()
if (CurrentProceduralAvionicsConfig != null && CurrentProceduralAvionicsTechNode != null)
{

tonnageToMassRatio = CurrentProceduralAvionicsTechNode.tonnageToMassRatio;
proceduralMassLimitEdit.maxValue = CeilingToSmallIncrement(GetMaximumControllableTonnage());
proceduralMassLimitEdit.minValue = 0;

var procMassDelta = proceduralMassLimitEdit.maxValue - proceduralMassLimitEdit.minValue;

proceduralMassLimitEdit.incrementSmall = GetSmallIncrement(procMassDelta);
proceduralMassLimitEdit.incrementLarge = proceduralMassLimitEdit.incrementSmall * 10;
proceduralMassLimitEdit.incrementSlide = GetSliderIncrement(procMassDelta);
proceduralMassLimitEdit.sigFigs = GetSigFigs(procMassDelta);
}
else
{
Log("Cannot update max value yet, CurrentProceduralAvionicsConfig is null");
proceduralMassLimitEdit.maxValue = float.MaxValue;
proceduralMassLimitEdit.minValue = 0;
}
}

private int GetSigFigs(float value)
{
var smallIncrementExponent = GetSmallIncrementExponent(value);
return Math.Max(1 - (int)smallIncrementExponent, 0);
}

private float CeilingToSmallIncrement(float value)
{
var smallIncrement = GetSmallIncrement(value);
return (float) Math.Ceiling(value / smallIncrement) * smallIncrement;
}

private float FloorToSliderIncrement(float value)
{
float sliderIncrement = GetSliderIncrement(value);
return (float) Math.Floor(value / sliderIncrement) * sliderIncrement;
}

private float GetSliderIncrement(float value)
{
var smallIncrement = GetSmallIncrement(value);
return Math.Min(smallIncrement / 10, 1f);
}

private float GetSmallIncrement(float value)
{
var exponent = GetSmallIncrementExponent(value);
return (float) Math.Pow(10, exponent);
}

private double GetSmallIncrementExponent(float procMassDelta)
{
var log = Math.Log(procMassDelta, 10);
return Math.Max(Math.Floor(log - 1.3), -2);
}

private void UpdateConfigSliders()
{
Log("Updating Config Slider");
BaseField avionicsConfigField = Fields["avionicsConfigName"];
@@ -466,66 +498,80 @@ public void OnPartVolumeChanged(BaseEventDetails eventData)
try {
float volume = (float)eventData.Get<double>("newTotalVolume");
Log("volume changed to ", volume);
if (volume * FLOAT_ERROR_ALLOWANCE < cachedMinVolue && cachedMinVolue != float.MaxValue) {
Log("volume of ", volume, " is less than expected min volume of ", cachedMinVolue, " expecting another update");
if (volume * FLOAT_TOLERANCE < cachedMinVolume && cachedMinVolume != float.MaxValue) {
Log("volume of ", volume, " is less than expected min volume of ", cachedMinVolume, " expecting another update");
RefreshPartWindow();
//assuming the part will be resized
return;
}
Log("setting cachedVolume to ", volume);
cachedVolume = volume;
//Log("cached total volume set from eventData: ", cachedVolume);
AvionicsConfigChanged();
}
//Log("cached total volume set from eventData: ", cachedVolume);
UpdateMaxValues();
RefreshDisplays();
}
catch (Exception ex) {
Log("error getting changed volume: ", ex);
}
}

private void SetInternalKSPFields()
{
Log("Setting internal KSP fields");
Log("avionics tech level: ", avionicsTechLevel);

tonnageToMassRatio = CurrentProceduralAvionicsTechNode.tonnageToMassRatio;
costPerControlledTon = CurrentProceduralAvionicsTechNode.costPerControlledTon;
enabledProceduralW = CurrentProceduralAvionicsTechNode.enabledProceduralW;
disabledProceduralW = CurrentProceduralAvionicsTechNode.disabledProceduralW;

minimumTonnage = CurrentProceduralAvionicsTechNode.minimumTonnage;
maximumTonnage = CurrentProceduralAvionicsTechNode.maximumTonnage;

SASServiceLevel = CurrentProceduralAvionicsTechNode.SASServiceLevel;
hasScienceContainer = CurrentProceduralAvionicsTechNode.hasScienceContainer;

UpdateCostAndMassDisplays();

utilizationDisplay = String.Format("{0:0.#}%", GetControllableUtilizationPercentage() * 200);
Log("Utilization display: ", utilizationDisplay);

StringBuilder powerConsumptionBuilder = StringBuilderCache.Acquire();
if (GetEnabledkW() >= 0.1) {
powerConsumptionBuilder.AppendFormat(kwFormat, GetEnabledkW()).Append(" kW");
}
else {
powerConsumptionBuilder.AppendFormat(wFormat, GetEnabledkW() * 1000).Append(" W");
}
if (GetDisabledkW() > 0) {
powerConsumptionBuilder.Append(" /");
if (GetDisabledkW() >= 0.1) {
powerConsumptionBuilder.AppendFormat(kwFormat, GetDisabledkW()).Append(" kW");
}
else {
powerConsumptionBuilder.AppendFormat(wFormat, GetDisabledkW() * 1000).Append(" W");
}
}

powerRequirementsDisplay = powerConsumptionBuilder.ToStringAndRelease();

}

// creating a field for this so we don't need to look it up every update
private ModuleSAS sasModule = null;
{
Log("Setting internal KSP fields");
Log("avionics tech level: ", avionicsTechLevel);

tonnageToMassRatio = CurrentProceduralAvionicsTechNode.tonnageToMassRatio;
maxDensityOfAvionics = CurrentProceduralAvionicsTechNode.standardAvionicsDensity * 4 / 3;
costPerControlledTon = CurrentProceduralAvionicsTechNode.costPerControlledTon;
enabledProceduralW = CurrentProceduralAvionicsTechNode.enabledProceduralW;
disabledProceduralW = CurrentProceduralAvionicsTechNode.disabledProceduralW;

minimumTonnage = CurrentProceduralAvionicsTechNode.minimumTonnage;
maximumTonnage = CurrentProceduralAvionicsTechNode.maximumTonnage;

SASServiceLevel = CurrentProceduralAvionicsTechNode.SASServiceLevel;
hasScienceContainer = CurrentProceduralAvionicsTechNode.hasScienceContainer;
}

private void RefreshDisplays()
{
RefreshCostAndMassDisplays();

utilizationDisplay = String.Format("{0:0.#}%", GetControllableUtilizationPercentage() * 200);
Log("Utilization display: ", utilizationDisplay);

RefreshPowerDisplay();
}

private void RefreshPowerDisplay()
{
StringBuilder powerConsumptionBuilder = StringBuilderCache.Acquire();
if (GetEnabledkW() >= 0.1)
{
powerConsumptionBuilder.AppendFormat(kwFormat, GetEnabledkW()).Append(" kW");
}
else
{
powerConsumptionBuilder.AppendFormat(wFormat, GetEnabledkW() * 1000).Append(" W");
}
if (GetDisabledkW() > 0)
{
powerConsumptionBuilder.Append(" /");
if (GetDisabledkW() >= 0.1)
{
powerConsumptionBuilder.AppendFormat(kwFormat, GetDisabledkW()).Append(" kW");
}
else
{
powerConsumptionBuilder.AppendFormat(wFormat, GetDisabledkW() * 1000).Append(" W");
}
}

powerRequirementsDisplay = powerConsumptionBuilder.ToStringAndRelease();
}

// creating a field for this so we don't need to look it up every update
private ModuleSAS sasModule = null;
private void SetSASServiceLevel()
{
if (SASServiceLevel >= 0) {
@@ -560,7 +606,8 @@ private void SetScienceContainer()
bool ppFieldsHidden = false;
string TCSmoduleName = "TankContentSwitcher";
string PPModuleName = "ProceduralPart";
private void UpdateCostAndMassDisplays()

private void RefreshCostAndMassDisplays()
{
if (!ppFieldsHidden) {
ppFieldsHidden = HideField(TCSmoduleName, "massDisplay") && HideField(TCSmoduleName, "volumeDisplay");
@@ -700,9 +747,7 @@ void WindowFunction(int windowID)
currentProceduralAvionicsConfig = currentlyDisplayedConfigs;
avionicsConfigName = guiAvionicsConfigName;
AvionicsConfigChanged();
SetMinVolume(true);
ResetTo100();
}
}
}
}
GUILayout.Label(" "); // blank space
6 changes: 3 additions & 3 deletions Source/Avionics/ProceduralAvionicsUtils.cs
Original file line number Diff line number Diff line change
@@ -7,12 +7,12 @@ namespace RP0.ProceduralAvionics
{
static class ProceduralAvionicsUtils
{
private static bool ENABLED = false;
private static bool _enableLogging = false;

private const string logPreix = "[ProcAvi] - ";
public static void Log(params string[] message)
{
if (ENABLED) {
if (_enableLogging) {
var builder = StringBuilderCache.Acquire();
builder.Append(logPreix);
foreach (string part in message) {
@@ -24,7 +24,7 @@ public static void Log(params string[] message)

public static void Log(params object[] parts)
{
if (ENABLED) {
if (_enableLogging) {
var builder = StringBuilderCache.Acquire();
builder.Append(logPreix);
foreach (object part in parts) {

0 comments on commit 73660c0

Please sign in to comment.