Please disclose if your mod was created primarily 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 Interactive Terminal API v1.3.2
BepInEx/plugins/InteractiveTerminalAPI/InteractiveTerminalAPI.dll
Decompiled 2 months agousing System; using System.Collections; 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 BepInEx; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using InteractiveTerminalAPI.Compat; using InteractiveTerminalAPI.Extensions; using InteractiveTerminalAPI.Input; using InteractiveTerminalAPI.Misc.Util; using InteractiveTerminalAPI.NetcodePatcher; using InteractiveTerminalAPI.Patches.TerminalComponents; using InteractiveTerminalAPI.UI; using InteractiveTerminalAPI.UI.Application; using InteractiveTerminalAPI.UI.Cursor; using InteractiveTerminalAPI.UI.Hierarchy; using InteractiveTerminalAPI.UI.Page; using InteractiveTerminalAPI.UI.Screen; using InteractiveTerminalAPI.Util; using LethalCompanyInputUtils.Api; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] [module: NetcodePatchedAssembly] internal class <Module> { static <Module>() { } } 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 InteractiveTerminalAPI { [BepInPlugin("WhiteSpike.InteractiveTerminalAPI", "Interactive Terminal API", "1.3.2")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { internal static readonly Harmony harmony = new Harmony("WhiteSpike.InteractiveTerminalAPI"); internal static readonly ManualLogSource mls = Logger.CreateLogSource("Interactive Terminal API"); private void Awake() { InputUtils_Compat.Init(); PatchMainVersion(); mls.LogInfo((object)"Interactive Terminal API 1.3.2 has been loaded successfully."); } internal static void PatchMainVersion() { PatchVitalComponents(); } private static void PatchVitalComponents() { harmony.PatchAll(typeof(Keybinds)); harmony.PatchAll(typeof(TerminalPatcher)); mls.LogInfo((object)"Game managers have been patched"); } } public static class PluginInfo { public const string PLUGIN_GUID = "InteractiveTerminalAPI"; public const string PLUGIN_NAME = "MoreShipUpgrades"; public const string PLUGIN_VERSION = "1.0.0"; } } namespace InteractiveTerminalAPI.Util { internal static class TerminalNodeUtils { private static readonly Dictionary<string, TerminalNode> retrievedTerminalNodes = new Dictionary<string, TerminalNode>(); private const string HELP_NOUN = "help"; internal static TerminalKeyword FindTerminalKeyword(string word) { TerminalKeyword val = Tools.GetTerminal().CheckForExactSentences(word); if ((Object)(object)val == (Object)null) { Plugin.mls.LogError((object)("Couldn't find terminal node for the word \"" + word + "\"")); } return val; } internal static TerminalNode GetSpecialTerminalNodeByWord(string word) { TerminalNode valueOrDefault = retrievedTerminalNodes.GetValueOrDefault(word, null); if ((Object)(object)valueOrDefault != (Object)null) { return valueOrDefault; } TerminalKeyword val = FindTerminalKeyword(word); if ((Object)(object)val == (Object)null) { return null; } valueOrDefault = FindTerminalKeyword(word).specialKeywordResult; if (!retrievedTerminalNodes.ContainsKey(word)) { retrievedTerminalNodes[word] = valueOrDefault; } return valueOrDefault; } internal static TerminalNode GetHelpTerminalNode() { return GetSpecialTerminalNodeByWord("help"); } } internal static class Tools { private static Terminal terminal; public static void FindCodeInstruction(ref int index, ref List<CodeInstruction> codes, object findValue, MethodInfo addCode, bool skip = false, bool requireInstance = false, bool notInstruction = false, bool andInstruction = false, bool orInstruction = false, string errorMessage = "Not found") { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Expected O, but got Unknown bool flag = false; while (index < codes.Count) { if (CheckCodeInstruction(codes[index], findValue)) { flag = true; if (!skip) { if (andInstruction) { codes.Insert(index + 1, new CodeInstruction(OpCodes.And, (object)null)); } if (!andInstruction && orInstruction) { codes.Insert(index + 1, new CodeInstruction(OpCodes.Or, (object)null)); } if (notInstruction) { codes.Insert(index + 1, new CodeInstruction(OpCodes.Not, (object)null)); } codes.Insert(index + 1, new CodeInstruction(OpCodes.Call, (object)addCode)); if (requireInstance) { codes.Insert(index + 1, new CodeInstruction(OpCodes.Ldarg_0, (object)null)); } } break; } index++; } if (!flag) { Plugin.mls.LogError((object)errorMessage); } index++; } public static int FindLocalField(int index, ref List<CodeInstruction> codes, int localIndex, object addCode, bool skip = false, bool store = false, bool requireInstance = false, string errorMessage = "Not found") { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown bool flag = false; while (index < codes.Count) { if (CheckCodeInstruction(codes[index], localIndex, store)) { flag = true; if (!skip) { codes.Insert(index + 1, new CodeInstruction(OpCodes.Call, addCode)); if (requireInstance) { codes.Insert(index + 1, new CodeInstruction(OpCodes.Ldarg_0, (object)null)); } } break; } index++; } if (!flag) { Plugin.mls.LogError((object)errorMessage); } return index + 1; } public static void FindString(ref int index, ref List<CodeInstruction> codes, string findValue, MethodInfo addCode = null, bool skip = false, bool notInstruction = false, bool andInstruction = false, bool orInstruction = false, bool requireInstance = false, string errorMessage = "Not found") { FindCodeInstruction(ref index, ref codes, findValue, addCode, skip, requireInstance, notInstruction, andInstruction, orInstruction, errorMessage); } public static void FindField(ref int index, ref List<CodeInstruction> codes, FieldInfo findField, MethodInfo addCode = null, bool skip = false, bool notInstruction = false, bool andInstruction = false, bool orInstruction = false, bool requireInstance = false, string errorMessage = "Not found") { FindCodeInstruction(ref index, ref codes, findField, addCode, skip, requireInstance, notInstruction, andInstruction, orInstruction, errorMessage); } public static void FindMethod(ref int index, ref List<CodeInstruction> codes, MethodInfo findMethod, MethodInfo addCode = null, bool skip = false, bool notInstruction = false, bool andInstruction = false, bool orInstruction = false, bool requireInstance = false, string errorMessage = "Not found") { FindCodeInstruction(ref index, ref codes, findMethod, addCode, skip, requireInstance, notInstruction, andInstruction, orInstruction, errorMessage); } public static void FindFloat(ref int index, ref List<CodeInstruction> codes, float findValue, MethodInfo addCode = null, bool skip = false, bool notInstruction = false, bool andInstruction = false, bool orInstruction = false, bool requireInstance = false, string errorMessage = "Not found") { FindCodeInstruction(ref index, ref codes, findValue, addCode, skip, requireInstance, notInstruction, andInstruction, orInstruction, errorMessage); } public static void FindInteger(ref int index, ref List<CodeInstruction> codes, sbyte findValue, MethodInfo addCode = null, bool skip = false, bool notInstruction = false, bool andInstruction = false, bool orInstruction = false, bool requireInstance = false, string errorMessage = "Not found") { FindCodeInstruction(ref index, ref codes, findValue, addCode, skip, requireInstance, notInstruction, andInstruction, orInstruction, errorMessage); } public static void FindSub(ref int index, ref List<CodeInstruction> codes, MethodInfo addCode = null, bool skip = false, bool notInstruction = false, bool andInstruction = false, bool orInstruction = false, bool requireInstance = false, string errorMessage = "Not found") { object findValue = OpCodes.Sub; bool notInstruction2 = notInstruction; bool andInstruction2 = andInstruction; bool orInstruction2 = orInstruction; FindCodeInstruction(ref index, ref codes, findValue, addCode, skip, requireInstance, notInstruction2, andInstruction2, orInstruction2, errorMessage); } public static void FindDiv(ref int index, ref List<CodeInstruction> codes, MethodInfo addCode = null, bool skip = false, bool notInstruction = false, bool andInstruction = false, bool orInstruction = false, bool requireInstance = false, string errorMessage = "Not found") { object findValue = OpCodes.Div; bool notInstruction2 = notInstruction; bool andInstruction2 = andInstruction; bool orInstruction2 = orInstruction; FindCodeInstruction(ref index, ref codes, findValue, addCode, skip, requireInstance, notInstruction2, andInstruction2, orInstruction2, errorMessage); } public static void FindAdd(ref int index, ref List<CodeInstruction> codes, MethodInfo addCode = null, bool skip = false, bool notInstruction = false, bool andInstruction = false, bool orInstruction = false, bool requireInstance = false, string errorMessage = "Not found") { object findValue = OpCodes.Add; bool notInstruction2 = notInstruction; bool andInstruction2 = andInstruction; bool orInstruction2 = orInstruction; FindCodeInstruction(ref index, ref codes, findValue, addCode, skip, requireInstance, notInstruction2, andInstruction2, orInstruction2, errorMessage); } public static void FindMul(ref int index, ref List<CodeInstruction> codes, MethodInfo addCode = null, bool skip = false, bool notInstruction = false, bool andInstruction = false, bool orInstruction = false, bool requireInstance = false, string errorMessage = "Not found") { object findValue = OpCodes.Mul; bool notInstruction2 = notInstruction; bool andInstruction2 = andInstruction; bool orInstruction2 = orInstruction; FindCodeInstruction(ref index, ref codes, findValue, addCode, skip, requireInstance, notInstruction2, andInstruction2, orInstruction2, errorMessage); } private static bool CheckCodeInstruction(CodeInstruction code, int localIndex, bool store = false) { if (!store) { switch (localIndex) { case 0: return code.opcode == OpCodes.Ldloc_0; case 1: return code.opcode == OpCodes.Ldloc_1; case 2: return code.opcode == OpCodes.Ldloc_2; case 3: return code.opcode == OpCodes.Ldloc_3; default: if (code.opcode == OpCodes.Ldloc) { return (int)code.operand == localIndex; } return false; } } switch (localIndex) { case 0: return code.opcode == OpCodes.Stloc_0; case 1: return code.opcode == OpCodes.Stloc_1; case 2: return code.opcode == OpCodes.Stloc_2; case 3: return code.opcode == OpCodes.Stloc_3; default: if (code.opcode == OpCodes.Stloc) { return (int)code.operand == localIndex; } return false; } } private static bool CheckCodeInstruction(CodeInstruction code, object findValue) { if (findValue is sbyte) { return CheckIntegerCodeInstruction(code, findValue); } if (findValue is float) { return code.opcode == OpCodes.Ldc_R4 && code.operand.Equals(findValue); } if (findValue is string) { if (code.opcode == OpCodes.Ldstr) { return code.operand.Equals(findValue); } return false; } if (findValue is MethodInfo) { if (code.opcode == OpCodes.Call || code.opcode == OpCodes.Callvirt) { return code.operand == findValue; } return false; } if (findValue is FieldInfo) { if (code.opcode == OpCodes.Ldfld || code.opcode == OpCodes.Stfld) { return code.operand == findValue; } return false; } if (findValue is OpCode) { return code.opcode == (OpCode)findValue; } return false; } private static bool CheckIntegerCodeInstruction(CodeInstruction code, object findValue) { switch ((sbyte)findValue) { case 0: return code.opcode == OpCodes.Ldc_I4_0; case 1: return code.opcode == OpCodes.Ldc_I4_1; case 2: return code.opcode == OpCodes.Ldc_I4_2; case 3: return code.opcode == OpCodes.Ldc_I4_3; case 4: return code.opcode == OpCodes.Ldc_I4_4; case 5: return code.opcode == OpCodes.Ldc_I4_5; case 6: return code.opcode == OpCodes.Ldc_I4_6; case 7: return code.opcode == OpCodes.Ldc_I4_7; case 8: return code.opcode == OpCodes.Ldc_I4_8; default: if (code.opcode == OpCodes.Ldc_I4_S) { return code.operand.Equals(findValue); } return false; } } public static void ShuffleList<T>(List<T> list) { if (list == null) { throw new ArgumentNullException("list"); } Random random = new Random(); int num = list.Count; while (num > 1) { num--; int index = random.Next(num + 1); T value = list[index]; list[index] = list[num]; list[num] = value; } } public static bool SpawnMob(string mob, Vector3 position, int numToSpawn) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < RoundManager.Instance.currentLevel.Enemies.Count; i++) { if (RoundManager.Instance.currentLevel.Enemies[i].enemyType.enemyName == mob) { for (int j = 0; j < numToSpawn; j++) { RoundManager.Instance.SpawnEnemyOnServer(position, 0f, i); } return true; } } return false; } internal static string GenerateInfoForUpgrade(string infoFormat, int initialPrice, int[] incrementalPrices, Func<int, float> infoFunction) { string text = string.Format(infoFormat, 1, initialPrice, infoFunction(0)); for (int i = 0; i < incrementalPrices.Length; i++) { float num = infoFunction(i + 1); text = ((num % 1f != 0f) ? (text + string.Format(infoFormat, i + 2, incrementalPrices[i], num)) : (text + string.Format(infoFormat, i + 2, incrementalPrices[i], Mathf.RoundToInt(num)))); } return text; } public static Color ConvertValueToColor(string hex, Color defaultValue) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) Color result = default(Color); if (hex == null || !ColorUtility.TryParseHtmlString("#" + hex.Trim('#', ' '), ref result)) { return defaultValue; } return result; } internal static string WrapText(string text, int availableLength, string leftPadding = "", string rightPadding = "", bool padLeftFirst = true) { int num = availableLength - leftPadding.Length - rightPadding.Length; string text2 = ""; string text3 = ""; int num2 = 0; int num3 = -1; bool flag = true; bool flag2 = false; for (int i = 0; i < text.Length; i++) { char c = text[i]; if (c == '<') { flag2 = true; } if (c == ' ' && !flag2) { num3 = text3.Length; } if (c != '\n') { text3 += c; if (!flag2) { num2++; } } if (c == '>' && flag2) { flag2 = false; } if (num2 < num && c != '\n') { continue; } if (c != '\n' && c != ' ') { if (num3 != -1) { string text4 = ((padLeftFirst || !flag) ? leftPadding : "") + text3.Substring(0, num3) + new string(' ', Mathf.Max(0, num - num3)) + rightPadding; text2 = text2 + text4 + "\n"; text3 = text3.Substring(num3 + 1); } else { string text5 = ((padLeftFirst || !flag) ? leftPadding : "") + text3 + rightPadding; text2 = text2 + text5 + "\n"; text3 = ""; } } else { if (text3 != "") { text2 = text2 + ((padLeftFirst || !flag) ? leftPadding : "") + text3 + new string(' ', Mathf.Max(0, num - num2)) + rightPadding + "\n"; } text3 = ""; } num3 = -1; flag = false; num2 = text3.Length; } if (text3 != "") { text2 = text2 + ((padLeftFirst || !flag) ? leftPadding : "") + text3 + new string(' ', Mathf.Max(0, num - num2)) + rightPadding + "\n"; } return text2; } internal static void SpawnExplosion(Vector3 explosionPosition, bool spawnExplosionEffect = false, float killRange = 1f, float damageRange = 1f, int nonLethalDamage = 50, float physicsForce = 0f, GameObject overridePrefab = null) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) Landmine.SpawnExplosion(explosionPosition, spawnExplosionEffect, killRange, damageRange, nonLethalDamage, physicsForce, overridePrefab, false); } internal static Terminal GetTerminal() { if ((Object)(object)terminal == (Object)null) { terminal = GameObject.Find("TerminalScript").GetComponent<Terminal>(); } return terminal; } } } namespace InteractiveTerminalAPI.UI { public class InteractiveTerminalManager : MonoBehaviour { internal static Dictionary<string, Func<TerminalApplication>> registeredApplications = new Dictionary<string, Func<TerminalApplication>>(); internal static Dictionary<string, bool> commandSensitive = new Dictionary<string, bool>(); public static InteractiveTerminalManager Instance; internal TerminalApplication mainApplication; private Terminal terminalReference; private TerminalNode lastTerminalNode; private Color previousCaretColor; private void Start() { Instance = this; terminalReference = Tools.GetTerminal(); lastTerminalNode = terminalReference.currentNode; UpdateInput(enable: false); } internal void Initialize(string command) { Func<TerminalApplication> func = registeredApplications.GetValueOrDefault(command, null); if (commandSensitive.ContainsKey(command.ToLower())) { foreach (string key in registeredApplications.Keys) { if (string.Equals(key, command, StringComparison.OrdinalIgnoreCase)) { func = registeredApplications[key]; break; } } } if (func == null) { Plugin.mls.LogError((object)"An application was not selected to change the terminal's text."); return; } mainApplication = func(); if (mainApplication == null) { Plugin.mls.LogError((object)"The selected application doesn't have a valid constructor."); return; } mainApplication.Initialization(); mainApplication.SetDefaultKeyboardAudio(); mainApplication.UpdateInputBindings(enable: true); } private void Update() { if (!((Object)(object)terminalReference == (Object)null) && mainApplication != null) { mainApplication.UpdateText(); } } private void OnDestroy() { mainApplication.UpdateInputBindings(); terminalReference.LoadNewNode(lastTerminalNode); UpdateInput(enable: true); Instance = null; } internal void UpdateInput(bool enable) { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) if (enable) { ((Selectable)terminalReference.screenText).interactable = true; terminalReference.screenText.ActivateInputField(); ((Selectable)terminalReference.screenText).Select(); terminalReference.screenText.caretColor = previousCaretColor; } else { terminalReference.screenText.DeactivateInputField(false); ((Selectable)terminalReference.screenText).interactable = false; previousCaretColor = terminalReference.screenText.caretColor; terminalReference.screenText.caretColor = APIConstants.Invisible; } } public static bool InteractiveTerminalBeingUsed() { return (Object)(object)Instance != (Object)null; } public static bool ContainsApplication(string command) { if (!commandSensitive.ContainsKey(command.ToLower())) { return registeredApplications.ContainsKey(command); } foreach (string key in registeredApplications.Keys) { if (string.Equals(key, command, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } public static void RegisterApplication<T>(string command) where T : TerminalApplication, new() { if (registeredApplications.ContainsKey(command)) { Plugin.mls.LogError((object)("An application has already been registered under the command \"" + command + "\"")); return; } registeredApplications.Add(command, () => new T()); } public static void RegisterApplication<T>(string command, bool caseSensitive) where T : TerminalApplication, new() { RegisterApplication<T>(command); commandSensitive.Add(command.ToLower(), caseSensitive); } public static void RegisterApplication<T>(string[] commands) where T : TerminalApplication, new() { foreach (string command in commands) { RegisterApplication<T>(command); } } public static void RegisterApplication<T>(string[] commands, bool caseSensitive) where T : TerminalApplication, new() { foreach (string command in commands) { RegisterApplication<T>(command, caseSensitive); } } } public interface ITextElement { string GetText(int availableLength); } public class TextElement : ITextElement { public string Text { get; set; } public string GetText(int availableLength) { return Tools.WrapText(Text, availableLength); } public static TextElement Create(string text = "") { return new TextElement { Text = text }; } } } namespace InteractiveTerminalAPI.UI.Screen { public class BoxedOutputScreen<K, V> : BoxedScreen { public Func<K> Input { get; set; } public Func<K, V> Output { get; set; } public V GetOutput() { if (Output == null || Input == null) { return default(V); } return Output(Input()); } public override string GetText(int availableLength) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine().AppendLine(); stringBuilder.Append(new string(' ', 1)).Append('╔').Append(new string('═', Title.Length + 2)) .Append('╗') .AppendLine(); stringBuilder.Append('╭').Append('╢').Append(' ') .Append(Title) .Append(' ') .Append('╟') .Append(new string('─', availableLength - 6 - Title.Length)) .Append('╮') .AppendLine(); stringBuilder.Append('│').Append('╚').Append(new string('═', Title.Length + 2)) .Append('╝') .Append(new string(' ', availableLength - 6 - Title.Length)) .Append('│') .AppendLine(); for (int i = 0; i < elements.Length; i++) { stringBuilder.Append(Tools.WrapText(elements[i].GetText(availableLength - 4), availableLength, "│ ", " │")); } string text = GetOutput().ToString(); int num = availableLength - text.NonHTMLLength() - 6; stringBuilder.Append('│').Append(new string(' ', num - 3)).Append('╔'); stringBuilder.Append(new string('═', text.NonHTMLLength() + 2)).Append('╗').Append(new string(' ', 3)) .Append('│') .Append('\n'); stringBuilder.Append('╰').Append(new string('─', num - 3)).Append('╢') .Append(' ') .Append(text) .Append(' ') .Append('╟') .Append(new string('─', 3)) .Append('╯') .Append('\n'); stringBuilder.Append(new string(' ', num - 2)).Append('╚').Append(new string('═', text.NonHTMLLength() + 2)) .Append('╝') .Append('\n'); return stringBuilder.ToString(); } public static BoxedOutputScreen<K, V> Create(string title = "", ITextElement[] elements = null, Func<K> input = null, Func<K, V> output = null) { return new BoxedOutputScreen<K, V> { Title = title, elements = elements, Input = input, Output = output }; } } public class BoxedScreen : IScreen, ITextElement { public string Title; public ITextElement[] elements; public virtual string GetText(int availableLength) { string text = Title; if (Title.Length > availableLength - 6) { text = text.Substring(0, availableLength - 6); } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine().AppendLine(); stringBuilder.Append(new string(' ', 1)).Append('╔').Append(new string('═', text.Length + 2)) .Append('╗') .AppendLine(); stringBuilder.Append('╭').Append('╢').Append(' ') .Append(text) .Append(' ') .Append('╟') .Append(new string('─', availableLength - 6 - text.Length)) .Append('╮') .AppendLine(); stringBuilder.Append('│').Append('╚').Append(new string('═', text.Length + 2)) .Append('╝') .Append(new string(' ', availableLength - 6 - text.Length)) .Append('│') .AppendLine(); for (int i = 0; i < elements.Length; i++) { stringBuilder.Append(Tools.WrapText(elements[i].GetText(availableLength - 4), availableLength, "│ ", " │")); } stringBuilder.Append('╰').Append(new string('─', availableLength - 2)).Append('╯') .AppendLine(); return stringBuilder.ToString(); } public static BoxedScreen Create(string title = "", ITextElement[] elements = null) { return new BoxedScreen { Title = title, elements = elements }; } } public interface IScreen : ITextElement { } } namespace InteractiveTerminalAPI.UI.Page { public class PageCursorElement<T> : PageElement where T : CursorElement { public BaseCursorMenu<T>[] cursorMenus; public BaseCursorMenu<T> GetCurrentCursorMenu() { return cursorMenus[pageIndex]; } public void ChangeSorting() { CollectionExtensions.Do<BaseCursorMenu<T>>((IEnumerable<BaseCursorMenu<T>>)cursorMenus, (Action<BaseCursorMenu<T>>)delegate(BaseCursorMenu<T> x) { x.ChangeSorting(); }); T[] array = cursorMenus.SelectMany((BaseCursorMenu<T> menu) => menu.elements).ToArray(); Func<T, T, int> sortFunction = cursorMenus[0].GetCurrentSorting(); Array.Sort(array, (T x, T y) => sortFunction(x, y)); int num = 0; BaseCursorMenu<T>[] array2 = cursorMenus; foreach (BaseCursorMenu<T> baseCursorMenu in array2) { int num2 = baseCursorMenu.elements.Length; Array.Copy(array, num, baseCursorMenu.elements, 0, num2); num += num2; } } public static PageCursorElement<T> Create(int startingPageIndex = 0, IScreen[] elements = null, BaseCursorMenu<T>[] cursorMenus = null) { return new PageCursorElement<T> { pageIndex = startingPageIndex, elements = elements, cursorMenus = cursorMenus }; } } public class PageElement : ITextElement { public int pageIndex; public IScreen[] elements; public string GetText(int availableLength) { IScreen screen = elements[pageIndex]; StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(screen.GetText(availableLength)); if (elements.Length > 1) { stringBuilder.Append(new string(' ', availableLength - 30)).Append($"Page {pageIndex + 1}/{elements.Length}"); } return stringBuilder.ToString(); } public void PageUp() { pageIndex = (pageIndex + 1) % elements.Length; } public void PageDown() { pageIndex--; if (pageIndex < 0) { pageIndex = elements.Length - 1; } } public void ResetPage() { pageIndex = 0; } public IScreen GetCurrentScreen() { return elements[pageIndex]; } public static PageElement Create(int startingPageIndex = 0, IScreen[] elements = null) { return new PageElement { pageIndex = startingPageIndex, elements = elements }; } } } namespace InteractiveTerminalAPI.UI.Hierarchy { public class BaseCursorHierarchyElement : CursorElement { private BaseCursorMenu<CursorElement> previousCursorMenu; public char IntersectionCharacter { get; set; } = '├'; public char LastIntersectionCharacter { get; set; } = '└'; public char SpacingCharacter { get; set; } = '─'; public int Spacing { get; set; } = 2; public char VerticalSpacingCharacter { get; set; } = '│'; public int VerticalSpacing { get; set; } = 1; public string Title { get; set; } public BaseCursorMenu<CursorElement> innerCursorMenu { get; set; } public bool Selected { get; set; } public override void ExecuteAction() { base.ExecuteAction(); Selected = true; if (InteractiveTerminalManager.Instance.mainApplication is InteractiveTerminalApplication<CursorElement> interactiveTerminalApplication) { Keybinds.cursorExitAction.performed -= InteractiveTerminalManager.Instance.mainApplication.OnScreenExit; Keybinds.cursorExitAction.performed += OnHierarchyExit; previousCursorMenu = interactiveTerminalApplication.currentCursorMenu; interactiveTerminalApplication.currentCursorMenu = innerCursorMenu; } } public void OnHierarchyExit(CallbackContext context) { Keybinds.cursorExitAction.performed += InteractiveTerminalManager.Instance.mainApplication.OnScreenExit; Keybinds.cursorExitAction.performed -= OnHierarchyExit; if (InteractiveTerminalManager.Instance.mainApplication is InteractiveTerminalApplication<CursorElement> interactiveTerminalApplication) { interactiveTerminalApplication.currentCursorMenu = previousCursorMenu; previousCursorMenu = null; } Selected = false; } public override string GetText(int availableLength) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(Title); if (!Selected) { return stringBuilder.ToString(); } stringBuilder.Append("\n"); int num = 0; CursorElement[] elements = innerCursorMenu.elements; int num2 = 0; while (num2 < elements.Length) { CursorElement cursorElement = elements[num2]; if (num != VerticalSpacing) { stringBuilder.Append(VerticalSpacingCharacter + "\n"); num++; continue; } num = 0; bool flag = num2 == elements.Length - 1; if (flag) { stringBuilder.Append(LastIntersectionCharacter); } else { stringBuilder.Append(IntersectionCharacter); } stringBuilder.Append(new string(SpacingCharacter, Spacing)); stringBuilder.Append(' '); string text = cursorElement.GetText(availableLength - 5 - Spacing); string arg = (cursorElement.Active(cursorElement) ? "#00FF0066" : "#66666633"); text = ((num2 == innerCursorMenu.cursorIndex) ? string.Format("<mark={0}><color={1}>{2}</color></mark>", arg, "#FFFFFFFF", text) : text); if (cursorElement is BaseCursorHierarchyElement) { stringBuilder.Append(Tools.WrapText(text, availableLength - 5 - Spacing, ((!flag) ? new string(VerticalSpacingCharacter, 1) : "") + new string(' ', Spacing + 1), "", padLeftFirst: false)); } else if (flag) { stringBuilder.Append(Tools.WrapText(text, availableLength - 5 - Spacing)); } else { stringBuilder.Append(Tools.WrapText(text, availableLength - 5 - Spacing, new string(VerticalSpacingCharacter, 1), "", padLeftFirst: false)); } num2++; } return stringBuilder.ToString(); } } public abstract class BaseHierarchyElement<T> : ITextElement where T : ITextElement { public char IntersectionCharacter { get; set; } = '├'; public char LastIntersectionCharacter { get; set; } = '└'; public char SpacingCharacter { get; set; } = '─'; public int Spacing { get; set; } = 2; public char VerticalSpacingCharacter { get; set; } = '│'; public int VerticalSpacing { get; set; } = 1; public string Title { get; set; } public T[] textElements { get; set; } public string GetText(int availableLength) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(Title + "\n"); int num = 0; int num2 = 0; while (num2 < textElements.Length) { T val = textElements[num2]; if (num != VerticalSpacing) { stringBuilder.Append(VerticalSpacingCharacter + "\n"); num++; continue; } num = 0; bool flag = num2 == textElements.Length - 1; if (flag) { stringBuilder.Append(LastIntersectionCharacter); } else { stringBuilder.Append(IntersectionCharacter); } stringBuilder.Append(new string(SpacingCharacter, Spacing)); stringBuilder.Append(' '); T val2; if (val is BaseHierarchyElement<T>) { ref T reference = ref val; val2 = default(T); if (val2 == null) { val2 = reference; reference = ref val2; } stringBuilder.Append(Tools.WrapText(reference.GetText(availableLength - 5 - Spacing), availableLength - 5 - Spacing, ((!flag) ? new string(VerticalSpacingCharacter, 1) : "") + new string(' ', Spacing + 1), "", padLeftFirst: false)); } else if (flag) { ref T reference2 = ref val; val2 = default(T); if (val2 == null) { val2 = reference2; reference2 = ref val2; } stringBuilder.Append(Tools.WrapText(reference2.GetText(availableLength - 5 - Spacing), availableLength - 5 - Spacing)); } else { ref T reference3 = ref val; val2 = default(T); if (val2 == null) { val2 = reference3; reference3 = ref val2; } stringBuilder.Append(Tools.WrapText(reference3.GetText(availableLength - 5 - Spacing), availableLength - 5 - Spacing, new string(VerticalSpacingCharacter, 1), "", padLeftFirst: false)); } num2++; } return stringBuilder.ToString(); } } internal class TextHierarchyElement : BaseHierarchyElement<ITextElement> { public static TextHierarchyElement Create(string title, ITextElement[] textElements, int spacing = 2, char intersectionCharacter = '├', char lastIntersectionCharacter = '└', char spacingCharacter = '─') { return new TextHierarchyElement { Title = title, textElements = textElements, Spacing = spacing, IntersectionCharacter = intersectionCharacter, LastIntersectionCharacter = lastIntersectionCharacter, SpacingCharacter = spacingCharacter }; } } } namespace InteractiveTerminalAPI.UI.Cursor { public abstract class BaseCursorMenu<T> : ITextElement where T : CursorElement { public char cursorCharacter = '>'; public int cursorIndex; public int sortingIndex; public T[] elements; public Func<T, T, int>[] SortingFunctions { get; set; } public T GetSelectedElement() { return elements[cursorIndex]; } public void Execute() { GetSelectedElement().ExecuteAction(); } public Func<T, T, int> GetCurrentSorting() { return SortingFunctions[sortingIndex]; } public void ChangeSorting() { sortingIndex = (sortingIndex + 1) % SortingFunctions.Length; Array.Sort(elements, (T x, T y) => SortingFunctions[sortingIndex](x, y)); } public void Forward() { cursorIndex = (cursorIndex + 1) % elements.Length; while (!IsCurrentElementSelectable()) { cursorIndex = (cursorIndex + 1) % elements.Length; } } public void Backward() { cursorIndex--; if (cursorIndex < 0) { cursorIndex = elements.Length - 1; } while (!IsCurrentElementSelectable()) { cursorIndex--; if (cursorIndex < 0) { cursorIndex = elements.Length - 1; } } } public bool IsCurrentElementSelectable() { T selectedElement = GetSelectedElement(); if (selectedElement != null) { if (!selectedElement.Active(selectedElement)) { return selectedElement.SelectInactive; } return true; } return false; } public void ResetCursor() { cursorIndex = 0; if (!IsCurrentElementSelectable()) { Forward(); } } public abstract string GetText(int availableLength); } public class CursorCounterElement : CursorElement { public int Counter { get; set; } public bool ShowCounter { get; set; } public void ToggleShow(bool showCounter) { ShowCounter = showCounter; } public void IncrementCounter() { Counter++; } public void DecrementCounter() { if (Counter > 0) { Counter--; } } public override string GetText(int availableLength) { StringBuilder stringBuilder = new StringBuilder(); if (!base.Active(this)) { stringBuilder.Append(string.Format("<color={0}>", "#666666")); } if (ShowCounter) { stringBuilder.Append('←').Append(Counter).Append('→') .Append(' '); } stringBuilder.Append(base.GetText(availableLength)); if (!base.Active(this)) { stringBuilder.Append("</color>"); } return stringBuilder.ToString(); } public static CursorCounterElement Create(string name = "", string description = "", Action action = null, int counter = 0, Func<CursorElement, bool> active = null, bool selectInactive = true, bool showCounter = true) { return new CursorCounterElement { Name = name, Description = description, Action = action, Counter = counter, Active = ((active == null) ? ((Func<CursorElement, bool>)((CursorElement _) => true)) : active), SelectInactive = selectInactive, ShowCounter = showCounter }; } } public class CursorElement : ITextElement { public string Name { get; set; } public string Description { get; set; } public Func<CursorElement, bool> Active { get; set; } = (CursorElement x) => true; public bool SelectInactive { get; set; } = true; public Action Action { get; set; } public virtual string GetText(int availableLength) { StringBuilder stringBuilder = new StringBuilder(); if (!Active(this)) { stringBuilder.Append(string.Format("<color={0}>", "#666666")); } stringBuilder.Append(Name); if (!Active(this)) { stringBuilder.Append("</color>"); } if (!string.IsNullOrEmpty(Description)) { stringBuilder.Append("\n" + Description); } return stringBuilder.ToString(); } public virtual void ExecuteAction() { if (Action != null) { Action(); } } public static CursorElement Create(string name = "", string description = "", Action action = null, Func<CursorElement, bool> active = null, bool selectInactive = true) { return new CursorElement { Name = name, Description = description, Action = action, Active = ((active == null) ? ((Func<CursorElement, bool>)((CursorElement _) => true)) : active), SelectInactive = selectInactive }; } } public class CursorMenu<T> : BaseCursorMenu<T> where T : CursorElement { public override string GetText(int availableLength) { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < elements.Length; i++) { CursorElement cursorElement = elements[i]; if (cursorElement != null) { if (i == cursorIndex) { stringBuilder.Append(cursorCharacter).Append(' '); } else { stringBuilder.Append(' ').Append(' '); } string text = cursorElement.GetText(availableLength - 2); string text2 = ((!string.IsNullOrEmpty(cursorElement.Name)) ? text.Split('\n')[0] : text); string text3 = ((!string.IsNullOrEmpty(cursorElement.Description)) ? text.Substring(text2.Length, text.Length - text2.Length) : ""); string arg = (cursorElement.Active(cursorElement) ? "#00FF0066" : "#66666633"); if (!(cursorElement is BaseCursorHierarchyElement baseCursorHierarchyElement) || !baseCursorHierarchyElement.Selected) { text = ((i == cursorIndex) ? (string.Format("<mark={0}><color={1}>{2}</color></mark>", arg, "#FFFFFFFF", text2) + text3) : text); } stringBuilder.Append(Tools.WrapText(text, availableLength - 2, " ", "", padLeftFirst: false)); } } return stringBuilder.ToString(); } public static CursorMenu<T> Create(int startingCursorIndex = 0, char cursorCharacter = '>', T[] elements = null, Func<T, T, int>[] sorting = null) { if (sorting == null) { sorting = new Func<T, T, int>[1] { (T element1, T element2) => string.Compare(element2.Name, element1.Name) }; } return new CursorMenu<T> { cursorIndex = startingCursorIndex, cursorCharacter = cursorCharacter, elements = elements, SortingFunctions = sorting }; } } public class CursorOutputElement<T> : CursorCounterElement { public Func<int, T> Func { get; set; } public T ApplyFunction() { if (Func == null) { return default(T); } return Func(base.Counter); } public override string GetText(int availableLength) { StringBuilder stringBuilder = new StringBuilder(); if (!base.Active(this)) { stringBuilder.Append(string.Format("<color={0}>", "#666666")); } stringBuilder.Append(base.GetText(availableLength)); if (base.ShowCounter) { stringBuilder.Append(new string(' ', 10)); stringBuilder.Append(ApplyFunction()); } if (!base.Active(this)) { stringBuilder.Append("</color>"); } return stringBuilder.ToString(); } public static CursorOutputElement<T> Create(string name = "", string description = "", Action action = null, int counter = 0, Func<int, T> func = null, Func<CursorElement, bool> active = null, bool selectInactive = true, bool showCounter = true) { return new CursorOutputElement<T> { Name = name, Description = description, Action = action, Counter = counter, Func = func, Active = ((active == null) ? ((Func<CursorElement, bool>)((CursorElement _) => true)) : active), SelectInactive = selectInactive, ShowCounter = showCounter }; } } } namespace InteractiveTerminalAPI.UI.Application { public abstract class BaseInteractiveApplication<T> : TerminalApplication where T : CursorElement { [CompilerGenerated] private sealed class <RepeatScrolling>d__21 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public BaseInteractiveApplication<T> <>4__this; public bool scrollUp; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <RepeatScrolling>d__21(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown int num = <>1__state; BaseInteractiveApplication<T> baseInteractiveApplication = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; baseInteractiveApplication.MoveCursor(scrollUp); <>2__current = (object)new WaitForSeconds(baseInteractiveApplication.scrollDelay); <>1__state = 1; return true; case 1: <>1__state = -1; goto IL_005a; case 2: <>1__state = -1; <>2__current = (object)new WaitForSeconds(baseInteractiveApplication.scrollRate); <>1__state = 3; return true; case 3: { <>1__state = -1; goto IL_005a; } IL_005a: baseInteractiveApplication.MoveCursor(scrollUp); <>2__current = (object)new WaitForEndOfFrame(); <>1__state = 2; return true; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public BaseCursorMenu<T> currentCursorMenu; public BaseCursorMenu<T> previousCursorMenu; public IScreen previousScreen; private Coroutine scrollingRoutine; protected float scrollRate = 0.1f; protected float scrollDelay = 0.5f; protected override string GetApplicationText() { return currentScreen.GetText(51); } protected override Action PreviousScreen() { return delegate { SwitchScreen((previousScreen != null) ? previousScreen : currentScreen, (previousCursorMenu != null) ? previousCursorMenu : currentCursorMenu, previous: true); }; } protected override void AddInputBindings() { base.AddInputBindings(); Keybinds.changeSortingAction.started += OnApplicationChangeSorting; Keybinds.cursorUpAction.started += OnApplicationCursorUp; Keybinds.cursorDownAction.started += OnApplicationCursorDown; Keybinds.storeConfirmAction.started += OnApplicationConfirm; Keybinds.cursorUpAction.canceled += OnApplicationCursorScrollCancel; Keybinds.cursorDownAction.canceled += OnApplicationCursorScrollCancel; } protected override void RemoveInputBindings() { base.RemoveInputBindings(); Keybinds.changeSortingAction.started -= OnApplicationChangeSorting; Keybinds.cursorUpAction.started -= OnApplicationCursorUp; Keybinds.cursorDownAction.started -= OnApplicationCursorDown; Keybinds.storeConfirmAction.started -= OnApplicationConfirm; Keybinds.cursorUpAction.canceled -= OnApplicationCursorScrollCancel; Keybinds.cursorDownAction.canceled -= OnApplicationCursorScrollCancel; } internal void OnApplicationConfirm(CallbackContext context) { Submit(); } internal void OnApplicationCursorUp(CallbackContext context) { if (scrollingRoutine != null) { ((MonoBehaviour)InteractiveTerminalManager.Instance).StopCoroutine(scrollingRoutine); } scrollingRoutine = ((MonoBehaviour)InteractiveTerminalManager.Instance).StartCoroutine(RepeatScrolling(scrollUp: true)); } internal void OnApplicationCursorScrollCancel(CallbackContext context) { ((MonoBehaviour)InteractiveTerminalManager.Instance).StopCoroutine(scrollingRoutine); } internal void OnApplicationChangeSorting(CallbackContext context) { ChangeSorting(); } internal void OnApplicationCursorDown(CallbackContext context) { if (scrollingRoutine != null) { ((MonoBehaviour)InteractiveTerminalManager.Instance).StopCoroutine(scrollingRoutine); } scrollingRoutine = ((MonoBehaviour)InteractiveTerminalManager.Instance).StartCoroutine(RepeatScrolling(scrollUp: false)); } protected virtual void ChangeSorting() { PlayKeyboardSounds(); currentCursorMenu.ChangeSorting(); } public virtual void MoveCursorUp() { PlayKeyboardSounds(); currentCursorMenu.Backward(); } public virtual void MoveCursorDown() { PlayKeyboardSounds(); currentCursorMenu.Forward(); } public void Submit() { PlayKeyboardSounds(); currentCursorMenu.Execute(); } protected virtual void SwitchScreen(IScreen screen, BaseCursorMenu<T> cursorMenu, bool previous) { previousScreen = currentScreen; previousCursorMenu = currentCursorMenu; currentScreen = screen; currentCursorMenu = cursorMenu; if (!previous) { cursorMenu.ResetCursor(); } } private void MoveCursor(bool up) { if (up) { MoveCursorUp(); } else { MoveCursorDown(); } } [IteratorStateMachine(typeof(BaseInteractiveApplication<>.<RepeatScrolling>d__21))] private IEnumerator RepeatScrolling(bool scrollUp) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <RepeatScrolling>d__21(0) { <>4__this = this, scrollUp = scrollUp }; } } public abstract class CounterPageApplication<T> : PageApplication<T> where T : CursorCounterElement { protected override void AddInputBindings() { base.AddInputBindings(); Keybinds.pageDownAction.performed -= base.OnApplicationPageDown; Keybinds.pageUpAction.performed -= base.OnApplicationPageUp; Keybinds.pageUpCounterOnlyAction.performed += base.OnApplicationPageUp; Keybinds.pageDownCounterOnlyAction.performed += base.OnApplicationPageDown; Keybinds.pageUpAction.performed += OnApplicationCountUp; Keybinds.pageDownAction.performed += OnApplicationCountDown; } protected override void RemoveInputBindings() { base.RemoveInputBindings(); Keybinds.pageUpAction.performed -= OnApplicationCountUp; Keybinds.pageDownAction.performed -= OnApplicationCountDown; Keybinds.pageUpCounterOnlyAction.performed -= base.OnApplicationPageUp; Keybinds.pageDownCounterOnlyAction.performed -= base.OnApplicationPageDown; } protected override void SwitchScreen(IScreen screen, BaseCursorMenu<T> cursorMenu, bool previous) { base.SwitchScreen(screen, cursorMenu, previous); Keybinds.pageDownAction.performed -= base.OnApplicationPageDown; Keybinds.pageUpAction.performed -= base.OnApplicationPageUp; if (screen == currentPage.GetCurrentScreen()) { Keybinds.pageUpCounterOnlyAction.performed += base.OnApplicationPageUp; Keybinds.pageDownCounterOnlyAction.performed += base.OnApplicationPageDown; } else { Keybinds.pageUpCounterOnlyAction.performed -= base.OnApplicationPageUp; Keybinds.pageDownCounterOnlyAction.performed -= base.OnApplicationPageDown; } } private void IncrementSelectedCounter() { PlayKeyboardSounds(); currentCursorMenu.GetSelectedElement().IncrementCounter(); } private void DecrementSelectedCounter() { PlayKeyboardSounds(); currentCursorMenu.GetSelectedElement().DecrementCounter(); } internal void OnApplicationCountUp(CallbackContext context) { IncrementSelectedCounter(); } internal void OnApplicationCountDown(CallbackContext context) { DecrementSelectedCounter(); } } public abstract class InteractiveCounterApplication<T> : BaseInteractiveApplication<T> where T : CursorCounterElement { protected override void AddInputBindings() { base.AddInputBindings(); Keybinds.pageUpAction.performed += OnApplicationCountUp; Keybinds.pageDownAction.performed += OnApplicationCountDown; } protected override void RemoveInputBindings() { base.RemoveInputBindings(); Keybinds.pageUpAction.performed -= OnApplicationCountUp; Keybinds.pageDownAction.performed -= OnApplicationCountDown; } private void IncrementSelectedCounter() { PlayKeyboardSounds(); currentCursorMenu.GetSelectedElement()?.IncrementCounter(); } private void DecrementSelectedCounter() { PlayKeyboardSounds(); currentCursorMenu.GetSelectedElement()?.DecrementCounter(); } internal void OnApplicationCountUp(CallbackContext context) { IncrementSelectedCounter(); } internal void OnApplicationCountDown(CallbackContext context) { DecrementSelectedCounter(); } } public abstract class InteractiveTerminalApplication<T> : BaseInteractiveApplication<T> where T : CursorElement { protected void Confirm(string title, string description, Action confirmAction, Action declineAction, string additionalMessage = "") { T[] elements = new T[2] { (T)CursorElement.Create("Confirm", "", confirmAction), (T)CursorElement.Create("Abort", "", declineAction) }; CursorMenu<T> cursorMenu = CursorMenu<T>.Create(0, '>', elements); ITextElement[] elements2 = new ITextElement[4] { TextElement.Create(description), TextElement.Create(" "), TextElement.Create(additionalMessage), cursorMenu }; IScreen screen = BoxedScreen.Create(title, elements2); SwitchScreen(screen, cursorMenu, previous: false); } protected void ErrorMessage(string title, Action backAction, string error) { T[] elements = new T[1] { (T)CursorElement.Create("Back", "", backAction) }; CursorMenu<T> cursorMenu = CursorMenu<T>.Create(0, '>', elements); ITextElement[] elements2 = new ITextElement[3] { TextElement.Create(error), TextElement.Create(" "), cursorMenu }; IScreen screen = BoxedScreen.Create(title, elements2); SwitchScreen(screen, cursorMenu, previous: false); } protected void ErrorMessage(string title, string description, Action backAction, string error) { T[] elements = new T[1] { (T)CursorElement.Create("Back", "", backAction) }; CursorMenu<T> cursorMenu = CursorMenu<T>.Create(0, '>', elements); ITextElement[] elements2 = new ITextElement[5] { TextElement.Create(description), TextElement.Create(" "), TextElement.Create(error), TextElement.Create(" "), cursorMenu }; IScreen screen = BoxedScreen.Create(title, elements2); SwitchScreen(screen, cursorMenu, previous: false); } } public abstract class PageApplication<T> : InteractiveTerminalApplication<T> where T : CursorElement { protected PageCursorElement<T> initialPage; protected PageCursorElement<T> previousPage; protected PageCursorElement<T> currentPage; protected override string GetApplicationText() { if (currentScreen != currentPage.GetCurrentScreen()) { return currentScreen.GetText(51); } return currentPage.GetText(51); } protected virtual int GetEntriesPerPage<K>(K[] entries) { return Mathf.CeilToInt((float)entries.Length / 2f); } protected override Action PreviousScreen() { return delegate { if (previousPage != null) { SwitchScreen(previousPage, previous: true); } else { ResetScreen(); } }; } protected virtual int GetAmountPages<K>(K[] entries) { return Mathf.CeilToInt((float)entries.Length / (float)GetEntriesPerPage(entries)); } protected override void ChangeSorting() { PlayKeyboardSounds(); currentPage.ChangeSorting(); } public override void MoveCursorUp() { int cursorIndex = currentCursorMenu.cursorIndex; base.MoveCursorUp(); if (currentPage.GetCurrentCursorMenu() == currentCursorMenu && currentCursorMenu.cursorIndex > cursorIndex) { ChangeScreenBackward(); base.MoveCursorUp(); } } public override void MoveCursorDown() { int cursorIndex = currentCursorMenu.cursorIndex; base.MoveCursorDown(); if (currentPage.GetCurrentCursorMenu() == currentCursorMenu && currentCursorMenu.cursorIndex < cursorIndex) { ChangeScreenForward(); base.MoveCursorDown(); } } protected void ResetScreen() { SwitchScreen(initialPage, previous: true); } protected void SwitchScreen(PageCursorElement<T> pages, bool previous) { previousPage = currentPage; currentPage = pages; SwitchScreen(currentPage.GetCurrentScreen(), currentPage.GetCurrentCursorMenu(), previous); } protected override void SwitchScreen(IScreen screen, BaseCursorMenu<T> cursorMenu, bool previous) { base.SwitchScreen(screen, cursorMenu, previous); if (screen == currentPage.GetCurrentScreen()) { Keybinds.pageUpAction.performed += OnApplicationPageUp; Keybinds.pageDownAction.performed += OnApplicationPageDown; } else { Keybinds.pageUpAction.performed -= OnApplicationPageUp; Keybinds.pageDownAction.performed -= OnApplicationPageDown; } } public void ChangeScreenForward() { PlayKeyboardSounds(); currentPage.PageUp(); SwitchScreen(currentPage.GetCurrentScreen(), currentPage.GetCurrentCursorMenu(), previous: false); } public void ChangeScreenBackward() { PlayKeyboardSounds(); currentPage.PageDown(); SwitchScreen(currentPage.GetCurrentScreen(), currentPage.GetCurrentCursorMenu(), previous: false); } protected override void AddInputBindings() { base.AddInputBindings(); Keybinds.pageUpAction.performed += OnApplicationPageUp; Keybinds.pageDownAction.performed += OnApplicationPageDown; } protected override void RemoveInputBindings() { base.RemoveInputBindings(); Keybinds.pageUpAction.performed -= OnApplicationPageUp; Keybinds.pageDownAction.performed -= OnApplicationPageDown; } protected (K[][], BaseCursorMenu<T>[], IScreen[]) GetPageEntries<K>(K[] entries) { int amountPages = GetAmountPages(entries); int entriesPerPage = GetEntriesPerPage(entries); K[][] array = new K[amountPages][]; for (int i = 0; i < amountPages; i++) { array[i] = new K[entriesPerPage]; } for (int j = 0; j < entries.Length; j++) { int num = j / entriesPerPage; int num2 = j % entriesPerPage; array[num][num2] = entries[j]; } BaseCursorMenu<T>[] array2 = new BaseCursorMenu<T>[array.Length]; IScreen[] array3 = new IScreen[array.Length]; BaseCursorMenu<T>[] cursorMenus = array2; initialPage = PageCursorElement<T>.Create(0, array3, cursorMenus); return (array, array2, array3); } protected void OnApplicationPageUp(CallbackContext context) { ChangeScreenForward(); } protected void OnApplicationPageDown(CallbackContext context) { ChangeScreenBackward(); } } public abstract class TerminalApplication { protected IScreen currentScreen; protected readonly Terminal terminal = Tools.GetTerminal(); protected AudioSource terminalAudio; protected AudioClip[] terminalKeyboardAudioClips; public abstract void Initialization(); protected abstract string GetApplicationText(); protected abstract Action PreviousScreen(); public virtual void SetDefaultKeyboardAudio() { terminalAudio = terminal.terminalAudio; terminalKeyboardAudioClips = terminal.keyboardClips; } public void UpdateText() { string applicationText = GetApplicationText(); terminal.screenText.text = applicationText; terminal.currentText = applicationText; } public void OnScreenExit(CallbackContext context) { PlayKeyboardSounds(); ScreenExit(); } internal void UpdateInputBindings(bool enable = false) { if (enable) { AddInputBindings(); } else { RemoveInputBindings(); } } protected virtual void AddInputBindings() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) Keybinds.cursorExitAction.performed += OnScreenExit; MovementActions movement = terminal.playerActions.Movement; ((MovementActions)(ref movement)).OpenMenu.performed -= terminal.PressESC; } protected virtual void RemoveInputBindings() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) Keybinds.cursorExitAction.performed -= OnScreenExit; MovementActions movement = terminal.playerActions.Movement; ((MovementActions)(ref movement)).OpenMenu.performed += terminal.PressESC; } protected virtual void PlayKeyboardSounds() { RoundManager.PlayRandomClip(terminalAudio, terminalKeyboardAudioClips, true, 1f, 0, 1000); } protected virtual void ScreenExit() { Object.Destroy((Object)(object)((Component)InteractiveTerminalManager.Instance).gameObject); } } } namespace InteractiveTerminalAPI.Patches.TerminalComponents { [HarmonyPatch(typeof(Terminal))] internal static class TerminalPatcher { [HarmonyTranspiler] [HarmonyPatch("Update")] private static IEnumerable<CodeInstruction> UpdateTranspiler(IEnumerable<CodeInstruction> instructions) { MethodInfo method = typeof(InteractiveTerminalManager).GetMethod("InteractiveTerminalBeingUsed"); MethodInfo method2 = typeof(ButtonControl).GetMethod("get_wasPressedThisFrame"); List<CodeInstruction> codes = new List<CodeInstruction>(instructions); int index = 0; Tools.FindMethod(ref index, ref codes, method2, method, skip: false, notInstruction: true, andInstruction: true, orInstruction: false, requireInstance: false, "Couldn't find field used to check if the terminal is being used"); return codes; } [HarmonyPostfix] [HarmonyPatch("ParsePlayerSentence")] private static void CustomParser(ref Terminal __instance, ref TerminalNode __result) { string command = __instance.screenText.text.Substring(__instance.screenText.text.Length - __instance.textAdded); if (InteractiveTerminalManager.ContainsApplication(command)) { GameObject val = Object.Instantiate<GameObject>(GameObject.CreatePrimitive((PrimitiveType)3)); ((Object)val).name = "InteractiveTerminal"; Object.Destroy((Object)(object)val.GetComponent<MeshRenderer>()); Object.Destroy((Object)(object)val.GetComponent<MeshFilter>()); Object.Destroy((Object)(object)val.GetComponent<BoxCollider>()); InteractiveTerminalManager interactiveTerminalManager = val.AddComponent<InteractiveTerminalManager>(); interactiveTerminalManager.Initialize(command); __result = TerminalNodeUtils.GetHelpTerminalNode(); } } } } namespace InteractiveTerminalAPI.Misc { internal static class Metadata { public const string GUID = "WhiteSpike.InteractiveTerminalAPI"; public const string NAME = "Interactive Terminal API"; public const string VERSION = "1.3.2"; } } namespace InteractiveTerminalAPI.Misc.Util { internal static class APIConstants { internal const char WHITE_SPACE = ' '; internal const string HEXADECIMAL_RED = "#FF0000"; internal const string HEXADECIMAL_WHITE = "#FFFFFF"; internal const string HEXADECIMAL_GREEN = "#00FF00"; internal const string HEXADECIMAL_GREY = "#666666"; internal const string MOVE_CURSOR_UP_KEYBIND_NAME = "Move Cursor Up in the current application"; internal const string MOVE_CURSOR_UP_DEFAULT_KEYBIND = "<Keyboard>/w"; internal const string MOVE_CURSOR_DOWN_KEYBIND_NAME = "Move Cursor Down in the current application"; internal const string MOVE_CURSOR_DOWN_DEFAULT_KEYBIND = "<Keyboard>/s"; internal const string EXIT_STORE_KEYBIND_NAME = "Exit application"; internal const string EXIT_STORE_DEFAULT_KEYBIND = "<Keyboard>/escape"; internal const string NEXT_PAGE_KEYBIND_NAME = "Next Page in the current application"; internal const string NEXT_PAGE_DEFAULT_KEYBIND = "<Keyboard>/d"; internal const string PREVIOUS_PAGE_KEYBIND_NAME = "Previous Page in the current application"; internal const string PREVIOUS_PAGE_DEFAULT_KEYBIND = "<Keyboard>/a"; internal const string SUBMIT_PROMPT_KEYBIND_NAME = "Submit Prompt in the current application"; internal const string SUBMIT_PROMPT_DEFAULT_KEYBIND = "<Keyboard>/enter"; internal const string CHANGE_SORTING_KEYBIND_NAME = "Change sorting in the current application"; internal const string CHANGE_SORTING_DEFAULT_KEYBIND = "<Keyboard>/f"; internal const string NEXT_PAGE_COUNTER_ONLY_KEYBIND_NAME = "Next Page in the current application (only applied for applications with counters)"; internal const string NEXT_PAGE_COUNTER_ONLY_KEYBIND = "<Keyboard>/e"; internal const string PREVIOUS_PAGE_COUNTER_ONLY_KEYBIND_NAME = "Previous Page in the current Application (only applied for applications with counters)"; internal const string PREVIOUS_PAGE_COUNTER_ONLY_KEYBIND = "<Keyboard>/q"; internal const char INTERSECTION = '├'; internal const char LAST_INTERSECTION = '└'; internal const char SPACING = '─'; internal const int SPACING_AMOUNT = 2; internal const char VERTICAL_SPACING = '│'; internal const int VERTICAL_SPACING_AMOUNT = 1; internal const char CURSOR = '>'; internal const string SELECTED_CURSOR_ELEMENT_FORMAT = "<mark={0}><color={1}>{2}</color></mark>"; internal const string COLOR_INITIAL_FORMAT = "<color={0}>"; internal const string COLOR_FINAL_FORMAT = "</color>"; internal const string DEFAULT_BACKGROUND_SELECTED_COLOR = "#00FF0066"; internal const string INACTIVE_BACKGROUND_SELECTED_COLOR = "#66666633"; internal const string DEFAULT_TEXT_SELECTED_COLOR = "#FFFFFFFF"; internal const string DEFAULT_DEACTIVATED_TEXT_COLOUR = "#66666655"; internal const string GO_BACK_PROMPT = "Back"; internal const string CONFIRM_PROMPT = "Confirm"; internal const string CANCEL_PROMPT = "Abort"; internal const int AVAILABLE_CHARACTERS_PER_LINE = 51; internal const char TOP_LEFT_CORNER = '╭'; internal const char TOP_RIGHT_CORNER = '╮'; internal const char BOTTOM_LEFT_CORNER = '╰'; internal const char BOTTOM_RIGHT_CORNER = '╯'; internal const char VERTICAL_LINE = '│'; internal const char HORIZONTAL_LINE = '─'; internal const char CONNECTING_TITLE_LEFT = '╢'; internal const char CONNECTING_TITLE_RIGHT = '╟'; internal const char TOP_RIGHT_TITLE_CORNER = '╗'; internal const char TOP_LEFT_TITLE_CORNER = '╔'; internal const char BOTTOM_RIGHT_TITLE_CORNER = '╝'; internal const char BOTTOM_LEFT_TITLE_CORNER = '╚'; internal const char VERTICAL_TITLE_LINE = '║'; internal const char HORIZONTAL_TITLE_LINE = '═'; internal const char LEFT_ARROW = '←'; internal const char RIGHT_ARROW = '→'; internal const int START_PAGE_COUNTER = 30; internal const int NAME_LENGTH = 17; internal const int LEVEL_LENGTH = 7; internal const char EMPTY_LEVEL = '○'; internal const char FILLED_LEVEL = '●'; internal static readonly Color Invisible = new Color(0f, 0f, 0f, 0f); } } namespace InteractiveTerminalAPI.Input { internal class IngameKeybinds : LcInputActions { public static IngameKeybinds Instance; [InputAction("<Keyboard>/w", Name = "Move Cursor Up in the current application")] public InputAction CursorUpKey { get; set; } [InputAction("<Keyboard>/s", Name = "Move Cursor Down in the current application")] public InputAction CursorDownKey { get; set; } [InputAction("<Keyboard>/escape", Name = "Exit application")] public InputAction CursorExitKey { get; set; } [InputAction("<Keyboard>/d", Name = "Next Page in the current application")] public InputAction PageUpKey { get; set; } [InputAction("<Keyboard>/a", Name = "Previous Page in the current application")] public InputAction PageDownKey { get; set; } [InputAction("<Keyboard>/enter", Name = "Submit Prompt in the current application")] public InputAction LguStoreConfirmKey { get; set; } [InputAction("<Keyboard>/f", Name = "Change sorting in the current application")] public InputAction ApplicationChangeSorting { get; set; } [InputAction("<Keyboard>/e", Name = "Next Page in the current application (only applied for applications with counters)")] public InputAction PageUpCounterOnlyKey { get; set; } [InputAction("<Keyboard>/q", Name = "Previous Page in the current Application (only applied for applications with counters)")] public InputAction PageDownCounterOnlyKey { get; set; } internal static InputActionAsset GetAsset() { return ((LcInputActions)Instance).Asset; } } [HarmonyPatch] internal static class Keybinds { public static InputActionAsset Asset; public static InputActionMap ActionMap; public static InputAction cursorUpAction; public static InputAction cursorDownAction; public static InputAction cursorExitAction; public static InputAction pageUpAction; public static InputAction pageDownAction; public static InputAction storeConfirmAction; public static InputAction changeSortingAction; public static InputAction pageUpCounterOnlyAction; public static InputAction pageDownCounterOnlyAction; public static PlayerControllerB localPlayerController => StartOfRound.Instance?.localPlayerController; [HarmonyPatch(typeof(PreInitSceneScript), "Awake")] [HarmonyPrefix] public static void AddToKeybindMenu() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) Asset = InputUtils_Compat.Asset; ActionMap = Asset.actionMaps[0]; cursorUpAction = InputUtils_Compat.CursorUpKey; cursorDownAction = InputUtils_Compat.CursorDownKey; cursorExitAction = InputUtils_Compat.CursorExitKey; pageUpAction = InputUtils_Compat.PageUpKey; pageDownAction = InputUtils_Compat.PageDownKey; storeConfirmAction = InputUtils_Compat.LguStoreConfirmKey; changeSortingAction = InputUtils_Compat.ChangeApplicationSortingKey; pageUpCounterOnlyAction = InputUtils_Compat.PageUpCounterOnlyKey; pageDownCounterOnlyAction = InputUtils_Compat.PageDownCounterOnlyKey; } } } namespace InteractiveTerminalAPI.Extensions { public static class StringExtensions { public static int NonHTMLLength(this string characters) { int num = 0; bool flag = false; foreach (char c in characters) { if (c == '<' && !flag) { flag = true; } else if (c == '>' && flag) { flag = false; } else if (!flag) { num++; } } return num; } } } namespace InteractiveTerminalAPI.Compat { public static class InputUtils_Compat { internal static InputActionAsset Asset => IngameKeybinds.GetAsset(); public static InputAction CursorUpKey => IngameKeybinds.Instance.CursorUpKey; public static InputAction CursorDownKey => IngameKeybinds.Instance.CursorDownKey; public static InputAction CursorExitKey => IngameKeybinds.Instance.CursorExitKey; public static InputAction PageUpKey => IngameKeybinds.Instance.PageUpKey; public static InputAction PageDownKey => IngameKeybinds.Instance.PageDownKey; public static InputAction LguStoreConfirmKey => IngameKeybinds.Instance.LguStoreConfirmKey; public static InputAction ChangeApplicationSortingKey => IngameKeybinds.Instance.ApplicationChangeSorting; public static InputAction PageDownCounterOnlyKey => IngameKeybinds.Instance.PageDownCounterOnlyKey; public static InputAction PageUpCounterOnlyKey => IngameKeybinds.Instance.PageUpCounterOnlyKey; internal static void Init() { IngameKeybinds.Instance = new IngameKeybinds(); } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } } namespace InteractiveTerminalAPI.NetcodePatcher { [AttributeUsage(AttributeTargets.Module)] internal class NetcodePatchedAssemblyAttribute : Attribute { } }