Decompiled source of GraduatedHealth v1.0.1

BepInEx/plugins/GraduatedHealth.dll

Decompiled a month 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(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Sparroh")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1")]
[assembly: AssemblyProduct("GraduatedHealth")]
[assembly: AssemblyTitle("GraduatedHealth")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.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 sealed class BossHealthbarNotches
{
	private readonly FieldInfo bossHealthbarField;

	private readonly FieldInfo bossHealthbarParentField;

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

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

	private readonly FieldInfo bossTargetField;

	public int Count => bossNotches.Count;

	public BossHealthbarNotches()
	{
		bossTargetField = AccessTools.Field(typeof(BossHealthbar), "target");
		bossHealthbarField = AccessTools.Field(typeof(BossHealthbar), "healthbar");
		bossHealthbarParentField = AccessTools.Field(typeof(BossHealthbar), "healthbarParent");
		if (bossTargetField == null)
		{
			GraduatedHealthPlugin.Logger.LogError((object)"Could not find BossHealthbar.target");
		}
		if (bossHealthbarParentField == null)
		{
			GraduatedHealthPlugin.Logger.LogWarning((object)"Could not find BossHealthbar.healthbarParent");
		}
	}

	public void Attach(BossHealthbar bar)
	{
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)bar == (Object)null) && !bossNotches.ContainsKey(bar) && ConfigManager.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, ConfigManager.GetNotchColor(), Mathf.Max(1f, ConfigManager.NotchThickness.Value), Mathf.Clamp01(ConfigManager.NotchHeightFraction.Value));
			}
		}
	}

	public void Update(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))
				{
					Attach(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);
					}
				}
			}
		}
	}

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

	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 RefreshBossBar(BossHealthbar bar, HealthbarNotches notches, Color color, float thickness, float heightFrac)
	{
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		object? obj = bossTargetField?.GetValue(bar);
		if (EnemyHealthHelpers.TryGetEnemyMaxHealth((EnemyBrain)((obj is EnemyBrain) ? obj : null), 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, ConfigManager.EnemyInterval.Value), color, thickness, heightFrac);
		}
	}
}
public static class ConfigManager
{
	private const float DebounceSeconds = 0.25f;

	private static ConfigFile config;

	private static ManualLogSource logger;

	private static FileSystemWatcher configWatcher;

	private static volatile bool reloadPending;

	private static float lastReloadTime;

	public static ConfigEntry<bool> EnablePlayerNotches { get; private set; }

	public static ConfigEntry<bool> EnableEnemyBossNotches { get; private set; }

	public static ConfigEntry<bool> EnableFloatingEnemyNotches { get; private set; }

	public static ConfigEntry<float> PlayerInterval { get; private set; }

	public static ConfigEntry<float> EnemyInterval { get; private set; }

	public static ConfigEntry<float> NotchThickness { get; private set; }

	public static ConfigEntry<float> NotchHeightFraction { get; private set; }

	public static ConfigEntry<string> NotchColorHex { get; private set; }

	public static void Initialize(ConfigFile configFile, ManualLogSource log)
	{
		config = configFile;
		logger = log;
		EnablePlayerNotches = config.Bind<bool>("General", "Enable Player Notches", true, "Add graduation notches to the local player health bar.");
		EnableEnemyBossNotches = config.Bind<bool>("General", "Enable Boss Notches", true, "Add graduation notches to boss / abomination health bars.");
		EnableFloatingEnemyNotches = config.Bind<bool>("General", "Enable Enemy Notches", true, "Add graduation notches to floating world-space enemy health bars.");
		PlayerInterval = config.Bind<float>("Intervals", "Player Health Per Notch", 5f, "Player health bar: one notch every N max HP.");
		EnemyInterval = config.Bind<float>("Intervals", "Enemy Health Per Notch", 500f, "Enemy health bars (boss + floating): one notch every N max HP.");
		NotchThickness = config.Bind<float>("Display", "Notch Thickness", 2f, "Width of each notch line in UI pixels.");
		NotchHeightFraction = config.Bind<float>("Display", "Notch Height Fraction", 0.33f, "Notch height as a fraction of the bar height (0-1).");
		NotchColorHex = config.Bind<string>("Display", "Notch Color", "000000AA", "Notch color as hex RRGGBB or RRGGBBAA.");
		try
		{
			SetupFileWatcher();
		}
		catch (Exception ex)
		{
			logger.LogError((object)("Error setting up config file watcher: " + ex.Message));
		}
	}

	public static void Tick()
	{
		if (!reloadPending || Time.unscaledTime - lastReloadTime < 0.25f)
		{
			return;
		}
		reloadPending = false;
		lastReloadTime = Time.unscaledTime;
		try
		{
			config.Reload();
			logger.LogInfo((object)"Config reloaded from disk.");
		}
		catch (Exception ex)
		{
			logger.LogError((object)("Error reloading config: " + ex.Message));
		}
	}

	public static Color GetNotchColor()
	{
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_005e: Unknown result type (might be due to invalid IL or missing references)
		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 static void Dispose()
	{
		if (configWatcher != null)
		{
			configWatcher.EnableRaisingEvents = false;
			configWatcher.Changed -= OnConfigFileChanged;
			configWatcher.Created -= OnConfigFileChanged;
			configWatcher.Renamed -= OnConfigFileChanged;
			configWatcher.Dispose();
			configWatcher = null;
		}
	}

	private static void SetupFileWatcher()
	{
		configWatcher = new FileSystemWatcher(Paths.ConfigPath, "sparroh.graduatedhealth.cfg");
		configWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite;
		configWatcher.Changed += OnConfigFileChanged;
		configWatcher.Created += OnConfigFileChanged;
		configWatcher.Renamed += OnConfigFileChanged;
		configWatcher.EnableRaisingEvents = true;
	}

	private static void OnConfigFileChanged(object sender, FileSystemEventArgs e)
	{
		reloadPending = true;
	}
}
public static class EnemyHealthHelpers
{
	public static bool TryGetEnemyMaxHealth(EnemyBrain brain, out float max)
	{
		max = 0f;
		if ((Object)(object)brain == (Object)null || (Object)(object)brain.Core == (Object)null)
		{
			return false;
		}
		bool flag = false;
		try
		{
			if ((Object)(object)brain.EnemyClass != (Object)null)
			{
				flag = brain.EnemyClass.config.onlyUseCoreHealthForHealthbar;
			}
		}
		catch
		{
			flag = false;
		}
		if (flag)
		{
			max = ((EnemyPart)brain.Core).MaxHealth;
			return max > 0f;
		}
		float current = 0f;
		SumShellHealth((IEnemyComponent)(object)brain.Core, ref current, ref max);
		if (max <= 0f)
		{
			max = ((EnemyPart)brain.Core).MaxHealth;
		}
		return max > 0f;
	}

	public 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);
			}
		}
	}
}
public sealed class FloatingHealthbarNotches
{
	private readonly FieldInfo floatingHealthbarField;

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

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

	private float floatingScanCooldown;

	public int Count => floatingNotches.Count;

	public FloatingHealthbarNotches()
	{
		floatingHealthbarField = AccessTools.Field(typeof(Healthbar), "healthbar");
		if (floatingHealthbarField == null)
		{
			GraduatedHealthPlugin.Logger.LogWarning((object)"Could not find Healthbar.healthbar");
		}
	}

	public void Attach(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;
			}
		}
	}

	public void Update(Color color, float thickness, float heightFrac)
	{
		//IL_015a: 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))
					{
						Attach(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))
			{
				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, ConfigManager.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);
			}
		}
	}

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

	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);
	}
}
public class GraduatedHealthMod
{
	private readonly BossHealthbarNotches bossNotches = new BossHealthbarNotches();

	private readonly FloatingHealthbarNotches floatingNotches = new FloatingHealthbarNotches();

	private float lastPlayerMaxHealth = -1f;

	private RectTransform playerBarParent;

	private float playerDiscoverCooldown;

	private Graphic playerHealthGraphic;

	private HealthbarNotches playerNotches;

	public static GraduatedHealthMod Instance { get; private set; }

	public GraduatedHealthMod()
	{
		Instance = this;
	}

	public void AttachBossBar(BossHealthbar bar)
	{
		bossNotches.Attach(bar);
	}

	public void AttachFloatingBar(Healthbar bar)
	{
		floatingNotches.Attach(bar);
	}

	public void Update()
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: 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_00bc: Unknown result type (might be due to invalid IL or missing references)
		Color notchColor = ConfigManager.GetNotchColor();
		float thickness = Mathf.Max(1f, ConfigManager.NotchThickness.Value);
		float heightFrac = Mathf.Clamp01(ConfigManager.NotchHeightFraction.Value);
		if (ConfigManager.EnablePlayerNotches.Value)
		{
			UpdatePlayerNotches(notchColor, thickness, heightFrac);
		}
		else if (playerNotches != null)
		{
			playerNotches.Destroy();
			playerNotches = null;
			playerBarParent = null;
			playerHealthGraphic = null;
			lastPlayerMaxHealth = -1f;
		}
		if (ConfigManager.EnableEnemyBossNotches.Value)
		{
			bossNotches.Update(notchColor, thickness, heightFrac);
		}
		else if (bossNotches.Count > 0)
		{
			bossNotches.Clear();
		}
		if (ConfigManager.EnableFloatingEnemyNotches.Value)
		{
			floatingNotches.Update(notchColor, thickness, heightFrac);
		}
		else if (floatingNotches.Count > 0)
		{
			floatingNotches.Clear();
		}
	}

	public void OnDestroy()
	{
		playerNotches?.Destroy();
		playerNotches = null;
		bossNotches.Clear();
		floatingNotches.Clear();
		Instance = null;
	}

	private void UpdatePlayerNotches(Color color, float thickness, float heightFrac)
	{
		//IL_0102: 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;
				if (PlayerHealthbarDiscovery.TryDiscover(localPlayer, out var graphic, out var parent))
				{
					playerHealthGraphic = graphic;
					playerBarParent = parent;
				}
			}
			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, ConfigManager.PlayerInterval.Value), color, thickness, heightFrac);
		}
	}
}
public sealed class HealthbarNotches
{
	private static Sprite whiteSprite;

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

	private Color lastColor;

	private float lastHeight = -1f;

	private float lastInterval = -1f;

	private float lastMaxHealth = -1f;

	private float lastThickness = -1f;

	private float lastWidth = -1f;

	private GameObject root;

	private RectTransform rootRect;

	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 { get; private set; }

	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;
	}
}
internal static class BossHealthbarPatches
{
	[HarmonyPostfix]
	[HarmonyPatch(typeof(BossHealthbar), "Initialize")]
	private static void PostfixInitialize(BossHealthbar prefab, EnemyBrain target)
	{
		if (GraduatedHealthMod.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)
				{
					GraduatedHealthMod.Instance.AttachBossBar(val);
				}
			}
		}
		catch (Exception ex)
		{
			GraduatedHealthPlugin.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 (GraduatedHealthMod.Instance == null || !ConfigManager.EnableFloatingEnemyNotches.Value)
		{
			return;
		}
		try
		{
			GraduatedHealthMod.Instance.AttachFloatingBar(__instance);
		}
		catch (Exception ex)
		{
			GraduatedHealthPlugin.Logger.LogError((object)("Failed to attach floating health notches: " + ex.Message));
		}
	}
}
public static class PlayerHealthbarDiscovery
{
	public static bool TryDiscover(Player player, out Graphic graphic, out RectTransform parent)
	{
		graphic = null;
		parent = null;
		if ((Object)(object)player == (Object)null)
		{
			return false;
		}
		if (TryFindHealthGraphicOn(player, out graphic, out parent))
		{
			GraduatedHealthPlugin.Logger.LogInfo((object)("Player health bar found via Player fields: " + ((Object)parent).name));
			return true;
		}
		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))
		{
			GraduatedHealthPlugin.Logger.LogInfo((object)("Player health bar found via PlayerLook fields: " + ((Object)parent).name));
			return true;
		}
		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))
		{
			GraduatedHealthPlugin.Logger.LogInfo((object)("Player health bar found via hierarchy under " + ((Object)val2).name + ": " + ((Object)parent).name));
			return true;
		}
		return false;
	}

	public 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;
	}

	public 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);
		}
	}
}
[BepInPlugin("sparroh.graduatedhealth", "GraduatedHealth", "1.0.1")]
[MycoMod(/*Could not decode attribute arguments.*/)]
public class GraduatedHealthPlugin : BaseUnityPlugin
{
	public const string PluginGUID = "sparroh.graduatedhealth";

	public const string PluginName = "GraduatedHealth";

	public const string PluginVersion = "1.0.1";

	internal static ManualLogSource Logger;

	private Harmony harmony;

	private GraduatedHealthMod mod;

	private void Awake()
	{
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Expected O, but got Unknown
		Logger = ((BaseUnityPlugin)this).Logger;
		try
		{
			ConfigManager.Initialize(((BaseUnityPlugin)this).Config, Logger);
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Failed to initialize config: " + ex.Message));
			return;
		}
		try
		{
			harmony = new Harmony("sparroh.graduatedhealth");
		}
		catch (Exception ex2)
		{
			Logger.LogError((object)("Failed to create Harmony instance: " + ex2.Message));
			return;
		}
		try
		{
			mod = new GraduatedHealthMod();
		}
		catch (Exception ex3)
		{
			Logger.LogError((object)("Failed to initialize GraduatedHealth: " + ex3.Message));
		}
		try
		{
			harmony.PatchAll(typeof(BossHealthbarPatches));
			harmony.PatchAll(typeof(HealthbarPatches));
		}
		catch (Exception ex4)
		{
			Logger.LogError((object)("Failed to apply Harmony patches: " + ex4.Message));
		}
		Logger.LogInfo((object)"GraduatedHealth loaded successfully.");
	}

	private void Update()
	{
		try
		{
			ConfigManager.Tick();
			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
		{
			ConfigManager.Dispose();
		}
		catch (Exception ex2)
		{
			Logger.LogError((object)("Error disposing config: " + ex2.Message));
		}
		try
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}
		catch (Exception ex3)
		{
			Logger.LogError((object)("Error unpatching Harmony: " + ex3.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.1";
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}