Decompiled source of Enemy Editor v1.0.0

EnemyHealthEditor.dll

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

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("")]
[assembly: AssemblyCompany("Omniscye")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("EnemyHealthEditor")]
[assembly: AssemblyTitle("EnemyHealthEditor")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace RepoMods.EnemyHealthEditor
{
	[BepInPlugin("com.repo.mods.enemy-health-editor", "Enemy Health + Respawn Editor", "1.4.0")]
	public class EnemyHealthEditorPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.repo.mods.enemy-health-editor";

		public const string PluginName = "Enemy Health + Respawn Editor";

		public const string PluginVersion = "1.4.0";

		internal static Harmony Harmony;

		internal static EnemyHealthEditorPlugin Instance;

		internal static ConfigEntry<int> DefaultEnemyHealth;

		internal static ConfigEntry<bool> ClampExistingHealthOnChange;

		internal static ConfigEntry<float> DefaultRespawnMin;

		internal static ConfigEntry<float> DefaultRespawnMax;

		internal static ConfigEntry<bool> ApplyRespawnToExistingWhenChanged;

		internal static readonly Dictionary<string, ConfigEntry<int>> PerEnemyHP = new Dictionary<string, ConfigEntry<int>>();

		internal static readonly Dictionary<string, ConfigEntry<float>> PerEnemyRespawnMin = new Dictionary<string, ConfigEntry<float>>();

		internal static readonly Dictionary<string, ConfigEntry<float>> PerEnemyRespawnMax = new Dictionary<string, ConfigEntry<float>>();

		private Rect _windowRect = new Rect(60f, 60f, 760f, 680f);

		private bool _showUI;

		private string _pendingHP = "";

		private string _pendingRMin = "";

		private string _pendingRMax = "";

		private Vector2 _scroll;

		private string _search = string.Empty;

		private void Awake()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Expected O, but got Unknown
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Expected O, but got Unknown
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Expected O, but got Unknown
			Instance = this;
			DefaultEnemyHealth = ((BaseUnityPlugin)this).Config.Bind<int>("General", "DefaultEnemyHealth", 100, new ConfigDescription("The base health value applied to enemies on Awake/Spawn.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100000), Array.Empty<object>()));
			ClampExistingHealthOnChange = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ApplyToExistingEnemiesWhenChanged", true, "When the HP value changes, immediately set all existing enemies to the new default.");
			DefaultRespawnMin = ((BaseUnityPlugin)this).Config.Bind<float>("Respawn", "DefaultRespawnMinSeconds", 5f, new ConfigDescription("Default minimum seconds between despawn and respawn (EnemyParent.DespawnedTimeMin).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 86400f), Array.Empty<object>()));
			DefaultRespawnMax = ((BaseUnityPlugin)this).Config.Bind<float>("Respawn", "DefaultRespawnMaxSeconds", 12f, new ConfigDescription("Default maximum seconds between despawn and respawn (EnemyParent.DespawnedTimeMax).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 86400f), Array.Empty<object>()));
			ApplyRespawnToExistingWhenChanged = ((BaseUnityPlugin)this).Config.Bind<bool>("Respawn", "ApplyRespawnToExistingWhenChanged", true, "When the respawn window changes, push to all existing EnemyParent instances.");
			_pendingHP = DefaultEnemyHealth.Value.ToString();
			_pendingRMin = DefaultRespawnMin.Value.ToString("0.##");
			_pendingRMax = DefaultRespawnMax.Value.ToString("0.##");
			Harmony = new Harmony("com.repo.mods.enemy-health-editor");
			Harmony.PatchAll(Assembly.GetExecutingAssembly());
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded. Default HP={2}, Respawn={3}-{4}s", "Enemy Health + Respawn Editor", "1.4.0", DefaultEnemyHealth.Value, DefaultRespawnMin.Value, DefaultRespawnMax.Value));
		}

		private void OnDestroy()
		{
			try
			{
				Harmony harmony = Harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
			}
			catch
			{
			}
		}

		private void Update()
		{
			if (Input.GetKeyDown((KeyCode)277))
			{
				_showUI = !_showUI;
			}
		}

		private void OnGUI()
		{
			//IL_0017: 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_0037: Expected O, but got Unknown
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			if (_showUI)
			{
				_windowRect = GUILayout.Window(14753536, _windowRect, new WindowFunction(DrawWindow), "Enemy Health + Respawn Editor", Array.Empty<GUILayoutOption>());
			}
		}

		private void DrawWindow(int id)
		{
			//IL_056d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0585: Unknown result type (might be due to invalid IL or missing references)
			//IL_058a: Unknown result type (might be due to invalid IL or missing references)
			//IL_08af: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.BeginVertical(Array.Empty<GUILayoutOption>());
			GUILayout.Label("Default Enemy Health", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(20f) });
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			int num = Mathf.Clamp(DefaultEnemyHealth.Value, 1, 100000);
			int num2 = (int)GUILayout.HorizontalSlider((float)num, 1f, 100000f, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MinWidth(320f) });
			if (num2 != DefaultEnemyHealth.Value)
			{
				SetDefaultHPFromUI(num2);
			}
			GUILayout.Space(12f);
			_pendingHP = GUILayout.TextField(_pendingHP, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) });
			if (GUILayout.Button("Set HP", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }))
			{
				if (int.TryParse(_pendingHP, out var result))
				{
					SetDefaultHPFromUI(Mathf.Clamp(result, 1, 100000));
				}
				else
				{
					_pendingHP = DefaultEnemyHealth.Value.ToString();
				}
			}
			GUILayout.EndHorizontal();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("Apply DEFAULT HP to ALL existing", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(360f) }))
			{
				EnemyUtilities.ApplyDefaultHPToAllExisting(DefaultEnemyHealth.Value);
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Applied default {DefaultEnemyHealth.Value} HP to all existing enemies.");
			}
			GUILayout.FlexibleSpace();
			GUILayout.EndHorizontal();
			GUILayout.Space(10f);
			GUILayout.Label("Respawn Window (DespawnedTimeMin/Max in EnemyParent)", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(20f) });
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label("Min s:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(48f) });
			_pendingRMin = GUILayout.TextField(_pendingRMin, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) });
			GUILayout.Space(12f);
			GUILayout.Label("Max s:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(56f) });
			_pendingRMax = GUILayout.TextField(_pendingRMax, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) });
			GUILayout.Space(12f);
			if (GUILayout.Button("Set Respawn", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) }))
			{
				if (float.TryParse(_pendingRMin, out var result2) && float.TryParse(_pendingRMax, out var result3))
				{
					SetDefaultRespawnFromUI(result2, result3);
				}
				else
				{
					_pendingRMin = DefaultRespawnMin.Value.ToString("0.##");
					_pendingRMax = DefaultRespawnMax.Value.ToString("0.##");
				}
			}
			GUILayout.EndHorizontal();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("Apply DEFAULT RESPAWN to ALL existing", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(360f) }))
			{
				EnemyUtilities.ApplyDefaultRespawnToAllExisting(DefaultRespawnMin.Value, DefaultRespawnMax.Value);
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Applied default respawn {DefaultRespawnMin.Value}-{DefaultRespawnMax.Value}s to all existing EnemyParents.");
			}
			GUILayout.FlexibleSpace();
			GUILayout.EndHorizontal();
			GUILayout.Space(12f);
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("Discover enemies in scene", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(220f) }))
			{
				EnemyUtilities.DiscoverAndRegisterOverrides();
			}
			GUILayout.Space(10f);
			if (GUILayout.Button("Discover from EnemyDirector", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(240f) }))
			{
				EnemyUtilities.RegisterFromEnemyDirector();
			}
			GUILayout.Space(10f);
			GUILayout.Label("Search:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) });
			_search = GUILayout.TextField(_search ?? string.Empty, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MinWidth(180f) });
			GUILayout.FlexibleSpace();
			if (GUILayout.Button("Apply ALL overrides to existing", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(280f) }))
			{
				EnemyUtilities.ApplyAllOverridesToExisting();
			}
			GUILayout.EndHorizontal();
			GUILayout.Space(6f);
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label("Enemy (Prefab|Type)", (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.MinWidth(320f),
				GUILayout.Height(18f)
			});
			GUILayout.Label("HP", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) });
			GUILayout.Label("Min s", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) });
			GUILayout.Label("Max s", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) });
			GUILayout.Space(70f);
			GUILayout.Space(70f);
			GUILayout.EndHorizontal();
			GUILayout.Box("", (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.ExpandWidth(true),
				GUILayout.Height(1f)
			});
			_scroll = GUILayout.BeginScrollView(_scroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(340f) });
			List<string> list = new List<string>(PerEnemyHP.Keys);
			list.Sort(StringComparer.OrdinalIgnoreCase);
			foreach (string item in list)
			{
				if (string.IsNullOrEmpty(_search) || item.IndexOf(_search, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					ConfigEntry<int> val = PerEnemyHP[item];
					ConfigEntry<float> orCreateRespawnMinEntry = EnemyUtilities.GetOrCreateRespawnMinEntry(item, DefaultRespawnMin.Value);
					ConfigEntry<float> orCreateRespawnMaxEntry = EnemyUtilities.GetOrCreateRespawnMaxEntry(item, DefaultRespawnMax.Value);
					GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
					GUILayout.Label(item, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MinWidth(320f) });
					string text = val.Value.ToString();
					string text2 = GUILayout.TextField(text, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) });
					if (text2 != text && int.TryParse(text2, out var result4))
					{
						result4 = (val.Value = Mathf.Clamp(result4, 1, 100000));
						((BaseUnityPlugin)this).Config.Save();
					}
					string text3 = orCreateRespawnMinEntry.Value.ToString("0.##");
					string text4 = GUILayout.TextField(text3, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) });
					if (text4 != text3 && float.TryParse(text4, out var result5))
					{
						orCreateRespawnMinEntry.Value = Mathf.Clamp(result5, 0f, 86400f);
						((BaseUnityPlugin)this).Config.Save();
					}
					string text5 = orCreateRespawnMaxEntry.Value.ToString("0.##");
					string text6 = GUILayout.TextField(text5, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) });
					if (text6 != text5 && float.TryParse(text6, out var result6))
					{
						orCreateRespawnMaxEntry.Value = Mathf.Clamp(result6, 0f, 86400f);
						((BaseUnityPlugin)this).Config.Save();
					}
					if (GUILayout.Button("Apply", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }))
					{
						EnemyUtilities.ApplyOverrideToExisting(item, val.Value, orCreateRespawnMinEntry.Value, orCreateRespawnMaxEntry.Value);
					}
					if (GUILayout.Button("Reset", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }))
					{
						val.Value = DefaultEnemyHealth.Value;
						orCreateRespawnMinEntry.Value = DefaultRespawnMin.Value;
						orCreateRespawnMaxEntry.Value = DefaultRespawnMax.Value;
						((BaseUnityPlugin)this).Config.Save();
					}
					GUILayout.EndHorizontal();
				}
			}
			GUILayout.EndScrollView();
			GUILayout.Space(6f);
			GUILayout.Label("Toggle UI with the Insert key.", Array.Empty<GUILayoutOption>());
			GUILayout.EndVertical();
			GUI.DragWindow(new Rect(0f, 0f, 10000f, 24f));
		}

		private void SetDefaultHPFromUI(int value)
		{
			if (DefaultEnemyHealth.Value != value)
			{
				DefaultEnemyHealth.Value = value;
				_pendingHP = value.ToString();
				((BaseUnityPlugin)this).Config.Save();
				if (ClampExistingHealthOnChange.Value)
				{
					EnemyUtilities.ApplyDefaultHPToAllExisting(value);
				}
			}
		}

		private void SetDefaultRespawnFromUI(float min, float max)
		{
			min = Mathf.Clamp(min, 0f, 86400f);
			max = Mathf.Clamp(max, 0f, 86400f);
			if (max < min)
			{
				float num = min;
				min = max;
				max = num;
			}
			if (!(Math.Abs(DefaultRespawnMin.Value - min) < 0.0001f) || !(Math.Abs(DefaultRespawnMax.Value - max) < 0.0001f))
			{
				DefaultRespawnMin.Value = min;
				DefaultRespawnMax.Value = max;
				_pendingRMin = min.ToString("0.##");
				_pendingRMax = max.ToString("0.##");
				((BaseUnityPlugin)this).Config.Save();
				if (ApplyRespawnToExistingWhenChanged.Value)
				{
					EnemyUtilities.ApplyDefaultRespawnToAllExisting(min, max);
				}
			}
		}
	}
	internal static class EnemyUtilities
	{
		private static Type EnemyHealthType => AccessTools.TypeByName("EnemyHealth");

		private static Type EnemyParentType => AccessTools.TypeByName("EnemyParent");

		public static string BuildKey(object enemyHealthInstance)
		{
			if (enemyHealthInstance == null)
			{
				return "<null>|<null>";
			}
			Component val = (Component)((enemyHealthInstance is Component) ? enemyHealthInstance : null);
			string text = (((Object)(object)val != (Object)null) ? ((Object)val.gameObject).name : enemyHealthInstance.GetType().Name);
			if (!string.IsNullOrEmpty(text))
			{
				text = text.Replace("(Clone)", string.Empty).Trim();
			}
			string name = enemyHealthInstance.GetType().Name;
			return text + "|" + name;
		}

		public static void DiscoverAndRegisterOverrides()
		{
			if (EnemyHealthType == null)
			{
				Debug.LogWarning((object)"EnemyHealth type not found.");
				return;
			}
			Object[] array = Resources.FindObjectsOfTypeAll(EnemyHealthType);
			Object[] array2 = array;
			foreach (Object enemyHealthInstance in array2)
			{
				string key = BuildKey(enemyHealthInstance);
				GetOrCreateHPEntry(key, EnemyHealthEditorPlugin.DefaultEnemyHealth.Value);
				GetOrCreateRespawnMinEntry(key, EnemyHealthEditorPlugin.DefaultRespawnMin.Value);
				GetOrCreateRespawnMaxEntry(key, EnemyHealthEditorPlugin.DefaultRespawnMax.Value);
			}
		}

		public static void RegisterFromEnemyDirector()
		{
			try
			{
				Type type = AccessTools.TypeByName("EnemyDirector");
				if (type == null)
				{
					Debug.LogWarning((object)"EnemyDirector type not found.");
					return;
				}
				object? obj = AccessTools.Field(type, "instance")?.GetValue(null);
				Object val = (Object)(((obj is Object) ? obj : null) ?? Object.FindObjectOfType(type));
				if (val == (Object)null)
				{
					Debug.LogWarning((object)"EnemyDirector instance not found.");
					return;
				}
				string[] array = new string[3] { "enemiesDifficulty1", "enemiesDifficulty2", "enemiesDifficulty3" };
				string[] array2 = array;
				foreach (string text in array2)
				{
					if (!(AccessTools.Field(type, text)?.GetValue(val) is IEnumerable enumerable))
					{
						continue;
					}
					foreach (object item in enumerable)
					{
						if (item == null)
						{
							continue;
						}
						Type type2 = item.GetType();
						if (!(AccessTools.Field(type2, "spawnObjects")?.GetValue(item) is IEnumerable enumerable2))
						{
							continue;
						}
						foreach (object item2 in enumerable2)
						{
							GameObject val2 = (GameObject)((item2 is GameObject) ? item2 : null);
							if ((Object)(object)val2 == (Object)null)
							{
								continue;
							}
							string text2 = ((Object)val2).name?.Replace("(Clone)", string.Empty).Trim();
							string text3 = "EnemyHealth";
							try
							{
								MonoBehaviour[] componentsInChildren = val2.GetComponentsInChildren<MonoBehaviour>(true);
								MonoBehaviour[] array3 = componentsInChildren;
								foreach (MonoBehaviour val3 in array3)
								{
									if ((Object)(object)val3 != (Object)null && ((object)val3).GetType().Name == "EnemyHealth")
									{
										text3 = ((object)val3).GetType().Name;
										break;
									}
								}
							}
							catch
							{
							}
							string key = text2 + "|" + text3;
							GetOrCreateHPEntry(key, EnemyHealthEditorPlugin.DefaultEnemyHealth.Value);
							GetOrCreateRespawnMinEntry(key, EnemyHealthEditorPlugin.DefaultRespawnMin.Value);
							GetOrCreateRespawnMaxEntry(key, EnemyHealthEditorPlugin.DefaultRespawnMax.Value);
						}
					}
				}
			}
			catch (Exception arg)
			{
				Debug.LogError((object)$"EnemyHealthEditor: RegisterFromEnemyDirector failed: {arg}");
			}
		}

		public static ConfigEntry<int> GetOrCreateHPEntry(string key, int fallback)
		{
			if (EnemyHealthEditorPlugin.PerEnemyHP.TryGetValue(key, out ConfigEntry<int> value))
			{
				return value;
			}
			value = ((BaseUnityPlugin)EnemyHealthEditorPlugin.Instance).Config.Bind<int>("PerEnemyHealth", key, fallback, "Override health for this enemy (Prefab|Type)");
			EnemyHealthEditorPlugin.PerEnemyHP[key] = value;
			return value;
		}

		public static ConfigEntry<float> GetOrCreateRespawnMinEntry(string key, float fallback)
		{
			if (EnemyHealthEditorPlugin.PerEnemyRespawnMin.TryGetValue(key, out ConfigEntry<float> value))
			{
				return value;
			}
			value = ((BaseUnityPlugin)EnemyHealthEditorPlugin.Instance).Config.Bind<float>("PerEnemyRespawnMin", key, fallback, "Override DESPAWN→RESPAWN minimum seconds (EnemyParent.DespawnedTimeMin)");
			EnemyHealthEditorPlugin.PerEnemyRespawnMin[key] = value;
			return value;
		}

		public static ConfigEntry<float> GetOrCreateRespawnMaxEntry(string key, float fallback)
		{
			if (EnemyHealthEditorPlugin.PerEnemyRespawnMax.TryGetValue(key, out ConfigEntry<float> value))
			{
				return value;
			}
			value = ((BaseUnityPlugin)EnemyHealthEditorPlugin.Instance).Config.Bind<float>("PerEnemyRespawnMax", key, fallback, "Override DESPAWN→RESPAWN maximum seconds (EnemyParent.DespawnedTimeMax)");
			EnemyHealthEditorPlugin.PerEnemyRespawnMax[key] = value;
			return value;
		}

		public static int GetHealthFor(object enemyHealthInstance)
		{
			int fallback = EnemyHealthEditorPlugin.DefaultEnemyHealth?.Value ?? 100;
			string key = BuildKey(enemyHealthInstance);
			if (EnemyHealthEditorPlugin.PerEnemyHP.TryGetValue(key, out ConfigEntry<int> value))
			{
				return value.Value;
			}
			return GetOrCreateHPEntry(key, fallback).Value;
		}

		public static (float min, float max) GetRespawnFor(object enemyHealthInstance)
		{
			float fallback = EnemyHealthEditorPlugin.DefaultRespawnMin?.Value ?? 0f;
			float fallback2 = EnemyHealthEditorPlugin.DefaultRespawnMax?.Value ?? 0f;
			string key = BuildKey(enemyHealthInstance);
			float value = GetOrCreateRespawnMinEntry(key, fallback).Value;
			float num = GetOrCreateRespawnMaxEntry(key, fallback2).Value;
			if (num < value)
			{
				num = value;
			}
			return (value, num);
		}

		public static void ApplyDefaultHPToAllExisting(int hp)
		{
			if (!(EnemyHealthType == null))
			{
				Object[] array = Resources.FindObjectsOfTypeAll(EnemyHealthType);
				Object[] array2 = array;
				foreach (Object enemyHealthInstance in array2)
				{
					SetEnemyHealth(enemyHealthInstance, hp);
				}
			}
		}

		public static void ApplyDefaultRespawnToAllExisting(float min, float max)
		{
			if (EnemyParentType == null || EnemyHealthType == null)
			{
				return;
			}
			Object[] array = Resources.FindObjectsOfTypeAll(EnemyHealthType);
			Object[] array2 = array;
			foreach (Object val in array2)
			{
				Component enemyParentFromHealth = GetEnemyParentFromHealth((Component)(object)((val is Component) ? val : null));
				if ((Object)(object)enemyParentFromHealth != (Object)null)
				{
					SetRespawnWindowOnParent(enemyParentFromHealth, min, max, adjustCurrentTimer: true);
				}
			}
		}

		public static void ApplyAllOverridesToExisting()
		{
			if (EnemyHealthType == null)
			{
				return;
			}
			Object[] array = Resources.FindObjectsOfTypeAll(EnemyHealthType);
			Object[] array2 = array;
			foreach (Object val in array2)
			{
				int healthFor = GetHealthFor(val);
				SetEnemyHealth(val, healthFor);
				(float min, float max) respawnFor = GetRespawnFor(val);
				float item = respawnFor.min;
				float item2 = respawnFor.max;
				Component enemyParentFromHealth = GetEnemyParentFromHealth((Component)(object)((val is Component) ? val : null));
				if ((Object)(object)enemyParentFromHealth != (Object)null)
				{
					SetRespawnWindowOnParent(enemyParentFromHealth, item, item2, adjustCurrentTimer: true);
				}
			}
		}

		public static void ApplyOverrideToExisting(string key, int hp, float rmin, float rmax)
		{
			if (EnemyHealthType == null)
			{
				return;
			}
			Object[] array = Resources.FindObjectsOfTypeAll(EnemyHealthType);
			Object[] array2 = array;
			foreach (Object val in array2)
			{
				if (BuildKey(val) == key)
				{
					SetEnemyHealth(val, hp);
					Component enemyParentFromHealth = GetEnemyParentFromHealth((Component)(object)((val is Component) ? val : null));
					if ((Object)(object)enemyParentFromHealth != (Object)null)
					{
						SetRespawnWindowOnParent(enemyParentFromHealth, rmin, rmax, adjustCurrentTimer: true);
					}
				}
			}
		}

		public static void SetEnemyHealth(object enemyHealthInstance, int hp)
		{
			if (enemyHealthInstance != null)
			{
				Type type = enemyHealthInstance.GetType();
				FieldInfo fieldInfo = AccessTools.Field(type, "health");
				FieldInfo fieldInfo2 = AccessTools.Field(type, "healthCurrent");
				fieldInfo?.SetValue(enemyHealthInstance, hp);
				fieldInfo2?.SetValue(enemyHealthInstance, hp);
			}
		}

		public static Component GetEnemyParentFromHealth(Component enemyHealthComp)
		{
			if ((Object)(object)enemyHealthComp == (Object)null || EnemyParentType == null)
			{
				return null;
			}
			return enemyHealthComp.GetComponentInParent(EnemyParentType);
		}

		public static void SetRespawnWindowOnParent(Component enemyParent, float min, float max, bool adjustCurrentTimer)
		{
			if ((Object)(object)enemyParent == (Object)null)
			{
				return;
			}
			min = Mathf.Clamp(min, 0f, 86400f);
			max = Mathf.Clamp(max, 0f, 86400f);
			if (max < min)
			{
				float num = min;
				min = max;
				max = num;
			}
			Type type = ((object)enemyParent).GetType();
			FieldInfo fieldInfo = AccessTools.Field(type, "DespawnedTimeMin");
			FieldInfo fieldInfo2 = AccessTools.Field(type, "DespawnedTimeMax");
			FieldInfo fieldInfo3 = AccessTools.Field(type, "DespawnedTimer");
			FieldInfo fieldInfo4 = AccessTools.Field(type, "Spawned");
			try
			{
				fieldInfo?.SetValue(enemyParent, min);
			}
			catch
			{
			}
			try
			{
				fieldInfo2?.SetValue(enemyParent, max);
			}
			catch
			{
			}
			if (!adjustCurrentTimer || !(fieldInfo3 != null))
			{
				return;
			}
			bool flag = true;
			try
			{
				if (fieldInfo4 != null)
				{
					flag = (bool)fieldInfo4.GetValue(enemyParent);
				}
			}
			catch
			{
			}
			if (flag)
			{
				return;
			}
			float num2 = (min + max) * 0.5f;
			try
			{
				fieldInfo3.SetValue(enemyParent, num2);
			}
			catch
			{
			}
		}
	}
	[HarmonyPatch]
	internal static class EnemyHealthPatches
	{
		[CompilerGenerated]
		private sealed class <TargetMethods>d__0 : IEnumerable<MethodBase>, IEnumerable, IEnumerator<MethodBase>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private MethodBase <>2__current;

			private int <>l__initialThreadId;

			private Type <t>5__1;

			private string[] <names>5__2;

			private string[] <>s__3;

			private int <>s__4;

			private string <n>5__5;

			private MethodInfo <m>5__6;

			MethodBase IEnumerator<MethodBase>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <TargetMethods>d__0(int <>1__state)
			{
				this.<>1__state = <>1__state;
				<>l__initialThreadId = Environment.CurrentManagedThreadId;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<t>5__1 = null;
				<names>5__2 = null;
				<>s__3 = null;
				<n>5__5 = null;
				<m>5__6 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				int num = <>1__state;
				if (num != 0)
				{
					if (num != 1)
					{
						return false;
					}
					<>1__state = -1;
					goto IL_00e0;
				}
				<>1__state = -1;
				<t>5__1 = AccessTools.TypeByName("EnemyHealth");
				if (<t>5__1 == null)
				{
					return false;
				}
				<names>5__2 = new string[4] { "Awake", "OnSpawn", "Start", "OnEnable" };
				<>s__3 = <names>5__2;
				<>s__4 = 0;
				goto IL_00fd;
				IL_00e0:
				<m>5__6 = null;
				<n>5__5 = null;
				<>s__4++;
				goto IL_00fd;
				IL_00fd:
				if (<>s__4 < <>s__3.Length)
				{
					<n>5__5 = <>s__3[<>s__4];
					<m>5__6 = AccessTools.Method(<t>5__1, <n>5__5, (Type[])null, (Type[])null);
					if (<m>5__6 != null)
					{
						<>2__current = <m>5__6;
						<>1__state = 1;
						return true;
					}
					goto IL_00e0;
				}
				<>s__3 = null;
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}

			[DebuggerHidden]
			IEnumerator<MethodBase> IEnumerable<MethodBase>.GetEnumerator()
			{
				if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
				{
					<>1__state = 0;
					return this;
				}
				return new <TargetMethods>d__0(0);
			}

			[DebuggerHidden]
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<MethodBase>)this).GetEnumerator();
			}
		}

		[IteratorStateMachine(typeof(<TargetMethods>d__0))]
		private static IEnumerable<MethodBase> TargetMethods()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <TargetMethods>d__0(-2);
		}

		private static void Postfix(object __instance)
		{
			try
			{
				int healthFor = EnemyUtilities.GetHealthFor(__instance);
				EnemyUtilities.SetEnemyHealth(__instance, healthFor);
				(float min, float max) respawnFor = EnemyUtilities.GetRespawnFor(__instance);
				float item = respawnFor.min;
				float item2 = respawnFor.max;
				Component enemyParentFromHealth = EnemyUtilities.GetEnemyParentFromHealth((Component)((__instance is Component) ? __instance : null));
				if ((Object)(object)enemyParentFromHealth != (Object)null)
				{
					EnemyUtilities.SetRespawnWindowOnParent(enemyParentFromHealth, item, item2, adjustCurrentTimer: false);
				}
			}
			catch (Exception arg)
			{
				Debug.LogError((object)$"EnemyHealthEditor: failed to apply HP/Respawn: {arg}");
			}
		}
	}
}
namespace EnemyHealthEditor
{
	[BepInPlugin("Omniscye.EnemyHealthEditor", "EnemyHealthEditor", "1.0")]
	public class EnemyHealthEditor : BaseUnityPlugin
	{
		internal static EnemyHealthEditor Instance { get; private set; }

		internal static ManualLogSource Logger => Instance._logger;

		private ManualLogSource _logger => ((BaseUnityPlugin)this).Logger;

		internal Harmony? Harmony { get; set; }

		private void Awake()
		{
			Instance = this;
			((Component)this).gameObject.transform.parent = null;
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			Patch();
			Logger.LogInfo((object)$"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} has loaded!");
		}

		internal void Patch()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			//IL_0026: Expected O, but got Unknown
			if (Harmony == null)
			{
				Harmony val = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID);
				Harmony val2 = val;
				Harmony = val;
			}
			Harmony.PatchAll();
		}

		internal void Unpatch()
		{
			Harmony? harmony = Harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void Update()
		{
		}
	}
}