Decompiled source of MoreEnemies v1.2.3

IncreasedEnemyCount.dll

Decompiled 4 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Increased Enemy Count")]
[assembly: AssemblyDescription("Outward mod: multiplies the number of enemies in every encounter.")]
[assembly: AssemblyFileVersion("1.2.3.0")]
[assembly: AssemblyVersion("1.2.3.0")]
internal sealed class ConfigurationManagerAttributes
{
	public bool? ShowRangeAsPercent;

	public Action<ConfigEntryBase> CustomDrawer;

	public bool? Browsable;

	public string Category;

	public object DefaultValue;

	public bool? HideDefaultButton;

	public bool? HideSettingName;

	public string Description;

	public string DispName;

	public int? Order;

	public bool? ReadOnly;

	public bool? IsAdvanced;

	public Func<object, string> ObjToStr;

	public Func<string, object> StrToObj;
}
namespace IncreasedEnemyCount;

internal static class AudioVoiceLimit
{
	private const int MAX_REAL = 255;

	private const int MAX_VIRTUAL = 4095;

	public static void Apply(int wantedRealVoices)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0190: 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_01a1: 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)
		try
		{
			AudioConfiguration configuration = AudioSettings.GetConfiguration();
			Plugin.Log.LogInfo((object)$"Game audio: {configuration.numRealVoices} audible sounds at once, {configuration.numVirtualVoices} tracked.");
			int num = Mathf.Clamp(wantedRealVoices, 1, 255);
			int num2 = Mathf.Clamp(Math.Max(configuration.numVirtualVoices, num * 8), num, 4095);
			if (num <= configuration.numRealVoices && num2 <= configuration.numVirtualVoices)
			{
				Plugin.Log.LogInfo((object)"Audio limits already at or above the requested values - leaving them alone.");
				return;
			}
			num = Math.Max(num, configuration.numRealVoices);
			configuration.numRealVoices = num;
			configuration.numVirtualVoices = num2;
			List<KeyValuePair<AudioSource, float>> list = new List<KeyValuePair<AudioSource, float>>();
			AudioSource[] array = Object.FindObjectsOfType<AudioSource>();
			foreach (AudioSource val in array)
			{
				if (Object.op_Implicit((Object)(object)val) && val.isPlaying)
				{
					list.Add(new KeyValuePair<AudioSource, float>(val, val.time));
				}
			}
			if (!AudioSettings.Reset(configuration))
			{
				Plugin.Log.LogWarning((object)"Unity refused the new audio limits; keeping the game's defaults.");
				return;
			}
			foreach (KeyValuePair<AudioSource, float> item in list)
			{
				try
				{
					if (Object.op_Implicit((Object)(object)item.Key))
					{
						item.Key.Play();
						if (Object.op_Implicit((Object)(object)item.Key.clip) && item.Value < item.Key.clip.length)
						{
							item.Key.time = item.Value;
						}
					}
				}
				catch
				{
				}
			}
			AudioConfiguration configuration2 = AudioSettings.GetConfiguration();
			Plugin.Log.LogMessage((object)$"Raised audio limits: {configuration2.numRealVoices} audible sounds at once, {configuration2.numVirtualVoices} tracked.");
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Could not change audio limits: " + ex.Message));
		}
	}
}
internal enum EnemyCategory
{
	Standard,
	Unique,
	Boss
}
internal static class EnemyCategories
{
	private static readonly string[] s_bosses = new string[30]
	{
		"Ash Giant Highmonk", "Brand Squire", "Breath of Darkness", "Calixa", "Concealed Knight: ???", "Crimson Avatar", "Djinn", "Elite Alpha Tuanosaur", "Elite Ash Giant", "Elite Beast Golem",
		"Elite Boozu", "Elite Burning Man", "Elite Crescent Shark", "Elite Crimson Avatar", "Elite Gargoyle Alchemist", "Elite Gargoyle Mage", "Elite Gargoyle Warrior", "Elite Krypteia Warrior", "Elite Krypteia Witch", "Elite Mantis Shrimp",
		"Elite Obsidian Elemental", "Elite Sublime Shell", "Elite Torcrab", "Forge Master", "Grandmother", "Immaculate Dreamer", "Immaculate's Bird", "Light Mender", "Plague Doctor", "Troglodyte Queen"
	};

	private static readonly string[] s_uniques = new string[40]
	{
		"Accursed Wendigo", "Altered Gargoyle", "Ancestral General", "Ancestral Soldier", "Bloody Alexis", "Calygrey Hero", "Chromatic Arcane Elemental", "Cracked Gargoyle", "Elemental Parasite", "Executioner Bug",
		"Ghost of Vanasse", "Giant Horror", "Glacial Tuanosaur", "Golden Matriarch", "Grandmother Medyse", "Greater Grotesque", "Guardian of the Compass", "Kazite Admiral", "Lightning Dancer", "Liquid-Cooled Golem",
		"Luke the Pearlescent", "Mad Captain's Bones", "Matriarch Myrmitaur", "Quartz Elemental", "Razorhorn Stekosaur", "Royal Manticore", "Rusted Enforcer", "Sandrose Horror", "She Who Speaks", "That Annoying Troglodyte",
		"The Crusher", "The First Cannibal", "The Last Acolyte", "Thunderbolt Golem", "Titanic Guardian Mk-7", "Troglodyte Archmage", "Tyrant of the Hive", "Vile Illuminator", "Virulent Hiveman", "Volcanic Gastrocin"
	};

	private static readonly Dictionary<string, EnemyCategory> s_byName = Build();

	private static Dictionary<string, EnemyCategory> Build()
	{
		Dictionary<string, EnemyCategory> dictionary = new Dictionary<string, EnemyCategory>(StringComparer.Ordinal);
		string[] array = s_uniques;
		foreach (string s in array)
		{
			dictionary[Normalize(s)] = EnemyCategory.Unique;
		}
		array = s_bosses;
		foreach (string s2 in array)
		{
			dictionary[Normalize(s2)] = EnemyCategory.Boss;
		}
		return dictionary;
	}

	public static EnemyCategory Lookup(string name)
	{
		if (string.IsNullOrEmpty(name))
		{
			return EnemyCategory.Standard;
		}
		if (!s_byName.TryGetValue(Normalize(name), out var value))
		{
			return EnemyCategory.Standard;
		}
		return value;
	}

	private static string Normalize(string s)
	{
		return s.Trim().Replace('’', '\'').Replace('‘', '\'')
			.ToLowerInvariant();
	}
}
public class EnemyDoubler : MonoBehaviour
{
	private class CloneRecord
	{
		public string SceneName;

		public int CandidateIndex;

		public Character Original;

		public Character Clone;

		public string OriginalUID;

		public string CloneUID;

		public Vector3 Offset;

		public float Spacing;

		public int CopyIndex;

		public int TotalCopies;

		public bool OriginalWasActive;

		public bool HiddenWhilePeaceful;
	}

	private class SceneState
	{
		public string SceneName;

		public int CandidateCount;

		public Dictionary<int, int> CopyCounts = new Dictionary<int, int>();

		public bool Reconciled;
	}

	private sealed class ViewIDAllocator
	{
		private readonly int m_lastOwner;

		private int m_owner;

		private int m_slot;

		public ViewIDAllocator(int firstOwner)
		{
			m_owner = firstOwner;
			m_lastOwner = firstOwner + 1000 - 1;
			m_slot = 0;
		}

		public int[] Take(int count)
		{
			int[] array = new int[count];
			for (int i = 0; i < count; i++)
			{
				m_slot++;
				if (m_slot >= PhotonNetwork.MAX_VIEW_IDS)
				{
					m_owner++;
					m_slot = 1;
				}
				if (m_owner > m_lastOwner)
				{
					return null;
				}
				array[i] = m_owner * PhotonNetwork.MAX_VIEW_IDS + m_slot;
			}
			return array;
		}
	}

	internal static EnemyDoubler Instance;

	private const string CLONE_SUFFIX = "_x";

	private const int COPY_OWNER_BASE = 2000000;

	private const int OWNERS_PER_AREA = 1000;

	private const int AREA_BLOCKS = 100;

	private readonly List<CloneRecord> m_records = new List<CloneRecord>();

	private readonly Dictionary<string, CloneRecord> m_recordsByCloneUID = new Dictionary<string, CloneRecord>();

	private SceneState m_currentScene;

	private float m_nextMirrorCheck;

	private float m_nextNetTick;

	private static readonly FieldInfo f_networkingPeer = AccessTools.Field(typeof(PhotonNetwork), "networkingPeer");

	private static readonly FieldInfo f_photonViewList = ((f_networkingPeer == null) ? null : AccessTools.Field(f_networkingPeer.FieldType, "photonViewList"));

	private static readonly FieldInfo f_aiStates = AccessTools.Field(typeof(CharacterAI), "m_aiStates");

	private static readonly FieldInfo f_aiStatesRoot = AccessTools.Field(typeof(CharacterAI), "m_aiStatesRoot");

	private static readonly FieldInfo f_nameLocKey = AccessTools.Field(typeof(Character), "m_nameLocKey");

	private static readonly FieldInfo f_name = AccessTools.Field(typeof(Character), "m_name");

	private void Awake()
	{
		Instance = this;
	}

	internal void OnSceneLoaded(Scene scene, LoadSceneMode mode)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			ProcessScene(scene);
		}
		catch (Exception arg)
		{
			Plugin.Log.LogError((object)$"Error while multiplying enemies in '{((Scene)(ref scene)).name}': {arg}");
		}
	}

	internal void OnSceneUnloaded(Scene scene)
	{
		PurgeDeadRecords();
	}

	private void ProcessScene(Scene scene)
	{
		//IL_0188: 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_01bd: Expected O, but got Unknown
		//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_026f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0309: Unknown result type (might be due to invalid IL or missing references)
		//IL_0345: Unknown result type (might be due to invalid IL or missing references)
		//IL_034a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0389: Unknown result type (might be due to invalid IL or missing references)
		GameObject[] rootGameObjects = ((Scene)(ref scene)).GetRootGameObjects();
		EnemyInspector.ClearTracking();
		List<Character> list = new List<Character>();
		GameObject[] array = rootGameObjects;
		for (int i = 0; i < array.Length; i++)
		{
			Character[] componentsInChildren = array[i].GetComponentsInChildren<Character>(true);
			foreach (Character val in componentsInChildren)
			{
				if (IsCandidate(val))
				{
					list.Add(val);
				}
			}
		}
		if (list.Count == 0)
		{
			return;
		}
		string source;
		EnemySettings enemySettings = NetSync.ResolveSettingsForScene(((Scene)(ref scene)).name, out source);
		if (enemySettings == null)
		{
			Plugin.Log.LogMessage((object)("[" + ((Scene)(ref scene)).name + "] The host doesn't have Increased Enemy Count - leaving enemies alone."));
			m_currentScene = null;
			return;
		}
		if (enemySettings.AnyCopies && NetSync.IsOnline && NetSync.IsHost && Plugin.RequireModOnAllPlayers.Value)
		{
			List<string> list2 = NetSync.PlayersWithoutMod();
			if (list2.Count > 0)
			{
				NetSync.Notify(string.Join(", ", list2.ToArray()) + " " + ((list2.Count == 1) ? "doesn't" : "don't") + " have Increased Enemy Count, so no extra enemies were added in this area.");
				enemySettings = enemySettings.WithEnabled(enabled: false);
			}
		}
		SceneState sceneState = (m_currentScene = new SceneState
		{
			SceneName = ((Scene)(ref scene)).name,
			CandidateCount = list.Count
		});
		if (!enemySettings.AnyCopies)
		{
			NetSync.PublishArea(((Scene)(ref scene)).name, enemySettings, list.Count, sceneState.CopyCounts);
			return;
		}
		if (NetSync.IsOnline)
		{
			Plugin.Log.LogInfo((object)$"[{((Scene)(ref scene)).name}] Using settings from {source}: {enemySettings}.");
		}
		ViewIDAllocator viewIDAllocator = new ViewIDAllocator(FirstCopyOwnerFor(scene));
		Dictionary<int, PhotonView> livePhotonViews = GetLivePhotonViews();
		int num = 0;
		int num2 = 0;
		int num3 = 0;
		Dictionary<EnemyCategory, int> perCategory = new Dictionary<EnemyCategory, int>();
		GameObject val2 = new GameObject("IncreasedEnemyCount_Holder");
		val2.SetActive(false);
		SceneManager.MoveGameObjectToScene(val2, scene);
		try
		{
			for (int k = 0; k < list.Count; k++)
			{
				Character val3 = list[k];
				int num4 = CountViews(val3);
				int[] array2 = viewIDAllocator.Take(num4 * enemySettings.MaxCopies);
				string text = SafeName(val3);
				float num5 = SafeBaseMaxHealth(val3);
				string how;
				EnemyCategory enemyCategory = Classify(val3, enemySettings, out how);
				int num6 = ((!(how == "excluded")) ? enemySettings.CopiesFor(enemyCategory) : 0);
				if (Plugin.LogEachEnemy.Value)
				{
					Plugin.Log.LogInfo((object)($"[{((Scene)(ref scene)).name}] {text} | HP {num5:0} | faction {val3.Faction} | " + string.Format("{0}{1} | {2} cop{3}", enemyCategory, (how != null) ? (" (" + how + ")") : "", num6, (num6 == 1) ? "y" : "ies") + (((Component)val3).gameObject.activeInHierarchy ? "" : " | currently inactive")));
				}
				bool flag = EnemyInspector.Wants(val3, text);
				if (flag)
				{
					EnemyInspector.Report(scene, val3, text);
				}
				if (how == "excluded")
				{
					num2++;
				}
				else
				{
					if (num6 <= 0)
					{
						continue;
					}
					if (array2 == null)
					{
						num3++;
						continue;
					}
					UID uID = val3.UID;
					if (string.IsNullOrEmpty(((UID)(ref uID)).Value) && NetSync.IsOnline)
					{
						continue;
					}
					int num7 = 0;
					for (int l = 1; l <= num6; l++)
					{
						int[] array3 = new int[num4];
						Array.Copy(array2, (l - 1) * num4, array3, 0, num4);
						if (CreateClone(scene, val2.transform, val3, k, l, num6, array3, enemySettings.Spacing, livePhotonViews))
						{
							num7++;
						}
					}
					if (flag)
					{
						List<Character> list3 = new List<Character>();
						foreach (CloneRecord record in m_records)
						{
							if ((Object)(object)record.Original == (Object)(object)val3 && Object.op_Implicit((Object)(object)record.Clone))
							{
								list3.Add(record.Clone);
							}
						}
						EnemyInspector.Track(val3, text, list3);
					}
					if (num7 > 0)
					{
						sceneState.CopyCounts[k] = num7;
						num += num7;
						perCategory[enemyCategory] = (perCategory.TryGetValue(enemyCategory, out var value) ? value : 0) + num7;
					}
				}
			}
		}
		finally
		{
			Object.Destroy((Object)(object)val2);
		}
		NetSync.PublishArea(((Scene)(ref scene)).name, enemySettings, list.Count, sceneState.CopyCounts);
		Plugin.Log.LogMessage((object)($"[{((Scene)(ref scene)).name}] Enemies found: {list.Count}, copies created: {num} " + $"(standard {Cat(EnemyCategory.Standard)}, unique {Cat(EnemyCategory.Unique)}, bosses {Cat(EnemyCategory.Boss)})" + ((num2 > 0) ? $", excluded by name: {num2}" : "") + "."));
		if (num3 > 0)
		{
			Plugin.Log.LogWarning((object)$"[{((Scene)(ref scene)).name}] Ran out of network IDs for this area: {num3} enemies weren't copied.");
		}
		int Cat(EnemyCategory c)
		{
			if (!perCategory.TryGetValue(c, out var value2))
			{
				return 0;
			}
			return value2;
		}
	}

	private bool IsCandidate(Character c)
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)c))
		{
			return false;
		}
		Dictionary<string, CloneRecord> recordsByCloneUID = m_recordsByCloneUID;
		UID uID = c.UID;
		if (recordsByCloneUID.ContainsKey(((UID)(ref uID)).Value ?? ""))
		{
			return false;
		}
		if (!Object.op_Implicit((Object)(object)((Component)c).GetComponent<EditorCharacterAILoadAI>()))
		{
			return false;
		}
		if (IsPeacefulFaction(c.Faction))
		{
			return false;
		}
		if (Object.op_Implicit((Object)(object)((Component)c).GetComponentInChildren<Merchant>(true)) || Object.op_Implicit((Object)(object)((Component)c).GetComponentInChildren<Trainer>(true)))
		{
			return false;
		}
		MonoBehaviour[] componentsInChildren = ((Component)c).GetComponentsInChildren<MonoBehaviour>(true);
		foreach (MonoBehaviour val in componentsInChildren)
		{
			if (Object.op_Implicit((Object)(object)val) && ((object)val).GetType().Name.IndexOf("Dialogue", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return false;
			}
		}
		return true;
	}

	private static EnemyCategory Classify(Character c, EnemySettings s, out string how)
	{
		if (MatchesAnyName(c, s.ExcludedNames))
		{
			how = "excluded";
			return EnemyCategory.Standard;
		}
		EnemyCategory enemyCategory = EnemyCategories.Lookup(SafeName(c));
		if (enemyCategory == EnemyCategory.Standard)
		{
			enemyCategory = EnemyCategories.Lookup(f_name?.GetValue(c) as string);
		}
		if (MatchesAnyName(c, s.IncludedNames))
		{
			how = ((enemyCategory != EnemyCategory.Standard) ? $"always standard, wiki says {enemyCategory}" : "always standard");
			return EnemyCategory.Standard;
		}
		how = ((enemyCategory != EnemyCategory.Standard) ? "wiki list" : null);
		return enemyCategory;
	}

	private static bool MatchesAnyName(Character c, string commaList)
	{
		if (string.IsNullOrEmpty(commaList))
		{
			return false;
		}
		if (!NameMatchesList(SafeName(c), commaList) && !NameMatchesList(((Object)((Component)c).gameObject).name, commaList) && !NameMatchesList(f_name?.GetValue(c) as string, commaList))
		{
			return NameMatchesList(f_nameLocKey?.GetValue(c) as string, commaList);
		}
		return true;
	}

	private static bool NameMatchesList(string name, string commaList)
	{
		if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(commaList))
		{
			return false;
		}
		string[] array = commaList.Split(new char[1] { ',' });
		for (int i = 0; i < array.Length; i++)
		{
			string text = array[i].Trim();
			if (text.Length > 0 && name.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return true;
			}
		}
		return false;
	}

	private static string SafeName(Character c)
	{
		string text = null;
		try
		{
			text = c.Name;
		}
		catch
		{
		}
		if (string.IsNullOrEmpty(text))
		{
			text = ((Object)((Component)c).gameObject).name;
		}
		return text;
	}

	private static bool SafeAlive(Character c)
	{
		try
		{
			return c.Alive;
		}
		catch
		{
			return true;
		}
	}

	private static float SafeBaseMaxHealth(Character c)
	{
		try
		{
			CharacterStats component = ((Component)c).GetComponent<CharacterStats>();
			if (Object.op_Implicit((Object)(object)component))
			{
				return component.BaseMaxHealth;
			}
		}
		catch
		{
		}
		return 0f;
	}

	private bool CreateClone(Scene scene, Transform holder, Character original, int candidateIndex, int index, int totalCopies, int[] viewIDs, float spacing, Dictionary<int, PhotonView> liveViews)
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0107: Unknown result type (might be due to invalid IL or missing references)
		//IL_010c: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_026a: Unknown result type (might be due to invalid IL or missing references)
		//IL_026f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0270: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01df: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_029a: Unknown result type (might be due to invalid IL or missing references)
		//IL_032d: Unknown result type (might be due to invalid IL or missing references)
		//IL_032e: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = null;
		try
		{
			Transform transform = ((Component)original).transform;
			Vector3 val2 = ComputeOffset(transform, index, totalCopies, spacing);
			val = Object.Instantiate<GameObject>(((Component)original).gameObject, transform.position, transform.rotation, holder);
			((Object)val).name = ((Object)((Component)original).gameObject).name + "_x" + index;
			val.SetActive(((Component)original).gameObject.activeSelf);
			Character component = val.GetComponent<Character>();
			UID val3 = original.UID;
			string value = ((UID)(ref val3)).Value;
			string text = (string.IsNullOrEmpty(value) ? null : (value + "_x" + index));
			component.SetUID((UID)((text == null) ? UID.Empty : new UID(text)));
			int num = 0;
			PhotonView[] componentsInChildren = val.GetComponentsInChildren<PhotonView>(true);
			foreach (PhotonView val4 in componentsInChildren)
			{
				if (val4.viewID != 0)
				{
					int num2 = viewIDs[num++];
					if (liveViews != null && liveViews.TryGetValue(num2, out var value2) && Object.op_Implicit((Object)(object)value2) && ((Component)value2).gameObject.scene != scene)
					{
						Plugin.Log.LogWarning((object)($"Network ID {num2} is already used by '{((Object)value2).name}' on this machine; skipping a copy of {((Object)original).name}. " + "In co-op this copy will be missing for this player."));
						Object.DestroyImmediate((Object)(object)val);
						return false;
					}
					val4.viewID = num2;
					val4.ownerId = 0;
				}
			}
			MonoBehaviour[] componentsInChildren2 = val.GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val5 in componentsInChildren2)
			{
				if (val5 is Character || val5 is AISquad)
				{
					continue;
				}
				IUIDHolder val6 = (IUIDHolder)(object)((val5 is IUIDHolder) ? val5 : null);
				if (val6 != null)
				{
					val3 = val6.HolderUID;
					if (!((UID)(ref val3)).IsNull)
					{
						val3 = val6.HolderUID;
						val6.SetHolderUID(new UID(((UID)(ref val3)).Value + "_x" + index));
					}
				}
			}
			CharacterAI component2 = val.GetComponent<CharacterAI>();
			if (Object.op_Implicit((Object)(object)component2) && component2.InstantiateCharAIDisable)
			{
				CharAIDisable[] components = val.GetComponents<CharAIDisable>();
				for (int i = 0; i < components.Length; i++)
				{
					Object.DestroyImmediate((Object)(object)components[i]);
				}
			}
			RestoreAIStates(original, val);
			QuestEventOnDeath[] componentsInChildren3 = val.GetComponentsInChildren<QuestEventOnDeath>(true);
			for (int i = 0; i < componentsInChildren3.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren3[i]);
			}
			val.transform.position = transform.position + val2;
			val.transform.SetParent(transform.parent, true);
			if (!Object.op_Implicit((Object)(object)transform.parent))
			{
				SceneManager.MoveGameObjectToScene(val, scene);
			}
			val.transform.SetSiblingIndex(transform.GetSiblingIndex() + 1);
			val.transform.localScale = transform.localScale;
			RegisterWithSquad(val, original);
			if (text != null && NetSync.IsOnline && !PhotonNetwork.isMasterClient && Object.op_Implicit((Object)(object)CharacterManager.Instance))
			{
				CharacterManager.Instance.AddCharacter(component);
			}
			CloneRecord cloneRecord = new CloneRecord
			{
				SceneName = ((Scene)(ref scene)).name,
				CandidateIndex = candidateIndex,
				Original = original,
				Clone = component,
				OriginalUID = value,
				CloneUID = text,
				Offset = val2,
				Spacing = spacing,
				CopyIndex = index,
				TotalCopies = totalCopies,
				OriginalWasActive = ((Component)original).gameObject.activeSelf
			};
			m_records.Add(cloneRecord);
			if (text != null)
			{
				m_recordsByCloneUID[text] = cloneRecord;
			}
			return true;
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Could not copy " + ((original != null) ? ((Object)original).name : null) + ": " + ex.Message));
			if (Object.op_Implicit((Object)(object)val))
			{
				Object.DestroyImmediate((Object)(object)val);
			}
			return false;
		}
	}

	private static void RestoreAIStates(Character original, GameObject cloneGO)
	{
		CharacterAI component = ((Component)original).GetComponent<CharacterAI>();
		if (!Object.op_Implicit((Object)(object)component))
		{
			return;
		}
		object? obj = f_aiStatesRoot?.GetValue(component);
		AIRoot val = (AIRoot)((obj is AIRoot) ? obj : null);
		AIState[] array = f_aiStates?.GetValue(component) as AIState[];
		if (!Object.op_Implicit((Object)(object)val) && array == null)
		{
			return;
		}
		if (Object.op_Implicit((Object)(object)val))
		{
			Transform val2 = FindCounterpart(((Component)original).transform, ((Component)val).transform, cloneGO.transform);
			if (Object.op_Implicit((Object)(object)val2))
			{
				((Component)val2).gameObject.SetActive(true);
			}
		}
		if (array == null)
		{
			return;
		}
		int num = 0;
		AIState[] array2 = array;
		foreach (AIState val3 in array2)
		{
			if (Object.op_Implicit((Object)(object)val3))
			{
				Transform val4 = FindCounterpart(((Component)original).transform, ((Component)val3).transform, cloneGO.transform);
				if (Object.op_Implicit((Object)(object)val4))
				{
					((Component)val4).gameObject.SetActive(true);
					num++;
				}
			}
		}
		if (num != array.Length)
		{
			Plugin.Log.LogWarning((object)$"{((Object)original).name}: only matched {num}/{array.Length} AI states on its copy; it may behave oddly.");
		}
	}

	private static Transform FindCounterpart(Transform originalRoot, Transform target, Transform cloneRoot)
	{
		List<int> list = new List<int>();
		Transform val = target;
		while ((Object)(object)val != (Object)(object)originalRoot)
		{
			if (!Object.op_Implicit((Object)(object)val))
			{
				return null;
			}
			list.Add(val.GetSiblingIndex());
			val = val.parent;
		}
		Transform val2 = cloneRoot;
		for (int num = list.Count - 1; num >= 0; num--)
		{
			if (list[num] >= val2.childCount)
			{
				return null;
			}
			val2 = val2.GetChild(list[num]);
		}
		return val2;
	}

	private static Vector3 ComputeOffset(Transform origT, int index, int totalCopies, float spacing)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_005e: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: 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_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_009a: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		//IL_00df: Unknown result type (might be due to invalid IL or missing references)
		//IL_010e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0110: 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_00f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fd: 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_0108: Unknown result type (might be due to invalid IL or missing references)
		//IL_010d: Unknown result type (might be due to invalid IL or missing references)
		float num = Mathf.Abs(origT.position.x * 13.7f + origT.position.z * 7.3f) % 360f + (float)index * (360f / (float)(totalCopies + 1));
		Vector3 val = Quaternion.Euler(0f, num, 0f) * Vector3.forward;
		Vector3 val2 = origT.position + val * spacing;
		RaycastHit val3 = default(RaycastHit);
		if (Physics.Raycast(val2 + Vector3.up * 3f, Vector3.down, ref val3, 8f, Global.FullEnvironmentMask))
		{
			val2 = ((RaycastHit)(ref val3)).point + Vector3.up * 0.1f;
		}
		NavMeshHit val4 = default(NavMeshHit);
		NavMeshHit val5 = default(NavMeshHit);
		if (NavMesh.SamplePosition(val2, ref val4, 2.5f, -1))
		{
			val2 = ((NavMeshHit)(ref val4)).position + Vector3.up * 0.1f;
		}
		else if (NavMesh.SamplePosition(origT.position, ref val5, 2.5f, -1))
		{
			val2 = origT.position + val * 0.75f;
		}
		return val2 - origT.position;
	}

	private static void RegisterWithSquad(GameObject cloneGO, Character original)
	{
		AISquadMember component = cloneGO.GetComponent<AISquadMember>();
		if (!Object.op_Implicit((Object)(object)component))
		{
			return;
		}
		AISquad val = null;
		Transform parent = ((Component)original).transform.parent;
		while (Object.op_Implicit((Object)(object)parent) && !Object.op_Implicit((Object)(object)val))
		{
			val = ((Component)parent).GetComponent<AISquad>();
			parent = parent.parent;
		}
		if (Object.op_Implicit((Object)(object)val) && val.MemberCount != 0)
		{
			component.AISquad = val;
			CharacterAI component2 = cloneGO.GetComponent<CharacterAI>();
			if (Object.op_Implicit((Object)(object)component2))
			{
				component2.AISquad = val;
			}
			val.AddCharAI(component);
		}
	}

	private static int CountViews(Character c)
	{
		int num = 0;
		PhotonView[] componentsInChildren = ((Component)c).GetComponentsInChildren<PhotonView>(true);
		for (int i = 0; i < componentsInChildren.Length; i++)
		{
			if (componentsInChildren[i].viewID != 0)
			{
				num++;
			}
		}
		return num;
	}

	private static int FirstCopyOwnerFor(Scene scene)
	{
		int num = ((((Scene)(ref scene)).buildIndex >= 0) ? ((Scene)(ref scene)).buildIndex : StableHash(((Scene)(ref scene)).name));
		return (int)(2000000 + (uint)num % 100u * 1000);
	}

	private static int StableHash(string s)
	{
		uint num = 2166136261u;
		string text = s ?? "";
		foreach (char c in text)
		{
			num = (num ^ c) * 16777619;
		}
		return (int)(num & 0x7FFFFFFF);
	}

	private static Dictionary<int, PhotonView> GetLivePhotonViews()
	{
		try
		{
			object obj = f_networkingPeer?.GetValue(null);
			if (obj != null)
			{
				return f_photonViewList?.GetValue(obj) as Dictionary<int, PhotonView>;
			}
		}
		catch
		{
		}
		return null;
	}

	internal static bool IsPeacefulFaction(Factions f)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0003: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: Invalid comparison between Unknown and I4
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0009: Invalid comparison between Unknown and I4
		if ((int)f != 0 && (int)f != 1)
		{
			return (int)f == 7;
		}
		return true;
	}

	internal void OnFactionChanged(Character original)
	{
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f1: 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_00ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_023d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0155: Unknown result type (might be due to invalid IL or missing references)
		//IL_0175: 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_0196: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (!Object.op_Implicit((Object)(object)original) || m_records.Count == 0 || Object.op_Implicit((Object)(object)((Component)original).GetComponentInChildren<AIFamiliarRoot>(true)))
			{
				return;
			}
			Factions faction = original.Faction;
			bool flag = IsPeacefulFaction(faction);
			int num = 0;
			int num2 = 0;
			foreach (CloneRecord record in m_records)
			{
				if ((Object)(object)record.Original != (Object)(object)original || !Object.op_Implicit((Object)(object)record.Clone))
				{
					continue;
				}
				if (flag)
				{
					if (!record.HiddenWhilePeaceful)
					{
						record.HiddenWhilePeaceful = true;
						if (((Component)record.Clone).gameObject.activeSelf)
						{
							((Component)record.Clone).gameObject.SetActive(false);
						}
						num++;
					}
					continue;
				}
				if (record.Clone.Faction != faction)
				{
					if (Object.op_Implicit((Object)(object)record.Clone.DetectabilityEmitter))
					{
						record.Clone.ChangeFaction(faction, false);
					}
					else
					{
						record.Clone.Faction = faction;
					}
				}
				if (record.HiddenWhilePeaceful)
				{
					record.HiddenWhilePeaceful = false;
					if (((Component)original).gameObject.activeSelf && original.Alive && !((Component)record.Clone).gameObject.activeSelf)
					{
						((Component)record.Clone).transform.position = ((Component)original).transform.position + ComputeOffset(((Component)original).transform, record.CopyIndex, record.TotalCopies, record.Spacing);
						((Component)record.Clone).transform.rotation = ((Component)original).transform.rotation;
						((Component)record.Clone).gameObject.SetActive(true);
					}
					num2++;
				}
			}
			if (num > 0)
			{
				Plugin.Log.LogInfo((object)string.Format("{0} became peaceful ({1}); hid {2} cop{3}.", SafeName(original), faction, num, (num == 1) ? "y" : "ies"));
			}
			if (num2 > 0)
			{
				Plugin.Log.LogInfo((object)string.Format("{0} became hostile ({1}); brought back {2} cop{3}.", SafeName(original), faction, num2, (num2 == 1) ? "y" : "ies"));
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Could not update copies after a faction change: " + ex.Message));
		}
	}

	internal void ApplySavesToClones(CharacterSaveData[] saves)
	{
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0105: Unknown result type (might be due to invalid IL or missing references)
		//IL_0112: Unknown result type (might be due to invalid IL or missing references)
		//IL_011f: Unknown result type (might be due to invalid IL or missing references)
		//IL_012c: Unknown result type (might be due to invalid IL or missing references)
		//IL_012f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0136: Unknown result type (might be due to invalid IL or missing references)
		//IL_013b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0140: Unknown result type (might be due to invalid IL or missing references)
		//IL_0145: Unknown result type (might be due to invalid IL or missing references)
		//IL_0148: Unknown result type (might be due to invalid IL or missing references)
		//IL_014d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0152: Unknown result type (might be due to invalid IL or missing references)
		//IL_0159: Unknown result type (might be due to invalid IL or missing references)
		//IL_0166: Unknown result type (might be due to invalid IL or missing references)
		//IL_0175: Expected O, but got Unknown
		if (saves == null || m_records.Count == 0)
		{
			return;
		}
		try
		{
			Dictionary<string, CharacterSaveData> dictionary = new Dictionary<string, CharacterSaveData>();
			foreach (CharacterSaveData val in saves)
			{
				if (val != null && !string.IsNullOrEmpty(val.UID))
				{
					dictionary[val.UID] = val;
				}
			}
			foreach (CloneRecord record in m_records)
			{
				if (Object.op_Implicit((Object)(object)record.Clone) && record.CloneUID != null && record.OriginalUID != null && !dictionary.ContainsKey(record.CloneUID) && dictionary.TryGetValue(record.OriginalUID, out var value) && !(value.Health > 0f) && !((Object)(object)CharacterManager.Instance.GetCharacter(record.CloneUID) != (Object)(object)record.Clone))
				{
					CharacterSaveData val2 = new CharacterSaveData
					{
						NewSave = value.NewSave,
						VisualData = value.VisualData,
						UID = record.CloneUID,
						Health = value.Health,
						ManaPoint = value.ManaPoint,
						Position = value.Position + record.Offset,
						Forward = value.Forward,
						Money = 0,
						EncounterTime = value.EncounterTime,
						IsAiActive = value.IsAiActive
					};
					record.Clone.LoadCharSave(val2);
				}
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Could not sync saved state to copies: " + ex.Message));
		}
	}

	private void Update()
	{
		if (Time.unscaledTime >= m_nextNetTick)
		{
			m_nextNetTick = Time.unscaledTime + 1f;
			NetSync.Tick();
			ReconcileWithHost();
		}
		MirrorActivation();
		EnemyInspector.Tick();
	}

	private void ReconcileWithHost()
	{
		SceneState currentScene = m_currentScene;
		if (currentScene == null || currentScene.Reconciled || !NetSync.IsOnline || NetSync.IsHost || !NetSync.TryGetHostManifest(currentScene.SceneName, out var candidateCount, out var copyCounts))
		{
			return;
		}
		currentScene.Reconciled = true;
		if (candidateCount != currentScene.CandidateCount)
		{
			NetSync.Notify($"Your copy of this area doesn't match the host's ({currentScene.CandidateCount} enemies here, " + $"{candidateCount} on the host). Leave and re-enter the area. " + "If it keeps happening, check that everyone has the same mods.");
			return;
		}
		int num = 0;
		for (int num2 = m_records.Count - 1; num2 >= 0; num2--)
		{
			CloneRecord cloneRecord = m_records[num2];
			if (!(cloneRecord.SceneName != currentScene.SceneName))
			{
				int value;
				int num3 = (copyCounts.TryGetValue(cloneRecord.CandidateIndex, out value) ? value : 0);
				if (cloneRecord.CopyIndex > num3)
				{
					if (Object.op_Implicit((Object)(object)cloneRecord.Clone))
					{
						Object.Destroy((Object)(object)((Component)cloneRecord.Clone).gameObject);
					}
					if (cloneRecord.CloneUID != null)
					{
						m_recordsByCloneUID.Remove(cloneRecord.CloneUID);
					}
					m_records.RemoveAt(num2);
					num++;
				}
			}
		}
		int num4 = 0;
		foreach (KeyValuePair<int, int> item in copyCounts)
		{
			int value2;
			int num5 = (currentScene.CopyCounts.TryGetValue(item.Key, out value2) ? value2 : 0);
			if (item.Value > num5)
			{
				num4 += item.Value - num5;
			}
		}
		if (num > 0 || num4 > 0)
		{
			Plugin.Log.LogWarning((object)$"[{currentScene.SceneName}] Matched the host's enemy copies: removed {num} extra, {num4} copies are missing here.");
			if (num4 > 0)
			{
				NetSync.Notify($"{num4} of the host's extra enemies couldn't be created on your side, so they may be invisible to you. " + "Make sure everyone uses the same game language and mods.");
			}
		}
	}

	private void MirrorActivation()
	{
		//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fe: 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_0108: 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_0132: Unknown result type (might be due to invalid IL or missing references)
		if (m_records.Count == 0 || Time.unscaledTime < m_nextMirrorCheck)
		{
			return;
		}
		m_nextMirrorCheck = Time.unscaledTime + 0.25f;
		bool flag = false;
		for (int i = 0; i < m_records.Count; i++)
		{
			CloneRecord cloneRecord = m_records[i];
			if (!Object.op_Implicit((Object)(object)cloneRecord.Original) || !Object.op_Implicit((Object)(object)cloneRecord.Clone))
			{
				flag = true;
				continue;
			}
			bool activeSelf = ((Component)cloneRecord.Original).gameObject.activeSelf;
			if (cloneRecord.HiddenWhilePeaceful)
			{
				if (((Component)cloneRecord.Clone).gameObject.activeSelf)
				{
					((Component)cloneRecord.Clone).gameObject.SetActive(false);
				}
				cloneRecord.OriginalWasActive = activeSelf;
				continue;
			}
			if (activeSelf && !cloneRecord.OriginalWasActive && !((Component)cloneRecord.Clone).gameObject.activeSelf)
			{
				try
				{
					Vector3 position = ((Component)cloneRecord.Original).transform.position + ComputeOffset(((Component)cloneRecord.Original).transform, cloneRecord.CopyIndex, cloneRecord.TotalCopies, cloneRecord.Spacing);
					((Component)cloneRecord.Clone).transform.position = position;
					((Component)cloneRecord.Clone).transform.rotation = ((Component)cloneRecord.Original).transform.rotation;
					((Component)cloneRecord.Clone).gameObject.SetActive(true);
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Could not activate copy of " + ((Object)cloneRecord.Original).name + ": " + ex.Message));
				}
			}
			else if (!activeSelf && cloneRecord.OriginalWasActive && ((Component)cloneRecord.Clone).gameObject.activeSelf && SafeAlive(cloneRecord.Original))
			{
				((Component)cloneRecord.Clone).gameObject.SetActive(false);
				Plugin.Log.LogInfo((object)$"{SafeName(cloneRecord.Original)} was switched off by the game; hid its copy {cloneRecord.CopyIndex}.");
			}
			cloneRecord.OriginalWasActive = activeSelf;
		}
		if (flag)
		{
			PurgeDeadRecords();
		}
	}

	private void PurgeDeadRecords()
	{
		for (int num = m_records.Count - 1; num >= 0; num--)
		{
			CloneRecord cloneRecord = m_records[num];
			if (!Object.op_Implicit((Object)(object)cloneRecord.Clone) || !Object.op_Implicit((Object)(object)cloneRecord.Original))
			{
				if (cloneRecord.CloneUID != null)
				{
					m_recordsByCloneUID.Remove(cloneRecord.CloneUID);
				}
				m_records.RemoveAt(num);
			}
		}
	}
}
internal static class EnemyInspector
{
	private class Tracked
	{
		public string Label;

		public Character Character;

		public string Last;
	}

	private static readonly List<Tracked> s_tracked = new List<Tracked>();

	private static float s_nextTick;

	private const BindingFlags FIELDS = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

	public static bool Wants(Character c, string name)
	{
		string value = Plugin.InspectEnemies.Value;
		if (string.IsNullOrEmpty(value))
		{
			return false;
		}
		string[] array = value.Split(new char[1] { ',' });
		for (int i = 0; i < array.Length; i++)
		{
			string text = array[i].Trim();
			if (text.Length > 0 && ((name ?? "").IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0 || ((Object)((Component)c).gameObject).name.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0))
			{
				return true;
			}
		}
		return false;
	}

	public static void ClearTracking()
	{
		s_tracked.Clear();
	}

	public static void Report(Scene scene, Character c, string name)
	{
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0091: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("===== INSPECT [" + ((Scene)(ref scene)).name + "] " + name + " =====");
			stringBuilder.AppendLine($"  path: {PathOf(((Component)c).transform)} | activeSelf {((Component)c).gameObject.activeSelf} | activeInHierarchy {((Component)c).gameObject.activeInHierarchy}");
			object arg = c.Faction;
			UID uID = c.UID;
			stringBuilder.AppendLine($"  faction {arg} | UID {((UID)(ref uID)).Value}");
			TargetingSystem component = ((Component)c).GetComponent<TargetingSystem>();
			if (Object.op_Implicit((Object)(object)component))
			{
				stringBuilder.AppendLine($"  targeting: StartAlliedFactions [{Join(component.StartAlliedFactions)}] AlliedToSameFaction {component.AlliedToSameFaction}");
			}
			CharacterAI component2 = ((Component)c).GetComponent<CharacterAI>();
			if (Object.op_Implicit((Object)(object)component2))
			{
				object obj = AccessField(component2, "m_aiActiveOnQuestEvent");
				object arg2 = ((obj != null) ? (AccessField(obj, "m_eventUID") ?? obj.GetType().GetProperty("EventUID")?.GetValue(obj, null)) : null);
				stringBuilder.AppendLine(string.Format("  AI: enabled {0} | AIStatesPrefab {1} | active-on-quest-event '{2}'", ((Behaviour)component2).enabled, Object.op_Implicit((Object)(object)component2.AIStatesPrefab) ? ((Object)component2.AIStatesPrefab).name : "none", arg2));
				AIState[] componentsInChildren = ((Component)c).GetComponentsInChildren<AIState>(true);
				foreach (AIState val in componentsInChildren)
				{
					stringBuilder.AppendLine($"    state {((object)val).GetType().Name} '{((Object)val).name}' active {((Component)val).gameObject.activeSelf}");
				}
			}
			stringBuilder.AppendLine("  components (type x count):");
			foreach (IGrouping<string, Component> item in from x in ((Component)c).GetComponentsInChildren<Component>(true)
				where Object.op_Implicit((Object)(object)x)
				group x by ((object)x).GetType().Name into g
				orderby g.Key
				select g)
			{
				stringBuilder.AppendLine($"    {item.Key} x{item.Count()}");
			}
			stringBuilder.AppendLine("  referenced by (outside its own object):");
			int num = 0;
			foreach (string item2 in FindReferences(scene, ((Component)c).transform))
			{
				stringBuilder.AppendLine("    " + item2);
				if (++num >= 60)
				{
					stringBuilder.AppendLine("    ...");
					break;
				}
			}
			if (num == 0)
			{
				stringBuilder.AppendLine("    (nothing found)");
			}
			Plugin.Log.LogInfo((object)stringBuilder.ToString());
		}
		catch (Exception arg3)
		{
			Plugin.Log.LogWarning((object)$"Inspect failed for {name}: {arg3}");
		}
	}

	public static void Track(Character original, string name, IEnumerable<Character> clones)
	{
		s_tracked.Add(new Tracked
		{
			Label = name + " (original)",
			Character = original
		});
		int num = 1;
		foreach (Character clone in clones)
		{
			s_tracked.Add(new Tracked
			{
				Label = $"{name} (copy {num++})",
				Character = clone
			});
		}
	}

	public static void Tick()
	{
		if (s_tracked.Count == 0 || Time.unscaledTime < s_nextTick)
		{
			return;
		}
		s_nextTick = Time.unscaledTime + 1f;
		for (int num = s_tracked.Count - 1; num >= 0; num--)
		{
			Tracked tracked = s_tracked[num];
			if (!Object.op_Implicit((Object)(object)tracked.Character))
			{
				s_tracked.RemoveAt(num);
			}
			else
			{
				string text = Snapshot(tracked.Character);
				if (text != tracked.Last)
				{
					Plugin.Log.LogInfo((object)("[inspect] " + tracked.Label + ": " + text));
					tracked.Last = text;
				}
			}
		}
	}

	private static string Snapshot(Character c)
	{
		List<string> parts = new List<string>();
		Add("active", () => ((Component)c).gameObject.activeInHierarchy);
		Add("alive", () => c.Alive);
		Add("faction", () => c.Faction);
		Add("inCombat", () => c.InCombat);
		CharacterAI ai = ((Component)c).GetComponent<CharacterAI>();
		Add("ai", delegate
		{
			if (!Object.op_Implicit((Object)(object)ai))
			{
				return "none";
			}
			return (!((Behaviour)ai).enabled) ? "off" : "on";
		});
		Add("aiRoot", delegate
		{
			if (!Object.op_Implicit((Object)(object)ai) || !Object.op_Implicit((Object)(object)ai.AIStatesRoot))
			{
				return "-";
			}
			return (!((Component)ai.AIStatesRoot).gameObject.activeSelf) ? "off" : "on";
		});
		Add("state", () => (!Object.op_Implicit((Object)(object)ai)) ? "-" : ((Object)ai.CurrentAiState).name);
		Add("closeToPlayer", () => (!Object.op_Implicit((Object)(object)ai)) ? "-" : ((object)((CharacterControl)ai).CloseToPlayer));
		TargetingSystem ts = c.TargetingSystem;
		Add("targets", () => (!Object.op_Implicit((Object)(object)ts)) ? "-" : Join(ts.TargetableFactions));
		Add("locked", () => (!Object.op_Implicit((Object)(object)ts) || !Object.op_Implicit((Object)(object)ts.LockedCharacter)) ? "none" : ts.LockedCharacter.Name);
		return string.Join(" ", parts.ToArray());
		void Add(string k, Func<object> f)
		{
			try
			{
				parts.Add($"{k}={f()}");
			}
			catch
			{
				parts.Add(k + "=?");
			}
		}
	}

	private static IEnumerable<string> FindReferences(Scene scene, Transform target)
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		GameObject[] rootGameObjects = ((Scene)(ref scene)).GetRootGameObjects();
		foreach (GameObject val in rootGameObjects)
		{
			MonoBehaviour[] componentsInChildren = val.GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val2 in componentsInChildren)
			{
				if (!Object.op_Implicit((Object)(object)val2) || ((Component)val2).transform.IsChildOf(target))
				{
					continue;
				}
				List<string> list = new List<string>();
				if (((object)val2).GetType().Name.Contains("Blackboard"))
				{
					try
					{
						if (((object)val2).GetType().GetProperty("variables")?.GetValue(val2, null) is IDictionary dictionary)
						{
							foreach (DictionaryEntry item in dictionary)
							{
								if (Points(item.Value?.GetType().GetProperty("value")?.GetValue(item.Value, null), target))
								{
									list.Add($"blackboard var '{item.Key}'");
								}
							}
						}
					}
					catch
					{
					}
				}
				Type type = ((object)val2).GetType();
				while (type != null && type != typeof(MonoBehaviour))
				{
					FieldInfo[] fields = type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					foreach (FieldInfo fieldInfo in fields)
					{
						object value;
						try
						{
							value = fieldInfo.GetValue(val2);
						}
						catch
						{
							continue;
						}
						if (value == null)
						{
							continue;
						}
						if (Points(value, target))
						{
							list.Add(fieldInfo.Name);
						}
						else
						{
							if (!(value is IEnumerable enumerable) || value is string)
							{
								continue;
							}
							int num = 0;
							try
							{
								foreach (object item2 in enumerable)
								{
									if (++num <= 300)
									{
										if (Points(item2, target))
										{
											list.Add(fieldInfo.Name + "[]");
											break;
										}
										continue;
									}
									break;
								}
							}
							catch
							{
							}
						}
					}
					type = type.BaseType;
				}
				if (list.Count > 0)
				{
					yield return ((object)val2).GetType().Name + " on '" + PathOf(((Component)val2).transform) + "' -> " + string.Join(", ", list.ToArray());
				}
			}
		}
	}

	private static bool Points(object val, Transform target)
	{
		try
		{
			GameObject val2 = (GameObject)((val is GameObject) ? val : null);
			if (val2 != null)
			{
				return Object.op_Implicit((Object)(object)val2) && val2.transform.IsChildOf(target);
			}
			Component val3 = (Component)((val is Component) ? val : null);
			if (val3 != null)
			{
				return Object.op_Implicit((Object)(object)val3) && val3.transform.IsChildOf(target);
			}
		}
		catch
		{
		}
		return false;
	}

	private static object AccessField(object obj, string name)
	{
		Type type = obj.GetType();
		while (type != null)
		{
			FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				return field.GetValue(obj);
			}
			type = type.BaseType;
		}
		return null;
	}

	private unsafe static string Join(Factions[] fs)
	{
		if (fs != null)
		{
			return string.Join(",", fs.Select((Factions f) => ((object)(*(Factions*)(&f))/*cast due to .constrained prefix*/).ToString()).ToArray());
		}
		return "";
	}

	private static string PathOf(Transform t)
	{
		List<string> list = new List<string>();
		while (Object.op_Implicit((Object)(object)t))
		{
			list.Add(((Object)t).name);
			t = t.parent;
		}
		list.Reverse();
		return string.Join("/", list.ToArray());
	}
}
internal sealed class EnemySettings
{
	private const string FORMAT = "3";

	private const char SEP = '\u001f';

	public int MaxMultiplier;

	public bool Enabled;

	public int StandardMultiplier;

	public int UniqueMultiplier;

	public int BossMultiplier;

	public float Spacing;

	public string ExcludedNames;

	public string IncludedNames;

	public int MaxCopies => Math.Max(1, MaxMultiplier) - 1;

	public bool AnyCopies
	{
		get
		{
			if (Enabled)
			{
				if (StandardMultiplier <= 1 && UniqueMultiplier <= 1)
				{
					return BossMultiplier > 1;
				}
				return true;
			}
			return false;
		}
	}

	public int CopiesFor(EnemyCategory category)
	{
		if (!Enabled)
		{
			return 0;
		}
		return Math.Max(0, Math.Min(val2: category switch
		{
			EnemyCategory.Boss => BossMultiplier, 
			EnemyCategory.Unique => UniqueMultiplier, 
			_ => StandardMultiplier, 
		} - 1, val1: MaxCopies));
	}

	public static EnemySettings FromConfig()
	{
		return new EnemySettings
		{
			Enabled = Plugin.Enabled.Value,
			StandardMultiplier = Plugin.StandardMultiplier.Value,
			UniqueMultiplier = Plugin.UniqueMultiplier.Value,
			BossMultiplier = Plugin.BossMultiplier.Value,
			MaxMultiplier = Plugin.MaxMultiplier.Value,
			Spacing = Plugin.SpawnSpacing.Value,
			ExcludedNames = (Plugin.ExcludedNames.Value ?? ""),
			IncludedNames = (Plugin.IncludedNames.Value ?? "")
		};
	}

	public EnemySettings WithEnabled(bool enabled)
	{
		EnemySettings obj = (EnemySettings)MemberwiseClone();
		obj.Enabled = enabled;
		return obj;
	}

	public string Serialize()
	{
		CultureInfo invariantCulture = CultureInfo.InvariantCulture;
		return string.Join('\u001f'.ToString(), "3", Enabled ? "1" : "0", StandardMultiplier.ToString(invariantCulture), UniqueMultiplier.ToString(invariantCulture), BossMultiplier.ToString(invariantCulture), MaxMultiplier.ToString(invariantCulture), Spacing.ToString("R", invariantCulture), Clean(ExcludedNames), Clean(IncludedNames));
	}

	public static EnemySettings Deserialize(string data)
	{
		if (string.IsNullOrEmpty(data))
		{
			return null;
		}
		try
		{
			string[] array = data.Split(new char[1] { '\u001f' });
			if (array.Length < 9 || array[0] != "3")
			{
				return null;
			}
			CultureInfo invariantCulture = CultureInfo.InvariantCulture;
			return new EnemySettings
			{
				Enabled = (array[1] == "1"),
				StandardMultiplier = int.Parse(array[2], invariantCulture),
				UniqueMultiplier = int.Parse(array[3], invariantCulture),
				BossMultiplier = int.Parse(array[4], invariantCulture),
				MaxMultiplier = Math.Min(100, Math.Max(2, int.Parse(array[5], invariantCulture))),
				Spacing = float.Parse(array[6], invariantCulture),
				ExcludedNames = array[7],
				IncludedNames = array[8]
			};
		}
		catch
		{
			return null;
		}
	}

	public override string ToString()
	{
		if (!Enabled)
		{
			return "off";
		}
		return $"standard x{StandardMultiplier}, unique x{UniqueMultiplier}, bosses x{BossMultiplier}";
	}

	private static string Clean(string s)
	{
		return (s ?? "").Replace('\u001f', ',');
	}
}
internal static class NetSync
{
	public const string PROTOCOL = "5";

	private const string KEY_CFG = "iec.cfg";

	private const string KEY_AREA = "iec.area";

	private const string KEY_MANIFEST = "iec.man";

	private const string KEY_VERSION = "iec.ver";

	private const char SEP = '|';

	private static string s_lastRoomProcessed;

	private static string s_lastPublishedCfg;

	private static string s_trackedRoom;

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

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

	public static bool IsOnline
	{
		get
		{
			if (PhotonNetwork.inRoom)
			{
				return !PhotonNetwork.offlineMode;
			}
			return false;
		}
	}

	public static bool IsHost
	{
		get
		{
			if (IsOnline)
			{
				return PhotonNetwork.isMasterClient;
			}
			return true;
		}
	}

	public static EnemySettings ResolveSettingsForScene(string sceneName, out string source)
	{
		if (IsHost)
		{
			source = (IsOnline ? "this machine (host)" : "this machine");
			return EnemySettings.FromConfig();
		}
		Room room = PhotonNetwork.room;
		bool num = s_lastRoomProcessed != room.Name;
		s_lastRoomProcessed = room.Name;
		Hashtable customProperties = ((RoomInfo)room).CustomProperties;
		if (num && TrySplit(GetString(customProperties, "iec.area"), out var scene, out var rest) && scene == sceneName)
		{
			EnemySettings enemySettings = EnemySettings.Deserialize(rest);
			if (enemySettings != null)
			{
				source = "host (settings it used for this area)";
				return enemySettings;
			}
		}
		EnemySettings enemySettings2 = EnemySettings.Deserialize(GetString(customProperties, "iec.cfg"));
		if (enemySettings2 != null)
		{
			source = "host";
			return enemySettings2;
		}
		source = "host does not have the mod";
		return null;
	}

	public static void PublishArea(string sceneName, EnemySettings used, int candidateCount, IDictionary<int, int> copyCounts)
	{
		//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00df: Expected O, but got Unknown
		//IL_00df: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fb: Expected O, but got Unknown
		//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_010c: Expected O, but got Unknown
		//IL_0113: Expected O, but got Unknown
		if (!IsOnline || !PhotonNetwork.isMasterClient)
		{
			return;
		}
		try
		{
			CultureInfo invariantCulture = CultureInfo.InvariantCulture;
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(sceneName).Append('|').Append(candidateCount.ToString(invariantCulture))
				.Append('|');
			bool flag = true;
			foreach (KeyValuePair<int, int> copyCount in copyCounts)
			{
				if (copyCount.Value > 0)
				{
					if (!flag)
					{
						stringBuilder.Append(',');
					}
					stringBuilder.Append(copyCount.Key.ToString(invariantCulture)).Append(':').Append(copyCount.Value.ToString(invariantCulture));
					flag = false;
				}
			}
			string value = (s_lastPublishedCfg = EnemySettings.FromConfig().Serialize());
			Room room = PhotonNetwork.room;
			Hashtable val = new Hashtable();
			((Dictionary<object, object>)val).Add((object)"iec.cfg", (object)value);
			((Dictionary<object, object>)val).Add((object)"iec.area", (object)(sceneName + "|" + used.Serialize()));
			((Dictionary<object, object>)val).Add((object)"iec.man", (object)stringBuilder.ToString());
			room.SetCustomProperties(val, (Hashtable)null, false);
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Could not share enemy copy info with other players: " + ex.Message));
		}
	}

	public static bool TryGetHostManifest(string sceneName, out int candidateCount, out Dictionary<int, int> copyCounts)
	{
		candidateCount = 0;
		copyCounts = null;
		if (!IsOnline || PhotonNetwork.isMasterClient)
		{
			return false;
		}
		string text = GetString(((RoomInfo)PhotonNetwork.room).CustomProperties, "iec.man");
		if (string.IsNullOrEmpty(text))
		{
			return false;
		}
		string[] array = text.Split(new char[1] { '|' });
		if (array.Length < 3 || array[0] != sceneName)
		{
			return false;
		}
		try
		{
			CultureInfo invariantCulture = CultureInfo.InvariantCulture;
			candidateCount = int.Parse(array[1], invariantCulture);
			copyCounts = new Dictionary<int, int>();
			if (array[2].Length > 0)
			{
				string[] array2 = array[2].Split(new char[1] { ',' });
				for (int i = 0; i < array2.Length; i++)
				{
					string[] array3 = array2[i].Split(new char[1] { ':' });
					copyCounts[int.Parse(array3[0], invariantCulture)] = int.Parse(array3[1], invariantCulture);
				}
			}
			return true;
		}
		catch
		{
			return false;
		}
	}

	public static List<string> PlayersWithoutMod()
	{
		List<string> list = new List<string>();
		if (!IsOnline)
		{
			return list;
		}
		PhotonPlayer[] otherPlayers = PhotonNetwork.otherPlayers;
		foreach (PhotonPlayer val in otherPlayers)
		{
			if (val != null && GetString(val.CustomProperties, "iec.ver") != "5")
			{
				list.Add(DisplayName(val));
			}
		}
		return list;
	}

	public static void Tick()
	{
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0070: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: Expected O, but got Unknown
		//IL_0087: Expected O, but got Unknown
		//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c3: Expected O, but got Unknown
		//IL_00ca: Expected O, but got Unknown
		if (!IsOnline)
		{
			return;
		}
		try
		{
			Room room = PhotonNetwork.room;
			if (s_trackedRoom != room.Name)
			{
				s_trackedRoom = room.Name;
				s_firstSeen.Clear();
				s_warnedPlayers.Clear();
				s_lastPublishedCfg = null;
			}
			if (GetString(PhotonNetwork.player.CustomProperties, "iec.ver") != "5")
			{
				PhotonPlayer player = PhotonNetwork.player;
				Hashtable val = new Hashtable();
				((Dictionary<object, object>)val).Add((object)"iec.ver", (object)"5");
				player.SetCustomProperties(val, (Hashtable)null, false);
			}
			if (!PhotonNetwork.isMasterClient)
			{
				return;
			}
			string text = EnemySettings.FromConfig().Serialize();
			if (text != s_lastPublishedCfg)
			{
				s_lastPublishedCfg = text;
				Hashtable val2 = new Hashtable();
				((Dictionary<object, object>)val2).Add((object)"iec.cfg", (object)text);
				room.SetCustomProperties(val2, (Hashtable)null, false);
			}
			PhotonPlayer[] otherPlayers = PhotonNetwork.otherPlayers;
			foreach (PhotonPlayer val3 in otherPlayers)
			{
				if (val3 == null)
				{
					continue;
				}
				if (!s_firstSeen.ContainsKey(val3.ID))
				{
					s_firstSeen[val3.ID] = Time.unscaledTime;
				}
				if (!s_warnedPlayers.Contains(val3.ID) && !(Time.unscaledTime - s_firstSeen[val3.ID] < 8f))
				{
					string text2 = GetString(val3.CustomProperties, "iec.ver");
					if (!(text2 == "5"))
					{
						s_warnedPlayers.Add(val3.ID);
						string text3 = ((text2 == null) ? "doesn't have Increased Enemy Count" : "has an incompatible version of Increased Enemy Count");
						string text4 = (Plugin.RequireModOnAllPlayers.Value ? "Extra enemies will be skipped from the next area on." : "They won't see the extra enemies in this area or later ones.");
						Notify(DisplayName(val3) + " " + text3 + ". " + text4);
					}
				}
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogDebug((object)("NetSync tick failed: " + ex.Message));
		}
	}

	public static void Notify(string message)
	{
		Plugin.Log.LogWarning((object)message);
		try
		{
			Character val = (Object.op_Implicit((Object)(object)CharacterManager.Instance) ? CharacterManager.Instance.GetFirstLocalCharacter() : null);
			if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.CharacterUI))
			{
				val.CharacterUI.ShowInfoNotification("Increased Enemy Count: " + message);
			}
		}
		catch
		{
		}
	}

	private static string DisplayName(PhotonPlayer p)
	{
		try
		{
			if (!string.IsNullOrEmpty(p.NickName))
			{
				return p.NickName;
			}
		}
		catch
		{
		}
		return "Player " + p.ID;
	}

	private static string GetString(Hashtable props, string key)
	{
		if (props == null || !((Dictionary<object, object>)(object)props).ContainsKey((object)key))
		{
			return null;
		}
		return props[(object)key] as string;
	}

	private static bool TrySplit(string raw, out string scene, out string rest)
	{
		scene = (rest = null);
		if (string.IsNullOrEmpty(raw))
		{
			return false;
		}
		int num = raw.IndexOf('|');
		if (num <= 0)
		{
			return false;
		}
		scene = raw.Substring(0, num);
		rest = raw.Substring(num + 1);
		return true;
	}
}
internal static class Patches
{
	[HarmonyPatch(typeof(CharacterManager), "LoadAiCharactersFromSave")]
	[HarmonyPostfix]
	private static void LoadAiCharactersFromSave_Postfix(CharacterSaveData[] _saves)
	{
		EnemyDoubler.Instance?.ApplySavesToClones(_saves);
	}

	[HarmonyPatch(typeof(Character), "SendChangeFaction")]
	[HarmonyPostfix]
	private static void SendChangeFaction_Postfix(Character __instance)
	{
		EnemyDoubler.Instance?.OnFactionChanged(__instance);
	}

	[HarmonyPatch(typeof(GlobalAudioManager), "PlayMusic")]
	[HarmonyPostfix]
	private static void PlayMusic_Postfix(AudioSource __result)
	{
		if (Object.op_Implicit((Object)(object)__result) && Plugin.PrioritizeMusic.Value)
		{
			__result.priority = 0;
		}
	}
}
[BepInPlugin("catalysten.increasedenemycount", "Increased Enemy Count", "1.2.3")]
public class Plugin : BaseUnityPlugin
{
	public const string GUID = "catalysten.increasedenemycount";

	public const string NAME = "Increased Enemy Count";

	public const string VERSION = "1.2.3";

	internal static Plugin Instance;

	internal static ManualLogSource Log;

	internal static ConfigEntry<bool> Enabled;

	internal static ConfigEntry<float> SpawnSpacing;

	internal static ConfigEntry<string> ExcludedNames;

	internal static ConfigEntry<string> IncludedNames;

	internal static ConfigEntry<int> StandardMultiplier;

	internal static ConfigEntry<int> UniqueMultiplier;

	internal static ConfigEntry<int> BossMultiplier;

	internal static ConfigEntry<int> MaxMultiplier;

	internal const int MAX_MULTIPLIER_CEILING = 100;

	internal static ConfigEntry<bool> RequireModOnAllPlayers;

	internal static ConfigEntry<bool> PrioritizeMusic;

	internal static ConfigEntry<int> MaxAudibleSounds;

	internal static ConfigEntry<bool> LogEachEnemy;

	internal static ConfigEntry<string> InspectEnemies;

	private EnemyDoubler m_doubler;

	private void Awake()
	{
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Expected O, but got Unknown
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c2: Expected O, but got Unknown
		//IL_0108: Unknown result type (might be due to invalid IL or missing references)
		//IL_0112: Expected O, but got Unknown
		//IL_0158: Unknown result type (might be due to invalid IL or missing references)
		//IL_0162: Expected O, but got Unknown
		//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c5: Expected O, but got Unknown
		//IL_0224: Unknown result type (might be due to invalid IL or missing references)
		//IL_022e: Expected O, but got Unknown
		//IL_0282: Unknown result type (might be due to invalid IL or missing references)
		//IL_028c: Expected O, but got Unknown
		//IL_02e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ea: Expected O, but got Unknown
		//IL_032c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0336: Expected O, but got Unknown
		//IL_0378: Unknown result type (might be due to invalid IL or missing references)
		//IL_0382: Expected O, but got Unknown
		//IL_03df: Unknown result type (might be due to invalid IL or missing references)
		//IL_03e9: Expected O, but got Unknown
		//IL_042b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0435: Expected O, but got Unknown
		//IL_047b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0485: Expected O, but got Unknown
		//IL_04db: Unknown result type (might be due to invalid IL or missing references)
		Instance = this;
		Log = ((BaseUnityPlugin)this).Logger;
		Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, new ConfigDescription("Master switch. Takes effect the next time an area loads. In online co-op only the host's setting is used.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 100,
				DispName = "Enabled"
			}
		}));
		SpawnSpacing = ((BaseUnityPlugin)this).Config.Bind<float>("General", "SpawnSpacing", 2f, new ConfigDescription("How far (metres) each copy is placed from the original enemy. Takes effect the next time an area loads. In online co-op only the host's setting is used.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 10f), new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 90,
				DispName = "Spawn spacing (m)",
				ShowRangeAsPercent = false
			}
		}));
		ExcludedNames = ((BaseUnityPlugin)this).Config.Bind<string>("General", "ExcludedNames", "", new ConfigDescription("Comma-separated keywords. Any enemy whose name contains one of these (case-insensitive) is never copied, whatever its type. Wins over 'Always standard'. Takes effect the next time an area loads. In online co-op only the host's setting is used.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 80,
				DispName = "Never copy (keywords)"
			}
		}));
		IncludedNames = ((BaseUnityPlugin)this).Config.Bind<string>("General", "IncludedNames", "", new ConfigDescription("Comma-separated keywords. Any enemy whose name contains one of these (case-insensitive) is treated as a standard enemy and uses the standard multiplier, even if it's a boss or unique enemy. Takes effect the next time an area loads. In online co-op only the host's setting is used.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 70,
				DispName = "Always standard (keywords)"
			}
		}));
		MaxMultiplier = ((BaseUnityPlugin)this).Config.Bind<int>("Advanced", "MaxMultiplier", 10, new ConfigDescription("Highest value the three multiplier sliders can be set to (they also go up to this in the F5 menu). " + $"Up to {100}. Values above 10 are untested and can hurt performance badly: every enemy in an area " + "is multiplied, so 100x turns an area with 76 enemies into 7,600. Requires restarting the game. In online co-op the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 100), new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Browsable = false
			}
		}));
		int value = MaxMultiplier.Value;
		StandardMultiplier = ((BaseUnityPlugin)this).Config.Bind<int>("Multipliers", "StandardEnemies", 2, new ConfigDescription("Total number of each standard enemy (everything that isn't a boss or unique enemy). 1 = unchanged, 2 = double. Takes effect the next time an area loads. In online co-op only the host's setting is used.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, value), new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 100,
				DispName = "Standard enemies",
				ShowRangeAsPercent = false
			}
		}));
		UniqueMultiplier = ((BaseUnityPlugin)this).Config.Bind<int>("Multipliers", "UniqueEnemies", 2, new ConfigDescription("Total number of each unique enemy (named one-off enemies like the Ghost of Vanasse). 1 = unchanged. Bosses and unique enemies are the ones listed on the official Outward wiki (outward.wiki.gg); the names are matched in English. Takes effect the next time an area loads. In online co-op only the host's setting is used.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, value), new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 90,
				DispName = "Unique enemies",
				ShowRangeAsPercent = false
			}
		}));
		BossMultiplier = ((BaseUnityPlugin)this).Config.Bind<int>("Multipliers", "Bosses", 2, new ConfigDescription("Total number of each boss (Elite enemies and other arena bosses). 1 = unchanged. Bosses and unique enemies are the ones listed on the official Outward wiki (outward.wiki.gg); the names are matched in English. Takes effect the next time an area loads. In online co-op only the host's setting is used.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, value), new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 80,
				DispName = "Bosses",
				ShowRangeAsPercent = false
			}
		}));
		RequireModOnAllPlayers = ((BaseUnityPlugin)this).Config.Bind<bool>("Multiplayer", "RequireModOnAllPlayers", true, new ConfigDescription("Host only. If on, no extra enemies are added to an area while anyone in the session is missing this mod. Players without the mod can't see the extra enemies, but can still be hit by them.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 100,
				DispName = "Require mod on all players"
			}
		}));
		PrioritizeMusic = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "PrioritizeMusic", true, new ConfigDescription("Keeps music from cutting out when lots of enemies are making noise, by giving music the highest sound priority. Applies to the next music track that starts. Affects only this player.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 100,
				DispName = "Keep music playing"
			}
		}));
		MaxAudibleSounds = ((BaseUnityPlugin)this).Config.Bind<int>("Audio", "MaxAudibleSounds", 128, new ConfigDescription("How many sounds can be heard at once. Past this, Unity mutes the quietest ones, which makes voices in big groups choppy. Unity's default is 32. Higher costs a little CPU. Requires restarting the game. Affects only this player.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(32, 255), new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 90,
				DispName = "Max audible sounds (restart)",
				ShowRangeAsPercent = false
			}
		}));
		LogEachEnemy = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogEachEnemy", true, new ConfigDescription("Write one line per enemy to the BepInEx log (name, health, type and how many copies). Useful for checking the boss and unique lists.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 100,
				DispName = "Log each enemy"
			}
		}));
		InspectEnemies = ((BaseUnityPlugin)this).Config.Bind<string>("Debug", "InspectEnemies", "", new ConfigDescription("For troubleshooting. Comma-separated keywords: matching enemies get a detailed report in the BepInEx log when their area loads, and every change in the state of the original and its copies is logged while you play. Leave empty normally.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 90,
				DispName = "Inspect enemies (keywords)"
			}
		}));
		AudioVoiceLimit.Apply(MaxAudibleSounds.Value);
		m_doubler = ((Component)this).gameObject.AddComponent<EnemyDoubler>();
		SceneManager.sceneLoaded += m_doubler.OnSceneLoaded;
		SceneManager.sceneUnloaded += m_doubler.OnSceneUnloaded;
		new Harmony("catalysten.increasedenemycount").PatchAll(typeof(Patches));
		Log.LogMessage((object)string.Format("{0} {1} loaded. Standard x{2}, unique x{3}, bosses x{4}.", "Increased Enemy Count", "1.2.3", StandardMultiplier.Value, UniqueMultiplier.Value, BossMultiplier.Value));
	}
}