From 8b0d38aa019565de855faf274967cfa29df5c401 Mon Sep 17 00:00:00 2001 From: Ben C Date: Fri, 4 Feb 2022 18:21:19 -0500 Subject: [PATCH 01/50] Basic Version --- .gitignore | 3 +- NewHorizons/Builder/General/ShipLogBuilder.cs | 419 ++++++++++++++++++ NewHorizons/External/IPlanetConfig.cs | 1 + NewHorizons/External/PlanetConfig.cs | 1 + NewHorizons/External/ShipLogModule.cs | 34 ++ NewHorizons/Main.cs | 32 +- NewHorizons/NewHorizons.csproj.user | 6 +- NewHorizons/Tools/Patches.cs | 177 +++++++- NewHorizons/Utility/MVector2.cs | 26 ++ NewHorizons/Utility/SearchUtilities.cs | 10 + 10 files changed, 703 insertions(+), 6 deletions(-) create mode 100644 NewHorizons/External/ShipLogModule.cs create mode 100644 NewHorizons/Utility/MVector2.cs diff --git a/.gitignore b/.gitignore index 204daa57..70f0ab13 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ obj zip *.zip -*/Build/* \ No newline at end of file +*/Build/* +.idea/ diff --git a/NewHorizons/Builder/General/ShipLogBuilder.cs b/NewHorizons/Builder/General/ShipLogBuilder.cs index 8d38d135..2fceed15 100644 --- a/NewHorizons/Builder/General/ShipLogBuilder.cs +++ b/NewHorizons/Builder/General/ShipLogBuilder.cs @@ -1,17 +1,436 @@ using NewHorizons.Components; using System; +using System.Collections; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Xml.Linq; using System.Text; using System.Threading.Tasks; +using NewHorizons.External; +using NewHorizons.Utility; +using OWML.Common; using UnityEngine; +using UnityEngine.UI; using Logger = NewHorizons.Utility.Logger; namespace NewHorizons.Builder.General { public static class ShipLogBuilder { + public static readonly string PAN_ROOT_PATH = "Ship_Body/Module_Cabin/Systems_Cabin/ShipLogPivot/ShipLog/ShipLogPivot/ShipLogCanvas/MapMode/ScaleRoot/PanRoot"; + public static ShipLogStarChartMode ShipLogStarChartMode; + private static Dictionary curiosityColors = new Dictionary(); + private static Dictionary curiosityHighlightColors = new Dictionary(); + private static Dictionary rawNameToCuriosityName = new Dictionary(); + private static Dictionary entryIdToRawName = new Dictionary(); + private static Dictionary astroIdToBody = new Dictionary(); + + private class MapModeObject + { + public int x; + public int y; + public int branch_width; + public int branch_height; + public int level; + public NewHorizonsBody mainBody; + public ShipLogAstroObject astroObject; + public List children; + public MapModeObject parent; + public void increment_width() + { + branch_width++; + parent?.increment_width(); + } + public void increment_height() + { + branch_height++; + parent?.increment_height(); + } + } + + public static string GetAstroBodyShipLogName(string id) + { + return astroIdToBody[id].Config.Name; + } + + public static ShipLogAstroObject[][] ConstructMapMode(string systemName, GameObject transformParent, int layer) + { + MapModeObject rootObject = MakePrimaryNode(systemName); + if (rootObject.mainBody != null) + { + CreateAllNodes(ref rootObject, transformParent, layer); + } + + const int maxAmount = 20; + ShipLogAstroObject[][] navMatrix = new ShipLogAstroObject[maxAmount][]; + for (int i = 0; i < maxAmount; i++) + { + navMatrix[i] = new ShipLogAstroObject[maxAmount]; + } + CreateNavigationMatrix(rootObject, ref navMatrix); + navMatrix = navMatrix.Where(a => a.Count(c => c != null) > 0).Prepend(new ShipLogAstroObject[1]).ToArray(); + for (var index = 0; index < navMatrix.Length; index++) + { + navMatrix[index] = navMatrix[index].Where(a => a != null).ToArray(); + } + return navMatrix; + } + + private static void CreateNavigationMatrix(MapModeObject root, ref ShipLogAstroObject[][] navMatrix) + { + if (root.astroObject != null) + { + navMatrix[root.y][root.x] = root.astroObject; + } + foreach (MapModeObject child in root.children) + { + CreateNavigationMatrix(child, ref navMatrix); + } + } + + private static void CreateAllNodes(ref MapModeObject parentNode, GameObject parent, int layer) + { + CreateNode(ref parentNode, parent, layer); + for (var i = 0; i < parentNode.children.Count; i++) + { + MapModeObject child = parentNode.children[i]; + CreateAllNodes(ref child, parent, layer); + parentNode.children[i] = child; + } + } + + private static GameObject CreateImage(GameObject nodeGO, IModAssets assets, string imagePath, string name, int layer) + { + GameObject newImageGO = new GameObject(name); + newImageGO.layer = layer; + newImageGO.transform.SetParent(nodeGO.transform); + + RectTransform transform = newImageGO.AddComponent(); + transform.localPosition = Vector3.zero; + transform.localRotation = Quaternion.identity; + transform.localScale = Vector3.one; + + Image newImage = newImageGO.AddComponent(); + if (imagePath == "DEFAULT") + { + newImage.sprite = Locator.GetShipLogManager()._shipLogLibrary.defaultEntrySprite; + } + else + { + Texture2D newTexture = assets.GetTexture(imagePath); + Rect rect = new Rect(0, 0, newTexture.width, newTexture.height); + Vector2 pivot = new Vector2(newTexture.width / 2, newTexture.height / 2); + newImage.sprite = Sprite.Create(newTexture, rect, pivot); + } + return newImageGO; + } + + public static T KeyByValue(this Dictionary dict, W val) + { + T key = default; + foreach (KeyValuePair pair in dict) + { + if (EqualityComparer.Default.Equals(pair.Value, val)) + { + key = pair.Key; + break; + } + } + return key; + } + + private static string GetAstroObjectId(NewHorizonsBody body) + { + return KeyByValue(astroIdToBody, body); + } + + private static void CreateAstroObject(GameObject nodeGO, ref MapModeObject node, GameObject referenceUnviewedSprite, int layer) + { + const float unviewedIconOffset = 15; + ShipLogAstroObject astroObject = nodeGO.AddComponent(); + astroObject._id = GetAstroObjectId(node.mainBody); + string imagePath = node.mainBody.Config.ShipLog?.mapMode?.revealedSprite ?? "DEFAULT"; + string outlinePath = node.mainBody.Config.ShipLog?.mapMode?.outlineSprite ?? imagePath; + astroObject._imageObj = CreateImage(nodeGO, node.mainBody.Mod.Assets, imagePath, "Image", layer); + astroObject._outlineObj = CreateImage(nodeGO, node.mainBody.Mod.Assets, outlinePath, "Outline", layer); + astroObject._unviewedObj = GameObject.Instantiate(referenceUnviewedSprite, nodeGO.transform, false); + astroObject._invisibleWhenHidden = node.mainBody.Config.ShipLog?.mapMode?.invisibleWhenHidden ?? false; + Rect imageRect = astroObject._imageObj.GetComponent().rect; + astroObject._unviewedObj.transform.localPosition = new Vector3(imageRect.width / 2 + unviewedIconOffset, imageRect.height / 2 + unviewedIconOffset, 0); + node.astroObject = astroObject; + } + + private static void CreateNode(ref MapModeObject node, GameObject parent, int layer) + { + const float padding = 250f; + + GameObject newNodeGO = new GameObject(node.mainBody.Config.Name + "_ShipLog"); + newNodeGO.layer = layer; + newNodeGO.transform.SetParent(parent.transform); + + RectTransform transform = newNodeGO.AddComponent(); + float scale = node.mainBody.Config.ShipLog?.mapMode?.scale?? 1f; + scale = scale <= 0 ? 1f : scale; + transform.localPosition = new Vector3(node.x * padding, node.y * padding, 0); + transform.localRotation = Quaternion.identity; + transform.localScale = Vector3.one * scale; + + if (node.mainBody.Config.ShipLog?.xmlFile == null) + { + Image newImage = newNodeGO.AddComponent(); + string imagePath = node.mainBody.Config.ShipLog?.mapMode?.revealedSprite ?? "DEFAULT"; + if (imagePath == "DEFAULT") + { + newImage.sprite = Locator.GetShipLogManager()._shipLogLibrary.defaultEntrySprite; + } + else + { + Texture2D newTexture = node.mainBody.Mod.Assets.GetTexture(imagePath); + Rect rect = new Rect(0, 0, newTexture.width, newTexture.height); + Vector2 pivot = new Vector2(newTexture.width / 2, newTexture.height / 2); + newImage.sprite = Sprite.Create(newTexture, rect, pivot); + } + } + else + { + CreateAstroObject(newNodeGO, ref node, GameObject.Find(PAN_ROOT_PATH + "/TimberHearth/UnviewedIcon"), layer); + } + } + + private static MapModeObject MakePrimaryNode(string systemName) + { + foreach (NewHorizonsBody body in Main.BodyDict[systemName]) + { + if (!body.Config.Base.CenterOfSolarSystem) continue; + MapModeObject newNode = new MapModeObject + { + mainBody = body, + level = 0, + x = 0, + y = 0 + }; + newNode.children = MakeChildrenNodes(systemName, newNode); + return newNode; + } + Logger.LogError("Couldn't find center of system!"); + return new MapModeObject(); + } + + private static List MakeChildrenNodes(string systemName, MapModeObject parent) + { + List children = new List(); + int newX = parent.x; + int newY = parent.y; + foreach (NewHorizonsBody body in Main.BodyDict[systemName]) + { + if (body.Config.Orbit.PrimaryBody == parent.mainBody.Config.Name) + { + int newLevel = parent.level + 1; + bool even = newLevel % 2 == 0; + newX = even ? newX : newX + 1; + newY = even ? newY + 1 : newY; + MapModeObject newNode = new MapModeObject() + { + mainBody = body, + level = newLevel, + x = newX, + y = newY, + parent=parent + }; + newNode.children = MakeChildrenNodes(systemName, newNode); + if (even) + { + newY += newNode.branch_height; + parent.increment_height(); + } + else + { + newX += newNode.branch_width; + parent.increment_width(); + } + children.Add(newNode); + } + } + return children; + } + + public static void AddCuriosityColors(ShipLogModule.CuriosityColor[] newColors) + { + foreach (ShipLogModule.CuriosityColor newColor in newColors) + { + if (rawNameToCuriosityName.ContainsKey(newColor.id) == false) + { + CuriosityName newName = (CuriosityName) 8 + rawNameToCuriosityName.Count; + rawNameToCuriosityName.Add(newColor.id, newName); + curiosityColors.Add(newName, newColor.color.ToColor()); + curiosityHighlightColors.Add(newName, newColor.highlightColor.ToColor()); + } + } + } + + public static Color GetCuriosityColor(CuriosityName curiosityName, bool highlighted, Color defaultColor, Color defaultHighlight) + { + if (curiosityColors.ContainsKey(curiosityName) && curiosityHighlightColors.ContainsKey(curiosityName)) + { + return (highlighted ? curiosityHighlightColors : curiosityColors)[curiosityName]; + } + else + { + return highlighted? defaultHighlight : defaultColor; + } + } + + public static void AddAstroBodyToShipLog(ShipLogManager manager, NewHorizonsBody body) + { + string systemName = body.Config.StarSystem; + XElement astroBodyFile = XElement.Load(Main.Instance.ModHelper.Manifest.ModFolderPath + body.Config.ShipLog.xmlFile); + XElement astroBodyId = astroBodyFile.Element("ID"); + if (astroBodyId == null) + { + Logger.LogError("Failed to load ship log for " + systemName + "!"); + } + else + { + astroBodyId.SetValue(systemName + "/" + astroBodyId.Value); + foreach (XElement entryElement in astroBodyFile.DescendantsAndSelf("Entry")) + { + XElement curiosityName = entryElement.Element("Curiosity"); + XElement id = entryElement.Element("ID"); + if (curiosityName != null && id != null && entryIdToRawName.ContainsKey(id.Value) == false) + { + entryIdToRawName.Add(id.Value, curiosityName.Value); + } + AddTranslation(entryElement); + } + TextAsset newAsset = new TextAsset(astroBodyFile.ToString()); + List newBodies = new List(manager._shipLogXmlAssets) {newAsset}; + manager._shipLogXmlAssets = newBodies.ToArray(); + if (astroIdToBody.ContainsKey(astroBodyId.Value) == false) + { + astroIdToBody.Add(astroBodyId.Value, body); + } + } + } + + private static void AddTranslation(XElement entry) + { + Dictionary table = TextTranslation.Get().m_table.theShipLogTable; + XElement nameElement = entry.Element("Name"); + if (nameElement != null) + { + string name = nameElement.Value; + table[name] = name; + foreach (XElement rumorFact in entry.Elements("RumorFact")) + { + XElement rumorName = rumorFact.Element("RumorName"); + if (rumorName != null) + { + table[rumorName.Value] = rumorName.Value; + } + + XElement rumorText = rumorFact.Element("Text"); + if (rumorText != null) + { + table[name + rumorText.Value] = rumorText.Value; + } + } + foreach (XElement exploreFact in entry.Elements("ExploreFact")) + { + XElement exploreText = exploreFact.Element("Text"); + if (exploreText != null) + { + table[name + exploreText.Value] = exploreText.Value; + } + } + } + } + + public static void UpdateEntryCuriosity(ref ShipLogEntry entry) + { + if (entryIdToRawName.ContainsKey(entry._id)) + { + entry._curiosity = rawNameToCuriosityName[entryIdToRawName[entry._id]]; + } + } + + private static Sprite GetSprite(string entryId, NewHorizonsBody body) + { + IModAssets assets = body.Mod.Assets; + string path = body.Config.ShipLog.spriteFolder + "/" + entryId + ".png"; + if (File.Exists(Main.Instance.ModHelper.Manifest.ModFolderPath + path)) + { + Texture2D newTexture = assets.GetTexture(path); + Rect rect = new Rect(0, 0, newTexture.width, newTexture.height); + Vector2 pivot = new Vector2(newTexture.width / 2, newTexture.height / 2); + return Sprite.Create(newTexture, rect, pivot); + } + else + { + return Locator.GetShipLogManager()._shipLogLibrary.defaultEntrySprite; + } + } + + private static NewHorizonsBody GetConfigFromEntry(ShipLogEntry entry) + { + return astroIdToBody[entry._astroObjectID]; + } + + private static Vector2? FindPosition(string entryId, ShipLogModule config) + { + if (config.positions == null) return null; + foreach (ShipLogModule.EntryPosition position in config.positions) + { + if (position.id == entryId) + { + return position.position; + } + } + return null; + } + + public static void GenerateEntryData(ShipLogManager manager) + { + const int step = 400; + int colAccumulator = 0; + int rowAccumulator = 0; + foreach(ShipLogEntry entry in manager._entryList) + { + if (manager._entryDataDict.ContainsKey(entry._id) == false) + { + NewHorizonsBody body = GetConfigFromEntry(entry); + Vector2? manualEntryPosition = FindPosition(entry._id, body.Config.ShipLog); + Vector2 entryPosition; + if (manualEntryPosition == null) + { + entryPosition = new Vector2(colAccumulator, rowAccumulator); + } + else + { + entryPosition = (Vector2) manualEntryPosition; + } + EntryData newData = new EntryData + { + id = entry._id, + cardPosition = entryPosition, + sprite = body.Config.ShipLog.spriteFolder == null? null : GetSprite(entry._id, body) + }; + entry.SetSprite(newData.sprite == null? manager._shipLogLibrary.defaultEntrySprite : newData.sprite); + manager._entryDataDict.Add(entry._id, newData); + int index = manager._entryList.IndexOf(entry); + if (index < manager._entryList.Count - 2 && manager._entryList[index + 1]._astroObjectID != entry._astroObjectID) + { + rowAccumulator += step; + colAccumulator = 0; + } + else + { + colAccumulator += step; + } + } + } + } public static void Init() { diff --git a/NewHorizons/External/IPlanetConfig.cs b/NewHorizons/External/IPlanetConfig.cs index 56d12c12..0922c4fa 100644 --- a/NewHorizons/External/IPlanetConfig.cs +++ b/NewHorizons/External/IPlanetConfig.cs @@ -20,6 +20,7 @@ namespace NewHorizons.External StarModule Star { get; } FocalPointModule FocalPoint { get; } PropModule Props { get; } + ShipLogModule ShipLog { get; } SpawnModule Spawn { get; } SignalModule Signal { get; } SingularityModule Singularity { get; } diff --git a/NewHorizons/External/PlanetConfig.cs b/NewHorizons/External/PlanetConfig.cs index 4724d3a2..7539d0cf 100644 --- a/NewHorizons/External/PlanetConfig.cs +++ b/NewHorizons/External/PlanetConfig.cs @@ -23,6 +23,7 @@ namespace NewHorizons.External public StarModule Star { get; set; } public FocalPointModule FocalPoint { get; set; } public PropModule Props { get; set; } + public ShipLogModule ShipLog { get; set; } public SpawnModule Spawn { get; set; } public SignalModule Signal { get; set; } public SingularityModule Singularity { get; set; } diff --git a/NewHorizons/External/ShipLogModule.cs b/NewHorizons/External/ShipLogModule.cs new file mode 100644 index 00000000..a805e303 --- /dev/null +++ b/NewHorizons/External/ShipLogModule.cs @@ -0,0 +1,34 @@ +using NewHorizons.Utility; + +namespace NewHorizons.External +{ + public class ShipLogModule : Module + { + public string xmlFile; + public string spriteFolder; + public MapMode mapMode; + public CuriosityColor[] curiosities; + public EntryPosition[] positions; + + public class MapMode + { + public string revealedSprite; + public string outlineSprite; + public float scale; + public bool invisibleWhenHidden; + } + + public class CuriosityColor + { + public string id; + public MColor color; + public MColor highlightColor; + } + + public class EntryPosition + { + public string id; + public MVector2 position; + } + } +} \ No newline at end of file diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index e2c01d43..769aeaff 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -12,12 +12,13 @@ using NewHorizons.Utility; using OWML.Common; using OWML.ModHelper; using OWML.Utils; -using PacificEngine.OW_CommonResources.Game.Player; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; +using Epic.OnlineServices; +using PacificEngine.OW_CommonResources.Game.Player; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; @@ -50,6 +51,35 @@ namespace NewHorizons return new NewHorizonsApi(); } + private void OnGUI() + { + GUILayout.BeginArea(new Rect(0, 0, 100, 100)); + bool learnPress = GUILayout.Button("Learn Thing"); + if (learnPress) + { + Locator.GetShipLogManager().RevealFact("COOL_ROCK_R1", false, true); + Locator.GetShipLogManager().RevealFact("COOL_ROCK_R2", false, true); + Locator.GetShipLogManager().RevealFact("UNCOOL_ROCK_R1", false, true); + Locator.GetShipLogManager().RevealFact("UNCOOL_ROCK_R2", false, true); + Locator.GetShipLogManager().RevealFact("UNCOOL_ROCK_R3", false, true); + + } + + bool iRemem = GUILayout.Button("I Remem"); + if (iRemem) + { + Data.knowAllFacts = true; + Data.knowAllRumors = true; + } + bool forgorPress = GUILayout.Button("I Forgor"); + if (forgorPress) + { + Data.knowAllFacts = false; + Data.knowAllRumors = false; + } + GUILayout.EndArea(); + } + public void Start() { SceneManager.sceneLoaded += OnSceneLoaded; diff --git a/NewHorizons/NewHorizons.csproj.user b/NewHorizons/NewHorizons.csproj.user index 4a7efd53..9aeb566d 100644 --- a/NewHorizons/NewHorizons.csproj.user +++ b/NewHorizons/NewHorizons.csproj.user @@ -2,7 +2,7 @@ ProjectFiles - $(AppData)\OuterWildsModManager\OWML\Mods\xen.NewHorizons - $(AppData)\OuterWildsModManager\OWML\Mods + $(AppData)\OuterWildsModManager\OWML\Mods\xen.NewHorizons + $(AppData)\OuterWildsModManager\OWML\Mods - + \ No newline at end of file diff --git a/NewHorizons/Tools/Patches.cs b/NewHorizons/Tools/Patches.cs index 4ee1062b..ee9522a4 100644 --- a/NewHorizons/Tools/Patches.cs +++ b/NewHorizons/Tools/Patches.cs @@ -5,11 +5,17 @@ using NewHorizons.External; using OWML.Common; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Xml.Linq; +using Harmony; +using NewHorizons.Utility; +using OWML.Utils; using UnityEngine; using Logger = NewHorizons.Utility.Logger; +using Object = UnityEngine.Object; namespace NewHorizons.Tools { @@ -41,9 +47,11 @@ namespace NewHorizons.Tools var playerDataLearnFrequency = typeof(PlayerData).GetMethod("LearnFrequency"); Main.Instance.ModHelper.HarmonyHelper.AddPrefix(playerDataLearnFrequency, typeof(Patches), nameof(Patches.OnPlayerDataLearnFrequency)); var playerDataKnowsMultipleFrequencies = typeof(PlayerData).GetMethod("KnowsMultipleFrequencies"); - Main.Instance.ModHelper.HarmonyHelper.AddPrefix(playerDataKnowsMultipleFrequencies, typeof(Patches), nameof(Patches.OnPlayerDataKnowsMultipleFrequencies)); + Main.Instance.ModHelper.HarmonyHelper.AddPrefix(playerDataKnowsMultipleFrequencies, typeof(Patches), nameof(Patches.OnPlayerDataKnowsMultipleFrequencies)); var playerDataResetGame = typeof(PlayerData).GetMethod("ResetGame"); Main.Instance.ModHelper.HarmonyHelper.AddPostfix(playerDataResetGame, typeof(Patches), nameof(Patches.OnPlayerDataResetGame)); + var playerDataGetNewlyRevealedFactIDs = typeof(PlayerData).GetMethod("GetNewlyRevealedFactIDs"); + Main.Instance.ModHelper.HarmonyHelper.AddPostfix(playerDataGetNewlyRevealedFactIDs, typeof(Patches), nameof(Patches.OnPlayerDataGetNewlyRevealedFactIDsComplete)); Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Start", typeof(Patches), nameof(Patches.OnBlackHoleVolumeStart)); Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Awake", typeof(Patches), nameof(Patches.OnWhiteHoleVolumeAwake)); @@ -51,12 +59,30 @@ namespace NewHorizons.Tools Main.Instance.ModHelper.HarmonyHelper.AddPrefix("IsLaunched", typeof(Patches), nameof(Patches.OnSurveyorProbeIsLaunched)); Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Update", typeof(Patches), nameof(Patches.OnShipLogControllerUpdate)); + + Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Awake", typeof(Patches), nameof(Patches.OnShipLogManagerAwake)); + Main.Instance.ModHelper.HarmonyHelper.AddPrefix("IsFactRevealed", typeof(Patches), nameof(Patches.OnShipLogManagerIsFactRevealed)); + Main.Instance.ModHelper.HarmonyHelper.AddPrefix("CheckForCompletionAchievement", typeof(Patches), nameof(Patches.OnShipLogManagerCheckForCompletionAchievement)); + Main.Instance.ModHelper.HarmonyHelper.AddPrefix("RevealFact", typeof(Patches), nameof(Patches.OnShipLogManagerRevealFact)); + + Main.Instance.ModHelper.HarmonyHelper.AddPrefix("GetCuriosityColor", typeof(Patches), nameof(Patches.OnUIStyleManagerGetCuriosityColor)); + + Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Initialize", typeof(Patches), nameof(Patches.OnShipLogMapModeInitialize)); + + Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Awake", typeof(Patches), nameof(Patches.DisableShipLogSandFunnel)); + Main.Instance.ModHelper.HarmonyHelper.AddPrefix("UpdateState", typeof(Patches), nameof(Patches.DisableShipLogSandFunnel)); + + Main.Instance.ModHelper.HarmonyHelper.AddPrefix("GetName", typeof(Patches), nameof(Patches.OnShipLogAstroObjectGetName)); Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Update", typeof(Patches), nameof(Patches.OnShipCockpitControllerUpdate)); // Postfixes Main.Instance.ModHelper.HarmonyHelper.AddPostfix("Awake", typeof(Patches), nameof(Patches.OnMapControllerAwake)); Main.Instance.ModHelper.HarmonyHelper.AddPostfix("EnterMode", typeof(Patches), nameof(Patches.OnShipLogMapModeEnterMode)); + + Main.Instance.ModHelper.HarmonyHelper.AddPostfix("Awake", typeof(Patches), nameof(Patches.OnShipLogManagerAwakeComplete)); + + Main.Instance.ModHelper.HarmonyHelper.AddPostfix("Initialize", typeof(Patches), nameof(Patches.OnShipLogMapModeInitializeComplete)); } public static bool GetHUDDisplayName(ReferenceFrame __instance, ref string __result) @@ -398,6 +424,155 @@ namespace NewHorizons.Tools } return true; } + + #region ShipLog + public static void OnShipLogManagerAwake(ShipLogManager __instance) + { + Logger.Log("Beginning Ship Log Generation For: " + Main.Instance.CurrentStarSystem, Logger.LogType.Log); + if (Main.Instance.CurrentStarSystem != "SolarSystem") + { + __instance._shipLogXmlAssets = new TextAsset[] {}; + foreach (ShipLogEntryLocation logEntryLocation in GameObject.FindObjectsOfType()) + { + logEntryLocation._initialized = true; + } + } + foreach (NewHorizonsBody body in Main.BodyDict[Main.Instance.CurrentStarSystem]) + { + if (body.Config.ShipLog?.curiosities != null) + { + ShipLogBuilder.AddCuriosityColors(body.Config.ShipLog.curiosities); + } + } + foreach (NewHorizonsBody body in Main.BodyDict[Main.Instance.CurrentStarSystem]) + { + if (body.Config.ShipLog?.xmlFile != null) + { + ShipLogBuilder.AddAstroBodyToShipLog(__instance, body); + } + } + } + + public static void OnShipLogManagerAwakeComplete(ShipLogManager __instance) + { + ShipLogBuilder.GenerateEntryData(__instance); + for (var i = 0; i < __instance._entryList.Count; i++) + { + ShipLogEntry logEntry = __instance._entryList[i]; + ShipLogBuilder.UpdateEntryCuriosity(ref logEntry); + } + Logger.Log("Ship Log Generation Complete For: " + Main.Instance.CurrentStarSystem, Logger.LogType.Log); + } + + public static bool OnShipLogManagerIsFactRevealed(ShipLogManager __instance, ref bool __result, string __0) + { + if (Main.Instance.CurrentStarSystem == "SolarSystem") + { + return true; + } + else + { + if (__instance._factDict.ContainsKey(__0) == false) + { + __result = false; + return false; + } + else + { + return true; + } + } + } + + public static bool OnShipLogManagerCheckForCompletionAchievement() + { + return Main.Instance.CurrentStarSystem == "SolarSystem"; + } + + public static bool OnShipLogManagerRevealFact(string __0) + { + if (Main.Instance.CurrentStarSystem != "SolarSystem" && __0 == "TH_VILLAGE_X1") + { + return false; + } + + return true; + } + + public static bool OnUIStyleManagerGetCuriosityColor(UIStyleManager __instance, CuriosityName __0, bool __1, ref Color __result) + { + if (Main.Instance.CurrentStarSystem == "SolarSystem") + { + return true; + } + else + { + __result = ShipLogBuilder.GetCuriosityColor(__0, __1, __instance._neutralColor, __instance._neutralHighlight); + return false; + } + } + + private static void DeleteDetail(string name) + { + Object.Destroy(GameObject.Find(ShipLogBuilder.PAN_ROOT_PATH + "/" + name)); + } + + public static void OnShipLogMapModeInitialize(ShipLogMapMode __instance) + { + + } + + public static void OnShipLogMapModeInitializeComplete(ShipLogMapMode __instance) + { + if (Main.Instance.CurrentStarSystem != "SolarSystem") + { + GameObject panRoot = GameObject.Find(ShipLogBuilder.PAN_ROOT_PATH); + GameObject sunObject = GameObject.Find(ShipLogBuilder.PAN_ROOT_PATH + "/Sun"); + ShipLogAstroObject[][] navMatrix = ShipLogBuilder.ConstructMapMode(Main.Instance.CurrentStarSystem, panRoot, sunObject.layer); + if (navMatrix.Length <= 1) + { + Logger.LogWarning("No planets suitable for map mode found! Defaulting to vanilla menu (expect weirdness!)."); + } + else + { + __instance._astroObjects = navMatrix; + __instance._startingAstroObjectID = navMatrix[1][0].GetID(); + List delete = SearchUtilities.GetAllChildren(panRoot).Where(g => g.name.Contains("_ShipLog") == false).ToList(); + foreach (GameObject gameObject in delete) + { + DeleteDetail(gameObject.name); + } + + __instance._sandFunnel = __instance.gameObject.AddComponent(); + } + } + Logger.Log("Map Mode Construction Complete", Logger.LogType.Log); + } + + public static bool OnShipLogAstroObjectGetName(ShipLogAstroObject __instance, ref string __result) + { + if (Main.Instance.CurrentStarSystem == "SolarSystem") + { + return true; + } + else + { + __result = ShipLogBuilder.GetAstroBodyShipLogName(__instance.GetID()); + return false; + } + } + + public static bool DisableShipLogSandFunnel() + { + return Main.Instance.CurrentStarSystem == "SolarSystem"; + } + + public static void OnPlayerDataGetNewlyRevealedFactIDsComplete(ref List __result) + { + ShipLogManager manager = Locator.GetShipLogManager(); + __result = __result.Where(e => manager.GetFact(e) != null).ToList(); + } + # endregion public static void OnShipLogMapModeEnterMode(ShipLogMapMode __instance) { diff --git a/NewHorizons/Utility/MVector2.cs b/NewHorizons/Utility/MVector2.cs new file mode 100644 index 00000000..4d6ccf84 --- /dev/null +++ b/NewHorizons/Utility/MVector2.cs @@ -0,0 +1,26 @@ +using UnityEngine; + +namespace NewHorizons.Utility +{ + public class MVector2 + { + public MVector2(float x, float y) + { + X = x; + Y = y; + } + + public float X { get; } + public float Y { get; } + + public static implicit operator MVector2(Vector2 vec) + { + return new MVector2(vec.x, vec.y); + } + + public static implicit operator Vector2(MVector2 vec) + { + return new Vector2(vec.X, vec.Y); + } + } +} \ No newline at end of file diff --git a/NewHorizons/Utility/SearchUtilities.cs b/NewHorizons/Utility/SearchUtilities.cs index 7eee7333..901851b2 100644 --- a/NewHorizons/Utility/SearchUtilities.cs +++ b/NewHorizons/Utility/SearchUtilities.cs @@ -72,5 +72,15 @@ namespace NewHorizons.Utility return null; } + + public static List GetAllChildren(GameObject parent) + { + List children = new List(); + foreach (Transform child in parent.transform) + { + children.Add(child.gameObject); + } + return children; + } } } From 1673de69acb7c8efd090aad4bae3bff739113a58 Mon Sep 17 00:00:00 2001 From: Ben C Date: Sat, 5 Feb 2022 19:32:32 -0500 Subject: [PATCH 02/50] Added XML Schemas - Improved Map mode - Added XML Schemas for Dialogue and Ship Log - Added New Properties to JSON Schema - Added Reveal Props --- NewHorizons/Builder/General/ShipLogBuilder.cs | 828 ++++++++++-------- NewHorizons/Builder/Props/PropBuildManager.cs | 15 + NewHorizons/Builder/Props/SignalBuilder.cs | 1 + NewHorizons/External/PropModule.cs | 19 + NewHorizons/External/ShipLogModule.cs | 5 +- NewHorizons/External/SignalModule.cs | 1 + NewHorizons/Tools/Patches.cs | 51 +- NewHorizons/dialogue_schema.xsd | 39 + NewHorizons/schema.json | 246 +++++- NewHorizons/shiplog_schema.xsd | 38 + 10 files changed, 869 insertions(+), 374 deletions(-) create mode 100644 NewHorizons/dialogue_schema.xsd create mode 100644 NewHorizons/shiplog_schema.xsd diff --git a/NewHorizons/Builder/General/ShipLogBuilder.cs b/NewHorizons/Builder/General/ShipLogBuilder.cs index 2fceed15..ac764a21 100644 --- a/NewHorizons/Builder/General/ShipLogBuilder.cs +++ b/NewHorizons/Builder/General/ShipLogBuilder.cs @@ -21,417 +21,549 @@ namespace NewHorizons.Builder.General public static readonly string PAN_ROOT_PATH = "Ship_Body/Module_Cabin/Systems_Cabin/ShipLogPivot/ShipLog/ShipLogPivot/ShipLogCanvas/MapMode/ScaleRoot/PanRoot"; public static ShipLogStarChartMode ShipLogStarChartMode; - private static Dictionary curiosityColors = new Dictionary(); - private static Dictionary curiosityHighlightColors = new Dictionary(); - private static Dictionary rawNameToCuriosityName = new Dictionary(); - private static Dictionary entryIdToRawName = new Dictionary(); private static Dictionary astroIdToBody = new Dictionary(); - - private class MapModeObject - { - public int x; - public int y; - public int branch_width; - public int branch_height; - public int level; - public NewHorizonsBody mainBody; - public ShipLogAstroObject astroObject; - public List children; - public MapModeObject parent; - public void increment_width() - { - branch_width++; - parent?.increment_width(); - } - public void increment_height() - { - branch_height++; - parent?.increment_height(); - } - } - - public static string GetAstroBodyShipLogName(string id) - { - return astroIdToBody[id].Config.Name; - } - - public static ShipLogAstroObject[][] ConstructMapMode(string systemName, GameObject transformParent, int layer) - { - MapModeObject rootObject = MakePrimaryNode(systemName); - if (rootObject.mainBody != null) - { - CreateAllNodes(ref rootObject, transformParent, layer); - } - - const int maxAmount = 20; - ShipLogAstroObject[][] navMatrix = new ShipLogAstroObject[maxAmount][]; - for (int i = 0; i < maxAmount; i++) - { - navMatrix[i] = new ShipLogAstroObject[maxAmount]; - } - CreateNavigationMatrix(rootObject, ref navMatrix); - navMatrix = navMatrix.Where(a => a.Count(c => c != null) > 0).Prepend(new ShipLogAstroObject[1]).ToArray(); - for (var index = 0; index < navMatrix.Length; index++) - { - navMatrix[index] = navMatrix[index].Where(a => a != null).ToArray(); - } - return navMatrix; - } - - private static void CreateNavigationMatrix(MapModeObject root, ref ShipLogAstroObject[][] navMatrix) - { - if (root.astroObject != null) - { - navMatrix[root.y][root.x] = root.astroObject; - } - foreach (MapModeObject child in root.children) - { - CreateNavigationMatrix(child, ref navMatrix); - } - } - - private static void CreateAllNodes(ref MapModeObject parentNode, GameObject parent, int layer) - { - CreateNode(ref parentNode, parent, layer); - for (var i = 0; i < parentNode.children.Count; i++) - { - MapModeObject child = parentNode.children[i]; - CreateAllNodes(ref child, parent, layer); - parentNode.children[i] = child; - } - } - - private static GameObject CreateImage(GameObject nodeGO, IModAssets assets, string imagePath, string name, int layer) - { - GameObject newImageGO = new GameObject(name); - newImageGO.layer = layer; - newImageGO.transform.SetParent(nodeGO.transform); - - RectTransform transform = newImageGO.AddComponent(); - transform.localPosition = Vector3.zero; - transform.localRotation = Quaternion.identity; - transform.localScale = Vector3.one; - - Image newImage = newImageGO.AddComponent(); - if (imagePath == "DEFAULT") - { - newImage.sprite = Locator.GetShipLogManager()._shipLogLibrary.defaultEntrySprite; - } - else - { - Texture2D newTexture = assets.GetTexture(imagePath); - Rect rect = new Rect(0, 0, newTexture.width, newTexture.height); - Vector2 pivot = new Vector2(newTexture.width / 2, newTexture.height / 2); - newImage.sprite = Sprite.Create(newTexture, rect, pivot); - } - return newImageGO; - } - public static T KeyByValue(this Dictionary dict, W val) + private static NewHorizonsBody GetConfigFromEntry(ShipLogEntry entry) { - T key = default; - foreach (KeyValuePair pair in dict) + return astroIdToBody[entry._astroObjectID]; + } + + #region Map Mode + + public class MapModeBuilder + { + private class MapModeObject { - if (EqualityComparer.Default.Equals(pair.Value, val)) + public int x; + public int y; + public int branch_width; + public int branch_height; + public int level; + public NewHorizonsBody mainBody; + public ShipLogAstroObject astroObject; + public List children; + public MapModeObject parent; + public MapModeObject lastSibling; + public void increment_width() { - key = pair.Key; - break; + branch_width++; + parent?.increment_width(); + } + public void increment_height() + { + branch_height++; + parent?.increment_height(); } } - return key; - } - private static string GetAstroObjectId(NewHorizonsBody body) - { - return KeyByValue(astroIdToBody, body); - } - - private static void CreateAstroObject(GameObject nodeGO, ref MapModeObject node, GameObject referenceUnviewedSprite, int layer) - { - const float unviewedIconOffset = 15; - ShipLogAstroObject astroObject = nodeGO.AddComponent(); - astroObject._id = GetAstroObjectId(node.mainBody); - string imagePath = node.mainBody.Config.ShipLog?.mapMode?.revealedSprite ?? "DEFAULT"; - string outlinePath = node.mainBody.Config.ShipLog?.mapMode?.outlineSprite ?? imagePath; - astroObject._imageObj = CreateImage(nodeGO, node.mainBody.Mod.Assets, imagePath, "Image", layer); - astroObject._outlineObj = CreateImage(nodeGO, node.mainBody.Mod.Assets, outlinePath, "Outline", layer); - astroObject._unviewedObj = GameObject.Instantiate(referenceUnviewedSprite, nodeGO.transform, false); - astroObject._invisibleWhenHidden = node.mainBody.Config.ShipLog?.mapMode?.invisibleWhenHidden ?? false; - Rect imageRect = astroObject._imageObj.GetComponent().rect; - astroObject._unviewedObj.transform.localPosition = new Vector3(imageRect.width / 2 + unviewedIconOffset, imageRect.height / 2 + unviewedIconOffset, 0); - node.astroObject = astroObject; - } - - private static void CreateNode(ref MapModeObject node, GameObject parent, int layer) - { - const float padding = 250f; - - GameObject newNodeGO = new GameObject(node.mainBody.Config.Name + "_ShipLog"); - newNodeGO.layer = layer; - newNodeGO.transform.SetParent(parent.transform); - - RectTransform transform = newNodeGO.AddComponent(); - float scale = node.mainBody.Config.ShipLog?.mapMode?.scale?? 1f; - scale = scale <= 0 ? 1f : scale; - transform.localPosition = new Vector3(node.x * padding, node.y * padding, 0); - transform.localRotation = Quaternion.identity; - transform.localScale = Vector3.one * scale; - - if (node.mainBody.Config.ShipLog?.xmlFile == null) + public static string GetAstroBodyShipLogName(string id) { - Image newImage = newNodeGO.AddComponent(); - string imagePath = node.mainBody.Config.ShipLog?.mapMode?.revealedSprite ?? "DEFAULT"; + if (astroIdToBody.ContainsKey(id)) + { + return astroIdToBody[id].Config.Name; + } + else + { + return id; + } + } + + public static ShipLogAstroObject[][] ConstructMapMode(string systemName, GameObject transformParent, int layer) + { + MapModeObject rootObject = MakePrimaryNode(systemName); + if (rootObject.mainBody != null) + { + CreateAllNodes(ref rootObject, transformParent, layer); + } + + const int maxAmount = 20; + ShipLogAstroObject[][] navMatrix = new ShipLogAstroObject[maxAmount][]; + for (int i = 0; i < maxAmount; i++) + { + navMatrix[i] = new ShipLogAstroObject[maxAmount]; + } + + CreateNavigationMatrix(rootObject, ref navMatrix); + navMatrix = navMatrix.Where(a => a.Count(c => c != null) > 0).Prepend(new ShipLogAstroObject[1]).ToArray(); + for (var index = 0; index < navMatrix.Length; index++) + { + navMatrix[index] = navMatrix[index].Where(a => a != null).ToArray(); + } + return navMatrix; + } + + private static void CreateNavigationMatrix(MapModeObject root, ref ShipLogAstroObject[][] navMatrix) + { + if (root.astroObject != null) + { + navMatrix[root.y][root.x] = root.astroObject; + } + foreach (MapModeObject child in root.children) + { + CreateNavigationMatrix(child, ref navMatrix); + } + } + + private static void CreateAllNodes(ref MapModeObject parentNode, GameObject parent, int layer) + { + CreateNode(ref parentNode, parent, layer); + for (var i = 0; i < parentNode.children.Count; i++) + { + MapModeObject child = parentNode.children[i]; + CreateAllNodes(ref child, parent, layer); + parentNode.children[i] = child; + } + } + + private static GameObject CreateImage(GameObject nodeGO, IModAssets assets, string imagePath, string name, int layer) + { + GameObject newImageGO = new GameObject(name); + newImageGO.layer = layer; + newImageGO.transform.SetParent(nodeGO.transform); + + RectTransform transform = newImageGO.AddComponent(); + transform.localPosition = Vector3.zero; + transform.localRotation = Quaternion.identity; + transform.localScale = Vector3.one; + + Image newImage = newImageGO.AddComponent(); if (imagePath == "DEFAULT") { newImage.sprite = Locator.GetShipLogManager()._shipLogLibrary.defaultEntrySprite; } else { - Texture2D newTexture = node.mainBody.Mod.Assets.GetTexture(imagePath); + Texture2D newTexture = assets.GetTexture(imagePath); Rect rect = new Rect(0, 0, newTexture.width, newTexture.height); Vector2 pivot = new Vector2(newTexture.width / 2, newTexture.height / 2); newImage.sprite = Sprite.Create(newTexture, rect, pivot); } + return newImageGO; } - else + + public static T KeyByValue(Dictionary dict, W val) { - CreateAstroObject(newNodeGO, ref node, GameObject.Find(PAN_ROOT_PATH + "/TimberHearth/UnviewedIcon"), layer); - } - } - - private static MapModeObject MakePrimaryNode(string systemName) - { - foreach (NewHorizonsBody body in Main.BodyDict[systemName]) - { - if (!body.Config.Base.CenterOfSolarSystem) continue; - MapModeObject newNode = new MapModeObject + T key = default; + foreach (KeyValuePair pair in dict) { - mainBody = body, - level = 0, - x = 0, - y = 0 - }; - newNode.children = MakeChildrenNodes(systemName, newNode); - return newNode; + if (EqualityComparer.Default.Equals(pair.Value, val)) + { + key = pair.Key; + break; + } + } + return key; } - Logger.LogError("Couldn't find center of system!"); - return new MapModeObject(); - } - private static List MakeChildrenNodes(string systemName, MapModeObject parent) - { - List children = new List(); - int newX = parent.x; - int newY = parent.y; - foreach (NewHorizonsBody body in Main.BodyDict[systemName]) + private static string GetAstroObjectId(NewHorizonsBody body) { - if (body.Config.Orbit.PrimaryBody == parent.mainBody.Config.Name) + if (astroIdToBody.ContainsValue(body)) { - int newLevel = parent.level + 1; - bool even = newLevel % 2 == 0; - newX = even ? newX : newX + 1; - newY = even ? newY + 1 : newY; - MapModeObject newNode = new MapModeObject() + return KeyByValue(astroIdToBody, body); + } + else + { + return body.Config.Name; + } + } + + private static void CreateShipLogAstroObject(GameObject nodeGO, ref MapModeObject node, GameObject referenceUnviewedSprite, int layer) + { + const float unviewedIconOffset = 15; + ShipLogAstroObject astroObject = nodeGO.AddComponent(); + astroObject._id = GetAstroObjectId(node.mainBody); + string imagePath = node.mainBody.Config.ShipLog?.mapMode?.revealedSprite ?? "DEFAULT"; + string outlinePath = node.mainBody.Config.ShipLog?.mapMode?.outlineSprite ?? imagePath; + astroObject._imageObj = CreateImage(nodeGO, node.mainBody.Mod.Assets, imagePath, "Image", layer); + astroObject._outlineObj = CreateImage(nodeGO, node.mainBody.Mod.Assets, outlinePath, "Outline", layer); + astroObject._unviewedObj = GameObject.Instantiate(referenceUnviewedSprite, nodeGO.transform, false); + astroObject._invisibleWhenHidden = node.mainBody.Config.ShipLog?.mapMode?.invisibleWhenHidden ?? false; + Rect imageRect = astroObject._imageObj.GetComponent().rect; + astroObject._unviewedObj.transform.localPosition = new Vector3(imageRect.width / 2 + unviewedIconOffset, imageRect.height / 2 + unviewedIconOffset, 0); + node.astroObject = astroObject; + } + + private static void ConnectNodes(MapModeObject from, MapModeObject to) + { + + } + + private static void CreateNode(ref MapModeObject node, GameObject parent, int layer) + { + const float padding = 250f; + + GameObject newNodeGO = new GameObject(node.mainBody.Config.Name + "_ShipLog"); + newNodeGO.layer = layer; + newNodeGO.transform.SetParent(parent.transform); + + RectTransform transform = newNodeGO.AddComponent(); + float scale = node.mainBody.Config.ShipLog?.mapMode?.scale?? 1f; + Vector2 position = Vector2.zero; + if (node.lastSibling != null) + { + ShipLogAstroObject lastAstroObject = node.lastSibling.astroObject; + Vector3 lastPosition = lastAstroObject.transform.localPosition; + position = lastPosition; + float extraDistance = (node.mainBody.Config.ShipLog?.mapMode?.offset ?? 0f) * 100; + if (node.level % 2 == 0) + { + position.y += padding * (node.y - node.lastSibling.y) + extraDistance; + } + else + { + position.x += padding * (node.x - node.lastSibling.x) + extraDistance; + } + } + transform.localPosition = new Vector3(position.x, position.y, 0); + transform.localRotation = Quaternion.identity; + transform.localScale = Vector3.one * scale; + CreateShipLogAstroObject(newNodeGO, ref node, GameObject.Find(PAN_ROOT_PATH + "/TimberHearth/UnviewedIcon"), layer); + } + + private static MapModeObject MakePrimaryNode(string systemName) + { + foreach (NewHorizonsBody body in Main.BodyDict[systemName].Where(b => b.Config.Base.CenterOfSolarSystem)) + { + MapModeObject newNode = new MapModeObject { mainBody = body, - level = newLevel, - x = newX, - y = newY, - parent=parent + level = 0, + x = 0, + y = 0 }; newNode.children = MakeChildrenNodes(systemName, newNode); - if (even) - { - newY += newNode.branch_height; - parent.increment_height(); - } - else - { - newX += newNode.branch_width; - parent.increment_width(); - } - children.Add(newNode); + return newNode; } + Logger.LogError("Couldn't find center of system!"); + return new MapModeObject(); } - return children; - } - public static void AddCuriosityColors(ShipLogModule.CuriosityColor[] newColors) - { - foreach (ShipLogModule.CuriosityColor newColor in newColors) + private static List MakeChildrenNodes(string systemName, MapModeObject parent) { - if (rawNameToCuriosityName.ContainsKey(newColor.id) == false) + List children = new List(); + int newX = parent.x; + int newY = parent.y; + int newLevel = parent.level + 1; + MapModeObject lastSibling = parent; + foreach (NewHorizonsBody body in Main.BodyDict[systemName].Where(b => b.Config.ShipLog?.mapMode?.remove == false && b.Config.Orbit.PrimaryBody == parent.mainBody.Config.Name)) { - CuriosityName newName = (CuriosityName) 8 + rawNameToCuriosityName.Count; - rawNameToCuriosityName.Add(newColor.id, newName); - curiosityColors.Add(newName, newColor.color.ToColor()); - curiosityHighlightColors.Add(newName, newColor.highlightColor.ToColor()); + if (body.Config.Orbit.PrimaryBody == parent.mainBody.Config.Name) + { + bool even = newLevel % 2 == 0; + newX = even ? newX : newX + 1; + newY = even ? newY + 1 : newY; + MapModeObject newNode = new MapModeObject() + { + mainBody = body, + level = newLevel, + x = newX, + y = newY, + parent = parent, + lastSibling = lastSibling + }; + newNode.children = MakeChildrenNodes(systemName, newNode); + if (even) + { + newY += newNode.branch_height; + parent.increment_height(); + } + else + { + newX += newNode.branch_width; + parent.increment_width(); + } + lastSibling = newNode; + children.Add(newNode); + } } + return children; } } + #endregion - public static Color GetCuriosityColor(CuriosityName curiosityName, bool highlighted, Color defaultColor, Color defaultHighlight) + #region Rumor Mode + public static class RumorModeBuilder { - if (curiosityColors.ContainsKey(curiosityName) && curiosityHighlightColors.ContainsKey(curiosityName)) - { - return (highlighted ? curiosityHighlightColors : curiosityColors)[curiosityName]; - } - else - { - return highlighted? defaultHighlight : defaultColor; - } - } + private static Dictionary curiosityColors = new Dictionary(); + private static Dictionary curiosityHighlightColors = new Dictionary(); + private static Dictionary rawNameToCuriosityName = new Dictionary(); + private static Dictionary entryIdToRawName = new Dictionary(); - public static void AddAstroBodyToShipLog(ShipLogManager manager, NewHorizonsBody body) - { - string systemName = body.Config.StarSystem; - XElement astroBodyFile = XElement.Load(Main.Instance.ModHelper.Manifest.ModFolderPath + body.Config.ShipLog.xmlFile); - XElement astroBodyId = astroBodyFile.Element("ID"); - if (astroBodyId == null) + public static void AddCuriosityColors(ShipLogModule.CuriosityColor[] newColors) { - Logger.LogError("Failed to load ship log for " + systemName + "!"); - } - else - { - astroBodyId.SetValue(systemName + "/" + astroBodyId.Value); - foreach (XElement entryElement in astroBodyFile.DescendantsAndSelf("Entry")) + foreach (ShipLogModule.CuriosityColor newColor in newColors) { - XElement curiosityName = entryElement.Element("Curiosity"); - XElement id = entryElement.Element("ID"); - if (curiosityName != null && id != null && entryIdToRawName.ContainsKey(id.Value) == false) + if (rawNameToCuriosityName.ContainsKey(newColor.id) == false) { - entryIdToRawName.Add(id.Value, curiosityName.Value); + CuriosityName newName = (CuriosityName) 8 + rawNameToCuriosityName.Count; + rawNameToCuriosityName.Add(newColor.id, newName); + curiosityColors.Add(newName, newColor.color.ToColor()); + curiosityHighlightColors.Add(newName, newColor.highlightColor.ToColor()); } - AddTranslation(entryElement); } - TextAsset newAsset = new TextAsset(astroBodyFile.ToString()); - List newBodies = new List(manager._shipLogXmlAssets) {newAsset}; - manager._shipLogXmlAssets = newBodies.ToArray(); - if (astroIdToBody.ContainsKey(astroBodyId.Value) == false) + } + + public static Color GetCuriosityColor(CuriosityName curiosityName, bool highlighted, Color defaultColor, Color defaultHighlight) + { + if (curiosityColors.ContainsKey(curiosityName) && curiosityHighlightColors.ContainsKey(curiosityName)) { - astroIdToBody.Add(astroBodyId.Value, body); + return (highlighted ? curiosityHighlightColors : curiosityColors)[curiosityName]; } - } - } - - private static void AddTranslation(XElement entry) - { - Dictionary table = TextTranslation.Get().m_table.theShipLogTable; - XElement nameElement = entry.Element("Name"); - if (nameElement != null) - { - string name = nameElement.Value; - table[name] = name; - foreach (XElement rumorFact in entry.Elements("RumorFact")) + else { - XElement rumorName = rumorFact.Element("RumorName"); - if (rumorName != null) - { - table[rumorName.Value] = rumorName.Value; - } - - XElement rumorText = rumorFact.Element("Text"); - if (rumorText != null) - { - table[name + rumorText.Value] = rumorText.Value; - } + return highlighted? defaultHighlight : defaultColor; } - foreach (XElement exploreFact in entry.Elements("ExploreFact")) + } + + public static void AddBodyToShipLog(ShipLogManager manager, NewHorizonsBody body) + { + string systemName = body.Config.StarSystem; + XElement astroBodyFile = XElement.Load(Main.Instance.ModHelper.Manifest.ModFolderPath + body.Config.ShipLog.xmlFile); + XElement astroBodyId = astroBodyFile.Element("ID"); + if (astroBodyId == null) { - XElement exploreText = exploreFact.Element("Text"); - if (exploreText != null) - { - table[name + exploreText.Value] = exploreText.Value; - } + Logger.LogError("Failed to load ship logs for " + systemName + "!"); } - } - } - - public static void UpdateEntryCuriosity(ref ShipLogEntry entry) - { - if (entryIdToRawName.ContainsKey(entry._id)) - { - entry._curiosity = rawNameToCuriosityName[entryIdToRawName[entry._id]]; - } - } - - private static Sprite GetSprite(string entryId, NewHorizonsBody body) - { - IModAssets assets = body.Mod.Assets; - string path = body.Config.ShipLog.spriteFolder + "/" + entryId + ".png"; - if (File.Exists(Main.Instance.ModHelper.Manifest.ModFolderPath + path)) - { - Texture2D newTexture = assets.GetTexture(path); - Rect rect = new Rect(0, 0, newTexture.width, newTexture.height); - Vector2 pivot = new Vector2(newTexture.width / 2, newTexture.height / 2); - return Sprite.Create(newTexture, rect, pivot); - } - else - { - return Locator.GetShipLogManager()._shipLogLibrary.defaultEntrySprite; - } - } - - private static NewHorizonsBody GetConfigFromEntry(ShipLogEntry entry) - { - return astroIdToBody[entry._astroObjectID]; - } - - private static Vector2? FindPosition(string entryId, ShipLogModule config) - { - if (config.positions == null) return null; - foreach (ShipLogModule.EntryPosition position in config.positions) - { - if (position.id == entryId) + else { - return position.position; + astroBodyId.SetValue(systemName + "/" + astroBodyId.Value); + foreach (XElement entryElement in astroBodyFile.DescendantsAndSelf("Entry")) + { + XElement curiosityName = entryElement.Element("Curiosity"); + XElement id = entryElement.Element("ID"); + if (curiosityName != null && id != null && entryIdToRawName.ContainsKey(id.Value) == false) + { + entryIdToRawName.Add(id.Value, curiosityName.Value); + } + AddTranslation(entryElement); + } + TextAsset newAsset = new TextAsset(astroBodyFile.ToString()); + List newBodies = new List(manager._shipLogXmlAssets) {newAsset}; + manager._shipLogXmlAssets = newBodies.ToArray(); + if (astroIdToBody.ContainsKey(astroBodyId.Value) == false) + { + astroIdToBody.Add(astroBodyId.Value, body); + } } } - return null; - } - public static void GenerateEntryData(ShipLogManager manager) - { - const int step = 400; - int colAccumulator = 0; - int rowAccumulator = 0; - foreach(ShipLogEntry entry in manager._entryList) + public static void GenerateEntryData(ShipLogManager manager) { - if (manager._entryDataDict.ContainsKey(entry._id) == false) + const int step = 400; + int colAccumulator = 0; + int rowAccumulator = 0; + foreach(ShipLogEntry entry in manager._entryList) { - NewHorizonsBody body = GetConfigFromEntry(entry); - Vector2? manualEntryPosition = FindPosition(entry._id, body.Config.ShipLog); - Vector2 entryPosition; - if (manualEntryPosition == null) + if (manager._entryDataDict.ContainsKey(entry._id) == false) { - entryPosition = new Vector2(colAccumulator, rowAccumulator); - } - else - { - entryPosition = (Vector2) manualEntryPosition; - } - EntryData newData = new EntryData - { - id = entry._id, - cardPosition = entryPosition, - sprite = body.Config.ShipLog.spriteFolder == null? null : GetSprite(entry._id, body) - }; - entry.SetSprite(newData.sprite == null? manager._shipLogLibrary.defaultEntrySprite : newData.sprite); - manager._entryDataDict.Add(entry._id, newData); - int index = manager._entryList.IndexOf(entry); - if (index < manager._entryList.Count - 2 && manager._entryList[index + 1]._astroObjectID != entry._astroObjectID) - { - rowAccumulator += step; - colAccumulator = 0; - } - else - { - colAccumulator += step; + NewHorizonsBody body = GetConfigFromEntry(entry); + Vector2? manualEntryPosition = GetManualEntryPosition(entry._id, body.Config.ShipLog); + Vector2 entryPosition; + if (manualEntryPosition == null) + { + entryPosition = new Vector2(colAccumulator, rowAccumulator); + } + else + { + entryPosition = (Vector2) manualEntryPosition; + } + EntryData newData = new EntryData + { + id = entry._id, + cardPosition = entryPosition, + sprite = body.Config.ShipLog.spriteFolder == null? null : GetEntrySprite(entry._id, body) + }; + entry.SetSprite(newData.sprite == null? manager._shipLogLibrary.defaultEntrySprite : newData.sprite); + manager._entryDataDict.Add(entry._id, newData); + int index = manager._entryList.IndexOf(entry); + if (index < manager._entryList.Count - 2 && manager._entryList[index + 1]._astroObjectID != entry._astroObjectID) + { + rowAccumulator += step; + colAccumulator = 0; + } + else + { + colAccumulator += step; + } } } } - } + private static void AddTranslation(XElement entry) + { + Dictionary table = TextTranslation.Get().m_table.theShipLogTable; + XElement nameElement = entry.Element("Name"); + if (nameElement != null) + { + string name = nameElement.Value; + table[name] = name; + foreach (XElement rumorFact in entry.Elements("RumorFact")) + { + XElement rumorName = rumorFact.Element("RumorName"); + if (rumorName != null) + { + table[rumorName.Value] = rumorName.Value; + } + + XElement rumorText = rumorFact.Element("Text"); + if (rumorText != null) + { + table[name + rumorText.Value] = rumorText.Value; + } + } + foreach (XElement exploreFact in entry.Elements("ExploreFact")) + { + XElement exploreText = exploreFact.Element("Text"); + if (exploreText != null) + { + table[name + exploreText.Value] = exploreText.Value; + } + } + } + } + + public static void UpdateEntryCuriosity(ref ShipLogEntry entry) + { + if (entryIdToRawName.ContainsKey(entry._id)) + { + entry._curiosity = rawNameToCuriosityName[entryIdToRawName[entry._id]]; + } + } + + private static Sprite GetEntrySprite(string entryId, NewHorizonsBody body) + { + IModAssets assets = body.Mod.Assets; + string path = body.Config.ShipLog.spriteFolder + "/" + entryId + ".png"; + if (File.Exists(Main.Instance.ModHelper.Manifest.ModFolderPath + path)) + { + Texture2D newTexture = assets.GetTexture(path); + Rect rect = new Rect(0, 0, newTexture.width, newTexture.height); + Vector2 pivot = new Vector2(newTexture.width / 2, newTexture.height / 2); + return Sprite.Create(newTexture, rect, pivot); + } + else + { + return null; + } + } + + private static Vector2? GetManualEntryPosition(string entryId, ShipLogModule config) + { + if (config.positions == null) return null; + foreach (ShipLogModule.EntryPosition position in config.positions) + { + if (position.id == entryId) + { + return position.position; + } + } + return null; + } + } + #endregion + + #region Fact Reveals + public static class RevealBuilder + { + public static void Make(GameObject go, Sector sector, PropModule.RevealInfo info, IModHelper mod) + { + GameObject newRevealGO = MakeGameObject(go, sector, info, mod); + switch (info.revealOn.ToLower()) + { + case "enter": + MakeTrigger(newRevealGO, sector, info, mod); + break; + case "observe": + MakeObservable(newRevealGO, sector, info, mod); + break; + case "snapshot": + MakeSnapshot(newRevealGO, sector, info, mod); + break; + default: + Logger.LogError("Invalid revealOn: " + info.revealOn); + break; + } + + newRevealGO.SetActive(true); + } + + private static SphereShape MakeShape(GameObject go, PropModule.RevealInfo info, Shape.CollisionMode collisionMode) + { + SphereShape newShape = go.AddComponent(); + newShape.radius = info.radius; + newShape.SetCollisionMode(collisionMode); + return newShape; + } + + private static GameObject MakeGameObject(GameObject go, Sector sector, PropModule.RevealInfo info, IModHelper mod) + { + GameObject revealTriggerVolume = new GameObject("Reveal Volume (" + info.revealOn + ")"); + revealTriggerVolume.SetActive(false); + revealTriggerVolume.transform.parent = sector?.transform ?? go.transform; + revealTriggerVolume.transform.localPosition = info.position; + return revealTriggerVolume; + } + + private static void MakeTrigger(GameObject go, Sector sector, PropModule.RevealInfo info, IModHelper mod) + { + SphereShape newShape = MakeShape(go, info, Shape.CollisionMode.Volume); + OWTriggerVolume newVolume = go.AddComponent(); + newVolume._shape = newShape; + ShipLogFactListTriggerVolume volume = go.AddComponent(); + volume._factIDs = info.reveals; + } + + private static void MakeObservable(GameObject go, Sector sector, PropModule.RevealInfo info, IModHelper mod) + { + go.layer = LayerMask.NameToLayer("Interactible"); + SphereCollider newSphere = go.AddComponent(); + newSphere.radius = info.radius; + OWCollider newCollider = go.AddComponent(); + ShipLogFactObserveTrigger newObserveTrigger = go.AddComponent(); + newObserveTrigger._factIDs = info.reveals; + newObserveTrigger._maxViewDistance = info.maxDistance == -1f ? 2f : info.maxDistance; + newObserveTrigger._maxViewAngle = info.maxAngle; + newObserveTrigger._owCollider = newCollider; + newObserveTrigger._disableColliderOnRevealFact = true; + } + + private static void MakeSnapshot(GameObject go, Sector sector, PropModule.RevealInfo info, IModHelper mod) + { + SphereShape newShape = MakeShape(go, info, Shape.CollisionMode.Manual); + ShapeVisibilityTracker newTracker = go.AddComponent(); + newTracker._shapes = new Shape[] {newShape}; + ShipLogFactSnapshotTrigger newSnapshotTrigger = go.AddComponent(); + newSnapshotTrigger._maxDistance = info.maxDistance == -1f ? 200f : info.maxDistance; + newSnapshotTrigger._factIDs = info.reveals; + } + } + #endregion + + #region Entry Locations + public static class EntryLocationBuilder + { + private static List locationsToInitialize = new List(); + public static void Make(GameObject go, Sector sector, PropModule.EntryLocationInfo info, IModHelper mod) + { + GameObject entryLocationGameObject = new GameObject("Entry Location (" + info.id + ")"); + entryLocationGameObject.SetActive(false); + entryLocationGameObject.transform.parent = sector?.transform ?? go.transform; + entryLocationGameObject.transform.localPosition = info.position; + ShipLogEntryLocation newLocation = entryLocationGameObject.AddComponent(); + newLocation._entryID = info.id; + newLocation._isWithinCloakField = info.cloaked; + locationsToInitialize.Add(newLocation); + entryLocationGameObject.SetActive(true); + } + + public static void InitializeLocations() + { + locationsToInitialize.ForEach(l => l.InitEntry()); + locationsToInitialize.Clear(); + } + } + #endregion + public static void Init() { var shipLogRoot = GameObject.Find("Ship_Body/Module_Cabin/Systems_Cabin/ShipLogPivot/ShipLog/ShipLogPivot/ShipLogCanvas"); diff --git a/NewHorizons/Builder/Props/PropBuildManager.cs b/NewHorizons/Builder/Props/PropBuildManager.cs index 281a2dd0..eadbfe7c 100644 --- a/NewHorizons/Builder/Props/PropBuildManager.cs +++ b/NewHorizons/Builder/Props/PropBuildManager.cs @@ -8,6 +8,7 @@ using UnityEngine; using Random = UnityEngine.Random; using Logger = NewHorizons.Utility.Logger; using System.Reflection; +using NewHorizons.Builder.General; using NewHorizons.Utility; using OWML.Common; @@ -53,6 +54,20 @@ namespace NewHorizons.Builder.Props DialogueBuilder.Make(go, sector, dialogueInfo, mod); } } + if (config.Props.Reveal != null) + { + foreach (var revealInfo in config.Props.Reveal) + { + ShipLogBuilder.RevealBuilder.Make(go, sector, revealInfo, mod); + } + } + if (config.Props.EntryLocation != null) + { + foreach (var entryLocationInfo in config.Props.EntryLocation) + { + ShipLogBuilder.EntryLocationBuilder.Make(go, sector, entryLocationInfo, mod); + } + } } public static GameObject LoadPrefab(string assetBundle, string path, string uniqueModName, IModAssets assets) diff --git a/NewHorizons/Builder/Props/SignalBuilder.cs b/NewHorizons/Builder/Props/SignalBuilder.cs index 96d7c3af..cc1a66e8 100644 --- a/NewHorizons/Builder/Props/SignalBuilder.cs +++ b/NewHorizons/Builder/Props/SignalBuilder.cs @@ -145,6 +145,7 @@ namespace NewHorizons.Builder.Props } audioSignal._name = name; audioSignal._sourceRadius = info.SourceRadius; + audioSignal._revealFactID = info.Reveals; audioSignal._onlyAudibleToScope = info.OnlyAudibleToScope; audioSignal._identificationDistance = info.IdentificationRadius; audioSignal._canBePickedUpByScope = true; diff --git a/NewHorizons/External/PropModule.cs b/NewHorizons/External/PropModule.cs index 02f66142..7364985b 100644 --- a/NewHorizons/External/PropModule.cs +++ b/NewHorizons/External/PropModule.cs @@ -15,6 +15,8 @@ namespace NewHorizons.External public GeyserInfo[] Geysers; public TornadoInfo[] Tornados; public DialogueInfo[] Dialogue; + public RevealInfo[] Reveal; + public EntryLocationInfo[] EntryLocation; public class ScatterInfo { @@ -68,5 +70,22 @@ namespace NewHorizons.External public MVector3 remoteTriggerPosition; public string persistentCondition; } + + public class RevealInfo + { + public string revealOn; + public string[] reveals; + public MVector3 position; + public float radius = 1f; + public float maxDistance = -1f; // Snapshot & Observe Only + public float maxAngle = 180f; // Observe Only + } + + public class EntryLocationInfo + { + public string id; + public bool cloaked; + public MVector3 position; + } } } diff --git a/NewHorizons/External/ShipLogModule.cs b/NewHorizons/External/ShipLogModule.cs index a805e303..b0d2ad53 100644 --- a/NewHorizons/External/ShipLogModule.cs +++ b/NewHorizons/External/ShipLogModule.cs @@ -9,13 +9,16 @@ namespace NewHorizons.External public MapMode mapMode; public CuriosityColor[] curiosities; public EntryPosition[] positions; + public string[] initialReveal; public class MapMode { public string revealedSprite; public string outlineSprite; - public float scale; + public float scale = 1f; public bool invisibleWhenHidden; + public float offset = 0f; + public bool remove = false; } public class CuriosityColor diff --git a/NewHorizons/External/SignalModule.cs b/NewHorizons/External/SignalModule.cs index 07a72252..906e3411 100644 --- a/NewHorizons/External/SignalModule.cs +++ b/NewHorizons/External/SignalModule.cs @@ -18,6 +18,7 @@ namespace NewHorizons.External public string Name; public string AudioClip = null; public string AudioFilePath = null; + public string Reveals = ""; public float SourceRadius = 1f; public float DetectionRadius = 0f; public float IdentificationRadius = 10f; diff --git a/NewHorizons/Tools/Patches.cs b/NewHorizons/Tools/Patches.cs index fdf97dae..f10c14ab 100644 --- a/NewHorizons/Tools/Patches.cs +++ b/NewHorizons/Tools/Patches.cs @@ -60,14 +60,12 @@ namespace NewHorizons.Tools Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Awake", typeof(Patches), nameof(Patches.OnShipLogManagerAwake)); + Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Start", typeof(Patches), nameof(Patches.OnShipLogManagerStart)); Main.Instance.ModHelper.HarmonyHelper.AddPrefix("IsFactRevealed", typeof(Patches), nameof(Patches.OnShipLogManagerIsFactRevealed)); Main.Instance.ModHelper.HarmonyHelper.AddPrefix("CheckForCompletionAchievement", typeof(Patches), nameof(Patches.OnShipLogManagerCheckForCompletionAchievement)); - Main.Instance.ModHelper.HarmonyHelper.AddPrefix("RevealFact", typeof(Patches), nameof(Patches.OnShipLogManagerRevealFact)); - + Main.Instance.ModHelper.HarmonyHelper.AddPrefix("GetCuriosityColor", typeof(Patches), nameof(Patches.OnUIStyleManagerGetCuriosityColor)); - - Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Initialize", typeof(Patches), nameof(Patches.OnShipLogMapModeInitialize)); - + Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Awake", typeof(Patches), nameof(Patches.DisableShipLogSandFunnel)); Main.Instance.ModHelper.HarmonyHelper.AddPrefix("UpdateState", typeof(Patches), nameof(Patches.DisableShipLogSandFunnel)); @@ -81,7 +79,7 @@ namespace NewHorizons.Tools Main.Instance.ModHelper.HarmonyHelper.AddPostfix("Awake", typeof(Patches), nameof(Patches.OnShipLogManagerAwakeComplete)); - Main.Instance.ModHelper.HarmonyHelper.AddPostfix("Initialize", typeof(Patches), nameof(Patches.OnShipLogMapModeInitializeComplete)); + Main.Instance.ModHelper.HarmonyHelper.AddPostfix("Initialize", typeof(Patches), nameof(Patches.OnShipLogMapModeInitialize)); } public static bool GetHUDDisplayName(ReferenceFrame __instance, ref string __result) @@ -333,7 +331,7 @@ namespace NewHorizons.Tools } return true; } - + public static void OnPlayerDataResetGame() { NewHorizonsData.Reset(); @@ -408,25 +406,25 @@ namespace NewHorizons.Tools { if (body.Config.ShipLog?.curiosities != null) { - ShipLogBuilder.AddCuriosityColors(body.Config.ShipLog.curiosities); + ShipLogBuilder.RumorModeBuilder.AddCuriosityColors(body.Config.ShipLog.curiosities); } } foreach (NewHorizonsBody body in Main.BodyDict[Main.Instance.CurrentStarSystem]) { if (body.Config.ShipLog?.xmlFile != null) { - ShipLogBuilder.AddAstroBodyToShipLog(__instance, body); + ShipLogBuilder.RumorModeBuilder.AddBodyToShipLog(__instance, body); } } } public static void OnShipLogManagerAwakeComplete(ShipLogManager __instance) { - ShipLogBuilder.GenerateEntryData(__instance); + ShipLogBuilder.RumorModeBuilder.GenerateEntryData(__instance); for (var i = 0; i < __instance._entryList.Count; i++) { ShipLogEntry logEntry = __instance._entryList[i]; - ShipLogBuilder.UpdateEntryCuriosity(ref logEntry); + ShipLogBuilder.RumorModeBuilder.UpdateEntryCuriosity(ref logEntry); } Logger.Log("Ship Log Generation Complete For: " + Main.Instance.CurrentStarSystem, Logger.LogType.Log); } @@ -456,14 +454,24 @@ namespace NewHorizons.Tools return Main.Instance.CurrentStarSystem == "SolarSystem"; } - public static bool OnShipLogManagerRevealFact(string __0) + public static bool OnShipLogManagerStart(ShipLogManager __instance) { - if (Main.Instance.CurrentStarSystem != "SolarSystem" && __0 == "TH_VILLAGE_X1") + if (Main.Instance.CurrentStarSystem == "SolarSystem") { + return true; + } + else + { + foreach (NewHorizonsBody body in Main.BodyDict[Main.Instance.CurrentStarSystem]) + { + foreach (string fact in body.Config.ShipLog?.initialReveal ?? Array.Empty()) + { + __instance.RevealFact(fact, false, false); + } + } + ShipLogBuilder.EntryLocationBuilder.InitializeLocations(); return false; } - - return true; } public static bool OnUIStyleManagerGetCuriosityColor(UIStyleManager __instance, CuriosityName __0, bool __1, ref Color __result) @@ -474,7 +482,7 @@ namespace NewHorizons.Tools } else { - __result = ShipLogBuilder.GetCuriosityColor(__0, __1, __instance._neutralColor, __instance._neutralHighlight); + __result = ShipLogBuilder.RumorModeBuilder.GetCuriosityColor(__0, __1, __instance._neutralColor, __instance._neutralHighlight); return false; } } @@ -485,17 +493,12 @@ namespace NewHorizons.Tools } public static void OnShipLogMapModeInitialize(ShipLogMapMode __instance) - { - - } - - public static void OnShipLogMapModeInitializeComplete(ShipLogMapMode __instance) { if (Main.Instance.CurrentStarSystem != "SolarSystem") { GameObject panRoot = GameObject.Find(ShipLogBuilder.PAN_ROOT_PATH); GameObject sunObject = GameObject.Find(ShipLogBuilder.PAN_ROOT_PATH + "/Sun"); - ShipLogAstroObject[][] navMatrix = ShipLogBuilder.ConstructMapMode(Main.Instance.CurrentStarSystem, panRoot, sunObject.layer); + ShipLogAstroObject[][] navMatrix = ShipLogBuilder.MapModeBuilder.ConstructMapMode(Main.Instance.CurrentStarSystem, panRoot, sunObject.layer); if (navMatrix.Length <= 1) { Logger.LogWarning("No planets suitable for map mode found! Defaulting to vanilla menu (expect weirdness!)."); @@ -509,7 +512,7 @@ namespace NewHorizons.Tools { DeleteDetail(gameObject.name); } - + // Just Lie About Having A Sand Funnel __instance._sandFunnel = __instance.gameObject.AddComponent(); } } @@ -524,7 +527,7 @@ namespace NewHorizons.Tools } else { - __result = ShipLogBuilder.GetAstroBodyShipLogName(__instance.GetID()); + __result = ShipLogBuilder.MapModeBuilder.GetAstroBodyShipLogName(__instance.GetID()); return false; } } diff --git a/NewHorizons/dialogue_schema.xsd b/NewHorizons/dialogue_schema.xsd new file mode 100644 index 00000000..e8c70424 --- /dev/null +++ b/NewHorizons/dialogue_schema.xsd @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NewHorizons/schema.json b/NewHorizons/schema.json index 2b005b04..85a03c54 100644 --- a/NewHorizons/schema.json +++ b/NewHorizons/schema.json @@ -669,7 +669,7 @@ "generateColliders": { "type": "bool", "default": false, - "description": "For each mesh filter found here should we make a mesh collider?" + "description": "For each mesh filter found here should we make a mesh collider?" } } } @@ -791,6 +791,94 @@ } } } + }, + "reveal": { + "type": "array", + "description": "A set of volumes that reveal ship log fact", + "items": { + "type": "object", + "properties": { + "revealOn": { + "type": "string", + "description": "'enter', 'observe', or 'snapshot' what needs to be done to the volume to unlock the facts" + }, + "reveals": { + "type": "array", + "description": "A list of facts to reveal", + "items": { + "type": "string" + } + }, + "position": { + "type": "object", + "description": "The position to place the volume at", + "properties": { + "x": { + "type": "number", + "default": 0 + }, + "y": { + "type": "number", + "default": 0 + }, + "z": { + "type": "number", + "default": 0 + } + } + }, + "radius": { + "type": "number", + "description": "The radius of the volume", + "default": 1.0 + }, + "maxDistance": { + "type": "number", + "description": "The max distance the user can be away from the volume to reveal the fact (snapshot and observe only)" + }, + "maxAngle": { + "type": "number", + "description": "The max view angle the player can see the volume with to unlock the fact", + "default": 180.0 + } + } + } + }, + "entryLocation": { + "type": "array", + "description": "A set of locations for ship log entries", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The ID of the entry this location is for" + }, + "cloaked": { + "type": "bool", + "description": "Whether this entry location is in a cloaking field", + "default": false + }, + "position": { + "type": "object", + "description": "The position of this entry location", + "properties": { + "x": { + "type": "number", + "default": 0 + }, + "y": { + "type": "number", + "default": 0 + }, + "z": { + "type": "number", + "default": 0 + } + } + } + } + } } } }, @@ -886,6 +974,10 @@ "type": "string", "description": "Relative filepath to the .wav file to use as the audio. Mutually exclusive with audioClip" }, + "reveals": { + "type": "string", + "description": "A ship log fact to reveal when the signal is identified" + }, "sourceRadius": { "type": "number", "default": 1, @@ -1154,6 +1246,158 @@ } } } + }, + "ShipLog": { + "type": "object", + "properties": { + "xmlFile": { + "type": "string", + "description": "The xml file to load ship log entries from" + }, + "spriteFolder": { + "type": "string", + "description": "A path to the folder where entry sprites are stored" + }, + "initialReveal": { + "type": "array", + "description": "A list of fact IDs to reveal when the game starts", + "items": { + "type": "string" + } + }, + "positions": { + "type": "array", + "description": "A set of positions to use instead of automatic layout in rumor mode", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The name of the entry to apply the position" + }, + "position": { + "type": "object", + "properties": { + "x": { + "type": "number", + "default": 0 + }, + "y": { + "type": "number", + "default": 0 + } + } + } + } + } + }, + "MapMode": { + "type": "object", + "properties": { + "revealedSprite": { + "type": "string", + "description": "The path to the sprite to show when the planet is revealed in map mode" + }, + "outlineSprite": { + "type": "string", + "description": "The path to the sprite to show when the planet is unexplored in map mode" + }, + "scale": { + "type": "number", + "description": "Scale to apply to the planet in map mode", + "default": 1 + }, + "invisibleWhenHidden": { + "type": "bool", + "description": "Hide the planet completely if unexplored instead of showing an outline", + "default": false + }, + "offset": { + "type": "number", + "description": "Extra distance to apply to this object in map mode", + "default": 0 + }, + "remove": { + "type": "boolean", + "description": "Completely remove this planet from map mode", + "default": false + } + } + }, + "Curiosities": { + "type": "array", + "description": "A set of colors to apply to curiosities", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The ID of the curiosity to apply the color to" + }, + "color": { + "type": "object", + "description": "The color to apply to entries with this curiosity", + "properties": { + "R": { + "type": "integer", + "default": 0, + "minimum": 0, + "maximum": 255 + }, + "G": { + "type": "integer", + "default": 0, + "minimum": 0, + "maximum": 255 + }, + "B": { + "type": "integer", + "default": 0, + "minimum": 0, + "maximum": 255 + }, + "A": { + "type": "integer", + "default": 0, + "minimum": 0, + "maximum": 255 + } + } + }, + "highlightColor": { + "type": "object", + "description": "The color to apply to highlighted entries with this curiosity", + "properties": { + "R": { + "type": "integer", + "default": 0, + "minimum": 0, + "maximum": 255 + }, + "G": { + "type": "integer", + "default": 0, + "minimum": 0, + "maximum": 255 + }, + "B": { + "type": "integer", + "default": 0, + "minimum": 0, + "maximum": 255 + }, + "A": { + "type": "integer", + "default": 0, + "minimum": 0, + "maximum": 255 + } + } + } + } + } + } + } } } } \ No newline at end of file diff --git a/NewHorizons/shiplog_schema.xsd b/NewHorizons/shiplog_schema.xsd new file mode 100644 index 00000000..fe3ade33 --- /dev/null +++ b/NewHorizons/shiplog_schema.xsd @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 5e680fb89e395974623698d9da1c584581c1ba98 Mon Sep 17 00:00:00 2001 From: Ben C Date: Sat, 5 Feb 2022 19:44:21 -0500 Subject: [PATCH 03/50] Added RevealFacts to dialogue_schema.xsd --- NewHorizons/dialogue_schema.xsd | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/NewHorizons/dialogue_schema.xsd b/NewHorizons/dialogue_schema.xsd index e8c70424..bd3e38ca 100644 --- a/NewHorizons/dialogue_schema.xsd +++ b/NewHorizons/dialogue_schema.xsd @@ -16,6 +16,13 @@ + + + + + + + From 31b23851561da1840ef6e438ae4d511cfd8cb485 Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Sun, 6 Feb 2022 10:47:17 -0500 Subject: [PATCH 04/50] Added dialogue block after persistent condition --- NewHorizons/AssetBundle/WarpDriveConfig.json | 3 ++- NewHorizons/Builder/Props/DialogueBuilder.cs | 3 +++ NewHorizons/External/PropModule.cs | 2 +- NewHorizons/Main.cs | 5 +---- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/NewHorizons/AssetBundle/WarpDriveConfig.json b/NewHorizons/AssetBundle/WarpDriveConfig.json index eb685c70..cc45a642 100644 --- a/NewHorizons/AssetBundle/WarpDriveConfig.json +++ b/NewHorizons/AssetBundle/WarpDriveConfig.json @@ -8,7 +8,8 @@ "position":{"x": -0.3071011, "y": 2.741472, "z": -4.005298}, "radius":1, "xmlFile":"AssetBundle/WarpDriveDialogue.xml", - "remoteTriggerPosition": {"x": -0.05656214, "y": 0.5362684, "z": 0.5467669} + "remoteTriggerPosition": {"x": -0.05656214, "y": 0.5362684, "z": 0.5467669}, + "blockAfterPersistentCondition" : "KnowsAboutWarpDrive" } ] } diff --git a/NewHorizons/Builder/Props/DialogueBuilder.cs b/NewHorizons/Builder/Props/DialogueBuilder.cs index 3b1a786a..76e5d95d 100644 --- a/NewHorizons/Builder/Props/DialogueBuilder.cs +++ b/NewHorizons/Builder/Props/DialogueBuilder.cs @@ -15,6 +15,9 @@ namespace NewHorizons.Builder.Props { public static void Make(GameObject go, Sector sector, PropModule.DialogueInfo info, IModHelper mod) { + + if (PlayerData._currentGameSave.GetPersistentCondition(info.blockAfterPersistentCondition)) return; + var dialogue = MakeConversationZone(go, sector, info, mod); if (info.remoteTriggerPosition != null) MakeRemoteDialogueTrigger(go, sector, info, dialogue); } diff --git a/NewHorizons/External/PropModule.cs b/NewHorizons/External/PropModule.cs index 02f66142..dfe7455c 100644 --- a/NewHorizons/External/PropModule.cs +++ b/NewHorizons/External/PropModule.cs @@ -66,7 +66,7 @@ namespace NewHorizons.External public float radius = 1f; public string xmlFile; public MVector3 remoteTriggerPosition; - public string persistentCondition; + public string blockAfterPersistentCondition; } } } diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index 967b4466..c3ab9039 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -115,10 +115,7 @@ namespace NewHorizons _shipWarpController = GameObject.Find("Ship_Body").AddComponent(); Instance.ModHelper.Events.Unity.FireOnNextUpdate(() => ShipLogBuilder.Init()); - if (!PlayerData._currentGameSave.GetPersistentCondition("KnowsAboutWarpDrive")) - { - LoadBody(LoadConfig(this, "AssetBundle/WarpDriveConfig.json")); - } + LoadBody(LoadConfig(this, "AssetBundle/WarpDriveConfig.json")); } Instance.ModHelper.Events.Unity.FireOnNextUpdate(() => AstroObjectLocator.GetAstroObject("MapSatellite").gameObject.AddComponent()); From 71fb83891d64bd06fe773903476038f64bbd5cd1 Mon Sep 17 00:00:00 2001 From: Ben C Date: Sun, 6 Feb 2022 10:58:55 -0500 Subject: [PATCH 05/50] Added Ship Log Details - Removed Debugging GUI - Added lines to map mode so orbits are more obvious - Added details to ship log - Updated schema.json - Added missing elements to dialogue_schema.xsd --- NewHorizons/Builder/General/ShipLogBuilder.cs | 113 ++++++++++++++---- NewHorizons/Components/ShipLogDetail.cs | 37 ++++++ NewHorizons/External/ShipLogModule.cs | 23 +++- NewHorizons/Main.cs | 29 ----- NewHorizons/Tools/Patches.cs | 24 +++- NewHorizons/dialogue_schema.xsd | 2 + NewHorizons/schema.json | 57 ++++++++- 7 files changed, 223 insertions(+), 62 deletions(-) create mode 100644 NewHorizons/Components/ShipLogDetail.cs diff --git a/NewHorizons/Builder/General/ShipLogBuilder.cs b/NewHorizons/Builder/General/ShipLogBuilder.cs index ac764a21..52d9e4ee 100644 --- a/NewHorizons/Builder/General/ShipLogBuilder.cs +++ b/NewHorizons/Builder/General/ShipLogBuilder.cs @@ -1,12 +1,8 @@ using NewHorizons.Components; -using System; -using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Xml.Linq; -using System.Text; -using System.Threading.Tasks; using NewHorizons.External; using NewHorizons.Utility; using OWML.Common; @@ -70,10 +66,10 @@ namespace NewHorizons.Builder.General public static ShipLogAstroObject[][] ConstructMapMode(string systemName, GameObject transformParent, int layer) { - MapModeObject rootObject = MakePrimaryNode(systemName); + MapModeObject rootObject = ConstructPrimaryNode(systemName); if (rootObject.mainBody != null) { - CreateAllNodes(ref rootObject, transformParent, layer); + MakeAllNodes(ref rootObject, transformParent, layer); } const int maxAmount = 20; @@ -104,13 +100,13 @@ namespace NewHorizons.Builder.General } } - private static void CreateAllNodes(ref MapModeObject parentNode, GameObject parent, int layer) + private static void MakeAllNodes(ref MapModeObject parentNode, GameObject parent, int layer) { - CreateNode(ref parentNode, parent, layer); + MakeNode(ref parentNode, parent, layer); for (var i = 0; i < parentNode.children.Count; i++) { MapModeObject child = parentNode.children[i]; - CreateAllNodes(ref child, parent, layer); + MakeAllNodes(ref child, parent, layer); parentNode.children[i] = child; } } @@ -174,21 +170,85 @@ namespace NewHorizons.Builder.General astroObject._id = GetAstroObjectId(node.mainBody); string imagePath = node.mainBody.Config.ShipLog?.mapMode?.revealedSprite ?? "DEFAULT"; string outlinePath = node.mainBody.Config.ShipLog?.mapMode?.outlineSprite ?? imagePath; - astroObject._imageObj = CreateImage(nodeGO, node.mainBody.Mod.Assets, imagePath, "Image", layer); - astroObject._outlineObj = CreateImage(nodeGO, node.mainBody.Mod.Assets, outlinePath, "Outline", layer); - astroObject._unviewedObj = GameObject.Instantiate(referenceUnviewedSprite, nodeGO.transform, false); + astroObject._imageObj = CreateImage(nodeGO, node.mainBody.Mod.Assets, imagePath, node.mainBody.Config.Name + " Revealed", layer); + astroObject._outlineObj = CreateImage(nodeGO, node.mainBody.Mod.Assets, outlinePath, node.mainBody.Config.Name + " Outline", layer); + astroObject._unviewedObj = Object.Instantiate(referenceUnviewedSprite, nodeGO.transform, false); + if (node.mainBody.Config.FocalPoint != null) + { + astroObject._imageObj.GetComponent().enabled = false; + astroObject._outlineObj.GetComponent().enabled = false; + astroObject._unviewedObj.GetComponent().enabled = false; + astroObject.transform.localScale = node.lastSibling.astroObject.transform.localScale; + } astroObject._invisibleWhenHidden = node.mainBody.Config.ShipLog?.mapMode?.invisibleWhenHidden ?? false; Rect imageRect = astroObject._imageObj.GetComponent().rect; astroObject._unviewedObj.transform.localPosition = new Vector3(imageRect.width / 2 + unviewedIconOffset, imageRect.height / 2 + unviewedIconOffset, 0); node.astroObject = astroObject; } - private static void ConnectNodes(MapModeObject from, MapModeObject to) + private static void ConnectNodeToLastSibling(MapModeObject node) { - + Vector2 fromPosition = node.astroObject.transform.localPosition; + Vector2 toPosition = node.lastSibling.astroObject.transform.localPosition; + GameObject newLink = new GameObject(node.mainBody.Config.Name + " To " + node.lastSibling.mainBody.Config.Name + " Link_ShipLog"); + newLink.layer = node.astroObject.gameObject.layer; + newLink.SetActive(false); + RectTransform transform = newLink.AddComponent(); + transform.SetParent(node.astroObject.transform.parent); + Vector2 center = toPosition + (fromPosition - toPosition) / 2; + transform.localPosition = new Vector3(center.x, center.y, -1); + transform.localRotation = Quaternion.identity; + transform.localScale = node.level % 2 == 0 ? new Vector3(node.astroObject.transform.localScale.x / 5f, Mathf.Abs(fromPosition.y - toPosition.y) / 100f, 1) : new Vector3(Mathf.Abs(fromPosition.x - toPosition.x) / 100f, node.astroObject.transform.localScale.y / 5f , 1); + newLink.AddComponent(); + transform.SetParent(node.astroObject.transform); + transform.SetAsFirstSibling(); + newLink.SetActive(true); } - private static void CreateNode(ref MapModeObject node, GameObject parent, int layer) + private static void MakeDetail(ShipLogModule.ShipLogDetailInfo info, Transform parent, IModAssets assets) + { + GameObject detailGameObject = new GameObject("Detail"); + detailGameObject.transform.SetParent(parent); + detailGameObject.SetActive(false); + + RectTransform detailTransform = detailGameObject.AddComponent(); + detailTransform.localPosition = (Vector2)(info.position ?? new MVector2(0, 0)); + detailTransform.localRotation = Quaternion.Euler(0f, 0f, info.rotation); + detailTransform.localScale = (Vector2)(info.scale ?? new MVector2(0, 0)); + + string revealedPath = info.revealedSprite ?? "DEFAULT"; + string outlinePath = info.outlineSprite ?? revealedPath; + + Image revealedImage = CreateImage(detailGameObject, assets, revealedPath, "Detail Revealed", parent.gameObject.layer).GetComponent(); + Image outlineImage = CreateImage(detailGameObject, assets, outlinePath, "Detail Outline", parent.gameObject.layer).GetComponent(); + + ShipLogDetail detail = detailGameObject.AddComponent(); + detail.Init(info, revealedImage, outlineImage); + detailGameObject.SetActive(true); + } + + private static void MakeDetails(MapModeObject node) + { + if (node.mainBody.Config.ShipLog?.mapMode?.details?.Length > 0) + { + GameObject detailsParent = new GameObject("Details"); + detailsParent.transform.SetParent(node.astroObject.transform); + detailsParent.SetActive(false); + + RectTransform detailsTransform = detailsParent.AddComponent(); + detailsTransform.localPosition = Vector3.zero; + detailsTransform.localRotation = Quaternion.identity; + detailsTransform.localScale = Vector3.one; + + foreach (ShipLogModule.ShipLogDetailInfo detailInfo in node.mainBody.Config.ShipLog.mapMode.details) + { + MakeDetail(detailInfo, detailsTransform, node.mainBody.Mod.Assets); + } + detailsParent.SetActive(true); + } + } + + private static void MakeNode(ref MapModeObject node, GameObject parent, int layer) { const float padding = 250f; @@ -218,12 +278,17 @@ namespace NewHorizons.Builder.General transform.localRotation = Quaternion.identity; transform.localScale = Vector3.one * scale; CreateShipLogAstroObject(newNodeGO, ref node, GameObject.Find(PAN_ROOT_PATH + "/TimberHearth/UnviewedIcon"), layer); + if (node.lastSibling != null) ConnectNodeToLastSibling(node); + MakeDetails(node); + transform.SetAsFirstSibling(); } - private static MapModeObject MakePrimaryNode(string systemName) + private static MapModeObject ConstructPrimaryNode(string systemName) { foreach (NewHorizonsBody body in Main.BodyDict[systemName].Where(b => b.Config.Base.CenterOfSolarSystem)) { + List searchList = Main.BodyDict[systemName].Where(b => (b.Config.ShipLog?.mapMode?.remove ?? false) == false).ToList(); + searchList.Sort((b, o) => b.Config.Orbit.SemiMajorAxis.CompareTo(o.Config.Orbit.SemiMajorAxis)); MapModeObject newNode = new MapModeObject { mainBody = body, @@ -231,21 +296,21 @@ namespace NewHorizons.Builder.General x = 0, y = 0 }; - newNode.children = MakeChildrenNodes(systemName, newNode); + newNode.children = ConstructChildrenNodes(systemName, newNode, searchList); return newNode; } Logger.LogError("Couldn't find center of system!"); return new MapModeObject(); } - private static List MakeChildrenNodes(string systemName, MapModeObject parent) + private static List ConstructChildrenNodes(string systemName, MapModeObject parent, List searchList) { List children = new List(); int newX = parent.x; int newY = parent.y; int newLevel = parent.level + 1; MapModeObject lastSibling = parent; - foreach (NewHorizonsBody body in Main.BodyDict[systemName].Where(b => b.Config.ShipLog?.mapMode?.remove == false && b.Config.Orbit.PrimaryBody == parent.mainBody.Config.Name)) + foreach (NewHorizonsBody body in searchList.Where(b => b.Config.Orbit.PrimaryBody == parent.mainBody.Config.Name)) { if (body.Config.Orbit.PrimaryBody == parent.mainBody.Config.Name) { @@ -261,16 +326,18 @@ namespace NewHorizons.Builder.General parent = parent, lastSibling = lastSibling }; - newNode.children = MakeChildrenNodes(systemName, newNode); + newNode.children = ConstructChildrenNodes(systemName, newNode, searchList); if (even) { newY += newNode.branch_height; parent.increment_height(); + newY += 1; } else { newX += newNode.branch_width; parent.increment_width(); + newX += 1; } lastSibling = newNode; children.Add(newNode); @@ -289,9 +356,9 @@ namespace NewHorizons.Builder.General private static Dictionary rawNameToCuriosityName = new Dictionary(); private static Dictionary entryIdToRawName = new Dictionary(); - public static void AddCuriosityColors(ShipLogModule.CuriosityColor[] newColors) + public static void AddCuriosityColors(ShipLogModule.CuriosityColorInfo[] newColors) { - foreach (ShipLogModule.CuriosityColor newColor in newColors) + foreach (ShipLogModule.CuriosityColorInfo newColor in newColors) { if (rawNameToCuriosityName.ContainsKey(newColor.id) == false) { @@ -450,7 +517,7 @@ namespace NewHorizons.Builder.General private static Vector2? GetManualEntryPosition(string entryId, ShipLogModule config) { if (config.positions == null) return null; - foreach (ShipLogModule.EntryPosition position in config.positions) + foreach (ShipLogModule.EntryPositionInfo position in config.positions) { if (position.id == entryId) { diff --git a/NewHorizons/Components/ShipLogDetail.cs b/NewHorizons/Components/ShipLogDetail.cs new file mode 100644 index 00000000..21059059 --- /dev/null +++ b/NewHorizons/Components/ShipLogDetail.cs @@ -0,0 +1,37 @@ +using NewHorizons.External; +using OWML.Common; +using UnityEngine; +using UnityEngine.UI; + +namespace NewHorizons.Components +{ + public class ShipLogDetail : MonoBehaviour + { + private Image revealedImage; + private Image outlineImage; + private ShipLogModule.ShipLogDetailInfo detailInfo; + + public void Init(ShipLogModule.ShipLogDetailInfo info, Image revealed, Image outline) + { + detailInfo = info; + revealedImage = revealed; + outlineImage = outline; + revealedImage.enabled = false; + outlineImage.enabled = false; + } + + public void UpdateState(bool parentRevealed) + { + if (parentRevealed) + { + revealedImage.enabled = true; + outlineImage.enabled = false; + } + else + { + revealedImage.enabled = false; + outlineImage.enabled = !detailInfo.invisibleWhenHidden; + } + } + } +} \ No newline at end of file diff --git a/NewHorizons/External/ShipLogModule.cs b/NewHorizons/External/ShipLogModule.cs index b0d2ad53..8883d8f5 100644 --- a/NewHorizons/External/ShipLogModule.cs +++ b/NewHorizons/External/ShipLogModule.cs @@ -6,12 +6,12 @@ namespace NewHorizons.External { public string xmlFile; public string spriteFolder; - public MapMode mapMode; - public CuriosityColor[] curiosities; - public EntryPosition[] positions; public string[] initialReveal; + public MapModeInfo mapMode; + public CuriosityColorInfo[] curiosities; + public EntryPositionInfo[] positions; - public class MapMode + public class MapModeInfo { public string revealedSprite; public string outlineSprite; @@ -19,19 +19,30 @@ namespace NewHorizons.External public bool invisibleWhenHidden; public float offset = 0f; public bool remove = false; + public ShipLogDetailInfo[] details; } - public class CuriosityColor + public class CuriosityColorInfo { public string id; public MColor color; public MColor highlightColor; } - public class EntryPosition + public class EntryPositionInfo { public string id; public MVector2 position; } + + public class ShipLogDetailInfo + { + public string revealedSprite; + public string outlineSprite; + public float rotation = 0f; + public bool invisibleWhenHidden; + public MVector2 position; + public MVector2 scale; + } } } \ No newline at end of file diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index c3367a5e..bb9dbb3f 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -53,35 +53,6 @@ namespace NewHorizons return new NewHorizonsApi(); } - private void OnGUI() - { - GUILayout.BeginArea(new Rect(0, 0, 100, 100)); - bool learnPress = GUILayout.Button("Learn Thing"); - if (learnPress) - { - Locator.GetShipLogManager().RevealFact("COOL_ROCK_R1", false, true); - Locator.GetShipLogManager().RevealFact("COOL_ROCK_R2", false, true); - Locator.GetShipLogManager().RevealFact("UNCOOL_ROCK_R1", false, true); - Locator.GetShipLogManager().RevealFact("UNCOOL_ROCK_R2", false, true); - Locator.GetShipLogManager().RevealFact("UNCOOL_ROCK_R3", false, true); - - } - - bool iRemem = GUILayout.Button("I Remem"); - if (iRemem) - { - Data.knowAllFacts = true; - Data.knowAllRumors = true; - } - bool forgorPress = GUILayout.Button("I Forgor"); - if (forgorPress) - { - Data.knowAllFacts = false; - Data.knowAllRumors = false; - } - GUILayout.EndArea(); - } - public void Start() { SceneManager.sceneLoaded += OnSceneLoaded; diff --git a/NewHorizons/Tools/Patches.cs b/NewHorizons/Tools/Patches.cs index f10c14ab..43316198 100644 --- a/NewHorizons/Tools/Patches.cs +++ b/NewHorizons/Tools/Patches.cs @@ -75,10 +75,12 @@ namespace NewHorizons.Tools // Postfixes Main.Instance.ModHelper.HarmonyHelper.AddPostfix("Awake", typeof(Patches), nameof(Patches.OnMapControllerAwake)); - Main.Instance.ModHelper.HarmonyHelper.AddPostfix("EnterMode", typeof(Patches), nameof(Patches.OnShipLogMapModeEnterMode)); - - Main.Instance.ModHelper.HarmonyHelper.AddPostfix("Awake", typeof(Patches), nameof(Patches.OnShipLogManagerAwakeComplete)); + Main.Instance.ModHelper.HarmonyHelper.AddPostfix("Awake", typeof(Patches), nameof(Patches.OnShipLogManagerAwakeComplete)); + + Main.Instance.ModHelper.HarmonyHelper.AddPostfix("UpdateState", typeof(Patches), nameof(Patches.OnShipLogAstroObjectUpdateState)); + + Main.Instance.ModHelper.HarmonyHelper.AddPostfix("EnterMode", typeof(Patches), nameof(Patches.OnShipLogMapModeEnterMode)); Main.Instance.ModHelper.HarmonyHelper.AddPostfix("Initialize", typeof(Patches), nameof(Patches.OnShipLogMapModeInitialize)); } @@ -532,6 +534,22 @@ namespace NewHorizons.Tools } } + public static void OnShipLogAstroObjectUpdateState(ShipLogAstroObject __instance) + { + Transform detailsParent = __instance.transform.Find("Details"); + if (detailsParent != null) + { + foreach (GameObject child in SearchUtilities.GetAllChildren(detailsParent.gameObject)) + { + Component detail; + if (child.TryGetComponent(typeof(ShipLogDetail), out detail)) + { + (detail as ShipLogDetail)?.UpdateState(__instance._state == ShipLogEntry.State.Explored); + } + } + } + } + public static bool DisableShipLogSandFunnel() { return Main.Instance.CurrentStarSystem == "SolarSystem"; diff --git a/NewHorizons/dialogue_schema.xsd b/NewHorizons/dialogue_schema.xsd index bd3e38ca..f956f814 100644 --- a/NewHorizons/dialogue_schema.xsd +++ b/NewHorizons/dialogue_schema.xsd @@ -29,6 +29,8 @@ + + diff --git a/NewHorizons/schema.json b/NewHorizons/schema.json index 85a03c54..32e42d7b 100644 --- a/NewHorizons/schema.json +++ b/NewHorizons/schema.json @@ -1319,8 +1319,63 @@ }, "remove": { "type": "boolean", - "description": "Completely remove this planet from map mode", + "description": "Completely remove this planet (and it's children) from map mode", "default": false + }, + "details": { + "type": "array", + "description": "Place non-selectable object in map mode (like sand funnels)", + "items": { + "type": "object", + "properties": { + "revealedSprite": { + "type": "string", + "description": "The sprite to show when the parent AstroBody is revealed" + }, + "outlineSprite": { + "type": "string", + "description": "The sprite to show when the parent AstroBody is rumored/unexplored" + }, + "rotation": { + "type": "number", + "description": "The angle in degrees to rotate the detail", + "default": 0 + }, + "invisibleWhenHidden": { + "type": "boolean", + "description": "Whether to completely hide this detail when the parent AstroBody is unexplored", + "default": false + }, + "position": { + "type": "object", + "description": "The position (relative to the parent) to place the detail", + "properties": { + "x": { + "type": "number", + "default": 0 + }, + "y": { + "type": "number", + "default": 0 + } + } + }, + "scale": { + "type": "object", + "description": "The amount to scale the x and y axis of the detail by", + "properties": { + "x": { + "type": "number", + "default": 0 + }, + "y": { + "type": "number", + "default": 0 + } + } + } + } + } } } }, From 44211c1febfb7e5b2b470377785344219d1f5d43 Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Sun, 6 Feb 2022 11:15:40 -0500 Subject: [PATCH 06/50] Null check --- NewHorizons/Builder/Props/DialogueBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/Builder/Props/DialogueBuilder.cs b/NewHorizons/Builder/Props/DialogueBuilder.cs index 76e5d95d..79ebaa44 100644 --- a/NewHorizons/Builder/Props/DialogueBuilder.cs +++ b/NewHorizons/Builder/Props/DialogueBuilder.cs @@ -16,7 +16,7 @@ namespace NewHorizons.Builder.Props public static void Make(GameObject go, Sector sector, PropModule.DialogueInfo info, IModHelper mod) { - if (PlayerData._currentGameSave.GetPersistentCondition(info.blockAfterPersistentCondition)) return; + if (info.blockAfterPersistentCondition != null && PlayerData._currentGameSave.GetPersistentCondition(info.blockAfterPersistentCondition)) return; var dialogue = MakeConversationZone(go, sector, info, mod); if (info.remoteTriggerPosition != null) MakeRemoteDialogueTrigger(go, sector, info, dialogue); From 7a1a51a17b2b736042df93ee2d4986f502580fa1 Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Sun, 6 Feb 2022 11:32:20 -0500 Subject: [PATCH 07/50] Fix rain/snow effect volumes --- NewHorizons/Builder/Atmosphere/EffectsBuilder.cs | 14 ++++++++++++-- NewHorizons/Main.cs | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/NewHorizons/Builder/Atmosphere/EffectsBuilder.cs b/NewHorizons/Builder/Atmosphere/EffectsBuilder.cs index f53b9a18..1b15c24a 100644 --- a/NewHorizons/Builder/Atmosphere/EffectsBuilder.cs +++ b/NewHorizons/Builder/Atmosphere/EffectsBuilder.cs @@ -26,7 +26,12 @@ namespace NewHorizons.Atmosphere rainGO.transform.localPosition = Vector3.zero; var pvc = rainGO.GetComponent(); - pvc.SetValue("_densityByHeight", new AnimationCurve(new Keyframe[] { new Keyframe(surfaceSize, 10f), new Keyframe(atmoSize / 2f, 0f) })); + pvc._densityByHeight = new AnimationCurve(new Keyframe[] + { + new Keyframe(surfaceSize - 0.5f, 0), + new Keyframe(surfaceSize, 10f), + new Keyframe(atmoSize, 0f) + }); rainGO.GetComponent().SetValue("_activeInSector", sector); rainGO.GetComponent().SetValue("_exclusionSectors", new Sector[] { }); @@ -45,7 +50,12 @@ namespace NewHorizons.Atmosphere snowEmitter.transform.localPosition = Vector3.zero; var pvc = snowEmitter.GetComponent(); - pvc.SetValue("_densityByHeight", new AnimationCurve(new Keyframe[] { new Keyframe(surfaceSize, 10f), new Keyframe(atmoSize / 2f, 0f) })); + pvc._densityByHeight = new AnimationCurve(new Keyframe[] + { + new Keyframe(surfaceSize - 0.5f, 0), + new Keyframe(surfaceSize, 10f), + new Keyframe(atmoSize, 0f) + }); snowEmitter.GetComponent().SetValue("_activeInSector", sector); snowEmitter.GetComponent().SetValue("_exclusionSectors", new Sector[] { }); diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index c3ab9039..b79877d1 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -579,7 +579,7 @@ namespace NewHorizons } if (body.Config.Atmosphere.HasRain || body.Config.Atmosphere.HasSnow) - EffectsBuilder.Make(go, sector, body.Config.Base.SurfaceSize, body.Config.Atmosphere.Size / 2f, body.Config.Atmosphere.HasRain, body.Config.Atmosphere.HasSnow); + EffectsBuilder.Make(go, sector, body.Config.Base.SurfaceSize, body.Config.Atmosphere.Size, body.Config.Atmosphere.HasRain, body.Config.Atmosphere.HasSnow); if (body.Config.Atmosphere.FogSize != 0) FogBuilder.Make(go, sector, body.Config.Atmosphere); From 1b320f6fc11a4ec3e45ec419ec0bb38f2fd9af6b Mon Sep 17 00:00:00 2001 From: Ben C Date: Sun, 6 Feb 2022 12:11:04 -0500 Subject: [PATCH 08/50] Changed Map Mode Lines - Lines are now hidden if their parent is hidden - Better Line Color --- NewHorizons/Builder/General/ShipLogBuilder.cs | 19 +++++++++++-- NewHorizons/Components/ShipLogDetail.cs | 28 +++++++++++++------ NewHorizons/Tools/Patches.cs | 9 +++++- 3 files changed, 44 insertions(+), 12 deletions(-) diff --git a/NewHorizons/Builder/General/ShipLogBuilder.cs b/NewHorizons/Builder/General/ShipLogBuilder.cs index 52d9e4ee..6997b38f 100644 --- a/NewHorizons/Builder/General/ShipLogBuilder.cs +++ b/NewHorizons/Builder/General/ShipLogBuilder.cs @@ -168,10 +168,12 @@ namespace NewHorizons.Builder.General const float unviewedIconOffset = 15; ShipLogAstroObject astroObject = nodeGO.AddComponent(); astroObject._id = GetAstroObjectId(node.mainBody); + string imagePath = node.mainBody.Config.ShipLog?.mapMode?.revealedSprite ?? "DEFAULT"; string outlinePath = node.mainBody.Config.ShipLog?.mapMode?.outlineSprite ?? imagePath; astroObject._imageObj = CreateImage(nodeGO, node.mainBody.Mod.Assets, imagePath, node.mainBody.Config.Name + " Revealed", layer); astroObject._outlineObj = CreateImage(nodeGO, node.mainBody.Mod.Assets, outlinePath, node.mainBody.Config.Name + " Outline", layer); + astroObject._unviewedObj = Object.Instantiate(referenceUnviewedSprite, nodeGO.transform, false); if (node.mainBody.Config.FocalPoint != null) { @@ -181,6 +183,7 @@ namespace NewHorizons.Builder.General astroObject.transform.localScale = node.lastSibling.astroObject.transform.localScale; } astroObject._invisibleWhenHidden = node.mainBody.Config.ShipLog?.mapMode?.invisibleWhenHidden ?? false; + Rect imageRect = astroObject._imageObj.GetComponent().rect; astroObject._unviewedObj.transform.localPosition = new Vector3(imageRect.width / 2 + unviewedIconOffset, imageRect.height / 2 + unviewedIconOffset, 0); node.astroObject = astroObject; @@ -190,16 +193,28 @@ namespace NewHorizons.Builder.General { Vector2 fromPosition = node.astroObject.transform.localPosition; Vector2 toPosition = node.lastSibling.astroObject.transform.localPosition; - GameObject newLink = new GameObject(node.mainBody.Config.Name + " To " + node.lastSibling.mainBody.Config.Name + " Link_ShipLog"); + + GameObject newLink = new GameObject("Line_ShipLog"); newLink.layer = node.astroObject.gameObject.layer; newLink.SetActive(false); + RectTransform transform = newLink.AddComponent(); transform.SetParent(node.astroObject.transform.parent); Vector2 center = toPosition + (fromPosition - toPosition) / 2; transform.localPosition = new Vector3(center.x, center.y, -1); transform.localRotation = Quaternion.identity; transform.localScale = node.level % 2 == 0 ? new Vector3(node.astroObject.transform.localScale.x / 5f, Mathf.Abs(fromPosition.y - toPosition.y) / 100f, 1) : new Vector3(Mathf.Abs(fromPosition.x - toPosition.x) / 100f, node.astroObject.transform.localScale.y / 5f , 1); - newLink.AddComponent(); + Image linkImage = newLink.AddComponent(); + linkImage.color = new Color(0.28f, 0.28f, 0.5f, 0.28f); + + ShipLogModule.ShipLogDetailInfo linkDetailInfo = new ShipLogModule.ShipLogDetailInfo() + { + invisibleWhenHidden = node.mainBody.Config.ShipLog?.mapMode?.invisibleWhenHidden ?? false + }; + + ShipLogDetail linkDetail = newLink.AddComponent(); + linkDetail.Init(linkDetailInfo, linkImage, linkImage); + transform.SetParent(node.astroObject.transform); transform.SetAsFirstSibling(); newLink.SetActive(true); diff --git a/NewHorizons/Components/ShipLogDetail.cs b/NewHorizons/Components/ShipLogDetail.cs index 21059059..c6f77304 100644 --- a/NewHorizons/Components/ShipLogDetail.cs +++ b/NewHorizons/Components/ShipLogDetail.cs @@ -2,6 +2,7 @@ using OWML.Common; using UnityEngine; using UnityEngine.UI; +using Logger = NewHorizons.Utility.Logger; namespace NewHorizons.Components { @@ -20,17 +21,26 @@ namespace NewHorizons.Components outlineImage.enabled = false; } - public void UpdateState(bool parentRevealed) + public void UpdateState(ShipLogEntry.State parentState) { - if (parentRevealed) + switch (parentState) { - revealedImage.enabled = true; - outlineImage.enabled = false; - } - else - { - revealedImage.enabled = false; - outlineImage.enabled = !detailInfo.invisibleWhenHidden; + case ShipLogEntry.State.Explored: + outlineImage.enabled = false; + revealedImage.enabled = true; + break; + case ShipLogEntry.State.Rumored: + revealedImage.enabled = false; + outlineImage.enabled = true; + break; + case ShipLogEntry.State.Hidden: + revealedImage.enabled = false; + outlineImage.enabled = !detailInfo.invisibleWhenHidden; + break; + case ShipLogEntry.State.None: + revealedImage.enabled = false; + outlineImage.enabled = false; + break; } } } diff --git a/NewHorizons/Tools/Patches.cs b/NewHorizons/Tools/Patches.cs index 43316198..4c885417 100644 --- a/NewHorizons/Tools/Patches.cs +++ b/NewHorizons/Tools/Patches.cs @@ -544,10 +544,17 @@ namespace NewHorizons.Tools Component detail; if (child.TryGetComponent(typeof(ShipLogDetail), out detail)) { - (detail as ShipLogDetail)?.UpdateState(__instance._state == ShipLogEntry.State.Explored); + (detail as ShipLogDetail)?.UpdateState(__instance._state); } } } + + Transform lineObject = __instance.transform.Find("Line_ShipLog"); + if (lineObject != null) + { + ShipLogDetail lineDetail = lineObject.gameObject.GetComponent(); + lineDetail.UpdateState(__instance._state); + } } public static bool DisableShipLogSandFunnel() From 218c760e7a7041dcb4682f0f62232710595239b3 Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Sun, 6 Feb 2022 12:31:02 -0500 Subject: [PATCH 09/50] Changed warp drive xml to use pages bc im dumb --- NewHorizons/AssetBundle/WarpDriveDialogue.xml | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/NewHorizons/AssetBundle/WarpDriveDialogue.xml b/NewHorizons/AssetBundle/WarpDriveDialogue.xml index 37c2c43a..1f9ea63f 100644 --- a/NewHorizons/AssetBundle/WarpDriveDialogue.xml +++ b/NewHorizons/AssetBundle/WarpDriveDialogue.xml @@ -6,33 +6,7 @@ DEFAULT Your ship is now equiped with a warp drive! - - - - - ... - 1 - - - - - - 1 - You can use the new "Interstellar Mode" page in the ship log to lock-on your autopilot to another star system. - - - - - ... - 2 - - - - - - 2 - Then just buckle up and engage the autopilot to warp there! KnowsAboutWarpDrive From 58947aaba82c36d84d0d357a9f7c078edd3a9fad Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Sun, 6 Feb 2022 12:31:16 -0500 Subject: [PATCH 10/50] Actually colour water properly --- NewHorizons/Builder/Body/WaterBuilder.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/NewHorizons/Builder/Body/WaterBuilder.cs b/NewHorizons/Builder/Body/WaterBuilder.cs index c6cc0711..f7cccd41 100644 --- a/NewHorizons/Builder/Body/WaterBuilder.cs +++ b/NewHorizons/Builder/Body/WaterBuilder.cs @@ -39,7 +39,8 @@ namespace NewHorizons.Builder.Body tempArray[i] = new Material(GDSharedMaterials[i]); if (module.Tint != null) { - tempArray[i].color = module.Tint.ToColor32(); + tempArray[i].color = module.Tint.ToColor(); + tempArray[i].SetColor("_FogColor", module.Tint.ToColor()); } } @@ -80,6 +81,13 @@ namespace NewHorizons.Builder.Body fogGO.name = "OceanFog"; fogGO.transform.localPosition = Vector3.zero; fogGO.transform.localScale = Vector3.one; + if(module.Tint != null) + { + var adjustedColour = module.Tint.ToColor() / 4f; + adjustedColour.a = 1f; + + fogGO.GetComponent().material.color = adjustedColour; + } if (module.Curve != null) { From c027cdddc425c4ec1435dee63f90ae6ca3b4fe65 Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Sun, 6 Feb 2022 14:27:30 -0500 Subject: [PATCH 11/50] Detail shouldn't have count in the schema --- NewHorizons/schema.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/NewHorizons/schema.json b/NewHorizons/schema.json index 2b005b04..2d340409 100644 --- a/NewHorizons/schema.json +++ b/NewHorizons/schema.json @@ -680,9 +680,6 @@ "items": { "type": "object", "properties": { - "count": { - "type": "integer" - }, "path": { "type": "string", "description": "Either the path in the scene hierarchy of the item to copy or the path to the object in the supplied asset bundle" From 28970e4138c26b3da8b06eb22ad5b3c6a0daafaa Mon Sep 17 00:00:00 2001 From: Ben C Date: Sun, 6 Feb 2022 22:52:26 -0500 Subject: [PATCH 12/50] Fixed error if there are too many planets Found by `Jammer` --- NewHorizons/Builder/General/ShipLogBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/Builder/General/ShipLogBuilder.cs b/NewHorizons/Builder/General/ShipLogBuilder.cs index 6997b38f..6c89fbf9 100644 --- a/NewHorizons/Builder/General/ShipLogBuilder.cs +++ b/NewHorizons/Builder/General/ShipLogBuilder.cs @@ -72,7 +72,7 @@ namespace NewHorizons.Builder.General MakeAllNodes(ref rootObject, transformParent, layer); } - const int maxAmount = 20; + int maxAmount = Main.BodyDict[Main.Instance.CurrentStarSystem].Count; ShipLogAstroObject[][] navMatrix = new ShipLogAstroObject[maxAmount][]; for (int i = 0; i < maxAmount; i++) { From ffe403ecea76281d1d73655d976ee3474bfb8906 Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Sun, 6 Feb 2022 22:58:52 -0500 Subject: [PATCH 13/50] Update schema.json --- NewHorizons/schema.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NewHorizons/schema.json b/NewHorizons/schema.json index 2d340409..d805b63f 100644 --- a/NewHorizons/schema.json +++ b/NewHorizons/schema.json @@ -736,6 +736,10 @@ "default": false, "description": "For each mesh filter found here should we make a mesh collider?" } + }, + "scale": { + "type": "number", + "default": 1 } } } From 862c98133c0b28a6e12c049eb6965180fd3ccf65 Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Sun, 6 Feb 2022 23:05:38 -0500 Subject: [PATCH 14/50] Separate StarChartHandler into its own thing --- NewHorizons/Builder/General/ShipLogBuilder.cs | 40 +------------- NewHorizons/Handlers/StarChartHandler.cs | 52 +++++++++++++++++++ NewHorizons/Main.cs | 3 +- NewHorizons/Tools/Patches.cs | 3 +- NewHorizons/Tools/WarpDrivePatches.cs | 9 ++-- 5 files changed, 62 insertions(+), 45 deletions(-) create mode 100644 NewHorizons/Handlers/StarChartHandler.cs diff --git a/NewHorizons/Builder/General/ShipLogBuilder.cs b/NewHorizons/Builder/General/ShipLogBuilder.cs index 6c89fbf9..1f37f1e6 100644 --- a/NewHorizons/Builder/General/ShipLogBuilder.cs +++ b/NewHorizons/Builder/General/ShipLogBuilder.cs @@ -15,8 +15,7 @@ namespace NewHorizons.Builder.General public static class ShipLogBuilder { public static readonly string PAN_ROOT_PATH = "Ship_Body/Module_Cabin/Systems_Cabin/ShipLogPivot/ShipLog/ShipLogPivot/ShipLogCanvas/MapMode/ScaleRoot/PanRoot"; - - public static ShipLogStarChartMode ShipLogStarChartMode; + private static Dictionary astroIdToBody = new Dictionary(); private static NewHorizonsBody GetConfigFromEntry(ShipLogEntry entry) @@ -645,42 +644,5 @@ namespace NewHorizons.Builder.General } } #endregion - - public static void Init() - { - var shipLogRoot = GameObject.Find("Ship_Body/Module_Cabin/Systems_Cabin/ShipLogPivot/ShipLog/ShipLogPivot/ShipLogCanvas"); - - var starChartLog = new GameObject("StarChartMode"); - starChartLog.SetActive(false); - starChartLog.transform.parent = shipLogRoot.transform; - starChartLog.transform.localScale = Vector3.one * 1f; - starChartLog.transform.localPosition = Vector3.zero; - starChartLog.transform.localRotation = Quaternion.Euler(0, 0, 0); - - ShipLogStarChartMode = starChartLog.AddComponent(); - - var reticleImage = GameObject.Instantiate(GameObject.Find("Ship_Body/Module_Cabin/Systems_Cabin/ShipLogPivot/ShipLog/ShipLogPivot/ShipLogCanvas/DetectiveMode/ReticleImage (1)/"), starChartLog.transform); - - var scaleRoot = new GameObject("ScaleRoot"); - scaleRoot.transform.parent = starChartLog.transform; - scaleRoot.transform.localScale = Vector3.one; - scaleRoot.transform.localPosition = Vector3.zero; - scaleRoot.transform.localRotation = Quaternion.Euler(0, 0, 0); - - var panRoot = new GameObject("PanRoot"); - panRoot.transform.parent = scaleRoot.transform; - panRoot.transform.localScale = Vector3.one; - panRoot.transform.localPosition = Vector3.zero; - panRoot.transform.localRotation = Quaternion.Euler(0,0,0); - - var centerPromptList = shipLogRoot.transform.Find("ScreenPromptListScaleRoot/ScreenPromptList_Center")?.GetComponent(); - var upperRightPromptList = shipLogRoot.transform.Find("ScreenPromptListScaleRoot/ScreenPromptList_UpperRight")?.GetComponent(); - var oneShotSource = GameObject.Find("Ship_Body/Module_Cabin/Systems_Cabin/ShipLogPivot/ShipLog/OneShotAudio_ShipLog")?.GetComponent(); - - ShipLogStarChartMode.Initialize( - centerPromptList, - upperRightPromptList, - oneShotSource); - } } } diff --git a/NewHorizons/Handlers/StarChartHandler.cs b/NewHorizons/Handlers/StarChartHandler.cs new file mode 100644 index 00000000..d60d5bb0 --- /dev/null +++ b/NewHorizons/Handlers/StarChartHandler.cs @@ -0,0 +1,52 @@ +using NewHorizons.Components; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace NewHorizons.Handlers +{ + public static class StarChartHandler + { + public static ShipLogStarChartMode ShipLogStarChartMode; + + public static void Init() + { + var shipLogRoot = GameObject.Find("Ship_Body/Module_Cabin/Systems_Cabin/ShipLogPivot/ShipLog/ShipLogPivot/ShipLogCanvas"); + + var starChartLog = new GameObject("StarChartMode"); + starChartLog.SetActive(false); + starChartLog.transform.parent = shipLogRoot.transform; + starChartLog.transform.localScale = Vector3.one * 1f; + starChartLog.transform.localPosition = Vector3.zero; + starChartLog.transform.localRotation = Quaternion.Euler(0, 0, 0); + + ShipLogStarChartMode = starChartLog.AddComponent(); + + var reticleImage = GameObject.Instantiate(GameObject.Find("Ship_Body/Module_Cabin/Systems_Cabin/ShipLogPivot/ShipLog/ShipLogPivot/ShipLogCanvas/DetectiveMode/ReticleImage (1)/"), starChartLog.transform); + + var scaleRoot = new GameObject("ScaleRoot"); + scaleRoot.transform.parent = starChartLog.transform; + scaleRoot.transform.localScale = Vector3.one; + scaleRoot.transform.localPosition = Vector3.zero; + scaleRoot.transform.localRotation = Quaternion.Euler(0, 0, 0); + + var panRoot = new GameObject("PanRoot"); + panRoot.transform.parent = scaleRoot.transform; + panRoot.transform.localScale = Vector3.one; + panRoot.transform.localPosition = Vector3.zero; + panRoot.transform.localRotation = Quaternion.Euler(0, 0, 0); + + var centerPromptList = shipLogRoot.transform.Find("ScreenPromptListScaleRoot/ScreenPromptList_Center")?.GetComponent(); + var upperRightPromptList = shipLogRoot.transform.Find("ScreenPromptListScaleRoot/ScreenPromptList_UpperRight")?.GetComponent(); + var oneShotSource = GameObject.Find("Ship_Body/Module_Cabin/Systems_Cabin/ShipLogPivot/ShipLog/OneShotAudio_ShipLog")?.GetComponent(); + + ShipLogStarChartMode.Initialize( + centerPromptList, + upperRightPromptList, + oneShotSource); + } + } +} diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index 625b076b..af99a57d 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -23,6 +23,7 @@ using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; using Logger = NewHorizons.Utility.Logger; +using NewHorizons.Handlers; namespace NewHorizons { @@ -114,7 +115,7 @@ namespace NewHorizons HasWarpDrive = true; _shipWarpController = GameObject.Find("Ship_Body").AddComponent(); - Instance.ModHelper.Events.Unity.FireOnNextUpdate(() => ShipLogBuilder.Init()); + Instance.ModHelper.Events.Unity.FireOnNextUpdate(() => StarChartHandler.Init()); LoadBody(LoadConfig(this, "AssetBundle/WarpDriveConfig.json")); } diff --git a/NewHorizons/Tools/Patches.cs b/NewHorizons/Tools/Patches.cs index 4c885417..2a74328e 100644 --- a/NewHorizons/Tools/Patches.cs +++ b/NewHorizons/Tools/Patches.cs @@ -16,6 +16,7 @@ using OWML.Utils; using UnityEngine; using Logger = NewHorizons.Utility.Logger; using Object = UnityEngine.Object; +using NewHorizons.Handlers; namespace NewHorizons.Tools { @@ -382,7 +383,7 @@ namespace NewHorizons.Tools { if(__instance._playerAtFlightConsole && OWInput.IsNewlyPressed(InputLibrary.autopilot, InputMode.ShipCockpit)) { - var targetSystem = ShipLogBuilder.ShipLogStarChartMode.GetTargetStarSystem(); + var targetSystem = StarChartHandler.ShipLogStarChartMode.GetTargetStarSystem(); if (targetSystem != null) { Main.Instance.ChangeCurrentStarSystem(targetSystem, true); diff --git a/NewHorizons/Tools/WarpDrivePatches.cs b/NewHorizons/Tools/WarpDrivePatches.cs index e84d0d71..e06ce4c0 100644 --- a/NewHorizons/Tools/WarpDrivePatches.cs +++ b/NewHorizons/Tools/WarpDrivePatches.cs @@ -1,4 +1,5 @@ using NewHorizons.Builder.General; +using NewHorizons.Handlers; using System; using System.Collections.Generic; using System.Linq; @@ -35,7 +36,7 @@ namespace NewHorizons.Tools if (__instance._playerAtFlightConsole && OWInput.IsNewlyPressed(InputLibrary.autopilot, InputMode.ShipCockpit)) { - var targetSystem = ShipLogBuilder.ShipLogStarChartMode.GetTargetStarSystem(); + var targetSystem = StarChartHandler.ShipLogStarChartMode.GetTargetStarSystem(); if (targetSystem != null) { Main.Instance.ChangeCurrentStarSystem(targetSystem, true); @@ -52,7 +53,7 @@ namespace NewHorizons.Tools if (__instance._exiting || OWInput.GetInputMode() != InputMode.ShipComputer || __instance._currentMode.AllowCancelInput() && OWInput.IsNewlyPressed(InputLibrary.cancel, InputMode.All) - || ShipLogBuilder.ShipLogStarChartMode == null) + || StarChartHandler.ShipLogStarChartMode == null) return true; __instance._exitPrompt.SetVisibility(__instance._currentMode.AllowCancelInput()); @@ -66,8 +67,8 @@ namespace NewHorizons.Tools __instance._currentMode = (flag ? __instance._detectiveMode : __instance._mapMode); if (currentMode.Equals(__instance._mapMode)) - __instance._currentMode = ShipLogBuilder.ShipLogStarChartMode; - else if (currentMode.Equals(ShipLogBuilder.ShipLogStarChartMode)) + __instance._currentMode = StarChartHandler.ShipLogStarChartMode; + else if (currentMode.Equals(StarChartHandler.ShipLogStarChartMode)) __instance._currentMode = __instance._detectiveMode; else __instance._currentMode = __instance._mapMode; From 70abed2ae3813d7954dfde3e2bbd3302690d77fc Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Sun, 6 Feb 2022 23:05:51 -0500 Subject: [PATCH 15/50] Remove old blackhole portal gun code --- NewHorizons/Utility/DebugRaycaster.cs | 45 --------------------------- 1 file changed, 45 deletions(-) diff --git a/NewHorizons/Utility/DebugRaycaster.cs b/NewHorizons/Utility/DebugRaycaster.cs index 39537d07..3d8e3116 100644 --- a/NewHorizons/Utility/DebugRaycaster.cs +++ b/NewHorizons/Utility/DebugRaycaster.cs @@ -14,9 +14,6 @@ namespace NewHorizons.Utility { private OWRigidbody _rb; - private GameObject blackHole; - private GameObject whiteHole; - private void Awake() { _rb = this.GetRequiredComponent(); @@ -38,48 +35,6 @@ namespace NewHorizons.Utility } _rb.EnableCollisionDetection(); } - - /* - // Portal Gun: - if (Keyboard.current == null) return; - var fireBlackHole = Keyboard.current[Key.B].wasReleasedThisFrame; - var fireWhiteHole = Keyboard.current[Key.N].wasReleasedThisFrame; - if (fireBlackHole || fireWhiteHole) - { - // Raycast - _rb.DisableCollisionDetection(); - int layerMask = OWLayerMask.physicalMask; - var origin = Locator.GetActiveCamera().transform.position; - var direction = Locator.GetActiveCamera().transform.TransformDirection(Vector3.forward); - if (Physics.Raycast(origin, direction, out RaycastHit hitInfo, Mathf.Infinity, OWLayerMask.physicalMask)) - { - var pos = hitInfo.transform.InverseTransformPoint(hitInfo.point + hitInfo.normal); - var hitBody = hitInfo.transform.gameObject; - var sector = hitBody.GetComponent()?.GetRootSector(); - - if (hitBody == null || sector == null) return; - Logger.Log($"{hitBody}"); - if (fireBlackHole) - { - if (blackHole != null) GameObject.Destroy(blackHole); - blackHole = SingularityBuilder.MakeBlackHole(hitBody, sector, pos, 2, false, null, false); - Logger.Log("Make black hole"); - } - else - { - if (whiteHole != null) GameObject.Destroy(whiteHole); - whiteHole = SingularityBuilder.MakeWhiteHole(hitBody, sector, hitBody.GetAttachedOWRigidbody(), pos, 2, false); - Logger.Log("Make white hole"); - } - - if(blackHole && whiteHole) - { - SingularityBuilder.PairSingularities(blackHole, whiteHole); - } - } - _rb.EnableCollisionDetection(); - } - */ } } } From c3a7588b226732c18e9548d57c19046e02b019e0 Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Sun, 6 Feb 2022 23:47:09 -0500 Subject: [PATCH 16/50] Code refactor after PR --- NewHorizons/Builder/General/ShipLogBuilder.cs | 648 ------------------ NewHorizons/Builder/Props/PropBuildManager.cs | 5 +- .../Builder/ShipLog/EntryLocationBuilder.cs | 37 + NewHorizons/Builder/ShipLog/MapModeBuilder.cs | 319 +++++++++ NewHorizons/Builder/ShipLog/RevealBuilder.cs | 89 +++ .../Builder/ShipLog/RumorModeBuilder.cs | 191 ++++++ NewHorizons/Handlers/ShipLogHandler.cs | 46 ++ NewHorizons/Tools/Patches.cs | 24 +- NewHorizons/Utility/CollectionUtilities.cs | 25 + 9 files changed, 723 insertions(+), 661 deletions(-) delete mode 100644 NewHorizons/Builder/General/ShipLogBuilder.cs create mode 100644 NewHorizons/Builder/ShipLog/EntryLocationBuilder.cs create mode 100644 NewHorizons/Builder/ShipLog/MapModeBuilder.cs create mode 100644 NewHorizons/Builder/ShipLog/RevealBuilder.cs create mode 100644 NewHorizons/Builder/ShipLog/RumorModeBuilder.cs create mode 100644 NewHorizons/Handlers/ShipLogHandler.cs create mode 100644 NewHorizons/Utility/CollectionUtilities.cs diff --git a/NewHorizons/Builder/General/ShipLogBuilder.cs b/NewHorizons/Builder/General/ShipLogBuilder.cs deleted file mode 100644 index 1f37f1e6..00000000 --- a/NewHorizons/Builder/General/ShipLogBuilder.cs +++ /dev/null @@ -1,648 +0,0 @@ -using NewHorizons.Components; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Xml.Linq; -using NewHorizons.External; -using NewHorizons.Utility; -using OWML.Common; -using UnityEngine; -using UnityEngine.UI; -using Logger = NewHorizons.Utility.Logger; - -namespace NewHorizons.Builder.General -{ - public static class ShipLogBuilder - { - public static readonly string PAN_ROOT_PATH = "Ship_Body/Module_Cabin/Systems_Cabin/ShipLogPivot/ShipLog/ShipLogPivot/ShipLogCanvas/MapMode/ScaleRoot/PanRoot"; - - private static Dictionary astroIdToBody = new Dictionary(); - - private static NewHorizonsBody GetConfigFromEntry(ShipLogEntry entry) - { - return astroIdToBody[entry._astroObjectID]; - } - - #region Map Mode - - public class MapModeBuilder - { - private class MapModeObject - { - public int x; - public int y; - public int branch_width; - public int branch_height; - public int level; - public NewHorizonsBody mainBody; - public ShipLogAstroObject astroObject; - public List children; - public MapModeObject parent; - public MapModeObject lastSibling; - public void increment_width() - { - branch_width++; - parent?.increment_width(); - } - public void increment_height() - { - branch_height++; - parent?.increment_height(); - } - } - - public static string GetAstroBodyShipLogName(string id) - { - if (astroIdToBody.ContainsKey(id)) - { - return astroIdToBody[id].Config.Name; - } - else - { - return id; - } - } - - public static ShipLogAstroObject[][] ConstructMapMode(string systemName, GameObject transformParent, int layer) - { - MapModeObject rootObject = ConstructPrimaryNode(systemName); - if (rootObject.mainBody != null) - { - MakeAllNodes(ref rootObject, transformParent, layer); - } - - int maxAmount = Main.BodyDict[Main.Instance.CurrentStarSystem].Count; - ShipLogAstroObject[][] navMatrix = new ShipLogAstroObject[maxAmount][]; - for (int i = 0; i < maxAmount; i++) - { - navMatrix[i] = new ShipLogAstroObject[maxAmount]; - } - - CreateNavigationMatrix(rootObject, ref navMatrix); - navMatrix = navMatrix.Where(a => a.Count(c => c != null) > 0).Prepend(new ShipLogAstroObject[1]).ToArray(); - for (var index = 0; index < navMatrix.Length; index++) - { - navMatrix[index] = navMatrix[index].Where(a => a != null).ToArray(); - } - return navMatrix; - } - - private static void CreateNavigationMatrix(MapModeObject root, ref ShipLogAstroObject[][] navMatrix) - { - if (root.astroObject != null) - { - navMatrix[root.y][root.x] = root.astroObject; - } - foreach (MapModeObject child in root.children) - { - CreateNavigationMatrix(child, ref navMatrix); - } - } - - private static void MakeAllNodes(ref MapModeObject parentNode, GameObject parent, int layer) - { - MakeNode(ref parentNode, parent, layer); - for (var i = 0; i < parentNode.children.Count; i++) - { - MapModeObject child = parentNode.children[i]; - MakeAllNodes(ref child, parent, layer); - parentNode.children[i] = child; - } - } - - private static GameObject CreateImage(GameObject nodeGO, IModAssets assets, string imagePath, string name, int layer) - { - GameObject newImageGO = new GameObject(name); - newImageGO.layer = layer; - newImageGO.transform.SetParent(nodeGO.transform); - - RectTransform transform = newImageGO.AddComponent(); - transform.localPosition = Vector3.zero; - transform.localRotation = Quaternion.identity; - transform.localScale = Vector3.one; - - Image newImage = newImageGO.AddComponent(); - if (imagePath == "DEFAULT") - { - newImage.sprite = Locator.GetShipLogManager()._shipLogLibrary.defaultEntrySprite; - } - else - { - Texture2D newTexture = assets.GetTexture(imagePath); - Rect rect = new Rect(0, 0, newTexture.width, newTexture.height); - Vector2 pivot = new Vector2(newTexture.width / 2, newTexture.height / 2); - newImage.sprite = Sprite.Create(newTexture, rect, pivot); - } - return newImageGO; - } - - public static T KeyByValue(Dictionary dict, W val) - { - T key = default; - foreach (KeyValuePair pair in dict) - { - if (EqualityComparer.Default.Equals(pair.Value, val)) - { - key = pair.Key; - break; - } - } - return key; - } - - private static string GetAstroObjectId(NewHorizonsBody body) - { - if (astroIdToBody.ContainsValue(body)) - { - return KeyByValue(astroIdToBody, body); - } - else - { - return body.Config.Name; - } - } - - private static void CreateShipLogAstroObject(GameObject nodeGO, ref MapModeObject node, GameObject referenceUnviewedSprite, int layer) - { - const float unviewedIconOffset = 15; - ShipLogAstroObject astroObject = nodeGO.AddComponent(); - astroObject._id = GetAstroObjectId(node.mainBody); - - string imagePath = node.mainBody.Config.ShipLog?.mapMode?.revealedSprite ?? "DEFAULT"; - string outlinePath = node.mainBody.Config.ShipLog?.mapMode?.outlineSprite ?? imagePath; - astroObject._imageObj = CreateImage(nodeGO, node.mainBody.Mod.Assets, imagePath, node.mainBody.Config.Name + " Revealed", layer); - astroObject._outlineObj = CreateImage(nodeGO, node.mainBody.Mod.Assets, outlinePath, node.mainBody.Config.Name + " Outline", layer); - - astroObject._unviewedObj = Object.Instantiate(referenceUnviewedSprite, nodeGO.transform, false); - if (node.mainBody.Config.FocalPoint != null) - { - astroObject._imageObj.GetComponent().enabled = false; - astroObject._outlineObj.GetComponent().enabled = false; - astroObject._unviewedObj.GetComponent().enabled = false; - astroObject.transform.localScale = node.lastSibling.astroObject.transform.localScale; - } - astroObject._invisibleWhenHidden = node.mainBody.Config.ShipLog?.mapMode?.invisibleWhenHidden ?? false; - - Rect imageRect = astroObject._imageObj.GetComponent().rect; - astroObject._unviewedObj.transform.localPosition = new Vector3(imageRect.width / 2 + unviewedIconOffset, imageRect.height / 2 + unviewedIconOffset, 0); - node.astroObject = astroObject; - } - - private static void ConnectNodeToLastSibling(MapModeObject node) - { - Vector2 fromPosition = node.astroObject.transform.localPosition; - Vector2 toPosition = node.lastSibling.astroObject.transform.localPosition; - - GameObject newLink = new GameObject("Line_ShipLog"); - newLink.layer = node.astroObject.gameObject.layer; - newLink.SetActive(false); - - RectTransform transform = newLink.AddComponent(); - transform.SetParent(node.astroObject.transform.parent); - Vector2 center = toPosition + (fromPosition - toPosition) / 2; - transform.localPosition = new Vector3(center.x, center.y, -1); - transform.localRotation = Quaternion.identity; - transform.localScale = node.level % 2 == 0 ? new Vector3(node.astroObject.transform.localScale.x / 5f, Mathf.Abs(fromPosition.y - toPosition.y) / 100f, 1) : new Vector3(Mathf.Abs(fromPosition.x - toPosition.x) / 100f, node.astroObject.transform.localScale.y / 5f , 1); - Image linkImage = newLink.AddComponent(); - linkImage.color = new Color(0.28f, 0.28f, 0.5f, 0.28f); - - ShipLogModule.ShipLogDetailInfo linkDetailInfo = new ShipLogModule.ShipLogDetailInfo() - { - invisibleWhenHidden = node.mainBody.Config.ShipLog?.mapMode?.invisibleWhenHidden ?? false - }; - - ShipLogDetail linkDetail = newLink.AddComponent(); - linkDetail.Init(linkDetailInfo, linkImage, linkImage); - - transform.SetParent(node.astroObject.transform); - transform.SetAsFirstSibling(); - newLink.SetActive(true); - } - - private static void MakeDetail(ShipLogModule.ShipLogDetailInfo info, Transform parent, IModAssets assets) - { - GameObject detailGameObject = new GameObject("Detail"); - detailGameObject.transform.SetParent(parent); - detailGameObject.SetActive(false); - - RectTransform detailTransform = detailGameObject.AddComponent(); - detailTransform.localPosition = (Vector2)(info.position ?? new MVector2(0, 0)); - detailTransform.localRotation = Quaternion.Euler(0f, 0f, info.rotation); - detailTransform.localScale = (Vector2)(info.scale ?? new MVector2(0, 0)); - - string revealedPath = info.revealedSprite ?? "DEFAULT"; - string outlinePath = info.outlineSprite ?? revealedPath; - - Image revealedImage = CreateImage(detailGameObject, assets, revealedPath, "Detail Revealed", parent.gameObject.layer).GetComponent(); - Image outlineImage = CreateImage(detailGameObject, assets, outlinePath, "Detail Outline", parent.gameObject.layer).GetComponent(); - - ShipLogDetail detail = detailGameObject.AddComponent(); - detail.Init(info, revealedImage, outlineImage); - detailGameObject.SetActive(true); - } - - private static void MakeDetails(MapModeObject node) - { - if (node.mainBody.Config.ShipLog?.mapMode?.details?.Length > 0) - { - GameObject detailsParent = new GameObject("Details"); - detailsParent.transform.SetParent(node.astroObject.transform); - detailsParent.SetActive(false); - - RectTransform detailsTransform = detailsParent.AddComponent(); - detailsTransform.localPosition = Vector3.zero; - detailsTransform.localRotation = Quaternion.identity; - detailsTransform.localScale = Vector3.one; - - foreach (ShipLogModule.ShipLogDetailInfo detailInfo in node.mainBody.Config.ShipLog.mapMode.details) - { - MakeDetail(detailInfo, detailsTransform, node.mainBody.Mod.Assets); - } - detailsParent.SetActive(true); - } - } - - private static void MakeNode(ref MapModeObject node, GameObject parent, int layer) - { - const float padding = 250f; - - GameObject newNodeGO = new GameObject(node.mainBody.Config.Name + "_ShipLog"); - newNodeGO.layer = layer; - newNodeGO.transform.SetParent(parent.transform); - - RectTransform transform = newNodeGO.AddComponent(); - float scale = node.mainBody.Config.ShipLog?.mapMode?.scale?? 1f; - Vector2 position = Vector2.zero; - if (node.lastSibling != null) - { - ShipLogAstroObject lastAstroObject = node.lastSibling.astroObject; - Vector3 lastPosition = lastAstroObject.transform.localPosition; - position = lastPosition; - float extraDistance = (node.mainBody.Config.ShipLog?.mapMode?.offset ?? 0f) * 100; - if (node.level % 2 == 0) - { - position.y += padding * (node.y - node.lastSibling.y) + extraDistance; - } - else - { - position.x += padding * (node.x - node.lastSibling.x) + extraDistance; - } - } - transform.localPosition = new Vector3(position.x, position.y, 0); - transform.localRotation = Quaternion.identity; - transform.localScale = Vector3.one * scale; - CreateShipLogAstroObject(newNodeGO, ref node, GameObject.Find(PAN_ROOT_PATH + "/TimberHearth/UnviewedIcon"), layer); - if (node.lastSibling != null) ConnectNodeToLastSibling(node); - MakeDetails(node); - transform.SetAsFirstSibling(); - } - - private static MapModeObject ConstructPrimaryNode(string systemName) - { - foreach (NewHorizonsBody body in Main.BodyDict[systemName].Where(b => b.Config.Base.CenterOfSolarSystem)) - { - List searchList = Main.BodyDict[systemName].Where(b => (b.Config.ShipLog?.mapMode?.remove ?? false) == false).ToList(); - searchList.Sort((b, o) => b.Config.Orbit.SemiMajorAxis.CompareTo(o.Config.Orbit.SemiMajorAxis)); - MapModeObject newNode = new MapModeObject - { - mainBody = body, - level = 0, - x = 0, - y = 0 - }; - newNode.children = ConstructChildrenNodes(systemName, newNode, searchList); - return newNode; - } - Logger.LogError("Couldn't find center of system!"); - return new MapModeObject(); - } - - private static List ConstructChildrenNodes(string systemName, MapModeObject parent, List searchList) - { - List children = new List(); - int newX = parent.x; - int newY = parent.y; - int newLevel = parent.level + 1; - MapModeObject lastSibling = parent; - foreach (NewHorizonsBody body in searchList.Where(b => b.Config.Orbit.PrimaryBody == parent.mainBody.Config.Name)) - { - if (body.Config.Orbit.PrimaryBody == parent.mainBody.Config.Name) - { - bool even = newLevel % 2 == 0; - newX = even ? newX : newX + 1; - newY = even ? newY + 1 : newY; - MapModeObject newNode = new MapModeObject() - { - mainBody = body, - level = newLevel, - x = newX, - y = newY, - parent = parent, - lastSibling = lastSibling - }; - newNode.children = ConstructChildrenNodes(systemName, newNode, searchList); - if (even) - { - newY += newNode.branch_height; - parent.increment_height(); - newY += 1; - } - else - { - newX += newNode.branch_width; - parent.increment_width(); - newX += 1; - } - lastSibling = newNode; - children.Add(newNode); - } - } - return children; - } - } - #endregion - - #region Rumor Mode - public static class RumorModeBuilder - { - private static Dictionary curiosityColors = new Dictionary(); - private static Dictionary curiosityHighlightColors = new Dictionary(); - private static Dictionary rawNameToCuriosityName = new Dictionary(); - private static Dictionary entryIdToRawName = new Dictionary(); - - public static void AddCuriosityColors(ShipLogModule.CuriosityColorInfo[] newColors) - { - foreach (ShipLogModule.CuriosityColorInfo newColor in newColors) - { - if (rawNameToCuriosityName.ContainsKey(newColor.id) == false) - { - CuriosityName newName = (CuriosityName) 8 + rawNameToCuriosityName.Count; - rawNameToCuriosityName.Add(newColor.id, newName); - curiosityColors.Add(newName, newColor.color.ToColor()); - curiosityHighlightColors.Add(newName, newColor.highlightColor.ToColor()); - } - } - } - - public static Color GetCuriosityColor(CuriosityName curiosityName, bool highlighted, Color defaultColor, Color defaultHighlight) - { - if (curiosityColors.ContainsKey(curiosityName) && curiosityHighlightColors.ContainsKey(curiosityName)) - { - return (highlighted ? curiosityHighlightColors : curiosityColors)[curiosityName]; - } - else - { - return highlighted? defaultHighlight : defaultColor; - } - } - - public static void AddBodyToShipLog(ShipLogManager manager, NewHorizonsBody body) - { - string systemName = body.Config.StarSystem; - XElement astroBodyFile = XElement.Load(Main.Instance.ModHelper.Manifest.ModFolderPath + body.Config.ShipLog.xmlFile); - XElement astroBodyId = astroBodyFile.Element("ID"); - if (astroBodyId == null) - { - Logger.LogError("Failed to load ship logs for " + systemName + "!"); - } - else - { - astroBodyId.SetValue(systemName + "/" + astroBodyId.Value); - foreach (XElement entryElement in astroBodyFile.DescendantsAndSelf("Entry")) - { - XElement curiosityName = entryElement.Element("Curiosity"); - XElement id = entryElement.Element("ID"); - if (curiosityName != null && id != null && entryIdToRawName.ContainsKey(id.Value) == false) - { - entryIdToRawName.Add(id.Value, curiosityName.Value); - } - AddTranslation(entryElement); - } - TextAsset newAsset = new TextAsset(astroBodyFile.ToString()); - List newBodies = new List(manager._shipLogXmlAssets) {newAsset}; - manager._shipLogXmlAssets = newBodies.ToArray(); - if (astroIdToBody.ContainsKey(astroBodyId.Value) == false) - { - astroIdToBody.Add(astroBodyId.Value, body); - } - } - } - - public static void GenerateEntryData(ShipLogManager manager) - { - const int step = 400; - int colAccumulator = 0; - int rowAccumulator = 0; - foreach(ShipLogEntry entry in manager._entryList) - { - if (manager._entryDataDict.ContainsKey(entry._id) == false) - { - NewHorizonsBody body = GetConfigFromEntry(entry); - Vector2? manualEntryPosition = GetManualEntryPosition(entry._id, body.Config.ShipLog); - Vector2 entryPosition; - if (manualEntryPosition == null) - { - entryPosition = new Vector2(colAccumulator, rowAccumulator); - } - else - { - entryPosition = (Vector2) manualEntryPosition; - } - EntryData newData = new EntryData - { - id = entry._id, - cardPosition = entryPosition, - sprite = body.Config.ShipLog.spriteFolder == null? null : GetEntrySprite(entry._id, body) - }; - entry.SetSprite(newData.sprite == null? manager._shipLogLibrary.defaultEntrySprite : newData.sprite); - manager._entryDataDict.Add(entry._id, newData); - int index = manager._entryList.IndexOf(entry); - if (index < manager._entryList.Count - 2 && manager._entryList[index + 1]._astroObjectID != entry._astroObjectID) - { - rowAccumulator += step; - colAccumulator = 0; - } - else - { - colAccumulator += step; - } - } - } - } - - private static void AddTranslation(XElement entry) - { - Dictionary table = TextTranslation.Get().m_table.theShipLogTable; - XElement nameElement = entry.Element("Name"); - if (nameElement != null) - { - string name = nameElement.Value; - table[name] = name; - foreach (XElement rumorFact in entry.Elements("RumorFact")) - { - XElement rumorName = rumorFact.Element("RumorName"); - if (rumorName != null) - { - table[rumorName.Value] = rumorName.Value; - } - - XElement rumorText = rumorFact.Element("Text"); - if (rumorText != null) - { - table[name + rumorText.Value] = rumorText.Value; - } - } - foreach (XElement exploreFact in entry.Elements("ExploreFact")) - { - XElement exploreText = exploreFact.Element("Text"); - if (exploreText != null) - { - table[name + exploreText.Value] = exploreText.Value; - } - } - } - } - - public static void UpdateEntryCuriosity(ref ShipLogEntry entry) - { - if (entryIdToRawName.ContainsKey(entry._id)) - { - entry._curiosity = rawNameToCuriosityName[entryIdToRawName[entry._id]]; - } - } - - private static Sprite GetEntrySprite(string entryId, NewHorizonsBody body) - { - IModAssets assets = body.Mod.Assets; - string path = body.Config.ShipLog.spriteFolder + "/" + entryId + ".png"; - if (File.Exists(Main.Instance.ModHelper.Manifest.ModFolderPath + path)) - { - Texture2D newTexture = assets.GetTexture(path); - Rect rect = new Rect(0, 0, newTexture.width, newTexture.height); - Vector2 pivot = new Vector2(newTexture.width / 2, newTexture.height / 2); - return Sprite.Create(newTexture, rect, pivot); - } - else - { - return null; - } - } - - private static Vector2? GetManualEntryPosition(string entryId, ShipLogModule config) - { - if (config.positions == null) return null; - foreach (ShipLogModule.EntryPositionInfo position in config.positions) - { - if (position.id == entryId) - { - return position.position; - } - } - return null; - } - } - #endregion - - #region Fact Reveals - public static class RevealBuilder - { - public static void Make(GameObject go, Sector sector, PropModule.RevealInfo info, IModHelper mod) - { - GameObject newRevealGO = MakeGameObject(go, sector, info, mod); - switch (info.revealOn.ToLower()) - { - case "enter": - MakeTrigger(newRevealGO, sector, info, mod); - break; - case "observe": - MakeObservable(newRevealGO, sector, info, mod); - break; - case "snapshot": - MakeSnapshot(newRevealGO, sector, info, mod); - break; - default: - Logger.LogError("Invalid revealOn: " + info.revealOn); - break; - } - - newRevealGO.SetActive(true); - } - - private static SphereShape MakeShape(GameObject go, PropModule.RevealInfo info, Shape.CollisionMode collisionMode) - { - SphereShape newShape = go.AddComponent(); - newShape.radius = info.radius; - newShape.SetCollisionMode(collisionMode); - return newShape; - } - - private static GameObject MakeGameObject(GameObject go, Sector sector, PropModule.RevealInfo info, IModHelper mod) - { - GameObject revealTriggerVolume = new GameObject("Reveal Volume (" + info.revealOn + ")"); - revealTriggerVolume.SetActive(false); - revealTriggerVolume.transform.parent = sector?.transform ?? go.transform; - revealTriggerVolume.transform.localPosition = info.position; - return revealTriggerVolume; - } - - private static void MakeTrigger(GameObject go, Sector sector, PropModule.RevealInfo info, IModHelper mod) - { - SphereShape newShape = MakeShape(go, info, Shape.CollisionMode.Volume); - OWTriggerVolume newVolume = go.AddComponent(); - newVolume._shape = newShape; - ShipLogFactListTriggerVolume volume = go.AddComponent(); - volume._factIDs = info.reveals; - } - - private static void MakeObservable(GameObject go, Sector sector, PropModule.RevealInfo info, IModHelper mod) - { - go.layer = LayerMask.NameToLayer("Interactible"); - SphereCollider newSphere = go.AddComponent(); - newSphere.radius = info.radius; - OWCollider newCollider = go.AddComponent(); - ShipLogFactObserveTrigger newObserveTrigger = go.AddComponent(); - newObserveTrigger._factIDs = info.reveals; - newObserveTrigger._maxViewDistance = info.maxDistance == -1f ? 2f : info.maxDistance; - newObserveTrigger._maxViewAngle = info.maxAngle; - newObserveTrigger._owCollider = newCollider; - newObserveTrigger._disableColliderOnRevealFact = true; - } - - private static void MakeSnapshot(GameObject go, Sector sector, PropModule.RevealInfo info, IModHelper mod) - { - SphereShape newShape = MakeShape(go, info, Shape.CollisionMode.Manual); - ShapeVisibilityTracker newTracker = go.AddComponent(); - newTracker._shapes = new Shape[] {newShape}; - ShipLogFactSnapshotTrigger newSnapshotTrigger = go.AddComponent(); - newSnapshotTrigger._maxDistance = info.maxDistance == -1f ? 200f : info.maxDistance; - newSnapshotTrigger._factIDs = info.reveals; - } - } - #endregion - - #region Entry Locations - public static class EntryLocationBuilder - { - private static List locationsToInitialize = new List(); - public static void Make(GameObject go, Sector sector, PropModule.EntryLocationInfo info, IModHelper mod) - { - GameObject entryLocationGameObject = new GameObject("Entry Location (" + info.id + ")"); - entryLocationGameObject.SetActive(false); - entryLocationGameObject.transform.parent = sector?.transform ?? go.transform; - entryLocationGameObject.transform.localPosition = info.position; - ShipLogEntryLocation newLocation = entryLocationGameObject.AddComponent(); - newLocation._entryID = info.id; - newLocation._isWithinCloakField = info.cloaked; - locationsToInitialize.Add(newLocation); - entryLocationGameObject.SetActive(true); - } - - public static void InitializeLocations() - { - locationsToInitialize.ForEach(l => l.InitEntry()); - locationsToInitialize.Clear(); - } - } - #endregion - } -} diff --git a/NewHorizons/Builder/Props/PropBuildManager.cs b/NewHorizons/Builder/Props/PropBuildManager.cs index eadbfe7c..aecb8ad9 100644 --- a/NewHorizons/Builder/Props/PropBuildManager.cs +++ b/NewHorizons/Builder/Props/PropBuildManager.cs @@ -11,6 +11,7 @@ using System.Reflection; using NewHorizons.Builder.General; using NewHorizons.Utility; using OWML.Common; +using NewHorizons.Builder.ShipLog; namespace NewHorizons.Builder.Props { @@ -58,14 +59,14 @@ namespace NewHorizons.Builder.Props { foreach (var revealInfo in config.Props.Reveal) { - ShipLogBuilder.RevealBuilder.Make(go, sector, revealInfo, mod); + RevealBuilder.Make(go, sector, revealInfo, mod); } } if (config.Props.EntryLocation != null) { foreach (var entryLocationInfo in config.Props.EntryLocation) { - ShipLogBuilder.EntryLocationBuilder.Make(go, sector, entryLocationInfo, mod); + EntryLocationBuilder.Make(go, sector, entryLocationInfo, mod); } } } diff --git a/NewHorizons/Builder/ShipLog/EntryLocationBuilder.cs b/NewHorizons/Builder/ShipLog/EntryLocationBuilder.cs new file mode 100644 index 00000000..905b76bd --- /dev/null +++ b/NewHorizons/Builder/ShipLog/EntryLocationBuilder.cs @@ -0,0 +1,37 @@ +using NewHorizons.Components; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml.Linq; +using NewHorizons.External; +using NewHorizons.Utility; +using OWML.Common; +using UnityEngine; +using UnityEngine.UI; +using Logger = NewHorizons.Utility.Logger; + +namespace NewHorizons.Builder.ShipLog +{ + public static class EntryLocationBuilder + { + private static readonly List _locationsToInitialize = new List(); + public static void Make(GameObject go, Sector sector, PropModule.EntryLocationInfo info, IModHelper mod) + { + GameObject entryLocationGameObject = new GameObject("Entry Location (" + info.id + ")"); + entryLocationGameObject.SetActive(false); + entryLocationGameObject.transform.parent = sector?.transform ?? go.transform; + entryLocationGameObject.transform.localPosition = info.position; + ShipLogEntryLocation newLocation = entryLocationGameObject.AddComponent(); + newLocation._entryID = info.id; + newLocation._isWithinCloakField = info.cloaked; + _locationsToInitialize.Add(newLocation); + entryLocationGameObject.SetActive(true); + } + + public static void InitializeLocations() + { + _locationsToInitialize.ForEach(l => l.InitEntry()); + _locationsToInitialize.Clear(); + } + } +} diff --git a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs new file mode 100644 index 00000000..cfe6051d --- /dev/null +++ b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs @@ -0,0 +1,319 @@ +using NewHorizons.Components; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml.Linq; +using NewHorizons.External; +using NewHorizons.Utility; +using OWML.Common; +using UnityEngine; +using UnityEngine.UI; +using Logger = NewHorizons.Utility.Logger; +using NewHorizons.Builder.Handlers; + +namespace NewHorizons.Builder.ShipLog +{ + public static class MapModeBuilder + { + private class MapModeObject + { + public int x; + public int y; + public int branch_width; + public int branch_height; + public int level; + public NewHorizonsBody mainBody; + public ShipLogAstroObject astroObject; + public List children; + public MapModeObject parent; + public MapModeObject lastSibling; + public void Increment_width() + { + branch_width++; + parent?.Increment_width(); + } + public void Increment_height() + { + branch_height++; + parent?.Increment_height(); + } + } + + public static string GetAstroBodyShipLogName(string id) + { + return ShipLogHandler.GetConfigFromID(id)?.Config?.Name ?? id; + } + + public static ShipLogAstroObject[][] ConstructMapMode(string systemName, GameObject transformParent, int layer) + { + MapModeObject rootObject = ConstructPrimaryNode(systemName); + if (rootObject.mainBody != null) + { + MakeAllNodes(ref rootObject, transformParent, layer); + } + + int maxAmount = Main.BodyDict[Main.Instance.CurrentStarSystem].Count; + ShipLogAstroObject[][] navMatrix = new ShipLogAstroObject[maxAmount][]; + for (int i = 0; i < maxAmount; i++) + { + navMatrix[i] = new ShipLogAstroObject[maxAmount]; + } + + CreateNavigationMatrix(rootObject, ref navMatrix); + navMatrix = navMatrix.Where(a => a.Count(c => c != null) > 0).Prepend(new ShipLogAstroObject[1]).ToArray(); + for (var index = 0; index < navMatrix.Length; index++) + { + navMatrix[index] = navMatrix[index].Where(a => a != null).ToArray(); + } + return navMatrix; + } + + private static void CreateNavigationMatrix(MapModeObject root, ref ShipLogAstroObject[][] navMatrix) + { + if (root.astroObject != null) + { + navMatrix[root.y][root.x] = root.astroObject; + } + foreach (MapModeObject child in root.children) + { + CreateNavigationMatrix(child, ref navMatrix); + } + } + + private static void MakeAllNodes(ref MapModeObject parentNode, GameObject parent, int layer) + { + MakeNode(ref parentNode, parent, layer); + for (var i = 0; i < parentNode.children.Count; i++) + { + MapModeObject child = parentNode.children[i]; + MakeAllNodes(ref child, parent, layer); + parentNode.children[i] = child; + } + } + + private static GameObject CreateImage(GameObject nodeGO, IModAssets assets, string imagePath, string name, int layer) + { + GameObject newImageGO = new GameObject(name); + newImageGO.layer = layer; + newImageGO.transform.SetParent(nodeGO.transform); + + RectTransform transform = newImageGO.AddComponent(); + transform.localPosition = Vector3.zero; + transform.localRotation = Quaternion.identity; + transform.localScale = Vector3.one; + + Image newImage = newImageGO.AddComponent(); + if (imagePath == "DEFAULT") + { + newImage.sprite = Locator.GetShipLogManager()._shipLogLibrary.defaultEntrySprite; + } + else + { + Texture2D newTexture = assets.GetTexture(imagePath); + Rect rect = new Rect(0, 0, newTexture.width, newTexture.height); + Vector2 pivot = new Vector2(newTexture.width / 2, newTexture.height / 2); + newImage.sprite = Sprite.Create(newTexture, rect, pivot); + } + return newImageGO; + } + + private static void CreateShipLogAstroObject(GameObject nodeGO, ref MapModeObject node, GameObject referenceUnviewedSprite, int layer) + { + const float unviewedIconOffset = 15; + ShipLogAstroObject astroObject = nodeGO.AddComponent(); + astroObject._id = ShipLogHandler.GetAstroObjectId(node.mainBody); + + string imagePath = node.mainBody.Config.ShipLog?.mapMode?.revealedSprite ?? "DEFAULT"; + string outlinePath = node.mainBody.Config.ShipLog?.mapMode?.outlineSprite ?? imagePath; + astroObject._imageObj = CreateImage(nodeGO, node.mainBody.Mod.Assets, imagePath, node.mainBody.Config.Name + " Revealed", layer); + astroObject._outlineObj = CreateImage(nodeGO, node.mainBody.Mod.Assets, outlinePath, node.mainBody.Config.Name + " Outline", layer); + + astroObject._unviewedObj = Object.Instantiate(referenceUnviewedSprite, nodeGO.transform, false); + if (node.mainBody.Config.FocalPoint != null) + { + astroObject._imageObj.GetComponent().enabled = false; + astroObject._outlineObj.GetComponent().enabled = false; + astroObject._unviewedObj.GetComponent().enabled = false; + astroObject.transform.localScale = node.lastSibling.astroObject.transform.localScale; + } + astroObject._invisibleWhenHidden = node.mainBody.Config.ShipLog?.mapMode?.invisibleWhenHidden ?? false; + + Rect imageRect = astroObject._imageObj.GetComponent().rect; + astroObject._unviewedObj.transform.localPosition = new Vector3(imageRect.width / 2 + unviewedIconOffset, imageRect.height / 2 + unviewedIconOffset, 0); + node.astroObject = astroObject; + } + + private static void ConnectNodeToLastSibling(MapModeObject node) + { + Vector2 fromPosition = node.astroObject.transform.localPosition; + Vector2 toPosition = node.lastSibling.astroObject.transform.localPosition; + + GameObject newLink = new GameObject("Line_ShipLog"); + newLink.layer = node.astroObject.gameObject.layer; + newLink.SetActive(false); + + RectTransform transform = newLink.AddComponent(); + transform.SetParent(node.astroObject.transform.parent); + Vector2 center = toPosition + (fromPosition - toPosition) / 2; + transform.localPosition = new Vector3(center.x, center.y, -1); + transform.localRotation = Quaternion.identity; + transform.localScale = node.level % 2 == 0 ? new Vector3(node.astroObject.transform.localScale.x / 5f, Mathf.Abs(fromPosition.y - toPosition.y) / 100f, 1) : new Vector3(Mathf.Abs(fromPosition.x - toPosition.x) / 100f, node.astroObject.transform.localScale.y / 5f, 1); + Image linkImage = newLink.AddComponent(); + linkImage.color = new Color(0.28f, 0.28f, 0.5f, 0.28f); + + ShipLogModule.ShipLogDetailInfo linkDetailInfo = new ShipLogModule.ShipLogDetailInfo() + { + invisibleWhenHidden = node.mainBody.Config.ShipLog?.mapMode?.invisibleWhenHidden ?? false + }; + + ShipLogDetail linkDetail = newLink.AddComponent(); + linkDetail.Init(linkDetailInfo, linkImage, linkImage); + + transform.SetParent(node.astroObject.transform); + transform.SetAsFirstSibling(); + newLink.SetActive(true); + } + + private static void MakeDetail(ShipLogModule.ShipLogDetailInfo info, Transform parent, IModAssets assets) + { + GameObject detailGameObject = new GameObject("Detail"); + detailGameObject.transform.SetParent(parent); + detailGameObject.SetActive(false); + + RectTransform detailTransform = detailGameObject.AddComponent(); + detailTransform.localPosition = (Vector2)(info.position ?? new MVector2(0, 0)); + detailTransform.localRotation = Quaternion.Euler(0f, 0f, info.rotation); + detailTransform.localScale = (Vector2)(info.scale ?? new MVector2(0, 0)); + + string revealedPath = info.revealedSprite ?? "DEFAULT"; + string outlinePath = info.outlineSprite ?? revealedPath; + + Image revealedImage = CreateImage(detailGameObject, assets, revealedPath, "Detail Revealed", parent.gameObject.layer).GetComponent(); + Image outlineImage = CreateImage(detailGameObject, assets, outlinePath, "Detail Outline", parent.gameObject.layer).GetComponent(); + + ShipLogDetail detail = detailGameObject.AddComponent(); + detail.Init(info, revealedImage, outlineImage); + detailGameObject.SetActive(true); + } + + private static void MakeDetails(MapModeObject node) + { + if (node.mainBody.Config.ShipLog?.mapMode?.details?.Length > 0) + { + GameObject detailsParent = new GameObject("Details"); + detailsParent.transform.SetParent(node.astroObject.transform); + detailsParent.SetActive(false); + + RectTransform detailsTransform = detailsParent.AddComponent(); + detailsTransform.localPosition = Vector3.zero; + detailsTransform.localRotation = Quaternion.identity; + detailsTransform.localScale = Vector3.one; + + foreach (ShipLogModule.ShipLogDetailInfo detailInfo in node.mainBody.Config.ShipLog.mapMode.details) + { + MakeDetail(detailInfo, detailsTransform, node.mainBody.Mod.Assets); + } + detailsParent.SetActive(true); + } + } + + private static void MakeNode(ref MapModeObject node, GameObject parent, int layer) + { + const float padding = 250f; + + GameObject newNodeGO = new GameObject(node.mainBody.Config.Name + "_ShipLog"); + newNodeGO.layer = layer; + newNodeGO.transform.SetParent(parent.transform); + + RectTransform transform = newNodeGO.AddComponent(); + float scale = node.mainBody.Config.ShipLog?.mapMode?.scale ?? 1f; + Vector2 position = Vector2.zero; + if (node.lastSibling != null) + { + ShipLogAstroObject lastAstroObject = node.lastSibling.astroObject; + Vector3 lastPosition = lastAstroObject.transform.localPosition; + position = lastPosition; + float extraDistance = (node.mainBody.Config.ShipLog?.mapMode?.offset ?? 0f) * 100; + if (node.level % 2 == 0) + { + position.y += padding * (node.y - node.lastSibling.y) + extraDistance; + } + else + { + position.x += padding * (node.x - node.lastSibling.x) + extraDistance; + } + } + transform.localPosition = new Vector3(position.x, position.y, 0); + transform.localRotation = Quaternion.identity; + transform.localScale = Vector3.one * scale; + CreateShipLogAstroObject(newNodeGO, ref node, GameObject.Find("Ship_Body/Module_Cabin/Systems_Cabin/ShipLogPivot/ShipLog/ShipLogPivot/ShipLogCanvas/MapMode/ScaleRoot/PanRoot/TimberHearth/UnviewedIcon"), layer); + if (node.lastSibling != null) ConnectNodeToLastSibling(node); + MakeDetails(node); + transform.SetAsFirstSibling(); + } + + private static MapModeObject ConstructPrimaryNode(string systemName) + { + foreach (NewHorizonsBody body in Main.BodyDict[systemName].Where(b => b.Config.Base.CenterOfSolarSystem)) + { + List searchList = Main.BodyDict[systemName].Where(b => (b.Config.ShipLog?.mapMode?.remove ?? false) == false).ToList(); + searchList.Sort((b, o) => b.Config.Orbit.SemiMajorAxis.CompareTo(o.Config.Orbit.SemiMajorAxis)); + MapModeObject newNode = new MapModeObject + { + mainBody = body, + level = 0, + x = 0, + y = 0 + }; + newNode.children = ConstructChildrenNodes(systemName, newNode, searchList); + return newNode; + } + Logger.LogError("Couldn't find center of system!"); + return new MapModeObject(); + } + + private static List ConstructChildrenNodes(string systemName, MapModeObject parent, List searchList) + { + List children = new List(); + int newX = parent.x; + int newY = parent.y; + int newLevel = parent.level + 1; + MapModeObject lastSibling = parent; + foreach (NewHorizonsBody body in searchList.Where(b => b.Config.Orbit.PrimaryBody == parent.mainBody.Config.Name)) + { + if (body.Config.Orbit.PrimaryBody == parent.mainBody.Config.Name) + { + bool even = newLevel % 2 == 0; + newX = even ? newX : newX + 1; + newY = even ? newY + 1 : newY; + MapModeObject newNode = new MapModeObject() + { + mainBody = body, + level = newLevel, + x = newX, + y = newY, + parent = parent, + lastSibling = lastSibling + }; + newNode.children = ConstructChildrenNodes(systemName, newNode, searchList); + if (even) + { + newY += newNode.branch_height; + parent.Increment_height(); + newY += 1; + } + else + { + newX += newNode.branch_width; + parent.Increment_width(); + newX += 1; + } + lastSibling = newNode; + children.Add(newNode); + } + } + return children; + } + } + +} diff --git a/NewHorizons/Builder/ShipLog/RevealBuilder.cs b/NewHorizons/Builder/ShipLog/RevealBuilder.cs new file mode 100644 index 00000000..ef8c5aae --- /dev/null +++ b/NewHorizons/Builder/ShipLog/RevealBuilder.cs @@ -0,0 +1,89 @@ +using NewHorizons.Components; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml.Linq; +using NewHorizons.External; +using NewHorizons.Utility; +using OWML.Common; +using UnityEngine; +using UnityEngine.UI; +using Logger = NewHorizons.Utility.Logger; + +namespace NewHorizons.Builder.ShipLog +{ + public static class RevealBuilder + { + public static void Make(GameObject go, Sector sector, PropModule.RevealInfo info, IModHelper mod) + { + GameObject newRevealGO = MakeGameObject(go, sector, info, mod); + switch (info.revealOn.ToLower()) + { + case "enter": + MakeTrigger(newRevealGO, sector, info, mod); + break; + case "observe": + MakeObservable(newRevealGO, sector, info, mod); + break; + case "snapshot": + MakeSnapshot(newRevealGO, sector, info, mod); + break; + default: + Logger.LogError("Invalid revealOn: " + info.revealOn); + break; + } + + newRevealGO.SetActive(true); + } + + private static SphereShape MakeShape(GameObject go, PropModule.RevealInfo info, Shape.CollisionMode collisionMode) + { + SphereShape newShape = go.AddComponent(); + newShape.radius = info.radius; + newShape.SetCollisionMode(collisionMode); + return newShape; + } + + private static GameObject MakeGameObject(GameObject go, Sector sector, PropModule.RevealInfo info, IModHelper mod) + { + GameObject revealTriggerVolume = new GameObject("Reveal Volume (" + info.revealOn + ")"); + revealTriggerVolume.SetActive(false); + revealTriggerVolume.transform.parent = sector?.transform ?? go.transform; + revealTriggerVolume.transform.localPosition = info.position; + return revealTriggerVolume; + } + + private static void MakeTrigger(GameObject go, Sector sector, PropModule.RevealInfo info, IModHelper mod) + { + SphereShape newShape = MakeShape(go, info, Shape.CollisionMode.Volume); + OWTriggerVolume newVolume = go.AddComponent(); + newVolume._shape = newShape; + ShipLogFactListTriggerVolume volume = go.AddComponent(); + volume._factIDs = info.reveals; + } + + private static void MakeObservable(GameObject go, Sector sector, PropModule.RevealInfo info, IModHelper mod) + { + go.layer = LayerMask.NameToLayer("Interactible"); + SphereCollider newSphere = go.AddComponent(); + newSphere.radius = info.radius; + OWCollider newCollider = go.AddComponent(); + ShipLogFactObserveTrigger newObserveTrigger = go.AddComponent(); + newObserveTrigger._factIDs = info.reveals; + newObserveTrigger._maxViewDistance = info.maxDistance == -1f ? 2f : info.maxDistance; + newObserveTrigger._maxViewAngle = info.maxAngle; + newObserveTrigger._owCollider = newCollider; + newObserveTrigger._disableColliderOnRevealFact = true; + } + + private static void MakeSnapshot(GameObject go, Sector sector, PropModule.RevealInfo info, IModHelper mod) + { + SphereShape newShape = MakeShape(go, info, Shape.CollisionMode.Manual); + ShapeVisibilityTracker newTracker = go.AddComponent(); + newTracker._shapes = new Shape[] { newShape }; + ShipLogFactSnapshotTrigger newSnapshotTrigger = go.AddComponent(); + newSnapshotTrigger._maxDistance = info.maxDistance == -1f ? 200f : info.maxDistance; + newSnapshotTrigger._factIDs = info.reveals; + } + } +} diff --git a/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs new file mode 100644 index 00000000..0c8869c6 --- /dev/null +++ b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs @@ -0,0 +1,191 @@ +using NewHorizons.Components; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml.Linq; +using NewHorizons.External; +using NewHorizons.Utility; +using OWML.Common; +using UnityEngine; +using UnityEngine.UI; +using Logger = NewHorizons.Utility.Logger; +using NewHorizons.Builder.Handlers; + +namespace NewHorizons.Builder.ShipLog +{ + public static class RumorModeBuilder + { + private static readonly Dictionary _curiosityColors = new Dictionary(); + private static readonly Dictionary _curiosityHighlightColors = new Dictionary(); + private static readonly Dictionary _rawNameToCuriosityName = new Dictionary(); + private static readonly Dictionary _entryIdToRawName = new Dictionary(); + + public static void AddCuriosityColors(ShipLogModule.CuriosityColorInfo[] newColors) + { + foreach (ShipLogModule.CuriosityColorInfo newColor in newColors) + { + if (_rawNameToCuriosityName.ContainsKey(newColor.id) == false) + { + CuriosityName newName = (CuriosityName)8 + _rawNameToCuriosityName.Count; + _rawNameToCuriosityName.Add(newColor.id, newName); + _curiosityColors.Add(newName, newColor.color.ToColor()); + _curiosityHighlightColors.Add(newName, newColor.highlightColor.ToColor()); + } + } + } + + public static Color GetCuriosityColor(CuriosityName curiosityName, bool highlighted, Color defaultColor, Color defaultHighlight) + { + if (_curiosityColors.ContainsKey(curiosityName) && _curiosityHighlightColors.ContainsKey(curiosityName)) + { + return (highlighted ? _curiosityHighlightColors : _curiosityColors)[curiosityName]; + } + else + { + return highlighted ? defaultHighlight : defaultColor; + } + } + + public static void AddBodyToShipLog(ShipLogManager manager, NewHorizonsBody body) + { + string systemName = body.Config.StarSystem; + XElement astroBodyFile = XElement.Load(Main.Instance.ModHelper.Manifest.ModFolderPath + body.Config.ShipLog.xmlFile); + XElement astroBodyId = astroBodyFile.Element("ID"); + if (astroBodyId == null) + { + Logger.LogError("Failed to load ship logs for " + systemName + "!"); + } + else + { + astroBodyId.SetValue(systemName + "/" + astroBodyId.Value); + foreach (XElement entryElement in astroBodyFile.DescendantsAndSelf("Entry")) + { + XElement curiosityName = entryElement.Element("Curiosity"); + XElement id = entryElement.Element("ID"); + if (curiosityName != null && id != null && _entryIdToRawName.ContainsKey(id.Value) == false) + { + _entryIdToRawName.Add(id.Value, curiosityName.Value); + } + AddTranslation(entryElement); + } + TextAsset newAsset = new TextAsset(astroBodyFile.ToString()); + List newBodies = new List(manager._shipLogXmlAssets) { newAsset }; + manager._shipLogXmlAssets = newBodies.ToArray(); + ShipLogHandler.AddConfig(astroBodyId.Value, body); + } + } + + public static void GenerateEntryData(ShipLogManager manager) + { + const int step = 400; + int colAccumulator = 0; + int rowAccumulator = 0; + foreach (ShipLogEntry entry in manager._entryList) + { + if (manager._entryDataDict.ContainsKey(entry._id) == false) + { + NewHorizonsBody body = ShipLogHandler.GetConfigFromID(entry._astroObjectID); + Vector2? manualEntryPosition = GetManualEntryPosition(entry._id, body.Config.ShipLog); + Vector2 entryPosition; + if (manualEntryPosition == null) + { + entryPosition = new Vector2(colAccumulator, rowAccumulator); + } + else + { + entryPosition = (Vector2)manualEntryPosition; + } + EntryData newData = new EntryData + { + id = entry._id, + cardPosition = entryPosition, + sprite = body.Config.ShipLog.spriteFolder == null ? null : GetEntrySprite(entry._id, body) + }; + entry.SetSprite(newData.sprite == null ? manager._shipLogLibrary.defaultEntrySprite : newData.sprite); + manager._entryDataDict.Add(entry._id, newData); + int index = manager._entryList.IndexOf(entry); + if (index < manager._entryList.Count - 2 && manager._entryList[index + 1]._astroObjectID != entry._astroObjectID) + { + rowAccumulator += step; + colAccumulator = 0; + } + else + { + colAccumulator += step; + } + } + } + } + + private static void AddTranslation(XElement entry) + { + Dictionary table = TextTranslation.Get().m_table.theShipLogTable; + XElement nameElement = entry.Element("Name"); + if (nameElement != null) + { + string name = nameElement.Value; + table[name] = name; + foreach (XElement rumorFact in entry.Elements("RumorFact")) + { + XElement rumorName = rumorFact.Element("RumorName"); + if (rumorName != null) + { + table[rumorName.Value] = rumorName.Value; + } + + XElement rumorText = rumorFact.Element("Text"); + if (rumorText != null) + { + table[name + rumorText.Value] = rumorText.Value; + } + } + foreach (XElement exploreFact in entry.Elements("ExploreFact")) + { + XElement exploreText = exploreFact.Element("Text"); + if (exploreText != null) + { + table[name + exploreText.Value] = exploreText.Value; + } + } + } + } + + public static void UpdateEntryCuriosity(ref ShipLogEntry entry) + { + if (_entryIdToRawName.ContainsKey(entry._id)) + { + entry._curiosity = _rawNameToCuriosityName[_entryIdToRawName[entry._id]]; + } + } + + private static Sprite GetEntrySprite(string entryId, NewHorizonsBody body) + { + IModAssets assets = body.Mod.Assets; + string path = body.Config.ShipLog.spriteFolder + "/" + entryId + ".png"; + if (File.Exists(Main.Instance.ModHelper.Manifest.ModFolderPath + path)) + { + Texture2D newTexture = assets.GetTexture(path); + Rect rect = new Rect(0, 0, newTexture.width, newTexture.height); + Vector2 pivot = new Vector2(newTexture.width / 2, newTexture.height / 2); + return Sprite.Create(newTexture, rect, pivot); + } + else + { + return null; + } + } + + private static Vector2? GetManualEntryPosition(string entryId, ShipLogModule config) + { + if (config.positions == null) return null; + foreach (ShipLogModule.EntryPositionInfo position in config.positions) + { + if (position.id == entryId) + { + return position.position; + } + } + return null; + } + } +} diff --git a/NewHorizons/Handlers/ShipLogHandler.cs b/NewHorizons/Handlers/ShipLogHandler.cs new file mode 100644 index 00000000..ff565b54 --- /dev/null +++ b/NewHorizons/Handlers/ShipLogHandler.cs @@ -0,0 +1,46 @@ +using NewHorizons.Components; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml.Linq; +using NewHorizons.External; +using NewHorizons.Utility; +using OWML.Common; +using UnityEngine; +using UnityEngine.UI; +using Logger = NewHorizons.Utility.Logger; + +namespace NewHorizons.Builder.Handlers +{ + public static class ShipLogHandler + { + public static readonly string PAN_ROOT_PATH = "Ship_Body/Module_Cabin/Systems_Cabin/ShipLogPivot/ShipLog/ShipLogPivot/ShipLogCanvas/MapMode/ScaleRoot/PanRoot"; + + private static Dictionary _astroIdToBody = new Dictionary(); + + public static NewHorizonsBody GetConfigFromID(string id) + { + return _astroIdToBody.ContainsKey(id) ? _astroIdToBody[id] : null; + } + + public static void AddConfig(string id, NewHorizonsBody body) + { + if (!_astroIdToBody.ContainsKey(id)) + { + _astroIdToBody.Add(id, body); + } + } + + public static string GetAstroObjectId(NewHorizonsBody body) + { + if (_astroIdToBody.ContainsValue(body)) + { + return CollectionUtilities.KeyByValue(_astroIdToBody, body); + } + else + { + return body.Config.Name; + } + } + } +} diff --git a/NewHorizons/Tools/Patches.cs b/NewHorizons/Tools/Patches.cs index 2a74328e..91be6c27 100644 --- a/NewHorizons/Tools/Patches.cs +++ b/NewHorizons/Tools/Patches.cs @@ -17,6 +17,8 @@ using UnityEngine; using Logger = NewHorizons.Utility.Logger; using Object = UnityEngine.Object; using NewHorizons.Handlers; +using NewHorizons.Builder.ShipLog; +using NewHorizons.Builder.Handlers; namespace NewHorizons.Tools { @@ -409,25 +411,25 @@ namespace NewHorizons.Tools { if (body.Config.ShipLog?.curiosities != null) { - ShipLogBuilder.RumorModeBuilder.AddCuriosityColors(body.Config.ShipLog.curiosities); + RumorModeBuilder.AddCuriosityColors(body.Config.ShipLog.curiosities); } } foreach (NewHorizonsBody body in Main.BodyDict[Main.Instance.CurrentStarSystem]) { if (body.Config.ShipLog?.xmlFile != null) { - ShipLogBuilder.RumorModeBuilder.AddBodyToShipLog(__instance, body); + RumorModeBuilder.AddBodyToShipLog(__instance, body); } } } public static void OnShipLogManagerAwakeComplete(ShipLogManager __instance) { - ShipLogBuilder.RumorModeBuilder.GenerateEntryData(__instance); + RumorModeBuilder.GenerateEntryData(__instance); for (var i = 0; i < __instance._entryList.Count; i++) { ShipLogEntry logEntry = __instance._entryList[i]; - ShipLogBuilder.RumorModeBuilder.UpdateEntryCuriosity(ref logEntry); + RumorModeBuilder.UpdateEntryCuriosity(ref logEntry); } Logger.Log("Ship Log Generation Complete For: " + Main.Instance.CurrentStarSystem, Logger.LogType.Log); } @@ -472,7 +474,7 @@ namespace NewHorizons.Tools __instance.RevealFact(fact, false, false); } } - ShipLogBuilder.EntryLocationBuilder.InitializeLocations(); + EntryLocationBuilder.InitializeLocations(); return false; } } @@ -485,23 +487,23 @@ namespace NewHorizons.Tools } else { - __result = ShipLogBuilder.RumorModeBuilder.GetCuriosityColor(__0, __1, __instance._neutralColor, __instance._neutralHighlight); + __result = RumorModeBuilder.GetCuriosityColor(__0, __1, __instance._neutralColor, __instance._neutralHighlight); return false; } } private static void DeleteDetail(string name) { - Object.Destroy(GameObject.Find(ShipLogBuilder.PAN_ROOT_PATH + "/" + name)); + Object.Destroy(GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/" + name)); } public static void OnShipLogMapModeInitialize(ShipLogMapMode __instance) { if (Main.Instance.CurrentStarSystem != "SolarSystem") { - GameObject panRoot = GameObject.Find(ShipLogBuilder.PAN_ROOT_PATH); - GameObject sunObject = GameObject.Find(ShipLogBuilder.PAN_ROOT_PATH + "/Sun"); - ShipLogAstroObject[][] navMatrix = ShipLogBuilder.MapModeBuilder.ConstructMapMode(Main.Instance.CurrentStarSystem, panRoot, sunObject.layer); + GameObject panRoot = GameObject.Find(ShipLogHandler.PAN_ROOT_PATH); + GameObject sunObject = GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/Sun"); + ShipLogAstroObject[][] navMatrix = MapModeBuilder.ConstructMapMode(Main.Instance.CurrentStarSystem, panRoot, sunObject.layer); if (navMatrix.Length <= 1) { Logger.LogWarning("No planets suitable for map mode found! Defaulting to vanilla menu (expect weirdness!)."); @@ -530,7 +532,7 @@ namespace NewHorizons.Tools } else { - __result = ShipLogBuilder.MapModeBuilder.GetAstroBodyShipLogName(__instance.GetID()); + __result = MapModeBuilder.GetAstroBodyShipLogName(__instance.GetID()); return false; } } diff --git a/NewHorizons/Utility/CollectionUtilities.cs b/NewHorizons/Utility/CollectionUtilities.cs new file mode 100644 index 00000000..e4b49e6d --- /dev/null +++ b/NewHorizons/Utility/CollectionUtilities.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NewHorizons.Utility +{ + public static class CollectionUtilities + { + public static T KeyByValue(Dictionary dict, W val) + { + T key = default; + foreach (KeyValuePair pair in dict) + { + if (EqualityComparer.Default.Equals(pair.Value, val)) + { + key = pair.Key; + break; + } + } + return key; + } + } +} From 3487832b184cc5207b584f2aade5f3ee17be85c3 Mon Sep 17 00:00:00 2001 From: Nick Date: Sun, 6 Feb 2022 23:56:18 -0500 Subject: [PATCH 17/50] Update README.md --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b9e1b79c..4b4efbe0 100644 --- a/README.md +++ b/README.md @@ -623,9 +623,10 @@ Authors: - [Mister_Nebula](https://github.com/misternebula) (Marshmallow v0.1 to v1.1.0) New Horizons was made with help from: -- [jtsalomo](https://github.com/jtsalomo) (Implemented [OW_CommonResources](https://github.com/PacificEngine/OW_CommonResources) support introduced in v0.5.0) -- [Raicuparta](https://github.com/Raicuparta) (Integrated the [New Horizons Template](https://github.com/xen-42/ow-new-horizons-config-template) into the Outer Wilds Mods website) -- [Nageld](https://github.com/Nageld) (Set up xml reading for custom dialogue in v0.8.0) +- [jtsalomo](https://github.com/jtsalomo): Implemented [OW_CommonResources](https://github.com/PacificEngine/OW_CommonResources) support introduced in v0.5.0 +- [Raicuparta](https://github.com/Raicuparta): Integrated the [New Horizons Template](https://github.com/xen-42/ow-new-horizons-config-template) into the Outer Wilds Mods website +- [Nageld](https://github.com/Nageld): Set up xml reading for custom dialogue in v0.8.0 +- [Bwc9876](https://github.com/Bwc9876): Set up ship log entires for planets in v0.8.2 Marshmallow was made with help from: - TAImatem From d674c25163ed14b79c91e995dee3e0f2a8427f15 Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Mon, 7 Feb 2022 21:28:37 -0500 Subject: [PATCH 18/50] Make rumors work in NH addons --- NewHorizons/Builder/ShipLog/RumorModeBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs index 0c8869c6..2495d090 100644 --- a/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs @@ -49,7 +49,7 @@ namespace NewHorizons.Builder.ShipLog public static void AddBodyToShipLog(ShipLogManager manager, NewHorizonsBody body) { string systemName = body.Config.StarSystem; - XElement astroBodyFile = XElement.Load(Main.Instance.ModHelper.Manifest.ModFolderPath + body.Config.ShipLog.xmlFile); + XElement astroBodyFile = XElement.Load(body.Mod.Manifest.ModFolderPath + "/" + body.Config.ShipLog.xmlFile); XElement astroBodyId = astroBodyFile.Element("ID"); if (astroBodyId == null) { From fa5fa9572a0ac5bd68b46818bc21072cb21a0e2e Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Mon, 7 Feb 2022 21:28:52 -0500 Subject: [PATCH 19/50] Reorganization --- NewHorizons/External/ShipLogModule.cs | 20 +-- NewHorizons/Main.cs | 1 + NewHorizons/Tools/Patches.cs | 208 -------------------------- NewHorizons/Tools/ShipLogPatches.cs | 201 +++++++++++++++++++++++++ NewHorizons/Tools/WarpDrivePatches.cs | 2 - 5 files changed, 212 insertions(+), 220 deletions(-) create mode 100644 NewHorizons/Tools/ShipLogPatches.cs diff --git a/NewHorizons/External/ShipLogModule.cs b/NewHorizons/External/ShipLogModule.cs index 8883d8f5..14ee2cdd 100644 --- a/NewHorizons/External/ShipLogModule.cs +++ b/NewHorizons/External/ShipLogModule.cs @@ -22,6 +22,16 @@ namespace NewHorizons.External public ShipLogDetailInfo[] details; } + public class ShipLogDetailInfo + { + public string revealedSprite; + public string outlineSprite; + public float rotation = 0f; + public bool invisibleWhenHidden; + public MVector2 position; + public MVector2 scale; + } + public class CuriosityColorInfo { public string id; @@ -34,15 +44,5 @@ namespace NewHorizons.External public string id; public MVector2 position; } - - public class ShipLogDetailInfo - { - public string revealedSprite; - public string outlineSprite; - public float rotation = 0f; - public bool invisibleWhenHidden; - public MVector2 position; - public MVector2 scale; - } } } \ No newline at end of file diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index af99a57d..e9f6274f 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -65,6 +65,7 @@ namespace NewHorizons Tools.Patches.Apply(); Tools.WarpDrivePatches.Apply(); Tools.OWCameraFix.Apply(); + Tools.ShipLogPatches.Apply(); Logger.Log("Begin load of config files...", Logger.LogType.Log); diff --git a/NewHorizons/Tools/Patches.cs b/NewHorizons/Tools/Patches.cs index 91be6c27..7d9f5007 100644 --- a/NewHorizons/Tools/Patches.cs +++ b/NewHorizons/Tools/Patches.cs @@ -53,38 +53,15 @@ namespace NewHorizons.Tools Main.Instance.ModHelper.HarmonyHelper.AddPrefix(playerDataKnowsMultipleFrequencies, typeof(Patches), nameof(Patches.OnPlayerDataKnowsMultipleFrequencies)); var playerDataResetGame = typeof(PlayerData).GetMethod("ResetGame"); Main.Instance.ModHelper.HarmonyHelper.AddPostfix(playerDataResetGame, typeof(Patches), nameof(Patches.OnPlayerDataResetGame)); - var playerDataGetNewlyRevealedFactIDs = typeof(PlayerData).GetMethod("GetNewlyRevealedFactIDs"); - Main.Instance.ModHelper.HarmonyHelper.AddPostfix(playerDataGetNewlyRevealedFactIDs, typeof(Patches), nameof(Patches.OnPlayerDataGetNewlyRevealedFactIDsComplete)); Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Start", typeof(Patches), nameof(Patches.OnBlackHoleVolumeStart)); Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Awake", typeof(Patches), nameof(Patches.OnWhiteHoleVolumeAwake)); Main.Instance.ModHelper.HarmonyHelper.AddPrefix("UpdateOrbitalLaunchValues", typeof(Patches), nameof(Patches.OnProbeLauncherUpdateOrbitalLaunchValues)); Main.Instance.ModHelper.HarmonyHelper.AddPrefix("IsLaunched", typeof(Patches), nameof(Patches.OnSurveyorProbeIsLaunched)); - - - Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Awake", typeof(Patches), nameof(Patches.OnShipLogManagerAwake)); - Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Start", typeof(Patches), nameof(Patches.OnShipLogManagerStart)); - Main.Instance.ModHelper.HarmonyHelper.AddPrefix("IsFactRevealed", typeof(Patches), nameof(Patches.OnShipLogManagerIsFactRevealed)); - Main.Instance.ModHelper.HarmonyHelper.AddPrefix("CheckForCompletionAchievement", typeof(Patches), nameof(Patches.OnShipLogManagerCheckForCompletionAchievement)); - - Main.Instance.ModHelper.HarmonyHelper.AddPrefix("GetCuriosityColor", typeof(Patches), nameof(Patches.OnUIStyleManagerGetCuriosityColor)); - - Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Awake", typeof(Patches), nameof(Patches.DisableShipLogSandFunnel)); - Main.Instance.ModHelper.HarmonyHelper.AddPrefix("UpdateState", typeof(Patches), nameof(Patches.DisableShipLogSandFunnel)); - - Main.Instance.ModHelper.HarmonyHelper.AddPrefix("GetName", typeof(Patches), nameof(Patches.OnShipLogAstroObjectGetName)); - Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Update", typeof(Patches), nameof(Patches.OnShipCockpitControllerUpdate)); // Postfixes Main.Instance.ModHelper.HarmonyHelper.AddPostfix("Awake", typeof(Patches), nameof(Patches.OnMapControllerAwake)); - - Main.Instance.ModHelper.HarmonyHelper.AddPostfix("Awake", typeof(Patches), nameof(Patches.OnShipLogManagerAwakeComplete)); - - Main.Instance.ModHelper.HarmonyHelper.AddPostfix("UpdateState", typeof(Patches), nameof(Patches.OnShipLogAstroObjectUpdateState)); - - Main.Instance.ModHelper.HarmonyHelper.AddPostfix("EnterMode", typeof(Patches), nameof(Patches.OnShipLogMapModeEnterMode)); - Main.Instance.ModHelper.HarmonyHelper.AddPostfix("Initialize", typeof(Patches), nameof(Patches.OnShipLogMapModeInitialize)); } public static bool GetHUDDisplayName(ReferenceFrame __instance, ref string __result) @@ -394,190 +371,5 @@ namespace NewHorizons.Tools } return true; } - - #region ShipLog - public static void OnShipLogManagerAwake(ShipLogManager __instance) - { - Logger.Log("Beginning Ship Log Generation For: " + Main.Instance.CurrentStarSystem, Logger.LogType.Log); - if (Main.Instance.CurrentStarSystem != "SolarSystem") - { - __instance._shipLogXmlAssets = new TextAsset[] {}; - foreach (ShipLogEntryLocation logEntryLocation in GameObject.FindObjectsOfType()) - { - logEntryLocation._initialized = true; - } - } - foreach (NewHorizonsBody body in Main.BodyDict[Main.Instance.CurrentStarSystem]) - { - if (body.Config.ShipLog?.curiosities != null) - { - RumorModeBuilder.AddCuriosityColors(body.Config.ShipLog.curiosities); - } - } - foreach (NewHorizonsBody body in Main.BodyDict[Main.Instance.CurrentStarSystem]) - { - if (body.Config.ShipLog?.xmlFile != null) - { - RumorModeBuilder.AddBodyToShipLog(__instance, body); - } - } - } - - public static void OnShipLogManagerAwakeComplete(ShipLogManager __instance) - { - RumorModeBuilder.GenerateEntryData(__instance); - for (var i = 0; i < __instance._entryList.Count; i++) - { - ShipLogEntry logEntry = __instance._entryList[i]; - RumorModeBuilder.UpdateEntryCuriosity(ref logEntry); - } - Logger.Log("Ship Log Generation Complete For: " + Main.Instance.CurrentStarSystem, Logger.LogType.Log); - } - - public static bool OnShipLogManagerIsFactRevealed(ShipLogManager __instance, ref bool __result, string __0) - { - if (Main.Instance.CurrentStarSystem == "SolarSystem") - { - return true; - } - else - { - if (__instance._factDict.ContainsKey(__0) == false) - { - __result = false; - return false; - } - else - { - return true; - } - } - } - - public static bool OnShipLogManagerCheckForCompletionAchievement() - { - return Main.Instance.CurrentStarSystem == "SolarSystem"; - } - - public static bool OnShipLogManagerStart(ShipLogManager __instance) - { - if (Main.Instance.CurrentStarSystem == "SolarSystem") - { - return true; - } - else - { - foreach (NewHorizonsBody body in Main.BodyDict[Main.Instance.CurrentStarSystem]) - { - foreach (string fact in body.Config.ShipLog?.initialReveal ?? Array.Empty()) - { - __instance.RevealFact(fact, false, false); - } - } - EntryLocationBuilder.InitializeLocations(); - return false; - } - } - - public static bool OnUIStyleManagerGetCuriosityColor(UIStyleManager __instance, CuriosityName __0, bool __1, ref Color __result) - { - if (Main.Instance.CurrentStarSystem == "SolarSystem") - { - return true; - } - else - { - __result = RumorModeBuilder.GetCuriosityColor(__0, __1, __instance._neutralColor, __instance._neutralHighlight); - return false; - } - } - - private static void DeleteDetail(string name) - { - Object.Destroy(GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/" + name)); - } - - public static void OnShipLogMapModeInitialize(ShipLogMapMode __instance) - { - if (Main.Instance.CurrentStarSystem != "SolarSystem") - { - GameObject panRoot = GameObject.Find(ShipLogHandler.PAN_ROOT_PATH); - GameObject sunObject = GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/Sun"); - ShipLogAstroObject[][] navMatrix = MapModeBuilder.ConstructMapMode(Main.Instance.CurrentStarSystem, panRoot, sunObject.layer); - if (navMatrix.Length <= 1) - { - Logger.LogWarning("No planets suitable for map mode found! Defaulting to vanilla menu (expect weirdness!)."); - } - else - { - __instance._astroObjects = navMatrix; - __instance._startingAstroObjectID = navMatrix[1][0].GetID(); - List delete = SearchUtilities.GetAllChildren(panRoot).Where(g => g.name.Contains("_ShipLog") == false).ToList(); - foreach (GameObject gameObject in delete) - { - DeleteDetail(gameObject.name); - } - // Just Lie About Having A Sand Funnel - __instance._sandFunnel = __instance.gameObject.AddComponent(); - } - } - Logger.Log("Map Mode Construction Complete", Logger.LogType.Log); - } - - public static bool OnShipLogAstroObjectGetName(ShipLogAstroObject __instance, ref string __result) - { - if (Main.Instance.CurrentStarSystem == "SolarSystem") - { - return true; - } - else - { - __result = MapModeBuilder.GetAstroBodyShipLogName(__instance.GetID()); - return false; - } - } - - public static void OnShipLogAstroObjectUpdateState(ShipLogAstroObject __instance) - { - Transform detailsParent = __instance.transform.Find("Details"); - if (detailsParent != null) - { - foreach (GameObject child in SearchUtilities.GetAllChildren(detailsParent.gameObject)) - { - Component detail; - if (child.TryGetComponent(typeof(ShipLogDetail), out detail)) - { - (detail as ShipLogDetail)?.UpdateState(__instance._state); - } - } - } - - Transform lineObject = __instance.transform.Find("Line_ShipLog"); - if (lineObject != null) - { - ShipLogDetail lineDetail = lineObject.gameObject.GetComponent(); - lineDetail.UpdateState(__instance._state); - } - } - - public static bool DisableShipLogSandFunnel() - { - return Main.Instance.CurrentStarSystem == "SolarSystem"; - } - - public static void OnPlayerDataGetNewlyRevealedFactIDsComplete(ref List __result) - { - ShipLogManager manager = Locator.GetShipLogManager(); - __result = __result.Where(e => manager.GetFact(e) != null).ToList(); - } - # endregion - - public static void OnShipLogMapModeEnterMode(ShipLogMapMode __instance) - { - var newPrompt = "Interstellar Mode"; - __instance._detectiveModePrompt.SetText(newPrompt); - var text = GameObject.Find("Ship_Body/Module_Cabin/Systems_Cabin/ShipLogPivot/ShipLog/ShipLogPivot/ShipLogCanvas/ScreenPromptListScaleRoot/ScreenPromptList_UpperRight/ScreenPrompt/Text").GetComponent(); - text.text = newPrompt; - } } } diff --git a/NewHorizons/Tools/ShipLogPatches.cs b/NewHorizons/Tools/ShipLogPatches.cs new file mode 100644 index 00000000..751a3684 --- /dev/null +++ b/NewHorizons/Tools/ShipLogPatches.cs @@ -0,0 +1,201 @@ +using NewHorizons.Components; +using System; +using System.Collections.Generic; +using System.Linq; +using NewHorizons.Utility; +using UnityEngine; +using Logger = NewHorizons.Utility.Logger; +using Object = UnityEngine.Object; +using NewHorizons.Builder.ShipLog; +using NewHorizons.Builder.Handlers; + +namespace NewHorizons.Tools +{ + public static class ShipLogPatches + { + public static void Apply() + { + var playerDataGetNewlyRevealedFactIDs = typeof(PlayerData).GetMethod("GetNewlyRevealedFactIDs"); + Main.Instance.ModHelper.HarmonyHelper.AddPostfix(playerDataGetNewlyRevealedFactIDs, typeof(ShipLogPatches), nameof(ShipLogPatches.OnPlayerDataGetNewlyRevealedFactIDsComplete)); + + Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Awake", typeof(ShipLogPatches), nameof(ShipLogPatches.OnShipLogManagerAwake)); + Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Start", typeof(ShipLogPatches), nameof(ShipLogPatches.OnShipLogManagerStart)); + Main.Instance.ModHelper.HarmonyHelper.AddPrefix("IsFactRevealed", typeof(ShipLogPatches), nameof(ShipLogPatches.OnShipLogManagerIsFactRevealed)); + Main.Instance.ModHelper.HarmonyHelper.AddPrefix("CheckForCompletionAchievement", typeof(ShipLogPatches), nameof(ShipLogPatches.OnShipLogManagerCheckForCompletionAchievement)); + Main.Instance.ModHelper.HarmonyHelper.AddPrefix("GetCuriosityColor", typeof(ShipLogPatches), nameof(ShipLogPatches.OnUIStyleManagerGetCuriosityColor)); + Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Awake", typeof(ShipLogPatches), nameof(ShipLogPatches.DisableShipLogSandFunnel)); + Main.Instance.ModHelper.HarmonyHelper.AddPrefix("UpdateState", typeof(ShipLogPatches), nameof(ShipLogPatches.DisableShipLogSandFunnel)); + Main.Instance.ModHelper.HarmonyHelper.AddPrefix("GetName", typeof(ShipLogPatches), nameof(ShipLogPatches.OnShipLogAstroObjectGetName)); + Main.Instance.ModHelper.HarmonyHelper.AddPostfix("Initialize", typeof(ShipLogPatches), nameof(ShipLogPatches.OnShipLogMapModeInitialize)); + Main.Instance.ModHelper.HarmonyHelper.AddPostfix("Awake", typeof(ShipLogPatches), nameof(ShipLogPatches.OnShipLogManagerAwakeComplete)); + Main.Instance.ModHelper.HarmonyHelper.AddPostfix("UpdateState", typeof(ShipLogPatches), nameof(ShipLogPatches.OnShipLogAstroObjectUpdateState)); + } + + public static void OnShipLogManagerAwake(ShipLogManager __instance) + { + Logger.Log("Beginning Ship Log Generation For: " + Main.Instance.CurrentStarSystem, Logger.LogType.Log); + if (Main.Instance.CurrentStarSystem != "SolarSystem") + { + __instance._shipLogXmlAssets = new TextAsset[] { }; + foreach (ShipLogEntryLocation logEntryLocation in GameObject.FindObjectsOfType()) + { + logEntryLocation._initialized = true; + } + } + foreach (NewHorizonsBody body in Main.BodyDict[Main.Instance.CurrentStarSystem]) + { + if (body.Config.ShipLog?.curiosities != null) + { + RumorModeBuilder.AddCuriosityColors(body.Config.ShipLog.curiosities); + } + } + foreach (NewHorizonsBody body in Main.BodyDict[Main.Instance.CurrentStarSystem]) + { + if (body.Config.ShipLog?.xmlFile != null) + { + RumorModeBuilder.AddBodyToShipLog(__instance, body); + } + } + } + + public static void OnShipLogManagerAwakeComplete(ShipLogManager __instance) + { + RumorModeBuilder.GenerateEntryData(__instance); + for (var i = 0; i < __instance._entryList.Count; i++) + { + ShipLogEntry logEntry = __instance._entryList[i]; + RumorModeBuilder.UpdateEntryCuriosity(ref logEntry); + } + Logger.Log("Ship Log Generation Complete For: " + Main.Instance.CurrentStarSystem, Logger.LogType.Log); + } + + public static bool OnShipLogManagerIsFactRevealed(ShipLogManager __instance, ref bool __result, string __0) + { + Logger.Log(__0); + + // Just replace the entire function, no need to Debug Break or wtv + if (__instance._factDict != null && __instance._factDict.ContainsKey(__0)) + { + __result = __instance._factDict[__0].IsRevealed(); + } + else + { + __result = false; + } + return false; + } + + public static bool OnShipLogManagerCheckForCompletionAchievement() + { + return Main.Instance.CurrentStarSystem == "SolarSystem"; + } + + public static bool OnShipLogManagerStart(ShipLogManager __instance) + { + if (Main.Instance.CurrentStarSystem == "SolarSystem") + { + return true; + } + else + { + foreach (NewHorizonsBody body in Main.BodyDict[Main.Instance.CurrentStarSystem]) + { + foreach (string fact in body.Config.ShipLog?.initialReveal ?? Array.Empty()) + { + __instance.RevealFact(fact, false, false); + } + } + EntryLocationBuilder.InitializeLocations(); + return false; + } + } + + public static bool OnUIStyleManagerGetCuriosityColor(UIStyleManager __instance, CuriosityName __0, bool __1, ref Color __result) + { + if (Main.Instance.CurrentStarSystem == "SolarSystem") + { + return true; + } + else + { + __result = RumorModeBuilder.GetCuriosityColor(__0, __1, __instance._neutralColor, __instance._neutralHighlight); + return false; + } + } + + public static void OnShipLogMapModeInitialize(ShipLogMapMode __instance) + { + if (Main.Instance.CurrentStarSystem != "SolarSystem") + { + GameObject panRoot = GameObject.Find(ShipLogHandler.PAN_ROOT_PATH); + GameObject sunObject = GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/Sun"); + ShipLogAstroObject[][] navMatrix = MapModeBuilder.ConstructMapMode(Main.Instance.CurrentStarSystem, panRoot, sunObject.layer); + if (navMatrix.Length <= 1) + { + Logger.LogWarning("No planets suitable for map mode found! Defaulting to vanilla menu (expect weirdness!)."); + } + else + { + __instance._astroObjects = navMatrix; + __instance._startingAstroObjectID = navMatrix[1][0].GetID(); + List delete = SearchUtilities.GetAllChildren(panRoot).Where(g => g.name.Contains("_ShipLog") == false).ToList(); + foreach (GameObject gameObject in delete) + { + Object.Destroy(GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/" + gameObject.name)); + } + // Just Lie About Having A Sand Funnel + __instance._sandFunnel = __instance.gameObject.AddComponent(); + } + } + Logger.Log("Map Mode Construction Complete", Logger.LogType.Log); + } + + public static bool OnShipLogAstroObjectGetName(ShipLogAstroObject __instance, ref string __result) + { + if (Main.Instance.CurrentStarSystem == "SolarSystem") + { + return true; + } + else + { + __result = MapModeBuilder.GetAstroBodyShipLogName(__instance.GetID()); + return false; + } + } + + public static void OnShipLogAstroObjectUpdateState(ShipLogAstroObject __instance) + { + Transform detailsParent = __instance.transform.Find("Details"); + if (detailsParent != null) + { + foreach (GameObject child in SearchUtilities.GetAllChildren(detailsParent.gameObject)) + { + Component detail; + if (child.TryGetComponent(typeof(ShipLogDetail), out detail)) + { + (detail as ShipLogDetail)?.UpdateState(__instance._state); + } + } + } + + Transform lineObject = __instance.transform.Find("Line_ShipLog"); + if (lineObject != null) + { + ShipLogDetail lineDetail = lineObject.gameObject.GetComponent(); + lineDetail.UpdateState(__instance._state); + } + } + + public static bool DisableShipLogSandFunnel() + { + return Main.Instance.CurrentStarSystem == "SolarSystem"; + } + + public static void OnPlayerDataGetNewlyRevealedFactIDsComplete(ref List __result) + { + ShipLogManager manager = Locator.GetShipLogManager(); + __result = __result.Where(e => manager.GetFact(e) != null).ToList(); + } + } +} + \ No newline at end of file diff --git a/NewHorizons/Tools/WarpDrivePatches.cs b/NewHorizons/Tools/WarpDrivePatches.cs index e06ce4c0..4fa7e713 100644 --- a/NewHorizons/Tools/WarpDrivePatches.cs +++ b/NewHorizons/Tools/WarpDrivePatches.cs @@ -14,9 +14,7 @@ namespace NewHorizons.Tools public static void Apply() { Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Update", typeof(WarpDrivePatches), nameof(WarpDrivePatches.OnShipCockpitControllerUpdate)); - Main.Instance.ModHelper.HarmonyHelper.AddPostfix("EnterMode", typeof(WarpDrivePatches), nameof(WarpDrivePatches.OnShipLogMapModeEnterMode)); - Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Update", typeof(WarpDrivePatches), nameof(WarpDrivePatches.OnShipLogControllerUpdate)); } From 781570c66219551e1e59b10a3ebb2cf69e3d3f94 Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Mon, 7 Feb 2022 22:48:56 -0500 Subject: [PATCH 20/50] Update ShipLogPatches.cs --- NewHorizons/Tools/ShipLogPatches.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/NewHorizons/Tools/ShipLogPatches.cs b/NewHorizons/Tools/ShipLogPatches.cs index 751a3684..982e7e8b 100644 --- a/NewHorizons/Tools/ShipLogPatches.cs +++ b/NewHorizons/Tools/ShipLogPatches.cs @@ -20,7 +20,7 @@ namespace NewHorizons.Tools Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Awake", typeof(ShipLogPatches), nameof(ShipLogPatches.OnShipLogManagerAwake)); Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Start", typeof(ShipLogPatches), nameof(ShipLogPatches.OnShipLogManagerStart)); - Main.Instance.ModHelper.HarmonyHelper.AddPrefix("IsFactRevealed", typeof(ShipLogPatches), nameof(ShipLogPatches.OnShipLogManagerIsFactRevealed)); + //Main.Instance.ModHelper.HarmonyHelper.AddPrefix("IsFactRevealed", typeof(ShipLogPatches), nameof(ShipLogPatches.OnShipLogManagerIsFactRevealed)); Main.Instance.ModHelper.HarmonyHelper.AddPrefix("CheckForCompletionAchievement", typeof(ShipLogPatches), nameof(ShipLogPatches.OnShipLogManagerCheckForCompletionAchievement)); Main.Instance.ModHelper.HarmonyHelper.AddPrefix("GetCuriosityColor", typeof(ShipLogPatches), nameof(ShipLogPatches.OnUIStyleManagerGetCuriosityColor)); Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Awake", typeof(ShipLogPatches), nameof(ShipLogPatches.DisableShipLogSandFunnel)); @@ -73,7 +73,6 @@ namespace NewHorizons.Tools { Logger.Log(__0); - // Just replace the entire function, no need to Debug Break or wtv if (__instance._factDict != null && __instance._factDict.ContainsKey(__0)) { __result = __instance._factDict[__0].IsRevealed(); From 45adcb457ff4cf6a27ac688b43f2ca866592edff Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Mon, 7 Feb 2022 23:00:21 -0500 Subject: [PATCH 21/50] Allow targeting off the plane --- NewHorizons/Tools/Patches.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/NewHorizons/Tools/Patches.cs b/NewHorizons/Tools/Patches.cs index 7d9f5007..55846fb2 100644 --- a/NewHorizons/Tools/Patches.cs +++ b/NewHorizons/Tools/Patches.cs @@ -62,6 +62,7 @@ namespace NewHorizons.Tools // Postfixes Main.Instance.ModHelper.HarmonyHelper.AddPostfix("Awake", typeof(Patches), nameof(Patches.OnMapControllerAwake)); + Main.Instance.ModHelper.HarmonyHelper.AddPostfix("OnTargetReferenceFrame", typeof(Patches), nameof(Patches.OnMapControllerOnTargetReferenceFrame)); } public static bool GetHUDDisplayName(ReferenceFrame __instance, ref string __result) @@ -371,5 +372,10 @@ namespace NewHorizons.Tools } return true; } + + public static void OnMapControllerOnTargetReferenceFrame(MapController __instance, ReferenceFrame __0) + { + __instance._isLockedOntoMapSatellite = true; + } } } From 464484a94d3992969432edc3c9a826eacf284345 Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Mon, 7 Feb 2022 23:11:26 -0500 Subject: [PATCH 22/50] Accidentally had the warp patch down twice --- NewHorizons/Tools/Patches.cs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/NewHorizons/Tools/Patches.cs b/NewHorizons/Tools/Patches.cs index 55846fb2..d9a92420 100644 --- a/NewHorizons/Tools/Patches.cs +++ b/NewHorizons/Tools/Patches.cs @@ -58,7 +58,6 @@ namespace NewHorizons.Tools Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Awake", typeof(Patches), nameof(Patches.OnWhiteHoleVolumeAwake)); Main.Instance.ModHelper.HarmonyHelper.AddPrefix("UpdateOrbitalLaunchValues", typeof(Patches), nameof(Patches.OnProbeLauncherUpdateOrbitalLaunchValues)); Main.Instance.ModHelper.HarmonyHelper.AddPrefix("IsLaunched", typeof(Patches), nameof(Patches.OnSurveyorProbeIsLaunched)); - Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Update", typeof(Patches), nameof(Patches.OnShipCockpitControllerUpdate)); // Postfixes Main.Instance.ModHelper.HarmonyHelper.AddPostfix("Awake", typeof(Patches), nameof(Patches.OnMapControllerAwake)); @@ -359,20 +358,6 @@ namespace NewHorizons.Tools return false; } - public static bool OnShipCockpitControllerUpdate(ShipCockpitController __instance) - { - if(__instance._playerAtFlightConsole && OWInput.IsNewlyPressed(InputLibrary.autopilot, InputMode.ShipCockpit)) - { - var targetSystem = StarChartHandler.ShipLogStarChartMode.GetTargetStarSystem(); - if (targetSystem != null) - { - Main.Instance.ChangeCurrentStarSystem(targetSystem, true); - return false; - } - } - return true; - } - public static void OnMapControllerOnTargetReferenceFrame(MapController __instance, ReferenceFrame __0) { __instance._isLockedOntoMapSatellite = true; From 3d200a19db49ebd51cacd85100a5afbcb48d1091 Mon Sep 17 00:00:00 2001 From: Ben C Date: Tue, 8 Feb 2022 16:04:24 -0500 Subject: [PATCH 23/50] Added grayscale if a planet is rumored but not explored --- NewHorizons/Builder/ShipLog/MapModeBuilder.cs | 39 +++++++++------ NewHorizons/Components/ShipLogDetail.cs | 49 ++++++++++++------- NewHorizons/Handlers/ShipLogHandler.cs | 7 ++- 3 files changed, 61 insertions(+), 34 deletions(-) diff --git a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs index cfe6051d..849d4d79 100644 --- a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs @@ -47,9 +47,10 @@ namespace NewHorizons.Builder.ShipLog public static ShipLogAstroObject[][] ConstructMapMode(string systemName, GameObject transformParent, int layer) { MapModeObject rootObject = ConstructPrimaryNode(systemName); + Material greyScaleMaterial = GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/TimberHearth/Sprite").GetComponent().material; if (rootObject.mainBody != null) { - MakeAllNodes(ref rootObject, transformParent, layer); + MakeAllNodes(ref rootObject, transformParent, greyScaleMaterial, layer); } int maxAmount = Main.BodyDict[Main.Instance.CurrentStarSystem].Count; @@ -80,13 +81,13 @@ namespace NewHorizons.Builder.ShipLog } } - private static void MakeAllNodes(ref MapModeObject parentNode, GameObject parent, int layer) + private static void MakeAllNodes(ref MapModeObject parentNode, GameObject parent, Material greyScaleMaterial, int layer) { - MakeNode(ref parentNode, parent, layer); + MakeNode(ref parentNode, parent, greyScaleMaterial, layer); for (var i = 0; i < parentNode.children.Count; i++) { MapModeObject child = parentNode.children[i]; - MakeAllNodes(ref child, parent, layer); + MakeAllNodes(ref child, parent, greyScaleMaterial, layer); parentNode.children[i] = child; } } @@ -117,7 +118,7 @@ namespace NewHorizons.Builder.ShipLog return newImageGO; } - private static void CreateShipLogAstroObject(GameObject nodeGO, ref MapModeObject node, GameObject referenceUnviewedSprite, int layer) + private static void CreateShipLogAstroObject(GameObject nodeGO, ref MapModeObject node, GameObject referenceUnviewedSprite, Material greyScaleMaterial, int layer) { const float unviewedIconOffset = 15; ShipLogAstroObject astroObject = nodeGO.AddComponent(); @@ -126,6 +127,14 @@ namespace NewHorizons.Builder.ShipLog string imagePath = node.mainBody.Config.ShipLog?.mapMode?.revealedSprite ?? "DEFAULT"; string outlinePath = node.mainBody.Config.ShipLog?.mapMode?.outlineSprite ?? imagePath; astroObject._imageObj = CreateImage(nodeGO, node.mainBody.Mod.Assets, imagePath, node.mainBody.Config.Name + " Revealed", layer); + if (ShipLogHandler.BodyHasEntries(node.mainBody)) + { + Image revealedImage = astroObject._imageObj.GetComponent(); + astroObject._greyscaleMaterial = greyScaleMaterial; + revealedImage.material = greyScaleMaterial; + revealedImage.color = Color.white; + astroObject._image = revealedImage; + } astroObject._outlineObj = CreateImage(nodeGO, node.mainBody.Mod.Assets, outlinePath, node.mainBody.Config.Name + " Outline", layer); astroObject._unviewedObj = Object.Instantiate(referenceUnviewedSprite, nodeGO.transform, false); @@ -143,7 +152,7 @@ namespace NewHorizons.Builder.ShipLog node.astroObject = astroObject; } - private static void ConnectNodeToLastSibling(MapModeObject node) + private static void ConnectNodeToLastSibling(MapModeObject node, Material greyScaleMaterial) { Vector2 fromPosition = node.astroObject.transform.localPosition; Vector2 toPosition = node.lastSibling.astroObject.transform.localPosition; @@ -167,14 +176,14 @@ namespace NewHorizons.Builder.ShipLog }; ShipLogDetail linkDetail = newLink.AddComponent(); - linkDetail.Init(linkDetailInfo, linkImage, linkImage); + linkDetail.Init(linkDetailInfo, linkImage, linkImage, greyScaleMaterial); transform.SetParent(node.astroObject.transform); transform.SetAsFirstSibling(); newLink.SetActive(true); } - private static void MakeDetail(ShipLogModule.ShipLogDetailInfo info, Transform parent, IModAssets assets) + private static void MakeDetail(ShipLogModule.ShipLogDetailInfo info, Transform parent, IModAssets assets, Material greyScaleMaterial) { GameObject detailGameObject = new GameObject("Detail"); detailGameObject.transform.SetParent(parent); @@ -192,11 +201,11 @@ namespace NewHorizons.Builder.ShipLog Image outlineImage = CreateImage(detailGameObject, assets, outlinePath, "Detail Outline", parent.gameObject.layer).GetComponent(); ShipLogDetail detail = detailGameObject.AddComponent(); - detail.Init(info, revealedImage, outlineImage); + detail.Init(info, revealedImage, outlineImage, greyScaleMaterial); detailGameObject.SetActive(true); } - private static void MakeDetails(MapModeObject node) + private static void MakeDetails(MapModeObject node, Material greyScaleMaterial) { if (node.mainBody.Config.ShipLog?.mapMode?.details?.Length > 0) { @@ -211,13 +220,13 @@ namespace NewHorizons.Builder.ShipLog foreach (ShipLogModule.ShipLogDetailInfo detailInfo in node.mainBody.Config.ShipLog.mapMode.details) { - MakeDetail(detailInfo, detailsTransform, node.mainBody.Mod.Assets); + MakeDetail(detailInfo, detailsTransform, node.mainBody.Mod.Assets, greyScaleMaterial); } detailsParent.SetActive(true); } } - private static void MakeNode(ref MapModeObject node, GameObject parent, int layer) + private static void MakeNode(ref MapModeObject node, GameObject parent, Material greyScaleMaterial, int layer) { const float padding = 250f; @@ -246,9 +255,9 @@ namespace NewHorizons.Builder.ShipLog transform.localPosition = new Vector3(position.x, position.y, 0); transform.localRotation = Quaternion.identity; transform.localScale = Vector3.one * scale; - CreateShipLogAstroObject(newNodeGO, ref node, GameObject.Find("Ship_Body/Module_Cabin/Systems_Cabin/ShipLogPivot/ShipLog/ShipLogPivot/ShipLogCanvas/MapMode/ScaleRoot/PanRoot/TimberHearth/UnviewedIcon"), layer); - if (node.lastSibling != null) ConnectNodeToLastSibling(node); - MakeDetails(node); + CreateShipLogAstroObject(newNodeGO, ref node, GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/TimberHearth/UnviewedIcon"), greyScaleMaterial, layer); + if (node.lastSibling != null) ConnectNodeToLastSibling(node, greyScaleMaterial); + MakeDetails(node, greyScaleMaterial); transform.SetAsFirstSibling(); } diff --git a/NewHorizons/Components/ShipLogDetail.cs b/NewHorizons/Components/ShipLogDetail.cs index c6f77304..e1936a30 100644 --- a/NewHorizons/Components/ShipLogDetail.cs +++ b/NewHorizons/Components/ShipLogDetail.cs @@ -1,4 +1,5 @@ -using NewHorizons.External; +using System; +using NewHorizons.External; using OWML.Common; using UnityEngine; using UnityEngine.UI; @@ -8,17 +9,19 @@ namespace NewHorizons.Components { public class ShipLogDetail : MonoBehaviour { - private Image revealedImage; - private Image outlineImage; - private ShipLogModule.ShipLogDetailInfo detailInfo; + private Image _revealedImage; + private Image _outlineImage; + private Material _greyScaleMaterial; + private ShipLogModule.ShipLogDetailInfo _detailInfo; - public void Init(ShipLogModule.ShipLogDetailInfo info, Image revealed, Image outline) + public void Init(ShipLogModule.ShipLogDetailInfo info, Image revealed, Image outline, Material greyScale) { - detailInfo = info; - revealedImage = revealed; - outlineImage = outline; - revealedImage.enabled = false; - outlineImage.enabled = false; + _detailInfo = info; + _revealedImage = revealed; + _outlineImage = outline; + _greyScaleMaterial = greyScale; + _revealedImage.enabled = false; + _outlineImage.enabled = false; } public void UpdateState(ShipLogEntry.State parentState) @@ -26,22 +29,32 @@ namespace NewHorizons.Components switch (parentState) { case ShipLogEntry.State.Explored: - outlineImage.enabled = false; - revealedImage.enabled = true; + _outlineImage.enabled = false; + _revealedImage.enabled = true; + SetGreyScale(false); break; case ShipLogEntry.State.Rumored: - revealedImage.enabled = false; - outlineImage.enabled = true; + _outlineImage.enabled = false; + _revealedImage.enabled = true; + SetGreyScale(true); break; case ShipLogEntry.State.Hidden: - revealedImage.enabled = false; - outlineImage.enabled = !detailInfo.invisibleWhenHidden; + _revealedImage.enabled = false; + _outlineImage.enabled = !_detailInfo.invisibleWhenHidden; break; case ShipLogEntry.State.None: - revealedImage.enabled = false; - outlineImage.enabled = false; + _revealedImage.enabled = false; + _outlineImage.enabled = false; + break; + default: + Logger.LogError("Invalid ShipLogEntryState for " + _revealedImage.transform.parent.parent.gameObject.name); break; } } + + private void SetGreyScale(bool greyScale) + { + _revealedImage.material = (greyScale ? _greyScaleMaterial : null); + } } } \ No newline at end of file diff --git a/NewHorizons/Handlers/ShipLogHandler.cs b/NewHorizons/Handlers/ShipLogHandler.cs index ff565b54..098592f2 100644 --- a/NewHorizons/Handlers/ShipLogHandler.cs +++ b/NewHorizons/Handlers/ShipLogHandler.cs @@ -33,7 +33,7 @@ namespace NewHorizons.Builder.Handlers public static string GetAstroObjectId(NewHorizonsBody body) { - if (_astroIdToBody.ContainsValue(body)) + if (BodyHasEntries(body)) { return CollectionUtilities.KeyByValue(_astroIdToBody, body); } @@ -42,5 +42,10 @@ namespace NewHorizons.Builder.Handlers return body.Config.Name; } } + + public static bool BodyHasEntries(NewHorizonsBody body) + { + return _astroIdToBody.ContainsValue(body); + } } } From cfdf22d341711a72f9671269e939cbb779b39b7d Mon Sep 17 00:00:00 2001 From: Ben C Date: Tue, 8 Feb 2022 16:17:18 -0500 Subject: [PATCH 24/50] Fixed initialReveal and curiosity colors --- NewHorizons/Tools/ShipLogPatches.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/NewHorizons/Tools/ShipLogPatches.cs b/NewHorizons/Tools/ShipLogPatches.cs index 982e7e8b..c14c21ab 100644 --- a/NewHorizons/Tools/ShipLogPatches.cs +++ b/NewHorizons/Tools/ShipLogPatches.cs @@ -91,19 +91,19 @@ namespace NewHorizons.Tools public static bool OnShipLogManagerStart(ShipLogManager __instance) { + foreach (NewHorizonsBody body in Main.BodyDict[Main.Instance.CurrentStarSystem]) + { + foreach (string fact in body.Config.ShipLog?.initialReveal ?? Array.Empty()) + { + __instance.RevealFact(fact, false, false); + } + } if (Main.Instance.CurrentStarSystem == "SolarSystem") { return true; } else { - foreach (NewHorizonsBody body in Main.BodyDict[Main.Instance.CurrentStarSystem]) - { - foreach (string fact in body.Config.ShipLog?.initialReveal ?? Array.Empty()) - { - __instance.RevealFact(fact, false, false); - } - } EntryLocationBuilder.InitializeLocations(); return false; } @@ -111,7 +111,7 @@ namespace NewHorizons.Tools public static bool OnUIStyleManagerGetCuriosityColor(UIStyleManager __instance, CuriosityName __0, bool __1, ref Color __result) { - if (Main.Instance.CurrentStarSystem == "SolarSystem") + if ((int)__0 < 7) { return true; } From 14f0e4cf800c8d24c2cfaea28b9357a72fc01928 Mon Sep 17 00:00:00 2001 From: Ben C Date: Tue, 8 Feb 2022 16:59:56 -0500 Subject: [PATCH 25/50] Added Ability For Child Entries --- .../Builder/ShipLog/RumorModeBuilder.cs | 17 +++++++++++ NewHorizons/shiplog_schema.xsd | 29 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs index 2495d090..b19ebcae 100644 --- a/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs @@ -66,6 +66,23 @@ namespace NewHorizons.Builder.ShipLog { _entryIdToRawName.Add(id.Value, curiosityName.Value); } + foreach (XElement childEntryElement in entryElement.Elements("Entry")) + { + XElement childCuriosityName = childEntryElement.Element("Curiosity"); + XElement childId = childEntryElement.Element("ID"); + if (childId != null && _entryIdToRawName.ContainsKey(childId.Value)) + { + if (childCuriosityName == null && curiosityName != null) + { + _entryIdToRawName.Add(childId.Value, curiosityName.Value); + } + else if (childCuriosityName != null) + { + _entryIdToRawName.Add(childId.Value, childCuriosityName.Value); + } + } + AddTranslation(childEntryElement); + } AddTranslation(entryElement); } TextAsset newAsset = new TextAsset(astroBodyFile.ToString()); diff --git a/NewHorizons/shiplog_schema.xsd b/NewHorizons/shiplog_schema.xsd index fe3ade33..43ea8334 100644 --- a/NewHorizons/shiplog_schema.xsd +++ b/NewHorizons/shiplog_schema.xsd @@ -17,6 +17,7 @@ + @@ -29,6 +30,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 87244e317cf006f6d317cf76f78a230bfa330fc7 Mon Sep 17 00:00:00 2001 From: Ben C Date: Tue, 8 Feb 2022 23:03:19 -0500 Subject: [PATCH 26/50] Added Alt Text And Images --- .../Builder/ShipLog/RumorModeBuilder.cs | 35 ++++-- NewHorizons/shiplog_schema.xsd | 110 +++++++++--------- 2 files changed, 78 insertions(+), 67 deletions(-) diff --git a/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs index b19ebcae..ff051fac 100644 --- a/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs @@ -116,7 +116,8 @@ namespace NewHorizons.Builder.ShipLog { id = entry._id, cardPosition = entryPosition, - sprite = body.Config.ShipLog.spriteFolder == null ? null : GetEntrySprite(entry._id, body) + sprite = body.Config.ShipLog.spriteFolder == null ? null : GetEntrySprite(entry._id, body), + altSprite = body.Config.ShipLog.spriteFolder == null ? null : GetEntrySprite(entry._id + "_ALT", body) }; entry.SetSprite(newData.sprite == null ? manager._shipLogLibrary.defaultEntrySprite : newData.sprite); manager._entryDataDict.Add(entry._id, newData); @@ -150,23 +151,35 @@ namespace NewHorizons.Builder.ShipLog table[rumorName.Value] = rumorName.Value; } - XElement rumorText = rumorFact.Element("Text"); - if (rumorText != null) - { - table[name + rumorText.Value] = rumorText.Value; - } + AddTextTranslation(rumorFact, name, table); + AddAltTextTranslation(rumorFact, name, table); } foreach (XElement exploreFact in entry.Elements("ExploreFact")) { - XElement exploreText = exploreFact.Element("Text"); - if (exploreText != null) - { - table[name + exploreText.Value] = exploreText.Value; - } + AddTextTranslation(exploreFact, name, table); + AddAltTextTranslation(exploreFact, name, table); } } } + private static void AddTextTranslation(XElement fact, string name, Dictionary table) + { + XElement textElement = fact.Element("Text"); + if (textElement != null) + { + table[name + textElement.Value] = textElement.Value; + } + } + + private static void AddAltTextTranslation(XElement fact, string name, Dictionary table) + { + XElement altTextElement = fact.Element("AltText"); + if (altTextElement != null) + { + AddTextTranslation(altTextElement, name, table); + } + } + public static void UpdateEntryCuriosity(ref ShipLogEntry entry) { if (_entryIdToRawName.ContainsKey(entry._id)) diff --git a/NewHorizons/shiplog_schema.xsd b/NewHorizons/shiplog_schema.xsd index 43ea8334..c6b77898 100644 --- a/NewHorizons/shiplog_schema.xsd +++ b/NewHorizons/shiplog_schema.xsd @@ -1,67 +1,65 @@  + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 77c749a2acb84b6b22dee1ef0b0cc51a4e9c64fb Mon Sep 17 00:00:00 2001 From: Ben C Date: Thu, 10 Feb 2022 16:17:32 -0500 Subject: [PATCH 27/50] Added Manual Ship Log Positioning --- NewHorizons/Builder/ShipLog/MapModeBuilder.cs | 445 +++++++++++------- .../Builder/ShipLog/RumorModeBuilder.cs | 34 +- NewHorizons/External/ShipLogModule.cs | 2 + NewHorizons/Handlers/ShipLogHandler.cs | 11 + NewHorizons/Tools/ShipLogPatches.cs | 3 +- 5 files changed, 293 insertions(+), 202 deletions(-) diff --git a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs index 849d4d79..3cb96b34 100644 --- a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs @@ -15,6 +15,199 @@ namespace NewHorizons.Builder.ShipLog { public static class MapModeBuilder { + #region General + public static ShipLogAstroObject[][] ConstructMapMode(string systemName, GameObject transformParent, int layer) + { + Material greyScaleMaterial = GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/TimberHearth/Sprite").GetComponent().material; + List bodies = Main.BodyDict[systemName].Where(b => (b.Config.ShipLog?.mapMode?.remove ?? false) == false).ToList(); + bool flagManualPositionUsed = false; + bool flagAutoPositionUsed = false; + foreach (NewHorizonsBody body in bodies) + { + if (body.Config.ShipLog?.mapMode?.manualPosition == null) + { + flagAutoPositionUsed = true; + } + else + { + flagManualPositionUsed = true; + if (body.Config.ShipLog?.mapMode?.manualNavigationPosition == null) + { + Logger.LogError("Navigation position is missing for: " + body.Config.Name); + return null; + } + } + } + + if (flagAutoPositionUsed && flagManualPositionUsed) + { + Logger.LogError("Can't mix manual and automatic layout of ship log map mode, skipping generation"); + return null; + } + else if (flagAutoPositionUsed) + { + return ConstructMapModeAuto(bodies, transformParent, greyScaleMaterial, layer); + } + else if (flagManualPositionUsed) + { + return ConstructMapModeManual(bodies, transformParent, greyScaleMaterial, layer); + } + return null; + } + + public static string GetAstroBodyShipLogName(string id) + { + return ShipLogHandler.GetConfigFromID(id)?.Config?.Name ?? id; + } + + private static GameObject CreateImage(GameObject nodeGO, IModAssets assets, string imagePath, string name, int layer) + { + GameObject newImageGO = new GameObject(name); + newImageGO.layer = layer; + newImageGO.transform.SetParent(nodeGO.transform); + + RectTransform transform = newImageGO.AddComponent(); + transform.localPosition = Vector3.zero; + transform.localRotation = Quaternion.identity; + transform.localScale = Vector3.one; + + Image newImage = newImageGO.AddComponent(); + if (imagePath == "DEFAULT") + { + newImage.sprite = Locator.GetShipLogManager()._shipLogLibrary.defaultEntrySprite; + } + else + { + Texture2D newTexture = assets.GetTexture(imagePath); + Rect rect = new Rect(0, 0, newTexture.width, newTexture.height); + Vector2 pivot = new Vector2(newTexture.width / 2, newTexture.height / 2); + newImage.sprite = Sprite.Create(newTexture, rect, pivot); + } + return newImageGO; + } + + private static GameObject CreateMapModeGameObject(NewHorizonsBody body, GameObject parent, int layer, Vector2 position) + { + GameObject newGameObject = new GameObject(body.Config.Name + "_ShipLog"); + newGameObject.layer = layer; + newGameObject.transform.SetParent(parent.transform); + + RectTransform transform = newGameObject.AddComponent(); + float scale = body.Config.ShipLog?.mapMode?.scale ?? 1f; + transform.localPosition = position; + transform.localRotation = Quaternion.identity; + transform.localScale = Vector3.one * scale; + transform.SetAsFirstSibling(); + return newGameObject; + } + + private static ShipLogAstroObject AddShipLogAstroObject(GameObject gameObject, NewHorizonsBody body, Material greyScaleMaterial, int layer) + { + const float unviewedIconOffset = 15; + + GameObject unviewedReference = GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/TimberHearth/UnviewedIcon"); + + ShipLogAstroObject astroObject = gameObject.AddComponent(); + astroObject._id = ShipLogHandler.GetAstroObjectId(body); + + string imagePath = body.Config.ShipLog?.mapMode?.revealedSprite ?? "DEFAULT"; + string outlinePath = body.Config.ShipLog?.mapMode?.outlineSprite ?? imagePath; + astroObject._imageObj = CreateImage(gameObject, body.Mod.Assets, imagePath, body.Config.Name + " Revealed", layer); + if (ShipLogHandler.BodyHasEntries(body)) + { + Image revealedImage = astroObject._imageObj.GetComponent(); + astroObject._greyscaleMaterial = greyScaleMaterial; + revealedImage.material = greyScaleMaterial; + revealedImage.color = Color.white; + astroObject._image = revealedImage; + } + astroObject._outlineObj = CreateImage(gameObject, body.Mod.Assets, outlinePath, body.Config.Name + " Outline", layer); + + astroObject._unviewedObj = Object.Instantiate(unviewedReference, gameObject.transform, false); + astroObject._invisibleWhenHidden = body.Config.ShipLog?.mapMode?.invisibleWhenHidden ?? false; + + Rect imageRect = astroObject._imageObj.GetComponent().rect; + astroObject._unviewedObj.transform.localPosition = new Vector3(imageRect.width / 2 + unviewedIconOffset, imageRect.height / 2 + unviewedIconOffset, 0); + return astroObject; + } + #endregion + + # region Details + private static void MakeDetail(ShipLogModule.ShipLogDetailInfo info, Transform parent, IModAssets assets, Material greyScaleMaterial) + { + GameObject detailGameObject = new GameObject("Detail"); + detailGameObject.transform.SetParent(parent); + detailGameObject.SetActive(false); + + RectTransform detailTransform = detailGameObject.AddComponent(); + detailTransform.localPosition = (Vector2)(info.position ?? new MVector2(0, 0)); + detailTransform.localRotation = Quaternion.Euler(0f, 0f, info.rotation); + detailTransform.localScale = (Vector2)(info.scale ?? new MVector2(0, 0)); + + string revealedPath = info.revealedSprite ?? "DEFAULT"; + string outlinePath = info.outlineSprite ?? revealedPath; + + Image revealedImage = CreateImage(detailGameObject, assets, revealedPath, "Detail Revealed", parent.gameObject.layer).GetComponent(); + Image outlineImage = CreateImage(detailGameObject, assets, outlinePath, "Detail Outline", parent.gameObject.layer).GetComponent(); + + ShipLogDetail detail = detailGameObject.AddComponent(); + detail.Init(info, revealedImage, outlineImage, greyScaleMaterial); + detailGameObject.SetActive(true); + } + + private static void MakeDetails(NewHorizonsBody body, Transform parent, Material greyScaleMaterial) + { + if (body.Config.ShipLog?.mapMode?.details?.Length > 0) + { + GameObject detailsParent = new GameObject("Details"); + detailsParent.transform.SetParent(parent); + detailsParent.SetActive(false); + + RectTransform detailsTransform = detailsParent.AddComponent(); + detailsTransform.localPosition = Vector3.zero; + detailsTransform.localRotation = Quaternion.identity; + detailsTransform.localScale = Vector3.one; + + foreach (ShipLogModule.ShipLogDetailInfo detailInfo in body.Config.ShipLog.mapMode.details) + { + MakeDetail(detailInfo, detailsTransform, body.Mod.Assets, greyScaleMaterial); + } + detailsParent.SetActive(true); + } + } + #endregion + + #region Manual Map Mode + private static ShipLogAstroObject[][] ConstructMapModeManual(List bodies, GameObject transformParent, Material greyScaleMaterial, int layer) + { + int maxAmount = bodies.Count; + ShipLogAstroObject[][] navMatrix = new ShipLogAstroObject[maxAmount][]; + for (int i = 0; i < maxAmount; i++) + { + navMatrix[i] = new ShipLogAstroObject[maxAmount]; + } + + foreach (NewHorizonsBody body in bodies) + { + if (ShipLogHandler.IsVanillaBody(body)) continue; + GameObject newMapModeGO = CreateMapModeGameObject(body, transformParent, layer, body.Config.ShipLog?.mapMode?.manualPosition); + ShipLogAstroObject newAstroObject = AddShipLogAstroObject(newMapModeGO, body, greyScaleMaterial, layer); + MakeDetails(body, newMapModeGO.transform, greyScaleMaterial); + Vector2 navigationPosition = body.Config.ShipLog?.mapMode?.manualNavigationPosition; + navMatrix[(int) navigationPosition.y][(int) navigationPosition.x] = newAstroObject; + } + + navMatrix = navMatrix.Where(a => a.Count(c => c != null) > 0).Prepend(new ShipLogAstroObject[1]).ToArray(); + for (var index = 0; index < navMatrix.Length; index++) + { + navMatrix[index] = navMatrix[index].Where(a => a != null).ToArray(); + } + + return navMatrix; + } + #endregion + + #region Automatic Map Mode private class MapModeObject { public int x; @@ -38,22 +231,16 @@ namespace NewHorizons.Builder.ShipLog parent?.Increment_height(); } } - - public static string GetAstroBodyShipLogName(string id) + + private static ShipLogAstroObject[][] ConstructMapModeAuto(List bodies, GameObject transformParent, Material greyScaleMaterial, int layer) { - return ShipLogHandler.GetConfigFromID(id)?.Config?.Name ?? id; - } - - public static ShipLogAstroObject[][] ConstructMapMode(string systemName, GameObject transformParent, int layer) - { - MapModeObject rootObject = ConstructPrimaryNode(systemName); - Material greyScaleMaterial = GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/TimberHearth/Sprite").GetComponent().material; + MapModeObject rootObject = ConstructPrimaryNode(bodies); if (rootObject.mainBody != null) { MakeAllNodes(ref rootObject, transformParent, greyScaleMaterial, layer); } - int maxAmount = Main.BodyDict[Main.Instance.CurrentStarSystem].Count; + int maxAmount = bodies.Count; ShipLogAstroObject[][] navMatrix = new ShipLogAstroObject[maxAmount][]; for (int i = 0; i < maxAmount; i++) { @@ -91,67 +278,69 @@ namespace NewHorizons.Builder.ShipLog parentNode.children[i] = child; } } - - private static GameObject CreateImage(GameObject nodeGO, IModAssets assets, string imagePath, string name, int layer) + + private static MapModeObject ConstructPrimaryNode(List bodies) { - GameObject newImageGO = new GameObject(name); - newImageGO.layer = layer; - newImageGO.transform.SetParent(nodeGO.transform); - - RectTransform transform = newImageGO.AddComponent(); - transform.localPosition = Vector3.zero; - transform.localRotation = Quaternion.identity; - transform.localScale = Vector3.one; - - Image newImage = newImageGO.AddComponent(); - if (imagePath == "DEFAULT") + foreach (NewHorizonsBody body in bodies.Where(b => b.Config.Base.CenterOfSolarSystem)) { - newImage.sprite = Locator.GetShipLogManager()._shipLogLibrary.defaultEntrySprite; + bodies.Sort((b, o) => b.Config.Orbit.SemiMajorAxis.CompareTo(o.Config.Orbit.SemiMajorAxis)); + MapModeObject newNode = new MapModeObject + { + mainBody = body, + level = 0, + x = 0, + y = 0 + }; + newNode.children = ConstructChildrenNodes(newNode, bodies); + return newNode; } - else - { - Texture2D newTexture = assets.GetTexture(imagePath); - Rect rect = new Rect(0, 0, newTexture.width, newTexture.height); - Vector2 pivot = new Vector2(newTexture.width / 2, newTexture.height / 2); - newImage.sprite = Sprite.Create(newTexture, rect, pivot); - } - return newImageGO; + Logger.LogError("Couldn't find center of system!"); + return new MapModeObject(); } - private static void CreateShipLogAstroObject(GameObject nodeGO, ref MapModeObject node, GameObject referenceUnviewedSprite, Material greyScaleMaterial, int layer) + private static List ConstructChildrenNodes(MapModeObject parent, List searchList) { - const float unviewedIconOffset = 15; - ShipLogAstroObject astroObject = nodeGO.AddComponent(); - astroObject._id = ShipLogHandler.GetAstroObjectId(node.mainBody); - - string imagePath = node.mainBody.Config.ShipLog?.mapMode?.revealedSprite ?? "DEFAULT"; - string outlinePath = node.mainBody.Config.ShipLog?.mapMode?.outlineSprite ?? imagePath; - astroObject._imageObj = CreateImage(nodeGO, node.mainBody.Mod.Assets, imagePath, node.mainBody.Config.Name + " Revealed", layer); - if (ShipLogHandler.BodyHasEntries(node.mainBody)) + List children = new List(); + int newX = parent.x; + int newY = parent.y; + int newLevel = parent.level + 1; + MapModeObject lastSibling = parent; + foreach (NewHorizonsBody body in searchList.Where(b => b.Config.Orbit.PrimaryBody == parent.mainBody.Config.Name)) { - Image revealedImage = astroObject._imageObj.GetComponent(); - astroObject._greyscaleMaterial = greyScaleMaterial; - revealedImage.material = greyScaleMaterial; - revealedImage.color = Color.white; - astroObject._image = revealedImage; + if (body.Config.Orbit.PrimaryBody == parent.mainBody.Config.Name) + { + bool even = newLevel % 2 == 0; + newX = even ? newX : newX + 1; + newY = even ? newY + 1 : newY; + MapModeObject newNode = new MapModeObject() + { + mainBody = body, + level = newLevel, + x = newX, + y = newY, + parent = parent, + lastSibling = lastSibling + }; + newNode.children = ConstructChildrenNodes(newNode, searchList); + if (even) + { + newY += newNode.branch_height; + parent.Increment_height(); + newY += 1; + } + else + { + newX += newNode.branch_width; + parent.Increment_width(); + newX += 1; + } + lastSibling = newNode; + children.Add(newNode); + } } - astroObject._outlineObj = CreateImage(nodeGO, node.mainBody.Mod.Assets, outlinePath, node.mainBody.Config.Name + " Outline", layer); - - astroObject._unviewedObj = Object.Instantiate(referenceUnviewedSprite, nodeGO.transform, false); - if (node.mainBody.Config.FocalPoint != null) - { - astroObject._imageObj.GetComponent().enabled = false; - astroObject._outlineObj.GetComponent().enabled = false; - astroObject._unviewedObj.GetComponent().enabled = false; - astroObject.transform.localScale = node.lastSibling.astroObject.transform.localScale; - } - astroObject._invisibleWhenHidden = node.mainBody.Config.ShipLog?.mapMode?.invisibleWhenHidden ?? false; - - Rect imageRect = astroObject._imageObj.GetComponent().rect; - astroObject._unviewedObj.transform.localPosition = new Vector3(imageRect.width / 2 + unviewedIconOffset, imageRect.height / 2 + unviewedIconOffset, 0); - node.astroObject = astroObject; + return children; } - + private static void ConnectNodeToLastSibling(MapModeObject node, Material greyScaleMaterial) { Vector2 fromPosition = node.astroObject.transform.localPosition; @@ -183,59 +372,9 @@ namespace NewHorizons.Builder.ShipLog newLink.SetActive(true); } - private static void MakeDetail(ShipLogModule.ShipLogDetailInfo info, Transform parent, IModAssets assets, Material greyScaleMaterial) - { - GameObject detailGameObject = new GameObject("Detail"); - detailGameObject.transform.SetParent(parent); - detailGameObject.SetActive(false); - - RectTransform detailTransform = detailGameObject.AddComponent(); - detailTransform.localPosition = (Vector2)(info.position ?? new MVector2(0, 0)); - detailTransform.localRotation = Quaternion.Euler(0f, 0f, info.rotation); - detailTransform.localScale = (Vector2)(info.scale ?? new MVector2(0, 0)); - - string revealedPath = info.revealedSprite ?? "DEFAULT"; - string outlinePath = info.outlineSprite ?? revealedPath; - - Image revealedImage = CreateImage(detailGameObject, assets, revealedPath, "Detail Revealed", parent.gameObject.layer).GetComponent(); - Image outlineImage = CreateImage(detailGameObject, assets, outlinePath, "Detail Outline", parent.gameObject.layer).GetComponent(); - - ShipLogDetail detail = detailGameObject.AddComponent(); - detail.Init(info, revealedImage, outlineImage, greyScaleMaterial); - detailGameObject.SetActive(true); - } - - private static void MakeDetails(MapModeObject node, Material greyScaleMaterial) - { - if (node.mainBody.Config.ShipLog?.mapMode?.details?.Length > 0) - { - GameObject detailsParent = new GameObject("Details"); - detailsParent.transform.SetParent(node.astroObject.transform); - detailsParent.SetActive(false); - - RectTransform detailsTransform = detailsParent.AddComponent(); - detailsTransform.localPosition = Vector3.zero; - detailsTransform.localRotation = Quaternion.identity; - detailsTransform.localScale = Vector3.one; - - foreach (ShipLogModule.ShipLogDetailInfo detailInfo in node.mainBody.Config.ShipLog.mapMode.details) - { - MakeDetail(detailInfo, detailsTransform, node.mainBody.Mod.Assets, greyScaleMaterial); - } - detailsParent.SetActive(true); - } - } - private static void MakeNode(ref MapModeObject node, GameObject parent, Material greyScaleMaterial, int layer) { const float padding = 250f; - - GameObject newNodeGO = new GameObject(node.mainBody.Config.Name + "_ShipLog"); - newNodeGO.layer = layer; - newNodeGO.transform.SetParent(parent.transform); - - RectTransform transform = newNodeGO.AddComponent(); - float scale = node.mainBody.Config.ShipLog?.mapMode?.scale ?? 1f; Vector2 position = Vector2.zero; if (node.lastSibling != null) { @@ -252,77 +391,19 @@ namespace NewHorizons.Builder.ShipLog position.x += padding * (node.x - node.lastSibling.x) + extraDistance; } } - transform.localPosition = new Vector3(position.x, position.y, 0); - transform.localRotation = Quaternion.identity; - transform.localScale = Vector3.one * scale; - CreateShipLogAstroObject(newNodeGO, ref node, GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/TimberHearth/UnviewedIcon"), greyScaleMaterial, layer); + GameObject newNodeGO = CreateMapModeGameObject(node.mainBody, parent, layer, position); + ShipLogAstroObject astroObject = AddShipLogAstroObject(newNodeGO, node.mainBody, greyScaleMaterial, layer); + if (node.mainBody.Config.FocalPoint != null) + { + astroObject._imageObj.GetComponent().enabled = false; + astroObject._outlineObj.GetComponent().enabled = false; + astroObject._unviewedObj.GetComponent().enabled = false; + astroObject.transform.localScale = node.lastSibling.astroObject.transform.localScale; + } + node.astroObject = astroObject; if (node.lastSibling != null) ConnectNodeToLastSibling(node, greyScaleMaterial); - MakeDetails(node, greyScaleMaterial); - transform.SetAsFirstSibling(); - } - - private static MapModeObject ConstructPrimaryNode(string systemName) - { - foreach (NewHorizonsBody body in Main.BodyDict[systemName].Where(b => b.Config.Base.CenterOfSolarSystem)) - { - List searchList = Main.BodyDict[systemName].Where(b => (b.Config.ShipLog?.mapMode?.remove ?? false) == false).ToList(); - searchList.Sort((b, o) => b.Config.Orbit.SemiMajorAxis.CompareTo(o.Config.Orbit.SemiMajorAxis)); - MapModeObject newNode = new MapModeObject - { - mainBody = body, - level = 0, - x = 0, - y = 0 - }; - newNode.children = ConstructChildrenNodes(systemName, newNode, searchList); - return newNode; - } - Logger.LogError("Couldn't find center of system!"); - return new MapModeObject(); - } - - private static List ConstructChildrenNodes(string systemName, MapModeObject parent, List searchList) - { - List children = new List(); - int newX = parent.x; - int newY = parent.y; - int newLevel = parent.level + 1; - MapModeObject lastSibling = parent; - foreach (NewHorizonsBody body in searchList.Where(b => b.Config.Orbit.PrimaryBody == parent.mainBody.Config.Name)) - { - if (body.Config.Orbit.PrimaryBody == parent.mainBody.Config.Name) - { - bool even = newLevel % 2 == 0; - newX = even ? newX : newX + 1; - newY = even ? newY + 1 : newY; - MapModeObject newNode = new MapModeObject() - { - mainBody = body, - level = newLevel, - x = newX, - y = newY, - parent = parent, - lastSibling = lastSibling - }; - newNode.children = ConstructChildrenNodes(systemName, newNode, searchList); - if (even) - { - newY += newNode.branch_height; - parent.Increment_height(); - newY += 1; - } - else - { - newX += newNode.branch_width; - parent.Increment_width(); - newX += 1; - } - lastSibling = newNode; - children.Add(newNode); - } - } - return children; + MakeDetails(node.mainBody, newNodeGO.transform, greyScaleMaterial); } + #endregion } - } diff --git a/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs index ff051fac..b3d5c298 100644 --- a/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs @@ -1,4 +1,5 @@ -using NewHorizons.Components; +using System; +using NewHorizons.Components; using System.Collections.Generic; using System.IO; using System.Linq; @@ -145,38 +146,33 @@ namespace NewHorizons.Builder.ShipLog table[name] = name; foreach (XElement rumorFact in entry.Elements("RumorFact")) { - XElement rumorName = rumorFact.Element("RumorName"); - if (rumorName != null) - { - table[rumorName.Value] = rumorName.Value; - } - - AddTextTranslation(rumorFact, name, table); - AddAltTextTranslation(rumorFact, name, table); + AddTranslationForElement(rumorFact, "RumorName", string.Empty, table); + AddTranslationForElement(rumorFact, "Text", name, table); + AddTranslationForAltText(rumorFact, name, table); } foreach (XElement exploreFact in entry.Elements("ExploreFact")) { - AddTextTranslation(exploreFact, name, table); - AddAltTextTranslation(exploreFact, name, table); + AddTranslationForElement(exploreFact, "Text", name, table); + AddTranslationForAltText(exploreFact, name, table); } } } - private static void AddTextTranslation(XElement fact, string name, Dictionary table) + private static void AddTranslationForElement(XElement parent, string elementName, string keyName, Dictionary table) { - XElement textElement = fact.Element("Text"); - if (textElement != null) + XElement element = parent.Element(elementName); + if (element != null) { - table[name + textElement.Value] = textElement.Value; + table[keyName + element.Value] = element.Value; } } - private static void AddAltTextTranslation(XElement fact, string name, Dictionary table) + private static void AddTranslationForAltText(XElement fact, string keyName, Dictionary table) { - XElement altTextElement = fact.Element("AltText"); - if (altTextElement != null) + XElement altText = fact.Element("AltText"); + if (altText != null) { - AddTextTranslation(altTextElement, name, table); + AddTranslationForElement(altText, "Text", keyName, table); } } diff --git a/NewHorizons/External/ShipLogModule.cs b/NewHorizons/External/ShipLogModule.cs index 14ee2cdd..f329bedb 100644 --- a/NewHorizons/External/ShipLogModule.cs +++ b/NewHorizons/External/ShipLogModule.cs @@ -18,6 +18,8 @@ namespace NewHorizons.External public float scale = 1f; public bool invisibleWhenHidden; public float offset = 0f; + public MVector2 manualPosition; + public MVector2 manualNavigationPosition; public bool remove = false; public ShipLogDetailInfo[] details; } diff --git a/NewHorizons/Handlers/ShipLogHandler.cs b/NewHorizons/Handlers/ShipLogHandler.cs index 098592f2..ef4244f9 100644 --- a/NewHorizons/Handlers/ShipLogHandler.cs +++ b/NewHorizons/Handlers/ShipLogHandler.cs @@ -17,6 +17,17 @@ namespace NewHorizons.Builder.Handlers public static readonly string PAN_ROOT_PATH = "Ship_Body/Module_Cabin/Systems_Cabin/ShipLogPivot/ShipLog/ShipLogPivot/ShipLogCanvas/MapMode/ScaleRoot/PanRoot"; private static Dictionary _astroIdToBody = new Dictionary(); + private static string[] vanillaBodies; + + public static void Init() + { + vanillaBodies = SearchUtilities.GetAllChildren(GameObject.Find(PAN_ROOT_PATH)).ConvertAll(g => g.name).ToArray(); + } + + public static bool IsVanillaBody(NewHorizonsBody body) + { + return vanillaBodies.Contains(body.Config.Name.Replace(" ", "")); + } public static NewHorizonsBody GetConfigFromID(string id) { diff --git a/NewHorizons/Tools/ShipLogPatches.cs b/NewHorizons/Tools/ShipLogPatches.cs index c14c21ab..4996366d 100644 --- a/NewHorizons/Tools/ShipLogPatches.cs +++ b/NewHorizons/Tools/ShipLogPatches.cs @@ -33,6 +33,7 @@ namespace NewHorizons.Tools public static void OnShipLogManagerAwake(ShipLogManager __instance) { + ShipLogHandler.Init(); Logger.Log("Beginning Ship Log Generation For: " + Main.Instance.CurrentStarSystem, Logger.LogType.Log); if (Main.Instance.CurrentStarSystem != "SolarSystem") { @@ -129,7 +130,7 @@ namespace NewHorizons.Tools GameObject panRoot = GameObject.Find(ShipLogHandler.PAN_ROOT_PATH); GameObject sunObject = GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/Sun"); ShipLogAstroObject[][] navMatrix = MapModeBuilder.ConstructMapMode(Main.Instance.CurrentStarSystem, panRoot, sunObject.layer); - if (navMatrix.Length <= 1) + if (navMatrix == null || navMatrix.Length <= 1) { Logger.LogWarning("No planets suitable for map mode found! Defaulting to vanilla menu (expect weirdness!)."); } From a0b1de7e605df55604ca7c922b1eb6788e3a89ba Mon Sep 17 00:00:00 2001 From: Ben C Date: Thu, 10 Feb 2022 18:50:59 -0500 Subject: [PATCH 28/50] Added Overwriting SolarSystem Planets --- NewHorizons/Builder/ShipLog/MapModeBuilder.cs | 70 ++++++++++++++++--- NewHorizons/Handlers/ShipLogHandler.cs | 10 ++- NewHorizons/Tools/ShipLogPatches.cs | 36 +++++----- 3 files changed, 88 insertions(+), 28 deletions(-) diff --git a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs index 3cb96b34..7913453c 100644 --- a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs @@ -16,13 +16,13 @@ namespace NewHorizons.Builder.ShipLog public static class MapModeBuilder { #region General - public static ShipLogAstroObject[][] ConstructMapMode(string systemName, GameObject transformParent, int layer) + public static ShipLogAstroObject[][] ConstructMapMode(string systemName, GameObject transformParent, ShipLogAstroObject[][] currentNav, int layer) { Material greyScaleMaterial = GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/TimberHearth/Sprite").GetComponent().material; List bodies = Main.BodyDict[systemName].Where(b => (b.Config.ShipLog?.mapMode?.remove ?? false) == false).ToList(); - bool flagManualPositionUsed = false; + bool flagManualPositionUsed = systemName == "SolarSystem"; bool flagAutoPositionUsed = false; - foreach (NewHorizonsBody body in bodies) + foreach (NewHorizonsBody body in bodies.Where(b => ShipLogHandler.IsVanillaBody(b) == false)) { if (body.Config.ShipLog?.mapMode?.manualPosition == null) { @@ -50,7 +50,7 @@ namespace NewHorizons.Builder.ShipLog } else if (flagManualPositionUsed) { - return ConstructMapModeManual(bodies, transformParent, greyScaleMaterial, layer); + return ConstructMapModeManual(bodies, transformParent, greyScaleMaterial, currentNav, layer); } return null; } @@ -178,29 +178,77 @@ namespace NewHorizons.Builder.ShipLog #endregion #region Manual Map Mode - private static ShipLogAstroObject[][] ConstructMapModeManual(List bodies, GameObject transformParent, Material greyScaleMaterial, int layer) + private static ShipLogAstroObject[][] ConstructMapModeManual(List bodies, GameObject transformParent, Material greyScaleMaterial, ShipLogAstroObject[][] currentNav, int layer) { - int maxAmount = bodies.Count; + int maxAmount = bodies.Count + 20; ShipLogAstroObject[][] navMatrix = new ShipLogAstroObject[maxAmount][]; for (int i = 0; i < maxAmount; i++) { navMatrix[i] = new ShipLogAstroObject[maxAmount]; } - foreach (NewHorizonsBody body in bodies) + Dictionary astroIdToNavIndex = new Dictionary(); + + if (Main.Instance.CurrentStarSystem == "SolarSystem") + { + + for (int y = 0; y < currentNav.Length; y++) + { + for (int x = 0; x < currentNav[y].Length; x++) + { + navMatrix[y][x] = currentNav[y][x]; + astroIdToNavIndex.Add(currentNav[y][x].GetID(), new [] {y, x}); + } + } + } + + foreach (NewHorizonsBody body in bodies.Where(b => ShipLogHandler.IsVanillaBody(b) == false)) { - if (ShipLogHandler.IsVanillaBody(body)) continue; GameObject newMapModeGO = CreateMapModeGameObject(body, transformParent, layer, body.Config.ShipLog?.mapMode?.manualPosition); ShipLogAstroObject newAstroObject = AddShipLogAstroObject(newMapModeGO, body, greyScaleMaterial, layer); MakeDetails(body, newMapModeGO.transform, greyScaleMaterial); Vector2 navigationPosition = body.Config.ShipLog?.mapMode?.manualNavigationPosition; navMatrix[(int) navigationPosition.y][(int) navigationPosition.x] = newAstroObject; } - - navMatrix = navMatrix.Where(a => a.Count(c => c != null) > 0).Prepend(new ShipLogAstroObject[1]).ToArray(); + + if (Main.Instance.CurrentStarSystem == "SolarSystem") + { + foreach (NewHorizonsBody body in Main.BodyDict["SolarSystem"].Where(ShipLogHandler.IsVanillaBody)) + { + GameObject gameObject = GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/" + body.Config.Name.Replace(" ", "")); + if (body.Config.Destroy || (body.Config.ShipLog?.mapMode?.remove ?? false)) + { + ShipLogAstroObject astroObject = gameObject.GetComponent(); + if (astroObject != null) + { + int[] navIndex = astroIdToNavIndex[astroObject.GetID()]; + navMatrix[navIndex[0]][navIndex[1]] = null; + } + Object.Destroy(gameObject); + } + else + { + if (body.Config.ShipLog?.mapMode?.manualPosition != null) + { + gameObject.transform.localPosition = (Vector2)body.Config.ShipLog.mapMode.manualPosition; + } + if (body.Config.ShipLog?.mapMode?.manualNavigationPosition != null) + { + Vector2 navigationPosition = body.Config.ShipLog?.mapMode?.manualNavigationPosition; + navMatrix[(int) navigationPosition.y][(int) navigationPosition.x] = gameObject.GetComponent(); + } + if (body.Config.ShipLog?.mapMode?.scale != null) + { + gameObject.transform.localScale = Vector3.one * body.Config.ShipLog.mapMode.scale; + } + } + } + } + + navMatrix = navMatrix.Where(a => a.Count(c => c != null && c.gameObject != null) > 0).Prepend(new ShipLogAstroObject[1]).ToArray(); for (var index = 0; index < navMatrix.Length; index++) { - navMatrix[index] = navMatrix[index].Where(a => a != null).ToArray(); + navMatrix[index] = navMatrix[index].Where(a => a != null && a.gameObject != null).ToArray(); } return navMatrix; diff --git a/NewHorizons/Handlers/ShipLogHandler.cs b/NewHorizons/Handlers/ShipLogHandler.cs index ef4244f9..2ce83ffe 100644 --- a/NewHorizons/Handlers/ShipLogHandler.cs +++ b/NewHorizons/Handlers/ShipLogHandler.cs @@ -18,10 +18,18 @@ namespace NewHorizons.Builder.Handlers private static Dictionary _astroIdToBody = new Dictionary(); private static string[] vanillaBodies; + private static string[] vanillaIDs; public static void Init() { - vanillaBodies = SearchUtilities.GetAllChildren(GameObject.Find(PAN_ROOT_PATH)).ConvertAll(g => g.name).ToArray(); + List gameObjects = SearchUtilities.GetAllChildren(GameObject.Find(PAN_ROOT_PATH)); + vanillaBodies = gameObjects.ConvertAll(g => g.name).ToArray(); + vanillaIDs = gameObjects.ConvertAll(g => g.GetComponent()?.GetID()).ToArray(); + } + + public static bool IsVanillaAstroID(string astroId) + { + return vanillaIDs.Contains(astroId); } public static bool IsVanillaBody(NewHorizonsBody body) diff --git a/NewHorizons/Tools/ShipLogPatches.cs b/NewHorizons/Tools/ShipLogPatches.cs index 4996366d..7faf1b65 100644 --- a/NewHorizons/Tools/ShipLogPatches.cs +++ b/NewHorizons/Tools/ShipLogPatches.cs @@ -43,6 +43,7 @@ namespace NewHorizons.Tools logEntryLocation._initialized = true; } } + foreach (NewHorizonsBody body in Main.BodyDict[Main.Instance.CurrentStarSystem]) { if (body.Config.ShipLog?.curiosities != null) @@ -50,6 +51,7 @@ namespace NewHorizons.Tools RumorModeBuilder.AddCuriosityColors(body.Config.ShipLog.curiosities); } } + foreach (NewHorizonsBody body in Main.BodyDict[Main.Instance.CurrentStarSystem]) { if (body.Config.ShipLog?.xmlFile != null) @@ -67,6 +69,7 @@ namespace NewHorizons.Tools ShipLogEntry logEntry = __instance._entryList[i]; RumorModeBuilder.UpdateEntryCuriosity(ref logEntry); } + Logger.Log("Ship Log Generation Complete For: " + Main.Instance.CurrentStarSystem, Logger.LogType.Log); } @@ -82,6 +85,7 @@ namespace NewHorizons.Tools { __result = false; } + return false; } @@ -99,6 +103,7 @@ namespace NewHorizons.Tools __instance.RevealFact(fact, false, false); } } + if (Main.Instance.CurrentStarSystem == "SolarSystem") { return true; @@ -112,7 +117,7 @@ namespace NewHorizons.Tools public static bool OnUIStyleManagerGetCuriosityColor(UIStyleManager __instance, CuriosityName __0, bool __1, ref Color __result) { - if ((int)__0 < 7) + if ((int) __0 < 7) { return true; } @@ -125,34 +130,34 @@ namespace NewHorizons.Tools public static void OnShipLogMapModeInitialize(ShipLogMapMode __instance) { - if (Main.Instance.CurrentStarSystem != "SolarSystem") + GameObject panRoot = GameObject.Find(ShipLogHandler.PAN_ROOT_PATH); + GameObject sunObject = GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/Sun"); + ShipLogAstroObject[][] navMatrix = MapModeBuilder.ConstructMapMode(Main.Instance.CurrentStarSystem, panRoot, __instance._astroObjects, sunObject.layer); + if (navMatrix == null || navMatrix.Length <= 1) { - GameObject panRoot = GameObject.Find(ShipLogHandler.PAN_ROOT_PATH); - GameObject sunObject = GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/Sun"); - ShipLogAstroObject[][] navMatrix = MapModeBuilder.ConstructMapMode(Main.Instance.CurrentStarSystem, panRoot, sunObject.layer); - if (navMatrix == null || navMatrix.Length <= 1) + Logger.LogWarning("Skipping Map Mode Generation."); + } + else + { + __instance._astroObjects = navMatrix; + __instance._startingAstroObjectID = navMatrix[1][0].GetID(); + if (Main.Instance.CurrentStarSystem != "SolarSystem") { - Logger.LogWarning("No planets suitable for map mode found! Defaulting to vanilla menu (expect weirdness!)."); - } - else - { - __instance._astroObjects = navMatrix; - __instance._startingAstroObjectID = navMatrix[1][0].GetID(); List delete = SearchUtilities.GetAllChildren(panRoot).Where(g => g.name.Contains("_ShipLog") == false).ToList(); foreach (GameObject gameObject in delete) { Object.Destroy(GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/" + gameObject.name)); } - // Just Lie About Having A Sand Funnel __instance._sandFunnel = __instance.gameObject.AddComponent(); } } + Logger.Log("Map Mode Construction Complete", Logger.LogType.Log); } public static bool OnShipLogAstroObjectGetName(ShipLogAstroObject __instance, ref string __result) { - if (Main.Instance.CurrentStarSystem == "SolarSystem") + if (ShipLogHandler.IsVanillaAstroID(__instance.GetID())) { return true; } @@ -197,5 +202,4 @@ namespace NewHorizons.Tools __result = __result.Where(e => manager.GetFact(e) != null).ToList(); } } -} - \ No newline at end of file +} \ No newline at end of file From 05d51f54a730279931bf1b5e5a11ad126efa3795 Mon Sep 17 00:00:00 2001 From: Ben C Date: Thu, 10 Feb 2022 19:00:07 -0500 Subject: [PATCH 29/50] Fixed Completion Achievement Check --- NewHorizons/Tools/ShipLogPatches.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/NewHorizons/Tools/ShipLogPatches.cs b/NewHorizons/Tools/ShipLogPatches.cs index 7faf1b65..060c98e2 100644 --- a/NewHorizons/Tools/ShipLogPatches.cs +++ b/NewHorizons/Tools/ShipLogPatches.cs @@ -89,9 +89,17 @@ namespace NewHorizons.Tools return false; } - public static bool OnShipLogManagerCheckForCompletionAchievement() + public static bool OnShipLogManagerCheckForCompletionAchievement(ShipLogManager __instance) { - return Main.Instance.CurrentStarSystem == "SolarSystem"; + foreach (KeyValuePair keyValuePair in __instance._factDict) + { + if (ShipLogHandler.IsVanillaAstroID(__instance.GetEntry(keyValuePair.Value.GetEntryID()).GetAstroObjectID()) && !keyValuePair.Value.IsRumor() && !keyValuePair.Value.IsRevealed() && !keyValuePair.Key.Equals("TH_VILLAGE_X3") && !keyValuePair.Key.Equals("GD_GABBRO_ISLAND_X1") && __instance.GetEntry(keyValuePair.Value.GetEntryID()).GetCuriosityName() != CuriosityName.InvisiblePlanet) + { + return false; + } + } + Achievements.Earn(Achievements.Type.STUDIOUS); + return false; } public static bool OnShipLogManagerStart(ShipLogManager __instance) From 7469b03677348571d3d626dd5de37779a9212387 Mon Sep 17 00:00:00 2001 From: Ben C Date: Thu, 10 Feb 2022 19:15:10 -0500 Subject: [PATCH 30/50] Accounted For Sand Funnel --- NewHorizons/Builder/ShipLog/MapModeBuilder.cs | 4 ++++ NewHorizons/Tools/ShipLogPatches.cs | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs index 7913453c..c25c891c 100644 --- a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs @@ -223,6 +223,10 @@ namespace NewHorizons.Builder.ShipLog { int[] navIndex = astroIdToNavIndex[astroObject.GetID()]; navMatrix[navIndex[0]][navIndex[1]] = null; + if (astroObject.GetID() == "CAVE_TWIN" || astroObject.GetID() == "TOWER_TWIN") + { + Object.Destroy(GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/" + "SandFunnel")); + } } Object.Destroy(gameObject); } diff --git a/NewHorizons/Tools/ShipLogPatches.cs b/NewHorizons/Tools/ShipLogPatches.cs index 060c98e2..efb474d7 100644 --- a/NewHorizons/Tools/ShipLogPatches.cs +++ b/NewHorizons/Tools/ShipLogPatches.cs @@ -156,7 +156,10 @@ namespace NewHorizons.Tools { Object.Destroy(GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/" + gameObject.name)); } - __instance._sandFunnel = __instance.gameObject.AddComponent(); + if (GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/" + "SandFunnel") == null) + { + __instance._sandFunnel = __instance.gameObject.AddComponent(); + } } } From f326422b85990f5a895f643c0b500328d60082c9 Mon Sep 17 00:00:00 2001 From: Ben C Date: Thu, 10 Feb 2022 19:21:30 -0500 Subject: [PATCH 31/50] Updated schema.json --- .../Builder/ShipLog/RumorModeBuilder.cs | 4 +-- NewHorizons/External/ShipLogModule.cs | 2 +- NewHorizons/schema.json | 30 ++++++++++++++++++- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs index b3d5c298..948dfa33 100644 --- a/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs @@ -203,8 +203,8 @@ namespace NewHorizons.Builder.ShipLog private static Vector2? GetManualEntryPosition(string entryId, ShipLogModule config) { - if (config.positions == null) return null; - foreach (ShipLogModule.EntryPositionInfo position in config.positions) + if (config.entryPositions == null) return null; + foreach (ShipLogModule.EntryPositionInfo position in config.entryPositions) { if (position.id == entryId) { diff --git a/NewHorizons/External/ShipLogModule.cs b/NewHorizons/External/ShipLogModule.cs index f329bedb..9b3be829 100644 --- a/NewHorizons/External/ShipLogModule.cs +++ b/NewHorizons/External/ShipLogModule.cs @@ -9,7 +9,7 @@ namespace NewHorizons.External public string[] initialReveal; public MapModeInfo mapMode; public CuriosityColorInfo[] curiosities; - public EntryPositionInfo[] positions; + public EntryPositionInfo[] entryPositions; public class MapModeInfo { diff --git a/NewHorizons/schema.json b/NewHorizons/schema.json index 934b3218..966be976 100644 --- a/NewHorizons/schema.json +++ b/NewHorizons/schema.json @@ -1266,7 +1266,7 @@ "type": "string" } }, - "positions": { + "entryPositions": { "type": "array", "description": "A set of positions to use instead of automatic layout in rumor mode", "items": { @@ -1303,6 +1303,34 @@ "type": "string", "description": "The path to the sprite to show when the planet is unexplored in map mode" }, + "manualPosition": { + "type": "object", + "description": "Manually place this planet at the specified position", + "properties": { + "x": { + "type": "number", + "default": 0 + }, + "y": { + "type": "number", + "default": 0 + } + } + }, + "manualNavigationPosition": { + "type": "object", + "description": "Specify where this planet is in terms of navigation", + "properties": { + "x": { + "type": "integer", + "default": 0 + }, + "y": { + "type": "integer", + "default": 0 + } + } + }, "scale": { "type": "number", "description": "Scale to apply to the planet in map mode", From bf8806376271d6698b3ebd37522381bbf31562fc Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 19 Feb 2022 15:43:03 -0500 Subject: [PATCH 32/50] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 8c267445..b03bd788 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ Check the ship's log for how to use your warp drive to travel between star syste - [Water](#water) - [Lava](#lava) - [Sand](#sand) + - [Ship Log](#ship-log] - [How to destroy existing planets](#how-to-destroy-existing-planets) - [How to update existing planets](#how-to-update-existing-planets) - [How to use New Horizons in other mods](#how-to-use-new-horizons-in-other-mods) @@ -575,6 +576,9 @@ This allows you to make black holes and white holes, and to pair them. - "tint" : (colour) - "curve": (scale curve) +### Ship Log +You can make custom ship logs for your planets. There's a guide [here](https://gist.github.com/Bwc9876/1817f8726e7f1900e57e3b05dd047d86#intro). + ### How to destroy existing planets You do this (but with the appropriate name) as it's own config. From b33d6fc67ff63ecc2516c5f3426528179c6b808f Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 19 Feb 2022 15:43:17 -0500 Subject: [PATCH 33/50] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b03bd788..12c1d771 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Check the ship's log for how to use your warp drive to travel between star syste - [Water](#water) - [Lava](#lava) - [Sand](#sand) - - [Ship Log](#ship-log] + - [Ship Log](#ship-log) - [How to destroy existing planets](#how-to-destroy-existing-planets) - [How to update existing planets](#how-to-update-existing-planets) - [How to use New Horizons in other mods](#how-to-use-new-horizons-in-other-mods) From ee37fe7164efe5c1f48be7f642086b1634fda947 Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Sat, 19 Feb 2022 17:47:13 -0500 Subject: [PATCH 34/50] Make ShipLog module not required for others to work --- NewHorizons/Builder/ShipLog/MapModeBuilder.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs index c25c891c..f50d4cbc 100644 --- a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs @@ -24,6 +24,8 @@ namespace NewHorizons.Builder.ShipLog bool flagAutoPositionUsed = false; foreach (NewHorizonsBody body in bodies.Where(b => ShipLogHandler.IsVanillaBody(b) == false)) { + if (body.Config.ShipLog == null) continue; + if (body.Config.ShipLog?.mapMode?.manualPosition == null) { flagAutoPositionUsed = true; @@ -204,6 +206,8 @@ namespace NewHorizons.Builder.ShipLog foreach (NewHorizonsBody body in bodies.Where(b => ShipLogHandler.IsVanillaBody(b) == false)) { + if (body.Config.ShipLog == null) continue; + GameObject newMapModeGO = CreateMapModeGameObject(body, transformParent, layer, body.Config.ShipLog?.mapMode?.manualPosition); ShipLogAstroObject newAstroObject = AddShipLogAstroObject(newMapModeGO, body, greyScaleMaterial, layer); MakeDetails(body, newMapModeGO.transform, greyScaleMaterial); @@ -225,10 +229,14 @@ namespace NewHorizons.Builder.ShipLog navMatrix[navIndex[0]][navIndex[1]] = null; if (astroObject.GetID() == "CAVE_TWIN" || astroObject.GetID() == "TOWER_TWIN") { - Object.Destroy(GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/" + "SandFunnel")); + GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/" + "SandFunnel").SetActive(false); } } - Object.Destroy(gameObject); + else if(body.Config.Name == "SandFunnel") + { + GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/" + "SandFunnel").SetActive(false); + } + gameObject.SetActive(false); } else { From 790303afde04718f7cd1335fc093d6e84b0df93d Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Sat, 19 Feb 2022 23:47:50 -0500 Subject: [PATCH 35/50] Fix entry image loading --- NewHorizons/Builder/ShipLog/RumorModeBuilder.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs index 948dfa33..a8b2cf35 100644 --- a/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs @@ -186,17 +186,17 @@ namespace NewHorizons.Builder.ShipLog private static Sprite GetEntrySprite(string entryId, NewHorizonsBody body) { - IModAssets assets = body.Mod.Assets; - string path = body.Config.ShipLog.spriteFolder + "/" + entryId + ".png"; - if (File.Exists(Main.Instance.ModHelper.Manifest.ModFolderPath + path)) + string relativePath = body.Config.ShipLog.spriteFolder + "/" + entryId + ".png"; + try { - Texture2D newTexture = assets.GetTexture(path); + Texture2D newTexture = body.Mod.Assets.GetTexture(relativePath); Rect rect = new Rect(0, 0, newTexture.width, newTexture.height); Vector2 pivot = new Vector2(newTexture.width / 2, newTexture.height / 2); return Sprite.Create(newTexture, rect, pivot); } - else + catch(Exception) { + Logger.LogError($"Couldn't load image for {entryId} at {relativePath}"); return null; } } From 518c7e26ae0b34e56a59c5ce77b67a1aa4670fa9 Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Sun, 20 Feb 2022 01:14:19 -0500 Subject: [PATCH 36/50] Fix adding entries to existing ship log ID --- NewHorizons/Builder/ShipLog/RumorModeBuilder.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs index a8b2cf35..e4160ed6 100644 --- a/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs @@ -58,7 +58,6 @@ namespace NewHorizons.Builder.ShipLog } else { - astroBodyId.SetValue(systemName + "/" + astroBodyId.Value); foreach (XElement entryElement in astroBodyFile.DescendantsAndSelf("Entry")) { XElement curiosityName = entryElement.Element("Curiosity"); From 345f5a916969349174113b1cb6942c21d7c42cb3 Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Sun, 20 Feb 2022 01:14:33 -0500 Subject: [PATCH 37/50] Set default values for RevealInfo --- NewHorizons/External/PropModule.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NewHorizons/External/PropModule.cs b/NewHorizons/External/PropModule.cs index 496c7f31..4119ca66 100644 --- a/NewHorizons/External/PropModule.cs +++ b/NewHorizons/External/PropModule.cs @@ -72,9 +72,9 @@ namespace NewHorizons.External public class RevealInfo { - public string revealOn; + public string revealOn = "enter"; public string[] reveals; - public MVector3 position; + public MVector3 position = new MVector3(0, 0, 0); public float radius = 1f; public float maxDistance = -1f; // Snapshot & Observe Only public float maxAngle = 180f; // Observe Only From 69ab1f399aa14c27789e74c43bdcd924c0cbfbb4 Mon Sep 17 00:00:00 2001 From: Nick Date: Sun, 20 Feb 2022 12:17:15 -0500 Subject: [PATCH 38/50] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 12c1d771..1ee7a446 100644 --- a/README.md +++ b/README.md @@ -646,7 +646,7 @@ New Horizons was made with help from: - [jtsalomo](https://github.com/jtsalomo): Implemented [OW_CommonResources](https://github.com/PacificEngine/OW_CommonResources) support introduced in v0.5.0 - [Raicuparta](https://github.com/Raicuparta): Integrated the [New Horizons Template](https://github.com/xen-42/ow-new-horizons-config-template) into the Outer Wilds Mods website - [Nageld](https://github.com/Nageld): Set up xml reading for custom dialogue in v0.8.0 -- [Bwc9876](https://github.com/Bwc9876): Set up ship log entires for planets in v0.8.2 +- [Bwc9876](https://github.com/Bwc9876): Set up ship log entires for planets in v0.9.0 Marshmallow was made with help from: - TAImatem From 193098b8edd052b154fe442db286860995756f52 Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Sun, 20 Feb 2022 19:02:19 -0500 Subject: [PATCH 39/50] Fixes --- .../Builder/ShipLog/RumorModeBuilder.cs | 9 +++--- NewHorizons/Handlers/ShipLogHandler.cs | 30 ++++++++++++------- NewHorizons/Tools/ShipLogPatches.cs | 4 +-- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs index e4160ed6..6647123d 100644 --- a/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs @@ -116,10 +116,11 @@ namespace NewHorizons.Builder.ShipLog { id = entry._id, cardPosition = entryPosition, - sprite = body.Config.ShipLog.spriteFolder == null ? null : GetEntrySprite(entry._id, body), - altSprite = body.Config.ShipLog.spriteFolder == null ? null : GetEntrySprite(entry._id + "_ALT", body) + sprite = body.Config.ShipLog.spriteFolder == null ? null : GetEntrySprite(entry._id, body, true), + altSprite = body.Config.ShipLog.spriteFolder == null ? null : GetEntrySprite(entry._id + "_ALT", body, false) }; entry.SetSprite(newData.sprite == null ? manager._shipLogLibrary.defaultEntrySprite : newData.sprite); + entry.SetAltSprite(newData.sprite == null ? manager._shipLogLibrary.defaultEntrySprite : newData.altSprite); manager._entryDataDict.Add(entry._id, newData); int index = manager._entryList.IndexOf(entry); if (index < manager._entryList.Count - 2 && manager._entryList[index + 1]._astroObjectID != entry._astroObjectID) @@ -183,7 +184,7 @@ namespace NewHorizons.Builder.ShipLog } } - private static Sprite GetEntrySprite(string entryId, NewHorizonsBody body) + private static Sprite GetEntrySprite(string entryId, NewHorizonsBody body, bool logError) { string relativePath = body.Config.ShipLog.spriteFolder + "/" + entryId + ".png"; try @@ -195,7 +196,7 @@ namespace NewHorizons.Builder.ShipLog } catch(Exception) { - Logger.LogError($"Couldn't load image for {entryId} at {relativePath}"); + if(logError) Logger.LogError($"Couldn't load image for {entryId} at {relativePath}"); return null; } } diff --git a/NewHorizons/Handlers/ShipLogHandler.cs b/NewHorizons/Handlers/ShipLogHandler.cs index 2ce83ffe..8b9c5fd3 100644 --- a/NewHorizons/Handlers/ShipLogHandler.cs +++ b/NewHorizons/Handlers/ShipLogHandler.cs @@ -16,7 +16,7 @@ namespace NewHorizons.Builder.Handlers { public static readonly string PAN_ROOT_PATH = "Ship_Body/Module_Cabin/Systems_Cabin/ShipLogPivot/ShipLog/ShipLogPivot/ShipLogCanvas/MapMode/ScaleRoot/PanRoot"; - private static Dictionary _astroIdToBody = new Dictionary(); + private static Dictionary> _astroIdToBodies = new Dictionary>(); private static string[] vanillaBodies; private static string[] vanillaIDs; @@ -39,32 +39,40 @@ namespace NewHorizons.Builder.Handlers public static NewHorizonsBody GetConfigFromID(string id) { - return _astroIdToBody.ContainsKey(id) ? _astroIdToBody[id] : null; + return _astroIdToBodies.ContainsKey(id) ? _astroIdToBodies[id][0] : null; } public static void AddConfig(string id, NewHorizonsBody body) { - if (!_astroIdToBody.ContainsKey(id)) + if (!_astroIdToBodies.ContainsKey(id)) { - _astroIdToBody.Add(id, body); + _astroIdToBodies.Add(id, new List() { body }); + } + else + { + if(!_astroIdToBodies[id].Contains(body)) + _astroIdToBodies[id].Append(body); } } public static string GetAstroObjectId(NewHorizonsBody body) { - if (BodyHasEntries(body)) + foreach(var id in _astroIdToBodies.Keys) { - return CollectionUtilities.KeyByValue(_astroIdToBody, body); - } - else - { - return body.Config.Name; + if (_astroIdToBodies[id].Contains(body)) return id; } + + return body.Config.Name; } public static bool BodyHasEntries(NewHorizonsBody body) { - return _astroIdToBody.ContainsValue(body); + foreach(var id in _astroIdToBodies.Keys) + { + if (_astroIdToBodies[id].Contains(body)) return true; + } + + return false; } } } diff --git a/NewHorizons/Tools/ShipLogPatches.cs b/NewHorizons/Tools/ShipLogPatches.cs index efb474d7..bbb7d064 100644 --- a/NewHorizons/Tools/ShipLogPatches.cs +++ b/NewHorizons/Tools/ShipLogPatches.cs @@ -20,7 +20,7 @@ namespace NewHorizons.Tools Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Awake", typeof(ShipLogPatches), nameof(ShipLogPatches.OnShipLogManagerAwake)); Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Start", typeof(ShipLogPatches), nameof(ShipLogPatches.OnShipLogManagerStart)); - //Main.Instance.ModHelper.HarmonyHelper.AddPrefix("IsFactRevealed", typeof(ShipLogPatches), nameof(ShipLogPatches.OnShipLogManagerIsFactRevealed)); + Main.Instance.ModHelper.HarmonyHelper.AddPrefix("IsFactRevealed", typeof(ShipLogPatches), nameof(ShipLogPatches.OnShipLogManagerIsFactRevealed)); Main.Instance.ModHelper.HarmonyHelper.AddPrefix("CheckForCompletionAchievement", typeof(ShipLogPatches), nameof(ShipLogPatches.OnShipLogManagerCheckForCompletionAchievement)); Main.Instance.ModHelper.HarmonyHelper.AddPrefix("GetCuriosityColor", typeof(ShipLogPatches), nameof(ShipLogPatches.OnUIStyleManagerGetCuriosityColor)); Main.Instance.ModHelper.HarmonyHelper.AddPrefix("Awake", typeof(ShipLogPatches), nameof(ShipLogPatches.DisableShipLogSandFunnel)); @@ -75,8 +75,6 @@ namespace NewHorizons.Tools public static bool OnShipLogManagerIsFactRevealed(ShipLogManager __instance, ref bool __result, string __0) { - Logger.Log(__0); - if (__instance._factDict != null && __instance._factDict.ContainsKey(__0)) { __result = __instance._factDict[__0].IsRevealed(); From 65694f2666ef08bc4ec46c04f0476e450a68c436 Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Mon, 21 Feb 2022 02:47:39 -0500 Subject: [PATCH 40/50] Fix more issues with vanilla bodies --- NewHorizons/Builder/ShipLog/MapModeBuilder.cs | 47 ++++++++++++------- .../Builder/ShipLog/RumorModeBuilder.cs | 10 +++- NewHorizons/Handlers/ShipLogHandler.cs | 4 ++ 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs index f50d4cbc..10520e91 100644 --- a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs @@ -201,25 +201,38 @@ namespace NewHorizons.Builder.ShipLog navMatrix[y][x] = currentNav[y][x]; astroIdToNavIndex.Add(currentNav[y][x].GetID(), new [] {y, x}); } - } + } } - foreach (NewHorizonsBody body in bodies.Where(b => ShipLogHandler.IsVanillaBody(b) == false)) + foreach(NewHorizonsBody body in bodies) { - if (body.Config.ShipLog == null) continue; + if (body.Config.ShipLog?.mapMode?.manualNavigationPosition == null) continue; - GameObject newMapModeGO = CreateMapModeGameObject(body, transformParent, layer, body.Config.ShipLog?.mapMode?.manualPosition); - ShipLogAstroObject newAstroObject = AddShipLogAstroObject(newMapModeGO, body, greyScaleMaterial, layer); - MakeDetails(body, newMapModeGO.transform, greyScaleMaterial); - Vector2 navigationPosition = body.Config.ShipLog?.mapMode?.manualNavigationPosition; - navMatrix[(int) navigationPosition.y][(int) navigationPosition.x] = newAstroObject; - } - - if (Main.Instance.CurrentStarSystem == "SolarSystem") - { - foreach (NewHorizonsBody body in Main.BodyDict["SolarSystem"].Where(ShipLogHandler.IsVanillaBody)) + // Sometimes they got other names idk + var name = body.Config.Name.Replace(" ", ""); + var existingBody = AstroObjectLocator.GetAstroObject(body.Config.Name); + if (existingBody != null) { - GameObject gameObject = GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/" + body.Config.Name.Replace(" ", "")); + var astroName = existingBody.GetAstroObjectName(); + if (astroName == AstroObject.Name.RingWorld) name = "InvisiblePlanet"; + else if (astroName != AstroObject.Name.CustomString) name = astroName.ToString(); + } + // Should probably also just fix the IsVanilla method + var isVanilla = ShipLogHandler.IsVanillaBody(body); + + Logger.Log($"The name: {name} is vanilla? {isVanilla}"); + + if (!isVanilla) + { + GameObject newMapModeGO = CreateMapModeGameObject(body, transformParent, layer, body.Config.ShipLog?.mapMode?.manualPosition); + ShipLogAstroObject newAstroObject = AddShipLogAstroObject(newMapModeGO, body, greyScaleMaterial, layer); + MakeDetails(body, newMapModeGO.transform, greyScaleMaterial); + Vector2 navigationPosition = body.Config.ShipLog?.mapMode?.manualNavigationPosition; + navMatrix[(int)navigationPosition.y][(int)navigationPosition.x] = newAstroObject; + } + else if (Main.Instance.CurrentStarSystem == "SolarSystem") + { + GameObject gameObject = GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/" + name); if (body.Config.Destroy || (body.Config.ShipLog?.mapMode?.remove ?? false)) { ShipLogAstroObject astroObject = gameObject.GetComponent(); @@ -232,7 +245,7 @@ namespace NewHorizons.Builder.ShipLog GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/" + "SandFunnel").SetActive(false); } } - else if(body.Config.Name == "SandFunnel") + else if (name == "SandFunnel") { GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/" + "SandFunnel").SetActive(false); } @@ -247,11 +260,11 @@ namespace NewHorizons.Builder.ShipLog if (body.Config.ShipLog?.mapMode?.manualNavigationPosition != null) { Vector2 navigationPosition = body.Config.ShipLog?.mapMode?.manualNavigationPosition; - navMatrix[(int) navigationPosition.y][(int) navigationPosition.x] = gameObject.GetComponent(); + navMatrix[(int)navigationPosition.y][(int)navigationPosition.x] = gameObject.GetComponent(); } if (body.Config.ShipLog?.mapMode?.scale != null) { - gameObject.transform.localScale = Vector3.one * body.Config.ShipLog.mapMode.scale; + gameObject.transform.localScale = Vector3.one * body.Config.ShipLog.mapMode.scale; } } } diff --git a/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs index 6647123d..3b61eb33 100644 --- a/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs @@ -180,7 +180,15 @@ namespace NewHorizons.Builder.ShipLog { if (_entryIdToRawName.ContainsKey(entry._id)) { - entry._curiosity = _rawNameToCuriosityName[_entryIdToRawName[entry._id]]; + var raw = _entryIdToRawName[entry._id]; + if (_rawNameToCuriosityName.ContainsKey(raw)) + { + entry._curiosity = _rawNameToCuriosityName[raw]; + } + else + { + Logger.LogError($"Couldn't find {raw}. Did you define the curiosity in a json config? Because you have to."); + } } } diff --git a/NewHorizons/Handlers/ShipLogHandler.cs b/NewHorizons/Handlers/ShipLogHandler.cs index 8b9c5fd3..1dec08c2 100644 --- a/NewHorizons/Handlers/ShipLogHandler.cs +++ b/NewHorizons/Handlers/ShipLogHandler.cs @@ -34,6 +34,10 @@ namespace NewHorizons.Builder.Handlers public static bool IsVanillaBody(NewHorizonsBody body) { + var existingBody = AstroObjectLocator.GetAstroObject(body.Config.Name); + if (existingBody != null && existingBody.GetAstroObjectName() != AstroObject.Name.CustomString) + return true; + return vanillaBodies.Contains(body.Config.Name.Replace(" ", "")); } From 1e601f6c02f2d21c824a762d5ef7b4320fca3076 Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Mon, 21 Feb 2022 11:11:04 -0500 Subject: [PATCH 41/50] Allow mods to change the same planet shiplog --- NewHorizons/Builder/ShipLog/MapModeBuilder.cs | 14 ++--- .../Builder/ShipLog/RumorModeBuilder.cs | 7 ++- NewHorizons/Handlers/ShipLogHandler.cs | 60 ++++++++++++------- NewHorizons/Main.cs | 8 --- 4 files changed, 49 insertions(+), 40 deletions(-) diff --git a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs index 10520e91..6b9164ff 100644 --- a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs @@ -41,25 +41,23 @@ namespace NewHorizons.Builder.ShipLog } } - if (flagAutoPositionUsed && flagManualPositionUsed) + if(flagManualPositionUsed) { - Logger.LogError("Can't mix manual and automatic layout of ship log map mode, skipping generation"); - return null; + if (flagAutoPositionUsed && flagManualPositionUsed) + Logger.LogWarning("Can't mix manual and automatic layout of ship log map mode, defaulting to manual"); + return ConstructMapModeManual(bodies, transformParent, greyScaleMaterial, currentNav, layer); } else if (flagAutoPositionUsed) { return ConstructMapModeAuto(bodies, transformParent, greyScaleMaterial, layer); } - else if (flagManualPositionUsed) - { - return ConstructMapModeManual(bodies, transformParent, greyScaleMaterial, currentNav, layer); - } + return null; } public static string GetAstroBodyShipLogName(string id) { - return ShipLogHandler.GetConfigFromID(id)?.Config?.Name ?? id; + return ShipLogHandler.GetNameFromAstroID(id) ?? id; } private static GameObject CreateImage(GameObject nodeGO, IModAssets assets, string imagePath, string name, int layer) diff --git a/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs index 3b61eb33..489715c8 100644 --- a/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs @@ -58,12 +58,14 @@ namespace NewHorizons.Builder.ShipLog } else { + var entryIDs = new List(); foreach (XElement entryElement in astroBodyFile.DescendantsAndSelf("Entry")) { XElement curiosityName = entryElement.Element("Curiosity"); XElement id = entryElement.Element("ID"); if (curiosityName != null && id != null && _entryIdToRawName.ContainsKey(id.Value) == false) { + entryIDs.Add(id.Value); _entryIdToRawName.Add(id.Value, curiosityName.Value); } foreach (XElement childEntryElement in entryElement.Elements("Entry")) @@ -80,6 +82,7 @@ namespace NewHorizons.Builder.ShipLog { _entryIdToRawName.Add(childId.Value, childCuriosityName.Value); } + entryIDs.Add(childId.Value); } AddTranslation(childEntryElement); } @@ -88,7 +91,7 @@ namespace NewHorizons.Builder.ShipLog TextAsset newAsset = new TextAsset(astroBodyFile.ToString()); List newBodies = new List(manager._shipLogXmlAssets) { newAsset }; manager._shipLogXmlAssets = newBodies.ToArray(); - ShipLogHandler.AddConfig(astroBodyId.Value, body); + ShipLogHandler.AddConfig(astroBodyId.Value, entryIDs, body); } } @@ -101,7 +104,7 @@ namespace NewHorizons.Builder.ShipLog { if (manager._entryDataDict.ContainsKey(entry._id) == false) { - NewHorizonsBody body = ShipLogHandler.GetConfigFromID(entry._astroObjectID); + NewHorizonsBody body = ShipLogHandler.GetConfigFromEntryID(entry._id); Vector2? manualEntryPosition = GetManualEntryPosition(entry._id, body.Config.ShipLog); Vector2 entryPosition; if (manualEntryPosition == null) diff --git a/NewHorizons/Handlers/ShipLogHandler.cs b/NewHorizons/Handlers/ShipLogHandler.cs index 1dec08c2..5975e5f3 100644 --- a/NewHorizons/Handlers/ShipLogHandler.cs +++ b/NewHorizons/Handlers/ShipLogHandler.cs @@ -16,12 +16,22 @@ namespace NewHorizons.Builder.Handlers { public static readonly string PAN_ROOT_PATH = "Ship_Body/Module_Cabin/Systems_Cabin/ShipLogPivot/ShipLog/ShipLogPivot/ShipLogCanvas/MapMode/ScaleRoot/PanRoot"; - private static Dictionary> _astroIdToBodies = new Dictionary>(); + // NewHorizonsBody -> EntryIDs + private static Dictionary> _nhBodyToEntryIDs; + //EntryID -> NewHorizonsBody + private static Dictionary _entryIDsToNHBody; + // NewHorizonsBody -> AstroID + private static Dictionary _nhBodyToAstroIDs; + private static string[] vanillaBodies; private static string[] vanillaIDs; public static void Init() { + _nhBodyToEntryIDs = new Dictionary>(); + _entryIDsToNHBody = new Dictionary(); + _nhBodyToAstroIDs = new Dictionary(); + List gameObjects = SearchUtilities.GetAllChildren(GameObject.Find(PAN_ROOT_PATH)); vanillaBodies = gameObjects.ConvertAll(g => g.name).ToArray(); vanillaIDs = gameObjects.ConvertAll(g => g.GetComponent()?.GetID()).ToArray(); @@ -41,42 +51,48 @@ namespace NewHorizons.Builder.Handlers return vanillaBodies.Contains(body.Config.Name.Replace(" ", "")); } - public static NewHorizonsBody GetConfigFromID(string id) + public static string GetNameFromAstroID(string astroID) { - return _astroIdToBodies.ContainsKey(id) ? _astroIdToBodies[id][0] : null; + return CollectionUtilities.KeyByValue(_nhBodyToAstroIDs, astroID)?.Config.Name; } - public static void AddConfig(string id, NewHorizonsBody body) + public static NewHorizonsBody GetConfigFromEntryID(string entryID) { - if (!_astroIdToBodies.ContainsKey(id)) - { - _astroIdToBodies.Add(id, new List() { body }); - } + if (_entryIDsToNHBody.ContainsKey(entryID)) return _entryIDsToNHBody[entryID]; else { - if(!_astroIdToBodies[id].Contains(body)) - _astroIdToBodies[id].Append(body); + Logger.LogError($"Couldn't find NewHorizonsBody that corresponds to {entryID}"); + return null; + } + } + + public static void AddConfig(string astroID, List entryIDs, NewHorizonsBody body) + { + // Nice to be able to just get the AstroID from the body + if (!_nhBodyToEntryIDs.ContainsKey(body)) _nhBodyToEntryIDs.Add(body, entryIDs); + else Logger.LogWarning($"Possible duplicate shiplog entry {body.Config.Name}"); + + // AstroID + if (!_nhBodyToAstroIDs.ContainsKey(body)) _nhBodyToAstroIDs.Add(body, astroID); + else Logger.LogWarning($"Possible duplicate shiplog entry {astroID} for {body.Config.Name}"); + + // EntryID to Body + foreach (var entryID in entryIDs) + { + if (!_entryIDsToNHBody.ContainsKey(entryID)) _entryIDsToNHBody.Add(entryID, body); + else Logger.LogWarning($"Possible duplicate shiplog entry {entryID} for {astroID} from NewHorizonsBody {body.Config.Name}"); } } public static string GetAstroObjectId(NewHorizonsBody body) { - foreach(var id in _astroIdToBodies.Keys) - { - if (_astroIdToBodies[id].Contains(body)) return id; - } - - return body.Config.Name; + if (_nhBodyToAstroIDs.ContainsKey(body)) return _nhBodyToAstroIDs[body]; + else return body.Config.Name; } public static bool BodyHasEntries(NewHorizonsBody body) { - foreach(var id in _astroIdToBodies.Keys) - { - if (_astroIdToBodies[id].Contains(body)) return true; - } - - return false; + return _nhBodyToAstroIDs.ContainsKey(body) && _nhBodyToAstroIDs[body].Length > 0; } } } diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index a0ff7957..23bb0e27 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -14,21 +14,13 @@ using NewHorizons.Utility; using OWML.Common; using OWML.ModHelper; using OWML.Utils; -using PacificEngine.OW_CommonResources.Game.Player; -using PacificEngine.OW_CommonResources.Game.Resource; -using PacificEngine.OW_CommonResources.Game.State; using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Reflection; -using Epic.OnlineServices; -using PacificEngine.OW_CommonResources.Game.Player; using UnityEngine; using UnityEngine.SceneManagement; -using UnityEngine.UI; using Logger = NewHorizons.Utility.Logger; -using NewHorizons.Handlers; namespace NewHorizons { From f8178a78f88350f90e895004c9babc0bad2394df Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Mon, 21 Feb 2022 19:08:14 -0500 Subject: [PATCH 42/50] Auto-outline mapmode planets --- NewHorizons/Builder/ShipLog/MapModeBuilder.cs | 55 ++++++----- NewHorizons/Utility/ImageUtilities.cs | 92 +++++++++---------- 2 files changed, 79 insertions(+), 68 deletions(-) diff --git a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs index 6b9164ff..93fc8570 100644 --- a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs @@ -60,7 +60,7 @@ namespace NewHorizons.Builder.ShipLog return ShipLogHandler.GetNameFromAstroID(id) ?? id; } - private static GameObject CreateImage(GameObject nodeGO, IModAssets assets, string imagePath, string name, int layer) + private static GameObject CreateImage(GameObject nodeGO, IModAssets assets, Texture2D texture, string name, int layer) { GameObject newImageGO = new GameObject(name); newImageGO.layer = layer; @@ -72,17 +72,11 @@ namespace NewHorizons.Builder.ShipLog transform.localScale = Vector3.one; Image newImage = newImageGO.AddComponent(); - if (imagePath == "DEFAULT") - { - newImage.sprite = Locator.GetShipLogManager()._shipLogLibrary.defaultEntrySprite; - } - else - { - Texture2D newTexture = assets.GetTexture(imagePath); - Rect rect = new Rect(0, 0, newTexture.width, newTexture.height); - Vector2 pivot = new Vector2(newTexture.width / 2, newTexture.height / 2); - newImage.sprite = Sprite.Create(newTexture, rect, pivot); - } + + Rect rect = new Rect(0, 0, texture.width, texture.height); + Vector2 pivot = new Vector2(texture.width / 2, texture.height / 2); + newImage.sprite = Sprite.Create(texture, rect, pivot); + return newImageGO; } @@ -110,9 +104,20 @@ namespace NewHorizons.Builder.ShipLog ShipLogAstroObject astroObject = gameObject.AddComponent(); astroObject._id = ShipLogHandler.GetAstroObjectId(body); - string imagePath = body.Config.ShipLog?.mapMode?.revealedSprite ?? "DEFAULT"; - string outlinePath = body.Config.ShipLog?.mapMode?.outlineSprite ?? imagePath; - astroObject._imageObj = CreateImage(gameObject, body.Mod.Assets, imagePath, body.Config.Name + " Revealed", layer); + Texture2D image; + Texture2D outline; + + string imagePath = body.Config.ShipLog?.mapMode?.revealedSprite; + string outlinePath = body.Config.ShipLog?.mapMode?.outlineSprite; + + if (imagePath != null) image = body.Mod.Assets.GetTexture(imagePath); + else image = Locator.GetShipLogManager()._shipLogLibrary.defaultEntrySprite.texture; + + if (outlinePath != null) outline = body.Mod.Assets.GetTexture(outlinePath); + else outline = ImageUtilities.MakeOutline(image, Color.white, 12); + + astroObject._imageObj = CreateImage(gameObject, body.Mod.Assets, image, body.Config.Name + " Revealed", layer); + astroObject._outlineObj = CreateImage(gameObject, body.Mod.Assets, outline, body.Config.Name + " Outline", layer); if (ShipLogHandler.BodyHasEntries(body)) { Image revealedImage = astroObject._imageObj.GetComponent(); @@ -121,7 +126,6 @@ namespace NewHorizons.Builder.ShipLog revealedImage.color = Color.white; astroObject._image = revealedImage; } - astroObject._outlineObj = CreateImage(gameObject, body.Mod.Assets, outlinePath, body.Config.Name + " Outline", layer); astroObject._unviewedObj = Object.Instantiate(unviewedReference, gameObject.transform, false); astroObject._invisibleWhenHidden = body.Config.ShipLog?.mapMode?.invisibleWhenHidden ?? false; @@ -144,11 +148,20 @@ namespace NewHorizons.Builder.ShipLog detailTransform.localRotation = Quaternion.Euler(0f, 0f, info.rotation); detailTransform.localScale = (Vector2)(info.scale ?? new MVector2(0, 0)); - string revealedPath = info.revealedSprite ?? "DEFAULT"; - string outlinePath = info.outlineSprite ?? revealedPath; + Texture2D image; + Texture2D outline; - Image revealedImage = CreateImage(detailGameObject, assets, revealedPath, "Detail Revealed", parent.gameObject.layer).GetComponent(); - Image outlineImage = CreateImage(detailGameObject, assets, outlinePath, "Detail Outline", parent.gameObject.layer).GetComponent(); + string imagePath = info.revealedSprite; + string outlinePath = info.outlineSprite; + + if (imagePath != null) image = assets.GetTexture(imagePath); + else image = Locator.GetShipLogManager()._shipLogLibrary.defaultEntrySprite.texture; + + if (outlinePath != null) outline = assets.GetTexture(outlinePath); + else outline = ImageUtilities.MakeOutline(image, Color.white, 12); + + Image revealedImage = CreateImage(detailGameObject, assets, image, "Detail Revealed", parent.gameObject.layer).GetComponent(); + Image outlineImage = CreateImage(detailGameObject, assets, outline, "Detail Outline", parent.gameObject.layer).GetComponent(); ShipLogDetail detail = detailGameObject.AddComponent(); detail.Init(info, revealedImage, outlineImage, greyScaleMaterial); @@ -218,8 +231,6 @@ namespace NewHorizons.Builder.ShipLog // Should probably also just fix the IsVanilla method var isVanilla = ShipLogHandler.IsVanillaBody(body); - Logger.Log($"The name: {name} is vanilla? {isVanilla}"); - if (!isVanilla) { GameObject newMapModeGO = CreateMapModeGameObject(body, transformParent, layer, body.Config.ShipLog?.mapMode?.manualPosition); diff --git a/NewHorizons/Utility/ImageUtilities.cs b/NewHorizons/Utility/ImageUtilities.cs index 7877e218..b4d4d751 100644 --- a/NewHorizons/Utility/ImageUtilities.cs +++ b/NewHorizons/Utility/ImageUtilities.cs @@ -1,10 +1,55 @@ -using System.IO; +using System; +using System.IO; using UnityEngine; namespace NewHorizons.Utility { static class ImageUtilities { + public static Texture2D MakeOutline(Texture2D texture, Color color, int thickness) + { + var outline = new Texture2D(texture.width, texture.height, TextureFormat.ARGB32, false); + var outlinePixels = new Color[texture.width * texture.height]; + var pixels = texture.GetPixels(); + + for (int x = 0; x < texture.width; x++) + { + for (int y = 0; y < texture.height; y++) + { + var fillColor = new Color(0, 0, 0, 0); + + if(pixels[x + y * texture.width].a == 1 && CloseToTransparent(pixels, texture.width, texture.height, x, y, thickness)) + { + fillColor = color; + } + outlinePixels[x + y * texture.width] = fillColor; + } + } + + outline.SetPixels(outlinePixels); + outline.Apply(); + + return outline; + } + + private static bool CloseToTransparent(Color[] pixels, int width, int height, int x, int y, int thickness) + { + // Check nearby + var minX = Math.Max(0, x - thickness/2); + var minY = Math.Max(0, y - thickness/2); + var maxX = Math.Min(width, x + thickness/2); + var maxY = Math.Min(height, y + thickness/2); + + for (int i = minX; i < maxX; i++) + { + for (int j = minY; j < maxY; j++) + { + if (pixels[i + j * width].a < 1) return true; + } + } + return false; + } + public static Texture2D TintImage(Texture2D image, Color tint) { var pixels = image.GetPixels(); @@ -73,50 +118,5 @@ namespace NewHorizons.Utility tex.Apply(); return tex; } - - // Thank you PETERSVP - public static Texture2D Scaled(Texture2D src, int width, int height, FilterMode mode = FilterMode.Trilinear) - { - Rect texR = new Rect(0, 0, width, height); - _gpu_scale(src, width, height, mode); - - //Get rendered data back to a new texture - Texture2D result = new Texture2D(width, height, TextureFormat.ARGB32, true); - result.Resize(width, height); - result.ReadPixels(texR, 0, 0, true); - return result; - } - - public static void Scale(Texture2D tex, int width, int height, FilterMode mode = FilterMode.Trilinear) - { - Rect texR = new Rect(0, 0, width, height); - _gpu_scale(tex, width, height, mode); - - // Update new texture - tex.Resize(width, height); - tex.ReadPixels(texR, 0, 0, true); - tex.Apply(true); //Remove this if you hate us applying textures for you :) - } - - // Internal unility that renders the source texture into the RTT - the scaling method itself. - static void _gpu_scale(Texture2D src, int width, int height, FilterMode fmode) - { - //We need the source texture in VRAM because we render with it - src.filterMode = fmode; - src.Apply(true); - - //Using RTT for best quality and performance. Thanks, Unity 5 - RenderTexture rtt = new RenderTexture(width, height, 32); - - //Set the RTT in order to render to it - Graphics.SetRenderTarget(rtt); - - //Setup 2D matrix in range 0..1, so nobody needs to care about sized - GL.LoadPixelMatrix(0, 1, 1, 0); - - //Then clear & draw the texture to fill the entire RTT. - GL.Clear(true, true, new Color(0, 0, 0, 0)); - Graphics.DrawTexture(new Rect(0, 0, 1, 1), src); - } } } From 38ee1ae7a2f45f348ad560559a80abbc0487761b Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Mon, 21 Feb 2022 22:40:21 -0500 Subject: [PATCH 43/50] Refresh rumours properly between systems --- NewHorizons/Builder/ShipLog/RumorModeBuilder.cs | 16 ++++++++++++---- NewHorizons/Tools/ShipLogPatches.cs | 1 + 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs index 489715c8..7117fb68 100644 --- a/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs @@ -16,10 +16,18 @@ namespace NewHorizons.Builder.ShipLog { public static class RumorModeBuilder { - private static readonly Dictionary _curiosityColors = new Dictionary(); - private static readonly Dictionary _curiosityHighlightColors = new Dictionary(); - private static readonly Dictionary _rawNameToCuriosityName = new Dictionary(); - private static readonly Dictionary _entryIdToRawName = new Dictionary(); + private static Dictionary _curiosityColors; + private static Dictionary _curiosityHighlightColors; + private static Dictionary _rawNameToCuriosityName; + private static Dictionary _entryIdToRawName; + + public static void Init() + { + _curiosityColors = new Dictionary(); + _curiosityHighlightColors = new Dictionary(); + _rawNameToCuriosityName = new Dictionary(); + _entryIdToRawName = new Dictionary(); + } public static void AddCuriosityColors(ShipLogModule.CuriosityColorInfo[] newColors) { diff --git a/NewHorizons/Tools/ShipLogPatches.cs b/NewHorizons/Tools/ShipLogPatches.cs index bbb7d064..2c25b921 100644 --- a/NewHorizons/Tools/ShipLogPatches.cs +++ b/NewHorizons/Tools/ShipLogPatches.cs @@ -33,6 +33,7 @@ namespace NewHorizons.Tools public static void OnShipLogManagerAwake(ShipLogManager __instance) { + RumorModeBuilder.Init(); ShipLogHandler.Init(); Logger.Log("Beginning Ship Log Generation For: " + Main.Instance.CurrentStarSystem, Logger.LogType.Log); if (Main.Instance.CurrentStarSystem != "SolarSystem") From 85cc179240281b802d8c9c5cc8011051b176c149 Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Mon, 21 Feb 2022 22:40:40 -0500 Subject: [PATCH 44/50] Autogenerate map mode by default --- NewHorizons/External/PlanetConfig.cs | 1 + NewHorizons/External/ShipLogModule.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/NewHorizons/External/PlanetConfig.cs b/NewHorizons/External/PlanetConfig.cs index f084011f..d23a58ab 100644 --- a/NewHorizons/External/PlanetConfig.cs +++ b/NewHorizons/External/PlanetConfig.cs @@ -37,6 +37,7 @@ namespace NewHorizons.External // Always have to have a base module Base = new BaseModule(); Orbit = new OrbitModule(); + ShipLog = new ShipLogModule(); if (dict == null) return; diff --git a/NewHorizons/External/ShipLogModule.cs b/NewHorizons/External/ShipLogModule.cs index 9b3be829..dfd17be1 100644 --- a/NewHorizons/External/ShipLogModule.cs +++ b/NewHorizons/External/ShipLogModule.cs @@ -7,7 +7,7 @@ namespace NewHorizons.External public string xmlFile; public string spriteFolder; public string[] initialReveal; - public MapModeInfo mapMode; + public MapModeInfo mapMode = new MapModeInfo(); public CuriosityColorInfo[] curiosities; public EntryPositionInfo[] entryPositions; From d4c355059c8c2e2579d8f9b800829697a7d20875 Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Mon, 21 Feb 2022 22:41:00 -0500 Subject: [PATCH 45/50] Remove some random state init thing idk what that is --- NewHorizons/Main.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index 23bb0e27..9751e6e3 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -4,6 +4,7 @@ using NewHorizons.Builder.Body; using NewHorizons.Builder.General; using NewHorizons.Builder.Orbital; using NewHorizons.Builder.Props; +using NewHorizons.Builder.ShipLog; using NewHorizons.Builder.Updater; using NewHorizons.Components; using NewHorizons.External; @@ -75,7 +76,7 @@ namespace NewHorizons Logger.LogWarning("Couldn't find planets folder"); } - UnityEngine.Random.InitState((int)DateTime.Now.Ticks); + //UnityEngine.Random.InitState((int)DateTime.Now.Ticks); Instance.ModHelper.Events.Unity.FireOnNextUpdate(() => OnSceneLoaded(SceneManager.GetActiveScene(), LoadSceneMode.Single)); } From a6b9ceea622960cfd9777aa16bb8f154d63b7ed8 Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Mon, 21 Feb 2022 22:41:15 -0500 Subject: [PATCH 46/50] Slightly improve generated map mode markers --- NewHorizons/Builder/ShipLog/MapModeBuilder.cs | 84 +++++++++++++++---- NewHorizons/Utility/ImageUtilities.cs | 18 ++++ 2 files changed, 87 insertions(+), 15 deletions(-) diff --git a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs index 93fc8570..7d31418e 100644 --- a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs @@ -10,6 +10,7 @@ using UnityEngine; using UnityEngine.UI; using Logger = NewHorizons.Utility.Logger; using NewHorizons.Builder.Handlers; +using System; namespace NewHorizons.Builder.ShipLog { @@ -19,7 +20,9 @@ namespace NewHorizons.Builder.ShipLog public static ShipLogAstroObject[][] ConstructMapMode(string systemName, GameObject transformParent, ShipLogAstroObject[][] currentNav, int layer) { Material greyScaleMaterial = GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/TimberHearth/Sprite").GetComponent().material; - List bodies = Main.BodyDict[systemName].Where(b => (b.Config.ShipLog?.mapMode?.remove ?? false) == false).ToList(); + List bodies = Main.BodyDict[systemName].Where( + b => (b.Config.ShipLog?.mapMode?.remove ?? false) == false + ).ToList(); bool flagManualPositionUsed = systemName == "SolarSystem"; bool flagAutoPositionUsed = false; foreach (NewHorizonsBody body in bodies.Where(b => ShipLogHandler.IsVanillaBody(b) == false)) @@ -111,10 +114,10 @@ namespace NewHorizons.Builder.ShipLog string outlinePath = body.Config.ShipLog?.mapMode?.outlineSprite; if (imagePath != null) image = body.Mod.Assets.GetTexture(imagePath); - else image = Locator.GetShipLogManager()._shipLogLibrary.defaultEntrySprite.texture; + else image = AutoGenerateMapModePicture(body); if (outlinePath != null) outline = body.Mod.Assets.GetTexture(outlinePath); - else outline = ImageUtilities.MakeOutline(image, Color.white, 12); + else outline = ImageUtilities.MakeOutline(image, Color.white, 10); astroObject._imageObj = CreateImage(gameObject, body.Mod.Assets, image, body.Config.Name + " Revealed", layer); astroObject._outlineObj = CreateImage(gameObject, body.Mod.Assets, outline, body.Config.Name + " Outline", layer); @@ -127,7 +130,7 @@ namespace NewHorizons.Builder.ShipLog astroObject._image = revealedImage; } - astroObject._unviewedObj = Object.Instantiate(unviewedReference, gameObject.transform, false); + astroObject._unviewedObj = GameObject.Instantiate(unviewedReference, gameObject.transform, false); astroObject._invisibleWhenHidden = body.Config.ShipLog?.mapMode?.invisibleWhenHidden ?? false; Rect imageRect = astroObject._imageObj.GetComponent().rect; @@ -137,7 +140,7 @@ namespace NewHorizons.Builder.ShipLog #endregion # region Details - private static void MakeDetail(ShipLogModule.ShipLogDetailInfo info, Transform parent, IModAssets assets, Material greyScaleMaterial) + private static void MakeDetail(ShipLogModule.ShipLogDetailInfo info, Transform parent, NewHorizonsBody body, Material greyScaleMaterial) { GameObject detailGameObject = new GameObject("Detail"); detailGameObject.transform.SetParent(parent); @@ -154,14 +157,14 @@ namespace NewHorizons.Builder.ShipLog string imagePath = info.revealedSprite; string outlinePath = info.outlineSprite; - if (imagePath != null) image = assets.GetTexture(imagePath); - else image = Locator.GetShipLogManager()._shipLogLibrary.defaultEntrySprite.texture; + if (imagePath != null) image = body.Mod.Assets.GetTexture(imagePath); + else image = AutoGenerateMapModePicture(body); - if (outlinePath != null) outline = assets.GetTexture(outlinePath); - else outline = ImageUtilities.MakeOutline(image, Color.white, 12); + if (outlinePath != null) outline = body.Mod.Assets.GetTexture(outlinePath); + else outline = ImageUtilities.MakeOutline(image, Color.white, 10); - Image revealedImage = CreateImage(detailGameObject, assets, image, "Detail Revealed", parent.gameObject.layer).GetComponent(); - Image outlineImage = CreateImage(detailGameObject, assets, outline, "Detail Outline", parent.gameObject.layer).GetComponent(); + Image revealedImage = CreateImage(detailGameObject, body.Mod.Assets, image, "Detail Revealed", parent.gameObject.layer).GetComponent(); + Image outlineImage = CreateImage(detailGameObject, body.Mod.Assets, outline, "Detail Outline", parent.gameObject.layer).GetComponent(); ShipLogDetail detail = detailGameObject.AddComponent(); detail.Init(info, revealedImage, outlineImage, greyScaleMaterial); @@ -183,7 +186,7 @@ namespace NewHorizons.Builder.ShipLog foreach (ShipLogModule.ShipLogDetailInfo detailInfo in body.Config.ShipLog.mapMode.details) { - MakeDetail(detailInfo, detailsTransform, body.Mod.Assets, greyScaleMaterial); + MakeDetail(detailInfo, detailsTransform, body, greyScaleMaterial); } detailsParent.SetActive(true); } @@ -325,7 +328,7 @@ namespace NewHorizons.Builder.ShipLog int maxAmount = bodies.Count; ShipLogAstroObject[][] navMatrix = new ShipLogAstroObject[maxAmount][]; for (int i = 0; i < maxAmount; i++) - { + { navMatrix[i] = new ShipLogAstroObject[maxAmount]; } @@ -387,6 +390,7 @@ namespace NewHorizons.Builder.ShipLog int newY = parent.y; int newLevel = parent.level + 1; MapModeObject lastSibling = parent; + foreach (NewHorizonsBody body in searchList.Where(b => b.Config.Orbit.PrimaryBody == parent.mainBody.Config.Name)) { if (body.Config.Orbit.PrimaryBody == parent.mainBody.Config.Name) @@ -439,7 +443,7 @@ namespace NewHorizons.Builder.ShipLog transform.localRotation = Quaternion.identity; transform.localScale = node.level % 2 == 0 ? new Vector3(node.astroObject.transform.localScale.x / 5f, Mathf.Abs(fromPosition.y - toPosition.y) / 100f, 1) : new Vector3(Mathf.Abs(fromPosition.x - toPosition.x) / 100f, node.astroObject.transform.localScale.y / 5f, 1); Image linkImage = newLink.AddComponent(); - linkImage.color = new Color(0.28f, 0.28f, 0.5f, 0.28f); + linkImage.color = new Color(0.28f, 0.28f, 0.5f, 0.12f); ShipLogModule.ShipLogDetailInfo linkDetailInfo = new ShipLogModule.ShipLogDetailInfo() { @@ -456,7 +460,7 @@ namespace NewHorizons.Builder.ShipLog private static void MakeNode(ref MapModeObject node, GameObject parent, Material greyScaleMaterial, int layer) { - const float padding = 250f; + const float padding = 50f; Vector2 position = Vector2.zero; if (node.lastSibling != null) { @@ -464,6 +468,9 @@ namespace NewHorizons.Builder.ShipLog Vector3 lastPosition = lastAstroObject.transform.localPosition; position = lastPosition; float extraDistance = (node.mainBody.Config.ShipLog?.mapMode?.offset ?? 0f) * 100; + if (node.x == 1) position.x += (int)padding; + if (node.y == 1) position.y += (int)padding; + if (node.level % 2 == 0) { position.y += padding * (node.y - node.lastSibling.y) + extraDistance; @@ -487,5 +494,52 @@ namespace NewHorizons.Builder.ShipLog MakeDetails(node.mainBody, newNodeGO.transform, greyScaleMaterial); } #endregion + + private static Texture2D AutoGenerateMapModePicture(NewHorizonsBody body) + { + Texture2D texture; + + if(body.Config.Star != null) texture = Main.Instance.ModHelper.Assets.GetTexture("AssetBundle/DefaultMapModeStar.png"); + else if(body.Config.Atmosphere != null) texture = Main.Instance.ModHelper.Assets.GetTexture("AssetBundle/DefaultMapModNoAtmo.png"); + else texture = Main.Instance.ModHelper.Assets.GetTexture("AssetBundle/DefaultMapModePlanet.png"); + + var color = GetDominantPlanetColor(body); + var darkColor = new Color(color.r / 3f, color.g / 3f, color.b / 3f); + + texture = ImageUtilities.LerpGreyscaleImage(texture, color, darkColor); + + return texture; + } + + private static Color GetDominantPlanetColor(NewHorizonsBody body) + { + var starColor = body.Config?.Star?.Tint; + if (starColor != null) return starColor.ToColor(); + + var atmoColor = body.Config.Atmosphere?.AtmosphereTint; + if (body.Config.Atmosphere?.Cloud != null) return atmoColor.ToColor(); + + if (body.Config?.HeightMap?.TextureMap != null) + { + try + { + var texture = body.Mod.Assets.GetTexture(body.Config.HeightMap.TextureMap); + var landColor = ImageUtilities.GetAverageColor(texture); + if (landColor != null) return landColor; + } + catch (Exception) { } + } + + var waterColor = body.Config.Water?.Tint; + if (waterColor != null) return waterColor.ToColor(); + + var lavaColor = body.Config.Lava?.Tint; + if (lavaColor != null) return lavaColor.ToColor(); + + var sandColor = body.Config.Sand?.Tint; + if (sandColor != null) return sandColor.ToColor(); + + return Color.white; + } } } diff --git a/NewHorizons/Utility/ImageUtilities.cs b/NewHorizons/Utility/ImageUtilities.cs index b4d4d751..d66f7469 100644 --- a/NewHorizons/Utility/ImageUtilities.cs +++ b/NewHorizons/Utility/ImageUtilities.cs @@ -118,5 +118,23 @@ namespace NewHorizons.Utility tex.Apply(); return tex; } + + public static Color GetAverageColor(Texture2D src) + { + var pixels = src.GetPixels32(); + var r = 0f; + var g = 0f; + var b = 0f; + var length = pixels.Length; + for(int i = 0; i < pixels.Length; i++) + { + var color = pixels[i]; + r += (float)color.r / length; + g += (float)color.g / length; + b += (float)color.b / length; + } + + return new Color(r / 255, g / 255, b / 255); + } } } From 66dae913b68999cb756354cfb2132e26fda10656 Mon Sep 17 00:00:00 2001 From: Ben C Date: Mon, 21 Feb 2022 22:53:35 -0500 Subject: [PATCH 47/50] Changed Focal Points Made focal points follow more vanilla-like behaviour --- NewHorizons/Builder/ShipLog/MapModeBuilder.cs | 129 ++++++++++-------- 1 file changed, 74 insertions(+), 55 deletions(-) diff --git a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs index 6b9164ff..4a351553 100644 --- a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs @@ -16,6 +16,7 @@ namespace NewHorizons.Builder.ShipLog public static class MapModeBuilder { #region General + public static ShipLogAstroObject[][] ConstructMapMode(string systemName, GameObject transformParent, ShipLogAstroObject[][] currentNav, int layer) { Material greyScaleMaterial = GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/TimberHearth/Sprite").GetComponent().material; @@ -41,7 +42,7 @@ namespace NewHorizons.Builder.ShipLog } } - if(flagManualPositionUsed) + if (flagManualPositionUsed) { if (flagAutoPositionUsed && flagManualPositionUsed) Logger.LogWarning("Can't mix manual and automatic layout of ship log map mode, defaulting to manual"); @@ -83,6 +84,7 @@ namespace NewHorizons.Builder.ShipLog Vector2 pivot = new Vector2(newTexture.width / 2, newTexture.height / 2); newImage.sprite = Sprite.Create(newTexture, rect, pivot); } + return newImageGO; } @@ -104,9 +106,9 @@ namespace NewHorizons.Builder.ShipLog private static ShipLogAstroObject AddShipLogAstroObject(GameObject gameObject, NewHorizonsBody body, Material greyScaleMaterial, int layer) { const float unviewedIconOffset = 15; - + GameObject unviewedReference = GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/TimberHearth/UnviewedIcon"); - + ShipLogAstroObject astroObject = gameObject.AddComponent(); astroObject._id = ShipLogHandler.GetAstroObjectId(body); @@ -121,6 +123,7 @@ namespace NewHorizons.Builder.ShipLog revealedImage.color = Color.white; astroObject._image = revealedImage; } + astroObject._outlineObj = CreateImage(gameObject, body.Mod.Assets, outlinePath, body.Config.Name + " Outline", layer); astroObject._unviewedObj = Object.Instantiate(unviewedReference, gameObject.transform, false); @@ -130,9 +133,11 @@ namespace NewHorizons.Builder.ShipLog astroObject._unviewedObj.transform.localPosition = new Vector3(imageRect.width / 2 + unviewedIconOffset, imageRect.height / 2 + unviewedIconOffset, 0); return astroObject; } + #endregion - + # region Details + private static void MakeDetail(ShipLogModule.ShipLogDetailInfo info, Transform parent, IModAssets assets, Material greyScaleMaterial) { GameObject detailGameObject = new GameObject("Detail"); @@ -140,9 +145,9 @@ namespace NewHorizons.Builder.ShipLog detailGameObject.SetActive(false); RectTransform detailTransform = detailGameObject.AddComponent(); - detailTransform.localPosition = (Vector2)(info.position ?? new MVector2(0, 0)); + detailTransform.localPosition = (Vector2) (info.position ?? new MVector2(0, 0)); detailTransform.localRotation = Quaternion.Euler(0f, 0f, info.rotation); - detailTransform.localScale = (Vector2)(info.scale ?? new MVector2(0, 0)); + detailTransform.localScale = (Vector2) (info.scale ?? new MVector2(0, 0)); string revealedPath = info.revealedSprite ?? "DEFAULT"; string outlinePath = info.outlineSprite ?? revealedPath; @@ -172,12 +177,15 @@ namespace NewHorizons.Builder.ShipLog { MakeDetail(detailInfo, detailsTransform, body.Mod.Assets, greyScaleMaterial); } + detailsParent.SetActive(true); } } + #endregion #region Manual Map Mode + private static ShipLogAstroObject[][] ConstructMapModeManual(List bodies, GameObject transformParent, Material greyScaleMaterial, ShipLogAstroObject[][] currentNav, int layer) { int maxAmount = bodies.Count + 20; @@ -191,18 +199,17 @@ namespace NewHorizons.Builder.ShipLog if (Main.Instance.CurrentStarSystem == "SolarSystem") { - for (int y = 0; y < currentNav.Length; y++) { for (int x = 0; x < currentNav[y].Length; x++) { navMatrix[y][x] = currentNav[y][x]; - astroIdToNavIndex.Add(currentNav[y][x].GetID(), new [] {y, x}); + astroIdToNavIndex.Add(currentNav[y][x].GetID(), new[] {y, x}); } - } + } } - foreach(NewHorizonsBody body in bodies) + foreach (NewHorizonsBody body in bodies) { if (body.Config.ShipLog?.mapMode?.manualNavigationPosition == null) continue; @@ -215,6 +222,7 @@ namespace NewHorizons.Builder.ShipLog if (astroName == AstroObject.Name.RingWorld) name = "InvisiblePlanet"; else if (astroName != AstroObject.Name.CustomString) name = astroName.ToString(); } + // Should probably also just fix the IsVanilla method var isVanilla = ShipLogHandler.IsVanillaBody(body); @@ -226,7 +234,7 @@ namespace NewHorizons.Builder.ShipLog ShipLogAstroObject newAstroObject = AddShipLogAstroObject(newMapModeGO, body, greyScaleMaterial, layer); MakeDetails(body, newMapModeGO.transform, greyScaleMaterial); Vector2 navigationPosition = body.Config.ShipLog?.mapMode?.manualNavigationPosition; - navMatrix[(int)navigationPosition.y][(int)navigationPosition.x] = newAstroObject; + navMatrix[(int) navigationPosition.y][(int) navigationPosition.x] = newAstroObject; } else if (Main.Instance.CurrentStarSystem == "SolarSystem") { @@ -247,19 +255,22 @@ namespace NewHorizons.Builder.ShipLog { GameObject.Find(ShipLogHandler.PAN_ROOT_PATH + "/" + "SandFunnel").SetActive(false); } + gameObject.SetActive(false); } else { if (body.Config.ShipLog?.mapMode?.manualPosition != null) { - gameObject.transform.localPosition = (Vector2)body.Config.ShipLog.mapMode.manualPosition; + gameObject.transform.localPosition = (Vector2) body.Config.ShipLog.mapMode.manualPosition; } + if (body.Config.ShipLog?.mapMode?.manualNavigationPosition != null) { Vector2 navigationPosition = body.Config.ShipLog?.mapMode?.manualNavigationPosition; - navMatrix[(int)navigationPosition.y][(int)navigationPosition.x] = gameObject.GetComponent(); + navMatrix[(int) navigationPosition.y][(int) navigationPosition.x] = gameObject.GetComponent(); } + if (body.Config.ShipLog?.mapMode?.scale != null) { gameObject.transform.localScale = Vector3.one * body.Config.ShipLog.mapMode.scale; @@ -276,9 +287,11 @@ namespace NewHorizons.Builder.ShipLog return navMatrix; } + #endregion - + #region Automatic Map Mode + private class MapModeObject { public int x; @@ -291,18 +304,20 @@ namespace NewHorizons.Builder.ShipLog public List children; public MapModeObject parent; public MapModeObject lastSibling; + public void Increment_width() { branch_width++; parent?.Increment_width(); } + public void Increment_height() { branch_height++; parent?.Increment_height(); } } - + private static ShipLogAstroObject[][] ConstructMapModeAuto(List bodies, GameObject transformParent, Material greyScaleMaterial, int layer) { MapModeObject rootObject = ConstructPrimaryNode(bodies); @@ -324,6 +339,7 @@ namespace NewHorizons.Builder.ShipLog { navMatrix[index] = navMatrix[index].Where(a => a != null).ToArray(); } + return navMatrix; } @@ -333,6 +349,7 @@ namespace NewHorizons.Builder.ShipLog { navMatrix[root.y][root.x] = root.astroObject; } + foreach (MapModeObject child in root.children) { CreateNavigationMatrix(child, ref navMatrix); @@ -349,7 +366,7 @@ namespace NewHorizons.Builder.ShipLog parentNode.children[i] = child; } } - + private static MapModeObject ConstructPrimaryNode(List bodies) { foreach (NewHorizonsBody body in bodies.Where(b => b.Config.Base.CenterOfSolarSystem)) @@ -365,53 +382,60 @@ namespace NewHorizons.Builder.ShipLog newNode.children = ConstructChildrenNodes(newNode, bodies); return newNode; } + Logger.LogError("Couldn't find center of system!"); return new MapModeObject(); } - private static List ConstructChildrenNodes(MapModeObject parent, List searchList) + private static List ConstructChildrenNodes(MapModeObject parent, List searchList, string secondaryName = "") { List children = new List(); int newX = parent.x; int newY = parent.y; int newLevel = parent.level + 1; MapModeObject lastSibling = parent; - foreach (NewHorizonsBody body in searchList.Where(b => b.Config.Orbit.PrimaryBody == parent.mainBody.Config.Name)) + foreach (NewHorizonsBody body in searchList.Where(b => b.Config.Orbit.PrimaryBody == parent.mainBody.Config.Name || b.Config.Name == secondaryName)) { - if (body.Config.Orbit.PrimaryBody == parent.mainBody.Config.Name) + bool even = newLevel % 2 == 0; + newX = even ? newX : newX + 1; + newY = even ? newY + 1 : newY; + MapModeObject newNode = new MapModeObject() { - bool even = newLevel % 2 == 0; - newX = even ? newX : newX + 1; - newY = even ? newY + 1 : newY; - MapModeObject newNode = new MapModeObject() - { - mainBody = body, - level = newLevel, - x = newX, - y = newY, - parent = parent, - lastSibling = lastSibling - }; - newNode.children = ConstructChildrenNodes(newNode, searchList); - if (even) - { - newY += newNode.branch_height; - parent.Increment_height(); - newY += 1; - } - else - { - newX += newNode.branch_width; - parent.Increment_width(); - newX += 1; - } - lastSibling = newNode; - children.Add(newNode); + mainBody = body, + level = newLevel, + x = newX, + y = newY, + parent = parent, + lastSibling = lastSibling + }; + string newSecondaryName = ""; + if (body.Config.FocalPoint != null) + { + newNode.mainBody = Main.BodyDict[Main.Instance.CurrentStarSystem].Find(b => b.Config.Name == body.Config.FocalPoint.Primary); + newSecondaryName = Main.BodyDict[Main.Instance.CurrentStarSystem].Find(b => b.Config.Name == body.Config.FocalPoint.Secondary).Config.Name; } + + newNode.children = ConstructChildrenNodes(newNode, searchList, newSecondaryName); + if (even) + { + newY += newNode.branch_height; + parent.Increment_height(); + newY += 1; + } + else + { + newX += newNode.branch_width; + parent.Increment_width(); + newX += 1; + } + + lastSibling = newNode; + children.Add(newNode); } + return children; } - + private static void ConnectNodeToLastSibling(MapModeObject node, Material greyScaleMaterial) { Vector2 fromPosition = node.astroObject.transform.localPosition; @@ -462,19 +486,14 @@ namespace NewHorizons.Builder.ShipLog position.x += padding * (node.x - node.lastSibling.x) + extraDistance; } } + GameObject newNodeGO = CreateMapModeGameObject(node.mainBody, parent, layer, position); ShipLogAstroObject astroObject = AddShipLogAstroObject(newNodeGO, node.mainBody, greyScaleMaterial, layer); - if (node.mainBody.Config.FocalPoint != null) - { - astroObject._imageObj.GetComponent().enabled = false; - astroObject._outlineObj.GetComponent().enabled = false; - astroObject._unviewedObj.GetComponent().enabled = false; - astroObject.transform.localScale = node.lastSibling.astroObject.transform.localScale; - } node.astroObject = astroObject; if (node.lastSibling != null) ConnectNodeToLastSibling(node, greyScaleMaterial); MakeDetails(node.mainBody, newNodeGO.transform, greyScaleMaterial); } + #endregion } -} +} \ No newline at end of file From b61e67b0757f39aceeadcd40c62bd9be8862ff84 Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Mon, 21 Feb 2022 23:04:20 -0500 Subject: [PATCH 48/50] i forgor --- .../AssetBundle/DefaultMapModNoAtmo.png | Bin 0 -> 70474 bytes .../AssetBundle/DefaultMapModePlanet.png | Bin 0 -> 66448 bytes NewHorizons/AssetBundle/DefaultMapModeStar.png | Bin 0 -> 129877 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 NewHorizons/AssetBundle/DefaultMapModNoAtmo.png create mode 100644 NewHorizons/AssetBundle/DefaultMapModePlanet.png create mode 100644 NewHorizons/AssetBundle/DefaultMapModeStar.png diff --git a/NewHorizons/AssetBundle/DefaultMapModNoAtmo.png b/NewHorizons/AssetBundle/DefaultMapModNoAtmo.png new file mode 100644 index 0000000000000000000000000000000000000000..9a5438bf934c284eb4261630b9f89307652501b9 GIT binary patch literal 70474 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

tMnVIJk50?^nVSSbo(*f`@}Bw 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 OCg^R&djX4jJir6 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%0DF2Y3Q zFFHAlz*>#fZO(MCW#VY+R9@pN#OU7q4Yln1xJwnR={QSvTE!A7ZKW)}E%! z&Gf<6lpNqZVOSI%QYOj8*Vw2Ir|q|_Mq;4bZ`i%}2}YfWnrfwwrqz@dIq~WHY6#Fn zT~5drtXNuo=}8c?OcsI4t0m=!iXHwe8x5*7XYjrIFHWRra$M^N(_y?JYXCv^GNOU@ z*Yu5#Zzdwo7(_l%KA0<54R%X@*$dgxzAi|P=)X@9@8Ze_tBcyws9-?##t@J$kK&HB z>dMY`lyc zT&O!bE^7jaszKYn(gh^%bQhAiP4}C)1Mk9v`w+N^x7BNd*Cl(_+6yX6oAh>0s|G8? zx__DU@V9oV5Tx4G18?zb+Up$B>TV9iG$KEc<9Ckm7L>uoJu5ax;-lhcJ3)EWl`><# z!{r?1(KjZ@t1ICMMdR~ajyrXS!zfqFIhY{nrKj4C1aYlaPdEK)Z#Nb%TVA`ZuN}H8(|Mv%;^PT^H*r^@xB6pHxKQ ziHbtqf5$pMGY%k>ra|+nK^7hv_#mZ;BNv1xE4H_@&PUE0h7wo22m3P`AS}D;f;HL@yI%uEV zi;?URUwLyy;@jct3DxR~2G<)g9tx#dj5Y+R>k@5+-af#eS_UU6HQx3;gr zJQk(Ak4i0R0~2I!iWQPrw_O|nhV5jIeGJjJbiLQJ>`i|YBRQ84+b=>mFNhGwQSQu6 zitedgZuOQpi^-ZGbW%WrKEt3rYkKRMlz$ggVoD)b6kxD8_`GK7#t(>*jmXb%=b9mR zRSEQJq0Lgw^oJ;aDs0hbhr2yV_0pKN@wi~NrMa}a=A(Utq!TpiXz$z$YzkXu?dUZO zL2)wS2!k-WVQc`wfX*yg7|pvo25Wf_7|dV&9)t4p6%~2@o;z7hJV@V&H`G1vC(0Ej z;v(u(Ad8g5h;7G>pwg~#fAU8ub(DZXPVbsnfLv9ANa9*{Q9{_YY}uohx1gfVLC_wL zUmqofYs!i-Il#^FGD|S~tGdL|>RbXpp$;}T)W`1F*SJv_&V};7kv*pWmt1EF4h6GH zPG^vy4h+`_bKJ#9>_NxSH*onQc6Q;r1|T^mB$}{MTxpSe#2>ZU0#6ypYJm%~G918a zaFF^ZUVMAX+H8j!$m*jYbp*s_`-i`ly8Kb<9xh>Ai|s)rd8n1YY<$v{)E3D>9dY%E zb&3h46Exg8)ZCKPj{u}9r#f)6cds+R^q0wUWAgNfDnd%Y!AUy%?Q;c~= zaZiypoSXZDpF}pmsB51Cvu?$e%Dqq8!kLtT7RTEstzhCuu{(>SMmXSbBX4Q}A?&K; z#JiNVs9FRAgrQTh-sRv+8wqR=#`)=uC;2C*xHa)aeqx-z?`E~Ze#H!E-WeE*^Sp+3 zNous~)gOESkVsRkrY=jQW9W$2|>Od68woG%a4_VlfZbw4uc(;sPTQT=J7{vOep1D!=aCljss)>pXD-=VMaYEGlpnZ8y7 zBnyc%;EZmH6Tac1H{tS5f;A22nTYyG^r4Xs*M+FM@rtDDl0NKBwM^i)pw~JlUvbNw zU4p#rT86BoI2R!u>}{oF49S9}7yS{QwAh|M0w@ibV}Kp&#_F%qLMKA6-cz31o8jV7q?^n%yU()HV?Sas6Udh&Jq z@;ht5*6X}1*Ixt*_rJ|0uHQlUc~D+98ahIcj?;9fsW!EoD(1i%z&Bg#e6R^t)3dEM zH{ylt0b#LI{-q(X+tM{#)+gm|F-YP2ao?wx$q<_pX_Z zEDuB7TDJ1<^SgSVS-d0F5_;Breh#B(-Y0WY1CD{)wcm5RFKC038&^o9z6o?W#>~Sc`unk^G~qf1~joFbczUgodC`Qu$)<`p=L%oc9b#&4hgyD(o;YNN9E9 zHUt-jGf6leqOvmh_-I#Wt30!eb+MXv+7LIiHxTpaY4an_)3(%d>hgHc;!nn!iuO=D zVLO>#_FBfk)9aA$Q~bPa$z^1Fq6?i9CVpc)qx(F?ga%0|JC2mXOSM^gmroy?HcaV+ zJ6q@ioTQxhR=;lu7q3^PB^0TpBJF(D3W{4{`sK=PJx_kWSYc{0}iHA z>|K)4Xi8!mHKKOp2y-_lIJvG4*7zf_e-)Bv#!-aXJJ5G_HrJDNp7q zJMBVS6_fWDrE8yDtQua~zG|T(+79v!CEPo&Q^gQV;UKe9=jT7jVdxG0w5{&BPrK&H zOh#u8=n6CedQMH$XfI;`dt(I93awv2{2?%O2Utsw^DQ)s0U-}+t_%^kTYzN0)SmU+ ztYg6C7tHEt@8KAwnM)e_?|{s+S0&ar5ctLF$}|V&x_8<^Mx$&uo>ggOk$zRR{|P4Y z7Cd&+PxRyqc6CPELUnHtivJ_>iEt@z8?1QDW!bLYPhdMrA});+CQb_K(DcbsKIZwR4ht8gn>4Ha*%C`MpKwLSXmE z;XQ3(E=BTVC~J~3MAX`!s~M*_vC*yaMr~M|Y^JUM_A1t$S%v((KZ@QrP2>)OKNf)s z9EH1>52W3C?J{U?wsbDN2#0Es0t>}E0!s5ffo|PBv*aC!&?);UFXaU5{w!mn@2r_X zv45P!&Q>IZhloW#LF{d}q9hp@^pQB#gJW(YH&>588izTpit|+7MGD^+%vU|ga@t&~ zT^Lu{8_w2L!V(=R&5$SBrTh*A>~%Gmf)BG(q`+404Cur8@=~Ah-jc@pMnrR8_Z7bi z*JVR*=H)|kkO>fmv)n1KC#Vc6IY2w18fnc+-j)pzg@roVnl&!~bsS{}eArRME|L4U zu(Ljl9-U4&I7Jw(@XBCou^t)O13noSU#W35YTk&l- z#Ozur<*tv#^ZbV9{t1^aY93e)oJ&7Hb|KeEkXO59WvqPy8AMB^kN-5cdxig;3;#_< zFTvms$mY`Navy>0GZnh}(0t=7C$H&|fu}&q!LIEND}D{WM+R z5~ed6r4SXPR2uJ!YFs&7mH6rSPdfu-eQ;gQv|SKXEuv?x7mLA!IJKp27-bJ4BE~uk zI{yLh1Jdb-AlDd*3=<$o+>dQ@vw2*bgdW?m;NvNI;SXb(X);y4Az)0$%LiF^FL+t; zhc;bLm#9{n(=J zGEwmPB2T<(9SA*+f-(2X!nD6C`Cq$C|ERA8Hzc+zN8!YeNAL$B08tC*h(Z(XvFW0O z6Xod&kPsRNy}~ox3MEVD=!38+pIxOk6*kf+MB?${(s!L_DZQ3aQ_ZWbVbyJ^{r`4% z)RGmIrkpO1+IWN?zBt)v@dE4=f1pa(4>nqeYHvQbaF$kg9Ek}-eV|`{c)qAvNbcH= zIsCZ`rXw%1&Ms2;d`D_+huas!6C{g?l<#nbJ@rv>Q!}-g9;2a8=TE^bf*eW;3?@#kq@;^SkOxtCHwj z^UJl2uq@@-?-vGDvdv4Q8yWo+*ECGzAW-j~oL`oMp*OX?xLs#we=4BOUeI<4+7Wk^ zcY(DdFO-DwYybVB=aGW680cRZ(Y@xhT6!=M|40*2^$N2=@69gE!e00DV(K88-j2`O zt(ZEPIR1Y^dLX+;cDzd5mf{uM266#%A}d>Es50d>J;pkX1YE=!Amgu$y?y@zKPTuz zSVK-p?r3j!ig5rYr7-Qpu8Hx(=w6>m?s=hS*c51TU)U=DApeS8wze*pV<-h(gExV3 zLk)7bes*SWQvuqii|X8gWvCo~=QJZXMfgu6mV<1JOYy}M9t48~vc3Gm&E^JvQe6DD z#+SJb0_a~Ohx=IhWUF9IT%0DK>Hod`cfB2iwMG(OhM1)q)qDl0edyXELm*qu{jrpN z2T+Lx{Y#Ig0RzZUNRYtT$&2Zi4%h+A!;z01YKKd_;zzz?nO(bfJd7Z*7iDF+%AaT% zUczTD;to>#;ITtX*vKY?Ocev-<9V(TwhW=n`*ArZ?tYPL`)L1`DW`h!+RCHG2KRrs zDH4p`z(f{)hnsM4tKz`uvnS+=iCv8~AXOr=|6=Es9~OO zB7Q42OpuhnPQ41ToUJY(=}_?FcAo;w0=2kFL)ATpf&XhJA&FA~@MsfYzj68Y6tBhV zY(jn}|BrE2bw=c(TX8^oH@820QzUjf2e zX9EQuDW=z4MI@^qOK3v{PHAfg;-8=zla3^flwp1xnmDG(rcQ2kJ(pUM#Jhlt?@Qwb zqLV^&(>z0MSRFrm<(*KyWQRX>uctOd|tWPfwHO9J83 z;=s!uk3%m18nbgDYUg`yh0}UWe0DL-v!u%TXn&MHJXw=}P+Dz_LBho48T#~=j!G+O zcMhExYjWiV=zVp`Cy=zF*sQ*wJ;7{HD)&Hi)v#&9*9_#xIqTV;&go8O)=v1SYfV`n zD92T})ZJ;y_hy!1fgd7=8!+_`-mLXMq?hHL&bw5TA(Z|&9=Nj5c1WKGwU-|_sp&{W ze901GZz$JdjCu0IZYX8+X?f`R9j%CZTZz3q(^%!2Bg~Vszyt{!K{Da!> z2kNc4o$7OhFZJBBLp>YQPNlhtt!qeZ>l*5vZU(Qr689s&Z!jI?uDDO`iGKW!3Q-TbIjK#|1 z{FdpZ?x)n|RQs7IkCyk4EtV#caaUi!R>)qV>iWf~gOPW~>}-yhM^l7W_eWMdS9+XM zQQL)*P@sqVQCvg7MrPd^Im)RkH}GzsqTM@=8ZP%Tju|MlP(7bME_jnwxH5@H)miZncd4V`>PX+oe-ka{!`og;Id`3{n)}Gh7L$ zzlI*24W6N&zEXt2E`#+Kv|(ZU!r+Ak0iVn$eWNrHMBt6XE-_xpWulf%HIn*Ms)JgF z(2jw{e+Znoa(w;t%fzc@3uvD706RSX6!Pf$Vu{o4XVaS}YQ}Kc&HVy5hir5&dpPy# z(8X|QbY6X&9oZ~$-G6y=pSn$4{9LNyGvm;DORDUQ9#EuRM7ack892!xxyX^!tXz9p4tW* zx!jhfsf_}*V~1?(4ybBzeYF>xzK{-bA@lPpcI|2~k-sz^)y4%CS2oPg5WIL~4a(L9EhTiEH(!b_JN*sUwt#nJ8QxS;|39(IvLFn0Sul*rte)OS{^!#3_zTnD< z-1)lB&U5jAVoBum#mH})V{eoeij?DjoWRyH1;n8(bozevKTE21cC`3GdX| zjoy=2w%N$uZ}b;OgpSZv<)t)bS1a3R=g{p8XL~>y!o2KJ46KD#-m8*7ahQ+3YENpb zNcU9P{}a_Hl$=FL&)RV^U1^+7Qj2(+s9IkXxh064fj!$2_NRX1kd~3szn?xzY2JgobLl9{NMe5md_`aK z+&Fb1_cdau(5$X;PfN2!{%}F!$ZzWx6X%Ql>cR+>cTcP@W4@&G$(HvyA%duO=^;u_5 zKOT>ka3gJ+QqNbq2qlQiN7oyZajkZvZCkq`ZRdn%YV=tlK3R*j{$=7(_k8jq)yoUnO zP-p3&ACYP5wZ~c(wYy({)Na^xJ{lVnpGp_%6;Z8+by*Lz0z7^z8_AsuECIHn@9c*Z3yi^ zP>-?V<9qw0A4-H|Mq0=MPRtXf^vtFykLbYuf^Oj~qrlch&Qw`7Hk*fXnwrRL)>Hnp zYFZ&g)svN?1fLhe{?Q0vilgR{+-j$gWeXn-6^j{cZ(oVgxDP}50aQHZ>+iT}YoRw)mNsTee!-fze zTkYuk5UowDuNdpn&vB0$Zw8MxSX0#VQqHDo;$CqSp!7p=XglG(|HbPYO;12wy(8zY zl%@-uw6Q`L*fl{X%%j-jCpQ6ortlPw-W*CB{zhSaOM3t1Z3m21gRRB{_{I3mePgNL zxg!iXWADXpF^3N5_<==>!dAk{G1il8!cU@xFC5!94RTCflWDcKgXhX2yGBey)7AS5 zbi0wy#=ylemf?!|IrMz+VnzHM#IQ|kAGy1q<3n z^rKiT@H5m?ARg)&*N3{$>JcPX*IYejhLOC38^qZICD1RSZf21gtrD+~_UAl?!p{0x zR}u0#m+_j$`zjacqV>eXU>BOJ0MftGn6y8NSkv$J-<3M5BRTn(YKAeg$n}of9Y{KRHvZyuedI^Cff?6TZE~2883J!s*g9>s&Z~mls4=+ z;y-aSg?dYPW5hJJ71#l*0MnM>$WTGk9I-oQa&ni57!`}SMkeV3JP-p~aZH*6nf(r> z*pLIwT7UqD!~4JXOc~nyNDs#Dk+Z>{S0;|S{^k+glf}m3t=P}Gh{na!sIGl^z4^At zA9kW4*CFIldTY%cH=Fmt8TrMCpI#$`8Cb)KR>Ha}#~HmVDy|+H9qC_&f4mDca`hml zX-|Xd)j8(lf=CBl4d{hHmhmfIb_CPtGLpB8-*^w0!gXO*6!uF|R)W^5?8M%UZI;Sb zQNMb=SmT4P5CR8~XxbYZC7VjFJ4LwK|15V zZ{6E|TE`7b-@7U@_P^4o4Cs9&3;fz>FeY1~Dg>s~vK8Tzl~?G4EK|%9 zDWm!2`P`H4Lo{VZD)a14w}{x7Rv{uKuw52gwt$BAKSegsogN>knaM3iA!GS-o-@J% z3TzXrFBFZ7lzyv7+KjIWSpyj$*i)_Z*WxH|%ixTMJAzMRAY#D{WkHt0zW!4^a;m=B zoE%V3YmDL?7~Rc}KnXT4#UGMv!r*gV&q;HdFBdSRh-U{>$1_J_b{@v;nS0c{?ljUk zQbQXuFY7~}UH85)*wW+5SFv^+u!PG!U;e(=Mm9daa>-1vFR@*;!S!?TySl_^YTEv? zt~n3HxkLHB_p|nU3Tp84A_3e7H32)GK9MT2YEbsLa$i$Fh%UnPSm1FaPbZ`CC+#$e zCJFWlCMjMDvkcPBBnI?{?F-Y*kxvRqKAcWA7L#D2tVpi4ap-!SUU(O%Up&^X8b`J? z{sn%FSlYfuF9F-v7!U3R$@+tEH=Vkid?Pb9GHK3r=TUX0!SPq|6>f+T7vCx9UDtQc0RhDAA4|}o4$hA zRAnUQP&L0iB@P^VQLuEvz3nx5H9fF2B7dwBGjf1W%@*3@3m<@Mnl9M*mQ?Wy^3i3w zD1%h6?!UzL)<1QK6r@^2lHLoKJaR=ee4JKmpAN47E*8_YG|dgrr^_BivM=TV63w%z;?8%F2fslhJ3d7N`? z#h{W-wdej?qlF6CLe+8ix#n?I_N)7CocE^dqG;MuXjY}LKV5`Bu`bOV@ZeL4*5*NA zaf9t#H8r*x`5;x8c2h3))V#5n-0Z}$wcT~Yxy^qz2rFXuMbCm9OSq`K9wW@gr9B#G_28KA#O`yxCy zjxzlZ;RmXK2)4=sw4l;c4C*t5VTjl~#o-dnVNElsw!5Vg8+B+zF;?B2?HBdiYH|YW z5@B;`;;(vA3r>GrVV zY6Taha#-C=H0MRNlaXe)vW5-wO~^0mxWlCdzw0_;TEC=e3xynHvagdz8q&xRQub>5;~k}`0{7Tw_%5# zaK4gVqBwFd+`pqGAGoaf_MYr;ce)UdJ%bv1<7?c2Xj6t>=W@?U%Zo6t?hyDt-~wWr zk~aDSDg1}p_RyvFCbY}e<>z0hhPSd-7K2eE60UO~%KXw60;qjXVM!gzl>|^2Y|^~O zJyLQBgFCD#nY*`GQ1;Y(MzKU8R&HoW!8%?YWipf6o_UHrBrHrDMzG^4?OwJ;hU>$PrsJq^j`bkf5mi^zLXQUrzo3_JGq0 zKRnLJufb)d$Kx0SX*3@;18l}m53gT?M6GCdWWo$xlE?JbVTA#mq6#4hQ6Ba6=_V}D zP1jj#11K~Px{W?Fl(LdhN7Tnch!I2KSUl#!#Zp&GU}5Aiqr@v@Sus;do0TqsR7;(c z&ak5LB5!3HD;$4=V3|oWY!X#&s(r;f&UeohRB$-h*roAqc(HKE zWn^Gd717i#>=ovQb;4#^YI1>ka(+u#>jJuEuL!OYdN0oPe{t}$bw zs+L2ZY8Hn13+TZpp`N|I5$%tE1glf+d#L|ziIkD6`zA~>(>#8u6kxC2S+9CH^vO5r z#v)Um#A_r!KRiB_ztX{bdU@5_KxOGT*A0(!cZpx|f*RWbXA^2YzooZm^MVgVK%Y74AcbC7mnf-@LM6g*J+&b3(mHovB%-)754J^2+^p^lpdd7lsu%gkXO^tm zlqVpRqbMfq8Bbpv*LA?OcYX(-33sHt2z;6P`YhbTZK>~3#feUz8(94Pvw5f95Hjjm z5b`g=&Xu^mEXKRPuZ; zGMWYN_T%s;UynG)r#~~oZ%>(SlVi>lOSB(AmXTQe3KP0)+HyPON^+bh`a)KNCMdb? zK7SAX@>EWlun}=;8l?5=M=NQY$S>qkKg(g~oQFj`5-)sNbuhi|HMWzruJ1w;8UV_gN>b^0HKqICU^#o}wo0?>cxDa!!~ z0Iei`lGRj8H#T}&0YXn&;M}g{}k?guxi(W zJ_+w~?O`apj+aqZ@0$MXnGNr0RF>S}8MpYq?ZAF}Kg_>*@dq2o;3D+6Wv3hIu?_IM za`?iBV3a-n{vx`SvqSarDWTtELFI5R?`%b?K^yg=AOa`p<&aV(*$oW2wxq9YFnRo3 zP#;v$pRry|)P(VG@n!oUfiEAPO24Gq=Ct^Phh*~C()dBN2&2E`6Y}ev`@^1@uHe}1 z7O0p|EZV&85?yB|{`Vfif$XTDC7yDD70QXh)42WrBbx*){g|RJ780ZmYI)t|z#4=> z2Fc_L&xA>FH#X9?v2?-$lNz0p{RdyqV$QOt27=xVG^+mKuSAoV9GUBv&g5;`D08ZC z_+hz*?VV$hTdN#mcczJe$Gy=;lIv$`t|BDg@P1nU<1i{}eY~eKbb@6krNV4dhYgXj zw@4~m4U>Ed_)5x1Zpx~LL$ zTWPE7JN`4jqz4l3B8gz0PXg{^>jLaCyj`VZGZQqv$)^KSyt2T|9Ox!#=PG}M5kBZK z^4q*YG!D4Se@=bHI7_NSM}1j^S%->m)WITXy=Esl+~AB_CyNIlu!}x0|GS8hg~&IH z^*SW!6B=qD-ns|rUmeCu&Vn@-ZT^gQ*mqsM=?ieITI%#FmG1gQEpPy~&L8}`PA)yu zB^*9y)sN5LJi&TyC3ay5>-|?`|GoY^rbf2txYDy6P+Xd;|K?g`nS29gh?C};+iB^; z>xY*`n{cUV1ARG-eW)&pK)4;BVFAsdzZoIvcPB^@k=T1L#d-#;)oitSwRd zSLpV36^$@U*=RkSMsyWlDWke>|gkEktdlkR@!ue7J`+WNN zxrRpeKC3`9_y2Kp?eR?S|39hLslzzsxKsI_RL%*x%ysJ~rwh8Gu(?z&b8Rh`nURX* zQl~m$G?CEQI0$1ym@eDgHw~MpZ8nUyxopO7{r=d$du;pc{d&J%&)aJ`w+;4$6MFlq z%+5XRuh`s4KD$BHR7jM=DZ3-tDonoL=zgM7hm~DG+KAYi#=)vp;$L(wo|cqi(u6CvbcjOX=2LIs@Or;W<)aIY9*n*W3X6NXT)UtZ?yrKJue^!5 zaZz!`ogFVqH5-Nr#mGbt#@6D5*n7M=(qN((pTAOqANUY~R7zJ|xFZOhHGpLmoxx&~$j6TMCmq*T*=CG4atRM!l!xmwFcv zsv-s4NYvWAkq5#yWb8+PDgc1n0r{;h`m)7MGfr9}pVNYBUGy9)nYZ~?pGgraNA`kk z0PJpIj3RmW8%?0hCfMz62kDoyqB>2%jxaznou$~E%~L-$uWj#+#yfjfjwK7A>G187 zWtz0baB;B79+uP&Qc8pm8VPu{fZN2gSh4PGR=Z@7rLB^xKGP08JiBt_xT3#TaYI8j z1l}Taa8uM^TiZMlT}H2x_A-sELS_RRzH%u?4a=7MR;X70^d_9*{lCk&-S6&3vpIKJ zIx~bz90UO$W5o~X#YQ-*_W9S)dn%CHSXBTPGFl#{5!dh2N$=C|UckvqTL&u64|(Km zDqCc0qn`(BRAD+bWm`21QoeUqG74Rv@qWZwnB1Q8fVt6b6d&}@ND7A*fXP5!(6`tt ztogP(_fGgbJN)}xqqeF2y}^HPtIqok-2BVkiuH`Thc{#Q3IU0*iKg7l6hDFLl8*2* z*M-|#@>%WjmKN4NhR;6wD-~yi4sOq0f=x6s-wKF*J7(K735uk9F^&x4Pj+E?koyY# z)NwrqZ=br{LqbXpfQwW40uzm{zR=H`V^=tZnVH=3YH&F29I0?@oA!C{mr@Z z4qC+|9E|$mln-J&DS|d3LJBNYE|2kor^~7j!dFs zg4S;#Kx%8Wu@zGk5jDMFZCVH2MOwd$yVG67jM5CDhyLo=P|!E}aMhparMRpsd8-bt z$DsRLfQ-&-@+4sBU!56i+25>LMzW_ZQ0a{tWPx9XcAZMi|0|+D&#qe(bZ=F4>K=Ja zPzF}VHYxcZQhX-6VJRvy1(K|KYT3&iG}hS>d^JV>|4*&2Pf5gJ|Mg(1pBTMII5^Rf znNm6*bl%$CIuC%89K5eO{V6tm5yk11DYV8J3?VeK`YgILpS@`!z2A;!6b6nh@L8y% zkZrDUU(zd5%^JAuSIT&gi6p#H&WWT6{wy>PAvxE4m`Ef7&1fk!=N>yMbmt&RexN@3 z2Cv2Arb!e*cXs%tlGWrSqG05dg1Ds)hjR1?hW$oxX#i?^YR1m-eKumAB<%lkZX1%OJ@zoY#mr-9zwTE;-sWwmyDWe8{6Z z`bbwNa+>$`27y;(3v^Ifo|5rhPsVMw)%9hyq&DjVg$y;^*!3d_hbeqCt+C+a(uUT#*s>9`4e56bn0#4q|M-0Ch)v4|?%0d^M_?;@cD?DPfl? zI|ce=de`?n)Q@i;#VXB&8brb0V5fXsxs1*+yyWuhn*;}|rILQCfVxz2L3;Ijkg*h- z-=+TRL0-72O`iRnkQc2XyJ&hHic@t9+~+Y0Hvnpj(&_Ek#@iR{>b}b1UZkQHJl%rw zQ-gbu1keM>_SCT2r3{dF?^BreW3-px&#>F~@q9F+zU{km|!9#9W^O6;bP9 z%KAkYTZZxWtlOLmQ@9q}4!Su~+qXnMGhEf+o*iY1O3mC8vUk z@hN^U97|y${BqC%W<<1GYXetE#;InnQerUdOo8%m$)OZONr65cc8AQ! zXue8AR)Vr`{tjNj6aBILWa9>*sg1$Op83 zZ}ooj3gkrcQ1j~BD3dK>iekRWw9AyNngd8JuN-WX9GZ`%(Z@W9UTAHVi_<&*DIVKu zf3KyHomcBgoO~7h=le8b4idF+{yG@p1j5PazkG}GN7%fbwo%ysZiPh2qlWbf=Z>|;Ss0S+%a=V3C;5P_ zLhMk!TtC!<$~lX!z1pX;!HIua)8_Fya-&@)>))@_qBQsLt1U*8HL`<@G4RB{=DL!z z`wQvnydbcmNz;0Vt!YyGr5Tjj+Ej%XZhieJC2>KWy2MlF_XL2h*- z(k~Q`Qgx$@`gc%Lt_U(~MB7K>6#&W0r%RHz`)WY2jEBhC#OFiJEf4fU|E!LbJ2jcg zr^)@|_f|1<>Mw|>Y#E4Xce9$(ll-@+%9-e?kuPgFHa!pu=6732QZ6%Ohw;cTSSC_$ zr(L2~1}xsdk{WkMjQK5Lr|QJx7aw^|lM%@>RqS{olbKpj=c|6@2 zTUW2}xQ^M>oIYf^p=1t8e;Z$UMpLs|v|LX!^il(@yJ#g@35Fa5r1c>}O`HXq_xlvK zb(opb=`a_-0=!K}C@v&vG4%3NT)P8wQDKlGm5&=qr)k1|m8HLx4J@V59| zuU;7DS5S%{Q{&)TCvN18-|0AtQ{{dm0$($ds(*IgbWo3YJTFxnQ4n}$Fqr#@po%dc17C6puVTV#`Fe$JQ~X|eMg8yD&<`?8EX>k`_>+}*=-vl=wDVshn27##DQV&X=d z$rh}2<4pRzs7fO(@o-SnzbbFW6c=2z%!qri@%$-Ce|$Vqb>2}+vv5zL>bx9U|7N`g z{3&nAk(NQ^WtJdI?8AW*&Dh<0bL$NKggymW)DFjq1(m7xf=l3QySIJ*Q+6LCJaagY zmP*>;!kThIfa#V@zYCDpu2xw|MD3ZP{DZvWeIuRz>|(QGufj2Q`Jxrdg5lRoLib7S zUI=a4of;LKi##K1W*c)((d5*ye?2GYWP@37w(C0p5>>SN^qS*W>%6 z&!2i{3+n2`&Sa^L7h;EXy4aD?ii;hzd=Fo7n+M%hs!iTW`5NA~rCs`%NWS<2%L~D+ zODj}8Op_r!M?ghhdh7fmJhZ58@S4D6+xq50zzGmq5*9k#zNmDwKcy^ax)YU3*DG%^ zK(X51vCZ+K_n1-`y5<%1>X%Th_IvWe)TKPBiInsx<-F;SCY2V`mf1+{kREHZ9MTm6RYq_tk~i*sn@q1l!vH_u zMl>(g%%xC|@YV`Au#|FWwi2ZFd)r1pQ{rDnxXcZ5$jUqX7rEueDrCK`U^Lt4U8Do( zjguK5^>+I2(<>2aUD%eEzVFvBr~1Y+!m8th9YqGi>S#UJhLwNs%n7Lxp+$kZ%5J!n z(;OWTo+3<}rv1W!gkcz+j%6(hgYaIiOs*nRIPte<*AHxhp{(2$N6ek9LN%m|N!g$m zh!82u2jWibMcFDpPTQ&3cjQ9M>TE2{-kWTSM=9HP39oIcCE9lab zwQQq3JW(5``r#lePPV`hgc{;CD>9~EFOcAYTbd-BXV`L@37@O~SX()=)}aEVFjN&F zwV$&gjDr4{{bIA8{M}6@?OPja7iDZjNK%(tIP~n=L}i3tC)LUD+G2lr5#S#Z0+blV ztG|QTnQ}Kz&y2a7>7*OEv>D&5kIzHHMccb&N!O`&Nfd&a4MoDc*nroarhQd@Z}d}^ zmge`NMhb%&&xeUEv@%YcMHNuor60Pkv+^NZJL$W;g0@^zN3Z|SZ0X?e4c$J{PA%fAf!C0lgMJj?QfQ|h}H6iSnq~3N3BS3wGq9CgkE|zDi_F`0D*+toFZmm zkr>b1n+q&c9*rPwr^rI-NE!8ZfSM%#Gsoy>N=>7fV}3(!L8xjXE`g@2Up0d!@HIK- zOeuG77Nyh-6gGV&EoLuGCi5HVSH30b5sKRRqfN zb9~H3I$$f{SHs<-3DHiog3*Z3>l*$iidq>C(~ilnmQ1__)R~ZTicXeE!!&<1IlFrk zNs9%pF&nKOy5N$!unY0ES75i<%rUEuD&YF|Aa7ZHn$7W*1cP*AA#ZZ?)r~QdSIZ@h z4=C~jsdSfY`CaAs1F>3&!Do1Po82Mu{%&^l>iU9fP_}4P+PQ7C`q5$foRR4*b;{oe z7L3<63aAs5GX7=|teg6-xGJ@kB`E@i%#B7bLm2DG=R z>l0vcZ)!Ad&aYr`X(}jhUZybCM~$#r^gyiH9+LDjjv9tl{<6e_bArX0MUUXsl7j^O zu1Y#Gi3m0=0-uv%k9%@`gxM*ZBBFXmA1)7QzwLX;Hc$B*8BRQ-xZ+>m#0zOSEdGlN ztEc$(u`@+HOL=OGFmKfKc)P4!19f=^YwtIs@s&kBTB1U$An_ML05Rr8t}$z&2`==G_UCff~AzF}9^&zu<)ENRnwuTy~8$t0#HoEejA(2$S5e*aA}s-tuK2xeX+h&BiPHI$(y|BSRiwe)NBXv5($jvQoU#)CbUXG_&gnOeRcNXrdTM7^~{jl?q9Qs}~ z!FlhtvA-jZxw{dXqa#JBOmX$ z_zJvP#dhBK=})kZh{?87NiYa0^5bYd6!)5`wYOroQtSLO_$Vvq5>BSY|7N9W;8O(Z z&5rq1?r5#KsMhD>hhAuUuzG`V5brRzeN~Zmyw%a1+O#}q#4EBMN8MW{z}%1s_^w&DP!aT(4XAdPzOI znD>(E-bat`;sv)U=3Cc_(>9hzW+Nnv=U>)d7|#?$+Rh0g_eVuuR=#qoZ2hYLrET=% zz<8iX_c*&|?&h772JR`WoafYzVX`UDCIj5Ro@N(0+124dDLtaQo~^S$>hNWLs8TUh zLluiOL%zVttZv1Yy4I*pvfpB=JmimG?w|}~y9ScBt7KezM`wL-On^wguQE1HqW-nWqrMC4s-f;3i+`tXT{_ayKoA)r1K$(nWlu=Crsk!|QA z=i~6km-}+A#AG{_0*^;2;g*dSDt+z2YD4pyycMT|OWSMG^``Aay_H@to22uhb4FCQ z3~#JT z(|8l+PICM*-jCd3TCl_@m*10{@a9gZnM?^yigqAaXBEd5X8da(*-3Sx2!1H$b&uhk zt+#FwT`ILr9*?$V$Q6&nT=XDf$9*yYU#5m2z82k`M~;~~Wi*gVW2Rq`orEDY+6|Ks zGE60=zf0j??`gP%Sb{Ay@rJdn+$EJnX5nY}5CJBBBq?|lcLRX32ry@`nut9#Gs(4{ zmbzPqSw*<7r5i>Ifg8G=0HX9$B={OsHnw53JMwbut{_B6Qf8_laDefkh|;Kb2Xy$( zsfbkjdtiT7u1E2@3VMVhJGU`!{*Rg9JEA33g=^6opNtLor{;$FWZ4JP!s&POmSet$ z02?J=JJ{W~rAa}&8^h57#45B9kh2$H7Rb8kdTaBY>9swVW?vKvD6o4gOQCHfyBd+e zpf2+c;ulwBlw6;ymz@r6B$^?GrW3aqU6-&I>C2Xf9vF+-dKv3Itw}s?wXQ!i@jF#e-^?tRUzC2;(p1?5AA!2 zo_Y=4yuQ+BhtGgu%bL}CAG8FCw)w=Xld6haHEqQBm2=UeG&{|$!0dvv?Q)A8ga+g_ z%SMTgP26J#5ID0&ZY*8qjYEe!le4U=5qtKj{3JkHl7MPaEV^_Mp*cMGLXjHF)e`T7 zuN3c+T()G6aNxx(k$`-0*30-B0L_ewK95VJXJBA?x%jnU&2m>KhVLx>3vzDGeX&r_ zCqDz}w!y%1#Xo2UlXk@?xbn7$KmTNq4I#giV{&aaE}6>)RH2A%fQ`L<<;{|qi>+zn zC!?#QhoimwaVNrLfg!e0ioc4dgbYuJV$Yx?F;ThCpia5@SeDh+7Y277%o4ovtI@b9B9QEn0hO526 zL7C2+6^pzrC};VLvHy>DSjMCE(38L?SmK~6c^R|s$3#5$d*Ax{IQ|RD@XplDTTff| zK6!{WECiD4B(LtB!*pm;?Y0@wAzSsTSQ&l@?XjdR2NuyZ>CN`_xyI57lUe+6W5l(l z)lf~o-18@rLhMQQxGoo|^I~uei=he{@QH8B23UX-gr8Phz&d0mq^L;B8UV0SR6Km|76O z(Q8p@AM{u9*i2qoz`dUl#UG!_!xA?ve*Epo`L$j4ngaq+=#A{%R-p54-tbI#?snwp zJrkbm$|L)&?O)2^l0C7zmBY?Ud*YrkMD_orBefL8u0)=Vr*Sp^Yy8;WQ6n46%`#TT z6C&-2{!V6`CQq9$7jj*UE86uc4(F)x=q&iL23AEr{?2sc*Rq5@?J$2uL^i^@-3r(E zHaxtl=4laHk4ty zD)KY-?e!rmef#o-a($RKI&;yfaObrl*})Ri)e%Gf-^M)&5SN4Uee_4MeOenkeNOs` zo^aL2fMJ5<16IV8PuU@dN77o>jb-&OmU5$sw1D`Ar9<(k;ObOlIpTJY(T>qFQb{~? zLnnY$L9!c*SIXr{eTQ805Kt7!pYghY=+XYh(;)d=TdSmZYLYklO_ZQg*ags}b zOV2kPt%0P#GR&!-7QkqOS&pl@$@`k#A_qkkWZ35oT~4+3q3z7M6h92;4<#_tns*R& zYv++i-u5|IRiwBakncSiZh|;ioE}mKMa?PjWZmfwyC~kmtC{qt+J$~`^V|7JxTUPh zv0c6_1LiJPp~?iXo8_&?y|8G99NtF9OQF?5yXBKu(eR?{EAliTXyo&xvnu|2VtK<` zs$8txi0@Eg6Vlu*pRtIvM8+?^UUkrc2hzr`U00MovNjkw zKFc;kzWWsw@T%?zVueyDYcml)vhr!(&uxeLO!H9hSm+Lnc5;*CJ^_oX%{phqdRJQQ!AjdDP|= z`xvo00*%E`MQ{^-hv2JUc+SCUoBzOyNJu~QC}fL2Zl9!(bTe=1qeFppIXYF6Xz9>F zj{z$#Cc|EVbgg;e#G@wMN5(pe=nVn1VpYXBm%l5e8%ZlYV7-IL*l5$@^ySymLf_DN z^zALPav^_QFG^=Ne)Rw${!dAvlY91}9d=dg0`AUNyGgVbMQIy7U$Bfb$;;lk;ny$1 z#njlp&*&+Nz6NYIp|0E>@*JOZ!zNZhv99^;r9g+lN5HgI$*o{oSLNTxe1vEKW}Sz2 zBxa(Iqujl%M~=bnL!Ys|!TtU`f(Pl1RIVoiFeQ7#CeA4H72s}428rmu^vbhY00FqXRAX_Uqa(*%^i9nnbG96dpS*>6-q$e|*$iOruzz5< zt)G9haEf{)&VKOQ&oq`xmY#D@5+>wrI!V=+F5lu@vWAXHt1MY!*Q5QeE5};*jf6s(Z$Hm|d1=JIb|V;~iY;i^ zN00Nt33=!DBckN=afVv)%eI@mPc(J*8@GNBvBcHAJ*re?4oD>+)>`;y@j*MU$auU&cu_H)uoX9w zb|KrH^h7&O3}S8Xa~((h4YTPjWxn#J|#R^dn0zrpA$UnA25i z3O^%mbqZ6Tep5ZwjdZNgoy}Y&LyFVi=}6G8hdrxFv}C4(&*y~uYtKjEtfato#D915 z5q4P9_JfU2F@%Dq)fP<*mC#HLdr94E!|hH8skwi-2|AZeO;@J3bQ>u7m;lJ+m>PtC zhP+*Ob>G6?|JL_P&8Ai*{0-vMB;x-Yq!GVbZfw#5R^?&VrYpr=bn}pqwr=`;*w)sm zv@3?2*&=lt(?O2_KR&_l1U4~9{_xKIapuWXlg_mq-QWfrdrFzp!m&<0@%=_={Y!*c z_ipZlQQ(H;@yQYjZ-7}ih}@HxNT-jh+#s|ZoEa;|W4d@(MJh`pn?VM!bs}v-#Q{A? z_exJ7V=f%pioTn;c=ZCT!$+WfA>#^;d1qLYXLHK=983&Dr?%Cop)Fq-W+cSWr^7UB zE%}48EL(oms=sKv9N7SanXQqz<|%@nN4l8V*IbKUn@KLDDlblrp@8H(D>QEv-q( z=JlIGBsz+lm-!|>{wI!NsMNbl2obF(<=F^&lcaxVLh;}2KWwN5yz6vrOo#!#y>988 zM`W=grGc0=IhI(}o;ZM_-(dHB-^!x-EjAV{B0OE;OLjAvM^QG>Y;QrRXH_(eom~~3 z%ntzkkQx-5$riy^U}Q#c&1ra7%ZiZLAIB{@>3l)%@k~*;u8QA0z5@ON(|? zxglKMo<(yS)QHH-d6F3BfLq)S#4`W8qm21HqKB~O==C#wPd20{g}lj3d^7&;&ELp9 z7~<8ho0eXEZu+}UZ4s~7GuM>x7tih5qqvc?X!-?fkZVIz7TBUe1mHyj*_*h~RyaXD zj>&R&wpuQ{sd-&~jy49?QwEMKD;x`1BzAVI1xGOrf8BgXQ}m1pAfEnOs24B1UOjCqc$tGH&zBn6-z7RF4r4Flg6fA~qsd zXmVblgQjg|3Jk(RUt6|2U60H=8r{pQJlt)YB6s#_T26G8=|mxNoGc2KTi{1?!!7Ee z<*tW!bL;W`=n3yc{)t|faOw@O&idVs#LKEz95m=2jYC?4X%EhFX*cs396aNMgHvd0l3T%#}qHjabxsp>+7& zFADxY+$a1ySu)k_wqerIS+?ey+Pkhcd2vI1GAAaq?yJh8`^818L*x6lTYt)4$Vt&D zRFmdZoVDxq$5YAA>l5qfE6h`COxO3+NQLwq@+&J!3AB_A>6`D5-I zL4g#N12a3-3w|XjVEb|-+=Av@(5Owo0>ryFeyI={lfgHbWgv;gkm-P~T2#N@9JLsm zzWSJBnOr@6pt!FncPTEq!Q}4zxO}j22H!rl9(0`m3_h$#7v8P8BcFqhvkL6$EaBB# z^hAEMHvT^_VM_gq#S3*Qzq$VQQ`H!%;+%f`%u2NBUHCEdC-#Ad(_&z1i*>_Ur8FS& zrN~Y>p3L5{SX0Ir&4J>U{2}h}EOaV(ILcChhdRYiBvVDxZObRo@|RtdY-|Yp74CCI zu}GE@mvBaME{L9#N^b$zzRk%&nrsoOnr<~)cXXZT^WS2*6B9O}ZJP8_R$yU8+(ok- zg~K&N^gfO#{wI`>$SN=bp1-p#PjW3ggLeTEA3(R3wW#D20r_TRD06PtK{mFGQe!rx zE3JWv)opc2lH4}jO2!dLy>G(4KP$Z2G97$`-lH>+sj4k92hgR$fb^T8D`w`{K9>!{tV*IP(fqb6C;u_1_ zS9}Gng{oNr3l*O)m@1CFM*4NNl*OHiLQFWCO}UDcm+o<-F(zAuKz2?Qk;tk}G^{5` z*bUtGf3&MF97t?XxhL1!k1C}@D?dEiOR=S3zWo@jYg~h=er}?1teY*kXjoQB)Z8K~ zfV&}%E3xjhk00dzShOI-XnD(5yk-xoTorTo+8*i7^xL^u`|OJHATLc=Zgd3t9S*i8BR}+aa5``K3dSj zXfG@ctE zkR17!jKqN+hw^v&(@E0<+Q8j>9V_$TyiCVdC@wri%CU)`O+p`1PELy6SSBd~Ic>t8 z7VAtF&2>732FRU455(tgUknCjxtax9cH`4st1Jc+Sx=ZD(Jys!5a3>_P0YNIngBU; zv7Lbd5rEU(Ydy)&3(gvFp&~y___!2LAz>(%Mt&?+fqavvH&F_{cMWv zq{wr+?5j0q(+YJcfxBS2iEy>8v+q{4jdgr=f)?_!eC}ox^(tVrNQdPOHrbwer=zc} z#8^tqap7;FsZ*{0qa?-dUBXMR_1GbtSfLF1E2)XM_ad7ONzjmVoosjhD$}oj+hcHR z9()`9$=?|SKom5&rrs%#9FJa1+O)wLV=_aR{>Qr=wItrILhxGjbohMg8O76?^k*}o z%*S5XW%XJ*r`X0oowr#j#6uyztLKUpBE|2K*~woj|3Q=)XkqjmMoChT2}ksP0&il6lK6OFKA2BBaO{%$}V$1dr|UOSMh zC=mEuCnrsg5~M8}>rCsz-P<_42y)f*PL73@ZbGqz_77We)>XiwdZFw?Q1pD=@|U$w z{-NEB?E-%OYCar0TA|#_2;v=WI5E9Lgs5q0)`S?*;YBUt;3e1xxaU3OS|{t2HLz?Q zYD4}~FUghPt&1gCqO)x>_StA=U%XM1QqQFTinUjLg zfNk=R^7V~65Or(p!QdO;kZ5Ybn4`!f!RCQBL{m*JdNsK>dx9Rx2tF)1G*`AcTNX7t zTNem7nHhHwFH7$;L&y|i4{DM;08~S$@- zJ8nIIhzr{VdTBT}-ex~N+|!j-*NAt<(ULC_`_BS*)V>+K7XKv&gj-dnPK_2y2YmN} zYQY7p^QO0G$oq1ktm;FHC^{t&3R7%Lu5KjF6I9ncrW`!{RM?+P9oau;`c=s#CeWRy z@5!$EkyK1z6XM1dSb?iFNm!vLPO@IIzq)!FnGYUnA{LXanpkXkk{X=zf@3-@9l>2_ zr8Qn`Pba!E1|BIf%HH~~!Hp)H^M0szbe5-#NvKU5jwceY?RSc5ro)w0=8>BnEa(##O5G=r;A_bd|vddu3?0pCZdQ@`!p^`Ow z%Mdg>!)!C*N(5}C9;7%G2vsjz3ZGehPhYGZ?(lNV z$-CPtSG6^kKV&`|{7F#YghMJvqwm85L7#nNyxGB0n6e`3kCn6*+L%mkHfW+ZG2Ymx zcbn)Y9q6lTkyboF>haYpMf@!SL&ql^^;?T30>#lzU8A0s zl)&hMB=j7vP|%T#3>aLa|3T*`DQ`mPAbx2L(VYvBHRaYaS+5=Cd({`czW%rEn-D3b zq&iyg+@*F)?-cuJ54TD#H+bDNQKHEBxKG!i2$a2hxXG#Hl?~$^%MRm-xheNt-ePvF z_(C@M3*A!6y$I32fpXtduXP%%S6iSD*n0Z4PXriu!XTji`EY`nQNGE&zk%mNoOPoD z@W8l>&ZNfSsxPEV+3C>FTTFi>=F|gNA^_Qv?b`%W=8W{l1=l>MIpO_>pAdXflY3gq zT$|$EBD#7-H?C`Db7CS4xeie)tI*40)ULK|Az61=!+z>-TX_P5mf0?OrT`DQ9e~#9 z%dDxRS4`;5r^#u2dBZ_EKnS%2^R3=(rry3@vpk4?2rdT?K^@B8m#=@cSCOnS&R#EF zR#~1xv;+pu37s*-(J#+Dh(|Ym`S|tCiJP91J+9}sZ14os*==rbiP!&o+xTg~NuE}R zPsy<+l%@2@5_32<-6=IJ1&}V@UUNg0BfG@wCpJD|+Mj|+4x|#3s(`i?k6TqPj*kh- zIj<*jZS$3tqwc);dK$d?917)_q0ApcIrNY_eAVEzYt=)ogWRl8`gpmc9tzXgNaV%t zX0h?I02`|87ZGzCVkRa_9c5gIvoaGZDQBu;<&0NcSNcm$prgqgoq`C8y$81FZ8wIZyPq7M2Z%P8qmX%Y9{&=y~!Vzh_^F zKUrPg5ur&vqh#YrV)te1kY!uS(yPBEdzxZDnyR@R`dNd&Gcal$xK3$*!PK^(lYzOZ z#1hosVIZz>Pn<`8ok-;4ENij^wL)~=YM0gS?-uR-7P|X~Ju?xwOQId>HODHN^U?mX z=ZI)u&Fc4H4-wsoHijjk&$8UOLPpIE4bejirz{b7tsSwo+Ip)LUWD|Iuf~Dd+PChU z%+?`#6^IL{41=X#*^e2E;n7G0D8sj6aM}DW{n`|}+kh|B&&Y3DHGWtgkK_yEPosOT z4cdh~<~WyARLjFcJ=rMb&uEVMhboXTW%Xl49OV@^!%z*<0STn#dsq5lE&IMcGn)&Ee@(Iy!|e`BRE<=eS0R@KDl4!jSZ{CAz zHD=N8b!-@pgB04dS?Z|;f6q>gUfjWQL+g0FKpn`C7p0ovOh*Bvv)8mah^2^YY z^P;}zXn~_}$f5S8>91$tpOOW@1-OvtzJiKyk4C}p0V~U#NP>vlnj(De}w-R ze_ehSPP_1?-E2xUHoi3F`Y%sAq@zffd#R+Qu&j(^ecX$HZ}QqhUA`V`5=Ea_ZCt@qYi0EyTY&{dv|t z?ypl)YXGf{*=orx+YqUwV2a~^H|KM|0oJ_t3E8ff^+ueRwdT`kPEvLoSfP#xQb%2f zVq`i53-ZknS_mv~TzVnKll6>uVfmW(KKg*8U7*}6JDul41C_G|Mb>%pPd=!gq!JD* zC6{MsLZg$)#APWCq6}M!{*Zqg=L~#Cy%BVi9qMuy*8qsk%|Fqw%+}K(Rt4@t9HReK z6LdIAhgp@^K;29!A?<##Mx26KVcU6uYIwXxa{b4!lW=ech(T`RxSQ`B@Cjw)t++)Eds2Z*J8Q8d{VlO8AL zpS+g8ZW6A%R@znsN;qZ=eIG|-dO0Ti91+e-eICX0PQV4?tEXf)y4{W2SUckC-wh(~ zIf`E$VbITlpIOS>rr><*lugaUP4NUwbWg>Y4!iaAaO>5_EsMJary>sLQ*vf*-jK+i zuF$R;+Qw`(@X2q2y0u#8@@_wq>$B=wQdg*z)8;GEbtY4l_=p=^vtm6W6Re__JOY@r z^nsK>W+bG?B`~E2mD#pKKEfSmR^_88-laHJd3)`Cc?1^j22m{+IOG!BmM7mmC!gd3 zujblB_Z-5+DT{E+n-|wx@)``CQ9VqD`>T$$X&F+JcR*HmcyrC?f)MeIBCNuB({ji9 zrdW)Bd(ea`yvrB_FuU7s`2Fx37>GGON#H)9bbB|LG5uwgM6-kdsJUS11b&j`l)3EY zY`sIJ9j(h&Bu0U123c}fBm<^6*v2r=u2id!ol^@Ad=cRuA&r>pwJGqOKq3Sev z;YIJo5*RUxdUx?d8%Xepc0xQTrwS_=nSwH{F_hZUC4=-#3i(AAO)nX3EWg%}alBed zBmijQq^)nyM$v1kBL`EAQGb!aCm}te+tm~P_rdRbQUH^I8=4({wDOcK3s))@NcN17 zH7nbds;v-S)l-FH3sw(u4^AYv;2o-`fJdJv(rq&0sBek2P>D|=wxw*U&GpL*r^oJf zmgTiP@(lU7FeZwhy>vZ_i^+7Ic{B`aS3!vahf3J;AoEEnp}&HTRJ`BNyYKzb4+hM|9!t!9C#1zgTr>^Gw-Tw|w^&V?p0Lh>Yi05y26It|T0snf~7 zSyL0Z?y?I_8wX3grcas%k}1qaAS-)j)Mdm+^{ki6rrv>TG7=z4f;OuPH-$PiQ&@>> z)tt3?Q_DeBkH0VG&$#z2X0QBZF00`Wpf@$|iC-Vg!p506MH|`U0M&*w;}7md|BpF* z3^o^R0z`CoOhboEgqcYe#tPIR^ zLMI%oeh(tcuC*@>wm2R`g;YHc&{qlpoTpQ@8wq@>JedCj>@akD`vfPGSCYB@g>q^-0G~iBdm2U(?7w)9^zb_Edz8rk=%JRRqdG z0?R$Xx%r{%pDtDgKArPwBFnc(x(OXtJHipEvGhJxXfBP`mG(7g=S9DSn&0X5=`2I{Z?*us5-niXC$t1{S*z%*eXi^Lt-HHYxhl zxaul;SsADF!&6rK24vB@W6P}M{kM1N@QwNV{Z<_aYaJ=0v~qCG(7M)?!#U?9tJ>W5 z_p*_jn#nmA^81o58vDER0KmOCKr{NC`xK++o%!^zMPr{qDZw&D8FzB)0_-80jW^qj zH&JW~`IBnzW1Uf#Jvhoa(T%(ve25vCAGAX_2u0)r^2qr-Y-=(iF5#&~ z$AhCg;E_k|tFiA4|680)mUoQDx~BT8yZe6MmMkPPNXjE@cg4Zc`$j|V*gI6_tzoLb z^&|@%r88F=9pGkYYqGd*O_;ZY;~m1+A{q`SU{HC*JJ&e~H!klk^NAb;1X~mc+W+y% z8SviF$HU^tj@=0aA761}63j5e^Vj#Hh?l ^MGa0r@boacpPV5!9dbD-C@~9c_s^ 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 literal 0 HcmV?d00001 diff --git a/NewHorizons/AssetBundle/DefaultMapModeStar.png b/NewHorizons/AssetBundle/DefaultMapModeStar.png new file mode 100644 index 0000000000000000000000000000000000000000..7bc0ac1b87dad1de4dce9f98b0aa2a3e56a633d8 GIT binary patch literal 129877 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 z

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 F{U0j~O1J<3 literal 0 HcmV?d00001 From 2bcdc2e1ecb740012f379be3cf48dc93d27edaaa Mon Sep 17 00:00:00 2001 From: Ben C Date: Mon, 21 Feb 2022 23:05:17 -0500 Subject: [PATCH 49/50] Changed Focal Point --- NewHorizons/Builder/ShipLog/MapModeBuilder.cs | 67 ++++++++++--------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs index 7d31418e..42abf778 100644 --- a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs @@ -383,46 +383,51 @@ namespace NewHorizons.Builder.ShipLog return new MapModeObject(); } - private static List ConstructChildrenNodes(MapModeObject parent, List searchList) + private static List ConstructChildrenNodes(MapModeObject parent, List searchList, string secondaryName = "") { List children = new List(); int newX = parent.x; int newY = parent.y; int newLevel = parent.level + 1; MapModeObject lastSibling = parent; - - foreach (NewHorizonsBody body in searchList.Where(b => b.Config.Orbit.PrimaryBody == parent.mainBody.Config.Name)) + + foreach (NewHorizonsBody body in searchList.Where(b => b.Config.Orbit.PrimaryBody == parent.mainBody.Config.Name || b.Config.Name == secondaryName)) { - if (body.Config.Orbit.PrimaryBody == parent.mainBody.Config.Name) + bool even = newLevel % 2 == 0; + newX = even ? newX : newX + 1; + newY = even ? newY + 1 : newY; + MapModeObject newNode = new MapModeObject() { - bool even = newLevel % 2 == 0; - newX = even ? newX : newX + 1; - newY = even ? newY + 1 : newY; - MapModeObject newNode = new MapModeObject() - { - mainBody = body, - level = newLevel, - x = newX, - y = newY, - parent = parent, - lastSibling = lastSibling - }; - newNode.children = ConstructChildrenNodes(newNode, searchList); - if (even) - { - newY += newNode.branch_height; - parent.Increment_height(); - newY += 1; - } - else - { - newX += newNode.branch_width; - parent.Increment_width(); - newX += 1; - } - lastSibling = newNode; - children.Add(newNode); + mainBody = body, + level = newLevel, + x = newX, + y = newY, + parent = parent, + lastSibling = lastSibling + }; + string newSecondaryName = ""; + if (body.Config.FocalPoint != null) + { + newNode.mainBody = Main.BodyDict[Main.Instance.CurrentStarSystem].Find(b => b.Config.Name == body.Config.FocalPoint.Primary); + newSecondaryName = Main.BodyDict[Main.Instance.CurrentStarSystem].Find(b => b.Config.Name == body.Config.FocalPoint.Secondary).Config.Name; } + + newNode.children = ConstructChildrenNodes(newNode, searchList, newSecondaryName); + if (even) + { + newY += newNode.branch_height; + parent.Increment_height(); + newY += 1; + } + else + { + newX += newNode.branch_width; + parent.Increment_width(); + newX += 1; + } + + lastSibling = newNode; + children.Add(newNode); } return children; } From f60d2f1eb470ee8dff20a60c9b37086b96b1d737 Mon Sep 17 00:00:00 2001 From: "Nick J. Connors" Date: Mon, 21 Feb 2022 23:42:31 -0500 Subject: [PATCH 50/50] Fix spacing --- NewHorizons/Builder/ShipLog/MapModeBuilder.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs index 42abf778..1b8fba48 100644 --- a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs @@ -408,8 +408,8 @@ namespace NewHorizons.Builder.ShipLog string newSecondaryName = ""; if (body.Config.FocalPoint != null) { - newNode.mainBody = Main.BodyDict[Main.Instance.CurrentStarSystem].Find(b => b.Config.Name == body.Config.FocalPoint.Primary); - newSecondaryName = Main.BodyDict[Main.Instance.CurrentStarSystem].Find(b => b.Config.Name == body.Config.FocalPoint.Secondary).Config.Name; + newNode.mainBody = searchList.Find(b => b.Config.Name == body.Config.FocalPoint.Primary); + newSecondaryName = searchList.Find(b => b.Config.Name == body.Config.FocalPoint.Secondary).Config.Name; } newNode.children = ConstructChildrenNodes(newNode, searchList, newSecondaryName); @@ -473,8 +473,15 @@ namespace NewHorizons.Builder.ShipLog Vector3 lastPosition = lastAstroObject.transform.localPosition; position = lastPosition; float extraDistance = (node.mainBody.Config.ShipLog?.mapMode?.offset ?? 0f) * 100; - if (node.x == 1) position.x += (int)padding; - if (node.y == 1) position.y += (int)padding; + + if(node.parent != null) + { + var branchDistance = node.parent.children.IndexOf(node); + var goingUp = node.parent.level % 2 != 0; + + if(goingUp && branchDistance == 0) position.y += (int)padding; + if(!goingUp && branchDistance == 0) position.x += (int)padding; + } if (node.level % 2 == 0) {