You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
252 lines
11 KiB
252 lines
11 KiB
10 years ago
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Linq;
|
||
|
using System.Text;
|
||
|
using UnityEngine;
|
||
|
|
||
|
namespace Aerostats
|
||
|
{
|
||
9 years ago
|
public class ModuleBalloonCase : PartModule
|
||
10 years ago
|
{
|
||
|
/// <summary>
|
||
|
/// Maximum rate at which gas can be injected in the balloon to fill it, in m^3/s at stp
|
||
|
/// </summary>
|
||
|
[KSPField(isPersistant = false, guiActive = false)]
|
||
|
public float MaxGasFillRate = 100;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Maximum rate at which gas can be removed from the balloon to deflate it, in m^3/s at stp
|
||
|
/// </summary>
|
||
|
[KSPField(isPersistant = false, guiActive = false)]
|
||
|
public float MaxGasVentRate = 500;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Volume at which the balloon is full. Trying to add more gas, or simply having already stored gas expand due to lower exterior pressure or increased temperature, will cause gas to be vented through the security valve.
|
||
|
/// Air density at sea level is about 1.2kg/m^3, which means a 1000m^3 balloon will be able to lift about 1 ton at sea level (after substracting the balloon gas weight)
|
||
|
/// </summary>
|
||
|
[KSPField(isPersistant = false, guiActive = false)]
|
||
10 years ago
|
public float MaxBalloonVolume = 15000;
|
||
10 years ago
|
|
||
|
/// <summary>
|
||
|
/// Weight, in kilograms, of 1m^3 of gas at 100kPa and 0°C
|
||
|
/// Helium is 179g/m^3
|
||
|
/// Hydrogen is 90g/m^3
|
||
|
/// </summary>
|
||
|
[KSPField(isPersistant = false, guiActive = false)]
|
||
|
public float GasDensity = 0.179f;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Quantity of gas that the system is trying to get inside the balloon, in m^3 at 100kPa and 0°C. If the balloon contains more gas, some will be vented, otherwise, gas will be injected.
|
||
|
/// If the balloon can not store the target amount of gas (because the maximum volume has been reached), the system won't try to inject more gas, to avoid venting through the security valve of the balloon.
|
||
|
/// </summary>
|
||
|
[KSPField(isPersistant = true, guiActive = true)]
|
||
10 years ago
|
[UI_FloatRange(minValue = 0, maxValue = 20000.0f, stepIncrement = 100.0f)]
|
||
|
public float LiftingGasTargetQuantity = 100.0f;
|
||
10 years ago
|
|
||
10 years ago
|
/// <summary>
|
||
|
/// Mass of the empty balloon, in kg
|
||
|
/// </summary>
|
||
|
[KSPField(isPersistant = false, guiActive = false)]
|
||
|
public float BalloonEmptyMass = 50.0f;
|
||
|
|
||
|
/// <summary>
|
||
10 years ago
|
/// Minimum amount of gas that will be used to inflate the balloon at the beginning. The balloon can not be deflated below this amount.
|
||
10 years ago
|
/// </summary>
|
||
|
[KSPField(isPersistant = false, guiActive = false)]
|
||
10 years ago
|
public float MinimumFillQuantity = 10.0f;
|
||
10 years ago
|
|
||
|
/// <summary>
|
||
|
/// Drag coefficient of the balloon
|
||
|
/// </summary>
|
||
|
[KSPField(isPersistant = false, guiActive = false)]
|
||
|
public float BalloonDragCoeff = 0.3f;
|
||
|
|
||
10 years ago
|
/// <summary>
|
||
|
/// Length of spring that can not be extended (no force applied under this length)
|
||
|
/// </summary>
|
||
|
[KSPField(isPersistant = false, guiActive = false)]
|
||
|
public float SpringRestLength = 10.0f;
|
||
|
|
||
10 years ago
|
/// <summary>
|
||
|
/// Force applied by the spring between the balloon and the vessel, proportional to spring extension, in newton per meter
|
||
|
/// </summary>
|
||
|
[KSPField(isPersistant = false, guiActive = false)]
|
||
10 years ago
|
public float SpringHardness = 5000.0f;
|
||
10 years ago
|
|
||
9 years ago
|
[KSPField(isPersistant = false, guiActive = true)]
|
||
|
public string Status;
|
||
|
|
||
|
private bool Deployed;
|
||
10 years ago
|
|
||
10 years ago
|
private bool Destroyed;
|
||
|
|
||
10 years ago
|
/// <summary>
|
||
|
/// Gas quantity currently inside the balloon
|
||
|
/// </summary>
|
||
9 years ago
|
[KSPField(guiName = "Gas quantity", isPersistant = true, guiActive = true)]
|
||
10 years ago
|
public float LiftingGasQuantity;
|
||
|
|
||
9 years ago
|
[KSPField(guiName = "Volume", isPersistant = false, guiActive = true)]
|
||
|
public string VolumeStatus;
|
||
|
|
||
9 years ago
|
private ModuleBalloon Balloon;
|
||
10 years ago
|
private LineRenderer Spring;
|
||
10 years ago
|
|
||
10 years ago
|
private Vector3 EstimatedNextFramePosition;
|
||
|
|
||
9 years ago
|
[KSPEvent(guiActive = true, active = true, externalToEVAOnly = false, guiActiveUnfocused = true, guiName = "Deploy balloon", unfocusedRange = 5)]
|
||
|
public void Deploy()
|
||
|
{
|
||
|
if (Destroyed || Deployed)
|
||
|
return;
|
||
10 years ago
|
|
||
9 years ago
|
Deployed = true;
|
||
10 years ago
|
|
||
|
UnityEngine.Debug.Log("Aerostats: staged");
|
||
9 years ago
|
Util.PostScreenMessage("staged");
|
||
10 years ago
|
|
||
9 years ago
|
var balloonObject = GameObject.CreatePrimitive(PrimitiveType.Sphere);
|
||
|
balloonObject.transform.position = part.Rigidbody.position + part.Rigidbody.transform.up;
|
||
|
Balloon = balloonObject.AddComponent<ModuleBalloon>();
|
||
|
Balloon.part = part; // TODO: remove this hack and properly instantiate a new vessel?
|
||
|
Balloon.Initialize(BalloonEmptyMass, part.rigidbody.velocity, MaxBalloonVolume, GasDensity, BalloonDragCoeff);
|
||
10 years ago
|
|
||
9 years ago
|
LiftingGasQuantity = Balloon.InjectGas(part.RequestResource("Helium", MinimumFillQuantity));
|
||
10 years ago
|
|
||
9 years ago
|
Spring = part.gameObject.AddComponent<LineRenderer>();
|
||
10 years ago
|
Spring.useWorldSpace = true;
|
||
|
Spring.material = new Material(Shader.Find("VertexLit"));
|
||
|
Spring.SetColors(Color.black, Color.black);
|
||
9 years ago
|
Spring.SetWidth(0.2f, 0.2f);
|
||
10 years ago
|
Spring.SetVertexCount(2);
|
||
|
|
||
|
part.OnJustAboutToBeDestroyed += OnPartDestroyed;
|
||
10 years ago
|
|
||
9 years ago
|
EstimatedNextFramePosition = part.Rigidbody.position;
|
||
10 years ago
|
}
|
||
|
|
||
9 years ago
|
[KSPEvent(guiActive = true, active = true, externalToEVAOnly = false, guiActiveUnfocused = true, guiName = "Cut rope", unfocusedRange = 5)]
|
||
|
public void Cut()
|
||
|
{
|
||
|
if (Destroyed || !Deployed)
|
||
|
return;
|
||
|
|
||
10 years ago
|
Balloon = null;
|
||
9 years ago
|
Destroy(Spring);
|
||
10 years ago
|
Spring = null;
|
||
|
part.OnJustAboutToBeDestroyed -= OnPartDestroyed;
|
||
9 years ago
|
Destroyed = true;
|
||
|
}
|
||
|
|
||
|
public override void OnStart(PartModule.StartState state)
|
||
|
{
|
||
|
if (!HighLogic.LoadedSceneIsEditor && !HighLogic.LoadedSceneIsFlight) { return; }
|
||
|
|
||
|
Util.PostScreenMessage("Aerostat loaded");
|
||
|
part.stagingIcon = "PARACHUTES";
|
||
|
}
|
||
|
|
||
|
private void OnPartDestroyed()
|
||
|
{
|
||
|
Cut();
|
||
10 years ago
|
}
|
||
|
|
||
|
private void FixedUpdate()
|
||
|
{
|
||
10 years ago
|
if (Destroyed)
|
||
9 years ago
|
{
|
||
|
Status = "separated";
|
||
|
VolumeStatus = "-";
|
||
10 years ago
|
return;
|
||
9 years ago
|
}
|
||
10 years ago
|
|
||
9 years ago
|
if (GameSettings.LAUNCH_STAGES.GetKeyDown() && vessel.isActiveVessel && (part.inverseStage == Staging.CurrentStage - 1 || Staging.CurrentStage == 0))
|
||
10 years ago
|
{
|
||
9 years ago
|
Deploy();
|
||
10 years ago
|
}
|
||
|
|
||
9 years ago
|
if (Deployed)
|
||
10 years ago
|
{
|
||
10 years ago
|
// detect Krakensbane teleportation, and fix up the balloon position (otherwise it results in instant ship disintegration due to extreme forces on the spring)
|
||
9 years ago
|
if ((part.Rigidbody.position - EstimatedNextFramePosition).magnitude > 1000.0f)
|
||
10 years ago
|
{
|
||
9 years ago
|
Util.PostScreenMessage("Krakensbane teleportation detected! (dist=" + (part.Rigidbody.position - EstimatedNextFramePosition).magnitude+")");
|
||
10 years ago
|
var offset = part.rigidbody.position - EstimatedNextFramePosition;
|
||
|
Balloon.rigidbody.position += offset;
|
||
|
Balloon.transform.position = Balloon.rigidbody.position;
|
||
|
}
|
||
|
EstimatedNextFramePosition = part.rigidbody.position + part.rigidbody.velocity * Time.fixedDeltaTime;
|
||
|
|
||
9 years ago
|
float currentMaxQuantity = Balloon.GetCurrentMaxQuantity();
|
||
10 years ago
|
|
||
|
LiftingGasTargetQuantity = Math.Max(MinimumFillQuantity, LiftingGasTargetQuantity);
|
||
9 years ago
|
if(LiftingGasTargetQuantity > Balloon.LiftingGasQuantity)
|
||
10 years ago
|
{
|
||
9 years ago
|
// inflating balloon
|
||
|
float stepFinalQuantity = Math.Min(Balloon.LiftingGasQuantity + MaxGasFillRate * Time.fixedDeltaTime, Math.Min(LiftingGasTargetQuantity, currentMaxQuantity));
|
||
|
float step = Math.Max(stepFinalQuantity - Balloon.LiftingGasQuantity, 0.0f);
|
||
9 years ago
|
float stepResource = part.RequestResource("Helium", step);
|
||
9 years ago
|
LiftingGasQuantity = Balloon.InjectGas(stepResource);
|
||
9 years ago
|
if(step > 0.0f)
|
||
|
{
|
||
|
Status = stepResource > 0.0f ? "inflating" : "out-of-gas";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Status = "nominal";
|
||
|
}
|
||
10 years ago
|
}
|
||
|
else
|
||
|
{
|
||
|
// deflating balloon
|
||
9 years ago
|
Status = LiftingGasTargetQuantity == Balloon.LiftingGasQuantity ? "nominal" : "deflating";
|
||
|
float quantityAfterVenting = Math.Max(Balloon.LiftingGasQuantity - MaxGasVentRate * Time.fixedDeltaTime, LiftingGasTargetQuantity);
|
||
|
LiftingGasQuantity = Balloon.InjectGas(quantityAfterVenting - Balloon.LiftingGasQuantity);
|
||
10 years ago
|
}
|
||
|
|
||
9 years ago
|
if(Balloon.IsVenting)
|
||
10 years ago
|
{
|
||
9 years ago
|
Status = "full (venting)";
|
||
10 years ago
|
}
|
||
9 years ago
|
|
||
|
VolumeStatus = Mathf.Round(Balloon.CurrentVolume) + " / " + Mathf.Round(MaxBalloonVolume);
|
||
10 years ago
|
|
||
|
// spring between balloon and base
|
||
9 years ago
|
float restLength = SpringRestLength + Balloon.Radius;
|
||
10 years ago
|
var springVec = Balloon.rigidbody.position - part.rigidbody.position;
|
||
|
float springLength = springVec.magnitude;
|
||
|
float springForceMag = 0;
|
||
9 years ago
|
var balloonAttachPoint = Balloon.rigidbody.position - Balloon.transform.up.normalized * Balloon.Radius;
|
||
10 years ago
|
if(springLength > restLength)
|
||
|
{
|
||
|
float tensingLength = springLength - restLength;
|
||
10 years ago
|
springForceMag = tensingLength * SpringHardness;
|
||
10 years ago
|
var springForce = springVec * (springForceMag / springLength * 0.001f);
|
||
|
part.rigidbody.AddForce(springForce, ForceMode.Force);
|
||
|
Balloon.rigidbody.AddForceAtPosition(-springForce, balloonAttachPoint, ForceMode.Force);
|
||
10 years ago
|
}
|
||
10 years ago
|
Util.PostSingleScreenMessage("spring force", "Spring force = " + springForceMag + "N");
|
||
9 years ago
|
}
|
||
|
else
|
||
|
{
|
||
|
Status = "packed";
|
||
|
VolumeStatus = "-";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void LateUpdate()
|
||
|
{
|
||
|
if (Destroyed)
|
||
|
return;
|
||
|
|
||
|
if (Deployed)
|
||
|
{
|
||
9 years ago
|
var balloonAttachPoint = Balloon.rigidbody.position - Balloon.transform.up.normalized * Balloon.Radius;
|
||
10 years ago
|
|
||
|
Spring.SetPosition(0, part.transform.position);
|
||
|
Spring.SetPosition(1, balloonAttachPoint);
|
||
10 years ago
|
}
|
||
|
}
|
||
10 years ago
|
}
|
||
|
}
|