diff --git a/NewHorizons/Patches/RigidbodyPatches.cs b/NewHorizons/Patches/RigidbodyPatches.cs
new file mode 100644
index 00000000..50a0b8d0
--- /dev/null
+++ b/NewHorizons/Patches/RigidbodyPatches.cs
@@ -0,0 +1,190 @@
+using HarmonyLib;
+using NewHorizons.Utility;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace NewHorizons.Patches;
+
+///
+/// From QSB
+///
+[HarmonyPatch(typeof(OWRigidbody))]
+public static class OWRigidbodyPatches
+{
+ private static readonly Dictionary _setParentQueue = new();
+
+ [HarmonyPrefix]
+ [HarmonyPatch(nameof(OWRigidbody.Awake))]
+ private static bool Awake(OWRigidbody __instance)
+ {
+ __instance._transform = __instance.transform;
+
+ if (!__instance._scaleRoot)
+ {
+ __instance._scaleRoot = __instance._transform;
+ }
+
+ CenterOfTheUniverse.TrackRigidbody(__instance);
+ __instance._offsetApplier = __instance.gameObject.GetAddComponent();
+ __instance._offsetApplier.Init(__instance);
+ if (__instance._simulateInSector)
+ {
+ __instance._simulateInSector.OnSectorOccupantsUpdated += __instance.OnSectorOccupantsUpdated;
+ }
+
+ __instance._origParent = __instance._transform.parent;
+ __instance._origParentBody = __instance._origParent ? __instance._origParent.GetAttachedOWRigidbody() : null;
+ if (__instance._transform.parent)
+ {
+ _setParentQueue[__instance] = null;
+ }
+
+ __instance._rigidbody = __instance.GetRequiredComponent();
+ __instance._rigidbody.interpolation = RigidbodyInterpolation.None;
+ if (!__instance._autoGenerateCenterOfMass)
+ {
+ __instance._rigidbody.centerOfMass = __instance._centerOfMass;
+ }
+
+ if (__instance.IsSimulatedKinematic())
+ {
+ __instance.EnableKinematicSimulation();
+ }
+
+ __instance._origCenterOfMass = __instance.RunningKinematicSimulation() ? __instance._kinematicRigidbody.centerOfMass : __instance._rigidbody.centerOfMass;
+ __instance._referenceFrame = new ReferenceFrame(__instance);
+ return false;
+ }
+
+ [HarmonyPrefix]
+ [HarmonyPatch(nameof(OWRigidbody.Start))]
+ private static void Start(OWRigidbody __instance)
+ {
+ if (_setParentQueue.TryGetValue(__instance, out var parent))
+ {
+ __instance._transform.parent = parent;
+ _setParentQueue.Remove(__instance);
+ }
+ }
+
+ [HarmonyPrefix]
+ [HarmonyPatch(nameof(OWRigidbody.OnDestroy))]
+ private static void OnDestroy(OWRigidbody __instance)
+ {
+ _setParentQueue.Remove(__instance);
+ }
+
+ [HarmonyPrefix]
+ [HarmonyPatch(nameof(OWRigidbody.Suspend), typeof(Transform), typeof(OWRigidbody))]
+ private static bool Suspend(OWRigidbody __instance, Transform suspensionParent, OWRigidbody suspensionBody)
+ {
+ if (!__instance._suspended || __instance._unsuspendNextUpdate)
+ {
+ __instance._suspensionBody = suspensionBody;
+ var direction = __instance.GetVelocity() - suspensionBody.GetPointVelocity(__instance._transform.position);
+ __instance._cachedRelativeVelocity = suspensionBody.transform.InverseTransformDirection(direction);
+ __instance._cachedAngularVelocity = __instance.RunningKinematicSimulation() ? __instance._kinematicRigidbody.angularVelocity : __instance._rigidbody.angularVelocity;
+ __instance.enabled = false;
+ __instance._offsetApplier.enabled = false;
+ if (__instance.RunningKinematicSimulation())
+ {
+ __instance._kinematicRigidbody.enabled = false;
+ }
+ else
+ {
+ __instance.MakeKinematic();
+ }
+
+ if (_setParentQueue.ContainsKey(__instance))
+ {
+ _setParentQueue[__instance] = suspensionParent;
+ }
+ else
+ {
+ __instance._transform.parent = suspensionParent;
+ }
+
+ __instance._suspended = true;
+ __instance._unsuspendNextUpdate = false;
+ if (!Physics.autoSyncTransforms)
+ {
+ Physics.SyncTransforms();
+ }
+
+ if (__instance._childColliders == null)
+ {
+ __instance._childColliders = __instance.GetComponentsInChildren();
+ foreach (var childCollider in __instance._childColliders)
+ {
+ childCollider.gameObject.GetAddComponent().ListenForParentBodySuspension();
+ }
+ }
+
+ __instance.RaiseEvent(nameof(__instance.OnSuspendOWRigidbody), __instance);
+ }
+
+ return false;
+ }
+
+ [HarmonyPrefix]
+ [HarmonyPatch(nameof(OWRigidbody.ChangeSuspensionBody))]
+ private static bool ChangeSuspensionBody(OWRigidbody __instance, OWRigidbody newSuspensionBody)
+ {
+ if (__instance._suspended)
+ {
+ __instance._cachedRelativeVelocity = Vector3.zero;
+ __instance._suspensionBody = newSuspensionBody;
+ if (_setParentQueue.ContainsKey(__instance))
+ {
+ _setParentQueue[__instance] = newSuspensionBody.transform;
+ }
+ else
+ {
+ __instance._transform.parent = newSuspensionBody.transform;
+ }
+ }
+
+ return false;
+ }
+
+ [HarmonyPrefix]
+ [HarmonyPatch(nameof(OWRigidbody.UnsuspendImmediate))]
+ private static bool UnsuspendImmediate(OWRigidbody __instance, bool restoreCachedVelocity)
+ {
+ if (__instance._suspended)
+ {
+ if (__instance.RunningKinematicSimulation())
+ {
+ __instance._kinematicRigidbody.enabled = true;
+ }
+ else
+ {
+ __instance.MakeNonKinematic();
+ }
+
+ __instance.enabled = true;
+ if (_setParentQueue.ContainsKey(__instance))
+ {
+ _setParentQueue[__instance] = null;
+ }
+ else
+ {
+ __instance._transform.parent = null;
+ }
+
+ if (!Physics.autoSyncTransforms)
+ {
+ Physics.SyncTransforms();
+ }
+
+ var cachedVelocity = restoreCachedVelocity ? __instance._suspensionBody.transform.TransformDirection(__instance._cachedRelativeVelocity) : Vector3.zero;
+ __instance.SetVelocity(__instance._suspensionBody.GetPointVelocity(__instance._transform.position) + cachedVelocity);
+ __instance.SetAngularVelocity(restoreCachedVelocity ? __instance._cachedAngularVelocity : Vector3.zero);
+ __instance._suspended = false;
+ __instance._suspensionBody = null;
+ __instance.RaiseEvent(nameof(__instance.OnUnsuspendOWRigidbody), __instance);
+ }
+
+ return false;
+ }
+}
diff --git a/NewHorizons/Utility/NewHorizonExtensions.cs b/NewHorizons/Utility/NewHorizonExtensions.cs
index b292b06b..dd907c45 100644
--- a/NewHorizons/Utility/NewHorizonExtensions.cs
+++ b/NewHorizons/Utility/NewHorizonExtensions.cs
@@ -305,5 +305,39 @@ namespace NewHorizons.Utility
}
return curve;
}
+
+ // From QSB
+ public static void RaiseEvent(this T instance, string eventName, params object[] args)
+ {
+ const BindingFlags flags = BindingFlags.Instance
+ | BindingFlags.Static
+ | BindingFlags.Public
+ | BindingFlags.NonPublic
+ | BindingFlags.DeclaredOnly;
+ if (typeof(T)
+ .GetField(eventName, flags)?
+ .GetValue(instance) is not MulticastDelegate multiDelegate)
+ {
+ return;
+ }
+
+ multiDelegate.SafeInvoke(args);
+ }
+
+ // From QSB
+ public static void SafeInvoke(this MulticastDelegate multicast, params object[] args)
+ {
+ foreach (var del in multicast.GetInvocationList())
+ {
+ try
+ {
+ del.DynamicInvoke(args);
+ }
+ catch (TargetInvocationException ex)
+ {
+ NHLogger.LogError($"Error invoking delegate! {ex.InnerException}");
+ }
+ }
+ }
}
}
diff --git a/NewHorizons/manifest.json b/NewHorizons/manifest.json
index 52c18523..847b5dde 100644
--- a/NewHorizons/manifest.json
+++ b/NewHorizons/manifest.json
@@ -4,7 +4,7 @@
"author": "xen, Bwc9876, clay, MegaPiggy, John, Trifid, Hawkbar, Book",
"name": "New Horizons",
"uniqueName": "xen.NewHorizons",
- "version": "1.17.3",
+ "version": "1.17.4",
"owmlVersion": "2.9.8",
"dependencies": [ "JohnCorby.VanillaFix", "_nebula.MenuFramework", "xen.CommonCameraUtility", "dgarro.CustomShipLogModes" ],
"conflicts": [ "Raicuparta.QuantumSpaceBuddies", "PacificEngine.OW_CommonResources" ],