Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of EverythingsANail v1.0.2
plugins/EverythingsANail.dll
Decompiled 9 hours agousing 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) { } } }