using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using FishNet; using FishNet.Object.Synchronizing; using FishNet.Transporting; using HarmonyLib; using Microsoft.CodeAnalysis; using Steamworks; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.SceneManagement; using UnityEngine.UI; using moreStrafts.Patches; [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("moreStrafts")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0+34e216bd24532845f866566dd97ba804fddaf2b1")] [assembly: AssemblyProduct("moreStrafts")] [assembly: AssemblyTitle("moreStrafts")] [assembly: AssemblyVersion("0.1.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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 moreStrafts { public static class Log { private static ManualLogSource? source; private static StreamWriter? writer; private static readonly object gate = new object(); public static void Init(ManualLogSource logSource, bool perInstanceFile) { source = logSource; if (!perInstanceFile) { return; } try { string text = Path.Combine(Paths.BepInExRootPath, "moreStrafts_debug"); Directory.CreateDirectory(text); writer = new StreamWriter(Path.Combine(text, string.Format(arg1: Process.GetCurrentProcess().Id, format: "moreStrafts_{0:yyyyMMdd_HHmmss}_pid{1}.log", arg0: DateTime.Now)), append: false) { AutoFlush = true }; } catch (Exception ex) { logSource.LogWarning((object)("per-instance log unavailable: " + ex.Message)); } } public static void Info(string message) { Write("INFO", message); } public static void Warn(string message) { Write("WARN", message); } public static void Error(string message) { Write("ERROR", message); } private static void Write(string level, string message) { lock (gate) { try { writer?.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] [{level}] {message}"); } catch { } } if (!(level == "ERROR")) { if (level == "WARN") { ManualLogSource? obj2 = source; if (obj2 != null) { obj2.LogWarning((object)message); } } else { ManualLogSource? obj3 = source; if (obj3 != null) { obj3.LogInfo((object)message); } } } else { ManualLogSource? obj4 = source; if (obj4 != null) { obj4.LogError((object)message); } } } } public static class ModState { public const int MaxPlayers = 10; private static bool quickLoad; public static bool QuickLoad { get { return quickLoad; } set { if (value != quickLoad) { Log.Info($"QuickLoad {quickLoad} -> {value}"); } quickLoad = value; } } } [BepInPlugin("com.nitrogenia.morestrafts", "More Strafts Players", "0.1.0")] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "com.nitrogenia.morestrafts"; public const string PluginName = "More Strafts Players"; public const string PluginVersion = "0.1.0"; private static readonly Type[] PatchClasses = new Type[16] { typeof(SteamLobbyStartPatch), typeof(OnLobbyCreatedPatch), typeof(SetMaxPlayersPatch), typeof(UpdateOnClientsPatch), typeof(QuickLoadLobbyDataPatch), typeof(QuickLoadTogglePatch), typeof(QuickLoadInterfacePatch), typeof(PlayerListItemUpdatePatch), typeof(TeamDropdownPatch), typeof(RunIntoLobbyPatch), typeof(MenuAnimationObserversPatch), typeof(DressAboubiObserversPatch), typeof(RoundEndArraysPatch), typeof(GamemodeFfaPatch), typeof(StartGameFfaPatch), typeof(MatchPointsHudPatch) }; private void Awake() { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown ConfigEntry val = ((BaseUnityPlugin)this).Config.Bind("Debug", "PerInstanceLogFiles", false, "Write a small per-instance log file to BepInEx/moreStrafts_debug. Include these files in bug reports."); Log.Init(((BaseUnityPlugin)this).Logger, val.Value); SceneManager.sceneLoaded += delegate(Scene scene, LoadSceneMode mode) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) Log.Info($"scene '{((Scene)(ref scene)).name}' ({mode})"); }; Harmony val2 = new Harmony("com.nitrogenia.morestrafts"); int num = 0; Type[] patchClasses = PatchClasses; foreach (Type type in patchClasses) { try { val2.CreateClassProcessor(type).Patch(); num++; } catch (Exception arg) { Log.Error($"patch {type.Name} failed: {arg}"); } } Log.Info(string.Format("{0} {1}: {2}/{3} patches applied", "More Strafts Players", "0.1.0", num, PatchClasses.Length)); } } } namespace moreStrafts.Patches { [HarmonyPatch(typeof(MatchPoitnsHUD), "UpdateVisuals", new Type[] { typeof(int), typeof(Dictionary) })] internal static class MatchPointsHudPatch { private static readonly FieldRef SecondaryObjects = AccessTools.FieldRefAccess("secondaryPointObjects"); private static readonly FieldRef PointsTexts = AccessTools.FieldRefAccess("pointsTexts"); private static readonly FieldRef PrimaryMesh = AccessTools.FieldRefAccess("primaryPointMesh"); private static readonly FieldRef ActiveMaterials = AccessTools.FieldRefAccess("activeMaterials"); private static readonly FieldRef InactiveMaterial = AccessTools.FieldRefAccess("inactiveMaterial"); private static bool warned; private static bool Prefix(MatchPoitnsHUD __instance, int winnerTeamId, Dictionary roundScores) { try { if (ClientInstance.playerInstances.Count <= 1) { return false; } MeshRenderer[] array = SecondaryObjects.Invoke(__instance); TMP_Text[] array2 = PointsTexts.Invoke(__instance); MeshRenderer val = PrimaryMesh.Invoke(__instance); int[] array3 = ScoreManager.Instance.TeamIdToPlayerIds.Keys.ToArray(); int num = array3.Count((int teamId) => ((SyncIDictionary>)(object)ScoreManager.Instance.TeamIdToPlayerIds)[teamId].Any((int playerId) => ClientInstance.playerInstances.ContainsKey(playerId))); int num2 = Mathf.Clamp(num - 2, 0, array.Length); for (int num3 = 0; num3 < array.Length; num3++) { ((Component)array[num3]).gameObject.SetActive(num3 < num2); } TMP_Text[] array4 = array2; for (int num4 = 0; num4 < array4.Length; num4++) { array4[num4].text = ""; } Material[] materials = ((Renderer)val).materials; int num5 = Mathf.Min(num, 2 + array.Length); for (int num6 = 0; num6 < num5 && num6 < array3.Length; num6++) { int num7 = array3[num6]; roundScores.TryGetValue(num7, out var value); ApplyTeamVisual(__instance, num6, value, num7 == winnerTeamId, materials); } ((Renderer)val).materials = materials; return false; } catch (Exception arg) { if (!warned) { warned = true; Log.Error($"MatchPointsHudPatch: {arg}"); } return true; } } private static void ApplyTeamVisual(MatchPoitnsHUD hud, int teamNumber, int score, bool isWinner, Material[] primaryMaterials) { Material[] array = ActiveMaterials.Invoke(hud); if (teamNumber < 0 || teamNumber >= array.Length) { return; } Material val = ((score > 0) ? array[teamNumber] : InactiveMaterial.Invoke(hud)); if (teamNumber < 2) { primaryMaterials[(teamNumber == 0) ? 1 : 3] = val; } else { MeshRenderer[] array2 = SecondaryObjects.Invoke(hud); int num = teamNumber - 2; if (num < array2.Length) { Material[] materials = ((Renderer)array2[num]).materials; materials[1] = val; ((Renderer)array2[num]).materials = materials; } } TMP_Text[] array3 = PointsTexts.Invoke(hud); if (((score == 2 && !isWinner) || score > 2) && teamNumber < array3.Length) { array3[teamNumber].text = score.ToString(); } if (isWinner) { primaryMaterials[2] = val; } } } internal static class LobbyCapacity { public static void ExpandDropdown(TMP_Dropdown? dropdown) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown if (!((Object)(object)dropdown == (Object)null) && dropdown.options.Count < 9) { int value = dropdown.value; dropdown.options.Clear(); for (int i = 2; i <= 10; i++) { dropdown.options.Add(new OptionData($"{i} Players")); } dropdown.value = value; dropdown.RefreshShownValue(); Log.Info($"max-players dropdown expanded to 2-{10} (value {value})"); } } public static void SetTransportMaxClients(int maxClients) { Transport val = InstanceFinder.TransportManager?.Transport; if (!((Object)(object)val == (Object)null)) { val.SetMaximumClients(maxClients); Log.Info($"transport max clients set to {maxClients}"); } } } [HarmonyPatch(typeof(SteamLobby), "Start")] internal static class SteamLobbyStartPatch { private static void Postfix(SteamLobby __instance) { try { LobbyCapacity.ExpandDropdown(__instance.MaxPlayersDropdown); LobbyCapacity.SetTransportMaxClients(9); } catch (Exception arg) { Log.Error($"SteamLobbyStartPatch: {arg}"); } } } [HarmonyPatch(typeof(SteamLobby), "OnLobbyCreated")] internal static class OnLobbyCreatedPatch { private static void Prefix(SteamLobby __instance) { try { LobbyCapacity.ExpandDropdown(__instance.MaxPlayersDropdown); } catch (Exception arg) { Log.Error($"OnLobbyCreatedPatch prefix: {arg}"); } } private static void Postfix(SteamLobby __instance, LobbyCreated_t callback) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) try { if ((int)callback.m_eResult == 1) { SteamMatchmaking.SetLobbyData(new CSteamID(callback.m_ulSteamIDLobby), "QuickLoad", "0"); ModState.QuickLoad = false; LobbyCapacity.SetTransportMaxClients(__instance.maxPlayers - 1); Log.Info($"lobby {callback.m_ulSteamIDLobby} created with maxPlayers={__instance.maxPlayers}"); } } catch (Exception arg) { Log.Error($"OnLobbyCreatedPatch postfix: {arg}"); } } } [HarmonyPatch(typeof(SteamLobby), "SetMaxPlayers")] internal static class SetMaxPlayersPatch { private static void Postfix(SteamLobby __instance) { try { LobbyCapacity.SetTransportMaxClients(__instance.maxPlayers - 1); } catch (Exception arg) { Log.Error($"SetMaxPlayersPatch: {arg}"); } } } [HarmonyPatch(typeof(ClientInstance), "RpcLogic___UpdateOnClients_3316948804")] internal static class UpdateOnClientsPatch { private static void Prefix(ref int maxPlayers) { maxPlayers = Mathf.Clamp(maxPlayers, 2, 10); } } internal static class PreviewSlots { public static bool Exists(int id) { LobbyController instance = LobbyController.Instance; int num; if ((Object)(object)instance != (Object)null && id >= 0 && id < instance.previews.Length) { num = (((Object)(object)instance.previews[id] != (Object)null) ? 1 : 0); if (num != 0) { goto IL_0047; } } else { num = 0; } Log.Info($"player {id} has no lobby preview slot, skipping preview RPC"); goto IL_0047; IL_0047: return (byte)num != 0; } } [HarmonyPatch(typeof(ClientInstance), "RpcLogic___RunIntoLobby_3316948804")] internal static class RunIntoLobbyPatch { private static bool Prefix(int id) { return PreviewSlots.Exists(id); } } [HarmonyPatch(typeof(ClientInstance), "RpcLogic___MenuAnimationObservers_1692629761")] internal static class MenuAnimationObserversPatch { private static bool Prefix(int id) { return PreviewSlots.Exists(id); } } [HarmonyPatch(typeof(ClientInstance), "RpcLogic___DressAboubiObservers_2497120398")] internal static class DressAboubiObserversPatch { private static bool Prefix(int id) { return PreviewSlots.Exists(id); } } internal static class GameFields { public static readonly FieldRef CosmeticButton = AccessTools.FieldRefAccess("cosmeticbutton"); public static readonly FieldRef PauseManager = AccessTools.FieldRefAccess("pauseManager"); public static readonly FieldRef LobbyController = AccessTools.FieldRefAccess("lobbyController"); public static readonly FieldRef GameManager = AccessTools.FieldRefAccess("gameManager"); public static readonly FieldRef TeamDropdown = AccessTools.FieldRefAccess("teamIdDropdown"); public static readonly FieldRef LocalSteamId = AccessTools.FieldRefAccess("localSteamId"); public static readonly FieldRef PlayingTeams = AccessTools.FieldRefAccess("playingTeams"); } [HarmonyPatch(typeof(PlayerListItem), "Update")] internal static class PlayerListItemUpdatePatch { private static bool warned; private static bool Prefix(PlayerListItem __instance) { //IL_0041: 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_00bf: Unknown result type (might be due to invalid IL or missing references) try { PauseManager val = GameFields.PauseManager.Invoke(__instance); GameObject val2 = GameFields.CosmeticButton.Invoke(__instance); if ((Object)(object)val2 != (Object)null && (Object)(object)val != (Object)null) { val2.transform.localScale = (val.inMainMenu ? Vector3.one : Vector3.zero); } Transform parent = ((Component)__instance).transform.parent; bool flag = (Object)(object)parent != (Object)null && ((Object)((Component)parent).gameObject).name == "-- TAB SCREEN --"; LobbyController val3 = GameFields.LobbyController.Invoke(__instance); if (__instance.PlayerIdNumber > 0 && (Object)(object)val3 != (Object)null) { Transform[] slots = (flag ? val3.tabclientPosition : val3.clientPosition); ((Component)__instance).transform.position = SlotPosition(slots, __instance.PlayerIdNumber - 1); } GameManager val4 = GameFields.GameManager.Invoke(__instance); TMP_Dropdown val5 = GameFields.TeamDropdown.Invoke(__instance); if ((Object)(object)val4 != (Object)null && (Object)(object)val5 != (Object)null) { ((Component)val5).gameObject.SetActive(GameFields.PlayingTeams.Invoke(val4)); bool num = (Object)(object)InstanceFinder.NetworkManager != (Object)null && InstanceFinder.NetworkManager.IsServer; bool flag2 = GameFields.LocalSteamId.Invoke(__instance) == __instance.PlayerSteamID; if (num || flag2) { ((Selectable)val5).interactable = !flag; } } return false; } catch (Exception arg) { if (!warned) { warned = true; Log.Error($"PlayerListItemUpdatePatch: {arg}"); } return true; } } private static Vector3 SlotPosition(Transform[] slots, int index) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) if (index < slots.Length) { return slots[index].position; } Vector3 position = slots[^1].position; return new Vector3(position.x, position.y - (float)(index - slots.Length + 1) * 0.5f, position.z); } } [HarmonyPatch(typeof(PlayerListItem), "Start")] internal static class TeamDropdownPatch { private static void Postfix(PlayerListItem __instance) { try { if ((Object)(object)SteamLobby.Instance == (Object)null || SteamLobby.Instance.maxPlayers <= 4) { return; } TMP_Dropdown val = GameFields.TeamDropdown.Invoke(__instance); if (!((Object)(object)val == (Object)null)) { int maxPlayers = SteamLobby.Instance.maxPlayers; List list = new List(); for (int i = 1; i <= maxPlayers; i++) { list.Add($"Team {i}"); } val.ClearOptions(); val.AddOptions(list); val.SetValueWithoutNotify(ScoreManager.Instance.GetTeamId(__instance.PlayerIdNumber)); Log.Info($"team dropdown expanded to {maxPlayers} options for player {__instance.PlayerIdNumber}"); } } catch (Exception arg) { Log.Error($"TeamDropdownPatch: {arg}"); } } } [HarmonyPatch(typeof(SteamLobby), "OnGetLobbyData")] internal static class QuickLoadLobbyDataPatch { private static void Postfix(LobbyDataUpdate_t result) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) try { if (!((Object)(object)SteamLobby.Instance == (Object)null) && result.m_ulSteamIDLobby == SteamLobby.Instance.CurrentLobbyID) { ModState.QuickLoad = SteamMatchmaking.GetLobbyData(new CSteamID(result.m_ulSteamIDLobby), "QuickLoad") == "1"; } } catch (Exception arg) { Log.Error($"QuickLoadLobbyDataPatch: {arg}"); } } } [HarmonyPatch(typeof(SteamLobby), "OnLobbyEntered")] internal static class QuickLoadTogglePatch { private static void Postfix(SteamLobby __instance, LobbyEnter_t callback) { //IL_0002: 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_0028: Unknown result type (might be due to invalid IL or missing references) try { CSteamID val = default(CSteamID); ((CSteamID)(ref val))..ctor(callback.m_ulSteamIDLobby); ModState.QuickLoad = SteamMatchmaking.GetLobbyData(val, "QuickLoad") == "1"; CreateToggle(__instance, val); } catch (Exception arg) { Log.Error($"QuickLoadTogglePatch: {arg}"); } } private static void CreateToggle(SteamLobby lobby, CSteamID lobbyId) { //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) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)lobby.enemyOutlineToggle == (Object)null) { return; } Transform parent = ((Component)lobby.enemyOutlineToggle).transform.parent; RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null); if ((Object)(object)val == (Object)null || (Object)(object)((Transform)val).parent == (Object)null) { return; } Transform val2 = ((Transform)val).parent.Find("EnableQuickLoad"); if ((Object)(object)val2 != (Object)null) { Object.Destroy((Object)(object)((Component)val2).gameObject); } RectTransform val3 = Object.Instantiate(val, ((Transform)val).parent); ((Object)val3).name = "EnableQuickLoad"; ((Transform)val3).localScale = Vector3.one; ((Transform)val3).localPosition = ((Transform)val3).localPosition + new Vector3(0f, -60f, 0f); Toggle componentInChildren = ((Component)val3).GetComponentInChildren(); if ((Object)(object)componentInChildren == (Object)null) { Log.Warn("QuickLoad toggle row has no Toggle component"); return; } for (int i = 0; i < ((UnityEventBase)componentInChildren.onValueChanged).GetPersistentEventCount(); i++) { ((UnityEventBase)componentInChildren.onValueChanged).SetPersistentListenerState(i, (UnityEventCallState)0); } ((UnityEventBase)componentInChildren.onValueChanged).RemoveAllListeners(); bool flag = (Object)(object)InstanceFinder.NetworkManager != (Object)null && InstanceFinder.NetworkManager.IsServer; componentInChildren.SetIsOnWithoutNotify(ModState.QuickLoad); ((Selectable)componentInChildren).interactable = flag; if (flag) { ((UnityEvent)(object)componentInChildren.onValueChanged).AddListener((UnityAction)delegate(bool isOn) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) SteamMatchmaking.SetLobbyData(lobbyId, "QuickLoad", isOn ? "1" : "0"); }); } TextMeshProUGUI componentInChildren2 = ((Component)val3).GetComponentInChildren(); if ((Object)(object)componentInChildren2 != (Object)null) { ((TMP_Text)componentInChildren2).text = " Enable Quick Load"; } Log.Info($"QuickLoad toggle created (host={flag}, on={ModState.QuickLoad})"); } } [HarmonyPatch(typeof(RoundManager), "InterfaceSetup")] internal static class QuickLoadInterfacePatch { private static bool Prefix(ref IEnumerator __result) { if (!ModState.QuickLoad) { return true; } __result = LoadingScreenOnly(); return false; } private static IEnumerator LoadingScreenOnly() { if ((Object)(object)SceneMotor.Instance != (Object)null) { SceneMotor.Instance.ShowLoadingScreen(); } yield break; } } [HarmonyPatch(typeof(RoundManager), "NextRoundCall")] internal static class RoundEndArraysPatch { private static IEnumerable Transpiler(IEnumerable instructions) { List list = new List(instructions); int num = 0; for (int i = 0; i + 1 < list.Count; i++) { if (!(list[i].opcode != OpCodes.Ldc_I4_4) && !(list[i + 1].opcode != OpCodes.Newarr)) { Type type = list[i + 1].operand as Type; if (!(type != typeof(string)) || !(type != typeof(int))) { list[i].opcode = OpCodes.Ldc_I4_S; list[i].operand = (sbyte)10; num++; } } } if (num == 2) { Log.Info($"round-end name/score arrays extended to {10}"); } else { Log.Warn($"round-end array pattern changed ({num} matches, expected 2); post-round screen limited to 4 players"); } return list; } } internal static class FfaTeams { public static void AssignUniqueTeams(string context) { try { if (!((Object)(object)InstanceFinder.NetworkManager == (Object)null) && InstanceFinder.NetworkManager.IsServer && !((Object)(object)ScoreManager.Instance == (Object)null)) { int[] array = ClientInstance.playerInstances.Keys.ToArray(); ScoreManager.Instance.ResetTeams(); int[] array2 = array; foreach (int num in array2) { ScoreManager.Instance.SetTeamId(num, num); } Log.Info(context + ": unique FFA team per player [" + string.Join(",", array) + "]"); } } catch (Exception arg) { Log.Error($"FfaTeams({context}): {arg}"); } } } [HarmonyPatch(typeof(SteamLobby), "SetGamemode", new Type[] { typeof(int) })] internal static class GamemodeFfaPatch { private static void Postfix(int value) { if (value == 0) { FfaTeams.AssignUniqueTeams("SetGamemode"); } } } [HarmonyPatch(typeof(GameManager), "StartGame")] internal static class StartGameFfaPatch { private static void Postfix(GameManager __instance) { try { if (!GameFields.PlayingTeams.Invoke(__instance)) { FfaTeams.AssignUniqueTeams("StartGame"); } } catch (Exception arg) { Log.Error($"StartGameFfaPatch: {arg}"); } } } }