Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for '@' display to TextScroller #47

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net35</TargetFramework>
<AssemblyName>BepInEx5Plugins.Ash.Alisa.TextScrolling</AssemblyName>
<Description>TextScrolling</Description>
<Version>1.0.0</Version>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>9.0</LangVersion>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BepInEx.Analyzers" Version="1.*" PrivateAssets="all" />
<PackageReference Include="BepInEx.Core" Version="5.*" />
<PackageReference Include="BepInEx.PluginInfoProps" Version="1.*" />
<PackageReference Include="UnityEngine.Modules" Version="5.6.7" IncludeAssets="compile" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework.TrimEnd(`0123456789`))' == 'net'">
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
</ItemGroup>

<ItemGroup>
<Reference Include="Assembly-CSharp">
<HintPath>..\lib\SteamRelease\Assembly-CSharp.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine.UI">
<HintPath>..\lib\UnityEngine\UnityEngine.UI.dll</HintPath>
<Private>false</Private>
</Reference>
</ItemGroup>

<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="xcopy /Y /Q /C /I &quot;$(OutDir)&quot; &quot;..\$(OutDir)\$(TargetName)\&quot;" />
</Target>
</Project>
Original file line number Diff line number Diff line change
@@ -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<Interaction>();

if (interaction)
{
interaction.interactionIsActive = true;
}

if (__instance.isScrollingText)
{
__instance.StartCoroutine(TextBoxManager_Update.TextScroll(__instance, __instance.textLines[__instance.currentLine]));
}

return false;
}
}
}
Original file line number Diff line number Diff line change
@@ -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>())
{
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>|<color=(""?[a-zA-Z]+""?|#[0-9a-fA-F]{8})>)");
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("</color>"))
{
hasColor = false;
letter += "</color>".Length;
sub = lineOfText.Substring(letter);

textBox.theText.text = lineOfText.Substring(0, letter) + "<color=#00000000>" + colorTagRegex.Replace(sub, "") + "</color>";
}
else if (sub.StartsWith("<color"))
{
var match = colorTagRegex.Match(lineOfText, letter);

hasColor = true;
letter += match.Value.Length;
sub = lineOfText.Substring(letter);

textBox.theText.text = lineOfText.Substring(0, letter) + "</color>" + "<color=#00000000>" + colorTagRegex.Replace(sub, "") + "</color>";
}
}
else
{
if (!hasColor)
{
textBox.theText.text = lineOfText.Substring(0, letter) + "<color=#00000000>" + colorTagRegex.Replace(lineOfText.Substring(letter), "") + "</color>";
}
else
{
textBox.theText.text = lineOfText.Substring(0, letter) + "</color>" + "<color=#00000000>" + colorTagRegex.Replace(lineOfText.Substring(letter), "") + "</color>";
}

++letter;
}

yield return new WaitForSeconds(typeSpeed);
}

textBox.theText.text = lineOfText;
textBox.isTyping = false;
TextBoxManager_cancelTyping.SetValue(textBox, false);
}
}
}
6 changes: 6 additions & 0 deletions BepInEx5Plugins.Ash.Alisa.TextScrolling/NuGet.Config
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="BepInEx" value="https://nuget.bepinex.dev/v3/index.json" />
</packageSources>
</configuration>
60 changes: 60 additions & 0 deletions BepInEx5Plugins.Ash.Alisa.TextScrolling/Plugin.cs
Original file line number Diff line number Diff line change
@@ -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<float> typeSpeed;

private readonly ConfigEntry<bool> allowEscapedLineBreak;

private readonly ConfigEntry<string> lineBreakReplacement;

private readonly ConfigEntry<bool> 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);
}
}
}
}