diff --git a/NewHorizons/Assets/translations/english.json b/NewHorizons/Assets/translations/english.json index 9d1a4c281..48df51eef 100644 --- a/NewHorizons/Assets/translations/english.json +++ b/NewHorizons/Assets/translations/english.json @@ -24,6 +24,9 @@ "DEBUG_REDO": "Redo", "Vessel": "Vessel" }, + "OtherDictionary": { + "NOMAI_SHUTTLE_COMPUTER": "The shuttle]]> is currently resting at {0}]]>." + }, "AchievementTranslations": { "NH_EATEN_OUTSIDE_BRAMBLE": { "Name": "Containment Breach", diff --git a/NewHorizons/Builder/Body/GeometryBuilder.cs b/NewHorizons/Builder/Body/GeometryBuilder.cs index dc7c99cf6..1c59724bb 100644 --- a/NewHorizons/Builder/Body/GeometryBuilder.cs +++ b/NewHorizons/Builder/Body/GeometryBuilder.cs @@ -30,6 +30,11 @@ public static GameObject Make(GameObject planetGO, Sector sector, float groundSc { groundGO.transform.localScale *= 2; // Multiply by 2 to match top layer } + + var superGroup = planetGO.GetComponent(); + // idk if we need to set _superGroup manually since it does that in Awake, but it's done everywhere else so wtv + if (superGroup != null) groundGO.AddComponent()._superGroup = superGroup; + groundGO.SetActive(true); return groundGO; diff --git a/NewHorizons/Builder/Body/StarBuilder.cs b/NewHorizons/Builder/Body/StarBuilder.cs index f4954af58..ecbbee5c1 100644 --- a/NewHorizons/Builder/Body/StarBuilder.cs +++ b/NewHorizons/Builder/Body/StarBuilder.cs @@ -178,14 +178,11 @@ public static (GameObject, StarController, StarEvolutionController, Light) Make( light.color = lightColour; ambientLight.color = new Color(lightColour.r, lightColour.g, lightColour.b, lightColour.a == 0 ? 0.0001f : lightColour.a); + // used to use CopyPropertiesFrom, but that doesnt work here. instead, just copy settings from unity explorer var faceActiveCamera = sunLight.AddComponent(); - faceActiveCamera.CopyPropertiesFrom(_sunLight.GetComponent()); + faceActiveCamera._useLookAt = true; var csmTextureCacher = sunLight.AddComponent(); - csmTextureCacher.CopyPropertiesFrom(_sunLight.GetComponent()); - csmTextureCacher._light = light; var proxyShadowLight = sunLight.AddComponent(); - proxyShadowLight.CopyPropertiesFrom(_sunLight.GetComponent()); - proxyShadowLight._light = light; sunLight.name = "StarLight"; diff --git a/NewHorizons/Builder/General/RigidBodyBuilder.cs b/NewHorizons/Builder/General/RigidBodyBuilder.cs index ebfcd11e5..9a0d51865 100644 --- a/NewHorizons/Builder/General/RigidBodyBuilder.cs +++ b/NewHorizons/Builder/General/RigidBodyBuilder.cs @@ -1,13 +1,12 @@ -using NewHorizons.External.Configs; using NewHorizons.Utility; using UnityEngine; namespace NewHorizons.Builder.General { public static class RigidBodyBuilder { - public static OWRigidbody Make(GameObject body, PlanetConfig config) + public static OWRigidbody Make(GameObject body, float sphereOfInfluence) { - body.AddComponent(); + body.AddComponent()._bounds.radius = sphereOfInfluence; Rigidbody rigidBody = body.AddComponent(); rigidBody.mass = 10000; diff --git a/NewHorizons/Builder/Props/DetailBuilder.cs b/NewHorizons/Builder/Props/DetailBuilder.cs index 1dca98824..6c762931a 100644 --- a/NewHorizons/Builder/Props/DetailBuilder.cs +++ b/NewHorizons/Builder/Props/DetailBuilder.cs @@ -112,6 +112,8 @@ public static GameObject Make(GameObject go, Sector sector, GameObject prefab, D // Could check this in the for loop but I'm not sure what order we need to know about this in isItem = false; + var existingSectors = new HashSet(prop.GetComponentsInChildren(true)); + foreach (var component in prop.GetComponentsInChildren(true)) { // Components can come through as null here (yes, really), @@ -127,7 +129,10 @@ public static GameObject Make(GameObject go, Sector sector, GameObject prefab, D { if (FixUnsectoredComponent(component)) continue; } - else FixSectoredComponent(component, sector, detail.keepLoaded); + else + { + FixSectoredComponent(component, sector, existingSectors, detail.keepLoaded); + } FixComponent(component, go, detail.ignoreSun); } @@ -235,7 +240,7 @@ public static GameObject Make(GameObject go, Sector sector, GameObject prefab, D /// /// Fix components that have sectors. Has a specific fix if there is a VisionTorchItem on the object. /// - private static void FixSectoredComponent(Component component, Sector sector, bool keepLoaded) + private static void FixSectoredComponent(Component component, Sector sector, HashSet existingSectors, bool keepLoaded) { // keepLoaded should remove existing groups // renderers/colliders get enabled later so we dont have to do that here @@ -246,14 +251,15 @@ private static void FixSectoredComponent(Component component, Sector sector, boo } // fix Sector stuff, eg SectorCullGroup (without this, props that have a SectorCullGroup component will become invisible inappropriately) - if (component is ISectorGroup sectorGroup) + if (component is ISectorGroup sectorGroup && !existingSectors.Contains(sectorGroup.GetSector())) { sectorGroup.SetSector(sector); } // Not doing else if here because idk if any of the classes below implement ISectorGroup - - if (component is Sector s) + + // Null check else shuttles controls break + if (component is Sector s && s.GetParentSector() != null && !existingSectors.Contains(s.GetParentSector())) { s.SetParentSector(sector); } @@ -269,14 +275,14 @@ private static void FixSectoredComponent(Component component, Sector sector, boo sectorCullGroup.SetVisible(sectorCullGroup.ShouldBeVisible(), true, false); } - else if(component is SectoredMonoBehaviour behaviour) + else if(component is SectoredMonoBehaviour behaviour && !existingSectors.Contains(behaviour._sector)) { // not using SetSector here because it registers the events twice // perhaps this happens with ISectorGroup.SetSector or Sector.SetParentSector too? idk and nothing seems to break because of it yet behaviour._sector = sector; } - else if(component is OWItemSocket socket) + else if(component is OWItemSocket socket && !existingSectors.Contains(socket._sector)) { socket._sector = sector; } @@ -287,7 +293,7 @@ private static void FixSectoredComponent(Component component, Sector sector, boo } - else if(component is NomaiRemoteCameraPlatform remoteCameraPlatform) + else if(component is NomaiRemoteCameraPlatform remoteCameraPlatform && !existingSectors.Contains(remoteCameraPlatform._visualSector)) { remoteCameraPlatform._visualSector = sector; } diff --git a/NewHorizons/Builder/Props/GravityCannonBuilder.cs b/NewHorizons/Builder/Props/GravityCannonBuilder.cs new file mode 100644 index 000000000..e86ccd42c --- /dev/null +++ b/NewHorizons/Builder/Props/GravityCannonBuilder.cs @@ -0,0 +1,188 @@ +using NewHorizons.Builder.Props.TranslatorText; +using NewHorizons.Components.Orbital; +using NewHorizons.External.Modules; +using NewHorizons.External.Modules.Props; +using NewHorizons.External.Modules.Props.Shuttle; +using NewHorizons.External.Modules.TranslatorText; +using NewHorizons.Handlers; +using NewHorizons.Utility; +using NewHorizons.Utility.OuterWilds; +using NewHorizons.Utility.OWML; +using Newtonsoft.Json; +using OWML.Common; +using UnityEngine; + +namespace NewHorizons.Builder.Props +{ + public static class GravityCannonBuilder + { + private static GameObject _interfacePrefab; + private static GameObject _detailedPlatformPrefab, _platformPrefab; + private static GameObject _orbPrefab; + + internal static void InitPrefab() + { + if (_interfacePrefab == null) + { + _interfacePrefab = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_GravityCannon/Interactables_GravityCannon/Prefab_NOM_GravityCannonInterface") + .InstantiateInactive() + .Rename("Prefab_GravityCannon") + .DontDestroyOnLoad(); + } + + if (_detailedPlatformPrefab == null) + { + // Creating it in the original position so we can instantiate the other parts in the right relative positions + var original = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_GravityCannon/Geometry_GravityCannon/ControlledByProxy_OPC/Structure_NOM_GravityCannon_BH"); + _detailedPlatformPrefab = original + .InstantiateInactive() + .Rename("Prefab_GravityCannonPlatform_Detailed") + .DontDestroyOnLoad(); + _detailedPlatformPrefab.transform.position = original.transform.position; + _detailedPlatformPrefab.transform.rotation = original.transform.rotation; + + GameObject.Instantiate(SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_GravityCannon/Interactables_GravityCannon/Prefab_NOM_ShuttleSocket"), _detailedPlatformPrefab.transform, true) + .Rename("ShuttleSocket"); + GameObject.Instantiate(SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_GravityCannon/Interactables_GravityCannon/CannonForceVolume"), _detailedPlatformPrefab.transform, true) + .Rename("ForceVolume"); + GameObject.Instantiate(SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_GravityCannon/Volumes_GravityCannon/CannonPlatformTrigger"), _detailedPlatformPrefab.transform, true) + .Rename("PlatformTrigger"); + } + + if (_platformPrefab == null) + { + _platformPrefab = _detailedPlatformPrefab + .InstantiateInactive() + .Rename("Prefab_GravityCannonPlatform") + .DontDestroyOnLoad(); + GameObject.DestroyImmediate(_platformPrefab.FindChild("Structure_NOM_GravityCannon_Collider")); + GameObject.DestroyImmediate(_platformPrefab.FindChild("Structure_NOM_GravityCannon_Crystals")); + GameObject.DestroyImmediate(_platformPrefab.FindChild("Structure_NOM_GravityCannon_Geo")); + } + + if (_orbPrefab == null) + { + _orbPrefab = SearchUtilities.Find("Prefab_NOM_InterfaceOrb") + .InstantiateInactive() + .Rename("Prefab_NOM_InterfaceOrb") + .DontDestroyOnLoad(); + } + } + + public static GameObject Make(GameObject planetGO, Sector sector, GravityCannonInfo info, IModBehaviour mod) + { + InitPrefab(); + + if (_interfacePrefab == null || planetGO == null || sector == null || _detailedPlatformPrefab == null || _platformPrefab == null || _orbPrefab == null) return null; + + var detailInfo = new DetailInfo(info.controls) { keepLoaded = true }; + var gravityCannonObject = DetailBuilder.Make(planetGO, sector, _interfacePrefab, detailInfo); + gravityCannonObject.SetActive(false); + + var gravityCannonController = gravityCannonObject.GetComponent(); + var id = ShuttleHandler.GetShuttleID(info.shuttleID); + gravityCannonController._shuttleID = id; + + // Gravity controller checks string length instead of isnullorempty + gravityCannonController._retrieveShipLogFact = info.retrieveReveal ?? string.Empty; + gravityCannonController._launchShipLogFact = info.launchReveal ?? string.Empty; + + CreatePlatform(planetGO, sector, gravityCannonController, info); + + if (info.computer != null) + { + // Do it next update so that the shuttle has been made + Delay.FireOnNextUpdate(() => + { + gravityCannonController._nomaiComputer = CreateComputer(planetGO, sector, info.computer, id); + }); + } + else + { + gravityCannonController._nomaiComputer = null; + } + + CreateOrb(planetGO, gravityCannonController); + + gravityCannonObject.SetActive(true); + + return gravityCannonObject; + } + + private static void CreateOrb(GameObject planetGO, GravityCannonController gravityCannonController) + { + var orb = _orbPrefab.InstantiateInactive().Rename(_orbPrefab.name); + orb.transform.parent = gravityCannonController.transform; + orb.transform.localPosition = new Vector3(0f, 0.9673f, 0f); + orb.transform.localScale = Vector3.one; + orb.SetActive(true); + + var planetBody = planetGO.GetComponent(); + var orbBody = orb.GetComponent(); + + var nomaiInterfaceOrb = orb.GetComponent(); + nomaiInterfaceOrb._orbBody = orbBody; + nomaiInterfaceOrb._slotRoot = gravityCannonController.gameObject; + orbBody._origParent = planetGO.transform; + orbBody._origParentBody = planetBody; + nomaiInterfaceOrb._slots = nomaiInterfaceOrb._slotRoot.GetComponentsInChildren(); + nomaiInterfaceOrb.SetParentBody(planetBody); + nomaiInterfaceOrb._safetyRails = new OWRail[0]; + nomaiInterfaceOrb.RemoveAllLocks(); + + var spawnVelocity = planetBody.GetVelocity(); + var spawnAngularVelocity = planetBody.GetPointTangentialVelocity(orbBody.transform.position); + var velocity = spawnVelocity + spawnAngularVelocity; + + orbBody._lastVelocity = velocity; + orbBody._currentVelocity = velocity; + + orb.GetComponent()._detectableFields = new ForceVolume[] { planetGO.GetComponentInChildren() }; + + Delay.RunWhenAndInNUpdates(() => + { + foreach (var component in orb.GetComponents()) + { + component.enabled = true; + } + nomaiInterfaceOrb.RemoveAllLocks(); + }, () => Main.IsSystemReady, 10); + } + + private static NomaiComputer CreateComputer(GameObject planetGO, Sector sector, GeneralPropInfo computerInfo, NomaiShuttleController.ShuttleID id) + { + // Load the position info from the GeneralPropInfo + var translatorTextInfo = new TranslatorTextInfo(); + JsonConvert.PopulateObject(JsonConvert.SerializeObject(computerInfo), translatorTextInfo); + translatorTextInfo.type = NomaiTextType.Computer; + + var shuttle = ShuttleBuilder.Shuttles[id]; + var planet = AstroObjectLocator.GetPlanetName(shuttle.GetComponentInParent()); + + var displayText = TranslationHandler.GetTranslation("NOMAI_SHUTTLE_COMPUTER", TranslationHandler.TextType.OTHER).Replace("{0}", planet); + NHLogger.Log(displayText); + var xmlContent = $"\r\n \r\n 1\r\n {displayText}\r\n \r\n"; + + var computerObject = TranslatorTextBuilder.Make(planetGO, sector, translatorTextInfo, null, xmlContent); + + var computer = computerObject.GetComponentInChildren(); + + computerObject.SetActive(true); + + return computer; + } + + private static GameObject CreatePlatform(GameObject planetGO, Sector sector, GravityCannonController gravityCannonController, GravityCannonInfo platformInfo) + { + var platform = DetailBuilder.Make(planetGO, sector, platformInfo.detailed ? _detailedPlatformPrefab : _platformPrefab, new DetailInfo(platformInfo) { keepLoaded = true }); + + gravityCannonController._forceVolume = platform.FindChild("ForceVolume").GetComponent(); + gravityCannonController._platformTrigger = platform.FindChild("PlatformTrigger").GetComponent(); + gravityCannonController._shuttleSocket = platform.FindChild("ShuttleSocket").transform; + gravityCannonController._warpEffect = gravityCannonController._shuttleSocket.GetComponentInChildren(); + gravityCannonController._recallProxyGeometry = gravityCannonController._shuttleSocket.gameObject.FindChild("ShuttleRecallProxy"); + + return platform; + } + } +} diff --git a/NewHorizons/Builder/Props/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiTextBuilder.cs index 9dfe17bdd..e70353651 100644 --- a/NewHorizons/Builder/Props/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiTextBuilder.cs @@ -744,6 +744,12 @@ internal static GameObject MakeArc(NomaiTextArcInfo arcInfo, GameObject conversa XmlDocument xmlDocument = new XmlDocument(); xmlDocument.LoadXml(xmlPath); XmlNode rootNode = xmlDocument.SelectSingleNode("NomaiObject"); + + if (rootNode == null) + { + NHLogger.LogError($"Couldn't find NomaiObject in [{xmlPath}]"); + return dict; + } foreach (object obj in rootNode.SelectNodes("TextBlock")) { diff --git a/NewHorizons/Builder/Props/PropBuildManager.cs b/NewHorizons/Builder/Props/PropBuildManager.cs index ddb5a17e5..af781f529 100644 --- a/NewHorizons/Builder/Props/PropBuildManager.cs +++ b/NewHorizons/Builder/Props/PropBuildManager.cs @@ -19,6 +19,34 @@ public static void Make(GameObject go, Sector sector, OWRigidbody planetBody, Ne PlanetConfig config = nhBody.Config; IModBehaviour mod = nhBody.Mod; + if (config.Props.gravityCannons != null) + { + foreach (var gravityCannonInfo in config.Props.gravityCannons) + { + try + { + GravityCannonBuilder.Make(go, sector, gravityCannonInfo, mod); + } + catch (Exception ex) + { + NHLogger.LogError($"Couldn't make gravity cannon [{gravityCannonInfo.shuttleID}] for [{go.name}]:\n{ex}"); + } + } + } + if (config.Props.shuttles != null) + { + foreach (var shuttleInfo in config.Props.shuttles) + { + try + { + ShuttleBuilder.Make(go, sector, shuttleInfo); + } + catch (Exception ex) + { + NHLogger.LogError($"Couldn't make shuttle [{shuttleInfo.id}] for [{go.name}]:\n{ex}"); + } + } + } if (config.Props.scatter != null) { try diff --git a/NewHorizons/Builder/Props/ShuttleBuilder.cs b/NewHorizons/Builder/Props/ShuttleBuilder.cs new file mode 100644 index 000000000..06d8dabbc --- /dev/null +++ b/NewHorizons/Builder/Props/ShuttleBuilder.cs @@ -0,0 +1,119 @@ +using NewHorizons.Components; +using NewHorizons.External.Modules.Props; +using NewHorizons.External.Modules.Props.Shuttle; +using NewHorizons.Handlers; +using NewHorizons.Utility; +using System.Collections.Generic; +using UnityEngine; + +namespace NewHorizons.Builder.Props +{ + public static class ShuttleBuilder + { + private static GameObject _prefab; + private static GameObject _orbPrefab; + private static GameObject _bodyPrefab; + + public static Dictionary Shuttles { get; } = new(); + + internal static void InitPrefab() + { + if (_prefab == null) + { + _prefab = SearchUtilities.Find("QuantumMoon_Body/Sector_QuantumMoon/QuantumShuttle/Prefab_NOM_Shuttle")?.InstantiateInactive()?.Rename("Prefab_QM_Shuttle")?.DontDestroyOnLoad(); + NomaiShuttleController shuttleController = _prefab.GetComponent(); + NHShuttleController nhShuttleController = _prefab.AddComponent(); + nhShuttleController._exteriorSector = _prefab.FindChild("Sector_Shuttle").GetComponent(); + nhShuttleController._interiorSector = _prefab.FindChild("Sector_NomaiShuttleInterior").GetComponent(); + nhShuttleController._shuttleBody = shuttleController._shuttleBody; + nhShuttleController._retrievalLength = shuttleController._retrievalLength; + nhShuttleController._launchSlot = shuttleController._launchSlot; + nhShuttleController._retrieveSlot = shuttleController._retrieveSlot; + nhShuttleController._landSlot = shuttleController._landSlot; + nhShuttleController._triggerVolume = shuttleController._triggerVolume; + nhShuttleController._beamResetVolume = shuttleController._beamResetVolume; + nhShuttleController._tractorBeam = shuttleController._tractorBeam; + nhShuttleController._forceVolume = shuttleController._forceVolume; + nhShuttleController._exteriorCollisionGroup = shuttleController._exteriorCollisionGroup; + nhShuttleController._exteriorColliderRoot = shuttleController._exteriorColliderRoot; + nhShuttleController._landingBeamRoot = shuttleController._landingBeamRoot; + nhShuttleController._warpEffect = _prefab.GetComponentInChildren(); + nhShuttleController._exteriorLegColliders = shuttleController._exteriorLegColliders; + nhShuttleController._id = NomaiShuttleController.ShuttleID.HourglassShuttle; + nhShuttleController._cannon = null; + GameObject slots = _prefab.FindChild("Sector_NomaiShuttleInterior/Interactibles_NomaiShuttleInterior/ControlPanel/Slots"); + GameObject neutralSlotObject = new GameObject("Slot_Neutral", typeof(NomaiInterfaceSlot)); + neutralSlotObject.transform.SetParent(slots.transform, false); + neutralSlotObject.transform.localPosition = new Vector3(-0.0153f, -0.2386f, 0.0205f); + neutralSlotObject.transform.localRotation = Quaternion.identity; + NomaiInterfaceSlot neutralSlot = neutralSlotObject.GetComponent(); + neutralSlot._exitRadius = 0.1f; + neutralSlot._radius = 0.05f; + neutralSlot._attractive = true; + neutralSlot._muteAudio = true; + nhShuttleController._neutralSlot = neutralSlot; + _orbPrefab = shuttleController._orb.gameObject?.InstantiateInactive()?.Rename("Prefab_QM_Shuttle_InterfaceOrbSmall")?.DontDestroyOnLoad(); + nhShuttleController._orb = _orbPrefab.GetComponent(); + nhShuttleController._orb._sector = nhShuttleController._interiorSector; + nhShuttleController._orb._slotRoot = slots; + nhShuttleController._orb._safetyRails = slots.GetComponentsInChildren(); + nhShuttleController._orb._slots = slots.GetComponentsInChildren(); + _bodyPrefab = shuttleController._shuttleBody.gameObject?.InstantiateInactive()?.Rename("Prefab_QM_Shuttle_Body")?.DontDestroyOnLoad(); + nhShuttleController._shuttleBody = _bodyPrefab.GetComponent(); + nhShuttleController._impactSensor = _bodyPrefab.GetComponent(); + nhShuttleController._forceApplier = _bodyPrefab.GetComponentInChildren(); + nhShuttleController._detectorObj = nhShuttleController._forceApplier.gameObject; + GameObject.DestroyImmediate(shuttleController); + GameObject.DestroyImmediate(_prefab.FindChild("Sector_NomaiShuttleInterior/Interactibles_NomaiShuttleInterior/Prefab_NOM_Recorder")); + } + } + + public static GameObject Make(GameObject planetGO, Sector sector, ShuttleInfo info) + { + InitPrefab(); + + if (_prefab == null || planetGO == null || sector == null) return null; + + var detailInfo = new DetailInfo(info) { keepLoaded = true }; + var shuttleObject = DetailBuilder.Make(planetGO, sector, _prefab, detailInfo); + shuttleObject.SetActive(false); + + StreamingHandler.SetUpStreaming(shuttleObject, sector); + + NHShuttleController shuttleController = shuttleObject.GetComponent(); + NomaiShuttleController.ShuttleID id = ShuttleHandler.GetShuttleID(info.id); + shuttleController._id = id; + shuttleController._cannon = Locator.GetGravityCannon(id); + + GameObject slots = shuttleObject.FindChild("Sector_NomaiShuttleInterior/Interactibles_NomaiShuttleInterior/ControlPanel/Slots"); + GameObject orbObject = _orbPrefab.InstantiateInactive().Rename("InterfaceOrbSmall"); + orbObject.transform.SetParent(slots.transform, false); + orbObject.transform.localPosition = new Vector3(-0.0153f, -0.2386f, 0.0205f); + shuttleController._orb = orbObject.GetComponent(); + shuttleController._orb._sector = shuttleController._interiorSector; + shuttleController._orb._slotRoot = slots; + shuttleController._orb._safetyRails = slots.GetComponentsInChildren(); + shuttleController._orb._slots = slots.GetComponentsInChildren(); + + StreamingHandler.SetUpStreaming(orbObject, sector); + + GameObject bodyObject = _bodyPrefab.InstantiateInactive().Rename("Shuttle_Body"); + bodyObject.transform.SetParent(shuttleObject.transform, false); + bodyObject.transform.localPosition = Vector3.zero; + bodyObject.transform.localEulerAngles = Vector3.zero; + shuttleController._shuttleBody = bodyObject.GetComponent(); + shuttleController._impactSensor = bodyObject.GetComponent(); + shuttleController._forceApplier = bodyObject.GetComponentInChildren(); + shuttleController._detectorObj = shuttleController._forceApplier.gameObject; + + shuttleObject.SetActive(true); + bodyObject.SetActive(true); + orbObject.SetActive(true); + shuttleController._orb.RemoveAllLocks(); + + Shuttles[id] = shuttleController; + + return shuttleObject; + } + } +} diff --git a/NewHorizons/Builder/Props/TranslatorText/TranslatorTextBuilder.cs b/NewHorizons/Builder/Props/TranslatorText/TranslatorTextBuilder.cs index 862257ed6..48a1cc4e0 100644 --- a/NewHorizons/Builder/Props/TranslatorText/TranslatorTextBuilder.cs +++ b/NewHorizons/Builder/Props/TranslatorText/TranslatorTextBuilder.cs @@ -1,7 +1,7 @@ using NewHorizons.External; using NewHorizons.External.Modules.Props; -using NewHorizons.External.SerializableData; using NewHorizons.External.Modules.TranslatorText; +using NewHorizons.External.SerializableData; using NewHorizons.Handlers; using NewHorizons.Utility; using NewHorizons.Utility.Geometry; @@ -14,7 +14,6 @@ using System.Linq; using System.Xml; using UnityEngine; - using Random = UnityEngine.Random; namespace NewHorizons.Builder.Props.TranslatorText @@ -26,7 +25,7 @@ public static class TranslatorTextBuilder private static Material _childArcMaterial; private static GameObject _scrollPrefab; public static GameObject ComputerPrefab { get; private set; } - private static GameObject _preCrashComputerPrefab; + public static GameObject PreCrashComputerPrefab { get; private set; } private static GameObject _cairnBHPrefab; private static GameObject _cairnTHPrefab; private static GameObject _cairnCTPrefab; @@ -89,9 +88,9 @@ internal static void InitPrefabs() ComputerPrefab = SearchUtilities.Find("VolcanicMoon_Body/Sector_VM/Interactables_VM/Prefab_NOM_Computer").InstantiateInactive().Rename("Prefab_NOM_Computer").DontDestroyOnLoad(); } - if (_preCrashComputerPrefab == null) + if (PreCrashComputerPrefab == null) { - _preCrashComputerPrefab = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_EscapePodCrashSite/Sector_CrashFragment/EscapePod_Socket/Interactibles_EscapePod/Prefab_NOM_Vessel_Computer").InstantiateInactive().Rename("Prefab_NOM_Vessel_Computer").DontDestroyOnLoad(); + PreCrashComputerPrefab = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_EscapePodCrashSite/Sector_CrashFragment/EscapePod_Socket/Interactibles_EscapePod/Prefab_NOM_Vessel_Computer").InstantiateInactive().Rename("Prefab_NOM_Vessel_Computer").DontDestroyOnLoad(); } if (_cairnBHPrefab == null) @@ -137,6 +136,11 @@ public static GameObject Make(GameObject planetGO, Sector sector, TranslatorText return null; } + return Make(planetGO, sector, info, nhBody, xmlContent); + } + + public static GameObject Make(GameObject planetGO, Sector sector, TranslatorTextInfo info, NewHorizonsBody nhBody, string xmlContent) + { switch (info.type) { case NomaiTextType.Wall: @@ -238,7 +242,7 @@ public static GameObject Make(GameObject planetGO, Sector sector, TranslatorText } case NomaiTextType.PreCrashComputer: { - var computerObject = DetailBuilder.Make(planetGO, sector, _preCrashComputerPrefab, new DetailInfo(info)); + var computerObject = DetailBuilder.Make(planetGO, sector, PreCrashComputerPrefab, new DetailInfo(info)); computerObject.SetActive(false); var computer = computerObject.GetComponent(); @@ -401,7 +405,7 @@ public static GameObject Make(GameObject planetGO, Sector sector, TranslatorText } } - private static NomaiWallText MakeWallText(GameObject go, Sector sector, TranslatorTextInfo info, string xmlPath, NewHorizonsBody nhBody) + private static NomaiWallText MakeWallText(GameObject go, Sector sector, TranslatorTextInfo info, string xmlContent, NewHorizonsBody nhBody) { GameObject nomaiWallTextObj = new GameObject("NomaiWallText"); nomaiWallTextObj.SetActive(false); @@ -418,13 +422,13 @@ private static NomaiWallText MakeWallText(GameObject go, Sector sector, Translat nomaiWallText._location = EnumUtils.Parse(info.location.ToString()); - var text = new TextAsset(xmlPath); + var text = new TextAsset(xmlContent); // Text assets need a name to be used with VoiceMod text.name = Path.GetFileNameWithoutExtension(info.xmlFile); - BuildArcs(xmlPath, nomaiWallText, nomaiWallTextObj, info, nhBody); - AddTranslation(xmlPath); + BuildArcs(xmlContent, nomaiWallText, nomaiWallTextObj, info, nhBody); + AddTranslation(xmlContent); nomaiWallText._nomaiTextAsset = text; nomaiWallText.SetTextAsset(text); @@ -542,7 +546,7 @@ internal static void RefreshArcs(NomaiWallText nomaiWallText, GameObject convers // make an entry in the cache for all these spirals - if (nhBody.Cache != null) + if (nhBody?.Cache != null) { var cacheData = arranger.spirals.Select(spiralManipulator => new ArcCacheData() { diff --git a/NewHorizons/Builder/Props/WarpPadBuilder.cs b/NewHorizons/Builder/Props/WarpPadBuilder.cs index 41bf6ed6a..4c5a9ae8b 100644 --- a/NewHorizons/Builder/Props/WarpPadBuilder.cs +++ b/NewHorizons/Builder/Props/WarpPadBuilder.cs @@ -1,5 +1,6 @@ using NewHorizons.Builder.Props.TranslatorText; using NewHorizons.Components; +using NewHorizons.External.Modules; using NewHorizons.External.Modules.Props; using NewHorizons.External.Modules.WarpPad; using NewHorizons.Utility; @@ -144,7 +145,7 @@ public static void Make(GameObject planetGO, Sector sector, NomaiWarpTransmitter transmitterObject.SetActive(true); } - private static void CreateComputer(GameObject planetGO, Sector sector, NomaiWarpComputerLoggerInfo computerInfo, NomaiWarpReceiver receiver) + private static void CreateComputer(GameObject planetGO, Sector sector, GeneralPropInfo computerInfo, NomaiWarpReceiver receiver) { var computerObject = DetailBuilder.Make(planetGO, sector, TranslatorTextBuilder.ComputerPrefab, new DetailInfo(computerInfo)); diff --git a/NewHorizons/Components/NHShuttleController.cs b/NewHorizons/Components/NHShuttleController.cs new file mode 100644 index 000000000..522bce798 --- /dev/null +++ b/NewHorizons/Components/NHShuttleController.cs @@ -0,0 +1,17 @@ +using NewHorizons.Utility; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace NewHorizons.Components +{ + public class NHShuttleController : NomaiShuttleController + { + public NomaiInterfaceSlot _neutralSlot; + public Sector _exteriorSector; + public Sector _interiorSector; + } +} diff --git a/NewHorizons/Components/PreserveActiveCenterOfTheUniverse.cs b/NewHorizons/Components/PreserveActiveCenterOfTheUniverse.cs index 0e8cd3c01..a6d658ea8 100644 --- a/NewHorizons/Components/PreserveActiveCenterOfTheUniverse.cs +++ b/NewHorizons/Components/PreserveActiveCenterOfTheUniverse.cs @@ -16,9 +16,9 @@ public static void Apply(GameObject center) public void Update() { - if (!_centerOfTheUniverse.activeInHierarchy) + if (!_centerOfTheUniverse.activeInHierarchy && !CenterOfTheUniverse.IsUniverseDeactivated()) { - NHLogger.LogWarning("Center of the universe cannot be inactive."); + NHLogger.LogWarning("Center of the universe cannot be inactive until the universe is deactivated."); _centerOfTheUniverse.SetActive(true); } } diff --git a/NewHorizons/External/Configs/TranslationConfig.cs b/NewHorizons/External/Configs/TranslationConfig.cs index 4f3371ca8..7341af9eb 100644 --- a/NewHorizons/External/Configs/TranslationConfig.cs +++ b/NewHorizons/External/Configs/TranslationConfig.cs @@ -23,6 +23,11 @@ public class TranslationConfig /// public Dictionary UIDictionary; + /// + /// Translation table for other text, will be taken verbatim without correcting CDATA + /// + public Dictionary OtherDictionary; + #region Achievements+ // This only exists for schema generation, Achievements+ handles the parsing #pragma warning disable 0169 @@ -63,6 +68,10 @@ public TranslationConfig(string filename) UIDictionary = (Dictionary) (dict[nameof(UIDictionary)] as JObject).ToObject( typeof(Dictionary)); + if (dict.ContainsKey(nameof(OtherDictionary))) + OtherDictionary = + (Dictionary)(dict[nameof(OtherDictionary)] as JObject).ToObject( + typeof(Dictionary)); } } } \ No newline at end of file diff --git a/NewHorizons/External/Modules/GeneralPropInfo.cs b/NewHorizons/External/Modules/GeneralPropInfo.cs index 5b4d9b02e..fc855f9af 100644 --- a/NewHorizons/External/Modules/GeneralPropInfo.cs +++ b/NewHorizons/External/Modules/GeneralPropInfo.cs @@ -29,7 +29,7 @@ public abstract class GeneralPointPropInfo } [JsonObject] - public abstract class GeneralPropInfo : GeneralPointPropInfo + public class GeneralPropInfo : GeneralPointPropInfo { /// /// Rotation of the object diff --git a/NewHorizons/External/Modules/PropModule.cs b/NewHorizons/External/Modules/PropModule.cs index e6bc7d5eb..b77c2f68e 100644 --- a/NewHorizons/External/Modules/PropModule.cs +++ b/NewHorizons/External/Modules/PropModule.cs @@ -4,6 +4,7 @@ using NewHorizons.External.Modules.Props.EchoesOfTheEye; using NewHorizons.External.Modules.Props.Quantum; using NewHorizons.External.Modules.Props.Remote; +using NewHorizons.External.Modules.Props.Shuttle; using NewHorizons.External.Modules.TranslatorText; using NewHorizons.External.Modules.VariableSize; using NewHorizons.External.Modules.Volumes.VolumeInfos; @@ -112,6 +113,16 @@ public class PropModule /// public AudioSourceInfo[] audioSources; + /// + /// Add a gravity cannon to this planet. Must be paired to a new shuttle, which can be placed on this planet or elsewhere. + /// + public GravityCannonInfo[] gravityCannons; + + /// + /// Add a Nomai shuttle to this planet. Can be paired to a gravity cannon on this planet or elsewhere. + /// + public ShuttleInfo[] shuttles; + [Obsolete("reveal is deprecated. Use Volumes->revealVolumes instead.")] public RevealVolumeInfo[] reveal; [Obsolete("audioVolumes is deprecated. Use Volumes->audioVolumes instead.")] public AudioVolumeInfo[] audioVolumes; diff --git a/NewHorizons/External/Modules/Props/NomaiComputerInfo.cs b/NewHorizons/External/Modules/Props/NomaiComputerInfo.cs new file mode 100644 index 000000000..6b17b0323 --- /dev/null +++ b/NewHorizons/External/Modules/Props/NomaiComputerInfo.cs @@ -0,0 +1,9 @@ +using Newtonsoft.Json; + +namespace NewHorizons.External.Modules.Props +{ + [JsonObject] + public class NomaiComputerInfo : GeneralPropInfo + { + } +} diff --git a/NewHorizons/External/Modules/Props/Shuttle/GravityCannonInfo.cs b/NewHorizons/External/Modules/Props/Shuttle/GravityCannonInfo.cs new file mode 100644 index 000000000..ffdcb0de4 --- /dev/null +++ b/NewHorizons/External/Modules/Props/Shuttle/GravityCannonInfo.cs @@ -0,0 +1,46 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; + +namespace NewHorizons.External.Modules.Props.Shuttle +{ + [JsonObject] + public class GravityCannonInfo : GeneralPropInfo + { + /// + /// Unique ID for the shuttle that pairs with this gravity cannon + /// + public string shuttleID; + + /// + /// Ship log fact revealed when retrieving the shuttle to this pad. Optional. + /// + public string retrieveReveal; + + /// + /// Ship log fact revealed when launching from this pad. Optional. + /// + public string launchReveal; + + /// + /// Hide the lattice cage around the platform. Defaults to true. + /// + [DefaultValue(true)] public bool detailed = true; + + /// + /// Will create a modern Nomai computer linked to this gravity cannon. + /// + public NomaiComputerInfo computer; + + /// + /// Position of the interface used to launc the shuttle + /// + public GeneralPropInfo controls; + } +} diff --git a/NewHorizons/External/Modules/Props/Shuttle/ShuttleInfo.cs b/NewHorizons/External/Modules/Props/Shuttle/ShuttleInfo.cs new file mode 100644 index 000000000..8bf959c1f --- /dev/null +++ b/NewHorizons/External/Modules/Props/Shuttle/ShuttleInfo.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NewHorizons.External.Modules.Props.Shuttle +{ + [JsonObject] + public class ShuttleInfo : GeneralPropInfo + { + /// + /// Unique ID for this shuttle + /// + public string id; + } +} diff --git a/NewHorizons/External/Modules/WarpPad/NomaiWarpComputerInfo.cs b/NewHorizons/External/Modules/WarpPad/NomaiWarpComputerInfo.cs deleted file mode 100644 index bd2bb6d75..000000000 --- a/NewHorizons/External/Modules/WarpPad/NomaiWarpComputerInfo.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Newtonsoft.Json; - -namespace NewHorizons.External.Modules.WarpPad -{ - [JsonObject] - public class NomaiWarpComputerLoggerInfo : GeneralPropInfo - { - } -} diff --git a/NewHorizons/External/Modules/WarpPad/NomaiWarpPadReceiverInfo.cs b/NewHorizons/External/Modules/WarpPad/NomaiWarpPadReceiverInfo.cs index 6680284c1..05b0b20aa 100644 --- a/NewHorizons/External/Modules/WarpPad/NomaiWarpPadReceiverInfo.cs +++ b/NewHorizons/External/Modules/WarpPad/NomaiWarpPadReceiverInfo.cs @@ -1,3 +1,4 @@ +using NewHorizons.External.Modules.Props; using Newtonsoft.Json; namespace NewHorizons.External.Modules.WarpPad @@ -14,7 +15,7 @@ public class NomaiWarpReceiverInfo : NomaiWarpPadInfo /// /// Will create a modern Nomai computer linked to this receiver. /// - public NomaiWarpComputerLoggerInfo computer; + public NomaiComputerInfo computer; /// /// Set to true if you want to include Nomai ruin details around the warp pad. diff --git a/NewHorizons/Handlers/PlanetCreationHandler.cs b/NewHorizons/Handlers/PlanetCreationHandler.cs index 38958aa27..ea235665e 100644 --- a/NewHorizons/Handlers/PlanetCreationHandler.cs +++ b/NewHorizons/Handlers/PlanetCreationHandler.cs @@ -36,6 +36,12 @@ public static class PlanetCreationHandler public static void Init(List bodies) { + // Start by destroying all planets if need be + if (Main.SystemDict[Main.Instance.CurrentStarSystem].Config.destroyStockPlanets) + { + PlanetDestructionHandler.RemoveStockPlanets(); + } + // Base game value SolarSystemRadius = DefaultFurthestOrbit; @@ -60,7 +66,7 @@ public static void Init(List bodies) var starLightGO = UnityEngine.Object.Instantiate(sun.GetComponentInChildren().gameObject); foreach (var comp in starLightGO.GetComponents()) { - if (!(comp is SunLightController) && !(comp is SunLightParamUpdater) && !(comp is Light) && !(comp is Transform)) + if (comp is not (SunLightController or SunLightParamUpdater or Light or Transform)) { UnityEngine.Object.Destroy(comp); } @@ -138,10 +144,6 @@ public static void Init(List bodies) NHLogger.Log("Done loading bodies"); SingularityBuilder.PairAllSingularities(); - - // Events.FireOnNextUpdate(PlanetDestroyer.RemoveAllProxies); - - if (Main.SystemDict[Main.Instance.CurrentStarSystem].Config.destroyStockPlanets) PlanetDestructionHandler.RemoveStockPlanets(); } public static bool LoadBody(NewHorizonsBody body, bool defaultPrimaryToSun = false) @@ -344,10 +346,12 @@ public static GameObject GenerateBrambleDimensionBody(NewHorizonsBody body) body.Config.Base.showMinimap = false; body.Config.Base.hasMapMarker = false; - var owRigidBody = RigidBodyBuilder.Make(go, body.Config); + const float sphereOfInfluence = 2000f; + + var owRigidBody = RigidBodyBuilder.Make(go, sphereOfInfluence); var ao = AstroObjectBuilder.Make(go, null, body.Config, false); - var sector = SectorBuilder.Make(go, owRigidBody, 2000f); + var sector = SectorBuilder.Make(go, owRigidBody, sphereOfInfluence); ao._rootSector = sector; ao._type = AstroObject.Type.None; @@ -417,10 +421,10 @@ public static GameObject GenerateStandardBody(NewHorizonsBody body, bool default }; } - var owRigidBody = RigidBodyBuilder.Make(go, body.Config); - var ao = AstroObjectBuilder.Make(go, primaryBody, body.Config, false); - var sphereOfInfluence = GetSphereOfInfluence(body); + + var owRigidBody = RigidBodyBuilder.Make(go, sphereOfInfluence); + var ao = AstroObjectBuilder.Make(go, primaryBody, body.Config, false); var sector = SectorBuilder.Make(go, owRigidBody, sphereOfInfluence * 2f); ao._rootSector = sector; diff --git a/NewHorizons/Handlers/PlanetDestructionHandler.cs b/NewHorizons/Handlers/PlanetDestructionHandler.cs index a5f7add75..bf683b916 100644 --- a/NewHorizons/Handlers/PlanetDestructionHandler.cs +++ b/NewHorizons/Handlers/PlanetDestructionHandler.cs @@ -54,20 +54,42 @@ public static void RemoveStockPlanets() public static void RemoveSolarSystem() { - // Stop the sun from killing the player if they spawn at the center of the solar system - var sunVolumes = SearchUtilities.Find("Sun_Body/Sector_SUN/Volumes_SUN"); - sunVolumes.SetActive(false); + // Adapted from EOTS thanks corby + var toDisable = new List(); - // Random shit breaks if we don't wait idk why - foreach (var name in _solarSystemBodies) + // Collect all rigid bodies and proxies + foreach (var rigidbody in CenterOfTheUniverse.s_rigidbodies) { - var ao = AstroObjectLocator.GetAstroObject(name); - if (ao != null) Delay.FireInNUpdates(() => RemoveBody(ao, false), 2); - else NHLogger.LogError($"Couldn't find [{name}]"); + if (rigidbody.name is not ("Player_Body" or "Probe_Body" or "Ship_Body")) + { + toDisable.Add(rigidbody.gameObject); + } } - // Bring the sun back - Delay.FireInNUpdates(() => { if (Locator.GetAstroObject(AstroObject.Name.Sun).gameObject.activeInHierarchy) { sunVolumes.SetActive(true); } }, 3); + foreach (var proxyBody in GameObject.FindObjectsOfType()) + { + toDisable.Add(proxyBody.gameObject); + } + + Delay.FireInNUpdates(() => + { + foreach (var gameObject in toDisable) + { + gameObject.SetActive(false); + } + GameObject.FindObjectOfType().gameObject.SetActive(false); + + // force call update here to make it switch to an active star. idk why we didnt have to do this before + SunLightEffectsController.Instance.Update(); + + // Since we didn't call RemoveBody on the Stranger have to call this here + StrangerRemoved(); + }, 2); // Have to wait or shit goes wild + + foreach (var streamingAssetBundle in StreamingManager.s_activeBundles) + { + //streamingAssetBundle.Unload(); + } } public static void RemoveEyeOfTheUniverse() @@ -80,14 +102,23 @@ public static void RemoveEyeOfTheUniverse() } } + public static void StrangerRemoved() + { + CloakHandler.FlagStrangerDisabled = true; + + if (Locator._cloakFieldController?.GetComponentInParent()?.GetAstroObjectName() == AstroObject.Name.RingWorld) + { + Locator._cloakFieldController = null; + } + } + public static void RemoveBody(AstroObject ao, bool delete = false, List toDestroy = null) { NHLogger.LogVerbose($"Removing [{ao.name}]"); if (ao.GetAstroObjectName() == AstroObject.Name.RingWorld) { - CloakHandler.FlagStrangerDisabled = true; - if (Locator._cloakFieldController?.GetComponentInParent() == ao) Locator._cloakFieldController = null; + StrangerRemoved(); } if (ao.gameObject == null || !ao.gameObject.activeInHierarchy) diff --git a/NewHorizons/Handlers/ShuttleHandler.cs b/NewHorizons/Handlers/ShuttleHandler.cs new file mode 100644 index 000000000..60f4493fc --- /dev/null +++ b/NewHorizons/Handlers/ShuttleHandler.cs @@ -0,0 +1,36 @@ +using NewHorizons.Utility.OWML; +using OWML.Utils; +using System; + +namespace NewHorizons.Handlers +{ + public static class ShuttleHandler + { + public static NomaiShuttleController.ShuttleID GetShuttleID(string id) + { + try + { + if (EnumUtils.TryParse(id, out NomaiShuttleController.ShuttleID shuttleID)) + { + return shuttleID; + } + else + { + return AddCustomShuttleID(id); + } + } + catch (Exception e) + { + NHLogger.LogError($"Couldn't load shuttle id [{id}]:\n{e}"); + return EnumUtils.FromObject(-1); + } + } + + public static NomaiShuttleController.ShuttleID AddCustomShuttleID(string id) + { + NHLogger.LogVerbose($"Registering new shuttle id [{id}]"); + + return EnumUtilities.Create(id); + } + } +} diff --git a/NewHorizons/Handlers/TranslationHandler.cs b/NewHorizons/Handlers/TranslationHandler.cs index 3447c7259..2db44131b 100644 --- a/NewHorizons/Handlers/TranslationHandler.cs +++ b/NewHorizons/Handlers/TranslationHandler.cs @@ -8,15 +8,17 @@ namespace NewHorizons.Handlers { public static class TranslationHandler { - private static Dictionary> _shipLogTranslationDictionary = new Dictionary>(); - private static Dictionary> _dialogueTranslationDictionary = new Dictionary>(); - private static Dictionary> _uiTranslationDictionary = new Dictionary>(); + private static Dictionary> _shipLogTranslationDictionary = new(); + private static Dictionary> _dialogueTranslationDictionary = new(); + private static Dictionary> _uiTranslationDictionary = new(); + private static Dictionary> _otherTranslationDictionary = new(); public enum TextType { SHIPLOG, DIALOGUE, - UI + UI, + OTHER } public static string GetTranslation(string text, TextType type) => GetTranslation(text, type, true); @@ -36,6 +38,9 @@ public static string GetTranslation(string text, TextType type, bool warn) break; case TextType.UI: dictionary = _uiTranslationDictionary; + break; + case TextType.OTHER: + dictionary = _otherTranslationDictionary; break; default: if (warn) NHLogger.LogVerbose($"Invalid TextType {type}"); @@ -100,6 +105,20 @@ public static void RegisterTranslation(TextTranslation.Language language, Transl else _uiTranslationDictionary[language][key] = value; } } + + if (config.OtherDictionary != null && config.OtherDictionary.Count() > 0) + { + if (!_otherTranslationDictionary.ContainsKey(language)) _otherTranslationDictionary.Add(language, new Dictionary()); + foreach (var originalKey in config.OtherDictionary.Keys) + { + // Don't remove CDATA + var key = originalKey.Replace("<", "<").Replace(">", ">"); + var value = config.OtherDictionary[originalKey].Replace("<", "<").Replace(">", ">"); + + if (!_otherTranslationDictionary[language].ContainsKey(key)) _otherTranslationDictionary[language].Add(key, value); + else _otherTranslationDictionary[language][key] = value; + } + } } public static void AddDialogue(string rawText, bool trimRawTextForKey = false, params string[] rawPreText) diff --git a/NewHorizons/INewHorizons.cs b/NewHorizons/INewHorizons.cs index 717819c09..c4447d282 100644 --- a/NewHorizons/INewHorizons.cs +++ b/NewHorizons/INewHorizons.cs @@ -147,6 +147,15 @@ public interface INewHorizons /// (CharacterDialogueTree, RemoteDialogueTrigger) CreateDialogueFromXML(string textAssetID, string xml, string dialogueInfo, GameObject planetGO); + /// + /// Allows the creation of Nomai text by directly passing the xml and translatorTextInfo json contents as strings + /// + /// The contents of the translator text file as a string + /// The json translator text info as a string. See the documentation/schema for what this can contain. + /// The root planet rigidbody that this text is attached to. Any paths in the translatorTextInfo are relative to this body. + /// + GameObject CreateNomaiText(string xml, string textInfo, GameObject planetGO); + /// /// Directly add ship logs from XML. Call this method right before ShipLogManager awake. /// diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index 499c2468a..f2d5c7e7e 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -314,6 +314,8 @@ private void OnSceneLoaded(Scene scene, LoadSceneMode mode) VolcanoBuilder.InitPrefab(); VolumesBuilder.InitPrefabs(); WaterBuilder.InitPrefabs(); + GravityCannonBuilder.InitPrefab(); + ShuttleBuilder.InitPrefab(); ProjectionBuilder.InitPrefabs(); CloakBuilder.InitPrefab(); @@ -547,7 +549,7 @@ private void OnSceneLoaded(Scene scene, LoadSceneMode mode) } // Wait for player to be awake and also for frames to pass - Delay.RunWhenAndInNUpdates(() => OnSystemReady(DidWarpFromShip, DidWarpFromVessel), () => _playerAwake && PlayerSpawned, 30); + Delay.RunWhenOrInNUpdates(() => OnSystemReady(DidWarpFromShip, DidWarpFromVessel), () => _playerAwake && PlayerSpawned, 30); } else { @@ -811,7 +813,14 @@ public NewHorizonsBody RegisterPlanetConfig(PlanetConfig config, IModBehaviour m public void SetDefaultSystem(string defaultSystem) { - _defaultStarSystem = defaultSystem; + if (string.IsNullOrEmpty(defaultSystem)) + { + _defaultStarSystem = "SolarSystem"; + } + else + { + _defaultStarSystem = defaultSystem; + } } #endregion Load diff --git a/NewHorizons/NewHorizonsApi.cs b/NewHorizons/NewHorizonsApi.cs index e395bfa18..5eaf322eb 100644 --- a/NewHorizons/NewHorizonsApi.cs +++ b/NewHorizons/NewHorizonsApi.cs @@ -1,5 +1,6 @@ using NewHorizons.Builder.Props; using NewHorizons.Builder.Props.Audio; +using NewHorizons.Builder.Props.TranslatorText; using NewHorizons.Builder.ShipLog; using NewHorizons.External; using NewHorizons.External.Configs; @@ -7,6 +8,7 @@ using NewHorizons.External.Modules.Props; using NewHorizons.External.Modules.Props.Audio; using NewHorizons.External.Modules.Props.Dialogue; +using NewHorizons.External.Modules.TranslatorText; using NewHorizons.External.SerializableData; using NewHorizons.Utility; using NewHorizons.Utility.OWML; @@ -259,6 +261,12 @@ public void DefineStarSystem(string name, string config, IModBehaviour mod) return DialogueBuilder.Make(planetGO, null, info, xml, textAssetID); } + public GameObject CreateNomaiText(string xml, string textInfo, GameObject planetGO) + { + var info = JsonConvert.DeserializeObject(textInfo); + return TranslatorTextBuilder.Make(planetGO, null, info, null, xml); + } + public void AddShipLogXML(IModBehaviour mod, XElement xml, string planetName, string imageFolder, Dictionary entryPositions, Dictionary curiousityColours) { // This method has to be called each time the ship log manager is created, i.e. each time a system loads so it will only ever be relevant to the current one. diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index ce5d26838..62a2069cb 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -1238,6 +1238,20 @@ "items": { "$ref": "#/definitions/AudioSourceInfo" } + }, + "gravityCannons": { + "type": "array", + "description": "Add a gravity cannon to this planet. Must be paired to a new shuttle, which can be placed on this planet or elsewhere.", + "items": { + "$ref": "#/definitions/GravityCannonInfo" + } + }, + "shuttles": { + "type": "array", + "description": "Add a Nomai shuttle to this planet. Can be paired to a gravity cannon on this planet or elsewhere.", + "items": { + "$ref": "#/definitions/ShuttleInfo" + } } } }, @@ -2696,7 +2710,7 @@ }, "computer": { "description": "Will create a modern Nomai computer linked to this receiver.", - "$ref": "#/definitions/NomaiWarpComputerLoggerInfo" + "$ref": "#/definitions/NomaiComputerInfo" }, "detailed": { "type": "boolean", @@ -2704,7 +2718,7 @@ } } }, - "NomaiWarpComputerLoggerInfo": { + "NomaiComputerInfo": { "type": "object", "additionalProperties": false, "properties": { @@ -2872,6 +2886,134 @@ "slideReelMusic" ] }, + "GravityCannonInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "rotation": { + "description": "Rotation of the object", + "$ref": "#/definitions/MVector3" + }, + "alignRadial": { + "type": [ + "boolean", + "null" + ], + "description": "Do we try to automatically align this object to stand upright relative to the body's center? Stacks with rotation.\nDefaults to true for geysers, tornados, and volcanoes, and false for everything else." + }, + "position": { + "description": "Position of the object", + "$ref": "#/definitions/MVector3" + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "isRelativeToParent": { + "type": "boolean", + "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." + }, + "rename": { + "type": "string", + "description": "An optional rename of this object" + }, + "shuttleID": { + "type": "string", + "description": "Unique ID for the shuttle that pairs with this gravity cannon" + }, + "retrieveReveal": { + "type": "string", + "description": "Ship log fact revealed when retrieving the shuttle to this pad. Optional." + }, + "launchReveal": { + "type": "string", + "description": "Ship log fact revealed when launching from this pad. Optional." + }, + "detailed": { + "type": "boolean", + "description": "Hide the lattice cage around the platform. Defaults to true.", + "default": true + }, + "computer": { + "description": "Will create a modern Nomai computer linked to this gravity cannon.", + "$ref": "#/definitions/NomaiComputerInfo" + }, + "controls": { + "description": "Position of the interface used to launc the shuttle", + "$ref": "#/definitions/GeneralPropInfo" + } + } + }, + "GeneralPropInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "position": { + "description": "Position of the object", + "$ref": "#/definitions/MVector3" + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "isRelativeToParent": { + "type": "boolean", + "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." + }, + "rename": { + "type": "string", + "description": "An optional rename of this object" + }, + "rotation": { + "description": "Rotation of the object", + "$ref": "#/definitions/MVector3" + }, + "alignRadial": { + "type": [ + "boolean", + "null" + ], + "description": "Do we try to automatically align this object to stand upright relative to the body's center? Stacks with rotation.\nDefaults to true for geysers, tornados, and volcanoes, and false for everything else." + } + } + }, + "ShuttleInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "rotation": { + "description": "Rotation of the object", + "$ref": "#/definitions/MVector3" + }, + "alignRadial": { + "type": [ + "boolean", + "null" + ], + "description": "Do we try to automatically align this object to stand upright relative to the body's center? Stacks with rotation.\nDefaults to true for geysers, tornados, and volcanoes, and false for everything else." + }, + "position": { + "description": "Position of the object", + "$ref": "#/definitions/MVector3" + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "isRelativeToParent": { + "type": "boolean", + "description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object." + }, + "rename": { + "type": "string", + "description": "An optional rename of this object" + }, + "id": { + "type": "string", + "description": "Unique ID for this shuttle" + } + } + }, "ReferenceFrameModule": { "type": "object", "additionalProperties": false, diff --git a/NewHorizons/Schemas/translation_schema.json b/NewHorizons/Schemas/translation_schema.json index 92a114ef2..da210c418 100644 --- a/NewHorizons/Schemas/translation_schema.json +++ b/NewHorizons/Schemas/translation_schema.json @@ -25,6 +25,13 @@ "type": "string" } }, + "OtherDictionary": { + "type": "object", + "description": "Translation table for other text, will be taken verbatim without correcting CDATA", + "additionalProperties": { + "type": "string" + } + }, "AchievementTranslations": { "type": "object", "description": "Translation table for achievements. The key is the unique ID of the achievement", diff --git a/NewHorizons/Utility/OWML/Delay.cs b/NewHorizons/Utility/OWML/Delay.cs index e25dc9287..ea400a651 100644 --- a/NewHorizons/Utility/OWML/Delay.cs +++ b/NewHorizons/Utility/OWML/Delay.cs @@ -22,7 +22,8 @@ public static class Delay public static void FireOnNextUpdate(Action action) => FireInNUpdates(action, 1); - public static void RunWhenAndInNUpdates(Action action, Func predicate, int n) => Delay.StartCoroutine(RunWhenOrInNUpdatesCoroutine(action, predicate, n)); + public static void RunWhenOrInNUpdates(Action action, Func predicate, int n) => Delay.StartCoroutine(RunWhenOrInNUpdatesCoroutine(action, predicate, n)); + public static void RunWhenAndInNUpdates(Action action, Func predicate, int n) => Delay.StartCoroutine(RunWhenAndInNUpdatesCoroutine(action, predicate, n)); #endregion #region Coroutines @@ -58,6 +59,20 @@ private static IEnumerator RunWhenOrInNUpdatesCoroutine(Action action, Func predicate, int n) + { + while (!predicate.Invoke()) + { + yield return new WaitForEndOfFrame(); + } + for (int i = 0; i < n; i++) + { + yield return new WaitForEndOfFrame(); + } + + action.Invoke(); + } #endregion } } diff --git a/NewHorizons/Utility/OuterWilds/AstroObjectLocator.cs b/NewHorizons/Utility/OuterWilds/AstroObjectLocator.cs index 88d46e517..b1aba93ec 100644 --- a/NewHorizons/Utility/OuterWilds/AstroObjectLocator.cs +++ b/NewHorizons/Utility/OuterWilds/AstroObjectLocator.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; using System.Linq; +using NewHorizons.Components.Orbital; +using NewHorizons.Handlers; using NewHorizons.Utility.OWML; using UnityEngine; @@ -177,5 +179,32 @@ public static GameObject[] GetChildren(AstroObject primary) return otherChildren.ToArray(); } + + public static string GetPlanetName(AstroObject astroObject) + { + if (astroObject != null) + { + if (astroObject is NHAstroObject nhAstroObject) + { + var customName = nhAstroObject.GetCustomName(); + + if (!string.IsNullOrWhiteSpace(customName)) + { + return TranslationHandler.GetTranslation(customName, TranslationHandler.TextType.UI, false); + } + } + else + { + AstroObject.Name astroObjectName = astroObject.GetAstroObjectName(); + + if (astroObjectName - AstroObject.Name.Sun <= 7 || astroObjectName - AstroObject.Name.TimberMoon <= 1) + { + return AstroObject.AstroObjectNameToString(astroObject.GetAstroObjectName()); + } + } + } + + return "???"; + } } } diff --git a/NewHorizons/manifest.json b/NewHorizons/manifest.json index 273520daf..56cf858bc 100644 --- a/NewHorizons/manifest.json +++ b/NewHorizons/manifest.json @@ -4,7 +4,7 @@ "author": "xen, Bwc9876, clay, MegaPiggy, John, Trifid, Hawkbar, Book", "name": "New Horizons", "uniqueName": "xen.NewHorizons", - "version": "1.13.0", + "version": "1.14.0", "owmlVersion": "2.9.3", "dependencies": [ "JohnCorby.VanillaFix", "_nebula.MenuFramework", "xen.CommonCameraUtility", "dgarro.CustomShipLogModes" ], "conflicts": [ "Raicuparta.QuantumSpaceBuddies", "PacificEngine.OW_CommonResources" ],