From cca8e375fbd0c73765ff1fa8f2ad3b65cc7defe1 Mon Sep 17 00:00:00 2001 From: Nick Date: Tue, 26 Jul 2022 12:30:01 -0400 Subject: [PATCH] Change line endings --- NewHorizons/Builder/Props/NomaiTextBuilder.cs | 1190 ++--- NewHorizons/Builder/Props/SignalBuilder.cs | 484 +- NewHorizons/External/Modules/PropModule.cs | 1608 +++--- NewHorizons/Schemas/body_schema.json | 4318 ++++++++--------- 4 files changed, 3800 insertions(+), 3800 deletions(-) diff --git a/NewHorizons/Builder/Props/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiTextBuilder.cs index a7624f00..e0a404e1 100644 --- a/NewHorizons/Builder/Props/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiTextBuilder.cs @@ -1,595 +1,595 @@ -using NewHorizons.External.Modules; -using NewHorizons.Handlers; -using NewHorizons.Utility; -using OWML.Common; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Xml; -using UnityEngine; -using Logger = NewHorizons.Utility.Logger; -using Random = UnityEngine.Random; -namespace NewHorizons.Builder.Props -{ - public static class NomaiTextBuilder - { - private static List _arcPrefabs; - private static List _childArcPrefabs; - private static List _ghostArcPrefabs; - private static GameObject _scrollPrefab; - private static GameObject _computerPrefab; - private static GameObject _preCrashComputerPrefab; - private static GameObject _cairnPrefab; - private static GameObject _recorderPrefab; - private static GameObject _preCrashRecorderPrefab; - - private static Dictionary arcInfoToCorrespondingSpawnedGameObject = new Dictionary(); - public static GameObject GetSpawnedGameObjectByNomaiTextArcInfo(PropModule.NomaiTextArcInfo arc) - { - if (!arcInfoToCorrespondingSpawnedGameObject.ContainsKey(arc)) return null; - return arcInfoToCorrespondingSpawnedGameObject[arc]; - } - - private static Dictionary conversationInfoToCorrespondingSpawnedGameObject = new Dictionary(); - - public static GameObject GetSpawnedGameObjectByNomaiTextInfo(PropModule.NomaiTextInfo convo) - { - Logger.LogVerbose("Retrieving wall text obj for " + convo); - if (!conversationInfoToCorrespondingSpawnedGameObject.ContainsKey(convo)) return null; - return conversationInfoToCorrespondingSpawnedGameObject[convo]; - } - - public static List GetArcPrefabs() { return _arcPrefabs; } - public static List GetChildArcPrefabs() { return _childArcPrefabs; } - public static List GetGhostArcPrefabs() { return _ghostArcPrefabs; } - - private static void InitPrefabs() - { - // Just take every scroll and get the first arc - var existingArcs = GameObject.FindObjectsOfType().Select(x => x?._nomaiWallText?.gameObject?.transform?.Find("Arc 1")?.gameObject).Where(x => x != null).ToArray(); - _arcPrefabs = new List(); - _childArcPrefabs = new List(); - foreach (var existingArc in existingArcs) - { - if (existingArc.GetComponent().material.name.Contains("Child")) - { - var arc = existingArc.InstantiateInactive(); - arc.name = "Arc (Child)"; - _childArcPrefabs.Add(arc); - } - else - { - var arc = existingArc.InstantiateInactive(); - arc.name = "Arc"; - _arcPrefabs.Add(arc); - } - } - - var existingGhostArcs = GameObject.FindObjectsOfType().Select(x => x?._textLine?.gameObject).Where(x => x != null).ToArray(); - _ghostArcPrefabs = new List(); - foreach (var existingArc in existingGhostArcs) - { - var arc = existingArc.InstantiateInactive(); - arc.name = "Arc (Ghost)"; - _ghostArcPrefabs.Add(arc); - } - - _scrollPrefab = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_NorthHemisphere/Sector_NorthPole/Sector_HangingCity/Sector_HangingCity_District2/Interactables_HangingCity_District2/Prefab_NOM_Scroll").InstantiateInactive(); - _scrollPrefab.name = "Prefab_NOM_Scroll"; - - _computerPrefab = SearchUtilities.Find("VolcanicMoon_Body/Sector_VM/Interactables_VM/Prefab_NOM_Computer").InstantiateInactive(); - _computerPrefab.name = "Prefab_NOM_Computer"; - _computerPrefab.transform.rotation = Quaternion.identity; - - _preCrashComputerPrefab = SearchUtilities.Find("DB_EscapePodDimension_Body/Sector_EscapePodDimension/Sector_EscapePodBody/Interactables_EscapePodBody/Prefab_NOM_Vessel_Computer").InstantiateInactive(); - _preCrashComputerPrefab.name = "Prefab_NOM_Vessel_Computer"; - _preCrashComputerPrefab.transform.rotation = Quaternion.identity; - - _cairnPrefab = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_Crossroads/Interactables_Crossroads/Trailmarkers/Prefab_NOM_BH_Cairn_Arc (1)").InstantiateInactive(); - _cairnPrefab.name = "Prefab_NOM_Cairn"; - _cairnPrefab.transform.rotation = Quaternion.identity; - - _recorderPrefab = SearchUtilities.Find("Comet_Body/Prefab_NOM_Shuttle/Sector_NomaiShuttleInterior/Interactibles_NomaiShuttleInterior/Prefab_NOM_Recorder").InstantiateInactive(); - _recorderPrefab.name = "Prefab_NOM_Recorder"; - _recorderPrefab.transform.rotation = Quaternion.identity; - - _preCrashRecorderPrefab = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_EscapePodCrashSite/Sector_CrashFragment/Interactables_CrashFragment/Prefab_NOM_Recorder").InstantiateInactive(); - _preCrashRecorderPrefab.name = "Prefab_NOM_Recorder_Vessel"; - _preCrashRecorderPrefab.transform.rotation = Quaternion.identity; - } - - public static GameObject Make(GameObject planetGO, Sector sector, PropModule.NomaiTextInfo info, IModBehaviour mod) - { - if (_scrollPrefab == null) InitPrefabs(); - - var xmlPath = File.ReadAllText(mod.ModHelper.Manifest.ModFolderPath + info.xmlFile); - - switch (info.type) - { - case PropModule.NomaiTextInfo.NomaiTextType.Wall: - { - var nomaiWallTextObj = MakeWallText(planetGO, sector, info, xmlPath).gameObject; - - if (!string.IsNullOrEmpty(info.rename)) - { - nomaiWallTextObj.name = info.rename; - } - - nomaiWallTextObj.transform.parent = sector?.transform ?? planetGO.transform; - - if (!string.IsNullOrEmpty(info.parentPath)) - { - nomaiWallTextObj.transform.parent = planetGO.transform.Find(info.parentPath); - } - - nomaiWallTextObj.transform.position = planetGO.transform.TransformPoint(info.position); - if (info.normal != null) - { - // In global coordinates (normal was in local coordinates) - var up = (nomaiWallTextObj.transform.position - planetGO.transform.position).normalized; - var forward = planetGO.transform.TransformDirection(info.normal).normalized; - - nomaiWallTextObj.transform.up = up; - nomaiWallTextObj.transform.forward = forward; - } - if (info.rotation != null) - { - nomaiWallTextObj.transform.rotation = planetGO.transform.TransformRotation(Quaternion.Euler(info.rotation)); - } - - nomaiWallTextObj.SetActive(true); - conversationInfoToCorrespondingSpawnedGameObject[info] = nomaiWallTextObj; - - return nomaiWallTextObj; - } - case PropModule.NomaiTextInfo.NomaiTextType.Scroll: - { - var customScroll = _scrollPrefab.InstantiateInactive(); - - if (!string.IsNullOrEmpty(info.rename)) - { - customScroll.name = info.rename; - } - - var nomaiWallText = MakeWallText(planetGO, sector, info, xmlPath); - nomaiWallText.transform.parent = customScroll.transform; - nomaiWallText.transform.localPosition = Vector3.zero; - nomaiWallText.transform.localRotation = Quaternion.identity; - - nomaiWallText._showTextOnStart = false; - - // Don't want to be able to translate until its in a socket - nomaiWallText.GetComponent().enabled = false; - - nomaiWallText.gameObject.SetActive(true); - - var scrollItem = customScroll.GetComponent(); - - // Idk why this thing is always around - GameObject.Destroy(customScroll.transform.Find("Arc_BH_City_Forum_2").gameObject); - - // This variable is the bane of my existence i dont get it - scrollItem._nomaiWallText = nomaiWallText; - - // Because the scroll was already awake it does weird shit in Awake and makes some of the entries in this array be null - scrollItem._colliders = new OWCollider[] { scrollItem.GetComponent() }; - - // Else when you put them down you can't pick them back up - customScroll.GetComponent()._physicsRemoved = false; - - // Place scroll - customScroll.transform.parent = sector?.transform ?? planetGO.transform; - - if (!string.IsNullOrEmpty(info.parentPath)) - { - customScroll.transform.parent = planetGO.transform.Find(info.parentPath); - } - - customScroll.transform.position = planetGO.transform.TransformPoint(info.position ?? Vector3.zero); - - var up = planetGO.transform.InverseTransformPoint(customScroll.transform.position).normalized; - customScroll.transform.rotation = Quaternion.FromToRotation(customScroll.transform.up, up) * customScroll.transform.rotation; - - customScroll.SetActive(true); - - // Enable the collider and renderer - Delay.RunWhen( - () => Main.IsSystemReady, - () => - { - Logger.LogVerbose("Fixing scroll!"); - scrollItem._nomaiWallText = nomaiWallText; - scrollItem.SetSector(sector); - customScroll.transform.Find("Props_NOM_Scroll/Props_NOM_Scroll_Geo").GetComponent().enabled = true; - customScroll.transform.Find("Props_NOM_Scroll/Props_NOM_Scroll_Collider").gameObject.SetActive(true); - nomaiWallText.gameObject.GetComponent().enabled = false; - customScroll.GetComponent().enabled = true; - } - ); - conversationInfoToCorrespondingSpawnedGameObject[info] = customScroll; - - return customScroll; - } - case PropModule.NomaiTextInfo.NomaiTextType.Computer: - { - var computerObject = _computerPrefab.InstantiateInactive(); - - if (!string.IsNullOrEmpty(info.rename)) - { - computerObject.name = info.rename; - } - - computerObject.transform.parent = sector?.transform ?? planetGO.transform; - - if (!string.IsNullOrEmpty(info.parentPath)) - { - computerObject.transform.parent = planetGO.transform.Find(info.parentPath); - } - - computerObject.transform.position = planetGO.transform.TransformPoint(info?.position ?? Vector3.zero); - - var up = computerObject.transform.position - planetGO.transform.position; - if (info.normal != null) up = planetGO.transform.TransformDirection(info.normal); - computerObject.transform.rotation = Quaternion.FromToRotation(Vector3.up, up) * computerObject.transform.rotation; - - var computer = computerObject.GetComponent(); - computer.SetSector(sector); - - computer._dictNomaiTextData = MakeNomaiTextDict(xmlPath); - computer._nomaiTextAsset = new TextAsset(xmlPath); - computer._nomaiTextAsset.name = Path.GetFileNameWithoutExtension(info.xmlFile); - AddTranslation(xmlPath); - - // Make sure the computer model is loaded - StreamingHandler.SetUpStreaming(computerObject, sector); - - computerObject.SetActive(true); - conversationInfoToCorrespondingSpawnedGameObject[info] = computerObject; - - return computerObject; - } - case PropModule.NomaiTextInfo.NomaiTextType.PreCrashComputer: - { - var computerObject = DetailBuilder.MakeDetail(planetGO, sector, _preCrashComputerPrefab, info.position, Vector3.zero, 1, false); - computerObject.SetActive(false); - - if (!string.IsNullOrEmpty(info.rename)) - { - computerObject.name = info.rename; - } - - if (!string.IsNullOrEmpty(info.parentPath)) - { - computerObject.transform.SetParent(planetGO.transform.Find(info.parentPath), true); - } - - var up = computerObject.transform.position - planetGO.transform.position; - if (info.normal != null) up = planetGO.transform.TransformDirection(info.normal); - computerObject.transform.rotation = Quaternion.FromToRotation(Vector3.up, up) * computerObject.transform.rotation; - - // Move it slightly up more - computerObject.transform.position += up.normalized * 0.1f; - - var computer = computerObject.GetComponent(); - computer.SetSector(sector); - - computer._dictNomaiTextData = MakeNomaiTextDict(xmlPath); - computer._nomaiTextAsset = new TextAsset(xmlPath); - computer._nomaiTextAsset.name = Path.GetFileNameWithoutExtension(info.xmlFile); - AddTranslation(xmlPath); - - computerObject.SetActive(true); - - // All rings are rendered by detail builder so dont do that (have to wait for entries to be set) - Delay.FireOnNextUpdate(() => - { - for (var i = computer.GetNumTextBlocks(); i < 5; i++) - { - var ring = computer._computerRings[i]; - ring.gameObject.SetActive(false); - } - }); - - conversationInfoToCorrespondingSpawnedGameObject[info] = computerObject; - - return computerObject; - } - case PropModule.NomaiTextInfo.NomaiTextType.Cairn: - { - var cairnObject = _cairnPrefab.InstantiateInactive(); - - if (!string.IsNullOrEmpty(info.rename)) - { - cairnObject.name = info.rename; - } - - cairnObject.transform.parent = sector?.transform ?? planetGO.transform; - - if (!string.IsNullOrEmpty(info.parentPath)) - { - cairnObject.transform.parent = planetGO.transform.Find(info.parentPath); - } - - cairnObject.transform.position = planetGO.transform.TransformPoint(info?.position ?? Vector3.zero); - - if (info.rotation != null) - { - cairnObject.transform.rotation = planetGO.transform.TransformRotation(Quaternion.Euler(info.rotation)); - } - else - { - // By default align it to normal - var up = (cairnObject.transform.position - planetGO.transform.position).normalized; - cairnObject.transform.rotation = Quaternion.FromToRotation(Vector3.up, up) * cairnObject.transform.rotation; - } - - // Idk do we have to set it active before finding things? - cairnObject.SetActive(true); - - // Make it do the thing when it finishes being knocked over - foreach (var rock in cairnObject.GetComponent()._rocks) - { - rock._returning = false; - rock._owCollider.SetActivation(true); - rock.enabled = false; - } - - // So we can actually knock it over - cairnObject.GetComponent().enabled = true; - - var nomaiWallText = cairnObject.transform.Find("Props_TH_ClutterSmall/Arc_Short").GetComponent(); - nomaiWallText.SetSector(sector); - - nomaiWallText._dictNomaiTextData = MakeNomaiTextDict(xmlPath); - nomaiWallText._nomaiTextAsset = new TextAsset(xmlPath); - nomaiWallText._nomaiTextAsset.name = Path.GetFileNameWithoutExtension(info.xmlFile); - AddTranslation(xmlPath); - - // Make sure the computer model is loaded - StreamingHandler.SetUpStreaming(cairnObject, sector); - conversationInfoToCorrespondingSpawnedGameObject[info] = cairnObject; - - return cairnObject; - } - case PropModule.NomaiTextInfo.NomaiTextType.PreCrashRecorder: - case PropModule.NomaiTextInfo.NomaiTextType.Recorder: - { - var recorderObject = (info.type == PropModule.NomaiTextInfo.NomaiTextType.PreCrashRecorder ? _preCrashRecorderPrefab : _recorderPrefab).InstantiateInactive(); - - if (!string.IsNullOrEmpty(info.rename)) - { - recorderObject.name = info.rename; - } - - recorderObject.transform.parent = sector?.transform ?? planetGO.transform; - - if (!string.IsNullOrEmpty(info.parentPath)) - { - recorderObject.transform.parent = planetGO.transform.Find(info.parentPath); - } - - recorderObject.transform.position = planetGO.transform.TransformPoint(info?.position ?? Vector3.zero); - - if (info.rotation != null) - { - recorderObject.transform.rotation = planetGO.transform.TransformRotation(Quaternion.Euler(info.rotation)); - } - else - { - var up = recorderObject.transform.position - planetGO.transform.position; - recorderObject.transform.rotation = Quaternion.FromToRotation(Vector3.up, up) * recorderObject.transform.rotation; - } - - var nomaiText = recorderObject.GetComponentInChildren(); - nomaiText.SetSector(sector); - - nomaiText._dictNomaiTextData = MakeNomaiTextDict(xmlPath); - nomaiText._nomaiTextAsset = new TextAsset(xmlPath); - nomaiText._nomaiTextAsset.name = Path.GetFileNameWithoutExtension(info.xmlFile); - AddTranslation(xmlPath); - - // Make sure the recorder model is loaded - StreamingHandler.SetUpStreaming(recorderObject, sector); - - recorderObject.SetActive(true); - - recorderObject.transform.Find("InteractSphere").gameObject.GetComponent().enabled = true; - conversationInfoToCorrespondingSpawnedGameObject[info] = recorderObject; - return recorderObject; - } - default: - Logger.LogError($"Unsupported NomaiText type {info.type}"); - return null; - } - } - - private static NomaiWallText MakeWallText(GameObject go, Sector sector, PropModule.NomaiTextInfo info, string xmlPath) - { - GameObject nomaiWallTextObj = new GameObject("NomaiWallText"); - nomaiWallTextObj.SetActive(false); - - var box = nomaiWallTextObj.AddComponent(); - box.center = new Vector3(-0.0643f, 1.1254f, 0f); - box.size = new Vector3(6.1424f, 5.2508f, 0.5f); - - box.isTrigger = true; - - nomaiWallTextObj.AddComponent(); - var nomaiWallText = nomaiWallTextObj.AddComponent(); - - var text = new TextAsset(xmlPath); - - // Text assets need a name to be used with VoiceMod - text.name = Path.GetFileNameWithoutExtension(info.xmlFile); - - BuildArcs(xmlPath, nomaiWallText, nomaiWallTextObj, info); - AddTranslation(xmlPath); - nomaiWallText._nomaiTextAsset = text; - - nomaiWallText.SetTextAsset(text); - - return nomaiWallText; - } - - internal static void BuildArcs(string xml, NomaiWallText nomaiWallText, GameObject conversationZone, PropModule.NomaiTextInfo info) - { - var dict = MakeNomaiTextDict(xml); - - nomaiWallText._dictNomaiTextData = dict; - - RefreshArcs(nomaiWallText, conversationZone, info); - } - - internal static void RefreshArcs(NomaiWallText nomaiWallText, GameObject conversationZone, PropModule.NomaiTextInfo info) - { - var dict = nomaiWallText._dictNomaiTextData; - Random.InitState(info.seed); - - var arcsByID = new Dictionary(); - - if (info.arcInfo != null && info.arcInfo.Count() != dict.Values.Count()) - { - Logger.LogError($"Can't make NomaiWallText, arcInfo length [{info.arcInfo.Count()}] doesn't equal text entries [{dict.Values.Count()}]"); - return; - } - - var i = 0; - foreach (var textData in dict.Values) - { - var arcInfo = info.arcInfo?.Length > i ? info.arcInfo[i] : null; - var textEntryID = textData.ID; - var parentID = textData.ParentID; - - var parent = parentID == -1 ? null : arcsByID[parentID]; - - GameObject arc = MakeArc(arcInfo, conversationZone, parent, textEntryID); - arc.name = $"Arc {i} - Child of {parentID}"; - - arcsByID.Add(textEntryID, arc); - - i++; - } - } - - internal static GameObject MakeArc(PropModule.NomaiTextArcInfo arcInfo, GameObject conversationZone, GameObject parent, int textEntryID) - { - GameObject arc; - var type = arcInfo != null ? arcInfo.type : PropModule.NomaiTextArcInfo.NomaiTextArcType.Adult; - var variation = arcInfo != null ? arcInfo.variation : -1; - switch (type) - { - case PropModule.NomaiTextArcInfo.NomaiTextArcType.Child: - variation = variation < 0 - ? Random.Range(0, _childArcPrefabs.Count()) - : (variation % _childArcPrefabs.Count()); - arc = _childArcPrefabs[variation].InstantiateInactive(); - break; - case PropModule.NomaiTextArcInfo.NomaiTextArcType.Stranger when _ghostArcPrefabs.Any(): - variation = variation < 0 - ? Random.Range(0, _ghostArcPrefabs.Count()) - : (variation % _ghostArcPrefabs.Count()); - arc = _ghostArcPrefabs[variation].InstantiateInactive(); - break; - case PropModule.NomaiTextArcInfo.NomaiTextArcType.Adult: - default: - variation = variation < 0 - ? Random.Range(0, _arcPrefabs.Count()) - : (variation % _arcPrefabs.Count()); - arc = _arcPrefabs[variation].InstantiateInactive(); - break; - } - - arc.transform.parent = conversationZone.transform; - arc.GetComponent()._prebuilt = false; - - if (arcInfo != null) - { - arcInfo.variation = variation; - if (arcInfo.position == null) arc.transform.localPosition = Vector3.zero; - else arc.transform.localPosition = new Vector3(arcInfo.position.x, arcInfo.position.y, 0); - - arc.transform.localRotation = Quaternion.Euler(0, 0, arcInfo.zRotation); - - if (arcInfo.mirror) arc.transform.localScale = new Vector3(-1, 1, 1); - } - // Try auto I guess - else - { - if (parent == null) - { - arc.transform.localPosition = Vector3.zero; - } - else - { - var points = parent.GetComponent().GetPoints(); - var point = points[points.Count() / 2]; - - arc.transform.localPosition = point; - arc.transform.localRotation = Quaternion.Euler(0, 0, Random.Range(0, 360)); - } - } - - arc.GetComponent().SetEntryID(textEntryID); - arc.GetComponent().enabled = false; - - arc.SetActive(true); - - if (arcInfo != null) arcInfoToCorrespondingSpawnedGameObject[arcInfo] = arc; - - return arc; - } - - private static Dictionary MakeNomaiTextDict(string xmlPath) - { - var dict = new Dictionary(); - - XmlDocument xmlDocument = new XmlDocument(); - xmlDocument.LoadXml(xmlPath); - XmlNode rootNode = xmlDocument.SelectSingleNode("NomaiObject"); - - foreach (object obj in rootNode.SelectNodes("TextBlock")) - { - XmlNode xmlNode = (XmlNode)obj; - - int textEntryID = -1; - int parentID = -1; - - XmlNode textNode = xmlNode.SelectSingleNode("Text"); - XmlNode entryIDNode = xmlNode.SelectSingleNode("ID"); - XmlNode parentIDNode = xmlNode.SelectSingleNode("ParentID"); - - if (entryIDNode != null && !int.TryParse(entryIDNode.InnerText, out textEntryID)) - { - Logger.LogError($"Couldn't parse int ID in [{entryIDNode?.InnerText}] for [{xmlPath}]"); - textEntryID = -1; - } - - if (parentIDNode != null && !int.TryParse(parentIDNode.InnerText, out parentID)) - { - Logger.LogError($"Couldn't parse int ParentID in [{parentIDNode?.InnerText}] for [{xmlPath}]"); - parentID = -1; - } - - NomaiText.NomaiTextData value = new NomaiText.NomaiTextData(textEntryID, parentID, textNode, false, NomaiText.Location.UNSPECIFIED); - dict.Add(textEntryID, value); - } - return dict; - } - - private static void AddTranslation(string xmlPath) - { - XmlDocument xmlDocument = new XmlDocument(); - xmlDocument.LoadXml(xmlPath); - - XmlNode xmlNode = xmlDocument.SelectSingleNode("NomaiObject"); - XmlNodeList xmlNodeList = xmlNode.SelectNodes("TextBlock"); - - foreach (object obj in xmlNodeList) - { - XmlNode xmlNode2 = (XmlNode)obj; - var text = xmlNode2.SelectSingleNode("Text").InnerText; - TranslationHandler.AddDialogue(text); - } - } - } -} +using NewHorizons.External.Modules; +using NewHorizons.Handlers; +using NewHorizons.Utility; +using OWML.Common; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml; +using UnityEngine; +using Logger = NewHorizons.Utility.Logger; +using Random = UnityEngine.Random; +namespace NewHorizons.Builder.Props +{ + public static class NomaiTextBuilder + { + private static List _arcPrefabs; + private static List _childArcPrefabs; + private static List _ghostArcPrefabs; + private static GameObject _scrollPrefab; + private static GameObject _computerPrefab; + private static GameObject _preCrashComputerPrefab; + private static GameObject _cairnPrefab; + private static GameObject _recorderPrefab; + private static GameObject _preCrashRecorderPrefab; + + private static Dictionary arcInfoToCorrespondingSpawnedGameObject = new Dictionary(); + public static GameObject GetSpawnedGameObjectByNomaiTextArcInfo(PropModule.NomaiTextArcInfo arc) + { + if (!arcInfoToCorrespondingSpawnedGameObject.ContainsKey(arc)) return null; + return arcInfoToCorrespondingSpawnedGameObject[arc]; + } + + private static Dictionary conversationInfoToCorrespondingSpawnedGameObject = new Dictionary(); + + public static GameObject GetSpawnedGameObjectByNomaiTextInfo(PropModule.NomaiTextInfo convo) + { + Logger.LogVerbose("Retrieving wall text obj for " + convo); + if (!conversationInfoToCorrespondingSpawnedGameObject.ContainsKey(convo)) return null; + return conversationInfoToCorrespondingSpawnedGameObject[convo]; + } + + public static List GetArcPrefabs() { return _arcPrefabs; } + public static List GetChildArcPrefabs() { return _childArcPrefabs; } + public static List GetGhostArcPrefabs() { return _ghostArcPrefabs; } + + private static void InitPrefabs() + { + // Just take every scroll and get the first arc + var existingArcs = GameObject.FindObjectsOfType().Select(x => x?._nomaiWallText?.gameObject?.transform?.Find("Arc 1")?.gameObject).Where(x => x != null).ToArray(); + _arcPrefabs = new List(); + _childArcPrefabs = new List(); + foreach (var existingArc in existingArcs) + { + if (existingArc.GetComponent().material.name.Contains("Child")) + { + var arc = existingArc.InstantiateInactive(); + arc.name = "Arc (Child)"; + _childArcPrefabs.Add(arc); + } + else + { + var arc = existingArc.InstantiateInactive(); + arc.name = "Arc"; + _arcPrefabs.Add(arc); + } + } + + var existingGhostArcs = GameObject.FindObjectsOfType().Select(x => x?._textLine?.gameObject).Where(x => x != null).ToArray(); + _ghostArcPrefabs = new List(); + foreach (var existingArc in existingGhostArcs) + { + var arc = existingArc.InstantiateInactive(); + arc.name = "Arc (Ghost)"; + _ghostArcPrefabs.Add(arc); + } + + _scrollPrefab = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_NorthHemisphere/Sector_NorthPole/Sector_HangingCity/Sector_HangingCity_District2/Interactables_HangingCity_District2/Prefab_NOM_Scroll").InstantiateInactive(); + _scrollPrefab.name = "Prefab_NOM_Scroll"; + + _computerPrefab = SearchUtilities.Find("VolcanicMoon_Body/Sector_VM/Interactables_VM/Prefab_NOM_Computer").InstantiateInactive(); + _computerPrefab.name = "Prefab_NOM_Computer"; + _computerPrefab.transform.rotation = Quaternion.identity; + + _preCrashComputerPrefab = SearchUtilities.Find("DB_EscapePodDimension_Body/Sector_EscapePodDimension/Sector_EscapePodBody/Interactables_EscapePodBody/Prefab_NOM_Vessel_Computer").InstantiateInactive(); + _preCrashComputerPrefab.name = "Prefab_NOM_Vessel_Computer"; + _preCrashComputerPrefab.transform.rotation = Quaternion.identity; + + _cairnPrefab = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_Crossroads/Interactables_Crossroads/Trailmarkers/Prefab_NOM_BH_Cairn_Arc (1)").InstantiateInactive(); + _cairnPrefab.name = "Prefab_NOM_Cairn"; + _cairnPrefab.transform.rotation = Quaternion.identity; + + _recorderPrefab = SearchUtilities.Find("Comet_Body/Prefab_NOM_Shuttle/Sector_NomaiShuttleInterior/Interactibles_NomaiShuttleInterior/Prefab_NOM_Recorder").InstantiateInactive(); + _recorderPrefab.name = "Prefab_NOM_Recorder"; + _recorderPrefab.transform.rotation = Quaternion.identity; + + _preCrashRecorderPrefab = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_EscapePodCrashSite/Sector_CrashFragment/Interactables_CrashFragment/Prefab_NOM_Recorder").InstantiateInactive(); + _preCrashRecorderPrefab.name = "Prefab_NOM_Recorder_Vessel"; + _preCrashRecorderPrefab.transform.rotation = Quaternion.identity; + } + + public static GameObject Make(GameObject planetGO, Sector sector, PropModule.NomaiTextInfo info, IModBehaviour mod) + { + if (_scrollPrefab == null) InitPrefabs(); + + var xmlPath = File.ReadAllText(mod.ModHelper.Manifest.ModFolderPath + info.xmlFile); + + switch (info.type) + { + case PropModule.NomaiTextInfo.NomaiTextType.Wall: + { + var nomaiWallTextObj = MakeWallText(planetGO, sector, info, xmlPath).gameObject; + + if (!string.IsNullOrEmpty(info.rename)) + { + nomaiWallTextObj.name = info.rename; + } + + nomaiWallTextObj.transform.parent = sector?.transform ?? planetGO.transform; + + if (!string.IsNullOrEmpty(info.parentPath)) + { + nomaiWallTextObj.transform.parent = planetGO.transform.Find(info.parentPath); + } + + nomaiWallTextObj.transform.position = planetGO.transform.TransformPoint(info.position); + if (info.normal != null) + { + // In global coordinates (normal was in local coordinates) + var up = (nomaiWallTextObj.transform.position - planetGO.transform.position).normalized; + var forward = planetGO.transform.TransformDirection(info.normal).normalized; + + nomaiWallTextObj.transform.up = up; + nomaiWallTextObj.transform.forward = forward; + } + if (info.rotation != null) + { + nomaiWallTextObj.transform.rotation = planetGO.transform.TransformRotation(Quaternion.Euler(info.rotation)); + } + + nomaiWallTextObj.SetActive(true); + conversationInfoToCorrespondingSpawnedGameObject[info] = nomaiWallTextObj; + + return nomaiWallTextObj; + } + case PropModule.NomaiTextInfo.NomaiTextType.Scroll: + { + var customScroll = _scrollPrefab.InstantiateInactive(); + + if (!string.IsNullOrEmpty(info.rename)) + { + customScroll.name = info.rename; + } + + var nomaiWallText = MakeWallText(planetGO, sector, info, xmlPath); + nomaiWallText.transform.parent = customScroll.transform; + nomaiWallText.transform.localPosition = Vector3.zero; + nomaiWallText.transform.localRotation = Quaternion.identity; + + nomaiWallText._showTextOnStart = false; + + // Don't want to be able to translate until its in a socket + nomaiWallText.GetComponent().enabled = false; + + nomaiWallText.gameObject.SetActive(true); + + var scrollItem = customScroll.GetComponent(); + + // Idk why this thing is always around + GameObject.Destroy(customScroll.transform.Find("Arc_BH_City_Forum_2").gameObject); + + // This variable is the bane of my existence i dont get it + scrollItem._nomaiWallText = nomaiWallText; + + // Because the scroll was already awake it does weird shit in Awake and makes some of the entries in this array be null + scrollItem._colliders = new OWCollider[] { scrollItem.GetComponent() }; + + // Else when you put them down you can't pick them back up + customScroll.GetComponent()._physicsRemoved = false; + + // Place scroll + customScroll.transform.parent = sector?.transform ?? planetGO.transform; + + if (!string.IsNullOrEmpty(info.parentPath)) + { + customScroll.transform.parent = planetGO.transform.Find(info.parentPath); + } + + customScroll.transform.position = planetGO.transform.TransformPoint(info.position ?? Vector3.zero); + + var up = planetGO.transform.InverseTransformPoint(customScroll.transform.position).normalized; + customScroll.transform.rotation = Quaternion.FromToRotation(customScroll.transform.up, up) * customScroll.transform.rotation; + + customScroll.SetActive(true); + + // Enable the collider and renderer + Delay.RunWhen( + () => Main.IsSystemReady, + () => + { + Logger.LogVerbose("Fixing scroll!"); + scrollItem._nomaiWallText = nomaiWallText; + scrollItem.SetSector(sector); + customScroll.transform.Find("Props_NOM_Scroll/Props_NOM_Scroll_Geo").GetComponent().enabled = true; + customScroll.transform.Find("Props_NOM_Scroll/Props_NOM_Scroll_Collider").gameObject.SetActive(true); + nomaiWallText.gameObject.GetComponent().enabled = false; + customScroll.GetComponent().enabled = true; + } + ); + conversationInfoToCorrespondingSpawnedGameObject[info] = customScroll; + + return customScroll; + } + case PropModule.NomaiTextInfo.NomaiTextType.Computer: + { + var computerObject = _computerPrefab.InstantiateInactive(); + + if (!string.IsNullOrEmpty(info.rename)) + { + computerObject.name = info.rename; + } + + computerObject.transform.parent = sector?.transform ?? planetGO.transform; + + if (!string.IsNullOrEmpty(info.parentPath)) + { + computerObject.transform.parent = planetGO.transform.Find(info.parentPath); + } + + computerObject.transform.position = planetGO.transform.TransformPoint(info?.position ?? Vector3.zero); + + var up = computerObject.transform.position - planetGO.transform.position; + if (info.normal != null) up = planetGO.transform.TransformDirection(info.normal); + computerObject.transform.rotation = Quaternion.FromToRotation(Vector3.up, up) * computerObject.transform.rotation; + + var computer = computerObject.GetComponent(); + computer.SetSector(sector); + + computer._dictNomaiTextData = MakeNomaiTextDict(xmlPath); + computer._nomaiTextAsset = new TextAsset(xmlPath); + computer._nomaiTextAsset.name = Path.GetFileNameWithoutExtension(info.xmlFile); + AddTranslation(xmlPath); + + // Make sure the computer model is loaded + StreamingHandler.SetUpStreaming(computerObject, sector); + + computerObject.SetActive(true); + conversationInfoToCorrespondingSpawnedGameObject[info] = computerObject; + + return computerObject; + } + case PropModule.NomaiTextInfo.NomaiTextType.PreCrashComputer: + { + var computerObject = DetailBuilder.MakeDetail(planetGO, sector, _preCrashComputerPrefab, info.position, Vector3.zero, 1, false); + computerObject.SetActive(false); + + if (!string.IsNullOrEmpty(info.rename)) + { + computerObject.name = info.rename; + } + + if (!string.IsNullOrEmpty(info.parentPath)) + { + computerObject.transform.SetParent(planetGO.transform.Find(info.parentPath), true); + } + + var up = computerObject.transform.position - planetGO.transform.position; + if (info.normal != null) up = planetGO.transform.TransformDirection(info.normal); + computerObject.transform.rotation = Quaternion.FromToRotation(Vector3.up, up) * computerObject.transform.rotation; + + // Move it slightly up more + computerObject.transform.position += up.normalized * 0.1f; + + var computer = computerObject.GetComponent(); + computer.SetSector(sector); + + computer._dictNomaiTextData = MakeNomaiTextDict(xmlPath); + computer._nomaiTextAsset = new TextAsset(xmlPath); + computer._nomaiTextAsset.name = Path.GetFileNameWithoutExtension(info.xmlFile); + AddTranslation(xmlPath); + + computerObject.SetActive(true); + + // All rings are rendered by detail builder so dont do that (have to wait for entries to be set) + Delay.FireOnNextUpdate(() => + { + for (var i = computer.GetNumTextBlocks(); i < 5; i++) + { + var ring = computer._computerRings[i]; + ring.gameObject.SetActive(false); + } + }); + + conversationInfoToCorrespondingSpawnedGameObject[info] = computerObject; + + return computerObject; + } + case PropModule.NomaiTextInfo.NomaiTextType.Cairn: + { + var cairnObject = _cairnPrefab.InstantiateInactive(); + + if (!string.IsNullOrEmpty(info.rename)) + { + cairnObject.name = info.rename; + } + + cairnObject.transform.parent = sector?.transform ?? planetGO.transform; + + if (!string.IsNullOrEmpty(info.parentPath)) + { + cairnObject.transform.parent = planetGO.transform.Find(info.parentPath); + } + + cairnObject.transform.position = planetGO.transform.TransformPoint(info?.position ?? Vector3.zero); + + if (info.rotation != null) + { + cairnObject.transform.rotation = planetGO.transform.TransformRotation(Quaternion.Euler(info.rotation)); + } + else + { + // By default align it to normal + var up = (cairnObject.transform.position - planetGO.transform.position).normalized; + cairnObject.transform.rotation = Quaternion.FromToRotation(Vector3.up, up) * cairnObject.transform.rotation; + } + + // Idk do we have to set it active before finding things? + cairnObject.SetActive(true); + + // Make it do the thing when it finishes being knocked over + foreach (var rock in cairnObject.GetComponent()._rocks) + { + rock._returning = false; + rock._owCollider.SetActivation(true); + rock.enabled = false; + } + + // So we can actually knock it over + cairnObject.GetComponent().enabled = true; + + var nomaiWallText = cairnObject.transform.Find("Props_TH_ClutterSmall/Arc_Short").GetComponent(); + nomaiWallText.SetSector(sector); + + nomaiWallText._dictNomaiTextData = MakeNomaiTextDict(xmlPath); + nomaiWallText._nomaiTextAsset = new TextAsset(xmlPath); + nomaiWallText._nomaiTextAsset.name = Path.GetFileNameWithoutExtension(info.xmlFile); + AddTranslation(xmlPath); + + // Make sure the computer model is loaded + StreamingHandler.SetUpStreaming(cairnObject, sector); + conversationInfoToCorrespondingSpawnedGameObject[info] = cairnObject; + + return cairnObject; + } + case PropModule.NomaiTextInfo.NomaiTextType.PreCrashRecorder: + case PropModule.NomaiTextInfo.NomaiTextType.Recorder: + { + var recorderObject = (info.type == PropModule.NomaiTextInfo.NomaiTextType.PreCrashRecorder ? _preCrashRecorderPrefab : _recorderPrefab).InstantiateInactive(); + + if (!string.IsNullOrEmpty(info.rename)) + { + recorderObject.name = info.rename; + } + + recorderObject.transform.parent = sector?.transform ?? planetGO.transform; + + if (!string.IsNullOrEmpty(info.parentPath)) + { + recorderObject.transform.parent = planetGO.transform.Find(info.parentPath); + } + + recorderObject.transform.position = planetGO.transform.TransformPoint(info?.position ?? Vector3.zero); + + if (info.rotation != null) + { + recorderObject.transform.rotation = planetGO.transform.TransformRotation(Quaternion.Euler(info.rotation)); + } + else + { + var up = recorderObject.transform.position - planetGO.transform.position; + recorderObject.transform.rotation = Quaternion.FromToRotation(Vector3.up, up) * recorderObject.transform.rotation; + } + + var nomaiText = recorderObject.GetComponentInChildren(); + nomaiText.SetSector(sector); + + nomaiText._dictNomaiTextData = MakeNomaiTextDict(xmlPath); + nomaiText._nomaiTextAsset = new TextAsset(xmlPath); + nomaiText._nomaiTextAsset.name = Path.GetFileNameWithoutExtension(info.xmlFile); + AddTranslation(xmlPath); + + // Make sure the recorder model is loaded + StreamingHandler.SetUpStreaming(recorderObject, sector); + + recorderObject.SetActive(true); + + recorderObject.transform.Find("InteractSphere").gameObject.GetComponent().enabled = true; + conversationInfoToCorrespondingSpawnedGameObject[info] = recorderObject; + return recorderObject; + } + default: + Logger.LogError($"Unsupported NomaiText type {info.type}"); + return null; + } + } + + private static NomaiWallText MakeWallText(GameObject go, Sector sector, PropModule.NomaiTextInfo info, string xmlPath) + { + GameObject nomaiWallTextObj = new GameObject("NomaiWallText"); + nomaiWallTextObj.SetActive(false); + + var box = nomaiWallTextObj.AddComponent(); + box.center = new Vector3(-0.0643f, 1.1254f, 0f); + box.size = new Vector3(6.1424f, 5.2508f, 0.5f); + + box.isTrigger = true; + + nomaiWallTextObj.AddComponent(); + var nomaiWallText = nomaiWallTextObj.AddComponent(); + + var text = new TextAsset(xmlPath); + + // Text assets need a name to be used with VoiceMod + text.name = Path.GetFileNameWithoutExtension(info.xmlFile); + + BuildArcs(xmlPath, nomaiWallText, nomaiWallTextObj, info); + AddTranslation(xmlPath); + nomaiWallText._nomaiTextAsset = text; + + nomaiWallText.SetTextAsset(text); + + return nomaiWallText; + } + + internal static void BuildArcs(string xml, NomaiWallText nomaiWallText, GameObject conversationZone, PropModule.NomaiTextInfo info) + { + var dict = MakeNomaiTextDict(xml); + + nomaiWallText._dictNomaiTextData = dict; + + RefreshArcs(nomaiWallText, conversationZone, info); + } + + internal static void RefreshArcs(NomaiWallText nomaiWallText, GameObject conversationZone, PropModule.NomaiTextInfo info) + { + var dict = nomaiWallText._dictNomaiTextData; + Random.InitState(info.seed); + + var arcsByID = new Dictionary(); + + if (info.arcInfo != null && info.arcInfo.Count() != dict.Values.Count()) + { + Logger.LogError($"Can't make NomaiWallText, arcInfo length [{info.arcInfo.Count()}] doesn't equal text entries [{dict.Values.Count()}]"); + return; + } + + var i = 0; + foreach (var textData in dict.Values) + { + var arcInfo = info.arcInfo?.Length > i ? info.arcInfo[i] : null; + var textEntryID = textData.ID; + var parentID = textData.ParentID; + + var parent = parentID == -1 ? null : arcsByID[parentID]; + + GameObject arc = MakeArc(arcInfo, conversationZone, parent, textEntryID); + arc.name = $"Arc {i} - Child of {parentID}"; + + arcsByID.Add(textEntryID, arc); + + i++; + } + } + + internal static GameObject MakeArc(PropModule.NomaiTextArcInfo arcInfo, GameObject conversationZone, GameObject parent, int textEntryID) + { + GameObject arc; + var type = arcInfo != null ? arcInfo.type : PropModule.NomaiTextArcInfo.NomaiTextArcType.Adult; + var variation = arcInfo != null ? arcInfo.variation : -1; + switch (type) + { + case PropModule.NomaiTextArcInfo.NomaiTextArcType.Child: + variation = variation < 0 + ? Random.Range(0, _childArcPrefabs.Count()) + : (variation % _childArcPrefabs.Count()); + arc = _childArcPrefabs[variation].InstantiateInactive(); + break; + case PropModule.NomaiTextArcInfo.NomaiTextArcType.Stranger when _ghostArcPrefabs.Any(): + variation = variation < 0 + ? Random.Range(0, _ghostArcPrefabs.Count()) + : (variation % _ghostArcPrefabs.Count()); + arc = _ghostArcPrefabs[variation].InstantiateInactive(); + break; + case PropModule.NomaiTextArcInfo.NomaiTextArcType.Adult: + default: + variation = variation < 0 + ? Random.Range(0, _arcPrefabs.Count()) + : (variation % _arcPrefabs.Count()); + arc = _arcPrefabs[variation].InstantiateInactive(); + break; + } + + arc.transform.parent = conversationZone.transform; + arc.GetComponent()._prebuilt = false; + + if (arcInfo != null) + { + arcInfo.variation = variation; + if (arcInfo.position == null) arc.transform.localPosition = Vector3.zero; + else arc.transform.localPosition = new Vector3(arcInfo.position.x, arcInfo.position.y, 0); + + arc.transform.localRotation = Quaternion.Euler(0, 0, arcInfo.zRotation); + + if (arcInfo.mirror) arc.transform.localScale = new Vector3(-1, 1, 1); + } + // Try auto I guess + else + { + if (parent == null) + { + arc.transform.localPosition = Vector3.zero; + } + else + { + var points = parent.GetComponent().GetPoints(); + var point = points[points.Count() / 2]; + + arc.transform.localPosition = point; + arc.transform.localRotation = Quaternion.Euler(0, 0, Random.Range(0, 360)); + } + } + + arc.GetComponent().SetEntryID(textEntryID); + arc.GetComponent().enabled = false; + + arc.SetActive(true); + + if (arcInfo != null) arcInfoToCorrespondingSpawnedGameObject[arcInfo] = arc; + + return arc; + } + + private static Dictionary MakeNomaiTextDict(string xmlPath) + { + var dict = new Dictionary(); + + XmlDocument xmlDocument = new XmlDocument(); + xmlDocument.LoadXml(xmlPath); + XmlNode rootNode = xmlDocument.SelectSingleNode("NomaiObject"); + + foreach (object obj in rootNode.SelectNodes("TextBlock")) + { + XmlNode xmlNode = (XmlNode)obj; + + int textEntryID = -1; + int parentID = -1; + + XmlNode textNode = xmlNode.SelectSingleNode("Text"); + XmlNode entryIDNode = xmlNode.SelectSingleNode("ID"); + XmlNode parentIDNode = xmlNode.SelectSingleNode("ParentID"); + + if (entryIDNode != null && !int.TryParse(entryIDNode.InnerText, out textEntryID)) + { + Logger.LogError($"Couldn't parse int ID in [{entryIDNode?.InnerText}] for [{xmlPath}]"); + textEntryID = -1; + } + + if (parentIDNode != null && !int.TryParse(parentIDNode.InnerText, out parentID)) + { + Logger.LogError($"Couldn't parse int ParentID in [{parentIDNode?.InnerText}] for [{xmlPath}]"); + parentID = -1; + } + + NomaiText.NomaiTextData value = new NomaiText.NomaiTextData(textEntryID, parentID, textNode, false, NomaiText.Location.UNSPECIFIED); + dict.Add(textEntryID, value); + } + return dict; + } + + private static void AddTranslation(string xmlPath) + { + XmlDocument xmlDocument = new XmlDocument(); + xmlDocument.LoadXml(xmlPath); + + XmlNode xmlNode = xmlDocument.SelectSingleNode("NomaiObject"); + XmlNodeList xmlNodeList = xmlNode.SelectNodes("TextBlock"); + + foreach (object obj in xmlNodeList) + { + XmlNode xmlNode2 = (XmlNode)obj; + var text = xmlNode2.SelectSingleNode("Text").InnerText; + TranslationHandler.AddDialogue(text); + } + } + } +} diff --git a/NewHorizons/Builder/Props/SignalBuilder.cs b/NewHorizons/Builder/Props/SignalBuilder.cs index 9cf4c0b5..bc7b8195 100644 --- a/NewHorizons/Builder/Props/SignalBuilder.cs +++ b/NewHorizons/Builder/Props/SignalBuilder.cs @@ -1,242 +1,242 @@ -using NewHorizons.AchievementsPlus; -using NewHorizons.Components; -using NewHorizons.External.Modules; -using NewHorizons.Utility; -using OWML.Common; -using System; -using System.Collections.Generic; -using UnityEngine; -using Logger = NewHorizons.Utility.Logger; -namespace NewHorizons.Builder.Props -{ - public static class SignalBuilder - { - private static AnimationCurve _customCurve = null; - - private static Dictionary _customSignalNames; - private static Stack _availableSignalNames; - private static int _nextCustomSignalName; - - private static Dictionary _customFrequencyNames; - private static int _nextCustomFrequencyName; - - public static int NumberOfFrequencies; - - public static List QMSignals { get; private set; } - public static List CloakedSignals { get; private set; } - - public static void Init() - { - Logger.LogVerbose($"Initializing SignalBuilder"); - _customSignalNames = new Dictionary(); - _availableSignalNames = new Stack(new SignalName[] - { - (SignalName)17, - (SignalName)18, - (SignalName)19, - (SignalName)26, - (SignalName)27, - (SignalName)28, - (SignalName)29, - (SignalName)33, - (SignalName)34, - (SignalName)35, - (SignalName)36, - (SignalName)37, - (SignalName)38, - (SignalName)39, - (SignalName)50, - (SignalName)51, - (SignalName)52, - (SignalName)53, - (SignalName)54, - (SignalName)55, - (SignalName)56, - (SignalName)57, - (SignalName)58, - (SignalName)59, - SignalName.WhiteHole_WH, - SignalName.WhiteHole_SS_Receiver, - SignalName.WhiteHole_CT_Receiver, - SignalName.WhiteHole_CT_Experiment, - SignalName.WhiteHole_TT_Receiver, - SignalName.WhiteHole_TT_TimeLoopCore, - SignalName.WhiteHole_TH_Receiver, - SignalName.WhiteHole_BH_NorthPoleReceiver, - SignalName.WhiteHole_BH_ForgeReceiver, - SignalName.WhiteHole_GD_Receiver, - }); - _customFrequencyNames = new Dictionary() { - { SignalFrequency.Statue, "FREQ_STATUE" }, - { SignalFrequency.Default, "FREQ_UNKNOWN" }, - { SignalFrequency.WarpCore, "FREQ_WARP_CORE" } - }; - _nextCustomSignalName = 200; - _nextCustomFrequencyName = 256; - NumberOfFrequencies = 8; - - QMSignals = new List() { SignalName.Quantum_QM }; - CloakedSignals = new List(); - } - - public static SignalFrequency AddFrequency(string str) - { - Logger.Log($"Registering new frequency name [{str}]"); - - if (NumberOfFrequencies == 31) - { - Logger.LogWarning($"Can't store any more frequencies, skipping [{str}]"); - return SignalFrequency.Default; - } - - var freq = CollectionUtilities.KeyByValue(_customFrequencyNames, str); - if (freq != default) - { - return freq; - } - - freq = (SignalFrequency)_nextCustomFrequencyName; - _nextCustomFrequencyName *= 2; - _customFrequencyNames.Add(freq, str); - - NumberOfFrequencies++; - - // This stuff happens after the signalscope is Awake so we have to change the number of frequencies now - GameObject.FindObjectOfType()._strongestSignals = new AudioSignal[NumberOfFrequencies + 1]; - - return freq; - } - - public static string GetCustomFrequencyName(SignalFrequency frequencyName) - { - _customFrequencyNames.TryGetValue(frequencyName, out string name); - return name; - } - - public static SignalName AddSignalName(string str) - { - Logger.Log($"Registering new signal name [{str}]"); - SignalName newName; - - if (_availableSignalNames.Count == 0) newName = (SignalName)_nextCustomSignalName++; - else newName = _availableSignalNames.Pop(); - - _customSignalNames.Add(newName, str); - return newName; - } - - public static string GetCustomSignalName(SignalName signalName) - { - _customSignalNames.TryGetValue(signalName, out string name); - return name; - } - - public static GameObject Make(GameObject planetGO, Sector sector, SignalModule.SignalInfo info, IModBehaviour mod) - { - var signalGO = new GameObject($"Signal_{info.name}"); - signalGO.SetActive(false); - signalGO.transform.parent = sector?.transform ?? planetGO.transform; - - if (!string.IsNullOrEmpty(info.parentPath)) - { - signalGO.transform.parent = planetGO.transform.Find(info.parentPath); - } - - signalGO.transform.position = planetGO.transform.TransformPoint(info.position != null ? (Vector3)info.position : Vector3.zero); - signalGO.layer = LayerMask.NameToLayer("AdvancedEffectVolume"); - - var source = signalGO.AddComponent(); - var owAudioSource = signalGO.AddComponent(); - owAudioSource._audioSource = source; - - var audioSignal = signalGO.AddComponent(); - audioSignal._owAudioSource = owAudioSource; - - var frequency = StringToFrequency(info.frequency); - var name = StringToSignalName(info.name); - - audioSignal.SetSector(sector); - - if (name == SignalName.Default) audioSignal._preventIdentification = true; - - audioSignal._frequency = frequency; - audioSignal._name = name; - audioSignal._sourceRadius = info.sourceRadius; - audioSignal._revealFactID = info.reveals; - audioSignal._onlyAudibleToScope = info.onlyAudibleToScope; - audioSignal._identificationDistance = info.identificationRadius; - audioSignal._canBePickedUpByScope = true; - audioSignal._outerFogWarpVolume = planetGO.GetComponentInChildren(); // shouldn't break non-bramble signals - - source.loop = true; - source.minDistance = 0; - source.maxDistance = 30; - source.velocityUpdateMode = AudioVelocityUpdateMode.Fixed; - source.rolloffMode = AudioRolloffMode.Custom; - - if (_customCurve == null) - { - _customCurve = new AnimationCurve( - new Keyframe(0.0333f, 1f, -30.012f, -30.012f, 0.3333f, 0.3333f), - new Keyframe(0.0667f, 0.5f, -7.503f, -7.503f, 0.3333f, 0.3333f), - new Keyframe(0.1333f, 0.25f, -1.8758f, -1.8758f, 0.3333f, 0.3333f), - new Keyframe(0.2667f, 0.125f, -0.4689f, -0.4689f, 0.3333f, 0.3333f), - new Keyframe(0.5333f, 0.0625f, -0.1172f, -0.1172f, 0.3333f, 0.3333f), - new Keyframe(1f, 0f, -0.0333f, -0.0333f, 0.3333f, 0.3333f)); - } - - source.SetCustomCurve(AudioSourceCurveType.CustomRolloff, _customCurve); - // If it can be heard regularly then we play it immediately - source.playOnAwake = !info.onlyAudibleToScope; - source.spatialBlend = 1f; - source.volume = 0.5f; - source.dopplerLevel = 0; - - owAudioSource.SetTrack(OWAudioMixer.TrackName.Signal); - AudioUtilities.SetAudioClip(owAudioSource, info.audio, mod); - - // Frequency detection trigger volume - - var sphereShape = signalGO.AddComponent(); - var owTriggerVolume = signalGO.AddComponent(); - var audioSignalDetectionTrigger = signalGO.AddComponent(); - - sphereShape.radius = info.detectionRadius == 0 ? info.sourceRadius + 30 : info.detectionRadius; - audioSignalDetectionTrigger._signal = audioSignal; - audioSignalDetectionTrigger._trigger = owTriggerVolume; - - signalGO.SetActive(true); - - // Track certain special signal things - if (planetGO.GetComponent()?.GetAstroObjectName() == AstroObject.Name.QuantumMoon) QMSignals.Add(name); - if (info.insideCloak) CloakedSignals.Add(name); - - return signalGO; - } - - private static SignalFrequency StringToFrequency(string str) - { - foreach (SignalFrequency freq in Enum.GetValues(typeof(SignalFrequency))) - { - if (str.Equals(freq.ToString())) return freq; - } - var customName = CollectionUtilities.KeyByValue(_customFrequencyNames, str); - - if (customName == default) customName = AddFrequency(str); - - return customName; - } - - public static SignalName StringToSignalName(string str) - { - foreach (SignalName name in Enum.GetValues(typeof(SignalName))) - { - if (str.Equals(name.ToString())) return name; - } - var customName = CollectionUtilities.KeyByValue(_customSignalNames, str); - if (customName == default) customName = AddSignalName(str); - - return customName; - } - } -} +using NewHorizons.AchievementsPlus; +using NewHorizons.Components; +using NewHorizons.External.Modules; +using NewHorizons.Utility; +using OWML.Common; +using System; +using System.Collections.Generic; +using UnityEngine; +using Logger = NewHorizons.Utility.Logger; +namespace NewHorizons.Builder.Props +{ + public static class SignalBuilder + { + private static AnimationCurve _customCurve = null; + + private static Dictionary _customSignalNames; + private static Stack _availableSignalNames; + private static int _nextCustomSignalName; + + private static Dictionary _customFrequencyNames; + private static int _nextCustomFrequencyName; + + public static int NumberOfFrequencies; + + public static List QMSignals { get; private set; } + public static List CloakedSignals { get; private set; } + + public static void Init() + { + Logger.LogVerbose($"Initializing SignalBuilder"); + _customSignalNames = new Dictionary(); + _availableSignalNames = new Stack(new SignalName[] + { + (SignalName)17, + (SignalName)18, + (SignalName)19, + (SignalName)26, + (SignalName)27, + (SignalName)28, + (SignalName)29, + (SignalName)33, + (SignalName)34, + (SignalName)35, + (SignalName)36, + (SignalName)37, + (SignalName)38, + (SignalName)39, + (SignalName)50, + (SignalName)51, + (SignalName)52, + (SignalName)53, + (SignalName)54, + (SignalName)55, + (SignalName)56, + (SignalName)57, + (SignalName)58, + (SignalName)59, + SignalName.WhiteHole_WH, + SignalName.WhiteHole_SS_Receiver, + SignalName.WhiteHole_CT_Receiver, + SignalName.WhiteHole_CT_Experiment, + SignalName.WhiteHole_TT_Receiver, + SignalName.WhiteHole_TT_TimeLoopCore, + SignalName.WhiteHole_TH_Receiver, + SignalName.WhiteHole_BH_NorthPoleReceiver, + SignalName.WhiteHole_BH_ForgeReceiver, + SignalName.WhiteHole_GD_Receiver, + }); + _customFrequencyNames = new Dictionary() { + { SignalFrequency.Statue, "FREQ_STATUE" }, + { SignalFrequency.Default, "FREQ_UNKNOWN" }, + { SignalFrequency.WarpCore, "FREQ_WARP_CORE" } + }; + _nextCustomSignalName = 200; + _nextCustomFrequencyName = 256; + NumberOfFrequencies = 8; + + QMSignals = new List() { SignalName.Quantum_QM }; + CloakedSignals = new List(); + } + + public static SignalFrequency AddFrequency(string str) + { + Logger.Log($"Registering new frequency name [{str}]"); + + if (NumberOfFrequencies == 31) + { + Logger.LogWarning($"Can't store any more frequencies, skipping [{str}]"); + return SignalFrequency.Default; + } + + var freq = CollectionUtilities.KeyByValue(_customFrequencyNames, str); + if (freq != default) + { + return freq; + } + + freq = (SignalFrequency)_nextCustomFrequencyName; + _nextCustomFrequencyName *= 2; + _customFrequencyNames.Add(freq, str); + + NumberOfFrequencies++; + + // This stuff happens after the signalscope is Awake so we have to change the number of frequencies now + GameObject.FindObjectOfType()._strongestSignals = new AudioSignal[NumberOfFrequencies + 1]; + + return freq; + } + + public static string GetCustomFrequencyName(SignalFrequency frequencyName) + { + _customFrequencyNames.TryGetValue(frequencyName, out string name); + return name; + } + + public static SignalName AddSignalName(string str) + { + Logger.Log($"Registering new signal name [{str}]"); + SignalName newName; + + if (_availableSignalNames.Count == 0) newName = (SignalName)_nextCustomSignalName++; + else newName = _availableSignalNames.Pop(); + + _customSignalNames.Add(newName, str); + return newName; + } + + public static string GetCustomSignalName(SignalName signalName) + { + _customSignalNames.TryGetValue(signalName, out string name); + return name; + } + + public static GameObject Make(GameObject planetGO, Sector sector, SignalModule.SignalInfo info, IModBehaviour mod) + { + var signalGO = new GameObject($"Signal_{info.name}"); + signalGO.SetActive(false); + signalGO.transform.parent = sector?.transform ?? planetGO.transform; + + if (!string.IsNullOrEmpty(info.parentPath)) + { + signalGO.transform.parent = planetGO.transform.Find(info.parentPath); + } + + signalGO.transform.position = planetGO.transform.TransformPoint(info.position != null ? (Vector3)info.position : Vector3.zero); + signalGO.layer = LayerMask.NameToLayer("AdvancedEffectVolume"); + + var source = signalGO.AddComponent(); + var owAudioSource = signalGO.AddComponent(); + owAudioSource._audioSource = source; + + var audioSignal = signalGO.AddComponent(); + audioSignal._owAudioSource = owAudioSource; + + var frequency = StringToFrequency(info.frequency); + var name = StringToSignalName(info.name); + + audioSignal.SetSector(sector); + + if (name == SignalName.Default) audioSignal._preventIdentification = true; + + audioSignal._frequency = frequency; + audioSignal._name = name; + audioSignal._sourceRadius = info.sourceRadius; + audioSignal._revealFactID = info.reveals; + audioSignal._onlyAudibleToScope = info.onlyAudibleToScope; + audioSignal._identificationDistance = info.identificationRadius; + audioSignal._canBePickedUpByScope = true; + audioSignal._outerFogWarpVolume = planetGO.GetComponentInChildren(); // shouldn't break non-bramble signals + + source.loop = true; + source.minDistance = 0; + source.maxDistance = 30; + source.velocityUpdateMode = AudioVelocityUpdateMode.Fixed; + source.rolloffMode = AudioRolloffMode.Custom; + + if (_customCurve == null) + { + _customCurve = new AnimationCurve( + new Keyframe(0.0333f, 1f, -30.012f, -30.012f, 0.3333f, 0.3333f), + new Keyframe(0.0667f, 0.5f, -7.503f, -7.503f, 0.3333f, 0.3333f), + new Keyframe(0.1333f, 0.25f, -1.8758f, -1.8758f, 0.3333f, 0.3333f), + new Keyframe(0.2667f, 0.125f, -0.4689f, -0.4689f, 0.3333f, 0.3333f), + new Keyframe(0.5333f, 0.0625f, -0.1172f, -0.1172f, 0.3333f, 0.3333f), + new Keyframe(1f, 0f, -0.0333f, -0.0333f, 0.3333f, 0.3333f)); + } + + source.SetCustomCurve(AudioSourceCurveType.CustomRolloff, _customCurve); + // If it can be heard regularly then we play it immediately + source.playOnAwake = !info.onlyAudibleToScope; + source.spatialBlend = 1f; + source.volume = 0.5f; + source.dopplerLevel = 0; + + owAudioSource.SetTrack(OWAudioMixer.TrackName.Signal); + AudioUtilities.SetAudioClip(owAudioSource, info.audio, mod); + + // Frequency detection trigger volume + + var sphereShape = signalGO.AddComponent(); + var owTriggerVolume = signalGO.AddComponent(); + var audioSignalDetectionTrigger = signalGO.AddComponent(); + + sphereShape.radius = info.detectionRadius == 0 ? info.sourceRadius + 30 : info.detectionRadius; + audioSignalDetectionTrigger._signal = audioSignal; + audioSignalDetectionTrigger._trigger = owTriggerVolume; + + signalGO.SetActive(true); + + // Track certain special signal things + if (planetGO.GetComponent()?.GetAstroObjectName() == AstroObject.Name.QuantumMoon) QMSignals.Add(name); + if (info.insideCloak) CloakedSignals.Add(name); + + return signalGO; + } + + private static SignalFrequency StringToFrequency(string str) + { + foreach (SignalFrequency freq in Enum.GetValues(typeof(SignalFrequency))) + { + if (str.Equals(freq.ToString())) return freq; + } + var customName = CollectionUtilities.KeyByValue(_customFrequencyNames, str); + + if (customName == default) customName = AddFrequency(str); + + return customName; + } + + public static SignalName StringToSignalName(string str) + { + foreach (SignalName name in Enum.GetValues(typeof(SignalName))) + { + if (str.Equals(name.ToString())) return name; + } + var customName = CollectionUtilities.KeyByValue(_customSignalNames, str); + if (customName == default) customName = AddSignalName(str); + + return customName; + } + } +} diff --git a/NewHorizons/External/Modules/PropModule.cs b/NewHorizons/External/Modules/PropModule.cs index c909ff68..5e424bbd 100644 --- a/NewHorizons/External/Modules/PropModule.cs +++ b/NewHorizons/External/Modules/PropModule.cs @@ -1,805 +1,805 @@ - -using NewHorizons.Utility; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; -using System.Runtime.Serialization; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using System; -using NewHorizons.External.Modules.VariableSize; - -namespace NewHorizons.External.Modules -{ - [JsonObject] - public class PropModule - { - /// - /// Place props in predefined positions on the planet - /// - public DetailInfo[] details; - - /// - /// Add dialogue triggers to this planet - /// - public DialogueInfo[] dialogue; - - /// - /// Add ship log entry locations on this planet - /// - public EntryLocationInfo[] entryLocation; - - /// - /// Add Geysers to this planet - /// - public GeyserInfo[] geysers; - - /// - /// Add translatable text to this planet - /// - public NomaiTextInfo[] nomaiText; - - /// - /// Details which will be shown from 50km away. Meant to be lower resolution. - /// - public DetailInfo[] proxyDetails; - - /// - /// Add rafts to this planet - /// - public RaftInfo[] rafts; - - /// - /// Add triggers that reveal parts of the ship log on this planet - /// - public RevealInfo[] reveal; - - /// - /// Scatter props around this planet's surface - /// - public ScatterInfo[] scatter; - - /// - /// Add slideshows (from the DLC) to the planet - /// - public ProjectionInfo[] slideShows; - - /// - /// A list of quantum groups that props can be added to. An example of a group would be a list of possible locations for a QuantumSocketedObject. - /// - public QuantumGroupInfo[] quantumGroups; - - /// - /// Add tornadoes to this planet - /// - public TornadoInfo[] tornados; - - /// - /// Add volcanoes to this planet - /// - public VolcanoInfo[] volcanoes; - - /// - /// Add black/white-holes to this planet - /// - public SingularityModule[] singularities; - - /// - /// Add audio volumes to this planet - /// - public AudioVolumeInfo[] audioVolumes; - - /// - /// Add signalscope signals to this planet - /// - public SignalModule.SignalInfo[] signals; - - [JsonObject] - public class ScatterInfo - { - /// - /// Relative filepath to an asset-bundle - /// - public string assetBundle; - - /// - /// Number of props to scatter - /// - public int count; - - /// - /// Offset this prop once it is placed - /// - public MVector3 offset; - - /// - /// Either the path in the scene hierarchy of the item to copy or the path to the object in the supplied asset bundle - /// - public string path; - - /// - /// Rotate this prop once it is placed - /// - public MVector3 rotation; - - /// - /// Scale this prop once it is placed - /// - public float scale = 1f; - - /// - /// The number used as entropy for scattering the props - /// - public int seed; - - /// - /// The lowest height that these object will be placed at (only relevant if there's a heightmap) - /// - public float? minHeight; - - /// - /// The highest height that these objects will be placed at (only relevant if there's a heightmap) - /// - public float? maxHeight; - } - - [JsonObject] - public class DetailInfo - { - /// - /// An optional rename of the detail - /// - public string rename; - - /// - /// Do we override rotation and try to automatically align this object to stand upright on the body's surface? - /// - public bool alignToNormal; - - /// - /// Relative filepath to an asset-bundle to load the prefab defined in `path` from - /// - public string assetBundle; - - /// - /// Either the path in the scene hierarchy of the item to copy or the path to the object in the supplied asset bundle - /// - public string path; - - /// - /// Position of this prop relative to the body's center - /// - public MVector3 position; - - /// - /// A list of children to remove from this detail - /// - public string[] removeChildren; - - /// - /// Do we reset all the components on this object? Useful for certain props that have dialogue components attached to - /// them. - /// - public bool removeComponents; - - /// - /// Rotate this prop - /// - public MVector3 rotation; - - /// - /// Scale the prop - /// - [DefaultValue(1f)] public float scale = 1f; - - /// - /// If this value is not null, this prop will be quantum. Assign this field to the id of the quantum group it should be a part of. The group it is assigned to determines what kind of quantum object it is - /// - public string quantumGroupID; - - /// - /// The path (not including the root planet object) of the parent of this game object. Optional (will default to the root sector). - /// - public string parentPath; - } - - [JsonObject] - public class RaftInfo - { - /// - /// Position of the raft - /// - public MVector3 position; - } - - [JsonObject] - public class GeyserInfo - { - /// - /// Position of the geyser - /// - public MVector3 position; - - /// - /// Disable the individual particle systems of the geyser - /// - public bool disableBubbles, disableShaft, disableSpout; - } - - [JsonObject] - public class TornadoInfo - { - [JsonConverter(typeof(StringEnumConverter))] - public enum TornadoType - { - [EnumMember(Value = @"downwards")] Downwards = 0, - - [EnumMember(Value = @"upwards")] Upwards = 1, - - [EnumMember(Value = @"hurricane")] Hurricane = 2 - } - - [Obsolete("Downwards is deprecated. Use Type instead.")] public bool downwards; - - /// - /// Alternative to setting the position. Will choose a random place at this elevation. - /// - public float elevation; - - /// - /// The height of this tornado. - /// - [DefaultValue(30f)] public float height = 30f; - - /// - /// Position of the tornado - /// - public MVector3 position; - - /// - /// The colour of the tornado. - /// - public MColor tint; - - /// - /// What type of cyclone should this be? Upwards and downwards are both tornados and will push in that direction. - /// - [DefaultValue("downwards")] public TornadoType type = TornadoType.Downwards; - - /// - /// Angular distance from the starting position that it will wander, in terms of the angle around the x-axis. - /// - [DefaultValue(45f)] public float wanderDegreesX = 45f; - - /// - /// Angular distance from the starting position that it will wander, in terms of the angle around the z-axis. - /// - [DefaultValue(45f)] public float wanderDegreesZ = 45f; - - /// - /// The rate at which the tornado will wander around the planet. Set to 0 for it to be stationary. Should be around - /// 0.1. - /// - public float wanderRate; - - /// - /// The maximum distance at which you'll hear the sounds of the cyclone. If not set it will scale relative to the size of the cyclone. - /// - public float audioDistance; - } - - [JsonObject] - public class VolcanoInfo - { - /// - /// The colour of the meteor's lava. - /// - public MColor lavaTint; - - /// - /// Maximum time between meteor launches. - /// - [DefaultValue(20f)] - public float maxInterval = 20f; - - /// - /// Maximum random speed at which meteors are launched. - /// - [DefaultValue(150f)] - public float maxLaunchSpeed = 150f; - - /// - /// Minimum time between meteor launches. - /// - [DefaultValue(5f)] - public float minInterval = 5f; - - /// - /// Minimum random speed at which meteors are launched. - /// - [DefaultValue(50f)] - public float minLaunchSpeed = 50f; - - /// - /// Position of this volcano. - /// - public MVector3 position; - - /// - /// Scale of the meteors. - /// - public float scale = 1; - - /// - /// The colour of the meteor's stone. - /// - public MColor stoneTint; - } - - [JsonObject] - public class DialogueInfo - { - /// - /// Prevents the dialogue from being created after a specific persistent condition is set. Useful for remote dialogue - /// triggers that you want to have happen only once. - /// - public string blockAfterPersistentCondition; - - /// - /// If a pathToAnimController is supplied, if you are within this distance the character will look at you. If it is set - /// to 0, they will only look at you when spoken to. - /// - public float lookAtRadius; - - /// - /// If this dialogue is meant for a character, this is the relative path from the planet to that character's - /// CharacterAnimController or SolanumAnimController. - /// - public string pathToAnimController; - - /// - /// When you enter into dialogue, you will look here. - /// - public MVector3 position; - - /// - /// Radius of the spherical collision volume where you get the "talk to" prompt when looking at. If you use a - /// remoteTriggerPosition, you can set this to 0 to make the dialogue only trigger remotely. - /// - public float radius = 1f; - - /// - /// Allows you to trigger dialogue from a distance when you walk into an area. - /// - public MVector3 remoteTriggerPosition; - - /// - /// Distance from radius the prompt appears - /// - [DefaultValue(2f)] public float range = 2f; - - /// - /// The radius of the remote trigger volume. - /// - public float remoteTriggerRadius; - - /// - /// Relative path to the xml file defining the dialogue. - /// - public string xmlFile; - } - - [JsonObject] - public class RevealInfo - { - [JsonConverter(typeof(StringEnumConverter))] - public enum RevealVolumeType - { - [EnumMember(Value = @"enter")] Enter = 0, - - [EnumMember(Value = @"observe")] Observe = 1, - - [EnumMember(Value = @"snapshot")] Snapshot = 2 - } - - /// - /// The max view angle (in degrees) the player can see the volume with to unlock the fact (`observe` only) - /// - public float maxAngle = 180f; // Observe Only - - /// - /// The max distance the user can be away from the volume to reveal the fact (`snapshot` and `observe` only) - /// - public float maxDistance = -1f; // Snapshot & Observe Only - - /// - /// The position to place this volume at - /// - public MVector3 position; - - /// - /// The radius of this reveal volume - /// - public float radius = 1f; - - /// - /// What needs to be done to the volume to unlock the facts - /// - [DefaultValue("enter")] public RevealVolumeType revealOn = RevealVolumeType.Enter; - - /// - /// A list of facts to reveal - /// - public string[] reveals; - - /// - /// An achievement to unlock. Optional. - /// - public string achievementID; - } - - [JsonObject] - public class EntryLocationInfo - { - /// - /// Whether this location is cloaked - /// - public bool cloaked; - - /// - /// ID of the entry this location relates to - /// - public string id; - - /// - /// The position of this entry location - /// - public MVector3 position; - } - - [JsonObject] - public class NomaiTextInfo - { - [JsonConverter(typeof(StringEnumConverter))] - public enum NomaiTextType - { - [EnumMember(Value = @"wall")] Wall = 0, - - [EnumMember(Value = @"scroll")] Scroll = 1, - - [EnumMember(Value = @"computer")] Computer = 2, - - [EnumMember(Value = @"cairn")] Cairn = 3, - - [EnumMember(Value = @"recorder")] Recorder = 4, - - [EnumMember(Value = @"preCrashRecorder")] PreCrashRecorder = 5, - - [EnumMember(Value = @"preCrashComputer")] PreCrashComputer = 6 - } - - /// - /// Additional information about each arc in the text - /// - public NomaiTextArcInfo[] arcInfo; - - /// - /// The normal vector for this object. Used for writing on walls and positioning computers. - /// - public MVector3 normal; - - /// - /// Position of the root of this text - /// - public MVector3 position; - - /// - /// The euler angle rotation of this object. Not required if setting the normal. Computers and cairns will orient - /// themselves to the surface of the planet automatically. - /// - public MVector3 rotation; - - /// - /// The random seed used to pick what the text arcs will look like. - /// - public int seed; // For randomizing arcs - - /// - /// The type of object this is. - /// - [DefaultValue("wall")] public NomaiTextType type = NomaiTextType.Wall; - - /// - /// The relative path to the xml file for this object. - /// - public string xmlFile; - - /// - /// The relative path from the planet to the parent of this object. Optional (will default to the root sector). - /// - public string parentPath; - - /// - /// An optional rename of this object - /// - public string rename; - - } - - [JsonObject] - public class NomaiTextArcInfo - { - [JsonConverter(typeof(StringEnumConverter))] - public enum NomaiTextArcType - { - [EnumMember(Value = @"adult")] Adult = 0, - - [EnumMember(Value = @"child")] Child = 1, - - [EnumMember(Value = @"stranger")] Stranger = 2 - } - - /// - /// Whether to flip the spiral from left-curling to right-curling or vice versa. - /// - public bool mirror; - - /// - /// The local position of this object on the wall. - /// - public MVector2 position; - - /// - /// The type of text to display. - /// - [DefaultValue("adult")] public NomaiTextArcType type = NomaiTextArcType.Adult; - - /// - /// Which variation of the chosen type to place. If not specified, a random variation will be selected based on the seed provided in the parent module. - /// - [DefaultValue(-1)] public int variation = -1; - - /// - /// The z euler angle for this arc. - /// - [Range(0f, 360f)] public float zRotation; - } - - [JsonObject] - public class ProjectionInfo - { - [JsonConverter(typeof(StringEnumConverter))] - public enum SlideShowType - { - [EnumMember(Value = @"slideReel")] SlideReel = 0, - - [EnumMember(Value = @"autoProjector")] AutoProjector = 1, - - [EnumMember(Value = @"visionTorchTarget")] VisionTorchTarget = 2, - - [EnumMember(Value = @"standingVisionTorch")] StandingVisionTorch = 3, - - } - - /// - /// The position of this slideshow. - /// - public MVector3 position; - - /// - /// The ship log entries revealed after finishing this slide reel. - /// - public string[] reveals; - - /// - /// The rotation of this slideshow. - /// - public MVector3 rotation; - - /// - /// The list of slides for this object. - /// - public SlideInfo[] slides; - - /// - /// The type of object this is. - /// - [DefaultValue("slideReel")] public SlideShowType type = SlideShowType.SlideReel; - - /// - /// The relative path from the planet to the parent of this slideshow. Optional (will default to the root sector). - /// - public string parentPath; - } - - [JsonObject] - public class SlideInfo - { - /// - /// Ambient light colour when viewing this slide. - /// - public MColor ambientLightColor; - - - // SlideAmbientLightModule - - /// - /// Ambient light intensity when viewing this slide. - /// - public float ambientLightIntensity; - - /// - /// Ambient light range when viewing this slide. - /// - public float ambientLightRange; - - // SlideBackdropAudioModule - - /// - /// The name of the AudioClip that will continuously play while watching these slides - /// - public string backdropAudio; - - /// - /// The time to fade into the backdrop audio - /// - public float backdropFadeTime; - - // SlideBeatAudioModule - - /// - /// The name of the AudioClip for a one-shot sound when opening the slide. - /// - public string beatAudio; - - /// - /// The time delay until the one-shot audio - /// - public float beatDelay; - - - // SlideBlackFrameModule - - /// - /// Before viewing this slide, there will be a black frame for this many seconds. - /// - public float blackFrameDuration; - - /// - /// The path to the image file for this slide. - /// - public string imagePath; - - - // SlidePlayTimeModule - - /// - /// Play-time duration for auto-projector slides. - /// - public float playTimeDuration; - - - // SlideShipLogEntryModule - - /// - /// Ship log entry revealed when viewing this slide - /// - public string reveal; - - /// - /// Spotlight intensity modifier when viewing this slide. - /// - public float spotIntensityMod; - } - - - - - [JsonConverter(typeof(StringEnumConverter))] - public enum QuantumGroupType - { - [EnumMember(Value = @"sockets")] Sockets = 0, - - [EnumMember(Value = @"states")] States = 1, - - FailedValidation = 10 - } - - [JsonObject] - public class QuantumGroupInfo - { - /// - /// What type of group this is: does it define a list of states a single quantum object could take or a list of sockets one or more quantum objects could share? - /// - public QuantumGroupType type; - - /// - /// A unique string used by props (that are marked as quantum) use to refer back to this group - /// - public string id; - - /// - /// Only required if type is `sockets`. This lists all the possible locations for any props assigned to this group. - /// - public QuantumSocketInfo[] sockets; - - /// - /// Optional. Only used if type is `states`. If this is true, then the first prop made part of this group will be used to construct a visibility box for an empty game object, which will be considered one of the states. - /// - public bool hasEmptyState; - - /// - /// Optional. Only used if type is `states`. If this is true, then the states will be presented in order, rather than in a random order - /// - public bool sequential; - - /// - /// Optional. Only used if type is `states` and `sequential` is true. If this is false, then after the last state has appeared, the object will no longer change state - /// - [DefaultValue(true)] public bool loop = true; - } - - [JsonObject] - public class QuantumSocketInfo - { - /// - /// The location of this socket - /// - public MVector3 position; - - /// - /// The rotation the quantum object will take if it's occupying this socket - /// - public MVector3 rotation; - - /// - /// The probability any props that are part of this group will occupy this socket - /// - [DefaultValue(1f)] public float probability = 1f; - } - - [JsonObject] - public class AudioVolumeInfo - { - /// - /// The location of this audio volume. Optional (will default to 0,0,0). - /// - public MVector3 position; - - /// - /// The radius of this audio volume - /// - public float radius; - - /// - /// The audio to use. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list. - /// - public string audio; - - /// - /// The audio track of this audio volume - /// - [DefaultValue("environment")] public AudioMixerTrackName track = AudioMixerTrackName.Environment; - } - } - - [JsonConverter(typeof(StringEnumConverter))] - public enum AudioMixerTrackName - { - [EnumMember(Value = @"undefined")] Undefined = 0, - [EnumMember(Value = @"menu")] Menu = 1, - [EnumMember(Value = @"music")] Music = 2, - [EnumMember(Value = @"environment")] Environment = 4, - [EnumMember(Value = @"environmentUnfiltered")] Environment_Unfiltered = 5, - [EnumMember(Value = @"endTimesSfx")] EndTimes_SFX = 8, - [EnumMember(Value = @"signal")] Signal = 16, - [EnumMember(Value = @"death")] Death = 32, - [EnumMember(Value = @"player")] Player = 64, - [EnumMember(Value = @"playerExternal")] Player_External = 65, - [EnumMember(Value = @"ship")] Ship = 128, - [EnumMember(Value = @"map")] Map = 256, - [EnumMember(Value = @"endTimesMusic")] EndTimes_Music = 512, - [EnumMember(Value = @"muffleWhileRafting")] MuffleWhileRafting = 1024, - [EnumMember(Value = @"muffleIndoors")] MuffleIndoors = 2048, - [EnumMember(Value = @"slideReelMusic")] SlideReelMusic = 4096, - } + +using NewHorizons.Utility; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Runtime.Serialization; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using System; +using NewHorizons.External.Modules.VariableSize; + +namespace NewHorizons.External.Modules +{ + [JsonObject] + public class PropModule + { + /// + /// Place props in predefined positions on the planet + /// + public DetailInfo[] details; + + /// + /// Add dialogue triggers to this planet + /// + public DialogueInfo[] dialogue; + + /// + /// Add ship log entry locations on this planet + /// + public EntryLocationInfo[] entryLocation; + + /// + /// Add Geysers to this planet + /// + public GeyserInfo[] geysers; + + /// + /// Add translatable text to this planet + /// + public NomaiTextInfo[] nomaiText; + + /// + /// Details which will be shown from 50km away. Meant to be lower resolution. + /// + public DetailInfo[] proxyDetails; + + /// + /// Add rafts to this planet + /// + public RaftInfo[] rafts; + + /// + /// Add triggers that reveal parts of the ship log on this planet + /// + public RevealInfo[] reveal; + + /// + /// Scatter props around this planet's surface + /// + public ScatterInfo[] scatter; + + /// + /// Add slideshows (from the DLC) to the planet + /// + public ProjectionInfo[] slideShows; + + /// + /// A list of quantum groups that props can be added to. An example of a group would be a list of possible locations for a QuantumSocketedObject. + /// + public QuantumGroupInfo[] quantumGroups; + + /// + /// Add tornadoes to this planet + /// + public TornadoInfo[] tornados; + + /// + /// Add volcanoes to this planet + /// + public VolcanoInfo[] volcanoes; + + /// + /// Add black/white-holes to this planet + /// + public SingularityModule[] singularities; + + /// + /// Add audio volumes to this planet + /// + public AudioVolumeInfo[] audioVolumes; + + /// + /// Add signalscope signals to this planet + /// + public SignalModule.SignalInfo[] signals; + + [JsonObject] + public class ScatterInfo + { + /// + /// Relative filepath to an asset-bundle + /// + public string assetBundle; + + /// + /// Number of props to scatter + /// + public int count; + + /// + /// Offset this prop once it is placed + /// + public MVector3 offset; + + /// + /// Either the path in the scene hierarchy of the item to copy or the path to the object in the supplied asset bundle + /// + public string path; + + /// + /// Rotate this prop once it is placed + /// + public MVector3 rotation; + + /// + /// Scale this prop once it is placed + /// + public float scale = 1f; + + /// + /// The number used as entropy for scattering the props + /// + public int seed; + + /// + /// The lowest height that these object will be placed at (only relevant if there's a heightmap) + /// + public float? minHeight; + + /// + /// The highest height that these objects will be placed at (only relevant if there's a heightmap) + /// + public float? maxHeight; + } + + [JsonObject] + public class DetailInfo + { + /// + /// An optional rename of the detail + /// + public string rename; + + /// + /// Do we override rotation and try to automatically align this object to stand upright on the body's surface? + /// + public bool alignToNormal; + + /// + /// Relative filepath to an asset-bundle to load the prefab defined in `path` from + /// + public string assetBundle; + + /// + /// Either the path in the scene hierarchy of the item to copy or the path to the object in the supplied asset bundle + /// + public string path; + + /// + /// Position of this prop relative to the body's center + /// + public MVector3 position; + + /// + /// A list of children to remove from this detail + /// + public string[] removeChildren; + + /// + /// Do we reset all the components on this object? Useful for certain props that have dialogue components attached to + /// them. + /// + public bool removeComponents; + + /// + /// Rotate this prop + /// + public MVector3 rotation; + + /// + /// Scale the prop + /// + [DefaultValue(1f)] public float scale = 1f; + + /// + /// If this value is not null, this prop will be quantum. Assign this field to the id of the quantum group it should be a part of. The group it is assigned to determines what kind of quantum object it is + /// + public string quantumGroupID; + + /// + /// The path (not including the root planet object) of the parent of this game object. Optional (will default to the root sector). + /// + public string parentPath; + } + + [JsonObject] + public class RaftInfo + { + /// + /// Position of the raft + /// + public MVector3 position; + } + + [JsonObject] + public class GeyserInfo + { + /// + /// Position of the geyser + /// + public MVector3 position; + + /// + /// Disable the individual particle systems of the geyser + /// + public bool disableBubbles, disableShaft, disableSpout; + } + + [JsonObject] + public class TornadoInfo + { + [JsonConverter(typeof(StringEnumConverter))] + public enum TornadoType + { + [EnumMember(Value = @"downwards")] Downwards = 0, + + [EnumMember(Value = @"upwards")] Upwards = 1, + + [EnumMember(Value = @"hurricane")] Hurricane = 2 + } + + [Obsolete("Downwards is deprecated. Use Type instead.")] public bool downwards; + + /// + /// Alternative to setting the position. Will choose a random place at this elevation. + /// + public float elevation; + + /// + /// The height of this tornado. + /// + [DefaultValue(30f)] public float height = 30f; + + /// + /// Position of the tornado + /// + public MVector3 position; + + /// + /// The colour of the tornado. + /// + public MColor tint; + + /// + /// What type of cyclone should this be? Upwards and downwards are both tornados and will push in that direction. + /// + [DefaultValue("downwards")] public TornadoType type = TornadoType.Downwards; + + /// + /// Angular distance from the starting position that it will wander, in terms of the angle around the x-axis. + /// + [DefaultValue(45f)] public float wanderDegreesX = 45f; + + /// + /// Angular distance from the starting position that it will wander, in terms of the angle around the z-axis. + /// + [DefaultValue(45f)] public float wanderDegreesZ = 45f; + + /// + /// The rate at which the tornado will wander around the planet. Set to 0 for it to be stationary. Should be around + /// 0.1. + /// + public float wanderRate; + + /// + /// The maximum distance at which you'll hear the sounds of the cyclone. If not set it will scale relative to the size of the cyclone. + /// + public float audioDistance; + } + + [JsonObject] + public class VolcanoInfo + { + /// + /// The colour of the meteor's lava. + /// + public MColor lavaTint; + + /// + /// Maximum time between meteor launches. + /// + [DefaultValue(20f)] + public float maxInterval = 20f; + + /// + /// Maximum random speed at which meteors are launched. + /// + [DefaultValue(150f)] + public float maxLaunchSpeed = 150f; + + /// + /// Minimum time between meteor launches. + /// + [DefaultValue(5f)] + public float minInterval = 5f; + + /// + /// Minimum random speed at which meteors are launched. + /// + [DefaultValue(50f)] + public float minLaunchSpeed = 50f; + + /// + /// Position of this volcano. + /// + public MVector3 position; + + /// + /// Scale of the meteors. + /// + public float scale = 1; + + /// + /// The colour of the meteor's stone. + /// + public MColor stoneTint; + } + + [JsonObject] + public class DialogueInfo + { + /// + /// Prevents the dialogue from being created after a specific persistent condition is set. Useful for remote dialogue + /// triggers that you want to have happen only once. + /// + public string blockAfterPersistentCondition; + + /// + /// If a pathToAnimController is supplied, if you are within this distance the character will look at you. If it is set + /// to 0, they will only look at you when spoken to. + /// + public float lookAtRadius; + + /// + /// If this dialogue is meant for a character, this is the relative path from the planet to that character's + /// CharacterAnimController or SolanumAnimController. + /// + public string pathToAnimController; + + /// + /// When you enter into dialogue, you will look here. + /// + public MVector3 position; + + /// + /// Radius of the spherical collision volume where you get the "talk to" prompt when looking at. If you use a + /// remoteTriggerPosition, you can set this to 0 to make the dialogue only trigger remotely. + /// + public float radius = 1f; + + /// + /// Allows you to trigger dialogue from a distance when you walk into an area. + /// + public MVector3 remoteTriggerPosition; + + /// + /// Distance from radius the prompt appears + /// + [DefaultValue(2f)] public float range = 2f; + + /// + /// The radius of the remote trigger volume. + /// + public float remoteTriggerRadius; + + /// + /// Relative path to the xml file defining the dialogue. + /// + public string xmlFile; + } + + [JsonObject] + public class RevealInfo + { + [JsonConverter(typeof(StringEnumConverter))] + public enum RevealVolumeType + { + [EnumMember(Value = @"enter")] Enter = 0, + + [EnumMember(Value = @"observe")] Observe = 1, + + [EnumMember(Value = @"snapshot")] Snapshot = 2 + } + + /// + /// The max view angle (in degrees) the player can see the volume with to unlock the fact (`observe` only) + /// + public float maxAngle = 180f; // Observe Only + + /// + /// The max distance the user can be away from the volume to reveal the fact (`snapshot` and `observe` only) + /// + public float maxDistance = -1f; // Snapshot & Observe Only + + /// + /// The position to place this volume at + /// + public MVector3 position; + + /// + /// The radius of this reveal volume + /// + public float radius = 1f; + + /// + /// What needs to be done to the volume to unlock the facts + /// + [DefaultValue("enter")] public RevealVolumeType revealOn = RevealVolumeType.Enter; + + /// + /// A list of facts to reveal + /// + public string[] reveals; + + /// + /// An achievement to unlock. Optional. + /// + public string achievementID; + } + + [JsonObject] + public class EntryLocationInfo + { + /// + /// Whether this location is cloaked + /// + public bool cloaked; + + /// + /// ID of the entry this location relates to + /// + public string id; + + /// + /// The position of this entry location + /// + public MVector3 position; + } + + [JsonObject] + public class NomaiTextInfo + { + [JsonConverter(typeof(StringEnumConverter))] + public enum NomaiTextType + { + [EnumMember(Value = @"wall")] Wall = 0, + + [EnumMember(Value = @"scroll")] Scroll = 1, + + [EnumMember(Value = @"computer")] Computer = 2, + + [EnumMember(Value = @"cairn")] Cairn = 3, + + [EnumMember(Value = @"recorder")] Recorder = 4, + + [EnumMember(Value = @"preCrashRecorder")] PreCrashRecorder = 5, + + [EnumMember(Value = @"preCrashComputer")] PreCrashComputer = 6 + } + + /// + /// Additional information about each arc in the text + /// + public NomaiTextArcInfo[] arcInfo; + + /// + /// The normal vector for this object. Used for writing on walls and positioning computers. + /// + public MVector3 normal; + + /// + /// Position of the root of this text + /// + public MVector3 position; + + /// + /// The euler angle rotation of this object. Not required if setting the normal. Computers and cairns will orient + /// themselves to the surface of the planet automatically. + /// + public MVector3 rotation; + + /// + /// The random seed used to pick what the text arcs will look like. + /// + public int seed; // For randomizing arcs + + /// + /// The type of object this is. + /// + [DefaultValue("wall")] public NomaiTextType type = NomaiTextType.Wall; + + /// + /// The relative path to the xml file for this object. + /// + public string xmlFile; + + /// + /// The relative path from the planet to the parent of this object. Optional (will default to the root sector). + /// + public string parentPath; + + /// + /// An optional rename of this object + /// + public string rename; + + } + + [JsonObject] + public class NomaiTextArcInfo + { + [JsonConverter(typeof(StringEnumConverter))] + public enum NomaiTextArcType + { + [EnumMember(Value = @"adult")] Adult = 0, + + [EnumMember(Value = @"child")] Child = 1, + + [EnumMember(Value = @"stranger")] Stranger = 2 + } + + /// + /// Whether to flip the spiral from left-curling to right-curling or vice versa. + /// + public bool mirror; + + /// + /// The local position of this object on the wall. + /// + public MVector2 position; + + /// + /// The type of text to display. + /// + [DefaultValue("adult")] public NomaiTextArcType type = NomaiTextArcType.Adult; + + /// + /// Which variation of the chosen type to place. If not specified, a random variation will be selected based on the seed provided in the parent module. + /// + [DefaultValue(-1)] public int variation = -1; + + /// + /// The z euler angle for this arc. + /// + [Range(0f, 360f)] public float zRotation; + } + + [JsonObject] + public class ProjectionInfo + { + [JsonConverter(typeof(StringEnumConverter))] + public enum SlideShowType + { + [EnumMember(Value = @"slideReel")] SlideReel = 0, + + [EnumMember(Value = @"autoProjector")] AutoProjector = 1, + + [EnumMember(Value = @"visionTorchTarget")] VisionTorchTarget = 2, + + [EnumMember(Value = @"standingVisionTorch")] StandingVisionTorch = 3, + + } + + /// + /// The position of this slideshow. + /// + public MVector3 position; + + /// + /// The ship log entries revealed after finishing this slide reel. + /// + public string[] reveals; + + /// + /// The rotation of this slideshow. + /// + public MVector3 rotation; + + /// + /// The list of slides for this object. + /// + public SlideInfo[] slides; + + /// + /// The type of object this is. + /// + [DefaultValue("slideReel")] public SlideShowType type = SlideShowType.SlideReel; + + /// + /// The relative path from the planet to the parent of this slideshow. Optional (will default to the root sector). + /// + public string parentPath; + } + + [JsonObject] + public class SlideInfo + { + /// + /// Ambient light colour when viewing this slide. + /// + public MColor ambientLightColor; + + + // SlideAmbientLightModule + + /// + /// Ambient light intensity when viewing this slide. + /// + public float ambientLightIntensity; + + /// + /// Ambient light range when viewing this slide. + /// + public float ambientLightRange; + + // SlideBackdropAudioModule + + /// + /// The name of the AudioClip that will continuously play while watching these slides + /// + public string backdropAudio; + + /// + /// The time to fade into the backdrop audio + /// + public float backdropFadeTime; + + // SlideBeatAudioModule + + /// + /// The name of the AudioClip for a one-shot sound when opening the slide. + /// + public string beatAudio; + + /// + /// The time delay until the one-shot audio + /// + public float beatDelay; + + + // SlideBlackFrameModule + + /// + /// Before viewing this slide, there will be a black frame for this many seconds. + /// + public float blackFrameDuration; + + /// + /// The path to the image file for this slide. + /// + public string imagePath; + + + // SlidePlayTimeModule + + /// + /// Play-time duration for auto-projector slides. + /// + public float playTimeDuration; + + + // SlideShipLogEntryModule + + /// + /// Ship log entry revealed when viewing this slide + /// + public string reveal; + + /// + /// Spotlight intensity modifier when viewing this slide. + /// + public float spotIntensityMod; + } + + + + + [JsonConverter(typeof(StringEnumConverter))] + public enum QuantumGroupType + { + [EnumMember(Value = @"sockets")] Sockets = 0, + + [EnumMember(Value = @"states")] States = 1, + + FailedValidation = 10 + } + + [JsonObject] + public class QuantumGroupInfo + { + /// + /// What type of group this is: does it define a list of states a single quantum object could take or a list of sockets one or more quantum objects could share? + /// + public QuantumGroupType type; + + /// + /// A unique string used by props (that are marked as quantum) use to refer back to this group + /// + public string id; + + /// + /// Only required if type is `sockets`. This lists all the possible locations for any props assigned to this group. + /// + public QuantumSocketInfo[] sockets; + + /// + /// Optional. Only used if type is `states`. If this is true, then the first prop made part of this group will be used to construct a visibility box for an empty game object, which will be considered one of the states. + /// + public bool hasEmptyState; + + /// + /// Optional. Only used if type is `states`. If this is true, then the states will be presented in order, rather than in a random order + /// + public bool sequential; + + /// + /// Optional. Only used if type is `states` and `sequential` is true. If this is false, then after the last state has appeared, the object will no longer change state + /// + [DefaultValue(true)] public bool loop = true; + } + + [JsonObject] + public class QuantumSocketInfo + { + /// + /// The location of this socket + /// + public MVector3 position; + + /// + /// The rotation the quantum object will take if it's occupying this socket + /// + public MVector3 rotation; + + /// + /// The probability any props that are part of this group will occupy this socket + /// + [DefaultValue(1f)] public float probability = 1f; + } + + [JsonObject] + public class AudioVolumeInfo + { + /// + /// The location of this audio volume. Optional (will default to 0,0,0). + /// + public MVector3 position; + + /// + /// The radius of this audio volume + /// + public float radius; + + /// + /// The audio to use. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list. + /// + public string audio; + + /// + /// The audio track of this audio volume + /// + [DefaultValue("environment")] public AudioMixerTrackName track = AudioMixerTrackName.Environment; + } + } + + [JsonConverter(typeof(StringEnumConverter))] + public enum AudioMixerTrackName + { + [EnumMember(Value = @"undefined")] Undefined = 0, + [EnumMember(Value = @"menu")] Menu = 1, + [EnumMember(Value = @"music")] Music = 2, + [EnumMember(Value = @"environment")] Environment = 4, + [EnumMember(Value = @"environmentUnfiltered")] Environment_Unfiltered = 5, + [EnumMember(Value = @"endTimesSfx")] EndTimes_SFX = 8, + [EnumMember(Value = @"signal")] Signal = 16, + [EnumMember(Value = @"death")] Death = 32, + [EnumMember(Value = @"player")] Player = 64, + [EnumMember(Value = @"playerExternal")] Player_External = 65, + [EnumMember(Value = @"ship")] Ship = 128, + [EnumMember(Value = @"map")] Map = 256, + [EnumMember(Value = @"endTimesMusic")] EndTimes_Music = 512, + [EnumMember(Value = @"muffleWhileRafting")] MuffleWhileRafting = 1024, + [EnumMember(Value = @"muffleIndoors")] MuffleIndoors = 2048, + [EnumMember(Value = @"slideReelMusic")] SlideReelMusic = 4096, + } } \ No newline at end of file diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index 0abbdeee..8b247950 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -1,2160 +1,2160 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Celestial Body Schema", - "type": "object", - "description": "Describes a body to generate", - "additionalProperties": false, - "required": [ - "name" - ], - "properties": { - "AsteroidBelt": { - "description": "Generate asteroids around this body", - "$ref": "#/definitions/AsteroidBeltModule" - }, - "Atmosphere": { - "description": "Describes this Body's atmosphere", - "$ref": "#/definitions/AtmosphereModule" - }, - "Base": { - "description": "Base Properties of this Body", - "$ref": "#/definitions/BaseModule" - }, - "Bramble": { - "description": "Add bramble nodes to this planet and/or make this planet a bramble dimension", - "$ref": "#/definitions/BrambleModule" - }, - "buildPriority": { - "type": "integer", - "description": "Set to a higher number if you wish for this body to be built sooner", - "format": "int32", - "default": -1 - }, - "canShowOnTitle": { - "type": "boolean", - "description": "Should this planet ever be shown on the title screen?" - }, - "Cloak": { - "description": "Add a cloaking field to this planet", - "$ref": "#/definitions/CloakModule" - }, - "destroy": { - "type": "boolean", - "description": "`true` if you want to delete this planet" - }, - "FocalPoint": { - "description": "Make this body into a focal point (barycenter)", - "$ref": "#/definitions/FocalPointModule" - }, - "Funnel": { - "description": "Add funnel from this planet to another", - "$ref": "#/definitions/FunnelModule" - }, - "HeightMap": { - "description": "Generate the surface of this planet using a heightmap", - "$ref": "#/definitions/HeightMapModule" - }, - "isQuantumState": { - "type": "boolean", - "description": "Does this config describe a quantum state of a custom planet defined in another file?" - }, - "Lava": { - "description": "Add lava to this planet", - "$ref": "#/definitions/LavaModule" - }, - "name": { - "type": "string", - "description": "Unique name of your planet", - "minLength": 1 - }, - "Orbit": { - "description": "Describes this Body's orbit (or lack there of)", - "$ref": "#/definitions/OrbitModule" - }, - "ProcGen": { - "description": "Procedural Generation", - "$ref": "#/definitions/ProcGenModule" - }, - "Props": { - "description": "Spawn various objects on this body", - "$ref": "#/definitions/PropModule" - }, - "ReferenceFrame": { - "description": "Reference frame properties of this body", - "$ref": "#/definitions/ReferenceFrameModule" - }, - "removeChildren": { - "type": "array", - "description": "A list of paths to child GameObjects to destroy on this planet", - "items": { - "type": "string" - } - }, - "Ring": { - "description": "Creates a ring around the planet", - "$ref": "#/definitions/RingModule" - }, - "Sand": { - "description": "Add sand to this planet", - "$ref": "#/definitions/SandModule" - }, - "ShipLog": { - "description": "Add ship log entries to this planet and describe how it looks in map mode", - "$ref": "#/definitions/ShipLogModule" - }, - "Spawn": { - "description": "Spawn the player at this planet", - "$ref": "#/definitions/SpawnModule" - }, - "Star": { - "description": "Make this body a star", - "$ref": "#/definitions/StarModule" - }, - "starSystem": { - "type": "string", - "description": "Unique star system containing your planet", - "default": "SolarSystem" - }, - "version": { - "type": "string", - "description": "Version of New Horizons this config is using (Doesn't do anything)" - }, - "Water": { - "description": "Add water to this planet", - "$ref": "#/definitions/WaterModule" - }, - "$schema": { - "type": "string", - "description": "The schema to validate with" - } - }, - "definitions": { - "AsteroidBeltModule": { - "type": "object", - "additionalProperties": false, - "properties": { - "amount": { - "type": "integer", - "description": "Amount of asteroids to create.", - "format": "int32", - "default": -1, - "maximum": 200.0, - "minimum": -1.0 - }, - "inclination": { - "type": "number", - "description": "Angle between the rings and the equatorial plane of the planet.", - "format": "float" - }, - "innerRadius": { - "type": "number", - "description": "Lowest distance from the planet asteroids can spawn", - "format": "float", - "minimum": 0.0 - }, - "longitudeOfAscendingNode": { - "type": "number", - "description": "Angle defining the point where the rings rise up from the planet's equatorial plane if inclination is nonzero.", - "format": "float" - }, - "maxSize": { - "type": "number", - "description": "Maximum size of the asteroids.", - "format": "float", - "default": 50, - "minimum": 0.0 - }, - "minSize": { - "type": "number", - "description": "Minimum size of the asteroids.", - "format": "float", - "default": 20, - "minimum": 0.0 - }, - "outerRadius": { - "type": "number", - "description": "Greatest distance from the planet asteroids can spawn", - "format": "float", - "minimum": 0.0 - }, - "procGen": { - "description": "How the asteroids are generated", - "$ref": "#/definitions/ProcGenModule" - }, - "randomSeed": { - "type": "integer", - "description": "Number used to randomize asteroid positions", - "format": "int32" - } - } - }, - "ProcGenModule": { - "type": "object", - "additionalProperties": false, - "properties": { - "color": { - "$ref": "#/definitions/MColor" - }, - "scale": { - "type": "number", - "format": "float", - "minimum": 0.0 - } - } - }, - "MColor": { - "type": "object", - "additionalProperties": false, - "properties": { - "r": { - "type": "integer", - "description": "The red component of this colour", - "format": "int32", - "maximum": 2147483647.0, - "minimum": 0.0 - }, - "g": { - "type": "integer", - "description": "The green component of this colour", - "format": "int32", - "maximum": 2147483647.0, - "minimum": 0.0 - }, - "b": { - "type": "integer", - "description": "The blue component of this colour", - "format": "int32", - "maximum": 2147483647.0, - "minimum": 0.0 - }, - "a": { - "type": "integer", - "description": "The alpha (opacity) component of this colour", - "format": "int32", - "default": 255, - "maximum": 255.0, - "minimum": 0.0 - } - } - }, - "AtmosphereModule": { - "type": "object", - "additionalProperties": false, - "properties": { - "atmosphereTint": { - "description": "Colour of atmospheric shader on the planet.", - "$ref": "#/definitions/MColor" - }, - "atmosphereSunIntensity": { - "type": "number", - "description": "How intense should the sun appear in the sky.\nAlso affects general atmosphere brightness.\nDefault value of 1 matches Timber Hearth.\nIf not set, this will be determined based off the distance to the sun.", - "format": "float", - "minimum": 0.0 - }, - "clouds": { - "description": "Describes the clouds in the atmosphere", - "$ref": "#/definitions/CloudInfo" - }, - "fogDensity": { - "type": "number", - "description": "How dense the fog is, if you put fog.", - "format": "float", - "maximum": 1.0, - "minimum": 0.0 - }, - "fogSize": { - "type": "number", - "description": "Radius of fog sphere, independent of the atmosphere. This has to be set for there to be fog.", - "format": "float", - "minimum": 0.0 - }, - "fogTint": { - "description": "Colour of fog on the planet, if you put fog.", - "$ref": "#/definitions/MColor" - }, - "hasOxygen": { - "type": "boolean", - "description": "Lets you survive on the planet without a suit." - }, - "hasRain": { - "type": "boolean", - "description": "Does this planet have rain?" - }, - "hasSnow": { - "type": "boolean", - "description": "Does this planet have snow?" - }, - "size": { - "type": "number", - "description": "Scale height of the atmosphere", - "format": "float" - }, - "useAtmosphereShader": { - "type": "boolean", - "description": "Whether we use an atmospheric shader on the planet. Doesn't affect clouds, fog, rain, snow, oxygen, etc. Purely\nvisual." - }, - "minShockSpeed": { - "type": "number", - "description": "Minimum speed that your ship can go in the atmosphere where flames will appear.", - "format": "float", - "default": 100.0 - }, - "maxShockSpeed": { - "type": "number", - "description": "Maximum speed that your ship can go in the atmosphere where flames will appear at their brightest.", - "format": "float", - "default": 300.0 - } - } - }, - "CloudInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "cloudsPrefab": { - "description": "Should these clouds be based on Giant's Deep's banded clouds, or the Quantum Moon's non-banded clouds?", - "$ref": "#/definitions/CloudPrefabType" - }, - "capPath": { - "type": "string", - "description": "Relative filepath to the cloud cap texture, if the planet has clouds." - }, - "fluidType": { - "description": "Fluid type for sounds/effects when colliding with this cloud.", - "default": "cloud", - "$ref": "#/definitions/CloudFluidType" - }, - "hasLightning": { - "type": "boolean", - "description": "Add lightning to this planet like on Giant's Deep." - }, - "innerCloudRadius": { - "type": "number", - "description": "Radius from the center to the inner layer of the clouds.", - "format": "float" - }, - "lightningGradient": { - "type": "array", - "description": "Colour gradient of the lightning, time is in seconds.", - "items": { - "$ref": "#/definitions/MGradient" - } - }, - "outerCloudRadius": { - "type": "number", - "description": "Radius from the center to the outer layer of the clouds.", - "format": "float" - }, - "rampPath": { - "type": "string", - "description": "Relative filepath to the cloud ramp texture, if the planet has clouds. If you don't put anything here it will be\nauto-generated." - }, - "texturePath": { - "type": "string", - "description": "Relative filepath to the cloud texture, if the planet has clouds." - }, - "tint": { - "description": "Colour of the inner cloud layer.", - "$ref": "#/definitions/MColor" - }, - "unlit": { - "type": "boolean", - "description": "If the top layer shouldn't have shadows. Set to true if you're making a brown dwarf for example." - } - } - }, - "CloudPrefabType": { - "type": "string", - "description": "", - "x-enumNames": [ - "GiantsDeep", - "QuantumMoon", - "Basic" - ], - "enum": [ - "giantsDeep", - "quantumMoon", - "basic" - ] - }, - "CloudFluidType": { - "type": "string", - "description": "", - "x-enumNames": [ - "None", - "Water", - "Cloud", - "Sand", - "Plasma" - ], - "enum": [ - "none", - "water", - "cloud", - "sand", - "plasma" - ] - }, - "MGradient": { - "type": "object", - "additionalProperties": false, - "properties": { - "time": { - "type": "number", - "format": "float" - }, - "tint": { - "$ref": "#/definitions/MColor" - } - } - }, - "BaseModule": { - "type": "object", - "additionalProperties": false, - "properties": { - "ambientLight": { - "type": "number", - "description": "The intensity of light the dark side of the body should have. Timber Hearth has `1.4` for reference", - "format": "float" - }, - "centerOfSolarSystem": { - "type": "boolean", - "description": "Set this to true if you are replacing the sun with a different body. Only one object in a star system should ever\nhave this set to true." - }, - "cometTailRotation": { - "description": "If it has a comet tail, it'll be oriented according to these Euler angles.", - "$ref": "#/definitions/MVector3" - }, - "gravityFallOff": { - "description": "How gravity falls off with distance. Most planets use linear but the sun and some moons use inverseSquared.", - "default": "linear", - "$ref": "#/definitions/GravityFallOff" - }, - "groundSize": { - "type": "number", - "description": "Radius of a simple sphere used as the ground for the planet. If you want to use more complex terrain, leave this as\n0.", - "format": "float" - }, - "hasCometTail": { - "type": "boolean", - "description": "If you want the body to have a tail like the Interloper." - }, - "hasMapMarker": { - "type": "boolean", - "description": "If the body should have a marker on the map screen." - }, - "invulnerableToSun": { - "type": "boolean", - "description": "Can this planet survive entering a star?" - }, - "showMinimap": { - "type": "boolean", - "description": "Do we show the minimap when walking around this planet?", - "default": true - }, - "soiOverride": { - "type": "number", - "description": "An override for the radius of the planet's gravitational sphere of influence. Optional", - "format": "float" - }, - "surfaceGravity": { - "type": "number", - "description": "The acceleration due to gravity felt as the surfaceSize. Timber Hearth has 12 for reference", - "format": "float" - }, - "surfaceSize": { - "type": "number", - "description": "A scale height used for a number of things. Should be the approximate radius of the body.", - "format": "float" - }, - "zeroGravityRadius": { - "type": "number", - "description": "Radius of the zero gravity volume. This will make it so no gravity from any planet will affect you. Useful for satellites.", - "format": "float" - } - } - }, - "MVector3": { - "type": "object", - "additionalProperties": false, - "properties": { - "x": { - "type": "number", - "format": "float" - }, - "y": { - "type": "number", - "format": "float" - }, - "z": { - "type": "number", - "format": "float" - } - } - }, - "GravityFallOff": { - "type": "string", - "description": "", - "x-enumNames": [ - "Linear", - "InverseSquared" - ], - "enum": [ - "linear", - "inverseSquared" - ] - }, - "BrambleModule": { - "type": "object", - "additionalProperties": false, - "properties": { - "dimension": { - "description": "Defining this value will make this body a bramble dimension. Leave it null to not do that.", - "$ref": "#/definitions/BrambleDimensionInfo" - }, - "nodes": { - "type": "array", - "description": "Place nodes/seeds that take you to other bramble dimensions", - "items": { - "$ref": "#/definitions/BrambleNodeInfo" - } - } - } - }, - "BrambleDimensionInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "fogTint": { - "description": "The color of the fog inside this dimension. Leave blank for the default yellowish color: (113, 107, 81)", - "$ref": "#/definitions/MColor" - }, - "linksTo": { - "type": "string", - "description": "The name of the *node* that the player is taken to when exiting this dimension." - }, - "radius": { - "type": "number", - "description": "The internal radius (in meters) of the dimension. The default is 1705.", - "format": "float", - "default": 1705.0 - }, - "allowedEntrances": { - "type": "array", - "description": "An array of integers from 0-5. By default, all entrances are allowed. To force this dimension to warp players in from only one point (like the anglerfish nest dimension in the base game) set this value to [3], [5], or similar. Values of 0-5 only.", - "items": { - "type": "integer", - "format": "int32" - } - } - } - }, - "BrambleNodeInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "position": { - "description": "The physical position of the node", - "$ref": "#/definitions/MVector3" - }, - "rotation": { - "description": "The physical rotation of the node", - "$ref": "#/definitions/MVector3" - }, - "scale": { - "type": "number", - "description": "The physical scale of the node, as a multiplier of the original size", - "format": "float", - "default": 1.0 - }, - "linksTo": { - "type": "string", - "description": "The name of the planet that hosts the dimension this node links to" - }, - "name": { - "type": "string", - "description": "The name of this node. Only required if this node should serve as an exit." - }, - "isSeed": { - "type": "boolean", - "description": "Set this to true to make this node a seed instead of a node the player can enter", - "default": false - }, - "fogTint": { - "description": "The color of the fog inside the node. Leave blank for the default yellowish color: (131, 124, 105, 255)", - "$ref": "#/definitions/MColor" - }, - "lightTint": { - "description": "The color of the shafts of light coming from the entrances to the node. Leave blank for the default yellowish color: (131, 124, 105, 255)", - "$ref": "#/definitions/MColor" - }, - "farFogTint": { - "description": "The color a dimension's background fog turns when you approach this node (if it's in a dimension). If this node is not in a dimension, this does nothing. Leave blank for the default yellowish white color: (255, 245, 217, 255)", - "$ref": "#/definitions/MColor" - }, - "possibleExits": { - "type": "array", - "description": "An array of integers from 0-5. By default, all exits are allowed. To force this node to warp players out from only one hole set this value to [3], [5], or similar. Values of 0-5 only.", - "items": { - "type": "integer", - "format": "int32" - } - } - } - }, - "CloakModule": { - "type": "object", - "additionalProperties": false, - "properties": { - "radius": { - "type": "number", - "description": "Radius of the cloaking field around the planet. It's a bit finicky so experiment with different values. If you\ndon't want a cloak, leave this as 0.", - "format": "float" - }, - "audio": { - "type": "string", - "description": "The audio that will play when entering the cloaking field. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." - } - } - }, - "FocalPointModule": { - "type": "object", - "additionalProperties": false, - "properties": { - "primary": { - "type": "string", - "description": "Name of the primary planet in this binary system" - }, - "secondary": { - "type": "string", - "description": "Name of the secondary planet in this binary system" - } - } - }, - "FunnelModule": { - "type": "object", - "additionalProperties": false, - "properties": { - "curve": { - "type": "array", - "description": "Scale this module over time", - "items": { - "$ref": "#/definitions/TimeValuePair" - } - }, - "target": { - "type": "string", - "description": "The planet the funnel will flow to" - }, - "tint": { - "description": "Tint of the funnel", - "$ref": "#/definitions/MColor" - }, - "type": { - "description": "Type of fluid the funnel transfers", - "default": "sand", - "$ref": "#/definitions/FunnelType" - } - } - }, - "TimeValuePair": { - "type": "object", - "additionalProperties": false, - "properties": { - "time": { - "type": "number", - "description": "A specific point in time", - "format": "float" - }, - "value": { - "type": "number", - "description": "The value for this point in time", - "format": "float" - } - } - }, - "FunnelType": { - "type": "string", - "description": "", - "x-enumNames": [ - "Sand", - "Water", - "Lava", - "Star" - ], - "enum": [ - "sand", - "water", - "lava", - "star" - ] - }, - "HeightMapModule": { - "type": "object", - "additionalProperties": false, - "properties": { - "heightMap": { - "type": "string", - "description": "Relative filepath to the texture used for the terrain height." - }, - "maxHeight": { - "type": "number", - "description": "The highest points on your planet will be at this height.", - "format": "float", - "minimum": 0.0 - }, - "minHeight": { - "type": "number", - "description": "The lowest points on your planet will be at this height.", - "format": "float", - "minimum": 0.0 - }, - "stretch": { - "description": "The scale of the terrain.", - "$ref": "#/definitions/MVector3" - }, - "textureMap": { - "type": "string", - "description": "Relative filepath to the texture used for the terrain." - }, - "resolution": { - "type": "integer", - "description": "Resolution of the heightmap.\nHigher values means more detail but also more memory/cpu/gpu usage.\nThis value will be 1:1 with the heightmap texture width, but only at the equator.", - "format": "int32", - "default": 204, - "maximum": 2000.0, - "minimum": 4.0 - } - } - }, - "LavaModule": { - "type": "object", - "additionalProperties": false, - "properties": { - "curve": { - "type": "array", - "description": "Scale this module over time", - "items": { - "$ref": "#/definitions/TimeValuePair" - } - }, - "size": { - "type": "number", - "description": "Size of the lava sphere", - "format": "float" - }, - "tint": { - "description": "Tint of the lava", - "$ref": "#/definitions/MColor" - } - } - }, - "OrbitModule": { - "type": "object", - "additionalProperties": false, - "properties": { - "staticPosition": { - "description": "Specify this if you want the body to remain stationary at a given location (ie not orbit its parent). Required for Bramble dimensions", - "$ref": "#/definitions/MVector3" - }, - "primaryBody": { - "type": "string", - "description": "The name of the body this one will orbit around" - }, - "isMoon": { - "type": "boolean", - "description": "Is this the moon of a planet? Used for determining when its name is shown on the map." - }, - "axialTilt": { - "type": "number", - "description": "The angle between the normal to the orbital plane and its axis of rotation.", - "format": "float" - }, - "siderealPeriod": { - "type": "number", - "description": "Rotation period in minutes.", - "format": "float" - }, - "isTidallyLocked": { - "type": "boolean", - "description": "Should the body always have one side facing its primary?" - }, - "alignmentAxis": { - "description": "If it is tidally locked, this direction will face towards the primary. Ex: Interloper uses `0, -1, 0`. Most planets\nwill want something like `-1, 0, 0`.", - "$ref": "#/definitions/MVector3" - }, - "showOrbitLine": { - "type": "boolean", - "description": "Referring to the orbit line in the map screen.", - "default": true - }, - "dottedOrbitLine": { - "type": "boolean", - "description": "Should the orbit line be dotted?" - }, - "isStatic": { - "type": "boolean", - "description": "Is the body meant to stay in one place without moving? If staticPosition is not set, the initial position\nwill be determined using its orbital parameters." - }, - "tint": { - "description": "Colour of the orbit-line in the map view.", - "$ref": "#/definitions/MColor" - }, - "trackingOrbitLine": { - "type": "boolean", - "description": "Should we just draw a line behind its orbit instead of the entire circle/ellipse?" - }, - "semiMajorAxis": { - "type": "number", - "description": "The semi-major axis of the ellipse that is the body's orbit. For a circular orbit this is the radius.", - "format": "float", - "default": 5000.0, - "minimum": 0.0 - }, - "inclination": { - "type": "number", - "description": "The angle (in degrees) between the body's orbit and the plane of the star system", - "format": "float" - }, - "longitudeOfAscendingNode": { - "type": "number", - "description": "An angle (in degrees) defining the point where the orbit of the body rises above the orbital plane if it has\nnonzero inclination.", - "format": "float" - }, - "eccentricity": { - "type": "number", - "description": "At 0 the orbit is a circle. The closer to 1 it is, the more oval-shaped the orbit is.", - "format": "float", - "maximum": 1.0, - "minimum": 0.0 - }, - "argumentOfPeriapsis": { - "type": "number", - "description": "An angle (in degrees) defining the location of the periapsis (the closest distance to it's primary body) if it has\nnonzero eccentricity.", - "format": "float" - }, - "trueAnomaly": { - "type": "number", - "description": "Where the planet should start off in its orbit in terms of the central angle.", - "format": "float" - } - } - }, - "PropModule": { - "type": "object", - "additionalProperties": false, - "properties": { - "details": { - "type": "array", - "description": "Place props in predefined positions on the planet", - "items": { - "$ref": "#/definitions/DetailInfo" - } - }, - "dialogue": { - "type": "array", - "description": "Add dialogue triggers to this planet", - "items": { - "$ref": "#/definitions/DialogueInfo" - } - }, - "entryLocation": { - "type": "array", - "description": "Add ship log entry locations on this planet", - "items": { - "$ref": "#/definitions/EntryLocationInfo" - } - }, - "geysers": { - "type": "array", - "description": "Add Geysers to this planet", - "items": { - "$ref": "#/definitions/GeyserInfo" - } - }, - "nomaiText": { - "type": "array", - "description": "Add translatable text to this planet", - "items": { - "$ref": "#/definitions/NomaiTextInfo" - } - }, - "proxyDetails": { - "type": "array", - "description": "Details which will be shown from 50km away. Meant to be lower resolution.", - "items": { - "$ref": "#/definitions/DetailInfo" - } - }, - "rafts": { - "type": "array", - "description": "Add rafts to this planet", - "items": { - "$ref": "#/definitions/RaftInfo" - } - }, - "reveal": { - "type": "array", - "description": "Add triggers that reveal parts of the ship log on this planet", - "items": { - "$ref": "#/definitions/RevealInfo" - } - }, - "scatter": { - "type": "array", - "description": "Scatter props around this planet's surface", - "items": { - "$ref": "#/definitions/ScatterInfo" - } - }, - "slideShows": { - "type": "array", - "description": "Add slideshows (from the DLC) to the planet", - "items": { - "$ref": "#/definitions/ProjectionInfo" - } - }, - "quantumGroups": { - "type": "array", - "description": "A list of quantum groups that props can be added to. An example of a group would be a list of possible locations for a QuantumSocketedObject.", - "items": { - "$ref": "#/definitions/QuantumGroupInfo" - } - }, - "tornados": { - "type": "array", - "description": "Add tornadoes to this planet", - "items": { - "$ref": "#/definitions/TornadoInfo" - } - }, - "volcanoes": { - "type": "array", - "description": "Add volcanoes to this planet", - "items": { - "$ref": "#/definitions/VolcanoInfo" - } - }, - "singularities": { - "type": "array", - "description": "Add black/white-holes to this planet", - "items": { - "$ref": "#/definitions/SingularityModule" - } - }, - "audioVolumes": { - "type": "array", - "description": "Add audio volumes to this planet", - "items": { - "$ref": "#/definitions/AudioVolumeInfo" - } - }, - "signals": { - "type": "array", - "description": "Add signalscope signals to this planet", - "items": { - "$ref": "#/definitions/SignalInfo" - } - } - } - }, - "DetailInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "rename": { - "type": "string", - "description": "An optional rename of the detail" - }, - "alignToNormal": { - "type": "boolean", - "description": "Do we override rotation and try to automatically align this object to stand upright on the body's surface?" - }, - "assetBundle": { - "type": "string", - "description": "Relative filepath to an asset-bundle to load the prefab defined in `path` from" - }, - "path": { - "type": "string", - "description": "Either the path in the scene hierarchy of the item to copy or the path to the object in the supplied asset bundle" - }, - "position": { - "description": "Position of this prop relative to the body's center", - "$ref": "#/definitions/MVector3" - }, - "removeChildren": { - "type": "array", - "description": "A list of children to remove from this detail", - "items": { - "type": "string" - } - }, - "removeComponents": { - "type": "boolean", - "description": "Do we reset all the components on this object? Useful for certain props that have dialogue components attached to\nthem." - }, - "rotation": { - "description": "Rotate this prop", - "$ref": "#/definitions/MVector3" - }, - "scale": { - "type": "number", - "description": "Scale the prop", - "format": "float", - "default": 1.0 - }, - "quantumGroupID": { - "type": "string", - "description": "If this value is not null, this prop will be quantum. Assign this field to the id of the quantum group it should be a part of. The group it is assigned to determines what kind of quantum object it is" - }, - "parentPath": { - "type": "string", - "description": "The path (not including the root planet object) of the parent of this game object. Optional (will default to the root sector)." - } - } - }, - "DialogueInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "blockAfterPersistentCondition": { - "type": "string", - "description": "Prevents the dialogue from being created after a specific persistent condition is set. Useful for remote dialogue\ntriggers that you want to have happen only once." - }, - "lookAtRadius": { - "type": "number", - "description": "If a pathToAnimController is supplied, if you are within this distance the character will look at you. If it is set\nto 0, they will only look at you when spoken to.", - "format": "float" - }, - "pathToAnimController": { - "type": "string", - "description": "If this dialogue is meant for a character, this is the relative path from the planet to that character's\nCharacterAnimController or SolanumAnimController." - }, - "position": { - "description": "When you enter into dialogue, you will look here.", - "$ref": "#/definitions/MVector3" - }, - "radius": { - "type": "number", - "description": "Radius of the spherical collision volume where you get the \"talk to\" prompt when looking at. If you use a\nremoteTriggerPosition, you can set this to 0 to make the dialogue only trigger remotely.", - "format": "float" - }, - "remoteTriggerPosition": { - "description": "Allows you to trigger dialogue from a distance when you walk into an area.", - "$ref": "#/definitions/MVector3" - }, - "range": { - "type": "number", - "description": "Distance from radius the prompt appears", - "format": "float", - "default": 2.0 - }, - "remoteTriggerRadius": { - "type": "number", - "description": "The radius of the remote trigger volume.", - "format": "float" - }, - "xmlFile": { - "type": "string", - "description": "Relative path to the xml file defining the dialogue." - } - } - }, - "EntryLocationInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "cloaked": { - "type": "boolean", - "description": "Whether this location is cloaked" - }, - "id": { - "type": "string", - "description": "ID of the entry this location relates to" - }, - "position": { - "description": "The position of this entry location", - "$ref": "#/definitions/MVector3" - } - } - }, - "GeyserInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "position": { - "description": "Position of the geyser", - "$ref": "#/definitions/MVector3" - }, - "disableBubbles": { - "type": "boolean", - "description": "Disable the individual particle systems of the geyser" - }, - "disableShaft": { - "type": "boolean", - "description": "Disable the individual particle systems of the geyser" - }, - "disableSpout": { - "type": "boolean", - "description": "Disable the individual particle systems of the geyser" - } - } - }, - "NomaiTextInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "arcInfo": { - "type": "array", - "description": "Additional information about each arc in the text", - "items": { - "$ref": "#/definitions/NomaiTextArcInfo" - } - }, - "normal": { - "description": "The normal vector for this object. Used for writing on walls and positioning computers.", - "$ref": "#/definitions/MVector3" - }, - "position": { - "description": "Position of the root of this text", - "$ref": "#/definitions/MVector3" - }, - "rotation": { - "description": "The euler angle rotation of this object. Not required if setting the normal. Computers and cairns will orient\nthemselves to the surface of the planet automatically.", - "$ref": "#/definitions/MVector3" - }, - "seed": { - "type": "integer", - "description": "The random seed used to pick what the text arcs will look like.", - "format": "int32" - }, - "type": { - "description": "The type of object this is.", - "default": "wall", - "$ref": "#/definitions/NomaiTextType" - }, - "xmlFile": { - "type": "string", - "description": "The relative path to the xml file for this 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" - } - } - }, - "NomaiTextArcInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "mirror": { - "type": "boolean", - "description": "Whether to flip the spiral from left-curling to right-curling or vice versa." - }, - "position": { - "description": "The local position of this object on the wall.", - "$ref": "#/definitions/MVector2" - }, - "type": { - "description": "The type of text to display.", - "default": "adult", - "$ref": "#/definitions/NomaiTextArcType" - }, - "variation": { - "type": "integer", - "description": "Which variation of the chosen type to place. If not specified, a random variation will be selected based on the seed provided in the parent module.", - "format": "int32", - "default": -1 - }, - "zRotation": { - "type": "number", - "description": "The z euler angle for this arc.", - "format": "float", - "maximum": 360.0, - "minimum": 0.0 - } - } - }, - "MVector2": { - "type": "object", - "additionalProperties": false, - "properties": { - "x": { - "type": "number", - "format": "float" - }, - "y": { - "type": "number", - "format": "float" - } - } - }, - "NomaiTextArcType": { - "type": "string", - "description": "", - "x-enumNames": [ - "Adult", - "Child", - "Stranger" - ], - "enum": [ - "adult", - "child", - "stranger" - ] - }, - "NomaiTextType": { - "type": "string", - "description": "", - "x-enumNames": [ - "Wall", - "Scroll", - "Computer", - "Cairn", - "Recorder", - "PreCrashRecorder", - "PreCrashComputer" - ], - "enum": [ - "wall", - "scroll", - "computer", - "cairn", - "recorder", - "preCrashRecorder", - "preCrashComputer" - ] - }, - "RaftInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "position": { - "description": "Position of the raft", - "$ref": "#/definitions/MVector3" - } - } - }, - "RevealInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "maxAngle": { - "type": "number", - "description": "The max view angle (in degrees) the player can see the volume with to unlock the fact (`observe` only)", - "format": "float" - }, - "maxDistance": { - "type": "number", - "description": "The max distance the user can be away from the volume to reveal the fact (`snapshot` and `observe` only)", - "format": "float" - }, - "position": { - "description": "The position to place this volume at", - "$ref": "#/definitions/MVector3" - }, - "radius": { - "type": "number", - "description": "The radius of this reveal volume", - "format": "float" - }, - "revealOn": { - "description": "What needs to be done to the volume to unlock the facts", - "default": "enter", - "$ref": "#/definitions/RevealVolumeType" - }, - "reveals": { - "type": "array", - "description": "A list of facts to reveal", - "items": { - "type": "string" - } - }, - "achievementID": { - "type": "string", - "description": "An achievement to unlock. Optional." - } - } - }, - "RevealVolumeType": { - "type": "string", - "description": "", - "x-enumNames": [ - "Enter", - "Observe", - "Snapshot" - ], - "enum": [ - "enter", - "observe", - "snapshot" - ] - }, - "ScatterInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "assetBundle": { - "type": "string", - "description": "Relative filepath to an asset-bundle" - }, - "count": { - "type": "integer", - "description": "Number of props to scatter", - "format": "int32" - }, - "offset": { - "description": "Offset this prop once it is placed", - "$ref": "#/definitions/MVector3" - }, - "path": { - "type": "string", - "description": "Either the path in the scene hierarchy of the item to copy or the path to the object in the supplied asset bundle" - }, - "rotation": { - "description": "Rotate this prop once it is placed", - "$ref": "#/definitions/MVector3" - }, - "scale": { - "type": "number", - "description": "Scale this prop once it is placed", - "format": "float" - }, - "seed": { - "type": "integer", - "description": "The number used as entropy for scattering the props", - "format": "int32" - }, - "minHeight": { - "type": [ - "null", - "number" - ], - "description": "The lowest height that these object will be placed at (only relevant if there's a heightmap)", - "format": "float" - }, - "maxHeight": { - "type": [ - "null", - "number" - ], - "description": "The highest height that these objects will be placed at (only relevant if there's a heightmap)", - "format": "float" - } - } - }, - "ProjectionInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "position": { - "description": "The position of this slideshow.", - "$ref": "#/definitions/MVector3" - }, - "reveals": { - "type": "array", - "description": "The ship log entries revealed after finishing this slide reel.", - "items": { - "type": "string" - } - }, - "rotation": { - "description": "The rotation of this slideshow.", - "$ref": "#/definitions/MVector3" - }, - "slides": { - "type": "array", - "description": "The list of slides for this object.", - "items": { - "$ref": "#/definitions/SlideInfo" - } - }, - "type": { - "description": "The type of object this is.", - "default": "slideReel", - "$ref": "#/definitions/SlideShowType" - }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this slideshow. Optional (will default to the root sector)." - } - } - }, - "SlideInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "ambientLightColor": { - "description": "Ambient light colour when viewing this slide.", - "$ref": "#/definitions/MColor" - }, - "ambientLightIntensity": { - "type": "number", - "description": "Ambient light intensity when viewing this slide.", - "format": "float" - }, - "ambientLightRange": { - "type": "number", - "description": "Ambient light range when viewing this slide.", - "format": "float" - }, - "backdropAudio": { - "type": "string", - "description": "The name of the AudioClip that will continuously play while watching these slides" - }, - "backdropFadeTime": { - "type": "number", - "description": "The time to fade into the backdrop audio", - "format": "float" - }, - "beatAudio": { - "type": "string", - "description": "The name of the AudioClip for a one-shot sound when opening the slide." - }, - "beatDelay": { - "type": "number", - "description": "The time delay until the one-shot audio", - "format": "float" - }, - "blackFrameDuration": { - "type": "number", - "description": "Before viewing this slide, there will be a black frame for this many seconds.", - "format": "float" - }, - "imagePath": { - "type": "string", - "description": "The path to the image file for this slide." - }, - "playTimeDuration": { - "type": "number", - "description": "Play-time duration for auto-projector slides.", - "format": "float" - }, - "reveal": { - "type": "string", - "description": "Ship log entry revealed when viewing this slide" - }, - "spotIntensityMod": { - "type": "number", - "description": "Spotlight intensity modifier when viewing this slide.", - "format": "float" - } - } - }, - "SlideShowType": { - "type": "string", - "description": "", - "x-enumNames": [ - "SlideReel", - "AutoProjector", - "VisionTorchTarget", - "StandingVisionTorch" - ], - "enum": [ - "slideReel", - "autoProjector", - "visionTorchTarget", - "standingVisionTorch" - ] - }, - "QuantumGroupInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "type": { - "description": "What type of group this is: does it define a list of states a single quantum object could take or a list of sockets one or more quantum objects could share?", - "$ref": "#/definitions/QuantumGroupType" - }, - "id": { - "type": "string", - "description": "A unique string used by props (that are marked as quantum) use to refer back to this group" - }, - "sockets": { - "type": "array", - "description": "Only required if type is `sockets`. This lists all the possible locations for any props assigned to this group.", - "items": { - "$ref": "#/definitions/QuantumSocketInfo" - } - }, - "hasEmptyState": { - "type": "boolean", - "description": "Optional. Only used if type is `states`. If this is true, then the first prop made part of this group will be used to construct a visibility box for an empty game object, which will be considered one of the states." - }, - "sequential": { - "type": "boolean", - "description": "Optional. Only used if type is `states`. If this is true, then the states will be presented in order, rather than in a random order" - }, - "loop": { - "type": "boolean", - "description": "Optional. Only used if type is `states` and `sequential` is true. If this is false, then after the last state has appeared, the object will no longer change state", - "default": true - } - } - }, - "QuantumGroupType": { - "type": "string", - "description": "", - "x-enumNames": [ - "Sockets", - "States", - "FailedValidation" - ], - "enum": [ - "sockets", - "states", - "FailedValidation" - ] - }, - "QuantumSocketInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "position": { - "description": "The location of this socket", - "$ref": "#/definitions/MVector3" - }, - "rotation": { - "description": "The rotation the quantum object will take if it's occupying this socket", - "$ref": "#/definitions/MVector3" - }, - "probability": { - "type": "number", - "description": "The probability any props that are part of this group will occupy this socket", - "format": "float", - "default": 1.0 - } - } - }, - "TornadoInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "elevation": { - "type": "number", - "description": "Alternative to setting the position. Will choose a random place at this elevation.", - "format": "float" - }, - "height": { - "type": "number", - "description": "The height of this tornado.", - "format": "float", - "default": 30.0 - }, - "position": { - "description": "Position of the tornado", - "$ref": "#/definitions/MVector3" - }, - "tint": { - "description": "The colour of the tornado.", - "$ref": "#/definitions/MColor" - }, - "type": { - "description": "What type of cyclone should this be? Upwards and downwards are both tornados and will push in that direction.", - "default": "downwards", - "$ref": "#/definitions/TornadoType" - }, - "wanderDegreesX": { - "type": "number", - "description": "Angular distance from the starting position that it will wander, in terms of the angle around the x-axis.", - "format": "float", - "default": 45.0 - }, - "wanderDegreesZ": { - "type": "number", - "description": "Angular distance from the starting position that it will wander, in terms of the angle around the z-axis.", - "format": "float", - "default": 45.0 - }, - "wanderRate": { - "type": "number", - "description": "The rate at which the tornado will wander around the planet. Set to 0 for it to be stationary. Should be around\n0.1.", - "format": "float" - }, - "audioDistance": { - "type": "number", - "description": "The maximum distance at which you'll hear the sounds of the cyclone. If not set it will scale relative to the size of the cyclone.", - "format": "float" - } - } - }, - "TornadoType": { - "type": "string", - "description": "", - "x-enumNames": [ - "Downwards", - "Upwards", - "Hurricane" - ], - "enum": [ - "downwards", - "upwards", - "hurricane" - ] - }, - "VolcanoInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "lavaTint": { - "description": "The colour of the meteor's lava.", - "$ref": "#/definitions/MColor" - }, - "maxInterval": { - "type": "number", - "description": "Maximum time between meteor launches.", - "format": "float", - "default": 20.0 - }, - "maxLaunchSpeed": { - "type": "number", - "description": "Maximum random speed at which meteors are launched.", - "format": "float", - "default": 150.0 - }, - "minInterval": { - "type": "number", - "description": "Minimum time between meteor launches.", - "format": "float", - "default": 5.0 - }, - "minLaunchSpeed": { - "type": "number", - "description": "Minimum random speed at which meteors are launched.", - "format": "float", - "default": 50.0 - }, - "position": { - "description": "Position of this volcano.", - "$ref": "#/definitions/MVector3" - }, - "scale": { - "type": "number", - "description": "Scale of the meteors.", - "format": "float" - }, - "stoneTint": { - "description": "The colour of the meteor's stone.", - "$ref": "#/definitions/MColor" - } - } - }, - "SingularityModule": { - "type": "object", - "additionalProperties": false, - "properties": { - "curve": { - "type": "array", - "description": "Scale this module over time", - "items": { - "$ref": "#/definitions/TimeValuePair" - } - }, - "makeZeroGVolume": { - "type": "boolean", - "description": "Only for White Holes. Should this white hole repel the player from it.", - "default": true - }, - "pairedSingularity": { - "type": "string", - "description": "The uniqueID of the white hole or black hole that is paired to this one. If you don't set a value, entering will kill\nthe player" - }, - "uniqueID": { - "type": "string", - "description": "The uniqueID of this white hole or black hole. If not set it will default to the name of the planet" - }, - "position": { - "description": "Position of the singularity", - "$ref": "#/definitions/MVector3" - }, - "size": { - "type": "number", - "description": "Radius of the singularity. Note that this isn't the same as the event horizon, but includes the entire volume that\nhas warped effects in it.", - "format": "float", - "minimum": 0.0 - }, - "targetStarSystem": { - "type": "string", - "description": "If you want a black hole to load a new star system scene, put its name here." - }, - "type": { - "description": "Type of singularity (white hole or black hole)", - "$ref": "#/definitions/SingularityType" - } - } - }, - "SingularityType": { - "type": "string", - "description": "", - "x-enumNames": [ - "BlackHole", - "WhiteHole" - ], - "enum": [ - "blackHole", - "whiteHole" - ] - }, - "AudioVolumeInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "position": { - "description": "The location of this audio volume. Optional (will default to 0,0,0).", - "$ref": "#/definitions/MVector3" - }, - "radius": { - "type": "number", - "description": "The radius of this audio volume", - "format": "float" - }, - "audio": { - "type": "string", - "description": "The audio to use. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." - }, - "track": { - "description": "The audio track of this audio volume", - "default": "environment", - "$ref": "#/definitions/AudioMixerTrackName" - } - } - }, - "AudioMixerTrackName": { - "type": "string", - "description": "", - "x-enumNames": [ - "Undefined", - "Menu", - "Music", - "Environment", - "Environment_Unfiltered", - "EndTimes_SFX", - "Signal", - "Death", - "Player", - "Player_External", - "Ship", - "Map", - "EndTimes_Music", - "MuffleWhileRafting", - "MuffleIndoors", - "SlideReelMusic" - ], - "enum": [ - "undefined", - "menu", - "music", - "environment", - "environmentUnfiltered", - "endTimesSfx", - "signal", - "death", - "player", - "playerExternal", - "ship", - "map", - "endTimesMusic", - "muffleWhileRafting", - "muffleIndoors", - "slideReelMusic" - ] - }, - "SignalInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "audio": { - "type": "string", - "description": "The audio to use. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." - }, - "detectionRadius": { - "type": "number", - "description": "How close the player must get to the signal to detect it. This is when you get the \"Unknown Signal Detected\"\nnotification.", - "format": "float", - "minimum": 0.0 - }, - "frequency": { - "type": "string", - "description": "The frequency ID of the signal. The built-in game values are `Default`, `Traveler`, `Quantum`, `EscapePod`,\n`Statue`, `WarpCore`, `HideAndSeek`, and `Radio`. You can also put a custom value." - }, - "identificationRadius": { - "type": "number", - "description": "How close the player must get to the signal to identify it. This is when you learn its name.", - "format": "float", - "default": 10.0, - "minimum": 0.0 - }, - "insideCloak": { - "type": "boolean", - "description": "Only set to `true` if you are putting this signal inside a cloaking field." - }, - "name": { - "type": "string", - "description": "The unique ID of the signal." - }, - "onlyAudibleToScope": { - "type": "boolean", - "description": "`false` if the player can hear the signal without equipping the signal-scope.", - "default": true - }, - "position": { - "description": "Position of the signal's source", - "$ref": "#/definitions/MVector3" - }, - "reveals": { - "type": "string", - "description": "A ship log fact to reveal when the signal is identified.", - "default": "" - }, - "sourceRadius": { - "type": "number", - "description": "Radius of the sphere giving off the signal.", - "format": "float", - "default": 1.0 - }, - "parentPath": { - "type": "string", - "description": "The relative path from the planet to the parent of this signal. Optional (will default to the root sector)." - } - } - }, - "ReferenceFrameModule": { - "type": "object", - "additionalProperties": false, - "properties": { - "enabled": { - "type": "boolean", - "description": "Allows the object to be targeted.", - "default": true - }, - "hideInMap": { - "type": "boolean", - "description": "Stop the object from being targeted on the map." - }, - "bracketRadius": { - "type": "number", - "description": "Radius of the brackets that show up when you target this. Defaults to the sphere of influence.", - "format": "float", - "default": -1 - }, - "targetWhenClose": { - "type": "boolean", - "description": "If it should be targetable even when super close." - }, - "maxTargetDistance": { - "type": "number", - "description": "The maximum distance that the reference frame can be targeted from. Defaults to 100km and cannot be greater than that.", - "format": "float" - }, - "targetColliderRadius": { - "type": "number", - "description": "The radius of the sphere around the planet which you can click on to target it. Defaults to twice the sphere of influence.", - "format": "float" - }, - "localPosition": { - "description": "Position of the reference frame relative to the object.", - "$ref": "#/definitions/MVector3" - } - } - }, - "RingModule": { - "type": "object", - "additionalProperties": false, - "properties": { - "curve": { - "type": "array", - "description": "Scale this module over time", - "items": { - "$ref": "#/definitions/TimeValuePair" - } - }, - "fluidType": { - "description": "Fluid type for sounds/effects when colliding with this ring.", - "$ref": "#/definitions/CloudFluidType" - }, - "inclination": { - "type": "number", - "description": "Angle between the rings and the equatorial plane of the planet.", - "format": "float" - }, - "innerRadius": { - "type": "number", - "description": "Inner radius of the disk", - "format": "float", - "minimum": 0.0 - }, - "longitudeOfAscendingNode": { - "type": "number", - "description": "Angle defining the point where the rings rise up from the planet's equatorial plane if inclination is nonzero.", - "format": "float" - }, - "outerRadius": { - "type": "number", - "description": "Outer radius of the disk", - "format": "float", - "minimum": 0.0 - }, - "rotationSpeed": { - "type": "number", - "description": "Allows the rings to rotate.", - "format": "float" - }, - "texture": { - "type": "string", - "description": "Relative filepath to the texture used for the rings." - }, - "unlit": { - "type": "boolean", - "description": "Should this ring be unlit?" - } - } - }, - "SandModule": { - "type": "object", - "additionalProperties": false, - "properties": { - "curve": { - "type": "array", - "description": "Scale this module over time", - "items": { - "$ref": "#/definitions/TimeValuePair" - } - }, - "size": { - "type": "number", - "description": "Size of the sand", - "format": "float" - }, - "tint": { - "description": "Tint of the sand", - "$ref": "#/definitions/MColor" - } - } - }, - "ShipLogModule": { - "type": "object", - "additionalProperties": false, - "properties": { - "mapMode": { - "description": "Describe what this planet looks and like in map mode", - "$ref": "#/definitions/MapModeInfo" - }, - "spriteFolder": { - "type": "string", - "description": "A path to the folder where entry sprites are stored." - }, - "xmlFile": { - "type": "string", - "description": "The relative path to the xml file to load ship log entries from." - } - } - }, - "MapModeInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "details": { - "type": "array", - "description": "Place non-selectable objects in map mode (like sand funnels).", - "items": { - "$ref": "#/definitions/ShipLogDetailInfo" - } - }, - "invisibleWhenHidden": { - "type": "boolean", - "description": "Hide the planet completely if unexplored instead of showing an outline." - }, - "manualNavigationPosition": { - "description": "Specify where this planet is in terms of navigation.", - "$ref": "#/definitions/MVector2" - }, - "manualPosition": { - "description": "Manually place this planet at the specified position.", - "$ref": "#/definitions/MVector2" - }, - "offset": { - "type": "number", - "description": "Extra distance to apply to this object in map mode.", - "format": "float" - }, - "outlineSprite": { - "type": "string", - "description": "The path to the sprite to show when the planet is unexplored in map mode." - }, - "remove": { - "type": "boolean", - "description": "Completely remove this planet (and it's children) from map mode." - }, - "revealedSprite": { - "type": "string", - "description": "The path to the sprite to show when the planet is revealed in map mode." - }, - "scale": { - "type": "number", - "description": "Scale to apply to the planet in map mode.", - "format": "float", - "default": 1.0 - } - } - }, - "ShipLogDetailInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "invisibleWhenHidden": { - "type": "boolean", - "description": "Whether to completely hide this detail when the parent AstroBody is unexplored." - }, - "outlineSprite": { - "type": "string", - "description": "The sprite to show when the parent AstroBody is rumored/unexplored." - }, - "position": { - "description": "The position (relative to the parent) to place the detail.", - "$ref": "#/definitions/MVector2" - }, - "revealedSprite": { - "type": "string", - "description": "The sprite to show when the parent AstroBody is revealed." - }, - "rotation": { - "type": "number", - "description": "The angle in degrees to rotate the detail.", - "format": "float" - }, - "scale": { - "description": "The amount to scale the x and y-axis of the detail by.", - "$ref": "#/definitions/MVector2" - } - } - }, - "SpawnModule": { - "type": "object", - "additionalProperties": false, - "properties": { - "playerSpawnPoint": { - "description": "If you want the player to spawn on the new body, set a value for this. Press `P` in game with Debug mode on to have\nthe game log the position you're looking at to find a good value for this.", - "$ref": "#/definitions/MVector3" - }, - "playerSpawnRotation": { - "description": "Euler angles by which the player will be oriented.", - "$ref": "#/definitions/MVector3" - }, - "shipSpawnPoint": { - "description": "Required for the system to be accessible by warp drive.", - "$ref": "#/definitions/MVector3" - }, - "shipSpawnRotation": { - "description": "Euler angles by which the ship will be oriented.", - "$ref": "#/definitions/MVector3" - }, - "startWithSuit": { - "type": "boolean", - "description": "If you spawn on a planet with no oxygen, you probably want to set this to true ;;)" - } - } - }, - "StarModule": { - "type": "object", - "additionalProperties": false, - "properties": { - "curve": { - "type": "array", - "description": "Scale this module over time", - "items": { - "$ref": "#/definitions/TimeValuePair" - } - }, - "endTint": { - "description": "Colour of the star at the end of its life.", - "$ref": "#/definitions/MColor" - }, - "goSupernova": { - "type": "boolean", - "description": "Should this star explode after 22 minutes?", - "default": true - }, - "hasStarController": { - "type": "boolean", - "description": "Should we add a star controller to this body? If you want clouds to work on a binary brown dwarf system, set this to false.", - "default": true - }, - "hasAtmosphere": { - "type": "boolean", - "description": "The default sun has its own atmosphere that is different from regular planets. If you want that, set this to\n`true`.", - "default": true - }, - "lightTint": { - "description": "Colour of the light given off. Defaults to yellowish.", - "$ref": "#/definitions/MColor" - }, - "size": { - "type": "number", - "description": "Radius of the star.", - "format": "float", - "default": 2000.0, - "minimum": 0.0 - }, - "solarLuminosity": { - "type": "number", - "description": "Relative strength of the light compared to the sun.", - "format": "float", - "default": 1.0, - "minimum": 0.0 - }, - "supernovaTint": { - "description": "The tint of the supernova this star creates when it dies.", - "$ref": "#/definitions/MColor" - }, - "tint": { - "description": "Colour of the star.", - "$ref": "#/definitions/MColor" - }, - "starRampTexture": { - "type": "string", - "description": "Path to the texture to put as the star ramp. Optional." - }, - "starCollapseRampTexture": { - "type": "string", - "description": "Path to the texture to put as the star ramp while it is collapsing. Optional." - }, - "lightRadius": { - "type": "number", - "description": "How far the light from the star can reach.", - "format": "float", - "default": 50000.0, - "minimum": 0.0 - } - } - }, - "WaterModule": { - "type": "object", - "additionalProperties": false, - "properties": { - "curve": { - "type": "array", - "description": "Scale this module over time", - "items": { - "$ref": "#/definitions/TimeValuePair" - } - }, - "size": { - "type": "number", - "description": "Size of the water sphere", - "format": "float" - }, - "tint": { - "description": "Tint of the water", - "$ref": "#/definitions/MColor" - } - } - } - }, - "$docs": { - "title": "Celestial Body Schema", - "description": "Schema for a celestial body in New Horizons" - } +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Celestial Body Schema", + "type": "object", + "description": "Describes a body to generate", + "additionalProperties": false, + "required": [ + "name" + ], + "properties": { + "AsteroidBelt": { + "description": "Generate asteroids around this body", + "$ref": "#/definitions/AsteroidBeltModule" + }, + "Atmosphere": { + "description": "Describes this Body's atmosphere", + "$ref": "#/definitions/AtmosphereModule" + }, + "Base": { + "description": "Base Properties of this Body", + "$ref": "#/definitions/BaseModule" + }, + "Bramble": { + "description": "Add bramble nodes to this planet and/or make this planet a bramble dimension", + "$ref": "#/definitions/BrambleModule" + }, + "buildPriority": { + "type": "integer", + "description": "Set to a higher number if you wish for this body to be built sooner", + "format": "int32", + "default": -1 + }, + "canShowOnTitle": { + "type": "boolean", + "description": "Should this planet ever be shown on the title screen?" + }, + "Cloak": { + "description": "Add a cloaking field to this planet", + "$ref": "#/definitions/CloakModule" + }, + "destroy": { + "type": "boolean", + "description": "`true` if you want to delete this planet" + }, + "FocalPoint": { + "description": "Make this body into a focal point (barycenter)", + "$ref": "#/definitions/FocalPointModule" + }, + "Funnel": { + "description": "Add funnel from this planet to another", + "$ref": "#/definitions/FunnelModule" + }, + "HeightMap": { + "description": "Generate the surface of this planet using a heightmap", + "$ref": "#/definitions/HeightMapModule" + }, + "isQuantumState": { + "type": "boolean", + "description": "Does this config describe a quantum state of a custom planet defined in another file?" + }, + "Lava": { + "description": "Add lava to this planet", + "$ref": "#/definitions/LavaModule" + }, + "name": { + "type": "string", + "description": "Unique name of your planet", + "minLength": 1 + }, + "Orbit": { + "description": "Describes this Body's orbit (or lack there of)", + "$ref": "#/definitions/OrbitModule" + }, + "ProcGen": { + "description": "Procedural Generation", + "$ref": "#/definitions/ProcGenModule" + }, + "Props": { + "description": "Spawn various objects on this body", + "$ref": "#/definitions/PropModule" + }, + "ReferenceFrame": { + "description": "Reference frame properties of this body", + "$ref": "#/definitions/ReferenceFrameModule" + }, + "removeChildren": { + "type": "array", + "description": "A list of paths to child GameObjects to destroy on this planet", + "items": { + "type": "string" + } + }, + "Ring": { + "description": "Creates a ring around the planet", + "$ref": "#/definitions/RingModule" + }, + "Sand": { + "description": "Add sand to this planet", + "$ref": "#/definitions/SandModule" + }, + "ShipLog": { + "description": "Add ship log entries to this planet and describe how it looks in map mode", + "$ref": "#/definitions/ShipLogModule" + }, + "Spawn": { + "description": "Spawn the player at this planet", + "$ref": "#/definitions/SpawnModule" + }, + "Star": { + "description": "Make this body a star", + "$ref": "#/definitions/StarModule" + }, + "starSystem": { + "type": "string", + "description": "Unique star system containing your planet", + "default": "SolarSystem" + }, + "version": { + "type": "string", + "description": "Version of New Horizons this config is using (Doesn't do anything)" + }, + "Water": { + "description": "Add water to this planet", + "$ref": "#/definitions/WaterModule" + }, + "$schema": { + "type": "string", + "description": "The schema to validate with" + } + }, + "definitions": { + "AsteroidBeltModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "amount": { + "type": "integer", + "description": "Amount of asteroids to create.", + "format": "int32", + "default": -1, + "maximum": 200.0, + "minimum": -1.0 + }, + "inclination": { + "type": "number", + "description": "Angle between the rings and the equatorial plane of the planet.", + "format": "float" + }, + "innerRadius": { + "type": "number", + "description": "Lowest distance from the planet asteroids can spawn", + "format": "float", + "minimum": 0.0 + }, + "longitudeOfAscendingNode": { + "type": "number", + "description": "Angle defining the point where the rings rise up from the planet's equatorial plane if inclination is nonzero.", + "format": "float" + }, + "maxSize": { + "type": "number", + "description": "Maximum size of the asteroids.", + "format": "float", + "default": 50, + "minimum": 0.0 + }, + "minSize": { + "type": "number", + "description": "Minimum size of the asteroids.", + "format": "float", + "default": 20, + "minimum": 0.0 + }, + "outerRadius": { + "type": "number", + "description": "Greatest distance from the planet asteroids can spawn", + "format": "float", + "minimum": 0.0 + }, + "procGen": { + "description": "How the asteroids are generated", + "$ref": "#/definitions/ProcGenModule" + }, + "randomSeed": { + "type": "integer", + "description": "Number used to randomize asteroid positions", + "format": "int32" + } + } + }, + "ProcGenModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "color": { + "$ref": "#/definitions/MColor" + }, + "scale": { + "type": "number", + "format": "float", + "minimum": 0.0 + } + } + }, + "MColor": { + "type": "object", + "additionalProperties": false, + "properties": { + "r": { + "type": "integer", + "description": "The red component of this colour", + "format": "int32", + "maximum": 2147483647.0, + "minimum": 0.0 + }, + "g": { + "type": "integer", + "description": "The green component of this colour", + "format": "int32", + "maximum": 2147483647.0, + "minimum": 0.0 + }, + "b": { + "type": "integer", + "description": "The blue component of this colour", + "format": "int32", + "maximum": 2147483647.0, + "minimum": 0.0 + }, + "a": { + "type": "integer", + "description": "The alpha (opacity) component of this colour", + "format": "int32", + "default": 255, + "maximum": 255.0, + "minimum": 0.0 + } + } + }, + "AtmosphereModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "atmosphereTint": { + "description": "Colour of atmospheric shader on the planet.", + "$ref": "#/definitions/MColor" + }, + "atmosphereSunIntensity": { + "type": "number", + "description": "How intense should the sun appear in the sky.\nAlso affects general atmosphere brightness.\nDefault value of 1 matches Timber Hearth.\nIf not set, this will be determined based off the distance to the sun.", + "format": "float", + "minimum": 0.0 + }, + "clouds": { + "description": "Describes the clouds in the atmosphere", + "$ref": "#/definitions/CloudInfo" + }, + "fogDensity": { + "type": "number", + "description": "How dense the fog is, if you put fog.", + "format": "float", + "maximum": 1.0, + "minimum": 0.0 + }, + "fogSize": { + "type": "number", + "description": "Radius of fog sphere, independent of the atmosphere. This has to be set for there to be fog.", + "format": "float", + "minimum": 0.0 + }, + "fogTint": { + "description": "Colour of fog on the planet, if you put fog.", + "$ref": "#/definitions/MColor" + }, + "hasOxygen": { + "type": "boolean", + "description": "Lets you survive on the planet without a suit." + }, + "hasRain": { + "type": "boolean", + "description": "Does this planet have rain?" + }, + "hasSnow": { + "type": "boolean", + "description": "Does this planet have snow?" + }, + "size": { + "type": "number", + "description": "Scale height of the atmosphere", + "format": "float" + }, + "useAtmosphereShader": { + "type": "boolean", + "description": "Whether we use an atmospheric shader on the planet. Doesn't affect clouds, fog, rain, snow, oxygen, etc. Purely\nvisual." + }, + "minShockSpeed": { + "type": "number", + "description": "Minimum speed that your ship can go in the atmosphere where flames will appear.", + "format": "float", + "default": 100.0 + }, + "maxShockSpeed": { + "type": "number", + "description": "Maximum speed that your ship can go in the atmosphere where flames will appear at their brightest.", + "format": "float", + "default": 300.0 + } + } + }, + "CloudInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "cloudsPrefab": { + "description": "Should these clouds be based on Giant's Deep's banded clouds, or the Quantum Moon's non-banded clouds?", + "$ref": "#/definitions/CloudPrefabType" + }, + "capPath": { + "type": "string", + "description": "Relative filepath to the cloud cap texture, if the planet has clouds." + }, + "fluidType": { + "description": "Fluid type for sounds/effects when colliding with this cloud.", + "default": "cloud", + "$ref": "#/definitions/CloudFluidType" + }, + "hasLightning": { + "type": "boolean", + "description": "Add lightning to this planet like on Giant's Deep." + }, + "innerCloudRadius": { + "type": "number", + "description": "Radius from the center to the inner layer of the clouds.", + "format": "float" + }, + "lightningGradient": { + "type": "array", + "description": "Colour gradient of the lightning, time is in seconds.", + "items": { + "$ref": "#/definitions/MGradient" + } + }, + "outerCloudRadius": { + "type": "number", + "description": "Radius from the center to the outer layer of the clouds.", + "format": "float" + }, + "rampPath": { + "type": "string", + "description": "Relative filepath to the cloud ramp texture, if the planet has clouds. If you don't put anything here it will be\nauto-generated." + }, + "texturePath": { + "type": "string", + "description": "Relative filepath to the cloud texture, if the planet has clouds." + }, + "tint": { + "description": "Colour of the inner cloud layer.", + "$ref": "#/definitions/MColor" + }, + "unlit": { + "type": "boolean", + "description": "If the top layer shouldn't have shadows. Set to true if you're making a brown dwarf for example." + } + } + }, + "CloudPrefabType": { + "type": "string", + "description": "", + "x-enumNames": [ + "GiantsDeep", + "QuantumMoon", + "Basic" + ], + "enum": [ + "giantsDeep", + "quantumMoon", + "basic" + ] + }, + "CloudFluidType": { + "type": "string", + "description": "", + "x-enumNames": [ + "None", + "Water", + "Cloud", + "Sand", + "Plasma" + ], + "enum": [ + "none", + "water", + "cloud", + "sand", + "plasma" + ] + }, + "MGradient": { + "type": "object", + "additionalProperties": false, + "properties": { + "time": { + "type": "number", + "format": "float" + }, + "tint": { + "$ref": "#/definitions/MColor" + } + } + }, + "BaseModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "ambientLight": { + "type": "number", + "description": "The intensity of light the dark side of the body should have. Timber Hearth has `1.4` for reference", + "format": "float" + }, + "centerOfSolarSystem": { + "type": "boolean", + "description": "Set this to true if you are replacing the sun with a different body. Only one object in a star system should ever\nhave this set to true." + }, + "cometTailRotation": { + "description": "If it has a comet tail, it'll be oriented according to these Euler angles.", + "$ref": "#/definitions/MVector3" + }, + "gravityFallOff": { + "description": "How gravity falls off with distance. Most planets use linear but the sun and some moons use inverseSquared.", + "default": "linear", + "$ref": "#/definitions/GravityFallOff" + }, + "groundSize": { + "type": "number", + "description": "Radius of a simple sphere used as the ground for the planet. If you want to use more complex terrain, leave this as\n0.", + "format": "float" + }, + "hasCometTail": { + "type": "boolean", + "description": "If you want the body to have a tail like the Interloper." + }, + "hasMapMarker": { + "type": "boolean", + "description": "If the body should have a marker on the map screen." + }, + "invulnerableToSun": { + "type": "boolean", + "description": "Can this planet survive entering a star?" + }, + "showMinimap": { + "type": "boolean", + "description": "Do we show the minimap when walking around this planet?", + "default": true + }, + "soiOverride": { + "type": "number", + "description": "An override for the radius of the planet's gravitational sphere of influence. Optional", + "format": "float" + }, + "surfaceGravity": { + "type": "number", + "description": "The acceleration due to gravity felt as the surfaceSize. Timber Hearth has 12 for reference", + "format": "float" + }, + "surfaceSize": { + "type": "number", + "description": "A scale height used for a number of things. Should be the approximate radius of the body.", + "format": "float" + }, + "zeroGravityRadius": { + "type": "number", + "description": "Radius of the zero gravity volume. This will make it so no gravity from any planet will affect you. Useful for satellites.", + "format": "float" + } + } + }, + "MVector3": { + "type": "object", + "additionalProperties": false, + "properties": { + "x": { + "type": "number", + "format": "float" + }, + "y": { + "type": "number", + "format": "float" + }, + "z": { + "type": "number", + "format": "float" + } + } + }, + "GravityFallOff": { + "type": "string", + "description": "", + "x-enumNames": [ + "Linear", + "InverseSquared" + ], + "enum": [ + "linear", + "inverseSquared" + ] + }, + "BrambleModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "dimension": { + "description": "Defining this value will make this body a bramble dimension. Leave it null to not do that.", + "$ref": "#/definitions/BrambleDimensionInfo" + }, + "nodes": { + "type": "array", + "description": "Place nodes/seeds that take you to other bramble dimensions", + "items": { + "$ref": "#/definitions/BrambleNodeInfo" + } + } + } + }, + "BrambleDimensionInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "fogTint": { + "description": "The color of the fog inside this dimension. Leave blank for the default yellowish color: (113, 107, 81)", + "$ref": "#/definitions/MColor" + }, + "linksTo": { + "type": "string", + "description": "The name of the *node* that the player is taken to when exiting this dimension." + }, + "radius": { + "type": "number", + "description": "The internal radius (in meters) of the dimension. The default is 1705.", + "format": "float", + "default": 1705.0 + }, + "allowedEntrances": { + "type": "array", + "description": "An array of integers from 0-5. By default, all entrances are allowed. To force this dimension to warp players in from only one point (like the anglerfish nest dimension in the base game) set this value to [3], [5], or similar. Values of 0-5 only.", + "items": { + "type": "integer", + "format": "int32" + } + } + } + }, + "BrambleNodeInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "position": { + "description": "The physical position of the node", + "$ref": "#/definitions/MVector3" + }, + "rotation": { + "description": "The physical rotation of the node", + "$ref": "#/definitions/MVector3" + }, + "scale": { + "type": "number", + "description": "The physical scale of the node, as a multiplier of the original size", + "format": "float", + "default": 1.0 + }, + "linksTo": { + "type": "string", + "description": "The name of the planet that hosts the dimension this node links to" + }, + "name": { + "type": "string", + "description": "The name of this node. Only required if this node should serve as an exit." + }, + "isSeed": { + "type": "boolean", + "description": "Set this to true to make this node a seed instead of a node the player can enter", + "default": false + }, + "fogTint": { + "description": "The color of the fog inside the node. Leave blank for the default yellowish color: (131, 124, 105, 255)", + "$ref": "#/definitions/MColor" + }, + "lightTint": { + "description": "The color of the shafts of light coming from the entrances to the node. Leave blank for the default yellowish color: (131, 124, 105, 255)", + "$ref": "#/definitions/MColor" + }, + "farFogTint": { + "description": "The color a dimension's background fog turns when you approach this node (if it's in a dimension). If this node is not in a dimension, this does nothing. Leave blank for the default yellowish white color: (255, 245, 217, 255)", + "$ref": "#/definitions/MColor" + }, + "possibleExits": { + "type": "array", + "description": "An array of integers from 0-5. By default, all exits are allowed. To force this node to warp players out from only one hole set this value to [3], [5], or similar. Values of 0-5 only.", + "items": { + "type": "integer", + "format": "int32" + } + } + } + }, + "CloakModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "radius": { + "type": "number", + "description": "Radius of the cloaking field around the planet. It's a bit finicky so experiment with different values. If you\ndon't want a cloak, leave this as 0.", + "format": "float" + }, + "audio": { + "type": "string", + "description": "The audio that will play when entering the cloaking field. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." + } + } + }, + "FocalPointModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "primary": { + "type": "string", + "description": "Name of the primary planet in this binary system" + }, + "secondary": { + "type": "string", + "description": "Name of the secondary planet in this binary system" + } + } + }, + "FunnelModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "curve": { + "type": "array", + "description": "Scale this module over time", + "items": { + "$ref": "#/definitions/TimeValuePair" + } + }, + "target": { + "type": "string", + "description": "The planet the funnel will flow to" + }, + "tint": { + "description": "Tint of the funnel", + "$ref": "#/definitions/MColor" + }, + "type": { + "description": "Type of fluid the funnel transfers", + "default": "sand", + "$ref": "#/definitions/FunnelType" + } + } + }, + "TimeValuePair": { + "type": "object", + "additionalProperties": false, + "properties": { + "time": { + "type": "number", + "description": "A specific point in time", + "format": "float" + }, + "value": { + "type": "number", + "description": "The value for this point in time", + "format": "float" + } + } + }, + "FunnelType": { + "type": "string", + "description": "", + "x-enumNames": [ + "Sand", + "Water", + "Lava", + "Star" + ], + "enum": [ + "sand", + "water", + "lava", + "star" + ] + }, + "HeightMapModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "heightMap": { + "type": "string", + "description": "Relative filepath to the texture used for the terrain height." + }, + "maxHeight": { + "type": "number", + "description": "The highest points on your planet will be at this height.", + "format": "float", + "minimum": 0.0 + }, + "minHeight": { + "type": "number", + "description": "The lowest points on your planet will be at this height.", + "format": "float", + "minimum": 0.0 + }, + "stretch": { + "description": "The scale of the terrain.", + "$ref": "#/definitions/MVector3" + }, + "textureMap": { + "type": "string", + "description": "Relative filepath to the texture used for the terrain." + }, + "resolution": { + "type": "integer", + "description": "Resolution of the heightmap.\nHigher values means more detail but also more memory/cpu/gpu usage.\nThis value will be 1:1 with the heightmap texture width, but only at the equator.", + "format": "int32", + "default": 204, + "maximum": 2000.0, + "minimum": 4.0 + } + } + }, + "LavaModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "curve": { + "type": "array", + "description": "Scale this module over time", + "items": { + "$ref": "#/definitions/TimeValuePair" + } + }, + "size": { + "type": "number", + "description": "Size of the lava sphere", + "format": "float" + }, + "tint": { + "description": "Tint of the lava", + "$ref": "#/definitions/MColor" + } + } + }, + "OrbitModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "staticPosition": { + "description": "Specify this if you want the body to remain stationary at a given location (ie not orbit its parent). Required for Bramble dimensions", + "$ref": "#/definitions/MVector3" + }, + "primaryBody": { + "type": "string", + "description": "The name of the body this one will orbit around" + }, + "isMoon": { + "type": "boolean", + "description": "Is this the moon of a planet? Used for determining when its name is shown on the map." + }, + "axialTilt": { + "type": "number", + "description": "The angle between the normal to the orbital plane and its axis of rotation.", + "format": "float" + }, + "siderealPeriod": { + "type": "number", + "description": "Rotation period in minutes.", + "format": "float" + }, + "isTidallyLocked": { + "type": "boolean", + "description": "Should the body always have one side facing its primary?" + }, + "alignmentAxis": { + "description": "If it is tidally locked, this direction will face towards the primary. Ex: Interloper uses `0, -1, 0`. Most planets\nwill want something like `-1, 0, 0`.", + "$ref": "#/definitions/MVector3" + }, + "showOrbitLine": { + "type": "boolean", + "description": "Referring to the orbit line in the map screen.", + "default": true + }, + "dottedOrbitLine": { + "type": "boolean", + "description": "Should the orbit line be dotted?" + }, + "isStatic": { + "type": "boolean", + "description": "Is the body meant to stay in one place without moving? If staticPosition is not set, the initial position\nwill be determined using its orbital parameters." + }, + "tint": { + "description": "Colour of the orbit-line in the map view.", + "$ref": "#/definitions/MColor" + }, + "trackingOrbitLine": { + "type": "boolean", + "description": "Should we just draw a line behind its orbit instead of the entire circle/ellipse?" + }, + "semiMajorAxis": { + "type": "number", + "description": "The semi-major axis of the ellipse that is the body's orbit. For a circular orbit this is the radius.", + "format": "float", + "default": 5000.0, + "minimum": 0.0 + }, + "inclination": { + "type": "number", + "description": "The angle (in degrees) between the body's orbit and the plane of the star system", + "format": "float" + }, + "longitudeOfAscendingNode": { + "type": "number", + "description": "An angle (in degrees) defining the point where the orbit of the body rises above the orbital plane if it has\nnonzero inclination.", + "format": "float" + }, + "eccentricity": { + "type": "number", + "description": "At 0 the orbit is a circle. The closer to 1 it is, the more oval-shaped the orbit is.", + "format": "float", + "maximum": 1.0, + "minimum": 0.0 + }, + "argumentOfPeriapsis": { + "type": "number", + "description": "An angle (in degrees) defining the location of the periapsis (the closest distance to it's primary body) if it has\nnonzero eccentricity.", + "format": "float" + }, + "trueAnomaly": { + "type": "number", + "description": "Where the planet should start off in its orbit in terms of the central angle.", + "format": "float" + } + } + }, + "PropModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "details": { + "type": "array", + "description": "Place props in predefined positions on the planet", + "items": { + "$ref": "#/definitions/DetailInfo" + } + }, + "dialogue": { + "type": "array", + "description": "Add dialogue triggers to this planet", + "items": { + "$ref": "#/definitions/DialogueInfo" + } + }, + "entryLocation": { + "type": "array", + "description": "Add ship log entry locations on this planet", + "items": { + "$ref": "#/definitions/EntryLocationInfo" + } + }, + "geysers": { + "type": "array", + "description": "Add Geysers to this planet", + "items": { + "$ref": "#/definitions/GeyserInfo" + } + }, + "nomaiText": { + "type": "array", + "description": "Add translatable text to this planet", + "items": { + "$ref": "#/definitions/NomaiTextInfo" + } + }, + "proxyDetails": { + "type": "array", + "description": "Details which will be shown from 50km away. Meant to be lower resolution.", + "items": { + "$ref": "#/definitions/DetailInfo" + } + }, + "rafts": { + "type": "array", + "description": "Add rafts to this planet", + "items": { + "$ref": "#/definitions/RaftInfo" + } + }, + "reveal": { + "type": "array", + "description": "Add triggers that reveal parts of the ship log on this planet", + "items": { + "$ref": "#/definitions/RevealInfo" + } + }, + "scatter": { + "type": "array", + "description": "Scatter props around this planet's surface", + "items": { + "$ref": "#/definitions/ScatterInfo" + } + }, + "slideShows": { + "type": "array", + "description": "Add slideshows (from the DLC) to the planet", + "items": { + "$ref": "#/definitions/ProjectionInfo" + } + }, + "quantumGroups": { + "type": "array", + "description": "A list of quantum groups that props can be added to. An example of a group would be a list of possible locations for a QuantumSocketedObject.", + "items": { + "$ref": "#/definitions/QuantumGroupInfo" + } + }, + "tornados": { + "type": "array", + "description": "Add tornadoes to this planet", + "items": { + "$ref": "#/definitions/TornadoInfo" + } + }, + "volcanoes": { + "type": "array", + "description": "Add volcanoes to this planet", + "items": { + "$ref": "#/definitions/VolcanoInfo" + } + }, + "singularities": { + "type": "array", + "description": "Add black/white-holes to this planet", + "items": { + "$ref": "#/definitions/SingularityModule" + } + }, + "audioVolumes": { + "type": "array", + "description": "Add audio volumes to this planet", + "items": { + "$ref": "#/definitions/AudioVolumeInfo" + } + }, + "signals": { + "type": "array", + "description": "Add signalscope signals to this planet", + "items": { + "$ref": "#/definitions/SignalInfo" + } + } + } + }, + "DetailInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "rename": { + "type": "string", + "description": "An optional rename of the detail" + }, + "alignToNormal": { + "type": "boolean", + "description": "Do we override rotation and try to automatically align this object to stand upright on the body's surface?" + }, + "assetBundle": { + "type": "string", + "description": "Relative filepath to an asset-bundle to load the prefab defined in `path` from" + }, + "path": { + "type": "string", + "description": "Either the path in the scene hierarchy of the item to copy or the path to the object in the supplied asset bundle" + }, + "position": { + "description": "Position of this prop relative to the body's center", + "$ref": "#/definitions/MVector3" + }, + "removeChildren": { + "type": "array", + "description": "A list of children to remove from this detail", + "items": { + "type": "string" + } + }, + "removeComponents": { + "type": "boolean", + "description": "Do we reset all the components on this object? Useful for certain props that have dialogue components attached to\nthem." + }, + "rotation": { + "description": "Rotate this prop", + "$ref": "#/definitions/MVector3" + }, + "scale": { + "type": "number", + "description": "Scale the prop", + "format": "float", + "default": 1.0 + }, + "quantumGroupID": { + "type": "string", + "description": "If this value is not null, this prop will be quantum. Assign this field to the id of the quantum group it should be a part of. The group it is assigned to determines what kind of quantum object it is" + }, + "parentPath": { + "type": "string", + "description": "The path (not including the root planet object) of the parent of this game object. Optional (will default to the root sector)." + } + } + }, + "DialogueInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "blockAfterPersistentCondition": { + "type": "string", + "description": "Prevents the dialogue from being created after a specific persistent condition is set. Useful for remote dialogue\ntriggers that you want to have happen only once." + }, + "lookAtRadius": { + "type": "number", + "description": "If a pathToAnimController is supplied, if you are within this distance the character will look at you. If it is set\nto 0, they will only look at you when spoken to.", + "format": "float" + }, + "pathToAnimController": { + "type": "string", + "description": "If this dialogue is meant for a character, this is the relative path from the planet to that character's\nCharacterAnimController or SolanumAnimController." + }, + "position": { + "description": "When you enter into dialogue, you will look here.", + "$ref": "#/definitions/MVector3" + }, + "radius": { + "type": "number", + "description": "Radius of the spherical collision volume where you get the \"talk to\" prompt when looking at. If you use a\nremoteTriggerPosition, you can set this to 0 to make the dialogue only trigger remotely.", + "format": "float" + }, + "remoteTriggerPosition": { + "description": "Allows you to trigger dialogue from a distance when you walk into an area.", + "$ref": "#/definitions/MVector3" + }, + "range": { + "type": "number", + "description": "Distance from radius the prompt appears", + "format": "float", + "default": 2.0 + }, + "remoteTriggerRadius": { + "type": "number", + "description": "The radius of the remote trigger volume.", + "format": "float" + }, + "xmlFile": { + "type": "string", + "description": "Relative path to the xml file defining the dialogue." + } + } + }, + "EntryLocationInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "cloaked": { + "type": "boolean", + "description": "Whether this location is cloaked" + }, + "id": { + "type": "string", + "description": "ID of the entry this location relates to" + }, + "position": { + "description": "The position of this entry location", + "$ref": "#/definitions/MVector3" + } + } + }, + "GeyserInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "position": { + "description": "Position of the geyser", + "$ref": "#/definitions/MVector3" + }, + "disableBubbles": { + "type": "boolean", + "description": "Disable the individual particle systems of the geyser" + }, + "disableShaft": { + "type": "boolean", + "description": "Disable the individual particle systems of the geyser" + }, + "disableSpout": { + "type": "boolean", + "description": "Disable the individual particle systems of the geyser" + } + } + }, + "NomaiTextInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "arcInfo": { + "type": "array", + "description": "Additional information about each arc in the text", + "items": { + "$ref": "#/definitions/NomaiTextArcInfo" + } + }, + "normal": { + "description": "The normal vector for this object. Used for writing on walls and positioning computers.", + "$ref": "#/definitions/MVector3" + }, + "position": { + "description": "Position of the root of this text", + "$ref": "#/definitions/MVector3" + }, + "rotation": { + "description": "The euler angle rotation of this object. Not required if setting the normal. Computers and cairns will orient\nthemselves to the surface of the planet automatically.", + "$ref": "#/definitions/MVector3" + }, + "seed": { + "type": "integer", + "description": "The random seed used to pick what the text arcs will look like.", + "format": "int32" + }, + "type": { + "description": "The type of object this is.", + "default": "wall", + "$ref": "#/definitions/NomaiTextType" + }, + "xmlFile": { + "type": "string", + "description": "The relative path to the xml file for this 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" + } + } + }, + "NomaiTextArcInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "mirror": { + "type": "boolean", + "description": "Whether to flip the spiral from left-curling to right-curling or vice versa." + }, + "position": { + "description": "The local position of this object on the wall.", + "$ref": "#/definitions/MVector2" + }, + "type": { + "description": "The type of text to display.", + "default": "adult", + "$ref": "#/definitions/NomaiTextArcType" + }, + "variation": { + "type": "integer", + "description": "Which variation of the chosen type to place. If not specified, a random variation will be selected based on the seed provided in the parent module.", + "format": "int32", + "default": -1 + }, + "zRotation": { + "type": "number", + "description": "The z euler angle for this arc.", + "format": "float", + "maximum": 360.0, + "minimum": 0.0 + } + } + }, + "MVector2": { + "type": "object", + "additionalProperties": false, + "properties": { + "x": { + "type": "number", + "format": "float" + }, + "y": { + "type": "number", + "format": "float" + } + } + }, + "NomaiTextArcType": { + "type": "string", + "description": "", + "x-enumNames": [ + "Adult", + "Child", + "Stranger" + ], + "enum": [ + "adult", + "child", + "stranger" + ] + }, + "NomaiTextType": { + "type": "string", + "description": "", + "x-enumNames": [ + "Wall", + "Scroll", + "Computer", + "Cairn", + "Recorder", + "PreCrashRecorder", + "PreCrashComputer" + ], + "enum": [ + "wall", + "scroll", + "computer", + "cairn", + "recorder", + "preCrashRecorder", + "preCrashComputer" + ] + }, + "RaftInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "position": { + "description": "Position of the raft", + "$ref": "#/definitions/MVector3" + } + } + }, + "RevealInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxAngle": { + "type": "number", + "description": "The max view angle (in degrees) the player can see the volume with to unlock the fact (`observe` only)", + "format": "float" + }, + "maxDistance": { + "type": "number", + "description": "The max distance the user can be away from the volume to reveal the fact (`snapshot` and `observe` only)", + "format": "float" + }, + "position": { + "description": "The position to place this volume at", + "$ref": "#/definitions/MVector3" + }, + "radius": { + "type": "number", + "description": "The radius of this reveal volume", + "format": "float" + }, + "revealOn": { + "description": "What needs to be done to the volume to unlock the facts", + "default": "enter", + "$ref": "#/definitions/RevealVolumeType" + }, + "reveals": { + "type": "array", + "description": "A list of facts to reveal", + "items": { + "type": "string" + } + }, + "achievementID": { + "type": "string", + "description": "An achievement to unlock. Optional." + } + } + }, + "RevealVolumeType": { + "type": "string", + "description": "", + "x-enumNames": [ + "Enter", + "Observe", + "Snapshot" + ], + "enum": [ + "enter", + "observe", + "snapshot" + ] + }, + "ScatterInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "assetBundle": { + "type": "string", + "description": "Relative filepath to an asset-bundle" + }, + "count": { + "type": "integer", + "description": "Number of props to scatter", + "format": "int32" + }, + "offset": { + "description": "Offset this prop once it is placed", + "$ref": "#/definitions/MVector3" + }, + "path": { + "type": "string", + "description": "Either the path in the scene hierarchy of the item to copy or the path to the object in the supplied asset bundle" + }, + "rotation": { + "description": "Rotate this prop once it is placed", + "$ref": "#/definitions/MVector3" + }, + "scale": { + "type": "number", + "description": "Scale this prop once it is placed", + "format": "float" + }, + "seed": { + "type": "integer", + "description": "The number used as entropy for scattering the props", + "format": "int32" + }, + "minHeight": { + "type": [ + "null", + "number" + ], + "description": "The lowest height that these object will be placed at (only relevant if there's a heightmap)", + "format": "float" + }, + "maxHeight": { + "type": [ + "null", + "number" + ], + "description": "The highest height that these objects will be placed at (only relevant if there's a heightmap)", + "format": "float" + } + } + }, + "ProjectionInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "position": { + "description": "The position of this slideshow.", + "$ref": "#/definitions/MVector3" + }, + "reveals": { + "type": "array", + "description": "The ship log entries revealed after finishing this slide reel.", + "items": { + "type": "string" + } + }, + "rotation": { + "description": "The rotation of this slideshow.", + "$ref": "#/definitions/MVector3" + }, + "slides": { + "type": "array", + "description": "The list of slides for this object.", + "items": { + "$ref": "#/definitions/SlideInfo" + } + }, + "type": { + "description": "The type of object this is.", + "default": "slideReel", + "$ref": "#/definitions/SlideShowType" + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this slideshow. Optional (will default to the root sector)." + } + } + }, + "SlideInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "ambientLightColor": { + "description": "Ambient light colour when viewing this slide.", + "$ref": "#/definitions/MColor" + }, + "ambientLightIntensity": { + "type": "number", + "description": "Ambient light intensity when viewing this slide.", + "format": "float" + }, + "ambientLightRange": { + "type": "number", + "description": "Ambient light range when viewing this slide.", + "format": "float" + }, + "backdropAudio": { + "type": "string", + "description": "The name of the AudioClip that will continuously play while watching these slides" + }, + "backdropFadeTime": { + "type": "number", + "description": "The time to fade into the backdrop audio", + "format": "float" + }, + "beatAudio": { + "type": "string", + "description": "The name of the AudioClip for a one-shot sound when opening the slide." + }, + "beatDelay": { + "type": "number", + "description": "The time delay until the one-shot audio", + "format": "float" + }, + "blackFrameDuration": { + "type": "number", + "description": "Before viewing this slide, there will be a black frame for this many seconds.", + "format": "float" + }, + "imagePath": { + "type": "string", + "description": "The path to the image file for this slide." + }, + "playTimeDuration": { + "type": "number", + "description": "Play-time duration for auto-projector slides.", + "format": "float" + }, + "reveal": { + "type": "string", + "description": "Ship log entry revealed when viewing this slide" + }, + "spotIntensityMod": { + "type": "number", + "description": "Spotlight intensity modifier when viewing this slide.", + "format": "float" + } + } + }, + "SlideShowType": { + "type": "string", + "description": "", + "x-enumNames": [ + "SlideReel", + "AutoProjector", + "VisionTorchTarget", + "StandingVisionTorch" + ], + "enum": [ + "slideReel", + "autoProjector", + "visionTorchTarget", + "standingVisionTorch" + ] + }, + "QuantumGroupInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "What type of group this is: does it define a list of states a single quantum object could take or a list of sockets one or more quantum objects could share?", + "$ref": "#/definitions/QuantumGroupType" + }, + "id": { + "type": "string", + "description": "A unique string used by props (that are marked as quantum) use to refer back to this group" + }, + "sockets": { + "type": "array", + "description": "Only required if type is `sockets`. This lists all the possible locations for any props assigned to this group.", + "items": { + "$ref": "#/definitions/QuantumSocketInfo" + } + }, + "hasEmptyState": { + "type": "boolean", + "description": "Optional. Only used if type is `states`. If this is true, then the first prop made part of this group will be used to construct a visibility box for an empty game object, which will be considered one of the states." + }, + "sequential": { + "type": "boolean", + "description": "Optional. Only used if type is `states`. If this is true, then the states will be presented in order, rather than in a random order" + }, + "loop": { + "type": "boolean", + "description": "Optional. Only used if type is `states` and `sequential` is true. If this is false, then after the last state has appeared, the object will no longer change state", + "default": true + } + } + }, + "QuantumGroupType": { + "type": "string", + "description": "", + "x-enumNames": [ + "Sockets", + "States", + "FailedValidation" + ], + "enum": [ + "sockets", + "states", + "FailedValidation" + ] + }, + "QuantumSocketInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "position": { + "description": "The location of this socket", + "$ref": "#/definitions/MVector3" + }, + "rotation": { + "description": "The rotation the quantum object will take if it's occupying this socket", + "$ref": "#/definitions/MVector3" + }, + "probability": { + "type": "number", + "description": "The probability any props that are part of this group will occupy this socket", + "format": "float", + "default": 1.0 + } + } + }, + "TornadoInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "elevation": { + "type": "number", + "description": "Alternative to setting the position. Will choose a random place at this elevation.", + "format": "float" + }, + "height": { + "type": "number", + "description": "The height of this tornado.", + "format": "float", + "default": 30.0 + }, + "position": { + "description": "Position of the tornado", + "$ref": "#/definitions/MVector3" + }, + "tint": { + "description": "The colour of the tornado.", + "$ref": "#/definitions/MColor" + }, + "type": { + "description": "What type of cyclone should this be? Upwards and downwards are both tornados and will push in that direction.", + "default": "downwards", + "$ref": "#/definitions/TornadoType" + }, + "wanderDegreesX": { + "type": "number", + "description": "Angular distance from the starting position that it will wander, in terms of the angle around the x-axis.", + "format": "float", + "default": 45.0 + }, + "wanderDegreesZ": { + "type": "number", + "description": "Angular distance from the starting position that it will wander, in terms of the angle around the z-axis.", + "format": "float", + "default": 45.0 + }, + "wanderRate": { + "type": "number", + "description": "The rate at which the tornado will wander around the planet. Set to 0 for it to be stationary. Should be around\n0.1.", + "format": "float" + }, + "audioDistance": { + "type": "number", + "description": "The maximum distance at which you'll hear the sounds of the cyclone. If not set it will scale relative to the size of the cyclone.", + "format": "float" + } + } + }, + "TornadoType": { + "type": "string", + "description": "", + "x-enumNames": [ + "Downwards", + "Upwards", + "Hurricane" + ], + "enum": [ + "downwards", + "upwards", + "hurricane" + ] + }, + "VolcanoInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "lavaTint": { + "description": "The colour of the meteor's lava.", + "$ref": "#/definitions/MColor" + }, + "maxInterval": { + "type": "number", + "description": "Maximum time between meteor launches.", + "format": "float", + "default": 20.0 + }, + "maxLaunchSpeed": { + "type": "number", + "description": "Maximum random speed at which meteors are launched.", + "format": "float", + "default": 150.0 + }, + "minInterval": { + "type": "number", + "description": "Minimum time between meteor launches.", + "format": "float", + "default": 5.0 + }, + "minLaunchSpeed": { + "type": "number", + "description": "Minimum random speed at which meteors are launched.", + "format": "float", + "default": 50.0 + }, + "position": { + "description": "Position of this volcano.", + "$ref": "#/definitions/MVector3" + }, + "scale": { + "type": "number", + "description": "Scale of the meteors.", + "format": "float" + }, + "stoneTint": { + "description": "The colour of the meteor's stone.", + "$ref": "#/definitions/MColor" + } + } + }, + "SingularityModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "curve": { + "type": "array", + "description": "Scale this module over time", + "items": { + "$ref": "#/definitions/TimeValuePair" + } + }, + "makeZeroGVolume": { + "type": "boolean", + "description": "Only for White Holes. Should this white hole repel the player from it.", + "default": true + }, + "pairedSingularity": { + "type": "string", + "description": "The uniqueID of the white hole or black hole that is paired to this one. If you don't set a value, entering will kill\nthe player" + }, + "uniqueID": { + "type": "string", + "description": "The uniqueID of this white hole or black hole. If not set it will default to the name of the planet" + }, + "position": { + "description": "Position of the singularity", + "$ref": "#/definitions/MVector3" + }, + "size": { + "type": "number", + "description": "Radius of the singularity. Note that this isn't the same as the event horizon, but includes the entire volume that\nhas warped effects in it.", + "format": "float", + "minimum": 0.0 + }, + "targetStarSystem": { + "type": "string", + "description": "If you want a black hole to load a new star system scene, put its name here." + }, + "type": { + "description": "Type of singularity (white hole or black hole)", + "$ref": "#/definitions/SingularityType" + } + } + }, + "SingularityType": { + "type": "string", + "description": "", + "x-enumNames": [ + "BlackHole", + "WhiteHole" + ], + "enum": [ + "blackHole", + "whiteHole" + ] + }, + "AudioVolumeInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "position": { + "description": "The location of this audio volume. Optional (will default to 0,0,0).", + "$ref": "#/definitions/MVector3" + }, + "radius": { + "type": "number", + "description": "The radius of this audio volume", + "format": "float" + }, + "audio": { + "type": "string", + "description": "The audio to use. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." + }, + "track": { + "description": "The audio track of this audio volume", + "default": "environment", + "$ref": "#/definitions/AudioMixerTrackName" + } + } + }, + "AudioMixerTrackName": { + "type": "string", + "description": "", + "x-enumNames": [ + "Undefined", + "Menu", + "Music", + "Environment", + "Environment_Unfiltered", + "EndTimes_SFX", + "Signal", + "Death", + "Player", + "Player_External", + "Ship", + "Map", + "EndTimes_Music", + "MuffleWhileRafting", + "MuffleIndoors", + "SlideReelMusic" + ], + "enum": [ + "undefined", + "menu", + "music", + "environment", + "environmentUnfiltered", + "endTimesSfx", + "signal", + "death", + "player", + "playerExternal", + "ship", + "map", + "endTimesMusic", + "muffleWhileRafting", + "muffleIndoors", + "slideReelMusic" + ] + }, + "SignalInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "audio": { + "type": "string", + "description": "The audio to use. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." + }, + "detectionRadius": { + "type": "number", + "description": "How close the player must get to the signal to detect it. This is when you get the \"Unknown Signal Detected\"\nnotification.", + "format": "float", + "minimum": 0.0 + }, + "frequency": { + "type": "string", + "description": "The frequency ID of the signal. The built-in game values are `Default`, `Traveler`, `Quantum`, `EscapePod`,\n`Statue`, `WarpCore`, `HideAndSeek`, and `Radio`. You can also put a custom value." + }, + "identificationRadius": { + "type": "number", + "description": "How close the player must get to the signal to identify it. This is when you learn its name.", + "format": "float", + "default": 10.0, + "minimum": 0.0 + }, + "insideCloak": { + "type": "boolean", + "description": "Only set to `true` if you are putting this signal inside a cloaking field." + }, + "name": { + "type": "string", + "description": "The unique ID of the signal." + }, + "onlyAudibleToScope": { + "type": "boolean", + "description": "`false` if the player can hear the signal without equipping the signal-scope.", + "default": true + }, + "position": { + "description": "Position of the signal's source", + "$ref": "#/definitions/MVector3" + }, + "reveals": { + "type": "string", + "description": "A ship log fact to reveal when the signal is identified.", + "default": "" + }, + "sourceRadius": { + "type": "number", + "description": "Radius of the sphere giving off the signal.", + "format": "float", + "default": 1.0 + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this signal. Optional (will default to the root sector)." + } + } + }, + "ReferenceFrameModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean", + "description": "Allows the object to be targeted.", + "default": true + }, + "hideInMap": { + "type": "boolean", + "description": "Stop the object from being targeted on the map." + }, + "bracketRadius": { + "type": "number", + "description": "Radius of the brackets that show up when you target this. Defaults to the sphere of influence.", + "format": "float", + "default": -1 + }, + "targetWhenClose": { + "type": "boolean", + "description": "If it should be targetable even when super close." + }, + "maxTargetDistance": { + "type": "number", + "description": "The maximum distance that the reference frame can be targeted from. Defaults to 100km and cannot be greater than that.", + "format": "float" + }, + "targetColliderRadius": { + "type": "number", + "description": "The radius of the sphere around the planet which you can click on to target it. Defaults to twice the sphere of influence.", + "format": "float" + }, + "localPosition": { + "description": "Position of the reference frame relative to the object.", + "$ref": "#/definitions/MVector3" + } + } + }, + "RingModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "curve": { + "type": "array", + "description": "Scale this module over time", + "items": { + "$ref": "#/definitions/TimeValuePair" + } + }, + "fluidType": { + "description": "Fluid type for sounds/effects when colliding with this ring.", + "$ref": "#/definitions/CloudFluidType" + }, + "inclination": { + "type": "number", + "description": "Angle between the rings and the equatorial plane of the planet.", + "format": "float" + }, + "innerRadius": { + "type": "number", + "description": "Inner radius of the disk", + "format": "float", + "minimum": 0.0 + }, + "longitudeOfAscendingNode": { + "type": "number", + "description": "Angle defining the point where the rings rise up from the planet's equatorial plane if inclination is nonzero.", + "format": "float" + }, + "outerRadius": { + "type": "number", + "description": "Outer radius of the disk", + "format": "float", + "minimum": 0.0 + }, + "rotationSpeed": { + "type": "number", + "description": "Allows the rings to rotate.", + "format": "float" + }, + "texture": { + "type": "string", + "description": "Relative filepath to the texture used for the rings." + }, + "unlit": { + "type": "boolean", + "description": "Should this ring be unlit?" + } + } + }, + "SandModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "curve": { + "type": "array", + "description": "Scale this module over time", + "items": { + "$ref": "#/definitions/TimeValuePair" + } + }, + "size": { + "type": "number", + "description": "Size of the sand", + "format": "float" + }, + "tint": { + "description": "Tint of the sand", + "$ref": "#/definitions/MColor" + } + } + }, + "ShipLogModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "mapMode": { + "description": "Describe what this planet looks and like in map mode", + "$ref": "#/definitions/MapModeInfo" + }, + "spriteFolder": { + "type": "string", + "description": "A path to the folder where entry sprites are stored." + }, + "xmlFile": { + "type": "string", + "description": "The relative path to the xml file to load ship log entries from." + } + } + }, + "MapModeInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "details": { + "type": "array", + "description": "Place non-selectable objects in map mode (like sand funnels).", + "items": { + "$ref": "#/definitions/ShipLogDetailInfo" + } + }, + "invisibleWhenHidden": { + "type": "boolean", + "description": "Hide the planet completely if unexplored instead of showing an outline." + }, + "manualNavigationPosition": { + "description": "Specify where this planet is in terms of navigation.", + "$ref": "#/definitions/MVector2" + }, + "manualPosition": { + "description": "Manually place this planet at the specified position.", + "$ref": "#/definitions/MVector2" + }, + "offset": { + "type": "number", + "description": "Extra distance to apply to this object in map mode.", + "format": "float" + }, + "outlineSprite": { + "type": "string", + "description": "The path to the sprite to show when the planet is unexplored in map mode." + }, + "remove": { + "type": "boolean", + "description": "Completely remove this planet (and it's children) from map mode." + }, + "revealedSprite": { + "type": "string", + "description": "The path to the sprite to show when the planet is revealed in map mode." + }, + "scale": { + "type": "number", + "description": "Scale to apply to the planet in map mode.", + "format": "float", + "default": 1.0 + } + } + }, + "ShipLogDetailInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "invisibleWhenHidden": { + "type": "boolean", + "description": "Whether to completely hide this detail when the parent AstroBody is unexplored." + }, + "outlineSprite": { + "type": "string", + "description": "The sprite to show when the parent AstroBody is rumored/unexplored." + }, + "position": { + "description": "The position (relative to the parent) to place the detail.", + "$ref": "#/definitions/MVector2" + }, + "revealedSprite": { + "type": "string", + "description": "The sprite to show when the parent AstroBody is revealed." + }, + "rotation": { + "type": "number", + "description": "The angle in degrees to rotate the detail.", + "format": "float" + }, + "scale": { + "description": "The amount to scale the x and y-axis of the detail by.", + "$ref": "#/definitions/MVector2" + } + } + }, + "SpawnModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "playerSpawnPoint": { + "description": "If you want the player to spawn on the new body, set a value for this. Press `P` in game with Debug mode on to have\nthe game log the position you're looking at to find a good value for this.", + "$ref": "#/definitions/MVector3" + }, + "playerSpawnRotation": { + "description": "Euler angles by which the player will be oriented.", + "$ref": "#/definitions/MVector3" + }, + "shipSpawnPoint": { + "description": "Required for the system to be accessible by warp drive.", + "$ref": "#/definitions/MVector3" + }, + "shipSpawnRotation": { + "description": "Euler angles by which the ship will be oriented.", + "$ref": "#/definitions/MVector3" + }, + "startWithSuit": { + "type": "boolean", + "description": "If you spawn on a planet with no oxygen, you probably want to set this to true ;;)" + } + } + }, + "StarModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "curve": { + "type": "array", + "description": "Scale this module over time", + "items": { + "$ref": "#/definitions/TimeValuePair" + } + }, + "endTint": { + "description": "Colour of the star at the end of its life.", + "$ref": "#/definitions/MColor" + }, + "goSupernova": { + "type": "boolean", + "description": "Should this star explode after 22 minutes?", + "default": true + }, + "hasStarController": { + "type": "boolean", + "description": "Should we add a star controller to this body? If you want clouds to work on a binary brown dwarf system, set this to false.", + "default": true + }, + "hasAtmosphere": { + "type": "boolean", + "description": "The default sun has its own atmosphere that is different from regular planets. If you want that, set this to\n`true`.", + "default": true + }, + "lightTint": { + "description": "Colour of the light given off. Defaults to yellowish.", + "$ref": "#/definitions/MColor" + }, + "size": { + "type": "number", + "description": "Radius of the star.", + "format": "float", + "default": 2000.0, + "minimum": 0.0 + }, + "solarLuminosity": { + "type": "number", + "description": "Relative strength of the light compared to the sun.", + "format": "float", + "default": 1.0, + "minimum": 0.0 + }, + "supernovaTint": { + "description": "The tint of the supernova this star creates when it dies.", + "$ref": "#/definitions/MColor" + }, + "tint": { + "description": "Colour of the star.", + "$ref": "#/definitions/MColor" + }, + "starRampTexture": { + "type": "string", + "description": "Path to the texture to put as the star ramp. Optional." + }, + "starCollapseRampTexture": { + "type": "string", + "description": "Path to the texture to put as the star ramp while it is collapsing. Optional." + }, + "lightRadius": { + "type": "number", + "description": "How far the light from the star can reach.", + "format": "float", + "default": 50000.0, + "minimum": 0.0 + } + } + }, + "WaterModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "curve": { + "type": "array", + "description": "Scale this module over time", + "items": { + "$ref": "#/definitions/TimeValuePair" + } + }, + "size": { + "type": "number", + "description": "Size of the water sphere", + "format": "float" + }, + "tint": { + "description": "Tint of the water", + "$ref": "#/definitions/MColor" + } + } + } + }, + "$docs": { + "title": "Celestial Body Schema", + "description": "Schema for a celestial body in New Horizons" + } } \ No newline at end of file