Decompiled source of ShowBossHealth v1.0.0

BepInEx/plugins/ShowBossHealth.dll

Decompiled 18 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Sparroh.UI;
using TMPro;
using UnityEngine;

[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("ShowBossHealth")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ShowBossHealth")]
[assembly: AssemblyTitle("ShowBossHealth")]
[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 EnemyHealthValueMod
{
	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)
				{
					return;
				}
				BossHealthbar val = instances[instances.Count - 1];
				if ((Object)(object)val != (Object)null && val.targetMatches(target))
				{
					Instance.AttachToBar(val);
					return;
				}
				for (int num = instances.Count - 1; num >= 0; num--)
				{
					BossHealthbar val2 = instances[num];
					if ((Object)(object)val2 != (Object)null && val2.targetMatches(target))
					{
						Instance.AttachToBar(val2);
						break;
					}
				}
			}
			catch (Exception ex)
			{
				SparrohPlugin.Logger.LogError((object)("Failed to attach enemy health label: " + ex.Message));
			}
		}
	}

	private readonly ConfigEntry<bool> enableHealthValues;

	private readonly ConfigEntry<float> fontSize;

	private readonly ConfigEntry<float> yOffset;

	private readonly ConfigColor valueColor;

	private readonly FieldInfo targetField;

	private readonly FieldInfo healthbarParentField;

	private readonly Dictionary<BossHealthbar, UIText> labels = new Dictionary<BossHealthbar, UIText>();

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

	public static EnemyHealthValueMod Instance { get; private set; }

	public EnemyHealthValueMod(ConfigFile configFile, Harmony harmony)
	{
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		Instance = this;
		enableHealthValues = configFile.Bind<bool>("General", "EnableEnemyHealthValues", true, "Show current / max health under enemy HUD health bars.");
		fontSize = configFile.Bind<float>("Display", "FontSize", 14f, "Font size for health value text (reference 1080p pixels).");
		yOffset = configFile.Bind<float>("Display", "YOffset", -4f, "Vertical offset below the health bar (reference pixels; negative is further down).");
		valueColor = ConfigColor.Bind(configFile, "Colors", "ValueColor", UIColors.TextPrimary, "Health value text color (hex RRGGBB or #RRGGBB).");
		targetField = AccessTools.Field(typeof(BossHealthbar), "target");
		healthbarParentField = AccessTools.Field(typeof(BossHealthbar), "healthbarParent");
		if (targetField == null)
		{
			SparrohPlugin.Logger.LogError((object)"Could not find BossHealthbar.target field.");
		}
		if (healthbarParentField == null)
		{
			SparrohPlugin.Logger.LogWarning((object)"Could not find BossHealthbar.healthbarParent field; using bar root for layout.");
		}
	}

	public void AttachToBar(BossHealthbar bar)
	{
		if (!((Object)(object)bar == (Object)null) && !labels.ContainsKey(bar) && enableHealthValues.Value)
		{
			CreateLabel(bar);
		}
	}

	private void CreateLabel(BossHealthbar bar)
	{
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_0085: Unknown result type (might be due to invalid IL or missing references)
		//IL_009a: 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_0174: 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_00e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0114: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: Unknown result type (might be due to invalid IL or missing references)
		//IL_0147: Unknown result type (might be due to invalid IL or missing references)
		//IL_0134: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)bar == (Object)null || labels.ContainsKey(bar))
		{
			return;
		}
		Transform transform = ((Component)bar).transform;
		object? obj = healthbarParentField?.GetValue(bar);
		RectTransform val = (RectTransform)((obj is RectTransform) ? obj : null);
		float num = UITheme.S(fontSize.Value);
		UIText val2 = UIText.Create(transform, "EnemyHealthValue", "", num, (Color?)valueColor.Value, (TextAlignmentOptions)514, false);
		if (val2 == null)
		{
			return;
		}
		RectTransform rect = val2.Rect;
		rect.anchorMin = new Vector2(0.5f, 0.5f);
		rect.anchorMax = new Vector2(0.5f, 0.5f);
		rect.pivot = new Vector2(0.5f, 1f);
		float num2 = 400f;
		float num3 = UITheme.S(yOffset.Value);
		if ((Object)(object)val != (Object)null)
		{
			float[] array = new float[3];
			Rect rect2 = val.rect;
			array[0] = ((Rect)(ref rect2)).width;
			array[1] = val.sizeDelta.x;
			array[2] = 200f;
			num2 = Mathf.Max(array);
			rect2 = val.rect;
			float num4 = ((Rect)(ref rect2)).height * 0.5f;
			if (num4 <= 0f)
			{
				num4 = val.sizeDelta.y * 0.5f;
			}
			num3 = val.anchoredPosition.y - num4 + UITheme.S(yOffset.Value);
		}
		rect.sizeDelta = new Vector2(num2, UITheme.S(22f));
		rect.anchoredPosition = new Vector2(0f, num3);
		labels[bar] = val2;
		UpdateLabel(bar, val2);
	}

	public void Update()
	{
		if (!enableHealthValues.Value)
		{
			if (labels.Count > 0)
			{
				ClearAllLabels();
			}
			return;
		}
		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 && !labels.ContainsKey(val))
				{
					CreateLabel(val);
				}
			}
		}
		removeBuffer.Clear();
		foreach (KeyValuePair<BossHealthbar, UIText> label in labels)
		{
			BossHealthbar key = label.Key;
			UIText value = label.Value;
			if ((Object)(object)key == (Object)null || value == null || (Object)(object)value.GameObject == (Object)null)
			{
				removeBuffer.Add(key);
			}
			else
			{
				UpdateLabel(key, value);
			}
		}
		for (int j = 0; j < removeBuffer.Count; j++)
		{
			BossHealthbar val2 = removeBuffer[j];
			if (labels.TryGetValue(val2, out var value2))
			{
				value2?.DestroySafe();
				labels.Remove(val2);
			}
			else if ((Object)(object)val2 == (Object)null)
			{
				labels.Remove(val2);
			}
		}
	}

	private void UpdateLabel(BossHealthbar bar, UIText text)
	{
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		object? obj = targetField?.GetValue(bar);
		EnemyBrain val = (EnemyBrain)((obj is EnemyBrain) ? obj : null);
		if (!((Object)(object)val == (Object)null) && TryGetHealthValues(val, out var current, out var max))
		{
			Color value = valueColor.Value;
			text.Color = value;
			text.FontSize = UITheme.S(fontSize.Value);
			int num = Mathf.Max(0, Mathf.RoundToInt(current));
			int num2 = Mathf.Max(0, Mathf.RoundToInt(max));
			text.Text = $"{num} / {num2}";
		}
	}

	internal static bool TryGetHealthValues(EnemyBrain brain, out float current, out float max)
	{
		current = 0f;
		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)
		{
			current = ((EnemyPart)brain.Core).Health;
			max = ((EnemyPart)brain.Core).MaxHealth;
			return max > 0f;
		}
		SumShellHealth((IEnemyComponent)(object)brain.Core, ref current, ref max);
		if (max <= 0f)
		{
			current = ((EnemyPart)brain.Core).Health;
			max = ((EnemyPart)brain.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 ClearAllLabels()
	{
		foreach (KeyValuePair<BossHealthbar, UIText> label in labels)
		{
			label.Value?.DestroySafe();
		}
		labels.Clear();
	}

	public void OnDestroy()
	{
		ClearAllLabels();
		Instance = null;
	}
}
internal static class BossHealthbarExtensions
{
	private static readonly FieldInfo TargetField = AccessTools.Field(typeof(BossHealthbar), "target");

	public static bool targetMatches(this BossHealthbar bar, EnemyBrain brain)
	{
		if ((Object)(object)bar == (Object)null || (Object)(object)brain == (Object)null || TargetField == null)
		{
			return false;
		}
		return TargetField.GetValue(bar) == brain;
	}

	public static void DestroySafe(this UIText text)
	{
		if (text != null)
		{
			UIHelpers.DestroySafe((Object)(object)text.GameObject);
		}
	}
}
[BepInPlugin("sparroh.showbosshealth", "ShowBossHealth", "1.0.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[MycoMod(/*Could not decode attribute arguments.*/)]
public class SparrohPlugin : BaseUnityPlugin
{
	public const string PluginGUID = "sparroh.showbosshealth";

	public const string PluginName = "ShowBossHealth";

	public const string PluginVersion = "1.0.0";

	internal static ManualLogSource Logger;

	private Harmony harmony;

	private EnemyHealthValueMod 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.showbosshealth");
		}
		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.showbosshealth.cfg");
			fileSystemWatcher.Changed += delegate
			{
				configFile.Reload();
			};
			fileSystemWatcher.EnableRaisingEvents = true;
		}
		catch (Exception ex2)
		{
			Logger.LogWarning((object)("Failed to set up config watcher: " + ex2.Message));
		}
		try
		{
			mod = new EnemyHealthValueMod(configFile, harmony);
		}
		catch (Exception ex3)
		{
			Logger.LogError((object)("Failed to initialize ShowBossHealth: " + ex3.Message));
		}
		try
		{
			harmony.PatchAll();
		}
		catch (Exception ex4)
		{
			Logger.LogError((object)("Failed to apply Harmony patches: " + ex4.Message));
		}
		Logger.LogInfo((object)"ShowBossHealth loaded successfully.");
	}

	private void Update()
	{
		try
		{
			if (mod != null)
			{
				mod.Update();
			}
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error in ShowBossHealth.Update(): " + ex.Message));
		}
	}

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

		public const string PLUGIN_NAME = "ShowBossHealth";

		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)
		{
		}
	}
}