Implement API methods for creating planet, system, dialogue directly from json/xml strings

This commit is contained in:
Nick 2023-07-21 13:03:02 -04:00
parent 664f946322
commit d89ad0ff84
4 changed files with 188 additions and 103 deletions

View File

@ -7,6 +7,7 @@ using NewHorizons.Utility.OWML;
using OWML.Common; using OWML.Common;
using System.IO; using System.IO;
using System.Xml; using System.Xml;
using System.Xml.Linq;
using UnityEngine; using UnityEngine;
namespace NewHorizons.Builder.Props namespace NewHorizons.Builder.Props
@ -15,6 +16,14 @@ namespace NewHorizons.Builder.Props
{ {
// Returns the character dialogue tree and remote dialogue trigger, if applicable. // Returns the character dialogue tree and remote dialogue trigger, if applicable.
public static (CharacterDialogueTree, RemoteDialogueTrigger) Make(GameObject go, Sector sector, DialogueInfo info, IModBehaviour mod) 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}]"); NHLogger.LogVerbose($"[DIALOGUE] Created a new character dialogue [{info.rename}] on [{info.parentPath}]");
@ -26,7 +35,7 @@ namespace NewHorizons.Builder.Props
return (null, null); return (null, null);
} }
var dialogue = MakeConversationZone(go, sector, info, mod.ModHelper); var dialogue = MakeConversationZone(go, sector, info, xml, dialogueName);
RemoteDialogueTrigger remoteTrigger = null; RemoteDialogueTrigger remoteTrigger = null;
if (info.remoteTrigger != null) if (info.remoteTrigger != null)
@ -76,7 +85,7 @@ namespace NewHorizons.Builder.Props
return remoteDialogueTrigger; 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); var conversationZone = GeneralPropBuilder.MakeNew("ConversationZone", planetGO, sector, info, defaultParentPath: info.pathToAnimController);
@ -100,13 +109,11 @@ namespace NewHorizons.Builder.Props
var dialogueTree = conversationZone.AddComponent<NHCharacterDialogueTree>(); var dialogueTree = conversationZone.AddComponent<NHCharacterDialogueTree>();
var xml = File.ReadAllText(Path.Combine(mod.Manifest.ModFolderPath, info.xmlFile));
var text = new TextAsset(xml) var text = new TextAsset(xml)
{ {
// Text assets need a name to be used with VoiceMod // Text assets need a name to be used with VoiceMod
name = Path.GetFileNameWithoutExtension(info.xmlFile) name = dialogueName
}; };
dialogueTree.SetTextXml(text); dialogueTree.SetTextXml(text);
AddTranslation(xml); AddTranslation(xml);

View File

@ -9,22 +9,13 @@ namespace NewHorizons
{ {
public interface INewHorizons public interface INewHorizons
{ {
#region Obsolete
[Obsolete("Create(Dictionary<string, object> config) is deprecated, please use LoadConfigs(IModBehaviour mod) instead")] [Obsolete("Create(Dictionary<string, object> config) is deprecated, please use LoadConfigs(IModBehaviour mod) instead")]
void Create(Dictionary<string, object> config); void Create(Dictionary<string, object> config);
[Obsolete("Create(Dictionary<string, object> config) is deprecated, please use LoadConfigs(IModBehaviour mod) instead")] [Obsolete("Create(Dictionary<string, object> config) is deprecated, please use LoadConfigs(IModBehaviour mod) instead")]
void Create(Dictionary<string, object> config, IModBehaviour mod); void Create(Dictionary<string, object> config, IModBehaviour mod);
#endregion
/// <summary>
/// Directly add ship logs from XML. Call this method right before ShipLogManager awake.
/// </summary>
/// <param name="mod">The mod this method is being called from. This is required for loading files.</param>
/// <param name="xml">The ship log xml contents</param>
/// <param name="planetName">The planet that these ship logs correspond to in the map mode</param>
/// <param name="imageFolder">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.</param>
/// <param name="entryPositions">A dictionary of each fact id to its 2D position in the ship log. Optional.</param>
/// <param name="curiousityColours">A dictionary of each curiousity ID to its colour and highlight colour in the ship log. Optional.</param>
void AddShipLogXML(IModBehaviour mod, XElement xml, string planetName, string imageFolder = null, Dictionary<string, Vector2> entryPositions = null, Dictionary<string, (Color colour, Color highlight)> curiousityColours = null);
/// <summary> /// <summary>
/// Will load all configs in the regular folders (planets, systems, translations, etc) for this mod. /// Will load all configs in the regular folders (planets, systems, translations, etc) for this mod.
@ -38,11 +29,30 @@ namespace NewHorizons
/// </summary> /// </summary>
GameObject GetPlanet(string name); GameObject GetPlanet(string name);
/// <summary>
/// Returns the uniqueIDs of each installed NH addon.
/// </summary>
string[] GetInstalledAddons();
#region Get/set/change star system
/// <summary> /// <summary>
/// The name of the current star system loaded. /// The name of the current star system loaded.
/// </summary> /// </summary>
string GetCurrentStarSystem(); string GetCurrentStarSystem();
/// <summary>
/// Allows you to overwrite the default system. This is where the player is respawned after dying.
/// </summary>
bool SetDefaultSystem(string name);
/// <summary>
/// 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).
/// </summary>
bool ChangeCurrentStarSystem(string name);
#endregion
#region events
/// <summary> /// <summary>
/// An event invoked when the player begins loading the new star system, before the scene starts to load. /// 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. /// 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. /// Gives the name of the planet that was just loaded.
/// </summary> /// </summary>
UnityEvent<string> GetBodyLoadedEvent(); UnityEvent<string> GetBodyLoadedEvent();
#endregion
#region Querying configs
/// <summary> /// <summary>
/// Uses JSONPath to query a body /// Uses JSONPath to query a body
/// </summary> /// </summary>
@ -80,23 +92,9 @@ namespace NewHorizons
/// Uses JSONPath to query the current star system /// Uses JSONPath to query the current star system
///</summary> ///</summary>
T QuerySystem<T>(string path); T QuerySystem<T>(string path);
#endregion
/// <summary> #region Spawn props
/// Allows you to overwrite the default system. This is where the player is respawned after dying.
/// </summary>
bool SetDefaultSystem(string name);
/// <summary>
/// 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).
/// </summary>
bool ChangeCurrentStarSystem(string name);
/// <summary>
/// Returns the uniqueIDs of each installed NH addon.
/// </summary>
string[] GetInstalledAddons();
/// <summary> /// <summary>
/// Allows you to spawn a copy of a prop by specifying its path. /// 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. /// 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, (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 range = 1f, string blockAfterPersistentCondition = null, float lookAtRadius = 1f, string pathToAnimController = null,
float remoteTriggerRadius = 0f); float remoteTriggerRadius = 0f);
#endregion
#region Load json/xml directly
/// <summary>
/// Allows creation of a planet by directly passing the json contents as a string.
/// </summary>
/// <param name="config"></param>
/// <param name="mod"></param>
void CreatePlanet(string config, IModBehaviour mod);
/// <summary>
/// Allows defining details of a star system by directly passing the json contents as a string.
/// </summary>
/// <param name="name"></param>
/// <param name="config"></param>
/// <param name="mod"></param>
void DefineStarSystem(string name, string config, IModBehaviour mod);
/// <summary>
/// Allows creation of dialogue by directly passing the xml and dialogueInfo json contents as strings
/// </summary>
/// <param name="textAssetID">TextAsset name used for compatibility with voice mod. Just has to be a unique identifier.</param>
/// <param name="xml">The contents of the dialogue xml file as a string</param>
/// <param name="dialogueInfo">The json dialogue info as a string. See the documentation/schema for what this can contain.</param>
/// <param name="planetGO">The root planet rigidbody that this dialogue is attached to. Any paths in the dialogueInfo are relative to this body.</param>
/// <returns></returns>
(CharacterDialogueTree, RemoteDialogueTrigger) CreateDialogueFromXML(string textAssetID, string xml, string dialogueInfo, GameObject planetGO);
/// <summary>
/// Directly add ship logs from XML. Call this method right before ShipLogManager awake.
/// </summary>
/// <param name="mod">The mod this method is being called from. This is required for loading files.</param>
/// <param name="xml">The ship log xml contents</param>
/// <param name="planetName">The planet that these ship logs correspond to in the map mode</param>
/// <param name="imageFolder">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.</param>
/// <param name="entryPositions">A dictionary of each fact id to its 2D position in the ship log. Optional.</param>
/// <param name="curiousityColours">A dictionary of each curiousity ID to its colour and highlight colour in the ship log. Optional.</param>
void AddShipLogXML(IModBehaviour mod, XElement xml, string planetName, string imageFolder = null, Dictionary<string, Vector2> entryPositions = null, Dictionary<string, (Color colour, Color highlight)> curiousityColours = null);
#endregion
} }
} }

View File

@ -33,6 +33,7 @@ using NewHorizons.Utility.DebugTools;
using NewHorizons.Utility.DebugTools.Menu; using NewHorizons.Utility.DebugTools.Menu;
using NewHorizons.Components.Ship; using NewHorizons.Components.Ship;
using NewHorizons.Builder.Props.Audio; using NewHorizons.Builder.Props.Audio;
using Epic.OnlineServices;
namespace NewHorizons namespace NewHorizons
{ {
@ -771,6 +772,19 @@ namespace NewHorizons
NHLogger.LogVerbose($"Loaded {config.name}"); NHLogger.LogVerbose($"Loaded {config.name}");
body = RegisterPlanetConfig(config, mod, relativePath);
}
catch (Exception e)
{
NHLogger.LogError($"Error encounter when loading {relativePath}:\n{e}");
MenuHandler.RegisterFailedConfig(Path.GetFileName(relativePath));
}
return body;
}
public NewHorizonsBody RegisterPlanetConfig(PlanetConfig config, IModBehaviour mod, string relativePath)
{
if (!SystemDict.ContainsKey(config.starSystem)) if (!SystemDict.ContainsKey(config.starSystem))
{ {
// Since we didn't load it earlier there shouldn't be a star system config // Since we didn't load it earlier there shouldn't be a star system config
@ -792,15 +806,7 @@ namespace NewHorizons
config.Validate(); config.Validate();
config.Migrate(); config.Migrate();
body = new NewHorizonsBody(config, mod, relativePath); return new NewHorizonsBody(config, mod, relativePath);
}
catch (Exception e)
{
NHLogger.LogError($"Error encounter when loading {relativePath}:\n{e}");
MenuHandler.RegisterFailedConfig(Path.GetFileName(relativePath));
}
return body;
} }
public void SetDefaultSystem(string defaultSystem) public void SetDefaultSystem(string defaultSystem)

View File

@ -8,6 +8,7 @@ using NewHorizons.External.Modules.Props;
using NewHorizons.External.Modules.Props.Audio; using NewHorizons.External.Modules.Props.Audio;
using NewHorizons.External.Modules.Props.Dialogue; using NewHorizons.External.Modules.Props.Dialogue;
using NewHorizons.External.SerializableData; using NewHorizons.External.SerializableData;
using NewHorizons.OtherMods.MenuFramework;
using NewHorizons.Utility; using NewHorizons.Utility;
using NewHorizons.Utility.OWML; using NewHorizons.Utility.OWML;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -67,55 +68,6 @@ namespace NewHorizons
} }
} }
public void AddShipLogXML(IModBehaviour mod, XElement xml, string planetName, string imageFolder, Dictionary<string, Vector2> entryPositions, Dictionary<string, (Color colour, Color highlight)> 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<NewHorizonsBody>());
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<ShipLogManager>(), xml, body);
}
public void LoadConfigs(IModBehaviour mod) public void LoadConfigs(IModBehaviour mod)
{ {
Main.Instance.LoadConfigs(mod); Main.Instance.LoadConfigs(mod);
@ -269,5 +221,88 @@ namespace NewHorizons
return DialogueBuilder.Make(root, null, info, mod); return DialogueBuilder.Make(root, null, info, mod);
} }
public void CreatePlanet(string config, IModBehaviour mod)
{
try
{
var planet = JsonConvert.DeserializeObject<PlanetConfig>(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<NewHorizonsBody>());
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<StarSystemConfig>(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>(dialogueInfo);
return DialogueBuilder.Make(planetGO, null, info, xml, textAssetID);
}
public void AddShipLogXML(IModBehaviour mod, XElement xml, string planetName, string imageFolder, Dictionary<string, Vector2> entryPositions, Dictionary<string, (Color colour, Color highlight)> 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<NewHorizonsBody>());
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<ShipLogManager>(), xml, body);
}
} }
} }