Decompiled source of MCZombieLegacyCompat v1.0.1

BepInEx/plugins/MCZombieLegacyCompat/MCZOMBIEfix.dll

Decompiled 4 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace LegacyREPOCompat;

[BepInPlugin("chatgpt.mczombiefix", "MCZOMBIEfix", "1.2.4")]
public sealed class Plugin : BaseUnityPlugin
{
	internal static ManualLogSource Log;

	internal static ConfigEntry<bool> GuaranteeMinecraftZombie;

	internal static ConfigEntry<bool> GuaranteeFreddyOnFNAF;

	internal static ConfigEntry<bool> VillageTargetedFixes;

	internal static ConfigEntry<bool> VillageBrokenChestContainment;

	internal static ConfigEntry<float> ArenaMusicMaxVolume;

	internal static ConfigEntry<bool> AutoSkipPrivateHostPassword;

	internal static ConfigEntry<bool> AutoPickBestRegionPrivateHost;

	internal static ConfigEntry<bool> VillageCleanupCrewTruckStartGuard;

	internal static ConfigEntry<bool> ShowAllEnemyHealthBars;

	internal static ConfigEntry<float> EnemyHealthBarMaxDistance;

	internal static ConfigEntry<float> EnemyHealthBarVisibleSeconds;

	internal static ConfigEntry<bool> VillageClickToggleDoors;

	internal static ConfigEntry<float> VillageLegacyEnemyTeleportSafeSeconds;

	internal static ConfigEntry<bool> RepairMinecraftLoadingGraphics;

	private Harmony _harmony;

	private void Awake()
	{
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		GuaranteeMinecraftZombie = ((BaseUnityPlugin)this).Config.Bind<bool>("MCZombie Compatibility", "Guarantee Zombie On Minecraft Maps", true, "On Minecraft maps, guarantees MCZombie by replacing one already-selected enemy slot of the same difficulty tier. It never adds an extra enemy group above the level's normal quota.");
		GuaranteeFreddyOnFNAF = ((BaseUnityPlugin)this).Config.Bind<bool>("Freddy Compatibility", "Guarantee Freddy On FNAF", true, "On FNAF levels, guarantees Freddy by replacing one already-selected enemy slot of the same difficulty tier. It never increases the normal enemy quota.");
		((BaseUnityPlugin)this).Logger.LogInfo((object)"MCZOMBIEfix 1.2.4 loaded.");
		_harmony = new Harmony("chatgpt.mczombiefix.v124");
		Bridge.InstallBackroomsEnemySpawnFix(_harmony);
		((MonoBehaviour)this).StartCoroutine(Bridge.MinecraftZombieLiveGuaranteeLoop());
		((MonoBehaviour)this).StartCoroutine(Bridge.MinecraftZombieDiagnosticsLoop());
		Bridge.InstallMCZombieLegacyEnemyLookupFix(_harmony);
		((MonoBehaviour)this).StartCoroutine(Bridge.FreddyFNAFLiveGuaranteeLoop());
		if (GuaranteeMinecraftZombie.Value)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)"[MCZOMBIEfix] MCZombie Minecraft live quota replacement active.");
		}
		if (GuaranteeFreddyOnFNAF.Value)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)"[MCZOMBIEfix] Freddy FNAF live quota replacement active.");
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)"[MCZOMBIEfix] MCZombie legacy horde enemy lookup compatibility active.");
		((BaseUnityPlugin)this).Logger.LogInfo((object)"[MCZOMBIEfix] Backrooms FirstSpawnPointAdd replacement active; no Arena, Village, lobby or enemy-HUD repair is enabled.");
	}
}
public static class Bridge
{
	private static bool _warpPathLogged;

	private static readonly Dictionary<string, List<GameObject>> ValuableCache = new Dictionary<string, List<GameObject>>(StringComparer.OrdinalIgnoreCase);

	private static bool _backroomsSpawnSuppressionLogged;

	private static bool _backroomsSpawnReplacementLogged;

	private static bool _backroomsNoLevelPointLogged;

	private static int _backroomsSpawnPointAssignments;

	private static EnemySetup _mcZombieSetup;

	private static EnemySetup _freddySetup;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	private static readonly HashSet<string> SoundPlayLoopSignaturesLogged = new HashSet<string>();

	private static bool _privateHostPasswordFlowArmed;

	private static float _privateHostPasswordFlowArmedAt = -1000f;

	private static bool _privateHostPasswordConfirmed;

	private static bool _privatePasswordPatchLogged;

	private static bool _privateHostRegionFlowArmed;

	private static float _privateHostRegionFlowArmedAt = -1000f;

	private static bool _privateHostRegionConfirmed;

	private static bool _privateRegionPatchLogged;

	private static int _villageNullDoorPrefabSkips;

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

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

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

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

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

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

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

	private static GameObject _nativeEnemyHealthTemplate;

	private static bool _enemyHealthReflectionLogged;

	private static bool _damageShowConflictLogged;

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

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

	private static bool _villageLegacyTPPatchInstalled;

	private static bool _minecraftLoadingReferenceMigrationInstalled;

	private static bool _minecraftLoadingGraphicPatchInstalled;

	private static readonly HashSet<string> MinecraftLoadingGraphicAppliedLevels = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

	private static readonly HashSet<string> MinecraftLoadingGraphicMissingLogged = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

	private static Sprite[] _villageGeneratedLoadingSprites;

	private static bool _villageGeneratedLoadingSpritesLogged;

	private static GameObject _villageLoadingOverlayRoot;

	private static Image _villageLoadingOverlayImage;

	private static int _villageLoadingOverlayGeneration;

	private static bool _strongholdDoorAudioResolved;

	private static bool _strongholdDoorAudioMissingLogged;

	private static AudioSource _strongholdDoorOpenSource;

	private static AudioSource _strongholdDoorCloseSource;

	private static AudioClip _villageWoodDoorOpenClip;

	private static AudioClip _villageWoodDoorCloseClip;

	private static bool _villageWoodDoorAudioResolved;

	private static bool _villageWoodDoorAudioMissingLogged;

	private static readonly Dictionary<string, GameObject> LegacyNetworkPrefabCache = new Dictionary<string, GameObject>(StringComparer.OrdinalIgnoreCase);

	private static readonly Dictionary<string, string> PluginBundlePathCache = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

	private static readonly HashSet<string> PluginBundlePathMissing = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

	public static void RegisterEnemy(EnemySetup setup)
	{
		if ((Object)(object)setup != (Object)null)
		{
			_mcZombieSetup = setup;
		}
		try
		{
			Assembly assembly = FindAssembly("REPOLib");
			if (!(assembly == null) && !((Object)(object)setup == (Object)null))
			{
				Type contentType = assembly.GetType("REPOLib.Objects.Sdk.EnemyContent", throwOnError: false);
				Type type = assembly.GetType("REPOLib.Modules.Enemies", throwOnError: false);
				if (contentType == null || type == null)
				{
					throw new MissingMemberException("REPOLib 4 EnemyContent/Enemies introuvable.");
				}
				Object val = (Object)(object)ScriptableObject.CreateInstance(contentType);
				FieldInfo field = contentType.GetField("_setup", BindingFlags.Instance | BindingFlags.NonPublic);
				FieldInfo field2 = contentType.GetField("_spawnObjects", BindingFlags.Instance | BindingFlags.NonPublic);
				if (val == (Object)null || field == null || field2 == null)
				{
					throw new MissingFieldException("EnemyContent _setup/_spawnObjects introuvable.");
				}
				List<GameObject> list = FindZombieEnemyPrefabs();
				if (list.Count == 0)
				{
					throw new Exception("Prefab Enemy - MCZombie introuvable.");
				}
				field.SetValue(val, setup);
				field2.SetValue(val, list);
				MethodInfo methodInfo = type.GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault((MethodInfo m) => m.Name == "RegisterEnemy" && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == contentType);
				if (methodInfo == null)
				{
					throw new MissingMethodException("RegisterEnemy(EnemyContent) introuvable.");
				}
				methodInfo.Invoke(null, new object[1] { val });
				Info("MCZombie: enemy registered through REPOLib 4.");
			}
		}
		catch (Exception ex)
		{
			Error("MCZombie RegisterEnemy bridge failed: " + Unwrap(ex));
		}
	}

	public static void InstallMCZombieLegacyEnemyLookupFix(Harmony harmony)
	{
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Expected O, but got Unknown
		try
		{
			Assembly assembly = FindAssembly("REPOLib");
			Type type = ((assembly == null) ? null : assembly.GetType("REPOLib.Modules.Enemies", throwOnError: false));
			if (!(type == null))
			{
				MethodInfo method = type.GetMethod("GetEnemyByName", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null);
				MethodInfo method2 = typeof(Bridge).GetMethod("MCZombieLegacyGetEnemyByNamePrefix", BindingFlags.Static | BindingFlags.Public);
				if (!(method == null) && !(method2 == null))
				{
					harmony.Patch((MethodBase)method, new HarmonyMethod(method2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Info("MCZombie legacy GetEnemyByName bridge installed.");
				}
			}
		}
		catch (Exception ex)
		{
			Warn("MCZombie legacy GetEnemyByName bridge failed: " + Unwrap(ex));
		}
	}

	public static bool MCZombieLegacyGetEnemyByNamePrefix(string name, ref EnemySetup __result)
	{
		if (string.IsNullOrEmpty(name) || (name.IndexOf("Zombie", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("MCZombie", StringComparison.OrdinalIgnoreCase) < 0))
		{
			return true;
		}
		EnemySetup found = _mcZombieSetup;
		if ((Object)(object)found == (Object)null)
		{
			TryFindModernEnemySetupContains("Zombie", out found);
		}
		if ((Object)(object)found == (Object)null)
		{
			return true;
		}
		__result = found;
		Info("MCZombie legacy GetEnemyByName redirected to modern EnemySetup | requested='" + name + "'.");
		return false;
	}

	private static bool TryFindModernEnemySetupContains(string contains, out EnemySetup found)
	{
		found = null;
		try
		{
			Assembly assembly = FindAssembly("REPOLib");
			Type type = ((assembly == null) ? null : assembly.GetType("REPOLib.Modules.Enemies", throwOnError: false));
			if (type == null)
			{
				return false;
			}
			string[] array = new string[2] { "AllEnemies", "RegisteredEnemies" };
			for (int i = 0; i < array.Length; i++)
			{
				PropertyInfo property = type.GetProperty(array[i], BindingFlags.Static | BindingFlags.Public);
				if (property == null || !(property.GetValue(null, null) is IEnumerable enumerable))
				{
					continue;
				}
				foreach (object item in enumerable)
				{
					EnemySetup val = (EnemySetup)((item is EnemySetup) ? item : null);
					if (!((Object)(object)val == (Object)null))
					{
						string text = "";
						try
						{
							text = ((Object)val).name ?? "";
						}
						catch
						{
						}
						TryGetEnemySetupIdentity(val, out var _, out var enemyName);
						if (text.IndexOf(contains, StringComparison.OrdinalIgnoreCase) >= 0 || (!string.IsNullOrEmpty(enemyName) && enemyName.IndexOf(contains, StringComparison.OrdinalIgnoreCase) >= 0))
						{
							found = val;
							return true;
						}
					}
				}
			}
		}
		catch (Exception ex)
		{
			Warn("Modern enemy setup lookup failed for '" + contains + "': " + Unwrap(ex));
		}
		return false;
	}

	public static IEnumerator FreddyFNAFLiveGuaranteeLoop()
	{
		while (true)
		{
			yield return (object)new WaitForSeconds(2f);
			try
			{
				if (Plugin.GuaranteeFreddyOnFNAF != null && Plugin.GuaranteeFreddyOnFNAF.Value && IsFNAFLevelName(GetCurrentLevelName()) && IsHostOrSingleplayer())
				{
					if (!((Object)(object)_freddySetup == (Object)null))
					{
						goto IL_009a;
					}
					if (TryFindModernEnemySetupContains("Freddy", out var found))
					{
						_freddySetup = found;
						Info("Freddy modern EnemySetup resolved for FNAF quota guarantee.");
						goto IL_009a;
					}
				}
				goto end_IL_0043;
				IL_009a:
				LevelGenerator instance = LevelGenerator.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					continue;
				}
				int instanceID = ((Object)instance).GetInstanceID();
				if (!FreddyLevelFirstSeenAt.TryGetValue(instanceID, out var value))
				{
					value = Time.realtimeSinceStartup;
					FreddyLevelFirstSeenAt[instanceID] = value;
				}
				if (FreddyLiveDoneForGenerator.Contains(instanceID) || Time.realtimeSinceStartup - value < 10f)
				{
					continue;
				}
				EnemyParent value2 = null;
				bool flag = FreddyPendingVictim.TryGetValue(instanceID, out value2);
				int num = CountActiveFreddys();
				if (num > 0)
				{
					if (flag)
					{
						int value3 = -1;
						FreddyPendingActiveBefore.TryGetValue(instanceID, out value3);
						string text = (((Object)(object)value2 != (Object)null) ? (value2.enemyName ?? ((Object)value2).name ?? "<unknown>") : "<already gone>");
						if ((Object)(object)value2 != (Object)null)
						{
							RemoveEnemyParentFromDirector(value2);
							DestroyEnemyParentNetworkSafe(value2);
						}
						int num2 = CountActiveEnemyParents();
						FreddyLiveDoneForGenerator.Add(instanceID);
						FreddyPendingVictim.Remove(instanceID);
						FreddyPendingAt.Remove(instanceID);
						FreddyPendingActiveBefore.Remove(instanceID);
						Info("Freddy FNAF QUOTA REPLACE complete | replaced='" + text + "' | activeBefore=" + value3 + " | activeAfter=" + num2 + " | freddyCount=" + num + " | level='" + GetCurrentLevelName() + "'");
					}
					else
					{
						FreddyLiveDoneForGenerator.Add(instanceID);
						Info("Freddy FNAF QUOTA OK | Freddy already active naturally | count=" + num + " | level='" + GetCurrentLevelName() + "'");
					}
				}
				else if (flag)
				{
					float value4 = 0f;
					FreddyPendingAt.TryGetValue(instanceID, out value4);
					if (Time.realtimeSinceStartup - value4 > 6f)
					{
						Warn("Freddy FNAF QUOTA spawn verification timed out; original enemy kept.");
						FreddyPendingVictim.Remove(instanceID);
						FreddyPendingAt.Remove(instanceID);
						FreddyPendingActiveBefore.Remove(instanceID);
					}
				}
				else
				{
					BeginFreddyLiveReplacement(instanceID);
				}
				end_IL_0043:;
			}
			catch (Exception ex)
			{
				Error("Freddy FNAF quota replacement failed: " + Unwrap(ex));
			}
		}
	}

	private static void BeginFreddyLiveReplacement(int generatorId)
	{
		//IL_016f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0174: Unknown result type (might be due to invalid IL or missing references)
		//IL_0176: Unknown result type (might be due to invalid IL or missing references)
		//IL_0178: Unknown result type (might be due to invalid IL or missing references)
		//IL_017a: Unknown result type (might be due to invalid IL or missing references)
		//IL_019f: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_0191: Unknown result type (might be due to invalid IL or missing references)
		//IL_0196: Unknown result type (might be due to invalid IL or missing references)
		//IL_0253: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cf: Invalid comparison between Unknown and I4
		//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
		int difficulty = -1;
		string enemyName = "Freddy";
		if (!TryGetEnemySetupIdentity(_freddySetup, out difficulty, out enemyName))
		{
			if (!FreddyLiveWaitLogged.Contains(generatorId))
			{
				FreddyLiveWaitLogged.Add(generatorId);
				Warn("Freddy FNAF QUOTA: Freddy difficulty unavailable; waiting.");
			}
			return;
		}
		EnemyParent[] array = Object.FindObjectsOfType<EnemyParent>();
		if (array == null || array.Length == 0)
		{
			return;
		}
		Transform val = FindAnyPlayerTransform();
		EnemyParent val2 = null;
		float num = -1f;
		EnemyParent[] array2 = array;
		foreach (EnemyParent val3 in array2)
		{
			if ((Object)(object)val3 == (Object)null || (Object)(object)((Component)val3).gameObject == (Object)null || !((Component)val3).gameObject.activeInHierarchy)
			{
				continue;
			}
			string text = val3.enemyName ?? ((Object)val3).name ?? string.Empty;
			if (text.IndexOf("Freddy", StringComparison.OrdinalIgnoreCase) < 0 && (int)val3.difficulty == difficulty)
			{
				float num2 = (((Object)(object)val != (Object)null) ? Vector3.Distance(val.position, ((Component)val3).transform.position) : 0f);
				if ((Object)(object)val2 == (Object)null || num2 > num)
				{
					val2 = val3;
					num = num2;
				}
			}
		}
		if ((Object)(object)val2 == (Object)null)
		{
			if (!FreddyLiveWaitLogged.Contains(generatorId))
			{
				FreddyLiveWaitLogged.Add(generatorId);
				Warn("Freddy FNAF QUOTA: no active enemy of Freddy tier " + difficulty + " available to replace without changing the quota.");
			}
			return;
		}
		int num3 = CountActiveEnemyParents();
		Vector3 position = ((Component)val2).transform.position;
		Vector3 val4 = position;
		NavMeshHit val5 = default(NavMeshHit);
		if (NavMesh.SamplePosition(position, ref val5, 6f, -1))
		{
			val4 = ((NavMeshHit)(ref val5)).position;
		}
		Quaternion rotation = ((Component)val2).transform.rotation;
		if (!SpawnEnemyThroughModernREPOLib(_freddySetup, val4, rotation))
		{
			Warn("Freddy FNAF QUOTA: modern Freddy spawn failed; original enemy kept.");
			return;
		}
		FreddyPendingVictim[generatorId] = val2;
		FreddyPendingAt[generatorId] = Time.realtimeSinceStartup;
		FreddyPendingActiveBefore[generatorId] = num3;
		Info("Freddy FNAF QUOTA REPLACE pending | replace='" + (val2.enemyName ?? ((Object)val2).name ?? "<unknown>") + "' | tier=" + difficulty + " | activeBefore=" + num3 + " | spawnAt=" + Vec(val4) + " | level='" + GetCurrentLevelName() + "'");
	}

	private static bool SpawnEnemyThroughModernREPOLib(EnemySetup setup, Vector3 position, Quaternion rotation)
	{
		//IL_008e: 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)
		if ((Object)(object)setup == (Object)null)
		{
			return false;
		}
		try
		{
			Assembly assembly = FindAssembly("REPOLib");
			Type type = ((assembly == null) ? null : assembly.GetType("REPOLib.Modules.Enemies", throwOnError: false));
			if (type == null)
			{
				return false;
			}
			MethodInfo methodInfo = type.GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault(delegate(MethodInfo m)
			{
				if (m.Name != "SpawnEnemy")
				{
					return false;
				}
				ParameterInfo[] parameters = m.GetParameters();
				return parameters.Length == 4 && parameters[0].ParameterType == typeof(EnemySetup) && parameters[1].ParameterType == typeof(Vector3) && parameters[2].ParameterType == typeof(Quaternion) && parameters[3].ParameterType == typeof(bool);
			});
			if (methodInfo == null)
			{
				return false;
			}
			object obj = methodInfo.Invoke(null, new object[4] { setup, position, rotation, false });
			if (!(obj is IEnumerable enumerable))
			{
				return obj != null;
			}
			foreach (object item in enumerable)
			{
				if (item != null)
				{
					return true;
				}
			}
			return false;
		}
		catch (Exception ex)
		{
			Error("Modern enemy spawn failed: " + Unwrap(ex));
			return false;
		}
	}

	private static int CountActiveFreddys()
	{
		int num = 0;
		EnemyParent[] array = Object.FindObjectsOfType<EnemyParent>();
		if (array == null)
		{
			return 0;
		}
		EnemyParent[] array2 = array;
		foreach (EnemyParent val in array2)
		{
			if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null) && ((Component)val).gameObject.activeInHierarchy)
			{
				string text = val.enemyName ?? ((Object)val).name ?? string.Empty;
				if (text.IndexOf("Freddy", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					num++;
				}
			}
		}
		return num;
	}

	public static IEnumerator MinecraftZombieLiveGuaranteeLoop()
	{
		while (true)
		{
			yield return (object)new WaitForSeconds(2f);
			try
			{
				if (Plugin.GuaranteeMinecraftZombie == null || !Plugin.GuaranteeMinecraftZombie.Value || !IsMinecraftLevelName(GetCurrentLevelName()) || !IsHostOrSingleplayer() || (Object)(object)_mcZombieSetup == (Object)null)
				{
					continue;
				}
				LevelGenerator instance = LevelGenerator.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					continue;
				}
				int instanceID = ((Object)instance).GetInstanceID();
				if (!MinecraftZombieLevelFirstSeenAt.TryGetValue(instanceID, out var value))
				{
					value = Time.realtimeSinceStartup;
					MinecraftZombieLevelFirstSeenAt[instanceID] = value;
				}
				if (MinecraftZombieLiveDoneForGenerator.Contains(instanceID) || Time.realtimeSinceStartup - value < 10f)
				{
					continue;
				}
				EnemyParent value2 = null;
				bool flag = MinecraftZombiePendingVictim.TryGetValue(instanceID, out value2);
				int num = CountActiveMCZombies();
				if (num > 0)
				{
					if (flag)
					{
						int value3 = -1;
						MinecraftZombiePendingActiveBefore.TryGetValue(instanceID, out value3);
						string text = (((Object)(object)value2 != (Object)null) ? (value2.enemyName ?? ((Object)value2).name ?? "<unknown>") : "<already gone>");
						if ((Object)(object)value2 != (Object)null)
						{
							RemoveEnemyParentFromDirector(value2);
							DestroyEnemyParentNetworkSafe(value2);
						}
						int num2 = CountActiveEnemyParents();
						MinecraftZombieLiveDoneForGenerator.Add(instanceID);
						MinecraftZombiePendingVictim.Remove(instanceID);
						MinecraftZombiePendingAt.Remove(instanceID);
						MinecraftZombiePendingActiveBefore.Remove(instanceID);
						Info("MCZombie LIVE QUOTA REPLACE complete | replaced='" + text + "' | activeBefore=" + value3 + " | activeAfter=" + num2 + " | zombieCount=" + num + " | level='" + GetCurrentLevelName() + "'");
					}
					else
					{
						MinecraftZombieLiveDoneForGenerator.Add(instanceID);
						Info("MCZombie LIVE QUOTA OK | zombie already active naturally | count=" + num + " | level='" + GetCurrentLevelName() + "'");
					}
				}
				else if (flag)
				{
					float value4 = 0f;
					MinecraftZombiePendingAt.TryGetValue(instanceID, out value4);
					if (Time.realtimeSinceStartup - value4 > 6f)
					{
						Warn("MCZombie LIVE QUOTA REPLACE spawn verification timed out; original enemy kept.");
						MinecraftZombiePendingVictim.Remove(instanceID);
						MinecraftZombiePendingAt.Remove(instanceID);
						MinecraftZombiePendingActiveBefore.Remove(instanceID);
					}
				}
				else
				{
					BeginMinecraftZombieLiveReplacement(instanceID);
				}
			}
			catch (Exception ex)
			{
				Error("MCZombie live quota replacement failed: " + Unwrap(ex));
			}
		}
	}

	public static IEnumerator MinecraftZombieDiagnosticsLoop()
	{
		NavMeshHit val2 = default(NavMeshHit);
		while (true)
		{
			yield return (object)new WaitForSeconds(1f);
			try
			{
				if (!IsMinecraftLevelName(GetCurrentLevelName()))
				{
					MinecraftZombieTracked.Clear();
					MinecraftZombieSpawnLogged.Clear();
					MinecraftZombieInvalidLogged.Clear();
					MinecraftZombieSafeBodyPosition.Clear();
					MinecraftZombieSafeParentPosition.Clear();
					MinecraftZombieRescueCount.Clear();
					continue;
				}
				EnemyParent[] array = Object.FindObjectsOfType<EnemyParent>();
				if (array != null)
				{
					EnemyParent[] array2 = array;
					foreach (EnemyParent val in array2)
					{
						if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).gameObject == (Object)null)
						{
							continue;
						}
						string text = val.enemyName ?? ((Object)val).name ?? string.Empty;
						if (text.IndexOf("Zombie", StringComparison.OrdinalIgnoreCase) >= 0 || (((Object)val).name ?? string.Empty).IndexOf("MCZombie", StringComparison.OrdinalIgnoreCase) >= 0)
						{
							int instanceID = ((Object)val).GetInstanceID();
							MinecraftZombieTracked[instanceID] = val;
							if (!MinecraftZombieSpawnLogged.Contains(instanceID))
							{
								MinecraftZombieSpawnLogged.Add(instanceID);
								bool flag = NavMesh.SamplePosition(((Component)val).transform.position, ref val2, 1.5f, -1);
								Enemy componentInChildren = ((Component)val).GetComponentInChildren<Enemy>(true);
								float current = -1f;
								float maximum = -1f;
								bool flag2 = (Object)(object)componentInChildren != (Object)null && TryGetEnemyHealth(componentInChildren, out current, out maximum);
								Transform minecraftZombieBodyTransform = GetMinecraftZombieBodyTransform(val);
								Vector3 val3 = (((Object)(object)minecraftZombieBodyTransform != (Object)null) ? minecraftZombieBodyTransform.position : ((Component)val).transform.position);
								MinecraftZombieSafeBodyPosition[instanceID] = val3;
								MinecraftZombieSafeParentPosition[instanceID] = ((Component)val).transform.position;
								MinecraftZombieRescueCount[instanceID] = 0;
								Info("MCZombie TRACK start | id=" + instanceID + " | parentPos=" + Vec(((Component)val).transform.position) + " | bodyPos=" + Vec(val3) + " | active=" + ((Component)val).gameObject.activeInHierarchy + " | onNavMesh=" + flag + (flag2 ? (" | hp=" + current.ToString("0.##") + "/" + maximum.ToString("0.##")) : " | hp=<unavailable>"));
							}
						}
					}
				}
				KeyValuePair<int, EnemyParent>[] array3 = MinecraftZombieTracked.ToArray();
				for (int j = 0; j < array3.Length; j++)
				{
					KeyValuePair<int, EnemyParent> keyValuePair = array3[j];
					int key = keyValuePair.Key;
					EnemyParent value = keyValuePair.Value;
					bool flag3 = (Object)(object)value == (Object)null || (Object)(object)((Component)value).gameObject == (Object)null || !((Component)value).gameObject.activeInHierarchy;
					float current2 = -1f;
					float maximum2 = -1f;
					bool flag4 = false;
					if (!flag3)
					{
						Enemy componentInChildren2 = ((Component)value).GetComponentInChildren<Enemy>(true);
						flag4 = (Object)(object)componentInChildren2 != (Object)null && TryGetEnemyHealth(componentInChildren2, out current2, out maximum2);
						if (flag4 && current2 <= 0.01f)
						{
							flag3 = true;
						}
						if (!flag3)
						{
							RescueMinecraftZombieIfFalling(key, value);
						}
					}
					if (flag3 && !MinecraftZombieInvalidLogged.Contains(key))
					{
						MinecraftZombieInvalidLogged.Add(key);
						string text2 = (((Object)(object)value != (Object)null) ? Vec(((Component)value).transform.position) : "<destroyed>");
						Warn("MCZombie TRACK invalid/dead | id=" + key + " | pos=" + text2 + (flag4 ? (" | hp=" + current2.ToString("0.##") + "/" + maximum2.ToString("0.##")) : " | hp=<unavailable>") + " | level='" + GetCurrentLevelName() + "'");
					}
				}
			}
			catch (Exception ex)
			{
				Warn("MCZombie diagnostics failed: " + Unwrap(ex));
			}
		}
	}

	private static Transform GetMinecraftZombieBodyTransform(EnemyParent parent)
	{
		if ((Object)(object)parent == (Object)null || (Object)(object)((Component)parent).gameObject == (Object)null)
		{
			return null;
		}
		try
		{
			Transform val = ((Component)parent).transform.Find("Enable/Rigidbody/Center");
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			Rigidbody[] componentsInChildren = ((Component)parent).GetComponentsInChildren<Rigidbody>(true);
			if (componentsInChildren != null && componentsInChildren.Length > 0)
			{
				Rigidbody val2 = ((IEnumerable<Rigidbody>)componentsInChildren).FirstOrDefault((Func<Rigidbody, bool>)((Rigidbody b) => (Object)(object)b != (Object)null && ((Object)b).name.IndexOf("Rigidbody", StringComparison.OrdinalIgnoreCase) >= 0));
				if ((Object)(object)val2 == (Object)null)
				{
					val2 = ((IEnumerable<Rigidbody>)componentsInChildren).FirstOrDefault((Func<Rigidbody, bool>)((Rigidbody b) => (Object)(object)b != (Object)null && !b.isKinematic));
				}
				if ((Object)(object)val2 == (Object)null)
				{
					val2 = ((IEnumerable<Rigidbody>)componentsInChildren).FirstOrDefault((Func<Rigidbody, bool>)((Rigidbody b) => (Object)(object)b != (Object)null));
				}
				if ((Object)(object)val2 != (Object)null)
				{
					return ((Component)val2).transform;
				}
			}
			Enemy componentInChildren = ((Component)parent).GetComponentInChildren<Enemy>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				return ((Component)componentInChildren).transform;
			}
		}
		catch
		{
		}
		return ((Component)parent).transform;
	}

	private static void RescueMinecraftZombieIfFalling(int id, EnemyParent parent)
	{
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_015f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0160: Unknown result type (might be due to invalid IL or missing references)
		//IL_0162: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_017b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0183: Unknown result type (might be due to invalid IL or missing references)
		//IL_0188: Unknown result type (might be due to invalid IL or missing references)
		//IL_018d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0195: Unknown result type (might be due to invalid IL or missing references)
		//IL_0174: Unknown result type (might be due to invalid IL or missing references)
		//IL_0179: Unknown result type (might be due to invalid IL or missing references)
		//IL_0103: Unknown result type (might be due to invalid IL or missing references)
		//IL_0115: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0288: Unknown result type (might be due to invalid IL or missing references)
		//IL_02cf: 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_02f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0237: Unknown result type (might be due to invalid IL or missing references)
		//IL_0229: Unknown result type (might be due to invalid IL or missing references)
		//IL_022e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0230: Unknown result type (might be due to invalid IL or missing references)
		//IL_0238: Unknown result type (might be due to invalid IL or missing references)
		//IL_023c: Unknown result type (might be due to invalid IL or missing references)
		//IL_024a: Unknown result type (might be due to invalid IL or missing references)
		//IL_025c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0268: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)parent == (Object)null || (Object)(object)((Component)parent).gameObject == (Object)null)
		{
			return;
		}
		Transform minecraftZombieBodyTransform = GetMinecraftZombieBodyTransform(parent);
		if ((Object)(object)minecraftZombieBodyTransform == (Object)null)
		{
			return;
		}
		if (!MinecraftZombieSafeBodyPosition.TryGetValue(id, out var value))
		{
			value = minecraftZombieBodyTransform.position;
			MinecraftZombieSafeBodyPosition[id] = value;
		}
		if (!MinecraftZombieSafeParentPosition.TryGetValue(id, out var value2))
		{
			value2 = ((Component)parent).transform.position;
			MinecraftZombieSafeParentPosition[id] = value2;
		}
		Vector3 position = minecraftZombieBodyTransform.position;
		bool flag = !float.IsNaN(position.x) && !float.IsNaN(position.y) && !float.IsNaN(position.z) && !float.IsInfinity(position.x) && !float.IsInfinity(position.y) && !float.IsInfinity(position.z);
		if (flag && position.y >= value.y - 2.5f)
		{
			NavMeshHit val = default(NavMeshHit);
			if (NavMesh.SamplePosition(position, ref val, 2f, -1))
			{
				MinecraftZombieSafeBodyPosition[id] = position;
				MinecraftZombieSafeParentPosition[id] = ((Component)parent).transform.position;
			}
		}
		else
		{
			if (flag && position.y >= value.y - 8f)
			{
				return;
			}
			int value3 = 0;
			MinecraftZombieRescueCount.TryGetValue(id, out value3);
			value3++;
			MinecraftZombieRescueCount[id] = value3;
			Vector3 val2 = value2;
			NavMeshHit val3 = default(NavMeshHit);
			if (NavMesh.SamplePosition(value2, ref val3, 4f, -1))
			{
				val2 = ((NavMeshHit)(ref val3)).position;
			}
			Vector3 val4 = val2 - ((Component)parent).transform.position;
			((Component)parent).transform.position = val2;
			object obj = GetMemberValue(parent, "Enemy");
			if (obj == null)
			{
				obj = ((Component)parent).GetComponentInChildren<Enemy>(true);
			}
			object obj2 = ((obj == null) ? null : GetMemberValue(obj, "NavMeshAgent"));
			if (obj2 == null && obj != null)
			{
				obj2 = GetMemberValue(obj, "navMeshAgent");
			}
			if (obj2 != null)
			{
				WarpCompat(obj2, val2);
			}
			Rigidbody[] componentsInChildren = ((Component)parent).GetComponentsInChildren<Rigidbody>(true);
			if (componentsInChildren != null)
			{
				Rigidbody[] array = componentsInChildren;
				foreach (Rigidbody val5 in array)
				{
					if (!((Object)(object)val5 == (Object)null))
					{
						Vector3 position2 = (val5.position = (((Object)(object)((Component)val5).transform == (Object)(object)minecraftZombieBodyTransform) ? value : (val5.position + val4)));
						((Component)val5).transform.position = position2;
						if (!val5.isKinematic)
						{
							val5.velocity = Vector3.zero;
							val5.angularVelocity = Vector3.zero;
						}
						val5.WakeUp();
					}
				}
			}
			minecraftZombieBodyTransform.position = value;
			Physics.SyncTransforms();
			Warn("MCZombie FALL RESCUE | id=" + id + " | count=" + value3 + " | fallenBody=" + Vec(position) + " | restoredBody=" + Vec(value) + " | parent=" + Vec(val2) + " | level='" + GetCurrentLevelName() + "'");
		}
	}

	private static void BeginMinecraftZombieLiveReplacement(int generatorId)
	{
		//IL_0194: Unknown result type (might be due to invalid IL or missing references)
		//IL_0199: Unknown result type (might be due to invalid IL or missing references)
		//IL_019b: Unknown result type (might be due to invalid IL or missing references)
		//IL_019d: Unknown result type (might be due to invalid IL or missing references)
		//IL_019f: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_024d: Unknown result type (might be due to invalid IL or missing references)
		//IL_024f: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Expected I4, but got Unknown
		//IL_0107: Unknown result type (might be due to invalid IL or missing references)
		//IL_0113: Unknown result type (might be due to invalid IL or missing references)
		int difficulty = -1;
		string enemyName = "Zombie";
		if (!TryGetEnemySetupIdentity(_mcZombieSetup, out difficulty, out enemyName))
		{
			if (!MinecraftZombieLiveWaitLogged.Contains(generatorId))
			{
				MinecraftZombieLiveWaitLogged.Add(generatorId);
				Warn("MCZombie LIVE QUOTA: zombie difficulty unavailable; waiting.");
			}
			return;
		}
		EnemyParent[] array = Object.FindObjectsOfType<EnemyParent>();
		if (array == null || array.Length == 0)
		{
			return;
		}
		Transform val = FindAnyPlayerTransform();
		EnemyParent val2 = null;
		float num = -1f;
		EnemyParent[] array2 = array;
		foreach (EnemyParent val3 in array2)
		{
			if ((Object)(object)val3 == (Object)null || (Object)(object)((Component)val3).gameObject == (Object)null || !((Component)val3).gameObject.activeInHierarchy)
			{
				continue;
			}
			string text = val3.enemyName ?? ((Object)val3).name ?? string.Empty;
			if (text.IndexOf("Zombie", StringComparison.OrdinalIgnoreCase) >= 0 || (((Object)val3).name ?? string.Empty).IndexOf("MCZombie", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				continue;
			}
			int num2 = (int)val3.difficulty;
			if (num2 == difficulty)
			{
				float num3 = (((Object)(object)val != (Object)null) ? Vector3.Distance(val.position, ((Component)val3).transform.position) : 0f);
				if ((Object)(object)val2 == (Object)null || num3 > num)
				{
					val2 = val3;
					num = num3;
				}
			}
		}
		if ((Object)(object)val2 == (Object)null)
		{
			if (!MinecraftZombieLiveWaitLogged.Contains(generatorId))
			{
				MinecraftZombieLiveWaitLogged.Add(generatorId);
				Warn("MCZombie LIVE QUOTA: no active enemy of zombie tier " + difficulty + " available to replace without changing the tier quota.");
			}
			return;
		}
		int num4 = CountActiveEnemyParents();
		Vector3 position = ((Component)val2).transform.position;
		Vector3 val4 = position;
		NavMeshHit val5 = default(NavMeshHit);
		bool flag = NavMesh.SamplePosition(position, ref val5, 6f, -1);
		if (flag)
		{
			val4 = ((NavMeshHit)(ref val5)).position;
		}
		Quaternion rotation = ((Component)val2).transform.rotation;
		Info("MCZombie spawn position resolved | requested=" + Vec(position) + " | final=" + Vec(val4) + " | navMeshSnapped=" + flag + " | victim='" + (val2.enemyName ?? ((Object)val2).name ?? "<unknown>") + "'");
		if (!SpawnMCZombieThroughModernREPOLib(val4, rotation))
		{
			Warn("MCZombie LIVE QUOTA: modern zombie spawn failed; original enemy kept.");
			return;
		}
		MinecraftZombiePendingVictim[generatorId] = val2;
		MinecraftZombiePendingAt[generatorId] = Time.realtimeSinceStartup;
		MinecraftZombiePendingActiveBefore[generatorId] = num4;
		Info("MCZombie LIVE QUOTA REPLACE pending | replace='" + (val2.enemyName ?? ((Object)val2).name ?? "<unknown>") + "' | tier=" + difficulty + " | activeBefore=" + num4 + " | spawnAt=" + Vec(val4) + " | level='" + GetCurrentLevelName() + "'");
	}

	private static bool SpawnMCZombieThroughModernREPOLib(Vector3 position, Quaternion rotation)
	{
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_0091: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			Assembly assembly = FindAssembly("REPOLib");
			Type type = ((assembly == null) ? null : assembly.GetType("REPOLib.Modules.Enemies", throwOnError: false));
			if (type == null)
			{
				return false;
			}
			MethodInfo methodInfo = type.GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault(delegate(MethodInfo m)
			{
				if (m.Name != "SpawnEnemy")
				{
					return false;
				}
				ParameterInfo[] parameters = m.GetParameters();
				return parameters.Length == 4 && parameters[0].ParameterType == typeof(EnemySetup) && parameters[1].ParameterType == typeof(Vector3) && parameters[2].ParameterType == typeof(Quaternion) && parameters[3].ParameterType == typeof(bool);
			});
			if (methodInfo == null)
			{
				return false;
			}
			object obj = methodInfo.Invoke(null, new object[4] { _mcZombieSetup, position, rotation, false });
			if (!(obj is IEnumerable enumerable))
			{
				return obj != null;
			}
			foreach (object item in enumerable)
			{
				if (item != null)
				{
					return true;
				}
			}
			return false;
		}
		catch (Exception ex)
		{
			Error("MCZombie modern live replacement spawn failed: " + Unwrap(ex));
			return false;
		}
	}

	private static int CountActiveMCZombies()
	{
		int num = 0;
		EnemyParent[] array = Object.FindObjectsOfType<EnemyParent>();
		if (array == null)
		{
			return 0;
		}
		EnemyParent[] array2 = array;
		foreach (EnemyParent val in array2)
		{
			if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null) && ((Component)val).gameObject.activeInHierarchy)
			{
				string text = val.enemyName ?? ((Object)val).name ?? string.Empty;
				if (text.IndexOf("Zombie", StringComparison.OrdinalIgnoreCase) >= 0 || (((Object)val).name ?? string.Empty).IndexOf("MCZombie", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					num++;
				}
			}
		}
		return num;
	}

	private static int CountActiveEnemyParents()
	{
		int num = 0;
		EnemyParent[] array = Object.FindObjectsOfType<EnemyParent>();
		if (array == null)
		{
			return 0;
		}
		EnemyParent[] array2 = array;
		foreach (EnemyParent val in array2)
		{
			if ((Object)(object)val != (Object)null && (Object)(object)((Component)val).gameObject != (Object)null && ((Component)val).gameObject.activeInHierarchy)
			{
				num++;
			}
		}
		return num;
	}

	private static void RemoveEnemyParentFromDirector(EnemyParent parent)
	{
		if ((Object)(object)parent == (Object)null)
		{
			return;
		}
		try
		{
			EnemyDirector val = Object.FindObjectOfType<EnemyDirector>();
			if (!((Object)(object)val == (Object)null) && GetMemberValue(val, "enemiesSpawned") is IList list && list.Contains(parent))
			{
				list.Remove(parent);
			}
		}
		catch (Exception ex)
		{
			Warn("MCZombie quota replacement could not remove old EnemyParent from director list: " + Unwrap(ex));
		}
	}

	private static void DestroyEnemyParentNetworkSafe(EnemyParent parent)
	{
		if ((Object)(object)parent == (Object)null || (Object)(object)((Component)parent).gameObject == (Object)null)
		{
			return;
		}
		GameObject gameObject = ((Component)parent).gameObject;
		try
		{
			Assembly assembly = FindAssembly("PhotonUnityNetworking");
			Type type = ((assembly == null) ? null : assembly.GetType("Photon.Pun.PhotonNetwork", throwOnError: false));
			MethodInfo methodInfo = ((type == null) ? null : type.GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault(delegate(MethodInfo m)
			{
				if (m.Name != "Destroy")
				{
					return false;
				}
				ParameterInfo[] parameters = m.GetParameters();
				return parameters.Length == 1 && parameters[0].ParameterType == typeof(GameObject);
			}));
			if (methodInfo != null)
			{
				methodInfo.Invoke(null, new object[1] { gameObject });
				return;
			}
		}
		catch (Exception ex)
		{
			Warn("Photon destroy of replaced enemy failed; falling back to Unity destroy: " + Unwrap(ex));
		}
		Object.Destroy((Object)(object)gameObject);
	}

	private static bool IsMCZombieSetup(EnemySetup setup)
	{
		if ((Object)(object)setup == (Object)null)
		{
			return false;
		}
		if (object.ReferenceEquals(setup, _mcZombieSetup))
		{
			return true;
		}
		string text = ((Object)setup).name ?? string.Empty;
		if (text.IndexOf("MCZombie", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("MC Zombie", StringComparison.OrdinalIgnoreCase) >= 0)
		{
			return true;
		}
		if (!TryGetEnemySetupIdentity(setup, out var _, out var enemyName))
		{
			return false;
		}
		if (enemyName.IndexOf("MCZombie", StringComparison.OrdinalIgnoreCase) < 0)
		{
			return enemyName.IndexOf("MC Zombie", StringComparison.OrdinalIgnoreCase) >= 0;
		}
		return true;
	}

	private static bool TryGetEnemySetupIdentity(EnemySetup setup, out int difficulty, out string enemyName)
	{
		//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b6: Expected I4, but got Unknown
		//IL_0171: Unknown result type (might be due to invalid IL or missing references)
		//IL_0177: Expected I4, but got Unknown
		difficulty = -1;
		enemyName = (((Object)(object)setup == (Object)null) ? string.Empty : (((Object)setup).name ?? string.Empty));
		if ((Object)(object)setup == (Object)null)
		{
			return false;
		}
		try
		{
			object memberValue = GetMemberValue(setup, "spawnObjects");
			if (memberValue is IEnumerable enumerable)
			{
				foreach (object item in enumerable)
				{
					GameObject val = ResolveEnemySpawnPrefab(item);
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					EnemyParent val2 = val.GetComponentsInChildren<EnemyParent>(true)?.FirstOrDefault((Func<EnemyParent, bool>)((EnemyParent p) => (Object)(object)p != (Object)null));
					if (!((Object)(object)val2 == (Object)null))
					{
						difficulty = (int)val2.difficulty;
						if (!string.IsNullOrEmpty(val2.enemyName))
						{
							enemyName = val2.enemyName;
						}
						return true;
					}
				}
			}
			if (object.ReferenceEquals(setup, _mcZombieSetup))
			{
				List<GameObject> list = FindZombieEnemyPrefabs();
				foreach (GameObject item2 in list)
				{
					if ((Object)(object)item2 == (Object)null)
					{
						continue;
					}
					EnemyParent val3 = item2.GetComponentsInChildren<EnemyParent>(true)?.FirstOrDefault((Func<EnemyParent, bool>)((EnemyParent p) => (Object)(object)p != (Object)null));
					if (!((Object)(object)val3 == (Object)null))
					{
						difficulty = (int)val3.difficulty;
						if (!string.IsNullOrEmpty(val3.enemyName))
						{
							enemyName = val3.enemyName;
						}
						return true;
					}
				}
			}
		}
		catch (Exception ex)
		{
			Warn("MCZombie enemy-setup identity inspection failed: " + Unwrap(ex));
		}
		return false;
	}

	private static GameObject ResolveEnemySpawnPrefab(object spawnObject)
	{
		if (spawnObject == null)
		{
			return null;
		}
		GameObject val = (GameObject)((spawnObject is GameObject) ? spawnObject : null);
		if ((Object)(object)val != (Object)null)
		{
			return val;
		}
		object memberValue = GetMemberValue(spawnObject, "Prefab");
		GameObject val2 = (GameObject)((memberValue is GameObject) ? memberValue : null);
		if ((Object)(object)val2 != (Object)null)
		{
			return val2;
		}
		memberValue = GetMemberValue(spawnObject, "prefab");
		return (GameObject)((memberValue is GameObject) ? memberValue : null);
	}

	private static bool IsFNAFLevelName(string levelName)
	{
		if (string.IsNullOrEmpty(levelName))
		{
			return false;
		}
		if (levelName.IndexOf("FNAF", StringComparison.OrdinalIgnoreCase) < 0)
		{
			return levelName.IndexOf("Freddy", StringComparison.OrdinalIgnoreCase) >= 0;
		}
		return true;
	}

	private static bool IsMinecraftLevelName(string levelName)
	{
		if (string.IsNullOrEmpty(levelName))
		{
			return false;
		}
		if (levelName.IndexOf("Minecraft Village", StringComparison.OrdinalIgnoreCase) < 0 && levelName.IndexOf("Minecraft Cave", StringComparison.OrdinalIgnoreCase) < 0 && levelName.IndexOf("Minecraft Stronghold", StringComparison.OrdinalIgnoreCase) < 0)
		{
			return levelName.IndexOf("Stronghold", StringComparison.OrdinalIgnoreCase) >= 0;
		}
		return true;
	}

	private static bool IsHostOrSingleplayer()
	{
		try
		{
			Assembly assembly = FindAssembly("PhotonUnityNetworking");
			Type type = ((assembly == null) ? null : assembly.GetType("Photon.Pun.PhotonNetwork", throwOnError: false));
			if (type == null)
			{
				return true;
			}
			PropertyInfo property = type.GetProperty("OfflineMode", BindingFlags.Static | BindingFlags.Public);
			if (property != null && Convert.ToBoolean(property.GetValue(null, null)))
			{
				return true;
			}
			PropertyInfo property2 = type.GetProperty("IsMasterClient", BindingFlags.Static | BindingFlags.Public);
			if (property2 == null)
			{
				return true;
			}
			return Convert.ToBoolean(property2.GetValue(null, null));
		}
		catch
		{
			return true;
		}
	}

	private static Transform FindAnyPlayerTransform()
	{
		try
		{
			Assembly assembly = FindAssembly("Assembly-CSharp");
			Type type = ((assembly == null) ? null : assembly.GetType("PlayerAvatar", throwOnError: false));
			if (type == null)
			{
				return null;
			}
			Object[] array = Object.FindObjectsOfType(type);
			if (array == null || array.Length == 0)
			{
				return null;
			}
			Object[] array2 = array;
			foreach (Object val in array2)
			{
				Component val2 = (Component)(object)((val is Component) ? val : null);
				if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2.transform == (Object)null) && val2.gameObject.activeInHierarchy)
				{
					object memberValue = GetMemberValue(val2, "isLocal");
					if (memberValue is bool && (bool)memberValue)
					{
						return val2.transform;
					}
					memberValue = GetMemberValue(val2, "isLocalPlayer");
					if (memberValue is bool && (bool)memberValue)
					{
						return val2.transform;
					}
				}
			}
			Object obj = array[0];
			Component val3 = (Component)(object)((obj is Component) ? obj : null);
			return ((Object)(object)val3 == (Object)null) ? null : val3.transform;
		}
		catch
		{
			return null;
		}
	}

	public static IEnumerator AllEnemyNativeHealthLoop()
	{
		while (true)
		{
			yield return (object)new WaitForSeconds(0.12f);
			try
			{
				if (Plugin.ShowAllEnemyHealthBars == null || !Plugin.ShowAllEnemyHealthBars.Value)
				{
					HideAllNativeEnemyHealth();
				}
				else if (IsDamageShowLoaded())
				{
					HideAllNativeEnemyHealth();
					if (!_damageShowConflictLogged)
					{
						_damageShowConflictLogged = true;
						Warn("Enemy Health UI paused because DamageShow/EnemyHealthBar is loaded. Remove or disable DamageShow to use LegacyREPOCompat native health bars.");
					}
				}
				else
				{
					UpdateNativeEnemyHealth();
				}
			}
			catch (Exception ex)
			{
				if (!_enemyHealthReflectionLogged)
				{
					_enemyHealthReflectionLogged = true;
					Warn("Native enemy health UI failed: " + Unwrap(ex));
				}
			}
		}
	}

	private static bool IsDamageShowLoaded()
	{
		try
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				if (!(assembly == null))
				{
					string text = assembly.GetName().Name ?? string.Empty;
					if (text.IndexOf("DamageShow", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("EnemyHealthBar", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						return true;
					}
				}
			}
		}
		catch
		{
		}
		return false;
	}

	private static void HideAllNativeEnemyHealth()
	{
		GameObject[] array = EnemyNativeHealthRoots.Values.ToArray();
		foreach (GameObject val in array)
		{
			if ((Object)(object)val != (Object)null)
			{
				val.SetActive(false);
			}
		}
	}

	private static void UpdateNativeEnemyHealth()
	{
		//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f7: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = FindNativeEnemyHealthTemplate();
		if ((Object)(object)val == (Object)null)
		{
			return;
		}
		Camera main = Camera.main;
		if ((Object)(object)main == (Object)null)
		{
			return;
		}
		EnemyParent[] array = Object.FindObjectsOfType<EnemyParent>();
		HashSet<int> hashSet = new HashSet<int>();
		float num = ((Plugin.EnemyHealthBarMaxDistance != null) ? Plugin.EnemyHealthBarMaxDistance.Value : 40f);
		float num2 = ((Plugin.EnemyHealthBarVisibleSeconds != null) ? Plugin.EnemyHealthBarVisibleSeconds.Value : 4f);
		EnemyParent[] array2 = array;
		foreach (EnemyParent val2 in array2)
		{
			if ((Object)(object)val2 == (Object)null || (Object)(object)((Component)val2).gameObject == (Object)null || !((Component)val2).gameObject.activeInHierarchy)
			{
				continue;
			}
			Enemy componentInChildren = ((Component)val2).GetComponentInChildren<Enemy>(true);
			if ((Object)(object)componentInChildren == (Object)null)
			{
				continue;
			}
			int instanceID = ((Object)val2).GetInstanceID();
			hashSet.Add(instanceID);
			float current;
			float maximum;
			bool flag = TryGetEnemyHealth(componentInChildren, out current, out maximum);
			GameObject value = null;
			EnemyNativeHealthRoots.TryGetValue(instanceID, out value);
			if (!flag)
			{
				if ((Object)(object)value != (Object)null)
				{
					value.SetActive(false);
				}
				continue;
			}
			if (!EnemyLastObservedHealth.TryGetValue(instanceID, out var value2))
			{
				EnemyLastObservedHealth[instanceID] = current;
				if (!(maximum > 0f) || !(current < maximum - 0.01f))
				{
					if ((Object)(object)value != (Object)null)
					{
						value.SetActive(false);
					}
					continue;
				}
				EnemyHealthVisibleUntil[instanceID] = Time.realtimeSinceStartup + num2;
			}
			if (Mathf.Abs(current - value2) > 0.01f)
			{
				EnemyHealthVisibleUntil[instanceID] = Time.realtimeSinceStartup + num2;
				EnemyLastObservedHealth[instanceID] = current;
			}
			else
			{
				EnemyLastObservedHealth[instanceID] = current;
			}
			float value3 = -1000f;
			EnemyHealthVisibleUntil.TryGetValue(instanceID, out value3);
			if (Time.realtimeSinceStartup > value3)
			{
				if ((Object)(object)value != (Object)null)
				{
					value.SetActive(false);
				}
				continue;
			}
			float num3 = Vector3.Distance(((Component)main).transform.position, ((Component)val2).transform.position);
			if (num3 > num)
			{
				if ((Object)(object)value != (Object)null)
				{
					value.SetActive(false);
				}
				continue;
			}
			GameObject val3 = value;
			if ((Object)(object)val3 == (Object)null)
			{
				val3 = CreateNativeEnemyHealthRoot(componentInChildren, val);
				if ((Object)(object)val3 == (Object)null)
				{
					continue;
				}
				EnemyNativeHealthRoots[instanceID] = val3;
				Info("Native enemy HealthUI attached | enemy=" + (val2.enemyName ?? ((Object)val2).name ?? "<unnamed>") + " | hp=" + current.ToString("0.##") + "/" + maximum.ToString("0.##"));
			}
			val3.SetActive(true);
			val3.transform.position = GetEnemyHealthAnchorFromHitbox(val2, componentInChildren);
			val3.transform.rotation = ((Component)main).transform.rotation;
			GameObject val4 = FindChildGameObject(val3.transform, "Health");
			if ((Object)(object)val4 != (Object)null)
			{
				SetAnyTextComponent(val4, Mathf.CeilToInt(current).ToString());
				GameObject val5 = FindChildGameObject(val4.transform, "HealthMax");
				if ((Object)(object)val5 != (Object)null)
				{
					SetAnyTextComponent(val5, Mathf.CeilToInt(maximum).ToString());
				}
			}
		}
		KeyValuePair<int, GameObject>[] array3 = EnemyNativeHealthRoots.ToArray();
		for (int j = 0; j < array3.Length; j++)
		{
			KeyValuePair<int, GameObject> keyValuePair = array3[j];
			if (!hashSet.Contains(keyValuePair.Key))
			{
				if ((Object)(object)keyValuePair.Value != (Object)null)
				{
					Object.Destroy((Object)(object)keyValuePair.Value);
				}
				EnemyNativeHealthRoots.Remove(keyValuePair.Key);
				EnemyObservedMaxHealth.Remove(keyValuePair.Key);
				EnemyLastObservedHealth.Remove(keyValuePair.Key);
				EnemyHealthVisibleUntil.Remove(keyValuePair.Key);
			}
		}
	}

	private static Vector3 GetEnemyHealthAnchorFromHitbox(EnemyParent parent, Enemy enemy)
	{
		//IL_002d: 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_0062: 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_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_009d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0199: Unknown result type (might be due to invalid IL or missing references)
		//IL_019e: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_016a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0176: Unknown result type (might be due to invalid IL or missing references)
		//IL_0188: Unknown result type (might be due to invalid IL or missing references)
		//IL_0192: Unknown result type (might be due to invalid IL or missing references)
		//IL_0106: Unknown result type (might be due to invalid IL or missing references)
		//IL_010b: Unknown result type (might be due to invalid IL or missing references)
		//IL_010f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0114: Unknown result type (might be due to invalid IL or missing references)
		//IL_0126: Unknown result type (might be due to invalid IL or missing references)
		//IL_012c: Unknown result type (might be due to invalid IL or missing references)
		//IL_014c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0141: Unknown result type (might be due to invalid IL or missing references)
		//IL_0143: Unknown result type (might be due to invalid IL or missing references)
		Transform val = (((Object)(object)parent != (Object)null) ? ((Component)parent).transform : (((Object)(object)enemy != (Object)null) ? ((Component)enemy).transform : null));
		if ((Object)(object)val == (Object)null)
		{
			return Vector3.zero;
		}
		Collider[] colliders = (((Object)(object)parent != (Object)null) ? ((Component)parent).GetComponentsInChildren<Collider>(true) : ((Component)enemy).GetComponentsInChildren<Collider>(true));
		Bounds bounds;
		bool flag = TryGetCompactEnemyColliderBounds(colliders, val.position, includeTriggers: false, out bounds);
		if (!flag)
		{
			flag = TryGetCompactEnemyColliderBounds(colliders, val.position, includeTriggers: true, out bounds);
		}
		if (flag)
		{
			return new Vector3(((Bounds)(ref bounds)).center.x, ((Bounds)(ref bounds)).max.y + 0.08f, ((Bounds)(ref bounds)).center.z);
		}
		Renderer[] array = (((Object)(object)parent != (Object)null) ? ((Component)parent).GetComponentsInChildren<Renderer>(true) : ((Component)enemy).GetComponentsInChildren<Renderer>(true));
		bool flag2 = false;
		Bounds val2 = default(Bounds);
		((Bounds)(ref val2))..ctor(val.position, Vector3.zero);
		Renderer[] array2 = array;
		foreach (Renderer val3 in array2)
		{
			if ((Object)(object)val3 == (Object)null || !val3.enabled || !((Component)val3).gameObject.activeInHierarchy)
			{
				continue;
			}
			Bounds bounds2 = val3.bounds;
			Vector3 size = ((Bounds)(ref bounds2)).size;
			if (!(((Vector3)(ref size)).magnitude > 6f) && !(Vector3.Distance(((Bounds)(ref bounds2)).center, val.position) > 4f))
			{
				if (!flag2)
				{
					val2 = bounds2;
					flag2 = true;
				}
				else
				{
					((Bounds)(ref val2)).Encapsulate(bounds2);
				}
			}
		}
		if (flag2)
		{
			return new Vector3(((Bounds)(ref val2)).center.x, ((Bounds)(ref val2)).max.y + 0.08f, ((Bounds)(ref val2)).center.z);
		}
		return val.position + Vector3.up * 1.6f;
	}

	private static bool TryGetCompactEnemyColliderBounds(Collider[] colliders, Vector3 enemyOrigin, bool includeTriggers, out Bounds bounds)
	{
		//IL_0001: 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)
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: 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_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: 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)
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		bounds = default(Bounds);
		bool flag = false;
		if (colliders == null)
		{
			return false;
		}
		foreach (Collider val in colliders)
		{
			if ((Object)(object)val == (Object)null || !val.enabled || (!includeTriggers && val.isTrigger))
			{
				continue;
			}
			Bounds bounds2 = val.bounds;
			Vector3 size = ((Bounds)(ref bounds2)).size;
			float magnitude = ((Vector3)(ref size)).magnitude;
			float num = Vector3.Distance(((Bounds)(ref bounds2)).center, enemyOrigin);
			if (!(magnitude < 0.05f) && !(magnitude > 6f) && !(num > 4f))
			{
				if (!flag)
				{
					bounds = bounds2;
					flag = true;
				}
				else
				{
					((Bounds)(ref bounds)).Encapsulate(bounds2);
				}
			}
		}
		return flag;
	}

	private static GameObject FindNativeEnemyHealthTemplate()
	{
		if ((Object)(object)_nativeEnemyHealthTemplate != (Object)null)
		{
			return _nativeEnemyHealthTemplate;
		}
		Transform[] array = Object.FindObjectsOfType<Transform>();
		Transform[] array2 = array;
		foreach (Transform val in array2)
		{
			if (!((Object)(object)val == (Object)null) && string.Equals(((Object)val).name, "Health", StringComparison.OrdinalIgnoreCase))
			{
				Component[] components = ((Component)val).GetComponents<Component>();
				if (components.Any((Component c) => (Object)(object)c != (Object)null && string.Equals(((object)c).GetType().Name, "HealthUI", StringComparison.OrdinalIgnoreCase)))
				{
					_nativeEnemyHealthTemplate = ((Component)val).gameObject;
					Info("Native enemy HUD template resolved: " + GetHierarchyPath(val));
					return _nativeEnemyHealthTemplate;
				}
			}
		}
		return null;
	}

	private static GameObject CreateNativeEnemyHealthRoot(Enemy enemy, GameObject template)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Expected O, but got Unknown
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: 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_0117: Unknown result type (might be due to invalid IL or missing references)
		//IL_012d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0143: Unknown result type (might be due to invalid IL or missing references)
		//IL_014f: Unknown result type (might be due to invalid IL or missing references)
		//IL_015b: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			GameObject val = new GameObject("LegacyREPOCompat - Native Enemy Health");
			Canvas val2 = val.AddComponent<Canvas>();
			val2.renderMode = (RenderMode)2;
			val2.worldCamera = Camera.main;
			val2.sortingOrder = 1000;
			RectTransform component = val.GetComponent<RectTransform>();
			if ((Object)(object)component != (Object)null)
			{
				component.sizeDelta = new Vector2(220f, 70f);
			}
			val.transform.localScale = Vector3.one * 0.006f;
			GameObject val3 = Object.Instantiate<GameObject>(template, val.transform, false);
			((Object)val3).name = "Health";
			val3.SetActive(true);
			Component[] components = val3.GetComponents<Component>();
			Component[] array = components;
			foreach (Component val4 in array)
			{
				if (!((Object)(object)val4 == (Object)null) && string.Equals(((object)val4).GetType().Name, "HealthUI", StringComparison.OrdinalIgnoreCase))
				{
					Behaviour val5 = (Behaviour)(object)((val4 is Behaviour) ? val4 : null);
					if ((Object)(object)val5 != (Object)null)
					{
						val5.enabled = false;
					}
					Object.Destroy((Object)(object)val4);
				}
			}
			RectTransform component2 = val3.GetComponent<RectTransform>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.anchorMin = new Vector2(0.5f, 0.5f);
				component2.anchorMax = new Vector2(0.5f, 0.5f);
				component2.pivot = new Vector2(0.5f, 0.5f);
				component2.anchoredPosition = Vector2.zero;
				((Transform)component2).localScale = Vector3.one;
			}
			return val;
		}
		catch (Exception ex)
		{
			Warn("Could not create native enemy HealthUI clone: " + Unwrap(ex));
			return null;
		}
	}

	private static bool TryGetEnemyHealth(Enemy enemy, out float current, out float maximum)
	{
		current = 0f;
		maximum = 0f;
		if ((Object)(object)enemy == (Object)null)
		{
			return false;
		}
		object memberValue = GetMemberValue(enemy, "Health");
		if (memberValue == null)
		{
			return false;
		}
		string[] names = new string[7] { "healthCurrent", "currentHealth", "HealthCurrent", "health", "Health", "hp", "HP" };
		string[] names2 = new string[8] { "healthMax", "maxHealth", "HealthMax", "MaxHealth", "healthMaximum", "maximumHealth", "startingHealth", "startHealth" };
		bool flag = TryReadNumericMember(memberValue, names, out current);
		bool flag2 = TryReadNumericMember(memberValue, names2, out maximum);
		if (!flag)
		{
			Type type = memberValue.GetType();
			BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
			FieldInfo[] fields = type.GetFields(bindingAttr);
			foreach (FieldInfo fieldInfo in fields)
			{
				string text = fieldInfo.Name.ToLowerInvariant();
				if (text.Contains("health") && !text.Contains("max") && !text.Contains("previous") && !text.Contains("old"))
				{
					object value;
					try
					{
						value = fieldInfo.GetValue(memberValue);
					}
					catch
					{
						continue;
					}
					if (TryConvertFloat(value, out current))
					{
						flag = true;
						break;
					}
				}
			}
		}
		if (!flag)
		{
			return false;
		}
		int instanceID = ((Object)enemy).GetInstanceID();
		if (!flag2 || maximum <= 0f)
		{
			if (!EnemyObservedMaxHealth.TryGetValue(instanceID, out var value2))
			{
				value2 = current;
			}
			value2 = Mathf.Max(value2, current);
			EnemyObservedMaxHealth[instanceID] = value2;
			maximum = value2;
		}
		else
		{
			EnemyObservedMaxHealth[instanceID] = Mathf.Max(maximum, current);
			maximum = Mathf.Max(maximum, current);
		}
		current = Mathf.Max(0f, current);
		return maximum > 0f;
	}

	private static GameObject FindChildGameObject(Transform root, string name)
	{
		if ((Object)(object)root == (Object)null)
		{
			return null;
		}
		Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren<Transform>(true);
		Transform[] array = componentsInChildren;
		foreach (Transform val in array)
		{
			if ((Object)(object)val != (Object)null && string.Equals(((Object)val).name, name, StringComparison.OrdinalIgnoreCase))
			{
				return ((Component)val).gameObject;
			}
		}
		return null;
	}

	private static void SetAnyTextComponent(GameObject obj, string text)
	{
		if ((Object)(object)obj == (Object)null)
		{
			return;
		}
		Component[] components = obj.GetComponents<Component>();
		Component[] array = components;
		foreach (Component val in array)
		{
			if ((Object)(object)val == (Object)null)
			{
				continue;
			}
			PropertyInfo property = ((object)val).GetType().GetProperty("text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (!(property == null) && property.CanWrite && !(property.PropertyType != typeof(string)))
			{
				try
				{
					property.SetValue(val, text, null);
					break;
				}
				catch
				{
				}
			}
		}
	}

	private static bool TryReadNumericMember(object target, string[] names, out float value)
	{
		value = 0f;
		if (target == null)
		{
			return false;
		}
		foreach (string name in names)
		{
			object memberValue = GetMemberValue(target, name);
			if (TryConvertFloat(memberValue, out value))
			{
				return true;
			}
		}
		return false;
	}

	private static bool TryConvertFloat(object value, out float result)
	{
		result = 0f;
		if (value == null)
		{
			return false;
		}
		try
		{
			if (value is float)
			{
				result = (float)value;
				return true;
			}
			if (value is double)
			{
				result = (float)(double)value;
				return true;
			}
			if (value is int)
			{
				result = (int)value;
				return true;
			}
			if (value is uint)
			{
				result = (uint)value;
				return true;
			}
			if (value is short)
			{
				result = (short)value;
				return true;
			}
			if (value is ushort)
			{
				result = (int)(ushort)value;
				return true;
			}
			if (value is byte)
			{
				result = (int)(byte)value;
				return true;
			}
			if (value is long)
			{
				result = (long)value;
				return true;
			}
			result = Convert.ToSingle(value);
			return true;
		}
		catch
		{
			return false;
		}
	}

	public static IEnumerator ArenaAudioLimiterLoop()
	{
		while (true)
		{
			yield return (object)new WaitForSeconds(0.75f);
			try
			{
				string currentLevelName = GetCurrentLevelName();
				if (string.IsNullOrEmpty(currentLevelName) || currentLevelName.IndexOf("Arena", StringComparison.OrdinalIgnoreCase) < 0)
				{
					continue;
				}
				float num = ((Plugin.ArenaMusicMaxVolume == null) ? 0.3f : Mathf.Clamp01(Plugin.ArenaMusicMaxVolume.Value));
				AudioSource[] array = Object.FindObjectsOfType<AudioSource>();
				AudioSource[] array2 = array;
				foreach (AudioSource val in array2)
				{
					if (!((Object)(object)val == (Object)null) && val.isPlaying && val.loop && !((Object)(object)val.clip == (Object)null) && !(val.clip.length < 15f))
					{
						if (val.volume > num)
						{
							val.volume = num;
						}
						int instanceID = ((Object)val).GetInstanceID();
						if (!ArenaAudioSourcesCapped.Contains(instanceID))
						{
							ArenaAudioSourcesCapped.Add(instanceID);
							Info("ARENA MUSIC CAPPED | " + GetHierarchyPath(((Component)val).transform) + " | clip=" + ((Object)val.clip).name + " | max=" + num);
						}
					}
				}
			}
			catch (Exception ex)
			{
				Warn("Arena music limiter failed: " + Unwrap(ex));
			}
		}
	}

	public static void SoundPlayLoopLegacyCompat(object sound, bool playing, float pitch, float volume, float fade)
	{
		if (sound == null)
		{
			return;
		}
		try
		{
			Type type = sound.GetType();
			MethodInfo[] array = (from m in type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
				where m.Name == "PlayLoop"
				orderby m.GetParameters().Length
				select m).ToArray();
			MethodInfo[] array2 = array;
			foreach (MethodInfo methodInfo in array2)
			{
				ParameterInfo[] parameters = methodInfo.GetParameters();
				object[] array3 = new object[parameters.Length];
				int num2 = 0;
				bool flag = true;
				for (int num3 = 0; num3 < parameters.Length; num3++)
				{
					ParameterInfo parameterInfo = parameters[num3];
					string text = (parameterInfo.Name ?? string.Empty).ToLowerInvariant();
					if (parameterInfo.ParameterType == typeof(bool))
					{
						array3[num3] = playing;
					}
					else if (parameterInfo.ParameterType == typeof(float))
					{
						float num4;
						if (text.Contains("pitch"))
						{
							num4 = pitch;
						}
						else if (text.Contains("volume"))
						{
							num4 = volume;
						}
						else if (text.Contains("fade") || text.Contains("time"))
						{
							num4 = fade;
						}
						else
						{
							num4 = num2 switch
							{
								1 => volume, 
								0 => pitch, 
								_ => fade, 
							};
							num2++;
						}
						if (text.Contains("volume") && CurrentLevelContains("Arena") && Plugin.ArenaMusicMaxVolume != null)
						{
							num4 = Mathf.Min(num4, Plugin.ArenaMusicMaxVolume.Value);
						}
						array3[num3] = num4;
					}
					else
					{
						if (!parameterInfo.HasDefaultValue)
						{
							flag = false;
							break;
						}
						array3[num3] = parameterInfo.DefaultValue;
					}
				}
				if (flag)
				{
					try
					{
						methodInfo.Invoke(sound, array3);
						return;
					}
					catch
					{
					}
				}
			}
			string item = type.FullName + "::PlayLoop";
			if (!SoundPlayLoopSignaturesLogged.Contains(item))
			{
				SoundPlayLoopSignaturesLogged.Add(item);
				Warn("Legacy Sound.PlayLoop bridge found no compatible current overload on " + type.FullName + ". Current overloads: " + string.Join(" | ", array.Select(DescribeMethod).ToArray()));
			}
		}
		catch (Exception ex)
		{
			Warn("Legacy Sound.PlayLoop bridge failed: " + Unwrap(ex));
		}
	}

	private static string DescribeMethod(MethodInfo method)
	{
		if (method == null)
		{
			return "<null>";
		}
		return method.Name + "(" + string.Join(",", (from p in method.GetParameters()
			select p.ParameterType.Name + " " + p.Name).ToArray()) + ")";
	}

	public static void RegisterNetworkPrefabLegacyCompat(GameObject prefab)
	{
		if ((Object)(object)prefab == (Object)null)
		{
			return;
		}
		try
		{
			CacheLegacyNetworkPrefab(prefab);
			Assembly assembly = FindAssembly("REPOLib");
			Type type = ((assembly == null) ? null : assembly.GetType("REPOLib.Modules.NetworkPrefabs", throwOnError: false));
			if (type == null)
			{
				return;
			}
			MethodInfo methodInfo = type.GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault(delegate(MethodInfo m)
			{
				if (m.Name != "RegisterNetworkPrefab")
				{
					return false;
				}
				ParameterInfo[] parameters = m.GetParameters();
				return parameters.Length == 1 && parameters[0].ParameterType == typeof(GameObject);
			});
			if (methodInfo != null)
			{
				methodInfo.Invoke(null, new object[1] { prefab });
			}
			Info("PaintedUtils: legacy network prefab registered/cached | id='" + ((Object)prefab).name + "'.");
		}
		catch (Exception ex)
		{
			Warn("PaintedUtils RegisterNetworkPrefab compatibility warning | prefab='" + (((Object)(object)prefab != (Object)null) ? ((Object)prefab).name : "<null>") + "' | " + Unwrap(ex));
		}
	}

	private static void CacheLegacyNetworkPrefab(GameObject prefab)
	{
		if ((Object)(object)prefab == (Object)null || string.IsNullOrEmpty(((Object)prefab).name))
		{
			return;
		}
		LegacyNetworkPrefabCache[((Object)prefab).name] = prefab;
		if (((Object)prefab).name.EndsWith("(Clone)", StringComparison.OrdinalIgnoreCase))
		{
			string text = ((Object)prefab).name.Substring(0, ((Object)prefab).name.Length - "(Clone)".Length).Trim();
			if (!string.IsNullOrEmpty(text))
			{
				LegacyNetworkPrefabCache[text] = prefab;
			}
		}
	}

	private static string NormalizePrefabId(string value)
	{
		if (string.IsNullOrEmpty(value))
		{
			return string.Empty;
		}
		string text = value.Trim();
		if (text.EndsWith("(Clone)", StringComparison.OrdinalIgnoreCase))
		{
			text = text.Substring(0, text.Length - 7).Trim();
		}
		char[] value2 = text.Where(char.IsLetterOrDigit).Select(char.ToLowerInvariant).ToArray();
		return new string(value2);
	}

	private static void EnsureLegacyNetworkPrefabRegistered(GameObject prefab)
	{
		if ((Object)(object)prefab == (Object)null)
		{
			return;
		}
		CacheLegacyNetworkPrefab(prefab);
		try
		{
			Assembly assembly = FindAssembly("REPOLib");
			Type type = ((assembly == null) ? null : assembly.GetType("REPOLib.Modules.NetworkPrefabs", throwOnError: false));
			if (type == null)
			{
				return;
			}
			MethodInfo methodInfo = type.GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault(delegate(MethodInfo m)
			{
				if (m.Name != "RegisterNetworkPrefab")
				{
					return false;
				}
				ParameterInfo[] parameters = m.GetParameters();
				return parameters.Length == 1 && parameters[0].ParameterType == typeof(GameObject);
			});
			if (methodInfo != null)
			{
				methodInfo.Invoke(null, new object[1] { prefab });
				Info("PaintedUtils: runtime network prefab registration ensured | id='" + ((Object)prefab).name + "'.");
			}
		}
		catch (Exception ex)
		{
			Warn("PaintedUtils runtime network prefab registration warning | prefab='" + ((Object)prefab).name + "' | " + Unwrap(ex));
		}
	}

	private static GameObject FindLegacyNetworkPrefab(string prefabId)
	{
		if (string.IsNullOrEmpty(prefabId))
		{
			return null;
		}
		if (LegacyNetworkPrefabCache.TryGetValue(prefabId, out var value) && (Object)(object)value != (Object)null)
		{
			return value;
		}
		GameObject[] array = Resources.FindObjectsOfTypeAll<GameObject>();
		string text = NormalizePrefabId(prefabId);
		GameObject val = null;
		GameObject[] array2 = array;
		foreach (GameObject val2 in array2)
		{
			if (!((Object)(object)val2 == (Object)null) && !string.IsNullOrEmpty(((Object)val2).name))
			{
				if (string.Equals(((Object)val2).name, prefabId, StringComparison.OrdinalIgnoreCase))
				{
					CacheLegacyNetworkPrefab(val2);
					return val2;
				}
				if ((Object)(object)val == (Object)null && !string.IsNullOrEmpty(text) && string.Equals(NormalizePrefabId(((Object)val2).name), text, StringComparison.OrdinalIgnoreCase))
				{
					val = val2;
				}
			}
		}
		if ((Object)(object)val != (Object)null)
		{
			CacheLegacyNetworkPrefab(val);
			Info("PaintedUtils: fuzzy legacy prefab resolved | requested='" + prefabId + "' | actual='" + ((Object)val).name + "'.");
			return val;
		}
		return null;
	}

	public static GameObject GetNetworkPrefabLegacyCompat(string prefabId)
	{
		try
		{
			if (string.IsNullOrEmpty(prefabId))
			{
				return null;
			}
			GameObject val = FindLegacyNetworkPrefab(prefabId);
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			Assembly assembly = FindAssembly("REPOLib");
			Type type = ((assembly == null) ? null : assembly.GetType("REPOLib.Modules.NetworkPrefabs", throwOnError: false));
			if (type == null)
			{
				return null;
			}
			PropertyInfo property = type.GetProperty("Prefabs", BindingFlags.Static | BindingFlags.Public);
			object obj = ((property == null) ? null : property.GetValue(null, null));
			if (obj is IDictionary dictionary && dictionary.Contains(prefabId))
			{
				object? obj2 = dictionary[prefabId];
				return (GameObject)((obj2 is GameObject) ? obj2 : null);
			}
			MethodInfo methodInfo = type.GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault((MethodInfo m) => m.Name == "GetNetworkPrefabRef" && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(string));
			if (methodInfo != null)
			{
				object value = methodInfo.Invoke(null, new object[1] { prefabId });
				GameObject val2 = ToGameObject(value);
				if ((Object)(object)val2 != (Object)null)
				{
					return val2;
				}
			}
			return Resources.Load<GameObject>(prefabId);
		}
		catch (Exception ex)
		{
			Warn("PaintedUtils GetPrefab compatibility failed for '" + prefabId + "': " + Unwrap(ex));
			return null;
		}
	}

	public static GameObject SpawnNetworkPrefabLegacyCompat(string prefabId, Vector3 position, Quaternion rotation, byte group, object[] data)
	{
		//IL_016f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0179: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			Assembly assembly = FindAssembly("REPOLib");
			Type type = ((assembly == null) ? null : assembly.GetType("REPOLib.Modules.NetworkPrefabs", throwOnError: false));
			if (type == null)
			{
				throw new MissingMemberException("REPOLib.Modules.NetworkPrefabs introuvable.");
			}
			MethodInfo methodInfo = type.GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault(delegate(MethodInfo m)
			{
				if (m.Name != "SpawnNetworkPrefab")
				{
					return false;
				}
				ParameterInfo[] parameters = m.GetParameters();
				return parameters.Length == 5 && parameters[0].ParameterType.Name.IndexOf("PrefabRef", StringComparison.OrdinalIgnoreCase) >= 0 && parameters[1].ParameterType == typeof(Vector3) && parameters[2].ParameterType == typeof(Quaternion) && parameters[3].ParameterType == typeof(byte) && parameters[4].ParameterType == typeof(object[]);
			});
			if (methodInfo == null)
			{
				throw new MissingMethodException("NetworkPrefabs.SpawnNetworkPrefab(PrefabRef, Vector3, Quaternion, byte, object[]) introuvable.");
			}
			MethodInfo methodInfo2 = type.GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault((MethodInfo m) => m.Name == "GetNetworkPrefabRef" && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(string));
			object obj = ((methodInfo2 == null) ? null : methodInfo2.Invoke(null, new object[1] { prefabId }));
			if (obj == null)
			{
				GameObject val = FindLegacyNetworkPrefab(prefabId);
				if ((Object)(object)val != (Object)null)
				{
					EnsureLegacyNetworkPrefabRegistered(val);
					if (methodInfo2 != null)
					{
						try
						{
							obj = methodInfo2.Invoke(null, new object[1] { prefabId });
						}
						catch
						{
							obj = null;
						}
					}
					Type parameterType = methodInfo.GetParameters()[0].ParameterType;
					if (obj == null)
					{
						obj = ConstructPrefabRef(parameterType, val);
					}
					if (obj != null)
					{
						Info("PaintedUtils: built modern PrefabRef from legacy cached prefab | id='" + prefabId + "'.");
					}
				}
			}
			if (obj == null)
			{
				Warn("PaintedUtils: network prefab ID not registered/resolvable: " + prefabId);
				return null;
			}
			object? obj3 = methodInfo.Invoke(null, new object[5] { obj, position, rotation, group, data });
			GameObject result = (GameObject)((obj3 is GameObject) ? obj3 : null);
			Info("PaintedUtils: legacy SpawnNetworkPrefab adapted for '" + prefabId + "'.");
			return result;
		}
		catch (Exception ex)
		{
			Error("PaintedUtils SpawnNetworkPrefab compatibility failed: " + Unwrap(ex));
			return null;
		}
	}

	private static List<GameObject> GetLegacyBucket(object levelValuables, string bucket)
	{
		try
		{
			List<GameObject> list = new List<GameObject>();
			if (levelValuables == null)
			{
				return list;
			}
			Type type = levelValuables.GetType();
			BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
			FieldInfo fieldInfo = type.GetFields(bindingAttr).FirstOrDefault((FieldInfo x) => string.Equals(x.Name, bucket, StringComparison.OrdinalIgnoreCase));
			if (fieldInfo != null)
			{
				AddObjectsFromCollection(list, fieldInfo.GetValue(levelValuables));
			}
			PropertyInfo propertyInfo = type.GetProperties(bindingAttr).FirstOrDefault((PropertyInfo x) => x.CanRead && string.Equals(x.Name, bucket, StringComparison.OrdinalIgnoreCase));
			if (list.Count == 0 && propertyInfo != null)
			{
				AddObjectsFromCollection(list, propertyInfo.GetValue(levelValuables, null));
			}
			if (list.Count > 0)
			{
				Info("Legacy valuable bridge: LevelValuables." + bucket + " converted to " + list.Count + " GameObject(s).");
				return list;
			}
			Assembly assembly = FindAssembly("REPOLib");
			if (assembly != null)
			{
				foreach (Type item in SafeGetTypes(assembly))
				{
					if (item == null || item.Name.IndexOf("LevelValuables", StringComparison.OrdinalIgnoreCase) < 0)
					{
						continue;
					}
					MethodInfo[] methods = item.GetMethods(BindingFlags.Static | BindingFlags.Public);
					foreach (MethodInfo methodInfo in methods)
					{
						if (methodInfo.Name.IndexOf("Valuable", StringComparison.OrdinalIgnoreCase) < 0)
						{
							continue;
						}
						ParameterInfo[] parameters = methodInfo.GetParameters();
						if (parameters.Length == 1 && parameters[0].ParameterType.IsInstanceOfType(levelValuables))
						{
							object collection = methodInfo.Invoke(null, new object[1] { levelValuables });
							AddObjectsFromCollection(list, collection);
							if (list.Count > 0)
							{
								return list;
							}
						}
					}
				}
			}
			Warn("Legacy valuable bridge: could not resolve LevelValuables bucket '" + bucket + "'. Modern members: " + DescribeMembers(type));
			return list;
		}
		catch (Exception ex)
		{
			Error("GetLegacyBucket(" + bucket + ") failed: " + Unwrap(ex));
			return new List<GameObject>();
		}
	}

	public static void InstallBackroomsEnemySpawnFix(Harmony harmony)
	{
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_0082: Expected O, but got Unknown
		//IL_0082: Expected O, but got Unknown
		try
		{
			MethodInfo method = typeof(EnemyDirector).GetMethod("FirstSpawnPointAdd", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			MethodInfo method2 = typeof(Bridge).GetMethod("BackroomsFirstSpawnPointPrefix", BindingFlags.Static | BindingFlags.Public);
			MethodInfo method3 = typeof(Bridge).GetMethod("BackroomsFirstSpawnPointFinalizer", BindingFlags.Static | BindingFlags.Public);
			if (method == null || method2 == null || method3 == null)
			{
				Warn("Backrooms: EnemyDirector.FirstSpawnPointAdd compatibility target not found.");
				return;
			}
			harmony.Patch((MethodBase)method, new HarmonyMethod(method2), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(method3), (HarmonyMethod)null);
			Info("Backrooms: EnemyDirector.FirstSpawnPointAdd replacement patch active.");
		}
		catch (Exception ex)
		{
			Error("Backrooms enemy-spawn compatibility patch failed: " + Unwrap(ex));
		}
	}

	public static bool BackroomsFirstSpawnPointPrefix(EnemyDirector __instance, EnemyParent _enemyParent)
	{
		//IL_0212: Unknown result type (might be due to invalid IL or missing references)
		if (!IsBackroomsLevel())
		{
			return true;
		}
		try
		{
			if ((Object)(object)__instance == (Object)null || (Object)(object)_enemyParent == (Object)null)
			{
				return true;
			}
			List<LevelPoint> list = SemiFunc.LevelPointsGetAll();
			if (list == null || list.Count == 0)
			{
				if (!_backroomsNoLevelPointLogged)
				{
					_backroomsNoLevelPointLogged = true;
					Warn("Backrooms: no LevelPoint available yet; falling back to vanilla FirstSpawnPointAdd.");
				}
				return true;
			}
			FieldInfo field = typeof(EnemyParent).GetField("firstSpawnPoint", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field == null)
			{
				Warn("Backrooms: EnemyParent.firstSpawnPoint field not found; falling back to vanilla.");
				return true;
			}
			FieldInfo field2 = typeof(EnemyDirector).GetField("enemyFirstSpawnPoints", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			IList list2 = null;
			if (field2 != null)
			{
				object obj = field2.GetValue(__instance);
				if (obj == null)
				{
					try
					{
						obj = Activator.CreateInstance(field2.FieldType);
						field2.SetValue(__instance, obj);
					}
					catch
					{
						obj = null;
					}
				}
				list2 = obj as IList;
			}
			LevelPoint val = null;
			for (int i = 0; i < list.Count; i++)
			{
				LevelPoint val2 = list[i];
				if (!((Object)(object)val2 == (Object)null) && !val2.Truck && (list2 == null || !list2.Contains(val2)))
				{
					val = val2;
					break;
				}
			}
			if ((Object)(object)val == (Object)null && list2 != null)
			{
				list2.Clear();
				for (int j = 0; j < list.Count; j++)
				{
					LevelPoint val3 = list[j];
					if (!((Object)(object)val3 == (Object)null) && !val3.Truck)
					{
						val = val3;
						break;
					}
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				if (!_backroomsNoLevelPointLogged)
				{
					_backroomsNoLevelPointLogged = true;
					Warn("Backrooms: no usable non-truck LevelPoint found; falling back to vanilla.");
				}
				return true;
			}
			field.SetValue(_enemyParent, val);
			if (list2 != null && !list2.Contains(val))
			{
				list2.Add(val);
			}
			_backroomsSpawnPointAssignments++;
			if (!_backroomsSpawnReplacementLogged)
			{
				_backroomsSpawnReplacementLogged = true;
				Info("Backrooms: FirstSpawnPointAdd replaced successfully; valid first spawn points are now assigned without running the broken vanilla body.");
			}
			if (_backroomsSpawnPointAssignments <= 6)
			{
				Info(string.Concat("Backrooms: assigned first spawn point #", _backroomsSpawnPointAssignments, " at ", ((Component)val).transform.position, " to enemy '", ((Object)_enemyParent).name, "'."));
			}
			return false;
		}
		catch (Exception ex)
		{
			Error("Backrooms FirstSpawnPointAdd replacement failed; falling back to vanilla: " + Unwrap(ex));
			return true;
		}
	}

	public static Exception BackroomsFirstSpawnPointFinalizer(Exception __exception)
	{
		if (__exception == null)
		{
			return null;
		}
		try
		{
			if (__exception is NullReferenceException && IsBackroomsLevel())
			{
				if (!_backroomsSpawnSuppressionLogged)
				{
					_backroomsSpawnSuppressionLogged = true;
					Warn("Backrooms: fallback suppressed FirstSpawnPointAdd NullReferenceException.");
				}
				return null;
			}
		}
		catch (Exception ex)
		{
			Error("Backrooms FirstSpawnPointAdd fallback guard failed: " + Unwrap(ex));
		}
		return __exception;
	}

	private static string GetCurrentLevelName()
	{
		try
		{
			FieldInfo field = typeof(RunManager).GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			object obj = ((field == null) ? null : field.GetValue(null));
			if (obj == null)
			{
				return string.Empty;
			}
			object memberValue = GetMemberValue(obj, "levelCurrent");
			if (memberValue == null)
			{
				return string.Empty;
			}
			Object val = (Object)((memberValue is Object) ? memberValue : null);
			if (val != (Object)null && !string.IsNullOrEmpty(val.name))
			{
				return val.name;
			}
			object memberValue2 = GetMemberValue(memberValue, "name");
			return (memberValue2 == null) ? string.Empty : memberValue2.ToString();
		}
		catch
		{
			return string.Empty;
		}
	}

	private static bool CurrentLevelContains(string text)
	{
		string currentLevelName = GetCurrentLevelName();
		if (!string.IsNullOrEmpty(currentLevelName))
		{
			return currentLevelName.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0;
		}
		return false;
	}

	private static bool IsBackroomsLevel()
	{
		return CurrentLevelContains("Backrooms");
	}

	public static void InstallMinecraftLoadingReferenceMigration(Harmony harmony)
	{
		//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Expected O, but got Unknown
		if (harmony == null || Plugin.RepairMinecraftLoadingGraphics == null || !Plugin.RepairMinecraftLoadingGraphics.Value)
		{
			return;
		}
		try
		{
			Assembly assembly = FindAssembly("REPOLib");
			Type type = ((assembly == null) ? null : assembly.GetType("REPOLib.Modules.Levels", throwOnError: false));
			MethodInfo methodInfo = ((type == null) ? null : type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo m)
			{
				ParameterInfo[] parameters = m.GetParameters();
				return m.Name == "RegisterLevel" && parameters.Length == 1 && parameters[0].ParameterType.FullName == "REPOLib.Objects.Sdk.LevelContent";
			}));
			MethodInfo method = typeof(Bridge).GetMethod("MinecraftLoadingReferenceRegistrationPrefix", BindingFlags.Static | BindingFlags.Public);
			if (methodInfo == null || method == null)
			{
				Warn("Minecraft loading-reference migration unavailable: REPOLib Levels.RegisterLevel(LevelContent) not found.");
				return;
			}
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			_minecraftLoadingReferenceMigrationInstalled = true;
			Info("Minecraft loading-reference migration installed on REPOLib.Modules.Levels.RegisterLevel.");
		}
		catch (Exception ex)
		{
			Error("Minecraft loading-reference migration install failed: " + Unwrap(ex));
		}
	}

	public static void MinecraftLoadingReferenceRegistrationPrefix(object __0)
	{
		if (!_minecraftLoadingReferenceMigrationInstalled || __0 == null || Plugin.RepairMinecraftLoadingGraphics == null || !Plugin.RepairMinecraftLoadingGraphics.Value)
		{
			return;
		}
		try
		{
			object memberValue = GetMemberValue(__0, "Level");
			if (memberValue == null)
			{
				return;
			}
			Object val = (Object)((memberValue is Object) ? memberValue : null);
			string text = ((val != (Object)null) ? val.name : Convert.ToString(GetMemberValue(memberValue, "name")));
			if (string.IsNullOrEmpty(text) || text.IndexOf("Minecraft Village", StringComparison.OrdinalIgnoreCase) < 0)
			{
				return;
			}
			object memberValue2 = GetMemberValue(__0, "Bundle");
			AssetBundle val2 = (AssetBundle)((memberValue2 is AssetBundle) ? memberValue2 : null);
			if ((Object)(object)val2 == (Object)null)
			{
				Warn("MINECRAFT LOADING MIGRATION skipped | level='" + text + "' | LevelContent.Bundle=<null>");
				return;
			}
			string[] allAssetNames = val2.GetAllAssetNames();
			LogVillageLoadingAssetDiscovery(val2, allAssetNames);
			LogVillageLoadingLevelState(__0, memberValue);
			int num = 0;
			if (RepairEmptyLoadingGraphicReference(memberValue, "LoadingGraphic01", val2, allAssetNames, "village3"))
			{
				num++;
			}
			if (RepairEmptyLoadingGraphicReference(memberValue, "LoadingGraphic02", val2, allAssetNames, "village2"))
			{
				num++;
			}
			if (RepairEmptyLoadingGraphicReference(memberValue, "LoadingGraphic03", val2, allAssetNames, "village1"))
			{
				num++;
			}
			Info("MINECRAFT LOADING MIGRATION registration complete | level='" + text + "' | repaired=" + num + "/3");
		}
		catch (Exception ex)
		{
			Warn("Minecraft loading-reference registration migration failed: " + Unwrap(ex));
		}
	}

	private static void LogVillageLoadingAssetDiscovery(AssetBundle bundle, string[] assetNames)
	{
		if ((Object)(object)bundle == (Object)null)
		{
			Warn("MINECRAFT LOADING DISCOVERY skipped | bundle=<null>");
			return;
		}
		if (assetNames == null)
		{
			assetNames = new string[0];
		}
		int num = 0;
		int num2 = 0;
		int num3 = 0;
		int num4 = 0;
		int num5 = 0;
		string[] array = assetNames;
		foreach (string text in array)
		{
			if (string.IsNullOrEmpty(text))
			{
				continue;
			}
			string text2 = text.ToLowerInvariant();
			bool flag = text2.EndsWith(".png") || text2.EndsWith(".jpg") || text2.EndsWith(".jpeg") || text2.EndsWith(".tga") || text2.EndsWith(".bmp") || text2.EndsWith(".psd");
			bool flag2 = text2.IndexOf("load", StringComparison.Ordinal) >= 0 || text2.IndexOf("splash", StringComparison.Ordinal) >= 0 || text2.IndexOf("graphic", StringComparison.Ordinal) >= 0 || text2.IndexOf("title", StringComparison.Ordinal) >= 0 || text2.IndexOf("village", StringComparison.Ordinal) >= 0;
			if (!flag && !flag2)
			{
				continue;
			}
			num++;
			List<string> list = new List<string>();
			try
			{
				Sprite val = bundle.LoadAsset<Sprite>(text);
				if ((Object)(object)val != (Object)null && !string.IsNullOrEmpty(((Object)val).name))
				{
					list.Add(((Object)val).name);
				}
			}
			catch
			{
			}
			try
			{
				Sprite[] array2 = bundle.LoadAssetWithSubAssets<Sprite>(text);
				if (array2 != null)
				{
					Sprite[] array3 = array2;
					foreach (Sprite val2 in array3)
					{
						if (!((Object)(object)val2 == (Object)null) && !string.IsNullOrEmpty(((Object)val2).name) && !list.Contains(((Object)val2).name))
						{
							list.Add(((Object)val2).name);
						}
					}
				}
			}
			catch
			{
			}
			if (list.Count > 0)
			{
				num2++;
				if (num4 < 80)
				{
					Info("MINECRAFT LOADING DISCOVERY sprite asset | asset='" + text + "' | sprites='" + string.Join(",", list.ToArray()) + "'");
					num4++;
				}
				continue;
			}
			try
			{
				Texture2D val3 = bundle.LoadAsset<Texture2D>(text);
				if ((Object)(object)val3 == (Object)null)
				{
					continue;
				}
				bool flag3 = ((Texture)val3).width >= 512 && ((Texture)val3).height >= 256;
				if (flag2 || flag3)
				{
					num3++;
					if (num5 < 80)
					{
						Info("MINECRAFT LOADING DISCOVERY texture asset | asset='" + text + "' | texture='" + ((Object)val3).name + "' | size=" + ((Texture)val3).width + "x" + ((Texture)val3).height + " | hint=" + flag2);
						num5++;
					}
				}
			}
			catch
			{
			}
		}
		Info("MINECRAFT LOADING DISCOVERY summary | totalAssets=" + assetNames.Length + " | plausiblePaths=" + num + " | spriteAssets=" + num2 + " | reportedLargeOrHintedTextures=" + num3 + " | logCaps=80/80");
		LogVillageLoadingDeepDiscovery(bundle, assetNames);
	}

	private static void LogVillageLoadingDeepDiscovery(AssetBundle bundle, string[] assetNames)
	{
		if ((Object)(object)bundle == (Object)null)
		{
			return;
		}
		if (assetNames == null)
		{
			assetNames = new string[0];
		}
		for (int i = 0; i < assetNames.Length; i++)
		{
			string text = assetNames[i];
			if (string.IsNullOrEmpty(text))
			{
				continue;
			}
			Info("MINECRAFT LOADING DEEP path | index=" + i + " | asset='" + text + "'");
			try
			{
				Object[] array = bundle.LoadAssetWithSubAssets<Object>(text);
				int num = ((array != null) ? array.Length : 0);
				Info("MINECRAFT LOADING DEEP subassets | asset='" + text + "' | count=" + num);
				if (array == null)
				{
					continue;
				}
				int num2 = Math.Min(array.Length, 120);
				for (int j = 0; j < num2; j++)
				{
					Object val = array[j];
					if (!(val == (Object)null))
					{
						Texture2D val2 = (Texture2D)(object)((val is Texture2D) ? val : null);
						Sprite val3 = (Sprite)(object)((val is Sprite) ? val : null);
						string text2 = (((Object)(object)val2 != (Object)null) ? (" | size=" + ((Texture)val2).width + "x" + ((Texture)val2).height) : (((Object)(object)val3 != (Object)null && (Object)(object)val3.texture != (Object)null) ? (" | spriteTexture='" + ((Object)val3.texture).name + "' | textureSize=" + ((Texture)val3.texture).width + "x" + ((Texture)val3.texture).height) : string.Empty));
						Info("MINECRAFT LOADING DEEP object | asset='" + text + "' | subIndex=" + j + " | type='" + ((object)val).GetType().FullName + "' | name='" + val.name + "'" + text2);
					}
				}
			}
			catch (Exception ex)
			{
				Warn("MINECRAFT LOADING DEEP subassets failed | asset='" + text + "' | error=" + Unwrap(ex));
			}
		}
		try
		{
			Object[] array2 = bundle.LoadAllAssets<Object>();
			int num3 = ((array2 != null) ? array2.Length : 0);
			int num4 = 0;
			int num5 = 0;
			int num6 = 0;
			if (array2 != null)
			{
				Object[] array3 = array2;
				foreach (Object val4 in array3)
				{
					if (!(val4 == (Object)null))
					{
						Sprite val5 = (Sprite)(object)((val4 is Sprite) ? val4 : null);
						Texture2D val6 = (Texture2D)(object)((val4 is Texture2D) ? val4 : null);
						if ((Object)(object)val5 != (Object)null)
						{
							num4++;
						}
						if ((Object)(object)val6 != (Object)null)
						{
							num5++;
						}
						string text3 = val4.name