From 8b2880248d6d0b4942accd7d9caeaff7e586f71d Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 13 Aug 2022 14:45:04 -0400 Subject: [PATCH 1/4] Scrolling two-column credits work --- NewHorizons/Assets/addon-manifest.json | 18 ++ NewHorizons/External/Configs/AddonConfig.cs | 4 + NewHorizons/Handlers/CreditsHandler.cs | 164 ++++++++++++++++++ NewHorizons/Main.cs | 18 +- .../CreditsScene/CreditsEntryPatches.cs | 33 ++++ .../Patches/CreditsScene/CreditsPatches.cs | 17 ++ 6 files changed, 251 insertions(+), 3 deletions(-) create mode 100644 NewHorizons/Assets/addon-manifest.json create mode 100644 NewHorizons/Handlers/CreditsHandler.cs create mode 100644 NewHorizons/Patches/CreditsScene/CreditsEntryPatches.cs create mode 100644 NewHorizons/Patches/CreditsScene/CreditsPatches.cs diff --git a/NewHorizons/Assets/addon-manifest.json b/NewHorizons/Assets/addon-manifest.json new file mode 100644 index 00000000..bf105729 --- /dev/null +++ b/NewHorizons/Assets/addon-manifest.json @@ -0,0 +1,18 @@ +{ + "credits": [ + "xen#Mod Director\n#Programmer", + "Bwc9876#Mod Manager\n#Programmer\n#Dev Ops", + "FreezeDriedMangos#Programmer\n#Dev Tool Creator", + "MegaPiggy#Programmer", + "JohnCorby#Programmer", + "Hawkbat#Programmer", + "Trifid#Tester\n#Programmer", + "Nageld#Programmer", + "Ernesto#Fish", + "with help from#Raicuparta\n#dgarroDC\n#jtsalomo\n#and the modding community", + "Based off Marshmallow made by#Mister_Nebula", + "with help from#AmazingAlek\n#Raicuparta\n#and the Outer Wilds discord server", + " ", + "This work is unofficial Fan Content" + ] +} diff --git a/NewHorizons/External/Configs/AddonConfig.cs b/NewHorizons/External/Configs/AddonConfig.cs index bf89193f..2fe85d7b 100644 --- a/NewHorizons/External/Configs/AddonConfig.cs +++ b/NewHorizons/External/Configs/AddonConfig.cs @@ -24,5 +24,9 @@ namespace NewHorizons.External.Configs /// public AchievementInfo[] achievements; + /// + /// Credits info for this mod. A list of contributors and their roles separated by #. For example: xen#New Horizons dev. + /// + public string[] credits; } } diff --git a/NewHorizons/Handlers/CreditsHandler.cs b/NewHorizons/Handlers/CreditsHandler.cs new file mode 100644 index 00000000..8b026921 --- /dev/null +++ b/NewHorizons/Handlers/CreditsHandler.cs @@ -0,0 +1,164 @@ +using System.Collections.Generic; +using System.Xml; +using UnityEngine; +using Logger = NewHorizons.Utility.Logger; + +namespace NewHorizons.Handlers +{ + public static class CreditsHandler + { + private static Dictionary _creditsInfo; + + public static void RegisterCredits(string sectionName, string[] entries) + { + if (_creditsInfo == null) _creditsInfo = new(); + + Logger.LogVerbose($"Registering credits for {sectionName}"); + + _creditsInfo[sectionName] = entries; + } + + public static void AddCredits(Credits credits) + { + Logger.LogVerbose($"Adding to credits"); + + var creditsAsset = credits._creditsAsset; + + var xml = new XmlDocument(); + xml.LoadXml(creditsAsset.xml.text); + + foreach (var pair in _creditsInfo) + { + AddCreditsSection(pair.Key, pair.Value, ref xml); + } + + var outerXml = xml.OuterXml.Replace("/n", " "); + + creditsAsset.xml = new TextAsset(outerXml); + } + + private static void AddCreditsSection(string sectionName, string[] entries, ref XmlDocument xml) + { + var finalCredits = xml.SelectSingleNode("Credits/section"); + + // Fade credits look wrong right now bc it works with just one column not two + /* + var nodeFade = CreateCreditsFromList(xml, sectionName, entries, true); + finalCredits.InsertAfter(nodeFade, finalCredits.ChildNodes[0]); + */ + + var fastCredits = NodeWhere(finalCredits.ChildNodes, "MainScrollSection"); + var nodeScroll = CreateCreditsFromList(xml, sectionName, entries, false); + fastCredits.InsertBefore(nodeScroll, fastCredits.ChildNodes[0]); + } + + private static XmlNode NodeWhere(XmlNodeList list, string name) + { + foreach(XmlNode node in list) + { + try + { + if (node.Attributes[0].Value == name) return node; + } + catch { } + } + return null; + } + + private static XmlNode CreateCreditsFromList(XmlDocument doc, string title, string[] entries, bool fade) + { + var rootSection = MakeNode(doc, "section", fade ? new Dictionary() + { + { "platform", "All" }, + { "type", "Fade" }, + { "fadeInTime", "1.3" }, + { "displayTime", "10" }, + { "fadeOutTime", "1.4" }, + { "waitTime", "0.5" }, + { "padding-bottom", "-8" }, + { "spacing", "16" } + } : new Dictionary() + { + { "platform", "All" }, + { "type", "Scroll" }, + { "scrollDuration", "214" }, + { "spacing", "12" }, + { "width", "1590" } + }); + + var titleLayout = MakeNode(doc, "layout", new Dictionary() + { + { "type", fade ? "SingleColumnFadeCentered" : "SingleColumnScrollCentered" } + }); + + var titleNode = MakeNode(doc, "title", new Dictionary() + { + {"text-align", "UpperCenter" }, + {"height", "122" } + }); + titleNode.InnerText = title; + titleLayout.AppendChild(titleNode); + + var type = fade ? "SingleColumnFadeCentered" : "TwoColumnScrollAlignRightLeft"; + var xmlText = $"\n"; + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + if (fade) entry = entry.Split('#')[0]; + + xmlText += $"{entry}\n"; + if (fade) + { + if (i == entries.Length - 1) xmlText += "\n"; + else xmlText += "\n"; + } + } + xmlText += ""; + + rootSection.AppendChild(titleLayout); + rootSection.AppendChild(StringToNode(doc, xmlText)); + + return rootSection; + } + + private static XmlNode StringToNode(XmlDocument docContext, string text) + { + var doc = new XmlDocument(); + doc.LoadXml(text); + + // ArgumentException: The node to be inserted is from a different document context. + var importedNode = docContext.ImportNode(doc.DocumentElement, true); + + return importedNode; + } + + private static XmlNode MakeNode(XmlDocument doc, string nodeType, Dictionary attributes) + { + var xmlNode = doc.CreateElement(nodeType); + + if (attributes != null) + { + foreach (var pair in attributes) + { + var attribute = doc.CreateAttribute(pair.Key); + attribute.Value = pair.Value; + xmlNode.Attributes.Append(attribute); + } + } + + return xmlNode; + } + + internal class CreditsInfo + { + string sectionName; + string[] entries; + + internal CreditsInfo(string sectionName, string[] entries) + { + this.sectionName = sectionName; + this.entries = entries; + } + } + } +} diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index aeacc748..75ad2e23 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -198,6 +198,8 @@ namespace NewHorizons AchievementHandler.Init(); VoiceHandler.Init(); + + LoadAddonManifest("Assets/addon-manifest.json", this); } public void OnDestroy() @@ -507,9 +509,7 @@ namespace NewHorizons // Has to go before translations for achievements if (File.Exists(folder + "addon-manifest.json")) { - var addonConfig = mod.ModHelper.Storage.Load("addon-manifest.json"); - - AchievementHandler.RegisterAddon(addonConfig, mod as ModBehaviour); + LoadAddonManifest("addon-manifest.json", mod); } if (Directory.Exists(folder + @"translations\")) { @@ -523,6 +523,18 @@ namespace NewHorizons } } + private void LoadAddonManifest(string file, IModBehaviour mod) + { + Logger.LogError($"{mod.ModHelper.Manifest.Name}"); + + var addonConfig = mod.ModHelper.Storage.Load(file); + + + + if (addonConfig.achievements != null) AchievementHandler.RegisterAddon(addonConfig, mod as ModBehaviour); + if (addonConfig.credits != null) CreditsHandler.RegisterCredits(mod.ModHelper.Manifest.Name, addonConfig.credits); + } + private void LoadTranslations(string folder, IModBehaviour mod) { var foundFile = false; diff --git a/NewHorizons/Patches/CreditsScene/CreditsEntryPatches.cs b/NewHorizons/Patches/CreditsScene/CreditsEntryPatches.cs new file mode 100644 index 00000000..fe255ed6 --- /dev/null +++ b/NewHorizons/Patches/CreditsScene/CreditsEntryPatches.cs @@ -0,0 +1,33 @@ +using HarmonyLib; +using NewHorizons.Utility; +using System; + +namespace NewHorizons.Patches.CreditsScene +{ + [HarmonyPatch] + public static class CreditsEntryPatches + { + [HarmonyPrefix] + [HarmonyPatch(typeof(CreditsEntry), nameof(CreditsEntry.SetContents))] + public static bool CreditsEntry_SetContents(CreditsEntry __instance, string[] __0) + { + var columnTexts = __0; + + for (int i = 0; i < __instance._columns.Length; i++) + { + // Base method throws out of bounds exception sometimes (_columns length doesn't match columnTexts length) + // Trim also NREs sometimes because of the TrimHelper class Mobius has idk + try + { + __instance._columns[i].text = columnTexts[i].Trim(); + } + catch + { + // Error occurs when column 2 is empty + __instance._columns[i].text = " "; + } + } + return false; + } + } +} diff --git a/NewHorizons/Patches/CreditsScene/CreditsPatches.cs b/NewHorizons/Patches/CreditsScene/CreditsPatches.cs new file mode 100644 index 00000000..724ef696 --- /dev/null +++ b/NewHorizons/Patches/CreditsScene/CreditsPatches.cs @@ -0,0 +1,17 @@ +using HarmonyLib; +using NewHorizons.Handlers; + +namespace NewHorizons.Patches.CreditsScene +{ + [HarmonyPatch] + public static class CreditsPatches + { + [HarmonyPrefix] + [HarmonyPatch(typeof(Credits), nameof(Credits.Start))] + public static void Credits_Start(Credits __instance) + { + CreditsHandler.AddCredits(__instance); + } + } +} + From dceea1d237b35b922ed6ae4d85b0e168bc49b214 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 13 Aug 2022 18:46:43 +0000 Subject: [PATCH 2/4] Updated Schemas --- NewHorizons/Schemas/addon_manifest_schema.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/NewHorizons/Schemas/addon_manifest_schema.json b/NewHorizons/Schemas/addon_manifest_schema.json index 4df4aa0f..bfe88b86 100644 --- a/NewHorizons/Schemas/addon_manifest_schema.json +++ b/NewHorizons/Schemas/addon_manifest_schema.json @@ -12,6 +12,13 @@ "$ref": "#/definitions/AchievementInfo" } }, + "credits": { + "type": "array", + "description": "Credits info for this mod. A list of contributors and their roles separated by #. For example: xen#New Horizons dev.", + "items": { + "type": "string" + } + }, "$schema": { "type": "string", "description": "The schema to validate with" From 7217946ab85bfbf7016cacebfb08566232013d4f Mon Sep 17 00:00:00 2001 From: Nick Date: Sun, 14 Aug 2022 13:10:12 -0400 Subject: [PATCH 3/4] Remove unused code, separate fade and scroll credits --- NewHorizons/Handlers/CreditsHandler.cs | 82 ++++++++++++++++---------- 1 file changed, 51 insertions(+), 31 deletions(-) diff --git a/NewHorizons/Handlers/CreditsHandler.cs b/NewHorizons/Handlers/CreditsHandler.cs index 8b026921..13161548 100644 --- a/NewHorizons/Handlers/CreditsHandler.cs +++ b/NewHorizons/Handlers/CreditsHandler.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Xml; using UnityEngine; +using static RumbleManager.Rumble; using Logger = NewHorizons.Utility.Logger; namespace NewHorizons.Handlers @@ -43,12 +44,12 @@ namespace NewHorizons.Handlers // Fade credits look wrong right now bc it works with just one column not two /* - var nodeFade = CreateCreditsFromList(xml, sectionName, entries, true); + var nodeFade = CreateFadeCreditsFromList(xml, sectionName, entries, true); finalCredits.InsertAfter(nodeFade, finalCredits.ChildNodes[0]); */ var fastCredits = NodeWhere(finalCredits.ChildNodes, "MainScrollSection"); - var nodeScroll = CreateCreditsFromList(xml, sectionName, entries, false); + var nodeScroll = CreateScrollCreditsFromList(xml, sectionName, entries); fastCredits.InsertBefore(nodeScroll, fastCredits.ChildNodes[0]); } @@ -65,9 +66,9 @@ namespace NewHorizons.Handlers return null; } - private static XmlNode CreateCreditsFromList(XmlDocument doc, string title, string[] entries, bool fade) + private static XmlNode CreateFadeCreditsFromList(XmlDocument doc, string title, string[] entries) { - var rootSection = MakeNode(doc, "section", fade ? new Dictionary() + var rootSection = MakeNode(doc, "section", new Dictionary() { { "platform", "All" }, { "type", "Fade" }, @@ -77,18 +78,11 @@ namespace NewHorizons.Handlers { "waitTime", "0.5" }, { "padding-bottom", "-8" }, { "spacing", "16" } - } : new Dictionary() - { - { "platform", "All" }, - { "type", "Scroll" }, - { "scrollDuration", "214" }, - { "spacing", "12" }, - { "width", "1590" } }); var titleLayout = MakeNode(doc, "layout", new Dictionary() { - { "type", fade ? "SingleColumnFadeCentered" : "SingleColumnScrollCentered" } + { "type", "SingleColumnFadeCentered" } }); var titleNode = MakeNode(doc, "title", new Dictionary() @@ -99,20 +93,58 @@ namespace NewHorizons.Handlers titleNode.InnerText = title; titleLayout.AppendChild(titleNode); - var type = fade ? "SingleColumnFadeCentered" : "TwoColumnScrollAlignRightLeft"; + var type = "SingleColumnFadeCentered"; var xmlText = $"\n"; for (int i = 0; i < entries.Length; i++) { var entry = entries[i]; - if (fade) entry = entry.Split('#')[0]; + entry = entry.Split('#')[0]; xmlText += $"{entry}\n"; - if (fade) - { - if (i == entries.Length - 1) xmlText += "\n"; - else xmlText += "\n"; - } + xmlText += "\n"; } + xmlText += "\n"; + xmlText += ""; + + rootSection.AppendChild(titleLayout); + rootSection.AppendChild(StringToNode(doc, xmlText)); + + return rootSection; + } + + private static XmlNode CreateScrollCreditsFromList(XmlDocument doc, string title, string[] entries) + { + var rootSection = MakeNode(doc, "section", new Dictionary() + { + { "platform", "All" }, + { "type", "Scroll" }, + { "scrollDuration", "214" }, + { "spacing", "12" }, + { "width", "1590" } + }); + + var titleLayout = MakeNode(doc, "layout", new Dictionary() + { + { "type", "SingleColumnScrollCentered" } + }); + + var titleNode = MakeNode(doc, "title", new Dictionary() + { + {"text-align", "UpperCenter" }, + {"height", "122" } + }); + titleNode.InnerText = title; + titleLayout.AppendChild(titleNode); + + var type = "TwoColumnScrollAlignRightLeft"; + var xmlText = $"\n"; + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + + xmlText += $"{entry}\n"; + } + xmlText += "\n"; xmlText += ""; rootSection.AppendChild(titleLayout); @@ -148,17 +180,5 @@ namespace NewHorizons.Handlers return xmlNode; } - - internal class CreditsInfo - { - string sectionName; - string[] entries; - - internal CreditsInfo(string sectionName, string[] entries) - { - this.sectionName = sectionName; - this.entries = entries; - } - } } } From 268437dfc5c6f4611760b460a9e19dddecfc193d Mon Sep 17 00:00:00 2001 From: Nick Date: Sun, 14 Aug 2022 14:29:23 -0400 Subject: [PATCH 4/4] Finalize credits #246 --- NewHorizons/Assets/addon-manifest.json | 5 ++- NewHorizons/Handlers/CreditsHandler.cs | 58 ++++++++++++++++++++------ NewHorizons/Main.cs | 4 +- 3 files changed, 50 insertions(+), 17 deletions(-) diff --git a/NewHorizons/Assets/addon-manifest.json b/NewHorizons/Assets/addon-manifest.json index bf105729..4a7dd87c 100644 --- a/NewHorizons/Assets/addon-manifest.json +++ b/NewHorizons/Assets/addon-manifest.json @@ -9,9 +9,10 @@ "Trifid#Tester\n#Programmer", "Nageld#Programmer", "Ernesto#Fish", - "with help from#Raicuparta\n#dgarroDC\n#jtsalomo\n#and the modding community", + "With help from#Raicuparta\n#dgarroDC\n#jtsalomo\n#and the modding community", + " ", "Based off Marshmallow made by#Mister_Nebula", - "with help from#AmazingAlek\n#Raicuparta\n#and the Outer Wilds discord server", + "With help from#AmazingAlek\n#Raicuparta\n#and the Outer Wilds discord server", " ", "This work is unofficial Fan Content" ] diff --git a/NewHorizons/Handlers/CreditsHandler.cs b/NewHorizons/Handlers/CreditsHandler.cs index 13161548..9d7e7405 100644 --- a/NewHorizons/Handlers/CreditsHandler.cs +++ b/NewHorizons/Handlers/CreditsHandler.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.Xml; using UnityEngine; -using static RumbleManager.Rumble; using Logger = NewHorizons.Utility.Logger; namespace NewHorizons.Handlers @@ -42,9 +41,9 @@ namespace NewHorizons.Handlers { var finalCredits = xml.SelectSingleNode("Credits/section"); - // Fade credits look wrong right now bc it works with just one column not two /* - var nodeFade = CreateFadeCreditsFromList(xml, sectionName, entries, true); + * Looks bad, would need more customization, complicated, messes up music timing, wont do for now + var nodeFade = CreateFadeCreditsFromList(xml, sectionName, entries); finalCredits.InsertAfter(nodeFade, finalCredits.ChildNodes[0]); */ @@ -66,6 +65,8 @@ namespace NewHorizons.Handlers return null; } + // Looked bad so not used + /* private static XmlNode CreateFadeCreditsFromList(XmlDocument doc, string title, string[] entries) { var rootSection = MakeNode(doc, "section", new Dictionary() @@ -98,7 +99,16 @@ namespace NewHorizons.Handlers for (int i = 0; i < entries.Length; i++) { var entry = entries[i]; - entry = entry.Split('#')[0]; + + if (entry.Contains("#")) + { + // Replace first one with a space + entry = RemoveExcessSpaces(entry); + var indexOfColon = entry.IndexOf(":"); + var firstPart = entry.Substring(0, Math.Min(entry.IndexOf("#"), indexOfColon == -1 ? int.MaxValue : indexOfColon)); + entry = firstPart + ": " + entry.Substring(entry.IndexOf("#") + 1); + } + entry = entry.Replace("#", ", ").Replace("/n", ""); xmlText += $"{entry}\n"; xmlText += "\n"; @@ -107,11 +117,19 @@ namespace NewHorizons.Handlers xmlText += ""; rootSection.AppendChild(titleLayout); - rootSection.AppendChild(StringToNode(doc, xmlText)); + foreach (var node in StringToNodes(doc, xmlText)) rootSection.AppendChild(node); return rootSection; } + private static string RemoveExcessSpaces(string s) + { + var options = RegexOptions.None; + Regex regex = new Regex("[ ]{2,}", options); + return regex.Replace(s, " "); + } + */ + private static XmlNode CreateScrollCreditsFromList(XmlDocument doc, string title, string[] entries) { var rootSection = MakeNode(doc, "section", new Dictionary() @@ -136,32 +154,48 @@ namespace NewHorizons.Handlers titleNode.InnerText = title; titleLayout.AppendChild(titleNode); - var type = "TwoColumnScrollAlignRightLeft"; - var xmlText = $"\n"; + + var xmlText = ""; + bool? flag = null; for (int i = 0; i < entries.Length; i++) { var entry = entries[i]; + var twoColumn = entry.Contains("#"); + if (flag != twoColumn) + { + if (i != 0) xmlText += ""; + var type = twoColumn ? "TwoColumnScrollAlignRightLeft" : "SingleColumnScrollCentered"; + xmlText += $"\n"; + flag = twoColumn; + } + xmlText += $"{entry}\n"; + xmlText += ""; } xmlText += "\n"; xmlText += ""; rootSection.AppendChild(titleLayout); - rootSection.AppendChild(StringToNode(doc, xmlText)); + foreach(var node in StringToNodes(doc, xmlText)) rootSection.AppendChild(node); return rootSection; } - private static XmlNode StringToNode(XmlDocument docContext, string text) + private static XmlNode[] StringToNodes(XmlDocument docContext, string text) { var doc = new XmlDocument(); - doc.LoadXml(text); + // Doing this funny thing so that theres a single parent root thing + doc.LoadXml("" + text + ""); // ArgumentException: The node to be inserted is from a different document context. - var importedNode = docContext.ImportNode(doc.DocumentElement, true); + var nodes = new List(); + foreach (XmlNode node in doc.DocumentElement.ChildNodes) + { + nodes.Add(docContext.ImportNode(node, true)); + } - return importedNode; + return nodes.ToArray(); } private static XmlNode MakeNode(XmlDocument doc, string nodeType, Dictionary attributes) diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index 75ad2e23..aafed90f 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -525,12 +525,10 @@ namespace NewHorizons private void LoadAddonManifest(string file, IModBehaviour mod) { - Logger.LogError($"{mod.ModHelper.Manifest.Name}"); + Logger.LogVerbose($"Loading addon manifest for {mod.ModHelper.Manifest.Name}"); var addonConfig = mod.ModHelper.Storage.Load(file); - - if (addonConfig.achievements != null) AchievementHandler.RegisterAddon(addonConfig, mod as ModBehaviour); if (addonConfig.credits != null) CreditsHandler.RegisterCredits(mod.ModHelper.Manifest.Name, addonConfig.credits); }