Decompiled source of FarmGridRemake v1.1.3

FarmGridRemake.dll

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

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("Sarcenzzz (original FarmGrid); FarmGridRemake compatibility update")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Client-side compatibility remake of the original FarmGrid mod by Sarcenzzz for current Valheim versions.")]
[assembly: AssemblyFileVersion("1.1.3.0")]
[assembly: AssemblyInformationalVersion("1.1.3")]
[assembly: AssemblyProduct("FarmGridRemake")]
[assembly: AssemblyTitle("FarmGridRemake")]
[assembly: AssemblyVersion("1.1.3.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace FarmGridRemake
{
	[BepInPlugin("Moopfam.FarmGridRemake", "FarmGridRemake", "1.1.3")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class FarmGridRemakePlugin : BaseUnityPlugin
	{
		internal const string Author = "Sarcenzzz";

		internal const string ModName = "FarmGridRemake";

		internal const string ModVersion = "1.1.3";

		internal const string BepInGuid = "Moopfam.FarmGridRemake";

		internal const string HarmonyGuid = "Moopfam.FarmGridRemake.Harmony";

		internal static ManualLogSource Log;

		private Harmony _harmony;

		private ConfigEntry<float> _plantSpacing;

		private ConfigEntry<int> _farmGridSections;

		private ConfigEntry<float> _farmGridYOffset;

		private ConfigEntry<Color> _farmGridColor;

		private ConfigEntry<Color> _secondaryGridColor;

		private ConfigEntry<int> _farmGridFixedRotation;

		private ConfigEntry<string> _plantObjectMasks;

		private ConfigEntry<string> _customPlants;

		private ConfigEntry<bool> _useCustomGridSize;

		private ConfigEntry<float> _customGridSize;

		private ConfigEntry<bool> _enableGrid;

		private ConfigEntry<bool> _holdToDisableGrid;

		private ConfigEntry<KeyCode> _disableHoldGridKey;

		private ConfigEntry<KeyCode> _secondaryGridKey;

		private ConfigEntry<bool> _connectToOtherPlants;

		private ConfigEntry<bool> _removeGrowthRestrictions;

		private ConfigEntry<bool> _allowPlantOverlap;

		private void Awake()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Expected O, but got Unknown
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Expected O, but got Unknown
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Expected O, but got Unknown
			//IL_04cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d6: Expected O, but got Unknown
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			_plantSpacing = ((BaseUnityPlugin)this).Config.Bind<float>("FarmGrid", "plantSpacing", 0.01f, "Amount of extra space between plants on top of the growth size needed to grow.");
			_farmGridSections = ((BaseUnityPlugin)this).Config.Bind<int>("FarmGrid", "farmGridSections", 2, new ConfigDescription("Amount of grid sections on either side of the main axis.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 12), Array.Empty<object>()));
			_farmGridYOffset = ((BaseUnityPlugin)this).Config.Bind<float>("FarmGrid", "farmGridYOffset", 0.2f, new ConfigDescription("Grid offset from the ground.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
			_farmGridColor = ((BaseUnityPlugin)this).Config.Bind<Color>("FarmGrid", "farmGridColor", new Color(0f, 1f, 0f, 0.35f), "Color of the normal grid. Default is green.");
			_secondaryGridColor = ((BaseUnityPlugin)this).Config.Bind<Color>("FarmGrid", "secondaryGridColor", new Color(1f, 0f, 0f, 0.35f), "Color of the secondary interleaved grid. Default is red.");
			Color b = default(Color);
			((Color)(ref b))..ctor(0.8f, 0.8f, 0.8f, 0.2f);
			if (ColorsApproximatelyEqual(_farmGridColor.Value, b))
			{
				_farmGridColor.Value = new Color(0f, 1f, 0f, 0.35f);
			}
			_farmGridFixedRotation = ((BaseUnityPlugin)this).Config.Bind<int>("FarmGrid", "farmGridFixedRotation", 0, new ConfigDescription("Fixed grid rotation in degrees. 0 = follow the plant placement rotation. Use 360 for a fixed 0 degree world axis.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 360), Array.Empty<object>()));
			_plantObjectMasks = ((BaseUnityPlugin)this).Config.Bind<string>("FarmGrid", "plantObjectMasks", "piece,piece_nonsolid,item", "Layer masks used by overlap detection for cultivator plants and pickable plant objects.");
			_customPlants = ((BaseUnityPlugin)this).Config.Bind<string>("FarmGrid", "customPlants", "", "Additional non-Plant objects treated as plants. Format: PrefabName:size, PrefabName:size.");
			_useCustomGridSize = ((BaseUnityPlugin)this).Config.Bind<bool>("Grid Size", "useCustomGridSize", false, "Use the custom grid size instead of automatic plant spacing. Disabled by default.");
			_customGridSize = ((BaseUnityPlugin)this).Config.Bind<float>("Grid Size", "customGridSize", 1f, new ConfigDescription("Grid cell size in metres when useCustomGridSize is enabled.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 10f), Array.Empty<object>()));
			_enableGrid = ((BaseUnityPlugin)this).Config.Bind<bool>("Grid Toggle", "EnableGrid", true, "Master switch for FarmGrid snapping and grid lines. When false, normal Valheim cultivator placement is used.");
			_disableHoldGridKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Grid Toggle", "DisableHoldGridKey", (KeyCode)308, "Key held to temporarily disable FarmGrid snapping while EnableGrid is true. Set to None to disable the hold key.");
			_secondaryGridKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Grid Toggle", "SecondaryGridKey", (KeyCode)304, "Hold this key to use the secondary red grid halfway between the normal green grid positions. Set to None to disable the secondary grid.");
			_holdToDisableGrid = ((BaseUnityPlugin)this).Config.Bind<bool>("Grid Toggle", "holdToDisableGrid", true, "When enabled, holding DisableHoldGridKey temporarily disables FarmGrid snapping and hides the grid. CraftFromContainers remains active while this FarmGrid bypass is held.");
			_connectToOtherPlants = ((BaseUnityPlugin)this).Config.Bind<bool>("Grid Toggle", "ConnectToOtherPlants", true, "When enabled, any recognized cultivator plant can be used as the local snap anchor. When disabled, FarmGrid only connects to the same plant type as the plant currently being placed.");
			_removeGrowthRestrictions = ((BaseUnityPlugin)this).Config.Bind<bool>("Plant Growth", "removeGrowthRestrictions", true, "Toggle planting without normal Valheim plant restrictions. Covers cultivated ground, biome, grow-space and CanGrow checks. In multiplayer install FarmGridRemake on the server and all clients for consistent growth behavior.");
			_allowPlantOverlap = ((BaseUnityPlugin)this).Config.Bind<bool>("Plant Growth", "AllowPlantOverlap", true, "Allow multiple plants to occupy the same position. This option only takes effect while removeGrowthRestrictions is enabled.");
			ApplyConfig();
			_plantSpacing.SettingChanged += delegate
			{
				ApplyConfig();
			};
			_farmGridSections.SettingChanged += delegate
			{
				ApplyConfig(rebuildGrid: true);
			};
			_farmGridYOffset.SettingChanged += delegate
			{
				ApplyConfig();
			};
			_farmGridColor.SettingChanged += delegate
			{
				ApplyConfig(rebuildGrid: true);
			};
			_secondaryGridColor.SettingChanged += delegate
			{
				ApplyConfig(rebuildGrid: true);
			};
			_farmGridFixedRotation.SettingChanged += delegate
			{
				ApplyConfig();
			};
			_plantObjectMasks.SettingChanged += delegate
			{
				ApplyConfig();
			};
			_customPlants.SettingChanged += delegate
			{
				ApplyConfig();
			};
			_useCustomGridSize.SettingChanged += delegate
			{
				ApplyConfig();
			};
			_customGridSize.SettingChanged += delegate
			{
				ApplyConfig();
			};
			_enableGrid.SettingChanged += delegate
			{
				ApplyConfig(rebuildGrid: true);
			};
			_holdToDisableGrid.SettingChanged += delegate
			{
				ApplyConfig();
			};
			_disableHoldGridKey.SettingChanged += delegate
			{
				ApplyConfig();
			};
			_secondaryGridKey.SettingChanged += delegate
			{
				ApplyConfig();
			};
			_connectToOtherPlants.SettingChanged += delegate
			{
				ApplyConfig();
			};
			_removeGrowthRestrictions.SettingChanged += delegate
			{
				ApplyConfig();
			};
			_allowPlantOverlap.SettingChanged += delegate
			{
				ApplyConfig();
			};
			ModCompatibility.DetectInstalledMods();
			_harmony = new Harmony("Moopfam.FarmGridRemake.Harmony");
			_harmony.PatchAll();
			ModCompatibility.PatchCraftFromContainers(_harmony);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"FarmGridRemake 1.1.3 loaded. Grid features run on clients; unrestricted planting also runs on servers when enabled.");
		}

		private void Update()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || !FarmGridPatch.GridEnabled || !FarmGridPatch.IsCultivatorEquipped(localPlayer))
			{
				FarmGridPatch.HideFarmGrid();
			}
		}

		private void ApplyConfig(bool rebuildGrid = false)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			FarmGridPatch.PlantSpacing = Mathf.Max(0f, _plantSpacing.Value);
			FarmGridPatch.GridSections = Mathf.Clamp(_farmGridSections.Value, 1, 12);
			FarmGridPatch.GridYOffset = Mathf.Clamp(_farmGridYOffset.Value, 0f, 2f);
			FarmGridPatch.GridColor = _farmGridColor.Value;
			FarmGridPatch.SecondaryGridColor = _secondaryGridColor.Value;
			FarmGridPatch.FixedRotationDegrees = Mathf.Clamp(_farmGridFixedRotation.Value, 0, 360);
			FarmGridPatch.UseCustomGridSize = _useCustomGridSize.Value;
			FarmGridPatch.CustomGridSize = Mathf.Clamp(_customGridSize.Value, 0.25f, 10f);
			FarmGridPatch.GridEnabled = _enableGrid.Value;
			FarmGridPatch.HoldToDisableGrid = _holdToDisableGrid.Value;
			FarmGridPatch.DisableHoldGridKey = _disableHoldGridKey.Value;
			FarmGridPatch.SecondaryGridKey = _secondaryGridKey.Value;
			FarmGridPatch.ConnectToOtherPlants = _connectToOtherPlants.Value;
			PlantGrowthPatch.RemoveGrowthRestrictions = _removeGrowthRestrictions.Value;
			PlantGrowthPatch.AllowPlantOverlap = _allowPlantOverlap.Value;
			FarmGridPatch.ApplyPlantObjectMasks(_plantObjectMasks.Value);
			FarmGridPatch.ApplyCustomPlants(_customPlants.Value);
			if (rebuildGrid)
			{
				FarmGridPatch.DestroyFarmGrid();
			}
		}

		private static bool ColorsApproximatelyEqual(Color a, Color b)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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_0032: 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_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)
			if (Mathf.Abs(a.r - b.r) < 0.001f && Mathf.Abs(a.g - b.g) < 0.001f && Mathf.Abs(a.b - b.b) < 0.001f)
			{
				return Mathf.Abs(a.a - b.a) < 0.001f;
			}
			return false;
		}

		private void OnDestroy()
		{
			FarmGridPatch.DestroyFarmGrid();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal static class ModCompatibility
	{
		internal const string PlantEverythingGuid = "advize.PlantEverything";

		internal const string CraftFromContainersGuid = "aedenthorn.CraftFromContainers";

		private static bool _craftFromContainersPatched;

		internal static bool PlantEverythingInstalled { get; private set; }

		internal static bool CraftFromContainersInstalled { get; private set; }

		internal static void DetectInstalledMods()
		{
			PlantEverythingInstalled = Chainloader.PluginInfos.ContainsKey("advize.PlantEverything");
			CraftFromContainersInstalled = Chainloader.PluginInfos.ContainsKey("aedenthorn.CraftFromContainers");
			if (PlantEverythingInstalled)
			{
				ManualLogSource log = FarmGridRemakePlugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"Plant Everything detected: FarmGridRemake cultivator deconstruction is disabled so Plant Everything handles removal.");
				}
			}
			if (CraftFromContainersInstalled)
			{
				ManualLogSource log2 = FarmGridRemakePlugin.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)"CraftFromContainers detected: enabling FarmGrid hold-bypass compatibility.");
				}
			}
		}

		internal static void PatchCraftFromContainers(Harmony harmony)
		{
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Expected O, but got Unknown
			if (_craftFromContainersPatched || !CraftFromContainersInstalled || harmony == null)
			{
				return;
			}
			try
			{
				BaseUnityPlugin instance = Chainloader.PluginInfos["aedenthorn.CraftFromContainers"].Instance;
				if ((Object)(object)instance == (Object)null)
				{
					ManualLogSource log = FarmGridRemakePlugin.Log;
					if (log != null)
					{
						log.LogWarning((object)"CraftFromContainers was detected but its plugin instance was not available; compatibility patch was skipped.");
					}
					return;
				}
				MethodInfo methodInfo = FindAllowByKey(((object)instance).GetType().Assembly);
				if (methodInfo == null)
				{
					ManualLogSource log2 = FarmGridRemakePlugin.Log;
					if (log2 != null)
					{
						log2.LogWarning((object)"CraftFromContainers AllowByKey() was not found; FarmGrid hold-bypass compatibility was skipped.");
					}
				}
				else
				{
					HarmonyMethod val = new HarmonyMethod(typeof(ModCompatibility), "CraftFromContainersAllowByKeyPostfix", (Type[])null);
					harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					_craftFromContainersPatched = true;
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log3 = FarmGridRemakePlugin.Log;
				if (log3 != null)
				{
					log3.LogWarning((object)("CraftFromContainers compatibility patch failed: " + ex.GetBaseException().Message));
				}
			}
		}

		private static MethodInfo FindAllowByKey(Assembly assembly)
		{
			if (assembly == null)
			{
				return null;
			}
			Type[] types;
			try
			{
				types = assembly.GetTypes();
			}
			catch (ReflectionTypeLoadException ex)
			{
				types = ex.Types;
			}
			if (types == null)
			{
				return null;
			}
			foreach (Type type in types)
			{
				if (!(type == null))
				{
					MethodInfo method = type.GetMethod("AllowByKey", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
					if (method != null && method.ReturnType == typeof(bool))
					{
						return method;
					}
				}
			}
			return null;
		}

		private static void CraftFromContainersAllowByKeyPostfix(ref bool __result)
		{
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && FarmGridPatch.IsCultivatorEquipped(localPlayer) && FarmGridPatch.IsGridTemporarilyDisabled())
			{
				__result = true;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "RemovePiece")]
	internal static class CultivatorDeconstructionPatch
	{
		private static readonly string[] RemovalLayers = new string[4] { "item", "piece_nonsolid", "Default_small", "Default" };

		private static readonly FieldInfo EyeField = AccessTools.Field(typeof(Character), "m_eye") ?? AccessTools.Field(typeof(Player), "m_eye");

		private static readonly FieldInfo MaxPlaceDistanceField = AccessTools.Field(typeof(Player), "m_maxPlaceDistance");

		private static readonly FieldInfo RemoveEffectsField = AccessTools.Field(typeof(Player), "m_removeEffects");

		private static readonly FieldInfo ZAnimField = AccessTools.Field(typeof(Character), "m_zanim") ?? AccessTools.Field(typeof(Player), "m_zanim");

		private static readonly FieldInfo CanBeRemovedField = AccessTools.Field(typeof(Piece), "m_canBeRemoved");

		private static readonly FieldInfo PieceNViewField = AccessTools.Field(typeof(Piece), "m_nview");

		private static readonly FieldInfo PlaceEffectField = AccessTools.Field(typeof(Piece), "m_placeEffect");

		private static readonly FieldInfo PickableNViewField = AccessTools.Field(typeof(Pickable), "m_nview");

		private static readonly FieldInfo PickedField = AccessTools.Field(typeof(Pickable), "m_picked");

		private static readonly MethodInfo GetRightItemMethod = FindMethod(typeof(Humanoid), "GetRightItem", 0) ?? FindMethod(typeof(Player), "GetRightItem", 0);

		private static readonly MethodInfo FaceLookDirectionMethod = FindMethod(typeof(Player), "FaceLookDirection", 0) ?? FindMethod(typeof(Character), "FaceLookDirection", 0);

		private static readonly MethodInfo DropResourcesMethod = FindMethod(typeof(Piece), "DropResources", 0);

		private static readonly MethodInfo IsPlacedByPlayerMethod = FindMethod(typeof(Piece), "IsPlacedByPlayer", 0);

		private static readonly MethodInfo WearNTearRemoveMethod = FindMethod(typeof(WearNTear), "Remove", 0);

		private static readonly MethodInfo ClaimOwnershipMethod = FindMethod(typeof(ZNetView), "ClaimOwnership", 0);

		[HarmonyPrepare]
		private static bool Prepare()
		{
			return !ModCompatibility.PlantEverythingInstalled;
		}

		[HarmonyPrefix]
		[HarmonyPriority(0)]
		private static bool Prefix(Player __instance)
		{
			if ((Object)(object)__instance == (Object)null || !FarmGridPatch.IsCultivatorEquipped(__instance))
			{
				return true;
			}
			FarmGridPatch.PrepareCultivatorPlantCache(__instance);
			Piece val = FindTargetPiece(__instance);
			if ((Object)(object)val == (Object)null || !FarmGridPatch.IsRecognizedCultivatorPlant(((Component)val).gameObject))
			{
				return true;
			}
			if (!CanRemove(__instance, val))
			{
				return false;
			}
			Remove(__instance, val);
			return false;
		}

		private static Piece FindTargetPiece(Player player)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: 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)
			Transform cameraTransform = GetCameraTransform();
			if ((Object)(object)cameraTransform == (Object)null)
			{
				return null;
			}
			int mask = LayerMask.GetMask(RemovalLayers);
			RaycastHit val = default(RaycastHit);
			if (!Physics.Raycast(cameraTransform.position, cameraTransform.forward, ref val, 50f, mask, (QueryTriggerInteraction)2))
			{
				return null;
			}
			if (Vector3.Distance(((RaycastHit)(ref val)).point, GetEyePosition(player)) >= GetMaxPlaceDistance(player))
			{
				return null;
			}
			if (!((Object)(object)((RaycastHit)(ref val)).collider != (Object)null))
			{
				return null;
			}
			return ((Component)((RaycastHit)(ref val)).collider).GetComponentInParent<Piece>();
		}

		private static bool CanRemove(Player player, Piece piece)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)piece == (Object)null)
			{
				return false;
			}
			if (!ReadBool(CanBeRemovedField, piece) && !InvokeBool(IsPlacedByPlayerMethod, piece))
			{
				return false;
			}
			if (PrivateArea.CheckAccess(((Component)piece).transform.position, 0f, true, false))
			{
				return true;
			}
			try
			{
				((Character)player).Message((MessageType)2, "$msg_privatezone", 0, (Sprite)null, false);
			}
			catch
			{
			}
			return false;
		}

		private static void Remove(Player player, Piece piece)
		{
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			WearNTear component = ((Component)piece).GetComponent<WearNTear>();
			if ((Object)(object)component != (Object)null && WearNTearRemoveMethod != null)
			{
				CreateEffect(ReadField(RemoveEffectsField, player), ((Component)piece).transform.position, Quaternion.identity);
				InvokeNoArgs(WearNTearRemoveMethod, component);
				PlayRemoveAnimation(player);
				return;
			}
			object obj = ReadField(PieceNViewField, piece);
			ZNetView target = (ZNetView)(((obj is ZNetView) ? obj : null) ?? ((Component)piece).GetComponent<ZNetView>());
			InvokeNoArgs(ClaimOwnershipMethod, target);
			InvokeNoArgs(DropResourcesMethod, piece);
			CreateEffect(ReadField(PlaceEffectField, piece), ((Component)piece).transform.position, ((Component)piece).transform.rotation);
			Pickable component2 = ((Component)piece).GetComponent<Pickable>();
			if ((Object)(object)component2 != (Object)null && !ReadBool(PickedField, component2))
			{
				InvokePick(component2);
			}
			DestroyNetworkObject(((Component)piece).gameObject);
			PlayRemoveAnimation(player);
		}

		private static void InvokePick(Pickable pickable)
		{
			object obj = ReadField(PickableNViewField, pickable);
			ZNetView val = (ZNetView)(((obj is ZNetView) ? obj : null) ?? ((Component)pickable).GetComponent<ZNetView>());
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			MethodInfo methodInfo = FindRpcMethod(((object)val).GetType());
			if (methodInfo == null)
			{
				return;
			}
			ParameterInfo[] parameters = methodInfo.GetParameters();
			object[] array = new object[parameters.Length];
			array[0] = "RPC_Pick";
			for (int i = 1; i < parameters.Length; i++)
			{
				if (parameters[i].ParameterType == typeof(object[]))
				{
					array[i] = new object[1] { 0 };
				}
				else if (parameters[i].ParameterType == typeof(int))
				{
					array[i] = 0;
				}
				else if (parameters[i].HasDefaultValue)
				{
					array[i] = parameters[i].DefaultValue;
				}
				else
				{
					array[i] = (parameters[i].ParameterType.IsValueType ? Activator.CreateInstance(parameters[i].ParameterType) : null);
				}
			}
			try
			{
				methodInfo.Invoke(val, array);
			}
			catch (Exception ex)
			{
				ManualLogSource log = FarmGridRemakePlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("FarmGrid: pickable removal RPC failed: " + ex.GetBaseException().Message));
				}
			}
		}

		private static void DestroyNetworkObject(GameObject gameObject)
		{
			if ((Object)(object)gameObject == (Object)null || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return;
			}
			MethodInfo methodInfo = FindDestroyMethod(((object)ZNetScene.instance).GetType());
			if (methodInfo == null)
			{
				return;
			}
			try
			{
				methodInfo.Invoke(ZNetScene.instance, new object[1] { gameObject });
			}
			catch (Exception ex)
			{
				ManualLogSource log = FarmGridRemakePlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("FarmGrid: network destroy failed: " + ex.GetBaseException().Message));
				}
			}
		}

		private static void PlayRemoveAnimation(Player player)
		{
			InvokeNoArgs(FaceLookDirectionMethod, player);
			ItemData val = (ItemData)((GetRightItemMethod != null) ? /*isinst with value type is only supported in some contexts*/: null);
			if (val == null || val.m_shared == null || val.m_shared.m_attack == null || ZAnimField == null)
			{
				return;
			}
			string attackAnimation = val.m_shared.m_attack.m_attackAnimation;
			if (string.IsNullOrEmpty(attackAnimation))
			{
				return;
			}
			object obj = ReadField(ZAnimField, player);
			if (obj == null)
			{
				return;
			}
			MethodInfo methodInfo = AccessTools.Method(obj.GetType(), "SetTrigger", new Type[1] { typeof(string) }, (Type[])null);
			try
			{
				methodInfo?.Invoke(obj, new object[1] { attackAnimation });
			}
			catch
			{
			}
		}

		private static Transform GetCameraTransform()
		{
			try
			{
				FieldInfo fieldInfo = AccessTools.Field(typeof(GameCamera), "instance");
				PropertyInfo propertyInfo = AccessTools.Property(typeof(GameCamera), "instance") ?? AccessTools.Property(typeof(GameCamera), "Instance");
				object? obj = ((fieldInfo != null) ? fieldInfo.GetValue(null) : ((propertyInfo != null) ? propertyInfo.GetValue(null, null) : null));
				Component val = (Component)((obj is Component) ? obj : null);
				if ((Object)(object)val != (Object)null)
				{
					return val.transform;
				}
			}
			catch
			{
			}
			if (!((Object)(object)Camera.main != (Object)null))
			{
				return null;
			}
			return ((Component)Camera.main).transform;
		}

		private static Vector3 GetEyePosition(Player player)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			object obj = ReadField(EyeField, player);
			Transform val = (Transform)((obj is Transform) ? obj : null);
			if (!((Object)(object)val != (Object)null))
			{
				return ((Component)player).transform.position + Vector3.up * 1.6f;
			}
			return val.position;
		}

		private static float GetMaxPlaceDistance(Player player)
		{
			object obj = ReadField(MaxPlaceDistanceField, player);
			if (!(obj is float))
			{
				return 5f;
			}
			return (float)obj;
		}

		private static void CreateEffect(object effectList, Vector3 position, Quaternion rotation)
		{
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			if (effectList == null)
			{
				return;
			}
			MethodInfo[] methods = effectList.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (methodInfo == null || methodInfo.Name != "Create")
				{
					continue;
				}
				ParameterInfo[] parameters = methodInfo.GetParameters();
				if (parameters.Length >= 2 && !(parameters[0].ParameterType != typeof(Vector3)) && !(parameters[1].ParameterType != typeof(Quaternion)))
				{
					object[] array = BuildDefaultArguments(parameters);
					array[0] = position;
					array[1] = rotation;
					try
					{
						methodInfo.Invoke(effectList, array);
						break;
					}
					catch
					{
						break;
					}
				}
			}
		}

		private static MethodInfo FindMethod(Type type, string name, int parameterCount)
		{
			if (type == null)
			{
				return null;
			}
			MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (methodInfo != null && methodInfo.Name == name && !methodInfo.IsStatic && methodInfo.GetParameters().Length == parameterCount)
				{
					return methodInfo;
				}
			}
			return null;
		}

		private static MethodInfo FindRpcMethod(Type type)
		{
			MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (!(methodInfo == null) && !(methodInfo.Name != "InvokeRPC"))
				{
					ParameterInfo[] parameters = methodInfo.GetParameters();
					if (parameters.Length != 0 && parameters[0].ParameterType == typeof(string))
					{
						return methodInfo;
					}
				}
			}
			return null;
		}

		private static MethodInfo FindDestroyMethod(Type type)
		{
			MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (!(methodInfo == null) && !(methodInfo.Name != "Destroy"))
				{
					ParameterInfo[] parameters = methodInfo.GetParameters();
					if (parameters.Length == 1 && parameters[0].ParameterType == typeof(GameObject))
					{
						return methodInfo;
					}
				}
			}
			return null;
		}

		private static object[] BuildDefaultArguments(ParameterInfo[] parameters)
		{
			object[] array = new object[parameters.Length];
			for (int i = 0; i < parameters.Length; i++)
			{
				if (parameters[i].HasDefaultValue)
				{
					array[i] = parameters[i].DefaultValue;
				}
				else
				{
					array[i] = (parameters[i].ParameterType.IsValueType ? Activator.CreateInstance(parameters[i].ParameterType) : null);
				}
			}
			return array;
		}

		private static object ReadField(FieldInfo field, object target)
		{
			if (field == null || target == null)
			{
				return null;
			}
			try
			{
				return field.GetValue(target);
			}
			catch
			{
				return null;
			}
		}

		private static bool ReadBool(FieldInfo field, object target)
		{
			object obj = ReadField(field, target);
			if (obj is bool)
			{
				return (bool)obj;
			}
			return false;
		}

		private static bool InvokeBool(MethodInfo method, object target)
		{
			if (method == null || target == null)
			{
				return false;
			}
			try
			{
				object obj = method.Invoke(target, null);
				return obj is bool && (bool)obj;
			}
			catch
			{
				return false;
			}
		}

		private static void InvokeNoArgs(MethodInfo method, object target)
		{
			if (method == null || target == null)
			{
				return;
			}
			try
			{
				method.Invoke(target, null);
			}
			catch
			{
			}
		}
	}
	[HarmonyPatch(typeof(Player), "UpdatePlacementGhost")]
	internal static class FarmGridPatch
	{
		private sealed class PlantObject
		{
			internal Vector3 Position;

			internal readonly float GrowthSize;

			internal readonly HashSet<string> GridKeys;

			internal PlantObject(Vector3 position, float growthSize, IEnumerable<string> gridKeys)
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				Position = position;
				GrowthSize = growthSize;
				GridKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
				if (gridKeys == null)
				{
					return;
				}
				foreach (string gridKey in gridKeys)
				{
					if (!string.IsNullOrEmpty(gridKey))
					{
						GridKeys.Add(gridKey);
					}
				}
			}
		}

		internal static float PlantSpacing = 0.01f;

		internal static int GridSections = 2;

		internal static float GridYOffset = 0.2f;

		internal static Color GridColor = new Color(0f, 1f, 0f, 0.35f);

		internal static Color SecondaryGridColor = new Color(1f, 0f, 0f, 0.35f);

		internal static int FixedRotationDegrees;

		internal static bool UseCustomGridSize;

		internal static float CustomGridSize = 1f;

		internal static bool GridEnabled = true;

		internal static bool HoldToDisableGrid = true;

		internal static KeyCode DisableHoldGridKey = (KeyCode)308;

		internal static KeyCode SecondaryGridKey = (KeyCode)304;

		internal static bool ConnectToOtherPlants = true;

		private static readonly Dictionary<string, float> CustomPlants = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);

		private static readonly List<string> PlantObjectMasks = new List<string> { "piece", "piece_nonsolid", "item" };

		private static readonly Collider[] ColliderBuffer = (Collider[])(object)new Collider[1024];

		private static readonly Collider[] AnchorColliderBuffer = (Collider[])(object)new Collider[4096];

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

		private static readonly string[] PlantNameTokens = new string[26]
		{
			"plant", "sapling", "seed", "tree", "bush", "shrub", "berry", "mushroom", "thistle", "dandelion",
			"fiddlehead", "smokepuff", "carrot", "turnip", "onion", "kale", "barley", "flax", "vine", "beech",
			"birch", "fir", "pine", "oak", "ygg", "ashwood"
		};

		private static int _cultivatorPrefabLayerMask;

		private static bool _cultivatorPrefabCacheReady;

		private static float _nextCultivatorCacheRefreshTime;

		private static readonly FieldInfo PlacementGhostField = AccessTools.Field(typeof(Player), "m_placementGhost");

		private static readonly MethodInfo GetRightItemMethod = AccessTools.Method(typeof(Humanoid), "GetRightItem", Type.EmptyTypes, (Type[])null) ?? AccessTools.Method(typeof(Player), "GetRightItem", Type.EmptyTypes, (Type[])null);

		private static GameObject[] _farmGrid;

		private static bool _farmGridVisible;

		private static int _plantObjectMask;

		private static Material _lineMaterial;

		private static bool _snapPairValid;

		private static int _snapGhostInstanceId;

		private static Vector3 _snapAnchorPosition;

		private static Vector3 _snapRawPosition;

		private static Vector3 _greenSnapPoint;

		private static Vector3 _redSnapPoint;

		private static float _snapGridSize;

		private static int _redFromGreenHalfX;

		private static int _redFromGreenHalfZ;

		private static bool _lastSecondaryGridHeld;

		private static bool _hasLastPlacementYaw;

		private static float _lastPlacementYaw;

		private static bool _stableGridDirectionValid;

		private static Vector3 _stableGridDirection = Vector3.forward;

		private static bool _visualGridOriginValid;

		private static Vector3 _visualGridOrigin;

		[HarmonyPrefix]
		private static void UpdatePlacementGhostPrefix(Player __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && IsCultivatorEquipped(__instance))
			{
				EnsureCultivatorPrefabCache(__instance);
				GameObject placementGhost = GetPlacementGhost(__instance);
				if (!((Object)(object)placementGhost == (Object)null) && TryGetPlantObject(placementGhost, out var _))
				{
					PlantGrowthPatch.ApplyPlacementFreedom(placementGhost);
				}
			}
		}

		[HarmonyPostfix]
		private static void UpdatePlacementGhostPostfix(Player __instance)
		{
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_024d: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02be: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_036e: Unknown result type (might be due to invalid IL or missing references)
			//IL_037a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0395: Unknown result type (might be due to invalid IL or missing references)
			//IL_039c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0316: Unknown result type (might be due to invalid IL or missing references)
			//IL_031b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0422: Unknown result type (might be due to invalid IL or missing references)
			//IL_041b: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0427: Unknown result type (might be due to invalid IL or missing references)
			//IL_0437: Unknown result type (might be due to invalid IL or missing references)
			//IL_0465: Unknown result type (might be due to invalid IL or missing references)
			//IL_046b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0470: Unknown result type (might be due to invalid IL or missing references)
			//IL_0472: Unknown result type (might be due to invalid IL or missing references)
			//IL_0486: Unknown result type (might be due to invalid IL or missing references)
			//IL_048b: Unknown result type (might be due to invalid IL or missing references)
			//IL_048d: Unknown result type (might be due to invalid IL or missing references)
			//IL_048f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0591: Unknown result type (might be due to invalid IL or missing references)
			//IL_059c: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_05b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_05b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_04af: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_04bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_04da: Unknown result type (might be due to invalid IL or missing references)
			//IL_04df: Unknown result type (might be due to invalid IL or missing references)
			//IL_061c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0621: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_05dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_05de: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_05bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_05cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0528: Unknown result type (might be due to invalid IL or missing references)
			//IL_052d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0533: Unknown result type (might be due to invalid IL or missing references)
			//IL_0538: Unknown result type (might be due to invalid IL or missing references)
			//IL_054b: Unknown result type (might be due to invalid IL or missing references)
			//IL_054d: Unknown result type (might be due to invalid IL or missing references)
			//IL_04eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0638: Unknown result type (might be due to invalid IL or missing references)
			//IL_0631: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_05fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0600: Unknown result type (might be due to invalid IL or missing references)
			//IL_0605: Unknown result type (might be due to invalid IL or missing references)
			//IL_060a: Unknown result type (might be due to invalid IL or missing references)
			//IL_056b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0570: Unknown result type (might be due to invalid IL or missing references)
			//IL_063d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0645: Unknown result type (might be due to invalid IL or missing references)
			//IL_065a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0653: Unknown result type (might be due to invalid IL or missing references)
			//IL_065f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0661: Unknown result type (might be due to invalid IL or missing references)
			//IL_0663: Unknown result type (might be due to invalid IL or missing references)
			//IL_0667: Unknown result type (might be due to invalid IL or missing references)
			//IL_0680: Unknown result type (might be due to invalid IL or missing references)
			//IL_0682: Unknown result type (might be due to invalid IL or missing references)
			//IL_0686: Unknown result type (might be due to invalid IL or missing references)
			//IL_068b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0690: Unknown result type (might be due to invalid IL or missing references)
			//IL_0694: Unknown result type (might be due to invalid IL or missing references)
			//IL_069f: Unknown result type (might be due to invalid IL or missing references)
			//IL_06a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_06a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_06aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_06af: Unknown result type (might be due to invalid IL or missing references)
			//IL_06b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_06be: Unknown result type (might be due to invalid IL or missing references)
			//IL_06c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_06c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_06c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_06ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_06d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_06d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_06dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_06e0: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			if (!IsCultivatorEquipped(__instance))
			{
				HideFarmGrid();
				ResetSnapPair();
				return;
			}
			if (!GridEnabled)
			{
				HideFarmGrid();
				ResetSnapPair();
				return;
			}
			if (IsGridTemporarilyDisabled())
			{
				HideFarmGrid();
				ResetSnapPair();
				return;
			}
			EnsureCultivatorPrefabCache(__instance);
			GameObject placementGhost = GetPlacementGhost(__instance);
			if ((Object)(object)placementGhost == (Object)null || !placementGhost.activeInHierarchy)
			{
				HideFarmGrid();
				ResetSnapPair();
				return;
			}
			if (!TryGetPlantObject(placementGhost, out var plantObject))
			{
				HideFarmGrid();
				ResetSnapPair();
				return;
			}
			PlantGrowthPatch.ApplyPlacementFreedom(placementGhost);
			Vector3 rawGhostPosition = placementGhost.transform.position;
			plantObject.Position = rawGhostPosition;
			float num = (UseCustomGridSize ? Mathf.Max(0.05f, CustomGridSize) : Mathf.Max(0.05f, plantObject.GrowthSize * 2f + PlantSpacing));
			float searchRadius = Mathf.Max(2.5f, num * 2.25f);
			List<PlantObject> otherPlants = GetOtherPlants(rawGhostPosition, searchRadius, placementGhost, scanAllLayers: true);
			if (!ConnectToOtherPlants)
			{
				for (int num2 = otherPlants.Count - 1; num2 >= 0; num2--)
				{
					if (!SharesGridType(otherPlants[num2], plantObject))
					{
						otherPlants.RemoveAt(num2);
					}
				}
			}
			if (otherPlants.Count == 0)
			{
				HideFarmGrid();
				ResetSnapPair();
				return;
			}
			otherPlants.Sort((PlantObject a, PlantObject b) => HorizontalSqrDistance(a.Position, rawGhostPosition).CompareTo(HorizontalSqrDistance(b.Position, rawGhostPosition)));
			int instanceID = ((Object)placementGhost).GetInstanceID();
			bool flag = _snapPairValid && _snapGhostInstanceId != instanceID;
			PlantObject plantObject2 = otherPlants[0];
			float num3 = ((_snapPairValid && _snapGridSize > 0.001f) ? _snapGridSize : num);
			float num4 = Mathf.Max(0.15f, num3 * 0.45f);
			bool flag2 = _snapPairValid && HorizontalSqrDistance(_snapRawPosition, rawGhostPosition) <= num4 * num4;
			if (_snapPairValid && (flag || flag2))
			{
				for (int num5 = 0; num5 < otherPlants.Count; num5++)
				{
					if (HorizontalSqrDistance(otherPlants[num5].Position, _snapAnchorPosition) <= 0.0025f)
					{
						plantObject2 = otherPlants[num5];
						break;
					}
				}
			}
			bool num6 = FixedRotationDegrees != 0;
			float y = placementGhost.transform.eulerAngles.y;
			bool flag3 = !num6 && _snapPairValid && !flag && _hasLastPlacementYaw && Mathf.Abs(Mathf.DeltaAngle(_lastPlacementYaw, y)) > 0.1f;
			Vector3 val;
			if (!num6)
			{
				val = ((!_snapPairValid || !_stableGridDirectionValid || flag3) ? GetPlacementGridDirection(placementGhost) : _stableGridDirection);
			}
			else
			{
				float num7 = ((FixedRotationDegrees == 360) ? 0f : ((float)FixedRotationDegrees));
				val = Quaternion.Euler(0f, num7, 0f) * Vector3.forward;
				val.y = 0f;
				((Vector3)(ref val)).Normalize();
			}
			if (flag3 && _snapPairValid)
			{
				for (int num8 = 0; num8 < otherPlants.Count; num8++)
				{
					if (HorizontalSqrDistance(otherPlants[num8].Position, _snapAnchorPosition) <= 0.0025f)
					{
						plantObject2 = otherPlants[num8];
						break;
					}
				}
			}
			float num9 = (UseCustomGridSize ? Mathf.Max(0.05f, CustomGridSize) : CalculateGridSize(plantObject2, plantObject));
			Vector3 val2 = default(Vector3);
			((Vector3)(ref val2))..ctor(val.z, 0f, 0f - val.x);
			bool flag4 = IsSecondaryGridHeld();
			bool flag5 = !_snapPairValid || HorizontalSqrDistance(_snapAnchorPosition, plantObject2.Position) > 0.0025f;
			bool flag6 = !_snapPairValid || Mathf.Abs(_snapGridSize - num9) > 0.001f;
			float num10 = Mathf.Max(0.15f, num9 * 0.45f);
			bool flag7 = !_snapPairValid || (!flag3 && !flag && HorizontalSqrDistance(_snapRawPosition, rawGhostPosition) > num10 * num10);
			Vector3 position = (_lastSecondaryGridHeld ? _redSnapPoint : _greenSnapPoint);
			bool flag8 = _snapPairValid && !PlantGrowthPatch.PlantOverlapEnabled && HasOverlappingPlants(position, plantObject.GrowthSize, placementGhost);
			if (!_snapPairValid || flag5 || flag6 || flag7 || flag8)
			{
				int selectedOffsetX;
				int selectedOffsetZ;
				Vector3 val3 = FindClosestLocalGreenPoint(plantObject2.Position, rawGhostPosition, val, val2, num9, plantObject.GrowthSize, placementGhost, PlantGrowthPatch.PlantOverlapEnabled, out selectedOffsetX, out selectedOffsetZ);
				if (val3 == Vector3.zero)
				{
					HideFarmGrid();
					ResetSnapPair();
					return;
				}
				_greenSnapPoint = val3;
				_redSnapPoint = FindClosestLocalRedPoint(plantObject2.Position, rawGhostPosition, val, val2, num9, plantObject.GrowthSize, placementGhost, PlantGrowthPatch.PlantOverlapEnabled, out var selectedHalfOffsetX, out var selectedHalfOffsetZ);
				if (_redSnapPoint == Vector3.zero)
				{
					_redSnapPoint = _greenSnapPoint;
					selectedHalfOffsetX = selectedOffsetX * 2;
					selectedHalfOffsetZ = selectedOffsetZ * 2;
				}
				_redFromGreenHalfX = selectedHalfOffsetX - selectedOffsetX * 2;
				_redFromGreenHalfZ = selectedHalfOffsetZ - selectedOffsetZ * 2;
				_snapPairValid = true;
				_snapGhostInstanceId = instanceID;
				_snapAnchorPosition = plantObject2.Position;
				_snapRawPosition = rawGhostPosition;
				_snapGridSize = num9;
				_lastSecondaryGridHeld = flag4;
				_stableGridDirection = val;
				_stableGridDirectionValid = true;
				if (!_visualGridOriginValid || flag5 || flag6)
				{
					_visualGridOrigin = _greenSnapPoint;
					_visualGridOriginValid = true;
				}
			}
			else if (flag3)
			{
				float num11 = num9 * 0.5f;
				Vector3 val4 = val * ((float)_redFromGreenHalfX * num11) + val2 * ((float)_redFromGreenHalfZ * num11);
				if (_lastSecondaryGridHeld)
				{
					_greenSnapPoint = SetGroundHeight(_redSnapPoint - val4);
				}
				else
				{
					_redSnapPoint = SetGroundHeight(_greenSnapPoint + val4);
				}
				_stableGridDirection = val;
				_stableGridDirectionValid = true;
				_snapRawPosition = rawGhostPosition;
				_visualGridOrigin = _greenSnapPoint;
				_visualGridOriginValid = true;
			}
			else if (flag)
			{
				_snapRawPosition = rawGhostPosition;
			}
			_snapGhostInstanceId = instanceID;
			Vector3 position2 = (flag4 ? _redSnapPoint : _greenSnapPoint);
			placementGhost.transform.position = position2;
			Vector3 val5 = (_visualGridOriginValid ? _visualGridOrigin : _greenSnapPoint);
			DrawGridSet(val5, val, num9, GridColor, 0);
			if (flag4)
			{
				float num12 = num9 * 0.5f;
				DrawGridSet(val5 + val * num12, val, num9, SecondaryGridColor, 1);
				DrawGridSet(val5 + val2 * num12, val, num9, SecondaryGridColor, 2);
				DrawGridSet(val5 + val * num12 + val2 * num12, val, num9, SecondaryGridColor, 3);
			}
			else
			{
				HideGridSet(1);
				HideGridSet(2);
				HideGridSet(3);
			}
			_lastSecondaryGridHeld = flag4;
			_lastPlacementYaw = y;
			_hasLastPlacementYaw = true;
		}

		internal static void PrepareCultivatorPlantCache(Player player)
		{
			EnsureCultivatorPrefabCache(player);
		}

		internal static bool IsRecognizedCultivatorPlant(GameObject gameObject)
		{
			if ((Object)(object)gameObject == (Object)null)
			{
				return false;
			}
			if ((Object)(object)gameObject.GetComponentInParent<Plant>() != (Object)null || (Object)(object)gameObject.GetComponentInChildren<Plant>(true) != (Object)null)
			{
				return true;
			}
			PlantObject plantObject;
			return TryGetPlantObject(gameObject, out plantObject);
		}

		internal static bool IsGridTemporarilyDisabled()
		{
			//IL_000e: 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)
			if (!GridEnabled || !HoldToDisableGrid || (int)DisableHoldGridKey == 0)
			{
				return false;
			}
			try
			{
				return Input.GetKey(DisableHoldGridKey);
			}
			catch
			{
				return false;
			}
		}

		internal static bool IsSecondaryGridHeld()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			if (!GridEnabled || (int)SecondaryGridKey == 0)
			{
				return false;
			}
			try
			{
				return Input.GetKey(SecondaryGridKey);
			}
			catch
			{
				return false;
			}
		}

		internal static bool IsCultivatorEquipped(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			ItemData rightItem = GetRightItem(player);
			if (rightItem != null && rightItem.m_shared != null)
			{
				return rightItem.m_shared.m_name == "$item_cultivator";
			}
			return false;
		}

		private static GameObject GetPlacementGhost(Player player)
		{
			if ((Object)(object)player == (Object)null || PlacementGhostField == null)
			{
				return null;
			}
			try
			{
				object? value = PlacementGhostField.GetValue(player);
				return (GameObject)((value is GameObject) ? value : null);
			}
			catch (Exception ex)
			{
				ManualLogSource log = FarmGridRemakePlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("FarmGrid: failed to read Player.m_placementGhost: " + ex.Message));
				}
				return null;
			}
		}

		private static ItemData GetRightItem(Player player)
		{
			if ((Object)(object)player == (Object)null || GetRightItemMethod == null)
			{
				return null;
			}
			try
			{
				object? obj = GetRightItemMethod.Invoke(player, null);
				return (ItemData)((obj is ItemData) ? obj : null);
			}
			catch (Exception ex)
			{
				ManualLogSource log = FarmGridRemakePlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("FarmGrid: failed to call Humanoid.GetRightItem(): " + ex.Message));
				}
				return null;
			}
		}

		private static void EnsureCultivatorPrefabCache(Player player)
		{
			if ((Object)(object)player == (Object)null || Time.unscaledTime < _nextCultivatorCacheRefreshTime)
			{
				return;
			}
			ItemData rightItem = GetRightItem(player);
			if (rightItem == null || rightItem.m_shared == null)
			{
				return;
			}
			try
			{
				object shared = rightItem.m_shared;
				FieldInfo fieldInfo = AccessTools.Field(shared.GetType(), "m_buildPieces");
				PropertyInfo propertyInfo = AccessTools.Property(shared.GetType(), "m_buildPieces");
				object obj = ((fieldInfo != null) ? fieldInfo.GetValue(shared) : ((propertyInfo != null) ? propertyInfo.GetValue(shared, null) : null));
				if (obj == null)
				{
					return;
				}
				FieldInfo fieldInfo2 = AccessTools.Field(obj.GetType(), "m_pieces");
				PropertyInfo propertyInfo2 = AccessTools.Property(obj.GetType(), "m_pieces");
				if (!(((fieldInfo2 != null) ? fieldInfo2.GetValue(obj) : ((propertyInfo2 != null) ? propertyInfo2.GetValue(obj, null) : null)) is IEnumerable enumerable))
				{
					return;
				}
				CultivatorPrefabNames.Clear();
				_cultivatorPrefabLayerMask = 0;
				foreach (object item in enumerable)
				{
					GameObject val = (GameObject)((item is GameObject) ? item : null);
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					string text = NormalizeObjectName(((Object)val).name);
					if (!((Object)(object)val.GetComponentInChildren<Plant>(true) != (Object)null) && !((Object)(object)val.GetComponentInChildren<Pickable>(true) != (Object)null) && !CustomPlants.ContainsKey(text) && !LooksLikePlantPrefabName(text))
					{
						continue;
					}
					if (!string.IsNullOrEmpty(text))
					{
						CultivatorPrefabNames.Add(text);
					}
					Transform[] componentsInChildren = val.GetComponentsInChildren<Transform>(true);
					for (int i = 0; i < componentsInChildren.Length; i++)
					{
						if (!((Object)(object)componentsInChildren[i] == (Object)null) && !((Object)(object)((Component)componentsInChildren[i]).gameObject == (Object)null))
						{
							int layer = ((Component)componentsInChildren[i]).gameObject.layer;
							if (layer >= 0 && layer < 32)
							{
								_cultivatorPrefabLayerMask |= 1 << layer;
							}
						}
					}
				}
				_cultivatorPrefabCacheReady = CultivatorPrefabNames.Count > 0;
				_nextCultivatorCacheRefreshTime = Time.unscaledTime + 2f;
				_plantObjectMask = 0;
				ManualLogSource log = FarmGridRemakePlugin.Log;
				if (log != null)
				{
					log.LogDebug((object)("FarmGrid: discovered " + CultivatorPrefabNames.Count + " cultivator prefab(s) for automatic plant grid support."));
				}
			}
			catch (Exception ex)
			{
				_nextCultivatorCacheRefreshTime = Time.unscaledTime + 1f;
				ManualLogSource log2 = FarmGridRemakePlugin.Log;
				if (log2 != null)
				{
					log2.LogWarning((object)("FarmGrid: failed to inspect cultivator PieceTable: " + ex.Message));
				}
			}
		}

		internal static void ApplyPlantObjectMasks(string raw)
		{
			PlantObjectMasks.Clear();
			if (!string.IsNullOrWhiteSpace(raw))
			{
				string[] array = raw.Split(new char[1] { ',' });
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i].Trim();
					if (!string.IsNullOrEmpty(text))
					{
						PlantObjectMasks.Add(text);
					}
				}
			}
			if (PlantObjectMasks.Count == 0)
			{
				PlantObjectMasks.Add("piece");
				PlantObjectMasks.Add("piece_nonsolid");
				PlantObjectMasks.Add("item");
			}
			_plantObjectMask = 0;
		}

		internal static void ApplyCustomPlants(string raw)
		{
			CustomPlants.Clear();
			CultivatorPrefabNames.Clear();
			_cultivatorPrefabLayerMask = 0;
			_cultivatorPrefabCacheReady = false;
			_nextCultivatorCacheRefreshTime = 0f;
			_plantObjectMask = 0;
			if (string.IsNullOrWhiteSpace(raw))
			{
				return;
			}
			string[] array = raw.Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Split(new char[1] { ':' }, 2);
				if (array2.Length == 2)
				{
					string text = array2[0].Trim();
					if (!string.IsNullOrEmpty(text) && float.TryParse(array2[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
					{
						CustomPlants[text] = Mathf.Max(0f, result);
					}
				}
			}
		}

		private static float CalculateGridSize(PlantObject anchor, PlantObject ghostPlant)
		{
			return Mathf.Max(0.05f, ghostPlant.GrowthSize * 2f + PlantSpacing);
		}

		private static Vector3 GetPlacementGridDirection(GameObject ghost)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: 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_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ghost == (Object)null)
			{
				return Vector3.forward;
			}
			Vector3 result = Flatten(ghost.transform.forward);
			if (((Vector3)(ref result)).sqrMagnitude < 0.0001f)
			{
				result = Flatten(ghost.transform.right);
			}
			if (((Vector3)(ref result)).sqrMagnitude < 0.0001f)
			{
				return Vector3.forward;
			}
			((Vector3)(ref result)).Normalize();
			return result;
		}

		private static Vector3 FindClosestLocalGreenPoint(Vector3 anchor, Vector3 ghostPosition, Vector3 gridDir, Vector3 perpendicular, float gridSize, float ghostGrowthSize, GameObject placementGhost, bool allowPlantOverlap, out int selectedOffsetX, out int selectedOffsetZ)
		{
			//IL_0008: 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_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: 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_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: 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_0099: 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)
			selectedOffsetX = 0;
			selectedOffsetZ = 0;
			Vector3 val = Vector3.zero;
			float num = float.MaxValue;
			bool flag = false;
			if (allowPlantOverlap)
			{
				val = SetGroundHeight(anchor);
				num = HorizontalSqrDistance(val, ghostPosition);
				flag = true;
			}
			for (int i = -1; i <= 1; i++)
			{
				for (int j = -1; j <= 1; j++)
				{
					if (i != 0 || j != 0)
					{
						Vector3 groundHeight = anchor + gridDir * ((float)i * gridSize) + perpendicular * ((float)j * gridSize);
						groundHeight = SetGroundHeight(groundHeight);
						float num2 = HorizontalSqrDistance(groundHeight, ghostPosition);
						if ((allowPlantOverlap || !HasOverlappingPlants(groundHeight, ghostGrowthSize, placementGhost)) && (!flag || num2 < num))
						{
							flag = true;
							num = num2;
							val = groundHeight;
							selectedOffsetX = i;
							selectedOffsetZ = j;
						}
					}
				}
			}
			return val;
		}

		private static Vector3 FindClosestLocalRedPoint(Vector3 anchor, Vector3 ghostPosition, Vector3 gridDir, Vector3 perpendicular, float gridSize, float ghostGrowthSize, GameObject placementGhost, bool allowPlantOverlap, out int selectedHalfOffsetX, out int selectedHalfOffsetZ)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: 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_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: 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_0050: 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_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			selectedHalfOffsetX = 0;
			selectedHalfOffsetZ = 0;
			float num = gridSize * 0.5f;
			Vector3 val = Vector3.zero;
			float num2 = float.MaxValue;
			if (allowPlantOverlap)
			{
				val = SetGroundHeight(anchor);
				num2 = HorizontalSqrDistance(val, ghostPosition);
			}
			for (int i = -1; i <= 1; i++)
			{
				for (int j = -1; j <= 1; j++)
				{
					if (i == 0 && j == 0)
					{
						continue;
					}
					Vector3 groundHeight = anchor + gridDir * ((float)i * num) + perpendicular * ((float)j * num);
					groundHeight = SetGroundHeight(groundHeight);
					if (allowPlantOverlap || !HasOverlappingPlants(groundHeight, ghostGrowthSize, placementGhost))
					{
						float num3 = HorizontalSqrDistance(groundHeight, ghostPosition);
						if (num3 < num2)
						{
							num2 = num3;
							val = groundHeight;
							selectedHalfOffsetX = i;
							selectedHalfOffsetZ = j;
						}
					}
				}
			}
			return val;
		}

		private static bool SharesGridType(PlantObject a, PlantObject b)
		{
			if (a == null || b == null || a.GridKeys == null || b.GridKeys == null || a.GridKeys.Count == 0 || b.GridKeys.Count == 0)
			{
				return false;
			}
			foreach (string gridKey in a.GridKeys)
			{
				if (b.GridKeys.Contains(gridKey))
				{
					return true;
				}
			}
			return false;
		}

		private static Vector3 GetGridDir(List<PlantObject> plants, PlantObject ghostPlant)
		{
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: 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_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < plants.Count - 1; i++)
			{
				for (int j = i + 1; j < plants.Count; j++)
				{
					Vector3 result = Flatten(plants[i].Position - plants[j].Position);
					float magnitude = ((Vector3)(ref result)).magnitude;
					if (magnitude < 0.001f)
					{
						continue;
					}
					float num = Mathf.Max(Mathf.Max(plants[i].GrowthSize, plants[j].GrowthSize), ghostPlant.GrowthSize) * 2f + PlantSpacing;
					if (num <= 0.001f)
					{
						continue;
					}
					float num2 = Mathf.Round(magnitude / num);
					if (!(num2 < 1f))
					{
						float num3 = num2 * num;
						float num4 = Mathf.Max(0.02f, num2 * 0.03f);
						if (Mathf.Abs(magnitude - num3) <= num4)
						{
							((Vector3)(ref result)).Normalize();
							return result;
						}
					}
				}
			}
			Vector3 result2 = Flatten(plants[0].Position - plants[1].Position);
			if (((Vector3)(ref result2)).sqrMagnitude < 0.0001f)
			{
				result2 = Vector3.forward;
			}
			else
			{
				((Vector3)(ref result2)).Normalize();
			}
			return result2;
		}

		private static List<PlantObject> GetOtherPlants(Vector3 position, float searchRadius, GameObject placementGhost, bool scanAllLayers = false)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			List<PlantObject> list = new List<PlantObject>();
			int num;
			if (scanAllLayers)
			{
				num = -1;
			}
			else
			{
				EnsurePlantMask();
				num = _plantObjectMask;
			}
			Collider[] array = (scanAllLayers ? AnchorColliderBuffer : ColliderBuffer);
			int num2 = Physics.OverlapSphereNonAlloc(position, searchRadius, array, num, (QueryTriggerInteraction)2);
			for (int i = 0; i < num2; i++)
			{
				Collider val = array[i];
				if ((Object)(object)val == (Object)null || ((Object)(object)placementGhost != (Object)null && (Object)(object)((Component)val).transform != (Object)null && ((Component)val).transform.IsChildOf(placementGhost.transform)) || !TryGetPlantObject(((Component)val).gameObject, out var plantObject))
				{
					continue;
				}
				bool flag = false;
				for (int j = 0; j < list.Count; j++)
				{
					if (HorizontalSqrDistance(list[j].Position, plantObject.Position) < 0.0004f)
					{
						flag = true;
						break;
					}
				}
				if (!flag)
				{
					list.Add(plantObject);
				}
			}
			return list;
		}

		private static bool HasOverlappingPlants(Vector3 position, float growthSize, GameObject placementGhost)
		{
			//IL_001e: 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_0048: Unknown result type (might be due to invalid IL or missing references)
			float searchRadius = Mathf.Max(growthSize * 2f + PlantSpacing + 1f, 2f);
			foreach (PlantObject otherPlant in GetOtherPlants(position, searchRadius, placementGhost))
			{
				float num = growthSize + otherPlant.GrowthSize + PlantSpacing;
				if (HorizontalSqrDistance(position, otherPlant.Position) < num * num * 0.999f)
				{
					return true;
				}
			}
			return false;
		}

		private static bool TryGetPlantObject(GameObject gameObject, out PlantObject plantObject)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			plantObject = null;
			if ((Object)(object)gameObject == (Object)null)
			{
				return false;
			}
			Plant val = gameObject.GetComponentInParent<Plant>();
			if ((Object)(object)val == (Object)null)
			{
				val = gameObject.GetComponentInChildren<Plant>(true);
			}
			if ((Object)(object)val != (Object)null)
			{
				plantObject = new PlantObject(((Component)val).transform.position, Mathf.Max(0f, val.m_growRadius), ResolveGridKeys(gameObject, ((Component)val).gameObject));
				return true;
			}
			Piece val2 = gameObject.GetComponentInParent<Piece>();
			if ((Object)(object)val2 == (Object)null)
			{
				val2 = gameObject.GetComponentInChildren<Piece>(true);
			}
			Pickable val3 = gameObject.GetComponentInParent<Pickable>();
			if ((Object)(object)val3 == (Object)null)
			{
				val3 = gameObject.GetComponentInChildren<Pickable>(true);
			}
			ZNetView val4 = gameObject.GetComponentInParent<ZNetView>();
			if ((Object)(object)val4 == (Object)null)
			{
				val4 = gameObject.GetComponentInChildren<ZNetView>(true);
			}
			List<string> list = new List<string>(8);
			AddCandidateName(list, ((Object)gameObject).name);
			if ((Object)(object)val2 != (Object)null)
			{
				AddCandidateName(list, ((Object)((Component)val2).gameObject).name);
				AddCandidateName(list, ((Object)val2).name);
			}
			if ((Object)(object)val3 != (Object)null)
			{
				AddCandidateName(list, ((Object)((Component)val3).gameObject).name);
				AddCandidateName(list, ((Object)val3).name);
			}
			if ((Object)(object)val4 != (Object)null)
			{
				AddCandidateName(list, ((Object)((Component)val4).gameObject).name);
			}
			if (TryGetConfiguredSize(CustomPlants, list, out var size, out var matchedName))
			{
				Transform plantSourceTransform = GetPlantSourceTransform(gameObject, val2, val3, val4);
				plantObject = new PlantObject(plantSourceTransform.position, size, ResolveGridKeys(gameObject, ((Component)plantSourceTransform).gameObject, matchedName));
				return true;
			}
			for (int i = 0; i < list.Count; i++)
			{
				if (CultivatorPrefabNames.Contains(list[i]))
				{
					Transform plantSourceTransform2 = GetPlantSourceTransform(gameObject, val2, val3, val4);
					plantObject = new PlantObject(plantSourceTransform2.position, 0.5f, ResolveGridKeys(gameObject, ((Component)plantSourceTransform2).gameObject, list[i]));
					return true;
				}
			}
			return false;
		}

		private static Transform GetPlantSourceTransform(GameObject fallback, Piece piece, Pickable pickable, ZNetView znetView)
		{
			if ((Object)(object)piece != (Object)null)
			{
				return ((Component)piece).transform;
			}
			if ((Object)(object)pickable != (Object)null)
			{
				return ((Component)pickable).transform;
			}
			if ((Object)(object)znetView != (Object)null)
			{
				return ((Component)znetView).transform;
			}
			return fallback.transform;
		}

		private static IEnumerable<string> ResolveGridKeys(GameObject inspectedObject, GameObject preferredObject, string matchedName = null)
		{
			List<string> list = new List<string>(10);
			AddCandidateName(list, matchedName);
			AddObjectGridKeys(list, preferredObject);
			if ((Object)(object)inspectedObject != (Object)(object)preferredObject)
			{
				AddObjectGridKeys(list, inspectedObject);
			}
			return list;
		}

		private static void AddObjectGridKeys(List<string> keys, GameObject gameObject)
		{
			if (!((Object)(object)gameObject == (Object)null))
			{
				ZNetView val = gameObject.GetComponentInParent<ZNetView>();
				if ((Object)(object)val == (Object)null)
				{
					val = gameObject.GetComponentInChildren<ZNetView>(true);
				}
				if ((Object)(object)val != (Object)null)
				{
					AddCandidateName(keys, ((Object)((Component)val).gameObject).name);
				}
				Piece val2 = gameObject.GetComponentInParent<Piece>();
				if ((Object)(object)val2 == (Object)null)
				{
					val2 = gameObject.GetComponentInChildren<Piece>(true);
				}
				if ((Object)(object)val2 != (Object)null)
				{
					AddCandidateName(keys, ((Object)((Component)val2).gameObject).name);
				}
				Plant val3 = gameObject.GetComponentInParent<Plant>();
				if ((Object)(object)val3 == (Object)null)
				{
					val3 = gameObject.GetComponentInChildren<Plant>(true);
				}
				if ((Object)(object)val3 != (Object)null)
				{
					AddCandidateName(keys, ((Object)((Component)val3).gameObject).name);
				}
				Pickable val4 = gameObject.GetComponentInParent<Pickable>();
				if ((Object)(object)val4 == (Object)null)
				{
					val4 = gameObject.GetComponentInChildren<Pickable>(true);
				}
				if ((Object)(object)val4 != (Object)null)
				{
					AddCandidateName(keys, ((Object)((Component)val4).gameObject).name);
				}
				AddCandidateName(keys, ((Object)gameObject).name);
			}
		}

		private static bool LooksLikePlantPrefabName(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return false;
			}
			string text = name.ToLowerInvariant();
			for (int i = 0; i < PlantNameTokens.Length; i++)
			{
				if (text.Contains(PlantNameTokens[i]))
				{
					return true;
				}
			}
			return false;
		}

		private static bool TryGetConfiguredSize(Dictionary<string, float> table, List<string> names, out float size, out string matchedName)
		{
			for (int i = 0; i < names.Count; i++)
			{
				if (table.TryGetValue(names[i], out size))
				{
					matchedName = names[i];
					return true;
				}
			}
			size = 0f;
			matchedName = string.Empty;
			return false;
		}

		private static void AddCandidateName(List<string> names, string rawName)
		{
			string text = NormalizeObjectName(rawName);
			if (string.IsNullOrEmpty(text))
			{
				return;
			}
			for (int i = 0; i < names.Count; i++)
			{
				if (string.Equals(names[i], text, StringComparison.OrdinalIgnoreCase))
				{
					return;
				}
			}
			names.Add(text);
		}

		private static string NormalizeObjectName(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return string.Empty;
			}
			return name.Replace("(Clone)", string.Empty).Trim();
		}

		private static void EnsurePlantMask()
		{
			if (_plantObjectMask != 0)
			{
				return;
			}
			_plantObjectMask = LayerMask.GetMask(PlantObjectMasks.ToArray());
			_plantObjectMask |= _cultivatorPrefabLayerMask;
			if (_plantObjectMask == 0)
			{
				_plantObjectMask = -1;
				ManualLogSource log = FarmGridRemakePlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)"FarmGrid: no configured/cultivator plant layers were found; scanning all layers.");
				}
			}
		}

		private static Vector3 SetGroundHeight(Vector3 position)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZoneSystem.instance != (Object)null)
			{
				position.y = ZoneSystem.instance.GetGroundHeight(position);
			}
			return position;
		}

		private static Vector3 Flatten(Vector3 vector)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			vector.y = 0f;
			return vector;
		}

		private static float HorizontalSqrDistance(Vector3 a, Vector3 b)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			float num = a.x - b.x;
			float num2 = a.z - b.z;
			return num * num + num2 * num2;
		}

		private static void DrawGridSet(Vector3 position, Vector3 gridDir, float gridSize, Color color, int setIndex)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: 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_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			if (gridSize <= 0.001f)
			{
				return;
			}
			EnsureFarmGrid();
			if (_farmGrid == null)
			{
				return;
			}
			int num = GridSections * 2 + 1;
			int num2 = num * 2;
			int num3 = setIndex * num2;
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor(gridDir.z, 0f, 0f - gridDir.x);
			int positionCount = num;
			for (int i = -GridSections; i <= GridSections; i++)
			{
				int num4 = num3 + i + GridSections;
				LineRenderer component = _farmGrid[num4].GetComponent<LineRenderer>();
				((Renderer)component).enabled = true;
				component.positionCount = positionCount;
				component.startColor = color;
				component.endColor = color;
				Vector3 val2 = position + gridDir * ((float)i * gridSize);
				for (int j = -GridSections; j <= GridSections; j++)
				{
					Vector3 groundHeight = val2 + val * ((float)j * gridSize);
					groundHeight = SetGroundHeight(groundHeight);
					groundHeight.y += GridYOffset;
					component.SetPosition(j + GridSections, groundHeight);
				}
			}
			int num5 = num3 + num;
			for (int k = -GridSections; k <= GridSections; k++)
			{
				int num6 = num5 + k + GridSections;
				LineRenderer component2 = _farmGrid[num6].GetComponent<LineRenderer>();
				((Renderer)component2).enabled = true;
				component2.positionCount = positionCount;
				component2.startColor = color;
				component2.endColor = color;
				Vector3 val3 = position + val * ((float)k * gridSize);
				for (int l = -GridSections; l <= GridSections; l++)
				{
					Vector3 groundHeight2 = val3 + gridDir * ((float)l * gridSize);
					groundHeight2 = SetGroundHeight(groundHeight2);
					groundHeight2.y += GridYOffset;
					component2.SetPosition(l + GridSections, groundHeight2);
				}
			}
			_farmGridVisible = true;
		}

		private static void HideGridSet(int setIndex)
		{
			if (_farmGrid == null)
			{
				return;
			}
			int num = (GridSections * 2 + 1) * 2;
			int num2 = setIndex * num;
			int num3 = Mathf.Min(num2 + num, _farmGrid.Length);
			for (int i = num2; i < num3; i++)
			{
				GameObject val = _farmGrid[i];
				if (!((Object)(object)val == (Object)null))
				{
					LineRenderer component = val.GetComponent<LineRenderer>();
					if ((Object)(object)component != (Object)null)
					{
						((Renderer)component).enabled = false;
					}
				}
			}
		}

		private static void EnsureFarmGrid()
		{
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Expected O, but got Unknown
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Expected O, but got Unknown
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			int num = (GridSections * 2 + 1) * 2;
			int num2 = num * 4;
			if (_farmGrid != null && _farmGrid.Length == num2 && _farmGrid.Length != 0 && (Object)(object)_farmGrid[0] != (Object)null)
			{
				return;
			}
			DestroyFarmGrid();
			if ((Object)(object)_lineMaterial == (Object)null)
			{
				Material[] array = Resources.FindObjectsOfTypeAll<Material>();
				foreach (Material val in array)
				{
					if ((Object)(object)val != (Object)null && ((Object)val).name == "Default-Line")
					{
						_lineMaterial = val;
						break;
					}
				}
				if ((Object)(object)_lineMaterial == (Object)null)
				{
					Shader val2 = Shader.Find("Sprites/Default");
					if ((Object)(object)val2 != (Object)null)
					{
						_lineMaterial = new Material(val2);
					}
				}
			}
			_farmGrid = (GameObject[])(object)new GameObject[num2];
			for (int j = 0; j < num2; j++)
			{
				int num3 = j / num;
				GameObject val3 = new GameObject((num3 == 0) ? "FarmGrid_GreenLine" : "FarmGrid_RedLine");
				Object.DontDestroyOnLoad((Object)(object)val3);
				LineRenderer val4 = val3.AddComponent<LineRenderer>();
				val4.useWorldSpace = true;
				val4.widthMultiplier = 0.015f;
				val4.positionCount = GridSections * 2 + 1;
				Color endColor = (val4.startColor = ((num3 == 0) ? GridColor : SecondaryGridColor));
				val4.endColor = endColor;
				if ((Object)(object)_lineMaterial != (Object)null)
				{
					((Renderer)val4).material = _lineMaterial;
				}
				((Renderer)val4).enabled = false;
				_farmGrid[j] = val3;
			}
		}

		private static void ResetSnapPair()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: 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_007b: Unknown result type (might be due to invalid IL or missing references)
			_snapPairValid = false;
			_snapGhostInstanceId = 0;
			_snapAnchorPosition = Vector3.zero;
			_snapRawPosition = Vector3.zero;
			_greenSnapPoint = Vector3.zero;
			_redSnapPoint = Vector3.zero;
			_snapGridSize = 0f;
			_redFromGreenHalfX = 0;
			_redFromGreenHalfZ = 0;
			_lastSecondaryGridHeld = false;
			_hasLastPlacementYaw = false;
			_lastPlacementYaw = 0f;
			_stableGridDirectionValid = false;
			_stableGridDirection = Vector3.forward;
			_visualGridOriginValid = false;
			_visualGridOrigin = Vector3.zero;
		}

		internal static void HideFarmGrid()
		{
			ResetSnapPair();
			if (!_farmGridVisible || _farmGrid == null)
			{
				return;
			}
			GameObject[] farmGrid = _farmGrid;
			foreach (GameObject val in farmGrid)
			{
				if (!((Object)(object)val == (Object)null))
				{
					LineRenderer component = val.GetComponent<LineRenderer>();
					if ((Object)(object)component != (Object)null)
					{
						((Renderer)component).enabled = false;
					}
				}
			}
			_farmGridVisible = false;
		}

		internal static void DestroyFarmGrid()
		{
			if (_farmGrid != null)
			{
				GameObject[] farmGrid = _farmGrid;
				foreach (GameObject val in farmGrid)
				{
					if ((Object)(object)val != (Object)null)
					{
						Object.Destroy((Object)(object)val);
					}
				}
			}
			_farmGrid = null;
			_farmGridVisible = false;
			ResetSnapPair();
		}
	}
	internal static class PlantGrowthPatch
	{
		internal static bool RemoveGrowthRestrictions = true;

		internal static bool AllowPlantOverlap = true;

		private static readonly FieldInfo CultivatedGroundOnlyField = AccessTools.Field(typeof(Piece), "m_cultivatedGroundOnly");

		private static readonly FieldInfo NeedCultivatedGroundField = AccessTools.Field(typeof(Plant), "m_needCultivatedGround");

		private static readonly FieldInfo DestroyIfCantGrowField = AccessTools.Field(typeof(Plant), "m_destroyIfCantGrow");

		private static readonly Dictionary<int, bool> OriginalCultivatedGroundOnly = new Dictionary<int, bool>();

		internal static bool PlantOverlapEnabled
		{
			get
			{
				if (RemoveGrowthRestrictions)
				{
					return AllowPlantOverlap;
				}
				return false;
			}
		}

		internal static void ApplyPlacementFreedom(GameObject plantGhost)
		{
			if ((Object)(object)plantGhost == (Object)null || CultivatedGroundOnlyField == null)
			{
				return;
			}
			Piece val = plantGhost.GetComponent<Piece>();
			if ((Object)(object)val == (Object)null)
			{
				val = plantGhost.GetComponentInChildren<Piece>(true);
			}
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			int instanceID = ((Object)val).GetInstanceID();
			if (!OriginalCultivatedGroundOnly.TryGetValue(instanceID, out var value))
			{
				try
				{
					object value2 = CultivatedGroundOnlyField.GetValue(val);
					value = value2 is bool && (bool)value2;
					OriginalCultivatedGroundOnly[instanceID] = value;
				}
				catch
				{
					return;
				}
			}
			try
			{
				CultivatedGroundOnlyField.SetValue(val, !RemoveGrowthRestrictions && value);
			}
			catch
			{
			}
		}

		internal static void ApplyRuntimePlantFreedom(Plant plant)
		{
			if (!RemoveGrowthRestrictions || (Object)(object)plant == (Object)null)
			{
				return;
			}
			try
			{
				if (NeedCultivatedGroundField != null)
				{
					NeedCultivatedGroundField.SetValue(plant, false);
				}
				if (DestroyIfCantGrowField != null)
				{
					DestroyIfCantGrowField.SetValue(plant, false);
				}
			}
			catch
			{
			}
		}
	}
	[HarmonyPatch]
	internal static class PlantGrowthBoolChecksPatch
	{
		private static readonly HashSet<string> GrowthChecks = new HashSet<string> { "CanGrow", "HaveGrowSpace", "InValidBiome", "IsCultivated" };

		private static IEnumerable<MethodBase> TargetMethods()
		{
			MethodInfo[] methods = typeof(Plant).GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (methodInfo != null && !methodInfo.IsStatic && methodInfo.ReturnType == typeof(bool) && GrowthChecks.Contains(methodInfo.Name))
				{
					yield return methodInfo;
				}
			}
		}

		private static bool Prefix(Plant __instance, ref bool __result)
		{
			if (!PlantGrowthPatch.RemoveGrowthRestrictions)
			{
				return true;
			}
			PlantGrowthPatch.ApplyRuntimePlantFreedom(__instance);
			__result = true;
			return false;
		}
	}
}