diff --git a/Parts/Balloon/balloon.cfg b/Parts/Balloon/balloon.cfg index da650ed..8552083 100644 --- a/Parts/Balloon/balloon.cfg +++ b/Parts/Balloon/balloon.cfg @@ -11,11 +11,11 @@ PART sound_parachute_open = activate TechRequired = advLanding entryCost = 3500 - cost = 400 + cost = 5000 category = Utility subcategory = 0 title = Balloon - description = A test balloon. + description = A big balloon that can be inflated with helium (sold separately). attachRules = 1,0,0,1,0 mass = 0.2 dragModelType = default @@ -30,7 +30,5 @@ PART MODULE { name = ModuleAerostat - DeployAnimation = semiDeployLarge - InflateAnimation = fullyDeployLarge } } diff --git a/Parts/HeliumTankRadial/heliumTankRadial.cfg b/Parts/HeliumTankRadial/heliumTankRadial.cfg new file mode 100644 index 0000000..d04e366 --- /dev/null +++ b/Parts/HeliumTankRadial/heliumTankRadial.cfg @@ -0,0 +1,31 @@ +PART +{ + name = heliumTankRadial + module = Part + author = Youen + mesh = model.mu + rescaleFactor = 1 + node_attach = 0.0, 0.0, -0.1875, 0.0, 0.0, -1.0 + TechRequired = advLanding + entryCost = 2000 + cost = 2200 + category = FuelTank + subcategory = 0 + title = Helium tank + description = Disposable helium tank for all your lifting needs (and birthday party balloons). Do not refill after use. + attachRules = 0,1,0,0,1 + mass = 0.003 + dragModelType = default + maximum_drag = 0.2 + minimum_drag = 0.2 + angularDrag = 2 + crashTolerance = 12 + maxTemp = 2000 // = 2900 + bulkheadProfiles = srf + RESOURCE + { + name = Helium + amount = 30 + maxAmount = 30 + } +} diff --git a/Parts/HeliumTankRadial/ksp_r_xenonTank_diff.dds b/Parts/HeliumTankRadial/ksp_r_xenonTank_diff.dds new file mode 100644 index 0000000..00e3691 Binary files /dev/null and b/Parts/HeliumTankRadial/ksp_r_xenonTank_diff.dds differ diff --git a/Parts/HeliumTankRadial/model.mu b/Parts/HeliumTankRadial/model.mu new file mode 100644 index 0000000..d79b57f Binary files /dev/null and b/Parts/HeliumTankRadial/model.mu differ diff --git a/Plugin/ModuleAerostat.cs b/Plugin/ModuleAerostat.cs index 302f983..d5cee59 100644 --- a/Plugin/ModuleAerostat.cs +++ b/Plugin/ModuleAerostat.cs @@ -8,12 +8,6 @@ namespace Aerostats { public class ModuleAerostat : PartModule { - [KSPField(isPersistant = false, guiActive = false)] - public string DeployAnimation = "semiDeploy"; - - [KSPField(isPersistant = false, guiActive = false)] - public string InflateAnimation = "fullyDeploy"; - // In this class, amount of gas Q is specified in m^3 at stp (standard pressure of 100kPa and temperature of 0°C = 273K). Ideal gas equation P.V = n.R.T gives us the equivalent number of moles: n = P.V/R/T = 100000.Q/8.314/273 = 44.Q mol private static readonly float R = 8.314f; private static readonly float ZeroCelsius = 273.15f; @@ -39,16 +33,6 @@ namespace Aerostats [KSPField(isPersistant = false, guiActive = false)] public float MaxBalloonVolume = 10000; - /// - /// Maximum initial reserve of compressed gas, in m^3 at stp - /// For helium, the volume needed to store this reserve is about 0.0014m^3 of compressed storage (liquid) for 1m^3 of gas at stp - /// - [KSPField(isPersistant = false, guiActive = false)] - public float CompressedGasStorage = 15000; - - [KSPField(isPersistant = true, guiActive = true)] - public float RemainingCompressedGas = 15000; - /// /// Weight, in kilograms, of 1m^3 of gas at 100kPa and 0°C /// Helium is 179g/m^3 @@ -112,35 +96,6 @@ namespace Aerostats private GameObject Balloon; private LineRenderer Spring; - private void PlayAnimation(string animationName, float animationSpeed) - { - foreach (Animation animation in part.FindModelAnimators(animationName)) - { - AnimationState state = animation[animationName]; - state.normalizedTime = 0; - state.normalizedSpeed = animationSpeed; - state.enabled = true; - animation.Play(animationName); - } - } - - private bool IsAnimationPlaying(string animationName) - { - return part.FindModelAnimators(animationName).Any(a => a[animationName].enabled); - } - - private void SetInflation(float ratio) - { - foreach (Animation animation in part.FindModelAnimators(InflateAnimation)) - { - AnimationState state = animation[InflateAnimation]; - state.normalizedTime = ratio; - state.normalizedSpeed = 0; - state.enabled = true; - animation.Play(InflateAnimation); - } - } - public override void OnStart(PartModule.StartState state) { if (!HighLogic.LoadedSceneIsEditor && !HighLogic.LoadedSceneIsFlight) { return; } @@ -153,7 +108,6 @@ namespace Aerostats { UnityEngine.Debug.Log("Aerostats: staged"); ScreenMessages.PostScreenMessage("staged"); - //PlayAnimation(DeployAnimation, 1.0f); Balloon = GameObject.CreatePrimitive(PrimitiveType.Sphere); Balloon.transform.position = part.Rigidbody.position + part.Rigidbody.transform.up; @@ -162,8 +116,7 @@ namespace Aerostats Balloon.rigidbody.angularDrag = 10.0f; - LiftingGasQuantity = Math.Min(MinimumFillQuantity, RemainingCompressedGas); - RemainingCompressedGas -= LiftingGasQuantity; + LiftingGasQuantity = part.RequestResource("Helium", MinimumFillQuantity); Spring = Balloon.AddComponent(); Spring.useWorldSpace = true; @@ -193,96 +146,83 @@ namespace Aerostats if (Staged) { - //if (!IsAnimationPlaying(DeployAnimation)) + float externalTemperature = (float)FlightGlobals.getExternalTemperature(); + float balloonInternalTemperature = externalTemperature; + float externalPressure = Math.Max((float)FlightGlobals.getStaticPressure() * 1000.0f, 0.00001f); + Util.PostSingleScreenMessage("external atmo", "Temperature = " + externalTemperature + "K, Pressure=" + externalPressure + "Pa"); + + float currentMaxQuantity = MaxBalloonVolume / R / balloonInternalTemperature * externalPressure / StpVolumeToMoles; + + LiftingGasTargetQuantity = Math.Max(MinimumFillQuantity, LiftingGasTargetQuantity); + if(LiftingGasTargetQuantity > LiftingGasQuantity) + { + // infalting balloon + float stepFinalQuantity = Math.Min(LiftingGasQuantity + MaxGasFillRate * Time.fixedDeltaTime, Math.Min(LiftingGasTargetQuantity, currentMaxQuantity)); + float step = Math.Max(stepFinalQuantity - LiftingGasQuantity, 0.0f); + LiftingGasQuantity += part.RequestResource("Helium", step); + } + else + { + // deflating balloon + LiftingGasQuantity = Math.Max(LiftingGasQuantity - MaxGasVentRate * Time.fixedDeltaTime, LiftingGasTargetQuantity); + } + + + // balloon security valve + if (LiftingGasQuantity > currentMaxQuantity) { - float externalTemperature = (float)FlightGlobals.getExternalTemperature(); - float balloonInternalTemperature = externalTemperature; - float externalPressure = Math.Max((float)FlightGlobals.getStaticPressure() * 1000.0f, 0.00001f); - Util.PostSingleScreenMessage("external atmo", "Temperature = " + externalTemperature + "K, Pressure=" + externalPressure + "Pa"); - - float currentMaxQuantity = MaxBalloonVolume / R / balloonInternalTemperature * externalPressure / StpVolumeToMoles; - - LiftingGasTargetQuantity = Math.Max(MinimumFillQuantity, LiftingGasTargetQuantity); - if(LiftingGasTargetQuantity > LiftingGasQuantity) - { - // infalting balloon - float stepFinalQuantity = Math.Min(LiftingGasQuantity + MaxGasFillRate * Time.fixedDeltaTime, Math.Min(LiftingGasTargetQuantity, currentMaxQuantity)); - float step = Math.Max(stepFinalQuantity - LiftingGasQuantity, 0.0f); - LiftingGasQuantity += step; - RemainingCompressedGas -= step; - - // gas exhausted - if(RemainingCompressedGas < 0) - { - Util.PostSingleScreenMessage("out of gas", "Lifting gas reserve exhausted"); - LiftingGasQuantity += RemainingCompressedGas; - RemainingCompressedGas = 0; - } - } - else - { - // deflating balloon - LiftingGasQuantity = Math.Max(LiftingGasQuantity - MaxGasVentRate * Time.fixedDeltaTime, LiftingGasTargetQuantity); - } - - - // balloon security valve - if (LiftingGasQuantity > currentMaxQuantity) - { - Util.PostSingleScreenMessage("security valve", "Some gas has been vented by the balloon security valve"); - LiftingGasQuantity = currentMaxQuantity; - } - - float currentGasMoles = StpVolumeToMoles * LiftingGasQuantity; - float currentGasVolume = currentGasMoles * R * balloonInternalTemperature / externalPressure; - Inflation = currentGasVolume / MaxBalloonVolume; - - Util.PostSingleScreenMessage("inflation", "Inflation = " + (Inflation * 100.0f).ToString("0.00") + "%"); - //SetInflation(Inflation); - - float airDensity = (float)FlightGlobals.getAtmDensity(externalPressure / 1000.0f, externalTemperature, vessel.mainBody); - float currentGasDensity = GasDensity * balloonInternalTemperature / ZeroCelsius / externalPressure * StandardPressure; - float balloonLift = currentGasVolume * airDensity; - float balloonGasMass = currentGasDensity * currentGasVolume; - Util.PostSingleScreenMessage("lift", "Air density = " + airDensity + "kg/m^3, Lift = " + (balloonLift - balloonGasMass) + "kg"); - var gravityAccel = FlightGlobals.getGeeForceAtPosition(vessel.GetWorldPos3D()); - //Util.PostSingleScreenMessage("gravity", "Gravity accel = (" + gravityAccel.x + ", " + gravityAccel.y + ", " + gravityAccel.z + ")"); + Util.PostSingleScreenMessage("security valve", "Some gas has been vented by the balloon security valve"); + LiftingGasQuantity = currentMaxQuantity; + } + + float currentGasMoles = StpVolumeToMoles * LiftingGasQuantity; + float currentGasVolume = currentGasMoles * R * balloonInternalTemperature / externalPressure; + Inflation = currentGasVolume / MaxBalloonVolume; + + Util.PostSingleScreenMessage("inflation", "Inflation = " + (Inflation * 100.0f).ToString("0.00") + "%"); + + float airDensity = (float)FlightGlobals.getAtmDensity(externalPressure / 1000.0f, externalTemperature, vessel.mainBody); + float currentGasDensity = GasDensity * balloonInternalTemperature / ZeroCelsius / externalPressure * StandardPressure; + float balloonLift = currentGasVolume * airDensity; + float balloonGasMass = currentGasDensity * currentGasVolume; + Util.PostSingleScreenMessage("lift", "Air density = " + airDensity + "kg/m^3, Lift = " + (balloonLift - balloonGasMass) + "kg"); + var gravityAccel = FlightGlobals.getGeeForceAtPosition(vessel.GetWorldPos3D()); + //Util.PostSingleScreenMessage("gravity", "Gravity accel = (" + gravityAccel.x + ", " + gravityAccel.y + ", " + gravityAccel.z + ")"); - // V = 4/3*pi*r^3 - float radius = Mathf.Pow(currentGasVolume * 0.75f / Mathf.PI, 0.333f); - float scale = radius * 2.0f + 0.1f; - Balloon.transform.localScale = new Vector3(scale,scale,scale); - Balloon.rigidbody.mass = (BalloonEmptyMass + balloonGasMass) * 0.001f; - - Balloon.rigidbody.AddForce(-gravityAccel * balloonLift / 1000.0f, ForceMode.Force); - - // balloon drag - var airVelocity = Balloon.rigidbody.velocity + Krakensbane.GetFrameVelocity() /*- vessel.mainBody.getRFrmVel(vessel.GetWorldPos3D())*/; - float sqVel = (float)airVelocity.magnitude; - sqVel *= sqVel; - float dragForce = 0.5f * airDensity * sqVel * BalloonDragCoeff * (Mathf.PI * radius * radius); - Util.PostSingleScreenMessage("balloon drag", "Drag = " + dragForce + "N"); - Balloon.rigidbody.AddForce(-airVelocity.normalized * dragForce / 1000.0f, ForceMode.Force); - - // spring between balloon and base - float restLength = SpringRestLength + radius; - var springVec = Balloon.rigidbody.position - part.rigidbody.position; - float springLength = springVec.magnitude; - float springForceMag = 0; - var balloonAttachPoint = Balloon.rigidbody.position - Balloon.transform.up.normalized * radius; - if(springLength > restLength) - { - float tensingLength = springLength - restLength; - springForceMag = tensingLength * SpringHardness; - var springForce = springVec * (springForceMag / springLength * 0.001f); - part.rigidbody.AddForce(springForce, ForceMode.Force); - Balloon.rigidbody.AddForceAtPosition(-springForce, balloonAttachPoint, ForceMode.Force); - } - Util.PostSingleScreenMessage("spring force", "Spring force = " + springForceMag + "N"); - - Spring.SetPosition(0, part.transform.position); - Spring.SetPosition(1, balloonAttachPoint); + // V = 4/3*pi*r^3 + float radius = Mathf.Pow(currentGasVolume * 0.75f / Mathf.PI, 0.333f); + float scale = radius * 2.0f + 0.1f; + Balloon.transform.localScale = new Vector3(scale,scale,scale); + Balloon.rigidbody.mass = (BalloonEmptyMass + balloonGasMass) * 0.001f; + + Balloon.rigidbody.AddForce(-gravityAccel * balloonLift / 1000.0f, ForceMode.Force); + + // balloon drag + var airVelocity = Balloon.rigidbody.velocity + Krakensbane.GetFrameVelocity() /*- vessel.mainBody.getRFrmVel(vessel.GetWorldPos3D())*/; + float sqVel = (float)airVelocity.magnitude; + sqVel *= sqVel; + float dragForce = 0.5f * airDensity * sqVel * BalloonDragCoeff * (Mathf.PI * radius * radius); + Util.PostSingleScreenMessage("balloon drag", "Drag = " + dragForce + "N"); + Balloon.rigidbody.AddForce(-airVelocity.normalized * dragForce / 1000.0f, ForceMode.Force); + + // spring between balloon and base + float restLength = SpringRestLength + radius; + var springVec = Balloon.rigidbody.position - part.rigidbody.position; + float springLength = springVec.magnitude; + float springForceMag = 0; + var balloonAttachPoint = Balloon.rigidbody.position - Balloon.transform.up.normalized * radius; + if(springLength > restLength) + { + float tensingLength = springLength - restLength; + springForceMag = tensingLength * SpringHardness; + var springForce = springVec * (springForceMag / springLength * 0.001f); + part.rigidbody.AddForce(springForce, ForceMode.Force); + Balloon.rigidbody.AddForceAtPosition(-springForce, balloonAttachPoint, ForceMode.Force); } + Util.PostSingleScreenMessage("spring force", "Spring force = " + springForceMag + "N"); + + Spring.SetPosition(0, part.transform.position); + Spring.SetPosition(1, balloonAttachPoint); } } } diff --git a/TweakScale.cfg b/TweakScale.cfg new file mode 100644 index 0000000..f202707 --- /dev/null +++ b/TweakScale.cfg @@ -0,0 +1,16 @@ +@PART[heliumTankRadial] +{ + %MODULE[TweakScale] + { + type = free + } +} + +@PART[balloon] +{ + %MODULE[TweakScale] + { + type = stack + defaultScale = 1.25 + } +} diff --git a/resources.cfg b/resources.cfg new file mode 100644 index 0000000..7689ae3 --- /dev/null +++ b/resources.cfg @@ -0,0 +1,10 @@ +RESOURCE_DEFINITION +{ + name = Helium + density = 0.000179 // storage unit is m^3 of Helium at standard pressure (100kPa, 0°C), which has a density of 0.179kg/m^3 (helium tanks store liquid helium which occupies less space, but still weights the same) + unitCost = 1.2 + hsp = 3000 + flowMode = STAGE_PRIORITY_FLOW + transfer = PUMP + isTweakable = true +}