Decompiled source of EnemyHealthBarMod v0.1.0

EnemyHealthBarMod.dll

Decompiled 17 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using Assets.Scripts.Actors.Enemies;
using Assets.Scripts.Managers;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Microsoft.CodeAnalysis;
using ModSettingsTabMod;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("EnemyHealthBarMod")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+5f9babd392f363ec169e9d5b09860072e6cd82aa")]
[assembly: AssemblyProduct("Enemy Health Bar Mod")]
[assembly: AssemblyTitle("EnemyHealthBarMod")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.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;
		}
	}
}
internal static class MyPluginInfo
{
	public const string PLUGIN_GUID = "com.megabonk.enemyhealthbarmod";

	public const string PLUGIN_NAME = "EnemyHealthBarMod";

	public const string PLUGIN_VERSION = "1.0.0";
}
namespace EnemyHealthBarMod
{
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "EnemyHealthBarMod";

		public const string PLUGIN_NAME = "Enemy Health Bar Mod";

		public const string PLUGIN_VERSION = "0.1.0";
	}
}
namespace EnemyHealthBarModBepinex
{
	public static class HealthBarManager
	{
		private static readonly Dictionary<uint, HealthBarUI> healthBarsByEnemyId = new Dictionary<uint, HealthBarUI>();

		private static readonly List<HealthBarUI> activeBarList = new List<HealthBarUI>();

		private static readonly List<uint> removalList = new List<uint>();

		private static readonly Dictionary<uint, Enemy> trackedEnemies = new Dictionary<uint, Enemy>();

		public static readonly Dictionary<string, string> BoneNameCache = new Dictionary<string, string>();

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

		private static readonly Queue<(Enemy enemy, uint id)> creationQueue = new Queue<(Enemy, uint)>();

		private static readonly HashSet<uint> queuedIds = new HashSet<uint>();

		private static Camera cachedCamera;

		private static Vector3 lastCameraPosition;

		private static int frameCounter = 0;

		private static float sqrSkipUpdateDistance = 0f;

		private static float sqrFadeEndDistance = 0f;

		private static bool distanceSettingsDirty = true;

		private static int culledBarCount = 0;

		private const int HIGH_BAR_COUNT_THRESHOLD = 75;

		private const float TARGET_FRAME_TIME = 1f / 60f;

		private static float lastUpdateTime = 0f;

		private static bool isThrottling = false;

		private static bool isSkipFrame = false;

		private const int PRIORITY_POOL_SIZE = 75;

		private const float PRIORITY_POOL_UPDATE_INTERVAL = 0.2f;

		private static readonly HashSet<uint> priorityPoolIds = new HashSet<uint>();

		private static readonly List<(uint id, float sqrDist)> distanceSortList = new List<(uint, float)>();

		private static float lastPriorityPoolUpdateTime = 0f;

		private static readonly HashSet<uint> damagedBarIds = new HashSet<uint>();

		private static readonly float[] frustumNormalsX = new float[6];

		private static readonly float[] frustumNormalsY = new float[6];

		private static readonly float[] frustumNormalsZ = new float[6];

		private static readonly float[] frustumDistances = new float[6];

		private static bool frustumPlanesValid = false;

		private static Vector3 lastFrustumCameraPos;

		private static Quaternion lastFrustumCameraRot;

		private static Quaternion cachedBillboardRotation;

		private static bool billboardRotationValid = false;

		private static readonly Stack<HealthBarUI> healthBarPool = new Stack<HealthBarUI>();

		private static bool poolPreallocated = false;

		private const int SIMD_BATCH_THRESHOLD = 32;

		private static float[] positionXArray = new float[128];

		private static float[] positionYArray = new float[128];

		private static float[] positionZArray = new float[128];

		private static float[] sqrDistanceArray = new float[128];

		private static Type cachedUiManagerType;

		private static PropertyInfo cachedUiManagerInstanceProperty;

		private static PropertyInfo cachedPauseProperty;

		private static PropertyInfo cachedPauseCurrentProperty;

		public static GameObject cachedEncounterWindowsObject;

		public static GameObject cachedLevelupChild;

		public static GameObject cachedEncounterChild;

		public static GameObject cachedChestChild;

		private static Type cachedGameManagerType;

		private static PropertyInfo cachedGameManagerInstanceProperty;

		private static FieldInfo cachedCutsceneField;

		private static PropertyInfo cachedIsGameOverProperty;

		private static MethodInfo cachedIsTimeFreezeMethod;

		private static Type cachedLevelupScreenType;

		private static FieldInfo cachedIsLevelingUpField;

		private static Func<object> getGameManagerInstance;

		private static Func<object, bool> getCutsceneValue;

		private static Func<object, bool> getIsGameOverValue;

		private static Func<object, bool> invokeIsTimeFreeze;

		private static Func<object> getUiManagerInstance;

		private static Func<object, object> getPauseValue;

		private static Func<object, object> getPauseCurrentValue;

		private static bool delegatesCompiled = false;

		public static bool IsPaused { get; private set; }

		public static Camera CachedCamera => cachedCamera;

		public static Quaternion BillboardRotation => cachedBillboardRotation;

		public static bool HasValidBillboard => billboardRotationValid;

		public static float GetEnemyYOffsetOverride(string enemyTypeName)
		{
			return 0f;
		}

		public static bool UsesMeshHeightPositioning(string enemyTypeName)
		{
			return string.Equals(enemyTypeName, "Bee", StringComparison.OrdinalIgnoreCase);
		}

		public static float GetEnemyMeshHeight(Enemy enemy)
		{
			if ((Object)(object)enemy == (Object)null)
			{
				return -1f;
			}
			try
			{
				Type type = ((object)enemy).GetType();
				PropertyInfo property = type.GetProperty("meshHeight", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null && property.CanRead && property.GetValue(enemy) is float result)
				{
					return result;
				}
				FieldInfo field = type.GetField("_meshHeight_k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);
				if (field != null && field.GetValue(enemy) is float result2)
				{
					return result2;
				}
				field = type.GetField("meshHeight", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					object value = field.GetValue(enemy);
					if (value is float)
					{
						return (float)value;
					}
				}
			}
			catch
			{
			}
			return -1f;
		}

		public static void CreateHealthBar(Enemy enemy, uint id)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			if ((Object)(object)enemy == (Object)null)
			{
				return;
			}
			trackedEnemies[id] = enemy;
			if (Plugin.EnableDebugLogs.Value)
			{
				string enemyTypeName = GetEnemyTypeName(enemy);
				if (loggedEnemyNames.Add(enemyTypeName))
				{
					ManualLogSource log = Plugin.Log;
					bool flag = default(bool);
					BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(52, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[EnemyDiscovery] New enemy type: \"");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(enemyTypeName);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("\" (GameObject: \"");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(((Object)enemy).name);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("\")");
					}
					log.LogInfo(val);
				}
			}
			if (!healthBarsByEnemyId.ContainsKey(id) && !queuedIds.Contains(id))
			{
				creationQueue.Enqueue((enemy, id));
				queuedIds.Add(id);
			}
		}

		private static void ProcessCreationQueue()
		{
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Expected O, but got Unknown
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			int value = Plugin.MaxBarsPerFrame.Value;
			bool flag = default(bool);
			while (creationQueue.Count > 0 && num < value)
			{
				var (val, num2) = creationQueue.Dequeue();
				queuedIds.Remove(num2);
				if ((Object)(object)val == (Object)null || val.hp <= 0f)
				{
					num++;
					continue;
				}
				try
				{
					HealthBarUI pooledHealthBarUI = GetPooledHealthBarUI(val, num2);
					if (billboardRotationValid)
					{
						pooledHealthBarUI.ApplyBillboardRotation(cachedBillboardRotation);
					}
					healthBarsByEnemyId[num2] = pooledHealthBarUI;
					activeBarList.Add(pooledHealthBarUI);
				}
				catch (Exception ex)
				{
					ManualLogSource log = Plugin.Log;
					BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(40, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Failed to create health bar for enemy ");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<uint>(num2);
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(": ");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message);
					}
					log.LogWarning(val2);
				}
				num++;
			}
		}

		public static void UpdateHealthBar(Enemy enemy, uint id)
		{
			if (!Plugin.ShowHealthBars.Value || (Object)(object)enemy == (Object)null || enemy.hp <= 0f)
			{
				return;
			}
			trackedEnemies[id] = enemy;
			damagedBarIds.Add(id);
			if (!healthBarsByEnemyId.TryGetValue(id, out var value))
			{
				if (!queuedIds.Contains(id))
				{
					CreateHealthBar(enemy, id);
				}
			}
			else
			{
				value?.Update(enemy);
			}
		}

		public static void UpdateAllHealthBars()
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			frameCounter++;
			int count = activeBarList.Count;
			isThrottling = count > 75;
			isSkipFrame = false;
			if (isThrottling)
			{
				float unscaledTime = Time.unscaledTime;
				if (unscaledTime - lastUpdateTime < 1f / 60f)
				{
					isSkipFrame = true;
				}
				else
				{
					lastUpdateTime = unscaledTime;
				}
			}
			UpdateCameraCache();
			UpdatePauseState();
			if ((Object)(object)cachedCamera != (Object)null)
			{
				cachedBillboardRotation = Quaternion.LookRotation(((Component)cachedCamera).transform.forward);
				billboardRotationValid = true;
			}
			else
			{
				billboardRotationValid = false;
			}
			ProcessCreationQueue();
			removalList.Clear();
			culledBarCount = 0;
			int updateFrequencyFar = (isThrottling ? Math.Max(Plugin.UpdateFrequencyFar.Value, 5) : Plugin.UpdateFrequencyFar.Value);
			if (distanceSettingsDirty)
			{
				float num = Plugin.SkipUpdateDistance.Value;
				sqrSkipUpdateDistance = num * num;
				float num2 = Plugin.FadeEndDistance.Value;
				sqrFadeEndDistance = num2 * num2;
				distanceSettingsDirty = false;
			}
			Vector3 camPos = (((Object)(object)cachedCamera != (Object)null) ? ((Component)cachedCamera).transform.position : Vector3.zero);
			bool flag = (Object)(object)cachedCamera != (Object)null;
			if (isThrottling && flag)
			{
				UpdatePriorityPool(camPos);
			}
			if (flag && count >= 32)
			{
				UpdateAllHealthBarsSimd(count, camPos, updateFrequencyFar);
			}
			else
			{
				UpdateAllHealthBarsScalar(count, camPos, flag, updateFrequencyFar);
			}
			for (int i = 0; i < removalList.Count; i++)
			{
				RemoveHealthBar(removalList[i]);
			}
		}

		private static void UpdatePriorityPool(Vector3 camPos)
		{
			//IL_0044: 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_004b: 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_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: 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_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Expected O, but got Unknown
			float unscaledTime = Time.unscaledTime;
			if (unscaledTime - lastPriorityPoolUpdateTime < 0.2f)
			{
				return;
			}
			lastPriorityPoolUpdateTime = unscaledTime;
			distanceSortList.Clear();
			for (int i = 0; i < activeBarList.Count; i++)
			{
				HealthBarUI healthBarUI = activeBarList[i];
				if (healthBarUI != null && healthBarUI.IsValid())
				{
					Vector3 currentPosition = healthBarUI.CurrentPosition;
					if (!(currentPosition == Vector3.zero))
					{
						float num = currentPosition.x - camPos.x;
						float num2 = currentPosition.y - camPos.y;
						float num3 = currentPosition.z - camPos.z;
						float item = num * num + num2 * num2 + num3 * num3;
						distanceSortList.Add((healthBarUI.EnemyId, item));
					}
				}
			}
			distanceSortList.Sort(((uint id, float sqrDist) a, (uint id, float sqrDist) b) => a.sqrDist.CompareTo(b.sqrDist));
			priorityPoolIds.Clear();
			int num4 = Math.Min(75, distanceSortList.Count);
			for (int j = 0; j < num4; j++)
			{
				priorityPoolIds.Add(distanceSortList[j].id);
			}
			if (Plugin.EnableDebugLogs.Value)
			{
				ManualLogSource log = Plugin.Log;
				bool flag = default(bool);
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(51, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Priority pool updated: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num4);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" bars in pool, ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(distanceSortList.Count - num4);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" low priority");
				}
				log.LogInfo(val);
			}
		}

		private static void UpdateAllHealthBarsScalar(int count, Vector3 camPos, bool hasCamera, int updateFrequencyFar)
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: 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_01b9: 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)
			bool flag = hasCamera && frustumPlanesValid && Plugin.EnableFrustumCulling.Value;
			bool flag2 = frameCounter % 30 == 0;
			for (int i = 0; i < count; i++)
			{
				HealthBarUI healthBarUI = activeBarList[i];
				uint enemyId = healthBarUI.EnemyId;
				if (healthBarUI == null || !healthBarUI.IsValid())
				{
					removalList.Add(enemyId);
					continue;
				}
				trackedEnemies.TryGetValue(enemyId, out var value);
				Vector3 currentPosition = healthBarUI.CurrentPosition;
				bool flag3 = damagedBarIds.Contains(enemyId);
				bool flag4 = flag3 || flag2 || (Object)(object)value == (Object)null || value.hp <= 0f;
				if (flag && currentPosition != Vector3.zero && !IsInFrustum(currentPosition))
				{
					healthBarUI.SetCulled(culled: true);
					culledBarCount++;
					if (billboardRotationValid)
					{
						healthBarUI.ApplyBillboardRotation(cachedBillboardRotation);
					}
					if (flag4)
					{
						healthBarUI.Update(value);
						damagedBarIds.Remove(enemyId);
					}
					continue;
				}
				healthBarUI.SetCulled(culled: false);
				float num = -1f;
				if (hasCamera && currentPosition != Vector3.zero)
				{
					float num2 = currentPosition.x - camPos.x;
					float num3 = currentPosition.y - camPos.y;
					float num4 = currentPosition.z - camPos.z;
					num = num2 * num2 + num3 * num3 + num4 * num4;
				}
				healthBarUI.UpdateVisibility(num);
				if (isSkipFrame)
				{
					if ((Object)(object)value == (Object)null || value.hp <= 0f)
					{
						healthBarUI.Update(value, num);
						damagedBarIds.Remove(enemyId);
					}
					continue;
				}
				if (billboardRotationValid)
				{
					healthBarUI.ApplyBillboardRotation(cachedBillboardRotation);
				}
				if (hasCamera && num >= 0f)
				{
					if ((num > sqrSkipUpdateDistance && !flag4) || (num > sqrFadeEndDistance && !flag3 && frameCounter % updateFrequencyFar != 0))
					{
						continue;
					}
					if (isThrottling && !priorityPoolIds.Contains(enemyId) && !flag3)
					{
						if ((Object)(object)value == (Object)null || value.hp <= 0f)
						{
							healthBarUI.Update(value, num);
						}
						continue;
					}
				}
				healthBarUI.Update(value, num);
				healthBarUI.LateTick();
				damagedBarIds.Remove(enemyId);
			}
		}

		private static void UpdateAllHealthBarsSimd(int count, Vector3 camPos, int updateFrequencyFar)
		{
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			EnsureArrayCapacity(count);
			bool flag = frustumPlanesValid && Plugin.EnableFrustumCulling.Value;
			bool flag2 = frameCounter % 30 == 0;
			int num = 0;
			for (int i = 0; i < count; i++)
			{
				HealthBarUI healthBarUI = activeBarList[i];
				if (healthBarUI == null || !healthBarUI.IsValid())
				{
					removalList.Add(healthBarUI?.EnemyId ?? 0);
					positionXArray[i] = float.MaxValue;
					continue;
				}
				Vector3 currentPosition = healthBarUI.CurrentPosition;
				uint enemyId = healthBarUI.EnemyId;
				if (flag && currentPosition != Vector3.zero && !IsInFrustum(currentPosition))
				{
					healthBarUI.SetCulled(culled: true);
					culledBarCount++;
					if (billboardRotationValid)
					{
						healthBarUI.ApplyBillboardRotation(cachedBillboardRotation);
					}
					trackedEnemies.TryGetValue(enemyId, out var value);
					if (damagedBarIds.Contains(enemyId) || flag2 || (Object)(object)value == (Object)null || value.hp <= 0f)
					{
						healthBarUI.Update(value);
						damagedBarIds.Remove(enemyId);
					}
					positionXArray[i] = float.MaxValue;
				}
				else
				{
					positionXArray[i] = currentPosition.x;
					positionYArray[i] = currentPosition.y;
					positionZArray[i] = currentPosition.z;
					num++;
				}
			}
			BatchCalculateDistances(count, camPos.x, camPos.y, camPos.z);
			for (int j = 0; j < count; j++)
			{
				if (positionXArray[j] == float.MaxValue)
				{
					continue;
				}
				HealthBarUI healthBarUI2 = activeBarList[j];
				uint enemyId2 = healthBarUI2.EnemyId;
				trackedEnemies.TryGetValue(enemyId2, out var value2);
				float num2 = sqrDistanceArray[j];
				healthBarUI2.SetCulled(culled: false);
				bool flag3 = damagedBarIds.Contains(enemyId2);
				bool flag4 = flag3 || flag2 || (Object)(object)value2 == (Object)null || value2.hp <= 0f;
				healthBarUI2.UpdateVisibility(num2);
				if (isSkipFrame)
				{
					if ((Object)(object)value2 == (Object)null || value2.hp <= 0f)
					{
						healthBarUI2.Update(value2, num2);
						damagedBarIds.Remove(enemyId2);
					}
					continue;
				}
				if (billboardRotationValid)
				{
					healthBarUI2.ApplyBillboardRotation(cachedBillboardRotation);
				}
				if ((num2 > sqrSkipUpdateDistance && !flag4) || (num2 > sqrFadeEndDistance && !flag3 && frameCounter % updateFrequencyFar != 0))
				{
					continue;
				}
				if (isThrottling && !priorityPoolIds.Contains(enemyId2) && !flag3)
				{
					if ((Object)(object)value2 == (Object)null || value2.hp <= 0f)
					{
						healthBarUI2.Update(value2, num2);
					}
				}
				else
				{
					healthBarUI2.Update(value2, num2);
					healthBarUI2.LateTick();
					damagedBarIds.Remove(enemyId2);
				}
			}
		}

		private static void UpdateCameraCache()
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)cachedCamera == (Object)null || frameCounter % 30 == 0)
			{
				cachedCamera = Camera.main;
			}
			if ((Object)(object)cachedCamera != (Object)null)
			{
				Vector3 position = ((Component)cachedCamera).transform.position;
				if (Vector3.SqrMagnitude(position - lastCameraPosition) > 1f)
				{
					lastCameraPosition = position;
				}
				UpdateFrustumPlanes();
			}
			else
			{
				frustumPlanesValid = false;
			}
		}

		private static void UpdateFrustumPlanes()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: 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)
			//IL_002a: 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_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)cachedCamera == (Object)null)
			{
				frustumPlanesValid = false;
				return;
			}
			Transform transform = ((Component)cachedCamera).transform;
			Vector3 position = transform.position;
			Quaternion rotation = transform.rotation;
			bool flag = Vector3.SqrMagnitude(position - lastFrustumCameraPos) > 0.25f;
			bool flag2 = Quaternion.Angle(rotation, lastFrustumCameraRot) > 1f;
			if (!(!frustumPlanesValid || flag || flag2))
			{
				return;
			}
			try
			{
				ExtractFrustumPlanes(cachedCamera.projectionMatrix * cachedCamera.worldToCameraMatrix);
				lastFrustumCameraPos = position;
				lastFrustumCameraRot = rotation;
				frustumPlanesValid = true;
			}
			catch
			{
				frustumPlanesValid = false;
			}
		}

		private static void ExtractFrustumPlanes(Matrix4x4 vp)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: 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_00c4: 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_00d4: 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_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: 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_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: 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_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: 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_014e: 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_015e: 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_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: 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)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			float[] array = new float[6];
			float[] array2 = new float[6];
			float[] array3 = new float[6];
			float[] array4 = new float[6];
			array[0] = vp.m30 + vp.m00;
			array2[0] = vp.m31 + vp.m01;
			array3[0] = vp.m32 + vp.m02;
			array4[0] = vp.m33 + vp.m03;
			array[1] = vp.m30 - vp.m00;
			array2[1] = vp.m31 - vp.m01;
			array3[1] = vp.m32 - vp.m02;
			array4[1] = vp.m33 - vp.m03;
			array[2] = vp.m30 + vp.m10;
			array2[2] = vp.m31 + vp.m11;
			array3[2] = vp.m32 + vp.m12;
			array4[2] = vp.m33 + vp.m13;
			array[3] = vp.m30 - vp.m10;
			array2[3] = vp.m31 - vp.m11;
			array3[3] = vp.m32 - vp.m12;
			array4[3] = vp.m33 - vp.m13;
			array[4] = vp.m30 + vp.m20;
			array2[4] = vp.m31 + vp.m21;
			array3[4] = vp.m32 + vp.m22;
			array4[4] = vp.m33 + vp.m23;
			array[5] = vp.m30 - vp.m20;
			array2[5] = vp.m31 - vp.m21;
			array3[5] = vp.m32 - vp.m22;
			array4[5] = vp.m33 - vp.m23;
			for (int i = 0; i < 6; i++)
			{
				float num = Mathf.Sqrt(array[i] * array[i] + array2[i] * array2[i] + array3[i] * array3[i]);
				if (num > 0.0001f)
				{
					float num2 = 1f / num;
					frustumNormalsX[i] = array[i] * num2;
					frustumNormalsY[i] = array2[i] * num2;
					frustumNormalsZ[i] = array3[i] * num2;
					frustumDistances[i] = array4[i] * num2;
				}
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private static bool IsInFrustum(Vector3 position)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			if (!frustumPlanesValid)
			{
				return true;
			}
			float x = position.x;
			float y = position.y;
			float z = position.z;
			for (int i = 0; i < 6; i++)
			{
				float num = frustumNormalsX[i];
				float num2 = frustumNormalsY[i];
				float num3 = frustumNormalsZ[i];
				if (num * ((num >= 0f) ? (x + 1f) : (x - 1f)) + num2 * ((num2 >= 0f) ? (y + 3f) : (y - 3f)) + num3 * ((num3 >= 0f) ? (z + 1f) : (z - 1f)) + frustumDistances[i] < 0f)
				{
					return false;
				}
			}
			return true;
		}

		[MethodImpl(MethodImplOptions.AggressiveOptimization)]
		private static void BatchCalculateDistances(int count, float camX, float camY, float camZ)
		{
			int count2 = Vector<float>.Count;
			Vector<float> vector = new Vector<float>(camX);
			Vector<float> vector2 = new Vector<float>(camY);
			Vector<float> vector3 = new Vector<float>(camZ);
			int i = 0;
			for (int num = count - count2 + 1; i < num; i += count2)
			{
				Vector<float> vector4 = new Vector<float>(positionXArray, i);
				Vector<float> vector5 = new Vector<float>(positionYArray, i);
				Vector<float> vector6 = new Vector<float>(positionZArray, i);
				Vector<float> vector7 = vector4 - vector;
				Vector<float> vector8 = vector5 - vector2;
				Vector<float> vector9 = vector6 - vector3;
				(vector7 * vector7 + vector8 * vector8 + vector9 * vector9).CopyTo(sqrDistanceArray, i);
			}
			for (; i < count; i++)
			{
				float num2 = positionXArray[i] - camX;
				float num3 = positionYArray[i] - camY;
				float num4 = positionZArray[i] - camZ;
				sqrDistanceArray[i] = num2 * num2 + num3 * num3 + num4 * num4;
			}
		}

		private static void EnsureArrayCapacity(int required)
		{
			if (positionXArray.Length < required)
			{
				int num = Math.Max(required, positionXArray.Length * 2);
				positionXArray = new float[num];
				positionYArray = new float[num];
				positionZArray = new float[num];
				sqrDistanceArray = new float[num];
			}
		}

		public static void RemoveHealthBar(uint id)
		{
			if (healthBarsByEnemyId.TryGetValue(id, out var value))
			{
				if (value != null)
				{
					SwapRemoveFromActiveList(value);
					ReturnHealthBarToPool(value);
				}
				healthBarsByEnemyId.Remove(id);
			}
			trackedEnemies.Remove(id);
			queuedIds.Remove(id);
		}

		private static void SwapRemoveFromActiveList(HealthBarUI item)
		{
			int count = activeBarList.Count;
			for (int i = 0; i < count; i++)
			{
				if (activeBarList[i] == item)
				{
					int num = count - 1;
					if (i != num)
					{
						activeBarList[i] = activeBarList[num];
					}
					activeBarList.RemoveAt(num);
					break;
				}
			}
		}

		public static void SetGlobalVisibility(bool visible)
		{
			foreach (HealthBarUI activeBar in activeBarList)
			{
				activeBar?.SetVisible(visible);
			}
		}

		public static void RebuildAllHealthBars()
		{
			MarkDistanceSettingsDirty();
			float num = Plugin.SkipUpdateDistance.Value;
			sqrSkipUpdateDistance = num * num;
			float num2 = Plugin.FadeEndDistance.Value;
			sqrFadeEndDistance = num2 * num2;
			distanceSettingsDirty = false;
			foreach (HealthBarUI activeBar in activeBarList)
			{
				if (activeBar != null)
				{
					ReturnHealthBarToPool(activeBar);
				}
			}
			activeBarList.Clear();
			healthBarsByEnemyId.Clear();
			creationQueue.Clear();
			queuedIds.Clear();
			foreach (KeyValuePair<uint, Enemy> trackedEnemy in trackedEnemies)
			{
				if ((Object)(object)trackedEnemy.Value != (Object)null && trackedEnemy.Value.hp > 0f)
				{
					CreateHealthBar(trackedEnemy.Value, trackedEnemy.Key);
				}
			}
		}

		public static int GetActiveBarCount()
		{
			return activeBarList.Count;
		}

		public static int GetTrackedEnemyCount()
		{
			return trackedEnemies.Count;
		}

		public static int GetCulledBarCount()
		{
			return culledBarCount;
		}

		public static IEnumerable<HealthBarUI> GetAllActiveBars()
		{
			return activeBarList;
		}

		public static void OnSceneChanged()
		{
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			MarkDistanceSettingsDirty();
			ResetReflectionDelegates();
			foreach (HealthBarUI activeBar in activeBarList)
			{
				activeBar?.Destroy();
			}
			activeBarList.Clear();
			healthBarsByEnemyId.Clear();
			trackedEnemies.Clear();
			creationQueue.Clear();
			queuedIds.Clear();
			while (healthBarPool.Count > 0)
			{
				healthBarPool.Pop()?.Destroy();
			}
			poolPreallocated = false;
			cachedCamera = null;
			lastCameraPosition = Vector3.zero;
			frustumPlanesValid = false;
			priorityPoolIds.Clear();
			distanceSortList.Clear();
			lastPriorityPoolUpdateTime = 0f;
			damagedBarIds.Clear();
			loggedEnemyNames.Clear();
			Scene activeScene = SceneManager.GetActiveScene();
			if (((Scene)(ref activeScene)).name == "GeneratedMap")
			{
				WarmUpPauseDetection();
				PreallocatePool();
			}
		}

		public static void WarmUpPauseDetection()
		{
			cachedLevelupChild = null;
			cachedEncounterChild = null;
			cachedChestChild = null;
			cachedEncounterWindowsObject = null;
			EnsureGameManagerReflection();
			EnsureUiManagerReflection();
			EnsureLevelupScreenReflection();
			BoneNameCache["Enemy1"] = "Render";
			BoneNameCache["Enemy2"] = "Render";
			BoneNameCache["Enemy3"] = "Render";
			BoneNameCache["Enemy4"] = "Render";
			BoneNameCache["Enemy5"] = "Render";
			BoneNameCache["Enemy6"] = "Render";
			GameObject val = GameObject.Find("EncounterWindows");
			if ((Object)(object)val != (Object)null)
			{
				cachedEncounterWindowsObject = val;
				Transform transform = val.transform;
				Transform val2 = transform.Find("LevelupScreen/New");
				if (Object.op_Implicit((Object)(object)val2))
				{
					cachedLevelupChild = ((Component)val2).gameObject;
				}
				Transform val3 = transform.Find("EncounterWindow/EncounterWindowNew");
				if (Object.op_Implicit((Object)(object)val3))
				{
					cachedEncounterChild = ((Component)val3).gameObject;
				}
				Transform val4 = transform.Find("ChestOpeningWindow/W_Offers");
				if (Object.op_Implicit((Object)(object)val4))
				{
					cachedChestChild = ((Component)val4).gameObject;
				}
			}
		}

		public static void UpdatePauseState()
		{
			if (frameCounter % 10 == 0)
			{
				IsPaused = CheckIfGamePaused();
			}
		}

		private static bool CheckIfGamePaused()
		{
			//IL_0247: Unknown result type (might be due to invalid IL or missing references)
			//IL_024e: Expected O, but got Unknown
			try
			{
				if ((Object)(object)cachedLevelupChild != (Object)null && cachedLevelupChild.activeInHierarchy)
				{
					return true;
				}
				if ((Object)(object)cachedEncounterChild != (Object)null && cachedEncounterChild.activeInHierarchy)
				{
					return true;
				}
				if ((Object)(object)cachedChestChild != (Object)null && cachedChestChild.activeInHierarchy)
				{
					return true;
				}
				if (!delegatesCompiled)
				{
					CompileReflectionDelegates();
				}
				object obj = null;
				if (getGameManagerInstance != null)
				{
					obj = getGameManagerInstance();
				}
				else if (EnsureGameManagerReflection() && cachedGameManagerInstanceProperty != null)
				{
					obj = cachedGameManagerInstanceProperty.GetValue(null);
				}
				if (obj != null)
				{
					if (getCutsceneValue != null)
					{
						if (getCutsceneValue(obj))
						{
							return true;
						}
					}
					else if (cachedCutsceneField != null && (bool)cachedCutsceneField.GetValue(obj))
					{
						return true;
					}
					if (getIsGameOverValue != null)
					{
						if (getIsGameOverValue(obj))
						{
							return true;
						}
					}
					else if (cachedIsGameOverProperty != null && (bool)cachedIsGameOverProperty.GetValue(obj))
					{
						return true;
					}
					if (invokeIsTimeFreeze != null)
					{
						if (invokeIsTimeFreeze(obj))
						{
							return true;
						}
					}
					else if (cachedIsTimeFreezeMethod != null && (bool)cachedIsTimeFreezeMethod.Invoke(obj, null))
					{
						return true;
					}
				}
				object obj2 = null;
				if (getUiManagerInstance != null)
				{
					obj2 = getUiManagerInstance();
				}
				else if (EnsureUiManagerReflection() && cachedUiManagerInstanceProperty != null)
				{
					obj2 = cachedUiManagerInstanceProperty.GetValue(null);
				}
				if (obj2 != null)
				{
					object obj3 = null;
					if (getPauseValue != null)
					{
						obj3 = getPauseValue(obj2);
					}
					else if (cachedPauseProperty != null)
					{
						obj3 = cachedPauseProperty.GetValue(obj2);
					}
					if (obj3 != null)
					{
						object obj4 = null;
						if (getPauseCurrentValue != null)
						{
							obj4 = getPauseCurrentValue(obj3);
						}
						else if (cachedPauseCurrentProperty != null)
						{
							obj4 = cachedPauseCurrentProperty.GetValue(obj3);
						}
						if (obj4 != null)
						{
							GameObject val = (GameObject)((obj4 is GameObject) ? obj4 : null);
							if (val != null)
							{
								return val.activeInHierarchy;
							}
							return true;
						}
					}
				}
			}
			catch (Exception ex)
			{
				if (Plugin.EnableDebugLogs.Value)
				{
					ManualLogSource log = Plugin.Log;
					bool flag = default(bool);
					BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(25, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("CheckIfGamePaused error: ");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message);
					}
					log.LogError(val2);
				}
			}
			return false;
		}

		public static bool IsGamePaused()
		{
			return IsPaused;
		}

		private static bool EnsureGameManagerReflection()
		{
			if (cachedGameManagerType != null)
			{
				return true;
			}
			cachedGameManagerType = Type.GetType("GameManager, Assembly-CSharp");
			if (cachedGameManagerType == null)
			{
				return false;
			}
			cachedGameManagerInstanceProperty = cachedGameManagerType.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public);
			cachedCutsceneField = cachedGameManagerType.GetField("cutscene", BindingFlags.Instance | BindingFlags.Public);
			cachedIsGameOverProperty = cachedGameManagerType.GetProperty("isGameOver", BindingFlags.Instance | BindingFlags.Public);
			cachedIsTimeFreezeMethod = cachedGameManagerType.GetMethod("IsTimeFreeze", BindingFlags.Instance | BindingFlags.Public);
			return true;
		}

		private static bool EnsureUiManagerReflection()
		{
			if (cachedUiManagerType != null)
			{
				return true;
			}
			cachedUiManagerType = Type.GetType("UiManager, Assembly-CSharp");
			if (cachedUiManagerType == null)
			{
				return false;
			}
			cachedUiManagerInstanceProperty = cachedUiManagerType.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public);
			cachedPauseProperty = cachedUiManagerType.GetProperty("pause", BindingFlags.Instance | BindingFlags.Public);
			if (cachedPauseProperty != null)
			{
				cachedPauseCurrentProperty = cachedPauseProperty.PropertyType.GetProperty("current", BindingFlags.Instance | BindingFlags.Public);
			}
			return true;
		}

		private static bool EnsureLevelupScreenReflection()
		{
			if (cachedLevelupScreenType != null)
			{
				return true;
			}
			cachedLevelupScreenType = Type.GetType("LevelupScreen, Assembly-CSharp");
			if (cachedLevelupScreenType != null)
			{
				cachedIsLevelingUpField = cachedLevelupScreenType.GetField("isLevelingUp", BindingFlags.Static | BindingFlags.Public);
			}
			return cachedLevelupScreenType != null;
		}

		private static void CompileReflectionDelegates()
		{
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Expected O, but got Unknown
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Expected O, but got Unknown
			if (delegatesCompiled)
			{
				return;
			}
			bool flag = default(bool);
			try
			{
				EnsureGameManagerReflection();
				EnsureUiManagerReflection();
				if (cachedGameManagerInstanceProperty != null)
				{
					PropertyInfo prop5 = cachedGameManagerInstanceProperty;
					getGameManagerInstance = () => prop5.GetValue(null);
				}
				if (cachedCutsceneField != null)
				{
					FieldInfo field = cachedCutsceneField;
					getCutsceneValue = (object obj) => (bool)field.GetValue(obj);
				}
				if (cachedIsGameOverProperty != null)
				{
					PropertyInfo prop4 = cachedIsGameOverProperty;
					getIsGameOverValue = (object obj) => (bool)prop4.GetValue(obj);
				}
				if (cachedIsTimeFreezeMethod != null)
				{
					MethodInfo method = cachedIsTimeFreezeMethod;
					invokeIsTimeFreeze = (object obj) => (bool)method.Invoke(obj, null);
				}
				if (cachedUiManagerInstanceProperty != null)
				{
					PropertyInfo prop3 = cachedUiManagerInstanceProperty;
					getUiManagerInstance = () => prop3.GetValue(null);
				}
				if (cachedPauseProperty != null)
				{
					PropertyInfo prop2 = cachedPauseProperty;
					getPauseValue = (object obj) => prop2.GetValue(obj);
				}
				if (cachedPauseCurrentProperty != null)
				{
					PropertyInfo prop = cachedPauseCurrentProperty;
					getPauseCurrentValue = (object obj) => prop.GetValue(obj);
				}
				delegatesCompiled = true;
				if (Plugin.EnableDebugLogs.Value)
				{
					ManualLogSource log = Plugin.Log;
					BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(39, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Reflection delegates compiled: GM=");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<bool>(getGameManagerInstance != null);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", UI=");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<bool>(getUiManagerInstance != null);
					}
					log.LogInfo(val);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = Plugin.Log;
				BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(40, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Failed to compile reflection delegates: ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message);
				}
				log2.LogWarning(val2);
				delegatesCompiled = true;
			}
		}

		private static void ResetReflectionDelegates()
		{
			delegatesCompiled = false;
			getGameManagerInstance = null;
			getCutsceneValue = null;
			getIsGameOverValue = null;
			invokeIsTimeFreeze = null;
			getUiManagerInstance = null;
			getPauseValue = null;
			getPauseCurrentValue = null;
			cachedGameManagerType = null;
			cachedUiManagerType = null;
			cachedLevelupScreenType = null;
		}

		public static void Cleanup()
		{
			MarkDistanceSettingsDirty();
			foreach (HealthBarUI activeBar in activeBarList)
			{
				if (activeBar != null)
				{
					ReturnHealthBarToPool(activeBar);
				}
			}
			activeBarList.Clear();
			healthBarsByEnemyId.Clear();
			trackedEnemies.Clear();
			creationQueue.Clear();
			queuedIds.Clear();
			while (healthBarPool.Count > 0)
			{
				healthBarPool.Pop()?.Destroy();
			}
			poolPreallocated = false;
		}

		private static HealthBarUI GetPooledHealthBarUI(Enemy enemy, uint id)
		{
			while (healthBarPool.Count > 0)
			{
				HealthBarUI healthBarUI = healthBarPool.Pop();
				if (healthBarUI != null && healthBarUI.IsValid())
				{
					try
					{
						healthBarUI.Reinitialize(enemy, id);
						return healthBarUI;
					}
					catch
					{
					}
				}
			}
			return new HealthBarUI(enemy, id);
		}

		private static void ReturnHealthBarToPool(HealthBarUI healthBarUI)
		{
			if (healthBarUI != null && healthBarUI.IsValid())
			{
				healthBarUI.ResetForPooling();
				if (healthBarPool.Count < Plugin.PoolPreallocationCount.Value)
				{
					healthBarPool.Push(healthBarUI);
				}
				else
				{
					healthBarUI.Destroy();
				}
			}
		}

		public static void PreallocatePool()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Expected O, but got Unknown
			if (poolPreallocated)
			{
				return;
			}
			int num = Plugin.PoolPreallocationCount.Value - healthBarPool.Count;
			if (num <= 0)
			{
				poolPreallocated = true;
				return;
			}
			ManualLogSource log = Plugin.Log;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(30, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Pre-allocating ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" health bars...");
			}
			log.LogInfo(val);
			for (int i = 0; i < num; i++)
			{
				HealthBarUI item = HealthBarUI.CreateForPool();
				healthBarPool.Push(item);
			}
			poolPreallocated = true;
			ManualLogSource log2 = Plugin.Log;
			val = new BepInExInfoLogInterpolatedStringHandler(41, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Pool pre-allocation complete. Pool size: ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(healthBarPool.Count);
			}
			log2.LogInfo(val);
		}

		internal static void MarkDistanceSettingsDirty()
		{
			distanceSettingsDirty = true;
			sqrSkipUpdateDistance = 0f;
			sqrFadeEndDistance = 0f;
			HealthBarUI.InvalidateFadeCache();
		}

		public static string GetEnemyTypeName(Enemy enemy)
		{
			if ((Object)(object)enemy == (Object)null)
			{
				return "null";
			}
			try
			{
				Type type = ((object)enemy).GetType();
				PropertyInfo property = type.GetProperty("enemyData", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null && property.CanRead)
				{
					string text = TryGetObjectName(property.GetValue(enemy));
					if (text != null)
					{
						return text;
					}
				}
				FieldInfo fieldInfo = type.GetField("_enemyData_k__BackingField", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetField("<enemyData>k__BackingField", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (fieldInfo != null)
				{
					string text2 = TryGetObjectName(fieldInfo.GetValue(enemy));
					if (text2 != null)
					{
						return text2;
					}
				}
				string[] array = new string[6] { "data", "enemyType", "type", "enemyInfo", "info", "config" };
				foreach (string name in array)
				{
					FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field != null)
					{
						string text3 = TryGetObjectName(field.GetValue(enemy));
						if (text3 != null)
						{
							return text3;
						}
					}
					PropertyInfo property2 = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (!(property2 != null) || !property2.CanRead)
					{
						continue;
					}
					try
					{
						string text4 = TryGetObjectName(property2.GetValue(enemy));
						if (text4 != null)
						{
							return text4;
						}
					}
					catch
					{
					}
				}
			}
			catch
			{
			}
			GameObject gameObject = ((Component)enemy).gameObject;
			return ((gameObject == null) ? null : ((Object)gameObject).name?.Replace("(Clone)", "").Trim()) ?? "Unknown";
		}

		private static string TryGetObjectName(object value)
		{
			if (value == null)
			{
				return null;
			}
			Object val = (Object)((value is Object) ? value : null);
			if (val != null)
			{
				string name = val.name;
				if (!string.IsNullOrEmpty(name))
				{
					return name;
				}
			}
			try
			{
				Type type = value.GetType();
				if (type.IsEnum)
				{
					return value.ToString();
				}
				PropertyInfo property = type.GetProperty("name", BindingFlags.Instance | BindingFlags.Public);
				if (property != null && property.CanRead)
				{
					object value2 = property.GetValue(value);
					if (value2 != null)
					{
						string text = value2.ToString();
						if (!string.IsNullOrEmpty(text))
						{
							return text;
						}
					}
				}
			}
			catch
			{
			}
			return null;
		}
	}
	public class HealthBarUI
	{
		private static readonly char[] TextBuffer = new char[32];

		private readonly GameObject rootObject;

		private readonly Canvas canvas;

		private readonly CanvasGroup canvasGroup;

		private readonly RectTransform canvasRect;

		private readonly RawImage backgroundBar;

		private readonly RawImage foregroundBar;

		private readonly Transform foregroundTransform;

		private readonly RawImage damageBar;

		private readonly Transform damageTransform;

		private TextMeshProUGUI healthText;

		private Vector3 scaleBuffer = Vector3.one;

		private uint enemyId;

		private bool useManualBillboard;

		private float cachedMaxHp = -1f;

		private bool isPlayingDeathAnimation;

		private bool isDestroyed;

		private Transform trackedBone;

		private Renderer trackedRenderer;

		private float deathAnimationTime;

		private const float DEATH_ANIMATION_DURATION = 0.1f;

		private Vector3 lastKnownPosition;

		private float currentHealth = 1f;

		private float lastHealthPercent = 1f;

		private bool isDamageFlashing;

		private bool isLingering;

		private float damageFlashTime;

		private float damageBarTargetScale = 1f;

		private const float DAMAGE_FLASH_DURATION = 0.5f;

		private float lastHpValue = -1f;

		private float lastMaxHpValue = -1f;

		private static float sqrFadeStart = -1f;

		private static float sqrFadeEnd = -1f;

		private static float cachedFadeStart = -1f;

		private static float cachedFadeEnd = -1f;

		private static readonly Color[] ColorLUT = (Color[])(object)new Color[256];

		private static bool colorLutDirty = true;

		public uint EnemyId => enemyId;

		public Vector3 LastKnownPosition => lastKnownPosition;

		public Vector3 CurrentPosition
		{
			get
			{
				//IL_0020: 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)
				if (!((Object)(object)rootObject != (Object)null))
				{
					return lastKnownPosition;
				}
				return rootObject.transform.position;
			}
		}

		internal static void InvalidateColorLUT()
		{
			colorLutDirty = true;
		}

		private static void RebuildColorLUT()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: 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_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: 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)
			//IL_004c: 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_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			Color cachedLowHealthColor = Plugin.CachedLowHealthColor;
			Color cachedMidHealthColor = Plugin.CachedMidHealthColor;
			Color cachedFullHealthColor = Plugin.CachedFullHealthColor;
			for (int i = 0; i < 256; i++)
			{
				float num = (float)i / 255f;
				if (num < 0.5f)
				{
					ColorLUT[i] = Color.Lerp(cachedLowHealthColor, cachedMidHealthColor, num * 2f);
				}
				else
				{
					ColorLUT[i] = Color.Lerp(cachedMidHealthColor, cachedFullHealthColor, (num - 0.5f) * 2f);
				}
			}
			colorLutDirty = false;
		}

		internal static void InvalidateFadeCache()
		{
			cachedFadeStart = -1f;
			cachedFadeEnd = -1f;
			sqrFadeStart = -1f;
			sqrFadeEnd = -1f;
		}

		public void ApplyBillboardRotation(Quaternion rotation)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)rootObject != (Object)null)
			{
				rootObject.transform.rotation = rotation;
			}
		}

		public static HealthBarUI CreateForPool()
		{
			return new HealthBarUI();
		}

		private HealthBarUI()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Expected O, but got Unknown
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: 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_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: 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)
			//IL_018a: Expected O, but got Unknown
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			//IL_0237: Expected O, but got Unknown
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0298: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e9: Expected O, but got Unknown
			//IL_0311: Unknown result type (might be due to invalid IL or missing references)
			//IL_031e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0333: Unknown result type (might be due to invalid IL or missing references)
			//IL_034c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0371: Unknown result type (might be due to invalid IL or missing references)
			enemyId = 0u;
			currentHealth = 1f;
			lastHealthPercent = 1f;
			isDamageFlashing = false;
			isLingering = false;
			damageFlashTime = 0f;
			damageBarTargetScale = 1f;
			trackedBone = null;
			trackedRenderer = null;
			rootObject = new GameObject("HealthBar_Pooled");
			rootObject.transform.position = Vector3.zero;
			lastKnownPosition = Vector3.zero;
			canvas = rootObject.AddComponent<Canvas>();
			canvas.renderMode = (RenderMode)2;
			canvas.sortingOrder = 100;
			canvasGroup = rootObject.AddComponent<CanvasGroup>();
			canvasGroup.alpha = 1f;
			canvasRect = ((Component)canvas).GetComponent<RectTransform>();
			float num = Plugin.BarWidth.Value;
			float num2 = Plugin.BarHeight.Value;
			canvasRect.sizeDelta = new Vector2(num, num2 + 30f);
			((Transform)canvasRect).localScale = Vector3.one * 0.01f;
			useManualBillboard = true;
			GameObject val = new GameObject("Background");
			val.transform.SetParent(rootObject.transform, false);
			RectTransform obj = val.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(num, num2);
			obj.anchoredPosition = Vector2.zero;
			val.AddComponent<CanvasRenderer>();
			backgroundBar = val.AddComponent<RawImage>();
			((Graphic)backgroundBar).color = Plugin.CachedBackgroundColor;
			((Graphic)backgroundBar).raycastTarget = false;
			GameObject val2 = new GameObject("DamageFlash");
			val2.transform.SetParent(val.transform, false);
			RectTransform obj2 = val2.AddComponent<RectTransform>();
			obj2.anchoredPosition = new Vector2((0f - num) / 2f, 0f);
			obj2.sizeDelta = new Vector2(num, num2);
			obj2.pivot = new Vector2(0f, 0.5f);
			((Transform)obj2).localScale = new Vector3(1f, 1f, 1f);
			val2.AddComponent<CanvasRenderer>();
			damageBar = val2.AddComponent<RawImage>();
			((Graphic)damageBar).color = Color.white;
			((Graphic)damageBar).raycastTarget = false;
			damageTransform = val2.transform;
			GameObject val3 = new GameObject("Current");
			val3.transform.SetParent(val.transform, false);
			RectTransform obj3 = val3.AddComponent<RectTransform>();
			obj3.anchoredPosition = new Vector2((0f - num) / 2f, 0f);
			obj3.sizeDelta = new Vector2(num, num2);
			obj3.pivot = new Vector2(0f, 0.5f);
			((Transform)obj3).localScale = new Vector3(1f, 1f, 1f);
			val3.AddComponent<CanvasRenderer>();
			foregroundBar = val3.AddComponent<RawImage>();
			((Graphic)foregroundBar).color = Plugin.CachedFullHealthColor;
			((Graphic)foregroundBar).raycastTarget = false;
			foregroundTransform = val3.transform;
			if (Plugin.ShowHealthText.Value)
			{
				CreateHealthText(num, num2);
			}
			rootObject.SetActive(false);
			isDestroyed = false;
		}

		public HealthBarUI(Enemy enemy, uint id)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Expected O, but got Unknown
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Expected O, but got Unknown
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_040d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0412: Unknown result type (might be due to invalid IL or missing references)
			//IL_049d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0315: Unknown result type (might be due to invalid IL or missing references)
			//IL_0325: Unknown result type (might be due to invalid IL or missing references)
			//IL_032a: Unknown result type (might be due to invalid IL or missing references)
			//IL_032e: Unknown result type (might be due to invalid IL or missing references)
			//IL_033e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0353: Unknown result type (might be due to invalid IL or missing references)
			//IL_0355: Unknown result type (might be due to invalid IL or missing references)
			//IL_035a: Unknown result type (might be due to invalid IL or missing references)
			//IL_035c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0368: Unknown result type (might be due to invalid IL or missing references)
			//IL_037b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0387: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_04bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0229: Unknown result type (might be due to invalid IL or missing references)
			//IL_0233: Unknown result type (might be due to invalid IL or missing references)
			//IL_025a: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: Unknown result type (might be due to invalid IL or missing references)
			//IL_026f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0273: Unknown result type (might be due to invalid IL or missing references)
			//IL_0283: Unknown result type (might be due to invalid IL or missing references)
			//IL_0298: Unknown result type (might be due to invalid IL or missing references)
			//IL_029a: Unknown result type (might be due to invalid IL or missing references)
			//IL_029f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_04db: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_04fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_051c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0526: Unknown result type (might be due to invalid IL or missing references)
			//IL_052d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0543: Unknown result type (might be due to invalid IL or missing references)
			//IL_054a: Expected O, but got Unknown
			//IL_0574: Unknown result type (might be due to invalid IL or missing references)
			//IL_0589: Unknown result type (might be due to invalid IL or missing references)
			//IL_059e: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_05b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_05dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_05f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_05fe: Expected O, but got Unknown
			//IL_0628: Unknown result type (might be due to invalid IL or missing references)
			//IL_0637: Unknown result type (might be due to invalid IL or missing references)
			//IL_064c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0665: Unknown result type (might be due to invalid IL or missing references)
			//IL_068a: Unknown result type (might be due to invalid IL or missing references)
			//IL_06b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_06b9: Expected O, but got Unknown
			//IL_06e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_06f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0707: Unknown result type (might be due to invalid IL or missing references)
			//IL_0720: Unknown result type (might be due to invalid IL or missing references)
			//IL_0745: Unknown result type (might be due to invalid IL or missing references)
			enemyId = id;
			currentHealth = 1f;
			lastHealthPercent = 1f;
			isDamageFlashing = false;
			isLingering = false;
			damageFlashTime = 0f;
			damageBarTargetScale = 1f;
			trackedBone = FindHeadBone(enemy);
			if ((Object)(object)trackedBone != (Object)null)
			{
				trackedRenderer = ((Component)trackedBone).GetComponent<Renderer>();
				if ((Object)(object)trackedRenderer == (Object)null)
				{
					trackedRenderer = ((Component)trackedBone).GetComponentInChildren<Renderer>();
				}
			}
			else if (Plugin.EnableDebugLogs.Value)
			{
				ManualLogSource log = Plugin.Log;
				bool flag = default(bool);
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(60, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[HealthBarInit] ID:");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<uint>(id);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" No bone found, using default positioning");
				}
				log.LogInfo(val);
			}
			rootObject = new GameObject($"HealthBar_{id}");
			Transform val2 = (((Object)(object)trackedBone != (Object)null) ? trackedBone : ((Component)enemy).transform);
			rootObject.transform.SetParent(val2, false);
			Vector3 zero = Vector3.zero;
			bool flag2 = HealthBarManager.UsesMeshHeightPositioning(HealthBarManager.GetEnemyTypeName(enemy));
			if (!flag2)
			{
				flag2 = HealthBarManager.UsesMeshHeightPositioning(((Object)enemy).name.Replace("(Clone)", "").Trim());
			}
			float enemyMeshHeight = HealthBarManager.GetEnemyMeshHeight(enemy);
			Bounds bounds;
			if (flag2 && enemyMeshHeight > 0f && (Object)(object)trackedBone != (Object)null && (Object)(object)trackedRenderer != (Object)null)
			{
				float num = enemyMeshHeight + Plugin.BarYOffset.Value;
				Vector3 lossyScale = trackedBone.lossyScale;
				float num2 = 1f / Mathf.Max(new float[4] { lossyScale.x, lossyScale.y, lossyScale.z, 0.01f });
				num *= num2;
				float x = trackedBone.position.x;
				bounds = trackedRenderer.bounds;
				Vector3 val3 = default(Vector3);
				((Vector3)(ref val3))..ctor(x, ((Bounds)(ref bounds)).max.y, trackedBone.position.z);
				Vector3 val4 = trackedBone.InverseTransformPoint(val3);
				if (Mathf.Abs(val4.z) > Mathf.Abs(val4.y))
				{
					((Vector3)(ref zero))..ctor(0f, 0f, num);
				}
				else
				{
					((Vector3)(ref zero))..ctor(0f, num, 0f);
				}
			}
			else if ((Object)(object)trackedBone != (Object)null && (Object)(object)trackedRenderer != (Object)null)
			{
				float x2 = trackedBone.position.x;
				bounds = trackedRenderer.bounds;
				Vector3 val5 = default(Vector3);
				((Vector3)(ref val5))..ctor(x2, ((Bounds)(ref bounds)).max.y, trackedBone.position.z);
				Vector3 val6 = trackedBone.InverseTransformPoint(val5);
				float num3 = Mathf.Max(Mathf.Abs(val6.y), Mathf.Abs(val6.z));
				if (Mathf.Abs(val6.z) > Mathf.Abs(val6.y))
				{
					((Vector3)(ref zero))..ctor(0f, 0f, num3 + Plugin.BarYOffset.Value);
				}
				else
				{
					((Vector3)(ref zero))..ctor(0f, num3 + Plugin.BarYOffset.Value, 0f);
				}
			}
			else
			{
				((Vector3)(ref zero))..ctor(0f, Plugin.BarYOffset.Value, 0f);
			}
			rootObject.transform.localPosition = zero;
			lastKnownPosition = rootObject.transform.position;
			canvas = rootObject.AddComponent<Canvas>();
			canvas.renderMode = (RenderMode)2;
			canvas.sortingOrder = 100;
			canvasGroup = rootObject.AddComponent<CanvasGroup>();
			canvasGroup.alpha = 1f;
			canvasRect = ((Component)canvas).GetComponent<RectTransform>();
			float num4 = Plugin.BarWidth.Value;
			float num5 = Plugin.BarHeight.Value;
			canvasRect.sizeDelta = new Vector2(num4, num5 + 30f);
			Vector3 val7 = (((Object)(object)rootObject.transform.parent != (Object)null) ? rootObject.transform.parent.lossyScale : Vector3.one);
			float num6 = 1f / Mathf.Max(new float[4] { val7.x, val7.y, val7.z, 0.01f });
			((Transform)canvasRect).localScale = Vector3.one * 0.01f * num6;
			useManualBillboard = true;
			GameObject val8 = new GameObject("Background");
			val8.transform.SetParent(rootObject.transform, false);
			RectTransform obj = val8.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(num4, num5);
			obj.anchoredPosition = Vector2.zero;
			val8.AddComponent<CanvasRenderer>();
			backgroundBar = val8.AddComponent<RawImage>();
			((Graphic)backgroundBar).color = Plugin.CachedBackgroundColor;
			((Graphic)backgroundBar).raycastTarget = false;
			GameObject val9 = new GameObject("DamageFlash");
			val9.transform.SetParent(val8.transform, false);
			RectTransform obj2 = val9.AddComponent<RectTransform>();
			obj2.anchoredPosition = new Vector2((0f - num4) / 2f, 0f);
			obj2.sizeDelta = new Vector2(num4, num5);
			obj2.pivot = new Vector2(0f, 0.5f);
			((Transform)obj2).localScale = new Vector3(1f, 1f, 1f);
			val9.AddComponent<CanvasRenderer>();
			damageBar = val9.AddComponent<RawImage>();
			((Graphic)damageBar).color = Color.white;
			((Graphic)damageBar).raycastTarget = false;
			damageTransform = val9.transform;
			GameObject val10 = new GameObject("Current");
			val10.transform.SetParent(val8.transform, false);
			RectTransform obj3 = val10.AddComponent<RectTransform>();
			obj3.anchoredPosition = new Vector2((0f - num4) / 2f, 0f);
			obj3.sizeDelta = new Vector2(num4, num5);
			obj3.pivot = new Vector2(0f, 0.5f);
			((Transform)obj3).localScale = new Vector3(1f, 1f, 1f);
			val10.AddComponent<CanvasRenderer>();
			foregroundBar = val10.AddComponent<RawImage>();
			((Graphic)foregroundBar).color = Plugin.CachedFullHealthColor;
			((Graphic)foregroundBar).raycastTarget = false;
			foregroundTransform = val10.transform;
			if (Plugin.ShowHealthText.Value)
			{
				CreateHealthText(num4, num5);
			}
			rootObject.SetActive(Plugin.ShowHealthBars.Value);
			Update(enemy);
		}

		private void CreateHealthText(float barWidth, float barHeight)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("HealthText");
			val.transform.SetParent(rootObject.transform, false);
			RectTransform obj = val.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(barWidth, 30f);
			obj.anchoredPosition = new Vector2(0f, barHeight / 2f + 15f);
			healthText = val.AddComponent<TextMeshProUGUI>();
			((TMP_Text)healthText).alignment = (TextAlignmentOptions)514;
			((TMP_Text)healthText).fontSize = Plugin.TextSize.Value;
			((Graphic)healthText).color = Color.white;
			((TMP_Text)healthText).text = "";
			((TMP_Text)healthText).enableWordWrapping = false;
			((TMP_Text)healthText).overflowMode = (TextOverflowModes)0;
			((TMP_Text)healthText).richText = false;
			((TMP_Text)healthText).enableKerning = false;
			((TMP_Text)healthText).extraPadding = false;
			((Graphic)healthText).raycastTarget = false;
			((TMP_Text)healthText).enableAutoSizing = false;
			((TMP_Text)healthText).geometrySortingOrder = (VertexSortingOrder)0;
		}

		public void Update(Enemy enemy, float sqrDistanceToCamera = -1f)
		{
			//IL_0213: Unknown result type (might be due to invalid IL or missing references)
			//IL_021a: Expected O, but got Unknown
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (isPlayingDeathAnimation)
				{
					UpdateDeathAnimation();
					return;
				}
				if ((Object)(object)enemy == (Object)null || enemy.hp <= 0f)
				{
					if (Plugin.EnableDeathAnimation.Value)
					{
						StartDeathAnimation();
					}
					else
					{
						Destroy();
					}
					return;
				}
				lastKnownPosition = rootObject.transform.position;
				if (sqrDistanceToCamera < 0f)
				{
					Camera val = HealthBarManager.CachedCamera ?? Camera.main;
					if ((Object)(object)val != (Object)null)
					{
						Vector3 val2 = lastKnownPosition - ((Component)val).transform.position;
						sqrDistanceToCamera = ((Vector3)(ref val2)).sqrMagnitude;
					}
				}
				UpdateDistanceFadeSqr(sqrDistanceToCamera);
				if (canvasGroup.alpha <= 0.01f)
				{
					return;
				}
				float hp = enemy.hp;
				float healthPercent = GetHealthPercent(enemy, hp);
				if (healthPercent < lastHealthPercent && !isDamageFlashing && Plugin.ShowDamageFlash.Value)
				{
					StartDamageFlash(enemy, lastHealthPercent, healthPercent);
				}
				if (isDamageFlashing)
				{
					UpdateDamageFlash();
				}
				else
				{
					scaleBuffer.x = healthPercent;
					damageTransform.localScale = scaleBuffer;
				}
				currentHealth = healthPercent;
				scaleBuffer.x = healthPercent;
				foregroundTransform.localScale = scaleBuffer;
				if (Plugin.UseColorGradient.Value)
				{
					((Graphic)foregroundBar).color = GetHealthColor(healthPercent);
				}
				lastHealthPercent = healthPercent;
				if ((Object)(object)healthText != (Object)null)
				{
					float maxHp = GetMaxHp(enemy, hp, healthPercent);
					int num = ((hp > 0f && hp < 1f) ? 1 : Mathf.RoundToInt(hp));
					int num2 = Mathf.RoundToInt(maxHp);
					if ((float)num != lastHpValue || (float)num2 != lastMaxHpValue)
					{
						int num3 = FormatHealthText(num, num2);
						((TMP_Text)healthText).SetCharArray(Il2CppStructArray<char>.op_Implicit(TextBuffer), 0, num3);
						lastHpValue = num;
						lastMaxHpValue = num2;
					}
				}
			}
			catch (Exception ex)
			{
				if (Plugin.EnableDebugLogs.Value && enemyId < 3)
				{
					ManualLogSource log = Plugin.Log;
					bool flag = default(bool);
					BepInExErrorLogInterpolatedStringHandler val3 = new BepInExErrorLogInterpolatedStringHandler(26, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("[BarUpdate] Enemy ");
						((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<uint>(enemyId);
						((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" error: ");
						((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>(ex.Message);
					}
					log.LogError(val3);
				}
			}
		}

		private void StartDeathAnimation()
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Expected O, but got Unknown
			if (isPlayingDeathAnimation)
			{
				return;
			}
			isPlayingDeathAnimation = true;
			deathAnimationTime = 0f;
			isDamageFlashing = false;
			isLingering = false;
			damageFlashTime = 0f;
			Transform transform = rootObject.transform;
			transform.SetParent((Transform)null, true);
			transform.position = lastKnownPosition;
			((Graphic)foregroundBar).color = Plugin.CachedLowHealthColor;
			scaleBuffer.x = 1f;
			foregroundTransform.localScale = scaleBuffer;
			scaleBuffer.x = 0f;
			damageTransform.localScale = scaleBuffer;
			scaleBuffer.x = 1f;
			if ((Object)(object)healthText != (Object)null)
			{
				((Component)healthText).gameObject.SetActive(false);
			}
			if (Plugin.EnableDebugLogs.Value)
			{
				ManualLogSource log = Plugin.Log;
				bool flag = default(bool);
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(30, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[DeathAnim] Started for enemy ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<uint>(enemyId);
				}
				log.LogInfo(val);
			}
		}

		private void UpdateDeathAnimation()
		{
			//IL_0037: 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_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			if (HealthBarManager.IsGamePaused())
			{
				return;
			}
			deathAnimationTime += Time.deltaTime;
			float num = deathAnimationTime / 0.1f;
			if (num >= 1f)
			{
				Destroy();
				return;
			}
			if (lastKnownPosition != Vector3.zero)
			{
				rootObject.transform.position = lastKnownPosition;
			}
			float num2 = 1f + num * 1.5f;
			((Transform)canvasRect).localScale = Vector3.one * 0.01f * num2;
			float alpha = 1f - num;
			canvasGroup.alpha = alpha;
		}

		private void StartDamageFlash(Enemy enemy, float previousHealthPercent, float newHealthPercent)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (!isDamageFlashing)
			{
				isDamageFlashing = true;
				isLingering = true;
				damageFlashTime = 0f;
				scaleBuffer.x = previousHealthPercent;
				damageTransform.localScale = scaleBuffer;
				damageBarTargetScale = newHealthPercent;
			}
		}

		private void UpdateDamageFlash()
		{
			//IL_00a7: 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)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			if (HealthBarManager.IsGamePaused())
			{
				return;
			}
			damageFlashTime += Time.deltaTime;
			if (isLingering)
			{
				float num = (float)Plugin.DamageFlashLingerMs.Value / 1000f;
				if (damageFlashTime >= num)
				{
					isLingering = false;
					damageFlashTime = 0f;
				}
				return;
			}
			float num2 = damageFlashTime / 0.5f;
			if (num2 >= 1f)
			{
				isDamageFlashing = false;
				isLingering = false;
				damageFlashTime = 0f;
				scaleBuffer.x = damageBarTargetScale;
				damageTransform.localScale = scaleBuffer;
			}
			else
			{
				float x = Mathf.Lerp(damageTransform.localScale.x, damageBarTargetScale, num2 * 2f);
				scaleBuffer.x = x;
				damageTransform.localScale = scaleBuffer;
			}
		}

		private void UpdateDistanceFadeSqr(float sqrDistance)
		{
			if (sqrDistance < 0f)
			{
				canvasGroup.alpha = 1f;
				return;
			}
			float num = Plugin.FadeStartDistance.Value;
			if (Math.Abs(num - cachedFadeStart) > 0.1f)
			{
				cachedFadeStart = num;
				sqrFadeStart = num * num;
				float num2 = (cachedFadeEnd = Plugin.FadeEndDistance.Value);
				sqrFadeEnd = num2 * num2;
			}
			if (sqrDistance < sqrFadeStart)
			{
				canvasGroup.alpha = 1f;
				return;
			}
			if (sqrDistance > sqrFadeEnd)
			{
				canvasGroup.alpha = 0f;
				return;
			}
			float num3 = (Mathf.Sqrt(sqrDistance) - cachedFadeStart) / (cachedFadeEnd - cachedFadeStart);
			canvasGroup.alpha = 1f - num3;
		}

		public void UpdateVisibility(float sqrDistanceToCamera)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			if (!isPlayingDeathAnimation)
			{
				if ((Object)(object)rootObject != (Object)null)
				{
					lastKnownPosition = rootObject.transform.position;
				}
				UpdateDistanceFadeSqr(sqrDistanceToCamera);
			}
		}

		public void LateTick()
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			if (useManualBillboard && (isPlayingDeathAnimation || rootObject.activeInHierarchy) && (!((((Object)(object)canvasGroup != (Object)null) ? canvasGroup.alpha : 1f) <= 0.01f) || isPlayingDeathAnimation) && HealthBarManager.HasValidBillboard)
			{
				rootObject.transform.rotation = HealthBarManager.BillboardRotation;
			}
		}

		private float GetMaxHp(Enemy enemy, float currentHp, float healthPercent)
		{
			if (cachedMaxHp > 0f && healthPercent > 0.99f)
			{
				return cachedMaxHp;
			}
			try
			{
				if (healthPercent > 0.01f && currentHp > 0f)
				{
					float result = currentHp / healthPercent;
					if (healthPercent > 0.99f)
					{
						cachedMaxHp = result;
					}
					return result;
				}
			}
			catch
			{
			}
			if (cachedMaxHp > 0f)
			{
				return cachedMaxHp;
			}
			if (!(currentHp > 0f))
			{
				return 100f;
			}
			return currentHp;
		}

		private float GetHealthPercent(Enemy enemy, float currentHp)
		{
			try
			{
				float hpRatio = enemy.GetHpRatio();
				if (!float.IsNaN(hpRatio) && !float.IsInfinity(hpRatio) && hpRatio >= 0f && hpRatio <= 1.1f)
				{
					return Mathf.Clamp01(hpRatio);
				}
			}
			catch
			{
			}
			if (cachedMaxHp > 0f && currentHp > 0f)
			{
				return Mathf.Clamp01(currentHp / cachedMaxHp);
			}
			return 1f;
		}

		private static Color GetHealthColor(float healthPercent)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			if (colorLutDirty)
			{
				RebuildColorLUT();
			}
			int num = (int)(healthPercent * 255f);
			if (num < 0)
			{
				num = 0;
			}
			else if (num > 255)
			{
				num = 255;
			}
			return ColorLUT[num];
		}

		public void SetVisible(bool visible)
		{
			if ((Object)(object)rootObject != (Object)null)
			{
				rootObject.SetActive(visible);
			}
		}

		public void SetCulled(bool culled)
		{
			if ((Object)(object)rootObject != (Object)null && !isPlayingDeathAnimation)
			{
				rootObject.SetActive(!culled);
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public bool IsValid()
		{
			if (!isDestroyed)
			{
				return (Object)(object)rootObject != (Object)null;
			}
			return false;
		}

		public bool IsPlayingDeathAnimation()
		{
			return isPlayingDeathAnimation;
		}

		public bool IsDamageFlashing()
		{
			return isDamageFlashing;
		}

		public void Destroy()
		{
			isDestroyed = true;
			if ((Object)(object)rootObject != (Object)null)
			{
				Object.Destroy((Object)(object)rootObject);
			}
		}

		public void Reinitialize(Enemy enemy, uint id)
		{
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0300: Unknown result type (might be due to invalid IL or missing references)
			//IL_0305: Unknown result type (might be due to invalid IL or missing references)
			//IL_0307: Unknown result type (might be due to invalid IL or missing references)
			//IL_0313: Unknown result type (might be due to invalid IL or missing references)
			//IL_0326: Unknown result type (might be due to invalid IL or missing references)
			//IL_0332: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_023b: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0242: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_0250: Unknown result type (might be due to invalid IL or missing references)
			//IL_0421: Unknown result type (might be due to invalid IL or missing references)
			//IL_040a: Unknown result type (might be due to invalid IL or missing references)
			//IL_037f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0287: Unknown result type (might be due to invalid IL or missing references)
			//IL_026b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0426: Unknown result type (might be due to invalid IL or missing references)
			//IL_0435: Unknown result type (might be due to invalid IL or missing references)
			//IL_043f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0449: Unknown result type (might be due to invalid IL or missing references)
			//IL_0467: Unknown result type (might be due to invalid IL or missing references)
			//IL_0471: Unknown result type (might be due to invalid IL or missing references)
			//IL_0478: Unknown result type (might be due to invalid IL or missing references)
			//IL_0499: Unknown result type (might be due to invalid IL or missing references)
			//IL_04cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0500: Unknown result type (might be due to invalid IL or missing references)
			//IL_0518: Unknown result type (might be due to invalid IL or missing references)
			//IL_0532: Unknown result type (might be due to invalid IL or missing references)
			//IL_054a: Unknown result type (might be due to invalid IL or missing references)
			//IL_055a: Unknown result type (might be due to invalid IL or missing references)
			//IL_056a: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_05de: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)rootObject == (Object)null)
			{
				throw new InvalidOperationException("Cannot reinitialize destroyed health bar");
			}
			enemyId = id;
			isDestroyed = false;
			cachedMaxHp = -1f;
			lastHpValue = -1f;
			lastMaxHpValue = -1f;
			isPlayingDeathAnimation = false;
			deathAnimationTime = 0f;
			currentHealth = 1f;
			lastHealthPercent = 1f;
			isDamageFlashing = false;
			isLingering = false;
			damageFlashTime = 0f;
			damageBarTargetScale = 1f;
			scaleBuffer.x = 1f;
			damageTransform.localScale = scaleBuffer;
			trackedBone = FindHeadBone(enemy);
			Transform transform = rootObject.transform;
			Transform val = (((Object)(object)trackedBone != (Object)null) ? trackedBone : ((Component)enemy).transform);
			transform.SetParent(val, false);
			bool flag = HealthBarManager.UsesMeshHeightPositioning(HealthBarManager.GetEnemyTypeName(enemy));
			if (!flag)
			{
				flag = HealthBarManager.UsesMeshHeightPositioning(((Object)enemy).name.Replace("(Clone)", "").Trim());
			}
			if ((Object)(object)trackedBone != (Object)null)
			{
				trackedRenderer = ((Component)trackedBone).GetComponent<Renderer>();
				if ((Object)(object)trackedRenderer == (Object)null)
				{
					trackedRenderer = ((Component)trackedBone).GetComponentInChildren<Renderer>();
				}
			}
			float enemyMeshHeight = HealthBarManager.GetEnemyMeshHeight(enemy);
			Bounds bounds;
			if (flag && enemyMeshHeight > 0f && (Object)(object)trackedBone != (Object)null && (Object)(object)trackedRenderer != (Object)null)
			{
				float num = enemyMeshHeight + Plugin.BarYOffset.Value;
				Vector3 lossyScale = trackedBone.lossyScale;
				float num2 = 1f / Mathf.Max(new float[4] { lossyScale.x, lossyScale.y, lossyScale.z, 0.01f });
				num *= num2;
				float x = trackedBone.position.x;
				bounds = trackedRenderer.bounds;
				Vector3 val2 = default(Vector3);
				((Vector3)(ref val2))..ctor(x, ((Bounds)(ref bounds)).max.y, trackedBone.position.z);
				Vector3 val3 = trackedBone.InverseTransformPoint(val2);
				if (Mathf.Abs(val3.z) > Mathf.Abs(val3.y))
				{
					transform.localPosition = new Vector3(0f, 0f, num);
				}
				else
				{
					transform.localPosition = new Vector3(0f, num, 0f);
				}
			}
			else if ((Object)(object)trackedBone != (Object)null && (Object)(object)trackedRenderer != (Object)null)
			{
				float x2 = trackedBone.position.x;
				bounds = trackedRenderer.bounds;
				Vector3 val4 = default(Vector3);
				((Vector3)(ref val4))..ctor(x2, ((Bounds)(ref bounds)).max.y, trackedBone.position.z);
				Vector3 val5 = trackedBone.InverseTransformPoint(val4);
				float num3 = Mathf.Max(Mathf.Abs(val5.y), Mathf.Abs(val5.z));
				Vector3 localPosition = default(Vector3);
				if (Mathf.Abs(val5.z) > Mathf.Abs(val5.y))
				{
					((Vector3)(ref localPosition))..ctor(0f, 0f, num3 + Plugin.BarYOffset.Value);
				}
				else
				{
					((Vector3)(ref localPosition))..ctor(0f, num3 + Plugin.BarYOffset.Value, 0f);
				}
				transform.localPosition = localPosition;
			}
			else if ((Object)(object)trackedBone != (Object)null)
			{
				transform.localPosition = new Vector3(0f, Plugin.BarYOffset.Value, 0f);
			}
			else
			{
				transform.localPosition = new Vector3(0f, Plugin.BarYOffset.Value, 0f);
			}
			lastKnownPosition = transform.position;
			canvasGroup.alpha = 1f;
			Vector3 val6 = (((Object)(object)rootObject.transform.parent != (Object)null) ? rootObject.transform.parent.lossyScale : Vector3.one);
			float num4 = 1f / Mathf.Max(new float[4] { val6.x, val6.y, val6.z, 0.01f });
			((Transform)canvasRect).localScale = Vector3.one * 0.01f * num4;
			scaleBuffer.x = 1f;
			foregroundTransform.localScale = scaleBuffer;
			float num5 = Plugin.BarWidth.Value;
			float num6 = Plugin.BarHeight.Value;
			canvasRect.sizeDelta = new Vector2(num5, num6 + 30f);
			((Component)backgroundBar).GetComponent<RectTransform>().sizeDelta = new Vector2(num5, num6);
			RectTransform component = ((Component)damageBar).GetComponent<RectTransform>();
			component.sizeDelta = new Vector2(num5, num6);
			component.anchoredPosition = new Vector2((0f - num5) / 2f, 0f);
			RectTransform component2 = ((Component)foregroundBar).GetComponent<RectTransform>();
			component2.sizeDelta = new Vector2(num5, num6);
			component2.anchoredPosition = new Vector2((0f - num5) / 2f, 0f);
			((Graphic)backgroundBar).color = Plugin.CachedBackgroundColor;
			((Graphic)foregroundBar).color = Plugin.CachedFullHealthColor;
			if (Plugin.ShowHealthText.Value)
			{
				if ((Object)(object)healthText == (Object)null)
				{
					CreateHealthText(num5, num6);
				}
				else
				{
					((Component)healthText).gameObject.SetActive(true);
					RectTransform component3 = ((Component)healthText).GetComponent<RectTransform>();
					component3.sizeDelta = new Vector2(num5, 30f);
					component3.anchoredPosition = new Vector2(0f, num6 / 2f + 15f);
				}
				((TMP_Text)healthText).text = "";
				((TMP_Text)healthText).fontSize = Plugin.TextSize.Value;
			}
			else if ((Object)(object)healthText != (Object)null)
			{
				((Component)healthText).gameObject.SetActive(false);
			}
			rootObject.SetActive(Plugin.ShowHealthBars.Value);
			Update(enemy);
		}

		public void ResetForPooling()
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)rootObject != (Object)null)
			{
				rootObject.transform.SetParent((Transform)null, true);
				cachedMaxHp = -1f;
				lastHpValue = -1f;
				lastMaxHpValue = -1f;
				isPlayingDeathAnimation = false;
				deathAnimationTime = 0f;
				trackedBone = null;
				trackedRenderer = null;
				lastKnownPosition = Vector3.zero;
				currentHealth = 1f;
				lastHealthPercent = 1f;
				isDamageFlashing = false;
				isLingering = false;
				damageFlashTime = 0f;
				damageBarTargetScale = 1f;
				rootObject.SetActive(false);
				if ((Object)(object)healthText != (Object)null)
				{
					((TMP_Text)healthText).text = "";
				}
			}
		}

		private Transform FindHeadBone(Enemy enemy)
		{
			if ((Object)(object)enemy == (Object)null)
			{
				return null;
			}
			string key = ((Object)enemy).name.Replace("(Clone)", "").Trim();
			if (HealthBarManager.BoneNameCache.TryGetValue(key, out var value))
			{
				Transform val = FindChildRecursive(((Component)enemy).transform, value);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			try
			{
				Animator componentInChildren = ((Component)enemy).GetComponentInChildren<Animator>();
				if ((Object)(object)componentInChildren != (Object)null && componentInChildren.isHuman)
				{
					Transform boneTransform = componentInChildren.GetBoneTransform((HumanBodyBones)10);
					if ((Object)(object)boneTransform != (Object)null)
					{
						return boneTransform;
					}
				}
			}
			catch
			{
			}
			string[] array = new string[4] { "head", "Render", "Body", "Mesh" };
			foreach (string text in array)
			{
				Transform val2 = FindChildRecursive(((Component)enemy).transform, text);
				if ((Object)(object)val2 != (Object)null)
				{
					HealthBarManager.BoneNameCache[key] = text;
					return val2;
				}
			}
			return null;
		}

		private Transform FindChildRecursive(Transform parent, string namePart)
		{
			for (int i = 0; i < parent.childCount; i++)
			{
				Transform child = parent.GetChild(i);
				if (((Object)child).name.IndexOf(namePart, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return child;
				}
				Transform val = FindChildRecursive(child, namePart);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			return null;
		}

		private static int FormatHealthText(int currentHp, int maxHp)
		{
			int pos = 0;
			pos = WriteInt(currentHp, pos);
			TextBuffer[pos++] = ' ';
			TextBuffer[pos++] = '/';
			TextBuffer[pos++] = ' ';
			return WriteInt(maxHp, pos);
		}

		private static int WriteInt(int value, int pos)
		{
			if (value == 0)
			{
				TextBuffer[pos++] = '0';
				return pos;
			}
			if (value < 0)
			{
				TextBuffer[pos++] = '-';
				value = -value;
			}
			int num = pos;
			while (value > 0)
			{
				TextBuffer[pos++] = (char)(48 + value % 10);
				value /= 10;
			}
			int num2 = pos - 1;
			while (num < num2)
			{
				ref char reference = ref TextBuffer[num];
				ref char reference2 = ref TextBuffer[num2];
				char c = TextBuffer[num2];
				char c2 = TextBuffer[num];
				reference = c;
				reference2 = c2;
				num++;
				num2--;
			}
			return pos;
		}
	}
	[HarmonyPatch(typeof(Enemy), "InitEnemy")]
	internal static class EnemyInitPatch
	{
		private static void Postfix(Enemy __instance, uint id)
		{
			if ((Object)(object)__instance != (Object)null && Plugin.ShowHealthBars.Value)
			{
				HealthBarManager.CreateHealthBar(__instance, id);
			}
		}
	}
	[HarmonyPatch(typeof(Enemy), "Damage")]
	internal static class EnemyDamagePatch
	{
		private static void Postfix(Enemy __instance)
		{
			if ((Object)(object)__instance != (Object)null && Plugin.ShowHealthBars.Value)
			{
				HealthBarManager.UpdateHealthBar(__instance, __instance.id);
			}
		}
	}
	[HarmonyPatch(typeof(Enemy), "OnDestroy")]
	internal static class EnemyDestroyPatch
	{
		private static void Prefix(Enemy __instance)
		{
			if ((Object)(object)__instance != (Object)null)
			{
				HealthBarManager.RemoveHealthBar(__instance.id);
			}
		}
	}
	[HarmonyPatch(typeof(EnemyManager), "RemoveEnemy")]
	internal static class EnemyManagerRemovePatch
	{
		private static void Postfix(EnemyManager __instance, Enemy enemy)
		{
			if ((Object)(object)enemy != (Object)null)
			{
				HealthBarManager.RemoveHealthBar(enemy.