Decompiled source of TerminalScrollPatch v1.0.0

TerminalScrollPatch.dll

Decompiled a day ago
using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("TerminalRelativeScroll")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("TerminalRelativeScroll")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("19e0a1ba-5c2c-4d94-99f0-91c651f9621e")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace TerminalRelativeScroll;

[BepInPlugin("brabru.TerminalScrollPatch", "Terminal Scroll Patch", "1.0.0")]
[BepInProcess("Lethal Company.exe")]
public class Plugin : BaseUnityPlugin
{
	internal static Plugin Instance;

	internal static PluginSettings Settings;

	internal static ManualLogSource StaticLogger;

	private void Awake()
	{
		Instance = this;
		StaticLogger = ((BaseUnityPlugin)this).Logger;
		Settings = new PluginSettings(((BaseUnityPlugin)this).Config);
		StaticLogger.LogInfo((object)$"Config: CustomScroll={Settings.RelativeScrollEnabled.Value}, LinesToScroll={Settings.LinesToScroll.Value}");
		Harmony.CreateAndPatchAll(typeof(TerminalScrollPatch), (string)null);
		((BaseUnityPlugin)this).Logger.LogInfo((object)"TerminalScrollPatch mod loaded (v81)");
	}
}
public class PluginSettings
{
	public ConfigEntry<bool> RelativeScrollEnabled { get; }

	public ConfigEntry<int> LinesToScroll { get; }

	public PluginSettings(ConfigFile config)
	{
		RelativeScrollEnabled = config.Bind<bool>("General", "CustomScrollEnabled", true, "If true, terminal scrolling is relative to line count. If false, vanilla behavior.");
		LinesToScroll = config.Bind<int>("General", "LinesToScroll", 8, "Number of lines to scroll when CustomScrollEnabled is true.");
	}
}
[HarmonyPatch]
internal class TerminalScrollPatch
{
	private static string cachedText;

	private static float scrollAmount;

	private static float originalScrollbarValue;

	private static float currentScrollDelta;

	private static bool shouldOverride;

	private static FieldInfo terminalField;

	private static bool terminalFieldFromStartOfRound;

	static TerminalScrollPatch()
	{
		cachedText = "";
		scrollAmount = 1f / 3f;
		terminalField = AccessTools.GetDeclaredFields(typeof(PlayerControllerB)).FirstOrDefault((FieldInfo f) => f.FieldType == typeof(Terminal));
		if (terminalField == null)
		{
			terminalField = AccessTools.GetDeclaredFields(typeof(StartOfRound)).FirstOrDefault((FieldInfo f) => f.FieldType == typeof(Terminal));
			terminalFieldFromStartOfRound = true;
		}
		else
		{
			terminalFieldFromStartOfRound = false;
		}
		if (terminalField != null)
		{
			Plugin.StaticLogger.LogInfo((object)("TerminalScrollPatch: found Terminal field '" + terminalField.Name + "' in " + terminalField.DeclaringType.Name));
		}
		else
		{
			Plugin.StaticLogger.LogWarning((object)"TerminalScrollPatch: no Terminal field found! Will fallback to FindObjectOfType.");
		}
	}

	[HarmonyTargetMethod]
	public static MethodBase TargetMethod()
	{
		string[] obj = new string[3] { "ScrollMouse_performed", "UpdateTerminalMouseScroll", "TerminalScroll_performed" };
		Type typeFromHandle = typeof(PlayerControllerB);
		string[] array = obj;
		foreach (string text in array)
		{
			MethodInfo methodInfo = AccessTools.Method(typeFromHandle, text, new Type[1] { typeof(CallbackContext) }, (Type[])null);
			if (methodInfo != null)
			{
				Plugin.StaticLogger.LogInfo((object)("TerminalScrollPatch: method found -> " + methodInfo.DeclaringType?.Name + "." + methodInfo.Name));
				return methodInfo;
			}
		}
		Plugin.StaticLogger.LogError((object)"TerminalScrollPatch: no terminal scroll method found! Mod will not work.");
		return null;
	}

	[HarmonyPrefix]
	private static void Prefix(PlayerControllerB __instance, CallbackContext context)
	{
		Plugin.StaticLogger.LogDebug((object)"Prefix: Entered");
		currentScrollDelta = ((CallbackContext)(ref context)).ReadValue<float>();
		Plugin.StaticLogger.LogDebug((object)$"Prefix: scrollDelta = {currentScrollDelta}");
		if (Mathf.Approximately(currentScrollDelta, 0f))
		{
			shouldOverride = false;
			Plugin.StaticLogger.LogDebug((object)"Prefix: scrollDelta zero, setting shouldOverride=false");
			return;
		}
		if ((Object)(object)__instance.terminalScrollVertical != (Object)null)
		{
			originalScrollbarValue = __instance.terminalScrollVertical.value;
			Plugin.StaticLogger.LogDebug((object)$"Prefix: originalScrollbarValue = {originalScrollbarValue}");
		}
		else
		{
			Plugin.StaticLogger.LogWarning((object)"Prefix: terminalScrollVertical is null!");
		}
		Terminal terminal = GetTerminal(__instance);
		bool inTerminalMenu = __instance.inTerminalMenu;
		bool flag = Plugin.Settings != null;
		bool flag2 = flag && Plugin.Settings.RelativeScrollEnabled.Value;
		bool flag3 = (Object)(object)terminal != (Object)null;
		Plugin.StaticLogger.LogDebug((object)$"Prefix: inTerminalMenu={inTerminalMenu}, settingsExist={flag}, relativeEnabled={flag2}, terminalExists={flag3}");
		shouldOverride = inTerminalMenu && flag && flag2 && flag3;
		Plugin.StaticLogger.LogDebug((object)$"Prefix: shouldOverride = {shouldOverride}");
	}

	[HarmonyPostfix]
	private static void Postfix(PlayerControllerB __instance)
	{
		Plugin.StaticLogger.LogDebug((object)"Postfix: Entered");
		if (!shouldOverride)
		{
			Plugin.StaticLogger.LogDebug((object)"Postfix: shouldOverride false, exiting");
			return;
		}
		Scrollbar terminalScrollVertical = __instance.terminalScrollVertical;
		if ((Object)(object)terminalScrollVertical == (Object)null)
		{
			Plugin.StaticLogger.LogWarning((object)"Postfix: scrollbar is null");
			return;
		}
		Terminal terminal = GetTerminal(__instance);
		if ((Object)(object)terminal == (Object)null)
		{
			Plugin.StaticLogger.LogWarning((object)"Postfix: terminal is null");
			return;
		}
		float num = Mathf.Sign(currentScrollDelta);
		Plugin.StaticLogger.LogDebug((object)$"Postfix: direction = {num}");
		string currentText = terminal.currentText;
		if (string.CompareOrdinal(currentText, cachedText) != 0)
		{
			cachedText = currentText;
			int num2 = cachedText.Count((char c) => c == '\n') + 1;
			scrollAmount = (float)Plugin.Settings.LinesToScroll.Value / (float)num2;
			Plugin.StaticLogger.LogInfo((object)$"Recalculated scroll: lines={num2}, scrollAmount={scrollAmount:F4}");
		}
		float num3 = originalScrollbarValue + num * scrollAmount;
		num3 = Mathf.Clamp01(num3);
		Plugin.StaticLogger.LogInfo((object)$"Postfix: original={originalScrollbarValue}, desired={num3}, delta={num * scrollAmount}");
		terminalScrollVertical.value = num3;
	}

	private static Terminal GetTerminal(PlayerControllerB player)
	{
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Expected O, but got Unknown
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Expected O, but got Unknown
		if (terminalField != null)
		{
			if (!terminalFieldFromStartOfRound)
			{
				return (Terminal)terminalField.GetValue(player);
			}
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance != (Object)null)
			{
				return (Terminal)terminalField.GetValue(instance);
			}
		}
		return Object.FindObjectOfType<Terminal>();
	}
}