diff --git a/NewHorizons/Builder/Atmosphere/EffectsBuilder.cs b/NewHorizons/Builder/Atmosphere/EffectsBuilder.cs index 3b11b857..d952101c 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,55 @@ 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 vectionField in config.VectionFields) { - var rainGO = Object.Instantiate(_rainEmitterPrefab, effectsGO.transform); - rainGO.name = "RainEmitter"; - rainGO.transform.position = planetGO.transform.position; + var emitter = Object.Instantiate(_rainEmitterPrefab, effectsGO.transform); + emitter.name = !string.IsNullOrWhiteSpace(vectionField.rename) ? vectionField.rename : emitter.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 = vectionField.densityByHeightCurve != null ? vectionField.densityByHeightCurve.ToAnimationCurve() : new AnimationCurve(new Keyframe[] { new Keyframe(minHeight - 0.5f, 0), new Keyframe(minHeight, 10f), new Keyframe(maxHeight, 0f) }); + pvc._followTarget = vectionField.followTarget == VectionFieldModule.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(VectionFieldModule.VectionFieldType type) + { + return type switch + { + VectionFieldModule.VectionFieldType.Rain => _rainEmitterPrefab, + VectionFieldModule.VectionFieldType.SnowflakesHeavy => _snowEmitterPrefab, + VectionFieldModule.VectionFieldType.SnowflakesLight => _snowLightEmitterPrefab, + VectionFieldModule.VectionFieldType.Embers => _emberEmitterPrefab, + VectionFieldModule.VectionFieldType.Clouds => _cloudEmitterPrefab, + VectionFieldModule.VectionFieldType.Leaves => _leafEmitterPrefab, + VectionFieldModule.VectionFieldType.Bubbles => _bubbleEmitterPrefab, + VectionFieldModule.VectionFieldType.Fog => _fogEmitterPrefab, + VectionFieldModule.VectionFieldType.CrystalMotes => _crystalMoteEmitterPrefab, + VectionFieldModule.VectionFieldType.RockMotes => _rockMoteEmitterPrefab, + VectionFieldModule.VectionFieldType.IceMotes => _iceMoteEmitterPrefab, + VectionFieldModule.VectionFieldType.SandMotes => _sandMoteEmitterPrefab, + VectionFieldModule.VectionFieldType.Crawlies => _crawliesEmitterPrefab, + VectionFieldModule.VectionFieldType.Fireflies => _firefliesEmitterPrefab, + VectionFieldModule.VectionFieldType.Plankton => _planktonEmitterPrefab, + VectionFieldModule.VectionFieldType.Pollen => _pollenEmitterPrefab, + VectionFieldModule.VectionFieldType.Current => _currentEmitterPrefab, + _ => null, + }; + } } } diff --git a/NewHorizons/External/Configs/PlanetConfig.cs b/NewHorizons/External/Configs/PlanetConfig.cs index e76225c4..d5727b69 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,11 @@ namespace NewHorizons.External.Configs /// public WaterModule Water; + /// + /// Add particle effects to the planet + /// + public VectionFieldModule[] VectionFields; + /// /// Add various volumes on this body /// @@ -341,6 +347,29 @@ namespace NewHorizons.External.Configs // useBasicCloudShader is obsolete if (Atmosphere.clouds != null && Atmosphere.clouds.useBasicCloudShader) Atmosphere.clouds.cloudsPrefab = CloudPrefabType.Basic; + + if (Atmosphere.hasRain) + { + if (VectionFields == null) VectionFields = new VectionFieldModule[0]; + VectionFields = VectionFields.Append(new VectionFieldModule + { + type = VectionFieldModule.VectionFieldType.Rain, + rename = "RainEmitter" + }).ToArray(); + } + + if (Atmosphere.hasSnow) + { + if (VectionFields == null) VectionFields = new VectionFieldModule[0]; + for (int i = 0; i < 5; i++) + { + VectionFields = VectionFields.Append(new VectionFieldModule + { + type = VectionFieldModule.VectionFieldType.SnowflakesHeavy, + rename = "SnowEmitter" + }).ToArray(); + } + } } if (Props?.tornados != null) diff --git a/NewHorizons/External/Modules/AtmosphereModule.cs b/NewHorizons/External/Modules/AtmosphereModule.cs index 675ed837..d0665941 100644 --- a/NewHorizons/External/Modules/AtmosphereModule.cs +++ b/NewHorizons/External/Modules/AtmosphereModule.cs @@ -74,16 +74,6 @@ namespace NewHorizons.External.Modules /// public bool hasTrees; - /// - /// Does this planet have rain? - /// - public bool hasRain; - - /// - /// Does this planet have snow? - /// - public bool hasSnow; - /// /// Scale height of the atmosphere /// @@ -190,6 +180,12 @@ namespace NewHorizons.External.Modules #region Obsolete + [Obsolete("HasRain is deprecated, please use VectionFields instead")] + public bool hasRain; + + [Obsolete("HasSnow is deprecated, please use VectionFields instead")] + public bool hasSnow; + [Obsolete("CloudTint is deprecated, please use CloudInfo instead")] public MColor cloudTint; diff --git a/NewHorizons/External/Modules/VectionFieldModule.cs b/NewHorizons/External/Modules/VectionFieldModule.cs new file mode 100644 index 00000000..a7636ae7 --- /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 VectionFieldModule + { + /// + /// Particle type for this vection field. + /// + public VectionFieldType type; + + /// + /// What the vection 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 VectionFieldType + { + [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 38958aa2..b65dbdc4 100644 --- a/NewHorizons/Handlers/PlanetCreationHandler.cs +++ b/NewHorizons/Handlers/PlanetCreationHandler.cs @@ -650,9 +650,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); @@ -661,6 +658,11 @@ namespace NewHorizons.Handlers atmosphere = AtmosphereBuilder.Make(go, sector, body.Config.Atmosphere, surfaceSize).GetComponentInChildren(); } + if (body.Config.VectionFields != null) + { + EffectsBuilder.Make(go, sector, body.Config); + } + if (body.Config.Props != null) { PropBuildManager.Make(go, sector, rb, body); diff --git a/NewHorizons/Utility/NewHorizonExtensions.cs b/NewHorizons/Utility/NewHorizonExtensions.cs index e8e2222c..22f0ff4d 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.VectionFieldModule; 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; + } } }