Decompiled source of ImpostorMod v1.3.0

LethalAPI.Terminal.dll

Decompiled 4 months ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using LethalAPI.LibTerminal.Attributes;
using LethalAPI.LibTerminal.Commands;
using LethalAPI.LibTerminal.Interfaces;
using LethalAPI.LibTerminal.Models;
using LethalAPI.LibTerminal.Models.Enums;
using LethalAPI.LibTerminal.Patches;
using Microsoft.CodeAnalysis;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Video;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("ShimmyMySherbet, LethalAPI Modding Team")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("A library that allows the creation of custom terminal commands for Lethal Company mods")]
[assembly: AssemblyFileVersion("1.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+ca75a0c8c866f23427b380e2a531bbae90b0e939")]
[assembly: AssemblyProduct("LethalAPI.Terminal")]
[assembly: AssemblyTitle("LethalAPI.Terminal")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/LethalCompany/LethalAPI.Terminal")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace LethalAPI.LibTerminal
{
	[BepInPlugin("LethalAPI.Terminal", "LethalAPI.Terminal", "1.0.0")]
	internal class LibTerminalPlugin : BaseUnityPlugin
	{
		private readonly Harmony m_Harmony = new Harmony("LethalAPI.Terminal");

		private TerminalModRegistry? m_Registry;

		private void Awake()
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)"LethalAPI.Terminal is loading...");
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Installing patches");
			m_Harmony.PatchAll(typeof(LibTerminalPlugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Registering built-in Commands");
			m_Registry = TerminalRegistry.CreateTerminalRegistry();
			m_Registry.RegisterFrom<CommandInfoCommands>();
			Object.DontDestroyOnLoad((Object)(object)this);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Plugin LethalAPI.Terminal is loaded!");
		}
	}
	public static class TerminalExtensions
	{
		public static void PlayVideoFile(this Terminal terminal, string filePath)
		{
			string url = "file:///" + filePath.Replace('\\', '/');
			terminal.PlayVideoLink(url);
		}

		public static void PlayVideoLink(this Terminal terminal, Uri url)
		{
			((MonoBehaviour)terminal).StartCoroutine(PlayVideoLink(url.AbsoluteUri, terminal));
		}

		public static void PlayVideoLink(this Terminal terminal, string url)
		{
			((MonoBehaviour)terminal).StartCoroutine(PlayVideoLink(url, terminal));
		}

		private static IEnumerator PlayVideoLink(string url, Terminal terminal)
		{
			yield return (object)new WaitForFixedUpdate();
			((Behaviour)terminal.terminalImage).enabled = true;
			terminal.terminalImage.texture = (Texture)(object)terminal.videoTexture;
			terminal.displayingPersistentImage = null;
			terminal.videoPlayer.clip = null;
			terminal.videoPlayer.source = (VideoSource)1;
			terminal.videoPlayer.url = url;
			((Behaviour)terminal.videoPlayer).enabled = true;
		}

		public static TerminalNode? GetLastLoadedNode()
		{
			return LoadNewNodePatch.LastLoadedNode;
		}

		public static TerminalNode? GetLastLoadedNode(this Terminal terminal)
		{
			return LoadNewNodePatch.LastLoadedNode ?? terminal.currentNode;
		}
	}
	public static class TerminalNodeExtensions
	{
		public static TerminalNode WithDisplayText(this TerminalNode node, object? displayText)
		{
			node.displayText = displayText?.ToString() ?? string.Empty;
			return node;
		}

		public static TerminalNode WithDisplayTexture(this TerminalNode node, Texture texture)
		{
			node.displayTexture = texture;
			return node;
		}

		public static TerminalNode WithVideoClip(this TerminalNode node, VideoClip video)
		{
			node.displayVideo = video;
			return node;
		}

		public static TerminalNode WithAcceptsAnything(this TerminalNode node)
		{
			node.acceptAnything = true;
			return node;
		}

		public static TerminalNode WithBuyItemIndex(this TerminalNode node, int index)
		{
			node.buyItemIndex = index;
			return node;
		}

		public static TerminalNode WithMoonIndex(this TerminalNode node, int index)
		{
			node.buyRerouteToMoon = index;
			return node;
		}

		public static TerminalNode ClearText(this TerminalNode node)
		{
			node.clearPreviousText = true;
			return node;
		}

		public static TerminalNode WithItemCost(this TerminalNode node, int cost)
		{
			node.itemCost = cost;
			return node;
		}

		public static TerminalNode LoadImageSlowly(this TerminalNode node)
		{
			node.loadImageSlowly = true;
			return node;
		}

		public static TerminalNode PersistImage(this TerminalNode node)
		{
			node.persistentImage = true;
			return node;
		}

		public static TerminalNode WithAudio(this TerminalNode node, AudioClip audio)
		{
			node.playClip = audio;
			return node;
		}

		public static TerminalNode WithTerminalEvent(this TerminalNode node, string eventName)
		{
			node.terminalEvent = eventName;
			return node;
		}

		public static TerminalNode ReturnItemFromStorage(this TerminalNode node, int unlockID)
		{
			node.shipUnlockableID = unlockID;
			node.returnFromStorage = true;
			return node;
		}

		public static TerminalNode PlayEffect(this TerminalNode node, SyncedTerminalClip clip)
		{
			node.playSyncedClip = (int)clip;
			return node;
		}

		public static TerminalNode PlayError(this TerminalNode node)
		{
			node.playSyncedClip = 1;
			return node;
		}
	}
	public class TerminalRegistry
	{
		private static readonly ConcurrentDictionary<string, List<TerminalCommand>> m_RegisteredCommands = new ConcurrentDictionary<string, List<TerminalCommand>>(StringComparer.InvariantCultureIgnoreCase);

		public static TerminalModRegistry RegisterFrom<T>(T instance) where T : class
		{
			TerminalModRegistry terminalModRegistry = new TerminalModRegistry();
			terminalModRegistry.RegisterFrom(instance);
			return terminalModRegistry;
		}

		public static TerminalModRegistry CreateTerminalRegistry()
		{
			return new TerminalModRegistry();
		}

		public static void RegisterCommand(TerminalCommand command)
		{
			if (!m_RegisteredCommands.TryGetValue(command.Name, out List<TerminalCommand> value))
			{
				value = new List<TerminalCommand>();
				m_RegisteredCommands[command.Name] = value;
			}
			lock (value)
			{
				value.Add(command);
			}
		}

		public static void Deregister(TerminalCommand command)
		{
			if (!m_RegisteredCommands.TryGetValue(command.Name, out List<TerminalCommand> value))
			{
				return;
			}
			lock (value)
			{
				value.Remove(command);
			}
		}

		public static IReadOnlyList<TerminalCommand> GetCommands(string commandName)
		{
			if (m_RegisteredCommands.TryGetValue(commandName, out List<TerminalCommand> value))
			{
				return value;
			}
			return new List<TerminalCommand>();
		}

		public static IEnumerable<TerminalCommand> EnumerateCommands(string name)
		{
			if (!m_RegisteredCommands.TryGetValue(name, out List<TerminalCommand> value))
			{
				return Enumerable.Empty<TerminalCommand>();
			}
			return value;
		}

		public static IEnumerable<TerminalCommand> EnumerateCommands()
		{
			string[] keys = m_RegisteredCommands.Keys.ToArray();
			for (int i = 0; i < keys.Length; i++)
			{
				List<TerminalCommand> overloads = m_RegisteredCommands[keys[i]];
				for (int c = 0; c < overloads.Count; c++)
				{
					yield return overloads[c];
				}
			}
		}

		public static IEnumerable<MethodInfo> GetCommandMethods<T>()
		{
			MethodInfo[] methods = typeof(T).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (methodInfo.GetCustomAttribute<TerminalCommandAttribute>() != null)
				{
					yield return methodInfo;
				}
			}
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "LethalAPI.Terminal";

		public const string PLUGIN_NAME = "LethalAPI.Terminal";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}
namespace LethalAPI.LibTerminal.Patches
{
	[HarmonyPatch(typeof(Terminal), "BeginUsingTerminal")]
	internal static class BeginUsingTerminalPatch
	{
		[HarmonyPostfix]
		public static void Postfix(Terminal __instance)
		{
			ITerminalInterface currentInterface = CommandHandler.CurrentInterface;
			if (currentInterface != null)
			{
				TerminalNode splashScreen = currentInterface.GetSplashScreen(__instance);
				if ((Object)(object)splashScreen != (Object)null)
				{
					__instance.LoadNewNode(splashScreen);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Terminal), "LoadNewNode")]
	internal static class LoadNewNodePatch
	{
		public static TerminalNode? LastLoadedNode { get; private set; }

		[HarmonyPrefix]
		public static void Prefix(TerminalNode node)
		{
			LastLoadedNode = node;
		}
	}
	[HarmonyPatch(typeof(Terminal), "ParsePlayerSentence")]
	internal static class ParseSentencePatch
	{
		[HarmonyPrefix]
		public static bool ParsePrefix(Terminal __instance, ref TerminalNode? __state)
		{
			__state = null;
			string command = __instance.screenText.text.Substring(__instance.screenText.text.Length - __instance.textAdded);
			__state = CommandHandler.HandleCommandInput(command, __instance);
			return (Object)(object)__state == (Object)null;
		}

		[HarmonyPostfix]
		public static TerminalNode? ParsePostfix(TerminalNode? __result, TerminalNode? __state, Terminal __instance)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Invalid comparison between Unknown and I4
			if ((Object)(object)__state != (Object)null)
			{
				TerminalSubmitPatch.LastNode = __state;
				return __state;
			}
			if ((int)__instance.videoPlayer.source == 1)
			{
				__instance.videoPlayer.source = (VideoSource)0;
			}
			TerminalSubmitPatch.LastNode = __result;
			return __result;
		}
	}
	[HarmonyPatch(typeof(Terminal), "selectTextFieldDelayed")]
	internal static class SelectTextFieldPatch
	{
		[HarmonyPrefix]
		public static bool Prefix()
		{
			return false;
		}

		[HarmonyPostfix]
		public static void Postfix(Terminal __instance, ref IEnumerator __result)
		{
			__result = Patch(__instance);
		}

		private static IEnumerator Patch(Terminal terminal)
		{
			yield return (object)new WaitForSeconds(0.2f);
			terminal.screenText.ActivateInputField();
			((Selectable)terminal.screenText).Select();
		}
	}
	[HarmonyPatch(typeof(Terminal), "OnSubmit")]
	internal static class TerminalSubmitPatch
	{
		private static ManualLogSource m_LogSource = new ManualLogSource("LethalAPI.Terminal");

		public static TerminalNode? LastNode { get; set; }

		[HarmonyPrefix]
		public static void Prefix()
		{
			LastNode = null;
		}

		[HarmonyTranspiler]
		public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected O, but got Unknown
			CodeInstruction[] array = instructions.ToArray();
			for (int num = array.Length - 1; num >= 0; num--)
			{
				if (!(array[num].opcode != OpCodes.Callvirt))
				{
					if (array[num + 1].opcode != OpCodes.Ldarg_0)
					{
						ReportTranspileError("Ldarg_0 expected after final callVirt, not found");
						return array;
					}
					array[num + 1] = new CodeInstruction(OpCodes.Ret, (object)null);
					return array;
				}
			}
			ReportTranspileError("Failed to find Callvirt in backward scan");
			return array;
		}

		private static void ReportTranspileError(string message)
		{
			m_LogSource.LogError((object)("Failed to transpile OnSubmit to remove Scroll To Bottom. Did the method get modified in an update? (" + message + ")"));
			m_LogSource.LogWarning((object)"This won't break the mod, but it will cause some odd terminal scrolling behavior");
		}

		[HarmonyPostfix]
		public static void Postfix(Terminal __instance, ref Coroutine ___forceScrollbarCoroutine)
		{
			if ((Object)(object)LastNode == (Object)null || LastNode.clearPreviousText)
			{
				ExecuteScrollCoroutine(__instance, ref ___forceScrollbarCoroutine);
			}
			else
			{
				((MonoBehaviour)__instance).StartCoroutine("forceScrollbarDown");
			}
		}

		private static void ExecuteScrollCoroutine(Terminal terminal, ref Coroutine forceScrollbarCoroutine)
		{
			if (forceScrollbarCoroutine != null)
			{
				((MonoBehaviour)terminal).StopCoroutine(forceScrollbarCoroutine);
			}
			forceScrollbarCoroutine = ((MonoBehaviour)terminal).StartCoroutine("forceScrollbarUp");
		}
	}
	[HarmonyPatch(typeof(Terminal), "TextPostProcess")]
	internal static class TextPostProcessPatch
	{
		[HarmonyPrefix]
		public static bool Prefix(Terminal __instance, ref string modifiedDisplayText, ref string? __state)
		{
			__state = null;
			if (CommandHandler.CurrentInterface == null || CommandHandler.CurrentInterface.APITextPostProcessing)
			{
				Console.WriteLine("Run text post process");
				modifiedDisplayText = TextUtil.PostProcessResponse(__instance, modifiedDisplayText);
				return true;
			}
			bool num = ProcessInterface(CommandHandler.CurrentInterface, ref modifiedDisplayText, __instance);
			if (!num)
			{
				__state = modifiedDisplayText;
			}
			return num;
		}

		[HarmonyPostfix]
		public static string? Postfix(string __result, Terminal __instance, string? __state)
		{
			string text = __state ?? __result;
			if (CommandHandler.CurrentInterface != null)
			{
				text = CommandHandler.CurrentInterface.PostProcessText(__instance, text);
			}
			return text;
		}

		private static bool ProcessInterface(ITerminalInterface terminalInterface, ref string modifiedDisplayText, Terminal terminal)
		{
			modifiedDisplayText = terminalInterface.PreProcessText(terminal, modifiedDisplayText);
			return terminalInterface.VanillaTextPostProcessing;
		}
	}
}
namespace LethalAPI.LibTerminal.Models
{
	public class ArgumentStream
	{
		public string[] Arguments { get; }

		public int Index { get; set; }

		public bool EndOfStream => Index >= Arguments.Length;

		public ArgumentStream(string[] arguments)
		{
			Arguments = arguments;
		}

		public ArgumentStream(IEnumerable<string> arguments)
		{
			Arguments = arguments.ToArray();
		}

		public void Reset()
		{
			Index = 0;
		}

		public bool TryReadNext(Type type, out object? value)
		{
			if (EndOfStream)
			{
				value = null;
				return false;
			}
			return StringConverter.TryConvert(Arguments[Index++], type, out value);
		}

		public bool TryReadRemaining(out string? result)
		{
			if (EndOfStream)
			{
				result = null;
				return false;
			}
			result = string.Join(" ", Arguments.Skip(Index));
			return true;
		}

		public bool TryReadNext(out string value)
		{
			value = string.Empty;
			if (EndOfStream)
			{
				return false;
			}
			value = Arguments[Index++];
			return true;
		}

		public bool TryReadNext(out sbyte value)
		{
			value = 0;
			if (EndOfStream)
			{
				return false;
			}
			return sbyte.TryParse(Arguments[Index++], out value);
		}

		public bool TryReadNext(out byte value)
		{
			value = 0;
			if (EndOfStream)
			{
				return false;
			}
			return byte.TryParse(Arguments[Index++], out value);
		}

		public bool TryReadNext(out short value)
		{
			value = 0;
			if (EndOfStream)
			{
				return false;
			}
			return short.TryParse(Arguments[Index++], out value);
		}

		public bool TryReadNext(out ushort value)
		{
			value = 0;
			if (EndOfStream)
			{
				return false;
			}
			return ushort.TryParse(Arguments[Index++], out value);
		}

		public bool TryReadNext(out int value)
		{
			value = 0;
			if (EndOfStream)
			{
				return false;
			}
			return int.TryParse(Arguments[Index++], out value);
		}

		public bool TryReadNext(out uint value)
		{
			value = 0u;
			if (EndOfStream)
			{
				return false;
			}
			return uint.TryParse(Arguments[Index++], out value);
		}

		public bool TryReadNext(out long value)
		{
			value = 0L;
			if (EndOfStream)
			{
				return false;
			}
			return long.TryParse(Arguments[Index++], out value);
		}

		public bool TryReadNext(out ulong value)
		{
			value = 0uL;
			if (EndOfStream)
			{
				return false;
			}
			return ulong.TryParse(Arguments[Index++], out value);
		}

		public bool TryReadNext(out float value)
		{
			value = 0f;
			if (EndOfStream)
			{
				return false;
			}
			return float.TryParse(Arguments[Index++], out value);
		}

		public bool TryReadNext(out double value)
		{
			value = 0.0;
			if (EndOfStream)
			{
				return false;
			}
			return double.TryParse(Arguments[Index++], out value);
		}
	}
	public static class CommandActivator
	{
		public static bool TryCreateInvoker(ArgumentStream arguments, ServiceCollection services, MethodInfo method, out Func<object, object?>? invoker)
		{
			MethodInfo method2 = method;
			TerminalCommandAttribute commandAttribute = method2.GetCustomAttribute<TerminalCommandAttribute>();
			ParameterInfo[] parameters = method2.GetParameters();
			object?[] values = new object[parameters.Length];
			invoker = null;
			for (int i = 0; i < parameters.Length; i++)
			{
				ParameterInfo parameterInfo = parameters[i];
				Type parameterType = parameterInfo.ParameterType;
				if (services.TryGetService(parameterType, out object service))
				{
					values[i] = service;
				}
				else if (parameterType == typeof(string) && parameterInfo.GetCustomAttribute<RemainingTextAttribute>() != null)
				{
					if (!arguments.TryReadRemaining(out string result) || result == null)
					{
						return false;
					}
					values[i] = result;
				}
				else
				{
					if (!arguments.TryReadNext(parameterType, out object value) || value == null)
					{
						return false;
					}
					values[i] = value;
				}
			}
			arguments.Reset();
			invoker = (object instance) => ExecuteCommand(method2, instance, values, commandAttribute?.ClearText ?? false);
			return true;
		}

		private static object? ExecuteCommand(MethodInfo method, object instance, object?[] arguments, bool clearConsole)
		{
			object obj;
			try
			{
				obj = method.Invoke(instance, arguments);
			}
			catch (Exception ex)
			{
				Console.WriteLine("Error caught while invoking command hander: " + ex.Message);
				Console.WriteLine(ex.StackTrace);
				return null;
			}
			if (obj == null)
			{
				return null;
			}
			Type type = obj.GetType();
			if (typeof(TerminalNode).IsAssignableFrom(type))
			{
				return obj;
			}
			if (typeof(ITerminalInteraction).IsAssignableFrom(type))
			{
				return obj;
			}
			if (typeof(ITerminalInterface).IsAssignableFrom(type))
			{
				return obj;
			}
			TerminalNode obj2 = ScriptableObject.CreateInstance<TerminalNode>();
			obj2.displayText = obj.ToString() + "\n";
			obj2.clearPreviousText = clearConsole;
			return obj2;
		}
	}
	public class CommandComparer : IComparer<TerminalCommand>
	{
		public int Compare(TerminalCommand x, TerminalCommand y)
		{
			if (x.Priority > y.Priority)
			{
				return 1;
			}
			if (x.Priority < y.Priority)
			{
				return -1;
			}
			return x.ArgumentCount.CompareTo(y.ArgumentCount);
		}
	}
	public static class CommandHandler
	{
		private static readonly Stack<ITerminalInteraction> m_Interactions = new Stack<ITerminalInteraction>();

		private static readonly Regex m_SplitRegex = new Regex("[\\\"](.+?)[\\\"]|([^ ]+)", RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture | RegexOptions.Compiled);

		private static readonly CommandComparer m_Comparer = new CommandComparer();

		public static int InteractionDepth => m_Interactions.Count;

		public static ITerminalInterface? CurrentInterface { get; private set; }

		public static TerminalNode? HandleCommandInput(string command, Terminal terminal)
		{
			string[] array = (from Match x in m_SplitRegex.Matches(command.Trim())
				select x.Value.Trim('"', ' ')).ToArray();
			ArgumentStream arguments = new ArgumentStream(array);
			if (CurrentInterface != null)
			{
				return CurrentInterface.HandleInput(terminal, arguments);
			}
			TerminalNode val = ExecuteInteractions(arguments, terminal);
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			string commandName = array.First();
			ArgumentStream arguments2 = new ArgumentStream(array.Skip(1));
			return ExecuteCommand(commandName, arguments2, terminal);
		}

		public static TerminalNode? ExecuteInteractions(ArgumentStream arguments, Terminal terminal)
		{
			while (m_Interactions.Count > 0)
			{
				ITerminalInteraction terminalInteraction = m_Interactions.Pop();
				arguments.Reset();
				try
				{
					terminalInteraction.Services.WithServices(arguments, terminal, arguments.Arguments);
					object obj = terminalInteraction.HandleTerminalResponse(arguments);
					if (obj != null)
					{
						return HandleCommandResult(obj, terminal);
					}
				}
				catch (Exception ex)
				{
					Console.WriteLine(ex.GetType().Name);
					Console.WriteLine("Error executing interaction: " + ex.Message + ", " + ex.StackTrace);
				}
			}
			return null;
		}

		public static TerminalNode? ExecuteCommand(ArgumentStream arguments, Terminal terminal)
		{
			arguments.Reset();
			if (!arguments.TryReadNext(out string value))
			{
				return null;
			}
			ArgumentStream arguments2 = new ArgumentStream(arguments.Arguments.Skip(1));
			return ExecuteCommand(value, arguments2, terminal);
		}

		public static TerminalNode? ExecuteCommand(string commandName, ArgumentStream arguments, Terminal terminal)
		{
			Terminal terminal2 = terminal;
			List<(TerminalCommand, Func<TerminalNode>)> list = new List<(TerminalCommand, Func<TerminalNode>)>();
			TerminalCommand[] array = TerminalRegistry.GetCommands(commandName).ToArray();
			ServiceCollection services = new ServiceCollection(arguments, arguments.Arguments, terminal2);
			foreach (TerminalCommand terminalCommand in array)
			{
				if (!terminalCommand.CheckAllowed())
				{
					continue;
				}
				arguments.Reset();
				if (terminalCommand.TryCreateInvoker(arguments, services, out Func<object?> invoker) && invoker != null)
				{
					Func<TerminalNode> item = () => HandleCommandResult(invoker(), terminal2);
					list.Add((terminalCommand, item));
				}
			}
			foreach (var item2 in list.OrderByDescending<(TerminalCommand, Func<TerminalNode>), TerminalCommand>(((TerminalCommand command, Func<TerminalNode> invoker) x) => x.command, m_Comparer))
			{
				TerminalNode val = item2.Item2();
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			return null;
		}

		private static TerminalNode? HandleCommandResult(object? result, Terminal terminal)
		{
			TerminalNode val = (TerminalNode)((result is TerminalNode) ? result : null);
			if (val != null)
			{
				return val;
			}
			if (result is ITerminalInteraction terminalInteraction)
			{
				SetInteraction(terminalInteraction);
				return terminalInteraction.Prompt;
			}
			if (result is ITerminalInterface terminalInterface)
			{
				SetInterface(terminalInterface);
				return terminalInterface.GetSplashScreen(terminal);
			}
			return ScriptableObject.CreateInstance<TerminalNode>().WithDisplayText(result);
		}

		public static void SetInteraction(ITerminalInteraction interaction)
		{
			m_Interactions.Push(interaction);
		}

		public static void SetInterface(ITerminalInterface terminalInterface)
		{
			CurrentInterface = terminalInterface;
		}

		public static void ResetInterface()
		{
			CurrentInterface = null;
		}

		public static void ResetInterface<T>() where T : ITerminalInterface
		{
			if (CurrentInterface != null && CurrentInterface is T)
			{
				ResetInterface();
			}
		}
	}
	public static class DefaultStringConverters
	{
		[StringConverter]
		public static string ParseString(string input)
		{
			return input;
		}

		[StringConverter]
		public static sbyte ParseSByte(string input)
		{
			if (sbyte.TryParse(input, out var result))
			{
				return result;
			}
			throw new ArgumentException();
		}

		[StringConverter]
		public static byte ParseByte(string input)
		{
			if (byte.TryParse(input, out var result))
			{
				return result;
			}
			throw new ArgumentException();
		}

		[StringConverter]
		public static short ParseShort(string input)
		{
			if (short.TryParse(input, out var result))
			{
				return result;
			}
			throw new ArgumentException();
		}

		[StringConverter]
		public static ushort ParseUShort(string input)
		{
			if (ushort.TryParse(input, out var result))
			{
				return result;
			}
			throw new ArgumentException();
		}

		[StringConverter]
		public static int ParseInt(string input)
		{
			if (int.TryParse(input, out var result))
			{
				return result;
			}
			throw new ArgumentException();
		}

		[StringConverter]
		public static uint ParseUInt(string input)
		{
			if (uint.TryParse(input, out var result))
			{
				return result;
			}
			throw new ArgumentException();
		}

		[StringConverter]
		public static long ParseLong(string input)
		{
			if (long.TryParse(input, out var result))
			{
				return result;
			}
			throw new ArgumentException();
		}

		[StringConverter]
		public static ulong ParseULong(string input)
		{
			if (ulong.TryParse(input, out var result))
			{
				return result;
			}
			throw new ArgumentException();
		}

		[StringConverter]
		public static float ParseFloat(string input)
		{
			if (float.TryParse(input, out var result))
			{
				return result;
			}
			throw new ArgumentException();
		}

		[StringConverter]
		public static double ParseDouble(string input)
		{
			if (double.TryParse(input, out var result))
			{
				return result;
			}
			throw new ArgumentException();
		}

		[StringConverter]
		public static decimal ParseDecimal(string input)
		{
			if (decimal.TryParse(input, out var result))
			{
				return result;
			}
			throw new ArgumentException();
		}

		[StringConverter]
		public static PlayerControllerB ParsePlayerControllerB(string value)
		{
			string value2 = value;
			if ((Object)(object)StartOfRound.Instance == (Object)null)
			{
				throw new ArgumentException("Game has not started");
			}
			PlayerControllerB val = null;
			if (ulong.TryParse(value2, out var steamID))
			{
				val = ((IEnumerable<PlayerControllerB>)StartOfRound.Instance.allPlayerScripts).FirstOrDefault((Func<PlayerControllerB, bool>)((PlayerControllerB x) => x.playerSteamId == steamID));
			}
			if ((Object)(object)val == (Object)null)
			{
				val = ((IEnumerable<PlayerControllerB>)StartOfRound.Instance.allPlayerScripts).FirstOrDefault((Func<PlayerControllerB, bool>)((PlayerControllerB x) => x.playerUsername.IndexOf(value2, StringComparison.InvariantCultureIgnoreCase) != -1));
			}
			if ((Object)(object)val == (Object)null)
			{
				throw new ArgumentException("Failed to find player");
			}
			return val;
		}
	}
	public readonly struct RegisteredStringConverter
	{
		public Type Type { get; }

		public StringConversionHandler Handler { get; }

		public RegisteredStringConverter(Type type, StringConversionHandler handler)
		{
			Type = type;
			Handler = handler;
		}
	}
	public struct ServiceCollection
	{
		private Dictionary<Type, object?> m_Services;

		public ServiceCollection(params object[] services)
		{
			m_Services = new Dictionary<Type, object>();
			WithServices(services);
		}

		public ServiceCollection()
		{
			m_Services = new Dictionary<Type, object>();
		}

		public bool TryGetService(Type t, out object? service)
		{
			if (m_Services == null)
			{
				service = null;
				return false;
			}
			return m_Services.TryGetValue(t, out service);
		}

		public void WithService<T>(T instance)
		{
			if (m_Services != null)
			{
				m_Services[typeof(T)] = instance;
			}
		}

		public void WithServices(params object[] services)
		{
			if (m_Services == null)
			{
				return;
			}
			foreach (object obj in services)
			{
				if (obj != null)
				{
					m_Services.Add(obj.GetType(), obj);
				}
			}
		}
	}
	public delegate object StringConversionHandler(string value);
	public static class StringConverter
	{
		private static bool m_Initialized = false;

		private static ConcurrentDictionary<Type, List<StringConversionHandler>> m_StringConverters { get; } = new ConcurrentDictionary<Type, List<StringConversionHandler>>();


		public static bool TryConvert(string value, Type type, out object? result)
		{
			if (!m_Initialized)
			{
				m_Initialized = true;
				RegisterFromType(typeof(DefaultStringConverters));
			}
			if (!m_StringConverters.TryGetValue(type, out List<StringConversionHandler> value2))
			{
				result = null;
				return false;
			}
			for (int i = 0; i < value2.Count; i++)
			{
				try
				{
					result = value2[i](value);
					return true;
				}
				catch (Exception)
				{
				}
			}
			result = null;
			return false;
		}

		public static List<RegisteredStringConverter> RegisterFrom<T>(T instance) where T : class
		{
			return RegisterFromType(typeof(T), instance);
		}

		public static bool Deregister(RegisteredStringConverter converter)
		{
			if (!m_StringConverters.TryGetValue(converter.Type, out List<StringConversionHandler> value))
			{
				return false;
			}
			return value.Remove(converter.Handler);
		}

		public static bool CanConvert(Type type)
		{
			return m_StringConverters.ContainsKey(type);
		}

		public static List<RegisteredStringConverter> RegisterFromType(Type type, object? instance = null)
		{
			object instance2 = instance;
			List<RegisteredStringConverter> list = new List<RegisteredStringConverter>();
			MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo method in methods)
			{
				if (method.GetCustomAttribute<StringConverterAttribute>() == null)
				{
					continue;
				}
				ParameterInfo[] parameters = method.GetParameters();
				if (parameters.Length == 1 && !(parameters[0].ParameterType != typeof(string)))
				{
					Type returnType = method.ReturnType;
					StringConversionHandler stringConversionHandler = (string value) => method.Invoke(instance2, new object[1] { value });
					if (!m_StringConverters.TryGetValue(returnType, out List<StringConversionHandler> value2))
					{
						value2 = new List<StringConversionHandler>();
						m_StringConverters[returnType] = value2;
					}
					value2.Add(stringConversionHandler);
					list.Add(new RegisteredStringConverter(returnType, stringConversionHandler));
				}
			}
			return list;
		}
	}
	public class TerminalCommand
	{
		private ManualLogSource m_LogSource = new ManualLogSource("LethalAPI.Terminal");

		public string Name { get; }

		public MethodInfo Method { get; }

		public object Instance { get; }

		public bool ClearConsole { get; }

		public int ArgumentCount { get; }

		public string? Syntax { get; }

		public string? Description { get; }

		public int Priority { get; }

		public TerminalCommand(string name, MethodInfo method, object instance, bool clearConsole, string? syntax = null, string? description = null, int priority = 0)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			Name = name;
			Method = method;
			Instance = instance;
			ClearConsole = clearConsole;
			ArgumentCount = method.GetParameters().Length;
			Syntax = syntax;
			Description = description;
			Priority = priority;
		}

		public bool CheckAllowed()
		{
			foreach (AccessControlAttribute customAttribute in Method.GetCustomAttributes<AccessControlAttribute>())
			{
				if (!customAttribute.CheckAllowed())
				{
					return false;
				}
			}
			return true;
		}

		public static TerminalCommand FromMethod(MethodInfo info, object instance, string? overrideName = null)
		{
			bool clearConsole = false;
			string syntax = null;
			string description = null;
			string text = overrideName;
			int priority = 0;
			TerminalCommandAttribute customAttribute = info.GetCustomAttribute<TerminalCommandAttribute>();
			if (customAttribute != null)
			{
				text = text ?? customAttribute.CommandName;
				clearConsole = customAttribute.ClearText;
			}
			CommandInfoAttribute customAttribute2 = info.GetCustomAttribute<CommandInfoAttribute>();
			if (customAttribute2 != null)
			{
				syntax = customAttribute2.Syntax;
				description = customAttribute2.Description;
			}
			CommandPriority customAttribute3 = info.GetCustomAttribute<CommandPriority>();
			if (customAttribute3 != null)
			{
				priority = customAttribute3.Priority;
			}
			if (text == null)
			{
				throw new ArgumentException("No override name provided, and command name could not be resolved from method");
			}
			return new TerminalCommand(text, info, instance, clearConsole, syntax, description, priority);
		}

		public bool TryCreateInvoker(ArgumentStream arguments, ServiceCollection services, out Func<object?>? invoker)
		{
			if (CommandActivator.TryCreateInvoker(arguments, services, Method, out Func<object, object?> activatedInvoker) && activatedInvoker != null)
			{
				invoker = () => activatedInvoker(Instance);
				return true;
			}
			invoker = null;
			return false;
		}
	}
	public class TerminalModRegistry
	{
		public List<TerminalCommand> Commands { get; } = new List<TerminalCommand>();


		public List<RegisteredStringConverter> StringConverters { get; } = new List<RegisteredStringConverter>();


		public T RegisterFrom<T>() where T : class, new()
		{
			return RegisterFrom(new T());
		}

		public T RegisterFrom<T>(T instance) where T : class
		{
			foreach (MethodInfo commandMethod in TerminalRegistry.GetCommandMethods<T>())
			{
				TerminalCommand terminalCommand = TerminalCommand.FromMethod(commandMethod, instance);
				TerminalRegistry.RegisterCommand(terminalCommand);
				lock (Commands)
				{
					Commands.Add(terminalCommand);
				}
			}
			StringConverters.AddRange(StringConverter.RegisterFrom(instance));
			return instance;
		}

		public void Deregister()
		{
			for (int i = 0; i < Commands.Count; i++)
			{
				TerminalRegistry.Deregister(Commands[i]);
			}
			Commands.Clear();
			for (int j = 0; j < StringConverters.Count; j++)
			{
				StringConverter.Deregister(StringConverters[j]);
			}
			StringConverters.Clear();
		}
	}
	public static class TextUtil
	{
		public static string PostProcessResponse(Terminal terminal, string message)
		{
			TerminalNode? lastLoadedNode = terminal.GetLastLoadedNode();
			message = ((lastLoadedNode != null && !lastLoadedNode.clearPreviousText) ? message.TrimStart('\n', ' ') : message.SetStartPadding('\n', 2));
			message = message.SetEndPadding('\n', 2);
			return message;
		}

		public static string SetEndPadding(this string text, char character, int minPadding)
		{
			string text2 = text;
			int num = 0;
			int num2 = text.Length - 1;
			while (num2 >= 0 && text[num2] == character)
			{
				num++;
				num2--;
			}
			int num3 = minPadding - num;
			if (num3 > 0)
			{
				text2 += new string(character, num3);
			}
			return text2;
		}

		public static string SetStartPadding(this string text, char character, int minPadding)
		{
			string text2 = text;
			int num = 0;
			for (int i = 0; i < text.Length && text[i] == character; i++)
			{
				num++;
			}
			int num2 = minPadding - num;
			if (num2 > 0)
			{
				text2 = new string(character, num2) + text2;
			}
			return text2;
		}
	}
}
namespace LethalAPI.LibTerminal.Models.Enums
{
	public enum AllowedCaller
	{
		None = -1,
		Player,
		Host
	}
	public enum SyncedTerminalClip
	{
		ItemPurchased,
		Error,
		BroadcastEffect
	}
}
namespace LethalAPI.LibTerminal.Interfaces
{
	public interface ITerminalInteraction
	{
		TerminalNode? Prompt { get; }

		ServiceCollection Services { get; }

		object? HandleTerminalResponse(ArgumentStream arguments);
	}
	public interface ITerminalInterface
	{
		bool APITextPostProcessing { get; }

		bool VanillaTextPostProcessing { get; }

		TerminalNode HandleInput(Terminal instance, ArgumentStream arguments);

		string PreProcessText(Terminal instance, string text);

		string PostProcessText(Terminal terminal, string text);

		TerminalNode GetSplashScreen(Terminal terminal);
	}
}
namespace LethalAPI.LibTerminal.Interactions
{
	public class ConfirmInteraction : ITerminalInteraction
	{
		public TerminalNode? Prompt { get; private set; }

		public ServiceCollection Services { get; } = new ServiceCollection();


		public Delegate? ConfirmHandler { get; set; }

		public Delegate? DenyHandler { get; set; }

		public ConfirmInteraction()
		{
		}

		public ConfirmInteraction(TerminalNode prompt)
		{
			Prompt = prompt;
			PostprocessPrompt();
		}

		public ConfirmInteraction(Action<TerminalNode> promptBuilder)
		{
			TerminalNode val = ScriptableObject.CreateInstance<TerminalNode>();
			promptBuilder(val);
			WithPrompt(val);
		}

		public ConfirmInteraction(TerminalNode prompt, Delegate confirm, Delegate deny)
		{
			WithPrompt(prompt);
			ConfirmHandler = confirm;
			DenyHandler = deny;
		}

		public ConfirmInteraction(Action<TerminalNode> promptBuilder, Delegate confirm, Delegate deny)
		{
			WithPrompt(promptBuilder);
			ConfirmHandler = confirm;
			DenyHandler = deny;
		}

		public ConfirmInteraction(string prompt)
		{
			WithPrompt(prompt);
		}

		public ConfirmInteraction(string prompt, Delegate confirm, Delegate deny)
		{
			WithPrompt(prompt);
			ConfirmHandler = confirm;
			DenyHandler = deny;
		}

		public ConfirmInteraction WithPrompt(string prompt)
		{
			Prompt = ScriptableObject.CreateInstance<TerminalNode>();
			Prompt.WithDisplayText(prompt);
			PostprocessPrompt();
			return this;
		}

		public ConfirmInteraction WithPrompt(TerminalNode prompt)
		{
			Prompt = prompt;
			PostprocessPrompt();
			return this;
		}

		public ConfirmInteraction WithPrompt(Action<TerminalNode> promptBuilder)
		{
			TerminalNode val = ScriptableObject.CreateInstance<TerminalNode>();
			promptBuilder(val);
			WithPrompt(val);
			return this;
		}

		public ConfirmInteraction Confirm(Delegate confirmHandler)
		{
			ConfirmHandler = confirmHandler;
			return this;
		}

		public ConfirmInteraction Deny(Delegate denyHandler)
		{
			DenyHandler = denyHandler;
			return this;
		}

		public ConfirmInteraction WithContext(params object[] services)
		{
			Services.WithServices(services);
			return this;
		}

		public object? HandleTerminalResponse(ArgumentStream arguments)
		{
			if (!arguments.TryReadNext(out string value))
			{
				return Deny(arguments);
			}
			if (value.Trim().Equals("confirm", StringComparison.InvariantCultureIgnoreCase))
			{
				return Confirm(arguments);
			}
			return Deny(arguments);
		}

		private object? Confirm(ArgumentStream arguments)
		{
			if ((object)DenyHandler == null)
			{
				return string.Empty;
			}
			if ((object)ConfirmHandler != null && CommandActivator.TryCreateInvoker(arguments, Services, ConfirmHandler.GetMethodInfo(), out Func<object, object> invoker) && invoker != null)
			{
				return invoker(ConfirmHandler.Target);
			}
			return null;
		}

		private object? Deny(ArgumentStream arguments)
		{
			if ((object)DenyHandler == null)
			{
				return string.Empty;
			}
			if (CommandActivator.TryCreateInvoker(arguments, Services, DenyHandler.GetMethodInfo(), out Func<object, object> invoker) && invoker != null)
			{
				return invoker(DenyHandler.Target);
			}
			return null;
		}

		private void PostprocessPrompt()
		{
			if ((Object)(object)Prompt != (Object)null)
			{
				string text = Prompt.displayText ?? string.Empty;
				text = text.SetEndPadding('\n', 2) + "Please CONFIRM or DENY.\n";
				Prompt.displayText = text;
			}
		}
	}
	public class TerminalInteraction : ITerminalInteraction
	{
		public TerminalNode? Prompt { get; private set; }

		public ServiceCollection Services { get; } = new ServiceCollection();


		public List<Delegate> Handlers { get; } = new List<Delegate>();


		public TerminalInteraction()
		{
		}

		public TerminalInteraction(TerminalNode prompt, Delegate handler)
		{
			Prompt = prompt;
			Handlers.Add(handler);
		}

		public TerminalInteraction(Action<TerminalNode> promptBuilder, Delegate handler)
		{
			TerminalNode val = ScriptableObject.CreateInstance<TerminalNode>();
			promptBuilder(val);
			WithPrompt(val);
			Handlers.Add(handler);
		}

		public TerminalInteraction WithContext(params object[] services)
		{
			Services.WithServices(services);
			return this;
		}

		public TerminalInteraction WithPrompt(TerminalNode prompt)
		{
			Prompt = prompt;
			return this;
		}

		public TerminalInteraction WithPrompt(Action<TerminalNode> promptBuilder)
		{
			TerminalNode val = ScriptableObject.CreateInstance<TerminalNode>();
			promptBuilder(val);
			WithPrompt(val);
			return this;
		}

		public TerminalInteraction WithPrompt(string prompt)
		{
			Prompt = ScriptableObject.CreateInstance<TerminalNode>();
			Prompt.WithDisplayText(prompt);
			return this;
		}

		public TerminalInteraction WithHandler(Delegate handler)
		{
			Handlers.Add(handler);
			return this;
		}

		public object? HandleTerminalResponse(ArgumentStream arguments)
		{
			(from x in Handlers
				select (x.GetMethodInfo(), x.Target) into x
				orderby x.info.GetParameters().Length descending
				select x).ToList();
			foreach (Delegate handler in Handlers)
			{
				MethodInfo methodInfo = handler.GetMethodInfo();
				arguments.Reset();
				if (CommandActivator.TryCreateInvoker(arguments, Services, methodInfo, out Func<object, object> invoker) && invoker != null)
				{
					object obj = invoker(handler.Target);
					if (obj != null)
					{
						return obj;
					}
				}
			}
			return null;
		}
	}
}
namespace LethalAPI.LibTerminal.Commands
{
	public class CommandInfoCommands
	{
		[TerminalCommand("Other", true)]
		public string CommandList()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("Other commands:");
			stringBuilder.AppendLine();
			stringBuilder.AppendLine(">VIEW MONITOR");
			stringBuilder.AppendLine("To toggle on/off the main monitor's map cam");
			stringBuilder.AppendLine();
			stringBuilder.AppendLine(">SWITCH {RADAR}");
			stringBuilder.AppendLine("To switch the player view on the main monitor");
			stringBuilder.AppendLine();
			stringBuilder.AppendLine(">PING [Radar booster name]");
			stringBuilder.AppendLine("To switch the player view on the main monitor");
			stringBuilder.AppendLine();
			stringBuilder.AppendLine(">SCAN");
			stringBuilder.AppendLine("To scan for the number of items left on the current planet");
			stringBuilder.AppendLine();
			stringBuilder.AppendLine(">TRANSMIT [message]");
			stringBuilder.AppendLine("Transmit a message with the signal translator");
			stringBuilder.AppendLine();
			foreach (TerminalCommand item in TerminalRegistry.EnumerateCommands())
			{
				if (item.Description != null && item.CheckAllowed())
				{
					stringBuilder.AppendLine(">" + item.Name.ToUpper() + " " + item.Syntax?.ToUpper());
					stringBuilder.AppendLine(item.Description);
					stringBuilder.AppendLine();
				}
			}
			return stringBuilder.ToString();
		}

		[TerminalCommand("Help", false)]
		[CommandInfo("Shows further information about a command", "[Command]")]
		public string HelpCommand(string name)
		{
			StringBuilder stringBuilder = new StringBuilder();
			TerminalCommand[] array = TerminalRegistry.EnumerateCommands(name).ToArray();
			if (array.Length == 0)
			{
				return "Unknown command: '" + name + "'";
			}
			TerminalCommand[] array2 = array;
			foreach (TerminalCommand terminalCommand in array2)
			{
				stringBuilder.AppendLine(">" + terminalCommand.Name.ToUpper() + " " + terminalCommand.Syntax?.ToUpper());
				stringBuilder.AppendLine(terminalCommand.Description);
				if (!terminalCommand.CheckAllowed())
				{
					stringBuilder.AppendLine("[Host Only]");
				}
				stringBuilder.AppendLine();
			}
			return stringBuilder.ToString();
		}
	}
}
namespace LethalAPI.LibTerminal.Attributes
{
	[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
	public abstract class AccessControlAttribute : Attribute
	{
		public abstract bool CheckAllowed();
	}
	[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
	public class AllowedCallerAttribute : AccessControlAttribute
	{
		public AllowedCaller Caller { get; }

		public AllowedCallerAttribute(AllowedCaller caller)
		{
			Caller = caller;
		}

		public override bool CheckAllowed()
		{
			switch (Caller)
			{
			case AllowedCaller.None:
				return false;
			case AllowedCaller.Player:
				return true;
			case AllowedCaller.Host:
				if ((Object)(object)StartOfRound.Instance == (Object)null)
				{
					return false;
				}
				return ((NetworkBehaviour)StartOfRound.Instance).IsHost;
			default:
				return true;
			}
		}
	}
	[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
	public class CommandInfoAttribute : Attribute
	{
		public string Syntax { get; }

		public string Description { get; }

		public CommandInfoAttribute(string description, string syntax = "")
		{
			Syntax = syntax;
			Description = description;
		}
	}
	public sealed class CommandPriority : Attribute
	{
		public int Priority { get; }

		public CommandPriority(int priority)
		{
			Priority = priority;
		}
	}
	[AttributeUsage(AttributeTargets.Parameter)]
	public sealed class RemainingTextAttribute : Attribute
	{
	}
	public class RequireInterfaceAttribute : AccessControlAttribute
	{
		public Type InterfaceType { get; }

		public RequireInterfaceAttribute(Type interfaceType)
		{
			InterfaceType = interfaceType;
		}

		public override bool CheckAllowed()
		{
			if (CommandHandler.CurrentInterface != null)
			{
				return CommandHandler.CurrentInterface.GetType() == InterfaceType;
			}
			return false;
		}
	}
	public sealed class RequireInterfaceAttribute<T> : RequireInterfaceAttribute where T : ITerminalInterface
	{
		public RequireInterfaceAttribute()
			: base(typeof(T))
		{
		}
	}
	public sealed class StringConverterAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
	public class TerminalCommandAttribute : Attribute
	{
		public string CommandName { get; }

		public bool ClearText { get; }

		public TerminalCommandAttribute(string name, bool clearText = false)
		{
			CommandName = name;
			ClearText = clearText;
		}
	}
}

ImpostorMod.dll

Decompiled 4 months ago
using System;
using System.Collections;
using System.Collections.Generic;
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.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using ImpostorMod.Patches;
using LethalAPI.LibTerminal;
using LethalAPI.LibTerminal.Attributes;
using LethalAPI.LibTerminal.Models;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("ImpostorMod")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("HP")]
[assembly: AssemblyProduct("ImpostorMod")]
[assembly: AssemblyCopyright("Copyright © HP 2024")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("255b2931-97f2-4f38-bc66-042e43b38280")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace ImpostorMod
{
	[BepInPlugin("Sporekid.ImpostorMod", "Impostor Mod", "1.3.0")]
	public class ImpostorModBase : BaseUnityPlugin
	{
		private const string modGUID = "Sporekid.ImpostorMod";

		private const string modName = "Impostor Mod";

		private const string modVersion = "1.3.0";

		private readonly Harmony harmony = new Harmony("Sporekid.ImpostorMod");

		public static ImpostorModBase Instance;

		internal ManualLogSource mls;

		public TerminalModRegistry Commands;

		public bool isVoting = false;

		public Dictionary<string, int> votedPlayers;

		public Dictionary<string, bool> hasVotedList;

		private void Awake()
		{
			if ((Object)(object)Instance == (Object)null)
			{
				Instance = this;
			}
			mls = Logger.CreateLogSource("Sporekid.ImpostorMod");
			mls.LogInfo((object)"--- Impostor mod started ---");
			harmony.PatchAll(typeof(ImpostorModBase));
			harmony.PatchAll(typeof(StartOfRoundPatch));
			Commands = TerminalRegistry.CreateTerminalRegistry();
			Commands.RegisterFrom<ImpostorModBase>(this);
			votedPlayers = new Dictionary<string, int>();
			hasVotedList = new Dictionary<string, bool>();
		}

		[TerminalCommand("vote", false)]
		public string VoteCommand([RemainingText] string text)
		{
			if (isVoting)
			{
				FillPlayersList();
				PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
				PlayerControllerB[] array = allPlayerScripts;
				foreach (PlayerControllerB val in array)
				{
					if (hasVotedList[allPlayerScripts[StartOfRound.Instance.thisClientPlayerId].playerUsername])
					{
						return "You already voted";
					}
					if (text == val.playerUsername)
					{
						hasVotedList[allPlayerScripts[StartOfRound.Instance.thisClientPlayerId].playerUsername] = true;
						votedPlayers[val.playerUsername]++;
						return "You voted for " + val.playerUsername;
					}
				}
				return "Enter a valid username.";
			}
			return "Can't vote";
		}

		public void FillPlayersList()
		{
			votedPlayers.Clear();
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			PlayerControllerB[] array = allPlayerScripts;
			foreach (PlayerControllerB val in array)
			{
				votedPlayers.Add(val.playerUsername, 0);
				if (!hasVotedList.TryGetValue(val.playerUsername, out var _))
				{
					hasVotedList.Add(val.playerUsername, value: false);
				}
			}
		}
	}
}
namespace ImpostorMod.Patches
{
	[HarmonyPatch(typeof(StartOfRound))]
	internal class StartOfRoundPatch : MonoBehaviour
	{
		public static StartOfRoundPatch Instance;

		public static int day = 0;

		private static int impostorID = -1;

		private static bool isImpostorPicked = false;

		private static Color nightVisionColor;

		private static float nightVisionIntensity;

		private static float nightVisionRange;

		public static string mostVotedPlayer = "";

		public static List<string> GetPlayersNames()
		{
			List<string> list = new List<string>();
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			for (int i = 0; i < allPlayerScripts.Length; i++)
			{
				if (allPlayerScripts[i].isPlayerDead || allPlayerScripts[i].isPlayerControlled)
				{
					list.Add(allPlayerScripts[i].playerUsername);
				}
			}
			return list;
		}

		public static GameObject DisplayRolesFixed()
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected O, but got Unknown
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = GameObject.Find("Systems/UI/Canvas/IngamePlayerHUD/HandsFullText");
			GameObject val2 = (GameObject)(Object.op_Implicit((Object)(object)GameObject.Find("RoleDisplay")) ? ((object)GameObject.Find("RoleDisplay")) : ((object)new GameObject("RoleDisplay")));
			val2.transform.SetParent(val.transform, false);
			TextMeshProUGUI val3 = val2.AddComponent<TextMeshProUGUI>();
			((TMP_Text)val3).fontSize = 14f;
			((Behaviour)val3).enabled = true;
			Color color = default(Color);
			ColorUtility.TryParseHtmlString("#e36d19", ref color);
			((Graphic)val3).color = color;
			((TMP_Text)val3).text = "You are : ";
			int thisClientPlayerId = StartOfRound.Instance.thisClientPlayerId;
			PlayerControllerB val4 = StartOfRound.Instance.allPlayerScripts[thisClientPlayerId];
			if (impostorID == thisClientPlayerId)
			{
				((TMP_Text)val3).text = ((TMP_Text)val3).text + "impostor.";
			}
			else
			{
				((TMP_Text)val3).text = ((TMP_Text)val3).text + "crewmate.";
			}
			((TMP_Text)val3).alignment = (TextAlignmentOptions)514;
			val2.GetComponent<RectTransform>().anchoredPosition = new Vector2(0f, -35f);
			return val2;
		}

		[HarmonyPatch("OnShipLandedMiscEvents")]
		[HarmonyPostfix]
		public static void OnShipLandedMiscEventsPatch()
		{
			ImpostorModBase.Instance.mls.LogInfo((object)"# OnShipLandedMiscEventsPatch() begins #");
			if (day == 0)
			{
				if (impostorID > -1)
				{
					ResetImpostor();
				}
				PickImpostor();
				DisplayRoles();
				SetImpostorCharacteristics();
			}
			ImpostorModBase.Instance.mls.LogInfo((object)"# OnShipLandedMiscEventsPatch() ends #");
		}

		private static void SetImpostorCharacteristics()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			allPlayerScripts[impostorID].nightVision.color = Color.white;
			allPlayerScripts[impostorID].nightVision.intensity = 1000f;
			allPlayerScripts[impostorID].nightVision.range = 10000f;
			((Behaviour)allPlayerScripts[impostorID].nightVision).enabled = true;
		}

		private static void ResetImpostor()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			allPlayerScripts[impostorID].nightVision.color = nightVisionColor;
			allPlayerScripts[impostorID].nightVision.intensity = nightVisionIntensity;
			allPlayerScripts[impostorID].nightVision.range = nightVisionRange;
			((Behaviour)allPlayerScripts[impostorID].nightVision).enabled = false;
		}

		[HarmonyPatch("SetShipReadyToLand")]
		[HarmonyPostfix]
		public static void SetShipReadyToLandPatch()
		{
			ImpostorModBase.Instance.mls.LogInfo((object)"# SetShipReadyToLandPatch() begins #");
			if (day > -1)
			{
				int thisClientPlayerId = StartOfRound.Instance.thisClientPlayerId;
				PlayerControllerB val = StartOfRound.Instance.allPlayerScripts[thisClientPlayerId];
				Object.FindObjectOfType<StartMatchLever>().triggerScript.disabledHoverTip = "You need to eliminate someone !";
				Object.FindObjectOfType<StartMatchLever>().triggerScript.interactable = false;
				((MonoBehaviour)TimeOfDay.Instance).StartCoroutine(waitTime(60f));
			}
			if (day < 2)
			{
				day++;
			}
			else
			{
				day = -1;
				impostorID = -1;
			}
			ImpostorModBase.Instance.mls.LogInfo((object)("# NEW DAY : #" + day));
			ImpostorModBase.Instance.mls.LogInfo((object)"# SetShipReadyToLandPatch() ends #");
		}

		public static IEnumerator waitTime(float timeToWait)
		{
			ImpostorModBase.Instance.mls.LogInfo((object)"# waitTime() begins #");
			int thisClientPlayerId = StartOfRound.Instance.thisClientPlayerId;
			_ = StartOfRound.Instance.allPlayerScripts[thisClientPlayerId];
			HUDManager.Instance.DisplayTip("You need to vote for who you suspect.", "", false, false, "LC_Tip1");
			ImpostorModBase.Instance.isVoting = true;
			yield return (object)new WaitForSeconds(timeToWait);
			ImpostorModBase.Instance.hasVotedList.Clear();
			ImpostorModBase.Instance.isVoting = false;
			mostVotedPlayer = ImpostorModBase.Instance.votedPlayers.FirstOrDefault((KeyValuePair<string, int> x) => x.Value == ImpostorModBase.Instance.votedPlayers.Values.Max()).Key;
			HUDManager.Instance.DisplayTip("MOST VOTED PLAYER : " + mostVotedPlayer, "", false, false, "LC_Tip1");
			KillPlayer(mostVotedPlayer);
			mostVotedPlayer = "";
			Object.FindObjectOfType<StartMatchLever>().triggerScript.interactable = true;
			ImpostorModBase.Instance.mls.LogInfo((object)"# waitTime() ends #");
		}

		public static void KillPlayer(string username)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			PlayerControllerB[] array = allPlayerScripts;
			foreach (PlayerControllerB val in array)
			{
				if (val.playerUsername == username)
				{
					StartOfRound.Instance.allowLocalPlayerDeath = true;
					val.playersManager.shipDoorsEnabled = true;
					val.KillPlayer(default(Vector3), true, (CauseOfDeath)0, 0, default(Vector3));
				}
			}
		}

		private static void DisplayRoles()
		{
			ImpostorModBase.Instance.mls.LogInfo((object)"# DisplayRoles() begins #");
			int thisClientPlayerId = StartOfRound.Instance.thisClientPlayerId;
			PlayerControllerB val = StartOfRound.Instance.allPlayerScripts[thisClientPlayerId];
			if (impostorID == thisClientPlayerId)
			{
				HUDManager.Instance.DisplayTip("You are the IMPOSTOR !", "Good Luck.", true, false, "LC_Tip1");
			}
			else
			{
				HUDManager.Instance.DisplayTip("You are a CREWMATE.", "Good Luck.", false, false, "LC_Tip1");
			}
			DisplayRolesFixed();
			ImpostorModBase.Instance.mls.LogInfo((object)"# DisplayRoles() ends #");
		}

		private static void PickImpostor()
		{
			ImpostorModBase.Instance.mls.LogInfo((object)"# PickImposor() begins #");
			List<int> connectedPlayerIds = GetConnectedPlayerIds();
			ImpostorModBase.Instance.mls.LogInfo((object)("# LONGUEUR LISTE : #" + connectedPlayerIds.Count));
			Random random = new Random(StartOfRound.Instance.randomMapSeed + 2);
			int index = random.Next(connectedPlayerIds.Count);
			ImpostorModBase.Instance.mls.LogInfo((object)("# INDEX : #" + index));
			impostorID = connectedPlayerIds[index];
			ImpostorModBase.Instance.mls.LogInfo((object)("# IMPOSTOR ID  : #" + impostorID));
			isImpostorPicked = true;
			ImpostorModBase.Instance.mls.LogInfo((object)"Impostor picked");
			ImpostorModBase.Instance.mls.LogInfo((object)"# PickImposor() ends #");
		}

		public static List<int> GetConnectedPlayerIds()
		{
			ImpostorModBase.Instance.mls.LogInfo((object)"# GetConnectedPlayerIds() begins #");
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			List<int> list = new List<int>();
			for (int i = 0; i < allPlayerScripts.Length; i++)
			{
				if (allPlayerScripts[i].isPlayerDead || allPlayerScripts[i].isPlayerControlled)
				{
					ImpostorModBase.Instance.mls.LogInfo((object)("# JOUEUR AJOUTE :  #" + i));
					list.Add(i);
				}
			}
			ImpostorModBase.Instance.mls.LogInfo((object)"# GetConnectedPlayerIds() ends #");
			return list;
		}

		[HarmonyPatch(typeof(PlayerControllerB), "DamagePlayerFromOtherClientServerRpc")]
		[HarmonyPrefix]
		public static void DamagePlayerFromOtherClientServerRpc(ref int damageAmount, Vector3 hitDirection, int playerWhoHit)
		{
			damageAmount = 0;
		}

		[HarmonyPatch(typeof(PlayerControllerB), "Update")]
		[HarmonyPostfix]
		private static void infiniteSprintPatch(ref float ___sprintMeter)
		{
			int thisClientPlayerId = StartOfRound.Instance.thisClientPlayerId;
			PlayerControllerB val = StartOfRound.Instance.allPlayerScripts[thisClientPlayerId];
			if (impostorID == thisClientPlayerId)
			{
				___sprintMeter = 1f;
			}
		}
	}
}