From d89ad0ff84c7ec3b5db10e4b0c77d16f89343740 Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 21 Jul 2023 13:03:02 -0400 Subject: [PATCH] Implement API methods for creating planet, system, dialogue directly from json/xml strings --- NewHorizons/Builder/Props/DialogueBuilder.cs | 17 ++- NewHorizons/INewHorizons.cs | 91 +++++++++---- NewHorizons/Main.cs | 50 ++++--- NewHorizons/NewHorizonsApi.cs | 133 ++++++++++++------- 4 files changed, 188 insertions(+), 103 deletions(-) diff --git a/NewHorizons/Builder/Props/DialogueBuilder.cs b/NewHorizons/Builder/Props/DialogueBuilder.cs index 8016f80d..c2068b1c 100644 --- a/NewHorizons/Builder/Props/DialogueBuilder.cs +++ b/NewHorizons/Builder/Props/DialogueBuilder.cs @@ -7,6 +7,7 @@ using NewHorizons.Utility.OWML; using OWML.Common; using System.IO; using System.Xml; +using System.Xml.Linq; using UnityEngine; namespace NewHorizons.Builder.Props @@ -15,6 +16,14 @@ namespace NewHorizons.Builder.Props { // Returns the character dialogue tree and remote dialogue trigger, if applicable. public static (CharacterDialogueTree, RemoteDialogueTrigger) Make(GameObject go, Sector sector, DialogueInfo info, IModBehaviour mod) + { + var xml = File.ReadAllText(Path.Combine(mod.ModHelper.Manifest.ModFolderPath, info.xmlFile)); + var dialogueName = Path.GetFileNameWithoutExtension(info.xmlFile); + return Make(go, sector, info, xml, dialogueName); + } + + // Create dialogue directly from xml string instead of loading it from a file + public static (CharacterDialogueTree, RemoteDialogueTrigger) Make(GameObject go, Sector sector, DialogueInfo info, string xml, string dialogueName) { NHLogger.LogVerbose($"[DIALOGUE] Created a new character dialogue [{info.rename}] on [{info.parentPath}]"); @@ -26,7 +35,7 @@ namespace NewHorizons.Builder.Props return (null, null); } - var dialogue = MakeConversationZone(go, sector, info, mod.ModHelper); + var dialogue = MakeConversationZone(go, sector, info, xml, dialogueName); RemoteDialogueTrigger remoteTrigger = null; if (info.remoteTrigger != null) @@ -76,7 +85,7 @@ namespace NewHorizons.Builder.Props return remoteDialogueTrigger; } - private static CharacterDialogueTree MakeConversationZone(GameObject planetGO, Sector sector, DialogueInfo info, IModHelper mod) + private static CharacterDialogueTree MakeConversationZone(GameObject planetGO, Sector sector, DialogueInfo info, string xml, string dialogueName) { var conversationZone = GeneralPropBuilder.MakeNew("ConversationZone", planetGO, sector, info, defaultParentPath: info.pathToAnimController); @@ -100,13 +109,11 @@ namespace NewHorizons.Builder.Props var dialogueTree = conversationZone.AddComponent(); - var xml = File.ReadAllText(Path.Combine(mod.Manifest.ModFolderPath, info.xmlFile)); var text = new TextAsset(xml) { // Text assets need a name to be used with VoiceMod - name = Path.GetFileNameWithoutExtension(info.xmlFile) + name = dialogueName }; - dialogueTree.SetTextXml(text); AddTranslation(xml); diff --git a/NewHorizons/INewHorizons.cs b/NewHorizons/INewHorizons.cs index 9f74f883..717819c0 100644 --- a/NewHorizons/INewHorizons.cs +++ b/NewHorizons/INewHorizons.cs @@ -9,22 +9,13 @@ namespace NewHorizons { public interface INewHorizons { + #region Obsolete [Obsolete("Create(Dictionary config) is deprecated, please use LoadConfigs(IModBehaviour mod) instead")] void Create(Dictionary config); [Obsolete("Create(Dictionary config) is deprecated, please use LoadConfigs(IModBehaviour mod) instead")] void Create(Dictionary config, IModBehaviour mod); - - /// - /// Directly add ship logs from XML. Call this method right before ShipLogManager awake. - /// - /// The mod this method is being called from. This is required for loading files. - /// The ship log xml contents - /// The planet that these ship logs correspond to in the map mode - /// The relative path from your mod manifest.json where the ship log images are located. The filename must be the same as the fact id. Optional. - /// A dictionary of each fact id to its 2D position in the ship log. Optional. - /// A dictionary of each curiousity ID to its colour and highlight colour in the ship log. Optional. - void AddShipLogXML(IModBehaviour mod, XElement xml, string planetName, string imageFolder = null, Dictionary entryPositions = null, Dictionary curiousityColours = null); + #endregion /// /// Will load all configs in the regular folders (planets, systems, translations, etc) for this mod. @@ -38,11 +29,30 @@ namespace NewHorizons /// GameObject GetPlanet(string name); + /// + /// Returns the uniqueIDs of each installed NH addon. + /// + string[] GetInstalledAddons(); + + #region Get/set/change star system /// /// The name of the current star system loaded. /// string GetCurrentStarSystem(); + /// + /// Allows you to overwrite the default system. This is where the player is respawned after dying. + /// + bool SetDefaultSystem(string name); + + /// + /// Allows you to instantly begin a warp to a new star system. + /// Will return false if that system does not exist (cannot be warped to). + /// + bool ChangeCurrentStarSystem(string name); + #endregion + + #region events /// /// An event invoked when the player begins loading the new star system, before the scene starts to load. /// Gives the name of the star system being switched to. @@ -60,7 +70,9 @@ namespace NewHorizons /// Gives the name of the planet that was just loaded. /// UnityEvent GetBodyLoadedEvent(); + #endregion + #region Querying configs /// /// Uses JSONPath to query a body /// @@ -80,23 +92,9 @@ namespace NewHorizons /// Uses JSONPath to query the current star system /// T QuerySystem(string path); + #endregion - /// - /// Allows you to overwrite the default system. This is where the player is respawned after dying. - /// - bool SetDefaultSystem(string name); - - /// - /// Allows you to instantly begin a warp to a new star system. - /// Will return false if that system does not exist (cannot be warped to). - /// - bool ChangeCurrentStarSystem(string name); - - /// - /// Returns the uniqueIDs of each installed NH addon. - /// - string[] GetInstalledAddons(); - + #region Spawn props /// /// Allows you to spawn a copy of a prop by specifying its path. /// This is the same as using Props->details in a config, but also returns the spawned gameObject to you. @@ -121,5 +119,44 @@ namespace NewHorizons (CharacterDialogueTree, RemoteDialogueTrigger) SpawnDialogue(IModBehaviour mod, GameObject root, string xmlFile, float radius = 1f, float range = 1f, string blockAfterPersistentCondition = null, float lookAtRadius = 1f, string pathToAnimController = null, float remoteTriggerRadius = 0f); + #endregion + + #region Load json/xml directly + /// + /// Allows creation of a planet by directly passing the json contents as a string. + /// + /// + /// + void CreatePlanet(string config, IModBehaviour mod); + + /// + /// Allows defining details of a star system by directly passing the json contents as a string. + /// + /// + /// + /// + void DefineStarSystem(string name, string config, IModBehaviour mod); + + /// + /// Allows creation of dialogue by directly passing the xml and dialogueInfo json contents as strings + /// + /// TextAsset name used for compatibility with voice mod. Just has to be a unique identifier. + /// The contents of the dialogue xml file as a string + /// The json dialogue info as a string. See the documentation/schema for what this can contain. + /// The root planet rigidbody that this dialogue is attached to. Any paths in the dialogueInfo are relative to this body. + /// + (CharacterDialogueTree, RemoteDialogueTrigger) CreateDialogueFromXML(string textAssetID, string xml, string dialogueInfo, GameObject planetGO); + + /// + /// Directly add ship logs from XML. Call this method right before ShipLogManager awake. + /// + /// The mod this method is being called from. This is required for loading files. + /// The ship log xml contents + /// The planet that these ship logs correspond to in the map mode + /// The relative path from your mod manifest.json where the ship log images are located. The filename must be the same as the fact id. Optional. + /// A dictionary of each fact id to its 2D position in the ship log. Optional. + /// A dictionary of each curiousity ID to its colour and highlight colour in the ship log. Optional. + void AddShipLogXML(IModBehaviour mod, XElement xml, string planetName, string imageFolder = null, Dictionary entryPositions = null, Dictionary curiousityColours = null); + #endregion } } diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index 8dcfaa2f..499c2468 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -33,6 +33,7 @@ using NewHorizons.Utility.DebugTools; using NewHorizons.Utility.DebugTools.Menu; using NewHorizons.Components.Ship; using NewHorizons.Builder.Props.Audio; +using Epic.OnlineServices; namespace NewHorizons { @@ -771,28 +772,7 @@ namespace NewHorizons NHLogger.LogVerbose($"Loaded {config.name}"); - if (!SystemDict.ContainsKey(config.starSystem)) - { - // Since we didn't load it earlier there shouldn't be a star system config - var starSystemConfig = mod.ModHelper.Storage.Load(Path.Combine("systems", config.starSystem + ".json"), false); - if (starSystemConfig == null) starSystemConfig = new StarSystemConfig(); - else NHLogger.LogWarning($"Loaded system config for {config.starSystem}. Why wasn't this loaded earlier?"); - - starSystemConfig.Migrate(); - starSystemConfig.FixCoordinates(); - - var system = new NewHorizonsSystem(config.starSystem, starSystemConfig, $"", mod); - - SystemDict.Add(config.starSystem, system); - - BodyDict.Add(config.starSystem, new List()); - } - - // Has to happen after we make sure theres a system config - config.Validate(); - config.Migrate(); - - body = new NewHorizonsBody(config, mod, relativePath); + body = RegisterPlanetConfig(config, mod, relativePath); } catch (Exception e) { @@ -803,6 +783,32 @@ namespace NewHorizons return body; } + public NewHorizonsBody RegisterPlanetConfig(PlanetConfig config, IModBehaviour mod, string relativePath) + { + if (!SystemDict.ContainsKey(config.starSystem)) + { + // Since we didn't load it earlier there shouldn't be a star system config + var starSystemConfig = mod.ModHelper.Storage.Load(Path.Combine("systems", config.starSystem + ".json"), false); + if (starSystemConfig == null) starSystemConfig = new StarSystemConfig(); + else NHLogger.LogWarning($"Loaded system config for {config.starSystem}. Why wasn't this loaded earlier?"); + + starSystemConfig.Migrate(); + starSystemConfig.FixCoordinates(); + + var system = new NewHorizonsSystem(config.starSystem, starSystemConfig, $"", mod); + + SystemDict.Add(config.starSystem, system); + + BodyDict.Add(config.starSystem, new List()); + } + + // Has to happen after we make sure theres a system config + config.Validate(); + config.Migrate(); + + return new NewHorizonsBody(config, mod, relativePath); + } + public void SetDefaultSystem(string defaultSystem) { _defaultStarSystem = defaultSystem; diff --git a/NewHorizons/NewHorizonsApi.cs b/NewHorizons/NewHorizonsApi.cs index 4518f9fe..611233b9 100644 --- a/NewHorizons/NewHorizonsApi.cs +++ b/NewHorizons/NewHorizonsApi.cs @@ -8,6 +8,7 @@ using NewHorizons.External.Modules.Props; using NewHorizons.External.Modules.Props.Audio; using NewHorizons.External.Modules.Props.Dialogue; using NewHorizons.External.SerializableData; +using NewHorizons.OtherMods.MenuFramework; using NewHorizons.Utility; using NewHorizons.Utility.OWML; using Newtonsoft.Json; @@ -67,55 +68,6 @@ namespace NewHorizons } } - public void AddShipLogXML(IModBehaviour mod, XElement xml, string planetName, string imageFolder, Dictionary entryPositions, Dictionary curiousityColours) - { - // This method has to be called each time the ship log manager is created, i.e. each time a system loads so it will only ever be relevant to the current one. - var starSystem = Main.Instance.CurrentStarSystem; - - var body = new NewHorizonsBody(new PlanetConfig() - { - name = planetName, - starSystem = starSystem, - ShipLog = new ShipLogModule() - { - spriteFolder = imageFolder - } - }, mod); - - if (!Main.BodyDict.ContainsKey(starSystem)) - { - Main.BodyDict.Add(starSystem, new List()); - Main.BodyDict[starSystem].Add(body); - } - else - { - var existingBody = Main.BodyDict[starSystem] - .FirstOrDefault(x => x.Config.name == planetName && x.Mod.ModHelper.Manifest.UniqueName == mod.ModHelper.Manifest.UniqueName); - if (existingBody != null) - { - body = existingBody; - } - else - { - Main.BodyDict[starSystem].Add(body); - } - } - - var system = new StarSystemConfig() - { - entryPositions = entryPositions - .Select((pair) => new EntryPositionInfo() { id = pair.Key, position = pair.Value }) - .ToArray(), - curiosities = curiousityColours - .Select((pair) => new CuriosityColorInfo() { id = pair.Key, color = MColor.FromColor(pair.Value.colour), highlightColor = MColor.FromColor(pair.Value.highlight) }) - .ToArray() - }; - - Main.Instance.LoadStarSystemConfig(starSystem, system, null, mod); - - RumorModeBuilder.AddShipLogXML(GameObject.FindObjectOfType(), xml, body); - } - public void LoadConfigs(IModBehaviour mod) { Main.Instance.LoadConfigs(mod); @@ -269,5 +221,88 @@ namespace NewHorizons return DialogueBuilder.Make(root, null, info, mod); } + + public void CreatePlanet(string config, IModBehaviour mod) + { + try + { + var planet = JsonConvert.DeserializeObject(config); + if (planet == null) + { + NHLogger.LogError($"Couldn't load planet via API. Is your Json formatted correctly? {config}"); + return; + } + + var body = Main.Instance.RegisterPlanetConfig(planet, mod, null); + + if (!Main.BodyDict.ContainsKey(body.Config.starSystem)) Main.BodyDict.Add(body.Config.starSystem, new List()); + Main.BodyDict[body.Config.starSystem].Add(body); + } + catch (Exception ex) + { + NHLogger.LogError($"Error in CreatePlanet API:\n{ex}"); + } + } + + public void DefineStarSystem(string name, string config, IModBehaviour mod) + { + var starSystemConfig = JsonConvert.DeserializeObject(config); + Main.Instance.LoadStarSystemConfig(name, starSystemConfig, null, mod); + } + + public (CharacterDialogueTree, RemoteDialogueTrigger) CreateDialogueFromXML(string textAssetID, string xml, string dialogueInfo, GameObject planetGO) + { + var info = JsonConvert.DeserializeObject(dialogueInfo); + return DialogueBuilder.Make(planetGO, null, info, xml, textAssetID); + } + + public void AddShipLogXML(IModBehaviour mod, XElement xml, string planetName, string imageFolder, Dictionary entryPositions, Dictionary curiousityColours) + { + // This method has to be called each time the ship log manager is created, i.e. each time a system loads so it will only ever be relevant to the current one. + var starSystem = Main.Instance.CurrentStarSystem; + + var body = new NewHorizonsBody(new PlanetConfig() + { + name = planetName, + starSystem = starSystem, + ShipLog = new ShipLogModule() + { + spriteFolder = imageFolder + } + }, mod); + + if (!Main.BodyDict.ContainsKey(starSystem)) + { + Main.BodyDict.Add(starSystem, new List()); + Main.BodyDict[starSystem].Add(body); + } + else + { + var existingBody = Main.BodyDict[starSystem] + .FirstOrDefault(x => x.Config.name == planetName && x.Mod.ModHelper.Manifest.UniqueName == mod.ModHelper.Manifest.UniqueName); + if (existingBody != null) + { + body = existingBody; + } + else + { + Main.BodyDict[starSystem].Add(body); + } + } + + var system = new StarSystemConfig() + { + entryPositions = entryPositions + .Select((pair) => new EntryPositionInfo() { id = pair.Key, position = pair.Value }) + .ToArray(), + curiosities = curiousityColours + .Select((pair) => new CuriosityColorInfo() { id = pair.Key, color = MColor.FromColor(pair.Value.colour), highlightColor = MColor.FromColor(pair.Value.highlight) }) + .ToArray() + }; + + Main.Instance.LoadStarSystemConfig(starSystem, system, null, mod); + + RumorModeBuilder.AddShipLogXML(GameObject.FindObjectOfType(), xml, body); + } } }