Add force volumes and give volumes rotation

This commit is contained in:
Joshua Thome 2025-02-22 11:03:32 -06:00
parent 5aace8408a
commit 4a81798078
11 changed files with 401 additions and 1 deletions

View File

@ -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<CylindricalForceVolume>(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<DirectionalForceVolume>(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<GravityVolume>(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<PolarForceVolume>(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<RadialForceVolume>(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<T>(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<OWTriggerVolume>();
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<OWCollider>();
}
else
{
var col = go.AddComponent<SphereCollider>();
col.radius = info.radius;
col.isTrigger = true;
var owCollider = go.GetAddComponent<OWCollider>();
owTriggerVolume._owCollider = owCollider;
}
var forceVolume = go.AddComponent<T>();
forceVolume._priority = info.priority;
forceVolume._alignmentPriority = info.alignmentPriority;
forceVolume._inheritable = info.inheritable;
return forceVolume;
}
}
}

View File

@ -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)

View File

@ -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
{
/// <summary>
/// Applies a constant force along the volume's XZ plane towards the volume's center. Affects alignment.
/// </summary>
public CylindricalForceVolumeInfo[] cylindricalVolumes;
/// <summary>
/// Applies a constant force in the direction of the volume's Y axis. May affect alignment.
/// </summary>
public DirectionalForceVolumeInfo[] directionalVolumes;
/// <summary>
/// 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.
/// </summary>
public GravityVolumeInfo[] gravityVolumes;
/// <summary>
/// Applies a constant force towards the volume's center. Affects alignment.
/// </summary>
public PolarForceVolumeInfo[] polarVolumes;
/// <summary>
/// Applies a force towards the volume's center with falloff by distance. Affects alignment.
/// </summary>
public RadialForceVolumeInfo[] radialVolumes;
}
}

View File

@ -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
{
/// <summary>
/// The direction that the force applied by this volume will be perpendicular to. Defaults to up (0, 1, 0).
/// </summary>
public MVector3 normal;
/// <summary>
/// Whether to play the gravity crystal audio when the player is in this volume.
/// </summary>
public bool playGravityCrystalAudio;
}
}

View File

@ -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
{
/// <summary>
/// The direction of the force applied by this volume. Defaults to up (0, 1, 0).
/// </summary>
public MVector3 normal;
/// <summary>
/// Whether this force volume affects alignment. Defaults to true.
/// </summary>
[DefaultValue(true)] public bool affectsAlignment = true;
/// <summary>
/// Whether the force applied by this volume takes the centripetal force of the volume's parent body into account. Defaults to false.
/// </summary>
public bool offsetCentripetalForce;
/// <summary>
/// Whether to play the gravity crystal audio when the player is in this volume.
/// </summary>
public bool playGravityCrystalAudio;
}
}

View File

@ -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
{
/// <summary>
/// The shape of this volume. Defaults to a sphere shape with a radius of `radius`.
/// </summary>
public ShapeInfo shape;
/// <summary>
/// The force applied by this volume. Can be negative to reverse the direction.
/// </summary>
public float force;
/// <summary>
/// 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.
/// </summary>
[DefaultValue(1)] public int alignmentPriority = 1;
/// <summary>
/// Whether this force volume is inheritable. The most recently activated inheritable force volume will stack with other force volumes even if their priorities differ.
/// </summary>
public bool inheritable;
}
}

View File

@ -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
{
/// <summary>
/// The upper bounds of the volume's "surface". Above this radius, the force applied by this volume will have falloff applied.
/// </summary>
public float upperRadius;
/// <summary>
/// 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.
/// </summary>
[DefaultValue(0f)] public float lowerRadius;
/// <summary>
/// The volume's force will decrease linearly from `force` to `minForce` as distance decreases from `lowerRadius` to `minRadius`. Defaults to 0.
/// </summary>
[DefaultValue(0f)] public float minRadius;
/// <summary>
/// The minimum force applied by this volume between `lowerRadius` and `minRadius`. Defaults to 0.
/// </summary>
[DefaultValue(0f)] public float minForce;
/// <summary>
/// How the force falls off with distance. Most planets use linear but the sun and some moons use inverseSquared.
/// </summary>
[DefaultValue("linear")] public GravityFallOff fallOff = GravityFallOff.Linear;
/// <summary>
/// The radius where objects will be aligned to the volume's force. Defaults to 1.5x the `upperRadius`. Set to 0 to disable alignment.
/// </summary>
public float? alignmentRadius;
}
}

View File

@ -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
{
/// <summary>
/// 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).
/// </summary>
public MVector3 normal;
/// <summary>
/// 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.
/// </summary>
public bool tangential;
}
}

View File

@ -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
{
/// <summary>
/// How the force falls off with distance. Defaults to linear.
/// </summary>
[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
}
}
}

View File

@ -4,7 +4,7 @@ using System.ComponentModel;
namespace NewHorizons.External.Modules.Volumes.VolumeInfos
{
[JsonObject]
public class VolumeInfo : GeneralPointPropInfo
public class VolumeInfo : GeneralPropInfo
{
/// <summary>
/// The radius of this volume.

View File

@ -27,6 +27,11 @@ namespace NewHorizons.External.Modules.Volumes
/// </summary>
public FluidVolumeInfo[] fluidVolumes;
/// <summary>
/// Add force volumes to this planet.
/// </summary>
public ForceModule forces;
/// <summary>
/// Add hazard volumes to this planet.
/// Causes damage to player when inside this volume.