Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Terminal Tools v1.0.2
LethalAPI.Terminal.dll
Decompiled 2 years agousing 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.Helpers; 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.Events; 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.0")] [assembly: AssemblyInformationalVersion("1.0.0+3abb9ab9dd16dac2bfc11bf6e7b10e642706a0c8")] [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.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.Attributes { public class InverseTeleporterUnlockedAttribute : AccessControlAttribute { public override bool CheckAllowed() { if ((Object)(object)StartOfRound.Instance == (Object)null) { return false; } return StartOfRound.Instance.SpawnedShipUnlockables.ContainsKey(6); } } public class TeleporterUnlockedAttribute : AccessControlAttribute { public override bool CheckAllowed() { if ((Object)(object)StartOfRound.Instance == (Object)null) { return false; } return StartOfRound.Instance.SpawnedShipUnlockables.ContainsKey(5); } } } namespace LethalAPI.LibTerminal { [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; } } 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; } } [BepInPlugin("LethalAPI.Terminal", "LethalAPI.Terminal", "1.0.0")] public class TerminalLibPlugin : BaseUnityPlugin { private Harmony HarmonyInstance = new Harmony("LethalAPI.Terminal"); private TerminalModRegistry Terminal; private void Awake() { ((BaseUnityPlugin)this).Logger.LogInfo((object)"LethalAPI.Terminal is loading..."); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Installing patches"); HarmonyInstance.PatchAll(typeof(TerminalLibPlugin).Assembly); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Registering built-in Commands"); Terminal = TerminalRegistry.CreateTerminalRegistry(); Terminal.RegisterFrom<CommandInfoCommands>(); Terminal.RegisterFrom<CheatCommands>(); Terminal.RegisterFrom<EntityCommands>(); Terminal.RegisterFrom<EquipmentCommands>(); Terminal.RegisterFrom<InfoCommands>(); Terminal.RegisterFrom<PlayerCommands>(); Terminal.RegisterFrom<ShipCommands>(); Object.DontDestroyOnLoad((Object)(object)this); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Plugin LethalAPI.Terminal is loaded!"); } } public static class TerminalNodeExtensions { public static TerminalNode WithDisplayText(this TerminalNode node, object displayText) { node.displayText = displayText.ToString(); 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 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), "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) { modifiedDisplayText = modifiedDisplayText.TrimStart('\n', ' ').SetEndPadding('\n', 2); 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) { if (terminalInterface.APITextPostProcessing) { modifiedDisplayText = modifiedDisplayText.TrimStart('\n', ' ').SetEndPadding('\n', 2); } 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) { TerminalCommandAttribute commandAttribute = method.GetCustomAttribute<TerminalCommandAttribute>(); ParameterInfo[] parameters = method.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 var service)) { values[i] = service; } else if (parameterType == typeof(string) && parameterInfo.GetCustomAttribute<RemainingTextAttribute>() != null) { if (!arguments.TryReadRemaining(out var result)) { return false; } values[i] = result; } else { if (!arguments.TryReadNext(parameterType, out var value)) { return false; } values[i] = value; } } arguments.Reset(); invoker = (object instance) => ExecuteCommand(method, 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) { List<(TerminalCommand, Func<TerminalNode>)> list = new List<(TerminalCommand, Func<TerminalNode>)>(); TerminalCommand[] array = TerminalRegistry.GetCommands(commandName).ToArray(); ServiceCollection services = new ServiceCollection(arguments, arguments.Arguments, terminal); foreach (TerminalCommand terminalCommand in array) { if (!terminalCommand.CheckAllowed()) { continue; } arguments.Reset(); if (terminalCommand.TryCreateInvoker(arguments, services, out var invoker)) { Func<TerminalNode> item = () => HandleCommandResult(invoker(), terminal); 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) { if ((Object)(object)StartOfRound.Instance == (Object)null) { throw new ArgumentException("Game has not started"); } PlayerControllerB val = null; if (ulong.TryParse(value, 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(value, StringComparison.InvariantCultureIgnoreCase) != -1)); } if ((Object)(object)val == (Object)null) { throw new ArgumentException("Failed to find player"); } return val; } } 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; public static ConcurrentDictionary<Type, StringConversionHandler> StringConverters { get; } = new ConcurrentDictionary<Type, StringConversionHandler>(); public static bool TryConvert(string value, Type type, out object result) { if (!m_Initialized) { m_Initialized = true; RegisterFromType(typeof(DefaultStringConverters), null, replaceExisting: false); } if (!StringConverters.TryGetValue(type, out var value2)) { result = null; return false; } try { result = value2(value); return true; } catch (Exception) { } result = null; return false; } public static void RegisterFrom<T>(T instance, bool replaceExisting = true) where T : class { RegisterFromType(typeof(T), instance, replaceExisting); } public static void RegisterFromType(Type type, object instance = null, bool replaceExisting = true) { 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 value2 = (string value) => method.Invoke(instance, new object[1] { value }); if (replaceExisting || !StringConverters.ContainsKey(returnType)) { StringConverters[returnType] = value2; } } } } } 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; } 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 var activatedInvoker)) { invoker = () => activatedInvoker(Instance); return true; } invoker = null; return false; } } public class TerminalModRegistry { public List<TerminalCommand> Commands { get; } = new List<TerminalCommand>(); 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); } } StringConverter.RegisterFrom(instance); return instance; } public void Deregister() { if (Commands != null) { for (int i = 0; i < Commands.Count; i++) { TerminalRegistry.Deregister(Commands[i]); } } } } 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(); foreach (MethodInfo commandMethod in GetCommandMethods<T>()) { TerminalCommand terminalCommand = TerminalCommand.FromMethod(commandMethod, instance); RegisterCommand(terminalCommand); terminalModRegistry.Commands.Add(terminalCommand); } StringConverter.RegisterFrom(instance); return terminalModRegistry; } public static TerminalModRegistry CreateTerminalRegistry() { return new TerminalModRegistry(); } public static void RegisterCommand(TerminalCommand command) { if (!m_RegisteredCommands.TryGetValue(command.Name, out var 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 var value)) { return; } lock (value) { value.Remove(command); } } public static IReadOnlyList<TerminalCommand> GetCommands(string commandName) { if (m_RegisteredCommands.TryGetValue(commandName, out var value)) { return value; } return new List<TerminalCommand>(); } public static IEnumerable<TerminalCommand> EnumerateCommands(string name) { if (!m_RegisteredCommands.TryGetValue(name, out var 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 TextUtil { 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 (CommandActivator.TryCreateInvoker(arguments, Services, ConfirmHandler.GetMethodInfo(), out var invoker)) { 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 var invoker)) { return invoker(DenyHandler.Target); } return null; } private void PostprocessPrompt() { 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 var invoker)) { object obj = invoker(handler.Target); if (obj != null) { return obj; } } } return null; } } } namespace LethalAPI.LibTerminal.Helpers { internal static class MiscHelper { public static string Buffer(string input) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine(); stringBuilder.AppendLine(); stringBuilder.AppendLine(); stringBuilder.AppendLine(input); Console.WriteLine(stringBuilder.ToString()); return stringBuilder.ToString(); } public static bool IsGameStarted() { return Object.FindFirstObjectByType<StartMatchLever>().leverHasBeenPulled; } public static string GetObjectProperties(object obj) { if (obj == null) { return "The provided object is null."; } StringBuilder stringBuilder = new StringBuilder(); Type type = obj.GetType(); stringBuilder.AppendLine("Properties of " + type.Name + ":"); PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (PropertyInfo propertyInfo in properties) { try { object value = propertyInfo.GetValue(obj, null); stringBuilder.AppendLine($"{propertyInfo.Name}: {value}"); } catch (Exception ex) { stringBuilder.AppendLine(propertyInfo.Name + ": Could not read value (" + ex.Message + ")"); } } return stringBuilder.ToString(); } } internal static class TimeHelper { public static string GetTime() { if (MiscHelper.IsGameStarted()) { return DecimalToTimeString(TimeOfDay.Instance.normalizedTimeOfDay); } return null; } public static string DecimalToTimeString(float time) { int num = 8; int num2 = 1080; int num3 = (int)(time * (float)num2); return DateTime.Today.AddHours(num).AddMinutes(num3).AddHours(-2.0) .ToString("h:mm tt"); } } } namespace LethalAPI.LibTerminal.Commands { internal class CheatCommands { [TerminalCommand("GiveMoney", true, false)] [CommandInfo("Makes you a dirty cheater", "[Amount]")] public string GiveMoneyCommand(int amount) { Terminal obj = Object.FindObjectOfType<Terminal>(); obj.groupCredits += amount; return MiscHelper.Buffer($"${amount} was added, cheater!"); } } public class CommandInfoCommands { [TerminalCommand("Other", true, true)] public string CommandList() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine(MiscHelper.Buffer("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("Ping the designated radar booster"); 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(); Assembly executingAssembly = Assembly.GetExecutingAssembly(); string namespaceName = GetType().Namespace; foreach (Type item in from t in executingAssembly.GetTypes() where string.Equals(t.Namespace, namespaceName, StringComparison.Ordinal) select t) { MethodInfo[] methods = item.GetMethods(BindingFlags.Instance | BindingFlags.Public); foreach (MethodInfo methodInfo in methods) { TerminalCommandAttribute customAttribute = methodInfo.GetCustomAttribute<TerminalCommandAttribute>(); if (customAttribute == null || (methodInfo.DeclaringType == GetType() && methodInfo.Name.Equals("CommandList", StringComparison.OrdinalIgnoreCase)) || !customAttribute.ShowHelp) { continue; } string text = customAttribute.CommandName ?? methodInfo.Name; CommandInfoAttribute customAttribute2 = methodInfo.GetCustomAttribute<CommandInfoAttribute>(); stringBuilder.AppendLine("> " + text.ToUpper()); if (customAttribute2 != null) { if (!string.IsNullOrWhiteSpace(customAttribute2.Syntax)) { stringBuilder.AppendLine(customAttribute2.Description + " - " + customAttribute2.Syntax); } else { stringBuilder.AppendLine(customAttribute2.Description); } } stringBuilder.AppendLine(); } } return stringBuilder.ToString(); } [TerminalCommand("Help", false, true)] [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(); } } internal class EntityCommands { [TerminalCommand("ListMines", true, true)] [CommandInfo("Lists all landmines", "")] public string ListMines() { return ListObjectsByName("Landmine"); } [TerminalCommand("ListTurrets", true, true)] [CommandInfo("Lists all turrets", "")] public string ListTurrets() { return ListObjectsByName("TurretScript"); } [TerminalCommand("ListDoors", true, true)] [CommandInfo("Lists all facility doors", "")] public string ListDoors() { return ListObjectsByName("BigDoor", startsWith: true); } [TerminalCommand("Detonate", true, true)] [CommandInfo("Detonates designated landmine(s)", "[All / Mine Id]")] public string DetonateLandmineCommand(string code) { if (string.IsNullOrEmpty(code)) { return MiscHelper.Buffer("No valid identifier provided!"); } TerminalAccessibleObject[] source = Object.FindObjectsOfType<TerminalAccessibleObject>(); Landmine[] array = Object.FindObjectsOfType<Landmine>(); TerminalAccessibleObject selectedObject = source.Where((TerminalAccessibleObject tao) => tao.objectCode.ToLower() == code.ToLower()).FirstOrDefault(); if ((Object)(object)selectedObject != (Object)null) { Landmine val = array.Where((Landmine l) => ((NetworkBehaviour)l).NetworkObjectId == ((NetworkBehaviour)selectedObject).NetworkObjectId).FirstOrDefault(); if (val.hasExploded) { return MiscHelper.Buffer(code + " has already detonated!"); } val.ExplodeMineServerRpc(); val.Detonate(); return MiscHelper.Buffer("Kaboom! " + code + " has been detonated!"); } if (code.ToLower() == "all") { int num = 0; Landmine[] array2 = array; foreach (Landmine val2 in array2) { if (!val2.hasExploded) { num++; val2.ExplodeMineServerRpc(); val2.Detonate(); } } if (num > 0) { return MiscHelper.Buffer($"Kaboom! Detonating {num} landmines!"); } return MiscHelper.Buffer("There were no mines to detonate!"); } return MiscHelper.Buffer("Landmine " + code + " not found!"); } [TerminalCommand("Berserk", true, true)] [CommandInfo("Makes designated turret(s) go berserk", "[All / Turret Id]")] public string TurretBerserkCommand(string code) { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Invalid comparison between Unknown and I4 if (string.IsNullOrEmpty(code)) { return MiscHelper.Buffer("No valid identifier provided!"); } int num = (int)GameNetworkManager.Instance.localPlayerController.playerClientId; TerminalAccessibleObject[] source = Object.FindObjectsOfType<TerminalAccessibleObject>(); Turret[] array = Object.FindObjectsOfType<Turret>(); TerminalAccessibleObject selectedObject = source.Where((TerminalAccessibleObject tao) => tao.objectCode.ToLower() == code.ToLower()).FirstOrDefault(); if ((Object)(object)selectedObject != (Object)null) { Turret val = array.Where((Turret l) => ((NetworkBehaviour)l).NetworkObjectId == ((NetworkBehaviour)selectedObject).NetworkObjectId).FirstOrDefault(); if ((int)val.turretMode == 3) { val.SetToModeClientRpc(0); return MiscHelper.Buffer(code + " has calmed down."); } val.EnterBerserkModeServerRpc(num); return MiscHelper.Buffer("Danger! " + code + " has gone berserk!"); } if (code.ToLower() == "all") { int num2 = 0; Turret[] array2 = array; foreach (Turret obj in array2) { num2++; obj.EnterBerserkModeServerRpc(num); } if (num2 > 0) { return MiscHelper.Buffer($"Danger! {num2} turrets have gone berserk!"); } return MiscHelper.Buffer("There were no turrets found!"); } return MiscHelper.Buffer("Turret " + code + " not found!"); } private string ListObjectsByName(string objectName, bool startsWith = false) { StringBuilder stringBuilder = new StringBuilder(); TerminalAccessibleObject[] array = Object.FindObjectsOfType<TerminalAccessibleObject>(); foreach (TerminalAccessibleObject val in array) { if (startsWith ? ((Object)val).name.StartsWith(objectName) : (((Object)val).name == objectName)) { stringBuilder.AppendLine(val.objectCode); } } return MiscHelper.Buffer(stringBuilder.ToString()); } } internal class EquipmentCommands { [TerminalCommand("Teleport", false, false)] [CommandInfo("Teleport the specified member of the crew", "[Player Name]")] public string TeleportCommand() { return TeleportCommand(null); } [TerminalCommand("Teleport", false, true)] [CommandInfo("Teleport the specified member of the crew", "[Player Name]")] public string TeleportCommand(PlayerControllerB player = null) { Console.WriteLine("Teleporting"); ShipTeleporter teleporter = FindTeleporter(isInverse: false); string text = CanUseTeleporter(teleporter); if (text != null) { return MiscHelper.Buffer(text); } if ((Object)(object)player == (Object)null && (Object)(object)StartOfRound.Instance.mapScreen.targetedPlayer == (Object)null) { return MiscHelper.Buffer("No player selected or found to teleport."); } if ((Object)(object)player == (Object)null) { player = StartOfRound.Instance.mapScreen.targetedPlayer; Teleport(teleporter); } else { PlayerControllerB targetedPlayer = StartOfRound.Instance.mapScreen.targetedPlayer; StartOfRound.Instance.mapScreen.targetedPlayer = player; Teleport(teleporter); StartOfRound.Instance.mapScreen.targetedPlayer = targetedPlayer; } return MiscHelper.Buffer("Teleporting " + player.playerUsername + "..."); } [TerminalCommand("Inverse", false, true)] [CommandInfo("Start the inverse teleporter", "")] public string InverseTeleportCommand() { Console.WriteLine("Inverse Teleporting"); ShipTeleporter teleporter = FindTeleporter(isInverse: true); string text = CanUseTeleporter(teleporter); if (text != null) { return MiscHelper.Buffer(text); } Teleport(teleporter); return MiscHelper.Buffer("Have a safe trip!"); } [TerminalCommand("ResetInverse", false, true)] [CommandInfo("Resets the inverse teleporter cooldown. DO NOT ABUSE.", "")] public string ResetInverseCommand() { ShipTeleporter teleporter = FindTeleporter(isInverse: true); ResetCooldown(teleporter); return MiscHelper.Buffer("Inverse Teleporter Cooldown reset."); } [TerminalCommand("Reset", false, false)] [CommandInfo("Shortcut for ResetInverse.", "")] public string ResetCommand() { return ResetInverseCommand(); } [TerminalCommand("Scramble", false, true)] [CommandInfo("Reset then activate inverse teleporter.", "")] public string ScrambleCommand() { ResetInverseCommand(); return InverseTeleportCommand(); } private ShipTeleporter FindTeleporter(bool isInverse) { return ((IEnumerable<ShipTeleporter>)Object.FindObjectsOfType<ShipTeleporter>()).FirstOrDefault((Func<ShipTeleporter, bool>)((ShipTeleporter t) => t.isInverseTeleporter == isInverse)); } private void Teleport(ShipTeleporter teleporter) { ((UnityEvent<PlayerControllerB>)(object)teleporter.buttonTrigger.onInteract).Invoke(GameNetworkManager.Instance.localPlayerController); } private void ResetCooldown(ShipTeleporter teleporter) { teleporter.buttonTrigger.currentCooldownValue = 0f; teleporter.buttonTrigger.interactable = true; } private string CanUseTeleporter(ShipTeleporter teleporter) { if (teleporter == null) { return "There is no teleporter to use."; } if (!teleporter.buttonTrigger.interactable) { int privateFieldValue = GetPrivateFieldValue<int>(teleporter, "cooldownTime"); if (privateFieldValue != 0) { Console.WriteLine("Cooldown Time: " + privateFieldValue); return $"The teleporter is on cooldown for {privateFieldValue}."; } Console.WriteLine("Field 'cooldownTime' not found."); return "The teleporter is on cooldown."; } return null; } private T GetPrivateFieldValue<T>(object instance, string fieldName) { FieldInfo field = instance.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { return (T)field.GetValue(instance); } return default(T); } } internal class InfoCommands { [TerminalCommand("Time", true, true)] [CommandInfo("Tells time", "")] public string TimeCommand() { string time = TimeHelper.GetTime(); if (string.IsNullOrEmpty(time)) { return MiscHelper.Buffer("There are no clocks in interstellar space."); } return MiscHelper.Buffer("Time: " + time); } [TerminalCommand("Status", true, true)] [CommandInfo("Get status of all crewmembers.", "")] public string StatusCommand() { PlayerControllerB[] array = Object.FindObjectsOfType<PlayerControllerB>(); StringBuilder stringBuilder = new StringBuilder(); PlayerControllerB[] array2 = array; foreach (PlayerControllerB val in array2) { if (!val.playerUsername.StartsWith("Player #")) { stringBuilder.AppendLine(BuildPlayerStatus(val)); } } return MiscHelper.Buffer(stringBuilder.ToString()); } [TerminalCommand("Status", true, true)] [CommandInfo("Get status of selected crewmembers.", "[Player name]")] public string StatusCommand(PlayerControllerB selectedPlayer) { return MiscHelper.Buffer(BuildPlayerStatus(selectedPlayer)); } [TerminalCommand("Clear", true, true)] [CommandInfo("Clear the console.", "")] public string ClearCommand() { return MiscHelper.Buffer(""); } private string BuildPlayerStatus(PlayerControllerB player) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("/// " + player.playerUsername + " ///"); stringBuilder.AppendLine((!player.isPlayerDead) ? $"Health: {player.health}%" : $"Cause of death: {player.causeOfDeath}"); stringBuilder.AppendLine(); return stringBuilder.ToString(); } } internal class PlayerCommands { } internal class ShipCommands { [TerminalCommand("Lights", false, true)] [CommandInfo("Toggle the lights.", "")] public string LightsCommand() { ((UnityEvent<PlayerControllerB>)(object)GameObject.Find("LightSwitch").GetComponent<InteractTrigger>().onInteract).Invoke(GameNetworkManager.Instance.localPlayerController); return MiscHelper.Buffer("Toggled lights."); } [TerminalCommand("Doors", false, false)] public string ToggleDoorsCommand() { return ToggleDoorCommand(); } [TerminalCommand("Door", false, true)] [CommandInfo("Toggle the door", "")] public string ToggleDoorCommand() { if (!StartOfRound.Instance.hangarDoorsClosed) { return CloseCommand(); } return OpenCommand(); } [TerminalCommand("Close", false, true)] [CommandInfo("Close the door", "")] public string CloseCommand() { Console.WriteLine("Closing Door"); string text = CanUseDoor(); if (text != null) { return text; } TriggerInteract("StopButton"); HangarShipDoor val = FindHangarShipDoor(); val.PlayDoorAnimation(true); return "Closing Door. Door power: " + GetDoorPower(val.doorPower); } [TerminalCommand("Open", false, true)] [CommandInfo("Open the door", "")] public string OpenCommand() { Console.WriteLine("Opening Door"); string text = CanUseDoor(); if (text != null) { return text; } TriggerInteract("StartButton"); HangarShipDoor val = FindHangarShipDoor(); val.PlayDoorAnimation(false); return "Opening Door. Door power: " + GetDoorPower(val.doorPower); } [TerminalCommand("Launch", false, true)] [CommandInfo("Launch the ship", "")] public string LaunchCommand() { if (!StartOfRound.Instance.shipHasLanded) { return MiscHelper.Buffer("You need to land first!"); } InteractTrigger componentInChildren = GameObject.Find("StartGameLever").GetComponentInChildren<InteractTrigger>(); StartMatchLever val = Object.FindObjectOfType<StartMatchLever>(); ((UnityEvent<PlayerControllerB>)(object)componentInChildren.onInteract).Invoke(GameNetworkManager.Instance.localPlayerController); val.EndGame(); val.PlayLeverPullEffectsServerRpc(false); return MiscHelper.Buffer("Launching the ship..."); } [TerminalCommand("GTFO", false, false)] [CommandInfo("GET ME OUTTA HERE!", "")] public string GTFOCommand() { LaunchCommand(); return MiscHelper.Buffer("RUN AWAY!!!1!!"); } [TerminalCommand("Land", false, true)] [CommandInfo("Land the ship", "")] public string LandCommand() { if (StartOfRound.Instance.shipHasLanded) { return MiscHelper.Buffer("You're already on the ground, dummy!"); } string text = CanStartGame(); if (!string.IsNullOrEmpty(text)) { return MiscHelper.Buffer(text); } Console.WriteLine("Landing"); InteractTrigger componentInChildren = GameObject.Find("StartGameLever").GetComponentInChildren<InteractTrigger>(); StartMatchLever val = Object.FindObjectOfType<StartMatchLever>(); ((UnityEvent<PlayerControllerB>)(object)componentInChildren.onInteract).Invoke(GameNetworkManager.Instance.localPlayerController); val.StartGame(); val.PlayLeverPullEffectsServerRpc(true); return MiscHelper.Buffer("Landing the ship..."); } private string CanStartGame() { GameNetworkManager val = ((IEnumerable<GameNetworkManager>)Object.FindObjectsOfType<GameNetworkManager>()).FirstOrDefault((Func<GameNetworkManager, bool>)((GameNetworkManager x) => (Object)(object)x.localPlayerController != (Object)null)); if (val.gameHasStarted) { return null; } if (val.isHostingGame) { return null; } return "The host must be the one to start the game!"; } private string CanUseDoor() { if (MiscHelper.IsGameStarted()) { return null; } return "You really shouldn't mess with the doors while in orbit."; } private string GetDoorPower(float doorPower) { return $"{doorPower * 100f:0\\%}"; } private void TriggerInteract(string buttonName) { ((UnityEvent<PlayerControllerB>)(object)GameObject.Find(buttonName).GetComponentInChildren<InteractTrigger>().onInteract).Invoke(GameNetworkManager.Instance.localPlayerController); } private HangarShipDoor FindHangarShipDoor() { return Object.FindObjectsOfType<HangarShipDoor>().FirstOrDefault(); } } } 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 bool ShowHelp { get; } public TerminalCommandAttribute(string name, bool clearText = false, bool showHelp = true) { CommandName = name; ClearText = clearText; ShowHelp = showHelp; } } }