Decompiled source of CraftMore v1.0.1

BepInEx/plugins/CraftMore/CraftMore.dll

Decompiled 5 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Kriona.Shared;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.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("Kriona")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) 2026 Kriona Hagen - MIT License")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1")]
[assembly: AssemblyProduct("Craft More")]
[assembly: AssemblyTitle("Craft More")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.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 CraftMore
{
	internal enum CraftMode
	{
		Count,
		Stack,
		All
	}
	internal class CraftKey
	{
		public readonly KeyCode Key;

		public readonly CraftMode Mode;

		public readonly int Count;

		public CraftKey(KeyCode key, CraftMode mode, int count)
		{
			//IL_0007: 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)
			Key = key;
			Mode = mode;
			Count = count;
		}
	}
	internal static class CraftConfig
	{
		private const string FileName = "kriona.CraftMore.cfg";

		private const string SettingsSection = "1 - Crafting";

		private const string KeysSection = "2 - Keys";

		private const string Comment = "##";

		private const string StackValue = "Stack";

		private const string AllValue = "All";

		private const string ShowHintsKey = "Show Hints";

		private const string FullExperienceKey = "Full Experience";

		private const string OneAtATimeKey = "One At A Time";

		public const int MaxCount = 10000;

		private static readonly string[] defaultContents = new string[25]
		{
			"## Craft More",
			"## Changes take effect as soon as this file is saved; you don't need to restart Valheim or rejoin the world",
			"",
			"Show Hints = true ## Lists the keys and how many each crafts under the Craft button, true or false",
			"",
			"## The game raises a crafting skill by at most one level per batch, however many crafts are in it, and throws",
			"## away the rest of the experience - with Full Experience on, a batch gives the experience of crafting each one",
			"## separately, so a large batch can raise the skill several levels",
			"## It isn't needed when One At A Time is true, since each craft then gives its own experience",
			"Full Experience = true ## true or false",
			"",
			"## With One At A Time on, a batch crafts its items one after another, each going into your inventory as it's done,",
			"## instead of all of them at the end - the batch takes as long either way, drops what you can't carry on the",
			"## ground, and stops early when it's canceled or you run out of materials",
			"One At A Time = true ## true or false",
			"",
			"## Hold a key while pressing Craft to craft more than one at a time. Each line is: Key = Stack/All/number",
			"## Key is a Unity KeyCode name, e.g. LeftControl, LeftAlt or LeftShift - either side of Ctrl, Shift, Alt and Command counts",
			"## Stack crafts as many as fit in one inventory slot",
			"## All crafts as many as you have the materials for, dropping what you can't carry on the ground",
			"## A number crafts that many, up to " + 10000,
			"## When more than one key is held, the first line wins",
			"",
			"LeftControl = Stack",
			"LeftAlt = All"
		};

		public static readonly List<CraftKey> Keys = new List<CraftKey>();

		private static ConfigMirror mirror;

		private static ConfigEntry<bool> showHintsEntry;

		private static ConfigEntry<bool> fullExperienceEntry;

		private static ConfigEntry<bool> oneAtATimeEntry;

		private static readonly KeyValuePair<KeyCode, string>[] defaultKeys = new KeyValuePair<KeyCode, string>[2]
		{
			new KeyValuePair<KeyCode, string>((KeyCode)306, "Stack"),
			new KeyValuePair<KeyCode, string>((KeyCode)308, "All")
		};

		private static readonly List<KeyValuePair<KeyCode, string>> keyLines = new List<KeyValuePair<KeyCode, string>>();

		private static readonly List<ConfigEntry<KeyCode>> keyEntries = new List<ConfigEntry<KeyCode>>();

		private static readonly List<ConfigEntry<string>> craftsEntries = new List<ConfigEntry<string>>();

		public static bool ShowHints { get; private set; } = true;

		public static bool FullExperience { get; private set; } = true;

		public static bool OneAtATime { get; private set; } = true;

		public static string FilePath => Path.Combine(Paths.ConfigPath, "kriona.CraftMore.cfg");

		public static void Init(ConfigFile file)
		{
			mirror = new ConfigMirror(file, OnEntryChanged);
			showHintsEntry = mirror.Bind("1 - Crafting", "Show Hints", defaultValue: true, "List the keys and how many each crafts under the Craft button", null, 3, null);
			fullExperienceEntry = mirror.Bind("1 - Crafting", "Full Experience", defaultValue: true, "A batch gives the experience of crafting each one separately, so it can raise the skill several levels", null, 2, null);
			oneAtATimeEntry = mirror.Bind("1 - Crafting", "One At A Time", defaultValue: true, "A batch crafts its items one after another, each going into your inventory as it's done, instead of all of them at the end", null, 1, null);
		}

		private static void SyncEntries()
		{
			if (mirror == null)
			{
				return;
			}
			mirror.Sync(delegate
			{
				//IL_005d: Unknown result type (might be due to invalid IL or missing references)
				showHintsEntry.Value = ShowHints;
				fullExperienceEntry.Value = FullExperience;
				oneAtATimeEntry.Value = OneAtATime;
				for (int i = 0; i < keyLines.Count; i++)
				{
					if (i == keyEntries.Count)
					{
						BindKeySlot(i);
					}
					keyEntries[i].Value = keyLines[i].Key;
					craftsEntries[i].Value = keyLines[i].Value;
				}
				while (keyEntries.Count > keyLines.Count)
				{
					int index = keyEntries.Count - 1;
					mirror.Remove((ConfigEntryBase)(object)keyEntries[index]);
					mirror.Remove((ConfigEntryBase)(object)craftsEntries[index]);
					keyEntries.RemoveAt(index);
					craftsEntries.RemoveAt(index);
				}
			});
		}

		private static void BindKeySlot(int slot)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			KeyValuePair<KeyCode, string> keyValuePair = ((slot < defaultKeys.Length) ? defaultKeys[slot] : keyLines[slot]);
			string text = "Key " + (slot + 1);
			int num = 1000 - slot * 2;
			keyEntries.Add(mirror.Bind<KeyCode>("2 - Keys", text, keyValuePair.Key, "The key to hold while pressing Craft - either side of Ctrl, Shift, Alt and Command counts. When more than one key is held, the first slot wins", null, num, null));
			craftsEntries.Add(mirror.Bind("2 - Keys", text + " Crafts", keyValuePair.Value, "Stack crafts as many as fit in one inventory slot, All crafts as many as you have the materials for, and a number crafts that many, up to " + 10000, null, num - 1, null));
		}

		public static void Load()
		{
			string filePath = FilePath;
			string[] lines = defaultContents;
			try
			{
				if (File.Exists(filePath))
				{
					lines = File.ReadAllLines(filePath);
				}
				else
				{
					File.WriteAllText(filePath, string.Join("\r\n", defaultContents) + "\r\n");
				}
			}
			catch (IOException ex)
			{
				Debug.LogWarning((object)("[Craft More] Couldn't read " + filePath + ", using the default keys: " + ex.Message));
			}
			Parse(lines);
			SyncEntries();
		}

		private static void OnEntryChanged()
		{
			string filePath = FilePath;
			string[] array = defaultContents;
			try
			{
				if (File.Exists(filePath))
				{
					array = File.ReadAllLines(filePath);
				}
				List<string> list = new List<string>(array);
				SetLine(list, "Show Hints", showHintsEntry.Value ? "true" : "false");
				SetLine(list, "Full Experience", fullExperienceEntry.Value ? "true" : "false");
				SetLine(list, "One At A Time", oneAtATimeEntry.Value ? "true" : "false");
				SetKeyLines(list);
				array = list.ToArray();
				File.WriteAllText(filePath, string.Join("\r\n", array) + "\r\n");
			}
			catch (IOException ex)
			{
				Debug.LogWarning((object)("[Craft More] Couldn't write " + filePath + ": " + ex.Message));
			}
			Parse(array);
			SyncEntries();
			Plugin.Watcher?.Remember();
		}

		private static void SetLine(List<string> lines, string key, string valueText)
		{
			bool flag = false;
			for (int i = 0; i < lines.Count; i++)
			{
				if (SplitLine(lines[i], out var keyText, out var _, out var comment) && string.Equals(keyText, key, StringComparison.OrdinalIgnoreCase))
				{
					lines[i] = JoinLine(keyText, valueText, comment);
					flag = true;
				}
			}
			if (!flag)
			{
				lines.Add(key + " = " + valueText);
			}
		}

		private unsafe static void SetKeyLines(List<string> lines)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: 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)
			int num = 0;
			for (int i = 0; i < lines.Count; i++)
			{
				if (num >= keyEntries.Count)
				{
					break;
				}
				if (SplitLine(lines[i], out var keyText, out var _, out var comment) && TryParseKey(keyText, out var _))
				{
					KeyCode value = keyEntries[num].Value;
					lines[i] = JoinLine(((int)value == 0) ? keyText : ((object)(*(KeyCode*)(&value))/*cast due to .constrained prefix*/).ToString(), craftsEntries[num].Value.Trim(), comment);
					num++;
				}
			}
		}

		private static bool SplitLine(string line, out string keyText, out string valueText, out string comment)
		{
			string text = line;
			comment = "";
			int num = text.IndexOf("##", StringComparison.Ordinal);
			if (num >= 0)
			{
				comment = text.Substring(num);
				text = text.Substring(0, num);
			}
			int num2 = text.IndexOf('=');
			keyText = ((num2 > 0) ? text.Substring(0, num2).Trim() : "");
			valueText = ((num2 > 0) ? text.Substring(num2 + 1).Trim() : "");
			return keyText.Length > 0;
		}

		private static string JoinLine(string keyText, string valueText, string comment)
		{
			return keyText + " = " + valueText + ((comment.Length > 0) ? (" " + comment) : "");
		}

		private static bool TryParseKey(string keyText, out KeyCode key)
		{
			key = (KeyCode)0;
			if (keyText.Length > 0 && char.IsLetter(keyText[0]) && Enum.TryParse<KeyCode>(keyText, ignoreCase: true, out key))
			{
				return (int)key != 0;
			}
			return false;
		}

		private static void Parse(string[] lines)
		{
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			Keys.Clear();
			keyLines.Clear();
			ShowHints = true;
			FullExperience = true;
			OneAtATime = true;
			foreach (string text in lines)
			{
				string text2 = text;
				int num = text2.IndexOf("##", StringComparison.Ordinal);
				if (num >= 0)
				{
					text2 = text2.Substring(0, num);
				}
				if (text2.Trim().Length == 0)
				{
					continue;
				}
				int num2 = text2.IndexOf('=');
				if (num2 <= 0)
				{
					Warn(text);
					continue;
				}
				string text3 = text2.Substring(0, num2).Trim();
				string text4 = text2.Substring(num2 + 1).Trim();
				if (string.Equals(text3, "Show Hints", StringComparison.OrdinalIgnoreCase))
				{
					ShowHints = ParseBool(text, text4, ShowHints);
					continue;
				}
				if (string.Equals(text3, "Full Experience", StringComparison.OrdinalIgnoreCase))
				{
					FullExperience = ParseBool(text, text4, FullExperience);
					continue;
				}
				if (string.Equals(text3, "One At A Time", StringComparison.OrdinalIgnoreCase))
				{
					OneAtATime = ParseBool(text, text4, OneAtATime);
					continue;
				}
				if (!TryParseKey(text3, out var key))
				{
					Warn(text);
					continue;
				}
				keyLines.Add(new KeyValuePair<KeyCode, string>(key, text4));
				int result;
				if (string.Equals(text4, "Stack", StringComparison.OrdinalIgnoreCase))
				{
					Keys.Add(new CraftKey(key, CraftMode.Stack, 0));
				}
				else if (string.Equals(text4, "All", StringComparison.OrdinalIgnoreCase))
				{
					Keys.Add(new CraftKey(key, CraftMode.All, 0));
				}
				else if (int.TryParse(text4, out result) && result >= 1)
				{
					Keys.Add(new CraftKey(key, CraftMode.Count, Mathf.Min(result, 10000)));
				}
				else
				{
					Warn(text);
				}
			}
		}

		private static bool ParseBool(string line, string valueText, bool fallback)
		{
			if (bool.TryParse(valueText, out var result))
			{
				return result;
			}
			Warn(line);
			return fallback;
		}

		private static void Warn(string line)
		{
			Debug.LogWarning((object)("[Craft More] Couldn't read \"" + line.Trim() + "\" in kriona.CraftMore.cfg, skipping it"));
		}
	}
	internal static class CraftHint
	{
		private const float FontScale = 0.55f;

		private const string Separator = "   ";

		private const float Gap = 4f;

		private const string GameButton = "AltPlace";

		private static TextMeshProUGUI label;

		private static readonly StringBuilder builder = new StringBuilder();

		public static void Update(InventoryGui gui, Player player)
		{
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			Recipe recipe = ((RecipeDataPair)(ref gui.m_selectedRecipe)).Recipe;
			if (!CraftConfig.ShowHints || !((Object)(object)recipe != (Object)null) || ((RecipeDataPair)(ref gui.m_selectedRecipe)).ItemData != null || CraftConfig.Keys.Count <= 0 || ZInput.IsGamepadActive())
			{
				if ((Object)(object)label != (Object)null)
				{
					((Component)label).gameObject.SetActive(false);
				}
				return;
			}
			if ((Object)(object)label == (Object)null)
			{
				label = CreateLabel(gui);
				if ((Object)(object)label == (Object)null)
				{
					return;
				}
			}
			builder.Clear();
			string gameKeyName = GetGameKeyName();
			int num;
			if (gameKeyName != null)
			{
				num = ((Craft.DefaultAmount > 0) ? 1 : 0);
				if (num != 0)
				{
					int value = Craft.DefaultAmount * Mathf.Max(recipe.m_amount, 1);
					builder.Append(gameKeyName).Append(": x").Append(Craft.DefaultAmount)
						.Append(" (")
						.Append(value)
						.Append(')');
				}
			}
			else
			{
				num = 0;
			}
			int num2 = ((num != 0) ? Craft.DefaultAmount : (-1));
			int num3 = -1;
			foreach (CraftKey key in CraftConfig.Keys)
			{
				if (key.Mode == CraftMode.Stack)
				{
					num3 = Craft.GetStackAmount(recipe);
					break;
				}
			}
			foreach (CraftKey key2 in CraftConfig.Keys)
			{
				int craftCount = Craft.GetCraftCount(key2, recipe, player);
				if ((key2.Mode != CraftMode.Stack || (craftCount != num2 && craftCount > 1)) && (key2.Mode != CraftMode.All || (craftCount != num2 && craftCount != num3)))
				{
					if (builder.Length > 0)
					{
						builder.Append("   ");
					}
					int value2 = craftCount * Mathf.Max(recipe.m_amount, 1);
					builder.Append(GetKeyName(key2.Key)).Append(": ").Append(GetModeName(key2))
						.Append(" (")
						.Append(value2)
						.Append(')');
				}
			}
			string text = builder.ToString();
			if (((TMP_Text)label).text != text)
			{
				((TMP_Text)label).text = text;
			}
			((Component)label).gameObject.SetActive(true);
		}

		private static TextMeshProUGUI CreateLabel(InventoryGui gui)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: 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_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: 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_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			TMP_Text componentInChildren = ((Component)gui.m_craftButton).GetComponentInChildren<TMP_Text>(true);
			if ((Object)(object)componentInChildren == (Object)null)
			{
				return null;
			}
			GameObject val = new GameObject("CraftMoreHint", new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(((Component)gui.m_craftButton).transform, false);
			RectTransform val2 = (RectTransform)val.transform;
			val2.anchorMin = new Vector2(0f, 0f);
			val2.anchorMax = new Vector2(1f, 0f);
			val2.pivot = new Vector2(0.5f, 1f);
			val2.anchoredPosition = new Vector2(0f, -4f);
			val2.sizeDelta = new Vector2(0f, 30f);
			TextMeshProUGUI obj = val.AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj).font = componentInChildren.font;
			((TMP_Text)obj).fontSharedMaterial = componentInChildren.fontSharedMaterial;
			((TMP_Text)obj).fontSize = (componentInChildren.enableAutoSizing ? componentInChildren.fontSizeMax : componentInChildren.fontSize) * 0.55f;
			((Graphic)obj).color = Color.white;
			((TMP_Text)obj).alignment = (TextAlignmentOptions)258;
			((TMP_Text)obj).textWrappingMode = (TextWrappingModes)0;
			((TMP_Text)obj).overflowMode = (TextOverflowModes)0;
			((Graphic)obj).raycastTarget = false;
			return obj;
		}

		private static string GetGameKeyName()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Invalid comparison between Unknown and I4
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: 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_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: 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_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			ButtonDef val = ((ZInput.instance != null) ? ZInput.instance.GetButtonDef("AltPlace") : null);
			if (val == null || (int)val.Source == 180 || val.ButtonAction.bindings.Count == 0)
			{
				return null;
			}
			InputBinding val2 = val.ButtonAction.bindings[0];
			string effectivePath = ((InputBinding)(ref val2)).effectivePath;
			if (string.IsNullOrEmpty(effectivePath))
			{
				return null;
			}
			string text = NormalizeKey(effectivePath.Substring(effectivePath.LastIndexOf('/') + 1));
			foreach (CraftKey key in CraftConfig.Keys)
			{
				if (NormalizeKey(((object)key.Key/*cast due to .constrained prefix*/).ToString()) == text)
				{
					return null;
				}
			}
			return text switch
			{
				"control" => "Ctrl", 
				"shift" => "Shift", 
				"alt" => "Alt", 
				"command" => "Cmd", 
				_ => Localization.instance.Localize(ZInput.instance.GetBoundKeyString("AltPlace", true)), 
			};
		}

		private static string NormalizeKey(string name)
		{
			string text = name.ToLowerInvariant().Replace("left", "").Replace("right", "");
			if (!(text == "ctrl"))
			{
				if (text == "meta")
				{
					return "command";
				}
				return text;
			}
			return "control";
		}

		private static string GetModeName(CraftKey craftKey)
		{
			return craftKey.Mode switch
			{
				CraftMode.Stack => "Stack", 
				CraftMode.All => "All", 
				_ => "x" + craftKey.Count, 
			};
		}

		private unsafe static string GetKeyName(KeyCode key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected I4, but got Unknown
			switch (key - 303)
			{
			case 2:
			case 3:
				return "Ctrl";
			case 0:
			case 1:
				return "Shift";
			case 4:
			case 5:
				return "Alt";
			case 6:
			case 7:
				return "Cmd";
			default:
				return ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString();
			}
		}
	}
	internal static class Craft
	{
		private static int defaultAmount = -1;

		private static float defaultDuration;

		private static bool dropExtra;

		private static int queuedCrafts;

		private static int batchCrafts;

		private static readonly FieldRef<GuiBar, float> barMaxValue = AccessTools.FieldRefAccess<GuiBar, float>("m_maxValue");

		private static readonly FieldRef<GuiBar, float> barSmoothValue = AccessTools.FieldRefAccess<GuiBar, float>("m_smoothValue");

		public static int DefaultAmount => defaultAmount;

		public static void QueueBatch(InventoryGui gui)
		{
			queuedCrafts = 0;
			batchCrafts = 0;
			if (CraftConfig.OneAtATime && gui.m_craftTimer == 0f && gui.m_multiCrafting && gui.m_multiCraftAmount > 1)
			{
				batchCrafts = gui.m_multiCraftAmount;
				queuedCrafts = batchCrafts - 1;
				gui.m_multiCraftAmount = 1;
				gui.m_multiCraftDuration = GetSingleDuration();
			}
		}

		public static void ContinueBatch(InventoryGui gui, Player player)
		{
			//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)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: 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_00be: 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_00d2: Unknown result type (might be due to invalid IL or missing references)
			if (queuedCrafts <= 0 || gui.m_craftTimer >= 0f)
			{
				return;
			}
			Recipe craftRecipe = gui.m_craftRecipe;
			if ((Object)(object)craftRecipe == (Object)null)
			{
				queuedCrafts = 0;
				return;
			}
			if (!player.NoCostCheat() && !ZoneSystem.instance.GetGlobalKey((GlobalKeys)25) && !player.HaveRequirements(craftRecipe, false, 1, 1))
			{
				queuedCrafts = 0;
				return;
			}
			queuedCrafts--;
			dropExtra = true;
			gui.m_multiCrafting = true;
			gui.m_multiCraftAmount = 1;
			gui.m_multiCraftDuration = GetSingleDuration();
			gui.m_craftTimer = 0f;
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			if ((Object)(object)craftRecipe.m_craftingStation != (Object)null)
			{
				if ((Object)(object)currentCraftingStation != (Object)null)
				{
					currentCraftingStation.m_craftItemEffects.Create(((Component)player).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
				}
			}
			else
			{
				gui.m_craftItemEffects.Create(((Component)player).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
			}
		}

		public static void StopBatch()
		{
			queuedCrafts = 0;
			batchCrafts = 0;
		}

		public static void ShowBatchProgress(InventoryGui gui)
		{
			if (gui.m_craftTimer < 0f)
			{
				batchCrafts = 0;
			}
			else if (batchCrafts > 1)
			{
				GuiBar craftProgressBar = gui.m_craftProgressBar;
				float num = barMaxValue.Invoke(craftProgressBar);
				int num2 = batchCrafts - queuedCrafts - 1;
				float num3 = num * (float)batchCrafts;
				float num4 = Mathf.Min(num * (float)num2 + gui.m_craftTimer, num3);
				barMaxValue.Invoke(craftProgressBar) = num3;
				craftProgressBar.SetValue(num4);
				barSmoothValue.Invoke(craftProgressBar) = Mathf.Clamp01(num4 / num3);
			}
		}

		private static float GetSingleDuration()
		{
			return defaultDuration / (float)Mathf.Max(defaultAmount, 1);
		}

		private static bool IsKeyHeld(KeyCode key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			KeyCode otherSide = GetOtherSide(key);
			if (!ZInput.GetKey(key, false))
			{
				if ((int)otherSide != 0)
				{
					return ZInput.GetKey(otherSide, false);
				}
				return false;
			}
			return true;
		}

		private static KeyCode GetOtherSide(KeyCode key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected I4, but got Unknown
			return (KeyCode)((key - 303) switch
			{
				3 => 305, 
				2 => 306, 
				1 => 303, 
				0 => 304, 
				5 => 307, 
				4 => 308, 
				7 => 309, 
				6 => 310, 
				_ => 0, 
			});
		}

		private static CraftKey GetHeldKey()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			foreach (CraftKey key in CraftConfig.Keys)
			{
				if (IsKeyHeld(key.Key))
				{
					return key;
				}
			}
			return null;
		}

		public static bool GetButton(string name)
		{
			if (!ZInput.GetButton(name))
			{
				if (name == "AltPlace")
				{
					return GetHeldKey() != null;
				}
				return false;
			}
			return true;
		}

		public static int GetStackAmount(Recipe recipe)
		{
			int num = Mathf.Max(recipe.m_amount, 1);
			return Mathf.Max(recipe.m_item.m_itemData.m_shared.m_maxStackSize / num, 1);
		}

		private static int GetAllAmount(Recipe recipe, Player player)
		{
			if (player.NoCostCheat() || ZoneSystem.instance.GetGlobalKey((GlobalKeys)25) || IsFree(recipe))
			{
				return GetStackAmount(recipe);
			}
			if (!player.HaveRequirements(recipe, false, 1, 1))
			{
				return 0;
			}
			int num = 1;
			int num2 = 2;
			while (num2 <= 10000 && player.HaveRequirements(recipe, false, 1, num2))
			{
				num = num2;
				num2 *= 2;
			}
			num2 = Mathf.Min(num2, 10001);
			while (num2 - num > 1)
			{
				int num3 = (num + num2) / 2;
				if (player.HaveRequirements(recipe, false, 1, num3))
				{
					num = num3;
				}
				else
				{
					num2 = num3;
				}
			}
			return num;
		}

		private static bool IsFree(Recipe recipe)
		{
			bool result = false;
			bool result2 = true;
			Requirement[] resources = recipe.m_resources;
			foreach (Requirement val in resources)
			{
				if (!((Object)(object)val.m_resItem == (Object)null))
				{
					if (val.GetAmount(1) <= 0)
					{
						result = true;
					}
					else
					{
						result2 = false;
					}
				}
			}
			if (!recipe.m_requireOnlyOneIngredient)
			{
				return result2;
			}
			return result;
		}

		public static int GetCraftCount(CraftKey craftKey, Recipe recipe, Player player)
		{
			return craftKey.Mode switch
			{
				CraftMode.Stack => GetStackAmount(recipe), 
				CraftMode.All => GetAllAmount(recipe, player), 
				_ => craftKey.Count, 
			};
		}

		public static CraftKey ApplyAmount(InventoryGui gui)
		{
			if (defaultAmount < 0)
			{
				defaultAmount = gui.m_multiCraftAmount;
				defaultDuration = gui.m_multiCraftDuration;
			}
			CraftKey heldKey = GetHeldKey();
			Recipe recipe = ((RecipeDataPair)(ref gui.m_selectedRecipe)).Recipe;
			Player localPlayer = Player.m_localPlayer;
			int num = defaultAmount;
			if (heldKey != null && (Object)(object)recipe != (Object)null && (Object)(object)localPlayer != (Object)null)
			{
				num = Mathf.Max(GetCraftCount(heldKey, recipe, localPlayer), 1);
			}
			gui.m_multiCraftAmount = num;
			gui.m_multiCraftDuration = defaultDuration * (float)num / (float)Mathf.Max(defaultAmount, 1);
			return heldKey;
		}

		public static void StartCraft(InventoryGui gui, CraftKey craftKey)
		{
			dropExtra = craftKey != null && craftKey.Mode == CraftMode.All && ((RecipeDataPair)(ref gui.m_selectedRecipe)).ItemData == null;
		}

		public static bool CanAddItem(Inventory inventory, GameObject prefab, int stack)
		{
			if (!dropExtra)
			{
				return inventory.CanAddItem(prefab, stack);
			}
			return true;
		}

		public static ItemData AddItem(Inventory inventory, string name, int stack, int quality, int variant, long crafterID, string crafterName, Vector2i position, bool cheated, bool pickedUp, bool dropIfFullInv)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = (dropExtra ? ObjectDB.instance.GetItemPrefab(name) : null);
			ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null);
			if ((Object)(object)val2 == (Object)null)
			{
				return inventory.AddItem(name, stack, quality, variant, crafterID, crafterName, position, cheated, pickedUp, dropIfFullInv);
			}
			int num = Mathf.Min(stack, GetFreeSpace(inventory, val2.m_itemData.m_shared, quality));
			ItemData val3 = null;
			if (num > 0)
			{
				val3 = inventory.AddItem(name, num, quality, variant, crafterID, crafterName, position, cheated, pickedUp, dropIfFullInv);
			}
			if (stack > num)
			{
				ItemData val4 = Drop(val2, stack - num, quality, variant, crafterID, crafterName, cheated);
				if (val3 == null)
				{
					val3 = val4;
				}
			}
			return val3;
		}

		public static void RaiseSkill(Character character, SkillType skill, float value)
		{
			//IL_0010: 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_003d: Unknown result type (might be due to invalid IL or missing references)
			if (!CraftConfig.FullExperience || value <= 1f)
			{
				character.RaiseSkill(skill, value);
				return;
			}
			int num = Mathf.FloorToInt(value);
			for (int i = 0; i < num; i++)
			{
				character.RaiseSkill(skill, 1f);
			}
			if (value > (float)num)
			{
				character.RaiseSkill(skill, value - (float)num);
			}
		}

		private static int GetFreeSpace(Inventory inventory, SharedData shared, int quality)
		{
			int num = inventory.GetEmptySlots() * shared.m_maxStackSize;
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem.m_shared.m_name == shared.m_name && allItem.m_quality == quality && allItem.m_worldLevel == Game.m_worldLevel)
				{
					num += Mathf.Max(shared.m_maxStackSize - allItem.m_stack, 0);
				}
			}
			return num;
		}

		private static ItemData Drop(ItemDrop itemDrop, int amount, int quality, int variant, long crafterID, string crafterName, bool cheated)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: 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_00af: 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)
			Transform transform = ((Component)Player.m_localPlayer).transform;
			Vector3 val = transform.position + transform.forward + Vector3.up;
			int num = Mathf.Max(itemDrop.m_itemData.m_shared.m_maxStackSize, 1);
			ItemData val2 = null;
			while (amount > 0)
			{
				val2 = itemDrop.m_itemData.Clone();
				val2.m_dropPrefab = ((Component)itemDrop).gameObject;
				val2.m_stack = Mathf.Min(amount, num);
				val2.m_quality = quality;
				val2.m_variant = variant;
				val2.m_durability = val2.GetMaxDurability();
				val2.m_crafterID = crafterID;
				val2.m_crafterName = crafterName;
				val2.m_worldLevel = (byte)Game.m_worldLevel;
				val2.m_cheated = cheated;
				ItemDrop.DropItem(val2, val2.m_stack, val, Quaternion.identity);
				amount -= val2.m_stack;
			}
			return val2;
		}

		public static IEnumerable<CodeInstruction> ReplaceCalls(IEnumerable<CodeInstruction> instructions, MethodInfo original, MethodInfo replacement)
		{
			foreach (CodeInstruction instruction in instructions)
			{
				if (CodeInstructionExtensions.Calls(instruction, original))
				{
					instruction.opcode = OpCodes.Call;
					instruction.operand = replacement;
				}
				yield return instruction;
			}
		}

		public static IEnumerable<CodeInstruction> ReplaceButtonAndSpace(IEnumerable<CodeInstruction> instructions)
		{
			instructions = ReplaceCalls(instructions, AccessTools.Method(typeof(ZInput), "GetButton", (Type[])null, (Type[])null), AccessTools.Method(typeof(Craft), "GetButton", (Type[])null, (Type[])null));
			return ReplaceCalls(instructions, AccessTools.Method(typeof(Inventory), "CanAddItem", new Type[2]
			{
				typeof(GameObject),
				typeof(int)
			}, (Type[])null), AccessTools.Method(typeof(Craft), "CanAddItem", (Type[])null, (Type[])null));
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "UpdateRecipe")]
	public static class UpdateRecipePatch
	{
		private static void Prefix(InventoryGui __instance)
		{
			if (__instance.m_craftTimer < 0f)
			{
				Craft.ApplyAmount(__instance);
			}
		}

		private static void Postfix(InventoryGui __instance, Player player)
		{
			Craft.ContinueBatch(__instance, player);
			Craft.ShowBatchProgress(__instance);
			CraftHint.Update(__instance, player);
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return Craft.ReplaceCalls(instructions, AccessTools.Method(typeof(ZInput), "GetButton", (Type[])null, (Type[])null), AccessTools.Method(typeof(Craft), "GetButton", (Type[])null, (Type[])null));
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "OnCraftPressed")]
	public static class OnCraftPressedPatch
	{
		private static void Prefix(InventoryGui __instance)
		{
			Craft.StartCraft(__instance, Craft.ApplyAmount(__instance));
		}

		private static void Postfix(InventoryGui __instance)
		{
			Craft.QueueBatch(__instance);
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return Craft.ReplaceButtonAndSpace(instructions);
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
	public static class DoCraftingPatch
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			MethodInfo original = AccessTools.Method(typeof(Inventory), "AddItem", new Type[10]
			{
				typeof(string),
				typeof(int),
				typeof(int),
				typeof(int),
				typeof(long),
				typeof(string),
				typeof(Vector2i),
				typeof(bool),
				typeof(bool),
				typeof(bool)
			}, (Type[])null);
			instructions = Craft.ReplaceButtonAndSpace(instructions);
			instructions = Craft.ReplaceCalls(instructions, original, AccessTools.Method(typeof(Craft), "AddItem", (Type[])null, (Type[])null));
			return Craft.ReplaceCalls(instructions, AccessTools.Method(typeof(Character), "RaiseSkill", (Type[])null, (Type[])null), AccessTools.Method(typeof(Craft), "RaiseSkill", (Type[])null, (Type[])null));
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "OnCraftCancelPressed")]
	public static class OnCraftCancelPressedPatch
	{
		private static void Prefix()
		{
			Craft.StopBatch();
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Hide")]
	public static class HidePatch
	{
		private static void Prefix()
		{
			Craft.StopBatch();
		}
	}
	[BepInPlugin("kriona.CraftMore", "Craft More", "1.0.1")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "kriona.CraftMore";

		public const string PluginName = "Craft More";

		public const string PluginVersion = "1.0.1";

		internal static ConfigWatcher Watcher;

		private Harmony harmony;

		private void Awake()
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected O, but got Unknown
			CraftConfig.Init(((BaseUnityPlugin)this).Config);
			CraftConfig.Load();
			Watcher = new ConfigWatcher(CraftConfig.FilePath, CraftConfig.Load, ((BaseUnityPlugin)this).Logger, "Craft More");
			harmony = new Harmony("kriona.CraftMore");
			harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Craft More 1.0.1 loaded");
		}

		private void Update()
		{
			Watcher.Update();
		}

		private void OnDestroy()
		{
			Watcher?.Dispose();
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}
namespace Kriona.Shared
{
	internal class ConfigMirror
	{
		private readonly ConfigFile config;

		private readonly Action changed;

		private bool syncing;

		public ConfigMirror(ConfigFile config, Action changed)
		{
			this.config = config;
			this.changed = changed;
			config.SaveOnConfigSet = false;
			config.SettingChanged += OnSettingChanged;
		}

		public ConfigEntry<T> Bind<T>(string section, string key, T defaultValue, string description, AcceptableValueBase acceptableValues, int? order, string displayName)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes();
			configurationManagerAttributes.Order = order;
			configurationManagerAttributes.DispName = displayName;
			return config.Bind<T>(section, key, defaultValue, new ConfigDescription(description, acceptableValues, new object[1] { configurationManagerAttributes }));
		}

		public void Remove(ConfigEntryBase entry)
		{
			config.Remove(entry.Definition);
		}

		public void Sync(Action apply)
		{
			syncing = true;
			try
			{
				apply();
			}
			finally
			{
				syncing = false;
			}
		}

		public static void SetDisplayName(ConfigEntryBase entry, string displayName)
		{
			object[] tags = entry.Description.Tags;
			for (int i = 0; i < tags.Length; i++)
			{
				if (tags[i] is ConfigurationManagerAttributes configurationManagerAttributes)
				{
					configurationManagerAttributes.DispName = displayName;
				}
			}
		}

		private void OnSettingChanged(object sender, SettingChangedEventArgs args)
		{
			if (!syncing)
			{
				changed();
			}
		}
	}
	internal sealed class ConfigurationManagerAttributes
	{
		public int? Order;

		public string DispName;
	}
	internal class ConfigWatcher : IDisposable
	{
		private const float ReloadDelay = 0.5f;

		private readonly string path;

		private readonly Action reload;

		private readonly ManualLogSource log;

		private readonly string pluginName;

		private readonly FileSystemWatcher watcher;

		private string knownContents;

		private volatile bool fileChanged;

		private float reloadTime = -1f;

		public ConfigWatcher(string path, Action reload, ManualLogSource log, string pluginName)
		{
			this.path = path;
			this.reload = reload;
			this.log = log;
			this.pluginName = pluginName;
			Remember();
			try
			{
				watcher = new FileSystemWatcher(Path.GetDirectoryName(path), Path.GetFileName(path));
				watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite;
				watcher.Changed += delegate
				{
					fileChanged = true;
				};
				watcher.Created += delegate
				{
					fileChanged = true;
				};
				watcher.Renamed += delegate
				{
					fileChanged = true;
				};
				watcher.EnableRaisingEvents = true;
			}
			catch (Exception ex)
			{
				watcher = null;
				log.LogWarning((object)("Couldn't watch " + path + " for changes, so changes made to it while Valheim is running aren't read: " + ex.Message));
			}
		}

		public ConfigWatcher(ConfigFile config, ManualLogSource log, string pluginName)
			: this(config.ConfigFilePath, delegate
			{
				ReloadConfig(config);
			}, log, pluginName)
		{
		}

		private static void ReloadConfig(ConfigFile config)
		{
			bool saveOnConfigSet = config.SaveOnConfigSet;
			config.SaveOnConfigSet = false;
			try
			{
				config.Reload();
			}
			finally
			{
				config.SaveOnConfigSet = saveOnConfigSet;
			}
			if (saveOnConfigSet && !MatchesFile(config))
			{
				config.Save();
			}
		}

		private static bool MatchesFile(ConfigFile config)
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Expected O, but got Unknown
			Dictionary<ConfigDefinition, string> dictionary = new Dictionary<ConfigDefinition, string>();
			string text = string.Empty;
			string[] array = File.ReadAllLines(config.ConfigFilePath);
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.StartsWith("#"))
				{
					continue;
				}
				if (text2.StartsWith("[") && text2.EndsWith("]"))
				{
					text = text2.Substring(1, text2.Length - 2);
					continue;
				}
				string[] array2 = text2.Split(new char[1] { '=' }, 2);
				if (array2.Length == 2)
				{
					dictionary[new ConfigDefinition(text, array2[0].Trim())] = array2[1].Trim();
				}
			}
			foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in config)
			{
				if (!dictionary.TryGetValue(item.Key, out var value))
				{
					return false;
				}
				try
				{
					if (!object.Equals(TomlTypeConverter.ConvertToValue(value, item.Value.SettingType), item.Value.BoxedValue))
					{
						return false;
					}
				}
				catch (Exception)
				{
					return false;
				}
			}
			return true;
		}

		public void Update()
		{
			if (fileChanged)
			{
				fileChanged = false;
				reloadTime = Time.realtimeSinceStartup + 0.5f;
			}
			if (reloadTime < 0f || Time.realtimeSinceStartup < reloadTime)
			{
				return;
			}
			reloadTime = -1f;
			string text;
			try
			{
				text = File.ReadAllText(path);
			}
			catch (FileNotFoundException)
			{
				return;
			}
			catch (IOException)
			{
				reloadTime = Time.realtimeSinceStartup + 0.5f;
				return;
			}
			if (text == knownContents)
			{
				return;
			}
			reload();
			Remember();
			if (pluginName != null)
			{
				log.LogInfo((object)("Reloaded " + Path.GetFileName(path)));
				MessageHud instance = MessageHud.instance;
				if (instance != null)
				{
					instance.ShowMessage((MessageType)1, pluginName + ": config reloaded", 0, (Sprite)null, false, true);
				}
			}
		}

		public void Remember()
		{
			try
			{
				knownContents = File.ReadAllText(path);
			}
			catch (IOException)
			{
				knownContents = null;
			}
		}

		public void Dispose()
		{
			watcher?.Dispose();
		}
	}
	internal class StateEntries
	{
		public const string Inherit = "inherit";

		private static readonly AcceptableValueList<string> values = new AcceptableValueList<string>(new string[3] { "inherit", "true", "false" });

		private readonly ConfigMirror mirror;

		private readonly string help;

		private readonly Dictionary<string, ConfigEntry<string>> entries = new Dictionary<string, ConfigEntry<string>>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<string> set = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		public StateEntries(ConfigMirror mirror, string help)
		{
			this.mirror = mirror;
			this.help = help;
		}

		public void Set(string section, string key, string displayName, bool? state)
		{
			string entryName = GetEntryName(key, displayName);
			if (entries.TryGetValue(key, out var value) && ((ConfigEntryBase)value).Definition.Section != section)
			{
				mirror.Remove((ConfigEntryBase)(object)value);
				entries.Remove(key);
				value = null;
			}
			if (value == null)
			{
				try
				{
					value = mirror.Bind(section, key, "inherit", "Prefab " + key + " - " + help, (AcceptableValueBase)(object)values, null, entryName);
				}
				catch (ArgumentException)
				{
					return;
				}
				entries[key] = value;
			}
			else
			{
				ConfigMirror.SetDisplayName((ConfigEntryBase)(object)value, entryName);
			}
			value.Value = Format(state);
			set.Add(key);
		}

		public void RemoveUnset()
		{
			List<string> list = new List<string>();
			foreach (KeyValuePair<string, ConfigEntry<string>> entry in entries)
			{
				if (!set.Contains(entry.Key))
				{
					mirror.Remove((ConfigEntryBase)(object)entry.Value);
					list.Add(entry.Key);
				}
			}
			foreach (string item in list)
			{
				entries.Remove(item);
			}
			set.Clear();
		}

		public bool TryGetState(string key, out bool? state)
		{
			state = null;
			if (!entries.TryGetValue(key, out var value))
			{
				return false;
			}
			if (bool.TryParse(value.Value, out var result))
			{
				state = result;
			}
			return true;
		}

		public static string Format(bool? state)
		{
			if (!state.HasValue)
			{
				return "inherit";
			}
			if (!state.Value)
			{
				return "false";
			}
			return "true";
		}

		private static string GetEntryName(string key, string displayName)
		{
			string text = displayName.Trim();
			if (text.Length == 0 || text.Equals(key, StringComparison.OrdinalIgnoreCase))
			{
				return key;
			}
			return text + " (" + key + ")";
		}
	}
}