using 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 { static () { } } 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.3")] [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.3 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 retrievedTerminalNodes = new Dictionary(); 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 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 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 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 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 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 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 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 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 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 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 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(List 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 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(); } return terminal; } } } namespace InteractiveTerminalAPI.UI { public class InteractiveTerminalManager : MonoBehaviour { internal static Dictionary> registeredApplications = new Dictionary>(); internal static Dictionary commandSensitive = new Dictionary(); 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 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(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(string command, bool caseSensitive) where T : TerminalApplication, new() { RegisterApplication(command); commandSensitive.Add(command.ToLower(), caseSensitive); } public static void RegisterApplication(string[] commands) where T : TerminalApplication, new() { foreach (string command in commands) { RegisterApplication(command); } } public static void RegisterApplication(string[] commands, bool caseSensitive) where T : TerminalApplication, new() { foreach (string command in commands) { RegisterApplication(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 : BoxedScreen { public Func Input { get; set; } public Func 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 Create(string title = "", ITextElement[] elements = null, Func input = null, Func output = null) { return new BoxedOutputScreen { 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 : PageElement where T : CursorElement { public BaseCursorMenu[] cursorMenus; public BaseCursorMenu GetCurrentCursorMenu() { return cursorMenus[pageIndex]; } public void ChangeSorting() { CollectionExtensions.Do>((IEnumerable>)cursorMenus, (Action>)delegate(BaseCursorMenu x) { x.ChangeSorting(); }); T[] array = cursorMenus.SelectMany((BaseCursorMenu menu) => menu.elements).ToArray(); Func sortFunction = cursorMenus[0].GetCurrentSorting(); Array.Sort(array, (T x, T y) => sortFunction(x, y)); int num = 0; BaseCursorMenu[] array2 = cursorMenus; foreach (BaseCursorMenu baseCursorMenu in array2) { int num2 = baseCursorMenu.elements.Length; Array.Copy(array, num, baseCursorMenu.elements, 0, num2); num += num2; } } public static PageCursorElement Create(int startingPageIndex = 0, IScreen[] elements = null, BaseCursorMenu[] cursorMenus = null) { return new PageCursorElement { 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 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 innerCursorMenu { get; set; } public bool Selected { get; set; } public override void ExecuteAction() { base.ExecuteAction(); Selected = true; if (InteractiveTerminalManager.Instance.mainApplication is InteractiveTerminalApplication 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 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("{2}", 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 : 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) { 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 { 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 : ITextElement where T : CursorElement { public char cursorCharacter = '>'; public int cursorIndex; public int sortingIndex; public T[] elements; public Func[] SortingFunctions { get; set; } public T GetSelectedElement() { return elements[cursorIndex]; } public void Execute() { GetSelectedElement().ExecuteAction(); } public Func 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++; if (cursorIndex >= elements.Length) { cursorIndex = 0; } while (!IsCurrentElementSelectable()) { cursorIndex++; if (cursorIndex >= elements.Length) { cursorIndex = 0; } } } 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; } 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("", "#666666")); } if (ShowCounter) { stringBuilder.Append('←').Append(Counter).Append('→') .Append(' '); } stringBuilder.Append(base.GetText(availableLength)); if (!base.Active(this)) { stringBuilder.Append(""); } return stringBuilder.ToString(); } public static CursorCounterElement Create(string name = "", string description = "", Action action = null, int counter = 0, Func active = null, bool selectInactive = true, bool showCounter = true) { return new CursorCounterElement { Name = name, Description = description, Action = action, Counter = counter, Active = ((active == null) ? ((Func)((CursorElement _) => true)) : active), SelectInactive = selectInactive, ShowCounter = showCounter }; } } public class CursorElement : ITextElement { public string Name { get; set; } public string Description { get; set; } public Func 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("", "#666666")); } stringBuilder.Append(Name); if (!Active(this)) { stringBuilder.Append(""); } 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 active = null, bool selectInactive = true) { return new CursorElement { Name = name, Description = description, Action = action, Active = ((active == null) ? ((Func)((CursorElement _) => true)) : active), SelectInactive = selectInactive }; } } public class CursorMenu : BaseCursorMenu 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("{2}", arg, "#FFFFFFFF", text2) + text3) : text); } stringBuilder.Append(Tools.WrapText(text, availableLength - 2, " ", "", padLeftFirst: false)); } } return stringBuilder.ToString(); } public static CursorMenu Create(int startingCursorIndex = 0, char cursorCharacter = '>', T[] elements = null, Func[] sorting = null) { if (sorting == null) { sorting = new Func[1] { (T element1, T element2) => string.Compare(element2.Name, element1.Name) }; } return new CursorMenu { cursorIndex = startingCursorIndex, cursorCharacter = cursorCharacter, elements = elements, SortingFunctions = sorting }; } } public class CursorOutputElement : CursorCounterElement { public Func 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("", "#666666")); } stringBuilder.Append(base.GetText(availableLength)); if (base.ShowCounter) { stringBuilder.Append(new string(' ', 10)); stringBuilder.Append(ApplyFunction()); } if (!base.Active(this)) { stringBuilder.Append(""); } return stringBuilder.ToString(); } public static CursorOutputElement Create(string name = "", string description = "", Action action = null, int counter = 0, Func func = null, Func active = null, bool selectInactive = true, bool showCounter = true) { return new CursorOutputElement { Name = name, Description = description, Action = action, Counter = counter, Func = func, Active = ((active == null) ? ((Func)((CursorElement _) => true)) : active), SelectInactive = selectInactive, ShowCounter = showCounter }; } } } namespace InteractiveTerminalAPI.UI.Application { public abstract class BaseInteractiveApplication : TerminalApplication where T : CursorElement { [CompilerGenerated] private sealed class d__19 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public bool scrollUp; public BaseInteractiveApplication <>4__this; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__19(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006b: 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 int num = <>1__state; BaseInteractiveApplication baseInteractiveApplication = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; goto IL_0029; case 1: <>1__state = -1; <>2__current = (object)new WaitForSeconds(baseInteractiveApplication.scrollRate); <>1__state = 2; return true; case 2: { <>1__state = -1; goto IL_0029; } IL_0029: if (scrollUp) { baseInteractiveApplication.MoveCursorUp(); } else { baseInteractiveApplication.MoveCursorDown(); } <>2__current = (object)new WaitForEndOfFrame(); <>1__state = 1; 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 currentCursorMenu; public BaseCursorMenu previousCursorMenu; public IScreen previousScreen; private Coroutine scrollingRoutine; protected float scrollRate = 0.1f; 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 cursorMenu, bool previous) { previousScreen = currentScreen; previousCursorMenu = currentCursorMenu; currentScreen = screen; currentCursorMenu = cursorMenu; if (!previous) { cursorMenu.ResetCursor(); } } [IteratorStateMachine(typeof(BaseInteractiveApplication<>.d__19))] private IEnumerator RepeatScrolling(bool scrollUp) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__19(0) { <>4__this = this, scrollUp = scrollUp }; } } public abstract class CounterPageApplication : PageApplication 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 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 : BaseInteractiveApplication 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 : BaseInteractiveApplication 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 cursorMenu = CursorMenu.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 cursorMenu = CursorMenu.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 cursorMenu = CursorMenu.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 : InteractiveTerminalApplication where T : CursorElement { protected PageCursorElement initialPage; protected PageCursorElement previousPage; protected PageCursorElement currentPage; protected override string GetApplicationText() { if (currentScreen != currentPage.GetCurrentScreen()) { return currentScreen.GetText(51); } return currentPage.GetText(51); } protected virtual int GetEntriesPerPage(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[] 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(); if (!currentCursorMenu.IsCurrentElementSelectable()) { base.MoveCursorUp(); } } } public override void MoveCursorDown() { int cursorIndex = currentCursorMenu.cursorIndex; base.MoveCursorDown(); if (currentPage.GetCurrentCursorMenu() == currentCursorMenu && currentCursorMenu.cursorIndex < cursorIndex) { ChangeScreenForward(); if (!currentCursorMenu.IsCurrentElementSelectable()) { base.MoveCursorDown(); } } } protected void ResetScreen() { SwitchScreen(initialPage, previous: true); } protected void SwitchScreen(PageCursorElement pages, bool previous) { previousPage = currentPage; currentPage = pages; SwitchScreen(currentPage.GetCurrentScreen(), currentPage.GetCurrentCursorMenu(), previous); } protected override void SwitchScreen(IScreen screen, BaseCursorMenu 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); currentCursorMenu.cursorIndex = currentCursorMenu.elements.Length - 1; } 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[], IScreen[]) GetPageEntries(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[] array2 = new BaseCursorMenu[array.Length]; IScreen[] array3 = new IScreen[array.Length]; BaseCursorMenu[] cursorMenus = array2; initialPage = PageCursorElement.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 UpdateTranspiler(IEnumerable instructions) { MethodInfo method = typeof(InteractiveTerminalManager).GetMethod("InteractiveTerminalBeingUsed"); MethodInfo method2 = typeof(ButtonControl).GetMethod("get_wasPressedThisFrame"); List codes = new List(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.CreatePrimitive((PrimitiveType)3)); ((Object)val).name = "InteractiveTerminal"; Object.Destroy((Object)(object)val.GetComponent()); Object.Destroy((Object)(object)val.GetComponent()); Object.Destroy((Object)(object)val.GetComponent()); InteractiveTerminalManager interactiveTerminalManager = val.AddComponent(); 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.3"; } } 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 = "/w"; internal const string MOVE_CURSOR_DOWN_KEYBIND_NAME = "Move Cursor Down in the current application"; internal const string MOVE_CURSOR_DOWN_DEFAULT_KEYBIND = "/s"; internal const string EXIT_STORE_KEYBIND_NAME = "Exit application"; internal const string EXIT_STORE_DEFAULT_KEYBIND = "/escape"; internal const string NEXT_PAGE_KEYBIND_NAME = "Next Page in the current application"; internal const string NEXT_PAGE_DEFAULT_KEYBIND = "/d"; internal const string PREVIOUS_PAGE_KEYBIND_NAME = "Previous Page in the current application"; internal const string PREVIOUS_PAGE_DEFAULT_KEYBIND = "/a"; internal const string SUBMIT_PROMPT_KEYBIND_NAME = "Submit Prompt in the current application"; internal const string SUBMIT_PROMPT_DEFAULT_KEYBIND = "/enter"; internal const string CHANGE_SORTING_KEYBIND_NAME = "Change sorting in the current application"; internal const string CHANGE_SORTING_DEFAULT_KEYBIND = "/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 = "/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 = "/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 = "{2}"; internal const string COLOR_INITIAL_FORMAT = ""; internal const string COLOR_FINAL_FORMAT = ""; 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("/w", Name = "Move Cursor Up in the current application")] public InputAction CursorUpKey { get; set; } [InputAction("/s", Name = "Move Cursor Down in the current application")] public InputAction CursorDownKey { get; set; } [InputAction("/escape", Name = "Exit application")] public InputAction CursorExitKey { get; set; } [InputAction("/d", Name = "Next Page in the current application")] public InputAction PageUpKey { get; set; } [InputAction("/a", Name = "Previous Page in the current application")] public InputAction PageDownKey { get; set; } [InputAction("/enter", Name = "Submit Prompt in the current application")] public InputAction LguStoreConfirmKey { get; set; } [InputAction("/f", Name = "Change sorting in the current application")] public InputAction ApplicationChangeSorting { get; set; } [InputAction("/e", Name = "Next Page in the current application (only applied for applications with counters)")] public InputAction PageUpCounterOnlyKey { get; set; } [InputAction("/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 { } }