Merge branch 'dev' into hawkbar-colliders

This commit is contained in:
Noah Pilarski 2025-04-19 00:20:33 -04:00
commit 550f992398
18 changed files with 630 additions and 119 deletions

View File

@ -473,11 +473,11 @@ namespace NewHorizons.Builder.Props
{
// These flood toggles are to disable flooded docks on the Stranger
// Presumably the user isn't making one of those
foreach (var toggle in dock.GetComponents<FloodToggle>().Concat(dock.GetComponentsInChildren<FloodToggle>()))
foreach (var toggle in dock.GetComponentsInChildren<FloodToggle>())
{
Component.DestroyImmediate(toggle);
}
foreach (var floodSensor in dock.GetComponents<RingRiverFloodSensor>().Concat(dock.GetComponentsInChildren<RingRiverFloodSensor>()))
foreach (var floodSensor in dock.GetComponentsInChildren<RingRiverFloodSensor>())
{
Component.DestroyImmediate(floodSensor);
}

View File

@ -0,0 +1,180 @@
using NewHorizons.Components.Props;
using NewHorizons.External.Modules.Props.EchoesOfTheEye;
using NewHorizons.Handlers;
using NewHorizons.Utility;
using NewHorizons.Utility.OWML;
using System.Collections.Generic;
using UnityEngine;
namespace NewHorizons.Builder.Props.EchoesOfTheEye
{
public static class RaftBuilder
{
private static GameObject _prefab;
private static GameObject _cleanPrefab;
internal static void InitPrefab()
{
if (_prefab == null)
{
_prefab = Object.FindObjectOfType<RaftController>()?.gameObject?.InstantiateInactive()?.Rename("Raft_Body_Prefab")?.DontDestroyOnLoad();
if (_prefab == null)
{
NHLogger.LogWarning($"Tried to make a raft but couldn't. Do you have the DLC installed?");
return;
}
else
{
_prefab.AddComponent<DestroyOnDLC>()._destroyOnDLCNotOwned = true;
var raftController = _prefab.GetComponent<RaftController>();
if (raftController._sector != null)
{
// Since awake already ran we have to unhook these events
raftController._sector.OnOccupantEnterSector -= raftController.OnOccupantEnterSector;
raftController._sector.OnOccupantExitSector -= raftController.OnOccupantExitSector;
raftController._sector = null;
}
raftController._riverFluid = null;
foreach (var lightSensor in _prefab.GetComponentsInChildren<SingleLightSensor>())
{
if (lightSensor._sector != null)
{
lightSensor._sector.OnSectorOccupantsUpdated -= lightSensor.OnSectorOccupantsUpdated;
lightSensor._sector = null;
}
lightSensor._detectDreamLanterns = true;
lightSensor._lanternFocusThreshold = 0.9f;
lightSensor._illuminatingDreamLanternList = new List<DreamLanternController>();
lightSensor._lightSourceMask |= LightSourceType.DREAM_LANTERN;
}
// TODO: Change to one mesh
var twRaftRoot = new GameObject("Effects_IP_SIM_Raft");
twRaftRoot.transform.SetParent(_prefab.transform, false);
var twRaft = SearchUtilities.Find("DreamWorld_Body/Sector_DreamWorld/Interactibles_Dreamworld/DreamRaft_Body/Effects_IP_SIM_Raft_1Way")
.Instantiate(Vector3.zero, Quaternion.identity, twRaftRoot.transform).Rename("Effects_IP_SIM_Raft_1Way");
twRaft.Instantiate(Vector3.zero, Quaternion.Euler(0, 180, 0), twRaftRoot.transform).Rename(twRaft.name);
twRaft.Instantiate(Vector3.zero, Quaternion.Euler(0, 90, 0), twRaftRoot.transform).Rename(twRaft.name);
twRaft.Instantiate(Vector3.zero, Quaternion.Euler(0, -90, 0), twRaftRoot.transform).Rename(twRaft.name);
}
}
if (_cleanPrefab == null && _prefab != null)
{
_cleanPrefab = _prefab?.InstantiateInactive()?.Rename("Raft_Body_Prefab_Clean")?.DontDestroyOnLoad();
if (_cleanPrefab == null)
{
NHLogger.LogWarning($"Tried to make a raft but couldn't. Do you have the DLC installed?");
return;
}
else
{
var raftController = _cleanPrefab.GetComponent<RaftController>();
var rwRaft = _cleanPrefab.FindChild("Structure_IP_Raft");
var rwRaftAnimator = rwRaft.GetComponent<Animator>();
var rac = rwRaftAnimator.runtimeAnimatorController;
Object.DestroyImmediate(rwRaft);
var dwRaft = SearchUtilities.Find("DreamWorld_Body/Sector_DreamWorld/Interactibles_Dreamworld/DreamRaft_Body/Structure_IP_Raft")
.Instantiate(Vector3.zero, Quaternion.identity, _cleanPrefab.transform).Rename("Structure_IP_DreamRaft");
dwRaft.transform.SetSiblingIndex(3);
foreach (var child in dwRaft.GetAllChildren())
{
child.SetActive(true);
}
var dwRaftAnimator = dwRaft.AddComponent<Animator>();
dwRaftAnimator.runtimeAnimatorController = rac;
raftController._railingAnimator = dwRaftAnimator;
var dwLightSensorForward = SearchUtilities.Find("DreamWorld_Body/Sector_DreamWorld/Interactibles_Dreamworld/DreamRaft_Body/LightSensor_Forward");
var dwLightSensorOrigMaterial = dwLightSensorForward.GetComponent<LightSensorEffects>()._origMaterial;
var dwLightSensor = dwLightSensorForward.FindChild("Structure_IP_Raft_Sensor");
ChangeSensor(_cleanPrefab.FindChild("LightSensorRoot/LightSensor_Forward"), dwLightSensorOrigMaterial, dwLightSensor);
ChangeSensor(_cleanPrefab.FindChild("LightSensorRoot/LightSensor_Right"), dwLightSensorOrigMaterial, dwLightSensor);
ChangeSensor(_cleanPrefab.FindChild("LightSensorRoot/LightSensor_Rear"), dwLightSensorOrigMaterial, dwLightSensor, true);
ChangeSensor(_cleanPrefab.FindChild("LightSensorRoot/LightSensor_Left"), dwLightSensorOrigMaterial, dwLightSensor);
}
}
}
private static void ChangeSensor(GameObject lightSensor, Material origMaterial, GameObject newLightSensor, bool reverse = false)
{
var singleLightSensor = lightSensor.GetComponent<SingleLightSensor>();
var lightSensorEffects = lightSensor.GetComponent<LightSensorEffects>();
lightSensorEffects._lightSensor = singleLightSensor;
Object.DestroyImmediate(lightSensor.FindChild("Structure_IP_Raft_Sensor"));
lightSensorEffects._origMaterial = origMaterial;
var copiedLightSensor = newLightSensor
.Instantiate(reverse ? new Vector3(0, -1.5f, -0.1303f) : new Vector3(0, -1.5f, -0.0297f),
reverse ? Quaternion.identity : Quaternion.Euler(0, 180, 0),
lightSensor.transform).Rename("Structure_IP_DreamRaft_Sensor");
var bulb = copiedLightSensor.FindChild("Props_IP_Raft_Lamp_geoBulb");
lightSensorEffects._renderer = bulb.GetComponent<MeshRenderer>();
lightSensorEffects._lightRenderer = bulb.GetComponent<OWRenderer>();
}
public static GameObject Make(GameObject planetGO, Sector sector, RaftInfo info, OWRigidbody planetBody)
{
InitPrefab();
if (_prefab == null || _cleanPrefab == null || sector == null) return null;
GameObject raftObject = GeneralPropBuilder.MakeFromPrefab(info.pristine ? _cleanPrefab : _prefab, "Raft_Body", planetGO, sector, info);
StreamingHandler.SetUpStreaming(raftObject, sector);
var raftController = raftObject.GetComponent<RaftController>();
raftController._sector = sector;
raftController._acceleration = info.acceleration;
sector.OnOccupantEnterSector += raftController.OnOccupantEnterSector;
sector.OnOccupantExitSector += raftController.OnOccupantExitSector;
// Detectors
var fluidDetector = raftObject.transform.Find("Detector_Raft").GetComponent<RaftFluidDetector>();
var waterVolume = planetGO.GetComponentInChildren<RadialFluidVolume>();
fluidDetector._alignmentFluid = waterVolume;
fluidDetector._buoyancy.checkAgainstWaves = true;
// Rafts were unable to trigger docks because these were disabled for some reason
fluidDetector.GetComponent<BoxShape>().enabled = true;
fluidDetector.GetComponent<OWCollider>().enabled = true;
// Light sensors
foreach (var lightSensor in raftObject.GetComponentsInChildren<SingleLightSensor>())
{
lightSensor._sector = sector;
sector.OnSectorOccupantsUpdated += lightSensor.OnSectorOccupantsUpdated;
}
var nhRaftController = raftObject.AddComponent<NHRaftController>();
var achievementObject = new GameObject("AchievementVolume");
achievementObject.transform.SetParent(raftObject.transform, false);
var shape = achievementObject.AddComponent<SphereShape>();
shape.radius = 3;
shape.SetCollisionMode(Shape.CollisionMode.Volume);
achievementObject.AddComponent<OWTriggerVolume>()._shape = shape;
achievementObject.AddComponent<OtherMods.AchievementsPlus.NH.RaftingAchievement>();
raftObject.SetActive(true);
if (planetGO != null && !string.IsNullOrEmpty(info.dockPath))
{
var dockTransform = planetGO.transform.Find(info.dockPath);
if (dockTransform != null && dockTransform.TryGetComponent(out RaftDock raftDock))
{
raftController.SkipSuspendOnStart();
raftDock._startRaft = raftController;
raftDock._raft = raftController;
}
else
{
NHLogger.LogError($"Cannot find raft dock object at path: {planetGO.name}/{info.dockPath}");
}
}
return raftObject;
}
}
}

View File

@ -0,0 +1,55 @@
using NewHorizons.Components.Props;
using NewHorizons.External.Modules.Props;
using NewHorizons.External.Modules.Props.EchoesOfTheEye;
using NewHorizons.Handlers;
using NewHorizons.Utility;
using NewHorizons.Utility.OWML;
using OWML.Common;
using UnityEngine;
namespace NewHorizons.Builder.Props.EchoesOfTheEye
{
public static class RaftDockBuilder
{
private static GameObject _prefab;
internal static void InitPrefab()
{
if (_prefab == null)
{
_prefab = SearchUtilities.Find("RingWorld_Body/Sector_RingInterior/Sector_Zone1/Structures_Zone1/RaftHouse_ArrivalPatio_Zone1/Interactables_RaftHouse_ArrivalPatio_Zone1/Prefab_IP_RaftDock").InstantiateInactive().Rename("Prefab_RaftDock").DontDestroyOnLoad();
if (_prefab == null)
{
NHLogger.LogWarning($"Tried to make a raft dock but couldn't. Do you have the DLC installed?");
return;
}
else
{
_prefab.AddComponent<DestroyOnDLC>()._destroyOnDLCNotOwned = true;
var raftDock = _prefab.GetComponent<RaftDock>();
raftDock._startRaft = null;
raftDock._floodSensor = null;
foreach (var floodToggle in _prefab.GetComponents<FloodToggle>())
{
Component.DestroyImmediate(floodToggle);
}
Object.DestroyImmediate(_prefab.FindChild("FloodSensor"));
Object.DestroyImmediate(_prefab.FindChild("FloodSensor_RaftHouseArrivalPatio_NoDelay"));
}
}
}
public static GameObject Make(GameObject planetGO, Sector sector, RaftDockInfo info, IModBehaviour mod)
{
InitPrefab();
if (_prefab == null || sector == null) return null;
var dockObject = DetailBuilder.Make(planetGO, sector, mod, _prefab, new DetailInfo(info));
//var raftDock = dockObject.GetComponent<RaftDock>();
return dockObject;
}
}
}

View File

@ -114,6 +114,7 @@ namespace NewHorizons.Builder.Props
MakeGeneralProps(go, config.Props.grappleTotems, (totem) => GrappleTotemBuilder.Make(go, sector, totem, mod));
MakeGeneralProps(go, config.Props.dreamCampfires, (campfire) => DreamCampfireBuilder.Make(go, sector, campfire, mod), (campfire) => campfire.id);
MakeGeneralProps(go, config.Props.dreamArrivalPoints, (point) => DreamArrivalPointBuilder.Make(go, sector, point, mod), (point) => point.id);
MakeGeneralProps(go, config.Props.raftDocks, (dock) => RaftDockBuilder.Make(go, sector, dock, mod));
MakeGeneralProps(go, config.Props.rafts, (raft) => RaftBuilder.Make(go, sector, raft, planetBody));
}
MakeGeneralProps(go, config.Props.tornados, (tornado) => TornadoBuilder.Make(go, sector, tornado, config.Atmosphere?.clouds != null));

View File

@ -1,97 +0,0 @@
using NewHorizons.Components.Props;
using NewHorizons.External.Modules.Props.EchoesOfTheEye;
using NewHorizons.Handlers;
using NewHorizons.Utility;
using NewHorizons.Utility.OWML;
using UnityEngine;
namespace NewHorizons.Builder.Props
{
public static class RaftBuilder
{
private static GameObject _prefab;
internal static void InitPrefab()
{
if (_prefab == null)
{
_prefab = Object.FindObjectOfType<RaftController>()?.gameObject?.InstantiateInactive()?.Rename("Raft_Body_Prefab")?.DontDestroyOnLoad();
if (_prefab == null)
{
NHLogger.LogWarning($"Tried to make a raft but couldn't. Do you have the DLC installed?");
return;
}
else
{
_prefab.AddComponent<DestroyOnDLC>()._destroyOnDLCNotOwned = true;
var raftController = _prefab.GetComponent<RaftController>();
if (raftController._sector != null)
{
// Since awake already ran we have to unhook these events
raftController._sector.OnOccupantEnterSector -= raftController.OnOccupantEnterSector;
raftController._sector.OnOccupantExitSector -= raftController.OnOccupantExitSector;
raftController._sector = null;
}
raftController._riverFluid = null;
foreach (var lightSensor in _prefab.GetComponentsInChildren<SingleLightSensor>())
{
if (lightSensor._sector != null)
{
lightSensor._sector.OnSectorOccupantsUpdated -= lightSensor.OnSectorOccupantsUpdated;
lightSensor._sector = null;
}
}
}
}
}
public static GameObject Make(GameObject planetGO, Sector sector, RaftInfo info, OWRigidbody planetBody)
{
InitPrefab();
if (_prefab == null || sector == null) return null;
GameObject raftObject = GeneralPropBuilder.MakeFromPrefab(_prefab, "Raft_Body", planetGO, sector, info);
StreamingHandler.SetUpStreaming(raftObject, sector);
var raftController = raftObject.GetComponent<RaftController>();
raftController._sector = sector;
raftController._acceleration = info.acceleration;
sector.OnOccupantEnterSector += raftController.OnOccupantEnterSector;
sector.OnOccupantExitSector += raftController.OnOccupantExitSector;
// Detectors
var fluidDetector = raftObject.transform.Find("Detector_Raft").GetComponent<RaftFluidDetector>();
var waterVolume = planetGO.GetComponentInChildren<RadialFluidVolume>();
fluidDetector._alignmentFluid = waterVolume;
fluidDetector._buoyancy.checkAgainstWaves = true;
// Rafts were unable to trigger docks because these were disabled for some reason
fluidDetector.GetComponent<BoxShape>().enabled = true;
fluidDetector.GetComponent<OWCollider>().enabled = true;
// Light sensors
foreach (var lightSensor in raftObject.GetComponentsInChildren<SingleLightSensor>())
{
lightSensor._sector = sector;
sector.OnSectorOccupantsUpdated += lightSensor.OnSectorOccupantsUpdated;
}
var nhRaftController = raftObject.AddComponent<NHRaftController>();
var achievementObject = new GameObject("AchievementVolume");
achievementObject.transform.SetParent(raftObject.transform, false);
var shape = achievementObject.AddComponent<SphereShape>();
shape.radius = 3;
shape.SetCollisionMode(Shape.CollisionMode.Volume);
achievementObject.AddComponent<OWTriggerVolume>()._shape = shape;
achievementObject.AddComponent<OtherMods.AchievementsPlus.NH.RaftingAchievement>();
raftObject.SetActive(true);
return raftObject;
}
}
}

View File

@ -471,7 +471,7 @@ namespace NewHorizons.Builder.Props.TranslatorText
if (info.arcInfo != null && info.arcInfo.Count() != dict.Values.Count())
{
NHLogger.LogError($"Can't make NomaiWallText, arcInfo length [{info.arcInfo.Count()}] doesn't equal number of TextBlocks [{dict.Values.Count()}] in the xml");
NHLogger.LogError($"Can't make NomaiWallText [{info.xmlFile}], arcInfo length [{info.arcInfo.Count()}] doesn't equal number of TextBlocks [{dict.Values.Count()}] in the xml");
return;
}

View File

@ -0,0 +1,20 @@
using NewHorizons.Components.Volumes;
using NewHorizons.External.Modules.Volumes.VolumeInfos;
using UnityEngine;
namespace NewHorizons.Builder.Volumes
{
public static class SpeedLimiterVolumeBuilder
{
public static SpeedLimiterVolume Make(GameObject planetGO, Sector sector, SpeedLimiterVolumeInfo info)
{
var volume = VolumeBuilder.Make<SpeedLimiterVolume>(planetGO, sector, info);
volume.maxSpeed = info.maxSpeed;
volume.stoppingDistance = info.stoppingDistance;
volume.maxEntryAngle = info.maxEntryAngle;
return volume;
}
}
}

View File

@ -229,6 +229,13 @@ namespace NewHorizons.Builder.Volumes
SpeedTrapVolumeBuilder.Make(go, sector, speedTrapVolume);
}
}
if (config.Volumes.speedLimiterVolumes != null)
{
foreach (var speedLimiterVolume in config.Volumes.speedLimiterVolumes)
{
SpeedLimiterVolumeBuilder.Make(go, sector, speedLimiterVolume);
}
}
if (config.Volumes.lightSourceVolumes != null)
{
foreach (var lightSourceVolume in config.Volumes.lightSourceVolumes)

View File

@ -0,0 +1,164 @@
using System.Collections.Generic;
using System;
using UnityEngine;
namespace NewHorizons.Components.Volumes
{
public class SpeedLimiterVolume : BaseVolume
{
public float maxSpeed = 10f;
public float stoppingDistance = 100f;
public float maxEntryAngle = 60f;
private OWRigidbody _parentBody;
private List<TrackedBody> _trackedBodies = new List<TrackedBody>();
private bool _playerJustExitedDream;
public override void Awake()
{
_parentBody = GetComponentInParent<OWRigidbody>();
base.Awake();
GlobalMessenger.AddListener("ExitDreamWorld", OnExitDreamWorld);
}
public void Start()
{
enabled = false;
}
public override void OnDestroy()
{
base.OnDestroy();
GlobalMessenger.RemoveListener("ExitDreamWorld", OnExitDreamWorld);
}
public void FixedUpdate()
{
foreach (var trackedBody in _trackedBodies)
{
bool slowed = false;
Vector3 velocity = trackedBody.body.GetVelocity() - _parentBody.GetVelocity();
float magnitude = velocity.magnitude;
if (magnitude <= maxSpeed)
{
slowed = true;
}
else
{
bool needsSlowing = true;
float velocityReduction = trackedBody.deceleration * Time.deltaTime;
float requiredReduction = maxSpeed - magnitude;
if (requiredReduction > velocityReduction)
{
velocityReduction = requiredReduction;
slowed = true;
}
if (trackedBody.name == Detector.Name.Ship)
{
Autopilot component = Locator.GetShipTransform().GetComponent<Autopilot>();
if (component != null && component.IsFlyingToDestination())
{
needsSlowing = false;
}
}
if (needsSlowing)
{
Vector3 velocityChange = velocityReduction * velocity.normalized;
trackedBody.body.AddVelocityChange(velocityChange);
if (trackedBody.name == Detector.Name.Ship && PlayerState.IsInsideShip())
{
Locator.GetPlayerBody().AddVelocityChange(velocityChange);
}
}
}
if (slowed)
{
if (trackedBody.name == Detector.Name.Ship)
GlobalMessenger.FireEvent("ShipExitSpeedLimiter");
_trackedBodies.Remove(trackedBody);
if (_trackedBodies.Count == 0)
enabled = false;
}
}
}
private void OnExitDreamWorld()
{
_playerJustExitedDream = true;
}
public override void OnTriggerVolumeEntry(GameObject hitObj)
{
DynamicForceDetector component = hitObj.GetComponent<DynamicForceDetector>();
if (component == null || !component.CompareNameMask(Detector.Name.Player | Detector.Name.Probe | Detector.Name.Ship)) return;
if (component.GetName() == Detector.Name.Player && (PlayerState.IsInsideShip() || _playerJustExitedDream))
{
_playerJustExitedDream = false;
}
else
{
OWRigidbody attachedOWRigidbody = component.GetAttachedOWRigidbody();
Vector3 from = transform.position - attachedOWRigidbody.GetPosition();
Vector3 to = attachedOWRigidbody.GetVelocity() - _parentBody.GetVelocity();
float magnitude = to.magnitude;
if (magnitude > maxSpeed && Vector3.Angle(from, to) < maxEntryAngle)
{
float deceleration = (maxSpeed * maxSpeed - magnitude * magnitude) / (2f * stoppingDistance);
TrackedBody trackedBody = new TrackedBody(attachedOWRigidbody, component.GetName(), deceleration);
_trackedBodies.Add(trackedBody);
if (component.GetName() == Detector.Name.Ship)
GlobalMessenger.FireEvent("ShipEnterSpeedLimiter");
enabled = true;
}
}
}
public override void OnTriggerVolumeExit(GameObject hitObj)
{
DynamicForceDetector component = hitObj.GetComponent<DynamicForceDetector>();
if (component == null) return;
OWRigidbody body = component.GetAttachedOWRigidbody();
TrackedBody trackedBody = _trackedBodies.Find((TrackedBody i) => i.body == body);
if (trackedBody != null)
{
if (trackedBody.name == Detector.Name.Ship)
GlobalMessenger.FireEvent("ShipExitSpeedLimiter");
_trackedBodies.Remove(trackedBody);
if (_trackedBodies.Count == 0)
enabled = false;
}
}
[Serializable]
protected class TrackedBody
{
public OWRigidbody body;
public Detector.Name name;
public float deceleration;
public TrackedBody(OWRigidbody body, Detector.Name name, float deceleration)
{
this.body = body;
this.name = name;
this.deceleration = deceleration;
}
public override bool Equals(object obj)
{
if (obj is TrackedBody trackedBody)
{
return trackedBody.body == body && trackedBody.name == name;
}
return base.Equals(obj);
}
public override int GetHashCode() => body.GetHashCode();
}
}
}

View File

@ -58,6 +58,11 @@ namespace NewHorizons.External.Modules
/// </summary>
public RaftInfo[] rafts;
/// <summary>
/// Add raft docks to this planet (requires Echoes of the Eye DLC)
/// </summary>
public RaftDockInfo[] raftDocks;
/// <summary>
/// Scatter props around this planet's surface
/// </summary>

View File

@ -0,0 +1,10 @@
using Newtonsoft.Json;
using System.ComponentModel;
namespace NewHorizons.External.Modules.Props.EchoesOfTheEye
{
[JsonObject]
public class RaftDockInfo : GeneralPropInfo
{
}
}

View File

@ -10,6 +10,16 @@ namespace NewHorizons.External.Modules.Props.EchoesOfTheEye
/// Acceleration of the raft. Default acceleration is 5.
/// </summary>
[DefaultValue(5f)] public float acceleration = 5f;
/// <summary>
/// Path to the dock this raft will start attached to.
/// </summary>
public string dockPath;
/// <summary>
/// Uses the raft model from the dreamworld
/// </summary>
public bool pristine;
}
}

View File

@ -0,0 +1,28 @@
using Newtonsoft.Json;
using System.ComponentModel;
using UnityEngine;
namespace NewHorizons.External.Modules.Volumes.VolumeInfos
{
[JsonObject]
public class SpeedLimiterVolumeInfo : VolumeInfo
{
/// <summary>
/// The speed the volume will slow you down to when you enter it.
/// </summary>
[DefaultValue(10f)]
public float maxSpeed = 10f;
/// <summary>
/// The distance from the outside of the volume that the limiter slows you down to max speed at.
/// </summary>
[DefaultValue(100f)]
public float stoppingDistance = 100f;
/// <summary>
/// The maximum angle (in degrees) between the direction the incoming object is moving relative to the volume's center and the line from the object toward the center of the volume, within which the speed limiter will activate.
/// </summary>
[DefaultValue(60f)]
public float maxEntryAngle = 60f;
}
}

View File

@ -106,6 +106,13 @@ namespace NewHorizons.External.Modules.Volumes
/// </summary>
public SpeedTrapVolumeInfo[] speedTrapVolumes;
/// <summary>
/// Add speed limiter volumes to this planet.
/// Slows down the player, ship, and probe when they enter this volume.
/// Used on the Stranger in DLC.
/// </summary>
public SpeedLimiterVolumeInfo[] speedLimiterVolumes;
/// <summary>
/// Add visor effect volumes to this planet.
/// </summary>

View File

@ -382,6 +382,7 @@ namespace NewHorizons
ProjectionBuilder.InitPrefabs();
CloakBuilder.InitPrefab();
RaftBuilder.InitPrefab();
RaftDockBuilder.InitPrefab();
DreamCampfireBuilder.InitPrefab();
DreamArrivalPointBuilder.InitPrefab();
}

View File

@ -75,30 +75,41 @@ namespace NewHorizons.Patches.EchoesOfTheEyePatches
return false;
}
[HarmonyPostfix]
[HarmonyPrefix]
[HarmonyPatch(nameof(RaftController.UpdateMoveToTarget))]
public static void UpdateMoveToTarget(RaftController __instance)
public static bool UpdateMoveToTarget(RaftController __instance)
{
OWRigidbody raftBody = __instance._raftBody;
OWRigidbody origParentBody = raftBody.GetOrigParentBody();
Transform transform = origParentBody.transform;
Vector3 position = transform.TransformPoint(__instance._targetLocalPosition);
Vector3 distance = position - raftBody.GetPosition();
float speed = Mathf.Min(__instance._targetSpeed, distance.magnitude / Time.deltaTime);
Vector3 pointVelocity = raftBody.GetOrigParentBody().GetPointVelocity(raftBody.GetPosition());
raftBody.SetVelocity(pointVelocity + (distance.normalized * speed));
float t = (__instance.currentDistanceLerp = Mathf.InverseLerp(__instance._startDistance, 0.001f, distance.magnitude));
var st = Mathf.SmoothStep(0f, 1f, t);
// If it has a riverFluid volume then its a regular stranger one
if (__instance._movingToTarget && __instance._riverFluid == null)
var isStranger = __instance._riverFluid != null;
// Base game threshold has this at 1f (after doing smoothstep on it)
// For whatever reason it never hits that for NH planets (probably since they're moving so much compared to the steady velocity of the Stranger)
// Might break for somebody with a wacky spinning planet in which case we can adjust this or add some kind of fallback (i.e., wait x seconds and then just say its there)
// Fixes #1005
if (isStranger ? (st >= 1f) : (t > 0.999f))
{
OWRigidbody raftBody = __instance._raftBody;
OWRigidbody origParentBody = __instance._raftBody.GetOrigParentBody();
Transform transform = origParentBody.transform;
Vector3 vector = transform.TransformPoint(__instance._targetLocalPosition);
// Base game threshold has this at 1f (after doing smoothstep on it)
// For whatever reason it never hits that for NH planets (probably since they're moving so much compared to the steady velocity of the Stranger)
// Might break for somebody with a wacky spinning planet in which case we can adjust this or add some kind of fallback (i.e., wait x seconds and then just say its there)
// Fixes #1005
if (__instance.currentDistanceLerp > 0.999f)
{
raftBody.SetPosition(vector);
raftBody.SetRotation(transform.rotation * __instance._targetLocalRotation);
__instance.StopMovingToTarget();
__instance.OnArriveAtTarget.Invoke();
}
raftBody.SetPosition(position);
raftBody.SetRotation(transform.rotation * __instance._targetLocalRotation);
__instance.StopMovingToTarget();
__instance.OnArriveAtTarget.Invoke();
}
else
{
Quaternion quaternion = Quaternion.Slerp(__instance._startLocalRotation, __instance._targetLocalRotation, st);
Quaternion toRotation = transform.rotation * quaternion;
Vector3 vector4 = OWPhysics.FromToAngularVelocity(raftBody.GetRotation(), toRotation);
raftBody.SetAngularVelocity(origParentBody.GetAngularVelocity() + vector4);
}
return false;
}
}
}

View File

@ -2231,6 +2231,13 @@
"$ref": "#/definitions/RaftInfo"
}
},
"raftDocks": {
"type": "array",
"description": "Add raft docks to this planet (requires Echoes of the Eye DLC)",
"items": {
"$ref": "#/definitions/RaftDockInfo"
}
},
"scatter": {
"type": "array",
"description": "Scatter props around this planet's surface",
@ -2813,6 +2820,47 @@
"description": "Acceleration of the raft. Default acceleration is 5.",
"format": "float",
"default": 5.0
},
"dockPath": {
"type": "string",
"description": "Path to the dock this raft will start attached to."
},
"pristine": {
"type": "boolean",
"description": "Uses the raft model from the dreamworld"
}
}
},
"RaftDockInfo": {
"type": "object",
"additionalProperties": false,
"properties": {
"rotation": {
"description": "Rotation of the object",
"$ref": "#/definitions/MVector3"
},
"alignRadial": {
"type": [
"boolean",
"null"
],
"description": "Do we try to automatically align this object to stand upright relative to the body's center? Stacks with rotation.\nDefaults to true for geysers, tornados, and volcanoes, and false for everything else."
},
"position": {
"description": "Position of the object",
"$ref": "#/definitions/MVector3"
},
"isRelativeToParent": {
"type": "boolean",
"description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object."
},
"parentPath": {
"type": "string",
"description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)."
},
"rename": {
"type": "string",
"description": "An optional rename of this object"
}
}
},
@ -5427,6 +5475,13 @@
"$ref": "#/definitions/SpeedTrapVolumeInfo"
}
},
"speedLimiterVolumes": {
"type": "array",
"description": "Add speed limiter volumes to this planet.\nSlows down the player, ship, and probe when they enter this volume.\nUsed on the Stranger in DLC.",
"items": {
"$ref": "#/definitions/SpeedLimiterVolumeInfo"
}
},
"visorEffects": {
"description": "Add visor effect volumes to this planet.",
"$ref": "#/definitions/VisorEffectModule"
@ -7039,6 +7094,52 @@
}
}
},
"SpeedLimiterVolumeInfo": {
"type": "object",
"additionalProperties": false,
"properties": {
"radius": {
"type": "number",
"description": "The radius of this volume.",
"format": "float",
"default": 1.0
},
"position": {
"description": "Position of the object",
"$ref": "#/definitions/MVector3"
},
"isRelativeToParent": {
"type": "boolean",
"description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object."
},
"parentPath": {
"type": "string",
"description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)."
},
"rename": {
"type": "string",
"description": "An optional rename of this object"
},
"maxSpeed": {
"type": "number",
"description": "The speed the volume will slow you down to when you enter it.",
"format": "float",
"default": 10.0
},
"stoppingDistance": {
"type": "number",
"description": "The distance from the outside of the volume that the limiter slows you down to max speed at.",
"format": "float",
"default": 100.0
},
"maxEntryAngle": {
"type": "number",
"description": "The maximum angle (in degrees) between the direction the incoming object is moving relative to the volume's center and the line from the object toward the center of the volume, within which the speed limiter will activate.",
"format": "float",
"default": 60.0
}
}
},
"VisorEffectModule": {
"type": "object",
"additionalProperties": false,

View File

@ -274,6 +274,14 @@ namespace NewHorizons.Utility
return UnityEngine.Object.Instantiate(original);
}
public static GameObject Instantiate(this GameObject original, Vector3 localPosition, Quaternion localRotation, Transform parent)
{
var copy = UnityEngine.Object.Instantiate(original, parent, false);
copy.transform.localPosition = localPosition;
copy.transform.localRotation = localRotation;
return copy;
}
public static T DontDestroyOnLoad<T>(this T target) where T : UnityEngine.Object
{
UnityEngine.Object.DontDestroyOnLoad(target);