Decompiled source of NoCapLumber v1.0.0

NoCapLumber.dll

Decompiled 3 weeks ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis;
using ServerSync;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Core.ObjectPool;
using YamlDotNet.Core.Tokens;
using YamlDotNet.Helpers;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.BufferedDeserialization;
using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators;
using YamlDotNet.Serialization.Callbacks;
using YamlDotNet.Serialization.Converters;
using YamlDotNet.Serialization.EventEmitters;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization.NodeDeserializers;
using YamlDotNet.Serialization.NodeTypeResolvers;
using YamlDotNet.Serialization.ObjectFactories;
using YamlDotNet.Serialization.ObjectGraphTraversalStrategies;
using YamlDotNet.Serialization.ObjectGraphVisitors;
using YamlDotNet.Serialization.Schemas;
using YamlDotNet.Serialization.TypeInspectors;
using YamlDotNet.Serialization.TypeResolvers;
using YamlDotNet.Serialization.Utilities;
using YamlDotNet.Serialization.ValueDeserializers;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("NoCapLumber")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("sighsorry")]
[assembly: AssemblyProduct("NoCapLumber")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")]
[assembly: AssemblyFileVersion("1.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 LocalizationManager
{
	[PublicAPI]
	public class Localizer
	{
		private static readonly Dictionary<string, Dictionary<string, Func<string>>> PlaceholderProcessors;

		private static readonly Dictionary<string, Dictionary<string, string>> loadedTexts;

		private static readonly ConditionalWeakTable<Localization, string> localizationLanguage;

		private static readonly List<WeakReference<Localization>> localizationObjects;

		private static BaseUnityPlugin? _plugin;

		private static readonly List<string> fileExtensions;

		private static BaseUnityPlugin plugin
		{
			get
			{
				//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b2: Expected O, but got Unknown
				if (_plugin == null)
				{
					IEnumerable<TypeInfo> source;
					try
					{
						source = Assembly.GetExecutingAssembly().DefinedTypes.ToList();
					}
					catch (ReflectionTypeLoadException ex)
					{
						source = from t in ex.Types
							where t != null
							select t.GetTypeInfo();
					}
					_plugin = (BaseUnityPlugin)Chainloader.ManagerObject.GetComponent((Type)source.First((TypeInfo t) => t.IsClass && typeof(BaseUnityPlugin).IsAssignableFrom(t)));
				}
				return _plugin;
			}
		}

		public static event Action? OnLocalizationComplete;

		private static void UpdatePlaceholderText(Localization localization, string key)
		{
			localizationLanguage.TryGetValue(localization, out string value);
			string text = loadedTexts[value][key];
			if (PlaceholderProcessors.TryGetValue(key, out Dictionary<string, Func<string>> value2))
			{
				text = value2.Aggregate(text, (string current, KeyValuePair<string, Func<string>> kv) => current.Replace("{" + kv.Key + "}", kv.Value()));
			}
			localization.AddWord(key, text);
		}

		public static void AddPlaceholder<T>(string key, string placeholder, ConfigEntry<T> config, Func<T, string>? convertConfigValue = null) where T : notnull
		{
			string key2 = key;
			string placeholder2 = placeholder;
			Func<T, string> convertConfigValue2 = convertConfigValue;
			ConfigEntry<T> config2 = config;
			if (convertConfigValue2 == null)
			{
				convertConfigValue2 = (T val) => val.ToString();
			}
			if (!PlaceholderProcessors.ContainsKey(key2))
			{
				PlaceholderProcessors[key2] = new Dictionary<string, Func<string>>();
			}
			config2.SettingChanged += delegate
			{
				UpdatePlaceholder();
			};
			if (loadedTexts.ContainsKey(Localization.instance.GetSelectedLanguage()))
			{
				UpdatePlaceholder();
			}
			void UpdatePlaceholder()
			{
				PlaceholderProcessors[key2][placeholder2] = () => convertConfigValue2(config2.Value);
				UpdatePlaceholderText(Localization.instance, key2);
			}
		}

		public static void AddText(string key, string text)
		{
			List<WeakReference<Localization>> list = new List<WeakReference<Localization>>();
			foreach (WeakReference<Localization> localizationObject in localizationObjects)
			{
				if (localizationObject.TryGetTarget(out var target))
				{
					Dictionary<string, string> dictionary = loadedTexts[localizationLanguage.GetOrCreateValue(target)];
					if (!target.m_translations.ContainsKey(key))
					{
						dictionary[key] = text;
						target.AddWord(key, text);
					}
				}
				else
				{
					list.Add(localizationObject);
				}
			}
			foreach (WeakReference<Localization> item in list)
			{
				localizationObjects.Remove(item);
			}
		}

		public static void Load()
		{
			_ = plugin;
		}

		public static void LoadLocalizationLater(Localization __instance)
		{
			LoadLocalization(Localization.instance, __instance.GetSelectedLanguage());
		}

		public static void SafeCallLocalizeComplete()
		{
			Localizer.OnLocalizationComplete?.Invoke();
		}

		private static void LoadLocalization(Localization __instance, string language)
		{
			if (!localizationLanguage.Remove(__instance))
			{
				localizationObjects.Add(new WeakReference<Localization>(__instance));
			}
			localizationLanguage.Add(__instance, language);
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			foreach (string item in from f in Directory.GetFiles(Path.GetDirectoryName(Paths.PluginPath), plugin.Info.Metadata.Name + ".*", SearchOption.AllDirectories)
				where fileExtensions.IndexOf(Path.GetExtension(f)) >= 0
				select f)
			{
				string[] array = Path.GetFileNameWithoutExtension(item).Split(new char[1] { '.' });
				if (array.Length >= 2)
				{
					string text = array[1];
					if (dictionary.ContainsKey(text))
					{
						Debug.LogWarning((object)("Duplicate key " + text + " found for " + plugin.Info.Metadata.Name + ". The duplicate file found at " + item + " will be skipped."));
					}
					else
					{
						dictionary[text] = item;
					}
				}
			}
			byte[] array2 = LoadTranslationFromAssembly("English");
			if (array2 == null)
			{
				throw new Exception("Found no English localizations in mod " + plugin.Info.Metadata.Name + ". Expected an embedded resource translations/English.json or translations/English.yml.");
			}
			Dictionary<string, string> dictionary2 = new DeserializerBuilder().IgnoreFields().Build().Deserialize<Dictionary<string, string>>(Encoding.UTF8.GetString(array2));
			if (dictionary2 == null)
			{
				throw new Exception("Localization for mod " + plugin.Info.Metadata.Name + " failed: Localization file was empty.");
			}
			string text2 = null;
			if (language != "English")
			{
				if (dictionary.TryGetValue(language, out var value))
				{
					text2 = File.ReadAllText(value);
				}
				else
				{
					byte[] array3 = LoadTranslationFromAssembly(language);
					if (array3 != null)
					{
						text2 = Encoding.UTF8.GetString(array3);
					}
				}
			}
			if (text2 == null && dictionary.TryGetValue("English", out var value2))
			{
				text2 = File.ReadAllText(value2);
			}
			if (text2 != null)
			{
				foreach (KeyValuePair<string, string> item2 in new DeserializerBuilder().IgnoreFields().Build().Deserialize<Dictionary<string, string>>(text2) ?? new Dictionary<string, string>())
				{
					dictionary2[item2.Key] = item2.Value;
				}
			}
			loadedTexts[language] = dictionary2;
			foreach (KeyValuePair<string, string> item3 in dictionary2)
			{
				UpdatePlaceholderText(__instance, item3.Key);
			}
		}

		static Localizer()
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Expected O, but got Unknown
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Expected O, but got Unknown
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Expected O, but got Unknown
			PlaceholderProcessors = new Dictionary<string, Dictionary<string, Func<string>>>();
			loadedTexts = new Dictionary<string, Dictionary<string, string>>();
			localizationLanguage = new ConditionalWeakTable<Localization, string>();
			localizationObjects = new List<WeakReference<Localization>>();
			fileExtensions = new List<string>(2) { ".json", ".yml" };
			Harmony val = new Harmony("org.bepinex.helpers.LocalizationManager");
			val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(Localization), "SetupLanguage", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "LoadLocalization", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(FejdStartup), "SetupGui", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "LoadLocalizationLater", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(FejdStartup), "Start", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "SafeCallLocalizeComplete", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private static byte[]? LoadTranslationFromAssembly(string language)
		{
			foreach (string fileExtension in fileExtensions)
			{
				byte[] array = ReadEmbeddedFileBytes("translations." + language + fileExtension);
				if (array != null)
				{
					return array;
				}
			}
			return null;
		}

		public static byte[]? ReadEmbeddedFileBytes(string resourceFileName, Assembly? containingAssembly = null)
		{
			string resourceFileName2 = resourceFileName;
			using MemoryStream memoryStream = new MemoryStream();
			if ((object)containingAssembly == null)
			{
				containingAssembly = Assembly.GetCallingAssembly();
			}
			string text = containingAssembly.GetManifestResourceNames().FirstOrDefault((string str) => str.EndsWith(resourceFileName2, StringComparison.Ordinal));
			if (text != null)
			{
				containingAssembly.GetManifestResourceStream(text)?.CopyTo(memoryStream);
			}
			return (memoryStream.Length == 0L) ? null : memoryStream.ToArray();
		}
	}
	public static class LocalizationManagerVersion
	{
		public const string Version = "1.4.1";
	}
}
namespace NoCapLumber
{
	[HarmonyPatch(typeof(Attack), "DoMeleeAttack")]
	public static class AxeTreeComboPatch
	{
		[HarmonyPrefix]
		private static void Prefix(Attack __instance)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Invalid comparison between Unknown and I4
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Invalid comparison between Unknown and I4
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			if (NoCapLumberPlugin._enableAxeComboOnTrees.Value.IsOn())
			{
				ItemData weapon = __instance.GetWeapon();
				if (weapon?.m_shared != null && (int)weapon.m_shared.m_skillType == 7 && (__instance.m_resetChainIfHit & 2) != 0)
				{
					__instance.m_resetChainIfHit = (DestructibleType)(__instance.m_resetChainIfHit & -3);
				}
			}
		}
	}
	public static class NextSwingLungeState
	{
		private sealed class CharacterState
		{
			public int PendingSwingCount;

			public float PendingExpireAt;

			public int LastStartedChainLevel = -1;

			public bool LastStartedWasAxe;

			public float LastStartedAt;
		}

		private sealed class AttackState
		{
			public float LungeMultiplier = 1f;
		}

		private static readonly ConditionalWeakTable<Character, CharacterState> CharacterStates = new ConditionalWeakTable<Character, CharacterState>();

		private static readonly ConditionalWeakTable<Attack, AttackState> AttackStates = new ConditionalWeakTable<Attack, AttackState>();

		private const float PendingDurationSeconds = 1.25f;

		private const float ComboReturnWindowSeconds = 1.5f;

		private const int PendingSwingCountPerTreeChopHit = 1;

		public static void MarkPending(Character attacker)
		{
			if (!((Object)(object)attacker == (Object)null))
			{
				CharacterState orCreateValue = CharacterStates.GetOrCreateValue(attacker);
				orCreateValue.PendingSwingCount = Math.Max(orCreateValue.PendingSwingCount, 1);
				orCreateValue.PendingExpireAt = Time.time + 1.25f;
			}
		}

		public static bool HasPending(Character attacker)
		{
			if ((Object)(object)attacker == (Object)null)
			{
				return false;
			}
			if (!CharacterStates.TryGetValue(attacker, out CharacterState value))
			{
				return false;
			}
			if (Time.time > value.PendingExpireAt)
			{
				value.PendingSwingCount = 0;
				return false;
			}
			return value.PendingSwingCount > 0;
		}

		public static bool TryConsumePending(Character attacker)
		{
			if ((Object)(object)attacker == (Object)null)
			{
				return false;
			}
			if (!CharacterStates.TryGetValue(attacker, out CharacterState value))
			{
				return false;
			}
			if (Time.time > value.PendingExpireAt)
			{
				value.PendingSwingCount = 0;
				return false;
			}
			if (value.PendingSwingCount <= 0)
			{
				return false;
			}
			value.PendingSwingCount--;
			return true;
		}

		public static bool IsComboReturnFirstSwing(Character attacker, int currentChainLevel, bool currentIsAxe)
		{
			if ((Object)(object)attacker == (Object)null || !currentIsAxe || currentChainLevel < 0)
			{
				return false;
			}
			if (!CharacterStates.TryGetValue(attacker, out CharacterState value))
			{
				return false;
			}
			if (!value.LastStartedWasAxe || value.LastStartedChainLevel < 0)
			{
				return false;
			}
			if (Time.time - value.LastStartedAt > 1.5f)
			{
				return false;
			}
			return value.LastStartedChainLevel > currentChainLevel;
		}

		public static void NoteSwingStart(Character attacker, int chainLevel, bool isAxe)
		{
			if (!((Object)(object)attacker == (Object)null))
			{
				CharacterState orCreateValue = CharacterStates.GetOrCreateValue(attacker);
				orCreateValue.LastStartedChainLevel = chainLevel;
				orCreateValue.LastStartedWasAxe = isAxe;
				orCreateValue.LastStartedAt = Time.time;
			}
		}

		public static void SetAttackLungeMultiplier(Attack attack, float lungeMultiplier)
		{
			if (attack != null)
			{
				AttackStates.GetOrCreateValue(attack).LungeMultiplier = Mathf.Clamp01(lungeMultiplier);
			}
		}

		public static bool TryGetAttackLungeMultiplier(Attack attack, out float lungeMultiplier)
		{
			lungeMultiplier = 1f;
			if (attack == null)
			{
				return false;
			}
			if (!AttackStates.TryGetValue(attack, out AttackState value))
			{
				return false;
			}
			lungeMultiplier = value.LungeMultiplier;
			return true;
		}

		public static void ClearAttack(Attack attack)
		{
			if (attack != null)
			{
				AttackStates.Remove(attack);
			}
		}
	}
	[HarmonyPatch(typeof(Attack))]
	public static class NextSwingLungeAttackPatch
	{
		private static readonly RaycastHit[] TreeCheckHits = (RaycastHit[])(object)new RaycastHit[64];

		private const float SuppressedLungeMultiplier = 0f;

		[HarmonyPatch("Start", new Type[]
		{
			typeof(Humanoid),
			typeof(Rigidbody),
			typeof(ZSyncAnimation),
			typeof(CharacterAnimEvent),
			typeof(VisEquipment),
			typeof(ItemData),
			typeof(Attack),
			typeof(float),
			typeof(float)
		})]
		[HarmonyPostfix]
		private static void StartPostfix(bool __result, Attack __instance, Humanoid character, ItemData weapon)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Invalid comparison between Unknown and I4
			if (!__result)
			{
				return;
			}
			int currentAttackCainLevel = __instance.m_currentAttackCainLevel;
			bool flag = weapon?.m_shared != null && (int)weapon.m_shared.m_skillType == 7;
			bool flag2 = NextSwingLungeState.IsComboReturnFirstSwing((Character)(object)character, currentAttackCainLevel, flag);
			try
			{
				if (!NoCapLumberPlugin._enableNextSwingLungeSuppressionAfterTreeChop.Value.IsOff())
				{
					float num = 0f;
					if (flag && (currentAttackCainLevel > 0 || flag2) && NextSwingLungeState.HasPending((Character)(object)character) && (flag2 || IsLikelyWoodcuttingTarget(character, __instance.m_attackRange)) && NextSwingLungeState.TryConsumePending((Character)(object)character))
					{
						__instance.m_speedFactor *= num;
						NextSwingLungeState.SetAttackLungeMultiplier(__instance, num);
					}
				}
			}
			finally
			{
				NextSwingLungeState.NoteSwingStart((Character)(object)character, currentAttackCainLevel, flag);
			}
		}

		[HarmonyPatch("Stop")]
		[HarmonyPrefix]
		private static void StopPrefix(Attack __instance)
		{
			NextSwingLungeState.ClearAttack(__instance);
		}

		private static bool IsLikelyWoodcuttingTarget(Humanoid character, float attackRange)
		{
			//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_003c: 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)
			if ((Object)(object)character == (Object)null)
			{
				return false;
			}
			Vector3 centerPoint = ((Character)character).GetCenterPoint();
			Vector3 forward = ((Component)character).transform.forward;
			float num = Mathf.Max(1.5f, attackRange + 0.75f);
			int num2 = Physics.SphereCastNonAlloc(centerPoint, 1f, forward, TreeCheckHits, num, -1, (QueryTriggerInteraction)1);
			for (int i = 0; i < num2; i++)
			{
				Collider collider = ((RaycastHit)(ref TreeCheckHits[i])).collider;
				if (!((Object)(object)collider == (Object)null))
				{
					GameObject val = Projectile.FindHitObject(collider);
					if ((Object)(object)val == (Object)null)
					{
						val = ((Component)collider).gameObject;
					}
					if (!((Object)(object)val == (Object)(object)((Component)character).gameObject) && WoodcuttingTargetUtils.IsWoodcuttingTarget(val))
					{
						return true;
					}
				}
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Character), "AddRootMotion")]
	public static class NextSwingLungeRootMotionPatch
	{
		[HarmonyPrefix]
		private static void Prefix(Character __instance, ref Vector3 vel)
		{
			Humanoid val = (Humanoid)(object)((__instance is Humanoid) ? __instance : null);
			if (val != null)
			{
				Attack currentAttack = val.m_currentAttack;
				if (currentAttack != null && NextSwingLungeState.TryGetAttackLungeMultiplier(currentAttack, out var lungeMultiplier) && !(lungeMultiplier >= 1f))
				{
					vel.x *= lungeMultiplier;
					vel.z *= lungeMultiplier;
				}
			}
		}
	}
	[BepInPlugin("sighsorry.NoCapLumber", "NoCapLumber", "1.0.0")]
	public class NoCapLumberPlugin : BaseUnityPlugin
	{
		public enum Toggle
		{
			On = 1,
			Off = 0
		}

		private class ConfigurationManagerAttributes
		{
			[UsedImplicitly]
			public int? Order = null;

			[UsedImplicitly]
			public bool? Browsable = null;

			[UsedImplicitly]
			public string? Category = null;

			[UsedImplicitly]
			public Action<ConfigEntryBase>? CustomDrawer = null;
		}

		private class AcceptableShortcuts : AcceptableValueBase
		{
			public AcceptableShortcuts()
				: base(typeof(KeyboardShortcut))
			{
			}

			public override object Clamp(object value)
			{
				return value;
			}

			public override bool IsValid(object value)
			{
				return true;
			}

			public override string ToDescriptionString()
			{
				return "# Acceptable values: " + string.Join(", ", UnityInput.Current.SupportedKeyCodes);
			}
		}

		internal const string ModName = "NoCapLumber";

		internal const string ModVersion = "1.0.0";

		internal const string Author = "sighsorry";

		private const string ModGUID = "sighsorry.NoCapLumber";

		private static string ConfigFileName = "sighsorry.NoCapLumber.cfg";

		private static string ConfigFileFullPath;

		internal static string ConnectionError;

		private static NoCapLumberPlugin? _instance;

		private readonly Harmony _harmony = new Harmony("sighsorry.NoCapLumber");

		public static readonly ManualLogSource NoCapLumberLogger;

		private static readonly ConfigSync ConfigSync;

		private FileSystemWatcher? _watcher;

		private readonly object _reloadLock = new object();

		private DateTime _lastConfigReloadTime;

		private DateTime _suppressWatcherUntil;

		private const long RELOAD_DELAY = 10000000L;

		private const long LOCAL_SAVE_SUPPRESS_DELAY = 15000000L;

		internal const string TreeHealthNumberUiOff = "Off";

		internal const string TreeHealthNumberUiTreeHpCurrentMax = "Tree HP Current/Max";

		internal const string TreeHealthNumberUiTreeCurrentMax = "Tree Current/Max";

		internal const string TreeHealthNumberUiCurrentMax = "Current/Max";

		private static ConfigEntry<Toggle> _serverConfigLocked;

		internal static ConfigEntry<Toggle> _enableAxeComboOnTrees;

		internal static ConfigEntry<float> _oneHandedAxeTreeChopDamageMultiplier;

		internal static ConfigEntry<float> _dualAxesTreeChopDamageMultiplier;

		internal static ConfigEntry<float> _battleAxeTreeChopDamageMultiplier;

		internal static ConfigEntry<Toggle> _blockAxeSlashOnStumpTargets;

		internal static ConfigEntry<Toggle> _enableNextSwingLungeSuppressionAfterTreeChop;

		internal static ConfigEntry<string> _treeHealthNumberUiMode;

		internal static ConfigEntry<Toggle> _treeHealthNumberUiDragLock;

		internal static ConfigEntry<Toggle> _treeHealthNumberUiSizeLock;

		internal static ConfigEntry<float> _treeHealthNumberUiPosX;

		internal static ConfigEntry<float> _treeHealthNumberUiPosY;

		internal static ConfigEntry<float> _treeHealthNumberUiFontSize;

		internal static ConfigEntry<Toggle> _showTreeHealthBarUi;

		internal static ConfigEntry<Toggle> _treeHealthBarUiDragLock;

		internal static ConfigEntry<Toggle> _treeHealthBarUiSizeLock;

		internal static ConfigEntry<float> _treeHealthBarUiPosX;

		internal static ConfigEntry<float> _treeHealthBarUiPosY;

		internal static ConfigEntry<float> _treeHealthBarUiWidth;

		internal static ConfigEntry<float> _treeHealthBarUiHeight;

		internal static ConfigEntry<string> _treeHealthBarHighHpColor;

		internal static ConfigEntry<string> _treeHealthBarMidHpColor;

		internal static ConfigEntry<string> _treeHealthBarLowHpColor;

		internal static ConfigEntry<float> _treeHealthUiDurationSeconds;

		public void Awake()
		{
			_instance = this;
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			_serverConfigLocked = config("1 - General", "Lock Configuration", Toggle.On, orderedDescription("If on, the configuration is locked and can be changed by server admins only.", 700));
			ConfigSync.AddLockingConfigEntry<Toggle>(_serverConfigLocked);
			_oneHandedAxeTreeChopDamageMultiplier = config("1 - General", "One-Handed Axe Tree Chop Damage Multiplier", 0.85f, orderedDescription("Multiplier applied to one-handed axe chop damage (AnimationState.OneHanded) against TreeBase/TreeLog.", 690, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 2f)));
			_battleAxeTreeChopDamageMultiplier = config("1 - General", "Battle Axe Tree Chop Damage Multiplier", 0.7f, orderedDescription("Multiplier applied to battle-axe chop damage (AnimationState.TwoHandedAxe) against TreeBase/TreeLog.", 680, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 2f)));
			_dualAxesTreeChopDamageMultiplier = config("1 - General", "Dual Axes Tree Chop Damage Multiplier", 0.55f, orderedDescription("Multiplier applied to dual-axes chop damage (AnimationState.DualAxes) against TreeBase/TreeLog.", 670, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 2f)));
			_enableAxeComboOnTrees = config("1 - General", "Enable Axe Combo On Trees", Toggle.On, orderedDescription("If on, axe combo attacks won't reset to the first swing when hitting TreeBase/Tree targets.", 660));
			_blockAxeSlashOnStumpTargets = config("1 - General", "Block Axe Slash On Stump Targets", Toggle.On, orderedDescription("If on, player axe hits against stump targets remove slash damage so damage follows chop-based behavior.", 650));
			_enableNextSwingLungeSuppressionAfterTreeChop = config("1 - General", "Enable Next Swing Lunge Suppression After Tree Chop", Toggle.On, orderedDescription("If on, after a player deals chop damage to a woodcutting target with an axe, the next eligible combo swing has no forward lunge (server-synced).", 640));
			_treeHealthUiDurationSeconds = config("2 - UI", "Tree Health UI Duration Seconds", 1.5f, orderedDescription("How long the tree health UI stays visible after a valid hit. Recommended range: 0.5 to 10.", 500), synchronizedSetting: false);
			_showTreeHealthBarUi = config("2 - UI", "Show Tree Health Bar UI", Toggle.On, orderedDescription("If on, shows tree health bar when you hit with chop damage.", 490), synchronizedSetting: false);
			_treeHealthBarHighHpColor = config("2 - UI", "Tree Health Bar Color High HP", "#3ACC57", orderedDescription("Color used when tree HP is at or above 66.7%. Supports #RRGGBB or #RRGGBBAA.", 480), synchronizedSetting: false);
			_treeHealthBarMidHpColor = config("2 - UI", "Tree Health Bar Color Mid HP", "#E6C12A", orderedDescription("Color used when tree HP is at or above 33.3% and below 66.7%. Supports #RRGGBB or #RRGGBBAA.", 470), synchronizedSetting: false);
			_treeHealthBarLowHpColor = config("2 - UI", "Tree Health Bar Color Low HP", "#D94A44", orderedDescription("Color used when tree HP is below 33.3%. Supports #RRGGBB or #RRGGBBAA.", 460), synchronizedSetting: false);
			_treeHealthBarUiHeight = config("2 - UI", "Tree Health Bar UI Height", 20f, orderedDescription("Height of the health bar in UI units. Recommended range: 5 to 50.", 450), synchronizedSetting: false);
			_treeHealthBarUiWidth = config("2 - UI", "Tree Health Bar UI Width", 320f, orderedDescription("Width of the health bar in UI units. Recommended range: 50 to 500.", 440), synchronizedSetting: false);
			_treeHealthBarUiSizeLock = config("2 - UI", "Tree Health Bar UI Size Lock", Toggle.Off, orderedDescription("If on, resizing health bar UI with mouse is disabled.", 430), synchronizedSetting: false);
			_treeHealthBarUiPosX = config("2 - UI", "Tree Health Bar UI Position X", 0.5f, orderedDescription("Horizontal position of the health bar in normalized screen space. Recommended range: 0 to 1.", 420), synchronizedSetting: false);
			_treeHealthBarUiPosY = config("2 - UI", "Tree Health Bar UI Position Y", 0.76f, orderedDescription("Vertical position of the health bar in normalized screen space. Recommended range: 0 to 1.", 410), synchronizedSetting: false);
			_treeHealthBarUiDragLock = config("2 - UI", "Tree Health Bar UI Drag Lock", Toggle.Off, orderedDescription("If on, dragging (moving position) of health bar UI with mouse is disabled.", 400), synchronizedSetting: false);
			_treeHealthNumberUiMode = config("2 - UI", "Show Tree Health Number UI", "Tree HP Current/Max", orderedDescription("Number UI mode.", 390, (AcceptableValueBase?)(object)new AcceptableValueList<string>(new string[4] { "Off", "Tree HP Current/Max", "Tree Current/Max", "Current/Max" })), synchronizedSetting: false);
			_treeHealthNumberUiFontSize = config("2 - UI", "Tree Health Number UI Font Size", 22f, orderedDescription("Font size of the number UI. Recommended range: 10 to 50.", 380), synchronizedSetting: false);
			_treeHealthNumberUiSizeLock = config("2 - UI", "Tree Health Number UI Size Lock", Toggle.Off, orderedDescription("If on, resizing number UI (font size) with mouse is disabled.", 370), synchronizedSetting: false);
			_treeHealthNumberUiPosX = config("2 - UI", "Tree Health Number UI Position X", 0.5f, orderedDescription("Horizontal position of the number UI in normalized screen space. Recommended range: 0 to 1.", 360), synchronizedSetting: false);
			_treeHealthNumberUiPosY = config("2 - UI", "Tree Health Number UI Position Y", 0.79f, orderedDescription("Vertical position of the number UI in normalized screen space. Recommended range: 0 to 1.", 350), synchronizedSetting: false);
			_treeHealthNumberUiDragLock = config("2 - UI", "Tree Health Number UI Drag Lock", Toggle.Off, orderedDescription("If on, dragging (moving position) of number UI with mouse is disabled.", 340), synchronizedSetting: false);
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			_harmony.PatchAll(executingAssembly);
			SaveWithRespectToConfigSet();
			if (saveOnConfigSet)
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			}
			SetupWatcher();
		}

		private void OnDestroy()
		{
			if (_instance == this)
			{
				_instance = null;
			}
			SaveWithRespectToConfigSet();
			_watcher?.Dispose();
		}

		internal static void SaveConfigToDisk()
		{
			try
			{
				_instance?.SaveWithRespectToConfigSet();
			}
			catch (Exception ex)
			{
				NoCapLumberLogger.LogWarning((object)("Failed to save config: " + ex.Message));
			}
		}

		private void SetupWatcher()
		{
			_watcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName);
			_watcher.Changed += ReadConfigValues;
			_watcher.Created += ReadConfigValues;
			_watcher.Renamed += ReadConfigValues;
			_watcher.IncludeSubdirectories = true;
			_watcher.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			_watcher.EnableRaisingEvents = true;
		}

		private void ReadConfigValues(object sender, FileSystemEventArgs e)
		{
			DateTime utcNow = DateTime.UtcNow;
			if (utcNow < _suppressWatcherUntil)
			{
				return;
			}
			long num = utcNow.Ticks - _lastConfigReloadTime.Ticks;
			if (num < 10000000)
			{
				return;
			}
			lock (_reloadLock)
			{
				utcNow = DateTime.UtcNow;
				if (utcNow < _suppressWatcherUntil)
				{
					return;
				}
				if (!File.Exists(ConfigFileFullPath))
				{
					NoCapLumberLogger.LogWarning((object)"Config file does not exist. Skipping reload.");
					return;
				}
				try
				{
					NoCapLumberLogger.LogDebug((object)"Reloading configuration...");
					SaveWithRespectToConfigSet(reload: true, save: false);
					NoCapLumberLogger.LogInfo((object)"Configuration reload complete.");
				}
				catch (Exception ex)
				{
					NoCapLumberLogger.LogError((object)("Error reloading configuration: " + ex.Message));
				}
			}
			_lastConfigReloadTime = utcNow;
		}

		private void SaveWithRespectToConfigSet(bool reload = false, bool save = true)
		{
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			try
			{
				if (reload)
				{
					((BaseUnityPlugin)this).Config.Reload();
				}
				if (save)
				{
					MarkLocalConfigWrite();
					((BaseUnityPlugin)this).Config.Save();
				}
			}
			finally
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			}
		}

		private void MarkLocalConfigWrite()
		{
			_suppressWatcherUntil = DateTime.UtcNow.AddTicks(15000000L);
		}

		private ConfigEntry<T> config<T>(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			ConfigDescription val = new ConfigDescription(description.Description + (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]"), description.AcceptableValues, description.Tags);
			ConfigEntry<T> val2 = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, val);
			SyncedConfigEntry<T> syncedConfigEntry = ConfigSync.AddConfigEntry<T>(val2);
			syncedConfigEntry.SynchronizedConfig = synchronizedSetting;
			return val2;
		}

		private ConfigEntry<T> config<T>(string group, string name, T value, string description, bool synchronizedSetting = true)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			return config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting);
		}

		private static ConfigDescription orderedDescription(string description, int order, AcceptableValueBase? acceptableValues = null)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			return new ConfigDescription(description, acceptableValues, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = order
				}
			});
		}

		static NoCapLumberPlugin()
		{
			string configPath = Paths.ConfigPath;
			char directorySeparatorChar = Path.DirectorySeparatorChar;
			ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName;
			ConnectionError = "";
			NoCapLumberLogger = Logger.CreateLogSource("NoCapLumber");
			ConfigSync = new ConfigSync("sighsorry.NoCapLumber")
			{
				DisplayName = "NoCapLumber",
				CurrentVersion = "1.0.0",
				MinimumRequiredVersion = "1.0.0"
			};
			_serverConfigLocked = null;
			_enableAxeComboOnTrees = null;
			_oneHandedAxeTreeChopDamageMultiplier = null;
			_dualAxesTreeChopDamageMultiplier = null;
			_battleAxeTreeChopDamageMultiplier = null;
			_blockAxeSlashOnStumpTargets = null;
			_enableNextSwingLungeSuppressionAfterTreeChop = null;
			_treeHealthNumberUiMode = null;
			_treeHealthNumberUiDragLock = null;
			_treeHealthNumberUiSizeLock = null;
			_treeHealthNumberUiPosX = null;
			_treeHealthNumberUiPosY = null;
			_treeHealthNumberUiFontSize = null;
			_showTreeHealthBarUi = null;
			_treeHealthBarUiDragLock = null;
			_treeHealthBarUiSizeLock = null;
			_treeHealthBarUiPosX = null;
			_treeHealthBarUiPosY = null;
			_treeHealthBarUiWidth = null;
			_treeHealthBarUiHeight = null;
			_treeHealthBarHighHpColor = null;
			_treeHealthBarMidHpColor = null;
			_treeHealthBarLowHpColor = null;
			_treeHealthUiDurationSeconds = null;
		}
	}
	public static class KeyboardExtensions
	{
		public static bool IsKeyDown(this KeyboardShortcut shortcut)
		{
			//IL_0003: 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)
			return (int)((KeyboardShortcut)(ref shortcut)).MainKey != 0 && Input.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey) && ((KeyboardShortcut)(ref shortcut)).Modifiers.All((Func<KeyCode, bool>)Input.GetKey);
		}

		public static bool IsKeyHeld(this KeyboardShortcut shortcut)
		{
			//IL_0003: 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)
			return (int)((KeyboardShortcut)(ref shortcut)).MainKey != 0 && Input.GetKey(((KeyboardShortcut)(ref shortcut)).MainKey) && ((KeyboardShortcut)(ref shortcut)).Modifiers.All((Func<KeyCode, bool>)Input.GetKey);
		}
	}
	public static class ToggleExtensions
	{
		public static bool IsOn(this NoCapLumberPlugin.Toggle value)
		{
			return value == NoCapLumberPlugin.Toggle.On;
		}

		public static bool IsOff(this NoCapLumberPlugin.Toggle value)
		{
			return value == NoCapLumberPlugin.Toggle.Off;
		}
	}
	public static class TreeChopDamagePatch
	{
		[HarmonyPatch(typeof(TreeBase), "Damage")]
		private static class TreeBaseDamagePatch
		{
			[HarmonyPrefix]
			private static void Prefix(TreeBase __instance, HitData hit)
			{
				if ((Object)(object)__instance != (Object)null)
				{
					ApplyPlayerTreeChopMultiplier(hit, ((Component)__instance).gameObject);
				}
			}
		}

		[HarmonyPatch(typeof(TreeLog), "Damage")]
		private static class TreeLogDamagePatch
		{
			[HarmonyPrefix]
			private static void Prefix(TreeLog __instance, HitData hit)
			{
				if ((Object)(object)__instance != (Object)null)
				{
					ApplyPlayerTreeChopMultiplier(hit, ((Component)__instance).gameObject);
				}
			}
		}

		[HarmonyPatch(typeof(Destructible), "Damage")]
		private static class DestructibleDamagePatch
		{
			[HarmonyPrefix]
			private static void Prefix(Destructible __instance, HitData hit)
			{
				if (!((Object)(object)__instance == (Object)null))
				{
					GameObject gameObject = ((Component)__instance).gameObject;
					if (WoodcuttingTargetUtils.IsWoodcuttingTarget(gameObject))
					{
						ApplyPlayerTreeChopMultiplier(hit, gameObject);
					}
				}
			}
		}

		[HarmonyPatch(typeof(WearNTear), "Damage")]
		private static class WearNTearDamagePatch
		{
			[HarmonyPrefix]
			private static void Prefix(WearNTear __instance, HitData hit)
			{
				if (!((Object)(object)__instance == (Object)null))
				{
					GameObject gameObject = ((Component)__instance).gameObject;
					if (WoodcuttingTargetUtils.IsWoodcuttingTarget(gameObject))
					{
						ApplyPlayerTreeChopMultiplier(hit, gameObject);
					}
				}
			}
		}

		private static void ApplyPlayerTreeChopMultiplier(HitData hit, GameObject? target)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Invalid comparison between Unknown and I4
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Invalid comparison between Unknown and I4
			if (hit == null || ((Object)(object)target != (Object)null && !WoodcuttingTargetUtils.IsWoodcuttingTarget(target)) || hit.m_damage.m_chop <= 0f)
			{
				return;
			}
			Character attacker = hit.GetAttacker();
			if (!(attacker is Player) && (int)hit.m_hitType != 2)
			{
				return;
			}
			ItemData val = null;
			Humanoid val2 = (Humanoid)(object)((attacker is Humanoid) ? attacker : null);
			if (val2 != null)
			{
				val = val2.GetCurrentWeapon();
			}
			if (val?.m_shared != null && (int)val.m_shared.m_skillType == 7)
			{
				if ((Object)(object)target != (Object)null && NoCapLumberPlugin._blockAxeSlashOnStumpTargets.Value.IsOn() && WoodcuttingTargetUtils.IsLikelyStumpName(((Object)target).name) && hit.m_damage.m_slash > 0f)
				{
					hit.m_damage.m_slash = 0f;
				}
				if (NoCapLumberPlugin._enableNextSwingLungeSuppressionAfterTreeChop.Value.IsOn())
				{
					NextSwingLungeState.MarkPending(attacker);
				}
				float effectiveChopMultiplier = GetEffectiveChopMultiplier(val);
				if (!Mathf.Approximately(effectiveChopMultiplier, 1f))
				{
					hit.m_damage.m_chop *= effectiveChopMultiplier;
				}
			}
		}

		private static float GetEffectiveChopMultiplier(ItemData? weapon)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Invalid comparison between Unknown and I4
			//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_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Invalid comparison between Unknown and I4
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Invalid comparison between Unknown and I4
			if (weapon?.m_shared == null || (int)weapon.m_shared.m_skillType != 7)
			{
				return 1f;
			}
			AnimationState animationState = weapon.m_shared.m_animationState;
			if (1 == 0)
			{
			}
			float result = (((int)animationState == 8) ? NoCapLumberPlugin._battleAxeTreeChopDamageMultiplier.Value : (((int)animationState != 15) ? NoCapLumberPlugin._oneHandedAxeTreeChopDamageMultiplier.Value : NoCapLumberPlugin._dualAxesTreeChopDamageMultiplier.Value));
			if (1 == 0)
			{
			}
			return result;
		}
	}
	public static class TreeHealthUiPatch
	{
		[HarmonyPatch(typeof(TreeBase), "Damage")]
		private static class TreeBaseDamagePatch
		{
			[HarmonyPrefix]
			private static void Prefix(TreeBase __instance, HitData hit)
			{
				if (ShouldShow(hit) && !((Object)(object)__instance?.m_nview == (Object)null))
				{
					TreeHealthOverlay.Show(__instance.m_nview, WoodcuttingTargetUtils.ScaleMineHealth(__instance.m_health));
				}
			}
		}

		[HarmonyPatch(typeof(TreeLog), "Damage")]
		private static class TreeLogDamagePatch
		{
			[HarmonyPrefix]
			private static void Prefix(TreeLog __instance, HitData hit)
			{
				if (ShouldShow(hit) && !((Object)(object)__instance?.m_nview == (Object)null))
				{
					TreeHealthOverlay.Show(__instance.m_nview, WoodcuttingTargetUtils.ScaleMineHealth(__instance.m_health));
				}
			}
		}

		[HarmonyPatch(typeof(Destructible), "Damage")]
		private static class DestructibleDamagePatch
		{
			[HarmonyPrefix]
			private static void Prefix(Destructible __instance, HitData hit)
			{
				if (ShouldShow(hit) && !((Object)(object)__instance?.m_nview == (Object)null))
				{
					GameObject gameObject = ((Component)__instance).gameObject;
					if (WoodcuttingTargetUtils.IsWoodcuttingTarget(gameObject))
					{
						TreeHealthOverlay.Show(__instance.m_nview, WoodcuttingTargetUtils.ScaleMineHealth(__instance.m_health));
					}
				}
			}
		}

		[HarmonyPatch(typeof(WearNTear), "Damage")]
		private static class WearNTearDamagePatch
		{
			[HarmonyPrefix]
			private static void Prefix(WearNTear __instance, HitData hit)
			{
				if (ShouldShow(hit) && !((Object)(object)__instance?.m_nview == (Object)null))
				{
					GameObject gameObject = ((Component)__instance).gameObject;
					if (WoodcuttingTargetUtils.IsWoodcuttingTarget(gameObject))
					{
						TreeHealthOverlay.Show(__instance.m_nview, WoodcuttingTargetUtils.ScalePieceHealth(__instance.m_health));
					}
				}
			}
		}

		private static bool ShouldShow(HitData hit)
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Invalid comparison between Unknown and I4
			if (!IsNumberUiEnabled() && !NoCapLumberPlugin._showTreeHealthBarUi.Value.IsOn())
			{
				return false;
			}
			if (hit == null || hit.m_damage.m_chop <= 0f || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				return false;
			}
			Character attacker = hit.GetAttacker();
			if ((Object)(object)attacker == (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			if (attacker is Player)
			{
				return false;
			}
			return (int)hit.m_hitType == 2;
		}

		private static bool IsNumberUiEnabled()
		{
			return !string.Equals(NoCapLumberPlugin._treeHealthNumberUiMode.Value, "Off", StringComparison.Ordinal);
		}
	}
	internal sealed class TreeHealthOverlay : MonoBehaviour
	{
		private enum DragTarget
		{
			None,
			Bar,
			BarResize,
			Text,
			TextResize
		}

		private enum BarResizeMode
		{
			None,
			Width,
			Height
		}

		private static TreeHealthOverlay? _instance;

		private const float TextHitMinWidth = 80f;

		private const float TextHitMinHeight = 28f;

		private const float TextHitPaddingX = 22f;

		private const float TextHitPaddingY = 12f;

		private ZNetView? _target;

		private float _maxHealth = 1f;

		private float _lastKnownHealth = 1f;

		private float _hideAt;

		private Canvas? _canvas;

		private RectTransform? _barRoot;

		private RectTransform? _barFillRect;

		private Image? _barBackgroundImage;

		private Image? _barFillImage;

		private GameObject? _barDragOutlineObject;

		private RectTransform? _textRoot;

		private TextMeshProUGUI? _text;

		private GameObject? _textDragOutlineObject;

		private bool _fontBound;

		private float _appliedBarX = -1f;

		private float _appliedBarY = -1f;

		private float _appliedBarWidth = -1f;

		private float _appliedBarHeight = -1f;

		private float _appliedTextX = -1f;

		private float _appliedTextY = -1f;

		private float _appliedTextSize = -1f;

		private string _appliedBarHighColorHex = string.Empty;

		private string _appliedBarMidColorHex = string.Empty;

		private string _appliedBarLowColorHex = string.Empty;

		private Color _barHighColor = new Color(0.23f, 0.8f, 0.34f, 0.95f);

		private Color _barMidColor = new Color(0.9f, 0.76f, 0.16f, 0.95f);

		private Color _barLowColor = new Color(0.85f, 0.29f, 0.27f, 0.95f);

		private DragTarget _dragTarget = DragTarget.None;

		private Vector2 _dragPointerOffsetPixels;

		private Vector2 _textResizeCenterScreen;

		private float _textResizeStartRadius;

		private float _textResizeStartFontSize;

		private float _lastDragSaveAt;

		private string _lastMeasuredText = string.Empty;

		private float _lastMeasuredFontSize = -1f;

		private TMP_FontAsset? _lastMeasuredFont;

		private FontStyles _lastMeasuredFontStyle = (FontStyles)0;

		private BarResizeMode _barResizeMode = BarResizeMode.None;

		private Vector2 _barResizeStartLocal;

		private float _barResizeStartWidth;

		private float _barResizeStartHeight;

		private float _barResizeEdgeSign = 1f;

		private const float DragSaveIntervalSeconds = 0.2f;

		private const float BarResizeVerticalEdgeGrabPixels = 22f;

		private const float BarResizeHorizontalEdgeGrabPixels = 6f;

		private const float TextResizeCornerThresholdPixels = 16f;

		private readonly Color _dragOutlineColor = new Color(1f, 1f, 1f, 0.9f);

		private readonly Vector2 _dragOutlineDistance = new Vector2(2f, 2f);

		private readonly float _dragOutlineThickness = 2f;

		private readonly Color _backgroundColor = new Color(0f, 0f, 0f, 0.65f);

		private static TreeHealthOverlay Instance
		{
			get
			{
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0024: Expected O, but got Unknown
				if ((Object)(object)_instance != (Object)null)
				{
					return _instance;
				}
				GameObject val = new GameObject("NoCapLumber.TreeHealthOverlay");
				Object.DontDestroyOnLoad((Object)(object)val);
				_instance = val.AddComponent<TreeHealthOverlay>();
				return _instance;
			}
		}

		public static void Show(ZNetView target, float maxHealth)
		{
			if (!((Object)(object)target == (Object)null))
			{
				TreeHealthOverlay instance = Instance;
				instance.EnsureUi();
				instance.BindHudFontIfAvailable();
				instance._target = target;
				instance._maxHealth = Mathf.Max(1f, maxHealth);
				instance._lastKnownHealth = instance.ReadCurrentHealth();
				instance._hideAt = Time.time + Mathf.Max(0.5f, NoCapLumberPlugin._treeHealthUiDurationSeconds.Value);
			}
		}

		private void Update()
		{
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_canvas == (Object)null)
			{
				return;
			}
			bool flag = NoCapLumberPlugin._showTreeHealthBarUi.Value.IsOn();
			bool flag2 = !string.Equals(NoCapLumberPlugin._treeHealthNumberUiMode.Value, "Off", StringComparison.Ordinal);
			HandleDragInput(flag, flag2);
			if (((Object)(object)_target == (Object)null || Time.time > _hideAt) && _dragTarget == DragTarget.None)
			{
				SetElementsVisible(showBar: false, showNumber: false);
				UpdateDragOutlines(showBar: false, showNumber: false);
				return;
			}
			BindHudFontIfAvailable();
			ApplyLayout(force: false);
			RefreshBarColors(force: false);
			float num = (_lastKnownHealth = (((Object)(object)_target != (Object)null) ? ReadCurrentHealth() : _lastKnownHealth));
			float num2 = Mathf.Clamp01(num / _maxHealth);
			SetElementsVisible(flag, flag2);
			if (flag2 && (Object)(object)_text != (Object)null)
			{
				string healthText = GetHealthText(num, _maxHealth);
				bool flag3 = !string.Equals(((TMP_Text)_text).text, healthText, StringComparison.Ordinal);
				if (flag3)
				{
					((TMP_Text)_text).text = healthText;
				}
				RefreshTextBounds(flag3);
			}
			if (flag && (Object)(object)_barRoot != (Object)null && (Object)(object)_barFillRect != (Object)null)
			{
				RectTransform? barFillRect = _barFillRect;
				Rect rect = _barRoot.rect;
				barFillRect.SetSizeWithCurrentAnchors((Axis)0, ((Rect)(ref rect)).width * num2);
				if ((Object)(object)_barFillImage != (Object)null)
				{
					((Graphic)_barFillImage).color = GetBarFillColor(num2);
				}
			}
			UpdateDragOutlines(flag, flag2);
		}

		private float ReadCurrentHealth()
		{
			if ((Object)(object)_target == (Object)null || !_target.IsValid())
			{
				return _lastKnownHealth;
			}
			ZDO zDO = _target.GetZDO();
			if (zDO == null)
			{
				return _lastKnownHealth;
			}
			float @float = zDO.GetFloat(ZDOVars.s_health, _maxHealth);
			return Mathf.Clamp(@float, 0f, _maxHealth);
		}

		private void EnsureUi()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Expected O, but got Unknown
			//IL_00db: 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_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Expected O, but got Unknown
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: 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_01dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Expected O, but got Unknown
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0250: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_canvas != (Object)null))
			{
				GameObject val = new GameObject("NoCapLumber.TreeHealthCanvas");
				val.transform.SetParent(((Component)this).transform, false);
				_canvas = val.AddComponent<Canvas>();
				_canvas.renderMode = (RenderMode)0;
				_canvas.sortingOrder = 1200;
				CanvasScaler val2 = val.AddComponent<CanvasScaler>();
				val2.uiScaleMode = (ScaleMode)1;
				val2.referenceResolution = new Vector2(1920f, 1080f);
				val2.screenMatchMode = (ScreenMatchMode)0;
				val2.matchWidthOrHeight = 0.5f;
				val.AddComponent<GraphicRaycaster>();
				GameObject val3 = new GameObject("BarRoot");
				val3.transform.SetParent(val.transform, false);
				_barRoot = val3.AddComponent<RectTransform>();
				_barRoot.pivot = new Vector2(0.5f, 0.5f);
				_barRoot.anchoredPosition = Vector2.zero;
				_barBackgroundImage = val3.AddComponent<Image>();
				((Graphic)_barBackgroundImage).color = _backgroundColor;
				_barDragOutlineObject = CreateDragOutline(val3.transform, "BarDragOutline");
				GameObject val4 = new GameObject("BarFill");
				val4.transform.SetParent(val3.transform, false);
				_barFillRect = val4.AddComponent<RectTransform>();
				_barFillRect.anchorMin = new Vector2(0f, 0f);
				_barFillRect.anchorMax = new Vector2(0f, 1f);
				_barFillRect.pivot = new Vector2(0f, 0.5f);
				_barFillRect.anchoredPosition = Vector2.zero;
				_barFillRect.sizeDelta = Vector2.zero;
				_barFillImage = val4.AddComponent<Image>();
				((Graphic)_barFillImage).color = _barHighColor;
				GameObject val5 = new GameObject("TextRoot");
				val5.transform.SetParent(val.transform, false);
				_textRoot = val5.AddComponent<RectTransform>();
				_textRoot.pivot = new Vector2(0.5f, 0.5f);
				_textRoot.sizeDelta = new Vector2(80f, 28f);
				_textRoot.anchoredPosition = Vector2.zero;
				_text = val5.AddComponent<TextMeshProUGUI>();
				((TMP_Text)_text).text = string.Empty;
				((TMP_Text)_text).alignment = (TextAlignmentOptions)514;
				((TMP_Text)_text).fontSize = 22f;
				((Graphic)_text).color = Color.white;
				((TMP_Text)_text).textWrappingMode = (TextWrappingModes)0;
				_textDragOutlineObject = CreateRectBorder(val5.transform, "TextDragOutline", _dragOutlineColor, _dragOutlineThickness);
				RefreshBarColors(force: true);
				ApplyLayout(force: true);
				BindHudFontIfAvailable();
				RefreshTextBounds(force: true);
				SetElementsVisible(showBar: false, showNumber: false);
			}
		}

		private void BindHudFontIfAvailable()
		{
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_text == (Object)null) && !_fontBound && !((Object)(object)Hud.instance == (Object)null))
			{
				TMP_Text val = (((Object)(object)Hud.instance.m_healthText != (Object)null) ? Hud.instance.m_healthText : Hud.instance.m_staminaText);
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val.font == (Object)null))
				{
					((TMP_Text)_text).font = val.font;
					((TMP_Text)_text).fontSharedMaterial = val.fontSharedMaterial;
					((TMP_Text)_text).fontStyle = val.fontStyle;
					((Graphic)_text).color = ((Graphic)val).color;
					((TMP_Text)_text).alignment = (TextAlignmentOptions)514;
					_fontBound = true;
					ResetTextMeasureCache();
				}
			}
		}

		private GameObject CreateDragOutline(Transform parent, string objectName)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected O, but got Unknown
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: 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_0055: 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)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(objectName);
			val.transform.SetParent(parent, false);
			RectTransform val2 = val.AddComponent<RectTransform>();
			val2.anchorMin = Vector2.zero;
			val2.anchorMax = Vector2.one;
			val2.sizeDelta = Vector2.zero;
			val2.anchoredPosition = Vector2.zero;
			Image val3 = val.AddComponent<Image>();
			((Graphic)val3).color = Color.clear;
			((Graphic)val3).raycastTarget = false;
			Outline val4 = val.AddComponent<Outline>();
			((Shadow)val4).effectColor = _dragOutlineColor;
			((Shadow)val4).effectDistance = _dragOutlineDistance;
			((Shadow)val4).useGraphicAlpha = false;
			val.SetActive(false);
			return val;
		}

		private GameObject CreateRectBorder(Transform parent, string objectName, Color color, float thickness)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected O, but got Unknown
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: 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_0058: 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_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: 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)
			//IL_00ee: 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_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(objectName);
			val.transform.SetParent(parent, false);
			RectTransform val2 = val.AddComponent<RectTransform>();
			val2.anchorMin = Vector2.zero;
			val2.anchorMax = Vector2.one;
			val2.offsetMin = Vector2.zero;
			val2.offsetMax = Vector2.zero;
			CreateBorderEdge(val.transform, "Top", color, new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, thickness));
			CreateBorderEdge(val.transform, "Bottom", color, new Vector2(0f, 0f), new Vector2(1f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, thickness));
			CreateBorderEdge(val.transform, "Left", color, new Vector2(0f, 0f), new Vector2(0f, 1f), new Vector2(0f, 0.5f), new Vector2(thickness, 0f));
			CreateBorderEdge(val.transform, "Right", color, new Vector2(1f, 0f), new Vector2(1f, 1f), new Vector2(1f, 0.5f), new Vector2(thickness, 0f));
			val.SetActive(false);
			return val;
		}

		private static void CreateBorderEdge(Transform parent, string name, Color color, Vector2 anchorMin, Vector2 anchorMax, Vector2 pivot, Vector2 sizeDelta)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected O, but got Unknown
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: 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_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			val.transform.SetParent(parent, false);
			RectTransform val2 = val.AddComponent<RectTransform>();
			val2.anchorMin = anchorMin;
			val2.anchorMax = anchorMax;
			val2.pivot = pivot;
			val2.anchoredPosition = Vector2.zero;
			val2.sizeDelta = sizeDelta;
			Image val3 = val.AddComponent<Image>();
			((Graphic)val3).color = color;
			((Graphic)val3).raycastTarget = false;
		}

		private void RefreshTextBounds(bool force)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: 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_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: 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_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_textRoot == (Object)null || (Object)(object)_text == (Object)null)
			{
				return;
			}
			if (!EnsureTextFontAssigned())
			{
				if (force)
				{
					_textRoot.sizeDelta = new Vector2(80f, 28f);
				}
				return;
			}
			string text = (string.IsNullOrEmpty(((TMP_Text)_text).text) ? "Tree HP 000/000" : ((TMP_Text)_text).text);
			if (!force && string.Equals(text, _lastMeasuredText, StringComparison.Ordinal) && Mathf.Approximately(((TMP_Text)_text).fontSize, _lastMeasuredFontSize) && (Object)(object)((TMP_Text)_text).font == (Object)(object)_lastMeasuredFont && ((TMP_Text)_text).fontStyle == _lastMeasuredFontStyle)
			{
				return;
			}
			Vector2 preferredValues = ((TMP_Text)_text).GetPreferredValues(text);
			float num = Mathf.Clamp(preferredValues.x + 22f, 80f, 1400f);
			float num2 = Mathf.Clamp(preferredValues.y + 12f, 28f, 260f);
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(num, num2);
			_lastMeasuredText = text;
			_lastMeasuredFontSize = ((TMP_Text)_text).fontSize;
			_lastMeasuredFont = ((TMP_Text)_text).font;
			_lastMeasuredFontStyle = ((TMP_Text)_text).fontStyle;
			if (!force)
			{
				Vector2 val2 = _textRoot.sizeDelta - val;
				if (((Vector2)(ref val2)).sqrMagnitude < 0.01f)
				{
					return;
				}
			}
			_textRoot.sizeDelta = val;
		}

		private bool EnsureTextFontAssigned()
		{
			if ((Object)(object)_text == (Object)null)
			{
				return false;
			}
			if ((Object)(object)((TMP_Text)_text).font != (Object)null)
			{
				return true;
			}
			TMP_FontAsset val = null;
			TMP_Text val2 = null;
			if ((Object)(object)Hud.instance != (Object)null)
			{
				val2 = (((Object)(object)Hud.instance.m_healthText != (Object)null) ? Hud.instance.m_healthText : Hud.instance.m_staminaText);
				if ((Object)(object)val2 != (Object)null && (Object)(object)val2.font != (Object)null)
				{
					val = val2.font;
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				val = TMP_Settings.defaultFontAsset;
			}
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			((TMP_Text)_text).font = val;
			if ((Object)(object)val2 != (Object)null && (Object)(object)val2.fontSharedMaterial != (Object)null)
			{
				((TMP_Text)_text).fontSharedMaterial = val2.fontSharedMaterial;
			}
			else if ((Object)(object)((TMP_Text)_text).fontSharedMaterial == (Object)null && (Object)(object)((TMP_Asset)val).material != (Object)null)
			{
				((TMP_Text)_text).fontSharedMaterial = ((TMP_Asset)val).material;
			}
			ResetTextMeasureCache();
			return true;
		}

		private void ResetTextMeasureCache()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			_lastMeasuredText = string.Empty;
			_lastMeasuredFontSize = -1f;
			_lastMeasuredFont = null;
			_lastMeasuredFontStyle = (FontStyles)0;
		}

		private void UpdateDragOutlines(bool showBar, bool showNumber)
		{
			//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_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_barRoot == (Object)null) && !((Object)(object)_textRoot == (Object)null))
			{
				bool flag = IsBarDragLocked();
				bool flag2 = IsBarSizeLocked();
				bool flag3 = IsNumberDragLocked();
				bool flag4 = IsNumberSizeLocked();
				bool flag5 = !flag || !flag2;
				bool flag6 = !flag3 || !flag4;
				bool visible = Cursor.visible;
				Vector2 val = Vector2.op_Implicit(Input.mousePosition);
				bool flag7 = visible && showBar && flag5 && RectTransformUtility.RectangleContainsScreenPoint(_barRoot, val, (Camera)null);
				bool flag8 = visible && showNumber && flag6 && RectTransformUtility.RectangleContainsScreenPoint(_textRoot, val, (Camera)null);
				bool flag9 = showBar && (_dragTarget == DragTarget.Bar || _dragTarget == DragTarget.BarResize || (_dragTarget == DragTarget.None && flag7));
				bool flag10 = showNumber && (_dragTarget == DragTarget.Text || _dragTarget == DragTarget.TextResize || (_dragTarget == DragTarget.None && flag8));
				if ((Object)(object)_barDragOutlineObject != (Object)null && _barDragOutlineObject.activeSelf != flag9)
				{
					_barDragOutlineObject.SetActive(flag9);
				}
				if ((Object)(object)_textDragOutlineObject != (Object)null && _textDragOutlineObject.activeSelf != flag10)
				{
					_textDragOutlineObject.SetActive(flag10);
				}
			}
		}

		private void RefreshBarColors(bool force)
		{
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: 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)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			string text = NormalizeColorConfig(NoCapLumberPlugin._treeHealthBarHighHpColor.Value);
			string text2 = NormalizeColorConfig(NoCapLumberPlugin._treeHealthBarMidHpColor.Value);
			string text3 = NormalizeColorConfig(NoCapLumberPlugin._treeHealthBarLowHpColor.Value);
			if (force || !string.Equals(text, _appliedBarHighColorHex, StringComparison.OrdinalIgnoreCase) || !string.Equals(text2, _appliedBarMidColorHex, StringComparison.OrdinalIgnoreCase) || !string.Equals(text3, _appliedBarLowColorHex, StringComparison.OrdinalIgnoreCase))
			{
				_appliedBarHighColorHex = text;
				_appliedBarMidColorHex = text2;
				_appliedBarLowColorHex = text3;
				_barHighColor = ParseHtmlColor(text, new Color(0.23f, 0.8f, 0.34f, 0.95f));
				_barMidColor = ParseHtmlColor(text2, new Color(0.9f, 0.76f, 0.16f, 0.95f));
				_barLowColor = ParseHtmlColor(text3, new Color(0.85f, 0.29f, 0.27f, 0.95f));
			}
		}

		private static string NormalizeColorConfig(string raw)
		{
			return string.IsNullOrWhiteSpace(raw) ? string.Empty : raw.Trim();
		}

		private static Color ParseHtmlColor(string raw, Color fallback)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: 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_003f: 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)
			if (string.IsNullOrWhiteSpace(raw))
			{
				return fallback;
			}
			string text = raw;
			if (!text.StartsWith("#", StringComparison.Ordinal))
			{
				text = "#" + text;
			}
			Color val = default(Color);
			return ColorUtility.TryParseHtmlString(text, ref val) ? val : fallback;
		}

		private static string GetHealthText(float currentHealth, float maxHealth)
		{
			string value = NoCapLumberPlugin._treeHealthNumberUiMode.Value;
			string text = value;
			if (1 == 0)
			{
			}
			string result;
			if (string.Equals(text, "Tree Current/Max", StringComparison.Ordinal))
			{
				result = $"Tree {currentHealth:0}/{maxHealth:0}";
			}
			else
			{
				string a = text;
				result = ((!string.Equals(a, "Current/Max", StringComparison.Ordinal)) ? $"Tree HP {currentHealth:0}/{maxHealth:0}" : $"{currentHealth:0}/{maxHealth:0}");
			}
			if (1 == 0)
			{
			}
			return result;
		}

		private Color GetBarFillColor(float ratio)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			if (ratio < 1f / 3f)
			{
				return _barLowColor;
			}
			if (ratio < 2f / 3f)
			{
				return _barMidColor;
			}
			return _barHighColor;
		}

		private void HandleDragInput(bool showBar, bool showNumber)
		{
			//IL_00b5: 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_00bf: 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)
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_02af: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_barRoot == (Object)null || (Object)(object)_textRoot == (Object)null)
			{
				return;
			}
			bool flag = IsBarDragLocked();
			bool flag2 = IsBarSizeLocked();
			bool flag3 = IsNumberDragLocked();
			bool flag4 = IsNumberSizeLocked();
			if (!Cursor.visible)
			{
				if (_dragTarget != 0)
				{
					_dragTarget = DragTarget.None;
					_barResizeMode = BarResizeMode.None;
					NoCapLumberPlugin.SaveConfigToDisk();
				}
				return;
			}
			if (_dragTarget != 0 && IsDragOperationLocked(_dragTarget, flag, flag2, flag3, flag4))
			{
				_dragTarget = DragTarget.None;
				_barResizeMode = BarResizeMode.None;
				NoCapLumberPlugin.SaveConfigToDisk();
				return;
			}
			Vector2 val = Vector2.op_Implicit(Input.mousePosition);
			if (Input.GetMouseButtonDown(0))
			{
				if (showNumber && !flag4 && IsOverTextResizeHandle(val))
				{
					BeginTextResize(val);
					return;
				}
				if (showNumber && !flag3 && RectTransformUtility.RectangleContainsScreenPoint(_textRoot, val, (Camera)null))
				{
					BeginDrag(DragTarget.Text, val, NoCapLumberPlugin._treeHealthNumberUiPosX.Value, NoCapLumberPlugin._treeHealthNumberUiPosY.Value);
					return;
				}
				if (showBar && !flag2 && IsOverBarResizeHandle(val))
				{
					BeginBarResize(val);
					return;
				}
				if (showBar && !flag && RectTransformUtility.RectangleContainsScreenPoint(_barRoot, val, (Camera)null))
				{
					BeginDrag(DragTarget.Bar, val, NoCapLumberPlugin._treeHealthBarUiPosX.Value, NoCapLumberPlugin._treeHealthBarUiPosY.Value);
					return;
				}
			}
			if (_dragTarget == DragTarget.None)
			{
				return;
			}
			if (!Input.GetMouseButton(0))
			{
				_dragTarget = DragTarget.None;
				_barResizeMode = BarResizeMode.None;
				NoCapLumberPlugin.SaveConfigToDisk();
				return;
			}
			if (_dragTarget == DragTarget.BarResize)
			{
				if (UpdateBarResize(val) && Time.unscaledTime - _lastDragSaveAt >= 0.2f)
				{
					_lastDragSaveAt = Time.unscaledTime;
					NoCapLumberPlugin.SaveConfigToDisk();
				}
				return;
			}
			if (_dragTarget == DragTarget.TextResize)
			{
				if (UpdateTextResize(val) && Time.unscaledTime - _lastDragSaveAt >= 0.2f)
				{
					_lastDragSaveAt = Time.unscaledTime;
					NoCapLumberPlugin.SaveConfigToDisk();
				}
				return;
			}
			float num = Mathf.Max(1f, (float)Screen.width);
			float num2 = Mathf.Max(1f, (float)Screen.height);
			Vector2 val2 = val + _dragPointerOffsetPixels;
			float value = Quantize01(val2.x / num);
			float value2 = Quantize01(val2.y / num2);
			DragTarget dragTarget = _dragTarget;
			if (1 == 0)
			{
			}
			bool flag5 = dragTarget switch
			{
				DragTarget.Bar => SetConfigFloat(NoCapLumberPlugin._treeHealthBarUiPosX, value) | SetConfigFloat(NoCapLumberPlugin._treeHealthBarUiPosY, value2), 
				DragTarget.Text => SetConfigFloat(NoCapLumberPlugin._treeHealthNumberUiPosX, value) | SetConfigFloat(NoCapLumberPlugin._treeHealthNumberUiPosY, value2), 
				_ => false, 
			};
			if (1 == 0)
			{
			}
			if (flag5 && Time.unscaledTime - _lastDragSaveAt >= 0.2f)
			{
				_lastDragSaveAt = Time.unscaledTime;
				NoCapLumberPlugin.SaveConfigToDisk();
			}
		}

		private static bool IsNumberDragLocked()
		{
			return NoCapLumberPlugin._treeHealthNumberUiDragLock.Value.IsOn();
		}

		private static bool IsNumberSizeLocked()
		{
			return NoCapLumberPlugin._treeHealthNumberUiSizeLock.Value.IsOn();
		}

		private static bool IsBarDragLocked()
		{
			return NoCapLumberPlugin._treeHealthBarUiDragLock.Value.IsOn();
		}

		private static bool IsBarSizeLocked()
		{
			return NoCapLumberPlugin._treeHealthBarUiSizeLock.Value.IsOn();
		}

		private static bool IsDragOperationLocked(DragTarget dragTarget, bool barDragLocked, bool barSizeLocked, bool textDragLocked, bool textSizeLocked)
		{
			if (1 == 0)
			{
			}
			bool result = dragTarget switch
			{
				DragTarget.Bar => barDragLocked, 
				DragTarget.BarResize => barSizeLocked, 
				DragTarget.Text => textDragLocked, 
				DragTarget.TextResize => textSizeLocked, 
				_ => false, 
			};
			if (1 == 0)
			{
			}
			return result;
		}

		private bool IsOverTextResizeHandle(Vector2 mousePosition)
		{
			//IL_0022: 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_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_textRoot == (Object)null)
			{
				return false;
			}
			if (!RectTransformUtility.RectangleContainsScreenPoint(_textRoot, mousePosition, (Camera)null))
			{
				return false;
			}
			Vector2 val = default(Vector2);
			if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(_textRoot, mousePosition, (Camera)null, ref val))
			{
				return false;
			}
			Rect rect = _textRoot.rect;
			float num = ((Rect)(ref rect)).width * 0.5f;
			float num2 = ((Rect)(ref rect)).height * 0.5f;
			float num3 = Mathf.Min(16f, num);
			float num4 = Mathf.Min(16f, num2);
			bool flag = Mathf.Abs(val.x) >= num - num3;
			bool flag2 = Mathf.Abs(val.y) >= num2 - num4;
			return flag && flag2;
		}

		private bool IsOverBarResizeHandle(Vector2 mousePosition)
		{
			//IL_0029: 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_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			_barResizeMode = BarResizeMode.None;
			if ((Object)(object)_barRoot == (Object)null)
			{
				return false;
			}
			Vector2 val = default(Vector2);
			if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(_barRoot, mousePosition, (Camera)null, ref val))
			{
				return false;
			}
			Rect rect = _barRoot.rect;
			float num = ((Rect)(ref rect)).width * 0.5f;
			float num2 = ((Rect)(ref rect)).height * 0.5f;
			float num3 = 22f;
			float num4 = 6f;
			if (Mathf.Abs(val.x) > num + num3 || Mathf.Abs(val.y) > num2 + num4)
			{
				return false;
			}
			float num5 = Mathf.Abs(Mathf.Abs(val.x) - num);
			float num6 = Mathf.Abs(Mathf.Abs(val.y) - num2);
			bool flag = num5 <= num3;
			bool flag2 = num6 <= num4;
			if (flag == flag2)
			{
				return false;
			}
			_barResizeMode = (flag ? BarResizeMode.Width : BarResizeMode.Height);
			return true;
		}

		private void BeginBarResize(Vector2 mousePosition)
		{
			//IL_0030: 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)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			Vector2 val = default(Vector2);
			if (!((Object)(object)_barRoot == (Object)null) && _barResizeMode != 0 && RectTransformUtility.ScreenPointToLocalPointInRectangle(_barRoot, mousePosition, (Camera)null, ref val))
			{
				_barResizeStartLocal = val;
				_barResizeStartWidth = Mathf.Clamp(NoCapLumberPlugin._treeHealthBarUiWidth.Value, 50f, 500f);
				_barResizeStartHeight = Mathf.Clamp(NoCapLumberPlugin._treeHealthBarUiHeight.Value, 5f, 50f);
				_barResizeEdgeSign = ((_barResizeMode != BarResizeMode.Width) ? ((val.y >= 0f) ? 1f : (-1f)) : ((val.x >= 0f) ? 1f : (-1f)));
				_dragTarget = DragTarget.BarResize;
			}
		}

		private bool UpdateBarResize(Vector2 mousePosition)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_barRoot == (Object)null)
			{
				return false;
			}
			Vector2 val = default(Vector2);
			if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(_barRoot, mousePosition, (Camera)null, ref val))
			{
				return false;
			}
			switch (_barResizeMode)
			{
			case BarResizeMode.Width:
			{
				float num3 = (val.x - _barResizeStartLocal.x) * _barResizeEdgeSign * 2f;
				float num4 = Mathf.Clamp(_barResizeStartWidth + num3, 50f, 500f);
				num4 = Mathf.Round(num4 * 10f) / 10f;
				return SetConfigFloatRange(NoCapLumberPlugin._treeHealthBarUiWidth, num4, 50f, 500f);
			}
			case BarResizeMode.Height:
			{
				float num = (val.y - _barResizeStartLocal.y) * _barResizeEdgeSign * 2f;
				float num2 = Mathf.Clamp(_barResizeStartHeight + num, 5f, 50f);
				num2 = Mathf.Round(num2 * 10f) / 10f;
				return SetConfigFloatRange(NoCapLumberPlugin._treeHealthBarUiHeight, num2, 5f, 50f);
			}
			default:
				return false;
			}
		}

		private void BeginTextResize(Vector2 mousePosition)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: 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_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_textRoot == (Object)null))
			{
				_dragTarget = DragTarget.TextResize;
				_textResizeCenterScreen = RectTransformUtility.WorldToScreenPoint((Camera)null, ((Transform)_textRoot).position);
				_textResizeStartRadius = Mathf.Max(20f, Vector2.Distance(mousePosition, _textResizeCenterScreen));
				_textResizeStartFontSize = Mathf.Clamp(NoCapLumberPlugin._treeHealthNumberUiFontSize.Value, 10f, 50f);
			}
		}

		private bool UpdateTextResize(Vector2 mousePosition)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			float num = Mathf.Max(1f, Vector2.Distance(mousePosition, _textResizeCenterScreen));
			float num2 = num / _textResizeStartRadius;
			float num3 = Mathf.Clamp(_textResizeStartFontSize * num2, 10f, 50f);
			num3 = Mathf.Round(num3 * 10f) / 10f;
			return SetConfigFloatRange(NoCapLumberPlugin._treeHealthNumberUiFontSize, num3, 10f, 50f);
		}

		private void BeginDrag(DragTarget target, Vector2 mousePosition, float currentX, float currentY)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: 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)
			_dragTarget = target;
			float num = Mathf.Max(1f, (float)Screen.width);
			float num2 = Mathf.Max(1f, (float)Screen.height);
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(Mathf.Clamp01(currentX) * num, Mathf.Clamp01(currentY) * num2);
			_dragPointerOffsetPixels = val - mousePosition;
		}

		private static bool SetConfigFloat(ConfigEntry<float> configEntry, float value)
		{
			float num = Mathf.Clamp01(value);
			if (Mathf.Abs(configEntry.Value - num) <= 0.0001f)
			{
				return false;
			}
			configEntry.Value = num;
			return true;
		}

		private static bool SetConfigFloatRange(ConfigEntry<float> configEntry, float value, float min, float max)
		{
			float num = Mathf.Clamp(value, min, max);
			if (Mathf.Abs(configEntry.Value - num) <= 0.0001f)
			{
				return false;
			}
			configEntry.Value = num;
			return true;
		}

		private static float Quantize01(float value)
		{
			float num = Mathf.Clamp01(value);
			return Mathf.Round(num * 10000f) / 10000f;
		}

		private void ApplyLayout(bool force)
		{
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_barRoot == (Object)null) && !((Object)(object)_textRoot == (Object)null) && !((Object)(object)_text == (Object)null))
			{
				float num = Mathf.Clamp01(NoCapLumberPlugin._treeHealthBarUiPosX.Value);
				float num2 = Mathf.Clamp01(NoCapLumberPlugin._treeHealthBarUiPosY.Value);
				float num3 = Mathf.Clamp(NoCapLumberPlugin._treeHealthBarUiWidth.Value, 50f, 500f);
				float num4 = Mathf.Clamp(NoCapLumberPlugin._treeHealthBarUiHeight.Value, 5f, 50f);
				float num5 = Mathf.Clamp01(NoCapLumberPlugin._treeHealthNumberUiPosX.Value);
				float num6 = Mathf.Clamp01(NoCapLumberPlugin._treeHealthNumberUiPosY.Value);
				float num7 = Mathf.Clamp(NoCapLumberPlugin._treeHealthNumberUiFontSize.Value, 10f, 50f);
				if (force || !Mathf.Approximately(num, _appliedBarX) || !Mathf.Approximately(num2, _appliedBarY) || !Mathf.Approximately(num3, _appliedBarWidth) || !Mathf.Approximately(num4, _appliedBarHeight) || !Mathf.Approximately(num5, _appliedTextX) || !Mathf.Approximately(num6, _appliedTextY) || !Mathf.Approximately(num7, _appliedTextSize))
				{
					_appliedBarX = num;
					_appliedBarY = num2;
					_appliedBarWidth = num3;
					_appliedBarHeight = num4;
					_appliedTextX = num5;
					_appliedTextY = num6;
					_appliedTextSize = num7;
					_barRoot.anchorMin = new Vector2(num, num2);
					_barRoot.anchorMax = new Vector2(num, num2);
					_barRoot.sizeDelta = new Vector2(num3, num4);
					_barRoot.anchoredPosition = Vector2.zero;
					_textRoot.anchorMin = new Vector2(num5, num6);
					_textRoot.anchorMax = new Vector2(num5, num6);
					_textRoot.anchoredPosition = Vector2.zero;
					((TMP_Text)_text).fontSize = num7;
				}
			}
		}

		private void SetElementsVisible(bool showBar, bool showNumber)
		{
			if ((Object)(object)_barRoot != (Object)null && ((Component)_barRoot).gameObject.activeSelf != showBar)
			{
				((Component)_barRoot).gameObject.SetActive(showBar);
			}
			if ((Object)(object)_textRoot != (Object)null && ((Component)_textRoot).gameObject.activeSelf != showNumber)
			{
				((Component)_textRoot).gameObject.SetActive(showNumber);
			}
		}
	}
	[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
	public static class RegisterAndCheckVersion
	{
		private static void Prefix(ZNetPeer peer, ref ZNet __instance)
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Expected O, but got Unknown
			if (peer?.m_rpc != null)
			{
				if (__instance.IsServer())
				{
					RpcHandlers.TrackPendingPeer(peer.m_rpc);
				}
				NoCapLumberPlugin.NoCapLumberLogger.LogDebug((object)"Registering version RPC handler");
				peer.m_rpc.Register<ZPackage>("NoCapLumber_VersionCheck", (Action<ZRpc, ZPackage>)RpcHandlers.RPC_NoCapLumber_Version);
				NoCapLumberPlugin.NoCapLumberLogger.LogInfo((object)"Invoking version check");
				ZPackage val = new ZPackage();
				val.Write("1.0.0");
				peer.m_rpc.Invoke("NoCapLumber_VersionCheck", new object[1] { val });
			}
		}
	}
	[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
	public static class VerifyClient
	{
		private static bool Prefix(ZRpc rpc, ZPackage pkg, ref ZNet __instance)
		{
			if (__instance.IsServer())
			{
				RpcHandlers.TrackPendingPeer(rpc);
			}
			return true;
		}

		private static void Postfix(ZNet __instance)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			if (!__instance.IsServer() && ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "NoCapLumberRequestAdminSync", new object[1] { (object)new ZPackage() });
			}
		}
	}
	[HarmonyPatch(typeof(ZNet), "Update")]
	public static class DisconnectUnvalidatedPeers
	{
		private static void Postfix(ZNet __instance)
		{
			RpcHandlers.DisconnectTimedOutPeers(__instance);
		}
	}
	[HarmonyPatch(typeof(FejdStartup), "ShowConnectError")]
	public class ShowConnectionError
	{
		private static void Postfix(FejdStartup __instance)
		{
			if (__instance.m_connectionFailedPanel.activeSelf)
			{
				__instance.m_connectionFailedError.fontSizeMax = 25f;
				__instance.m_connectionFailedError.fontSizeMin = 15f;
				TMP_Text connectionFailedError = __instance.m_connectionFailedError;
				connectionFailedError.text = connectionFailedError.text + "\n" + NoCapLumberPlugin.ConnectionError;
			}
		}
	}
	[HarmonyPatch(typeof(ZNet), "Disconnect")]
	public static class RemoveDisconnectedPeerFromVerified
	{
		private static void Prefix(ZNetPeer peer, ref ZNet __instance)
		{
			if (__instance.IsServer() && peer?.m_rpc != null)
			{
				NoCapLumberPlugin.NoCapLumberLogger.LogInfo((object)("Peer (" + RpcHandlers.GetPeerName(peer.m_rpc) + ") disconnected, removing from validation state"));
				RpcHandlers.RemovePeer(peer.m_rpc);
			}
		}
	}
	public static class RpcHandlers
	{
		private static readonly HashSet<ZRpc> ValidatedPeers = new HashSet<ZRpc>();

		private static readonly Dictionary<ZRpc, float> PendingValidation = new Dictionary<ZRpc, float>();

		private static readonly object PeerStateLock = new object();

		private const float ValidationTimeoutSeconds = 6f;

		public static string GetPeerName(ZRpc rpc)
		{
			try
			{
				object obj;
				if (rpc == null)
				{
					obj = null;
				}
				else
				{
					ISocket socket = rpc.m_socket;
					obj = ((socket != null) ? socket.GetHostName() : null);
				}
				if (obj == null)
				{
					obj = "Unknown";
				}
				return (string)obj;
			}
			catch
			{
				return "Unknown";
			}
		}

		public static void TrackPendingPeer(ZRpc rpc)
		{
			if (rpc == null)
			{
				return;
			}
			lock (PeerStateLock)
			{
				if (!ValidatedPeers.Contains(rpc))
				{
					PendingValidation[rpc] = Time.unscaledTime + 6f;
				}
			}
		}

		public static void RemovePeer(ZRpc rpc)
		{
			if (rpc == null)
			{
				return;
			}
			lock (PeerStateLock)
			{
				PendingValidation.Remove(rpc);
				ValidatedPeers.Remove(rpc);
			}
		}

		public static void DisconnectTimedOutPeers(ZNet znet)
		{
			if ((Object)(object)znet == (Object)null || !znet.IsServer())
			{
				return;
			}
			List<ZRpc> list = null;
			float unscaledTime = Time.unscaledTime;
			lock (PeerStateLock)
			{
				foreach (KeyValuePair<ZRpc, float> item in PendingValidation)
				{
					if (!(unscaledTime < item.Value))
					{
						if (list == null)
						{
							list = new List<ZRpc>();
						}
						list.Add(item.Key);
					}
				}
				if (list != null)
				{
					foreach (ZRpc item2 in list)
					{
						PendingValidation.Remove(item2);
					}
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (ZRpc item3 in list)
			{
				NoCapLumberPlugin.NoCapLumberLogger.LogWarning((object)$"Peer ({GetPeerName(item3)}) failed version handshake within {6f:0.0}s, disconnecting");
				item3.Invoke("Error", new object[1] { 3 });
			}
		}

		private static void MarkPeerValidated(ZRpc rpc)
		{
			if (rpc == null)
			{
				return;
			}
			lock (PeerStateLock)
			{
				PendingValidation.Remove(rpc);
				ValidatedPeers.Add(rpc);
			}
		}

		public static void RPC_NoCapLumber_Version(ZRpc rpc, ZPackage pkg)
		{
			if (rpc == null || pkg == null)
			{
				return;
			}
			string text;
			try
			{
				text = pkg.ReadString();
			}
			catch (Exception ex)
			{
				NoCapLumberPlugin.NoCapLumberLogger.LogWarning((object)("Version payload from peer (" + GetPeerName(rpc) + ") was invalid: " + ex.Message));
				RemovePeer(rpc);
				if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
				{
					rpc.Invoke("Error", new object[1] { 3 });
				}
				return;
			}
			NoCapLumberPlugin.NoCapLumberLogger.LogInfo((object)("Version check, local: 1.0.0, remote: " + text));
			bool flag = (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer();
			if (text != "1.0.0")
			{
				NoCapLumberPlugin.ConnectionError = "NoCapLumber Installed: 1.0.0\n Needed: " + text;
				if (flag)
				{
					RemovePeer(rpc);
					NoCapLumberPlugin.NoCapLumberLogger.LogWarning((object)("Peer (" + GetPeerName(rpc) + ") has incompatible version, disconnecting..."));
					rpc.Invoke("Error", new object[1] { 3 });
				}
			}
			else if (!flag)
			{
				NoCapLumberPlugin.NoCapLumberLogger.LogInfo((object)"Received same version from server!");
			}
			else
			{
				NoCapLumberPlugin.NoCapLumberLogger.LogInfo((object)("Adding peer (" + GetPeerName(rpc) + ") to validated list"));
				MarkPeerValidated(rpc);
			}
		}
	}
	internal static class WoodcuttingTargetUtils
	{
		private sealed class TargetClassification
		{
			public bool Initialized;

			public string CachedName = string.Empty;

			public bool IsWoodcuttingTarget;
		}

		private static readonly string[] StumpNameTokens = new string[3] { "stub", "stump", "stomp" };

		private static readonly ConditionalWeakTable<GameObject, TargetClassification> TargetCache = new ConditionalWeakTable<GameObject, TargetClassification>();

		public static bool IsWoodcuttingTarget(GameObject target)
		{
			if ((Object)(object)target == (Object)null)
			{
				return false;
			}
			TargetClassification orCreateValue = TargetCache.GetOrCreateValue(target);
			string text = ((Object)target).name ?? string.Empty;
			if (!orCreateValue.Initialized || !string.Equals(orCreateValue.CachedName, text, StringComparison.Ordinal))
			{
				orCreateValue.CachedName = text;
				orCreateValue.IsWoodcuttingTarget = ComputeIsWoodcuttingTarget(target, text);
				orCreateValue.Initialized = true;
			}
			return orCreateValue.IsWoodcuttingTarget;
		}

		private static bool ComputeIsWoodcuttingTarget(GameObject target, string targetName)
		{
			//IL_0032: 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_003a: Invalid comparison between Unknown and I4
			if ((Object)(object)target.GetComponent<TreeBase>() != (Object)null || (Object)(object)target.GetComponent<TreeLog>() != (Object)null)
			{
				return true;
			}
			IDestructible component = target.GetComponent<IDestructible>();
			if (component != null && (component.GetDestructibleType() & 2) > 0)
			{
				return true;
			}
			return IsLikelyStumpDestructible(target, targetName);
		}

		public static bool IsLikelyStumpName(string name)
		{
			if (string.IsNullOrWhiteSpace(name))
			{
				return false;
			}
			string text = NormalizeName(name);
			string[] stumpNameTokens = StumpNameTokens;
			foreach (string value in stumpNameTokens)
			{
				if (text.IndexOf(value, StringComparison.Ordinal) >= 0)
				{
					return true;
				}
			}
			return false;
		}

		private static bool IsLikelyStumpDestructible(GameObject target, string targetName)
		{
			if ((Object)(object)target.GetComponent<Destructible>() == (Object)null)
			{
				return false;
			}
			if ((Object)(object)target.GetComponent<StaticPhysics>() == (Object)null)
			{
				return false;
			}
			return IsLikelyStumpName(targetName);
		}

		public static float ScaleMineHealth(float baseHealth)
		{
			if ((Object)(object)Game.instance == (Object)null)
			{
				return baseHealth;
			}
			return baseHealth + (float)Game.m_worldLevel * baseHealth * Game.instance.m_worldLevelMineHPMultiplier;
		}

		public static float ScalePieceHealth(float baseHealth)
		{
			if ((Object)(object)Game.instance == (Object)null)
			{
				return baseHealth;
			}
			return baseHealth + (float)Game.m_worldLevel * baseHealth * Game.instance.m_worldLevelPieceHPMultiplier;
		}

		private static string NormalizeName(string name)
		{
			string text = name.Trim();
			int num = text.IndexOf("(Clone)", StringComparison.OrdinalIgnoreCase);
			if (num >= 0)
			{
				text = text.Substring(0, num);
			}
			return text.Trim().ToLowerInvariant();
		}
	}
}
namespace ServerSync
{
	[PublicAPI]
	internal abstract class OwnConfigEntryBase
	{
		public object? LocalBaseValue;

		public bool SynchronizedConfig = true;

		public abstract ConfigEntryBase BaseConfig { get; }
	}
	[PublicAPI]
	internal class SyncedConfigEntry<T> : OwnConfigEntryBase
	{
		public readonly ConfigEntry<T> SourceConfig;

		public override ConfigEntryBase BaseConfig => (ConfigEntryBase)(object)SourceConfig;

		public T Value
		{
			get
			{
				return SourceConfig.Value;
			}
			set
			{
				SourceConfig.Value = value;
			}
		}

		public SyncedConfigEntry(ConfigEntry<T> sourceConfig)
		{
			SourceConfig = sourceConfig;
			base..ctor();
		}

		public void AssignLocalValue(T value)
		{
			if (LocalBaseValue == null)
			{
				Value = value;
			}
			else
			{
				LocalBaseValue = value;
			}
		}
	}
	internal abstract class CustomSyncedValueBase
	{
		public object? LocalBaseValue;

		public readonly string Identifier;

		public readonly Type Type;

		private object? boxedValue;

		protected bool localIsOwner;

		public readonly int Priority;

		public object? BoxedValue
		{
			get
			{
				return boxedValue;
			}
			set
			{
				boxedValue = value;
				this.ValueChanged?.Invoke();
			}
		}

		public event Action? ValueChanged;

		protected CustomSyncedValueBase(ConfigSync configSync, string identifier, Type type, int priority)
		{
			Priority = priority;
			Identifier = identifier;
			Type = type;
			configSync.AddCustomValue(this);
			localIsOwner = configSync.IsSourceOfTruth;
			configSync.SourceOfTruthChanged += delegate(bool truth)
			{
				localIsOwner = truth;
			};
		}
	}
	[PublicAPI]
	internal sealed class CustomSyncedValue<T> : CustomSyncedValueBase
	{
		public T Value
		{
			get
			{
				return (T)base.BoxedValue;
			}
			set
			{
				base.BoxedValue = value;
			}
		}

		public CustomSyncedValue(ConfigSync configSync, string identifier, T value = default(T), int priority = 0)
			: base(configSync, identifier, typeof(T), priority)
		{
			Value = value;
		}

		public void AssignLocalValue(T value)
		{
			if (localIsOwner)
			{
				Value = value;
			}
			else
			{
				LocalBaseValue = value;
			}
		}
	}
	internal class ConfigurationManagerAttributes
	{
		[UsedImplicitly]
		public bool? ReadOnly = false;
	}
	[PublicAPI]
	internal class ConfigSync
	{
		[HarmonyPatch(typeof(ZRpc), "HandlePackage")]
		private static class SnatchCurrentlyHandlingRPC
		{
			public static ZRpc? currentRpc;

			[HarmonyPrefix]
			private static void Prefix(ZRpc __instance)
			{
				currentRpc = __instance;
			}
		}

		[HarmonyPatch(typeof(ZNet), "Awake")]
		internal static class RegisterRPCPatch
		{
			[HarmonyPostfix]
			private static void Postfix(ZNet __instance)
			{
				isServer = __instance.IsServer();
				foreach (ConfigSync configSync2 in configSyncs)
				{
					ZRoutedRpc.instance.Register<ZPackage>(configSync2.Name + " ConfigSync", (Action<long, ZPackage>)configSync2.RPC_FromOtherClientConfigSync);
					if (isServer)
					{
						configSync2.InitialSyncDone = true;
						Debug.Log((object)("Registered '" + configSync2.Name + " ConfigSync' RPC - waiting for incoming connections"));
					}
				}
				if (isServer)
				{
					((MonoBehaviour)__instance).StartCoroutine(Wat