Decompiled source of Enemy Health Bar v0.0.1

BepInEx/plugins/EnemyHealthBar/EnemyHealthBar.dll

Decompiled 8 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using Auuueser.EnemyHealthBars.Configuration;
using Auuueser.EnemyHealthBars.Core.Configuration;
using Auuueser.EnemyHealthBars.Core.Domain;
using Auuueser.EnemyHealthBars.Core.GameData;
using Auuueser.EnemyHealthBars.Core.Presentation;
using Auuueser.EnemyHealthBars.Game;
using Auuueser.EnemyHealthBars.Presentation;
using Auuueser.EnemyHealthBars.Runtime;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using LethalConfig;
using LethalConfig.ConfigItems;
using LethalConfig.ConfigItems.Options;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("EnemyHealthBar")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+acb061bd561295755ea67507eed9dd688145cd1f")]
[assembly: AssemblyProduct("EnemyHealthBar")]
[assembly: AssemblyTitle("EnemyHealthBar")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace Auuueser.EnemyHealthBars
{
	[BepInPlugin("auuueser.lethalcompany.enemyhealthbar", "Enemy Health Bar", "0.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInProcess("Lethal Company.exe")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private HealthBarRuntime? runtime;

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			ConfigLanguage language = ChineseProjectLanguageDetector.Detect(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger);
			ModConfig config = ModConfig.Bind(((BaseUnityPlugin)this).Config, language);
			if (LethalConfigIntegration.IsAvailable())
			{
				LethalConfigIntegration.Register(config, ((BaseUnityPlugin)this).Logger);
			}
			runtime = HealthBarRuntime.Start(config, ((BaseUnityPlugin)this).Logger);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Enemy Health Bar 0.0.1 loaded.");
		}

		private void OnDestroy()
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Plugin component destroyed; runtime remains active.");
		}
	}
	internal static class PluginInfo
	{
		public const string PluginGuid = "auuueser.lethalcompany.enemyhealthbar";

		public const string PluginName = "Enemy Health Bar";

		public const string PluginVersion = "0.0.1";
	}
}
namespace Auuueser.EnemyHealthBars.Runtime
{
	internal sealed class EnemyHealthBarController : MonoBehaviour
	{
		private struct DiagnosticScanCounts
		{
			public static readonly DiagnosticScanCounts Empty;

			public int Spawned;

			public int Readable;

			public int Shown;

			public int Active;

			public int RejectedMissingEnemy;

			public int RejectedMissingEnemyType;

			public int RejectedCannotDie;

			public int HiddenDead;

			public int HiddenZeroHealth;

			public int HiddenFull;

			public int HiddenDistance;
		}

		private readonly EnemyHealthTracker tracker = new EnemyHealthTracker();

		private readonly EnemyHealthReader healthReader = new EnemyHealthReader();

		private readonly EnemyMaxHealthResolver maxHealthResolver = new EnemyMaxHealthResolver();

		private readonly HashSet<int> activeEnemyIds = new HashSet<int>();

		private ModConfig? config;

		private ManualLogSource? logger;

		private RoundManagerEnemySource? enemySource;

		private LocalPlayerCameraProvider? cameraProvider;

		private EnemyHealthBarPresenter? presenter;

		private float nextScanTime;

		private float nextDiagnosticLogTime;

		private float nextDebugHeartbeatLogTime;

		private int observedSettingsVersion;

		public void Initialize(ModConfig config, ManualLogSource logger, RoundManagerEnemySource enemySource, LocalPlayerCameraProvider cameraProvider, EnemyHealthBarPresenter presenter)
		{
			this.config = config;
			this.logger = logger;
			this.enemySource = enemySource;
			this.cameraProvider = cameraProvider;
			this.presenter = presenter;
			observedSettingsVersion = config.SettingsVersion;
		}

		public void SetEnabled(bool enabled)
		{
			((Behaviour)this).enabled = enabled;
			((Component)this).gameObject.SetActive(enabled);
		}

		private void Update()
		{
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: 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)
			if (config == null || enemySource == null || cameraProvider == null || presenter == null)
			{
				return;
			}
			config.ReloadIfChangedOnDisk(Time.unscaledTime);
			if (observedSettingsVersion != config.SettingsVersion)
			{
				observedSettingsVersion = config.SettingsVersion;
				presenter.RefreshStyle();
			}
			if (!config.Enabled)
			{
				presenter.Clear();
				LogDiagnosticsIfDue(DiagnosticScanCounts.Empty, null, "Disabled");
				LogDebugHeartbeatIfDue(null);
				return;
			}
			Camera activeCamera = cameraProvider.GetActiveCamera();
			if ((Object)(object)activeCamera == (Object)null)
			{
				presenter.Clear();
				LogDiagnosticsIfDue(DiagnosticScanCounts.Empty, null, "No active camera");
				LogDebugHeartbeatIfDue(null);
				return;
			}
			Quaternion billboardRotation = HealthBarBillboard.CalculateRotation(activeCamera);
			UpdateDebugTestBar(activeCamera, billboardRotation);
			LogDebugHeartbeatIfDue(activeCamera);
			bool flag = false;
			if (Time.unscaledTime >= nextScanTime)
			{
				nextScanTime = Time.unscaledTime + config.ScanInterval;
				RefreshEnemies(activeCamera, billboardRotation);
				flag = true;
			}
			if (!flag)
			{
				presenter.UpdateActive(activeCamera, billboardRotation);
			}
		}

		private void OnDestroy()
		{
			presenter?.Clear();
		}

		private void RefreshEnemies(Camera camera, Quaternion billboardRotation)
		{
			//IL_002a: 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_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: 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_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			if (config == null || enemySource == null || presenter == null)
			{
				return;
			}
			activeEnemyIds.Clear();
			HealthBarVisibilityRules val = config.CreateVisibilityRules();
			Vector3 position = ((Component)camera).transform.position;
			List<EnemyAI> spawnedEnemies = enemySource.GetSpawnedEnemies();
			if (spawnedEnemies == null)
			{
				tracker.KeepOnly((ISet<int>)activeEnemyIds);
				presenter.HideMissing(activeEnemyIds);
				LogDiagnosticsIfDue(DiagnosticScanCounts.Empty, camera, "SpawnedEnemies unavailable");
				return;
			}
			DiagnosticScanCounts diagnosticScanCounts = default(DiagnosticScanCounts);
			diagnosticScanCounts.Spawned = spawnedEnemies.Count;
			DiagnosticScanCounts counts = diagnosticScanCounts;
			for (int i = 0; i < spawnedEnemies.Count; i++)
			{
				EnemyAI val2 = spawnedEnemies[i];
				if ((Object)(object)val2 == (Object)null)
				{
					counts.RejectedMissingEnemy++;
					continue;
				}
				if (!healthReader.TryRead(val2, config.ShowInvulnerableEnemies, maxHealthResolver, out var sample, out var failure))
				{
					AddReadFailure(ref counts, failure);
					continue;
				}
				counts.Readable++;
				activeEnemyIds.Add(((EnemyHealthSample)(ref sample)).EnemyId);
				EnemyHealthSnapshot val3 = tracker.Track(sample, config.MaxHealthMode);
				if (((EnemyHealthSnapshot)(ref val3)).IsDead)
				{
					counts.HiddenDead++;
					presenter.Hide(((EnemyHealthSample)(ref sample)).EnemyId);
					continue;
				}
				if (((EnemyHealthSnapshot)(ref val3)).CurrentHealth <= 0 || ((EnemyHealthSnapshot)(ref val3)).MaxHealth <= 0)
				{
					counts.HiddenZeroHealth++;
					presenter.Hide(((EnemyHealthSample)(ref sample)).EnemyId);
					continue;
				}
				if (!config.ShowFullHealthEnemies && ((EnemyHealthSnapshot)(ref val3)).IsFullHealth)
				{
					counts.HiddenFull++;
					presenter.Hide(((EnemyHealthSample)(ref sample)).EnemyId);
					continue;
				}
				Vector3 worldPosition = presenter.GetWorldPosition(val2);
				Vector3 val4 = position - worldPosition;
				float sqrMagnitude = ((Vector3)(ref val4)).sqrMagnitude;
				if (!((HealthBarVisibilityRules)(ref val)).ShouldShowBySquaredDistance(val3, sqrMagnitude))
				{
					counts.HiddenDistance++;
					presenter.Hide(((EnemyHealthSample)(ref sample)).EnemyId);
				}
				else
				{
					counts.Shown++;
					presenter.ShowOrUpdate(val2, val3, worldPosition, camera, billboardRotation);
				}
			}
			tracker.KeepOnly((ISet<int>)activeEnemyIds);
			presenter.HideMissing(activeEnemyIds);
			counts.Active = presenter.ActiveCount;
			LogDiagnosticsIfDue(counts, camera, "scan");
		}

		private void LogDiagnosticsIfDue(DiagnosticScanCounts counts, Camera? camera, string source)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			if (config != null && logger != null && config.DiagnosticsEnabled && !(Time.unscaledTime < nextDiagnosticLogTime))
			{
				nextDiagnosticLogTime = Time.unscaledTime + config.DiagnosticsLogInterval;
				string text = (((Object)(object)camera != (Object)null) ? $"{((Object)camera).name}@{FormatVector(((Component)camera).transform.position)} mask={camera.cullingMask}" : "none");
				string arg = maxHealthResolver.FormatUnresolvedNamesForDiagnostics(5);
				logger.LogInfo((object)($"Diagnostics {source}: camera={text}, spawned={counts.Spawned}, readable={counts.Readable}, shown={counts.Shown}, active={counts.Active}, " + $"rejectedNull={counts.RejectedMissingEnemy}, rejectedNoType={counts.RejectedMissingEnemyType}, rejectedCannotDie={counts.RejectedCannotDie}, " + $"hiddenDead={counts.HiddenDead}, hiddenZeroHp={counts.HiddenZeroHealth}, hiddenFull={counts.HiddenFull}, hiddenDistance={counts.HiddenDistance}, " + $"unresolvedMaxHealth={maxHealthResolver.UnresolvedCount}, unresolvedNames={arg}"));
			}
		}

		private void UpdateDebugTestBar(Camera camera, Quaternion billboardRotation)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			if (config != null && presenter != null)
			{
				if (config.DebugShowTestBar)
				{
					presenter.ShowDebugTestBar(camera, billboardRotation);
				}
				else
				{
					presenter.HideDebugTestBar();
				}
			}
		}

		private void LogDebugHeartbeatIfDue(Camera? camera)
		{
			if (config != null && logger != null && presenter != null && config.DiagnosticsEnabled && !(Time.unscaledTime < nextDebugHeartbeatLogTime))
			{
				nextDebugHeartbeatLogTime = Time.unscaledTime + config.DiagnosticsLogInterval;
				string text = (((Object)(object)camera != (Object)null) ? ((Object)camera).name : "none");
				logger.LogInfo((object)$"Debug heartbeat: enabled={config.Enabled}, camera='{text}', active={presenter.ActiveCount}, testBar={presenter.DebugTestBarVisible}");
			}
		}

		private static void AddReadFailure(ref DiagnosticScanCounts counts, EnemyHealthReadFailure failure)
		{
			switch (failure)
			{
			case EnemyHealthReadFailure.MissingEnemy:
				counts.RejectedMissingEnemy++;
				break;
			case EnemyHealthReadFailure.MissingEnemyType:
				counts.RejectedMissingEnemyType++;
				break;
			case EnemyHealthReadFailure.CannotDie:
				counts.RejectedCannotDie++;
				break;
			}
		}

		private static string FormatVector(Vector3 value)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			return $"({value.x:0.0},{value.y:0.0},{value.z:0.0})";
		}
	}
	internal sealed class HealthBarRuntime : IDisposable
	{
		private static GameObject? runtimeObject;

		private static EnemyHealthBarController? runtimeController;

		private readonly GameObject host;

		private HealthBarRuntime(GameObject host)
		{
			this.host = host;
		}

		public static HealthBarRuntime Start(ModConfig config, ManualLogSource logger)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			if ((Object)(object)runtimeObject == (Object)null)
			{
				runtimeObject = new GameObject("EnemyHealthBars.Runtime");
				((Object)runtimeObject).hideFlags = (HideFlags)61;
				Object.DontDestroyOnLoad((Object)(object)runtimeObject);
			}
			else if (!runtimeObject.activeSelf)
			{
				runtimeObject.SetActive(true);
			}
			GameObject val = runtimeObject;
			runtimeController = val.GetComponent<EnemyHealthBarController>();
			if ((Object)(object)runtimeController == (Object)null)
			{
				EnemyHealthBarPresenter presenter = new EnemyHealthBarPresenter(config, val.transform);
				runtimeController = val.AddComponent<EnemyHealthBarController>();
				runtimeController.Initialize(config, logger, new RoundManagerEnemySource(), new LocalPlayerCameraProvider(), presenter);
				logger.LogInfo((object)"Runtime controller active.");
			}
			else
			{
				runtimeController.SetEnabled(enabled: true);
				logger.LogInfo((object)"Runtime controller active.");
			}
			return new HealthBarRuntime(val);
		}

		public void Dispose()
		{
			if ((Object)(object)host != (Object)null)
			{
				Object.Destroy((Object)(object)host);
			}
		}
	}
}
namespace Auuueser.EnemyHealthBars.Presentation
{
	internal sealed class EnemyHealthBarPresenter
	{
		private sealed class ActiveHealthBar
		{
			public EnemyAI Enemy { get; set; }

			public EnemyHealthSnapshot Snapshot { get; set; }

			public HealthBarView View { get; }

			public ActiveHealthBar(EnemyAI enemy, HealthBarView view)
			{
				Enemy = enemy;
				View = view;
			}
		}

		private readonly ModConfig config;

		private readonly HealthBarPool pool;

		private readonly HealthBarTargetResolver targetResolver = new HealthBarTargetResolver();

		private readonly Dictionary<int, ActiveHealthBar> activeBars = new Dictionary<int, ActiveHealthBar>();

		private readonly List<int> removeBuffer = new List<int>();

		private HealthBarView? debugTestBar;

		public int ActiveCount => activeBars.Count;

		public bool DebugTestBarVisible => debugTestBar != null;

		public EnemyHealthBarPresenter(ModConfig config, Transform parent)
		{
			this.config = config;
			pool = new HealthBarPool(config, parent);
		}

		public void ShowOrUpdate(EnemyAI enemy, EnemyHealthSnapshot snapshot, Vector3 worldPosition, Camera camera, Quaternion billboardRotation)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: 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_005e: Unknown result type (might be due to invalid IL or missing references)
			if (!activeBars.TryGetValue(((EnemyHealthSnapshot)(ref snapshot)).EnemyId, out ActiveHealthBar value))
			{
				value = new ActiveHealthBar(enemy, pool.Get());
				activeBars.Add(((EnemyHealthSnapshot)(ref snapshot)).EnemyId, value);
			}
			value.Enemy = enemy;
			value.Snapshot = snapshot;
			value.View.SetHealth(snapshot);
			value.View.SetWorldPosition(worldPosition, camera, billboardRotation);
			value.View.SetVisible(visible: true);
		}

		public void Hide(int enemyId)
		{
			if (activeBars.TryGetValue(enemyId, out ActiveHealthBar value))
			{
				activeBars.Remove(enemyId);
				pool.Release(value.View);
			}
		}

		public void HideMissing(ISet<int> activeEnemyIds)
		{
			removeBuffer.Clear();
			foreach (KeyValuePair<int, ActiveHealthBar> activeBar in activeBars)
			{
				if (!activeEnemyIds.Contains(activeBar.Key))
				{
					removeBuffer.Add(activeBar.Key);
				}
			}
			foreach (int item in removeBuffer)
			{
				Hide(item);
			}
		}

		public void RefreshStyle()
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			HealthBarStyle style = HealthBarStyle.FromConfig(config);
			pool.RefreshStyle(style);
			debugTestBar?.ApplyStyle(style);
			foreach (KeyValuePair<int, ActiveHealthBar> activeBar in activeBars)
			{
				activeBar.Value.View.ApplyStyle(style);
				activeBar.Value.View.SetHealth(activeBar.Value.Snapshot);
			}
		}

		public void ShowDebugTestBar(Camera camera, Quaternion billboardRotation)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: 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)
			if (debugTestBar == null)
			{
				debugTestBar = pool.Get();
				debugTestBar.SetVisible(visible: true);
			}
			Vector3 worldPosition = ((Component)camera).transform.position + ((Component)camera).transform.forward * 2.25f - ((Component)camera).transform.up * 0.35f;
			debugTestBar.SetVisible(visible: true);
			debugTestBar.SetHealth(new EnemyHealthSnapshot(-1, "Test enemy", 13, 20, false));
			debugTestBar.SetWorldPosition(worldPosition, camera, billboardRotation);
		}

		public void HideDebugTestBar()
		{
			if (debugTestBar != null)
			{
				pool.Release(debugTestBar);
				debugTestBar = null;
			}
		}

		public void UpdateActive(Camera camera, Quaternion billboardRotation)
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: 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_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			removeBuffer.Clear();
			foreach (KeyValuePair<int, ActiveHealthBar> activeBar in activeBars)
			{
				ActiveHealthBar value = activeBar.Value;
				if ((Object)(object)value.Enemy == (Object)null)
				{
					removeBuffer.Add(activeBar.Key);
					continue;
				}
				Vector3 worldPosition = targetResolver.GetWorldPosition(value.Enemy, config.VerticalOffset, config.DisplayMode);
				value.View.SetWorldPosition(worldPosition, camera, billboardRotation);
			}
			foreach (int item in removeBuffer)
			{
				Hide(item);
			}
		}

		public void Clear()
		{
			HideDebugTestBar();
			removeBuffer.Clear();
			foreach (int key in activeBars.Keys)
			{
				removeBuffer.Add(key);
			}
			foreach (int item in removeBuffer)
			{
				Hide(item);
			}
		}

		public Vector3 GetWorldPosition(EnemyAI enemy)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			return targetResolver.GetWorldPosition(enemy, config.VerticalOffset, config.DisplayMode);
		}
	}
	internal sealed class HealthBarBillboard : MonoBehaviour
	{
		public void SetCamera(Camera? camera)
		{
		}

		public void ApplyRotation(Quaternion rotation)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			((Component)this).transform.rotation = rotation;
		}

		public static Quaternion CalculateRotation(Camera camera)
		{
			//IL_0007: 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_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			Transform transform = ((Component)camera).transform;
			Vector3 forward = transform.forward;
			Vector3 up = transform.up;
			if (forward == Vector3.zero || up == Vector3.zero)
			{
				return Quaternion.identity;
			}
			return Quaternion.LookRotation(forward, up);
		}
	}
	internal sealed class HealthBarPool
	{
		private readonly ModConfig config;

		private readonly Transform parent;

		private readonly Stack<HealthBarView> pooledViews = new Stack<HealthBarView>();

		private HealthBarStyle currentStyle;

		public HealthBarPool(ModConfig config, Transform parent)
		{
			this.config = config;
			this.parent = parent;
			currentStyle = HealthBarStyle.FromConfig(config);
		}

		public HealthBarView Get()
		{
			if (pooledViews.Count > 0)
			{
				HealthBarView healthBarView = pooledViews.Pop();
				healthBarView.ApplyStyle(currentStyle);
				return healthBarView;
			}
			return HealthBarView.Create(parent, currentStyle);
		}

		public void Release(HealthBarView view)
		{
			view.SetVisible(visible: false);
			pooledViews.Push(view);
		}

		public void RefreshStyle(HealthBarStyle style)
		{
			currentStyle = style;
			foreach (HealthBarView pooledView in pooledViews)
			{
				pooledView.ApplyStyle(style);
			}
		}
	}
	internal sealed class HealthBarStyle
	{
		private const float PixelUnitScale = 140f;

		private const float WorldScaleMultiplier = 0.012f;

		public float Width { get; }

		public float Height { get; }

		public float WorldScale { get; }

		public HealthBarDisplayMode DisplayMode { get; }

		public bool ShowHealthNumbers { get; }

		public HealthTextFormat HealthTextFormat { get; }

		public bool ShowEnemyName { get; }

		public float SideBarWidth { get; }

		public float SideBarHeight { get; }

		public float SideBarHorizontalOffset { get; }

		public Color BackgroundColor { get; } = new Color(0.02f, 0.02f, 0.02f, 0.78f);


		public Color FillColor { get; } = new Color(0.78f, 0.08f, 0.07f, 0.92f);


		public Color BorderColor { get; } = new Color(0f, 0f, 0f, 0.95f);


		public Color TextColor { get; } = new Color(0.96f, 0.96f, 0.96f, 1f);


		private HealthBarStyle(float width, float height, float worldScale, HealthBarDisplayMode displayMode, bool showHealthNumbers, HealthTextFormat healthTextFormat, bool showEnemyName, float sideBarWidth, float sideBarHeight, float sideBarHorizontalOffset)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: 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_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: 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_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: 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_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			Width = width;
			Height = height;
			WorldScale = worldScale;
			DisplayMode = displayMode;
			ShowHealthNumbers = showHealthNumbers;
			HealthTextFormat = healthTextFormat;
			ShowEnemyName = showEnemyName;
			SideBarWidth = sideBarWidth;
			SideBarHeight = sideBarHeight;
			SideBarHorizontalOffset = sideBarHorizontalOffset;
		}

		public static HealthBarStyle FromConfig(ModConfig config)
		{
			//IL_0025: 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)
			return new HealthBarStyle(config.BarWidth * 140f, config.BarHeight * 140f, config.WorldScale * 0.012f, config.DisplayMode, config.ShowHealthNumbers, config.HealthTextFormat, config.ShowEnemyName, config.SideBarWidth * 140f, config.SideBarHeight * 140f, config.SideBarHorizontalOffset * 140f);
		}
	}
	internal sealed class HealthBarTargetResolver
	{
		public Vector3 GetWorldPosition(EnemyAI enemy, float verticalOffset, HealthBarDisplayMode displayMode)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: 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_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: 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_0042: 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_0081: 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)
			if ((int)displayMode == 1)
			{
				return GetVerticalSideWorldPosition(enemy, verticalOffset);
			}
			if (TryGetMaskedStableAnchor(enemy, out var anchor))
			{
				return anchor + Vector3.up * verticalOffset;
			}
			if (TryGetRendererBounds(enemy, out var bounds))
			{
				float num = (((Object)(object)enemy.eye != (Object)null) ? Mathf.Max(enemy.eye.position.y, ((Bounds)(ref bounds)).max.y) : ((Bounds)(ref bounds)).max.y);
				return new Vector3(((Bounds)(ref bounds)).center.x, num + verticalOffset, ((Bounds)(ref bounds)).center.z);
			}
			if ((Object)(object)enemy.eye != (Object)null)
			{
				return enemy.eye.position + Vector3.up * verticalOffset;
			}
			return ((Component)enemy).transform.position + Vector3.up * (1.5f + verticalOffset);
		}

		private static Vector3 GetVerticalSideWorldPosition(EnemyAI enemy, float verticalOffset)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: 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)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: 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_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_0085: 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)
			float num = verticalOffset * 0.25f;
			if (TryGetMaskedStableAnchor(enemy, out var anchor))
			{
				return anchor + Vector3.up * (num - 0.35f);
			}
			if (TryGetRendererBounds(enemy, out var bounds))
			{
				return new Vector3(((Bounds)(ref bounds)).center.x, ((Bounds)(ref bounds)).center.y + num, ((Bounds)(ref bounds)).center.z);
			}
			if ((Object)(object)enemy.eye != (Object)null)
			{
				return enemy.eye.position + Vector3.up * (num - 0.35f);
			}
			return ((Component)enemy).transform.position + Vector3.up * (1f + num);
		}

		private static bool TryGetMaskedStableAnchor(EnemyAI enemy, out Vector3 anchor)
		{
			//IL_0001: 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)
			anchor = default(Vector3);
			if (!(enemy is MaskedPlayerEnemy))
			{
				return false;
			}
			Transform radarHeadTransform = enemy.GetRadarHeadTransform();
			if ((Object)(object)radarHeadTransform == (Object)null)
			{
				return false;
			}
			anchor = radarHeadTransform.position;
			return true;
		}

		private static bool TryGetRendererBounds(EnemyAI enemy, out Bounds bounds)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			bounds = default(Bounds);
			bool flag = false;
			if (enemy.skinnedMeshRenderers != null)
			{
				SkinnedMeshRenderer[] skinnedMeshRenderers = enemy.skinnedMeshRenderers;
				for (int i = 0; i < skinnedMeshRenderers.Length; i++)
				{
					flag = EncapsulateRendererBounds((Renderer?)(object)skinnedMeshRenderers[i], ref bounds, flag);
				}
			}
			if (enemy.meshRenderers != null)
			{
				MeshRenderer[] meshRenderers = enemy.meshRenderers;
				for (int i = 0; i < meshRenderers.Length; i++)
				{
					flag = EncapsulateRendererBounds((Renderer?)(object)meshRenderers[i], ref bounds, flag);
				}
			}
			return flag;
		}

		private static bool EncapsulateRendererBounds(Renderer? renderer, ref Bounds bounds, bool hasBounds)
		{
			//IL_001e: 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_0015: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)renderer == (Object)null)
			{
				return hasBounds;
			}
			if (!hasBounds)
			{
				bounds = renderer.bounds;
				return true;
			}
			((Bounds)(ref bounds)).Encapsulate(renderer.bounds);
			return true;
		}
	}
	internal sealed class HealthBarView
	{
		private const float FillPadding = 1f;

		private const float SideTextPadding = 6f;

		private const float TextFontSize = 18f;

		private static TMP_FontAsset? resolvedTextFont;

		private static bool textFontResolved;

		private readonly GameObject gameObject;

		private readonly RectTransform root;

		private readonly Image backgroundImage;

		private readonly Image fillImage;

		private readonly Outline border;

		private readonly RectTransform backgroundRect;

		private readonly RectTransform fillRect;

		private readonly TextMeshProUGUI healthText;

		private readonly RectTransform textRect;

		private readonly Canvas worldCanvas;

		private readonly HealthBarBillboard billboard;

		private Camera? lastAppliedCanvasCamera;

		private HealthBarStyle currentStyle;

		private int lastTextCurrentHealth = int.MinValue;

		private int lastTextMaxHealth = int.MinValue;

		private HealthTextFormat lastTextFormat = (HealthTextFormat)(-1);

		private HealthBarDisplayMode lastDisplayMode = (HealthBarDisplayMode)(-1);

		private HealthBarDisplayMode lastFillDisplayMode = (HealthBarDisplayMode)(-1);

		private float lastFillFraction = -1f;

		private bool lastShowEnemyName;

		private string? lastDisplayName;

		private HealthBarView(GameObject gameObject, RectTransform root, Image backgroundImage, Image fillImage, Outline border, RectTransform backgroundRect, RectTransform fillRect, TextMeshProUGUI healthText, RectTransform textRect, Canvas worldCanvas, HealthBarBillboard billboard, HealthBarStyle style)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			this.gameObject = gameObject;
			this.root = root;
			this.backgroundImage = backgroundImage;
			this.fillImage = fillImage;
			this.border = border;
			this.backgroundRect = backgroundRect;
			this.fillRect = fillRect;
			this.healthText = healthText;
			this.textRect = textRect;
			this.worldCanvas = worldCanvas;
			this.billboard = billboard;
			currentStyle = style;
		}

		public static HealthBarView Create(Transform parent, HealthBarStyle style)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: 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_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: 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)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Expected O, but got Unknown
			//IL_00d2: Expected O, but got Unknown
			GameObject val = new GameObject("EnemyHealthBar");
			val.transform.SetParent(parent, false);
			HealthBarBillboard healthBarBillboard = val.AddComponent<HealthBarBillboard>();
			RectTransform parent2 = val.AddComponent<RectTransform>();
			Canvas val2 = val.AddComponent<Canvas>();
			val2.renderMode = (RenderMode)2;
			val2.overrideSorting = true;
			val2.sortingOrder = 500;
			val.AddComponent<CanvasScaler>();
			Image val3 = CreateImage("Background", (Transform)(object)parent2, style.BackgroundColor);
			RectTransform component = ((Component)val3).GetComponent<RectTransform>();
			Outline val4 = ((Component)val3).gameObject.AddComponent<Outline>();
			Image val5 = CreateImage("Fill", ((Component)val3).transform, style.FillColor);
			RectTransform component2 = ((Component)val5).GetComponent<RectTransform>();
			TextMeshProUGUI val6 = CreateHealthText((Transform)(object)parent2, style.TextColor);
			RectTransform component3 = ((Component)val6).GetComponent<RectTransform>();
			SetLayerRecursively(val, ((Component)parent).gameObject.layer);
			HealthBarView healthBarView = new HealthBarView(val, parent2, val3, val5, val4, component, component2, val6, component3, val2, healthBarBillboard, style);
			healthBarView.ApplyStyle(style);
			healthBarView.SetVisible(visible: false);
			return healthBarView;
		}

		public void ApplyStyle(HealthBarStyle style)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: 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_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: 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_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Invalid comparison between Unknown and I4
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Invalid comparison between Unknown and I4
			currentStyle = style;
			((Transform)root).localScale = Vector3.one * style.WorldScale;
			((Graphic)backgroundImage).color = style.BackgroundColor;
			((Shadow)border).effectColor = style.BorderColor;
			((Shadow)border).effectDistance = new Vector2(1f, -1f);
			((Graphic)fillImage).color = style.FillColor;
			((Graphic)healthText).color = style.TextColor;
			((TMP_Text)healthText).fontSize = 18f;
			((Component)healthText).gameObject.SetActive(ShouldShowText(style));
			HealthBarDisplayMode displayMode = style.DisplayMode;
			if ((int)displayMode != 1)
			{
				if ((int)displayMode == 2)
				{
					ApplyNumbersOnlyLayout(style);
				}
				else
				{
					ApplyHorizontalLayout(style);
				}
			}
			else
			{
				ApplyVerticalSideLayout(style);
			}
			ResetTextCache();
		}

		public void SetFill(float fraction)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Invalid comparison between Unknown and I4
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Invalid comparison between Unknown and I4
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: 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_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			if (fraction < 0f)
			{
				fraction = 0f;
			}
			else if (fraction > 1f)
			{
				fraction = 1f;
			}
			if (lastFillFraction == fraction && lastFillDisplayMode == currentStyle.DisplayMode)
			{
				return;
			}
			if ((int)currentStyle.DisplayMode == 2)
			{
				SetActiveIfChanged(((Component)backgroundImage).gameObject, active: false);
				lastFillFraction = fraction;
				lastFillDisplayMode = currentStyle.DisplayMode;
				return;
			}
			SetActiveIfChanged(((Component)backgroundImage).gameObject, active: true);
			if ((int)currentStyle.DisplayMode == 1)
			{
				fillRect.SetSizeWithCurrentAnchors((Axis)1, Mathf.Lerp(0f, Mathf.Max(0f, currentStyle.SideBarHeight - 2f), fraction));
				lastFillFraction = fraction;
				lastFillDisplayMode = currentStyle.DisplayMode;
			}
			else
			{
				fillRect.SetSizeWithCurrentAnchors((Axis)0, Mathf.Lerp(0f, Mathf.Max(0f, currentStyle.Width - 2f), fraction));
				lastFillFraction = fraction;
				lastFillDisplayMode = currentStyle.DisplayMode;
			}
		}

		public void SetHealth(EnemyHealthSnapshot snapshot)
		{
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: 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_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			SetFill(((EnemyHealthSnapshot)(ref snapshot)).HealthFraction);
			if (!ShouldShowText(currentStyle))
			{
				if (((TMP_Text)healthText).text.Length > 0)
				{
					((TMP_Text)healthText).text = string.Empty;
				}
				return;
			}
			string text = ((EnemyHealthSnapshot)(ref snapshot)).DisplayName ?? string.Empty;
			if (lastTextCurrentHealth != ((EnemyHealthSnapshot)(ref snapshot)).CurrentHealth || lastTextMaxHealth != ((EnemyHealthSnapshot)(ref snapshot)).MaxHealth || lastTextFormat != currentStyle.HealthTextFormat || lastDisplayMode != currentStyle.DisplayMode || lastShowEnemyName != currentStyle.ShowEnemyName || !(lastDisplayName == text))
			{
				string text2 = HealthTextFormatter.Format(((EnemyHealthSnapshot)(ref snapshot)).CurrentHealth, ((EnemyHealthSnapshot)(ref snapshot)).MaxHealth, currentStyle.HealthTextFormat);
				if (currentStyle.ShowEnemyName && text.Length > 0)
				{
					text2 = text + " " + text2;
				}
				((TMP_Text)healthText).text = text2;
				lastTextCurrentHealth = ((EnemyHealthSnapshot)(ref snapshot)).CurrentHealth;
				lastTextMaxHealth = ((EnemyHealthSnapshot)(ref snapshot)).MaxHealth;
				lastTextFormat = currentStyle.HealthTextFormat;
				lastDisplayMode = currentStyle.DisplayMode;
				lastShowEnemyName = currentStyle.ShowEnemyName;
				lastDisplayName = text;
			}
		}

		public void SetWorldPosition(Vector3 worldPosition, Camera camera, Quaternion billboardRotation)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			((Transform)root).position = worldPosition;
			ApplyCanvasCamera(camera);
			billboard.ApplyRotation(billboardRotation);
		}

		public void SetVisible(bool visible)
		{
			SetActiveIfChanged(gameObject, visible);
		}

		private void ApplyHorizontalLayout(HealthBarStyle style)
		{
			//IL_0012: 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_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: 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_009a: 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_00d4: Unknown result type (might be due to invalid IL or missing references)
			root.sizeDelta = new Vector2(style.Width, style.Height);
			SetActiveIfChanged(((Component)backgroundImage).gameObject, active: true);
			SetRect(backgroundRect, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(style.Width, style.Height));
			SetRect(fillRect, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(1f, 0f), new Vector2(0f, Mathf.Max(0f, style.Height - 2f)));
			fillRect.pivot = new Vector2(0f, 0.5f);
			fillRect.SetSizeWithCurrentAnchors((Axis)0, 0f);
			SetFullTextRect();
		}

		private void ApplyVerticalSideLayout(HealthBarStyle style)
		{
			//IL_005f: 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_0099: 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_00ba: 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_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			float verticalSideTextWidth = GetVerticalSideTextWidth(style);
			float num = Mathf.Abs(style.SideBarHorizontalOffset) + style.SideBarWidth * 0.5f + 6f + verticalSideTextWidth;
			float num2 = Mathf.Max(style.Width, num * 2f);
			float num3 = Mathf.Max(style.SideBarHeight, Mathf.Max(style.Height, 24f));
			root.sizeDelta = new Vector2(num2, num3);
			SetActiveIfChanged(((Component)backgroundImage).gameObject, active: true);
			SetRect(backgroundRect, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(style.SideBarHorizontalOffset, 0f), new Vector2(style.SideBarWidth, style.SideBarHeight));
			SetRect(fillRect, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 1f), new Vector2(Mathf.Max(0f, style.SideBarWidth - 2f), 0f));
			fillRect.pivot = new Vector2(0.5f, 0f);
			fillRect.SetSizeWithCurrentAnchors((Axis)1, 0f);
			SetVerticalSideTextRect(style);
		}

		private void ApplyNumbersOnlyLayout(HealthBarStyle style)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			root.sizeDelta = new Vector2(style.Width, Mathf.Max(style.Height, 24f));
			SetActiveIfChanged(((Component)backgroundImage).gameObject, active: false);
			SetFullTextRect();
		}

		private static Image CreateImage(string name, Transform parent, Color color)
		{
			//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_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			val.transform.SetParent(parent, false);
			val.AddComponent<RectTransform>();
			val.AddComponent<CanvasRenderer>();
			Image obj = val.AddComponent<Image>();
			((Graphic)obj).color = color;
			((Graphic)obj).raycastTarget = false;
			return obj;
		}

		private static TextMeshProUGUI CreateHealthText(Transform parent, Color color)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("HealthText");
			val.transform.SetParent(parent, false);
			val.AddComponent<RectTransform>();
			val.AddComponent<CanvasRenderer>();
			TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>();
			((TMP_Text)val2).text = string.Empty;
			((Graphic)val2).raycastTarget = false;
			((TMP_Text)val2).alignment = (TextAlignmentOptions)514;
			((TMP_Text)val2).enableWordWrapping = false;
			((TMP_Text)val2).enableAutoSizing = true;
			((TMP_Text)val2).fontSizeMin = 10f;
			((TMP_Text)val2).fontSizeMax = 18f;
			((TMP_Text)val2).overflowMode = (TextOverflowModes)1;
			((TMP_Text)val2).fontStyle = (FontStyles)1;
			((TMP_Text)val2).fontSize = 18f;
			((Graphic)val2).color = color;
			TMP_FontAsset val3 = ResolveTextFont();
			if ((Object)(object)val3 != (Object)null)
			{
				((TMP_Text)val2).font = val3;
			}
			return val2;
		}

		private static TMP_FontAsset? ResolveTextFont()
		{
			if (textFontResolved)
			{
				return resolvedTextFont;
			}
			textFontResolved = true;
			resolvedTextFont = TMP_Settings.defaultFontAsset;
			if ((Object)(object)resolvedTextFont != (Object)null)
			{
				return resolvedTextFont;
			}
			Font builtinResource = Resources.GetBuiltinResource<Font>("Arial.ttf");
			if ((Object)(object)builtinResource != (Object)null)
			{
				resolvedTextFont = TMP_FontAsset.CreateFontAsset(builtinResource);
			}
			return resolvedTextFont;
		}

		private void SetFullTextRect()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			textRect.anchorMin = Vector2.zero;
			textRect.anchorMax = Vector2.one;
			textRect.pivot = new Vector2(0.5f, 0.5f);
			textRect.offsetMin = Vector2.zero;
			textRect.offsetMax = Vector2.zero;
			((TMP_Text)healthText).alignment = (TextAlignmentOptions)514;
		}

		private void SetVerticalSideTextRect(HealthBarStyle style)
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: 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)
			float num = ((style.SideBarHorizontalOffset >= 0f) ? 1f : (-1f));
			float verticalSideTextWidth = GetVerticalSideTextWidth(style);
			float num2 = Mathf.Max(style.SideBarHeight, 24f);
			float num3 = style.SideBarHorizontalOffset + num * style.SideBarWidth * 0.5f + num * (6f + verticalSideTextWidth * 0.5f);
			SetRect(textRect, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(num3, 0f), new Vector2(verticalSideTextWidth, num2));
			((TMP_Text)healthText).alignment = (TextAlignmentOptions)((num > 0f) ? 513 : 516);
		}

		private static float GetVerticalSideTextWidth(HealthBarStyle style)
		{
			return Mathf.Max(style.Width, 72f);
		}

		private static void SetRect(RectTransform rect, Vector2 anchorMin, Vector2 anchorMax, Vector2 anchoredPosition, Vector2 sizeDelta)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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_002b: Unknown result type (might be due to invalid IL or missing references)
			rect.anchorMin = anchorMin;
			rect.anchorMax = anchorMax;
			rect.pivot = new Vector2(0.5f, 0.5f);
			rect.anchoredPosition = anchoredPosition;
			rect.sizeDelta = sizeDelta;
		}

		private static bool ShouldShowText(HealthBarStyle style)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			if (!style.ShowHealthNumbers)
			{
				return (int)style.DisplayMode == 2;
			}
			return true;
		}

		private void ResetTextCache()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			lastTextCurrentHealth = int.MinValue;
			lastTextMaxHealth = int.MinValue;
			lastTextFormat = (HealthTextFormat)(-1);
			lastDisplayMode = (HealthBarDisplayMode)(-1);
			lastFillDisplayMode = (HealthBarDisplayMode)(-1);
			lastFillFraction = -1f;
			lastShowEnemyName = false;
			lastDisplayName = null;
		}

		private void ApplyCanvasCamera(Camera camera)
		{
			billboard.SetCamera(camera);
			if (!((Object)(object)lastAppliedCanvasCamera == (Object)(object)camera) || !((Object)(object)worldCanvas.worldCamera == (Object)(object)camera))
			{
				worldCanvas.worldCamera = camera;
				lastAppliedCanvasCamera = camera;
			}
		}

		private static void SetLayerRecursively(GameObject target, int layer)
		{
			target.layer = layer;
			Transform transform = target.transform;
			for (int i = 0; i < transform.childCount; i++)
			{
				SetLayerRecursively(((Component)transform.GetChild(i)).gameObject, layer);
			}
		}

		private static void SetActiveIfChanged(GameObject target, bool active)
		{
			if (target.activeSelf != active)
			{
				target.SetActive(active);
			}
		}
	}
}
namespace Auuueser.EnemyHealthBars.Game
{
	internal enum EnemyHealthReadFailure
	{
		None,
		MissingEnemy,
		MissingEnemyType,
		CannotDie
	}
	internal sealed class EnemyHealthReader
	{
		public bool TryRead(EnemyAI? enemy, bool includeInvulnerableEnemies, EnemyMaxHealthResolver maxHealthResolver, out EnemyHealthSample sample, out EnemyHealthReadFailure failure)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: 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)
			sample = default(EnemyHealthSample);
			failure = EnemyHealthReadFailure.None;
			if ((Object)(object)enemy == (Object)null)
			{
				failure = EnemyHealthReadFailure.MissingEnemy;
				return false;
			}
			if ((Object)(object)enemy.enemyType == (Object)null)
			{
				failure = EnemyHealthReadFailure.MissingEnemyType;
				return false;
			}
			if (!includeInvulnerableEnemies && !enemy.enemyType.canDie)
			{
				failure = EnemyHealthReadFailure.CannotDie;
				return false;
			}
			int instanceID = ((Object)enemy).GetInstanceID();
			string text = ResolveDisplayName(enemy);
			int num = maxHealthResolver.Resolve(enemy, text);
			sample = new EnemyHealthSample(instanceID, text, enemy.enemyHP, num, enemy.isEnemyDead);
			return true;
		}

		private static string ResolveDisplayName(EnemyAI enemy)
		{
			EnemyType enemyType = enemy.enemyType;
			if ((Object)(object)enemyType != (Object)null && !string.IsNullOrWhiteSpace(enemyType.enemyName))
			{
				return enemyType.enemyName;
			}
			if (!string.IsNullOrWhiteSpace(((Object)enemy).name))
			{
				return ((Object)enemy).name;
			}
			return "Enemy";
		}
	}
	internal sealed class EnemyMaxHealthResolver
	{
		private readonly EnemyMaxHealthLookup maxHealthLookup = new EnemyMaxHealthLookup(EnemyHealthDefaults.Items);

		public int UnresolvedCount => maxHealthLookup.UnresolvedCount;

		public int Resolve(EnemyAI enemy, string displayName)
		{
			string cacheKey = ResolveCacheKey(enemy, displayName);
			int baseMaxHealth = ResolveBaseMaxHealth(enemy, cacheKey);
			return ResolveSpecialMaxHealth(enemy, baseMaxHealth);
		}

		public string FormatUnresolvedNamesForDiagnostics(int maxNames)
		{
			return maxHealthLookup.FormatUnresolvedNamesForDiagnostics(maxNames);
		}

		private int ResolveBaseMaxHealth(EnemyAI enemy, string cacheKey)
		{
			int result = default(int);
			if (maxHealthLookup.TryGetStrictMaxHealth(cacheKey, ref result))
			{
				return result;
			}
			if (maxHealthLookup.IsUnresolved(cacheKey))
			{
				return 0;
			}
			return maxHealthLookup.StoreResolvedStrictMaxHealth(cacheKey, ResolveFromPrefab(enemy));
		}

		private static int ResolveSpecialMaxHealth(EnemyAI enemy, int baseMaxHealth)
		{
			if (enemy is ButlerEnemyAI)
			{
				int num = ((!((Object)(object)StartOfRound.Instance != (Object)null)) ? 1 : StartOfRound.Instance.connectedPlayersAmount);
				return EnemySpecialMaxHealthRules.ResolveButlerMaxHealth(baseMaxHealth, num);
			}
			if (enemy is CaveDwellerAI)
			{
				return EnemySpecialMaxHealthRules.ResolveManeaterMaxHealth(baseMaxHealth, enemy.currentBehaviourStateIndex);
			}
			return baseMaxHealth;
		}

		private static string ResolveCacheKey(EnemyAI enemy, string displayName)
		{
			EnemyType enemyType = enemy.enemyType;
			if ((Object)(object)enemyType != (Object)null && !string.IsNullOrWhiteSpace(enemyType.enemyName))
			{
				return enemyType.enemyName;
			}
			if (!string.IsNullOrWhiteSpace(displayName))
			{
				return displayName;
			}
			if (!string.IsNullOrWhiteSpace(((Object)enemy).name))
			{
				return ((Object)enemy).name;
			}
			return string.Empty;
		}

		private static int ResolveFromPrefab(EnemyAI enemy)
		{
			EnemyType enemyType = enemy.enemyType;
			if ((Object)(object)enemyType == (Object)null || (Object)(object)enemyType.enemyPrefab == (Object)null)
			{
				return 0;
			}
			EnemyAI val = default(EnemyAI);
			if (!enemyType.enemyPrefab.TryGetComponent<EnemyAI>(ref val))
			{
				return 0;
			}
			if (val.enemyHP <= 0)
			{
				return 0;
			}
			return val.enemyHP;
		}
	}
	internal sealed class LocalPlayerCameraProvider
	{
		public Camera? GetActiveCamera()
		{
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance != (Object)null)
			{
				if ((Object)(object)instance.localPlayerController != (Object)null && IsUsable(instance.localPlayerController.gameplayCamera))
				{
					return instance.localPlayerController.gameplayCamera;
				}
				if (IsUsable(instance.activeCamera))
				{
					return instance.activeCamera;
				}
				if (IsUsable(instance.spectateCamera))
				{
					return instance.spectateCamera;
				}
			}
			HUDManager instance2 = HUDManager.Instance;
			if ((Object)(object)instance2 != (Object)null && (Object)(object)instance2.localPlayer != (Object)null && IsUsable(instance2.localPlayer.gameplayCamera))
			{
				return instance2.localPlayer.gameplayCamera;
			}
			if (!((Object)(object)Camera.main != (Object)null))
			{
				return Camera.current;
			}
			return Camera.main;
		}

		private static bool IsUsable(Camera? camera)
		{
			if ((Object)(object)camera != (Object)null)
			{
				return ((Behaviour)camera).isActiveAndEnabled;
			}
			return false;
		}
	}
	internal sealed class RoundManagerEnemySource
	{
		public List<EnemyAI>? GetSpawnedEnemies()
		{
			RoundManager instance = RoundManager.Instance;
			if (!((Object)(object)instance != (Object)null))
			{
				return null;
			}
			return instance.SpawnedEnemies;
		}
	}
}
namespace Auuueser.EnemyHealthBars.Configuration
{
	internal static class ChineseProjectLanguageDetector
	{
		public static ConfigLanguage Detect(ConfigFile config, ManualLogSource logger)
		{
			if (!DetectLoadedPlugin(logger) && !DetectExistingConfig(config, logger) && !DetectManifestNearConfig(config, logger))
			{
				return (ConfigLanguage)0;
			}
			return (ConfigLanguage)1;
		}

		private static bool DetectLoadedPlugin(ManualLogSource logger)
		{
			foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos)
			{
				PluginInfo value = pluginInfo.Value;
				if (value != null && value.Metadata != null && ChineseProjectDetection.IsChineseProjectPlugin(value.Metadata.GUID, value.Metadata.Name, value.Location))
				{
					logger.LogInfo((object)("LC Chinese Project detected from plugin '" + value.Metadata.GUID + "'. Using Chinese config text."));
					return true;
				}
			}
			return false;
		}

		private static bool DetectExistingConfig(ConfigFile config, ManualLogSource logger)
		{
			if (!File.Exists(config.ConfigFilePath))
			{
				return false;
			}
			try
			{
				if (!ChineseProjectDetection.ContainsChineseConfigSections(File.ReadAllText(config.ConfigFilePath)))
				{
					return false;
				}
				logger.LogInfo((object)"Existing Chinese config sections detected. Using Chinese config text.");
				return true;
			}
			catch (Exception ex)
			{
				logger.LogWarning((object)("Could not inspect existing config language: " + ex.Message));
				return false;
			}
		}

		private static bool DetectManifestNearConfig(ConfigFile config, ManualLogSource logger)
		{
			string directoryName = Path.GetDirectoryName(config.ConfigFilePath);
			if (string.IsNullOrEmpty(directoryName))
			{
				return false;
			}
			DirectoryInfo parent = Directory.GetParent(directoryName);
			if (parent == null)
			{
				return false;
			}
			string path = Path.Combine(parent.FullName, "plugins");
			if (!Directory.Exists(path))
			{
				return false;
			}
			try
			{
				string[] files = Directory.GetFiles(path, "manifest.json", SearchOption.AllDirectories);
				for (int i = 0; i < files.Length; i++)
				{
					if (ChineseProjectDetection.ContainsChineseProjectManifestText(File.ReadAllText(files[i])))
					{
						logger.LogInfo((object)("LC Chinese Project manifest detected at '" + files[i] + "'. Using Chinese config text."));
						return true;
					}
				}
			}
			catch (Exception ex)
			{
				logger.LogWarning((object)("Could not inspect plugin manifests for config language: " + ex.Message));
			}
			return false;
		}
	}
	internal static class LethalConfigIntegration
	{
		private const string PluginGuid = "ainavt.lc.lethalconfig";

		public static bool IsAvailable()
		{
			return Chainloader.PluginInfos.ContainsKey("ainavt.lc.lethalconfig");
		}

		public static void Register(ModConfig config, ManualLogSource logger)
		{
			try
			{
				ConfigTexts texts = config.Texts;
				LethalConfigManager.SetModDescription(texts.ModDescription);
				AddBool(config.EnabledEntry, texts.EnabledName, texts.GeneralSection, texts.EnabledDescription);
				AddBool(config.ShowInvulnerableEnemiesEntry, texts.ShowInvulnerableEnemiesName, texts.VisibilitySection, texts.ShowInvulnerableEnemiesDescription);
				AddEnum<MaxHealthMode>(config.MaxHealthModeEntry, texts.MaxHealthModeName, texts.VisibilitySection, texts.MaxHealthModeDescription);
				AddFloat(config.MaxDistanceEntry, texts.MaxDistanceName, texts.VisibilitySection, texts.MaxDistanceDescription);
				AddFloat(config.ScanIntervalEntry, texts.ScanIntervalName, texts.PerformanceSection, texts.ScanIntervalDescription);
				AddFloat(config.VerticalOffsetEntry, texts.VerticalOffsetName, texts.LayoutSection, texts.VerticalOffsetDescription);
				AddFloat(config.BarWidthEntry, texts.BarWidthName, texts.LayoutSection, texts.BarWidthDescription);
				AddFloat(config.BarHeightEntry, texts.BarHeightName, texts.LayoutSection, texts.BarHeightDescription);
				AddFloat(config.WorldScaleEntry, texts.WorldScaleName, texts.LayoutSection, texts.WorldScaleDescription);
				AddEnum<HealthBarDisplayMode>(config.DisplayModeEntry, texts.DisplayModeName, texts.LayoutSection, texts.DisplayModeDescription);
				AddBool(config.ShowHealthNumbersEntry, texts.ShowHealthNumbersName, texts.LayoutSection, texts.ShowHealthNumbersDescription);
				AddEnum<HealthTextFormat>(config.HealthTextFormatEntry, texts.HealthTextFormatName, texts.LayoutSection, texts.HealthTextFormatDescription);
				AddBool(config.ShowEnemyNameEntry, texts.ShowEnemyNameName, texts.LayoutSection, texts.ShowEnemyNameDescription);
				AddFloat(config.SideBarWidthEntry, texts.SideBarWidthName, texts.LayoutSection, texts.SideBarWidthDescription);
				AddFloat(config.SideBarHeightEntry, texts.SideBarHeightName, texts.LayoutSection, texts.SideBarHeightDescription);
				AddFloat(config.SideBarHorizontalOffsetEntry, texts.SideBarHorizontalOffsetName, texts.LayoutSection, texts.SideBarHorizontalOffsetDescription);
				AddBool(config.DebugEnabledEntry, texts.DebugEnabledName, texts.DebugSection, texts.DebugEnabledDescription);
				AddBool(config.DebugShowFullHealthEnemiesEntry, texts.DebugShowFullHealthEnemiesName, texts.DebugSection, texts.DebugShowFullHealthEnemiesDescription);
				AddBool(config.DebugDiagnosticsEnabledEntry, texts.DebugDiagnosticsEnabledName, texts.DebugSection, texts.DebugDiagnosticsEnabledDescription);
				AddFloat(config.DebugDiagnosticsLogIntervalEntry, texts.DebugDiagnosticsLogIntervalName, texts.DebugSection, texts.DebugDiagnosticsLogIntervalDescription);
				AddBool(config.DebugShowTestBarEntry, texts.DebugShowTestBarName, texts.DebugSection, texts.DebugShowTestBarDescription);
				logger.LogInfo((object)"Registered localized LethalConfig entries.");
			}
			catch (Exception ex)
			{
				logger.LogWarning((object)("Could not register LethalConfig entries: " + ex.Message));
			}
		}

		private static void AddBool(ConfigEntry<bool> entry, string name, string section, string description)
		{
			//IL_0007: 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_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			LethalConfigManager.SkipAutoGenFor((ConfigEntryBase)(object)entry);
			LethalConfigManager.AddConfigItem((BaseConfigItem)new BoolCheckBoxConfigItem(entry, new BoolCheckBoxOptions
			{
				Name = name,
				Section = section,
				Description = description,
				RequiresRestart = false
			}));
		}

		private static void AddFloat(ConfigEntry<float> entry, string name, string section, string description)
		{
			//IL_0007: 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_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			LethalConfigManager.SkipAutoGenFor((ConfigEntryBase)(object)entry);
			LethalConfigManager.AddConfigItem((BaseConfigItem)new FloatInputFieldConfigItem(entry, new FloatInputFieldOptions
			{
				Name = name,
				Section = section,
				Description = description,
				RequiresRestart = false
			}));
		}

		private static void AddEnum<T>(ConfigEntry<T> entry, string name, string section, string description) where T : Enum
		{
			//IL_0007: 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_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			LethalConfigManager.SkipAutoGenFor((ConfigEntryBase)(object)entry);
			LethalConfigManager.AddConfigItem((BaseConfigItem)(object)new EnumDropDownConfigItem<T>(entry, new EnumDropDownOptions
			{
				Name = name,
				Section = section,
				Description = description,
				RequiresRestart = false
			}));
		}
	}
	internal sealed class ModConfig
	{
		private const float ConfigFilePollInterval = 0.5f;

		private readonly ConfigFile configFile;

		private readonly string configFilePath;

		private readonly ConfigEntry<bool> enabled;

		private readonly ConfigEntry<bool> showInvulnerableEnemies;

		private readonly ConfigEntry<MaxHealthMode> maxHealthMode;

		private readonly ConfigEntry<float> maxDistance;

		private readonly ConfigEntry<float> scanInterval;

		private readonly ConfigEntry<float> verticalOffset;

		private readonly ConfigEntry<float> barWidth;

		private readonly ConfigEntry<float> barHeight;

		private readonly ConfigEntry<float> worldScale;

		private readonly ConfigEntry<HealthBarDisplayMode> displayMode;

		private readonly ConfigEntry<bool> showHealthNumbers;

		private readonly ConfigEntry<HealthTextFormat> healthTextFormat;

		private readonly ConfigEntry<bool> showEnemyName;

		private readonly ConfigEntry<float> sideBarWidth;

		private readonly ConfigEntry<float> sideBarHeight;

		private readonly ConfigEntry<float> sideBarHorizontalOffset;

		private readonly ConfigEntry<bool> debugEnabled;

		private readonly ConfigEntry<bool> debugShowFullHealthEnemies;

		private readonly ConfigEntry<bool> debugDiagnosticsEnabled;

		private readonly ConfigEntry<float> debugDiagnosticsLogInterval;

		private readonly ConfigEntry<bool> debugShowTestBar;

		private DateTime lastConfigWriteTimeUtc;

		private float nextConfigFilePollTime;

		public ConfigLanguage Language { get; }

		public ConfigTexts Texts { get; }

		public int SettingsVersion { get; private set; }

		public bool Enabled => enabled.Value;

		public bool ShowInvulnerableEnemies => showInvulnerableEnemies.Value;

		public MaxHealthMode MaxHealthMode => maxHealthMode.Value;

		public bool DebugEnabled => debugEnabled.Value;

		public bool DiagnosticsEnabled
		{
			get
			{
				if (DebugEnabled)
				{
					return debugDiagnosticsEnabled.Value;
				}
				return false;
			}
		}

		public bool ShowFullHealthEnemies
		{
			get
			{
				if (DebugEnabled)
				{
					return debugShowFullHealthEnemies.Value;
				}
				return false;
			}
		}

		public bool DebugShowTestBar
		{
			get
			{
				if (DebugEnabled)
				{
					return debugShowTestBar.Value;
				}
				return false;
			}
		}

		public float DiagnosticsLogInterval
		{
			get
			{
				if (!(debugDiagnosticsLogInterval.Value < 1f))
				{
					return debugDiagnosticsLogInterval.Value;
				}
				return 1f;
			}
		}

		public float ScanInterval
		{
			get
			{
				if (!(scanInterval.Value < 0.02f))
				{
					return scanInterval.Value;
				}
				return 0.02f;
			}
		}

		public float VerticalOffset => verticalOffset.Value;

		public float BarWidth
		{
			get
			{
				if (!(barWidth.Value < 0.1f))
				{
					return barWidth.Value;
				}
				return 0.1f;
			}
		}

		public float BarHeight
		{
			get
			{
				if (!(barHeight.Value < 0.03f))
				{
					return barHeight.Value;
				}
				return 0.03f;
			}
		}

		public float WorldScale
		{
			get
			{
				if (!(worldScale.Value < 0.01f))
				{
					return worldScale.Value;
				}
				return 0.01f;
			}
		}

		public HealthBarDisplayMode DisplayMode => displayMode.Value;

		public bool ShowHealthNumbers => showHealthNumbers.Value;

		public HealthTextFormat HealthTextFormat => healthTextFormat.Value;

		public bool ShowEnemyName => showEnemyName.Value;

		public float SideBarWidth
		{
			get
			{
				if (!(sideBarWidth.Value < 0.03f))
				{
					return sideBarWidth.Value;
				}
				return 0.03f;
			}
		}

		public float SideBarHeight
		{
			get
			{
				if (!(sideBarHeight.Value < 0.1f))
				{
					return sideBarHeight.Value;
				}
				return 0.1f;
			}
		}

		public float SideBarHorizontalOffset => sideBarHorizontalOffset.Value;

		internal ConfigEntry<bool> EnabledEntry => enabled;

		internal ConfigEntry<bool> ShowInvulnerableEnemiesEntry => showInvulnerableEnemies;

		internal ConfigEntry<MaxHealthMode> MaxHealthModeEntry => maxHealthMode;

		internal ConfigEntry<float> MaxDistanceEntry => maxDistance;

		internal ConfigEntry<float> ScanIntervalEntry => scanInterval;

		internal ConfigEntry<float> VerticalOffsetEntry => verticalOffset;

		internal ConfigEntry<float> BarWidthEntry => barWidth;

		internal ConfigEntry<float> BarHeightEntry => barHeight;

		internal ConfigEntry<float> WorldScaleEntry => worldScale;

		internal ConfigEntry<HealthBarDisplayMode> DisplayModeEntry => displayMode;

		internal ConfigEntry<bool> ShowHealthNumbersEntry => showHealthNumbers;

		internal ConfigEntry<HealthTextFormat> HealthTextFormatEntry => healthTextFormat;

		internal ConfigEntry<bool> ShowEnemyNameEntry => showEnemyName;

		internal ConfigEntry<float> SideBarWidthEntry => sideBarWidth;

		internal ConfigEntry<float> SideBarHeightEntry => sideBarHeight;

		internal ConfigEntry<float> SideBarHorizontalOffsetEntry => sideBarHorizontalOffset;

		internal ConfigEntry<bool> DebugEnabledEntry => debugEnabled;

		internal ConfigEntry<bool> DebugShowFullHealthEnemiesEntry => debugShowFullHealthEnemies;

		internal ConfigEntry<bool> DebugDiagnosticsEnabledEntry => debugDiagnosticsEnabled;

		internal ConfigEntry<float> DebugDiagnosticsLogIntervalEntry => debugDiagnosticsLogInterval;

		internal ConfigEntry<bool> DebugShowTestBarEntry => debugShowTestBar;

		private ModConfig(ConfigFile config, ConfigLanguage language)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			configFile = config;
			configFilePath = config.ConfigFilePath;
			Language = language;
			Texts = ConfigTextCatalog.Get(language);
			enabled = config.Bind<bool>(Texts.GeneralSection, "Enabled", true, Texts.EnabledDescription);
			showInvulnerableEnemies = config.Bind<bool>(Texts.VisibilitySection, "ShowInvulnerableEnemies", false, Texts.ShowInvulnerableEnemiesDescription);
			maxHealthMode = config.Bind<MaxHealthMode>(Texts.VisibilitySection, "MaxHealthMode", (MaxHealthMode)2, Texts.MaxHealthModeDescription);
			maxDistance = config.Bind<float>(Texts.VisibilitySection, "MaxDistance", 35f, Texts.MaxDistanceDescription);
			scanInterval = config.Bind<float>(Texts.PerformanceSection, "ScanInterval", 0.2f, Texts.ScanIntervalDescription);
			verticalOffset = config.Bind<float>(Texts.LayoutSection, "VerticalOffset", 0.45f, Texts.VerticalOffsetDescription);
			barWidth = config.Bind<float>(Texts.LayoutSection, "BarWidth", 1.25f, Texts.BarWidthDescription);
			barHeight = config.Bind<float>(Texts.LayoutSection, "BarHeight", 0.14f, Texts.BarHeightDescription);
			worldScale = config.Bind<float>(Texts.LayoutSection, "WorldScale", 0.7f, Texts.WorldScaleDescription);
			displayMode = config.Bind<HealthBarDisplayMode>(Texts.LayoutSection, "DisplayMode", (HealthBarDisplayMode)0, Texts.DisplayModeDescription);
			showHealthNumbers = config.Bind<bool>(Texts.LayoutSection, "ShowHealthNumbers", true, Texts.ShowHealthNumbersDescription);
			healthTextFormat = config.Bind<HealthTextFormat>(Texts.LayoutSection, "HealthTextFormat", (HealthTextFormat)0, Texts.HealthTextFormatDescription);
			showEnemyName = config.Bind<bool>(Texts.LayoutSection, "ShowEnemyName", false, Texts.ShowEnemyNameDescription);
			sideBarWidth = config.Bind<float>(Texts.LayoutSection, "SideBarWidth", 0.14f, Texts.SideBarWidthDescription);
			sideBarHeight = config.Bind<float>(Texts.LayoutSection, "SideBarHeight", 0.55f, Texts.SideBarHeightDescription);
			sideBarHorizontalOffset = config.Bind<float>(Texts.LayoutSection, "SideBarHorizontalOffset", 0.55f, Texts.SideBarHorizontalOffsetDescription);
			debugEnabled = config.Bind<bool>(Texts.DebugSection, "Enabled", false, Texts.DebugEnabledDescription);
			debugShowFullHealthEnemies = config.Bind<bool>(Texts.DebugSection, "ShowFullHealthEnemies", false, Texts.DebugShowFullHealthEnemiesDescription);
			debugDiagnosticsEnabled = config.Bind<bool>(Texts.DebugSection, "DiagnosticsEnabled", false, Texts.DebugDiagnosticsEnabledDescription);
			debugDiagnosticsLogInterval = config.Bind<float>(Texts.DebugSection, "DiagnosticsLogInterval", 3f, Texts.DebugDiagnosticsLogIntervalDescription);
			debugShowTestBar = config.Bind<bool>(Texts.DebugSection, "ShowTestBar", false, Texts.DebugShowTestBarDescription);
			config.SettingChanged += OnConfigChanged;
			config.ConfigReloaded += OnConfigReloaded;
			config.Save();
			RefreshLastWriteTime();
		}

		public static ModConfig Bind(ConfigFile config, ConfigLanguage language)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return new ModConfig(config, language);
		}

		public HealthBarVisibilityRules CreateVisibilityRules()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			return new HealthBarVisibilityRules(ShowFullHealthEnemies, maxDistance.Value);
		}

		public void ReloadIfChangedOnDisk(float currentTime)
		{
			if (!(currentTime < nextConfigFilePollTime) && File.Exists(configFilePath))
			{
				nextConfigFilePollTime = currentTime + 0.5f;
				if (!(File.GetLastWriteTimeUtc(configFilePath) <= lastConfigWriteTimeUtc))
				{
					configFile.Reload();
					RefreshLastWriteTime();
				}
			}
		}

		private void OnConfigChanged(object sender, SettingChangedEventArgs args)
		{
			SettingsVersion++;
			RefreshLastWriteTime();
		}

		private void OnConfigReloaded(object sender, EventArgs args)
		{
			SettingsVersion++;
			RefreshLastWriteTime();
		}

		private void RefreshLastWriteTime()
		{
			if (File.Exists(configFilePath))
			{
				lastConfigWriteTimeUtc = File.GetLastWriteTimeUtc(configFilePath);
			}
		}
	}
}

BepInEx/plugins/EnemyHealthBar/EnemyHealthBars.Core.dll

Decompiled 8 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using Auuueser.EnemyHealthBars.Core.Configuration;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("EnemyHealthBars.Core")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+acb061bd561295755ea67507eed9dd688145cd1f")]
[assembly: AssemblyProduct("EnemyHealthBars.Core")]
[assembly: AssemblyTitle("EnemyHealthBars.Core")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace Auuueser.EnemyHealthBars.Core.Presentation
{
	public static class HealthTextFormatter
	{
		public static string Format(int currentHealth, int maxHealth, HealthTextFormat format)
		{
			if (currentHealth < 0)
			{
				currentHealth = 0;
			}
			if (maxHealth < 0)
			{
				maxHealth = 0;
			}
			switch (format)
			{
			case HealthTextFormat.CurrentOnly:
				return currentHealth.ToString();
			case HealthTextFormat.PercentOnly:
			{
				if (maxHealth <= 0)
				{
					return "0%";
				}
				int num = (int)Math.Round((double)currentHealth * 100.0 / (double)maxHealth);
				if (num < 0)
				{
					num = 0;
				}
				else if (num > 100)
				{
					num = 100;
				}
				return num + "%";
			}
			default:
				return currentHealth + " / " + maxHealth;
			}
		}
	}
}
namespace Auuueser.EnemyHealthBars.Core.GameData
{
	public readonly record struct EnemyHealthDefault(string EnemyName, int MaxHealth);
	public static class EnemyHealthDefaults
	{
		public const string Source = "Lethal Company_Data; Unity 2022.3.62f2; Assembly-CSharp.dll; assets=41; generator=tools/generate_enemy_health_defaults.py";

		public static readonly EnemyHealthDefault[] Items = new EnemyHealthDefault[33]
		{
			new EnemyHealthDefault("Baboon hawk", 4),
			new EnemyHealthDefault("Blob", 3),
			new EnemyHealthDefault("Bunker Spider", 5),
			new EnemyHealthDefault("Bush Wolf", 7),
			new EnemyHealthDefault("Butler", 8),
			new EnemyHealthDefault("Butler Bees", 3),
			new EnemyHealthDefault("Cadaver Bloom", 4),
			new EnemyHealthDefault("Cadaver Growths", 3),
			new EnemyHealthDefault("Centipede", 3),
			new EnemyHealthDefault("Clay Surgeon", 3),
			new EnemyHealthDefault("Crawler", 4),
			new EnemyHealthDefault("Docile Locust Bees", 3),
			new EnemyHealthDefault("Earth Leviathan", 3),
			new EnemyHealthDefault("Feiopar", 4),
			new EnemyHealthDefault("Flowerman", 5),
			new EnemyHealthDefault("ForestGiant", 38),
			new EnemyHealthDefault("GiantKiwi", 28),
			new EnemyHealthDefault("Girl", 3),
			new EnemyHealthDefault("Hoarding bug", 3),
			new EnemyHealthDefault("Jester", 3),
			new EnemyHealthDefault("Lasso", 3),
			new EnemyHealthDefault("Maneater", 5),
			new EnemyHealthDefault("Manticoil", 2),
			new EnemyHealthDefault("Masked", 4),
			new EnemyHealthDefault("MouthDog", 12),
			new EnemyHealthDefault("Nutcracker", 5),
			new EnemyHealthDefault("Puffer", 3),
			new EnemyHealthDefault("RadMech", 3),
			new EnemyHealthDefault("Red Locust Bees", 3),
			new EnemyHealthDefault("Red pill", 3),
			new EnemyHealthDefault("Spring", 3),
			new EnemyHealthDefault("Stingray", 4),
			new EnemyHealthDefault("Tulip Snake", 3)
		};
	}
	public sealed class EnemyMaxHealthLookup
	{
		private readonly Dictionary<string, int> maxHealthByName = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<string> unresolvedNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		public int UnresolvedCount => unresolvedNames.Count;

		public EnemyMaxHealthLookup(EnemyHealthDefault[] defaults)
		{
			if (defaults == null)
			{
				throw new ArgumentNullException("defaults");
			}
			for (int i = 0; i < defaults.Length; i++)
			{
				EnemyHealthDefault enemyHealthDefault = defaults[i];
				if (!string.IsNullOrWhiteSpace(enemyHealthDefault.EnemyName) && enemyHealthDefault.MaxHealth > 0)
				{
					maxHealthByName[enemyHealthDefault.EnemyName] = enemyHealthDefault.MaxHealth;
				}
			}
		}

		public int StoreResolvedStrictMaxHealth(string? key, int resolvedMaxHealth)
		{
			if (string.IsNullOrWhiteSpace(key))
			{
				return 0;
			}
			if (resolvedMaxHealth > 0)
			{
				unresolvedNames.Remove(key);
				maxHealthByName[key] = resolvedMaxHealth;
				return resolvedMaxHealth;
			}
			unresolvedNames.Add(key);
			return 0;
		}

		public bool TryGetStrictMaxHealth(string? key, out int maxHealth)
		{
			maxHealth = 0;
			if (!string.IsNullOrWhiteSpace(key))
			{
				return maxHealthByName.TryGetValue(key, out maxHealth);
			}
			return false;
		}

		public bool IsUnresolved(string? key)
		{
			if (!string.IsNullOrWhiteSpace(key))
			{
				return unresolvedNames.Contains(key);
			}
			return false;
		}

		public string FormatUnresolvedNamesForDiagnostics(int maxNames)
		{
			if (unresolvedNames.Count == 0)
			{
				return "none";
			}
			if (maxNames < 1)
			{
				maxNames = 1;
			}
			StringBuilder stringBuilder = new StringBuilder();
			int num = 0;
			foreach (string unresolvedName in unresolvedNames)
			{
				if (num >= maxNames)
				{
					break;
				}
				if (num > 0)
				{
					stringBuilder.Append(", ");
				}
				stringBuilder.Append(unresolvedName);
				num++;
			}
			if (unresolvedNames.Count > num)
			{
				stringBuilder.Append(", +");
				stringBuilder.Append(unresolvedNames.Count - num);
				stringBuilder.Append(" more");
			}
			return stringBuilder.ToString();
		}
	}
	public static class EnemySpecialMaxHealthRules
	{
		private const int ButlerSinglePlayerMaxHealth = 2;

		private const int ManeaterBabyKillableMaxHealth = -1;

		public static int ResolveButlerMaxHealth(int generatedMaxHealth, int connectedPlayersAmount)
		{
			if (connectedPlayersAmount != 0)
			{
				return generatedMaxHealth;
			}
			return 2;
		}

		public static int ResolveManeaterMaxHealth(int generatedMaxHealth, int currentBehaviourStateIndex)
		{
			if (currentBehaviourStateIndex != 0)
			{
				return generatedMaxHealth;
			}
			return -1;
		}
	}
}
namespace Auuueser.EnemyHealthBars.Core.Domain
{
	public readonly record struct EnemyHealthSample(int EnemyId, string DisplayName, int CurrentHealth, int MaxHealth, bool IsDead)
	{
		public EnemyHealthSample(int EnemyId, string DisplayName, int CurrentHealth, int MaxHealth, bool IsDead)
		{
			this.EnemyId = EnemyId;
			this.DisplayName = DisplayName;
			this.CurrentHealth = CurrentHealth;
			this.MaxHealth = MaxHealth;
			this.IsDead = IsDead;
		}

		public EnemyHealthSample(int EnemyId, string DisplayName, int CurrentHealth, bool IsDead)
			: this(EnemyId, DisplayName, CurrentHealth, 0, IsDead)
		{
		}
	}
	public readonly record struct EnemyHealthSnapshot(int EnemyId, string DisplayName, int CurrentHealth, int MaxHealth, bool IsDead)
	{
		public float HealthFraction
		{
			get
			{
				if (MaxHealth <= 0)
				{
					return 0f;
				}
				float num = (float)CurrentHealth / (float)MaxHealth;
				if (num < 0f)
				{
					return 0f;
				}
				if (!(num > 1f))
				{
					return num;
				}
				return 1f;
			}
		}

		public bool IsFullHealth
		{
			get
			{
				if (!IsDead && MaxHealth > 0)
				{
					return CurrentHealth >= MaxHealth;
				}
				return false;
			}
		}
	}
	public sealed class EnemyHealthTracker
	{
		private readonly Dictionary<int, ObservedEnemyHealth> trackedEnemies = new Dictionary<int, ObservedEnemyHealth>();

		private readonly List<int> removedEnemyIds = new List<int>();

		public EnemyHealthSnapshot Track(EnemyHealthSample sample, MaxHealthMode maxHealthMode = MaxHealthMode.VanillaStrict)
		{
			if (!trackedEnemies.TryGetValue(sample.EnemyId, out ObservedEnemyHealth value))
			{
				value = ObservedEnemyHealth.FromSample(sample.CurrentHealth);
				trackedEnemies.Add(sample.EnemyId, value);
			}
			else
			{
				value.ApplySample(sample.CurrentHealth);
			}
			int currentHealth = value.CurrentHealth;
			int maxHealth = ResolveMaxHealth(sample, value, maxHealthMode);
			return new EnemyHealthSnapshot(sample.EnemyId, sample.DisplayName, currentHealth, maxHealth, sample.IsDead);
		}

		private static int ResolveMaxHealth(EnemyHealthSample sample, ObservedEnemyHealth observedHealth, MaxHealthMode maxHealthMode)
		{
			if (sample.MaxHealth < 0)
			{
				return 0;
			}
			if (maxHealthMode == MaxHealthMode.Adaptive || maxHealthMode == MaxHealthMode.Hybrid)
			{
				return observedHealth.MaxObservedHealth;
			}
			int num = ((sample.MaxHealth > 0) ? sample.MaxHealth : observedHealth.MaxObservedHealth);
			if (num >= observedHealth.CurrentHealth)
			{
				return num;
			}
			return observedHealth.CurrentHealth;
		}

		public void Remove(int enemyId)
		{
			trackedEnemies.Remove(enemyId);
		}

		public void KeepOnly(ISet<int> activeEnemyIds)
		{
			removedEnemyIds.Clear();
			foreach (int key in trackedEnemies.Keys)
			{
				if (!activeEnemyIds.Contains(key))
				{
					removedEnemyIds.Add(key);
				}
			}
			foreach (int removedEnemyId in removedEnemyIds)
			{
				trackedEnemies.Remove(removedEnemyId);
			}
		}
	}
	public readonly struct HealthBarVisibilityRules
	{
		private readonly bool showWhenFullHealth;

		private readonly float maxDistance;

		private readonly float maxDistanceSquared;

		public HealthBarVisibilityRules(bool showWhenFullHealth, float maxDistance)
		{
			this.showWhenFullHealth = showWhenFullHealth;
			this.maxDistance = maxDistance;
			maxDistanceSquared = ((maxDistance > 0f) ? (maxDistance * maxDistance) : 0f);
		}

		public bool ShouldShow(EnemyHealthSnapshot snapshot, float distanceToCamera)
		{
			if (snapshot.IsDead || snapshot.CurrentHealth <= 0 || snapshot.MaxHealth <= 0)
			{
				return false;
			}
			if (!showWhenFullHealth && snapshot.IsFullHealth)
			{
				return false;
			}
			return IsWithinSquaredDistance(distanceToCamera * distanceToCamera);
		}

		public bool ShouldShowBySquaredDistance(EnemyHealthSnapshot snapshot, float squaredDistanceToCamera)
		{
			if (snapshot.IsDead || snapshot.CurrentHealth <= 0 || snapshot.MaxHealth <= 0)
			{
				return false;
			}
			if (!showWhenFullHealth && snapshot.IsFullHealth)
			{
				return false;
			}
			return IsWithinSquaredDistance(squaredDistanceToCamera);
		}

		public bool IsWithinSquaredDistance(float squaredDistanceToCamera)
		{
			if (!(maxDistance <= 0f))
			{
				return squaredDistanceToCamera <= maxDistanceSquared;
			}
			return true;
		}
	}
	public sealed class ObservedEnemyHealth
	{
		public int CurrentHealth { get; private set; }

		public int MaxObservedHealth { get; private set; }

		public float HealthFraction
		{
			get
			{
				if (MaxObservedHealth <= 0)
				{
					return 0f;
				}
				float num = (float)CurrentHealth / (float)MaxObservedHealth;
				if (num < 0f)
				{
					return 0f;
				}
				if (!(num > 1f))
				{
					return num;
				}
				return 1f;
			}
		}

		private ObservedEnemyHealth(int currentHealth, int maxObservedHealth)
		{
			CurrentHealth = currentHealth;
			MaxObservedHealth = maxObservedHealth;
		}

		public static ObservedEnemyHealth FromSample(int currentHealth)
		{
			int num = NormalizeHealth(currentHealth);
			return new ObservedEnemyHealth(num, num);
		}

		public void ApplySample(int currentHealth)
		{
			CurrentHealth = NormalizeHealth(currentHealth);
			if (CurrentHealth > MaxObservedHealth)
			{
				MaxObservedHealth = CurrentHealth;
			}
		}

		private static int NormalizeHealth(int health)
		{
			if (health >= 0)
			{
				return health;
			}
			return 0;
		}
	}
}
namespace Auuueser.EnemyHealthBars.Core.Configuration
{
	public static class ChineseProjectDetection
	{
		private const string CurrentKnownGuid = "cn.codex.v81testchn";

		private const string PackageName = "LC_Chinese_Project";

		private const string RepositoryUrl = "github.com/Auuueser/LC-Chinese-Project";

		public static bool IsChineseProjectPlugin(string? pluginGuid, string? pluginName, string? pluginLocation)
		{
			if (EqualsIgnoreCase(pluginGuid, "cn.codex.v81testchn"))
			{
				return true;
			}
			if (ContainsIgnoreCase(pluginName, "LC Chinese Project") || ContainsIgnoreCase(pluginName, "Chinese Project") || ContainsIgnoreCase(pluginName, "V81 TEST CHN"))
			{
				return true;
			}
			if (!ContainsIgnoreCase(pluginLocation, "LC_Chinese_Project") && !ContainsIgnoreCase(pluginLocation, "LC-Chinese-Project"))
			{
				return ContainsIgnoreCase(pluginLocation, "LC Chinese Project");
			}
			return true;
		}

		public static bool IsChineseProjectManifest(string? packageName, string? websiteUrl)
		{
			if (!EqualsIgnoreCase(packageName, "LC_Chinese_Project") && !ContainsIgnoreCase(packageName, "LC Chinese Project") && !ContainsIgnoreCase(packageName, "LC-Chinese-Project"))
			{
				return ContainsIgnoreCase(websiteUrl, "github.com/Auuueser/LC-Chinese-Project");
			}
			return true;
		}

		public static bool ContainsChineseProjectManifestText(string? manifestText)
		{
			if (!ContainsIgnoreCase(manifestText, "LC_Chinese_Project") && !ContainsIgnoreCase(manifestText, "LC Chinese Project") && !ContainsIgnoreCase(manifestText, "LC-Chinese-Project"))
			{
				return ContainsIgnoreCase(manifestText, "github.com/Auuueser/LC-Chinese-Project");
			}
			return true;
		}

		public static bool ContainsChineseConfigSections(string? configText)
		{
			if (!ContainsIgnoreCase(configText, "[通用]") && !ContainsIgnoreCase(configText, "[可见性]"))
			{
				return ContainsIgnoreCase(configText, "[诊断]");
			}
			return true;
		}

		private static bool EqualsIgnoreCase(string? value, string expected)
		{
			return string.Equals(value, expected, StringComparison.OrdinalIgnoreCase);
		}

		private static bool ContainsIgnoreCase(string? value, string expected)
		{
			if (value != null)
			{
				return value.IndexOf(expected, StringComparison.OrdinalIgnoreCase) >= 0;
			}
			return false;
		}
	}
	public enum ConfigLanguage
	{
		English,
		Chinese
	}
	public static class ConfigTextCatalog
	{
		private static readonly ConfigTexts English = new ConfigTexts("General", "Visibility", "Performance", "Diagnostics", "Layout", "Debug", "Client-side enemy health bars. All settings apply immediately after saving.", "Enabled", "Show full-health enemies", "Show invulnerable enemies", "Max health mode", "Maximum display distance", "Scan interval", "Enable diagnostic logs", "Diagnostic log interval", "Vertical offset", "Bar width", "Bar height", "World scale", "Display mode", "Show health numbers", "Health text format", "Show enemy name", "Side bar width", "Side bar height", "Side bar horizontal offset", "Enable debug mode", "Show full-health enemies", "Enable diagnostic logs", "Diagnostic log interval", "Show test bar", "Enable enemy health bars. Applied immediately.", "Show bars for enemies that have not taken damage yet. Applied immediately.", "Show bars for enemies whose EnemyType cannot die. Applied immediately.", "How maximum health is resolved: VanillaStrict keeps base-game defaults, Adaptive uses observed instance health, Hybrid keeps special vanilla rules and adapts to observed modded health. Applied immediately.", "Maximum distance for rendering enemy health bars. Set 0 or below for no distance limit. Applied immediately.", "Seconds between enemy list refreshes. Applied immediately.", "Log low-frequency health bar scan counters for troubleshooting. Applied immediately.", "Seconds between diagnostic log lines when diagnostics are enabled. Applied immediately.", "World-space offset above the enemy eye transform. Applied immediately.", "World-space canvas width before scale is applied. Applied immediately.", "World-space canvas height before scale is applied. Applied immediately.", "World-space canvas scale. Applied immediately.", "Health bar layout mode: horizontal bar, side vertical bar, or numbers only. Applied immediately.", "Show current and max health text on enemy health bars. Applied immediately.", "Health text format: Current / max, current only, or percent only. Applied immediately.", "Show the enemy name beside the health number text. Applied immediately.", "Vertical side-bar width before scale is applied. Applied immediately.", "Vertical side-bar height before scale is applied. Applied immediately.", "Horizontal offset used by side vertical bar mode. Applied immediately.", "Enable test-only debug features. Keep this disabled during normal play. Applied immediately.", "Debug only: show bars for full-health enemies to verify enemy tracking. Requires debug mode. Applied immediately.", "Debug only: log low-frequency runtime, camera, scan, and active-bar counters. Requires debug mode. Applied immediately.", "Debug only: seconds between diagnostic log lines. Applied immediately.", "Debug only: render a fixed test health bar in front of the active camera. Requires debug mode. Applied immediately.");

		private static readonly ConfigTexts Chinese = new ConfigTexts("通用", "可见性", "性能", "诊断", "布局", "调试", "显示怪物血条的纯客户端模组。所有配置保存后立即生效。", "启用", "显示满血怪物", "显示不可死亡怪物", "最大血量模式", "最大显示距离", "扫描间隔", "启用诊断日志", "诊断日志间隔", "垂直偏移", "血条宽度", "血条高度", "世界缩放", "显示模式", "显示血量数字", "血量文字格式", "显示怪物名称", "侧边血条宽度", "侧边血条高度", "侧边血条水平偏移", "启用调试模式", "显示满血怪物", "启用诊断日志", "诊断日志间隔", "显示测试血条", "启用怪物血条。保存后立即生效。", "显示未受伤怪物的满血血条。保存后立即生效。", "显示 EnemyType 标记为不可死亡的怪物血条。保存后立即生效。", "最大血量计算方式:VanillaStrict 使用原版默认值,Adaptive 使用实际观察到的怪物血量,Hybrid 保留原版特殊规则并适应模组修改后的血量。保存后立即生效。", "血条最大显示距离。设置为 0 或更低表示不限制距离。保存后立即生效。", "刷新怪物列表的间隔秒数。保存后立即生效。", "输出低频血条扫描计数日志,用于排查问题。保存后立即生效。", "启用诊断时,两次诊断日志之间的秒数。保存后立即生效。", "血条相对怪物眼部锚点的世界空间高度偏移。保存后立即生效。", "应用缩放前的世界空间血条宽度。保存后立即生效。", "应用缩放前的世界空间血条高度。保存后立即生效。", "世界空间血条缩放。保存后立即生效。", "血条布局模式:横向血条、身旁竖条或纯数字。保存后立即生效。", "在怪物血条上显示当前血量和满血血量文字。保存后立即生效。", "血量文字格式:当前 / 满血、仅当前血量或仅百分比。保存后立即生效。", "在血量数字旁显示怪物名称。保存后立即生效。", "应用缩放前的竖向侧边血条宽度。保存后立即生效。", "应用缩放前的竖向侧边血条高度。保存后立即生效。", "身旁竖条模式使用的水平偏移。保存后立即生效。", "启用仅用于测试的调试功能。正常游玩时应保持关闭。保存后立即生效。", "仅调试:显示满血怪物血条,用于确认怪物追踪是否工作。需要启用调试模式。保存后立即生效。", "仅调试:输出低频运行时、相机、扫描和活动血条计数日志。需要启用调试模式。保存后立即生效。", "仅调试:两次诊断日志之间的秒数。保存后立即生效。", "仅调试:在活动相机前方渲染固定测试血条。需要启用调试模式。保存后立即生效。");

		public static ConfigTexts Get(ConfigLanguage language)
		{
			if (language != ConfigLanguage.Chinese)
			{
				return English;
			}
			return Chinese;
		}
	}
	public sealed class ConfigTexts
	{
		public string GeneralSection { get; }

		public string VisibilitySection { get; }

		public string PerformanceSection { get; }

		public string DiagnosticsSection { get; }

		public string LayoutSection { get; }

		public string DebugSection { get; }

		public string ModDescription { get; }

		public string EnabledName { get; }

		public string ShowWhenFullHealthName { get; }

		public string ShowInvulnerableEnemiesName { get; }

		public string MaxHealthModeName { get; }

		public string MaxDistanceName { get; }

		public string ScanIntervalName { get; }

		public string DiagnosticsEnabledName { get; }

		public string DiagnosticsLogIntervalName { get; }

		public string VerticalOffsetName { get; }

		public string BarWidthName { get; }

		public string BarHeightName { get; }

		public string WorldScaleName { get; }

		public string DisplayModeName { get; }

		public string ShowHealthNumbersName { get; }

		public string HealthTextFormatName { get; }

		public string ShowEnemyNameName { get; }

		public string SideBarWidthName { get; }

		public string SideBarHeightName { get; }

		public string SideBarHorizontalOffsetName { get; }

		public string DebugEnabledName { get; }

		public string DebugShowFullHealthEnemiesName { get; }

		public string DebugDiagnosticsEnabledName { get; }

		public string DebugDiagnosticsLogIntervalName { get; }

		public string DebugShowTestBarName { get; }

		public string EnabledDescription { get; }

		public string ShowWhenFullHealthDescription { get; }

		public string ShowInvulnerableEnemiesDescription { get; }

		public string MaxHealthModeDescription { get; }

		public string MaxDistanceDescription { get; }

		public string ScanIntervalDescription { get; }

		public string DiagnosticsEnabledDescription { get; }

		public string DiagnosticsLogIntervalDescription { get; }

		public string VerticalOffsetDescription { get; }

		public string BarWidthDescription { get; }

		public string BarHeightDescription { get; }

		public string WorldScaleDescription { get; }

		public string DisplayModeDescription { get; }

		public string ShowHealthNumbersDescription { get; }

		public string HealthTextFormatDescription { get; }

		public string ShowEnemyNameDescription { get; }

		public string SideBarWidthDescription { get; }

		public string SideBarHeightDescription { get; }

		public string SideBarHorizontalOffsetDescription { get; }

		public string DebugEnabledDescription { get; }

		public string DebugShowFullHealthEnemiesDescription { get; }

		public string DebugDiagnosticsEnabledDescription { get; }

		public string DebugDiagnosticsLogIntervalDescription { get; }

		public string DebugShowTestBarDescription { get; }

		public ConfigTexts(string generalSection, string visibilitySection, string performanceSection, string diagnosticsSection, string layoutSection, string debugSection, string modDescription, string enabledName, string showWhenFullHealthName, string showInvulnerableEnemiesName, string maxHealthModeName, string maxDistanceName, string scanIntervalName, string diagnosticsEnabledName, string diagnosticsLogIntervalName, string verticalOffsetName, string barWidthName, string barHeightName, string worldScaleName, string displayModeName, string showHealthNumbersName, string healthTextFormatName, string showEnemyNameName, string sideBarWidthName, string sideBarHeightName, string sideBarHorizontalOffsetName, string debugEnabledName, string debugShowFullHealthEnemiesName, string debugDiagnosticsEnabledName, string debugDiagnosticsLogIntervalName, string debugShowTestBarName, string enabledDescription, string showWhenFullHealthDescription, string showInvulnerableEnemiesDescription, string maxHealthModeDescription, string maxDistanceDescription, string scanIntervalDescription, string diagnosticsEnabledDescription, string diagnosticsLogIntervalDescription, string verticalOffsetDescription, string barWidthDescription, string barHeightDescription, string worldScaleDescription, string displayModeDescription, string showHealthNumbersDescription, string healthTextFormatDescription, string showEnemyNameDescription, string sideBarWidthDescription, string sideBarHeightDescription, string sideBarHorizontalOffsetDescription, string debugEnabledDescription, string debugShowFullHealthEnemiesDescription, string debugDiagnosticsEnabledDescription, string debugDiagnosticsLogIntervalDescription, string debugShowTestBarDescription)
		{
			GeneralSection = generalSection;
			VisibilitySection = visibilitySection;
			PerformanceSection = performanceSection;
			DiagnosticsSection = diagnosticsSection;
			LayoutSection = layoutSection;
			DebugSection = debugSection;
			ModDescription = modDescription;
			EnabledName = enabledName;
			ShowWhenFullHealthName = showWhenFullHealthName;
			ShowInvulnerableEnemiesName = showInvulnerableEnemiesName;
			MaxHealthModeName = maxHealthModeName;
			MaxDistanceName = maxDistanceName;
			ScanIntervalName = scanIntervalName;
			DiagnosticsEnabledName = diagnosticsEnabledName;
			DiagnosticsLogIntervalName = diagnosticsLogIntervalName;
			VerticalOffsetName = verticalOffsetName;
			BarWidthName = barWidthName;
			BarHeightName = barHeightName;
			WorldScaleName = worldScaleName;
			DisplayModeName = displayModeName;
			ShowHealthNumbersName = showHealthNumbersName;
			HealthTextFormatName = healthTextFormatName;
			ShowEnemyNameName = showEnemyNameName;
			SideBarWidthName = sideBarWidthName;
			SideBarHeightName = sideBarHeightName;
			SideBarHorizontalOffsetName = sideBarHorizontalOffsetName;
			DebugEnabledName = debugEnabledName;
			DebugShowFullHealthEnemiesName = debugShowFullHealthEnemiesName;
			DebugDiagnosticsEnabledName = debugDiagnosticsEnabledName;
			DebugDiagnosticsLogIntervalName = debugDiagnosticsLogIntervalName;
			DebugShowTestBarName = debugShowTestBarName;
			EnabledDescription = enabledDescription;
			ShowWhenFullHealthDescription = showWhenFullHealthDescription;
			ShowInvulnerableEnemiesDescription = showInvulnerableEnemiesDescription;
			MaxHealthModeDescription = maxHealthModeDescription;
			MaxDistanceDescription = maxDistanceDescription;
			ScanIntervalDescription = scanIntervalDescription;
			DiagnosticsEnabledDescription = diagnosticsEnabledDescription;
			DiagnosticsLogIntervalDescription = diagnosticsLogIntervalDescription;
			VerticalOffsetDescription = verticalOffsetDescription;
			BarWidthDescription = barWidthDescription;
			BarHeightDescription = barHeightDescription;
			WorldScaleDescription = worldScaleDescription;
			DisplayModeDescription = displayModeDescription;
			ShowHealthNumbersDescription = showHealthNumbersDescription;
			HealthTextFormatDescription = healthTextFormatDescription;
			ShowEnemyNameDescription = showEnemyNameDescription;
			SideBarWidthDescription = sideBarWidthDescription;
			SideBarHeightDescription = sideBarHeightDescription;
			SideBarHorizontalOffsetDescription = sideBarHorizontalOffsetDescription;
			DebugEnabledDescription = debugEnabledDescription;
			DebugShowFullHealthEnemiesDescription = debugShowFullHealthEnemiesDescription;
			DebugDiagnosticsEnabledDescription = debugDiagnosticsEnabledDescription;
			DebugDiagnosticsLogIntervalDescription = debugDiagnosticsLogIntervalDescription;
			DebugShowTestBarDescription = debugShowTestBarDescription;
		}
	}
	public enum HealthBarDisplayMode
	{
		HorizontalBar,
		VerticalSideBar,
		NumbersOnly
	}
	public enum HealthTextFormat
	{
		CurrentAndMax,
		CurrentOnly,
		PercentOnly
	}
	public enum MaxHealthMode
	{
		VanillaStrict,
		Adaptive,
		Hybrid
	}
}
namespace System.Runtime.CompilerServices
{
	internal static class IsExternalInit
	{
	}
}