Decompiled source of MoreCrackTogether v0.6.2
CrackLargerLobbies.dll
Decompiled 4 days 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.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using CrackLargerLobbies.Config; using CrackLargerLobbies.Patches; using Dissonance; using HarmonyLib; using Microsoft.CodeAnalysis; using Mirror; using Steamworks; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("CrackLargerLobbies")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Raises CRACK's 4-player lobby cap (configurable, default 8). Extra players share quarters rooms. Every player installs it.")] [assembly: AssemblyFileVersion("0.6.2.0")] [assembly: AssemblyInformationalVersion("0.6.2")] [assembly: AssemblyProduct("CrackLargerLobbies")] [assembly: AssemblyTitle("CrackLargerLobbies")] [assembly: AssemblyVersion("0.6.2.0")] [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 CrackLargerLobbies { public static class Diagnostics { private static readonly HashSet<string> _failed = new HashSet<string>(); public static void Safe(string what, Action a) { try { a(); } catch (Exception ex) { if (_failed.Add(what)) { Plugin.Log.LogWarning((object)("[LL] probe '" + what + "' failed (logged once): " + ex.GetType().Name + ": " + ex.Message)); } } } public static bool SafeBool(string what, Func<bool> a) { try { return a(); } catch (Exception ex) { if (_failed.Add(what)) { Plugin.Log.LogWarning((object)("[LL] probe '" + what + "' failed (logged once): " + ex.GetType().Name + ": " + ex.Message)); } return true; } } private static object F(object o, string field) { return Traverse.Create(o).Field(field).GetValue(); } public static void LogScene(Scene scene) { //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) Plugin.Log.LogInfo((object)$"[LL] scene '{((Scene)(ref scene)).name}' loaded. IsModded={ModDetection.IsModded} Blocked={ModDetection.Blocked}"); QuartersRoom[] array = Object.FindObjectsByType<QuartersRoom>((FindObjectsSortMode)0); Bed[] array2 = Object.FindObjectsByType<Bed>((FindObjectsSortMode)0); Wardrobe[] array3 = Object.FindObjectsByType<Wardrobe>((FindObjectsSortMode)0); QuartersDoor[] array4 = Object.FindObjectsByType<QuartersDoor>((FindObjectsSortMode)0); Plugin.Log.LogInfo((object)$"[LL] inventory rooms={array.Length} beds={array2.Length} wardrobes={array3.Length} doors={array4.Length}"); QuartersRoom[] array5 = array; foreach (QuartersRoom val in array5) { Plugin.Log.LogInfo((object)string.Format("[LL] room '{0}' idx={1} door={2} spawn={3}", ((Object)val).name, val.PlayerIndex, F(val, "_door") != null, ((Object)(object)val.SpawnPoint != (Object)null) ? ((object)val.SpawnPoint.position/*cast due to .constrained prefix*/).ToString() : "null")); } Bed[] array6 = array2; foreach (Bed val2 in array6) { Plugin.Log.LogInfo((object)string.Format("[LL] bed '{0}' idx={1} sceneId={2}", ((Object)val2).name, F(val2, "_playerIndex"), SceneId((Component)(object)val2))); } Wardrobe[] array7 = array3; foreach (Wardrobe val3 in array7) { Plugin.Log.LogInfo((object)string.Format("[LL] wardrobe '{0}' idx={1} sceneId={2}", ((Object)val3).name, F(val3, "_playerIndex"), SceneId((Component)(object)val3))); } QuartersDoor[] array8 = array4; foreach (QuartersDoor val4 in array8) { Plugin.Log.LogInfo((object)string.Format("[LL] door '{0}' idx={1} sceneId={2}", ((Object)val4).name, F(val4, "_assignedPlayerIndex"), SceneId((Component)(object)val4))); } } private static string SceneId(Component c) { NetworkIdentity val = c.GetComponent<NetworkIdentity>() ?? c.GetComponentInParent<NetworkIdentity>(); if (!((Object)(object)val != (Object)null)) { return "none"; } return val.sceneId.ToString("X"); } public static void LogPeer() { if (!NetworkClient.active && !NetworkServer.active) { return; } GameManager instance = GameManager.instance; if ((Object)(object)instance == (Object)null) { return; } List<string> list = new List<string>(); foreach (PlayerController player in instance.players) { list.Add(((Object)(object)player != (Object)null) ? player.index.ToString() : "null"); } Plugin.Log.LogInfo((object)string.Format("[LL] peer server={0} client={1} players={2} idx=[{3}] NS.max={4} conns={5}", NetworkServer.active, NetworkClient.active, instance.players.Count, string.Join(",", list), NetworkServer.maxConnections, NetworkServer.connections.Count)); DissonanceComms val = Object.FindFirstObjectByType<DissonanceComms>(); if ((Object)(object)val != (Object)null) { Plugin.Log.LogInfo((object)$"[LL] dissonance players={val.Players.Count}"); } } } [HarmonyPatch(typeof(MyNetworkManager), "Awake")] internal static class P_NM_Awake { private static void Postfix(MyNetworkManager __instance) { Diagnostics.Safe("NM.Awake", delegate { Plugin.Log.LogInfo((object)$"[LL] NM.maxConnections={((NetworkManager)__instance).maxConnections} auth={((object)((NetworkManager)__instance).authenticator)?.GetType().Name} transport={((object)((NetworkManager)__instance).transport)?.GetType().Name}"); }); } } [HarmonyPatch(typeof(NetworkManager), "StartHost")] internal static class P_NM_StartHost { private static void Postfix() { Diagnostics.Safe("StartHost", delegate { object obj = (((Object)(object)Transport.active != (Object)null) ? Traverse.Create((object)Transport.active).Field("UseNextGenSteamNetworking").GetValue() : null); object obj2 = null; if ((Object)(object)Transport.active != (Object)null) { object value = Traverse.Create((object)Transport.active).Field("server").GetValue(); if (value != null) { obj2 = Traverse.Create(value).Field("maxConnections").GetValue(); } } Plugin.Log.LogInfo((object)$"[LL] StartHost NS.max={NetworkServer.maxConnections} nextGen={obj} fizzyMax={obj2} aoi={(Object)(object)NetworkServer.aoi != (Object)null} transport={((object)Transport.active)?.GetType().FullName}"); }); } } [HarmonyPatch(typeof(MyNetworkManager), "OnServerSceneChanged")] internal static class P_NM_SceneChanged { private static void Postfix(string sceneName) { Diagnostics.Safe("OnServerSceneChanged", delegate { Plugin.Log.LogInfo((object)$"[LL] sceneChanged '{sceneName}' NS.max={NetworkServer.maxConnections}"); }); } } [HarmonyPatch(typeof(MyNetworkManager), "SpawnPlayerAndSync")] internal static class P_NM_SpawnGate { private static void Prefix(MyNetworkManager __instance, NetworkConnectionToClient conn) { Diagnostics.Safe("SpawnGate", delegate { ICollection<int> collection = Traverse.Create((object)__instance).Field("_spawnPending").GetValue() as ICollection<int>; GameManager instance = GameManager.instance; Plugin.Log.LogInfo((object)$"[LL] gate conn={conn?.connectionId} players={instance?.players.Count} pending={collection?.Count} freeIdx={((instance != null) ? new int?(instance.GetAvailablePlayerIndex()) : ((int?)null))} max={((NetworkManager)__instance).maxConnections}"); }); } } [HarmonyPatch(typeof(SteamLobby), "UpdateLobbyMemberLimit")] internal static class P_Lobby_Limit { private static int _lastLogged = int.MinValue; private static void Postfix(SteamLobby __instance) { Diagnostics.Safe("LobbyLimit", delegate { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) if (NetworkServer.active) { CSteamID val = (CSteamID)Traverse.Create((object)__instance).Field("currentLobbyID").GetValue(); if (((CSteamID)(ref val)).IsValid()) { int lobbyMemberLimit = SteamMatchmaking.GetLobbyMemberLimit(val); if (lobbyMemberLimit != _lastLogged) { _lastLogged = lobbyMemberLimit; Plugin.Log.LogInfo((object)$"[LL] lobbyLimit={lobbyMemberLimit} members={SteamMatchmaking.GetNumLobbyMembers(val)} conns={NetworkServer.connections.Count}"); } } } }); } } [HarmonyPatch(typeof(SteamLobby), "Awake")] internal static class P_Lobby_Awake { private static void Postfix(SteamLobby __instance) { Diagnostics.Safe("LobbyAwake", delegate { Plugin.Log.LogInfo((object)string.Format("[LL] lobby grace={0} roomCheck={1}", Traverse.Create((object)__instance).Field("_connectingGrace").GetValue(), Traverse.Create((object)__instance).Field("_roomCheckInterval").GetValue())); }); } } [HarmonyPatch(typeof(VersionAuthenticator), "OnStartServer")] internal static class P_Auth_Start { private static void Postfix(VersionAuthenticator __instance) { Diagnostics.Safe("AuthStart", delegate { Plugin.Log.LogInfo((object)string.Format("[LL] auth rejectDelay={0} IsModded={1}", Traverse.Create((object)__instance).Field("_rejectDelay").GetValue(), ModDetection.IsModded)); }); } } [HarmonyPatch(typeof(GameManager), "GetSpawnPoint")] internal static class P_GM_Spawn { private static void Postfix(int playerIndex, (Vector3, Quaternion) __result) { Diagnostics.Safe("GetSpawnPoint", delegate { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) if (__result.Item1 == Vector3.zero) { Plugin.Log.LogWarning((object)$"[LL] origin spawn idx={playerIndex} t={Time.time}"); } else { Plugin.Log.LogInfo((object)$"[LL] spawn idx={playerIndex} pos={__result.Item1}"); } }); } } [HarmonyPatch(typeof(GameManager), "AddPlayer")] internal static class P_GM_AddPlayer { private static void Postfix(GameManager __instance, PlayerController playerController) { Diagnostics.Safe("AddPlayer", delegate { Plugin.Log.LogInfo((object)$"[LL] addPlayer idx={playerController?.index} count={__instance.players.Count} server={NetworkServer.active}"); }); } } [HarmonyPatch(typeof(KillBox), "RecoverPlayer")] internal static class P_KillBox { private static void Prefix(Player player) { Diagnostics.Safe("RecoverPlayer", delegate { Plugin.Log.LogInfo((object)$"[LL] killbox recover idx={player?.playerController?.index} t={Time.time}"); }); } } [HarmonyPatch(typeof(ShiftSummaryManager), "ServerBuildSummary")] internal static class P_Summary { private static void Postfix(ShiftSummaryManager __instance) { Diagnostics.Safe("Summary", delegate { string s = (Traverse.Create((object)__instance).Field("_summaryJson").GetValue() as string) ?? ""; Plugin.Log.LogInfo((object)$"[LL] summaryJson bytes={Encoding.UTF8.GetByteCount(s)} crew={GameManager.PlayerCount} (Mirror string limit 65534)"); }); } } [HarmonyPatch(typeof(BreakManager), "CalculateSleepLocationFees")] internal static class P_SleepFees { private static void Postfix(BreakManager __instance, object __result) { Diagnostics.Safe("SleepFees", delegate { int num = ((__result is ICollection collection) ? collection.Count : (-1)); Plugin.Log.LogInfo((object)$"[LL] sleepFees count={num} allAsleep={__instance.AllPlayersAsleep} crew={GameManager.PlayerCount}"); }); } } [HarmonyPatch(typeof(BreakManager), "StartWork")] internal static class P_StartWork { private static void Prefix() { Diagnostics.Safe("StartWork", delegate { MethodBase methodBase = new StackTrace().GetFrame(3)?.GetMethod(); Plugin.Log.LogInfo((object)("[LL] StartWork from " + methodBase?.DeclaringType?.Name + "." + methodBase?.Name)); }); } } [BepInPlugin("Haaylo.CrackLargerLobbies", "CrackLargerLobbies", "0.6.2")] public class Plugin : BaseUnityPlugin { private static readonly HashSet<string> _faulted = new HashSet<string>(); private Harmony _harmony; private float _nextPeerLog; private float _menuSeenAt = -1f; private bool _autoHosted; private bool _selfTested; private bool _menuProbed; public static Plugin Instance { get; private set; } public static ManualLogSource Log { get; private set; } public static int FaultCount => _faulted.Count; public static void Fault(string site, Exception e) { if (_faulted.Add(site)) { Log.LogError((object)$"[LL] patch '{site}' failed (vanilla behaviour from now on): {e}"); } } private void Awake() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; try { ModConfig.Bind(((BaseUnityPlugin)this).Config); } catch (Exception arg) { Log.LogError((object)$"Config binding failed; defaults in effect: {arg}"); } try { _harmony = new Harmony("Haaylo.CrackLargerLobbies"); _harmony.PatchAll(typeof(Plugin).Assembly); int num = 0; foreach (MethodBase patchedMethod in _harmony.GetPatchedMethods()) { _ = patchedMethod; num++; } Log.LogInfo((object)string.Format("{0} {1}: {2} game methods patched.", "CrackLargerLobbies", "0.6.2", num)); } catch (Exception arg2) { Log.LogError((object)$"Harmony patching failed; the mod stays idle: {arg2}"); } Log.LogInfo((object)$"[LL] cap={ModConfig.CapValue} shareAllRooms={ModConfig.ShareAllRooms?.Value} scaleEvents={ModConfig.ScaleEvents?.Value} extendColors={ModConfig.ExtendColors?.Value} unity={Application.unityVersion} appVersion={Application.version}"); SceneManager.sceneLoaded += OnSceneLoaded; } private void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoaded; } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) try { Rooms.Invalidate(); Apply.Cap("sceneLoaded:" + ((Scene)(ref scene)).name); } catch (Exception e) { Fault("sceneLoaded", e); } if (ModConfig.DiagnosticsEnabled != null && ModConfig.DiagnosticsEnabled.Value) { Diagnostics.Safe("sceneLoaded", delegate { //IL_0001: Unknown result type (might be due to invalid IL or missing references) Diagnostics.LogScene(scene); }); } if (!NetworkServer.active && !NetworkClient.active) { _menuSeenAt = Time.unscaledTime; } if (ModConfig.SelfTest != null && ModConfig.SelfTest.Value && !_selfTested && NetworkServer.active) { _selfTested = true; ((MonoBehaviour)this).StartCoroutine(SelfTestAfterSettle()); } } private static bool SceneSettled() { try { return (Object)(object)GameManager.instance != (Object)null && GameManager.instance.players.Count > 0 && Rooms.Count > 0; } catch (Exception e) { Fault("selfTest.settle", e); return true; } } private IEnumerator SelfTestAfterSettle() { float deadline = Time.unscaledTime + 60f; while (Time.unscaledTime < deadline && !SceneSettled()) { yield return (object)new WaitForSecondsRealtime(1f); } yield return (object)new WaitForSecondsRealtime(3f); yield return ((MonoBehaviour)this).StartCoroutine(SummaryProbe()); Diagnostics.Safe("selfTest", delegate { SelfTest.Run(_harmony); }); Diagnostics.Safe("summaryProbe.clear", delegate { if ((Object)(object)ShiftSummaryManager.instance != (Object)null) { ShiftSummaryManager.instance.ServerClearSummary(); } }); float num = ((ModConfig.SelfTestQuitSeconds != null) ? ModConfig.SelfTestQuitSeconds.Value : 0f); if (num > 0f) { yield return (object)new WaitForSecondsRealtime(num); Log.LogInfo((object)"[LL][SELFTEST] quitting"); Application.Quit(); } } private IEnumerator SummaryProbe() { bool flag = false; try { ShiftSummaryManager instance = ShiftSummaryManager.instance; if ((Object)(object)instance == (Object)null) { Log.LogWarning((object)"[LL][SELFTEST] shiftsummary: no ShiftSummaryManager"); } else { int capValue = ModConfig.CapValue; string[] array = new string[capValue]; for (int i = 0; i < capValue; i++) { array[i] = "TESTER " + (i + 1); } AccessTools.Field(typeof(ShiftSummaryManager), "SimNamesLong").SetValue(null, array); instance.ServerSimulate("busy"); flag = true; } } catch (Exception e) { Fault("summaryProbe", e); } if (!flag) { yield break; } yield return (object)new WaitForSecondsRealtime(7f); try { ShiftCompleteUI val = Object.FindFirstObjectByType<ShiftCompleteUI>((FindObjectsInactive)1); if ((Object)(object)val != (Object)null) { SummaryScroll.ScrollTo(val, 1f); } Log.LogInfo((object)("[LL][SELFTEST] shiftsummary " + (((Object)(object)val != (Object)null) ? SummaryScroll.Describe(val) : "no ShiftCompleteUI") + " (top; shown for 7 s)")); } catch (Exception e2) { Fault("summaryProbe.describe", e2); } yield return (object)new WaitForSecondsRealtime(7f); try { ShiftCompleteUI val2 = Object.FindFirstObjectByType<ShiftCompleteUI>((FindObjectsInactive)1); float num = (((Object)(object)val2 != (Object)null) ? SummaryScroll.ScrollTo(val2, 0f) : (-1f)); Log.LogInfo((object)$"[LL][SELFTEST] shiftsummary-scrolled normalized={num:F2} (shown for 7 s)"); } catch (Exception e3) { Fault("summaryProbe.scroll", e3); } yield return (object)new WaitForSecondsRealtime(7f); } private IEnumerator MenuProbe() { string opened = "none"; try { MainMenuController val = Object.FindFirstObjectByType<MainMenuController>(); if ((Object)(object)val != (Object)null) { val.ShowHost(); opened = "ShowHost"; } else { Button[] array = Object.FindObjectsByType<Button>((FindObjectsSortMode)0); foreach (Button val2 in array) { TMP_Text componentInChildren = ((Component)val2).GetComponentInChildren<TMP_Text>(true); if ((Object)(object)componentInChildren != (Object)null && componentInChildren.text != null && componentInChildren.text.IndexOf("HOST", StringComparison.OrdinalIgnoreCase) >= 0) { ((UnityEvent)val2.onClick).Invoke(); opened = ((Object)val2).name; break; } } } } catch (Exception e) { Fault("menuProbe.open", e); } yield return (object)new WaitForSecondsRealtime(1f); HostPanelController val3 = Object.FindFirstObjectByType<HostPanelController>((FindObjectsInactive)1); if ((Object)(object)val3 == (Object)null) { Log.LogWarning((object)"[LL][SELFTEST] hostpanel: no HostPanelController in the menu scene"); yield break; } GameObject val4 = null; try { val4 = HostPanelUi.Ensure(val3); } catch (Exception e2) { Fault("menuProbe", e2); } string text = (((Object)(object)val4 != (Object)null) ? HostPanelUi.CurrentLabel(val4) : null); Slider val5 = HostPanelUi.FindSlider(val4); Log.LogInfo((object)string.Format("[LL][SELFTEST] hostpanel row={0} label=\"{1}\" slider={2} opened={3} panelActive={4} (shown for 4 s)", ((Object)(object)val4 != (Object)null) ? "ok" : "missing", text, ((Object)(object)val5 != (Object)null) ? val5.value.ToString() : "none", opened, ((Component)val3).gameObject.activeInHierarchy)); yield return (object)new WaitForSecondsRealtime(4f); try { MainMenuController val6 = Object.FindFirstObjectByType<MainMenuController>(); if ((Object)(object)val6 != (Object)null) { val6.ShowSettings(); } } catch (Exception e3) { Fault("menuProbe.settings", e3); } yield return (object)new WaitForSecondsRealtime(1f); try { SettingsPanel val7 = Object.FindFirstObjectByType<SettingsPanel>((FindObjectsInactive)1); GameObject val8 = (((Object)(object)val7 != (Object)null) ? SettingsUi.Ensure(val7) : null); List<string> values = SettingsUi.Verify(val8); Log.LogInfo((object)("[LL][SELFTEST] settings card=" + (((Object)(object)val8 != (Object)null) ? "ok" : "missing") + " problems=[" + string.Join("; ", values) + "] (panel shown for 4 s)")); } catch (Exception e4) { Fault("menuProbe.settingsCard", e4); } yield return (object)new WaitForSecondsRealtime(4f); } private void Update() { if (ModConfig.SelfTest != null && ModConfig.SelfTest.Value && !_menuProbed && _menuSeenAt >= 0f && Time.unscaledTime - _menuSeenAt > 3f && !NetworkServer.active && !NetworkClient.active) { _menuProbed = true; ((MonoBehaviour)this).StartCoroutine(MenuProbe()); } if (ModConfig.AutoHostOffline != null && ModConfig.AutoHostOffline.Value && !_autoHosted && Time.unscaledTime > 12f && (_menuSeenAt < 0f || Time.unscaledTime - _menuSeenAt > 10f) && !NetworkServer.active && !NetworkClient.active) { _autoHosted = Diagnostics.SafeBool("autoHost", AutoHostOffline); } if (ModConfig.DiagnosticsEnabled != null && ModConfig.DiagnosticsEnabled.Value && ModConfig.PeerIntervalSeconds != null && ModConfig.PeerIntervalSeconds.Value > 0f && Time.unscaledTime >= _nextPeerLog) { _nextPeerLog = Time.unscaledTime + ModConfig.PeerIntervalSeconds.Value; Diagnostics.Safe("peer", Diagnostics.LogPeer); } } private static bool AutoHostOffline() { if ((Object)(object)SteamLobby.instance == (Object)null || (Object)(object)NetworkManager.singleton == (Object)null) { return false; } int num = -1; for (int i = 0; i < 3; i++) { if (!SaveSlots.SlotExists(i)) { num = i; break; } } if (num < 0) { Log.LogWarning((object)"[LL] autoHost: no empty save slot; not hosting (will not touch existing saves)"); return true; } GameMode.Set((GameModeKind)0); SaveSlots.SelectedSlot = num; SaveSlots.StartNewGame = SaveSlots.PrepareSlot(num); SaveSlots.HandoffDone = true; Log.LogInfo((object)$"[LL] autoHost: offline session in empty slot {num}"); SteamLobby.instance.HostOffline(); return true; } } public static class PluginInfo { public const string GUID = "Haaylo.CrackLargerLobbies"; public const string NAME = "CrackLargerLobbies"; public const string VERSION = "0.6.2"; } public static class RoomMath { public static bool NeedsMapping(int playerIndex, int roomCount) { if (roomCount > 0) { return playerIndex >= roomCount; } return false; } public static int RoomIndex(int playerIndex, int roomCount) { if (roomCount > 0 && playerIndex >= 0) { return playerIndex % roomCount; } return playerIndex; } public static int Occupant(int playerIndex, int roomCount) { if (roomCount > 0 && playerIndex >= 0) { return playerIndex / roomCount; } return 0; } public static void Offset(int occupant, float step, out float right, out float forward) { right = 0f; forward = 0f; if (occupant > 0 && !(step <= 0f)) { int num = (occupant - 1) / 8 + 1; int num2 = (occupant - 1) % 8; float num3 = step * (float)num; switch (num2) { case 0: right = num3; break; case 1: right = 0f - num3; break; case 2: forward = num3; break; case 3: forward = 0f - num3; break; case 4: right = num3; forward = num3; break; case 5: right = 0f - num3; forward = num3; break; case 6: right = num3; forward = 0f - num3; break; default: right = 0f - num3; forward = 0f - num3; break; } } } public static int BedSlot(int playerIndex, int bedIndex) { if (playerIndex < 0 || playerIndex == bedIndex) { return 0; } if (bedIndex >= 0 && playerIndex >= bedIndex) { return playerIndex; } return playerIndex + 1; } public static int LowestFreeIndex(ICollection<int> used, int cap) { for (int i = 0; i < cap; i++) { if (!used.Contains(i)) { return i; } } return -1; } public static int Extrapolate(int[] table, int crew) { if (table == null || table.Length == 0) { return 0; } int num = table.Length; if (crew <= num) { return table[Math.Max(0, crew - 1)]; } int num2 = table[num - 1]; int num3 = ((num >= 2) ? Math.Max(0, num2 - table[num - 2]) : Math.Max(0, num2 / Math.Max(1, num))); return num2 + num3 * (crew - num); } public static float ExtraHue(int k) { double num = 0.08 + 0.6180339887 * (double)k; return (float)(num - Math.Floor(num)); } } public static class SelfTest { private static int _pass; private static int _fail; private static void Check(string name, bool ok, string detail) { if (ok) { _pass++; Plugin.Log.LogInfo((object)("[LL][SELFTEST] " + name + ": PASS " + detail)); } else { _fail++; Plugin.Log.LogError((object)("[LL][SELFTEST] " + name + ": FAIL " + detail)); } } private static void Guarded(string name, Action a) { try { a(); } catch (Exception ex) { Check(name, ok: false, "threw " + ex.GetType().Name + ": " + ex.Message); } } public static void Run(Harmony harmony) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) _pass = (_fail = 0); int cap = ModConfig.CapValue; int r = Rooms.Count; ManualLogSource log = Plugin.Log; object[] obj = new object[4] { cap, r, NetworkServer.active, null }; Scene activeScene = SceneManager.GetActiveScene(); obj[3] = ((Scene)(ref activeScene)).name; log.LogInfo((object)string.Format("[LL][SELFTEST] begin cap={0} rooms={1} server={2} scene={3}", obj)); Guarded("patches", delegate { int num2 = 0; int num3 = 0; List<string> list = new List<string>(); Type[] types = typeof(SelfTest).Assembly.GetTypes(); for (int j = 0; j < types.Length; j++) { foreach (HarmonyPatch customAttribute in ((MemberInfo)types[j]).GetCustomAttributes<HarmonyPatch>(inherit: true)) { if (!(((HarmonyAttribute)customAttribute).info.declaringType == null) && ((HarmonyAttribute)customAttribute).info.methodName != null) { num2++; MethodInfo methodInfo = AccessTools.Method(((HarmonyAttribute)customAttribute).info.declaringType, ((HarmonyAttribute)customAttribute).info.methodName, (Type[])null, (Type[])null); Patches val = ((methodInfo != null) ? Harmony.GetPatchInfo((MethodBase)methodInfo) : null); if (val != null && val.Owners.Contains("Haaylo.CrackLargerLobbies")) { num3++; } else { list.Add(((HarmonyAttribute)customAttribute).info.declaringType.Name + "." + ((HarmonyAttribute)customAttribute).info.methodName); } } } } Check("patches", list.Count == 0, $"{num3}/{num2} patch classes bound" + ((list.Count > 0) ? (" missing: " + string.Join(",", list)) : "")); }); Guarded("cap.networkManager", delegate { NetworkManager singleton = NetworkManager.singleton; Check("cap.networkManager", (Object)(object)singleton != (Object)null && singleton.maxConnections == cap, $"maxConnections={singleton?.maxConnections}"); }); Guarded("cap.networkServer", delegate { Check("cap.networkServer", !NetworkServer.active || NetworkServer.maxConnections == cap, $"NetworkServer.maxConnections={NetworkServer.maxConnections} active={NetworkServer.active}"); }); Guarded("index.free", delegate { GameManager instance = GameManager.instance; int num2 = (((Object)(object)instance != (Object)null) ? instance.GetAvailablePlayerIndex() : (-2)); int num3 = (((Object)(object)instance != (Object)null) ? instance.players.Count : (-1)); Check("index.free", (Object)(object)instance != (Object)null && num2 >= 0 && num2 < cap, $"players={num3} nextFree={num2}"); }); Guarded("rooms.count", delegate { Check("rooms.count", r > 0, $"R={r} (0 means the play scene is not loaded or _all is empty)"); }); if (r > 0) { GameManager gm = GameManager.instance; for (int num = r; num < cap; num++) { int i = num; Guarded($"spawn[{i}]", delegate { //IL_0039: 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_0067: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) (Vector3, Quaternion) spawnPoint = gm.GetSpawnPoint(i); QuartersRoom val = QuartersRoom.Get(RoomMath.RoomIndex(i, r)); bool flag = spawnPoint.Item1 != Vector3.zero; float num2 = -1f; if ((Object)(object)val != (Object)null && (Object)(object)val.SpawnPoint != (Object)null) { num2 = Vector3.Distance(spawnPoint.Item1, Rooms.OffsetPosition(val.SpawnPoint, RoomMath.Occupant(i, r))); } bool flag2 = num2 >= 0f && num2 < 0.05f; Check($"spawn[{i}]", flag && flag2, $"pos={spawnPoint.Item1} room={((val != null) ? new int?(val.PlayerIndex) : ((int?)null))} distToExpected={num2:F3}"); }); Guarded($"bed[{i}]", delegate { Bed forPlayer = Bed.GetForPlayer(i); Bed forPlayer2 = Bed.GetForPlayer(RoomMath.RoomIndex(i, r)); Check($"bed[{i}]", (Object)(object)forPlayer != (Object)null && (Object)(object)forPlayer == (Object)(object)forPlayer2, "bed=" + (((Object)(object)forPlayer != (Object)null) ? ((Object)forPlayer).name : "null") + " sharedWith=" + (((Object)(object)forPlayer2 != (Object)null) ? ((Object)forPlayer2).name : "null")); }); } Guarded("doors", delegate { QuartersDoor[] array = Object.FindObjectsByType<QuartersDoor>((FindObjectsSortMode)0); FieldRef<QuartersDoor, int> val = AccessTools.FieldRefAccess<QuartersDoor, int>("_assignedPlayerIndex"); int num2 = 0; int num3 = 0; QuartersDoor[] array2 = array; foreach (QuartersDoor val2 in array2) { int num4 = val.Invoke(val2); if (num4 >= 0) { num3++; bool num5 = Owner.Shares(num4 + r, num4); int playerIndex = ((num4 + r + 1 < cap) ? (num4 + r + 1) : ((num4 + 1) % r)); bool flag = (Rooms.ShareAll ? Owner.Shares(playerIndex, num4) : (!Owner.Shares(playerIndex, num4) || r == 1)); if (num5 && flag) { num2++; } } } Check("doors", num3 > 0 && num2 == num3, string.Format("{0}/{1} doors accept their sharer and {2}", num2, num3, Rooms.ShareAll ? "everyone else (ShareAllRooms)" : "reject others")); }); Guarded("wardrobe.restore", delegate { Wardrobe[] array = Object.FindObjectsByType<Wardrobe>((FindObjectsSortMode)0); int num2 = 0; Wardrobe[] array2 = array; foreach (Wardrobe val in array2) { int num3 = WardrobeFields.PlayerIndex.Invoke(val); int num4 = WardrobeFields.SwapFor(val, null); WardrobeFields.Restore(val, num4); if (num4 == int.MinValue && WardrobeFields.PlayerIndex.Invoke(val) == num3) { num2++; } } Check("wardrobe.restore", array.Length != 0 && num2 == array.Length, $"{num2}/{array.Length} wardrobes unchanged by a no-op swap"); }); } Guarded("scale.candy", delegate { CandyDropEvent val = Object.FindFirstObjectByType<CandyDropEvent>((FindObjectsInactive)1); if ((Object)(object)val == (Object)null) { Check("scale.candy", ok: true, "no CandyDropEvent in scene (skipped)"); } else { MethodInfo methodInfo = AccessTools.Method(typeof(CandyDropEvent), "CountForCrew", (Type[])null, (Type[])null); int num2 = (int)methodInfo.Invoke(val, new object[1] { 4 }); int num3 = (int)methodInfo.Invoke(val, new object[1] { cap }); Check("scale.candy", cap <= 4 || num3 >= num2, $"crew4={num2} crew{cap}={num3}"); } }); Guarded("colors.blips", delegate { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) PlayerBlips val = Object.FindFirstObjectByType<PlayerBlips>((FindObjectsInactive)1); if ((Object)(object)val == (Object)null) { Check("colors.blips", ok: true, "no PlayerBlips in scene (skipped)"); } else { MethodInfo methodInfo = AccessTools.Method(typeof(PlayerBlips), "ColorForIndex", (Type[])null, (Type[])null); Color val2 = (Color)methodInfo.Invoke(val, new object[1] { 0 }); Color val3 = (Color)methodInfo.Invoke(val, new object[1] { 4 }); Check("colors.blips", cap <= 4 || val2 != val3, $"c0={val2} c4={val3}"); } }); Guarded("colors.palette", delegate { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) Color[] array = Palette.Extend((Color[])(object)new Color[4] { Color.red, Color.blue, Color.green, Color.yellow }, cap); HashSet<Color> hashSet = new HashSet<Color>(array); Check("colors.palette", array.Length == Math.Max(4, cap) && hashSet.Count == array.Length, $"{array.Length} colours, {hashSet.Count} distinct"); }); Guarded("hostpanel.row", delegate { HostPanelController val = Object.FindFirstObjectByType<HostPanelController>((FindObjectsInactive)1); if ((Object)(object)val == (Object)null) { Check("hostpanel.row", ok: true, "no HostPanelController in this scene (skipped)"); } else { GameObject val2 = HostPanelUi.Ensure(val); string text = (((Object)(object)val2 != (Object)null) ? HostPanelUi.CurrentLabel(val2) : null); Slider val3 = HostPanelUi.FindSlider(val2); bool flag = (((Object)(object)Object.FindFirstObjectByType<SettingsPanel>((FindObjectsInactive)1) == (Object)null) ? ((Object)(object)val3 == (Object)null) : ((Object)(object)val3 != (Object)null && (int)val3.value == cap && val3.minValue == 4f && val3.maxValue == 16f && val3.wholeNumbers)); bool ok = (Object)(object)val2 != (Object)null && text == HostPanelUi.Label(cap) && flag; Check("hostpanel.row", ok, "label=\"" + text + "\" slider=" + (((Object)(object)val3 != (Object)null) ? $"{val3.value} [{val3.minValue}-{val3.maxValue}] whole={val3.wholeNumbers}" : "null")); } }); Guarded("settings.card", delegate { SettingsPanel val = Object.FindFirstObjectByType<SettingsPanel>((FindObjectsInactive)1); if ((Object)(object)val == (Object)null) { Check("settings.card", ok: true, "no SettingsPanel in this scene (skipped)"); } else { List<string> list = SettingsUi.Verify(SettingsUi.Ensure(val)); Check("settings.card", list.Count == 0, (list.Count == 0) ? "3 tweak toggles match config" : string.Join("; ", list)); } }); Guarded("summary.scroll", delegate { ShiftCompleteUI val = Object.FindFirstObjectByType<ShiftCompleteUI>((FindObjectsInactive)1); if ((Object)(object)val == (Object)null) { Check("summary.scroll", ok: true, "no ShiftCompleteUI in this scene (skipped)"); } else { string text = SummaryScroll.Describe(val); bool flag = text.Contains($"rows={cap} "); bool flag2 = text.Contains("scroll=True"); Check("summary.scroll", flag && flag2, text); } }); Check("patchFaults", Plugin.FaultCount == 0, $"{Plugin.FaultCount} patch sites faulted (see [LL] patch '...' failed lines)"); Plugin.Log.LogInfo((object)$"[LL][SELFTEST] summary: {_pass} passed, {_fail} failed"); } } } namespace CrackLargerLobbies.Patches { [HarmonyPatch(typeof(MyNetworkManager), "Awake")] internal static class P_Cap_Awake { private static void Postfix(MyNetworkManager __instance) { try { int maxConnections = ((NetworkManager)__instance).maxConnections; ((NetworkManager)__instance).maxConnections = ModConfig.CapValue; Plugin.Log.LogInfo((object)$"[LL] cap: maxConnections {maxConnections} -> {((NetworkManager)__instance).maxConnections}"); } catch (Exception e) { Plugin.Fault("Cap.Awake", e); } } } [HarmonyPatch(typeof(SteamLobby), "HostLobby")] internal static class P_Cap_HostLobby { private static void Prefix() { Apply.Cap("HostLobby"); } } public static class Apply { public static void Cap(string where, bool quiet = false) { try { NetworkManager singleton = NetworkManager.singleton; if ((Object)(object)singleton == (Object)null) { return; } int capValue = ModConfig.CapValue; if (singleton.maxConnections != capValue) { if (quiet) { Plugin.Log.LogInfo((object)$"[LL] cap: maxConnections {singleton.maxConnections} -> {capValue} ({where})"); } else { Plugin.Log.LogWarning((object)$"[LL] cap: maxConnections was {singleton.maxConnections} at {where}, forcing {capValue}"); } singleton.maxConnections = capValue; } } catch (Exception e) { Plugin.Fault("Cap." + where, e); } } } [HarmonyPatch(typeof(NetworkManager), "StartHost")] [HarmonyPriority(600)] internal static class P_Cap_StartHost { private static void Postfix() { try { int capValue = ModConfig.CapValue; if (NetworkServer.maxConnections != capValue) { Plugin.Log.LogWarning((object)$"[LL] cap: NetworkServer.maxConnections was {NetworkServer.maxConnections}, forcing {capValue}"); NetworkServer.maxConnections = capValue; } Transport active = Transport.active; if ((Object)(object)active == (Object)null) { return; } object value = Traverse.Create((object)active).Field("server").GetValue(); if (value == null) { return; } Traverse val = Traverse.Create(value).Field("maxConnections"); if (val.FieldExists()) { int num = (int)val.GetValue(); if (num != capValue) { Plugin.Log.LogWarning((object)$"[LL] cap: transport server maxConnections was {num}, forcing {capValue}"); val.SetValue((object)capValue); } } } catch (Exception e) { Plugin.Fault("Cap.StartHost", e); } } } [HarmonyPatch(typeof(GameManager), "GetAvailablePlayerIndex")] internal static class P_Cap_Index { private static readonly HashSet<int> Used = new HashSet<int>(); private static bool Prefix(GameManager __instance, ref int __result) { try { Used.Clear(); List<PlayerController> players = __instance.players; for (int i = 0; i < players.Count; i++) { if ((Object)(object)players[i] != (Object)null) { Used.Add(players[i].index); } } __result = RoomMath.LowestFreeIndex(Used, ModConfig.CapValue); return false; } catch (Exception e) { Plugin.Fault("Cap.Index", e); return true; } } } public static class HostPanelUi { public const string LabelName = "LL_MaxPlayersLabel"; public const string SliderName = "LL_MaxPlayersSlider"; public const int SliderMin = 4; private const float RowY = -427f; private const float OldTopY = -320.93f; private const float OldPitch = 83.07f; private const float NewTopY = -311f; private const float NewPitch = 58f; private static readonly FieldRef<HostPanelController, Toggle> ChillToggle = AccessTools.FieldRefAccess<HostPanelController, Toggle>("_chillModeToggle"); private static readonly FieldRef<HostPanelController, GameObject> HolidayRow = AccessTools.FieldRefAccess<HostPanelController, GameObject>("_holidayThemeRow"); private static bool _hierarchyLogged; public static string Label(int cap) { return $"MAX PLAYERS: {cap}"; } public static GameObject Ensure(HostPanelController panel) { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Expected O, but got Unknown //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)panel == (Object)null) { return null; } Toggle val = ChillToggle.Invoke(panel); if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)"[LL] hostpanel: no _chillModeToggle to clone"); return null; } Transform val2 = FindRow(((Component)val).transform, HolidayRow.Invoke(panel)); if ((Object)(object)val2 == (Object)null) { Plugin.Log.LogWarning((object)"[LL] hostpanel: could not identify the chill-mode row"); return null; } Transform parent = val2.parent; Transform val3 = parent.Find("LL_MaxPlayersLabel"); if ((Object)(object)val3 != (Object)null) { if ((Object)(object)parent.Find("LL_MaxPlayersSlider") == (Object)null) { AddSlider(parent, ((Component)val3).gameObject, ((RectTransform)val3).anchoredPosition.x); } Refresh(parent); return ((Component)val3).gameObject; } if (!_hierarchyLogged) { _hierarchyLogged = true; Plugin.Log.LogInfo((object)("[LL] hostpanel template: " + UiKit.Describe(val2, 2))); } float num = float.MaxValue; foreach (RectTransform item in LabelRows(parent)) { float x = item.anchoredPosition.x; if (x < num) { num = x; } } if (num == float.MaxValue) { num = -308f; } GameObject val4 = UiKit.CloneDetached(((Component)val2).gameObject, parent, "LL_MaxPlayersLabel"); Toggle componentInChildren = val4.GetComponentInChildren<Toggle>(true); if ((Object)(object)componentInChildren != (Object)null) { Object.DestroyImmediate((Object)(object)((Component)componentInChildren).gameObject); } UiKit.Rect(val4).anchoredPosition = new Vector2(num, -427f); TMP_Text val5 = UiKit.FirstText(val4); if ((Object)(object)val5 != (Object)null) { ((Graphic)val5).raycastTarget = false; } UiKit.SetText(val4, Label(ModConfig.CapValue)); val4.SetActive(true); foreach (RectTransform item2 in LabelRows(parent)) { RectTransform val6 = item2; float num2 = -311f + (val6.anchoredPosition.y - -320.93f) * 0.6982063f; val6.anchoredPosition = new Vector2(val6.anchoredPosition.x, num2); } bool flag = AddSlider(parent, val4, num); Plugin.Log.LogInfo((object)$"[LL] hostpanel: max-players row added (slider={flag}, container={((Object)parent).name})"); return val4; } private static bool AddSlider(Transform container, GameObject label, float leftX) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) Slider val = FindMenuSlider(); if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)"[LL] hostpanel: no Settings slider to clone yet; cap stays config-only until the panel is reopened"); return false; } GameObject obj = UiKit.CloneDetached(((Component)val).gameObject, container, "LL_MaxPlayersSlider"); RectTransform val2 = UiKit.Rect(obj); UiKit.Place(val2, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(leftX + 181f + 40f + 290f, -427f), new Vector2(580f, (val2.sizeDelta.y > 0f) ? val2.sizeDelta.y : 46f)); UiKit.BindSlider(obj, 4f, 16f, whole: true, ModConfig.CapValue, "HostPanel.Slider", delegate(float v) { int num = Mathf.Clamp(Mathf.RoundToInt(v), 4, 16); if (ModConfig.Cap != null && ModConfig.Cap.Value != num) { ModConfig.Cap.Value = num; } UiKit.SetText(label, Label(ModConfig.CapValue)); Apply.Cap("HostPanel", quiet: true); }); obj.SetActive(true); return true; } public static void Refresh(Transform container) { Transform val = container.Find("LL_MaxPlayersLabel"); if ((Object)(object)val != (Object)null) { UiKit.SetText(((Component)val).gameObject, Label(ModConfig.CapValue)); } Transform val2 = container.Find("LL_MaxPlayersSlider"); Slider val3 = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponentInChildren<Slider>(true) : null); if ((Object)(object)val3 != (Object)null) { val3.SetValueWithoutNotify((float)ModConfig.CapValue); } } public static Slider FindSlider(GameObject label) { Transform val = (((Object)(object)label != (Object)null && (Object)(object)label.transform.parent != (Object)null) ? label.transform.parent.Find("LL_MaxPlayersSlider") : null); if (!((Object)(object)val != (Object)null)) { return null; } return ((Component)val).GetComponentInChildren<Slider>(true); } public static string CurrentLabel(GameObject label) { return UiKit.GetText(label); } private static IEnumerable<Transform> LabelRows(Transform container) { for (int i = 0; i < container.childCount; i++) { Transform child = container.GetChild(i); if (!(((Object)child).name == "LL_MaxPlayersLabel") && !(((Object)child).name == "LL_MaxPlayersSlider") && (Object)(object)((Component)child).GetComponent<TMP_Text>() != (Object)null && (Object)(object)((Component)child).GetComponentInChildren<Toggle>(true) != (Object)null) { yield return child; } } } private static Transform FindRow(Transform toggle, GameObject holidayRow) { if ((Object)(object)holidayRow != (Object)null && (Object)(object)holidayRow.transform.parent != (Object)null) { Transform parent = holidayRow.transform.parent; Transform val = toggle; while ((Object)(object)val != (Object)null && (Object)(object)val.parent != (Object)null) { if ((Object)(object)val.parent == (Object)(object)parent) { return val; } val = val.parent; } } Transform parent2 = toggle.parent; if ((Object)(object)parent2 != (Object)null && (Object)(object)parent2.parent != (Object)null && ((Component)parent2).GetComponentsInChildren<Toggle>(true).Length == 1) { return parent2; } return null; } private static Slider FindMenuSlider() { SettingsPanel val = Object.FindFirstObjectByType<SettingsPanel>((FindObjectsInactive)1); if ((Object)(object)val != (Object)null && SettingsUi.Locate(val, out var _, out var _, out var fov)) { return fov; } return null; } } [HarmonyPatch(typeof(HostPanelController), "OnEnable")] internal static class P_HostPanel_OnEnable { private static void Postfix(HostPanelController __instance) { try { HostPanelUi.Ensure(__instance); } catch (Exception e) { Plugin.Fault("HostPanel.OnEnable", e); } } } internal static class Owner { public static bool Shares(int playerIndex, int roomIndex) { if (roomIndex < 0 || playerIndex < 0) { return false; } if (Rooms.ShareAll) { return true; } int count = Rooms.Count; if (RoomMath.NeedsMapping(playerIndex, count)) { return RoomMath.RoomIndex(playerIndex, count) == roomIndex; } return false; } public static int BedSlot(int playerIndex, int bedIndex) { if (Rooms.ShareAll) { return RoomMath.BedSlot(playerIndex, bedIndex); } int count = Rooms.Count; if (!RoomMath.NeedsMapping(playerIndex, count)) { return 0; } return RoomMath.Occupant(playerIndex, count); } public static PlayerController ControllerOf(BadgeReader reader) { if ((Object)(object)reader == (Object)null || !reader.HasBadgeAuthoritative) { return null; } Player signedInPlayer = reader.SignedInPlayer; if (!((Object)(object)signedInPlayer != (Object)null)) { return null; } return signedInPlayer.playerController; } } [HarmonyPatch(typeof(QuartersDoor), "ReaderHoldsAssignedPlayer")] internal static class P_Door_Holds { private static readonly FieldRef<QuartersDoor, int> Assigned = AccessTools.FieldRefAccess<QuartersDoor, int>("_assignedPlayerIndex"); private static void Postfix(QuartersDoor __instance, BadgeReader reader, ref bool __result) { try { if (!__result) { PlayerController val = Owner.ControllerOf(reader); if ((Object)(object)val != (Object)null && Owner.Shares(val.index, Assigned.Invoke(__instance))) { __result = true; } } } catch (Exception e) { Plugin.Fault("Door.Holds", e); } } } [HarmonyPatch(typeof(QuartersDoor), "HasUnauthorizedBadge")] internal static class P_Door_Unauthorized { private static readonly FieldRef<QuartersDoor, int> Assigned = AccessTools.FieldRefAccess<QuartersDoor, int>("_assignedPlayerIndex"); private static void Postfix(QuartersDoor __instance, BadgeReader reader, ref bool __result) { try { if (__result) { PlayerController val = Owner.ControllerOf(reader); if ((Object)(object)val != (Object)null && Owner.Shares(val.index, Assigned.Invoke(__instance))) { __result = false; } } } catch (Exception e) { Plugin.Fault("Door.Unauthorized", e); } } } internal static class BedFields { public static readonly FieldRef<Bed, int> PlayerIndex = AccessTools.FieldRefAccess<Bed, int>("_playerIndex"); } [HarmonyPatch(typeof(Bed), "CanInteract")] internal static class P_Bed_CanInteract { private static void Postfix(Bed __instance, PlayerItemHolder holder, ref bool __result) { try { if (__result || (Object)(object)holder == (Object)null) { return; } int num = BedFields.PlayerIndex.Invoke(__instance); if (num >= 0 && !((Object)(object)BreakManager.instance == (Object)null) && BreakManager.instance.IsSleepPhase) { Player player = holder.Player; if (!((Object)(object)player == (Object)null) && !((Object)(object)player.playerController == (Object)null) && !player.IsSleepingInBed && Owner.Shares(player.playerController.index, num)) { __result = true; } } } catch (Exception e) { Plugin.Fault("Bed.CanInteract", e); } } } [HarmonyPatch(typeof(Bed), "Interact")] internal static class P_Bed_Interact { private static bool Prefix(Bed __instance, PlayerItemHolder holder) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)holder == (Object)null || (Object)(object)__instance.SleepPoint == (Object)null) { return true; } Player player = holder.Player; if ((Object)(object)player == (Object)null || (Object)(object)player.playerController == (Object)null) { return true; } int num = Owner.BedSlot(player.playerController.index, BedFields.PlayerIndex.Invoke(__instance)); if (num <= 0) { return true; } Vector3 val = Rooms.OffsetPosition(__instance.SleepPoint, num); player.RequestSleepInBed(val); return false; } catch (Exception e) { Plugin.Fault("Bed.Interact", e); return true; } } } [HarmonyPatch(typeof(Bed), "GetForPlayer")] internal static class P_Bed_GetForPlayer { private static void Postfix(int playerIndex, ref Bed __result) { try { if (!((Object)(object)__result != (Object)null)) { int count = Rooms.Count; if (RoomMath.NeedsMapping(playerIndex, count)) { __result = Bed.GetForPlayer(RoomMath.RoomIndex(playerIndex, count)); } } } catch (Exception e) { Plugin.Fault("Bed.GetForPlayer", e); } } } internal static class WardrobeFields { public static readonly FieldRef<Wardrobe, int> PlayerIndex = AccessTools.FieldRefAccess<Wardrobe, int>("_playerIndex"); public const int NoSwap = int.MinValue; public static int SwapFor(Wardrobe w, PlayerController pc) { if ((Object)(object)pc == (Object)null) { return int.MinValue; } int num = PlayerIndex.Invoke(w); if (!Owner.Shares(pc.index, num)) { return int.MinValue; } PlayerIndex.Invoke(w) = pc.index; return num; } public static void Restore(Wardrobe w, int state) { try { if (state != int.MinValue) { PlayerIndex.Invoke(w) = state; } } catch (Exception e) { Plugin.Fault("Wardrobe.Restore", e); } } } [HarmonyPatch(typeof(Wardrobe), "IsAssignedPlayer")] internal static class P_Wardrobe_IsAssigned { private static void Postfix(Wardrobe __instance, PlayerItemHolder holder, ref bool __result) { try { if (!__result && !((Object)(object)holder == (Object)null)) { Player player = holder.Player; if (!((Object)(object)player == (Object)null) && !((Object)(object)player.playerController == (Object)null) && Owner.Shares(player.playerController.index, WardrobeFields.PlayerIndex.Invoke(__instance))) { __result = true; } } } catch (Exception e) { Plugin.Fault("Wardrobe.IsAssigned", e); } } } [HarmonyPatch(typeof(Wardrobe), "ServerCycle")] internal static class P_Wardrobe_ServerCycle { private static void Prefix(Wardrobe __instance, Player presser, ref int __state) { __state = int.MinValue; try { __state = WardrobeFields.SwapFor(__instance, ((Object)(object)presser != (Object)null) ? presser.playerController : null); } catch (Exception e) { Plugin.Fault("Wardrobe.ServerCycle", e); } } private static void Finalizer(Wardrobe __instance, int __state) { WardrobeFields.Restore(__instance, __state); } } [HarmonyPatch(typeof(Wardrobe), "UserCode_CmdEquip__NetworkConnectionToClient")] internal static class P_Wardrobe_CmdEquip { private static void Prefix(Wardrobe __instance, NetworkConnectionToClient sender, ref int __state) { __state = int.MinValue; try { PlayerController pc = ((sender != null && (Object)(object)((NetworkConnection)sender).identity != (Object)null) ? ((Component)((NetworkConnection)sender).identity).GetComponent<PlayerController>() : null); __state = WardrobeFields.SwapFor(__instance, pc); } catch (Exception e) { Plugin.Fault("Wardrobe.CmdEquip", e); } } private static void Finalizer(Wardrobe __instance, int __state) { WardrobeFields.Restore(__instance, __state); } } public static class Rooms { private static readonly FieldRef<List<QuartersRoom>> AllRef = AccessTools.StaticFieldRefAccess<List<QuartersRoom>>(AccessTools.Field(typeof(QuartersRoom), "_all")); private static int _cachedFrame = -1; private static int _cachedCount; public static int Count { get { if (_cachedFrame == Time.frameCount) { return _cachedCount; } _cachedFrame = Time.frameCount; _cachedCount = Compute(); return _cachedCount; } } public static bool ShareAll { get { if (ModConfig.ShareAllRooms != null) { return ModConfig.ShareAllRooms.Value; } return false; } } public static void Invalidate() { _cachedFrame = -1; } private static int Compute() { List<QuartersRoom> list = AllRef.Invoke(); if (list == null || list.Count == 0) { return 0; } int num = -1; for (int i = 0; i < list.Count; i++) { QuartersRoom val = list[i]; if ((Object)(object)val != (Object)null && val.PlayerIndex > num) { num = val.PlayerIndex; } } return num + 1; } public static Vector3 OffsetPosition(Transform anchor, int occupant) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) float step = ((ModConfig.SpawnOffsetMeters != null) ? ModConfig.SpawnOffsetMeters.Value : 0.7f); RoomMath.Offset(occupant, step, out var right, out var forward); return anchor.position + anchor.right * right + anchor.forward * forward; } } internal static class Scaling { public static bool On { get { if (ModConfig.ScaleEvents != null) { return ModConfig.ScaleEvents.Value; } return false; } } } [HarmonyPatch(typeof(DailyTaskManager), "TargetFor")] internal static class P_Scale_WorkOrder { private static readonly FieldRef<WorkOrderStep, int[]> Table = AccessTools.FieldRefAccess<WorkOrderStep, int[]>("amountByCrew"); private static void Postfix(WorkOrderStep step, ref int __result) { try { if (Scaling.On && step != null && step.HasCrewAmounts) { int[] array = Table.Invoke(step); int playerCount = GameManager.PlayerCount; if (array != null && playerCount > array.Length) { __result = Math.Max(1, RoomMath.Extrapolate(array, playerCount)); } } } catch (Exception e) { Plugin.Fault("Scale.WorkOrder", e); } } } [HarmonyPatch(typeof(CandyDropEvent), "CountForCrew")] internal static class P_Scale_Candy { private static readonly FieldRef<CandyDropEvent, int[]> Table = AccessTools.FieldRefAccess<CandyDropEvent, int[]>("countByCrew"); private static void Postfix(CandyDropEvent __instance, int crew, ref int __result) { try { if (Scaling.On) { int[] array = Table.Invoke(__instance); if (array != null && crew > array.Length) { __result = Math.Max(0, RoomMath.Extrapolate(array, crew)); } } } catch (Exception e) { Plugin.Fault("Scale.Candy", e); } } } [HarmonyPatch(typeof(PowerCutEvent), "ServerDispenseLanterns")] internal static class P_Scale_Lanterns { private static readonly FieldRef<PowerCutEvent, int> Max = AccessTools.FieldRefAccess<PowerCutEvent, int>("maxLanterns"); private static void Prefix(PowerCutEvent __instance) { try { if (Scaling.On && Max.Invoke(__instance) < ModConfig.CapValue) { Max.Invoke(__instance) = ModConfig.CapValue; } } catch (Exception e) { Plugin.Fault("Scale.Lanterns", e); } } } [HarmonyPatch(typeof(HappyHourEvent), "ServerPillCount")] internal static class P_Scale_Pills { private static readonly FieldRef<HappyHourEvent, int> Max = AccessTools.FieldRefAccess<HappyHourEvent, int>("maxPills"); private static readonly FieldRef<HappyHourEvent, int> PerPlayer = AccessTools.FieldRefAccess<HappyHourEvent, int>("pillsPerPlayer"); private static void Prefix(HappyHourEvent __instance) { try { if (Scaling.On) { int num = ModConfig.CapValue * Math.Max(1, PerPlayer.Invoke(__instance)); if (Max.Invoke(__instance) < num) { Max.Invoke(__instance) = num; } } } catch (Exception e) { Plugin.Fault("Scale.Pills", e); } } } [HarmonyPatch(typeof(BalanceDisplayApp), "ServerRebuild")] internal static class P_Scale_BalanceRows { private static readonly FieldRef<BalanceDisplayApp, int> Rows = AccessTools.FieldRefAccess<BalanceDisplayApp, int>("maxRows"); private static void Prefix(BalanceDisplayApp __instance) { try { if (Scaling.On && Rows.Invoke(__instance) > 0 && Rows.Invoke(__instance) < ModConfig.CapValue) { Rows.Invoke(__instance) = ModConfig.CapValue; } } catch (Exception e) { Plugin.Fault("Scale.BalanceRows", e); } } } [HarmonyPatch(typeof(EfficiencyDisplayApp), "ServerRebuild")] internal static class P_Scale_EfficiencyRows { private static readonly FieldRef<EfficiencyDisplayApp, int> Rows = AccessTools.FieldRefAccess<EfficiencyDisplayApp, int>("maxRows"); private static void Prefix(EfficiencyDisplayApp __instance) { try { if (Scaling.On && Rows.Invoke(__instance) > 0 && Rows.Invoke(__instance) < ModConfig.CapValue) { Rows.Invoke(__instance) = ModConfig.CapValue; } } catch (Exception e) { Plugin.Fault("Scale.EfficiencyRows", e); } } } public static class SettingsUi { public struct Tweak { public string Key; public string Label; public ConfigEntry<bool> Entry; } public const string CardName = "LL_LobbiesCard"; private static readonly FieldRef<SettingsPanel, Slider> FovSlider = AccessTools.FieldRefAccess<SettingsPanel, Slider>("fieldOfViewSlider"); public static Tweak[] Tweaks() { return new Tweak[3] { new Tweak { Key = "ShareAllRooms", Label = "SHARE ALL ROOMS", Entry = ModConfig.ShareAllRooms }, new Tweak { Key = "ScaleEvents", Label = "SCALE EVENTS TO CREW", Entry = ModConfig.ScaleEvents }, new Tweak { Key = "ExtendColors", Label = "EXTRA PLAYER COLOURS", Entry = ModConfig.ExtendColors } }; } public static bool Locate(SettingsPanel panel, out Transform generalCard, out Transform generalContent, out Slider fov) { generalCard = null; generalContent = null; fov = null; if ((Object)(object)panel == (Object)null) { return false; } fov = FovSlider.Invoke(panel); if ((Object)(object)fov == (Object)null) { return false; } Transform parent = ((Component)fov).transform.parent; generalContent = (((Object)(object)parent != (Object)null) ? parent.parent : null); generalCard = (((Object)(object)generalContent != (Object)null) ? generalContent.parent : null); if ((Object)(object)generalCard != (Object)null) { return (Object)(object)generalCard.parent != (Object)null; } return false; } public static GameObject Ensure(SettingsPanel panel) { if (!Locate(panel, out var generalCard, out var generalContent, out var _)) { Plugin.Log.LogWarning((object)"[LL] settings: could not locate the General card"); return null; } Transform parent = generalCard.parent; Transform val = parent.Find("LL_LobbiesCard"); if ((Object)(object)val != (Object)null) { Refresh(((Component)val).gameObject); return ((Component)val).gameObject; } Transform val2 = null; for (int i = 0; i < generalContent.childCount; i++) { Transform child = generalContent.GetChild(i); if ((Object)(object)((Component)child).GetComponentInChildren<Toggle>(true) != (Object)null && (Object)(object)((Component)child).GetComponentInChildren<TMP_Dropdown>(true) == (Object)null && (Object)(object)((Component)child).GetComponentInChildren<Slider>(true) == (Object)null) { val2 = child; break; } } if ((Object)(object)val2 == (Object)null) { Plugin.Log.LogWarning((object)"[LL] settings: no toggle row to clone"); return null; } GameObject val3 = UiKit.CloneDetached(((Component)generalCard).gameObject, parent, "LL_LobbiesCard"); try { Build(val3, generalCard, val2); } catch (Exception) { Object.DestroyImmediate((Object)(object)val3); throw; } val3.SetActive(true); Plugin.Log.LogInfo((object)string.Format("[LL] settings: LARGER LOBBIES card added with {0} rows", val3.transform.Find("Content").childCount)); return val3; } private static void Build(GameObject card, Transform generalCard, Transform toggleTemplate) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0067: 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_007d: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Expected O, but got Unknown //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_0288: Unknown result type (might be due to invalid IL or missing references) //IL_02e1: Unknown result type (might be due to invalid IL or missing references) //IL_02eb: Unknown result type (might be due to invalid IL or missing references) card.transform.SetSiblingIndex(generalCard.GetSiblingIndex() + 1); RectTransform obj = UiKit.Rect(card); RectTransform val = (RectTransform)(object)((generalCard is RectTransform) ? generalCard : null); obj.anchorMin = new Vector2(0f, 0f); obj.anchorMax = new Vector2(val.anchorMax.x, val.anchorMin.y); obj.offsetMin = new Vector2(val.offsetMin.x, 155f); obj.offsetMax = new Vector2(val.offsetMax.x, val.offsetMin.y - 12f); Transform val2 = card.transform.Find("Content"); if ((Object)(object)val2 == (Object)null) { throw new InvalidOperationException("cloned card has no Content"); } for (int num = val2.childCount - 1; num >= 0; num--) { Object.DestroyImmediate((Object)(object)((Component)val2.GetChild(num)).gameObject); } Transform obj2 = ((val2 is RectTransform) ? val2 : null); ((RectTransform)obj2).anchoredPosition = new Vector2(0f, -14f); ((RectTransform)obj2).sizeDelta = new Vector2(((RectTransform)obj2).sizeDelta.x, -60f); VerticalLayoutGroup component = ((Component)val2).GetComponent<VerticalLayoutGroup>(); if ((Object)(object)component != (Object)null) { ((HorizontalOrVerticalLayoutGroup)component).spacing = 2f; ((LayoutGroup)component).padding = new RectOffset(((LayoutGroup)component).padding.left, ((LayoutGroup)component).padding.right, 0, 0); ((HorizontalOrVerticalLayoutGroup)component).childForceExpandHeight = false; ((LayoutGroup)component).childAlignment = (TextAnchor)1; } for (int i = 0; i < card.transform.childCount; i++) { TMP_Text component2 = ((Component)card.transform.GetChild(i)).GetComponent<TMP_Text>(); if (!((Object)(object)component2 == (Object)null)) { component2.text = "LARGER LOBBIES"; component2.fontSize *= 0.8f; RectTransform rectTransform = component2.rectTransform; rectTransform.anchoredPosition = new Vector2(rectTransform.anchoredPosition.x, -26f); break; } } Tweak[] array = Tweaks(); for (int j = 0; j < array.Length; j++) { Tweak tw = array[j]; ConfigEntry<bool> entry = tw.Entry; GameObject val3 = UiKit.CloneDetached(((Component)toggleTemplate).gameObject, val2, "LL_Row_" + tw.Key); UiKit.SetText(val3, tw.Label); TMP_Text val4 = UiKit.FirstText(val3); if ((Object)(object)val4 != (Object)null) { val4.fontSize *= 0.8f; } RectTransform obj3 = UiKit.Rect(val3); obj3.sizeDelta = new Vector2(obj3.sizeDelta.x, 42f); LayoutElement val5 = val3.GetComponent<LayoutElement>(); if ((Object)(object)val5 == (Object)null) { val5 = val3.AddComponent<LayoutElement>(); } val5.preferredHeight = 42f; val5.minHeight = 42f; Toggle componentInChildren = val3.GetComponentInChildren<Toggle>(true); if ((Object)(object)componentInChildren != (Object)null) { ((Component)componentInChildren).transform.localScale = Vector3.one * 0.8f; } UiKit.BindToggle(val3, entry != null && entry.Value, "Settings." + tw.Key, delegate(bool v) { if (entry != null) { entry.Value = v; } Plugin.Log.LogInfo((object)$"[LL] settings: {tw.Key}={v}"); }); val3.SetActive(true); } } public static void Refresh(GameObject card) { Tweak[] array = Tweaks(); for (int i = 0; i < array.Length; i++) { Tweak tweak = array[i]; Transform val = card.transform.Find("Content/LL_Row_" + tweak.Key); Toggle val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponentInChildren<Toggle>(true) : null); if ((Object)(object)val2 != (Object)null && tweak.Entry != null) { val2.SetIsOnWithoutNotify(tweak.Entry.Value); } } } public static List<string> Verify(GameObject card) { List<string> list = new List<string>(); if ((Object)(object)card == (Object)null) { list.Add("no card"); return list; } Tweak[] array = Tweaks(); for (int i = 0; i < array.Length; i++) { Tweak tweak = array[i]; Transform val = card.transform.Find("Content/LL_Row_" + tweak.Key); Toggle val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponentInChildren<Toggle>(true) : null); if ((Object)(object)val == (Object)null) { list.Add(tweak.Key + ": no row"); } else if ((Object)(object)val2 == (Object)null) { list.Add(tweak.Key + ": no toggle"); } else if ((Object)(object)((Component)val).GetComponentInChildren<TMP_Dropdown>(true) != (Object)null) { list.Add(tweak.Key + ": cloned a dropdown row"); } else if (tweak.Entry != null && val2.isOn != tweak.Entry.Value) { list.Add(tweak.Key + ": toggle != config"); } else if (UiKit.GetText(((Component)val).gameObject) != tweak.Label) { list.Add(tweak.Key + ": label"); } } return list; } } [HarmonyPatch(typeof(SettingsPanel), "OnEnable")] internal static class P_Settings_OnEnable { private static void Postfix(SettingsPanel __instance) { try { SettingsUi.Ensure(__instance); } catch (Exception e) { Plugin.Fault("Settings.OnEnable", e); } } } [HarmonyPatch(typeof(GameManager), "GetSpawnPoint")] internal static class P_Spawn_Point { private static bool Prefix(int playerIndex, ref (Vector3, Quaternion) __result) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: 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_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) try { int count = Rooms.Count; if (!RoomMath.NeedsMapping(playerIndex, count)) { return true; } if ((Object)(object)QuartersRoom.Get(playerIndex) != (Object)null) { return true; } QuartersRoom val = QuartersRoom.Get(RoomMath.RoomIndex(playerIndex, count)); if ((Object)(object)val == (Object)null || (Object)(object)val.SpawnPoint == (Object)null) { return true; } Transform spawnPoint = val.SpawnPoint; Vector3 val2 = Rooms.OffsetPosition(spawnPoint, RoomMath.Occupant(playerIndex, count)); __result = (val2, spawnPoint.rotation); Plugin.Log.LogInfo((object)$"[LL] spawn: idx={playerIndex} shares room {val.PlayerIndex} at {val2}"); return false; } catch (Exception e) { Plugin.Fault("Spawn.Point", e); return true; } } } public static class SummaryScroll { public const string ViewportName = "LL_RowsViewport"; public const float HeaderInset = 44f; private static readonly FieldRef<ShiftCompleteUI, RectTransform> RowContainer = AccessTools.FieldRefAccess<ShiftCompleteUI, RectTransform>("_rowContainer"); private static readonly FieldRef<ShiftCompleteUI, List<ShiftPlayerRow>> Rows = AccessTools.FieldRefAccess<ShiftCompleteUI, List<ShiftPlayerRow>>("_rows"); private static float MeasureRows(ShiftCompleteUI ui, RectTransform container) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) List<ShiftPlayerRow> list = Rows.Invoke(ui); float num = 0f; int num2 = 0; if (list != null) { foreach (ShiftPlayerRow item in list) { if ((Object)(object)item == (Object)null) { continue; } Transform transform = ((Component)item).transform; RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null); if ((Object)(object)val == (Object)null) { continue; } Rect rect = val.rect; float num3 = ((Rect)(ref rect)).height; if (num3 <= 0f) { num3 = LayoutUtility.GetPreferredHeight(val); } if (num3 <= 0f) { continue; } if (LayoutUtility.GetPreferredHeight(val) < num3 - 0.5f) { LayoutElement val2 = ((Component)val).GetComponent<LayoutElement>(); if ((Object)(object)val2 == (Object)null) { val2 = ((Component)val).gameObject.AddComponent<LayoutElement>(); } val2.minHeight = num3; val2.preferredHeight = num3; } num += num3; num2++; } } HorizontalOrVerticalLayoutGroup component = ((Component)container).GetComponent<HorizontalOrVerticalLayoutGroup>(); if ((Object)(object)component != (Object)null && num2 > 0) { num += component.spacing * (float)(num2 - 1) + (float)((LayoutGroup)component).padding.top + (float)((LayoutGroup)component).padding.bottom; } return num; } public static void OnShow(ShiftCompleteUI ui) { //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) RectTransform val = RowContainer.Invoke(ui); if ((Object)(object)val == (Object)null) { return; } ScrollRect val2 = (((Object)(object)((Transform)val).parent != (Object)null && ((Object)((Transform)val).parent).name == "LL_RowsViewport") ? ((Component)((Transform)val).parent).GetComponent<ScrollRect>() : null); Rect rect; if ((Object)(object)val2 == (Object)null) { LayoutGroup component = ((Component)val).GetComponent<LayoutGroup>(); if (!(component is VerticalLayoutGroup) && !(component is GridLayoutGroup)) { Plugin.Log.LogWarning((object)("[LL] summary: row container layout is " + (((Object)(object)component != (Object)null) ? ((object)component).GetType().Name : "none") + "; not wrapping")); return; } float num = MeasureRows(ui, val); rect = val.rect; float height = ((Rect)(ref rect)).height; if (num <= height + 1f) { Plugin.Log.LogInfo((object)$"[LL] summary: rows fit ({num:F0} of {height:F0} px); no scroll view"); return; } val2 = Wrap(val, height); if ((Object)(object)val2 == (Object)null) { return; } Plugin.Log.LogInfo((object)$"[LL] summary: rows overflow ({num:F0} of {height:F0} px); scroll view added"); } else { float num2 = MeasureRows(ui, val); rect = val2.viewport.rect; bool enabled = (((Behaviour)val2).enabled = num2 > ((Rect)(ref rect)).height + 1f); RectMask2D component2 = ((Component)val2).GetComponent<RectMask2D>(); if ((Object)(object)component2 != (Object)null) { ((Behaviour)component2).enabled = enabled; } } Reset(val2); } private static ScrollRect Wrap(RectTransform container, float availableHeight) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Expected O, but got Unknown //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected O, but got Unknown //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) Transform parent = ((Transform)container).parent; if ((Object)(object)parent == (Object)null) { return null; } int siblingIndex = ((Transform)container).GetSiblingIndex(); float minWidth = LayoutUtility.GetMinWidth(container); float preferredWidth = LayoutUtility.GetPreferredWidth(container); float flexibleWidth = LayoutUtility.GetFlexibleWidth(container); float minHeight = LayoutUtility.GetMinHeight(container); float flexibleHeight = LayoutUtility.GetFlexibleHeight(container); GameObject val = new GameObject("LL_RowsViewport", new Type[4] { typeof(RectTransform), typeof(RectMask2D), typeof(ScrollRect), typeof(SummaryScrollInput) }); RectTransform val2 = (RectTransform)val.transform; ((Transform)val2).SetParent(parent, false); ((Transform)val2).SetSiblingIndex(siblingIndex); val2.anchorMin = container.anchorMin; val2.anchorMax = container.anchorMax; val2.pivot = container.pivot; val2.anchoredPosition = container.anchoredPosition; val2.sizeDelta = container.sizeDelta; ((Transform)val2).localScale = ((Transform)container).localScale; ((Transform)val2).localRotation = ((Transform)container).localRotation; val2.offsetMax = new Vector2(val2.offsetMax.x, val2.offsetMax.y - 44f); if ((Object)(object)((Component)parent).GetComponent<LayoutGroup>() != (Object)null) { LayoutElement obj = val.AddComponent<LayoutElement>(); obj.minWidth = minWidth; obj.preferredWidth = preferredWidth; obj.flexibleWidth = flexibleWidth; obj.minHeight = minHeight; obj.preferredHeight = availableHeight; obj.flexibleHeight = flexibleHeight; } ((Transform)container).SetParent((Transform)(object)val2, false); container.anchorMin = new Vector2(0f, 1f); container.anchorMax = new Vector2(1f, 1f); container.pivot = new Vector2(0.5f, 1f); container.anchoredPosition = Vector2.zero; container.sizeDelta = new Vector2(0f, availableHeight); LayoutElement component = ((Component)container).GetComponent<LayoutElement>(); if ((Object)(object)component != (Object)null) { component.ignoreLayout = false; } ContentSizeFitter val3 = ((Component)container).GetComponent<ContentSizeFitter>(); if ((Object)(object)val3 == (Object)null) { val3 = ((Component)container).gameObject.AddComponent<ContentSizeFitter>(); } val3.verticalFit = (FitMode)2; val3.horizontalFit = (FitMode)0; ScrollRect component2 = val.GetComponent<ScrollRect>(); component2.content = container; component2.viewport = val2; component2.horizontal = false; component2.vertical = true; component2.movementType = (MovementType)2; component2.inertia = false; component2.scrollSensitivity = 40f; return component2; } private static void Reset(ScrollRect scroll) { if (!((Object)(object)scroll == (Object)null) && !((Object)(object)scroll.content == (Object)null)) { LayoutRebuilder.ForceRebuildLayoutImmediate(scroll.content); scroll.StopMovement(); scroll.verticalNormalizedPosition = 1f; } } public static ScrollRect Find(ShiftCompleteUI ui) { RectTransform val = RowContainer.Invoke(ui); if (!((Object)(object)val != (Object)null) || !((Object)(object)((Transform)val).parent != (Object)null)) { return null; } return ((Component)((Transform)val).parent).GetComponent<ScrollRect>(); } public static void Follow(ShiftPlayerRow row) { if ((Object)(object)row == (Object)null) { return; } Transform transform = ((Component)row).transform; RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null); RectTransform val2 = (RectTransform)(((Object)(object)val != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null); ScrollRect val3 = (((Object)(object)val2 != (Object)null && (Object)(object)((Transform)val2).parent != (Object)null) ? ((Component)((Transform)val2).parent).GetComponent<ScrollRect>() : null); if (!((Object)(object)val3 == (Object)null) && ((Behaviour)val3).enabled && !((Object)(object)val3.viewport == (Object)null)) { SummaryScrollInput component = ((Component)val3).GetComponent<SummaryScrollInput>(); if ((Object)(object)component != (Object)null) { component.RevealRow(val); } } } public static float ScrollTo(ShiftCompleteUI ui, float normalized) { ScrollRect val = Find(ui); if ((Object)(object)val == (Object)null) { return -1f; } SummaryScrollInput component = ((Component)val).GetComponent<SummaryScrollInput>(); if ((Object)(object)component != (Object)null) { component.CancelFollow(10f); } val.verticalNormalizedPosition = normalized; return val.verticalNormalizedPosition; } public static string Describe(ShiftCompleteUI ui) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) RectTransform val = RowContainer.Invoke(ui); if ((Object)(object)val == (Object)null) { return "no container"; } ScrollRect val2 = Find(ui); LayoutRebuilder.ForceRebuildLayoutImmediate(val); float preferredHeight = LayoutUtility.GetPreferredHeight(val); Rect rect; float height; if (!((Object)(object)val2 != (Object)null) || !((Object)(object)val2.viewport != (Object)null)) { rect = val.rect; height = ((Rect)(ref rect)).height; } else { rect = val2.viewport.rect; height = ((Rect)(ref rect)).height; } float num = height; int num2 = 0; for (int i = 0; i < ((Transform)val).childCount; i++) { if (((Component)((Transform)val).GetChild(i)).gameObject.activeSelf && (Object)(object)((Component)((Transform)val).GetChild(i)).GetComponent<ShiftPlayerRow>() != (Object)null) { num2++; } } return $"rows={num2} contentH={preferredHeight:F0} viewportH={num:F0} scroll={(Object)(object)val2 != (Object)null && ((Behaviour)val2).enabled} scrollable={(Object)(object)val2 != (Object)null && ((Behaviour)val2).enabled && preferredHeight > num + 1f}"; } } public class SummaryScrollInput : MonoBehaviour { private ScrollRect _scroll; private float _target = -1f; private float _idleUntil; private void Awake() { _scroll = ((Component)this).GetComponent<ScrollRect>(); } public void RevealRow(RectTransform row) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_scroll == (Object)null) && !((Object)(object)_scroll.content == (Object)null) && !((Object)(object)_scroll.viewport == (Object)null) && !(Time.unscaledTime < _idleUntil)) { LayoutRebuilder.ForceRebuildLayoutImmediate(_scroll.content); Rect rect = _scroll.content.rect; float height = ((Rect)(ref rect)).height; rect = _scroll.viewport.rect; float height2 = ((Rect)(ref rect)).height; float num = height - height2; if (!(num <= 1f)) { float num2 = 0f - row.anchoredPosition.y; rect = row.rect; float num3 = num2 - ((Rect)(ref rect)).height * (1f - row.pivot.y); rect = row.rect; float num4 = Mathf.Clamp(num3 + ((Rect)(ref rect)).height - height2 + 8f, 0f, num); _target = 1f - num4 / num; } } } public void CancelFollow(float holdSeconds = 0f) { _target = -1f; if (holdSeconds > 0f) { _idleUntil = Time.unscaledTime + holdSeconds; } } private void Update() { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: 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) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_scroll == (Object)null || !((Behaviour)_scroll).enabled || (Object)(object)_scroll.content == (Object)null || (Object)(object)_scroll.viewport == (Object)null) { return; } Rect rect = _scroll.content.rect; float height = ((Rect)(ref rect)).height; rect = _scroll.viewport.rect; float height2 = ((Rect)(ref rect)).height; float num = height - height2; if (num <= 1f) { return; } float num2 = 0f; try { Mouse current = Mouse.current; if (current != null) { num2 -= ((InputControl<Vector2>)(object)current.scroll).ReadValue().y * 0.6f; } Keyboard current2 = Keyboard.current; if (current2 != null) { float num3 = 420f * Time.unscaledDeltaTime; if (((ButtonControl)current2.downArrowKey).isPressed) { num2 += num3; } if (((ButtonControl)current2.upArrowKey).isPressed) { num2 -= num3; } if (((ButtonControl)current2.pageDownKey).wasPressedThisFrame) { num2 += height2 * 0.8f; } if (((ButtonControl)current2.pageUpKey).wasPressedThisFrame) { num2 -= height2 * 0.8f; } } Gamepad current3 = Gamepad.current; if (current3 != null) { float y = ((InputControl<Vector2>)(object)current3.rightStick).ReadValue().y; if (Mathf.Abs(y) > 0.25f) { num2 -= y * 600f * Time.unscaledDeltaTime; } } } catch (Exception e) { Plugin.Fault("Summary.Input", e); ((Behaviour)this).enabled = false; return; } if (Mathf.Abs(num2) > 0.01f) { _target = -1f; _idleUntil = Time.unscaledTime + 2f; _scroll.verticalNormalizedPosition = Mathf.Clamp01(_scroll.verticalNormalizedPosition - num2 / num); } else if (_target >= 0f) { float num4 = Mathf.MoveTowards(_scroll.verticalNormalizedPosition, _target, Time.unscaledDeltaTime * 2.5f); _scroll.verticalNormalizedPosition = num4; if (Mathf.Abs(num4 - _target) < 0.001f) { _target = -1f; } } } } [HarmonyPatch(typeof(ShiftCompleteUI), "Show")] internal static class P_Summary_Show { private static void Postfix(ShiftCompleteUI __instance) { try { SummaryScroll.OnShow(__instance); } catch (Exception e) { Plugin.Fault("Summary.Scroll", e); } } } [HarmonyPatch(typeof(ShiftPlayerRow), "PlayIn")] internal static class P_Summary_RowPlayIn { private static void Postfix(ShiftPlayerRow __instance) { try { SummaryScroll.Follow(__instance); } catch (Exception e) { Plugin.Fault("Summary.Follow", e); } } } public static class UiKit { public static GameObject CloneDetached(GameObject template, Transform parent, string name) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown GameObject val = new GameObject("LL_holder"); val.SetActive(false); val.transform.SetParent(parent, false); GameObject obj = Object.Instantiate<GameObject>(template, val.transform); obj.SetActive(false); ((Object)obj).name = name; Strip(obj); obj.transform.SetParent(parent, false); Object.DestroyImmediate((Object)(object)val); return obj; } public static void Strip(GameObject go) { LocalizedText[] componentsInChildren = go.GetComponentsInChildren<LocalizedText>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren[i]); } HoverHint[] componentsInChildren2 = go.GetComponentsInChildren<HoverHint>(true); for (int i = 0; i < componentsInChildren2.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren2[i]); } } public static TMP_Text FirstText(GameObject go) { if (!((Object)(object)go != (Object)null)) { return null; } return go.GetComponentInChildren<TMP_Text>(true); } public static void SetText(GameObject go, string text) { TMP_Text val = FirstText(go); if ((Object)(object)val != (Object)null) { val.text = text; } } public static string GetText(GameObject go) { TMP_Text val = FirstText(go); if (!((Object)(object)val != (Object)null)) { return null; } return val.text; } public static Toggle BindToggle(GameObject row, bool initial, string site, Action<bool> onChanged) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown Toggle componentInChildren = row.GetComponentInChildren<Toggle>(true); if ((Object)(object)componentInChildren == (Object)null) { return null; } componentInChildren.group = null; componentInChildren.onValueChanged = new ToggleEvent(); componentInChildren.SetIsOnWithoutNotify(initial); ((UnityEvent<bool>)(object)componentInChildren.onValueChanged).AddListener((UnityAction<bool>)delegate(bool v) { try { onChanged(v); } catch (Exception e) { Plugin.Fault(site, e); } }); return componentInChildren; } public static Slider BindSlider(GameObject sliderObject, float min, float max, bool whole, float initial, string site, Action<float> onChanged) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown Slider componentInChildren = sliderObject.GetComponentInChildren<Slider>(true); if ((Object)(object)componentInChildren == (Object)null) { return null; } componentInChildren.onValueChanged = new SliderEvent(); componentInChildren.minValue = min; componentInChildren.maxValue = max; componentInChildren.wholeNumbers = whole; componentInChildren.SetValueWithoutNotify(initial); ((UnityEvent<float>)(object)componentInChildren.onValueChanged).AddListener((UnityAction<float>)delegate(float v) { try { onChanged(v); } catch (Exception e) { Plugin.Fault(site, e); } }); return componentInChildren; } public static RectTransform Rect(GameObject go) { if (!((Object)(object)go != (Object)null)) { return null; } Transform transform = go.transform; return (RectTransform)(object)((transform is RectTransform) ? transform : null); } public static void Place(RectTransform rt, Vector2 anchorMin, Vector2 anchorMax, Vector2 pivot, Vector2 anchoredPos, Vector2 sizeDelta) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000f: 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) //IL_001e: Unknown result type (might be due to invalid IL or missing references) rt.anchorMin = anchorMin; rt.anchorMax = anchorMax; rt.pivot = pivot; rt.anchoredPosition = anchoredPos; rt.sizeDelta = sizeDelta; } public static string Describe(Transform t, int maxDepth = 4) { StringBuilder stringBuilder = new StringBuilder(); Walk(t, 0, maxDepth, stringBuilder); if ((Object)(object)t.parent != (Object)null) { stringBuilder.Append(" container=").Append(((Object)t.parent).name).Append(" layout=") .Append((Object)(object)((Component)t.parent).GetComponent<LayoutGroup>() != (Object)null) .Append(" children=") .Append(t.parent.childCount); } return stringBuilder.ToString(); } private static void Walk(Transform t, int depth, int maxDepth, StringBuilder sb) { //IL_009c: 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_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) sb.Append(' ', depth * 2).Append(((Object)t).name).Append(" ["); Component[] components = ((Component)t).GetComponents<Component>(); foreach (Component val in components) { if ((Object)(object)val != (Object)null && !(val is Transform) && !(val is CanvasRenderer)) { sb.Append(((object)val).GetType().Name).Append(' '); } } sb.Append(']'); RectTransform val2 = (RectTransform)(object)((t is RectTransform) ? t : null); if ((Object)(object)val2 != (Object)null) { object[] obj = new object[7] { val2.anchoredPosition, val2.sizeDelta, null, null, null, null, null }; Rect rect = val2.rect; obj[2] = ((Rect)(ref rect)).width; rect = val2.rect; obj[3] = ((Rect)(ref rect)).height; obj[4] = val2.anchorMin; obj[5] = val2.anchorMax; obj[6] = val2.pivot; sb.Append(string.Format(" pos={0} size={1} rect={2:F0}x{3:F0} aMin={4} aMax={5} pivot={6}", obj)); } TMP_Text component = ((Component)t).GetComponent<TMP_Text>(); if ((Object)(object)component != (Object)null) { sb.Append(" text=\"").Append(component.text).Append('"'); } if (!((Component)t).gameObject.activeSelf) { sb.Append(" INACTIVE"); } sb.Append(" | "); for (int j = 0; j < t.childCount; j++) { if (depth >= maxDepth) { break; } Walk(t.GetChild(j), depth + 1, maxDepth, sb); } } } internal static class Palette { public static bool On { get { if (ModConfig.ExtendColors != null) { return ModConfig.ExtendColors.Value; } return false; } } public static Color[] Extend(Color[] original, int size) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) int num = ((original != null) ? original.Length : 0); if (num >= size) { return original; } Color[] array = (Color[])(object)new Color[size]; for (int i = 0; i < num; i++) { array[i] = original[i]; } for (int j = num; j < size; j++) { array[j] = Extra(j - num); } return array; } public static Color Extra(int k) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) return Color.HSVToRGB(RoomMath.ExtraHue(k), 0.78f, 0.95f); } } [HarmonyPatch(typeof(PlayerBlips), "ColorForIndex")] internal static class P_Ui_BlipColor { private static readonly FieldRef<PlayerBlips, Color[]> Colors = AccessTools.FieldRefAccess<PlayerBlips, Color[]>("playerColors"); private static void Postfix(PlayerBlips __instance, int i, ref Color __result) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) try { if (Palette.On && i >= 0) { Color[] array = Colors.Invoke(__instance); if (array != null && array.Length != 0 && i >= array.Length) { __result = Palette.Extra(i - array.Length); } } } catch (Exception e) { Plugin.Fault("Ui.BlipColor", e); } } } [HarmonyPatch(typeof(ShiftCompleteUI), "Awake")] internal static class P_Ui_SummaryColors { private static readonly FieldRef<ShiftCompleteUI, Color[]> Colors = AccessTools.FieldRefAccess<ShiftCompleteUI, Color[]>("_playerColors"); private static void Postfix(ShiftCompleteUI __instance) { try { if (Palette.On) { Colors.Invoke(__instance) = Palette.Extend(Colors.Invoke(__instance), ModConfig.CapValue); } } catch (Exception e) { Plugin.Fault("Ui.SummaryColors", e); } } } } namespace CrackLargerLobbies.Config { public static class ModConfig { public const int MinCap = 4; public const int MaxCap = 16; public static ConfigEntry<int> Cap; public static ConfigEntry<bool> ShareAllRooms; public static ConfigEntry<float> SpawnOffsetMeters;