using NewHorizons.Builder.Orbital; using NewHorizons.External; using NewHorizons.OrbitalPhysics; using OWML.Utils; using System; using System.Collections.Generic; using System.Reflection; using UnityEngine; using NewHorizons.Utility; using Logger = NewHorizons.Utility.Logger; namespace NewHorizons.Builder.General { static class DetectorBuilder { public static void Make(GameObject body, OWRigidbody OWRB, AstroObject primaryBody, AstroObject astroObject) { GameObject detectorGO = new GameObject("FieldDetector"); detectorGO.SetActive(false); detectorGO.transform.parent = body.transform; detectorGO.transform.localPosition = Vector3.zero; detectorGO.layer = 20; ConstantForceDetector forceDetector = detectorGO.AddComponent(); forceDetector.SetValue("_inheritElement0", true); OWRB.RegisterAttachedForceDetector(forceDetector); GravityVolume parentGravityVolume = primaryBody.GetAttachedOWRigidbody().GetAttachedGravityVolume(); if (parentGravityVolume != null) { forceDetector.SetValue("_detectableFields", new ForceVolume[] { parentGravityVolume }); } else { // It's probably a focal point (or its just broken) var binaryFocalPoint = primaryBody.gameObject.GetComponent(); if(binaryFocalPoint != null) { if(astroObject.GetCustomName().Equals(binaryFocalPoint.PrimaryName)) { binaryFocalPoint.Primary = astroObject; if (binaryFocalPoint.Secondary != null) { var secondaryRB = binaryFocalPoint.Secondary.GetAttachedOWRigidbody(); SetBinaryForceDetectableFields(binaryFocalPoint, forceDetector, secondaryRB.GetAttachedForceDetector(), OWRB, secondaryRB); } } else if (astroObject.GetCustomName().Equals(binaryFocalPoint.SecondaryName)) { binaryFocalPoint.Secondary = astroObject; if (binaryFocalPoint.Primary != null) { var primaryRB = binaryFocalPoint.Primary.GetAttachedOWRigidbody(); SetBinaryForceDetectableFields(binaryFocalPoint, primaryRB.GetAttachedForceDetector(), forceDetector, primaryRB, OWRB); } } else { // It's a planet binaryFocalPoint.Planets.Add(astroObject); if(binaryFocalPoint.Primary != null && binaryFocalPoint.Secondary != null) { var primaryGravityVolume = binaryFocalPoint.Primary.GetGravityVolume(); var secondaryGravityVolume = binaryFocalPoint.Secondary.GetGravityVolume(); forceDetector.SetValue("_detectableFields", new ForceVolume[] { primaryGravityVolume, secondaryGravityVolume }); } } } } detectorGO.SetActive(true); } private static void SetBinaryForceDetectableFields(BinaryFocalPoint point, ForceDetector primaryCFD, ForceDetector secondaryCFD, OWRigidbody primaryRB, OWRigidbody secondaryRB) { Logger.Log($"Setting up binary focal point for {point.name}"); var primary = point.Primary; var secondary = point.Secondary; var planets = point.Planets; // Binaries have to use the same gravity exponent var primaryGV = primary.GetGravityVolume(); var secondaryGV = secondary.GetGravityVolume(); if (primaryGV.GetFalloffType() != secondaryGV.GetFalloffType()) { Logger.LogError($"Binaries must have the same gravity falloff! {primaryGV.GetFalloffType()} != {secondaryGV.GetFalloffType()}"); return; } // Have to use fall off type rn instead of exponent since it the exponent doesnt update until the Awake method I think var exponent = 0f; if (primaryGV.GetFalloffType() == OrbitalHelper.FalloffType.linear) exponent = 1f; if (primaryGV.GetFalloffType() == OrbitalHelper.FalloffType.inverseSquared) exponent = 2f; // Very specific distance between them Vector3 separation = primary.transform.position - secondary.transform.position; var Gm1 = primaryGV.GetStandardGravitationalParameter(); var Gm2 = secondaryGV.GetStandardGravitationalParameter(); // One of the two is zero if it just loaded if (Gm1 == 0) Gm1 = GravityVolume.GRAVITATIONAL_CONSTANT * primaryGV.GetValue("_surfaceAcceleration") * Mathf.Pow(primaryGV.GetValue("_upperSurfaceRadius"), exponent) / 0.001f; if (Gm2 == 0) Gm2 = GravityVolume.GRAVITATIONAL_CONSTANT * secondaryGV.GetValue("_surfaceAcceleration") * Mathf.Pow(secondaryGV.GetValue("_upperSurfaceRadius"), exponent) / 0.001f; float r1 = separation.magnitude * Gm2 / (Gm1 + Gm2); float r2 = separation.magnitude * Gm1 / (Gm1 + Gm2); primary.transform.position = point.transform.position + r1 * separation.normalized; secondary.transform.position = point.transform.position - r2 * separation.normalized; Logger.Log($"{primary.transform.position}, {secondary.transform.position}, {point.transform.position}"); // Set detectable fields primaryCFD.SetValue("_detectableFields", new ForceVolume[] { secondaryGV }); primaryCFD.SetValue("_inheritDetector", point.GetAttachedOWRigidbody().GetAttachedForceDetector()); primaryCFD.SetValue("_activeInheritedDetector", point.GetAttachedOWRigidbody().GetAttachedForceDetector()); primaryCFD.SetValue("_inheritElement0", false); secondaryCFD.SetValue("_detectableFields", new ForceVolume[] { primaryGV }); secondaryCFD.SetValue("_inheritDetector", point.GetAttachedOWRigidbody().GetAttachedForceDetector()); secondaryCFD.SetValue("_activeInheritedDetector", point.GetAttachedOWRigidbody().GetAttachedForceDetector()); secondaryCFD.SetValue("_inheritElement0", false); // Update speeds var direction = Vector3.Cross(separation, Vector3.up).normalized; var m1 = Gm1 / GravityVolume.GRAVITATIONAL_CONSTANT; var m2 = Gm2 / GravityVolume.GRAVITATIONAL_CONSTANT; var reducedMass = m1 * m2 / (m1 + m2); var totalMass = m1 + m2; // They must have the same eccentricity var parameterizedAstroObject = primary.GetComponent(); var parameterizedAstroObject2 = secondary.GetComponent(); float ecc = 0; float i = 0; float l = 0; float p = 0; if (parameterizedAstroObject != null) { ecc = parameterizedAstroObject.keplerElements.Eccentricity; i = parameterizedAstroObject.keplerElements.Inclination; l = parameterizedAstroObject.keplerElements.LongitudeOfAscendingNode; p = parameterizedAstroObject.keplerElements.ArgumentOfPeriapsis; } var r = separation.magnitude; // Start them off at their periapsis var primaryKeplerElements = KeplerElements.FromTrueAnomaly(ecc, r1 / (1f - ecc), i, l, p, 0); var secondaryKeplerElements = KeplerElements.FromTrueAnomaly(ecc, r2 / (1f - ecc), i, l, p, 180); // Maybe we'll need these orbital parameters later if(parameterizedAstroObject != null) parameterizedAstroObject.keplerElements = primaryKeplerElements; if(parameterizedAstroObject2 != null) parameterizedAstroObject2.keplerElements = secondaryKeplerElements; // Finally we update the speeds var b = r * Mathf.Sqrt(1 - ecc * ecc); float v = Mathf.Sqrt(GravityVolume.GRAVITATIONAL_CONSTANT * totalMass * (1 - ecc * ecc) / Mathf.Pow(r, exponent - 1)); var v2 = v / (1f + (m2 / m2)); var v1 = v - v2; // Rotate around argument of periapsis var periapsisRotation = Quaternion.AngleAxis(p, Vector3.up); var ascendingAxis = Quaternion.AngleAxis(p + l, Vector3.up) * Vector3.right; var longitudeRotation = Quaternion.AngleAxis(i, ascendingAxis); //direction = periapsisRotation * longitudeRotation * direction; //Do this next tick for... reasons? Main.Instance.ModHelper.Events.Unity.FireOnNextUpdate(() => primaryRB.SetVelocity(direction * v1)); Main.Instance.ModHelper.Events.Unity.FireOnNextUpdate(() => secondaryRB.SetVelocity(direction * -v2)); // If they have tracking orbits set the period var period = 2 * Mathf.PI * Mathf.Sqrt(Mathf.Pow(r, exponent + 1) / (GravityVolume.GRAVITATIONAL_CONSTANT * totalMass)); // Only one of these won't be null, the other one gets done next tick var trackingOrbitPrimary = primary.GetComponentInChildren(); if (trackingOrbitPrimary != null) { trackingOrbitPrimary.TrailTime = period; Main.Instance.ModHelper.Events.Unity.FireInNUpdates(() => trackingOrbitPrimary.ResetLineVertices(), 15); } var trackingOrbitSecondary = secondary.GetComponentInChildren(); if (trackingOrbitSecondary != null) { trackingOrbitSecondary.TrailTime = period; Main.Instance.ModHelper.Events.Unity.FireInNUpdates(() => trackingOrbitSecondary.ResetLineVertices(), 15); } } } }