mirror of
https://github.com/Outer-Wilds-New-Horizons/new-horizons.git
synced 2025-12-11 20:15:44 +01:00
Volume Shapes (#1056)
## Major features - Added a new `forces` sub-module to the `Volumes` planet module with new volume types for the various force volumes available in the game, including `directionalVolumes` (used by Nomai gravity crystals and anti-gravity surfaces) and `cylindricalVolumes` (used by the rings on the Construction Yard island), as well as `gravityVolumes`, `polarVolumes`, and `radialVolumes`. Resolves #1041 ## Minor features - All volumes now support shapes other than the default sphere via the new `shape` property, such as boxes, capsules, cylinders, hemispheres, and more. - All volumes now have `rotation` and `alignRadial` fields, allowing for non-spherical shapes to be rotated, and for `revealVolumes` to be pointed in other directions for the `maxAngle` check.
This commit is contained in:
commit
e07277c664
@ -150,6 +150,7 @@ namespace NewHorizons.Builder.Body
|
||||
streamingVolume.streamingGroup = streamingGroup;
|
||||
streamingVolume.transform.parent = blackHoleVolume.transform;
|
||||
streamingVolume.transform.localPosition = Vector3.zero;
|
||||
streamingVolume.gameObject.SetActive(true);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
170
NewHorizons/Builder/Props/ShapeBuilder.cs
Normal file
170
NewHorizons/Builder/Props/ShapeBuilder.cs
Normal file
@ -0,0 +1,170 @@
|
||||
using NewHorizons.Components;
|
||||
using NewHorizons.External.Modules.Props;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Builder.Props
|
||||
{
|
||||
public static class ShapeBuilder
|
||||
{
|
||||
public static OWTriggerVolume AddTriggerVolume(GameObject go, ShapeInfo info, float defaultRadius)
|
||||
{
|
||||
var owTriggerVolume = go.AddComponent<OWTriggerVolume>();
|
||||
|
||||
if (info != null)
|
||||
{
|
||||
var shapeOrCol = AddShapeOrCollider(go, info);
|
||||
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 = defaultRadius;
|
||||
col.isTrigger = true;
|
||||
var owCollider = go.GetAddComponent<OWCollider>();
|
||||
|
||||
owTriggerVolume._owCollider = owCollider;
|
||||
}
|
||||
|
||||
return owTriggerVolume;
|
||||
}
|
||||
|
||||
public static Component AddShapeOrCollider(GameObject go, ShapeInfo info)
|
||||
{
|
||||
if (info.useShape.HasValue)
|
||||
{
|
||||
// Explicitly add either a shape or collider if specified
|
||||
if (info.useShape.Value)
|
||||
return AddShape(go, info);
|
||||
else
|
||||
return AddCollider(go, info);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Prefer colliders over shapes if no preference is specified
|
||||
if (info.type is ShapeType.Sphere or ShapeType.Box or ShapeType.Capsule)
|
||||
return AddCollider(go, info);
|
||||
else
|
||||
return AddShape(go, info);
|
||||
}
|
||||
}
|
||||
|
||||
public static Shape AddShape(GameObject go, ShapeInfo info)
|
||||
{
|
||||
if (info.hasCollision)
|
||||
{
|
||||
throw new NotSupportedException($"Shapes do not support collision; set {nameof(info.hasCollision)} to false or use a supported collider type (sphere, box, or capsule).");
|
||||
}
|
||||
if (info.useShape.HasValue && !info.useShape.Value)
|
||||
{
|
||||
throw new NotSupportedException($"{info.useShape} was explicitly set to false but a shape is required here.");
|
||||
}
|
||||
switch (info.type)
|
||||
{
|
||||
case ShapeType.Sphere:
|
||||
var sphereShape = go.AddComponent<SphereShape>();
|
||||
sphereShape._radius = info.radius;
|
||||
sphereShape._center = info.offset ?? Vector3.zero;
|
||||
return sphereShape;
|
||||
case ShapeType.Box:
|
||||
var boxShape = go.AddComponent<BoxShape>();
|
||||
boxShape._size = info.size ?? Vector3.one;
|
||||
boxShape._center = info.offset ?? Vector3.zero;
|
||||
return boxShape;
|
||||
case ShapeType.Capsule:
|
||||
var capsuleShape = go.AddComponent<CapsuleShape>();
|
||||
capsuleShape._radius = info.radius;
|
||||
capsuleShape._direction = (int)info.direction;
|
||||
capsuleShape._height = info.height;
|
||||
capsuleShape._center = info.offset ?? Vector3.zero;
|
||||
return capsuleShape;
|
||||
case ShapeType.Cylinder:
|
||||
var cylinderShape = go.AddComponent<CylinderShape>();
|
||||
cylinderShape._radius = info.radius;
|
||||
cylinderShape._height = info.height;
|
||||
cylinderShape._center = info.offset ?? Vector3.zero;
|
||||
cylinderShape._pointChecksOnly = true;
|
||||
return cylinderShape;
|
||||
case ShapeType.Cone:
|
||||
var coneShape = go.AddComponent<ConeShape>();
|
||||
coneShape._topRadius = info.innerRadius;
|
||||
coneShape._bottomRadius = info.outerRadius;
|
||||
coneShape._direction = (int)info.direction;
|
||||
coneShape._height = info.height;
|
||||
coneShape._center = info.offset ?? Vector3.zero;
|
||||
coneShape._pointChecksOnly = true;
|
||||
return coneShape;
|
||||
case ShapeType.Hemisphere:
|
||||
var hemisphereShape = go.AddComponent<HemisphereShape>();
|
||||
hemisphereShape._radius = info.radius;
|
||||
hemisphereShape._direction = (int)info.direction;
|
||||
hemisphereShape._cap = info.cap;
|
||||
hemisphereShape._center = info.offset ?? Vector3.zero;
|
||||
hemisphereShape._pointChecksOnly = true;
|
||||
return hemisphereShape;
|
||||
case ShapeType.Hemicapsule:
|
||||
var hemicapsuleShape = go.AddComponent<HemicapsuleShape>();
|
||||
hemicapsuleShape._radius = info.radius;
|
||||
hemicapsuleShape._direction = (int)info.direction;
|
||||
hemicapsuleShape._height = info.height;
|
||||
hemicapsuleShape._cap = info.cap;
|
||||
hemicapsuleShape._center = info.offset ?? Vector3.zero;
|
||||
hemicapsuleShape._pointChecksOnly = true;
|
||||
return hemicapsuleShape;
|
||||
case ShapeType.Ring:
|
||||
var ringShape = go.AddComponent<RingShape>();
|
||||
ringShape.innerRadius = info.innerRadius;
|
||||
ringShape.outerRadius = info.outerRadius;
|
||||
ringShape.height = info.height;
|
||||
ringShape.center = info.offset ?? Vector3.zero;
|
||||
ringShape._pointChecksOnly = true;
|
||||
return ringShape;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(info.type), info.type, $"Unsupported shape type");
|
||||
}
|
||||
}
|
||||
|
||||
public static Collider AddCollider(GameObject go, ShapeInfo info)
|
||||
{
|
||||
if (info.useShape.HasValue && info.useShape.Value)
|
||||
{
|
||||
throw new NotSupportedException($"{info.useShape} was explicitly set to true but a non-shape collider is required here.");
|
||||
}
|
||||
switch (info.type)
|
||||
{
|
||||
case ShapeType.Sphere:
|
||||
var sphereCollider = go.AddComponent<SphereCollider>();
|
||||
sphereCollider.radius = info.radius;
|
||||
sphereCollider.center = info.offset ?? Vector3.zero;
|
||||
sphereCollider.isTrigger = !info.hasCollision;
|
||||
go.GetAddComponent<OWCollider>();
|
||||
return sphereCollider;
|
||||
case ShapeType.Box:
|
||||
var boxCollider = go.AddComponent<BoxCollider>();
|
||||
boxCollider.size = info.size ?? Vector3.one;
|
||||
boxCollider.center = info.offset ?? Vector3.zero;
|
||||
boxCollider.isTrigger = !info.hasCollision;
|
||||
go.GetAddComponent<OWCollider>();
|
||||
return boxCollider;
|
||||
case ShapeType.Capsule:
|
||||
var capsuleCollider = go.AddComponent<CapsuleCollider>();
|
||||
capsuleCollider.radius = info.radius;
|
||||
capsuleCollider.direction = (int)info.direction;
|
||||
capsuleCollider.height = info.height;
|
||||
capsuleCollider.center = info.offset ?? Vector3.zero;
|
||||
capsuleCollider.isTrigger = !info.hasCollision;
|
||||
go.GetAddComponent<OWCollider>();
|
||||
return capsuleCollider;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(info.type), info.type, $"Unsupported collider type");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -25,7 +25,8 @@ namespace NewHorizons.Builder.Volumes
|
||||
owAudioSource.SetTrack(info.track.ConvertToOW());
|
||||
AudioUtilities.SetAudioClip(owAudioSource, info.audio, mod);
|
||||
|
||||
var audioVolume = go.AddComponent<AudioVolume>();
|
||||
var audioVolume = PriorityVolumeBuilder.MakeExisting<AudioVolume>(go, planetGO, sector, info);
|
||||
|
||||
audioVolume._layer = info.layer;
|
||||
audioVolume.SetPriority(info.priority);
|
||||
audioVolume._fadeSeconds = info.fadeSeconds;
|
||||
@ -33,11 +34,7 @@ namespace NewHorizons.Builder.Volumes
|
||||
audioVolume._randomizePlayhead = info.randomizePlayhead;
|
||||
audioVolume._pauseOnFadeOut = info.pauseOnFadeOut;
|
||||
|
||||
var shape = go.AddComponent<SphereShape>();
|
||||
shape.radius = info.radius;
|
||||
|
||||
var owTriggerVolume = go.AddComponent<OWTriggerVolume>();
|
||||
owTriggerVolume._shape = shape;
|
||||
var owTriggerVolume = go.GetComponent<OWTriggerVolume>();
|
||||
audioVolume._triggerVolumeOverride = owTriggerVolume;
|
||||
|
||||
go.SetActive(true);
|
||||
|
||||
@ -13,6 +13,8 @@ namespace NewHorizons.Builder.Volumes
|
||||
volume.TargetSolarSystem = info.targetStarSystem;
|
||||
volume.TargetSpawnID = info.spawnPointID;
|
||||
|
||||
volume.gameObject.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,6 +16,8 @@ namespace NewHorizons.Builder.Volumes
|
||||
volume.deathType = info.deathType == null ? null : EnumUtils.Parse(info.deathType.ToString(), DeathType.Default);
|
||||
volume.mod = mod;
|
||||
|
||||
volume.gameObject.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,7 +15,8 @@ namespace NewHorizons.Builder.Volumes
|
||||
var go = GeneralPropBuilder.MakeNew("DayNightAudioVolume", planetGO, sector, info);
|
||||
go.layer = Layer.AdvancedEffectVolume;
|
||||
|
||||
var audioVolume = go.AddComponent<NHDayNightAudioVolume>();
|
||||
var audioVolume = PriorityVolumeBuilder.MakeExisting<NHDayNightAudioVolume>(go, planetGO, sector, info);
|
||||
|
||||
audioVolume.sunName = info.sun;
|
||||
audioVolume.dayWindow = info.dayWindow;
|
||||
audioVolume.dayAudio = info.dayAudio;
|
||||
@ -24,13 +25,7 @@ namespace NewHorizons.Builder.Volumes
|
||||
audioVolume.volume = info.volume;
|
||||
audioVolume.SetTrack(info.track.ConvertToOW());
|
||||
|
||||
var shape = go.AddComponent<SphereShape>();
|
||||
shape.radius = info.radius;
|
||||
|
||||
var owTriggerVolume = go.AddComponent<OWTriggerVolume>();
|
||||
owTriggerVolume._shape = shape;
|
||||
|
||||
go.SetActive(true);
|
||||
audioVolume.gameObject.SetActive(true);
|
||||
|
||||
return audioVolume;
|
||||
}
|
||||
|
||||
@ -12,6 +12,8 @@ namespace NewHorizons.Builder.Volumes
|
||||
|
||||
volume._deathType = EnumUtils.Parse<DeathType>(info.deathType.ToString(), DeathType.Default);
|
||||
|
||||
volume.gameObject.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,6 +35,8 @@ namespace NewHorizons.Builder.Volumes
|
||||
volume._allowShipAutoroll = info.allowShipAutoroll;
|
||||
volume._disableOnStart = info.disableOnStart;
|
||||
|
||||
volume.gameObject.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
111
NewHorizons/Builder/Volumes/ForceVolumeBuilder.cs
Normal file
111
NewHorizons/Builder/Volumes/ForceVolumeBuilder.cs
Normal file
@ -0,0 +1,111 @@
|
||||
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 TVolume Make<TVolume>(GameObject planetGO, Sector sector, ForceVolumeInfo info) where TVolume : ForceVolume
|
||||
{
|
||||
var forceVolume = PriorityVolumeBuilder.Make<TVolume>(planetGO, sector, info);
|
||||
|
||||
forceVolume._alignmentPriority = info.alignmentPriority;
|
||||
forceVolume._inheritable = info.inheritable;
|
||||
|
||||
return forceVolume;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,7 @@ using NewHorizons.External.Modules.Volumes.VolumeInfos;
|
||||
using NewHorizons.Utility.OuterWilds;
|
||||
using OWML.Common;
|
||||
using OWML.Utils;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
@ -13,35 +14,28 @@ namespace NewHorizons.Builder.Volumes
|
||||
public static HazardVolume Make(GameObject planetGO, Sector sector, OWRigidbody owrb, HazardVolumeInfo info, IModBehaviour mod)
|
||||
{
|
||||
var go = GeneralPropBuilder.MakeNew("HazardVolume", planetGO, sector, info);
|
||||
go.layer = Layer.BasicEffectVolume;
|
||||
|
||||
var shape = go.AddComponent<SphereShape>();
|
||||
shape.radius = info.radius;
|
||||
|
||||
var owTriggerVolume = go.AddComponent<OWTriggerVolume>();
|
||||
owTriggerVolume._shape = shape;
|
||||
|
||||
var volume = AddHazardVolume(go, sector, owrb, info.type, info.firstContactDamageType, info.firstContactDamage, info.damagePerSecond);
|
||||
|
||||
var volume = MakeExisting(go, planetGO, sector, owrb, info);
|
||||
|
||||
go.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
|
||||
public static HazardVolume AddHazardVolume(GameObject go, Sector sector, OWRigidbody owrb, HazardVolumeInfo.HazardType? type, HazardVolumeInfo.InstantDamageType? firstContactDamageType, float firstContactDamage, float damagePerSecond)
|
||||
public static HazardVolume MakeExisting(GameObject go, GameObject planetGO, Sector sector, OWRigidbody owrb, HazardVolumeInfo info)
|
||||
{
|
||||
HazardVolume hazardVolume = null;
|
||||
if (type == HazardVolumeInfo.HazardType.RIVERHEAT)
|
||||
if (info.type == HazardVolumeInfo.HazardType.RIVERHEAT)
|
||||
{
|
||||
hazardVolume = go.AddComponent<RiverHeatHazardVolume>();
|
||||
hazardVolume = VolumeBuilder.MakeExisting<RiverHeatHazardVolume>(go, planetGO, sector, info);
|
||||
}
|
||||
else if (type == HazardVolumeInfo.HazardType.HEAT)
|
||||
else if (info.type == HazardVolumeInfo.HazardType.HEAT)
|
||||
{
|
||||
hazardVolume = go.AddComponent<HeatHazardVolume>();
|
||||
hazardVolume = VolumeBuilder.MakeExisting<HeatHazardVolume>(go, planetGO, sector, info);
|
||||
}
|
||||
else if (type == HazardVolumeInfo.HazardType.DARKMATTER)
|
||||
else if (info.type == HazardVolumeInfo.HazardType.DARKMATTER)
|
||||
{
|
||||
hazardVolume = go.AddComponent<DarkMatterVolume>();
|
||||
hazardVolume = VolumeBuilder.MakeExisting<DarkMatterVolume>(go, planetGO, sector, info);
|
||||
var visorFrostEffectVolume = go.AddComponent<VisorFrostEffectVolume>();
|
||||
visorFrostEffectVolume._frostRate = 0.5f;
|
||||
visorFrostEffectVolume._maxFrost = 0.91f;
|
||||
@ -67,28 +61,38 @@ namespace NewHorizons.Builder.Volumes
|
||||
submerge._fluidDetector = detector;
|
||||
}
|
||||
}
|
||||
else if (type == HazardVolumeInfo.HazardType.ELECTRICITY)
|
||||
else if (info.type == HazardVolumeInfo.HazardType.ELECTRICITY)
|
||||
{
|
||||
var electricityVolume = go.AddComponent<ElectricityVolume>();
|
||||
var electricityVolume = VolumeBuilder.MakeExisting<ElectricityVolume>(go, planetGO, sector, info);
|
||||
electricityVolume._shockAudioPool = new OWAudioSource[0];
|
||||
hazardVolume = electricityVolume;
|
||||
}
|
||||
else
|
||||
{
|
||||
var simpleHazardVolume = go.AddComponent<SimpleHazardVolume>();
|
||||
simpleHazardVolume._type = EnumUtils.Parse(type.ToString(), HazardVolume.HazardType.GENERAL);
|
||||
simpleHazardVolume._type = EnumUtils.Parse(info.type.ToString(), HazardVolume.HazardType.GENERAL);
|
||||
hazardVolume = simpleHazardVolume;
|
||||
}
|
||||
hazardVolume._attachedBody = owrb;
|
||||
hazardVolume._damagePerSecond = type == null ? 0f : damagePerSecond;
|
||||
hazardVolume._damagePerSecond = info.type == HazardVolumeInfo.HazardType.NONE ? 0f : info.damagePerSecond;
|
||||
|
||||
if (firstContactDamageType != null)
|
||||
{
|
||||
hazardVolume._firstContactDamageType = EnumUtils.Parse(firstContactDamageType.ToString(), InstantDamageType.Impact);
|
||||
hazardVolume._firstContactDamage = firstContactDamage;
|
||||
}
|
||||
hazardVolume._firstContactDamageType = EnumUtils.Parse(info.firstContactDamageType.ToString(), InstantDamageType.Impact);
|
||||
hazardVolume._firstContactDamage = info.firstContactDamage;
|
||||
|
||||
return hazardVolume;
|
||||
}
|
||||
|
||||
public static HazardVolume AddHazardVolume(GameObject go, Sector sector, OWRigidbody owrb, HazardVolumeInfo.HazardType? type, HazardVolumeInfo.InstantDamageType? firstContactDamageType, float firstContactDamage, float damagePerSecond)
|
||||
{
|
||||
var planetGO = sector.transform.root.gameObject;
|
||||
return MakeExisting(go, planetGO, sector, owrb, new HazardVolumeInfo
|
||||
{
|
||||
radius = 0f, // Volume builder should skip creating an extra trigger volume and collider if radius is 0
|
||||
type = type ?? HazardVolumeInfo.HazardType.NONE,
|
||||
firstContactDamageType = firstContactDamageType ?? HazardVolumeInfo.InstantDamageType.Impact,
|
||||
firstContactDamage = firstContactDamage,
|
||||
damagePerSecond = damagePerSecond
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,21 +11,16 @@ namespace NewHorizons.Builder.Volumes
|
||||
{
|
||||
public static NHNotificationVolume Make(GameObject planetGO, Sector sector, NotificationVolumeInfo info, IModBehaviour mod)
|
||||
{
|
||||
var go = GeneralPropBuilder.MakeNew("NotificationVolume", planetGO, sector, info);
|
||||
go.layer = Layer.BasicEffectVolume;
|
||||
var notificationVolume = VolumeBuilder.Make<NHNotificationVolume>(planetGO, sector, info);
|
||||
|
||||
var shape = go.AddComponent<SphereShape>();
|
||||
shape.radius = info.radius;
|
||||
// Preserving name for backwards compatibility
|
||||
notificationVolume.gameObject.name = string.IsNullOrEmpty(info.rename) ? "NotificationVolume" : info.rename;
|
||||
|
||||
var owTriggerVolume = go.AddComponent<OWTriggerVolume>();
|
||||
owTriggerVolume._shape = shape;
|
||||
|
||||
var notificationVolume = go.AddComponent<NHNotificationVolume>();
|
||||
notificationVolume.SetTarget(info.target);
|
||||
if (info.entryNotification != null) notificationVolume.SetEntryNotification(info.entryNotification.displayMessage, info.entryNotification.duration);
|
||||
if (info.exitNotification != null) notificationVolume.SetExitNotification(info.exitNotification.displayMessage, info.exitNotification.duration);
|
||||
|
||||
go.SetActive(true);
|
||||
notificationVolume.gameObject.SetActive(true);
|
||||
|
||||
return notificationVolume;
|
||||
}
|
||||
|
||||
@ -12,6 +12,8 @@ namespace NewHorizons.Builder.Volumes
|
||||
volume._treeVolume = info.treeVolume;
|
||||
volume._playRefillAudio = info.playRefillAudio;
|
||||
|
||||
volume.gameObject.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,17 @@ namespace NewHorizons.Builder.Volumes
|
||||
{
|
||||
public static class PriorityVolumeBuilder
|
||||
{
|
||||
public static TVolume MakeExisting<TVolume>(GameObject go, GameObject planetGO, Sector sector, PriorityVolumeInfo info) where TVolume : PriorityVolume
|
||||
{
|
||||
var volume = VolumeBuilder.MakeExisting<TVolume>(go, planetGO, sector, info);
|
||||
|
||||
volume._layer = info.layer;
|
||||
volume.SetPriority(info.priority);
|
||||
|
||||
return volume;
|
||||
}
|
||||
|
||||
|
||||
public static TVolume Make<TVolume>(GameObject planetGO, Sector sector, PriorityVolumeInfo info) where TVolume : PriorityVolume
|
||||
{
|
||||
var volume = VolumeBuilder.Make<TVolume>(planetGO, sector, info);
|
||||
|
||||
@ -12,6 +12,8 @@ namespace NewHorizons.Builder.Volumes.Rulesets
|
||||
volume.minImpactSpeed = info.minImpactSpeed;
|
||||
volume.maxImpactSpeed = info.maxImpactSpeed;
|
||||
|
||||
volume.gameObject.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,6 +15,8 @@ namespace NewHorizons.Builder.Volumes.Rulesets
|
||||
volume._lanternRange = info.lanternRange;
|
||||
volume._ignoreAnchor = info.ignoreAnchor;
|
||||
|
||||
volume.gameObject.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,6 +13,8 @@ namespace NewHorizons.Builder.Volumes.Rulesets
|
||||
volume._nerfJetpackBooster = info.nerfJetpackBooster;
|
||||
volume._nerfDuration = info.nerfDuration;
|
||||
|
||||
volume.gameObject.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,6 +14,8 @@ namespace NewHorizons.Builder.Volumes
|
||||
volume.stoppingDistance = info.stoppingDistance;
|
||||
volume.maxEntryAngle = info.maxEntryAngle;
|
||||
|
||||
volume.gameObject.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,6 +12,8 @@ namespace NewHorizons.Builder.Volumes
|
||||
volume._speedLimit = info.speedLimit;
|
||||
volume._acceleration = info.acceleration;
|
||||
|
||||
volume.gameObject.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
using NewHorizons.Builder.Props;
|
||||
using NewHorizons.External.Modules.Volumes.VolumeInfos;
|
||||
using NewHorizons.Utility.OuterWilds;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Builder.Volumes
|
||||
@ -9,27 +7,13 @@ namespace NewHorizons.Builder.Volumes
|
||||
{
|
||||
public static TVolume Make<TVolume>(GameObject planetGO, Sector sector, VanishVolumeInfo info) where TVolume : VanishVolume
|
||||
{
|
||||
var go = GeneralPropBuilder.MakeNew(typeof(TVolume).Name, planetGO, sector, info);
|
||||
go.layer = Layer.BasicEffectVolume;
|
||||
|
||||
var collider = go.AddComponent<SphereCollider>();
|
||||
collider.isTrigger = true;
|
||||
collider.radius = info.radius;
|
||||
|
||||
var owCollider = go.AddComponent<OWCollider>();
|
||||
owCollider._collider = collider;
|
||||
|
||||
var owTriggerVolume = go.AddComponent<OWTriggerVolume>();
|
||||
owTriggerVolume._owCollider = owCollider;
|
||||
|
||||
var volume = go.AddComponent<TVolume>();
|
||||
var volume = VolumeBuilder.Make<TVolume>(planetGO, sector, info);
|
||||
|
||||
var collider = volume.gameObject.GetComponent<SphereCollider>();
|
||||
volume._collider = collider;
|
||||
volume._shrinkBodies = info.shrinkBodies;
|
||||
volume._onlyAffectsPlayerAndShip = info.onlyAffectsPlayerRelatedBodies;
|
||||
|
||||
go.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,6 +12,8 @@ namespace NewHorizons.Builder.Volumes.VisorEffects
|
||||
volume._frostRate = info.frostRate;
|
||||
volume._maxFrost = info.maxFrost;
|
||||
|
||||
volume.gameObject.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,6 +13,8 @@ namespace NewHorizons.Builder.Volumes.VisorEffects
|
||||
volume._dropletRate = info.dropletRate;
|
||||
volume._streakRate = info.streakRate;
|
||||
|
||||
volume.gameObject.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,21 +7,36 @@ namespace NewHorizons.Builder.Volumes
|
||||
{
|
||||
public static class VolumeBuilder
|
||||
{
|
||||
public static TVolume MakeExisting<TVolume>(GameObject go, GameObject planetGO, Sector sector, VolumeInfo info) where TVolume : MonoBehaviour
|
||||
{
|
||||
// Respect existing layer if set to a valid volume layer
|
||||
if (go.layer != Layer.AdvancedEffectVolume)
|
||||
{
|
||||
go.layer = Layer.BasicEffectVolume;
|
||||
}
|
||||
|
||||
// Skip creating a trigger volume if one already exists and has a shape set and we aren't overriding it
|
||||
var trigger = go.GetComponent<OWTriggerVolume>();
|
||||
if (trigger == null || (trigger._shape == null && trigger._owCollider == null) || info.shape != null || info.radius > 0f)
|
||||
{
|
||||
ShapeBuilder.AddTriggerVolume(go, info.shape, info.radius);
|
||||
}
|
||||
|
||||
var volume = go.AddComponent<TVolume>();
|
||||
|
||||
return volume;
|
||||
}
|
||||
|
||||
public static TVolume Make<TVolume>(GameObject planetGO, Sector sector, VolumeInfo info) where TVolume : MonoBehaviour //Could be BaseVolume but I need to create vanilla volumes too.
|
||||
{
|
||||
var go = GeneralPropBuilder.MakeNew(typeof(TVolume).Name, planetGO, sector, info);
|
||||
go.layer = Layer.BasicEffectVolume;
|
||||
|
||||
var shape = go.AddComponent<SphereShape>();
|
||||
shape.radius = info.radius;
|
||||
|
||||
var owTriggerVolume = go.AddComponent<OWTriggerVolume>();
|
||||
owTriggerVolume._shape = shape;
|
||||
|
||||
var volume = go.AddComponent<TVolume>();
|
||||
|
||||
go.SetActive(true);
|
||||
return MakeExisting<TVolume>(go, planetGO, sector, info);
|
||||
}
|
||||
|
||||
public static TVolume MakeAndEnable<TVolume>(GameObject planetGO, Sector sector, VolumeInfo info) where TVolume : MonoBehaviour
|
||||
{
|
||||
var volume = Make<TVolume>(planetGO, sector, info);
|
||||
volume.gameObject.SetActive(true);
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,28 +60,28 @@ namespace NewHorizons.Builder.Volumes
|
||||
{
|
||||
foreach (var mapRestrictionVolume in config.Volumes.mapRestrictionVolumes)
|
||||
{
|
||||
VolumeBuilder.Make<MapRestrictionVolume>(go, sector, mapRestrictionVolume);
|
||||
VolumeBuilder.MakeAndEnable<MapRestrictionVolume>(go, sector, mapRestrictionVolume);
|
||||
}
|
||||
}
|
||||
if (config.Volumes.interferenceVolumes != null)
|
||||
{
|
||||
foreach (var interferenceVolume in config.Volumes.interferenceVolumes)
|
||||
{
|
||||
VolumeBuilder.Make<Components.Volumes.InterferenceVolume>(go, sector, interferenceVolume);
|
||||
VolumeBuilder.MakeAndEnable<Components.Volumes.InterferenceVolume>(go, sector, interferenceVolume);
|
||||
}
|
||||
}
|
||||
if (config.Volumes.reverbVolumes != null)
|
||||
{
|
||||
foreach (var reverbVolume in config.Volumes.reverbVolumes)
|
||||
{
|
||||
VolumeBuilder.Make<ReverbTriggerVolume>(go, sector, reverbVolume);
|
||||
VolumeBuilder.MakeAndEnable<ReverbTriggerVolume>(go, sector, reverbVolume);
|
||||
}
|
||||
}
|
||||
if (config.Volumes.insulatingVolumes != null)
|
||||
{
|
||||
foreach (var insulatingVolume in config.Volumes.insulatingVolumes)
|
||||
{
|
||||
VolumeBuilder.Make<InsulatingVolume>(go, sector, insulatingVolume);
|
||||
VolumeBuilder.MakeAndEnable<InsulatingVolume>(go, sector, insulatingVolume);
|
||||
}
|
||||
}
|
||||
if (config.Volumes.zeroGravityVolumes != null)
|
||||
@ -112,20 +112,58 @@ 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)
|
||||
{
|
||||
foreach (var destructionVolume in config.Volumes.probe.destructionVolumes)
|
||||
{
|
||||
VolumeBuilder.Make<ProbeDestructionVolume>(go, sector, destructionVolume);
|
||||
VolumeBuilder.MakeAndEnable<ProbeDestructionVolume>(go, sector, destructionVolume);
|
||||
}
|
||||
}
|
||||
if (config.Volumes.probe.safetyVolumes != null)
|
||||
{
|
||||
foreach (var safetyVolume in config.Volumes.probe.safetyVolumes)
|
||||
{
|
||||
VolumeBuilder.Make<ProbeSafetyVolume>(go, sector, safetyVolume);
|
||||
VolumeBuilder.MakeAndEnable<ProbeSafetyVolume>(go, sector, safetyVolume);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -152,7 +190,7 @@ namespace NewHorizons.Builder.Volumes
|
||||
{
|
||||
foreach (var antiTravelMusicRuleset in config.Volumes.rulesets.antiTravelMusicRulesets)
|
||||
{
|
||||
VolumeBuilder.Make<AntiTravelMusicRuleset>(go, sector, antiTravelMusicRuleset);
|
||||
VolumeBuilder.MakeAndEnable<AntiTravelMusicRuleset>(go, sector, antiTravelMusicRuleset);
|
||||
}
|
||||
}
|
||||
if (config.Volumes.rulesets.playerImpactRulesets != null)
|
||||
@ -181,7 +219,7 @@ namespace NewHorizons.Builder.Volumes
|
||||
{
|
||||
foreach (var referenceFrameBlockerVolume in config.Volumes.referenceFrameBlockerVolumes)
|
||||
{
|
||||
VolumeBuilder.Make<ReferenceFrameBlockerVolume>(go, sector, referenceFrameBlockerVolume);
|
||||
VolumeBuilder.MakeAndEnable<ReferenceFrameBlockerVolume>(go, sector, referenceFrameBlockerVolume);
|
||||
}
|
||||
}
|
||||
if (config.Volumes.speedTrapVolumes != null)
|
||||
@ -202,7 +240,7 @@ namespace NewHorizons.Builder.Volumes
|
||||
{
|
||||
foreach (var lightSourceVolume in config.Volumes.lightSourceVolumes)
|
||||
{
|
||||
VolumeBuilder.Make<LightlessLightSourceVolume>(go, sector, lightSourceVolume);
|
||||
VolumeBuilder.MakeAndEnable<LightlessLightSourceVolume>(go, sector, lightSourceVolume);
|
||||
}
|
||||
}
|
||||
if (config.Volumes.solarSystemVolume != null)
|
||||
|
||||
@ -11,6 +11,8 @@ namespace NewHorizons.Builder.Volumes
|
||||
|
||||
volume._inheritable = true;
|
||||
|
||||
volume.gameObject.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
92
NewHorizons/External/Modules/Props/ShapeInfo.cs
vendored
Normal file
92
NewHorizons/External/Modules/Props/ShapeInfo.cs
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
using NewHorizons.External.SerializableData;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NewHorizons.External.Modules.Props
|
||||
{
|
||||
[JsonObject]
|
||||
public class ShapeInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of shape or collider to add. Sphere, box, and capsule colliders are more performant and support collision. Defaults to sphere.
|
||||
/// </summary>
|
||||
public ShapeType type = ShapeType.Sphere;
|
||||
|
||||
/// <summary>
|
||||
/// The radius of the shape or collider. Defaults to 0.5 meters. Only used by spheres, capsules, cylinders, hemispheres, hemicapsules, and rings.
|
||||
/// </summary>
|
||||
public float radius = 0.5f;
|
||||
|
||||
/// <summary>
|
||||
/// The height of the shape or collider. Defaults to 1 meter. Only used by capsules, cylinders, cones, hemicapsules, and rings.
|
||||
/// </summary>
|
||||
public float height = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// The axis that the shape or collider is aligned with. Defaults to the Y axis (up). The flat bottom of the shape will be pointing towards the negative axis. Only used by capsules, cones, hemispheres, and hemicapsules.
|
||||
/// </summary>
|
||||
public ColliderAxis direction = ColliderAxis.Y;
|
||||
|
||||
/// <summary>
|
||||
/// The inner radius of the shape. Defaults to 0 meters. Only used by cones and rings.
|
||||
/// </summary>
|
||||
public float innerRadius = 0f;
|
||||
|
||||
/// <summary>
|
||||
/// The outer radius of the shape. Defaults to 0.5 meters. Only used by cones and rings.
|
||||
/// </summary>
|
||||
public float outerRadius = 0.5f;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the shape has an end cap. Defaults to true. Only used by hemispheres and hemicapsules.
|
||||
/// </summary>
|
||||
public bool cap = true;
|
||||
|
||||
/// <summary>
|
||||
/// The size of the shape or collider. Defaults to (1,1,1). Only used by boxes.
|
||||
/// </summary>
|
||||
public MVector3 size;
|
||||
|
||||
/// <summary>
|
||||
/// The offset of the shape or collider from the object's origin. Defaults to (0,0,0). Supported by all collider and shape types.
|
||||
/// </summary>
|
||||
public MVector3 offset;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the collider should have collision enabled. If false, the collider will be a trigger. Defaults to false. Only supported for spheres, boxes, and capsules.
|
||||
/// </summary>
|
||||
public bool hasCollision = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to explicitly use a shape instead of a collider. Shapes do not support collision and are less performant, but support a wider set of shapes and are required by some components. Omit this unless you explicitly want to use a sphere, box, or capsule shape instead of a collider.
|
||||
/// </summary>
|
||||
public bool? useShape;
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum ShapeType
|
||||
{
|
||||
[EnumMember(Value = @"sphere")] Sphere,
|
||||
[EnumMember(Value = @"box")] Box,
|
||||
[EnumMember(Value = @"capsule")] Capsule,
|
||||
[EnumMember(Value = @"cylinder")] Cylinder,
|
||||
[EnumMember(Value = @"cone")] Cone,
|
||||
[EnumMember(Value = @"hemisphere")] Hemisphere,
|
||||
[EnumMember(Value = @"hemicapsule")] Hemicapsule,
|
||||
[EnumMember(Value = @"ring")] Ring,
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum ColliderAxis
|
||||
{
|
||||
[EnumMember(Value = @"x")] X = 0,
|
||||
[EnumMember(Value = @"y")] Y = 1,
|
||||
[EnumMember(Value = @"z")] Z = 2,
|
||||
}
|
||||
}
|
||||
40
NewHorizons/External/Modules/Volumes/ForceModule.cs
vendored
Normal file
40
NewHorizons/External/Modules/Volumes/ForceModule.cs
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
24
NewHorizons/External/Modules/Volumes/VolumeInfos/CylindricalForceVolumeInfo.cs
vendored
Normal file
24
NewHorizons/External/Modules/Volumes/VolumeInfos/CylindricalForceVolumeInfo.cs
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
35
NewHorizons/External/Modules/Volumes/VolumeInfos/DirectionalForceVolumeInfo.cs
vendored
Normal file
35
NewHorizons/External/Modules/Volumes/VolumeInfos/DirectionalForceVolumeInfo.cs
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
35
NewHorizons/External/Modules/Volumes/VolumeInfos/ForceVolumeInfo.cs
vendored
Normal file
35
NewHorizons/External/Modules/Volumes/VolumeInfos/ForceVolumeInfo.cs
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
44
NewHorizons/External/Modules/Volumes/VolumeInfos/GravityVolumeInfo.cs
vendored
Normal file
44
NewHorizons/External/Modules/Volumes/VolumeInfos/GravityVolumeInfo.cs
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
23
NewHorizons/External/Modules/Volumes/VolumeInfos/PolarForceVolumeInfo.cs
vendored
Normal file
23
NewHorizons/External/Modules/Volumes/VolumeInfos/PolarForceVolumeInfo.cs
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
32
NewHorizons/External/Modules/Volumes/VolumeInfos/RadialForceVolumeInfo.cs
vendored
Normal file
32
NewHorizons/External/Modules/Volumes/VolumeInfos/RadialForceVolumeInfo.cs
vendored
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -29,7 +29,7 @@ namespace NewHorizons.External.Modules.Volumes.VolumeInfos
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The max view angle (in degrees) the player can see the volume with to unlock the fact (`observe` only)
|
||||
/// The max view angle (in degrees) the player can see the volume with to unlock the fact (`observe` only). This will effectively be a cone extending from the volume's center forwards (along the Z axis) based on the volume's rotation.
|
||||
/// </summary>
|
||||
[DefaultValue(180f)]
|
||||
public float maxAngle = 180f; // Observe Only
|
||||
|
||||
@ -1,14 +1,20 @@
|
||||
using NewHorizons.External.Modules.Props;
|
||||
using Newtonsoft.Json;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace NewHorizons.External.Modules.Volumes.VolumeInfos
|
||||
{
|
||||
[JsonObject]
|
||||
public class VolumeInfo : GeneralPointPropInfo
|
||||
public class VolumeInfo : GeneralPropInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The radius of this volume.
|
||||
/// The radius of this volume, if a shape is not specified.
|
||||
/// </summary>
|
||||
[DefaultValue(1f)] public float radius = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// The shape of this volume. Defaults to a sphere with a radius of `radius` if not specified.
|
||||
/// </summary>
|
||||
public ShapeInfo shape;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
72
docs/src/content/docs/guides/volumes.md
Normal file
72
docs/src/content/docs/guides/volumes.md
Normal file
@ -0,0 +1,72 @@
|
||||
---
|
||||
title: Volumes
|
||||
description: Guide to making volumes in New Horizons
|
||||
---
|
||||
|
||||
Volumes are invisible 3D "zones" or "triggers" that cause various effects when objects enter or leave them. For example, `oxygenVolumes` refill the player's oxygen when they enter (used for the various oxygen-generating trees in the game), `forces.directionalVolumes` push players and other physics objects in a specific direction (used by both Nomai artificial gravity surfaces and tractor beams), `revealVolumes` unlock ship log facts when the player enters or observes them (used everywhere in the game), and more.
|
||||
|
||||
New Horizons makes adding volumes to your planets easy; just specify them like you would [for a prop](/guides/details/) but under `Volumes` instead of `Props`. For example, to add an oxygen volume at certain location:
|
||||
|
||||
```json title="planets/My Cool Planet.json"
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/Outer-Wilds-New-Horizons/new-horizons/main/NewHorizons/Schemas/body_schema.json",
|
||||
"name" : "My Cool Planet",
|
||||
"Volumes": {
|
||||
"oxygenVolumes": [
|
||||
{
|
||||
"position": {"x": 399.4909, "y": -1.562098, "z": 20.11444},
|
||||
"radius": 30,
|
||||
"treeVolume": true,
|
||||
"playRefillAudio": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Listing out every type of volume is outside the scope of this guide, but you can see every supported type of volume and the properties they need in [the VolumesModule schema](/schemas/body-schema/defs/volumesmodule/).
|
||||
|
||||
## Volume Shapes
|
||||
|
||||
By default, volumes are spherical, and you can specify the radius of that sphere with the `radius` property. If you want to use a different shape for your volume, such as a box or capsule, you can specify your volume's `shape` like so:
|
||||
|
||||
```json title="planets/My Cool Planet.json"
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/Outer-Wilds-New-Horizons/new-horizons/main/NewHorizons/Schemas/body_schema.json",
|
||||
"name" : "My Cool Planet",
|
||||
"Volumes": {
|
||||
"forces": {
|
||||
"directionalVolumes": [
|
||||
{
|
||||
"rename": "ArtificialGravitySurface",
|
||||
"force": 8,
|
||||
"playGravityCrystalAudio": true,
|
||||
"shape": {
|
||||
"type": "box",
|
||||
"size": {
|
||||
"x": 15.0,
|
||||
"y": 10.0,
|
||||
"z": 5.0
|
||||
},
|
||||
"offset": {
|
||||
"x": 0,
|
||||
"y": 5.0,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
"position": { "x": 0, "y": -110, "z": 0 },
|
||||
"rotation": { "x": 180, "y": 0, "z": 0 }
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The supported shape types are: `sphere`, `box`, `capsule`, `cylinder`, `cone`, `hemisphere`, `hemicapsule`, and `ring`. See [the ShapeInfo schema](/schemas/body-schema/defs/shapeinfo/) for the full list of properties available to define each shape.
|
||||
|
||||
Note that `sphere`, `box`, and `capsule` shapes are more reliable and efficient than other shapes, so prefer using them whenever possible.
|
||||
|
||||
### Debugging
|
||||
|
||||
To visualize the shapes of your volumes in-game, use the [Collider Visualizer mod](https://outerwildsmods.com/mods/collidervisualizer/). It will display a wireframe of the shapes around you so you can see precisely where they are and reposition or resize them as needed.
|
||||
Loading…
x
Reference in New Issue
Block a user