Decompiled source of StructureHealth v1.5.1

plugins/StructureHealth/StructureHealth.dll

Decompiled 6 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("lubertmiliutin")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("MIT")]
[assembly: AssemblyFileVersion("1.5.1.0")]
[assembly: AssemblyInformationalVersion("1.5.1+d821256bef6e454729b94226289dcb5106b687cc")]
[assembly: AssemblyProduct("StructureHealth")]
[assembly: AssemblyTitle("StructureHealth")]
[assembly: AssemblyVersion("1.5.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace StructureHealth
{
	internal static class TextStyle
	{
		public static string ForFraction(float p)
		{
			if (p > 0.66f)
			{
				return "#7dd87d";
			}
			if (p > 0.33f)
			{
				return "#e9d36b";
			}
			if (p > 0f)
			{
				return "#e07070";
			}
			return "#a33a3a";
		}

		public static string WrapHoverSize(string text)
		{
			int value = StructureHealthPlugin.HoverFontSize.Value;
			if (value <= 0)
			{
				return text;
			}
			return $"<size={value}>{text}</size>";
		}
	}
	internal static class PieceNames
	{
		public static string Get(GameObject pieceObj)
		{
			if ((Object)(object)pieceObj == (Object)null)
			{
				return string.Empty;
			}
			return Resolve(pieceObj.GetComponent<Piece>(), ((Object)pieceObj).name);
		}

		public static string Get(WearNTear wnt)
		{
			if ((Object)(object)wnt == (Object)null)
			{
				return string.Empty;
			}
			return Resolve(((Component)wnt).GetComponent<Piece>(), ((Object)wnt).name);
		}

		private static string Resolve(Piece piece, string fallback)
		{
			if ((Object)(object)piece == (Object)null)
			{
				return fallback;
			}
			if (Localization.instance == null)
			{
				return piece.m_name;
			}
			return Localization.instance.Localize(piece.m_name);
		}
	}
	internal static class ValheimRefs
	{
		public static readonly MethodInfo GetSupport = AccessTools.Method(typeof(WearNTear), "GetSupport", (Type[])null, (Type[])null);

		public static readonly MethodInfo GetMaxSupport = AccessTools.Method(typeof(WearNTear), "GetMaxSupport", (Type[])null, (Type[])null);

		public static readonly MethodInfo UpdateSupport = AccessTools.Method(typeof(WearNTear), "UpdateSupport", (Type[])null, (Type[])null);

		public static readonly MethodInfo SetupColliders = AccessTools.Method(typeof(WearNTear), "SetupColliders", (Type[])null, (Type[])null);

		public static readonly MethodInfo ClearCachedSupport = AccessTools.Method(typeof(WearNTear), "ClearCachedSupport", (Type[])null, (Type[])null);

		public static readonly FieldInfo SupportField = AccessTools.Field(typeof(WearNTear), "m_support");

		public static readonly FieldInfo RayMaskField = AccessTools.Field(typeof(WearNTear), "s_rayMask");

		public static readonly FieldInfo PlacementGhostField = AccessTools.Field(typeof(Player), "m_placementGhost");

		public static readonly FieldInfo RightItemField = AccessTools.Field(typeof(Humanoid), "m_rightItem");
	}
	internal static class SafeNView
	{
		public static bool IsOwner(ZNetView nv)
		{
			if ((Object)(object)nv != (Object)null)
			{
				return nv.IsOwner();
			}
			return false;
		}

		public static bool IsValid(ZNetView nv)
		{
			if ((Object)(object)nv != (Object)null)
			{
				return nv.IsValid();
			}
			return false;
		}
	}
	internal struct PieceStats
	{
		public int CurHealth;

		public int MaxHealth;

		public float CurSupport;

		public float MaxSupport;
	}
	internal static class WearNTearInfo
	{
		public static bool TryRead(WearNTear wnt, out PieceStats stats)
		{
			stats = default(PieceStats);
			if ((Object)(object)wnt == (Object)null)
			{
				return false;
			}
			stats.MaxHealth = Mathf.RoundToInt(wnt.m_health);
			stats.CurHealth = Mathf.RoundToInt(Mathf.Clamp01(wnt.GetHealthPercentage()) * wnt.m_health);
			stats.CurSupport = SupportOf(wnt);
			stats.MaxSupport = MaxSupportOf(wnt);
			return true;
		}

		public static float SupportOf(WearNTear wnt)
		{
			return InvokeFloat(ValheimRefs.GetSupport, wnt);
		}

		public static float MaxSupportOf(WearNTear wnt)
		{
			return InvokeFloat(ValheimRefs.GetMaxSupport, wnt);
		}

		private static float InvokeFloat(MethodInfo m, WearNTear wnt)
		{
			if (m == null || (Object)(object)wnt == (Object)null)
			{
				return 0f;
			}
			try
			{
				return (float)m.Invoke(wnt, null);
			}
			catch
			{
				return 0f;
			}
		}
	}
	internal static class GhostSupport
	{
		private const float ScanHalfExtent = 1f;

		private static readonly Collider[] _hitBuffer = (Collider[])(object)new Collider[64];

		private static int _groundMask = -1;

		private static bool _useHeuristic;

		public static GameObject GetPlacementGhost(Player player)
		{
			object? obj = ValheimRefs.PlacementGhostField?.GetValue(player);
			return (GameObject)((obj is GameObject) ? obj : null);
		}

		public static bool Estimate(WearNTear ghostWnt, out float curSupport, out float maxSupport)
		{
			curSupport = 0f;
			maxSupport = WearNTearInfo.MaxSupportOf(ghostWnt);
			if ((Object)(object)ghostWnt == (Object)null)
			{
				return false;
			}
			if (!_useHeuristic && TryNative(ghostWnt, out curSupport))
			{
				return true;
			}
			return Heuristic(ghostWnt, maxSupport, out curSupport);
		}

		private static bool TryNative(WearNTear ghostWnt, out float curSupport)
		{
			curSupport = 0f;
			try
			{
				ValheimRefs.ClearCachedSupport?.Invoke(ghostWnt, null);
				ValheimRefs.SetupColliders?.Invoke(ghostWnt, null);
			}
			catch (Exception e)
			{
				DisableNative("SetupColliders", e);
				return false;
			}
			try
			{
				ValheimRefs.UpdateSupport?.Invoke(ghostWnt, null);
			}
			catch (TargetInvocationException ex) when (ex.InnerException is NullReferenceException)
			{
			}
			catch (Exception e2)
			{
				DisableNative("UpdateSupport", e2);
				return false;
			}
			if (ValheimRefs.SupportField == null)
			{
				return false;
			}
			curSupport = (float)ValheimRefs.SupportField.GetValue(ghostWnt);
			return true;
		}

		private static bool Heuristic(WearNTear ghostWnt, float maxSupport, out float curSupport)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			curSupport = 0f;
			if ((Object)(object)ghostWnt == (Object)null)
			{
				return false;
			}
			Vector3 position = ((Component)ghostWnt).transform.position;
			Quaternion rotation = ((Component)ghostWnt).transform.rotation;
			Vector3 half = default(Vector3);
			((Vector3)(ref half))..ctor(1f, 1f, 1f);
			if (IsGrounded(position, rotation, half))
			{
				curSupport = maxSupport;
				return true;
			}
			curSupport = StrongestNeighbourSupport(ghostWnt, position, rotation, half, maxSupport);
			return true;
		}

		private static bool IsGrounded(Vector3 pos, Quaternion rot, Vector3 half)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			int num = GroundMask();
			if (num == 0)
			{
				return false;
			}
			return Physics.OverlapBoxNonAlloc(pos, half, _hitBuffer, rot, num, (QueryTriggerInteraction)1) > 0;
		}

		private static float StrongestNeighbourSupport(WearNTear self, Vector3 pos, Quaternion rot, Vector3 half, float cap)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: 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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			int num = Physics.OverlapBoxNonAlloc(pos, half, _hitBuffer, rot, PieceMask(), (QueryTriggerInteraction)1);
			float num2 = 0f;
			for (int i = 0; i < num; i++)
			{
				WearNTear val = NeighbourFrom(_hitBuffer[i], self);
				if (!((Object)(object)val == (Object)null))
				{
					float num3 = WearNTearInfo.SupportOf(val);
					float num4 = DirectionalFactor(pos - ((Component)val).transform.position);
					float num5 = num3 * num4;
					if (num5 > num2)
					{
						num2 = num5;
					}
				}
			}
			return Mathf.Min(num2, cap);
		}

		private static float DirectionalFactor(Vector3 toGhost)
		{
			//IL_0017: 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_0023: Unknown result type (might be due to invalid IL or missing references)
			float sqrMagnitude = ((Vector3)(ref toGhost)).sqrMagnitude;
			float num = ((sqrMagnitude > 0.0001f) ? Vector3.Dot(toGhost / Mathf.Sqrt(sqrMagnitude), Vector3.up) : 0f);
			return Mathf.Lerp(0.55f, 0.9f, (num + 1f) * 0.5f);
		}

		private static WearNTear NeighbourFrom(Collider col, WearNTear self)
		{
			if ((Object)(object)col == (Object)null)
			{
				return null;
			}
			WearNTear componentInParent = ((Component)col).GetComponentInParent<WearNTear>();
			if ((Object)(object)componentInParent == (Object)null || (Object)(object)componentInParent == (Object)(object)self)
			{
				return null;
			}
			if ((Object)(object)((Component)componentInParent).gameObject == (Object)(object)((Component)self).gameObject)
			{
				return null;
			}
			if (!componentInParent.m_supports)
			{
				return null;
			}
			return componentInParent;
		}

		private static int GroundMask()
		{
			if (_groundMask != -1)
			{
				return _groundMask;
			}
			_groundMask = LayerMask.GetMask(new string[3] { "terrain", "static_solid", "Default" });
			return _groundMask;
		}

		private static int PieceMask()
		{
			if (ValheimRefs.RayMaskField != null && ValheimRefs.RayMaskField.GetValue(null) is int num && num != 0)
			{
				return num;
			}
			return -5;
		}

		private static void DisableNative(string where, Exception e)
		{
			Exception ex = (e as TargetInvocationException)?.InnerException ?? e;
			StructureHealthPlugin.Log.LogWarning((object)(where + " threw " + ex.GetType().Name + ": " + ex.Message + ". Using heuristic from now on."));
			_useHeuristic = true;
		}
	}
	internal static class HoverFormatter
	{
		public static string ForHover(PieceStats s)
		{
			Strings.Translation current = Strings.Current;
			string text3;
			if (StructureHealthPlugin.UseRichText.Value)
			{
				string text = TextStyle.ForFraction((s.MaxHealth > 0) ? ((float)s.CurHealth / (float)s.MaxHealth) : 0f);
				string text2 = TextStyle.ForFraction((s.MaxSupport > 0f) ? (s.CurSupport / s.MaxSupport) : 0f);
				text3 = $"<color=#dddddd>{current.Health}:</color> <color={text}>{s.CurHealth} / {s.MaxHealth}</color>\n" + $"<color=#dddddd>{current.Stability}:</color> <color={text2}>{s.CurSupport:0.#} / {s.MaxSupport:0.#}</color>";
			}
			else
			{
				text3 = $"{current.Health}: {s.CurHealth} / {s.MaxHealth}\n{current.Stability}: {s.CurSupport:0.#} / {s.MaxSupport:0.#}";
			}
			return TextStyle.WrapHoverSize(text3);
		}

		public static string ForGhost(string ghostName, int maxHealth, float curSupport, float maxSupport)
		{
			Strings.Translation current = Strings.Current;
			string text2;
			string text3;
			string text4;
			if (StructureHealthPlugin.UseRichText.Value)
			{
				string text = TextStyle.ForFraction((maxSupport > 0f) ? (curSupport / maxSupport) : 0f);
				text2 = "<color=#9fc0e0>" + current.Placing + ":</color> <b>" + ghostName + "</b>";
				text3 = $"<color=#dddddd>{current.MaxHealth}:</color> {maxHealth}";
				text4 = $"<color=#dddddd>{current.Stability}:</color> <color={text}>{curSupport:0.#} / {maxSupport:0.#}</color>";
			}
			else
			{
				text2 = current.Placing + ": " + ghostName;
				text3 = $"{current.MaxHealth}: {maxHealth}";
				text4 = $"{current.Stability}: {curSupport:0.#} / {maxSupport:0.#}";
			}
			return TextStyle.WrapHoverSize(text2 + "\n" + text3 + "\n" + text4);
		}
	}
	internal class OverlayRenderer : MonoBehaviour
	{
		public static WearNTear CurrentTarget;

		public static float LastSeenTime;

		private const float TargetTimeoutSeconds = 0.2f;

		private const string HammerItemKey = "$item_hammer";

		private GUIStyle _style;

		private int _cachedFontSize = -1;

		private void Update()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			KeyboardShortcut value = StructureHealthPlugin.ToggleOverlayKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				bool flag = !StructureHealthPlugin.ShowOverlay.Value;
				StructureHealthPlugin.ShowOverlay.Value = flag;
				StructureHealthPlugin.Log.LogInfo((object)$"Overlay panel: {flag}");
			}
		}

		private void OnGUI()
		{
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			if (StructureHealthPlugin.ShowOverlay.Value && IsTargetFresh() && IsHammerEquipped(Player.m_localPlayer) && WearNTearInfo.TryRead(CurrentTarget, out var stats))
			{
				Strings.Translation current = Strings.Current;
				string text = PieceNames.Get(CurrentTarget) + "\n" + $"{current.Health}: {stats.CurHealth} / {stats.MaxHealth}\n" + $"{current.Stability}: {stats.CurSupport:0.#} / {stats.MaxSupport:0.#}";
				EnsureStyle();
				DrawShadowed(new Rect(20f, 20f, 480f, 120f), text);
			}
		}

		private static bool IsTargetFresh()
		{
			if ((Object)(object)CurrentTarget != (Object)null)
			{
				return Time.unscaledTime - LastSeenTime <= 0.2f;
			}
			return false;
		}

		private static bool IsHammerEquipped(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			object? obj = ValheimRefs.RightItemField?.GetValue(player);
			ItemData val = (ItemData)((obj is ItemData) ? obj : null);
			if (val != null && val.m_shared != null)
			{
				return val.m_shared.m_name == "$item_hammer";
			}
			return false;
		}

		private void DrawShadowed(Rect rect, string text)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: 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_0084: Unknown result type (might be due to invalid IL or missing references)
			Color textColor = _style.normal.textColor;
			_style.normal.textColor = new Color(0f, 0f, 0f, 0.85f);
			GUI.Label(new Rect(((Rect)(ref rect)).x + 2f, ((Rect)(ref rect)).y + 2f, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height), text, _style);
			_style.normal.textColor = textColor;
			GUI.Label(rect, text, _style);
		}

		private void EnsureStyle()
		{
			//IL_0037: 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_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: 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_005d: Expected O, but got Unknown
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			int num = Mathf.Clamp(StructureHealthPlugin.OverlayFontSize.Value, 8, 64);
			if (_style == null || _cachedFontSize != num)
			{
				_cachedFontSize = num;
				_style = new GUIStyle(GUI.skin.label)
				{
					fontSize = num,
					fontStyle = (FontStyle)1,
					alignment = (TextAnchor)0,
					richText = false
				};
				_style.normal.textColor = Color.white;
			}
		}
	}
	[HarmonyPatch(typeof(WearNTear), "Highlight")]
	internal static class WearNTear_Highlight_Patch
	{
		private static void Postfix(WearNTear __instance)
		{
			OverlayRenderer.CurrentTarget = __instance;
			OverlayRenderer.LastSeenTime = Time.unscaledTime;
		}
	}
	[HarmonyPatch(typeof(Hud), "UpdateCrosshair")]
	internal static class Hud_UpdateCrosshair_Patch
	{
		private static void Postfix(Hud __instance, Player player)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)__instance == (Object)null || !((Character)player).InPlaceMode())
			{
				return;
			}
			TextMeshProUGUI hoverName = __instance.m_hoverName;
			if (!((Object)(object)hoverName == (Object)null))
			{
				if (player.InRepairMode())
				{
					AppendRepairInfo(player, hoverName);
				}
				else
				{
					OverrideWithGhostInfo(player, hoverName);
				}
			}
		}

		private static void AppendRepairInfo(Player player, TextMeshProUGUI label)
		{
			if (!StructureHealthPlugin.EnableRepairInfo.Value)
			{
				return;
			}
			Piece hoveringPiece = player.GetHoveringPiece();
			if ((Object)(object)hoveringPiece == (Object)null)
			{
				return;
			}
			WearNTear component = ((Component)hoveringPiece).GetComponent<WearNTear>();
			if (!((Object)(object)component == (Object)null) && WearNTearInfo.TryRead(component, out var stats))
			{
				string text = HoverFormatter.ForHover(stats);
				if (!string.IsNullOrEmpty(text))
				{
					((TMP_Text)label).richText = true;
					string text2 = ((TMP_Text)label).text ?? string.Empty;
					((TMP_Text)label).text = ((text2.Length > 0) ? (text2 + "\n" + text) : text);
				}
			}
		}

		private static void OverrideWithGhostInfo(Player player, TextMeshProUGUI label)
		{
			if (!StructureHealthPlugin.EnableBuildInfo.Value)
			{
				return;
			}
			GameObject placementGhost = GhostSupport.GetPlacementGhost(player);
			if (!((Object)(object)placementGhost == (Object)null))
			{
				WearNTear component = placementGhost.GetComponent<WearNTear>();
				if (!((Object)(object)component == (Object)null) && GhostSupport.Estimate(component, out var curSupport, out var maxSupport))
				{
					int maxHealth = Mathf.RoundToInt(component.m_health);
					string text = HoverFormatter.ForGhost(PieceNames.Get(placementGhost), maxHealth, curSupport, maxSupport);
					((TMP_Text)label).richText = true;
					((TMP_Text)label).text = text;
				}
			}
		}
	}
	[HarmonyPatch(typeof(WearNTear), "UpdateSupport")]
	internal static class WearNTear_UpdateSupport_Transpiler
	{
		private static readonly MethodInfo NViewIsOwner = AccessTools.Method(typeof(ZNetView), "IsOwner", (Type[])null, (Type[])null);

		private static readonly MethodInfo NViewIsValid = AccessTools.Method(typeof(ZNetView), "IsValid", (Type[])null, (Type[])null);

		private static readonly MethodInfo SafeIsOwner = AccessTools.Method(typeof(SafeNView), "IsOwner", (Type[])null, (Type[])null);

		private static readonly MethodInfo SafeIsValid = AccessTools.Method(typeof(SafeNView), "IsValid", (Type[])null, (Type[])null);

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			int earlyExitGuard = FindEarlyExitGuard(list);
			Label? continueLabel = ((earlyExitGuard > 0) ? PriorLoopContinueLabel(list, earlyExitGuard) : null);
			for (int i = 0; i < list.Count; i++)
			{
				CodeInstruction val = list[i];
				if (val.opcode == OpCodes.Callvirt && val.operand is MethodInfo methodInfo)
				{
					if (methodInfo == NViewIsOwner)
					{
						yield return new CodeInstruction(OpCodes.Call, (object)SafeIsOwner);
						continue;
					}
					if (methodInfo == NViewIsValid)
					{
						yield return new CodeInstruction(OpCodes.Call, (object)SafeIsValid);
						continue;
					}
				}
				yield return val;
				if (i == earlyExitGuard && continueLabel.HasValue)
				{
					yield return new CodeInstruction(OpCodes.Br, (object)continueLabel.Value);
				}
			}
		}

		private static int FindEarlyExitGuard(List<CodeInstruction> list)
		{
			for (int i = 1; i < list.Count - 3; i++)
			{
				if ((!(list[i].opcode != OpCodes.Brfalse_S) || !(list[i].opcode != OpCodes.Brfalse)) && !(list[i - 1].opcode != OpCodes.Call) && !((list[i - 1].operand as MethodInfo)?.Name != "op_Equality") && !(list[i + 1].opcode != OpCodes.Ldarg_0) && !(list[i + 3].opcode != OpCodes.Stfld) && !((list[i + 3].operand as FieldInfo)?.Name != "m_support"))
				{
					return i;
				}
			}
			return -1;
		}

		private static Label? PriorLoopContinueLabel(List<CodeInstruction> list, int beforeIdx)
		{
			for (int num = beforeIdx - 1; num >= 0; num--)
			{
				if (list[num].opcode == OpCodes.Br)
				{
					object operand = list[num].operand;
					if (operand is Label)
					{
						return (Label)operand;
					}
				}
			}
			return null;
		}
	}
	[BepInPlugin("com.lubert.valheim.structurehealth", "StructureHealth", "1.5.1")]
	public class StructureHealthPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.lubert.valheim.structurehealth";

		public const string PluginName = "StructureHealth";

		public const string PluginVersion = "1.5.1";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> EnableRepairInfo;

		internal static ConfigEntry<bool> EnableBuildInfo;

		internal static ConfigEntry<bool> ShowOverlay;

		internal static ConfigEntry<KeyboardShortcut> ToggleOverlayKey;

		internal static ConfigEntry<bool> UseRichText;

		internal static ConfigEntry<int> HoverFontSize;

		internal static ConfigEntry<int> OverlayFontSize;

		private Harmony _harmony;

		private GameObject _runner;

		private void Awake()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			BindConfig();
			_harmony = new Harmony("com.lubert.valheim.structurehealth");
			_harmony.PatchAll(Assembly.GetExecutingAssembly());
			_runner = new GameObject("StructureHealthRunner");
			_runner.AddComponent<OverlayRenderer>();
			Object.DontDestroyOnLoad((Object)(object)_runner);
			Log.LogInfo((object)"StructureHealth v1.5.1 loaded.");
		}

		private void OnDestroy()
		{
			if ((Object)(object)_runner != (Object)null)
			{
				Object.Destroy((Object)(object)_runner);
			}
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void BindConfig()
		{
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Expected O, but got Unknown
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Expected O, but got Unknown
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			EnableRepairInfo = ((BaseUnityPlugin)this).Config.Bind<bool>("1. Modes", "EnableRepairInfo", true, "Show current health/stability of the hovered structure while the Repair tool is selected.");
			EnableBuildInfo = ((BaseUnityPlugin)this).Config.Bind<bool>("1. Modes", "EnableBuildInfo", true, "Show max health and predicted stability for the placement ghost while building a new piece.");
			ShowOverlay = ((BaseUnityPlugin)this).Config.Bind<bool>("1. Modes", "ShowOverlayPanel", false, "Draw a parallel info panel via IMGUI in the top-left corner of the screen.");
			UseRichText = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Display", "UseRichText", true, "Use TMP rich-text formatting (bold labels, coloured values) in the cursor hover label.");
			HoverFontSize = ((BaseUnityPlugin)this).Config.Bind<int>("2. Display", "HoverFontSize", 18, new ConfigDescription("Font size (TMP units) for the info text appended to the cursor hover label. 0 = inherit Valheim's default.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 48), Array.Empty<object>()));
			OverlayFontSize = ((BaseUnityPlugin)this).Config.Bind<int>("2. Display", "OverlayFontSize", 16, new ConfigDescription("Font size (pixels) for the IMGUI overlay in the top-left corner.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(8, 64), Array.Empty<object>()));
			ToggleOverlayKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("3. Input", "ToggleOverlayKey", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Toggle the IMGUI overlay panel on/off.");
		}
	}
	internal static class Strings
	{
		public class Translation
		{
			public string Health;

			public string Stability;

			public string Placing;

			public string MaxHealth;
		}

		private static readonly Translation _fallback = new Translation
		{
			Health = "Health",
			Stability = "Stability",
			Placing = "Placing",
			MaxHealth = "Max Health"
		};

		private static readonly Dictionary<string, Translation> _byLanguage = new Dictionary<string, Translation>
		{
			["English"] = _fallback,
			["Ukrainian"] = new Translation
			{
				Health = "Здоров'я",
				Stability = "Стабільність",
				Placing = "Розміщення",
				MaxHealth = "Макс. здоров'я"
			},
			["German"] = new Translation
			{
				Health = "Leben",
				Stability = "Stabilität",
				Placing = "Platzierung",
				MaxHealth = "Max. Leben"
			},
			["French"] = new Translation
			{
				Health = "Santé",
				Stability = "Stabilité",
				Placing = "Placement",
				MaxHealth = "Santé max."
			},
			["Spanish"] = new Translation
			{
				Health = "Salud",
				Stability = "Estabilidad",
				Placing = "Colocación",
				MaxHealth = "Salud máx."
			}
		};

		public static Translation Current
		{
			get
			{
				string text = ((Localization.instance != null) ? Localization.instance.GetSelectedLanguage() : null);
				if (string.IsNullOrEmpty(text) || !_byLanguage.TryGetValue(text, out var value))
				{
					return _fallback;
				}
				return value;
			}
		}
	}
}