Decompiled source of EnemyHealthBar v1.0.0

EnemyHealthBar.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using EnemyHealthBar.Config;
using EnemyHealthBar.Services;
using EnemyHealthBar.UI;
using EnemyHealthBar.Utilities;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[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: AssemblyVersion("0.0.0.0")]
[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 EnemyHealthBar
{
	[BepInPlugin("DarkSpider90.EnemyHealthBar", "EnemyHealthBar", "1.0.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string ModGuid = "DarkSpider90.EnemyHealthBar";

		public const string ModName = "EnemyHealthBar";

		public const string ModVersion = "1.0.0";

		private readonly Harmony _harmony = new Harmony("DarkSpider90.EnemyHealthBar");

		internal static ManualLogSource Log { get; private set; }

		internal static ModConfig Settings { get; private set; }

		private void Awake()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			Settings = new ModConfig(((BaseUnityPlugin)this).Config);
			_harmony.PatchAll();
			Log.LogInfo((object)"EnemyHealthBar by DarkSpider90 loaded. v1.0.0");
		}

		private void OnDestroy()
		{
			_harmony.UnpatchSelf();
		}
	}
}
namespace EnemyHealthBar.Utilities
{
	internal static class ColorUtils
	{
		internal static Color Parse(string htmlColor, Color fallback)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: 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)
			Color val = default(Color);
			return ColorUtility.TryParseHtmlString(htmlColor, ref val) ? val : fallback;
		}

		internal static Color WithAlpha(Color color, float alpha)
		{
			//IL_000e: 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_0012: Unknown result type (might be due to invalid IL or missing references)
			color.a = Mathf.Clamp01(alpha);
			return color;
		}
	}
}
namespace EnemyHealthBar.UI
{
	internal sealed class EnemyHealthBarDisplay : MonoBehaviour
	{
		private const float BoundsRefreshInterval = 0.65f;

		private EnemyHealth _enemyHealth;

		private GameObject _root;

		private RectTransform _canvasRect;

		private Image _foreground;

		private Image _background;

		private TextMeshProUGUI _healthText;

		private int _displayedCurrent;

		private int _displayedMax;

		private bool _hasHealthState;

		private float _targetFill = 1f;

		private float _smoothedFill = 1f;

		private float _lastRevealTime = -999f;

		private float _verticalBarOffset = 1.7f;

		private float _nextBoundsRefreshTime;

		private static Sprite _whiteSprite;

		private void Awake()
		{
			_enemyHealth = ((Component)this).GetComponent<EnemyHealth>();
		}

		private void Start()
		{
			if (Plugin.Settings.HealthBarEnabled.Value && !((Object)(object)_enemyHealth == (Object)null))
			{
				SetHealth(_enemyHealth.health, _enemyHealth.health, silent: true);
			}
		}

		private void LateUpdate()
		{
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: 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_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_enemyHealth == (Object)null)
			{
				DestroyRoot();
				Object.Destroy((Object)(object)this);
				return;
			}
			if (!Plugin.Settings.HealthBarEnabled.Value || !_hasHealthState || _displayedCurrent <= 0)
			{
				HideRoot();
				return;
			}
			if (!Plugin.Settings.AlwaysVisible.Value && Time.time - _lastRevealTime > Plugin.Settings.HideAfterSeconds.Value)
			{
				HideRoot();
				return;
			}
			EnsureCanvas();
			if ((Object)(object)_root == (Object)null || (Object)(object)_canvasRect == (Object)null)
			{
				return;
			}
			Vector3 val = CalculateTargetPosition();
			_root.transform.position = val;
			Camera main = Camera.main;
			if ((Object)(object)main != (Object)null)
			{
				_root.transform.rotation = ((Component)main).transform.rotation;
			}
			float num = CalculateVisibilityAlpha(val, main);
			if (num <= 0f)
			{
				HideRoot();
				return;
			}
			if (!_root.activeSelf)
			{
				_root.SetActive(true);
			}
			((Transform)_canvasRect).localScale = Vector3.one * Mathf.Max(0.001f, Plugin.Settings.BarScale.Value);
			RefreshVisuals(num);
		}

		internal void SetHealth(int current, int max, bool silent)
		{
			_displayedMax = Mathf.Max(1, max);
			_displayedCurrent = Mathf.Clamp(current, 0, _displayedMax);
			_hasHealthState = true;
			_targetFill = Mathf.Clamp01((float)_displayedCurrent / (float)_displayedMax);
			if (silent)
			{
				_smoothedFill = _targetFill;
			}
			else
			{
				Reveal();
			}
		}

		internal void ApplyClientDamage(int damage, int max)
		{
			int num = Mathf.Max(1, max);
			if (!_hasHealthState || _displayedMax != num)
			{
				_displayedMax = num;
				_displayedCurrent = num;
				_hasHealthState = true;
				_smoothedFill = 1f;
			}
			_displayedCurrent = Mathf.Clamp(_displayedCurrent - Mathf.Max(0, damage), 0, _displayedMax);
			_targetFill = Mathf.Clamp01((float)_displayedCurrent / (float)_displayedMax);
			Reveal();
		}

		internal void Reveal()
		{
			_lastRevealTime = Time.time;
			if ((Object)(object)_root != (Object)null && !_root.activeSelf)
			{
				_root.SetActive(true);
			}
		}

		internal void KillImmediately()
		{
			DestroyRoot();
			Object.Destroy((Object)(object)this);
		}

		private float CalculateVisibilityAlpha(Vector3 barPosition, Camera camera)
		{
			//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)
			float num = 1f;
			if (!Plugin.Settings.AlwaysVisible.Value)
			{
				float num2 = _lastRevealTime + Mathf.Max(0.1f, Plugin.Settings.HideAfterSeconds.Value);
				float num3 = num2 - Time.time;
				if (num3 <= 0f)
				{
					return 0f;
				}
				float num4 = Mathf.Max(0.01f, Plugin.Settings.HideFadeDuration.Value);
				if (num3 < num4)
				{
					num *= Mathf.Clamp01(num3 / num4);
				}
			}
			if ((Object)(object)camera != (Object)null)
			{
				float num5 = Mathf.Max(1f, Plugin.Settings.FadeDistance.Value);
				float num6 = Vector3.Distance(((Component)camera).transform.position, barPosition);
				num *= Mathf.Clamp01(1f - num6 / num5);
			}
			return num;
		}

		private Vector3 CalculateTargetPosition()
		{
			//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_003b: 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_0045: 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)
			Transform val = ResolveFollowAnchor();
			RefreshVerticalOffset(val);
			return val.position + Vector3.up * (_verticalBarOffset + Mathf.Max(0f, Plugin.Settings.ExtraHeightOffset.Value));
		}

		private Transform ResolveFollowAnchor()
		{
			Transform val = ResolveEnemyRootForBounds();
			if ((Object)(object)val != (Object)null)
			{
				EnemyRigidbody componentInChildren = ((Component)val).GetComponentInChildren<EnemyRigidbody>(true);
				if ((Object)(object)componentInChildren != (Object)null)
				{
					return ((Component)componentInChildren).transform;
				}
				Transform val2 = FindNamedDescendant(val, "Center");
				if ((Object)(object)val2 != (Object)null)
				{
					return val2;
				}
			}
			return ((Component)this).transform;
		}

		private void RefreshVerticalOffset(Transform anchor)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)anchor == (Object)null) && !(Time.time < _nextBoundsRefreshTime))
			{
				_nextBoundsRefreshTime = Time.time + 0.65f;
				Transform val = ResolveEnemyRootForBounds();
				if (!((Object)(object)val == (Object)null) && TryResolveTopY(val, out var topY))
				{
					_verticalBarOffset = Mathf.Clamp(topY - anchor.position.y + 0.2f, 0.4f, 16f);
				}
			}
		}

		private Transform ResolveEnemyRootForBounds()
		{
			if ((Object)(object)_enemyHealth != (Object)null && (Object)(object)((Component)_enemyHealth).transform != (Object)null && (Object)(object)((Component)_enemyHealth).transform.parent != (Object)null)
			{
				return ((Component)_enemyHealth).transform.parent;
			}
			return ((Component)this).transform;
		}

		private void EnsureCanvas()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			//IL_006a: 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_0099: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_root != (Object)null))
			{
				_root = new GameObject("EnemyHealthBar_View");
				Canvas val = _root.AddComponent<Canvas>();
				val.renderMode = (RenderMode)2;
				_canvasRect = ((Component)val).GetComponent<RectTransform>();
				_canvasRect.sizeDelta = new Vector2(Plugin.Settings.BarWidth.Value, Plugin.Settings.BarHeight.Value);
				((Transform)_canvasRect).localScale = Vector3.one * Mathf.Max(0.001f, Plugin.Settings.BarScale.Value);
				_background = CreateImage((Transform)(object)_canvasRect, "Background");
				Stretch(((Graphic)_background).rectTransform, 0f);
				_foreground = CreateImage((Transform)(object)_canvasRect, "Foreground");
				Stretch(((Graphic)_foreground).rectTransform, 3f);
				_foreground.type = (Type)3;
				_foreground.fillMethod = (FillMethod)0;
				_foreground.fillOrigin = 0;
				_foreground.fillAmount = _smoothedFill;
				CreateHealthText((Transform)(object)_canvasRect);
			}
		}

		private static Image CreateImage(Transform parent, string name)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected O, but got Unknown
			GameObject val = new GameObject(name);
			val.transform.SetParent(parent, false);
			Image val2 = val.AddComponent<Image>();
			val2.sprite = GetWhiteSprite();
			((Graphic)val2).raycastTarget = false;
			return val2;
		}

		private void CreateHealthText(Transform parent)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			GameObject val = new GameObject("HealthText");
			val.transform.SetParent(parent, false);
			_healthText = val.AddComponent<TextMeshProUGUI>();
			((TMP_Text)_healthText).alignment = (TextAlignmentOptions)514;
			((TMP_Text)_healthText).enableAutoSizing = false;
			((Graphic)_healthText).raycastTarget = false;
			RectTransform rectTransform = ((TMP_Text)_healthText).rectTransform;
			Stretch(rectTransform, 0f);
		}

		private static void Stretch(RectTransform rect, float inset)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			rect.anchorMin = Vector2.zero;
			rect.anchorMax = Vector2.one;
			rect.offsetMin = new Vector2(inset, inset);
			rect.offsetMax = new Vector2(0f - inset, 0f - inset);
		}

		private void RefreshVisuals(float alpha)
		{
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: 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_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_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_foreground == (Object)null) && !((Object)(object)_background == (Object)null))
			{
				_smoothedFill = Mathf.Lerp(_smoothedFill, _targetFill, Time.deltaTime * Mathf.Max(0.01f, Plugin.Settings.BarLerpSpeed.Value));
				_foreground.fillAmount = _smoothedFill;
				float num = ((_displayedMax > 0) ? Mathf.Clamp01((float)_displayedCurrent / (float)_displayedMax) : 0f);
				bool flag = num <= Plugin.Settings.LowHealthThreshold.Value;
				Color color = (flag ? ColorUtils.Parse(Plugin.Settings.LowHealthBarColor.Value, Color.yellow) : ColorUtils.Parse(Plugin.Settings.BarColor.Value, Color.red));
				Color val = ColorUtils.Parse(Plugin.Settings.BackgroundColor.Value, new Color(0f, 0f, 0f, 0.65f));
				Color val2 = (flag ? ColorUtils.Parse(Plugin.Settings.LowHealthTextColor.Value, Color.yellow) : ColorUtils.Parse(Plugin.Settings.TextColor.Value, Color.white));
				((Graphic)_foreground).color = ColorUtils.WithAlpha(color, alpha);
				((Graphic)_background).color = ColorUtils.WithAlpha(val, val.a * alpha);
				if (!((Object)(object)_healthText == (Object)null))
				{
					((Component)_healthText).gameObject.SetActive(Plugin.Settings.ShowHealthText.Value);
					((TMP_Text)_healthText).fontSize = Plugin.Settings.TextSize.Value;
					((Graphic)_healthText).color = ColorUtils.WithAlpha(val2, val2.a * alpha);
					((TMP_Text)_healthText).rectTransform.anchoredPosition = new Vector2(0f, Plugin.Settings.TextOffsetY.Value);
					((TMP_Text)_healthText).text = (Plugin.Settings.ShowMaxHealth.Value ? $"{_displayedCurrent} / {_displayedMax}" : _displayedCurrent.ToString());
				}
			}
		}

		private static Sprite GetWhiteSprite()
		{
			//IL_0037: 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)
			if ((Object)(object)_whiteSprite != (Object)null)
			{
				return _whiteSprite;
			}
			Texture2D whiteTexture = Texture2D.whiteTexture;
			_whiteSprite = Sprite.Create(whiteTexture, new Rect(0f, 0f, (float)((Texture)whiteTexture).width, (float)((Texture)whiteTexture).height), new Vector2(0.5f, 0.5f), 100f);
			return _whiteSprite;
		}

		private static bool TryResolveTopY(Transform root, out float topY)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: 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_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			topY = 0f;
			bool flag = false;
			Renderer[] componentsInChildren = ((Component)root).GetComponentsInChildren<Renderer>(false);
			Renderer[] array = componentsInChildren;
			Bounds bounds;
			foreach (Renderer val in array)
			{
				if (!((Object)(object)val == (Object)null) && !(((object)val).GetType().Name == "ParticleSystemRenderer"))
				{
					bounds = val.bounds;
					float y = ((Bounds)(ref bounds)).max.y;
					if (!flag || y > topY)
					{
						topY = y;
						flag = true;
					}
				}
			}
			if (flag)
			{
				return true;
			}
			Collider[] componentsInChildren2 = ((Component)root).GetComponentsInChildren<Collider>(false);
			Collider[] array2 = componentsInChildren2;
			foreach (Collider val2 in array2)
			{
				if (!((Object)(object)val2 == (Object)null) && !val2.isTrigger)
				{
					bounds = val2.bounds;
					float y2 = ((Bounds)(ref bounds)).max.y;
					if (!flag || y2 > topY)
					{
						topY = y2;
						flag = true;
					}
				}
			}
			return flag;
		}

		private static Transform FindNamedDescendant(Transform root, string namePart)
		{
			if ((Object)(object)root == (Object)null)
			{
				return null;
			}
			string value = namePart.ToLowerInvariant();
			Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren<Transform>(true);
			foreach (Transform val in componentsInChildren)
			{
				if ((Object)(object)val != (Object)null && ((Object)val).name.ToLowerInvariant().Contains(value))
				{
					return val;
				}
			}
			return null;
		}

		private void HideRoot()
		{
			if ((Object)(object)_root != (Object)null && _root.activeSelf)
			{
				_root.SetActive(false);
			}
		}

		private void DestroyRoot()
		{
			if ((Object)(object)_root != (Object)null)
			{
				Object.Destroy((Object)(object)_root);
				_root = null;
			}
		}

		private void OnDisable()
		{
			DestroyRoot();
		}

		private void OnDestroy()
		{
			DestroyRoot();
		}
	}
	internal sealed class FloatingDamageText : MonoBehaviour
	{
		private TextMeshProUGUI _text;

		private Vector3 _startWorldPosition;

		private float _age;

		private float _lifetime;

		private float _riseDistance;

		private float _fadeMultiplier;

		internal void Initialize(int damage, Vector3 position, DamageTextStyle style)
		{
			//IL_0002: 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_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Expected O, but got Unknown
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			_startWorldPosition = position + Random.insideUnitSphere * Plugin.Settings.DamageTextRandomOffset.Value;
			_lifetime = Mathf.Max(0.05f, style.Lifetime);
			_riseDistance = style.FloatSpeed;
			_fadeMultiplier = Mathf.Max(0.01f, style.FadeMultiplier);
			Canvas val = ((Component)this).gameObject.AddComponent<Canvas>();
			val.renderMode = (RenderMode)2;
			RectTransform component = ((Component)val).GetComponent<RectTransform>();
			component.sizeDelta = new Vector2(150f, 72f);
			((Transform)component).localScale = Vector3.one * style.CanvasScale;
			GameObject val2 = new GameObject("Text");
			val2.transform.SetParent((Transform)(object)component, false);
			_text = val2.AddComponent<TextMeshProUGUI>();
			((TMP_Text)_text).alignment = (TextAlignmentOptions)514;
			((TMP_Text)_text).enableAutoSizing = false;
			((TMP_Text)_text).fontStyle = (FontStyles)1;
			((Graphic)_text).raycastTarget = false;
			((TMP_Text)_text).text = FormatDamage(style.Format, damage);
			((Graphic)_text).color = style.Color;
			((TMP_Text)_text).fontSize = style.Size;
			RectTransform rectTransform = ((TMP_Text)_text).rectTransform;
			rectTransform.anchorMin = Vector2.zero;
			rectTransform.anchorMax = Vector2.one;
			rectTransform.offsetMin = Vector2.zero;
			rectTransform.offsetMax = Vector2.zero;
			((Component)this).transform.position = _startWorldPosition;
			FaceMainCamera();
		}

		private void LateUpdate()
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			_age += Time.deltaTime;
			float num = Mathf.Clamp01(_age / _lifetime);
			if (num >= 1f)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
				return;
			}
			((Component)this).transform.position = _startWorldPosition + Vector3.up * (num * _riseDistance);
			FaceMainCamera();
			if ((Object)(object)_text != (Object)null)
			{
				((TMP_Text)_text).alpha = Mathf.Clamp01((1f - num * num) * _fadeMultiplier);
			}
		}

		private static string FormatDamage(string format, int damage)
		{
			if (string.IsNullOrWhiteSpace(format))
			{
				format = "-{damage}";
			}
			return format.Replace("{damage}", damage.ToString());
		}

		private void FaceMainCamera()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			Camera main = Camera.main;
			if ((Object)(object)main != (Object)null)
			{
				((Component)this).transform.rotation = ((Component)main).transform.rotation;
			}
		}
	}
}
namespace EnemyHealthBar.Services
{
	internal static class DamageTextSpawner
	{
		internal static void Spawn(Vector3 worldPosition, int damage)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.Settings.DamageTextEnabled.Value)
			{
				DamageTextStyle style = DamageTextStyle.FromDamage(damage);
				GameObject val = new GameObject("EnemyHealthBar_DamageText");
				val.AddComponent<FloatingDamageText>().Initialize(damage, worldPosition, style);
			}
		}
	}
	internal readonly struct DamageTextStyle
	{
		internal readonly Color Color;

		internal readonly float Size;

		internal readonly float Lifetime;

		internal readonly float FloatSpeed;

		internal readonly float FadeMultiplier;

		internal readonly float CanvasScale;

		internal readonly string Format;

		private DamageTextStyle(Color color, float size, float lifetime, float floatSpeed, float fadeMultiplier, float canvasScale, string format)
		{
			//IL_0002: 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)
			Color = color;
			Size = size;
			Lifetime = lifetime;
			FloatSpeed = floatSpeed;
			FadeMultiplier = fadeMultiplier;
			CanvasScale = canvasScale;
			Format = format;
		}

		internal static DamageTextStyle FromDamage(int damage)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_00d3: Unknown result type (might be due to invalid IL or missing references)
			ModConfig settings = Plugin.Settings;
			Color color = ColorUtils.Parse(settings.DamageTextColor.Value, new Color(1f, 0.6f, 0.18f));
			float value = settings.DamageTextSize.Value;
			if (settings.UseDamageTiers.Value)
			{
				if (damage >= settings.CriticalDamageThreshold.Value)
				{
					color = ColorUtils.Parse(settings.CriticalDamageTextColor.Value, new Color(1f, 0.31f, 0.27f));
					value = settings.CriticalDamageTextSize.Value;
				}
				else if (damage >= settings.MediumDamageThreshold.Value)
				{
					color = ColorUtils.Parse(settings.MediumDamageTextColor.Value, new Color(1f, 0.83f, 0.35f));
					value = settings.MediumDamageTextSize.Value;
				}
			}
			return new DamageTextStyle(color, value, Mathf.Max(0.05f, settings.DamageTextLifetime.Value), settings.DamageTextFloatSpeed.Value, settings.DamageTextFadeMultiplier.Value, Mathf.Max(0.001f, settings.DamageTextScale.Value), settings.DamageTextFormat.Value);
		}
	}
}
namespace EnemyHealthBar.Patches
{
	[HarmonyPatch]
	internal static class EnemyHealthPatches
	{
		private static readonly Dictionary<int, float> LastDamageTextTimeByEnemy = new Dictionary<int, float>();

		private static readonly FieldRef<EnemyHealth, int> HealthCurrentRef = AccessTools.FieldRefAccess<EnemyHealth, int>("healthCurrent");

		[HarmonyPostfix]
		[HarmonyPatch(typeof(EnemyHealth), "Awake")]
		private static void EnemyHealthAwakePostfix(EnemyHealth __instance)
		{
			try
			{
				EnsureHealthBar(__instance);
				if (Plugin.Settings.VerboseLogging.Value)
				{
					Plugin.Log.LogInfo((object)("Initialized health bar for " + ((Object)__instance).name));
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"EnemyHealthBar Awake patch failed: {arg}");
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(EnemyHealth), "OnSpawn")]
		private static void EnemyHealthOnSpawnPostfix(EnemyHealth __instance)
		{
			EnemyHealthBarDisplay enemyHealthBarDisplay = EnsureHealthBar(__instance);
			enemyHealthBarDisplay.SetHealth(__instance.health, __instance.health, silent: true);
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(EnemyHealth), "HurtRPC")]
		private static void EnemyHealthHurtRpcPostfix(EnemyHealth __instance, int _damage, Vector3 _hurtDirection)
		{
			if ((Object)(object)__instance == (Object)null || _damage <= 0)
			{
				return;
			}
			try
			{
				EnemyHealthBarDisplay enemyHealthBarDisplay = EnsureHealthBar(__instance);
				if (SemiFunc.IsMasterClientOrSingleplayer())
				{
					enemyHealthBarDisplay.SetHealth(HealthCurrentRef.Invoke(__instance), __instance.health, silent: false);
				}
				else
				{
					enemyHealthBarDisplay.ApplyClientDamage(_damage, __instance.health);
				}
				SpawnDamageText(__instance, _damage);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"EnemyHealthBar HurtRPC patch failed: {arg}");
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(EnemyHealth), "DeathRPC")]
		private static void EnemyHealthDeathRpcPostfix(EnemyHealth __instance)
		{
			DestroyHealthBar(__instance);
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(EnemyHealth), "Death")]
		private static void EnemyHealthDeathPostfix(EnemyHealth __instance)
		{
			DestroyHealthBar(__instance);
		}

		private static EnemyHealthBarDisplay EnsureHealthBar(EnemyHealth enemyHealth)
		{
			EnemyHealthBarDisplay enemyHealthBarDisplay = ((Component)enemyHealth).GetComponent<EnemyHealthBarDisplay>();
			if ((Object)(object)enemyHealthBarDisplay == (Object)null)
			{
				enemyHealthBarDisplay = ((Component)enemyHealth).gameObject.AddComponent<EnemyHealthBarDisplay>();
			}
			return enemyHealthBarDisplay;
		}

		private static void DestroyHealthBar(EnemyHealth enemyHealth)
		{
			if (!((Object)(object)enemyHealth == (Object)null))
			{
				LastDamageTextTimeByEnemy.Remove(((Object)enemyHealth).GetInstanceID());
				EnemyHealthBarDisplay component = ((Component)enemyHealth).GetComponent<EnemyHealthBarDisplay>();
				if ((Object)(object)component != (Object)null)
				{
					component.KillImmediately();
				}
			}
		}

		private static void SpawnDamageText(EnemyHealth enemyHealth, int damage)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: 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_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.Settings.DamageTextEnabled.Value)
			{
				int instanceID = ((Object)enemyHealth).GetInstanceID();
				float time = Time.time;
				if (!LastDamageTextTimeByEnemy.TryGetValue(instanceID, out var value) || !(time - value < 0.05f))
				{
					LastDamageTextTimeByEnemy[instanceID] = time;
					Vector3 position = ((Component)enemyHealth).transform.position;
					Vector3 worldPosition = position + Vector3.up * Plugin.Settings.DamageTextHeightOffset.Value;
					DamageTextSpawner.Spawn(worldPosition, Mathf.Clamp(damage, 1, 9999));
				}
			}
		}
	}
}
namespace EnemyHealthBar.Config
{
	internal sealed class ModConfig
	{
		internal readonly ConfigEntry<bool> HealthBarEnabled;

		internal readonly ConfigEntry<bool> AlwaysVisible;

		internal readonly ConfigEntry<bool> ShowHealthText;

		internal readonly ConfigEntry<bool> ShowMaxHealth;

		internal readonly ConfigEntry<float> BarWidth;

		internal readonly ConfigEntry<float> BarHeight;

		internal readonly ConfigEntry<float> BarScale;

		internal readonly ConfigEntry<float> BarLerpSpeed;

		internal readonly ConfigEntry<float> ExtraHeightOffset;

		internal readonly ConfigEntry<float> HideAfterSeconds;

		internal readonly ConfigEntry<float> HideFadeDuration;

		internal readonly ConfigEntry<float> FadeDistance;

		internal readonly ConfigEntry<string> BarColor;

		internal readonly ConfigEntry<string> LowHealthBarColor;

		internal readonly ConfigEntry<string> BackgroundColor;

		internal readonly ConfigEntry<string> TextColor;

		internal readonly ConfigEntry<string> LowHealthTextColor;

		internal readonly ConfigEntry<float> LowHealthThreshold;

		internal readonly ConfigEntry<float> TextSize;

		internal readonly ConfigEntry<float> TextOffsetY;

		internal readonly ConfigEntry<bool> DamageTextEnabled;

		internal readonly ConfigEntry<string> DamageTextFormat;

		internal readonly ConfigEntry<bool> UseDamageTiers;

		internal readonly ConfigEntry<float> DamageTextHeightOffset;

		internal readonly ConfigEntry<float> DamageTextRandomOffset;

		internal readonly ConfigEntry<float> DamageTextLifetime;

		internal readonly ConfigEntry<float> DamageTextFloatSpeed;

		internal readonly ConfigEntry<float> DamageTextFadeMultiplier;

		internal readonly ConfigEntry<float> DamageTextScale;

		internal readonly ConfigEntry<string> DamageTextColor;

		internal readonly ConfigEntry<float> DamageTextSize;

		internal readonly ConfigEntry<int> MediumDamageThreshold;

		internal readonly ConfigEntry<string> MediumDamageTextColor;

		internal readonly ConfigEntry<float> MediumDamageTextSize;

		internal readonly ConfigEntry<int> CriticalDamageThreshold;

		internal readonly ConfigEntry<string> CriticalDamageTextColor;

		internal readonly ConfigEntry<float> CriticalDamageTextSize;

		internal readonly ConfigEntry<bool> VerboseLogging;

		internal ModConfig(ConfigFile config)
		{
			HealthBarEnabled = config.Bind<bool>("Health Bar", "Enabled", true, "Show enemy health bars.");
			AlwaysVisible = config.Bind<bool>("Health Bar", "AlwaysVisible", false, "Always show health bars, even without recent damage.");
			ShowHealthText = config.Bind<bool>("Health Bar", "ShowHealthText", true, "Show health numbers above the bar.");
			ShowMaxHealth = config.Bind<bool>("Health Bar", "ShowMaxHealth", true, "Show current/max health instead of current health only.");
			BarWidth = config.Bind<float>("Health Bar", "BarWidth", 210f, "World-space health bar canvas width.");
			BarHeight = config.Bind<float>("Health Bar", "BarHeight", 28f, "World-space health bar canvas height.");
			BarScale = config.Bind<float>("Health Bar", "BarScale", 0.0068f, "World-space health bar scale.");
			BarLerpSpeed = config.Bind<float>("Health Bar", "BarLerpSpeed", 11f, "Health bar fill smoothing speed.");
			ExtraHeightOffset = config.Bind<float>("Health Bar", "ExtraHeightOffset", 0.62f, "Extra height above enemy after automatic height calculation.");
			HideAfterSeconds = config.Bind<float>("Health Bar", "HideAfterSeconds", 8f, "Hide health bar after this many seconds without damage.");
			HideFadeDuration = config.Bind<float>("Health Bar", "HideFadeDuration", 0.45f, "Fade-out duration before the bar disappears.");
			FadeDistance = config.Bind<float>("Health Bar", "FadeDistance", 48f, "Distance where health bar fully fades out.");
			BarColor = config.Bind<string>("Health Bar", "BarColor", "#E2372F", "Health bar color in HTML format.");
			LowHealthBarColor = config.Bind<string>("Health Bar", "LowHealthBarColor", "#FFD447", "Health bar color below low health threshold.");
			BackgroundColor = config.Bind<string>("Health Bar", "BackgroundColor", "#16191ECC", "Health bar background color in HTML format.");
			TextColor = config.Bind<string>("Health Bar", "TextColor", "#F7F8FA", "Health text color in HTML format.");
			LowHealthTextColor = config.Bind<string>("Health Bar", "LowHealthTextColor", "#FFD447", "Health text color below low health threshold.");
			LowHealthThreshold = config.Bind<float>("Health Bar", "LowHealthThreshold", 0.3f, "Low health threshold from 0 to 1.");
			TextSize = config.Bind<float>("Health Bar", "TextSize", 14f, "Health text size.");
			TextOffsetY = config.Bind<float>("Health Bar", "TextOffsetY", 0f, "Health text vertical offset.");
			DamageTextEnabled = config.Bind<bool>("Damage Text", "Enabled", true, "Show floating damage numbers.");
			DamageTextFormat = config.Bind<string>("Damage Text", "TextFormat", "-{damage}", "Damage text format. Use {damage} as placeholder.");
			UseDamageTiers = config.Bind<bool>("Damage Text", "UseDamageTiers", true, "Use separate colors/sizes for medium and critical damage.");
			DamageTextHeightOffset = config.Bind<float>("Damage Text", "HeightOffset", 1.65f, "Damage text height above enemy transform.");
			DamageTextRandomOffset = config.Bind<float>("Damage Text", "RandomOffsetRadius", 0.28f, "Random offset radius for damage text.");
			DamageTextLifetime = config.Bind<float>("Damage Text", "Lifetime", 1.05f, "Damage text lifetime.");
			DamageTextFloatSpeed = config.Bind<float>("Damage Text", "FloatSpeed", 1.25f, "Damage text upward movement distance.");
			DamageTextFadeMultiplier = config.Bind<float>("Damage Text", "FadeMultiplier", 1f, "Damage text fade multiplier.");
			DamageTextScale = config.Bind<float>("Damage Text", "Scale", 0.0105f, "World-space damage text canvas scale.");
			DamageTextColor = config.Bind<string>("Damage Text", "Color", "#FF9A2F", "Default damage text color.");
			DamageTextSize = config.Bind<float>("Damage Text", "Size", 30f, "Default damage text size.");
			MediumDamageThreshold = config.Bind<int>("Damage Text", "MediumDamageThreshold", 30, "Minimum damage for medium damage text style.");
			MediumDamageTextColor = config.Bind<string>("Damage Text", "MediumColor", "#FFD35A", "Medium damage text color.");
			MediumDamageTextSize = config.Bind<float>("Damage Text", "MediumSize", 35f, "Medium damage text size.");
			CriticalDamageThreshold = config.Bind<int>("Damage Text", "CriticalDamageThreshold", 60, "Minimum damage for critical damage text style.");
			CriticalDamageTextColor = config.Bind<string>("Damage Text", "CriticalColor", "#FF4E45", "Critical damage text color.");
			CriticalDamageTextSize = config.Bind<float>("Damage Text", "CriticalSize", 42f, "Critical damage text size.");
			VerboseLogging = config.Bind<bool>("Debug", "VerboseLogging", false, "Enable verbose logs.");
		}
	}
}