Decompiled source of ChipGauge v1.2.0

plugins/ChipGauge.dll

Decompiled 8 hours ago
using System;
using System.Collections;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyCompany("Reynum")]
[assembly: AssemblyTitle("ChipGauge")]
[assembly: AssemblyVersion("1.2.0.0")]
namespace ChipGauge;

[BepInPlugin("Reynum.ChipGauge", "Chip Gauge", "1.2.0")]
public class ChipGaugePlugin : BaseUnityPlugin
{
	public const string ModGuid = "Reynum.ChipGauge";

	public const string ModName = "Chip Gauge";

	public const string ModVersion = "1.2.0";

	internal static ConfigEntry<bool> Enabled;

	internal static ConfigEntry<float> Range;

	internal static ConfigEntry<bool> ShowToolWarning;

	internal static ConfigEntry<bool> ShowRockChunk;

	internal static ConfigEntry<bool> ShowPercent;

	internal static ConfigEntry<bool> OnlyMatchingTool;

	internal static ConfigEntry<bool> HideInBase;

	private Harmony _harmony;

	private void Awake()
	{
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Expected O, but got Unknown
		//IL_0103: Unknown result type (might be due to invalid IL or missing references)
		//IL_010d: Expected O, but got Unknown
		Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Enabled", true, "Show remaining health of trees, logs, rocks and other destructibles in the crosshair hover text.");
		Range = ((BaseUnityPlugin)this).Config.Bind<float>("1 - General", "Range", 6f, new ConfigDescription("How far from your character (metres) to show health.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 20f), Array.Empty<object>()));
		ShowPercent = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Show Percentage", true, "Add the percentage after the numbers.");
		ShowToolWarning = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Warn If Tool Too Weak", true, "Add a red line when your current tool can't damage the target.");
		OnlyMatchingTool = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Only With Matching Tool", true, "Only show health when your equipped tool can damage the target: axe for trees, logs and stumps; pickaxe for rocks and ore deposits.");
		HideInBase = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Hide Inside Base", true, "Don't show anything inside your base area (the zone around workbenches, fires and beds where enemies can't spawn).");
		ShowRockChunk = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Show Rock Chunk Health", true, "For big rocks and deposits, also show the health of the chunk you're aiming at.");
		_harmony = new Harmony("Reynum.ChipGauge");
		_harmony.PatchAll(typeof(HudPatch));
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Chip Gauge 1.2.0 loaded.");
	}

	private void OnDestroy()
	{
		if (_harmony != null)
		{
			_harmony.UnpatchSelf();
		}
	}
}
[HarmonyPatch]
internal static class HudPatch
{
	private static readonly FieldInfo F_TreeBase_nview = AccessTools.Field(typeof(TreeBase), "m_nview");

	private static readonly FieldInfo F_TreeLog_nview = AccessTools.Field(typeof(TreeLog), "m_nview");

	private static readonly FieldInfo F_Destr_nview = AccessTools.Field(typeof(Destructible), "m_nview");

	private static readonly FieldInfo F_MineRock_nview = AccessTools.Field(typeof(MineRock), "m_nview");

	private static readonly FieldInfo F_MineRock_areas = AccessTools.Field(typeof(MineRock), "m_hitAreas");

	private static readonly MethodInfo M_MineRock_areaIndex = AccessTools.Method(typeof(MineRock), "GetAreaIndex", (Type[])null, (Type[])null);

	private static readonly FieldInfo F_MineRock5_areas = AccessTools.Field(typeof(MineRock5), "m_hitAreas");

	private static readonly MethodInfo M_MineRock5_areaIndex = AccessTools.Method(typeof(MineRock5), "GetAreaIndex", (Type[])null, (Type[])null);

	private static FieldInfo F_HitArea_health;

	private static int s_healthHash = int.MinValue;

	private static bool s_failedOnce;

	[HarmonyPatch(typeof(Hud), "UpdateCrosshair")]
	[HarmonyPostfix]
	[HarmonyPriority(0)]
	private static void UpdateCrosshair_Postfix(Hud __instance, Player player)
	{
		if (!ChipGaugePlugin.Enabled.Value || (Object)(object)__instance == (Object)null || (Object)(object)__instance.m_hoverName == (Object)null || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || ((Character)player).IsDead())
		{
			return;
		}
		try
		{
			if (!Describe(player, out var name, out var health, out var root))
			{
				return;
			}
			string text = ((TMP_Text)__instance.m_hoverName).text ?? "";
			if (text.Length == 0)
			{
				((TMP_Text)__instance.m_hoverName).text = name + "\n" + health;
				return;
			}
			GameObject hoverObject = ((Humanoid)player).GetHoverObject();
			if (!((Object)(object)hoverObject == (Object)null) && !((Object)(object)root == (Object)null) && hoverObject.transform.IsChildOf(root.transform))
			{
				((TMP_Text)__instance.m_hoverName).text = text + "\n" + health;
			}
		}
		catch (Exception ex)
		{
			if (!s_failedOnce)
			{
				s_failedOnce = true;
				Debug.LogWarning((object)("[ChipGauge] " + ex));
			}
		}
	}

	private static bool Describe(Player p, out string name, out string health, out GameObject root)
	{
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: 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_00a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
		name = (health = null);
		root = null;
		Camera main = Camera.main;
		if ((Object)(object)main == (Object)null)
		{
			return false;
		}
		float num = Vector3.Distance(((Component)main).transform.position, ((Component)p).transform.position) + ChipGaugePlugin.Range.Value + 1f;
		RaycastHit[] array = Physics.RaycastAll(((Component)main).transform.position, ((Component)main).transform.forward, num, -1, (QueryTriggerInteraction)1);
		if (array.Length == 0)
		{
			return false;
		}
		Array.Sort(array, (RaycastHit a, RaycastHit b) => ((RaycastHit)(ref a)).distance.CompareTo(((RaycastHit)(ref b)).distance));
		RaycastHit[] array2 = array;
		for (int num2 = 0; num2 < array2.Length; num2++)
		{
			RaycastHit val = array2[num2];
			Collider collider = ((RaycastHit)(ref val)).collider;
			if (!((Object)(object)collider == (Object)null) && !((Object)(object)((Component)collider).GetComponentInParent<Player>() == (Object)(object)p) && !(Vector3.Distance(((Component)p).transform.position, ((RaycastHit)(ref val)).point) > ChipGaugePlugin.Range.Value))
			{
				if (Target(p, collider, out name, out health, out root))
				{
					return true;
				}
				if ((Object)(object)((Component)collider).GetComponentInParent<WearNTear>() != (Object)null || (Object)(object)((Component)collider).GetComponentInParent<Heightmap>() != (Object)null)
				{
					return false;
				}
			}
		}
		return false;
	}

	private static bool Target(Player p, Collider col, out string name, out string health, out GameObject root)
	{
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0133: Unknown result type (might be due to invalid IL or missing references)
		//IL_0173: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
		name = (health = null);
		root = null;
		if ((Object)(object)((Component)col).GetComponentInParent<Character>() != (Object)null)
		{
			return false;
		}
		if (ChipGaugePlugin.HideInBase.Value && (Object)(object)EffectArea.IsPointInsideArea(((Component)col).transform.position, (Type)4, 0f) != (Object)null)
		{
			return false;
		}
		ItemData currentWeapon = ((Humanoid)p).GetCurrentWeapon();
		TreeBase componentInParent = ((Component)col).GetComponentInParent<TreeBase>();
		if ((Object)(object)componentInParent != (Object)null)
		{
			if (!ToolFits(currentWeapon, componentInParent.m_damageModifiers))
			{
				return false;
			}
			root = ((Component)componentInParent).gameObject;
			name = NameOf(((Component)componentInParent).gameObject);
			object? value = F_TreeBase_nview.GetValue(componentInParent);
			health = Line(ZdoHealth((ZNetView)((value is ZNetView) ? value : null), componentInParent.m_health), componentInParent.m_health, p, componentInParent.m_minToolTier, null);
			return true;
		}
		TreeLog componentInParent2 = ((Component)col).GetComponentInParent<TreeLog>();
		if ((Object)(object)componentInParent2 != (Object)null)
		{
			if (!ToolFits(currentWeapon, componentInParent2.m_damages))
			{
				return false;
			}
			root = ((Component)componentInParent2).gameObject;
			name = NameOf(((Component)componentInParent2).gameObject);
			object? value2 = F_TreeLog_nview.GetValue(componentInParent2);
			health = Line(ZdoHealth((ZNetView)((value2 is ZNetView) ? value2 : null), componentInParent2.m_health), componentInParent2.m_health, p, componentInParent2.m_minToolTier, null);
			return true;
		}
		MineRock5 componentInParent3 = ((Component)col).GetComponentInParent<MineRock5>();
		if ((Object)(object)componentInParent3 != (Object)null && !ToolFits(currentWeapon, componentInParent3.m_damageModifiers))
		{
			return false;
		}
		if ((Object)(object)componentInParent3 != (Object)null)
		{
			root = ((Component)componentInParent3).gameObject;
			return Rock5(p, componentInParent3, col, out name, out health);
		}
		MineRock componentInParent4 = ((Component)col).GetComponentInParent<MineRock>();
		if ((Object)(object)componentInParent4 != (Object)null && !ToolFits(currentWeapon, componentInParent4.m_damageModifiers))
		{
			return false;
		}
		if ((Object)(object)componentInParent4 != (Object)null)
		{
			root = ((Component)componentInParent4).gameObject;
			return Rock(p, componentInParent4, col, out name, out health);
		}
		Destructible componentInParent5 = ((Component)col).GetComponentInParent<Destructible>();
		if ((Object)(object)componentInParent5 != (Object)null && (Object)(object)((Component)componentInParent5).GetComponentInParent<Piece>() == (Object)null)
		{
			if (!ToolFits(currentWeapon, componentInParent5.m_damages))
			{
				return false;
			}
			root = ((Component)componentInParent5).gameObject;
			name = NameOf(((Component)componentInParent5).gameObject);
			object? value3 = F_Destr_nview.GetValue(componentInParent5);
			health = Line(ZdoHealth((ZNetView)((value3 is ZNetView) ? value3 : null), componentInParent5.m_health), componentInParent5.m_health, p, componentInParent5.m_minToolTier, null);
			return true;
		}
		return false;
	}

	private static bool Rock5(Player p, MineRock5 rock, Collider col, out string name, out string health)
	{
		name = Localize(rock.m_name);
		health = null;
		if (!(F_MineRock5_areas.GetValue(rock) is IList { Count: not 0 } list))
		{
			return false;
		}
		if (F_HitArea_health == null)
		{
			F_HitArea_health = AccessTools.Field(list[0].GetType(), "m_health");
		}
		float num = 0f;
		float num2 = 0f;
		string chunk = null;
		foreach (object item in list)
		{
			num += rock.m_health;
			num2 += Mathf.Max(0f, (float)F_HitArea_health.GetValue(item));
		}
		if (ChipGaugePlugin.ShowRockChunk.Value && list.Count > 1)
		{
			try
			{
				int num3 = (int)M_MineRock5_areaIndex.Invoke(rock, new object[1] { col });
				if (num3 >= 0 && num3 < list.Count)
				{
					chunk = "Chunk: " + Mathf.CeilToInt(Mathf.Max(0f, (float)F_HitArea_health.GetValue(list[num3]))) + " / " + Mathf.CeilToInt(rock.m_health);
				}
			}
			catch
			{
			}
		}
		health = Line(num2, num, p, rock.m_minToolTier, chunk);
		return true;
	}

	private static bool Rock(Player p, MineRock rock, Collider col, out string name, out string health)
	{
		name = Localize(rock.m_name);
		health = null;
		object? value = F_MineRock_nview.GetValue(rock);
		ZNetView val = (ZNetView)((value is ZNetView) ? value : null);
		Collider[] array = F_MineRock_areas.GetValue(rock) as Collider[];
		if ((Object)(object)val == (Object)null || !val.IsValid() || array == null || array.Length == 0)
		{
			return false;
		}
		ZDO zDO = val.GetZDO();
		float health2 = rock.GetHealth();
		float num = 0f;
		float num2 = 0f;
		string chunk = null;
		for (int i = 0; i < array.Length; i++)
		{
			num += health2;
			num2 += Mathf.Max(0f, zDO.GetFloat("Health" + i, health2));
		}
		if (ChipGaugePlugin.ShowRockChunk.Value && array.Length > 1)
		{
			try
			{
				int num3 = (int)M_MineRock_areaIndex.Invoke(rock, new object[1] { col });
				if (num3 >= 0)
				{
					chunk = "Chunk: " + Mathf.CeilToInt(Mathf.Max(0f, zDO.GetFloat("Health" + num3, health2))) + " / " + Mathf.CeilToInt(health2);
				}
			}
			catch
			{
			}
		}
		health = Line(num2, num, p, rock.m_minToolTier, chunk);
		return true;
	}

	private static bool ToolFits(ItemData tool, DamageModifiers mods)
	{
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: 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)
		if (!ChipGaugePlugin.OnlyMatchingTool.Value)
		{
			return true;
		}
		if (tool == null)
		{
			return false;
		}
		DamageTypes damages = tool.m_shared.m_damages;
		if (damages.m_chop > 0f && Hurts(((DamageModifiers)(ref mods)).GetModifier((DamageType)8)))
		{
			return true;
		}
		if (damages.m_pickaxe > 0f && Hurts(((DamageModifiers)(ref mods)).GetModifier((DamageType)16)))
		{
			return true;
		}
		return false;
	}

	private static bool Hurts(DamageModifier m)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0002: Invalid comparison between Unknown and I4
		//IL_0004: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Invalid comparison between Unknown and I4
		if ((int)m != 3)
		{
			return (int)m != 4;
		}
		return false;
	}

	private static float ZdoHealth(ZNetView nv, float def)
	{
		if ((Object)(object)nv == (Object)null || !nv.IsValid())
		{
			return def;
		}
		if (s_healthHash == int.MinValue)
		{
			try
			{
				s_healthHash = (int)AccessTools.Field(typeof(ZDOVars), "s_health").GetValue(null);
			}
			catch
			{
				s_healthHash = StringExtensionMethods.GetStableHashCode("health");
			}
		}
		return nv.GetZDO().GetFloat(s_healthHash, def);
	}

	private static string Line(float cur, float max, Player p, int minTier, string chunk)
	{
		if (max <= 0f)
		{
			max = 1f;
		}
		cur = Mathf.Max(0f, cur);
		string text = Mathf.CeilToInt(cur) + " / " + Mathf.CeilToInt(max);
		if (ChipGaugePlugin.ShowPercent.Value)
		{
			text = text + "  (" + Mathf.RoundToInt(Mathf.Clamp01(cur / max) * 100f) + "%)";
		}
		if (!string.IsNullOrEmpty(chunk))
		{
			text = text + "\n" + chunk;
		}
		if (ChipGaugePlugin.ShowToolWarning.Value && minTier > 0)
		{
			int num = ((Humanoid)p).GetCurrentWeapon()?.m_shared.m_toolTier ?? 0;
			if (num < minTier)
			{
				text = text + "\n<color=red>Tool too weak (tier " + num + ", needs " + minTier + ")</color>";
			}
		}
		return text;
	}

	private static string NameOf(GameObject go)
	{
		HoverText componentInParent = go.GetComponentInParent<HoverText>();
		if ((Object)(object)componentInParent != (Object)null && !string.IsNullOrEmpty(componentInParent.m_text))
		{
			return Localize(componentInParent.m_text);
		}
		string text = ((Object)go).name;
		int num = text.IndexOf("(Clone)", StringComparison.Ordinal);
		if (num >= 0)
		{
			text = text.Substring(0, num);
		}
		text = Regex.Replace(text, "[_\\s]*\\d+$", "");
		text = text.Replace('_', ' ');
		text = Regex.Replace(text, "([a-z])([A-Z])", "$1 $2");
		text = text.Trim();
		if (text.Length <= 0)
		{
			return ((Object)go).name;
		}
		return char.ToUpperInvariant(text[0]) + text.Substring(1);
	}

	private static string Localize(string s)
	{
		if (string.IsNullOrEmpty(s))
		{
			return s;
		}
		if (Localization.instance == null)
		{
			return s;
		}
		return Localization.instance.Localize(s);
	}
}