Decompiled source of HealthNumbers v0.4.4

plugins/MycopunkHealthNumbers/MycopunkHealthNumbers.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using MycopunkShared;
using Pigeon.Movement;
using TMPro;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("MycopunkHealthNumbers")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.4.4.0")]
[assembly: AssemblyInformationalVersion("0.4.4+fe8cef1291afd0a7f7ea8a9ddfd28d3607c4cdd5")]
[assembly: AssemblyProduct("MycopunkHealthNumbers")]
[assembly: AssemblyTitle("MycopunkHealthNumbers")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.4.4.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace MycopunkHealthNumbers
{
	internal sealed class BossHealthLabel : MonoBehaviour
	{
		[HarmonyPatch(typeof(BossHealthbar), "Update")]
		private static class BossBarPatch
		{
			[HarmonyPostfix]
			private static void Postfix(BossHealthbar __instance)
			{
				Attach((MonoBehaviour)(object)__instance);
			}
		}

		[HarmonyPatch(typeof(RhombominationHealthbar), "Update")]
		private static class RhomboBarPatch
		{
			[HarmonyPostfix]
			private static void Postfix(RhombominationHealthbar __instance)
			{
				Attach((MonoBehaviour)(object)__instance);
			}
		}

		private readonly HealthText _text = new HealthText();

		private TextMeshProUGUI _label;

		private RectTransform _parent;

		private string _lastApproxSource;

		private string _lastApproxText;

		private bool _failed;

		internal static void Install(Harmony harmony)
		{
			Install(harmony, typeof(BossBarPatch), typeof(BossHealthbar));
			Install(harmony, typeof(RhomboBarPatch), typeof(RhombominationHealthbar));
		}

		private static void Install(Harmony harmony, Type patch, Type type)
		{
			try
			{
				harmony.CreateClassProcessor(patch).Patch();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Boss health unavailable for " + type.Name + ": " + ex.Message));
			}
		}

		private static void Attach(MonoBehaviour __instance)
		{
			if ((Object)(object)__instance != (Object)null && (Object)(object)((Component)__instance).GetComponent<BossHealthLabel>() == (Object)null)
			{
				((Component)__instance).gameObject.AddComponent<BossHealthLabel>();
			}
		}

		private void LateUpdate()
		{
			if (_failed)
			{
				return;
			}
			try
			{
				Refresh();
			}
			catch (Exception ex)
			{
				_failed = true;
				if ((Object)(object)_label != (Object)null)
				{
					((Component)_label).gameObject.SetActive(false);
				}
				Plugin.Log.LogWarning((object)("Boss health label disabled: " + ex.Message));
			}
		}

		private void Refresh()
		{
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_0380: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03da: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_042e: Unknown result type (might be due to invalid IL or missing references)
			Settings settings = Plugin.Settings;
			if (settings == null || !settings.Enabled || !settings.Boss || HudVisibility.ResultsOpen())
			{
				Hide();
				return;
			}
			Graphic val = null;
			TextMeshProUGUI val2 = null;
			float num = 0f;
			float num2 = 0f;
			bool flag = false;
			bool flag2 = false;
			BossHealthbar component = ((Component)this).GetComponent<BossHealthbar>();
			if ((Object)(object)component != (Object)null)
			{
				EnemyBrain target = component.target;
				if ((Object)(object)target == (Object)null)
				{
					Hide();
					return;
				}
				val = component.healthbar;
				val2 = component.nameText;
				AmalgamationHealthbar val3 = (AmalgamationHealthbar)(object)((component is AmalgamationHealthbar) ? component : null);
				if (val3 != null)
				{
					bool canBeKilled = val3.canBeKilled;
					if (canBeKilled)
					{
						val = val3.totalHealthbar;
					}
					if (!canBeKilled)
					{
						AmalgamationBehaviour component2 = ((Component)target).GetComponent<AmalgamationBehaviour>();
						if ((Object)(object)component2 == (Object)null)
						{
							Hide();
							return;
						}
						AmalgamationGlobalEvent val4 = (AmalgamationGlobalEvent)(((Object)(object)Global.Instance != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null);
						num2 = (((Object)(object)val4 != (Object)null) ? ((float)val4.CurrentMaxInstanceDamage) : 1000f);
						num = Mathf.Max(0f, num2 - component2.DamageTaken);
					}
				}
				if (num2 <= 0f)
				{
					EnemyCore core = target.Core;
					if ((Object)(object)core == (Object)null)
					{
						Hide();
						return;
					}
					if ((Object)(object)target.EnemyClass != (Object)null && !target.EnemyClass.config.onlyUseCoreHealthForHealthbar && (Object)(object)val != (Object)null)
					{
						float num3 = Mathf.Clamp01(((Transform)val.rectTransform).localScale.x);
						float num4 = (((NetworkBehaviour)target).IsServer ? target.totalMaxHealth : 0f);
						if (!((NetworkBehaviour)target).IsServer && Plugin.BossNetwork != null && Plugin.BossNetwork.TryGet(target, out var max, out var ratio))
						{
							num4 = max;
							num3 = ratio;
						}
						if (num4 > 0f && !float.IsNaN(num4) && !float.IsInfinity(num4))
						{
							num2 = num4;
							num = num3 * num2;
							flag = true;
						}
						else
						{
							num2 = 100f;
							num = num3 * num2;
							flag2 = true;
						}
					}
					else
					{
						num = ((EnemyPart)core).Health;
						num2 = ((EnemyPart)core).MaxHealth;
					}
				}
			}
			else
			{
				RhombominationHealthbar component3 = ((Component)this).GetComponent<RhombominationHealthbar>();
				if ((Object)(object)component3 == (Object)null)
				{
					Hide();
					return;
				}
				Rhombomination source = component3.source;
				ObjectTarget val5 = (((Object)(object)source != (Object)null) ? source.Core : null);
				if ((Object)(object)val5 == (Object)null)
				{
					Hide();
					return;
				}
				val = component3.healthbar;
				num = val5.Health;
				num2 = val5.MaxHealth;
			}
			if (!((Object)(object)val == (Object)null) && ((Behaviour)val).isActiveAndEnabled)
			{
				Transform parent = ((Transform)val.rectTransform).parent;
				RectTransform val6 = (RectTransform)(object)((parent is RectTransform) ? parent : null);
				if (val6 != null && !(num2 <= 0f) && !float.IsNaN(num) && !float.IsNaN(num2) && !float.IsInfinity(num) && !float.IsInfinity(num2))
				{
					if ((Object)(object)_label == (Object)null || (Object)(object)_parent != (Object)(object)val6)
					{
						if ((Object)(object)_label != (Object)null)
						{
							Object.Destroy((Object)(object)((Component)_label).gameObject);
						}
						if ((Object)(object)val2 == (Object)null && (Object)(object)PlayerLook.Instance != (Object)null)
						{
							val2 = PlayerLook.Instance.PlayerTitleText;
						}
						if ((Object)(object)val2 == (Object)null)
						{
							return;
						}
						_parent = val6;
						_label = GameText.Create(val2, (Transform)(object)val6, "Boss Health Numbers");
						((TMP_Text)_label).alignment = (TextAlignmentOptions)514;
						((TMP_Text)_label).fontSize = 16f;
						((TMP_Text)_label).fontStyle = (FontStyles)1;
						((Graphic)_label).color = Color.white;
						((TMP_Text)_label).maxVisibleCharacters = int.MaxValue;
						RectTransform rectTransform = ((TMP_Text)_label).rectTransform;
						rectTransform.anchorMin = new Vector2(0f, 0.5f);
						rectTransform.anchorMax = new Vector2(1f, 0.5f);
						rectTransform.pivot = new Vector2(0.5f, 0.5f);
						rectTransform.anchoredPosition = Vector2.zero;
						rectTransform.sizeDelta = new Vector2(0f, 24f);
						((TMP_Text)_label).transform.SetAsLastSibling();
						GameText.SetShadow(_label, on: true, new Color(0f, 0f, 0f, 0.9f));
					}
					string text = _text.Get(num, num2, 1f, 0, flag2 ? "{0}%" : "{0} / {1}", "");
					if (flag)
					{
						if ((object)text != _lastApproxSource)
						{
							_lastApproxSource = text;
							_lastApproxText = "~" + text;
						}
						text = _lastApproxText;
					}
					GameHudText.SetText(_label, text);
					GameHudText.SetActive(_label, true);
					return;
				}
			}
			Hide();
		}

		private void Hide()
		{
			if ((Object)(object)_label != (Object)null)
			{
				GameHudText.SetActive(_label, false);
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)_label != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_label).gameObject);
			}
		}
	}
	internal sealed class BossHealthNetwork : IDisposable
	{
		private sealed class Entry
		{
			public NetworkObject Object;

			public ulong Nonce;

			public float Max;

			public float Ratio;

			public float ReceivedAt = float.NegativeInfinity;

			public float NextRequest;

			public float LastUsed;
		}

		private const string Request = "mycopunk.healthnumbers.boss.v1.request";

		private const string Response = "mycopunk.healthnumbers.boss.v1.response";

		private const float RequestInterval = 0.5f;

		private const float ValueLifetime = 3f;

		private readonly ManualLogSource _log;

		private readonly Dictionary<ulong, Entry> _values = new Dictionary<ulong, Entry>();

		private readonly List<ulong> _stale = new List<ulong>();

		private NetworkManager _manager;

		private bool _registered;

		private bool _serverHandler;

		private bool _failed;

		private ulong _nextNonce = (ulong)DateTime.UtcNow.Ticks;

		private float _nextCleanup;

		public BossHealthNetwork(ManualLogSource log)
		{
			_log = log;
		}

		public void Tick()
		{
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Expected O, but got Unknown
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Expected O, but got Unknown
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton != (Object)(object)_manager || ((Object)(object)_manager != (Object)null && !_manager.IsListening))
			{
				Dispose();
				_manager = (((Object)(object)singleton != (Object)null && singleton.IsListening) ? singleton : null);
				_failed = false;
			}
			if ((Object)(object)_manager == (Object)null || _failed || !_manager.IsListening || (!_manager.IsServer && !_manager.IsConnectedClient))
			{
				return;
			}
			try
			{
				CustomMessagingManager customMessagingManager = _manager.CustomMessagingManager;
				if (customMessagingManager == null)
				{
					return;
				}
				if (!_registered)
				{
					_serverHandler = _manager.IsServer;
					if (_serverHandler)
					{
						customMessagingManager.RegisterNamedMessageHandler("mycopunk.healthnumbers.boss.v1.request", new HandleNamedMessageDelegate(OnRequest));
					}
					else
					{
						customMessagingManager.RegisterNamedMessageHandler("mycopunk.healthnumbers.boss.v1.response", new HandleNamedMessageDelegate(OnResponse));
					}
					_registered = true;
					_log.LogInfo((object)("Boss health sharing ready (" + (_serverHandler ? "host" : "client") + ")."));
				}
				float unscaledTime = Time.unscaledTime;
				if (unscaledTime < _nextCleanup)
				{
					return;
				}
				_nextCleanup = unscaledTime + 15f;
				foreach (KeyValuePair<ulong, Entry> value in _values)
				{
					if ((Object)(object)value.Value.Object == (Object)null || !value.Value.Object.IsSpawned || unscaledTime - value.Value.LastUsed > 10f)
					{
						_stale.Add(value.Key);
					}
				}
				foreach (ulong item in _stale)
				{
					_values.Remove(item);
				}
				_stale.Clear();
			}
			catch (Exception ex)
			{
				_failed = true;
				_log.LogWarning((object)("Boss health sharing unavailable; using local values: " + ex.Message));
			}
		}

		public unsafe bool TryGet(EnemyBrain brain, out float max, out float ratio)
		{
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			max = 0f;
			ratio = 0f;
			if ((Object)(object)_manager == (Object)null || _failed || !_registered || _manager.IsServer || !_manager.IsConnectedClient || (Object)(object)brain == (Object)null)
			{
				return false;
			}
			try
			{
				NetworkObject networkObject = ((NetworkBehaviour)brain).NetworkObject;
				if ((Object)(object)networkObject == (Object)null || !networkObject.IsSpawned)
				{
					return false;
				}
				ulong networkObjectId = networkObject.NetworkObjectId;
				float unscaledTime = Time.unscaledTime;
				if (!_values.TryGetValue(networkObjectId, out var value) || (Object)(object)value.Object != (Object)(object)networkObject)
				{
					Dictionary<ulong, Entry> values = _values;
					ulong key = networkObjectId;
					Entry obj = new Entry
					{
						Object = networkObject
					};
					value = obj;
					values[key] = obj;
				}
				value.LastUsed = unscaledTime;
				if (unscaledTime >= value.NextRequest)
				{
					value.NextRequest = unscaledTime + 3f;
					value.Nonce = ++_nextNonce;
					FastBufferWriter val = default(FastBufferWriter);
					((FastBufferWriter)(ref val))..ctor(16, (Allocator)2, -1);
					try
					{
						((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref networkObjectId, default(ForPrimitives));
						((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref value.Nonce, default(ForPrimitives));
						_manager.CustomMessagingManager.SendNamedMessage("mycopunk.healthnumbers.boss.v1.request", 0uL, val, (NetworkDelivery)2);
					}
					finally
					{
						((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
					}
				}
				if (unscaledTime - value.ReceivedAt >= 3f)
				{
					return false;
				}
				max = value.Max;
				ratio = value.Ratio;
				return max > 0f;
			}
			catch (Exception ex)
			{
				_failed = true;
				_log.LogWarning((object)("Boss health request failed; using percentage: " + ex.Message));
				return false;
			}
		}

		private unsafe void OnRequest(ulong sender, FastBufferReader reader)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: 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_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)_manager == (Object)null || !_manager.IsServer || sender == _manager.LocalClientId || !_manager.ConnectedClients.ContainsKey(sender))
				{
					return;
				}
				ulong key = default(ulong);
				((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref key, default(ForPrimitives));
				ulong num = default(ulong);
				((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num, default(ForPrimitives));
				if (_manager.SpawnManager == null || !_manager.SpawnManager.SpawnedObjects.TryGetValue(key, out var value) || (Object)(object)value == (Object)null || !value.IsSpawned)
				{
					return;
				}
				EnemyBrain val = ((Component)value).GetComponent<EnemyBrain>();
				if ((Object)(object)val == (Object)null)
				{
					val = ((Component)value).GetComponentInChildren<EnemyBrain>(true);
				}
				if ((Object)(object)val == (Object)null || (Object)(object)((NetworkBehaviour)val).NetworkObject != (Object)(object)value)
				{
					return;
				}
				float totalMaxHealth = val.totalMaxHealth;
				if (totalMaxHealth <= 0f || float.IsNaN(totalMaxHealth) || float.IsInfinity(totalMaxHealth))
				{
					return;
				}
				EnemyCore core = val.Core;
				if ((Object)(object)core == (Object)null || (Object)(object)val.EnemyClass == (Object)null)
				{
					return;
				}
				float num2 = ((EnemyPart)core).NormalizedHealth;
				if (!val.EnemyClass.config.onlyUseCoreHealthForHealthbar)
				{
					num2 *= val.NormalizedHealth;
				}
				if (float.IsNaN(num2) || float.IsInfinity(num2))
				{
					return;
				}
				num2 = Mathf.Clamp01(num2);
				FastBufferWriter val2 = default(FastBufferWriter);
				((FastBufferWriter)(ref val2))..ctor(24, (Allocator)2, -1);
				try
				{
					((FastBufferWriter)(ref val2)).WriteValueSafe<ulong>(ref key, default(ForPrimitives));
					((FastBufferWriter)(ref val2)).WriteValueSafe<ulong>(ref num, default(ForPrimitives));
					((FastBufferWriter)(ref val2)).WriteValueSafe<float>(ref totalMaxHealth, default(ForPrimitives));
					((FastBufferWriter)(ref val2)).WriteValueSafe<float>(ref num2, default(ForPrimitives));
					_manager.CustomMessagingManager.SendNamedMessage("mycopunk.healthnumbers.boss.v1.response", sender, val2, (NetworkDelivery)2);
				}
				finally
				{
					((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose();
				}
			}
			catch (Exception ex)
			{
				_log.LogWarning((object)("Invalid boss health request: " + ex.Message));
			}
		}

		private void OnResponse(ulong sender, FastBufferReader reader)
		{
			//IL_0029: 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_003c: 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_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!((Object)(object)_manager == (Object)null) && !_manager.IsServer && sender == 0L)
				{
					ulong key = default(ulong);
					((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref key, default(ForPrimitives));
					ulong num = default(ulong);
					((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num, default(ForPrimitives));
					float num2 = default(float);
					((FastBufferReader)(ref reader)).ReadValueSafe<float>(ref num2, default(ForPrimitives));
					float num3 = default(float);
					((FastBufferReader)(ref reader)).ReadValueSafe<float>(ref num3, default(ForPrimitives));
					if (!(num2 <= 0f) && !float.IsNaN(num2) && !float.IsInfinity(num2) && !(num3 < 0f) && !(num3 > 1f) && !float.IsNaN(num3) && !float.IsInfinity(num3) && _values.TryGetValue(key, out var value) && value.Nonce == num && !((Object)(object)value.Object == (Object)null) && value.Object.IsSpawned && _manager.SpawnManager != null && _manager.SpawnManager.SpawnedObjects.TryGetValue(key, out var value2) && !((Object)(object)value2 != (Object)(object)value.Object))
					{
						value.Max = num2;
						value.Ratio = num3;
						value.ReceivedAt = Time.unscaledTime;
						value.NextRequest = value.ReceivedAt + 0.5f;
					}
				}
			}
			catch (Exception ex)
			{
				_log.LogWarning((object)("Invalid boss health response: " + ex.Message));
			}
		}

		public void Dispose()
		{
			try
			{
				if ((Object)(object)_manager != (Object)null && _registered && _manager.CustomMessagingManager != null)
				{
					if (_serverHandler)
					{
						_manager.CustomMessagingManager.UnregisterNamedMessageHandler("mycopunk.healthnumbers.boss.v1.request");
					}
					else
					{
						_manager.CustomMessagingManager.UnregisterNamedMessageHandler("mycopunk.healthnumbers.boss.v1.response");
					}
				}
			}
			catch (Exception ex)
			{
				_log.LogWarning((object)("Boss health sharing cleanup failed: " + ex.Message));
			}
			_registered = false;
			_values.Clear();
			_stale.Clear();
			_manager = null;
			_nextCleanup = 0f;
		}
	}
	internal static class CapTracker
	{
		[HarmonyPatch(typeof(Player), "UpdateTarget")]
		private static class UpdateTargetPatch
		{
			[HarmonyPrefix]
			private static void Prefix(Player __instance)
			{
				if (__instance == Player.LocalPlayer)
				{
					_cap = __instance.tempMaxHealthCap;
					_frame = Time.frameCount;
				}
			}
		}

		private static float _cap = 1f;

		private static int _frame = -1;

		public static bool Installed { get; private set; }

		public static float Current
		{
			get
			{
				if (!Installed || Time.frameCount - _frame > 2)
				{
					return 1f;
				}
				return _cap;
			}
		}

		public static void Install(Harmony harmony)
		{
			try
			{
				harmony.CreateClassProcessor(typeof(UpdateTargetPatch)).Patch();
				Installed = true;
			}
			catch (Exception arg)
			{
				Plugin.Log.LogWarning((object)$"Could not hook Player.UpdateTarget; the max-health cap is not shown. {arg}");
			}
		}
	}
	internal static class DamageLog
	{
		[HarmonyPatch(typeof(Player), "ApplyDamage_Owner")]
		private static class ApplyDamagePatch
		{
			[HarmonyPrefix]
			private static void Prefix(Player __instance, ref DamageData data, IDamageSource source, out float __state)
			{
				//IL_004a: 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_0050: Unknown result type (might be due to invalid IL or missing references)
				//IL_0056: Unknown result type (might be due to invalid IL or missing references)
				//IL_0058: Invalid comparison between Unknown and I4
				//IL_0096: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
				__state = float.NaN;
				if (Enabled() && __instance == Player.LocalPlayer && __instance.IsAlive)
				{
					__state = __instance.Health;
					_inDamage = true;
					_healthSet = false;
					_guardBefore = Guard(__instance);
					LogHandlers(__instance);
					DamageFlags damageFlags = data.damageFlags;
					bool flag = (damageFlags & 0x220) > 0;
					Plugin.Log.LogInfo((object)($"[damage] t={Time.time:0.00} raw={F(data.damage)} " + $"effect={data.effect} effectAmount={F(data.effectAmount)} " + $"dot={((DamageData)(ref data)).IsDOT} flags={damageFlags} " + "source=" + DescribeSource(source) + " hp=" + F(__state) + "/" + F(__instance.MaxHealth) + " guard=" + (_guardBefore ? "on" : "off") + (flag ? " (this hit ignores the guard)" : "")));
				}
			}

			[HarmonyFinalizer]
			private static void Finalizer(Player __instance, float __state)
			{
				if (!float.IsNaN(__state))
				{
					_inDamage = false;
					bool flag = Guard(__instance);
					string text = " guard " + (_guardBefore ? "on" : "off") + " -> " + (flag ? "on" : "off");
					if (_healthSet)
					{
						Plugin.Log.LogInfo((object)("[damage]   lost=" + F(__state - _newHealth) + " hp " + F(__state) + " -> " + F(_newHealth) + text));
					}
					else
					{
						Plugin.Log.LogInfo((object)("[damage]   no health change (blocked, mitigated or deferred)" + text));
					}
					if (_guardBefore && !flag)
					{
						Plugin.Log.LogInfo((object)$"[guard] t={Time.time:0.00} used: the hit left {F(_newHealth)}");
					}
					if (_healthSet && _newHealth <= 0f)
					{
						Plugin.Log.LogInfo((object)string.Format("[guard] t={0:0.00} killed (guard was {1} before the hit)", Time.time, _guardBefore ? "on" : "off"));
					}
				}
			}
		}

		[HarmonyPatch(typeof(Player), "SetHealth_Owner")]
		private static class SetHealthPatch
		{
			[HarmonyPrefix]
			private static void Prefix(Player __instance, float value)
			{
				if (_inDamage && __instance == Player.LocalPlayer)
				{
					_newHealth = value;
					_healthSet = true;
				}
			}
		}

		[HarmonyPatch(typeof(Player), "ApplyHeal_Owner")]
		private static class HealPatch
		{
			[HarmonyPrefix]
			private static void Prefix(Player __instance, out bool __state)
			{
				__state = Enabled() && __instance == Player.LocalPlayer && Guard(__instance);
			}

			[HarmonyPostfix]
			private static void Postfix(Player __instance, bool __state)
			{
				if (Enabled() && __instance == Player.LocalPlayer && !__state && Guard(__instance))
				{
					Plugin.Log.LogInfo((object)$"[guard] t={Time.time:0.00} back on (health {F(__instance.Health)}/{F(__instance.MaxHealth)})");
				}
			}
		}

		public static Func<bool> Enabled = () => false;

		private static readonly FieldInfo BeforeTakeDamage = AccessTools.Field(typeof(Player), "OnBeforeTakeDamage");

		private static string _lastHandlers;

		private const int IgnoresGuardFlags = 544;

		private static bool _inDamage;

		private static float _newHealth;

		private static bool _healthSet;

		private static bool _guardBefore;

		public static void Install(Harmony harmony)
		{
			try
			{
				ModSetup.PatchTogether(harmony, new Type[3]
				{
					typeof(ApplyDamagePatch),
					typeof(SetHealthPatch),
					typeof(HealPatch)
				});
			}
			catch (Exception arg)
			{
				Plugin.Log.LogWarning((object)$"Could not hook the damage path; the damage log is off. {arg}");
			}
		}

		private static bool Guard(Player p)
		{
			return p.hasDeathProtection;
		}

		private static void LogHandlers(Player p)
		{
			if (BeforeTakeDamage == null)
			{
				return;
			}
			Delegate obj = BeforeTakeDamage.GetValue(p) as Delegate;
			string text = "none";
			if ((object)obj != null)
			{
				List<string> list = new List<string>();
				Delegate[] invocationList = obj.GetInvocationList();
				foreach (Delegate obj2 in invocationList)
				{
					list.Add(((obj2.Method.DeclaringType != null) ? (obj2.Method.DeclaringType.Name + ".") : "") + obj2.Method.Name);
				}
				text = string.Join(", ", list);
			}
			if (!(text == _lastHandlers))
			{
				_lastHandlers = text;
				Plugin.Log.LogInfo((object)("[damage] OnBeforeTakeDamage handlers: " + text));
			}
		}

		private static string DescribeSource(IDamageSource source)
		{
			if (source == null)
			{
				return "null";
			}
			string text = ((object)source).GetType().Name;
			Component val = (Component)(object)((source is Component) ? source : null);
			if (val != null && (Object)(object)val != (Object)null)
			{
				text = text + "(" + ((Object)val.gameObject).name + ")";
			}
			try
			{
				IDamageSource val2 = source.GetBase();
				if (val2 != null && val2 != source)
				{
					text = text + " base=" + ((object)val2).GetType().Name;
				}
			}
			catch
			{
			}
			return text;
		}

		private static string F(object v)
		{
			if (!(v is float num))
			{
				return Convert.ToString(v, CultureInfo.InvariantCulture);
			}
			return num.ToString("0.###", CultureInfo.InvariantCulture);
		}
	}
	internal static class GameText
	{
		public static TextMeshProUGUI Create(TextMeshProUGUI template, Transform parent, string name)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: 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)
			TextMeshProUGUI obj = GameHudText.Create(template, parent, name);
			((TMP_Text)obj).alignment = (TextAlignmentOptions)4097;
			RectTransform rectTransform = ((TMP_Text)obj).rectTransform;
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0.5f, 0.5f);
			rectTransform.anchorMax = val;
			rectTransform.anchorMin = val;
			rectTransform.pivot = new Vector2(0f, 0.5f);
			((Transform)rectTransform).localRotation = Quaternion.identity;
			((Transform)rectTransform).localScale = Vector3.one;
			((TMP_Text)obj).text = "";
			((Component)obj).gameObject.SetActive(true);
			return obj;
		}

		public static void SetShadow(TextMeshProUGUI label, bool on, Color color)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			Material fontMaterial = ((TMP_Text)label).fontMaterial;
			if (!((Object)(object)fontMaterial == (Object)null))
			{
				if (on)
				{
					fontMaterial.EnableKeyword(ShaderUtilities.Keyword_Underlay);
					fontMaterial.SetColor(ShaderUtilities.ID_UnderlayColor, color);
					fontMaterial.SetFloat(ShaderUtilities.ID_UnderlayOffsetX, 0.6f);
					fontMaterial.SetFloat(ShaderUtilities.ID_UnderlayOffsetY, -0.6f);
					fontMaterial.SetFloat(ShaderUtilities.ID_UnderlaySoftness, 0.3f);
				}
				else
				{
					fontMaterial.DisableKeyword(ShaderUtilities.Keyword_Underlay);
				}
			}
		}
	}
	internal sealed class HealthText
	{
		private string _text;

		private float _health = float.NaN;

		private float _max = float.NaN;

		private float _cap = float.NaN;

		private int _decimals = -1;

		private string _format;

		private string _capFormat;

		public string Get(float health, float max, float cap, int decimals, string format, string capFormat)
		{
			if (cap >= 1f || string.IsNullOrEmpty(capFormat))
			{
				cap = 1f;
			}
			if (_text != null && health == _health && max == _max && cap == _cap && decimals == _decimals && format == _format && capFormat == _capFormat)
			{
				return _text;
			}
			_health = health;
			_max = max;
			_cap = cap;
			_decimals = decimals;
			_format = format;
			_capFormat = capFormat;
			string text = "F" + decimals;
			string text2 = health.ToString(text, CultureInfo.InvariantCulture);
			string text3 = max.ToString(text, CultureInfo.InvariantCulture);
			if (cap < 1f)
			{
				string text4 = (max * cap).ToString(text, CultureInfo.InvariantCulture);
				_text = SafeFormat(format, text2 + " / " + text4, text2, text4) + SafeFormat(capFormat, " (" + text3 + ")", text3);
			}
			else
			{
				_text = SafeFormat(format, text2 + " / " + text3, text2, text3);
			}
			return _text;
		}

		private static string SafeFormat(string format, string fallback, params object[] args)
		{
			try
			{
				return string.Format(CultureInfo.InvariantCulture, format, args);
			}
			catch (FormatException)
			{
				return fallback;
			}
		}
	}
	internal static class MenuText
	{
		private static readonly Dictionary<string, Dictionary<string, string>> Names = new Dictionary<string, Dictionary<string, string>>
		{
			["en"] = new Dictionary<string, string>
			{
				["health.enabled"] = "Show health numbers",
				["health.dead"] = "Hide while dead",
				["health.bar"] = "Hide with the health bar",
				["health.bold"] = "Bold",
				["health.color"] = "Text color",
				["health.shadow"] = "Shadow",
				["health.teammates"] = "Teammates' health",
				["health.boss"] = "Show boss health numbers",
				["health.name_color"] = "Teammates' health in their name color",
				["health.damage_log"] = "Damage log (debug)"
			},
			["ja"] = new Dictionary<string, string>
			{
				["health.enabled"] = "体力を数値で表示",
				["health.dead"] = "死亡中は非表示",
				["health.bar"] = "体力バーと一緒に非表示にする",
				["health.bold"] = "太字",
				["health.color"] = "文字の色",
				["health.shadow"] = "文字の影",
				["health.teammates"] = "味方の体力を表示",
				["health.boss"] = "ボスの体力を数値で表示",
				["health.name_color"] = "味方の名前と同じ色",
				["health.damage_log"] = "被ダメージログ(デバッグ)"
			}
		};

		private static readonly Dictionary<string, Dictionary<string, string>> Descriptions = new Dictionary<string, Dictionary<string, string>> { ["ja"] = new Dictionary<string, string>
		{
			["health.enabled"] = "体力を数値で表示します。",
			["health.dead"] = "死亡している間は数値を表示しません。",
			["health.bar"] = "Anchor が HealthBar のとき、メニューや HUD 非表示などで体力バーが隠れる間は数値も隠します。",
			["health.bold"] = "文字を太字にします。",
			["health.color"] = "数値の文字の色です。不透明度は設定ファイルの値のままです。",
			["health.shadow"] = "文字の後ろに影を付け、読みやすくします。",
			["health.teammates"] = "味方 HUD の名前の下に現在の体力と最大体力を表示します。自分だけがこの Mod を入れれば使えます。",
			["health.boss"] = "ボスの体力バーに数値を表示します。複数パーツのボスはバーから換算した概算値を表示し、参加側は Mod を導入したホストから最大値を取得します。",
			["health.name_color"] = "味方の名前と同じ色で数値を表示します。オフの場合は Style.Color を使います。",
			["health.damage_log"] = "受けた攻撃の元のダメージ、属性、継続ダメージ、攻撃元、実際に減った体力、ワンパンガード(即死を防ぐ仕組み)の状態を BepInEx のログに記録します。"
		} };

		public static object[] Label(string key)
		{
			return ModsMenuTag.Label(key, (IDictionary<string, Dictionary<string, string>>)Names, (IDictionary<string, Dictionary<string, string>>)Descriptions);
		}
	}
	public enum AnchorMode
	{
		HealthBar,
		Screen
	}
	public enum RendererMode
	{
		Game,
		OnGUI
	}
	internal sealed class Settings
	{
		internal ConfigEntry<bool> EnabledEntry;

		internal ConfigEntry<string> FormatEntry;

		internal ConfigEntry<int> DecimalsEntry;

		internal ConfigEntry<string> CapFormatEntry;

		internal ConfigEntry<bool> HideWhenDeadEntry;

		internal ConfigEntry<Color> ColorEntry;

		internal ConfigEntry<bool> TeammatesEntry;

		internal ConfigEntry<float> TeammateOffsetXEntry;

		internal ConfigEntry<float> TeammateOffsetYEntry;

		internal ConfigEntry<float> TeammateFontScaleEntry;

		internal ConfigEntry<bool> TeammateNameColorEntry;

		internal ConfigEntry<bool> BossEntry;

		public bool Enabled => EnabledEntry.Value;

		public string Format => FormatEntry.Value;

		public int Decimals => DecimalsEntry.Value;

		public string CapFormat => CapFormatEntry.Value;

		public bool HideWhenDead => HideWhenDeadEntry.Value;

		public Color Color => ColorEntry.Value;

		public bool Teammates => TeammatesEntry.Value;

		public float TeammateOffsetX => TeammateOffsetXEntry.Value;

		public float TeammateOffsetY => TeammateOffsetYEntry.Value;

		public float TeammateFontScale => TeammateFontScaleEntry.Value;

		public bool TeammateNameColor => TeammateNameColorEntry.Value;

		public bool Boss => BossEntry.Value;
	}
	[BepInPlugin("t4t5u0.mycopunk.healthnumbers", "Mycopunk Health Numbers", "0.4.4")]
	[BepInDependency("doku_macaron.mycopunk.macaroncore", "1.1.0")]
	[MycoMod(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "t4t5u0.mycopunk.healthnumbers";

		public const string Name = "Mycopunk Health Numbers";

		public const string Version = "0.4.4";

		internal static ManualLogSource Log;

		internal static Settings Settings;

		internal static BossHealthNetwork BossNetwork;

		private ConfigEntry<AnchorMode> _anchor;

		private ConfigEntry<RendererMode> _renderer;

		private ConfigEntry<bool> _hideWithHealthBar;

		private ConfigEntry<float> _offsetX;

		private ConfigEntry<float> _offsetY;

		private ConfigEntry<float> _screenX;

		private ConfigEntry<float> _screenY;

		private ConfigEntry<int> _fontSize;

		private ConfigEntry<bool> _bold;

		private ConfigEntry<bool> _shadow;

		private ConfigEntry<Color> _shadowColor;

		private ConfigEntry<Key> _statDumpKey;

		private ConfigEntry<bool> _damageLog;

		private Harmony _harmony;

		private HealthBarLocator _locator;

		private readonly HealthText _localText = new HealthText();

		private TextMeshProUGUI _label;

		private Transform _labelParent;

		private bool _labelShadow;

		private Color _labelShadowColor;

		private bool _gameLabelFailed;

		private GUIStyle _style;

		private GUIStyle _shadowStyle;

		private readonly GUIContent _content = new GUIContent();

		private bool UseGameRenderer
		{
			get
			{
				if (_renderer.Value == RendererMode.Game)
				{
					return !_gameLabelFailed;
				}
				return false;
			}
		}

		private static Color TextColor => Settings.Color;

		private void Awake()
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Expected O, but got Unknown
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Expected O, but got Unknown
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Expected O, but got Unknown
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Expected O, but got Unknown
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Expected O, but got Unknown
			//IL_024f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0259: Expected O, but got Unknown
			//IL_0280: Unknown result type (might be due to invalid IL or missing references)
			//IL_028a: Expected O, but got Unknown
			//IL_02a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bf: Expected O, but got Unknown
			//IL_02e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f0: Expected O, but got Unknown
			//IL_031a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0350: Unknown result type (might be due to invalid IL or missing references)
			//IL_035a: Expected O, but got Unknown
			//IL_03d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e2: Expected O, but got Unknown
			//IL_0409: Unknown result type (might be due to invalid IL or missing references)
			//IL_0413: Expected O, but got Unknown
			//IL_043a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0444: Expected O, but got Unknown
			//IL_04ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b5: Expected O, but got Unknown
			//IL_04c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ca: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Settings obj = new Settings
			{
				EnabledEntry = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, new ConfigDescription("Show the health numbers.", (AcceptableValueBase)null, MenuText.Label("health.enabled"))),
				FormatEntry = ((BaseUnityPlugin)this).Config.Bind<string>("Display", "Format", "{0} / {1}", "Text to show. {0} is the current health, {1} is the max health (the capped max while capped)."),
				DecimalsEntry = ((BaseUnityPlugin)this).Config.Bind<int>("Display", "Decimals", 1, new ConfigDescription("Digits after the decimal point. Values are rounded.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 2), Array.Empty<object>())),
				CapFormatEntry = ((BaseUnityPlugin)this).Config.Bind<string>("Display", "CapFormat", " ({0})", "Appended while max health is temporarily capped (Mycologist's consumed max health, some objectives). Format then shows the capped max as {1}, and {0} here is the original max health. Leave empty to ignore the cap. Only for yourself: teammates' caps are not known on your machine."),
				HideWhenDeadEntry = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "HideWhenDead", true, new ConfigDescription("Hide the text while the player is dead.", (AcceptableValueBase)null, MenuText.Label("health.dead")))
			};
			_anchor = ((BaseUnityPlugin)this).Config.Bind<AnchorMode>("Position", "Anchor", AnchorMode.HealthBar, "HealthBar: place the text to the right of the game's health bar (falls back to Screen if the bar is not found). Screen: place the text at ScreenX / ScreenY.");
			_hideWithHealthBar = ((BaseUnityPlugin)this).Config.Bind<bool>("Position", "HideWithHealthBar", true, new ConfigDescription("With Anchor = HealthBar, hide the text while the game's health bar is hidden (menus, HUD off, and so on).", (AcceptableValueBase)null, MenuText.Label("health.bar")));
			_offsetX = ((BaseUnityPlugin)this).Config.Bind<float>("Position", "OffsetX", 12f, "With Anchor = HealthBar: horizontal offset from the right end of the bar, in pixels at 1080p (scaled with the screen height).");
			_offsetY = ((BaseUnityPlugin)this).Config.Bind<float>("Position", "OffsetY", 0f, "With Anchor = HealthBar: vertical offset from the middle of the bar, in pixels at 1080p. Positive moves down.");
			_screenX = ((BaseUnityPlugin)this).Config.Bind<float>("Position", "ScreenX", 0.02f, new ConfigDescription("With Anchor = Screen (or as the fallback): left edge of the text, as a fraction of the screen width from the left.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			_screenY = ((BaseUnityPlugin)this).Config.Bind<float>("Position", "ScreenY", 0.93f, new ConfigDescription("With Anchor = Screen (or as the fallback): vertical middle of the text, as a fraction of the screen height from the top.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			_renderer = ((BaseUnityPlugin)this).Config.Bind<RendererMode>("Style", "Renderer", RendererMode.Game, "Game: draw with a copy of the game's own HUD text (same font, behind menus like the rest of the HUD). OnGUI: draw with Unity IMGUI on top of everything (the look of 0.1.0). Game falls back to OnGUI if the HUD text is not found.");
			_fontSize = ((BaseUnityPlugin)this).Config.Bind<int>("Style", "FontSize", 22, new ConfigDescription("Font size in pixels at 1080p (scaled with the screen height).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(6, 200), Array.Empty<object>()));
			_bold = ((BaseUnityPlugin)this).Config.Bind<bool>("Style", "Bold", true, new ConfigDescription("Use a bold font.", (AcceptableValueBase)null, MenuText.Label("health.bold")));
			obj.ColorEntry = ((BaseUnityPlugin)this).Config.Bind<Color>("Style", "Color", Color.white, new ConfigDescription("Text color (RRGGBBAA).", (AcceptableValueBase)null, MenuText.Label("health.color")));
			_shadow = ((BaseUnityPlugin)this).Config.Bind<bool>("Style", "Shadow", true, new ConfigDescription("Draw a drop shadow behind the text so it stays readable.", (AcceptableValueBase)null, MenuText.Label("health.shadow")));
			_shadowColor = ((BaseUnityPlugin)this).Config.Bind<Color>("Style", "ShadowColor", new Color(0f, 0f, 0f, 0.8f), "Shadow color (RRGGBBAA).");
			obj.TeammatesEntry = ((BaseUnityPlugin)this).Config.Bind<bool>("Teammates", "Enabled", true, new ConfigDescription("Also show each teammate's health / max health on a line under their name in the teammate HUD. Needs only this mod on your side; the game already sends teammates' health to everyone.", (AcceptableValueBase)null, MenuText.Label("health.teammates")));
			obj.TeammateOffsetXEntry = ((BaseUnityPlugin)this).Config.Bind<float>("Teammates", "OffsetX", 6f, "Not used since 0.4.0 (the teammate number is on its own line under the name now). Kept so old cfg files stay valid.");
			obj.TeammateOffsetYEntry = ((BaseUnityPlugin)this).Config.Bind<float>("Teammates", "OffsetY", 0f, "Not used since 0.4.0 (the teammate number is on its own line under the name now). Kept so old cfg files stay valid.");
			obj.TeammateFontScaleEntry = ((BaseUnityPlugin)this).Config.Bind<float>("Teammates", "FontScale", 0.9f, new ConfigDescription("Font size relative to the teammate's name.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.3f, 3f), Array.Empty<object>()));
			obj.TeammateNameColorEntry = ((BaseUnityPlugin)this).Config.Bind<bool>("Teammates", "UseNameColor", true, new ConfigDescription("Use the color of the teammate's name. Off: use Style.Color.", (AcceptableValueBase)null, MenuText.Label("health.name_color")));
			obj.BossEntry = ((BaseUnityPlugin)this).Config.Bind<bool>("Boss", "Enabled", true, new ConfigDescription("Show boss health over the health bar. Composite bosses use an approximate bar-based value; clients can request the maximum from a host with this mod.", (AcceptableValueBase)null, MenuText.Label("health.boss")));
			Settings = obj;
			BossNetwork = new BossHealthNetwork(Log);
			_locator = new HealthBarLocator();
			_statDumpKey = ((BaseUnityPlugin)this).Config.Bind<Key>("Debug", "StatDumpKey", (Key)0, "Key that writes the Player prefab's base stats and your current stats to BepInEx/HealthNumbers/stats-*.md. None = off.");
			_damageLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DamageLog", false, new ConfigDescription("Log every hit you take (raw damage, element, DoT, source, health lost, the one-hit guard) to the BepInEx log.", (AcceptableValueBase)null, MenuText.Label("health.damage_log")));
			_harmony = new Harmony("t4t5u0.mycopunk.healthnumbers");
			ModsMenu.Install(_harmony, (BaseUnityPlugin)(object)this);
			CapTracker.Install(_harmony);
			BossHealthLabel.Install(_harmony);
			DamageLog.Enabled = () => _damageLog.Value;
			DamageLog.Install(_harmony);
			try
			{
				TeammateHealthLabel.Install(_harmony);
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Teammate health numbers could not be hooked: " + ex));
			}
			Hotkeys.ReportConflicts((BaseUnityPlugin)(object)this);
			Log.LogInfo((object)"Mycopunk Health Numbers 0.4.4 loaded");
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			BossNetwork?.Dispose();
			BossNetwork = null;
			if ((Object)(object)_label != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_label).gameObject);
			}
		}

		private void Update()
		{
			//IL_0016: 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_0022: 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)
			BossNetwork?.Tick();
			Key value = _statDumpKey.Value;
			Keyboard current = Keyboard.current;
			if ((int)value == 0 || current == null)
			{
				return;
			}
			try
			{
				if (((ButtonControl)current[value]).wasPressedThisFrame)
				{
					StatDump.Write();
				}
			}
			catch (Exception arg)
			{
				Log.LogWarning((object)$"Stat dump failed: {arg}");
			}
		}

		private bool TryGetPlacement(out Player player, out float x, out float yMid)
		{
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			x = (yMid = 0f);
			player = Player.LocalPlayer;
			if (!Settings.Enabled || (Object)(object)player == (Object)null)
			{
				return false;
			}
			if (Settings.HideWhenDead && !player.IsAlive)
			{
				return false;
			}
			if (_anchor.Value == AnchorMode.HealthBar && _hideWithHealthBar.Value && HudVisibility.ResultsOpen())
			{
				return false;
			}
			float num = (float)Screen.height / 1080f;
			if (_anchor.Value == AnchorMode.HealthBar)
			{
				Rect screenRect;
				switch (_locator.TryGetScreenRect(player, out screenRect))
				{
				case HealthBarLocator.Result.Found:
					x = ((Rect)(ref screenRect)).xMax + _offsetX.Value * num;
					yMid = ((Rect)(ref screenRect)).center.y + _offsetY.Value * num;
					return true;
				case HealthBarLocator.Result.Hidden:
					if (_hideWithHealthBar.Value)
					{
						return false;
					}
					break;
				}
			}
			x = _screenX.Value * (float)Screen.width;
			yMid = _screenY.Value * (float)Screen.height;
			return true;
		}

		private string LocalText(Player player)
		{
			return _localText.Get(player.Health, player.MaxHealth, CapTracker.Current, Settings.Decimals, Settings.Format, Settings.CapFormat);
		}

		private void LateUpdate()
		{
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Expected O, but got Unknown
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			if (!UseGameRenderer)
			{
				if ((Object)(object)_label != (Object)null)
				{
					GameHudText.SetActive(_label, false);
				}
				return;
			}
			try
			{
				if (!TryGetPlacement(out var player, out var x, out var yMid) || !EnsureLabel(out var root))
				{
					if ((Object)(object)_label != (Object)null)
					{
						GameHudText.SetActive(_label, false);
					}
					return;
				}
				GameHudText.SetText(_label, LocalText(player));
				float num = (float)_fontSize.Value * ((float)Screen.height / 1080f) / Mathf.Max(0.0001f, root.scaleFactor);
				if (((TMP_Text)_label).fontSize != num)
				{
					((TMP_Text)_label).fontSize = num;
				}
				FontStyles val = (FontStyles)(_bold.Value ? 1 : 0);
				if (((TMP_Text)_label).fontStyle != val)
				{
					((TMP_Text)_label).fontStyle = val;
				}
				if (((Graphic)_label).color != TextColor)
				{
					((Graphic)_label).color = TextColor;
				}
				if (_labelShadow != _shadow.Value || _labelShadowColor != _shadowColor.Value)
				{
					_labelShadow = _shadow.Value;
					_labelShadowColor = _shadowColor.Value;
					GameText.SetShadow(_label, _labelShadow, _labelShadowColor);
				}
				Camera val2 = HudCoordinates.CameraFor(root);
				Vector2 val3 = default(Vector2);
				((Vector2)(ref val3))..ctor(x, (float)Screen.height - yMid);
				Vector2 val4 = default(Vector2);
				if (RectTransformUtility.ScreenPointToLocalPointInRectangle((RectTransform)_labelParent, val3, val2, ref val4))
				{
					((Transform)((TMP_Text)_label).rectTransform).localPosition = new Vector3(val4.x, val4.y, 0f);
				}
				GameHudText.SetActive(_label, true);
			}
			catch (Exception ex)
			{
				_gameLabelFailed = true;
				if ((Object)(object)_label != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)_label).gameObject);
				}
				_label = null;
				Log.LogWarning((object)("Game-style health label failed; using OnGUI instead: " + ex));
			}
		}

		private bool EnsureLabel(out Canvas root)
		{
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			root = null;
			PlayerLook instance = PlayerLook.Instance;
			TextMeshProUGUI val = (((Object)(object)instance != (Object)null) ? instance.PlayerTitleText : null);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			Canvas canvas = ((Graphic)val).canvas;
			if ((Object)(object)canvas == (Object)null)
			{
				return false;
			}
			root = canvas.rootCanvas;
			Transform transform = ((Component)root).transform;
			if ((Object)(object)_label != (Object)null && (Object)(object)_labelParent == (Object)(object)transform)
			{
				return true;
			}
			if ((Object)(object)_label != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_label).gameObject);
			}
			_labelParent = transform;
			_label = GameText.Create(val, _labelParent, "Health Numbers (local)");
			((TMP_Text)_label).transform.SetAsLastSibling();
			_labelShadow = !_shadow.Value;
			Log.LogInfo((object)$"Health label: copied {((Object)val).name} onto canvas {((Object)root).name} ({root.renderMode})");
			return true;
		}

		private void OnGUI()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Invalid comparison between Unknown and I4
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: 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_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			if (!UseGameRenderer && (int)Event.current.type == 7 && TryGetPlacement(out var player, out var x, out var yMid))
			{
				float num = (float)Screen.height / 1080f;
				EnsureStyles();
				int fontSize = Mathf.Max(1, Mathf.RoundToInt((float)_fontSize.Value * num));
				_style.fontSize = fontSize;
				_style.fontStyle = (FontStyle)(_bold.Value ? 1 : 0);
				_style.normal.textColor = TextColor;
				_content.text = LocalText(player);
				Vector2 val = _style.CalcSize(_content);
				Rect val2 = default(Rect);
				((Rect)(ref val2))..ctor(Mathf.Round(x), Mathf.Round(yMid - val.y / 2f), val.x, val.y);
				if (_shadow.Value)
				{
					_shadowStyle.fontSize = fontSize;
					_shadowStyle.fontStyle = _style.fontStyle;
					_shadowStyle.normal.textColor = _shadowColor.Value;
					float num2 = Mathf.Max(1f, Mathf.Round(2f * num));
					GUI.Label(new Rect(((Rect)(ref val2)).x + num2, ((Rect)(ref val2)).y + num2, ((Rect)(ref val2)).width, ((Rect)(ref val2)).height), _content, _shadowStyle);
				}
				GUI.Label(val2, _content, _style);
			}
		}

		private void EnsureStyles()
		{
			//IL_0014: 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_0020: 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)
			//IL_002e: 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_003d: Expected O, but got Unknown
			//IL_003d: 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_004c: Expected O, but got Unknown
			//IL_0051: Expected O, but got Unknown
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			if (_style == null)
			{
				_style = new GUIStyle(GUI.skin.label)
				{
					alignment = (TextAnchor)3,
					wordWrap = false,
					clipping = (TextClipping)0,
					padding = new RectOffset(0, 0, 0, 0),
					margin = new RectOffset(0, 0, 0, 0)
				};
				_shadowStyle = new GUIStyle(_style);
			}
		}
	}
	internal sealed class HealthBarLocator
	{
		public enum Result
		{
			NotFound,
			Hidden,
			Found
		}

		private readonly Vector3[] _corners = (Vector3[])(object)new Vector3[4];

		private Player _player;

		private RectTransform _bar;

		private Canvas _canvas;

		private bool _found;

		public Result TryGetScreenRect(Player player, out Rect screenRect)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: 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_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: 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_01e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0214: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			//IL_0237: Unknown result type (might be due to invalid IL or missing references)
			//IL_023c: Unknown result type (might be due to invalid IL or missing references)
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Unknown result type (might be due to invalid IL or missing references)
			//IL_0267: Unknown result type (might be due to invalid IL or missing references)
			//IL_0288: Unknown result type (might be due to invalid IL or missing references)
			//IL_028d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0292: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0391: Unknown result type (might be due to invalid IL or missing references)
			//IL_0396: Unknown result type (might be due to invalid IL or missing references)
			//IL_030c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0311: Unknown result type (might be due to invalid IL or missing references)
			//IL_032e: Unknown result type (might be due to invalid IL or missing references)
			//IL_033e: Unknown result type (might be due to invalid IL or missing references)
			//IL_034e: Unknown result type (might be due to invalid IL or missing references)
			//IL_035e: Unknown result type (might be due to invalid IL or missing references)
			//IL_031c: Unknown result type (might be due to invalid IL or missing references)
			screenRect = default(Rect);
			bool flag = _found && ((Object)(object)_bar == (Object)null || (Object)(object)_canvas == (Object)null);
			if (player != _player || flag)
			{
				_found = false;
				_player = player;
				_bar = null;
				_canvas = null;
				Component healthbar = (Component)(object)player.healthbar;
				if (healthbar != null && (Object)(object)healthbar != (Object)null)
				{
					ref RectTransform bar = ref _bar;
					Transform transform = healthbar.transform;
					bar = (RectTransform)(object)((transform is RectTransform) ? transform : null);
					if ((Object)(object)_bar != (Object)null)
					{
						_canvas = ((Component)_bar).GetComponentInParent<Canvas>(true);
					}
				}
				_found = (Object)(object)_canvas != (Object)null;
				if (_found)
				{
					Plugin.Log.LogInfo((object)$"Health bar: {((Object)_bar).name}, canvas {((Object)_canvas.rootCanvas).name} ({_canvas.rootCanvas.renderMode}), rect {_bar.rect}");
				}
				else
				{
					Plugin.Log.LogInfo((object)"Health bar not found for the local player; using the Screen position.");
				}
			}
			if ((Object)(object)_bar == (Object)null || (Object)(object)_canvas == (Object)null)
			{
				return Result.NotFound;
			}
			if (!((Component)_bar).gameObject.activeInHierarchy || !((Behaviour)_canvas).isActiveAndEnabled)
			{
				return Result.Hidden;
			}
			Canvas rootCanvas = _canvas.rootCanvas;
			Camera val = null;
			if ((int)rootCanvas.renderMode != 0)
			{
				val = HudCoordinates.CameraFor(rootCanvas);
				if ((Object)(object)val == (Object)null)
				{
					return Result.NotFound;
				}
			}
			Transform parent = ((Transform)_bar).parent;
			Vector3 localScale = ((Transform)_bar).localScale;
			Matrix4x4 val2 = Matrix4x4.TRS(((Transform)_bar).localPosition, ((Transform)_bar).localRotation, new Vector3(1f, localScale.y, localScale.z));
			Matrix4x4 val3 = (((Object)(object)parent != (Object)null) ? (parent.localToWorldMatrix * val2) : val2);
			Rect rect = _bar.rect;
			_corners[0] = ((Matrix4x4)(ref val3)).MultiplyPoint3x4(new Vector3(((Rect)(ref rect)).xMin, ((Rect)(ref rect)).yMin, 0f));
			_corners[1] = ((Matrix4x4)(ref val3)).MultiplyPoint3x4(new Vector3(((Rect)(ref rect)).xMin, ((Rect)(ref rect)).yMax, 0f));
			_corners[2] = ((Matrix4x4)(ref val3)).MultiplyPoint3x4(new Vector3(((Rect)(ref rect)).xMax, ((Rect)(ref rect)).yMax, 0f));
			_corners[3] = ((Matrix4x4)(ref val3)).MultiplyPoint3x4(new Vector3(((Rect)(ref rect)).xMax, ((Rect)(ref rect)).yMin, 0f));
			float num = float.MaxValue;
			float num2 = float.MinValue;
			float num3 = float.MaxValue;
			float num4 = float.MinValue;
			for (int i = 0; i < 4; i++)
			{
				Vector3 val4 = (((Object)(object)val == (Object)null) ? _corners[i] : val.WorldToScreenPoint(_corners[i]));
				if ((Object)(object)val != (Object)null && val4.z < 0f)
				{
					return Result.Hidden;
				}
				num = Mathf.Min(num, val4.x);
				num2 = Mathf.Max(num2, val4.x);
				num3 = Mathf.Min(num3, val4.y);
				num4 = Mathf.Max(num4, val4.y);
			}
			screenRect = Rect.MinMaxRect(num, (float)Screen.height - num4, num2, (float)Screen.height - num3);
			return Result.Found;
		}
	}
	internal static class StatDump
	{
		private const BindingFlags InstanceFields = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		public static void Write()
		{
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.LocalPlayer;
			Player prefab = GetPrefab();
			if ((Object)(object)prefab == (Object)null && (Object)(object)localPlayer == (Object)null)
			{
				Plugin.Log.LogWarning((object)"Stat dump: neither the Player prefab nor the local player is available.");
				return;
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("# Mycopunk player stats");
			stringBuilder.AppendLine();
			stringBuilder.AppendLine($"- Time: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
			stringBuilder.AppendLine("- Game version: " + Application.version);
			stringBuilder.AppendLine("- Class: " + (((Object)(object)localPlayer != (Object)null) ? DescribeCharacter(localPlayer) : "(no local player)"));
			stringBuilder.AppendLine("- base = Player prefab (NetworkConfig.PlayerPrefab), current = local player after upgrades. Rows marked * differ.");
			stringBuilder.AppendLine();
			AppendTable(stringBuilder, "Player", typeof(Player), prefab, localPlayer);
			object baseObj = (((Object)(object)prefab != (Object)null) ? ((object)prefab.upgradeVariables) : null);
			object curObj = (((Object)(object)localPlayer != (Object)null) ? ((object)localPlayer.upgradeVariables) : null);
			AppendTable(stringBuilder, "Player.upgradeVariables", typeof(UpgradeVariableData), baseObj, curObj);
			if ((Object)(object)localPlayer != (Object)null)
			{
				AppendGear(stringBuilder, localPlayer);
			}
			string text = Path.Combine(Paths.BepInExRootPath, "HealthNumbers");
			Directory.CreateDirectory(text);
			string text2 = Path.Combine(text, $"stats-{DateTime.Now:yyyyMMdd-HHmmss}.md");
			File.WriteAllText(text2, stringBuilder.ToString().Replace("\r\n", "\n"));
			Plugin.Log.LogInfo((object)("Stat dump written to " + text2));
		}

		private static Player GetPrefab()
		{
			try
			{
				GameObject val = ((!((Object)(object)NetworkManager.Singleton != (Object)null)) ? null : NetworkManager.Singleton.NetworkConfig?.PlayerPrefab);
				return ((Object)(object)val != (Object)null) ? val.GetComponent<Player>() : null;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Stat dump: could not read the Player prefab. " + ex.Message));
				return null;
			}
		}

		private static string DescribeCharacter(Player player)
		{
			try
			{
				object character = player.Character;
				Object val = (Object)((character is Object) ? character : null);
				if (val != null && val != (Object)null)
				{
					return val.name;
				}
				return character?.ToString() ?? "(unknown)";
			}
			catch
			{
				return "(unknown)";
			}
		}

		private static void AppendTable(StringBuilder sb, string title, Type type, object baseObj, object curObj)
		{
			sb.AppendLine("## " + title);
			sb.AppendLine();
			sb.AppendLine("| Field | Base | Current | |");
			sb.AppendLine("|---|---|---|---|");
			foreach (FieldInfo item in SerializedSimpleFields(type))
			{
				string text = ((baseObj != null) ? Format(item.GetValue(baseObj)) : "");
				string text2 = ((curObj != null) ? Format(item.GetValue(curObj)) : "");
				string text3 = ((baseObj != null && curObj != null && text != text2) ? "*" : "");
				sb.AppendLine("| `" + item.Name + "` | " + text + " | " + text2 + " | " + text3 + " |");
			}
			sb.AppendLine();
		}

		private static void AppendGear(StringBuilder sb, Player player)
		{
			IEnumerable gear = player.Gear;
			if (gear == null)
			{
				return;
			}
			int num = 0;
			foreach (object item in gear)
			{
				int num2 = num++;
				if (item != null)
				{
					Object val = (Object)((item is Object) ? item : null);
					if (val == null || !(val == (Object)null))
					{
						Object val2 = (Object)((item is Object) ? item : null);
						string arg = ((val2 != null) ? val2.name : item.ToString());
						sb.AppendLine($"## Gear {num2}: {arg} ({item.GetType().Name})");
						sb.AppendLine();
						sb.AppendLine("| Field | Value |");
						sb.AppendLine("|---|---|");
						AppendFieldsRecursive(sb, item, item.GetType(), "", 0);
						sb.AppendLine();
					}
				}
			}
		}

		private static void AppendFieldsRecursive(StringBuilder sb, object obj, Type type, string prefix, int depth)
		{
			Assembly assembly = typeof(Player).Assembly;
			List<Type> list = new List<Type>();
			Type type2 = type;
			while (type2 != null && type2.Assembly == assembly)
			{
				list.Insert(0, type2);
				type2 = type2.BaseType;
			}
			foreach (Type item in list)
			{
				FieldInfo[] fields = item.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (FieldInfo fieldInfo in fields)
				{
					Type fieldType = fieldInfo.FieldType;
					object value;
					try
					{
						value = fieldInfo.GetValue(obj);
					}
					catch
					{
						continue;
					}
					string text = prefix + CleanName(fieldInfo.Name);
					if (IsSimple(fieldType))
					{
						sb.AppendLine("| `" + text + "` | " + Format(value) + " |");
					}
					else if (depth < 2 && fieldType.IsValueType && !fieldType.IsPrimitive && fieldType.Assembly == assembly && value != null)
					{
						AppendFieldsRecursive(sb, value, fieldType, text + ".", depth + 1);
					}
				}
			}
		}

		private static string CleanName(string n)
		{
			if (n.StartsWith("<") && n.Contains(">"))
			{
				return n.Substring(1, n.IndexOf('>') - 1);
			}
			return n;
		}

		private static IEnumerable<FieldInfo> SerializedSimpleFields(Type type)
		{
			List<Type> list = new List<Type>();
			Type type2 = type;
			while (type2 != null && type2.Assembly == type.Assembly)
			{
				list.Insert(0, type2);
				type2 = type2.BaseType;
			}
			foreach (Type item in list)
			{
				FieldInfo[] fields = item.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (FieldInfo fieldInfo in fields)
				{
					if (((fieldInfo.IsPublic && !fieldInfo.IsNotSerialized) || fieldInfo.IsDefined(typeof(SerializeField), inherit: false)) && IsSimple(fieldInfo.FieldType))
					{
						yield return fieldInfo;
					}
				}
			}
		}

		private static bool IsSimple(Type t)
		{
			if (!(t == typeof(float)) && !(t == typeof(int)) && !(t == typeof(bool)) && !t.IsEnum && !(t == typeof(Vector2)))
			{
				return t == typeof(Vector3);
			}
			return true;
		}

		private static string Format(object v)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: 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_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			if (!(v is float num))
			{
				if (!(v is Vector2 val))
				{
					if (!(v is Vector3 val2))
					{
						if (v == null)
						{
							return "null";
						}
						return Convert.ToString(v, CultureInfo.InvariantCulture);
					}
					return "(" + Format(val2.x) + ", " + Format(val2.y) + ", " + Format(val2.z) + ")";
				}
				return "(" + Format(val.x) + ", " + Format(val.y) + ")";
			}
			return num.ToString("0.#####", CultureInfo.InvariantCulture);
		}
	}
	internal sealed class TeammateHealthLabel : MonoBehaviour
	{
		[HarmonyPatch(typeof(TeammateHUD), "Initialize")]
		private static class InitializePatch
		{
			[HarmonyPostfix]
			private static void Postfix(TeammateHUD __instance, Player player)
			{
				Initialize_Postfix(__instance, player);
			}
		}

		internal const string LineId = "t4t5u0.healthnumbers";

		internal const int LineOrder = 50;

		private TeammateHUD _hud;

		private Player _player;

		private TextMeshProUGUI _nameText;

		private TextMeshProUGUI _label;

		private readonly HealthText _text = new HealthText();

		private string _shown;

		public static void Install(Harmony harmony)
		{
			if (!UnityTeammateHud.Available)
			{
				Plugin.Log.LogWarning((object)"TeammateHUD.Initialize or its fields not found; teammate health numbers are off.");
				return;
			}
			harmony.CreateClassProcessor(typeof(InitializePatch)).Patch();
			TeammateHUD[] array = Object.FindObjectsByType<TeammateHUD>((FindObjectsInactive)1);
			foreach (TeammateHUD val in array)
			{
				Player player = val.player;
				if (player != null)
				{
					Initialize_Postfix(val, player);
				}
			}
		}

		private static void Initialize_Postfix(TeammateHUD __instance, Player player)
		{
			try
			{
				TeammateHealthLabel teammateHealthLabel = ((Component)__instance).GetComponent<TeammateHealthLabel>();
				if ((Object)(object)teammateHealthLabel == (Object)null)
				{
					teammateHealthLabel = ((Component)__instance).gameObject.AddComponent<TeammateHealthLabel>();
				}
				teammateHealthLabel._hud = __instance;
				teammateHealthLabel._player = player;
				teammateHealthLabel._nameText = __instance.nameText;
				teammateHealthLabel._label = null;
				teammateHealthLabel._shown = null;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Teammate health label could not be created: " + ex));
			}
		}

		private void LateUpdate()
		{
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_hud == (Object)null || (Object)(object)_nameText == (Object)null)
			{
				return;
			}
			try
			{
				Settings settings = Plugin.Settings;
				if (settings == null || !settings.Enabled || !settings.Teammates || !((Object)(object)_player != (Object)null))
				{
					if ((Object)(object)_label != (Object)null)
					{
						UnityTeammateHud.Remove(_hud, "t4t5u0.healthnumbers");
						_label = null;
						_shown = null;
					}
					return;
				}
				if ((Object)(object)_label == (Object)null)
				{
					_label = UnityTeammateHud.AddText(_hud, "t4t5u0.healthnumbers", 50);
					if ((Object)(object)_label == (Object)null)
					{
						Plugin.Log.LogWarning((object)"Teammate health line could not be added; teammate health numbers are off for this HUD.");
						((Behaviour)this).enabled = false;
						return;
					}
				}
				string text = ((settings.HideWhenDead && !_player.IsAlive) ? "" : _text.Get(_player.Health, _player.MaxHealth, 1f, settings.Decimals, settings.Format, ""));
				if ((object)text != _shown)
				{
					_shown = text;
					string text2 = ((text.Length == 0) ? "" : ("HP " + text));
					if (((TMP_Text)_label).text != text2)
					{
						((TMP_Text)_label).text = text2;
					}
				}
				float num = ((TMP_Text)_nameText).fontSize * settings.TeammateFontScale;
				if (((TMP_Text)_label).fontSize != num)
				{
					((TMP_Text)_label).fontSize = num;
				}
				Color val = (settings.TeammateNameColor ? ((Graphic)_nameText).color : settings.Color);
				if (((Graphic)_label).color != val)
				{
					((Graphic)_label).color = val;
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Teammate health label failed and is turned off: " + ex));
				((Behaviour)this).enabled = false;
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)_hud != (Object)null && (Object)(object)_label != (Object)null)
			{
				UnityTeammateHud.Remove(_hud, "t4t5u0.healthnumbers");
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}