Decompiled source of Hearthkeeper v0.1.11

Hearthkeeper.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("Hearthkeeper")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.10.0")]
[assembly: AssemblyInformationalVersion("0.1.10")]
[assembly: AssemblyProduct("Hearthkeeper")]
[assembly: AssemblyTitle("Hearthkeeper")]
[assembly: AssemblyVersion("0.1.10.0")]
[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 Hearthkeeper
{
	internal static class AutoRefuelService
	{
		private const string DeviceDisabledKey = "MaddCatter.Hearthkeeper.autoRefuelDisabled";

		private static readonly List<Fireplace> Fireplaces = new List<Fireplace>();

		private static readonly List<Smelter> Smelters = new List<Smelter>();

		private static int _fireplaceCursor;

		private static int _smelterCursor;

		internal static void Register(Fireplace fireplace)
		{
			if ((Object)(object)fireplace != (Object)null && !Fireplaces.Contains(fireplace))
			{
				Fireplaces.Add(fireplace);
			}
		}

		internal static void Register(Smelter smelter)
		{
			if ((Object)(object)smelter != (Object)null && !Smelters.Contains(smelter))
			{
				Smelters.Add(smelter);
			}
		}

		internal static void Process(Player player)
		{
			if (!((Object)(object)player == (Object)null) && HearthkeeperPlugin.AutoRefuelEnabled.Value)
			{
				Cleanup();
				int value = HearthkeeperPlugin.AutoRefuelMaximumDevicesPerCycle.Value;
				if (HearthkeeperPlugin.AutoRefuelFires.Value)
				{
					ProcessRotating<Fireplace>(Fireplaces, ref _fireplaceCursor, value, (Action<Fireplace>)ProcessFireplace);
				}
				if (HearthkeeperPlugin.AutoRefuelProcessingFuel.Value || HearthkeeperPlugin.AutoRefuelProcessingInputs.Value)
				{
					ProcessRotating<Smelter>(Smelters, ref _smelterCursor, value, (Action<Smelter>)ProcessSmelter);
				}
			}
		}

		internal static bool ToggleHoveredDevice(Player player)
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			GameObject hoverObject = ((Humanoid)player).GetHoverObject();
			if ((Object)(object)hoverObject == (Object)null)
			{
				return false;
			}
			Component componentInParent = (Component)(object)hoverObject.GetComponentInParent<Fireplace>();
			if ((Object)(object)componentInParent == (Object)null)
			{
				componentInParent = (Component)(object)hoverObject.GetComponentInParent<Smelter>();
			}
			if ((Object)(object)componentInParent == (Object)null)
			{
				return false;
			}
			ZNetView view = GetView(componentInParent);
			if ((Object)(object)view == (Object)null || !view.IsValid() || view.GetZDO() == null)
			{
				return false;
			}
			if (!PrivateArea.CheckAccess(componentInParent.transform.position, 0f, true, false))
			{
				return true;
			}
			if (!view.IsOwner())
			{
				view.ClaimOwnership();
			}
			if (!view.IsOwner())
			{
				((Character)player).Message((MessageType)2, "Hearthkeeper could not change this device", 0, (Sprite)null);
				return true;
			}
			bool flag = view.GetZDO().GetBool("MaddCatter.Hearthkeeper.autoRefuelDisabled", false);
			view.GetZDO().Set("MaddCatter.Hearthkeeper.autoRefuelDisabled", !flag);
			((Character)player).Message((MessageType)2, (!flag) ? "Hearthkeeper auto-refuel: DISABLED for this device" : "Hearthkeeper auto-refuel: ENABLED for this device", 0, (Sprite)null);
			return true;
		}

		private static void ProcessFireplace(Fireplace fireplace)
		{
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)fireplace == (Object)null || fireplace.m_infiniteFuel || !fireplace.m_canRefill || (Object)(object)fireplace.m_fuelItem == (Object)null || fireplace.m_maxFuel <= 0f)
			{
				return;
			}
			ZNetView view = GetView((Component)(object)fireplace);
			if (!CanAutomate((Component)(object)fireplace, view))
			{
				return;
			}
			float num = view.GetZDO().GetFloat(ZDOVars.s_fuel, 0f);
			float num2 = PercentOf(fireplace.m_maxFuel, HearthkeeperPlugin.AutoRefuelFireThresholdPercent.Value);
			if (num > num2)
			{
				return;
			}
			float num3 = PercentOf(fireplace.m_maxFuel, HearthkeeperPlugin.AutoRefuelFireTargetPercent.Value);
			num3 = Mathf.Clamp(Mathf.Max(num3, num2), 0f, fireplace.m_maxFuel);
			int num4 = Math.Min(HearthkeeperPlugin.AutoRefuelMaximumItemsPerDevice.Value, Mathf.CeilToInt(num3 - num));
			if (num4 > 0)
			{
				string name = fireplace.m_fuelItem.m_itemData.m_shared.m_name;
				int num5 = TakeItems(((Component)fireplace).transform.position, name, num4, null);
				if (num5 > 0)
				{
					fireplace.AddFuel((float)num5);
				}
			}
		}

		private static void ProcessSmelter(Smelter smelter)
		{
			if ((Object)(object)smelter == (Object)null)
			{
				return;
			}
			ZNetView view = GetView((Component)(object)smelter);
			if (CanAutomate((Component)(object)smelter, view))
			{
				int num = HearthkeeperPlugin.AutoRefuelMaximumItemsPerDevice.Value;
				if (HearthkeeperPlugin.AutoRefuelProcessingFuel.Value && num > 0)
				{
					num -= RefillProcessingFuel(smelter, view, num);
				}
				if (HearthkeeperPlugin.AutoRefuelProcessingInputs.Value && num > 0)
				{
					RefillProcessingInputs(smelter, view, num);
				}
			}
		}

		private static int RefillProcessingFuel(Smelter smelter, ZNetView view, int budget)
		{
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)smelter.m_fuelItem == (Object)null || smelter.m_maxFuel <= 0)
			{
				return 0;
			}
			float num = view.GetZDO().GetFloat(ZDOVars.s_fuel, 0f);
			float num2 = PercentOf(smelter.m_maxFuel, HearthkeeperPlugin.AutoRefuelProcessingFuelThresholdPercent.Value);
			if (num > num2)
			{
				return 0;
			}
			float num3 = PercentOf(smelter.m_maxFuel, HearthkeeperPlugin.AutoRefuelProcessingFuelTargetPercent.Value);
			num3 = Mathf.Clamp(Mathf.Max(num3, num2), 0f, (float)smelter.m_maxFuel);
			int num4 = Math.Min(budget, Mathf.CeilToInt(num3 - num));
			if (num4 <= 0)
			{
				return 0;
			}
			string name = smelter.m_fuelItem.m_itemData.m_shared.m_name;
			int num5 = TakeItems(((Component)smelter).transform.position, name, num4, null);
			for (int i = 0; i < num5; i++)
			{
				view.InvokeRPC("RPC_AddFuel", Array.Empty<object>());
			}
			return num5;
		}

		private static int RefillProcessingInputs(Smelter smelter, ZNetView view, int budget)
		{
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			if (smelter.m_conversion == null || smelter.m_maxOre <= 0)
			{
				return 0;
			}
			int num = view.GetZDO().GetInt(ZDOVars.s_queued, 0);
			float num2 = PercentOf(smelter.m_maxOre, HearthkeeperPlugin.AutoRefuelProcessingInputThresholdPercent.Value);
			if ((float)num > num2)
			{
				return 0;
			}
			int num3 = Mathf.Clamp(Mathf.CeilToInt(Mathf.Max(PercentOf(smelter.m_maxOre, HearthkeeperPlugin.AutoRefuelProcessingInputTargetPercent.Value), num2)), 0, smelter.m_maxOre);
			int num4 = Math.Min(budget, num3 - num);
			if (num4 <= 0)
			{
				return 0;
			}
			for (int i = 0; i < smelter.m_conversion.Count; i++)
			{
				ItemConversion val = smelter.m_conversion[i];
				if (val == null || (Object)(object)val.m_from == (Object)null || val.m_from.m_itemData == null)
				{
					continue;
				}
				string name = val.m_from.m_itemData.m_shared.m_name;
				List<ItemData> list = new List<ItemData>();
				int num5 = TakeItems(((Component)smelter).transform.position, name, num4, list);
				if (num5 > 0)
				{
					string name2 = ((Object)((Component)val.m_from).gameObject).name;
					for (int j = 0; j < list.Count; j++)
					{
						view.InvokeRPC("RPC_AddOre", new object[2] { name2, false });
					}
					return num5;
				}
			}
			return 0;
		}

		private static int TakeItems(Vector3 point, string sharedName, int requested, List<ItemData> taken)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (requested <= 0 || string.IsNullOrEmpty(sharedName))
			{
				return 0;
			}
			List<Container> list = ContainerRegistry.Nearby(point, HearthkeeperPlugin.AutoRefuelRange.Value, requireAccept: false, allowInUse: false);
			int num = requested;
			for (int i = 0; i < list.Count; i++)
			{
				if (num <= 0)
				{
					break;
				}
				Container container = list[i];
				Inventory val = ContainerRegistry.SafeInventory(container);
				if (val == null)
				{
					continue;
				}
				List<ItemData> list2 = new List<ItemData>(val.GetAllItems());
				for (int j = 0; j < list2.Count; j++)
				{
					if (num <= 0)
					{
						break;
					}
					ItemData val2 = list2[j];
					if (val2 == null || val2.m_shared == null || val2.m_shared.m_name != sharedName)
					{
						continue;
					}
					int val3 = InventoryTransfers.AvailableInContainer(container, sharedName, val2.m_quality);
					int num2 = Math.Min(num, Math.Min(val2.m_stack, val3));
					if (num2 <= 0)
					{
						continue;
					}
					ItemData val4 = val2.Clone();
					val4.m_stack = 1;
					if (!val.RemoveItem(val2, num2))
					{
						continue;
					}
					if (taken != null)
					{
						for (int k = 0; k < num2; k++)
						{
							taken.Add(val4.Clone());
						}
					}
					num -= num2;
				}
			}
			return requested - num;
		}

		private static bool CanAutomate(Component device, ZNetView view)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)device == (Object)null || (Object)(object)view == (Object)null || !view.IsValid() || !view.IsOwner() || view.GetZDO() == null)
			{
				return false;
			}
			if ((Object)(object)device.GetComponentInParent<Piece>() == (Object)null)
			{
				return false;
			}
			if (view.GetZDO().GetBool("MaddCatter.Hearthkeeper.autoRefuelDisabled", false))
			{
				return false;
			}
			return PrivateArea.CheckAccess(device.transform.position, 0f, false, true);
		}

		private static ZNetView GetView(Component component)
		{
			if ((Object)(object)component == (Object)null)
			{
				return null;
			}
			ZNetView component2 = component.GetComponent<ZNetView>();
			if (!((Object)(object)component2 != (Object)null))
			{
				return component.GetComponentInParent<ZNetView>();
			}
			return component2;
		}

		private static float PercentOf(float maximum, float percent)
		{
			return maximum * Mathf.Clamp(percent, 0f, 100f) / 100f;
		}

		private static void ProcessRotating<T>(List<T> devices, ref int cursor, int budget, Action<T> process) where T : Object
		{
			if (devices.Count != 0 && budget > 0)
			{
				cursor = Mathf.Clamp(cursor, 0, devices.Count - 1);
				int num = Math.Min(budget, devices.Count);
				for (int i = 0; i < num; i++)
				{
					process(devices[(cursor + i) % devices.Count]);
				}
				cursor = (cursor + num) % devices.Count;
			}
		}

		private static void Cleanup()
		{
			for (int num = Fireplaces.Count - 1; num >= 0; num--)
			{
				if ((Object)(object)Fireplaces[num] == (Object)null)
				{
					Fireplaces.RemoveAt(num);
				}
			}
			for (int num2 = Smelters.Count - 1; num2 >= 0; num2--)
			{
				if ((Object)(object)Smelters[num2] == (Object)null)
				{
					Smelters.RemoveAt(num2);
				}
			}
			if (Fireplaces.Count == 0)
			{
				_fireplaceCursor = 0;
			}
			if (Smelters.Count == 0)
			{
				_smelterCursor = 0;
			}
		}
	}
	internal static class ChestGlowService
	{
		private sealed class GlowState
		{
			internal Container Container;

			internal GameObject VisualRoot;

			internal GameObject LightObject;

			internal Light Light;

			internal float EndsAt;

			internal bool EmissionApplied;
		}

		private static readonly Dictionary<Container, GlowState> Active = new Dictionary<Container, GlowState>();

		private static readonly List<Container> Finished = new List<Container>();

		private static readonly int EmissionColor = Shader.PropertyToID("_EmissionColor");

		private static readonly MethodInfo SetValueMethod = FindSetValueMethod();

		internal static void Pulse(Container container)
		{
			if (!HearthkeeperPlugin.ReceivingGlowEnabled.Value || (Object)(object)container == (Object)null)
			{
				return;
			}
			if (!Active.TryGetValue(container, out var value) || value == null)
			{
				value = Create(container);
				if (value == null)
				{
					return;
				}
				Active[container] = value;
			}
			value.EndsAt = Time.unscaledTime + HearthkeeperPlugin.ReceivingGlowDuration.Value;
		}

		internal static void Update()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			if (!HearthkeeperPlugin.ReceivingGlowEnabled.Value)
			{
				Clear();
			}
			else
			{
				if (Active.Count == 0)
				{
					return;
				}
				Finished.Clear();
				Color color = ParseColor(HearthkeeperPlugin.ReceivingGlowColor.Value);
				float num = Mathf.Max(0.25f, HearthkeeperPlugin.ReceivingGlowDuration.Value);
				foreach (KeyValuePair<Container, GlowState> item in Active)
				{
					GlowState value = item.Value;
					if ((Object)(object)item.Key == (Object)null || value == null || (Object)(object)value.Container == (Object)null || (Object)(object)value.VisualRoot == (Object)null || Time.unscaledTime >= value.EndsAt)
					{
						Finish(value);
						Finished.Add(item.Key);
						continue;
					}
					float num2 = Mathf.Clamp01((value.EndsAt - Time.unscaledTime) / Mathf.Min(0.45f, num));
					float num3 = 0.5f + 0.5f * Mathf.Sin(Time.unscaledTime * 11f);
					float num4 = (0.35f + num3 * 0.65f) * num2;
					float value2 = HearthkeeperPlugin.ReceivingGlowIntensity.Value;
					if ((Object)(object)value.Light != (Object)null)
					{
						value.Light.color = color;
						value.Light.range = HearthkeeperPlugin.ReceivingGlowRadius.Value;
						value.Light.intensity = value2 * num4;
					}
				}
				for (int i = 0; i < Finished.Count; i++)
				{
					Active.Remove(Finished[i]);
				}
			}
		}

		private static bool ApplyEmission(GameObject root, Color color)
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			if (SetValueMethod == null)
			{
				return false;
			}
			try
			{
				if (SetValueMethod.GetParameters().Length == 4)
				{
					SetValueMethod.Invoke(MaterialMan.instance, new object[4] { root, EmissionColor, color, true });
				}
				else
				{
					SetValueMethod.Invoke(MaterialMan.instance, new object[3] { root, EmissionColor, color });
				}
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static MethodInfo FindSetValueMethod()
		{
			MethodInfo[] methods = typeof(MaterialMan).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			for (int i = 0; i < methods.Length; i++)
			{
				if (!(methods[i].Name != "SetValue"))
				{
					int num = methods[i].GetParameters().Length;
					if (num == 3 || num == 4)
					{
						return methods[i];
					}
				}
			}
			return null;
		}

		internal static void Clear()
		{
			foreach (GlowState value in Active.Values)
			{
				Finish(value);
			}
			Active.Clear();
			Finished.Clear();
		}

		private static GlowState Create(Container container)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			GameObject gameObject = ((Component)container).gameObject;
			ZNetView view = ContainerRegistry.GetView(container);
			if ((Object)(object)view != (Object)null)
			{
				gameObject = ((Component)view).gameObject;
			}
			GameObject val = new GameObject("Hearthkeeper_ReceivingGlow");
			val.transform.SetParent(((Component)container).transform, false);
			val.transform.localPosition = Vector3.up * 0.75f;
			Renderer[] componentsInChildren = gameObject.GetComponentsInChildren<Renderer>(true);
			if (componentsInChildren.Length != 0)
			{
				Bounds bounds = componentsInChildren[0].bounds;
				for (int i = 1; i < componentsInChildren.Length; i++)
				{
					((Bounds)(ref bounds)).Encapsulate(componentsInChildren[i].bounds);
				}
				val.transform.position = ((Bounds)(ref bounds)).center + Vector3.up * 0.2f;
			}
			Light val2 = val.AddComponent<Light>();
			val2.type = (LightType)2;
			val2.shadows = (LightShadows)0;
			val2.renderMode = (LightRenderMode)0;
			val2.intensity = 0f;
			return new GlowState
			{
				Container = container,
				VisualRoot = gameObject,
				LightObject = val,
				Light = val2,
				EndsAt = Time.unscaledTime + HearthkeeperPlugin.ReceivingGlowDuration.Value
			};
		}

		private static void Finish(GlowState state)
		{
			if (state != null && (Object)(object)state.LightObject != (Object)null)
			{
				Object.Destroy((Object)(object)state.LightObject);
			}
		}

		private static Color ParseColor(string value)
		{
			//IL_0039: 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)
			Color result = default(Color);
			if (!string.IsNullOrEmpty(value) && ColorUtility.TryParseHtmlString(value.Trim(), ref result))
			{
				result.a = 1f;
				return result;
			}
			return new Color(1f, 0.82f, 0.48f, 1f);
		}
	}
	internal static class ContainerNameService
	{
		private sealed class NameReceiver : TextReceiver
		{
			private readonly Container _container;

			internal NameReceiver(Container container)
			{
				_container = container;
			}

			public string GetText()
			{
				return GetCustomName(_container);
			}

			public void SetText(string text)
			{
				SetCustomName(_container, text);
			}
		}

		private const string NameKey = "MaddCatter.Hearthkeeper.name";

		private const int CharacterLimit = 40;

		private static readonly FieldInfo ContainerNameField = typeof(InventoryGui).GetField("m_containerName");

		internal static string GetCustomName(Container container)
		{
			ZNetView view = ContainerRegistry.GetView(container);
			ZDO val = (((Object)(object)view != (Object)null && view.IsValid()) ? view.GetZDO() : null);
			if (val != null)
			{
				return Sanitize(val.GetString("MaddCatter.Hearthkeeper.name", string.Empty));
			}
			return string.Empty;
		}

		internal static string DisplayName(Container container)
		{
			string customName = GetCustomName(container);
			if (!string.IsNullOrEmpty(customName))
			{
				return customName;
			}
			if ((Object)(object)container == (Object)null)
			{
				return "Chest";
			}
			if (Localization.instance == null)
			{
				return container.m_name;
			}
			return Localization.instance.Localize(container.m_name);
		}

		internal static void RequestRename(Container container)
		{
			if (!((Object)(object)container == (Object)null) && !((Object)(object)TextInput.instance == (Object)null))
			{
				TextInput.instance.RequestText((TextReceiver)(object)new NameReceiver(container), "Chest name (blank restores default)", 40);
			}
		}

		internal static bool SetCustomName(Container container, string value)
		{
			if ((Object)(object)container == (Object)null)
			{
				return false;
			}
			ZNetView view = ContainerRegistry.GetView(container);
			if ((Object)(object)view == (Object)null || !view.IsValid())
			{
				return false;
			}
			if (!view.IsOwner())
			{
				view.ClaimOwnership();
			}
			if (!view.IsOwner())
			{
				return false;
			}
			ZDO zDO = view.GetZDO();
			if (zDO == null)
			{
				return false;
			}
			zDO.Set("MaddCatter.Hearthkeeper.name", Sanitize(value));
			ApplyOpenContainerTitle(container);
			return true;
		}

		internal static void ApplyOpenContainerTitle(Container container)
		{
			InventoryGui instance = InventoryGui.instance;
			if (!((Object)(object)container == (Object)null) && !((Object)(object)instance == (Object)null) && !(ContainerNameField == null))
			{
				object value = ContainerNameField.GetValue(instance);
				PropertyInfo propertyInfo = value?.GetType().GetProperty("text");
				if (propertyInfo != null && propertyInfo.CanWrite)
				{
					propertyInfo.SetValue(value, DisplayName(container), null);
				}
			}
		}

		internal static void ApplyHoverName(Container container, ref string result)
		{
			string customName = GetCustomName(container);
			if (!string.IsNullOrEmpty(customName))
			{
				result = customName;
			}
		}

		internal static void ApplyHoverText(Container container, ref string result)
		{
			string customName = GetCustomName(container);
			if (!string.IsNullOrEmpty(customName) && !string.IsNullOrEmpty(result))
			{
				string text = (((Object)(object)container != (Object)null && Localization.instance != null) ? Localization.instance.Localize(container.m_name) : (((Object)(object)container != (Object)null) ? container.m_name : string.Empty));
				if (!string.IsNullOrEmpty(text) && result.StartsWith(text, StringComparison.Ordinal))
				{
					result = customName + result.Substring(text.Length);
					return;
				}
				int num = result.IndexOf('\n');
				result = ((num >= 0) ? (customName + result.Substring(num)) : customName);
			}
		}

		private static string Sanitize(string value)
		{
			if (string.IsNullOrEmpty(value))
			{
				return string.Empty;
			}
			StringBuilder stringBuilder = new StringBuilder(Math.Min(value.Length, 40));
			bool flag = false;
			for (int i = 0; i < value.Length; i++)
			{
				if (stringBuilder.Length >= 40)
				{
					break;
				}
				char c = value[i];
				switch (c)
				{
				case '<':
					flag = true;
					continue;
				case '>':
					flag = false;
					continue;
				}
				if (!flag && !char.IsControl(c))
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.ToString().Trim();
		}
	}
	internal static class ContainerRegistry
	{
		private const string KeyReserve = "MaddCatter.Hearthkeeper.reserve";

		private const string KeyCustom = "MaddCatter.Hearthkeeper.custom";

		private const string KeyManual = "MaddCatter.Hearthkeeper.manual";

		private const string KeyAccept = "MaddCatter.Hearthkeeper.accept";

		private const string KeyCraft = "MaddCatter.Hearthkeeper.craft";

		private const string KeyFeed = "MaddCatter.Hearthkeeper.feed";

		private static readonly List<Container> Containers = new List<Container>();

		private static readonly Dictionary<Inventory, Container> InventoryOwners = new Dictionary<Inventory, Container>();

		private static readonly MethodInfo CheckAccessMethod = AccessTools.Method(typeof(Container), "CheckAccess", (Type[])null, (Type[])null);

		internal static void Register(Container container)
		{
			if (!((Object)(object)container == (Object)null) && !Containers.Contains(container))
			{
				Containers.Add(container);
				Inventory val = SafeInventory(container);
				if (val != null)
				{
					InventoryOwners[val] = container;
				}
			}
		}

		internal static void Unregister(Container container)
		{
			if (!((Object)(object)container == (Object)null))
			{
				Containers.Remove(container);
				Inventory val = SafeInventory(container);
				if (val != null)
				{
					InventoryOwners.Remove(val);
				}
			}
		}

		internal static Container OwnerOf(Inventory inventory)
		{
			if (inventory == null)
			{
				return null;
			}
			if (!InventoryOwners.TryGetValue(inventory, out var value) || !((Object)(object)value != (Object)null))
			{
				return null;
			}
			return value;
		}

		internal static List<Container> Nearby(Vector3 point, float range, bool requireAccept, bool allowInUse)
		{
			//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)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			Cleanup();
			float num = range * range;
			List<Container> list = new List<Container>();
			for (int i = 0; i < Containers.Count; i++)
			{
				Container val = Containers[i];
				if (IsUsable(val, allowInUse))
				{
					Vector3 val2 = ((Component)val).transform.position - point;
					if (!(((Vector3)(ref val2)).sqrMagnitude > num) && (!requireAccept || GetSettings(val).AcceptStorage))
					{
						list.Add(val);
					}
				}
			}
			list.Sort(delegate(Container a, Container b)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				//IL_0027: Unknown result type (might be due to invalid IL or missing references)
				//IL_002d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0032: Unknown result type (might be due to invalid IL or missing references)
				//IL_0037: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val3 = ((Component)a).transform.position - point;
				float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude;
				val3 = ((Component)b).transform.position - point;
				int num2 = sqrMagnitude.CompareTo(((Vector3)(ref val3)).sqrMagnitude);
				return (num2 == 0) ? string.CompareOrdinal(PrefabName(a), PrefabName(b)) : num2;
			});
			return list;
		}

		internal static List<Container> All()
		{
			Cleanup();
			return new List<Container>(Containers);
		}

		internal static bool IsUsable(Container container, bool allowInUse)
		{
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)container == (Object)null || !HearthkeeperPlugin.Enabled.Value)
			{
				return false;
			}
			if (!HearthkeeperPlugin.IsAllowedPrefab(PrefabName(container)))
			{
				return false;
			}
			if (!allowInUse && container.IsInUse())
			{
				return false;
			}
			if (SafeInventory(container) == null)
			{
				return false;
			}
			ZNetView view = GetView(container);
			if ((Object)(object)view == (Object)null || !view.IsValid() || !view.IsOwner())
			{
				return false;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return false;
			}
			try
			{
				if (CheckAccessMethod != null && !(bool)CheckAccessMethod.Invoke(container, new object[1] { localPlayer.GetPlayerID() }))
				{
					return false;
				}
			}
			catch
			{
				return false;
			}
			if (container.m_checkGuardStone && !PrivateArea.CheckAccess(((Component)container).transform.position, 0f, false, true))
			{
				return false;
			}
			return true;
		}

		internal static Inventory SafeInventory(Container container)
		{
			try
			{
				return ((Object)(object)container != (Object)null) ? container.GetInventory() : null;
			}
			catch
			{
				return null;
			}
		}

		internal static ZNetView GetView(Container container)
		{
			if ((Object)(object)container == (Object)null)
			{
				return null;
			}
			if ((Object)(object)container.m_rootObjectOverride != (Object)null)
			{
				return container.m_rootObjectOverride;
			}
			ZNetView component = ((Component)container).GetComponent<ZNetView>();
			if (!((Object)(object)component != (Object)null))
			{
				return ((Component)container).GetComponentInParent<ZNetView>();
			}
			return component;
		}

		internal static ContainerSettings GetSettings(Container container)
		{
			ContainerSettings containerSettings = new ContainerSettings
			{
				Reserve = HearthkeeperPlugin.DefaultReserve.Value,
				CustomReserve = HearthkeeperPlugin.DefaultCustomReserve.Value,
				ManualLock = HearthkeeperPlugin.DefaultManualLock.Value,
				AcceptStorage = HearthkeeperPlugin.DefaultAcceptStorage.Value,
				CraftingSupply = HearthkeeperPlugin.DefaultCraftingSupply.Value,
				LivestockFeed = HearthkeeperPlugin.DefaultLivestockFeed.Value
			};
			ZNetView view = GetView(container);
			ZDO val = (((Object)(object)view != (Object)null && view.IsValid()) ? view.GetZDO() : null);
			if (val == null)
			{
				return containerSettings;
			}
			containerSettings.Reserve = (ReserveMode)val.GetInt("MaddCatter.Hearthkeeper.reserve", (int)containerSettings.Reserve);
			containerSettings.CustomReserve = val.GetInt("MaddCatter.Hearthkeeper.custom", containerSettings.CustomReserve);
			containerSettings.ManualLock = val.GetBool("MaddCatter.Hearthkeeper.manual", containerSettings.ManualLock);
			containerSettings.AcceptStorage = val.GetBool("MaddCatter.Hearthkeeper.accept", containerSettings.AcceptStorage);
			containerSettings.CraftingSupply = val.GetBool("MaddCatter.Hearthkeeper.craft", containerSettings.CraftingSupply);
			containerSettings.LivestockFeed = val.GetBool("MaddCatter.Hearthkeeper.feed", containerSettings.LivestockFeed);
			return containerSettings;
		}

		internal static bool SaveSettings(Container container, ContainerSettings settings)
		{
			if ((Object)(object)container == (Object)null || settings == null)
			{
				return false;
			}
			ZNetView view = GetView(container);
			if ((Object)(object)view == (Object)null || !view.IsValid())
			{
				return false;
			}
			if (!view.IsOwner())
			{
				view.ClaimOwnership();
			}
			if (!view.IsOwner())
			{
				return false;
			}
			ZDO zDO = view.GetZDO();
			if (zDO == null)
			{
				return false;
			}
			zDO.Set("MaddCatter.Hearthkeeper.reserve", (int)settings.Reserve);
			zDO.Set("MaddCatter.Hearthkeeper.custom", Math.Max(0, settings.CustomReserve));
			zDO.Set("MaddCatter.Hearthkeeper.manual", settings.ManualLock);
			zDO.Set("MaddCatter.Hearthkeeper.accept", settings.AcceptStorage);
			zDO.Set("MaddCatter.Hearthkeeper.craft", settings.CraftingSupply);
			zDO.Set("MaddCatter.Hearthkeeper.feed", settings.LivestockFeed);
			return true;
		}

		internal static int ReserveFor(Container container, string sharedName)
		{
			Inventory val = SafeInventory(container);
			if (val == null)
			{
				return 0;
			}
			ItemData val2 = null;
			List<ItemData> allItems = val.GetAllItems();
			for (int i = 0; i < allItems.Count; i++)
			{
				if (allItems[i].m_shared.m_name == sharedName)
				{
					val2 = allItems[i];
					break;
				}
			}
			if (val2 != null)
			{
				return GetSettings(container).AmountFor(val2);
			}
			return 0;
		}

		internal static string PrefabName(Container container)
		{
			if ((Object)(object)container == (Object)null || (Object)(object)((Component)container).gameObject == (Object)null)
			{
				return string.Empty;
			}
			string text = ((Object)((Component)container).gameObject).name ?? string.Empty;
			if (!text.EndsWith("(Clone)", StringComparison.Ordinal))
			{
				return text;
			}
			return text.Substring(0, text.Length - "(Clone)".Length);
		}

		private static void Cleanup()
		{
			for (int num = Containers.Count - 1; num >= 0; num--)
			{
				if (!((Object)(object)Containers[num] != (Object)null))
				{
					Containers.RemoveAt(num);
				}
			}
			List<Inventory> list = new List<Inventory>();
			foreach (KeyValuePair<Inventory, Container> inventoryOwner in InventoryOwners)
			{
				if (inventoryOwner.Key == null || (Object)(object)inventoryOwner.Value == (Object)null)
				{
					list.Add(inventoryOwner.Key);
				}
			}
			for (int i = 0; i < list.Count; i++)
			{
				InventoryOwners.Remove(list[i]);
			}
		}
	}
	internal enum ReserveMode
	{
		Off,
		OneItem,
		OneStack,
		Custom
	}
	internal sealed class ContainerSettings
	{
		internal ReserveMode Reserve;

		internal int CustomReserve;

		internal bool ManualLock;

		internal bool AcceptStorage;

		internal bool CraftingSupply;

		internal bool LivestockFeed;

		internal int AmountFor(ItemData item)
		{
			switch (Reserve)
			{
			case ReserveMode.OneItem:
				return 1;
			case ReserveMode.OneStack:
				if (item == null || item.m_shared == null)
				{
					return 1;
				}
				return item.m_shared.m_maxStackSize;
			case ReserveMode.Custom:
				if (CustomReserve >= 0)
				{
					return CustomReserve;
				}
				return 0;
			default:
				return 0;
			}
		}
	}
	internal static class ContainerSizeService
	{
		private struct Size
		{
			internal readonly int Width;

			internal readonly int Height;

			internal Size(int width, int height)
			{
				Width = width;
				Height = height;
			}
		}

		private const string WidthKey = "MaddCatter.Hearthkeeper.width";

		private const string HeightKey = "MaddCatter.Hearthkeeper.height";

		internal static void ApplyBeforeAwake(Container container)
		{
			if ((Object)(object)container == (Object)null || !HearthkeeperPlugin.ChestSizingEnabled.Value)
			{
				return;
			}
			string text = ContainerRegistry.PrefabName(container);
			Size value;
			bool flag = ParseRules(HearthkeeperPlugin.ChestSizeRules.Value).TryGetValue(text, out value);
			if (flag || IsUnlistedChestPiece(container, text))
			{
				if (!flag)
				{
					value = new Size(HearthkeeperPlugin.MinimumChestColumns.Value, HearthkeeperPlugin.MinimumChestRows.Value);
				}
				int val = Math.Max(container.m_width, Math.Max(HearthkeeperPlugin.MinimumChestColumns.Value, value.Width));
				int val2 = Math.Max(container.m_height, Math.Max(HearthkeeperPlugin.MinimumChestRows.Value, value.Height));
				val = Math.Min(HearthkeeperPlugin.MaximumChestColumns.Value, val);
				val2 = Math.Min(HearthkeeperPlugin.MaximumChestRows.Value, val2);
				ZNetView val3 = ((Component)container).GetComponent<ZNetView>();
				if ((Object)(object)val3 == (Object)null)
				{
					val3 = ((Component)container).GetComponentInParent<ZNetView>();
				}
				ZDO val4 = (((Object)(object)val3 != (Object)null && val3.IsValid()) ? val3.GetZDO() : null);
				if (val4 != null)
				{
					val = Math.Max(val, val4.GetInt("MaddCatter.Hearthkeeper.width", 0));
					val2 = Math.Max(val2, val4.GetInt("MaddCatter.Hearthkeeper.height", 0));
				}
				container.m_width = val;
				container.m_height = val2;
			}
		}

		internal static void RememberAppliedSize(Container container)
		{
			if ((Object)(object)container == (Object)null || !HearthkeeperPlugin.ChestSizingEnabled.Value)
			{
				return;
			}
			ZNetView view = ContainerRegistry.GetView(container);
			if ((Object)(object)view == (Object)null || !view.IsValid() || !view.IsOwner())
			{
				return;
			}
			ZDO zDO = view.GetZDO();
			if (zDO != null)
			{
				int num = zDO.GetInt("MaddCatter.Hearthkeeper.width", 0);
				int num2 = zDO.GetInt("MaddCatter.Hearthkeeper.height", 0);
				if (container.m_width > num)
				{
					zDO.Set("MaddCatter.Hearthkeeper.width", container.m_width);
				}
				if (container.m_height > num2)
				{
					zDO.Set("MaddCatter.Hearthkeeper.height", container.m_height);
				}
			}
		}

		private static bool IsUnlistedChestPiece(Container container, string prefab)
		{
			if (!HearthkeeperPlugin.ResizeUnlistedChestPrefabs.Value)
			{
				return false;
			}
			Piece val = ((Component)container).GetComponent<Piece>();
			if ((Object)(object)val == (Object)null)
			{
				val = ((Component)container).GetComponentInParent<Piece>();
			}
			if ((Object)(object)val != (Object)null)
			{
				return prefab.IndexOf("chest", StringComparison.OrdinalIgnoreCase) >= 0;
			}
			return false;
		}

		private static Dictionary<string, Size> ParseRules(string raw)
		{
			Dictionary<string, Size> dictionary = new Dictionary<string, Size>(StringComparer.OrdinalIgnoreCase);
			if (string.IsNullOrEmpty(raw))
			{
				return dictionary;
			}
			string[] array = raw.Split(new char[3] { ',', ';', '\n' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Split(new char[1] { '=' }, 2);
				if (array2.Length == 2)
				{
					string[] array3 = array2[1].Trim().ToLowerInvariant().Split(new char[1] { 'x' });
					if (array3.Length == 2 && int.TryParse(array3[0], out var result) && int.TryParse(array3[1], out var result2) && result > 0 && result2 > 0)
					{
						dictionary[array2[0].Trim()] = new Size(result, result2);
					}
				}
			}
			return dictionary;
		}
	}
	internal static class ExternalSlotCompatibility
	{
		private const string EaqsApiTypeName = "EquipmentAndQuickSlots.API, EquipmentAndQuickSlots";

		private static bool _lookupAttempted;

		private static bool _availabilityLogged;

		private static MethodInfo _eaqsIsSlotCell;

		private static MethodInfo _eaqsGetVisibleRows;

		internal static bool IsProtectedPlayerSlot(ItemData item)
		{
			if (item != null)
			{
				return IsProtectedPlayerSlot(item.m_gridPos.x, item.m_gridPos.y);
			}
			return false;
		}

		internal static bool IsProtectedPlayerSlot(int x, int y)
		{
			EnsureEaqsApi();
			if (_eaqsIsSlotCell == null)
			{
				return false;
			}
			try
			{
				if (_eaqsGetVisibleRows != null && _eaqsGetVisibleRows.Invoke(null, null) is int num && y >= num)
				{
					return true;
				}
				object[] parameters = new object[3] { x, y, null };
				object obj = _eaqsIsSlotCell.Invoke(null, parameters);
				bool flag = default(bool);
				int num2;
				if (obj is bool)
				{
					flag = (bool)obj;
					num2 = 1;
				}
				else
				{
					num2 = 0;
				}
				return (byte)((uint)num2 & (flag ? 1u : 0u)) != 0;
			}
			catch (Exception ex)
			{
				ManualLogSource log = HearthkeeperPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Equipment and Quick Slots compatibility check failed: " + ex.GetBaseException().Message));
				}
				_eaqsIsSlotCell = null;
				return false;
			}
		}

		private static void EnsureEaqsApi()
		{
			if (_lookupAttempted)
			{
				return;
			}
			_lookupAttempted = true;
			Type type = Type.GetType("EquipmentAndQuickSlots.API, EquipmentAndQuickSlots", throwOnError: false);
			_eaqsIsSlotCell = type?.GetMethod("IsSlotCell", BindingFlags.Static | BindingFlags.Public, null, new Type[3]
			{
				typeof(int),
				typeof(int),
				typeof(string).MakeByRefType()
			}, null);
			_eaqsGetVisibleRows = type?.GetMethod("GetVisibleRows", BindingFlags.Static | BindingFlags.Public, null, Type.EmptyTypes, null);
			if (_eaqsIsSlotCell != null && !_availabilityLogged)
			{
				_availabilityLogged = true;
				ManualLogSource log = HearthkeeperPlugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"Equipment and Quick Slots compatibility enabled; equipment, quick, and custom slots are protected.");
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class HearthkeeperPatches
	{
		private struct NamedRemovalState
		{
			internal int Requested;

			internal int Before;
		}

		internal static bool CraftingRemoval;

		[HarmonyPatch(typeof(Container), "Awake")]
		[HarmonyPrefix]
		private static void ContainerAwakePrefix(Container __instance)
		{
			ContainerSizeService.ApplyBeforeAwake(__instance);
		}

		[HarmonyPatch(typeof(Container), "Awake")]
		[HarmonyPostfix]
		private static void ContainerAwakePostfix(Container __instance)
		{
			ContainerRegistry.Register(__instance);
			ContainerSizeService.RememberAppliedSize(__instance);
		}

		[HarmonyPatch(typeof(ItemDrop), "Awake")]
		[HarmonyPostfix]
		private static void ItemAwakePostfix(ItemDrop __instance)
		{
			WarehouseService.RegisterGroundItem(__instance);
		}

		[HarmonyPatch(typeof(Fireplace), "Awake")]
		[HarmonyPostfix]
		private static void FireplaceAwakePostfix(Fireplace __instance)
		{
			AutoRefuelService.Register(__instance);
		}

		[HarmonyPatch(typeof(Smelter), "Awake")]
		[HarmonyPostfix]
		private static void SmelterAwakePostfix(Smelter __instance)
		{
			AutoRefuelService.Register(__instance);
		}

		[HarmonyPatch(typeof(ItemDrop), "OnDestroy")]
		[HarmonyPostfix]
		private static void ItemDestroyPostfix(ItemDrop __instance)
		{
			WarehouseService.UnregisterGroundItem(__instance);
		}

		[HarmonyPatch(typeof(MonsterAI), "Awake")]
		[HarmonyPostfix]
		private static void MonsterAwakePostfix(MonsterAI __instance)
		{
			WarehouseService.RegisterCreature(__instance);
		}

		[HarmonyPatch(typeof(ObjectDB), "Awake")]
		[HarmonyPostfix]
		private static void ObjectDbAwakePostfix()
		{
			StackSizeService.Apply();
		}

		[HarmonyPatch(typeof(InventoryGui), "Show")]
		[HarmonyPostfix]
		private static void InventoryShowPostfix(Container container)
		{
			HearthkeeperPlugin.OpenContainer = container;
		}

		[HarmonyPatch(typeof(InventoryGui), "Awake")]
		[HarmonyPostfix]
		private static void InventoryAwakePostfix(InventoryGui __instance)
		{
			HearthkeeperPlugin.AttachInterface(__instance);
		}

		[HarmonyPatch(typeof(InventoryGui), "Hide")]
		[HarmonyPostfix]
		private static void InventoryHidePostfix()
		{
			HearthkeeperPlugin.OpenContainer = null;
		}

		[HarmonyPatch(typeof(InventoryGui), "UpdateInventory")]
		[HarmonyPostfix]
		private static void InventoryUpdatePostfix()
		{
			if ((Object)(object)HearthkeeperPlugin.OpenContainer != (Object)null)
			{
				ContainerNameService.ApplyOpenContainerTitle(HearthkeeperPlugin.OpenContainer);
			}
		}

		[HarmonyPatch(typeof(Container), "GetHoverName")]
		[HarmonyPostfix]
		private static void ContainerHoverNamePostfix(Container __instance, ref string __result)
		{
			ContainerNameService.ApplyHoverName(__instance, ref __result);
		}

		[HarmonyPatch(typeof(Container), "GetHoverText")]
		[HarmonyPostfix]
		private static void ContainerHoverTextPostfix(Container __instance, ref string __result)
		{
			ContainerNameService.ApplyHoverText(__instance, ref __result);
		}

		[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
		[HarmonyPrefix]
		private static void CraftingPrefix()
		{
			CraftingRemoval = true;
		}

		[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
		[HarmonyFinalizer]
		private static Exception CraftingFinalizer(Exception __exception)
		{
			CraftingRemoval = false;
			return __exception;
		}

		[HarmonyPatch(typeof(Player), "ConsumeResources")]
		[HarmonyPrefix]
		private static bool ConsumeResourcesPrefix(Player __instance, Requirement[] requirements, int qualityLevel, int itemQuality, int multiplier)
		{
			if (!HearthkeeperPlugin.Enabled.Value || !HearthkeeperPlugin.CraftFromContainers.Value || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			__instance.GetCurrentCraftingStation();
			foreach (Requirement val in requirements)
			{
				if (!((Object)(object)val.m_resItem == (Object)null))
				{
					int num = val.GetAmount(qualityLevel) * multiplier;
					if (num > 0)
					{
						string name = val.m_resItem.m_itemData.m_shared.m_name;
						WarehouseService.RemovePlayerThenNearby(__instance, name, num, itemQuality, matchWorldLevel: true);
					}
				}
			}
			return false;
		}

		[HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[]
		{
			typeof(string),
			typeof(int),
			typeof(int),
			typeof(bool)
		})]
		[HarmonyPrefix]
		private static void NamedRemovePrefix(Inventory __instance, string name, ref int amount, int itemQuality, bool worldLevelBased, out NamedRemovalState __state)
		{
			__state = new NamedRemovalState
			{
				Requested = amount,
				Before = 0
			};
			if (amount > 0)
			{
				if (CraftingRemoval && (Object)(object)Player.m_localPlayer != (Object)null && __instance == ((Humanoid)Player.m_localPlayer).GetInventory())
				{
					__state.Before = InventoryTransfers.CountType(__instance, name, itemQuality, worldLevelBased);
				}
				ClampProtectedRemoval(__instance, name, itemQuality, worldLevelBased, ref amount);
			}
		}

		[HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[]
		{
			typeof(string),
			typeof(int),
			typeof(int),
			typeof(bool)
		})]
		[HarmonyPostfix]
		private static void NamedRemovePostfix(Inventory __instance, string name, int itemQuality, bool worldLevelBased, NamedRemovalState __state)
		{
			if (__state.Requested > 0 && CraftingRemoval && !((Object)(object)Player.m_localPlayer == (Object)null) && __instance == ((Humanoid)Player.m_localPlayer).GetInventory())
			{
				int num = InventoryTransfers.CountType(__instance, name, itemQuality, worldLevelBased);
				int num2 = Math.Max(0, __state.Before - num);
				int num3 = Math.Max(0, __state.Requested - num2);
				if (num3 > 0)
				{
					WarehouseService.RemoveNearby(Player.m_localPlayer, name, num3, itemQuality, worldLevelBased);
				}
			}
		}

		[HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[]
		{
			typeof(ItemData),
			typeof(int)
		})]
		[HarmonyPrefix]
		private static bool StackRemovePrefix(Inventory __instance, ItemData item, ref int amount, ref bool __result)
		{
			Container val = ContainerRegistry.OwnerOf(__instance);
			if ((Object)(object)val == (Object)null || !ContainerRegistry.GetSettings(val).ManualLock || item == null)
			{
				return true;
			}
			int val2 = InventoryTransfers.AvailableInContainer(val, item.m_shared.m_name, item.m_quality);
			amount = Math.Min(amount, val2);
			if (amount > 0)
			{
				return true;
			}
			__result = false;
			return false;
		}

		[HarmonyPatch(typeof(Inventory), "RemoveOneItem")]
		[HarmonyPrefix]
		private static bool RemoveOnePrefix(Inventory __instance, ItemData item, ref bool __result)
		{
			Container val = ContainerRegistry.OwnerOf(__instance);
			if ((Object)(object)val == (Object)null || !ContainerRegistry.GetSettings(val).ManualLock || item == null)
			{
				return true;
			}
			if (InventoryTransfers.AvailableInContainer(val, item.m_shared.m_name, item.m_quality) > 0)
			{
				return true;
			}
			__result = false;
			return false;
		}

		[HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[] { typeof(int) })]
		[HarmonyPrefix]
		private static bool IndexRemovePrefix(Inventory __instance, int index, ref bool __result)
		{
			Container val = ContainerRegistry.OwnerOf(__instance);
			if ((Object)(object)val == (Object)null || !ContainerRegistry.GetSettings(val).ManualLock)
			{
				return true;
			}
			ItemData item = __instance.GetItem(index);
			if (item != null && InventoryTransfers.AvailableInContainer(val, item.m_shared.m_name, item.m_quality) >= item.m_stack)
			{
				return true;
			}
			__result = false;
			return false;
		}

		[HarmonyPatch(typeof(Inventory), "MoveItemToThis", new Type[]
		{
			typeof(Inventory),
			typeof(ItemData)
		})]
		[HarmonyPrefix]
		private static bool MoveWholePrefix(Inventory __instance, Inventory fromInventory, ItemData item)
		{
			Container val = ContainerRegistry.OwnerOf(fromInventory);
			if ((Object)(object)val == (Object)null || !ContainerRegistry.GetSettings(val).ManualLock || item == null)
			{
				return true;
			}
			int val2 = InventoryTransfers.AvailableInContainer(val, item.m_shared.m_name, item.m_quality);
			InventoryTransfers.Move(fromInventory, __instance, item, Math.Min(item.m_stack, val2), matchingOnly: false);
			return false;
		}

		[HarmonyPatch(typeof(Inventory), "MoveItemToThis", new Type[]
		{
			typeof(Inventory),
			typeof(ItemData),
			typeof(int),
			typeof(int),
			typeof(int)
		})]
		[HarmonyPrefix]
		private static void MoveAmountPrefix(Inventory fromInventory, ItemData item, ref int amount)
		{
			Container val = ContainerRegistry.OwnerOf(fromInventory);
			if (!((Object)(object)val == (Object)null) && ContainerRegistry.GetSettings(val).ManualLock && item != null)
			{
				amount = Math.Min(amount, InventoryTransfers.AvailableInContainer(val, item.m_shared.m_name, item.m_quality));
			}
		}

		[HarmonyPatch(typeof(Inventory), "MoveAll")]
		[HarmonyPrefix]
		private static bool MoveAllPrefix(Inventory __instance, Inventory fromInventory)
		{
			Container val = ContainerRegistry.OwnerOf(fromInventory);
			if ((Object)(object)val == (Object)null || !ContainerRegistry.GetSettings(val).ManualLock)
			{
				return true;
			}
			List<ItemData> list = new List<ItemData>(fromInventory.GetAllItems());
			for (int i = 0; i < list.Count; i++)
			{
				ItemData val2 = list[i];
				int val3 = InventoryTransfers.AvailableInContainer(val, val2.m_shared.m_name, val2.m_quality);
				InventoryTransfers.Move(fromInventory, __instance, val2, Math.Min(val2.m_stack, val3), matchingOnly: false);
			}
			return false;
		}

		[HarmonyPatch(typeof(Player), "HaveRequirementItems")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> RecipeCountsTranspiler(IEnumerable<CodeInstruction> instructions)
		{
			return ReplaceCountCalls(instructions);
		}

		[HarmonyPatch(typeof(Player), "HaveRequirements", new Type[]
		{
			typeof(Piece),
			typeof(RequirementMode)
		})]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> BuildCountsTranspiler(IEnumerable<CodeInstruction> instructions)
		{
			return ReplaceCountCalls(instructions);
		}

		[HarmonyPatch(typeof(InventoryGui), "SetupRequirement")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> RequirementUiTranspiler(IEnumerable<CodeInstruction> instructions)
		{
			return ReplaceCountCalls(instructions);
		}

		private static IEnumerable<CodeInstruction> ReplaceCountCalls(IEnumerable<CodeInstruction> instructions)
		{
			MethodInfo original = AccessTools.Method(typeof(Inventory), "CountItems", new Type[3]
			{
				typeof(string),
				typeof(int),
				typeof(bool)
			}, (Type[])null);
			MethodInfo replacement = AccessTools.Method(typeof(HearthkeeperPatches), "CountItemsIncludingWarehouse", (Type[])null, (Type[])null);
			foreach (CodeInstruction instruction in instructions)
			{
				if (CodeInstructionExtensions.Calls(instruction, original))
				{
					yield return CodeInstructionExtensions.MoveLabelsFrom(new CodeInstruction(OpCodes.Call, (object)replacement), instruction);
				}
				else
				{
					yield return instruction;
				}
			}
		}

		internal static int CountItemsIncludingWarehouse(Inventory inventory, string name, int quality, bool matchWorldLevel)
		{
			int num = inventory.CountItems(name, quality, matchWorldLevel);
			if (!HearthkeeperPlugin.Enabled.Value || !HearthkeeperPlugin.CraftFromContainers.Value || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				return num;
			}
			if (inventory != ((Humanoid)Player.m_localPlayer).GetInventory())
			{
				return num;
			}
			return num + WarehouseService.CountAvailableNearPlayer(name, quality, matchWorldLevel);
		}

		private static void ClampProtectedRemoval(Inventory inventory, string name, int quality, bool matchWorldLevel, ref int amount)
		{
			Container val = ContainerRegistry.OwnerOf(inventory);
			if (!((Object)(object)val == (Object)null) && ContainerRegistry.GetSettings(val).ManualLock)
			{
				amount = Math.Min(amount, InventoryTransfers.AvailableInContainer(val, name, quality, matchWorldLevel));
			}
		}
	}
	[BepInPlugin("MaddCatter.Hearthkeeper", "Hearthkeeper", "0.1.10")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class HearthkeeperPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "MaddCatter.Hearthkeeper";

		public const string PluginName = "Hearthkeeper";

		public const string PluginVersion = "0.1.10";

		internal static HearthkeeperPlugin Instance;

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> GroundStorage;

		internal static ConfigEntry<float> GroundPickupRange;

		internal static ConfigEntry<float> GroundInterval;

		internal static ConfigEntry<int> GroundItemsPerCycle;

		internal static ConfigEntry<float> InventoryStoreRange;

		internal static ConfigEntry<float> CraftRange;

		internal static ConfigEntry<bool> CraftFromContainers;

		internal static ConfigEntry<float> WarehouseSortRange;

		internal static ConfigEntry<bool> AutomaticSortEnabled;

		internal static ConfigEntry<float> AutomaticSortIntervalMinutes;

		internal static ConfigEntry<bool> ProtectHotbar;

		internal static ConfigEntry<bool> LivestockFeeding;

		internal static ConfigEntry<float> FeedRange;

		internal static ConfigEntry<float> FeedInterval;

		internal static ConfigEntry<ReserveMode> DefaultReserve;

		internal static ConfigEntry<int> DefaultCustomReserve;

		internal static ConfigEntry<bool> DefaultManualLock;

		internal static ConfigEntry<bool> DefaultAcceptStorage;

		internal static ConfigEntry<bool> DefaultCraftingSupply;

		internal static ConfigEntry<bool> DefaultLivestockFeed;

		internal static ConfigEntry<string> AllowedPrefabs;

		internal static ConfigEntry<string> BlockedPrefabs;

		internal static ConfigEntry<KeyboardShortcut> StoreKey;

		internal static ConfigEntry<KeyboardShortcut> SortWarehouseKey;

		internal static ConfigEntry<KeyboardShortcut> SortInventoryKey;

		internal static ConfigEntry<bool> StackSizesEnabled;

		internal static ConfigEntry<float> StackMultiplier;

		internal static ConfigEntry<int> MaximumStackSize;

		internal static ConfigEntry<string> StackOverrides;

		internal static ConfigEntry<bool> ChestSizingEnabled;

		internal static ConfigEntry<int> MinimumChestColumns;

		internal static ConfigEntry<int> MinimumChestRows;

		internal static ConfigEntry<int> MaximumChestColumns;

		internal static ConfigEntry<int> MaximumChestRows;

		internal static ConfigEntry<bool> ResizeUnlistedChestPrefabs;

		internal static ConfigEntry<string> ChestSizeRules;

		internal static ConfigEntry<InterfaceMode> InterfaceStyle;

		internal static ConfigEntry<float> PlayerButtonsOffsetX;

		internal static ConfigEntry<float> PlayerButtonsOffsetY;

		internal static ConfigEntry<float> ChestButtonsOffsetX;

		internal static ConfigEntry<float> ChestButtonsOffsetY;

		internal static ConfigEntry<float> ChestRulesOffsetX;

		internal static ConfigEntry<float> ChestRulesOffsetY;

		internal static ConfigEntry<bool> ReceivingGlowEnabled;

		internal static ConfigEntry<float> ReceivingGlowDuration;

		internal static ConfigEntry<float> ReceivingGlowIntensity;

		internal static ConfigEntry<float> ReceivingGlowRadius;

		internal static ConfigEntry<string> ReceivingGlowColor;

		internal static ConfigEntry<bool> ChestNameLabelsEnabled;

		internal static ConfigEntry<float> ChestNameLabelRange;

		internal static ConfigEntry<bool> AutoRefuelEnabled;

		internal static ConfigEntry<bool> AutoRefuelFires;

		internal static ConfigEntry<bool> AutoRefuelProcessingFuel;

		internal static ConfigEntry<bool> AutoRefuelProcessingInputs;

		internal static ConfigEntry<float> AutoRefuelRange;

		internal static ConfigEntry<float> AutoRefuelIntervalSeconds;

		internal static ConfigEntry<int> AutoRefuelMaximumDevicesPerCycle;

		internal static ConfigEntry<int> AutoRefuelMaximumItemsPerDevice;

		internal static ConfigEntry<float> AutoRefuelFireThresholdPercent;

		internal static ConfigEntry<float> AutoRefuelFireTargetPercent;

		internal static ConfigEntry<float> AutoRefuelProcessingFuelThresholdPercent;

		internal static ConfigEntry<float> AutoRefuelProcessingFuelTargetPercent;

		internal static ConfigEntry<float> AutoRefuelProcessingInputThresholdPercent;

		internal static ConfigEntry<float> AutoRefuelProcessingInputTargetPercent;

		internal static ConfigEntry<KeyboardShortcut> AutoRefuelDeviceToggleKey;

		internal static Container OpenContainer;

		private ConfigEntry<bool> _panelsDraggable;

		private ConfigEntry<float> _warehousePanelX;

		private ConfigEntry<float> _warehousePanelY;

		private ConfigEntry<float> _chestPanelX;

		private ConfigEntry<float> _chestPanelY;

		private ConfigEntry<bool> _resetPanelPositions;

		private Harmony _harmony;

		private NativeInterface _nativeInterface;

		private bool _nativeInterfaceFailed;

		private float _nextGroundRun;

		private float _nextFeedRun;

		private float _nextAutomaticSortRun;

		private float _nextAutoRefuelRun;

		private bool _automaticSortScheduled;

		private string _status = string.Empty;

		private float _statusUntil;

		private string _customReserveText = "1";

		private Container _lastPanelContainer;

		private GUIStyle _heading;

		private GUIStyle _small;

		private GUIStyle _worldName;

		private GUIStyle _worldNameShadow;

		private Rect _warehouseRect;

		private Rect _chestRect;

		private bool _panelPositionsInitialized;

		private bool _warehouseMoved;

		private bool _chestMoved;

		private const float WarehouseWidth = 220f;

		private const float WarehouseHeight = 176f;

		private const float ChestWidth = 252f;

		private const float ChestHeight = 382f;

		private const float DefaultWarehouseX = 0.01f;

		private const float DefaultWarehouseY = 0.98f;

		private const float DefaultChestX = 0.99f;

		private const float DefaultChestY = 0.5f;

		private void Awake()
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Game.isModded = true;
			BindConfiguration();
			_harmony = new Harmony("MaddCatter.Hearthkeeper");
			_harmony.PatchAll(typeof(HearthkeeperPatches));
			StackSizeService.Apply();
			StackSizesEnabled.SettingChanged += delegate
			{
				StackSizeService.Apply();
			};
			StackMultiplier.SettingChanged += delegate
			{
				StackSizeService.Apply();
			};
			MaximumStackSize.SettingChanged += delegate
			{
				StackSizeService.Apply();
			};
			StackOverrides.SettingChanged += delegate
			{
				StackSizeService.Apply();
			};
			Enabled.SettingChanged += delegate
			{
				_automaticSortScheduled = false;
			};
			AutomaticSortEnabled.SettingChanged += delegate
			{
				_automaticSortScheduled = false;
			};
			AutomaticSortIntervalMinutes.SettingChanged += delegate
			{
				_automaticSortScheduled = false;
			};
			_resetPanelPositions.SettingChanged += delegate
			{
				if (_resetPanelPositions.Value)
				{
					ResetPanelPositions();
				}
			};
			InterfaceStyle.SettingChanged += delegate
			{
				_nativeInterfaceFailed = false;
				if (InterfaceStyle.Value != InterfaceMode.NativeAttached && _nativeInterface != null)
				{
					_nativeInterface.Dispose();
					_nativeInterface = null;
				}
			};
			if (_resetPanelPositions.Value)
			{
				ResetPanelPositions();
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Hearthkeeper 0.1.10 loaded for Valheim 1.0.12.");
		}

		private void OnDestroy()
		{
			if (_nativeInterface != null)
			{
				_nativeInterface.Dispose();
			}
			ChestGlowService.Clear();
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
			}
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		private void Update()
		{
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			ChestGlowService.Update();
			if (!Enabled.Value || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				return;
			}
			if (InterfaceStyle.Value == InterfaceMode.NativeAttached)
			{
				EnsureNativeInterface();
				if (_nativeInterface != null)
				{
					_nativeInterface.UpdateVisibility();
				}
			}
			else if (_nativeInterface != null)
			{
				_nativeInterface.Dispose();
				_nativeInterface = null;
			}
			if ((Object)(object)OpenContainer != (Object)null)
			{
				ContainerNameService.ApplyOpenContainerTitle(OpenContainer);
			}
			if (Time.time >= _nextGroundRun)
			{
				_nextGroundRun = Time.time + GroundInterval.Value;
				try
				{
					WarehouseService.ProcessGroundItems();
				}
				catch (Exception arg)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)$"Ground storage cycle failed: {arg}");
				}
			}
			if (Time.time >= _nextFeedRun)
			{
				_nextFeedRun = Time.time + FeedInterval.Value;
				try
				{
					WarehouseService.ProcessFeeding();
				}
				catch (Exception arg2)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)$"Livestock feed cycle failed: {arg2}");
				}
			}
			Player localPlayer = Player.m_localPlayer;
			if (Time.time >= _nextAutoRefuelRun)
			{
				_nextAutoRefuelRun = Time.time + AutoRefuelIntervalSeconds.Value;
				try
				{
					AutoRefuelService.Process(localPlayer);
				}
				catch (Exception arg3)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)$"Automatic refueling cycle failed: {arg3}");
				}
			}
			ProcessAutomaticSort(localPlayer);
			if (HotkeysBlocked(localPlayer))
			{
				return;
			}
			bool flag = InventoryGui.IsVisible();
			KeyboardShortcut value = AutoRefuelDeviceToggleKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				AutoRefuelService.ToggleHoveredDevice(localPlayer);
				return;
			}
			value = StoreKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				RunStoreMatching();
				return;
			}
			if (flag)
			{
				value = SortWarehouseKey.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					RunSortWarehouse();
					return;
				}
			}
			if (flag)
			{
				value = SortInventoryKey.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					RunSortInventory();
				}
			}
		}

		private void OnGUI()
		{
			if (!Enabled.Value || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				return;
			}
			DrawWorldChestNames();
			if (InventoryGui.IsVisible() && (InterfaceStyle.Value != InterfaceMode.NativeAttached || _nativeInterfaceFailed))
			{
				EnsureStyles();
				EnsurePanelPositions();
				DrawWarehousePanel();
				if ((Object)(object)OpenContainer != (Object)null)
				{
					DrawContainerPanel(OpenContainer);
				}
			}
		}

		private void DrawWorldChestNames()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: 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_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: 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_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: 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)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: 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_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			if (!ChestNameLabelsEnabled.Value || (Object)(object)Camera.main == (Object)null)
			{
				return;
			}
			EnsureStyles();
			Vector3 position = ((Component)Player.m_localPlayer).transform.position;
			float num = Mathf.Max(1f, ChestNameLabelRange.Value);
			List<Container> list = ContainerRegistry.All();
			Rect val6 = default(Rect);
			for (int i = 0; i < list.Count; i++)
			{
				Container val = list[i];
				if ((Object)(object)val == (Object)null || val.IsInUse())
				{
					continue;
				}
				string customName = ContainerNameService.GetCustomName(val);
				if (string.IsNullOrEmpty(customName))
				{
					continue;
				}
				Vector3 val2 = position - ((Component)val).transform.position;
				val2.y = 0f;
				if (((Vector3)(ref val2)).sqrMagnitude > 0.01f)
				{
					((Vector3)(ref val2)).Normalize();
				}
				Vector3 val3 = ((Component)val).transform.position + Vector3.up * 0.72f + val2 * 0.38f;
				Vector3 val4 = val3 - position;
				if (!(((Vector3)(ref val4)).sqrMagnitude > num * num))
				{
					Vector3 val5 = Camera.main.WorldToScreenPoint(val3);
					if (!(val5.z <= 0f))
					{
						val5.y = (float)Screen.height - val5.y;
						((Rect)(ref val6))..ctor(val5.x - 140f, val5.y - 18f, 280f, 36f);
						GUI.Label(new Rect(((Rect)(ref val6)).x + 2f, ((Rect)(ref val6)).y + 2f, ((Rect)(ref val6)).width, ((Rect)(ref val6)).height), customName, _worldNameShadow);
						GUI.Label(val6, customName, _worldName);
					}
				}
			}
		}

		private void DrawWarehousePanel()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: 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_002d: Expected O, but got Unknown
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			Rect warehouseRect = _warehouseRect;
			_warehouseRect = GUI.Window(487201, ClampToScreen(_warehouseRect), new WindowFunction(DrawWarehouseWindow), string.Empty);
			SavePanelPosition(warehouseRect, _warehouseRect, _warehousePanelX, _warehousePanelY, ref _warehouseMoved);
		}

		private void DrawContainerPanel(Container container)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_lastPanelContainer != (Object)(object)container)
			{
				_lastPanelContainer = container;
				_customReserveText = ContainerRegistry.GetSettings(container).CustomReserve.ToString();
			}
			Rect chestRect = _chestRect;
			_chestRect = GUI.Window(487202, ClampToScreen(_chestRect), new WindowFunction(DrawChestWindow), string.Empty);
			SavePanelPosition(chestRect, _chestRect, _chestPanelX, _chestPanelY, ref _chestMoved);
		}

		private void DrawWarehouseWindow(int windowId)
		{
			//IL_0014: 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)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			GUI.Label(new Rect(12f, 8f, 196f, 25f), "HEARTHKEEPER", _heading);
			Rect val = new Rect(12f, 38f, 196f, 28f);
			KeyboardShortcut value = StoreKey.Value;
			if (GUI.Button(val, $"Store Matching  [{((KeyboardShortcut)(ref value)).MainKey}]"))
			{
				RunStoreMatching();
			}
			Rect val2 = new Rect(12f, 70f, 196f, 28f);
			value = SortWarehouseKey.Value;
			if (GUI.Button(val2, $"Consolidate Chests  [{((KeyboardShortcut)(ref value)).MainKey}]"))
			{
				RunSortWarehouse();
			}
			Rect val3 = new Rect(12f, 102f, 196f, 28f);
			value = SortInventoryKey.Value;
			if (GUI.Button(val3, $"Sort Inventory  [{((KeyboardShortcut)(ref value)).MainKey}]"))
			{
				RunSortInventory();
			}
			string text = ((Time.unscaledTime < _statusUntil) ? _status : "Ready — drag this title bar to move");
			GUI.Label(new Rect(12f, 136f, 196f, 34f), text, _small);
			if (_panelsDraggable.Value)
			{
				GUI.DragWindow(new Rect(0f, 0f, 220f, 34f));
			}
		}

		private void DrawChestWindow(int windowId)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_031b: Unknown result type (might be due to invalid IL or missing references)
			Container openContainer = OpenContainer;
			if (!((Object)(object)openContainer == (Object)null))
			{
				ContainerSettings settings = ContainerRegistry.GetSettings(openContainer);
				GUI.Label(new Rect(12f, 8f, 228f, 24f), "CHEST RULES", _heading);
				GUI.Label(new Rect(12f, 36f, 228f, 22f), ContainerNameService.DisplayName(openContainer), _small);
				if (GUI.Button(new Rect(12f, 64f, 228f, 28f), "Rename Chest"))
				{
					RequestRenameChest();
				}
				if (GUI.Button(new Rect(12f, 96f, 228f, 28f), "Reserve: " + ReserveLabel(settings.Reserve)))
				{
					settings.Reserve = (ReserveMode)((int)(settings.Reserve + 1) % 4);
					Save(openContainer, settings);
				}
				GUI.Label(new Rect(12f, 130f, 100f, 24f), "Custom amount", _small);
				_customReserveText = GUI.TextField(new Rect(118f, 128f, 122f, 24f), _customReserveText, 6);
				if (int.TryParse(_customReserveText, out var result) && result >= 0 && result != settings.CustomReserve)
				{
					settings.CustomReserve = result;
					Save(openContainer, settings);
				}
				if (GUI.Button(new Rect(12f, 160f, 228f, 28f), "Manual reserve lock: " + OnOff(settings.ManualLock)))
				{
					settings.ManualLock = !settings.ManualLock;
					Save(openContainer, settings);
				}
				if (GUI.Button(new Rect(12f, 192f, 228f, 28f), "Accept automatic storage: " + OnOff(settings.AcceptStorage)))
				{
					settings.AcceptStorage = !settings.AcceptStorage;
					Save(openContainer, settings);
				}
				if (GUI.Button(new Rect(12f, 224f, 228f, 28f), "Crafting supply: " + AllowBlock(settings.CraftingSupply)))
				{
					settings.CraftingSupply = !settings.CraftingSupply;
					Save(openContainer, settings);
				}
				if (GUI.Button(new Rect(12f, 256f, 228f, 28f), "Livestock feed: " + OnOff(settings.LivestockFeed)))
				{
					settings.LivestockFeed = !settings.LivestockFeed;
					Save(openContainer, settings);
				}
				if (GUI.Button(new Rect(12f, 296f, 228f, 28f), "Store All"))
				{
					RunStoreAll();
				}
				if (GUI.Button(new Rect(12f, 336f, 228f, 28f), "Sort This Chest"))
				{
					WarehouseService.SortInventory(ContainerRegistry.SafeInventory(openContainer), preserveFirstRow: false);
					SetStatus("Chest sorted");
				}
				if (_panelsDraggable.Value)
				{
					GUI.DragWindow(new Rect(0f, 0f, 252f, 34f));
				}
			}
		}

		private void EnsurePanelPositions()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			if (!_panelPositionsInitialized)
			{
				_warehouseRect = RectFromNormalized(_warehousePanelX.Value, _warehousePanelY.Value, 220f, 176f);
				_chestRect = RectFromNormalized(_chestPanelX.Value, _chestPanelY.Value, 252f, 382f);
				_panelPositionsInitialized = true;
			}
		}

		private static Rect RectFromNormalized(float x, float y, float width, float height)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			float num = Math.Max(0f, (float)Screen.width - width);
			float num2 = Math.Max(0f, (float)Screen.height - height);
			return new Rect(Mathf.Clamp01(x) * num, Mathf.Clamp01(y) * num2, width, height);
		}

		private static Rect ClampToScreen(Rect rect)
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			((Rect)(ref rect)).x = Mathf.Clamp(((Rect)(ref rect)).x, 0f, Math.Max(0f, (float)Screen.width - ((Rect)(ref rect)).width));
			((Rect)(ref rect)).y = Mathf.Clamp(((Rect)(ref rect)).y, 0f, Math.Max(0f, (float)Screen.height - ((Rect)(ref rect)).height));
			return rect;
		}

		private static void SavePanelPosition(Rect before, Rect after, ConfigEntry<float> xEntry, ConfigEntry<float> yEntry, ref bool moved)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: 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)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Invalid comparison between Unknown and I4
			Vector2 val = ((Rect)(ref before)).position - ((Rect)(ref after)).position;
			if (((Vector2)(ref val)).sqrMagnitude >= 0.01f)
			{
				moved = true;
			}
			if ((int)Event.current.type == 1 && moved)
			{
				float num = Math.Max(1f, (float)Screen.width - ((Rect)(ref after)).width);
				float num2 = Math.Max(1f, (float)Screen.height - ((Rect)(ref after)).height);
				xEntry.Value = Mathf.Clamp01(((Rect)(ref after)).x / num);
				yEntry.Value = Mathf.Clamp01(((Rect)(ref after)).y / num2);
				moved = false;
			}
		}

		private void ResetPanelPositions()
		{
			_warehousePanelX.Value = 0.01f;
			_warehousePanelY.Value = 0.98f;
			_chestPanelX.Value = 0.99f;
			_chestPanelY.Value = 0.5f;
			_resetPanelPositions.Value = false;
			_panelPositionsInitialized = false;
		}

		internal void RunStoreMatching()
		{
			try
			{
				int num = WarehouseService.StoreMatchingFromPlayer(Player.m_localPlayer);
				SetStatus($"Stored {num} item(s)");
				if (_nativeInterface != null)
				{
					_nativeInterface.ShowActionResult(NativeAction.StoreMatching, $"Stored {num}");
				}
			}
			catch (Exception ex)
			{
				ReportError("Store Matching failed", ex);
			}
		}

		internal void RunStoreAll()
		{
			try
			{
				int num = WarehouseService.StoreAllInOpenChest(Player.m_localPlayer, OpenContainer);
				SetStatus($"Stored {num} item(s)");
				if (_nativeInterface != null)
				{
					_nativeInterface.ShowActionResult(NativeAction.StoreAll, $"Stored {num}");
				}
			}
			catch (Exception ex)
			{
				ReportError("Store All failed", ex);
			}
		}

		internal void RunSortWarehouse()
		{
			try
			{
				int num = WarehouseService.SortWarehouse(Player.m_localPlayer);
				SetStatus($"Moved {num} item(s)");
				if (_nativeInterface != null)
				{
					_nativeInterface.ShowActionResult(NativeAction.Consolidate, $"Moved {num}");
				}
			}
			catch (Exception ex)
			{
				ReportError("Consolidate Chests failed", ex);
			}
		}

		internal void RunSortInventory()
		{
			try
			{
				WarehouseService.SortInventory(((Humanoid)Player.m_localPlayer).GetInventory(), ProtectHotbar.Value, preserveEquipped: true);
				SetStatus("Inventory sorted");
				if (_nativeInterface != null)
				{
					_nativeInterface.ShowActionResult(NativeAction.SortInventory, "Inventory Sorted");
				}
			}
			catch (Exception ex)
			{
				ReportError("Inventory sort failed", ex);
			}
		}

		internal void RequestRenameChest()
		{
			try
			{
				ContainerNameService.RequestRename(OpenContainer);
			}
			catch (Exception ex)
			{
				ReportError("Rename Chest failed", ex);
			}
		}

		private void ProcessAutomaticSort(Player player)
		{
			if (!AutomaticSortEnabled.Value)
			{
				_automaticSortScheduled = false;
			}
			else if (!_automaticSortScheduled)
			{
				ScheduleNextAutomaticSort();
			}
			else
			{
				if (Time.time < _nextAutomaticSortRun)
				{
					return;
				}
				if (!InventoryGui.IsVisible() && !HotkeysBlocked(player))
				{
					ScheduleNextAutomaticSort();
					try
					{
						int arrangedChests;
						int num = WarehouseService.AutomaticSortWarehouse(player, out arrangedChests);
						if (num > 0 || arrangedChests > 0)
						{
							((BaseUnityPlugin)this).Logger.LogInfo((object)$"Automatic sort moved {num} item(s) and arranged {arrangedChests} chest(s).");
						}
						return;
					}
					catch (Exception ex)
					{
						ReportError("Automatic sorting failed", ex);
						return;
					}
				}
				_nextAutomaticSortRun = Time.time + 5f;
			}
		}

		private void ScheduleNextAutomaticSort()
		{
			_nextAutomaticSortRun = Time.time + Mathf.Max(0.25f, AutomaticSortIntervalMinutes.Value) * 60f;
			_automaticSortScheduled = true;
		}

		internal void Save(Container container, ContainerSettings settings)
		{
			if (!ContainerRegistry.SaveSettings(container, settings))
			{
				SetStatus("Could not save chest rules");
			}
		}

		private void ReportError(string message, Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)(message + ": " + ex));
			SetStatus(message + "; see log");
		}

		private void SetStatus(string status)
		{
			_status = status;
			_statusUntil = Time.unscaledTime + 4f;
		}

		private void EnsureStyles()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: 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_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Expected O, but got Unknown
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Expected O, but got Unknown
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Expected O, but got Unknown
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			if (_heading == null)
			{
				_heading = new GUIStyle(GUI.skin.label)
				{
					fontStyle = (FontStyle)1,
					alignment = (TextAnchor)4,
					fontSize = 15
				};
				_small = new GUIStyle(GUI.skin.label)
				{
					wordWrap = true,
					fontSize = 12
				};
				_worldName = new GUIStyle(GUI.skin.label)
				{
					alignment = (TextAnchor)4,
					fontStyle = (FontStyle)1,
					fontSize = 15
				};
				_worldName.normal.textColor = new Color(1f, 0.86f, 0.58f, 1f);
				_worldName.padding = new RectOffset(4, 4, 2, 2);
				_worldNameShadow = new GUIStyle(_worldName);
				_worldNameShadow.normal.textColor = new Color(0.05f, 0.035f, 0.02f, 0.95f);
			}
		}

		private static string ReserveLabel(ReserveMode mode)
		{
			return mode switch
			{
				ReserveMode.OneItem => "1 item", 
				ReserveMode.OneStack => "1 stack", 
				ReserveMode.Custom => "custom", 
				_ => "off", 
			};
		}

		private static string OnOff(bool value)
		{
			if (!value)
			{
				return "OFF";
			}
			return "ON";
		}

		private static string AllowBlock(bool value)
		{
			if (!value)
			{
				return "BLOCK";
			}
			return "ALLOW";
		}

		internal static string ReserveLabelForUi(ReserveMode mode)
		{
			return ReserveLabel(mode);
		}

		private static bool HotkeysBlocked(Player player)
		{
			if (!((Object)(object)player == (Object)null) && !((Character)player).IsDead() && !((Character)player).InCutscene() && !((Character)player).IsTeleporting() && !TextInput.IsVisible() && !Console.IsVisible() && !Menu.IsVisible())
			{
				if ((Object)(object)Chat.instance != (Object)null)
				{
					return Chat.instance.HasFocus();
				}
				return false;
			}
			return true;
		}

		private void EnsureNativeInterface()
		{
			if (InterfaceStyle.Value != InterfaceMode.NativeAttached || _nativeInterface != null || _nativeInterfaceFailed)
			{
				return;
			}
			InventoryGui instance = InventoryGui.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			try
			{
				_nativeInterface = new NativeInterface(this, instance);
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Attached Hearthkeeper controls to the Valheim inventory interface.");
			}
			catch (Exception ex)
			{
				_nativeInterfaceFailed = true;
				((BaseUnityPlugin)this).Logger.LogError((object)("Could not attach the native Hearthkeeper interface; using the floating fallback. " + ex));
			}
		}

		internal static void AttachInterface(InventoryGui gui)
		{
			if (!((Object)(object)Instance == (Object)null) && !((Object)(object)gui == (Object)null))
			{
				if (Instance._nativeInterface != null)
				{
					Instance._nativeInterface.Dispose();
				}
				Instance._nativeInterface = null;
				Instance._nativeInterfaceFailed = false;
				Instance.EnsureNativeInterface();
			}
		}

		internal static bool IsAllowedPrefab(string prefab)
		{
			HashSet<string> hashSet = ParseNames(AllowedPrefabs.Value);
			if (ParseNames(BlockedPrefabs.Value).Contains(prefab))
			{
				return false;
			}
			if (hashSet.Count != 0)
			{
				return hashSet.Contains(prefab);
			}
			return true;
		}

		private static HashSet<string> ParseNames(string raw)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			if (string.IsNullOrEmpty(raw))
			{
				return hashSet;
			}
			string[] array = raw.Split(new char[3] { ',', ';', '\n' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				hashSet.Add(array[i].Trim());
			}
			return hashSet;
		}

		private void BindConfiguration()
		{
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Expected O, but got Unknown
			//IL_02d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02de: Expected O, but got Unknown
			//IL_03a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b0: Expected O, but got Unknown
			//IL_03d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e2: Expected O, but got Unknown
			//IL_040b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0415: Expected O, but got Unknown
			//IL_043e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0448: Expected O, but got Unknown
			//IL_04f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0521: Unknown result type (might be due to invalid IL or missing references)
			//IL_054f: Unknown result type (might be due to invalid IL or missing references)
			//IL_057d: Unknown result type (might be due to invalid IL or missing references)
			//IL_09a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_09ac: Expected O, but got Unknown
			//IL_0ae2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0aec: Expected O, but got Unknown
			//IL_0b15: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b1f: Expected O, but got Unknown
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("01 General", "Enabled", true, "Master switch for Hearthkeeper.");
			GroundStorage = ((BaseUnityPlugin)this).Config.Bind<bool>("02 Ground Storage", "Enabled", true, "Move loaded ground drops into eligible matching chests.");
			GroundPickupRange = ((BaseUnityPlugin)this).Config.Bind<float>("02 Ground Storage", "Range", 20f, Range(1f, 100f, "Maximum distance from a ground item to a matching chest."));
			GroundInterval = ((BaseUnityPlugin)this).Config.Bind<float>("02 Ground Storage", "IntervalSeconds", 2f, Range(0.5f, 30f, "Seconds between ground storage passes."));
			GroundItemsPerCycle = ((BaseUnityPlugin)this).Config.Bind<int>("02 Ground Storage", "MaximumItemsPerCycle", 50, new ConfigDescription("Performance budget for each pass.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 500), Array.Empty<object>()));
			InventoryStoreRange = ((BaseUnityPlugin)this).Config.Bind<float>("03 Inventory Storage", "Range", 20f, Range(1f, 100f, "Store Matching search radius around the player."));
			ProtectHotbar = ((BaseUnityPlugin)this).Config.Bind<bool>("03 Inventory Storage", "ProtectHotbar", true, "Do not auto-store or rearrange the first inventory row.");
			CraftFromContainers = ((BaseUnityPlugin)this).Config.Bind<bool>("04 Crafting and Building", "Enabled", true, "Count and consume eligible nearby chest materials for recipes and pieces.");
			CraftRange = ((BaseUnityPlugin)this).Config.Bind<float>("04 Crafting and Building", "Range", 20f, Range(1f, 100f, "Crafting and building container radius."));
			WarehouseSortRange = ((BaseUnityPlugin)this).Config.Bind<float>("05 Sorting", "Range", 20f, Range(1f, 100f, "Warehouse redistribution radius."));
			AutomaticSortEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("05 Sorting", "AutomaticCycleEnabled", false, "Periodically consolidate matching items between nearby chests and arrange each chest. The cycle waits while inventory or chest interfaces are open.");
			AutomaticSortIntervalMinutes = ((BaseUnityPlugin)this).Config.Bind<float>("05 Sorting", "AutomaticCycleMinutes", 5f, Range(0.25f, 120f, "Minutes between automatic warehouse sorting passes."));
			LivestockFeeding = ((BaseUnityPlugin)this).Config.Bind<bool>("06 Livestock Feed", "Enabled", true, "Supply hungry vanilla-style tameables from feed-enabled chests.");
			FeedRange = ((BaseUnityPlugin)this).Config.Bind<float>("06 Livestock Feed", "Range", 20f, Range(1f, 100f, "Maximum chest-to-creature feeding distance."));
			FeedInterval = ((BaseUnityPlugin)this).Config.Bind<float>("06 Livestock Feed", "IntervalSeconds", 5f, Range(1f, 60f, "Seconds between feeding passes."));
			DefaultReserve = ((BaseUnityPlugin)this).Config.Bind<ReserveMode>("07 New Chest Defaults", "ReserveMode", ReserveMode.OneItem, "Off, OneItem, OneStack, or Custom.");
			DefaultCustomReserve = ((BaseUnityPlugin)this).Config.Bind<int>("07 New Chest Defaults", "CustomReserve", 1, new ConfigDescription("Quantity used by Custom reserve mode.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100000), Array.Empty<object>()));
			DefaultManualLock = ((BaseUnityPlugin)this).Config.Bind<bool>("07 New Chest Defaults", "ManualReserveLock", false, "Prevent manual removal below the chest reserve.");
			DefaultAcceptStorage = ((BaseUnityPlugin)this).Config.Bind<bool>("07 New Chest Defaults", "AcceptAutomaticStorage", true, "Allow automated deposits into new/unconfigured chests.");
			DefaultCraftingSupply = ((BaseUnityPlugin)this).Config.Bind<bool>("07 New Chest Defaults", "CraftingSupply", true, "Allow crafting and building to count and consume materials from new/unconfigured chests.");
			DefaultLivestockFeed = ((BaseUnityPlugin)this).Config.Bind<bool>("07 New Chest Defaults", "LivestockFeed", false, "Allow new/unconfigured chests to supply livestock.");
			ChestSizingEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("08 Chest Sizes", "Enabled", true, "Resize listed player storage chests when they load. A world reload is required after changes.");
			MinimumChestColumns = ((BaseUnityPlugin)this).Config.Bind<int>("08 Chest Sizes", "MinimumColumns", 7, new ConfigDescription("Smallest permitted width for a resized chest.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>()));
			MinimumChestRows = ((BaseUnityPlugin)this).Config.Bind<int>("08 Chest Sizes", "MinimumRows", 4, new ConfigDescription("Smallest permitted height. Set to 5 for a 7x5 smallest chest.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>()));
			MaximumChestColumns = ((BaseUnityPlugin)this).Config.Bind<int>("08 Chest Sizes", "MaximumColumns", 12, new ConfigDescription("Maximum width accepted from a size rule.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>()));
			MaximumChestRows = ((BaseUnityPlugin)this).Config.Bind<int>("08 Chest Sizes", "MaximumRows", 10, new ConfigDescription("Maximum height accepted from a size rule.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>()));
			ResizeUnlistedChestPrefabs = ((BaseUnityPlugin)this).Config.Bind<bool>("08 Chest Sizes", "ResizeUnlistedChestPrefabs", true, "Apply the minimum to unlisted build pieces whose prefab name contains 'chest'. Other container types remain untouched.");
			ChestSizeRules = ((BaseUnityPlugin)this).Config.Bind<string>("08 Chest Sizes", "PrefabSizes", "piece_chest_wood=7x4,piece_chest_private=7x5,piece_chest=9x6,piece_chest_blackmetal=10x8,piece_chest_ashwood=10x8", "Comma-separated prefab=size rules. Sizes only grow automatically; they never silently shrink a previously enlarged chest.");
			AllowedPrefabs = ((BaseUnityPlugin)this).Config.Bind<string>("09 Compatibility", "AllowedContainerPrefabs", string.Empty, "Optional comma-separated allow list. Empty permits all standard Container prefabs.");
			BlockedPrefabs = ((BaseUnityPlugin)this).Config.Bind<string>("09 Compatibility", "BlockedContainerPrefabs", string.Empty, "Comma-separated prefab names Hearthkeeper must ignore.");
			StoreKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("10 Hotkeys", "StoreMatching", new KeyboardShortcut((KeyCode)287, Array.Empty<KeyCode>()), "Store matching player items during normal gameplay or while inventory is open.");
			SortWarehouseKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("10 Hotkeys", "SortWarehouse", new KeyboardShortcut((KeyCode)288, Array.Empty<KeyCode>()), "Consolidate matching items while inventory is open.");
			SortInventoryKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("10 Hotkeys", "SortInventory", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Sort player inventory while inventory is open.");
			AutoRefuelDeviceToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("10 Hotkeys", "ToggleHoveredDeviceAutoRefuel", new KeyboardShortcut((KeyCode)290, Array.Empty<KeyCode>()), "Toggle automatic refueling for the fire, torch, or processing device under the crosshair.");
			InterfaceStyle = ((BaseUnityPlugin)this).Config.Bind<InterfaceMode>("11 Interface", "Style", InterfaceMode.NativeAttached, "NativeAttached uses Valheim-styled controls on the inventory and chest frames. FloatingLegacy keeps the movable gray test panels.");
			PlayerButtonsOffsetX = ((BaseUnityPlugin)this).Config.Bind<float>("11 Interface", "PlayerButtonsOffsetX", 0f, Range(-500f, 500f, "Horizontal offset for the attached player inventory buttons."));
			PlayerButtonsOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("11 Interface", "PlayerButtonsOffsetY", -8f, Range(-500f, 500f, "Vertical offset for the attached player inventory buttons."));
			ChestButtonsOffsetX = ((BaseUnityPlugin)this).Config.Bind<float>("11 Interface", "ChestButtonsOffsetX", 0f, Range(-500f, 500f, "Horizontal offset for Consolidate Chests and Chest Rules."));
			ChestButtonsOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("11 Interface", "ChestButtonsOffsetY", -8f, Range(-500f, 500f, "Vertical offset for Consolidate Chests and Chest Rules."));
			ChestRulesOffsetX = ((BaseUnityPlugin)this).Config.Bind<float>("11 Interface", "ChestRulesPanelOffsetX", 0f, Range(-800f, 800f, "Horizontal offset for the attached Chest Rules panel."));
			ChestRulesOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("11 Interface", "ChestRulesPanelOffsetY", 0f, Range(-800f, 800f, "Vertical offset for the attached Chest Rules panel."));
			_panelsDraggable = ((BaseUnityPlugin)this).Config.Bind<bool>("11 Interface", "PanelsDraggable", true, "Drag either panel by its title area. Positions are saved as screen-relative values.");
			_warehousePanelX = ((BaseUnityPlugin)this).Config.Bind<float>("11 Interface", "WarehousePanelX", 0.01f, Range(0f, 1f, "Horizontal Warehouse panel position: 0 is left, 1 is right."));
			_warehousePanelY = ((BaseUnityPlugin)this).Config.Bind<float>("11 Interface", "WarehousePanelY", 0.98f, Range(0f, 1f, "Vertical Warehouse panel position: 0 is top, 1 is bottom."));
			_chestPanelX = ((BaseUnityPlugin)this).Config.Bind<float>("11 Interface", "ChestRulesPanelX", 0.99f, Range(0f, 1f, "Horizontal Chest Rules position: 0 is left, 1 is right."));
			_chestPanelY = ((BaseUnityPlugin)this).Config.Bind<float>("11 Interface", "ChestRulesPanelY", 0.5f, Range(0f, 1f, "Vertical Chest Rules position: 0 is top, 1 is bottom."));
			_resetPanelPositions = ((BaseUnityPlugin)this).Config.Bind<bool>("11 Interface", "ResetPanelPositions", false, "Set true to restore both default panel positions; Hearthkeeper changes it back to false.");
			ReceivingGlowEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("12 Chest Activity Glow", "Enabled", true, "Briefly pulse a chest when Hearthkeeper automatically deposits items into it.");
			ReceivingGlowDuration = ((BaseUnityPlugin)this).Config.Bind<float>("12 Chest Activity Glow", "DurationSeconds", 3f, Range(0.25f, 10f, "How long a receiving chest remains highlighted. Repeated deposits refresh the timer."));
			ReceivingGlowIntensity = ((BaseUnityPlugin)this).Config.Bind<float>("12 Chest Activity Glow", "Intensity", 2.2f, Range(0.1f, 8f, "Brightness of the pulsing chest light and emissive highlight."));
			ReceivingGlowRadius = ((BaseUnityPlugin)this).Config.Bind<float>("12 Chest Activity Glow", "LightRadius", 3.5f, Range(0.5f, 12f, "World-space radius of the temporary light around a receiving chest."));
			ReceivingGlowColor = ((BaseUnityPlugin)this).Config.Bind<string>("12 Chest Activity Glow", "Color", "#FFD27A", "HTML color for the receiving-chest glow, such as #FFD27A. Pulsing brightness keeps the signal readable without relying only on hue.");
			ChestNameLabelsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("11 Interface", "ChestNameLabelsEnabled", true, "Show custom chest names above closed chests in the world.");
			ChestNameLabelRange = ((BaseUnityPlugin)this).Config.Bind<float>("11 Interface", "ChestNameLabelRange", 20f, Range(5f, 60f, "Maximum distance at which a custom chest name is visible."));
			StackSizesEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("12 Stack Sizes - Experimental", "Enabled", false, "Optional global stack resizing. Leave disabled while testing core storage features.");
			StackMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("12 Stack Sizes - Experimental", "Multiplier", 1f, Range(0.1f, 100f, "Multiplier applied to each item's native stack size."));
			MaximumStackSize = ((BaseUnityPlugin)this).Config.Bind<int>("12 Stack Sizes - Experimental", "Maximum", 10000, new ConfigDescription("Hard safety cap.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100000), Array.Empty<object>()));
			StackOverrides = ((BaseUnityPlugin)this).Config.Bind<string>("12 Stack Sizes - Experimental", "PerItemOverrides", string.Empty, "Comma-separated prefab/shared-name values, for example Wood=200,Stone=150.");
			AutoRefuelEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("13 Automatic Refueling", "Enabled", false, "Master switch for automatic refueling and processing input loading. Disabled by default.");
			AutoRefuelFires = ((BaseUnityPlugin)this).Config.Bind<bool>("13 Automatic Refueling", "FiresAndTorches", true, "Refill player-built fires, hearths, torches, and other standard Fireplace devices.");
			AutoRefuelProcessingFuel = ((BaseUnityPlugin)this).Config.Bind<bool>("13 Automatic Refueling", "ProcessingFuel", true, "Refill fuel in standard Smelter devices, including smelters, blast furnaces, and eitr refineries.");
			AutoRefuelProcessingInputs = ((BaseUnityPlugin)this).Config.Bind<bool>("13 Automatic Refueling", "ProcessingInputs", false, "Load processable materials into standard Smelter devices, including kilns, furnaces, windmills, and spinning wheels.");
			AutoRefuelRange = ((BaseUnityPlugin)this).Config.Bind<float>("13 Automatic Refueling", "ChestRange", 20f, Range(1f, 100f, "Maximum distance from a device to eligible supply chests."));
			AutoRefuelIntervalSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("13 Automatic Refueling", "IntervalSeconds", 10f, Range(1f, 120f, "Seconds between automatic refueling passes."));
			AutoRefuelMaximumDevicesPerCycle = ((BaseUnityPlugin)this).Config.Bind<int>("13 Automatic Refueling", "MaximumDevicesPerCycle", 50, new ConfigDescription("Performance limit applied separately to fires and processing devices on each pass.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 500), Array.Empty<object>()));
			AutoRefuelMaximumItemsPerDevice = ((BaseUnityPlugin)this).Config.Bind<int>("13 Automatic Refueling", "MaximumItemsPerDevicePerCycle", 10, new ConfigDescription("Maximum combined fuel and input items transferred into one device per pass.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
			AutoRefuelFireThresholdPercent = ((BaseUnityPlugin)this).Config.Bind<float>("13 Automatic Refueling", "FireRefillBelowPercent", 25f, Range(0f, 100f, "Begin refilling a fire or torch when fuel is at or below this percentage."));
			AutoRefuelFireTargetPercent = ((BaseUnityPlugin)this).Config.Bind<float>("13 Automatic Refueling", "FireRefillToPercent", 100f, Range(0f, 100f, "Target fuel percentage for fires and torches."));
			AutoRefuelProcessingFuelThresholdPercent = ((BaseUnityPlugin)this).Config.Bind<float>("13 Automatic Refueling", "ProcessingFuelRefillBelowPercent", 25f, Range(0f, 100f, "Begin refilling processing fuel at or below this percentage."));
			AutoRefuelProcessingFuelTargetPercent = ((Ba