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 $"{text}";
}
}
internal static class PieceNames
{
public static string Get(GameObject pieceObj)
{
if ((Object)(object)pieceObj == (Object)null)
{
return string.Empty;
}
return Resolve(pieceObj.GetComponent(), ((Object)pieceObj).name);
}
public static string Get(WearNTear wnt)
{
if ((Object)(object)wnt == (Object)null)
{
return string.Empty;
}
return Resolve(((Component)wnt).GetComponent(), ((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();
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 = $"{current.Health}:{s.CurHealth} / {s.MaxHealth}\n" + $"{current.Stability}:{s.CurSupport:0.#} / {s.MaxSupport:0.#}";
}
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 = "" + current.Placing + ":" + ghostName + "";
text3 = $"{current.MaxHealth}: {maxHealth}";
text4 = $"{current.Stability}:{curSupport:0.#} / {maxSupport:0.#}";
}
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();
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();
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 Transpiler(IEnumerable instructions)
{
List list = new List(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 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 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 EnableRepairInfo;
internal static ConfigEntry EnableBuildInfo;
internal static ConfigEntry ShowOverlay;
internal static ConfigEntry ToggleOverlayKey;
internal static ConfigEntry UseRichText;
internal static ConfigEntry HoverFontSize;
internal static ConfigEntry 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();
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("1. Modes", "EnableRepairInfo", true, "Show current health/stability of the hovered structure while the Repair tool is selected.");
EnableBuildInfo = ((BaseUnityPlugin)this).Config.Bind("1. Modes", "EnableBuildInfo", true, "Show max health and predicted stability for the placement ghost while building a new piece.");
ShowOverlay = ((BaseUnityPlugin)this).Config.Bind("1. Modes", "ShowOverlayPanel", false, "Draw a parallel info panel via IMGUI in the top-left corner of the screen.");
UseRichText = ((BaseUnityPlugin)this).Config.Bind("2. Display", "UseRichText", true, "Use TMP rich-text formatting (bold labels, coloured values) in the cursor hover label.");
HoverFontSize = ((BaseUnityPlugin)this).Config.Bind("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(0, 48), Array.Empty