Decompiled source of ValheimQol v0.0.9

ValheimQol/ValheimQoL.dll

Decompiled 5 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis;
using ServerSync;
using Splatform;
using Steamworks;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: AssemblyFileVersion("0.0.9")]
[assembly: Guid("E74EB49A-461D-48EA-85BC-F462D60C98C4")]
[assembly: ComVisible(false)]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyProduct("ValheimQoL")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyDescription("Configurable quality-of-life patches for Valheim.")]
[assembly: AssemblyTitle("ValheimQoL")]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: CompilationRelaxations(8)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("Marlthon")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.9.0")]
[module: <77e5158d-c0db-4a00-8e6f-3772a98c63b9>RefSafetyRules(11)]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[<e057d413-f32e-43db-88ae-e78a5554fda6>Embedded]
	internal sealed class <e057d413-f32e-43db-88ae-e78a5554fda6>EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[<e057d413-f32e-43db-88ae-e78a5554fda6>Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class <1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public <1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public <1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[<e057d413-f32e-43db-88ae-e78a5554fda6>Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class <54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public <54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[<e057d413-f32e-43db-88ae-e78a5554fda6>Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class <77e5158d-c0db-4a00-8e6f-3772a98c63b9>RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public <77e5158d-c0db-4a00-8e6f-3772a98c63b9>RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ValheimQoL
{
	[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)]
	[BepInPlugin("marlthon.ValheimQoL", "ValheimQoL", "0.0.9")]
	[<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)]
	public sealed class ValheimQoLPlugin : BaseUnityPlugin
	{
		[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(0)]
		private sealed class ConfigurationManagerAttributes
		{
			public bool? Browsable = false;
		}

		internal const string ModName = "ValheimQoL";

		internal const string ModVersion = "0.0.9";

		internal const string Author = "marlthon";

		internal const string ModGUID = "marlthon.ValheimQoL";

		private static readonly string ConfigFileName = "marlthon.ValheimQoL.cfg";

		private static readonly string ConfigFileFullPath;

		private readonly Harmony _harmony = new Harmony("marlthon.ValheimQoL");

		[<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(2)]
		private FileSystemWatcher _configWatcher;

		internal static readonly ManualLogSource Log;

		private static readonly ConfigSync ConfigSync;

		private static ConfigEntry<bool> _serverConfigLocked;

		internal static bool IsLocalPlayerAdmin()
		{
			return ConfigSync != null && ConfigSync.IsAdmin;
		}

		private void Awake()
		{
			_serverConfigLocked = config("General", "Force Server Config", value: true, "Locks synchronized gameplay settings to the server. Example: when true, clients use the server's DayNight and WearNTear values instead of their local values.");
			ConfigSync.AddLockingConfigEntry<bool>(_serverConfigLocked);
			Patch_Base.InitConfig(this);
			Patch_BulkHarvest.InitConfig(this);
			MenuQuickJoin.InitConfig(this);
			Patch_Clock.InitConfig(this);
			Patch_Container.InitConfig(this);
			Patch_DayNight.InitConfig(this);
			Patch_DeleteItem.InitConfig(this);
			Patch_HoverTimers.InitConfig(this);
			Patch_InfiniteFuel.InitConfig(this);
			Patch_ItemBalance.InitConfig(this);
			Patch_MapShare.InitConfig(this);
			NearbyContainerManager.InitConfig(this);
			Patch_PlantGrid.InitConfig(this);
			Patch_PortalProtection.InitConfig(this);
			Patch_Server.InitConfig(this);
			Patch_Smelter.InitConfig(this);
			Patch_ServerQoLFeatures.InitConfig(this);
			Patch_SwimmingEquipment.InitConfig(this);
			Patch_WearNTear.InitConfig(this);
			Patch_Workbench.InitConfig(this);
			_harmony.PatchAll();
			Patch_DayNight.LogHarmonyPatchStatus();
			SetupConfigWatcher();
			Log.LogInfo((object)"ValheimQoL 0.0.9 carregado.");
		}

		private void OnDestroy()
		{
			if (_configWatcher != null)
			{
				_configWatcher.Changed -= ReadConfigValues;
				_configWatcher.Created -= ReadConfigValues;
				_configWatcher.Renamed -= ReadConfigValues;
				_configWatcher.Dispose();
				_configWatcher = null;
			}
			Patch_InfiniteFuel.Shutdown();
			Patch_ServerQoLFeatures.Shutdown();
			Patch_Server.Shutdown();
			_harmony.UnpatchSelf();
		}

		private void SetupConfigWatcher()
		{
			string directoryName = Path.GetDirectoryName(ConfigFileFullPath);
			string fileName = Path.GetFileName(ConfigFileFullPath);
			if (!string.IsNullOrEmpty(directoryName) && !string.IsNullOrEmpty(fileName))
			{
				_configWatcher = new FileSystemWatcher(directoryName, fileName)
				{
					IncludeSubdirectories = false,
					SynchronizingObject = ThreadingHelper.SynchronizingObject,
					EnableRaisingEvents = true
				};
				_configWatcher.Changed += ReadConfigValues;
				_configWatcher.Created += ReadConfigValues;
				_configWatcher.Renamed += ReadConfigValues;
			}
		}

		private void ReadConfigValues(object sender, FileSystemEventArgs e)
		{
			if (!File.Exists(ConfigFileFullPath))
			{
				return;
			}
			try
			{
				((BaseUnityPlugin)this).Config.Reload();
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Falha ao recarregar a configuração: " + ex));
			}
		}

		public ConfigEntry<T> config<[<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(2)] T>(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			ConfigDescription val = new ConfigDescription(description.Description + (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]"), description.AcceptableValues, new object[1]
			{
				new ConfigurationManagerAttributes()
			});
			ConfigEntry<T> val2 = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, val);
			SyncedConfigEntry<T> syncedConfigEntry = ConfigSync.AddConfigEntry<T>(val2);
			syncedConfigEntry.SynchronizedConfig = synchronizedSetting;
			return val2;
		}

		public ConfigEntry<T> config<[<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(2)] T>(string group, string name, T value, string description, bool synchronizedSetting = true)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			return config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting);
		}

		internal ConfigEntry<T> configWithSyncReference<[<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(2)] T>(string group, string name, T value, string description, bool synchronizedSetting, out SyncedConfigEntry<T> syncedConfigEntry)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			ConfigDescription val = new ConfigDescription(description + (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]"), (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes()
			});
			ConfigEntry<T> val2 = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, val);
			syncedConfigEntry = ConfigSync.AddConfigEntry<T>(val2);
			syncedConfigEntry.SynchronizedConfig = synchronizedSetting;
			return val2;
		}

		static ValheimQoLPlugin()
		{
			string configPath = Paths.ConfigPath;
			char directorySeparatorChar = Path.DirectorySeparatorChar;
			ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName;
			Log = Logger.CreateLogSource("ValheimQoL");
			ConfigSync = new ConfigSync("marlthon.ValheimQoL")
			{
				DisplayName = "ValheimQoL",
				CurrentVersion = "0.0.9",
				MinimumRequiredVersion = "0.0.9",
				ModRequired = true
			};
			_serverConfigLocked = null;
		}
	}
	[<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)]
	[HarmonyPatch]
	[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)]
	public static class Patch_Base
	{
		private static ConfigEntry<float> CarryWeightBase;

		private static ConfigEntry<float> MegingjordBonus;

		private static ConfigEntry<float> BuildDistance;

		private static ConfigEntry<float> AutoPickupRange;

		private static ConfigEntry<float> InteractDistance;

		private static bool _appliedRuntimeTuningOnce;

		private const string MegingjordPrefab = "BeltStrength";

		private const string MegingjordToken = "$item_beltstrength";

		private const float VanillaCarryBase = 300f;

		private const float VanillaMegiBonus = 150f;

		public static void InitConfig(ValheimQoLPlugin plugin)
		{
			CarryWeightBase = plugin.config("BaseTweaks", "CarryWeightBase", 300f, "Sets the player's base carry capacity. Example: 450 allows carrying 450 weight before applying the Megingjord bonus.");
			MegingjordBonus = plugin.config("BaseTweaks", "MegingjordBonus", 150f, "Sets the carry capacity added by Megingjord. Example: 300 combined with CarryWeightBase=300 provides 600 total capacity.");
			BuildDistance = plugin.config("BaseTweaks", "BuildDistance", 5f, "Sets the maximum building placement distance in meters. Example: 10 allows placing pieces up to 10 meters away.");
			AutoPickupRange = plugin.config("BaseTweaks", "AutoPickupRange", 2f, "Sets the automatic item pickup radius in meters. Example: 4 collects nearby drops within 4 meters.");
			InteractDistance = plugin.config("BaseTweaks", "InteractDistance", 5f, "Sets the maximum interaction distance in meters. Example: 8 allows opening containers and using objects from up to 8 meters away. Vanilla: 5.");
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "GetMaxCarryWeight")]
		private static void Player_GetMaxCarryWeight_Postfix(Player __instance, ref float __result)
		{
			float num = CarryWeightBase.Value - 300f;
			__result += num;
			if (HasMegingjordEquipped(__instance))
			{
				float num2 = MegingjordBonus.Value - 150f;
				__result += num2;
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "Awake")]
		private static void Player_Awake_Postfix(Player __instance)
		{
			__instance.m_maxPlaceDistance = BuildDistance.Value;
			__instance.m_autoPickupRange = AutoPickupRange.Value;
			__instance.m_maxInteractDistance = InteractDistance.Value;
			if (!_appliedRuntimeTuningOnce && (Object)(object)Player.m_localPlayer == (Object)(object)__instance)
			{
				_appliedRuntimeTuningOnce = true;
				((Character)Player.m_localPlayer).Message((MessageType)1, $"CarryWeight base: {CarryWeightBase.Value:0.#}  |  Megingjord: {MegingjordBonus.Value:0.#}", 0, (Sprite)null, false);
				((Character)Player.m_localPlayer).Message((MessageType)1, $"Build distance: {BuildDistance.Value:0.#}  |  Auto-pickup: {AutoPickupRange.Value:0.#}  |  Interact: {InteractDistance.Value:0.#}", 0, (Sprite)null, false);
			}
		}

		private static bool HasMegingjordEquipped(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				return false;
			}
			List<ItemData> allItems = inventory.GetAllItems();
			foreach (ItemData item in allItems)
			{
				if (item != null && item.m_equipped)
				{
					if ((Object)(object)item.m_dropPrefab != (Object)null && ((Object)item.m_dropPrefab).name == "BeltStrength")
					{
						return true;
					}
					string text = item.m_shared?.m_name;
					if (!string.IsNullOrEmpty(text) && string.Equals(text, "$item_beltstrength", StringComparison.OrdinalIgnoreCase))
					{
						return true;
					}
					if (text != null && text.IndexOf("belt", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						return true;
					}
				}
			}
			return false;
		}
	}
	[<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)]
	[HarmonyPatch]
	[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)]
	internal static class Patch_BulkHarvest
	{
		private const KeyCode HarvestModifierKey = (KeyCode)304;

		private const int ColliderBufferSize = 256;

		private static readonly Collider[] HarvestColliders = (Collider[])(object)new Collider[256];

		private static readonly HashSet<int> ProcessedObjects = new HashSet<int>();

		private static ConfigEntry<bool> Enabled = null;

		private static ConfigEntry<float> HarvestRadius = null;

		private static ConfigEntry<int> MaxTargets = null;

		private static ConfigEntry<bool> HarvestSameTypeOnly = null;

		private static ConfigEntry<bool> IncludeBeehives = null;

		private static ConfigEntry<bool> ShowHoverHint = null;

		internal static void InitConfig(ValheimQoLPlugin plugin)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Expected O, but got Unknown
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Expected O, but got Unknown
			Enabled = plugin.config("BulkHarvest", "Enabled", value: true, "Enables server-controlled area harvesting with LeftShift + Use. Example: false restores normal single-target harvesting.");
			HarvestRadius = plugin.config("BulkHarvest", "HarvestRadius", 3f, new ConfigDescription("Sets the search radius for bulk harvesting in meters. Example: 3 collects eligible plants and beehives within three meters.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), Array.Empty<object>()));
			MaxTargets = plugin.config("BulkHarvest", "MaxTargets", 50, new ConfigDescription("Limits how many objects one bulk-harvest action can interact with, including the original target. Example: 50 prevents very large farms from being processed in one frame.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 200), Array.Empty<object>()));
			HarvestSameTypeOnly = plugin.config("BulkHarvest", "HarvestSameTypeOnly", value: true, "Controls which nearby objects are harvested. Example: true harvests only the same plant type, while false harvests every eligible plant and beehive in range.");
			IncludeBeehives = plugin.config("BulkHarvest", "IncludeBeehives", value: true, "Includes beehives with available honey in bulk harvesting. Example: LeftShift + Use on one beehive collects honey from nearby ready beehives.");
			ShowHoverHint = plugin.config("BulkHarvest", "ShowHoverHint", value: true, "Adds a LeftShift + Use area-harvest hint to eligible plants and beehives. Example: false hides the hint without disabling bulk harvesting.");
		}

		private static bool IsEnabled()
		{
			return Enabled != null && Enabled.Value;
		}

		private static bool IsHarvestModifierHeld()
		{
			return ZInput.GetKey((KeyCode)304, false);
		}

		private static string GetPrefabName(GameObject gameObject)
		{
			return ((Object)(object)gameObject == (Object)null) ? string.Empty : Utils.GetPrefabName(gameObject);
		}

		[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(2)]
		private static bool IsEligiblePickable(Pickable pickable)
		{
			return (Object)(object)pickable != (Object)null && !pickable.GetPicked() && pickable.GetEnabled != 0;
		}

		[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(2)]
		private static bool IsEligibleBeehive(Beehive beehive)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			return IncludeBeehives != null && IncludeBeehives.Value && (Object)(object)beehive != (Object)null && beehive.GetHoneyLevel() > 0 && PrivateArea.CheckAccess(((Component)beehive).transform.position, 0f, false, false);
		}

		[return: <1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(2)]
		private static Interactable GetInteractable(GameObject gameObject)
		{
			return ((Object)(object)gameObject == (Object)null) ? null : gameObject.GetComponentInParent<Interactable>();
		}

		[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(2)]
		private static bool MatchesRootType(Pickable candidatePickable, Beehive candidateBeehive, Pickable rootPickable, Beehive rootBeehive, [<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(1)] string rootPrefabName)
		{
			if (HarvestSameTypeOnly == null || !HarvestSameTypeOnly.Value)
			{
				return true;
			}
			if ((Object)(object)rootPickable != (Object)null)
			{
				return (Object)(object)candidatePickable != (Object)null && GetPrefabName(((Component)candidatePickable).gameObject) == rootPrefabName;
			}
			return (Object)(object)rootBeehive != (Object)null && (Object)(object)candidateBeehive != (Object)null;
		}

		private static string BuildHoverHint()
		{
			return Localization.instance.Localize("\n[<color=yellow><b>LeftShift + $KEY_Use</b></color>] Harvest nearby");
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "Interact")]
		private static void Player_Interact_Prefix(Player __instance, GameObject go, bool hold, bool alt)
		{
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			if (!IsEnabled() || (Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || (Object)(object)go == (Object)null || hold || ((Character)__instance).InAttack() || ((Character)__instance).InDodge() || !IsHarvestModifierHeld())
			{
				return;
			}
			Interactable interactable = GetInteractable(go);
			Pickable val = (Pickable)(object)((interactable is Pickable) ? interactable : null);
			Beehive val2 = (Beehive)(object)((interactable is Beehive) ? interactable : null);
			if (!IsEligiblePickable(val) && !IsEligibleBeehive(val2))
			{
				return;
			}
			MonoBehaviour val3 = (MonoBehaviour)(object)((interactable is MonoBehaviour) ? interactable : null);
			if ((Object)(object)val3 == (Object)null)
			{
				return;
			}
			string rootPrefabName = (((Object)(object)val == (Object)null) ? string.Empty : GetPrefabName(((Component)val).gameObject));
			float num = ((HarvestRadius == null) ? 3f : Mathf.Clamp(HarvestRadius.Value, 1f, 20f));
			int num2 = ((MaxTargets == null) ? 50 : Mathf.Clamp(MaxTargets.Value, 1, 200));
			int instanceID = ((Object)((Component)val3).gameObject).GetInstanceID();
			ProcessedObjects.Clear();
			ProcessedObjects.Add(instanceID);
			int num3 = Physics.OverlapSphereNonAlloc(((Component)val3).transform.position, num, HarvestColliders, -1, (QueryTriggerInteraction)2);
			int num4 = 1;
			for (int i = 0; i < num3 && num4 < num2; i++)
			{
				Collider val4 = HarvestColliders[i];
				HarvestColliders[i] = null;
				if ((Object)(object)val4 == (Object)null)
				{
					continue;
				}
				Pickable componentInParent = ((Component)val4).GetComponentInParent<Pickable>();
				Beehive val5 = (((Object)(object)componentInParent == (Object)null) ? ((Component)val4).GetComponentInParent<Beehive>() : null);
				MonoBehaviour val6 = (MonoBehaviour)(((Object)(object)componentInParent != (Object)null) ? ((object)componentInParent) : ((object)val5));
				if ((Object)(object)val6 == (Object)null)
				{
					continue;
				}
				int instanceID2 = ((Object)((Component)val6).gameObject).GetInstanceID();
				if (!ProcessedObjects.Add(instanceID2) || !MatchesRootType(componentInParent, val5, val, val2, rootPrefabName))
				{
					continue;
				}
				Interactable val7;
				if (IsEligiblePickable(componentInParent))
				{
					val7 = (Interactable)(object)componentInParent;
				}
				else
				{
					if (!IsEligibleBeehive(val5))
					{
						continue;
					}
					val7 = (Interactable)(object)val5;
				}
				if (val7 != null)
				{
					val7.Interact((Humanoid)(object)__instance, false, alt);
					num4++;
				}
			}
			for (int j = 0; j < num3; j++)
			{
				HarvestColliders[j] = null;
			}
			ProcessedObjects.Clear();
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Pickable), "GetHoverText")]
		private static void Pickable_GetHoverText_Postfix(Pickable __instance, ref string __result)
		{
			if (IsEnabled() && ShowHoverHint != null && ShowHoverHint.Value && !string.IsNullOrEmpty(__result) && IsEligiblePickable(__instance))
			{
				__result += BuildHoverHint();
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Beehive), "GetHoverText")]
		private static void Beehive_GetHoverText_Postfix(Beehive __instance, ref string __result)
		{
			if (IsEnabled() && ShowHoverHint != null && ShowHoverHint.Value && !string.IsNullOrEmpty(__result) && IsEligibleBeehive(__instance))
			{
				__result += BuildHoverHint();
			}
		}
	}
	[HarmonyPatch]
	[<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)]
	[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)]
	internal static class MenuQuickJoin
	{
		[CompilerGenerated]
		private static class <>O
		{
			[<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)]
			public static UnityAction <0>__Connect;
		}

		private static ConfigEntry<bool> Enabled;

		private static ConfigEntry<string> ButtonText;

		private static ConfigEntry<string> ServerAddress;

		private static ConfigEntry<int> ServerPort;

		private static ConfigEntry<string> ServerPassword;

		public static void InitConfig(ValheimQoLPlugin plugin)
		{
			Enabled = plugin.config("QuickConnect", "Enabled", value: false, "Adds a direct-connect button to the main menu. Example: true displays the button; false keeps the vanilla menu unchanged.", synchronizedSetting: false);
			ButtonText = plugin.config("QuickConnect", "ButtonText", "My Server", "Sets the text shown on the direct-connect button. Example: My Community Server.", synchronizedSetting: false);
			ServerAddress = plugin.config("QuickConnect", "ServerAddress", "127.0.0.1", "Sets the server IP address or domain name. Example: 192.168.1.50 or play.example.com.", synchronizedSetting: false);
			ServerPort = plugin.config("QuickConnect", "ServerPort", 2456, "Sets the main Valheim server port. Example: 2456 is the standard game port.", synchronizedSetting: false);
			ServerPassword = plugin.config("QuickConnect", "ServerPassword", "", "Sets the server password. Example: Viking123. Leave empty for no password. Warning: this value is stored as plain text in the cfg file.", synchronizedSetting: false);
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(FejdStartup), "SetupGui")]
		private static void SetupGui_Postfix(FejdStartup __instance)
		{
			if (Enabled.Value)
			{
				TryCreateButton(__instance);
			}
		}

		private static void TryCreateButton(FejdStartup startup)
		{
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Expected O, but got Unknown
			if ((Object)(object)startup == (Object)null || (Object)(object)startup.m_menuList == (Object)null)
			{
				return;
			}
			Button[] componentsInChildren = startup.m_menuList.GetComponentsInChildren<Button>(true);
			if (componentsInChildren.Any([<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(0)] (Button button) => (Object)(object)button != (Object)null && string.Equals(((Object)button).name, "ValheimQoL_QuickJoin", StringComparison.Ordinal)))
			{
				return;
			}
			Button val = ((IEnumerable<Button>)componentsInChildren).FirstOrDefault((Func<Button, bool>)([<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(0)] (Button button) => (Object)(object)button != (Object)null));
			if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).transform.parent == (Object)null)
			{
				ValheimQoLPlugin.Log.LogWarning((object)"[QuickConnect] Nenhum botão vanilla foi encontrado para servir como modelo.");
				return;
			}
			GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, ((Component)val).transform.parent, false);
			((Object)val2).name = "ValheimQoL_QuickJoin";
			val2.transform.SetSiblingIndex(0);
			SetButtonText(val2, ButtonText.Value);
			Button component = val2.GetComponent<Button>();
			if ((Object)(object)component == (Object)null)
			{
				Object.Destroy((Object)(object)val2);
				return;
			}
			((UnityEventBase)component.onClick).RemoveAllListeners();
			ButtonClickedEvent onClick = component.onClick;
			object obj = <>O.<0>__Connect;
			if (obj == null)
			{
				UnityAction val3 = Connect;
				<>O.<0>__Connect = val3;
				obj = (object)val3;
			}
			((UnityEvent)onClick).AddListener((UnityAction)obj);
			Button[] array = startup.m_menuButtons ?? Array.Empty<Button>();
			if (!array.Contains(component))
			{
				startup.m_menuButtons = ((IEnumerable<Button>)(object)new Button[1] { component }).Concat(array).ToArray();
			}
		}

		private static void Connect()
		{
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			FejdStartup instance = FejdStartup.m_instance;
			if (!((Object)(object)instance == (Object)null))
			{
				string text = ((ServerAddress.Value == null) ? string.Empty : ServerAddress.Value.Trim());
				int num = Mathf.Clamp(ServerPort.Value, 1, 65535);
				if (string.IsNullOrEmpty(text))
				{
					ValheimQoLPlugin.Log.LogWarning((object)"[QuickConnect] ServerAddress está vazio no arquivo de configuração.");
					return;
				}
				FejdStartup.ServerPassword = ServerPassword.Value ?? string.Empty;
				instance.m_queuedJoinServer = new ServerJoinData(new ServerJoinDataDedicated(text + ":" + num));
				instance.HideAll();
				instance.ShowCharacterSelection();
			}
		}

		private static void SetButtonText(GameObject buttonObject, string text)
		{
			string text2 = (string.IsNullOrWhiteSpace(text) ? "Meu Servidor" : text.Trim());
			Text[] componentsInChildren = buttonObject.GetComponentsInChildren<Text>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				if ((Object)(object)componentsInChildren[i] != (Object)null)
				{
					componentsInChildren[i].text = text2;
				}
			}
			TMP_Text[] componentsInChildren2 = buttonObject.GetComponentsInChildren<TMP_Text>(true);
			for (int j = 0; j < componentsInChildren2.Length; j++)
			{
				if ((Object)(object)componentsInChildren2[j] != (Object)null)
				{
					componentsInChildren2[j].text = text2;
				}
			}
		}
	}
	[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)]
	[<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)]
	[HarmonyPatch]
	public static class Patch_Clock
	{
		private static ConfigEntry<bool> ClockEnabled;

		private static ConfigEntry<float> ClockUpdateInterval;

		private static ConfigEntry<int> ClockFontSize;

		private static ConfigEntry<bool> ShowRealTime;

		private static ConfigEntry<bool> RealTimeUse24H;

		private static ConfigEntry<int> RealTimeFontSize;

		private static float _nextUpdateTime;

		private static GameObject _clockRoot;

		private static TextMeshProUGUI _timeText;

		private static TextMeshProUGUI _realText;

		private const float ROOT_OFFSET_Y = -60f;

		private const float ROOT_WIDTH = 600f;

		private const float ROOT_HEIGHT = 120f;

		public static void InitConfig(ValheimQoLPlugin plugin)
		{
			ClockEnabled = plugin.config("Clock", "ClockEnabled", value: true, "Shows the in-game day and time on the HUD. Example: false completely hides the custom clock.");
			ClockUpdateInterval = plugin.config("Clock", "ClockUpdateInterval", 1f, "Sets how often the clock refreshes, in seconds. Example: 0.5 updates twice per second; 2 updates every two seconds.");
			ClockFontSize = plugin.config("Clock", "ClockFontSize", 40, "Sets the font size of the in-game clock. Example: 32 produces smaller text; 48 produces larger text.");
			ShowRealTime = plugin.config("Clock", "ShowRealTime", value: true, "Shows the computer's local time below the in-game clock. Example: false displays only the Valheim time.");
			RealTimeUse24H = plugin.config("Clock", "RealTimeUse24H", value: true, "Selects the real-time format. Example: true displays 18:30; false displays 06:30 PM.");
			RealTimeFontSize = plugin.config("Clock", "RealTimeFontSize", 28, "Sets the font size of the real-world clock. Example: 20 makes the secondary clock less prominent.");
		}

		[HarmonyPatch(typeof(Hud), "Update")]
		[HarmonyPostfix]
		private static void Hud_Update_Postfix(Hud __instance)
		{
			if (!ClockEnabled.Value)
			{
				if ((Object)(object)_clockRoot != (Object)null)
				{
					Object.Destroy((Object)(object)_clockRoot);
				}
				return;
			}
			if ((Object)(object)_clockRoot == (Object)null)
			{
				CreateClock(__instance);
			}
			if (!(Time.unscaledTime < _nextUpdateTime))
			{
				_nextUpdateTime = Time.unscaledTime + Mathf.Max(0.2f, ClockUpdateInterval.Value);
				UpdateClockTexts();
			}
		}

		private static void CreateClock(Hud hud)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			//IL_004a: 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_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			_clockRoot = new GameObject("MarlthonClockRoot");
			_clockRoot.transform.SetParent(hud.m_rootObject.transform, false);
			RectTransform val = _clockRoot.AddComponent<RectTransform>();
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0.5f, 1f);
			val.anchorMax = val2;
			val.anchorMin = val2;
			val.anchoredPosition = new Vector2(0f, -60f);
			val.sizeDelta = new Vector2(600f, 120f);
			TMP_FontAsset font = ((IEnumerable<TMP_FontAsset>)Resources.FindObjectsOfTypeAll<TMP_FontAsset>()).FirstOrDefault((Func<TMP_FontAsset, bool>)([<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(0)] (TMP_FontAsset f) => ((Object)f).name.ToLower().Contains("norsebold"))) ?? TMP_Settings.defaultFontAsset;
			_timeText = CreateTMP("ClockTime", font, ClockFontSize.Value, new Color(1f, 0.85f, 0.2f));
			((TMP_Text)_timeText).alignment = (TextAlignmentOptions)514;
			((TMP_Text)_timeText).transform.SetParent(_clockRoot.transform, false);
			((TMP_Text)_timeText).rectTransform.anchoredPosition = new Vector2(0f, 10f);
			_realText = CreateTMP("ClockReal", font, RealTimeFontSize.Value, new Color(0.8f, 0.8f, 0.8f));
			((TMP_Text)_realText).alignment = (TextAlignmentOptions)514;
			((TMP_Text)_realText).transform.SetParent(_clockRoot.transform, false);
			((TMP_Text)_realText).rectTransform.anchoredPosition = new Vector2(0f, -30f);
		}

		private static TextMeshProUGUI CreateTMP(string name, TMP_FontAsset font, int size, Color color)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected O, but got Unknown
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Expected O, but got Unknown
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			val.SetActive(false);
			TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>();
			((TMP_Text)val2).font = font ?? TMP_Settings.defaultFontAsset;
			((TMP_Text)val2).fontSize = size;
			((Graphic)val2).color = color;
			((TMP_Text)val2).alignment = (TextAlignmentOptions)514;
			((TMP_Text)val2).textWrappingMode = (TextWrappingModes)0;
			((TMP_Text)val2).richText = true;
			Material val3 = new Material(((TMP_Text)val2).fontSharedMaterial);
			val3.SetFloat("_OutlineWidth", 0.1f);
			val3.SetColor("_OutlineColor", new Color(0f, 0f, 0f, 0.6f));
			((TMP_Text)val2).fontSharedMaterial = val3;
			val.SetActive(true);
			return val2;
		}

		private static void UpdateClockTexts()
		{
			if (!((Object)(object)EnvMan.instance == (Object)null))
			{
				float num = EnvMan.instance.GetDay();
				float dayFraction = EnvMan.instance.GetDayFraction();
				int num2 = Mathf.FloorToInt(dayFraction * 24f);
				int num3 = Mathf.FloorToInt((dayFraction * 24f - (float)num2) * 60f);
				((TMP_Text)_timeText).text = $"Day {num} - {num2:00}:{num3:00}";
				if (ShowRealTime.Value)
				{
					DateTime now = DateTime.Now;
					string text = (RealTimeUse24H.Value ? now.ToString("HH:mm") : now.ToString("hh:mm tt"));
					((TMP_Text)_realText).text = "<color=#CCCCCC>Real Life: " + text + "</color>";
				}
				else
				{
					((TMP_Text)_realText).text = "";
				}
			}
		}
	}
	[HarmonyPatch(typeof(SE_Rested), "GetNearbyComfortPieces")]
	public static class SE_Rested_GetNearbyComfortPieces_Patch
	{
		[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			foreach (CodeInstruction instruction in instructions)
			{
				if (instruction.opcode == OpCodes.Ldc_R4 && (float)instruction.operand == 10f)
				{
					instruction.operand = 20f;
				}
				yield return instruction;
			}
		}
	}
	[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)]
	[HarmonyPatch(typeof(EffectArea), "Awake")]
	[<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)]
	public static class EffectArea_Awake_Patch
	{
		private static void Postfix(EffectArea __instance)
		{
			//IL_0002: 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_000a: Invalid comparison between Unknown and I4
			if ((__instance.m_type & 1) > 0)
			{
				UpdateFireEffectRadius(__instance, 20f);
			}
		}

		private static void UpdateFireEffectRadius(EffectArea area, float newRadius)
		{
			SphereCollider component = ((Component)area).GetComponent<SphereCollider>();
			if ((Object)(object)component != (Object)null)
			{
				component.radius = newRadius;
			}
		}
	}
	[HarmonyPatch]
	[<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)]
	[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)]
	public static class Patch_Container
	{
		private static ConfigEntry<bool> ChestControl;

		private static ConfigEntry<int> PrivateRows;

		private static ConfigEntry<int> PrivateCols;

		private static ConfigEntry<int> WoodRows;

		private static ConfigEntry<int> WoodCols;

		private static ConfigEntry<int> IronRows;

		private static ConfigEntry<int> IronCols;

		private static ConfigEntry<int> BlackmetalRows;

		private static ConfigEntry<int> BlackmetalCols;

		private static ConfigEntry<int> BarrelRows;

		private static ConfigEntry<int> BarrelCols;

		private static ConfigEntry<int> GraustenRows;

		private static ConfigEntry<int> GraustenCols;

		private static ConfigEntry<int> WardrobeRows;

		private static ConfigEntry<int> WardrobeCols;

		public static void InitConfig(ValheimQoLPlugin plugin)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Expected O, but got Unknown
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Expected O, but got Unknown
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Expected O, but got Unknown
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Expected O, but got Unknown
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Expected O, but got Unknown
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Expected O, but got Unknown
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Expected O, but got Unknown
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Expected O, but got Unknown
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Expected O, but got Unknown
			//IL_0202: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Expected O, but got Unknown
			//IL_022f: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Expected O, but got Unknown
			//IL_025e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: Expected O, but got Unknown
			//IL_028b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0296: Expected O, but got Unknown
			ChestControl = plugin.config("ContainerControl", "EnableChestControl", value: true, "Enables custom sizes for supported vanilla containers. Example: false keeps every chest at its vanilla size.");
			PrivateRows = plugin.config("Chests", "PrivateChestRows", 2, new ConfigDescription("Sets the row count of the personal chest. Example: 4 rows with PrivateChestCols=3 provides 12 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 20), Array.Empty<object>()));
			PrivateCols = plugin.config("Chests", "PrivateChestCols", 3, new ConfigDescription("Sets the column count of the personal chest. Example: 6 columns with PrivateChestRows=2 provides 12 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(3, 8), Array.Empty<object>()));
			WoodRows = plugin.config("Chests", "WoodChestRows", 2, new ConfigDescription("Sets the row count of the wooden chest. Example: 4 rows with WoodChestCols=5 provides 20 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 10), Array.Empty<object>()));
			WoodCols = plugin.config("Chests", "WoodChestCols", 5, new ConfigDescription("Sets the column count of the wooden chest. Example: 8 columns with WoodChestRows=2 provides 16 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 8), Array.Empty<object>()));
			IronRows = plugin.config("Chests", "IronChestRows", 4, new ConfigDescription("Sets the row count of the reinforced chest. Example: 6 rows with IronChestCols=6 provides 36 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 20), Array.Empty<object>()));
			IronCols = plugin.config("Chests", "IronChestCols", 6, new ConfigDescription("Sets the column count of the reinforced chest. Example: 8 columns with IronChestRows=4 provides 32 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(3, 8), Array.Empty<object>()));
			BlackmetalRows = plugin.config("Chests", "BlackmetalChestRows", 4, new ConfigDescription("Sets the row count of the black metal chest. Example: 6 rows with BlackmetalChestCols=8 provides 48 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(3, 20), Array.Empty<object>()));
			BlackmetalCols = plugin.config("Chests", "BlackmetalChestCols", 8, new ConfigDescription("Sets the column count of the black metal chest. Example: 8 columns with BlackmetalChestRows=4 provides 32 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(6, 8), Array.Empty<object>()));
			BarrelRows = plugin.config("Chests", "BarrelChestRows", 3, new ConfigDescription("Sets the row count of the barrel container. Example: 4 rows with BarrelChestCols=3 provides 12 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 10), Array.Empty<object>()));
			BarrelCols = plugin.config("Chests", "BarrelChestCols", 3, new ConfigDescription("Sets the column count of the barrel container. Example: 6 columns with BarrelChestRows=3 provides 18 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(3, 8), Array.Empty<object>()));
			GraustenRows = plugin.config("Chests", "GraustenChestRows", 5, new ConfigDescription("Sets the row count of the Grausten Chest. Vanilla: 5. Example: 6 rows with GraustenChestCols=8 provides 48 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 20), Array.Empty<object>()));
			GraustenCols = plugin.config("Chests", "GraustenChestCols", 8, new ConfigDescription("Sets the column count of the Grausten Chest. Vanilla: 8. Example: 8 columns with GraustenChestRows=6 provides 48 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(3, 8), Array.Empty<object>()));
			WardrobeRows = plugin.config("Chests", "WardrobeRows", 10, new ConfigDescription("Sets the row count of the Wardrobe. Vanilla: 10. Example: 12 rows with WardrobeCols=5 provides 60 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 20), Array.Empty<object>()));
			WardrobeCols = plugin.config("Chests", "WardrobeCols", 5, new ConfigDescription("Sets the column count of the Wardrobe. Vanilla: 5. Example: 8 columns with WardrobeRows=10 provides 80 slots.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(3, 8), Array.Empty<object>()));
			SubscribeChanges();
		}

		private static void SubscribeChanges()
		{
			Watch<bool>(ChestControl);
			Watch<int>(PrivateRows);
			Watch<int>(PrivateCols);
			Watch<int>(WoodRows);
			Watch<int>(WoodCols);
			Watch<int>(IronRows);
			Watch<int>(IronCols);
			Watch<int>(BlackmetalRows);
			Watch<int>(BlackmetalCols);
			Watch<int>(BarrelRows);
			Watch<int>(BarrelCols);
			Watch<int>(GraustenRows);
			Watch<int>(GraustenCols);
			Watch<int>(WardrobeRows);
			Watch<int>(WardrobeCols);
			static void Watch<T>([<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(new byte[] { 1, 0 })] ConfigEntry<T> entry)
			{
				if (entry != null)
				{
					entry.SettingChanged += [<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(0)] (object _, EventArgs _) =>
					{
						UpdateAllChests();
					};
				}
			}
		}

		[HarmonyPatch(typeof(Container), "Awake")]
		[HarmonyPostfix]
		private static void Container_Awake_Postfix(Container __instance)
		{
			TryResize(__instance);
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Container), "Interact")]
		private static void Container_Interact_Prefix(Container __instance, Humanoid character, bool hold, bool alt)
		{
			TryResize(__instance);
		}

		private static void TryResize(Container container)
		{
			if (!ChestControl.Value || (Object)(object)container == (Object)null || ((Object)container).name.StartsWith("Treasure"))
			{
				return;
			}
			ZNetView nview = container.m_nview;
			if ((Object)(object)nview == (Object)null || !nview.IsValid() || nview.GetZDO().GetLong(ZDOVars.s_creator, 0L) == 0)
			{
				return;
			}
			Inventory inventory = container.GetInventory();
			if (inventory != null)
			{
				switch (((Object)((Component)container).transform.root).name.Trim().Replace("(Clone)", ""))
				{
				case "piece_chest_private":
					inventory.m_height = PrivateRows.Value;
					inventory.m_width = PrivateCols.Value;
					break;
				case "piece_chest_wood":
					inventory.m_height = WoodRows.Value;
					inventory.m_width = WoodCols.Value;
					break;
				case "piece_chest":
					inventory.m_height = IronRows.Value;
					inventory.m_width = IronCols.Value;
					break;
				case "piece_chest_blackmetal":
					inventory.m_height = BlackmetalRows.Value;
					inventory.m_width = BlackmetalCols.Value;
					break;
				case "piece_chest_barrel":
					inventory.m_height = BarrelRows.Value;
					inventory.m_width = BarrelCols.Value;
					break;
				case "piece_chest_grausten":
					ApplyConfiguredSize(inventory, GraustenRows.Value, GraustenCols.Value);
					break;
				case "piece_chest_warderobe":
					ApplyConfiguredSize(inventory, WardrobeRows.Value, WardrobeCols.Value);
					break;
				}
			}
		}

		private static void ApplyConfiguredSize(Inventory inventory, int rows, int columns)
		{
			inventory.m_height = rows;
			inventory.m_width = columns;
		}

		private static void UpdateAllChests()
		{
			if (!ChestControl.Value)
			{
				return;
			}
			Container[] array = Resources.FindObjectsOfTypeAll<Container>();
			foreach (Container val in array)
			{
				if (!((Object)(object)val == (Object)null) && !((Object)val).name.StartsWith("Treasure"))
				{
					ZNetView nview = val.m_nview;
					if (!((Object)(object)nview == (Object)null) && nview.IsValid() && nview.GetZDO().GetLong(ZDOVars.s_creator, 0L) != 0)
					{
						TryResize(val);
					}
				}
			}
			ValheimQoLPlugin.Log.LogInfo((object)"[ValheimQoL/Container] Tamanhos dos baús atualizados.");
		}
	}
	[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)]
	[HarmonyPatch]
	[<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)]
	public static class Patch_DayNight
	{
		private const float DefaultDayDurationMinutes = 21f;

		private const float DefaultNightDurationMinutes = 9f;

		private const long FallbackVanillaCycleLengthSeconds = 1800L;

		private const double TimeSkipDurationSeconds = 12.0;

		private const double RuntimeVerificationDurationSeconds = 10.0;

		private static ConfigEntry<bool> Enabled = null;

		private static ConfigEntry<float> DayDurationMinutes = null;

		private static ConfigEntry<float> NightDurationMinutes = null;

		private static long _vanillaCycleLengthSeconds = 1800L;

		private static bool? _lastLoggedDayState;

		private static bool _loggedFirstRescaleExecution;

		private static double _phaseStartedAtNetTime;

		private static bool _phaseStartedAtBoundary;

		private static bool _phaseMeasurementInvalidated;

		private static bool? _verificationPhase;

		private static double _verificationStartedAtNetTime;

		private static float _verificationStartedAtMappedFraction;

		private static bool _runtimeVerificationCompleted;

		public static void InitConfig(ValheimQoLPlugin plugin)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Expected O, but got Unknown
			RemoveLegacySettings(plugin);
			Enabled = plugin.config("DayNight", "Enabled", value: false, "Enables custom real-time day and night durations. Example: false restores Valheim's original cycle duration and time mapping.");
			DayDurationMinutes = plugin.config("DayNight", "DayDurationMinutes", 21f, new ConfigDescription("Sets how many real minutes daytime lasts. Valheim's vanilla daytime duration is 21 real minutes.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 120f), Array.Empty<object>()));
			NightDurationMinutes = plugin.config("DayNight", "NightDurationMinutes", 9f, new ConfigDescription("Sets how many real minutes nighttime lasts. Valheim's vanilla nighttime duration is 9 real minutes.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 120f), Array.Empty<object>()));
			Enabled.SettingChanged += OnConfigChanged;
			DayDurationMinutes.SettingChanged += OnConfigChanged;
			NightDurationMinutes.SettingChanged += OnConfigChanged;
		}

		internal static void LogHarmonyPatchStatus()
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(EnvMan), "RescaleDayFraction", (Type[])null, (Type[])null);
			Patches patchInfo = Harmony.GetPatchInfo((MethodBase)methodInfo);
			bool flag = false;
			if (patchInfo != null)
			{
				for (int i = 0; i < patchInfo.Prefixes.Count; i++)
				{
					if (patchInfo.Prefixes[i].owner == "marlthon.ValheimQoL")
					{
						flag = true;
						break;
					}
				}
			}
			ValheimQoLPlugin.Log.LogInfo((object)("[DayNight] Harmony status: EnvMan.RescaleDayFraction prefix registered=" + flag + "."));
		}

		private static void RemoveLegacySettings(ValheimQoLPlugin plugin)
		{
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Expected O, but got Unknown
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Expected O, but got Unknown
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Expected O, but got Unknown
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Expected O, but got Unknown
			string configFilePath = ((BaseUnityPlugin)plugin).Config.ConfigFilePath;
			if (!File.Exists(configFilePath))
			{
				return;
			}
			bool flag = false;
			bool flag2 = false;
			try
			{
				string[] array = File.ReadAllLines(configFilePath);
				bool flag3 = false;
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i].Trim();
					if (text.StartsWith("[", StringComparison.Ordinal) && text.EndsWith("]", StringComparison.Ordinal))
					{
						flag3 = text.Equals("[DayNight]", StringComparison.Ordinal);
					}
					else if (flag3 && !text.StartsWith("#", StringComparison.Ordinal))
					{
						int num = text.IndexOf('=');
						if (num > 0)
						{
							string text2 = text.Substring(0, num).Trim();
							flag |= text2.Equals("CycleLengthSec", StringComparison.Ordinal);
							flag2 |= text2.Equals("DayFractionOfCycle", StringComparison.Ordinal);
						}
					}
				}
			}
			catch (Exception ex)
			{
				ValheimQoLPlugin.Log.LogWarning((object)("[DayNight] Could not inspect legacy configuration entries: " + ex.Message));
				return;
			}
			if (flag || flag2)
			{
				if (flag)
				{
					ConfigDefinition val = new ConfigDefinition("DayNight", "CycleLengthSec");
					((BaseUnityPlugin)plugin).Config.Bind<int>(val, 1800, new ConfigDescription("Obsolete DayNight setting.", (AcceptableValueBase)null, Array.Empty<object>()));
					((BaseUnityPlugin)plugin).Config.Remove(val);
				}
				if (flag2)
				{
					ConfigDefinition val2 = new ConfigDefinition("DayNight", "DayFractionOfCycle");
					((BaseUnityPlugin)plugin).Config.Bind<float>(val2, 0.5f, new ConfigDescription("Obsolete DayNight setting.", (AcceptableValueBase)null, Array.Empty<object>()));
					((BaseUnityPlugin)plugin).Config.Remove(val2);
				}
				((BaseUnityPlugin)plugin).Config.Save();
				ValheimQoLPlugin.Log.LogInfo((object)"[DayNight] Removed obsolete CycleLengthSec and DayFractionOfCycle settings. The new settings use explicit real-time minutes.");
			}
		}

		private static void OnConfigChanged(object sender, EventArgs eventArgs)
		{
			ApplySettings(EnvMan.instance);
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(EnvMan), "Awake")]
		private static void EnvMan_Awake_Postfix(EnvMan __instance)
		{
			_vanillaCycleLengthSeconds = Math.Max(60L, __instance.m_dayLengthSec);
			ApplySettings(__instance);
		}

		[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(2)]
		private static void ApplySettings(EnvMan envMan)
		{
			if (!((Object)(object)envMan == (Object)null))
			{
				_lastLoggedDayState = null;
				_loggedFirstRescaleExecution = false;
				_phaseStartedAtNetTime = 0.0;
				_phaseStartedAtBoundary = false;
				_phaseMeasurementInvalidated = false;
				_verificationPhase = null;
				_verificationStartedAtNetTime = 0.0;
				_verificationStartedAtMappedFraction = 0f;
				_runtimeVerificationCompleted = false;
				if (!Enabled.Value)
				{
					envMan.m_dayLengthSec = _vanillaCycleLengthSeconds;
					ValheimQoLPlugin.Log.LogInfo((object)("[DayNight] Disabled. Restored Valheim cycle length to " + _vanillaCycleLengthSeconds + " real seconds."));
					return;
				}
				double configuredDaySeconds = GetConfiguredDaySeconds();
				double configuredNightSeconds = GetConfiguredNightSeconds();
				long num = (envMan.m_dayLengthSec = Math.Max(60L, (long)Math.Round(configuredDaySeconds + configuredNightSeconds, MidpointRounding.AwayFromZero)));
				ValheimQoLPlugin.Log.LogInfo((object)("[DayNight] Applied: day=" + FormatMinutes(configuredDaySeconds / 60.0) + " real minute(s), night=" + FormatMinutes(configuredNightSeconds / 60.0) + " real minute(s), cycle=" + FormatMinutes((double)num / 60.0) + " real minute(s), day fraction=" + FormatNumber(GetConfiguredDayFraction()) + "."));
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(EnvMan), "RescaleDayFraction")]
		private static bool EnvMan_RescaleDayFraction_Prefix(float fraction, ref float __result)
		{
			if (!Enabled.Value)
			{
				return true;
			}
			float configuredDayFraction = GetConfiguredDayFraction();
			float num = (1f - configuredDayFraction) * 0.5f;
			float num2 = num + configuredDayFraction;
			float num3 = Mathf.Repeat(fraction, 1f);
			if (num3 < num)
			{
				__result = Mathf.Lerp(0f, 0.25f, num3 / num);
			}
			else if (num3 < num2)
			{
				__result = Mathf.Lerp(0.25f, 0.75f, (num3 - num) / configuredDayFraction);
			}
			else
			{
				__result = Mathf.Lerp(0.75f, 1f, (num3 - num2) / num);
			}
			LogRuntimeState(num3, __result, num, num2);
			return false;
		}

		private static void LogRuntimeState(float rawFraction, float mappedFraction, float morningStart, float eveningStart)
		{
			bool flag = rawFraction >= morningStart && rawFraction < eveningStart;
			UpdateRuntimeVerification(flag, mappedFraction);
			if (!_loggedFirstRescaleExecution)
			{
				_loggedFirstRescaleExecution = true;
				ValheimQoLPlugin.Log.LogInfo((object)("[DayNight] Rescale prefix is executing. Raw fraction=" + FormatNumber(rawFraction) + ", mapped clock fraction=" + FormatNumber(mappedFraction) + "."));
			}
			if (!_lastLoggedDayState.HasValue || _lastLoggedDayState.Value != flag)
			{
				bool hasValue = _lastLoggedDayState.HasValue;
				double num = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetTimeSeconds() : 0.0);
				if (hasValue && _phaseStartedAtBoundary && !_phaseMeasurementInvalidated && num >= _phaseStartedAtNetTime)
				{
					bool value = _lastLoggedDayState.Value;
					double num2 = num - _phaseStartedAtNetTime;
					double num3 = (value ? GetConfiguredDaySeconds() : GetConfiguredNightSeconds());
					ValheimQoLPlugin.Log.LogInfo((object)("[DayNight] Completed " + (value ? "DAY" : "NIGHT") + ": measured duration=" + FormatMinutes(num2 / 60.0) + " real minute(s), configured duration=" + FormatMinutes(num3 / 60.0) + " real minute(s)."));
				}
				_lastLoggedDayState = flag;
				_phaseStartedAtNetTime = num;
				_phaseStartedAtBoundary = hasValue;
				_phaseMeasurementInvalidated = false;
				ValheimQoLPlugin.Log.LogInfo((object)("[DayNight] Phase=" + (flag ? "DAY" : "NIGHT") + ", expected duration=" + FormatMinutes((flag ? GetConfiguredDaySeconds() : GetConfiguredNightSeconds()) / 60.0) + " real minute(s), clock=" + FormatClock(mappedFraction) + "."));
			}
		}

		private static void UpdateRuntimeVerification(bool isDay, float mappedFraction)
		{
			if (_runtimeVerificationCompleted || (Object)(object)ZNet.instance == (Object)null)
			{
				return;
			}
			double timeSeconds = ZNet.instance.GetTimeSeconds();
			if (_phaseMeasurementInvalidated || !_verificationPhase.HasValue || _verificationPhase.Value != isDay)
			{
				_verificationPhase = isDay;
				_verificationStartedAtNetTime = timeSeconds;
				_verificationStartedAtMappedFraction = mappedFraction;
				return;
			}
			double num = timeSeconds - _verificationStartedAtNetTime;
			if (!(num < 10.0))
			{
				double num2 = (isDay ? GetConfiguredDaySeconds() : GetConfiguredNightSeconds());
				double value = num * 720.0 / num2;
				double value2 = (double)Mathf.Repeat(mappedFraction - _verificationStartedAtMappedFraction, 1f) * 24.0 * 60.0;
				_runtimeVerificationCompleted = true;
				ValheimQoLPlugin.Log.LogInfo((object)("[DayNight] 10-second runtime verification: phase=" + (isDay ? "DAY" : "NIGHT") + ", elapsed=" + FormatNumber(num) + " real second(s), clock advanced=" + FormatNumber(value2) + " minute(s), expected=" + FormatNumber(value) + " minute(s)."));
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(EnvMan), "GetMorningStartSec")]
		private static bool EnvMan_GetMorningStartSec_Prefix(EnvMan __instance, int day, ref double __result)
		{
			if (!Enabled.Value)
			{
				return true;
			}
			double morningFraction = GetMorningFraction();
			__result = (double)day * (double)__instance.m_dayLengthSec + (double)__instance.m_dayLengthSec * morningFraction;
			return false;
		}

		[HarmonyPatch(typeof(EnvMan), "SkipToMorning")]
		[HarmonyPrefix]
		private static bool EnvMan_SkipToMorning_Prefix(EnvMan __instance)
		{
			if (!Enabled.Value || (Object)(object)ZNet.instance == (Object)null)
			{
				return true;
			}
			double timeSeconds = ZNet.instance.GetTimeSeconds();
			double morningFraction = GetMorningFraction();
			double num = __instance.m_dayLengthSec;
			int num2 = Mathf.FloorToInt((float)((timeSeconds - num * morningFraction) / num));
			double num3 = (double)(num2 + 1) * num + num * morningFraction;
			__instance.m_skipTime = true;
			__instance.m_skipToTime = num3;
			__instance.m_timeSkipSpeed = (num3 - timeSeconds) / 12.0;
			_phaseMeasurementInvalidated = true;
			return false;
		}

		private static double GetConfiguredDaySeconds()
		{
			float num = ((DayDurationMinutes == null) ? 21f : DayDurationMinutes.Value);
			return Math.Max(30.0, (double)num * 60.0);
		}

		private static double GetConfiguredNightSeconds()
		{
			float num = ((NightDurationMinutes == null) ? 9f : NightDurationMinutes.Value);
			return Math.Max(30.0, (double)num * 60.0);
		}

		private static float GetConfiguredDayFraction()
		{
			double configuredDaySeconds = GetConfiguredDaySeconds();
			double num = configuredDaySeconds + GetConfiguredNightSeconds();
			return (float)(configuredDaySeconds / num);
		}

		private static double GetMorningFraction()
		{
			return (1.0 - (double)GetConfiguredDayFraction()) * 0.5;
		}

		private static string FormatClock(float fraction)
		{
			float num = Mathf.Repeat(fraction, 1f) * 24f * 60f;
			int num2 = Mathf.FloorToInt(num / 60f);
			int num3 = Mathf.FloorToInt(num) % 60;
			return num2.ToString("00", CultureInfo.InvariantCulture) + ":" + num3.ToString("00", CultureInfo.InvariantCulture);
		}

		private static string FormatMinutes(double value)
		{
			return value.ToString("0.###", CultureInfo.InvariantCulture);
		}

		private static string FormatNumber(double value)
		{
			return value.ToString("0.0000", CultureInfo.InvariantCulture);
		}
	}
	[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)]
	[<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)]
	[HarmonyPatch]
	public static class Patch_DeleteItem
	{
		private static ConfigEntry<KeyCode> DeleteKey;

		public static void InitConfig(ValheimQoLPlugin plugin)
		{
			DeleteKey = plugin.config<KeyCode>("DeleteItem", "DeleteKey", (KeyCode)127, "Sets the key that permanently deletes the item currently held by the inventory cursor. Example: Delete uses the keyboard Delete key. Equipped items cannot be deleted.", synchronizedSetting: false);
		}

		[HarmonyPatch(typeof(Player), "Update")]
		[HarmonyPostfix]
		private static void PlayerUpdate_Postfix(Player __instance)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !((Object)(object)InventoryGui.instance == (Object)null) && Input.GetKeyDown(DeleteKey.Value))
			{
				TryDeleteSelectedItem();
			}
		}

		private static void TryDeleteSelectedItem()
		{
			InventoryGui instance = InventoryGui.instance;
			object? value = AccessTools.Field(typeof(InventoryGui), "m_dragItem").GetValue(instance);
			ItemData val = (ItemData)((value is ItemData) ? value : null);
			object? value2 = AccessTools.Field(typeof(InventoryGui), "m_dragInventory").GetValue(instance);
			Inventory val2 = (Inventory)((value2 is Inventory) ? value2 : null);
			object? value3 = AccessTools.Field(typeof(InventoryGui), "m_dragGo").GetValue(instance);
			GameObject val3 = (GameObject)((value3 is GameObject) ? value3 : null);
			if (val == null || val2 == null)
			{
				Player localPlayer = Player.m_localPlayer;
				if (localPlayer != null)
				{
					((Character)localPlayer).Message((MessageType)1, "No item selected.", 0, (Sprite)null, false);
				}
				return;
			}
			if (val.m_equipped)
			{
				Player localPlayer2 = Player.m_localPlayer;
				if (localPlayer2 != null)
				{
					((Character)localPlayer2).Message((MessageType)1, "Cannot delete equipped item!", 0, (Sprite)null, false);
				}
				return;
			}
			val2.RemoveItem(val);
			if ((Object)(object)val3 != (Object)null)
			{
				Object.Destroy((Object)(object)val3);
			}
			AccessTools.Field(typeof(InventoryGui), "m_dragGo").SetValue(instance, null);
			AccessTools.Field(typeof(InventoryGui), "m_dragItem").SetValue(instance, null);
			AccessTools.Field(typeof(InventoryGui), "m_dragInventory").SetValue(instance, null);
			Player localPlayer3 = Player.m_localPlayer;
			if (localPlayer3 != null)
			{
				((Character)localPlayer3).Message((MessageType)1, "Deleted: " + val.m_shared.m_name, 0, (Sprite)null, false);
			}
			Player localPlayer4 = Player.m_localPlayer;
			if ((Object)(object)localPlayer4 != (Object)null)
			{
				instance.m_playerGrid.UpdateInventory(((Humanoid)localPlayer4).GetInventory(), localPlayer4, (ItemData)null);
			}
		}
	}
	[HarmonyPatch]
	[<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)]
	[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)]
	internal static class Patch_HoverTimers
	{
		private const string RemainingLabel = "Remaining";

		private const string PausedLabel = "Paused";

		private static ConfigEntry<bool> ShowPlantTimer;

		private static ConfigEntry<bool> ShowFermenterTimer;

		private static ConfigEntry<bool> ShowCookingTimer;

		private static ConfigEntry<bool> ShowSmelterTimer;

		private static ConfigEntry<bool> ShowBeehiveTimer;

		private static ConfigEntry<bool> ShowPickableTimer;

		private static ConfigEntry<bool> ShowTamingTimer;

		private static ConfigEntry<bool> ShowGrowthTimer;

		internal static void InitConfig(ValheimQoLPlugin plugin)
		{
			ShowPlantTimer = plugin.config("HoverTimers", "ShowPlantTimer", value: true, "Shows the remaining growth time when looking at a healthy plant. Example: false hides only the plant timer.", synchronizedSetting: false);
			ShowFermenterTimer = plugin.config("HoverTimers", "ShowFermenterTimer", value: true, "Shows the remaining processing time when looking at an active fermenter. Example: false hides only the fermenter timer.", synchronizedSetting: false);
			ShowCookingTimer = plugin.config("HoverTimers", "ShowCookingTimer", value: true, "Shows the remaining cooking time for every uncooked food slot on Cooking Stations and Stone Ovens. Example: false hides only cooking timers.", synchronizedSetting: false);
			ShowSmelterTimer = plugin.config("HoverTimers", "ShowSmelterTimer", value: true, "Shows the remaining time for the item currently processing in furnaces, kilns, windmills, spinning wheels and Eitr refineries. Example: false hides only these timers.", synchronizedSetting: false);
			ShowBeehiveTimer = plugin.config("HoverTimers", "ShowBeehiveTimer", value: true, "Shows the remaining production time for the next honey when looking at a beehive. Example: false hides only the beehive timer.", synchronizedSetting: false);
			ShowPickableTimer = plugin.config("HoverTimers", "ShowPickableTimer", value: true, "Shows the remaining respawn time when looking at a harvested pickable resource. Example: false hides only the pickable timer.", synchronizedSetting: false);
			ShowTamingTimer = plugin.config("HoverTimers", "ShowTamingTimer", value: true, "Shows the remaining taming time when looking at an untamed creature. Example: false hides only the taming timer.", synchronizedSetting: false);
			ShowGrowthTimer = plugin.config("HoverTimers", "ShowGrowthTimer", value: true, "Shows the remaining growth time when looking at offspring or a growing egg. Example: false hides only offspring and egg growth timers.", synchronizedSetting: false);
		}

		private static bool TryGetZDO(Component component, out ZDO zdo)
		{
			zdo = null;
			ZNetView component2 = component.GetComponent<ZNetView>();
			if ((Object)(object)component2 == (Object)null || !component2.IsValid())
			{
				return false;
			}
			zdo = component2.GetZDO();
			return zdo != null;
		}

		private static bool TryGetElapsedSeconds(long startTicks, out double elapsedSeconds)
		{
			elapsedSeconds = 0.0;
			if (startTicks <= 0 || (Object)(object)ZNet.instance == (Object)null)
			{
				return false;
			}
			try
			{
				DateTime dateTime = new DateTime(startTicks);
				elapsedSeconds = (ZNet.instance.GetTime() - dateTime).TotalSeconds;
				if (elapsedSeconds < 0.0)
				{
					elapsedSeconds = 0.0;
				}
				return true;
			}
			catch (ArgumentOutOfRangeException)
			{
				return false;
			}
		}

		private static string FormatDuration(double seconds)
		{
			TimeSpan timeSpan = TimeSpan.FromSeconds(Math.Ceiling(Math.Max(0.0, seconds)));
			if (timeSpan.TotalDays >= 1.0)
			{
				return (int)timeSpan.TotalDays + "d " + timeSpan.Hours + "h";
			}
			if (timeSpan.TotalHours >= 1.0)
			{
				return (int)timeSpan.TotalHours + "h " + timeSpan.Minutes + "m";
			}
			if (timeSpan.TotalMinutes >= 1.0)
			{
				return (int)timeSpan.TotalMinutes + "m " + timeSpan.Seconds + "s";
			}
			return timeSpan.Seconds + "s";
		}

		private static void AppendRemaining(ref string hoverText, double remainingSeconds)
		{
			if (!(remainingSeconds <= 0.0))
			{
				string text = "Remaining: " + FormatDuration(remainingSeconds);
				hoverText = (string.IsNullOrEmpty(hoverText) ? text : (hoverText + "\n" + text));
			}
		}

		private static void AppendPaused(ref string hoverText)
		{
			hoverText = (string.IsNullOrEmpty(hoverText) ? "Paused" : (hoverText + "\nPaused"));
		}

		private static bool ContainsRemainingTimer(string hoverText)
		{
			return !string.IsNullOrEmpty(hoverText) && hoverText.IndexOf("Remaining:", StringComparison.Ordinal) >= 0;
		}

		private static bool TryGetGrowthRemaining(Component component, out double remainingSeconds)
		{
			remainingSeconds = 0.0;
			if (ShowGrowthTimer == null || !ShowGrowthTimer.Value)
			{
				return false;
			}
			Growup component2 = component.GetComponent<Growup>();
			BaseAI component3 = component.GetComponent<BaseAI>();
			if ((Object)(object)component2 == (Object)null || (Object)(object)component3 == (Object)null)
			{
				return false;
			}
			remainingSeconds = (double)component2.m_growTime - component3.GetTimeSinceSpawned().TotalSeconds;
			return remainingSeconds > 0.0;
		}

		private static void AppendCookingTimers(CookingStation cookingStation, ref string hoverText)
		{
			if (ShowCookingTimer == null || !ShowCookingTimer.Value || (Object)(object)cookingStation == (Object)null || cookingStation.m_slots == null || cookingStation.m_conversion == null || !TryGetZDO((Component)(object)cookingStation, out var zdo))
			{
				return;
			}
			string text = "";
			for (int i = 0; i < cookingStation.m_slots.Length; i++)
			{
				string text2 = "slot" + i;
				string text3 = zdo.GetString(text2, "");
				if (string.IsNullOrEmpty(text3) || zdo.GetInt("slotstatus" + i, 0) != 0)
				{
					continue;
				}
				ItemConversion val = null;
				for (int j = 0; j < cookingStation.m_conversion.Count; j++)
				{
					ItemConversion val2 = cookingStation.m_conversion[j];
					if (val2 != null && (Object)(object)val2.m_from != (Object)null && ((Object)((Component)val2.m_from).gameObject).name == text3)
					{
						val = val2;
						break;
					}
				}
				if (val == null)
				{
					continue;
				}
				double num = val.m_cookTime - zdo.GetFloat(text2, 0f);
				if (!(num <= 0.0))
				{
					if (text.Length > 0)
					{
						text += " / ";
					}
					text += FormatDuration(num);
				}
			}
			if (text.Length != 0)
			{
				if (string.IsNullOrEmpty(hoverText))
				{
					hoverText = Localization.instance.Localize(cookingStation.m_name);
				}
				hoverText = hoverText + "\nRemaining: " + text;
			}
		}

		private static void AppendSmelterTimer(Smelter smelter, ref string hoverText)
		{
			if (ShowSmelterTimer != null && ShowSmelterTimer.Value && !((Object)(object)smelter == (Object)null) && TryGetZDO((Component)(object)smelter, out var zdo) && zdo.GetInt(ZDOVars.s_queued, 0) > 0)
			{
				if (smelter.m_maxFuel > 0 && zdo.GetFloat(ZDOVars.s_fuel, 0f) <= 0f)
				{
					AppendPaused(ref hoverText);
				}
				else
				{
					AppendRemaining(ref hoverText, smelter.m_secPerProduct - zdo.GetFloat(ZDOVars.s_bakeTimer, 0f));
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Plant), "GetHoverText")]
		private static void Plant_GetHoverText_Postfix(Plant __instance, ref string __result)
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			if (ShowPlantTimer == null || !ShowPlantTimer.Value || (int)__instance.GetStatus() != 0 || !TryGetZDO((Component)(object)__instance, out var zdo))
			{
				return;
			}
			long startTicks = zdo.GetLong(ZDOVars.s_plantTime, 0L);
			if (TryGetElapsedSeconds(startTicks, out var elapsedSeconds))
			{
				int num = zdo.GetInt(ZDOVars.s_seed, 0);
				State state = Random.state;
				float value;
				try
				{
					Random.InitState(num);
					value = Random.value;
				}
				finally
				{
					Random.state = state;
				}
				double num2 = Mathf.Lerp(__instance.m_growTime, __instance.m_growTimeMax, value);
				AppendRemaining(ref __result, num2 - elapsedSeconds);
			}
		}

		[HarmonyPatch(typeof(Fermenter), "GetHoverText")]
		[HarmonyPostfix]
		private static void Fermenter_GetHoverText_Postfix(Fermenter __instance, ref string __result)
		{
			if (ShowFermenterTimer != null && ShowFermenterTimer.Value && TryGetZDO((Component)(object)__instance, out var zdo) && zdo.GetInt(ZDOVars.s_content, 0) != 0)
			{
				long startTicks = zdo.GetLong(ZDOVars.s_startTime, 0L);
				if (TryGetElapsedSeconds(startTicks, out var elapsedSeconds))
				{
					AppendRemaining(ref __result, (double)__instance.m_fermentationDuration - elapsedSeconds);
				}
			}
		}

		[HarmonyPatch(typeof(CookingStation), "GetHoverText")]
		[HarmonyPostfix]
		private static void CookingStation_GetHoverText_Postfix(CookingStation __instance, ref string __result)
		{
			AppendCookingTimers(__instance, ref __result);
		}

		[HarmonyPatch(typeof(Switch), "GetHoverText")]
		[HarmonyPostfix]
		private static void CookingStationSwitch_GetHoverText_Postfix(Switch __instance, ref string __result)
		{
			CookingStation componentInParent = ((Component)__instance).GetComponentInParent<CookingStation>();
			if (!((Object)(object)componentInParent == (Object)null) && !((Object)(object)componentInParent.m_addFoodSwitch != (Object)(object)__instance))
			{
				AppendCookingTimers(componentInParent, ref __result);
			}
		}

		[HarmonyPatch(typeof(Switch), "GetHoverText")]
		[HarmonyPostfix]
		private static void SmelterSwitch_GetHoverText_Postfix(Switch __instance, ref string __result)
		{
			Smelter componentInParent = ((Component)__instance).GetComponentInParent<Smelter>();
			if ((Object)(object)componentInParent != (Object)null && ((Object)(object)componentInParent.m_addOreSwitch == (Object)(object)__instance || (Object)(object)componentInParent.m_addWoodSwitch == (Object)(object)__instance))
			{
				AppendSmelterTimer(componentInParent, ref __result);
			}
		}

		[HarmonyPatch(typeof(Beehive), "GetHoverText")]
		[HarmonyPostfix]
		private static void Beehive_GetHoverText_Postfix(Beehive __instance, ref string __result)
		{
			if (ShowBeehiveTimer == null || !ShowBeehiveTimer.Value || !TryGetZDO((Component)(object)__instance, out var zdo))
			{
				return;
			}
			int num = zdo.GetInt(ZDOVars.s_level, 0);
			if (num >= __instance.m_maxHoney)
			{
				return;
			}
			if (!__instance.CheckBiome() || !__instance.HaveFreeSpace())
			{
				AppendPaused(ref __result);
				return;
			}
			long startTicks = zdo.GetLong(ZDOVars.s_lastTime, 0L);
			if (TryGetElapsedSeconds(startTicks, out var elapsedSeconds))
			{
				double num2 = (double)zdo.GetFloat(ZDOVars.s_product, 0f) + elapsedSeconds;
				AppendRemaining(ref __result, (double)__instance.m_secPerUnit - num2);
			}
		}

		[HarmonyPatch(typeof(Pickable), "GetHoverText")]
		[HarmonyPostfix]
		private static void Pickable_GetHoverText_Postfix(Pickable __instance, ref string __result)
		{
			if (ShowPickableTimer == null || !ShowPickableTimer.Value || __instance.m_respawnTimeMinutes <= 0f || __instance.GetEnabled == 0 || !TryGetZDO((Component)(object)__instance, out var zdo) || !zdo.GetBool(ZDOVars.s_picked, __instance.m_defaultPicked))
			{
				return;
			}
			long startTicks = zdo.GetLong(ZDOVars.s_pickedTime, 0L);
			if (!TryGetElapsedSeconds(startTicks, out var elapsedSeconds))
			{
				return;
			}
			double num = (double)__instance.m_respawnTimeMinutes * 60.0 - elapsedSeconds;
			if (!(num <= 0.0))
			{
				if (string.IsNullOrEmpty(__result))
				{
					__result = Localization.instance.Localize(__instance.GetHoverName());
				}
				AppendRemaining(ref __result, num);
			}
		}

		[HarmonyPatch(typeof(Tameable), "GetHoverText")]
		[HarmonyPostfix]
		private static void Tameable_GetHoverText_Postfix(Tameable __instance, ref string __result)
		{
			if (ShowTamingTimer != null && ShowTamingTimer.Value && !__instance.IsTamed() && TryGetZDO((Component)(object)__instance, out var zdo))
			{
				float num = zdo.GetFloat(ZDOVars.s_tameTimeLeft, __instance.m_tamingTime);
				AppendRemaining(ref __result, num);
			}
			if (TryGetGrowthRemaining((Component)(object)__instance, out var remainingSeconds))
			{
				AppendRemaining(ref __result, remainingSeconds);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Character), "GetHoverText")]
		private static void Character_GetHoverText_Postfix(Character __instance, ref string __result)
		{
			if (!ContainsRemainingTimer(__result) && TryGetGrowthRemaining((Component)(object)__instance, out var remainingSeconds))
			{
				if (string.IsNullOrEmpty(__result))
				{
					__result = Localization.instance.Localize(__instance.m_name);
				}
				AppendRemaining(ref __result, remainingSeconds);
			}
		}

		[HarmonyPatch(typeof(EggGrow), "GetHoverText")]
		[HarmonyPostfix]
		private static void EggGrow_GetHoverText_Postfix(EggGrow __instance, ref string __result)
		{
			if (ShowGrowthTimer != null && ShowGrowthTimer.Value && TryGetZDO((Component)(object)__instance, out var zdo) && !((Object)(object)ZNet.instance == (Object)null))
			{
				float num = zdo.GetFloat(ZDOVars.s_growStart, 0f);
				if (num <= 0f)
				{
					AppendPaused(ref __result);
					return;
				}
				double num2 = ZNet.instance.GetTimeSeconds() - (double)num;
				AppendRemaining(ref __result, (double)__instance.m_growTime - num2);
			}
		}
	}
	[<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)]
	[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)]
	internal static class Patch_InfiniteFuel
	{
		private const string BathtubToggleRpc = "ValheimQoL_ToggleBathtub";

		private static readonly List<FireplaceFuelState> RegisteredFireplaces = new List<FireplaceFuelState>();

		private static readonly List<SmokeSpawnerState> RegisteredSmokeSpawners = new List<SmokeSpawnerState>();

		private static readonly List<SmelterFuelState> RegisteredSmelters = new List<SmelterFuelState>();

		private static readonly List<CookingStationFuelState> RegisteredCookingStations = new List<CookingStationFuelState>();

		private static readonly int AutoExtinguishedByRainZdoKey = StringExtensionMethods.GetStableHashCode("ValheimQoL_AutoExtinguishedByRain");

		private static readonly int BathtubEnabledZdoKey = StringExtensionMethods.GetStableHashCode("ValheimQoL_BathtubEnabled");

		private static ConfigEntry<bool> NoFuelRequired = null;

		private static ConfigEntry<bool> OvenNoFuelRequired = null;

		private static ConfigEntry<bool> BathtubNoFuelRequired = null;

		private static ConfigEntry<bool> MakeToggleable = null;

		private static ConfigEntry<bool> DisableSmoke = null;

		internal static void InitConfig(ValheimQoLPlugin plugin)
		{
			NoFuelRequired = plugin.config("FireSources", "NoFuelRequired", value: true, "Makes every Fireplace-based fire source work without consuming fuel. This includes campfires, hearths, torches, sconces and braziers. Example: false restores normal Wood, Resin and Coal consumption.");
			OvenNoFuelRequired = plugin.config("FireSources", "OvenNoFuelRequired", value: true, "Makes the Stone Oven work without consuming Wood. Food is still required and cooking time is unchanged. Example: false restores the vanilla Wood requirement for the Stone Oven.");
			BathtubNoFuelRequired = plugin.config("FireSources", "BathtubNoFuelRequired", value: true, "Keeps the Hot Tub heated without consuming Wood and changes its Wood input into an On/Off switch. The selected state is saved in the world and synchronized in multiplayer. Example: false restores the vanilla Wood requirement and Wood input interaction for the Hot Tub.");
			MakeToggleable = plugin.config("FireSources", "MakeToggleable", value: true, "Allows every Fireplace-based fire source to be turned on and off with the Use key. Example: true lets players turn off campfires, torches, sconces and braziers.");
			DisableSmoke = plugin.config("FireSources", "DisableSmoke", value: false, "Stops Fireplace-based fire sources from creating smoke and prevents smoke blockage from extinguishing them. Example: true removes chimney smoke while keeping the fire and its heat active.");
			NoFuelRequired.SettingChanged += OnSettingChanged;
			OvenNoFuelRequired.SettingChanged += OnSettingChanged;
			BathtubNoFuelRequired.SettingChanged += OnSettingChanged;
			MakeToggleable.SettingChanged += OnSettingChanged;
			DisableSmoke.SettingChanged += OnSettingChanged;
		}

		internal static void Shutdown()
		{
			if (NoFuelRequired != null)
			{
				NoFuelRequired.SettingChanged -= OnSettingChanged;
			}
			if (OvenNoFuelRequired != null)
			{
				OvenNoFuelRequired.SettingChanged -= OnSettingChanged;
			}
			if (BathtubNoFuelRequired != null)
			{
				BathtubNoFuelRequired.SettingChanged -= OnSettingChanged;
			}
			if (MakeToggleable != null)
			{
				MakeToggleable.SettingChanged -= OnSettingChanged;
			}
			if (DisableSmoke != null)
			{
				DisableSmoke.SettingChanged -= OnSettingChanged;
			}
			for (int num = RegisteredFireplaces.Count - 1; num >= 0; num--)
			{
				FireplaceFuelState fireplaceFuelState = RegisteredFireplaces[num];
				if ((Object)(object)fireplaceFuelState != (Object)null && (Object)(object)fireplaceFuelState.Fireplace != (Object)null)
				{
					Restore(fireplaceFuelState);
				}
			}
			RegisteredFireplaces.Clear();
			RestoreSmokeSpawners();
			RestoreSmelters();
			RestoreCookingStations();
		}

		private static bool IsInfiniteFuelEnabled()
		{
			return NoFuelRequired != null && NoFuelRequired.Value;
		}

		private static bool IsToggleEnabled()
		{
			return MakeToggleable != null && MakeToggleable.Value;
		}

		private static bool IsOvenInfiniteFuelEnabled()
		{
			return OvenNoFuelRequired != null && OvenNoFuelRequired.Value;
		}

		private static bool IsBathtubInfiniteFuelEnabled()
		{
			return BathtubNoFuelRequired != null && BathtubNoFuelRequired.Value;
		}

		private static bool IsSmokeDisabled()
		{
			return DisableSmoke != null && DisableSmoke.Value;
		}

		private static void OnSettingChanged(object sender, EventArgs eventArgs)
		{
			ApplyToRegisteredFireplaces();
			ApplyToRegisteredSmokeSpawners();
			ApplyToRegisteredSmelters();
			ApplyToRegisteredCookingStations();
			if (IsSmokeDisabled())
			{
				FadeExistingSmoke();
			}
		}

		private static void ApplyToRegisteredFireplaces()
		{
			for (int num = RegisteredFireplaces.Count - 1; num >= 0; num--)
			{
				FireplaceFuelState fireplaceFuelState = RegisteredFireplaces[num];
				if ((Object)(object)fireplaceFuelState == (Object)null || (Object)(object)fireplaceFuelState.Fireplace == (Object)null)
				{
					RegisteredFireplaces.RemoveAt(num);
				}
				else
				{
					Apply(fireplaceFuelState);
				}
			}
		}

		private static void Apply(FireplaceFuelState state)
		{
			state.Fireplace.m_infiniteFuel = state.OriginalInfiniteFuel || IsInfiniteFuelEnabled();
			state.Fireplace.m_canTurnOff = state.OriginalCanTurnOff || IsToggleEnabled();
			if (IsSmokeDisabled())
			{
				state.Fireplace.m_smokeSpawner = null;
			}
			else
			{
				state.Fireplace.m_smokeSpawner = state.OriginalSmokeSpawner;
			}
		}

		private static void Restore(FireplaceFuelState state)
		{
			state.Fireplace.m_infiniteFuel = state.OriginalInfiniteFuel;
			state.Fireplace.m_canTurnOff = state.OriginalCanTurnOff;
			state.Fireplace.m_smokeSpawner = state.OriginalSmokeSpawner;
		}

		private static void FadeExistingSmoke()
		{
			for (int num = Smoke.Instances.Count - 1; num >= 0; num--)
			{
				IMonoUpdater obj = Smoke.Instances[num];
				Smoke val = (Smoke)(object)((obj is Smoke) ? obj : null);
				if ((Object)(object)val != (Object)null)
				{
					val.StartFadeOut();
				}
			}
		}

		internal static void Register(Fireplace fireplace)
		{
			if (!((Object)(object)fireplace == (Object)null))
			{
				FireplaceFuelState fireplaceFuelState = ((Component)fireplace).GetComponent<FireplaceFuelState>();
				if ((Object)(object)fireplaceFuelState == (Object)null)
				{
					fireplaceFuelState = ((Component)fireplace).gameObject.AddComponent<FireplaceFuelState>();
					fireplaceFuelState.Fireplace = fireplace;
					fireplaceFuelState.OriginalInfiniteFuel = fireplace.m_infiniteFuel;
					fireplaceFuelState.OriginalCanTurnOff = fireplace.m_canTurnOff;
					fireplaceFuelState.OriginalSmokeSpawner = fireplace.m_smokeSpawner;
					RegisteredFireplaces.Add(fireplaceFuelState);
				}
				else if (!RegisteredFireplaces.Contains(fireplaceFuelState))
				{
					RegisteredFireplaces.Add(fireplaceFuelState);
				}
				SmokeSpawner[] componentsInChildren = ((Component)fireplace).GetComponentsInChildren<SmokeSpawner>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					RegisterSmokeSpawner(componentsInChildren[i]);
				}
				RegisterSmokeSpawner(fireplaceFuelState.OriginalSmokeSpawner);
				Apply(fireplaceFuelState);
			}
		}

		[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(2)]
		internal static void RegisterSmokeSpawner(SmokeSpawner smokeSpawner)
		{
			if ((Object)(object)smokeSpawner == (Object)null || (Object)(object)((Component)smokeSpawner).GetComponentInParent<Smelter>() != (Object)null)
			{
				return;
			}
			for (int i = 0; i < RegisteredSmokeSpawners.Count; i++)
			{
				if ((Object)(object)RegisteredSmokeSpawners[i].SmokeSpawner == (Object)(object)smokeSpawner)
				{
					ApplySmokeSpawner(RegisteredSmokeSpawners[i]);
					return;
				}
			}
			SmokeSpawnerState smokeSpawnerState = new SmokeSpawnerState(smokeSpawner, ((Behaviour)smokeSpawner).enabled);
			RegisteredSmokeSpawners.Add(smokeSpawnerState);
			ApplySmokeSpawner(smokeSpawnerState);
		}

		private static void ApplyToRegisteredSmokeSpawners()
		{
			for (int num = RegisteredSmokeSpawners.Count - 1; num >= 0; num--)
			{
				SmokeSpawnerState smokeSpawnerState = RegisteredSmokeSpawners[num];
				if ((Object)(object)smokeSpawnerState.SmokeSpawner == (Object)null)
				{
					RegisteredSmokeSpawners.RemoveAt(num);
				}
				else
				{
					ApplySmokeSpawner(smokeSpawnerState);
				}
			}
		}

		private static void ApplySmokeSpawner(SmokeSpawnerState state)
		{
			((Behaviour)state.SmokeSpawner).enabled = state.OriginalEnabled && !IsSmokeDisabled();
		}

		private static void RestoreSmokeSpawners()
		{
			for (int num = RegisteredSmokeSpawners.Count - 1; num >= 0; num--)
			{
				SmokeSpawnerState smokeSpawnerState = RegisteredSmokeSpawners[num];
				if ((Object)(object)smokeSpawnerState.SmokeSpawner != (Object)null)
				{
					((Behaviour)smokeSpawnerState.SmokeSpawner).enabled = smokeSpawnerState.OriginalEnabled;
				}
			}
			RegisteredSmokeSpawners.Clear();
		}

		internal static void RegisterSmelter(Smelter smelter)
		{
			if ((Object)(object)smelter == (Object)null || !IsSupportedFuelFreeSmelter(smelter))
			{
				return;
			}
			SmelterFuelState smelterFuelState = ((Component)smelter).GetComponent<SmelterFuelState>();
			if ((Object)(object)smelterFuelState == (Object)null)
			{
				smelterFuelState = ((Component)smelter).gameObject.AddComponent<SmelterFuelState>();
				smelterFuelState.Smelter = smelter;
				smelterFuelState.OriginalMaxFuel = smelter.m_maxFuel;
				smelterFuelState.FuelSwitch = smelter.m_addWoodSwitch;
				smelterFuelState.OriginalFuelSwitchActive = (Object)(object)smelterFuelState.FuelSwitch != (Object)null && ((Component)smelterFuelState.FuelSwitch).gameObject.activeSelf;
				if ((Object)(object)smelterFuelState.FuelSwitch != (Object)null)
				{
					smelterFuelState.OriginalFuelOnUse = smelterFuelState.FuelSwitch.m_onUse;
					smelterFuelState.OriginalFuelOnHover = smelterFuelState.FuelSwitch.m_onHover;
				}
				RegisteredSmelters.Add(smelterFuelState);
			}
			else if (!RegisteredSmelters.Contains(smelterFuelState))
			{
				RegisteredSmelters.Add(smelterFuelState);
			}
			RegisterBathtubRpc(smelterFuelState);
			Apply(smelterFuelState);
		}

		private static void RegisterBathtubRpc(SmelterFuelState state)
		{
			if (!state.RpcRegistered)
			{
				ZNetView smelterNView = GetSmelterNView(state.Smelter);
				if (!((Object)(object)smelterNView == (Object)null) && smelterNView.IsValid())
				{
					state.NView = smelterNView;
					smelterNView.Register("ValheimQoL_ToggleBathtub", (Action<long>)state.ReceiveToggleRpc);
					state.RpcRegistered = true;
				}
			}
		}

		private static bool IsSupportedFuelFreeSmelter(Smelter smelter)
		{
			return smelter.m_name == "$piece_bathtub";
		}

		private static bool ShouldRemoveSmelterFuel(Smelter smelter)
		{
			if (smelter.m_name == "$piece_bathtub")
			{
				return IsBathtubInfiniteFuelEnabled();
			}
			return false;
		}

		internal static bool ShouldHandleBathtubToggle(Smelter smelter)
		{
			return (Object)(object)smelter != (Object)null && IsSupportedFuelFreeSmelter(smelter) && IsBathtubInfiniteFuelEnabled();
		}

		internal static bool RequestBathtubToggle(Smelter smelter)
		{
			if (!ShouldHandleBathtubToggle(smelter))
			{
				return false;
			}
			ZNetView smelterNView = GetSmelterNView(smelter);
			if ((Object)(object)smelterNView == (Object)null || !smelterNView.IsValid())
			{
				return false;
			}
			if (!smelterNView.HasOwner())
			{
				smelterNView.ClaimOwnership();
			}
			smelterNView.InvokeRPC("ValheimQoL_ToggleBathtub", Array.Empty<object>());
			return true;
		}

		internal static void ReceiveBathtubToggle(Smelter smelter, ZNetView nview)
		{
			if (ShouldHandleBathtubToggle(smelter) && !((Object)(object)nview == (Object)null) && nview.IsValid() && nview.IsOwner())
			{
				ZDO zDO = nview.GetZDO();
				if (zDO != null)
				{
					zDO.Set(BathtubEnabledZdoKey, !zDO.GetBool(BathtubEnabledZdoKey, true));
					ApplyBathtubActiveState(smelter);
				}
			}
		}

		internal static bool IsBathtubTurnedOn(Smelter smelter)
		{
			ZNetView smelterNView = GetSmelterNView(smelter);
			if ((Object)(object)smelterNView == (Object)null || !smelterNView.IsValid())
			{
				return true;
			}
			ZDO zDO = smelterNView.GetZDO();
			return zDO == null || zDO.GetBool(BathtubEnabledZdoKey, true);
		}

		internal static void ApplyBathtubActiveState(Smelter smelter)
		{
			if (ShouldHandleBathtubToggle(smelter) && !((Object)(object)smelter.m_enabledObject == (Object)null))
			{
				smelter.m_enabledObject.SetActive(IsBathtubTurnedOn(smelter));
			}
		}

		internal static string GetBathtubToggleHoverText(Smelter smelter)
		{
			bool flag = IsBathtubTurnedOn(smelter);
			return Localization.instance.Localize(smelter.m_name + " (" + (flag ? "On" : "Off") + ")\n[<color=yellow><b>$KEY_Use</b></color>] " + (flag ? "Turn off" : "Turn on"));
		}

		private static ZNetView GetSmelterNView(Smelter smelter)
		{
			if ((Object)(object)smelter == (Object)null)
			{
				return null;
			}
			ZNetView component = ((Component)smelter).GetComponent<ZNetView>();
			return ((Object)(object)component != (Object)null) ? component : ((Component)smelter).GetComponentInParent<ZNetView>();
		}

		private static void ApplyToRegisteredSmelters()
		{
			for (int num = RegisteredSmelters.Count - 1; num >= 0; num--)
			{
				SmelterFuelState smelterFuelState = RegisteredSmelters[num];
				if ((Object)(object)smelterFuelState == (Object)null || (Object)(object)smelterFuelState.Smelter == (Object)null)
				{
					RegisteredSmelters.RemoveAt(num);
				}
				else
				{
					Apply(smelterFuelState);
				}
			}
		}

		private static void Apply(SmelterFuelState state)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			bool flag = ShouldRemoveSmelterFuel(state.Smelter);
			state.Smelter.m_maxFuel = ((!flag) ? state.OriginalMaxFuel : 0);
			if ((Object)(object)state.FuelSwitch != (Object)null)
			{
				((Component)state.FuelSwitch).gameObject.SetActive(state.OriginalFuelSwitchActive);
				state.FuelSwitch.m_onUse = (Callback)((!flag) ? ((object)state.OriginalFuelOnUse) : ((object)new Callback(state.ToggleBathtub)));
				state.FuelSwitch.m_onHover = (TooltipCallback)((!flag) ? ((object)state.OriginalFuelOnHover) : ((object)new TooltipCallback(state.GetBathtubToggleHoverText)));
			}
		}

		private static void RestoreSmelters()
		{
			for (int num = RegisteredSmelters.Count - 1; num >= 0; num--)
			{
				SmelterFuelState smelterFuelState = RegisteredSmelters[num];
				if (!((Object)(object)smelterFuelState == (Object)null) && !((Object)(object)smelterFuelState.Smelter == (Object)null))
				{
					smelterFuelState.Smelter.m_maxFuel = smelterFuelState.OriginalMaxFuel;
					if ((Object)(object)smelterFuelState.FuelSwitch != (Object)null)
					{
						((Component)smelterFuelState.FuelSwitch).gameObject.SetActive(smelterFuelState.OriginalFuelSwitchActive);
						smelterFuelState.FuelSwitch.m_onUse = smelterFuelState.OriginalFuelOnUse;
						smelterFuelState.FuelSwitch.m_onHover = smelterFuelState.OriginalFuelOnHover;
					}
				}
			}
			RegisteredSmelters.Clear();
		}

		internal static void RegisterCookingStation(CookingStation cookingStation)
		{
			if (!((Object)(object)cookingStation == (Object)null) && IsStoneOven(cookingStation))
			{
				CookingStationFuelState cookingStationFuelState = ((Component)cookingStation).GetComponent<CookingStationFuelState>();
				if ((Object)(object)cookingStationFuelState == (Object)null)
				{
					cookingStationFuelState = ((Component)cookingStation).gameObject.AddComponent<CookingStationFuelState>();
					cookingStationFuelState.CookingStation = cookingStation;
					cookingStationFuelState.FuelSwitch = cookingStation.m_addFuelSwitch;
					cookingStationFuelState.OriginalFuelSwitchActive = (Object)(object)cookingStationFuelState.FuelSwitch != (Object)null && ((Component)cookingStationFuelState.FuelSwitch).gameObject.activeSelf;
					RegisteredCookingStations.Add(cookingStationFuelState);
				}
				else if (!RegisteredCookingStations.Contains(cookingStationFuelState))
				{
					RegisteredCookingStations.Add(cookingStationFuelState);
				}
				Apply(cookingStationFuelState);
			}
		}

		private static bool IsStoneOven(CookingStation cookingStation)
		{
			return cookingStation.m_name == "$piece_oven" || ((Object)((Component)cookingStation).gameObject).name == "piece_oven" || ((Object)((Component)cookingStation).gameObject).name == "piece_oven(Clone)";
		}

		internal static bool ShouldUseInfiniteOvenFuel(CookingStation cookingStation)
		{
			if ((Object)(object)cookingStation == (Object)null || !IsStoneOven(cookingStation) || !IsOvenInfiniteFuelEnabled())
			{
				return false;
			}
			CookingStationFuelState component = ((Component)cookingStation).GetComponent<CookingStationFuelState>();
			return (Object)(object)component == (Object)null || !component.SuppressInfiniteFuel;
		}

		private static void ApplyToRegisteredCookingStations()
		{
			for (int num = RegisteredCookingStations.Count - 1; num >= 0; num--)
			{
				CookingStationFuelState cookingStationFuelState = RegisteredCookingStations[num];
				if ((Object)(object)cookingStationFuelState == (Object)null || (Object)(object)cookingStationFuelState.CookingStation == (Object)null)
				{
					RegisteredCookingStations.RemoveAt(num);
				}
				else
				{
					Apply(cookingStationFuelState);
				}
			}
		}

		private static void Apply(CookingStationFuelState state)
		{
			if (!((Object)(object)state.FuelSwitch == (Object)null))
			{
				((Component)state.FuelSwitch).gameObject.SetActive(state.OriginalFuelSwitchActive && !IsOvenInfiniteFuelEnabled());
			}
		}

		private static void RestoreCookingStations()
		{
			for (int num = RegisteredCookingStations.Count - 1; num >= 0; num--)
			{
				CookingStationFuelState cookingStationFuelState = RegisteredCookingStations[num];
				if (!((Object)(object)cookingStationFuelState == (Object)null) && !((Object)(object)cookingStationFuelState.CookingStation == (Object)null) && !((Object)(object)cookingStationFuelState.FuelSwitch == (Object)null))
				{
					cookingStationFuelState.SuppressInfiniteFuel = false;
					((Component)cookingStationFuelState.FuelSwitch).gameObject.SetActive(cookingStationFuelState.OriginalFuelSwitchActive);
				}
			}
			RegisteredCookingStations.Clear();
		}

		internal static void SetCookingStationFuelSuppressed(CookingStation cookingStation, bool suppressed)
		{
			if (!((Object)(object)cookingStation == (Object)null))
			{
				CookingStationFuelState component = ((Component)cookingStation).GetComponent<CookingStationFuelState>();
				if ((Object)(object)component != (Object)null)
				{
					component.SuppressInfiniteFuel = suppressed;
				}
			}
		}

		internal static void Unregister(SmelterFuelState state)
		{
			if ((Object)(object)state != (Object)null)
			{
				RegisteredSmelters.Remove(state);
			}
		}

		internal static void Unregister(CookingStationFuelState state)
		{
			if ((Object)(object)state != (Object)null)
			{
				RegisteredCookingStations.Remove(state);
			}
		}

		internal static void UpdateRainAutoRelight(Fireplace fireplace, bool isWet, ZNetView nview)
		{
			if ((Object)(object)fireplace == (Object)null || (Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner())
			{
				return;
			}
			ZDO zDO = nview.GetZDO();
			if (zDO == null)
			{
				return;
			}
			if (!IsInfiniteFuelEnabled() || !IsToggleEnabled() || !fireplace.m_infiniteFuel || !fireplace.m_canTurnOff)
			{
				if (zDO.GetBool(AutoExtinguishedByRainZdoKey, false))
				{
					zDO.Set(AutoExtinguishedByRainZdoKey, false);
				}
				return;
			}
			int num = zDO.GetInt(ZDOVars.s_state, 1);
			if (isWet)
			{
				if (num == 1)
				{
					zDO.Set(AutoExtinguishedByRainZdoKey, true);
				}
			}
			else if (zDO.GetBool(AutoExtinguishedByRainZdoKey, false))
			{
				if (num != 1)
				{
					zDO.Set(ZDOVars.s_state, 1, false);
				}
				zDO.Set(AutoExtinguishedByRainZdoKey, false);
			}
		}

		internal static void Unregister(FireplaceFuelState state)
		{
			if ((Object)(object)state != (Object)null)
			{
				RegisteredFireplaces.Remove(state);
			}
		}

		internal static bool ShouldHandleInfiniteFuelToggle(Fireplace fireplace)
		{
			return (Object)(object)fireplace != (Object)null && fireplace.m_canTurnOff && fireplace.m_infiniteFuel;
		}
	}
	internal sealed class FireplaceFuelState : MonoBehaviour
	{
		[<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(1)]
		internal Fireplace Fireplace = null;

		internal bool OriginalInfiniteFuel;

		internal bool OriginalCanTurnOff;

		[<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(2)]
		internal SmokeSpawner OriginalSmokeSpawner;

		private void OnDestroy()
		{
			Patch_InfiniteFuel.Unregister(this);
		}
	}
	[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)]
	[<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)]
	internal sealed class SmokeSpawnerState
	{
		internal SmokeSpawner SmokeSpawner { get; }

		internal bool OriginalEnabled { get; }

		internal SmokeSpawnerState(SmokeSpawner smokeSpawner, bool originalEnabled)
		{
			SmokeSpawner = smokeSpawner;
			OriginalEnabled = originalEnabled;
		}
	}
	[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(2)]
	[<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(0)]
	internal sealed class SmelterFuelState : MonoBehaviour
	{
		[<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(1)]
		internal Smelter Smelter = null;

		internal ZNetView NView;

		internal Switch FuelSwitch;

		internal Callback OriginalFuelOnUse;

		internal TooltipCallback OriginalFuelOnHover;

		internal int OriginalMaxFuel;

		internal bool OriginalFuelSwitchActive;

		internal bool RpcRegistered;

		[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)]
		internal bool ToggleBathtub(Switch caller, Humanoid user, ItemData item)
		{
			return Patch_InfiniteFuel.RequestBathtubToggle(Smelter);
		}

		[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)]
		internal string GetBathtubToggleHoverText()
		{
			return Patch_InfiniteFuel.GetBathtubToggleHoverText(Smelter);
		}

		internal void ReceiveToggleRpc(long sender)
		{
			Patch_InfiniteFuel.ReceiveBathtubToggle(Smelter, NView);
		}

		private void OnDestroy()
		{
			Patch_InfiniteFuel.Unregister(this);
		}
	}
	internal sealed class CookingStationFuelState : MonoBehaviour
	{
		[<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(1)]
		internal CookingStation CookingStation = null;

		[<1467ecf2-d2cf-4ef2-a968-2b28bcb28a46>Nullable(2)]
		internal Switch FuelSwitch;

		internal bool OriginalFuelSwitchActive;

		internal bool SuppressInfiniteFuel;

		private void OnDestroy()
		{
			Patch_InfiniteFuel.Unregister(this);
		}
	}
	[HarmonyPatch(typeof(Fireplace), "Awake")]
	internal static class InfiniteFuel_Fireplace_Awake_Patch
	{
		[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)]
		private static void Postfix(Fireplace __instance)
		{
			Patch_InfiniteFuel.Register(__instance);
		}
	}
	[HarmonyPatch(typeof(Fireplace), "UpdateState")]
	internal static class FireSources_Fireplace_UpdateState_Patch
	{
		[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)]
		private static void Prefix(Fireplace __instance, bool ___m_wet, ZNetView ___m_nview)
		{
			Patch_InfiniteFuel.UpdateRainAutoRelight(__instance, ___m_wet, ___m_nview);
		}
	}
	[HarmonyPatch(typeof(Smelter), "Awake")]
	[HarmonyPriority(0)]
	internal static class InfiniteFuel_Smelter_Awake_Patch
	{
		[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)]
		private static void Postfix(Smelter __instance)
		{
			Patch_InfiniteFuel.RegisterSmelter(__instance);
		}
	}
	[HarmonyPatch(typeof(Smelter), "IsActive")]
	internal static class InfiniteFuel_Smelter_IsActive_Patch
	{
		[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)]
		private static void Postfix(Smelter __instance, ref bool __result)
		{
			if (Patch_InfiniteFuel.ShouldHandleBathtubToggle(__instance))
			{
				__result = Patch_InfiniteFuel.IsBathtubTurnedOn(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Smelter), "UpdateState")]
	internal static class InfiniteFuel_Smelter_UpdateState_Patch
	{
		[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)]
		private static void Postfix(Smelter __instance)
		{
			Patch_InfiniteFuel.ApplyBathtubActiveState(__instance);
		}
	}
	[HarmonyPatch(typeof(CookingStation), "Awake")]
	[HarmonyPriority(0)]
	internal static class InfiniteFuel_CookingStation_Awake_Patch
	{
		[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)]
		private static void Postfix(CookingStation __instance)
		{
			Patch_InfiniteFuel.RegisterCookingStation(__instance);
		}
	}
	[HarmonyPatch(typeof(CookingStation), "GetFuel")]
	internal static class InfiniteFuel_CookingStation_GetFuel_Patch
	{
		[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)]
		private static bool Prefix(CookingStation __instance, ref float __result)
		{
			if (!Patch_InfiniteFuel.ShouldUseInfiniteOvenFuel(__instance))
			{
				return true;
			}
			__result = Mathf.Max(1f, (float)__instance.m_maxFuel);
			return false;
		}
	}
	[HarmonyPatch(typeof(CookingStation), "UpdateFuel")]
	internal static class InfiniteFuel_CookingStation_UpdateFuel_Patch
	{
		[<54f20a1b-a8b0-4e2f-a983-5cf9ce1cddff>NullableContext(1)]
		private static bool Prefix(CookingStation __instance)
		{
			return !Patch_InfiniteFuel.ShouldUseInfiniteOvenFuel(__instance);
		}
	}
	[HarmonyPatch(typeof(CookingStation), "DropAllItems")]
	[<1