Decompiled source of AdvancedPlanting v0.4.7

AdvancedPlanting.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using AdvancedPlanting.Core;
using AdvancedPlanting.UI;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.UI;
using Valheim.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("kepqa")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) 2026 kepqa")]
[assembly: AssemblyFileVersion("0.4.7.0")]
[assembly: AssemblyInformationalVersion("0.4.7+634f9821e72cc99f55d00900f66b35f1c375ee61")]
[assembly: AssemblyProduct("AdvancedPlanting")]
[assembly: AssemblyTitle("AdvancedPlanting")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.4.7.0")]
[module: UnverifiableCode]
namespace AdvancedPlanting
{
	public sealed class CurveRenderer
	{
		private static readonly string[] ShaderCandidates = new string[5] { "Sprites/Default", "Particles/Standard Unlit", "Legacy Shaders/Particles/Alpha Blended", "Hidden/Internal-Colored", "Standard" };

		private static readonly Color ValidRing = new Color(0.35f, 0.95f, 0.45f, 0.85f);

		private static readonly Color BlockedRing = new Color(1f, 0.3f, 0.25f, 0.9f);

		private static readonly Color PendingRing = new Color(0.85f, 0.85f, 0.8f, 0.6f);

		private static readonly Color SnapColor = new Color(0.4f, 0.85f, 1f, 1f);

		private const int RingSegments = 16;

		private readonly ModConfig _config;

		private readonly List<LineRenderer> _lines = new List<LineRenderer>();

		private readonly List<LineRenderer> _markers = new List<LineRenderer>();

		private readonly List<LineRenderer> _rings = new List<LineRenderer>();

		private readonly Vector3[] _ringBuffer = (Vector3[])(object)new Vector3[16];

		private Material _material;

		private GameObject _root;

		private bool _warnedShader;

		public CurveRenderer(ModConfig config)
		{
			_config = config;
		}

		public void Apply(List<List<Vector3>> rowLines, List<Vector3> controlPoints, int snapIndex, List<PlantSlot> slots, float growRadius, bool warning)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: 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_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: 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_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: 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_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_0275: Unknown result type (might be due to invalid IL or missing references)
			//IL_028e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0293: Unknown result type (might be due to invalid IL or missing references)
			//IL_0297: Unknown result type (might be due to invalid IL or missing references)
			//IL_0285: Unknown result type (might be due to invalid IL or missing references)
			//IL_029c: Unknown result type (might be due to invalid IL or missing references)
			//IL_029e: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0318: Unknown result type (might be due to invalid IL or missing references)
			//IL_031a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0323: Unknown result type (might be due to invalid IL or missing references)
			//IL_0328: Unknown result type (might be due to invalid IL or missing references)
			//IL_0331: Unknown result type (might be due to invalid IL or missing references)
			//IL_0336: Unknown result type (might be due to invalid IL or missing references)
			//IL_033d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0342: Unknown result type (might be due to invalid IL or missing references)
			//IL_0347: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_039e: Unknown result type (might be due to invalid IL or missing references)
			EnsureRoot();
			float value = _config.CurveWidth.Value;
			int num = (_config.ShowCurve.Value ? rowLines.Count : 0);
			Color val = (warning ? BlockedRing : _config.CurveColor.Value);
			for (int i = 0; i < num; i++)
			{
				LineRenderer obj = Get(_lines, i, "row", loop: false);
				List<Vector3> list = rowLines[i];
				obj.positionCount = list.Count;
				obj.SetPositions(list.ToArray());
				float startWidth = (obj.endWidth = value);
				obj.startWidth = startWidth;
				Color val2 = (Color)((i == 0) ? val : new Color(val.r, val.g, val.b, val.a * 0.55f));
				Color startColor = (obj.endColor = val2);
				obj.startColor = startColor;
				((Renderer)obj).enabled = true;
			}
			Disable(_lines, num);
			Color value2 = _config.ControlPointColor.Value;
			for (int j = 0; j < controlPoints.Count; j++)
			{
				LineRenderer obj2 = Get(_markers, j, "cp", loop: false);
				Vector3 val4 = controlPoints[j];
				bool flag = j == snapIndex;
				float num3 = (flag ? 2.2f : ((j == 0) ? 1.6f : 1.2f));
				obj2.positionCount = 2;
				obj2.SetPosition(0, val4 + Vector3.up * 0.05f);
				obj2.SetPosition(1, val4 + Vector3.up * num3);
				obj2.startWidth = value * (flag ? 4f : 2.5f);
				obj2.endWidth = value * 0.5f;
				Color startColor = (obj2.endColor = (flag ? SnapColor : value2));
				obj2.startColor = startColor;
				((Renderer)obj2).enabled = true;
			}
			Disable(_markers, controlPoints.Count);
			int num4 = 0;
			if (_config.ShowGrowRadius.Value && slots != null && growRadius > 0.05f)
			{
				foreach (PlantSlot slot in slots)
				{
					if (!slot.IsPlanted)
					{
						LineRenderer val6 = Get(_rings, num4++, "ring", loop: true);
						Vector3 normal = slot.Normal;
						Vector3 val7;
						if (!(((Vector3)(ref normal)).sqrMagnitude > 0.01f))
						{
							val7 = Vector3.up;
						}
						else
						{
							normal = slot.Normal;
							val7 = ((Vector3)(ref normal)).normalized;
						}
						Vector3 val8 = val7;
						Vector3 val9 = Vector3.Cross(val8, Vector3.forward);
						if (((Vector3)(ref val9)).sqrMagnitude < 0.01f)
						{
							val9 = Vector3.Cross(val8, Vector3.right);
						}
						((Vector3)(ref val9)).Normalize();
						Vector3 val10 = Vector3.Cross(val8, val9);
						Vector3 val11 = slot.Position + val8 * 0.1f;
						for (int k = 0; k < 16; k++)
						{
							float num5 = (float)k * (float)Math.PI * 2f / 16f;
							_ringBuffer[k] = val11 + (val9 * Mathf.Cos(num5) + val10 * Mathf.Sin(num5)) * growRadius;
						}
						val6.positionCount = 16;
						val6.SetPositions(_ringBuffer);
						float startWidth = (val6.endWidth = value * 0.6f);
						val6.startWidth = startWidth;
						Color startColor = (val6.endColor = (slot.IsValid ? ValidRing : (slot.IsPending ? PendingRing : BlockedRing)));
						val6.startColor = startColor;
						((Renderer)val6).enabled = true;
					}
				}
			}
			Disable(_rings, num4);
		}

		public void HideAll()
		{
			Disable(_lines, 0);
			Disable(_markers, 0);
			Disable(_rings, 0);
		}

		public void Destroy()
		{
			if ((Object)(object)_root != (Object)null)
			{
				Object.Destroy((Object)(object)_root);
			}
			_root = null;
			_lines.Clear();
			_markers.Clear();
			_rings.Clear();
		}

		private static void Disable(List<LineRenderer> pool, int from)
		{
			for (int i = from; i < pool.Count; i++)
			{
				if ((Object)(object)pool[i] != (Object)null && ((Renderer)pool[i]).enabled)
				{
					((Renderer)pool[i]).enabled = false;
				}
			}
		}

		private void EnsureRoot()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			if (!((Object)(object)_root != (Object)null))
			{
				_root = new GameObject("AdvancedPlanting_Overlay");
				Object.DontDestroyOnLoad((Object)(object)_root);
				_lines.Clear();
				_markers.Clear();
				_rings.Clear();
			}
		}

		private Material GetMaterial()
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			if ((Object)(object)_material != (Object)null)
			{
				return _material;
			}
			string[] shaderCandidates = ShaderCandidates;
			foreach (string text in shaderCandidates)
			{
				Shader val = Shader.Find(text);
				if (!((Object)(object)val == (Object)null))
				{
					_material = new Material(val);
					Plugin.Log.LogDebug((object)("Overlay shader: " + text));
					return _material;
				}
			}
			if (!_warnedShader)
			{
				_warnedShader = true;
				Plugin.Log.LogWarning((object)"No overlay shader found; lines may render incorrectly.");
			}
			return null;
		}

		private LineRenderer Get(List<LineRenderer> pool, int index, string prefix, bool loop)
		{
			//IL_001e: 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)
			while (pool.Count <= index)
			{
				GameObject val = new GameObject(prefix + "_" + pool.Count);
				val.transform.SetParent(_root.transform, false);
				LineRenderer val2 = val.AddComponent<LineRenderer>();
				val2.useWorldSpace = true;
				val2.loop = loop;
				((Renderer)val2).shadowCastingMode = (ShadowCastingMode)0;
				((Renderer)val2).receiveShadows = false;
				val2.alignment = (LineAlignment)0;
				val2.numCapVertices = ((!loop) ? 2 : 0);
				val2.numCornerVertices = 2;
				Material material = GetMaterial();
				if ((Object)(object)material != (Object)null)
				{
					((Renderer)val2).material = material;
				}
				pool.Add(val2);
			}
			return pool[index];
		}
	}
	public sealed class FigureState
	{
		public readonly List<Vector3> ControlPoints = new List<Vector3>();

		public readonly List<Vector3> Planted = new List<Vector3>();

		public PathMode Mode = (PathMode)3;

		public RowSide Side;

		public int Rows = 1;

		public float Jitter;

		public float Thinning;

		public uint Seed = 1u;

		public bool Fill;

		public bool Enabled = true;

		public bool AutoContinue;

		private readonly Dictionary<string, float> _spacingByPrefab = new Dictionary<string, float>();

		private readonly List<Vector3> _lastShape = new List<Vector3>();

		private PathMode _lastMode;

		public bool IsClosedPath
		{
			get
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Invalid comparison between Unknown and I4
				//IL_000a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0010: Invalid comparison between Unknown and I4
				if ((int)Mode != 2)
				{
					return (int)Mode == 4;
				}
				return true;
			}
		}

		public bool Finished { get; private set; }

		public bool Dirty { get; private set; } = true;

		public bool IsEmpty => ControlPoints.Count == 0;

		public bool HasEnoughPoints => ControlPoints.Count >= PathModeExtensions.MinControlPoints(Mode);

		public bool IsFixedSize => PathModeExtensions.MaxControlPoints(Mode) > 0;

		public bool CanFinish
		{
			get
			{
				if (!Finished && !IsFixedSize)
				{
					return HasEnoughPoints;
				}
				return false;
			}
		}

		public bool CanRepeat
		{
			get
			{
				if (!Finished)
				{
					if (IsEmpty)
					{
						return _lastShape.Count > 0;
					}
					return false;
				}
				return true;
			}
		}

		public void SetFill(bool fill)
		{
			if (fill != Fill)
			{
				Fill = fill;
				Dirty = true;
			}
		}

		public void MarkDirty()
		{
			Dirty = true;
		}

		public void ClearDirty()
		{
			Dirty = false;
		}

		public void AddPoint(Vector3 p)
		{
			//IL_000f: 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)
			if (!Finished)
			{
				ControlPoints.Add(p);
				int num = PathModeExtensions.MaxControlPoints(Mode);
				if (num > 0 && ControlPoints.Count >= num)
				{
					Finished = true;
				}
				Dirty = true;
			}
		}

		public void Finish()
		{
			if (CanFinish)
			{
				Finished = true;
				Dirty = true;
			}
		}

		public void RemoveLastPoint()
		{
			if (ControlPoints.Count != 0)
			{
				ControlPoints.RemoveAt(ControlPoints.Count - 1);
				Finished = false;
				Dirty = true;
			}
		}

		public void Clear()
		{
			if (Finished)
			{
				RememberShape();
			}
			ControlPoints.Clear();
			Planted.Clear();
			Finished = false;
			Dirty = true;
		}

		public void SetMode(PathMode mode)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			if (mode != Mode)
			{
				Mode = mode;
				int num = PathModeExtensions.MaxControlPoints(mode);
				if (num > 0 && ControlPoints.Count > num)
				{
					ControlPoints.RemoveRange(num, ControlPoints.Count - num);
				}
				Finished = num > 0 && ControlPoints.Count >= num;
				Dirty = true;
			}
		}

		public void SetSide(RowSide side)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			if (side != Side)
			{
				Side = side;
				Dirty = true;
			}
		}

		public void CycleSide()
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			SetSide((RowSide)((Side + 1) % 3));
		}

		public void RerollSeed()
		{
			Seed = Seed * 1664525 + 1013904223;
			if (Seed == 0)
			{
				Seed = 1u;
			}
			Dirty = true;
		}

		public void SetRows(int rows)
		{
			rows = Mathf.Clamp(rows, 1, 50);
			if (rows != Rows)
			{
				Rows = rows;
				Dirty = true;
			}
		}

		public void SetJitter(float value)
		{
			value = Mathf.Clamp01(Mathf.Round(value * 100f) / 100f);
			if (!Mathf.Approximately(value, Jitter))
			{
				Jitter = value;
				Dirty = true;
			}
		}

		public void SetThinning(float value)
		{
			value = Mathf.Clamp(Mathf.Round(value * 100f) / 100f, 0f, 0.9f);
			if (!Mathf.Approximately(value, Thinning))
			{
				Thinning = value;
				Dirty = true;
			}
		}

		public float GetSpacing(string prefabName, float fallback)
		{
			if (string.IsNullOrEmpty(prefabName))
			{
				return fallback;
			}
			if (_spacingByPrefab.TryGetValue(prefabName, out var value))
			{
				return value;
			}
			_spacingByPrefab[prefabName] = fallback;
			return fallback;
		}

		public void SetSpacing(string prefabName, float value, float minimum)
		{
			if (!string.IsNullOrEmpty(prefabName))
			{
				value = Mathf.Max(minimum, Mathf.Round(value * 20f) / 20f);
				if (!_spacingByPrefab.TryGetValue(prefabName, out var value2) || !Mathf.Approximately(value2, value))
				{
					_spacingByPrefab[prefabName] = value;
					Dirty = true;
				}
			}
		}

		private void RememberShape()
		{
			//IL_001e: 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)
			_lastShape.Clear();
			_lastShape.AddRange(ControlPoints);
			_lastMode = Mode;
		}

		public bool RepeatAt(Vector3 anchor)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: 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_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: 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)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: 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_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: 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)
			if (!CanRepeat)
			{
				return false;
			}
			if (Finished)
			{
				RememberShape();
			}
			Vector3 val = AnchorOf(_lastShape, _lastMode);
			Vector3 val2 = anchor - val;
			ControlPoints.Clear();
			foreach (Vector3 item in _lastShape)
			{
				ControlPoints.Add(item + val2);
			}
			Planted.Clear();
			Mode = _lastMode;
			Finished = true;
			Dirty = true;
			return true;
		}

		private static Vector3 AnchorOf(List<Vector3> points, PathMode mode)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0006: 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_002c: 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_002e: 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)
			//IL_0035: 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_0037: 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_0056: 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_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: 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)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			if ((int)mode == 2)
			{
				return points[0];
			}
			Vector3 val = points[0];
			Vector3 val2 = points[0];
			foreach (Vector3 point in points)
			{
				val = Vector3.Min(val, point);
				val2 = Vector3.Max(val2, point);
			}
			return new Vector3((val.x + val2.x) * 0.5f, points[0].y, (val.z + val2.z) * 0.5f);
		}

		public void MarkPlanted(Vector3 p)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			Planted.Add(p);
		}

		public bool IsPlanted(Vector3 p, float tolerance)
		{
			//IL_000f: 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_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			float num = tolerance * tolerance;
			for (int i = 0; i < Planted.Count; i++)
			{
				float num2 = Planted[i].x - p.x;
				float num3 = Planted[i].z - p.z;
				if (num2 * num2 + num3 * num3 <= num)
				{
					return true;
				}
			}
			return false;
		}
	}
	internal static class GameText
	{
		public static string Localize(string text)
		{
			if (string.IsNullOrEmpty(text) || text.IndexOf('$') < 0)
			{
				return text;
			}
			Localization instance = Localization.instance;
			if (instance == null)
			{
				return text;
			}
			return instance.Localize(text);
		}
	}
	public sealed class GhostPool
	{
		private static readonly int ColorId = Shader.PropertyToID("_Color");

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

		private readonly List<GameObject> _ghosts = new List<GameObject>();

		private readonly List<bool> _invalidState = new List<bool>();

		private string _sourceName;

		private GameObject _activeRoot;

		private GameObject _staging;

		private const int MaxClonesPerCall = 24;

		public bool Apply(GameObject source, List<PlantSlot> slots)
		{
			//IL_00d0: 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)
			if ((Object)(object)source == (Object)null)
			{
				HideAll();
				return true;
			}
			EnsureRoots();
			if (_sourceName != ((Object)source).name)
			{
				DestroyAll();
				EnsureRoots();
				_sourceName = ((Object)source).name;
			}
			int num = 24;
			while (_ghosts.Count < slots.Count && num-- > 0)
			{
				GameObject val = Clone(source);
				if ((Object)(object)val == (Object)null)
				{
					break;
				}
				_ghosts.Add(val);
				_invalidState.Add(item: false);
			}
			for (int i = 0; i < _ghosts.Count; i++)
			{
				GameObject val2 = _ghosts[i];
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				if (i >= slots.Count)
				{
					if (val2.activeSelf)
					{
						val2.SetActive(false);
					}
					continue;
				}
				PlantSlot plantSlot = slots[i];
				val2.transform.position = plantSlot.Position;
				val2.transform.rotation = plantSlot.Rotation;
				if (!val2.activeSelf)
				{
					val2.SetActive(true);
				}
				SetInvalid(i, plantSlot.IsBlocked);
			}
			return _ghosts.Count >= slots.Count;
		}

		public void HideAll()
		{
			foreach (GameObject ghost in _ghosts)
			{
				if ((Object)(object)ghost != (Object)null && ghost.activeSelf)
				{
					ghost.SetActive(false);
				}
			}
		}

		public void DestroyAll()
		{
			foreach (GameObject ghost in _ghosts)
			{
				if ((Object)(object)ghost != (Object)null)
				{
					Object.Destroy((Object)(object)ghost);
				}
			}
			_ghosts.Clear();
			_invalidState.Clear();
			_sourceName = null;
		}

		public void DestroyRoots()
		{
			DestroyAll();
			if ((Object)(object)_activeRoot != (Object)null)
			{
				Object.Destroy((Object)(object)_activeRoot);
			}
			if ((Object)(object)_staging != (Object)null)
			{
				Object.Destroy((Object)(object)_staging);
			}
			_activeRoot = null;
			_staging = null;
		}

		private void EnsureRoots()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			if ((Object)(object)_activeRoot == (Object)null)
			{
				_activeRoot = new GameObject("AdvancedPlanting_Ghosts");
				Object.DontDestroyOnLoad((Object)(object)_activeRoot);
			}
			if ((Object)(object)_staging == (Object)null)
			{
				_staging = new GameObject("AdvancedPlanting_GhostStaging");
				_staging.SetActive(false);
				Object.DontDestroyOnLoad((Object)(object)_staging);
			}
		}

		private void SetInvalid(int index, bool invalid)
		{
			//IL_0044: 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_0060: Unknown result type (might be due to invalid IL or missing references)
			if (_invalidState[index] == invalid)
			{
				return;
			}
			_invalidState[index] = invalid;
			MaterialMan instance = MaterialMan.instance;
			if (!((Object)(object)instance == (Object)null))
			{
				GameObject val = _ghosts[index];
				if (invalid)
				{
					instance.SetValue<Color>(val, ColorId, Color.red, false);
					instance.SetValue<Color>(val, EmissionId, Color.red * 0.7f, false);
				}
				else
				{
					instance.ResetValue(val, ColorId);
					instance.ResetValue(val, EmissionId);
				}
			}
		}

		private GameObject Clone(GameObject source)
		{
			GameObject val = Object.Instantiate<GameObject>(source, _staging.transform, false);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			StripScripts(val);
			Collider[] componentsInChildren = val.GetComponentsInChildren<Collider>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				componentsInChildren[i].enabled = false;
			}
			((Object)val).name = "AdvancedPlantingGhost_" + ((Object)source).name;
			val.SetActive(false);
			val.transform.SetParent(_activeRoot.transform, false);
			return val;
		}

		private static void StripScripts(GameObject go)
		{
			for (int i = 0; i < 4; i++)
			{
				MonoBehaviour[] componentsInChildren = go.GetComponentsInChildren<MonoBehaviour>(true);
				if (componentsInChildren.Length == 0)
				{
					break;
				}
				for (int num = componentsInChildren.Length - 1; num >= 0; num--)
				{
					if ((Object)(object)componentsInChildren[num] != (Object)null)
					{
						Object.DestroyImmediate((Object)(object)componentsInChildren[num]);
					}
				}
			}
		}
	}
	internal static class Harvest
	{
		[HarmonyPatch(typeof(Pickable), "Interact")]
		private static class Pickable_Interact_Patch
		{
			[HarmonyPrefix]
			private static void Prefix(Pickable __instance, out bool __state)
			{
				__state = !_running && __instance.CanBePicked();
			}

			[HarmonyPostfix]
			private static void Postfix(Pickable __instance, Humanoid character, bool repeat, bool __state)
			{
				if (!__state || repeat || _running)
				{
					return;
				}
				Player val = (Player)(object)((character is Player) ? character : null);
				if (val == null || (Object)(object)val != (Object)(object)Player.m_localPlayer || !Armed)
				{
					return;
				}
				_running = true;
				try
				{
					PickAround(__instance, val);
				}
				finally
				{
					_running = false;
				}
			}
		}

		[HarmonyPatch(typeof(Pickable), "GetHoverText")]
		private static class Pickable_GetHoverText_Patch
		{
			[HarmonyPostfix]
			private static void Postfix(ref string __result)
			{
				//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_003f: 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)
				ModConfig config = Config;
				if (!string.IsNullOrEmpty(__result) && config != null && config.HarvestOn && config.HarvestHint.Value)
				{
					string text = GameText.Localize("$KEY_Use");
					KeyCode value = config.HarvestModifier.Value;
					string text2 = (((int)value == 0) ? text : (KeyName(value) + " + " + text));
					__result = __result + "\n[<color=yellow><b>" + text2 + "</b></color>] " + Loc.T("harvest.hover", Mathf.RoundToInt(config.HarvestRadiusLimit));
				}
			}
		}

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

		private static readonly HashSet<Pickable> Seen = new HashSet<Pickable>();

		private static bool _running;

		private static ModConfig Config => Plugin.Instance?.Settings;

		private static bool Armed
		{
			get
			{
				//IL_0019: Unknown result type (might be due to invalid IL or missing references)
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				//IL_001f: 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)
				ModConfig config = Config;
				if (config == null || !config.HarvestOn)
				{
					return false;
				}
				KeyCode value = config.HarvestModifier.Value;
				if ((int)value != 0)
				{
					return ZInput.GetKey(value, false);
				}
				return true;
			}
		}

		private static void PickAround(Pickable target, Player player)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			ModConfig config = Config;
			float harvestRadiusLimit = config.HarvestRadiusLimit;
			Vector3 position = ((Component)target).transform.position;
			int num = Physics.OverlapSphereNonAlloc(position, harvestRadiusLimit, Hits, player.m_interactMask, (QueryTriggerInteraction)2);
			Seen.Clear();
			Seen.Add(target);
			int num2 = 0;
			for (int i = 0; i < num; i++)
			{
				Pickable val = (((Object)(object)Hits[i] != (Object)null) ? ((Component)Hits[i]).GetComponentInParent<Pickable>() : null);
				if (!((Object)(object)val == (Object)null) && Seen.Add(val) && val.CanBePicked() && (!config.HarvestSameType.Value || !((Object)(object)val.m_itemPrefab != (Object)(object)target.m_itemPrefab)))
				{
					Vector3 val2 = ((Component)val).transform.position - position;
					if (!(((Vector3)(ref val2)).sqrMagnitude > harvestRadiusLimit * harvestRadiusLimit))
					{
						val.Interact((Humanoid)(object)player, false, false);
						num2++;
					}
				}
			}
			Seen.Clear();
			if (num2 > 0)
			{
				Plugin.Log.LogDebug((object)$"Area harvest: {num2 + 1} picked within {harvestRadiusLimit} m.");
			}
		}

		private unsafe static string KeyName(KeyCode key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected I4, but got Unknown
			switch (key - 303)
			{
			case 0:
			case 1:
				return "Shift";
			case 2:
			case 3:
				return "Ctrl";
			case 4:
			case 5:
				return "Alt";
			default:
				return ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString();
			}
		}
	}
	public struct PlantSlot
	{
		public Vector3 Position;

		public Vector3 Normal;

		public Quaternion Rotation;

		public PointStatus Status;

		public int Row;

		public bool IsValid => Status == PointStatus.Valid;

		public bool IsPlanted => Status == PointStatus.Planted;

		public bool IsPending => Status == PointStatus.Pending;

		public bool IsBlocked
		{
			get
			{
				if (!IsValid && !IsPlanted)
				{
					return !IsPending;
				}
				return false;
			}
		}
	}
	public sealed class LayoutService
	{
		private const float PlantedTolerance = 0.2f;

		private const float RowLineStep = 1f;

		private readonly ModConfig _config;

		private readonly int[] _statusCounts = new int[32];

		private int _validateCursor;

		private bool _passComplete = true;

		public readonly List<PlantSlot> Slots = new List<PlantSlot>();

		public readonly List<List<Vector3>> RowLines = new List<List<Vector3>>();

		public readonly List<Vector3> PreviewPoints = new List<Vector3>();

		public bool Truncated { get; private set; }

		public bool TooLarge { get; private set; }

		public float Extent { get; private set; }

		public int ValidCount { get; private set; }

		public int PlantedCount { get; private set; }

		public int PendingCount { get; private set; }

		public int BlockedCount => Slots.Count - ValidCount - PlantedCount - PendingCount;

		public PointStatus TopBlockReason { get; private set; }

		public int Version { get; private set; }

		public bool FullyValidated => PendingCount == 0;

		public bool PassComplete => _passComplete;

		public string BuiltForPrefab { get; private set; }

		public LayoutService(ModConfig config)
		{
			_config = config;
		}

		public float DefaultSpacing(Plant plant)
		{
			float num = (((Object)(object)plant != (Object)null) ? plant.m_growRadius : 1f);
			if (num <= 0.05f)
			{
				num = 0.5f;
			}
			return Mathf.Round(num * _config.DefaultSpacingFactor.Value * 100f) / 100f;
		}

		public float MinDistance(Plant plant)
		{
			float num = (((Object)(object)plant != (Object)null) ? plant.m_growRadius : 1f);
			if (num <= 0.05f)
			{
				num = 0.5f;
			}
			return num + _config.MinDistanceMargin.Value;
		}

		public void Clear()
		{
			Slots.Clear();
			RowLines.Clear();
			PreviewPoints.Clear();
			Truncated = false;
			TooLarge = false;
			Extent = 0f;
			int num = (PendingCount = 0);
			int validCount = (PlantedCount = num);
			ValidCount = validCount;
			TopBlockReason = PointStatus.Valid;
			BuiltForPrefab = null;
			_validateCursor = 0;
			_passComplete = true;
			Version++;
		}

		public void Rebuild(FigureState state, Vector3? cursor, Plant plant, string prefabName)
		{
			//IL_0020: 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_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: 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_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Expected O, but got Unknown
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_030e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0313: Unknown result type (might be due to invalid IL or missing references)
			//IL_031f: Unknown result type (might be due to invalid IL or missing references)
			//IL_032d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0332: Unknown result type (might be due to invalid IL or missing references)
			//IL_0344: Unknown result type (might be due to invalid IL or missing references)
			//IL_0346: Unknown result type (might be due to invalid IL or missing references)
			//IL_0350: Unknown result type (might be due to invalid IL or missing references)
			//IL_0352: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: Unknown result type (might be due to invalid IL or missing references)
			//IL_026e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0272: Unknown result type (might be due to invalid IL or missing references)
			//IL_0274: Unknown result type (might be due to invalid IL or missing references)
			//IL_027e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0283: Unknown result type (might be due to invalid IL or missing references)
			//IL_039a: 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_03a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0369: Unknown result type (might be due to invalid IL or missing references)
			//IL_036b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0377: Unknown result type (might be due to invalid IL or missing references)
			//IL_0379: Unknown result type (might be due to invalid IL or missing references)
			//IL_0383: Unknown result type (might be due to invalid IL or missing references)
			//IL_0388: Unknown result type (might be due to invalid IL or missing references)
			//IL_038f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0391: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bd: Unknown result type (might be due to invalid IL or missing references)
			Clear();
			BuiltForPrefab = prefabName;
			PreviewPoints.AddRange(state.ControlPoints);
			int num = PathModeExtensions.MaxControlPoints(state.Mode);
			if (!state.Finished && cursor.HasValue && (num == 0 || PreviewPoints.Count < num))
			{
				PreviewPoints.Add(cursor.Value);
			}
			if (PreviewPoints.Count < PathModeExtensions.MinControlPoints(state.Mode))
			{
				if (PreviewPoints.Count < 2)
				{
					return;
				}
				List<Vector3> list = new List<Vector3>();
				foreach (Vector3 previewPoint in PreviewPoints)
				{
					list.Add(previewPoint + Vector3.up * 0.15f);
				}
				RowLines.Add(list);
				return;
			}
			float spacing = state.GetSpacing(prefabName, DefaultSpacing(plant));
			LayoutRequest val = new LayoutRequest
			{
				Mode = state.Mode,
				Spacing = spacing,
				RowSpacing = spacing,
				Rows = state.Rows,
				Fill = (state.Fill && state.IsClosedPath),
				Side = state.Side,
				Jitter = state.Jitter,
				Thinning = state.Thinning,
				Seed = state.Seed,
				MinDistance = MinDistance(plant),
				WarpScale = _config.WarpScale.Value,
				MaxPoints = _config.PlantLimit,
				Resolution = Mathf.Clamp(spacing / 4f, 0.1f, 0.5f),
				MaxExtent = _config.FigureSizeLimit
			};
			foreach (Vector3 previewPoint2 in PreviewPoints)
			{
				val.ControlPoints.Add(new Vec2(previewPoint2.x, previewPoint2.z));
			}
			LayoutResult val2 = PlantingLayout.Generate(val);
			Truncated = val2.Truncated;
			TooLarge = val2.TooLarge;
			Extent = val2.Extent;
			if (TooLarge)
			{
				List<Vector3> list2 = new List<Vector3>();
				foreach (Vector3 previewPoint3 in PreviewPoints)
				{
					list2.Add(previewPoint3 + Vector3.up * 0.15f);
				}
				if (state.IsClosedPath && list2.Count > 2)
				{
					list2.Add(list2[0]);
				}
				RowLines.Add(list2);
				Version++;
				return;
			}
			float y = PreviewPoints[0].y;
			foreach (LayoutPoint point in val2.Points)
			{
				PlantSlot item = new PlantSlot
				{
					Row = point.Row,
					Normal = Vector3.up,
					Status = PointStatus.Pending
				};
				if (PlacementValidator.TryGetGround(point.Position.X, point.Position.Z, out var height, out var normal))
				{
					item.Position = new Vector3(point.Position.X, height, point.Position.Z);
					item.Normal = normal;
				}
				else
				{
					item.Position = new Vector3(point.Position.X, y, point.Position.Z);
					item.Status = PointStatus.NoGround;
				}
				if (state.IsPlanted(item.Position, 0.2f))
				{
					item.Status = PointStatus.Planted;
				}
				item.Rotation = RotationFor(item.Position, state.Seed);
				Slots.Add(item);
			}
			BuildRowLines(val2, y);
			_validateCursor = 0;
			_passComplete = false;
			CountStatuses();
		}

		public bool ValidateStep(FigureState state, Piece piece, Plant plant, Player player, int budget)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			if (_passComplete || Slots.Count == 0)
			{
				_passComplete = true;
				return false;
			}
			bool flag = false;
			int num = Mathf.Min(Slots.Count, _validateCursor + budget);
			while (_validateCursor < num)
			{
				PlantSlot value = Slots[_validateCursor];
				if (value.Status != PointStatus.NoGround && value.Status != PointStatus.Planted)
				{
					PointStatus pointStatus = (state.IsPlanted(value.Position, 0.2f) ? PointStatus.Planted : PlacementValidator.Check(value.Position, piece, plant, player));
					if (pointStatus != value.Status)
					{
						value.Status = pointStatus;
						Slots[_validateCursor] = value;
						flag = true;
					}
				}
				_validateCursor++;
			}
			if (_validateCursor >= Slots.Count)
			{
				_passComplete = true;
			}
			if (flag || _passComplete)
			{
				CountStatuses();
			}
			return _passComplete;
		}

		public void BeginRevalidation()
		{
			if (Slots.Count != 0)
			{
				_validateCursor = 0;
				_passComplete = false;
			}
		}

		public void ValidateAll(FigureState state, Piece piece, Plant plant, Player player)
		{
			if (_passComplete && PendingCount == 0)
			{
				BeginRevalidation();
			}
			ValidateStep(state, piece, plant, player, int.MaxValue);
		}

		public void RefreshPlanted(FigureState state)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			bool flag = false;
			for (int i = 0; i < Slots.Count; i++)
			{
				PlantSlot value = Slots[i];
				if (!value.IsPlanted && state.IsPlanted(value.Position, 0.2f))
				{
					value.Status = PointStatus.Planted;
					Slots[i] = value;
					flag = true;
				}
			}
			if (flag)
			{
				CountStatuses();
			}
		}

		private void CountStatuses()
		{
			Array.Clear(_statusCounts, 0, _statusCounts.Length);
			int num = (PendingCount = 0);
			int validCount = (PlantedCount = num);
			ValidCount = validCount;
			foreach (PlantSlot slot in Slots)
			{
				if (slot.IsValid)
				{
					ValidCount++;
				}
				else if (slot.IsPlanted)
				{
					PlantedCount++;
				}
				else if (slot.IsPending)
				{
					PendingCount++;
				}
				else
				{
					_statusCounts[(int)slot.Status]++;
				}
			}
			int num4 = 0;
			TopBlockReason = PointStatus.Valid;
			for (int i = 1; i < _statusCounts.Length; i++)
			{
				if (_statusCounts[i] > num4)
				{
					num4 = _statusCounts[i];
					TopBlockReason = (PointStatus)i;
				}
			}
			Version++;
		}

		private Quaternion RotationFor(Vector3 pos, uint seed)
		{
			//IL_0018: 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_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			if (!_config.RandomRotation.Value)
			{
				return Quaternion.identity;
			}
			float num = Noise.Float01((Mathf.RoundToInt(pos.x * 20f) * 73856093) ^ (Mathf.RoundToInt(pos.z * 20f) * 19349663), seed + 625341585) * 360f;
			return Quaternion.Euler(0f, num, 0f);
		}

		private void BuildRowLines(LayoutResult layout, float fallbackY)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: 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_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			foreach (Polyline rowCurf in layout.RowCurves)
			{
				if (!rowCurf.IsDegenerate)
				{
					int num = Mathf.Max(2, Mathf.CeilToInt(rowCurf.Length / 1f));
					List<Vector3> list = new List<Vector3>(num + 1);
					for (int i = 0; i <= num; i++)
					{
						Vec2 val = rowCurf.PointAt(rowCurf.Length * (float)i / (float)num);
						float height;
						float num2 = (PlacementValidator.TryGetGroundHeight(val.X, val.Z, out height) ? height : fallbackY);
						list.Add(new Vector3(val.X, num2 + 0.15f, val.Z));
					}
					RowLines.Add(list);
				}
			}
		}
	}
	public static class Loc
	{
		private const string Fallback = "English";

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

		private static string _language = "English";

		private static Dictionary<string, string> _current;

		private static Dictionary<string, string> _fallback;

		private static float _nextLanguageCheck;

		private static bool _loaded;

		public static string Language
		{
			get
			{
				Refresh();
				return _language;
			}
		}

		public static string T(string key, params object[] args)
		{
			Refresh();
			string value = null;
			if (_current != null)
			{
				_current.TryGetValue(key, out value);
			}
			if (value == null && _fallback != null)
			{
				_fallback.TryGetValue(key, out value);
			}
			if (value == null)
			{
				value = key;
			}
			if (args == null || args.Length == 0)
			{
				return value;
			}
			try
			{
				return string.Format(value, args);
			}
			catch (FormatException)
			{
				return value;
			}
		}

		public static void Load()
		{
			Languages.Clear();
			Assembly assembly = typeof(Loc).Assembly;
			string[] manifestResourceNames = assembly.GetManifestResourceNames();
			foreach (string text in manifestResourceNames)
			{
				if (!text.StartsWith("AdvancedPlanting.Translations.") || !text.EndsWith(".txt"))
				{
					continue;
				}
				string language = text.Substring("AdvancedPlanting.Translations.".Length, text.Length - "AdvancedPlanting.Translations.".Length - 4);
				using Stream stream = assembly.GetManifestResourceStream(text);
				using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8);
				Merge(language, streamReader.ReadToEnd(), "embedded");
			}
			try
			{
				string path = Path.Combine(Path.GetDirectoryName(assembly.Location) ?? ".", "Translations");
				if (Directory.Exists(path))
				{
					manifestResourceNames = Directory.GetFiles(path, "*.txt");
					foreach (string text2 in manifestResourceNames)
					{
						Merge(Path.GetFileNameWithoutExtension(text2), File.ReadAllText(text2, Encoding.UTF8), text2);
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Could not read translation files: " + ex.Message));
				}
			}
			_loaded = true;
			_fallback = (Languages.TryGetValue("English", out var value) ? value : null);
			_nextLanguageCheck = 0f;
			ApplyLanguage(DetectLanguage());
		}

		private static void Merge(string language, string content, string source)
		{
			if (!Languages.TryGetValue(language, out var value))
			{
				value = new Dictionary<string, string>();
				Languages[language] = value;
			}
			int num = 0;
			string[] array = content.Split(new char[1] { '\n' });
			foreach (string obj in array)
			{
				num++;
				string text = obj.TrimEnd(new char[1] { '\r' });
				string text2 = text.TrimStart(Array.Empty<char>());
				if (text2.Length == 0 || text2.StartsWith("#"))
				{
					continue;
				}
				int num2 = text.IndexOf('=');
				if (num2 <= 0)
				{
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogWarning((object)$"{source}:{num}: expected 'key = value'");
					}
				}
				else
				{
					string key = text.Substring(0, num2).Trim();
					string value2 = text.Substring(num2 + 1).Trim().Replace("\\n", "\n");
					value[key] = value2;
				}
			}
		}

		private static void Refresh()
		{
			if (!_loaded)
			{
				Load();
			}
			if (!(Time.unscaledTime < _nextLanguageCheck))
			{
				_nextLanguageCheck = Time.unscaledTime + 2f;
				string text = DetectLanguage();
				if (!string.Equals(text, _language, StringComparison.OrdinalIgnoreCase))
				{
					ApplyLanguage(text);
				}
			}
		}

		private static void ApplyLanguage(string lang)
		{
			_language = (string.IsNullOrEmpty(lang) ? "English" : lang);
			_current = (Languages.TryGetValue(_language, out var value) ? value : _fallback);
		}

		private static string DetectLanguage()
		{
			try
			{
				Localization instance = Localization.instance;
				string text = ((instance != null) ? instance.GetSelectedLanguage() : null);
				if (!string.IsNullOrEmpty(text))
				{
					return text;
				}
			}
			catch (Exception)
			{
			}
			return PlayerPrefs.GetString("language", "English");
		}
	}
	public sealed class ModConfig
	{
		public ConfigEntry<KeyCode> WheelKey;

		public ConfigEntry<KeyCode> FinishKey;

		public ConfigEntry<KeyCode> FillKey;

		public ConfigEntry<KeyCode> ClearKey;

		public ConfigEntry<KeyCode> RepeatKey;

		public ConfigEntry<bool> RowsOnMouseWheel;

		public ConfigEntry<bool> EnabledByDefault;

		public ConfigEntry<float> DefaultSpacingFactor;

		public ConfigEntry<float> MinDistanceMargin;

		public ConfigEntry<int> MaxPlants;

		public ConfigEntry<float> StaminaCostFactor;

		public ConfigEntry<float> MaxFigureSize;

		public ConfigEntry<bool> RandomRotation;

		public ConfigEntry<float> WarpScale;

		public ConfigEntry<float> RevalidateInterval;

		public ConfigEntry<float> SnapRadius;

		public ConfigEntry<float> AutoContinueInterval;

		public ConfigEntry<bool> HarvestEnabled;

		public ConfigEntry<KeyCode> HarvestModifier;

		public ConfigEntry<float> HarvestRadius;

		public ConfigEntry<bool> HarvestSameType;

		public ConfigEntry<bool> HarvestHint;

		public ConfigEntry<bool> EnforceOnClients;

		public readonly List<ConfigEntryBase> Synced = new List<ConfigEntryBase>();

		public ConfigEntry<bool> ShowHud;

		public ConfigEntry<int> HudX;

		public ConfigEntry<int> HudY;

		public ConfigEntry<bool> ShowCurve;

		public ConfigEntry<bool> ShowGrowRadius;

		public ConfigEntry<float> CurveWidth;

		public ConfigEntry<Color> CurveColor;

		public ConfigEntry<Color> ControlPointColor;

		public ConfigFile File { get; }

		public float StaminaFactor => ServerConfig.Get<float>(StaminaCostFactor);

		public int PlantLimit => ServerConfig.Get<int>(MaxPlants);

		public float FigureSizeLimit => ServerConfig.Get<float>(MaxFigureSize);

		public bool HarvestOn => ServerConfig.Get<bool>(HarvestEnabled);

		public float HarvestRadiusLimit => ServerConfig.Get<float>(HarvestRadius);

		public ModConfig(ConfigFile file)
		{
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Expected O, but got Unknown
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Expected O, but got Unknown
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Expected O, but got Unknown
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Expected O, but got Unknown
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Expected O, but got Unknown
			//IL_0240: Unknown result type (might be due to invalid IL or missing references)
			//IL_024a: Expected O, but got Unknown
			//IL_0299: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a3: Expected O, but got Unknown
			//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dc: Expected O, but got Unknown
			//IL_0347: Unknown result type (might be due to invalid IL or missing references)
			//IL_0351: Expected O, but got Unknown
			//IL_04d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0504: Unknown result type (might be due to invalid IL or missing references)
			File = file;
			WheelKey = file.Bind<KeyCode>("0. Keys", "PlantingWheel", (KeyCode)98, "Opens the planting wheel while a seed is selected in build mode. Press again or hold and release to choose. B is unused by the game.");
			FinishKey = file.Bind<KeyCode>("0. Keys", "FinishFigure", (KeyCode)122, "Finishes a curve or closes a shape (same as clicking its last or first point). None to disable.");
			FillKey = file.Bind<KeyCode>("0. Keys", "ToggleFill", (KeyCode)103, "Turns filling of circles and shapes on or off. The radial-menu key does nothing in build mode, so G is free there. None to disable.");
			ClearKey = file.Bind<KeyCode>("0. Keys", "ClearFigure", (KeyCode)8, "Removes the whole figure. None to disable.");
			RepeatKey = file.Bind<KeyCode>("0. Keys", "RepeatFigure", (KeyCode)121, "Moves the finished figure under the cursor, or places the last figure again once it is planted or cleared. None to disable.");
			RowsOnMouseWheel = file.Bind<bool>("0. Keys", "RowsOnMouseWheel", true, "In figure mode the mouse wheel changes the number of rows instead of rotating (plants get a random rotation anyway).");
			EnabledByDefault = file.Bind<bool>("1. Behaviour", "EnabledByDefault", true, "Start in figure mode. Switch to ordinary planting from the planting wheel.");
			DefaultSpacingFactor = file.Bind<float>("1. Behaviour", "DefaultSpacingFactor", 2f, new ConfigDescription("Initial spacing = plant grow radius × this factor.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 6f), Array.Empty<object>()));
			MinDistanceMargin = file.Bind<float>("1. Behaviour", "MinDistanceMargin", 0.35f, new ConfigDescription("Minimum distance between plants = grow radius + this margin. Jitter never goes below it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
			MaxPlants = file.Bind<int>("1. Behaviour", "MaxPlants", 500, new ConfigDescription("Hard cap on plants per figure, protects frame rate and the server.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 2000), Array.Empty<object>()));
			StaminaCostFactor = file.Bind<float>("1. Behaviour", "StaminaCostFactor", 0.5f, new ConfigDescription("Stamina per plant in figure mode = vanilla cost × this factor. 1 is vanilla, 0.5 is half, 0 is free. Single planting is not affected.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
			MaxFigureSize = file.Bind<float>("1. Behaviour", "MaxFigureSize", 80f, new ConfigDescription("Largest figure in meters (longer side; diameter for a circle). Bigger figures are not previewed or planted, so a stray click far away cannot freeze the game.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 500f), Array.Empty<object>()));
			RandomRotation = file.Bind<bool>("1. Behaviour", "RandomRotation", true, "Give every plant a random yaw instead of the cursor rotation.");
			WarpScale = file.Bind<float>("1. Behaviour", "WarpScale", 6f, new ConfigDescription("Size in meters of the slow waves that bend whole rows when jitter is on.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 30f), Array.Empty<object>()));
			RevalidateInterval = file.Bind<float>("1. Behaviour", "RevalidateInterval", 0.5f, "Seconds between re-checks of ground validity for the preview.");
			SnapRadius = file.Bind<float>("1. Behaviour", "SnapRadius", 0.8f, new ConfigDescription("Click this close to the last point to finish a curve, or to the first point to close a shape.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 3f), Array.Empty<object>()));
			AutoContinueInterval = file.Bind<float>("1. Behaviour", "AutoContinueInterval", 0.6f, new ConfigDescription("Seconds between plants when auto-continue is on.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 5f), Array.Empty<object>()));
			HarvestEnabled = file.Bind<bool>("3. Harvest", "Enabled", true, "Hold the modifier and pick a ripe crop to also pick every crop of the same kind around it.");
			HarvestModifier = file.Bind<KeyCode>("3. Harvest", "Modifier", (KeyCode)304, "Key to hold while picking. None picks the whole area with every pick.");
			HarvestRadius = file.Bind<float>("3. Harvest", "Radius", 5f, new ConfigDescription("Radius in meters around the picked crop.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 15f), Array.Empty<object>()));
			HarvestSameType = file.Bind<bool>("3. Harvest", "SameTypeOnly", true, "Only crops of the kind you picked. Off also picks berries, mushrooms, flint, branches and anything else pickable in range.");
			HarvestHint = file.Bind<bool>("3. Harvest", "ShowHint", true, "Show the area-harvest key under the game's pick-up hint.");
			EnforceOnClients = file.Bind<bool>("4. Server", "EnforceOnClients", true, "Only matters on a server or the host. Players who have this mod use the server's StaminaCostFactor, MaxPlants, MaxFigureSize, harvest Enabled and harvest Radius instead of their own.");
			Synced.Add((ConfigEntryBase)(object)StaminaCostFactor);
			Synced.Add((ConfigEntryBase)(object)MaxPlants);
			Synced.Add((ConfigEntryBase)(object)MaxFigureSize);
			Synced.Add((ConfigEntryBase)(object)HarvestEnabled);
			Synced.Add((ConfigEntryBase)(object)HarvestRadius);
			ShowHud = file.Bind<bool>("2. Display", "ShowHud", true, "Show the status panel while a seed is selected in figure mode.");
			HudX = file.Bind<int>("2. Display", "HudX", 20, "Panel X position in pixels from the left.");
			HudY = file.Bind<int>("2. Display", "HudY", 200, "Panel Y position in pixels from the top.");
			ShowCurve = file.Bind<bool>("2. Display", "ShowCurve", true, "Draw the guide path and rows as lines.");
			ShowGrowRadius = file.Bind<bool>("2. Display", "ShowGrowRadius", true, "Draw each plant's grow radius as a ring on the ground.");
			CurveWidth = file.Bind<float>("2. Display", "CurveWidth", 0.08f, "Line width in meters.");
			CurveColor = file.Bind<Color>("2. Display", "CurveColor", new Color(0.3f, 0.9f, 0.4f, 0.8f), "Row line colour.");
			ControlPointColor = file.Bind<Color>("2. Display", "ControlPointColor", new Color(1f, 0.85f, 0.2f, 0.9f), "Control point marker colour.");
		}
	}
	public enum PointStatus
	{
		Valid,
		NoGround,
		NoBuildZone,
		PrivateZone,
		NeedCultivated,
		NeedDirt,
		WrongBiome,
		TooHot,
		TooCold,
		DeepSnow,
		InWater,
		NoSpace,
		Planted,
		Pending
	}
	public static class PlacementValidator
	{
		private static int s_spaceMask;

		private static int SpaceMask
		{
			get
			{
				if (s_spaceMask == 0)
				{
					s_spaceMask = LayerMask.GetMask(new string[5] { "Default", "static_solid", "Default_small", "piece", "piece_nonsolid" });
				}
				return s_spaceMask;
			}
		}

		public static bool TryGetGroundHeight(float x, float z, out float height)
		{
			Vector3 normal;
			return TryGetGround(x, z, out height, out normal);
		}

		public static bool TryGetGround(float x, float z, out float height, out Vector3 normal)
		{
			//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_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_004d: 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)
			height = 0f;
			normal = Vector3.up;
			ZoneSystem instance = ZoneSystem.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			RaycastHit val = default(RaycastHit);
			if (!Physics.Raycast(new Vector3(x, 6000f, z), Vector3.down, ref val, 10000f, instance.m_terrainRayMask))
			{
				return false;
			}
			height = ((RaycastHit)(ref val)).point.y;
			normal = ((RaycastHit)(ref val)).normal;
			return true;
		}

		public static PointStatus Check(Vector3 pos, Piece piece, Plant plant, Player player)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: 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_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Invalid comparison between Unknown and I4
			//IL_0110: 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_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Invalid comparison between Unknown and I4
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Invalid comparison between Unknown and I4
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Invalid comparison between Unknown and I4
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Invalid comparison between Unknown and I4
			if (Location.IsInsideNoBuildLocation(pos))
			{
				return PointStatus.NoBuildZone;
			}
			if (!PrivateArea.CheckAccess(pos, 0f, false, false))
			{
				return PointStatus.PrivateZone;
			}
			Heightmap val = Heightmap.FindHeightmap(pos);
			if ((Object)(object)val == (Object)null && (piece.m_groundOnly || piece.m_cultivatedGroundOnly || piece.m_vegetationGroundOnly))
			{
				return PointStatus.NoGround;
			}
			ZoneSystem instance = ZoneSystem.instance;
			if (piece.m_noInWater && (Object)(object)instance != (Object)null && pos.y < instance.m_waterLevel)
			{
				return PointStatus.InWater;
			}
			if ((piece.m_cultivatedGroundOnly || ((Object)(object)plant != (Object)null && plant.m_needCultivatedGround)) && ((Object)(object)val == (Object)null || !val.IsCultivated(pos)))
			{
				return PointStatus.NeedCultivated;
			}
			Biome val2 = (((Object)(object)val != (Object)null) ? val.GetBiome(pos, 0.02f, false) : Heightmap.FindBiome(pos));
			if (piece.m_vegetationGroundOnly && (Object)(object)val != (Object)null)
			{
				float vegetationMask = val.GetVegetationMask(pos);
				if (((int)val2 == 32) ? (vegetationMask > 0.1f) : (vegetationMask < 0.25f))
				{
					return PointStatus.NeedDirt;
				}
			}
			if ((int)piece.m_onlyInBiome != 0 && (val2 & piece.m_onlyInBiome) == 0)
			{
				return PointStatus.WrongBiome;
			}
			if ((Object)(object)plant != (Object)null)
			{
				if ((val2 & plant.m_biome) == 0)
				{
					return PointStatus.WrongBiome;
				}
				if (!plant.m_tolerateHeat && (int)val2 == 32 && !ShieldGenerator.IsInsideShield(pos))
				{
					return PointStatus.TooHot;
				}
				if (!plant.m_tolerateCold && ((int)val2 == 64 || (int)val2 == 4) && !ShieldGenerator.IsInsideShield(pos))
				{
					return PointStatus.TooCold;
				}
			}
			if (!piece.m_allowedInDeepSnow && (int)val2 == 64 && (Object)(object)val != (Object)null && (Object)(object)player != (Object)null && val.GetCultivationMask(pos) > player.m_deepSnowBuildHeight)
			{
				return PointStatus.DeepSnow;
			}
			if ((Object)(object)plant != (Object)null && !HaveGrowSpace(pos, plant))
			{
				return PointStatus.NoSpace;
			}
			return PointStatus.Valid;
		}

		public static bool HaveGrowSpace(Vector3 pos, Plant plant)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: 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)
			float growRadius = plant.m_growRadius;
			if (growRadius > 0f)
			{
				Collider[] array = Physics.OverlapSphere(pos, growRadius, SpaceMask);
				for (int i = 0; i < array.Length; i++)
				{
					Plant component = ((Component)array[i]).GetComponent<Plant>();
					if ((Object)(object)component == (Object)null || (int)component.GetStatus() == 0)
					{
						return false;
					}
				}
			}
			if (plant.m_growRadiusVines > 0f)
			{
				Collider[] array2 = Physics.OverlapSphere(pos, plant.m_growRadiusVines, SpaceMask);
				for (int j = 0; j < array2.Length; j++)
				{
					if ((Object)(object)((Component)array2[j]).GetComponentInParent<Vine>() != (Object)null)
					{
						return false;
					}
				}
			}
			return true;
		}

		public static string Describe(PointStatus status)
		{
			return status switch
			{
				PointStatus.Valid => Loc.T("reason.ok"), 
				PointStatus.NoGround => Loc.T("reason.no_ground"), 
				PointStatus.NoBuildZone => Loc.T("reason.no_build"), 
				PointStatus.PrivateZone => Loc.T("reason.ward"), 
				PointStatus.NeedCultivated => Loc.T("reason.not_cultivated"), 
				PointStatus.NeedDirt => Loc.T("reason.needs_dirt"), 
				PointStatus.WrongBiome => Loc.T("reason.wrong_biome"), 
				PointStatus.TooHot => Loc.T("reason.too_hot"), 
				PointStatus.TooCold => Loc.T("reason.too_cold"), 
				PointStatus.DeepSnow => Loc.T("reason.deep_snow"), 
				PointStatus.InWater => Loc.T("reason.in_water"), 
				PointStatus.NoSpace => Loc.T("reason.no_space"), 
				PointStatus.Planted => Loc.T("reason.planted"), 
				PointStatus.Pending => Loc.T("reason.pending"), 
				_ => status.ToString(), 
			};
		}
	}
	public static class Planter
	{
		public struct Outcome
		{
			public int Planted;

			public int Blocked;

			public bool OutOfSeeds;

			public bool OutOfStamina;

			public bool ToolBroken;
		}

		private static readonly EffectList EmptyEffects = new EffectList();

		public static Outcome Plant(Player player, Piece piece, List<PlantSlot> slots, int limit, Action<Vector3> onPlanted)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0203: Unknown result type (might be due to invalid IL or missing references)
			Outcome result = default(Outcome);
			ItemData rightItem = ((Humanoid)player).GetRightItem();
			if (rightItem == null)
			{
				return result;
			}
			bool flag = player.m_noPlacementCost || ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey(piece.FreeBuildKey()));
			bool flag2 = (((Humanoid)player).m_inventory.ItemCheated(piece.m_resources, -1, true) || player.NoCostCheat()) && !PlayerProfile.s_bypassCheatChecks;
			float buildStamina = player.GetBuildStamina();
			List<int> list = new List<int>(slots.Count);
			for (int i = 0; i < slots.Count; i++)
			{
				if (slots[i].IsValid)
				{
					list.Add(i);
				}
				else if (slots[i].IsBlocked)
				{
					result.Blocked++;
				}
			}
			Vector3 origin = ((Component)player).transform.position;
			list.Sort((int a, int b) => SqrFlat(slots[a].Position, origin).CompareTo(SqrFlat(slots[b].Position, origin)));
			EffectList placeEffect = piece.m_placeEffect;
			try
			{
				foreach (int item in list)
				{
					if (result.Planted < limit)
					{
						PlantSlot plantSlot = slots[item];
						if (!flag && !player.HaveRequirements(piece, (RequirementMode)0))
						{
							result.OutOfSeeds = true;
							break;
						}
						if (!((Character)player).HaveStamina(buildStamina))
						{
							result.OutOfStamina = true;
							break;
						}
						if (rightItem.m_shared.m_useDurability && rightItem.m_durability <= 0f)
						{
							result.ToolBroken = true;
							break;
						}
						PayForPlant(player, piece, rightItem, flag, buildStamina);
						piece.m_placeEffect = ((result.Planted == 0) ? placeEffect : EmptyEffects);
						player.PlacePiece(piece, plantSlot.Position, plantSlot.Rotation, result.Planted == 0, flag2);
						CountStats(piece, flag2);
						result.Planted++;
						onPlanted?.Invoke(plantSlot.Position);
						continue;
					}
					break;
				}
				if (result.Planted > 0)
				{
					player.m_lastToolUseTime = Time.time;
					if ((Object)(object)Hud.instance != (Object)null && (Object)(object)Hud.instance.m_buildUi != (Object)null)
					{
						Hud.instance.m_buildUi.AddRecentPiece(piece);
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Planting failed: " + ex));
			}
			finally
			{
				piece.m_placeEffect = placeEffect;
			}
			return result;
		}

		private static float SqrFlat(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;
		}

		public static bool CanAffordOne(Player player, Piece piece)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			ItemData rightItem = ((Humanoid)player).GetRightItem();
			if (rightItem == null)
			{
				return false;
			}
			if (!player.m_noPlacementCost && (!((Object)(object)ZoneSystem.instance != (Object)null) || !ZoneSystem.instance.GetGlobalKey(piece.FreeBuildKey())) && !player.HaveRequirements(piece, (RequirementMode)0))
			{
				return false;
			}
			if (rightItem.m_shared.m_useDurability && rightItem.m_durability <= 0f)
			{
				return false;
			}
			return ((Character)player).HaveStamina(player.GetBuildStamina());
		}

		private static void PayForPlant(Player player, Piece piece, ItemData tool, bool free, float stamina)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Invalid comparison between Unknown and I4
			//IL_006c: 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_0083: Invalid comparison between Unknown and I4
			if (!free)
			{
				player.ConsumeResources(piece.m_resources, 0, -1, 1);
			}
			((Character)player).UseStamina(stamina);
			PieceTable buildPieces = player.m_buildPieces;
			if ((Object)(object)buildPieces != (Object)null && (int)buildPieces.m_skill != 0)
			{
				if ((int)buildPieces.m_skill == 107)
				{
					Game.instance.IncrementPlayerStat((PlayerStatType)168, 1f, false);
				}
				if (player.m_buildRemoveDebt > 0)
				{
					player.m_buildRemoveDebt--;
				}
				else
				{
					((Character)player).RaiseSkill(buildPieces.m_skill, 1f);
					if ((int)buildPieces.m_skill == 107)
					{
						Game.instance.IncrementPlayerStat((PlayerStatType)169, 1f, false);
					}
				}
			}
			if (tool.m_shared.m_useDurability)
			{
				tool.m_durability -= player.GetPlaceDurability(tool) * Game.m_durabilityRate;
			}
		}

		private static void CountStats(Piece piece, bool cheated)
		{
			Game instance = Game.instance;
			if (!((Object)(object)instance == (Object)null))
			{
				instance.IncrementPlayerStat((PlayerStatType)2, 1f, cheated);
				PlayerProfile playerProfile = instance.GetPlayerProfile();
				if (playerProfile != null)
				{
					playerProfile.IncrementStatBuildPiecePlaced(piece.m_name, 1f, cheated);
				}
			}
		}
	}
	[BepInPlugin("kepqa.AdvancedPlanting", "AdvancedPlanting", "0.4.7")]
	[BepInIncompatibility("advize.PlantEasily")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "kepqa.AdvancedPlanting";

		public const string PluginName = "AdvancedPlanting";

		public const string PluginVersion = "0.4.7";

		public const string PluginAuthor = "kepqa";

		private GhostPool _ghosts;

		private CurveRenderer _overlay;

		private StatusPanel _panel;

		private BuildKeyHints _keyHints;

		private Harmony _harmony;

		private float _scrollAccumulator;

		private float _nextRevalidate;

		private float _nextRebuild;

		private Vector3? _builtCursor;

		private int _appliedVersion = -1;

		private int _appliedSnap = -2;

		private bool _ghostsComplete = true;

		private const float RebuildInterval = 0.08f;

		private const int ValidationsPerFrame = 40;

		private bool _previewVisible;

		private float _nextAutoPlant;

		private readonly List<PlantSlot> _ghostSlots = new List<PlantSlot>();

		private Player _player;

		private Piece _piece;

		private Plant _plant;

		private string _prefabName;

		private int _validatedFrame = -1;

		public static Plugin Instance { get; private set; }

		public static ManualLogSource Log { get; private set; }

		public ModConfig Settings { get; private set; }

		public FigureState State { get; private set; }

		public LayoutService Layout { get; private set; }

		public SettingsWindow Window { get; private set; }

		internal PlantingWheel Wheel { get; private set; }

		private static bool IsHeadless => (int)SystemInfo.graphicsDeviceType == 4;

		public bool HasPlantContext
		{
			get
			{
				if ((Object)(object)_piece != (Object)null)
				{
					return (Object)(object)_player != (Object)null;
				}
				return false;
			}
		}

		private bool FigureModeActive
		{
			get
			{
				if (State.Enabled)
				{
					return HasPlantContext;
				}
				return false;
			}
		}

		public bool OpeningWheel { get; private set; }

		public int WheelOpenedFrame { get; private set; } = -10;

		public float WheelKeyDownTime { get; private set; }

		public bool WindowBlocksInput
		{
			get
			{
				if (Window != null)
				{
					return Window.IsBlockingInput;
				}
				return false;
			}
		}

		public string PlantDisplayName
		{
			get
			{
				if (!((Object)(object)_piece != (Object)null))
				{
					return "";
				}
				return GameText.Localize(_piece.m_name);
			}
		}

		public float MinSpacing => Layout.MinDistance(_plant);

		public float CurrentSpacing => State.GetSpacing(_prefabName, Layout.DefaultSpacing(_plant));

		public float JitterRoom => Mathf.Max(0f, (CurrentSpacing - MinSpacing) * 0.5f);

		public string WheelKeyName => ((object)Settings.WheelKey.Value/*cast due to .constrained prefix*/).ToString();

		public bool ShowsFigureHints
		{
			get
			{
				if (FigureModeActive)
				{
					return !Window.IsOpen;
				}
				return false;
			}
		}

		public bool KeyHintsShowing { get; private set; }

		private void Awake()
		{
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Settings = new ModConfig(((BaseUnityPlugin)this).Config);
			ServerConfig.Init(Settings);
			Loc.Load();
			if (!IsHeadless)
			{
				Icons.Prewarm();
				UiKit.Prewarm();
			}
			State = new FigureState
			{
				Enabled = Settings.EnabledByDefault.Value
			};
			Layout = new LayoutService(Settings);
			Window = new SettingsWindow(this);
			Wheel = new PlantingWheel(this);
			_ghosts = new GhostPool();
			_overlay = new CurveRenderer(Settings);
			_panel = new StatusPanel(Settings);
			_keyHints = new BuildKeyHints();
			_harmony = new Harmony("kepqa.AdvancedPlanting");
			_harmony.PatchAll(typeof(Plugin).Assembly);
			Log.LogInfo((object)"AdvancedPlanting 0.4.7 by kepqa loaded.");
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			_ghosts?.DestroyRoots();
			_overlay?.Destroy();
			Window?.Destroy();
			_panel?.Destroy();
			Instance = null;
		}

		private bool RefreshContext()
		{
			_player = Player.m_localPlayer;
			_piece = null;
			_plant = null;
			_prefabName = null;
			if ((Object)(object)_player == (Object)null || !((Character)_player).InPlaceMode())
			{
				return false;
			}
			Piece selectedPiece = _player.GetSelectedPiece();
			if ((Object)(object)selectedPiece == (Object)null)
			{
				return false;
			}
			Plant component = ((Component)selectedPiece).GetComponent<Plant>();
			if ((Object)(object)component == (Object)null)
			{
				return false;
			}
			_piece = selectedPiece;
			_plant = component;
			_prefabName = ((Object)((Component)selectedPiece).gameObject).name;
			return true;
		}

		public float ScaleBuildStamina(Player player, float stamina)
		{
			if ((Object)(object)player != (Object)(object)_player || !FigureModeActive)
			{
				return stamina;
			}
			return stamina * Settings.StaminaFactor;
		}

		public void SetSpacing(float value)
		{
			State.SetSpacing(_prefabName, value, MinSpacing);
		}

		internal void KeyHintsUpdated(KeyHints hints)
		{
			_keyHints.Update(hints, this);
			KeyHintsShowing = _keyHints.Available && hints.m_keyHintsEnabled && ShowsFigureHints;
		}

		public void SetFigureMode(bool on)
		{
			if (State.Enabled != on)
			{
				State.Enabled = on;
				State.MarkDirty();
				if (!on)
				{
					Window.Close();
					HidePreview();
				}
				Player player = _player;
				if (player != null)
				{
					((Character)player).Message((MessageType)2, Loc.T(on ? "msg.figure_mode" : "msg.single_mode"), 0, (Sprite)null, false);
				}
			}
		}

		private void Update()
		{
			ServerConfig.Update();
			if (!RefreshContext())
			{
				Window.Close();
				HidePreview();
				return;
			}
			Icons.UploadStep();
			Window.Update();
			HandleWheelKey();
			HandleQuickKeys();
			if (!State.Enabled)
			{
				HidePreview();
			}
			else
			{
				UpdateAutoContinue();
			}
		}

		private void HandleQuickKeys()
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: 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_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			if (!State.Enabled || Hud.InRadial() || Window.IsBlockingInput || Hud.IsPieceSelectionVisible() || !((Character)_player).TakeInput())
			{
				return;
			}
			if (Pressed(Settings.FinishKey.Value) && State.CanFinish)
			{
				State.Finish();
				((Character)_player).Message((MessageType)2, Loc.T("msg.ready", StatusPanel.ModeName(State.Mode)), 0, (Sprite)null, false);
			}
			if (Pressed(Settings.FillKey.Value) && State.IsClosedPath)
			{
				State.SetFill(!State.Fill);
				((Character)_player).Message((MessageType)2, Loc.T(State.Fill ? "wheel.fill.on" : "wheel.fill.off"), 0, (Sprite)null, false);
			}
			if (Pressed(Settings.RepeatKey.Value) && State.CanRepeat)
			{
				GameObject placementGhost = _player.m_placementGhost;
				if ((Object)(object)placementGhost == (Object)null || !placementGhost.activeSelf)
				{
					((Character)_player).Message((MessageType)2, Loc.T("msg.aim"), 0, (Sprite)null, false);
				}
				else if (State.RepeatAt(placementGhost.transform.position))
				{
					((Character)_player).Message((MessageType)2, Loc.T("msg.ready", StatusPanel.ModeName(State.Mode)), 0, (Sprite)null, false);
				}
			}
			if (Pressed(Settings.ClearKey.Value) && !State.IsEmpty)
			{
				State.Clear();
				((Character)_player).Message((MessageType)2, Loc.T("msg.cleared"), 0, (Sprite)null, false);
			}
		}

		private static bool Pressed(KeyCode key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			if ((int)key != 0)
			{
				return ZInput.GetKeyDown(key, true);
			}
			return false;
		}

		public bool HandleMouseWheel(Player player)
		{
			if (!Settings.RowsOnMouseWheel.Value || (Object)(object)player != (Object)(object)_player || !FigureModeActive)
			{
				return false;
			}
			if (Window.IsBlockingInput || Hud.InRadial() || Hud.IsPieceSelectionVisible())
			{
				return false;
			}
			float mouseScrollWheel = ZInput.GetMouseScrollWheel();
			if (Mathf.Abs(mouseScrollWheel) < 0.001f)
			{
				return false;
			}
			if (State.IsClosedPath && State.Fill)
			{
				return true;
			}
			_scrollAccumulator += mouseScrollWheel;
			float num = Mathf.Max(0.01f, player.m_scrollAmountThreshold);
			if (Mathf.Abs(_scrollAccumulator) < num)
			{
				return true;
			}
			int num2 = ((_scrollAccumulator > 0f) ? 1 : (-1));
			_scrollAccumulator = 0f;
			int rows = State.Rows;
			State.SetRows(State.Rows + num2);
			if (State.Rows != rows)
			{
				((Character)_player).Message((MessageType)1, Loc.T("msg.rows", State.Rows), 0, (Sprite)null, false);
			}
			return true;
		}

		private void HandleWheelKey()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: 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)
			KeyCode value = Settings.WheelKey.Value;
			if ((int)value != 0 && ZInput.GetKeyDown(value, true))
			{
				WheelKeyDownTime = Time.unscaledTime;
				if (!Hud.InRadial() && !Window.IsBlockingInput && !Hud.IsPieceSelectionVisible() && ((Character)_player).TakeInput())
				{
					OpenWheel();
				}
			}
		}

		private void OpenWheel()
		{
			Hud instance = Hud.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_radialMenu == (Object)null || (Object)(object)instance.m_config == (Object)null)
			{
				return;
			}
			OpeningWheel = true;
			try
			{
				WheelOpenedFrame = Time.frameCount;
				instance.m_radialMenu.CanOpen = true;
				instance.m_radialMenu.Open((IRadialConfig)(object)instance.m_config, (IRadialConfig)null);
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Could not open the planting wheel: " + ex));
			}
			finally
			{
				OpeningWheel = false;
			}
		}

		public bool HandlePlacementClick(Player player)
		{
			//IL_011c: 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_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player != (Object)(object)_player || !FigureModeActive)
			{
				return false;
			}
			if (Window.IsBlockingInput || Hud.InRadial() || Hud.IsPieceSelectionVisible())
			{
				return false;
			}
			if (!ZInput.GetButtonDown("Attack") && !ZInput.GetButtonDown("JoyPlace"))
			{
				return false;
			}
			if (State.Finished)
			{
				EnsureValidated();
				if (Layout.ValidCount > 0)
				{
					ItemData rightItem = ((Humanoid)player).GetRightItem();
					if (rightItem != null && !((Character)player).HaveStamina(rightItem.m_shared.m_attack.m_attackStamina))
					{
						((Character)player).Message((MessageType)2, Loc.T("msg.no_stamina") + (State.AutoContinue ? "" : Loc.T("msg.no_stamina.auto_hint")), 0, (Sprite)null, false);
					}
					return false;
				}
				State.Clear();
			}
			GameObject placementGhost = player.m_placementGhost;
			if ((Object)(object)placementGhost == (Object)null || !placementGhost.activeSelf)
			{
				((Character)player).Message((MessageType)2, Loc.T("msg.aim"), 0, (Sprite)null, false);
				return true;
			}
			Vector3 position = placementGhost.transform.position;
			if (SnapTarget(position) >= 0)
			{
				State.Finish();
				((Character)player).Message((MessageType)2, Loc.T("msg.ready", StatusPanel.ModeName(State.Mode)), 0, (Sprite)null, false);
				return true;
			}
			if (State.ControlPoints.Count > 0 && FlatDistance(State.ControlPoints[State.ControlPoints.Count - 1], position) < 0.2f)
			{
				return true;
			}
			float num = ExtentWith(position);
			if (num > Settings.FigureSizeLimit)
			{
				((Character)player).Message((MessageType)2, TooLargeText(num), 0, (Sprite)null, false);
				return true;
			}
			State.AddPoint(position);
			if (State.Finished)
			{
				((Character)player).Message((MessageType)2, Loc.T("msg.ready", StatusPanel.ModeName(State.Mode)), 0, (Sprite)null, false);
			}
			return true;
		}

		private int SnapTarget(Vector3 cursor)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Invalid comparison between Unknown and I4
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			if (!State.CanFinish)
			{
				return -1;
			}
			float value = Settings.SnapRadius.Value;
			int num = State.ControlPoints.Count - 1;
			if (FlatDistance(State.ControlPoints[num], cursor) <= value)
			{
				return num;
			}
			if ((int)State.Mode == 4 && FlatDistance(State.ControlPoints[0], cursor) <= value)
			{
				return 0;
			}
			return -1;
		}

		private float ExtentWith(Vector3 extra)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Invalid comparison between Unknown and I4
			//IL_003f: 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_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: 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_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: 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_00a3: Unknown result type (might be due to invalid IL or missing references)
			if ((int)State.Mode == 2 && State.ControlPoints.Count == 1)
			{
				return 2f * FlatDistance(State.ControlPoints[0], extra);
			}
			float num = extra.x;
			float num2 = extra.x;
			float num3 = extra.z;
			float num4 = extra.z;
			foreach (Vector3 controlPoint in State.ControlPoints)
			{
				num = Mathf.Min(num, controlPoint.x);
				num2 = Mathf.Max(num2, controlPoint.x);
				num3 = Mathf.Min(num3, controlPoint.z);
				num4 = Mathf.Max(num4, controlPoint.z);
			}
			return Mathf.Max(num2 - num, num4 - num3);
		}

		public string TooLargeText(float extent)
		{
			return Loc.T("msg.too_large", Mathf.RoundToInt(extent), Mathf.RoundToInt(Settings.FigureSizeLimit));
		}

		private static float FlatDistance(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 Mathf.Sqrt(num * num + num2 * num2);
		}

		private Vector3? CursorPoint()
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = (((Object)(object)_player != (Object)null) ? _player.m_placementGhost : null);
			if ((Object)(object)val == (Object)null || !val.activeSelf)
			{
				return null;
			}
			return val.transform.position;
		}

		private void RebuildNow()
		{
			Vector3? val = (State.Finished ? ((Vector3?)null) : CursorPoint());
			try
			{
				Layout.Rebuild(State, val, _plant, _prefabName);
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Layout rebuild failed: " + ex));
				Layout.Clear();
			}
			_builtCursor = val;
			State.ClearDirty();
			_nextRebuild = Time.unscaledTime + 0.08f;
			_nextRevalidate = Time.unscaledTime + Settings.RevalidateInterval.Value;
		}

		private void EnsureValidated()
		{
			if (_validatedFrame != Time.frameCount || State.Dirty)
			{
				if (State.Dirty || Layout.BuiltForPrefab != _prefabName)
				{
					RebuildNow();
				}
				Layout.ValidateAll(State, _piece, _plant, _player);
				_validatedFrame = Time.frameCount;
			}
		}

		public void OnPlacementGhostUpdated(Player player)
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			if (!FigureModeActive)
			{
				HidePreview();
				return;
			}
			float unscaledTime = Time.unscaledTime;
			bool flag = State.Dirty || Layout.BuiltForPrefab != _prefabName;
			Vector3? val = CursorPoint();
			if (!flag && !State.Finished)
			{
				flag = val.HasValue != _builtCursor.HasValue || (val.HasValue && FlatDistance(val.Value, _builtCursor.Value) > 0.1f);
			}
			if (flag && unscaledTime >= _nextRebuild)
			{
				RebuildNow();
			}
			if (!Layout.PassComplete)
			{
				Layout.ValidateStep(State, _piece, _plant, _player, 40);
			}
			else if (unscaledTime >= _nextRevalidate)
			{
				Layout.BeginRevalidation();
				_nextRevalidate = unscaledTime + Settings.RevalidateInterval.Value;
			}
			int num = (val.HasValue ? SnapTarget(val.Value) : (-1));
			if (Layout.Version == _appliedVersion && num == _appliedSnap && _ghostsComplete && _previewVisible)
			{
				return;
			}
			_ghostSlots.Clear();
			foreach (PlantSlot slot in Layout.Slots)
			{
				if (slot.IsValid || slot.IsPending)
				{
					_ghostSlots.Add(slot);
				}
			}
			_ghostsComplete = _ghosts.Apply(player.m_placementGhost, _ghostSlots);
			_overlay.Apply(Layout.RowLines, State.ControlPoints, num, Layout.Slots, ((Object)(object)_plant != (Object)null) ? _plant.m_growRadius : 0f, Layout.TooLarge);
			_appliedVersion = Layout.Version;
			_appliedSnap = num;
			_previewVisible = true;
		}

		private void HidePreview()
		{
			if (_previewVisible)
			{
				_ghosts.HideAll();
				_overlay.HideAll();
				_previewVisible = false;
				_appliedVersion = -1;
			}
		}

		public bool TryPlantFigure(Player player, Piece piece)
		{
			if (!FigureModeActive || (Object)(object)player != (Object)(object)_player || (Object)(object)piece == (Object)null || !State.Finished)
			{
				return false;
			}
			if ((Object)(object)((Component)piece).GetComponent<Plant>() == (Object)null)
			{
				return false;
			}
			EnsureValidated();
			if (Layout.ValidCount == 0)
			{
				((Character)player).Message((MessageType)2, Loc.T("msg.nothing", PlacementValidator.Describe(Layout.TopBlockReason)), 0, (Sprite)null, false);
				return true;
			}
			Planter.Outcome outcome = Planter.Plant(player, piece, Layout.Slots, int.MaxValue, State.MarkPlanted);
			Layout.RefreshPlanted(State);
			string text = Loc.T("msg.planted", outcome.Planted);
			if (Layout.ValidCount > 0)
			{
				text += Loc.T("msg.left", Layout.ValidCount);
				if (outcome.OutOfStamina)
				{
					text = text + " " + Loc.T(State.AutoContinue ? "msg.left.auto" : "msg.left.stamina");
				}
				else if (outcome.OutOfSeeds)
				{
					text = text + " " + Loc.T("msg.left.seeds");
				}
				else if (outcome.ToolBroken)
				{
					text = text + " " + Loc.T("msg.left.tool");
				}
			}
			if (outcome.Blocked > 0)
			{
				text += Loc.T("msg.blocked", outcome.Blocked, PlacementValidator.Describe(Layout.TopBlockReason));
			}
			((Character)player).Message((MessageType)1, text, 0, (Sprite)null, false);
			CompleteIfDone();
			return true;
		}

		private void UpdateAutoContinue()
		{
			if (State.AutoContinue && State.Finished && !Window.IsBlockingInput && !(Time.time < _nextAutoPlant) && Layout.ValidCount != 0 && !Hud.InRadial() && ((Character)_player).TakeInput())
			{
				_nextAutoPlant = Time.time + Settings.AutoContinueInterval.Value;
				if (!State.Dirty && Layout.FullyValidated && Layout.ValidCount != 0 && Planter.CanAffordOne(_player, _piece) && Planter.Plant(_player, _piece, Layout.Slots, 1, State.MarkPlanted).Planted != 0)
				{
					Layout.RefreshPlanted(State);
					CompleteIfDone();
				}
			}
		}

		private void CompleteIfDone()
		{
			if (Layout.ValidCount <= 0 && Layout.BlockedCount <= 0 && Layout.PlantedCount != 0)
			{
				Player player = _player;
				if (player != null)
				{
					((Character)player).Message((MessageType)2, Loc.T("msg.complete", Layout.PlantedCount), 0, (Sprite)null, false);
				}
				State.Clear();
			}
		}

		public string NextActionHint()
		{
			//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_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected I4, but got Unknown
			if (State.Finished)
			{
				if (Layout.ValidCount <= 0)
				{
					return Loc.T("hint.new_figure");
				}
				return Loc.T("hint.plant", Layout.ValidCount);
			}
			int count = State.ControlPoints.Count;
			PathMode mode = State.Mode;
			switch ((int)mode)
			{
			case 0:
				return Loc.T

AdvancedPlanting.Core.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("AdvancedPlanting.Core")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+634f9821e72cc99f55d00900f66b35f1c375ee61")]
[assembly: AssemblyProduct("AdvancedPlanting.Core")]
[assembly: AssemblyTitle("AdvancedPlanting.Core")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
}
namespace AdvancedPlanting.Core
{
	public static class CurveBuilder
	{
		public const float DefaultResolution = 0.1f;

		public static Polyline Build(PathMode mode, IReadOnlyList<Vec2> controlPoints, float resolution = 0.1f)
		{
			if (controlPoints == null)
			{
				throw new ArgumentNullException("controlPoints");
			}
			if (resolution <= 0f)
			{
				resolution = 0.1f;
			}
			List<Vec2> list = TrimToMode(mode, controlPoints);
			if (list.Count < mode.MinControlPoints())
			{
				return new Polyline(new Vec2[0], closed: false);
			}
			return mode switch
			{
				PathMode.Segment => new Polyline(new Vec2[2]
				{
					list[0],
					list[list.Count - 1]
				}, closed: false).Densify(resolution), 
				PathMode.Arc => BuildArc(list[0], list[1], list[2], fullCircle: false, resolution), 
				PathMode.Circle => BuildCircle(list[0], list[1], resolution), 
				PathMode.Spline => BuildCatmullRom(list, closed: false, resolution), 
				PathMode.Contour => BuildCatmullRom(list, closed: true, resolution), 
				_ => throw new ArgumentOutOfRangeException("mode"), 
			};
		}

		private static List<Vec2> TrimToMode(PathMode mode, IReadOnlyList<Vec2> controlPoints)
		{
			int num = mode.MaxControlPoints();
			List<Vec2> list = new List<Vec2>(controlPoints.Count);
			for (int i = 0; i < controlPoints.Count; i++)
			{
				if (num > 0 && list.Count >= num)
				{
					break;
				}
				list.Add(controlPoints[i]);
			}
			return list;
		}

		public static bool TryCircumcircle(Vec2 a, Vec2 b, Vec2 c, out Vec2 center, out float radius)
		{
			float num = 2f * (a.X * (b.Z - c.Z) + b.X * (c.Z - a.Z) + c.X * (a.Z - b.Z));
			if (Math.Abs(num) < 1E-06f)
			{
				center = Vec2.Zero;
				radius = 0f;
				return false;
			}
			float sqrLength = a.SqrLength;
			float sqrLength2 = b.SqrLength;
			float sqrLength3 = c.SqrLength;
			float x = (sqrLength * (b.Z - c.Z) + sqrLength2 * (c.Z - a.Z) + sqrLength3 * (a.Z - b.Z)) / num;
			float z = (sqrLength * (c.X - b.X) + sqrLength2 * (a.X - c.X) + sqrLength3 * (b.X - a.X)) / num;
			center = new Vec2(x, z);
			radius = Vec2.Distance(center, a);
			if (radius > 0.0001f)
			{
				return radius < 100000f;
			}
			return false;
		}

		private static Polyline BuildArc(Vec2 start, Vec2 mid, Vec2 end, bool fullCircle, float resolution)
		{
			if (!TryCircumcircle(start, mid, end, out var center, out var radius))
			{
				return new Polyline(new Vec2[3] { start, mid, end }, closed: false).Densify(resolution);
			}
			float num = (float)Math.Atan2(start.Z - center.Z, start.X - center.X);
			float num2 = (float)Math.Atan2(mid.Z - center.Z, mid.X - center.X);
			float num3 = Wrap((float)Math.Atan2(end.Z - center.Z, end.X - center.X) - num);
			bool flag = Wrap(num2 - num) <= num3;
			float num4 = (flag ? num3 : (0f - (MathF.PI * 2f - num3)));
			if (fullCircle)
			{
				num4 = (flag ? (MathF.PI * 2f) : (MathF.PI * -2f));
			}
			float num5 = Math.Abs(num4) * radius;
			int num6 = Math.Max(8, (int)Math.Ceiling(num5 / resolution));
			List<Vec2> list = new List<Vec2>(num6 + 1);
			int num7 = (fullCircle ? num6 : (num6 + 1));
			for (int i = 0; i < num7; i++)
			{
				float num8 = num + num4 * (float)i / (float)num6;
				list.Add(new Vec2(center.X + radius * (float)Math.Cos(num8), center.Z + radius * (float)Math.Sin(num8)));
			}
			return new Polyline(list, fullCircle);
		}

		public static Polyline BuildCircle(Vec2 center, Vec2 edge, float resolution)
		{
			float num = Vec2.Distance(center, edge);
			if (num < 0.001f)
			{
				return new Polyline(new Vec2[1] { center }, closed: false);
			}
			float num2 = (float)Math.Atan2(edge.Z - center.Z, edge.X - center.X);
			int num3 = Math.Max(12, (int)Math.Ceiling(MathF.PI * 2f * num / resolution));
			List<Vec2> list = new List<Vec2>(num3);
			for (int i = 0; i < num3; i++)
			{
				float num4 = num2 + MathF.PI * 2f * (float)i / (float)num3;
				list.Add(new Vec2(center.X + num * (float)Math.Cos(num4), center.Z + num * (float)Math.Sin(num4)));
			}
			return new Polyline(list, closed: true);
		}

		private static float Wrap(float angle)
		{
			angle %= MathF.PI * 2f;
			if (angle < 0f)
			{
				angle += MathF.PI * 2f;
			}
			return angle;
		}

		public static Polyline BuildCatmullRom(IReadOnlyList<Vec2> control, bool closed, float resolution)
		{
			List<Vec2> list = new List<Vec2>(control.Count);
			foreach (Vec2 item in control)
			{
				if (list.Count == 0 || Vec2.SqrDistance(list[list.Count - 1], item) > 1E-08f)
				{
					list.Add(item);
				}
			}
			if (closed && list.Count > 2 && Vec2.SqrDistance(list[0], list[list.Count - 1]) <= 1E-08f)
			{
				list.RemoveAt(list.Count - 1);
			}
			if (list.Count < 2)
			{
				return new Polyline(list, closed: false);
			}
			if (list.Count == 2 && !closed)
			{
				return new Polyline(list, closed: false).Densify(resolution);
			}
			if (closed && list.Count < 3)
			{
				return new Polyline(list, closed: false).Densify(resolution);
			}
			List<Vec2> list2 = new List<Vec2>();
			int num = (closed ? list.Count : (list.Count - 1));
			for (int i = 0; i < num; i++)
			{
				Vec2 control2 = GetControl(list, i - 1, closed);
				Vec2 control3 = GetControl(list, i, closed);
				Vec2 control4 = GetControl(list, i + 1, closed);
				Vec2 control5 = GetControl(list, i + 2, closed);
				float num2 = Vec2.Distance(control3, control4);
				int num3 = Math.Max(4, (int)Math.Ceiling(num2 / resolution));
				for (int j = 0; j < num3; j++)
				{
					float t = (float)j / (float)num3;
					list2.Add(EvaluateCentripetal(control2, control3, control4, control5, t));
				}
			}
			if (!closed)
			{
				list2.Add(list[list.Count - 1]);
			}
			return new Polyline(list2, closed);
		}

		private static Vec2 GetControl(List<Vec2> pts, int index, bool closed)
		{
			int count = pts.Count;
			if (closed)
			{
				index %= count;
				if (index < 0)
				{
					index += count;
				}
				return pts[index];
			}
			if (index < 0)
			{
				return pts[0] + (pts[0] - pts[1]);
			}
			if (index >= count)
			{
				return pts[count - 1] + (pts[count - 1] - pts[count - 2]);
			}
			return pts[index];
		}

		private static Vec2 EvaluateCentripetal(Vec2 p0, Vec2 p1, Vec2 p2, Vec2 p3, float t)
		{
			float num = 0f;
			float num2 = num + Knot(p0, p1, 0.5f);
			float num3 = num2 + Knot(p1, p2, 0.5f);
			float tb = num3 + Knot(p2, p3, 0.5f);
			if (num3 - num2 < 1E-06f)
			{
				return Vec2.Lerp(p1, p2, t);
			}
			float t2 = num2 + (num3 - num2) * t;
			Vec2 a = Blend(p0, p1, num, num2, t2);
			Vec2 vec = Blend(p1, p2, num2, num3, t2);
			Vec2 b = Blend(p2, p3, num3, tb, t2);
			Vec2 a2 = Blend(a, vec, num, num3, t2);
			Vec2 b2 = Blend(vec, b, num2, tb, t2);
			return Blend(a2, b2, num2, num3, t2);
		}

		private static float Knot(Vec2 a, Vec2 b, float alpha)
		{
			return (float)Math.Pow(Math.Max(Vec2.Distance(a, b), 1E-05f), alpha);
		}

		private static Vec2 Blend(Vec2 a, Vec2 b, float ta, float tb, float t)
		{
			float num = tb - ta;
			if (num < 1E-06f)
			{
				return a;
			}
			return a * ((tb - t) / num) + b * ((t - ta) / num);
		}
	}
	public static class Noise
	{
		public static uint Hash(int position, uint seed)
		{
			int num = position * -1255572915 + (int)seed;
			int num2 = (num ^ (num >>> 8)) + 1759714724;
			int num3 = (num2 ^ (num2 << 8)) * 458671337;
			return (uint)(num3 ^ (num3 >>> 8));
		}

		public static uint Hash(int x, int y, uint seed)
		{
			return Hash(x + 198491317 * y, seed);
		}

		public static float Float01(int position, uint seed)
		{
			return (float)Hash(position, seed) / 4.2949673E+09f;
		}

		public static float Float01(int x, int y, uint seed)
		{
			return (float)Hash(x, y, seed) / 4.2949673E+09f;
		}

		public static float FloatSigned(int position, uint seed)
		{
			return Float01(position, seed) * 2f - 1f;
		}

		public static Vec2 InUnitDisk(int position, uint seed)
		{
			float num = (float)Math.Sqrt(Float01(position, seed));
			float num2 = Float01(position, seed + 2654435769u) * 2f * MathF.PI;
			return new Vec2(num * (float)Math.Cos(num2), num * (float)Math.Sin(num2));
		}

		public static float Smooth2D(float x, float y, uint seed)
		{
			int num = FloorToInt(x);
			int num2 = FloorToInt(y);
			float t = x - (float)num;
			float t2 = y - (float)num2;
			float num3 = Fade(t);
			float num4 = Fade(t2);
			float num5 = Float01(num, num2, seed);
			float num6 = Float01(num + 1, num2, seed);
			float num7 = Float01(num, num2 + 1, seed);
			float num8 = Float01(num + 1, num2 + 1, seed);
			float num9 = num5 + (num6 - num5) * num3;
			float num10 = num7 + (num8 - num7) * num3;
			return (num9 + (num10 - num9) * num4) * 2f - 1f;
		}

		public static Vec2 SmoothVector2D(float x, float y, uint seed)
		{
			return new Vec2(Smooth2D(x, y, seed), Smooth2D(x, y, seed + 2135587861));
		}

		private static float Fade(float t)
		{
			return t * t * t * (t * (t * 6f - 15f) + 10f);
		}

		private static int FloorToInt(float v)
		{
			int num = (int)v;
			if (!(v < (float)num))
			{
				return num;
			}
			return num - 1;
		}
	}
	public static class Offset
	{
		public static List<Polyline> OffsetPolyline(Polyline source, float distance, float resolution = 0.1f)
		{
			List<Polyline> list = new List<Polyline>();
			if (source == null || source.IsDegenerate)
			{
				return list;
			}
			if (Math.Abs(distance) < 1E-05f)
			{
				list.Add(source);
				return list;
			}
			Polyline polyline = source.Densify(resolution);
			float num = Math.Abs(distance) * 0.98f - resolution;
			if (num < 0f)
			{
				num = 0f;
			}
			float length = polyline.Length;
			int num2 = Math.Max(2, (int)Math.Ceiling(length / resolution));
			if (polyline.IsClosed)
			{
				num2 = Math.Max(3, num2);
			}
			float num3 = Math.Min(2f, Math.Max(resolution, Math.Abs(distance) * 0.15f));
			SegmentGrid segmentGrid = new SegmentGrid((num3 > resolution * 1.5f) ? polyline.Resample(num3) : polyline, Math.Max(resolution * 2f, num * 0.5f));
			List<Vec2> list2 = new List<Vec2>();
			List<List<Vec2>> list3 = new List<List<Vec2>>();
			int num4 = (polyline.IsClosed ? num2 : (num2 + 1));
			for (int i = 0; i < num4; i++)
			{
				float distance2 = (polyline.IsClosed ? (length * (float)i / (float)num2) : Math.Min(length, length * (float)i / (float)num2));
				Vec2 vec = polyline.PointAt(distance2) + polyline.NormalAt(distance2) * distance;
				if (!segmentGrid.AnyCloserThan(vec, num))
				{
					list2.Add(vec);
				}
				else if (list2.Count > 0)
				{
					list3.Add(list2);
					list2 = new List<Vec2>();
				}
			}
			if (list2.Count > 0)
			{
				list3.Add(list2);
			}
			if (list3.Count == 0)
			{
				return list;
			}
			if (polyline.IsClosed)
			{
				if (list3.Count == 1 && list3[0].Count == num4)
				{
					list.Add(new Polyline(list3[0], closed: true));
					return list;
				}
				if (list3.Count > 1)
				{
					List<Vec2> list4 = list3[list3.Count - 1];
					list4.AddRange(list3[0]);
					list3[0] = list4;
					list3.RemoveAt(list3.Count - 1);
				}
			}
			foreach (List<Vec2> item in list3)
			{
				Polyline polyline2 = new Polyline(item, closed: false);
				if (!polyline2.IsDegenerate)
				{
					list.Add(polyline2);
				}
			}
			return list;
		}

		public static float InwardSign(Polyline closed)
		{
			if (closed == null || !closed.IsClosed)
			{
				return 1f;
			}
			if (!(closed.SignedArea() >= 0f))
			{
				return -1f;
			}
			return 1f;
		}
	}
	public enum PathMode
	{
		Segment,
		Arc,
		Circle,
		Spline,
		Contour
	}
	public static class PathModeExtensions
	{
		public static int MinControlPoints(this PathMode mode)
		{
			return mode switch
			{
				PathMode.Segment => 2, 
				PathMode.Arc => 3, 
				PathMode.Circle => 2, 
				PathMode.Spline => 2, 
				PathMode.Contour => 3, 
				_ => 2, 
			};
		}

		public static int MaxControlPoints(this PathMode mode)
		{
			return mode switch
			{
				PathMode.Segment => 2, 
				PathMode.Arc => 3, 
				PathMode.Circle => 2, 
				_ => 0, 
			};
		}

		public static bool IsClosed(this PathMode mode)
		{
			if (mode != PathMode.Circle)
			{
				return mode == PathMode.Contour;
			}
			return true;
		}

		public static PathMode Next(this PathMode mode)
		{
			return (PathMode)((int)(mode + 1) % 5);
		}
	}
	public enum RowSide
	{
		Both,
		Left,
		Right
	}
	public sealed class LayoutRequest
	{
		public PathMode Mode = PathMode.Spline;

		public List<Vec2> ControlPoints = new List<Vec2>();

		public float Spacing = 2f;

		public float RowSpacing;

		public int Rows = 1;

		public bool Fill;

		public RowSide Side;

		public float Jitter;

		public float Thinning;

		public uint Seed = 1u;

		public float MinDistance = 1f;

		public float WarpScale = 6f;

		public int MaxPoints = 400;

		public float MaxExtent;

		public float Resolution = 0.1f;

		public float EffectiveRowSpacing
		{
			get
			{
				if (!(RowSpacing > 0f))
				{
					return Spacing;
				}
				return RowSpacing;
			}
		}
	}
	public readonly struct LayoutPoint
	{
		public readonly Vec2 Position;

		public readonly int Row;

		public readonly int IndexInRow;

		public LayoutPoint(Vec2 position, int row, int indexInRow)
		{
			Position = position;
			Row = row;
			IndexInRow = indexInRow;
		}
	}
	public sealed class LayoutResult
	{
		public List<LayoutPoint> Points { get; } = new List<LayoutPoint>();

		public List<Polyline> RowCurves { get; } = new List<Polyline>();

		public bool Truncated { get; set; }

		public int Rejected { get; set; }

		public bool TooLarge { get; set; }

		public float Extent { get; set; }

		public bool IsEmpty => Points.Count == 0;
	}
	public static class PlantingLayout
	{
		private struct RowCurve
		{
			public Polyline Curve;

			public int Index;
		}

		private sealed class SpatialHash
		{
			private readonly float _cell;

			private readonly Dictionary<long, List<Vec2>> _buckets = new Dictionary<long, List<Vec2>>();

			public SpatialHash(float cellSize)
			{
				_cell = cellSize;
			}

			private long Key(int cx, int cz)
			{
				return ((long)cx << 32) ^ (uint)cz;
			}

			public void Add(Vec2 p)
			{
				long key = Key(Cell(p.X), Cell(p.Z));
				if (!_buckets.TryGetValue(key, out var value))
				{
					value = new List<Vec2>();
					_buckets[key] = value;
				}
				value.Add(p);
			}

			public bool IsFree(Vec2 p, float minDistance)
			{
				if (minDistance <= 0f)
				{
					return true;
				}
				float num = minDistance * minDistance;
				int num2 = (int)Math.Ceiling(minDistance / _cell);
				int num3 = Cell(p.X);
				int num4 = Cell(p.Z);
				for (int i = -num2; i <= num2; i++)
				{
					for (int j = -num2; j <= num2; j++)
					{
						if (!_buckets.TryGetValue(Key(num3 + i, num4 + j), out var value))
						{
							continue;
						}
						for (int k = 0; k < value.Count; k++)
						{
							if (Vec2.SqrDistance(value[k], p) < num)
							{
								return false;
							}
						}
					}
				}
				return true;
			}

			private int Cell(float v)
			{
				int num = (int)(v / _cell);
				if (!(v < 0f) || (float)num * _cell == v)
				{
					return num;
				}
				return num - 1;
			}
		}

		private const int MaxContourRows = 200;

		private const int JitterRetries = 5;

		public static LayoutResult Generate(LayoutRequest request)
		{
			if (request == null)
			{
				throw new ArgumentNullException("request");
			}
			LayoutResult layoutResult = new LayoutResult();
			if (request.ControlPoints == null || request.ControlPoints.Count < request.Mode.MinControlPoints())
			{
				return layoutResult;
			}
			if (request.Spacing <= 0.05f)
			{
				return layoutResult;
			}
			if (request.MaxExtent > 0f)
			{
				float num = ExtentOf(request.ControlPoints);
				if (request.Mode == PathMode.Circle && request.ControlPoints.Count >= 2)
				{
					num = 2f * Vec2.Distance(request.ControlPoints[0], request.ControlPoints[1]);
				}
				if (num > request.MaxExtent)
				{
					layoutResult.TooLarge = true;
					layoutResult.Extent = num;
					return layoutResult;
				}
			}
			Polyline polyline = CurveBuilder.Build(request.Mode, request.ControlPoints, request.Resolution);
			layoutResult.Extent = ExtentOf(polyline.Points);
			if (request.MaxExtent > 0f && layoutResult.Extent > request.MaxExtent)
			{
				layoutResult.TooLarge = true;
				layoutResult.RowCurves.Add(polyline);
				return layoutResult;
			}
			if (polyline.IsDegenerate)
			{
				if (polyline.Count == 1)
				{
					layoutResult.RowCurves.Add(polyline);
					layoutResult.Points.Add(new LayoutPoint(polyline.Points[0], 0, 0));
				}
				return layoutResult;
			}
			float maxJitter = MaxJitterOffset(request);
			SpatialHash spatialHash = new SpatialHash(Math.Max(0.5f, request.MinDistance));
			foreach (RowCurve item in BuildRows(request, polyline))
			{
				layoutResult.RowCurves.Add(item.Curve);
				List<float> list = item.Curve.EvenDistances(request.Spacing);
				for (int i = 0; i < list.Count; i++)
				{
					if (layoutResult.Points.Count >= request.MaxPoints)
					{
						layoutResult.Truncated = true;
						return layoutResult;
					}
					Vec2 vec = item.Curve.PointAt(list[i]);
					int num2 = QuantizedId(vec);
					if (!(request.Thinning > 0f) || !(Noise.Float01(num2, request.Seed + 20903) < request.Thinning))
					{
						if (!TryPlace(request, vec, num2, maxJitter, spatialHash, out var final))
						{
							layoutResult.Rejected++;
							continue;
						}
						spatialHash.Add(final);
						layoutResult.Points.Add(new LayoutPoint(final, item.Index, i));
					}
				}
			}
			return layoutResult;
		}

		private static float ExtentOf(IReadOnlyList<Vec2> pts)
		{
			if (pts == null || pts.Count == 0)
			{
				return 0f;
			}
			float num = float.MaxValue;
			float num2 = float.MinValue;
			float num3 = float.MaxValue;
			float num4 = float.MinValue;
			foreach (Vec2 pt in pts)
			{
				if (pt.X < num)
				{
					num = pt.X;
				}
				if (pt.X > num2)
				{
					num2 = pt.X;
				}
				if (pt.Z < num3)
				{
					num3 = pt.Z;
				}
				if (pt.Z > num4)
				{
					num4 = pt.Z;
				}
			}
			return Math.Max(num2 - num, num4 - num3);
		}

		public static float MaxJitterOffset(LayoutRequest request)
		{
			float num = (Math.Min(request.Spacing, request.EffectiveRowSpacing) - request.MinDistance) * 0.5f;
			if (!(num > 0f))
			{
				return 0f;
			}
			return num;
		}

		private static bool TryPlace(LayoutRequest request, Vec2 gridPoint, int cell, float maxJitter, SpatialHash accepted, out Vec2 final)
		{
			if (request.Jitter <= 0f || maxJitter <= 0f)
			{
				final = gridPoint;
				return accepted.IsFree(final, request.MinDistance);
			}
			float num = request.Jitter * maxJitter;
			float num2 = ((request.WarpScale > 0.1f) ? request.WarpScale : 6f);
			Vec2 vec = Noise.SmoothVector2D(gridPoint.X / num2, gridPoint.Z / num2, request.Seed) * 0.6f;
			for (int i = 0; i < 5; i++)
			{
				uint seed = request.Seed + (uint)(i * -1640531527);
				Vec2 vec2 = Noise.InUnitDisk(cell, seed) * 0.4f;
				Vec2 vec3 = gridPoint + (vec + vec2) * num;
				if (accepted.IsFree(vec3, request.MinDistance))
				{
					final = vec3;
					return true;
				}
			}
			final = gridPoint;
			return accepted.IsFree(final, request.MinDistance);
		}

		private static int QuantizedId(Vec2 p)
		{
			int num = (int)Math.Round(p.X * 20f);
			int num2 = (int)Math.Round(p.Z * 20f);
			return (num * 73856093) ^ (num2 * 19349663);
		}

		private static IEnumerable<RowCurve> BuildRows(LayoutRequest request, Polyline guide)
		{
			float rowSpacing = request.EffectiveRowSpacing;
			if (guide.IsClosed && request.Fill)
			{
				float inward = Offset.InwardSign(guide);
				yield return new RowCurve
				{
					Curve = guide,
					Index = 0
				};
				for (int k = 1; k < 200; k++)
				{
					List<Polyline> list = Offset.OffsetPolyline(guide, inward * rowSpacing * (float)k, request.Resolution);
					bool any = false;
					foreach (Polyline item in list)
					{
						if (!item.IsDegenerate)
						{
							any = true;
							yield return new RowCurve
							{
								Curve = item,
								Index = k
							};
						}
					}
					if (!any)
					{
						break;
					}
				}
				yield break;
			}
			float sideSign = (guide.IsClosed ? Offset.InwardSign(guide) : 1f);
			int count = Math.Max(1, request.Rows);
			for (int k = 0; k < count; k++)
			{
				int k2 = request.Side switch
				{
					RowSide.Left => k, 
					RowSide.Right => -k, 
					_ => k - (count - 1) / 2, 
				};
				if (k2 == 0)
				{
					yield return new RowCurve
					{
						Curve = guide,
						Index = 0
					};
					continue;
				}
				foreach (Polyline item2 in Offset.OffsetPolyline(guide, sideSign * rowSpacing * (float)k2, request.Resolution))
				{
					if (!item2.IsDegenerate)
					{
						yield return new RowCurve
						{
							Curve = item2,
							Index = k2
						};
					}
				}
			}
		}
	}
	public sealed class Polyline
	{
		private readonly List<Vec2> _points;

		private readonly float[] _cumulative;

		public bool IsClosed { get; }

		public IReadOnlyList<Vec2> Points => _points;

		public int Count => _points.Count;

		public float Length { get; }

		public bool IsDegenerate
		{
			get
			{
				if (_points.Count >= 2)
				{
					return Length < 1E-05f;
				}
				return true;
			}
		}

		private int EdgeCount => _cumulative.Length - 1;

		public Polyline(IEnumerable<Vec2> points, bool closed)
		{
			_points = RemoveDuplicates(points);
			IsClosed = closed && _points.Count >= 3;
			int num = (IsClosed ? _points.Count : Math.Max(0, _points.Count - 1));
			_cumulative = new float[num + 1];
			float num2 = 0f;
			for (int i = 0; i < num; i++)
			{
				Vec2 a = _points[i];
				Vec2 b = _points[(i + 1) % _points.Count];
				num2 += Vec2.Distance(a, b);
				_cumulative[i + 1] = num2;
			}
			Length = num2;
		}

		private static List<Vec2> RemoveDuplicates(IEnumerable<Vec2> points)
		{
			List<Vec2> list = new List<Vec2>();
			foreach (Vec2 point in points)
			{
				if (list.Count == 0 || Vec2.SqrDistance(list[list.Count - 1], point) > 1E-08f)
				{
					list.Add(point);
				}
			}
			if (list.Count > 2 && Vec2.SqrDistance(list[0], list[list.Count - 1]) <= 1E-08f)
			{
				list.RemoveAt(list.Count - 1);
			}
			return list;
		}

		public Vec2 PointAt(float distance)
		{
			if (_points.Count == 0)
			{
				return Vec2.Zero;
			}
			if (_points.Count == 1 || Length <= 0f)
			{
				return _points[0];
			}
			distance = NormalizeDistance(distance);
			int num = FindEdge(distance);
			float num2 = _cumulative[num];
			float num3 = _cumulative[num + 1] - num2;
			float t = ((num3 <= 0f) ? 0f : ((distance - num2) / num3));
			Vec2 a = _points[num];
			Vec2 b = _points[(num + 1) % _points.Count];
			return Vec2.Lerp(a, b, t);
		}

		public Vec2 TangentAt(float distance)
		{
			if (_points.Count < 2 || Length <= 0f)
			{
				return new Vec2(1f, 0f);
			}
			distance = NormalizeDistance(distance);
			int num = FindEdge(distance);
			Vec2 vec = EdgeDirection(num);
			float num2 = _cumulative[num];
			float num3 = _cumulative[num + 1];
			if (distance - num2 < 0.001f && (IsClosed || num > 0))
			{
				int edge = (num - 1 + EdgeCount) % EdgeCount;
				vec = (vec + EdgeDirection(edge)).Normalized;
			}
			else if (num3 - distance < 0.001f && (IsClosed || num < EdgeCount - 1))
			{
				int edge2 = (num + 1) % EdgeCount;
				vec = (vec + EdgeDirection(edge2)).Normalized;
			}
			if (!(vec.SqrLength < 1E-08f))
			{
				return vec;
			}
			return EdgeDirection(num);
		}

		public Vec2 NormalAt(float distance)
		{
			return TangentAt(distance).PerpLeft;
		}

		private Vec2 EdgeDirection(int edge)
		{
			Vec2 vec = _points[edge];
			return (_points[(edge + 1) % _points.Count] - vec).Normalized;
		}

		private float NormalizeDistance(float distance)
		{
			if (IsClosed)
			{
				distance %= Length;
				if (distance < 0f)
				{
					distance += Length;
				}
				return distance;
			}
			if (distance < 0f)
			{
				return 0f;
			}
			if (distance > Length)
			{
				return Length;
			}
			return distance;
		}

		private int FindEdge(float distance)
		{
			int num = 0;
			int num2 = _cumulative.Length - 2;
			while (num < num2)
			{
				int num3 = (num + num2 + 1) / 2;
				if (_cumulative[num3] <= distance)
				{
					num = num3;
				}
				else
				{
					num2 = num3 - 1;
				}
			}
			return num;
		}

		public float SignedArea()
		{
			if (_points.Count < 3)
			{
				return 0f;
			}
			float num = 0f;
			for (int i = 0; i < _points.Count; i++)
			{
				Vec2 a = _points[i];
				Vec2 b = _points[(i + 1) % _points.Count];
				num += Vec2.Cross(a, b);
			}
			return num * 0.5f;
		}

		public float DistanceTo(Vec2 p)
		{
			if (_points.Count == 0)
			{
				return float.PositiveInfinity;
			}
			if (_points.Count == 1)
			{
				return Vec2.Distance(p, _points[0]);
			}
			float num = float.PositiveInfinity;
			for (int i = 0; i < EdgeCount; i++)
			{
				Vec2 a = _points[i];
				Vec2 b = _points[(i + 1) % _points.Count];
				float num2 = DistancePointSegment(p, a, b);
				if (num2 < num)
				{
					num = num2;
				}
			}
			return num;
		}

		public static float DistancePointSegment(Vec2 p, Vec2 a, Vec2 b)
		{
			Vec2 vec = b - a;
			float sqrLength = vec.SqrLength;
			if (sqrLength <= 1E-12f)
			{
				return Vec2.Distance(p, a);
			}
			float num = Vec2.Dot(p - a, vec) / sqrLength;
			if (num < 0f)
			{
				num = 0f;
			}
			else if (num > 1f)
			{
				num = 1f;
			}
			return Vec2.Distance(p, a + vec * num);
		}

		public List<float> EvenDistances(float spacing)
		{
			List<float> list = new List<float>();
			if (IsDegenerate || spacing <= 0f)
			{
				if (_points.Count > 0)
				{
					list.Add(0f);
				}
				return list;
			}
			if (IsClosed)
			{
				int num = Math.Max(1, (int)Math.Floor(Length / spacing + 0.0001f));
				float num2 = Length / (float)num;
				for (int i = 0; i < num; i++)
				{
					list.Add((float)i * num2);
				}
			}
			else
			{
				int num3 = Math.Max(1, (int)Math.Floor(Length / spacing + 0.0001f));
				float num4 = Length / (float)num3;
				for (int j = 0; j <= num3; j++)
				{
					list.Add(Math.Min(Length, (float)j * num4));
				}
			}
			return list;
		}

		public Polyline Resample(float step)
		{
			if (IsDegenerate || step <= 0f)
			{
				return this;
			}
			List<Vec2> list = new List<Vec2>();
			foreach (float item in EvenDistances(step))
			{
				list.Add(PointAt(item));
			}
			return new Polyline(list, IsClosed);
		}

		public Polyline Densify(float maxEdge)
		{
			if (IsDegenerate || maxEdge <= 0f)
			{
				return this;
			}
			List<Vec2> list = new List<Vec2>();
			for (int i = 0; i < EdgeCount; i++)
			{
				Vec2 a = _points[i];
				Vec2 b = _points[(i + 1) % _points.Count];
				float num = Vec2.Distance(a, b);
				int num2 = Math.Max(1, (int)Math.Ceiling(num / maxEdge));
				for (int j = 0; j < num2; j++)
				{
					list.Add(Vec2.Lerp(a, b, (float)j / (float)num2));
				}
			}
			if (!IsClosed)
			{
				list.Add(_points[_points.Count - 1]);
			}
			return new Polyline(list, IsClosed);
		}
	}
	public sealed class SegmentGrid
	{
		private readonly IReadOnlyList<Vec2> _pts;

		private readonly int _edgeCount;

		private readonly float _cell;

		private readonly Dictionary<long, List<int>> _cells = new Dictionary<long, List<int>>();

		public SegmentGrid(Polyline polyline, float cellSize)
		{
			_pts = polyline.Points;
			_cell = Math.Max(0.05f, cellSize);
			int count = _pts.Count;
			_edgeCount = (polyline.IsClosed ? count : Math.Max(0, count - 1));
			for (int i = 0; i < _edgeCount; i++)
			{
				Vec2 vec = _pts[i];
				Vec2 vec2 = _pts[(i + 1) % count];
				int num = Cell(Math.Min(vec.X, vec2.X));
				int num2 = Cell(Math.Max(vec.X, vec2.X));
				int num3 = Cell(Math.Min(vec.Z, vec2.Z));
				int num4 = Cell(Math.Max(vec.Z, vec2.Z));
				for (int j = num; j <= num2; j++)
				{
					for (int k = num3; k <= num4; k++)
					{
						Bucket(j, k).Add(i);
					}
				}
			}
		}

		public bool AnyCloserThan(Vec2 p, float radius)
		{
			if (radius <= 0f)
			{
				return false;
			}
			int num = (int)Math.Ceiling(radius / _cell);
			int num2 = Cell(p.X);
			int num3 = Cell(p.Z);
			int count = _pts.Count;
			for (int i = num2 - num; i <= num2 + num; i++)
			{
				for (int j = num3 - num; j <= num3 + num; j++)
				{
					if (!_cells.TryGetValue(Key(i, j), out var value))
					{
						continue;
					}
					foreach (int item in value)
					{
						if (Polyline.DistancePointSegment(p, _pts[item], _pts[(item + 1) % count]) < radius)
						{
							return true;
						}
					}
				}
			}
			return false;
		}

		private List<int> Bucket(int x, int z)
		{
			long key = Key(x, z);
			if (!_cells.TryGetValue(key, out var value))
			{
				value = new List<int>(4);
				_cells[key] = value;
			}
			return value;
		}

		private int Cell(float v)
		{
			return (int)Math.Floor(v / _cell);
		}

		private static long Key(int x, int z)
		{
			return ((long)x << 32) ^ (uint)z;
		}
	}
	public readonly struct Vec2 : IEquatable<Vec2>
	{
		public readonly float X;

		public readonly float Z;

		public static readonly Vec2 Zero = new Vec2(0f, 0f);

		public float Length => (float)Math.Sqrt(X * X + Z * Z);

		public float SqrLength => X * X + Z * Z;

		public Vec2 Normalized
		{
			get
			{
				float length = Length;
				if (!(length < 1E-06f))
				{
					return new Vec2(X / length, Z / length);
				}
				return Zero;
			}
		}

		public Vec2 PerpLeft => new Vec2(0f - Z, X);

		public Vec2(float x, float z)
		{
			X = x;
			Z = z;
		}

		public static Vec2 operator +(Vec2 a, Vec2 b)
		{
			return new Vec2(a.X + b.X, a.Z + b.Z);
		}

		public static Vec2 operator -(Vec2 a, Vec2 b)
		{
			return new Vec2(a.X - b.X, a.Z - b.Z);
		}

		public static Vec2 operator *(Vec2 a, float s)
		{
			return new Vec2(a.X * s, a.Z * s);
		}

		public static Vec2 operator *(float s, Vec2 a)
		{
			return new Vec2(a.X * s, a.Z * s);
		}

		public static Vec2 operator /(Vec2 a, float s)
		{
			return new Vec2(a.X / s, a.Z / s);
		}

		public static Vec2 operator -(Vec2 a)
		{
			return new Vec2(0f - a.X, 0f - a.Z);
		}

		public static float Dot(Vec2 a, Vec2 b)
		{
			return a.X * b.X + a.Z * b.Z;
		}

		public static float Cross(Vec2 a, Vec2 b)
		{
			return a.X * b.Z - a.Z * b.X;
		}

		public static float Distance(Vec2 a, Vec2 b)
		{
			return (a - b).Length;
		}

		public static float SqrDistance(Vec2 a, Vec2 b)
		{
			return (a - b).SqrLength;
		}

		public static Vec2 Lerp(Vec2 a, Vec2 b, float t)
		{
			return a + (b - a) * t;
		}

		public bool Equals(Vec2 other)
		{
			if (X == other.X)
			{
				return Z == other.Z;
			}
			return false;
		}

		public override bool Equals(object obj)
		{
			if (obj is Vec2 other)
			{
				return Equals(other);
			}
			return false;
		}

		public override int GetHashCode()
		{
			float x = X;
			int num = x.GetHashCode() * 397;
			x = Z;
			return num ^ x.GetHashCode();
		}

		public override string ToString()
		{
			return $"({X:0.###}, {Z:0.###})";
		}
	}
}