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" ],