diff --git a/NewHorizons/Builder/Atmosphere/EffectsBuilder.cs b/NewHorizons/Builder/Atmosphere/EffectsBuilder.cs index 3b11b857..e06c1800 100644 --- a/NewHorizons/Builder/Atmosphere/EffectsBuilder.cs +++ b/NewHorizons/Builder/Atmosphere/EffectsBuilder.cs @@ -1,4 +1,5 @@ using NewHorizons.External.Configs; +using NewHorizons.External.Modules; using NewHorizons.Utility; using UnityEngine; namespace NewHorizons.Builder.Atmosphere @@ -7,6 +8,21 @@ namespace NewHorizons.Builder.Atmosphere { private static GameObject _rainEmitterPrefab; private static GameObject _snowEmitterPrefab; + private static GameObject _snowLightEmitterPrefab; + private static GameObject _emberEmitterPrefab; + private static GameObject _leafEmitterPrefab; + private static GameObject _planktonEmitterPrefab; + private static GameObject _bubbleEmitterPrefab; + private static GameObject _currentEmitterPrefab; + private static GameObject _cloudEmitterPrefab; + private static GameObject _crawliesEmitterPrefab; + private static GameObject _firefliesEmitterPrefab; + private static GameObject _pollenEmitterPrefab; + private static GameObject _iceMoteEmitterPrefab; + private static GameObject _rockMoteEmitterPrefab; + private static GameObject _crystalMoteEmitterPrefab; + private static GameObject _sandMoteEmitterPrefab; + private static GameObject _fogEmitterPrefab; private static bool _isInit; @@ -18,9 +34,24 @@ namespace NewHorizons.Builder.Atmosphere if (_rainEmitterPrefab == null) _rainEmitterPrefab = SearchUtilities.Find("GiantsDeep_Body/Sector_GD/Sector_GDInterior/Effects_GDInterior/Effects_GD_Rain").InstantiateInactive().Rename("Prefab_Effects_Rain").DontDestroyOnLoad(); if (_snowEmitterPrefab == null) _snowEmitterPrefab = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Effects_BH/Effects_BH_Snowflakes").InstantiateInactive().Rename("Prefab_Effects_Snowflakes").DontDestroyOnLoad(); + if (_snowLightEmitterPrefab == null) _snowLightEmitterPrefab = SearchUtilities.Find("TimberHearth_Body/Sector_TH/Effects_TH/Effects_TH_Snowflakes").InstantiateInactive().Rename("Prefab_Effects_SnowflakesLight").DontDestroyOnLoad(); + if (_emberEmitterPrefab == null) _emberEmitterPrefab = SearchUtilities.Find("VolcanicMoon_Body/Sector_VM/Effects_VM/Effects_VM_Embers").InstantiateInactive().Rename("Prefab_Effects_Embers").DontDestroyOnLoad(); + if (_leafEmitterPrefab == null) _leafEmitterPrefab = SearchUtilities.Find("GiantsDeep_Body/Sector_GD/Sector_GDInterior/Effects_GDInterior/Effects_GD_Leaves").InstantiateInactive().Rename("Prefab_Effects_Leaves").DontDestroyOnLoad(); + if (_planktonEmitterPrefab == null) _planktonEmitterPrefab = SearchUtilities.Find("GiantsDeep_Body/Sector_GD/Sector_GDInterior/Effects_GDInterior/Effects_GD_Plankton").InstantiateInactive().Rename("Prefab_Effects_Plankton").DontDestroyOnLoad(); + if (_bubbleEmitterPrefab == null) _bubbleEmitterPrefab = SearchUtilities.Find("GiantsDeep_Body/Sector_GD/Sector_GDInterior/Effects_GDInterior/Effects_GD_Bubbles").InstantiateInactive().Rename("Prefab_Effects_Bubbles").DontDestroyOnLoad(); + if (_currentEmitterPrefab == null) _currentEmitterPrefab = SearchUtilities.Find("GiantsDeep_Body/Sector_GD/Sector_GDInterior/Effects_GDInterior/Effects_GD_RadialCurrent").InstantiateInactive().Rename("Prefab_Effects_Current").DontDestroyOnLoad(); + if (_cloudEmitterPrefab == null) _cloudEmitterPrefab = SearchUtilities.Find("GiantsDeep_Body/Sector_GD/Effects_GD/Effects_GD_Clouds").InstantiateInactive().Rename("Prefab_Effects_Clouds").DontDestroyOnLoad(); + if (_crawliesEmitterPrefab == null) _crawliesEmitterPrefab = SearchUtilities.Find("DB_EscapePodDimension_Body/Sector_EscapePodDimension/Effects_EscapePodDimension/Effects_DB_Crawlies (1)").InstantiateInactive().Rename("Prefab_Effects_Crawlies").DontDestroyOnLoad(); + if (_firefliesEmitterPrefab == null) _firefliesEmitterPrefab = SearchUtilities.Find("TimberHearth_Body/Sector_TH/Effects_TH/Effects_TH_Fireflies").InstantiateInactive().Rename("Prefab_Effects_Fireflies").DontDestroyOnLoad(); + if (_pollenEmitterPrefab == null) _pollenEmitterPrefab = SearchUtilities.Find("TimberHearth_Body/Sector_TH/Effects_TH/Effects_TH_SurfacePollen").InstantiateInactive().Rename("Prefab_Effects_Pollen").DontDestroyOnLoad(); + if (_iceMoteEmitterPrefab == null) _iceMoteEmitterPrefab = SearchUtilities.Find("DarkBramble_Body/Effects_DB/Effects_DB_IceMotes").InstantiateInactive().Rename("Prefab_Effects_IceMotes").DontDestroyOnLoad(); + if (_rockMoteEmitterPrefab == null) _rockMoteEmitterPrefab = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Effects_BH/Effects_BH_RockMotes").InstantiateInactive().Rename("Prefab_Effects_RockMotes").DontDestroyOnLoad(); + if (_crystalMoteEmitterPrefab == null) _crystalMoteEmitterPrefab = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Effects_BH/Effects_BH_CrystalMotes").InstantiateInactive().Rename("Prefab_Effects_CrystalMotes").DontDestroyOnLoad(); + if (_sandMoteEmitterPrefab == null) _sandMoteEmitterPrefab = SearchUtilities.Find("CaveTwin_Body/Sector_CaveTwin/Effects_CaveTwin/Effects_HGT_SandMotes").InstantiateInactive().Rename("Prefab_Effects_SandMotes").DontDestroyOnLoad(); + if (_fogEmitterPrefab == null) _fogEmitterPrefab = SearchUtilities.Find("DB_EscapePodDimension_Body/Sector_EscapePodDimension/Effects_EscapePodDimension/Effects_DB_Fog (1)").InstantiateInactive().Rename("Prefab_Effects_Fog").DontDestroyOnLoad(); } - public static void Make(GameObject planetGO, Sector sector, PlanetConfig config, float surfaceSize) + public static void Make(GameObject planetGO, Sector sector, PlanetConfig config) { InitPrefabs(); @@ -36,7 +67,7 @@ namespace NewHorizons.Builder.Atmosphere SCG._dynamicCullingBounds = false; SCG._waitForStreaming = false; - var minHeight = surfaceSize; + var minHeight = config.Base.surfaceSize; if (config.HeightMap?.minHeight != null) { if (config.Water?.size >= config.HeightMap.minHeight) minHeight = config.Water.size; // use sea level if its higher @@ -48,52 +79,56 @@ namespace NewHorizons.Builder.Atmosphere var maxHeight = config.Atmosphere.size; if (config.Atmosphere.clouds?.outerCloudRadius != null) maxHeight = config.Atmosphere.clouds.outerCloudRadius; - if (config.Atmosphere.hasRain) + foreach (var particleField in config.ParticleFields) { - var rainGO = Object.Instantiate(_rainEmitterPrefab, effectsGO.transform); - rainGO.name = "RainEmitter"; - rainGO.transform.position = planetGO.transform.position; + var prefab = GetPrefabByType(particleField.type); + var emitter = Object.Instantiate(prefab, effectsGO.transform); + emitter.name = !string.IsNullOrWhiteSpace(particleField.rename) ? particleField.rename : prefab.name.Replace("Prefab_", ""); + emitter.transform.position = planetGO.transform.position; - var pvc = rainGO.GetComponent(); - pvc._densityByHeight = new AnimationCurve(new Keyframe[] + var vfe = emitter.GetComponent(); + var pvc = emitter.GetComponent(); + pvc._vectionFieldEmitter = vfe; + pvc._densityByHeight = particleField.densityByHeightCurve != null ? particleField.densityByHeightCurve.ToAnimationCurve() : new AnimationCurve(new Keyframe[] { new Keyframe(minHeight - 0.5f, 0), new Keyframe(minHeight, 10f), new Keyframe(maxHeight, 0f) }); + pvc._followTarget = particleField.followTarget == ParticleFieldModule.FollowTarget.Probe ? PlanetaryVectionController.FollowTarget.Probe : PlanetaryVectionController.FollowTarget.Player; + pvc._activeInSector = sector; + pvc._exclusionSectors = new Sector[] { }; - rainGO.GetComponent()._activeInSector = sector; - rainGO.GetComponent()._exclusionSectors = new Sector[] { }; - rainGO.SetActive(true); - } - - if (config.Atmosphere.hasSnow) - { - var snowGO = new GameObject("SnowEffects"); - snowGO.transform.parent = effectsGO.transform; - snowGO.transform.position = planetGO.transform.position; - for (int i = 0; i < 5; i++) - { - var snowEmitter = Object.Instantiate(_snowEmitterPrefab, snowGO.transform); - snowEmitter.name = "SnowEmitter"; - snowEmitter.transform.position = planetGO.transform.position; - - var pvc = snowEmitter.GetComponent(); - pvc._densityByHeight = new AnimationCurve(new Keyframe[] - { - new Keyframe(minHeight - 0.5f, 0), - new Keyframe(minHeight, 10f), - new Keyframe(maxHeight, 0f) - }); - - snowEmitter.GetComponent()._activeInSector = sector; - snowEmitter.GetComponent()._exclusionSectors = new Sector[] { }; - snowEmitter.SetActive(true); - } + emitter.SetActive(true); } effectsGO.transform.position = planetGO.transform.position; effectsGO.SetActive(true); } + + public static GameObject GetPrefabByType(ParticleFieldModule.ParticleFieldType type) + { + return type switch + { + ParticleFieldModule.ParticleFieldType.Rain => _rainEmitterPrefab, + ParticleFieldModule.ParticleFieldType.SnowflakesHeavy => _snowEmitterPrefab, + ParticleFieldModule.ParticleFieldType.SnowflakesLight => _snowLightEmitterPrefab, + ParticleFieldModule.ParticleFieldType.Embers => _emberEmitterPrefab, + ParticleFieldModule.ParticleFieldType.Clouds => _cloudEmitterPrefab, + ParticleFieldModule.ParticleFieldType.Leaves => _leafEmitterPrefab, + ParticleFieldModule.ParticleFieldType.Bubbles => _bubbleEmitterPrefab, + ParticleFieldModule.ParticleFieldType.Fog => _fogEmitterPrefab, + ParticleFieldModule.ParticleFieldType.CrystalMotes => _crystalMoteEmitterPrefab, + ParticleFieldModule.ParticleFieldType.RockMotes => _rockMoteEmitterPrefab, + ParticleFieldModule.ParticleFieldType.IceMotes => _iceMoteEmitterPrefab, + ParticleFieldModule.ParticleFieldType.SandMotes => _sandMoteEmitterPrefab, + ParticleFieldModule.ParticleFieldType.Crawlies => _crawliesEmitterPrefab, + ParticleFieldModule.ParticleFieldType.Fireflies => _firefliesEmitterPrefab, + ParticleFieldModule.ParticleFieldType.Plankton => _planktonEmitterPrefab, + ParticleFieldModule.ParticleFieldType.Pollen => _pollenEmitterPrefab, + ParticleFieldModule.ParticleFieldType.Current => _currentEmitterPrefab, + _ => null, + }; + } } } diff --git a/NewHorizons/Builder/Body/WaterBuilder.cs b/NewHorizons/Builder/Body/WaterBuilder.cs index 3039d6dd..c525099e 100644 --- a/NewHorizons/Builder/Body/WaterBuilder.cs +++ b/NewHorizons/Builder/Body/WaterBuilder.cs @@ -5,6 +5,9 @@ using NewHorizons.External.Modules.VariableSize; using Tessellation; using NewHorizons.Utility.OWML; using NewHorizons.Utility.OuterWilds; +using NewHorizons.External.Configs; +using NewHorizons.Components.Volumes; +using System.Linq; namespace NewHorizons.Builder.Body { @@ -41,10 +44,12 @@ namespace NewHorizons.Builder.Body if (_oceanAmbientLight == null) _oceanAmbientLight = SearchUtilities.Find("Ocean_GD").GetComponent()._ambientLight.gameObject.InstantiateInactive().Rename("OceanAmbientLight").DontDestroyOnLoad(); } - public static RadialFluidVolume Make(GameObject planetGO, Sector sector, OWRigidbody rb, WaterModule module) + public static RadialFluidVolume Make(GameObject planetGO, Sector sector, OWRigidbody rb, PlanetConfig config) { InitPrefabs(); + var module = config.Water; + var waterSize = module.size; GameObject waterGO = new GameObject("Water"); @@ -154,6 +159,11 @@ namespace NewHorizons.Builder.Body fogGO.GetComponent().material.SetFloat("_Radius2", 0); } + if (config.Cloak != null) + { + fluidVolume.gameObject.AddComponent().material = TSR.sharedMaterials.First(x => x.name == "Ocean_GD_Surface_mat"); + } + // TODO: fix ruleset making the sand bubble pop up waterGO.transform.position = planetGO.transform.position; diff --git a/NewHorizons/Builder/Orbital/OrbitlineBuilder.cs b/NewHorizons/Builder/Orbital/OrbitlineBuilder.cs index a2ffbab0..ae3aff08 100644 --- a/NewHorizons/Builder/Orbital/OrbitlineBuilder.cs +++ b/NewHorizons/Builder/Orbital/OrbitlineBuilder.cs @@ -61,15 +61,17 @@ namespace NewHorizons.Builder.Orbital var fade = isMoon; - /* - if (config.Base.IsSatellite) + if (config.Orbit.orbitLineFadeStartDistance >= 0) { - if (config.Orbit.Tint != null) color = new Color(0.4082f, 0.516f, 0.4469f, 1f); fade = true; - orbitLine._fadeEndDist = 5000; - orbitLine._fadeStartDist = 3000; + orbitLine._fadeStartDist = config.Orbit.orbitLineFadeStartDistance; + } + + if (config.Orbit.orbitLineFadeEndDistance >= 0) + { + fade = true; + orbitLine._fadeEndDist = config.Orbit.orbitLineFadeEndDistance; } - */ orbitLine._color = color; lineRenderer.endColor = new Color(color.r, color.g, color.b, 0f); diff --git a/NewHorizons/Builder/Props/Audio/SignalBuilder.cs b/NewHorizons/Builder/Props/Audio/SignalBuilder.cs index e5e0b885..66a86061 100644 --- a/NewHorizons/Builder/Props/Audio/SignalBuilder.cs +++ b/NewHorizons/Builder/Props/Audio/SignalBuilder.cs @@ -41,7 +41,9 @@ namespace NewHorizons.Builder.Props.Audio Initialized = true; + SceneManager.sceneUnloaded -= OnSceneUnloaded; SceneManager.sceneUnloaded += OnSceneUnloaded; + Main.Instance.OnStarSystemLoaded.RemoveListener(OnStarSystemLoaded); Main.Instance.OnStarSystemLoaded.AddListener(OnStarSystemLoaded); } diff --git a/NewHorizons/Builder/Props/BrambleNodeBuilder.cs b/NewHorizons/Builder/Props/BrambleNodeBuilder.cs index bbad4250..c02837c8 100644 --- a/NewHorizons/Builder/Props/BrambleNodeBuilder.cs +++ b/NewHorizons/Builder/Props/BrambleNodeBuilder.cs @@ -257,10 +257,20 @@ namespace NewHorizons.Builder.Props // Seed fog works differently, so it doesn't need to be fixed // (it's also located on a different child path, so the below FindChild calls wouldn't work) + // Default size is 70 var fog = brambleNode.FindChild("Effects/InnerWarpFogSphere"); - var fogMaterial = fog.GetComponent().material; - fogMaterial.SetFloat("_Radius", fogMaterial.GetFloat("_Radius") * config.scale); - fogMaterial.SetFloat("_Density", fogMaterial.GetFloat("_Density") / config.scale); + fog.transform.localScale = Vector3.one * config.scale * 70f; + + // Copy shared material to not be shared + var fogRenderer = fog.GetComponent(); + fogRenderer.material = new Material(fogRenderer.sharedMaterial); + fogRenderer.material.SetFloat("_Radius", fogRenderer.material.GetFloat("_Radius") * config.scale); + fogRenderer.material.SetFloat("_Density", fogRenderer.material.GetFloat("_Density") / config.scale); + // Fixes bramble nodes being a weird colour until you approach the first time #372 + if (config.fogTint != null) + { + fog.GetComponent().SetColor(config.fogTint.ToColor()); + } } // Set colors @@ -391,7 +401,8 @@ namespace NewHorizons.Builder.Props } } - StreamingHandler.SetUpStreaming(brambleNode, sector); + // If the outer fog warp volume is null we're exposed to the solar system so treat it as a keepLoaded type prop + StreamingHandler.SetUpStreaming(brambleNode, outerFogWarpVolume == null ? null : sector); // Done! brambleNode.SetActive(true); diff --git a/NewHorizons/Builder/Props/DetailBuilder.cs b/NewHorizons/Builder/Props/DetailBuilder.cs index eeef8d1b..5e9c80d9 100644 --- a/NewHorizons/Builder/Props/DetailBuilder.cs +++ b/NewHorizons/Builder/Props/DetailBuilder.cs @@ -116,25 +116,33 @@ namespace NewHorizons.Builder.Props foreach (var component in prop.GetComponentsInChildren(true)) { - // Components can come through as null here (yes, really), - // Usually if a script was added to a prefab in an asset bundle but isn't present in the loaded mod DLLs - if (component == null) + // Rather than having the entire prop not exist when a detail fails let's just try-catch and log an error + try { - invalidComponentFound = true; - continue; - } - if (component.gameObject == prop && component is OWItem) isItem = true; + // Components can come through as null here (yes, really), + // Usually if a script was added to a prefab in an asset bundle but isn't present in the loaded mod DLLs + if (component == null) + { + invalidComponentFound = true; + continue; + } + if (component.gameObject == prop && component is OWItem) isItem = true; - if (sector == null) - { - if (FixUnsectoredComponent(component)) continue; - } - else - { - FixSectoredComponent(component, sector, existingSectors, detail.keepLoaded); - } + if (sector == null) + { + if (FixUnsectoredComponent(component)) continue; + } + else + { + FixSectoredComponent(component, sector, existingSectors, detail.keepLoaded); + } - FixComponent(component, go, detail.ignoreSun); + FixComponent(component, go, detail.ignoreSun); + } + catch(Exception e) + { + NHLogger.LogError($"Failed to correct component {component?.GetType()?.Name} on {go?.name} - {e}"); + } } if (detail.path != null) @@ -155,7 +163,7 @@ namespace NewHorizons.Builder.Props } } - // Items shouldn't use these else they get weird + // Items should always be kept loaded else they will vanish in your hand as you leave the sector if (isItem) detail.keepLoaded = true; prop.transform.localScale = detail.stretch ?? (detail.scale != 0 ? Vector3.one * detail.scale : prefab.transform.localScale); @@ -374,7 +382,7 @@ namespace NewHorizons.Builder.Props else if (component is NomaiInterfaceOrb orb) { // detect planet gravity - var gravityVolume = planetGO.GetAttachedOWRigidbody().GetAttachedGravityVolume(); + var gravityVolume = planetGO.GetAttachedOWRigidbody()?.GetAttachedGravityVolume(); orb.GetComponent()._detectableFields = gravityVolume ? new ForceVolume[] { gravityVolume } : new ForceVolume[] { }; } diff --git a/NewHorizons/Builder/Props/TranslatorText/TranslatorTextBuilder.cs b/NewHorizons/Builder/Props/TranslatorText/TranslatorTextBuilder.cs index 48a1cc4e..6ff148ef 100644 --- a/NewHorizons/Builder/Props/TranslatorText/TranslatorTextBuilder.cs +++ b/NewHorizons/Builder/Props/TranslatorText/TranslatorTextBuilder.cs @@ -631,6 +631,12 @@ namespace NewHorizons.Builder.Props.TranslatorText 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/Components/Props/ConditionalObjectActivation.cs b/NewHorizons/Components/Props/ConditionalObjectActivation.cs index 41f07ca9..2fd0cde1 100644 --- a/NewHorizons/Components/Props/ConditionalObjectActivation.cs +++ b/NewHorizons/Components/Props/ConditionalObjectActivation.cs @@ -26,6 +26,12 @@ namespace NewHorizons.Components.Props } public void Start() + { + // We delay else some props don't get properly initialized before being disappeared + Delay.FireOnNextUpdate(LateStart); + } + + private void LateStart() { var currentConditionState = DialogueConditionManager.SharedInstance.GetConditionState(DialogueCondition); diff --git a/NewHorizons/Components/ShipLog/ShipLogStarChartMode.cs b/NewHorizons/Components/ShipLog/ShipLogStarChartMode.cs index cae4a4b9..a99ea18c 100644 --- a/NewHorizons/Components/ShipLog/ShipLogStarChartMode.cs +++ b/NewHorizons/Components/ShipLog/ShipLogStarChartMode.cs @@ -259,7 +259,7 @@ namespace NewHorizons.Components.ShipLog if (!name.Equals(uniqueID)) return name; // Else we return a default name - if (uniqueID.Equals("SolarSystem")) return "Hearthian System"; + if (uniqueID.Equals("SolarSystem")) return "The Outer Wilds"; var splitString = uniqueID.Split('.'); if (splitString.Length > 1) splitString = splitString.Skip(1).ToArray(); diff --git a/NewHorizons/Components/SizeControllers/StarEvolutionController.cs b/NewHorizons/Components/SizeControllers/StarEvolutionController.cs index f67bc42f..ecb273b1 100644 --- a/NewHorizons/Components/SizeControllers/StarEvolutionController.cs +++ b/NewHorizons/Components/SizeControllers/StarEvolutionController.cs @@ -454,6 +454,8 @@ namespace NewHorizons.Components.SizeControllers public float GetSupernovaRadius() => supernova.GetSupernovaRadius(); + public float GetSurfaceRadius() => transform.localScale.x; + public float GetMaxSupernovaRadius() => supernovaSize; protected new void FixedUpdate() diff --git a/NewHorizons/Components/Stars/SunLightEffectsController.cs b/NewHorizons/Components/Stars/SunLightEffectsController.cs index 6d26808e..b4c42d03 100644 --- a/NewHorizons/Components/Stars/SunLightEffectsController.cs +++ b/NewHorizons/Components/Stars/SunLightEffectsController.cs @@ -1,4 +1,5 @@ using NewHorizons.Builder.Atmosphere; +using NewHorizons.Components.SizeControllers; using NewHorizons.Utility.OWML; using System.Collections.Generic; using UnityEngine; @@ -17,7 +18,11 @@ namespace NewHorizons.Components.Stars private readonly List _stars = new(); private readonly List _lights = new(); - private StarController _activeStar; + // SunController or StarEvolutionController + public StarEvolutionController ActiveStarEvolutionController { get; private set; } + public SunController ActiveSunController { get; private set; } + + private StarController _activeStarController; private SunLightController _sunLightController; private SunLightParamUpdater _sunLightParamUpdater; @@ -54,7 +59,7 @@ namespace NewHorizons.Components.Stars NHLogger.LogVerbose($"Removing star from list: {star?.gameObject?.name}"); if (Instance._stars.Contains(star)) { - if (Instance._activeStar != null && Instance._activeStar.Equals(star)) + if (Instance._activeStarController != null && Instance._activeStarController.Equals(star)) { Instance._stars.Remove(star); if (Instance._stars.Count > 0) Instance.ChangeActiveStar(Instance._stars[0]); @@ -121,11 +126,11 @@ namespace NewHorizons.Components.Stars if (_stars.Count > 0) { - if (_activeStar == null || !_activeStar.gameObject.activeInHierarchy) + if (_activeStarController == null || !_activeStarController.gameObject.activeInHierarchy) { - if (_stars.Contains(_activeStar)) + if (_stars.Contains(_activeStarController)) { - _stars.Remove(_activeStar); + _stars.Remove(_activeStarController); } if (_stars.Count > 0) @@ -145,8 +150,8 @@ namespace NewHorizons.Components.Stars // Update atmo shaders foreach (var (planet, material) in AtmosphereBuilder.Skys) { - var sqrDist = (planet.transform.position - _activeStar.transform.position).sqrMagnitude; - var intensity = Mathf.Min(_activeStar.Intensity / (sqrDist / hearthSunDistanceSqr), 1f); + var sqrDist = (planet.transform.position - _activeStarController.transform.position).sqrMagnitude; + var intensity = Mathf.Min(_activeStarController.Intensity / (sqrDist / hearthSunDistanceSqr), 1f); material.SetFloat(SunIntensity, intensity); } @@ -156,7 +161,7 @@ namespace NewHorizons.Components.Stars if (star == null) continue; if (!(star.gameObject.activeSelf && star.gameObject.activeInHierarchy)) continue; - if (star.Intensity * (star.transform.position - origin).sqrMagnitude < _activeStar.Intensity * (_activeStar.transform.position - origin).sqrMagnitude) + if (star.Intensity * (star.transform.position - origin).sqrMagnitude < _activeStarController.Intensity * (_activeStarController.transform.position - origin).sqrMagnitude) { ChangeActiveStar(star); break; @@ -170,11 +175,13 @@ namespace NewHorizons.Components.Stars { if (_sunLightController == null || _sunLightParamUpdater == null) return; - if (_activeStar != null) _activeStar.Disable(); + _activeStarController?.Disable(); NHLogger.LogVerbose($"Switching active star: {star.gameObject.name}"); - _activeStar = star; + _activeStarController = star; + ActiveStarEvolutionController = star.GetComponentInChildren(); + ActiveSunController = star.GetComponent(); star.Enable(); @@ -194,7 +201,8 @@ namespace NewHorizons.Components.Stars transform.localPosition = Vector3.zero; // Some effects use Locator.GetSunTransform so hopefully its fine to change it - Locator._sunTransform = transform; + // Use the root transform of the star because that's the default behaviour, breaks SunProxy if not (potentially others) + Locator._sunTransform = star.transform; // TODO?: maybe also turn off star controller stuff (mainly proxy light) since idk if that can handle more than 1 being on } diff --git a/NewHorizons/Components/Volumes/WaterCloakFixerVolume.cs b/NewHorizons/Components/Volumes/WaterCloakFixerVolume.cs new file mode 100644 index 00000000..d74aaa89 --- /dev/null +++ b/NewHorizons/Components/Volumes/WaterCloakFixerVolume.cs @@ -0,0 +1,54 @@ +using UnityEngine; + +namespace NewHorizons.Components.Volumes +{ + /// + /// A cloak can interfere with the rendering of water + /// Water has a lower render queue and is transparent, so you can see the background black cloak over top of the water + /// We fix this by setting the water's render queue to that of the cloak + /// However, this means that when you are inside the water you will see through the cloak since it's not rendered on top + /// To fix that, we set the render queue back to normal when the player enters the water + /// Currently this doesnt nothing to fix probe camera pictures. If you are outside of the water, the probe will see the stars and through the cloak + /// Oh well + /// + internal class WaterCloakFixerVolume : MonoBehaviour + { + public Material material; + private OWTriggerVolume _volume; + + public const int WATER_RENDER_QUEUE = 2990; + public const int CLOAK_RENDER_QUEUE = 3000; + + public void Start() + { + _volume = GetComponent().GetOWTriggerVolume(); + + _volume.OnEntry += WaterCloakFixerVolume_OnEntry; + _volume.OnExit += WaterCloakFixerVolume_OnExit; + + material.renderQueue = CLOAK_RENDER_QUEUE; + } + + public void OnDestroy() + { + _volume.OnEntry -= WaterCloakFixerVolume_OnEntry; + _volume.OnExit -= WaterCloakFixerVolume_OnExit; + } + + private void WaterCloakFixerVolume_OnEntry(GameObject hitObj) + { + if (hitObj.CompareTag("PlayerDetector")) + { + material.renderQueue = WATER_RENDER_QUEUE; + } + } + + private void WaterCloakFixerVolume_OnExit(GameObject hitObj) + { + if (hitObj.CompareTag("PlayerDetector")) + { + material.renderQueue = CLOAK_RENDER_QUEUE; + } + } + } +} diff --git a/NewHorizons/External/Configs/PlanetConfig.cs b/NewHorizons/External/Configs/PlanetConfig.cs index e76225c4..d1248900 100644 --- a/NewHorizons/External/Configs/PlanetConfig.cs +++ b/NewHorizons/External/Configs/PlanetConfig.cs @@ -13,6 +13,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; +using UnityEngine; namespace NewHorizons.External.Configs { @@ -167,6 +168,12 @@ namespace NewHorizons.External.Configs /// public WaterModule Water; + /// + /// Add particle effects in a field around the planet. + /// Also known as Vection Fields. + /// + public ParticleFieldModule[] ParticleFields; + /// /// Add various volumes on this body /// @@ -341,6 +348,29 @@ namespace NewHorizons.External.Configs // useBasicCloudShader is obsolete if (Atmosphere.clouds != null && Atmosphere.clouds.useBasicCloudShader) Atmosphere.clouds.cloudsPrefab = CloudPrefabType.Basic; + + if (Atmosphere.hasRain) + { + if (ParticleFields == null) ParticleFields = new ParticleFieldModule[0]; + ParticleFields = ParticleFields.Append(new ParticleFieldModule + { + type = ParticleFieldModule.ParticleFieldType.Rain, + rename = "RainEmitter" + }).ToArray(); + } + + if (Atmosphere.hasSnow) + { + if (ParticleFields == null) ParticleFields = new ParticleFieldModule[0]; + for (int i = 0; i < 5; i++) + { + ParticleFields = ParticleFields.Append(new ParticleFieldModule + { + type = ParticleFieldModule.ParticleFieldType.SnowflakesHeavy, + rename = "SnowEmitter" + }).ToArray(); + } + } } if (Props?.tornados != null) diff --git a/NewHorizons/External/Modules/AtmosphereModule.cs b/NewHorizons/External/Modules/AtmosphereModule.cs index 675ed837..e318f372 100644 --- a/NewHorizons/External/Modules/AtmosphereModule.cs +++ b/NewHorizons/External/Modules/AtmosphereModule.cs @@ -57,7 +57,7 @@ namespace NewHorizons.External.Modules /// Colour of fog on the planet, if you put fog. /// public MColor fogTint; - + /// /// Relative filepath to the fog color ramp texture, if you put fog. /// x axis is angle to sun (left at midnight, right at noon), y axis is distance to camera (close at bottom, far at top). @@ -75,15 +75,12 @@ namespace NewHorizons.External.Modules public bool hasTrees; /// - /// Does this planet have rain? + /// Does this planet have rain? + /// This is equivalent to effects of setting a rain particle/vection field, rain audio volume, and visor effect volume, combined for convenience. + /// For more control over the rain, use those individual components. /// public bool hasRain; - /// - /// Does this planet have snow? - /// - public bool hasSnow; - /// /// Scale height of the atmosphere /// @@ -174,7 +171,6 @@ namespace NewHorizons.External.Modules /// [DefaultValue(0f)] public float rotationSpeed = 0f; - #region Obsolete /// @@ -189,6 +185,8 @@ namespace NewHorizons.External.Modules #region Obsolete + [Obsolete("HasSnow is deprecated, please use ParticleFields instead")] + public bool hasSnow; [Obsolete("CloudTint is deprecated, please use CloudInfo instead")] public MColor cloudTint; @@ -208,7 +206,8 @@ namespace NewHorizons.External.Modules [Obsolete("UseBasicCloudShader is deprecated, please use CloudInfo instead")] public bool useBasicCloudShader; - [DefaultValue(true)] [Obsolete("ShadowsOnClouds is deprecated, please use CloudInfo instead")] + [DefaultValue(true)] + [Obsolete("ShadowsOnClouds is deprecated, please use CloudInfo instead")] public bool shadowsOnClouds = true; [Obsolete("HasAtmosphere is deprecated, please use UseAtmosphereShader instead")] diff --git a/NewHorizons/External/Modules/OrbitModule.cs b/NewHorizons/External/Modules/OrbitModule.cs index e3a6a566..bb1325e4 100644 --- a/NewHorizons/External/Modules/OrbitModule.cs +++ b/NewHorizons/External/Modules/OrbitModule.cs @@ -45,6 +45,12 @@ namespace NewHorizons.External.Modules /// public bool isTidallyLocked; + /// + /// Is the body meant to stay in one place without moving? If staticPosition is not set, the initial position + /// will be determined using its orbital parameters. + /// + public bool isStatic; + /// /// If it is tidally locked, this direction will face towards the primary. Ex: Interloper uses `0, -1, 0`. Most planets /// will want something like `-1, 0, 0`. @@ -62,12 +68,6 @@ namespace NewHorizons.External.Modules /// public bool dottedOrbitLine; - /// - /// Is the body meant to stay in one place without moving? If staticPosition is not set, the initial position - /// will be determined using its orbital parameters. - /// - public bool isStatic; - /// /// Colour of the orbit-line in the map view. /// @@ -78,6 +78,16 @@ namespace NewHorizons.External.Modules /// public bool trackingOrbitLine; + /// + /// If the camera is farther than this distance the orbit line will fade out. Leave empty to not have it fade out. + /// + public float orbitLineFadeEndDistance = -1f; + + /// + /// If the camera is closer than this distance the orbit line will fade out. Leave empty to not have it fade out. + /// + public float orbitLineFadeStartDistance = -1f; + /// /// The semi-major axis of the ellipse that is the body's orbit. For a circular orbit this is the radius. /// diff --git a/NewHorizons/External/Modules/VectionFieldModule.cs b/NewHorizons/External/Modules/VectionFieldModule.cs new file mode 100644 index 00000000..19586544 --- /dev/null +++ b/NewHorizons/External/Modules/VectionFieldModule.cs @@ -0,0 +1,79 @@ +using NewHorizons.External.Modules.VariableSize; +using NewHorizons.External.SerializableData; +using NewHorizons.External.SerializableEnums; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using System; +using System.ComponentModel; +using System.Runtime.Serialization; + +namespace NewHorizons.External.Modules +{ + [JsonObject] + public class ParticleFieldModule + { + /// + /// Particle type for this vection field. + /// + public ParticleFieldType type; + + /// + /// What the particle field activates based on. + /// + [DefaultValue("player")] public FollowTarget followTarget = FollowTarget.Player; + + /// + /// Density by height curve. Determines how many particles are emitted at different heights. + /// Defaults to a curve based on minimum and maximum heights of various other modules. + /// + public HeightDensityPair[] densityByHeightCurve; + + /// + /// An optional rename of this object + /// + public string rename; + + [JsonObject] + public class HeightDensityPair + { + /// + /// A specific radius + /// + public float height; + + /// + /// The particle count for this radius. + /// + public float density; + } + + [JsonConverter(typeof(StringEnumConverter))] + public enum ParticleFieldType + { + [EnumMember(Value = @"rain")] Rain, + [EnumMember(Value = @"snowflakesHeavy")] SnowflakesHeavy, + [EnumMember(Value = @"snowflakesLight")] SnowflakesLight, + [EnumMember(Value = @"embers")] Embers, + [EnumMember(Value = @"clouds")] Clouds, + [EnumMember(Value = @"leaves")] Leaves, + [EnumMember(Value = @"bubbles")] Bubbles, + [EnumMember(Value = @"fog")] Fog, + [EnumMember(Value = @"crystalMotes")] CrystalMotes, + [EnumMember(Value = @"rockMotes")] RockMotes, + [EnumMember(Value = @"iceMotes")] IceMotes, + [EnumMember(Value = @"sandMotes")] SandMotes, + [EnumMember(Value = @"crawlies")] Crawlies, + [EnumMember(Value = @"fireflies")] Fireflies, + [EnumMember(Value = @"plankton")] Plankton, + [EnumMember(Value = @"pollen")] Pollen, + [EnumMember(Value = @"current")] Current + } + + [JsonConverter(typeof(StringEnumConverter))] + public enum FollowTarget + { + [EnumMember(Value = @"player")] Player, + [EnumMember(Value = @"probe")] Probe + } + } +} diff --git a/NewHorizons/Handlers/PlanetCreationHandler.cs b/NewHorizons/Handlers/PlanetCreationHandler.cs index e0e7fe93..31556ca6 100644 --- a/NewHorizons/Handlers/PlanetCreationHandler.cs +++ b/NewHorizons/Handlers/PlanetCreationHandler.cs @@ -17,6 +17,7 @@ using System.Collections.Generic; using System.Linq; using UnityEngine; using NewHorizons.Streaming; +using Newtonsoft.Json; namespace NewHorizons.Handlers { @@ -34,6 +35,8 @@ namespace NewHorizons.Handlers public static float SolarSystemRadius { get; private set; } public static float DefaultFurthestOrbit => 30000f; + public static List> CustomBuilders; + public static void Init(List bodies) { // Start by destroying all planets if need be @@ -158,8 +161,20 @@ namespace NewHorizons.Handlers } catch (Exception) { - if (body?.Config?.name == null) NHLogger.LogError($"How is there no name for {body}"); - else existingPlanet = SearchUtilities.Find(body.Config.name.Replace(" ", "") + "_Body", false); + if (body?.Config?.name == null) + { + NHLogger.LogError($"How is there no name for {body}"); + } + else + { + existingPlanet = SearchUtilities.Find(body.Config.name.Replace(" ", "") + "_Body", false); + } + } + + if (existingPlanet == null && body.Config.destroy) + { + NHLogger.LogError($"{body.Config.name} was meant to be destroyed, but was not found"); + return false; } if (existingPlanet != null) @@ -169,8 +184,14 @@ namespace NewHorizons.Handlers if (body.Config.destroy) { var ao = existingPlanet.GetComponent(); - if (ao != null) Delay.FireInNUpdates(() => PlanetDestructionHandler.RemoveBody(ao), 2); - else Delay.FireInNUpdates(() => PlanetDestructionHandler.DisableBody(existingPlanet, false), 2); + if (ao != null) + { + Delay.FireInNUpdates(() => PlanetDestructionHandler.DisableAstroObject(ao), 2); + } + else + { + Delay.FireInNUpdates(() => PlanetDestructionHandler.DisableGameObject(existingPlanet), 2); + } } else if (body.Config.isQuantumState) { @@ -391,7 +412,8 @@ namespace NewHorizons.Handlers if (defaultPrimaryToSun) { NHLogger.LogError($"Couldn't find {body.Config.Orbit.primaryBody}, defaulting to center of solar system"); - primaryBody = Locator.GetCenterOfTheUniverse().GetAttachedOWRigidbody().GetComponent(); + // TODO: Make this work in other systems. We tried using Locator.GetCenterOfUniverse before but that doesn't work since its too early now + primaryBody = SearchUtilities.Find("Sun_Body")?.GetComponent(); } else { @@ -627,7 +649,7 @@ namespace NewHorizons.Handlers if (body.Config.Water != null) { - WaterBuilder.Make(go, sector, rb, body.Config.Water); + WaterBuilder.Make(go, sector, rb, body.Config); } if (body.Config.Sand != null) @@ -654,9 +676,6 @@ namespace NewHorizons.Handlers } } - if (body.Config.Atmosphere.hasRain || body.Config.Atmosphere.hasSnow) - EffectsBuilder.Make(go, sector, body.Config, surfaceSize); - if (body.Config.Atmosphere.fogSize != 0) { fog = FogBuilder.Make(go, sector, body.Config.Atmosphere, body.Mod); @@ -665,6 +684,11 @@ namespace NewHorizons.Handlers atmosphere = AtmosphereBuilder.Make(go, sector, body.Config.Atmosphere, surfaceSize).GetComponentInChildren(); } + if (body.Config.ParticleFields != null) + { + EffectsBuilder.Make(go, sector, body.Config); + } + if (body.Config.Props != null) { PropBuildManager.Make(go, sector, rb, body); @@ -680,6 +704,21 @@ namespace NewHorizons.Handlers FunnelBuilder.Make(go, sector, rb, body.Config.Funnel); } + if (body.Config.extras != null) + { + foreach (var customBuilder in CustomBuilders) + { + try + { + customBuilder.Invoke(go, JsonConvert.SerializeObject(body.Config.extras)); + } + catch + { + NHLogger.LogError($"Failed to use custom builder on body {body.Config.name}"); + } + } + } + // Has to go last probably if (willHaveCloak) { diff --git a/NewHorizons/Handlers/PlanetDestructionHandler.cs b/NewHorizons/Handlers/PlanetDestructionHandler.cs index e8377893..cc36636c 100644 --- a/NewHorizons/Handlers/PlanetDestructionHandler.cs +++ b/NewHorizons/Handlers/PlanetDestructionHandler.cs @@ -6,53 +6,15 @@ using System; using System.Collections.Generic; using System.Linq; using UnityEngine; +using NewHorizons.Components; namespace NewHorizons.Handlers { public static class PlanetDestructionHandler { - private static readonly string[] _solarSystemBodies = new string[] - { - "Ash Twin", - "Attlerock", - "Brittle Hollow", - "Dark Bramble", - "DreamWorld", - "Ember Twin", - "Giant's Deep", - "Hollow's Lantern", - "Interloper", - "Map Satellite", - "Orbital Probe Cannon", - "Quantum Moon", - "RingWorld", - "Sun", - "Sun Station", - "Timber Hearth", - "White Hole" - }; - - private static readonly string[] _eyeOfTheUniverseBodies = new string[] - { - "Eye Of The Universe", - "Vessel" - }; - - private static readonly string[] _suspendBlacklist = new string[] - { - "Player_Body", - "Ship_Body" - }; + public static readonly string[] _suspendBlacklist = new string[] { "Player_Body", "Probe_Body", "Ship_Body" }; public static void RemoveStockPlanets() - { - if (Main.Instance.CurrentStarSystem == "EyeOfTheUniverse") - RemoveEyeOfTheUniverse(); - else - RemoveSolarSystem(); - } - - public static void RemoveSolarSystem() { // Adapted from EOTS thanks corby var toDisable = new List(); @@ -60,51 +22,41 @@ namespace NewHorizons.Handlers // Collect all rigid bodies and proxies foreach (var rigidbody in CenterOfTheUniverse.s_rigidbodies) { - if (rigidbody.name is not ("Player_Body" or "Probe_Body" or "Ship_Body")) + if (!_suspendBlacklist.Contains(rigidbody.name)) { toDisable.Add(rigidbody.gameObject); } } - foreach (var proxyBody in GameObject.FindObjectsOfType()) - { - toDisable.Add(proxyBody.gameObject); - } - Delay.FireInNUpdates(() => { foreach (var gameObject in toDisable) { gameObject.SetActive(false); } + // Kill all non nh proxies + foreach (var proxy in GameObject.FindObjectsOfType()) + { + if (proxy is not NHProxy) + { + proxy.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(); + if (Main.Instance.CurrentStarSystem != "EyeOfTheUniverse") + { + // Since we didn't call RemoveBody on the all planets there are some we have to call here + StrangerRemoved(); + TimberHearthRemoved(); + GiantsDeepRemoved(); + SunRemoved(); + } - // Don't forget to fix THE WARP BUG - DisableBody(SearchUtilities.Find("StreamingGroup_TH"), true); }, 2); // Have to wait or shit goes wild - - foreach (var streamingAssetBundle in StreamingManager.s_activeBundles) - { - //streamingAssetBundle.Unload(); - } - } - - public static void RemoveEyeOfTheUniverse() - { - foreach (var name in _eyeOfTheUniverseBodies) - { - var ao = AstroObjectLocator.GetAstroObject(name); - if (ao != null) Delay.FireInNUpdates(() => RemoveBody(ao, false), 2); - else NHLogger.LogError($"Couldn't find [{name}]"); - } } + #region Planet specific removals public static void StrangerRemoved() { CloakHandler.FlagStrangerDisabled = true; @@ -115,101 +67,122 @@ namespace NewHorizons.Handlers } } - public static void RemoveBody(AstroObject ao, bool delete = false, List toDestroy = null) + private static void SunRemoved() { - NHLogger.LogVerbose($"Removing [{ao.name}]"); + var sun = SearchUtilities.Find("Sun_Body").GetComponent(); - if (ao.GetAstroObjectName() == AstroObject.Name.RingWorld) + var starController = sun.gameObject.GetComponent(); + SunLightEffectsController.RemoveStar(starController); + SunLightEffectsController.RemoveStarLight(sun.transform.Find("Sector_SUN/Effects_SUN/SunLight").GetComponent()); + UnityEngine.Object.Destroy(starController); + + var audio = sun.GetComponentInChildren(); + UnityEngine.Object.Destroy(audio); + + foreach (var owAudioSource in sun.GetComponentsInChildren()) { - StrangerRemoved(); + owAudioSource.Stop(); + UnityEngine.Object.Destroy(owAudioSource); } + foreach (var audioSource in sun.GetComponentsInChildren()) + { + audioSource.Stop(); + UnityEngine.Object.Destroy(audioSource); + } + + foreach (var sunProxy in UnityEngine.Object.FindObjectsOfType()) + { + NHLogger.LogVerbose($"Destroying SunProxy {sunProxy.gameObject.name}"); + UnityEngine.Object.Destroy(sunProxy.gameObject); + } + + // Stop the sun from breaking stuff when the supernova gets triggered + GlobalMessenger.RemoveListener("TriggerSupernova", sun.GetComponent().OnTriggerSupernova); + + // Just to be safe + SunLightEffectsController.Instance.Update(); + } + + private static void TimberHearthRemoved() + { + // Always just fucking kill this one to stop THE WARP BUG!!! + GameObject.Destroy(SearchUtilities.Find("StreamingGroup_TH").gameObject); + + var timberHearth = SearchUtilities.Find("TimberHearth_Body"); + foreach (var obj in timberHearth.GetComponentsInChildren()) + { + GameObject.Destroy(obj.gameObject); + } + foreach (var obj in timberHearth.GetComponentsInChildren()) + { + GameObject.Destroy(obj.gameObject); + } + } + + private static void GiantsDeepRemoved() + { + foreach (var jelly in UnityEngine.Object.FindObjectsOfType()) + { + if (jelly.GetSector().GetRootSector().GetName() == Sector.Name.GiantsDeep) + { + DisableGameObject(jelly.gameObject); + } + } + } + #endregion + + public static void DisableAstroObject(AstroObject ao, List toDisable = null) + { if (ao.gameObject == null || !ao.gameObject.activeInHierarchy) { - NHLogger.LogVerbose($"[{ao.name}] was already removed"); + NHLogger.LogVerbose($"[{ao?.name}] was already removed"); return; } - if (toDestroy == null) toDestroy = new List(); + NHLogger.LogVerbose($"Removing [{ao.name}]"); - if (toDestroy.Contains(ao)) + toDisable ??= new List(); + + if (toDisable.Contains(ao)) { NHLogger.LogVerbose($"Possible infinite recursion in RemoveBody: {ao.name} might be it's own primary body?"); return; } - toDestroy.Add(ao); + toDisable.Add(ao); try { switch(ao._name) { case AstroObject.Name.BrittleHollow: - RemoveBody(AstroObjectLocator.GetAstroObject(AstroObject.Name.WhiteHole.ToString()), delete, toDestroy); + DisableAstroObject(AstroObjectLocator.GetAstroObject(AstroObject.Name.WhiteHole.ToString()), toDisable); // Might prevent leftover fragments from existing // Might also prevent people from using their own detachable fragments however foreach(var fragment in UnityEngine.Object.FindObjectsOfType()) { - DisableBody(fragment.gameObject, delete); + DisableGameObject(fragment.gameObject); } break; + case AstroObject.Name.CaveTwin: case AstroObject.Name.TowerTwin: - DisableBody(SearchUtilities.Find("FocalBody"), delete); - DisableBody(SearchUtilities.Find("SandFunnel_Body", false), delete); + DisableGameObject(SearchUtilities.Find("FocalBody")); + DisableGameObject(SearchUtilities.Find("SandFunnel_Body", false)); break; + case AstroObject.Name.GiantsDeep: - // Might prevent leftover jellyfish from existing - // Might also prevent people from using their own jellyfish however - foreach (var jelly in UnityEngine.Object.FindObjectsOfType()) - { - DisableBody(jelly.gameObject, delete); - } - // Else it will re-eanble the pieces - // ao.GetComponent()._realDebrisSectorProxies = null; + GiantsDeepRemoved(); break; case AstroObject.Name.TimberHearth: - // Always just fucking kill this one to stop THE WARP BUG!!! - DisableBody(SearchUtilities.Find("StreamingGroup_TH"), true); - - foreach (var obj in UnityEngine.Object.FindObjectsOfType()) - { - DisableBody(obj.gameObject, true); - } - foreach (var obj in UnityEngine.Object.FindObjectsOfType()) - { - DisableBody(obj.gameObject, true); - } + TimberHearthRemoved(); break; case AstroObject.Name.Sun: - var starController = ao.gameObject.GetComponent(); - SunLightEffectsController.RemoveStar(starController); - SunLightEffectsController.RemoveStarLight(ao.transform.Find("Sector_SUN/Effects_SUN/SunLight").GetComponent()); - UnityEngine.Object.Destroy(starController); - - var audio = ao.GetComponentInChildren(); - UnityEngine.Object.Destroy(audio); - - foreach (var owAudioSource in ao.GetComponentsInChildren()) - { - owAudioSource.Stop(); - UnityEngine.Object.Destroy(owAudioSource); - } - - foreach (var audioSource in ao.GetComponentsInChildren()) - { - audioSource.Stop(); - UnityEngine.Object.Destroy(audioSource); - } - - foreach (var sunProxy in UnityEngine.Object.FindObjectsOfType()) - { - NHLogger.LogVerbose($"Destroying SunProxy {sunProxy.gameObject.name}"); - UnityEngine.Object.Destroy(sunProxy.gameObject); - } - - // Stop the sun from breaking stuff when the supernova gets triggered - GlobalMessenger.RemoveListener("TriggerSupernova", ao.GetComponent().OnTriggerSupernova); + SunRemoved(); + break; + case AstroObject.Name.RingWorld: + StrangerRemoved(); break; } @@ -226,8 +199,14 @@ namespace NewHorizons.Handlers // Some children might be astro objects and as such can have children of their own var childAO = child.GetComponent(); - if (childAO != null) RemoveBody(childAO, false, toDestroy); - else DisableBody(child, true); + if (childAO != null) + { + DisableAstroObject(childAO, toDisable); + } + else + { + DisableGameObject(child); + } } // Always delete moons @@ -235,7 +214,7 @@ namespace NewHorizons.Handlers { if (obj == null) continue; - RemoveBody(obj.GetComponent(), false, toDestroy); + DisableAstroObject(obj.GetComponent(), toDisable); } } catch (Exception e) @@ -243,36 +222,8 @@ namespace NewHorizons.Handlers NHLogger.LogError($"Exception thrown when trying to delete bodies related to [{ao.name}]:\n{e}"); } - // Deal with proxies - foreach (var p in UnityEngine.Object.FindObjectsOfType()) - { - if (p._originalBody == ao.gameObject) - { - DisableBody(p.gameObject, true); - break; - } - } - RemoveProxy(ao.name.Replace("_Body", "")); - - Delay.RunWhen(() => Main.IsSystemReady, () => DisableBody(ao.gameObject, delete)); - - foreach (ProxyBody proxy in UnityEngine.Object.FindObjectsOfType()) - { - if (proxy?._realObjectTransform?.gameObject == ao.gameObject) - { - UnityEngine.Object.Destroy(proxy.gameObject); - } - } - } - - public static void RemoveAllProxies() - { - UnityEngine.Object.Destroy(UnityEngine.Object.FindObjectOfType().gameObject); - - foreach (var name in _solarSystemBodies) - { - RemoveProxy(name.Replace(" ", "").Replace("'", "")); - } + DisableGameObject(ao.gameObject); + RemoveProxy(ao); } private static bool CanSuspend(OWRigidbody rigidbody, string name) @@ -282,7 +233,7 @@ namespace NewHorizons.Handlers return CanSuspend(rigidbody._origParentBody, name); } - internal static void DisableBody(GameObject go, bool delete) + internal static void DisableGameObject(GameObject go) { if (go == null) return; @@ -313,33 +264,18 @@ namespace NewHorizons.Handlers } } - if (delete) + go.SetActive(false); + var ol = go.GetComponentInChildren(); + if (ol) { - UnityEngine.Object.Destroy(go); - } - else - { - go.SetActive(false); - var ol = go.GetComponentInChildren(); - if (ol) - { - ol.enabled = false; - } + ol.enabled = false; } } - private static void RemoveProxy(string name) + private static void RemoveProxy(AstroObject ao) { - if (name.Equals("TowerTwin")) name = "AshTwin"; - if (name.Equals("CaveTwin")) name = "EmberTwin"; - var distantProxy = SearchUtilities.Find(name + "_DistantProxy", false); - var distantProxyClone = SearchUtilities.Find(name + "_DistantProxy(Clone)", false); - - if (distantProxy != null) UnityEngine.Object.Destroy(distantProxy.gameObject); - if (distantProxyClone != null) UnityEngine.Object.Destroy(distantProxyClone.gameObject); - - if (distantProxy == null && distantProxyClone == null) - NHLogger.LogVerbose($"Couldn't find proxy for {name}"); + ProxyHandler.GetVanillaProxyBody(ao.transform)?.gameObject?.SetActive(false); + ProxyHandler.GetVanillaProxyOrbiter(ao.transform)?.gameObject?.SetActive(false); } } } diff --git a/NewHorizons/Handlers/PlayerSpawnHandler.cs b/NewHorizons/Handlers/PlayerSpawnHandler.cs index 8a9eb6f8..2b32cd69 100644 --- a/NewHorizons/Handlers/PlayerSpawnHandler.cs +++ b/NewHorizons/Handlers/PlayerSpawnHandler.cs @@ -10,11 +10,11 @@ namespace NewHorizons.Handlers { public static void SetUpPlayerSpawn() { - var spawnPoint = Main.SystemDict[Main.Instance.CurrentStarSystem].SpawnPoint; - if (spawnPoint != null) + if (UsingCustomSpawn()) { - SearchUtilities.Find("Player_Body").GetComponent().SetBodyToMatch(spawnPoint.GetAttachedOWRigidbody()); - GetPlayerSpawner().SetInitialSpawnPoint(spawnPoint); + var spawn = GetDefaultSpawn(); + SearchUtilities.Find("Player_Body").GetComponent().SetBodyToMatch(spawn.GetAttachedOWRigidbody()); + GetPlayerSpawner().SetInitialSpawnPoint(spawn); } else { @@ -44,24 +44,45 @@ namespace NewHorizons.Handlers // Arbitrary number, depending on the machine some people die, some people fall through the floor, its very inconsistent Delay.StartCoroutine(SpawnCoroutine(30)); } + + var cloak = GetDefaultSpawn()?.GetAttachedOWRigidbody()?.GetComponentInChildren(); + if (cloak != null) + { + // Ensures it has invoked everything and actually placed the player in the cloaking field #671 + cloak._firstUpdate = true; + } + + // Spawn ship + Delay.FireInNUpdates(SpawnShip, 30); } public static void SpawnShip() { var ship = SearchUtilities.Find("Ship_Body"); - if (ship != null) + + if (SpawnPointBuilder.ShipSpawn != null) { - ship.SetActive(true); + NHLogger.Log("Spawning player ship"); - var pos = SpawnPointBuilder.ShipSpawn.transform.position; - - // Move it up a bit more when aligning to surface - if (SpawnPointBuilder.ShipSpawnOffset != null) + if (ship != null) { - pos += SpawnPointBuilder.ShipSpawn.transform.TransformDirection(SpawnPointBuilder.ShipSpawnOffset); - } + ship.SetActive(true); - SpawnBody(ship.GetAttachedOWRigidbody(), SpawnPointBuilder.ShipSpawn, pos); + var pos = SpawnPointBuilder.ShipSpawn.transform.position; + + // Move it up a bit more when aligning to surface + if (SpawnPointBuilder.ShipSpawnOffset != null) + { + pos += SpawnPointBuilder.ShipSpawn.transform.TransformDirection(SpawnPointBuilder.ShipSpawnOffset); + } + + SpawnBody(ship.GetAttachedOWRigidbody(), SpawnPointBuilder.ShipSpawn, pos); + } + } + else if (Main.Instance.CurrentStarSystem != "SolarSystem" && !Main.Instance.IsWarpingFromShip) + { + NHLogger.Log("System has no ship spawn. Deactivating it."); + ship?.SetActive(false); } } @@ -74,20 +95,6 @@ namespace NewHorizons.Handlers } InvulnerabilityHandler.MakeInvulnerable(false); - - if (!Main.Instance.IsWarpingFromShip) - { - if (SpawnPointBuilder.ShipSpawn != null) - { - NHLogger.Log("Spawning player ship"); - SpawnShip(); - } - else - { - NHLogger.Log("System has no ship spawn. Deactivating it."); - SearchUtilities.Find("Ship_Body")?.SetActive(false); - } - } } private static void FixPlayerVelocity() diff --git a/NewHorizons/Handlers/ProxyHandler.cs b/NewHorizons/Handlers/ProxyHandler.cs index 090d519d..811d0837 100644 --- a/NewHorizons/Handlers/ProxyHandler.cs +++ b/NewHorizons/Handlers/ProxyHandler.cs @@ -1,11 +1,21 @@ using NewHorizons.Components; using System.Collections.Generic; +using UnityEngine; namespace NewHorizons.Handlers { public static class ProxyHandler { - private static List _proxies = new List(); + private static List _proxies = new(); + private static Dictionary _vanillaProxyBody = new(); + private static Dictionary _vanillaProxyOrbiter = new(); + + public static void ClearCache() + { + _proxies.Clear(); + _vanillaProxyBody.Clear(); + _vanillaProxyOrbiter.Clear(); + } public static NHProxy GetProxy(string astroName) { @@ -17,6 +27,48 @@ namespace NewHorizons.Handlers return null; } + public static void RegisterVanillaProxyBody(ProxyBody proxy) + { + if (proxy.realObjectTransform != null) + { + _vanillaProxyBody.Add(proxy.realObjectTransform, proxy); + } + } + + public static ProxyBody GetVanillaProxyBody(Transform t) + { + if (_vanillaProxyBody.TryGetValue(t, out ProxyBody proxy)) + { + return proxy; + } + else + { + return null; + } + } + + public static void RegisterVanillaProxyOrbiter(ProxyOrbiter proxy) + { + // The _originalBody is the moon + // For _originalPlanetBody, that game object will also have a ProxyBody on it so it'd be counted via the other cache + if (proxy._originalBody != null) + { + _vanillaProxyOrbiter.Add(proxy._originalBody, proxy); + } + } + + public static ProxyOrbiter GetVanillaProxyOrbiter(Transform t) + { + if (_vanillaProxyOrbiter.TryGetValue(t, out ProxyOrbiter proxy)) + { + return proxy; + } + else + { + return null; + } + } + public static void RegisterProxy(NHProxy proxy) { _proxies.SafeAdd(proxy); diff --git a/NewHorizons/INewHorizons.cs b/NewHorizons/INewHorizons.cs index c4447d28..903b76e5 100644 --- a/NewHorizons/INewHorizons.cs +++ b/NewHorizons/INewHorizons.cs @@ -1,3 +1,4 @@ +using NewHorizons.Handlers; using OWML.Common; using System; using System.Collections.Generic; @@ -92,6 +93,11 @@ namespace NewHorizons /// Uses JSONPath to query the current star system /// T QuerySystem(string path); + + /// + /// Register your own builder that will act on the given GameObject by reading the json string of its "extras" module + /// + void RegisterCustomBuilder(Action builder); #endregion #region Spawn props diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index f5014c8e..3d65513d 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -59,8 +59,27 @@ namespace NewHorizons public static bool IsSystemReady { get; private set; } public string DefaultStarSystem => SystemDict.ContainsKey(_defaultSystemOverride) ? _defaultSystemOverride : _defaultStarSystem; - public string CurrentStarSystem => _currentStarSystem; - public bool TimeLoopEnabled => SystemDict[CurrentStarSystem]?.Config?.enableTimeLoop ?? true; + public string CurrentStarSystem + { + get + { + return _currentStarSystem; + } + set + { + // Prevent invalid values + if (value != "SolarSystem" && value != "EyeOfTheUniverse" && !SystemDict.ContainsKey(value) && !BodyDict.ContainsKey(value)) + { + NHLogger.LogError($"System \"{value}\" does not exist!"); + _currentStarSystem = DefaultStarSystem; + } + _currentStarSystem = value; + } + } + private string _currentStarSystem; + private string _previousStarSystem; + + public bool TimeLoopEnabled => CurrentStarSystem == null || (SystemDict[CurrentStarSystem]?.Config?.enableTimeLoop ?? true); public bool IsWarpingFromShip { get; private set; } = false; public bool IsWarpingFromVessel { get; private set; } = false; public bool IsWarpingBackToEye { get; internal set; } = false; @@ -73,7 +92,7 @@ namespace NewHorizons public static bool HasWarpDrive { get; private set; } = false; private string _defaultStarSystem = "SolarSystem"; - internal string _currentStarSystem = "SolarSystem"; + private bool _firstLoad = true; private bool _playerAwake; @@ -260,11 +279,20 @@ namespace NewHorizons private void OnSceneUnloaded(Scene scene) { + // Caches of GameObjects must always be cleared SearchUtilities.ClearCache(); - ImageUtilities.ClearCache(); - AudioUtilities.ClearCache(); - AssetBundleUtilities.ClearCache(); - EnumUtilities.ClearCache(); + ProxyHandler.ClearCache(); + + // Caches of other assets only have to be cleared if we changed star systems + if (CurrentStarSystem != _previousStarSystem) + { + NHLogger.Log($"Changing star system from {_previousStarSystem} to {CurrentStarSystem} - Clearing system-specific caches!"); + ImageUtilities.ClearCache(); + AudioUtilities.ClearCache(); + AssetBundleUtilities.ClearCache(); + EnumUtilities.ClearCache(); + } + IsSystemReady = false; } @@ -330,7 +358,7 @@ namespace NewHorizons if (isEyeOfTheUniverse) { - _currentStarSystem = "EyeOfTheUniverse"; + CurrentStarSystem = "EyeOfTheUniverse"; } else if (IsWarpingBackToEye) { @@ -341,14 +369,8 @@ namespace NewHorizons return; } - if (!SystemDict.ContainsKey(_currentStarSystem) || !BodyDict.ContainsKey(_currentStarSystem)) - { - NHLogger.LogError($"System \"{_currentStarSystem}\" does not exist!"); - _currentStarSystem = DefaultStarSystem; - } - // Set time loop stuff if its enabled and if we're warping to a new place - if (IsChangingStarSystem && (SystemDict[_currentStarSystem].Config.enableTimeLoop || _currentStarSystem == "SolarSystem") && SecondsElapsedInLoop > 0f) + if (IsChangingStarSystem && (SystemDict[CurrentStarSystem].Config.enableTimeLoop || CurrentStarSystem == "SolarSystem") && SecondsElapsedInLoop > 0f) { TimeLoopUtilities.SetSecondsElapsed(SecondsElapsedInLoop); // Prevent the OPC from firing @@ -363,7 +385,7 @@ namespace NewHorizons launchController.enabled = false; } var nomaiProbe = SearchUtilities.Find("NomaiProbe_Body"); - if (nomaiProbe != null) nomaiProbe.gameObject.SetActive(false); + nomaiProbe?.gameObject.SetActive(false); } // Reset this @@ -518,6 +540,10 @@ namespace NewHorizons { ResetCurrentStarSystem(); } + + // We only check previous when the scene unloads, and at that point current should be updated to the new system + NHLogger.LogVerbose($"Set the previous system to {CurrentStarSystem}"); + _previousStarSystem = CurrentStarSystem; } // Had a bunch of separate unity things firing stuff when the system is ready so I moved it all to here @@ -567,7 +593,7 @@ namespace NewHorizons if (starSystemName != "SolarSystem") { SetDefaultSystem(starSystemName); - _currentStarSystem = DefaultStarSystem; + CurrentStarSystem = DefaultStarSystem; } } @@ -784,7 +810,7 @@ namespace NewHorizons // If we're just on the title screen set the system for later if (LoadManager.GetCurrentScene() == OWScene.TitleScreen) { - _currentStarSystem = newStarSystem; + CurrentStarSystem = newStarSystem; IsWarpingFromShip = warp; IsWarpingFromVessel = vessel; DidWarpFromVessel = false; @@ -828,13 +854,13 @@ namespace NewHorizons { PlayerData.SaveEyeCompletion(); // So that the title screen doesn't keep warping you back to eye - if (SystemDict[_currentStarSystem].Config.enableTimeLoop) SecondsElapsedInLoop = TimeLoop.GetSecondsElapsed(); + if (SystemDict[CurrentStarSystem].Config.enableTimeLoop) SecondsElapsedInLoop = TimeLoop.GetSecondsElapsed(); else SecondsElapsedInLoop = -1; sceneToLoad = OWScene.SolarSystem; } - _currentStarSystem = newStarSystem; + CurrentStarSystem = newStarSystem; // Freeze player inputs OWInput.ChangeInputMode(InputMode.None); @@ -854,7 +880,7 @@ namespace NewHorizons // We reset the solar system on death if (!IsChangingStarSystem) { - if (SystemDict[_currentStarSystem].Config.respawnHere) return; + if (SystemDict[CurrentStarSystem].Config.respawnHere) return; ResetCurrentStarSystem(); } @@ -864,19 +890,20 @@ namespace NewHorizons { if (SystemDict.ContainsKey(_defaultSystemOverride)) { - _currentStarSystem = _defaultSystemOverride; + CurrentStarSystem = _defaultSystemOverride; // Sometimes the override will not support spawning regularly, so always warp in IsWarpingFromShip = true; } else { - if (!string.IsNullOrEmpty(_defaultSystemOverride)) + // Ignore first load because it doesn't even know what systems we have + if (!_firstLoad && !string.IsNullOrEmpty(_defaultSystemOverride)) { NHLogger.LogError($"The given default system override {_defaultSystemOverride} is invalid - no system exists with that name"); } - _currentStarSystem = _defaultStarSystem; + CurrentStarSystem = _defaultStarSystem; IsWarpingFromShip = false; } } diff --git a/NewHorizons/NewHorizonsApi.cs b/NewHorizons/NewHorizonsApi.cs index 5eaf322e..0b25a339 100644 --- a/NewHorizons/NewHorizonsApi.cs +++ b/NewHorizons/NewHorizonsApi.cs @@ -10,6 +10,7 @@ using NewHorizons.External.Modules.Props.Audio; using NewHorizons.External.Modules.Props.Dialogue; using NewHorizons.External.Modules.TranslatorText; using NewHorizons.External.SerializableData; +using NewHorizons.Handlers; using NewHorizons.Utility; using NewHorizons.Utility.OWML; using Newtonsoft.Json; @@ -315,5 +316,11 @@ namespace NewHorizons RumorModeBuilder.AddShipLogXML(GameObject.FindObjectOfType(), xml, body); } + + /// + /// Register your own builder that will act on the given GameObject by reading its raw json string + /// + /// + public void RegisterCustomBuilder(Action builder) => PlanetCreationHandler.CustomBuilders.Add(builder); } } diff --git a/NewHorizons/Patches/EyeScenePatches/LoadManagerPatches.cs b/NewHorizons/Patches/EyeScenePatches/LoadManagerPatches.cs index 2bf53abf..cf12480a 100644 --- a/NewHorizons/Patches/EyeScenePatches/LoadManagerPatches.cs +++ b/NewHorizons/Patches/EyeScenePatches/LoadManagerPatches.cs @@ -12,10 +12,10 @@ namespace NewHorizons.Patches.EyeScenePatches PlayerData.SaveEyeCompletion(); // Switch to default just in case another mod warps back. - if (Main.Instance.CurrentStarSystem == "EyeOfTheUniverse") Main.Instance._currentStarSystem = Main.Instance.DefaultStarSystem; + if (Main.Instance.CurrentStarSystem == "EyeOfTheUniverse") Main.Instance.CurrentStarSystem = Main.Instance.DefaultStarSystem; } // Switch to eye just in case another mod warps there. - else if (scene == OWScene.EyeOfTheUniverse) Main.Instance._currentStarSystem = "EyeOfTheUniverse"; + else if (scene == OWScene.EyeOfTheUniverse) Main.Instance.CurrentStarSystem = "EyeOfTheUniverse"; } [HarmonyPrefix] diff --git a/NewHorizons/Patches/ProxyPatches/ProxyBodyPatches.cs b/NewHorizons/Patches/ProxyPatches/ProxyBodyPatches.cs index 5ff71810..c3d83d83 100644 --- a/NewHorizons/Patches/ProxyPatches/ProxyBodyPatches.cs +++ b/NewHorizons/Patches/ProxyPatches/ProxyBodyPatches.cs @@ -1,15 +1,22 @@ using HarmonyLib; +using NewHorizons.Handlers; -namespace NewHorizons.Patches.ProxyPatches +namespace NewHorizons.Patches.ProxyPatches; + +[HarmonyPatch(typeof(ProxyBody))] +public static class ProxyBodyPatches { - [HarmonyPatch(typeof(ProxyBody))] - public static class ProxyBodyPatches + [HarmonyPostfix] + [HarmonyPatch(nameof(ProxyBody.LateInitialize))] + public static void ProxyBody_LateInitialize(ProxyBody __instance) { - [HarmonyPrefix] - [HarmonyPatch(nameof(ProxyBody.IsObjectInSupernova))] - public static bool ProxyBody_IsObjectInSupernova(ProxyBody __instance) - { - return Locator.GetSunController() != null; - } + ProxyHandler.RegisterVanillaProxyBody(__instance); + } + + [HarmonyPrefix] + [HarmonyPatch(nameof(ProxyBody.IsObjectInSupernova))] + public static bool ProxyBody_IsObjectInSupernova(ProxyBody __instance) + { + return Locator.GetSunController() != null; } } diff --git a/NewHorizons/Patches/ProxyPatches/ProxyOrbiterPatches.cs b/NewHorizons/Patches/ProxyPatches/ProxyOrbiterPatches.cs new file mode 100644 index 00000000..be099350 --- /dev/null +++ b/NewHorizons/Patches/ProxyPatches/ProxyOrbiterPatches.cs @@ -0,0 +1,15 @@ +using HarmonyLib; +using NewHorizons.Handlers; + +namespace NewHorizons.Patches.ProxyPatches; + +[HarmonyPatch(typeof(ProxyOrbiter))] +public static class ProxyOrbiterPatches +{ + [HarmonyPostfix] + [HarmonyPatch(nameof(ProxyOrbiter.SetOriginalBodies))] + public static void ProxyOrbiter_SetOriginalBodies(ProxyOrbiter __instance) + { + ProxyHandler.RegisterVanillaProxyOrbiter(__instance); + } +} diff --git a/NewHorizons/Patches/ProxyPatches/SunProxyPatches.cs b/NewHorizons/Patches/ProxyPatches/SunProxyPatches.cs new file mode 100644 index 00000000..48590d22 --- /dev/null +++ b/NewHorizons/Patches/ProxyPatches/SunProxyPatches.cs @@ -0,0 +1,19 @@ +using HarmonyLib; +using NewHorizons.Utility; + +namespace NewHorizons.Patches.ProxyPatches +{ + [HarmonyPatch(typeof(SunProxy))] + public static class SunProxyPatches + { + [HarmonyPostfix] + [HarmonyPatch(nameof(SunProxy.Start))] + public static void SunProxy_Start(SunProxy __instance) + { + // We mess with the Locator.GetSunTransform() value to switch it to other relevant stars since it's used for some other effects + // However if it's set to a different star when the SunProxy starts it breaks, so we double check it here + __instance._sunTransform = SearchUtilities.Find("Sun_Body").transform; + __instance._realSunController = __instance._sunTransform.GetComponent(); + } + } +} diff --git a/NewHorizons/Patches/ShipLogPatches/ShipLogEntryLocationPatches.cs b/NewHorizons/Patches/ShipLogPatches/ShipLogEntryLocationPatches.cs new file mode 100644 index 00000000..956f22c5 --- /dev/null +++ b/NewHorizons/Patches/ShipLogPatches/ShipLogEntryLocationPatches.cs @@ -0,0 +1,30 @@ +using HarmonyLib; +using NewHorizons.Handlers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NewHorizons.Patches.ShipLogPatches +{ + [HarmonyPatch(typeof(ShipLogEntryLocation))] + public static class ShipLogEntryLocationPatches + { + [HarmonyPrefix] + [HarmonyPatch(nameof(ShipLogEntryLocation.OnValidate))] + public static bool ShipLogEntryLocation_OnValidate(ShipLogEntryLocation __instance) + { + // This part is unchanged + if (!__instance._entryID.Equals(string.Empty) && !__instance.gameObject.name.Equals(__instance._entryID)) + { + __instance.gameObject.name = __instance._entryID; + } + + // Base method checks if its on the Ringworld to see if it can be cloaked, we wanna check for a cloak field controller instead + var cloak = __instance.GetComponentInChildren(); + __instance._isWithinCloakField = cloak != null; + return false; + } + } +} diff --git a/NewHorizons/Patches/SunPatches/SunLightParamUpdaterPatches.cs b/NewHorizons/Patches/SunPatches/SunLightParamUpdaterPatches.cs index 45d68e3e..8062a9c2 100644 --- a/NewHorizons/Patches/SunPatches/SunLightParamUpdaterPatches.cs +++ b/NewHorizons/Patches/SunPatches/SunLightParamUpdaterPatches.cs @@ -1,4 +1,5 @@ using HarmonyLib; +using NewHorizons.Components.Stars; using UnityEngine; namespace NewHorizons.Patches.SunPatches { @@ -13,10 +14,20 @@ namespace NewHorizons.Patches.SunPatches { Vector3 position = __instance.transform.position; float w = 2000f; - if (__instance._sunController != null) + + var sunController = SunLightEffectsController.Instance.ActiveSunController; + var starEvolutionController = SunLightEffectsController.Instance.ActiveStarEvolutionController; + + if (sunController != null) { - w = __instance._sunController.HasSupernovaStarted() ? __instance._sunController.GetSupernovaRadius() : __instance._sunController.GetSurfaceRadius(); + w = sunController.HasSupernovaStarted() ? sunController.GetSupernovaRadius() : sunController.GetSurfaceRadius(); } + // This is an addition in this patch, to work with our stars + else if (starEvolutionController != null) + { + w = starEvolutionController.HasSupernovaStarted() ? starEvolutionController.GetSupernovaRadius() : starEvolutionController.GetSurfaceRadius(); + } + float range = __instance.sunLight.range; Color color = __instance._sunLightController != null ? __instance._sunLightController.sunColor : __instance.sunLight.color; float w2 = __instance._sunLightController != null ? __instance._sunLightController.sunIntensity : __instance.sunLight.intensity; diff --git a/NewHorizons/Patches/WarpPatches/VesselWarpControllerPatches.cs b/NewHorizons/Patches/WarpPatches/VesselWarpControllerPatches.cs index 4676edd4..559f43d0 100644 --- a/NewHorizons/Patches/WarpPatches/VesselWarpControllerPatches.cs +++ b/NewHorizons/Patches/WarpPatches/VesselWarpControllerPatches.cs @@ -29,12 +29,22 @@ namespace NewHorizons.Patches.WarpPatches [HarmonyPatch(nameof(VesselWarpController.CheckSystemActivation))] public static void VesselWarpController_CheckSystemActivation(VesselWarpController __instance) { - if (Locator.GetEyeStateManager() == null && Main.Instance.CurrentStarSystem != "EyeOfTheUniverse") + // Base method only manages the state of the source warp platform blackhole if the EyeStateManager isn't null + // However we place the vessel into other systems, so we want to handle the state in those locations as well + // For some reason the blackhole can also just be null in which case we ignore all this. Happens in Intervention 1.0.3 + if (Locator.GetEyeStateManager() == null && Main.Instance.CurrentStarSystem != "EyeOfTheUniverse" && __instance._sourceWarpPlatform._blackHole != null) { - if (!__instance._sourceWarpPlatform.IsBlackHoleOpen() && __instance._hasPower && __instance._warpPlatformPowerSlot.IsActivated() && __instance._targetWarpPlatform != null) - __instance._sourceWarpPlatform.OpenBlackHole(__instance._targetWarpPlatform, true); - else if (__instance._sourceWarpPlatform.IsBlackHoleOpen() && (!__instance._hasPower || !__instance._warpPlatformPowerSlot.IsActivated())) + var isBlackHoleOpen = __instance._sourceWarpPlatform.IsBlackHoleOpen(); + var shouldBlackHoleBeOpen = __instance._hasPower && __instance._warpPlatformPowerSlot.IsActivated() && __instance._targetWarpPlatform != null; + + if (isBlackHoleOpen && !shouldBlackHoleBeOpen) + { __instance._sourceWarpPlatform.CloseBlackHole(); + } + else if (!isBlackHoleOpen && shouldBlackHoleBeOpen) + { + __instance._sourceWarpPlatform.OpenBlackHole(__instance._targetWarpPlatform, true); + } } } @@ -53,7 +63,7 @@ namespace NewHorizons.Patches.WarpPatches Locator.GetPauseCommandListener().AddPauseCommandLock(); if (canWarpToEye || canWarpToStarSystem && targetSystem == "EyeOfTheUniverse") { - Main.Instance._currentStarSystem = "EyeOfTheUniverse"; + Main.Instance.CurrentStarSystem = "EyeOfTheUniverse"; LoadManager.LoadSceneAsync(OWScene.EyeOfTheUniverse, false, LoadManager.FadeType.ToWhite); } else if (canWarpToStarSystem) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index ddab910d..f02aafbb 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -132,6 +132,13 @@ "description": "Add water to this planet", "$ref": "#/definitions/WaterModule" }, + "ParticleFields": { + "type": "array", + "description": "Add particle effects in a field around the planet.\nAlso known as Vection Fields.", + "items": { + "$ref": "#/definitions/ParticleFieldModule" + } + }, "Volumes": { "description": "Add various volumes on this body", "$ref": "#/definitions/VolumesModule" @@ -371,11 +378,7 @@ }, "hasRain": { "type": "boolean", - "description": "Does this planet have rain?" - }, - "hasSnow": { - "type": "boolean", - "description": "Does this planet have snow?" + "description": "Does this planet have rain? \nThis is equivalent to effects of setting a rain particle/vection field, rain audio volume, and visor effect volume, combined for convenience.\nFor more control over the rain, use those individual components." }, "size": { "type": "number", @@ -1056,6 +1059,10 @@ "type": "boolean", "description": "Should the body always have one side facing its primary?" }, + "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." + }, "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" @@ -1069,10 +1076,6 @@ "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" @@ -1081,6 +1084,16 @@ "type": "boolean", "description": "Should we just draw a line behind its orbit instead of the entire circle/ellipse?" }, + "orbitLineFadeEndDistance": { + "type": "number", + "description": "If the camera is farther than this distance the orbit line will fade out. Leave empty to not have it fade out.", + "format": "float" + }, + "orbitLineFadeStartDistance": { + "type": "number", + "description": "If the camera is closer than this distance the orbit line will fade out. Leave empty to not have it fade out.", + "format": "float" + }, "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.", @@ -3573,6 +3586,102 @@ } } }, + "ParticleFieldModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "Particle type for this vection field.", + "$ref": "#/definitions/ParticleFieldType" + }, + "followTarget": { + "description": "What the particle field activates based on.", + "default": "player", + "$ref": "#/definitions/FollowTarget" + }, + "densityByHeightCurve": { + "type": "array", + "description": "Density by height curve. Determines how many particles are emitted at different heights.\nDefaults to a curve based on minimum and maximum heights of various other modules.", + "items": { + "$ref": "#/definitions/HeightDensityPair" + } + }, + "rename": { + "type": "string", + "description": "An optional rename of this object" + } + } + }, + "ParticleFieldType": { + "type": "string", + "description": "", + "x-enumNames": [ + "Rain", + "SnowflakesHeavy", + "SnowflakesLight", + "Embers", + "Clouds", + "Leaves", + "Bubbles", + "Fog", + "CrystalMotes", + "RockMotes", + "IceMotes", + "SandMotes", + "Crawlies", + "Fireflies", + "Plankton", + "Pollen", + "Current" + ], + "enum": [ + "rain", + "snowflakesHeavy", + "snowflakesLight", + "embers", + "clouds", + "leaves", + "bubbles", + "fog", + "crystalMotes", + "rockMotes", + "iceMotes", + "sandMotes", + "crawlies", + "fireflies", + "plankton", + "pollen", + "current" + ] + }, + "FollowTarget": { + "type": "string", + "description": "", + "x-enumNames": [ + "Player", + "Probe" + ], + "enum": [ + "player", + "probe" + ] + }, + "HeightDensityPair": { + "type": "object", + "additionalProperties": false, + "properties": { + "height": { + "type": "number", + "description": "A specific radius", + "format": "float" + }, + "density": { + "type": "number", + "description": "The particle count for this radius.", + "format": "float" + } + } + }, "VolumesModule": { "type": "object", "additionalProperties": false, diff --git a/NewHorizons/Utility/NewHorizonExtensions.cs b/NewHorizons/Utility/NewHorizonExtensions.cs index e8e2222c..b292b06b 100644 --- a/NewHorizons/Utility/NewHorizonExtensions.cs +++ b/NewHorizons/Utility/NewHorizonExtensions.cs @@ -1,4 +1,5 @@ using NewHorizons.External.Configs; +using NewHorizons.External.Modules.VariableSize; using NewHorizons.External.SerializableData; using NewHorizons.External.SerializableEnums; using NewHorizons.Utility.OWML; @@ -12,6 +13,7 @@ using System.Reflection; using System.Text; using System.Text.RegularExpressions; using UnityEngine; +using static NewHorizons.External.Modules.ParticleFieldModule; using NomaiCoordinates = NewHorizons.External.Configs.StarSystemConfig.NomaiCoordinates; namespace NewHorizons.Utility @@ -277,5 +279,31 @@ namespace NewHorizons.Utility { go.transform.rotation = Quaternion.FromToRotation(Vector3.forward, direction); } + + public static AnimationCurve ToAnimationCurve(this TimeValuePair[] pairs) + { + var curve = new AnimationCurve(); + if (pairs != null) + { + foreach (var pair in pairs) + { + curve.AddKey(new Keyframe(pair.time, pair.value)); + } + } + return curve; + } + + public static AnimationCurve ToAnimationCurve(this HeightDensityPair[] pairs) + { + var curve = new AnimationCurve(); + if (pairs != null) + { + foreach (var pair in pairs) + { + curve.AddKey(new Keyframe(pair.height, pair.density)); + } + } + return curve; + } } } diff --git a/NewHorizons/Utility/SearchUtilities.cs b/NewHorizons/Utility/SearchUtilities.cs index 0bbc5990..9ed77826 100644 --- a/NewHorizons/Utility/SearchUtilities.cs +++ b/NewHorizons/Utility/SearchUtilities.cs @@ -10,11 +10,13 @@ namespace NewHorizons.Utility public static class SearchUtilities { private static readonly Dictionary CachedGameObjects = new Dictionary(); + private static readonly Dictionary CachedRootGameObjects = new Dictionary(); public static void ClearCache() { NHLogger.LogVerbose("Clearing search cache"); CachedGameObjects.Clear(); + CachedRootGameObjects.Clear(); } public static List FindObjectsOfTypeAndName(string name) where T : Object @@ -107,8 +109,16 @@ namespace NewHorizons.Utility // 2: find inactive using root + transform.find var names = path.Split('/'); + // Cache the root objects so we don't loop through all of them each time var rootName = names[0]; - var root = SceneManager.GetActiveScene().GetRootGameObjects().FirstOrDefault(x => x.name == rootName); + if (!CachedRootGameObjects.TryGetValue(rootName, out var root)) + { + root = SceneManager.GetActiveScene().GetRootGameObjects().FirstOrDefault(x => x.name == rootName); + if (root != null) + { + CachedRootGameObjects.Add(rootName, root); + } + } var childPath = string.Join("/", names.Skip(1)); go = root ? root.FindChild(childPath) : null; diff --git a/NewHorizons/manifest.json b/NewHorizons/manifest.json index 762b1c52..ff707cb8 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.14.6", + "version": "1.16.0", "owmlVersion": "2.9.3", "dependencies": [ "JohnCorby.VanillaFix", "_nebula.MenuFramework", "xen.CommonCameraUtility", "dgarro.CustomShipLogModes" ], "conflicts": [ "Raicuparta.QuantumSpaceBuddies", "PacificEngine.OW_CommonResources" ],