From 4a817980781b2ba795a917a3c1372c6e9f65b388 Mon Sep 17 00:00:00 2001 From: Joshua Thome Date: Sat, 22 Feb 2025 11:03:32 -0600 Subject: [PATCH] Add force volumes and give volumes rotation --- .../Builder/Volumes/ForceVolumeBuilder.cs | 119 ++++++++++++++++++ .../Builder/Volumes/VolumesBuildManager.cs | 38 ++++++ .../External/Modules/Volumes/ForceModule.cs | 40 ++++++ .../VolumeInfos/CylindricalForceVolumeInfo.cs | 24 ++++ .../VolumeInfos/DirectionalForceVolumeInfo.cs | 35 ++++++ .../Volumes/VolumeInfos/ForceVolumeInfo.cs | 40 ++++++ .../Volumes/VolumeInfos/GravityVolumeInfo.cs | 44 +++++++ .../VolumeInfos/PolarForceVolumeInfo.cs | 23 ++++ .../VolumeInfos/RadialForceVolumeInfo.cs | 32 +++++ .../Modules/Volumes/VolumeInfos/VolumeInfo.cs | 2 +- .../External/Modules/Volumes/VolumesModule.cs | 5 + 11 files changed, 401 insertions(+), 1 deletion(-) create mode 100644 NewHorizons/Builder/Volumes/ForceVolumeBuilder.cs create mode 100644 NewHorizons/External/Modules/Volumes/ForceModule.cs create mode 100644 NewHorizons/External/Modules/Volumes/VolumeInfos/CylindricalForceVolumeInfo.cs create mode 100644 NewHorizons/External/Modules/Volumes/VolumeInfos/DirectionalForceVolumeInfo.cs create mode 100644 NewHorizons/External/Modules/Volumes/VolumeInfos/ForceVolumeInfo.cs create mode 100644 NewHorizons/External/Modules/Volumes/VolumeInfos/GravityVolumeInfo.cs create mode 100644 NewHorizons/External/Modules/Volumes/VolumeInfos/PolarForceVolumeInfo.cs create mode 100644 NewHorizons/External/Modules/Volumes/VolumeInfos/RadialForceVolumeInfo.cs diff --git a/NewHorizons/Builder/Volumes/ForceVolumeBuilder.cs b/NewHorizons/Builder/Volumes/ForceVolumeBuilder.cs new file mode 100644 index 00000000..12e1f83f --- /dev/null +++ b/NewHorizons/Builder/Volumes/ForceVolumeBuilder.cs @@ -0,0 +1,119 @@ +using NewHorizons.Builder.Props; +using NewHorizons.External; +using NewHorizons.External.Modules; +using NewHorizons.External.Modules.Volumes.VolumeInfos; +using NewHorizons.Utility.OuterWilds; +using OWML.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace NewHorizons.Builder.Volumes +{ + public static class ForceVolumeBuilder + { + public static CylindricalForceVolume Make(GameObject planetGO, Sector sector, CylindricalForceVolumeInfo info) + { + var forceVolume = Make(planetGO, sector, info); + forceVolume._acceleration = info.force; + forceVolume._localAxis = info.normal ?? Vector3.up; + forceVolume._playGravityCrystalAudio = info.playGravityCrystalAudio; + forceVolume.gameObject.SetActive(true); + return forceVolume; + } + + public static DirectionalForceVolume Make(GameObject planetGO, Sector sector, DirectionalForceVolumeInfo info) + { + var forceVolume = Make(planetGO, sector, info); + forceVolume._fieldDirection = info.normal ?? Vector3.up; + forceVolume._fieldMagnitude = info.force; + forceVolume._affectsAlignment = info.affectsAlignment; + forceVolume._offsetCentripetalForce = info.offsetCentripetalForce; + forceVolume._playGravityCrystalAudio = info.playGravityCrystalAudio; + forceVolume.gameObject.SetActive(true); + return forceVolume; + } + + public static GravityVolume Make(GameObject planetGO, Sector sector, GravityVolumeInfo info) + { + var forceVolume = Make(planetGO, sector, info); + forceVolume._isPlanetGravityVolume = false; + forceVolume._setMass = false; + forceVolume._surfaceAcceleration = info.force; + forceVolume._upperSurfaceRadius = info.upperRadius; + forceVolume._lowerSurfaceRadius = info.lowerRadius; + forceVolume._cutoffAcceleration = info.minForce; + forceVolume._cutoffRadius = info.minRadius; + forceVolume._alignmentRadius = info.alignmentRadius ?? info.upperRadius * 1.5f; + forceVolume._falloffType = info.fallOff switch + { + GravityFallOff.Linear => GravityVolume.FalloffType.linear, + GravityFallOff.InverseSquared => GravityVolume.FalloffType.inverseSquared, + _ => throw new NotImplementedException(), + }; + forceVolume.gameObject.SetActive(true); + return forceVolume; + } + + public static PolarForceVolume Make(GameObject planetGO, Sector sector, PolarForceVolumeInfo info) + { + var forceVolume = Make(planetGO, sector, info); + forceVolume._acceleration = info.force; + forceVolume._localAxis = info.normal ?? Vector3.up; + forceVolume._fieldMode = info.tangential ? PolarForceVolume.ForceMode.Tangential : PolarForceVolume.ForceMode.Polar; + forceVolume.gameObject.SetActive(true); + return forceVolume; + } + + public static RadialForceVolume Make(GameObject planetGO, Sector sector, RadialForceVolumeInfo info) + { + var forceVolume = Make(planetGO, sector, info); + forceVolume._acceleration = info.force; + forceVolume._falloff = info.fallOff switch + { + RadialForceVolumeInfo.FallOff.Constant => RadialForceVolume.Falloff.Constant, + RadialForceVolumeInfo.FallOff.Linear => RadialForceVolume.Falloff.Linear, + RadialForceVolumeInfo.FallOff.InverseSquared => RadialForceVolume.Falloff.InvSqr, + _ => throw new NotImplementedException(), + }; + forceVolume.gameObject.SetActive(true); + return forceVolume; + } + + public static T Make(GameObject planetGO, Sector sector, ForceVolumeInfo info) where T : ForceVolume + { + var go = GeneralPropBuilder.MakeNew(typeof(T).Name, planetGO, sector, info); + go.layer = Layer.BasicEffectVolume; + + var owTriggerVolume = go.AddComponent(); + + if (info.shape != null) + { + var shapeOrCol = ShapeBuilder.AddShapeOrCollider(go, info.shape); + if (shapeOrCol is Shape shape) + owTriggerVolume._shape = shape; + else if (shapeOrCol is Collider col) + owTriggerVolume._owCollider = col.GetComponent(); + } + else + { + var col = go.AddComponent(); + col.radius = info.radius; + col.isTrigger = true; + var owCollider = go.GetAddComponent(); + + owTriggerVolume._owCollider = owCollider; + } + + var forceVolume = go.AddComponent(); + forceVolume._priority = info.priority; + forceVolume._alignmentPriority = info.alignmentPriority; + forceVolume._inheritable = info.inheritable; + + return forceVolume; + } + } +} diff --git a/NewHorizons/Builder/Volumes/VolumesBuildManager.cs b/NewHorizons/Builder/Volumes/VolumesBuildManager.cs index bdaa4a5a..acd48bf1 100644 --- a/NewHorizons/Builder/Volumes/VolumesBuildManager.cs +++ b/NewHorizons/Builder/Volumes/VolumesBuildManager.cs @@ -112,6 +112,44 @@ namespace NewHorizons.Builder.Volumes FluidVolumeBuilder.Make(go, sector, fluidVolume); } } + if (config.Volumes.forces != null) + { + if (config.Volumes.forces.cylindricalVolumes != null) + { + foreach (var cylindricalVolume in config.Volumes.forces.cylindricalVolumes) + { + ForceVolumeBuilder.Make(go, sector, cylindricalVolume); + } + } + if (config.Volumes.forces.directionalVolumes != null) + { + foreach (var directionalVolume in config.Volumes.forces.directionalVolumes) + { + ForceVolumeBuilder.Make(go, sector, directionalVolume); + } + } + if (config.Volumes.forces.gravityVolumes != null) + { + foreach (var gravityVolume in config.Volumes.forces.gravityVolumes) + { + ForceVolumeBuilder.Make(go, sector, gravityVolume); + } + } + if (config.Volumes.forces.polarVolumes != null) + { + foreach (var polarVolume in config.Volumes.forces.polarVolumes) + { + ForceVolumeBuilder.Make(go, sector, polarVolume); + } + } + if (config.Volumes.forces.radialVolumes != null) + { + foreach (var radialVolume in config.Volumes.forces.radialVolumes) + { + ForceVolumeBuilder.Make(go, sector, radialVolume); + } + } + } if (config.Volumes.probe != null) { if (config.Volumes.probe.destructionVolumes != null) diff --git a/NewHorizons/External/Modules/Volumes/ForceModule.cs b/NewHorizons/External/Modules/Volumes/ForceModule.cs new file mode 100644 index 00000000..8250d3e1 --- /dev/null +++ b/NewHorizons/External/Modules/Volumes/ForceModule.cs @@ -0,0 +1,40 @@ +using NewHorizons.External.Modules.Volumes.VolumeInfos; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NewHorizons.External.Modules.Volumes +{ + [JsonObject] + public class ForceModule + { + /// + /// Applies a constant force along the volume's XZ plane towards the volume's center. Affects alignment. + /// + public CylindricalForceVolumeInfo[] cylindricalVolumes; + + /// + /// Applies a constant force in the direction of the volume's Y axis. May affect alignment. + /// + public DirectionalForceVolumeInfo[] directionalVolumes; + + /// + /// Applies planet-like gravity towards the volume's center with falloff by distance. May affect alignment. + /// For actual planetary body gravity, use the properties in the Base module. + /// + public GravityVolumeInfo[] gravityVolumes; + + /// + /// Applies a constant force towards the volume's center. Affects alignment. + /// + public PolarForceVolumeInfo[] polarVolumes; + + /// + /// Applies a force towards the volume's center with falloff by distance. Affects alignment. + /// + public RadialForceVolumeInfo[] radialVolumes; + } +} diff --git a/NewHorizons/External/Modules/Volumes/VolumeInfos/CylindricalForceVolumeInfo.cs b/NewHorizons/External/Modules/Volumes/VolumeInfos/CylindricalForceVolumeInfo.cs new file mode 100644 index 00000000..5926056b --- /dev/null +++ b/NewHorizons/External/Modules/Volumes/VolumeInfos/CylindricalForceVolumeInfo.cs @@ -0,0 +1,24 @@ +using NewHorizons.External.SerializableData; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NewHorizons.External.Modules.Volumes.VolumeInfos +{ + [JsonObject] + public class CylindricalForceVolumeInfo : ForceVolumeInfo + { + /// + /// The direction that the force applied by this volume will be perpendicular to. Defaults to up (0, 1, 0). + /// + public MVector3 normal; + + /// + /// Whether to play the gravity crystal audio when the player is in this volume. + /// + public bool playGravityCrystalAudio; + } +} diff --git a/NewHorizons/External/Modules/Volumes/VolumeInfos/DirectionalForceVolumeInfo.cs b/NewHorizons/External/Modules/Volumes/VolumeInfos/DirectionalForceVolumeInfo.cs new file mode 100644 index 00000000..82af81d2 --- /dev/null +++ b/NewHorizons/External/Modules/Volumes/VolumeInfos/DirectionalForceVolumeInfo.cs @@ -0,0 +1,35 @@ +using NewHorizons.External.SerializableData; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NewHorizons.External.Modules.Volumes.VolumeInfos +{ + [JsonObject] + public class DirectionalForceVolumeInfo : ForceVolumeInfo + { + /// + /// The direction of the force applied by this volume. Defaults to up (0, 1, 0). + /// + public MVector3 normal; + + /// + /// Whether this force volume affects alignment. Defaults to true. + /// + [DefaultValue(true)] public bool affectsAlignment = true; + + /// + /// Whether the force applied by this volume takes the centripetal force of the volume's parent body into account. Defaults to false. + /// + public bool offsetCentripetalForce; + + /// + /// Whether to play the gravity crystal audio when the player is in this volume. + /// + public bool playGravityCrystalAudio; + } +} diff --git a/NewHorizons/External/Modules/Volumes/VolumeInfos/ForceVolumeInfo.cs b/NewHorizons/External/Modules/Volumes/VolumeInfos/ForceVolumeInfo.cs new file mode 100644 index 00000000..4d5fe636 --- /dev/null +++ b/NewHorizons/External/Modules/Volumes/VolumeInfos/ForceVolumeInfo.cs @@ -0,0 +1,40 @@ +using NewHorizons.External.Modules.Props; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NewHorizons.External.Modules.Volumes.VolumeInfos +{ + [JsonObject] + public class ForceVolumeInfo : PriorityVolumeInfo + { + /// + /// The shape of this volume. Defaults to a sphere shape with a radius of `radius`. + /// + public ShapeInfo shape; + + /// + /// The force applied by this volume. Can be negative to reverse the direction. + /// + public float force; + + /// + /// The priority of this force volume for the purposes of alignment. + /// + /// Volumes of higher priority will override volumes of lower priority. Volumes of the same priority will stack like normal. + /// Ex: A player in a gravity volume with priority 0, and zero-gravity volume with priority 1, will feel zero gravity. + /// + /// Default value here is 1 instead of 0 so it automatically overrides planet gravity, which is 0 by default. + /// + [DefaultValue(1)] public int alignmentPriority = 1; + + /// + /// Whether this force volume is inheritable. The most recently activated inheritable force volume will stack with other force volumes even if their priorities differ. + /// + public bool inheritable; + } +} diff --git a/NewHorizons/External/Modules/Volumes/VolumeInfos/GravityVolumeInfo.cs b/NewHorizons/External/Modules/Volumes/VolumeInfos/GravityVolumeInfo.cs new file mode 100644 index 00000000..44b226fc --- /dev/null +++ b/NewHorizons/External/Modules/Volumes/VolumeInfos/GravityVolumeInfo.cs @@ -0,0 +1,44 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NewHorizons.External.Modules.Volumes.VolumeInfos +{ + [JsonObject] + public class GravityVolumeInfo : ForceVolumeInfo + { + /// + /// The upper bounds of the volume's "surface". Above this radius, the force applied by this volume will have falloff applied. + /// + public float upperRadius; + + /// + /// The lower bounds of the volume's "surface". Above this radius and below the `upperRadius`, the force applied by this volume will be constant. Defaults to 0. + /// + [DefaultValue(0f)] public float lowerRadius; + + /// + /// The volume's force will decrease linearly from `force` to `minForce` as distance decreases from `lowerRadius` to `minRadius`. Defaults to 0. + /// + [DefaultValue(0f)] public float minRadius; + + /// + /// The minimum force applied by this volume between `lowerRadius` and `minRadius`. Defaults to 0. + /// + [DefaultValue(0f)] public float minForce; + + /// + /// How the force falls off with distance. Most planets use linear but the sun and some moons use inverseSquared. + /// + [DefaultValue("linear")] public GravityFallOff fallOff = GravityFallOff.Linear; + + /// + /// The radius where objects will be aligned to the volume's force. Defaults to 1.5x the `upperRadius`. Set to 0 to disable alignment. + /// + public float? alignmentRadius; + } +} diff --git a/NewHorizons/External/Modules/Volumes/VolumeInfos/PolarForceVolumeInfo.cs b/NewHorizons/External/Modules/Volumes/VolumeInfos/PolarForceVolumeInfo.cs new file mode 100644 index 00000000..bdf1ee0d --- /dev/null +++ b/NewHorizons/External/Modules/Volumes/VolumeInfos/PolarForceVolumeInfo.cs @@ -0,0 +1,23 @@ +using NewHorizons.External.SerializableData; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NewHorizons.External.Modules.Volumes.VolumeInfos +{ + [JsonObject] + public class PolarForceVolumeInfo : ForceVolumeInfo + { + /// + /// Tangential mode only. The force applied by this volume will be perpendicular to this direction and the direction to the other body. Defaults to up (0, 1, 0). + /// + public MVector3 normal; + /// + /// Enables tangential mode. The force applied by this volume will be perpendicular to the normal and the direction to the other body. Defaults to false. + /// + public bool tangential; + } +} diff --git a/NewHorizons/External/Modules/Volumes/VolumeInfos/RadialForceVolumeInfo.cs b/NewHorizons/External/Modules/Volumes/VolumeInfos/RadialForceVolumeInfo.cs new file mode 100644 index 00000000..eefe58e4 --- /dev/null +++ b/NewHorizons/External/Modules/Volumes/VolumeInfos/RadialForceVolumeInfo.cs @@ -0,0 +1,32 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; + +namespace NewHorizons.External.Modules.Volumes.VolumeInfos +{ + [JsonObject] + public class RadialForceVolumeInfo : ForceVolumeInfo + { + /// + /// How the force falls off with distance. Defaults to linear. + /// + [DefaultValue("linear")] public FallOff fallOff = FallOff.Linear; + + [JsonConverter(typeof(StringEnumConverter))] + public enum FallOff + { + [EnumMember(Value = @"constant")] Constant = 0, + + [EnumMember(Value = @"linear")] Linear = 1, + + [EnumMember(Value = @"inverseSquared")] + InverseSquared = 2 + } + } +} diff --git a/NewHorizons/External/Modules/Volumes/VolumeInfos/VolumeInfo.cs b/NewHorizons/External/Modules/Volumes/VolumeInfos/VolumeInfo.cs index 7b106614..17cee4bb 100644 --- a/NewHorizons/External/Modules/Volumes/VolumeInfos/VolumeInfo.cs +++ b/NewHorizons/External/Modules/Volumes/VolumeInfos/VolumeInfo.cs @@ -4,7 +4,7 @@ using System.ComponentModel; namespace NewHorizons.External.Modules.Volumes.VolumeInfos { [JsonObject] - public class VolumeInfo : GeneralPointPropInfo + public class VolumeInfo : GeneralPropInfo { /// /// The radius of this volume. diff --git a/NewHorizons/External/Modules/Volumes/VolumesModule.cs b/NewHorizons/External/Modules/Volumes/VolumesModule.cs index 6e3afe19..6b5fc30a 100644 --- a/NewHorizons/External/Modules/Volumes/VolumesModule.cs +++ b/NewHorizons/External/Modules/Volumes/VolumesModule.cs @@ -27,6 +27,11 @@ namespace NewHorizons.External.Modules.Volumes /// public FluidVolumeInfo[] fluidVolumes; + /// + /// Add force volumes to this planet. + /// + public ForceModule forces; + /// /// Add hazard volumes to this planet. /// Causes damage to player when inside this volume.