Decompiled source of ShowEnemyHP v1.0.5

ShowEnemyHp.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using Assets.Scripts.Actors.Enemies;
using Assets.Scripts.Managers;
using BepInEx;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem.Collections.Generic;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ShowEnemyHp")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ShowEnemyHp")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("7eab9528-cad4-481a-af54-e0691c7353a7")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ShowEnemyHpMod
{
	internal sealed class EnemyUiContext
	{
		public GameObject Root { get; }

		public GameObject NumericRoot { get; }

		public TextMeshProUGUI NumericText { get; }

		public GameObject BarRoot { get; }

		public Image BarFill { get; }

		public RectTransform BarFillRect { get; }

		public float BarFullWidth { get; }

		public EnemyUiContext(GameObject root, GameObject numericRoot, TextMeshProUGUI numericText, GameObject barRoot, Image barFill, RectTransform barFillRect, float barFullWidth)
		{
			Root = root;
			NumericRoot = numericRoot;
			NumericText = numericText;
			BarRoot = barRoot;
			BarFill = barFill;
			BarFillRect = barFillRect;
			BarFullWidth = barFullWidth;
		}
	}
	internal enum HpDisplayMode
	{
		Numeric,
		Bar
	}
	internal readonly struct HpFontInfo
	{
		public string Key { get; }

		public TMP_FontAsset Asset { get; }

		public string Description { get; }

		public HpFontInfo(string key, TMP_FontAsset asset, string description = "")
		{
			Key = key;
			Asset = asset;
			Description = description ?? string.Empty;
		}
	}
	internal static class HpFontCatalog
	{
		[CompilerGenerated]
		private sealed class <GetFontSummaries>d__8 : IEnumerable<string>, IEnumerable, IEnumerator<string>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private string <>2__current;

			private int <>l__initialThreadId;

			private List<string>.Enumerator <>7__wrap1;

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

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

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

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || num == 1)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<>7__wrap1 = default(List<string>.Enumerator);
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				try
				{
					switch (<>1__state)
					{
					default:
						return false;
					case 0:
						<>1__state = -1;
						EnsureInitialized();
						<>7__wrap1 = _orderedKeys.GetEnumerator();
						<>1__state = -3;
						break;
					case 1:
						<>1__state = -3;
						break;
					}
					while (<>7__wrap1.MoveNext())
					{
						string current = <>7__wrap1.Current;
						if (_fonts.TryGetValue(current, out var value))
						{
							<>2__current = value.Key;
							<>1__state = 1;
							return true;
						}
					}
					<>m__Finally1();
					<>7__wrap1 = default(List<string>.Enumerator);
					return false;
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
			}

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

			private void <>m__Finally1()
			{
				<>1__state = -1;
				((IDisposable)<>7__wrap1).Dispose();
			}

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

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

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

		public const string DEFAULT_KEY = "Default";

		public const string NOTO_KEY = "NotoSansSC";

		private const string NOTO_ASSET_NAME = "NotoSansSC-Bold SDF";

		private static readonly string[] NotoResourcePaths = new string[2] { "Fonts & Materials/NotoSansSC-Bold SDF", "TextMesh Pro/Resources/Fonts & Materials/NotoSansSC-Bold SDF" };

		private static bool _initialized;

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

		private static readonly List<string> _orderedKeys = new List<string>();

		public static void EnsureInitialized()
		{
			if (!_initialized)
			{
				_initialized = true;
				_fonts.Clear();
				_orderedKeys.Clear();
				RegisterDefaultFromSettings();
				RegisterNotoIfPresent();
				if (_fonts.Count == 0)
				{
					LoggerBridge.Warn("[ShowEnemyHP] No TMP.TMP fonts registered (Default/Noto).");
				}
			}
		}

		[IteratorStateMachine(typeof(<GetFontSummaries>d__8))]
		public static IEnumerable<string> GetFontSummaries()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <GetFontSummaries>d__8(-2);
		}

		public static bool TryResolveFont(string raw, out string normalizedName)
		{
			EnsureInitialized();
			if (string.IsNullOrWhiteSpace(raw))
			{
				normalizedName = "Default";
				return false;
			}
			if (string.Equals(raw, "NotoSansSC", StringComparison.OrdinalIgnoreCase) || string.Equals(raw, "NotoSansSC-Bold SDF", StringComparison.OrdinalIgnoreCase))
			{
				if (_fonts.ContainsKey("NotoSansSC"))
				{
					normalizedName = "NotoSansSC";
					return true;
				}
				LoggerBridge.Warn("[ShowEnemyHP] Requested NotoSansSC but asset not found; using Default.");
				normalizedName = "Default";
				return false;
			}
			normalizedName = "Default";
			return string.Equals(raw, "Default", StringComparison.OrdinalIgnoreCase);
		}

		public static TMP_FontAsset GetFontAsset(string key)
		{
			EnsureInitialized();
			if (string.IsNullOrEmpty(key))
			{
				key = "Default";
			}
			if (string.Equals(key, "NotoSansSC", StringComparison.OrdinalIgnoreCase) && _fonts.TryGetValue("NotoSansSC", out var value))
			{
				return value.Asset;
			}
			if (_fonts.TryGetValue("Default", out var value2))
			{
				return value2.Asset;
			}
			throw new InvalidOperationException("No TextMeshPro font assets are registered.");
		}

		private static void RegisterDefaultFromSettings()
		{
			try
			{
				TMP_FontAsset defaultFontAsset = TMP_Settings.defaultFontAsset;
				if ((Object)(object)defaultFontAsset != (Object)null)
				{
					Register("Default", defaultFontAsset, "TMP.TMP default");
				}
				else
				{
					LoggerBridge.Warn("[ShowEnemyHP] TMP.TMP_Settings.defaultFontAsset is null.");
				}
			}
			catch (Exception ex)
			{
				LoggerBridge.Warn("[ShowEnemyHP] Failed to get TMP.TMP default font: " + ex.Message);
			}
		}

		private static void RegisterNotoIfPresent()
		{
			TMP_FontAsset val = null;
			for (int i = 0; i < NotoResourcePaths.Length; i++)
			{
				try
				{
					TMP_FontAsset val2 = Resources.Load<TMP_FontAsset>(NotoResourcePaths[i]);
					if ((Object)(object)val2 != (Object)null && string.Equals(((Object)val2).name, "NotoSansSC-Bold SDF", StringComparison.Ordinal))
					{
						val = val2;
						break;
					}
				}
				catch
				{
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				try
				{
					Il2CppArrayBase<TMP_FontAsset> val3 = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
					for (int j = 0; j < val3.Length; j++)
					{
						TMP_FontAsset val4 = val3[j];
						if ((Object)(object)val4 != (Object)null && string.Equals(((Object)val4).name, "NotoSansSC-Bold SDF", StringComparison.Ordinal))
						{
							val = val4;
							break;
						}
					}
				}
				catch
				{
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				try
				{
					List<TMP_FontAsset> fallbackFontAssets = TMP_Settings.fallbackFontAssets;
					if (fallbackFontAssets != null)
					{
						for (int k = 0; k < fallbackFontAssets.Count; k++)
						{
							TMP_FontAsset val5 = fallbackFontAssets[k];
							if ((Object)(object)val5 != (Object)null && string.Equals(((Object)val5).name, "NotoSansSC-Bold SDF", StringComparison.Ordinal))
							{
								val = val5;
								break;
							}
						}
					}
				}
				catch
				{
				}
			}
			if ((Object)(object)val != (Object)null)
			{
				Register("NotoSansSC", val, "Noto");
			}
		}

		private static void Register(string key, TMP_FontAsset font, string description)
		{
			if (!((Object)(object)font == (Object)null) && !string.IsNullOrEmpty(key) && !_fonts.ContainsKey(key))
			{
				_fonts[key] = new HpFontInfo(key, font, description);
				_orderedKeys.Add(key);
			}
		}
	}
	internal enum HpColorScheme
	{
		Classic,
		Cosmic,
		Electric,
		Aurora
	}
	internal readonly struct HpPaletteDefinition
	{
		public Color Low { get; }

		public Color Mid { get; }

		public Color High { get; }

		public HpPaletteDefinition(Color low, Color mid, Color high)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			Low = low;
			Mid = mid;
			High = high;
		}

		public Color Evaluate(float pct)
		{
			//IL_003a: 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)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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_0025: Unknown result type (might be due to invalid IL or missing references)
			pct = Mathf.Clamp01(pct);
			if (pct < 0.5f)
			{
				float num = pct / 0.5f;
				return Color.Lerp(Low, Mid, num);
			}
			float num2 = (pct - 0.5f) / 0.5f;
			return Color.Lerp(Mid, High, num2);
		}
	}
	internal static class HpPalette
	{
		[CompilerGenerated]
		private sealed class <GetSchemeSummaries>d__6 : IEnumerable<string>, IEnumerable, IEnumerator<string>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private string <>2__current;

			private int <>l__initialThreadId;

			private HpColorScheme[] <>7__wrap1;

			private int <>7__wrap2;

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

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

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

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>7__wrap1 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				int num = <>1__state;
				if (num != 0)
				{
					if (num != 1)
					{
						return false;
					}
					<>1__state = -1;
					goto IL_0099;
				}
				<>1__state = -1;
				<>7__wrap1 = ORDERED_SCHEMES;
				<>7__wrap2 = 0;
				goto IL_00a7;
				IL_0099:
				<>7__wrap2++;
				goto IL_00a7;
				IL_00a7:
				if (<>7__wrap2 < <>7__wrap1.Length)
				{
					HpColorScheme hpColorScheme = <>7__wrap1[<>7__wrap2];
					if (DESCRIPTIONS.TryGetValue(hpColorScheme, out var value))
					{
						<>2__current = $"{hpColorScheme} ({value})";
						<>1__state = 1;
						return true;
					}
					goto IL_0099;
				}
				<>7__wrap1 = 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<string> IEnumerable<string>.GetEnumerator()
			{
				if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
				{
					<>1__state = 0;
					return this;
				}
				return new <GetSchemeSummaries>d__6(0);
			}

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

		private static readonly HpPaletteDefinition CLASSIC = new HpPaletteDefinition(new Color(0.95f, 0.12f, 0.12f), new Color(1f, 0.85f, 0.05f), new Color(0.08f, 0.9f, 0.3f));

		private static readonly HpPaletteDefinition COSMIC = new HpPaletteDefinition(new Color(1f, 0.18f, 0.722f), new Color(1f, 0.478f, 0f), new Color(0.431f, 0.184f, 0.961f));

		private static readonly HpPaletteDefinition ELECTRIC = new HpPaletteDefinition(new Color(0.039f, 0.141f, 0.388f), new Color(0f, 0.549f, 1f), new Color(1f, 0.831f, 0f));

		private static readonly HpPaletteDefinition AURORA = new HpPaletteDefinition(new Color(0.659f, 0.937f, 1f), new Color(0.557f, 0.176f, 0.886f), new Color(0f, 0.902f, 0.463f));

		private static readonly HpColorScheme[] ORDERED_SCHEMES = new HpColorScheme[4]
		{
			HpColorScheme.Classic,
			HpColorScheme.Cosmic,
			HpColorScheme.Electric,
			HpColorScheme.Aurora
		};

		private static readonly Dictionary<HpColorScheme, string> DESCRIPTIONS = new Dictionary<HpColorScheme, string>
		{
			{
				HpColorScheme.Classic,
				"red→yellow→green"
			},
			{
				HpColorScheme.Cosmic,
				"pink→orange→purple"
			},
			{
				HpColorScheme.Electric,
				"dark blue→blue→yellow"
			},
			{
				HpColorScheme.Aurora,
				"ice blue→violet→emerald"
			}
		};

		[IteratorStateMachine(typeof(<GetSchemeSummaries>d__6))]
		public static IEnumerable<string> GetSchemeSummaries()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <GetSchemeSummaries>d__6(-2);
		}

		public static Color FromPercent(float pct)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			return (ModConfig.ColorScheme switch
			{
				HpColorScheme.Cosmic => COSMIC, 
				HpColorScheme.Electric => ELECTRIC, 
				HpColorScheme.Aurora => AURORA, 
				_ => CLASSIC, 
			}).Evaluate(pct);
		}
	}
	internal static class LoggerBridge
	{
		public static Action<string> Info = delegate
		{
		};

		public static Action<string> Warn = delegate
		{
		};

		public static Action<string> Error = delegate
		{
		};
	}
	internal static class ModConfig
	{
		private const string FILE_NAME = "ShowEnemyHP.cfg";

		private const float SCALE_DEFAULT = 0.03f;

		private const float SCALE_MIN = 0.005f;

		private const float SCALE_MAX = 0.2f;

		private const float OFFSET_DEFAULT = -0.25f;

		private const float OFFSET_MIN = -5f;

		private const float OFFSET_MAX = 5f;

		private const HpColorScheme COLOR_SCHEME_DEFAULT = HpColorScheme.Classic;

		private const HpDisplayMode DISPLAY_MODE_DEFAULT = HpDisplayMode.Numeric;

		private const string FONT_DEFAULT = "Default";

		private static float _scale = 0.03f;

		private static float _verticalOffset = -0.25f;

		private static HpColorScheme _colorScheme = HpColorScheme.Classic;

		private static HpDisplayMode _displayMode = HpDisplayMode.Numeric;

		private static string _fontName = "Default";

		public static float Scale => _scale;

		public static float VerticalOffset => _verticalOffset;

		public static HpColorScheme ColorScheme => _colorScheme;

		public static HpDisplayMode DisplayMode => _displayMode;

		public static string FontName => _fontName;

		public static void Init()
		{
			ResetToDefaults();
			if (!TryLoadFromDisk())
			{
				LoggerBridge.Warn("[ShowEnemyHP] Using default config values.");
			}
			WriteAnnotatedFile();
		}

		public static bool ReloadFromDisk()
		{
			if (TryLoadFromDisk())
			{
				LoggerBridge.Info("[ShowEnemyHP] Config reloaded from disk.");
				return true;
			}
			LoggerBridge.Warn("[ShowEnemyHP] Config reload failed; keeping previous values.");
			return false;
		}

		private static void ResetToDefaults()
		{
			_scale = 0.03f;
			_verticalOffset = -0.25f;
			_colorScheme = HpColorScheme.Classic;
			_displayMode = HpDisplayMode.Numeric;
			_fontName = "Default";
		}

		private static bool TryLoadFromDisk()
		{
			ResetToDefaults();
			HpFontCatalog.EnsureInitialized();
			string path = Path.Combine(PathsBridge.EnsureUserDataDir(), "ShowEnemyHP.cfg");
			if (!File.Exists(path))
			{
				return false;
			}
			try
			{
				string[] array = File.ReadAllLines(path);
				foreach (string text in array)
				{
					if (string.IsNullOrWhiteSpace(text))
					{
						continue;
					}
					string text2 = text.Trim();
					if (text2.StartsWith("#") || text2.StartsWith("//"))
					{
						continue;
					}
					int num = text2.IndexOf('=');
					if (num <= 0 || num >= text2.Length - 1)
					{
						continue;
					}
					string text3 = text2.Substring(0, num).Trim();
					string text4 = text2.Substring(num + 1).Trim();
					switch (text3.ToLowerInvariant())
					{
					case "scale":
					{
						if (TryParseFloat(text4, out var value))
						{
							_scale = Mathf.Clamp(value, 0.005f, 0.2f);
						}
						else
						{
							LoggerBridge.Warn("[ShowEnemyHP] Invalid float for Scale: '" + text4 + "'.");
						}
						break;
					}
					case "verticaloffset":
					{
						if (TryParseFloat(text4, out var value2))
						{
							_verticalOffset = Mathf.Clamp(value2, -5f, 5f);
						}
						else
						{
							LoggerBridge.Warn("[ShowEnemyHP] Invalid float for VerticalOffset: '" + text4 + "'.");
						}
						break;
					}
					case "colorscheme":
					{
						if (TryParseColorScheme(text4, out var scheme))
						{
							_colorScheme = scheme;
						}
						else
						{
							LoggerBridge.Warn("[ShowEnemyHP] Unknown ColorScheme '" + text4 + "'.");
						}
						break;
					}
					case "font":
					{
						if (HpFontCatalog.TryResolveFont(text4, out var normalizedName))
						{
							_fontName = normalizedName;
						}
						else
						{
							LoggerBridge.Warn("[ShowEnemyHP] Unknown Font '" + text4 + "'.");
						}
						break;
					}
					case "displaymode":
					{
						if (TryParseDisplayMode(text4, out var mode))
						{
							_displayMode = mode;
						}
						else
						{
							LoggerBridge.Warn("[ShowEnemyHP] Unknown DisplayMode '" + text4 + "'.");
						}
						break;
					}
					}
				}
				return true;
			}
			catch (Exception ex)
			{
				LoggerBridge.Error("[ShowEnemyHP] Failed to read config: " + ex.Message);
				ResetToDefaults();
				return false;
			}
		}

		private static bool TryParseFloat(string raw, out float value)
		{
			return float.TryParse(raw, NumberStyles.Float, CultureInfo.InvariantCulture, out value);
		}

		private static bool TryParseColorScheme(string raw, out HpColorScheme scheme)
		{
			return Enum.TryParse<HpColorScheme>(raw, ignoreCase: true, out scheme);
		}

		private static bool TryParseDisplayMode(string raw, out HpDisplayMode mode)
		{
			return Enum.TryParse<HpDisplayMode>(raw, ignoreCase: true, out mode);
		}

		private static void WriteAnnotatedFile()
		{
			try
			{
				string path = Path.Combine(PathsBridge.EnsureUserDataDir(), "ShowEnemyHP.cfg");
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.AppendLine("# Show Enemy HP configuration");
				stringBuilder.AppendLine("# Scale adjusts the size of the floating HP canvas.");
				stringBuilder.AppendLine("#   Range: 0.005 – 0.200 (default 0.03)");
				stringBuilder.AppendLine("Scale = " + _scale.ToString(CultureInfo.InvariantCulture));
				stringBuilder.AppendLine();
				stringBuilder.AppendLine("# VerticalOffset moves the label relative to the enemy head (positive is up).");
				stringBuilder.AppendLine("#   Range: -5.0 – 5.0 (default -0.25)");
				stringBuilder.AppendLine("VerticalOffset = " + _verticalOffset.ToString(CultureInfo.InvariantCulture));
				stringBuilder.AppendLine();
				stringBuilder.AppendLine("# ColorScheme controls the HP gradient.");
				stringBuilder.AppendLine("#   Options: " + string.Join(", ", HpPalette.GetSchemeSummaries()));
				stringBuilder.AppendLine("#   Default: " + HpColorScheme.Classic);
				stringBuilder.AppendLine("ColorScheme = " + _colorScheme);
				stringBuilder.AppendLine();
				stringBuilder.AppendLine("# DisplayMode switches between numeric text and a simple bar.");
				stringBuilder.AppendLine("#   Options: Numeric, Bar (default Numeric)");
				stringBuilder.AppendLine("DisplayMode = " + _displayMode);
				stringBuilder.AppendLine();
				stringBuilder.AppendLine("# Font selects the TextMeshPro font asset used for HP numbers.");
				stringBuilder.AppendLine("#   Options: " + string.Join(", ", HpFontCatalog.GetFontSummaries()));
				stringBuilder.AppendLine("#   Default: Default");
				stringBuilder.AppendLine("Font = " + _fontName);
				File.WriteAllText(path, stringBuilder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
			}
			catch (Exception ex)
			{
				LoggerBridge.Warn("[ShowEnemyHP] Failed to write config: " + ex.Message);
			}
		}
	}
	internal static class ModCore
	{
		public static Dictionary<uint, EnemyUiContext> enemyUIContextsById = new Dictionary<uint, EnemyUiContext>();

		public static Dictionary<uint, Enemy> enemiesById = new Dictionary<uint, Enemy>();

		public static bool ShowUI = true;

		public static bool UseManualBillboard = false;

		public static void Init()
		{
			ModConfig.Init();
			HpFontCatalog.EnsureInitialized();
			LoggerBridge.Info("Show Enemy HP initialized! (F2 to toggle, Ctrl+R to reload config)");
			LoggerBridge.Info("Available color schemes: " + string.Join(", ", HpPalette.GetSchemeSummaries()));
			LoggerBridge.Info("Available fonts: " + string.Join(", ", HpFontCatalog.GetFontSummaries()));
		}

		public static void UpdateLoop()
		{
			if (Input.GetKeyDown((KeyCode)283))
			{
				SetGlobalVisibility(!ShowUI);
			}
			HandleHotReload();
		}

		public static void SetGlobalVisibility(bool visible)
		{
			ShowUI = visible;
			LoggerBridge.Info("Enemy HP UI " + (visible ? "enabled" : "disabled") + " (F2)");
			foreach (EnemyUiContext value2 in enemyUIContextsById.Values)
			{
				if ((Object)(object)value2?.Root != (Object)null)
				{
					value2.Root.SetActive(visible);
				}
			}
			if (!visible)
			{
				return;
			}
			foreach (KeyValuePair<uint, Enemy> item in enemiesById)
			{
				uint key = item.Key;
				Enemy value = item.Value;
				if (!((Object)(object)value == (Object)null))
				{
					EnemyUpdatePatch.UpdateEnemyUI(value, key);
				}
			}
		}

		public static void CleanupEnemyUI(uint enemyId)
		{
			if (enemyUIContextsById.TryGetValue(enemyId, out var value))
			{
				if ((Object)(object)value?.Root != (Object)null)
				{
					Object.Destroy((Object)(object)value.Root);
				}
				enemyUIContextsById.Remove(enemyId);
			}
		}

		public static void ApplyConfigToAllUI()
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			TMP_FontAsset fontAsset = HpFontCatalog.GetFontAsset(ModConfig.FontName);
			foreach (EnemyUiContext value in enemyUIContextsById.Values)
			{
				if (value != null)
				{
					if ((Object)(object)value.Root != (Object)null)
					{
						value.Root.transform.localScale = Vector3.one * ModConfig.Scale;
					}
					ApplyDisplayModeToContext(value);
					if ((Object)(object)value.NumericText != (Object)null)
					{
						((TMP_Text)value.NumericText).font = fontAsset;
						((TMP_Text)value.NumericText).ForceMeshUpdate(false, false);
					}
				}
			}
			foreach (KeyValuePair<uint, Enemy> item in enemiesById)
			{
				if (!((Object)(object)item.Value == (Object)null))
				{
					EnemyUpdatePatch.UpdateEnemyUI(item.Value, item.Key);
				}
			}
		}

		public static void ApplyDisplayModeToContext(EnemyUiContext context)
		{
			if (context != null)
			{
				bool flag = ModConfig.DisplayMode == HpDisplayMode.Numeric;
				if ((Object)(object)context.NumericRoot != (Object)null)
				{
					context.NumericRoot.SetActive(flag);
				}
				if ((Object)(object)context.BarRoot != (Object)null)
				{
					context.BarRoot.SetActive(!flag);
				}
			}
		}

		private static void HandleHotReload()
		{
			if ((Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305)) && Input.GetKeyDown((KeyCode)114) && ModConfig.ReloadFromDisk())
			{
				ApplyConfigToAllUI();
			}
		}
	}
	internal static class EnemyHpUtil
	{
		public static float GetHealthPercent(Enemy enemy, float currentHp)
		{
			float hpRatio = enemy.GetHpRatio();
			if (hpRatio > 0f && hpRatio <= 1.001f)
			{
				return Mathf.Clamp01(hpRatio);
			}
			float num = enemy.maxHp;
			if (num <= 1f && (Object)(object)enemy.enemyData != (Object)null)
			{
				num = enemy.enemyData.hp;
			}
			if (num <= 0f)
			{
				num = 1f;
			}
			return Mathf.Clamp01(currentHp / num);
		}

		public static string FormatHealthValue(float hp)
		{
			float num = Mathf.Abs(hp);
			if (num < 1000f)
			{
				return Mathf.RoundToInt(hp).ToString();
			}
			if (!(num < 10000f))
			{
				if (!(num < 100000f))
				{
					if (!(num < 1000000f))
					{
						if (!(num < 10000000f))
						{
							if (!(num < 100000000f))
							{
								return $"{Mathf.RoundToInt(hp / 1000000f)}M";
							}
							return $"{hp / 1000000f:0.0}M";
						}
						return $"{hp / 1000000f:0.00}M";
					}
					return $"{Mathf.RoundToInt(hp / 1000f)}K";
				}
				return $"{hp / 1000f:0.0}K";
			}
			return $"{hp / 1000f:0.00}K";
		}
	}
	[HarmonyPatch(typeof(Enemy), "InitEnemy")]
	internal static class EnemyInitPatch
	{
		public static void Postfix(Enemy __instance, uint id)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Expected O, but got Unknown
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			ModCore.enemiesById[id] = __instance;
			ModCore.CleanupEnemyUI(id);
			GameObject val = new GameObject("EnemyHPUI");
			val.transform.SetParent(((Component)__instance).transform, false);
			val.transform.localScale = Vector3.one * ModConfig.Scale;
			Canvas obj = val.AddComponent<Canvas>();
			obj.renderMode = (RenderMode)2;
			obj.sortingOrder = 100;
			((Component)obj).GetComponent<RectTransform>().sizeDelta = new Vector2(250f, 60f);
			try
			{
				LookAtCamera obj2 = val.AddComponent<LookAtCamera>();
				obj2.xzOnly = true;
				obj2.invert = true;
				ModCore.UseManualBillboard = false;
			}
			catch (Exception ex)
			{
				ModCore.UseManualBillboard = true;
				LoggerBridge.Warn("LookAtCamera not found; using manual billboard. (" + ex.Message + ")");
			}
			GameObject val2 = new GameObject("HPText");
			val2.transform.SetParent(val.transform, false);
			TextMeshProUGUI val3 = val2.AddComponent<TextMeshProUGUI>();
			((TMP_Text)val3).alignment = (TextAlignmentOptions)514;
			((TMP_Text)val3).fontSize = 32f;
			((TMP_Text)val3).font = HpFontCatalog.GetFontAsset(ModConfig.FontName);
			((Graphic)val3).color = HpPalette.FromPercent(1f);
			((TMP_Text)val3).text = "100";
			GameObject barRoot;
			RectTransform fillRect;
			float barFullWidth;
			Image barFill = CreateHpBar(val, out barRoot, out fillRect, out barFullWidth);
			if (!ModCore.UseManualBillboard)
			{
				val2.transform.localRotation = Quaternion.Euler(0f, 180f, 0f);
				if ((Object)(object)barRoot != (Object)null)
				{
					barRoot.transform.localRotation = Quaternion.Euler(0f, 180f, 0f);
				}
			}
			val.transform.position = __instance.GetHeadPosition() + new Vector3(0f, ModConfig.VerticalOffset, 0f);
			EnemyUiContext enemyUiContext = new EnemyUiContext(val, val2, val3, barRoot, barFill, fillRect, barFullWidth);
			ModCore.enemyUIContextsById[id] = enemyUiContext;
			ModCore.ApplyDisplayModeToContext(enemyUiContext);
			val.SetActive(ModCore.ShowUI);
			if (ModCore.ShowUI)
			{
				EnemyUpdatePatch.UpdateEnemyUI(__instance, id);
			}
		}

		private static Image CreateHpBar(GameObject parent, out GameObject barRoot, out RectTransform fillRect, out float barFullWidth)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: 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_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Expected O, but got Unknown
			//IL_00e2: 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_010e: 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)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			barRoot = new GameObject("HPBar");
			barRoot.transform.SetParent(parent.transform, false);
			RectTransform obj = barRoot.AddComponent<RectTransform>();
			obj.anchorMin = new Vector2(0.5f, 0.5f);
			obj.anchorMax = new Vector2(0.5f, 0.5f);
			obj.pivot = new Vector2(0.5f, 0.5f);
			obj.sizeDelta = new Vector2(60f, 8f);
			obj.anchoredPosition = Vector2.zero;
			Image obj2 = barRoot.AddComponent<Image>();
			((Graphic)obj2).color = new Color(0f, 0f, 0f, 0.7f);
			((Graphic)obj2).raycastTarget = false;
			GameObject val = new GameObject("Fill");
			val.transform.SetParent(barRoot.transform, false);
			fillRect = val.AddComponent<RectTransform>();
			fillRect.anchorMin = new Vector2(0.5f, 0.5f);
			fillRect.anchorMax = new Vector2(0.5f, 0.5f);
			fillRect.pivot = new Vector2(0f, 0.5f);
			barFullWidth = Mathf.Max(0f, 59.6f);
			fillRect.sizeDelta = new Vector2(barFullWidth, 7.6f);
			fillRect.anchoredPosition = new Vector2(-29.8f, 0f);
			Image obj3 = val.AddComponent<Image>();
			((Graphic)obj3).color = HpPalette.FromPercent(1f);
			((Graphic)obj3).raycastTarget = false;
			return obj3;
		}
	}
	[HarmonyPatch(typeof(Enemy), "Damage")]
	internal static class EnemyDamagePatch
	{
		public static void Postfix(Enemy __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				uint id = __instance.id;
				if (!ModCore.enemyUIContextsById.ContainsKey(id))
				{
					EnemyInitPatch.Postfix(__instance, id);
				}
				if (ModCore.ShowUI)
				{
					EnemyUpdatePatch.UpdateEnemyUI(__instance, id);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Enemy), "MyUpdate")]
	internal static class EnemyUpdatePatch
	{
		public static void Postfix(Enemy __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && ModCore.ShowUI && !(__instance.hp <= 0f))
			{
				UpdateEnemyUI(__instance, __instance.id);
			}
		}

		public static void UpdateEnemyUI(Enemy enemy, uint id)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: 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_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: 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_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			if (!ModCore.enemyUIContextsById.TryGetValue(id, out var value) || value == null)
			{
				return;
			}
			GameObject root = value.Root;
			if ((Object)(object)root == (Object)null || !ModCore.ShowUI)
			{
				return;
			}
			root.transform.localScale = Vector3.one * ModConfig.Scale;
			root.transform.position = enemy.GetHeadPosition() + new Vector3(0f, ModConfig.VerticalOffset, 0f);
			if (ModCore.UseManualBillboard)
			{
				Camera main = Camera.main;
				if ((Object)(object)main == (Object)null)
				{
					return;
				}
				root.transform.LookAt(((Component)main).transform);
				root.transform.Rotate(Vector3.up, 180f);
			}
			float hp = enemy.hp;
			if (hp <= 0f)
			{
				return;
			}
			float healthPercent = EnemyHpUtil.GetHealthPercent(enemy, hp);
			if (ModConfig.DisplayMode == HpDisplayMode.Numeric)
			{
				TextMeshProUGUI numericText = value.NumericText;
				if (!((Object)(object)numericText == (Object)null))
				{
					TMP_FontAsset fontAsset = HpFontCatalog.GetFontAsset(ModConfig.FontName);
					if ((Object)(object)((TMP_Text)numericText).font != (Object)(object)fontAsset)
					{
						((TMP_Text)numericText).font = fontAsset;
						((TMP_Text)numericText).ForceMeshUpdate(false, false);
					}
					((TMP_Text)numericText).text = EnemyHpUtil.FormatHealthValue(hp);
					((Graphic)numericText).color = HpPalette.FromPercent(healthPercent);
				}
			}
			else
			{
				Image barFill = value.BarFill;
				RectTransform barFillRect = value.BarFillRect;
				if (!((Object)(object)barFill == (Object)null) && !((Object)(object)barFillRect == (Object)null))
				{
					Vector2 sizeDelta = barFillRect.sizeDelta;
					sizeDelta.x = value.BarFullWidth * Mathf.Clamp01(healthPercent);
					barFillRect.sizeDelta = sizeDelta;
					((Graphic)barFill).color = HpPalette.FromPercent(healthPercent);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Enemy), "OnDestroy")]
	internal static class EnemyDestroyPatch
	{
		public static void Prefix(Enemy __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				ModCore.CleanupEnemyUI(__instance.id);
				ModCore.enemiesById.Remove(__instance.id);
			}
		}
	}
	[HarmonyPatch(typeof(EnemyManager), "RemoveEnemy")]
	internal static class EnemyManagerRemovePatch
	{
		public static void Postfix(EnemyManager __instance, Enemy enemy)
		{
			if (!((Object)(object)enemy == (Object)null))
			{
				ModCore.CleanupEnemyUI(enemy.id);
				ModCore.enemiesById.Remove(enemy.id);
			}
		}
	}
	internal static class PathsBridge
	{
		public static string UserDataDir => Paths.ConfigPath;

		public static string EnsureUserDataDir()
		{
			string userDataDir = UserDataDir;
			if (!Directory.Exists(userDataDir))
			{
				try
				{
					Directory.CreateDirectory(userDataDir);
				}
				catch
				{
				}
			}
			return userDataDir;
		}
	}
	[BepInPlugin("com.maskoliver.showenemyhp", "Show Enemy HP", "1.0.0")]
	public sealed class BepInEntry : BasePlugin
	{
		private sealed class UpdateDriver : MonoBehaviour
		{
			public UpdateDriver(IntPtr ptr)
				: base(ptr)
			{
			}

			private void Start()
			{
				try
				{
					ModCore.Init();
				}
				catch (Exception ex)
				{
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogError((object)ex);
					}
				}
			}

			private void Update()
			{
				ModCore.UpdateLoop();
			}

			private void OnDestroy()
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogInfo((object)"UpdateDriver destroyed");
				}
			}
		}

		private Harmony _harmony;

		internal static ManualLogSource Log;

		private static GameObject _host;

		public override void Load()
		{
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Expected O, but got Unknown
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Expected O, but got Unknown
			Log = Logger.CreateLogSource("ShowEnemyHp");
			LoggerBridge.Info = delegate(string s)
			{
				Log.LogInfo((object)s);
			};
			LoggerBridge.Warn = delegate(string s)
			{
				Log.LogWarning((object)s);
			};
			LoggerBridge.Error = delegate(string s)
			{
				Log.LogError((object)s);
			};
			ClassInjector.RegisterTypeInIl2Cpp<UpdateDriver>();
			_host = new GameObject("ShowEnemyHp_UpdateHost");
			Object.DontDestroyOnLoad((Object)(object)_host);
			((Object)_host).hideFlags = (HideFlags)61;
			_host.AddComponent<UpdateDriver>();
			_harmony = new Harmony("com.maskoliver.showenemyhp");
			_harmony.PatchAll();
			Log.LogInfo((object)"Show Enemy HP (BepInEx) loaded.");
		}
	}
}