using System; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; using NewHorizons.Utility; using Newtonsoft.Json; using static NewHorizons.External.Modules.ShipLogModule; namespace NewHorizons.External.Configs { /// /// Configuration for a specific star system /// [JsonObject] public class StarSystemConfig { /// /// Whether this system can be warped to via the warp drive /// [DefaultValue(true)] public bool canEnterViaWarpDrive = true; /// /// Do you want a clean slate for this star system? Or will it be a modified version of the original. /// [DefaultValue(true)] public bool destroyStockPlanets = true; /// /// Should the time loop be enabled in this system? /// [DefaultValue(true)] public bool enableTimeLoop = true; /// /// Set to the FactID that must be revealed before it can be warped to. Don't set `CanEnterViaWarpDrive` to `false` if /// you're using this, that would make no sense. /// public string factRequiredForWarp; /// /// Should the player not be able to view the map in this system? /// public bool mapRestricted; /// /// Customize the skybox for this system /// [Obsolete("skybox is deprecated, please use Skybox instead")] public SkyboxConfig skybox; /// /// Customize the skybox for this system /// public SkyboxModule Skybox; /// /// Set to `true` if you want to spawn here after dying, not Timber Hearth. You can still warp back to the main star /// system. /// public bool startHere; [Obsolete("travelAudioClip is deprecated, please use travelAudio instead")] public string travelAudioClip; [Obsolete("travelAudioFilePath is deprecated, please use travelAudio instead")] public string travelAudioFilePath; /// /// The audio that will play when travelling in space. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list. /// public string travelAudio; /// /// Configure warping to this system with the vessel /// public VesselModule Vessel; [Obsolete("coords is deprecated, please use Vessel.coords instead")] public NomaiCoordinates coords; [Obsolete("vesselPosition is deprecated, please use Vessel.vesselPosition instead")] public MVector3 vesselPosition; [Obsolete("vesselRotation is deprecated, please use Vessel.vesselRotation instead")] public MVector3 vesselRotation; [Obsolete("warpExitPosition is deprecated, please use Vessel.warpExitPosition instead")] public MVector3 warpExitPosition; [Obsolete("warpExitRotation is deprecated, please use Vessel.warpExitRotation instead")] public MVector3 warpExitRotation; /// /// Manually layout ship log entries in detective mode /// public EntryPositionInfo[] entryPositions; /// /// A list of fact IDs to reveal when the game starts. /// public string[] initialReveal; /// /// List colors of curiosity entries /// public CuriosityColorInfo[] curiosities; public class NomaiCoordinates { [MinLength(2)] [MaxLength(6)] public int[] x; [MinLength(2)] [MaxLength(6)] public int[] y; [MinLength(2)] [MaxLength(6)] public int[] z; } [Obsolete("SkyboxConfig is deprecated, please use SkyboxModule instead")] [JsonObject] public class SkyboxConfig { /// /// Path to the Unity asset bundle to load the skybox material from /// public string assetBundle; /// /// Whether to destroy the star field around the player /// public bool destroyStarField; /// /// Path to the material within the asset bundle specified by `assetBundle` to use for the skybox /// public string path; } [JsonObject] public class SkyboxModule { /// /// Whether to destroy the star field around the player /// public bool destroyStarField; /// /// Whether to use a cube for the skybox instead of a smooth sphere /// public bool useCube; /// /// Relative filepath to the texture to use for the skybox's positive X direction /// public string rightPath; /// /// Relative filepath to the texture to use for the skybox's negative X direction /// public string leftPath; /// /// Relative filepath to the texture to use for the skybox's positive Y direction /// public string topPath; /// /// Relative filepath to the texture to use for the skybox's negative Y direction /// public string bottomPath; /// /// Relative filepath to the texture to use for the skybox's positive Z direction /// public string frontPath; /// /// Relative filepath to the texture to use for the skybox's negative Z direction /// public string backPath; } [JsonObject] public class VesselModule { /// /// Coordinates that the vessel can use to warp to your solar system. /// public NomaiCoordinates coords; /// /// The position in the solar system the vessel will warp to. /// public MVector3 vesselPosition; /// /// Euler angles by which the vessel will be oriented. /// public MVector3 vesselRotation; /// /// The relative position to the vessel that you will be teleported to when you exit the vessel through the black hole. /// public MVector3 warpExitPosition; /// /// Euler angles by which the warp exit will be oriented. /// public MVector3 warpExitRotation; } /// /// Makes sure they are all numbers are unique and between 0 and 5. /// private static int[] FixAxis(int[] axis) => axis.Distinct().Where(i => (i >= 0 && i <= 5)).ToArray(); public void FixCoordinates() { if (Vessel?.coords != null) { Vessel.coords.x = FixAxis(Vessel.coords.x); Vessel.coords.y = FixAxis(Vessel.coords.y); Vessel.coords.z = FixAxis(Vessel.coords.z); } } public void Merge(StarSystemConfig otherConfig) { // Imagine if this used reflection // True by default so if one is false go false canEnterViaWarpDrive = canEnterViaWarpDrive && otherConfig.canEnterViaWarpDrive; destroyStockPlanets = destroyStockPlanets && otherConfig.destroyStockPlanets; enableTimeLoop = enableTimeLoop && otherConfig.enableTimeLoop; // If current one is null take the other factRequiredForWarp = string.IsNullOrEmpty(factRequiredForWarp) ? otherConfig.factRequiredForWarp : factRequiredForWarp; #pragma warning disable CS0618 // Type or member is obsolete skybox = skybox == null ? otherConfig.skybox : skybox; #pragma warning restore CS0618 // Type or member is obsolete Skybox = Skybox == null ? otherConfig.Skybox : Skybox; travelAudio = string.IsNullOrEmpty(travelAudio) ? otherConfig.travelAudio : travelAudio; // False by default so if one is true go true mapRestricted = mapRestricted || otherConfig.mapRestricted; mapRestricted = mapRestricted || otherConfig.mapRestricted; startHere = startHere || otherConfig.startHere; Vessel = Vessel == null ? otherConfig.Vessel : Vessel; entryPositions = Concatenate(entryPositions, otherConfig.entryPositions); curiosities = Concatenate(curiosities, otherConfig.curiosities); initialReveal = Concatenate(initialReveal, otherConfig.initialReveal); } private T[] Concatenate(T[] array1, T[] array2) { return (array1 ?? new T[0]).Concat(array2 ?? new T[0]).ToArray(); } public void Migrate() { // Backwards compatability // Should be the only place that obsolete things are referenced #pragma warning disable 612, 618 if (!string.IsNullOrEmpty(travelAudioClip)) travelAudio = travelAudioClip; if (!string.IsNullOrEmpty(travelAudioFilePath)) travelAudio = travelAudioFilePath; if (skybox != null) { if (Skybox == null) { Skybox = new SkyboxModule(); Skybox.destroyStarField = skybox.destroyStarField; } } if (coords != null || vesselPosition != null || vesselRotation != null || warpExitPosition != null || warpExitRotation != null) { if (Vessel == null) { Vessel = new VesselModule(); } Vessel.coords = Vessel.coords ?? coords; Vessel.vesselPosition = Vessel.vesselPosition ?? vesselPosition; Vessel.vesselRotation = Vessel.vesselRotation ?? vesselRotation; Vessel.warpExitPosition = Vessel.warpExitPosition ?? warpExitPosition; Vessel.warpExitRotation = Vessel.warpExitRotation ?? warpExitRotation; } } } }