From 4876cf9ef80d2cba27f39c4b86dc0072709642f8 Mon Sep 17 00:00:00 2001 From: Joshua Thome Date: Sun, 6 Oct 2024 18:14:36 -0500 Subject: [PATCH 01/13] Grapple totems --- .../EchoesOfTheEye/GrappleTotemBuilder.cs | 66 +++++++++++++++++++ NewHorizons/Builder/Props/PropBuildManager.cs | 1 + NewHorizons/External/Modules/PropModule.cs | 5 ++ .../Props/EchoesOfTheEye/GrappleTotemInfo.cs | 39 +++++++++++ 4 files changed, 111 insertions(+) create mode 100644 NewHorizons/Builder/Props/EchoesOfTheEye/GrappleTotemBuilder.cs create mode 100644 NewHorizons/External/Modules/Props/EchoesOfTheEye/GrappleTotemInfo.cs diff --git a/NewHorizons/Builder/Props/EchoesOfTheEye/GrappleTotemBuilder.cs b/NewHorizons/Builder/Props/EchoesOfTheEye/GrappleTotemBuilder.cs new file mode 100644 index 00000000..d8e7ef90 --- /dev/null +++ b/NewHorizons/Builder/Props/EchoesOfTheEye/GrappleTotemBuilder.cs @@ -0,0 +1,66 @@ +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 System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace NewHorizons.Builder.Props.EchoesOfTheEye +{ + public static class GrappleTotemBuilder + { + private static GameObject _prefab; + + internal static void InitPrefab() + { + if (_prefab == null) + { + _prefab = SearchUtilities.Find("DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_4/Interactibles_DreamZone_4_Upper/Prefab_IP_GrappleTotem").InstantiateInactive().Rename("Prefab_GrappleTotem").DontDestroyOnLoad(); + if (_prefab == null) + { + NHLogger.LogWarning($"Tried to make a grapple totem but couldn't. Do you have the DLC installed?"); + return; + } + else + { + _prefab.AddComponent()._destroyOnDLCNotOwned = true; + var zoomPoint = _prefab.GetComponentInChildren(); + zoomPoint._sector = null; + var sensor = _prefab.GetComponentInChildren(); + sensor._sector = null; + } + } + } + + public static GameObject Make(GameObject planetGO, Sector sector, GrappleTotemInfo info, IModBehaviour mod) + { + InitPrefab(); + + if (_prefab == null || sector == null) return null; + + var totemObj = DetailBuilder.Make(planetGO, sector, mod, _prefab, new DetailInfo(info)); + + var zoomPoint = totemObj.GetComponentInChildren(); + zoomPoint._minActivationDistance = info.minDistance; + zoomPoint._arrivalDistance = info.arrivalDistance; + + var sensor = totemObj.GetComponentInChildren(); + sensor._detectionAngle = info.maxAngle; + sensor._maxDistance = info.maxDistance; + + if (info.allowFlashlight) + { + sensor._detectFlashlight = true; + sensor._lightSourceMask |= LightSourceType.FLASHLIGHT; + } + + return totemObj; + } + } +} diff --git a/NewHorizons/Builder/Props/PropBuildManager.cs b/NewHorizons/Builder/Props/PropBuildManager.cs index 8cff2939..e600841d 100644 --- a/NewHorizons/Builder/Props/PropBuildManager.cs +++ b/NewHorizons/Builder/Props/PropBuildManager.cs @@ -102,6 +102,7 @@ namespace NewHorizons.Builder.Props // If a prop has set its parentPath and the parent cannot be found, add it to the next pass and try again later nextPass = new List(); + if (Main.HasDLC) MakeGeneralProps(go, config.Props.grappleTotems, (totem) => GrappleTotemBuilder.Make(go, sector, totem, mod)); if (Main.HasDLC) MakeGeneralProps(go, config.Props.dreamCampfires, (campfire) => DreamCampfireBuilder.Make(go, sector, campfire, mod), (campfire) => campfire.id); if (Main.HasDLC) MakeGeneralProps(go, config.Props.dreamArrivalPoints, (point) => DreamArrivalPointBuilder.Make(go, sector, point, mod), (point) => point.id); MakeGeneralProps(go, config.Props.gravityCannons, (cannon) => GravityCannonBuilder.Make(go, sector, cannon, mod), (cannon) => cannon.shuttleID); diff --git a/NewHorizons/External/Modules/PropModule.cs b/NewHorizons/External/Modules/PropModule.cs index 04662df7..e717ef3e 100644 --- a/NewHorizons/External/Modules/PropModule.cs +++ b/NewHorizons/External/Modules/PropModule.cs @@ -133,6 +133,11 @@ namespace NewHorizons.External.Modules /// public DreamArrivalPointInfo[] dreamArrivalPoints; + /// + /// Adds dream world grapple totems to this planet. + /// + public GrappleTotemInfo[] grappleTotems; + [Obsolete("reveal is deprecated. Use Volumes->revealVolumes instead.")] public RevealVolumeInfo[] reveal; [Obsolete("audioVolumes is deprecated. Use Volumes->audioVolumes instead.")] public AudioVolumeInfo[] audioVolumes; diff --git a/NewHorizons/External/Modules/Props/EchoesOfTheEye/GrappleTotemInfo.cs b/NewHorizons/External/Modules/Props/EchoesOfTheEye/GrappleTotemInfo.cs new file mode 100644 index 00000000..86d46246 --- /dev/null +++ b/NewHorizons/External/Modules/Props/EchoesOfTheEye/GrappleTotemInfo.cs @@ -0,0 +1,39 @@ +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.Props.EchoesOfTheEye +{ + [JsonObject] + public class GrappleTotemInfo : GeneralPropInfo + { + /// + /// The minimum distance that the player must be from the grapple totem for it to activate. + /// + [DefaultValue(10f)] public float minDistance = 10f; + + /// + /// The distance from the grapple totem that the player will stop at when it activates. + /// + [DefaultValue(4f)] public float arrivalDistance = 4f; + + /// + /// The maximum angle in degrees allowed between the grapple totem's face and the player's lantern in order to activate the totem. + /// + [DefaultValue(45f)] public float maxAngle = 45f; + + /// + /// The maximum distance allowed between the grapple totem's face and the player's lantern in order to activate the totem. + /// + [DefaultValue(29f)] public float maxDistance = 29f; + + /// + /// Allows the grapple totem to be activated by the player's flashlight (when placed outside of the dream world). The player must still be holding an artifact, but it can be unlit. + /// + public bool allowFlashlight; + } +} From 4baf9b264e56fb70b8adef0503f8d17fffa45716 Mon Sep 17 00:00:00 2001 From: Joshua Thome Date: Mon, 7 Oct 2024 21:54:35 -0500 Subject: [PATCH 02/13] Alarm totems --- .../Props/EchoesOfTheEye/AlarmTotemBuilder.cs | 54 +++++++++++++++++++ NewHorizons/Builder/Props/PropBuildManager.cs | 1 + NewHorizons/External/Modules/PropModule.cs | 5 ++ .../Props/EchoesOfTheEye/AlarmTotemInfo.cs | 24 +++++++++ 4 files changed, 84 insertions(+) create mode 100644 NewHorizons/Builder/Props/EchoesOfTheEye/AlarmTotemBuilder.cs create mode 100644 NewHorizons/External/Modules/Props/EchoesOfTheEye/AlarmTotemInfo.cs diff --git a/NewHorizons/Builder/Props/EchoesOfTheEye/AlarmTotemBuilder.cs b/NewHorizons/Builder/Props/EchoesOfTheEye/AlarmTotemBuilder.cs new file mode 100644 index 00000000..245127bb --- /dev/null +++ b/NewHorizons/Builder/Props/EchoesOfTheEye/AlarmTotemBuilder.cs @@ -0,0 +1,54 @@ +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 System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace NewHorizons.Builder.Props.EchoesOfTheEye +{ + public static class AlarmTotemBuilder + { + private static GameObject _prefab; + + internal static void InitPrefab() + { + if (_prefab == null) + { + _prefab = SearchUtilities.Find("DreamWorld_Body/Sector_DreamWorld/Sector_Underground/IslandsRoot/IslandPivot_C/Island_C/Interactibles_Island_C/Prefab_IP_AlarmTotem").InstantiateInactive().Rename("Prefab_AlarmTotem").DontDestroyOnLoad(); + if (_prefab == null) + { + NHLogger.LogWarning($"Tried to make a grapple totem but couldn't. Do you have the DLC installed?"); + return; + } + else + { + _prefab.AddComponent()._destroyOnDLCNotOwned = true; + var alarmTotem = _prefab.GetComponent(); + alarmTotem._sector = null; + } + } + } + + public static GameObject Make(GameObject planetGO, Sector sector, AlarmTotemInfo info, IModBehaviour mod) + { + InitPrefab(); + + if (_prefab == null || sector == null) return null; + + var totemObj = DetailBuilder.Make(planetGO, sector, mod, _prefab, new DetailInfo(info)); + + var alarmTotem = _prefab.GetComponent(); + alarmTotem._sightAngle = info.sightAngle; + alarmTotem._sightDistance = info.sightDistance; + + return totemObj; + } + } +} diff --git a/NewHorizons/Builder/Props/PropBuildManager.cs b/NewHorizons/Builder/Props/PropBuildManager.cs index e600841d..cb8be189 100644 --- a/NewHorizons/Builder/Props/PropBuildManager.cs +++ b/NewHorizons/Builder/Props/PropBuildManager.cs @@ -102,6 +102,7 @@ namespace NewHorizons.Builder.Props // If a prop has set its parentPath and the parent cannot be found, add it to the next pass and try again later nextPass = new List(); + if (Main.HasDLC) MakeGeneralProps(go, config.Props.alarmTotems, (totem) => AlarmTotemBuilder.Make(go, sector, totem, mod)); if (Main.HasDLC) MakeGeneralProps(go, config.Props.grappleTotems, (totem) => GrappleTotemBuilder.Make(go, sector, totem, mod)); if (Main.HasDLC) MakeGeneralProps(go, config.Props.dreamCampfires, (campfire) => DreamCampfireBuilder.Make(go, sector, campfire, mod), (campfire) => campfire.id); if (Main.HasDLC) MakeGeneralProps(go, config.Props.dreamArrivalPoints, (point) => DreamArrivalPointBuilder.Make(go, sector, point, mod), (point) => point.id); diff --git a/NewHorizons/External/Modules/PropModule.cs b/NewHorizons/External/Modules/PropModule.cs index e717ef3e..539d0bed 100644 --- a/NewHorizons/External/Modules/PropModule.cs +++ b/NewHorizons/External/Modules/PropModule.cs @@ -138,6 +138,11 @@ namespace NewHorizons.External.Modules /// public GrappleTotemInfo[] grappleTotems; + /// + /// Adds dream world alarm totems to this planet. + /// + public AlarmTotemInfo[] alarmTotems; + [Obsolete("reveal is deprecated. Use Volumes->revealVolumes instead.")] public RevealVolumeInfo[] reveal; [Obsolete("audioVolumes is deprecated. Use Volumes->audioVolumes instead.")] public AudioVolumeInfo[] audioVolumes; diff --git a/NewHorizons/External/Modules/Props/EchoesOfTheEye/AlarmTotemInfo.cs b/NewHorizons/External/Modules/Props/EchoesOfTheEye/AlarmTotemInfo.cs new file mode 100644 index 00000000..cc902dbf --- /dev/null +++ b/NewHorizons/External/Modules/Props/EchoesOfTheEye/AlarmTotemInfo.cs @@ -0,0 +1,24 @@ +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.Props.EchoesOfTheEye +{ + [JsonObject] + public class AlarmTotemInfo : GeneralPropInfo + { + /// + /// The maximum distance of the alarm's "vision cone". + /// + [DefaultValue(45f)] public float sightDistance = 45; + + /// + /// The width of the alarm's "vision cone" in degrees. + /// + [DefaultValue(60f)] public float sightAngle = 60f; + } +} From 863717ee06d8178e1a373b7964fcf4978bdcff20 Mon Sep 17 00:00:00 2001 From: Joshua Thome Date: Mon, 7 Oct 2024 22:03:30 -0500 Subject: [PATCH 03/13] Update alarm properties on instances not the prefab --- NewHorizons/Builder/Props/EchoesOfTheEye/AlarmTotemBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/Builder/Props/EchoesOfTheEye/AlarmTotemBuilder.cs b/NewHorizons/Builder/Props/EchoesOfTheEye/AlarmTotemBuilder.cs index 245127bb..cf9b0c23 100644 --- a/NewHorizons/Builder/Props/EchoesOfTheEye/AlarmTotemBuilder.cs +++ b/NewHorizons/Builder/Props/EchoesOfTheEye/AlarmTotemBuilder.cs @@ -44,7 +44,7 @@ namespace NewHorizons.Builder.Props.EchoesOfTheEye var totemObj = DetailBuilder.Make(planetGO, sector, mod, _prefab, new DetailInfo(info)); - var alarmTotem = _prefab.GetComponent(); + var alarmTotem = totemObj.GetComponent(); alarmTotem._sightAngle = info.sightAngle; alarmTotem._sightDistance = info.sightDistance; From e5c21c6c3357528077615b61baebad1874860ff4 Mon Sep 17 00:00:00 2001 From: Joshua Thome Date: Sun, 13 Oct 2024 22:16:57 -0500 Subject: [PATCH 04/13] Make NH-spawned grapple totems work with the flashlight automatically --- .../EchoesOfTheEye/GrappleTotemBuilder.cs | 7 +- .../Props/EchoesOfTheEye/GrappleTotemInfo.cs | 5 - .../LanternZoomPointPatches.cs | 117 ++++++++++++++++++ 3 files changed, 119 insertions(+), 10 deletions(-) create mode 100644 NewHorizons/Patches/EchoesOfTheEyePatches/LanternZoomPointPatches.cs diff --git a/NewHorizons/Builder/Props/EchoesOfTheEye/GrappleTotemBuilder.cs b/NewHorizons/Builder/Props/EchoesOfTheEye/GrappleTotemBuilder.cs index d8e7ef90..9a25dd12 100644 --- a/NewHorizons/Builder/Props/EchoesOfTheEye/GrappleTotemBuilder.cs +++ b/NewHorizons/Builder/Props/EchoesOfTheEye/GrappleTotemBuilder.cs @@ -54,11 +54,8 @@ namespace NewHorizons.Builder.Props.EchoesOfTheEye sensor._detectionAngle = info.maxAngle; sensor._maxDistance = info.maxDistance; - if (info.allowFlashlight) - { - sensor._detectFlashlight = true; - sensor._lightSourceMask |= LightSourceType.FLASHLIGHT; - } + sensor._detectFlashlight = true; + sensor._lightSourceMask |= LightSourceType.FLASHLIGHT; return totemObj; } diff --git a/NewHorizons/External/Modules/Props/EchoesOfTheEye/GrappleTotemInfo.cs b/NewHorizons/External/Modules/Props/EchoesOfTheEye/GrappleTotemInfo.cs index 86d46246..487e42d6 100644 --- a/NewHorizons/External/Modules/Props/EchoesOfTheEye/GrappleTotemInfo.cs +++ b/NewHorizons/External/Modules/Props/EchoesOfTheEye/GrappleTotemInfo.cs @@ -30,10 +30,5 @@ namespace NewHorizons.External.Modules.Props.EchoesOfTheEye /// The maximum distance allowed between the grapple totem's face and the player's lantern in order to activate the totem. /// [DefaultValue(29f)] public float maxDistance = 29f; - - /// - /// Allows the grapple totem to be activated by the player's flashlight (when placed outside of the dream world). The player must still be holding an artifact, but it can be unlit. - /// - public bool allowFlashlight; } } diff --git a/NewHorizons/Patches/EchoesOfTheEyePatches/LanternZoomPointPatches.cs b/NewHorizons/Patches/EchoesOfTheEyePatches/LanternZoomPointPatches.cs new file mode 100644 index 00000000..38503ef4 --- /dev/null +++ b/NewHorizons/Patches/EchoesOfTheEyePatches/LanternZoomPointPatches.cs @@ -0,0 +1,117 @@ +using HarmonyLib; +using NewHorizons.Components.EOTE; +using System.Collections.Generic; +using System.Reflection.Emit; +using UnityEngine; + +namespace NewHorizons.Patches.EchoesOfTheEyePatches +{ + [HarmonyPatch(typeof(LanternZoomPoint))] + public static class LanternZoomPointPatches + { + // Patching all methods that assume the player is holding an artifact (_playerLantern) to add null checks so they'll work outside of the dream world + + [HarmonyPrefix] + [HarmonyPatch(nameof(LanternZoomPoint.Update))] + public static bool LanternZoomPoint_Update(LanternZoomPoint __instance) + { + if (PlayerState.InDreamWorld()) return true; + if (!__instance.enabled) + { + return false; + } + if (__instance._state != LanternZoomPoint.State.RetroZoom) + { + if (__instance._playerLantern != null) + { + __instance._playerLantern.GetLanternController().MoveTowardFocus(1f, 2f); + } + } + if (__instance._state == LanternZoomPoint.State.LookAt && Time.time > __instance._stateChangeTime + 0.4f) + { + __instance.ChangeState(LanternZoomPoint.State.ZoomIn); + __instance.StartZoomIn(); + } + else if (__instance._state == LanternZoomPoint.State.ZoomIn) + { + __instance.UpdateZoomIn(); + } + if (__instance._state == LanternZoomPoint.State.RetroZoom) + { + __instance.UpdateRetroZoom(); + } + return false; + } + + [HarmonyPrefix] + [HarmonyPatch(nameof(LanternZoomPoint.UpdateRetroZoom))] + public static bool LanternZoomPoint_UpdateRetroZoom(LanternZoomPoint __instance) + { + if (PlayerState.InDreamWorld()) return true; + float num = Mathf.InverseLerp(__instance._stateChangeTime, __instance._stateChangeTime + 1.2f, Time.time); + float focus = Mathf.Pow(Mathf.SmoothStep(0f, 1f, 1f - num), 0.2f); + if (__instance._playerLantern != null) + { + __instance._playerLantern.GetLanternController().SetFocus(focus); + } + float t = __instance._retroZoomCurve.Evaluate(num); + float targetFieldOfView = Mathf.Lerp(__instance._startFOV, Locator.GetPlayerCameraController().GetOrigFieldOfView(), t); + Locator.GetPlayerCameraController().SetTargetFieldOfView(targetFieldOfView); + float d = __instance._imageHalfWidth / Mathf.Tan(Locator.GetPlayerCamera().fieldOfView * 0.017453292f * 0.5f); + Vector3 vector = __instance._startLocalPos - __instance._endLocalPos; + __instance._attachPoint.transform.localPosition = __instance._endLocalPos + vector.normalized * d; + if (num >= 1f) + { + __instance.FinishRetroZoom(); + } + return false; + } + + [HarmonyPrefix] + [HarmonyPatch(nameof(LanternZoomPoint.FinishRetroZoom))] + public static bool LanternZoomPoint_FinishRetroZoom(LanternZoomPoint __instance) + { + if (PlayerState.InDreamWorld()) return true; + __instance.ChangeState(LanternZoomPoint.State.Idle); + __instance.enabled = false; + __instance._attachPoint.DetachPlayer(); + GlobalMessenger.FireEvent("PlayerRepositioned"); + if (__instance._playerLantern != null) + { + __instance._playerLantern.ForceUnfocus(); + __instance._playerLantern.enabled = true; + __instance._playerLantern = null; + } + OWInput.ChangeInputMode(InputMode.Character); + __instance._lightController.FadeTo(0f, 1f); + Locator.GetPlayerController().SetColliderActivation(true); + Locator.GetPlayerTransform().GetComponent().BreakLock(); + Locator.GetDreamWorldController().SetActiveZoomPoint(null); + return false; + } + + [HarmonyPrefix] + [HarmonyPatch(nameof(LanternZoomPoint.OnDetectLight))] + public static bool LanternZoomPoint_OnDetectLight(LanternZoomPoint __instance) + { + if (PlayerState.InDreamWorld()) return true; + if (__instance._state == LanternZoomPoint.State.Idle && !PlayerState.IsAttached() && Time.time > __instance._stateChangeTime + 1f && Vector3.Distance(__instance.transform.position, Locator.GetPlayerCamera().transform.position) > __instance._minActivationDistance) + { + __instance._playerLantern = Locator.GetToolModeSwapper().GetItemCarryTool().GetHeldItem() as DreamLanternItem; + Locator.GetDreamWorldController().SetActiveZoomPoint(__instance); + __instance._attachPoint.transform.position = Locator.GetPlayerTransform().position; + __instance._attachPoint.transform.rotation = Locator.GetPlayerTransform().rotation; + __instance._attachPoint.AttachPlayer(); + Locator.GetPlayerTransform().GetComponent().LockOn(__instance.transform, 5f, false, 1f); + OWInput.ChangeInputMode(InputMode.None); + if (__instance._playerLantern != null) + { + __instance._playerLantern.enabled = false; + } + __instance.ChangeState(LanternZoomPoint.State.LookAt); + __instance.enabled = true; + } + return false; + } + } +} From a1d165e2b98030ee263e38f1e1da61885f5a4f40 Mon Sep 17 00:00:00 2001 From: Joshua Thome Date: Sun, 13 Oct 2024 22:18:08 -0500 Subject: [PATCH 05/13] Portholes --- .../Props/EchoesOfTheEye/PortholeBuilder.cs | 90 +++++++++++++++++++ NewHorizons/Builder/Props/PropBuildManager.cs | 1 + NewHorizons/External/Modules/PropModule.cs | 5 ++ .../Props/EchoesOfTheEye/PortholeInfo.cs | 35 ++++++++ .../EchoesOfTheEyePatches/PeepholePatches.cs | 69 ++++++++++++++ 5 files changed, 200 insertions(+) create mode 100644 NewHorizons/Builder/Props/EchoesOfTheEye/PortholeBuilder.cs create mode 100644 NewHorizons/External/Modules/Props/EchoesOfTheEye/PortholeInfo.cs create mode 100644 NewHorizons/Patches/EchoesOfTheEyePatches/PeepholePatches.cs diff --git a/NewHorizons/Builder/Props/EchoesOfTheEye/PortholeBuilder.cs b/NewHorizons/Builder/Props/EchoesOfTheEye/PortholeBuilder.cs new file mode 100644 index 00000000..854dbd5f --- /dev/null +++ b/NewHorizons/Builder/Props/EchoesOfTheEye/PortholeBuilder.cs @@ -0,0 +1,90 @@ +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 System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace NewHorizons.Builder.Props.EchoesOfTheEye +{ + public static class PortholeBuilder + { + private static GameObject _mainPrefab; + private static GameObject _simPrefab; + + internal static void InitPrefabs() + { + if (_mainPrefab == null) + { + _mainPrefab = SearchUtilities.Find("DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_4/Interactibles_DreamZone_4_Upper/Props_IP_Peephole_Prison").InstantiateInactive().Rename("Prefab_Porthole").DontDestroyOnLoad(); + if (_mainPrefab == null) + { + NHLogger.LogWarning($"Tried to make a grapple totem but couldn't. Do you have the DLC installed?"); + return; + } + else + { + _mainPrefab.AddComponent()._destroyOnDLCNotOwned = true; + var peephole = _mainPrefab.GetComponentInChildren(); + peephole._factIDs = new string[0]; + peephole._viewingSector = null; + } + } + if (_simPrefab == null) + { + _simPrefab = SearchUtilities.Find("DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_4/Simulation_DreamZone_4/Geo_DreamZone_4_Upper/Effects_IP_SIM_Porthole").InstantiateInactive().Rename("Prefab_SIM_Porthole").DontDestroyOnLoad(); + if (_simPrefab == null) + { + NHLogger.LogWarning($"Tried to make a grapple totem but couldn't. Do you have the DLC installed?"); + return; + } + else + { + _simPrefab.AddComponent()._destroyOnDLCNotOwned = true; + } + } + } + + public static GameObject Make(GameObject planetGO, Sector sector, PortholeInfo info, IModBehaviour mod) + { + InitPrefabs(); + + if (_mainPrefab == null || _simPrefab == null || sector == null) return null; + + var portholeObj = DetailBuilder.Make(planetGO, sector, mod, _mainPrefab, new DetailInfo(info)); + + var sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere); + sphere.transform.SetParent(portholeObj.transform, false); + + var simObj = DetailBuilder.Make(planetGO, sector, mod, _simPrefab, new DetailInfo(info)); + simObj.transform.parent = portholeObj.transform; + + var peephole = portholeObj.GetComponentInChildren(); + if (info.revealFacts != null) + { + peephole._factIDs = info.revealFacts; + } + + peephole._peepholeCamera.farClipPlane = 4000f; + peephole._peepholeCamera.fieldOfView = info.fieldOfView; + + // Reposition the peephole camera later, after all planets are built, in case the target point is on a different astro body. + Delay.FireInNUpdates(() => + { + var cameraObj = GeneralPropBuilder.MakeFromExisting(peephole._peepholeCamera.gameObject, planetGO, sector, info.target); + cameraObj.transform.Rotate(Vector3.up, 180f, Space.Self); + cameraObj.transform.position += cameraObj.transform.up; + var viewingSector = cameraObj.GetComponentInParent(); + peephole._viewingSector = viewingSector; + }, 2); + + return portholeObj; + } + } +} diff --git a/NewHorizons/Builder/Props/PropBuildManager.cs b/NewHorizons/Builder/Props/PropBuildManager.cs index cb8be189..d171ae88 100644 --- a/NewHorizons/Builder/Props/PropBuildManager.cs +++ b/NewHorizons/Builder/Props/PropBuildManager.cs @@ -102,6 +102,7 @@ namespace NewHorizons.Builder.Props // If a prop has set its parentPath and the parent cannot be found, add it to the next pass and try again later nextPass = new List(); + if (Main.HasDLC) MakeGeneralProps(go, config.Props.portholes, (porthole) => PortholeBuilder.Make(go, sector, porthole, mod)); if (Main.HasDLC) MakeGeneralProps(go, config.Props.alarmTotems, (totem) => AlarmTotemBuilder.Make(go, sector, totem, mod)); if (Main.HasDLC) MakeGeneralProps(go, config.Props.grappleTotems, (totem) => GrappleTotemBuilder.Make(go, sector, totem, mod)); if (Main.HasDLC) MakeGeneralProps(go, config.Props.dreamCampfires, (campfire) => DreamCampfireBuilder.Make(go, sector, campfire, mod), (campfire) => campfire.id); diff --git a/NewHorizons/External/Modules/PropModule.cs b/NewHorizons/External/Modules/PropModule.cs index 539d0bed..046976d0 100644 --- a/NewHorizons/External/Modules/PropModule.cs +++ b/NewHorizons/External/Modules/PropModule.cs @@ -143,6 +143,11 @@ namespace NewHorizons.External.Modules /// public AlarmTotemInfo[] alarmTotems; + /// + /// Adds portholes (the windows you can peek through in the DLC) to this planet. + /// + public PortholeInfo[] portholes; + [Obsolete("reveal is deprecated. Use Volumes->revealVolumes instead.")] public RevealVolumeInfo[] reveal; [Obsolete("audioVolumes is deprecated. Use Volumes->audioVolumes instead.")] public AudioVolumeInfo[] audioVolumes; diff --git a/NewHorizons/External/Modules/Props/EchoesOfTheEye/PortholeInfo.cs b/NewHorizons/External/Modules/Props/EchoesOfTheEye/PortholeInfo.cs new file mode 100644 index 00000000..5f8659d3 --- /dev/null +++ b/NewHorizons/External/Modules/Props/EchoesOfTheEye/PortholeInfo.cs @@ -0,0 +1,35 @@ +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.Props.EchoesOfTheEye +{ + [JsonObject] + public class PortholeInfo : GeneralPropInfo + { + /// + /// Fact IDs to reveal when peeking through the porthole. + /// + public string[] revealFacts; + + /// + /// The field of view of the porthole camera. + /// + [DefaultValue(90f)] public float fieldOfView = 90f; + + /// + /// The location of the camera when the player peeks through the porthole. Can be placed on a different planet. + /// + public PortholeTargetInfo target; + } + + [JsonObject] + public class PortholeTargetInfo : GeneralSolarSystemPropInfo + { + + } +} diff --git a/NewHorizons/Patches/EchoesOfTheEyePatches/PeepholePatches.cs b/NewHorizons/Patches/EchoesOfTheEyePatches/PeepholePatches.cs new file mode 100644 index 00000000..f0b6df44 --- /dev/null +++ b/NewHorizons/Patches/EchoesOfTheEyePatches/PeepholePatches.cs @@ -0,0 +1,69 @@ +using HarmonyLib; +using NewHorizons.Components.EOTE; +using System.Collections.Generic; +using System.Reflection.Emit; +using UnityEngine; + +namespace NewHorizons.Patches.EchoesOfTheEyePatches +{ + [HarmonyPatch(typeof(Peephole))] + public static class PeepholePatches + { + static List _previousSectors = new List(); + + [HarmonyPrefix] + [HarmonyPatch(nameof(Peephole.SwitchToPeepholeCamera))] + public static void Peephole_SwitchToPeepholeCamera_Prefix() + { + _previousSectors.Clear(); + _previousSectors.AddRange(Locator.GetPlayerSectorDetector()._sectorList); + } + + + [HarmonyPostfix] + [HarmonyPatch(nameof(Peephole.SwitchToPeepholeCamera))] + public static void Peephole_SwitchToPeepholeCamera(Peephole __instance) + { + if (__instance._viewingSector) + { + var sector = __instance._viewingSector; + while (sector._parentSector != null) + { + sector = sector._parentSector; + + if (!_previousSectors.Contains(sector)) + { + sector.AddOccupant(Locator.GetPlayerSectorDetector()); + } + } + } + } + + [HarmonyPostfix] + [HarmonyPatch(nameof(Peephole.SwitchToPlayerCamera))] + public static void Peephole_SwitchToPlayerCamera(Peephole __instance) + { + if (__instance._viewingSector) + { + var sector = __instance._viewingSector; + + if (_previousSectors.Contains(sector)) + { + sector.AddOccupant(Locator.GetPlayerSectorDetector()); + } + + while (sector._parentSector != null) + { + sector = sector._parentSector; + + if (!_previousSectors.Contains(sector)) + { + sector.RemoveOccupant(Locator.GetPlayerSectorDetector()); + } + } + } + + _previousSectors.Clear(); + } + } +} From 9a2843c8dc563218197dae1e68e4b8383f68f84c Mon Sep 17 00:00:00 2001 From: Joshua Thome Date: Sun, 13 Oct 2024 22:18:24 -0500 Subject: [PATCH 06/13] Start of projection totems --- .../EchoesOfTheEye/ProjectionTotemBuilder.cs | 115 ++++++++++++++++++ .../EchoesOfTheEye/ProjectionTotemInfo.cs | 44 +++++++ 2 files changed, 159 insertions(+) create mode 100644 NewHorizons/Builder/Props/EchoesOfTheEye/ProjectionTotemBuilder.cs create mode 100644 NewHorizons/External/Modules/Props/EchoesOfTheEye/ProjectionTotemInfo.cs diff --git a/NewHorizons/Builder/Props/EchoesOfTheEye/ProjectionTotemBuilder.cs b/NewHorizons/Builder/Props/EchoesOfTheEye/ProjectionTotemBuilder.cs new file mode 100644 index 00000000..fdd18663 --- /dev/null +++ b/NewHorizons/Builder/Props/EchoesOfTheEye/ProjectionTotemBuilder.cs @@ -0,0 +1,115 @@ +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 System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace NewHorizons.Builder.Props.EchoesOfTheEye +{ + public static class ProjectionTotemBuilder + { + private static GameObject _prefab; + + internal static void InitPrefab() + { + if (_prefab == null) + { + _prefab = SearchUtilities.Find("DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_3/Interactibles_DreamZone_3/Prefab_IP_DreamObjectProjector_Bridge").InstantiateInactive().Rename("Prefab_ProjectionTotem").DontDestroyOnLoad(); + if (_prefab == null) + { + NHLogger.LogWarning($"Tried to make a grapple totem but couldn't. Do you have the DLC installed?"); + return; + } + else + { + _prefab.AddComponent()._destroyOnDLCNotOwned = true; + var projector = _prefab.GetComponent(); + projector._projections = new DreamObjectProjection[0]; + } + } + } + + public static GameObject Make(GameObject planetGO, Sector sector, ProjectionTotemInfo info, IModBehaviour mod) + { + InitPrefab(); + + if (_prefab == null || sector == null) return null; + + var totemObj = DetailBuilder.Make(planetGO, sector, mod, _prefab, new DetailInfo(info)); + + var projector = totemObj.GetComponent(); + + if (!string.IsNullOrEmpty(info.pathToAlarmTotem)) + { + var alarmTotemObj = planetGO.transform.Find(info.pathToAlarmTotem); + if (alarmTotemObj != null) + { + var alarmTotem = alarmTotemObj.GetComponentInChildren(); + if (alarmTotem != null) + { + projector._alarmTotem = alarmTotem; + } + } + } + + if (info.pathsToDreamCandles != null) + { + var dreamCandles = new List(); + foreach (var pathToDreamCandles in info.pathsToDreamCandles) + { + if (string.IsNullOrEmpty(pathToDreamCandles)) continue; + var dreamCandleObj = planetGO.transform.Find(pathToDreamCandles); + if (dreamCandleObj != null) + { + dreamCandles.AddRange(dreamCandleObj.GetComponentsInChildren()); + } + } + projector._dreamCandles = dreamCandles.ToArray(); + } + + if (info.pathsToProjectionTotems != null) + { + var projectionTotems = new List(); + foreach (var pathToProjectionTotems in info.pathsToProjectionTotems) + { + if (string.IsNullOrEmpty(pathToProjectionTotems)) continue; + var projectionTotemObj = planetGO.transform.Find(pathToProjectionTotems); + if (projectionTotemObj != null) + { + projectionTotems.AddRange(projectionTotemObj.GetComponentsInChildren()); + } + } + projector._extinguishedProjectors = projectionTotems.ToArray(); + } + + if (info.pathsToProjectedObjects != null) + { + var projections = new List(); + foreach (var pathToProjectedObject in info.pathsToProjectedObjects) + { + if (string.IsNullOrEmpty(pathToProjectedObject)) continue; + var projectionObj = planetGO.transform.Find(pathToProjectedObject); + if (projectionObj != null) + { + projectionObj.gameObject.AddComponent(); + var projection = projectionObj.gameObject.AddComponent(); + projection._setActive = true; + projection.Awake(); + } + } + projector._projections = projections.ToArray(); + } + + projector.SetLit(info.startLit); + + return totemObj; + } + } +} diff --git a/NewHorizons/External/Modules/Props/EchoesOfTheEye/ProjectionTotemInfo.cs b/NewHorizons/External/Modules/Props/EchoesOfTheEye/ProjectionTotemInfo.cs new file mode 100644 index 00000000..40b695bb --- /dev/null +++ b/NewHorizons/External/Modules/Props/EchoesOfTheEye/ProjectionTotemInfo.cs @@ -0,0 +1,44 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NewHorizons.External.Modules.Props.EchoesOfTheEye +{ + [JsonObject] + public class ProjectionTotemInfo : GeneralPropInfo + { + /// + /// Whether the totem should start lit or extinguished. + /// + public bool startLit; + + /// + /// Whether the projection totem should be able to extinguished but not be able to be lit again with the artifact. Mainly useful if `startLit` is set to true. + /// + public bool extinguishOnly; + + /// + /// A relative path from this planet to an alarm totem that will be activated or deactivated based on whether this totem is lit. + /// + public string pathToAlarmTotem; + + /// + /// Relative paths from this planet to objects containing dream candles that will be activated or deactivated based on whether this totem is lit. All dream candles in the selected objects will be connected to this totem, so they do not need to be specified individually if a parent object is specified. + /// + public string[] pathsToDreamCandles; + + /// + /// Relative paths from this planet to projection totems that will be deactivated if this totem is extinguished. All projection totems in the selected objects will be connected to this totem, so they do not need to be specified individually if a parent object is specified. + /// + public string[] pathsToProjectionTotems; + + /// + /// Relative paths from this planet to objects that will appear or disappear when this totem is lit or extinguished. Some types of objects and effects are not supported and will remain visible and active. + /// + public string[] pathsToProjectedObjects; + } +} From 0f0f704b75fe4ab1e0d631bce0d230486d5c0fa1 Mon Sep 17 00:00:00 2001 From: Joshua Thome Date: Sun, 13 Oct 2024 23:34:46 -0500 Subject: [PATCH 07/13] Correctly position portholes relative to provided position --- .../Builder/Props/EchoesOfTheEye/PortholeBuilder.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/NewHorizons/Builder/Props/EchoesOfTheEye/PortholeBuilder.cs b/NewHorizons/Builder/Props/EchoesOfTheEye/PortholeBuilder.cs index 854dbd5f..b466e272 100644 --- a/NewHorizons/Builder/Props/EchoesOfTheEye/PortholeBuilder.cs +++ b/NewHorizons/Builder/Props/EchoesOfTheEye/PortholeBuilder.cs @@ -58,13 +58,17 @@ namespace NewHorizons.Builder.Props.EchoesOfTheEye if (_mainPrefab == null || _simPrefab == null || sector == null) return null; var portholeObj = DetailBuilder.Make(planetGO, sector, mod, _mainPrefab, new DetailInfo(info)); - - var sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere); - sphere.transform.SetParent(portholeObj.transform, false); + portholeObj.name = "Prefab_Porthole"; var simObj = DetailBuilder.Make(planetGO, sector, mod, _simPrefab, new DetailInfo(info)); simObj.transform.parent = portholeObj.transform; + var parentObj = GeneralPropBuilder.MakeNew("Porthole", planetGO, sector, info); + parentObj.SetActive(true); + portholeObj.transform.SetParent(parentObj.transform, true); + portholeObj.transform.localPosition = new Vector3(0f, -4f, 8f); + portholeObj.transform.localEulerAngles = new Vector3(0f, 315f, 0f); + var peephole = portholeObj.GetComponentInChildren(); if (info.revealFacts != null) { From f4f22895bb923e0ca4af8d7658a14ec28417df2c Mon Sep 17 00:00:00 2001 From: Ben C Date: Mon, 14 Oct 2024 04:36:04 +0000 Subject: [PATCH 08/13] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 210 +++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index 100d8d92..d8fbfcd1 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -1326,6 +1326,27 @@ "items": { "$ref": "#/definitions/DreamArrivalPointInfo" } + }, + "grappleTotems": { + "type": "array", + "description": "Adds dream world grapple totems to this planet.", + "items": { + "$ref": "#/definitions/GrappleTotemInfo" + } + }, + "alarmTotems": { + "type": "array", + "description": "Adds dream world alarm totems to this planet.", + "items": { + "$ref": "#/definitions/AlarmTotemInfo" + } + }, + "portholes": { + "type": "array", + "description": "Adds portholes (the windows you can peek through in the DLC) to this planet.", + "items": { + "$ref": "#/definitions/PortholeInfo" + } } } }, @@ -3481,6 +3502,195 @@ } } }, + "GrappleTotemInfo": { + "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" + }, + "minDistance": { + "type": "number", + "description": "The minimum distance that the player must be from the grapple totem for it to activate.", + "format": "float", + "default": 10.0 + }, + "arrivalDistance": { + "type": "number", + "description": "The distance from the grapple totem that the player will stop at when it activates.", + "format": "float", + "default": 4.0 + }, + "maxAngle": { + "type": "number", + "description": "The maximum angle in degrees allowed between the grapple totem's face and the player's lantern in order to activate the totem.", + "format": "float", + "default": 45.0 + }, + "maxDistance": { + "type": "number", + "description": "The maximum distance allowed between the grapple totem's face and the player's lantern in order to activate the totem.", + "format": "float", + "default": 29.0 + } + } + }, + "AlarmTotemInfo": { + "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" + }, + "sightDistance": { + "type": "number", + "description": "The maximum distance of the alarm's \"vision cone\".", + "format": "float", + "default": 45.0 + }, + "sightAngle": { + "type": "number", + "description": "The width of the alarm's \"vision cone\" in degrees.", + "format": "float", + "default": 60.0 + } + } + }, + "PortholeInfo": { + "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" + }, + "revealFacts": { + "type": "array", + "description": "Fact IDs to reveal when peeking through the porthole.", + "items": { + "type": "string" + } + }, + "fieldOfView": { + "type": "number", + "description": "The field of view of the porthole camera.", + "format": "float", + "default": 90.0 + }, + "target": { + "description": "The location of the camera when the player peeks through the porthole. Can be placed on a different planet.", + "$ref": "#/definitions/PortholeTargetInfo" + } + } + }, + "PortholeTargetInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "parentBody": { + "type": "string", + "description": "The name of the planet that will be used with `parentPath`. Must be set if `parentPath` is set." + }, + "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" + } + } + }, "ReferenceFrameModule": { "type": "object", "additionalProperties": false, From 0752b11b65d5c77e7eae81e36d481ea880aef813 Mon Sep 17 00:00:00 2001 From: Joshua Thome Date: Mon, 14 Oct 2024 01:45:44 -0500 Subject: [PATCH 09/13] Dream candles --- NewHorizons/Builder/Props/DetailBuilder.cs | 10 +++ .../EchoesOfTheEye/DreamCandleBuilder.cs | 76 +++++++++++++++++++ NewHorizons/Builder/Props/PropBuildManager.cs | 14 ++-- NewHorizons/External/Modules/PropModule.cs | 21 +++-- .../Props/EchoesOfTheEye/DreamCandleInfo.cs | 24 ++++++ .../Props/EchoesOfTheEye/DreamCandleType.cs | 33 ++++++++ 6 files changed, 165 insertions(+), 13 deletions(-) create mode 100644 NewHorizons/Builder/Props/EchoesOfTheEye/DreamCandleBuilder.cs create mode 100644 NewHorizons/External/Modules/Props/EchoesOfTheEye/DreamCandleInfo.cs create mode 100644 NewHorizons/External/Modules/Props/EchoesOfTheEye/DreamCandleType.cs diff --git a/NewHorizons/Builder/Props/DetailBuilder.cs b/NewHorizons/Builder/Props/DetailBuilder.cs index e49feeee..4b499946 100644 --- a/NewHorizons/Builder/Props/DetailBuilder.cs +++ b/NewHorizons/Builder/Props/DetailBuilder.cs @@ -331,6 +331,16 @@ namespace NewHorizons.Builder.Props { remoteCameraPlatform._visualSector = sector; } + + else if(component is SingleLightSensor singleLightSensor && !existingSectors.Contains(singleLightSensor._sector)) + { + if (singleLightSensor._sector != null) + { + singleLightSensor._sector.OnSectorOccupantsUpdated -= singleLightSensor.OnSectorOccupantsUpdated; + } + singleLightSensor._sector = sector; + singleLightSensor._sector.OnSectorOccupantsUpdated += singleLightSensor.OnSectorOccupantsUpdated; + } } /// diff --git a/NewHorizons/Builder/Props/EchoesOfTheEye/DreamCandleBuilder.cs b/NewHorizons/Builder/Props/EchoesOfTheEye/DreamCandleBuilder.cs new file mode 100644 index 00000000..38387bf8 --- /dev/null +++ b/NewHorizons/Builder/Props/EchoesOfTheEye/DreamCandleBuilder.cs @@ -0,0 +1,76 @@ +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 System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace NewHorizons.Builder.Props.EchoesOfTheEye +{ + public static class DreamCandleBuilder + { + private static Dictionary _prefabs = new(); + + internal static void InitPrefabs() + { + InitPrefab(DreamCandleType.Ground, "DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_1/Interactibles_DreamZone_1/DreamHouseIsland/Prefab_IP_DreamCandle"); + InitPrefab(DreamCandleType.GroundSmall, "DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_2/Structure_DreamZone_2/City/StartingAreaLanterns/Prefab_IP_DreamCandle_Ground_Small"); + InitPrefab(DreamCandleType.GroundLarge, "DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_1/Interactibles_DreamZone_1/DreamHouseIsland/Prefab_IP_DreamCandle_Ground_Large"); + InitPrefab(DreamCandleType.GroundSingle, "DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_1/Sector_PartyHouse/Interactables_PartyHouse/Prefab_IP_DreamCandle_Ground_Single"); + InitPrefab(DreamCandleType.Wall, "DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_2/Structure_DreamZone_2/City/ParkLanterns/Prefab_IP_DreamCandle_Wall"); + InitPrefab(DreamCandleType.WallLargeFlame, "DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_2/Structure_DreamZone_2/City/FalseKnightHouse/CandleDoor/FirstDoorLanterns/Prefab_IP_DreamCandle_LargeFlame_Wall"); + InitPrefab(DreamCandleType.WallBigWick, "DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_2/Structure_DreamZone_2/DreamFireHouse_2/Interactibles_DreamFireHouse_2/Pivot_SlideReelRoom/CandleController/CandlePivot_0/Prefab_IP_DreamCandle_BigWick_Wall"); + InitPrefab(DreamCandleType.Standing, "DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_2/Structure_DreamZone_2/City/ElevatorHouse/CandleDoor/DoorTutorial/Prefab_IP_DreamCandle_LargeFlame_Standing"); + InitPrefab(DreamCandleType.Pile, "DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_3/Sector_Hotel/Gallery/Interactibles_Gallery/DreamCandles/Prefab_IP_DreamCandle_Pile"); + } + + private static void InitPrefab(DreamCandleType type, string path) + { + var prefab = _prefabs.ContainsKey(type) ? _prefabs[type] : null; + if (prefab == null) + { + prefab = SearchUtilities.Find(path).InstantiateInactive().Rename($"Prefab_DreamCandle_{type}").DontDestroyOnLoad(); + if (prefab == null) + { + NHLogger.LogWarning($"Tried to make a dream candle but couldn't. Do you have the DLC installed?"); + return; + } + else + { + prefab.AddComponent()._destroyOnDLCNotOwned = true; + var sensor = prefab.GetComponentInChildren(); + sensor._sector = null; + } + _prefabs.Add(type, prefab); + } + } + + public static GameObject Make(GameObject planetGO, Sector sector, DreamCandleInfo info, IModBehaviour mod) + { + InitPrefabs(); + + var prefab = _prefabs.ContainsKey(info.type) ? _prefabs[info.type] : null; + + if (prefab == null || sector == null) return null; + + var candleObj = DetailBuilder.Make(planetGO, sector, mod, prefab, new DetailInfo(info)); + + var dreamCandle = candleObj.GetComponent(); + + var sensor = candleObj.GetComponentInChildren(); + sensor._detectFlashlight = true; + sensor._lightSourceMask |= LightSourceType.FLASHLIGHT; + + dreamCandle._startLit = info.startLit; + dreamCandle.SetLit(info.startLit, false, true); + + return candleObj; + } + } +} diff --git a/NewHorizons/Builder/Props/PropBuildManager.cs b/NewHorizons/Builder/Props/PropBuildManager.cs index d171ae88..64c4221f 100644 --- a/NewHorizons/Builder/Props/PropBuildManager.cs +++ b/NewHorizons/Builder/Props/PropBuildManager.cs @@ -102,11 +102,15 @@ namespace NewHorizons.Builder.Props // If a prop has set its parentPath and the parent cannot be found, add it to the next pass and try again later nextPass = new List(); - if (Main.HasDLC) MakeGeneralProps(go, config.Props.portholes, (porthole) => PortholeBuilder.Make(go, sector, porthole, mod)); - if (Main.HasDLC) MakeGeneralProps(go, config.Props.alarmTotems, (totem) => AlarmTotemBuilder.Make(go, sector, totem, mod)); - if (Main.HasDLC) MakeGeneralProps(go, config.Props.grappleTotems, (totem) => GrappleTotemBuilder.Make(go, sector, totem, mod)); - if (Main.HasDLC) MakeGeneralProps(go, config.Props.dreamCampfires, (campfire) => DreamCampfireBuilder.Make(go, sector, campfire, mod), (campfire) => campfire.id); - if (Main.HasDLC) MakeGeneralProps(go, config.Props.dreamArrivalPoints, (point) => DreamArrivalPointBuilder.Make(go, sector, point, mod), (point) => point.id); + if (Main.HasDLC) + { + MakeGeneralProps(go, config.Props.dreamCandles, (candle) => DreamCandleBuilder.Make(go, sector, candle, mod)); + MakeGeneralProps(go, config.Props.portholes, (porthole) => PortholeBuilder.Make(go, sector, porthole, mod)); + MakeGeneralProps(go, config.Props.alarmTotems, (totem) => AlarmTotemBuilder.Make(go, sector, totem, mod)); + 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.gravityCannons, (cannon) => GravityCannonBuilder.Make(go, sector, cannon, mod), (cannon) => cannon.shuttleID); MakeGeneralProps(go, config.Props.shuttles, (shuttle) => ShuttleBuilder.Make(go, sector, mod, shuttle), (shuttle) => shuttle.id); MakeGeneralProps(go, config.Props.details, (detail) => DetailBuilder.Make(go, sector, mod, detail), (detail) => detail.path); diff --git a/NewHorizons/External/Modules/PropModule.cs b/NewHorizons/External/Modules/PropModule.cs index 046976d0..3ae2e82f 100644 --- a/NewHorizons/External/Modules/PropModule.cs +++ b/NewHorizons/External/Modules/PropModule.cs @@ -54,7 +54,7 @@ namespace NewHorizons.External.Modules public DetailInfo[] proxyDetails; /// - /// Add rafts to this planet + /// Add rafts to this planet (requires Echoes of the Eye DLC) /// public RaftInfo[] rafts; @@ -64,7 +64,7 @@ namespace NewHorizons.External.Modules public ScatterInfo[] scatter; /// - /// Add slideshows (from the DLC) to the planet + /// Add slideshows to the planet (requires Echoes of the Eye DLC) /// public ProjectionInfo[] slideShows; @@ -124,30 +124,35 @@ namespace NewHorizons.External.Modules public ShuttleInfo[] shuttles; /// - /// Add campfires that allow you to enter the dream world/simulation. Must be paired with a dream arrival point, which can be placed on this planet or elsewhere. + /// Add campfires that allow you to enter the dream world/simulation (requires Echoes of the Eye DLC). Must be paired with a dream arrival point, which can be placed on this planet or elsewhere. /// public DreamCampfireInfo[] dreamCampfires; - + /// - /// Add the points you will arrive at when entering the dream world/simulation from a paired dream campfire, which can be placed on this planet or elsewhere. The planet with the arrival point should be statically positioned to avoid issues with the simulation view materials. + /// Add the points you will arrive at when entering the dream world/simulation from a paired dream campfire (requires Echoes of the Eye DLC). The planet with the arrival point should be statically positioned to avoid issues with the simulation view materials. /// public DreamArrivalPointInfo[] dreamArrivalPoints; /// - /// Adds dream world grapple totems to this planet. + /// Adds dream world grapple totems to this planet (requires Echoes of the Eye DLC). /// public GrappleTotemInfo[] grappleTotems; /// - /// Adds dream world alarm totems to this planet. + /// Adds dream world alarm totems to this planet (requires Echoes of the Eye DLC). /// public AlarmTotemInfo[] alarmTotems; /// - /// Adds portholes (the windows you can peek through in the DLC) to this planet. + /// Adds portholes (the windows you can peek through in the Stranger) to this planet (requires Echoes of the Eye DLC). /// public PortholeInfo[] portholes; + /// + /// Adds dream world candles to this planet (requires Echoes of the Eye DLC). + /// + public DreamCandleInfo[] dreamCandles; + [Obsolete("reveal is deprecated. Use Volumes->revealVolumes instead.")] public RevealVolumeInfo[] reveal; [Obsolete("audioVolumes is deprecated. Use Volumes->audioVolumes instead.")] public AudioVolumeInfo[] audioVolumes; diff --git a/NewHorizons/External/Modules/Props/EchoesOfTheEye/DreamCandleInfo.cs b/NewHorizons/External/Modules/Props/EchoesOfTheEye/DreamCandleInfo.cs new file mode 100644 index 00000000..10106fb8 --- /dev/null +++ b/NewHorizons/External/Modules/Props/EchoesOfTheEye/DreamCandleInfo.cs @@ -0,0 +1,24 @@ +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.Props.EchoesOfTheEye +{ + [JsonObject] + public class DreamCandleInfo : GeneralPropInfo + { + /// + /// The type of dream candle this is. + /// + [DefaultValue(DreamCandleType.Ground)] public DreamCandleType type = DreamCandleType.Ground; + + /// + /// Whether the candle should start lit or extinguished. + /// + public bool startLit; + } +} diff --git a/NewHorizons/External/Modules/Props/EchoesOfTheEye/DreamCandleType.cs b/NewHorizons/External/Modules/Props/EchoesOfTheEye/DreamCandleType.cs new file mode 100644 index 00000000..07d1f764 --- /dev/null +++ b/NewHorizons/External/Modules/Props/EchoesOfTheEye/DreamCandleType.cs @@ -0,0 +1,33 @@ +using Newtonsoft.Json.Converters; +using Newtonsoft.Json; +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.EchoesOfTheEye +{ + [JsonConverter(typeof(StringEnumConverter))] + public enum DreamCandleType + { + [EnumMember(Value = @"ground")] Ground, + + [EnumMember(Value = @"groundSmall")] GroundSmall, + + [EnumMember(Value = @"groundLarge")] GroundLarge, + + [EnumMember(Value = @"groundSingle")] GroundSingle, + + [EnumMember(Value = @"wall")] Wall, + + [EnumMember(Value = @"wallLargeFlame")] WallLargeFlame, + + [EnumMember(Value = @"wallBigWick")] WallBigWick, + + [EnumMember(Value = @"standing")] Standing, + + [EnumMember(Value = @"pile")] Pile, + } +} From b85ec167db7ce588ca0e2ee825237700c497d0e5 Mon Sep 17 00:00:00 2001 From: Joshua Thome Date: Mon, 14 Oct 2024 11:26:08 -0500 Subject: [PATCH 10/13] Add vision cone model scaling for alarm totems --- .../Builder/Props/EchoesOfTheEye/AlarmTotemBuilder.cs | 6 ++++++ .../External/Modules/Props/EchoesOfTheEye/AlarmTotemInfo.cs | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/NewHorizons/Builder/Props/EchoesOfTheEye/AlarmTotemBuilder.cs b/NewHorizons/Builder/Props/EchoesOfTheEye/AlarmTotemBuilder.cs index cf9b0c23..46144b6b 100644 --- a/NewHorizons/Builder/Props/EchoesOfTheEye/AlarmTotemBuilder.cs +++ b/NewHorizons/Builder/Props/EchoesOfTheEye/AlarmTotemBuilder.cs @@ -48,6 +48,12 @@ namespace NewHorizons.Builder.Props.EchoesOfTheEye alarmTotem._sightAngle = info.sightAngle; alarmTotem._sightDistance = info.sightDistance; + if (info.stretchVisionCone != null) + { + var visionCone = totemObj.transform.Find("Effects_IP_SIM_AlarmTotem/AlarmTotemVisionCone"); + visionCone.localScale = Vector3.Scale(visionCone.localScale, info.stretchVisionCone); + } + return totemObj; } } diff --git a/NewHorizons/External/Modules/Props/EchoesOfTheEye/AlarmTotemInfo.cs b/NewHorizons/External/Modules/Props/EchoesOfTheEye/AlarmTotemInfo.cs index cc902dbf..94b0919f 100644 --- a/NewHorizons/External/Modules/Props/EchoesOfTheEye/AlarmTotemInfo.cs +++ b/NewHorizons/External/Modules/Props/EchoesOfTheEye/AlarmTotemInfo.cs @@ -20,5 +20,10 @@ namespace NewHorizons.External.Modules.Props.EchoesOfTheEye /// The width of the alarm's "vision cone" in degrees. /// [DefaultValue(60f)] public float sightAngle = 60f; + + /// + /// Scales the visible vision cone in the simulation view (does not affect the actual vision cone detection). + /// + public MVector3 stretchVisionCone; } } From 6c1605d0e9a272ccadf40a0f486322fb761982ba Mon Sep 17 00:00:00 2001 From: Joshua Thome Date: Mon, 14 Oct 2024 11:26:37 -0500 Subject: [PATCH 11/13] Fix alarm totem "face" angles when toggled by projection totem --- .../AlarmTotemPatches.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 NewHorizons/Patches/EchoesOfTheEyePatches/AlarmTotemPatches.cs diff --git a/NewHorizons/Patches/EchoesOfTheEyePatches/AlarmTotemPatches.cs b/NewHorizons/Patches/EchoesOfTheEyePatches/AlarmTotemPatches.cs new file mode 100644 index 00000000..5ec80d5d --- /dev/null +++ b/NewHorizons/Patches/EchoesOfTheEyePatches/AlarmTotemPatches.cs @@ -0,0 +1,21 @@ +using HarmonyLib; +using NewHorizons.Components.EOTE; +using System.Collections.Generic; +using System.Reflection.Emit; +using UnityEngine; + +namespace NewHorizons.Patches.EchoesOfTheEyePatches +{ + [HarmonyPatch(typeof(AlarmTotem))] + public static class AlarmTotemPatches + { + [HarmonyPostfix] + [HarmonyPatch(nameof(AlarmTotem.SetFaceOpen))] + public static void AlarmTotem_SetFaceOpen(AlarmTotem __instance, bool open) + { + // This method is unused in the base game and sets the rotations incorrectly (-90f instead of 90f); this corrects that + __instance._rightFaceCover.localEulerAngles = Vector3.up * (open ? 90f : 0f); + __instance._leftFaceCover.localEulerAngles = Vector3.up * (open ? -90f : 0f); + } + } +} From fb537f77309a15a1d0cc2c8ad98a1ea4b3036e6d Mon Sep 17 00:00:00 2001 From: Joshua Thome Date: Mon, 14 Oct 2024 11:27:05 -0500 Subject: [PATCH 12/13] Projection totems --- .../EchoesOfTheEye/ProjectionTotemBuilder.cs | 18 ++++++++++++++++-- NewHorizons/Builder/Props/PropBuildManager.cs | 11 ++++++----- NewHorizons/External/Modules/PropModule.cs | 5 +++++ .../Props/EchoesOfTheEye/AlarmTotemInfo.cs | 5 +++-- .../EchoesOfTheEye/ProjectionTotemInfo.cs | 5 +++++ 5 files changed, 35 insertions(+), 9 deletions(-) diff --git a/NewHorizons/Builder/Props/EchoesOfTheEye/ProjectionTotemBuilder.cs b/NewHorizons/Builder/Props/EchoesOfTheEye/ProjectionTotemBuilder.cs index fdd18663..69155147 100644 --- a/NewHorizons/Builder/Props/EchoesOfTheEye/ProjectionTotemBuilder.cs +++ b/NewHorizons/Builder/Props/EchoesOfTheEye/ProjectionTotemBuilder.cs @@ -10,6 +10,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using UnityEngine; +using UnityEngine.InputSystem; namespace NewHorizons.Builder.Props.EchoesOfTheEye { @@ -100,14 +101,27 @@ namespace NewHorizons.Builder.Props.EchoesOfTheEye { projectionObj.gameObject.AddComponent(); var projection = projectionObj.gameObject.AddComponent(); - projection._setActive = true; + projection._setActive = info.toggleProjectedObjectsActive; projection.Awake(); + projections.Add(projection); } } projector._projections = projections.ToArray(); } - projector.SetLit(info.startLit); + var sensor = projector._lightSensor as SingleLightSensor; + sensor._detectFlashlight = true; + sensor._lightSourceMask |= LightSourceType.FLASHLIGHT; + + projector._lit = info.startLit; + projector._startLit = info.startLit; + projector._extinguishOnly = info.extinguishOnly; + /* + Delay.FireOnNextUpdate(() => + { + projector.Start(); + }); + */ return totemObj; } diff --git a/NewHorizons/Builder/Props/PropBuildManager.cs b/NewHorizons/Builder/Props/PropBuildManager.cs index 64c4221f..74d638e9 100644 --- a/NewHorizons/Builder/Props/PropBuildManager.cs +++ b/NewHorizons/Builder/Props/PropBuildManager.cs @@ -102,6 +102,10 @@ namespace NewHorizons.Builder.Props // If a prop has set its parentPath and the parent cannot be found, add it to the next pass and try again later nextPass = new List(); + MakeGeneralProps(go, config.Props.gravityCannons, (cannon) => GravityCannonBuilder.Make(go, sector, cannon, mod), (cannon) => cannon.shuttleID); + MakeGeneralProps(go, config.Props.shuttles, (shuttle) => ShuttleBuilder.Make(go, sector, mod, shuttle), (shuttle) => shuttle.id); + MakeGeneralProps(go, config.Props.details, (detail) => DetailBuilder.Make(go, sector, mod, detail), (detail) => detail.path); + MakeGeneralProps(go, config.Props.geysers, (geyser) => GeyserBuilder.Make(go, sector, geyser)); if (Main.HasDLC) { MakeGeneralProps(go, config.Props.dreamCandles, (candle) => DreamCandleBuilder.Make(go, sector, candle, mod)); @@ -110,12 +114,8 @@ 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.rafts, (raft) => RaftBuilder.Make(go, sector, raft, planetBody)); } - MakeGeneralProps(go, config.Props.gravityCannons, (cannon) => GravityCannonBuilder.Make(go, sector, cannon, mod), (cannon) => cannon.shuttleID); - MakeGeneralProps(go, config.Props.shuttles, (shuttle) => ShuttleBuilder.Make(go, sector, mod, shuttle), (shuttle) => shuttle.id); - MakeGeneralProps(go, config.Props.details, (detail) => DetailBuilder.Make(go, sector, mod, detail), (detail) => detail.path); - MakeGeneralProps(go, config.Props.geysers, (geyser) => GeyserBuilder.Make(go, sector, geyser)); - if (Main.HasDLC) 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)); MakeGeneralProps(go, config.Props.volcanoes, (volcano) => VolcanoBuilder.Make(go, sector, volcano)); MakeGeneralProps(go, config.Props.dialogue, (dialogueInfo) => @@ -139,6 +139,7 @@ namespace NewHorizons.Builder.Props MakeGeneralProps(go, config.Props.warpTransmitters, (warpTransmitter) => WarpPadBuilder.Make(go, sector, mod, warpTransmitter), (warpTransmitter) => warpTransmitter.frequency); MakeGeneralProps(go, config.Props.audioSources, (audioSource) => AudioSourceBuilder.Make(go, sector, audioSource, mod), (audioSource) => audioSource.audio); RemoteBuilder.MakeGeneralProps(go, sector, config.Props.remotes, nhBody); + if (Main.HasDLC) MakeGeneralProps(go, config.Props.projectionTotems, (totem) => ProjectionTotemBuilder.Make(go, sector, totem, mod)); RunMultiPass(); diff --git a/NewHorizons/External/Modules/PropModule.cs b/NewHorizons/External/Modules/PropModule.cs index 3ae2e82f..b453b170 100644 --- a/NewHorizons/External/Modules/PropModule.cs +++ b/NewHorizons/External/Modules/PropModule.cs @@ -153,6 +153,11 @@ namespace NewHorizons.External.Modules /// public DreamCandleInfo[] dreamCandles; + /// + /// Adds dream world projection totems (requires Echoes of the Eye DLC). + /// + public ProjectionTotemInfo[] projectionTotems; + [Obsolete("reveal is deprecated. Use Volumes->revealVolumes instead.")] public RevealVolumeInfo[] reveal; [Obsolete("audioVolumes is deprecated. Use Volumes->audioVolumes instead.")] public AudioVolumeInfo[] audioVolumes; diff --git a/NewHorizons/External/Modules/Props/EchoesOfTheEye/AlarmTotemInfo.cs b/NewHorizons/External/Modules/Props/EchoesOfTheEye/AlarmTotemInfo.cs index 94b0919f..3537b978 100644 --- a/NewHorizons/External/Modules/Props/EchoesOfTheEye/AlarmTotemInfo.cs +++ b/NewHorizons/External/Modules/Props/EchoesOfTheEye/AlarmTotemInfo.cs @@ -1,3 +1,4 @@ +using NewHorizons.External.SerializableData; using Newtonsoft.Json; using System; using System.Collections.Generic; @@ -12,12 +13,12 @@ namespace NewHorizons.External.Modules.Props.EchoesOfTheEye public class AlarmTotemInfo : GeneralPropInfo { /// - /// The maximum distance of the alarm's "vision cone". + /// The maximum distance of the alarm's vision cone. /// [DefaultValue(45f)] public float sightDistance = 45; /// - /// The width of the alarm's "vision cone" in degrees. + /// The width of the alarm's vision cone in degrees. /// [DefaultValue(60f)] public float sightAngle = 60f; diff --git a/NewHorizons/External/Modules/Props/EchoesOfTheEye/ProjectionTotemInfo.cs b/NewHorizons/External/Modules/Props/EchoesOfTheEye/ProjectionTotemInfo.cs index 40b695bb..0852b7e9 100644 --- a/NewHorizons/External/Modules/Props/EchoesOfTheEye/ProjectionTotemInfo.cs +++ b/NewHorizons/External/Modules/Props/EchoesOfTheEye/ProjectionTotemInfo.cs @@ -40,5 +40,10 @@ namespace NewHorizons.External.Modules.Props.EchoesOfTheEye /// Relative paths from this planet to objects that will appear or disappear when this totem is lit or extinguished. Some types of objects and effects are not supported and will remain visible and active. /// public string[] pathsToProjectedObjects; + + /// + /// If set, projected objects will be set to fully active or fully disabled instantly instead of smoothly fading lights/renderers/colliders. Use this if the normal behavior is insufficient for the objects you're using. + /// + public bool toggleProjectedObjectsActive; } } From 71a485553ca5fde41afb7b3a3c6798b62489dbd7 Mon Sep 17 00:00:00 2001 From: Ben C Date: Mon, 14 Oct 2024 16:29:24 +0000 Subject: [PATCH 13/13] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 174 +++++++++++++++++++++++++-- 1 file changed, 165 insertions(+), 9 deletions(-) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index d8fbfcd1..1a87c106 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -1217,7 +1217,7 @@ }, "rafts": { "type": "array", - "description": "Add rafts to this planet", + "description": "Add rafts to this planet (requires Echoes of the Eye DLC)", "items": { "$ref": "#/definitions/RaftInfo" } @@ -1231,7 +1231,7 @@ }, "slideShows": { "type": "array", - "description": "Add slideshows (from the DLC) to the planet", + "description": "Add slideshows to the planet (requires Echoes of the Eye DLC)", "items": { "$ref": "#/definitions/ProjectionInfo" } @@ -1315,38 +1315,52 @@ }, "dreamCampfires": { "type": "array", - "description": "Add campfires that allow you to enter the dream world/simulation. Must be paired with a dream arrival point, which can be placed on this planet or elsewhere.", + "description": "Add campfires that allow you to enter the dream world/simulation (requires Echoes of the Eye DLC). Must be paired with a dream arrival point, which can be placed on this planet or elsewhere.", "items": { "$ref": "#/definitions/DreamCampfireInfo" } }, "dreamArrivalPoints": { "type": "array", - "description": "Add the points you will arrive at when entering the dream world/simulation from a paired dream campfire, which can be placed on this planet or elsewhere. The planet with the arrival point should be statically positioned to avoid issues with the simulation view materials.", + "description": "Add the points you will arrive at when entering the dream world/simulation from a paired dream campfire (requires Echoes of the Eye DLC). The planet with the arrival point should be statically positioned to avoid issues with the simulation view materials.", "items": { "$ref": "#/definitions/DreamArrivalPointInfo" } }, "grappleTotems": { "type": "array", - "description": "Adds dream world grapple totems to this planet.", + "description": "Adds dream world grapple totems to this planet (requires Echoes of the Eye DLC).", "items": { "$ref": "#/definitions/GrappleTotemInfo" } }, "alarmTotems": { "type": "array", - "description": "Adds dream world alarm totems to this planet.", + "description": "Adds dream world alarm totems to this planet (requires Echoes of the Eye DLC).", "items": { "$ref": "#/definitions/AlarmTotemInfo" } }, "portholes": { "type": "array", - "description": "Adds portholes (the windows you can peek through in the DLC) to this planet.", + "description": "Adds portholes (the windows you can peek through in the Stranger) to this planet (requires Echoes of the Eye DLC).", "items": { "$ref": "#/definitions/PortholeInfo" } + }, + "dreamCandles": { + "type": "array", + "description": "Adds dream world candles to this planet (requires Echoes of the Eye DLC).", + "items": { + "$ref": "#/definitions/DreamCandleInfo" + } + }, + "projectionTotems": { + "type": "array", + "description": "Adds dream world projection totems (requires Echoes of the Eye DLC).", + "items": { + "$ref": "#/definitions/ProjectionTotemInfo" + } } } }, @@ -3592,15 +3606,19 @@ }, "sightDistance": { "type": "number", - "description": "The maximum distance of the alarm's \"vision cone\".", + "description": "The maximum distance of the alarm's vision cone.", "format": "float", "default": 45.0 }, "sightAngle": { "type": "number", - "description": "The width of the alarm's \"vision cone\" in degrees.", + "description": "The width of the alarm's vision cone in degrees.", "format": "float", "default": 60.0 + }, + "stretchVisionCone": { + "description": "Scales the visible vision cone in the simulation view (does not affect the actual vision cone detection).", + "$ref": "#/definitions/MVector3" } } }, @@ -3691,6 +3709,144 @@ } } }, + "DreamCandleInfo": { + "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" + }, + "type": { + "description": "The type of dream candle this is.", + "default": "Ground", + "$ref": "#/definitions/DreamCandleType" + }, + "startLit": { + "type": "boolean", + "description": "Whether the candle should start lit or extinguished." + } + } + }, + "DreamCandleType": { + "type": "string", + "description": "", + "x-enumNames": [ + "Ground", + "GroundSmall", + "GroundLarge", + "GroundSingle", + "Wall", + "WallLargeFlame", + "WallBigWick", + "Standing", + "Pile" + ], + "enum": [ + "ground", + "groundSmall", + "groundLarge", + "groundSingle", + "wall", + "wallLargeFlame", + "wallBigWick", + "standing", + "pile" + ] + }, + "ProjectionTotemInfo": { + "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" + }, + "startLit": { + "type": "boolean", + "description": "Whether the totem should start lit or extinguished." + }, + "extinguishOnly": { + "type": "boolean", + "description": "Whether the projection totem should be able to extinguished but not be able to be lit again with the artifact. Mainly useful if `startLit` is set to true." + }, + "pathToAlarmTotem": { + "type": "string", + "description": "A relative path from this planet to an alarm totem that will be activated or deactivated based on whether this totem is lit." + }, + "pathsToDreamCandles": { + "type": "array", + "description": "Relative paths from this planet to objects containing dream candles that will be activated or deactivated based on whether this totem is lit. All dream candles in the selected objects will be connected to this totem, so they do not need to be specified individually if a parent object is specified.", + "items": { + "type": "string" + } + }, + "pathsToProjectionTotems": { + "type": "array", + "description": "Relative paths from this planet to projection totems that will be deactivated if this totem is extinguished. All projection totems in the selected objects will be connected to this totem, so they do not need to be specified individually if a parent object is specified.", + "items": { + "type": "string" + } + }, + "pathsToProjectedObjects": { + "type": "array", + "description": "Relative paths from this planet to objects that will appear or disappear when this totem is lit or extinguished. Some types of objects and effects are not supported and will remain visible and active.", + "items": { + "type": "string" + } + }, + "toggleProjectedObjectsActive": { + "type": "boolean", + "description": "If set, projected objects will be set to fully active or fully disabled instantly instead of smoothly fading lights/renderers/colliders. Use this if the normal behavior is insufficient for the objects you're using." + } + } + }, "ReferenceFrameModule": { "type": "object", "additionalProperties": false,