Decompiled source of EverythingsANail v1.0.2

plugins/EverythingsANail.dll

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

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("EverythingsANail")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Your hammer does it all: switch it to any tool you've owned (hoe, cultivator, scythe, fishing rod, knife, axes, pickaxes, modded tools) from the radial menu. Server-locked config.")]
[assembly: AssemblyFileVersion("1.0.2.0")]
[assembly: AssemblyInformationalVersion("1.0.2+08b36d09672fedd0301fabfcc6894e7c3da3d46f")]
[assembly: AssemblyProduct("EverythingsANail")]
[assembly: AssemblyTitle("EverythingsANail")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.2.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace EverythingsANail
{
	internal static class Commands
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static ConsoleEvent <>9__0_0;

			public static Func<ToolInfo, ToolCategories> <>9__1_0;

			public static Func<ToolInfo, string> <>9__1_1;

			public static Func<ToolInfo, float> <>9__1_2;

			public static Func<string, bool> <>9__1_3;

			internal void <Register>b__0_0(ConsoleEventArgs args)
			{
				ListTools(args.Context);
			}

			internal ToolCategories <ListTools>b__1_0(ToolInfo tool)
			{
				return tool.Category;
			}

			internal string <ListTools>b__1_1(ToolInfo tool)
			{
				return tool.Group;
			}

			internal float <ListTools>b__1_2(ToolInfo tool)
			{
				return tool.Rank;
			}

			internal bool <ListTools>b__1_3(string text)
			{
				return text != null;
			}
		}

		public static void Register()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			object obj = <>c.<>9__0_0;
			if (obj == null)
			{
				ConsoleEvent val = delegate(ConsoleEventArgs args)
				{
					ListTools(args.Context);
				};
				<>c.<>9__0_0 = val;
				obj = (object)val;
			}
			new ConsoleCommand("nail_tools", "Lists the tools the hammer can become, with their internal names (for the OnlyAllow and Exclude settings)", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		}

		private static void ListTools(Terminal terminal)
		{
			if ((Object)(object)terminal == (Object)null)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			Rules active = Settings.Active;
			string arg = ((ConfigSync.Remote != null) ? "the server" : "your config");
			terminal.AddString($"Rules from {arg}: categories {active.Categories}" + ((active.OnlyAllow.Count > 0) ? (", only allow " + Rules.FormatList(active.OnlyAllow) + " (categories ignored)") : "") + ((active.Exclude.Count > 0) ? (", exclude " + Rules.FormatList(active.Exclude)) : ""));
			foreach (ToolInfo item in from tool in ToolCatalog.All
				orderby tool.Category, tool.Group, tool.Rank descending
				select tool)
			{
				bool flag = ToolCatalog.IsAllowed(item);
				bool flag2 = ToolCatalog.IsUnlocked(localPlayer, item);
				string text = ((flag && flag2) ? "<color=green>available</color>" : string.Join(", ", new string[2]
				{
					flag ? null : "not allowed",
					flag2 ? null : "never owned"
				}.Where((string text2) => text2 != null)));
				terminal.AddString($"  {item.Category}: {item.Prefab} ({item.DisplayName}) - {text}");
			}
		}
	}
	internal static class ConfigSync
	{
		private const string RpcName = "s992.EverythingsANail.Rules";

		private const int Format = 1;

		private static ManualLogSource s_log;

		private static Rules s_remote;

		private static ZNet s_remoteNet;

		public static Rules Remote
		{
			get
			{
				if (s_remote == null || !((Object)(object)ZNet.instance != (Object)null) || !((Object)(object)s_remoteNet == (Object)(object)ZNet.instance))
				{
					return null;
				}
				return s_remote;
			}
		}

		private static bool IsServer
		{
			get
			{
				if ((Object)(object)ZNet.instance != (Object)null)
				{
					return ZNet.instance.IsServer();
				}
				return false;
			}
		}

		public static void Init(ManualLogSource log)
		{
			s_log = log;
		}

		public static void OnNewConnection(ZNetPeer peer)
		{
			peer.m_rpc.Register<ZPackage>("s992.EverythingsANail.Rules", (Action<ZRpc, ZPackage>)OnReceive);
		}

		public static void OnPeerInfo(ZNetPeer peer)
		{
			if (IsServer && peer != null)
			{
				SendTo(peer);
			}
		}

		public static void Broadcast()
		{
			if (!IsServer)
			{
				return;
			}
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				SendTo(peer);
			}
		}

		private static void SendTo(ZNetPeer peer)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			if (peer?.m_rpc != null)
			{
				Rules local = Settings.Local;
				ZPackage val = new ZPackage();
				val.Write(1);
				val.Write((int)local.Categories);
				val.Write(Rules.FormatList(local.OnlyAllow));
				val.Write(Rules.FormatList(local.Exclude));
				peer.m_rpc.Invoke("s992.EverythingsANail.Rules", new object[1] { val });
			}
		}

		private static void OnReceive(ZRpc rpc, ZPackage pkg)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && !IsServer)
			{
				int num = pkg.ReadInt();
				if (num != 1)
				{
					s_log.LogWarning((object)$"Server sent tool rules in format {num}, expected {1}; update the mod on both sides. Using local rules.");
					return;
				}
				s_remote = new Rules
				{
					Categories = (ToolCategories)pkg.ReadInt(),
					OnlyAllow = Rules.ParseList(pkg.ReadString()),
					Exclude = Rules.ParseList(pkg.ReadString())
				};
				s_remoteNet = ZNet.instance;
				s_log.LogInfo((object)($"Using the server's tool rules: categories {s_remote.Categories}, " + "only allow [" + Rules.FormatList(s_remote.OnlyAllow) + "], exclude [" + Rules.FormatList(s_remote.Exclude) + "]"));
				Settings.NotifyRulesChanged();
			}
		}
	}
	internal static class IconBadge
	{
		private const int Size = 128;

		private const float BadgeScale = 0.5f;

		public static Sprite Compose(Sprite icon, Sprite badge)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: 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_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Expected O, but got Unknown
			//IL_00dc: 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_0112: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)icon) || !Object.op_Implicit((Object)(object)badge))
			{
				return icon;
			}
			RenderTexture active = RenderTexture.active;
			RenderTexture temporary = RenderTexture.GetTemporary(128, 128, 0, (RenderTextureFormat)0);
			try
			{
				RenderTexture.active = temporary;
				GL.Clear(true, true, Color.clear);
				GL.PushMatrix();
				GL.LoadPixelMatrix(0f, 128f, 128f, 0f);
				Draw(icon, new Rect(0f, 0f, 128f, 128f));
				float num = 64f;
				Draw(badge, new Rect(128f - num, 128f - num, num, num));
				GL.PopMatrix();
				Texture2D val = new Texture2D(128, 128, (TextureFormat)4, false)
				{
					name = ((Object)icon).name + "_nail"
				};
				val.ReadPixels(new Rect(0f, 0f, 128f, 128f), 0, 0);
				val.Apply();
				Sprite obj = Sprite.Create(val, new Rect(0f, 0f, 128f, 128f), new Vector2(0.5f, 0.5f), 100f);
				((Object)obj).name = ((Object)val).name;
				return obj;
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("[EverythingsANail] couldn't draw the hammer badge on " + ((Object)icon).name + ", using the plain icon: " + ex.Message));
				return icon;
			}
			finally
			{
				RenderTexture.active = active;
				RenderTexture.ReleaseTemporary(temporary);
			}
		}

		private static void Draw(Sprite sprite, Rect dest)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Invalid comparison between Unknown and I4
			//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_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: 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_00f1: Unknown result type (might be due to invalid IL or missing references)
			Texture2D texture = sprite.texture;
			if (!sprite.packed || (int)sprite.packingMode == 1)
			{
				Rect textureRect = sprite.textureRect;
				Graphics.DrawTexture(dest, (Texture)(object)texture, new Rect(((Rect)(ref textureRect)).x / (float)((Texture)texture).width, ((Rect)(ref textureRect)).y / (float)((Texture)texture).height, ((Rect)(ref textureRect)).width / (float)((Texture)texture).width, ((Rect)(ref textureRect)).height / (float)((Texture)texture).height), 0, 0, 0, 0);
				return;
			}
			Vector2[] uv = sprite.uv;
			float num = 1f;
			float num2 = 1f;
			float num3 = 0f;
			float num4 = 0f;
			Vector2[] array = uv;
			foreach (Vector2 val in array)
			{
				num = Mathf.Min(num, val.x);
				num2 = Mathf.Min(num2, val.y);
				num3 = Mathf.Max(num3, val.x);
				num4 = Mathf.Max(num4, val.y);
			}
			Graphics.DrawTexture(dest, (Texture)(object)texture, new Rect(num, num2, num3 - num, num4 - num2), 0, 0, 0, 0);
		}
	}
	internal static class Impersonation
	{
		public const string ToolKey = "s992.EverythingsANail.Tool";

		private static readonly MethodInfo s_memberwiseClone = AccessTools.Method(typeof(object), "MemberwiseClone", (Type[])null, (Type[])null);

		private static readonly Dictionary<string, SharedData> s_disguises = new Dictionary<string, SharedData>();

		private static ObjectDB s_disguisesDb;

		private static readonly Dictionary<SharedData, ToolInfo> s_toolOf = new Dictionary<SharedData, ToolInfo>();

		public static bool IsHammer(ItemData item)
		{
			if ((Object)(object)item?.m_dropPrefab != (Object)null)
			{
				return ((Object)item.m_dropPrefab).name == "Hammer";
			}
			return false;
		}

		public static bool IsDisguised(ItemData item)
		{
			if (item?.m_shared != null)
			{
				return s_toolOf.ContainsKey(item.m_shared);
			}
			return false;
		}

		public static ToolInfo CurrentTool(ItemData hammer)
		{
			if (hammer?.m_shared == null || !s_toolOf.TryGetValue(hammer.m_shared, out var value))
			{
				return ToolCatalog.Hammer;
			}
			return value;
		}

		public static string StoredTool(ItemData hammer)
		{
			if (!hammer.m_customData.TryGetValue("s992.EverythingsANail.Tool", out var value))
			{
				return null;
			}
			return value;
		}

		public static void SetStoredTool(ItemData hammer, ToolInfo tool)
		{
			if (tool == null || tool.IsHammer)
			{
				hammer.m_customData.Remove("s992.EverythingsANail.Tool");
			}
			else
			{
				hammer.m_customData["s992.EverythingsANail.Tool"] = tool.Prefab;
			}
		}

		public static void Disguise(Player player, ItemData hammer)
		{
			Restore(hammer);
			ToolInfo toolInfo = ToolCatalog.Get(StoredTool(hammer));
			if (toolInfo != null && !toolInfo.IsHammer && ToolCatalog.IsAvailable(player, toolInfo))
			{
				hammer.m_shared = GetDisguise(toolInfo, hammer);
			}
		}

		public static void Restore(ItemData item)
		{
			if (IsDisguised(item))
			{
				ItemDrop val = (Object.op_Implicit((Object)(object)item.m_dropPrefab) ? item.m_dropPrefab.GetComponent<ItemDrop>() : null);
				if (Object.op_Implicit((Object)(object)val))
				{
					item.m_shared = val.m_itemData.m_shared;
				}
			}
		}

		private static SharedData GetDisguise(ToolInfo tool, ItemData hammer)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected O, but got Unknown
			if ((Object)(object)s_disguisesDb != (Object)(object)ObjectDB.instance)
			{
				s_disguises.Clear();
				s_disguisesDb = ObjectDB.instance;
			}
			if (s_disguises.TryGetValue(tool.Prefab, out var value))
			{
				return value;
			}
			SharedData shared = hammer.m_dropPrefab.GetComponent<ItemDrop>().m_itemData.m_shared;
			value = (SharedData)s_memberwiseClone.Invoke(tool.Shared, null);
			value.m_maxDurability = shared.m_maxDurability;
			value.m_durabilityPerLevel = shared.m_durabilityPerLevel;
			value.m_useDurability = shared.m_useDurability;
			value.m_canBeReparied = shared.m_canBeReparied;
			value.m_destroyBroken = shared.m_destroyBroken;
			value.m_maxQuality = shared.m_maxQuality;
			value.m_weight = shared.m_weight;
			value.m_scaleWeightByQuality = shared.m_scaleWeightByQuality;
			value.m_teleportable = shared.m_teleportable;
			value.m_value = shared.m_value;
			value.m_variants = shared.m_variants;
			Sprite val = ((shared.m_icons != null && shared.m_icons.Length != 0) ? shared.m_icons[0] : null);
			value.m_icons = (Sprite[])(object)new Sprite[1] { IconBadge.Compose(tool.Icon, val) ?? val };
			s_disguises[tool.Prefab] = value;
			s_toolOf[value] = tool;
			return value;
		}
	}
	[HarmonyPatch(typeof(Humanoid), "EquipItem")]
	internal static class EquipItemPatch
	{
		[HarmonyPriority(800)]
		private static void Prefix(Humanoid __instance, ItemData item)
		{
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (val != null && (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || !((Object)(object)val != (Object)(object)Player.m_localPlayer)) && Impersonation.IsHammer(item) && !__instance.IsItemEquiped(item))
			{
				Impersonation.Disguise(val, item);
			}
		}

		private static void Postfix(Humanoid __instance, ItemData item)
		{
			if (Impersonation.IsHammer(item) && !__instance.IsItemEquiped(item))
			{
				Impersonation.Restore(item);
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "UnequipItem")]
	internal static class UnequipItemPatch
	{
		private static void Postfix(Humanoid __instance, ItemData item)
		{
			if (Impersonation.IsHammer(item) && !__instance.IsItemEquiped(item))
			{
				Impersonation.Restore(item);
			}
		}
	}
	[HarmonyPatch(typeof(ItemData), "Clone")]
	internal static class ItemClonePatch
	{
		private static void Postfix(ItemData __result)
		{
			Impersonation.Restore(__result);
		}
	}
	[HarmonyPatch(typeof(Humanoid), "SetupVisEquipment")]
	internal static class SetupVisEquipmentPatch
	{
		private static void Postfix(Humanoid __instance, VisEquipment visEq, bool isRagdoll)
		{
			if (!isRagdoll && Object.op_Implicit((Object)(object)visEq))
			{
				ItemData rightItem = __instance.m_rightItem;
				if (Impersonation.IsDisguised(rightItem))
				{
					visEq.SetRightItem(StringExtensionMethods.GetStableHashCode(Impersonation.CurrentTool(rightItem).Prefab), rightItem.m_quality);
				}
				ItemData leftItem = __instance.m_leftItem;
				if (Impersonation.IsDisguised(leftItem))
				{
					visEq.SetLeftItem(StringExtensionMethods.GetStableHashCode(Impersonation.CurrentTool(leftItem).Prefab), 0, leftItem.m_quality);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Inventory), "GetAllPieceTables")]
	internal static class GetAllPieceTablesPatch
	{
		private static void Postfix(Inventory __instance, List<PieceTable> tables)
		{
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer) || __instance != ((Humanoid)localPlayer).GetInventory() || !__instance.GetAllItems().Any(Impersonation.IsHammer))
			{
				return;
			}
			foreach (ToolInfo item in ToolCatalog.All)
			{
				PieceTable buildPieces = item.Shared.m_buildPieces;
				if (Object.op_Implicit((Object)(object)buildPieces) && !tables.Contains(buildPieces) && ToolCatalog.IsAvailable(localPlayer, item))
				{
					tables.Add(buildPieces);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "SetCraftingStation")]
	internal static class SetCraftingStationPatch
	{
		private static void Postfix(Player __instance, CraftingStation station)
		{
			if (Object.op_Implicit((Object)(object)station) && (Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				ToolSwitcher.RevertToHammer(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[]
	{
		typeof(ItemData),
		typeof(int),
		typeof(bool),
		typeof(float),
		typeof(int),
		typeof(bool)
	})]
	internal static class TooltipPatch
	{
		private static void Postfix(ItemData item, ref string __result)
		{
			if (Impersonation.IsDisguised(item))
			{
				__result = __result + "\n\n<color=orange>Your hammer, working as this tool.</color> " + Localization.instance.Localize("Press <color=yellow>$KEY_OpenRadial</color> to switch tools.");
			}
		}
	}
	[HarmonyPatch(typeof(Player), "HandleRadialInput")]
	internal static class HandleRadialInputPatch
	{
		private static bool Prefix(Player __instance)
		{
			return !ToolMenu.TryOpen(__instance);
		}
	}
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	internal static class RadialBlockingPatch
	{
		private static bool Prefix(ref bool __result)
		{
			if (!ToolMenu.Opening)
			{
				return true;
			}
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(KeyHintsRadial), "UpdateRadialHints")]
	internal static class RadialHintsPatch
	{
		private static void Postfix(KeyHintsRadial __instance, RadialBase radial)
		{
			if (ToolMenu.IsOurs(radial.CurrentConfig))
			{
				((Component)__instance.m_gamepadDrop).gameObject.SetActive(false);
				((Component)__instance.m_gamepadDropMulti).gameObject.SetActive(false);
				__instance.m_kbDrop.SetActive(false);
				__instance.m_kbDropMulti.SetActive(false);
			}
		}
	}
	[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
	internal static class NewConnectionPatch
	{
		private static void Postfix(ZNetPeer peer)
		{
			ConfigSync.OnNewConnection(peer);
		}
	}
	[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
	internal static class PeerInfoPatch
	{
		private static void Postfix(ZNet __instance, ZRpc rpc)
		{
			ConfigSync.OnPeerInfo(__instance.GetPeer(rpc));
		}
	}
	[HarmonyPatch(typeof(Terminal), "InitTerminal")]
	internal static class InitTerminalPatch
	{
		private static void Postfix()
		{
			Commands.Register();
		}
	}
	[BepInPlugin("s992.EverythingsANail", "EverythingsANail", "1.0.2")]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Log;

		private FileSystemWatcher m_watcher;

		private volatile bool m_configFileChanged;

		private float m_reloadAt = -1f;

		private void Awake()
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			Settings.Bind(((BaseUnityPlugin)this).Config);
			ConfigSync.Init(((BaseUnityPlugin)this).Logger);
			Settings.RulesChanged += ToolSwitcher.Revalidate;
			new Harmony("s992.EverythingsANail").PatchAll();
			WatchConfigFile();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"EverythingsANail 1.0.2 loaded");
		}

		private void WatchConfigFile()
		{
			string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath;
			m_watcher = new FileSystemWatcher(Path.GetDirectoryName(configFilePath), Path.GetFileName(configFilePath))
			{
				NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite)
			};
			m_watcher.Changed += delegate
			{
				m_configFileChanged = true;
			};
			m_watcher.Created += delegate
			{
				m_configFileChanged = true;
			};
			m_watcher.Renamed += delegate
			{
				m_configFileChanged = true;
			};
			m_watcher.EnableRaisingEvents = true;
		}

		private void Update()
		{
			if (m_configFileChanged)
			{
				m_configFileChanged = false;
				m_reloadAt = Time.unscaledTime + 1f;
			}
			if (m_reloadAt >= 0f && Time.unscaledTime >= m_reloadAt)
			{
				m_reloadAt = -1f;
				if (File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath))
				{
					((BaseUnityPlugin)this).Config.Reload();
				}
			}
		}

		private void OnDestroy()
		{
			m_watcher?.Dispose();
		}
	}
	[Flags]
	public enum ToolCategories
	{
		None = 0,
		Building = 1,
		Butchering = 2,
		Harvesting = 4,
		Fishing = 8,
		Logging = 0x10,
		Mining = 0x20
	}
	internal sealed class Rules
	{
		public ToolCategories Categories;

		public HashSet<string> OnlyAllow = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		public HashSet<string> Exclude = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		public static HashSet<string> ParseList(string text)
		{
			return new HashSet<string>(from name in (text ?? "").Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries)
				select name.Trim() into name
				where name.Length > 0
				select name, StringComparer.OrdinalIgnoreCase);
		}

		public static string FormatList(IEnumerable<string> names)
		{
			return string.Join(", ", names);
		}
	}
	internal static class Settings
	{
		public static ConfigEntry<ToolCategories> Categories;

		public static ConfigEntry<string> OnlyAllow;

		public static ConfigEntry<string> Exclude;

		private static Rules s_local;

		public static Rules Local
		{
			get
			{
				if (s_local == null)
				{
					s_local = new Rules
					{
						Categories = Categories.Value,
						OnlyAllow = Rules.ParseList(OnlyAllow.Value),
						Exclude = Rules.ParseList(Exclude.Value)
					};
				}
				return s_local;
			}
		}

		public static Rules Active => ConfigSync.Remote ?? Local;

		public static event Action RulesChanged;

		public static void Bind(ConfigFile config)
		{
			Categories = config.Bind<ToolCategories>("Tools", "Categories", ToolCategories.Building | ToolCategories.Butchering | ToolCategories.Harvesting | ToolCategories.Fishing, "Kinds of tools the hammer can become. Building: anything with a build menu (hoe, cultivator, tools from other mods). Butchering: butcher knife. Harvesting: scythe. Fishing: fishing rod. Logging: one-handed axes. Mining: pickaxes. The hammer itself is always available. In multiplayer the server's setting is used when the server runs this mod.");
			OnlyAllow = config.Bind<string>("Tools", "OnlyAllow", "", "Comma-separated internal item names (e.g. AxeJotunBane, FishingRod). When set, only these tools are available and Categories is ignored. The nail_tools console command lists the names. In multiplayer the server's setting is used when the server runs this mod.");
			Exclude = config.Bind<string>("Tools", "Exclude", "", "Comma-separated internal item names that are never available, even if their category is enabled. In multiplayer the server's setting is used when the server runs this mod.");
			Categories.SettingChanged += delegate
			{
				OnLocalChanged();
			};
			OnlyAllow.SettingChanged += delegate
			{
				OnLocalChanged();
			};
			Exclude.SettingChanged += delegate
			{
				OnLocalChanged();
			};
		}

		private static void OnLocalChanged()
		{
			s_local = null;
			ConfigSync.Broadcast();
			NotifyRulesChanged();
		}

		public static void NotifyRulesChanged()
		{
			Settings.RulesChanged?.Invoke();
		}
	}
	internal sealed class ToolInfo
	{
		public string Prefab;

		public SharedData Shared;

		public ToolCategories Category;

		public string Group;

		public float Rank;

		public bool IsHammer => Prefab == "Hammer";

		public string DisplayName => Localization.instance.Localize(Shared.m_name);

		public Sprite Icon
		{
			get
			{
				if (Shared.m_icons == null || Shared.m_icons.Length == 0)
				{
					return null;
				}
				return Shared.m_icons[0];
			}
		}
	}
	internal static class ToolCatalog
	{
		public const string HammerPrefab = "Hammer";

		private static ObjectDB s_db;

		private static int s_itemCount;

		private static List<ToolInfo> s_tools = new List<ToolInfo>();

		private static Dictionary<string, ToolInfo> s_byPrefab = new Dictionary<string, ToolInfo>();

		public static IReadOnlyList<ToolInfo> All
		{
			get
			{
				Refresh();
				return s_tools;
			}
		}

		public static ToolInfo Hammer => Get("Hammer");

		public static ToolInfo Get(string prefab)
		{
			Refresh();
			if (prefab == null || !s_byPrefab.TryGetValue(prefab, out var value))
			{
				return null;
			}
			return value;
		}

		public static bool IsAllowed(ToolInfo tool)
		{
			if (tool.IsHammer)
			{
				return true;
			}
			Rules active = Settings.Active;
			if (active.Exclude.Contains(tool.Prefab))
			{
				return false;
			}
			if (active.OnlyAllow.Count > 0)
			{
				return active.OnlyAllow.Contains(tool.Prefab);
			}
			return (active.Categories & tool.Category) != 0;
		}

		public static bool IsUnlocked(Player player, ToolInfo tool)
		{
			if (!tool.IsHammer)
			{
				if ((Object)(object)player != (Object)null)
				{
					return player.IsKnownMaterial(tool.Shared.m_name);
				}
				return false;
			}
			return true;
		}

		public static bool IsAvailable(Player player, ToolInfo tool)
		{
			if (IsAllowed(tool))
			{
				return IsUnlocked(player, tool);
			}
			return false;
		}

		public static List<List<ToolInfo>> AvailableGroups(Player player)
		{
			return (from tool in All
				where IsAvailable(player, tool)
				group tool by tool.Group into @group
				select (from tool in @group
					orderby tool.Rank descending, tool.DisplayName
					select tool).ToList() into @group
				orderby (!@group[0].IsHammer) ? 1 : 0, @group[0].Category, @group[0].DisplayName
				select @group).ToList();
		}

		private static void Refresh()
		{
			ObjectDB instance = ObjectDB.instance;
			if (!Object.op_Implicit((Object)(object)instance))
			{
				s_db = null;
				s_tools = new List<ToolInfo>();
				s_byPrefab = new Dictionary<string, ToolInfo>();
			}
			else
			{
				if ((Object)(object)instance == (Object)(object)s_db && instance.m_items.Count == s_itemCount)
				{
					return;
				}
				s_db = instance;
				s_itemCount = instance.m_items.Count;
				List<ToolInfo> list = new List<ToolInfo>();
				Dictionary<string, ToolInfo> dictionary = new Dictionary<string, ToolInfo>();
				foreach (GameObject item in instance.m_items)
				{
					ToolInfo toolInfo = Classify(item);
					if (toolInfo != null && !dictionary.ContainsKey(toolInfo.Prefab))
					{
						list.Add(toolInfo);
						dictionary[toolInfo.Prefab] = toolInfo;
					}
				}
				s_tools = list;
				s_byPrefab = dictionary;
			}
		}

		private static ToolInfo Classify(GameObject prefab)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Invalid comparison between Unknown and I4
			//IL_005a: 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_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Invalid comparison between Unknown and I4
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Invalid comparison between Unknown and I4
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Invalid comparison between Unknown and I4
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Invalid comparison between Unknown and I4
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: 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)
			if (!Object.op_Implicit((Object)(object)prefab))
			{
				return null;
			}
			ItemDrop component = prefab.GetComponent<ItemDrop>();
			SharedData val = ((!Object.op_Implicit((Object)(object)component)) ? null : component.m_itemData?.m_shared);
			if (val == null)
			{
				return null;
			}
			if ((int)val.m_animationState == 16)
			{
				return null;
			}
			ToolInfo toolInfo = new ToolInfo
			{
				Prefab = ((Object)prefab).name,
				Shared = val
			};
			DamageTypes damages = val.m_damages;
			if (Object.op_Implicit((Object)(object)val.m_buildPieces))
			{
				toolInfo.Category = ToolCategories.Building;
				toolInfo.Group = ((Object)prefab).name;
			}
			else if (val.m_tamedOnly || ((Object)prefab).name == "KnifeButcher")
			{
				toolInfo.Category = ToolCategories.Butchering;
				toolInfo.Rank = ((DamageTypes)(ref damages)).GetTotalDamage();
			}
			else if ((int)val.m_animationState == 9)
			{
				toolInfo.Category = ToolCategories.Fishing;
			}
			else if (IsHarvest(val.m_attack) || IsHarvest(val.m_secondaryAttack))
			{
				toolInfo.Category = ToolCategories.Harvesting;
				toolInfo.Rank = (float)(val.m_toolTier * 1000) + ((DamageTypes)(ref damages)).GetTotalDamage();
			}
			else if ((int)val.m_skillType == 7 && (int)val.m_itemType == 3 && damages.m_chop > 0f)
			{
				toolInfo.Category = ToolCategories.Logging;
				toolInfo.Rank = (float)(val.m_toolTier * 1000) + damages.m_chop;
			}
			else
			{
				if ((int)val.m_skillType != 12 || !(damages.m_pickaxe > 0f))
				{
					return null;
				}
				toolInfo.Category = ToolCategories.Mining;
				toolInfo.Rank = (float)(val.m_toolTier * 1000) + damages.m_pickaxe;
			}
			if (toolInfo.Group == null)
			{
				toolInfo.Group = toolInfo.Category.ToString();
			}
			return toolInfo;
		}

		private static bool IsHarvest(Attack attack)
		{
			return attack?.m_harvest ?? false;
		}
	}
	internal static class ToolMenu
	{
		private static ToolsOpenConfig s_openConfig;

		private static Sprite s_mainMenuIcon;

		public static bool Opening { get; private set; }

		public static Sprite MainMenuIcon
		{
			get
			{
				//IL_0022: Unknown result type (might be due to invalid IL or missing references)
				//IL_0078: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b1: Expected O, but got Unknown
				//IL_0174: Unknown result type (might be due to invalid IL or missing references)
				//IL_0183: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
				//IL_0127: Unknown result type (might be due to invalid IL or missing references)
				//IL_012c: Unknown result type (might be due to invalid IL or missing references)
				if (Object.op_Implicit((Object)(object)s_mainMenuIcon))
				{
					return s_mainMenuIcon;
				}
				List<(Vector2, float)> list = new List<(Vector2, float)> { (new Vector2(64f, 64f), 13f) };
				for (int i = 0; i < 6; i++)
				{
					float num = MathF.PI / 2f + (float)i * MathF.PI / 3f;
					list.Add((new Vector2(64f + Mathf.Cos(num) * 42f, 64f + Mathf.Sin(num) * 42f), 11f));
				}
				Texture2D val = new Texture2D(128, 128, (TextureFormat)4, false)
				{
					name = "EverythingsANail_MainMenu"
				};
				Color32[] array = (Color32[])(object)new Color32[16384];
				for (int j = 0; j < 128; j++)
				{
					for (int k = 0; k < 128; k++)
					{
						Vector2 point = new Vector2((float)k + 0.5f, (float)j + 0.5f);
						float num2 = list.Max<(Vector2, float)>(((Vector2 centre, float radius) dot) => Mathf.Clamp01(dot.radius + 0.5f - Vector2.Distance(point, dot.centre)));
						array[j * 128 + k] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)(num2 * 255f));
					}
				}
				val.SetPixels32(array);
				val.Apply();
				s_mainMenuIcon = Sprite.Create(val, new Rect(0f, 0f, 128f, 128f), new Vector2(0.5f, 0.5f), 100f);
				((Object)s_mainMenuIcon).name = ((Object)val).name;
				return s_mainMenuIcon;
			}
		}

		public static bool IsOurs(IRadialConfig config)
		{
			if (!(config is TopConfig))
			{
				return config is GroupConfig;
			}
			return true;
		}

		public static bool TryOpen(Player player)
		{
			if (Hud.InRadial() || ToolSwitcher.EquippedHammer(player) == null)
			{
				return false;
			}
			bool buttonDown = ZInput.GetButtonDown("OpenRadial");
			bool flag = !((Character)player).InPlaceMode() && !ZInput.GetButton("JoyAltKeys") && ZInput.GetButtonPressedTimer("JoyRadial") > 0.33f;
			if (!buttonDown && !flag)
			{
				return false;
			}
			RadialBase radialMenu = Hud.instance.m_radialMenu;
			if (!radialMenu.CanOpen)
			{
				if (!buttonDown)
				{
					return false;
				}
				radialMenu.CanOpen = true;
			}
			if (Hud.IsPieceSelectionVisible())
			{
				return true;
			}
			if (!Object.op_Implicit((Object)(object)s_openConfig))
			{
				s_openConfig = ScriptableObject.CreateInstance<ToolsOpenConfig>();
			}
			Opening = true;
			try
			{
				radialMenu.Open((IRadialConfig)(object)s_openConfig, (IRadialConfig)null);
			}
			finally
			{
				Opening = false;
			}
			return true;
		}

		public static void OverrideInput(RadialBase radial)
		{
			Func<bool> back = radial.GetBack;
			Func<bool> openSubMenu = radial.GetOpenThrowMenu;
			Func<bool> secondary = radial.GetThrow;
			radial.GetBack = () => (!(radial.CurrentConfig is TopConfig)) ? (back != null && back()) : ZInput.GetButtonUp("JoyRadialBack");
			radial.GetOpenThrowMenu = delegate
			{
				if (!(radial.CurrentConfig is TopConfig))
				{
					if (!(radial.CurrentConfig is GroupConfig) && openSubMenu != null)
					{
						return openSubMenu();
					}
					return false;
				}
				return ZInput.GetButtonUp("RadialSecondaryInteract") || ZInput.GetButtonUp("JoyRadialSecondaryInteract");
			};
			radial.GetThrow = () => !IsOurs(radial.CurrentConfig) && secondary != null && secondary();
		}

		public static RadialMenuElement CreateToolElement(ToolInfo tool, ToolInfo current, int more)
		{
			GroupElement obj = Object.Instantiate<GroupElement>(RadialData.SO.GroupElement);
			((RadialMenuElement)obj).Name = tool.DisplayName;
			List<string> list = new List<string>();
			if (tool == current)
			{
				list.Add("In hand");
			}
			if (more > 0)
			{
				list.Add($"Right-click: {more} more");
			}
			((RadialMenuElement)obj).SubTitle = string.Join("  ·  ", list);
			((RadialMenuElement)obj).Icon.sprite = tool.Icon;
			((Component)((RadialMenuElement)obj).Icon).gameObject.SetActive(Object.op_Implicit((Object)(object)tool.Icon));
			((RadialMenuElement)obj).Interact = delegate
			{
				ToolSwitcher.Switch(Player.m_localPlayer, tool);
				return true;
			};
			((RadialMenuElement)obj).CloseOnInteract = () => true;
			((RadialMenuElement)obj).Activated = ((tool == current) ? 1f : 0f);
			return (RadialMenuElement)(object)obj;
		}

		public static ToolInfo CurrentTool()
		{
			return Impersonation.CurrentTool(ToolSwitcher.EquippedHammer(Player.m_localPlayer));
		}
	}
	internal sealed class ToolsOpenConfig : OpenRadialConfig, IRadialConfig
	{
		public void InitRadialConfig(RadialBase radial)
		{
			radial.OnInteractionDelay = delegate(float delay)
			{
				PlayerController.SetTakeInputDelay(delay);
			};
			radial.ShouldAnimateIn = true;
			ToolMenu.OverrideInput(radial);
			radial.Open((IRadialConfig)(object)new TopConfig(), (IRadialConfig)null);
		}
	}
	internal sealed class TopConfig : IRadialConfig
	{
		public string LocalizedName => "Tools";

		public Sprite Sprite => null;

		public void InitRadialConfig(RadialBase radial)
		{
			ToolInfo toolInfo = ToolMenu.CurrentTool();
			List<RadialMenuElement> list = new List<RadialMenuElement>();
			foreach (List<ToolInfo> item in ToolCatalog.AvailableGroups(Player.m_localPlayer))
			{
				ToolInfo toolInfo2 = (item.Contains(toolInfo) ? toolInfo : item[0]);
				RadialMenuElement val = ToolMenu.CreateToolElement(toolInfo2, toolInfo, item.Count - 1);
				if (item.Count > 1)
				{
					string key = toolInfo2.Group;
					val.TryOpenSubRadial = delegate(RadialBase menu, int index)
					{
						menu.BackIndex = index;
						menu.QueuedOpen((IRadialConfig)(object)new GroupConfig(key), (IRadialConfig)(object)this);
						return true;
					};
				}
				list.Add(val);
			}
			GroupElement val2 = Object.Instantiate<GroupElement>(RadialData.SO.GroupElement);
			val2.Init((IRadialConfig)(object)RadialData.SO.MainGroupConfig, (IRadialConfig)(object)this, radial);
			((RadialMenuElement)val2).Name = "Main menu";
			((RadialMenuElement)val2).Icon.sprite = ToolMenu.MainMenuIcon;
			((Component)((RadialMenuElement)val2).Icon).gameObject.SetActive(true);
			list.Add((RadialMenuElement)(object)val2);
			radial.ConstructRadial(list);
		}
	}
	internal sealed class GroupConfig : IRadialConfig
	{
		private readonly string m_group;

		public string LocalizedName => m_group;

		public Sprite Sprite => null;

		public GroupConfig(string group)
		{
			m_group = group;
		}

		public void InitRadialConfig(RadialBase radial)
		{
			ToolInfo current = ToolMenu.CurrentTool();
			List<ToolInfo> source = ToolCatalog.AvailableGroups(Player.m_localPlayer).FirstOrDefault((List<ToolInfo> group) => group[0].Group == m_group) ?? new List<ToolInfo>();
			radial.ConstructRadial(source.Select((ToolInfo tool) => ToolMenu.CreateToolElement(tool, current, 0)).ToList());
		}
	}
	internal static class ToolSwitcher
	{
		public static ItemData EquippedHammer(Player player)
		{
			if (!Object.op_Implicit((Object)(object)player))
			{
				return null;
			}
			if (Impersonation.IsHammer(((Humanoid)player).m_rightItem))
			{
				return ((Humanoid)player).m_rightItem;
			}
			if (Impersonation.IsHammer(((Humanoid)player).m_leftItem))
			{
				return ((Humanoid)player).m_leftItem;
			}
			return null;
		}

		public static void Switch(Player player, ToolInfo tool)
		{
			ItemData val = EquippedHammer(player);
			if (val != null && tool != null && Impersonation.CurrentTool(val) != tool)
			{
				if (((Character)player).InAttack() || ((Character)player).InDodge() || (((Character)player).IsSwimming() && !((Character)player).IsOnGround()))
				{
					((Character)player).Message((MessageType)2, "Can't switch tools right now", 0, (Sprite)null, false);
					return;
				}
				Impersonation.SetStoredTool(val, tool);
				Reequip(player, val);
			}
		}

		public static void RevertToHammer(Player player)
		{
			ItemData val = EquippedHammer(player);
			if (val != null && Impersonation.IsDisguised(val))
			{
				Impersonation.SetStoredTool(val, null);
				Reequip(player, val);
			}
		}

		public static void Revalidate()
		{
			Player localPlayer = Player.m_localPlayer;
			ItemData val = EquippedHammer(localPlayer);
			if (val != null && Impersonation.IsDisguised(val))
			{
				ToolInfo toolInfo = Impersonation.CurrentTool(val);
				if (!ToolCatalog.IsAvailable(localPlayer, toolInfo))
				{
					((Character)localPlayer).Message((MessageType)2, toolInfo.DisplayName + " isn't allowed here; back to the hammer", 0, (Sprite)null, false);
					Reequip(localPlayer, val);
				}
			}
		}

		private static void Reequip(Player player, ItemData hammer)
		{
			((Humanoid)player).UnequipItem(hammer, false);
			if (!((Humanoid)player).EquipItem(hammer, true))
			{
				Plugin.Log.LogWarning((object)("Couldn't re-equip the hammer as " + (Impersonation.StoredTool(hammer) ?? "Hammer")));
			}
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "s992.EverythingsANail";

		public const string PLUGIN_NAME = "EverythingsANail";

		public const string PLUGIN_VERSION = "1.0.2";
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}