Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow only EC to be added inside procedural avionics. #2236

Merged
merged 1 commit into from
Sep 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion GameData/RP-1/Tree/ProceduralAvionics.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
%vesselType = Probe
%maxTemp = 1073.15
%skinMaxTemp = 1073.15
%roTankType = service
%roTankType = battery

%MODULE[AdjustableCoMShifter] {}

Expand Down
21 changes: 21 additions & 0 deletions GameData/RP-1/Tree/ResourceTechs.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,27 @@
// ===============================================================================
// Part Upgrade patches
// ===============================================================================
@PARTUPGRADE[RFTech-Battery-II]:FOR[xxxRP0]
{
%techRequired = electronicsHuman
%entryCost = 0
%cost = 0
}

@PARTUPGRADE[RFTech-Battery-III]:FOR[xxxRP0]
{
%techRequired = electronicsAdvCapsules
%entryCost = 0
%cost = 0
}

@PARTUPGRADE[RFTech-Battery-IV]:FOR[xxxRP0]
{
%techRequired = electronicsLunar
%entryCost = 0
%cost = 0
}

@PARTUPGRADE[RFTech-SM-II]:FOR[xxxRP0]
{
%techRequired = materialsScienceHuman
Expand Down
12 changes: 3 additions & 9 deletions Source/Avionics/ModuleProceduralAvionics.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using KSPAPIExtensions;
using RealFuels.Tanks;
using RP0.Utilities;
using System;
using System.Collections.Generic;
using UniLinq;
Expand All @@ -16,8 +15,6 @@ public partial class ModuleProceduralAvionics : ModuleAvionics, IPartMassModifie
private const string KwFormat = "{0:0.##}";
private const string WFormat = "{0:0}";
private const float FloatTolerance = 1.00001f;
internal const float InternalTanksTotalVolumeUtilization = 0.246f; //Max utilization for 2 spheres within a cylindrical container worst case scenario
internal const float InternalTanksAvailableVolumeUtilization = 0.5f;

#region Fields and properties

Expand Down Expand Up @@ -90,8 +87,6 @@ public ProceduralAvionicsTechNode CurrentProceduralAvionicsTechNode
private float MaxAvionicsMass => (_cachedVolume - CurrentProceduralAvionicsTechNode.reservedRFTankVolume) *
CurrentProceduralAvionicsTechNode.avionicsDensity;

public float InternalTanksVolume { get; private set; }

public bool CanSeekVolume => _seekVolumeMethod != null && _seekVolumeMethod.GetParameters().Length == 2;

#endregion
Expand Down Expand Up @@ -471,9 +466,8 @@ internal void SendRemainingVolume()
{
Events[nameof(OnPartVolumeChanged)].active = false;
float availVol = GetAvailableVolume();
InternalTanksVolume = SphericalTankUtilities.GetSphericalTankVolume(availVol);
Log($"SendRemainingVolume(): Cached Volume: {_cachedVolume}. AvionicsVolume: {GetAvionicsVolume()}. AvailableVolume: {availVol}. Internal Tanks: {InternalTanksVolume}");
SendVolumeChangedEvent(InternalTanksVolume);
Log($"SendRemainingVolume(): Cached Volume: {_cachedVolume}. AvionicsVolume: {GetAvionicsVolume()}. AvailableVolume: {availVol}.");
SendVolumeChangedEvent(availVol);
_rfPM?.CalculateMass();
Events[nameof(OnPartVolumeChanged)].active = true;
}
Expand Down Expand Up @@ -504,7 +498,7 @@ private void UpdateControllableMassInWindow()
_window.ControllableMass = $"{controllableMass:0.###}";
}

internal float GetAvailableVolume() => Math.Max(Math.Min((_cachedVolume - GetAvionicsVolume()) * InternalTanksAvailableVolumeUtilization, _cachedVolume * InternalTanksTotalVolumeUtilization), 0);
internal float GetAvailableVolume() => Math.Max(_cachedVolume - GetAvionicsVolume(), 0);

internal bool SetProcPartVolumeLimit()
{
Expand Down
64 changes: 16 additions & 48 deletions Source/Avionics/ProceduralAvionicsWindow.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using ClickThroughFix;
using RealFuels.Tanks;
using RP0.Utilities;
using System;
using System.Collections.Generic;
using System.Reflection;
Expand All @@ -11,9 +10,6 @@ namespace RP0.ProceduralAvionics
{
public class ProceduralAvionicsWindow : MonoBehaviour
{
private const float InternalTanksAvailableVolumeUtilization = ModuleProceduralAvionics.InternalTanksAvailableVolumeUtilization;
private const float InternalTanksTotalVolumeUtilization = ModuleProceduralAvionics.InternalTanksTotalVolumeUtilization;

private static readonly int _windowId = "RP0ProcAviWindow".GetHashCode();

private Rect _windowRect = new Rect(267, 104, 400, 300);
Expand Down Expand Up @@ -204,9 +200,9 @@ private void WindowFunction(int windowID)
GUILayout.BeginHorizontal(GUILayout.Width(250));
_gc ??= new GUIContent();
_gc.text = "Additional tank volume: ";
_gc.tooltip = "How much tank volume will be left for other resources after applying the desired controllable mass and amount of EC.";
_gc.tooltip = "Amount of excess tank volume";
GUILayout.Label(_gc, HighLogic.Skin.label, GUILayout.Width(150));
GUI.enabled = _module.CanSeekVolume;
GUI.enabled = false;
_sExtraVolume = GUILayout.TextField(_sExtraVolume, HighLogic.Skin.textField);
GUI.enabled = true;
GUILayout.Label("l", HighLogic.Skin.label);
Expand All @@ -225,7 +221,7 @@ private void WindowFunction(int windowID)

GUILayout.BeginHorizontal();
_gc.text = "Apply (resize to fit)";
_gc.tooltip = "Applies the parameters above and resizes the part to have the correct amount of volume";
_gc.tooltip = "Applies the parameters above and resizes the part to have the optimal amount of volume";
if (GUILayout.Button(_gc, HighLogic.Skin.button))
{
ApplyAvionicsSettings(shouldSeekVolume: true);
Expand Down Expand Up @@ -366,43 +362,18 @@ private void ApplyAvionicsSettings(bool shouldSeekVolume)
return;
}

float extraVolumeLiters = 0;
if (_module.CanSeekVolume && (!float.TryParse(_sExtraVolume, out extraVolumeLiters) || extraVolumeLiters < 0))
{
ScreenMessages.PostScreenMessage("Invalid Additional volume value");
_sExtraVolume = "0";
return;
}

if (!float.TryParse(_sECAmount, out float ecAmount) || ecAmount <= 0)
{
ScreenMessages.PostScreenMessage("EC amount needs to be larger than 0");
_sECAmount = $"{_ecTank.maxAmount:F0}";
return;
}

// Store and sum together the volume of all resources other than EC on this part
double otherFuelVolume = 0;
var otherTanks = new List<KeyValuePair<FuelTank, double>>();
foreach (FuelTank t in _tanksDict.Values)
{
if (t == _ecTank || t.maxAmount == 0) continue;
otherTanks.Add(new KeyValuePair<FuelTank, double>(t, t.maxAmount));
otherFuelVolume += t.maxAmount / t.utilization;
}

_module.controllableMass = newControlMass;
if (shouldSeekVolume && _module.CanSeekVolume)
{
_module.SetProcPartVolumeLimit();
ApplyCorrectProcTankVolume(extraVolumeLiters + (float)otherFuelVolume, ecAmount);

// Restore all the pre-resize amounts in tanks
foreach (KeyValuePair<FuelTank, double> kvp in otherTanks)
{
FuelTank t = kvp.Key;
t.amount = t.maxAmount = kvp.Value;
}
ApplyCorrectProcTankVolume(0, ecAmount);
}
else
{
Expand All @@ -422,13 +393,13 @@ private void ApplyAvionicsSettings(bool shouldSeekVolume)
// In this case need to clamp EC amount to ensure that it stays within the available volume
float avVolume = _module.GetAvionicsVolume();
float m3AvailVol = _module.GetAvailableVolume();
double m3CurVol = avVolume + _rfPM.totalVolume / InternalTanksAvailableVolumeUtilization / 1000; // l to m³, assume 100% RF utilization but do account for the ProcAvi internal utilization
double m3MinVol = GetNeededProcTankVolume((float)otherFuelVolume, ecAmount);
double m3CurVol = avVolume + _rfPM.totalVolume / 1000; // l to m³, assume 100% RF utilization
double m3MinVol = GetNeededProcTankVolume(0, ecAmount);
double m3MissingVol = m3MinVol - m3CurVol;
if (m3MissingVol > 0.0001)
{
ecAmount = 1; // Never remove the EC resource entirely
double m3AvailVolForEC = m3AvailVol - otherFuelVolume;
double m3AvailVolForEC = m3AvailVol;
if (m3AvailVolForEC > 0)
{
ecAmount = GetECAmountForVolume((float)m3AvailVolForEC);
Expand Down Expand Up @@ -458,20 +429,18 @@ private float GetNeededProcTankVolume(float extraVolumeLiters, float ecAmount)
float utilization = utilizationPercent / 100;
float avVolume = _module.GetAvionicsVolume();

// The amount of final available volume that RF tanks get is calculated in 4 steps:
// The amount of final available volume that RF tanks get is calculated in 3 steps:
// 1) ModuleProceduralAvionics.GetAvailableVolume()
// 2) SphericalTankUtilities.GetSphericalTankVolume()
// 3) RF tank's utilization (the slider in the PAW)
// 4) RF (internal) tank's per-resource utilization value.
// 2) RF tank's utilization (the slider in the PAW)
// 3) RF (internal) tank's per-resource utilization value.
// This is currently set at 1000 for EC which means that 1l of volume can hold 1000 units of EC.
// The code below runs all these but in reversed order.

float lVolStep4 = ecAmount / _ecTank.utilization;
float lVolStep3 = (lVolStep4 + extraVolumeLiters) / utilization;
float m3VolStep3 = lVolStep3 / 1000; // RF volumes are in liters but avionics uses m³
float m3VolStep2 = SphericalTankUtilities.GetRequiredVolumeFromSphericalTankVolume(m3VolStep3);
float m3TotalVolume = Math.Max((InternalTanksAvailableVolumeUtilization * avVolume + m3VolStep2) / InternalTanksAvailableVolumeUtilization,
m3VolStep2 / InternalTanksTotalVolumeUtilization);
float lVolStep3 = ecAmount / _ecTank.utilization;
float lVolStep2 = (lVolStep3 + extraVolumeLiters) / utilization;
lVolStep2 = Math.Max(lVolStep2, _module.CurrentProceduralAvionicsTechNode.reservedRFTankVolume);
float m3VolStep2 = lVolStep2 / 1000; // RF volumes are in liters but avionics uses m³
float m3TotalVolume = Math.Max(avVolume + m3VolStep2, m3VolStep2);
return m3TotalVolume;
}

Expand All @@ -480,8 +449,7 @@ private float GetECAmountForVolume(float m3Volume)
float utilizationPercent = _rfPM.utilization;
float utilization = utilizationPercent / 100;

float m3VolStep2 = SphericalTankUtilities.GetSphericalTankVolume(m3Volume);
float step3 = m3VolStep2 * 1000 * utilization;
float step3 = m3Volume * 1000 * utilization;
float ecAmount = step3 * _ecTank.utilization;
return ecAmount;
}
Expand Down
1 change: 0 additions & 1 deletion Source/RP0.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,6 @@
<Compile Include="Utilities\ContinuousLogger.cs" />
<Compile Include="Utilities\ContractUtils.cs" />
<Compile Include="Utilities\CurrencyUtils.cs" />
<Compile Include="Utilities\SphericalTankUtilities.cs" />
<Compile Include="Utilities\KACWrapper.cs" />
<Compile Include="Utilities\KSPUtils.cs" />
<Compile Include="Utilities\MathUtils.cs" />
Expand Down
85 changes: 4 additions & 81 deletions Source/Tooling/ModuleToolingProcAvionics.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
using System;
using RP0.ProceduralAvionics;
using RP0.Utilities;
using UnityEngine;

namespace RP0
{
Expand All @@ -14,8 +12,6 @@ public class ModuleToolingProcAvionics : ModuleToolingPTank
private ModuleProceduralAvionics _procAvionics;

public override string ToolingType => $"{MainToolingType}-{_procAvionics.CurrentProceduralAvionicsConfig.name[0]}{_procAvionics.CurrentProceduralAvionicsTechNode.techLevel}";
private string TankToolingType => base.ToolingType;
private ToolingDefinition TankToolingDefinition => ToolingManager.Instance.GetToolingDefinition(TankToolingType);

private float ControllableMass => _procAvionics?.controllableMass ?? 0f;

Expand All @@ -27,25 +23,13 @@ protected override void LoadPartModules()

public override string GetToolingParameterInfo()
{
return $"{Math.Round(ControllableMass, 3)} t x {base.GetToolingParameterInfo()}{GetInternalTankDiameterInfo()}";
}

private object GetInternalTankDiameterInfo()
{
if (_procAvionics.InternalTanksVolume == 0)
{
return "";
}

GetDimensions(out var diameter, out var length);
var tankDiameter = GetInternalTankDiameter(diameter, length);
return $" ({TankToolingType} {tankDiameter} m)";
return $"{Math.Round(ControllableMass, 3)} t x {base.GetToolingParameterInfo()}";
}

public override float GetToolingCost()
{
GetDimensions(out var diameter, out var length);
return GetAvionicsToolingCost(diameter, length) + GetInternalTankToolingCost(diameter, length);
return GetAvionicsToolingCost(diameter, length);
}

private float GetAvionicsToolingCost(float diameter, float length)
Expand Down Expand Up @@ -78,69 +62,19 @@ private float[] GetPerLevelToolingCosts(float diameter, float length)
};
}

private float GetInternalTankToolingCost(float externalDiameter, float length)
{
if(_procAvionics.InternalTanksVolume == 0)
{
return 0;
}

var internalTankDiameter = GetInternalTankDiameter(externalDiameter, length);
var level = ToolingDatabase.GetToolingLevel(TankToolingType, internalTankDiameter, internalTankDiameter);
var perLevelCosts = new[] { GetDiameterToolingCost(internalTankDiameter), GetLengthToolingCost(internalTankDiameter, internalTankDiameter) };
var costMult = TankToolingDefinition?.finalToolingCostMultiplier ?? 0f;
return GetToolingCost(level, perLevelCosts) * costMult;
}

private float GetInternalTankDiameter(float externalDiameter, float length)
{
var maxDiameter = Mathf.Min(externalDiameter * 2 / 3, length);
var internalTankDiameter = SphericalTankUtilities.GetSphericalTankRadius(_procAvionics.InternalTanksVolume) * 2;
while (internalTankDiameter > maxDiameter) { internalTankDiameter /= 2; }

return internalTankDiameter;
}

private float GetControlledMassToolingCost() => _procAvionics.GetModuleCost(0, ModifierStagingSituation.UNSTAGED) * avionicsToolingCostMultiplier;

public override float GetModuleCost(float defaultCost, ModifierStagingSituation sit)
{
if (!onStartFinishedFinished || HighLogic.CurrentGame.Mode != Game.Modes.CAREER) return 0f;

return GetUntooledPenaltyCost() + GetInternalTankModuleCost();
}

private float GetInternalTankModuleCost()
{
if (_procAvionics.InternalTanksVolume == 0)
{
return 0;
}

GetDimensions(out var externalDiameter, out var length);
var internalTankDiameter = GetInternalTankDiameter(externalDiameter, length);
var tankCount = _procAvionics.InternalTanksVolume / SphericalTankUtilities.GetSphereVolume(internalTankDiameter / 2);
var costMultDL = TankToolingDefinition?.costMultiplierDL ?? 0f;

return GetDimensionModuleCost(internalTankDiameter, length, costMultDL) * tankCount;
return GetUntooledPenaltyCost();
}

public override void PurchaseTooling()
{
GetDimensions(out var diameter, out var length);
ToolingDatabase.UnlockTooling(ToolingType, ControllableMass, diameter, length);
UnlockInternalTankTooling(diameter, length);
}

private void UnlockInternalTankTooling(float diameter, float length)
{
if(_procAvionics.InternalTanksVolume == 0)
{
return;
}

var internalTankDiameter = GetInternalTankDiameter(diameter, length);
ToolingDatabase.UnlockTooling(TankToolingType, internalTankDiameter, internalTankDiameter);
}

public override bool IsUnlocked()
Expand All @@ -151,18 +85,7 @@ public override bool IsUnlocked()
}

GetDimensions(out var diameter, out var length);
return IsAvionicsTooled(diameter, length) && IsInternalTankTooled(diameter, length);
}

private bool IsInternalTankTooled(float diameter, float length)
{
if(_procAvionics.InternalTanksVolume == 0)
{
return true;
}

var internalTankDiameter = GetInternalTankDiameter(diameter, length);
return ToolingDatabase.GetToolingLevel(TankToolingType, internalTankDiameter, internalTankDiameter) == 2;
return IsAvionicsTooled(diameter, length);
}

private bool IsAvionicsTooled(float diameter, float length) => ToolingDatabase.GetToolingLevel(ToolingType, ControllableMass, diameter, length) == 3;
Expand Down
Loading
Loading