From 3084a97df1657d978f96a8299ead69a9a593b252 Mon Sep 17 00:00:00 2001 From: Nick Date: Sun, 25 Feb 2024 23:03:06 -0500 Subject: [PATCH 1/4] Manually add to dicts to avoid duplicate translation error spam --- NewHorizons/Handlers/TranslationHandler.cs | 24 +++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/NewHorizons/Handlers/TranslationHandler.cs b/NewHorizons/Handlers/TranslationHandler.cs index 2db44131..1695d954 100644 --- a/NewHorizons/Handlers/TranslationHandler.cs +++ b/NewHorizons/Handlers/TranslationHandler.cs @@ -121,22 +121,36 @@ namespace NewHorizons.Handlers } } + public static (string, string) FixKeyValue(string key, string value) + { + key = key.Replace("<", "<").Replace(">", ">").Replace("", ""); + value = value.Replace("<", "<").Replace(">", ">").Replace("", ""); + + return (key, value); + } + public static void AddDialogue(string rawText, bool trimRawTextForKey = false, params string[] rawPreText) { var key = string.Join(string.Empty, rawPreText) + (trimRawTextForKey? rawText.Trim() : rawText); - var text = GetTranslation(rawText, TextType.DIALOGUE); + var value = GetTranslation(rawText, TextType.DIALOGUE); - TextTranslation.Get().m_table.Insert(key, text); + // Manually insert directly into the dictionary, otherwise it logs errors about duplicates but we want to allow replacing + (key, value) = FixKeyValue(key, value); + + TextTranslation.Get().m_table.theTable[key] = value; } public static void AddShipLog(string rawText, params string[] rawPreText) { var key = string.Join(string.Empty, rawPreText) + rawText; - string text = GetTranslation(rawText, TextType.SHIPLOG); + string value = GetTranslation(rawText, TextType.SHIPLOG); - TextTranslation.Get().m_table.InsertShipLog(key, text); + // Manually insert directly into the dictionary, otherwise it logs errors about duplicates but we want to allow replacing + (key, value) = FixKeyValue(key, value); + + TextTranslation.Get().m_table.theShipLogTable[key] = value; } public static int AddUI(string rawText) @@ -154,7 +168,7 @@ namespace NewHorizons.Handlers } catch (Exception) { } - TextTranslation.Get().m_table.Insert_UI(key, text); + TextTranslation.Get().m_table.theUITable[key] = text; return key; } From 0298173935677d6f78396e30bc5232c08cc11b4a Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 26 Feb 2024 00:57:03 -0500 Subject: [PATCH 2/4] Set pathToExisting dialogue to merge dialogue into existing ones --- NewHorizons/Builder/Props/DialogueBuilder.cs | 77 +++++++++++++++++++ .../Modules/Props/Dialogue/DialogueInfo.cs | 5 ++ NewHorizons/Utility/NewHorizonExtensions.cs | 15 +++- 3 files changed, 95 insertions(+), 2 deletions(-) diff --git a/NewHorizons/Builder/Props/DialogueBuilder.cs b/NewHorizons/Builder/Props/DialogueBuilder.cs index c2068b1c..7afae72f 100644 --- a/NewHorizons/Builder/Props/DialogueBuilder.cs +++ b/NewHorizons/Builder/Props/DialogueBuilder.cs @@ -1,3 +1,4 @@ +using Delaunay; using NewHorizons.Components.Props; using NewHorizons.External.Modules.Props.Dialogue; using NewHorizons.Handlers; @@ -5,7 +6,9 @@ using NewHorizons.Utility; using NewHorizons.Utility.OuterWilds; using NewHorizons.Utility.OWML; using OWML.Common; +using System.Collections.Generic; using System.IO; +using System.Linq; using System.Xml; using System.Xml.Linq; using UnityEngine; @@ -25,6 +28,18 @@ namespace NewHorizons.Builder.Props // 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) { + if (string.IsNullOrEmpty(info.pathToExistingDialogue)) + { + return MakeNewDialogue(go, sector, info, xml, dialogueName); + } + else + { + return (AddToExistingDialogue(info, xml), null); + } + } + + private static (CharacterDialogueTree, RemoteDialogueTrigger) MakeNewDialogue(GameObject go, Sector sector, DialogueInfo info, string xml, string dialogueName) + { NHLogger.LogVerbose($"[DIALOGUE] Created a new character dialogue [{info.rename}] on [{info.parentPath}]"); // In stock I think they disable dialogue stuff with conditions @@ -54,6 +69,68 @@ namespace NewHorizons.Builder.Props return (dialogue, remoteTrigger); } + private static CharacterDialogueTree AddToExistingDialogue(DialogueInfo info, string xml) + { + AddTranslation(xml); + + var existingDialogue = SearchUtilities.Find(info.pathToExistingDialogue)?.GetComponent(); + + if (existingDialogue == null) + { + NHLogger.LogError($"Couldn't find dialogue at {info.pathToExistingDialogue}!"); + return null; + } + + var existingText = existingDialogue._xmlCharacterDialogueAsset.text; + + var existingDialogueDoc = new XmlDocument(); + existingDialogueDoc.LoadXml(existingText); + var existingDialogueTree = existingDialogueDoc.DocumentElement.SelectSingleNode("//DialogueTree"); + + var existingDialogueNodesByName = new Dictionary(); + foreach (XmlNode existingDialogueNode in existingDialogueTree.GetChildNodes("DialogueNode")) + { + var name = existingDialogueNode.GetChildNode("Name").InnerText; + existingDialogueNodesByName[name] = existingDialogueNode; + } + + var additionalDialogueDoc = new XmlDocument(); + additionalDialogueDoc.LoadXml(xml); + var newDialogueNodes = additionalDialogueDoc.DocumentElement.SelectSingleNode("//DialogueTree").GetChildNodes("DialogueNode"); + + foreach (XmlNode newDialogueNode in newDialogueNodes) + { + var name = newDialogueNode.GetChildNode("Name").InnerText; + + if (existingDialogueNodesByName.TryGetValue(name, out var existingNode)) + { + // We just have to merge the dialogue options + var dialogueOptions = newDialogueNode.GetChildNode("DialogueOptionsList").GetChildNodes("DialogueOption"); + var existingDialogueOptionsList = existingNode.GetChildNode("DialogueOptionsList"); + foreach (XmlNode node in dialogueOptions) + { + var importedNode = existingDialogueOptionsList.OwnerDocument.ImportNode(node, true); + existingDialogueOptionsList.AppendChild(importedNode); + } + } + else + { + // We add the new dialogue node to the existing dialogue + var importedNode = existingDialogueTree.OwnerDocument.ImportNode(newDialogueNode, true); + existingDialogueTree.AppendChild(importedNode); + } + } + + var newTextAsset = new TextAsset(existingDialogueDoc.OuterXml) + { + name = existingDialogue._xmlCharacterDialogueAsset.name + }; + + existingDialogue.SetTextXml(newTextAsset); + + return existingDialogue; + } + private static RemoteDialogueTrigger MakeRemoteDialogueTrigger(GameObject planetGO, Sector sector, DialogueInfo info, CharacterDialogueTree dialogue) { var conversationTrigger = GeneralPropBuilder.MakeNew("ConversationTrigger", planetGO, sector, info.remoteTrigger, defaultPosition: info.position, defaultParentPath: info.pathToAnimController); diff --git a/NewHorizons/External/Modules/Props/Dialogue/DialogueInfo.cs b/NewHorizons/External/Modules/Props/Dialogue/DialogueInfo.cs index 06207150..b2687c3c 100644 --- a/NewHorizons/External/Modules/Props/Dialogue/DialogueInfo.cs +++ b/NewHorizons/External/Modules/Props/Dialogue/DialogueInfo.cs @@ -31,6 +31,11 @@ namespace NewHorizons.External.Modules.Props.Dialogue /// public string pathToAnimController; + /// + /// If this dialogue is adding to existing character dialogue, put a path to the game object with the dialogue on it here + /// + public string pathToExistingDialogue; + /// /// Radius of the spherical collision volume where you get the "talk to" prompt when looking at. If you use a /// remoteTrigger, you can set this to 0 to make the dialogue only trigger remotely. diff --git a/NewHorizons/Utility/NewHorizonExtensions.cs b/NewHorizons/Utility/NewHorizonExtensions.cs index dd907c45..6e349e5d 100644 --- a/NewHorizons/Utility/NewHorizonExtensions.cs +++ b/NewHorizons/Utility/NewHorizonExtensions.cs @@ -12,6 +12,7 @@ using System.Linq; using System.Reflection; using System.Text; using System.Text.RegularExpressions; +using System.Xml; using UnityEngine; using static NewHorizons.External.Modules.ParticleFieldModule; using NomaiCoordinates = NewHorizons.External.Configs.StarSystemConfig.NomaiCoordinates; @@ -24,7 +25,7 @@ namespace NewHorizons.Utility { NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore, - Formatting = Formatting.Indented, + Formatting = Newtonsoft.Json.Formatting.Indented, }; private static StringBuilder stringBuilder = new StringBuilder(); @@ -36,7 +37,7 @@ namespace NewHorizons.Utility { using (JsonTextWriter jsonTextWriter = new JsonTextWriter(stringWriter) { - Formatting = Formatting.Indented, + Formatting = Newtonsoft.Json.Formatting.Indented, IndentChar = '\t', Indentation = 1 }) @@ -339,5 +340,15 @@ namespace NewHorizons.Utility } } } + + public static List GetChildNodes(this XmlNode parentNode, string tagName) + { + return parentNode.ChildNodes.Cast().Where(node => node.LocalName == tagName).ToList(); + } + + public static XmlNode GetChildNode(this XmlNode parentNode, string tagName) + { + return parentNode.ChildNodes.Cast().First(node => node.LocalName == tagName); + } } } From 1a4b29ca61ab5b5906baaceb2473187ea3c684be Mon Sep 17 00:00:00 2001 From: Ben C Date: Mon, 26 Feb 2024 05:58:32 +0000 Subject: [PATCH 3/4] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index 35fc04b2..833b1a89 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -1568,6 +1568,10 @@ "type": "string", "description": "If this dialogue is meant for a character, this is the relative path from the planet to that character's\nCharacterAnimController, TravelerController, TravelerEyeController (eye of the universe), FacePlayerWhenTalking, \nHearthianRecorderEffects or SolanumAnimController.\n\nIf it's a Recorder this will also delete the existing dialogue already attached to that prop.\n\nIf none of those components are present it will add a FacePlayerWhenTalking component." }, + "pathToExistingDialogue": { + "type": "string", + "description": "If this dialogue is adding to existing character dialogue, put a path to the game object with the dialogue on it here" + }, "radius": { "type": "number", "description": "Radius of the spherical collision volume where you get the \"talk to\" prompt when looking at. If you use a\nremoteTrigger, you can set this to 0 to make the dialogue only trigger remotely.", From 95dfe6a6905ecf51c61672b1c1ef0553248ae00c Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 26 Feb 2024 01:37:08 -0500 Subject: [PATCH 4/4] Update DialogueBuilder.cs --- NewHorizons/Builder/Props/DialogueBuilder.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/NewHorizons/Builder/Props/DialogueBuilder.cs b/NewHorizons/Builder/Props/DialogueBuilder.cs index 7afae72f..54eab78a 100644 --- a/NewHorizons/Builder/Props/DialogueBuilder.cs +++ b/NewHorizons/Builder/Props/DialogueBuilder.cs @@ -1,4 +1,3 @@ -using Delaunay; using NewHorizons.Components.Props; using NewHorizons.External.Modules.Props.Dialogue; using NewHorizons.Handlers; @@ -8,9 +7,7 @@ using NewHorizons.Utility.OWML; using OWML.Common; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Xml; -using System.Xml.Linq; using UnityEngine; namespace NewHorizons.Builder.Props