From 7011b6ec3a31a79f7653a2a2edcae283d0ebc596 Mon Sep 17 00:00:00 2001 From: FreezeDriedMangoes Date: Fri, 13 May 2022 19:08:27 -0400 Subject: [PATCH] feat: added full config file updating, added menu items for this as well --- NewHorizons/Utility/DebugMenu.cs | 130 +++++++++++++++++- NewHorizons/Utility/DebugPropPlacer.cs | 176 ++++++++++++++++++------- 2 files changed, 253 insertions(+), 53 deletions(-) diff --git a/NewHorizons/Utility/DebugMenu.cs b/NewHorizons/Utility/DebugMenu.cs index 28adc4ca..57f41317 100644 --- a/NewHorizons/Utility/DebugMenu.cs +++ b/NewHorizons/Utility/DebugMenu.cs @@ -1,4 +1,6 @@ -using System; +using NewHorizons.External.Configs; +using OWML.Common; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -22,9 +24,16 @@ namespace NewHorizons.Utility // menu params private Vector2 recentPropsScrollPosition = Vector2.zero; private HashSet favoriteProps = new HashSet(); - private char separatorCharacter = '☧'; // since no chars are illegal in game object names, I picked one that's extremely unlikely to be used to be a separator + public static readonly char separatorCharacter = '☧'; // since no chars are illegal in game object names, I picked one that's extremely unlikely to be used to be a separator private string favoritePropsPlayerPrefKey = "FavoriteProps"; + //private string workingModName = ""; + private IModBehaviour loadedMod = null; + private Dictionary loadedConfigFiles = new Dictionary(); + private bool saveButtonUnlocked = false; + private bool propsHaveBeenLoaded = false; + private Vector2 recentModListScrollPosition = Vector2.zero; + private void Awake() { _dpp = this.GetRequiredComponent(); @@ -113,6 +122,89 @@ namespace NewHorizons.Utility } GUILayout.EndScrollView(); + GUILayout.Space(5); + + // continue working on existing mod + + GUILayout.Label("Name of your mod"); + if (loadedMod == null) + { + recentModListScrollPosition = GUILayout.BeginScrollView(recentModListScrollPosition, GUILayout.Width(EditorMenuSize.x), GUILayout.Height(100)); + + foreach (var mod in Main.MountedAddons) + { + if (GUILayout.Button(mod.ModHelper.Manifest.UniqueName)) + { + loadedMod = mod; + } + } + + GUILayout.EndScrollView(); + } + else + { + GUILayout.Label(loadedMod.ModHelper.Manifest.UniqueName); + } + // workingModName = GUILayout.TextField(workingModName); + + + + GUI.enabled = !propsHaveBeenLoaded && loadedMod != null; + if (GUILayout.Button("Load Detail Props from Configs", GUILayout.ExpandWidth(false))) + { + propsHaveBeenLoaded = true; + var folder = loadedMod.ModHelper.Manifest.ModFolderPath; + + if (System.IO.Directory.Exists(folder + "planets")) + { + foreach (var file in System.IO.Directory.GetFiles(folder + @"planets\", "*.json", System.IO.SearchOption.AllDirectories)) + { + Logger.Log("READING FROM CONFIG @ " + file); + var relativeDirectory = file.Replace(folder, ""); + var bodyConfig = loadedMod.ModHelper.Storage.Load(relativeDirectory); + loadedConfigFiles[file] = bodyConfig; + _dpp.FindAndRegisterPropsFromConfig(bodyConfig); + } + } + } + GUI.enabled = true; + + GUILayout.Space(5); + + { + GUILayout.BeginHorizontal(); + if (GUILayout.Button(saveButtonUnlocked ? " O " : " | ", GUILayout.ExpandWidth(false))) + { + saveButtonUnlocked = !saveButtonUnlocked; + } + GUI.enabled = saveButtonUnlocked; + if (GUILayout.Button("Update your mod's configs")) + { + UpdateLoadedConfigs(); + + foreach (var filePath in loadedConfigFiles.Keys) + { + Logger.Log("Saving... " + filePath); + Main.Instance.ModHelper.Storage.Save(loadedConfigFiles[filePath], filePath); + } + saveButtonUnlocked = false; + } + GUI.enabled = true; + GUILayout.EndHorizontal(); + } + + if (GUILayout.Button("Print your mod's updated configs")) + { + UpdateLoadedConfigs(); + + foreach (var filePath in loadedConfigFiles.Keys) + { + Logger.Log("Updated copy of " + filePath); + Logger.Log(Newtonsoft.Json.JsonConvert.SerializeObject(loadedConfigFiles[filePath], Newtonsoft.Json.Formatting.Indented)); + } + //_dpp.PrintConfigs(); + } + // TODO: field to provide name of mod to load configs from, plus button to load those into the PropPlaecr (make sure not to load more than once, once the button has been pushed, disable it) // TODO: add a warning that the button cannot be pushed more than once @@ -122,6 +214,40 @@ namespace NewHorizons.Utility GUILayout.EndArea(); } + private void UpdateLoadedConfigs() + { + // for each keyvalue in _dpp.GetPropsConfigByBody() + // find the matching entry loadedConfigFiles + // entry matches if the value of AstroOBjectLocator.FindBody(key) matches + + var newDetails = _dpp.GetPropsConfigByBody(true); + + + //var allConfigsForMod = Main.Instance.BodyDict[Main.CurrentStarSystem].Where(x => x.Mod == mod).Select(x => x.Config) + + //var allConfigs = Main.BodyDict.Values.SelectMany(x => x).Where(x => x.Mod == loadedMod).Select(x => x.Config); + + // Get all configs + foreach (var filePath in loadedConfigFiles.Keys) + { + if (loadedConfigFiles[filePath].Name == null || AstroObjectLocator.GetAstroObject(loadedConfigFiles[filePath].Name) == null) continue; + + var bodyName = loadedConfigFiles[filePath].Name; + var astroObjectName = AstroObjectLocator.GetAstroObject(bodyName).name; + var systemName = loadedConfigFiles[filePath].StarSystem; + var composedName = systemName + separatorCharacter + astroObjectName; + + if (!newDetails.ContainsKey(composedName)) continue; + + if (loadedConfigFiles[filePath].Props == null) + { + (loadedConfigFiles[filePath] as PlanetConfig).Props = new External.PropModule(); + } + + loadedConfigFiles[filePath].Props.Details = newDetails[composedName]; + } + } + private void InitMenu() { if (_editorMenuStyle != null) return; diff --git a/NewHorizons/Utility/DebugPropPlacer.cs b/NewHorizons/Utility/DebugPropPlacer.cs index 818300bb..ca5cfb0e 100644 --- a/NewHorizons/Utility/DebugPropPlacer.cs +++ b/NewHorizons/Utility/DebugPropPlacer.cs @@ -7,6 +7,7 @@ using System.Text; using System.Threading.Tasks; using UnityEngine; using UnityEngine.InputSystem; +using static NewHorizons.External.PropModule; namespace NewHorizons.Utility { @@ -17,12 +18,16 @@ namespace NewHorizons.Utility private struct PropPlacementData { public string body; + public string system; public string propPath; public GameObject gameObject; public Vector3 pos { get { return gameObject.transform.localPosition; } } public Vector3 rotation { get { return gameObject.transform.localEulerAngles; } } + + public string assetBundle; + public string[] removeChildren; } // DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_1/Props_DreamZone_1/OtherComponentsGroup/Trees_Z1/DreamHouseIsland/Tree_DW_M_Var @@ -34,7 +39,7 @@ namespace NewHorizons.Utility private List deletedProps = new List(); private DebugRaycaster _rc; - public List RecentlyPlacedProps = new List(); + public HashSet RecentlyPlacedProps = new HashSet(); private void Awake() { @@ -51,10 +56,10 @@ namespace NewHorizons.Utility PlaceObject(); } - if (Keyboard.current[Key.Semicolon].wasReleasedThisFrame) - { - PrintConfigs(); - } + //if (Keyboard.current[Key.Semicolon].wasReleasedThisFrame) + //{ + // PrintConfigs(); + //} if (Keyboard.current[Key.Minus].wasReleasedThisFrame) { @@ -133,7 +138,7 @@ namespace NewHorizons.Utility prop.transform.parent = g.transform.parent; GameObject.Destroy(g); } - catch (Exception e) + catch { Logger.Log($"Failed to place object {currentObject} on body ${data.hitObject} at location ${data.pos}."); } @@ -143,17 +148,39 @@ namespace NewHorizons.Utility { AstroObject planet = AstroObjectLocator.GetAstroObject(config.Name); + if (planet == null || planet.GetRootSector() == null) return; + if (config.Props == null || config.Props.Details == null) return; + + List potentialProps = new List(); + foreach (Transform child in planet.GetRootSector().transform) potentialProps.Add(child); + potentialProps.Where(potentialProp => potentialProp.gameObject.name.EndsWith("(Clone)")).ToList(); + foreach (var detail in config.Props.Details) { - foreach (Transform child in planet.GetRootSector().transform) + var propPathElements = detail.path.Split('/'); + string propName = propPathElements[propPathElements.Length-1]; + + potentialProps + .Where(potentialProp => potentialProp.gameObject.name == propName+"(Clone)") + .OrderBy(potentialProp => Vector3.Distance(potentialProp.localPosition, detail.position)) + .ToList(); + + if (potentialProps.Count <= 0) { - bool childMatchesDetail = false; // TODO: this - - if (childMatchesDetail) - { - RegisterProp(detail.path, child.gameObject); - } - } + Logger.LogError($"No candidate found for prop {detail.path} on planet ${config.Name}."); + continue; + } + + Transform spawnedProp = potentialProps[0]; + PropPlacementData data = RegisterProp_WithReturn(config.Name, spawnedProp.gameObject, detail.path); + data.assetBundle = detail.assetBundle; + data.removeChildren = detail.removeChildren; + potentialProps.Remove(spawnedProp); + + if (!RecentlyPlacedProps.Contains(data.propPath)) + { + RecentlyPlacedProps.Add(data.propPath); + } } } @@ -162,72 +189,119 @@ namespace NewHorizons.Utility RegisterProp_WithReturn(bodyGameObjectName, prop); } - private PropPlacementData RegisterProp_WithReturn(string bodyGameObjectName, GameObject prop) + private PropPlacementData RegisterProp_WithReturn(string bodyGameObjectName, GameObject prop, string propPath = null, string systemName = null) { if (Main.Debug) { // TOOD: make this prop an item } - string bodyName = bodyGameObjectName.Substring(0, bodyGameObjectName.Length-"_Body".Length); + string bodyName = bodyGameObjectName.EndsWith("_Body") + ? bodyGameObjectName.Substring(0, bodyGameObjectName.Length-"_Body".Length) + : bodyGameObjectName; PropPlacementData data = new PropPlacementData { body = bodyName, - propPath = currentObject, - gameObject = prop + propPath = propPath == null ? currentObject : propPath, + gameObject = prop, + system = systemName }; props.Add(data); return data; } - public void PrintConfigs() - { - foreach(string configFile in GenerateConfigs()) - { - Logger.Log(configFile); - } - } + //public void PrintConfigs() + //{ + // foreach(string configFile in GenerateConfigs()) + // { + // Logger.Log(configFile); + // } + //} - public List GenerateConfigs() + //public List GenerateConfigs() + //{ + // var groupedProps = props + // .GroupBy(p => AstroObjectLocator.GetAstroObject(p.body).name) + // .Select(grp => grp.ToList()) + // .ToList(); + + // List configFiles = new List(); + + // foreach (List bodyProps in groupedProps) + // { + // string configFile = + // "{" + Environment.NewLine + + // " \"$schema\": \"https://raw.githubusercontent.com/xen-42/outer-wilds-new-horizons/master/NewHorizons/schema.json\"," + Environment.NewLine + + // $" \"name\" : \"{bodyProps[0].body}\"," + Environment.NewLine + + // " \"Props\" :" + Environment.NewLine + + // " {" + Environment.NewLine + + // " \"details\": [" + Environment.NewLine; + + // for(int i = 0; i < bodyProps.Count; i++) + // { + // PropPlacementData prop = bodyProps[i]; + + // string positionString = $"\"x\":{prop.pos.x},\"y\":{prop.pos.y},\"z\":{prop.pos.z}"; + // string rotationString = $"\"x\":{prop.rotation.x},\"y\":{prop.rotation.y},\"z\":{prop.rotation.z}"; + // string endingString = i == bodyProps.Count-1 ? "" : ","; + + // configFile += " {" + + // "\"path\" : \"" +prop.propPath+ "\", " + + // "\"position\": {"+positionString+"}, " + + // "\"rotation\": {"+rotationString+"}, " + + // "\"scale\": 1"+ + // (prop.assetBundle == null ? "" : $", \"assetBundle\": \"{prop.assetBundle}\"") + + // (prop.removeChildren == null ? "" : $", \"removeChildren\": \"[{string.Join(",",prop.removeChildren)}]\"") + + // "}" + endingString + Environment.NewLine; + // } + + // configFile += + // " ]" + Environment.NewLine + + // " }" + Environment.NewLine + + // "}"; + + // configFiles.Add(configFile); + // } + + // return configFiles; + //} + + public Dictionary GetPropsConfigByBody(bool useAstroObjectName = false) { var groupedProps = props - .GroupBy(p => p.body) + .GroupBy(p => p.system + "." + p.body) .Select(grp => grp.ToList()) .ToList(); - List configFiles = new List(); + Dictionary propConfigs = new Dictionary(); foreach (List bodyProps in groupedProps) { - string configFile = - "{" + Environment.NewLine + - " \"$schema\": \"https://raw.githubusercontent.com/xen-42/outer-wilds-new-horizons/master/NewHorizons/schema.json\"," + Environment.NewLine + - $" \"name\" : \"{bodyProps[0].body}\"," + Environment.NewLine + - " \"Props\" :" + Environment.NewLine + - " {" + Environment.NewLine + - " \"details\": [" + Environment.NewLine; - + + if (bodyProps == null || bodyProps.Count == 0) continue; + if ( AstroObjectLocator.GetAstroObject(bodyProps[0].body) == null ) continue; + string bodyName = useAstroObjectName ? AstroObjectLocator.GetAstroObject(bodyProps[0].body).name : bodyProps[0].body; + + DetailInfo[] infoArray = new DetailInfo[bodyProps.Count]; + propConfigs[bodyProps[0].system + DebugMenu.separatorCharacter + bodyName] = infoArray; + for(int i = 0; i < bodyProps.Count; i++) { - PropPlacementData prop = bodyProps[i]; - - string positionString = $"\"x\":{prop.pos.x},\"y\":{prop.pos.y},\"z\":{prop.pos.z}"; - string rotationString = $"\"x\":{prop.rotation.x},\"y\":{prop.rotation.y},\"z\":{prop.rotation.z}"; - string endingString = i == bodyProps.Count-1 ? "" : ","; - - configFile += " {\"path\" : \"" +prop.propPath+ "\", \"position\": {"+positionString+"}, \"rotation\": {"+rotationString+"}, \"scale\": 1}" + endingString + Environment.NewLine; + infoArray[i] = new DetailInfo() + { + path = bodyProps[i].propPath, + assetBundle = bodyProps[i].assetBundle, + position = bodyProps[i].gameObject.transform.localPosition, + rotation = bodyProps[i].gameObject.transform.localEulerAngles, + scale = bodyProps[i].gameObject.transform.localScale.x, + //public bool alignToNormal; // TODO: figure out how to recover this (or actually, rotation should cover it) + removeChildren = bodyProps[i].removeChildren + }; } - - configFile += - " ]" + Environment.NewLine + - " }" + Environment.NewLine + - "}"; - - configFiles.Add(configFile); } - return configFiles; + return propConfigs; } public void DeleteLast()