Decompiled source of GraduatedHealth v1.0.0

BepInEx/plugins/GraduatedHealth.dll

Decompiled 6 days 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 System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Pigeon.Movement;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("GraduatedHealth")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("GraduatedHealth")]
[assembly: AssemblyTitle("GraduatedHealth")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.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;
		}
	}
}
public class GraduatedHealthMod
{
	internal static class BossHealthbarPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(BossHealthbar), "Initialize")]
		private static void PostfixInitialize(BossHealthbar prefab, EnemyBrain target)
		{
			if (Instance == null)
			{
				return;
			}
			try
			{
				List<BossHealthbar> instances = BossHealthbar.instances;
				if (instances != null && instances.Count != 0)
				{
					BossHealthbar val = instances[instances.Count - 1];
					if ((Object)(object)val != (Object)null)
					{
						Instance.AttachBossBar(val);
					}
				}
			}
			catch (Exception ex)
			{
				SparrohPlugin.Logger.LogError((object)("Failed to attach boss health notches: " + ex.Message));
			}
		}
	}

	internal static class HealthbarPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(Healthbar), "Activate")]
		private static void PostfixActivate(Healthbar __instance)
		{
			if (Instance == null || !Instance.enableFloatingEnemyNotches.Value)
			{
				return;
			}
			try
			{
				Instance.AttachFloatingBar(__instance);
			}
			catch (Exception ex)
			{
				SparrohPlugin.Logger.LogError((object)("Failed to attach floating health notches: " + ex.Message));
			}
		}
	}

	private readonly ConfigEntry<bool> enablePlayerNotches;

	private readonly ConfigEntry<bool> enableEnemyBossNotches;

	private readonly ConfigEntry<bool> enableFloatingEnemyNotches;

	private readonly ConfigEntry<float> playerInterval;

	private readonly ConfigEntry<float> enemyInterval;

	private readonly ConfigEntry<float> notchThickness;

	private readonly ConfigEntry<float> notchHeightFraction;

	private readonly ConfigEntry<string> notchColorHex;

	private readonly FieldInfo bossTargetField;

	private readonly FieldInfo bossHealthbarField;

	private readonly FieldInfo bossHealthbarParentField;

	private readonly FieldInfo floatingHealthbarField;

	private readonly Dictionary<BossHealthbar, HealthbarNotches> bossNotches = new Dictionary<BossHealthbar, HealthbarNotches>();

	private readonly Dictionary<Healthbar, HealthbarNotches> floatingNotches = new Dictionary<Healthbar, HealthbarNotches>();

	private readonly List<BossHealthbar> bossRemoveBuffer = new List<BossHealthbar>();

	private readonly List<Healthbar> floatingRemoveBuffer = new List<Healthbar>();

	private HealthbarNotches playerNotches;

	private RectTransform playerBarParent;

	private Graphic playerHealthGraphic;

	private float lastPlayerMaxHealth = -1f;

	private float playerDiscoverCooldown;

	private float floatingScanCooldown;

	public static GraduatedHealthMod Instance { get; private set; }

	public GraduatedHealthMod(ConfigFile config, Harmony harmony)
	{
		Instance = this;
		enablePlayerNotches = config.Bind<bool>("General", "EnablePlayerNotches", true, "Add graduation notches to the local player health bar.");
		enableEnemyBossNotches = config.Bind<bool>("General", "EnableEnemyBossNotches", true, "Add graduation notches to boss / abomination health bars.");
		enableFloatingEnemyNotches = config.Bind<bool>("General", "EnableFloatingEnemyNotches", true, "Add graduation notches to floating world-space enemy health bars.");
		playerInterval = config.Bind<float>("Intervals", "PlayerHealthPerNotch", 5f, "Player health bar: one notch every N max HP.");
		enemyInterval = config.Bind<float>("Intervals", "EnemyHealthPerNotch", 500f, "Enemy health bars (boss + floating): one notch every N max HP.");
		notchThickness = config.Bind<float>("Display", "NotchThickness", 2f, "Width of each notch line in UI pixels.");
		notchHeightFraction = config.Bind<float>("Display", "NotchHeightFraction", 1f, "Notch height as a fraction of the bar height (0-1).");
		notchColorHex = config.Bind<string>("Display", "NotchColor", "000000AA", "Notch color as hex RRGGBB or RRGGBBAA.");
		bossTargetField = AccessTools.Field(typeof(BossHealthbar), "target");
		bossHealthbarField = AccessTools.Field(typeof(BossHealthbar), "healthbar");
		bossHealthbarParentField = AccessTools.Field(typeof(BossHealthbar), "healthbarParent");
		floatingHealthbarField = AccessTools.Field(typeof(Healthbar), "healthbar");
		if (bossTargetField == null)
		{
			SparrohPlugin.Logger.LogError((object)"Could not find BossHealthbar.target");
		}
		if (bossHealthbarParentField == null)
		{
			SparrohPlugin.Logger.LogWarning((object)"Could not find BossHealthbar.healthbarParent");
		}
		if (floatingHealthbarField == null)
		{
			SparrohPlugin.Logger.LogWarning((object)"Could not find Healthbar.healthbar");
		}
	}

	private Color GetNotchColor()
	{
		//IL_0075: 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)
		string text = notchColorHex.Value?.Trim() ?? "000000AA";
		if (text.StartsWith("#"))
		{
			text = text.Substring(1);
		}
		if (text.Length == 6)
		{
			text += "AA";
		}
		Color result = default(Color);
		if (ColorUtility.TryParseHtmlString("#" + text, ref result))
		{
			return result;
		}
		return new Color(0f, 0f, 0f, 0.66f);
	}

	public void Update()
	{
		//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_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		Color notchColor = GetNotchColor();
		float thickness = Mathf.Max(1f, notchThickness.Value);
		float heightFrac = Mathf.Clamp01(notchHeightFraction.Value);
		if (enablePlayerNotches.Value)
		{
			UpdatePlayerNotches(notchColor, thickness, heightFrac);
		}
		else if (playerNotches != null)
		{
			playerNotches.Destroy();
			playerNotches = null;
			playerBarParent = null;
			playerHealthGraphic = null;
			lastPlayerMaxHealth = -1f;
		}
		if (enableEnemyBossNotches.Value)
		{
			UpdateBossNotches(notchColor, thickness, heightFrac);
		}
		else if (bossNotches.Count > 0)
		{
			ClearBossNotches();
		}
		if (enableFloatingEnemyNotches.Value)
		{
			UpdateFloatingNotches(notchColor, thickness, heightFrac);
		}
		else if (floatingNotches.Count > 0)
		{
			ClearFloatingNotches();
		}
	}

	private void UpdatePlayerNotches(Color color, float thickness, float heightFrac)
	{
		//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
		Player localPlayer = Player.LocalPlayer;
		if ((Object)(object)localPlayer == (Object)null || !localPlayer.IsAlive)
		{
			return;
		}
		if ((Object)(object)playerBarParent == (Object)null || (Object)(object)playerHealthGraphic == (Object)null)
		{
			playerDiscoverCooldown -= Time.unscaledDeltaTime;
			if (playerDiscoverCooldown <= 0f)
			{
				playerDiscoverCooldown = 0.5f;
				TryDiscoverPlayerHealthBar(localPlayer);
			}
			if ((Object)(object)playerBarParent == (Object)null)
			{
				return;
			}
		}
		if (playerNotches == null)
		{
			playerNotches = new HealthbarNotches();
		}
		if (!playerNotches.IsValid || (Object)(object)playerNotches.Parent != (Object)(object)playerBarParent)
		{
			playerNotches.Attach(playerBarParent);
		}
		float maxHealth = localPlayer.MaxHealth;
		if (!(maxHealth <= 0f))
		{
			lastPlayerMaxHealth = maxHealth;
			playerNotches.Update(maxHealth, Mathf.Max(1f, playerInterval.Value), color, thickness, heightFrac);
		}
	}

	private void TryDiscoverPlayerHealthBar(Player player)
	{
		if (TryFindHealthGraphicOn(player, out var graphic, out var parent))
		{
			playerHealthGraphic = graphic;
			playerBarParent = parent;
			SparrohPlugin.Logger.LogInfo((object)("Player health bar found via Player fields: " + ((Object)parent).name));
			return;
		}
		PlayerLook val = null;
		try
		{
			val = player.PlayerLook;
		}
		catch
		{
		}
		if ((Object)(object)val == (Object)null)
		{
			try
			{
				val = PlayerLook.Instance;
			}
			catch
			{
			}
		}
		if ((Object)(object)val != (Object)null && TryFindHealthGraphicOn(val, out graphic, out parent))
		{
			playerHealthGraphic = graphic;
			playerBarParent = parent;
			SparrohPlugin.Logger.LogInfo((object)("Player health bar found via PlayerLook fields: " + ((Object)parent).name));
			return;
		}
		Transform val2 = null;
		if ((Object)(object)val != (Object)null)
		{
			val2 = ((Component)val).transform;
			try
			{
				object? obj3 = AccessTools.Property(typeof(PlayerLook), "DefaultHUDParent")?.GetValue(val);
				Transform val3 = (Transform)(((obj3 is Transform) ? obj3 : null) ?? ((object)(/*isinst with value type is only supported in some contexts*/ ?? /*isinst with value type is only supported in some contexts*/)));
				if ((Object)(object)val3 != (Object)null)
				{
					val2 = val3;
				}
			}
			catch
			{
			}
		}
		if ((Object)(object)val2 == (Object)null)
		{
			val2 = ((Component)player).transform;
		}
		if (TryFindHealthBarInHierarchy(val2, out graphic, out parent))
		{
			playerHealthGraphic = graphic;
			playerBarParent = parent;
			SparrohPlugin.Logger.LogInfo((object)("Player health bar found via hierarchy under " + ((Object)val2).name + ": " + ((Object)parent).name));
		}
	}

	private static bool TryFindHealthGraphicOn(object obj, out Graphic graphic, out RectTransform parent)
	{
		graphic = null;
		parent = null;
		if (obj == null)
		{
			return false;
		}
		Type type = obj.GetType();
		FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		foreach (FieldInfo fieldInfo in fields)
		{
			string name = fieldInfo.Name;
			if (name.IndexOf("health", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("hp", StringComparison.OrdinalIgnoreCase) < 0)
			{
				continue;
			}
			object obj2 = null;
			try
			{
				obj2 = fieldInfo.GetValue(obj);
			}
			catch
			{
				continue;
			}
			if (obj2 == null)
			{
				continue;
			}
			Graphic val = (Graphic)((obj2 is Graphic) ? obj2 : null);
			if (val != null)
			{
				graphic = val;
				Transform parent2 = ((Transform)val.rectTransform).parent;
				parent = (RectTransform)(((object)((parent2 is RectTransform) ? parent2 : null)) ?? ((object)val.rectTransform));
				return true;
			}
			RectTransform val2 = (RectTransform)((obj2 is RectTransform) ? obj2 : null);
			if (val2 != null)
			{
				Graphic componentInChildren = ((Component)val2).GetComponentInChildren<Graphic>(true);
				if ((Object)(object)componentInChildren != (Object)null)
				{
					graphic = componentInChildren;
					parent = val2;
					return true;
				}
			}
			Component val3 = (Component)((obj2 is Component) ? obj2 : null);
			if (val3 != null)
			{
				Graphic val4 = val3.GetComponent<Graphic>() ?? val3.GetComponentInChildren<Graphic>(true);
				if ((Object)(object)val4 != (Object)null)
				{
					graphic = val4;
					Transform parent3 = ((Transform)val4.rectTransform).parent;
					parent = (RectTransform)(((object)((parent3 is RectTransform) ? parent3 : null)) ?? ((object)val4.rectTransform));
					return true;
				}
			}
		}
		PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		foreach (PropertyInfo propertyInfo in properties)
		{
			if (!propertyInfo.CanRead || propertyInfo.GetIndexParameters().Length != 0)
			{
				continue;
			}
			string name2 = propertyInfo.Name;
			if (name2.IndexOf("health", StringComparison.OrdinalIgnoreCase) >= 0 || name2.IndexOf("hp", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				object obj4 = null;
				try
				{
					obj4 = propertyInfo.GetValue(obj, null);
				}
				catch
				{
					continue;
				}
				Graphic val5 = (Graphic)((obj4 is Graphic) ? obj4 : null);
				if (val5 != null)
				{
					graphic = val5;
					Transform parent4 = ((Transform)val5.rectTransform).parent;
					parent = (RectTransform)(((object)((parent4 is RectTransform) ? parent4 : null)) ?? ((object)val5.rectTransform));
					return true;
				}
			}
		}
		return false;
	}

	private static bool TryFindHealthBarInHierarchy(Transform root, out Graphic graphic, out RectTransform parent)
	{
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_011d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0122: Unknown result type (might be due to invalid IL or missing references)
		//IL_010f: Unknown result type (might be due to invalid IL or missing references)
		graphic = null;
		parent = null;
		if ((Object)(object)root == (Object)null)
		{
			return false;
		}
		List<Transform> list = new List<Transform>();
		CollectHealthNamed(root, list);
		foreach (Transform item in list)
		{
			Transform val = item.Find("Fill");
			if ((Object)(object)val == (Object)null)
			{
				for (int i = 0; i < item.childCount; i++)
				{
					Transform child = item.GetChild(i);
					if (((Object)child).name.IndexOf("fill", StringComparison.OrdinalIgnoreCase) >= 0 || ((Object)child).name.IndexOf("health", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						val = child;
						break;
					}
				}
			}
			Graphic val2 = null;
			if ((Object)(object)val != (Object)null)
			{
				val2 = ((Component)val).GetComponent<Graphic>();
			}
			if ((Object)(object)val2 == (Object)null)
			{
				val2 = ((Component)item).GetComponentInChildren<Graphic>(true);
			}
			if (!((Object)(object)val2 == (Object)null))
			{
				RectTransform val3 = (RectTransform)(object)((item is RectTransform) ? item : null);
				if ((Object)(object)val3 == (Object)null)
				{
					Transform parent2 = ((Transform)val2.rectTransform).parent;
					val3 = (RectTransform)(((object)((parent2 is RectTransform) ? parent2 : null)) ?? ((object)val2.rectTransform));
				}
				Rect rect = val3.rect;
				float num;
				if (!(((Rect)(ref rect)).width > 0f))
				{
					num = val3.sizeDelta.x;
				}
				else
				{
					rect = val3.rect;
					num = ((Rect)(ref rect)).width;
				}
				if (!(num < 40f))
				{
					graphic = val2;
					parent = val3;
					return true;
				}
			}
		}
		return false;
	}

	private static void CollectHealthNamed(Transform t, List<Transform> results)
	{
		string name = ((Object)t).name;
		if (name.IndexOf("health", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Healthbar", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("HealthBar", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("HPBar", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("HpBar", StringComparison.OrdinalIgnoreCase) >= 0)
		{
			results.Add(t);
		}
		for (int i = 0; i < t.childCount; i++)
		{
			CollectHealthNamed(t.GetChild(i), results);
		}
	}

	public void AttachBossBar(BossHealthbar bar)
	{
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)bar == (Object)null) && !bossNotches.ContainsKey(bar) && enableEnemyBossNotches.Value)
		{
			RectTransform bossBarParent = GetBossBarParent(bar);
			if (!((Object)(object)bossBarParent == (Object)null))
			{
				HealthbarNotches healthbarNotches = new HealthbarNotches();
				healthbarNotches.Attach(bossBarParent);
				bossNotches[bar] = healthbarNotches;
				RefreshBossBar(bar, healthbarNotches, GetNotchColor(), Mathf.Max(1f, notchThickness.Value), Mathf.Clamp01(notchHeightFraction.Value));
			}
		}
	}

	private RectTransform GetBossBarParent(BossHealthbar bar)
	{
		object? obj = bossHealthbarParentField?.GetValue(bar);
		RectTransform val = (RectTransform)((obj is RectTransform) ? obj : null);
		if ((Object)(object)val != (Object)null)
		{
			return val;
		}
		object? obj2 = bossHealthbarField?.GetValue(bar);
		Graphic val2 = (Graphic)((obj2 is Graphic) ? obj2 : null);
		if ((Object)(object)val2 != (Object)null)
		{
			Transform parent = ((Transform)val2.rectTransform).parent;
			return (RectTransform)(((object)((parent is RectTransform) ? parent : null)) ?? ((object)val2.rectTransform));
		}
		Transform transform = ((Component)bar).transform;
		return (RectTransform)(object)((transform is RectTransform) ? transform : null);
	}

	private void UpdateBossNotches(Color color, float thickness, float heightFrac)
	{
		//IL_009f: Unknown result type (might be due to invalid IL or missing references)
		List<BossHealthbar> instances = BossHealthbar.instances;
		if (instances != null)
		{
			for (int i = 0; i < instances.Count; i++)
			{
				BossHealthbar val = instances[i];
				if ((Object)(object)val != (Object)null && !bossNotches.ContainsKey(val))
				{
					AttachBossBar(val);
				}
			}
		}
		bossRemoveBuffer.Clear();
		foreach (KeyValuePair<BossHealthbar, HealthbarNotches> bossNotch in bossNotches)
		{
			BossHealthbar key = bossNotch.Key;
			HealthbarNotches value = bossNotch.Value;
			if ((Object)(object)key == (Object)null || value == null || !value.IsValid)
			{
				bossRemoveBuffer.Add(key);
			}
			else
			{
				RefreshBossBar(key, value, color, thickness, heightFrac);
			}
		}
		for (int j = 0; j < bossRemoveBuffer.Count; j++)
		{
			BossHealthbar val2 = bossRemoveBuffer[j];
			if (bossNotches.TryGetValue(val2, out var value2))
			{
				value2.Destroy();
				bossNotches.Remove(val2);
			}
			else
			{
				if (!((Object)(object)val2 == (Object)null))
				{
					continue;
				}
				foreach (BossHealthbar item in new List<BossHealthbar>(bossNotches.Keys))
				{
					if ((Object)(object)item == (Object)null)
					{
						bossNotches[item]?.Destroy();
						bossNotches.Remove(item);
					}
				}
			}
		}
	}

	private void RefreshBossBar(BossHealthbar bar, HealthbarNotches notches, Color color, float thickness, float heightFrac)
	{
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		if (TryGetEnemyMaxHealth(bar, out var max) && !(max <= 0f))
		{
			RectTransform bossBarParent = GetBossBarParent(bar);
			if ((Object)(object)bossBarParent != (Object)null && (Object)(object)notches.Parent != (Object)(object)bossBarParent)
			{
				notches.Attach(bossBarParent);
			}
			notches.Update(max, Mathf.Max(1f, enemyInterval.Value), color, thickness, heightFrac);
		}
	}

	private bool TryGetEnemyMaxHealth(BossHealthbar bar, out float max)
	{
		max = 0f;
		object? obj = bossTargetField?.GetValue(bar);
		EnemyBrain val = (EnemyBrain)((obj is EnemyBrain) ? obj : null);
		if ((Object)(object)val == (Object)null || (Object)(object)val.Core == (Object)null)
		{
			return false;
		}
		bool flag = false;
		try
		{
			if ((Object)(object)val.EnemyClass != (Object)null)
			{
				flag = val.EnemyClass.config.onlyUseCoreHealthForHealthbar;
			}
		}
		catch
		{
			flag = false;
		}
		if (flag)
		{
			max = ((EnemyPart)val.Core).MaxHealth;
			return max > 0f;
		}
		float current = 0f;
		SumShellHealth((IEnemyComponent)(object)val.Core, ref current, ref max);
		if (max <= 0f)
		{
			max = ((EnemyPart)val.Core).MaxHealth;
		}
		return max > 0f;
	}

	private static void SumShellHealth(IEnemyComponent component, ref float current, ref float max)
	{
		//IL_000f: 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 (component == null)
		{
			return;
		}
		EnemyPart val = (EnemyPart)(object)((component is EnemyPart) ? component : null);
		if (val != null && (val.ComponentType & 4) != 0)
		{
			max += val.MaxHealth;
			if (val.IsAlive)
			{
				current += val.Health;
			}
		}
		List<IEnemyComponent> childComponents = component.ChildComponents;
		if (childComponents != null)
		{
			for (int i = 0; i < childComponents.Count; i++)
			{
				SumShellHealth(childComponents[i], ref current, ref max);
			}
		}
	}

	private void ClearBossNotches()
	{
		foreach (KeyValuePair<BossHealthbar, HealthbarNotches> bossNotch in bossNotches)
		{
			bossNotch.Value?.Destroy();
		}
		bossNotches.Clear();
	}

	private void UpdateFloatingNotches(Color color, float thickness, float heightFrac)
	{
		//IL_016a: Unknown result type (might be due to invalid IL or missing references)
		floatingScanCooldown -= Time.unscaledDeltaTime;
		if (floatingScanCooldown <= 0f)
		{
			floatingScanCooldown = 0.35f;
			Healthbar[] array = Object.FindObjectsOfType<Healthbar>();
			if (array != null)
			{
				foreach (Healthbar val in array)
				{
					if (!((Object)(object)val == (Object)null) && ((Component)val).gameObject.activeInHierarchy && !floatingNotches.ContainsKey(val))
					{
						AttachFloatingBar(val);
					}
				}
			}
		}
		floatingRemoveBuffer.Clear();
		foreach (KeyValuePair<Healthbar, HealthbarNotches> floatingNotch in floatingNotches)
		{
			Healthbar key = floatingNotch.Key;
			HealthbarNotches value = floatingNotch.Value;
			if ((Object)(object)key == (Object)null || !((Component)key).gameObject.activeInHierarchy || value == null || !value.IsValid)
			{
				floatingRemoveBuffer.Add(key);
				continue;
			}
			ITarget target = key.Target;
			if (target == null || !((ISelectable)target).Exists())
			{
				floatingRemoveBuffer.Add(key);
				continue;
			}
			float maxHealth = target.MaxHealth;
			if (!(maxHealth <= 0f))
			{
				_ = enemyInterval.Value;
				RectTransform floatingBarParent = GetFloatingBarParent(key);
				if ((Object)(object)floatingBarParent != (Object)null && (Object)(object)value.Parent != (Object)(object)floatingBarParent)
				{
					value.Attach(floatingBarParent);
				}
				value.Update(maxHealth, Mathf.Max(1f, enemyInterval.Value), color, thickness, heightFrac);
			}
		}
		for (int j = 0; j < floatingRemoveBuffer.Count; j++)
		{
			Healthbar key2 = floatingRemoveBuffer[j];
			if (floatingNotches.TryGetValue(key2, out var value2))
			{
				value2.Destroy();
				floatingNotches.Remove(key2);
			}
		}
	}

	private void AttachFloatingBar(Healthbar bar)
	{
		if (!((Object)(object)bar == (Object)null) && !floatingNotches.ContainsKey(bar))
		{
			RectTransform floatingBarParent = GetFloatingBarParent(bar);
			if (!((Object)(object)floatingBarParent == (Object)null))
			{
				HealthbarNotches healthbarNotches = new HealthbarNotches();
				healthbarNotches.Attach(floatingBarParent);
				floatingNotches[bar] = healthbarNotches;
			}
		}
	}

	private RectTransform GetFloatingBarParent(Healthbar bar)
	{
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		object? obj = floatingHealthbarField?.GetValue(bar);
		Graphic val = (Graphic)((obj is Graphic) ? obj : null);
		if ((Object)(object)val != (Object)null)
		{
			Transform parent = ((Transform)val.rectTransform).parent;
			return (RectTransform)(((object)((parent is RectTransform) ? parent : null)) ?? ((object)(RectTransform)((Component)bar).transform));
		}
		Transform transform = ((Component)bar).transform;
		return (RectTransform)(object)((transform is RectTransform) ? transform : null);
	}

	private void ClearFloatingNotches()
	{
		foreach (KeyValuePair<Healthbar, HealthbarNotches> floatingNotch in floatingNotches)
		{
			floatingNotch.Value?.Destroy();
		}
		floatingNotches.Clear();
	}

	public void OnDestroy()
	{
		playerNotches?.Destroy();
		playerNotches = null;
		ClearBossNotches();
		ClearFloatingNotches();
		Instance = null;
	}
}
public sealed class HealthbarNotches
{
	private static Sprite whiteSprite;

	private readonly List<Image> ticks = new List<Image>(32);

	private RectTransform parent;

	private GameObject root;

	private RectTransform rootRect;

	private float lastMaxHealth = -1f;

	private float lastInterval = -1f;

	private float lastWidth = -1f;

	private float lastHeight = -1f;

	private Color lastColor;

	private float lastThickness = -1f;

	private static Sprite WhiteSprite
	{
		get
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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)
			//IL_003f: 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_005d: Expected O, but got Unknown
			if ((Object)(object)whiteSprite == (Object)null)
			{
				Texture2D val = new Texture2D(1, 1, (TextureFormat)4, false);
				val.SetPixel(0, 0, Color.white);
				val.Apply(false, false);
				whiteSprite = Sprite.Create(val, new Rect(0f, 0f, 1f, 1f), new Vector2(0.5f, 0.5f), 100f);
				((Object)whiteSprite).name = "GraduatedHealth_White";
			}
			return whiteSprite;
		}
	}

	public RectTransform Parent => parent;

	public bool IsValid
	{
		get
		{
			if ((Object)(object)root != (Object)null)
			{
				return (Object)(object)parent != (Object)null;
			}
			return false;
		}
	}

	public void Attach(RectTransform barParent)
	{
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Expected O, but got Unknown
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0084: Expected O, but got Unknown
		//IL_008a: 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_00aa: 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_00e4: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)barParent == (Object)null) && (!((Object)(object)parent == (Object)(object)barParent) || !((Object)(object)root != (Object)null)))
		{
			Destroy();
			parent = barParent;
			root = new GameObject("GraduatedHealth_Notches", new Type[1] { typeof(RectTransform) });
			root.transform.SetParent((Transform)(object)parent, false);
			rootRect = (RectTransform)root.transform;
			rootRect.anchorMin = Vector2.zero;
			rootRect.anchorMax = Vector2.one;
			rootRect.offsetMin = Vector2.zero;
			rootRect.offsetMax = Vector2.zero;
			rootRect.pivot = new Vector2(0.5f, 0.5f);
			((Transform)rootRect).localScale = Vector3.one;
			((Transform)rootRect).SetAsLastSibling();
		}
	}

	public void Update(float maxHealth, float interval, Color color, float thickness, float heightFraction = 1f)
	{
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_0100: 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_00d2: 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_0199: Unknown result type (might be due to invalid IL or missing references)
		//IL_01af: 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_01d3: 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_0204: 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_022b: Unknown result type (might be due to invalid IL or missing references)
		//IL_021f: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)rootRect == (Object)null || (Object)(object)parent == (Object)null || interval <= 0f || maxHealth <= 0f)
		{
			return;
		}
		Rect rect = parent.rect;
		float num = ((Rect)(ref rect)).width;
		if (num <= 0f)
		{
			num = parent.sizeDelta.x;
		}
		rect = parent.rect;
		float num2 = ((Rect)(ref rect)).height;
		if (num2 <= 0f)
		{
			num2 = parent.sizeDelta.y;
		}
		if (Mathf.Approximately(maxHealth, lastMaxHealth) && Mathf.Approximately(interval, lastInterval) && Mathf.Approximately(num, lastWidth) && Mathf.Approximately(num2, lastHeight) && Mathf.Approximately(thickness, lastThickness) && !(color != lastColor))
		{
			return;
		}
		lastMaxHealth = maxHealth;
		lastInterval = interval;
		lastWidth = num;
		lastHeight = num2;
		lastColor = color;
		lastThickness = thickness;
		int num3 = 0;
		for (float num4 = interval; num4 < maxHealth - 0.001f; num4 += interval)
		{
			num3++;
		}
		EnsureTickCount(num3);
		float num5 = Mathf.Max(1f, num2 * Mathf.Clamp01(heightFraction));
		float num6 = thickness * 0.5f;
		int num7 = 0;
		for (float num8 = interval; num8 < maxHealth - 0.001f; num8 += interval)
		{
			float num9 = num8 / maxHealth;
			Image val = ticks[num7];
			RectTransform rectTransform = ((Graphic)val).rectTransform;
			rectTransform.anchorMin = new Vector2(0f, 0.5f);
			rectTransform.anchorMax = new Vector2(0f, 0.5f);
			rectTransform.pivot = new Vector2(0.5f, 0.5f);
			rectTransform.sizeDelta = new Vector2(thickness, num5);
			rectTransform.anchoredPosition = new Vector2(num9 * num, 0f);
			if (rectTransform.anchoredPosition.x < num6)
			{
				rectTransform.anchoredPosition = new Vector2(num6, 0f);
			}
			else if (rectTransform.anchoredPosition.x > num - num6)
			{
				rectTransform.anchoredPosition = new Vector2(num - num6, 0f);
			}
			((Graphic)val).color = color;
			if (!((Component)val).gameObject.activeSelf)
			{
				((Component)val).gameObject.SetActive(true);
			}
			num7++;
		}
		for (int i = num7; i < ticks.Count; i++)
		{
			if (((Component)ticks[i]).gameObject.activeSelf)
			{
				((Component)ticks[i]).gameObject.SetActive(false);
			}
		}
	}

	private void EnsureTickCount(int count)
	{
		//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_0071: Unknown result type (might be due to invalid IL or missing references)
		while (ticks.Count < count)
		{
			GameObject val = new GameObject($"Notch_{ticks.Count}", new Type[3]
			{
				typeof(RectTransform),
				typeof(CanvasRenderer),
				typeof(Image)
			});
			val.transform.SetParent((Transform)(object)rootRect, false);
			Image component = val.GetComponent<Image>();
			((Graphic)component).raycastTarget = false;
			((Graphic)component).color = Color.white;
			component.sprite = WhiteSprite;
			component.type = (Type)0;
			ticks.Add(component);
		}
	}

	public void Destroy()
	{
		if ((Object)(object)root != (Object)null)
		{
			Object.Destroy((Object)(object)root);
			root = null;
			rootRect = null;
		}
		ticks.Clear();
		parent = null;
		lastMaxHealth = -1f;
		lastInterval = -1f;
		lastWidth = -1f;
		lastHeight = -1f;
		lastThickness = -1f;
	}
}
[BepInPlugin("sparroh.graduatedhealth", "GraduatedHealth", "1.0.0")]
[MycoMod(/*Could not decode attribute arguments.*/)]
public class SparrohPlugin : BaseUnityPlugin
{
	public const string PluginGUID = "sparroh.graduatedhealth";

	public const string PluginName = "GraduatedHealth";

	public const string PluginVersion = "1.0.0";

	internal static ManualLogSource Logger;

	private Harmony harmony;

	private GraduatedHealthMod mod;

	private void Awake()
	{
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Expected O, but got Unknown
		Logger = ((BaseUnityPlugin)this).Logger;
		try
		{
			harmony = new Harmony("sparroh.graduatedhealth");
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Failed to create Harmony instance: " + ex.Message));
			return;
		}
		ConfigFile configFile = ((BaseUnityPlugin)this).Config;
		try
		{
			FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(Paths.ConfigPath, "sparroh.graduatedhealth.cfg");
			fileSystemWatcher.Changed += delegate
			{
				try
				{
					configFile.Reload();
				}
				catch
				{
				}
			};
			fileSystemWatcher.EnableRaisingEvents = true;
		}
		catch (Exception ex2)
		{
			Logger.LogWarning((object)("Failed to set up config watcher: " + ex2.Message));
		}
		try
		{
			mod = new GraduatedHealthMod(configFile, harmony);
		}
		catch (Exception ex3)
		{
			Logger.LogError((object)("Failed to initialize GraduatedHealth: " + ex3.Message));
		}
		try
		{
			harmony.PatchAll();
		}
		catch (Exception ex4)
		{
			Logger.LogError((object)("Failed to apply Harmony patches: " + ex4.Message));
		}
		Logger.LogInfo((object)"GraduatedHealth loaded successfully.");
	}

	private void Update()
	{
		try
		{
			mod?.Update();
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error in GraduatedHealth.Update(): " + ex.Message));
		}
	}

	private void OnDestroy()
	{
		try
		{
			mod?.OnDestroy();
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error in GraduatedHealth.OnDestroy(): " + ex.Message));
		}
		try
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}
		catch (Exception ex2)
		{
			Logger.LogError((object)("Error unpatching Harmony: " + ex2.Message));
		}
	}
}
namespace GraduatedHealth
{
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "GraduatedHealth";

		public const string PLUGIN_NAME = "GraduatedHealth";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}