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 ClickableTerminal v0.1.0
ClickableTerminal.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using ClickableTerminal.Api; using GameNetcodeStuff; using HarmonyLib; using LethalCompanyInputUtils.Glyphs; using Microsoft.CodeAnalysis; using TMPro; using Unity.Collections; using Unity.Netcode; using UnityEngine; using UnityEngine.Events; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.InputSystem.LowLevel; using UnityEngine.InputSystem.Utilities; using UnityEngine.TextCore; using UnityEngine.UI; using UnityEngine.Video; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ClickableTerminal")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Interactive terminal with mouse and controller support.")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0+6308c2a2b380ed7c9e020c0f20eeeaa84a3428c5")] [assembly: AssemblyProduct("ClickableTerminal")] [assembly: AssemblyTitle("ClickableTerminal")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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 ClickableTerminal { internal class InteractiveMouse { private static readonly Regex Tags = new Regex("<[^>]+>", RegexOptions.Compiled); private readonly List<HotSpot> spots = new List<HotSpot>(); private readonly StringBuilder builder = new StringBuilder(); private string scanned; private object scannedMenu; private int scanIn; private bool screenChanged; internal bool TakeScreenChange() { bool result = screenChanged; screenChanged = false; return result; } internal void Reset() { spots.Clear(); scanned = null; scannedMenu = null; } internal void Tick(string screen, TMP_Text text) { object app = InteractiveTerminal.Application(); object obj = InteractiveTerminal.Menu(app); if (obj != scannedMenu) { screenChanged = true; } if (screen != scanned || obj != scannedMenu) { scanned = screen; scannedMenu = obj; scanIn = 2; } if (scanIn > 0 && --scanIn == 0) { Scan(text, app, obj); } } internal void Targets(RectTransform frame, List<Vector2> into) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) Vector2 val = default(Vector2); foreach (HotSpot spot in spots) { ((Vector2)(ref val))..ctor(Mathf.Lerp(((Rect)(ref spot.Area)).xMin, ((Rect)(ref spot.Area)).xMax, 0.12f), ((Rect)(ref spot.Area)).center.y); into.Add(Vector2.op_Implicit(((Transform)frame).InverseTransformPoint(((Transform)spot.Space).TransformPoint(Vector2.op_Implicit(val))))); } } internal HotSpot SpotAt(Vector3 world) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) foreach (HotSpot spot in spots) { if (((Rect)(ref spot.Area)).Contains(((Transform)spot.Space).InverseTransformPoint(world))) { return spot; } } return null; } private void Scan(TMP_Text text, object app, object menu) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Unknown result type (might be due to invalid IL or missing references) spots.Clear(); Array array = InteractiveTerminal.Elements(menu); TMP_TextInfo textInfo = text.textInfo; if (array == null || textInfo == null || textInfo.lineInfo == null || textInfo.characterInfo == null) { return; } int num = Mathf.Min(textInfo.lineCount, textInfo.lineInfo.Length); string[] array2 = new string[num]; for (int i = 0; i < num; i++) { array2[i] = LineText(textInfo, textInfo.lineInfo[i]); } List<int> list = new List<int>(); List<int> list2 = new List<int>(); int num2 = 0; for (int j = 0; j < array.Length; j++) { object value = array.GetValue(j); string text2 = ((value != null) ? InteractiveTerminal.TextOf(value) : null); string text3 = ((text2 != null) ? Key(text2) : ""); if (text3.Length == 0) { continue; } for (int k = num2; k < num; k++) { if (array2[k].Contains(text3)) { list.Add(k); list2.Add(j); num2 = k + 1; break; } } } for (int l = 0; l < list.Count; l++) { int index = list2[l]; object element = array.GetValue(index); int num3 = list[l]; int m = ((l + 1 < list.Count) ? (list[l + 1] - 1) : num3); if (l + 1 == list.Count) { for (; m + 1 < num && HasWords(array2[m + 1]) && !array2[m + 1].Contains("Page "); m++) { } } while (m > num3 && !HasWords(array2[m])) { m--; } bool num4 = InteractiveTerminal.CanSelect(element); HotSpot hotSpot = new HotSpot { Space = text.rectTransform, Area = AreaOf(text, textInfo, num3, m) }; if (num4) { hotSpot.Hover = delegate { InteractiveTerminal.Select(menu, index); }; hotSpot.Click = delegate { InteractiveTerminal.Select(menu, index); InteractiveTerminal.Submit(app); }; if (InteractiveTerminal.HasCounter(element)) { hotSpot.Scroll = delegate(bool up) { InteractiveTerminal.Count(element, up); }; } } spots.Add(hotSpot); } } private static string Key(string name) { string text = Tags.Replace(name, "").Split('\n')[0].Trim(); return ((text.Length > 14) ? text.Substring(0, 14) : text).TrimEnd(); } private string LineText(TMP_TextInfo info, TMP_LineInfo line) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) builder.Clear(); int num = Mathf.Min(line.lastCharacterIndex, info.characterInfo.Length - 1); for (int i = line.firstCharacterIndex; i <= num; i++) { builder.Append(info.characterInfo[i].character); } return builder.ToString(); } private static bool HasWords(string line) { for (int i = 0; i < line.Length; i++) { if (char.IsLetterOrDigit(line[i])) { return true; } } return false; } private static Rect AreaOf(TMP_Text text, TMP_TextInfo info, int first, int last) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) float num = float.MaxValue; float num2 = float.MinValue; for (int i = first; i <= last; i++) { TMP_LineInfo val = info.lineInfo[i]; int num3 = Mathf.Min(val.lastCharacterIndex, info.characterInfo.Length - 1); for (int j = val.firstCharacterIndex; j <= num3; j++) { if (!char.IsWhiteSpace(info.characterInfo[j].character)) { num = Mathf.Min(num, info.characterInfo[j].bottomLeft.x); num2 = Mathf.Max(num2, info.characterInfo[j].topRight.x); } } } if (num > num2) { num = 0f; num2 = 0f; } float ascender = info.lineInfo[first].ascender; float descender = info.lineInfo[last].descender; return new Rect(num, descender, num2 - num, ascender - descender); } } internal static class InteractiveTerminal { private static bool looked; private static FieldInfo instance; private static FieldInfo applications; private static FieldInfo application; private static FieldInfo exitAction; private static InputControl<float> held; private static int releaseIn; private static int pressedAt = -100; internal static bool PressingKey => Time.frameCount - pressedAt < 6; internal static bool RunExitHandlers() { //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) Find(); try { InputAction val = (InputAction)((exitAction != null) ? /*isinst with value type is only supported in some contexts*/: null); object obj = ((val == null) ? null : AccessTools.Field(typeof(InputAction), "m_OnPerformed")?.GetValue(val)); if (obj == null) { return false; } int num = (int)obj.GetType().GetProperty("length").GetValue(obj); PropertyInfo property = obj.GetType().GetProperty("Item"); List<Action<CallbackContext>> list = new List<Action<CallbackContext>>(); for (int i = 0; i < num; i++) { if (property.GetValue(obj, new object[1] { i }) is Action<CallbackContext> item) { list.Add(item); } } foreach (Action<CallbackContext> item2 in list) { item2(default(CallbackContext)); } return list.Count > 0; } catch (Exception ex) { Plugin.mlg.LogWarning((object)("Interactive Terminal API: could not run its exit handlers: " + (ex.InnerException ?? ex).Message)); return false; } } internal static bool PressExitKey() { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) Find(); try { InputAction val = (InputAction)((exitAction != null) ? /*isinst with value type is only supported in some contexts*/: null); InputControl<float> val2 = ((val != null && val.controls.Count > 0) ? (val.controls[0] as InputControl<float>) : null); if (val2 == null) { return false; } Queue(val2, 1f); held = val2; releaseIn = 2; pressedAt = Time.frameCount; return true; } catch (Exception ex) { Plugin.mlg.LogWarning((object)("Interactive Terminal API: could not press its exit key: " + ex.Message)); return false; } } internal static void Tick() { if (held != null && --releaseIn <= 0) { try { Queue(held, 0f); } catch (Exception) { } held = null; } } private static void Queue(InputControl<float> control, float value) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) InputEventPtr val2 = default(InputEventPtr); NativeArray<byte> val = StateEvent.From(((InputControl)control).device, ref val2, (Allocator)2); try { InputControlExtensions.WriteValueIntoEvent<float>(control, value, val2); InputSystem.QueueEvent(val2); } finally { ((IDisposable)val/*cast due to .constrained prefix*/).Dispose(); } } internal static bool Running() { return Manager() != null; } internal static string Registered(string command) { foreach (string item in Commands()) { if (string.Equals(item, command, StringComparison.OrdinalIgnoreCase)) { return item; } } return null; } internal static IEnumerable<string> Commands() { Find(); List<string> list = new List<string>(); try { if (applications?.GetValue(null) is IDictionary dictionary) { foreach (object key in dictionary.Keys) { list.Add(key.ToString()); } } } catch (Exception ex) { Plugin.WarnOnce("Could not read the Interactive Terminal API applications: " + ex.Message); } return list; } internal static object Application() { object obj = Manager(); if (obj == null || !(application != null)) { return null; } return application.GetValue(obj); } internal static object Menu(object app) { if (app == null) { return null; } return Traverse.Create(app).Field("currentCursorMenu").GetValue(); } internal static Array Elements(object menu) { if (menu == null) { return null; } return Traverse.Create(menu).Field("elements").GetValue() as Array; } internal static string TextOf(object element) { try { MethodInfo method = element.GetType().GetMethod("GetText", new Type[1] { typeof(int) }); if (method != null && method.Invoke(element, new object[1] { 49 }) is string text && !string.IsNullOrWhiteSpace(text)) { return text; } } catch (Exception) { } return Traverse.Create(element).Property("Name", (object[])null).GetValue() as string; } internal static bool CanSelect(object element) { if (element == null) { return false; } if (Traverse.Create(element).Property("Active", (object[])null).GetValue() is Delegate obj && !(bool)obj.DynamicInvoke(element)) { return (Traverse.Create(element).Property("SelectInactive", (object[])null).GetValue() as bool?) ?? true; } return true; } internal static void Select(object menu, int index) { Traverse.Create(menu).Field("cursorIndex").SetValue((object)index); } internal static void Submit(object app) { Call(app, "Submit"); } internal static bool HasPages(object app) { if (app == null) { return false; } object value = Traverse.Create(app).Field("currentPage").GetValue(); if (value == null || !(Traverse.Create(value).Field("elements").GetValue() is Array { Length: >=2 } array)) { return false; } object value2 = Traverse.Create(app).Field("currentScreen").GetValue(); int valueOrDefault = (Traverse.Create(value).Field("pageIndex").GetValue() as int?).GetValueOrDefault(); if (valueOrDefault >= 0 && valueOrDefault < array.Length) { return value2 == array.GetValue(valueOrDefault); } return false; } internal static bool Page(object app, bool forward) { if (HasPages(app)) { return Call(app, forward ? "ChangeScreenForward" : "ChangeScreenBackward"); } return false; } internal static bool HasCounter(object element) { if (element != null && element.GetType().GetMethod("IncrementCounter") != null) { return Traverse.Create(element).Property("ShowCounter", (object[])null).GetValue() as bool? == true; } return false; } internal static void Count(object element, bool up) { Call(element, up ? "IncrementCounter" : "DecrementCounter"); } internal static void Exit(object app) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) Call(app, "OnScreenExit", (object)default(CallbackContext)); } private static bool Call(object target, string method, params object[] arguments) { try { MethodInfo methodInfo = target?.GetType().GetMethod(method, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (methodInfo == null) { return false; } methodInfo.Invoke(target, arguments); return true; } catch (Exception ex) { Plugin.mlg.LogWarning((object)("Interactive Terminal API: " + method + " failed: " + (ex.InnerException ?? ex).Message)); return false; } } private static object Manager() { Find(); try { Object val = (Object)((instance != null) ? /*isinst with value type is only supported in some contexts*/: null); return (val != (Object)null) ? val : null; } catch (Exception) { return null; } } private static void Find() { if (!looked) { looked = true; Type type = Reflect.Find("InteractiveTerminalAPI.UI.InteractiveTerminalManager"); if (type != null) { instance = AccessTools.Field(type, "Instance"); applications = AccessTools.Field(type, "registeredApplications"); application = AccessTools.Field(type, "mainApplication"); } Type type2 = Reflect.Find("InteractiveTerminalAPI.Input.Keybinds"); if (type2 != null) { exitAction = AccessTools.Field(type2, "cursorExitAction"); } } } } internal static class KnownCommands { internal class Group { internal string Plugin; internal string Title; internal string Description; internal string[] Claims; internal MenuEntry[] Entries; internal string[] OnlyIfRegistered; internal Func<string, bool> Owns; internal Func<MenuPage> Page; } private static readonly HashSet<string> Hidden = new HashSet<string> { "reset credits", "melanie", "upgrades", "decor", "simulate", "filter", "preview", "sort" }; private static readonly MenuEntry[] Unexplained = new MenuEntry[2] { new MenuEntry { Label = "LOGS", Command = "sigurd", Description = "Read the log entries the crew has found." }, new MenuEntry { Label = "EJECT", Command = "eject", Description = "Fire the crew out of the airlock and start over." } }; private static readonly MenuEntry[] Singles = new MenuEntry[4] { new MenuEntry { Label = "SHIP INVENTORY", Command = "ship", Description = "Put items away in the ship's inventory and take them out." }, new MenuEntry { Label = "MERGE SCRAP", Command = "merge", Description = "Merge scrap of the same kind into one item." }, new MenuEntry { Label = "REVIVE", Command = "revive", Description = "Bring dead crew members back to life, for credits." }, new MenuEntry { Label = "WEATHER PROBE", Command = "probe", Description = "Change the weather on a moon, for credits." } }; private static readonly KeyValuePair<string, MenuEntry>[] PluginSingles = new KeyValuePair<string, MenuEntry>[3] { new KeyValuePair<string, MenuEntry>("ExtraDaysToDeadline", new MenuEntry { Label = "BUY EXTRA DAY", Command = "buyday", Description = "Ask the Company for one more day to reach the quota, for credits.", Click = delegate { Answers.Run(TheTerminal(), "BUY EXTRA DAY", "buyday", asks: true); } }), new KeyValuePair<string, MenuEntry>("abu.TooMuchScrap", new MenuEntry { Label = "TOO MUCH SCRAP", Command = "merge", Description = "Merge nearby piles of small scrap (bones, body parts) into one, to cut lag. Host only.", Click = delegate { Answers.AskFirst(TheTerminal(), "MERGE SCRAP", "Merge the small scrap lying close together in the ship into fewer pieces?\n\n<alpha=#99>Only the kinds set in the mod's config merge (body parts and bones by default), only the host can, and by default only at the Company.", "MERGE", "merge"); } }), new KeyValuePair<string, MenuEntry>("BLUJAY.WeatherReroll", new MenuEntry { Label = "WEATHER REROLL", Command = "reroll", Description = "Roll the weather of every moon again, for credits. Each reroll that day costs more.", Click = delegate { Answers.Run(TheTerminal(), "WEATHER REROLL", "reroll"); } }) }; private static readonly HashSet<string> SingleAliases = new HashSet<string> { "merge scrap", "mergescrap", "scrapmerge", "weather" }; private static readonly Group[] Groups = new Group[9] { new Group { Plugin = "com.malco.lethalcompany.moreshipupgrades", Title = "LATEGAME UPGRADES", Description = "Upgrade store, contracts and the crew's other tools.", Claims = new string[20] { "lategame", "lgu", "lategame store", "lgc", "contracts", "contract", "contract info", "trade", "trade player credits", "lgu trade", "convert", "pc", "interns", "intern", "atk", "initattack", "cd", "cooldown", "extend deadline", "probe" }, OnlyIfRegistered = new string[6] { "lgu", "contracts", "trade", "convert", "extend deadline", "probe" }, Entries = new MenuEntry[10] { new MenuEntry { Label = "UPGRADE STORE", Command = "lgu", Description = "Buy upgrades for the ship and the crew." }, new MenuEntry { Label = "CONTRACTS", Command = "contracts", Description = "Take on a contract for a reward." }, new MenuEntry { Label = "EXTEND DEADLINE", Command = "extend deadline", Description = "Buy more days before the quota is due." }, new MenuEntry { Label = "WEATHER PROBE", Command = "probe", Description = "Change the weather on a moon." }, new MenuEntry { Label = "INTERNS", Command = "interns", Description = "Replace the dead player on the radar with a new employee." }, new MenuEntry { Label = "TRADE CREDITS", Command = "trade", Description = "Give player credits to someone else." }, new MenuEntry { Label = "CONVERT CREDITS", Command = "convert", Description = "Change company credits into player credits and back." }, new MenuEntry { Label = "STUN ATTACK", Command = "atk", Description = "Stun nearby enemies. Needs the Discombobulator." }, new MenuEntry { Label = "STUN COOLDOWN", Command = "cd", Description = "How long until the stun can be used again." }, new MenuEntry { Label = "ABOUT", Command = "lategame", Description = "What the mod has to say about itself." } } }, new Group { Plugin = "com.github.zehsteam.SellMyScrap", Title = "SELL MY SCRAP", Description = "Sell scrap from the ship without carrying it to the counter.", Claims = new string[17] { "sell", "sell amount", "sellamount", "sell quota", "sellquota", "sell all", "sellall", "sell item", "sellitem", "sell list", "selllist", "view overtime", "viewovertime", "view scrap", "viewscrap", "view all scrap", "viewallscrap" }, Page = SellMyScrapPages.Menu }, new Group { Plugin = "GJ.ScrapBank", Title = "SCRAP BANK", Description = "Put scrap in the bank as money, and sell from it.", Claims = new string[0], Owns = ScrapBankPages.Owns, Page = ScrapBankPages.Menu }, new Group { Plugin = "NoteBoxz.SelectableInteriors", Title = "INTERIORS", Description = "Pick the interior the next moon will have.", Claims = new string[0], Owns = SelectableInteriorsPage.Owns, Page = () => SelectableInteriorsPage.Build(Object.FindObjectOfType<Terminal>()) }, new Group { Plugin = "cucumis.lethalrpg", Title = "LETHAL RPG", Description = "Spend XP on upgrades for your body and your tools.", Claims = new string[1] { "rpg" }, Page = LethalRpgPages.Menu }, new Group { Plugin = "mattymatty.LobbyControl", Title = "LOBBY", Description = "Open or close the lobby to new players, and save or load the game. Host only.", Claims = new string[1] { "lobby" }, Entries = new MenuEntry[13] { new MenuEntry { Label = "STATUS", Command = "lobby status", Description = "Whether the lobby is open, who can see it, and the save file." }, new MenuEntry { Label = "OPEN", Command = "lobby open", Description = "Let new players join. Only in orbit." }, new MenuEntry { Label = "CLOSE", Command = "lobby close", Description = "Keep new players out. Only in orbit." }, new MenuEntry { Label = "PUBLIC", Command = "lobby public", Description = "Anyone can find the lobby." }, new MenuEntry { Label = "FRIENDS ONLY", Command = "lobby friend", Description = "Only Steam friends can find the lobby." }, new MenuEntry { Label = "INVITE ONLY", Command = "lobby private", Description = "Only invited players can join." }, new MenuEntry { Label = "RENAME", Description = "Give the lobby a new name.", Click = delegate { Answers.Typed(TheTerminal(), "RENAME LOBBY", "Type the lobby's new name, then Enter.", "lobby rename ", "RENAME", 40); } }, new MenuEntry { Label = "AUTOSAVE", Command = "lobby autosave", Description = "Switch saving the game on its own on or off." }, new MenuEntry { Label = "SAVE", Description = "Save the game under a name.", Click = delegate { Answers.Typed(TheTerminal(), "SAVE GAME", "Type a name for the save, then Enter.", "lobby save ", "SAVE", 30); } }, new MenuEntry { Label = "LOAD", Description = "Load a saved game into this lobby.", Click = delegate { Answers.Typed(TheTerminal(), "LOAD GAME", "Type the name of the save to load, then Enter. What is not saved is lost.", "lobby load ", "LOAD", 30); } }, new MenuEntry { Label = "SWITCH SAVE", Description = "Change which save file the game writes to, without loading it.", Click = delegate { Answers.Typed(TheTerminal(), "SWITCH SAVE", "Type the name of the save file to use from now on, then Enter.", "lobby switch ", "SWITCH", 30); } }, new MenuEntry { Label = "DROP ALL", Description = "Everyone drops everything they hold.", Click = delegate { Answers.AskFirst(TheTerminal(), "DROP ALL", "Make every player drop everything they hold?", "DROP ALL", "lobby dropall"); } }, new MenuEntry { Label = "CLEAR", Description = "Reset this lobby to an empty game.", Click = delegate { Answers.AskFirst(TheTerminal(), "CLEAR LOBBY", "Reset this lobby to an empty game? Everything not saved is lost.", "CLEAR", "lobby clear"); } } } }, new Group { Plugin = "com.seeya.operator", Title = "CASSETTES", Description = "Play, save and delete what the camcorder recorded.", Claims = new string[14] { "ohelp", "cassettes", "play", "stop", "save", "delete", "slow", "normal", "fast", "turbo", "pause", "resume", "volume", "transfer" }, Entries = new MenuEntry[10] { new MenuEntry { Label = "SAVED CASSETTES", Command = "cassettes", Description = "Every saved cassette, with its ID, value, date and length." }, new MenuEntry { Label = "PLAY", Description = "Play a cassette on the terminal, by its ID.", Click = delegate { Answers.Typed(TheTerminal(), "PLAY", "Type the cassette's ID, then Enter.", "play ", "PLAY", 20); } }, new MenuEntry { Label = "STOP", Command = "stop", Description = "Stop playing." }, new MenuEntry { Label = "PAUSE", Command = "pause", Description = "Pause playing." }, new MenuEntry { Label = "RESUME", Command = "resume", Description = "Go on playing." }, new MenuEntry { Label = "SPEED", Description = "Play slower or faster.", Click = delegate { Terminal terminal = TheTerminal(); Window window = new Window { Title = "SPEED", Text = "How fast the cassette plays." }; string[] array = new string[4] { "slow", "normal", "fast", "turbo" }; foreach (string speed in array) { window.Buttons.Add(new ToolButton { Label = speed.ToUpperInvariant(), Click = delegate { Answers.Run(terminal, "SPEED", speed); } }); } Window.Show(window); } }, new MenuEntry { Label = "VOLUME", Description = "How loud the cassette plays, 0 to 100.", Click = delegate { Answers.Typed(TheTerminal(), "VOLUME", "Type the volume, 0 to 100, then Enter.", "volume ", "SET", 3); } }, new MenuEntry { Label = "SAVE", Description = "Keep a cassette in the library, by its ID.", Click = delegate { Answers.Typed(TheTerminal(), "SAVE CASSETTE", "Type the cassette's ID, then Enter.", "save ", "SAVE", 20); } }, new MenuEntry { Label = "DELETE", Description = "Take a cassette out of the library, by its ID.", Click = delegate { Answers.Typed(TheTerminal(), "DELETE CASSETTE", "Type the ID of the cassette to delete, then Enter.", "delete ", "DELETE", 20); } }, new MenuEntry { Label = "TRANSFER", Command = "transfer", Description = "Move what is playing onto the ship's monitor, where that is possible." } } }, new Group { Plugin = "gavin.shipcomputerassistant", Title = "CREW MONITOR", Description = "Health, place, weight and held item of every crew member.", Claims = new string[5] { "crew", "crew upgrade", "crew monitoring", "buy crew monitor", "status" }, Entries = new MenuEntry[2] { new MenuEntry { Label = "CREW STATUS", Command = "crew", Description = "How every crew member is doing. Needs the software installed." }, new MenuEntry { Label = "BUY THE SOFTWARE", Command = "crew upgrade", Description = "Install Crew Monitoring Software on the ship, for credits." } } }, new Group { Plugin = "thej01.lc.TerminalSeedPicker", Title = "SEED PICKER", Description = "See or set the seed the next moon is built from.", Claims = new string[4] { "seed", "get seed", "set seed", "random seed" }, Entries = new MenuEntry[3] { new MenuEntry { Label = "GET SEED", Command = "get seed", Description = "Show the seed of the moon you are on. Only after landing." }, new MenuEntry { Label = "RANDOM SEED", Command = "random seed", Description = "Set a completely random seed for the next moon. Only in orbit." }, new MenuEntry { Label = "SET SEED", Description = "Type a seed for the next moon, text or numbers. Only in orbit.", Click = delegate { Answers.Typed(Object.FindObjectOfType<Terminal>(), "SET SEED", "Type the seed for the next moon, text or numbers, then Enter.", "set seed ", "SET", 40); } } } } }; private static readonly HashSet<string> VanillaMain = new HashSet<string> { "moons", "store", "bestiary", "storage", "other" }; private static readonly HashSet<string> VanillaOther = new HashSet<string> { "view monitor", "switch", "ping", "flash", "transmit", "scan" }; private static readonly string[] None = new string[0]; private static Terminal TheTerminal() { return Object.FindObjectOfType<Terminal>(); } internal static IEnumerable<MenuEntry> SinglesAmong(IEnumerable<string> registered) { KeyValuePair<string, MenuEntry>[] pluginSingles = PluginSingles; for (int i = 0; i < pluginSingles.Length; i++) { KeyValuePair<string, MenuEntry> keyValuePair = pluginSingles[i]; if (Chainloader.PluginInfos.ContainsKey(keyValuePair.Key)) { yield return keyValuePair.Value; } } MenuEntry[] singles = Singles; foreach (MenuEntry entry in singles) { string key = Commands.Normalize(entry.Command); if (!Present().Any((Group group) => group.Claims.Contains(key))) { string text = registered.FirstOrDefault((string a) => string.Equals(a, entry.Command, StringComparison.OrdinalIgnoreCase)); if (text != null) { yield return new MenuEntry { Label = entry.Label, Description = entry.Description, Command = text }; } } } } internal static bool IsVanillaMain(MenuEntry entry) { return VanillaMain.Contains(Commands.Normalize(entry.Command ?? entry.Prefill ?? "")); } internal static bool IsVanillaOther(MenuEntry entry) { return VanillaOther.Contains(Commands.Normalize(entry.Command ?? entry.Prefill ?? "")); } internal static IEnumerable<string> VanillaCommands() { return VanillaMain.Concat(VanillaOther).Concat(Unexplained.Select((MenuEntry entry) => entry.Command)).Concat(new string[1] { "help" }); } internal static bool IsHidden(string command) { string key = Commands.Normalize(command ?? ""); if (!Hidden.Contains(key) && !SingleAliases.Contains(key)) { return Present().Any((Group group) => group.Claims.Contains(key) || (group.Owns != null && group.Owns(key))); } return true; } internal static IEnumerable<MenuEntry> UnexplainedFor(Terminal terminal) { TerminalKeyword[] keywords = (((Object)(object)terminal.terminalNodes != (Object)null) ? terminal.terminalNodes.allKeywords : null); if (keywords == null) { yield break; } MenuEntry[] unexplained = Unexplained; foreach (MenuEntry entry in unexplained) { if (keywords.Any((TerminalKeyword keyword) => (Object)(object)keyword != (Object)null && keyword.word == entry.Command)) { yield return entry; } } } internal static IEnumerable<MenuEntry> GroupEntries() { foreach (Group item in Present()) { Group shown = item; yield return new MenuEntry { Label = item.Title, Description = item.Description, Open = () => PageOf(shown) }; } } private static MenuPage PageOf(Group group) { if (group.Page != null) { return group.Page(); } MenuPage menuPage = Navigator.MenuLook(Object.FindObjectOfType<Terminal>(), new MenuPage { Title = group.Title }); MenuEntry[] entries = group.Entries; foreach (MenuEntry menuEntry in entries) { if (!(group.OnlyIfRegistered ?? None).Contains(menuEntry.Command)) { menuPage.Entries.Add(menuEntry); continue; } string text = InteractiveTerminal.Registered(menuEntry.Command); if (text != null) { menuPage.Entries.Add(new MenuEntry { Label = menuEntry.Label, Description = menuEntry.Description, Command = text }); } } return Answers.InWindows(Object.FindObjectOfType<Terminal>(), menuPage); } private static IEnumerable<Group> Present() { return Groups.Where((Group group) => Chainloader.PluginInfos.ContainsKey(group.Plugin)); } } internal static class LethalRpgPages { internal const string Guid = "cucumis.lethalrpg"; private static readonly Regex Upgrade = new Regex("^\\*\\s+(.+?)\\s{2,}Lv\\s*(\\d+)\\s*/\\s*(\\d+)", RegexOptions.Compiled); private static readonly Regex Tags = new Regex("<[^>]+>", RegexOptions.Compiled); private static readonly string[] Footer = new string[4] { "Pages:", "Buy:", "More:", "Skill menu:" }; private static readonly (string Label, string Command)[] Lists = new(string, string)[4] { ("BODY", "rpg"), ("TOOLS", "rpg tools"), ("UNIQUE", "rpg unique"), ("ULTIMATE", "rpg ult") }; internal static MenuPage Menu() { Terminal terminal = Object.FindObjectOfType<Terminal>(); MenuPage menuPage = Navigator.MenuLook(terminal, new MenuPage { Title = "LETHAL RPG" }); menuPage.Entries.Add(new MenuEntry { Label = "BODY UPGRADES", Description = "Stamina, strength, jump and the rest of the body.", Click = delegate { List(terminal, "rpg"); } }); menuPage.Entries.Add(new MenuEntry { Label = "TOOL UPGRADES", Description = "Upgrades for the tools you carry.", Click = delegate { List(terminal, "rpg tools"); } }); menuPage.Entries.Add(new MenuEntry { Label = "UNIQUE UPGRADES", Description = "One-of-a-kind upgrades.", Click = delegate { List(terminal, "rpg unique"); } }); menuPage.Entries.Add(new MenuEntry { Label = "ULTIMATES", Description = "The big ones, for the most XP.", Click = delegate { List(terminal, "rpg ult"); } }); menuPage.Entries.Add(new MenuEntry { Label = "MY STATS", Command = "rpg stats", Description = "Your level, XP and what you have bought." }); menuPage.Entries.Add(new MenuEntry { Label = "CREW", Command = "rpg crew", Description = "How the rest of the crew is built." }); menuPage.Entries.Add(new MenuEntry { Label = "BUILD", Command = "rpg build", Description = "Your build, to save and buy back later." }); menuPage.Entries.Add(new MenuEntry { Label = "INFO", Command = "rpg info", Description = "How the mod works." }); menuPage.Entries.Add(new MenuEntry { Label = "REFUND", Description = "Take back every upgrade and get the points to spend again.", Click = delegate { Refund(terminal); } }); return Answers.InWindows(terminal, menuPage); } private static void List(Terminal terminal, string command, string note = null) { string input = Commands.Ask(terminal, command); string[] array = Tags.Replace(input, "").Replace("\r", "").Split('\n'); string text = string.Join("\n", array.Where((string line) => !Footer.Any((string start) => line.TrimStart().StartsWith(start)))); Window window = new Window { Title = "LETHAL RPG", Text = ((note != null) ? ("<alpha=#99>" + note + "<alpha=#FF>\n\n") : "") + Window.Tidy(text, "") }; List<ToolButton> list = window.Group("LISTS"); (string, string)[] lists = Lists; for (int num = 0; num < lists.Length; num++) { (string, string) tuple = lists[num]; string target = tuple.Item2; list.Add(new ToolButton { Label = tuple.Item1, Active = (target == command), Click = delegate { List(terminal, target); } }); } List<ToolButton> buy = window.Group("BUY A LEVEL"); string[] array2 = array; foreach (string text2 in array2) { Match match = Upgrade.Match(text2.Trim()); if (match.Success) { string name = match.Groups[1].Value.Trim(); bool off = match.Groups[2].Value == match.Groups[3].Value; buy.Add(new ToolButton { Label = name, Off = off, Click = delegate { string text3 = Window.Tidy(Commands.Ask(terminal, "rpg buy " + name.ToLowerInvariant()), ""); List(terminal, command, (text3.Length > 0) ? text3 : null); ((MonoBehaviour)terminal).StartCoroutine(ReadAgain(terminal, command)); } }); } } if (buy.Count == 0) { window.Groups.RemoveAll((KeyValuePair<string, List<ToolButton>> group) => group.Value == buy); } Window.Show(window); } private static IEnumerator ReadAgain(Terminal terminal, string command) { yield return (object)new WaitForSecondsRealtime(0.8f); if (Window.Open != null && Window.Open.Title == "LETHAL RPG") { List(terminal, command); } } private static void Refund(Terminal terminal) { Window window = new Window(); window.Title = "REFUND"; window.Text = Window.Tidy(Commands.Ask(terminal, "rpg refund"), ""); window.Buttons.Add(new ToolButton { Label = "REFUND", Click = delegate { Answers.Run(terminal, "REFUND", "rpg refund confirm"); } }); window.Buttons.Add(new ToolButton { Label = "CANCEL", Click = Window.Close }); Window.Show(window); } } internal class MoonData { internal readonly Dictionary<SelectableLevel, HashSet<string>> Tags = new Dictionary<SelectableLevel, HashSet<string>>(); internal readonly HashSet<SelectableLevel> Hidden = new HashSet<SelectableLevel>(); internal readonly HashSet<SelectableLevel> Locked = new HashSet<SelectableLevel>(); internal const string Vanilla = "VANILLA"; internal readonly Dictionary<SelectableLevel, string> Authors = new Dictionary<SelectableLevel, string>(); internal bool LevelLoader; internal const string Rotation = "ROTATION"; internal bool Rotating; internal readonly HashSet<SelectableLevel> Drawn = new HashSet<SelectableLevel>(); private static MoonData last; private static float lastAt = -100f; internal static MoonData Read() { float realtimeSinceStartup = Time.realtimeSinceStartup; if (last != null && realtimeSinceStartup - lastAt < 3f) { return last; } MoonData moonData = new MoonData(); Try(moonData.ReadLevelLoader, "LethalLevelLoader"); Try(moonData.ReadDawnLib, "DawnLib"); Try(moonData.ReadRotation, "TooManyMoons"); last = moonData; lastAt = realtimeSinceStartup; return moonData; } private void ReadLevelLoader() { Type type = Reflect.Find("LethalLevelLoader.PatchedContent"); if (type == null || !(AccessTools.Property(type, "ExtendedLevels")?.GetValue(null) is IEnumerable enumerable)) { return; } LevelLoader = true; Dictionary<object, string> teams = Origins.ModTeams(); bool flag = default(bool); bool flag2 = default(bool); foreach (object item in enumerable) { if (item == null) { continue; } object value = Traverse.Create(item).Property("SelectableLevel", (object[])null).GetValue(); SelectableLevel val = (SelectableLevel)((value is SelectableLevel) ? value : null); if (val == null || (Object)(object)val == (Object)null) { continue; } object value2 = Traverse.Create(item).Property("IsRouteHidden", (object[])null).GetValue(); int num; if (value2 is bool) { flag = (bool)value2; num = 1; } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) != 0) { Hidden.Add(val); } value2 = Traverse.Create(item).Property("IsRouteLocked", (object[])null).GetValue(); int num2; if (value2 is bool) { flag2 = (bool)value2; num2 = 1; } else { num2 = 0; } if (((uint)num2 & (flag2 ? 1u : 0u)) != 0) { Locked.Add(val); } bool flag3 = Traverse.Create(item).Property("ContentType", (object[])null).GetValue()?.ToString() != "Vanilla"; Authors[val] = (flag3 ? (Origins.OfExtended(item, teams) ?? "VANILLA") : "VANILLA"); if (!(Traverse.Create(item).Property("ContentTags", (object[])null).GetValue() is IEnumerable enumerable2)) { continue; } foreach (object item2 in enumerable2) { if (item2 != null) { Add(val, Traverse.Create(item2).Field("contentTagName").GetValue() as string); } } } } private void ReadDawnLib() { Type type = Reflect.Find("Dawn.LethalContent"); object obj = ((!(type != null)) ? null : AccessTools.Field(type, "Moons")?.GetValue(null)); if (obj == null || !(Traverse.Create(obj).Property("Values", (object[])null).GetValue() is IEnumerable enumerable)) { return; } foreach (object item in enumerable) { if (item == null) { continue; } object value = Traverse.Create(item).Property("Level", (object[])null).GetValue(); SelectableLevel val = (SelectableLevel)((value is SelectableLevel) ? value : null); if (val == null || (Object)(object)val == (Object)null) { continue; } if (!Authors.ContainsKey(val)) { string text = Traverse.Create(item).Property("Key", (object[])null).Property("Namespace", (object[])null) .GetValue() as string; Authors[val] = ((text == "lethal_company") ? "VANILLA" : (Origins.Clean(text) ?? "VANILLA")); } if (!(Traverse.Create(item).Method("AllTags", Array.Empty<object>()).GetValue() is IEnumerable enumerable2)) { continue; } foreach (object item2 in enumerable2) { if (item2 != null) { Add(val, Traverse.Create(item2).Property("Key", (object[])null).GetValue() as string); } } } } private void ReadRotation() { Type type = Reflect.Find("TooManyMoons.RotationApi"); SelectableLevel[] array = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.levels : null); if (type == null || array == null) { return; } object obj = AccessTools.Property(type, "Active")?.GetValue(null); if (!(obj is bool) || !(bool)obj) { return; } MethodInfo methodInfo = AccessTools.Method(type, "IsDrawn", (Type[])null, (Type[])null); if (methodInfo == null) { return; } Rotating = true; SelectableLevel[] array2 = array; bool flag = default(bool); foreach (SelectableLevel val in array2) { int num; if ((Object)(object)val != (Object)null) { obj = methodInfo.Invoke(null, new object[1] { val }); if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) != 0) { Drawn.Add(val); } } } private void Add(SelectableLevel level, string tag) { if (!string.IsNullOrWhiteSpace(tag)) { if (!Tags.TryGetValue(level, out var value)) { value = (Tags[level] = new HashSet<string>()); } value.Add(tag.Trim().ToLowerInvariant().Replace('_', ' ')); } } private static void Try(Action read, string mod) { try { read(); } catch (Exception ex) { Plugin.WarnOnce("Could not read the moon data of " + mod + ": " + ex.Message); } } } internal static class Origins { internal const string Vanilla = "VANILLA"; internal const string Unknown = "OTHER"; private static Dictionary<string, string> spellings; internal static string TeamOfPath(string file) { if (string.IsNullOrEmpty(file)) { return null; } try { string text = Path.GetFullPath(Paths.PluginPath).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar; string fullPath = Path.GetFullPath(file); if (!fullPath.StartsWith(text, StringComparison.OrdinalIgnoreCase)) { return null; } string text2 = fullPath.Substring(text.Length).Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)[0]; int num = text2.IndexOf('-'); return (num > 0) ? Spelled(text2.Substring(0, num)) : null; } catch (Exception) { return null; } } internal static string Spelled(string name) { if (string.IsNullOrWhiteSpace(name)) { return null; } if (spellings == null) { spellings = new Dictionary<string, string>(); try { string[] directories = Directory.GetDirectories(Paths.PluginPath); for (int i = 0; i < directories.Length; i++) { string fileName = Path.GetFileName(directories[i]); int num = fileName.IndexOf('-'); if (num > 0) { string text = fileName.Substring(0, num).Replace('_', ' ').Trim() .ToUpperInvariant(); spellings[Squash(text)] = text; } } } catch (Exception) { } } string text2 = name.Replace('_', ' ').Trim().ToUpperInvariant(); string key = Squash(text2); if (!spellings.TryGetValue(key, out var value)) { value = (spellings[key] = text2); } return value; } private static string Squash(string name) { StringBuilder stringBuilder = new StringBuilder(); foreach (char c in name) { if (char.IsLetterOrDigit(c)) { stringBuilder.Append(c); } } return stringBuilder.ToString(); } internal static Dictionary<object, string> ModTeams() { Dictionary<object, string> dictionary = new Dictionary<object, string>(); try { Type type = Reflect.Find("LethalLevelLoader.LethalBundleManager"); if (type == null || !(AccessTools.Field(type, "obtainedExtendedModsDict")?.GetValue(null) is IDictionary dictionary2)) { return dictionary; } foreach (DictionaryEntry item in dictionary2) { if (!(Traverse.Create(item.Key).Field("assetBundleInfos").GetValue() is IEnumerable enumerable) || !(item.Value is IEnumerable enumerable2)) { continue; } string text = null; foreach (object item2 in enumerable) { text = ((item2 != null) ? TeamOfPath(Traverse.Create(item2).Property("AssetBundleFilePath", (object[])null).GetValue() as string) : null); if (text != null) { break; } } if (text == null) { continue; } foreach (object item3 in enumerable2) { if (item3 != null && !dictionary.ContainsKey(item3)) { dictionary[item3] = text; } } } } catch (Exception ex) { Plugin.WarnOnce("Could not read which package each mod came from: " + ex.Message); } return dictionary; } internal static string OfExtended(object extended, Dictionary<object, string> teams) { object value = Traverse.Create(extended).Property("ExtendedMod", (object[])null).GetValue(); if (value != null && teams.TryGetValue(value, out var value2)) { return value2; } return Clean(Traverse.Create(extended).Property("AuthorName", (object[])null).GetValue() as string) ?? Clean(Traverse.Create(extended).Property("ModName", (object[])null).GetValue() as string); } internal static Dictionary<EnemyType, string> EnemyCredits() { Dictionary<EnemyType, string> dictionary = new Dictionary<EnemyType, string>(); try { Type type = Reflect.Find("LethalLevelLoader.PatchedContent"); if (type != null && AccessTools.Property(type, "ExtendedEnemyTypes")?.GetValue(null) is IEnumerable enumerable) { Dictionary<object, string> teams = ModTeams(); foreach (object item in enumerable) { if (item != null) { object value = Traverse.Create(item).Property("EnemyType", (object[])null).GetValue(); EnemyType val = (EnemyType)((value is EnemyType) ? value : null); if (val != null && !((Object)(object)val == (Object)null)) { bool flag = Traverse.Create(item).Property("ContentType", (object[])null).GetValue()?.ToString() != "Vanilla"; dictionary[val] = (flag ? OfExtended(item, teams) : "VANILLA"); } } } } Type type2 = Reflect.Find("Dawn.LethalContent"); object obj = ((!(type2 != null)) ? null : AccessTools.Field(type2, "Enemies")?.GetValue(null)); if (obj != null && Traverse.Create(obj).Property("Values", (object[])null).GetValue() is IEnumerable enumerable2) { foreach (object item2 in enumerable2) { if (item2 != null) { object value2 = Traverse.Create(item2).Property("EnemyType", (object[])null).GetValue(); EnemyType val2 = (EnemyType)((value2 is EnemyType) ? value2 : null); if (val2 != null && !((Object)(object)val2 == (Object)null) && (!dictionary.TryGetValue(val2, out var value3) || value3 == null)) { string text = Traverse.Create(item2).Property("Key", (object[])null).Property("Namespace", (object[])null) .GetValue() as string; dictionary[val2] = ((text == "lethal_company") ? "VANILLA" : Clean(text)); } } } } } catch (Exception ex) { Plugin.WarnOnce("Could not read who made the enemies: " + ex.Message); } return dictionary; } internal static string OfEnemy(EnemyType enemy, Dictionary<EnemyType, string> credits) { if ((Object)(object)enemy == (Object)null) { return "OTHER"; } try { EnemyAI val = (((Object)(object)enemy.enemyPrefab != (Object)null) ? enemy.enemyPrefab.GetComponent<EnemyAI>() : null); Assembly assembly = (((Object)(object)val != (Object)null) ? ((object)val).GetType().Assembly : null); if (assembly != null && assembly != typeof(EnemyAI).Assembly) { string text = TeamOfPath(assembly.Location); if (text != null) { return text; } } } catch (Exception) { } if (!credits.TryGetValue(enemy, out var value) || value == null) { return "VANILLA"; } return value; } internal static string Clean(string name) { if (string.IsNullOrWhiteSpace(name)) { return null; } string text = name.Replace('_', ' ').Trim().ToUpperInvariant(); if (!text.StartsWith("UNKNOWN") && !(text == "UNSPECIFIED")) { return Spelled(text); } return null; } } internal static class Reflect { private static readonly Dictionary<string, Type> Known = new Dictionary<string, Type>(); internal static Type Find(string name) { if (Known.TryGetValue(name, out var value)) { return value; } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { try { value = (assembly.IsDynamic ? null : assembly.GetType(name, throwOnError: false)); } catch (Exception) { value = null; } if (value != null) { break; } } Known[name] = value; return value; } } internal static class ScrapBankPages { internal const string Guid = "GJ.ScrapBank"; private static readonly Regex Tags = new Regex("<[^>]+>", RegexOptions.Compiled); private static readonly Regex Heading = new Regex("^=+\\s*(.*?)\\s*=+$", RegexOptions.Compiled); private static readonly Regex Paged = new Regex("(\\d+)\\s*/\\s*(\\d+)\\s*$", RegexOptions.Compiled); private static readonly Regex Numbered = new Regex("^(\\d+)\\.\\s+(.+?)\\s+(\\$[\\d,.]+)$", RegexOptions.Compiled); private static readonly Regex Choice = new Regex("^(\\d+)\\s*/\\s*([A-Z]+)", RegexOptions.Compiled); private static readonly Regex Leftover = new Regex("(\\s*/\\s*)+(?=\\s*[:=]|\\s*$)", RegexOptions.Compiled); private static readonly Regex Spaces = new Regex("\\s{2,}", RegexOptions.Compiled); private static readonly HashSet<string> Words = new HashSet<string> { "bank", "sb", "scrapbank", "scrap", "ship", "sell", "scrapstore", "scrapsell", "scrapmoney", "banksell" }; private const string ItemTitle = "STORE BY ITEM"; private static readonly PageState ItemState = new PageState(); private static string report; private static bool storing; private static Terminal pending; internal static bool Owns(string key) { string text = key.Split(' ')[0]; if (text != "ship" && text != "sell") { return Words.Contains(text); } return false; } internal static MenuPage Menu() { Terminal terminal = Object.FindObjectOfType<Terminal>(); MenuPage menuPage = Navigator.MenuLook(terminal, new MenuPage { Title = "SCRAP BANK" }); menuPage.Entries.Add(new MenuEntry { Label = "BALANCE", Command = "bank status", Description = "The scrap money in the bank." }); menuPage.Entries.Add(new MenuEntry { Label = "STORE ALL", Command = "scrap all", Description = "Turn all the scrap in the ship into scrap money." }); menuPage.Entries.Add(new MenuEntry { Label = "STORE AMOUNT", Description = "Store scrap worth up to an amount you set.", Click = delegate { Answers.Typed(terminal, "STORE AMOUNT", "Type the value of scrap to store at most, then Enter.", "scrap ", "STORE", 6); } }); menuPage.Entries.Add(new MenuEntry { Label = "STORE BY ITEM", Description = "Tick single items in the ship and store those.", Open = () => ItemPage(terminal) }); menuPage.Entries.Add(new MenuEntry { Label = "SELL QUOTA", Command = "sell quota", Description = "Sell scrap money to meet the quota. Only at the Company." }); menuPage.Entries.Add(new MenuEntry { Label = "SELL ALL", Command = "sell all", Description = "Sell all the scrap money. Only at the Company." }); menuPage.Entries.Add(new MenuEntry { Label = "SELL AMOUNT", Description = "Sell an amount of scrap money you set. Only at the Company.", Click = delegate { TimeOfDay instance = TimeOfDay.Instance; int num = (((Object)(object)instance != (Object)null) ? Math.Max(0, instance.profitQuota - instance.quotaFulfilled) : 0); Answers.Typed(terminal, "SELL AMOUNT", "Type the scrap money to sell, then Enter.", "sell ", "SELL", 6, Answers.Fill(terminal, $"QUOTA NEEDS ${num}", num.ToString())); } }); return Answers.InWindows(terminal, menuPage); } private static MenuPage ItemPage(Terminal terminal) { MenuPage menuPage = new MenuPage { Title = "STORE BY ITEM", SortBy = delegate(int column) { ItemState.SortBy(column); Navigator.Rebuild(terminal); } }; menuPage.Columns.Add(new Column { Header = ItemState.HeaderFor("NAME", 0) }); menuPage.Columns.Add(new Column { Header = ItemState.HeaderFor("VALUE", 1), Chars = 9, AlignRight = true }); List<MenuEntry> list = new List<MenuEntry>(); foreach (GrabbableObject item in from item in Storable() orderby item.itemProperties.itemName, item.scrapValue descending select item) { GrabbableObject picked = item; string itemName = item.itemProperties.itemName; int scrapValue = item.scrapValue; list.Add(new MenuEntry { Label = itemName, Cells = new string[1] { "$" + scrapValue }, Keys = new IComparable[2] { itemName, scrapValue }, Dim = storing, Click = delegate { Store(terminal, picked); } }); } if (report != null && !storing) { Window.Show(new Window { Title = "STORE BY ITEM", Text = report }); report = null; } menuPage.Summary = (storing ? "Storing..." : ((list.Count == 0) ? "No scrap in the ship that can be stored." : "Click an item to store it.")); menuPage.Entries.AddRange(ItemState.Sorted(list)); return menuPage; } private static Type Find(string name) { return ((object)Chainloader.PluginInfos["GJ.ScrapBank"].Instance).GetType().Assembly.GetTypes().FirstOrDefault((Type type) => type.Name == name); } private static List<GrabbableObject> Storable() { MethodInfo eligible = null; try { eligible = Find("ScrapBankManager")?.GetMethod("IsEligibleShipScrap", AccessTools.all); } catch (Exception) { } return Object.FindObjectsOfType<GrabbableObject>().Where(delegate(GrabbableObject item) { if ((Object)(object)item == (Object)null || (Object)(object)item.itemProperties == (Object)null || !item.itemProperties.isScrap || !item.isInShipRoom || (Object)(object)((NetworkBehaviour)item).NetworkObject == (Object)null || !((NetworkBehaviour)item).NetworkObject.IsSpawned) { return false; } try { return (eligible != null) ? ((bool)eligible.Invoke(null, new object[1] { item })) : (!item.isHeld && !item.isPocketed && item.scrapValue > 0); } catch (Exception) { return false; } }).ToList(); } private static void Store(Terminal terminal, GrabbableObject item) { if (storing || (Object)(object)item == (Object)null || (Object)(object)((NetworkBehaviour)item).NetworkObject == (Object)null || !((NetworkBehaviour)item).NetworkObject.IsSpawned) { return; } try { pending = terminal; if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsServer) { Type type = Find("ScrapBankManager"); Type nestedType = type.GetNestedType("StoreResult", AccessTools.all); ParameterExpression parameterExpression = Expression.Parameter(nestedType); Delegate obj = Expression.Lambda(typeof(Action<>).MakeGenericType(nestedType), Expression.Call(typeof(ScrapBankPages).GetMethod("Stored", AccessTools.all), Expression.Convert(parameterExpression, typeof(object))), parameterExpression).Compile(); object[] array = new object[3] { ((NetworkBehaviour)item).NetworkObjectId, obj, null }; storing = true; if (!(bool)type.GetMethod("BeginStoreItem", AccessTools.all).Invoke(null, array)) { storing = false; report = Clean((((string)array[2]) ?? "Could not store it.").Replace('\n', ' ')); } } else { Find("NetworkBridge").GetMethod("RequestStoreItem", AccessTools.all).Invoke(null, new object[1] { ((NetworkBehaviour)item).NetworkObjectId }); storing = true; ((MonoBehaviour)terminal).StartCoroutine(WaitForHost(terminal, item)); } } catch (Exception ex) { storing = false; report = "Could not store: " + (ex.InnerException ?? ex).Message; Plugin.mlg.LogWarning((object)("ScrapBank: " + report)); } Redraw(); } private static void Stored(object result) { storing = false; try { report = Clean((((result.GetType().GetProperty("Message", AccessTools.all)?.GetValue(result) ?? result.GetType().GetField("Message", AccessTools.all)?.GetValue(result)) as string) ?? "Stored.").Replace('\n', ' ')); } catch (Exception) { report = "Stored."; } Redraw(); } private static IEnumerator WaitForHost(Terminal terminal, GrabbableObject item) { string name = item.itemProperties.itemName; float until = Time.unscaledTime + 6f; while ((Object)(object)item != (Object)null && (Object)(object)((NetworkBehaviour)item).NetworkObject != (Object)null && ((NetworkBehaviour)item).NetworkObject.IsSpawned && Time.unscaledTime < until) { yield return null; } storing = false; report = (((Object)(object)item == (Object)null || (Object)(object)((NetworkBehaviour)item).NetworkObject == (Object)null || !((NetworkBehaviour)item).NetworkObject.IsSpawned) ? ("Stored " + name + ".") : ("The host has not stored " + name + ".")); Redraw(); } private static void Redraw() { View current = Navigator.Current; if ((Object)(object)pending != (Object)null && current != null && current.Page != null && current.Page.Title == "STORE BY ITEM") { Navigator.Rebuild(pending); } } internal static bool Claim(Terminal terminal, View view, string printed, string submitted, string back) { if (!Chainloader.PluginInfos.ContainsKey("GJ.ScrapBank")) { return false; } List<string> lines = new List<string>(); string[] array = printed.Split('\n'); int i; for (i = 0; i < array.Length; i++) { string input = array[i]; lines.Add(Clean(Tags.Replace(input, ""))); } string text = lines.Find((string line) => line.Length > 0) ?? ""; string text2 = (submitted ?? "").Trim().ToLowerInvariant().Split(' ')[0].TrimStart('/'); if ((!text.StartsWith("===") && !Words.Contains(text2) && (text2.Length <= 0 || !int.TryParse(text2, out i) || !text.StartsWith("STORE"))) || text.StartsWith("Found ")) { return false; } if (lines.Exists((string line) => Choice.IsMatch(line) && line.Contains("CANCEL"))) { view.Question = true; view.Leave = "cancel"; } view.Builder = () => Page(terminal, lines, back); return true; } private static MenuPage Page(Terminal terminal, List<string> lines, string back) { MenuPage menuPage = new MenuPage { Title = "SCRAP BANK", HideHeads = true }; menuPage.Columns.Add(new Column()); menuPage.Columns.Add(new Column { Chars = 9, AlignRight = true }); bool flag = false; bool flag2 = true; foreach (string line in lines) { Match match = Heading.Match(line); Match match2 = Numbered.Match(line); Match match3 = Choice.Match(line); if (line.Length == 0) { if (!flag2) { menuPage.Entries.Add(new MenuEntry { Label = "", IsHeader = true }); } flag2 = true; } else { if (line.Contains("ENTER") || line.StartsWith("Page:")) { continue; } flag2 = false; if (match.Success) { string text = match.Groups[1].Value.Trim(' ', '/'); Match match4 = Paged.Match(text); if (match4.Success && int.TryParse(match4.Groups[1].Value, out var current2) && int.TryParse(match4.Groups[2].Value, out var last) && last > 1) { menuPage.Tools.Add(new ToolButton { Label = "< PREV", Off = (current2 <= 1), Click = delegate { Commands.Submit(terminal, "scrap list " + Math.Max(1, current2 - 1)); } }); menuPage.Tools.Add(new ToolButton { Label = "NEXT >", Off = (current2 >= last), Click = delegate { Commands.Submit(terminal, "scrap list " + Math.Min(last, current2 + 1)); } }); } menuPage.Entries.Add(new MenuEntry { Label = text, IsHeader = true }); } else if (match2.Success) { string number = match2.Groups[1].Value; menuPage.Entries.Add(new MenuEntry { Label = match2.Groups[2].Value.Trim(), Cells = new string[1] { match2.Groups[3].Value }, Click = delegate { Commands.Submit(terminal, number); } }); } else if (match3.Success) { string number2 = match3.Groups[1].Value; flag = true; menuPage.Entries.Add(new MenuEntry { Label = "[ " + match3.Groups[2].Value + " ]", Click = delegate { Commands.Submit(terminal, number2); } }); } else { menuPage.Entries.Add(new MenuEntry { Label = line }); } } } if (!flag) { ResultPages.AddBack(menuPage, terminal, back); } return menuPage; } private static string Clean(string line) { StringBuilder stringBuilder = new StringBuilder(line.Length); foreach (char c in line) { stringBuilder.Append((c < '\u0080') ? c : ' '); } string input = Leftover.Replace(stringBuilder.ToString(), ""); return Spaces.Replace(input, (Match m) => (m.Value.Length <= 3) ? " " : m.Value).Trim(); } } internal static class ScrollPatchFix { private static bool tried; private static Terminal terminal; internal static void Apply(Harmony harmony, Terminal current) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown terminal = current; if (tried) { return; } tried = true; try { Type type = Reflect.Find("TerminalRelativeScroll.TerminalScrollPatch"); MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, "GetTerminal", (Type[])null, (Type[])null) : null); if (methodInfo != null) { harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(ScrollPatchFix), "HandOver", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.mlg.LogInfo((object)"Terminal Scroll Patch found: its terminal lookup now uses the one we already have."); } } catch (Exception ex) { Plugin.mlg.LogWarning((object)("Could not speed up Terminal Scroll Patch: " + ex.Message)); } } private static bool HandOver(ref Terminal __result) { if ((Object)(object)terminal == (Object)null) { return true; } __result = terminal; return false; } } internal static class SelectableInteriorsPage { internal const string Guid = "NoteBoxz.SelectableInteriors"; internal const string ListCommand = "listinside"; private const string SetCommand = "setinside "; private static readonly Regex Answer = new Regex("^Forcing Interior (.+) \\((\\d+)\\) to be loaded", RegexOptions.Compiled); private static string clicked; private static Type plugin; private static bool complained; internal static bool Owns(string key) { if (!(key == "listinside")) { return key.StartsWith("setinside"); } return true; } internal static MenuPage Build(Terminal terminal) { TerminalKeyword[] array = (((Object)(object)terminal != (Object)null && (Object)(object)terminal.terminalNodes != (Object)null) ? terminal.terminalNodes.allKeywords : null); if (array == null) { return null; } MenuPage menuPage = Navigator.MenuLook(terminal, new MenuPage { Title = "INTERIORS" }); SortedDictionary<int, MenuEntry> sortedDictionary = new SortedDictionary<int, MenuEntry>(); List<MenuEntry> list = new List<MenuEntry>(); string text = "none"; TerminalKeyword[] array2 = array; foreach (TerminalKeyword val in array2) { if ((Object)(object)val == (Object)null || val.word == null || !val.word.StartsWith("setinside ") || (Object)(object)val.specialKeywordResult == (Object)null) { continue; } Match match = Answer.Match(val.specialKeywordResult.displayText ?? ""); if (match.Success && int.TryParse(match.Groups[2].Value, out var id)) { string word = val.word; Func<bool> func = delegate { if (!Readable()) { return clicked == word; } return !FullRandom() && Forced() == id; }; sortedDictionary[id] = Row(terminal, match.Groups[1].Value, word, func); text = (func() ? match.Groups[1].Value : text); } else if (val.word == "setinside none") { string word2 = val.word; list.Insert(0, Row(terminal, "No forced interior", word2, delegate { if (!Readable()) { return clicked == word2; } return !FullRandom() && Forced() < 0; })); } else if (val.word == "setinside fullrandom") { string word3 = val.word; Func<bool> func2 = () => (!Readable()) ? (clicked == word3) : FullRandom(); list.Add(Row(terminal, "Fully random", word3, func2)); text = (func2() ? "fully random" : text); } } menuPage.Summary = "Interior of the next moon: " + text; if (sortedDictionary.Count == 0 && list.Count == 0) { menuPage.Entries.Add(new MenuEntry { Label = "", IsHeader = true }); menuPage.Entries.Add(new MenuEntry { Label = "No interiors to pick from yet." }); return menuPage; } menuPage.Entries.AddRange(list); if (list.Count > 0 && sortedDictionary.Count > 0) { menuPage.Entries.Add(new MenuEntry { Label = "", IsHeader = true }); } menuPage.Entries.AddRange(sortedDictionary.Values); return menuPage; } private static MenuEntry Row(Terminal terminal, string name, string command, Func<bool> picked) { return new MenuEntry { Label = name, LiveLabel = () => (!picked()) ? name : ("> " + name + " (selected)"), Click = delegate { View current = Navigator.Current; clicked = command; Commands.Submit(terminal, command); if (Navigator.Current != current) { Navigator.Back(terminal); } } }; } private static bool Readable() { return Read("LVidForceing") is int; } private static int Forced() { object obj = Read("LVidForceing"); if (obj is int) { return (int)obj; } return -1; } private static bool FullRandom() { object obj = Read("FullRandom"); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } private static object Read(string field) { try { if (plugin == null) { plugin = ((Chainloader.PluginInfos.TryGetValue("NoteBoxz.SelectableInteriors", out var value) && (Object)(object)value.Instance != (Object)null) ? ((object)value.Instance).GetType() : Reflect.Find("SelectableInteriors.SelectableInteriors")); } return plugin?.GetField(field, AccessTools.all)?.GetValue(null); } catch (Exception arg) { if (!complained) { complained = true; Plugin.mlg.LogWarning((object)$"Interiors: cannot read the mod's '{field}': {arg}"); } return null; } } } internal static class SellMyScrapPages { private const string Guid = "com.github.zehsteam.SellMyScrap"; private static readonly Regex Tags = new Regex("<[^>]+>", RegexOptions.Compiled); private static readonly Regex Found = new Regex("^Found (\\d+) items? with a total value of \\$(-?\\d+)", RegexOptions.Compiled); private static readonly Regex Catalogue = new Regex("^Found (\\d+) total items from all moons\\. \\(Page (\\d+) / (\\d+)\\)", RegexOptions.Compiled); private static readonly Regex Quota = new Regex("^Quota fulfilled: (\\$-?\\d+ / \\$-?\\d+)(?: \\(Need: (\\$-?\\d+)\\))?", RegexOptions.Compiled); private static readonly Regex Overtime = new Regex("^Overtime bonus: (\\$-?\\d+)", RegexOptions.Compiled); private static readonly Regex Rate = new Regex("buying at %(\\d+)", RegexOptions.Compiled); private static readonly Regex Asks = new Regex("\\bCONFIRM or DENY\\b", RegexOptions.Compiled); private static readonly Regex Item = new Regex("^(.+?)\\s*\\(x(\\d+)\\)\\s*:?\\s*(\\$-?\\d+)\\s*(\\(\\w+\\))?$", RegexOptions.Compiled); private static readonly Regex Gap = new Regex("\\s{2,}", RegexOptions.Compiled); private static readonly PageState State = new PageState(); private static readonly PageState ItemState = new PageState(); private const string ItemTitle = "SELL ITEM"; private static string report; private static string Spelled(string command, string dashed) { if (!Chainloader.PluginInfos.ContainsKey("GJ.ScrapBank")) { return command; } return dashed; } internal static MenuPage Menu() { Terminal terminal = Object.FindObjectOfType<Terminal>(); MenuPage menuPage = Navigator.MenuLook(terminal, new MenuPage { Title = "SELL MY SCRAP" }); menuPage.Entries.Add(new MenuEntry { Label = "SELL QUOTA", Command = Spelled("sell quota", "sell-quota"), Description = "Sell just enough to meet the quota." }); menuPage.Entries.Add(new MenuEntry { Label = "SELL ALL", Command = Spelled("sell all", "sell-all"), Description = "Sell all the scrap on the ship." }); menuPage.Entries.Add(new MenuEntry { Label = "SELL AMOUNT", Description = "Sell scrap worth an amount you set.", Click = delegate { AmountWindow(terminal); } }); menuPage.Entries.Add(new MenuEntry { Label = "SELL ITEM", Description = "Pick a kind of scrap and sell every one of it.", Open = () => ItemPage(terminal) }); menuPage.Entries.Add(new MenuEntry { Label = "SELL LIST", Command = Spelled("sell list", "sell-list"), Description = "Sell what is on the mod's sell list." }); menuPage.Entries.Add(new MenuEntry { Label = "VIEW SCRAP", Command = "view scrap", Description = "The scrap on the ship and what it is worth." }); menuPage.Entries.Add(new MenuEntry { Label = "VIEW ALL SCRAP", Command = "view all scrap", Description = "Every kind of scrap there is in the game." }); menuPage.Entries.Add(new MenuEntry { Label = "VIEW OVERTIME", Command = "view overtime", Description = "The overtime bonus as it stands." }); menuPage.Entries.Add(new MenuEntry { Label = "ABOUT", Command = Spelled("sell", "sell-help"), Description = "The mod's own list of its commands." }); Answers.InWindows(terminal, menuPage); foreach (MenuEntry entry in menuPage.Entries) { Action click = entry.Click; if (click != null) { entry.Click = delegate { LetGo(); click(); }; } } menuPage.Entries.First((MenuEntry entry) => entry.Label == "VIEW ALL SCRAP").Click = delegate { LetGo(); AllScrap(terminal, "view all scrap"); }; return menuPage; } private static void LetGo() { try { Reflect.Find("com.github.zehsteam.SellMyScrap.Commands.CommandManager")?.GetField("AwaitingConfirmationCommand", AccessTools.all)?.SetValue(null, null); } catch (Exception ex) { Plugin.WarnOnce("SellMyScrap: could not let go of its waiting command: " + ex.Message); } } private static void AllScrap(Terminal terminal, string command) { string text = Commands.Ask(terminal, command); int num = text.IndexOf("The following commands are available", StringComparison.OrdinalIgnoreCase); Window.Show(new Window { Title = "ALL SCRAP", Text = Window.Tidy((num > 0) ? text.Substring(0, num) : text, ""), Closing = LetGo, Buttons = { new ToolButton { Label = "PREV", Click = delegate { AllScrap(terminal, "prev"); } }, new ToolButton { Label = "NEXT", Click = delegate { AllScrap(terminal, "next"); } }, new ToolButton { Label = "CLOSE", Click = Window.Close } } }); } private static void AmountWindow(Terminal terminal) { TimeOfDay instance = TimeOfDay.Instance; int num = (((Object)(object)instance != (Object)null) ? Math.Max(0, instance.profitQuota - instance.quotaFulfilled) : 0); Answers.Typed(terminal, "SELL AMOUNT", "Type the value of scrap to sell, then Enter.", Spelled("sell ", "sell-amount "), "SELL", 6, Answers.Fill(terminal, $"QUOTA NEEDS ${num}", num.ToString())); } private static MenuPage ItemPage(Terminal terminal) { MenuPage menuPage = new MenuPage { Title = "SELL ITEM", SortBy = delegate(int column) { ItemState.SortBy(column); Navigator.Rebuild(terminal); } }; menuPage.Columns.Add(new Column { Header = ItemState.HeaderFor("NAME", 0) }); menuPage.Columns.Add(new Column { Header = ItemState.HeaderFor("COUNT", 1), Chars = 5, AlignRight = true }); menuPage.Columns.Add(new Column { Header = ItemState.HeaderFor("VALUE", 2), Chars = 9, AlignRight = true }); IOrderedEnumerable<IGrouping<string, GrabbableObject>> orderedEnumerable = from item in Object.FindObjectsOfType<GrabbableObject>() where (Object)(object)item != (Object)null && (Object)(object)item.itemProperties != (Object)null && item.itemProperties.isScrap && item.isInShipRoom && !item.isHeld && !item.isPocketed group item by item.itemProperties.itemName into kind orderby kind.Key select kind; List<MenuEntry> list = new List<MenuEntry>(); foreach (IGrouping<string, GrabbableObject> item in orderedEnumerable) { string name = item.Key; int count = item.Count(); int value = item.Sum((GrabbableObject item) => item.scrapValue); list.Add(new MenuEntry { Label = name, Cells = new string[2] { "x" + count, "$" + value }, Keys = new IComparable[3] { name, count, value }, Click = delegate { SellNow(terminal, name, count, value); } }); } menuPage.Summary = report ?? ((list.Count == 0) ? "No scrap in the ship." : "Click a kind to sell all of it."); menuPage.Entries.AddRange(ItemState.Sorted(list)); return menuPage; } private static void SellNow(Terminal terminal, string name, int count, int value) { if (Sell(terminal, name, out var refused)) { report = refused ?? $"Selling {name} x{count} (${value})"; Navigator.Rebuild(terminal); ((MonoBehaviour)terminal).StartCoroutine(Refresh(terminal)); } else { report = null; Navigator.DropCurrent(); Commands.Submit(terminal, Spelled("sell item ", "sell-item ") + name); } } private static IEnumerator Refresh(Terminal terminal) { for (int i = 0; i < 16; i++) { yield return (object)new WaitForSecondsRealtime(0.5f); View current = Navigator.Current; if (current == null || current.Page == null || current.Page.Title != "SELL ITEM") { break; } Navigator.Rebuild(terminal); } } private static bool Sell(Terminal terminal, string name, out string refused) { refused = null; try { Assembly assembly = ((object)Chainloader.PluginInfos["com.github.zehsteam.SellMyScrap"].Instance).GetType().Assembly; BindingFlags all = AccessTools.all; Type type = assembly.GetType("com.github.zehsteam.SellMyScrap.Helpers.ScrapHelper", throwOnError: true); Type type2 = assembly.GetType("com.github.zehsteam.SellMyScrap.Managers.SellManager", throwOnError: true); Type type3 = assembly.GetType("com.github.zehsteam.SellMyScrap.Commands.SellCommand", throwOnError: true); Type type4 = assembly.GetType("com.github.zehsteam.SellMyScrap.Objects.ItemData", throwOnError: true); Type type5 = assembly.GetType("com.github.zehsteam.SellMyScrap.Data.SellType", throwOnError: true); Type type6 = assembly.GetType("com.github.zehsteam.SellMyScrap.Data.ConfirmationStatus", throwOnError: true); object[] array = new object[1]; if (!(bool)type3.GetMethod("CanUseCommand", all).Invoke(null, array)) { object obj = array[0]; TerminalNode val = (TerminalNode)((obj is TerminalNode) ? obj : null); refused = (((Object)(object)val != (Object)null) ? (val.displayText ?? "").Trim() : "Cannot sell here."); return true; } IList obj2 = (IList)type.GetMethod("GetAllScrapByItemNames", all).Invoke(null, new object[4] { new string[1] { name }, false, false, false }); IList list = (IList)Activator.CreateInstance(obj2.GetType()); PropertyInfo property = type4.GetProperty("ItemName", all); foreach (object item in obj2) { if ((string)property.GetValue(item) == name) { list.Add(item); } } if (list.Count == 0) { refused = "Nothing of that kind left to sell."; return true; } object obj3 = type2.GetMethod("SetScrapToSell", all).Invoke(null, new object[1] { list }); int num = (int)obj3.GetType().GetProperty("TotalScrapValue", all).GetValue(obj3); type2.GetMethod("CreateSellRequest", all).Invoke(null, new object[6] { Enum.Parse(type5, "Item"), num, num, Enum.Parse(type6, "AwaitingConfirmation"), -2, -1 }); type2.GetMethod("ConfirmSellRequest", all).Invoke(null, null); return true; } catch (Exception ex) { Plugin.mlg.LogWarning((object)("SellMyScrap: could not sell from the list, using the mod's command instead: " + (ex.InnerException ?? ex).Message)); return false; } } internal static bool Claim(Terminal terminal, View view, string printed, string back) { if (!Chainloader.PluginInfos.ContainsKey("com.github.zehsteam.SellMyScrap")) { return false; } List<string> lines = new List<string>(); string[] array = printed.Split('\n'); foreach (string input in array) { lines.Add(Tags.Replace(input, "").Trim()); } string input2 = lines.Find((string line) => line.Length > 0) ?? ""; if (Catalogue.IsMatch(input2)) { view.Question = true; view.Leave = "exit"; view.Builder = () => AllScrap(terminal, lines); return true; } if (!Found.IsMatch(input2)) { return false; } if (Asks.IsMatch(printed)) { view.Question = true; view.Leave = "deny"; view.Builder = () => Sale(terminal, lines); } else { view.Builder = () => Scrap(terminal, lines, back); } return true; } private static MenuPage Sale(Terminal terminal, List<string> lines) { MenuPage menuPage = new MenuPage { Title = "SELL", HideHeads = true }; menuPage.Columns.Add(new Column()); menuPage.Columns.Add(new Column { Chars = 4, AlignRight = true }); menuPage.Columns.Add(new Column { Chars = 9, AlignRight = true }); List<MenuEntry> list = new List<MenuEntry>(); Space(menuPage); foreach (string line in lines) { Match match = Found.Match(line); Match match2 = Quota.Match(line); Match match3 = Overtime.Match(line); Match match4 = Rate.Match(line); Match match5 = Item.Match(line); if (match.Success) { menuPage.Entries.Add(new MenuEntry { Label = ((match.Groups[1].Value == "1") ? "Sell 1 item" : ("Sell " + match.Groups[1].Value + " items")), Big = true }); Fact(menuPage, "You receive", "$" + match.Groups[2].Value); } else if (match2.Success) { Fact(menuPage, "Quota", match2.Groups[1].Value); if (match2.Groups[2].Success) { Fact(menuPage, "Still needed", match2.Groups[2].Value); } } else if (match3.Success) { Fact(menuPage, "Overtime bonus", match3.Groups[1].Value); } else if (match4.Success) { Fact(menuPage, "The Company is buying at", match4.Groups[1].Value + "%"); } else if (match5.Success) { list.Add(Row(match5)); } } if (list.Count > 0) { Space(menuPage); menuPage.Entries.Add(new MenuEntry { Label = "ITEMS", IsHeader = true }); menuPage.Entries.AddRange(list); } Space(menuPage); menuPage.Entries.Add(new MenuEntry { Label = "[ CONFIRM ]", Command = "confirm" }); menuPage.Entries.Add(new MenuEntry { Label = "[ DENY ]", Click = delegate { Navigator.Back(terminal); } }); return menuPage; } private static MenuPage Scrap(Terminal terminal, List<string> lines, string back) { MenuPage menuPage = new MenuPage { Title = "SCRAP ON THE SHIP", SortBy = delegate(int column) { State.SortBy(column); Navigator.Rebuild(terminal); } }; menuPage.Columns.Add(new Column { Header = State.HeaderFor("NAME", 0) }); menuPage.Columns.Add(new Column { Header = State.HeaderFor("COUNT", 1), Chars = 5, AlignRight = true }); menuPage.Columns.Add(new Column { Header = State.HeaderFor("VALUE", 2), Chars = 9, AlignRight = true }); List<MenuEntry> list = new List<MenuEntry>(); foreach (string line in lines) { Match match = Found.Match(line); Match match2 = Item.Match(line); if (match.Success) { menuPage.Summary = match.Groups[1].Value + " items worth $" + match.Groups[2].Value; } else if (match2.Success) { list.Add(Row(match2)); } } menuPage.Entries.AddRange(State.Sorted(list)); ResultPages.AddBack(menuPage, terminal, back); return menuPage; } private static MenuPage AllScrap(Terminal terminal, List<string> lines) { MenuPage menuPage = new MenuPage { Title = "ALL SCRAP", HideHeads = true }; menuPage.Columns.Add(new Column()); menuPage.Columns.Add(new Column { Chars = 24 }); int result = 1; int result2 = 1; bool flag = false; foreach (string line in lines) { Match match = Catalogue.Match(line); if (match.Success) { int.TryParse(match.Groups[2].Value, out result); int.TryParse(match.Groups[3].Value, out result2); menuPage.Summary = $"{match.Groups[1].Value} kinds of scrap in the game - page {result} / {result2}"; flag = true; } else if (line.StartsWith("The following commands")) { flag = false; } else if (line.StartsWith("Error:")) { menuPage.Summary = line; } else if (flag && line.Length > 0) { string[] array = Gap.Split(line); menuPage.Entries.Add(new MenuEntry { Label = array[0], Cells = new string[1] { (array.Length > 1) ? array[1] : "" } }); } } menuPage.Tools.Add(new ToolButton { Label = "< PREV", Off = (result <= 1), Click = delegate { Commands.Submit(terminal, "prev"); } }); menuPage.Tools.Add(new ToolButton { Label = "NEXT >", Off = (result >= result2), Click = delegate { Commands.Submit(terminal, "next"); } }); menuPage.Tools.Add(new ToolButton { Label = "EXIT", Click = delegate { Navigator.Back(terminal); } }); Space(menuPage); menuPage.Entries.Add(new MenuEntry { Label = "[ GO TO PAGE... ]", Prefill = "page " }); menuPage.Entries.Add(new MenuEntry { Label = "[ EXIT ]", Click = delegate { Navigator.Back(terminal); } }); return menuPage; } private static MenuEntry Row(Match item) { string text = item.Groups[1].Value.Trim(); int num = int.Parse(item.Groups[2].Value); int.TryParse(item.Groups[3].Value.TrimStart('$'), out var result); if (item.Groups[4].Success) { text = text + " " + item.Groups[4].Value; } MenuEntry menuEntry = new MenuEntry(); menuEntry.Label = text; menuEntry.Cells = new string[2] { "x" + num, item.Groups[3].Value }; menuEntry.Keys = new IComparable[3] { text, num, result }; return menuEntry; } private static void Fact(MenuPage page, string label, string value) { page.Entries.Add(new MenuEntry { Label = label, Cells = new string[2] { "", value } }); } private static void Space(MenuPage page) { page.Entries.Add(new MenuEntry { Label = "", IsHeader = true }); } } internal static class StoreRotationCompat { internal const string Guid = "pacoito.StoreRotationConfig"; private static PropertyInfo instance; private static PropertyInfo rotation; private static bool looked; internal static bool Installed => Chainloader.PluginInfos.ContainsKey("pacoito.StoreRotationConfig"); internal static int Discount(Terminal terminal, TerminalNode node) { if ((Object)(object)node == (Object)null || terminal.ShipDecorSelection == null || !Installed) { return 0; } try { Find(); object obj = instance?.GetValue(null); IEnumerable enumerable = ((obj != null) ? (rotation?.GetValue(obj) as IEnumerable) : null); if (enumerable == null) { return 0; } int num = (node.buyUnlockable ? terminal.ShipDecorSelection.FindIndex((TerminalNode other) => (Object)(object)other != (Object)null && other.shipUnlockableID == node.shipUnlockableID) : terminal.ShipDecorSelection.IndexOf(node)); int num2 = 0; foreach (object item in enumerable) { if (num2++ == num) { return (int)item.GetType().GetProperty("UnlockableDiscount", AccessTools.all).GetValue(item); } } } catch (Exception ex) { Plugin.mlg.LogWarning((object)("StoreRotationConfig: could not read its discounts: " + ex.Message)); looked = true; instance = null; } return 0; } internal static int PriceAfter(int price, int discount) { return price - (int)((float)price * ((float)discount / 100f)); } private static void Find() { if (!looked) { looked = true; Type type = Reflect.Find("StoreRotationConfig.Networking.StoreRotationNetworker"); instance = type?.GetProperty("Instance", AccessTools.all); rotation = type?.GetProperty("StoreRotation", AccessTools.all); } } } internal static class TooManyEmotesStore { private const string Header = "[TooManyEmotes]"; private static readonly Regex Tags = new Regex("<[^>]+>", RegexOptions.Compiled); private static readonly Regex Entry = new Regex("^\\s*\\*\\s*(.+?)\\s*//\\s*(.+?)\\s*$", RegexOptions.Compiled); private static readonly Regex Balance = new Regex("^Remaining (.+?) balance:\\s*(.+?)\\.?$", RegexOptions.IgnoreCase | RegexOptions.Compiled); internal static bool IsStore(string printed) { if (printed.Contains("[TooManyEmotes]")) { return printed.Contains("//"); } return false; } internal static MenuPage Build(Terminal terminal, string printed, string back) { MenuPage menuPage = new MenuPage { Title = "EMOTES", Dense = true }; menuPage.Columns.Add(new Column { Header = "NAME" }); menuPage.Columns.Add(new Column { Header = "PRICE", Chars = 13, AlignRight = true }); string[] array = printed.Split('\n'); foreach (string text in array) { string text2 = Tags.Replace(text, "").Trim(); Match match = Entry.Match(text2); Match match2 = Balance.Match(text2); if (match.Success) { string value = match.Groups[1].Value; string value2 = match.Groups[2].Value; bool flag = value2.IndexOf("purchased", StringComparison.OrdinalIgnoreCase) >= 0; Match match3 = Entry.Match(text.Trim()); menuPage.Entries.Add(new MenuEntry { Label = ((match3.Success && !flag) ? match3.Groups[1].Value : value), Command = (flag ? null : ("emote " + value.ToLowerInvariant())), Dim = flag, Cells = new string[1] { flag ? "owned" : value2 } }); } else if (match2.Success) { string text3 = Capital(match2.Groups[1].Value) + ": " + match2.Groups[2].Value; menuPage.Summary = ((menuPage.Summary == null) ? text3 : (menuPage.Summary + " " + text3)); } else if (text2.Length > 0 && !text2.StartsWith("[") && !text2.StartsWith("-") && text2 != "Store" && menuPage.Entries.Count == 0 && menuPage.Summary == null) { menuPage.Summary = text2; } } if (menuPage.Entries.Count == 0 && menuPage.Summary == null) { return null; } menuPage.Entries.Add(new MenuEntry { Label = "", IsHeader = true }); menuPage.Entries.Add(new MenuEntry { Label = ((back != null) ? ("[ BACK TO " + back + " ]") : "[ BACK ]"), Click = delegate { Navigator.Back(terminal); } }); return menuPage; } private static string Capital(string text) { if (text.Length <= 0) { return text; } return char.ToUpperInvariant(text[0]) + text.Substring(1); } } internal static class WeatherForecast { private class Chance { internal string Name; internal float Percent; internal bool Possible; } internal const string Guid = "mrov.WeatherRegistry"; private static readonly Regex Printed = new Regex("^Forecasting weather for (.+)$", RegexOptions.Compiled); private static readonly Regex Tags = new Regex("<[^>]+>", RegexOptions.Compiled); private static readonly Regex Alphanumeric = new Regex("^[0-9]+|[-_/\\\\ ]", RegexOptions.Compiled); internal static bool Available => Chainloader.PluginInfos.ContainsKey("mrov.WeatherRegistry"); internal static bool Claim(Terminal terminal, View view, string printed) { if (!Available) { return false; } string input = (from line in printed.Split('\n') select Tags.Replace(line, "").Trim()).FirstOrDefault((string line) => line.Length > 0) ?? ""; Match match = Printed.Match(input); if (!match.Success) { return false; } string name = match.Groups[1].Value.Trim(); SelectableLevel level = (((Object)(object)StartOfRound.Instance != (Object)null) ? ((IEnumerable<SelectableLevel>)StartOfRound.Instance.levels).FirstOrDefault((Func<SelectableLevel, bool>)((SelectableLevel each) => (Object)(object)each != (Object)null && Alphanumeric.Replace(each.PlanetName, "") == name)) : null); if ((Object)(object)level == (Object)null) { return false; } view.Builder = () => Page(terminal, level); return true; } internal static MenuPage Page(Terminal terminal, SelectableLevel level) { MenuPage menuPage = new MenuPage { Title = "FORECAST", HideHeads = true, Plain = true }; menuPage.Columns.Add(new Column()); menuPage.Columns.Add(new Column { Chars = 8, AlignRight = true }); Space(menuPage); menuPage.Entries.Add(new MenuEntry { Label = MoonsPage.NameOf(level), Big = true }); if (!Read(level, out var now, out var chances)) { menuPage.Entries.Add(new MenuEntry { Label = "WeatherRegistry did not give a forecast." }); ResultPages.AddBack(menuPage, terminal, null); return menuPage; } menuPage.Entries.Add(new MenuEntry { Label = "Weather now", Cells = new string[1] { now } }); Space(menuPage); menuPage.Entries.Add(new MenuEntry { Label = "TOMORROW", IsHeader = true }); foreach (Chance item in chances.Where((Chance each) => each.Possible && each.Percent > 0f)) { menuPage.Entries.Add(new MenuEntry { Label = item.Name, Cells = new string[1] { item.Percent.ToString("0.0") + "%" } }); } List<string> list = (from each in chances where !each.Possible select each.Name).ToList(); if (list.Count > 0) { Space(menuPage); menuPage.Entries.Add(new MenuEntry { Label = "Never here: " + string.Join(", ", list), Dim = true }); } ResultPages.AddBack(menuPage, terminal, null); return menuPage; } internal static void ShowWindow(SelectableLevel level) { Window window = new Window { Title = "FORECAST: " + MoonsPage.NameOf(level).ToUpperInvariant() }; if (!Read(level, out var now, out var chances)) { window.Text = "WeatherRegistry did not give a forecast."; Window.Show(window); return; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("<alpha=#99>WEATHER NOW<alpha=#FF><pos=55%>").Append(Named(now)).Append("\n\n"); stringBuilder.Append("<alpha=#99>TOMORROW<alpha=#FF>\n"); foreach (Chance item in chances.Where((Chance each) => each.Possible && each.Percent > 0f)) { stringBuilder.Append(Named(item.Name)).Append("<pos=55%><mspace=0.6em>").Append(item.Percent.ToString("0.0").PadLeft(5)) .Append("</mspace>%\n"); } List<string> list = (from each in chances where !each.Possible select Named(each.Name)).ToList(); if (list.Count > 0) { stringBuilder.Append("\n<alpha=#99>NEVER HERE<alpha=#BB>\n").Append(string.Join(", ", list)); } window.Text = stringBuilder.ToString(); Window.Show(window); static string Named(string weather) { if (!(weather == "None")) { return weather; } return "Clear"; } } private static bool Read(SelectableLevel level, out string now, out List<Chance> chances) { now = null; chances = new List<Chance>(); try { BindingFlags all = AccessTools.all; Type type = Reflect.Find("WeatherRegistry.WeatherManager"); Type type2 = Reflect.Find("WeatherRegistry.Managers.WeightsManager"); Type type3 = Reflect.Find("WeatherRegistry.Enums.WeatherWeightType"); if (type == null || type2 == null || type3 == null) { return false; } object obj = type.GetMethod("GetCurrentWeather", all, null, new Type[1] { typeof(SelectableLevel) }, null)?.Invoke(null, new object[1] { level }); now = (obj?.GetType().GetProperty("Name", all)?.GetValue(obj) as string) ?? ((object)Unsafe.As<LevelWeatherType, LevelWeatherType>(ref level.currentWeather)/*cast due to .constrained prefix*/).ToString(); IList list = type2.GetMethod("GetPlanetPossibleWeathers", all)?.Invoke(null, new object[1] { level }) as IList; MethodInfo method = type2.GetMethod("GetWeatherWeightsAllTypes", all); IEnumerable enumerable = type.GetMethod("GetWeathers", all)?.Invoke(null, null) as IEnumerable; if (method == null || enumerable == null) { return false; } object key = Enum.ToObject(type3, 0); object key2 = Enum.ToObject(type3, 1); object key3 = Enum.ToObject(type3, 2); List<(object, int)> list2 = new List<(object, int)>(); int num = 0; foreach (object item3 in enumerable) { if (method.Invoke(null, new object[2] { level, item3 }) is IDictionary dictionary) { int num2 = (dictionary.Contains(key) ? ((int)dictionary[key]) : 0); int num3 = (dictionary.Contains(key2) ? ((int)dictionary[key2]) : 0); int num4 = (dictionary.Contains(key3) ? ((int)dictionary[key3]) : 0); num += num2 + num3 + num4; list2.Add((item3, (num4 != 0) ? num4 : ((num3 != 0) ? num3 : num2))); } } if (num <= 0) { return false; } foreach (var item4 in list2.OrderByDescending<(object, int), int>(((object Weather, int Weight) each) => each.Weight)) { object item = item4.Item1; int item2 = item4.Item2; Type type4 = item.GetType(); object obj2 = type4.GetProperty("VanillaWeatherType", all)?.GetValue(item); chances.Add(new Chance { Name = ((type4.GetProperty("Name", all)?.GetValue(item) as string) ?? "?"), Percent = (float)item2 * 100f / (float)num, Possible = (list == null || obj2 == null || list.Contains(obj2)) }); } return true; } catch (Exception ex) { Plugin.WarnOnce("WeatherRegistry forecast could not be read: " + (ex.InnerException ?? ex).Message); return false; } } private static void Space(MenuPage page) { page.Entries.Add(new MenuEntry { Label = "", IsHeader = true }); } } internal enum MonsterDetail { Off, Count, Kinds, Places } internal enum ExtrasPlace { Off, OwnPage, OtherPage, StatusPanel } internal enum ShowHidden { Never, AfterOrbit, Mystery, Always } internal static class Settings { internal static ConfigEntry<bool> OwnPages; internal static ConfigEntry<bool> AppsInWindow; internal static ConfigEntry<bool> SkipWelcome; internal static ConfigEntry<bool> SharpTerminal; internal static ConfigEntry<bool> ZoomToScreen; internal static ConfigEntry<float> CursorSpeed; internal static ConfigEntry<bool> LineScroll; internal static ConfigEntry<int> ScrollLines; internal static ConfigEntry<ExtrasPlace> Extras; internal static ConfigEntry<MonsterDetail> Monsters; internal static ConfigEntry<bool> Re