diff --git a/BepInEx5Plugins.Ash.Alisa.TextScrolling/BepInEx5Plugins.Ash.Alisa.TextScrolling.csproj b/BepInEx5Plugins.Ash.Alisa.TextScrolling/BepInEx5Plugins.Ash.Alisa.TextScrolling.csproj new file mode 100644 index 0000000..6ced828 --- /dev/null +++ b/BepInEx5Plugins.Ash.Alisa.TextScrolling/BepInEx5Plugins.Ash.Alisa.TextScrolling.csproj @@ -0,0 +1,37 @@ + + + + net35 + BepInEx5Plugins.Ash.Alisa.TextScrolling + TextScrolling + 1.0.0 + true + 9.0 + + + + + + + + + + + + + + + + ..\lib\SteamRelease\Assembly-CSharp.dll + false + + + ..\lib\UnityEngine\UnityEngine.UI.dll + false + + + + + + + diff --git a/BepInEx5Plugins.Ash.Alisa.TextScrolling/HarmonyPatches/TextBoxManager_EnableTextBox.cs b/BepInEx5Plugins.Ash.Alisa.TextScrolling/HarmonyPatches/TextBoxManager_EnableTextBox.cs new file mode 100644 index 0000000..1dce498 --- /dev/null +++ b/BepInEx5Plugins.Ash.Alisa.TextScrolling/HarmonyPatches/TextBoxManager_EnableTextBox.cs @@ -0,0 +1,32 @@ +using HarmonyLib; +using Object = UnityEngine.Object; + +namespace BepInEx5Plugins.Ash.Alisa.TextScrolling.HarmonyPatches +{ + [HarmonyPatch(typeof(TextBoxManager), "EnableTextBox")] + public class TextBoxManager_EnableTextBox + { + // Replace call to TextScroll. + public static bool Prefix(TextBoxManager __instance + , ref bool ___cantSkip + ) + { + ___cantSkip = true; + __instance.isActive = true; + + var interaction = Object.FindObjectOfType(); + + if (interaction) + { + interaction.interactionIsActive = true; + } + + if (__instance.isScrollingText) + { + __instance.StartCoroutine(TextBoxManager_Update.TextScroll(__instance, __instance.textLines[__instance.currentLine])); + } + + return false; + } + } +} diff --git a/BepInEx5Plugins.Ash.Alisa.TextScrolling/HarmonyPatches/TextBoxManager_Update.cs b/BepInEx5Plugins.Ash.Alisa.TextScrolling/HarmonyPatches/TextBoxManager_Update.cs new file mode 100644 index 0000000..555b301 --- /dev/null +++ b/BepInEx5Plugins.Ash.Alisa.TextScrolling/HarmonyPatches/TextBoxManager_Update.cs @@ -0,0 +1,227 @@ +using HarmonyLib; +using System; +using System.Collections; +using System.Reflection; +using System.Text.RegularExpressions; +using UnityEngine; +using UnityEngine.UI; + +namespace BepInEx5Plugins.Ash.Alisa.TextScrolling.HarmonyPatches +{ + [HarmonyPatch(typeof(TextBoxManager), "Update")] + public class TextBoxManager_Update + { + public static float typeSpeed = 1f; + + public static bool allowEscapedLineBreak = false; + + public static bool stripLineBreaks = false; + + public static string lineBreakReplacement = " "; + + + private static FieldInfo TextBoxManager_cancelTyping; + + private static FieldInfo TextBoxManager_cantSkip; + + private static FieldInfo TextBoxManager_skipTimer; + + private static FieldInfo TextBoxManager_typeSpeed; + + private static MethodInfo TextBoxManager_TextLanguage; + + public static bool Prepare(MethodBase original) + { + if (original is null) + { + try + { + TextBoxManager_cancelTyping = typeof(TextBoxManager).GetField("cancelTyping", BindingFlags.NonPublic | BindingFlags.Instance); + TextBoxManager_cantSkip = typeof(TextBoxManager).GetField("cantSkip", BindingFlags.NonPublic | BindingFlags.Instance); + TextBoxManager_skipTimer = typeof(TextBoxManager).GetField("skipTimer", BindingFlags.NonPublic | BindingFlags.Instance); + TextBoxManager_typeSpeed = typeof(TextBoxManager).GetField("typeSpeed", BindingFlags.NonPublic | BindingFlags.Instance); + TextBoxManager_TextLanguage = typeof(TextBoxManager).GetMethod("TextLanguage", BindingFlags.NonPublic | BindingFlags.Instance); + } + catch (Exception exception) + { + Console.WriteLine(exception); + return false; + } + } + + return true; + } + + // Replace call to TextScroll. + private static bool Prefix(TextBoxManager __instance + , ref Interaction ___interaction + , ref Settings ___settings + ) + { + TextBoxManager_TextLanguage.Invoke(__instance, null); + + if (!__instance.isActive) + { + return false; + } + + if (!__instance.isScrollingText) + { + __instance.theText.text = __instance.textLines[__instance.currentLine]; + + if (!__instance.isSubtitles + && (Input.GetKeyDown(___settings.keys["Action"]) + || Input.GetKeyDown(___settings.keys["Cancel"])) + && !(bool)TextBoxManager_cantSkip.GetValue(__instance)) + { + TextBoxManager_cantSkip.SetValue(__instance, true); + ++__instance.currentLine; + } + + if (__instance.currentLine > __instance.endAtLine) + { + __instance.DisableTextBox(); + } + } + + if (__instance.isScrollingText + && (Input.GetKeyDown(___settings.keys["Action"]) + || Input.GetKeyDown(___settings.keys["Cancel"])) + ) + { + if (!__instance.isTyping) + { + TextBoxManager_cantSkip.SetValue(__instance, true); + ++__instance.currentLine; + + if (__instance.currentLine > __instance.endAtLine) + { + __instance.DisableTextBox(); + } + else + { + __instance.StartCoroutine(TextScroll(__instance, __instance.textLines[__instance.currentLine])); + } + } + else if (__instance.isTyping && !(bool)TextBoxManager_cancelTyping.GetValue(__instance)) + { + TextBoxManager_cancelTyping.SetValue(__instance, true); + } + } + + if (___interaction.watchingDoor) + { + __instance.watchDoor = true; + } + else + { + __instance.watchDoor = false; + } + + if ((bool)TextBoxManager_cantSkip.GetValue(__instance)) + { + TextBoxManager_skipTimer.SetValue(__instance, (float)TextBoxManager_skipTimer.GetValue(__instance) + Time.deltaTime); + + if ((float)TextBoxManager_skipTimer.GetValue(__instance) > 0.4f) + { + TextBoxManager_cantSkip.SetValue(__instance, false); + TextBoxManager_skipTimer.SetValue(__instance, 0f); + } + } + + return false; + } + + // Add support for word breaks. + // Add support for color tags. + // Add support for custom line replacement. + public static IEnumerator TextScroll(TextBoxManager textBox, string lineOfText) + { + foreach (var outline in textBox.theText.GetComponents()) + { + outline.useGraphicAlpha = true; + } + + textBox.isTyping = true; + TextBoxManager_cancelTyping.SetValue(textBox, false); + +#pragma warning disable Harmony003 // Harmony non-ref patch parameters modified + var br = (string)null; + + if (stripLineBreaks) + { + br = lineBreakReplacement; + } + else + { + br = "\n"; + } + + if (allowEscapedLineBreak) + { + lineOfText = lineOfText + .Replace("\\@", "\0") + .Replace("@", br) + .Replace("\0", "@"); + } + else + { + lineOfText = lineOfText.Replace("@", br); + } +#pragma warning restore Harmony003 // Harmony non-ref patch parameters modified + + var typeSpeed = (float)TextBoxManager_typeSpeed.GetValue(textBox) * TextBoxManager_Update.typeSpeed; + var colorTagRegex = new Regex(@"(<\/color>|)"); + var hasColor = false; + var letter = 1; + + while (textBox.isTyping + && !(bool)TextBoxManager_cancelTyping.GetValue(textBox) + && letter < lineOfText.Length - 1) + { + if (lineOfText[letter] == '<') + { + var sub = lineOfText.Substring(letter); + + if (sub.StartsWith("")) + { + hasColor = false; + letter += "".Length; + sub = lineOfText.Substring(letter); + + textBox.theText.text = lineOfText.Substring(0, letter) + "" + colorTagRegex.Replace(sub, "") + ""; + } + else if (sub.StartsWith("" + "" + colorTagRegex.Replace(sub, "") + ""; + } + } + else + { + if (!hasColor) + { + textBox.theText.text = lineOfText.Substring(0, letter) + "" + colorTagRegex.Replace(lineOfText.Substring(letter), "") + ""; + } + else + { + textBox.theText.text = lineOfText.Substring(0, letter) + "" + "" + colorTagRegex.Replace(lineOfText.Substring(letter), "") + ""; + } + + ++letter; + } + + yield return new WaitForSeconds(typeSpeed); + } + + textBox.theText.text = lineOfText; + textBox.isTyping = false; + TextBoxManager_cancelTyping.SetValue(textBox, false); + } + } +} diff --git a/BepInEx5Plugins.Ash.Alisa.TextScrolling/NuGet.Config b/BepInEx5Plugins.Ash.Alisa.TextScrolling/NuGet.Config new file mode 100644 index 0000000..1864ded --- /dev/null +++ b/BepInEx5Plugins.Ash.Alisa.TextScrolling/NuGet.Config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/BepInEx5Plugins.Ash.Alisa.TextScrolling/Plugin.cs b/BepInEx5Plugins.Ash.Alisa.TextScrolling/Plugin.cs new file mode 100644 index 0000000..55ec853 --- /dev/null +++ b/BepInEx5Plugins.Ash.Alisa.TextScrolling/Plugin.cs @@ -0,0 +1,60 @@ +using BepInEx; +using BepInEx.Configuration; +using HarmonyLib; +using System; + +namespace BepInEx5Plugins.Ash.Alisa.TextScrolling +{ + [BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)] + public class Plugin : BaseUnityPlugin + { + private readonly ConfigEntry typeSpeed; + + private readonly ConfigEntry allowEscapedLineBreak; + + private readonly ConfigEntry lineBreakReplacement; + + private readonly ConfigEntry stripLineBreaks; + + private Plugin() + { + typeSpeed = Config.Bind("TextScrolling", "Type Speed", 1f, "Multiply the speed of the type writer animation by this amount."); + allowEscapedLineBreak = Config.Bind("TextScrolling", "Allow Escaped Line Break", true, "If enabled, convert \\@ to @."); + stripLineBreaks = Config.Bind("TextScrolling", "Strip Line Breaks", false, "If enabled, remove manually inserted line breaks."); + lineBreakReplacement = Config.Bind("TextScrolling", "Line Break Replacement", " ", "If 'Strip Line Breaks' is true, replace line breaks with this value."); + + Config.SettingChanged += Config_SettingChanged; + + ApplySettings(); + } + + private void Config_SettingChanged(object sender, EventArgs e) + { + ApplySettings(); + } + + private void ApplySettings() + { + HarmonyPatches.TextBoxManager_Update.typeSpeed = typeSpeed.Value; + HarmonyPatches.TextBoxManager_Update.allowEscapedLineBreak = allowEscapedLineBreak.Value; + HarmonyPatches.TextBoxManager_Update.stripLineBreaks = stripLineBreaks.Value; + HarmonyPatches.TextBoxManager_Update.lineBreakReplacement = lineBreakReplacement.Value; + } + + private void Awake() + { + try + { + Logger.LogInfo($"Plugin {PluginInfo.PLUGIN_GUID} is loaded!"); + + var harmony = new Harmony(Info.Metadata.GUID); + + harmony.PatchAll(); + } + catch (Exception exception) + { + Console.WriteLine(exception); + } + } + } +}