diff --git a/NewHorizons/Assets/vessel.newhorizons b/NewHorizons/Assets/vessel.newhorizons index 2906f46b..dcc92757 100644 Binary files a/NewHorizons/Assets/vessel.newhorizons and b/NewHorizons/Assets/vessel.newhorizons differ diff --git a/NewHorizons/Builder/Atmosphere/SunOverrideBuilder.cs b/NewHorizons/Builder/Atmosphere/SunOverrideBuilder.cs index 33ebcca3..df696f4d 100644 --- a/NewHorizons/Builder/Atmosphere/SunOverrideBuilder.cs +++ b/NewHorizons/Builder/Atmosphere/SunOverrideBuilder.cs @@ -1,21 +1,43 @@ -using NewHorizons.External.Modules; +using NewHorizons.External.Modules; +using NewHorizons.External.Modules.VariableSize; using UnityEngine; namespace NewHorizons.Builder.Atmosphere { public static class SunOverrideBuilder { - public static void Make(GameObject planetGO, Sector sector, AtmosphereModule atmo, float surfaceSize) + public static void Make(GameObject planetGO, Sector sector, AtmosphereModule atmo, WaterModule water, float surfaceSize) { GameObject overrideGO = new GameObject("SunOverride"); overrideGO.SetActive(false); overrideGO.transform.parent = sector?.transform ?? planetGO.transform; - GiantsDeepSunOverrideVolume GDSOV = overrideGO.AddComponent(); - GDSOV._sector = sector; - GDSOV._cloudsOuterRadius = atmo.size; - GDSOV._cloudsInnerRadius = atmo.size * 0.9f; - GDSOV._waterOuterRadius = surfaceSize; - GDSOV._waterInnerRadius = 0f; + if (water != null) + { + var GDSOV = overrideGO.AddComponent(); + GDSOV._sector = sector; + GDSOV._cloudsOuterRadius = atmo.clouds.outerCloudRadius; + GDSOV._cloudsInnerRadius = atmo.clouds.innerCloudRadius; + GDSOV._waterOuterRadius = water.size; + GDSOV._waterInnerRadius = 0f; + } + else + { + var sunOverride = overrideGO.AddComponent(); + sunOverride._sector = sector; + + sunOverride._overrideColor = true; + sunOverride._color = Color.black; + + sunOverride._overrideIntensity = true; + sunOverride._intensity = 0f; + + sunOverride._overrideShadowStrength = true; + sunOverride._shadowStrength = 1f; + + sunOverride.shape = SimpleVolume.Shape.Sphere; + sunOverride.height = 2; + sunOverride.radius = atmo.clouds.innerCloudRadius; + } overrideGO.transform.position = planetGO.transform.position; overrideGO.SetActive(true); diff --git a/NewHorizons/Builder/Body/Geometry/CubeSphere.cs b/NewHorizons/Builder/Body/Geometry/CubeSphere.cs index 2e07b4dc..31ac6752 100644 --- a/NewHorizons/Builder/Body/Geometry/CubeSphere.cs +++ b/NewHorizons/Builder/Body/Geometry/CubeSphere.cs @@ -1,4 +1,4 @@ -using NewHorizons.Utility; +using NewHorizons.Utility; using UnityEngine; using Logger = NewHorizons.Utility.Logger; namespace NewHorizons.Builder.Body.Geometry @@ -7,13 +7,6 @@ namespace NewHorizons.Builder.Body.Geometry { public static Mesh Build(int resolution, Texture2D heightMap, float minHeight, float maxHeight, Vector3 stretch) { - // It breaks if resolution is greater than 100 I don't know why - if (resolution > 100) - { - Logger.LogWarning($"Can't make CubeSphere's with resolution higher than 100 for some reason"); - resolution = 100; - } - Mesh mesh = new Mesh(); mesh.name = "CubeSphere"; @@ -109,6 +102,12 @@ namespace NewHorizons.Builder.Body.Geometry } } + // Higher than this and we have to use a different indexFormat + if (vertices.Length > 65535) + { + mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; + } + mesh.vertices = vertices; mesh.normals = normals; mesh.uv = uvs; @@ -126,7 +125,8 @@ namespace NewHorizons.Builder.Body.Geometry v.y = v2.y * Mathf.Sqrt(1f - x2 / 2f - z2 / 2f + x2 * z2 / 3f); v.z = v2.z * Mathf.Sqrt(1f - x2 / 2f - y2 / 2f + x2 * y2 / 3f); - var sphericals = CoordinateUtilities.CartesianToSpherical(v); + // The shader uses real coords + var sphericals = CoordinateUtilities.CartesianToSpherical(v, false); float longitude = sphericals.x; float latitude = sphericals.y; diff --git a/NewHorizons/Builder/Body/Geometry/Icosphere.cs b/NewHorizons/Builder/Body/Geometry/Icosphere.cs index 67b634b7..71233c18 100644 --- a/NewHorizons/Builder/Body/Geometry/Icosphere.cs +++ b/NewHorizons/Builder/Body/Geometry/Icosphere.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using UnityEngine; using Random = UnityEngine.Random; @@ -59,9 +59,9 @@ namespace NewHorizons.Builder.Body.Geometry public static Mesh Build(int subdivisions, float minHeight, float maxHeight) { Mesh mesh = new Mesh(); + mesh.name = "Icosphere"; - if (vertices.Count <= subdivisions) - RefineFaces(subdivisions); + if (vertices.Count <= subdivisions) RefineFaces(subdivisions); var verticesToCopy = vertices[subdivisions]; @@ -89,6 +89,12 @@ namespace NewHorizons.Builder.Body.Geometry uvs[i] = new Vector2(x, y); } + // Higher than this and we have to use a different indexFormat + if (newVertices.Length > 65535) + { + mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; + } + mesh.vertices = newVertices; mesh.triangles = triangles[subdivisions]; mesh.normals = normals; diff --git a/NewHorizons/Builder/Body/HeightMapBuilder.cs b/NewHorizons/Builder/Body/HeightMapBuilder.cs index 31caa3c7..2c09240e 100644 --- a/NewHorizons/Builder/Body/HeightMapBuilder.cs +++ b/NewHorizons/Builder/Body/HeightMapBuilder.cs @@ -5,14 +5,13 @@ using OWML.Common; using System; using UnityEngine; using Logger = NewHorizons.Utility.Logger; -using Object = UnityEngine.Object; namespace NewHorizons.Builder.Body { public static class HeightMapBuilder { public static Shader PlanetShader; - public static void Make(GameObject planetGO, Sector sector, HeightMapModule module, IModBehaviour mod, int resolution = 51) + public static void Make(GameObject planetGO, Sector sector, HeightMapModule module, IModBehaviour mod, int resolution) { var deleteHeightmapFlag = false; @@ -26,8 +25,9 @@ namespace NewHorizons.Builder.Body else { // If we've loaded a new heightmap we'll delete the texture after + // Only delete it if it wasnt loaded before (something else is using it) + deleteHeightmapFlag = !ImageUtilities.IsTextureLoaded(mod, module.heightMap); heightMap = ImageUtilities.GetTexture(mod, module.heightMap); - deleteHeightmapFlag = true; } if (module.textureMap == null) @@ -74,6 +74,7 @@ namespace NewHorizons.Builder.Body if (superGroup != null) cubeSphere.AddComponent()._superGroup = superGroup; // Fix rotation in the end + // 90 degree rotation around x is because cube sphere uses Z as up, Unity uses Y cubeSphere.transform.rotation = planetGO.transform.TransformRotation(Quaternion.Euler(90, 0, 0)); cubeSphere.transform.position = planetGO.transform.position; diff --git a/NewHorizons/Builder/Body/RingBuilder.cs b/NewHorizons/Builder/Body/RingBuilder.cs index ff464bfc..55d84c04 100644 --- a/NewHorizons/Builder/Body/RingBuilder.cs +++ b/NewHorizons/Builder/Body/RingBuilder.cs @@ -1,4 +1,4 @@ -using NewHorizons.Components; +using NewHorizons.Components; using NewHorizons.Components.SizeControllers; using NewHorizons.Utility; using OWML.Common; @@ -45,7 +45,7 @@ namespace NewHorizons.Builder.Body var trigger = ringVolume.AddComponent(); trigger._shape = ringShape; - var sfv = ringVolume.AddComponent(); + var sfv = ringVolume.AddComponent(); var fluidType = FluidVolume.Type.NONE; try diff --git a/NewHorizons/Builder/Orbital/InitialMotionBuilder.cs b/NewHorizons/Builder/Orbital/InitialMotionBuilder.cs index bb3f95e9..d204e79f 100644 --- a/NewHorizons/Builder/Orbital/InitialMotionBuilder.cs +++ b/NewHorizons/Builder/Orbital/InitialMotionBuilder.cs @@ -1,5 +1,7 @@ -using NewHorizons.Components.Orbital; +using NewHorizons.Components.Orbital; using NewHorizons.External.Modules; +using NewHorizons.Utility; +using System.Linq; using UnityEngine; using Logger = NewHorizons.Utility.Logger; namespace NewHorizons.Builder.Orbital @@ -13,7 +15,7 @@ namespace NewHorizons.Builder.Orbital return SetInitialMotionFromConfig(initialMotion, primaryBody, secondaryBody, orbit); } - public static InitialMotion SetInitialMotionFromConfig(InitialMotion initialMotion, AstroObject primaryBody, AstroObject secondaryBody, OrbitModule orbit) + public static InitialMotion SetInitialMotionFromConfig(InitialMotion initialMotion, AstroObject primaryBody, AstroObject secondaryBody, OrbitModule orbit, bool isCustom = true) { // This bit makes the initial motion not try to calculate the orbit velocity itself for reasons initialMotion._orbitImpulseScalar = 0f; @@ -21,7 +23,12 @@ namespace NewHorizons.Builder.Orbital // Rotation initialMotion._initAngularSpeed = orbit.siderealPeriod == 0 ? 0f : 2f * Mathf.PI / (orbit.siderealPeriod * 60f); var rotationAxis = Quaternion.AngleAxis(orbit.axialTilt, Vector3.right) * Vector3.up; - secondaryBody.transform.rotation = Quaternion.FromToRotation(Vector3.up, rotationAxis); + + // For things with children this is broken + if (AstroObjectLocator.GetChildren(secondaryBody).Length == 0) + { + secondaryBody.transform.rotation = Quaternion.FromToRotation(Vector3.up, rotationAxis); + } if (!orbit.isStatic && primaryBody != null) { @@ -36,7 +43,7 @@ namespace NewHorizons.Builder.Orbital return initialMotion; } - public static void SetInitialMotion(InitialMotion initialMotion, AstroObject primaryBody, AstroObject secondaryBody) + private static void SetInitialMotion(InitialMotion initialMotion, AstroObject primaryBody, AstroObject secondaryBody) { var focalPoint = primaryBody.GetComponent(); if (focalPoint) diff --git a/NewHorizons/Builder/Props/DetailBuilder.cs b/NewHorizons/Builder/Props/DetailBuilder.cs index f66a4c06..99952903 100644 --- a/NewHorizons/Builder/Props/DetailBuilder.cs +++ b/NewHorizons/Builder/Props/DetailBuilder.cs @@ -99,7 +99,18 @@ namespace NewHorizons.Builder.Props { if (component is Sector s) { - s._parentSector = sector; + s.SetParentSector(sector); + } + + if (component is SectorCullGroup sectorCullGroup) + { + sectorCullGroup._controllingProxy = null; + } + + // fix Sector stuff, eg SectorCullGroup (without this, props that have a SectorCullGroup component will become invisible inappropriately) + if (component is ISectorGroup sectorGroup) + { + sectorGroup.SetSector(sector); } // TODO: Make this work or smthng @@ -119,7 +130,10 @@ namespace NewHorizons.Builder.Props else { var sectorField = component?.GetType()?.GetField("_sector"); - if (sectorField != null && sectorField.FieldType == typeof(Sector)) Main.Instance.ModHelper.Events.Unity.FireOnNextUpdate(() => sectorField.SetValue(component, sector)); + if (sectorField != null && sectorField.FieldType == typeof(Sector)) + { + Main.Instance.ModHelper.Events.Unity.FireOnNextUpdate(() => sectorField.SetValue(component, sector)); + } } if (component is AnglerfishController angler) @@ -169,8 +183,7 @@ namespace NewHorizons.Builder.Props } else { - // Remove things that require sectors. Will just keep extending this as things pop up - + // Remove things that require sectors if the sector is null. Will just keep extending this as things pop up. if (component is FogLight or SectoredMonoBehaviour) { GameObject.DestroyImmediate(component); @@ -188,6 +201,12 @@ namespace NewHorizons.Builder.Props else if (component is Collider collider) collider.enabled = true; else if (component is Renderer renderer) renderer.enabled = true; else if (component is Shape shape) shape.enabled = true; + else if (component is SectorCullGroup sectorCullGroup) + { + sectorCullGroup._inMapView = false; + sectorCullGroup._isFastForwarding = false; + sectorCullGroup.SetVisible(sectorCullGroup.ShouldBeVisible(), true, false); + } // If it's not a moving anglerfish make sure the anim controller is regular else if (component is AnglerfishAnimController angler && angler.GetComponentInParent() == null) { diff --git a/NewHorizons/Components/RingFluidVolume.cs b/NewHorizons/Components/RingFluidVolume.cs new file mode 100644 index 00000000..76619ef7 --- /dev/null +++ b/NewHorizons/Components/RingFluidVolume.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace NewHorizons.Components +{ + public class RingFluidVolume : SimpleFluidVolume + { + public override void OnEffectVolumeEnter(GameObject hitObj) + { + FluidDetector fluidDetector = hitObj.GetComponent(); + if (fluidDetector == null) return; + + ForceDetector forceDetector = hitObj.GetComponent(); + if (forceDetector != null && forceDetector._activeVolumes != null && forceDetector._activeVolumes.Count > 0 && forceDetector._activeVolumes.Where(activeVolume => activeVolume is ForceVolume).Select(activeVolume => activeVolume as ForceVolume).Any(activeVolume => activeVolume.GetAffectsAlignment(forceDetector._attachedBody))) return; + + fluidDetector.AddVolume(this); + } + } +} diff --git a/NewHorizons/Components/ShipLogStarChartMode.cs b/NewHorizons/Components/ShipLogStarChartMode.cs index 2d568bd6..314985b3 100644 --- a/NewHorizons/Components/ShipLogStarChartMode.cs +++ b/NewHorizons/Components/ShipLogStarChartMode.cs @@ -241,7 +241,7 @@ namespace NewHorizons.Components } } - public string UniqueIDToName(string uniqueID) + public static string UniqueIDToName(string uniqueID) { var name = TranslationHandler.GetTranslation(uniqueID, TranslationHandler.TextType.UI); diff --git a/NewHorizons/Components/VesselOrbLocker.cs b/NewHorizons/Components/VesselOrbLocker.cs new file mode 100644 index 00000000..34db1367 --- /dev/null +++ b/NewHorizons/Components/VesselOrbLocker.cs @@ -0,0 +1,52 @@ +using UnityEngine; + +namespace NewHorizons.Components +{ + public class VesselOrbLocker : MonoBehaviour + { + public GameObject _coordinateInterfaceOrbObject; + private NomaiInterfaceOrb _coordinateInterfaceOrb; + + public GameObject _coordinateInterfaceUpperOrbObject; + private NomaiInterfaceOrb _coordinateInterfaceUpperOrb; + + public GameObject _powerOrbObject; + private NomaiInterfaceOrb _powerOrb; + + public void InitializeOrbs() + { + _coordinateInterfaceOrb = _coordinateInterfaceOrbObject.GetComponent(); + _coordinateInterfaceUpperOrb = _coordinateInterfaceUpperOrbObject.GetComponent(); + _powerOrb = _powerOrbObject.GetComponent(); + } + + public void AddLocks() + { + _coordinateInterfaceOrb.AddLock(); + _coordinateInterfaceUpperOrb.AddLock(); + _powerOrb.AddLock(); + } + + public void AddLockToCoordinateOrb() + { + _coordinateInterfaceOrb.AddLock(); + } + + public void AddLockToWarpOrb() + { + _coordinateInterfaceUpperOrb.AddLock(); + } + + public void AddLockToPowerOrb() + { + _powerOrb.AddLock(); + } + + public void RemoveLocks() + { + _coordinateInterfaceOrb.RemoveAllLocks(); + _coordinateInterfaceUpperOrb.RemoveAllLocks(); + _powerOrb.RemoveAllLocks(); + } + } +} diff --git a/NewHorizons/Components/VesselSingularityRoot.cs b/NewHorizons/Components/VesselSingularityRoot.cs new file mode 100644 index 00000000..2cca4481 --- /dev/null +++ b/NewHorizons/Components/VesselSingularityRoot.cs @@ -0,0 +1,8 @@ +using UnityEngine; + +namespace NewHorizons.Components +{ + public class VesselSingularityRoot : MonoBehaviour + { + } +} diff --git a/NewHorizons/External/Configs/StarSystemConfig.cs b/NewHorizons/External/Configs/StarSystemConfig.cs index bf4cef06..bfed6035 100644 --- a/NewHorizons/External/Configs/StarSystemConfig.cs +++ b/NewHorizons/External/Configs/StarSystemConfig.cs @@ -1,5 +1,5 @@ -using System.Collections.Generic; using System.ComponentModel; +using System.ComponentModel.DataAnnotations; using System.Linq; using NewHorizons.Utility; using Newtonsoft.Json; @@ -102,8 +102,16 @@ namespace NewHorizons.External.Configs public class NomaiCoordinates { + [MinLength(2)] + [MaxLength(6)] public int[] x; + + [MinLength(2)] + [MaxLength(6)] public int[] y; + + [MinLength(2)] + [MaxLength(6)] public int[] z; } diff --git a/NewHorizons/Handlers/PlanetCreationHandler.cs b/NewHorizons/Handlers/PlanetCreationHandler.cs index 4b9c8ff4..2c7246f3 100644 --- a/NewHorizons/Handlers/PlanetCreationHandler.cs +++ b/NewHorizons/Handlers/PlanetCreationHandler.cs @@ -387,7 +387,7 @@ namespace NewHorizons.Handlers if (body.Config.HeightMap != null) { - HeightMapBuilder.Make(go, sector, body.Config.HeightMap, body.Mod); + HeightMapBuilder.Make(go, sector, body.Config.HeightMap, body.Mod, 51); } if (body.Config.ProcGen != null) @@ -447,7 +447,7 @@ namespace NewHorizons.Handlers if (!string.IsNullOrEmpty(body.Config.Atmosphere?.clouds?.texturePath)) { CloudsBuilder.Make(go, sector, body.Config.Atmosphere, body.Mod); - SunOverrideBuilder.Make(go, sector, body.Config.Atmosphere, surfaceSize); + SunOverrideBuilder.Make(go, sector, body.Config.Atmosphere, body.Config.Water, surfaceSize); } if (body.Config.Atmosphere.hasRain || body.Config.Atmosphere.hasSnow) @@ -534,7 +534,10 @@ namespace NewHorizons.Handlers // Since we destroyed the AO we have to replace links to it in other places newAO.gameObject.GetComponentInChildren()._referenceFrame._attachedAstroObject = newAO; - GameObject.Destroy(go.GetComponentInChildren().gameObject); + // QM and stuff don't have orbit lines + var orbitLine = go.GetComponentInChildren()?.gameObject; + if (orbitLine != null) GameObject.Destroy(orbitLine); + var isMoon = newAO.GetAstroObjectType() == AstroObject.Type.Moon || newAO.GetAstroObjectType() == AstroObject.Type.Satellite; if (body.Config.Orbit.showOrbitLine) OrbitlineBuilder.Make(go, newAO, isMoon, body.Config); @@ -563,7 +566,7 @@ namespace NewHorizons.Handlers { if (childAO is NHAstroObject && ExistingAOConfigs.ContainsKey(childAO)) { - // If it's already and NH object we repeat the whole process else it doesn't work idk + // If it's already an NH object we repeat the whole process else it doesn't work idk NextPassBodies.Add(ExistingAOConfigs[childAO]); } else @@ -583,7 +586,7 @@ namespace NewHorizons.Handlers } // Have to do this after setting position - InitialMotionBuilder.SetInitialMotion(im, primary, newAO); + InitialMotionBuilder.SetInitialMotionFromConfig(im, primary, newAO, body.Config.Orbit); // Have to register this new AO to the locator Locator.RegisterAstroObject(newAO); diff --git a/NewHorizons/Handlers/PlanetGraphHandler.cs b/NewHorizons/Handlers/PlanetGraphHandler.cs index 0da5aabc..da99907d 100644 --- a/NewHorizons/Handlers/PlanetGraphHandler.cs +++ b/NewHorizons/Handlers/PlanetGraphHandler.cs @@ -1,4 +1,4 @@ -using NewHorizons.External.Configs; +using NewHorizons.External.Configs; using NewHorizons.Utility; using System; using System.Collections; @@ -42,7 +42,7 @@ namespace NewHorizons.Handlers } else { - Logger.LogError("There must be one and only one centerOfSolarSystem!"); + Logger.LogError($"There must be one and only one centerOfSolarSystem! Found [{centers.Length}]"); } } } diff --git a/NewHorizons/Handlers/TitleSceneHandler.cs b/NewHorizons/Handlers/TitleSceneHandler.cs index ebbb4043..242590a4 100644 --- a/NewHorizons/Handlers/TitleSceneHandler.cs +++ b/NewHorizons/Handlers/TitleSceneHandler.cs @@ -87,6 +87,7 @@ namespace NewHorizons.Handlers heightMap.heightMap = body.Config.HeightMap.heightMap; heightMap.maxHeight = size; heightMap.minHeight = body.Config.HeightMap.minHeight * size / body.Config.HeightMap.maxHeight; + heightMap.stretch = body.Config.HeightMap.stretch; } if (body.Config.Atmosphere?.clouds?.texturePath != null) { @@ -96,7 +97,7 @@ namespace NewHorizons.Handlers heightMap.textureMap = body.Config.Atmosphere.clouds.texturePath; } - HeightMapBuilder.Make(titleScreenGO, null, heightMap, body.Mod); + HeightMapBuilder.Make(titleScreenGO, null, heightMap, body.Mod, 30); GameObject pivot = GameObject.Instantiate(SearchUtilities.Find("Scene/Background/PlanetPivot"), SearchUtilities.Find("Scene/Background").transform); pivot.GetComponent()._degreesPerSecond = 10f; diff --git a/NewHorizons/Handlers/VesselCoordinatePromptHandler.cs b/NewHorizons/Handlers/VesselCoordinatePromptHandler.cs new file mode 100644 index 00000000..e03ebdd9 --- /dev/null +++ b/NewHorizons/Handlers/VesselCoordinatePromptHandler.cs @@ -0,0 +1,201 @@ +using NewHorizons.Components; +using NewHorizons.Utility; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; +using UnityEngine; +using static NewHorizons.External.Configs.StarSystemConfig; + +namespace NewHorizons.Handlers +{ + public class VesselCoordinatePromptHandler + { + private static List> _factSystemIDPrompt; + private static List _textureCache; + + public static void RegisterPrompts(List systems) + { + // Have to destroy the images we've created if this isn't the first time it has run + if (_textureCache != null) + { + foreach (var texture in _textureCache) + { + UnityEngine.Object.Destroy(texture); + } + } + + _textureCache = new List(); + _factSystemIDPrompt = new List>(); + + foreach (var system in systems) + { + var systemName = system.UniqueID; + var fact = system.Config.factRequiredForWarp; + var nomaiCoords = system.Config.coords; + + if (system.UniqueID == "EyeOfTheUniverse" || nomaiCoords == null) continue; + + RegisterPrompt(systemName, fact, nomaiCoords); + } + } + + private static void RegisterPrompt(string systemID, string fact, NomaiCoordinates coords) + { + var texture = MakeTexture(coords.x, coords.y, coords.z); + + _textureCache.Add(texture); + + var sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(texture.width / 2f, texture.height / 2f)); + + var name = ShipLogStarChartMode.UniqueIDToName(systemID); + + var prompt = new ScreenPrompt($"{name}: ", sprite, 0); + + _factSystemIDPrompt.Add(new (fact, systemID, prompt)); + + var manager = Locator.GetPromptManager(); + manager.AddScreenPrompt(prompt, manager.GetScreenPromptList(PromptPosition.LowerLeft), manager.GetTextAnchor(PromptPosition.LowerLeft), -1, false); + } + + // Gets called from the patches + public static void SetPromptVisibility(bool visible) + { + foreach (var pair in _factSystemIDPrompt) + { + var fact = pair.Item1; + var systemID = pair.Item2; + var prompt = pair.Item3; + + if (visible) + { + if (Main.Instance.CurrentStarSystem != systemID && (string.IsNullOrEmpty(fact) || Locator.GetShipLogManager().IsFactRevealed(fact))) + { + prompt.SetVisibility(true); + } + else + { + prompt.SetVisibility(false); + } + } + else + { + prompt.SetVisibility(false); + } + } + } + + private static Texture2D MakeTexture(params int[][] coords) + { + if (coords == null || coords.Length == 0) + { + return null; + } + int width = 96; + int height = 96; + Texture2D texture = new Texture2D(width * coords.Length, height, TextureFormat.RGBA32, false, false); + texture.SetPixels(Enumerable.Repeat(Color.clear, texture.width * texture.height).ToArray()); + float offset = 0f; + for (int i = 0; i < coords.Length; i++) + { + if (coords[i] == null || coords[i].Length < 2) + { + continue; + } + // Remove extra space if coordinate doesn't use left slot + if (!coords[i].Contains(5)) + { + offset -= width * 0.175f; + } + Rect rect = new Rect(offset, 0f, width, height); + DrawCoordinateLines(texture, rect, coords[i]); + // Remove extra space if coordinate doesn't use right slot + if (!coords[i].Contains(2)) + { + offset -= width * 0.175f; + } + offset += width; + } + texture.Apply(); + return texture; + } + + private static void DrawCoordinateLines(Texture2D texture, Rect rect, int[] coords) + { + if (coords == null || coords.Length < 2) + { + return; + } + float lineWidth = 3f; + for (int i = 0; i < coords.Length - 1; i++) + { + // Calculate start and end points + Vector2 size = rect.size; + Vector2 center = size * 0.5f; + float radius = Mathf.Min(size.x, size.y) * 0.475f - lineWidth; + + float angle0 = Mathf.Deg2Rad * (120f - (60f * coords[i + 0])); + Vector2 pos0 = rect.position + center + new Vector2(Mathf.Cos(angle0), Mathf.Sin(angle0)) * radius; + Vector2 start = new Vector2(Mathf.Round(pos0.x), Mathf.Round(pos0.y)); + + float angle1 = Mathf.Deg2Rad * (120f - (60f * coords[i + 1])); + Vector2 pos1 = rect.position + center + new Vector2(Mathf.Cos(angle1), Mathf.Sin(angle1)) * radius; + Vector2 end = new Vector2(Mathf.Round(pos1.x), Mathf.Round(pos1.y)); + + // Draw lines + int x0 = Mathf.FloorToInt(Mathf.Min(start.x, end.x) - lineWidth * 2f); + int y0 = Mathf.FloorToInt(Mathf.Min(start.y, end.y) - lineWidth * 2f); + int x1 = Mathf.CeilToInt(Mathf.Max(start.x, end.x) + lineWidth * 2f); + int y1 = Mathf.CeilToInt(Mathf.Max(start.y, end.y) + lineWidth * 2f); + + Vector2 dir = end - start; + float length = dir.magnitude; + dir.Normalize(); + + for (int x = x0; x <= x1; x++) + { + for (int y = y0; y <= y1; y++) + { + Vector2 p = new Vector2(x, y); + float dot = Vector2.Dot(p - start, dir); + dot = Mathf.Clamp(dot, 0f, length); + Vector2 pointOnLine = start + dir * dot; + float distToLine = Mathf.Max(0f, Vector2.Distance(p, pointOnLine) - lineWidth); + if (distToLine <= 1f) + { + // Line is within 1 pixel, fill with color (with anti-aliased blending) + Color color = Color.white; + float blend = 1f - Mathf.Clamp01(distToLine); + + if (color.a * blend < 1f) + { + Color existing = texture.GetPixel(x, y); + if (existing.a > 0f) + { + float colorA = color.a; + color.a = 1f; + texture.SetPixel(x, y, Color.Lerp(existing, color, Mathf.Clamp01(colorA * blend))); + } else + { + color.a *= blend; + texture.SetPixel(x, y, color); + } + } else + { + color.a *= blend; + texture.SetPixel(x, y, color); + } + } + } + } + } + } + + public static bool KnowsEyeCoordinates() + { + // Works normally in the main system, else check save data directly + if (Main.Instance.CurrentStarSystem == "SolarSystem") return Locator.GetShipLogManager().IsFactRevealed("OPC_EYE_COORDINATES_X1"); + else return PlayerData._currentGameSave.shipLogFactSaves.ContainsKey("OPC_EYE_COORDINATES_X1"); + } + } +} diff --git a/NewHorizons/Handlers/VesselWarpHandler.cs b/NewHorizons/Handlers/VesselWarpHandler.cs index 619bf1a0..f124096a 100644 --- a/NewHorizons/Handlers/VesselWarpHandler.cs +++ b/NewHorizons/Handlers/VesselWarpHandler.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using UnityEngine; +using NewHorizons.Components; using NewHorizons.Utility; using Logger = NewHorizons.Utility.Logger; using static NewHorizons.Main; @@ -83,44 +84,53 @@ namespace NewHorizons.Handlers vesselObject.name = VesselPrefab.name; vesselObject.transform.parent = null; + VesselOrbLocker vesselOrbLocker = vesselObject.GetComponent(); + vesselOrbLocker.InitializeOrbs(); + vesselOrbLocker.AddLocks(); + if (system.Config.vesselPosition != null) vesselObject.transform.position += system.Config.vesselPosition; if (system.Config.vesselRotation != null) vesselObject.transform.eulerAngles = system.Config.vesselRotation; + vesselOrbLocker.RemoveLocks(); + vesselOrbLocker.AddLockToWarpOrb(); + + VesselSingularityRoot singularityRoot = vesselObject.GetComponentInChildren(true); + VesselWarpController vesselWarpController = vesselObject.GetComponentInChildren(true); WarpController = vesselWarpController; - vesselWarpController._sourceWarpPlatform.transform.DestroyAllChildren(); - vesselWarpController._targetWarpPlatform.transform.DestroyAllChildren(); - GameObject.Destroy(vesselWarpController._blackHole.transform.parent.gameObject); - GameObject.Destroy(vesselWarpController._whiteHole.transform.parent.gameObject); - GameObject WarpPlatform = SearchUtilities.Find("DB_VesselDimension_Body/Sector_VesselDimension/Sector_VesselBridge/Interactibles_VesselBridge/WarpController/Prefab_NOM_WarpPlatform"); GameObject warpBH = WarpPlatform.transform.Find("BlackHole").gameObject; GameObject warpWH = WarpPlatform.transform.Find("WhiteHole").gameObject; GameObject sourceBH = GameObject.Instantiate(warpBH, vesselWarpController._sourceWarpPlatform.transform, false); + sourceBH.name = "BlackHole"; vesselWarpController._sourceWarpPlatform._blackHole = sourceBH.GetComponentInChildren(); + GameObject sourceWH = GameObject.Instantiate(warpWH, vesselWarpController._sourceWarpPlatform.transform, false); + sourceWH.name = "WhiteHole"; vesselWarpController._sourceWarpPlatform._whiteHole = sourceWH.GetComponentInChildren(); GameObject targetBH = GameObject.Instantiate(warpBH, vesselWarpController._targetWarpPlatform.transform, false); + targetBH.name = "BlackHole"; vesselWarpController._targetWarpPlatform._blackHole = targetBH.GetComponentInChildren(); GameObject targetWH = GameObject.Instantiate(warpWH, vesselWarpController._targetWarpPlatform.transform, false); + targetWH.name = "WhiteHole"; vesselWarpController._targetWarpPlatform._whiteHole = targetWH.GetComponentInChildren(); GameObject blackHole = SearchUtilities.Find("DB_VesselDimension_Body/Sector_VesselDimension/Sector_VesselBridge/Interactibles_VesselBridge/BlackHole"); - GameObject newBlackHole = GameObject.Instantiate(blackHole, vesselWarpController.transform.parent, false); + GameObject newBlackHole = GameObject.Instantiate(blackHole, Vector3.zero, Quaternion.identity, singularityRoot.transform); newBlackHole.name = "BlackHole"; vesselWarpController._blackHole = newBlackHole.GetComponentInChildren(); vesselWarpController._blackHoleOneShot = vesselWarpController._blackHole.transform.parent.Find("BlackHoleAudio_OneShot").GetComponent(); GameObject whiteHole = SearchUtilities.Find("DB_VesselDimension_Body/Sector_VesselDimension/Sector_VesselBridge/Interactibles_VesselBridge/WhiteHole"); - GameObject newWhiteHole = GameObject.Instantiate(whiteHole, vesselWarpController.transform.parent, false); + GameObject newWhiteHole = GameObject.Instantiate(whiteHole, Vector3.zero, Quaternion.identity, singularityRoot.transform); newWhiteHole.name = "WhiteHole"; vesselWarpController._whiteHole = newWhiteHole.GetComponentInChildren(); vesselWarpController._whiteHoleOneShot = vesselWarpController._whiteHole.transform.parent.Find("WhiteHoleAudio_OneShot").GetComponent(); @@ -133,7 +143,7 @@ namespace NewHorizons.Handlers vesselWarpController._targetWarpPlatform.transform.localPosition = system.Config.warpExitPosition; if (system.Config.warpExitRotation != null) - vesselObject.transform.localEulerAngles = system.Config.warpExitRotation; + vesselWarpController._targetWarpPlatform.transform.localEulerAngles = system.Config.warpExitRotation; vesselObject.GetComponent()._labelID = (UITextType)TranslationHandler.AddUI("VESSEL"); @@ -227,8 +237,6 @@ namespace NewHorizons.Handlers vesselWarpController._cageAnimator.OnTranslationComplete -= new TransformAnimator.AnimationEvent(vesselWarpController.OnCageAnimationComplete); vesselWarpController._cageAnimator.OnTranslationComplete += new TransformAnimator.AnimationEvent(vesselWarpController.OnCageAnimationComplete); } - if (vesselWarpController._cageLoopingAudio != null) - vesselWarpController._cageLoopingAudio.FadeIn(1f); } } } diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index 506630b5..3b10c960 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -198,15 +198,6 @@ namespace NewHorizons private static void OnWakeUp() { IsSystemReady = true; - try - { - Logger.Log($"Star system loaded [{Instance.CurrentStarSystem}]"); - Instance.OnStarSystemLoaded?.Invoke(Instance.CurrentStarSystem); - } - catch (Exception e) - { - Logger.LogError($"Exception thrown when invoking star system loaded event with parameter [{Instance.CurrentStarSystem}] : {e.GetType().FullName} {e.Message} {e.StackTrace}"); - } } private void OnSceneUnloaded(Scene scene) @@ -302,6 +293,16 @@ namespace NewHorizons // Fix the map satellite SearchUtilities.Find("HearthianMapSatellite_Body", false).AddComponent(); + + try + { + Logger.Log($"Star system loaded [{Instance.CurrentStarSystem}]"); + Instance.OnStarSystemLoaded?.Invoke(Instance.CurrentStarSystem); + } + catch (Exception e) + { + Logger.LogError($"Exception thrown when invoking star system loaded event with parameter [{Instance.CurrentStarSystem}] : {e.GetType().FullName} {e.Message} {e.StackTrace}"); + } } else { @@ -331,6 +332,8 @@ namespace NewHorizons if (shouldWarpInFromShip) _shipWarpController.WarpIn(WearingSuit); else if (shouldWarpInFromVessel) VesselWarpHandler.TeleportToVessel(); else FindObjectOfType().DebugWarp(SystemDict[_currentStarSystem].SpawnPoint); + + VesselCoordinatePromptHandler.RegisterPrompts(SystemDict.Where(system => system.Value.Config.coords != null).Select(x => x.Value).ToList()); } public void EnableWarpDrive() @@ -368,7 +371,11 @@ namespace NewHorizons if (starSystemConfig.startHere) { // We always want to allow mods to overwrite setting the main SolarSystem as default but not the other way around - if (name != "SolarSystem") SetDefaultSystem(name); + if (name != "SolarSystem") + { + SetDefaultSystem(name); + _currentStarSystem = name; + } } if (SystemDict.ContainsKey(name)) @@ -446,15 +453,15 @@ namespace NewHorizons if (!foundFile) Logger.LogWarning($"{mod.ModHelper.Manifest.Name} has a folder for translations but none were loaded"); } - public NewHorizonsBody LoadConfig(IModBehaviour mod, string relativeDirectory) + public NewHorizonsBody LoadConfig(IModBehaviour mod, string relativePath) { NewHorizonsBody body = null; try { - var config = mod.ModHelper.Storage.Load(relativeDirectory); + var config = mod.ModHelper.Storage.Load(relativePath); if (config == null) { - Logger.LogError($"Couldn't load {relativeDirectory}. Is your Json formatted correctly?"); + Logger.LogError($"Couldn't load {relativePath}. Is your Json formatted correctly?"); return null; } @@ -479,11 +486,11 @@ namespace NewHorizons // Has to happen after we make sure theres a system config config.MigrateAndValidate(); - body = new NewHorizonsBody(config, mod, relativeDirectory); + body = new NewHorizonsBody(config, mod, relativePath); } catch (Exception e) { - Logger.LogError($"Error encounter when loading {relativeDirectory}: {e.Message} {e.StackTrace}"); + Logger.LogError($"Error encounter when loading {relativePath}: {e.Message} {e.StackTrace}"); } return body; @@ -492,7 +499,6 @@ namespace NewHorizons public void SetDefaultSystem(string defaultSystem) { _defaultStarSystem = defaultSystem; - _currentStarSystem = defaultSystem; } #endregion Load diff --git a/NewHorizons/NewHorizonsApi.cs b/NewHorizons/NewHorizonsApi.cs index 260d32e6..135579ac 100644 --- a/NewHorizons/NewHorizonsApi.cs +++ b/NewHorizons/NewHorizonsApi.cs @@ -1,4 +1,4 @@ -using NewHorizons.Builder.Props; +using NewHorizons.Builder.Props; using NewHorizons.Utility; using OWML.Common; using OWML.Utils; @@ -77,6 +77,14 @@ namespace NewHorizons return Main.Instance.OnStarSystemLoaded; } + public bool SetDefaultSystem(string name) + { + if (!Main.SystemDict.ContainsKey(name)) return false; + + Main.Instance.SetDefaultSystem(name); + return true; + } + public bool ChangeCurrentStarSystem(string name) { if (!Main.SystemDict.ContainsKey(name)) return false; diff --git a/NewHorizons/Patches/EyeCoordinatePromptTriggerPatches.cs b/NewHorizons/Patches/EyeCoordinatePromptTriggerPatches.cs new file mode 100644 index 00000000..ed827a6f --- /dev/null +++ b/NewHorizons/Patches/EyeCoordinatePromptTriggerPatches.cs @@ -0,0 +1,34 @@ +using HarmonyLib; +using NewHorizons.Handlers; +using UnityEngine; + +namespace NewHorizons.Patches +{ + [HarmonyPatch] + public static class EyeCoordinatePromptTriggerPatches + { + [HarmonyPrefix] + [HarmonyPatch(typeof(EyeCoordinatePromptTrigger), nameof(EyeCoordinatePromptTrigger.Update))] + public static bool EyeCoordinatePromptTrigger_Update(EyeCoordinatePromptTrigger __instance) + { + var showPrompts = __instance._warpController.HasPower(); + + // In other systems checking if the proper fact is revealed doesn't work, so we just overwrite this function + __instance._promptController.SetEyeCoordinatesVisibility(showPrompts && VesselCoordinatePromptHandler.KnowsEyeCoordinates()); + + VesselCoordinatePromptHandler.SetPromptVisibility(showPrompts); + + return false; + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(EyeCoordinatePromptTrigger), nameof(EyeCoordinatePromptTrigger.OnExit))] + public static void EyeCoordinatePromptTrigger_OnExit(GameObject __0) + { + if (__0.CompareTag("PlayerDetector")) + { + VesselCoordinatePromptHandler.SetPromptVisibility(false); + } + } + } +} diff --git a/NewHorizons/Schemas/star_system_schema.json b/NewHorizons/Schemas/star_system_schema.json index c3c3fe30..27ea2b68 100644 --- a/NewHorizons/Schemas/star_system_schema.json +++ b/NewHorizons/Schemas/star_system_schema.json @@ -115,6 +115,9 @@ "properties": { "x": { "type": "array", + "maxItems": 6, + "minItems": 2, + "uniqueItems": true, "items": { "type": "integer", "format": "int32" @@ -122,6 +125,9 @@ }, "y": { "type": "array", + "maxItems": 6, + "minItems": 2, + "uniqueItems": true, "items": { "type": "integer", "format": "int32" @@ -129,6 +135,9 @@ }, "z": { "type": "array", + "maxItems": 6, + "minItems": 2, + "uniqueItems": true, "items": { "type": "integer", "format": "int32" diff --git a/NewHorizons/Utility/AstroObjectLocator.cs b/NewHorizons/Utility/AstroObjectLocator.cs index 8c48e1bf..d2d87554 100644 --- a/NewHorizons/Utility/AstroObjectLocator.cs +++ b/NewHorizons/Utility/AstroObjectLocator.cs @@ -1,148 +1,151 @@ -using System.Collections.Generic; -using System.Linq; -using UnityEngine; -namespace NewHorizons.Utility -{ - public static class AstroObjectLocator - { - private static Dictionary _customAstroObjectDictionary = new Dictionary(); - - public static void Init() - { - _customAstroObjectDictionary = new Dictionary(); - foreach (AstroObject ao in GameObject.FindObjectsOfType()) - { - // Ignore the sun station debris, we handle it as a child of the sun station - if (ao.gameObject.name == "SS_Debris_Body") continue; - - RegisterCustomAstroObject(ao); - } - } - - public static AstroObject GetAstroObject(string name, bool flag = false) - { - if (string.IsNullOrEmpty(name)) return null; - - if (_customAstroObjectDictionary.ContainsKey(name)) - { - return _customAstroObjectDictionary[name]; - } - - // Else check stock names - var stringID = name.ToUpper().Replace(" ", "_").Replace("'", ""); - if (stringID.Equals("ATTLEROCK")) stringID = "TIMBER_MOON"; - if (stringID.Equals("HOLLOWS_LANTERN")) stringID = "VOLCANIC_MOON"; - if (stringID.Equals("ASH_TWIN")) stringID = "TOWER_TWIN"; - if (stringID.Equals("EMBER_TWIN")) stringID = "CAVE_TWIN"; - if (stringID.Equals("INTERLOPER")) stringID = "COMET"; - - string key; - if (stringID.ToUpper().Replace("_", "").Equals("MAPSATELLITE")) - { - key = AstroObject.Name.MapSatellite.ToString(); - } - else - { - key = AstroObject.StringIDToAstroObjectName(stringID).ToString(); - } - - if (_customAstroObjectDictionary.ContainsKey(key)) - { - return _customAstroObjectDictionary[key]; - } - - // Try again - if (!flag) return GetAstroObject(name.Replace(" ", ""), true); - - return null; - } - - public static void RegisterCustomAstroObject(AstroObject ao) - { - var key = ao._name == AstroObject.Name.CustomString ? ao.GetCustomName() : ao._name.ToString(); - - if (_customAstroObjectDictionary.Keys.Contains(key)) - { - Logger.LogWarning($"Registering duplicate [{ao.name}] as [{key}]"); - _customAstroObjectDictionary[key] = ao; - } - else - { - Logger.Log($"Registering [{ao.name}] as [{key}]"); - _customAstroObjectDictionary.Add(key, ao); - } - } - - public static void DeregisterCustomAstroObject(AstroObject ao) - { - var key = ao._name == AstroObject.Name.CustomString ? ao.GetCustomName() : ao._name.ToString(); - _customAstroObjectDictionary.Remove(key); - } - - public static AstroObject[] GetAllAstroObjects() - { - return _customAstroObjectDictionary.Values.ToArray(); - } - - public static GameObject[] GetMoons(AstroObject primary) - { - return _customAstroObjectDictionary.Values.Where(x => x._primaryBody == primary).Select(x => x.gameObject).ToArray(); - } - - public static GameObject[] GetChildren(AstroObject primary) - { - if (primary == null) return new GameObject[0]; - - var otherChildren = new List(); - switch (primary._name) - { - case AstroObject.Name.TowerTwin: - otherChildren.Add(SearchUtilities.Find("TimeLoopRing_Body")); - break; - case AstroObject.Name.ProbeCannon: - otherChildren.Add(SearchUtilities.Find("NomaiProbe_Body")); - otherChildren.Add(SearchUtilities.Find("CannonMuzzle_Body")); - otherChildren.Add(SearchUtilities.Find("FakeCannonMuzzle_Body (1)")); - otherChildren.Add(SearchUtilities.Find("CannonBarrel_Body")); - otherChildren.Add(SearchUtilities.Find("FakeCannonBarrel_Body (1)")); - otherChildren.Add(SearchUtilities.Find("Debris_Body (1)")); - break; - case AstroObject.Name.GiantsDeep: - otherChildren.Add(SearchUtilities.Find("BrambleIsland_Body")); - otherChildren.Add(SearchUtilities.Find("GabbroIsland_Body")); - otherChildren.Add(SearchUtilities.Find("QuantumIsland_Body")); - otherChildren.Add(SearchUtilities.Find("StatueIsland_Body")); - otherChildren.Add(SearchUtilities.Find("ConstructionYardIsland_Body")); - otherChildren.Add(SearchUtilities.Find("GabbroShip_Body")); - break; - case AstroObject.Name.WhiteHole: - otherChildren.Add(SearchUtilities.Find("WhiteholeStation_Body")); - otherChildren.Add(SearchUtilities.Find("WhiteholeStationSuperstructure_Body")); - break; - case AstroObject.Name.TimberHearth: - otherChildren.Add(SearchUtilities.Find("MiningRig_Body")); - otherChildren.Add(SearchUtilities.Find("Ship_Body")); - otherChildren.Add(SearchUtilities.Find("ModelRocket_Body")); - break; - case AstroObject.Name.DreamWorld: - otherChildren.Add(SearchUtilities.Find("BackRaft_Body")); - otherChildren.Add(SearchUtilities.Find("SealRaft_Body")); - break; - // For some dumb reason the sun station doesn't use AstroObject.Name.SunStation - case AstroObject.Name.CustomString: - if (primary._customName.Equals("Sun Station")) - { - // there are multiple debris with the same name - otherChildren.AddRange(Object.FindObjectsOfType() - .Select(x => x.gameObject) - .Where(x => x.name == "SS_Debris_Body")); - } - break; - default: - break; - } - - return otherChildren.ToArray(); - } - } -} +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +namespace NewHorizons.Utility +{ + public static class AstroObjectLocator + { + private static Dictionary _customAstroObjectDictionary = new Dictionary(); + + public static void Init() + { + _customAstroObjectDictionary = new Dictionary(); + foreach (AstroObject ao in GameObject.FindObjectsOfType()) + { + // Ignore the sun station debris, we handle it as a child of the sun station + if (ao.gameObject.name == "SS_Debris_Body") continue; + + RegisterCustomAstroObject(ao); + } + } + + public static AstroObject GetAstroObject(string name, bool flag = false) + { + if (string.IsNullOrEmpty(name)) return null; + + if (_customAstroObjectDictionary.ContainsKey(name)) + { + return _customAstroObjectDictionary[name]; + } + + // Else check stock names + var stringID = name.ToUpper().Replace(" ", "_").Replace("'", ""); + if (stringID.Equals("ATTLEROCK")) stringID = "TIMBER_MOON"; + if (stringID.Equals("HOLLOWS_LANTERN")) stringID = "VOLCANIC_MOON"; + if (stringID.Equals("ASH_TWIN")) stringID = "TOWER_TWIN"; + if (stringID.Equals("EMBER_TWIN")) stringID = "CAVE_TWIN"; + if (stringID.Equals("INTERLOPER")) stringID = "COMET"; + + string key; + if (stringID.ToUpper().Replace("_", "").Equals("MAPSATELLITE")) + { + key = AstroObject.Name.MapSatellite.ToString(); + } + else + { + key = AstroObject.StringIDToAstroObjectName(stringID).ToString(); + } + + if (_customAstroObjectDictionary.ContainsKey(key)) + { + return _customAstroObjectDictionary[key]; + } + + // Try again + if (!flag) return GetAstroObject(name.Replace(" ", ""), true); + + return null; + } + + public static void RegisterCustomAstroObject(AstroObject ao) + { + var key = ao._name == AstroObject.Name.CustomString ? ao.GetCustomName() : ao._name.ToString(); + + if (_customAstroObjectDictionary.Keys.Contains(key)) + { + Logger.LogWarning($"Registering duplicate [{ao.name}] as [{key}]"); + _customAstroObjectDictionary[key] = ao; + } + else + { + Logger.Log($"Registering [{ao.name}] as [{key}]"); + _customAstroObjectDictionary.Add(key, ao); + } + } + + public static void DeregisterCustomAstroObject(AstroObject ao) + { + var key = ao._name == AstroObject.Name.CustomString ? ao.GetCustomName() : ao._name.ToString(); + _customAstroObjectDictionary.Remove(key); + } + + public static AstroObject[] GetAllAstroObjects() + { + return _customAstroObjectDictionary.Values.ToArray(); + } + + public static GameObject[] GetMoons(AstroObject primary) + { + return _customAstroObjectDictionary.Values.Where(x => x._primaryBody == primary).Select(x => x.gameObject).ToArray(); + } + + public static GameObject[] GetChildren(AstroObject primary) + { + if (primary == null) return new GameObject[0]; + + var otherChildren = new List(); + switch (primary._name) + { + case AstroObject.Name.TowerTwin: + otherChildren.Add(SearchUtilities.Find("TimeLoopRing_Body")); + break; + case AstroObject.Name.ProbeCannon: + otherChildren.Add(SearchUtilities.Find("NomaiProbe_Body")); + otherChildren.Add(SearchUtilities.Find("CannonMuzzle_Body")); + otherChildren.Add(SearchUtilities.Find("FakeCannonMuzzle_Body (1)")); + otherChildren.Add(SearchUtilities.Find("CannonBarrel_Body")); + otherChildren.Add(SearchUtilities.Find("FakeCannonBarrel_Body (1)")); + otherChildren.Add(SearchUtilities.Find("Debris_Body (1)")); + break; + case AstroObject.Name.GiantsDeep: + otherChildren.Add(SearchUtilities.Find("BrambleIsland_Body")); + otherChildren.Add(SearchUtilities.Find("GabbroIsland_Body")); + otherChildren.Add(SearchUtilities.Find("QuantumIsland_Body")); + otherChildren.Add(SearchUtilities.Find("StatueIsland_Body")); + otherChildren.Add(SearchUtilities.Find("ConstructionYardIsland_Body")); + otherChildren.Add(SearchUtilities.Find("GabbroShip_Body")); + break; + case AstroObject.Name.WhiteHole: + otherChildren.Add(SearchUtilities.Find("WhiteholeStation_Body")); + otherChildren.Add(SearchUtilities.Find("WhiteholeStationSuperstructure_Body")); + break; + case AstroObject.Name.TimberHearth: + otherChildren.Add(SearchUtilities.Find("MiningRig_Body")); + otherChildren.Add(SearchUtilities.Find("Ship_Body")); + otherChildren.Add(SearchUtilities.Find("ModelRocket_Body")); + break; + case AstroObject.Name.DreamWorld: + otherChildren.Add(SearchUtilities.Find("BackRaft_Body")); + otherChildren.Add(SearchUtilities.Find("SealRaft_Body")); + break; + case AstroObject.Name.MapSatellite: + otherChildren.Add(SearchUtilities.Find("HearthianRecorder_Body")); + break; + // For some dumb reason the sun station doesn't use AstroObject.Name.SunStation + case AstroObject.Name.CustomString: + if (primary._customName.Equals("Sun Station")) + { + // there are multiple debris with the same name + otherChildren.AddRange(Object.FindObjectsOfType() + .Select(x => x.gameObject) + .Where(x => x.name == "SS_Debris_Body")); + } + break; + default: + break; + } + + return otherChildren.ToArray(); + } + } +} diff --git a/NewHorizons/Utility/CoordinateUtilities.cs b/NewHorizons/Utility/CoordinateUtilities.cs index 7d7fac9b..6d5af6ca 100644 --- a/NewHorizons/Utility/CoordinateUtilities.cs +++ b/NewHorizons/Utility/CoordinateUtilities.cs @@ -1,17 +1,65 @@ -using UnityEngine; +using UnityEngine; namespace NewHorizons.Utility { public static class CoordinateUtilities { - public static Vector3 CartesianToSpherical(Vector3 v) + // Longitude and latitude are in degrees + // Using the phi and theta convention used on https://mathworld.wolfram.com/SphericalCoordinates.html (Mathematics not physics convention) + public static Vector3 CartesianToSpherical(Vector3 v, bool useUnityCoords = true) { - float dist = Mathf.Sqrt(v.x * v.x + v.y * v.y + v.z * v.z); - float latitude = (Mathf.Rad2Deg * Mathf.Acos(v.z / dist)); + float x, y, z; + if (useUnityCoords) + { + // Y is up in unity + x = v.x; + y = v.z; + z = v.y; + } + else + { + x = v.x; + y = v.y; + z = v.z; + } + + float dist = Mathf.Sqrt(x * x + y * y + z * z); + + // theta float longitude = 180f; - if (v.x > 0) longitude = Mathf.Rad2Deg * Mathf.Atan(v.y / v.x); - if (v.x < 0) longitude = Mathf.Rad2Deg * (Mathf.Atan(v.y / v.x) + Mathf.PI); + if (x > 0) longitude = Mathf.Rad2Deg * Mathf.Atan(y / x); + if (x < 0) longitude = Mathf.Rad2Deg * (Mathf.Atan(y / x) + Mathf.PI); + + // phi + float latitude = (Mathf.Rad2Deg * Mathf.Acos(z / dist)); return new Vector3(longitude, latitude, dist); } + + public static Vector3 SphericalToCartesian(Vector3 v, bool useUnityCoords = true) + { + var longitude = v.x; + var latitude = v.y; + var r = v.z; + + var theta = Mathf.Deg2Rad * longitude; + var phi = Mathf.Deg2Rad * latitude; + + float x, y, z; + + if (useUnityCoords) + { + x = r * Mathf.Cos(theta) * Mathf.Sin(phi); + z = r * Mathf.Sin(theta) * Mathf.Sin(phi); + y = r * Mathf.Cos(phi); + } + else + { + x = r * Mathf.Cos(theta) * Mathf.Sin(phi); + y = r * Mathf.Sin(theta) * Mathf.Sin(phi); + z = r * Mathf.Cos(phi); + } + + return new Vector3(x, y, z); + } } } diff --git a/NewHorizons/Utility/DebugMenu/DebugMenu.cs b/NewHorizons/Utility/DebugMenu/DebugMenu.cs index ca306e27..c33e77f5 100644 --- a/NewHorizons/Utility/DebugMenu/DebugMenu.cs +++ b/NewHorizons/Utility/DebugMenu/DebugMenu.cs @@ -28,17 +28,16 @@ namespace NewHorizons.Utility.DebugMenu static bool openMenuOnPause; static bool staticInitialized; - // menu params + // Menu params internal static IModBehaviour loadedMod = null; internal Dictionary loadedConfigFiles = new Dictionary(); private bool saveButtonUnlocked = false; private Vector2 recentModListScrollPosition = Vector2.zero; - // submenus + // Submenus private List submenus; private int activeSubmenu = 0; - private static JsonSerializerSettings jsonSettings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, @@ -55,7 +54,6 @@ namespace NewHorizons.Utility.DebugMenu new DebugMenuNomaiText() }; - submenus.ForEach((submenu) => submenu.OnAwake(this)); } @@ -89,6 +87,7 @@ namespace NewHorizons.Utility.DebugMenu pauseMenuButton = Main.Instance.ModHelper.Menus.PauseMenu.OptionsButton.Duplicate(TranslationHandler.GetTranslation("Toggle Dev Tools Menu", TranslationHandler.TextType.UI).ToUpper()); InitMenu(); } + public static void UpdatePauseMenuButton() { if (pauseMenuButton != null) @@ -99,11 +98,11 @@ namespace NewHorizons.Utility.DebugMenu } private void RestoreMenuOpennessState() { menuOpen = openMenuOnPause; } + private void ToggleMenu() { menuOpen = !menuOpen; openMenuOnPause = !openMenuOnPause; } private void CloseMenu() { menuOpen = false; } - private void OnGUI() { if (!menuOpen) return; @@ -113,8 +112,7 @@ namespace NewHorizons.Utility.DebugMenu GUILayout.BeginArea(new Rect(menuPosition.x, menuPosition.y, EditorMenuSize.x, EditorMenuSize.y), _editorMenuStyle); - // continue working on existing mod - + // Continue working on existing mod GUILayout.Label("Name of your mod"); if (loadedMod == null) { @@ -138,9 +136,9 @@ namespace NewHorizons.Utility.DebugMenu GUILayout.Space(5); - // save your work - - if (loadedMod != null) { + // Save your work + if (loadedMod != null) + { GUILayout.BeginHorizontal(); if (GUILayout.Button(saveButtonUnlocked ? " O " : " | ", GUILayout.ExpandWidth(false))) { @@ -157,23 +155,23 @@ namespace NewHorizons.Utility.DebugMenu } GUILayout.Space(20); - - // draw submenu stuff + + // Draw submenu stuff if (loadedMod != null) { GUILayout.BeginHorizontal(_tabBarStyle); GUILayout.Space(5); - for (int i = 0; i < submenus.Count; i++) + for (int i = 0; i < submenus.Count; i++) { GUI.enabled = i != activeSubmenu; var style = i == activeSubmenu ? _submenuStyle : _tabBarStyle; - if (GUILayout.Button(" "+submenus[i].SubmenuName()+" ", style, GUILayout.ExpandWidth(false))) - { + if (GUILayout.Button(" " + submenus[i].SubmenuName() + " ", style, GUILayout.ExpandWidth(false))) + { GUI.enabled = true; submenus[activeSubmenu].LoseActive(); activeSubmenu = i; submenus[activeSubmenu].GainActive(); - + } GUI.enabled = true; @@ -186,7 +184,6 @@ namespace NewHorizons.Utility.DebugMenu submenus[activeSubmenu].OnGUI(this); GUILayout.EndVertical(); } - GUILayout.EndArea(); } @@ -198,15 +195,16 @@ namespace NewHorizons.Utility.DebugMenu var folder = loadedMod.ModHelper.Manifest.ModFolderPath; - List bodiesForThisMod = Main.BodyDict.Values.SelectMany(x => x).Where(x => x.Mod == loadedMod).ToList(); - foreach (NewHorizonsBody body in bodiesForThisMod) + var bodiesForThisMod = Main.BodyDict.Values.SelectMany(x => x).Where(x => x.Mod == loadedMod).ToList(); + foreach (var body in bodiesForThisMod) { if (body.RelativePath == null) { - Logger.Log("Error loading config for " + body.Config.name + " in " + body.Config.starSystem); + Logger.Log($"Error loading config for {body.Config.name} in {body.Config.starSystem}"); + continue; } - loadedConfigFiles[folder + body.RelativePath] = (body.Config as PlanetConfig); + loadedConfigFiles[folder + body.RelativePath] = body.Config; submenus.ForEach(submenu => submenu.LoadConfigFile(this, body.Config)); } } @@ -215,12 +213,14 @@ namespace NewHorizons.Utility.DebugMenu { submenus.ForEach(submenu => submenu.PreSave(this)); - string backupFolderName = "configBackups\\" + DateTime.Now.ToString("yyyyMMddTHHmmss") + "\\"; + var backupFolderName = $"configBackups\\{DateTime.Now.ToString("yyyyMMddTHHmmss")}\\"; + Logger.Log($"Potentially saving {loadedConfigFiles.Keys.Count} files"); foreach (var filePath in loadedConfigFiles.Keys) { - Logger.Log("Possibly Saving... " + loadedConfigFiles[filePath].name + " @ " + filePath); + Logger.Log($"Possibly Saving... {loadedConfigFiles[filePath].name} @ {filePath}"); + if (loadedConfigFiles[filePath].starSystem != Main.Instance.CurrentStarSystem) continue; var relativePath = filePath.Replace(loadedMod.ModHelper.Manifest.ModFolderPath, ""); @@ -231,14 +231,17 @@ namespace NewHorizons.Utility.DebugMenu try { - Logger.Log("Saving... " + relativePath + " to " + filePath); + Logger.Log($"Saving... {relativePath} to {filePath}"); var path = loadedMod.ModHelper.Manifest.ModFolderPath + relativePath; var directoryName = Path.GetDirectoryName(path); Directory.CreateDirectory(directoryName); File.WriteAllText(path, json); } - catch (Exception e) { Logger.LogError("Failed to save file " + backupFolderName + relativePath); Logger.LogError(e.Message + "\n" + e.StackTrace); } + catch (Exception e) + { + Logger.LogError($"Failed to save file {relativePath}:\n{e.Message}\n{e.StackTrace}"); + } try { @@ -248,7 +251,10 @@ namespace NewHorizons.Utility.DebugMenu File.WriteAllText(path, json); } - catch (Exception e) { Logger.LogError("Failed to save backup file " + backupFolderName + relativePath); Logger.LogError(e.Message + "\n" + e.StackTrace); } + catch (Exception e) + { + Logger.LogError($"Failed to save backup file {backupFolderName}{relativePath}:\n{e.Message}\n{e.StackTrace}"); + } } } @@ -262,7 +268,6 @@ namespace NewHorizons.Utility.DebugMenu pauseMenuButton.OnClick += ToggleMenu; submenus.ForEach(submenu => submenu.OnInit(this)); - _editorMenuStyle = new GUIStyle { diff --git a/NewHorizons/Utility/DebugMenu/DebugMenuNomaiText.cs b/NewHorizons/Utility/DebugMenu/DebugMenuNomaiText.cs index 6a174f68..c8d82122 100644 --- a/NewHorizons/Utility/DebugMenu/DebugMenuNomaiText.cs +++ b/NewHorizons/Utility/DebugMenu/DebugMenuNomaiText.cs @@ -122,11 +122,14 @@ namespace NewHorizons.Utility.DebugMenu id = id }; - string regex = @"Arc \d+ - Child of (\d*)"; // $"Arc {i} - Child of {parentID}"; - var parentID = (new Regex(regex)).Matches(metadata.spiralGo.name)[0].Groups[1].Value; - metadata.parentID = parentID == "" ? -1 : int.Parse(parentID); - - Logger.Log("Parent id for '" + metadata.spiralGo.name + "' : " + parentID); + if (metadata.spiralGo != null) + { + string regex = @"Arc \d+ - Child of (\d*)"; // $"Arc {i} - Child of {parentID}"; + var parentID = (new Regex(regex)).Matches(metadata.spiralGo.name)[0].Groups[1].Value; + metadata.parentID = parentID == "" ? -1 : int.Parse(parentID); + + Logger.Log("Parent id for '" + metadata.spiralGo.name + "' : " + parentID); + } conversationMetadata.spirals.Add(metadata); } @@ -216,7 +219,7 @@ namespace NewHorizons.Utility.DebugMenu conversationMeta.conversation.rotation = null; UpdateConversationTransform(conversationMeta, sectorObject); } - else + else if (conversationMeta.conversationGo != null) { conversationMeta.conversationGo.transform.localPosition = data.pos; DebugPropPlacer.SetGameObjectRotation(conversationMeta.conversationGo, data, _dnp.gameObject.transform.position); @@ -478,6 +481,7 @@ namespace NewHorizons.Utility.DebugMenu void UpdateConversationTransform(ConversationMetadata conversationMetadata, GameObject sectorParent) { var nomaiWallTextObj = conversationMetadata.conversationGo; + if (nomaiWallTextObj == null) return; var planetGO = sectorParent; var info = conversationMetadata.conversation; @@ -507,6 +511,7 @@ namespace NewHorizons.Utility.DebugMenu { conversations.ForEach(metadata => { + if (metadata.conversationGo == null) return; metadata.conversation.position = metadata.conversationGo.transform.localPosition; metadata.conversation.rotation = metadata.conversationGo.transform.localEulerAngles; }); diff --git a/NewHorizons/Utility/DebugMenu/DebugMenuPropPlacer.cs b/NewHorizons/Utility/DebugMenu/DebugMenuPropPlacer.cs index d98fd8ba..9d61f2f1 100644 --- a/NewHorizons/Utility/DebugMenu/DebugMenuPropPlacer.cs +++ b/NewHorizons/Utility/DebugMenu/DebugMenuPropPlacer.cs @@ -12,14 +12,28 @@ namespace NewHorizons.Utility.DebugMenu { class DebugMenuPropPlacer : DebugSubmenu { - private Vector2 recentPropsScrollPosition = Vector2.zero; private HashSet favoriteProps = new HashSet(); + private List propsLoadedFromConfig = new List(); public static readonly char separatorCharacter = '☧'; // since no chars are illegal in game object names, I picked one that's extremely unlikely to be used to be a separator private static readonly string favoritePropsPlayerPrefKey = "FavoriteProps"; internal DebugPropPlacer _dpp; internal DebugRaycaster _drc; + // misc + private GameObject mostRecentlyPlacedProp; + private Vector3 mostRecentlyPlacedPropSphericalPos; + + // menu params + private Vector2 recentPropsScrollPosition = Vector2.zero; + private bool propsCollapsed = false; + private bool propPositioningCollapsed = false; + private Vector3 propPosDelta = new Vector3(0.1f, 0.1f, 0.1f); + private Vector3 propRotDelta = new Vector3(0.1f, 0.1f, 0.1f); + private Vector3 propSphericalPosDelta = new Vector3(0.1f, 0.1f, 0.1f); + private float propRotationAboutLocalUpDelta = 0.1f; + private float propScaleDelta = 0.1f; + internal override string SubmenuName() { return "Prop Placer"; @@ -40,7 +54,7 @@ namespace NewHorizons.Utility.DebugMenu internal override void OnBeginLoadMod(DebugMenu debugMenu) { - + } internal override void GainActive() @@ -52,10 +66,10 @@ namespace NewHorizons.Utility.DebugMenu { DebugPropPlacer.active = false; } - + internal override void LoadConfigFile(DebugMenu menu, PlanetConfig config) { - _dpp.FindAndRegisterPropsFromConfig(config); + _dpp.FindAndRegisterPropsFromConfig(config, propsLoadedFromConfig); } private void LoadFavoriteProps() @@ -81,16 +95,176 @@ namespace NewHorizons.Utility.DebugMenu _dpp.SetCurrentObject(GUILayout.TextArea(_dpp.currentObject)); GUILayout.Space(5); + GUILayout.Space(5); + + var arrow = propsCollapsed ? " > " : " v "; + if (GUILayout.Button(arrow + "Recently placed objects", menu._tabBarStyle)) propsCollapsed = !propsCollapsed; + if (!propsCollapsed) DrawPropsList(menu); + GUILayout.Space(5); + + if (_dpp.mostRecentlyPlacedPropGO != null) + { + arrow = propPositioningCollapsed ? " > " : " v "; + if (GUILayout.Button(arrow + "Position last placed prop", menu._tabBarStyle)) propPositioningCollapsed = !propPositioningCollapsed; + if (!propPositioningCollapsed) DrawPropsAdustmentControls(menu); + } + } + + private void DrawPropsAdustmentControls(DebugMenu menu) + { + var propPath = _dpp.mostRecentlyPlacedPropPath; + var propPathElements = propPath[propPath.Length - 1] == '/' + ? propPath.Substring(0, propPath.Length - 1).Split('/') + : propPath.Split('/'); + string propName = propPathElements[propPathElements.Length - 1]; + GUILayout.Label($"Reposition {propName}: "); + + + Vector3 latestPropPosDelta = VectorInput(_dpp.mostRecentlyPlacedPropGO.transform.localPosition, propPosDelta, out propPosDelta, "x", "y", "z"); + _dpp.mostRecentlyPlacedPropGO.transform.localPosition += latestPropPosDelta; + if (latestPropPosDelta != Vector3.zero) mostRecentlyPlacedPropSphericalPos = DeltaSphericalPosition(mostRecentlyPlacedProp, Vector3.zero); + + //GUILayout.Space(5); + //Vector3 latestPropRotDelta = VectorInput(_dpp.mostRecentlyPlacedPropGO.transform.localEulerAngles, propRotDelta, out propRotDelta, "x", "y", "z"); + //_dpp.mostRecentlyPlacedPropGO.transform.localEulerAngles += latestPropRotDelta; + + GUILayout.Space(5); + GUILayout.Space(5); + + + if (mostRecentlyPlacedProp != _dpp.mostRecentlyPlacedPropGO) + { + mostRecentlyPlacedProp = _dpp.mostRecentlyPlacedPropGO; + mostRecentlyPlacedPropSphericalPos = DeltaSphericalPosition(mostRecentlyPlacedProp, Vector3.zero); + } + + Vector3 latestPropSphericalPosDelta = VectorInput(mostRecentlyPlacedPropSphericalPos, propSphericalPosDelta, out propSphericalPosDelta, "lat ", "lon ", "height"); + if (latestPropSphericalPosDelta != Vector3.zero) + { + DeltaSphericalPosition(mostRecentlyPlacedProp, latestPropSphericalPosDelta); + mostRecentlyPlacedPropSphericalPos = mostRecentlyPlacedPropSphericalPos + latestPropSphericalPosDelta; + } + + GUILayout.Space(5); + GUILayout.Space(5); + + + GUILayout.BeginHorizontal(); + GUILayout.Label("Rotate about up: ", GUILayout.Width(50)); + float deltaRot = 0; + if (GUILayout.Button("+", GUILayout.ExpandWidth(false))) deltaRot += propRotationAboutLocalUpDelta; + if (GUILayout.Button("-", GUILayout.ExpandWidth(false))) deltaRot -= propRotationAboutLocalUpDelta; + propRotationAboutLocalUpDelta = float.Parse(GUILayout.TextField(propRotationAboutLocalUpDelta + "", GUILayout.Width(100))); + + if (deltaRot != 0) + { + Transform astroObject = mostRecentlyPlacedProp.transform.parent.parent; + mostRecentlyPlacedProp.transform.RotateAround(mostRecentlyPlacedProp.transform.position, mostRecentlyPlacedProp.transform.up, deltaRot); + } + GUILayout.EndHorizontal(); + + + GUILayout.BeginHorizontal(); + GUILayout.Label("scale: ", GUILayout.Width(50)); + var scaleString = mostRecentlyPlacedProp.transform.localScale.x + ""; + var newScaleString = GUILayout.TextField(scaleString, GUILayout.Width(50)); + var parsedScaleString = mostRecentlyPlacedProp.transform.localScale.x; try { parsedScaleString = float.Parse(newScaleString); } catch { } + float deltaScale = scaleString == newScaleString ? 0 : parsedScaleString - mostRecentlyPlacedProp.transform.localScale.x; + if (GUILayout.Button("+", GUILayout.ExpandWidth(false))) deltaScale += propScaleDelta; + if (GUILayout.Button("-", GUILayout.ExpandWidth(false))) deltaScale -= propScaleDelta; + propScaleDelta = float.Parse(GUILayout.TextField(propScaleDelta + "", GUILayout.Width(100))); + + if (deltaScale != 0) + { + float newScale = mostRecentlyPlacedProp.transform.localScale.x + deltaScale; + mostRecentlyPlacedProp.transform.localScale = new Vector3(newScale, newScale, newScale); + } + GUILayout.EndHorizontal(); + } + private Vector3 DeltaSphericalPosition(GameObject prop, Vector3 deltaSpherical) + { + Transform astroObject = prop.transform.parent.parent; + Transform sector = prop.transform.parent; + Vector3 originalLocalPos = astroObject.InverseTransformPoint(prop.transform.position); // parent is the sector, this gives localPos relative to the astroobject (what the DetailBuilder asks for) + Vector3 sphericalPos = CoordinateUtilities.CartesianToSpherical(originalLocalPos); + + if (deltaSpherical == Vector3.zero) return sphericalPos; + Vector3 newSpherical = sphericalPos + deltaSpherical; + + Vector3 finalLocalPosition = CoordinateUtilities.SphericalToCartesian(newSpherical); + Vector3 finalAbsolutePosition = astroObject.TransformPoint(finalLocalPosition); + prop.transform.localPosition = prop.transform.parent.InverseTransformPoint(finalAbsolutePosition); + // prop.transform.rotation = Quaternion.FromToRotation(originalLocalPos.normalized, finalLocalPosition.normalized) * prop.transform.rotation; + + // first, rotate the object by the astroObject's rotation, that means anything afterwards is relative to this rotation (ie, we can pretend the astroObject has 0 rotation) + // then, rotate by the difference in position, basically accounting for the curvature of a sphere + // then re-apply the local rotations of the hierarchy down to the prop (apply the sector local rotation, then the prop local rotation) + + // since we're doing all rotation relative to the astro object, we start with its absolute rotation + // then we apply the rotation about the astroobject using FromTooRotation + // then we reapply the local rotations down through the hierarchy + prop.transform.rotation = astroObject.rotation * Quaternion.FromToRotation(originalLocalPos.normalized, finalLocalPosition.normalized) * sector.localRotation * prop.transform.localRotation; + + return newSpherical; + } + + private Vector3 VectorInput(Vector3 input, Vector3 deltaControls, out Vector3 deltaControlsOut, string labelX, string labelY, string labelZ) + { + var dx = deltaControls.x; + var dy = deltaControls.y; + var dz = deltaControls.z; + + // x + GUILayout.BeginHorizontal(); + GUILayout.Label(labelX + ": ", GUILayout.Width(50)); + var xString = input.x + ""; + var newXString = GUILayout.TextField(xString, GUILayout.Width(50)); + var parsedXString = input.x; try { parsedXString = float.Parse(newXString); } catch { } + float deltaX = xString == newXString ? 0 : parsedXString - input.x; + if (GUILayout.Button("+", GUILayout.ExpandWidth(false))) deltaX += dx; + if (GUILayout.Button("-", GUILayout.ExpandWidth(false))) deltaX -= dx; + dx = float.Parse(GUILayout.TextField(dx + "", GUILayout.Width(100))); + GUILayout.EndHorizontal(); + + // y + GUILayout.BeginHorizontal(); + GUILayout.Label(labelY + ": ", GUILayout.Width(50)); + var yString = input.y + ""; + var newYString = GUILayout.TextField(yString, GUILayout.Width(50)); + var parsedYString = input.y; try { parsedYString = float.Parse(newYString); } catch { } + float deltaY = yString == newYString ? 0 : parsedYString - input.y; + if (GUILayout.Button("+", GUILayout.ExpandWidth(false))) deltaY += dy; + if (GUILayout.Button("-", GUILayout.ExpandWidth(false))) deltaY -= dy; + dy = float.Parse(GUILayout.TextField(dy + "", GUILayout.Width(100))); + GUILayout.EndHorizontal(); + + // z + GUILayout.BeginHorizontal(); + GUILayout.Label(labelZ + ": ", GUILayout.Width(50)); + var zString = input.z + ""; + var newZString = GUILayout.TextField(zString, GUILayout.Width(50)); + var parsedZString = input.z; try { parsedZString = float.Parse(newZString); } catch { } + float deltaZ = zString == newZString ? 0 : parsedZString - input.z; + if (GUILayout.Button("+", GUILayout.ExpandWidth(false))) deltaZ += dz; + if (GUILayout.Button("-", GUILayout.ExpandWidth(false))) deltaZ -= dz; + dz = float.Parse(GUILayout.TextField(dz + "", GUILayout.Width(100))); + GUILayout.EndHorizontal(); + + deltaControlsOut = new Vector3(dx, dy, dz); + return new Vector3(deltaX, deltaY, deltaZ); + } + + private void DrawPropsList(DebugMenu menu) + { // List of recently placed objects - GUILayout.Label("Recently placed objects"); recentPropsScrollPosition = GUILayout.BeginScrollView(recentPropsScrollPosition, GUILayout.Width(menu.EditorMenuSize.x), GUILayout.Height(500)); foreach (string propPath in DebugPropPlacer.RecentlyPlacedProps) { GUILayout.BeginHorizontal(); - var propPathElements = propPath[propPath.Length-1] == '/' - ? propPath.Substring(0, propPath.Length-1).Split('/') + var propPathElements = propPath[propPath.Length - 1] == '/' + ? propPath.Substring(0, propPath.Length - 1).Split('/') : propPath.Split('/'); string propName = propPathElements[propPathElements.Length - 1]; @@ -118,8 +292,6 @@ namespace NewHorizons.Utility.DebugMenu GUILayout.EndHorizontal(); } GUILayout.EndScrollView(); - - GUILayout.Space(5); } internal override void PreSave(DebugMenu menu) @@ -131,43 +303,51 @@ namespace NewHorizons.Utility.DebugMenu { var newDetails = _dpp.GetPropsConfigByBody(); - Logger.Log("Updating config files. New Details Counts by planet: " + string.Join(", ", newDetails.Keys.Select(x => x + $" ({newDetails[x].Length})"))); + var newDetailsCountsByPlanet = string.Join(", ", newDetails.Keys.Select(x => $"{x.name} ({newDetails[x].Length})")); + Logger.Log($"Updating config files. New Details Counts by planet: {newDetailsCountsByPlanet}"); - Dictionary planetToConfigPath = new Dictionary(); + var planetToConfigPath = new Dictionary(); // Get all configs foreach (var filePath in menu.loadedConfigFiles.Keys) { - Logger.Log("potentially updating copy of config at " + filePath); + Logger.Log($"Potentially updating copy of config at {filePath}"); + Logger.Log($"{menu.loadedConfigFiles[filePath].name} {AstroObjectLocator.GetAstroObject(menu.loadedConfigFiles[filePath].name)?.name}"); + Logger.Log($"{menu.loadedConfigFiles[filePath].name}"); if (menu.loadedConfigFiles[filePath].starSystem != Main.Instance.CurrentStarSystem) return; - if (menu.loadedConfigFiles[filePath].name == null || AstroObjectLocator.GetAstroObject(menu.loadedConfigFiles[filePath].name) == null) { Logger.Log("Failed to update copy of config at " + filePath); continue; } + if (menu.loadedConfigFiles[filePath].name == null || AstroObjectLocator.GetAstroObject(menu.loadedConfigFiles[filePath].name) == null) + { + Logger.Log("Failed to update copy of config at " + filePath); + continue; + } - var astroObjectName = DebugPropPlacer.GetAstroObjectName(menu.loadedConfigFiles[filePath].name); - planetToConfigPath[astroObjectName] = filePath; + var astroObject = AstroObjectLocator.GetAstroObject(menu.loadedConfigFiles[filePath].name); + planetToConfigPath[astroObject] = filePath; - if (!newDetails.ContainsKey(astroObjectName)) continue; + if (!newDetails.ContainsKey(astroObject)) continue; - if (menu.loadedConfigFiles[filePath].Props == null) menu.loadedConfigFiles[filePath].Props = new External.Modules.PropModule(); - menu.loadedConfigFiles[filePath].Props.details = newDetails[astroObjectName]; + if (menu.loadedConfigFiles[filePath].Props == null) menu.loadedConfigFiles[filePath].Props = new PropModule(); + menu.loadedConfigFiles[filePath].Props.details = newDetails[astroObject]; - Logger.Log("successfully updated copy of config file for " + astroObjectName); + Logger.Log($"Successfully updated copy of config file for {astroObject.name}"); } // find all new planets that do not yet have config paths var planetsThatDoNotHaveConfigFiles = newDetails.Keys.Where(x => !planetToConfigPath.ContainsKey(x)).ToList(); - foreach (var astroObjectName in planetsThatDoNotHaveConfigFiles) + foreach (var astroObject in planetsThatDoNotHaveConfigFiles) { - Logger.Log("Fabricating new config file for " + astroObjectName); + Logger.Log("Fabricating new config file for " + astroObject.name); - var filepath = "planets/" + Main.Instance.CurrentStarSystem + "/" + astroObjectName + ".json"; - PlanetConfig c = new PlanetConfig(); - c.starSystem = Main.Instance.CurrentStarSystem; - c.name = astroObjectName; - c.Props = new PropModule(); - c.Props.details = newDetails[astroObjectName]; + var filepath = $"planets/{Main.Instance.CurrentStarSystem}/{astroObject.name}.json"; + + var config = new PlanetConfig(); + config.starSystem = Main.Instance.CurrentStarSystem; + config.name = astroObject._name == AstroObject.Name.CustomString ? astroObject.GetCustomName() : astroObject._name.ToString(); + config.Props = new PropModule(); + config.Props.details = newDetails[astroObject]; - menu.loadedConfigFiles[filepath] = c; + menu.loadedConfigFiles[filepath] = config; } } } diff --git a/NewHorizons/Utility/DebugUtilities/DebugArrow.cs b/NewHorizons/Utility/DebugUtilities/DebugArrow.cs index 9f07c954..faf7233d 100644 --- a/NewHorizons/Utility/DebugUtilities/DebugArrow.cs +++ b/NewHorizons/Utility/DebugUtilities/DebugArrow.cs @@ -125,6 +125,7 @@ namespace NewHorizons.Utility.DebugUtilities }.Select(vIdx => vIdx + topVerts.Length+bottomVerts.Length).ToArray(); Mesh m = new Mesh(); + m.name = "DebugArrow"; m.vertices = topVerts.Concat(bottomVerts).Concat(sideVerts).ToArray(); m.triangles = topTris.Concat(bottomTris).Concat(sideTris).ToArray(); m.RecalculateNormals(); diff --git a/NewHorizons/Utility/DebugUtilities/DebugPropPlacer.cs b/NewHorizons/Utility/DebugUtilities/DebugPropPlacer.cs index 29563f47..e482d84f 100644 --- a/NewHorizons/Utility/DebugUtilities/DebugPropPlacer.cs +++ b/NewHorizons/Utility/DebugUtilities/DebugPropPlacer.cs @@ -1,284 +1,283 @@ -using NewHorizons.Builder.Props; -using NewHorizons.External.Configs; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using UnityEngine; -using UnityEngine.InputSystem; -using static NewHorizons.External.Modules.PropModule; - -namespace NewHorizons.Utility.DebugUtilities -{ - - // - // The prop placer. Doesn't interact with any files, just places and tracks props. - // - - [RequireComponent(typeof(DebugRaycaster))] - class DebugPropPlacer : MonoBehaviour - { - private struct PropPlacementData - { - public string body; - public string system; - public GameObject gameObject; - public DetailInfo detailInfo; - } - - // VASE - public static readonly string DEFAULT_OBJECT = "BrittleHollow_Body/Sector_BH/Sector_NorthHemisphere/Sector_NorthPole/Sector_HangingCity/Sector_HangingCity_District1/Props_HangingCity_District1/OtherComponentsGroup/Props_HangingCity_Building_10/Prefab_NOM_VaseThin"; - - public string currentObject { get; private set; } // path of the prop to be placed - private bool hasAddedCurrentObjectToRecentsList = false; - private List props = new List(); - private List deletedProps = new List(); - private DebugRaycaster _rc; - - public static HashSet RecentlyPlacedProps = new HashSet(); - - public static bool active = false; - - private void Awake() - { - _rc = this.GetRequiredComponent(); - currentObject = DEFAULT_OBJECT; - } - - private void Update() - { - if (!Main.Debug) return; - if (!active) return; - - if (Keyboard.current[Key.G].wasReleasedThisFrame) - { - PlaceObject(); - } - - if (Keyboard.current[Key.Minus].wasReleasedThisFrame) - { - DeleteLast(); - } - - if (Keyboard.current[Key.Equals].wasReleasedThisFrame) - { - UndoDelete(); - } - } - - public void SetCurrentObject(string s) - { - currentObject = s; - hasAddedCurrentObjectToRecentsList = false; - } - - internal void PlaceObject() - { - DebugRaycastData data = _rc.Raycast(); - PlaceObject(data, this.gameObject.transform.position); - - if (!hasAddedCurrentObjectToRecentsList) - { - hasAddedCurrentObjectToRecentsList = true; - - if (!RecentlyPlacedProps.Contains(currentObject)) - { - RecentlyPlacedProps.Add(currentObject); - } - } - } - - public void PlaceObject(DebugRaycastData data, Vector3 playerAbsolutePosition) - { - // TODO: implement sectors - // if this hits a sector, store that sector and add a config file option for it - - if (!data.hitBodyGameObject.name.EndsWith("_Body")) - { - Logger.Log("Cannot place object on non-body object: " + data.hitBodyGameObject.name); - } - - try - { - if (currentObject == "" || currentObject == null) - { - SetCurrentObject(DEFAULT_OBJECT); - } - - GameObject prop = DetailBuilder.MakeDetail(data.hitBodyGameObject, data.hitBodyGameObject.GetComponentInChildren(), currentObject, data.pos, data.norm, 1, false); - PropPlacementData propData = RegisterProp_WithReturn(data.bodyName, prop); - - SetGameObjectRotation(prop, data, playerAbsolutePosition); - } - catch - { - Logger.Log($"Failed to place object {currentObject} on body ${data.hitBodyGameObject} at location ${data.pos}."); - } - } +using NewHorizons.Builder.Props; +using NewHorizons.External.Configs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using UnityEngine.InputSystem; +using static NewHorizons.External.Modules.PropModule; + +namespace NewHorizons.Utility.DebugUtilities +{ + + // + // The prop placer. Doesn't interact with any files, just places and tracks props. + // + + [RequireComponent(typeof(DebugRaycaster))] + class DebugPropPlacer : MonoBehaviour + { + private struct PropPlacementData + { + public AstroObject body; + public string system; + public GameObject gameObject; + public DetailInfo detailInfo; + } + + // VASE + public static readonly string DEFAULT_OBJECT = "BrittleHollow_Body/Sector_BH/Sector_NorthHemisphere/Sector_NorthPole/Sector_HangingCity/Sector_HangingCity_District1/Props_HangingCity_District1/OtherComponentsGroup/Props_HangingCity_Building_10/Prefab_NOM_VaseThin"; + + public string currentObject { get; private set; } // path of the prop to be placed + private bool hasAddedCurrentObjectToRecentsList = false; + private List props = new List(); + private List deletedProps = new List(); + private DebugRaycaster _rc; + + public static HashSet RecentlyPlacedProps = new HashSet(); + + public static bool active = false; + public GameObject mostRecentlyPlacedPropGO { get { return props.Count() <= 0 ? null : props[props.Count() - 1].gameObject; } } + public string mostRecentlyPlacedPropPath { get { return props.Count() <= 0 ? "" : props[props.Count() - 1].detailInfo.path; } } + + private void Awake() + { + _rc = this.GetRequiredComponent(); + currentObject = DEFAULT_OBJECT; + } + + private void Update() + { + if (!Main.Debug) return; + if (!active) return; + + if (Keyboard.current[Key.G].wasReleasedThisFrame) + { + PlaceObject(); + } + + if (Keyboard.current[Key.Minus].wasReleasedThisFrame) + { + DeleteLast(); + } + + if (Keyboard.current[Key.Equals].wasReleasedThisFrame) + { + UndoDelete(); + } + } + + public void SetCurrentObject(string s) + { + currentObject = s; + hasAddedCurrentObjectToRecentsList = false; + } + + internal void PlaceObject() + { + DebugRaycastData data = _rc.Raycast(); + PlaceObject(data, this.gameObject.transform.position); + + if (!hasAddedCurrentObjectToRecentsList) + { + hasAddedCurrentObjectToRecentsList = true; + + if (!RecentlyPlacedProps.Contains(currentObject)) + { + RecentlyPlacedProps.Add(currentObject); + } + } + } + + public void PlaceObject(DebugRaycastData data, Vector3 playerAbsolutePosition) + { + // TODO: implement sectors + // if this hits a sector, store that sector and add a config file option for it + + if (!data.hitBodyGameObject.name.EndsWith("_Body")) + { + Logger.Log("Cannot place object on non-body object: " + data.hitBodyGameObject.name); + } + + try + { + if (currentObject == "" || currentObject == null) + { + SetCurrentObject(DEFAULT_OBJECT); + } + + GameObject prop = DetailBuilder.MakeDetail(data.hitBodyGameObject, data.hitBodyGameObject.GetComponentInChildren(), currentObject, data.pos, data.norm, 1, false); + PropPlacementData propData = RegisterProp_WithReturn(data.hitBodyGameObject.GetComponent(), prop); + + SetGameObjectRotation(prop, data, playerAbsolutePosition); + } + catch + { + Logger.Log($"Failed to place object {currentObject} on body ${data.hitBodyGameObject} at location ${data.pos}."); + } + } public static void SetGameObjectRotation(GameObject prop, DebugRaycastData data, Vector3 playerAbsolutePosition) - { - // align with surface normal - Vector3 alignToSurface = (Quaternion.LookRotation(data.norm) * Quaternion.FromToRotation(Vector3.up, Vector3.forward)).eulerAngles; - prop.transform.localEulerAngles = alignToSurface; - - // rotate facing dir towards player - GameObject g = new GameObject("DebugProp"); - g.transform.parent = prop.transform.parent; - g.transform.localPosition = prop.transform.localPosition; - g.transform.localRotation = prop.transform.localRotation; - - prop.transform.parent = g.transform; - - var dirTowardsPlayer = prop.transform.parent.transform.InverseTransformPoint(playerAbsolutePosition) - prop.transform.localPosition; - dirTowardsPlayer.y = 0; - float rotation = Quaternion.LookRotation(dirTowardsPlayer).eulerAngles.y; - prop.transform.localEulerAngles = new Vector3(0, rotation, 0); - - prop.transform.parent = g.transform.parent; + { + // align with surface normal + Vector3 alignToSurface = (Quaternion.LookRotation(data.norm) * Quaternion.FromToRotation(Vector3.up, Vector3.forward)).eulerAngles; + prop.transform.localEulerAngles = alignToSurface; + + // rotate facing dir towards player + GameObject g = new GameObject("DebugProp"); + g.transform.parent = prop.transform.parent; + g.transform.localPosition = prop.transform.localPosition; + g.transform.localRotation = prop.transform.localRotation; + + prop.transform.parent = g.transform; + + var dirTowardsPlayer = prop.transform.parent.transform.InverseTransformPoint(playerAbsolutePosition) - prop.transform.localPosition; + dirTowardsPlayer.y = 0; + float rotation = Quaternion.LookRotation(dirTowardsPlayer).eulerAngles.y; + prop.transform.localEulerAngles = new Vector3(0, rotation, 0); + + prop.transform.parent = g.transform.parent; GameObject.Destroy(g); } - - public static string GetAstroObjectName(string bodyName) - { - if (bodyName.EndsWith("_Body")) bodyName = bodyName.Substring(0, bodyName.Length-"_Body".Length); - - var astroObject = AstroObjectLocator.GetAstroObject(bodyName); - if (astroObject == null) return null; - - var astroObjectName = astroObject.name; - if (astroObjectName.EndsWith("_Body")) astroObjectName = astroObjectName.Substring(0, astroObjectName.Length-"_Body".Length); - - return astroObjectName; - } - - public void FindAndRegisterPropsFromConfig(PlanetConfig config) - { - if (config.starSystem != Main.Instance.CurrentStarSystem) return; - - AstroObject planet = AstroObjectLocator.GetAstroObject(config.name); - - if (planet == null) return; - if (config.Props == null || config.Props.details == null) return; - - var astroObjectName = GetAstroObjectName(config.name); - - foreach (var detail in config.Props.details) - { - GameObject spawnedProp = DetailBuilder.GetSpawnedGameObjectByDetailInfo(detail); - - if (spawnedProp == null) - { - Logger.LogError("No spawned prop found for " + detail.path); - continue; - } - - PropPlacementData data = RegisterProp_WithReturn(astroObjectName, spawnedProp, detail.path, detail); - - // note: we do not support placing props from assetbundles, so they will not be added to the - // selectable list of placed props - if (detail.assetBundle == null && !RecentlyPlacedProps.Contains(data.detailInfo.path)) - { - RecentlyPlacedProps.Add(data.detailInfo.path); - } - } - } - - public void RegisterProp(string bodyGameObjectName, GameObject prop) - { - RegisterProp_WithReturn(bodyGameObjectName, prop); - } - - private PropPlacementData RegisterProp_WithReturn(string bodyGameObjectName, GameObject prop, string propPath = null, DetailInfo detailInfo = null) - { - if (Main.Debug) - { - // TOOD: make this prop an item - } - - - string bodyName = GetAstroObjectName(bodyGameObjectName); - - Logger.Log("Adding prop to " + Main.Instance.CurrentStarSystem + "::" + bodyName); - - - detailInfo = detailInfo == null ? new DetailInfo() : detailInfo; - detailInfo.path = propPath == null ? currentObject : propPath; - - PropPlacementData data = new PropPlacementData - { - body = bodyName, - gameObject = prop, - system = Main.Instance.CurrentStarSystem, - detailInfo = detailInfo - }; - - props.Add(data); - return data; - } - - public Dictionary GetPropsConfigByBody() - { - var groupedProps = props - .GroupBy(p => p.system + "." + p.body) - .Select(grp => grp.ToList()) - .ToList(); - - Dictionary propConfigs = new Dictionary(); - - foreach (List bodyProps in groupedProps) - { - if (bodyProps == null || bodyProps.Count == 0) continue; - Logger.Log("getting prop group for body " + bodyProps[0].body); - if (AstroObjectLocator.GetAstroObject(bodyProps[0].body) == null) continue; - string bodyName = GetAstroObjectName(bodyProps[0].body); - - DetailInfo[] infoArray = new DetailInfo[bodyProps.Count]; - propConfigs[bodyName] = infoArray; - - for(int i = 0; i < bodyProps.Count; i++) - { - var prop = bodyProps[i]; - var rootTransform = prop.gameObject.transform.root; - - // Objects are parented to the sector and not to the planet - // However, raycasted positions are reported relative to the root game object - // Normally these two are the same, but there are some notable exceptions (ex, floating islands) - // So we can't use local position/rotation here, we have to inverse transform the global position/rotation relative to root object - prop.detailInfo.position = rootTransform.InverseTransformPoint(prop.gameObject.transform.position); - prop.detailInfo.scale = prop.gameObject.transform.localScale.x; - if (!prop.detailInfo.alignToNormal) prop.detailInfo.rotation = rootTransform.InverseTransformRotation(prop.gameObject.transform.rotation).eulerAngles; - - infoArray[i] = prop.detailInfo; - } - } - - return propConfigs; - } - - public void DeleteLast() - { - if (props.Count <= 0) return; - - PropPlacementData last = props[props.Count-1]; - props.RemoveAt(props.Count-1); - - last.gameObject.SetActive(false); - - deletedProps.Add(last); - } - - public void UndoDelete() - { - if (deletedProps.Count <= 0) return; - - PropPlacementData last = deletedProps[deletedProps.Count-1]; - deletedProps.RemoveAt(deletedProps.Count-1); - - last.gameObject.SetActive(true); - - props.Add(last); - } - } -} + + public static string GetAstroObjectName(string bodyName) + { + var astroObject = AstroObjectLocator.GetAstroObject(bodyName); + if (astroObject == null) return null; + + var astroObjectName = astroObject.name; + + return astroObjectName; + } + + public void FindAndRegisterPropsFromConfig(PlanetConfig config, List pathsList = null) + { + if (config.starSystem != Main.Instance.CurrentStarSystem) return; + + AstroObject planet = AstroObjectLocator.GetAstroObject(config.name); + + if (planet == null) return; + if (config.Props == null || config.Props.details == null) return; + + var astroObject = AstroObjectLocator.GetAstroObject(config.name); + + foreach (var detail in config.Props.details) + { + GameObject spawnedProp = DetailBuilder.GetSpawnedGameObjectByDetailInfo(detail); + + if (spawnedProp == null) + { + Logger.LogError("No spawned prop found for " + detail.path); + continue; + } + + PropPlacementData data = RegisterProp_WithReturn(astroObject, spawnedProp, detail.path, detail); + + // note: we do not support placing props from assetbundles, so they will not be added to the + // selectable list of placed props + if (detail.assetBundle == null && !RecentlyPlacedProps.Contains(data.detailInfo.path)) + { + if (pathsList != null) pathsList.Add(data.detailInfo.path); + } + } + } + + public void RegisterProp(AstroObject body, GameObject prop) + { + RegisterProp_WithReturn(body, prop); + } + + private PropPlacementData RegisterProp_WithReturn(AstroObject body, GameObject prop, string propPath = null, DetailInfo detailInfo = null) + { + if (Main.Debug) + { + // TOOD: make this prop an item + } + + //var body = AstroObjectLocator.GetAstroObject(bodyGameObjectName); + + Logger.Log($"Adding prop to {Main.Instance.CurrentStarSystem}::{body.name}"); + + + detailInfo = detailInfo == null ? new DetailInfo() : detailInfo; + detailInfo.path = propPath == null ? currentObject : propPath; + + PropPlacementData data = new PropPlacementData + { + body = body, + gameObject = prop, + system = Main.Instance.CurrentStarSystem, + detailInfo = detailInfo + }; + + props.Add(data); + return data; + } + + public Dictionary GetPropsConfigByBody() + { + var groupedProps = props + .GroupBy(p => p.system + "." + p.body) + .Select(grp => grp.ToList()) + .ToList(); + + Dictionary propConfigs = new Dictionary(); + + foreach (List bodyProps in groupedProps) + { + if (bodyProps == null || bodyProps.Count == 0) continue; + if (bodyProps[0].body == null) continue; + var body = bodyProps[0].body; + Logger.Log("getting prop group for body " + body.name); + //string bodyName = GetAstroObjectName(bodyProps[0].body); + + DetailInfo[] infoArray = new DetailInfo[bodyProps.Count]; + propConfigs[body] = infoArray; + + for (int i = 0; i < bodyProps.Count; i++) + { + var prop = bodyProps[i]; + var rootTransform = prop.gameObject.transform.root; + + // Objects are parented to the sector and not to the planet + // However, raycasted positions are reported relative to the root game object + // Normally these two are the same, but there are some notable exceptions (ex, floating islands) + // So we can't use local position/rotation here, we have to inverse transform the global position/rotation relative to root object + prop.detailInfo.position = rootTransform.InverseTransformPoint(prop.gameObject.transform.position); + prop.detailInfo.scale = prop.gameObject.transform.localScale.x; + if (!prop.detailInfo.alignToNormal) prop.detailInfo.rotation = rootTransform.InverseTransformRotation(prop.gameObject.transform.rotation).eulerAngles; + + infoArray[i] = prop.detailInfo; + } + } + + return propConfigs; + } + + public void DeleteLast() + { + if (props.Count <= 0) return; + + PropPlacementData last = props[props.Count - 1]; + props.RemoveAt(props.Count - 1); + + last.gameObject.SetActive(false); + + deletedProps.Add(last); + } + + public void UndoDelete() + { + if (deletedProps.Count <= 0) return; + + PropPlacementData last = deletedProps[deletedProps.Count - 1]; + deletedProps.RemoveAt(deletedProps.Count - 1); + + last.gameObject.SetActive(true); + + props.Add(last); + } + } +} diff --git a/NewHorizons/Utility/ImageUtilities.cs b/NewHorizons/Utility/ImageUtilities.cs index 693f60e4..b65ea0e9 100644 --- a/NewHorizons/Utility/ImageUtilities.cs +++ b/NewHorizons/Utility/ImageUtilities.cs @@ -14,6 +14,12 @@ namespace NewHorizons.Utility private static Dictionary _loadedTextures = new Dictionary(); private static List _generatedTextures = new List(); + public static bool IsTextureLoaded(IModBehaviour mod, string filename) + { + var path = mod.ModHelper.Manifest.ModFolderPath + filename; + return _loadedTextures.ContainsKey(path); + } + public static Texture2D GetTexture(IModBehaviour mod, string filename) { return GetTexture(mod, filename, true); diff --git a/NewHorizons/manifest.json b/NewHorizons/manifest.json index 4f2896d8..7c418881 100644 --- a/NewHorizons/manifest.json +++ b/NewHorizons/manifest.json @@ -1,9 +1,9 @@ { "filename": "NewHorizons.dll", - "author": "xen and Bwc9876", + "author": "xen, Bwc9876, clay, MegaPiggy, John, Book", "name": "New Horizons", "uniqueName": "xen.NewHorizons", - "version": "1.3.1", + "version": "1.3.2", "owmlVersion": "2.5.2", "conflicts": [ "Raicuparta.QuantumSpaceBuddies", "Vesper.AutoResume", "PacificEngine.OW_Randomizer" ], "pathsToPreserve": [ "planets", "systems", "translations" ] diff --git a/SchemaExporter/SchemaExporter.cs b/SchemaExporter/SchemaExporter.cs index 87a701bd..7b0634fd 100644 --- a/SchemaExporter/SchemaExporter.cs +++ b/SchemaExporter/SchemaExporter.cs @@ -77,6 +77,14 @@ public static class SchemaExporter {"title", _title}, {"description", _description} }); + + if (_title == "Star System Schema") + { + schema.Definitions["NomaiCoordinates"].Properties["x"].UniqueItems = true; + schema.Definitions["NomaiCoordinates"].Properties["y"].UniqueItems = true; + schema.Definitions["NomaiCoordinates"].Properties["z"].UniqueItems = true; + } + return schema; } }