diff --git a/.gitignore b/.gitignore index b9d6bd9..553b4fb 100644 --- a/.gitignore +++ b/.gitignore @@ -213,3 +213,4 @@ pip-log.txt #Mr Developer .mr.developer.cfg +*.zip diff --git a/ModuleRCSFX.zip b/ModuleRCSFX.zip deleted file mode 100644 index 8cdd9a3..0000000 Binary files a/ModuleRCSFX.zip and /dev/null differ diff --git a/ModuleRCSFX/Plugins/ModuleRCSFX.dll b/ModuleRCSFX/Plugins/ModuleRCSFX.dll new file mode 100644 index 0000000..c807f5e Binary files /dev/null and b/ModuleRCSFX/Plugins/ModuleRCSFX.dll differ diff --git a/ModuleRCSFX/Readme_MRCSFX.txt b/ModuleRCSFX/Readme_MRCSFX.txt new file mode 100644 index 0000000..b6aa516 --- /dev/null +++ b/ModuleRCSFX/Readme_MRCSFX.txt @@ -0,0 +1,84 @@ +ModuleRCSFX is a fixed version of the stock RCS module. +It is derived from ModuleRCSFX by ialdabaoth (who is awesome). +It supports a lot of configuration, as well as fixing stock bugs. + +** RCS Part Controls ** +useZaxis defaults to false. If you set it to true, the RCS will fire along the Z axis of the given transform(s). This means you can use engine part models as RCS parts (like using the ion engine model as an RCS part). + +** RCS Axis Control ** +enablePitch +enableYaw +enableRoll +enableX +enableY +enableZ +All these default to true, but if one is set to false in the MODULE, the RCS part will not fire for that input. These can be toggled in the VAB/SPH. + +useThrottle +which, when set to true, means that RCS will fire forwards with the throttle. + + +** RCS Thrust Control ** +fullThrust defaults to false. Set it to true and if the thrust ratio is > fullThrustMin (default: 0.2) RCS will fire at full thrust (or 10% thrust in precision mode), rather than the less-than-full-thrust, dependent-on-angle they do stock. + +useLever defaults to false. When it's false, fine controls will make RCS fire at 10% (default) power only. When it's set to true, stock behavior returns (i.e. fine controls means lever arm compensation). + +precisionFactor is the multiplier to use when useLever is false (as it is by default). precisionFactor defaults to 0.1 (10%). + +** RCS Input Controls ** +EPSILON defaults to 0.05. That means a control actuation of less than 5% is ignored. This is because Unity is bad at joysticks and ignores deadzones. + +** RCS Effects ** +Currently disabled pending rework. + + +INSTALLATION: +unzip to GameData + +LICENSE remains the ialdabaoth license (CC-BY-SA + tweaks). +SOURCE is https://github.com/NathanKell/ModuleRCSFX + +CHANGELOG +v4.2 +* Fixed bug where inputs were being improperly normalized (thanks Starwaster!) +* Fixed a bug in applying useThrottle (was being done in global Y not local Y; ditto). + +v4.1 +* Fixed bug in thrust calculation (was 2% what it should be...) +* When in fullThrust mode, don't switch to full thrust unless thrust ratio already starts out at fullThrustMin (configurable). + +v4.0 +* Update to KSP 1.0. +* Speed improvements. (Death to foreach! Don't recalculate values!) +* Removed EFFECTS support for now (doesn't work). +* Easy integration with TestFlight. +* Added useLever, tunable precision mode thrust, tweaked EPSILON support. + +v3.5 +*Fix enable/disable functionality. +*Fix non-PROPELLANT RCS. +*Change how thrust scaling works: now thrust is scaled by thrusterPower correctly (I trust), and precision mode is always "10% thrust" rather than varying based on placement. +*Made rotatation/linear restrictions toggleable in the VAB. + +v3.4 +*Add control clamping +*Update to 0.90 + +v3.3 +*Remove debug spam + +v3.2 +*Fix more stock RCS bugs (no longer capped at min 100N thrust). + +v3.1 +*Recompile for KSP 0.25 + +v3.0 +*Fixed issues below 750m/s (thanks chicknblender!) +*Added EFFECTS support back in +*Added fullThrust as an option. + +v2.x +*Fixed for .24 +*Added the new fields +*Thrust correction by default. \ No newline at end of file diff --git a/Plugins/ModuleRCSFX.dll b/Plugins/ModuleRCSFX.dll deleted file mode 100644 index 8893e7c..0000000 Binary files a/Plugins/ModuleRCSFX.dll and /dev/null differ diff --git a/Plugins/Source/ModuleRCSFX.cs b/Plugins/Source/ModuleRCSFX.cs deleted file mode 100644 index bca5e05..0000000 --- a/Plugins/Source/ModuleRCSFX.cs +++ /dev/null @@ -1,223 +0,0 @@ -using System; -using System.Collections.Generic; -using UnityEngine; -using KSP; - -public class ModuleRCSFX : ModuleRCS -{ - [KSPField()] - string runningEffectName = "running_closed"; - - [KSPField()] - string engageEffectName = "engage"; - - [KSPField()] - string flameoutEffectName = "flameout"; - - [KSPField()] - public bool useZaxis; - - [KSPField()] - public bool rcs_active; - - public float mixtureFactor; - - private List _props; - public List propellants - { - get - { - if (_props == null) - _props = new List(); - return _props; - } - - } - - public override void OnLoad(ConfigNode node) - { - ModuleRCSFX prefab = null; - if (part != null && part.partInfo != null) - prefab = (ModuleRCSFX)part.partInfo.partPrefab.Modules["ModuleRCSFX"]; - - if (prefab != null && prefab.propellants != null) - _props = prefab.propellants; - else - LoadPropellants(node); - } - - public override void OnStart(StartState state) - { - base.OnStart(state); - SetupPropellant(); - } - - public float getMaxFuelFlow(Propellant p) - { - return p.ratio * this.thrusterPower / (atmosphereCurve.Evaluate(0f) * 9.82f) / this.resourceMass; - } - - public override string GetInfo() - { - string info = "Thruster Power: " + thrusterPower.ToString("F") + "\n"; - info += "Thruster ISP: " + atmosphereCurve.Evaluate(1f).ToString("F0") - + " (ASL) - " + atmosphereCurve.Evaluate(0f).ToString("F0") + "(Vac)\n"; - info += "Requires: \n"; - foreach (Propellant p in propellants) - info += "" + p.name + ": " + getMaxFuelFlow(p).ToString("F4") + "/sec. Max. \n"; - return info; - } - - Vector3 inputLinear; - Vector3 inputAngular; - bool precision; - - new public void Update() - { - if (this.part.vessel == null) - return; - - inputLinear = vessel.ReferenceTransform.rotation * new Vector3(vessel.ctrlState.X, vessel.ctrlState.Z, vessel.ctrlState.Y); - inputAngular = vessel.ReferenceTransform.rotation * new Vector3(vessel.ctrlState.pitch, vessel.ctrlState.roll, vessel.ctrlState.yaw); - precision = FlightInputHandler.fetch.precisionMode; - } - - new public void FixedUpdate() - { - if (HighLogic.LoadedSceneIsEditor) - return; - - if (TimeWarp.CurrentRate > 1.0f && TimeWarp.WarpMode == TimeWarp.Modes.HIGH) - { - foreach (FXGroup fx in thrusterFX) - { - fx.setActive(false); - fx.Power = 0f; - } - return; - } - - - realISP = atmosphereCurve.Evaluate((float)vessel.staticPressure); - thrustForces.Clear(); - if (isEnabled && part.isControllable) - { - if (vessel.ActionGroups[KSPActionGroup.RCS] != rcs_active) - { - rcs_active = vessel.ActionGroups[KSPActionGroup.RCS]; - part.Effect(engageEffectName, 1.0f); - part.Effect(runningEffectName, 0f); - foreach (FXGroup fx in thrusterFX) - { - fx.setActive(false); - fx.Power = 0f; - } - } - if (vessel.ActionGroups[KSPActionGroup.RCS]) - { - Vector3 CoM = vessel.CoM + vessel.rb_velocity * Time.deltaTime; - - float effectPower = 0f; - for (int i = 0; i < thrusterTransforms.Count; i++) - { - if (thrusterTransforms[i].position != Vector3.zero) - { - Vector3 torque = Vector3.Cross(inputAngular, (thrusterTransforms[i].position - CoM).normalized); - Vector3 thruster; - if (useZaxis) - thruster = thrusterTransforms[i].forward; - else - thruster = thrusterTransforms[i].up; - float thrust = Mathf.Max(Vector3.Dot(thruster, torque), 0f); - thrust += Mathf.Max(Vector3.Dot(thruster, inputLinear), 0f); - if (thrust > 0.0001f) - { - if (precision) - { - float arm = GetLeverDistance(-thruster, CoM); - if (arm > 1.0f) - thrust = thrust / arm; - } - thrust = Mathf.Clamp(thrust, 0f, 1f); - thrustForces.Add(thrust); - - if (!isJustForShow) - { - - thrust = FuelLimitedThrust(thrust); - - Vector3 force = (-thrusterPower * thrust) * thruster; - Vector3 position = thrusterTransforms[i].transform.position; - part.Rigidbody.AddForceAtPosition(force, position, ForceMode.Force); - } - - thrusterFX[i].Power = Mathf.Clamp(thrust, 0.1f, 1f); - if (effectPower < thrusterFX[i].Power) - effectPower = thrusterFX[i].Power; - thrusterFX[i].setActive(thrust > 0f); - } - else - { - thrusterFX[i].setActive(false); - thrusterFX[i].Power = 0f; - } - } - } - part.Effect(runningEffectName, effectPower); - } - } - else - { - foreach (FXGroup fx in thrusterFX) - { - fx.setActive(false); - fx.Power = 0f; - } - } - - } - - public void LoadPropellants(ConfigNode node) - { - propellants.Clear(); - foreach (ConfigNode prop in node.GetNodes("PROPELLANT")) - { - Propellant fuel = new Propellant(); - fuel.Load(prop); - propellants.Add(fuel); - } - SetupPropellant(); - } - - public void SetupPropellant() - { - mixtureFactor = 0.0f; - resourceMass = 0.0f; - foreach (Propellant fuel in propellants) - { - mixtureFactor += fuel.ratio; - resourceMass += fuel.ratio * PartResourceLibrary.Instance.GetDefinition(fuel.name).density; - } - - } - - public float FuelLimitedThrust(float thrust) - { - if (CheatOptions.InfiniteRCS) - return thrust; - foreach (Propellant propellant in propellants) - { - - float fuelAmount = (thrust * Time.deltaTime) / (this.resourceMass * realISP * 9.82f); - float requestedAmount = fuelAmount * propellant.ratio; - float actualAmount = part.RequestResource(propellant.id, requestedAmount); - if (actualAmount < requestedAmount) - { - thrust *= actualAmount / requestedAmount; - part.Effect(flameoutEffectName, 1.0f); - } - } - return thrust; - } - -} diff --git a/Plugins/Source/DeadlyReentry.userprefs b/Source/DeadlyReentry.userprefs similarity index 100% rename from Plugins/Source/DeadlyReentry.userprefs rename to Source/DeadlyReentry.userprefs diff --git a/Source/ModuleRCSFX.cs b/Source/ModuleRCSFX.cs new file mode 100644 index 0000000..2f2a035 --- /dev/null +++ b/Source/ModuleRCSFX.cs @@ -0,0 +1,357 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using KSP; + +public class ModuleRCSFX : ModuleRCS +{ + /// + /// Always use the full thrust of the thruster, don't decrease it when off-alignment + /// + [KSPField] + public bool fullThrust = false; // always use full thrust + + /// + /// If fullThrust = true, if thrust ratio is < this, do not apply full thrust (leave thrust unchanged) + /// + [KSPField] + public float fullThrustMin = 0.2f; // if thrust amount from dots < this, don't do full thrust + + [KSPField] + public bool useEffects = false; + + [KSPField] + string runningEffectName = ""; + [KSPField] + string engageEffectName = ""; + [KSPField] + string disengageEffectName = ""; + [KSPField] + string flameoutEffectName = ""; + + public bool rcs_active; + + [KSPField] + public bool useZaxis = false; + + [KSPField(guiActiveEditor = true)] + public string RCS = "Enable/Disable for:"; + + [KSPField(isPersistant = true, guiActive = false, guiActiveEditor = true, guiName = "Yaw"), + UI_Toggle(disabledText = "Off", enabledText = "On")] + public bool enableYaw = true; + [KSPField(isPersistant = true, guiActive = false, guiActiveEditor = true, guiName = "Pitch"), + UI_Toggle(disabledText = "Off", enabledText = "On")] + public bool enablePitch = true; + [KSPField(isPersistant = true, guiActive = false, guiActiveEditor = true, guiName = "Roll"), + UI_Toggle(disabledText = "Off", enabledText = "On")] + public bool enableRoll = true; + + [KSPField(isPersistant = true, guiActive = false, guiActiveEditor = true, guiName = "Port/Stbd"), + UI_Toggle(disabledText = "Off", enabledText = "On")] + public bool enableX = true; + [KSPField(isPersistant = true, guiActive = false, guiActiveEditor = true, guiName = "Dorsal/Ventral"), + UI_Toggle(disabledText = "Off", enabledText = "On")] + public bool enableY = true; + [KSPField(isPersistant = true, guiActive = false, guiActiveEditor = true, guiName = "Fore/Aft"), + UI_Toggle(disabledText = "Off", enabledText = "On")] + public bool enableZ = true; + + [KSPField] + public bool useThrottle = false; + + /// + /// Stock KSP lever compensation in precision mode (instead of just reduced thrsut + /// Defaults to false (reduce thrust uniformly + /// + [KSPField] + public bool useLever = false; + + /// + /// The factor by which thrust is multiplied in precision mode (if lever compensation is off + /// + [KSPField] + public float precisionFactor = 0.1f; + + //[KSPField(guiActive = true)] + public float curThrust = 0f; + + /// + /// Fuel flow in tonnes/sec + /// + public double fuelFlow = 0f; + + + //[KSPField(guiActive = true)] + float inputAngularX; + //[KSPField(guiActive = true)] + float inputAngularY; + //[KSPField(guiActive = true)] + float inputAngularZ; + //[KSPField(guiActive = true)] + float inputLinearX; + //[KSPField(guiActive = true)] + float inputLinearY; + //[KSPField(guiActive = true)] + float inputLinearZ; + + private Vector3 inputLinear; + private Vector3 inputAngular; + private bool precision; + private double exhaustVel = 0d; + + public double flowMult = 1d; + public double ispMult = 1d; + + private double invG = 1d / 9.80665d; + + /// + /// If control actuation < this, ignore. + /// + [KSPField] + float EPSILON = 0.05f; // 5% control actuation + + public float mixtureFactor; + + public override void OnLoad(ConfigNode node) + { + if (!node.HasNode("PROPELLANT") && node.HasValue("resourceName")) + { + ConfigNode c = new ConfigNode("PROPELLANT"); + c.AddValue("name", node.GetValue("resourceName")); + c.AddValue("ratio", "1.0"); + if (node.HasValue("resourceFlowMode")) + c.AddValue("resourceFlowMode", node.GetValue("resourceFlowMode")); + node.AddNode(c); + } + base.OnLoad(node); + G = 9.80665f; + fuelFlow = (double)thrusterPower / (double)atmosphereCurve.Evaluate(0f) * invG; + } + + public override string GetInfo() + { + string text = base.GetInfo(); + return text; + } + + public override void OnStart(StartState state) + { + if (useEffects) // use EFFECTS so don't do the base startup. That means we have to do this ourselves. + { + part.stackIcon.SetIcon(DefaultIcons.RCS_MODULE); + part.stackIconGrouping = StackIconGrouping.SAME_TYPE; + thrusterTransforms = new List(part.FindModelTransforms(thrusterTransformName)); + if (thrusterTransforms == null || thrusterTransforms.Count == 0) + { + Debug.Log("RCS module unable to find any transforms in part named " + thrusterTransformName); + } + + } + else + base.OnStart(state); + } + + new public void Update() + { + if (this.part.vessel == null) + return; + + float ctrlZ = vessel.ctrlState.Z; + if (useThrottle && ctrlZ < EPSILON && ctrlZ > -EPSILON) // only do this if not specifying axial thrust. + { + ctrlZ -= vessel.ctrlState.mainThrottle; + ctrlZ = Mathf.Clamp(ctrlZ, -1f, 1f); + } + inputLinear = vessel.ReferenceTransform.rotation * new Vector3(enableX ? vessel.ctrlState.X : 0f, enableZ ? ctrlZ : 0f, enableY ? vessel.ctrlState.Y : 0f); + inputAngular = vessel.ReferenceTransform.rotation * new Vector3(enablePitch ? vessel.ctrlState.pitch : 0f, enableRoll ? vessel.ctrlState.roll : 0f, enableYaw ? vessel.ctrlState.yaw : 0); + + // Epsilon checks (min values) + float EPSILON2 = EPSILON * EPSILON; + inputAngularX = inputAngular.x; + inputAngularY = inputAngular.y; + inputAngularZ = inputAngular.z; + inputLinearX = inputLinear.x; + inputLinearY = inputLinear.y; + inputLinearZ = inputLinear.z; + if (inputAngularX * inputAngularX < EPSILON2) + inputAngularX = 0f; + if (inputAngularY * inputAngularY < EPSILON2) + inputAngularY = 0f; + if (inputAngularZ * inputAngularZ < EPSILON2) + inputAngularZ = 0f; + if (inputLinearX * inputLinearX < EPSILON2) + inputLinearX = 0f; + if (inputLinearY * inputLinearY < EPSILON2) + inputLinearY = 0f; + if (inputLinearZ * inputLinearZ < EPSILON2) + inputLinearZ = 0f; + inputLinear.x = inputLinearX; + inputLinear.y = inputLinearY; + inputLinear.z = inputLinearZ; + inputAngular.x = inputAngularX; + inputAngular.y = inputAngularY; + inputAngular.z = inputAngularZ; + + precision = FlightInputHandler.fetch.precisionMode; + } + + new public void FixedUpdate() + { + if (HighLogic.LoadedSceneIsEditor) + return; + int fxC = thrusterFX.Count; + if (TimeWarp.CurrentRate > 1.0f && TimeWarp.WarpMode == TimeWarp.Modes.HIGH) + { + + for (int i = 0; i < fxC; ++i) + { + FXGroup fx = thrusterFX[i]; + fx.setActive(false); + fx.Power = 0f; + } + return; + } + + // set starting params for loop + bool success = false; + curThrust = 0f; + + // set Isp/EV + realISP = atmosphereCurve.Evaluate((float)(vessel.staticPressurekPa * PhysicsGlobals.KpaToAtmospheres)); + exhaustVel = (double)realISP * (double)G * ispMult; + + thrustForces.Clear(); + + if (rcsEnabled && !part.ShieldedFromAirstream) + { + if (vessel.ActionGroups[KSPActionGroup.RCS] != rcs_active) + { + rcs_active = vessel.ActionGroups[KSPActionGroup.RCS]; + } + if (vessel.ActionGroups[KSPActionGroup.RCS] && (inputAngular != Vector3.zero || inputLinear != Vector3.zero)) + { + + // rb_velocity should include timewarp, right? + Vector3 CoM = vessel.CoM + vessel.rb_velocity * Time.fixedDeltaTime; + + float effectPower = 0f; + int xformCount = thrusterTransforms.Count; + for (int i = 0; i < xformCount; ++i) + { + Transform xform = thrusterTransforms[i]; + if (xform.position != Vector3.zero) + { + Vector3 position = xform.position; + Vector3 torque = Vector3.Cross(inputAngular, (position - CoM).normalized); + + Vector3 thruster; + if (useZaxis) + thruster = xform.forward; + else + thruster = xform.up; + float thrust = Mathf.Max(Vector3.Dot(thruster, torque), 0f); + thrust += Mathf.Max(Vector3.Dot(thruster, inputLinear), 0f); + + // thrust should now be normalized 0-1. + + if (thrust > 0f) + { + if (fullThrust && thrust >= fullThrustMin) + thrust = 1f; + + if (precision) + { + if (useLever) + { + //leverDistance = GetLeverDistanceOriginal(predictedCOM); + float leverDistance = GetLeverDistance(-thruster, CoM); + + if (leverDistance > 1) + { + thrust /= leverDistance; + } + } + else + { + thrust *= precisionFactor; + } + } + + UpdatePropellantStatus(); + float thrustForce = CalculateThrust(thrust, out success); + + if (success) + { + curThrust += thrustForce; + thrustForces.Add(thrustForce); + if (!isJustForShow) + { + Vector3 force = -thrustForce * thruster; + + part.Rigidbody.AddForceAtPosition(force, position, ForceMode.Force); + //Debug.Log("Part " + part.name + " adding force " + force.x + "," + force.y + "," + force.z + " at " + position); + } + + thrusterFX[i].Power = Mathf.Clamp(thrust, 0.1f, 1f); + if (effectPower < thrusterFX[i].Power) + effectPower = thrusterFX[i].Power; + thrusterFX[i].setActive(thrustForce > 0f); + } + else + { + thrusterFX[i].Power = 0f; + + /*if (!(flameoutEffectName.Equals(""))) + part.Effect(flameoutEffectName, 1.0f);*/ + } + } + else + { + thrusterFX[i].Power = 0f; + } + } + } + /*if(!(runningEffectName.Equals(""))) + part.Effect(runningEffectName, effectPower);*/ + } + } + if (!success) + { + for (int i = 0; i < fxC; ++i) + { + FXGroup fx = thrusterFX[i]; + fx.setActive(false); + fx.Power = 0f; + } + } + + } + + private void UpdatePropellantStatus() + { + if ((object)propellants != null) + { + int pCount = propellants.Count; + for (int i = 0; i < pCount; ++i) + propellants[i].UpdateConnectedResources(part); + } + } + + new public float CalculateThrust(float totalForce, out bool success) + { + double massFlow = flowMult * fuelFlow * (double)totalForce; + + double propAvailable = 1.0d; + + if (!CheatOptions.InfiniteRCS) + propAvailable = RequestPropellant(massFlow * TimeWarp.fixedDeltaTime); + + totalForce = (float)(massFlow * exhaustVel * propAvailable); + + success = (propAvailable > 0f); // had some fuel + return totalForce; + } + +} + diff --git a/Plugins/Source/ModuleRCSFX.csproj b/Source/ModuleRCSFX.csproj similarity index 73% rename from Plugins/Source/ModuleRCSFX.csproj rename to Source/ModuleRCSFX.csproj index 99a370a..a259707 100644 --- a/Plugins/Source/ModuleRCSFX.csproj +++ b/Source/ModuleRCSFX.csproj @@ -14,19 +14,20 @@ 512 - True - full + false + none False .. - DEBUG;TRACE + DEBUG prompt 4 - pdbonly + none True - .. - TRACE + ..\ModuleRCSFX\Plugins\ + + prompt 4 @@ -36,20 +37,21 @@ - ..\..\..\..\KSP_Data\Managed\Assembly-CSharp.dll + ..\..\..\..\..\..\Games\KSP_win1\KSP_Data\Managed\Assembly-CSharp.dll False - ..\..\..\..\KSP_Data\Managed\UnityEngine.dll + ..\..\..\..\..\..\Games\KSP_win1\KSP_Data\Managed\UnityEngine.dll False - rd /s /q Source\bin -rd /s /q Source\obj -del *.pdb -del "System.Core.dll" + rem rd /s /q Source\bin +rem rd /s /q Source\obj +rem del "System.Core.dll" +xcopy /y ModuleRCSFX.dll ..\..\..\RealismOverhaul\GameData\ModuleRCSFX\Plugins\* +xcopy /y ..\Readme_MRCSFX.txt ..\..\..\RealismOverhaul\GameData\ModuleRCSFX\*