From 50ccd32b8e15135d9c16bb85a8e323ee46418708 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Mon, 7 Apr 2025 18:32:44 -0400 Subject: [PATCH 01/12] add docks --- .../Props/{ => EchoesOfTheEye}/RaftBuilder.cs | 17 ++++++- .../Props/EchoesOfTheEye/RaftDockBuilder.cs | 51 +++++++++++++++++++ NewHorizons/Builder/Props/PropBuildManager.cs | 1 + NewHorizons/External/Modules/PropModule.cs | 5 ++ .../Props/EchoesOfTheEye/RaftDockInfo.cs | 10 ++++ .../Modules/Props/EchoesOfTheEye/RaftInfo.cs | 5 ++ NewHorizons/Main.cs | 1 + 7 files changed, 89 insertions(+), 1 deletion(-) rename NewHorizons/Builder/Props/{ => EchoesOfTheEye}/RaftBuilder.cs (85%) create mode 100644 NewHorizons/Builder/Props/EchoesOfTheEye/RaftDockBuilder.cs create mode 100644 NewHorizons/External/Modules/Props/EchoesOfTheEye/RaftDockInfo.cs diff --git a/NewHorizons/Builder/Props/RaftBuilder.cs b/NewHorizons/Builder/Props/EchoesOfTheEye/RaftBuilder.cs similarity index 85% rename from NewHorizons/Builder/Props/RaftBuilder.cs rename to NewHorizons/Builder/Props/EchoesOfTheEye/RaftBuilder.cs index 59f734bf..5747c71b 100644 --- a/NewHorizons/Builder/Props/RaftBuilder.cs +++ b/NewHorizons/Builder/Props/EchoesOfTheEye/RaftBuilder.cs @@ -5,7 +5,7 @@ using NewHorizons.Utility; using NewHorizons.Utility.OWML; using UnityEngine; -namespace NewHorizons.Builder.Props +namespace NewHorizons.Builder.Props.EchoesOfTheEye { public static class RaftBuilder { @@ -91,6 +91,21 @@ namespace NewHorizons.Builder.Props 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; } } diff --git a/NewHorizons/Builder/Props/EchoesOfTheEye/RaftDockBuilder.cs b/NewHorizons/Builder/Props/EchoesOfTheEye/RaftDockBuilder.cs new file mode 100644 index 00000000..834e82ad --- /dev/null +++ b/NewHorizons/Builder/Props/EchoesOfTheEye/RaftDockBuilder.cs @@ -0,0 +1,51 @@ +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()._destroyOnDLCNotOwned = true; + var raftDock = _prefab.GetComponent(); + raftDock._startRaft = null; + raftDock._floodSensor = null; + 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(); + + return dockObject; + } + } +} diff --git a/NewHorizons/Builder/Props/PropBuildManager.cs b/NewHorizons/Builder/Props/PropBuildManager.cs index 579d5e21..fb8d21c7 100644 --- a/NewHorizons/Builder/Props/PropBuildManager.cs +++ b/NewHorizons/Builder/Props/PropBuildManager.cs @@ -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)); diff --git a/NewHorizons/External/Modules/PropModule.cs b/NewHorizons/External/Modules/PropModule.cs index 4a162bd8..1f7806ef 100644 --- a/NewHorizons/External/Modules/PropModule.cs +++ b/NewHorizons/External/Modules/PropModule.cs @@ -58,6 +58,11 @@ namespace NewHorizons.External.Modules /// public RaftInfo[] rafts; + /// + /// Add raft docks to this planet (requires Echoes of the Eye DLC) + /// + public RaftDockInfo[] raftDocks; + /// /// Scatter props around this planet's surface /// diff --git a/NewHorizons/External/Modules/Props/EchoesOfTheEye/RaftDockInfo.cs b/NewHorizons/External/Modules/Props/EchoesOfTheEye/RaftDockInfo.cs new file mode 100644 index 00000000..37730991 --- /dev/null +++ b/NewHorizons/External/Modules/Props/EchoesOfTheEye/RaftDockInfo.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; +using System.ComponentModel; + +namespace NewHorizons.External.Modules.Props.EchoesOfTheEye +{ + [JsonObject] + public class RaftDockInfo : GeneralPropInfo + { + } +} diff --git a/NewHorizons/External/Modules/Props/EchoesOfTheEye/RaftInfo.cs b/NewHorizons/External/Modules/Props/EchoesOfTheEye/RaftInfo.cs index d248679e..61c3a1dd 100644 --- a/NewHorizons/External/Modules/Props/EchoesOfTheEye/RaftInfo.cs +++ b/NewHorizons/External/Modules/Props/EchoesOfTheEye/RaftInfo.cs @@ -10,6 +10,11 @@ namespace NewHorizons.External.Modules.Props.EchoesOfTheEye /// Acceleration of the raft. Default acceleration is 5. /// [DefaultValue(5f)] public float acceleration = 5f; + + /// + /// Path to the dock this raft will start attached to. + /// + public string dockPath; } } diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index f81400a9..855372a2 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -382,6 +382,7 @@ namespace NewHorizons ProjectionBuilder.InitPrefabs(); CloakBuilder.InitPrefab(); RaftBuilder.InitPrefab(); + RaftDockBuilder.InitPrefab(); DreamCampfireBuilder.InitPrefab(); DreamArrivalPointBuilder.InitPrefab(); } From 92b7c70c16e00fe77b95161636f8e97d96e97fbf Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Tue, 8 Apr 2025 14:19:08 -0400 Subject: [PATCH 02/12] change patch so that it doesn't invoke twice --- .../RaftControllerPatches.cs | 49 ++++++++++++------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/NewHorizons/Patches/EchoesOfTheEyePatches/RaftControllerPatches.cs b/NewHorizons/Patches/EchoesOfTheEyePatches/RaftControllerPatches.cs index 48a08f08..f437e4a8 100644 --- a/NewHorizons/Patches/EchoesOfTheEyePatches/RaftControllerPatches.cs +++ b/NewHorizons/Patches/EchoesOfTheEyePatches/RaftControllerPatches.cs @@ -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; } } } From fe3196dac93bb0e98c94ce710a5916ddc5114778 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Tue, 8 Apr 2025 15:05:47 -0400 Subject: [PATCH 03/12] destroy flood toggles too --- NewHorizons/Builder/Props/EchoesOfTheEye/RaftDockBuilder.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NewHorizons/Builder/Props/EchoesOfTheEye/RaftDockBuilder.cs b/NewHorizons/Builder/Props/EchoesOfTheEye/RaftDockBuilder.cs index 834e82ad..b8cc22fa 100644 --- a/NewHorizons/Builder/Props/EchoesOfTheEye/RaftDockBuilder.cs +++ b/NewHorizons/Builder/Props/EchoesOfTheEye/RaftDockBuilder.cs @@ -29,6 +29,10 @@ namespace NewHorizons.Builder.Props.EchoesOfTheEye var raftDock = _prefab.GetComponent(); raftDock._startRaft = null; raftDock._floodSensor = null; + foreach (var floodToggle in _prefab.GetComponents()) + { + Component.DestroyImmediate(floodToggle); + } Object.DestroyImmediate(_prefab.FindChild("FloodSensor")); Object.DestroyImmediate(_prefab.FindChild("FloodSensor_RaftHouseArrivalPatio_NoDelay")); } From e734ada4b09a1cf11ae849c4e6988f69072e60a5 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Tue, 8 Apr 2025 21:59:55 -0400 Subject: [PATCH 04/12] allow dream lanterns to interact with these rafts --- NewHorizons/Builder/Props/EchoesOfTheEye/RaftBuilder.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NewHorizons/Builder/Props/EchoesOfTheEye/RaftBuilder.cs b/NewHorizons/Builder/Props/EchoesOfTheEye/RaftBuilder.cs index 5747c71b..c24c0106 100644 --- a/NewHorizons/Builder/Props/EchoesOfTheEye/RaftBuilder.cs +++ b/NewHorizons/Builder/Props/EchoesOfTheEye/RaftBuilder.cs @@ -40,6 +40,10 @@ namespace NewHorizons.Builder.Props.EchoesOfTheEye lightSensor._sector.OnSectorOccupantsUpdated -= lightSensor.OnSectorOccupantsUpdated; lightSensor._sector = null; } + lightSensor._detectDreamLanterns = true; + lightSensor._lanternFocusThreshold = 0.9f; + lightSensor._illuminatingDreamLanternList = new List(); + lightSensor._lightSourceMask |= LightSourceType.DREAM_LANTERN; } } } From 9365a4845df1bf5181f35f78806e2430c7884b80 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Tue, 8 Apr 2025 22:06:34 -0400 Subject: [PATCH 05/12] Add tronworld model for raft --- NewHorizons/Builder/Props/EchoesOfTheEye/RaftBuilder.cs | 9 +++++++++ NewHorizons/Utility/NewHorizonExtensions.cs | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/NewHorizons/Builder/Props/EchoesOfTheEye/RaftBuilder.cs b/NewHorizons/Builder/Props/EchoesOfTheEye/RaftBuilder.cs index c24c0106..28550681 100644 --- a/NewHorizons/Builder/Props/EchoesOfTheEye/RaftBuilder.cs +++ b/NewHorizons/Builder/Props/EchoesOfTheEye/RaftBuilder.cs @@ -45,6 +45,15 @@ namespace NewHorizons.Builder.Props.EchoesOfTheEye lightSensor._illuminatingDreamLanternList = new List(); 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); } } } diff --git a/NewHorizons/Utility/NewHorizonExtensions.cs b/NewHorizons/Utility/NewHorizonExtensions.cs index 216ea14a..10ca7570 100644 --- a/NewHorizons/Utility/NewHorizonExtensions.cs +++ b/NewHorizons/Utility/NewHorizonExtensions.cs @@ -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(this T target) where T : UnityEngine.Object { UnityEngine.Object.DontDestroyOnLoad(target); From a69fd7206ef2f205936d0ec99a9d372f137d454e Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Tue, 8 Apr 2025 22:06:49 -0400 Subject: [PATCH 06/12] Add clean parameter --- .../Props/EchoesOfTheEye/RaftBuilder.cs | 59 ++++++++++++++++++- .../Modules/Props/EchoesOfTheEye/RaftInfo.cs | 5 ++ 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/NewHorizons/Builder/Props/EchoesOfTheEye/RaftBuilder.cs b/NewHorizons/Builder/Props/EchoesOfTheEye/RaftBuilder.cs index 28550681..aa1aa110 100644 --- a/NewHorizons/Builder/Props/EchoesOfTheEye/RaftBuilder.cs +++ b/NewHorizons/Builder/Props/EchoesOfTheEye/RaftBuilder.cs @@ -3,6 +3,7 @@ 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 @@ -10,6 +11,7 @@ namespace NewHorizons.Builder.Props.EchoesOfTheEye public static class RaftBuilder { private static GameObject _prefab; + private static GameObject _cleanPrefab; internal static void InitPrefab() { @@ -56,15 +58,68 @@ namespace NewHorizons.Builder.Props.EchoesOfTheEye 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(); + var rwRaft = _cleanPrefab.FindChild("Structure_IP_Raft"); + var rwRaftAnimator = rwRaft.GetComponent(); + 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(); + dwRaftAnimator.runtimeAnimatorController = rac; + raftController._railingAnimator = dwRaftAnimator; + + var dwLightSensorForward = SearchUtilities.Find("DreamWorld_Body/Sector_DreamWorld/Interactibles_Dreamworld/DreamRaft_Body/LightSensor_Forward"); + var dwLightSensorOrigMaterial = dwLightSensorForward.GetComponent()._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(); + var lightSensorEffects = lightSensor.GetComponent(); + 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(); + lightSensorEffects._lightRenderer = bulb.GetComponent(); } public static GameObject Make(GameObject planetGO, Sector sector, RaftInfo info, OWRigidbody planetBody) { InitPrefab(); - if (_prefab == null || sector == null) return null; + if (_prefab == null || _cleanPrefab == null || sector == null) return null; - GameObject raftObject = GeneralPropBuilder.MakeFromPrefab(_prefab, "Raft_Body", planetGO, sector, info); + GameObject raftObject = GeneralPropBuilder.MakeFromPrefab(info.clean ? _cleanPrefab : _prefab, "Raft_Body", planetGO, sector, info); StreamingHandler.SetUpStreaming(raftObject, sector); diff --git a/NewHorizons/External/Modules/Props/EchoesOfTheEye/RaftInfo.cs b/NewHorizons/External/Modules/Props/EchoesOfTheEye/RaftInfo.cs index 61c3a1dd..ddf6cac1 100644 --- a/NewHorizons/External/Modules/Props/EchoesOfTheEye/RaftInfo.cs +++ b/NewHorizons/External/Modules/Props/EchoesOfTheEye/RaftInfo.cs @@ -15,6 +15,11 @@ namespace NewHorizons.External.Modules.Props.EchoesOfTheEye /// Path to the dock this raft will start attached to. /// public string dockPath; + + /// + /// Uses the raft model from the dreamworld + /// + public bool clean; } } From 264bcb24460d46657d3af542787cc0f53e2e73a4 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Thu, 10 Apr 2025 11:12:02 -0400 Subject: [PATCH 07/12] make name consistent with reels --- NewHorizons/Builder/Props/EchoesOfTheEye/RaftBuilder.cs | 2 +- NewHorizons/External/Modules/Props/EchoesOfTheEye/RaftInfo.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/NewHorizons/Builder/Props/EchoesOfTheEye/RaftBuilder.cs b/NewHorizons/Builder/Props/EchoesOfTheEye/RaftBuilder.cs index aa1aa110..2ae3435f 100644 --- a/NewHorizons/Builder/Props/EchoesOfTheEye/RaftBuilder.cs +++ b/NewHorizons/Builder/Props/EchoesOfTheEye/RaftBuilder.cs @@ -119,7 +119,7 @@ namespace NewHorizons.Builder.Props.EchoesOfTheEye if (_prefab == null || _cleanPrefab == null || sector == null) return null; - GameObject raftObject = GeneralPropBuilder.MakeFromPrefab(info.clean ? _cleanPrefab : _prefab, "Raft_Body", planetGO, sector, info); + GameObject raftObject = GeneralPropBuilder.MakeFromPrefab(info.pristine ? _cleanPrefab : _prefab, "Raft_Body", planetGO, sector, info); StreamingHandler.SetUpStreaming(raftObject, sector); diff --git a/NewHorizons/External/Modules/Props/EchoesOfTheEye/RaftInfo.cs b/NewHorizons/External/Modules/Props/EchoesOfTheEye/RaftInfo.cs index ddf6cac1..af35da12 100644 --- a/NewHorizons/External/Modules/Props/EchoesOfTheEye/RaftInfo.cs +++ b/NewHorizons/External/Modules/Props/EchoesOfTheEye/RaftInfo.cs @@ -19,7 +19,7 @@ namespace NewHorizons.External.Modules.Props.EchoesOfTheEye /// /// Uses the raft model from the dreamworld /// - public bool clean; + public bool pristine; } } From 5374448514b6495f26dab0e0c634488f570a5cea Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Thu, 10 Apr 2025 14:30:56 -0400 Subject: [PATCH 08/12] Speed Limiter --- .../Volumes/SpeedLimiterVolumeBuilder.cs | 20 +++ .../Builder/Volumes/VolumesBuildManager.cs | 7 + .../Components/Volumes/SpeedLimiterVolume.cs | 166 ++++++++++++++++++ .../VolumeInfos/SpeedLimiterVolumeInfo.cs | 28 +++ .../External/Modules/Volumes/VolumesModule.cs | 7 + 5 files changed, 228 insertions(+) create mode 100644 NewHorizons/Builder/Volumes/SpeedLimiterVolumeBuilder.cs create mode 100644 NewHorizons/Components/Volumes/SpeedLimiterVolume.cs create mode 100644 NewHorizons/External/Modules/Volumes/VolumeInfos/SpeedLimiterVolumeInfo.cs diff --git a/NewHorizons/Builder/Volumes/SpeedLimiterVolumeBuilder.cs b/NewHorizons/Builder/Volumes/SpeedLimiterVolumeBuilder.cs new file mode 100644 index 00000000..d28b0a24 --- /dev/null +++ b/NewHorizons/Builder/Volumes/SpeedLimiterVolumeBuilder.cs @@ -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(planetGO, sector, info); + + volume.maxSpeed = info.maxSpeed; + volume.stoppingDistance = info.stoppingDistance; + volume.maxEntryAngle = info.maxEntryAngle; + + return volume; + } + } +} diff --git a/NewHorizons/Builder/Volumes/VolumesBuildManager.cs b/NewHorizons/Builder/Volumes/VolumesBuildManager.cs index bdaa4a5a..fa713082 100644 --- a/NewHorizons/Builder/Volumes/VolumesBuildManager.cs +++ b/NewHorizons/Builder/Volumes/VolumesBuildManager.cs @@ -191,6 +191,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) diff --git a/NewHorizons/Components/Volumes/SpeedLimiterVolume.cs b/NewHorizons/Components/Volumes/SpeedLimiterVolume.cs new file mode 100644 index 00000000..f5d1e4af --- /dev/null +++ b/NewHorizons/Components/Volumes/SpeedLimiterVolume.cs @@ -0,0 +1,166 @@ +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 _trackedBodies = new List(); + private bool _playerJustExitedDream; + + public override void Awake() + { + _parentBody = GetComponentInParent(); + 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(); + 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(); + 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(); + 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 bool decelerated; + + 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(); + } + } +} diff --git a/NewHorizons/External/Modules/Volumes/VolumeInfos/SpeedLimiterVolumeInfo.cs b/NewHorizons/External/Modules/Volumes/VolumeInfos/SpeedLimiterVolumeInfo.cs new file mode 100644 index 00000000..f6f2fca9 --- /dev/null +++ b/NewHorizons/External/Modules/Volumes/VolumeInfos/SpeedLimiterVolumeInfo.cs @@ -0,0 +1,28 @@ +using Newtonsoft.Json; +using System.ComponentModel; +using UnityEngine; + +namespace NewHorizons.External.Modules.Volumes.VolumeInfos +{ + [JsonObject] + public class SpeedLimiterVolumeInfo : VolumeInfo + { + /// + /// The speed the volume will slow you down to when you enter it. + /// + [DefaultValue(10f)] + public float maxSpeed = 10f; + + /// + /// + /// + [DefaultValue(100f)] + public float stoppingDistance = 100f; + + /// + /// + /// + [DefaultValue(60f)] + public float maxEntryAngle = 60f; + } +} diff --git a/NewHorizons/External/Modules/Volumes/VolumesModule.cs b/NewHorizons/External/Modules/Volumes/VolumesModule.cs index 6e3afe19..e9791a51 100644 --- a/NewHorizons/External/Modules/Volumes/VolumesModule.cs +++ b/NewHorizons/External/Modules/Volumes/VolumesModule.cs @@ -101,6 +101,13 @@ namespace NewHorizons.External.Modules.Volumes /// public SpeedTrapVolumeInfo[] speedTrapVolumes; + /// + /// 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. + /// + public SpeedLimiterVolumeInfo[] speedLimiterVolumes; + /// /// Add visor effect volumes to this planet. /// From 5cde777159b45a244cb1118dbb485bd6f3c987ec Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Fri, 18 Apr 2025 05:53:41 -0400 Subject: [PATCH 09/12] log xml file --- .../Builder/Props/TranslatorText/TranslatorTextBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/Builder/Props/TranslatorText/TranslatorTextBuilder.cs b/NewHorizons/Builder/Props/TranslatorText/TranslatorTextBuilder.cs index cd967809..f5cf3b2b 100644 --- a/NewHorizons/Builder/Props/TranslatorText/TranslatorTextBuilder.cs +++ b/NewHorizons/Builder/Props/TranslatorText/TranslatorTextBuilder.cs @@ -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; } From 07da94959168cfee490e4918c566adb7eb0ee7c6 Mon Sep 17 00:00:00 2001 From: Ben C Date: Sat, 19 Apr 2025 04:06:57 +0000 Subject: [PATCH 10/12] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 99 ++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index 421029c5..758d2254 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -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" } } }, @@ -5423,6 +5471,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" @@ -6266,6 +6321,50 @@ } } }, + "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", + "format": "float", + "default": 100.0 + }, + "maxEntryAngle": { + "type": "number", + "format": "float", + "default": 60.0 + } + } + }, "VisorEffectModule": { "type": "object", "additionalProperties": false, From 781bb64e8593b8ce93889e2c6eb1a203038cc887 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Sat, 19 Apr 2025 00:08:33 -0400 Subject: [PATCH 11/12] Comments --- NewHorizons/Components/Volumes/SpeedLimiterVolume.cs | 2 -- .../Modules/Volumes/VolumeInfos/SpeedLimiterVolumeInfo.cs | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/NewHorizons/Components/Volumes/SpeedLimiterVolume.cs b/NewHorizons/Components/Volumes/SpeedLimiterVolume.cs index f5d1e4af..478589ff 100644 --- a/NewHorizons/Components/Volumes/SpeedLimiterVolume.cs +++ b/NewHorizons/Components/Volumes/SpeedLimiterVolume.cs @@ -142,8 +142,6 @@ namespace NewHorizons.Components.Volumes public float deceleration; - public bool decelerated; - public TrackedBody(OWRigidbody body, Detector.Name name, float deceleration) { this.body = body; diff --git a/NewHorizons/External/Modules/Volumes/VolumeInfos/SpeedLimiterVolumeInfo.cs b/NewHorizons/External/Modules/Volumes/VolumeInfos/SpeedLimiterVolumeInfo.cs index f6f2fca9..ef341d57 100644 --- a/NewHorizons/External/Modules/Volumes/VolumeInfos/SpeedLimiterVolumeInfo.cs +++ b/NewHorizons/External/Modules/Volumes/VolumeInfos/SpeedLimiterVolumeInfo.cs @@ -14,13 +14,13 @@ namespace NewHorizons.External.Modules.Volumes.VolumeInfos public float maxSpeed = 10f; /// - /// + /// The distance from the outside of the volume that the limiter slows you down to max speed at. /// [DefaultValue(100f)] public float stoppingDistance = 100f; /// - /// + /// 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. /// [DefaultValue(60f)] public float maxEntryAngle = 60f; From 33f468a50af3dcd9006dc8fd99b020d5556f51e9 Mon Sep 17 00:00:00 2001 From: Ben C Date: Sat, 19 Apr 2025 04:10:42 +0000 Subject: [PATCH 12/12] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index 758d2254..19669bff 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -6355,11 +6355,13 @@ }, "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 }