Decompiled source of Plantations v1.0.4

plugins/Plantations.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Linq.Expressions;
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 System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using ItemManager;
using JetBrains.Annotations;
using LocalizationManager;
using Microsoft.CodeAnalysis;
using PieceManager;
using ServerSync;
using Splatform;
using TMPro;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.UI;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Core.ObjectPool;
using YamlDotNet.Core.Tokens;
using YamlDotNet.Helpers;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.BufferedDeserialization;
using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators;
using YamlDotNet.Serialization.Callbacks;
using YamlDotNet.Serialization.Converters;
using YamlDotNet.Serialization.EventEmitters;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization.NodeDeserializers;
using YamlDotNet.Serialization.NodeTypeResolvers;
using YamlDotNet.Serialization.ObjectFactories;
using YamlDotNet.Serialization.ObjectGraphTraversalStrategies;
using YamlDotNet.Serialization.ObjectGraphVisitors;
using YamlDotNet.Serialization.Schemas;
using YamlDotNet.Serialization.TypeInspectors;
using YamlDotNet.Serialization.TypeResolvers;
using YamlDotNet.Serialization.Utilities;
using YamlDotNet.Serialization.ValueDeserializers;

[assembly: Guid("E74EB49A-461D-48EA-85BC-F462D60C98C4")]
[assembly: ComVisible(false)]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyProduct("Plantations")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyTitle("Plantations")]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyFileVersion("1.0.4")]
[assembly: AssemblyCompany("Radamanto")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.4.0")]
[module: <1af70648-03dc-4cf0-aa1a-92525b356746>RefSafetyRules(11)]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[<ac36e783-d8a0-4059-8544-836f43818104>Embedded]
	internal sealed class <ac36e783-d8a0-4059-8544-836f43818104>EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[<ac36e783-d8a0-4059-8544-836f43818104>Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class <ad47d627-eefa-4fa9-aeb5-d2cdcce92dc3>NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public <ad47d627-eefa-4fa9-aeb5-d2cdcce92dc3>NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public <ad47d627-eefa-4fa9-aeb5-d2cdcce92dc3>NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[<ac36e783-d8a0-4059-8544-836f43818104>Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class <751c224d-bfbf-4350-9e31-32d445024a3b>NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public <751c224d-bfbf-4350-9e31-32d445024a3b>NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	[CompilerGenerated]
	[<ac36e783-d8a0-4059-8544-836f43818104>Embedded]
	internal sealed class <1af70648-03dc-4cf0-aa1a-92525b356746>RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public <1af70648-03dc-4cf0-aa1a-92525b356746>RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace LocalizationManager
{
	[<ad47d627-eefa-4fa9-aeb5-d2cdcce92dc3>Nullable(0)]
	[<751c224d-bfbf-4350-9e31-32d445024a3b>NullableContext(1)]
	[PublicAPI]
	public class Localizer
	{
		private static readonly Dictionary<string, Dictionary<string, Func<string>>> PlaceholderProcessors;

		private static readonly Dictionary<string, Dictionary<string, string>> loadedTexts;

		private static readonly ConditionalWeakTable<Localization, string> localizationLanguage;

		private static readonly List<WeakReference<Localization>> localizationObjects;

		[<ad47d627-eefa-4fa9-aeb5-d2cdcce92dc3>Nullable(2)]
		private static BaseUnityPlugin _plugin;

		private static readonly List<string> fileExtensions;

		private static BaseUnityPlugin plugin
		{
			get
			{
				//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b2: Expected O, but got Unknown
				if (_plugin == null)
				{
					IEnumerable<TypeInfo> source;
					try
					{
						source = Assembly.GetExecutingAssembly().DefinedTypes.ToList();
					}
					catch (ReflectionTypeLoadException ex)
					{
						source = from t in ex.Types
							where t != null
							select t.GetTypeInfo();
					}
					_plugin = (BaseUnityPlugin)Chainloader.ManagerObject.GetComponent((Type)source.First([<751c224d-bfbf-4350-9e31-32d445024a3b>NullableContext(0)] (TypeInfo t) => t.IsClass && typeof(BaseUnityPlugin).IsAssignableFrom(t)));
				}
				return _plugin;
			}
		}

		[<ad47d627-eefa-4fa9-aeb5-d2cdcce92dc3>Nullable(2)]
		[method: <751c224d-bfbf-4350-9e31-32d445024a3b>NullableContext(2)]
		[field: <ad47d627-eefa-4fa9-aeb5-d2cdcce92dc3>Nullable(2)]
		public static event Action OnLocalizationComplete;

		private static void UpdatePlaceholderText(Localization localization, string key)
		{
			localizationLanguage.TryGetValue(localization, out var value);
			string text = loadedTexts[value][key];
			if (PlaceholderProcessors.TryGetValue(key, out var value2))
			{
				text = value2.Aggregate(text, [<751c224d-bfbf-4350-9e31-32d445024a3b>NullableContext(0)] (string current, KeyValuePair<string, Func<string>> kv) => current.Replace("{" + kv.Key + "}", kv.Value()));
			}
			if (key == "ve_plantation_ready_in")
			{
				text = text.Replace("{0}", "<color=yellow>{0}</color>");
			}
			localization.AddWord(key, text);
		}

		public static void AddPlaceholder<T>(string key, string placeholder, ConfigEntry<T> config, [<ad47d627-eefa-4fa9-aeb5-d2cdcce92dc3>Nullable(new byte[] { 2, 1, 1 })] Func<T, string> convertConfigValue = null)
		{
			if (convertConfigValue == null)
			{
				convertConfigValue = (T val) => val.ToString();
			}
			if (!PlaceholderProcessors.ContainsKey(key))
			{
				PlaceholderProcessors[key] = new Dictionary<string, Func<string>>();
			}
			config.SettingChanged += [<751c224d-bfbf-4350-9e31-32d445024a3b>NullableContext(0)] (object _, EventArgs _) =>
			{
				UpdatePlaceholder();
			};
			if (loadedTexts.ContainsKey(Localization.instance.GetSelectedLanguage()))
			{
				UpdatePlaceholder();
			}
			void UpdatePlaceholder()
			{
				PlaceholderProcessors[key][placeholder] = () => convertConfigValue(config.Value);
				UpdatePlaceholderText(Localization.instance, key);
			}
		}

		public static void AddText(string key, string text)
		{
			List<WeakReference<Localization>> list = new List<WeakReference<Localization>>();
			foreach (WeakReference<Localization> localizationObject in localizationObjects)
			{
				if (localizationObject.TryGetTarget(out var target))
				{
					Dictionary<string, string> dictionary = loadedTexts[localizationLanguage.GetOrCreateValue(target)];
					if (!target.m_translations.ContainsKey(key))
					{
						dictionary[key] = text;
						target.AddWord(key, text);
					}
				}
				else
				{
					list.Add(localizationObject);
				}
			}
			foreach (WeakReference<Localization> item in list)
			{
				localizationObjects.Remove(item);
			}
		}

		public static void Load()
		{
			_ = plugin;
		}

		public static void LoadLocalizationLater()
		{
			LoadLocalization(Localization.instance, Localization.instance.GetSelectedLanguage());
		}

		public static void SafeCallLocalizeComplete()
		{
			Localizer.OnLocalizationComplete?.Invoke();
		}

		private static void LoadLocalization(Localization __instance, string language)
		{
			if (!localizationLanguage.Remove(__instance))
			{
				localizationObjects.Add(new WeakReference<Localization>(__instance));
			}
			localizationLanguage.Add(__instance, language);
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			foreach (string item in from f in Directory.GetFiles(Path.GetDirectoryName(Paths.PluginPath), plugin.Info.Metadata.Name + ".*", SearchOption.AllDirectories)
				where fileExtensions.IndexOf(Path.GetExtension(f)) >= 0
				select f)
			{
				string[] array = Path.GetFileNameWithoutExtension(item).Split(new char[1] { '.' });
				if (array.Length >= 2)
				{
					string text = array[1];
					if (dictionary.ContainsKey(text))
					{
						Debug.LogWarning((object)("Duplicate key " + text + " found for " + plugin.Info.Metadata.Name + ". The duplicate file found at " + item + " will be skipped."));
					}
					else
					{
						dictionary[text] = item;
					}
				}
			}
			byte[] array2 = LoadTranslationFromAssembly("English");
			if (array2 == null)
			{
				throw new Exception("Found no English localizations in mod " + plugin.Info.Metadata.Name + ". Expected an embedded resource translations/English.json or translations/English.yml.");
			}
			string input = Encoding.UTF8.GetString(array2).TrimStart(new char[1] { '\ufeff' });
			Dictionary<string, string> dictionary2 = new DeserializerBuilder().IgnoreFields().Build().Deserialize<Dictionary<string, string>>(input);
			if (dictionary2 == null)
			{
				throw new Exception("Localization for mod " + plugin.Info.Metadata.Name + " failed: Localization file was empty.");
			}
			string text2 = null;
			if (language != "English")
			{
				if (dictionary.TryGetValue(language, out var value))
				{
					text2 = File.ReadAllText(value);
				}
				else
				{
					byte[] array3 = LoadTranslationFromAssembly(language);
					if (array3 != null)
					{
						text2 = Encoding.UTF8.GetString(array3);
					}
				}
			}
			if (text2 == null && dictionary.TryGetValue("English", out var value2))
			{
				text2 = File.ReadAllText(value2);
			}
			if (text2 != null)
			{
				text2 = text2.TrimStart(new char[1] { '\ufeff' });
				foreach (KeyValuePair<string, string> item2 in new DeserializerBuilder().IgnoreFields().Build().Deserialize<Dictionary<string, string>>(text2) ?? new Dictionary<string, string>())
				{
					dictionary2[item2.Key] = item2.Value;
				}
			}
			loadedTexts[language] = dictionary2;
			foreach (KeyValuePair<string, string> item3 in dictionary2)
			{
				UpdatePlaceholderText(__instance, item3.Key);
			}
		}

		static Localizer()
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Expected O, but got Unknown
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Expected O, but got Unknown
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Expected O, but got Unknown
			PlaceholderProcessors = new Dictionary<string, Dictionary<string, Func<string>>>();
			loadedTexts = new Dictionary<string, Dictionary<string, string>>();
			localizationLanguage = new ConditionalWeakTable<Localization, string>();
			localizationObjects = new List<WeakReference<Localization>>();
			fileExtensions = new List<string>(2) { ".json", ".yml" };
			Harmony val = new Harmony("org.bepinex.helpers.LocalizationManager");
			val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(Localization), "SetupLanguage", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "LoadLocalization", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(FejdStartup), "SetupGui", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "LoadLocalizationLater", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(FejdStartup), "Start", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "SafeCallLocalizeComplete", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		[return: <ad47d627-eefa-4fa9-aeb5-d2cdcce92dc3>Nullable(2)]
		private static byte[] LoadTranslationFromAssembly(string language)
		{
			foreach (string fileExtension in fileExtensions)
			{
				byte[] array = ReadEmbeddedFileBytes("translations." + language + fileExtension);
				if (array != null)
				{
					return array;
				}
			}
			return null;
		}

		[<751c224d-bfbf-4350-9e31-32d445024a3b>NullableContext(2)]
		public static byte[] ReadEmbeddedFileBytes([<ad47d627-eefa-4fa9-aeb5-d2cdcce92dc3>Nullable(1)] string resourceFileName, Assembly containingAssembly = null)
		{
			using MemoryStream memoryStream = new MemoryStream();
			if ((object)containingAssembly == null)
			{
				containingAssembly = Assembly.GetCallingAssembly();
			}
			string text = containingAssembly.GetManifestResourceNames().FirstOrDefault([<751c224d-bfbf-4350-9e31-32d445024a3b>NullableContext(0)] (string str) => str.EndsWith(resourceFileName, StringComparison.Ordinal));
			if (text != null)
			{
				containingAssembly.GetManifestResourceStream(text)?.CopyTo(memoryStream);
			}
			return (memoryStream.Length == 0L) ? null : memoryStream.ToArray();
		}
	}
	public static class LocalizationManagerVersion
	{
		[<ad47d627-eefa-4fa9-aeb5-d2cdcce92dc3>Nullable(1)]
		public const string Version = "1.4.1";
	}
}
namespace Plantations
{
	[HarmonyPatch]
	internal static class Code_PlantationsRemoval
	{
		private sealed class TableCache
		{
			public int PieceCount = -1;

			public readonly HashSet<int> TrackedHashes = new HashSet<int>();
		}

		private const string Feature = "Code_PlantationsRemoval";

		private const float RemoveRayDistance = 50f;

		private const float TerrainModifierSearchRange = 2.5f;

		private static readonly Dictionary<PieceTable, TableCache> TableCaches = new Dictionary<PieceTable, TableCache>();

		private static PieceTable _forcedTable;

		private static Player _rayPlayer;

		private static Piece _rayPiece;

		private static bool Prepare()
		{
			return PatchSafety.IsEnabled("Code_PlantationsRemoval");
		}

		private static bool IsTrackedPlantPiece(Piece piece)
		{
			return Object.op_Implicit((Object)(object)piece) && PlantDefinitions.IsPieceHash(PlantDefinitions.GetPrefabHash(((Component)piece).gameObject));
		}

		private static PieceTable GetToolTable(Player player)
		{
			return (Object.op_Implicit((Object)(object)player) ? ((Humanoid)player).GetRightItem() : null)?.m_shared?.m_buildPieces;
		}

		private static bool TableContainsPiece(PieceTable table, Piece piece)
		{
			if (!Object.op_Implicit((Object)(object)table) || !Object.op_Implicit((Object)(object)piece) || table.m_pieces == null)
			{
				return false;
			}
			if (!TableCaches.TryGetValue(table, out var value))
			{
				value = new TableCache();
				TableCaches[table] = value;
			}
			if (value.PieceCount != table.m_pieces.Count)
			{
				value.TrackedHashes.Clear();
				foreach (GameObject piece2 in table.m_pieces)
				{
					if (Object.op_Implicit((Object)(object)piece2))
					{
						int nameHash = PlantDefinitions.GetNameHash(((Object)piece2).name);
						if (PlantDefinitions.TryGetByPieceHash(nameHash, out var def))
						{
							value.TrackedHashes.Add(nameHash);
							value.TrackedHashes.Add(def.PieceHash);
						}
					}
				}
				value.PieceCount = table.m_pieces.Count;
			}
			return value.TrackedHashes.Contains(PlantDefinitions.GetPrefabHash(((Component)piece).gameObject));
		}

		private static bool TryGetRemoveTarget(Player player, out Piece piece)
		{
			if ((Object)(object)_forcedTable != (Object)null && _rayPlayer == player)
			{
				piece = _rayPiece;
				return (Object)(object)piece != (Object)null;
			}
			piece = RaycastRemoveTarget(player);
			_rayPlayer = player;
			_rayPiece = piece;
			return (Object)(object)piece != (Object)null;
		}

		private static Piece RaycastRemoveTarget(Player player)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || (Object)(object)GameCamera.instance == (Object)null || (Object)(object)((Character)player).m_eye == (Object)null)
			{
				return null;
			}
			Transform transform = ((Component)GameCamera.instance).transform;
			RaycastHit val = default(RaycastHit);
			if (!Physics.Raycast(transform.position, transform.forward, ref val, 50f, player.m_removeRayMask))
			{
				return null;
			}
			if (Vector3.Distance(((RaycastHit)(ref val)).point, ((Character)player).m_eye.position) >= player.m_maxPlaceDistance)
			{
				return null;
			}
			Piece val2 = ((Component)((RaycastHit)(ref val)).collider).GetComponentInParent<Piece>();
			if (!Object.op_Implicit((Object)(object)val2) && Object.op_Implicit((Object)(object)((Component)((RaycastHit)(ref val)).collider).GetComponent<Heightmap>()))
			{
				val2 = TerrainModifier.FindClosestModifierPieceInRange(((RaycastHit)(ref val)).point, 2.5f);
			}
			return Object.op_Implicit((Object)(object)val2) ? val2 : null;
		}

		[HarmonyPatch(typeof(Player), "UpdatePlacement")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Player_UpdatePlacement_Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Expected O, but got Unknown
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			FieldInfo objB = AccessTools.DeclaredField(typeof(Player), "m_removePressedTime");
			MethodInfo methodInfo = AccessTools.PropertyGetter(typeof(Time), "time");
			MethodInfo methodInfo2 = AccessTools.DeclaredMethod(typeof(Code_PlantationsRemoval), "OnRemovePressed", (Type[])null, (Type[])null);
			int num = 0;
			for (int i = 1; i < list.Count; i++)
			{
				if (!(list[i].opcode != OpCodes.Stfld) && object.Equals(list[i].operand, objB) && CodeInstructionExtensions.Calls(list[i - 1], methodInfo))
				{
					list.InsertRange(i + 1, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[2]
					{
						new CodeInstruction(OpCodes.Ldarg_0, (object)null),
						new CodeInstruction(OpCodes.Call, (object)methodInfo2)
					});
					num++;
					i += 2;
				}
			}
			if (num != 1)
			{
				throw new InvalidOperationException("Native remove input sequence not found.");
			}
			return list;
		}

		private static void OnRemovePressed(Player player)
		{
			if (!PatchSafety.IsEnabled("Code_PlantationsRemoval"))
			{
				return;
			}
			try
			{
				TryForceRemoveFlag(player, takeInput: true);
			}
			catch (Exception error)
			{
				PatchSafety.Fail("Code_PlantationsRemoval", error);
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static PieceTable TryForceRemoveFlag(Player player, bool takeInput)
		{
			if (!takeInput || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer)
			{
				return null;
			}
			if (!((Character)player).InPlaceMode() || ((Character)player).IsDead())
			{
				return null;
			}
			if (Hud.IsPieceSelectionVisible())
			{
				return null;
			}
			PieceTable toolTable = GetToolTable(player);
			if (!Object.op_Implicit((Object)(object)toolTable) || toolTable.m_canRemovePieces)
			{
				return null;
			}
			if (!TryGetRemoveTarget(player, out var piece))
			{
				return null;
			}
			if (!IsTrackedPlantPiece(piece) || !TableContainsPiece(toolTable, piece))
			{
				return null;
			}
			toolTable.m_canRemovePieces = true;
			_forcedTable = toolTable;
			return toolTable;
		}

		[HarmonyFinalizer]
		[HarmonyPatch(typeof(Player), "UpdatePlacement")]
		private static Exception Player_UpdatePlacement_Finalizer(Exception __exception)
		{
			if ((Object)(object)_forcedTable != (Object)null)
			{
				_forcedTable.m_canRemovePieces = false;
			}
			_forcedTable = null;
			_rayPlayer = null;
			_rayPiece = null;
			return __exception;
		}

		[HarmonyPatch(typeof(Player), "RemovePiece")]
		[HarmonyPrefix]
		private static bool Player_RemovePiece_Prefix(Player __instance, ref bool __result)
		{
			if (!PatchSafety.IsEnabled("Code_PlantationsRemoval"))
			{
				return true;
			}
			try
			{
				if (AllowRemove(__instance))
				{
					return true;
				}
				__result = false;
				return false;
			}
			catch (Exception error)
			{
				PatchSafety.Fail("Code_PlantationsRemoval", error);
				return true;
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static bool AllowRemove(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return true;
			}
			if (!TryGetRemoveTarget(player, out var piece))
			{
				return true;
			}
			if (IsTrackedPlantPiece(piece))
			{
				return TableContainsPiece(GetToolTable(player), piece);
			}
			return (Object)(object)_forcedTable == (Object)null;
		}
	}
	[HarmonyPatch]
	internal static class Code_PlantationsHarvest
	{
		private static readonly HashSet<Pickable> LoadedPlants = new HashSet<Pickable>();

		private const string Feature = "Code_PlantationsHarvest";

		private const float RespawnUpdateInterval = 10f;

		private const float RespawnInvokeMinDelay = 1f;

		private const float RespawnInvokeMaxDelay = 2f;

		private const string UpdateRespawnMethod = "UpdateRespawn";

		private static bool Prepare()
		{
			return PatchSafety.IsEnabled("Code_PlantationsHarvest");
		}

		internal static void ClearLoadedPlants()
		{
			LoadedPlants.Clear();
		}

		internal static void RefreshLoadedPlants()
		{
			if (!PatchSafety.IsEnabled("Code_PlantationsHarvest"))
			{
				return;
			}
			try
			{
				LoadedPlants.RemoveWhere((Pickable pickable) => !Object.op_Implicit((Object)(object)pickable));
				foreach (Pickable loadedPlant in LoadedPlants)
				{
					if (PlantDefinitions.TryGetPiece(((Component)loadedPlant).gameObject, out var def))
					{
						ApplyFullModConfig(loadedPlant, def);
					}
				}
			}
			catch (Exception error)
			{
				PatchSafety.Fail("Code_PlantationsHarvest", error);
			}
		}

		[HarmonyPatch(typeof(ZNetView), "OnDestroy")]
		[HarmonyPostfix]
		private static void ZNetView_OnDestroy_Postfix(ZNetView __instance)
		{
			Pickable component = ((Component)__instance).GetComponent<Pickable>();
			if (Object.op_Implicit((Object)(object)component))
			{
				LoadedPlants.Remove(component);
			}
		}

		internal static float SanitizeRespawnMinutes(float minutes)
		{
			if (float.IsNaN(minutes) || float.IsInfinity(minutes))
			{
				return 60f;
			}
			return Mathf.Clamp(minutes, 1f, 100000f);
		}

		private static void ApplyFullModConfig(Pickable pickable, PlantDefinition def)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			GameObject prefab = PlantDefinitions.GetPrefab(def.NormalDropPrefab);
			if (Object.op_Implicit((Object)(object)prefab))
			{
				pickable.m_itemPrefab = prefab;
			}
			pickable.m_amount = Mathf.Max(1, def.DropAmount());
			pickable.m_respawnTimeMinutes = SanitizeRespawnMinutes(def.RespawnMinutes());
			pickable.m_extraDrops = (DropTable)(((object)PlantDefinitions.GetSeedTable(def)) ?? ((object)new DropTable()));
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Pickable), "Awake")]
		private static void Pickable_Awake_Prefix(Pickable __instance)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			if (!PatchSafety.IsEnabled("Code_PlantationsHarvest"))
			{
				return;
			}
			try
			{
				AwakePrefix(__instance);
			}
			catch (Exception error)
			{
				PatchSafety.Fail("Code_PlantationsHarvest", error);
			}
			finally
			{
				if (Object.op_Implicit((Object)(object)__instance) && __instance.m_extraDrops == null)
				{
					__instance.m_extraDrops = new DropTable();
				}
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void AwakePrefix(Pickable pickable)
		{
			if (Object.op_Implicit((Object)(object)pickable) && PlantDefinitions.TryGetPiece(((Component)pickable).gameObject, out var def))
			{
				ApplyFullModConfig(pickable, def);
			}
		}

		[HarmonyPatch(typeof(Pickable), "Awake")]
		[HarmonyPostfix]
		private static void Pickable_Awake_Postfix(Pickable __instance)
		{
			if (!PatchSafety.IsEnabled("Code_PlantationsHarvest"))
			{
				return;
			}
			try
			{
				AwakePostfix(__instance);
			}
			catch (Exception error)
			{
				PatchSafety.Fail("Code_PlantationsHarvest", error);
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void AwakePostfix(Pickable pickable)
		{
			if (Object.op_Implicit((Object)(object)pickable))
			{
				ZNetView nview = pickable.m_nview;
				if (Object.op_Implicit((Object)(object)nview) && nview.IsValid() && !(pickable.m_respawnTimeMinutes <= 0f) && PlantDefinitions.TryGetPiece(((Component)pickable).gameObject, out var _))
				{
					LoadedPlants.Add(pickable);
					((MonoBehaviour)pickable).CancelInvoke("UpdateRespawn");
					((MonoBehaviour)pickable).InvokeRepeating("UpdateRespawn", Random.Range(1f, 2f), 10f);
				}
			}
		}

		[HarmonyPatch(typeof(Pickable), "RPC_Pick")]
		[HarmonyPrefix]
		private static void Pickable_RPC_Pick_Prefix(Pickable __instance, out bool __state)
		{
			__state = false;
			if (!PatchSafety.IsEnabled("Code_PlantationsHarvest"))
			{
				return;
			}
			try
			{
				__state = WillVanillaPick(__instance);
			}
			catch (Exception error)
			{
				PatchSafety.Fail("Code_PlantationsHarvest", error);
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static bool WillVanillaPick(Pickable pickable)
		{
			if (!Object.op_Implicit((Object)(object)pickable))
			{
				return false;
			}
			ZNetView nview = pickable.m_nview;
			return Object.op_Implicit((Object)(object)nview) && nview.IsValid() && nview.IsOwner() && !pickable.m_picked;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Pickable), "RPC_Pick")]
		private static void Pickable_RPC_Pick_Postfix(Pickable __instance, bool __state)
		{
			if (!__state || !PatchSafety.IsEnabled("Code_PlantationsHarvest"))
			{
				return;
			}
			try
			{
				DropVanillaSeed(__instance);
			}
			catch (Exception error)
			{
				PatchSafety.Fail("Code_PlantationsHarvest", error);
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void DropVanillaSeed(Pickable pickable)
		{
			if (!PlantDefinitions.TryGetVanilla(((Component)pickable).gameObject, out var def) || !def.VanillaSeedDropEnabled())
			{
				return;
			}
			int num = def.SeedAmount();
			float num2 = Mathf.Clamp01(def.SeedChance());
			if (num > 0 && !(num2 <= 0f) && !(Random.value > num2))
			{
				GameObject prefab = PlantDefinitions.GetPrefab(def.SeedPrefab);
				if (Object.op_Implicit((Object)(object)prefab))
				{
					pickable.Drop(prefab, pickable.m_amount + 1, num);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "Interact")]
	internal static class Code_PlantationsAreaHarvest
	{
		private static ConfigEntry<bool> Enabled;

		private static ConfigEntry<float> Radius;

		internal static void Initialize(ConfigFile config)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			Enabled = config.Bind<bool>("18 - Area Harvest", "18.01 - Enabled", true, "Hold Left Shift or Right Shift and use the game's interact key (default E) on a plant to harvest nearby pickables. Uses native harvesting without replanting.");
			Radius = config.Bind<float>("18 - Area Harvest", "18.02 - Radius", 6f, new ConfigDescription("Harvest radius in metres around the selected plant. Only loaded pickables are harvested.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), Array.Empty<object>()));
		}

		[HarmonyPrefix]
		private static void Prefix(Player __instance, GameObject go, bool hold, out Pickable __state)
		{
			__state = null;
			if (Enabled == null || !Enabled.Value || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || !Object.op_Implicit((Object)(object)go) || ((Character)__instance).InAttack() || ((Character)__instance).InDodge() || (hold && Time.time - __instance.m_lastHoverInteractTime < 0.2f) || (!ZInput.GetKey((KeyCode)304, false) && !ZInput.GetKey((KeyCode)303, false)))
			{
				return;
			}
			Pickable componentInParent = go.GetComponentInParent<Pickable>();
			if (!Object.op_Implicit((Object)(object)componentInParent) || !Object.op_Implicit((Object)(object)componentInParent.m_nview) || !componentInParent.m_nview.IsValid() || componentInParent.m_picked || componentInParent.m_pickedLocal || componentInParent.m_enabled == 0)
			{
				return;
			}
			if (componentInParent.m_tarPreventsPicking)
			{
				Floating component = ((Component)componentInParent).GetComponent<Floating>();
				if (component != null && component.IsInTar())
				{
					return;
				}
			}
			__state = componentInParent;
		}

		[HarmonyPostfix]
		private static void Postfix(Player __instance, Pickable __state, bool alt)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)__state) || !__state.m_pickedLocal)
			{
				return;
			}
			HashSet<Pickable> hashSet = new HashSet<Pickable> { __state };
			Collider[] array = Physics.OverlapSphere(((Component)__state).transform.position, Radius.Value, PlantationsGridCollisions.CollisionMask);
			foreach (Collider val in array)
			{
				Pickable componentInParent = ((Component)val).GetComponentInParent<Pickable>();
				if (Object.op_Implicit((Object)(object)componentInParent) && hashSet.Add(componentInParent) && Object.op_Implicit((Object)(object)componentInParent.m_nview) && componentInParent.m_nview.IsValid() && !componentInParent.m_picked && !componentInParent.m_pickedLocal && componentInParent.m_enabled != 0)
				{
					componentInParent.Interact((Humanoid)(object)__instance, false, alt);
				}
			}
		}
	}
	[HarmonyPatch(typeof(AudioMan), "Awake")]
	internal static class Code_PlantationsAudioMixer
	{
		private static AssetBundle Bundle;

		internal static void Initialize()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			if ((int)SystemInfo.graphicsDeviceType != 4)
			{
				Bundle = PrefabManager.RegisterAssetBundle("rd_plantations");
				Route(AudioMan.instance);
			}
		}

		[HarmonyPostfix]
		private static void Postfix(AudioMan __instance)
		{
			if ((Object)(object)__instance == (Object)(object)AudioMan.instance)
			{
				Route(__instance);
			}
		}

		private static void Route(AudioMan audioMan)
		{
			if (!Object.op_Implicit((Object)(object)Bundle) || !Object.op_Implicit((Object)(object)audioMan) || !Object.op_Implicit((Object)(object)audioMan.m_masterMixer))
			{
				return;
			}
			AudioMixerGroup val = null;
			AudioMixerGroup[] array = audioMan.m_masterMixer.FindMatchingGroups(string.Empty);
			foreach (AudioMixerGroup val2 in array)
			{
				if (Object.op_Implicit((Object)(object)val2) && string.Equals(((Object)val2).name, "SFX", StringComparison.OrdinalIgnoreCase))
				{
					val = val2;
					break;
				}
			}
			if (!Object.op_Implicit((Object)(object)val))
			{
				return;
			}
			HashSet<AudioSource> hashSet = new HashSet<AudioSource>();
			HashSet<AudioMixer> hashSet2 = new HashSet<AudioMixer>();
			GameObject[] array2 = Bundle.LoadAllAssets<GameObject>();
			foreach (GameObject val3 in array2)
			{
				if (!Object.op_Implicit((Object)(object)val3))
				{
					continue;
				}
				AudioSource[] componentsInChildren = val3.GetComponentsInChildren<AudioSource>(true);
				foreach (AudioSource val4 in componentsInChildren)
				{
					hashSet.Add(val4);
					if (Object.op_Implicit((Object)(object)val4.outputAudioMixerGroup) && Object.op_Implicit((Object)(object)val4.outputAudioMixerGroup.audioMixer))
					{
						hashSet2.Add(val4.outputAudioMixerGroup.audioMixer);
					}
				}
			}
			AudioSource[] array3 = Resources.FindObjectsOfTypeAll<AudioSource>();
			foreach (AudioSource val5 in array3)
			{
				if (Object.op_Implicit((Object)(object)val5) && Object.op_Implicit((Object)(object)val5.outputAudioMixerGroup) && hashSet2.Contains(val5.outputAudioMixerGroup.audioMixer))
				{
					hashSet.Add(val5);
				}
			}
			foreach (AudioSource item in hashSet)
			{
				if (Object.op_Implicit((Object)(object)item))
				{
					item.outputAudioMixerGroup = val;
				}
			}
		}
	}
	internal static class PatchSafety
	{
		private static readonly HashSet<string> DisabledFeatures = new HashSet<string>(StringComparer.Ordinal);

		internal static bool IsEnabled(string feature)
		{
			return !DisabledFeatures.Contains(feature);
		}

		internal static void Fail(string feature, Exception error)
		{
			if (DisabledFeatures.Add(feature))
			{
				ManualLogSource log = PlantationsPlugin.Log;
				if (log != null)
				{
					log.LogError((object)$"[{feature}] disabled after an unexpected error: {error}");
				}
			}
		}

		internal static void ApplyAll(Harmony harmony)
		{
			Type[] array = new Type[15]
			{
				typeof(Code_PlantationsRemoval),
				typeof(Code_PlantationsHarvest),
				typeof(Code_PlantationsAreaHarvest),
				typeof(Code_PlantationsAudioMixer),
				typeof(Code_PlantationsGrowth),
				typeof(Code_PlantationsTimers),
				typeof(Code_PlantationsGridRegistration),
				typeof(Code_PlantationsGridControls),
				typeof(Code_PlantationsGridHints),
				typeof(Code_PlantationsGridCost),
				typeof(Code_PlantationsGridPlacement.PlayerSetupPlacementGhost),
				typeof(Code_PlantationsGridPlacement.PlayerUpdatePlacementGhost),
				typeof(Code_PlantationsGridPlacement.PlayerTryPlacePiece),
				typeof(Code_PlantationsGridPlacement.PlayerPlacePiece),
				typeof(RegisterClientRPCPatch)
			};
			Type[] array2 = array;
			foreach (Type type in array2)
			{
				if (PlantationsGridContext.config != null || (!type.Name.StartsWith("Code_PlantationsGrid", StringComparison.Ordinal) && !(type.DeclaringType == typeof(Code_PlantationsGridPlacement))))
				{
					try
					{
						harmony.CreateClassProcessor(type).Patch();
					}
					catch (Exception error)
					{
						Fail(type.Name, error);
					}
				}
			}
		}
	}
	internal sealed class PlantDefinition
	{
		public string PiecePrefab;

		public string SeedlingPrefab;

		public string VanillaPickable;

		public string NormalDropPrefab;

		public string SeedPrefab;

		public Func<int> DropAmount;

		public Func<int> SeedAmount;

		public Func<float> SeedChance;

		public Func<float> RespawnMinutes;

		public Func<bool> VanillaSeedDropEnabled;

		public int PieceHash;

		public int SeedlingHash;

		public int VanillaHash;

		internal DropTable CachedSeedTable;

		internal bool SeedTableBuilt;
	}
	internal static class PlantDefinitions
	{
		private const string CloneSuffix = "(Clone)";

		private const int NameCacheLimit = 1024;

		internal static readonly PlantDefinition[] All;

		private static readonly Dictionary<int, PlantDefinition> ByPieceHash;

		private static readonly Dictionary<int, PlantDefinition> ByVanillaHash;

		private static readonly Dictionary<string, int> NameHashCache;

		private static ConfigFile _configuration;

		static PlantDefinitions()
		{
			All = new PlantDefinition[9]
			{
				Def("RD_Dandelion", "Pickable_Dandelion", "Dandelion", "RD_seed_dandelion", () => PlantationsPlugin.CE_RD_Dandelion_DropAmount.Value, () => PlantationsPlugin.CE_RD_Dandelion_ExtraDropAmount.Value, () => PlantationsPlugin.CE_RD_Dandelion_ExtraDropChance.Value, () => PlantationsPlugin.CE_RD_Dandelion_RespawnTimeMinutes.Value, () => PlantationsPlugin.CE_RD_Dandelion_VanillaSeedDropEnabled.Value),
				Def("RD_Mushroom", "Pickable_Mushroom", "Mushroom", "RD_red_spore", () => PlantationsPlugin.CE_RD_Mushroom_DropAmount.Value, () => PlantationsPlugin.CE_RD_Mushroom_ExtraDropAmount.Value, () => PlantationsPlugin.CE_RD_Mushroom_ExtraDropChance.Value, () => PlantationsPlugin.CE_RD_Mushroom_RespawnTimeMinutes.Value, () => PlantationsPlugin.CE_RD_Mushroom_VanillaSeedDropEnabled.Value),
				Def("RD_Mushroom_yellow", "Pickable_Mushroom_yellow", "MushroomYellow", "RD_yellow_spore", () => PlantationsPlugin.CE_RD_Mushroom_yellow_DropAmount.Value, () => PlantationsPlugin.CE_RD_Mushroom_yellow_ExtraDropAmount.Value, () => PlantationsPlugin.CE_RD_Mushroom_yellow_ExtraDropChance.Value, () => PlantationsPlugin.CE_RD_Mushroom_yellow_RespawnTimeMinutes.Value, () => PlantationsPlugin.CE_RD_Mushroom_yellow_VanillaSeedDropEnabled.Value),
				Def("RD_RaspberryBush", "RaspberryBush", "Raspberry", "RD_seed_raspberry", () => PlantationsPlugin.CE_RD_RaspberryBush_DropAmount.Value, () => PlantationsPlugin.CE_RD_RaspberryBush_ExtraDropAmount.Value, () => PlantationsPlugin.CE_RD_RaspberryBush_ExtraDropChance.Value, () => PlantationsPlugin.CE_RD_RaspberryBush_RespawnTimeMinutes.Value, () => PlantationsPlugin.CE_RD_RaspberryBush_VanillaSeedDropEnabled.Value),
				Def("RD_BlueberryBush", "BlueberryBush", "Blueberries", "RD_seed_blueberry", () => PlantationsPlugin.CE_RD_BlueberryBush_DropAmount.Value, () => PlantationsPlugin.CE_RD_BlueberryBush_ExtraDropAmount.Value, () => PlantationsPlugin.CE_RD_BlueberryBush_ExtraDropChance.Value, () => PlantationsPlugin.CE_RD_BlueberryBush_RespawnTimeMinutes.Value, () => PlantationsPlugin.CE_RD_BlueberryBush_VanillaSeedDropEnabled.Value),
				Def("RD_thistle", "Pickable_Thistle", "Thistle", "RD_seed_thistle", () => PlantationsPlugin.CE_RD_thistle_DropAmount.Value, () => PlantationsPlugin.CE_RD_thistle_ExtraDropAmount.Value, () => PlantationsPlugin.CE_RD_thistle_ExtraDropChance.Value, () => PlantationsPlugin.CE_RD_thistle_RespawnTimeMinutes.Value, () => PlantationsPlugin.CE_RD_thistle_VanillaSeedDropEnabled.Value),
				Def("RD_CloudberryBush", "CloudberryBush", "Cloudberry", "RD_seed_cloudberry", () => PlantationsPlugin.CE_RD_CloudberryBush_DropAmount.Value, () => PlantationsPlugin.CE_RD_CloudberryBush_ExtraDropAmount.Value, () => PlantationsPlugin.CE_RD_CloudberryBush_ExtraDropChance.Value, () => PlantationsPlugin.CE_RD_CloudberryBush_RespawnTimeMinutes.Value, () => PlantationsPlugin.CE_RD_CloudberryBush_VanillaSeedDropEnabled.Value),
				Def("RD_MushroomSmokePuff", "Pickable_SmokePuff", "MushroomSmokePuff", "RD_seed_smokepuff", () => PlantationsPlugin.CE_RD_MushroomSmokePuff_DropAmount.Value, () => PlantationsPlugin.CE_RD_MushroomSmokePuff_ExtraDropAmount.Value, () => PlantationsPlugin.CE_RD_MushroomSmokePuff_ExtraDropChance.Value, () => PlantationsPlugin.CE_RD_MushroomSmokePuff_RespawnTimeMinutes.Value, () => PlantationsPlugin.CE_RD_MushroomSmokePuff_VanillaSeedDropEnabled.Value),
				Def("RD_Fiddlehead", "Pickable_Fiddlehead", "Fiddleheadfern", "RD_seed_fiddlehead", () => PlantationsPlugin.CE_RD_Fiddlehead_DropAmount.Value, () => PlantationsPlugin.CE_RD_Fiddlehead_ExtraDropAmount.Value, () => PlantationsPlugin.CE_RD_Fiddlehead_ExtraDropChance.Value, () => PlantationsPlugin.CE_RD_Fiddlehead_RespawnTimeMinutes.Value, () => PlantationsPlugin.CE_RD_Fiddlehead_VanillaSeedDropEnabled.Value)
			};
			ByPieceHash = new Dictionary<int, PlantDefinition>();
			ByVanillaHash = new Dictionary<int, PlantDefinition>();
			NameHashCache = new Dictionary<string, int>(64, StringComparer.Ordinal);
			PlantDefinition[] all = All;
			foreach (PlantDefinition plantDefinition in all)
			{
				plantDefinition.PieceHash = StringExtensionMethods.GetStableHashCode(plantDefinition.PiecePrefab);
				plantDefinition.SeedlingHash = StringExtensionMethods.GetStableHashCode(plantDefinition.SeedlingPrefab);
				plantDefinition.VanillaHash = StringExtensionMethods.GetStableHashCode(plantDefinition.VanillaPickable);
				ByPieceHash[plantDefinition.PieceHash] = plantDefinition;
				ByPieceHash[plantDefinition.SeedlingHash] = plantDefinition;
				ByVanillaHash[plantDefinition.VanillaHash] = plantDefinition;
			}
		}

		private static PlantDefinition Def(string piece, string vanilla, string drop, string seed, Func<int> dropAmount, Func<int> seedAmount, Func<float> seedChance, Func<float> respawn, Func<bool> vanillaSeed)
		{
			return new PlantDefinition
			{
				PiecePrefab = piece,
				SeedlingPrefab = piece + "_seedling",
				VanillaPickable = vanilla,
				NormalDropPrefab = drop,
				SeedPrefab = seed,
				DropAmount = dropAmount,
				SeedAmount = seedAmount,
				SeedChance = seedChance,
				RespawnMinutes = respawn,
				VanillaSeedDropEnabled = vanillaSeed
			};
		}

		internal static void Init(ConfigFile config)
		{
			Shutdown();
			_configuration = config;
			config.SettingChanged += OnConfigurationChanged;
		}

		private static void OnConfigurationChanged(object sender, SettingChangedEventArgs args)
		{
			InvalidateCaches();
			PlantationsPlugin.Instance.QueuePlantRefresh();
		}

		internal static void Shutdown()
		{
			if (_configuration != null)
			{
				_configuration.SettingChanged -= OnConfigurationChanged;
			}
			_configuration = null;
			InvalidateCaches();
			NameHashCache.Clear();
		}

		internal static void InvalidateCaches()
		{
			PlantDefinition[] all = All;
			foreach (PlantDefinition plantDefinition in all)
			{
				plantDefinition.CachedSeedTable = null;
				plantDefinition.SeedTableBuilt = false;
			}
		}

		internal static int GetPrefabHash(GameObject obj)
		{
			if (!Object.op_Implicit((Object)(object)obj))
			{
				return 0;
			}
			ZNetView component = obj.GetComponent<ZNetView>();
			ZDO val = (Object.op_Implicit((Object)(object)component) ? component.GetZDO() : null);
			if (val != null)
			{
				return val.GetPrefab();
			}
			return GetNameHash(((Object)obj).name);
		}

		internal static int GetNameHash(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return 0;
			}
			if (NameHashCache.TryGetValue(name, out var value))
			{
				return value;
			}
			string text = (name.EndsWith("(Clone)", StringComparison.Ordinal) ? name.Substring(0, name.Length - "(Clone)".Length) : name);
			value = StringExtensionMethods.GetStableHashCode(text);
			if (NameHashCache.Count >= 1024)
			{
				NameHashCache.Clear();
			}
			NameHashCache[name] = value;
			return value;
		}

		internal static bool IsPieceHash(int hash)
		{
			return ByPieceHash.ContainsKey(hash);
		}

		internal static bool TryGetByPieceHash(int hash, out PlantDefinition def)
		{
			return ByPieceHash.TryGetValue(hash, out def);
		}

		internal static bool TryGetPiece(GameObject obj, out PlantDefinition def)
		{
			def = null;
			return Object.op_Implicit((Object)(object)obj) && ByPieceHash.TryGetValue(GetPrefabHash(obj), out def);
		}

		internal static bool TryGetVanilla(GameObject obj, out PlantDefinition def)
		{
			def = null;
			return Object.op_Implicit((Object)(object)obj) && ByVanillaHash.TryGetValue(GetPrefabHash(obj), out def);
		}

		internal static GameObject GetPrefab(string prefabName)
		{
			if (string.IsNullOrEmpty(prefabName))
			{
				return null;
			}
			GameObject val = (Object.op_Implicit((Object)(object)ObjectDB.instance) ? ObjectDB.instance.GetItemPrefab(prefabName) : null);
			if (Object.op_Implicit((Object)(object)val))
			{
				return val;
			}
			return Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.GetPrefab(prefabName) : null;
		}

		internal static DropTable GetSeedTable(PlantDefinition def)
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Expected O, but got Unknown
			if (def.SeedTableBuilt)
			{
				return def.CachedSeedTable;
			}
			int num = def.SeedAmount();
			float num2 = Mathf.Clamp01(def.SeedChance());
			GameObject prefab = GetPrefab(def.SeedPrefab);
			if (num <= 0 || num2 <= 0f)
			{
				def.CachedSeedTable = null;
				def.SeedTableBuilt = true;
				return null;
			}
			if (!Object.op_Implicit((Object)(object)prefab))
			{
				return null;
			}
			def.CachedSeedTable = new DropTable
			{
				m_dropMin = 1,
				m_dropMax = 1,
				m_dropChance = num2,
				m_oneOfEach = false,
				m_drops = new List<DropData>
				{
					new DropData
					{
						m_item = prefab,
						m_stackMin = num,
						m_stackMax = num,
						m_weight = 1f,
						m_dontScale = false
					}
				}
			};
			def.SeedTableBuilt = true;
			return def.CachedSeedTable;
		}
	}
	[HarmonyPatch]
	internal static class Code_PlantationsGrowth
	{
		private const string Feature = "Code_PlantationsGrowth";

		private static bool Prepare()
		{
			return PatchSafety.IsEnabled("Code_PlantationsGrowth");
		}

		[HarmonyPatch(typeof(Plant), "GetGrowTime")]
		[HarmonyPrefix]
		private static bool Plant_GetGrowTime_Prefix(Plant __instance, ref float __result)
		{
			if (!PatchSafety.IsEnabled("Code_PlantationsGrowth") || !PlantDefinitions.TryGetPiece(((Component)__instance).gameObject, out var def) || PlantDefinitions.GetPrefabHash(((Component)__instance).gameObject) != def.SeedlingHash)
			{
				return true;
			}
			__result = Code_PlantationsHarvest.SanitizeRespawnMinutes(def.RespawnMinutes()) * 60f;
			return false;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Piece), "Awake")]
		private static void Piece_Awake_Postfix(Piece __instance)
		{
			if (!PatchSafety.IsEnabled("Code_PlantationsGrowth"))
			{
				return;
			}
			try
			{
				ApplyResources(__instance);
			}
			catch (Exception error)
			{
				PatchSafety.Fail("Code_PlantationsGrowth", error);
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void ApplyResources(Piece piece)
		{
			int prefabHash = PlantDefinitions.GetPrefabHash(((Component)piece).gameObject);
			if (PlantDefinitions.TryGetByPieceHash(prefabHash, out var def) && prefabHash == def.PieceHash)
			{
				GameObject prefab = PlantDefinitions.GetPrefab(def.SeedlingPrefab);
				Piece val = (Object.op_Implicit((Object)(object)prefab) ? prefab.GetComponent<Piece>() : null);
				if (Object.op_Implicit((Object)(object)val))
				{
					piece.m_resources = val.m_resources;
				}
			}
		}

		[HarmonyPatch(typeof(Plant), "Grow")]
		[HarmonyPrefix]
		private static void Plant_Grow_Prefix(Plant __instance, out (long Creator, int PlatformIndex) __state)
		{
			__state = (Creator: 0L, PlatformIndex: -1);
			if (!PatchSafety.IsEnabled("Code_PlantationsGrowth"))
			{
				return;
			}
			try
			{
				__state = GetCreator(__instance);
			}
			catch (Exception error)
			{
				PatchSafety.Fail("Code_PlantationsGrowth", error);
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static (long, int) GetCreator(Plant plant)
		{
			int prefabHash = PlantDefinitions.GetPrefabHash(((Component)plant).gameObject);
			if (!PlantDefinitions.TryGetByPieceHash(prefabHash, out var def) || prefabHash != def.SeedlingHash)
			{
				return (0L, -1);
			}
			Piece component = ((Component)plant).GetComponent<Piece>();
			return Object.op_Implicit((Object)(object)component) ? (component.GetCreator(), component.GetCreatorPlatformUserIdIndex()) : (0L, -1);
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Plant), "Grow")]
		private static void Plant_Grow_Postfix(GameObject __result, (long Creator, int PlatformIndex) __state)
		{
			if (!PatchSafety.IsEnabled("Code_PlantationsGrowth"))
			{
				return;
			}
			try
			{
				TransferCreator(__result, __state);
			}
			catch (Exception error)
			{
				PatchSafety.Fail("Code_PlantationsGrowth", error);
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void TransferCreator(GameObject grown, (long Creator, int PlatformIndex) creator)
		{
			if (!Object.op_Implicit((Object)(object)grown) || creator.Creator == 0)
			{
				return;
			}
			int prefabHash = PlantDefinitions.GetPrefabHash(grown);
			if (PlantDefinitions.TryGetByPieceHash(prefabHash, out var def) && prefabHash == def.PieceHash)
			{
				Piece component = grown.GetComponent<Piece>();
				ZNetView val = (Object.op_Implicit((Object)(object)component) ? component.m_nview : null);
				if (Object.op_Implicit((Object)(object)val) && val.IsValid() && val.IsOwner() && component.GetCreator() == 0)
				{
					ZDO zDO = val.GetZDO();
					zDO.Set(ZDOVars.s_creator, creator.Creator);
					zDO.Set(ZDOVars.s_creatorIndex, creator.PlatformIndex, false);
					(component.m_creator, component.m_creatorPlatformUserIDIndex) = creator;
				}
			}
		}
	}
	public static class EffectRegistry
	{
		public static void Register()
		{
			PrefabManager.RegisterPrefab("rd_plantations", "vfx_Place_smallitem_rd_plantations");
			PrefabManager.RegisterPrefab("rd_plantations", "sfx_build_cultivator_rd_plantations");
			PrefabManager.RegisterPrefab("rd_plantations", "vfx_bush_destroyed_rd_plantations");
			PrefabManager.RegisterPrefab("rd_plantations", "vfx_bush_leaf_puff_rd_plantations");
			PrefabManager.RegisterPrefab("rd_plantations", "sfx_bush_hit_rd_plantations");
			PrefabManager.RegisterPrefab("rd_plantations", "sfx_pickable_pick_rd_plantations");
			PrefabManager.RegisterPrefab("rd_plantations", "vfx_pickable_pick_rd_plantations");
		}
	}
	[BepInPlugin("radamanto.Plantations", "Plantations", "1.0.4")]
	[<751c224d-bfbf-4350-9e31-32d445024a3b>NullableContext(1)]
	[<ad47d627-eefa-4fa9-aeb5-d2cdcce92dc3>Nullable(0)]
	[BepInIncompatibility("advize.PlantEasily")]
	public class PlantationsPlugin : BaseUnityPlugin
	{
		[<751c224d-bfbf-4350-9e31-32d445024a3b>NullableContext(0)]
		private class ConfigurationManagerAttributes
		{
			public bool? Browsable = false;
		}

		internal const string ModGUID = "radamanto.Plantations";

		internal const string ModName = "Plantations";

		internal const string ModVersion = "1.0.4";

		[<ad47d627-eefa-4fa9-aeb5-d2cdcce92dc3>Nullable(2)]
		internal static ManualLogSource Log;

		internal static PlantationsPlugin Instance;

		private Harmony _harmony = null;

		private bool _plantRefreshScheduled;

		internal const float MinRespawnMinutes = 1f;

		internal const float MaxRespawnMinutes = 100000f;

		internal const float DefaultRespawnMinutes = 60f;

		public static ConfigEntry<bool> CE_VanillaPlantTimerUI_Enabled;

		public static ConfigEntry<int> CE_RD_Dandelion_DropAmount;

		public static ConfigEntry<int> CE_RD_Dandelion_ExtraDropAmount;

		public static ConfigEntry<float> CE_RD_Dandelion_ExtraDropChance;

		public static ConfigEntry<bool> CE_RD_Dandelion_VanillaSeedDropEnabled;

		public static ConfigEntry<float> CE_RD_Dandelion_RespawnTimeMinutes;

		public static ConfigEntry<int> CE_RD_Mushroom_DropAmount;

		public static ConfigEntry<int> CE_RD_Mushroom_ExtraDropAmount;

		public static ConfigEntry<float> CE_RD_Mushroom_ExtraDropChance;

		public static ConfigEntry<bool> CE_RD_Mushroom_VanillaSeedDropEnabled;

		public static ConfigEntry<float> CE_RD_Mushroom_RespawnTimeMinutes;

		public static ConfigEntry<int> CE_RD_Mushroom_yellow_DropAmount;

		public static ConfigEntry<int> CE_RD_Mushroom_yellow_ExtraDropAmount;

		public static ConfigEntry<float> CE_RD_Mushroom_yellow_ExtraDropChance;

		public static ConfigEntry<bool> CE_RD_Mushroom_yellow_VanillaSeedDropEnabled;

		public static ConfigEntry<float> CE_RD_Mushroom_yellow_RespawnTimeMinutes;

		public static ConfigEntry<int> CE_RD_RaspberryBush_DropAmount;

		public static ConfigEntry<int> CE_RD_RaspberryBush_ExtraDropAmount;

		public static ConfigEntry<float> CE_RD_RaspberryBush_ExtraDropChance;

		public static ConfigEntry<bool> CE_RD_RaspberryBush_VanillaSeedDropEnabled;

		public static ConfigEntry<float> CE_RD_RaspberryBush_RespawnTimeMinutes;

		public static ConfigEntry<int> CE_RD_BlueberryBush_DropAmount;

		public static ConfigEntry<int> CE_RD_BlueberryBush_ExtraDropAmount;

		public static ConfigEntry<float> CE_RD_BlueberryBush_ExtraDropChance;

		public static ConfigEntry<bool> CE_RD_BlueberryBush_VanillaSeedDropEnabled;

		public static ConfigEntry<float> CE_RD_BlueberryBush_RespawnTimeMinutes;

		public static ConfigEntry<int> CE_RD_thistle_DropAmount;

		public static ConfigEntry<int> CE_RD_thistle_ExtraDropAmount;

		public static ConfigEntry<float> CE_RD_thistle_ExtraDropChance;

		public static ConfigEntry<bool> CE_RD_thistle_VanillaSeedDropEnabled;

		public static ConfigEntry<float> CE_RD_thistle_RespawnTimeMinutes;

		public static ConfigEntry<int> CE_RD_CloudberryBush_DropAmount;

		public static ConfigEntry<int> CE_RD_CloudberryBush_ExtraDropAmount;

		public static ConfigEntry<float> CE_RD_CloudberryBush_ExtraDropChance;

		public static ConfigEntry<bool> CE_RD_CloudberryBush_VanillaSeedDropEnabled;

		public static ConfigEntry<float> CE_RD_CloudberryBush_RespawnTimeMinutes;

		public static ConfigEntry<int> CE_RD_MushroomSmokePuff_DropAmount;

		public static ConfigEntry<int> CE_RD_MushroomSmokePuff_ExtraDropAmount;

		public static ConfigEntry<float> CE_RD_MushroomSmokePuff_ExtraDropChance;

		public static ConfigEntry<bool> CE_RD_MushroomSmokePuff_VanillaSeedDropEnabled;

		public static ConfigEntry<float> CE_RD_MushroomSmokePuff_RespawnTimeMinutes;

		public static ConfigEntry<int> CE_RD_Fiddlehead_DropAmount;

		public static ConfigEntry<int> CE_RD_Fiddlehead_ExtraDropAmount;

		public static ConfigEntry<float> CE_RD_Fiddlehead_ExtraDropChance;

		public static ConfigEntry<bool> CE_RD_Fiddlehead_VanillaSeedDropEnabled;

		public static ConfigEntry<float> CE_RD_Fiddlehead_RespawnTimeMinutes;

		[<ad47d627-eefa-4fa9-aeb5-d2cdcce92dc3>Nullable(2)]
		internal static ConfigSync ConfigSync;

		[<ad47d627-eefa-4fa9-aeb5-d2cdcce92dc3>Nullable(2)]
		private static ConfigEntry<bool> _serverConfigLocked;

		[<ad47d627-eefa-4fa9-aeb5-d2cdcce92dc3>Nullable(2)]
		private FileSystemWatcher _configWatcher;

		private long _lastCfgWrite;

		private bool _reloadScheduled;

		private bool _watcherReady;

		private void Awake()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Invalid comparison between Unknown and I4
			Instance = this;
			BuildPiece._plugin = (BaseUnityPlugin)(object)this;
			Localizer.Load();
			Log = ((BaseUnityPlugin)this).Logger;
			_harmony = new Harmony("radamanto.Plantations");
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			try
			{
				SetupServerSync();
				BindConfiguration();
				SeedRegistry.Register();
				PlantRegistry.Register();
				EffectRegistry.Register();
				PlantDefinitions.Init(((BaseUnityPlugin)this).Config);
				if ((int)SystemInfo.graphicsDeviceType != 4)
				{
					PlantationsGridContext.ModLogger = Log;
					PlantationsGridContext.config = new PlantationsGridConfig(((BaseUnityPlugin)this).Config);
				}
				Code_PlantationsAreaHarvest.Initialize(((BaseUnityPlugin)this).Config);
				PatchSafety.ApplyAll(_harmony);
				Code_PlantationsAudioMixer.Initialize();
			}
			finally
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			}
			((BaseUnityPlugin)this).Config.Save();
			SetupConfigWatcher();
		}

		internal void QueuePlantRefresh()
		{
			if (!_plantRefreshScheduled)
			{
				_plantRefreshScheduled = true;
				((MonoBehaviour)this).Invoke("RefreshPlants", 0f);
			}
		}

		private void RefreshPlants()
		{
			_plantRefreshScheduled = false;
			Code_PlantationsHarvest.RefreshLoadedPlants();
		}

		private void OnDestroy()
		{
			_watcherReady = false;
			if (_configWatcher != null)
			{
				_configWatcher.EnableRaisingEvents = false;
				_configWatcher.Dispose();
				_configWatcher = null;
			}
			_reloadScheduled = false;
			((MonoBehaviour)this).CancelInvoke();
			PlantDefinitions.Shutdown();
			Code_PlantationsHarvest.ClearLoadedPlants();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void BindConfiguration()
		{
			CE_VanillaPlantTimerUI_Enabled = Cfg("01 - General", "Vanilla Timer UI", defaultValue: true, "If true, all vanilla Plant-based crops also show the growth timer UI.");
			CE_RD_Dandelion_DropAmount = CfgRange("02 - Dandelion", "02.01 - Dandelion Drop Amount", 3, "Dandelion drop amount.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000));
			CE_RD_Dandelion_ExtraDropAmount = CfgRange("02 - Dandelion", "02.02 - Dandelion Seed Drop Amount", 1, "Dandelion seed drop amount.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000));
			CE_RD_Dandelion_ExtraDropChance = CfgRange("02 - Dandelion", "02.03 - Dandelion Seed Drop Chance", 0.05f, "Chance for Dandelion seed to drop. 0 = never, 1 = always.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f));
			CE_RD_Dandelion_VanillaSeedDropEnabled = Cfg("02 - Dandelion", "02.04 - Vanilla Seed Drop Enabled", defaultValue: true, "If true, vanilla Dandelion can also drop seed. If false, another external way to obtain seeds is required.");
			CE_RD_Dandelion_RespawnTimeMinutes = CfgRespawn("02 - Dandelion", "02.05 - Dandelion Respawn Time Minutes", "Dandelion respawn time in minutes.");
			CE_RD_Mushroom_DropAmount = CfgRange("03 - Mushroom", "03.01 - Mushroom Drop Amount", 3, "Mushroom drop amount.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000));
			CE_RD_Mushroom_ExtraDropAmount = CfgRange("03 - Mushroom", "03.02 - Red Mushroom Spore Drop Amount", 1, "Red Mushroom spore drop amount.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000));
			CE_RD_Mushroom_ExtraDropChance = CfgRange("03 - Mushroom", "03.03 - Red Mushroom Spore Drop Chance", 0.05f, "Chance for Red Mushroom spore to drop. 0 = never, 1 = always.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f));
			CE_RD_Mushroom_VanillaSeedDropEnabled = Cfg("03 - Mushroom", "03.04 - Vanilla Seed Drop Enabled", defaultValue: true, "If true, vanilla Mushroom can also drop red spore. If false, another external way to obtain seeds is required.");
			CE_RD_Mushroom_RespawnTimeMinutes = CfgRespawn("03 - Mushroom", "03.05 - Mushroom Respawn Time Minutes", "Mushroom respawn time in minutes.");
			CE_RD_Mushroom_yellow_DropAmount = CfgRange("04 - Yellow Mushroom", "04.01 - Yellow Mushroom Drop Amount", 3, "Yellow Mushroom drop amount.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000));
			CE_RD_Mushroom_yellow_ExtraDropAmount = CfgRange("04 - Yellow Mushroom", "04.02 - Yellow Mushroom Spore Drop Amount", 1, "Yellow Mushroom spore drop amount.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000));
			CE_RD_Mushroom_yellow_ExtraDropChance = CfgRange("04 - Yellow Mushroom", "04.03 - Yellow Mushroom Spore Drop Chance", 0.05f, "Chance for Yellow Mushroom spore to drop. 0 = never, 1 = always.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f));
			CE_RD_Mushroom_yellow_VanillaSeedDropEnabled = Cfg("04 - Yellow Mushroom", "04.04 - Vanilla Seed Drop Enabled", defaultValue: true, "If true, vanilla Yellow Mushroom can also drop spore. If false, another external way to obtain seeds is required.");
			CE_RD_Mushroom_yellow_RespawnTimeMinutes = CfgRespawn("04 - Yellow Mushroom", "04.05 - Yellow Mushroom Respawn Time Minutes", "Yellow Mushroom respawn time in minutes.");
			CE_RD_RaspberryBush_DropAmount = CfgRange("05 - Raspberry", "05.01 - Raspberry Drop Amount", 3, "Raspberry drop amount.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000));
			CE_RD_RaspberryBush_ExtraDropAmount = CfgRange("05 - Raspberry", "05.02 - Raspberry Seed Drop Amount", 1, "Raspberry seed drop amount.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000));
			CE_RD_RaspberryBush_ExtraDropChance = CfgRange("05 - Raspberry", "05.03 - Raspberry Seed Drop Chance", 0.05f, "Chance for Raspberry seed to drop. 0 = never, 1 = always.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f));
			CE_RD_RaspberryBush_VanillaSeedDropEnabled = Cfg("05 - Raspberry", "05.04 - Vanilla Seed Drop Enabled", defaultValue: true, "If true, vanilla RaspberryBush can also drop seed. If false, another external way to obtain seeds is required.");
			CE_RD_RaspberryBush_RespawnTimeMinutes = CfgRespawn("05 - Raspberry", "05.05 - Raspberry Respawn Time Minutes", "Raspberry respawn time in minutes.");
			CE_RD_BlueberryBush_DropAmount = CfgRange("06 - Blueberry", "06.01 - Blueberry Drop Amount", 3, "Blueberry drop amount.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000));
			CE_RD_BlueberryBush_ExtraDropAmount = CfgRange("06 - Blueberry", "06.02 - Blueberry Seed Drop Amount", 1, "Blueberry seed drop amount.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000));
			CE_RD_BlueberryBush_ExtraDropChance = CfgRange("06 - Blueberry", "06.03 - Blueberry Seed Drop Chance", 0.05f, "Chance for Blueberry seed to drop. 0 = never, 1 = always.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f));
			CE_RD_BlueberryBush_VanillaSeedDropEnabled = Cfg("06 - Blueberry", "06.04 - Vanilla Seed Drop Enabled", defaultValue: true, "If true, vanilla BlueberryBush can also drop seed. If false, another external way to obtain seeds is required.");
			CE_RD_BlueberryBush_RespawnTimeMinutes = CfgRespawn("06 - Blueberry", "06.05 - Blueberry Respawn Time Minutes", "Blueberry respawn time in minutes.");
			CE_RD_thistle_DropAmount = CfgRange("07 - Thistle", "07.01 - Thistle Drop Amount", 3, "Thistle drop amount.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000));
			CE_RD_thistle_ExtraDropAmount = CfgRange("07 - Thistle", "07.02 - Thistle Seed Drop Amount", 1, "Thistle seed drop amount.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000));
			CE_RD_thistle_ExtraDropChance = CfgRange("07 - Thistle", "07.03 - Thistle Seed Drop Chance", 0.05f, "Chance for Thistle seed to drop. 0 = never, 1 = always.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f));
			CE_RD_thistle_VanillaSeedDropEnabled = Cfg("07 - Thistle", "07.04 - Vanilla Seed Drop Enabled", defaultValue: true, "If true, vanilla Thistle can also drop seed. If false, another external way to obtain seeds is required.");
			CE_RD_thistle_RespawnTimeMinutes = CfgRespawn("07 - Thistle", "07.05 - Thistle Respawn Time Minutes", "Thistle respawn time in minutes.");
			CE_RD_CloudberryBush_DropAmount = CfgRange("08 - Cloudberry", "08.01 - Cloudberry Drop Amount", 3, "Cloudberry drop amount.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000));
			CE_RD_CloudberryBush_ExtraDropAmount = CfgRange("08 - Cloudberry", "08.02 - Cloudberry Seed Drop Amount", 1, "Cloudberry seed drop amount.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000));
			CE_RD_CloudberryBush_ExtraDropChance = CfgRange("08 - Cloudberry", "08.03 - Cloudberry Seed Drop Chance", 0.05f, "Chance for Cloudberry seed to drop. 0 = never, 1 = always.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f));
			CE_RD_CloudberryBush_VanillaSeedDropEnabled = Cfg("08 - Cloudberry", "08.04 - Vanilla Seed Drop Enabled", defaultValue: true, "If true, vanilla CloudberryBush can also drop seed. If false, another external way to obtain seeds is required.");
			CE_RD_CloudberryBush_RespawnTimeMinutes = CfgRespawn("08 - Cloudberry", "08.05 - Cloudberry Respawn Time Minutes", "Cloudberry respawn time in minutes.");
			CE_RD_MushroomSmokePuff_DropAmount = CfgRange("09 - Smoke Puff", "09.01 - Smoke Puff Drop Amount", 3, "Smoke Puff drop amount.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000));
			CE_RD_MushroomSmokePuff_ExtraDropAmount = CfgRange("09 - Smoke Puff", "09.02 - Smoke Puff Seed Drop Amount", 1, "Smoke Puff seed drop amount.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000));
			CE_RD_MushroomSmokePuff_ExtraDropChance = CfgRange("09 - Smoke Puff", "09.03 - Smoke Puff Seed Drop Chance", 0.05f, "Chance for Smoke Puff seed to drop. 0 = never, 1 = always.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f));
			CE_RD_MushroomSmokePuff_VanillaSeedDropEnabled = Cfg("09 - Smoke Puff", "09.04 - Vanilla Seed Drop Enabled", defaultValue: true, "If true, vanilla Smoke Puff can also drop seed. If false, another external way to obtain seeds is required.");
			CE_RD_MushroomSmokePuff_RespawnTimeMinutes = CfgRespawn("09 - Smoke Puff", "09.05 - Smoke Puff Respawn Time Minutes", "Smoke Puff respawn time in minutes.");
			CE_RD_Fiddlehead_DropAmount = CfgRange("10 - Fiddlehead", "10.01 - Fiddlehead Drop Amount", 3, "Fiddlehead drop amount.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000));
			CE_RD_Fiddlehead_ExtraDropAmount = CfgRange("10 - Fiddlehead", "10.02 - Fiddlehead Seed Drop Amount", 1, "Fiddlehead seed drop amount.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000));
			CE_RD_Fiddlehead_ExtraDropChance = CfgRange("10 - Fiddlehead", "10.03 - Fiddlehead Seed Drop Chance", 0.05f, "Chance for Fiddlehead seed to drop. 0 = never, 1 = always.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f));
			CE_RD_Fiddlehead_VanillaSeedDropEnabled = Cfg("10 - Fiddlehead", "10.04 - Vanilla Seed Drop Enabled", defaultValue: true, "If true, vanilla Fiddlehead can also drop seed. If false, another external way to obtain seeds is required.");
			CE_RD_Fiddlehead_RespawnTimeMinutes = CfgRespawn("10 - Fiddlehead", "10.05 - Fiddlehead Respawn Time Minutes", "Fiddlehead respawn time in minutes.");
		}

		private ConfigEntry<T> CfgRange<[<ad47d627-eefa-4fa9-aeb5-d2cdcce92dc3>Nullable(2)] T>(string section, string key, T defaultValue, string description, AcceptableValueBase range)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			return BindConfig(section, key, defaultValue, new ConfigDescription(description, range, Array.Empty<object>()));
		}

		private ConfigEntry<float> CfgRespawn(string section, string key, string description)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			return BindConfig(section, key, 60f, new ConfigDescription($"{description} Also controls initial seedling growth time. Minimum {1f} (0 would destroy the plant when harvested).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100000f), Array.Empty<object>()));
		}

		private void SetupServerSync()
		{
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Expected O, but got Unknown
			ConfigSync = new ConfigSync(((BaseUnityPlugin)this).Info.Metadata.GUID)
			{
				DisplayName = ((BaseUnityPlugin)this).Info.Metadata.Name,
				CurrentVersion = ((BaseUnityPlugin)this).Info.Metadata.Version.ToString(),
				MinimumRequiredVersion = ((BaseUnityPlugin)this).Info.Metadata.Version.ToString()
			};
			_serverConfigLocked = ((BaseUnityPlugin)this).Config.Bind<bool>("01 - General", "LockConfiguration", true, new ConfigDescription("If true, the server locks synchronized configs.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Browsable = false
				}
			}));
			ConfigSync.AddLockingConfigEntry<bool>(_serverConfigLocked);
		}

		private ConfigEntry<T> BindConfig<[<ad47d627-eefa-4fa9-aeb5-d2cdcce92dc3>Nullable(2)] T>(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			ConfigDescription val = new ConfigDescription(description.Description + (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]"), description.AcceptableValues, description.Tags);
			ConfigEntry<T> val2 = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, val);
			if (ConfigSync == null)
			{
				throw new InvalidOperationException("ServerSync has not been initialized.");
			}
			ConfigSync.AddConfigEntry<T>(val2).SynchronizedConfig = synchronizedSetting;
			return val2;
		}

		private ConfigEntry<T> BindConfig<[<ad47d627-eefa-4fa9-aeb5-d2cdcce92dc3>Nullable(2)] T>(string group, string name, T value, string description, bool synchronizedSetting = true)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			return BindConfig(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting);
		}

		protected ConfigEntry<T> Cfg<[<ad47d627-eefa-4fa9-aeb5-d2cdcce92dc3>Nullable(2)] T>(string section, string key, T defaultValue, string description, bool synced = true)
		{
			return BindConfig(section, key, defaultValue, description, synced);
		}

		private void SetupConfigWatcher()
		{
			try
			{
				_watcherReady = false;
				_configWatcher?.Dispose();
				_configWatcher = new FileSystemWatcher(Path.GetDirectoryName(((BaseUnityPlugin)this).Config.ConfigFilePath), Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath))
				{
					IncludeSubdirectories = false,
					NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite),
					SynchronizingObject = ThreadingHelper.SynchronizingObject
				};
				_configWatcher.Changed += OnConfigFileChanged;
				_configWatcher.Created += OnConfigFileChanged;
				_configWatcher.Renamed += [<751c224d-bfbf-4350-9e31-32d445024a3b>NullableContext(0)] (object _, RenamedEventArgs args) =>
				{
					OnConfigFileChanged(_, args);
				};
				_configWatcher.Error += [<751c224d-bfbf-4350-9e31-32d445024a3b>NullableContext(0)] (object _, ErrorEventArgs args) =>
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)$"Configuration watcher failed: {args.GetException()}");
				};
				_watcherReady = true;
				_configWatcher.EnableRaisingEvents = true;
			}
			catch (Exception arg)
			{
				_watcherReady = false;
				_configWatcher?.Dispose();
				_configWatcher = null;
				((BaseUnityPlugin)this).Logger.LogWarning((object)$"Automatic configuration reload is unavailable: {arg}");
			}
		}

		private void OnConfigFileChanged(object sender, FileSystemEventArgs args)
		{
			if (_watcherReady)
			{
				_lastCfgWrite = Stopwatch.GetTimestamp();
				if (!_reloadScheduled)
				{
					_reloadScheduled = true;
					((MonoBehaviour)this).StartCoroutine(ReloadConfigAfterQuietPeriod());
				}
			}
		}

		private IEnumerator ReloadConfigAfterQuietPeriod()
		{
			try
			{
				for (int attempt = 0; attempt < 3; attempt++)
				{
					if (!_watcherReady)
					{
						break;
					}
					while (_watcherReady && (double)(Stopwatch.GetTimestamp() - _lastCfgWrite) / (double)Stopwatch.Frequency < 0.75)
					{
						yield return (object)new WaitForSecondsRealtime(0.75f);
					}
					if (!_watcherReady || ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer()))
					{
						break;
					}
					bool saveOnSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
					bool retry = false;
					try
					{
						((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
						((BaseUnityPlugin)this).Config.Reload();
						((BaseUnityPlugin)this).Logger.LogInfo((object)"Configuration reloaded.");
					}
					catch (IOException ex)
					{
						IOException error = ex;
						retry = true;
						if (attempt == 2)
						{
							((BaseUnityPlugin)this).Logger.LogWarning((object)$"Configuration could not be read after 3 attempts: {error}");
						}
					}
					catch (Exception ex2)
					{
						Exception error2 = ex2;
						((BaseUnityPlugin)this).Logger.LogError((object)$"Configuration reload failed: {error2}");
					}
					finally
					{
						((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnSet;
					}
					if (!retry)
					{
						break;
					}
					_lastCfgWrite = Stopwatch.GetTimestamp();
				}
			}
			finally
			{
				_reloadScheduled = false;
			}
		}
	}
	internal static class PlantationsGridContext
	{
		internal static readonly Dictionary<string, GameObject> PrefabRefs = new Dictionary<string, GameObject>();

		internal static ManualLogSource ModLogger = new ManualLogSource("Plantations Grid");

		internal static PlantationsGridConfig config;

		internal static List<GridPickableDefinition> PickableRefs = new List<GridPickableDefinition>();

		internal static void Dbgl(string message, bool forceLog = false, LogLevel level = (LogLevel)16)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			if (forceLog || config.EnableDebugMessages)
			{
				ModLogger.Log(level, (object)message);
			}
		}
	}
	internal static class PlantationsGridRules
	{
		private static readonly Dictionary<int, float> AdultColliderRadii = new Dictionary<int, float>();

		internal static bool HoldingCultivator
		{
			get
			{
				Player localPlayer = Player.m_localPlayer;
				return ((localPlayer == null) ? null : ((Humanoid)localPlayer).GetRightItem()?.m_shared.m_name) == "$item_cultivator";
			}
		}

		internal static bool IsPlantOrPickable(GameObject go)
		{
			if (!Object.op_Implicit((Object)(object)go.GetComponent<Plant>()))
			{
				return Object.op_Implicit((Object)(object)go.GetComponent<Pickable>());
			}
			return true;
		}

		internal static bool HasGrowSpace(Plant plant, Vector3 position)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return Physics.OverlapSphere(position, plant.m_growRadius, Plant.m_spaceMask).Length == 0;
		}

		internal static bool PositionHasCollisions(Vector3 position)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			return Physics.CheckCapsule(position, position + Vector3.up * 0.1f, Mathf.Epsilon, PlantationsGridCollisions.CollisionMask);
		}

		internal static float GetPieceSpacing(GameObject go)
		{
			float num = 0f;
			bool flag = false;
			Plant component = go.GetComponent<Plant>();
			if (Object.op_Implicit((Object)(object)component))
			{
				GameObject[] grownPrefabs = component.m_grownPrefabs;
				List<GameObject> list = new List<GameObject>(1 + grownPrefabs.Length);
				list.Add(go);
				list.AddRange(grownPrefabs);
				List<GameObject> list2 = list;
				flag = list2.Any((GameObject x) => Object.op_Implicit((Object)(object)x.GetComponent<TreeBase>()));
				if (PlantationsGridContext.config.MinimizeGridSpacing && !flag)
				{
					for (int num2 = 0; num2 < list2.Count; num2++)
					{
						CapsuleCollider[] componentsInChildren = list2[num2].GetComponentsInChildren<CapsuleCollider>();
						CapsuleCollider[] array = componentsInChildren;
						foreach (CapsuleCollider val in array)
						{
							num = Mathf.Max(num, val.radius);
						}
					}
				}
				num += (flag ? PlantationsGridContext.config.ExtraSaplingSpacing : PlantationsGridContext.config.ExtraCropSpacing);
			}
			float num4 = (flag ? (component.m_growRadius * 2.2f) : ((component?.m_growRadius * (PlantationsGridContext.config.MinimizeGridSpacing ? 1.1f : 2f)) ?? PickableSnapRadius(go.GetComponent<Piece>()))) + num;
			if (Object.op_Implicit((Object)(object)component) && PlantDefinitions.TryGetPiece(go, out var def))
			{
				float adultColliderRadius = GetAdultColliderRadius(component, def);
				float radius;
				float num5 = (PlantationsGridContext.config.TryGetScatterRadius(out radius) ? Mathf.Abs(radius) : 0f);
				float num6 = component.m_growRadius + Mathf.Max(component.m_growRadius, adultColliderRadius) + 0.1f + num5 * 2f;
				num4 = Mathf.Max(num4, num6 + PlantationsGridContext.config.ExtraCropSpacing);
			}
			return num4;
		}

		private static float GetAdultColliderRadius(Plant plant, PlantDefinition definition)
		{
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			if (AdultColliderRadii.TryGetValue(definition.PieceHash, out var value))
			{
				return value * Mathf.Max(plant.m_minScale, plant.m_maxScale);
			}
			value = 0f;
			GameObject[] grownPrefabs = plant.m_grownPrefabs;
			foreach (GameObject val in grownPrefabs)
			{
				if (!Object.op_Implicit((Object)(object)val))
				{
					continue;
				}
				Collider[] componentsInChildren = val.GetComponentsInChildren<Collider>(true);
				foreach (Collider val2 in componentsInChildren)
				{
					if (!val2.enabled || val2.isTrigger || ((Component)val2).gameObject.layer == LayerMask.NameToLayer("viewblock"))
					{
						continue;
					}
					Collider val3 = val2;
					Collider val4 = val3;
					SphereCollider val5 = (SphereCollider)(object)((val4 is SphereCollider) ? val4 : null);
					Vector3 center;
					Vector3 val8;
					if (val5 == null)
					{
						CapsuleCollider val6 = (CapsuleCollider)(object)((val4 is CapsuleCollider) ? val4 : null);
						if (val6 == null)
						{
							BoxCollider val7 = (BoxCollider)(object)((val4 is BoxCollider) ? val4 : null);
							if (val7 == null)
							{
								continue;
							}
							center = val7.center;
							val8 = val7.size * 0.5f;
						}
						else
						{
							center = val6.center;
							val8 = Vector3.one * val6.radius;
							((Vector3)(ref val8))[val6.direction] = Mathf.Max(val6.height * 0.5f, val6.radius);
						}
					}
					else
					{
						center = val5.center;
						val8 = Vector3.one * val5.radius;
					}
					for (int k = 0; k < 8; k++)
					{
						Vector3 val9 = center + Vector3.Scale(val8, new Vector3(((k & 1) == 0) ? (-1f) : 1f, ((k & 2) == 0) ? (-1f) : 1f, ((k & 4) == 0) ? (-1f) : 1f));
						val9 = val.transform.InverseTransformPoint(((Component)val2).transform.TransformPoint(val9));
						float num = value;
						Vector2 val10 = new Vector2(val9.x, val9.z);
						value = Mathf.Max(num, ((Vector2)(ref val10)).magnitude);
					}
				}
			}
			AdultColliderRadii[definition.PieceHash] = value;
			return value * Mathf.Max(plant.m_minScale, plant.m_maxScale);
		}

		private static float PickableSnapRadius(Piece p)
		{
			if (p == null || !(p.m_harvestRadius > 0f))
			{
				return PlantationsGridContext.config.DefaultGridSpacing + PlantationsGridContext.config.ExtraCropSpacing;
			}
			return p.m_harvestRadius + PlantationsGridContext.config.ExtraCropSpacing;
		}
	}
	internal enum GridAlignmentMode
	{
		Intelligent,
		Legacy
	}
	internal enum GridPlacementStatus
	{
		Healthy,
		LackResources,
		NotCultivated,
		WrongBiome,
		NoSpace,
		NoSun,
		Invalid,
		NoAttachPiece,
		TooHot,
		TooCold
	}
	internal sealed class PlantationsGridConfig
	{
		private readonly ConfigFile Config;

		private readonly ConfigEntry<KeyboardShortcut> enableModKey;

		private readonly ConfigEntry<KeyboardShortcut> enableSnappingKey;

		private readonly ConfigEntry<KeyboardShortcut> cycleSizeKey;

		private readonly ConfigEntry<bool> modActive;

		private readonly ConfigEntry<bool> snapActive;

		private readonly ConfigEntry<bool> useStamina;

		private readonly ConfigEntry<bool> useDurability;

		private readonly ConfigEntry<bool> showCost;

		private readonly ConfigEntry<bool> showHUDKeyHints;

		private readonly ConfigEntry<bool> showGridDirections;

		private readonly ConfigEntry<float> extraSpacing;

		private int size = 3;

		internal KeyboardShortcut EnableModKey => enableModKey.Value;

		internal KeyboardShortcut EnableSnappingKey => enableSnappingKey.Value;

		internal KeyboardShortcut CycleSizeKey => cycleSizeKey.Value;

		internal KeyCode GamepadModifierKey => (KeyCode)334;

		internal bool ModActive
		{
			get
			{
				return modActive.Value;
			}
			set
			{
				modActive.Value = value;
			}
		}

		internal bool SnapActive
		{
			get
			{
				return snapActive.Value;
			}
			set
			{
				snapActive.Value = value;
			}
		}

		internal int Rows => size;

		internal int Columns => size;

		internal bool UseStamina => useStamina.Value;

		internal bool UseDurability => useDurability.Value;

		internal bool ShowCost => showCost.Value;

		internal bool ShowHUDKeyHints => showHUDKeyHints.Value;

		internal bool ShowGridDirections => showGridDirections.Value;

		internal float ExtraCropSpacing => extraSpacing.Value;

		internal float ExtraSaplingSpacing => extraSpacing.Value;

		internal bool PreventPartialPlanting => false;

		internal bool PreventInvalidPlanting => true;

		internal bool RandomizeRotation => true;

		internal bool EnableDebugMessages => false;

		internal bool GloballyAlignGridDirections => true;

		internal bool MinimizeGridSpacing => false;

		internal GridAlignmentMode GridAlignmentMode => GridAlignmentMode.Intelligent;

		internal bool ForceAltPlacement => false;

		internal bool PreferCardinalSnapping => false;

		internal int MaxConcurrentPlacements => 25;

		internal int GhostUpdateBatchSize => 20;

		internal int BulkPlantingBatchSize => 2;

		internal bool PreventOverlappingPlacements => true;

		internal float DefaultGridSpacing => 1f;

		internal bool ShowGhostsDuringPlacement => true;

		internal bool ShowSnapDirection => ShowGridDirections;

		internal bool HighlightRootPlacementGhost => true;

		internal Color RootGhostHighlightColor => Color32.op_Implicit(new Color32((byte)166, (byte)230, (byte)122, byte.MaxValue));

		internal Color RowStartColor => Color32.op_Implicit(new Color32((byte)230, (byte)166, (byte)47, byte.MaxValue));

		internal Color RowEndColor => Color32.op_Implicit(new Color32(byte.MaxValue, (byte)225, (byte)145, byte.MaxValue));

		internal Color ColumnStartColor => Color32.op_Implicit(new Color32((byte)30, (byte)179, (byte)159, byte.MaxValue));

		internal Color ColumnEndColor => Color32.op_Implicit(new Color32((byte)146, (byte)240, (byte)219, byte.MaxValue));

		internal Color SnapStartColor => Color32.op_Implicit(new Color32((byte)142, (byte)97, (byte)212, byte.MaxValue));

		internal Color SnapEndColor => Color32.op_Implicit(new Color32((byte)213, (byte)180, (byte)250, byte.MaxValue));

		internal PlantationsGridConfig(ConfigFile configFile)
		{
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Expected O, but got Unknown
			Config = configFile;
			bool saveOnConfigSet = Config.SaveOnConfigSet;
			Config.SaveOnConfigSet = false;
			try
			{
				modActive = Config.Bind<bool>("11 - Planting Grid", "11.01 - Enabled", true, "Enable the planting grid. F8 toggles it while using the cultivator.");
				enableModKey = Config.Bind<KeyboardShortcut>("12 - Grid Controls", "12.01 - Toggle Key", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Toggle the planting grid while using the cultivator. Set to None to disable this shortcut.");
				cycleSizeKey = Config.Bind<KeyboardShortcut>("12 - Grid Controls", "12.02 - Cycle Size Key", new KeyboardShortcut((KeyCode)290, Array.Empty<KeyCode>()), "Cycle square grid sizes: 2x2, 3x3, 4x4, 5x5. Initial size: 3x3 each session. Gamepad: LB + D-pad Up. Set to None to disable this keyboard shortcut.");
				snapActive = Config.Bind<bool>("11 - Planting Grid", "11.05 - Snapping", true, "Snap the grid to existing plants.");
				enableSnappingKey = Config.Bind<KeyboardShortcut>("12 - Grid Controls", "EnableSnappingKey", new KeyboardShortcut((KeyCode)291, Array.Empty<KeyCode>()), "Toggle grid snapping while using the cultivator. Set to None to disable this shortcut.");
				useStamina = Config.Bind<bool>("14 - Grid Placement", "UseStamina", true, "Consume the vanilla build stamina cost for each additional plant. The first plant keeps its vanilla cost.");
				useDurability = Config.Bind<bool>("14 - Grid Placement", "UseDurability", true, "Consume cultivator durability for each additional plant. The first plant keeps its vanilla cost.");
				extraSpacing = Config.Bind<float>("13 - Grid Alignment", "ExtraSpacing", 0f, new ConfigDescription("Extra distance in metres between plants, in addition to automatic spacing. Useful for larger plants from other mods.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
				showCost = Config.Bind<bool>("17 - Grid Appearance", "ShowCost", true, "Show total resource costs to the right of each vanilla cost, for example 2 (18) for nine plants costing two seeds each.");
				showHUDKeyHints = Config.Bind<bool>("17 - Grid Appearance", "ShowHUDKeyHints", true, "Show the grid size shortcut in the build HUD.");
				showGridDirections = Config.Bind<bool>("17 - Grid Appearance", "ShowGridDirections", true, "Show row, column and snapping direction lines.");
				cycleSizeKey.SettingChanged += GridConfigEvents.KeybindsChanged;
				showGridDirections.SettingChanged += delegate
				{
					GameObject directionRenderer = PlantationsGridPreview.DirectionRenderer;
					if (directionRenderer != null)
					{
						directionRenderer.SetActive(false);
					}
				};
				extraSpacing.SettingChanged += delegate
				{
					Player localPlayer = Player.m_localPlayer;
					if (localPlayer != null)
					{
						localPlayer.SetupPlacementGhost();
					}
				};
				RemoveLegacySettings();
				Config.Save();
			}
			finally
			{
				Config.SaveOnConfigSet = saveOnConfigSet;
			}
		}

		internal void CycleSize()
		{
			size = ((size == 5) ? 2 : (size + 1));
			PlantationsGridPreview.ResizeGrid();
		}

		private void RemoveLegacySettings()
		{
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Expected O, but got Unknown
			if (!File.Exists(Config.ConfigFilePath))
			{
				return;
			}
			HashSet<string> hashSet = new HashSet<string> { "11 - Planting Grid", "12 - Grid Controls", "13 - Grid Alignment", "14 - Grid Placement", "15 - Grid Performance", "16 - Grid Pickables", "17 - Grid Appearance" };
			string text = string.Empty;
			string[] array = File.ReadAllLines(Config.ConfigFilePath);
			foreach (string text2 in array)
			{
				string text3 = text2.Trim();
				if (text3.StartsWith("[") && text3.EndsWith("]"))
				{
					text = text3.Substring(1, text3.Length - 2);
				}
				else
				{
					if (!hashSet.Contains(text) || text3.StartsWith("#"))
					{
						continue;
					}
					int num = text3.IndexOf('=');
					if (num >= 1)
					{
						string text4 = text3.Substring(0, num).Trim();
						ConfigDefinition val = new ConfigDefinition(text, text4);
						if (!Config.ContainsKey(val))
						{
							Config.Bind<string>(text, text4, string.Empty, (ConfigDescription)null);
							Config.Remove(val);
						}
					}
				}
			}
		}

		internal float GetPickableSpacing(GridPickableDefinition pickable)
		{
			string key = pickable.key;
			if (1 == 0)
			{
			}
			float result;
			switch (key)
			{
			case "Pickable_Dandelion":
			case "Pickable_Thistle":
				result = 0.75f;
				break;
			case "Pickable_Mushroom":
			case "Pickable_Mushroom_blue":
			case "Pickable_Mushroom_yellow":
				result = 0.5f;
				break;
			case "BlueberryBush":
			case "RaspberryBush":
			case "LingonberryBush":
				result = 1.5f;
				break;
			case "Pickable_Fiddlehead":
			case "Pickable_SmokePuff":
			case "CloudberryBush":
				result = 1f;
				break;
			default:
				result = ((pickable.Piece.m_harvestRadius > 0f) ? pickable.Piece.m_harvestRadius : DefaultGridSpacing);
				break;
			}
			if (1 == 0)
			{
			}
			return result;
		}

		internal bool TryGetScatterRadius(out float radius)
		{
			radius = 0f;
			return false;
		}

		internal bool TryGetScatterAngle(out float angle)
		{
			angle = 0f;
			return false;
		}
	}
	internal static class GridConfigEvents
	{
		internal static void KeybindsChanged(object sender, EventArgs e)
		{
			Code_PlantationsGridHints.UpdateKeyHintText();
		}

		internal static void ApplyColors()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)PlantationsGridPreview.DirectionRenderer))
			{
				PlantationsGridPreview.LineRenderers[0].startColor = PlantationsGridContext.config.RowStartColor;
				PlantationsGridPreview.LineRenderers[0].endColor = PlantationsGridContext.config.RowEndColor;
				PlantationsGridPreview.LineRenderers[1].startColor = PlantationsGridContext.config.ColumnStartColor;
				PlantationsGridPreview.LineRenderers[1].endColor = PlantationsGridContext.config.ColumnEndColor;
				PlantationsGridPreview.LineRenderers[2].startColor = PlantationsGridContext.config.SnapStartColor;
				PlantationsGridPreview.LineRenderers[2].endColor = PlantationsGridContext.config.SnapEndColor;
			}
		}
	}
	internal static class PlantationsGridState
	{
		internal static Vector3 BasePosition;

		internal static Vector3 RowDirection;

		internal static Vector3 ColumnDirection;

		internal static Quaternion BaseRotation;

		internal static Quaternion FixedRotation;

		internal static bool AltPlacement;

		internal static GameObject PlacementGhost;

		internal static Piece Piece;

		internal static Plant Plant;

		internal static string LastSelectedPlantName = "";

		internal static Vector3 SnapDirection;

		internal static Vector3 SavedRowDirection;

		internal static Vector3 SavedColumnDirection;

		internal static Quaternion? SavedBaseRotation;

		internal static void SetReferences(GameObject rootGhost)
		{
			PlacementGhost = rootGhost;
			Plant = rootGhost.GetComponent<Plant>();
			Piece = rootGhost.GetComponent<Piece>();
			LastSelectedPlantName = null;
			if (!Object.op_Implicit((Object)(object)Plant))
			{
				return;
			}
			foreach (KeyValuePair<string, GridCropCatalog> item in GridCropCatalog.Registry)
			{
				if (item.Value.PlantName == ((Object)rootGhost).name)
				{
					LastSelectedPlantName = item.Key;
					break;
				}
			}
		}

		internal static void Update(Vector3 playerPosition)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			UpdateAltPlacementMode();
			UpdateBaseTransform();
			UpdateFixedRotation();
			UpdateGridDirectionsAndSnapping(playerPosition);
		}

		private static void UpdateAltPlacementMode()
		{
			if (PlantationsGridContext.config.ForceAltPlacement)
			{
				AltPlacement = true;
				return;
			}
			if (ZInput.IsNonClassicFunctionality() && ZInput.IsGamepadActive())
			{
				AltPlacement = Player.m_localPlayer.m_altPlace;
				return;
			}
			bool button = ZInput.GetButton("AltPlace");
			bool flag = ZInput.GetButton("JoyAltPlace") && !ZInput.GetButton("JoyRotate");
			AltPlacement = button || flag;
		}

		private static void UpdateBaseTransform()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			BasePosition = PlacementGhost.transform.position;
			BaseRotation = PlacementGhost.transform.rotation;
		}

		private static void UpdateFixedRotation()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: 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_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			FixedRotation = BaseRotation;
			Vector3 eulerAngles = ((Quaternion)(ref FixedRotation)).eulerAngles;
			eulerAngles.y = Mathf.Round(eulerAngles.y / 90f) * 90f;
			((Quaternion)(ref FixedRotation)).eulerAngles = eulerAngles;
		}

		private static void UpdateGridDirectionsAndSnapping(Vector3 playerPosition)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: 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_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			float pieceSpacing = PlantationsGridRules.GetPieceSpacing(PlacementGhost);
			ResetGridDirections();
			if (PlantationsGridContext.config.SnapActive)
			{
				if (PlantationsGridSnapping.FindSnapPoints(pieceSpacing))
				{
					return;
				}
				ResetGridDirections();
			}
			RowDirection = BaseRotation * RowDirection * pieceSpacing;
			ColumnDirection = BaseRotation * ColumnDirection * pieceSpacing;
			void ResetGridDirections()
			{
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				//IL_000d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				//IL_001d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0029: Unknown result type (might be due to invalid IL or missing references)
				//IL_002e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0033: Unknown result type (might be due to invalid IL or missing references)
				//IL_0038: Unknown result type (might be due to invalid IL or missing references)
				//IL_003d: Unknown result type (might be due to invalid IL or missing references)
				RowDirection = (PlantationsGridContext.config.GloballyAlignGridDirections ? Vector3.forward : Utils.DirectionXZ(BasePosition - playerPosition));
				ColumnDirection = Vector3.Cross(Vector3.up, RowDirection);
			}
		}
	}
	internal sealed class PlantationsGridGhost : MonoBehaviour
	{
		internal Piece piece;

		internal Plant plant;

		internal int lastUpdatedVersion = -1;

		public void Init()
		{
			piece = ((Component)this).GetComponent<Piece>();
			plant = ((Component)this).GetComponent<Plant>();
		}
	}
	internal static class PlantationsGridValidation
	{
		private static readonly Dictionary<Biome, GridPlacementStatus> TemperatureBiomeStatus = new Dictionary<Biome, GridPlacementStatus>
		{
			{
				(Biome)32,
				GridPlacementStatus.TooHot
			},
			{
				(Biome)64,
				GridPlacementStatus.TooCold
			},
			{
				(Biome)4,
				GridPlacementStatus.TooCold
			}
		};

		internal static GridPlacementStatus EvaluateStatus(GameObject ghost, GridPlacementStatus baseStatus = GridPlacementStatus.Healthy)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: 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_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Invalid comparison between Unknown and I4
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			Piece piece;
			Plant plant;
			if ((Object)(object)ghost == (Object)(object)PlantationsGridState.PlacementGhost)
			{
				piece = PlantationsGridState.Piece;
				plant = PlantationsGridState.Plant;
			}
			else
			{
				PlantationsGridGhost component = ghost.GetComponent<Planta