Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of AdvancedTerminalAPI v5.1.0
BepInEx/plugins/AdvancedTerminalAPI.dll
Decompiled 2 years agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text.RegularExpressions; using AdvancedTerminalAPI.NetcodePatcher; using BepInEx; using BepInEx.Logging; using Computerdores.AdvancedTerminalAPI.Vanillin; using Computerdores.AdvancedTerminalAPI.Vanillin.Commands; using Computerdores.AdvancedTerminalAPI.patch; using HarmonyLib; using JetBrains.Annotations; using Microsoft.CodeAnalysis; using TMPro; using Unity.Netcode; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("")] [assembly: AssemblyCompany("Computerdores")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("A Lethal Company Mod to provide a more advanced API for the Terminal.")] [assembly: AssemblyFileVersion("5.1.0.0")] [assembly: AssemblyInformationalVersion("5.1.0+33f6424e733f41848c95eec6180e9665bb94cecc")] [assembly: AssemblyProduct("AdvancedTerminalAPI")] [assembly: AssemblyTitle("AdvancedTerminalAPI")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/Computerdores/AdvancedTerminalAPI")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("5.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 Computerdores { public static class PluginInfo { public const string PLUGIN_GUID = "AdvancedTerminalAPI"; public const string PLUGIN_NAME = "AdvancedTerminalAPI"; public const string PLUGIN_VERSION = "1.0.0"; } } namespace Computerdores.AdvancedTerminalAPI { public struct CommandResult { public string output; public bool clearScreen; public bool success; public bool wantsMoreInput; public static readonly CommandResult IgnoreInput = new CommandResult(null, clearScreen: false, success: true, wantsMoreInput: true); public static readonly CommandResult GenericError = new CommandResult(null, clearScreen: true, success: false); public CommandResult(string output = null, bool clearScreen = true, bool success = true, bool wantsMoreInput = false) { this.output = output; this.clearScreen = clearScreen; this.success = success; this.wantsMoreInput = wantsMoreInput; } } public delegate void Consumer<in T>(T value); public interface IAliasable { IEnumerable<ICommand> GetAll(ITerminal term); } public interface ICommand { string GetName(); CommandResult Execute(string input, ITerminal terminal); ICommand CloneStateless(); } public interface IDescribable { string GetUsage(); string GetDescription(); } public class InputFieldDriver { private int _maxInputLength = 40; private readonly TMP_InputField _inputField; private string _displayedText = "\n\n\n"; private string _input = ""; public int MaxInputLength { get { return _maxInputLength; } set { _maxInputLength = value; if (_input.Length > _maxInputLength) { OnInputFieldChangedHandler(_inputField.text); } } } public string Input { get { return _input; } set { _input = value; _renderToInputField(); } } public Terminal VanillaTerminal { get; } public event Consumer<string> OnSubmit; public event Consumer<string> OnInputChange; public InputFieldDriver(Terminal __instance) { VanillaTerminal = __instance; _inputField = __instance.screenText; ((UnityEvent<string>)(object)_inputField.onValueChanged).AddListener((UnityAction<string>)OnInputFieldChangedHandler); ((UnityEvent<string>)(object)_inputField.onSubmit).AddListener((UnityAction<string>)OnInputFieldSubmitHandler); TerminalWrapper.Get(__instance).EnterTerminal += OnEnterTerminalHandler; } public void DisplayText([CanBeNull] string text, bool clearScreen) { if (clearScreen) { _displayedText = "\n"; } if (!Utility.IsNullOrWhiteSpace(text)) { if (!clearScreen) { _displayedText += Input; } _displayedText = _displayedText + "\n\n" + text; } Input = ""; _renderToInputField(); } public string GetDisplayedText() { return _displayedText; } private void _renderToInputField() { _inputField.text = _displayedText + Input; _inputField.caretPosition = _inputField.text.Length; } private void OnInputFieldChangedHandler(string newText) { if (newText.Length < _displayedText.Length) { Input = ""; } else { int length = Math.Min(_maxInputLength, newText.Length - _displayedText.Length); Input = newText.Substring(_displayedText.Length, length); } _renderToInputField(); this.OnInputChange?.Invoke(Input); } private void OnInputFieldSubmitHandler(string text) { this.OnSubmit?.Invoke(Input); _inputField.ActivateInputField(); ((Selectable)_inputField).Select(); } private void OnEnterTerminalHandler(bool firstTime) { Input = ""; } } public interface IPredictable { string PredictInput(string partialInput, ITerminal terminal); } public interface ITerminal { InputFieldDriver GetDriver(); void AddCommand(ICommand command); IEnumerable<ICommand> GetCommands(bool includeBuiltins); } public delegate T Method<out T, in T2>(T2 input); [BepInPlugin("AdvancedTerminalAPI", "AdvancedTerminalAPI", "1.0.0")] public class Plugin : BaseUnityPlugin { private readonly Harmony _harmony = new Harmony("AdvancedTerminalAPI"); private static Method<ITerminal, InputFieldDriver> _terminalConstructor; private static readonly List<ICommand> Commands = new List<ICommand>(); private static Plugin Instance { get; set; } internal static ManualLogSource Log => ((BaseUnityPlugin)Instance).Logger; private static InputFieldDriver Driver { get; set; } private static ITerminal CustomTerminal { get; set; } public Plugin() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown Instance = this; ReplaceITerminal((InputFieldDriver d) => new VanillinTerminal(d)); } public static void ReplaceITerminal(Method<ITerminal, InputFieldDriver> terminalConstructor) { _terminalConstructor = terminalConstructor; if (Driver != null) { ReplaceITerminalInstance(); } } public static void AddCommand(ICommand command) { Commands.Add(command); CustomTerminal.AddCommand(command); } internal static void RegisterTerminal(Terminal terminal) { Driver = new InputFieldDriver(terminal); ReplaceITerminalInstance(); } private static void ReplaceITerminalInstance() { CustomTerminal = _terminalConstructor(Driver); foreach (ICommand command in Commands) { CustomTerminal.AddCommand(command); } } private void Awake() { ApplyPatches(); } private void ApplyPatches() { _harmony.PatchAll(typeof(TerminalPatch)); } } public delegate void SimpleEvent(); public class TerminalWrapper { private readonly Terminal _terminal; private TerminalNode _lastLoadedNode; internal bool redirectLoadNewNode; private static readonly Dictionary<Terminal, TerminalWrapper> WrapperMap = new Dictionary<Terminal, TerminalWrapper>(); public bool TerminalInUse => _terminal.terminalInUse; public event Consumer<bool> EnterTerminal; public event SimpleEvent ExitTerminal; public event SimpleEvent PreAwake; public event SimpleEvent PostAwake; public event SimpleEvent PreStart; public event SimpleEvent PostStart; public event SimpleEvent PreUpdate; public event SimpleEvent PostUpdate; private TerminalWrapper(Terminal terminal) { _terminal = terminal; TerminalPatch.OnEnterTerminal += OnEnterTerminal; TerminalPatch.OnExitTerminal += OnExitTerminal; TerminalPatch.PreAwake += OnPreAwake; TerminalPatch.PostAwake += OnPostAwake; TerminalPatch.PreStart += OnPreStart; TerminalPatch.PostStart += OnPostStart; TerminalPatch.PreUpdate += OnPreUpdate; TerminalPatch.PostUpdate += OnPostUpdate; } public TerminalNode LoadNode(TerminalNode node) { if (node.itemCost != 0 || node.buyRerouteToMoon == -2) { _terminal.totalCostOfItems = node.itemCost * _terminal.playerDefinedAmount; } TerminalNode result = node; if (node.buyItemIndex != -1 || (node.buyRerouteToMoon != -1 && node.buyRerouteToMoon != -2) || node.shipUnlockableID != -1) { result = VanillaLoad(node, (Consumer<TerminalNode>)_terminal.LoadNewNodeIfAffordable); } else if (node.creatureFileID != -1) { result = VanillaLoad(node, (Consumer<TerminalNode>)_terminal.AttemptLoadCreatureFileNode); } else if (node.storyLogFileID != -1) { result = VanillaLoad(node, (Consumer<TerminalNode>)_terminal.AttemptLoadStoryLogFileNode); } else { LoadNewNode(node); } return result; } private TerminalNode VanillaLoad(TerminalNode node, Consumer<TerminalNode> loadMethod) { _lastLoadedNode = null; redirectLoadNewNode = true; loadMethod(node); redirectLoadNewNode = false; return _lastLoadedNode; } internal void LoadNewNode(TerminalNode node) { _terminal.RunTerminalEvents(node); ((Selectable)_terminal.screenText).interactable = true; _lastLoadedNode = node; if (node.playSyncedClip != -1) { _terminal.PlayTerminalAudioServerRpc(node.playSyncedClip); } else if ((Object)(object)node.playClip != (Object)null) { _terminal.terminalAudio.PlayOneShot(node.playClip); } _terminal.LoadTerminalImage(node); } private void OnEnterTerminal((bool firstTime, Terminal terminal) t) { if ((Object)(object)t.terminal == (Object)(object)_terminal) { this.EnterTerminal?.Invoke(t.firstTime); } } private void OnExitTerminal(Terminal terminal) { if ((Object)(object)terminal == (Object)(object)_terminal) { this.ExitTerminal?.Invoke(); } } private void OnPreAwake(Terminal terminal) { if ((Object)(object)terminal == (Object)(object)_terminal) { this.PreAwake?.Invoke(); } } private void OnPostAwake(Terminal terminal) { if ((Object)(object)terminal == (Object)(object)_terminal) { this.PostAwake?.Invoke(); } } private void OnPreStart(Terminal terminal) { if ((Object)(object)terminal == (Object)(object)_terminal) { this.PreStart?.Invoke(); } } private void OnPostStart(Terminal terminal) { if ((Object)(object)terminal == (Object)(object)_terminal) { this.PostStart?.Invoke(); } } private void OnPreUpdate(Terminal terminal) { if ((Object)(object)terminal == (Object)(object)_terminal) { this.PreUpdate?.Invoke(); } } private void OnPostUpdate(Terminal terminal) { if ((Object)(object)terminal == (Object)(object)_terminal) { this.PostUpdate?.Invoke(); } } public static TerminalWrapper Get(Terminal terminal) { if (WrapperMap.TryGetValue(terminal, out var value)) { return value; } WrapperMap[terminal] = new TerminalWrapper(terminal); return WrapperMap[terminal]; } } } namespace Computerdores.AdvancedTerminalAPI.Vanillin { public static class Util { public static TerminalNode GetSpecialNode(ITerminal terminal, int nodeIndex) { return GetSpecialNode(terminal.GetDriver().VanillaTerminal, nodeIndex); } public static TerminalNode GetSpecialNode(Terminal vanillaTerminal, int nodeIndex) { return vanillaTerminal.terminalNodes.specialNodes[nodeIndex]; } public static T VanillaStringMatch<T>(this IEnumerable<T> enumerable, string word, Converter<T, string> stringConverter, Predicate<T> predicate = null, int specificity = 3) { IEnumerable<T> source = (enumerable as T[]) ?? enumerable.ToArray(); T val = source.FirstOrDefault((T n) => string.Equals(stringConverter(n), word, StringComparison.CurrentCultureIgnoreCase) && (predicate?.Invoke(n) ?? true)); if (val != null || word.Length < specificity) { return val; } int spec; for (spec = word.Length; spec >= specificity; spec--) { if (val != null) { break; } val = source.FirstOrDefault((T n) => stringConverter(n).StartsWith(word.Substring(0, spec), StringComparison.CurrentCultureIgnoreCase) && (predicate?.Invoke(n) ?? true)); } return val; } public static CompatibleNoun FindTerminalOption(this TerminalNode node, string word, Predicate<CompatibleNoun> predicate = null) { return node.terminalOptions.VanillaStringMatch(word, (CompatibleNoun compatibleNoun) => compatibleNoun.noun.word, predicate, 1); } public static CompatibleNoun FindNoun(this TerminalKeyword keyword, string word, Predicate<CompatibleNoun> predicate = null) { return keyword.compatibleNouns.VanillaStringMatch(word, (CompatibleNoun compatibleNoun) => compatibleNoun.noun.word, predicate); } public static TerminalKeyword FindKeyword(Terminal vanillaTerm, string word, Predicate<TerminalKeyword> predicate = null) { return vanillaTerm.terminalNodes.allKeywords.VanillaStringMatch(word, (TerminalKeyword keyword) => keyword.word, predicate); } public static TerminalKeyword FindKeyword(ITerminal terminal, string word, Predicate<TerminalKeyword> predicate = null) { return FindKeyword(terminal.GetDriver().VanillaTerminal, word, predicate); } public static TerminalNode FindByKeyword(Terminal vanillaTerm, string word, Predicate<TerminalKeyword> predicate = null) { TerminalKeyword val = FindKeyword(vanillaTerm, word, predicate); return ((Object)(object)val != (Object)null) ? val.specialKeywordResult : null; } public static string TextPostProcess(this TerminalNode node, Terminal vanillaTerm) { return vanillaTerm.TextPostProcess(node.displayText, node); } public static int GetPlayerIndexByName(string name, int specificity = 3) { TransformAndName val = StartOfRound.Instance.mapScreen.radarTargets.VanillaStringMatch(name, (TransformAndName input) => input.name, null, specificity); if (val == null) { return -1; } return StartOfRound.Instance.mapScreen.radarTargets.IndexOf(val); } public static string PredictMoonName(Terminal terminal, string partialInput) { return ((Object)(object)terminal == (Object)null) ? partialInput : FindKeyword(terminal, "route").FindNoun(partialInput).noun.word; } public static string PredictConfirmation(string partialInput) { return new List<string> { "CONFIRM", "DENY" }.VanillaStringMatch(partialInput, (string s) => s, null, 1); } public static string PredictPlayerName(string partialInput) { int playerIndexByName = GetPlayerIndexByName(partialInput); return (playerIndexByName != -1) ? StartOfRound.Instance.mapScreen.radarTargets[playerIndexByName].name : partialInput; } public static string PredictBuyable(Terminal terminal, string partialInput) { return FindKeyword(terminal, "buy").compatibleNouns.VanillaStringMatch(partialInput, (CompatibleNoun cn) => cn.noun.word, (CompatibleNoun cn) => cn.result.shipUnlockableID != -1 || cn.result.buyItemIndex != -1)?.noun?.word; } } public class VanillinTerminal : ITerminal { protected readonly InputFieldDriver driver; protected readonly TerminalWrapper wrapper; protected ICommand currentCommand; protected List<ICommand> Commands { get; } = new List<ICommand>(); protected List<ICommand> BuiltinCommands { get; } = new List<ICommand>(); private static ManualLogSource Log => Plugin.Log; public VanillinTerminal(InputFieldDriver driver) { this.driver = driver; wrapper = TerminalWrapper.Get(driver.VanillaTerminal); this.driver.OnSubmit += OnSubmit; wrapper.EnterTerminal += OnEnterTerminal; AddBuiltinCommands(GetBuiltinCommands(GetDriver())); } public InputFieldDriver GetDriver() { return driver; } public virtual IEnumerable<ICommand> GetCommands(bool includeBuiltins) { IEnumerable<ICommand> result; if (!includeBuiltins) { IEnumerable<ICommand> commands = Commands; result = commands; } else { result = BuiltinCommands.Concat(Commands); } return result; } public void AddCommand(ICommand command) { AddCommand(Commands, command); } protected virtual void AddCommand(ICollection<ICommand> commands, ICommand command) { commands.Add(command); if (command is IAliasable aliasable) { CollectionExtensions.Do<ICommand>(aliasable.GetAll(this), (Action<ICommand>)delegate(ICommand cmd) { AddCommand(commands, cmd); }); } } public static IEnumerable<ICommand> GetBuiltinCommands(InputFieldDriver driver) { List<ICommand> list = new List<ICommand> { new SpecialNodeCommand("welcome", 1), new ViewCommand(), new SwitchCommand(), new PingCommand(), new TransmitCommand(), new ScanCommand(), new BuyCommand(), new RouteCommand(), new InfoCommand(), new EjectCommand(), new FlashCommand(), new OtherCommand() }; list.AddRange(SimpleCommand.GetAll()); list.AddRange(AccessibleObjectCommand.GetAll(driver)); return list; } private void AddBuiltinCommands(IEnumerable<ICommand> commands) { foreach (ICommand command in commands) { AddCommand(BuiltinCommands, command); } } protected virtual void OnSubmit(string text) { string input = text; if (currentCommand == null) { string[] array = text.Split(' '); input = ((array.Length == 1) ? "" : GeneralExtensions.Join<string>(array.Skip(1), (Func<string, string>)null, " ")); currentCommand = FindCommand(array[0])?.CloneStateless(); } ICommand command = currentCommand; if (command != null) { Log.LogInfo((object)("Executing Command (" + currentCommand.GetName() + ") for input : '" + text + "'")); CommandResult commandResult; try { commandResult = command.Execute(input, this); } catch (Exception arg) { commandResult = new CommandResult("An Error occured while executing the command.\nPlease contact the author of the mod that the command is from.\n\n"); Log.LogInfo((object)$"An error occurred during execution of '{currentCommand.GetName()}': {arg}"); } if (commandResult.success) { driver.DisplayText(commandResult.output, commandResult.clearScreen); } else { Log.LogInfo((object)("Command execution was not successful for input (" + currentCommand.GetName() + "): '" + text + "'")); driver.DisplayText(Utility.IsNullOrWhiteSpace(commandResult.output) ? SpecialText(11) : commandResult.output, commandResult.clearScreen); } if (!commandResult.wantsMoreInput) { currentCommand = null; } } else if (text != "") { Log.LogInfo((object)("Did not find Command for input: '" + text + "'")); driver.DisplayText(SpecialText(10), clearScreen: true); } } protected virtual void OnEnterTerminal(bool firstTime) { ICommand command = (firstTime ? FindCommand("welcome") : FindCommand("help")); Log.LogInfo((object)("Entering Terminal" + (firstTime ? " for the first time" : "") + ".")); driver.DisplayText(command?.Execute("", this).output, clearScreen: true); } protected virtual ICommand FindCommand(string command) { return FindCommand(Commands, command) ?? FindCommand(BuiltinCommands, command); } protected static ICommand FindCommand(IEnumerable<ICommand> commands, string command) { return commands.VanillaStringMatch(command, (ICommand s) => s.GetName()); } protected string SpecialText(int i) { return Util.GetSpecialNode(driver.VanillaTerminal, i).displayText; } private void DebugLogNodeInfo() { for (int i = 0; i < driver.VanillaTerminal.terminalNodes.specialNodes.Count; i++) { Log.LogDebug((object)($"Special Node ({i}): '" + driver.VanillaTerminal.terminalNodes.specialNodes[i].displayText.Replace("\n", "\\n") + "'")); } for (int j = 0; j < driver.VanillaTerminal.terminalNodes.allKeywords.Length; j++) { TerminalKeyword val = driver.VanillaTerminal.terminalNodes.allKeywords[j]; string text = (((Object)(object)val.specialKeywordResult == (Object)null) ? null : val.specialKeywordResult.displayText?.Replace("\n", "\\n")); Log.LogDebug((object)($"Keyword ({j}), " + (val.isVerb ? "Verb" : "Noun") + " '" + val.word + "'" + ((text != null) ? (": '" + text + "' ") : ""))); } } } } namespace Computerdores.AdvancedTerminalAPI.Vanillin.Commands { public class AccessibleObjectCommand : ICommand { private readonly string _name; public AccessibleObjectCommand(string name) { _name = name; } public string GetName() { return _name; } public CommandResult Execute(string input, ITerminal terminal) { Terminal vanillaTerminal = terminal.GetDriver().VanillaTerminal; vanillaTerminal.CallFunctionInAccessibleTerminalObject(_name); vanillaTerminal.PlayBroadcastCodeEffect(); return new CommandResult(Util.GetSpecialNode(vanillaTerminal, 19).displayText); } public ICommand CloneStateless() { return new AccessibleObjectCommand(_name); } public static IEnumerable<AccessibleObjectCommand> GetAll(InputFieldDriver driver) { return from keyword in driver.VanillaTerminal.terminalNodes.allKeywords where keyword.accessTerminalObjects select new AccessibleObjectCommand(keyword.word); } } public class BuyCommand : ICommand, IAliasable, IPredictable { private bool _awaitingConfirmation; private ICommand _command; public string GetName() { return "buy"; } public string PredictInput(string partialInput, ITerminal terminal) { return _awaitingConfirmation ? Util.PredictConfirmation(partialInput) : FromPlayerInput(terminal.GetDriver().VanillaTerminal, partialInput).GetName(); } public CommandResult Execute(string input, ITerminal terminal) { if (_awaitingConfirmation) { return _command.Execute(input, terminal); } _awaitingConfirmation = true; string[] source = input.Split(' '); _command = FromPlayerInput(terminal.GetDriver().VanillaTerminal, source.First()); return _command?.Execute(GeneralExtensions.Join<string>(source.Skip(1), (Func<string, string>)null, " "), terminal) ?? CommandResult.IgnoreInput; } public ICommand CloneStateless() { return new BuyCommand(); } public IEnumerable<ICommand> GetAll(ITerminal term) { return from cn in Util.FindKeyword(term, "buy").compatibleNouns where cn.result.shipUnlockableID != -1 || cn.result.buyItemIndex != -1 select FromCompatibleNoun(cn); } private static ICommand FromPlayerInput(Terminal term, string input) { CompatibleNoun cn2 = Util.FindKeyword(term, "buy").FindNoun(input, (CompatibleNoun cn) => cn.result.shipUnlockableID != -1 || cn.result.buyItemIndex != -1); return FromCompatibleNoun(cn2); } private static ICommand FromCompatibleNoun(CompatibleNoun cn) { if (cn.result.buyItemIndex != -1) { return new BuyItemCommand(cn.noun.word); } return new BuyUnlockableCommand(cn.noun.word); } } public class BuyItemCommand : ICommand, IPredictable { private readonly string _itemName; private bool _awaitingConfirmation; private CompatibleNoun _item; public BuyItemCommand(string itemName) { _itemName = itemName; } public string GetName() { return _itemName; } public string PredictInput(string partialInput, ITerminal terminal) { return _awaitingConfirmation ? Util.PredictConfirmation(partialInput) : partialInput; } public CommandResult Execute(string input, ITerminal terminal) { Terminal vanillaTerminal = terminal.GetDriver().VanillaTerminal; TerminalNode val; if (!_awaitingConfirmation) { _item = Util.FindKeyword(terminal, "buy").FindNoun(_itemName); string value = Regex.Match(input, "\\d+").Value; vanillaTerminal.playerDefinedAmount = (Utility.IsNullOrWhiteSpace(value) ? 1 : Mathf.Clamp(int.Parse(value), 0, 10)); val = TerminalWrapper.Get(vanillaTerminal).LoadNode(_item.result); _awaitingConfirmation = val.isConfirmationNode; return new CommandResult(val.TextPostProcess(vanillaTerminal), val.clearPreviousText, success: true, _awaitingConfirmation); } CompatibleNoun val2 = _item.result.FindTerminalOption(input); if (val2 == null) { return CommandResult.IgnoreInput; } val = TerminalWrapper.Get(vanillaTerminal).LoadNode(val2.result); return new CommandResult(val.TextPostProcess(vanillaTerminal), val.clearPreviousText); } public ICommand CloneStateless() { return new BuyItemCommand(_itemName); } } public class BuyUnlockableCommand : ICommand, IPredictable { private readonly string _itemName; private bool _awaitingConfirmation; private CompatibleNoun _item; public BuyUnlockableCommand(string itemName) { _itemName = itemName; } public string GetName() { return _itemName; } public string PredictInput(string partialInput, ITerminal terminal) { return _awaitingConfirmation ? Util.PredictConfirmation(partialInput) : partialInput; } public CommandResult Execute(string input, ITerminal terminal) { Terminal vanillaTerminal = terminal.GetDriver().VanillaTerminal; TerminalNode val; if (!_awaitingConfirmation) { _item = Util.FindKeyword(terminal, "buy").FindNoun(_itemName); val = TerminalWrapper.Get(vanillaTerminal).LoadNode(_item.result); CompatibleNoun[] terminalOptions = val.terminalOptions; _awaitingConfirmation = ((terminalOptions != null && terminalOptions.Length != 0) ? 1 : 0) > (false ? 1 : 0); return new CommandResult(val.TextPostProcess(vanillaTerminal), val.clearPreviousText, success: true, _awaitingConfirmation); } CompatibleNoun val2 = _item.result.FindTerminalOption(input); if (val2 == null) { return CommandResult.IgnoreInput; } val = TerminalWrapper.Get(vanillaTerminal).LoadNode(val2.result); return new CommandResult(val.TextPostProcess(vanillaTerminal), val.clearPreviousText); } public ICommand CloneStateless() { return new BuyUnlockableCommand(_itemName); } } public class EjectCommand : ICommand, IPredictable { private bool _awaitingConfirmation; public string GetName() { return "eject"; } public string PredictInput(string partialInput, ITerminal terminal) { return _awaitingConfirmation ? Util.PredictConfirmation(partialInput) : partialInput; } public CommandResult Execute(string input, ITerminal terminal) { Terminal vanillaTerminal = terminal.GetDriver().VanillaTerminal; if (!_awaitingConfirmation) { _awaitingConfirmation = true; return new CommandResult(Util.FindByKeyword(vanillaTerminal, "eject").displayText, clearScreen: true, success: true, wantsMoreInput: true); } TerminalNode val = Util.FindByKeyword(vanillaTerminal, "eject").FindTerminalOption(input)?.result; if ((Object)(object)val == (Object)null) { return CommandResult.IgnoreInput; } val = TerminalWrapper.Get(vanillaTerminal).LoadNode(val); return new CommandResult(val.TextPostProcess(vanillaTerminal)); } public ICommand CloneStateless() { return new EjectCommand(); } } public class FlashCommand : ICommand, IPredictable { public string GetName() { return "flash"; } public string PredictInput(string partialInput, ITerminal terminal) { return Util.PredictPlayerName(partialInput); } public CommandResult Execute(string input, ITerminal terminal) { int num = Util.GetPlayerIndexByName(input); if (num == -1) { num = StartOfRound.Instance.mapScreen.targetTransformIndex; if (!StartOfRound.Instance.mapScreen.radarTargets[num].isNonPlayer) { return CommandResult.GenericError; } } StartOfRound.Instance.mapScreen.FlashRadarBooster(num); return new CommandResult(Util.GetSpecialNode(terminal, 23).displayText); } public ICommand CloneStateless() { return new FlashCommand(); } } public class InfoCommand : ICommand, IAliasable, IPredictable { public string GetName() { return "info"; } public string PredictInput(string partialInput, ITerminal terminal) { TerminalKeyword val = Util.FindKeyword(terminal.GetDriver().VanillaTerminal, "info"); TerminalKeyword val2 = val.compatibleNouns.VanillaStringMatch(partialInput, (CompatibleNoun cn) => cn.noun.word)?.noun; if (val2 != null) { return val2.word; } return partialInput; } public CommandResult Execute(string input, ITerminal terminal) { string[] array = input.Split(' '); return new InfoThingCommand(array[0]).Execute(GeneralExtensions.Join<string>(array.Skip(1), (Func<string, string>)null, " "), terminal); } public ICommand CloneStateless() { return new InfoCommand(); } public IEnumerable<ICommand> GetAll(ITerminal term) { TerminalKeyword kw = Util.FindKeyword(term.GetDriver().VanillaTerminal, "info"); return from a in kw.compatibleNouns where (Object)(object)a.noun.defaultVerb == (Object)(object)kw select new InfoThingCommand(a.noun.word); } } public class InfoThingCommand : ICommand { private readonly string _name; public InfoThingCommand(string name) { _name = name; } public string GetName() { return _name; } public CommandResult Execute(string input, ITerminal terminal) { Terminal vanillaTerminal = terminal.GetDriver().VanillaTerminal; TerminalNode val = Util.FindKeyword(vanillaTerminal, "info").FindNoun(_name).result; if (val.creatureFileID != -1) { val = TerminalWrapper.Get(vanillaTerminal).LoadNode(val); } return ((Object)(object)val == (Object)null) ? CommandResult.GenericError : new CommandResult(val.TextPostProcess(vanillaTerminal), val.clearPreviousText); } public ICommand CloneStateless() { return new InfoThingCommand(_name); } } public class OtherCommand : ICommand { public string GetName() { return "other"; } public CommandResult Execute(string input, ITerminal terminal) { string text = "Other commands:\n\n"; foreach (ICommand command in terminal.GetCommands(includeBuiltins: true)) { if (command is IDescribable describable) { text = text + ">" + describable.GetUsage() + "\n" + describable.GetDescription() + "\n\n"; } } text += "\n"; return new CommandResult(text); } public ICommand CloneStateless() { return new OtherCommand(); } } public class PingCommand : ICommand, IPredictable, IDescribable { public string GetName() { return "ping"; } public string PredictInput(string partialInput, ITerminal terminal) { return Util.PredictPlayerName(partialInput); } public CommandResult Execute(string input, ITerminal terminal) { int playerIndexByName = Util.GetPlayerIndexByName(input); if (playerIndexByName == -1) { return CommandResult.GenericError; } StartOfRound.Instance.mapScreen.PingRadarBooster(playerIndexByName); return new CommandResult(Util.GetSpecialNode(terminal, 21).displayText); } public ICommand CloneStateless() { return new PingCommand(); } public string GetUsage() { return "PING [Radar booster name]"; } public string GetDescription() { return "To make a radar booster play a noise."; } } public class RouteCommand : ICommand, IPredictable, IAliasable { private RouteMoonCommand _command; private bool _awaitingConfirmation; public string GetName() { return "route"; } public string PredictInput(string partialInput, ITerminal terminal) { return _awaitingConfirmation ? Util.PredictConfirmation(partialInput) : Util.PredictMoonName(terminal.GetDriver().VanillaTerminal, partialInput); } public CommandResult Execute(string input, ITerminal terminal) { if (_awaitingConfirmation) { return _command.Execute(input, terminal); } _awaitingConfirmation = true; string[] source = input.Split(' '); _command = RouteMoonCommand.FromPlayerInput(terminal.GetDriver().VanillaTerminal, source.First()); return _command?.Execute(GeneralExtensions.Join<string>(source.Skip(1), (Func<string, string>)null, " "), terminal) ?? CommandResult.IgnoreInput; } public IEnumerable<ICommand> GetAll(ITerminal term) { return Util.FindKeyword(term, "route").compatibleNouns.Select((CompatibleNoun noun) => new RouteMoonCommand(noun.noun.word)); } public ICommand CloneStateless() { return new RouteCommand(); } } public class RouteMoonCommand : ICommand, IPredictable { private readonly string _moonName; private bool _awaitingConfirmation; private CompatibleNoun _moon; public RouteMoonCommand(string moonName) { _moonName = moonName; } public string PredictInput(string partialInput, ITerminal terminal) { return _awaitingConfirmation ? Util.PredictConfirmation(partialInput) : partialInput; } public string GetName() { return _moonName; } public CommandResult Execute(string input, ITerminal terminal) { Terminal vanillaTerminal = terminal.GetDriver().VanillaTerminal; TerminalNode val; if (!_awaitingConfirmation) { _moon = Util.FindKeyword(terminal, "route").FindNoun(_moonName); if (_moon.result.itemCost != 0 || _moon.result.buyRerouteToMoon == -2) { vanillaTerminal.totalCostOfItems = _moon.result.itemCost; } val = TerminalWrapper.Get(vanillaTerminal).LoadNode(_moon.result); CompatibleNoun[] terminalOptions = val.terminalOptions; _awaitingConfirmation = ((terminalOptions != null && terminalOptions.Length != 0) ? 1 : 0) > (false ? 1 : 0); return new CommandResult(val.TextPostProcess(vanillaTerminal), val.clearPreviousText, success: true, _awaitingConfirmation); } CompatibleNoun val2 = _moon.result.FindTerminalOption(input); if (val2 == null) { return CommandResult.IgnoreInput; } val = TerminalWrapper.Get(vanillaTerminal).LoadNode(val2.result); return new CommandResult(val.TextPostProcess(vanillaTerminal), val.clearPreviousText); } public ICommand CloneStateless() { return new RouteMoonCommand(_moonName); } public static RouteMoonCommand FromPlayerInput(Terminal term, string input) { return new RouteMoonCommand(Util.FindKeyword(term, "route").FindNoun(input).noun.word); } } public class ScanCommand : ICommand, IDescribable { public string GetName() { return "scan"; } public CommandResult Execute(string input, ITerminal terminal) { Terminal vanillaTerminal = terminal.GetDriver().VanillaTerminal; TerminalNode val = Util.FindByKeyword(terminal.GetDriver().VanillaTerminal, "scan"); return new CommandResult(vanillaTerminal.TextPostProcess(val.displayText, val)); } public ICommand CloneStateless() { return new ScanCommand(); } public string GetUsage() { return "SCAN"; } public string GetDescription() { return "To scan for the number of items left on the current planet."; } } public class SimpleCommand : ICommand { private string _name; public SimpleCommand(string name) { _name = name; } public string GetName() { return _name; } public CommandResult Execute(string input, ITerminal terminal) { Terminal vanillaTerminal = terminal.GetDriver().VanillaTerminal; TerminalNode specialKeywordResult = Util.FindKeyword(vanillaTerminal, _name).specialKeywordResult; return ((Object)(object)specialKeywordResult == (Object)null) ? CommandResult.GenericError : new CommandResult(specialKeywordResult.TextPostProcess(vanillaTerminal), specialKeywordResult.clearPreviousText); } public ICommand CloneStateless() { return new SimpleCommand(_name); } public static IEnumerable<SimpleCommand> GetAll() { return new string[8] { "help", "moons", "store", "upgrades", "decor", "storage", "sigurd", "bestiary" }.Select((string s) => new SimpleCommand(s)); } } public class SpecialNodeCommand : ICommand { private readonly string _name; private readonly int _index; public SpecialNodeCommand(string name, int index) { _name = name; _index = index; } public string GetName() { return _name; } public CommandResult Execute(string input, ITerminal terminal) { Terminal vanillaTerminal = terminal.GetDriver().VanillaTerminal; return new CommandResult(Util.GetSpecialNode(vanillaTerminal, _index).TextPostProcess(vanillaTerminal)); } public ICommand CloneStateless() { return new SpecialNodeCommand(_name, _index); } } public class SwitchCommand : ICommand, IPredictable, IDescribable { public string GetName() { return "switch"; } public string PredictInput(string partialInput, ITerminal terminal) { return Util.PredictPlayerName(partialInput); } public CommandResult Execute(string input, ITerminal terminal) { int playerIndexByName = Util.GetPlayerIndexByName(input); if (playerIndexByName == -1 && Utility.IsNullOrWhiteSpace(input)) { return CommandResult.GenericError; } if (playerIndexByName != -1) { StartOfRound.Instance.mapScreen.SwitchRadarTargetAndSync(playerIndexByName); } else { StartOfRound.Instance.mapScreen.SwitchRadarTargetForward(true); } return new CommandResult(Util.FindByKeyword(terminal.GetDriver().VanillaTerminal, "switch").displayText); } public ICommand CloneStateless() { return new SwitchCommand(); } public string GetUsage() { return "SWITCH [Player name]"; } public string GetDescription() { return "To switch view to a player on the main monitor."; } } public class TransmitCommand : ICommand, IDescribable { public string GetName() { return "transmit"; } public CommandResult Execute(string input, ITerminal terminal) { Terminal vanillaTerminal = terminal.GetDriver().VanillaTerminal; SignalTranslator val = Object.FindObjectOfType<SignalTranslator>(); if ((Object)(object)val == (Object)null || !((double)Time.realtimeSinceStartup - (double)val.timeLastUsingSignalTranslator > 8.0) || string.IsNullOrEmpty(input)) { return CommandResult.GenericError; } if (!((NetworkBehaviour)vanillaTerminal).IsServer) { val.timeLastUsingSignalTranslator = Time.realtimeSinceStartup; } HUDManager.Instance.UseSignalTranslatorServerRpc(input.Substring(0, Mathf.Min(input.Length, 10))); TerminalNode specialNode = Util.GetSpecialNode(terminal, 22); return new CommandResult(specialNode.displayText, specialNode.clearPreviousText); } public ICommand CloneStateless() { return new TransmitCommand(); } public string GetUsage() { return "TRANSMIT [message]"; } public string GetDescription() { return "To transmit a message with the signal translator."; } } public class ViewCommand : ICommand, IAliasable, IDescribable, IPredictable { public string GetName() { return "view"; } public string PredictInput(string partialInput, ITerminal terminal) { return Util.FindKeyword(terminal, "view").compatibleNouns.VanillaStringMatch(partialInput, (CompatibleNoun cn) => cn.noun.word, (CompatibleNoun cn) => (Object)(object)cn.noun.defaultVerb != (Object)null).noun.word; } public CommandResult Execute(string input, ITerminal terminal) { string[] array = input.Split(' '); ViewThingCommand viewThingCommand = new ViewThingCommand(array[0]); return viewThingCommand.Execute(GeneralExtensions.Join<string>(array.Skip(1), (Func<string, string>)null, " "), terminal); } public ICommand CloneStateless() { return new ViewCommand(); } public IEnumerable<ICommand> GetAll(ITerminal term) { return from noun in Util.FindKeyword(term, "view").compatibleNouns where (Object)(object)noun.noun.defaultVerb != (Object)null select new ViewThingCommand(noun.noun.word); } public string GetUsage() { return "VIEW MONITOR"; } public string GetDescription() { return "To toggle on AND off the main monitor's map cam."; } } public class ViewThingCommand : ICommand { private readonly string _name; public ViewThingCommand(string name) { _name = name; } public string GetName() { return _name; } public CommandResult Execute(string input, ITerminal terminal) { Terminal vanillaTerminal = terminal.GetDriver().VanillaTerminal; TerminalNode result = Util.FindKeyword(terminal, "view").FindNoun(_name).result; result = TerminalWrapper.Get(vanillaTerminal).LoadNode(result); return new CommandResult(result.TextPostProcess(vanillaTerminal), result.clearPreviousText); } public ICommand CloneStateless() { return new ViewThingCommand(_name); } } } namespace Computerdores.AdvancedTerminalAPI.patch { [HarmonyPatch(typeof(Terminal))] public static class TerminalPatch { internal static event Consumer<(bool, Terminal)> OnEnterTerminal; internal static event Consumer<Terminal> OnExitTerminal; internal static event Consumer<Terminal> PreAwake; internal static event Consumer<Terminal> PostAwake; internal static event Consumer<Terminal> PreStart; internal static event Consumer<Terminal> PostStart; internal static event Consumer<Terminal> PreUpdate; internal static event Consumer<Terminal> PostUpdate; [HarmonyPrefix] [HarmonyPatch("LoadNewNode")] private static bool InterceptLoadNewNode(this Terminal __instance, TerminalNode node) { TerminalWrapper terminalWrapper = TerminalWrapper.Get(__instance); if (!terminalWrapper.redirectLoadNewNode) { return true; } terminalWrapper.LoadNewNode(node); return false; } [HarmonyPrefix] [HarmonyPatch("OnSubmit")] private static bool OnSubmitPrefix() { return false; } [HarmonyPrefix] [HarmonyPatch("TextChanged")] private static bool TextChangedPrefix() { return false; } [HarmonyPostfix] [HarmonyPatch("BeginUsingTerminal")] private static void BeginUsingTerminalPostfix(Terminal __instance) { TerminalPatch.OnEnterTerminal?.Invoke((!__instance.usedTerminalThisSession, __instance)); } [HarmonyPrefix] [HarmonyPatch("QuitTerminal")] private static void QuitTerminalPrefix(Terminal __instance) { TerminalPatch.OnExitTerminal?.Invoke(__instance); } [HarmonyPrefix] [HarmonyPatch("Awake")] private static void AwakePrefix(Terminal __instance) { Plugin.RegisterTerminal(__instance); TerminalPatch.PreAwake?.Invoke(__instance); } [HarmonyPostfix] [HarmonyPatch("Awake")] private static void AwakePostfix(Terminal __instance) { TerminalPatch.PostAwake?.Invoke(__instance); } [HarmonyPrefix] [HarmonyPatch("Start")] private static void StartPrefix(Terminal __instance) { TerminalPatch.PreStart?.Invoke(__instance); } [HarmonyPostfix] [HarmonyPatch("Start")] private static void StartPostfix(Terminal __instance) { TerminalPatch.PostStart?.Invoke(__instance); } [HarmonyPrefix] [HarmonyPatch("Update")] private static void UpdatePrefix(Terminal __instance) { TerminalPatch.PreUpdate?.Invoke(__instance); } [HarmonyPostfix] [HarmonyPatch("Update")] private static void UpdatePostfix(Terminal __instance) { TerminalPatch.PostUpdate?.Invoke(__instance); } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } } namespace AdvancedTerminalAPI.NetcodePatcher { [AttributeUsage(AttributeTargets.Module)] internal class NetcodePatchedAssemblyAttribute : Attribute { } }