diff --git a/NewHorizons/Builder/Atmosphere/FogBuilder.cs b/NewHorizons/Builder/Atmosphere/FogBuilder.cs index c453dfea5..8c1025bca 100644 --- a/NewHorizons/Builder/Atmosphere/FogBuilder.cs +++ b/NewHorizons/Builder/Atmosphere/FogBuilder.cs @@ -1,6 +1,7 @@ using NewHorizons.External.Modules; using NewHorizons.Utility; using NewHorizons.Utility.Files; +using OWML.Common; using UnityEngine; namespace NewHorizons.Builder.Atmosphere { @@ -35,7 +36,7 @@ internal static void InitPrefabs() if (_dbImpostorMaterials == null) _dbImpostorMaterials = SearchUtilities.Find("DarkBramble_Body/Atmosphere_DB/FogLOD").GetComponent().sharedMaterials.MakePrefabMaterials(); } - public static PlanetaryFogController Make(GameObject planetGO, Sector sector, AtmosphereModule atmo) + public static PlanetaryFogController Make(GameObject planetGO, Sector sector, AtmosphereModule atmo, IModBehaviour mod) { InitPrefabs(); @@ -58,7 +59,10 @@ public static PlanetaryFogController Make(GameObject planetGO, Sector sector, At PFC.lodFadeDistance = PFC.fogRadius * 0.5f; PFC.fogDensity = atmo.fogDensity; PFC.fogExponent = 1f; - var colorRampTexture = atmo.fogTint == null ? _ramp : ImageUtilities.TintImage(_ramp, atmo.fogTint.ToColor()); + var colorRampTexture = + atmo.fogRampPath != null ? ImageUtilities.GetTexture(mod, atmo.fogRampPath) : + atmo.fogTint != null ? ImageUtilities.TintImage(_ramp, atmo.fogTint.ToColor()) : + _ramp; PFC.fogColorRampTexture = colorRampTexture; PFC.fogColorRampIntensity = 1f; if (atmo.fogTint != null) @@ -78,7 +82,7 @@ public static PlanetaryFogController Make(GameObject planetGO, Sector sector, At return PFC; } - public static Renderer MakeProxy(GameObject proxyGO, AtmosphereModule atmo) + public static Renderer MakeProxy(GameObject proxyGO, AtmosphereModule atmo, IModBehaviour mod) { InitPrefabs(); @@ -94,7 +98,10 @@ public static Renderer MakeProxy(GameObject proxyGO, AtmosphereModule atmo) MR.materials = _dbImpostorMaterials; MR.allowOcclusionWhenDynamic = true; - var colorRampTexture = atmo.fogTint == null ? _ramp : ImageUtilities.TintImage(_ramp, atmo.fogTint.ToColor()); + var colorRampTexture = + atmo.fogRampPath != null ? ImageUtilities.GetTexture(mod, atmo.fogRampPath) : + atmo.fogTint != null ? ImageUtilities.TintImage(_ramp, atmo.fogTint.ToColor()) : + _ramp; if (atmo.fogTint != null) { MR.material.SetColor(Tint, atmo.fogTint.ToColor()); diff --git a/NewHorizons/Builder/Body/ProxyBuilder.cs b/NewHorizons/Builder/Body/ProxyBuilder.cs index 0f87a90a5..c788c3b24 100644 --- a/NewHorizons/Builder/Body/ProxyBuilder.cs +++ b/NewHorizons/Builder/Body/ProxyBuilder.cs @@ -120,7 +120,7 @@ private static bool SharedMake(GameObject planetGO, GameObject proxy, NHProxy pr if (body.Config.Atmosphere.fogSize != 0) { - fog = FogBuilder.MakeProxy(proxy, body.Config.Atmosphere); + fog = FogBuilder.MakeProxy(proxy, body.Config.Atmosphere, body.Mod); fogCurveMaxVal = body.Config.Atmosphere.fogDensity; } diff --git a/NewHorizons/Builder/Body/StarBuilder.cs b/NewHorizons/Builder/Body/StarBuilder.cs index 53379f407..f4954af58 100644 --- a/NewHorizons/Builder/Body/StarBuilder.cs +++ b/NewHorizons/Builder/Body/StarBuilder.cs @@ -7,6 +7,7 @@ using NewHorizons.Components.Stars; using NewHorizons.Utility.OuterWilds; using NewHorizons.Utility.Files; +using NewHorizons.Utility.OWML; namespace NewHorizons.Builder.Body { @@ -348,28 +349,42 @@ public static GameObject MakeStarGraphics(GameObject rootObject, Sector sector, solarFlareEmitter.name = "SolarFlareEmitter"; solarFlareEmitter.SetActive(true); + var emitter = solarFlareEmitter.GetComponent(); + + if (starModule.solarFlareSettings != null) + { + emitter._minTimeBetweenFlares = starModule.solarFlareSettings.minTimeBetweenFlares; + emitter._maxTimeBetweenFlares = starModule.solarFlareSettings.maxTimeBetweenFlares; + emitter._lifeLength = starModule.solarFlareSettings.lifeLength; + } + if (starModule.tint != null) { - var flareTint = starModule.tint.ToColor(); - var emitter = solarFlareEmitter.GetComponent(); - emitter.tint = flareTint; + emitter.tint = starModule.tint.ToColor(); + } - var material = new Material(_flareMaterial); - // Since the star isn't awake yet the controllers haven't been made - foreach (var prefab in new GameObject[] { emitter.domePrefab, emitter.loopPrefab, emitter.streamerPrefab }) + var material = new Material(_flareMaterial); + // Since the star isn't awake yet the controllers haven't been made + foreach (var prefab in new GameObject[] { emitter.domePrefab, emitter.loopPrefab, emitter.streamerPrefab }) + { + var controller = prefab.GetComponent(); + // controller._meshRenderer doesn't exist yet since Awake hasn't been called + if (starModule.tint != null) { - var controller = prefab.GetComponent(); - // controller._meshRenderer doesn't exist yet since Awake hasn't been called controller.GetComponent().sharedMaterial = material; controller._color = Color.white; - controller._tint = flareTint; + controller._tint = starModule.tint.ToColor(); + } + if (starModule.solarFlareSettings != null) + { + controller._scaleFactor = Vector3.one * starModule.solarFlareSettings.scaleFactor; } } starGO.transform.position = rootObject.transform.position; starGO.transform.localScale = starModule.size * Vector3.one; - TessellatedSphereRenderer surface = sunSurface.GetComponent(); + var surface = sunSurface.GetComponent(); if (starModule.tint != null) { diff --git a/NewHorizons/Builder/General/AstroObjectBuilder.cs b/NewHorizons/Builder/General/AstroObjectBuilder.cs index 8278d056c..459667623 100644 --- a/NewHorizons/Builder/General/AstroObjectBuilder.cs +++ b/NewHorizons/Builder/General/AstroObjectBuilder.cs @@ -41,18 +41,19 @@ public static NHAstroObject Make(GameObject body, AstroObject primaryBody, Plane var alignmentAxis = config.Orbit.alignmentAxis ?? new Vector3(0, -1, 0); // Start it off facing the right way - var facing = body.transform.TransformDirection(alignmentAxis); - body.transform.rotation = Quaternion.FromToRotation(facing, alignmentAxis) * body.transform.rotation; - var alignment = body.AddComponent(); alignment.SetTargetBody(primaryBody?.GetAttachedOWRigidbody()); - alignment._usePhysicsToRotate = false; alignment._localAlignmentAxis = alignmentAxis; + alignment._owRigidbody = body.GetComponent(); + + // Have it face the right way + var currentDirection = alignment.transform.TransformDirection(alignment._localAlignmentAxis); + var targetDirection = alignment.GetAlignmentDirection(); + alignment.transform.rotation = Quaternion.FromToRotation(currentDirection, targetDirection) * alignment.transform.rotation; + alignment._owRigidbody.SetAngularVelocity(Vector3.zero); // Static bodies won't update rotation with physics for some reason - // Have to set it in 2 ticks else it flings the player into deep space on spawn (#171) - // Pushed to 3 frames after system is ready, bc spawning takes 2 frames, this is hurting my brain too much to try to improve the numbers idc - if (!config.Orbit.isStatic) Delay.RunWhen(() => Main.IsSystemReady, () => Delay.FireInNUpdates(() => alignment._usePhysicsToRotate = true, 3)); + alignment._usePhysicsToRotate = !config.Orbit.isStatic; } if (config.Base.centerOfSolarSystem) diff --git a/NewHorizons/Builder/General/SpawnPointBuilder.cs b/NewHorizons/Builder/General/SpawnPointBuilder.cs index 4ae1084b9..591defb42 100644 --- a/NewHorizons/Builder/General/SpawnPointBuilder.cs +++ b/NewHorizons/Builder/General/SpawnPointBuilder.cs @@ -21,7 +21,6 @@ public static SpawnPoint Make(GameObject planetGO, SpawnModule module, OWRigidbo if (module.playerSpawn != null) { GameObject spawnGO = GeneralPropBuilder.MakeNew("PlayerSpawnPoint", planetGO, null, module.playerSpawn); - spawnGO.SetActive(false); spawnGO.layer = Layer.PlayerSafetyCollider; playerSpawn = spawnGO.AddComponent(); @@ -31,8 +30,7 @@ public static SpawnPoint Make(GameObject planetGO, SpawnModule module, OWRigidbo playerSpawn._triggerVolumes = new OWTriggerVolume[0]; // This was a stupid hack to stop players getting stuck in the ground and now we have to keep it forever - spawnGO.transform.position += 4f * spawnGO.transform.up; - spawnGO.SetActive(true); + spawnGO.transform.position += spawnGO.transform.TransformDirection(module.playerSpawn.offset ?? Vector3.up * 4f); } if (module.shipSpawn != null) @@ -57,7 +55,11 @@ public static SpawnPoint Make(GameObject planetGO, SpawnModule module, OWRigidbo ship.transform.rotation = spawnGO.transform.rotation; // Move it up a bit more when aligning to surface - if (module.shipSpawn.alignRadial.GetValueOrDefault()) + if (module.shipSpawn.offset != null) + { + ship.transform.position += spawnGO.transform.TransformDirection(module.shipSpawn.offset); + } + else if (module.shipSpawn.alignRadial.GetValueOrDefault()) { ship.transform.position += ship.transform.up * 4f; } diff --git a/NewHorizons/Builder/Orbital/InitialMotionBuilder.cs b/NewHorizons/Builder/Orbital/InitialMotionBuilder.cs index c6405bd9b..420fa2bfe 100644 --- a/NewHorizons/Builder/Orbital/InitialMotionBuilder.cs +++ b/NewHorizons/Builder/Orbital/InitialMotionBuilder.cs @@ -24,10 +24,12 @@ public static InitialMotion SetInitialMotionFromConfig(InitialMotion initialMoti initialMotion._initAngularSpeed = orbit.siderealPeriod == 0 ? 0f : 2f * Mathf.PI / (orbit.siderealPeriod * 60f); var rotationAxis = Quaternion.AngleAxis(orbit.axialTilt, Vector3.right) * Vector3.up; - // For things with children this is broken + // For stock planets with unsuspended rigidbody children this is broken + // For planets with rafts this is broken if (AstroObjectLocator.GetChildren(secondaryBody).Length == 0) { secondaryBody.transform.rotation = Quaternion.FromToRotation(Vector3.up, rotationAxis); + secondaryBody.transform.Rotate(rotationAxis, orbit.initialRotation); } if (!orbit.isStatic && primaryBody != null) diff --git a/NewHorizons/Components/ShipLog/ShipLogStarChartMode.cs b/NewHorizons/Components/ShipLog/ShipLogStarChartMode.cs index afdf5f2e6..cae4a4b94 100644 --- a/NewHorizons/Components/ShipLog/ShipLogStarChartMode.cs +++ b/NewHorizons/Components/ShipLog/ShipLogStarChartMode.cs @@ -134,9 +134,18 @@ public GameObject CreateCard(string uniqueID, Transform parent, Vector2 position } else { - var path = Path.Combine("planets", uniqueID + ".png"); + var mod = Main.SystemDict[uniqueID].Mod; + + var path = Path.Combine("systems", uniqueID + ".png"); + + // Else check the old location + if (!File.Exists(Path.Combine(mod.ModHelper.Manifest.ModFolderPath, path))) + { + path = Path.Combine("planets", uniqueID + ".png"); + } + NHLogger.LogVerbose($"ShipLogStarChartManager - Trying to load {path}"); - texture = ImageUtilities.GetTexture(Main.SystemDict[uniqueID].Mod, path); + texture = ImageUtilities.GetTexture(mod, path); } } catch (Exception) { } diff --git a/NewHorizons/Components/Volumes/LoadCreditsVolume.cs b/NewHorizons/Components/Volumes/LoadCreditsVolume.cs index 3fc191f21..18edcbd11 100644 --- a/NewHorizons/Components/Volumes/LoadCreditsVolume.cs +++ b/NewHorizons/Components/Volumes/LoadCreditsVolume.cs @@ -1,5 +1,6 @@ using NewHorizons.External.SerializableEnums; using NewHorizons.Handlers; +using NewHorizons.Utility; using NewHorizons.Utility.OWML; using System.Collections; using UnityEngine; @@ -28,7 +29,7 @@ public override void OnTriggerVolumeEntry(GameObject hitObj) if (hitObj.CompareTag("PlayerDetector") && enabled) { // Have to run it off the mod behaviour since the game over controller disables everything - Main.Instance.StartCoroutine(GameOver()); + Delay.StartCoroutine(GameOver()); } } diff --git a/NewHorizons/External/Configs/StarSystemConfig.cs b/NewHorizons/External/Configs/StarSystemConfig.cs index b16a209e3..bd998909d 100644 --- a/NewHorizons/External/Configs/StarSystemConfig.cs +++ b/NewHorizons/External/Configs/StarSystemConfig.cs @@ -15,6 +15,16 @@ namespace NewHorizons.External.Configs [JsonObject] public class StarSystemConfig { + /// + /// In this system should the player be able to rotate their map camera freely or be stuck above the plane of the solar system? + /// + public bool freeMapAngle; + + /// + /// When well past the furthest orbit, should the player be summoned back to the star? + /// + public bool returnToSolarSystemWhenTooFar; + /// /// An override value for the far clip plane. Allows you to see farther. /// diff --git a/NewHorizons/External/Modules/AtmosphereModule.cs b/NewHorizons/External/Modules/AtmosphereModule.cs index b1f563aca..675ed8375 100644 --- a/NewHorizons/External/Modules/AtmosphereModule.cs +++ b/NewHorizons/External/Modules/AtmosphereModule.cs @@ -46,7 +46,7 @@ public class AtmosphereModule /// /// How dense the fog is, if you put fog. /// - [Range(0f, 1f)] public float fogDensity; + [Range(0f, double.MaxValue)] public float fogDensity; /// /// Radius of fog sphere, independent of the atmosphere. This has to be set for there to be fog. @@ -57,6 +57,12 @@ public class AtmosphereModule /// 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). + /// + public string fogRampPath; /// /// Lets you survive on the planet without a suit. diff --git a/NewHorizons/External/Modules/OrbitModule.cs b/NewHorizons/External/Modules/OrbitModule.cs index 84fa1d89b..e3a6a566c 100644 --- a/NewHorizons/External/Modules/OrbitModule.cs +++ b/NewHorizons/External/Modules/OrbitModule.cs @@ -34,6 +34,12 @@ public class OrbitModule : IOrbitalParameters /// public float siderealPeriod; + /// + /// Offsets the planet's starting sidereal rotation. In degrees. + /// + [Range(0f, 360f)] + public float initialRotation; + /// /// Should the body always have one side facing its primary? /// diff --git a/NewHorizons/External/Modules/SpawnModule.cs b/NewHorizons/External/Modules/SpawnModule.cs index 9a613bc73..f8af3fa18 100644 --- a/NewHorizons/External/Modules/SpawnModule.cs +++ b/NewHorizons/External/Modules/SpawnModule.cs @@ -24,7 +24,17 @@ public class SpawnModule [Obsolete("startWithSuit is deprecated. Use playerSpawn.startWithSuit instead")] public bool startWithSuit; [JsonObject] - public class PlayerSpawnPoint : GeneralPropInfo { + public class SpawnPoint : GeneralPropInfo + { + /// + /// Offsets the player/ship by this local vector when spawning. Used to prevent spawning in the floor. Optional: defaults to (0, 4, 0). + /// + public MVector3 offset; + } + + [JsonObject] + public class PlayerSpawnPoint : SpawnPoint + { /// /// If you spawn on a planet with no oxygen, you probably want to set this to true ;;) /// @@ -33,10 +43,13 @@ public class PlayerSpawnPoint : GeneralPropInfo { /// Whether this planet's spawn point is the one the player will initially spawn at, if multiple spawn points exist. /// public bool isDefault; + + } [JsonObject] - public class ShipSpawnPoint : GeneralPropInfo { + public class ShipSpawnPoint : SpawnPoint + { } } diff --git a/NewHorizons/External/Modules/VariableSize/StarModule.cs b/NewHorizons/External/Modules/VariableSize/StarModule.cs index 3476b8674..3b833051f 100644 --- a/NewHorizons/External/Modules/VariableSize/StarModule.cs +++ b/NewHorizons/External/Modules/VariableSize/StarModule.cs @@ -104,6 +104,39 @@ public class StarModule : VariableSizeModule /// The type of stellar remnant your star will leave behind. /// [DefaultValue("default")] public StellarRemnantType stellarRemnantType = StellarRemnantType.Default; + + /// + /// Allows overriding solar flare graphical settings. + /// + public SolarFlareModule solarFlareSettings; + + [JsonObject] + public class SolarFlareModule + { + /// + /// Size multiuplier for solar flares. Defaults to 1. + /// + [DefaultValue(1)] + public float scaleFactor = 1f; + + /// + /// How long a solar flare is visible for. Defaults to 15. + /// + [DefaultValue(15f)] + public float lifeLength = 15f; + + /// + /// Solar flares are emitted randomly. This is the minimum ammount of time between solar flares. + /// + [DefaultValue(5f)] + public float minTimeBetweenFlares = 5f; + + /// + /// Solar flares are emitted randomly. This is the maximum ammount of time between solar flares. + /// + [DefaultValue(30f)] + public float maxTimeBetweenFlares = 30f; + } } [JsonConverter(typeof(StringEnumConverter))] diff --git a/NewHorizons/Handlers/CloakHandler.cs b/NewHorizons/Handlers/CloakHandler.cs index 79bd8c474..f662af4a9 100644 --- a/NewHorizons/Handlers/CloakHandler.cs +++ b/NewHorizons/Handlers/CloakHandler.cs @@ -1,5 +1,6 @@ using NewHorizons.Components.EOTE; using NewHorizons.OtherMods.VoiceActing; +using NewHorizons.Utility; using System.Collections.Generic; using System.Linq; using UnityEngine; @@ -81,7 +82,8 @@ private static void Refresh() if (!_cloaks.Any()) { - Locator.RegisterCloakFieldController(null); + // For some reason ship/scout HUD markers break if this isn't set to the Stranger when it is disabled #647 + Locator.RegisterCloakFieldController(GameObject.FindObjectOfType()); Shader.DisableKeyword("_CLOAKINGFIELDENABLED"); _cloakLocator.SetCurrentCloak(null); _cloakLocator.enabled = false; diff --git a/NewHorizons/Handlers/FadeHandler.cs b/NewHorizons/Handlers/FadeHandler.cs index 9316030a6..fbaa0b5b1 100644 --- a/NewHorizons/Handlers/FadeHandler.cs +++ b/NewHorizons/Handlers/FadeHandler.cs @@ -1,3 +1,4 @@ +using NewHorizons.Utility.OWML; using System; using System.Collections; using UnityEngine; @@ -6,7 +7,7 @@ namespace NewHorizons.Handlers { public static class FadeHandler { - public static void FadeOut(float length) => Main.Instance.StartCoroutine(FadeOutCoroutine(length)); + public static void FadeOut(float length) => Delay.StartCoroutine(FadeOutCoroutine(length)); private static IEnumerator FadeOutCoroutine(float length) { @@ -24,7 +25,7 @@ private static IEnumerator FadeOutCoroutine(float length) yield return new WaitForEndOfFrame(); } - public static void FadeThen(float length, Action action) => Main.Instance.StartCoroutine(FadeThenCoroutine(length, action)); + public static void FadeThen(float length, Action action) => Delay.StartCoroutine(FadeThenCoroutine(length, action)); private static IEnumerator FadeThenCoroutine(float length, Action action) { diff --git a/NewHorizons/Handlers/PlanetCreationHandler.cs b/NewHorizons/Handlers/PlanetCreationHandler.cs index 98d55efab..983c8e980 100644 --- a/NewHorizons/Handlers/PlanetCreationHandler.cs +++ b/NewHorizons/Handlers/PlanetCreationHandler.cs @@ -30,9 +30,14 @@ public static class PlanetCreationHandler // Custom bodies being created private static Dictionary _customBodyDict; + // Farthest distance from the center of the solar system + public static float SolarSystemRadius { get; private set; } + public static float DefaultFurthestOrbit => 30000f; + public static void Init(List bodies) { - Main.FurthestOrbit = 30000; + // Base game value + SolarSystemRadius = DefaultFurthestOrbit; _existingBodyDict = new(); _customBodyDict = new(); @@ -349,6 +354,13 @@ public static GameObject GenerateBrambleDimensionBody(NewHorizonsBody body) BrambleDimensionBuilder.Make(body, go, ao, sector, owRigidBody); go = SharedGenerateBody(body, go, sector, owRigidBody); + + // Not included in SharedGenerate to not mess up gravity on base game planets + if (body.Config.Base.surfaceGravity != 0) + { + GravityBuilder.Make(go, ao, owRigidBody, body.Config); + } + body.Object = go; AstroObjectLocator.RegisterCustomAstroObject(ao); @@ -640,7 +652,7 @@ private static GameObject SharedGenerateBody(NewHorizonsBody body, GameObject go if (body.Config.Atmosphere.fogSize != 0) { - fog = FogBuilder.Make(go, sector, body.Config.Atmosphere); + fog = FogBuilder.Make(go, sector, body.Config.Atmosphere, body.Mod); } atmosphere = AtmosphereBuilder.Make(go, sector, body.Config.Atmosphere, surfaceSize).GetComponentInChildren(); @@ -841,9 +853,11 @@ public static void SetPositionFromVector(GameObject go, Vector3 position) go.transform.position = position; } - if (go.transform.position.magnitude > Main.FurthestOrbit) + // Uses the ratio of the interlopers furthest point to what the base game considers the edge of the solar system + var distanceToCenter = go.transform.position.magnitude * (24000 / 30000f); + if (distanceToCenter > SolarSystemRadius) { - Main.FurthestOrbit = go.transform.position.magnitude + 30000f; + SolarSystemRadius = distanceToCenter; } } diff --git a/NewHorizons/Handlers/PlayerSpawnHandler.cs b/NewHorizons/Handlers/PlayerSpawnHandler.cs index 8d70907b7..cb1a607a6 100644 --- a/NewHorizons/Handlers/PlayerSpawnHandler.cs +++ b/NewHorizons/Handlers/PlayerSpawnHandler.cs @@ -40,7 +40,8 @@ public static void OnSystemReady(bool shouldWarpInFromShip, bool shouldWarpInFro var matchInitialMotion = SearchUtilities.Find("Player_Body").GetComponent(); if (matchInitialMotion != null) UnityEngine.Object.Destroy(matchInitialMotion); - Main.Instance.StartCoroutine(SpawnCoroutine(2)); + // Arbitrary number, depending on the machine some people die, some people fall through the floor, its very inconsistent + Delay.StartCoroutine(SpawnCoroutine(30)); } } diff --git a/NewHorizons/Handlers/StarChartHandler.cs b/NewHorizons/Handlers/StarChartHandler.cs index c1dd09280..437c62ddd 100644 --- a/NewHorizons/Handlers/StarChartHandler.cs +++ b/NewHorizons/Handlers/StarChartHandler.cs @@ -107,5 +107,7 @@ public static void RegisterFactForSystem(string factID, string system) _starSystemToFactID.Add(system, factID); _factIDToStarSystem.Add(factID, system); } + + public static bool IsWarpDriveLockedOn() => StarChartHandler.ShipLogStarChartMode.GetTargetStarSystem() != null; } } \ No newline at end of file diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index c24b4b039..e204ccdcb 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -57,7 +57,6 @@ public class Main : ModBehaviour public static float SecondsElapsedInLoop = -1; public static bool IsSystemReady { get; private set; } - public static float FurthestOrbit { get; set; } = 50000f; public string DefaultStarSystem => SystemDict.ContainsKey(_defaultSystemOverride) ? _defaultSystemOverride : _defaultStarSystem; public string CurrentStarSystem => _currentStarSystem; @@ -90,6 +89,8 @@ public class StarSystemEvent : UnityEvent { } public static bool HasDLC { get => EntitlementsManager.IsDlcOwned() == EntitlementsManager.AsyncOwnershipStatus.Owned; } + public static StarSystemConfig GetCurrentSystemConfig => SystemDict[Instance.CurrentStarSystem].Config; + public override object GetApi() { return new NewHorizonsApi(); @@ -230,6 +231,7 @@ public void Start() NHLogger.LogWarning("Couldn't find planets folder"); } + // Call this from the menu since we hadn't hooked onto the event yet Delay.FireOnNextUpdate(() => OnSceneLoaded(SceneManager.GetActiveScene(), LoadSceneMode.Single)); Delay.FireOnNextUpdate(() => _firstLoad = false); Instance.ModHelper.Menus.PauseMenu.OnInit += DebugReload.InitializePauseMenu; @@ -448,9 +450,6 @@ private void OnSceneLoaded(Scene scene, LoadSceneMode mode) DidWarpFromShip = shouldWarpInFromShip; DidWarpFromVessel = shouldWarpInFromVessel; - var map = FindObjectOfType(); - if (map != null) map._maxPanDistance = FurthestOrbit * 1.5f; - // Fix the map satellite SearchUtilities.Find("HearthianMapSatellite_Body", false).AddComponent(); @@ -523,7 +522,7 @@ private void OnSceneLoaded(Scene scene, LoadSceneMode mode) var ssrLight = solarSystemRoot.AddComponent(); ssrLight.innerSpotAngle = 0; ssrLight.spotAngle = 179; - ssrLight.range = FurthestOrbit * (4f / 3f); + ssrLight.range = PlanetCreationHandler.SolarSystemRadius * (4f / 3f); ssrLight.intensity = 0.001f; var fluid = playerBody.FindChild("PlayerDetector").GetComponent(); @@ -545,6 +544,9 @@ private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { NHLogger.LogError($"Exception thrown when invoking star system loaded event with parameter [{Instance.CurrentStarSystem}]:\n{e}"); } + + // Wait for player to be awake and also for frames to pass + Delay.RunWhenAndInNUpdates(() => OnSystemReady(DidWarpFromShip, DidWarpFromVessel), () => _playerAwake && PlayerSpawned, 30); } else { @@ -560,29 +562,33 @@ private void OnSceneLoaded(Scene scene, LoadSceneMode mode) _currentStarSystem = _defaultStarSystem; } } - - // Wait for player to be awake and also for frames to pass - Delay.RunWhenAndInNUpdates(() => OnSystemReady(DidWarpFromShip, DidWarpFromVessel), () => _playerAwake && PlayerSpawned, 30); } // Had a bunch of separate unity things firing stuff when the system is ready so I moved it all to here private void OnSystemReady(bool shouldWarpInFromShip, bool shouldWarpInFromVessel) { - IsSystemReady = true; + if (IsSystemReady) + { + NHLogger.LogWarning("OnSystemReady was called twice."); + } + else + { + IsSystemReady = true; - // ShipWarpController will handle the invulnerability otherwise - if (!shouldWarpInFromShip) - Delay.FireOnNextUpdate(() => InvulnerabilityHandler.MakeInvulnerable(false)); + // ShipWarpController will handle the invulnerability otherwise + if (!shouldWarpInFromShip) + Delay.FireOnNextUpdate(() => InvulnerabilityHandler.MakeInvulnerable(false)); - Locator.GetPlayerBody().gameObject.AddComponent(); - Locator.GetPlayerBody().gameObject.AddComponent(); - Locator.GetPlayerBody().gameObject.AddComponent(); + Locator.GetPlayerBody().gameObject.AddComponent(); + Locator.GetPlayerBody().gameObject.AddComponent(); + Locator.GetPlayerBody().gameObject.AddComponent(); - PlayerSpawnHandler.OnSystemReady(shouldWarpInFromShip, shouldWarpInFromVessel); + PlayerSpawnHandler.OnSystemReady(shouldWarpInFromShip, shouldWarpInFromVessel); - VesselCoordinatePromptHandler.RegisterPrompts(SystemDict.Where(system => system.Value.Config.Vessel?.coords != null).Select(x => x.Value).ToList()); + VesselCoordinatePromptHandler.RegisterPrompts(SystemDict.Where(system => system.Value.Config.Vessel?.coords != null).Select(x => x.Value).ToList()); - CloakHandler.OnSystemReady(); + CloakHandler.OnSystemReady(); + } } public void EnableWarpDrive() diff --git a/NewHorizons/NewHorizons.csproj b/NewHorizons/NewHorizons.csproj index d3623e534..8b620bace 100644 --- a/NewHorizons/NewHorizons.csproj +++ b/NewHorizons/NewHorizons.csproj @@ -15,7 +15,8 @@ none - + + diff --git a/NewHorizons/Patches/MapPatches/MapControllerPatches.cs b/NewHorizons/Patches/MapPatches/MapControllerPatches.cs index c17fba0f7..48b0ebac2 100644 --- a/NewHorizons/Patches/MapPatches/MapControllerPatches.cs +++ b/NewHorizons/Patches/MapPatches/MapControllerPatches.cs @@ -1,4 +1,5 @@ using HarmonyLib; +using NewHorizons.Handlers; using UnityEngine; using UnityEngine.SceneManagement; @@ -8,14 +9,20 @@ namespace NewHorizons.Patches.MapPatches public static class MapControllerPatches { [HarmonyPostfix] - [HarmonyPatch(nameof(MapController.Awake))] - public static void MapController_Awake(MapController __instance) + [HarmonyPatch(nameof(MapController.Start))] + public static void MapController_Start(MapController __instance) { - __instance._maxPanDistance = Mathf.Max(__instance._maxPanDistance, Main.FurthestOrbit * 1.5f); - __instance._maxZoomDistance *= 6f; - __instance._minPitchAngle = -90f; - __instance._zoomSpeed *= 4f; - __instance._mapCamera.farClipPlane = Mathf.Max(__instance._mapCamera.farClipPlane, Main.FurthestOrbit * 10f); + var modifier = Mathf.Max(1f, PlanetCreationHandler.SolarSystemRadius / PlanetCreationHandler.DefaultFurthestOrbit); + + __instance._maxPanDistance *= modifier; + __instance._maxZoomDistance *= modifier; + __instance._zoomSpeed *= modifier; + __instance._mapCamera.farClipPlane *= modifier * 4f; + + if (Main.SystemDict[Main.Instance.CurrentStarSystem].Config.freeMapAngle) + { + __instance._minPitchAngle = -90f; + } } [HarmonyPostfix] diff --git a/NewHorizons/Patches/PlayerPatches/PlayerStatePatches.cs b/NewHorizons/Patches/PlayerPatches/PlayerStatePatches.cs index adc39008d..d747033ba 100644 --- a/NewHorizons/Patches/PlayerPatches/PlayerStatePatches.cs +++ b/NewHorizons/Patches/PlayerPatches/PlayerStatePatches.cs @@ -1,4 +1,5 @@ using HarmonyLib; +using NewHorizons.Handlers; using UnityEngine; namespace NewHorizons.Patches.PlayerPatches { @@ -13,10 +14,10 @@ public static bool PlayerState_CheckShipOutsideSolarSystem(PlayerState __instanc // Stop the game from trying to recall your ship when you're visiting far away planets - Transform sunTransform = Locator.GetSunTransform(); - OWRigidbody shipBody = Locator.GetShipBody(); - var maxDist2 = Mathf.Max(900000000f, Main.FurthestOrbit * Main.FurthestOrbit * 2f); - __result = sunTransform != null && shipBody != null && (sunTransform.position - shipBody.transform.position).sqrMagnitude > maxDist2; + var centerTransform = Locator.GetCenterOfTheUniverse().GetStaticReferenceFrame().transform; + var shipBody = Locator.GetShipBody(); + var maxDist = Mathf.Max(PlanetCreationHandler.DefaultFurthestOrbit, PlanetCreationHandler.SolarSystemRadius); + __result = centerTransform != null && shipBody != null && (shipBody.transform.position - centerTransform.position).sqrMagnitude > maxDist * maxDist; return false; } } diff --git a/NewHorizons/Patches/WarpPatches/ShipCockpitControllerPatches.cs b/NewHorizons/Patches/WarpPatches/ShipCockpitControllerPatches.cs index b17b02486..198e01ddd 100644 --- a/NewHorizons/Patches/WarpPatches/ShipCockpitControllerPatches.cs +++ b/NewHorizons/Patches/WarpPatches/ShipCockpitControllerPatches.cs @@ -1,5 +1,9 @@ + using HarmonyLib; using NewHorizons.Handlers; +using System; +using System.Collections.Generic; +using System.Reflection.Emit; namespace NewHorizons.Patches.WarpPatches { @@ -24,5 +28,41 @@ public static bool ShipCockpitController_Update(ShipCockpitController __instance } return true; } + + [HarmonyTranspiler] + [HarmonyPatch(nameof(ShipCockpitController.FixedUpdate))] + public static IEnumerable ShipCockpitController_FixedUpdate(IEnumerable instructions, ILGenerator generator) + { + // Instead of targetting the Sun target the center of the universe + return new CodeMatcher(instructions, generator) + // Have to create a label that goes to the method return for the if statement logic + .MatchForward(false, + new CodeMatch(OpCodes.Ret) + ) + .CreateLabel(out Label returnLabel) + .Start() + .MatchForward(false, + new CodeMatch(OpCodes.Ldc_I4_2), + new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(Locator), nameof(Locator.GetAstroObject), new Type[] {typeof(AstroObject.Name)})), + new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(AstroObject), nameof(AstroObject.GetOWRigidbody))) + ) + .SetOpcodeAndAdvance(OpCodes.Nop) // Have to set to Nop since the Ldc_I4_2 operation is a label + .RemoveInstructions(2) + .Insert( + // First do an if statement to see if the warp drive is locked on + new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(ShipCockpitControllerPatches), nameof(ShipCockpitControllerPatches.ShouldReturn))), + new CodeInstruction(OpCodes.Brfalse_S, returnLabel), + + // Then get the center of the universe and its reference frame + new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(Locator), nameof(Locator.GetCenterOfTheUniverse))), + new CodeInstruction(OpCodes.Callvirt, AccessTools.Method(typeof(CenterOfTheUniverse), nameof(CenterOfTheUniverse.GetStaticReferenceFrame))) + ) + .InstructionEnumeration(); + } + + private static bool ShouldReturn() + { + return !StarChartHandler.IsWarpDriveLockedOn() && Main.GetCurrentSystemConfig.returnToSolarSystemWhenTooFar; + } } } diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index ec9059952..8974ffde3 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -345,7 +345,6 @@ "type": "number", "description": "How dense the fog is, if you put fog.", "format": "float", - "maximum": 1.0, "minimum": 0.0 }, "fogSize": { @@ -358,6 +357,10 @@ "description": "Colour of fog on the planet, if you put fog.", "$ref": "#/definitions/MColor" }, + "fogRampPath": { + "type": "string", + "description": "Relative filepath to the fog color ramp texture, if you put fog.\nx axis is angle to sun (left at midnight, right at noon), y axis is distance to camera (close at bottom, far at top)." + }, "hasOxygen": { "type": "boolean", "description": "Lets you survive on the planet without a suit." @@ -1034,6 +1037,13 @@ "description": "Rotation period in minutes.", "format": "float" }, + "initialRotation": { + "type": "number", + "description": "Offsets the planet's starting sidereal rotation. In degrees.", + "format": "float", + "maximum": 360.0, + "minimum": 0.0 + }, "isTidallyLocked": { "type": "boolean", "description": "Should the body always have one side facing its primary?" @@ -3041,6 +3051,10 @@ "type": "object", "additionalProperties": false, "properties": { + "offset": { + "description": "Offsets the player/ship by this local vector when spawning. Used to prevent spawning in the floor. Optional: defaults to (0, 4, 0).", + "$ref": "#/definitions/MVector3" + }, "rotation": { "description": "Rotation of the object", "$ref": "#/definitions/MVector3" @@ -3082,6 +3096,10 @@ "type": "object", "additionalProperties": false, "properties": { + "offset": { + "description": "Offsets the player/ship by this local vector when spawning. Used to prevent spawning in the floor. Optional: defaults to (0, 4, 0).", + "$ref": "#/definitions/MVector3" + }, "rotation": { "description": "Rotation of the object", "$ref": "#/definitions/MVector3" @@ -3206,6 +3224,10 @@ "description": "The type of stellar remnant your star will leave behind.", "default": "default", "$ref": "#/definitions/StellarRemnantType" + }, + "solarFlareSettings": { + "description": "Allows overriding solar flare graphical settings.", + "$ref": "#/definitions/SolarFlareModule" } } }, @@ -3245,6 +3267,36 @@ "custom" ] }, + "SolarFlareModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "scaleFactor": { + "type": "number", + "description": "Size multiuplier for solar flares. Defaults to 1.", + "format": "float", + "default": 1 + }, + "lifeLength": { + "type": "number", + "description": "How long a solar flare is visible for. Defaults to 15.", + "format": "float", + "default": 15.0 + }, + "minTimeBetweenFlares": { + "type": "number", + "description": "Solar flares are emitted randomly. This is the minimum ammount of time between solar flares.", + "format": "float", + "default": 5.0 + }, + "maxTimeBetweenFlares": { + "type": "number", + "description": "Solar flares are emitted randomly. This is the maximum ammount of time between solar flares.", + "format": "float", + "default": 30.0 + } + } + }, "WaterModule": { "type": "object", "additionalProperties": false, diff --git a/NewHorizons/Schemas/star_system_schema.json b/NewHorizons/Schemas/star_system_schema.json index f7d281b89..0cba8563e 100644 --- a/NewHorizons/Schemas/star_system_schema.json +++ b/NewHorizons/Schemas/star_system_schema.json @@ -5,6 +5,14 @@ "description": "Configuration for a specific star system", "additionalProperties": false, "properties": { + "freeMapAngle": { + "type": "boolean", + "description": "In this system should the player be able to rotate their map camera freely or be stuck above the plane of the solar system?" + }, + "returnToSolarSystemWhenTooFar": { + "type": "boolean", + "description": "When well past the furthest orbit, should the player be summoned back to the star?" + }, "farClipPlaneOverride": { "type": "number", "description": "An override value for the far clip plane. Allows you to see farther.", diff --git a/NewHorizons/Utility/OWML/Delay.cs b/NewHorizons/Utility/OWML/Delay.cs index 979712b75..e25dc9287 100644 --- a/NewHorizons/Utility/OWML/Delay.cs +++ b/NewHorizons/Utility/OWML/Delay.cs @@ -1,15 +1,49 @@ using System; using System.Collections; using UnityEngine; +using UnityEngine.SceneManagement; namespace NewHorizons.Utility.OWML { public static class Delay { - public static void RunWhen(Func predicate, Action action) => Main.Instance.ModHelper.Events.Unity.RunWhen(predicate, action); - public static void FireInNUpdates(Action action, int n) => Main.Instance.ModHelper.Events.Unity.FireInNUpdates(action, n); - public static void FireOnNextUpdate(Action action) => Main.Instance.ModHelper.Events.Unity.FireOnNextUpdate(action); - public static void RunWhenAndInNUpdates(Action action, Func predicate, int n) => Main.Instance.StartCoroutine(RunWhenOrInNUpdatesCoroutine(action, predicate, n)); + #region OnSceneUnloaded + static Delay() => SceneManager.sceneUnloaded += OnSceneUnloaded; + + private static void OnSceneUnloaded(Scene _) => Main.Instance.StopAllCoroutines(); + #endregion + + #region public methods + public static void StartCoroutine(IEnumerator coroutine) => Main.Instance.StartCoroutine(coroutine); + + public static void RunWhen(Func predicate, Action action) => StartCoroutine(RunWhenCoroutine(action, predicate)); + + public static void FireInNUpdates(Action action, int n) => StartCoroutine(FireInNUpdatesCoroutine(action, n)); + + public static void FireOnNextUpdate(Action action) => FireInNUpdates(action, 1); + + public static void RunWhenAndInNUpdates(Action action, Func predicate, int n) => Delay.StartCoroutine(RunWhenOrInNUpdatesCoroutine(action, predicate, n)); + #endregion + + #region Coroutines + private static IEnumerator RunWhenCoroutine(Action action, Func predicate) + { + while (!predicate.Invoke()) + { + yield return new WaitForEndOfFrame(); + } + + action.Invoke(); + } + + private static IEnumerator FireInNUpdatesCoroutine(Action action, int n) + { + for (int i = 0; i < n; i++) + { + yield return new WaitForEndOfFrame(); + } + action?.Invoke(); + } private static IEnumerator RunWhenOrInNUpdatesCoroutine(Action action, Func predicate, int n) { @@ -24,5 +58,6 @@ private static IEnumerator RunWhenOrInNUpdatesCoroutine(Action action, Func - +