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 SimpleHQ v0.4.4
SimpleHQ.dll
Decompiled 12 hours 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.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using SimpleHQ.Core; using SimpleHQ.Features; using TMPro; using Unity.Collections; using Unity.Netcode; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("AmazingAssets.TerrainToMesh")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: IgnoresAccessChecksTo("ClientNetworkTransform")] [assembly: IgnoresAccessChecksTo("com.olegknyazev.softmask")] [assembly: IgnoresAccessChecksTo("DissonanceVoip")] [assembly: IgnoresAccessChecksTo("DunGen")] [assembly: IgnoresAccessChecksTo("DunGen.Integration.ASPP")] [assembly: IgnoresAccessChecksTo("DunGen.Integration.UnityNav")] [assembly: IgnoresAccessChecksTo("EasyTextEffects")] [assembly: IgnoresAccessChecksTo("Facepunch Transport for Netcode for GameObjects")] [assembly: IgnoresAccessChecksTo("Facepunch.Steamworks.Win64")] [assembly: IgnoresAccessChecksTo("Unity.AI.Navigation")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging.DocCodeExamples")] [assembly: IgnoresAccessChecksTo("Unity.Burst")] [assembly: IgnoresAccessChecksTo("Unity.Burst.Unsafe")] [assembly: IgnoresAccessChecksTo("Unity.Collections")] [assembly: IgnoresAccessChecksTo("Unity.Collections.LowLevel.ILSupport")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem.ForUI")] [assembly: IgnoresAccessChecksTo("Unity.Jobs")] [assembly: IgnoresAccessChecksTo("Unity.Mathematics")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.Common")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.MetricTypes")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStats")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Component")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsReporting")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkProfiler.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkSolutionInterface")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Components")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Networking.Transport")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Csg")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.KdTree")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Poly2Tri")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Stl")] [assembly: IgnoresAccessChecksTo("Unity.Profiling.Core")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.ShaderLibrary")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Config.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary")] [assembly: IgnoresAccessChecksTo("Unity.Services.Authentication")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Analytics")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Components")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Device")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Networking")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Registration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Scheduler")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Telemetry")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Threading")] [assembly: IgnoresAccessChecksTo("Unity.Services.QoS")] [assembly: IgnoresAccessChecksTo("Unity.Services.Relay")] [assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")] [assembly: IgnoresAccessChecksTo("Unity.Timeline")] [assembly: IgnoresAccessChecksTo("Unity.VisualEffectGraph.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.XR.CoreUtils")] [assembly: IgnoresAccessChecksTo("Unity.XR.Management")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.ConformanceAutomation")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.MetaQuestSupport")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.MockRuntime")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.OculusQuestSupport")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.RuntimeDebugger")] [assembly: IgnoresAccessChecksTo("UnityEngine.ARModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.NVIDIAModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.SpatialTracking")] [assembly: IgnoresAccessChecksTo("UnityEngine.UI")] [assembly: IgnoresAccessChecksTo("UnityEngine.XR.LegacyInputHelpers")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] 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 SimpleHQ { public static class BankInfo { public static int Total => BankStore.Total; public static int Count => BankStore.Items.Count; } internal static class ChatUtil { internal static bool BroadcastReplies; internal static void Send(string message, bool broadcast = false) { try { if (BroadcastReplies) { broadcast = true; } HUDManager instance = HUDManager.Instance; if (!((Object)(object)instance == (Object)null)) { string text = "<color=#ff6a33>HQ</color>: <color=#e8e8e8>" + message + "</color>"; if (broadcast) { instance.AddTextMessageServerRpc(text); } else { instance.AddChatMessage(text, "", -1, false); } } } catch (Exception arg) { Plugin.Logger.LogError((object)$"[ChatUtil.Send] {arg}"); } } internal static void SendChunked(string message, bool broadcast = false, int maxLen = 120) { if (string.IsNullOrEmpty(message)) { return; } while (message.Length > maxLen) { int num = message.LastIndexOf(',', maxLen); if (num < maxLen / 2) { num = message.LastIndexOf(' ', maxLen); } if (num < maxLen / 2) { num = maxLen; } Send(message.Substring(0, num).TrimEnd(), broadcast); message = message.Substring(num).TrimStart(' ', ','); } if (message.Length > 0) { Send(message, broadcast); } } } internal static class CommandRouter { internal static bool IsOurs(string raw) { string[] array = Split(raw); if (array.Length == 0) { return false; } switch (array[0].ToLowerInvariant()) { case "bank": case "tournament": case "agree": return true; case "sell": case "s": if (array.Length >= 2) { return array[1].ToLowerInvariant() == "bank"; } break; } return false; } internal static void Route(string raw, string who, PlayerControllerB requester = null) { string[] array = Split(raw); string text = array[0].ToLowerInvariant(); if (text == "tournament" || text == "agree") { RouteTournament(array, text, who, requester); return; } if (TournamentInfo.Active) { ChatUtil.Send("Tournament mode: the bank is off until the run ends."); return; } if ((!(text == "bank") || array.Length != 1) && (Object)(object)requester != (Object)null && !WhitelistBridge.IsWhitelisted(requester.playerSteamId)) { ChatUtil.Send("Only the host and whitelisted players can use bank commands" + (string.IsNullOrEmpty(who) ? "" : (" (" + who + ")")) + ". /bank shows the total."); return; } if (text != "bank") { BankService.SellFromBank(Join(array, 2), who); return; } switch ((array.Length >= 2) ? array[1].ToLowerInvariant() : "") { case "": BankService.Status(); break; case "d": case "deposit": BankService.Deposit(Join(array, 2), who); break; case "auto": BankService.SetAuto(Join(array, 2), who); break; case "info": BankService.Info(); break; case "w": case "withdraw": BankService.Withdraw(Join(array, 2), who, requester); break; case "l": case "list": BankService.List(); break; case "sell": ChatUtil.Send("Use /sell bank <amount> | quota | all at the Company."); break; default: ChatUtil.Send("/bank, /bank deposit <amount|all|item [count]>, /bank withdraw <item> [count|all], /bank auto on|off, /bank list, /bank info, /sell bank <amount|quota|all>"); break; } } private static void RouteTournament(string[] parts, string verb, string who, PlayerControllerB requester) { if (verb == "agree") { TournamentService.Agree(who, requester); return; } string text = ((parts.Length >= 2) ? parts[1].ToLowerInvariant() : ""); bool flag = (Object)(object)requester == (Object)null; switch (text) { case "status": case "": TournamentService.Status(); break; case "start": case "on": if (!flag) { ChatUtil.Send("Only the host can start tournament mode."); } else { TournamentService.Start(); } break; case "load": if (!flag) { ChatUtil.Send("Only the host can load a tournament kit."); } else { TournamentService.Load((parts.Length >= 3) ? parts[2] : ""); } break; case "stop": case "end": if (!flag) { ChatUtil.Send("Only the host can end tournament mode."); } else { TournamentService.End(); } break; default: ChatUtil.Send("/tournament (status), /tournament start (host), /agree, /tournament load <code> (host), /tournament end (host)"); break; } } internal static bool IsSenderTheHost(int playerId) { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsHost) { return false; } if (playerId < 0) { return true; } PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)val != (Object)null) { if (playerId != (int)val.playerClientId) { return playerId == (int)val.actualClientId; } return true; } return false; } private static string[] Split(string raw) { return (raw ?? "").Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); } private static string Join(string[] parts, int from) { if (parts.Length <= from) { return ""; } return string.Join(" ", parts, from, parts.Length - from); } } internal static class ModConfig { internal static ConfigEntry<string> CommandPrefix { get; private set; } internal static ConfigEntry<string> AltCommandPrefix { get; private set; } internal static ConfigEntry<string> KeepOnShip { get; private set; } internal static ConfigEntry<bool> IncludeCruiser { get; private set; } internal static ConfigEntry<bool> ShowBankOnTerminal { get; private set; } internal static ConfigEntry<bool> PrivateLobbiesOnly { get; private set; } internal static ConfigEntry<bool> AutoDeposit { get; private set; } internal static void Init(ConfigFile file) { CommandPrefix = file.Bind<string>("General", "CommandPrefix", "/", "Prefix for chat commands (e.g. /bank)."); AltCommandPrefix = file.Bind<string>("General", "AltCommandPrefix", "!", "Second prefix that always works, e.g. !bank. Use it if /bank says 'unknown command': another chat mod is taking every '/' message first. Leave empty to disable."); KeepOnShip = file.Bind<string>("Bank", "KeepOnShip", "Shotgun, Kitchen knife", "Scrap that /bank deposit never takes, comma separated item names. Weapons you still want to use."); IncludeCruiser = file.Bind<bool>("Bank", "IncludeCruiser", true, "Also deposit scrap sitting in the Company Cruiser, like /sell does."); AutoDeposit = file.Bind<bool>("Bank", "AutoDeposit", false, "Deposit all scrap on the ship into the bank at the end of each day, just before the game saves in orbit. Same items as /bank deposit all. Toggle in-game: /bank auto on|off."); ShowBankOnTerminal = file.Bind<bool>("Bank", "ShowBankOnTerminal", true, "Show the bank's value beside the credits at the top right of the terminal."); PrivateLobbiesOnly = file.Bind<bool>("Bank", "PrivateLobbiesOnly", true, "The bank only works while the lobby is not public. Banked scrap is kept, just locked, while public."); } internal static bool IsKeptOnShip(string itemName) { if (string.IsNullOrEmpty(itemName)) { return false; } IEnumerable<string> source = from s in (KeepOnShip.Value ?? "").Split(',') select s.Trim() into s where s.Length > 0 select s; return source.Any((string n) => string.Equals(n, itemName, StringComparison.OrdinalIgnoreCase)); } } [BepInPlugin("local.simplehq", "SimpleHQ", "0.4.4")] public class Plugin : BaseUnityPlugin { private readonly Harmony _harmony = new Harmony("local.simplehq"); public static Plugin Instance { get; private set; } internal static ManualLogSource Logger { get; private set; } private void Awake() { Instance = this; Logger = ((BaseUnityPlugin)this).Logger; ModConfig.Init(((BaseUnityPlugin)this).Config); _harmony.PatchAll(); Logger.LogInfo((object)"SimpleHQ v0.4.4 loaded."); } } public static class PluginInfo { public const string Guid = "local.simplehq"; public const string Name = "SimpleHQ"; public const string Version = "0.4.4"; } public static class TournamentInfo { public static bool Active { get; internal set; } public static string KitName { get; internal set; } } } namespace SimpleHQ.Patches { [HarmonyPatch(typeof(StartOfRound), "Start")] internal static class SessionStartPatch { [HarmonyPostfix] private static void Postfix() { try { BankNet.Reset(); BankNet.Register(); TournamentService.Reset(); TournamentNet.Register(); ItemPlaceNet.Register(); NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton != (Object)null && singleton.IsHost) { BankStore.LoadForCurrentSave(); BankNet.Publish(); } } catch (Exception arg) { Plugin.Logger.LogError((object)$"[SessionStartPatch] {arg}"); } } } [HarmonyPatch(typeof(StartOfRound), "OnPlayerConnectedClientRpc")] internal static class PlayerJoinedPatch { [HarmonyPostfix] private static void Postfix() { try { BankNet.Publish(); TournamentService.OnPlayersChanged(); } catch (Exception arg) { Plugin.Logger.LogError((object)$"[PlayerJoinedPatch] {arg}"); } } } [HarmonyPatch(typeof(GameNetworkManager), "SaveGameValues")] internal static class SaveCommitPatch { [HarmonyPostfix] private static void Postfix(GameNetworkManager __instance) { try { StartOfRound instance = StartOfRound.Instance; if (__instance.isHostingGame && !((Object)(object)instance == (Object)null) && !instance.isChallengeFile && instance.inShipPhase) { BankStore.Commit(); } } catch (Exception arg) { Plugin.Logger.LogError((object)$"[SaveCommitPatch] {arg}"); } } } [HarmonyPatch(typeof(StartOfRound), "SetShipReadyToLand")] internal static class AutoDepositPatch { [HarmonyPrefix] private static void Prefix() { BankService.AutoDepositEndOfDay(); } [HarmonyPostfix] private static void Postfix() { TournamentRun.OnShipInOrbit(); } } [HarmonyPatch(typeof(RoundManager), "DespawnPropsAtEndOfRound")] internal static class WipePatch { [HarmonyPrefix] private static void Prefix(bool despawnAllItems) { TournamentRun.NoteWipe(); BankService.OnPropsDespawning(despawnAllItems); } } [HarmonyPatch(typeof(GameNetworkManager), "ResetSavedGameValues")] internal static class FiredPatch { [HarmonyPostfix] private static void Postfix() { TournamentRun.OnFired(); BankService.OnFired(); } } internal static class ChatPrefix { internal static string Strip(string chatMessage) { if (string.IsNullOrEmpty(chatMessage)) { return null; } string value = ModConfig.CommandPrefix.Value; string value2 = ModConfig.AltCommandPrefix.Value; if (!string.IsNullOrEmpty(value) && chatMessage.StartsWith(value)) { return chatMessage.Substring(value.Length).Trim(); } if (!string.IsNullOrEmpty(value2) && chatMessage.StartsWith(value2)) { return chatMessage.Substring(value2.Length).Trim(); } return null; } } [HarmonyPatch(typeof(HUDManager), "AddTextToChatOnServer")] internal static class HostChatPatch { [HarmonyPrefix] [HarmonyPriority(800)] private static bool Prefix(string chatMessage, int playerId) { try { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsHost) { return true; } string text = ChatPrefix.Strip(chatMessage); if (text == null || !CommandRouter.IsOurs(text)) { return true; } if (!CommandRouter.IsSenderTheHost(playerId)) { return true; } CommandRouter.Route(text, null); return false; } catch (Exception arg) { Plugin.Logger.LogError((object)$"[HostChatPatch] {arg}"); return true; } } } [HarmonyPatch(typeof(HUDManager), "AddPlayerChatMessageServerRpc")] internal static class RemoteChatPatch { [HarmonyPrefix] [HarmonyPriority(800)] private static bool Prefix(string chatMessage, int playerId) { try { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsHost) { return true; } string text = ChatPrefix.Strip(chatMessage); if (text == null || !CommandRouter.IsOurs(text)) { return true; } if (CommandRouter.IsSenderTheHost(playerId)) { return true; } StartOfRound instance = StartOfRound.Instance; if (instance?.allPlayerScripts == null || playerId < 0 || playerId >= instance.allPlayerScripts.Length) { return true; } PlayerControllerB val = instance.allPlayerScripts[playerId]; if ((Object)(object)val == (Object)null || !val.isPlayerControlled) { return true; } ChatUtil.BroadcastReplies = true; try { CommandRouter.Route(text, val.playerUsername, val); } finally { ChatUtil.BroadcastReplies = false; } return false; } catch (Exception arg) { Plugin.Logger.LogError((object)$"[RemoteChatPatch] {arg}"); return true; } } } [HarmonyPatch(typeof(PlayerControllerB))] internal static class GrabBeforeStartPatch { private static bool Hide() { try { if (!TournamentNet.ActiveHere) { return false; } GameNetworkManager instance = GameNetworkManager.Instance; if ((Object)(object)instance == (Object)null || instance.gameHasStarted) { return false; } instance.gameHasStarted = true; return true; } catch (Exception arg) { Plugin.Logger.LogError((object)$"[GrabBeforeStartPatch.Hide] {arg}"); return false; } } private static void Restore(bool hidden) { if (hidden && !((Object)(object)GameNetworkManager.Instance == (Object)null)) { GameNetworkManager.Instance.gameHasStarted = false; } } [HarmonyPatch("BeginGrabObject")] [HarmonyPrefix] [HarmonyPriority(800)] private static void GrabPrefix(out bool __state) { __state = Hide(); } [HarmonyPatch("BeginGrabObject")] [HarmonyFinalizer] private static void GrabFinalizer(bool __state) { Restore(__state); } [HarmonyPatch("SetHoverTipAndCurrentInteractTrigger")] [HarmonyPrefix] [HarmonyPriority(800)] private static void TipPrefix(out bool __state) { __state = Hide(); } [HarmonyPatch("SetHoverTipAndCurrentInteractTrigger")] [HarmonyFinalizer] private static void TipFinalizer(bool __state) { Restore(__state); } } [HarmonyPatch(typeof(VehicleController), "Start")] internal static class MagnetStatePatch { [HarmonyPostfix] private static void Postfix(VehicleController __instance) { try { StartOfRound instance = StartOfRound.Instance; if (!((Object)(object)instance == (Object)null) && TournamentNet.ActiveHere && instance.inShipPhase && __instance.magnetedToShip) { instance.isObjectAttachedToMagnet = true; instance.attachedVehicle = __instance; instance.magnetOn = true; if ((Object)(object)instance.magnetLever != (Object)null) { instance.magnetLever.initialBoolState = true; instance.magnetLever.setInitialState = true; instance.magnetLever.SetInitialState(); } Plugin.Logger.LogInfo((object)"[Tournament] kit cruiser on the magnet; magnet switched on."); } } catch (Exception arg) { Plugin.Logger.LogError((object)$"[MagnetStatePatch] {arg}"); } } } [HarmonyPatch(typeof(Terminal), "Update")] internal static class TerminalCreditsPatch { [HarmonyPostfix] private static void Postfix(Terminal __instance) { try { if (ModConfig.ShowBankOnTerminal.Value && __instance.terminalInUse && !((Object)(object)__instance.topRightText == (Object)null) && BankNet.Received && BankNet.Count != 0) { if (((TMP_Text)__instance.topRightText).enableWordWrapping) { ((TMP_Text)__instance.topRightText).enableWordWrapping = false; } ((TMP_Text)__instance.topRightText).text = $"${__instance.groupCredits} <size=55%><color=#ff6a33>${BankNet.Total}</color></size>"; } } catch (Exception arg) { Plugin.Logger.LogError((object)$"[TerminalCreditsPatch] {arg}"); } } } [HarmonyPatch(typeof(Terminal), "ParsePlayerSentence")] internal static class TerminalBankPatch { private static TerminalNode _node; private static bool _awaitingCode; private static string _codeSoFar = ""; private static int _savedCharacterLimit = -1; private static bool BuilderActive { get { if (KitBuilder.Current != KitBuilder.Step.Off) { return KitBuilder.Current != KitBuilder.Step.Done; } return false; } } [HarmonyPrefix] private static bool Prefix(Terminal __instance, ref TerminalNode __result) { try { string text = __instance.screenText.text; int num = Mathf.Clamp(__instance.textAdded, 0, text.Length); string text2 = text.Substring(text.Length - num).Trim(); string text3 = __instance.RemovePunctuation(text2).Trim().ToLowerInvariant(); switch (text3) { case "tournament builder": case "tournament build": case "builder": if (TournamentNet.ActiveHere) { return ShowPage(__instance, "\n\n\nTOURNAMENT BUILDER\n\nNot during a run: build codes between tournaments.\n\n", ref __result); } SetTypingRoom(__instance, roomy: true); return ShowPage(__instance, "\n\n\n" + KitBuilder.Start() + "\n\n", ref __result); default: { if (BuilderActive) { if (text3 == "cancel") { KitBuilder.Cancel(); SetTypingRoom(__instance, roomy: false); return ShowPage(__instance, "\n\n\nTOURNAMENT BUILDER\n\nCancelled.\n\n", ref __result); } string text4 = KitBuilder.Answer(text2.Trim()); if (!BuilderActive) { SetTypingRoom(__instance, roomy: false); } return ShowPage(__instance, "\n\n\n" + text4 + "\n\n", ref __result); } if (_awaitingCode) { string text5; if (text3 == "cancel") { _awaitingCode = false; _codeSoFar = ""; SetTypingRoom(__instance, roomy: false); text5 = "\n\n\nTOURNAMENT\n\nCancelled.\n\n"; } else if (text3.Length == 0) { text5 = "\n\n\nTOURNAMENT\n\nType the code (or the rest of it), or cancel.\n\n"; } else { _codeSoFar += text2.Trim(); if (TournamentService.CanDecode(_codeSoFar)) { _awaitingCode = false; SetTypingRoom(__instance, roomy: false); string codeSoFar = _codeSoFar; _codeSoFar = ""; Kit activeKit = TournamentService.ActiveKit; TournamentService.Load(codeSoFar); bool flag = TournamentService.ActiveKit != null && TournamentService.ActiveKit != activeKit; text5 = "\n\n\nTOURNAMENT\n\n" + (flag ? ("Kit loaded: " + TournamentService.ActiveKit.name + "\nRouting to " + TournamentService.ActiveKit.days[0].moon + ". Pull the lever when ready.\n\n") : "That kit can't be used here. Chat says why.\nType tournament to try again.\n\n"); } else if (_codeSoFar.Length > 300) { _awaitingCode = false; SetTypingRoom(__instance, roomy: false); _codeSoFar = ""; text5 = "\n\n\nTOURNAMENT\n\nThat code doesn't check out.\nType tournament to start again.\n\n"; } else { text5 = $"\n\n\nTOURNAMENT\n\nCode so far: {Strip(_codeSoFar).Length} characters.\n" + "Type the rest and press Enter (cancel to stop).\n\n"; } } return ShowPage(__instance, text5, ref __result); } bool flag2 = text3 == "bank" || text3.StartsWith("bank "); bool flag3 = text3 == "sell" || text3.StartsWith("sell "); bool flag4 = text3 == "tournament" || text3.StartsWith("tournament ") || text3 == "agree"; if (!flag2 && !flag3 && !flag4) { return true; } if (!flag4 && TournamentNet.ActiveHere) { return true; } string displayText; if (text3 == "tournament" && (Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsHost) { if (TournamentInfo.Active) { _awaitingCode = true; _codeSoFar = ""; SetTypingRoom(__instance, roomy: true); displayText = "\n\n\nTOURNAMENT\n\n" + TournamentService.TerminalStatus() + "\n\nType the organisers' kit code and press Enter.\nDashes and case don't matter.\n(Type cancel to back out.)\n\n"; } else if (TournamentService.Proposed) { displayText = "\n\n\nTOURNAMENT\n\n" + TournamentService.TerminalStatus() + "\n\n"; } else { TournamentService.Start(); displayText = "\n\n\nTOURNAMENT\n\n" + TournamentService.TerminalStatus() + "\n\nEveryone types /agree in chat. Then the ship\nwaits at the Company: type tournament again\nhere to enter the code.\n\n(No code yet? Type tournament builder\nto make one.)\n\n"; } } else if (flag4) { string[] array = text2.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); string command = ((array.Length >= 3 && array[1].ToLowerInvariant() == "load") ? ("tournament load " + array[2]) : text3); displayText = (SendToHost(command) ? "\n\n\nTOURNAMENT\n\nSent. The reply shows in chat.\n\n" : "\n\n\nTOURNAMENT\n\nCouldn't reach the host.\n\n"); } else if (flag2) { displayText = ((text3 == "bank") ? BuildText() : RunCommand(text3)); } else { string text6 = ((text3.Length > 4) ? text3.Substring(5).Trim() : ""); if (text6 == "bank" || text6.StartsWith("bank ")) { displayText = RunCommand("bank sell " + text6.Substring(4).Trim()); } else { if (!BankNet.HostHasSimpleSell) { return true; } displayText = RunSell(text6); } } if ((Object)(object)_node == (Object)null) { _node = ScriptableObject.CreateInstance<TerminalNode>(); _node.clearPreviousText = true; } _node.maxCharactersToType = ((_awaitingCode || BuilderActive) ? 250 : 40); _node.displayText = displayText; __result = _node; return false; } } } catch (Exception arg) { Plugin.Logger.LogError((object)$"[TerminalBankPatch] {arg}"); return true; } } private static string Strip(string s) { return new string(s.Where(char.IsLetterOrDigit).ToArray()); } private static void SetTypingRoom(Terminal terminal, bool roomy) { try { if ((Object)(object)terminal?.screenText == (Object)null) { return; } if (roomy) { if (_savedCharacterLimit < 0) { _savedCharacterLimit = terminal.screenText.characterLimit; } terminal.screenText.characterLimit = 0; } else if (_savedCharacterLimit >= 0) { terminal.screenText.characterLimit = _savedCharacterLimit; _savedCharacterLimit = -1; } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[TerminalBankPatch] typing room: " + ex.Message)); } } private static bool ShowPage(Terminal terminal, string text, ref TerminalNode result) { if ((Object)(object)_node == (Object)null) { _node = ScriptableObject.CreateInstance<TerminalNode>(); _node.clearPreviousText = true; } _node.maxCharactersToType = ((_awaitingCode || BuilderActive) ? 250 : 40); _node.displayText = text; result = _node; return false; } private static string RunCommand(string typed) { string text = typed.Substring("bank ".Length).Trim(); string command; string text3; if (text.StartsWith("deposit ") || text.StartsWith("d ")) { string text2 = text.Substring(text.IndexOf(' ') + 1).Trim(); command = "bank deposit " + text2; text3 = "Depositing " + text2 + "."; } else if (text.StartsWith("withdraw ") || text.StartsWith("w ")) { string text4 = text.Substring(text.IndexOf(' ') + 1).Trim(); command = "bank withdraw " + text4; text3 = "Withdrawing " + text4 + "."; } else if (text == "auto" || text.StartsWith("auto ")) { command = "bank " + text; text3 = ((text == "auto") ? "Checking auto deposit." : ("Turning auto deposit " + text.Substring(5).Trim() + ".")); } else { switch (text) { case "info": command = "bank info"; text3 = "Telling everyone how the bank works."; break; case "list": case "l": command = "bank list"; text3 = "Listing the bank in chat."; break; default: if (text.StartsWith("sell ")) { string text5 = text.Substring("sell ".Length).Trim(); command = "sell bank " + text5; text3 = "Selling " + text5 + " from the bank."; break; } return "\n\n\nSCRAP BANK\n\nbank show the bank\nbank deposit <amount|all> store ship scrap\nbank deposit <item> [count]\nbank withdraw <item> [count|all]\nbank auto on|off deposit at end of day\nbank list list it in chat\nbank info explain the bank to everyone\nbank sell <amount|quota|all> at the Company\n\n"; } } if (!SendToHost(command)) { return "\n\n\nSCRAP BANK\n\nCouldn't reach the host.\n\n"; } return "\n\n\nSCRAP BANK\n\n" + text3 + "\nThe result shows in chat. Type bank to see the bank.\n\n"; } private static string RunSell(string rest) { NetworkManager singleton = NetworkManager.Singleton; PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)singleton == (Object)null || (Object)(object)val == (Object)null || (Object)(object)HUDManager.Instance == (Object)null) { return "\n\n\nSELL\n\nCouldn't reach the host.\n\n"; } if (singleton.IsHost) { HUDManager.Instance.AddTextToChatOnServer(("/sell " + rest).Trim(), (int)val.playerClientId); if (rest.Length != 0) { return "\n\n\nSELL\n\nSelling " + rest + ".\nThe result shows in chat.\n\n"; } return "\n\n\nSELL\n\nsell <amount>, sell quota, sell all, sell <item> [count]\nsell bank <amount|quota|all>\n\n"; } HUDManager.Instance.AddTextToChatOnServer(("!sell " + rest).Trim(), (int)val.playerClientId); return "\n\n\nSELL\n\nSent \"sell " + rest + "\" to the host.\nIt only runs if the host lets you sell\n(SimpleSell whitelist). The result shows in chat.\n\n"; } private static bool SendToHost(string command) { NetworkManager singleton = NetworkManager.Singleton; PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)singleton == (Object)null || (Object)(object)val == (Object)null) { return false; } if (singleton.IsHost) { CommandRouter.Route(command, null); return true; } string text = ((!string.IsNullOrEmpty(ModConfig.AltCommandPrefix.Value)) ? ModConfig.AltCommandPrefix.Value : ModConfig.CommandPrefix.Value); HUDManager instance = HUDManager.Instance; if (instance != null) { instance.AddTextToChatOnServer(text + command, (int)val.playerClientId); } return true; } private static string BuildText() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("\n\n\nSCRAP BANK\n\n"); if (!BankNet.Received) { stringBuilder.Append("No bank from the host. The host needs SimpleHQ installed.\n\n"); return stringBuilder.ToString(); } if (BankNet.Locked) { stringBuilder.Append("LOCKED: the lobby is public. Host as Friends only,\nor /lobby friends with SimpleSell.\n\n"); } stringBuilder.Append($"Total value: ${BankNet.Total}\n"); stringBuilder.Append($"Items: {BankNet.Count}\n\n"); foreach (BankGroup group in BankNet.Groups) { stringBuilder.Append($"* {group.Name} x{group.Count} ${group.Value}\n"); } stringBuilder.Append("\nDeposit from the ship: bank deposit <amount|all>\n"); stringBuilder.Append("Sell at the Company: bank sell <amount|quota|all>\n"); stringBuilder.Append("(also /bank deposit and /sell bank in chat)\n\n"); return stringBuilder.ToString(); } } [HarmonyPatch(typeof(GameNetworkManager), "ConnectionApproval")] internal static class TournamentApprovalPatch { [HarmonyPostfix] private static void Postfix(ConnectionApprovalRequest request, ConnectionApprovalResponse response) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) try { if (response != null && response.Approved && TournamentInfo.Active) { ulong clientNetworkId = request.ClientNetworkId; NetworkManager singleton = NetworkManager.Singleton; if (clientNetworkId != ((singleton != null) ? new ulong?(singleton.LocalClientId) : ((ulong?)null))) { response.Approved = false; response.Reason = "A tournament run is in progress."; Plugin.Logger.LogInfo((object)"[Tournament] refused a join: run in progress."); } } } catch (Exception arg) { Plugin.Logger.LogError((object)$"[TournamentApprovalPatch] {arg}"); } } } [HarmonyPatch(typeof(GameNetworkManager), "Singleton_OnClientDisconnectCallback")] internal static class TournamentDisconnectPatch { [HarmonyPostfix] private static void Postfix() { try { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton != (Object)null && singleton.IsHost) { TournamentService.OnPlayersChanged(); } } catch (Exception arg) { Plugin.Logger.LogError((object)$"[TournamentDisconnectPatch] {arg}"); } } } [HarmonyPatch(typeof(Terminal), "ParsePlayerSentence")] internal static class TournamentRouteLockPatch { private static TerminalNode _node; [HarmonyPostfix] private static void Postfix(ref TerminalNode __result) { try { int lockedLevelId = TournamentApply.LockedLevelId; if ((Object)(object)__result == (Object)null || lockedLevelId < 0 || !TournamentNet.ActiveHere) { return; } int num = ((__result.buyRerouteToMoon >= 0) ? __result.buyRerouteToMoon : ((__result.buyRerouteToMoon == -2) ? __result.displayPlanetInfo : (-1))); if (num >= 0) { if ((Object)(object)_node == (Object)null) { _node = ScriptableObject.CreateInstance<TerminalNode>(); _node.clearPreviousText = true; _node.maxCharactersToType = 25; } SelectableLevel[] array = StartOfRound.Instance?.levels; string text = ((array != null && lockedLevelId < array.Length) ? TournamentApply.ShortName(array[lockedLevelId].PlanetName) : "the kit's moon"); _node.displayText = ((num == lockedLevelId) ? ("\n\n\nTOURNAMENT\n\nThe ship is already routed to " + text + ",\nfree. No need to route.\n\n") : ("\n\n\nTOURNAMENT\n\nRouting is locked to " + text + " for this run.\n\n")); __result = _node; } } catch (Exception arg) { Plugin.Logger.LogError((object)$"[TournamentRouteLockPatch] {arg}"); } } } [HarmonyPatch(typeof(StartMatchLever), "LeverAnimation")] internal static class TournamentLeverLockPatch { internal const string Tip = "[ Waiting for the tournament code ]"; internal static bool Locked(StartMatchLever lever) { if (TournamentApply.LeverLocked && TournamentNet.ActiveHere && (Object)(object)lever?.playersManager != (Object)null) { return lever.playersManager.inShipPhase; } return false; } [HarmonyPrefix] private static bool Prefix(StartMatchLever __instance) { try { if (!Locked(__instance)) { return true; } HUDManager instance = HUDManager.Instance; if (instance != null) { instance.DisplayTip("Tournament", "The ship waits at the Company until the host enters the tournament code.", false, false, "LC_Tip1"); } return false; } catch (Exception arg) { Plugin.Logger.LogError((object)$"[TournamentLeverLockPatch] {arg}"); return true; } } } [HarmonyPatch(typeof(StartMatchLever), "Update")] internal static class TournamentLeverTipPatch { [HarmonyPostfix] private static void Postfix(StartMatchLever __instance) { if ((Object)(object)__instance.triggerScript != (Object)null && TournamentLeverLockPatch.Locked(__instance)) { __instance.triggerScript.hoverTip = "[ Waiting for the tournament code ]"; } } } [HarmonyPatch(typeof(StartOfRound), "StartGame")] internal static class TournamentStartGameGuardPatch { [HarmonyPrefix] private static bool Prefix(StartOfRound __instance) { try { if (!TournamentInfo.Active || !TournamentApply.LeverLocked || !__instance.inShipPhase) { return true; } StartMatchLever obj = Object.FindObjectOfType<StartMatchLever>(); if (obj != null) { obj.CancelStartGameClientRpc(); } Plugin.Logger.LogInfo((object)"[Tournament] landing refused: waiting for the code."); return false; } catch (Exception arg) { Plugin.Logger.LogError((object)$"[TournamentStartGameGuardPatch] {arg}"); return true; } } } [HarmonyPatch(typeof(StartOfRound), "PassTimeToNextDay")] internal static class TournamentDayEndPatch { [HarmonyPrefix] private static void Prefix() { TournamentRun.OnDayEnding(); } [HarmonyPostfix] private static void Postfix() { TournamentRun.OnDayPassed(); } } [HarmonyPatch(typeof(StartOfRound), "StartGame")] internal static class TournamentTimerPatch { [HarmonyPostfix] private static void Postfix(StartOfRound __instance) { if (__instance.beganLoadingNewLevel) { TournamentRun.OnLanding(); } } } [HarmonyPatch(typeof(PlayerControllerB), "KillPlayerClientRpc")] internal static class TournamentDeathPatch { [HarmonyPostfix] private static void Postfix(int playerId) { try { StartOfRound instance = StartOfRound.Instance; string who = ((!((Object)(object)instance != (Object)null) || playerId < 0 || playerId >= instance.allPlayerScripts.Length) ? null : instance.allPlayerScripts[playerId]?.playerUsername); TournamentRun.OnPlayerDied(who); } catch (Exception arg) { Plugin.Logger.LogError((object)$"[TournamentDeathPatch] {arg}"); } } } } namespace SimpleHQ.Features { internal static class BankService { internal static void Deposit(string arg, string who) { try { if (!Ready(out var why)) { ChatUtil.Send(why); return; } if (!BankGate.CanMoveScrap(out why)) { ChatUtil.Send(why); return; } if (string.IsNullOrWhiteSpace(arg)) { ChatUtil.Send("Usage: /bank deposit <amount> | all | <item> [count|all]"); return; } List<GrabbableObject> list = FindDepositable(); if (list.Count == 0) { ChatUtil.Send("No scrap on the ship to deposit."); return; } string text = arg.Trim().ToLowerInvariant(); List<GrabbableObject> list2; int result; if (text == "all") { list2 = list; } else if (int.TryParse(text.TrimStart('$'), out result)) { if (result <= 0) { ChatUtil.Send("Amount must be above 0."); return; } int num = list.Sum((GrabbableObject o) => o.scrapValue); if (num < result) { ChatUtil.Send($"Only ${num} of scrap on the ship, not ${result}."); return; } list2 = ((num == result) ? list : BankSelect.ClosestAtOrAbove(list, (GrabbableObject o) => o.scrapValue, result)); } else { list2 = MatchByName(list, arg, out var problem); if (list2 == null) { ChatUtil.Send(problem); return; } } List<BankEntry> list3 = Bank(list2); if (list3.Count == 0) { ChatUtil.Send("Nothing could be deposited."); return; } int num2 = list3.Sum((BankEntry e) => e.Value); string text2 = (string.IsNullOrEmpty(who) ? "Deposited" : (who + " deposited")); ChatUtil.Send($"{text2} {list3.Count} item(s), ${num2}. Bank: {BankStore.Items.Count} item(s), ${BankStore.Total}.", broadcast: true); Plugin.Logger.LogInfo((object)string.Format("[Bank] {0} deposited {1} item(s) ${2} ({3})", who ?? "host", list3.Count, num2, arg)); } catch (Exception arg2) { Plugin.Logger.LogError((object)$"[BankService.Deposit] {arg2}"); } } private static List<BankEntry> Bank(List<GrabbableObject> chosen) { List<BankEntry> list = new List<BankEntry>(); foreach (GrabbableObject item in chosen) { NetworkObject component = ((Component)item).GetComponent<NetworkObject>(); if (!((Object)(object)component == (Object)null) && component.IsSpawned) { list.Add(new BankEntry { Name = item.itemProperties.itemName, Value = item.scrapValue, ItemId = ItemSpawner.IdOf(item.itemProperties), Data = ItemSpawner.SaveDataOf(item) }); component.Despawn(true); } } if (list.Count > 0) { BankStore.Add(list); BankNet.Publish(); } return list; } internal static void SetAuto(string arg, string who) { string text = (arg ?? "").Trim().ToLowerInvariant(); if (text == "on" || text == "off") { ModConfig.AutoDeposit.Value = text == "on"; string text2 = (string.IsNullOrEmpty(who) ? "" : (" (" + who + ")")); ChatUtil.Send(ModConfig.AutoDeposit.Value ? ("Auto deposit ON" + text2 + ": scrap on the ship goes into the bank at the end of each day.") : ("Auto deposit OFF" + text2 + "."), broadcast: true); } else { ChatUtil.Send("Auto deposit is " + (ModConfig.AutoDeposit.Value ? "ON" : "OFF") + ". /bank auto on|off"); } } internal static void AutoDepositEndOfDay() { try { if (!ModConfig.AutoDeposit.Value || TournamentInfo.Active) { return; } NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsHost) { return; } if (BankGate.IsLocked(out var reason)) { Plugin.Logger.LogInfo((object)("[Bank] auto deposit skipped: " + reason)); return; } List<GrabbableObject> list = FindDepositable(); if (list.Count == 0) { return; } List<BankEntry> list2 = Bank(list); if (list2.Count != 0) { int num = list2.Sum((BankEntry e) => e.Value); ChatUtil.Send($"End of day: deposited {list2.Count} item(s), ${num}. Bank: {BankStore.Items.Count} item(s), ${BankStore.Total}.", broadcast: true); Plugin.Logger.LogInfo((object)$"[Bank] auto deposit: {list2.Count} item(s) ${num}"); } } catch (Exception arg) { Plugin.Logger.LogError((object)$"[BankService.AutoDepositEndOfDay] {arg}"); } } private static List<GrabbableObject> FindDepositable() { VehicleController vehicle = Object.FindObjectOfType<VehicleController>(); HashSet<GrabbableObject> inBags = new HashSet<GrabbableObject>(); BeltBagItem[] array = Object.FindObjectsByType<BeltBagItem>((FindObjectsSortMode)0); foreach (BeltBagItem val in array) { if (val.objectsInBag == null) { continue; } foreach (GrabbableObject item in val.objectsInBag) { if ((Object)(object)item != (Object)null) { inBags.Add(item); } } } return (from o in Object.FindObjectsByType<GrabbableObject>((FindObjectsSortMode)0) where (Object)(object)o != (Object)null && (Object)(object)o.itemProperties != (Object)null && o.itemProperties.isScrap where o.scrapValue > 0 where !o.isHeld && !o.heldByPlayerOnServer && !o.isPocketed && !o.deactivated where !(o is RagdollGrabbableObject) && !(o is BeltBagItem) && !inBags.Contains(o) where !ModConfig.IsKeptOnShip(o.itemProperties.itemName) where o.isInShipRoom || o.isInElevator || (ModConfig.IncludeCruiser.Value && (Object)(object)vehicle != (Object)null && ((Component)o).transform.IsChildOf(((Component)vehicle).transform)) select o).ToList(); } private static List<GrabbableObject> MatchByName(List<GrabbableObject> eligible, string arg, out string problem) { problem = null; string[] array = arg.Trim().Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); int count = int.MaxValue; string text = arg.Trim(); if (array.Length >= 2) { string text2 = array[^1].ToLowerInvariant(); int result; if (text2 == "all") { text = string.Join(" ", array, 0, array.Length - 1); } else if (int.TryParse(text2, out result) && result > 0) { count = result; text = string.Join(" ", array, 0, array.Length - 1); } } string norm = Normalize(text); List<GrabbableObject> list = (from o in eligible where Normalize(o.itemProperties.itemName).Contains(norm) orderby o.scrapValue select o).Take(count).ToList(); if (list.Count == 0) { problem = (ModConfig.IsKeptOnShip(text) ? (text + " is on the KeepOnShip list and is never deposited.") : ("No scrap on the ship matching \"" + text + "\".")); return null; } return list; } private static string Normalize(string s) { return (s ?? "").Replace(" ", "").ToLowerInvariant(); } internal static void Withdraw(string arg, string who, PlayerControllerB requester) { try { if (!Ready(out var why)) { ChatUtil.Send(why); return; } if (!BankGate.CanMoveScrap(out why)) { ChatUtil.Send(why.Replace("only takes deposits", "only hands scrap back")); return; } if (string.IsNullOrWhiteSpace(arg)) { ChatUtil.Send("Usage: /bank withdraw <item> [count|all]"); return; } if (BankStore.Items.Count == 0) { ChatUtil.Send("The bank is empty."); return; } string[] array = arg.Trim().Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); int count = 1; string text = arg.Trim(); if (array.Length >= 2) { string text2 = array[^1].ToLowerInvariant(); int result; if (text2 == "all") { count = int.MaxValue; text = string.Join(" ", array, 0, array.Length - 1); } else if (int.TryParse(text2, out result) && result > 0) { count = result; text = string.Join(" ", array, 0, array.Length - 1); } } string norm = Normalize(text); List<BankEntry> list = BankStore.Items.Where((BankEntry e) => Normalize(e.Name) == norm).ToList(); if (list.Count == 0) { list = BankStore.Items.Where((BankEntry e) => Normalize(e.Name).Contains(norm)).ToList(); } if (list.Count == 0) { ChatUtil.Send("Nothing in the bank matching \"" + text + "\". /bank list shows what's there."); return; } List<string> list2 = list.Select((BankEntry e) => e.Name).Distinct().ToList(); if (list2.Count > 1) { ChatUtil.Send("\"" + text + "\" matches " + string.Join(", ", list2) + ". Be more specific."); return; } List<BankEntry> entries = list.OrderByDescending((BankEntry e) => e.Value).Take(count).ToList(); PlayerControllerB near = requester ?? GameNetworkManager.Instance?.localPlayerController; List<BankEntry> list3 = ItemSpawner.SpawnOnShip(entries, near); if (list3.Count == 0) { ChatUtil.Send("Couldn't spawn " + list2[0] + ". It stays in the bank."); return; } BankStore.Remove(list3); BankNet.Publish(); int num = list3.Sum((BankEntry e) => e.Value); string text3 = (string.IsNullOrEmpty(who) ? "Withdrew" : (who + " withdrew")); ChatUtil.Send($"{text3} {list3.Count} {list2[0]} (${num}). Bank: {BankStore.Items.Count} item(s), ${BankStore.Total}.", broadcast: true); Plugin.Logger.LogInfo((object)string.Format("[Bank] {0} withdrew {1} item(s) ${2} ({3})", who ?? "host", list3.Count, num, arg)); } catch (Exception arg2) { Plugin.Logger.LogError((object)$"[BankService.Withdraw] {arg2}"); } } internal static void SellFromBank(string arg, string who) { try { if (!Ready(out var why)) { ChatUtil.Send(why); return; } DepositItemsDesk val = Object.FindObjectOfType<DepositItemsDesk>(); if ((Object)(object)val == (Object)null) { ChatUtil.Send("No sell desk here. Land at the Company first."); return; } if (val.inSellingItemsAnimation || val.attacking) { ChatUtil.Send("The Company is busy. Wait for the current sale to finish."); return; } if (val.itemsOnCounter.Count > 0 || ((Component)val.deskObjectsContainer).GetComponentsInChildren<GrabbableObject>().Length != 0) { ChatUtil.Send("Items are on the counter. Sell or take them back first."); return; } if (BankStore.Items.Count == 0) { ChatUtil.Send("The bank is empty."); return; } float num = Mathf.Max(0.01f, StartOfRound.Instance.companyBuyingRate); string text = (arg ?? "").Trim().ToLowerInvariant(); List<BankEntry> list; int num2; int total; switch (text) { case "all": list = BankStore.Items.ToList(); break; case "quota": case "q": { int num3 = Mathf.Max(0, TimeOfDay.Instance.profitQuota - TimeOfDay.Instance.quotaFulfilled); if (num3 <= 0) { ChatUtil.Send("Quota already met."); return; } num2 = Mathf.CeilToInt((float)num3 / num); goto IL_0172; } default: { if (int.TryParse(text.TrimStart('$'), out var result) && result > 0) { num2 = Mathf.CeilToInt((float)result / num); goto IL_0172; } ChatUtil.Send("Usage: /sell bank <amount> | quota | all"); return; } IL_0172: total = BankStore.Total; if (total < num2) { ChatUtil.Send($"The bank only has ${total} of scrap (${(int)((float)total * num)} at today's rate). /sell bank all sells it all."); return; } list = ((total == num2) ? BankStore.Items.ToList() : BankSelect.ClosestAtOrAbove(BankStore.Items, (BankEntry e) => e.Value, num2)); break; } if (list.Count == 0) { ChatUtil.Send("Nothing to sell."); return; } int num4 = list.Sum((BankEntry e) => e.Value); float companyBuyingRate = StartOfRound.Instance.companyBuyingRate; int num5 = (int)((float)num4 * companyBuyingRate); Terminal val2 = Object.FindObjectOfType<Terminal>(); BankStore.Remove(list); val.inSellingItemsAnimation = true; val.itemsOnCounterAmount = list.Count; val2.groupCredits += num5; val.SellItemsClientRpc(num5, val2.groupCredits, list.Count, companyBuyingRate); val.SellAndDisplayItemProfits(num5, val2.groupCredits); BankNet.Publish(); string text2 = (string.IsNullOrEmpty(who) ? "Sold" : (who + " sold")); ChatUtil.Send($"{text2} {list.Count} banked item(s) (${num4}) for ${num5}. " + $"Bank: {BankStore.Items.Count} item(s), ${BankStore.Total}.", broadcast: true); Plugin.Logger.LogInfo((object)string.Format("[Bank] {0} sold {1} item(s) raw ${2} profit ${3} ({4})", who ?? "host", list.Count, num4, num5, arg)); } catch (Exception arg2) { Plugin.Logger.LogError((object)$"[BankService.SellFromBank] {arg2}"); } } internal static void Status() { if (BankGate.IsLocked(out var reason)) { ChatUtil.Send(reason); } if (BankStore.Items.Count == 0) { ChatUtil.Send("The bank is empty. /bank deposit <amount|all> stores scrap."); } else { ChatUtil.Send(string.Format("{0} item(s), ${1}. Auto deposit {2}. /bank list, or type bank in the terminal.", BankStore.Items.Count, BankStore.Total, ModConfig.AutoDeposit.Value ? "ON" : "OFF")); } } internal static void Info() { ChatUtil.Send("This lobby's host runs SimpleHQ: scrap can be stored in a bank to cut ship lag. Anyone can type /bank to see the total.", broadcast: true); ChatUtil.Send("The host and whitelisted players can store scrap with /bank deposit <amount|all> (in orbit or at the Company), take it back with /bank withdraw <item>, and sell it with /sell bank <amount>. With SimpleHQ installed, type bank in the terminal.", broadcast: true); } internal static void List() { if (BankStore.Items.Count == 0) { ChatUtil.Send("The bank is empty."); return; } string message = string.Join(", ", from e in BankStore.Items group e by e.Name into g orderby g.Sum((BankEntry e) => e.Value) descending select $"{g.Key} x{g.Count()} ${g.Sum((BankEntry e) => e.Value)}"); ChatUtil.SendChunked(message); } private static bool Ready(out string why) { why = null; NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsHost) { why = "Only the host can do that."; return false; } return !BankGate.IsLocked(out why); } internal static void OnPropsDespawning(bool despawnAllItems) { try { NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.IsHost && (despawnAllItems || ((Object)(object)StartOfRound.Instance != (Object)null && StartOfRound.Instance.allPlayersDead)) && !TournamentInfo.Active && BankStore.Items.Count != 0) { int total = BankStore.Total; int count = BankStore.Items.Count; BankStore.Clear(); BankNet.Publish(); ChatUtil.Send($"The crew was wiped out. The bank lost {count} item(s), ${total}.", broadcast: true); Plugin.Logger.LogInfo((object)$"[Bank] wiped out: lost {count} item(s) ${total}"); } } catch (Exception arg) { Plugin.Logger.LogError((object)$"[BankService.OnPropsDespawning] {arg}"); } } internal static void OnFired() { try { NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.IsHost) { int count = BankStore.Items.Count; BankStore.Clear(); BankStore.Commit(); BankNet.Publish(); if (count > 0) { Plugin.Logger.LogInfo((object)$"[Bank] fired: bank of {count} item(s) cleared"); } } } catch (Exception arg) { Plugin.Logger.LogError((object)$"[BankService.OnFired] {arg}"); } } } internal static class TournamentApply { internal const int NoWeather = int.MinValue; private static readonly List<NetworkObject> _kitItems = new List<NetworkObject>(); private static (int level, bool overrideOn, LevelWeatherType overrideType, LevelWeatherType current)? _savedWeather; private static int _applyId; internal static int LevelId { get; private set; } = -1; internal static int Weather { get; private set; } = int.MinValue; internal static int LockedLevelId { get; private set; } = -1; internal static bool LeverLocked { get; private set; } internal static void Reset() { RestoreWeather(); LevelId = -1; Weather = int.MinValue; LockedLevelId = -1; LeverLocked = false; _kitItems.Clear(); _applyId++; } private static SelectableLevel CompanyLevel() { return (StartOfRound.Instance?.levels)?.FirstOrDefault((Func<SelectableLevel, bool>)((SelectableLevel l) => (Object)(object)l != (Object)null && (l.PlanetName.Contains("Gordion") || l.PlanetName.Contains("Company")))); } internal static string Problem(Kit kit) { if (kit.QuotaCount < 0) { return "this kit's format \"" + kit.format + "\" isn't 1o or a number of quotas like 3q."; } if (kit.days == null || kit.days.Length != kit.DaysNeeded) { string formatName = kit.FormatName; object arg = kit.DaysNeeded; KitDay[] days = kit.days; return $"a {formatName} kit needs {arg} day(s), this one has {((days != null) ? days.Length : 0)}."; } for (int i = 0; i < kit.days.Length; i++) { if ((Object)(object)FindLevel(kit.days[i].moon) == (Object)null) { return $"day {i + 1}'s moon \"{kit.days[i].moon}\" isn't in this game."; } if (!TryParseWeather(kit.days[i].weather, out var _)) { return $"day {i + 1}'s weather \"{kit.days[i].weather}\" isn't a weather."; } } if (kit.quotas != null && kit.quotas.Length != 0 && kit.quotas.Length != kit.QuotaCount) { return $"this kit lists {kit.quotas.Length} quota(s) for a {kit.FormatName} run."; } KitItem[] array = kit.items ?? new KitItem[0]; foreach (KitItem kitItem in array) { if ((Object)(object)FindItem(kitItem.item) == (Object)null) { return "this kit's item \"" + kitItem.item + "\" isn't in this game."; } } return null; } internal static void StartRun(Kit kit) { StartOfRound instance = StartOfRound.Instance; TimeOfDay instance2 = TimeOfDay.Instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null) { Plugin.Logger.LogError((object)"[TournamentApply] game not ready; kit not applied."); return; } TournamentCleanup.ClearShip(); SpawnKitItems(kit); if (kit.cruiser) { ((MonoBehaviour)CoroutineRunner.Instance).StartCoroutine(OrderCruiser(_applyId + 1)); } if (!kit.OneOff) { instance2.profitQuota = FirstQuota(kit); instance2.quotaFulfilled = 0; instance2.timesFulfilledQuota = 0; } SetDeadlineThreeDays(); ApplyDay(kit.days[0], kit.credits); } internal static int FirstQuota(Kit kit) { if (kit.quotas == null || kit.quotas.Length == 0) { if (!((Object)(object)TimeOfDay.Instance != (Object)null)) { return 130; } return TimeOfDay.Instance.quotaVariables.startingQuota; } return kit.quotas[0]; } internal static void ApplyDay(KitDay day, int credits = -1) { StartOfRound instance = StartOfRound.Instance; SelectableLevel val = FindLevel(day.moon); if ((Object)(object)instance == (Object)null || (Object)(object)val == (Object)null) { Plugin.Logger.LogError((object)("[TournamentApply] day's moon '" + day.moon + "' missing; not applied.")); return; } int id = ++_applyId; LevelId = val.levelID; Weather = (TryParseWeather(day.weather, out var weather) ? weather : int.MinValue); instance.overrideRandomSeed = true; instance.overrideSeedNumber = day.seed; RestoreWeather(); LeverLocked = false; ApplyLocal(LevelId, Weather); ((MonoBehaviour)CoroutineRunner.Instance).StartCoroutine(RouteThenCredits(id, LevelId, credits)); Plugin.Logger.LogInfo((object)$"[TournamentApply] day: level={LevelId} ({val.PlanetName}) weather={Weather} seed={day.seed}."); } internal static void GoToCompany(bool lockLever) { StartOfRound instance = StartOfRound.Instance; SelectableLevel val = CompanyLevel(); if ((Object)(object)instance == (Object)null || (Object)(object)val == (Object)null) { Plugin.Logger.LogError((object)"[TournamentApply] can't find the Company."); return; } int id = ++_applyId; RestoreWeather(); instance.overrideRandomSeed = false; LevelId = val.levelID; Weather = int.MinValue; LeverLocked = lockLever; ApplyLocal(LevelId, Weather); ((MonoBehaviour)CoroutineRunner.Instance).StartCoroutine(RouteThenCredits(id, LevelId, -1)); Plugin.Logger.LogInfo((object)("[TournamentApply] to " + val.PlanetName + ", lever " + (lockLever ? "locked" : "free") + ".")); } internal static void Stage() { try { Terminal val = Object.FindObjectOfType<Terminal>(); if ((Object)(object)val != (Object)null) { int num = val.orderedItemsFromTerminal?.Count ?? 0; val.orderedItemsFromTerminal?.Clear(); bool flag = val.orderedVehicleFromTerminal != -1 || val.vehicleInDropship; val.orderedVehicleFromTerminal = -1; val.vehicleInDropship = false; val.numberOfItemsInDropship = 0; val.groupCredits = 0; val.SyncGroupCreditsClientRpc(0, 0); if (num > 0 || flag) { ChatUtil.Send(string.Format("Tournament mode cancelled {0} ordered item(s){1}.", num, flag ? " and a vehicle" : ""), broadcast: true); } Plugin.Logger.LogInfo((object)$"[TournamentApply] staging: credits 0, cancelled {num} order(s), vehicle={flag}."); } } catch (Exception arg) { Plugin.Logger.LogError((object)$"[TournamentApply.Stage] orders: {arg}"); } GoToCompany(lockLever: true); } internal static void SetDeadlineThreeDays() { TimeOfDay instance = TimeOfDay.Instance; if (!((Object)(object)instance == (Object)null)) { int num = (int)(instance.totalTime * 3f) + 1; instance.timeUntilDeadline = num; instance.UpdateProfitQuotaCurrentTime(); instance.SyncTimeClientRpc(instance.globalTime, num); } } internal static void NextQuota(Kit kit, int banked, int quotaIndex) { TimeOfDay instance = TimeOfDay.Instance; if (!((Object)(object)instance == (Object)null)) { if (kit.quotas != null && quotaIndex < kit.quotas.Length) { int num = Mathf.Max(0, (banked - instance.profitQuota) / 5); instance.quotaFulfilled = 0; instance.timesFulfilledQuota++; instance.SyncNewProfitQuotaClientRpc(kit.quotas[quotaIndex], num, instance.timesFulfilledQuota); } else { instance.SetNewProfitQuota(); } SetDeadlineThreeDays(); } } internal static void AfterVanillaQuota(Kit kit, int quotaIndex) { TimeOfDay instance = TimeOfDay.Instance; if (!((Object)(object)instance == (Object)null)) { if (kit.quotas != null && quotaIndex < kit.quotas.Length && instance.profitQuota != kit.quotas[quotaIndex]) { instance.SyncNewProfitQuotaClientRpc(kit.quotas[quotaIndex], 0, instance.timesFulfilledQuota); } SetDeadlineThreeDays(); } } internal static void Revert() { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance != (Object)null) { instance.overrideRandomSeed = false; } RestoreWeather(); LevelId = -1; Weather = int.MinValue; LockedLevelId = -1; LeverLocked = false; _kitItems.Clear(); _applyId++; } private static IEnumerator RouteThenCredits(int id, int levelId, int credits) { StartOfRound sor = StartOfRound.Instance; float until = Time.realtimeSinceStartup + 60f; while ((Object)(object)sor != (Object)null && (sor.travellingToNewLevel || !sor.inShipPhase) && Time.realtimeSinceStartup < until) { yield return (object)new WaitForSecondsRealtime(0.25f); } if (id != _applyId || (Object)(object)sor == (Object)null) { yield break; } if (sor.currentLevelID != levelId) { Terminal val = Object.FindObjectOfType<Terminal>(); if ((Object)(object)val != (Object)null) { sor.ChangeLevelServerRpc(levelId, val.groupCredits); } yield return (object)new WaitForSecondsRealtime(0.5f); until = Time.realtimeSinceStartup + 60f; while (sor.travellingToNewLevel && Time.realtimeSinceStartup < until) { yield return (object)new WaitForSecondsRealtime(0.25f); } } if (id != _applyId || credits < 0) { yield break; } try { Terminal val2 = Object.FindObjectOfType<Terminal>(); if (!((Object)(object)val2 == (Object)null)) { val2.groupCredits = credits; val2.SyncGroupCreditsClientRpc(credits, -1); Plugin.Logger.LogInfo((object)$"[TournamentApply] credits set to {credits}."); } } catch (Exception arg) { Plugin.Logger.LogError((object)$"[TournamentApply.Credits] {arg}"); } } private static IEnumerator OrderCruiser(int id) { StartOfRound sor = StartOfRound.Instance; float until = Time.realtimeSinceStartup + 60f; while ((Object)(object)sor != (Object)null && (sor.travellingToNewLevel || !sor.inShipPhase) && Time.realtimeSinceStartup < until) { yield return (object)new WaitForSecondsRealtime(0.25f); } yield return (object)new WaitForSecondsRealtime(0.5f); if (id != _applyId || (Object)(object)sor == (Object)null) { yield break; } try { if (!sor.inShipPhase) { Plugin.Logger.LogWarning((object)"[TournamentApply] not in orbit; the kit's cruiser can't be put on the magnet."); yield break; } if ((Object)(object)Object.FindObjectOfType<VehicleController>() != (Object)null) { Plugin.Logger.LogInfo((object)"[TournamentApply] a cruiser is already here; the kit's isn't spawned."); yield break; } Terminal val = Object.FindObjectOfType<Terminal>(); BuyableVehicle val2 = ((val?.buyableVehicles != null && val.buyableVehicles.Length != 0) ? val.buyableVehicles[0] : null); if ((Object)(object)val2?.vehiclePrefab == (Object)null || (Object)(object)RoundManager.Instance == (Object)null) { Plugin.Logger.LogWarning((object)"[TournamentApply] no vehicle prefab in the store; the kit's cruiser can't be spawned."); ChatUtil.Send("This kit wants a cruiser, but there's no vehicle in the store to spawn."); yield break; } Object.Instantiate<GameObject>(val2.vehiclePrefab, RoundManager.Instance.VehiclesContainer).GetComponent<NetworkObject>().Spawn(false); if ((Object)(object)val2.secondaryPrefab != (Object)null) { Object.Instantiate<GameObject>(val2.secondaryPrefab, RoundManager.Instance.VehiclesContainer).GetComponent<NetworkObject>().Spawn(false); } Plugin.Logger.LogInfo((object)"[TournamentApply] kit cruiser spawned in orbit; it magnets itself to the ship."); ChatUtil.Send("The kit includes a Company Cruiser: it's on the magnet, ready to drop with you."); } catch (Exception arg) { Plugin.Logger.LogError((object)$"[TournamentApply.OrderCruiser] {arg}"); } } private static void SpawnKitItems(Kit kit) { List<BankEntry> list = new List<BankEntry>(); KitItem[] array = kit.items ?? new KitItem[0]; foreach (KitItem kitItem in array) { Item val = FindItem(kitItem.item); if (!((Object)(object)val == (Object)null)) { for (int j = 0; j < kitItem.count; j++) { list.Add(new BankEntry { Name = val.itemName, ItemId = ItemSpawner.IdOf(val), Value = 0, Data = -1 }); } } } if (list.Count != 0) { _kitItems.Clear(); ItemSpawner.SpawnOnShip(list, null, _kitItems); TournamentCleanup.RememberKitOnly(_kitItems); } } internal static void OnRemoteState(bool active, int levelId, int weather, bool leverLocked) { try { if (!active || levelId < 0) { RestoreWeather(); LockedLevelId = -1; Weather = int.MinValue; LeverLocked = false; return; } LeverLocked = leverLocked; if (LockedLevelId != levelId || Weather != weather) { RestoreWeather(); Weather = weather; ApplyLocal(levelId, weather); } } catch (Exception arg) { Plugin.Logger.LogError((object)$"[TournamentApply.OnRemoteState] {arg}"); } } private static void ApplyLocal(int levelId, int weather) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) LockedLevelId = levelId; StartOfRound instance = StartOfRound.Instance; if (instance?.levels != null && levelId >= 0 && levelId < instance.levels.Length && weather != int.MinValue) { SelectableLevel val = instance.levels[levelId]; if (!_savedWeather.HasValue || _savedWeather.Value.level != levelId) { RestoreWeather(); _savedWeather = (levelId, val.overrideWeather, val.overrideWeatherType, val.currentWeather); } val.overrideWeather = true; val.overrideWeatherType = (LevelWeatherType)weather; val.currentWeather = (LevelWeatherType)weather; RefreshScreens(); Plugin.Logger.LogInfo((object)$"[TournamentApply] {val.PlanetName} weather fixed to {(object)(LevelWeatherType)weather}."); } } private static void RestoreWeather() { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) if (_savedWeather.HasValue) { (int, bool, LevelWeatherType, LevelWeatherType) value = _savedWeather.Value; _savedWeather = null; StartOfRound instance = StartOfRound.Instance; if (instance?.levels != null && value.Item1 >= 0 && value.Item1 < instance.levels.Length) { SelectableLevel val = instance.levels[value.Item1]; val.overrideWeather = value.Item2; val.overrideWeatherType = value.Item3; val.currentWeather = value.Item4; RefreshScreens(); } } } private static void RefreshScreens() { try { StartOfRound instance = StartOfRound.Instance; if (instance != null) { instance.SetMapScreenInfoToCurrentLevel(); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[TournamentApply] monitor refresh: " + ex.Message)); } } internal static SelectableLevel FindLevel(string name) { SelectableLevel[] array = StartOfRound.Instance?.levels; if (array == null || string.IsNullOrWhiteSpace(name)) { return null; } string q = Squash(name); return ((IEnumerable<SelectableLevel>)array).FirstOrDefault((Func<SelectableLevel, bool>)((SelectableLevel l) => (Object)(object)l != (Object)null && Squash(ShortName(l.PlanetName)) == q)) ?? ((IEnumerable<SelectableLevel>)array).FirstOrDefault((Func<SelectableLevel, bool>)((SelectableLevel l) => (Object)(object)l != (Object)null && Squash(l.PlanetName).Contains(q))); } internal static string ShortName(string planet) { int num = planet.IndexOf(' '); if (num <= 0 || !char.IsDigit(planet[0])) { return planet; } return planet.Substring(num + 1); } internal static string MoonNameByKey(int key) { SelectableLevel[] array = StartOfRound.Instance?.levels; if (array == null) { return null; } SelectableLevel[] array2 = array; foreach (SelectableLevel val in array2) { if (!((Object)(object)val == (Object)null)) { string text = ShortName(val.PlanetName); if (KitCode.MoonKey(text) == key) { return text; } } } return null; } internal static string ItemNameByKey(int key) { Terminal val = Object.FindObjectOfType<Terminal>(); if ((Object)(object)val != (Object)null && val.buyableItemsList != null) { Item[] buyableItemsList = val.buyableItemsList; foreach (Item val2 in buyableItemsList) { if ((Object)(object)val2 != (Object)null && KitCode.ItemKey(val2.itemName) == key) { return val2.itemName; } } } List<Item> list = StartOfRound.Instance?.allItemsList?.itemsList; if (list == null) { return null; } foreach (Item item in list) { if ((Object)(object)item != (Object)null && KitCode.ItemKey(item.itemName) == key) { return item.itemName; } } return null; } internal static Item FindItem(string name) { List<Item> list = StartOfRound.Instance?.allItemsList?.itemsList; if (list == null || string.IsNullOrWhiteSpace(name)) { return null; } string q = Squash(name); return ((IEnumerable<Item>)list).FirstOrDefault((Func<Item, bool>)((Item i) => (Object)(object)i != (Object)null && Squash(i.itemName) == q)); } internal static bool TryParseWeather(string s, out int weather) { //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected I4, but got Unknown weather = int.MinValue; string text = Squash(s ?? ""); switch (text) { case "": case "random": case "any": return true; case "none": case "clear": case "mild": weather = -1; return true; default: foreach (LevelWeatherType value in Enum.GetValues(typeof(LevelWeatherType))) { if (Squash(((object)value/*cast due to .constrained prefix*/).ToString()) == text) { weather = (int)value; return true; } } return false; } } internal static string WeatherName(int weather) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) return weather switch { -1 => "clear", int.MinValue => "moon's own", _ => ((object)(LevelWeatherType)weather/*cast due to .constrained prefix*/).ToString(), }; } private static string Squash(string s) { return new string((s ?? "").ToLowerInvariant().Where(char.IsLetterOrDigit).ToArray()); } } internal static class TournamentBanner { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static HandleNamedMessageDelegate <>9__1_0; public static Func<TMP_Text, float> <>9__4_1; internal void <Register>b__1_0(ulong sender, FastBufferReader reader) { try { string title = default(string); ((FastBufferReader)(ref reader)).ReadValueSafe(ref title, false); string big = default(string); ((FastBufferReader)(ref reader)).ReadValueSafe(ref big, false); string line = default(string); ((FastBufferReader)(ref reader)).ReadValueSafe(ref line, false); ShowLocal(title, big, line); } catch (Exception arg) { Plugin.Logger.LogError((object)$"[TournamentBanner] {arg}"); } } internal float <ShowLocal>b__4_1(TMP_Text t) { return t.fontSize; } } internal const string Msg = "SimpleHQ.Banner"; private static int _showId; internal static void Register() { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown NetworkManager singleton = NetworkManager.Singleton; if (((singleton != null) ? singleton.CustomMessagingManager : null) == null || singleton.IsHost) { return; } CustomMessagingManager customMessagingManager = singleton.CustomMessagingManager; object obj = <>c.<>9__1_0; if (obj == null) { HandleNamedMessageDelegate val = delegate(ulong sender, FastBufferReader reader) { try { string title = default(string); ((FastBufferReader)(ref reader)).ReadValueSafe(ref title, false); string big = default(string); ((FastBufferReader)(ref reader)).ReadValueSafe(ref big, false); string line = default(string); ((FastBufferReader)(ref reader)).ReadValueSafe(ref line, false); ShowLocal(title, big, line); } catch (Exception arg) { Plugin.Logger.LogError((object)$"[TournamentBanner] {arg}"); } }; <>c.<>9__1_0 = val; obj = (object)val; } customMessagingManager.RegisterNamedMessageHandler("SimpleHQ.Banner", (HandleNamedMessageDelegate)obj); } internal unsafe static void Show(string title, string big, string line) { //IL_0085: Unknown result type (might be due to invalid IL or missing references) ShowLocal(title, big, line); try { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsHost || singleton.ConnectedClientsIds.Count <= 1) { return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(256, (Allocator)2, 2048); try { ((FastBufferWriter)(ref val)).WriteValueSafe(title ?? "", false); ((FastBufferWriter)(ref val)).WriteValueSafe(big ?? "", false); ((FastBufferWriter)(ref val)).WriteValueSafe(line ?? "", false); singleton.CustomMessagingManager.SendNamedMessageToAll("SimpleHQ.Banner", val, (NetworkDelivery)2); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } catch (Exception arg) { Plugin.Logger.LogError((object)$"[TournamentBanner.Show] {arg}"); } } private static void ShowLocal(string title, string big, string line) { try { HUDManager hud = HUDManager.Instance; if ((Object)(object)hud == (Object)null) { return; } Animator reachedProfitQuotaAnimator = hud.reachedProfitQuotaAnimator; List<TMP_Text> list = (((Object)(object)reachedProfitQuotaAnimator == (Object)null) ? new List<TMP_Text>() : (from t in ((Component)reachedProfitQuotaAnimator).GetComponentsInChildren<TMP_Text>(true) where (Object)(object)t != (Object)null && (Object)(object)t != (Object)(object)hud.newProfitQuotaText && (Object)(object)t != (Object)(object)hud.reachedProfitQuotaBonusText select t).ToList()); if ((Object)(object)reachedProfitQuotaAnimator == (Object)null || list.Count == 0 || (Object)(object)hud.newProfitQuotaText == (Object)null) { hud.DisplayGlobalNotification(title); return; } TMP_Text val = list.OrderByDescending((TMP_Text t) => t.fontSize).First(); string[] obj = new string[3] { val.text, ((TMP_Text)hud.newProfitQuotaText).text, null }; TextMeshProUGUI reachedProfitQuotaBonusText = hud.reachedProfitQuotaBonusText; obj[2] = ((reachedProfitQuotaBonusText != null) ? ((TMP_Text)reachedProfitQuotaBonusText).text : null); string[] saved = obj; val.text = title; ((TMP_Text)hud.newProfitQuotaText).text = big; if ((Object)(object)hud.reachedProfitQuotaBonusText != (Object)null) { ((TMP_Text)hud.reachedProfitQuotaBonusText).text = line; } reachedProfitQuotaAnimator.SetBool("display", true); if ((Object)(object)hud.UIAudio != (Object)null && (Object)(object)hud.reachedQuotaSFX != (Object)null) { hud.UIAudio.PlayOneShot(hud.reachedQuotaSFX); } ((MonoBehaviour)CoroutineRunner.Instance).StartCoroutine(HideLater(++_showId, reachedProfitQuotaAnimator, val, saved)); } catch (Exception arg) { Plugin.Logger.LogError((object)$"[TournamentBanner.ShowLocal] {arg}"); } } private static IEnumerator HideLater(int id, Animator card, TMP_Text header, string[] saved) { yield return (object)new WaitForSeconds(6f); if (id != _showId) { yield break; } HUDManager hud = HUDManager.Instance; if ((Object)(object)card != (Object)null) { card.SetBool("display", false); } yield return (object)new WaitForSeconds(1.5f); if (id == _showId && !((Object)(object)hud == (Object)null)) { if ((Object)(object)header != (Object)null) { header.text = saved[0]; } if ((Object)(object)hud.newProfitQuotaText != (Object)null) { ((TMP_Text)hud.newProfitQuotaText).text = saved[1]; } if ((Object)(object)hud.reachedProfitQuotaBonusText != (Object)null && saved[2] != null) { ((TMP_Text)hud.reachedProfitQuotaBonusText).text = saved[2]; } } } } internal static class TournamentCleanup { private static readonly HashSet<ulong> _noScore = new HashSet<ulong>(); private static readonly HashSet<ulong> _kitOnly = new HashSet<ulong>(); internal static void Reset() { _noScore.Clear(); } internal static void RememberKitItems(IEnumerable<NetworkObject> objects) { NoScore(objects); } internal static void NoScore(IEnumerable<NetworkObject> objects) { foreach (NetworkObject @object in objects) { if ((Object)(object)@object != (Object)null) { _noScore.Add(@object.NetworkObjectId); } } } internal static void CollectScrap(List<GrabbableObject> aboard, out List<BankEntry> scoring, out List<BankEntry> counted) { scoring = new List<BankEntry>(); counted = new List<BankEntry>(); foreach (GrabbableObject item2 in aboard) { if (!item2.itemProperties.isScrap || item2.scrapValue <= 0) { continue; } BankEntry item = new BankEntry { Name = item2.itemProperties.itemName, Value = item2.scrapValue, ItemId = ItemSpawner.IdOf(item2.itemProperties), Data = ItemSpawner.SaveDataOf(item2) }; if (_noScore.Contains(((NetworkBehaviour)item2).NetworkObjectId)) { if (!IsKitItem(item2)) { counted.Add(item); } } else { scoring.Add(item); } } } private static bool IsKitItem(GrabbableObject o) { return _kitOnly.Contains(((NetworkBehaviour)o).NetworkObjectId); } internal static void RememberKitOnly(IEnumerable<NetworkObject> objects) { foreach (NetworkObject @object in objects) { if ((Object)(object)@object != (Object)null) { _kitOnly.Add(@object.NetworkObjectId); _noScore.Add(@object.NetworkObjectId); } } } internal static int ClearShip() { try { int num = Remove(Aboard()); _noScore.Clear(); _kitOnly.Clear(); if (num > 0) { Plugin.Logger.LogInfo((object)$"[Tournament] cleared {num} item(s) off the ship for the kit."); } return num; } catch (Exception arg) { Plugin.Logger.LogError((object)$"[TournamentCleanup.ClearShip] {arg}"); return 0; } } internal static List<GrabbableObject> Aboard() { List<VehicleController> vehicles = (from v in Object.FindObjectsByType<VehicleController>((FindObjectsSortMode)0) where (Object)(object)v != (Object)null && v.magnetedToShip select v).ToList(); List<GrabbableObject> source = (from o in Object.FindObjectsByType<GrabbableObject>((FindObjectsSortMode)0) where (Object)(object)o != (Object)null && (Object)(object)o.itemProperties != (Object)null && (Object)(object)((NetworkBehaviour)o).NetworkObject != (Object)null && ((NetworkBehaviour)o).NetworkObject.IsSpawned where !(o is RagdollGrabbableObject) && !IsShipFixture(o) select o).ToList(); HashSet<GrabbableObject> hashSet = new HashSet<GrabbableObject>(source.Where(IsAboard)); foreach (BeltBagItem item in source.OfType<BeltBagItem>()) { if (!hashSet.Contains((GrabbableObject)(object)item) || item.objectsInBag == null) { continue; } foreach (GrabbableObject item2 in item.objectsInBag) { if ((Object)(object)item2 != (Object)null && (Object)(object)((NetworkBehaviour)item2).NetworkObject != (Object)null && ((NetworkBehaviour)item2).NetworkObject.IsSpawned) { hashSet.Add(item2); } } } return hashSet.ToList(); bool IsAboard(GrabbableObject o) { if (!o.isInShipRoom && !o.isInElevator && !o.isHeld && !o.isPocketed && !o.heldByPlayerOnServer) { return vehicles.Any((VehicleController v) => ((Component)o).transform.IsChildOf(((Component)v).transform)); } return true; } } private static bool IsShipFixture(GrabbableObject o) { string text = (o.itemProperties.itemName ?? "").ToLowerInvariant(); if (!text.Contains("clipboard")) { return text.Contains("sticky"); } return true; } internal static int Remove(List<GrabbableObject> items) { HashSet<PlayerControllerB> hashSet = new HashSet<PlayerControllerB>(); foreach (GrabbableObject item in items) { if (item.isHeld && (Object)(object)item.playerHeldBy != (Object)null) { hashSet.Add(item.playerHeldBy); } } foreach (PlayerControllerB item2 in hashSet) { try { item2.DropAllHeldItemsAndSyncNonexact(); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[Tournament] couldn't make " + item2.playerUsername + " drop: " + ex.Message)); } } int num = 0; foreach (GrabbableObject item3 in items) { try { if (!((Object)(object)item3 == (Object)null) && !((Object)(object)((NetworkBehaviour)item3).NetworkObject == (Object)null) && ((NetworkBehaviour)item3).NetworkObject.IsSpawned) { _noScore.Remove(((NetworkBehaviour)item3).NetworkObjectId); _kitOnly.Remove(((NetworkBehaviour)item3).NetworkObjectId); ((NetworkBehaviour)item3).NetworkObject.Despawn(true); num++; } } catch (Exception ex2) { Plugin.Logger.LogWarning((object)("[Tournament] couldn't remove " + item3?.itemProperties?.itemName + ": " + ex2.Message)); } } return num; } } internal static class TournamentRun { private static Kit _kit; private static string _overReason; private static bool _resultsPosted; private static bool _nextIsCompanyDay; private static bool _companyDayEnded; private static bool _pendingNextQuota; private static float _lastDayEnd = -100f; private static float _lastOrbit = -100f; private static bool _wipedThisDay; private static readonly List<BankEntry> _runBank = new List<BankEntry>(); private static readonly List<(string name, int total, int days, string time, string reason)> _series = new List<(string, int, int, string, string)>(); private static float _startedAt = -1f; private static float _endedAt = -1f; internal static int RunTotal { get; private set; } internal static int DaysDone { get; private set; } internal static int QuotaIndex { get; private set; } internal static int QuotasMet { get; private set; } internal static bool Over { get; private set; } internal static bool OnCompanyDay { get; private set; } internal static int QuotaBanked { get; private set; } internal static bool Running { get { if (_kit != null) { return TournamentInfo.Active; } return false; } } internal static int SeriesTotal => _series.Sum(((string name, int total, int days, string time, string reason) l) => l.total) + RunTotal; internal static int SeriesLegs => _series.Count + ((_kit != null) ? 1 : 0); internal static int ProfitQuotaShown { get { if (!Running || _kit.OneOff || !((Object)(object)TimeOfDay.Instance != (Object)null)) { return -1; } return TimeOfDay.Instance.profitQuota; } } internal static int QuotaFulfilledShown { get { if (!Running || _kit.OneOff || _kit.hardcore || !((Object)(object)TimeOfDay.Instance != (Object)null)) { return -1; } return TimeOfDay.Instance.quotaFulfilled; } } internal static void ResetAll() { _series.Clear(); Reset(); } internal static void Reset() { _kit = null; RunTotal = 0; DaysDone = 0; QuotaIndex = 0; QuotasMet = 0; QuotaBanked = 0; Over = false; OnCompanyDay = false; _overReason = null; _resultsPosted = false; _nextIsCompanyDay = false; _companyDayEnded = false; _pendingNextQuota = false; _wipedThisDay = false; _runBank.Clear(); _startedAt = -1f; _endedAt = -1f; TournamentCleanup.Reset(); } internal static void Begin(Kit kit) { CloseLeg(); Reset(); _kit = kit; TournamentApply.StartRun(kit); } private static void CloseLeg() { if (_kit != null && (DaysDone != 0 || RunTotal != 0)) { _series.Add((_kit.name, RunTotal, DaysDone, Elapsed(), _overReason ?? "stopped early")); Plugin.Logger.LogInfo((object)$"[Tournament] leg {_series.Count} kept: '{_kit.name}' ${RunTotal} over {DaysDone} day(s)."); } } internal static void PostSeries() { if (!IsHost()) { return; } PostResults(); CloseLeg(); if (_series.Count == 0) { return; } if (_series.Count == 1) { ChatUtil.Send($"Final: \"{_series[0].name}\" ${_series[0].total} over {_series[0].days} day(s), " + "time " + _series[0].time + ".", broadcast: true); } else { ChatUtil.Send($"SERIES OVER: {_series.Count} run(s), ${_series.Sum(((string name, int total, int days, string time, string reason) l) => l.total)} in total.", broadcast: true); for (int num = 0; num < _series.Count; num++) { ChatUtil.Send($" {num + 1}. \"{_series[num].name}\" ${_series[num].total} " + $"({_series[num].days} day(s), {_series[num].time}) - {_series[num].reason}", broadcast: true); } } Plugin.Logger.LogInfo((object)$"[Tournament] series: {_series.Count} leg(s), ${_series.Sum(((string name, int total, int days, string time, string reason) l) => l.total)} total."); } internal static void OnPlayerDied(string who) { if (IsHost() && Running && !Over && _kit != null && _kit.deathsEndRun) { End((string.IsNullOrEmpty(who) ? "a player" : who) + " died (this kit ends on any death)"); ChatUtil.Send((string.IsNullOrEmpty(who) ? "A player" : who) + " died: the run is over. Results when the ship is back in orbit.", broadcast: true); TournamentNet.Publish(); } } internal static void NoteWipe() { if (IsHost() && Running && (Object)(object)StartOfRound.Instance != (Object)null && StartOfRound.Instance.allPlayersDead) { _wipedThisDay = true; Plugin.Logger.LogInfo((object)"[Tournament] crew wiped out this day."); } } internal static void OnLanding() { if (IsHost() && Running && !Over && _startedAt < 0f) { _startedAt = Time.realtimeSinceStartup; Plugin.Logger.LogInfo((object)"[Tournament] run timer started."); } } internal static string Elapsed() { if (_startedAt < 0f) { return "not started"; } float num = ((_endedAt >= 0f) ? _endedAt : Time.realtimeSinceStartup); TimeSpan timeSpan = TimeSpan.FromSeconds(num - _startedAt); if (!(timeSpan.TotalHours >= 1.0)) { return $"{timeSpan.Minutes}:{timeSpan.Seconds:00}"; } return $"{(int)timeSpan.TotalHours}:{timeSpan.Minutes:00}:{timeSpan.Seconds:00}"; } internal static string StatusLine() { if (_kit == null) { return ""; } if (Over) { return $"Run over: {_overReason}. Scrap brought back: ${RunTotal}. Time {Elapsed()}." + ((_series.Count > 0) ? $" Series: ${SeriesTotal} over {_series.Count + 1} run(s)." : "") + " The host can load the next code, or end it."; } string text = (OnCompanyDay ? "Company day" : $"day {DaysDone + 1} of {_kit.DaysNeeded}"); string text2 = (_kit.OneOff ? "" : $", quota {QuotaIndex + 1} of {_kit.QuotaCount}"); string text3 = ((_series.Count > 0) ? $" Series: run {_series.Count + 1}, ${SeriesTotal} so far." : ""); return $"{_kit.FormatName}: {text}{text2}. Run total ${RunTotal}. Time {Elapsed()}.{text3}"; } internal static void OnDayEnding() { try { if (!IsHost() || !Running || Over || Time.realtimeSinceStartup - _lastDayEnd < 10f) { return; } _lastDayEnd = Time.realtimeSinceStartup; bool wipedThisDay = _wipedThisDay; _wipedThisDay = false; if (OnCompanyDay) { List<GrabbableObject> list = TournamentCleanup.Aboard(); TournamentCleanup.CollectScrap(list, out var scoring, out var counted); List<BankEntry> list2 = counted.Concat(scoring).ToList(); TournamentCleanup.Remove(list.Where((GrabbableObject o) => o.itemProperties.isScrap && o.scrapValue > 0).ToList()); AddToBank(list2); OnCompanyDay = false; _companyDayEnded = true; Plugin.Logger.LogInfo((object)$"[Tournament] Company day over: {list2.Count} unsold scrap back in the bank."); return; } int num = 0; int num2 = 0; if (wipedThisDay) { if (_kit.hardcore) { End("the crew was wiped out (hardcore)"); } ChatUtil.Send($"Day {DaysDone + 1}: the crew was wiped out. That day's scrap is lost" + (_kit.hardcore ? "." : $"; the run total stays ${RunTotal}."), broadcast: true); } else { List<GrabbableObject> list3 = TournamentCleanup.Aboard(); TournamentCleanup.CollectScrap(list3, out var scoring2, out var counted2); num = scoring2.Sum((BankEntry e) => e.Value); num2 = scoring2.Count; RunTotal += num; QuotaBanked += num; TournamentCleanup.Remove(_kit.OneOff ? list3 : list3.Where((GrabbableObject o) => o.itemProperties.isScrap && o.scrapValue > 0).ToList()); AddToBank(scoring2.Concat(counted2).ToList()); ChatUtil.Send($"Day {DaysDone + 1} over: {num2} scrap brought back, ${num}. Run total: ${RunTotal}.", broadcast: true); } DaysDone++; Plugin.Logger.LogInfo((object)$"[Tournament] day {DaysDone} ended: banked {num2} scrap ${num}, wiped={wipedThisDay}, total ${RunTotal}."); if (Over) { return; } if (_kit.OneOff) { End("one-off complete"); } else if (DaysDone % 3 != 0) { ShowQuotaProgress(); } else if (_kit.hardcore) { TimeOfDay instance = TimeOfDay.Instance; int num3 = (((Object)(object)instance != (Object)null) ? instance.profitQuota : 0); if (QuotaBanked >= num3) { QuotasMet++; ChatUtil.Send($"Quota {QuotaIndex + 1} met: ${QuotaBanked} of ${num3}.", broadcast: true); if (QuotaIndex + 1 >= _kit.QuotaCount) { End($"all {_kit.QuotaCount} quota(s) met"); } else { _pendingNextQuota = true; } } else { ShowQuotaProgress(); End($"quota {QuotaIndex + 1} missed: ${QuotaBanked} of ${num3} (hardcore)"); } } else { _nextIsCompanyDay = true; } } catch (Exception arg) { Plugin.Logger.LogError((object)$"[TournamentRun.OnDayEnding] {arg}"); } } internal static void OnDayPassed() { try { if (IsHost() && Running && _pendingNextQuota) { _pendingNextQuota = false; int quotaBanked = QuotaBanked; QuotaIndex++; QuotaBanked = 0; TournamentApply.NextQuota(_kit, quotaBanked, QuotaIndex); TournamentNet.Publish(); } } catch (Exception arg) { Plugin.Logger.LogError((object)$"[TournamentRun.OnDayPassed] {arg}"); } } internal static void OnShipInOrbit() { try { if (!IsHost() || !Running || Time.realtimeSinceStartup - _lastOrbit < 10f) { return; } _lastOrbit = Time.realtimeSinceStartup; if (_companyDayEnded) { _companyDayEnded = false; QuotasMet++; QuotaIndex++; QuotaBanked = 0; ChatUtil.Send($"Quota {QuotaIndex} met.", broadcast: true); if (QuotaIndex >= _kit.QuotaCount) { End($"all {_kit.QuotaCount} quota(s) met"); } else { TournamentApply.AfterVanillaQuota(_kit, QuotaIndex); } } if (Over) { PostResults(); TournamentApply.GoToCompany(lockLever: true); } else if (_nextIsCompanyDay) { _nextIsCompanyDay = false; OnCompanyDay = true; TournamentApply.GoToCompany(lockLever: false); int num = WithdrawRunBank(); TimeOfDay instance = TimeOfDay.Instance; ChatUtil.Send($"Company day: sell to meet quota {QuotaIndex + 1} (${instance?.profitQuota ?? 0}). " + $"Your banked scrap is back on the ship ({num} item(s)).", broadcast: true); } else if (DaysDone < _kit.days.Length) { KitDay kitDay = _kit.days[DaysDone]; TournamentApply.ApplyDay(kitDay); TournamentApply.TryParseWeather(kitDay.weather, out var weather); ChatUtil.Send($"Day {DaysDone + 1} of {_kit.DaysNeeded}: routing to {kitDay.moon} " + "(" + TournamentApply.WeatherName(weather) + " weather).", broadcast: true); } TournamentNet.Publish(); } catch (Exception arg) { Plugin.Logger.LogError((object)$"[TournamentRun.OnShipInOrbit] {arg}"); } } internal static void OnFired() { if (IsHost() && Running && !Over) { End($"quota {QuotaIndex + 1} missed, the crew was fired"); PostResults(); TournamentApply.GoToCompany(lockLever: true); TournamentNet.Publish(); } } private static void End(string reason) { Over = true; _overReason = reason; if (_startedAt >= 0f) { _endedAt = Time.realtimeSinceStartup; } Plugin.Logger.LogInfo((object)$"[Tournament] run over: {reason}. Total ${RunTotal}, days {DaysDone}, quotas met {QuotasMet}."); } private static void PostResults() { if (!_resultsPosted && _kit != null) { _resultsPosted = true; string text = (_kit.OneOff ? "" : $" Quotas met: {QuotasMet} of {_kit.QuotaCount}."); ChatUtil.Send("TOURNAMENT OVER: \"" + _kit.name + "\" (" + _kit.FormatName + "). " + Cap(_overReason) + ".", broadcast: true); string text2 = ((_series.Count > 0) ? $" Series so far: ${SeriesTotal} over {_series.Count + 1} run(s)." : ""); ChatUtil.Send($"Scrap brought back: ${RunTotal} over {DaysDone} day(s).{text} Time: {Elapsed()}.{text2} " + "The host can load the next code, or end it with /tournament end.", broadcast: true); } } private static string Cap(string s) { if (!string.IsNullOrEmpty(s)) { return char.ToUpperInvariant(s[0]) + s.Substring(1); } return s; } private static void ShowQuotaProgress() { if (_kit.hardcore && !_kit.OneOff && !((Object)(object)TimeOfDay.Instance == (Object)null)) { ChatUtil.Send($"Quota {QuotaIndex + 1}: ${QuotaBanked} of ${TimeOfDay.Instance.profitQuota} banked.", broadcast: true); } } private static void AddToBank(List<BankEntry> entries) { if (entries.Count != 0) { BankStore.Add(entries); _runBank.AddRange(entries); BankNet.Publish(); } } private static int WithdrawRunBank() { List<BankEntry> list = _runBank.Where((BankEntry e) => BankStore.Items.Contains(e)).ToList(); if (list.Count == 0) { return 0; } List<NetworkObject> list2 = new List<NetworkObject>(); List<BankEntry> list3 = ItemSpawner.SpawnOnShip(list, null, list2); BankStore.Remove(list3); foreach (BankEntry item in list3) { _runBank.Remove(item); } TournamentCleanup.NoScore(list2); BankNet.Publish(); return list3.Count; } private static bool IsHost() { if ((Object)(object)NetworkManager.Singleton != (Object)null) { return NetworkManager.Singleton.IsHost; } return false; } } internal static class TournamentService { private const float AgreeWindowSeconds = 120f; private static bool _proposed; private static Kit _active; private static readonly HashSet<ulong> _agreed = new HashSet<ulong>(); private static int _proposalId; internal static Kit ActiveKit => _active; internal static bool Proposed => _proposed; internal static void Reset() { _proposed = false; _active = null; _agreed.Clear(); _proposalId++; TournamentInfo.Active = false; TournamentInfo.KitName = null; TournamentApply.Reset(); TournamentRun.ResetAll(); } internal static void Status() { if (TournamentInfo.Active) { ChatUtil.Send((_active != null) ? ("Tournament mode ON: \"" + _active.name + "\". " + TournamentRun.StatusLine()) : "Tournament mode ON, waiting at the Company for the code. Lobby locked, mod commands off."); } else if (_proposed) { ChatUtil.Send("Tournament mode proposed. " + AgreedLine() + " Type /agree to start."); } else { ChatUtil.Send("Tournament mode is off. The host starts it with /tournament start (or tournament at the terminal), then enters the organisers' kit code."); } } internal static string TerminalStatus() { if (TournamentInfo.Active && _active != null) { return "ACTIVE: " + _active.name + "\n" + TournamentRun.StatusLine() + "\nLobby locked, mod commands off.\nAnother code in orbit restarts with that kit."; } if (TournamentInfo.Active) { return "ON: waiting at the Co