Decompiled source of CultOfTheLambMultiplayer v3.0.0

plugins/COTLMP - Enemy Spawn Scaling.dll

Decompiled 4 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ClassLibrary3")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ClassLibrary3")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("b9a6c547-5fae-4c80-847d-8da4b933a5dd")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace COTLMultiplayerEnemySpawnScaling
{
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("com.cotlmp.enemyspawnscaling", "COTL Multiplayer Enemy Spawn Scaling", "0.2.9")]
	public sealed class EnemySpawnScalingPlugin : BaseUnityPlugin
	{
		internal static ManualLogSource Log;

		internal static EnemySpawnScalingPlugin Instance;

		internal static ConfigEntry<bool> DuplicateMiniBosses;

		private Harmony _harmony;

		private bool _heartbeatLogged;

		private void Awake()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			DuplicateMiniBosses = ((BaseUnityPlugin)this).Config.Bind<bool>("Scaling", "DuplicateMiniBosses", false, "If true, mini-boss enemies can be duplicated instead of health-scaled. Can cause instability/softlocks for some encounters.");
			_harmony = new Harmony("com.cotlmp.enemyspawnscaling");
			PatchIfFound(AccessTools.Method(typeof(EnemySpawner), "Create", new Type[3]
			{
				typeof(Vector3),
				typeof(Transform),
				typeof(GameObject)
			}, (Type[])null), null, "EnemySpawner_Create_Postfix", "EnemySpawner.Create(Vector3,Transform,GameObject)");
			PatchIfFound(AccessTools.Method(typeof(EnemySpawner), "CreateWithAndInitInstantiatedEnemy", new Type[3]
			{
				typeof(Vector3),
				typeof(Transform),
				typeof(GameObject)
			}, (Type[])null), null, "EnemySpawner_CreateWithAndInit_Postfix", "EnemySpawner.CreateWithAndInitInstantiatedEnemy(Vector3,Transform,GameObject)");
			PatchIfFound(AccessTools.Method(typeof(EnemySpawner), "InitAndInstantiate", new Type[1] { typeof(GameObject) }, (Type[])null), null, "EnemySpawner_InitAndInstantiate_Postfix", "EnemySpawner.InitAndInstantiate(GameObject)");
			PatchIfFound(AccessTools.Method(typeof(EnemySpawner), "Init", new Type[1] { typeof(GameObject) }, (Type[])null), null, "EnemySpawner_Init_Postfix", "EnemySpawner.Init(GameObject)");
			PatchIfFound(AccessTools.Method(typeof(EnemyRoundsBase), "RoundStarted", new Type[2]
			{
				typeof(int),
				typeof(int)
			}, (Type[])null), "EnemyRoundsBase_RoundStarted_Prefix", null, "EnemyRoundsBase.RoundStarted(int,int)");
			PatchIfFound(AccessTools.Method(typeof(EnemyRoundsBase), "AddEnemyToRound", new Type[1] { typeof(Health) }, (Type[])null), null, "EnemyRoundsBase_AddEnemyToRound_Postfix", "EnemyRoundsBase.AddEnemyToRound(Health)");
			PatchIfFound(AccessTools.Method(typeof(EnemyEncounterChanceEvents), "AssignShieldsAndGroups", new Type[0], (Type[])null), null, "EnemyEncounterChanceEvents_AssignShieldsAndGroups_Postfix", "EnemyEncounterChanceEvents.AssignShieldsAndGroups()");
			PatchIfFound(AccessTools.Method(typeof(RoomLockController), "RoomCompleted", new Type[2]
			{
				typeof(bool),
				typeof(bool)
			}, (Type[])null), "RoomLockController_RoomCompleted_Prefix", "RoomLockController_RoomCompleted_Postfix", "RoomLockController.RoomCompleted(bool,bool)");
			Log.LogInfo((object)"Enemy spawn scaling addon loaded.");
			Log.LogInfo((object)"[SpawnScale] Ready v0.2.9");
			Log.LogInfo((object)("[SpawnScale] Config DuplicateMiniBosses=" + DuplicateMiniBosses.Value));
			Log.LogWarning((object)"[SPAWNSCALE_BOOT_017]");
			Debug.Log((object)"[SPAWNSCALE_BOOT_017]");
		}

		private void Update()
		{
			if (!_heartbeatLogged)
			{
				_heartbeatLogged = true;
				Log.LogWarning((object)"[SPAWNSCALE_HEARTBEAT_017]");
				Debug.Log((object)"[SPAWNSCALE_HEARTBEAT_017]");
			}
		}

		private void PatchIfFound(MethodInfo original, string prefix = null, string postfix = null, string label = null)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Expected O, but got Unknown
			if (original == null)
			{
				Log.LogWarning((object)("[SpawnScale] Skipped patch (missing): " + (label ?? "unknown")));
				return;
			}
			HarmonyMethod val = null;
			HarmonyMethod val2 = null;
			if (!string.IsNullOrEmpty(prefix))
			{
				val = new HarmonyMethod(typeof(EnemySpawnPatches), prefix, (Type[])null);
			}
			if (!string.IsNullOrEmpty(postfix))
			{
				val2 = new HarmonyMethod(typeof(EnemySpawnPatches), postfix, (Type[])null);
			}
			_harmony.Patch((MethodBase)original, val, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Log.LogInfo((object)("[SpawnScale] Patched: " + (label ?? original.Name)));
		}
	}
	internal static class EnemySpawnPatches
	{
		private sealed class SpawnScaleCloneMarker : MonoBehaviour
		{
			public int RoomToken;
		}

		private static bool _spawningScaledCopy = false;

		private static bool _allowRoomCompletePassThrough = false;

		private static bool _deferredRoomCompletionQueued = false;

		private static bool _deferredDoorsDown = true;

		private static float _extraSpawnCarry = 0f;

		private static float _nextSkipLogAt = 0f;

		private static float _nextHookLogAt = 0f;

		private static float _nextRejectLogAt = 0f;

		private static int _roomToken = 0;

		private static readonly HashSet<int> _scaledEncounterInstanceIds = new HashSet<int>();

		private static readonly HashSet<int> _scaledSpawnInstanceIds = new HashSet<int>();

		private static readonly HashSet<int> _scaledBossHealthIds = new HashSet<int>();

		private static readonly HashSet<SpawnScaleCloneMarker> _trackedScaledClones = new HashSet<SpawnScaleCloneMarker>();

		private static readonly List<SpawnScaleCloneMarker> _cloneSweep = new List<SpawnScaleCloneMarker>();

		private static readonly Type DungeonLeaderMechanicsType = AccessTools.TypeByName("DungeonLeaderMechanics");

		private static readonly Type DemonType = AccessTools.TypeByName("Demon");

		private static readonly Type FriendlyEnemyType = AccessTools.TypeByName("FriendlyEnemy");

		public static void EnemyRoundsBase_RoundStarted_Prefix(int round, int totalRounds)
		{
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			bool flag = round <= 1 || _roomToken == 0;
			if (flag)
			{
				_roomToken++;
				_trackedScaledClones.Clear();
				_scaledEncounterInstanceIds.Clear();
				_scaledSpawnInstanceIds.Clear();
				_scaledBossHealthIds.Clear();
			}
			_extraSpawnCarry = 0f;
			_deferredRoomCompletionQueued = false;
			_allowRoomCompletePassThrough = false;
			EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] round start {round}/{totalRounds} token={_roomToken} newRoomToken={flag} (players={PlayerFarming.playersCount}, coop={GetCoopStateForLog()}, loc={PlayerFarming.Location})");
		}

		public static bool RoomLockController_RoomCompleted_Prefix(bool wasCombatRoom, bool doorsDown)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			if (!wasCombatRoom)
			{
				return true;
			}
			if (_allowRoomCompletePassThrough)
			{
				return true;
			}
			if (PlayerFarming.playersCount <= 2)
			{
				return true;
			}
			if (!GameManager.IsDungeon(PlayerFarming.Location))
			{
				return true;
			}
			int num = CountAliveTrackedScaledClones();
			if (num <= 0)
			{
				return true;
			}
			if (!_deferredRoomCompletionQueued && (Object)(object)EnemySpawnScalingPlugin.Instance != (Object)null)
			{
				_deferredRoomCompletionQueued = true;
				_deferredDoorsDown = doorsDown;
				((MonoBehaviour)EnemySpawnScalingPlugin.Instance).StartCoroutine(DeferredRoomCompletionRoutine());
			}
			EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] delaying room completion pendingScaled={num} (players={PlayerFarming.playersCount}, loc={PlayerFarming.Location})");
			return false;
		}

		public static void RoomLockController_RoomCompleted_Postfix(bool wasCombatRoom, bool doorsDown)
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			if (wasCombatRoom)
			{
				_extraSpawnCarry = 0f;
				_deferredRoomCompletionQueued = false;
				_allowRoomCompletePassThrough = false;
				_trackedScaledClones.Clear();
				_scaledEncounterInstanceIds.Clear();
				_scaledBossHealthIds.Clear();
				EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] combat room completed reset carry (doorsDown={doorsDown}, players={PlayerFarming.playersCount}, coop={GetCoopStateForLog()}, loc={PlayerFarming.Location})");
			}
		}

		public static void EnemySpawner_Create_Postfix(Vector3 Position, Transform Parent, GameObject Spawn)
		{
			LogHookSeen("Create");
			Debug.Log((object)"[SPAWNSCALE_HOOK_017] Create");
		}

		public static void EnemySpawner_CreateWithAndInit_Postfix(Vector3 Position, Transform Parent, GameObject Spawn)
		{
			LogHookSeen("CreateWithInit");
			Debug.Log((object)"[SPAWNSCALE_HOOK_017] CreateWithInit");
		}

		public static void EnemySpawner_InitAndInstantiate_Postfix(EnemySpawner __instance, GameObject __0)
		{
			LogHookSeen("InitAndInstantiate");
			Debug.Log((object)"[SPAWNSCALE_HOOK_017] InitAndInstantiate");
		}

		public static void EnemySpawner_Init_Postfix(EnemySpawner __instance, GameObject __0)
		{
			LogHookSeen("Init");
			Debug.Log((object)"[SPAWNSCALE_HOOK_017] Init");
		}

		public static void EnemyRoundsBase_AddEnemyToRound_Postfix(Health e)
		{
			if (!((Object)(object)e == (Object)null))
			{
				LogHookSeen("AddEnemyToRound");
			}
		}

		public static void EnemyEncounterChanceEvents_AssignShieldsAndGroups_Postfix(EnemyEncounterChanceEvents __instance)
		{
			//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0228: Unknown result type (might be due to invalid IL or missing references)
			//IL_022f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0248: 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_00ba: Invalid comparison between Unknown and I4
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			if (!ShouldScaleDungeonSpawns(out var blockedReason))
			{
				LogSkip("encounter_gate=" + blockedReason);
				return;
			}
			int instanceID = ((Object)((Component)__instance).gameObject).GetInstanceID();
			if (!_scaledEncounterInstanceIds.Add(instanceID))
			{
				return;
			}
			UnitObject[] componentsInChildren;
			try
			{
				componentsInChildren = ((Component)__instance).GetComponentsInChildren<UnitObject>(true);
			}
			catch
			{
				return;
			}
			if (componentsInChildren == null || componentsInChildren.Length == 0)
			{
				return;
			}
			List<UnitObject> list = new List<UnitObject>();
			foreach (UnitObject val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null) && ((Component)val).gameObject.activeInHierarchy && !((Object)(object)val.health == (Object)null) && (int)val.health.team == 2 && !((Object)(object)((Component)val).GetComponent<SpawnScaleCloneMarker>() != (Object)null) && !TryScaleBossLikeHealth(((Component)val).gameObject, "RoomEncounter") && !((Object)(object)((Component)val).GetComponent<MinionProtector>() != (Object)null) && !((Object)(object)((Component)val).GetComponent<LineRenderer>() != (Object)null) && !HasComponentInChildren(((Component)val).gameObject, DungeonLeaderMechanicsType) && !HasComponentInChildren(((Component)val).gameObject, DemonType) && !HasComponentInChildren(((Component)val).gameObject, FriendlyEnemyType))
				{
					list.Add(val);
				}
			}
			if (list.Count == 0)
			{
				return;
			}
			int roomExtraCount = GetRoomExtraCount(list.Count);
			if (roomExtraCount <= 0)
			{
				return;
			}
			int num = 0;
			for (int j = 0; j < roomExtraCount; j++)
			{
				UnitObject val2 = list[j % list.Count];
				if (!((Object)(object)val2 == (Object)null) && !((Object)(object)((Component)val2).gameObject == (Object)null))
				{
					try
					{
						GameObject val3 = Object.Instantiate<GameObject>(((Component)val2).gameObject, ((Component)val2).transform.parent);
						((Object)val3).name = ((Object)((Component)val2).gameObject).name + "_SpawnScale";
						SpawnScaleCloneMarker spawnScaleCloneMarker = val3.AddComponent<SpawnScaleCloneMarker>();
						spawnScaleCloneMarker.RoomToken = _roomToken;
						_trackedScaledClones.Add(spawnScaleCloneMarker);
						SanitizeClone(val3);
						_scaledSpawnInstanceIds.Add(((Object)val3).GetInstanceID());
						val3.transform.position = GetNearbySpawnPoint(((Component)val2).transform.position, j);
						val3.SetActive(false);
						EnemySpawner.CreateWithAndInitInstantiatedEnemy(val3.transform.position, val3.transform.parent, val3);
						num++;
					}
					catch (Exception ex)
					{
						EnemySpawnScalingPlugin.Log.LogWarning((object)("[SpawnScale] room clone failed: " + ex.Message));
					}
				}
			}
			EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] room-scale eligible={list.Count} spawnedExtra={num} players={PlayerFarming.playersCount} loc={PlayerFarming.Location}");
		}

		private static void TrySpawnScaledCopies(Vector3 position, Transform parent, GameObject spawnPrefab, string source)
		{
			//IL_0074: 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)
			if (_spawningScaledCopy || (Object)(object)spawnPrefab == (Object)null)
			{
				return;
			}
			int instanceID = ((Object)spawnPrefab).GetInstanceID();
			if (_scaledSpawnInstanceIds.Remove(instanceID))
			{
				LogReject("scaled_copy_source");
			}
			else
			{
				if (TryScaleBossLikeHealth(spawnPrefab, source))
				{
					return;
				}
				if (!ShouldScaleDungeonSpawns(out var blockedReason))
				{
					LogSkip("gate=" + blockedReason);
				}
				else
				{
					if (!IsEligibleEnemyPrefab(spawnPrefab))
					{
						return;
					}
					int extraSpawnsForThisEnemy = GetExtraSpawnsForThisEnemy();
					if (extraSpawnsForThisEnemy <= 0)
					{
						return;
					}
					try
					{
						_spawningScaledCopy = true;
						for (int i = 0; i < extraSpawnsForThisEnemy; i++)
						{
							Vector3 nearbySpawnPoint = GetNearbySpawnPoint(position, i);
							GameObject val = Object.Instantiate<GameObject>(spawnPrefab, parent);
							SpawnScaleCloneMarker spawnScaleCloneMarker = val.AddComponent<SpawnScaleCloneMarker>();
							spawnScaleCloneMarker.RoomToken = _roomToken;
							_trackedScaledClones.Add(spawnScaleCloneMarker);
							_scaledSpawnInstanceIds.Add(((Object)val).GetInstanceID());
							val.SetActive(false);
							EnemySpawner.CreateWithAndInitInstantiatedEnemy(nearbySpawnPoint, parent, val);
						}
						EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] source={source} players={PlayerFarming.playersCount} spawnedExtra={extraSpawnsForThisEnemy} carry={_extraSpawnCarry:0.00}");
					}
					catch (Exception ex)
					{
						EnemySpawnScalingPlugin.Log.LogWarning((object)("[SpawnScale] Failed to spawn scaled enemy copy: " + ex.Message));
					}
					finally
					{
						_spawningScaledCopy = false;
					}
				}
			}
		}

		private static bool ShouldScaleDungeonSpawns(out string blockedReason)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			blockedReason = string.Empty;
			if (PlayerFarming.playersCount <= 2)
			{
				blockedReason = "players_le_2";
				return false;
			}
			if (!GameManager.IsDungeon(PlayerFarming.Location))
			{
				blockedReason = "not_dungeon";
				return false;
			}
			if ((Object)(object)EnemyRoundsBase.Instance != (Object)null && EnemyRoundsBase.Instance.Completed)
			{
				blockedReason = "rounds_completed";
				return false;
			}
			return true;
		}

		private static bool IsEligibleEnemyPrefab(GameObject spawnPrefab)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Invalid comparison between Unknown and I4
			Health component = spawnPrefab.GetComponent<Health>();
			if ((Object)(object)component == (Object)null)
			{
				LogReject("no_health");
				return false;
			}
			if ((int)component.team != 2)
			{
				LogReject("team_" + ((object)Unsafe.As<Team, Team>(ref component.team)/*cast due to .constrained prefix*/).ToString());
				return false;
			}
			if (HasComponentInChildren(spawnPrefab, DungeonLeaderMechanicsType))
			{
				LogReject("dungeon_leader");
				return false;
			}
			if (HasComponentInChildren(spawnPrefab, DemonType))
			{
				LogReject("demon");
				return false;
			}
			if (HasComponentInChildren(spawnPrefab, FriendlyEnemyType))
			{
				LogReject("friendly_enemy");
				return false;
			}
			return true;
		}

		private static bool TryScaleBossLikeHealth(GameObject go, string source)
		{
			if ((Object)(object)go == (Object)null)
			{
				return false;
			}
			if (!IsHealthScaleOnlyEnemy(go))
			{
				return false;
			}
			Health component = go.GetComponent<Health>();
			if ((Object)(object)component == (Object)null)
			{
				return false;
			}
			int instanceID = ((Object)go).GetInstanceID();
			if (!_scaledBossHealthIds.Add(instanceID))
			{
				return true;
			}
			float num = 1f;
			if (PlayerFarming.playersCount >= 4)
			{
				num = 2f;
			}
			else if (PlayerFarming.playersCount == 3)
			{
				num = 1.5f;
			}
			if (num <= 1f)
			{
				return true;
			}
			component.totalHP *= num;
			component.HP *= num;
			EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] boss-health source={source} enemy={GetEnemyName(go)} mult={num:0.00} players={PlayerFarming.playersCount}");
			return true;
		}

		private static bool IsHealthScaleOnlyEnemy(GameObject go)
		{
			//IL_002d: 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)
			if ((Object)(object)go == (Object)null)
			{
				return false;
			}
			if (HasComponentInChildren(go, DungeonLeaderMechanicsType))
			{
				return true;
			}
			UnitObject component = go.GetComponent<UnitObject>();
			if ((Object)(object)component == (Object)null)
			{
				return false;
			}
			string text = ((object)component.EnemyType/*cast due to .constrained prefix*/).ToString();
			if (string.IsNullOrEmpty(text))
			{
				return false;
			}
			if ((text.IndexOf("MiniBoss", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Miniboss", StringComparison.OrdinalIgnoreCase) >= 0) && EnemySpawnScalingPlugin.DuplicateMiniBosses != null && EnemySpawnScalingPlugin.DuplicateMiniBosses.Value)
			{
				return false;
			}
			if (text.IndexOf("Boss", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return true;
			}
			return false;
		}

		private static string GetEnemyName(GameObject go)
		{
			//IL_0023: 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)
			UnitObject val = (((Object)(object)go != (Object)null) ? go.GetComponent<UnitObject>() : null);
			if (!((Object)(object)val != (Object)null))
			{
				return "Unknown";
			}
			return ((object)val.EnemyType/*cast due to .constrained prefix*/).ToString();
		}

		private static void LogSkip(string reason)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			float unscaledTime = Time.unscaledTime;
			if (!(unscaledTime < _nextSkipLogAt))
			{
				_nextSkipLogAt = unscaledTime + 2f;
				EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] skip {reason} (players={PlayerFarming.playersCount}, coop={GetCoopStateForLog()}, loc={PlayerFarming.Location})");
			}
		}

		private static void LogHookSeen(string hook)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			float unscaledTime = Time.unscaledTime;
			if (!(unscaledTime < _nextHookLogAt))
			{
				_nextHookLogAt = unscaledTime + 1.5f;
				EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] hook={hook} seen (players={PlayerFarming.playersCount}, coop={GetCoopStateForLog()}, loc={PlayerFarming.Location})");
			}
		}

		private static void LogReject(string reason)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			float unscaledTime = Time.unscaledTime;
			if (!(unscaledTime < _nextRejectLogAt))
			{
				_nextRejectLogAt = unscaledTime + 2f;
				EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] reject {reason} (players={PlayerFarming.playersCount}, coop={GetCoopStateForLog()}, loc={PlayerFarming.Location})");
			}
		}

		private static string GetCoopStateForLog()
		{
			if ((Object)(object)CoopManager.Instance == (Object)null)
			{
				return "null";
			}
			if (!CoopManager.CoopActive)
			{
				return "inactive";
			}
			return "active";
		}

		private static bool HasComponentInChildren(GameObject go, Type componentType)
		{
			if ((Object)(object)go == (Object)null || componentType == null)
			{
				return false;
			}
			try
			{
				return (Object)(object)go.GetComponentInChildren(componentType, true) != (Object)null;
			}
			catch
			{
				return false;
			}
		}

		private static int GetExtraSpawnsForThisEnemy()
		{
			float num;
			if (PlayerFarming.playersCount >= 4)
			{
				num = 1f;
			}
			else
			{
				if (PlayerFarming.playersCount != 3)
				{
					return 0;
				}
				num = 0.5f;
			}
			_extraSpawnCarry += num;
			int num2 = Mathf.FloorToInt(_extraSpawnCarry + 0.0001f);
			if (num2 > 0)
			{
				_extraSpawnCarry -= num2;
			}
			return Mathf.Clamp(num2, 0, 3);
		}

		private static int GetRoomExtraCount(int baseCount)
		{
			if (baseCount <= 0)
			{
				return 0;
			}
			if (PlayerFarming.playersCount >= 4)
			{
				return Mathf.Clamp(Mathf.Max(4, Mathf.CeilToInt((float)baseCount * 2.25f)) - baseCount, 0, 12);
			}
			if (PlayerFarming.playersCount == 3)
			{
				return Mathf.Clamp(Mathf.Max(3, Mathf.CeilToInt((float)baseCount * 1.75f)) - baseCount, 0, 8);
			}
			return 0;
		}

		private static Vector3 GetNearbySpawnPoint(Vector3 origin, int index)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//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)
			float num = (float)(Time.frameCount * 37 + index * 113) % 360f * ((float)Math.PI / 180f);
			return origin + new Vector3(Mathf.Cos(num) * 0.75f, Mathf.Sin(num) * 0.75f, 0f);
		}

		private static void SanitizeClone(GameObject clone)
		{
			if (!((Object)(object)clone == (Object)null))
			{
				MinionProtector component = clone.GetComponent<MinionProtector>();
				if ((Object)(object)component != (Object)null)
				{
					Object.Destroy((Object)(object)component);
				}
				LineRenderer component2 = clone.GetComponent<LineRenderer>();
				if ((Object)(object)component2 != (Object)null)
				{
					Object.Destroy((Object)(object)component2);
				}
				UnitObject component3 = clone.GetComponent<UnitObject>();
				if ((Object)(object)component3 != (Object)null)
				{
					component3.orderIndicator = null;
					component3.RemoveModifier();
					component3.CanHaveModifier = false;
				}
			}
		}

		private static int CountAliveTrackedScaledClones()
		{
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Invalid comparison between Unknown and I4
			try
			{
				SpawnScaleCloneMarker[] array = Object.FindObjectsOfType<SpawnScaleCloneMarker>(true);
				foreach (SpawnScaleCloneMarker spawnScaleCloneMarker in array)
				{
					if (!((Object)(object)spawnScaleCloneMarker == (Object)null) && !((Object)(object)((Component)spawnScaleCloneMarker).gameObject == (Object)null) && spawnScaleCloneMarker.RoomToken == _roomToken)
					{
						_trackedScaledClones.Add(spawnScaleCloneMarker);
					}
				}
			}
			catch
			{
			}
			_cloneSweep.Clear();
			int num = 0;
			foreach (SpawnScaleCloneMarker trackedScaledClone in _trackedScaledClones)
			{
				if ((Object)(object)trackedScaledClone == (Object)null || (Object)(object)((Component)trackedScaledClone).gameObject == (Object)null)
				{
					_cloneSweep.Add(trackedScaledClone);
					continue;
				}
				if (trackedScaledClone.RoomToken != _roomToken)
				{
					_cloneSweep.Add(trackedScaledClone);
					continue;
				}
				Health component = ((Component)trackedScaledClone).GetComponent<Health>();
				if ((Object)(object)component == (Object)null)
				{
					_cloneSweep.Add(trackedScaledClone);
				}
				else if ((int)component.team != 2 || component.HP <= 0f)
				{
					_cloneSweep.Add(trackedScaledClone);
				}
				else
				{
					num++;
				}
			}
			for (int j = 0; j < _cloneSweep.Count; j++)
			{
				_trackedScaledClones.Remove(_cloneSweep[j]);
			}
			return num;
		}

		private static IEnumerator DeferredRoomCompletionRoutine()
		{
			float deadline = Time.unscaledTime + 25f;
			while (Time.unscaledTime < deadline && CountAliveTrackedScaledClones() > 0)
			{
				yield return (object)new WaitForSecondsRealtime(0.2f);
			}
			int num = CountAliveTrackedScaledClones();
			if (num > 0)
			{
				EnemySpawnScalingPlugin.Log.LogWarning((object)$"[SpawnScale] deferred completion timeout pendingScaled={num}; allowing completion to prevent softlock.");
			}
			_deferredRoomCompletionQueued = false;
			_allowRoomCompletePassThrough = true;
			try
			{
				if (!GameManager.IsDungeon(PlayerFarming.Location))
				{
					EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] deferred completion canceled (left dungeon, loc={PlayerFarming.Location})");
				}
				else
				{
					RoomLockController.RoomCompleted(true, _deferredDoorsDown);
				}
			}
			catch (Exception ex)
			{
				EnemySpawnScalingPlugin.Log.LogWarning((object)("[SpawnScale] deferred completion failed: " + ex.Message));
			}
			finally
			{
				_allowRoomCompletePassThrough = false;
			}
		}
	}
}

plugins/COTLMP - Multiplayer Expansion.dll

Decompiled 4 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Logging;
using DG.Tweening;
using DG.Tweening.Core;
using HarmonyLib;
using Lamb.UI;
using Lamb.UI.DeathScreen;
using Lamb.UI.PauseMenu;
using MMBiomeGeneration;
using MMTools;
using Microsoft.CodeAnalysis;
using Rewired;
using Spine;
using Spine.Unity;
using TMPro;
using Unify;
using Unify.Input;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ClassLibrary1")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ClassLibrary1")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("b05729a9-afdc-413c-b90e-c3a9f2cd0a2c")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace COTLMultiplayerExpansion
{
	[BepInPlugin("com.cotlmp.multiplayerexpansion", "COTL Multiplayer Expansion", "2.20.14")]
	public class Plugin : BaseUnityPlugin
	{
		public static ManualLogSource Log;

		public const int MAX_PLAYERS = 4;

		public static readonly Color[] GoatColors = (Color[])(object)new Color[4]
		{
			Color.white,
			Color.white,
			new Color(0.4f, 0.7f, 1f, 1f),
			new Color(1f, 0.4f, 0.4f, 1f)
		};

		public static readonly Color[] IndicatorColors = (Color[])(object)new Color[4]
		{
			new Color(0.9f, 0.2f, 0.2f, 1f),
			new Color(0.6f, 0.3f, 0.9f, 1f),
			new Color(1f, 0.6f, 0.1f, 1f),
			new Color(0.4f, 0.7f, 1f, 1f)
		};

		private void Awake()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Expected O, but got Unknown
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Expected O, but got Unknown
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Expected O, but got Unknown
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Expected O, but got Unknown
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Expected O, but got Unknown
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Expected O, but got Unknown
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Expected O, but got Unknown
			//IL_0205: Expected O, but got Unknown
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_026f: Unknown result type (might be due to invalid IL or missing references)
			//IL_027a: Expected O, but got Unknown
			//IL_027a: Expected O, but got Unknown
			//IL_02cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ed: Expected O, but got Unknown
			//IL_02ed: Expected O, but got Unknown
			//IL_0335: Unknown result type (might be due to invalid IL or missing references)
			//IL_0343: Expected O, but got Unknown
			//IL_036c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0379: Expected O, but got Unknown
			//IL_03b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c1: Expected O, but got Unknown
			//IL_03e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f7: Expected O, but got Unknown
			//IL_0420: Unknown result type (might be due to invalid IL or missing references)
			//IL_042d: Expected O, but got Unknown
			//IL_0456: Unknown result type (might be due to invalid IL or missing references)
			//IL_0463: Expected O, but got Unknown
			//IL_048c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0499: Expected O, but got Unknown
			//IL_04c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_04cf: Expected O, but got Unknown
			//IL_04f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0505: Expected O, but got Unknown
			//IL_053f: Unknown result type (might be due to invalid IL or missing references)
			//IL_054d: Expected O, but got Unknown
			//IL_0576: Unknown result type (might be due to invalid IL or missing references)
			//IL_0583: Expected O, but got Unknown
			//IL_05ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_05b9: Expected O, but got Unknown
			//IL_05e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ef: Expected O, but got Unknown
			//IL_061a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0625: Expected O, but got Unknown
			//IL_06b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_06c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_06d0: Expected O, but got Unknown
			//IL_06d0: Expected O, but got Unknown
			//IL_070a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0720: Unknown result type (might be due to invalid IL or missing references)
			//IL_0735: Unknown result type (might be due to invalid IL or missing references)
			//IL_0740: Expected O, but got Unknown
			//IL_0740: Expected O, but got Unknown
			//IL_0740: Expected O, but got Unknown
			//IL_077a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0791: Unknown result type (might be due to invalid IL or missing references)
			//IL_079c: Expected O, but got Unknown
			//IL_079c: Expected O, but got Unknown
			//IL_07d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_07e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_07f2: Expected O, but got Unknown
			//IL_07f2: Expected O, but got Unknown
			//IL_066c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0677: Expected O, but got Unknown
			//IL_0846: Unknown result type (might be due to invalid IL or missing references)
			//IL_085d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0868: Expected O, but got Unknown
			//IL_0868: Expected O, but got Unknown
			//IL_08bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_08d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_08e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_08f2: Expected O, but got Unknown
			//IL_08f2: Expected O, but got Unknown
			//IL_08f2: Expected O, but got Unknown
			//IL_0946: Unknown result type (might be due to invalid IL or missing references)
			//IL_095d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0968: Expected O, but got Unknown
			//IL_0968: Expected O, but got Unknown
			//IL_09bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_09d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_09de: Expected O, but got Unknown
			//IL_09de: Expected O, but got Unknown
			//IL_0a36: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a4b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a56: Expected O, but got Unknown
			//IL_0a56: Expected O, but got Unknown
			//IL_0a8e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0aa3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0aae: Expected O, but got Unknown
			//IL_0aae: Expected O, but got Unknown
			//IL_0b3d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b54: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b5f: Expected O, but got Unknown
			//IL_0b5f: Expected O, but got Unknown
			//IL_0ba6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0bb4: Expected O, but got Unknown
			//IL_0bee: Unknown result type (might be due to invalid IL or missing references)
			//IL_0c05: Unknown result type (might be due to invalid IL or missing references)
			//IL_0c10: Expected O, but got Unknown
			//IL_0c10: Expected O, but got Unknown
			//IL_0c38: Unknown result type (might be due to invalid IL or missing references)
			//IL_0c4e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0c63: Unknown result type (might be due to invalid IL or missing references)
			//IL_0c6e: Expected O, but got Unknown
			//IL_0c6e: Expected O, but got Unknown
			//IL_0c6e: Expected O, but got Unknown
			//IL_0ca8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0cb6: Expected O, but got Unknown
			//IL_0ae6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0afb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b06: Expected O, but got Unknown
			//IL_0b06: Expected O, but got Unknown
			//IL_0cea: Unknown result type (might be due to invalid IL or missing references)
			//IL_0d00: Unknown result type (might be due to invalid IL or missing references)
			//IL_0d15: Unknown result type (might be due to invalid IL or missing references)
			//IL_0d20: Expected O, but got Unknown
			//IL_0d20: Expected O, but got Unknown
			//IL_0d20: Expected O, but got Unknown
			//IL_0d7a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0d91: Unknown result type (might be due to invalid IL or missing references)
			//IL_0d9c: Expected O, but got Unknown
			//IL_0d9c: Expected O, but got Unknown
			//IL_0dc4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0dda: Unknown result type (might be due to invalid IL or missing references)
			//IL_0def: Unknown result type (might be due to invalid IL or missing references)
			//IL_0dfa: Expected O, but got Unknown
			//IL_0dfa: Expected O, but got Unknown
			//IL_0dfa: Expected O, but got Unknown
			//IL_0e2e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0e44: Unknown result type (might be due to invalid IL or missing references)
			//IL_0e59: Unknown result type (might be due to invalid IL or missing references)
			//IL_0e64: Expected O, but got Unknown
			//IL_0e64: Expected O, but got Unknown
			//IL_0e64: Expected O, but got Unknown
			//IL_0ebe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0ed5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0ee0: Expected O, but got Unknown
			//IL_0ee0: Expected O, but got Unknown
			//IL_0f08: Unknown result type (might be due to invalid IL or missing references)
			//IL_0f1e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0f33: Unknown result type (might be due to invalid IL or missing references)
			//IL_0f3e: Expected O, but got Unknown
			//IL_0f3e: Expected O, but got Unknown
			//IL_0f3e: Expected O, but got Unknown
			//IL_0f72: Unknown result type (might be due to invalid IL or missing references)
			//IL_0f88: Unknown result type (might be due to invalid IL or missing references)
			//IL_0f9d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0fa8: Expected O, but got Unknown
			//IL_0fa8: Expected O, but got Unknown
			//IL_0fa8: Expected O, but got Unknown
			//IL_1002: Unknown result type (might be due to invalid IL or missing references)
			//IL_1019: Unknown result type (might be due to invalid IL or missing references)
			//IL_1024: Expected O, but got Unknown
			//IL_1024: Expected O, but got Unknown
			//IL_104c: Unknown result type (might be due to invalid IL or missing references)
			//IL_1062: Unknown result type (might be due to invalid IL or missing references)
			//IL_1077: Unknown result type (might be due to invalid IL or missing references)
			//IL_1082: Expected O, but got Unknown
			//IL_1082: Expected O, but got Unknown
			//IL_1082: Expected O, but got Unknown
			//IL_10b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_10cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_10e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_10ec: Expected O, but got Unknown
			//IL_10ec: Expected O, but got Unknown
			//IL_10ec: Expected O, but got Unknown
			//IL_1134: Unknown result type (might be due to invalid IL or missing references)
			//IL_1142: Expected O, but got Unknown
			//IL_116b: Unknown result type (might be due to invalid IL or missing references)
			//IL_1178: Expected O, but got Unknown
			//IL_11b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_11c0: Expected O, but got Unknown
			//IL_11ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_11fa: Expected O, but got Unknown
			//IL_1269: Unknown result type (might be due to invalid IL or missing references)
			//IL_1276: Expected O, but got Unknown
			//IL_12cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_12dc: Expected O, but got Unknown
			//IL_1328: Unknown result type (might be due to invalid IL or missing references)
			//IL_1335: Expected O, but got Unknown
			//IL_1370: Unknown result type (might be due to invalid IL or missing references)
			//IL_137d: Expected O, but got Unknown
			//IL_13aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_13b7: Expected O, but got Unknown
			//IL_13f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_1406: Unknown result type (might be due to invalid IL or missing references)
			//IL_1413: Expected O, but got Unknown
			//IL_1413: Expected O, but got Unknown
			//IL_143c: Unknown result type (might be due to invalid IL or missing references)
			//IL_1449: Expected O, but got Unknown
			//IL_1471: Unknown result type (might be due to invalid IL or missing references)
			//IL_147f: Expected O, but got Unknown
			//IL_14a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_14b5: Expected O, but got Unknown
			//IL_14dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_14eb: Expected O, but got Unknown
			//IL_1525: Unknown result type (might be due to invalid IL or missing references)
			//IL_1533: Expected O, but got Unknown
			//IL_156d: Unknown result type (might be due to invalid IL or missing references)
			//IL_157b: Expected O, but got Unknown
			//IL_15a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_15b1: Expected O, but got Unknown
			//IL_1605: Unknown result type (might be due to invalid IL or missing references)
			//IL_1613: Expected O, but got Unknown
			//IL_165a: Unknown result type (might be due to invalid IL or missing references)
			//IL_1668: Expected O, but got Unknown
			//IL_1690: Unknown result type (might be due to invalid IL or missing references)
			//IL_169e: Expected O, but got Unknown
			//IL_16e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_16f3: Expected O, but got Unknown
			//IL_173a: Unknown result type (might be due to invalid IL or missing references)
			//IL_174f: Unknown result type (might be due to invalid IL or missing references)
			//IL_175c: Expected O, but got Unknown
			//IL_175c: Expected O, but got Unknown
			//IL_1796: Unknown result type (might be due to invalid IL or missing references)
			//IL_17ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_17b8: Expected O, but got Unknown
			//IL_17b8: Expected O, but got Unknown
			//IL_17f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_1809: Unknown result type (might be due to invalid IL or missing references)
			//IL_1814: Expected O, but got Unknown
			//IL_1814: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Log.LogInfo((object)"COTL Multiplayer Expansion loading...");
			Harmony val = new Harmony("com.cotlmp.multiplayerexpansion");
			val.Patch((MethodBase)AccessTools.Method(typeof(CoopManager), "Awake", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "CoopManager_Awake", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(CoopManager), "OnControllerConnected", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "OnControllerConnected", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(CoopManager), "AddPlayerFromMenu", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "AddPlayerFromMenu", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(UICoopAssignController), "ConfirmSpawnButtonPress", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "ConfirmSpawnButtonPress", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(CoopManager), "RemovePlayerFromMenu", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "RemovePlayerFromMenu", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_TempleAltar), "OnInteract", new Type[1] { typeof(StateMachine) }, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "TempleAltar_Finalizer", (Type[])null), (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_TempleAltar), "DoCancel", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "TempleAltar_Finalizer", (Type[])null), (HarmonyMethod)null);
			MethodInfo method = typeof(Interaction_TempleAltar).GetMethod("TryOnboardSin", BindingFlags.Instance | BindingFlags.NonPublic);
			if (method != null)
			{
				val.Patch((MethodBase)method, new HarmonyMethod(typeof(Patches), "TempleAltar_TryOnboardSin_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "TempleAltar_Finalizer", (Type[])null), (HarmonyMethod)null);
				Log.LogInfo((object)"[MP] Patched Interaction_TempleAltar.TryOnboardSin coop-safe guard");
			}
			else
			{
				Log.LogWarning((object)"[MP] Skipped patch: Interaction_TempleAltar.TryOnboardSin not found");
			}
			MethodInfo methodInfo = AccessTools.Method(typeof(Interaction_TempleAltar), "<DoCancel>b__68_1", (Type[])null, (Type[])null);
			if (methodInfo != null)
			{
				val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "TempleAltar_CancelCleanup_Postfix", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "TempleAltar_Finalizer", (Type[])null), (HarmonyMethod)null);
				Log.LogInfo((object)"[MP] Patched Interaction_TempleAltar.DoCancel cleanup callback finalizer");
			}
			else
			{
				Log.LogWarning((object)"[MP] Skipped patch: Interaction_TempleAltar.DoCancel cleanup callback not found");
			}
			MethodInfo methodInfo2 = Patches.FindNestedMoveNext(typeof(Interaction_TempleAltar), "DelayMenu");
			if (methodInfo2 != null)
			{
				val.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(Patches), "TempleAltarTwoPlayerScope_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "TempleAltarTwoPlayerScope_Finalizer", (Type[])null), (HarmonyMethod)null);
				Log.LogInfo((object)"[MP] Patched Interaction_TempleAltar.DelayMenu player presentation scope");
			}
			else
			{
				Log.LogWarning((object)"[MP] Skipped patch: Interaction_TempleAltar.DelayMenu MoveNext not found");
			}
			val.Patch((MethodBase)AccessTools.Method(typeof(PlayerFarming), "OnEnable", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerFarming_OnEnable_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(CoopManager), "SpawnCoopPlayer", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "SpawnCoopPlayer_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(PlayerFarming), "SetSkin", new Type[1] { typeof(bool) }, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "SetSkin_Recolor", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(UIPauseMenuController), "OnCoopButtonPressed", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "OnCoopButtonPressed", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(UIPauseMenuController), "RefreshCoopText", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "RefreshCoopText", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(HUD_Manager), "Update", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "HUD_Update", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(CoopManager), "RefreshCoopPlayerRewired", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "RefreshCoopPlayerRewired", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(PlayerFarming), "RefreshPlayersCount", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "RefreshPlayersCount_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(CoopIndicatorIcon), "SetUsername", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "SetUsername_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(CoopIndicatorIcon), "AnimateDamageOnIcons", new Type[1] { typeof(PlayerFarming) }, (Type[])null), new HarmonyMethod(typeof(Patches), "CoopIndicatorIcon_AnimateDamageOnIcons_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(PlayerFarming), "Update", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "PlayerFarming_Update_Camera", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_DLCShrine), "Update", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "SilentFinalizer", (Type[])null), (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_DLCShrine), "OnDisable", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "SilentFinalizer", (Type[])null), (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(CoopManager), "Update", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "SilentFinalizer", (Type[])null), (HarmonyMethod)null);
			Type type = AccessTools.TypeByName("SkeletonAnimationLODGlobalManager");
			if (type != null)
			{
				MethodInfo methodInfo3 = AccessTools.Method(type, "Update", (Type[])null, (Type[])null);
				if (methodInfo3 != null)
				{
					val.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "SilentFinalizer", (Type[])null), (HarmonyMethod)null);
					Log.LogInfo((object)"[MP] Patched SkeletonAnimationLODGlobalManager.Update finalizer");
				}
			}
			val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_Fishing), "Update", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerCount_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "FishingBounds_Finalizer", (Type[])null), (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_Fishing), "UpdatePlayer", new Type[1] { typeof(int) }, (Type[])null), new HarmonyMethod(typeof(Patches), "FishingTwoPlayerScope_Prefix", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerCount_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "FishingTwoPlayerScope_Finalizer", (Type[])null), (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_Fishing), "OnInteract", new Type[1] { typeof(StateMachine) }, (Type[])null), new HarmonyMethod(typeof(Patches), "InteractionFishing_BlockExtraPlayers_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "FishingBounds_Finalizer", (Type[])null), (HarmonyMethod)null);
			MethodInfo methodInfo4 = Patches.FindNestedMoveNext(typeof(Interaction_Fishing), "WaitForPlayersToArriveIE");
			if (methodInfo4 != null)
			{
				val.Patch((MethodBase)methodInfo4, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerScope_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerScope_Finalizer", (Type[])null), (HarmonyMethod)null);
				Log.LogInfo((object)"[MP] Patched Interaction_Fishing.WaitForPlayersToArriveIE two-player scope");
			}
			else
			{
				Log.LogWarning((object)"[MP] Skipped patch: Interaction_Fishing.WaitForPlayersToArriveIE MoveNext not found");
			}
			MethodInfo methodInfo5 = Patches.FindNestedMoveNext(typeof(Interaction_Fishing), "BeganFishingIE");
			if (methodInfo5 != null)
			{
				val.Patch((MethodBase)methodInfo5, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerScope_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerScope_Finalizer", (Type[])null), (HarmonyMethod)null);
				Log.LogInfo((object)"[MP] Patched Interaction_Fishing.BeganFishingIE two-player scope");
			}
			else
			{
				Log.LogWarning((object)"[MP] Skipped patch: Interaction_Fishing.BeganFishingIE MoveNext not found");
			}
			MethodInfo methodInfo6 = Patches.FindNestedMoveNext(typeof(Interaction_Fishing), "CastLineIE");
			if (methodInfo6 != null)
			{
				val.Patch((MethodBase)methodInfo6, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerScope_Prefix", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerCount_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "FishingTwoPlayerScope_Finalizer", (Type[])null), (HarmonyMethod)null);
				Log.LogInfo((object)"[MP] Patched Interaction_Fishing.CastLineIE two-player scope");
			}
			else
			{
				Log.LogWarning((object)"[MP] Skipped patch: Interaction_Fishing.CastLineIE MoveNext not found");
			}
			MethodInfo methodInfo7 = Patches.FindNestedMoveNext(typeof(Interaction_Fishing), "FishCaughtIE");
			if (methodInfo7 != null)
			{
				val.Patch((MethodBase)methodInfo7, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerScope_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerScope_Finalizer", (Type[])null), (HarmonyMethod)null);
				Log.LogInfo((object)"[MP] Patched Interaction_Fishing.FishCaughtIE two-player scope");
			}
			else
			{
				Log.LogWarning((object)"[MP] Skipped patch: Interaction_Fishing.FishCaughtIE MoveNext not found");
			}
			MethodInfo methodInfo8 = Patches.FindNestedMoveNext(typeof(Interaction_Fishing), "NoCatch");
			if (methodInfo8 != null)
			{
				val.Patch((MethodBase)methodInfo8, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerScope_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerScope_Finalizer", (Type[])null), (HarmonyMethod)null);
				Log.LogInfo((object)"[MP] Patched Interaction_Fishing.NoCatch two-player scope");
			}
			else
			{
				Log.LogWarning((object)"[MP] Skipped patch: Interaction_Fishing.NoCatch MoveNext not found");
			}
			MethodInfo methodInfo9 = AccessTools.Method(typeof(Fishable), "Update", (Type[])null, (Type[])null);
			if (methodInfo9 != null)
			{
				val.Patch((MethodBase)methodInfo9, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerCount_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "FishingBounds_Finalizer", (Type[])null), (HarmonyMethod)null);
			}
			MethodInfo methodInfo10 = AccessTools.Method(typeof(Fishable), "HookLanded", (Type[])null, (Type[])null);
			if (methodInfo10 != null)
			{
				val.Patch((MethodBase)methodInfo10, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "FishingTwoPlayerCount_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "FishingBounds_Finalizer", (Type[])null), (HarmonyMethod)null);
			}
			MethodInfo methodInfo11 = AccessTools.Method(typeof(BiomeConstants), "BiomeGenerator_OnBiomeChangeRoom", (Type[])null, (Type[])null);
			if (methodInfo11 != null)
			{
				val.Patch((MethodBase)methodInfo11, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "BiomeConstants_BiomeGenerator_OnBiomeChangeRoom_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "SilentFinalizer", (Type[])null), (HarmonyMethod)null);
				Log.LogInfo((object)"[MP] Patched BiomeConstants.BiomeGenerator_OnBiomeChangeRoom (safe particle-clear transpiler)");
			}
			val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_BaseTeleporter), "HandleAnimationStateEvent", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "TeleporterAnimEvent_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "TeleporterAnimEvent_Finalizer", (Type[])null), (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_BaseTeleporter), "SpawnPlayerIE", new Type[2]
			{
				typeof(PlayerFarming),
				typeof(Vector3)
			}, (Type[])null), new HarmonyMethod(typeof(Patches), "SpawnPlayerIE_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_PlayerClearRubble), "OnInteract", new Type[1] { typeof(StateMachine) }, (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerClearRubble_OnInteract_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "PlayerClearRubble_Finalizer", (Type[])null), (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_PlayerClearRubble), "Update", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerClearRubble_Update_Prefix", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "PlayerClearRubblePlayerIndex_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerClearRubble_Finalizer", (Type[])null), (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_PlayerClearRubble), "DoBuild", new Type[1] { typeof(PlayerFarming) }, (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerClearRubble_DoBuild_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			MethodInfo methodInfo12 = Patches.FindNestedMoveNext(typeof(Interaction_PlayerClearRubble), "DoBuild");
			if (methodInfo12 != null)
			{
				val.Patch((MethodBase)methodInfo12, new HarmonyMethod(typeof(Patches), "PlayerClearRubble_DoBuildMoveNext_Prefix", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "PlayerClearRubblePlayerIndex_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerClearRubble_Finalizer", (Type[])null), (HarmonyMethod)null);
				Log.LogInfo((object)"[MP] Patched Interaction_PlayerClearRubble.DoBuild MoveNext safety");
			}
			else
			{
				Log.LogWarning((object)"[MP] Skipped patch: Interaction_PlayerClearRubble.DoBuild MoveNext not found");
			}
			val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_Woodcutting), "OnInteract", new Type[1] { typeof(StateMachine) }, (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerWork_OnInteract_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "PlayerWork_Finalizer", (Type[])null), (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_Woodcutting), "Update", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerWork_Update_Prefix", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "PlayerClearRubblePlayerIndex_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerWork_Finalizer", (Type[])null), (HarmonyMethod)null);
			MethodInfo methodInfo13 = Patches.FindNestedMoveNext(typeof(Interaction_Woodcutting), "DoWoodCutting");
			if (methodInfo13 != null)
			{
				val.Patch((MethodBase)methodInfo13, new HarmonyMethod(typeof(Patches), "PlayerWork_MoveNext_Prefix", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "PlayerClearRubblePlayerIndex_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerWork_Finalizer", (Type[])null), (HarmonyMethod)null);
				Log.LogInfo((object)"[MP] Patched Interaction_Woodcutting.DoWoodCutting MoveNext safety");
			}
			else
			{
				Log.LogWarning((object)"[MP] Skipped patch: Interaction_Woodcutting.DoWoodCutting MoveNext not found");
			}
			val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_WoodcuttingRubble), "OnInteract", new Type[1] { typeof(StateMachine) }, (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerWork_OnInteract_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "PlayerWork_Finalizer", (Type[])null), (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_WoodcuttingRubble), "Update", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerWork_Update_Prefix", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "PlayerClearRubblePlayerIndex_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerWork_Finalizer", (Type[])null), (HarmonyMethod)null);
			MethodInfo methodInfo14 = Patches.FindNestedMoveNext(typeof(Interaction_WoodcuttingRubble), "DoWoodCutting");
			if (methodInfo14 != null)
			{
				val.Patch((MethodBase)methodInfo14, new HarmonyMethod(typeof(Patches), "PlayerWork_MoveNext_Prefix", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "PlayerClearRubblePlayerIndex_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerWork_Finalizer", (Type[])null), (HarmonyMethod)null);
				Log.LogInfo((object)"[MP] Patched Interaction_WoodcuttingRubble.DoWoodCutting MoveNext safety");
			}
			else
			{
				Log.LogWarning((object)"[MP] Skipped patch: Interaction_WoodcuttingRubble.DoWoodCutting MoveNext not found");
			}
			val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_Berries), "OnInteract", new Type[1] { typeof(StateMachine) }, (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerWork_OnInteract_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "PlayerWork_Finalizer", (Type[])null), (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_Berries), "Update", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerWork_Update_Prefix", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "PlayerClearRubblePlayerIndex_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerWork_Finalizer", (Type[])null), (HarmonyMethod)null);
			MethodInfo methodInfo15 = Patches.FindNestedMoveNext(typeof(Interaction_Berries), "PickBerries");
			if (methodInfo15 != null)
			{
				val.Patch((MethodBase)methodInfo15, new HarmonyMethod(typeof(Patches), "PlayerWork_MoveNext_Prefix", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "PlayerClearRubblePlayerIndex_Transpiler", (Type[])null), new HarmonyMethod(typeof(Patches), "PlayerWork_Finalizer", (Type[])null), (HarmonyMethod)null);
				Log.LogInfo((object)"[MP] Patched Interaction_Berries.PickBerries MoveNext safety");
			}
			else
			{
				Log.LogWarning((object)"[MP] Skipped patch: Interaction_Berries.PickBerries MoveNext not found");
			}
			val.Patch((MethodBase)AccessTools.Method(typeof(GameManager), "NewRun", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "NewRun_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(CoopManager), "Start", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "CoopManager_Start_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(CoopManager), "WakeAllKnockedOutPlayersWithHealth", new Type[1] { typeof(float) }, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "CoopManager_WakeAllKnockedOutPlayersWithHealth_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(CoopManager), "WakeAllKnockedOutPlayers", Type.EmptyTypes, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "CoopManager_WakeAllKnockedOutPlayers_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(CoopManager), "WakeKnockedOutPlayer", new Type[5]
			{
				typeof(PlayerFarming),
				typeof(float),
				typeof(PlayerFarming),
				typeof(HeartPickupType),
				typeof(bool)
			}, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "CoopManager_WakeKnockedOutPlayer_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			MethodInfo method2 = typeof(CoopManager).GetMethod("ResetMainPlayerOnConversationEnd", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[2]
			{
				typeof(bool),
				typeof(bool)
			}, null);
			if (method2 != null)
			{
				val.Patch((MethodBase)method2, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "CoopManager_ResetMainPlayerOnConversationEnd_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			else
			{
				Log.LogWarning((object)"[MP] Skipped patch: CoopManager.ResetMainPlayerOnConversationEnd(bool,bool) not found");
			}
			val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_WeaponSelectionPodium), "SetWeapon", new Type[1] { typeof(int) }, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "WeaponPodium_SetWeapon_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_WeaponSelectionPodium), "SetCurse", new Type[1] { typeof(int) }, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "WeaponPodium_SetCurse_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_WeaponSelectionPodium), "OnEnable", Type.EmptyTypes, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "WeaponPodium_OnEnable_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(Interaction_WeaponSelectionPodium), "OnInteract", new Type[1] { typeof(StateMachine) }, (Type[])null), new HarmonyMethod(typeof(Patches), "WeaponPodium_OnInteract_Prefix", (Type[])null), new HarmonyMethod(typeof(Patches), "WeaponPodium_OnInteract_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)typeof(UICoopAssignController).GetMethod("OnShowStarted", BindingFlags.Instance | BindingFlags.NonPublic), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "CoopAssign_OnShowStarted", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(Health_Manager), "Init", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "HealthManager_Init", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(HUD_Hearts), "SetWeapon", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "HUDHearts_SetWeapon_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(HUD_Hearts), "SetCurse", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "HUDHearts_SetCurse_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(HUD_Hearts), "OnHPUpdated", new Type[1] { typeof(HealthPlayer) }, (Type[])null), new HarmonyMethod(typeof(Patches), "HUDHearts_OnHPUpdated_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(HUD_Hearts), "OnTotalHPUpdated", new Type[1] { typeof(HealthPlayer) }, (Type[])null), new HarmonyMethod(typeof(Patches), "HUDHearts_OnTotalHPUpdated_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(HUD_Hearts), "OnEnable", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "HUDHearts_OnEnable_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(HUD_Heart), "SetSprite", new Type[3]
			{
				typeof(HeartState),
				typeof(bool),
				typeof(HeartType)
			}, (Type[])null), new HarmonyMethod(typeof(Patches), "HUDHeart_SetSprite_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(HUD_Heart), "Activate", new Type[2]
			{
				typeof(bool),
				typeof(bool)
			}, (Type[])null), new HarmonyMethod(typeof(Patches), "HUDHeart_Activate_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(FaithAmmo), "OnEnable", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "FaithAmmo_OnEnable_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(FaithAmmo), "OnGetSoul", new Type[2]
			{
				typeof(int),
				typeof(PlayerFarming)
			}, (Type[])null), new HarmonyMethod(typeof(Patches), "FaithAmmo_OnGetSoul_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(FaithAmmo), "UseAmmo", new Type[2]
			{
				typeof(float),
				typeof(bool)
			}, (Type[])null), new HarmonyMethod(typeof(Patches), "FaithAmmo_UseAmmo_Prefix", (Type[])null), new HarmonyMethod(typeof(Patches), "FaithAmmo_UseAmmo_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(HUD_ActiveTrinketsCoOp), "OnTrinketsChanged", new Type[1] { typeof(PlayerFarming) }, (Type[])null), new HarmonyMethod(typeof(Patches), "HUDActiveTrinketsCoOp_OnTrinketsChanged_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "SilentFinalizer", (Type[])null), (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(HUD_ActiveTrinketsCoOp), "OnRomanNumeralSettingChanged", new Type[1] { typeof(bool) }, (Type[])null), new HarmonyMethod(typeof(Patches), "HUDActiveTrinketsCoOp_OnRomanNumeralSettingChanged_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "SilentFinalizer", (Type[])null), (HarmonyMethod)null);
			Log.LogInfo((object)("All patches applied. MAX_PLAYERS = " + 4));
		}
	}
	public static class Patches
	{
		private sealed class PlayerListScopeState
		{
			public int SavedPlayersCount;

			public List<PlayerFarming> HiddenPlayers;
		}

		private static bool _removingPlayer = false;

		private static bool _manualRemoveRequested = false;

		private static float _autoRemoveSuppressedUntil = 0f;

		private const float AutoRemoveSuppressSeconds = 8f;

		private static int _savedExtraSlotCount = 0;

		private static bool _autoRespawnRoutineRunning = false;

		private static float _nextAutoRespawnAttemptAt = 0f;

		private static float _nextAutoRespawnHeartbeatAt = 0f;

		private static float _nextControllerHealHeartbeatAt = 0f;

		private static float _nextRumbleCleanupAt = 0f;

		private static readonly HashSet<int> _pendingSpawnSlots = new HashSet<int>();

		private static readonly Dictionary<int, bool> _pendingSpawnUseAnimation = new Dictionary<int, bool>();

		private static readonly Dictionary<int, float> _pendingSpawnFirstSeenAt = new Dictionary<int, float>();

		private static readonly HashSet<int> _desiredExtraSlots = new HashSet<int>();

		private static int _startupExtraRespawnDepth = 0;

		private static float _dungeonIntroProtectUntil = 0f;

		private static float _nextPendingBlockLogAt = 0f;

		private static string _lastLoggedDungeonRoomPath = null;

		private static bool _p2EntranceGuardRunning = false;

		private static bool _dungeonRoomStabilizeRunning = false;

		private static int _lastStabilizedDungeonRoomId = int.MinValue;

		private static float _nextDungeonRoomStabilizeCheckAt = 0f;

		private static float _roomEntryDistanceRepositionGraceUntil = 0f;

		private static readonly HashSet<int> _warnedOrphanSlotObjects = new HashSet<int>();

		private static readonly HashSet<int> _postReviveHardResetSlots = new HashSet<int>();

		private static float _lastObservedWakeHealth = 2f;

		private static float _lastObservedSacrificialWakeHealth = 2f;

		private static float _sacrificialReviveWindowUntil = 0f;

		private const float SacrificialReviveWindowSeconds = 7f;

		private const float BoostedWakeHealthThreshold = 2.01f;

		private static readonly Dictionary<int, float> _nextExtraFalseDownedWatchdogAt = new Dictionary<int, float>();

		private static readonly Dictionary<int, float> _postReviveRaceGuardUntil = new Dictionary<int, float>();

		private static readonly Dictionary<int, float> _nextPostReviveForceApplyAt = new Dictionary<int, float>();

		private static float _nextPostReviveRaceGuardHeartbeatAt = 0f;

		private const float PostReviveRaceGuardSeconds = 4f;

		private const float PostReviveRaceGuardPollInterval = 0.18f;

		private static Image _p2Widget;

		private static Image _p3Widget;

		private static Image _p4Widget;

		private static RectTransform _p2Arrow;

		private static RectTransform _p3Arrow;

		private static RectTransform _p4Arrow;

		private static readonly Dictionary<int, Sprite> _cachedSprites = new Dictionary<int, Sprite>();

		private static readonly Dictionary<int, Image> _cachedTextImages = new Dictionary<int, Image>();

		private static readonly Dictionary<int, SpriteRenderer> _cachedHaloSR = new Dictionary<int, SpriteRenderer>();

		private static readonly HashSet<int> _haloLogged = new HashSet<int>();

		private static readonly Dictionary<int, Sprite> _cachedHaloSprites = new Dictionary<int, Sprite>();

		private static readonly Dictionary<int, string> _haloSpriteNames = new Dictionary<int, string>
		{
			{ 2, "halo_orange.png" },
			{ 3, "halo_blue.png" }
		};

		private static int _podiumExtraCount = 0;

		private static readonly Vector3[] SpawnOffsets = (Vector3[])(object)new Vector3[4]
		{
			Vector3.zero,
			new Vector3(1.5f, 0f, 0f),
			new Vector3(0f, 1.5f, 0f),
			new Vector3(0f, -1.5f, 0f)
		};

		private static readonly FieldInfo _playerControllerUntouchableTimerField = AccessTools.Field(typeof(PlayerController), "untouchableTimer");

		private static readonly FieldInfo _playerControllerInvincibleTimerField = AccessTools.Field(typeof(PlayerController), "invincibleTimer");

		private static readonly FieldInfo _playerControllerImmuneToProjectilesTimerField = AccessTools.Field(typeof(PlayerController), "immuneToProjectilesTimer");

		private static readonly FieldInfo _playerControllerUntouchableTimerFlashField = AccessTools.Field(typeof(PlayerController), "untouchableTimerFlash");

		private static Type _cachedHealthReflectionType;

		private static FieldInfo _healthInvincibleField;

		private static FieldInfo _healthUntouchableField;

		private static FieldInfo _healthIgnoreProjectilesField;

		private static FieldInfo _healthMaxHpField;

		private static FieldInfo _healthTotalHpField;

		private static PropertyInfo _healthIgnoreProjectilesProperty;

		private static PropertyInfo _healthMaxHpProperty;

		private static PropertyInfo _healthTotalHpProperty;

		private static MethodInfo _healthClearAllStasisEffectsMethod;

		private static bool _faithAmmoDumped = false;

		private static bool _delayedRespawnAfterReviveQueued = false;

		private const float DelayedReviveRespawnInitialDelay = 0.35f;

		private const float DelayedReviveRespawnPollInterval = 0.2f;

		private const float DelayedReviveRespawnMaxDuration = 15f;

		private static readonly Vector3[] _teleporterOffsets = (Vector3[])(object)new Vector3[4]
		{
			new Vector3(-0.75f, 0f, 0f),
			new Vector3(0.75f, 0f, 0f),
			new Vector3(-1.5f, 0f, 0f),
			new Vector3(1.5f, 0f, 0f)
		};

		private static readonly FieldInfo _teleporterStencilField = typeof(Interaction_BaseTeleporter).GetField("PlayerStencilRT", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly FieldInfo _clearRubbleButtonDownField = typeof(Interaction_PlayerClearRubble).GetField("buttonDown", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly FieldInfo _clearRubbleActivatingPlayersField = typeof(Interaction_PlayerClearRubble).GetField("activatingPlayers", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly FieldInfo _templeAltarActivatedField = typeof(Interaction_TempleAltar).GetField("Activated", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly FieldInfo _templeAltarActivatingField = typeof(Interaction_TempleAltar).GetField("Activating", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly FieldInfo _templeAltarCoopPlayerFrozenField = typeof(Interaction_TempleAltar).GetField("CoopPlayerFrozen", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly FieldInfo _templeAltarInteractedField = typeof(Interaction_TempleAltar).GetField("<Interacted>k__BackingField", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly FieldInfo _templeAltarColliderField = typeof(Interaction_TempleAltar).GetField("Collider", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly FieldInfo _templeAltarForceRitualField = typeof(Interaction_TempleAltar).GetField("forceRitual", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static bool _templeExtraReleaseQueued = false;

		private static bool _loggedFishingBoundsSuppressed = false;

		private static readonly Stack<PlayerListScopeState> _twoPlayerListScopes = new Stack<PlayerListScopeState>();

		private static bool _spawnedExtraWeaponPodiums = false;

		private static bool _spawnedExtraSpellPodiums = false;

		private static readonly FieldInfo _twinPodiumField = typeof(Interaction_WeaponSelectionPodium).GetField("TwinPodium", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly Dictionary<int, List<Interaction_WeaponSelectionPodium>> _podiumClones = new Dictionary<int, List<Interaction_WeaponSelectionPodium>>();

		private static readonly Dictionary<int, int> _podiumOwnerSlotByInstanceId = new Dictionary<int, int>();

		private static bool _positioningDeferred = false;

		private static readonly Dictionary<int, GameObject> _extraHeartBars = new Dictionary<int, GameObject>();

		private static readonly HashSet<int> _suppressedHeartEffectLogs = new HashSet<int>();

		private static readonly HashSet<int> _suppressedFaithAmmoLogs = new HashSet<int>();

		private static readonly HashSet<int> _suppressedTrinketHudLogs = new HashSet<int>();

		private static readonly HashSet<int> _missingDamageIconLogs = new HashSet<int>();

		private static readonly MethodInfo _hudHeartsClearHealthEvents = typeof(HUD_Hearts).GetMethod("ClearHealthEvents", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly MethodInfo _faithAmmoUpdateBar = typeof(FaithAmmo).GetMethod("UpdateBar", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly FieldInfo _coopIndicatorAnimateOnDamagedField = typeof(CoopIndicatorIcon).GetField("animateIconOnDamaged", BindingFlags.Instance | BindingFlags.NonPublic);

		private static bool _reroutingFaithAmmoUse = false;

		private static int _safeBiomeClearPatchCountLog = -1;

		private static readonly Dictionary<char, int[]> _digitBitmaps = new Dictionary<char, int[]>
		{
			{
				'1',
				new int[35]
				{
					0, 1, 1, 0, 0, 1, 1, 1, 0, 0,
					0, 1, 1, 0, 0, 0, 1, 1, 0, 0,
					0, 1, 1, 0, 0, 0, 1, 1, 0, 0,
					1, 1, 1, 1, 0
				}
			},
			{
				'2',
				new int[35]
				{
					1, 1, 1, 1, 0, 1, 0, 0, 1, 1,
					0, 0, 0, 1, 1, 0, 1, 1, 1, 0,
					1, 1, 0, 0, 0, 1, 1, 0, 0, 0,
					1, 1, 1, 1, 1
				}
			},
			{
				'3',
				new int[35]
				{
					1, 1, 1, 1, 0, 1, 0, 0, 1, 1,
					0, 0, 0, 1, 1, 0, 1, 1, 1, 0,
					0, 0, 0, 1, 1, 1, 0, 0, 1, 1,
					1, 1, 1, 1, 0
				}
			},
			{
				'4',
				new int[35]
				{
					0, 0, 1, 1, 0, 0, 1, 1, 1, 0,
					1, 1, 0, 1, 0, 1, 0, 0, 1, 0,
					1, 1, 1, 1, 1, 0, 0, 0, 1, 0,
					0, 0, 1, 1, 0
				}
			}
		};

		public static void CoopManager_Awake()
		{
			GameObject[] allPlayerGameObjects = CoopManager.AllPlayerGameObjects;
			if (allPlayerGameObjects.Length < 4)
			{
				GameObject[] array = (GameObject[])(object)new GameObject[4];
				for (int i = 0; i < allPlayerGameObjects.Length; i++)
				{
					array[i] = allPlayerGameObjects[i];
				}
				typeof(CoopManager).GetField("AllPlayerGameObjects", BindingFlags.Static | BindingFlags.Public).SetValue(null, array);
				Plugin.Log.LogInfo((object)$"AllPlayerGameObjects expanded to {4}");
			}
		}

		public static void OnControllerConnected(ControllerStatusChangedEventArgs args)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Expected O, but got Unknown
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Expected O, but got Unknown
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Expected O, but got Unknown
			if (!CoopManager.CoopActive || PlayerFarming.playersCount >= 4 || (Object)CoopManager.Instance == (Object)null || CoopManager.Instance.IsSpawningOrRemovingPlayer)
			{
				return;
			}
			Joystick val = null;
			foreach (Joystick joystick in ReInput.controllers.Joysticks)
			{
				if (((Controller)joystick).id == args.controllerId)
				{
					val = joystick;
					break;
				}
			}
			if (val == null)
			{
				return;
			}
			for (int i = 0; i < 4; i++)
			{
				Player player = RewiredInputManager.GetPlayer(i);
				if (player != null && player.controllers.ContainsController((Controller)val))
				{
					Plugin.Log.LogInfo((object)$"OnControllerConnected: already assigned to slot {i}, skipping");
					return;
				}
			}
			int playersCount = PlayerFarming.playersCount;
			Plugin.Log.LogInfo((object)$"OnControllerConnected: auto-assigning to slot {playersCount}");
			for (int j = 0; j < 4; j++)
			{
				RewiredInputManager.GetPlayer(j).controllers.RemoveController((Controller)val);
			}
			RewiredInputManager.GetPlayer(playersCount).controllers.AddController((Controller)val, true);
			MarkDesiredExtraSlot(playersCount, desired: true);
			if (playersCount >= 2)
			{
				_pendingSpawnSlots.Add(playersCount);
			}
			CoopManager.Instance.SpawnCoopPlayer(playersCount, true, -1f);
			CoopManager.CoopActive = true;
			CoopManager.RefreshCoopPlayerRewired();
			TryStopAllRumble("OnControllerConnected");
			Plugin.Log.LogInfo((object)$"Auto-spawned goat at slot {playersCount}");
		}

		public static bool AddPlayerFromMenu()
		{
			if (PlayerFarming.playersCount < 4)
			{
				Plugin.Log.LogInfo((object)$"AddPlayerFromMenu: players={PlayerFarming.playersCount}, opening assign menu");
				UICoopAssignController val = MonoSingleton<UIManager>.Instance.ShowCoopAssignMenu();
				((UIMenuBase)val).OnHidden = (Action)Delegate.Combine(((UIMenuBase)val).OnHidden, (Action)delegate
				{
					if (CoopManager.CoopActive)
					{
						CoopManager.AddtionalUser = UserHelper.GetPlayer(1);
					}
				});
			}
			return false;
		}

		public static bool OnCoopButtonPressed(UIPauseMenuController __instance)
		{
			Plugin.Log.LogInfo((object)$"OnCoopButtonPressed: players={PlayerFarming.playersCount}, coopActive={CoopManager.CoopActive}");
			if (PlayerFarming.playersCount < 4)
			{
				((UIMenuBase)__instance).Hide(true);
				CoopManager.AddPlayerFromMenu();
				return false;
			}
			if (CoopManager.CoopActive)
			{
				((UIMenuBase)__instance).Hide(false);
				_manualRemoveRequested = true;
				CoopManager.RemovePlayerFromMenu();
				return false;
			}
			return true;
		}

		public static void RefreshCoopText(UIPauseMenuController __instance)
		{
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Expected O, but got Unknown
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Expected O, but got Unknown
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Expected O, but got Unknown
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Expected O, but got Unknown
			FieldInfo field = typeof(UIPauseMenuController).GetField("_coopButtonText", BindingFlags.Instance | BindingFlags.NonPublic);
			FieldInfo field2 = typeof(UIPauseMenuController).GetField("_coopButton", BindingFlags.Instance | BindingFlags.NonPublic);
			FieldInfo field3 = typeof(UIPauseMenuController).GetField("DenyCoop", BindingFlags.Instance | BindingFlags.NonPublic);
			if (field == null || field2 == null)
			{
				return;
			}
			object value = field.GetValue(__instance);
			TextMeshProUGUI val = (TextMeshProUGUI)((value is TextMeshProUGUI) ? value : null);
			object value2 = field2.GetValue(__instance);
			MMButton val2 = (MMButton)((value2 is MMButton) ? value2 : null);
			bool flag = field3 != null && (bool)field3.GetValue(__instance);
			if (!((Object)val == (Object)null || (Object)val2 == (Object)null || flag))
			{
				if (PlayerFarming.playersCount < 4)
				{
					((TMP_Text)val).text = $"Add Player ({PlayerFarming.playersCount}/{4})";
					val2.Interactable = true;
				}
				else
				{
					((TMP_Text)val).text = $"Remove Player ({PlayerFarming.playersCount}/{4})";
					val2.Interactable = true;
				}
			}
		}

		public static bool ConfirmSpawnButtonPress(UICoopAssignController __instance)
		{
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Expected O, but got Unknown
			//IL_0584: Unknown result type (might be due to invalid IL or missing references)
			//IL_058f: Expected O, but got Unknown
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Expected O, but got Unknown
			//IL_04ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b6: Expected O, but got Unknown
			//IL_05e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ef: Expected O, but got Unknown
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Expected O, but got Unknown
			//IL_04df: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e9: Expected O, but got Unknown
			//IL_0501: Unknown result type (might be due to invalid IL or missing references)
			//IL_050b: Expected O, but got Unknown
			//IL_051a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0525: Expected O, but got Unknown
			//IL_06ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_06b7: Expected O, but got Unknown
			//IL_06d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_06db: Expected O, but got Unknown
			//IL_0651: Unknown result type (might be due to invalid IL or missing references)
			//IL_065c: Expected O, but got Unknown
			//IL_0306: Unknown result type (might be due to invalid IL or missing references)
			//IL_0311: Expected O, but got Unknown
			//IL_0339: Unknown result type (might be due to invalid IL or missing references)
			//IL_0344: Expected O, but got Unknown
			//IL_0315: Unknown result type (might be due to invalid IL or missing references)
			//IL_031f: Expected O, but got Unknown
			//IL_042b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0435: Expected O, but got Unknown
			FieldInfo field = typeof(UICoopAssignController).GetField("displayedConnectedGamepads", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			FieldInfo field2 = typeof(UICoopAssignController).GetField("keyboardInputOption", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			FieldInfo field3 = typeof(UICoopAssignController).GetField("preventSpawnBufferTime", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			FieldInfo field4 = typeof(UICoopAssignController).GetField("confirmLock", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field == null || field2 == null)
			{
				Plugin.Log.LogError((object)"Reflection failed");
				return true;
			}
			if (field3 != null)
			{
				float num = (float)field3.GetValue(__instance);
				if (Time.realtimeSinceStartup < num + 0.25f)
				{
					return false;
				}
			}
			if (field4 != null)
			{
				if ((bool)field4.GetValue(__instance))
				{
					return false;
				}
				field4.SetValue(__instance, true);
			}
			if (!(field.GetValue(__instance) is CoopAssignInputOption[] array))
			{
				return true;
			}
			int num2 = PlayerFarming.playersCount;
			if (num2 < 1)
			{
				num2 = 1;
			}
			Plugin.Log.LogInfo((object)$"ConfirmSpawnButtonPress: nextSlot={num2}, players={PlayerFarming.playersCount}");
			EnforceKeyboardOwnership();
			List<Joystick> list = new List<Joystick>(ReInput.controllers.Joysticks);
			for (int i = 0; i < array.Length && i < list.Count; i++)
			{
				CoopAssignInputOption val = array[i];
				if ((Object)val == (Object)null || !((Component)val).gameObject.activeSelf || val.selection != -1)
				{
					continue;
				}
				Joystick val2 = list[i];
				if (val2 == null)
				{
					continue;
				}
				for (int j = 1; j < 4; j++)
				{
					if (!RewiredInputManager.GetPlayer(j).controllers.ContainsController((Controller)val2))
					{
						continue;
					}
					PlayerFarming val3 = null;
					foreach (PlayerFarming player2 in PlayerFarming.players)
					{
						if (!player2.isLamb && player2.playerID == j && ((Component)player2).gameObject.activeSelf)
						{
							val3 = player2;
							break;
						}
					}
					if ((Object)val3 == (Object)null)
					{
						continue;
					}
					Plugin.Log.LogInfo((object)$"Joystick [{i}] moved to Lamb side — removing goat slot {j} (dropout)");
					int playerID = val3.playerID;
					MarkDesiredExtraSlot(playerID, desired: false);
					_pendingSpawnSlots.Remove(playerID);
					_pendingSpawnUseAnimation.Remove(playerID);
					if (_cachedSprites.ContainsKey(playerID))
					{
						_cachedSprites.Remove(playerID);
					}
					if (_cachedTextImages.ContainsKey(playerID))
					{
						_cachedTextImages.Remove(playerID);
					}
					if (_cachedHaloSR.ContainsKey(playerID))
					{
						_cachedHaloSR.Remove(playerID);
					}
					if (_cachedHaloSprites.ContainsKey(playerID))
					{
						_cachedHaloSprites.Remove(playerID);
					}
					_haloLogged.Remove(playerID);
					_removingPlayer = true;
					CoopManager.AddtionalUser = null;
					UnhookVanillaCoopDelegates();
					CoopManager.Instance.RemoveCoopPlayer(val3, true, false, true);
					if (_extraHeartBars.TryGetValue(playerID, out var value) && (Object)value != (Object)null)
					{
						Object.Destroy((Object)value);
					}
					_extraHeartBars.Remove(playerID);
					if (PlayerFarming.playersCount > 1 && (Object)HUD_Manager.Instance != (Object)null)
					{
						foreach (PlayerFarming player3 in PlayerFarming.players)
						{
							if (!player3.isLamb)
							{
								HUD_Manager.Instance.healthManager.Init(player3);
							}
						}
					}
					int num3 = 0;
					foreach (PlayerFarming player4 in PlayerFarming.players)
					{
						if (!player4.isLamb)
						{
							num3++;
						}
					}
					if (num3 <= 0)
					{
						UICoopAssignController.SetInputForSoloPlay();
						UserHelper.DisengagePlayer(1);
						CoopManager.CoopActive = false;
						CoopManager.EnableCoopBlockers(false, true);
						DifficultyManager.LoadCurrentDifficulty();
					}
					else
					{
						Player player = RewiredInputManager.GetPlayer(playerID);
						if (player != null)
						{
							foreach (Joystick item in new List<Joystick>(player.controllers.Joysticks))
							{
								player.controllers.RemoveController((Controller)item);
							}
						}
						EnforceKeyboardOwnership();
						CoopManager.RefreshCoopPlayerRewired();
					}
					GameManager.GetInstance().WaitForSeconds(2f, (Action)delegate
					{
						_removingPlayer = false;
						Plugin.Log.LogInfo((object)"Dropout remove guard released");
					});
					break;
				}
				bool flag = false;
				for (int num4 = 1; num4 < 4; num4++)
				{
					if (RewiredInputManager.GetPlayer(num4).controllers.ContainsController((Controller)val2))
					{
						flag = true;
						break;
					}
				}
				if (!flag)
				{
					for (int num5 = 1; num5 < 4; num5++)
					{
						RewiredInputManager.GetPlayer(num5).controllers.RemoveController((Controller)val2);
					}
					if (!RewiredInputManager.GetPlayer(0).controllers.ContainsController((Controller)val2))
					{
						RewiredInputManager.GetPlayer(0).controllers.AddController((Controller)val2, true);
					}
					Plugin.Log.LogInfo((object)$"Joystick [{i}] assigned to Lamb (slot 0)");
				}
			}
			num2 = PlayerFarming.playersCount;
			if (num2 < 1)
			{
				num2 = 1;
			}
			for (int num6 = 0; num6 < array.Length && num6 < list.Count; num6++)
			{
				if (num2 >= 4)
				{
					break;
				}
				CoopAssignInputOption val4 = array[num6];
				if ((Object)val4 == (Object)null || !((Component)val4).gameObject.activeSelf || val4.selection != 1)
				{
					continue;
				}
				val4.inputLock = false;
				Joystick val5 = list[num6];
				if (val5 == null)
				{
					continue;
				}
				bool flag2 = false;
				for (int num7 = 1; num7 < 4; num7++)
				{
					if (!RewiredInputManager.GetPlayer(num7).controllers.ContainsController((Controller)val5))
					{
						continue;
					}
					PlayerFarming val6 = null;
					foreach (PlayerFarming player5 in PlayerFarming.players)
					{
						if (!player5.isLamb && player5.playerID == num7 && ((Component)player5).gameObject.activeSelf)
						{
							val6 = player5;
							break;
						}
					}
					if ((Object)val6 != (Object)null)
					{
						flag2 = true;
						Plugin.Log.LogInfo((object)$"Joystick [{num6}] already active goat slot {num7}, skipping");
						break;
					}
				}
				if (!flag2)
				{
					for (int num8 = 0; num8 < 4; num8++)
					{
						RewiredInputManager.GetPlayer(num8).controllers.RemoveController((Controller)val5);
					}
					RewiredInputManager.GetPlayer(num2).controllers.AddController((Controller)val5, true);
					Plugin.Log.LogInfo((object)$"Joystick [{num6}] -> new goat slot {num2}");
					MarkDesiredExtraSlot(num2, desired: true);
					if (num2 >= 2)
					{
						_pendingSpawnSlots.Add(num2);
					}
					CoopManager.Instance.SpawnCoopPlayer(num2, true, -1f);
					CoopManager.CoopActive = true;
					num2++;
				}
			}
			Plugin.Log.LogInfo((object)"Spawn complete");
			((UIMenuBase)__instance).Hide(false);
			return false;
		}

		public static void CoopAssign_OnShowStarted(UICoopAssignController __instance)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Expected O, but got Unknown
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Expected O, but got Unknown
			if (!CoopManager.CoopActive)
			{
				return;
			}
			FieldInfo field = typeof(UICoopAssignController).GetField("displayedConnectedGamepads", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			FieldInfo field2 = typeof(UICoopAssignController).GetField("keyboardInputOption", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field == null || field2 == null)
			{
				return;
			}
			CoopAssignInputOption[] array = field.GetValue(__instance) as CoopAssignInputOption[];
			object value = field2.GetValue(__instance);
			CoopAssignInputOption val = (CoopAssignInputOption)((value is CoopAssignInputOption) ? value : null);
			if (array == null)
			{
				return;
			}
			FieldInfo field3 = typeof(UICoopAssignController).GetField("keyboardInputOptionPreviousSelection", BindingFlags.Static | BindingFlags.NonPublic);
			if ((Object)val != (Object)null)
			{
				int num = ((!RewiredInputManager.GetPlayer(0).controllers.hasKeyboard) ? 1 : (-1));
				field3?.SetValue(null, num);
				val.SetSelection(num, true);
			}
			List<Joystick> list = new List<Joystick>(ReInput.controllers.Joysticks);
			for (int i = 0; i < array.Length && i < list.Count; i++)
			{
				Joystick val2 = list[i];
				int num2 = -1;
				for (int j = 0; j < 4; j++)
				{
					Player player = RewiredInputManager.GetPlayer(j);
					if (player != null && player.controllers.ContainsController((Controller)val2))
					{
						num2 = j;
						break;
					}
				}
				int num3 = ((num2 != 0) ? 1 : (-1));
				UICoopAssignController.displayedConnectedGamepadsPreviousSelection[i] = num3;
				array[i].SetSelection(num3, true);
				Plugin.Log.LogInfo((object)$"CoopAssign_OnShowStarted: joystick[{i}] ownerSlot={num2} → selection={num3}");
			}
		}

		public static bool RemovePlayerFromMenu()
		{
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Expected O, but got Unknown
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Expected O, but got Unknown
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Expected O, but got Unknown
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0236: Expected O, but got Unknown
			//IL_0239: Unknown result type (might be due to invalid IL or missing references)
			//IL_0243: Expected O, but got Unknown
			//IL_02e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02eb: Expected O, but got Unknown
			if (_removingPlayer)
			{
				Plugin.Log.LogInfo((object)"Remove guard active, skipping");
				return false;
			}
			if (PlayerFarming.playersCount > 1 && CoopManager.CoopActive)
			{
				bool manualRemoveRequested = _manualRemoveRequested;
				_manualRemoveRequested = false;
				if (!manualRemoveRequested && Time.unscaledTime < _autoRemoveSuppressedUntil)
				{
					float num = Math.Max(0f, _autoRemoveSuppressedUntil - Time.unscaledTime);
					Plugin.Log.LogInfo((object)$"Auto remove suppressed ({num:0.00}s left)");
					return false;
				}
				PlayerFarming val = null;
				for (int num2 = PlayerFarming.players.Count - 1; num2 >= 0; num2--)
				{
					PlayerFarming val2 = PlayerFarming.players[num2];
					if (!((Object)val2 == (Object)null) && !val2.isLamb && !((Object)((Component)val2).gameObject == (Object)null) && ((Component)val2).gameObject.activeSelf)
					{
						if (manualRemoveRequested)
						{
							val = val2;
							break;
						}
						int playerID = val2.playerID;
						if (playerID > 0 && playerID < 4 && !SlotHasJoystick(playerID))
						{
							val = val2;
							break;
						}
					}
				}
				if ((Object)val == (Object)null)
				{
					if (!manualRemoveRequested)
					{
						Plugin.Log.LogInfo((object)"Auto remove ignored: no goat slot lost joystick ownership");
					}
					else
					{
						Plugin.Log.LogWarning((object)"No Goat found to remove");
					}
					return false;
				}
				_removingPlayer = true;
				CoopManager.AddtionalUser = null;
				int playerID2 = val.playerID;
				MarkDesiredExtraSlot(playerID2, desired: false);
				_pendingSpawnSlots.Remove(playerID2);
				_pendingSpawnUseAnimation.Remove(playerID2);
				if (_cachedSprites.ContainsKey(playerID2))
				{
					_cachedSprites.Remove(playerID2);
				}
				if (_cachedTextImages.ContainsKey(playerID2))
				{
					_cachedTextImages.Remove(playerID2);
				}
				if (_cachedHaloSR.ContainsKey(playerID2))
				{
					_cachedHaloSR.Remove(playerID2);
				}
				if (_cachedHaloSprites.ContainsKey(playerID2))
				{
					_cachedHaloSprites.Remove(playerID2);
				}
				_haloLogged.Remove(playerID2);
				Plugin.Log.LogInfo((object)string.Format("Removing playerID {0} ({1})", playerID2, manualRemoveRequested ? "manual" : "auto-disconnect"));
				UnhookVanillaCoopDelegates();
				CoopManager.Instance.RemoveCoopPlayer(val, true, false, true);
				if (_extraHeartBars.TryGetValue(playerID2, out var value) && (Object)value != (Object)null)
				{
					Object.Destroy((Object)value);
				}
				_extraHeartBars.Remove(playerID2);
				int num3 = 0;
				foreach (PlayerFarming player2 in PlayerFarming.players)
				{
					if (!player2.isLamb)
					{
						num3++;
					}
				}
				if (num3 <= 0)
				{
					_desiredExtraSlots.Clear();
					UICoopAssignController.SetInputForSoloPlay();
					UserHelper.DisengagePlayer(1);
				}
				else
				{
					Player player = RewiredInputManager.GetPlayer(playerID2);
					if (player != null)
					{
						foreach (Joystick item in new List<Joystick>(player.controllers.Joysticks))
						{
							player.controllers.RemoveController((Controller)item);
						}
					}
					EnforceKeyboardOwnership();
					CoopManager.RefreshCoopPlayerRewired();
				}
				if (PlayerFarming.playersCount <= 1)
				{
					CoopManager.CoopActive = false;
					CoopManager.EnableCoopBlockers(false, true);
					DifficultyManager.LoadCurrentDifficulty();
				}
				TryStopAllRumble($"RemovePlayerFromMenu(slot={playerID2})");
				if (!manualRemoveRequested)
				{
					_autoRemoveSuppressedUntil = Time.unscaledTime + 8f;
					Plugin.Log.LogInfo((object)$"Auto remove cooldown armed for {8f:0.0}s");
				}
				GameManager.GetInstance().WaitForSeconds(2f, (Action)delegate
				{
					_removingPlayer = false;
					Plugin.Log.LogInfo((object)"Remove guard released");
				});
			}
			return false;
		}

		public static void PlayerFarming_OnEnable_Prefix(PlayerFarming __instance)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_001f: 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_003c: Expected O, but got Unknown
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Expected O, but got Unknown
			//IL_007f: Expected O, but got Unknown
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Expected O, but got Unknown
			if ((Object)__instance == (Object)null || __instance.isLamb || __instance.playerID <= 1 || !GameManager.IsDungeon(PlayerFarming.Location) || (Object)__instance.Spine == (Object)null)
			{
				return;
			}
			int num = __instance.playerID;
			if (num < 2 || num >= 4)
			{
				for (int i = 2; i < 4; i++)
				{
					if (CoopManager.AllPlayerGameObjects != null && i < CoopManager.AllPlayerGameObjects.Length && (Object)CoopManager.AllPlayerGameObjects[i] == (Object)((Component)__instance).gameObject)
					{
						num = i;
						break;
					}
				}
			}
			if (num < 2 || num >= 4)
			{
				return;
			}
			bool value = false;
			_pendingSpawnUseAnimation.TryGetValue(num, out value);
			if (value)
			{
				Plugin.Log.LogInfo((object)$"[MP] PlayerFarming_OnEnable_Prefix: keeping mesh visible for animated spawn slot {num}");
				return;
			}
			MeshRenderer component = ((Component)__instance.Spine).GetComponent<MeshRenderer>();
			if ((Object)component != (Object)null)
			{
				((Renderer)component).enabled = false;
			}
			Plugin.Log.LogInfo((object)$"[MP] PlayerFarming_OnEnable_Prefix: hid mesh for slot {num}");
		}

		public static void SpawnCoopPlayer_Postfix(int slot)
		{
			if (slot < 2)
			{
				_pendingSpawnSlots.Remove(slot);
				_pendingSpawnUseAnimation.Remove(slot);
			}
			else
			{
				MarkDesiredExtraSlot(slot, desired: true);
				((MonoBehaviour)CoopManager.Instance).StartCoroutine(ApplyVisualDelayed(slot));
			}
		}

		private static IEnumerator RespawnExtraSlots(int count)
		{
			_startupExtraRespawnDepth++;
			try
			{
				bool inDungeon = GameManager.IsDungeon(PlayerFarming.Location);
				float timeout = 30f;
				float waited;
				for (waited = 0f; waited < timeout; waited += 0.25f)
				{
					if ((Object)PlayerFarming.Instance != (Object)null && ((Component)PlayerFarming.Instance).gameObject.activeSelf && PlayerFarming.playersCount >= 1 && (Object)PlayerFarming.Instance.state != (Object)null && (inDungeon || (int)PlayerFarming.Instance.state.CURRENT_STATE != 13))
					{
						break;
					}
					yield return (object)new WaitForSeconds(0.25f);
				}
				if (waited >= timeout)
				{
					Plugin.Log.LogWarning((object)"RespawnExtraSlots: timed out waiting for PlayerFarming.Instance");
					yield break;
				}
				if (inDungeon)
				{
					_dungeonIntroProtectUntil = Time.unscaledTime + 8f;
				}
				yield return (object)new WaitForSeconds(1f);
				for (int s = 2; s < 2 + count && s < 4; s++)
				{
					if (_pendingSpawnSlots.Contains(s))
					{
						Plugin.Log.LogInfo((object)$"RespawnExtraSlots: slot {s} already pending, skipping duplicate startup spawn");
						continue;
					}
					if ((Object)FindActivePlayerBySlot(s) != (Object)null)
					{
						Plugin.Log.LogInfo((object)$"RespawnExtraSlots: slot {s} already active, skipping startup spawn");
						continue;
					}
					if (CoopManager.AllPlayerGameObjects != null && s < CoopManager.AllPlayerGameObjects.Length && (Object)CoopManager.AllPlayerGameObjects[s] != (Object)null && CoopManager.AllPlayerGameObjects[s].activeInHierarchy)
					{
						Plugin.Log.LogInfo((object)$"RespawnExtraSlots: slot {s} already has active gameobject, skipping startup spawn");
						continue;
					}
					Player player = RewiredInputManager.GetPlayer(s);
					if (player == null || player.controllers == null || player.controllers.Joysticks == null || player.controllers.Joysticks.Count <= 0)
					{
						Plugin.Log.LogWarning((object)$"RespawnExtraSlots: slot {s} has no joystick, skipping");
						continue;
					}
					Plugin.Log.LogInfo((object)$"RespawnExtraSlots: spawning slot {s}");
					float spawnWait = 0f;
					while ((Object)CoopManager.Instance != (Object)null && CoopManager.Instance.IsSpawningOrRemovingPlayer && spawnWait < 10f)
					{
						yield return (object)new WaitForSeconds(0.1f);
						spawnWait += 0.1f;
					}
					if (_pendingSpawnSlots.Contains(s))
					{
						Plugin.Log.LogInfo((object)$"RespawnExtraSlots: slot {s} became pending during wait, skipping duplicate startup spawn");
						continue;
					}
					if ((Object)FindActivePlayerBySlot(s) != (Object)null)
					{
						Plugin.Log.LogInfo((object)$"RespawnExtraSlots: slot {s} became active during wait, skipping duplicate startup spawn");
						continue;
					}
					_pendingSpawnSlots.Add(s);
					bool flag = false;
					_pendingSpawnUseAnimation[s] = flag;
					Plugin.Log.LogInfo((object)$"RespawnExtraSlots: slot {s} spawn mode animated={flag}");
					try
					{
						CoopManager.Instance.SpawnCoopPlayer(s, flag, -1f);
					}
					catch (Exception ex)
					{
						_pendingSpawnSlots.Remove(s);
						_pendingSpawnUseAnimation.Remove(s);
						Plugin.Log.LogWarning((object)$"RespawnExtraSlots: failed spawning slot {s}: {ex.GetType().Name}: {ex.Message}");
					}
					yield return (object)new WaitForSeconds(1f);
				}
			}
			finally
			{
				_startupExtraRespawnDepth = Math.Max(0, _startupExtraRespawnDepth - 1);
			}
		}

		public static void NewRun_Prefix()
		{
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Expected O, but got Unknown
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Expected O, but got Unknown
			_spawnedExtraWeaponPodiums = false;
			_spawnedExtraSpellPodiums = false;
			_podiumClones.Clear();
			_podiumOwnerSlotByInstanceId.Clear();
			int num = 0;
			foreach (PlayerFarming player in PlayerFarming.players)
			{
				if (!player.isLamb && player.playerID >= 2)
				{
					num++;
				}
			}
			if (num > 0)
			{
				_savedExtraSlotCount = num;
				Plugin.Log.LogInfo((object)$"NewRun_Prefix: saved {num} extra slot(s) for dungeon respawn");
			}
			_cachedHaloSR.Clear();
			_cachedHaloSprites.Clear();
			_cachedTextImages.Clear();
			_cachedSprites.Clear();
			_haloLogged.Clear();
			foreach (KeyValuePair<int, GameObject> extraHeartBar in _extraHeartBars)
			{
				if ((Object)extraHeartBar.Value != (Object)null)
				{
					Object.Destroy((Object)extraHeartBar.Value);
				}
			}
			_extraHeartBars.Clear();
			_positioningDeferred = false;
			_podiumExtraCount = 0;
			_autoRespawnRoutineRunning = false;
			_nextAutoRespawnAttemptAt = 0f;
			_nextAutoRespawnHeartbeatAt = 0f;
			_pendingSpawnSlots.Clear();
			_pendingSpawnUseAnimation.Clear();
			_pendingSpawnFirstSeenAt.Clear();
			_nextPendingBlockLogAt = 0f;
			_delayedRespawnAfterReviveQueued = false;
			_lastObservedWakeHealth = 2f;
			_lastObservedSacrificialWakeHealth = 2f;
			_sacrificialReviveWindowUntil = 0f;
			_nextExtraFalseDownedWatchdogAt.Clear();
			_postReviveRaceGuardUntil.Clear();
			_nextPostReviveForceApplyAt.Clear();
			_nextPostReviveRaceGuardHeartbeatAt = 0f;
			_lastLoggedDungeonRoomPath = null;
			_p2EntranceGuardRunning = false;
			_dungeonRoomStabilizeRunning = false;
			_lastStabilizedDungeonRoomId = int.MinValue;
			_nextDungeonRoomStabilizeCheckAt = 0f;
			_roomEntryDistanceRepositionGraceUntil = 0f;
			Plugin.Log.LogInfo((object)"NewRun_Prefix: all visual caches cleared");
		}

		public static void CoopManager_Start_Postfix()
		{
			CoopManager_Start_Postfix_Inner();
		}

		public static void CoopManager_Start_Postfix_Inner()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			RefreshDesiredExtraSlotsFromActivePlayers();
			_spawnedExtraWeaponPodiums = false;
			_spawnedExtraSpellPodiums = false;
			_delayedRespawnAfterReviveQueued = false;
			_nextAutoRespawnHeartbeatAt = 0f;
			_nextDungeonRoomStabilizeCheckAt = 0f;
			if (!CoopManager.CoopActive)
			{
				return;
			}
			if (GameManager.IsDungeon(PlayerFarming.Location) && (Object)CoopManager.Instance != (Object)null && !_p2EntranceGuardRunning)
			{
				((MonoBehaviour)CoopManager.Instance).StartCoroutine(EnsureP2DungeonEntranceNotStuckRoutine());
			}
			int savedExtraSlotCount = _savedExtraSlotCount;
			if (savedExtraSlotCount > 0)
			{
				if (GameManager.IsDungeon(PlayerFarming.Location))
				{
					_dungeonIntroProtectUntil = Time.unscaledTime + 8f;
				}
				_podiumExtraCount = savedExtraSlotCount;
				_savedExtraSlotCount = 0;
				Plugin.Log.LogInfo((object)$"CoopManager_Start_Postfix: scheduling respawn of {savedExtraSlotCount} extra slot(s)");
				((MonoBehaviour)CoopManager.Instance).StartCoroutine(RespawnExtraSlots(savedExtraSlotCount));
				_nextAutoRespawnAttemptAt = Time.unscaledTime + 5f;
			}
			else
			{
				TryAutoRespawnMissingExtraSlots("CoopManager.Start");
			}
		}

		private static IEnumerator ApplyVisualDelayed(int slot)
		{
			if (slot < 2)
			{
				_pendingSpawnSlots.Remove(slot);
				_pendingSpawnUseAnimation.Remove(slot);
				yield break;
			}
			PlayerFarming player = null;
			float waited = 0f;
			while ((Object)player == (Object)null && waited < 10f)
			{
				foreach (PlayerFarming player2 in PlayerFarming.players)
				{
					if ((Object)player2 != (Object)null && player2.playerID == slot)
					{
						player = player2;
						break;
					}
				}
				if ((Object)player == (Object)null)
				{
					yield return (object)new WaitForSeconds(0.2f);
					waited += 0.2f;
				}
			}
			if ((Object)player == (Object)null)
			{
				Plugin.Log.LogWarning((object)$"ApplyVisualDelayed: gave up waiting for playerID={slot}");
				_pendingSpawnSlots.Remove(slot);
				_pendingSpawnUseAnimation.Remove(slot);
				yield break;
			}
			bool useAnimatedSpawn = false;
			_pendingSpawnUseAnimation.TryGetValue(slot, out useAnimatedSpawn);
			bool inDungeon = GameManager.IsDungeon(PlayerFarming.Location);
			MeshRenderer meshR = null;
			if (slot >= 2 && (Object)player.Spine != (Object)null)
			{
				meshR = ((Component)player.Spine).GetComponent<MeshRenderer>();
				if (!useAnimatedSpawn && (Object)meshR != (Object)null && ((Renderer)meshR).enabled)
				{
					((Renderer)meshR).enabled = false;
				}
			}
			if (slot >= 2 && (Object)PlayerFarming.Instance != (Object)null && slot < SpawnOffsets.Length && (!useAnimatedSpawn || !inDungeon))
			{
				((Component)player).transform.position = ((Component)PlayerFarming.Instance).transform.position + SpawnOffsets[slot];
			}
			if (slot >= 2 && inDungeon)
			{
				float waitLimit = 8f;
				for (float w = 0f; w < waitLimit; w += 0.1f)
				{
					if (!((Object)player.state != (Object)null))
					{
						break;
					}
					if (!player.state.LockStateChanges && (int)player.state.CURRENT_STATE != 13 && (int)player.state.CURRENT_STATE != 49)
					{
						break;
					}
					yield return (object)new WaitForSeconds(0.1f);
				}
				yield return null;
				if ((!useAnimatedSpawn || !inDungeon) && (Object)PlayerFarming.Instance != (Object)null && slot < SpawnOffsets.Length)
				{
					((Component)player).transform.position = ((Component)PlayerFarming.Instance).transform.position + SpawnOffsets[slot];
				}
			}
			else
			{
				for (float waitLimit = 0f; waitLimit < 2f; waitLimit += 0.1f)
				{
					if (!((Object)PlayerFarming.Instance == (Object)null) && !((Object)PlayerFarming.Instance.state == (Object)null) && (int)PlayerFarming.Instance.state.CURRENT_STATE != 13)
					{
						break;
					}
					yield return (object)new WaitForSeconds(0.1f);
				}
				yield return null;
				if (slot < SpawnOffsets.Length && (Object)PlayerFarming.Instance != (Object)null)
				{
					((Component)player).transform.position = ((Component)PlayerFarming.Instance).transform.position + SpawnOffsets[slot];
				}
			}
			if (slot >= 2 && (Object)meshR != (Object)null && !((Renderer)meshR).enabled)
			{
				((Renderer)meshR).enabled = true;
			}
			if (slot < Plugin.GoatColors.Length && (Object)player.Spine != (Object)null && ((SkeletonRenderer)player.Spine).skeleton != null)
			{
				((SkeletonRenderer)player.Spine).skeleton.R = Plugin.GoatColors[slot].r;
				((SkeletonRenderer)player.Spine).skeleton.G = Plugin.GoatColors[slot].g;
				((SkeletonRenderer)player.Spine).skeleton.B = Plugin.GoatColors[slot].b;
				((SkeletonRenderer)player.Spine).skeleton.A = Plugin.GoatColors[slot].a;
			}
			if ((Object)GameManager.GetInstance() != (Object)null)
			{
				GameManager.GetInstance().AddToCamera(player.CameraBone, 0.1f);
			}
			EnsurePlayerAwakeAfterRespawn(player, $"ApplyVisualDelayed(slot={slot})");
			if (slot >= 2)
			{
				EnsureExtraSpawnStateUnlocked(player, $"ApplyVisualDelayed(slot={slot})");
			}
			if (_podiumClones.TryGetValue(slot, out var value))
			{
				foreach (Interaction_WeaponSelectionPodium item in value)
				{
					if ((Object)item != (Object)null)
					{
						EnsurePodiumCloneOwner(item, slot, player, forceInteractable: true);
						Plugin.Log.LogInfo((object)$"[MP] Refreshed podium clone state for slot {slot} on {((Object)item).name}");
					}
				}
			}
			HUD_Hearts val = player.hudHearts;
			if ((Object)val != (Object)null && (Object)val.playerFarming != (Object)player)
			{
				Plugin.Log.LogWarning((object)$"[MP] ApplyVisualDelayed: stale hudHearts owner for slot {slot}, skipping direct faithAmmo init");
				val = null;
			}
			if ((Object)val == (Object)null && _extraHeartBars.TryGetValue(slot, out var value2) && (Object)value2 != (Object)null)
			{
				HUD_Hearts component = value2.GetComponent<HUD_Hearts>();
				if ((Object)component != (Object)null && (Object)component.playerFarming == (Object)player)
				{
					val = component;
					player.hudHearts = component;
				}
			}
			if (slot >= 2 && (Object)val != (Object)null)
			{
				SyncFaithAmmoForOwner(player, val, $"ApplyVisualDelayed(slot={slot})", forceReinit: true);
			}
			_pendingSpawnSlots.Remove(slot);
			_pendingSpawnUseAnimation.Remove(slot);
			Plugin.Log.LogInfo((object)$"ApplyVisualDelayed done for slot {slot}");
		}

		private static bool IsDungeonContextLikely()
		{
			//IL_0000: 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_0014: Unknown result type (might be due to invalid IL or missing references)
			if (GameManager.IsDungeon(PlayerFarming.Location))
			{
				return true;
			}
			try
			{
				Scene activeScene = SceneManager.GetActiveScene();
				string name = ((Scene)(ref activeScene)).name;
				if (!string.IsNullOrEmpty(name) && name.IndexOf("Dungeon", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		private unsafe static bool IsReviveDesyncState(State st)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Invalid comparison between Unknown and I4
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Invalid comparison between Unknown and I4
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Invalid comparison between Unknown and I4
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Invalid comparison between Unknown and I4
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Invalid comparison between Unknown and I4
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Invalid comparison between Unknown and I4
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Invalid comparison between Unknown and I4
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Invalid comparison between Unknown and I4
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Invalid comparison between Unknown and I4
			if ((int)st != 13 && (int)st != 49 && (int)st != 24 && (int)st != 16 && !string.Equals(((object)(*(State*)(&st))/*cast due to .constrained prefix*/).ToString(), "CoopReviving", StringComparison.Ordinal) && (int)st != 63 && (int)st != 35 && (int)st != 56 && (int)st != 41 && (int)st != 42 && (int)st != 32 && (int)st != 33 && (int)st != 52)
			{
				return (int)st == 62;
			}
			return true;
		}

		private unsafe static bool IsHardDownedOrReviveState(State st)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Invalid comparison between Unknown and I4
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Invalid comparison between Unknown and I4
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Invalid comparison between Unknown and I4
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Invalid comparison between Unknown and I4
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Invalid comparison between Unknown and I4
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Invalid comparison between Unknown and I4
			if ((int)st != 63 && !string.Equals(((object)(*(State*)(&st))/*cast due to .constrained prefix*/).ToString(), "CoopReviving", StringComparison.Ordinal) && (int)st != 35 && (int)st != 56 && (int)st != 32 && (int)st != 33 && (int)st != 52)
			{
				return (int)st == 62;
			}
			return true;
		}

		private static bool IsFalseDownedVisualStateForExtra(PlayerFarming player)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Expected O, but got Unknown
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Invalid comparison between Unknown and I4
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			if ((Object)player == (Object)null || player.isLamb || player.playerID < 2)
			{
				return false;
			}
			try
			{
				if ((Object)player.health == (Object)null || ((Health)player.health).CurrentHP <= 0f)
				{
					return false;
				}
			}
			catch
			{
				return false;
			}
			try
			{
				if (Health.team2 != null && Health.team2.Count > 0)
				{
					return false;
				}
			}
			catch
			{
			}
			if (IsLikelyLegitDownedState(player))
			{
				return false;
			}
			bool flag = false;
			try
			{
				flag = player.IsKnockedOut;
			}
			catch
			{
			}
			if (!flag)
			{
				try
				{
					flag = (Object)player.state != (Object)null && (int)player.state.CURRENT_STATE == 63;
				}
				catch
				{
				}
			}
			return flag;
		}

		private static bool IsLikelyLegitDownedState(PlayerFarming player)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_003c: 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_0027: Expected O, but got Unknown
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Invalid comparison between Unknown and I4
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Expected O, but got Unknown
			if ((Object)player == (Object)null)
			{
				return false;
			}
			bool flag = false;
			try
			{
				Interaction_CoopRevive component = ((Component)player).GetComponent<Interaction_CoopRevive>();
				flag = (Object)component != (Object)null && ((Behaviour)component).enabled;
			}
			catch
			{
			}
			bool flag2 = false;
			State val = (State)0;
			try
			{
				if ((Object)player.state != (Object)null)
				{
					val = player.state.CURRENT_STATE;
					flag2 = true;
				}
			}
			catch
			{
			}
			if (flag2 && IsHardDownedOrReviveState(val))
			{
				return true;
			}
			bool flag3 = false;
			try
			{
				flag3 = player.IsKnockedOut;
			}
			catch
			{
			}
			if (!flag3)
			{
				return false;
			}
			if (flag)
			{
				return true;
			}
			if (flag2 && (int)val == 49)
			{
				return true;
			}
			try
			{
				if ((Object)player.health != (Object)null && ((Health)player.health).CurrentHP <= 0f)
				{
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		private static bool ShouldForceIdleForSpawnUnlock(State st)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return IsHardDownedOrReviveState(st);
		}

		private static bool IsLikelyReviveDesyncState(PlayerFarming player)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Expected O, but got Unknown
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)player == (Object)null || (Object)player.health == (Object)null || ((Health)player.health).CurrentHP <= 0f)
			{
				return false;
			}
			if (player.IsKnockedOut)
			{
				return true;
			}
			try
			{
				if ((Object)player.state != (Object)null && IsReviveDesyncState(player.state.CURRENT_STATE))
				{
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		private static bool NeedsWakeRecovery(PlayerFarming player)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Expected O, but got Unknown
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)player == (Object)null || (Object)player.health == (Object)null)
			{
				return false;
			}
			bool flag = ((Health)player.health)

plugins/COTLMP - Room Size Scaling.dll

Decompiled 4 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using MMBiomeGeneration;
using MMRoomGeneration;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ClassLibrary4")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ClassLibrary4")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("d7f3cd4f-957c-4fa6-a4ae-1b1904889467")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace COTLMultiplayerRoomSizeScaling
{
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("com.cotlmp.roomsizescaling", "COTL Multiplayer Room Size Scaling", "0.1.1")]
	public sealed class RoomSizeScalingPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.cotlmp.roomsizescaling";

		public const string PluginName = "COTL Multiplayer Room Size Scaling";

		public const string PluginVersion = "0.1.1";

		internal ConfigEntry<bool> Enabled;

		internal ConfigEntry<float> BiasStrength;

		internal ConfigEntry<float> KeepVanillaChance;

		internal ConfigEntry<float> BiasStrength3P;

		internal ConfigEntry<float> KeepVanillaChance3P;

		internal ConfigEntry<float> BiasStrength4P;

		internal ConfigEntry<float> KeepVanillaChance4P;

		internal ConfigEntry<float> MinAreaGainPct;

		internal ConfigEntry<bool> DebugLogging;

		private Harmony _harmony;

		internal static RoomSizeScalingPlugin Instance { get; private set; }

		internal static ManualLogSource Log => ((BaseUnityPlugin)Instance).Logger;

		private void Awake()
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Expected O, but got Unknown
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Expected O, but got Unknown
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Expected O, but got Unknown
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Expected O, but got Unknown
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Expected O, but got Unknown
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: Expected O, but got Unknown
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_020a: Expected O, but got Unknown
			Instance = this;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable generated room size scaling.");
			BiasStrength = ((BaseUnityPlugin)this).Config.Bind<float>("General", "BiasStrength", 0.7f, new ConfigDescription("Chance to attempt an upsize roll each generated room (0..1).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			KeepVanillaChance = ((BaseUnityPlugin)this).Config.Bind<float>("General", "KeepVanillaChance", 0.35f, new ConfigDescription("Chance to keep vanilla selection for room-flow variety (0..1).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			BiasStrength3P = ((BaseUnityPlugin)this).Config.Bind<float>("Multiplayer", "BiasStrength3P", 0.85f, new ConfigDescription("Upsize roll chance when 3 players are active.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			KeepVanillaChance3P = ((BaseUnityPlugin)this).Config.Bind<float>("Multiplayer", "KeepVanillaChance3P", 0.25f, new ConfigDescription("Chance to keep vanilla room size when 3 players are active.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			BiasStrength4P = ((BaseUnityPlugin)this).Config.Bind<float>("Multiplayer", "BiasStrength4P", 0.9f, new ConfigDescription("Upsize roll chance when 4+ players are active.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			KeepVanillaChance4P = ((BaseUnityPlugin)this).Config.Bind<float>("Multiplayer", "KeepVanillaChance4P", 0.12f, new ConfigDescription("Chance to keep vanilla room size when 4+ players are active.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			MinAreaGainPct = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MinAreaGainPct", 0.15f, new ConfigDescription("Minimum area gain required to swap to a larger generated room piece.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
			DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugLogging", false, "Verbose room-size selection logs.");
			_harmony = new Harmony("com.cotlmp.roomsizescaling");
			_harmony.PatchAll(Assembly.GetExecutingAssembly());
			((BaseUnityPlugin)this).Logger.LogInfo((object)"COTL Multiplayer Room Size Scaling v0.1.1 loaded.");
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	[HarmonyPatch]
	internal static class GenerateRoomGetRandomEncounterIslandPatch
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(typeof(GenerateRoom), "GetRandomEncounterIsland", (Type[])null, (Type[])null);
		}

		private static void Postfix(GenerateRoom __instance, ref IslandPiece __result)
		{
			RoomSizeScalingPlugin instance = RoomSizeScalingPlugin.Instance;
			if ((Object)(object)instance == (Object)null || !instance.Enabled.Value || (Object)(object)__instance == (Object)null || (Object)(object)__result == (Object)null)
			{
				return;
			}
			BiomeRoom val = (((Object)(object)BiomeGenerator.Instance != (Object)null) ? BiomeGenerator.Instance.CurrentRoom : null);
			if (val != null && !string.IsNullOrEmpty(val.GameObjectPath))
			{
				return;
			}
			List<IslandPiece> startPieces = __instance.StartPieces;
			if (startPieces == null || startPieces.Count == 0)
			{
				return;
			}
			int num = Mathf.Max(1, PlayerFarming.playersCount);
			float value = instance.KeepVanillaChance.Value;
			float value2 = instance.BiasStrength.Value;
			if (num >= 4)
			{
				value = instance.KeepVanillaChance4P.Value;
				value2 = instance.BiasStrength4P.Value;
			}
			else if (num >= 3)
			{
				value = instance.KeepVanillaChance3P.Value;
				value2 = instance.BiasStrength3P.Value;
			}
			if (Random.value < value || Random.value > value2)
			{
				return;
			}
			List<IslandPiece> list = CollectAvailableCandidates(startPieces);
			if (list.Count == 0)
			{
				return;
			}
			float area = GetArea(__result);
			IslandPiece val2 = __result;
			float num2 = area;
			for (int i = 0; i < list.Count; i++)
			{
				IslandPiece val3 = list[i];
				float area2 = GetArea(val3);
				if (area2 > num2)
				{
					num2 = area2;
					val2 = val3;
				}
			}
			if ((Object)(object)val2 == (Object)null || val2 == __result)
			{
				return;
			}
			float num3 = area * (1f + instance.MinAreaGainPct.Value);
			if (!(num2 < num3))
			{
				if (instance.DebugLogging.Value)
				{
					RoomSizeScalingPlugin.Log.LogInfo((object)$"[RoomSizeScale] Up-sized generated room piece area {area:0.00} -> {num2:0.00} (players={num}, keep={value:0.00}, bias={value2:0.00})");
				}
				__result = val2;
			}
		}

		private static List<IslandPiece> CollectAvailableCandidates(List<IslandPiece> source)
		{
			List<IslandPiece> list = new List<IslandPiece>();
			for (int i = 0; i < source.Count; i++)
			{
				IslandPiece val = source[i];
				if ((Object)(object)val == (Object)null || val.Encounters == null || val.Encounters.ObjectList == null)
				{
					continue;
				}
				int num = 0;
				int num2 = 0;
				for (int j = 0; j < val.Encounters.ObjectList.Count; j++)
				{
					GameObjectAndProbability val2 = val.Encounters.ObjectList[j];
					if (val2 != null && IsAvailableOnCurrentLayer(val2))
					{
						num++;
						if (!string.IsNullOrEmpty(val2.GameObjectPath) && BiomeGenerator.EncounterAlreadyUsed(val2.GameObjectPath))
						{
							num2++;
						}
					}
				}
				if (num > 0 && num2 < num)
				{
					list.Add(val);
				}
			}
			if (list.Count == 0)
			{
				for (int k = 0; k < source.Count; k++)
				{
					if ((Object)(object)source[k] != (Object)null)
					{
						list.Add(source[k]);
					}
				}
			}
			return list;
		}

		private static float GetArea(IslandPiece piece)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: 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)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: 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_0071: 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_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)piece == (Object)null)
			{
				return 0f;
			}
			PolygonCollider2D collider = piece.Collider;
			if ((Object)(object)collider == (Object)null)
			{
				return 0f;
			}
			Vector2[] points = collider.points;
			Bounds bounds;
			if (points == null || points.Length < 3)
			{
				bounds = ((Collider2D)collider).bounds;
				float x = ((Bounds)(ref bounds)).size.x;
				bounds = ((Collider2D)collider).bounds;
				return x * ((Bounds)(ref bounds)).size.y;
			}
			double num = 0.0;
			for (int i = 0; i < points.Length; i++)
			{
				Vector2 val = points[i];
				Vector2 val2 = points[(i + 1) % points.Length];
				num += (double)(val.x * val2.y - val2.x * val.y);
			}
			float num2 = Mathf.Abs((float)(num * 0.5));
			if (!(num2 > 0f))
			{
				bounds = ((Collider2D)collider).bounds;
				float x2 = ((Bounds)(ref bounds)).size.x;
				bounds = ((Collider2D)collider).bounds;
				return x2 * ((Bounds)(ref bounds)).size.y;
			}
			return num2;
		}

		private static bool IsAvailableOnCurrentLayer(GameObjectAndProbability encounter)
		{
			if (GameManager.DungeonUseAllLayers)
			{
				return true;
			}
			return GameManager.CurrentDungeonLayer switch
			{
				1 => encounter.LayerOne, 
				2 => encounter.LayerTwo, 
				3 => encounter.LayerThree, 
				4 => encounter.LayerFour, 
				_ => false, 
			};
		}
	}
}

plugins/COTLMP - Tarot Addon.dll

Decompiled 4 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using MMBiomeGeneration;
using Microsoft.CodeAnalysis;
using Rewired;
using Unify.Input;
using UnityEngine;
using src.UINavigator;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ClassLibrary2")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ClassLibrary2")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("528f5a23-6efd-4f6b-85e8-1d3d74dfddd6")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace COTLMultiplayerTarotAddon
{
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("com.cotlmp.tarotaddon", "COTL Multiplayer Tarot Addon", "0.1.0")]
	public sealed class TarotAddonPlugin : BaseUnityPlugin
	{
		internal static ManualLogSource Log;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			_harmony = new Harmony("com.cotlmp.tarotaddon");
			TarotPatches.EnsureClauneckTarotRoomsOnly(null, "Awake");
			PatchIfFound(AccessTools.Method(typeof(Interaction_Tarot), "OnInteract", new Type[1] { typeof(StateMachine) }, (Type[])null), "InteractionTarot_OnInteract_Prefix", null, "Interaction_Tarot.OnInteract(StateMachine)");
			PatchIfFound(AccessTools.Method(typeof(Interaction_Tarot), "DelayEffectsRoutine", new Type[3]
			{
				typeof(TarotCard),
				typeof(float),
				typeof(PlayerFarming)
			}, (Type[])null), "InteractionTarot_DelayEffectsRoutine_Prefix", "InteractionTarot_DelayEffectsRoutine_Postfix", "Interaction_Tarot.DelayEffectsRoutine");
			PatchIfFound(AccessTools.Method(typeof(Interaction_Tarot), "OnDisable", (Type[])null, (Type[])null), null, "InteractionTarot_OnDisable_Postfix", "Interaction_Tarot.OnDisable");
			PatchIfFound(AccessTools.Method(typeof(Interaction_Tarot), "DoRoutine", (Type[])null, (Type[])null), "InteractionTarot_DoRoutine_Prefix", null, "Interaction_Tarot.DoRoutine()");
			PatchIfFound(AccessTools.Method(typeof(Interaction_TarotCard), "OnInteract", new Type[1] { typeof(StateMachine) }, (Type[])null), "InteractionTarotCard_OnInteract_Prefix", null, "Interaction_TarotCard.OnInteract(StateMachine)");
			PatchIfFound(AccessTools.Method(typeof(Interaction_TarotCard), "DoRoutine", (Type[])null, (Type[])null), "InteractionTarotCard_DoRoutine_Prefix", null, "Interaction_TarotCard.DoRoutine()");
			PatchIfFound(AccessTools.Method(typeof(Interaction_TarotCard), "DelayEffectsRoutine", new Type[1] { typeof(PlayerFarming) }, (Type[])null), "InteractionTarotCard_DelayEffectsRoutine_Prefix", null, "Interaction_TarotCard.DelayEffectsRoutine(PlayerFarming)");
			PatchIfFound(AccessTools.Method(typeof(Interaction_TarotCard), "OnDisable", (Type[])null, (Type[])null), null, "InteractionTarotCard_OnDisable_Postfix", "Interaction_TarotCard.OnDisable");
			PatchIfFound(AccessTools.Method(typeof(Interaction_Chest), "SelectReward", (Type[])null, (Type[])null), null, "InteractionChest_SelectReward_Postfix", "Interaction_Chest.SelectReward()");
			PatchIfFound(AccessTools.Method(typeof(Interaction_Chest), "OnDisableInteraction", Type.EmptyTypes, (Type[])null), null, "InteractionChest_OnDisableInteraction_Postfix", "Interaction_Chest.OnDisableInteraction()");
			PatchIfFound(AccessTools.Method(typeof(InventoryItem), "Spawn", new Type[5]
			{
				typeof(ITEM_TYPE),
				typeof(int),
				typeof(Vector3),
				typeof(float),
				typeof(Action<PickUp>)
			}, (Type[])null), "InventoryItem_Spawn_Prefix", null, "InventoryItem.Spawn(ITEM_TYPE,int,Vector3,float,Action<PickUp>)");
			PatchIfFound(AccessTools.Method(typeof(BiomeGenerator), "Generate", Type.EmptyTypes, (Type[])null), "BiomeGenerator_Generate_Prefix", null, "BiomeGenerator.Generate()");
			PatchIfFound(AccessTools.Method(typeof(Shrines), "OnInteract", new Type[1] { typeof(StateMachine) }, (Type[])null), "Shrines_OnInteract_Prefix", null, "Shrines.OnInteract(StateMachine)");
			PatchIfFound(AccessTools.Method(typeof(Shrines), "DrawCardRoutine", new Type[1] { typeof(PlayerFarming) }, (Type[])null), "Shrines_DrawCardRoutine_Prefix", null, "Shrines.DrawCardRoutine(PlayerFarming)");
			PatchIfFound(AccessTools.Method(typeof(Shrines), "DelayEffectsRoutine", new Type[3]
			{
				typeof(TarotCard),
				typeof(float),
				typeof(PlayerFarming)
			}, (Type[])null), "Shrines_DelayEffectsRoutine_Prefix", "Shrines_DelayEffectsRoutine_Postfix", "Shrines.DelayEffectsRoutine(TarotCard,float,PlayerFarming)");
			PatchIfFound(AccessTools.Method(typeof(Shrines), "OnDisable", Type.EmptyTypes, (Type[])null), null, "Shrines_OnDisable_Postfix", "Shrines.OnDisable()");
			Log.LogInfo((object)"Tarot addon loaded.");
		}

		private void PatchIfFound(MethodInfo original, string prefix = null, string postfix = null, string originalLabel = null)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Expected O, but got Unknown
			if (original == null)
			{
				Log.LogWarning((object)("[Tarot] Skipped patch (method missing): " + (originalLabel ?? "unknown")));
				return;
			}
			HarmonyMethod val = null;
			HarmonyMethod val2 = null;
			if (!string.IsNullOrEmpty(prefix))
			{
				val = new HarmonyMethod(typeof(TarotPatches), prefix, (Type[])null);
			}
			if (!string.IsNullOrEmpty(postfix))
			{
				val2 = new HarmonyMethod(typeof(TarotPatches), postfix, (Type[])null);
			}
			_harmony.Patch((MethodBase)original, val, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}
	}
	internal static class TarotPatches
	{
		private static bool VERBOSE_LOGS = false;

		private const string ClauneckTarotRoomPath = "Assets/_Rooms/Reward Room Tarot.prefab";

		private const string CardsOnlyTarotRoomPath = "Assets/_Rooms/Special Free Tarot Cards.prefab";

		private static bool _loggedTarotRoomPolicy = false;

		private static readonly Dictionary<int, PlayerFarming> TarotOwnerByInstance = new Dictionary<int, PlayerFarming>();

		private static readonly Dictionary<int, Queue<int>> PendingTarotQueueByInstance = new Dictionary<int, Queue<int>>();

		private static readonly HashSet<int> WarnedSuppressedDiscardByInstance = new HashSet<int>();

		private static readonly Dictionary<int, PlayerFarming> TarotCardOwnerByInstance = new Dictionary<int, PlayerFarming>();

		private static readonly Dictionary<int, PlayerFarming> ShrineTarotOwnerByInstance = new Dictionary<int, PlayerFarming>();

		private static readonly Dictionary<int, Queue<int>> PendingShrineTarotQueueByInstance = new Dictionary<int, Queue<int>>();

		private static readonly HashSet<int> WarnedSuppressedShrineDiscardByInstance = new HashSet<int>();

		private static readonly Dictionary<int, int> PendingChestTarotExtraByChest = new Dictionary<int, int>();

		private static readonly Dictionary<int, float> PendingChestTarotExpiryByChest = new Dictionary<int, float>();

		private static int PendingChestTarotExtraAny = 0;

		private static float PendingChestTarotAnyExpiry = -1f;

		private static int PendingChestTarotSourceChestId = 0;

		private static readonly Dictionary<int, float> RecentChestRewardWindowByChest = new Dictionary<int, float>();

		private static readonly Dictionary<int, Vector3> RecentChestRewardPositionByChest = new Dictionary<int, Vector3>();

		private static int RecentChestRewardSourceChestId = 0;

		private static float RecentChestRewardAnyExpiry = -1f;

		private const float RecentChestRewardWindowSeconds = 8f;

		private const float ChestTarotFallbackRadius = 6f;

		private static readonly FieldInfo InteractionChestRewardField = typeof(Interaction_Chest).GetField("Reward", BindingFlags.Instance | BindingFlags.NonPublic);

		private static bool IsSpawningExtraChestTarot = false;

		public static void BiomeGenerator_Generate_Prefix(BiomeGenerator __instance)
		{
			EnsureClauneckTarotRoomsOnly(__instance, "BiomeGenerator.Generate");
		}

		internal static void EnsureClauneckTarotRoomsOnly(BiomeGenerator biomeGenerator, string reason)
		{
			try
			{
				if (!_loggedTarotRoomPolicy)
				{
					_loggedTarotRoomPolicy = true;
					TarotAddonPlugin.Log.LogInfo((object)"[TarotRoom] Vanilla tarot-room behavior preserved (no tarot room path filtering).");
				}
			}
			catch (Exception ex)
			{
				TarotAddonPlugin.Log.LogWarning((object)("[TarotRoom] Failed to enforce tarot room policy during " + reason + ": " + ex.Message));
			}
		}

		private static int RemoveDisallowedTarotRoomPaths(List<string> roomPaths)
		{
			return 0;
		}

		private static bool IsDisallowedTarotRoomPath(string candidate)
		{
			if (string.IsNullOrEmpty(candidate))
			{
				return false;
			}
			string text = NormalizeRoomPath(candidate);
			if (string.Equals(text, NormalizeRoomPath("Assets/_Rooms/Reward Room Tarot.prefab"), StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			return text.IndexOf("tarot", StringComparison.OrdinalIgnoreCase) >= 0;
		}

		private static string NormalizeRoomPath(string path)
		{
			if (string.IsNullOrEmpty(path))
			{
				return string.Empty;
			}
			return path.Replace('\\', '/').Trim();
		}

		public static void InteractionTarot_OnInteract_Prefix(Interaction_Tarot __instance, StateMachine state)
		{
			if ((Object)(object)__instance == (Object)null || (Object)(object)state == (Object)null)
			{
				return;
			}
			PlayerFarming val = ResolvePlayerFromState(state);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			TrySetAllowedInputPlayer(val);
			SetInteractionContext(__instance, val, state);
			int instanceID = ((Object)__instance).GetInstanceID();
			if (PendingTarotQueueByInstance.ContainsKey(instanceID) || __instance.Activated)
			{
				return;
			}
			Queue<int> queue = BuildTarotQueue(val);
			if (queue.Count > 0)
			{
				PendingTarotQueueByInstance[instanceID] = queue;
				if (VERBOSE_LOGS)
				{
					TarotAddonPlugin.Log.LogInfo((object)("[Tarot] Queue created for instance " + instanceID + "."));
				}
			}
		}

		public static bool InteractionTarot_DelayEffectsRoutine_Prefix(Interaction_Tarot __instance, TarotCard card, float delay, ref PlayerFarming playerFarming, ref IEnumerator __result)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			PlayerFarming trackedTarotOwner = GetTrackedTarotOwner(__instance);
			if ((Object)(object)trackedTarotOwner == (Object)null)
			{
				return true;
			}
			if (trackedTarotOwner.playerID >= 2 && (Object)(object)playerFarming != (Object)(object)trackedTarotOwner)
			{
				int instanceID = ((Object)__instance).GetInstanceID();
				if (WarnedSuppressedDiscardByInstance.Add(instanceID))
				{
					TarotAddonPlugin.Log.LogInfo((object)("[Tarot] Suppressed extra discard grant from P" + (trackedTarotOwner.playerID + 1) + "."));
				}
				__result = SuppressedDelayRoutine(delay);
				return false;
			}
			if ((Object)(object)playerFarming == (Object)null)
			{
				playerFarming = trackedTarotOwner;
			}
			return true;
		}

		public static void InteractionTarot_DelayEffectsRoutine_Postfix(Interaction_Tarot __instance, TarotCard card, float delay, PlayerFarming playerFarming, ref IEnumerator __result)
		{
			if (!((Object)(object)__instance == (Object)null) && __result != null)
			{
				PlayerFarming trackedTarotOwner = GetTrackedTarotOwner(__instance);
				if (!((Object)(object)trackedTarotOwner == (Object)null) && !((Object)(object)playerFarming != (Object)(object)trackedTarotOwner))
				{
					__result = WrapAndAdvanceTarotQueue(__result, __instance, trackedTarotOwner);
				}
			}
		}

		public static void InteractionTarot_OnDisable_Postfix(Interaction_Tarot __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				int instanceID = ((Object)__instance).GetInstanceID();
				TarotOwnerByInstance.Remove(instanceID);
				PendingTarotQueueByInstance.Remove(instanceID);
				WarnedSuppressedDiscardByInstance.Remove(instanceID);
			}
		}

		public static void InteractionTarot_DoRoutine_Prefix(Interaction_Tarot __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			int instanceID = ((Object)__instance).GetInstanceID();
			PlayerFarming trackedTarotOwner = GetTrackedTarotOwner(__instance);
			if ((Object)(object)trackedTarotOwner == (Object)null)
			{
				return;
			}
			StateMachine val = TryResolveInteractionState(__instance);
			if ((Object)(object)val == (Object)null && (Object)(object)trackedTarotOwner != (Object)null)
			{
				val = trackedTarotOwner.state;
			}
			TrySetAllowedInputPlayer(trackedTarotOwner);
			SetInteractionContext(__instance, trackedTarotOwner, val);
			if (!PendingTarotQueueByInstance.ContainsKey(instanceID))
			{
				Queue<int> queue = BuildTarotQueue(trackedTarotOwner);
				if (queue.Count > 0)
				{
					PendingTarotQueueByInstance[instanceID] = queue;
					TarotAddonPlugin.Log.LogInfo((object)("[Tarot] Bootstrapped chained queue for P" + (trackedTarotOwner.playerID + 1) + "."));
				}
			}
		}

		public static void Shrines_OnInteract_Prefix(Shrines __instance, StateMachine state)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Invalid comparison between Unknown and I4
			if ((Object)(object)__instance == (Object)null || (Object)(object)state == (Object)null || (int)__instance.TypeOfShrine != 7)
			{
				return;
			}
			PlayerFarming val = ResolvePlayerFromState(state);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			TrySetAllowedInputPlayer(val);
			SetInteractionContext(__instance, val, state);
			int instanceID = ((Object)__instance).GetInstanceID();
			ShrineTarotOwnerByInstance[instanceID] = val;
			if (!PendingShrineTarotQueueByInstance.ContainsKey(instanceID))
			{
				Queue<int> queue = BuildTarotQueue(val);
				if (queue.Count > 0)
				{
					PendingShrineTarotQueueByInstance[instanceID] = queue;
					TarotAddonPlugin.Log.LogInfo((object)("[ShrineTarot] Queue created for instance " + instanceID + "."));
				}
			}
		}

		public static void Shrines_DrawCardRoutine_Prefix(Shrines __instance, ref PlayerFarming playerFarming)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Invalid comparison between Unknown and I4
			if ((Object)(object)__instance == (Object)null || (int)__instance.TypeOfShrine != 7)
			{
				return;
			}
			PlayerFarming val = playerFarming;
			if ((Object)(object)val == (Object)null)
			{
				val = GetTrackedShrineTarotOwner(__instance);
			}
			if ((Object)(object)val == (Object)null)
			{
				val = TryResolveInteractionPlayer(__instance);
			}
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			playerFarming = val;
			TrySetAllowedInputPlayer(val);
			SetInteractionContext(__instance, val, val.state);
			int instanceID = ((Object)__instance).GetInstanceID();
			ShrineTarotOwnerByInstance[instanceID] = val;
			if (!PendingShrineTarotQueueByInstance.ContainsKey(instanceID))
			{
				Queue<int> queue = BuildTarotQueue(val);
				if (queue.Count > 0)
				{
					PendingShrineTarotQueueByInstance[instanceID] = queue;
					TarotAddonPlugin.Log.LogInfo((object)("[ShrineTarot] Bootstrapped queue for P" + (val.playerID + 1) + "."));
				}
			}
		}

		public static bool Shrines_DelayEffectsRoutine_Prefix(Shrines __instance, TarotCard card, float delay, ref PlayerFarming playerFarming, ref IEnumerator __result)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			if ((int)__instance.TypeOfShrine != 7)
			{
				return true;
			}
			PlayerFarming trackedShrineTarotOwner = GetTrackedShrineTarotOwner(__instance);
			if ((Object)(object)trackedShrineTarotOwner == (Object)null)
			{
				return true;
			}
			if (trackedShrineTarotOwner.playerID >= 2 && (Object)(object)playerFarming != (Object)(object)trackedShrineTarotOwner)
			{
				int instanceID = ((Object)__instance).GetInstanceID();
				if (WarnedSuppressedShrineDiscardByInstance.Add(instanceID))
				{
					TarotAddonPlugin.Log.LogInfo((object)("[ShrineTarot] Suppressed extra discard grant from P" + (trackedShrineTarotOwner.playerID + 1) + "."));
				}
				__result = SuppressedDelayRoutine(delay);
				return false;
			}
			if ((Object)(object)playerFarming == (Object)null)
			{
				playerFarming = trackedShrineTarotOwner;
			}
			return true;
		}

		public static void Shrines_DelayEffectsRoutine_Postfix(Shrines __instance, TarotCard card, float delay, PlayerFarming playerFarming, ref IEnumerator __result)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Invalid comparison between Unknown and I4
			if (!((Object)(object)__instance == (Object)null) && __result != null && (int)__instance.TypeOfShrine == 7)
			{
				PlayerFarming trackedShrineTarotOwner = GetTrackedShrineTarotOwner(__instance);
				if (!((Object)(object)trackedShrineTarotOwner == (Object)null) && !((Object)(object)playerFarming != (Object)(object)trackedShrineTarotOwner))
				{
					__result = WrapAndAdvanceShrineTarotQueue(__result, __instance, trackedShrineTarotOwner);
				}
			}
		}

		public static void Shrines_OnDisable_Postfix(Shrines __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				CleanupShrineTarotState(((Object)__instance).GetInstanceID());
			}
		}

		public static void InteractionTarotCard_OnInteract_Prefix(Interaction_TarotCard __instance, StateMachine state)
		{
			if (!((Object)(object)__instance == (Object)null) && !((Object)(object)state == (Object)null))
			{
				PlayerFarming val = ResolvePlayerFromState(state);
				if (!((Object)(object)val == (Object)null))
				{
					TrySetAllowedInputPlayer(val);
					SetInteractionContext(__instance, val, state);
					int instanceID = ((Object)__instance).GetInstanceID();
					TarotCardOwnerByInstance[instanceID] = val;
				}
			}
		}

		public static void InteractionTarotCard_DoRoutine_Prefix(Interaction_TarotCard __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			int instanceID = ((Object)__instance).GetInstanceID();
			if (!TarotCardOwnerByInstance.TryGetValue(instanceID, out var value) || (Object)(object)value == (Object)null)
			{
				value = TryResolveInteractionPlayer(__instance);
			}
			if (!((Object)(object)value == (Object)null))
			{
				StateMachine val = TryResolveInteractionState(__instance);
				if ((Object)(object)val == (Object)null && (Object)(object)value != (Object)null)
				{
					val = value.state;
				}
				TrySetAllowedInputPlayer(value);
				SetInteractionContext(__instance, value, val);
				TarotCardOwnerByInstance[instanceID] = value;
			}
		}

		public static bool InteractionTarotCard_DelayEffectsRoutine_Prefix(Interaction_TarotCard __instance, ref PlayerFarming playerFarming)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			int instanceID = ((Object)__instance).GetInstanceID();
			if (!TarotCardOwnerByInstance.TryGetValue(instanceID, out var value))
			{
				value = TryResolveInteractionPlayer(__instance);
			}
			if ((Object)(object)value == (Object)null)
			{
				return true;
			}
			if ((Object)(object)playerFarming == (Object)null || (Object)(object)playerFarming != (Object)(object)value)
			{
				playerFarming = value;
				if (VERBOSE_LOGS)
				{
					TarotAddonPlugin.Log.LogInfo((object)("[TarotCard] Rebound reward ownership to P" + (value.playerID + 1) + "."));
				}
			}
			return true;
		}

		public unsafe static void InteractionChest_SelectReward_Postfix(Interaction_Chest __instance)
		{
			//IL_0017: 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_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: 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)
			//IL_00b5: Invalid comparison between Unknown and I4
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			int instanceID = ((Object)__instance).GetInstanceID();
			ClearPendingChestTarot(instanceID);
			Vector3 value = Vector3.zero;
			bool flag = false;
			try
			{
				value = ((Component)__instance).transform.position;
				flag = true;
			}
			catch
			{
			}
			int effectivePlayerCountForChestTarot = GetEffectivePlayerCountForChestTarot();
			int num = Mathf.Clamp(effectivePlayerCountForChestTarot - 2, 0, 2);
			if (num <= 0)
			{
				return;
			}
			RecentChestRewardWindowByChest[instanceID] = Time.unscaledTime + 8f;
			if (flag)
			{
				RecentChestRewardPositionByChest[instanceID] = value;
			}
			RecentChestRewardSourceChestId = instanceID;
			RecentChestRewardAnyExpiry = Time.unscaledTime + 6f;
			if (TryGetChestReward(__instance, out var reward))
			{
				TarotAddonPlugin.Log.LogInfo((object)("[TarotCard] Chest reward rolled: " + ((object)(*(ITEM_TYPE*)(&reward))/*cast due to .constrained prefix*/).ToString()));
				if ((int)reward != 26)
				{
					TarotAddonPlugin.Log.LogInfo((object)"[TarotCard] Chest did not roll tarot card this time.");
					return;
				}
				PendingChestTarotExtraByChest[instanceID] = num;
				PendingChestTarotExpiryByChest[instanceID] = Time.unscaledTime + 10f;
				PendingChestTarotExtraAny = num;
				PendingChestTarotAnyExpiry = Time.unscaledTime + 6f;
				PendingChestTarotSourceChestId = instanceID;
				TarotAddonPlugin.Log.LogInfo((object)("[TarotCard] Chest tarot bonus prepared: +" + num + " card(s) for effectivePlayers=" + effectivePlayerCountForChestTarot + " (playersCount=" + PlayerFarming.playersCount + ")."));
			}
		}

		public static void InteractionChest_OnDisableInteraction_Postfix(Interaction_Chest __instance)
		{
			_ = (Object)(object)__instance == (Object)null;
		}

		public static void InventoryItem_Spawn_Prefix(ITEM_TYPE type, int quantity, Vector3 position, float StartSpeed, Action<PickUp> result)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			//IL_028b: Unknown result type (might be due to invalid IL or missing references)
			//IL_028d: Unknown result type (might be due to invalid IL or missing references)
			//IL_028e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0290: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: 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_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			if (IsSpawningExtraChestTarot || (int)type != 26 || quantity <= 0)
			{
				return;
			}
			int value = 0;
			int num = 0;
			Interaction_Chest instance = Interaction_Chest.Instance;
			if ((Object)(object)instance != (Object)null)
			{
				int instanceID = ((Object)instance).GetInstanceID();
				num = instanceID;
				if (PendingChestTarotExpiryByChest.TryGetValue(instanceID, out var value2) && Time.unscaledTime > value2)
				{
					ClearPendingChestTarot(instanceID);
				}
				else if (!PendingChestTarotExtraByChest.TryGetValue(instanceID, out value))
				{
					value = 0;
				}
			}
			if (value <= 0 && PendingChestTarotExtraAny > 0)
			{
				if (PendingChestTarotAnyExpiry > 0f && Time.unscaledTime > PendingChestTarotAnyExpiry)
				{
					ClearPendingChestTarotAny();
				}
				else
				{
					value = PendingChestTarotExtraAny;
					if (num == 0)
					{
						num = PendingChestTarotSourceChestId;
					}
				}
			}
			if (value <= 0)
			{
				Interaction_Chest instance2 = Interaction_Chest.Instance;
				if ((Object)(object)instance2 != (Object)null)
				{
					int instanceID2 = ((Object)instance2).GetInstanceID();
					if (RecentChestRewardWindowByChest.TryGetValue(instanceID2, out var value3))
					{
						if (Time.unscaledTime > value3)
						{
							ClearRecentChestRewardState(instanceID2);
						}
						else
						{
							bool flag = true;
							try
							{
								flag = Vector3.Distance(((Component)instance2).transform.position, position) <= 6f;
							}
							catch
							{
								flag = true;
							}
							if (flag)
							{
								int effectivePlayerCountForChestTarot = GetEffectivePlayerCountForChestTarot();
								value = Mathf.Clamp(effectivePlayerCountForChestTarot - 2, 0, 2);
								if (value > 0)
								{
									num = instanceID2;
									TarotAddonPlugin.Log.LogInfo((object)("[TarotCard] Chest tarot fallback engaged: +" + value + " card(s) for effectivePlayers=" + effectivePlayerCountForChestTarot + "."));
								}
							}
						}
					}
				}
				else if (RecentChestRewardAnyExpiry > 0f)
				{
					if (Time.unscaledTime > RecentChestRewardAnyExpiry)
					{
						if (RecentChestRewardSourceChestId != 0)
						{
							RecentChestRewardPositionByChest.Remove(RecentChestRewardSourceChestId);
						}
						RecentChestRewardAnyExpiry = -1f;
						RecentChestRewardSourceChestId = 0;
					}
					else
					{
						int recentChestRewardSourceChestId = RecentChestRewardSourceChestId;
						bool flag2 = true;
						if (recentChestRewardSourceChestId != 0 && RecentChestRewardPositionByChest.TryGetValue(recentChestRewardSourceChestId, out var value4))
						{
							flag2 = Vector3.Distance(value4, position) <= 6f;
						}
						if (flag2)
						{
							int effectivePlayerCountForChestTarot2 = GetEffectivePlayerCountForChestTarot();
							value = Mathf.Clamp(effectivePlayerCountForChestTarot2 - 2, 0, 2);
							if (value > 0)
							{
								num = recentChestRewardSourceChestId;
								TarotAddonPlugin.Log.LogInfo((object)("[TarotCard] Chest tarot fallback(any) engaged: +" + value + " card(s) for effectivePlayers=" + effectivePlayerCountForChestTarot2 + "."));
							}
						}
					}
				}
			}
			if (value <= 0)
			{
				return;
			}
			try
			{
				IsSpawningExtraChestTarot = true;
				Vector3 val = default(Vector3);
				for (int i = 0; i < value; i++)
				{
					((Vector3)(ref val))..ctor(Random.Range(-0.35f, 0.35f), Random.Range(-0.2f, 0.2f), 0f);
					InventoryItem.Spawn(type, 1, position + val, StartSpeed, result);
				}
				TarotAddonPlugin.Log.LogInfo((object)("[TarotCard] Chest tarot multiplied: base drop + " + value + " extra."));
			}
			catch (Exception ex)
			{
				TarotAddonPlugin.Log.LogWarning((object)("[TarotCard] Failed chest tarot multiply: " + ex.Message));
			}
			finally
			{
				IsSpawningExtraChestTarot = false;
				if (num != 0)
				{
					if (PendingChestTarotExpiryByChest.TryGetValue(num, out var value5) && Time.unscaledTime > value5)
					{
						ClearPendingChestTarot(num);
					}
					if (RecentChestRewardWindowByChest.TryGetValue(num, out var value6) && Time.unscaledTime > value6)
					{
						ClearRecentChestRewardState(num);
					}
				}
				if (PendingChestTarotAnyExpiry > 0f && Time.unscaledTime > PendingChestTarotAnyExpiry)
				{
					ClearPendingChestTarotAny();
				}
				if (RecentChestRewardAnyExpiry > 0f && Time.unscaledTime > RecentChestRewardAnyExpiry)
				{
					if (RecentChestRewardSourceChestId != 0)
					{
						RecentChestRewardPositionByChest.Remove(RecentChestRewardSourceChestId);
					}
					RecentChestRewardAnyExpiry = -1f;
					RecentChestRewardSourceChestId = 0;
				}
			}
		}

		public static void InteractionTarotCard_OnDisable_Postfix(Interaction_TarotCard __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				CleanupTarotCardState(((Object)__instance).GetInstanceID());
			}
		}

		private static void CleanupTarotCardState(int interactionId)
		{
			TarotCardOwnerByInstance.Remove(interactionId);
		}

		private static bool TryGetChestReward(Interaction_Chest chest, out ITEM_TYPE reward)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected I4, but got Unknown
			reward = (ITEM_TYPE)0;
			if ((Object)(object)chest == (Object)null || InteractionChestRewardField == null)
			{
				return false;
			}
			try
			{
				object value = InteractionChestRewardField.GetValue(chest);
				if (value is ITEM_TYPE)
				{
					reward = (ITEM_TYPE)(int)(ITEM_TYPE)value;
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		private static int GetEffectivePlayerCountForChestTarot()
		{
			int num = Mathf.Clamp(PlayerFarming.playersCount, 0, 4);
			HashSet<int> hashSet = new HashSet<int>();
			int num2 = CountRelaxedActivePlayerSlots();
			if (CoopManager.AllPlayerGameObjects != null)
			{
				int num3 = Mathf.Min(CoopManager.AllPlayerGameObjects.Length, 4);
				for (int i = 0; i < num3; i++)
				{
					GameObject val = CoopManager.AllPlayerGameObjects[i];
					if ((Object)(object)val != (Object)null && val.activeInHierarchy)
					{
						hashSet.Add(i);
					}
				}
			}
			int num4 = Mathf.Max(hashSet.Count, num2);
			int num5 = 0;
			for (int j = 0; j < 4; j++)
			{
				if (HasInputAssignedToSlot(j))
				{
					num5++;
				}
			}
			int num6 = Mathf.Max(new int[3] { num, num4, num5 });
			if (num6 <= 0)
			{
				num6 = 1;
			}
			return Mathf.Clamp(num6, 1, 4);
		}

		private static int CountRelaxedActivePlayerSlots()
		{
			if (PlayerFarming.players == null)
			{
				return 0;
			}
			HashSet<int> hashSet = new HashSet<int>();
			for (int i = 0; i < PlayerFarming.players.Count; i++)
			{
				PlayerFarming val = PlayerFarming.players[i];
				if (!((Object)(object)val == (Object)null) && val.playerID >= 0 && val.playerID <= 3 && !((Object)(object)val.state == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null) && ((Component)val).gameObject.activeInHierarchy && HasUsableInput(val))
				{
					hashSet.Add(val.playerID);
				}
			}
			return hashSet.Count;
		}

		private static bool HasInputAssignedToSlot(int slot)
		{
			try
			{
				Player player = RewiredInputManager.GetPlayer(slot);
				if (player == null || player.controllers == null)
				{
					return false;
				}
				if (player.controllers.hasKeyboard)
				{
					return true;
				}
				return player.controllers.Joysticks != null && player.controllers.Joysticks.Count > 0;
			}
			catch
			{
				return false;
			}
		}

		private static void ClearPendingChestTarot(int chestId)
		{
			PendingChestTarotExtraByChest.Remove(chestId);
			PendingChestTarotExpiryByChest.Remove(chestId);
		}

		private static void ClearPendingChestTarotAny()
		{
			PendingChestTarotExtraAny = 0;
			PendingChestTarotAnyExpiry = -1f;
			PendingChestTarotSourceChestId = 0;
		}

		private static void ClearRecentChestRewardState(int chestId)
		{
			if (chestId != 0)
			{
				RecentChestRewardWindowByChest.Remove(chestId);
				RecentChestRewardPositionByChest.Remove(chestId);
				if (RecentChestRewardSourceChestId == chestId)
				{
					RecentChestRewardSourceChestId = 0;
					RecentChestRewardAnyExpiry = -1f;
				}
			}
		}

		private static IEnumerator WrapAndAdvanceTarotQueue(IEnumerator original, Interaction_Tarot interaction, PlayerFarming owner)
		{
			while (SafeMoveNext(original))
			{
				yield return original.Current;
			}
			if ((Object)(object)interaction == (Object)null)
			{
				yield break;
			}
			int id = ((Object)interaction).GetInstanceID();
			if (!PendingTarotQueueByInstance.TryGetValue(id, out var queue) || queue == null || queue.Count == 0)
			{
				CleanupInteractionState(id);
				yield break;
			}
			yield return (object)new WaitForSeconds(0.05f);
			while (queue.Count > 0)
			{
				PlayerFarming activePlayerBySlot = GetActivePlayerBySlot(queue.Dequeue());
				if (!((Object)(object)activePlayerBySlot == (Object)null) && (!((Object)(object)owner != (Object)null) || !((Object)(object)activePlayerBySlot == (Object)(object)owner)))
				{
					if (!((Behaviour)interaction).isActiveAndEnabled)
					{
						CleanupInteractionState(id);
					}
					else
					{
						TriggerNextTarotInteraction(interaction, activePlayerBySlot);
					}
					yield break;
				}
			}
			CleanupInteractionState(id);
		}

		private static void TriggerNextTarotInteraction(Interaction_Tarot interaction, PlayerFarming player)
		{
			if ((Object)(object)interaction == (Object)null || (Object)(object)player == (Object)null || (Object)(object)player.state == (Object)null)
			{
				return;
			}
			try
			{
				TrySetAllowedInputPlayer(player);
				SetInteractionContext(interaction, player, player.state);
				interaction.Activated = false;
				TarotAddonPlugin.Log.LogInfo((object)("[Tarot] Chained selection owner -> P" + (player.playerID + 1) + "."));
				((Interaction)interaction).OnInteract(player.state);
				if (VERBOSE_LOGS)
				{
					TarotAddonPlugin.Log.LogInfo((object)("[Tarot] Triggered chained interaction for P" + (player.playerID + 1) + "."));
				}
			}
			catch (Exception ex)
			{
				TarotAddonPlugin.Log.LogWarning((object)("[Tarot] Failed to trigger chained interaction: " + ex.Message));
				CleanupInteractionState(((Object)interaction).GetInstanceID());
			}
		}

		private static IEnumerator WrapAndAdvanceShrineTarotQueue(IEnumerator original, Shrines shrine, PlayerFarming owner)
		{
			while (SafeMoveNext(original))
			{
				yield return original.Current;
			}
			if ((Object)(object)shrine == (Object)null)
			{
				yield break;
			}
			int id = ((Object)shrine).GetInstanceID();
			if (!PendingShrineTarotQueueByInstance.TryGetValue(id, out var queue) || queue == null || queue.Count == 0)
			{
				CleanupShrineTarotState(id);
				yield break;
			}
			yield return (object)new WaitForSeconds(0.05f);
			while (queue.Count > 0)
			{
				PlayerFarming activePlayerBySlot = GetActivePlayerBySlot(queue.Dequeue());
				if (!((Object)(object)activePlayerBySlot == (Object)null) && (!((Object)(object)owner != (Object)null) || !((Object)(object)activePlayerBySlot == (Object)(object)owner)))
				{
					if (!((Behaviour)shrine).isActiveAndEnabled)
					{
						CleanupShrineTarotState(id);
					}
					else
					{
						TriggerNextShrineTarotInteraction(shrine, activePlayerBySlot);
					}
					yield break;
				}
			}
			CleanupShrineTarotState(id);
		}

		private static void TriggerNextShrineTarotInteraction(Shrines shrine, PlayerFarming player)
		{
			if ((Object)(object)shrine == (Object)null || (Object)(object)player == (Object)null || (Object)(object)player.state == (Object)null)
			{
				return;
			}
			int instanceID = ((Object)shrine).GetInstanceID();
			try
			{
				TrySetAllowedInputPlayer(player);
				SetInteractionContext(shrine, player, player.state);
				ShrineTarotOwnerByInstance[instanceID] = player;
				if (!(Traverse.Create((object)shrine).Method("DrawCardRoutine", new object[1] { player }).GetValue() is IEnumerator enumerator))
				{
					TarotAddonPlugin.Log.LogWarning((object)("[ShrineTarot] Failed to resolve DrawCardRoutine for P" + (player.playerID + 1) + "."));
					CleanupShrineTarotState(instanceID);
				}
				else
				{
					TarotAddonPlugin.Log.LogInfo((object)("[ShrineTarot] Chained purchase owner -> P" + (player.playerID + 1) + "."));
					((MonoBehaviour)shrine).StartCoroutine(enumerator);
				}
			}
			catch (Exception ex)
			{
				TarotAddonPlugin.Log.LogWarning((object)("[ShrineTarot] Failed to trigger chained purchase: " + ex.Message));
				CleanupShrineTarotState(instanceID);
			}
		}

		private static Queue<int> BuildTarotQueue(PlayerFarming starter)
		{
			Queue<int> queue = new Queue<int>();
			if ((Object)(object)starter == (Object)null)
			{
				return queue;
			}
			int playerID = starter.playerID;
			if (playerID <= 1)
			{
				EnqueueSlotIfActive(queue, 2);
				EnqueueSlotIfActive(queue, 3);
				return queue;
			}
			int preferredVanillaOwnerSlot = GetPreferredVanillaOwnerSlot(starter);
			EnqueueSlotIfActive(queue, preferredVanillaOwnerSlot);
			switch (playerID)
			{
			case 2:
				EnqueueSlotIfActive(queue, 3);
				break;
			case 3:
				EnqueueSlotIfActive(queue, 2);
				break;
			}
			return queue;
		}

		private static void EnqueueSlotIfActive(Queue<int> queue, int slot)
		{
			if (queue != null && (Object)(object)GetActivePlayerBySlot(slot) != (Object)null)
			{
				queue.Enqueue(slot);
			}
		}

		private static PlayerFarming GetActivePlayerBySlot(int slot)
		{
			if (PlayerFarming.players == null)
			{
				return null;
			}
			for (int i = 0; i < PlayerFarming.players.Count; i++)
			{
				PlayerFarming val = PlayerFarming.players[i];
				if (!((Object)(object)val == (Object)null) && val.playerID == slot && (PlayerFarming.playersCount <= 0 || val.playerID < PlayerFarming.playersCount) && !((Object)(object)val.state == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null) && ((Component)val).gameObject.activeInHierarchy && HasUsableInput(val))
				{
					return val;
				}
			}
			return null;
		}

		private static int GetPreferredVanillaOwnerSlot(PlayerFarming directStarter)
		{
			if ((Object)(object)directStarter != (Object)null && directStarter.playerID == 1)
			{
				return 1;
			}
			if ((Object)(object)GetActivePlayerBySlot(0) != (Object)null)
			{
				return 0;
			}
			return 1;
		}

		private static PlayerFarming ResolvePlayerFromState(StateMachine state)
		{
			if ((Object)(object)state == (Object)null)
			{
				return null;
			}
			PlayerFarming component = ((Component)state).GetComponent<PlayerFarming>();
			if ((Object)(object)component != (Object)null)
			{
				return component;
			}
			try
			{
				if ((Object)(object)((Component)state).gameObject != (Object)null)
				{
					return PlayerFarming.GetPlayerFarmingComponent(((Component)state).gameObject);
				}
			}
			catch
			{
			}
			return PlayerFarming.Instance;
		}

		private static PlayerFarming GetTrackedTarotOwner(Interaction_Tarot interaction)
		{
			if ((Object)(object)interaction == (Object)null)
			{
				return null;
			}
			if (TarotOwnerByInstance.TryGetValue(((Object)interaction).GetInstanceID(), out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			value = TryResolveInteractionPlayer(interaction);
			if ((Object)(object)value != (Object)null)
			{
				TarotOwnerByInstance[((Object)interaction).GetInstanceID()] = value;
			}
			return value;
		}

		private static PlayerFarming GetTrackedShrineTarotOwner(Shrines shrine)
		{
			if ((Object)(object)shrine == (Object)null)
			{
				return null;
			}
			int instanceID = ((Object)shrine).GetInstanceID();
			if (ShrineTarotOwnerByInstance.TryGetValue(instanceID, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			value = TryResolveInteractionPlayer(shrine);
			if ((Object)(object)value != (Object)null)
			{
				ShrineTarotOwnerByInstance[instanceID] = value;
			}
			return value;
		}

		private static void SetInteractionContext(object interaction, PlayerFarming player, StateMachine state)
		{
			if (interaction != null)
			{
				if ((Object)(object)player != (Object)null)
				{
					TrySetInteractionPlayer(interaction, player);
				}
				if ((Object)(object)state != (Object)null)
				{
					TrySetInteractionState(interaction, state);
				}
				Interaction_Tarot val = (Interaction_Tarot)((interaction is Interaction_Tarot) ? interaction : null);
				if ((Object)(object)val != (Object)null && (Object)(object)player != (Object)null)
				{
					TarotOwnerByInstance[((Object)val).GetInstanceID()] = player;
				}
				Interaction_TarotCard val2 = (Interaction_TarotCard)((interaction is Interaction_TarotCard) ? interaction : null);
				if ((Object)(object)val2 != (Object)null && (Object)(object)player != (Object)null)
				{
					TarotCardOwnerByInstance[((Object)val2).GetInstanceID()] = player;
				}
				Shrines val3 = (Shrines)((interaction is Shrines) ? interaction : null);
				if ((Object)(object)val3 != (Object)null && (Object)(object)player != (Object)null)
				{
					ShrineTarotOwnerByInstance[((Object)val3).GetInstanceID()] = player;
				}
			}
		}

		private static void CleanupInteractionState(int interactionId)
		{
			TarotOwnerByInstance.Remove(interactionId);
			PendingTarotQueueByInstance.Remove(interactionId);
			WarnedSuppressedDiscardByInstance.Remove(interactionId);
		}

		private static void CleanupShrineTarotState(int interactionId)
		{
			ShrineTarotOwnerByInstance.Remove(interactionId);
			PendingShrineTarotQueueByInstance.Remove(interactionId);
			WarnedSuppressedShrineDiscardByInstance.Remove(interactionId);
		}

		private static IEnumerator SuppressedDelayRoutine(float delay)
		{
			yield return (object)new WaitForSeconds(0.2f + delay);
		}

		private static bool HasUsableInput(PlayerFarming player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			try
			{
				if (player.isLamb)
				{
					return true;
				}
				if (player.rewiredPlayer == null || player.rewiredPlayer.controllers == null)
				{
					return false;
				}
				return player.rewiredPlayer.controllers.hasKeyboard || player.rewiredPlayer.controllers.joystickCount > 0;
			}
			catch
			{
				return true;
			}
		}

		private static void TrySetAllowedInputPlayer(PlayerFarming player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			try
			{
				UINavigatorNew instance = MonoSingleton<UINavigatorNew>.Instance;
				if (!((Object)(object)instance == (Object)null))
				{
					instance.AllowInputOnlyFromPlayer = player;
					instance.TemporarilyAllowInputOnlyFromAnyPlayer = false;
				}
			}
			catch
			{
			}
		}

		private static bool SafeMoveNext(IEnumerator iterator)
		{
			if (iterator == null)
			{
				return false;
			}
			try
			{
				return iterator.MoveNext();
			}
			catch (Exception ex)
			{
				TarotAddonPlugin.Log.LogWarning((object)("[Tarot] Coroutine iteration failed: " + ex.Message));
				return false;
			}
		}

		private static T TryGetField<T>(object instance, string fieldName) where T : class
		{
			if (instance == null || string.IsNullOrEmpty(fieldName))
			{
				return null;
			}
			try
			{
				return Traverse.Create(instance).Field(fieldName).GetValue() as T;
			}
			catch
			{
				return null;
			}
		}

		private static T TryGetProperty<T>(object instance, string propertyName) where T : class
		{
			if (instance == null || string.IsNullOrEmpty(propertyName))
			{
				return null;
			}
			try
			{
				return Traverse.Create(instance).Property(propertyName, (object[])null).GetValue() as T;
			}
			catch
			{
				return null;
			}
		}

		private static bool TrySetField(object instance, string fieldName, object value)
		{
			if (instance == null || string.IsNullOrEmpty(fieldName))
			{
				return false;
			}
			try
			{
				Traverse.Create(instance).Field(fieldName).SetValue(value);
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static bool TrySetProperty(object instance, string propertyName, object value)
		{
			if (instance == null || string.IsNullOrEmpty(propertyName))
			{
				return false;
			}
			try
			{
				Traverse.Create(instance).Property(propertyName, (object[])null).SetValue(value);
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static PlayerFarming TryResolveInteractionPlayer(object interaction)
		{
			if (interaction == null)
			{
				return null;
			}
			PlayerFarming val = TryGetProperty<PlayerFarming>(interaction, "playerFarming");
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			val = TryGetField<PlayerFarming>(interaction, "playerFarming");
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			val = TryGetField<PlayerFarming>(interaction, "_playerFarming");
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			return TryGetField<PlayerFarming>(interaction, "<playerFarming>k__BackingField");
		}

		private static StateMachine TryResolveInteractionState(object interaction)
		{
			if (interaction == null)
			{
				return null;
			}
			StateMachine val = TryGetProperty<StateMachine>(interaction, "state");
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			val = TryGetField<StateMachine>(interaction, "state");
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			val = TryGetField<StateMachine>(interaction, "_state");
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			return TryGetField<StateMachine>(interaction, "<state>k__BackingField");
		}

		private static void TrySetInteractionPlayer(object interaction, PlayerFarming player)
		{
			if (interaction != null && !TrySetProperty(interaction, "playerFarming", player) && !TrySetField(interaction, "playerFarming", player) && !TrySetField(interaction, "_playerFarming", player))
			{
				TrySetField(interaction, "<playerFarming>k__BackingField", player);
			}
		}

		private static void TrySetInteractionState(object interaction, StateMachine state)
		{
			if (interaction != null && !TrySetProperty(interaction, "state", state) && !TrySetField(interaction, "state", state) && !TrySetField(interaction, "_state", state))
			{
				TrySetField(interaction, "<state>k__BackingField", state);
			}
		}
	}
}

plugins/FlockFocus.dll

Decompiled 4 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using MMTools;
using Microsoft.CodeAnalysis;
using Rewired;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("FlockFocus")]
[assembly: AssemblyDescription("Player camera focus controls for Cult of the Lamb")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("FlockFocus")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("4A42DA4C-3665-499C-A66C-D5A63C1D3105")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace FlockFocus
{
	[BepInPlugin("com.cotl.flockfocus", "FlockFocus", "0.1.0")]
	public sealed class FlockFocusPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.cotl.flockfocus";

		public const string PluginName = "FlockFocus";

		public const string PluginVersion = "0.1.0";

		internal static ManualLogSource Log;

		internal static FlockFocusController Controller;

		private Harmony _harmony;

		public static bool IsFocusActive
		{
			get
			{
				if ((Object)(object)Controller != (Object)null)
				{
					return Controller.ShouldFocus;
				}
				return false;
			}
		}

		public static bool ShouldHideIndicatorsNow
		{
			get
			{
				if (FlockFocusConfig.HidePlayerIndicatorsWhileFocused.Value)
				{
					return IsFocusActive;
				}
				return false;
			}
		}

		private void Awake()
		{
			//IL_001b: 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_0026: Expected O, but got Unknown
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Expected O, but got Unknown
			//IL_008e: Expected O, but got Unknown
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			FlockFocusConfig.Bind(((BaseUnityPlugin)this).Config);
			GameObject val = new GameObject("FlockFocusController");
			Object.DontDestroyOnLoad((Object)val);
			Controller = val.AddComponent<FlockFocusController>();
			_harmony = new Harmony("com.cotl.flockfocus");
			_harmony.Patch((MethodBase)AccessTools.Method(typeof(CameraFollowTarget), "LateUpdate", (Type[])null, (Type[])null), new HarmonyMethod(typeof(CameraFollowTargetPatch), "Prefix", (Type[])null), new HarmonyMethod(typeof(CameraFollowTargetPatch), "Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			HarmonyMethod val2 = new HarmonyMethod(typeof(PlayerFarmingIndicatorPatch), "Postfix", (Type[])null);
			val2.priority = 0;
			_harmony.Patch((MethodBase)AccessTools.Method(typeof(PlayerFarming), "Update", (Type[])null, (Type[])null), (HarmonyMethod)null, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Log.LogInfo((object)"FlockFocus loaded.");
		}

		private void OnDestroy()
		{
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
			}
		}
	}
	internal enum ActivationMode
	{
		Hold,
		Toggle
	}
	internal enum ReleaseBehavior
	{
		ReturnToDefault,
		StayOnLastTarget,
		TimedReturn
	}
	internal enum ControllerBindingMode
	{
		UnityJoystickButtons,
		RewiredActionIds,
		RewiredTemplateAxes,
		RewiredTemplateButtons
	}
	internal enum ActivationSource
	{
		None,
		Keyboard,
		Controller
	}
	internal static class FlockFocusConfig
	{
		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<ActivationMode> ActivationMode;

		internal static ConfigEntry<float> FocusZoom;

		internal static ConfigEntry<bool> DefaultZoomRestore;

		internal static ConfigEntry<float> PanSpeedMultiplier;

		internal static ConfigEntry<ReleaseBehavior> ReleaseBehavior;

		internal static ConfigEntry<float> TimedReturnSeconds;

		internal static ConfigEntry<bool> AllowKeyboard;

		internal static ConfigEntry<string> KeyboardKey;

		internal static ConfigEntry<string> KeyboardModifierKey;

		internal static ConfigEntry<bool> AllowControllers;

		internal static ConfigEntry<ControllerBindingMode> ControllerBindingMode;

		internal static ConfigEntry<bool> ControllerRequireCombo;

		internal static ConfigEntry<int> ControllerPrimaryButton;

		internal static ConfigEntry<int> ControllerModifierButton;

		internal static ConfigEntry<float> ControllerAxisThreshold;

		internal static ConfigEntry<bool> HidePlayerIndicatorsWhileFocused;

		internal static ConfigEntry<bool> DisableDuringScriptedCamera;

		internal static ConfigEntry<bool> DisableDuringConversations;

		internal static ConfigEntry<bool> DebugLogging;

		internal static void Bind(ConfigFile config)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Expected O, but got Unknown
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Expected O, but got Unknown
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0228: Expected O, but got Unknown
			Enabled = config.Bind<bool>("General", "Enabled", true, "Enable FlockFocus camera authority.");
			ActivationMode = config.Bind<ActivationMode>("Input", "ActivationMode", FlockFocus.ActivationMode.Toggle, "Hold or Toggle.");
			FocusZoom = config.Bind<float>("Camera", "FocusZoom", 10f, new ConfigDescription("Camera target zoom while focused. Set 0 or less to keep vanilla zoom.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 25f), Array.Empty<object>()));
			DefaultZoomRestore = config.Bind<bool>("Camera", "DefaultZoomRestore", true, "Restore the vanilla target zoom after the focus frame/release.");
			PanSpeedMultiplier = config.Bind<float>("Camera", "PanSpeedMultiplier", 1.25f, new ConfigDescription("Temporary camera MoveSpeed multiplier while focused.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), Array.Empty<object>()));
			ReleaseBehavior = config.Bind<ReleaseBehavior>("Input", "ReleaseBehavior", FlockFocus.ReleaseBehavior.ReturnToDefault, "ReturnToDefault, StayOnLastTarget, or TimedReturn.");
			TimedReturnSeconds = config.Bind<float>("Input", "TimedReturnSeconds", 1.5f, new ConfigDescription("Seconds to keep focus after release when ReleaseBehavior is TimedReturn.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			AllowKeyboard = config.Bind<bool>("Keyboard", "AllowKeyboard", true, "Allow keyboard activation.");
			KeyboardKey = config.Bind<string>("Keyboard", "KeyboardKey", "LeftAlt", "Unity KeyCode name for keyboard focus. Examples: LeftAlt, Tab, Mouse3.");
			KeyboardModifierKey = config.Bind<string>("Keyboard", "KeyboardModifierKey", "", "Optional second Unity KeyCode that must be held with KeyboardKey.");
			AllowControllers = config.Bind<bool>("Controller", "AllowControllers", true, "Allow controller activation.");
			ControllerBindingMode = config.Bind<ControllerBindingMode>("Controller", "ControllerBindingMode", FlockFocus.ControllerBindingMode.RewiredTemplateAxes, "UnityJoystickButtons reads physical joystick buttons. RewiredActionIds reads game action ids. RewiredTemplateAxes reads standardized gamepad axes such as triggers. RewiredTemplateButtons reads standardized gamepad buttons such as stick clicks.");
			ControllerRequireCombo = config.Bind<bool>("Controller", "ControllerRequireCombo", false, "Require both controller buttons to be held.");
			ControllerPrimaryButton = config.Bind<int>("Controller", "ControllerPrimaryButton", 11, "Unity joystick button index, Rewired action id, or Rewired gamepad template axis id. Default 11 is left trigger for RewiredTemplateAxes.");
			ControllerModifierButton = config.Bind<int>("Controller", "ControllerModifierButton", -1, "Second Unity joystick button index, Rewired action id, or Rewired gamepad template axis id. Use -1 to disable.");
			ControllerAxisThreshold = config.Bind<float>("Controller", "ControllerAxisThreshold", 0.55f, new ConfigDescription("How far an axis must be pressed for RewiredTemplateAxes mode.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 1f), Array.Empty<object>()));
			HidePlayerIndicatorsWhileFocused = config.Bind<bool>("Visuals", "HidePlayerIndicatorsWhileFocused", true, "Temporarily disable the game's Coop Visual Indicators setting while FlockFocus is active.");
			DisableDuringScriptedCamera = config.Bind<bool>("Safety", "DisableDuringScriptedCamera", true, "Do nothing while the game is using a scripted TargetCamera, photo mode, or letterbox camera sequence.");
			DisableDuringConversations = config.Bind<bool>("Safety", "DisableDuringConversations", true, "Do nothing while MMConversation is playing.");
			DebugLogging = config.Bind<bool>("Debug", "DebugLogging", false, "Verbose FlockFocus logs.");
		}
	}
	internal sealed class FlockFocusController : MonoBehaviour
	{
		private PlayerFarming _authority;

		private ActivationSource _source;

		private bool _active;

		private float _releaseAt = -1f;

		private KeyCode _keyboardKey = (KeyCode)308;

		private KeyCode _keyboardModifier;

		private string _lastKeyboardKeyRaw;

		private string _lastKeyboardModifierRaw;

		private readonly Dictionary<int, bool> _lastControllerHeld = new Dictionary<int, bool>();

		internal bool ShouldFocus
		{
			get
			{
				if (!_active)
				{
					return false;
				}
				if (_releaseAt > 0f && Time.unscaledTime >= _releaseAt)
				{
					ClearAuthority("timed return");
					return false;
				}
				if (!IsValidPlayer(_authority))
				{
					ClearAuthority("authority missing");
					return false;
				}
				return true;
			}
		}

		internal PlayerFarming Authority => _authority;

		private void Update()
		{
			if (!FlockFocusConfig.Enabled.Value)
			{
				ClearAuthority("disabled");
				return;
			}
			RefreshKeyboardCodes();
			HandleKeyboard();
			HandleControllers();
			HandleHoldRelease();
		}

		private void LateUpdate()
		{
			PlayerFarmingIndicatorPatch.ApplyGlobalSuppression();
		}

		private void RefreshKeyboardCodes()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: 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_0071: Unknown result type (might be due to invalid IL or missing references)
			if (_lastKeyboardKeyRaw != FlockFocusConfig.KeyboardKey.Value)
			{
				_lastKeyboardKeyRaw = FlockFocusConfig.KeyboardKey.Value;
				_keyboardKey = ParseKeyCode(_lastKeyboardKeyRaw, (KeyCode)308);
			}
			if (_lastKeyboardModifierRaw != FlockFocusConfig.KeyboardModifierKey.Value)
			{
				_lastKeyboardModifierRaw = FlockFocusConfig.KeyboardModifierKey.Value;
				_keyboardModifier = ParseKeyCode(_lastKeyboardModifierRaw, (KeyCode)0);
			}
		}

		private void HandleKeyboard()
		{
			//IL_000d: 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)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			if (FlockFocusConfig.AllowKeyboard.Value && (int)_keyboardKey != 0 && ((int)_keyboardModifier == 0 || Input.GetKey(_keyboardModifier)) && Input.GetKeyDown(_keyboardKey))
			{
				PlayerFarming val = FindKeyboardPlayer();
				if ((Object)(object)val == (Object)null)
				{
					DebugLog("Keyboard focus ignored: no keyboard-owned player found.");
				}
				else
				{
					Activate(val, ActivationSource.Keyboard);
				}
			}
		}

		private void HandleControllers()
		{
			if (!FlockFocusConfig.AllowControllers.Value)
			{
				return;
			}
			foreach (PlayerFarming item in EnumeratePlayers())
			{
				if (IsValidPlayer(item))
				{
					GetControllerComboState(item, out var pressed, out var _, updateEdgeState: true);
					if (pressed)
					{
						Activate(item, ActivationSource.Controller);
					}
				}
			}
		}

		private void HandleHoldRelease()
		{
			if (_active && FlockFocusConfig.ActivationMode.Value == ActivationMode.Hold)
			{
				bool held = false;
				if (_source == ActivationSource.Keyboard)
				{
					held = IsKeyboardHeld();
				}
				else if (_source == ActivationSource.Controller && IsValidPlayer(_authority))
				{
					GetControllerComboState(_authority, out var _, out held, updateEdgeState: false);
				}
				if (!held)
				{
					Release();
				}
			}
		}

		private bool IsKeyboardHeld()
		{
			//IL_0001: 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_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			if ((int)_keyboardKey == 0)
			{
				return false;
			}
			if ((int)_keyboardModifier == 0 || Input.GetKey(_keyboardModifier))
			{
				return Input.GetKey(_keyboardKey);
			}
			return false;
		}

		private void Activate(PlayerFarming player, ActivationSource source)
		{
			if (IsValidPlayer(player))
			{
				if (FlockFocusConfig.ActivationMode.Value == ActivationMode.Toggle && _active && (Object)(object)_authority == (Object)(object)player)
				{
					ClearAuthority("toggle off");
					return;
				}
				_authority = player;
				_source = source;
				_active = true;
				_releaseAt = -1f;
				DebugLog("Authority -> P" + (player.playerID + 1) + " via " + source);
			}
		}

		private void Release()
		{
			switch (FlockFocusConfig.ReleaseBehavior.Value)
			{
			case ReleaseBehavior.StayOnLastTarget:
				_releaseAt = -1f;
				break;
			case ReleaseBehavior.TimedReturn:
				_releaseAt = Time.unscaledTime + Mathf.Max(0f, FlockFocusConfig.TimedReturnSeconds.Value);
				break;
			default:
				ClearAuthority("released");
				break;
			}
		}

		private void ClearAuthority(string reason)
		{
			if (_active)
			{
				DebugLog("Cleared focus: " + reason);
			}
			_authority = null;
			_source = ActivationSource.None;
			_active = false;
			_releaseAt = -1f;
		}

		private void GetControllerComboState(PlayerFarming player, out bool pressed, out bool held, bool updateEdgeState)
		{
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			pressed = false;
			held = false;
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			int value = FlockFocusConfig.ControllerPrimaryButton.Value;
			int value2 = FlockFocusConfig.ControllerModifierButton.Value;
			bool flag = FlockFocusConfig.ControllerRequireCombo.Value && value2 >= 0;
			bool flag2;
			bool flag3;
			bool flag4;
			bool flag5;
			if (FlockFocusConfig.ControllerBindingMode.Value == ControllerBindingMode.RewiredTemplateAxes)
			{
				flag2 = value >= 0 && SafeGetTemplateAxis(player, value);
				flag3 = false;
				flag4 = !flag || SafeGetTemplateAxis(player, value2);
				flag5 = false;
			}
			else if (FlockFocusConfig.ControllerBindingMode.Value == ControllerBindingMode.RewiredTemplateButtons)
			{
				flag2 = value >= 0 && SafeGetTemplateButton(player, value);
				flag3 = false;
				flag4 = !flag || SafeGetTemplateButton(player, value2);
				flag5 = false;
			}
			else if (FlockFocusConfig.ControllerBindingMode.Value == ControllerBindingMode.RewiredActionIds)
			{
				Player rewiredPlayer = player.rewiredPlayer;
				if (rewiredPlayer == null)
				{
					return;
				}
				flag2 = value >= 0 && SafeGetButton(rewiredPlayer, value);
				flag3 = value >= 0 && SafeGetButtonDown(rewiredPlayer, value);
				flag4 = !flag || SafeGetButton(rewiredPlayer, value2);
				flag5 = flag && SafeGetButtonDown(rewiredPlayer, value2);
			}
			else
			{
				int joystickNumber = Mathf.Clamp(player.playerID + 1, 1, 8);
				flag2 = value >= 0 && Input.GetKey(UnityJoystickButton(joystickNumber, value));
				flag3 = value >= 0 && Input.GetKeyDown(UnityJoystickButton(joystickNumber, value));
				flag4 = !flag || Input.GetKey(UnityJoystickButton(joystickNumber, value2));
				flag5 = flag && Input.GetKeyDown(UnityJoystickButton(joystickNumber, value2));
			}
			held = (flag ? (flag2 && flag4) : flag2);
			if (FlockFocusConfig.ControllerBindingMode.Value == ControllerBindingMode.RewiredTemplateAxes || FlockFocusConfig.ControllerBindingMode.Value == ControllerBindingMode.RewiredTemplateButtons)
			{
				int playerID = player.playerID;
				bool flag6 = _lastControllerHeld.ContainsKey(playerID) && _lastControllerHeld[playerID];
				pressed = held && !flag6;
				if (updateEdgeState)
				{
					_lastControllerHeld[playerID] = held;
				}
			}
			else
			{
				pressed = ((!flag) ? flag3 : (held && (flag3 || flag5)));
			}
		}

		private static bool SafeGetTemplateAxis(PlayerFarming player, int templateElementId)
		{
			try
			{
				if ((Object)(object)player == (Object)null || player.rewiredPlayer == null || player.rewiredPlayer.controllers == null)
				{
					return false;
				}
				float num = Mathf.Clamp01(FlockFocusConfig.ControllerAxisThreshold.Value);
				Controller firstControllerWithTemplate = player.rewiredPlayer.controllers.GetFirstControllerWithTemplate<IGamepadTemplate>();
				if (firstControllerWithTemplate == null)
				{
					return false;
				}
				IControllerTemplate template = (IControllerTemplate)(object)firstControllerWithTemplate.GetTemplate<IGamepadTemplate>();
				IControllerTemplateAxis val = ((template != null) ? template.GetElement<IControllerTemplateAxis>(templateElementId) : null);
				return val != null && ((IControllerTemplateElement)val).exists && val.value > num;
			}
			catch
			{
				return false;
			}
		}

		private static bool SafeGetTemplateButton(PlayerFarming player, int templateElementId)
		{
			try
			{
				if ((Object)(object)player == (Object)null || player.rewiredPlayer == null || player.rewiredPlayer.controllers == null)
				{
					return false;
				}
				Controller firstControllerWithTemplate = player.rewiredPlayer.controllers.GetFirstControllerWithTemplate<IGamepadTemplate>();
				if (firstControllerWithTemplate == null)
				{
					return false;
				}
				IControllerTemplate template = (IControllerTemplate)(object)firstControllerWithTemplate.GetTemplate<IGamepadTemplate>();
				IControllerTemplateButton val = ((template != null) ? template.GetElement<IControllerTemplateButton>(templateElementId) : null);
				return val != null && ((IControllerTemplateElement)val).exists && val.value;
			}
			catch
			{
				return false;
			}
		}

		private static bool SafeGetButton(Player player, int actionId)
		{
			try
			{
				return player != null && player.GetButton(actionId);
			}
			catch
			{
				return false;
			}
		}

		private static bool SafeGetButtonDown(Player player, int actionId)
		{
			try
			{
				return player != null && player.GetButtonDown(actionId);
			}
			catch
			{
				return false;
			}
		}

		private static KeyCode UnityJoystickButton(int joystickNumber, int button)
		{
			return (KeyCode)(350 + (joystickNumber - 1) * 20 + button);
		}

		private unsafe static KeyCode ParseKeyCode(string raw, KeyCode fallback)
		{
			//IL_0051: 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_0055: 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_0026: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrWhiteSpace(raw))
			{
				return (KeyCode)0;
			}
			try
			{
				return (KeyCode)Enum.Parse(typeof(KeyCode), raw.Trim(), ignoreCase: true);
			}
			catch
			{
				FlockFocusPlugin.Log.LogWarning((object)("Invalid KeyCode '" + raw + "', using " + ((object)(*(KeyCode*)(&fallback))/*cast due to .constrained prefix*/).ToString()));
				return fallback;
			}
		}

		private static PlayerFarming FindKeyboardPlayer()
		{
			foreach (PlayerFarming item in EnumeratePlayers())
			{
				if (!IsValidPlayer(item) || item.rewiredPlayer == null)
				{
					continue;
				}
				try
				{
					if (item.rewiredPlayer.controllers != null && item.rewiredPlayer.controllers.hasKeyboard)
					{
						return item;
					}
				}
				catch
				{
					return item;
				}
			}
			if (!IsValidPlayer(PlayerFarming.Instance))
			{
				return null;
			}
			return PlayerFarming.Instance;
		}

		internal static IEnumerable<PlayerFarming> EnumeratePlayers()
		{
			if (PlayerFarming.players != null)
			{
				for (int i = 0; i < PlayerFarming.players.Count; i++)
				{
					PlayerFarming val = PlayerFarming.players[i];
					if ((Object)(object)val != (Object)null)
					{
						yield return val;
					}
				}
			}
			else if ((Object)(object)PlayerFarming.Instance != (Object)null)
			{
				yield return PlayerFarming.Instance;
			}
		}

		internal static bool IsValidPlayer(PlayerFarming player)
		{
			if ((Object)(object)player != (Object)null && (Object)(object)((Component)player).gameObject != (Object)null && ((Component)player).gameObject.activeInHierarchy)
			{
				if (!((Object)(object)player.CameraBone != (Object)null))
				{
					return (Object)(object)((Component)player).transform != (Object)null;
				}
				return true;
			}
			return false;
		}

		internal static void DebugLog(string message)
		{
			if (FlockFocusConfig.DebugLogging.Value && FlockFocusPlugin.Log != null)
			{
				FlockFocusPlugin.Log.LogInfo((object)("[FlockFocus] " + message));
			}
		}
	}
	internal sealed class CameraFocusPatchState
	{
		internal List<Target> Targets;

		internal float TargetDistance;

		internal float MoveSpeed;

		internal bool HasState;
	}
	internal static class CameraFollowTargetPatch
	{
		public static void Prefix(CameraFollowTarget __instance, ref CameraFocusPatchState __state)
		{
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Expected O, but got Unknown
			__state = null;
			FlockFocusController controller = FlockFocusPlugin.Controller;
			if ((Object)(object)controller == (Object)null || !controller.ShouldFocus || (Object)(object)__instance == (Object)null || !CanTouchCamera(__instance))
			{
				return;
			}
			PlayerFarming authority = controller.Authority;
			if (!FlockFocusController.IsValidPlayer(authority))
			{
				return;
			}
			GameObject val = (((Object)(object)authority.CameraBone != (Object)null) ? authority.CameraBone : ((Component)authority).gameObject);
			if (!((Object)(object)val == (Object)null))
			{
				__state = new CameraFocusPatchState
				{
					Targets = ((__instance.targets != null) ? new List<Target>(__instance.targets) : null),
					TargetDistance = __instance.targetDistance,
					MoveSpeed = __instance.MoveSpeed,
					HasState = true
				};
				__instance.targets = new List<Target>
				{
					new Target(val, 1f)
				};
				if (FlockFocusConfig.FocusZoom.Value > 0f)
				{
					__instance.targetDistance = FlockFocusConfig.FocusZoom.Value;
				}
				__instance.MoveSpeed = Mathf.Max(0.1f, __instance.MoveSpeed * FlockFocusConfig.PanSpeedMultiplier.Value);
			}
		}

		public static void Postfix(CameraFollowTarget __instance, CameraFocusPatchState __state)
		{
			if (!((Object)(object)__instance == (Object)null) && __state != null && __state.HasState)
			{
				__instance.targets = __state.Targets ?? new List<Target>();
				if (FlockFocusConfig.DefaultZoomRestore.Value)
				{
					__instance.targetDistance = __state.TargetDistance;
				}
				__instance.MoveSpeed = __state.MoveSpeed;
			}
		}

		private static bool CanTouchCamera(CameraFollowTarget camera)
		{
			if ((Object)(object)camera.TargetCamera != (Object)null && FlockFocusConfig.DisableDuringScriptedCamera.Value)
			{
				return false;
			}
			if (FlockFocusConfig.DisableDuringScriptedCamera.Value)
			{
				try
				{
					if (PhotoModeManager.PhotoModeActive || LetterBox.IsPlaying)
					{
						return false;
					}
				}
				catch
				{
					return false;
				}
			}
			if (FlockFocusConfig.DisableDuringConversations.Value)
			{
				try
				{
					if (MMConversation.isPlaying)
					{
						return false;
					}
				}
				catch
				{
					return false;
				}
			}
			return Time.timeScale > 0f;
		}
	}
	internal static class PlayerFarmingIndicatorPatch
	{
		private static bool _overrideActive;

		private static bool _previousCoopVisualIndicators;

		public static void Postfix(PlayerFarming __instance)
		{
			ApplyGlobalSuppression();
		}

		internal static void ApplyGlobalSuppression()
		{
			if (!ShouldSuppress())
			{
				RestoreCoopVisualIndicators();
			}
			else if (SettingsManager.Settings != null && SettingsManager.Settings.Accessibility != null)
			{
				if (!_overrideActive)
				{
					_previousCoopVisualIndicators = SettingsManager.Settings.Accessibility.CoopVisualIndicators;
					_overrideActive = true;
				}
				SettingsManager.Settings.Accessibility.CoopVisualIndicators = false;
			}
		}

		private static bool ShouldSuppress()
		{
			FlockFocusController controller = FlockFocusPlugin.Controller;
			if (FlockFocusConfig.HidePlayerIndicatorsWhileFocused.Value && (Object)(object)controller != (Object)null)
			{
				return controller.ShouldFocus;
			}
			return false;
		}

		private static void RestoreCoopVisualIndicators()
		{
			if (_overrideActive)
			{
				if (SettingsManager.Settings != null && SettingsManager.Settings.Accessibility != null)
				{
					SettingsManager.Settings.Accessibility.CoopVisualIndicators = _previousCoopVisualIndicators;
				}
				_overrideActive = false;
			}
		}
	}
}

plugins/FlockFocusCOTLMPCompat.dll

Decompiled 4 days ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("FlockFocusCOTLMPCompat")]
[assembly: AssemblyDescription("COTLMP compatibility patch for FlockFocus.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("FlockFocusCOTLMPCompat")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("5865765f-2797-4117-bb83-f07d06f5d1d4")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace FlockFocusCOTLMPCompat
{
	[BepInPlugin("com.cotl.flockfocus.cotlmpcompat", "FlockFocus COTLMP Compat", "0.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class FlockFocusCOTLMPCompatPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.cotl.flockfocus.cotlmpcompat";

		public const string PluginName = "FlockFocus COTLMP Compat";

		public const string PluginVersion = "0.1.0";

		internal static ManualLogSource Log;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Type type = ResolveCOTLMPPatchesType();
			MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, "HUD_Update", (Type[])null, (Type[])null) : null);
			if (methodInfo == null)
			{
				Log.LogInfo((object)"COTLMP HUD_Update was not found. Compat patch is idle.");
				return;
			}
			COTLMPHudUpdatePatch.Bind(type);
			_harmony = new Harmony("com.cotl.flockfocus.cotlmpcompat");
			_harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(COTLMPHudUpdatePatch), "Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Log.LogInfo((object)"FlockFocus COTLMP compat loaded.");
		}

		private static Type ResolveCOTLMPPatchesType()
		{
			Type type = AccessTools.TypeByName("COTLMultiplayerExpansion.Patches");
			if (type != null)
			{
				return type;
			}
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			for (int i = 0; i < assemblies.Length; i++)
			{
				Type type2 = assemblies[i].GetType("COTLMultiplayerExpansion.Patches", throwOnError: false);
				if (type2 != null)
				{
					return type2;
				}
			}
			return null;
		}

		private void OnDestroy()
		{
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
			}
		}
	}
	internal static class COTLMPHudUpdatePatch
	{
		private static readonly string[] WidgetFieldNames = new string[3] { "_p2Widget", "_p3Widget", "_p4Widget" };

		private static PropertyInfo _shouldHideIndicatorsNow;

		private static FieldInfo[] _widgetFields = new FieldInfo[0];

		internal static void Bind(Type cotlmpPatchesType)
		{
			Type type = AccessTools.TypeByName("FlockFocus.FlockFocusPlugin");
			_shouldHideIndicatorsNow = ((type != null) ? AccessTools.Property(type, "ShouldHideIndicatorsNow") : null);
			if (cotlmpPatchesType == null)
			{
				_widgetFields = new FieldInfo[0];
				return;
			}
			FieldInfo[] array = new FieldInfo[WidgetFieldNames.Length];
			for (int i = 0; i < WidgetFieldNames.Length; i++)
			{
				array[i] = AccessTools.Field(cotlmpPatchesType, WidgetFieldNames[i]);
			}
			_widgetFields = array;
		}

		public static bool Prefix()
		{
			if (!ShouldSuppressCOTLMPWidgets())
			{
				return true;
			}
			HideExistingWidgets();
			return false;
		}

		private static bool ShouldSuppressCOTLMPWidgets()
		{
			try
			{
				return _shouldHideIndicatorsNow != null && (bool)_shouldHideIndicatorsNow.GetValue(null, null);
			}
			catch (Exception ex)
			{
				ManualLogSource log = FlockFocusCOTLMPCompatPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Failed to query FlockFocus indicator state: " + ex.Message));
				}
				return false;
			}
		}

		private static void HideExistingWidgets()
		{
			for (int i = 0; i < _widgetFields.Length; i++)
			{
				FieldInfo fieldInfo = _widgetFields[i];
				if (!(fieldInfo == null))
				{
					object? value = fieldInfo.GetValue(null);
					Component val = (Component)((value is Component) ? value : null);
					if ((Object)(object)val != (Object)null && (Object)(object)val.gameObject != (Object)null && val.gameObject.activeSelf)
					{
						val.gameObject.SetActive(false);
					}
				}
			}
		}
	}
}