diff --git a/NewHorizons/Builder/Props/DetailBuilder.cs b/NewHorizons/Builder/Props/DetailBuilder.cs index 884ca25e..24baeb8c 100644 --- a/NewHorizons/Builder/Props/DetailBuilder.cs +++ b/NewHorizons/Builder/Props/DetailBuilder.cs @@ -1,4 +1,5 @@ using NewHorizons.Builder.General; +using NewHorizons.Components; using NewHorizons.External.Modules; using NewHorizons.Handlers; using NewHorizons.Utility; @@ -231,6 +232,14 @@ namespace NewHorizons.Builder.Props if (!detail.keepLoaded) GroupsBuilder.Make(prop, sector); prop.SetActive(true); + if (detail.hasPhysics) + { + var addPhysics = prop.AddComponent(); + addPhysics.Sector = sector; + addPhysics.Mass = detail.physicsMass; + addPhysics.Radius = detail.physicsRadius; + } + _detailInfoToCorrespondingSpawnedGameObject[detail] = prop; return prop; diff --git a/NewHorizons/Components/AddPhysics.cs b/NewHorizons/Components/AddPhysics.cs new file mode 100644 index 00000000..14785f9f --- /dev/null +++ b/NewHorizons/Components/AddPhysics.cs @@ -0,0 +1,76 @@ +using System.Collections; +using UnityEngine; + +namespace NewHorizons.Components; + +/// +/// properly add physics to a detail +/// +[DisallowMultipleComponent] +public class AddPhysics : MonoBehaviour +{ + [Tooltip("The sector that the rigidbody will be simulated in, or none for it to always be on.")] + public Sector Sector; + [Tooltip("The mass of the physics object.\n" + + "Most pushable props use the default value, which matches the player mass.")] + public float Mass = 0.001f; + [Tooltip("The radius that the added sphere collider will use for physics collision.\n" + + "If there's already good colliders on the detail, you can make this 0.")] + public float Radius = 1f; + + private IEnumerator Start() + { + yield return new WaitForSeconds(.1f); + + var parentBody = GetComponentInParent(); + + // hack: make all mesh colliders convex + // triggers are already convex + // prints errors for non readable meshes but whatever + foreach (var meshCollider in GetComponentsInChildren(true)) + meshCollider.convex = true; + + var bodyGo = new GameObject($"{name}_Body"); + bodyGo.SetActive(false); + bodyGo.transform.position = transform.position; + bodyGo.transform.rotation = transform.rotation; + + var owRigidbody = bodyGo.AddComponent(); + owRigidbody._simulateInSector = Sector; + + bodyGo.layer = LayerMask.NameToLayer("PhysicalDetector"); + bodyGo.tag = "DynamicPropDetector"; + // this collider is not included in groups. oh well + bodyGo.AddComponent().radius = Radius; + bodyGo.AddComponent(); + bodyGo.AddComponent(); + + var impactSensor = bodyGo.AddComponent(); + var audioSource = bodyGo.AddComponent(); + audioSource.maxDistance = 30; + audioSource.dopplerLevel = 0; + audioSource.rolloffMode = AudioRolloffMode.Custom; + audioSource.playOnAwake = false; + audioSource.spatialBlend = 1; + var owAudioSource = bodyGo.AddComponent(); + owAudioSource._audioSource = audioSource; + owAudioSource._track = OWAudioMixer.TrackName.Environment; + var objectImpactAudio = bodyGo.AddComponent(); + objectImpactAudio._minPitch = 0.4f; + objectImpactAudio._maxPitch = 0.6f; + objectImpactAudio._impactSensor = impactSensor; + + bodyGo.SetActive(true); + + transform.parent = bodyGo.transform; + owRigidbody.SetMass(Mass); + owRigidbody.SetVelocity(parentBody.GetPointVelocity(transform.position)); + + Destroy(this); + } + + private void OnDrawGizmosSelected() + { + Gizmos.DrawWireSphere(transform.position, Radius); + } +} \ No newline at end of file diff --git a/NewHorizons/External/Modules/PropModule.cs b/NewHorizons/External/Modules/PropModule.cs index 758b27ab..2fbf0f0a 100644 --- a/NewHorizons/External/Modules/PropModule.cs +++ b/NewHorizons/External/Modules/PropModule.cs @@ -229,6 +229,24 @@ namespace NewHorizons.External.Modules /// Should this detail stay loaded even if you're outside the sector (good for very large props) /// public bool keepLoaded; + + /// + /// Should this object dynamically move around? + /// This tries to make all mesh colliders convex, as well as adding a sphere collider in case the detail has no others. + /// + public bool hasPhysics; + + /// + /// The mass of the physics object. + /// Most pushable props use the default value, which matches the player mass. + /// + [DefaultValue(0.001f)] public float physicsMass = 0.001f; + + /// + /// The radius that the added sphere collider will use for physics collision. + /// If there's already good colliders on the detail, you can make this 0. + /// + [DefaultValue(1f)] public float physicsRadius = 1f; } [JsonObject] diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index ae6b6910..bc3099a1 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -1090,6 +1090,22 @@ "keepLoaded": { "type": "boolean", "description": "Should this detail stay loaded even if you're outside the sector (good for very large props)" + }, + "hasPhysics": { + "type": "boolean", + "description": "Should this object dynamically move around?\nThis tries to make all mesh colliders convex, as well as adding a sphere collider in case the detail has no others." + }, + "physicsMass": { + "type": "number", + "description": "The mass of the physics object.\nMost pushable props use the default value, which matches the player mass.", + "format": "float", + "default": 0.001 + }, + "physicsRadius": { + "type": "number", + "description": "The radius that the added sphere collider will use for physics collision.\nIf there's already good colliders on the detail, you can make this 0.", + "format": "float", + "default": 1.0 } } },