Decompiled source of RunicAgriculture v1.0.0

RunicAgriculture.dll

Decompiled 14 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Runic.Foundation.Core;
using RunicAgriculture.Core;
using RunicAgriculture.Integration;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Runic Agriculture")]
[assembly: AssemblyDescription("Bounded, validated planting patterns and modest crop harvesting for Valheim.")]
[assembly: AssemblyCompany("Chazman")]
[assembly: AssemblyProduct("Runic Agriculture")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: InternalsVisibleTo("RunicAgriculture.Tests")]
[assembly: InternalsVisibleTo("RunicAgriculture.DedicatedHarness")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Runic.Foundation.Core
{
	public static class RunicIdentifier
	{
		public static bool IsValid(string value)
		{
			if (string.IsNullOrEmpty(value) || value.Length > 128)
			{
				return false;
			}
			bool flag = false;
			bool flag2 = false;
			foreach (char c in value)
			{
				if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'))
				{
					flag = true;
					flag2 = false;
					continue;
				}
				switch (c)
				{
				case '-':
					if (!flag || flag2)
					{
						return false;
					}
					flag2 = true;
					break;
				case '.':
					if (!flag || flag2)
					{
						return false;
					}
					flag = false;
					flag2 = false;
					break;
				default:
					return false;
				}
			}
			if (flag)
			{
				return !flag2;
			}
			return false;
		}

		public static string Require(string value, string parameterName)
		{
			if (!IsValid(value))
			{
				throw new ArgumentException("Runic identifiers must be lowercase dot-separated tokens containing only ASCII letters, digits, and internal hyphens.", parameterName);
			}
			return value;
		}
	}
	public sealed class InputChord : IEquatable<InputChord>
	{
		private readonly IReadOnlyList<string> _modifiers;

		public string DeviceId { get; }

		public string PrimaryControl { get; }

		public IReadOnlyList<string> Modifiers => _modifiers;

		public string CanonicalId { get; }

		public InputChord(string deviceId, string primaryControl, IEnumerable<string> modifiers = null)
		{
			DeviceId = RunicIdentifier.Require(deviceId, "deviceId");
			PrimaryControl = RequireControl(primaryControl, "primaryControl");
			string text = CanonicalizeControl(PrimaryControl);
			SortedDictionary<string, string> sortedDictionary = new SortedDictionary<string, string>(StringComparer.Ordinal);
			if (modifiers != null)
			{
				foreach (string modifier in modifiers)
				{
					string text2 = RequireControl(modifier, "modifiers");
					string text3 = CanonicalizeControl(text2);
					if (string.Equals(text3, text, StringComparison.Ordinal))
					{
						throw new ArgumentException("The primary control cannot also be a modifier.", "modifiers");
					}
					if (sortedDictionary.ContainsKey(text3))
					{
						throw new ArgumentException("Duplicate input modifier: " + text2, "modifiers");
					}
					sortedDictionary.Add(text3, text2);
				}
			}
			string[] array = new string[sortedDictionary.Count];
			int num = 0;
			foreach (string value in sortedDictionary.Values)
			{
				array[num++] = value;
			}
			_modifiers = Array.AsReadOnly(array);
			StringBuilder stringBuilder = new StringBuilder(DeviceId).Append(':');
			foreach (string key in sortedDictionary.Keys)
			{
				stringBuilder.Append(key).Append('+');
			}
			stringBuilder.Append(text);
			CanonicalId = stringBuilder.ToString();
		}

		public bool Equals(InputChord other)
		{
			if (other != null)
			{
				return string.Equals(CanonicalId, other.CanonicalId, StringComparison.Ordinal);
			}
			return false;
		}

		public override bool Equals(object obj)
		{
			return Equals(obj as InputChord);
		}

		public override int GetHashCode()
		{
			return StringComparer.Ordinal.GetHashCode(CanonicalId);
		}

		public override string ToString()
		{
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < _modifiers.Count; i++)
			{
				if (i > 0)
				{
					stringBuilder.Append(" + ");
				}
				stringBuilder.Append(_modifiers[i]);
			}
			if (_modifiers.Count > 0)
			{
				stringBuilder.Append(" + ");
			}
			return stringBuilder.Append(PrimaryControl).Append(" [").Append(DeviceId)
				.Append(']')
				.ToString();
		}

		private static string RequireControl(string value, string parameterName)
		{
			if (string.IsNullOrWhiteSpace(value))
			{
				throw new ArgumentException("An input control name is required.", parameterName);
			}
			string text = value.Trim();
			if (text.Length > 64)
			{
				throw new ArgumentException("Input control names cannot exceed 64 characters.", parameterName);
			}
			string text2 = text;
			foreach (char c in text2)
			{
				if (char.IsControl(c) || c == ':' || c == '+' || c == '|')
				{
					throw new ArgumentException("The input control name contains a reserved character.", parameterName);
				}
			}
			if (CanonicalizeControl(text).Length == 0)
			{
				throw new ArgumentException("The input control name has no canonical characters.", parameterName);
			}
			return text;
		}

		private static string CanonicalizeControl(string value)
		{
			StringBuilder stringBuilder = new StringBuilder(value.Length);
			foreach (char c in value)
			{
				if (!char.IsWhiteSpace(c) && c != '_' && c != '-')
				{
					stringBuilder.Append(char.ToLowerInvariant(c));
				}
			}
			return stringBuilder.ToString();
		}
	}
	public sealed class KeybindingDescriptor
	{
		public string ModuleId { get; }

		public string BindingId { get; }

		public string QualifiedId => ModuleId + "/" + BindingId;

		public string DisplayName { get; }

		public InputChord Chord { get; }

		public string Context { get; }

		public KeybindingDescriptor(string moduleId, string bindingId, string displayName, InputChord chord, string context = "gameplay")
		{
			ModuleId = RunicIdentifier.Require(moduleId, "moduleId");
			BindingId = RunicIdentifier.Require(bindingId, "bindingId");
			Context = RunicIdentifier.Require(context, "context");
			if (string.IsNullOrWhiteSpace(displayName))
			{
				throw new ArgumentException("A keybinding display name is required.", "displayName");
			}
			Chord = chord ?? throw new ArgumentNullException("chord");
			DisplayName = displayName.Trim();
		}
	}
	public sealed class KeybindingConflict
	{
		public InputChord Chord { get; }

		public IReadOnlyList<KeybindingDescriptor> Bindings { get; }

		internal KeybindingConflict(InputChord chord, IReadOnlyList<KeybindingDescriptor> bindings)
		{
			Chord = chord;
			Bindings = bindings;
		}
	}
	public enum KeybindingChangeKind
	{
		Registered,
		Unregistered
	}
	public sealed class KeybindingsChangedEventArgs : EventArgs
	{
		public KeybindingChangeKind Kind { get; }

		public KeybindingDescriptor Binding { get; }

		public KeybindingConflict Conflict { get; }

		internal KeybindingsChangedEventArgs(KeybindingChangeKind kind, KeybindingDescriptor binding, KeybindingConflict conflict)
		{
			Kind = kind;
			Binding = binding;
			Conflict = conflict;
		}
	}
	public sealed class KeybindingConflictRegistry
	{
		private sealed class BindingEntry
		{
			internal KeybindingDescriptor Binding { get; }

			internal long Token { get; }

			internal BindingEntry(KeybindingDescriptor binding, long token)
			{
				Binding = binding;
				Token = token;
			}
		}

		private readonly object _sync = new object();

		private readonly Dictionary<string, BindingEntry> _byQualifiedId = new Dictionary<string, BindingEntry>(StringComparer.Ordinal);

		private readonly Dictionary<string, List<BindingEntry>> _byChord = new Dictionary<string, List<BindingEntry>>(StringComparer.Ordinal);

		private long _nextToken;

		public event EventHandler<KeybindingsChangedEventArgs> Changed;

		public KeybindingRegistration Register(KeybindingDescriptor binding)
		{
			if (binding == null)
			{
				throw new ArgumentNullException("binding");
			}
			long token;
			KeybindingConflict conflict;
			lock (_sync)
			{
				if (_byQualifiedId.ContainsKey(binding.QualifiedId))
				{
					throw new InvalidOperationException("A keybinding is already registered as '" + binding.QualifiedId + "'.");
				}
				if (_nextToken == long.MaxValue)
				{
					throw new InvalidOperationException("The keybinding registration token space is exhausted.");
				}
				token = ++_nextToken;
				BindingEntry bindingEntry = new BindingEntry(binding, token);
				_byQualifiedId.Add(binding.QualifiedId, bindingEntry);
				if (!_byChord.TryGetValue(binding.Chord.CanonicalId, out var value))
				{
					value = new List<BindingEntry>();
					_byChord.Add(binding.Chord.CanonicalId, value);
				}
				value.Add(bindingEntry);
				conflict = CreateConflictLocked(binding.Chord.CanonicalId);
			}
			RaiseChanged(new KeybindingsChangedEventArgs(KeybindingChangeKind.Registered, binding, conflict));
			return new KeybindingRegistration(this, binding, token);
		}

		public bool Unregister(string moduleId, string bindingId)
		{
			RunicIdentifier.Require(moduleId, "moduleId");
			RunicIdentifier.Require(bindingId, "bindingId");
			return Unregister(moduleId + "/" + bindingId, (long?)null);
		}

		public bool TryGetBinding(string moduleId, string bindingId, out KeybindingDescriptor binding)
		{
			binding = null;
			if (!RunicIdentifier.IsValid(moduleId) || !RunicIdentifier.IsValid(bindingId))
			{
				return false;
			}
			lock (_sync)
			{
				if (!_byQualifiedId.TryGetValue(moduleId + "/" + bindingId, out var value))
				{
					return false;
				}
				binding = value.Binding;
				return true;
			}
		}

		public IReadOnlyList<KeybindingDescriptor> GetBindings()
		{
			lock (_sync)
			{
				List<KeybindingDescriptor> list = new List<KeybindingDescriptor>(_byQualifiedId.Count);
				foreach (BindingEntry value in _byQualifiedId.Values)
				{
					list.Add(value.Binding);
				}
				list.Sort((KeybindingDescriptor left, KeybindingDescriptor right) => StringComparer.Ordinal.Compare(left.QualifiedId, right.QualifiedId));
				return list.AsReadOnly();
			}
		}

		public IReadOnlyList<KeybindingConflict> GetConflicts()
		{
			lock (_sync)
			{
				List<KeybindingConflict> list = new List<KeybindingConflict>();
				foreach (string key in _byChord.Keys)
				{
					KeybindingConflict keybindingConflict = CreateConflictLocked(key);
					if (keybindingConflict != null)
					{
						list.Add(keybindingConflict);
					}
				}
				list.Sort((KeybindingConflict left, KeybindingConflict right) => StringComparer.Ordinal.Compare(left.Chord.CanonicalId, right.Chord.CanonicalId));
				return list.AsReadOnly();
			}
		}

		public IReadOnlyList<KeybindingConflict> GetConflictsFor(string moduleId)
		{
			RunicIdentifier.Require(moduleId, "moduleId");
			IReadOnlyList<KeybindingConflict> conflicts = GetConflicts();
			List<KeybindingConflict> list = new List<KeybindingConflict>();
			foreach (KeybindingConflict item in conflicts)
			{
				foreach (KeybindingDescriptor binding in item.Bindings)
				{
					if (string.Equals(binding.ModuleId, moduleId, StringComparison.Ordinal))
					{
						list.Add(item);
						break;
					}
				}
			}
			return list.AsReadOnly();
		}

		internal bool IsActive(string qualifiedId, long token)
		{
			lock (_sync)
			{
				BindingEntry value;
				return _byQualifiedId.TryGetValue(qualifiedId, out value) && value.Token == token;
			}
		}

		internal bool Unregister(string qualifiedId, long? requiredToken)
		{
			KeybindingDescriptor binding;
			KeybindingConflict conflict;
			lock (_sync)
			{
				if (!_byQualifiedId.TryGetValue(qualifiedId, out var entry) || (requiredToken.HasValue && entry.Token != requiredToken.Value))
				{
					return false;
				}
				binding = entry.Binding;
				_byQualifiedId.Remove(qualifiedId);
				List<BindingEntry> list = _byChord[binding.Chord.CanonicalId];
				list.RemoveAll((BindingEntry candidate) => candidate.Token == entry.Token);
				if (list.Count == 0)
				{
					_byChord.Remove(binding.Chord.CanonicalId);
				}
				conflict = CreateConflictLocked(binding.Chord.CanonicalId);
			}
			RaiseChanged(new KeybindingsChangedEventArgs(KeybindingChangeKind.Unregistered, binding, conflict));
			return true;
		}

		private KeybindingConflict CreateConflictLocked(string chordId)
		{
			if (!_byChord.TryGetValue(chordId, out var value) || value.Count < 2)
			{
				return null;
			}
			List<KeybindingDescriptor> list = new List<KeybindingDescriptor>(value.Count);
			foreach (BindingEntry item in value)
			{
				list.Add(item.Binding);
			}
			list.Sort((KeybindingDescriptor left, KeybindingDescriptor right) => StringComparer.Ordinal.Compare(left.QualifiedId, right.QualifiedId));
			return new KeybindingConflict(list[0].Chord, list.AsReadOnly());
		}

		private void RaiseChanged(KeybindingsChangedEventArgs arguments)
		{
			EventHandler<KeybindingsChangedEventArgs> eventHandler = this.Changed;
			if (eventHandler == null)
			{
				return;
			}
			Delegate[] invocationList = eventHandler.GetInvocationList();
			for (int i = 0; i < invocationList.Length; i++)
			{
				EventHandler<KeybindingsChangedEventArgs> eventHandler2 = (EventHandler<KeybindingsChangedEventArgs>)invocationList[i];
				try
				{
					eventHandler2(this, arguments);
				}
				catch (Exception)
				{
				}
			}
		}
	}
	public sealed class KeybindingRegistration : IDisposable
	{
		private readonly KeybindingConflictRegistry _registry;

		private readonly long _token;

		public KeybindingDescriptor Binding { get; }

		public bool IsActive => _registry.IsActive(Binding.QualifiedId, _token);

		internal KeybindingRegistration(KeybindingConflictRegistry registry, KeybindingDescriptor binding, long token)
		{
			_registry = registry;
			Binding = binding;
			_token = token;
		}

		public void Dispose()
		{
			_registry.Unregister(Binding.QualifiedId, _token);
		}
	}
}
namespace RunicAgriculture
{
	internal static class AgricultureConfig
	{
		internal const int HardMaximumPreview = 1600;

		internal const int HardMaximumHarvest = 25;

		internal static ConfigEntry<bool> Enabled { get; private set; }

		internal static ConfigEntry<PlantPattern> Pattern { get; private set; }

		internal static ConfigEntry<AgricultureAlignment> Alignment { get; private set; }

		internal static ConfigEntry<int> Rows { get; private set; }

		internal static ConfigEntry<int> Columns { get; private set; }

		internal static ConfigEntry<float> Spacing { get; private set; }

		internal static ConfigEntry<float> LegacyCircleRadius { get; private set; }

		internal static ConfigEntry<bool> MirrorShape { get; private set; }

		internal static ConfigEntry<float> TrapezoidLeftPinch { get; private set; }

		internal static ConfigEntry<float> TrapezoidRightPinch { get; private set; }

		internal static ConfigEntry<float> NearbySeedChestRange { get; private set; }

		internal static ConfigEntry<InvalidPositionPolicy> InvalidPolicy { get; private set; }

		internal static ConfigEntry<ResourceShortfallPolicy> ResourcePolicy { get; private set; }

		internal static ConfigEntry<float> HarvestRadius { get; private set; }

		internal static ConfigEntry<int> MaximumHarvest { get; private set; }

		internal static ConfigEntry<bool> OfferReplantPreview { get; private set; }

		internal static ConfigEntry<bool> ShowHoverStatus { get; private set; }

		internal static ConfigEntry<bool> ShowContextualControls { get; private set; }

		internal static ConfigEntry<float> ControlBarScale { get; private set; }

		internal static ConfigEntry<bool> VerboseLogging { get; private set; }

		internal static ConfigEntry<KeyboardShortcut> ConfirmPattern { get; private set; }

		internal static ConfigEntry<KeyboardShortcut> CyclePattern { get; private set; }

		internal static ConfigEntry<KeyboardShortcut> AreaHarvest { get; private set; }

		internal static ConfigEntry<KeyboardShortcut> ConfirmReplant { get; private set; }

		internal static ConfigEntry<KeyboardShortcut> IncreaseRows { get; private set; }

		internal static ConfigEntry<KeyboardShortcut> DecreaseRows { get; private set; }

		internal static ConfigEntry<KeyboardShortcut> IncreaseColumns { get; private set; }

		internal static ConfigEntry<KeyboardShortcut> DecreaseColumns { get; private set; }

		internal static ConfigEntry<KeyboardShortcut> ToggleShapeSide { get; private set; }

		internal static ConfigEntry<KeyboardShortcut> DecreaseLeftPinch { get; private set; }

		internal static ConfigEntry<KeyboardShortcut> IncreaseLeftPinch { get; private set; }

		internal static ConfigEntry<KeyboardShortcut> DecreaseRightPinch { get; private set; }

		internal static ConfigEntry<KeyboardShortcut> IncreaseRightPinch { get; private set; }

		internal static ConfigEntry<bool> ControllerEnabled { get; private set; }

		internal static ConfigEntry<ValheimControllerAction> ControllerModifier { get; private set; }

		internal static ConfigEntry<ValheimControllerAction> ControllerConfirm { get; private set; }

		internal static ConfigEntry<ValheimControllerAction> ControllerCycle { get; private set; }

		internal static ConfigEntry<ValheimControllerAction> ControllerAreaHarvest { get; private set; }

		internal static ConfigEntry<ValheimControllerAction> ControllerPreviousEditorField { get; private set; }

		internal static ConfigEntry<ValheimControllerAction> ControllerNextEditorField { get; private set; }

		internal static ConfigEntry<ValheimControllerAction> ControllerDecreaseEditorValue { get; private set; }

		internal static ConfigEntry<ValheimControllerAction> ControllerIncreaseEditorValue { get; private set; }

		internal static ConfigEntry<bool> LegacyCircleRadiusMigrationApplied { get; private set; }

		internal static ConfigEntry<bool> GridAndCompactHudMigrationApplied { get; private set; }

		internal static void Bind(ConfigFile config)
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Expected O, but got Unknown
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Expected O, but got Unknown
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Expected O, but got Unknown
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Expected O, but got Unknown
			//IL_0222: Unknown result type (might be due to invalid IL or missing references)
			//IL_022c: Expected O, but got Unknown
			//IL_025a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0264: Expected O, but got Unknown
			//IL_0292: Unknown result type (might be due to invalid IL or missing references)
			//IL_029c: Expected O, but got Unknown
			//IL_0300: Unknown result type (might be due to invalid IL or missing references)
			//IL_030a: Expected O, but got Unknown
			//IL_032e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0338: Expected O, but got Unknown
			//IL_03b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c1: Expected O, but got Unknown
			//IL_0417: Unknown result type (might be due to invalid IL or missing references)
			//IL_0430: Unknown result type (might be due to invalid IL or missing references)
			//IL_0435: Unknown result type (might be due to invalid IL or missing references)
			//IL_0438: Unknown result type (might be due to invalid IL or missing references)
			//IL_0442: Invalid comparison between Unknown and I4
			//IL_046c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0449: Unknown result type (might be due to invalid IL or missing references)
			//IL_044e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0491: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0529: Unknown result type (might be due to invalid IL or missing references)
			//IL_0563: Unknown result type (might be due to invalid IL or missing references)
			//IL_059d: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_060e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0645: Unknown result type (might be due to invalid IL or missing references)
			//IL_067c: Unknown result type (might be due to invalid IL or missing references)
			//IL_06b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_06ea: Unknown result type (might be due to invalid IL or missing references)
			Enabled = config.Bind<bool>("General", "Enabled", true, "Enable Runic planting previews, explicit batch actions, and read-only status text.");
			Pattern = config.Bind<PlantPattern>("Planting Pattern", "Pattern", PlantingGridPolicy.DefaultPattern, "Preview shape: Row, Grid, Circle, Star, RightTriangle, HalfCircle, or Trapezoid. The cycle shortcut changes it live.");
			Alignment = config.Bind<AgricultureAlignment>("Planting Pattern", "Alignment", AgricultureAlignment.PlayerHeading, "Align to player heading, world axes, or the nearest two matching crops.");
			Rows = config.Bind<int>("Planting Pattern", "Rows", 5, new ConfigDescription("Forward footprint in planting rows for every shape except Row. Change it live with the row controls.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 256), Array.Empty<object>()));
			Columns = config.Bind<int>("Planting Pattern", "ColumnsOrPoints", 5, new ConfigDescription("Side-to-side footprint in planting columns. Change it live with the column controls.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 256), Array.Empty<object>()));
			Spacing = config.Bind<float>("Planting Pattern", "SpacingMeters", 1.5f, new ConfigDescription("Center-to-center spacing for every generated shape.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 6f), Array.Empty<object>()));
			LegacyCircleRadius = config.Bind<float>("Planting Pattern", "CircleRadiusMeters", 3f, new ConfigDescription("Legacy migration-only circle radius. Live Circle size now uses Rows and Columns; this value is never reapplied after the migration marker is set.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 12f), Array.Empty<object>()));
			LegacyCircleRadiusMigrationApplied = config.Bind<bool>("Migrations", "LegacyCircleRadiusMappedToRowsAndColumns", false, "Internal one-time migration marker. Custom row/column dimensions take precedence over the legacy radius.");
			if (!LegacyCircleRadiusMigrationApplied.Value)
			{
				bool saveOnConfigSet = config.SaveOnConfigSet;
				try
				{
					config.SaveOnConfigSet = false;
					if (ShouldMapLegacyCircleRadius(migrationApplied: false, Rows.Value, Columns.Value))
					{
						int num = LegacyCircleDimension(LegacyCircleRadius.Value, Spacing.Value);
						if (Rows.Value != num)
						{
							Rows.Value = num;
						}
						if (Columns.Value != num)
						{
							Columns.Value = num;
						}
					}
					LegacyCircleRadiusMigrationApplied.Value = true;
					config.Save();
				}
				finally
				{
					config.SaveOnConfigSet = saveOnConfigSet;
				}
			}
			MirrorShape = config.Bind<bool>("Planting Pattern", "MirrorShape", false, "Switch the side used by RightTriangle and HalfCircle, or mirror an asymmetric Trapezoid.");
			TrapezoidLeftPinch = config.Bind<float>("Planting Pattern", "TrapezoidLeftPinch", 0.5f, new ConfigDescription("How far the unmirrored trapezoid's left front edge tapers inward (0 = straight, 1 = center).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			TrapezoidRightPinch = config.Bind<float>("Planting Pattern", "TrapezoidRightPinch", 0.5f, new ConfigDescription("How far the unmirrored trapezoid's right front edge tapers inward (0 = straight, 1 = center).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			NearbySeedChestRange = config.Bind<float>("Planting Resources", "NearbyChestRangeMeters", 30f, new ConfigDescription("Player-centered range for eligible nearby chests that may supply planting resources. Personal inventory is always consumed first. Static locally-owned chests only; access and wards are rechecked before mutation.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 30f), Array.Empty<object>()));
			InvalidPolicy = config.Bind<InvalidPositionPolicy>("Confirmation", "InvalidPositionPolicy", InvalidPositionPolicy.SkipInvalid, "Skip amber/gray terrain or spacing failures, or block the entire confirmation before the first plant.");
			ResourcePolicy = config.Bind<ResourceShortfallPolicy>("Confirmation", "ResourceShortfallPolicy", ResourceShortfallPolicy.TruncatePredictably, "Stop cleanly at the combined personal-and-nearby-chest per-cell resource budget, or block first. A confirmed left-click batch uses one normal stamina/tool-durability action.");
			HarvestRadius = config.Bind<float>("Harvest", "RadiusMeters", 4f, new ConfigDescription("Area-harvest radius for the exact same registered Pickable prefab.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 8f), Array.Empty<object>()));
			MaximumHarvest = config.Bind<int>("Harvest", "MaximumPlants", 25, new ConfigDescription("Maximum Pickable requests in one area-harvest batch. Hard-capped at 25.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 25), Array.Empty<object>()));
			OfferReplantPreview = config.Bind<bool>("Harvest", "OfferConfirmedReplant", true, "After bounded area harvest, remember successful positions and offer replant ghosts only for matching crops in authorized planting areas; the cultivator does not need to be equipped and planting is never automatic.");
			ShowHoverStatus = config.Bind<bool>("Status", "ShowBeeAndCropStatus", true, "Append concise read-only honey, bee happiness, crop maturity, and growth-failure status.");
			ShowContextualControls = config.Bind<bool>("Status", "ShowContextualControls", true, "Replace the vanilla bottom build hints with the Agriculture control bar while a crop preview is active.");
			ControlBarScale = config.Bind<float>("Status", "ControlBarScale", 0.9f, new ConfigDescription("Scale Valheim's compact bottom Agriculture build-hint panel.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.75f, 1.75f), Array.Empty<object>()));
			GridAndCompactHudMigrationApplied = config.Bind<bool>("Migrations", "GridAndCompactBottomHudApplied", false, "Internal one-time migration marker. Resets the prior shape to the basic Grid and selects the compact native HUD scale once.");
			ApplyGridAndCompactHudMigration(config);
			VerboseLogging = config.Bind<bool>("Diagnostics", "VerboseLogging", false, "Log input routing, preview state changes, denials, and action results for troubleshooting.");
			ConfirmPattern = config.Bind<KeyboardShortcut>("Controls", "ConfirmPattern", new KeyboardShortcut((KeyCode)323, Array.Empty<KeyCode>()), "Informational binding for ordinary left-click planting; the live Valheim Attack action confirms the displayed pattern.");
			KeyboardShortcut value = ConfirmPattern.Value;
			if ((int)((KeyboardShortcut)(ref value)).MainKey == 323)
			{
				value = ConfirmPattern.Value;
				if (!((KeyboardShortcut)(ref value)).Modifiers.Any())
				{
					goto IL_0476;
				}
			}
			ConfirmPattern.Value = new KeyboardShortcut((KeyCode)323, Array.Empty<KeyCode>());
			goto IL_0476;
			IL_0476:
			CyclePattern = config.Bind<KeyboardShortcut>("Controls", "CyclePattern", new KeyboardShortcut((KeyCode)111, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Cycle Row, Grid, Circle, Star, RightTriangle, HalfCircle, and Trapezoid while holding a plant with the cultivator.");
			AreaHarvest = config.Bind<KeyboardShortcut>("Controls", "AreaHarvestModifierInteract", new KeyboardShortcut((KeyCode)101, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Modifier-interact an available Pickable to harvest nearby objects of that exact registered prefab.");
			ConfirmReplant = config.Bind<KeyboardShortcut>("Controls", "ConfirmReplant", new KeyboardShortcut((KeyCode)116, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Explicitly confirm a pending replant preview while the matching crop is selected.");
			IncreaseRows = config.Bind<KeyboardShortcut>("Pattern Editing Controls", "IncreaseRows", new KeyboardShortcut((KeyCode)273, (KeyCode[])(object)new KeyCode[2]
			{
				(KeyCode)308,
				(KeyCode)304
			}), "Add one forward row to the live planting preview.");
			DecreaseRows = config.Bind<KeyboardShortcut>("Pattern Editing Controls", "DecreaseRows", new KeyboardShortcut((KeyCode)274, (KeyCode[])(object)new KeyCode[2]
			{
				(KeyCode)308,
				(KeyCode)304
			}), "Remove one forward row from the live planting preview.");
			IncreaseColumns = config.Bind<KeyboardShortcut>("Pattern Editing Controls", "IncreaseColumns", new KeyboardShortcut((KeyCode)275, (KeyCode[])(object)new KeyCode[2]
			{
				(KeyCode)308,
				(KeyCode)304
			}), "Add one side-to-side column to the live planting preview.");
			DecreaseColumns = config.Bind<KeyboardShortcut>("Pattern Editing Controls", "DecreaseColumns", new KeyboardShortcut((KeyCode)276, (KeyCode[])(object)new KeyCode[2]
			{
				(KeyCode)308,
				(KeyCode)304
			}), "Remove one side-to-side column from the live planting preview.");
			ToggleShapeSide = config.Bind<KeyboardShortcut>("Pattern Editing Controls", "ToggleShapeSide", new KeyboardShortcut((KeyCode)108, (KeyCode[])(object)new KeyCode[2]
			{
				(KeyCode)308,
				(KeyCode)304
			}), "Switch RightTriangle/HalfCircle sides or mirror an asymmetric Trapezoid.");
			DecreaseLeftPinch = config.Bind<KeyboardShortcut>("Pattern Editing Controls", "DecreaseLeftTrapezoidPinch", new KeyboardShortcut((KeyCode)91, (KeyCode[])(object)new KeyCode[2]
			{
				(KeyCode)308,
				(KeyCode)304
			}), "Widen the unmirrored trapezoid's left front edge by one step.");
			IncreaseLeftPinch = config.Bind<KeyboardShortcut>("Pattern Editing Controls", "IncreaseLeftTrapezoidPinch", new KeyboardShortcut((KeyCode)93, (KeyCode[])(object)new KeyCode[2]
			{
				(KeyCode)308,
				(KeyCode)304
			}), "Pinch the unmirrored trapezoid's left front edge inward by one step.");
			DecreaseRightPinch = config.Bind<KeyboardShortcut>("Pattern Editing Controls", "DecreaseRightTrapezoidPinch", new KeyboardShortcut((KeyCode)59, (KeyCode[])(object)new KeyCode[2]
			{
				(KeyCode)308,
				(KeyCode)304
			}), "Widen the unmirrored trapezoid's right front edge by one step.");
			IncreaseRightPinch = config.Bind<KeyboardShortcut>("Pattern Editing Controls", "IncreaseRightTrapezoidPinch", new KeyboardShortcut((KeyCode)39, (KeyCode[])(object)new KeyCode[2]
			{
				(KeyCode)308,
				(KeyCode)304
			}), "Pinch the unmirrored trapezoid's right front edge inward by one step.");
			ControllerEnabled = config.Bind<bool>("Controller Controls", "Enabled", true, "Enable contextual controller chords through Valheim's ZInput action mappings.");
			ControllerModifier = config.Bind<ValheimControllerAction>("Controller Controls", "ModifierAction", ValheimControllerAction.JoyAltKeys, "Valheim controller action held as the Runic modifier.");
			ControllerConfirm = config.Bind<ValheimControllerAction>("Controller Controls", "ConfirmAction", ValheimControllerAction.JoyPlace, "Valheim controller action pressed with the modifier to confirm a visible pattern or matching replant preview.");
			ControllerCycle = config.Bind<ValheimControllerAction>("Controller Controls", "CyclePatternAction", ValheimControllerAction.JoyPrevSnap, "Valheim controller action pressed with the modifier to cycle the planting pattern.");
			ControllerAreaHarvest = config.Bind<ValheimControllerAction>("Controller Controls", "AreaHarvestAction", ValheimControllerAction.JoyUse, "Valheim controller action pressed with the modifier while interacting with an available Pickable.");
			ControllerPreviousEditorField = config.Bind<ValheimControllerAction>("Controller Pattern Editor", "PreviousEditorFieldAction", ValheimControllerAction.JoyDPadUp, "Unmodified Valheim controller action that selects the previous visible pattern setting only during an active crop preview.");
			ControllerNextEditorField = config.Bind<ValheimControllerAction>("Controller Pattern Editor", "NextEditorFieldAction", ValheimControllerAction.JoyDPadDown, "Unmodified Valheim controller action that selects the next visible pattern setting only during an active crop preview.");
			ControllerDecreaseEditorValue = config.Bind<ValheimControllerAction>("Controller Pattern Editor", "DecreaseEditorValueAction", ValheimControllerAction.JoyDPadLeft, "Unmodified Valheim controller action that decreases the selected pattern setting only during an active crop preview.");
			ControllerIncreaseEditorValue = config.Bind<ValheimControllerAction>("Controller Pattern Editor", "IncreaseEditorValueAction", ValheimControllerAction.JoyDPadRight, "Unmodified Valheim controller action that increases the selected pattern setting only during an active crop preview.");
		}

		internal static AgricultureControllerBindings CurrentControllerBindings()
		{
			return new AgricultureControllerBindings(ControllerModifier.Value, ControllerConfirm.Value, ControllerCycle.Value, ControllerAreaHarvest.Value, ControllerPreviousEditorField.Value, ControllerNextEditorField.Value, ControllerDecreaseEditorValue.Value, ControllerIncreaseEditorValue.Value);
		}

		internal static int LegacyCircleDimension(float radius, float spacing)
		{
			if (float.IsNaN(radius) || float.IsInfinity(radius) || radius <= 0f)
			{
				throw new ArgumentOutOfRangeException("radius");
			}
			if (float.IsNaN(spacing) || float.IsInfinity(spacing) || spacing <= 0f)
			{
				throw new ArgumentOutOfRangeException("spacing");
			}
			int num = (int)Math.Round(radius / spacing, MidpointRounding.AwayFromZero);
			return Math.Max(1, Math.Min(49, num * 2 + 1));
		}

		internal static bool ShouldMapLegacyCircleRadius(bool migrationApplied, int rows, int columns)
		{
			if (!migrationApplied && rows == 5)
			{
				return columns == 5;
			}
			return false;
		}

		private static void ApplyGridAndCompactHudMigration(ConfigFile config)
		{
			if (GridAndCompactHudMigrationApplied.Value)
			{
				return;
			}
			bool saveOnConfigSet = config.SaveOnConfigSet;
			try
			{
				config.SaveOnConfigSet = false;
				Pattern.Value = PlantingGridPolicy.MigrateToDefaultGrid(migrationAlreadyApplied: false, Pattern.Value);
				if (Math.Abs(ControlBarScale.Value - 1f) < 0.0001f)
				{
					ControlBarScale.Value = 0.9f;
				}
				GridAndCompactHudMigrationApplied.Value = true;
				config.Save();
			}
			finally
			{
				config.SaveOnConfigSet = saveOnConfigSet;
			}
		}
	}
	[BepInPlugin("chazman.RunicAgriculture", "Runic Agriculture", "1.0.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "chazman.RunicAgriculture";

		public const string Name = "Runic Agriculture";

		public const string Version = "1.0.0";

		public const string ModuleId = "runic.agriculture";

		public const string ProtocolVersion = "1.0";

		private readonly List<KeybindingRegistration> _keybindingRegistrations = new List<KeybindingRegistration>();

		private readonly KeybindingConflictRegistry _keybindings = new KeybindingConflictRegistry();

		private Harmony _harmony;

		private AgricultureRuntime _runtime;

		internal static Plugin Instance { get; private set; }

		internal AgricultureRuntime Runtime => _runtime;

		internal ManualLogSource Log => ((BaseUnityPlugin)this).Logger;

		private void Awake()
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			Instance = this;
			AgricultureConfig.Bind(((BaseUnityPlugin)this).Config);
			((BaseUnityPlugin)this).Config.SettingChanged += OnSettingChanged;
			try
			{
				AgriculturePatternService patternService = new AgriculturePatternService();
				_runtime = new AgricultureRuntime(patternService, ((BaseUnityPlugin)this).Logger);
				_harmony = new Harmony("chazman.RunicAgriculture");
				_harmony.PatchAll(typeof(Plugin).Assembly);
				RegisterKeybindings();
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Runic Agriculture v1.0.0 ready: bounded pattern preview, native owner-local planting, exact Pickable area harvest, replant offers, and live controls.");
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Runic Agriculture configuration: " + _runtime.ConfigurationSummary()));
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Runic Agriculture controls: " + _runtime.ControlSummary()));
			}
			catch (Exception ex)
			{
				ShutdownRuntime();
				((BaseUnityPlugin)this).Logger.LogError((object)("Runic Agriculture startup failed; vanilla agriculture remains unchanged. " + ex.GetType().Name + ": " + ex.Message));
			}
		}

		private void OnDestroy()
		{
			((BaseUnityPlugin)this).Config.SettingChanged -= OnSettingChanged;
			ShutdownRuntime();
			Instance = null;
		}

		private void OnSettingChanged(object sender, SettingChangedEventArgs arguments)
		{
			try
			{
				object obj;
				if (arguments == null)
				{
					obj = null;
				}
				else
				{
					ConfigEntryBase changedSetting = arguments.ChangedSetting;
					obj = ((changedSetting != null) ? changedSetting.Definition : null);
				}
				ConfigDefinition val = (ConfigDefinition)obj;
				if (val == (ConfigDefinition)null || val.Section == "Controls" || val.Section == "Pattern Editing Controls" || val.Section == "Controller Controls" || val.Section == "Controller Pattern Editor")
				{
					RegisterKeybindings();
				}
				string changedSetting2 = ((val == (ConfigDefinition)null) ? "unknown setting" : (val.Section + "/" + val.Key));
				_runtime?.OnConfigurationChanged(changedSetting2);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Agriculture configuration refresh failed: " + ex.Message));
			}
		}

		private void RegisterKeybindings()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: 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_0098: 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_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			DisposeKeybindings();
			RegisterKeybinding("confirm-pattern", "Confirm planting pattern", AgricultureConfig.ConfirmPattern.Value);
			RegisterKeybinding("cycle-pattern", "Cycle planting pattern", AgricultureConfig.CyclePattern.Value);
			RegisterKeybinding("area-harvest", "Area harvest", AgricultureConfig.AreaHarvest.Value);
			RegisterKeybinding("confirm-replant", "Confirm replant offer", AgricultureConfig.ConfirmReplant.Value);
			RegisterKeybinding("increase-rows", "Increase planting rows", AgricultureConfig.IncreaseRows.Value);
			RegisterKeybinding("decrease-rows", "Decrease planting rows", AgricultureConfig.DecreaseRows.Value);
			RegisterKeybinding("increase-columns", "Increase planting columns", AgricultureConfig.IncreaseColumns.Value);
			RegisterKeybinding("decrease-columns", "Decrease planting columns", AgricultureConfig.DecreaseColumns.Value);
			RegisterKeybinding("toggle-shape-side", "Switch planting shape side", AgricultureConfig.ToggleShapeSide.Value);
			RegisterKeybinding("decrease-left-pinch", "Widen trapezoid left edge", AgricultureConfig.DecreaseLeftPinch.Value);
			RegisterKeybinding("increase-left-pinch", "Pinch trapezoid left edge", AgricultureConfig.IncreaseLeftPinch.Value);
			RegisterKeybinding("decrease-right-pinch", "Widen trapezoid right edge", AgricultureConfig.DecreaseRightPinch.Value);
			RegisterKeybinding("increase-right-pinch", "Pinch trapezoid right edge", AgricultureConfig.IncreaseRightPinch.Value);
			ConfigEntry<bool> controllerEnabled = AgricultureConfig.ControllerEnabled;
			if (controllerEnabled != null && controllerEnabled.Value)
			{
				AgricultureControllerBindings agricultureControllerBindings = AgricultureConfig.CurrentControllerBindings();
				if (!agricultureControllerBindings.TryValidate(out var problem))
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Agriculture controller bindings were not registered: " + problem + "."));
					return;
				}
				RegisterControllerBinding("controller-confirm", "Confirm planting/replant preview (controller)", agricultureControllerBindings.Confirm, agricultureControllerBindings.Modifier);
				RegisterControllerBinding("controller-cycle", "Cycle planting pattern (controller)", agricultureControllerBindings.Cycle, agricultureControllerBindings.Modifier);
				RegisterControllerBinding("controller-area-harvest", "Area harvest (controller)", agricultureControllerBindings.AreaHarvest, agricultureControllerBindings.Modifier);
				RegisterControllerControl("controller-editor-previous", "Previous planting setting (controller)", agricultureControllerBindings.PreviousEditorField);
				RegisterControllerControl("controller-editor-next", "Next planting setting (controller)", agricultureControllerBindings.NextEditorField);
				RegisterControllerControl("controller-editor-decrease", "Decrease planting setting (controller)", agricultureControllerBindings.DecreaseEditorValue);
				RegisterControllerControl("controller-editor-increase", "Increase planting setting (controller)", agricultureControllerBindings.IncreaseEditorValue);
			}
		}

		private void RegisterKeybinding(string id, string name, KeyboardShortcut shortcut)
		{
			//IL_0002: 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_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0)
			{
				return;
			}
			List<string> list = new List<string>();
			IEnumerable<KeyCode> modifiers = ((KeyboardShortcut)(ref shortcut)).Modifiers;
			if (modifiers != null)
			{
				foreach (KeyCode item in modifiers)
				{
					list.Add(((object)item/*cast due to .constrained prefix*/).ToString());
				}
			}
			_keybindingRegistrations.Add(_keybindings.Register(new KeybindingDescriptor("runic.agriculture", id, name, new InputChord("keyboard", ((object)((KeyboardShortcut)(ref shortcut)).MainKey/*cast due to .constrained prefix*/).ToString(), list), "agriculture")));
		}

		private void RegisterControllerBinding(string id, string name, ValheimControllerAction primary, ValheimControllerAction modifier)
		{
			_keybindingRegistrations.Add(_keybindings.Register(new KeybindingDescriptor("runic.agriculture", id, name, new InputChord("controller", primary.ToString(), new string[1] { modifier.ToString() }), "agriculture")));
		}

		private void RegisterControllerControl(string id, string name, ValheimControllerAction primary)
		{
			_keybindingRegistrations.Add(_keybindings.Register(new KeybindingDescriptor("runic.agriculture", id, name, new InputChord("controller", primary.ToString(), Array.Empty<string>()), "agriculture")));
		}

		private void ShutdownRuntime()
		{
			DisposeKeybindings();
			try
			{
				_runtime?.Dispose();
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Agriculture runtime cleanup failed: " + ex.Message));
			}
			_runtime = null;
			try
			{
				Harmony harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
			}
			catch (Exception ex2)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Agriculture Harmony cleanup failed: " + ex2.Message));
			}
			_harmony = null;
		}

		private void DisposeKeybindings()
		{
			for (int num = _keybindingRegistrations.Count - 1; num >= 0; num--)
			{
				try
				{
					_keybindingRegistrations[num].Dispose();
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Keybinding cleanup failed: " + ex.Message));
				}
			}
			_keybindingRegistrations.Clear();
		}
	}
}
namespace RunicAgriculture.Integration
{
	internal readonly struct AgricultureHintRow
	{
		internal string Key { get; }

		internal string Action { get; }

		internal AgricultureHintRow(string key, string action)
		{
			Key = key ?? string.Empty;
			Action = action ?? string.Empty;
		}
	}
	internal sealed class AgricultureControlBarContent
	{
		internal IReadOnlyList<AgricultureHintRow> Rows { get; }

		internal AgricultureControlBarContent(params AgricultureHintRow[] rows)
		{
			Rows = rows ?? Array.Empty<AgricultureHintRow>();
		}
	}
	internal sealed class AgricultureControlBar : IDisposable
	{
		private readonly Dictionary<TMP_Text, string> _originalText = new Dictionary<TMP_Text, string>();

		private GameObject _root;

		private Vector3 _originalScale;

		private bool _hasOriginalScale;

		internal bool Apply(KeyHints hints, AgricultureControlBarContent content, float requestedScale)
		{
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: 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_0059: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = (((Object)(object)hints != (Object)null) ? hints.m_buildHints : null);
			if ((Object)(object)val == (Object)null || content == null || content.Rows.Count == 0)
			{
				Restore();
				return false;
			}
			if (_root != val)
			{
				Restore();
				_root = val;
				_originalScale = val.transform.localScale;
				_hasOriginalScale = true;
				TMP_Text[] componentsInChildren = val.GetComponentsInChildren<TMP_Text>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					if ((Object)(object)componentsInChildren[i] != (Object)null && !_originalText.ContainsKey(componentsInChildren[i]))
					{
						_originalText.Add(componentsInChildren[i], componentsInChildren[i].text ?? string.Empty);
					}
				}
			}
			float num = Mathf.Clamp(requestedScale, 0.75f, 1.75f);
			val.transform.localScale = Vector3.Scale(_originalScale, new Vector3(num, num, num));
			TMP_Text[] array = (TMP_Text[])(object)new TMP_Text[5]
			{
				(TMP_Text)hints.m_buildMenuKey,
				(TMP_Text)hints.m_buildRotateKey,
				(TMP_Text)hints.m_buildAlternativePlacingKey,
				(TMP_Text)hints.m_dodgeKey,
				(TMP_Text)hints.m_cycleSnapKey
			};
			HashSet<TMP_Text> knownKeys = new HashSet<TMP_Text>(array.Where((TMP_Text value) => (Object)(object)value != (Object)null));
			int num2 = 0;
			int num3 = Math.Min(array.Length, content.Rows.Count);
			for (int num4 = 0; num4 < num3; num4++)
			{
				TMP_Text val2 = array[num4];
				if (!((Object)(object)val2 == (Object)null))
				{
					val2.text = content.Rows[num4].Key;
					TMP_Text val3 = FindActionLabel(val2, val.transform, knownKeys);
					if ((Object)(object)val3 != (Object)null)
					{
						val3.text = content.Rows[num4].Action;
					}
					num2++;
				}
			}
			return num2 > 0;
		}

		internal void Restore()
		{
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			foreach (KeyValuePair<TMP_Text, string> item in _originalText)
			{
				if ((Object)(object)item.Key != (Object)null)
				{
					item.Key.text = item.Value;
				}
			}
			if ((Object)(object)_root != (Object)null && _hasOriginalScale)
			{
				_root.transform.localScale = _originalScale;
			}
			_originalText.Clear();
			_root = null;
			_hasOriginalScale = false;
		}

		public void Dispose()
		{
			Restore();
		}

		private static TMP_Text FindActionLabel(TMP_Text key, Transform root, ISet<TMP_Text> knownKeys)
		{
			Transform parent = key.transform.parent;
			while ((Object)(object)parent != (Object)null)
			{
				TMP_Text[] componentsInChildren = ((Component)parent).GetComponentsInChildren<TMP_Text>(true);
				int num = 0;
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					if (knownKeys.Contains(componentsInChildren[i]))
					{
						num++;
					}
				}
				if (num == 1 && componentsInChildren.Length >= 2)
				{
					TMP_Text val = null;
					foreach (TMP_Text val2 in componentsInChildren)
					{
						if (!((Object)(object)val2 == (Object)null) && val2 != key && !knownKeys.Contains(val2) && ((Object)(object)val == (Object)null || (val2.text?.Length ?? 0) > (val.text?.Length ?? 0)))
						{
							val = val2;
						}
					}
					if ((Object)(object)val != (Object)null)
					{
						return val;
					}
				}
				if (parent == root)
				{
					break;
				}
				parent = parent.parent;
			}
			return null;
		}
	}
	internal static class AgricultureInputConsumption
	{
		private static int _sampleFrame = -1;

		private static int _wheelFrame = -1;

		private static int _buildMenuFrame = -1;

		private static int _placeFrame = -1;

		internal static void BeginSample(int frame)
		{
			if (_sampleFrame != frame)
			{
				_sampleFrame = frame;
				_wheelFrame = -1;
				_buildMenuFrame = -1;
				_placeFrame = -1;
			}
		}

		internal static void ConsumeWheel(int frame)
		{
			_wheelFrame = frame;
		}

		internal static void ConsumeBuildMenu(int frame)
		{
			_buildMenuFrame = frame;
		}

		internal static void ConsumePlace(int frame)
		{
			_placeFrame = frame;
		}

		internal static bool ShouldSuppressWheel(int frame)
		{
			return _wheelFrame == frame;
		}

		internal static bool ShouldSuppressBuildMenu(string action, int frame)
		{
			if (_buildMenuFrame == frame)
			{
				return string.Equals(action, "BuildMenu", StringComparison.Ordinal);
			}
			return false;
		}

		internal static bool ShouldSuppressPlace(string action, int frame)
		{
			if (_placeFrame == frame)
			{
				return string.Equals(action, "Attack", StringComparison.Ordinal);
			}
			return false;
		}

		internal static void Reset()
		{
			_sampleFrame = -1;
			_wheelFrame = -1;
			_buildMenuFrame = -1;
			_placeFrame = -1;
		}
	}
	[HarmonyPatch(typeof(ZInput), "GetMouseScrollWheel")]
	internal static class AgricultureConsumedMouseWheelPatch
	{
		[HarmonyPriority(800)]
		[HarmonyBefore(new string[] { "chazman.RunicPrecisionBuildTool" })]
		private static bool Prefix(ref float __result)
		{
			if (!AgricultureInputConsumption.ShouldSuppressWheel(Time.frameCount))
			{
				return true;
			}
			__result = 0f;
			return false;
		}
	}
	[HarmonyPatch(typeof(ZInput), "GetButtonDown", new Type[] { typeof(string) })]
	internal static class AgricultureConsumedBuildMenuPatch
	{
		[HarmonyPriority(800)]
		[HarmonyBefore(new string[] { "chazman.RunicPrecisionBuildTool" })]
		private static bool Prefix(string name, ref bool __result)
		{
			if (!AgricultureInputConsumption.ShouldSuppressBuildMenu(name, Time.frameCount) && !AgricultureInputConsumption.ShouldSuppressPlace(name, Time.frameCount))
			{
				return true;
			}
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(KeyHints), "UpdateHints")]
	internal static class AgricultureBuildHintsReplacementPatch
	{
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		[HarmonyAfter(new string[] { "chazman.RunicPrecisionBuildTool" })]
		private static void Postfix(KeyHints __instance)
		{
			try
			{
				Plugin.Instance?.Runtime?.ApplyBuildHintsReplacement(__instance);
			}
			catch (Exception ex)
			{
				Plugin instance = Plugin.Instance;
				if (instance != null)
				{
					instance.Log.LogError((object)("Agriculture build-hint replacement failed closed: " + ex));
				}
				Plugin.Instance?.Runtime?.DisableForSession("agriculture.placement-failed");
			}
		}
	}
	internal sealed class AgricultureRuntime : IDisposable
	{
		private sealed class MutationScope : IDisposable
		{
			private AgricultureRuntime _owner;

			internal MutationScope(AgricultureRuntime owner)
			{
				_owner = owner;
			}

			public void Dispose()
			{
				AgricultureRuntime agricultureRuntime = Interlocked.Exchange(ref _owner, null);
				if (agricultureRuntime != null)
				{
					Interlocked.Exchange(ref agricultureRuntime._mutationActive, 0);
				}
			}
		}

		private sealed class PreviewSnapshot
		{
			internal string CropId { get; }

			internal Piece Piece { get; }

			internal Quaternion Rotation { get; }

			internal IReadOnlyList<Vector3> Positions { get; }

			internal bool IsReplant { get; }

			internal PreviewSnapshot(string cropId, Piece piece, Quaternion rotation, IReadOnlyList<Vector3> positions, bool isReplant)
			{
				//IL_0015: 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)
				CropId = cropId;
				Piece = piece;
				Rotation = rotation;
				Positions = positions;
				IsReplant = isReplant;
			}
		}

		private sealed class HarvestPickableCandidate
		{
			internal Pickable Pickable { get; }

			internal string PrefabName { get; }

			internal int PrefabHash { get; }

			internal ZDOID ZdoId { get; }

			internal Vector3 Position { get; }

			internal HarvestPickableCandidate(Pickable pickable, string prefabName, int prefabHash, ZDOID zdoId, Vector3 position)
			{
				//IL_001c: 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_0024: Unknown result type (might be due to invalid IL or missing references)
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				Pickable = pickable;
				PrefabName = prefabName;
				PrefabHash = prefabHash;
				ZdoId = zdoId;
				Position = position;
			}
		}

		private const int UnlimitedActions = 1000000;

		private readonly IAgriculturePatternService _patternService;

		private readonly ManualLogSource _log;

		private readonly ValheimPlacementValidator _validator = new ValheimPlacementValidator();

		private readonly PreviewPool _previewPool = new PreviewPool(1600);

		private readonly ReplantConfirmation<Vector3> _replant = new ReplantConfirmation<Vector3>(25);

		private readonly Collider[] _alignmentHits = (Collider[])(object)new Collider[96];

		private readonly Collider[] _harvestHits = (Collider[])(object)new Collider[160];

		private readonly Dictionary<string, string> _matureToPlant = new Dictionary<string, string>(StringComparer.Ordinal);

		private readonly AgricultureControlBar _controlBar = new AgricultureControlBar();

		private PreviewSnapshot _lastPreview;

		private bool _disabledForSession;

		private float _nextPreviewUpdate;

		private bool _controllerActionsVerified;

		private string _controllerPathSignature;

		private string _lastControllerProblem;

		private int _controllerHarvestRequestFrame = -1;

		private ControllerEditorField _controllerEditorField;

		private int _previewValidCount;

		private int _previewGroundValidCount;

		private int _previewTotalCount;

		private string _previewIssueSummary = string.Empty;

		private string _previewLimitSummary = string.Empty;

		private string _previewBatchActionSummary = string.Empty;

		private float _patternYawDegrees;

		private int _mutationActive;

		private Piece _seedBudgetPiece;

		private Vector3 _seedBudgetPlayerPosition;

		private int _seedBudgetValue;

		private float _seedBudgetExpiresAt;

		internal bool IsOperational
		{
			get
			{
				if (!_disabledForSession)
				{
					return AgricultureConfig.Enabled?.Value ?? false;
				}
				return false;
			}
		}

		internal AgricultureRuntime(IAgriculturePatternService patternService, ManualLogSource log)
		{
			_patternService = patternService ?? throw new ArgumentNullException("patternService");
			_log = log ?? throw new ArgumentNullException("log");
			ValheimAccess.Verify();
		}

		internal void DisableForSession(string reasonCode)
		{
			if (!_disabledForSession)
			{
				_disabledForSession = true;
				_lastPreview = null;
				_previewPool.Hide();
				RestoreBuildHintsIfOwned();
				AgricultureInputConsumption.Reset();
				Message(Player.m_localPlayer, "Runic Agriculture disabled for this session after an error. Check LogOutput.log.");
				Publish(reasonCode, "Agriculture runtime disabled after an unexpected failure.", "Restart Valheim, then check BepInEx/LogOutput.log before using batch actions.");
			}
		}

		internal string ConfigurationSummary()
		{
			return AgricultureFeedbackText.Configuration(AgricultureConfig.Enabled.Value, AgricultureConfig.Pattern.Value, AgricultureConfig.Rows.Value, AgricultureConfig.Columns.Value, AgricultureConfig.Spacing.Value, 1600, AgricultureConfig.HarvestRadius.Value, AgricultureConfig.MaximumHarvest.Value);
		}

		internal string ControlSummary()
		{
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0208: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			AgricultureControllerBindings agricultureControllerBindings = AgricultureConfig.CurrentControllerBindings();
			string problem;
			string text = ((!AgricultureConfig.ControllerEnabled.Value) ? "controller controls disabled" : (agricultureControllerBindings.TryValidate(out problem) ? ("controller confirm " + agricultureControllerBindings.ConfirmChord + ", cycle " + agricultureControllerBindings.CycleChord + ", harvest " + agricultureControllerBindings.AreaHarvestChord + ", choose setting " + ControllerActionDisplay.Friendly(agricultureControllerBindings.PreviousEditorField) + "/" + ControllerActionDisplay.Friendly(agricultureControllerBindings.NextEditorField) + " unmodified in crop preview, adjust " + ControllerActionDisplay.Friendly(agricultureControllerBindings.DecreaseEditorValue) + "/" + ControllerActionDisplay.Friendly(agricultureControllerBindings.IncreaseEditorValue) + " unmodified in crop preview") : ("controller disabled by invalid bindings (" + problem + ")")));
			return "keyboard plant Left Click, cycle " + ShortcutLabel(AgricultureConfig.CyclePattern.Value) + ", harvest " + ShortcutLabel(AgricultureConfig.AreaHarvest.Value) + ", replant " + ShortcutLabel(AgricultureConfig.ConfirmReplant.Value) + ", rows " + ShortcutLabel(AgricultureConfig.DecreaseRows.Value) + "/" + ShortcutLabel(AgricultureConfig.IncreaseRows.Value) + ", columns " + ShortcutLabel(AgricultureConfig.DecreaseColumns.Value) + "/" + ShortcutLabel(AgricultureConfig.IncreaseColumns.Value) + ", side " + ShortcutLabel(AgricultureConfig.ToggleShapeSide.Value) + ", trapezoid left " + ShortcutLabel(AgricultureConfig.DecreaseLeftPinch.Value) + "/" + ShortcutLabel(AgricultureConfig.IncreaseLeftPinch.Value) + ", right " + ShortcutLabel(AgricultureConfig.DecreaseRightPinch.Value) + "/" + ShortcutLabel(AgricultureConfig.IncreaseRightPinch.Value) + ", live wheel rotate Wheel, rows Alt+Wheel, columns Shift+Wheel, spacing Alt+Shift+Wheel, direct patterns Numpad 1-7, cycle Alt+BuildMenu; " + text + ".";
		}

		internal void OnConfigurationChanged(string changedSetting)
		{
			_lastPreview = null;
			_nextPreviewUpdate = 0f;
			_seedBudgetExpiresAt = 0f;
			_controllerEditorField = ControllerPatternEditor.Normalize(AgricultureConfig.Pattern.Value, _controllerEditorField);
			if (changedSetting != null && (changedSetting.StartsWith("Controller Controls/", StringComparison.Ordinal) || changedSetting.StartsWith("Controller Pattern Editor/", StringComparison.Ordinal)))
			{
				_controllerActionsVerified = false;
				_controllerPathSignature = null;
				_lastControllerProblem = null;
			}
			if (!IsOperational)
			{
				_previewPool.Hide();
			}
			string text = ConfigurationSummary();
			_log.LogInfo((object)("Runic Agriculture configuration changed (" + changedSetting + "): " + text));
			_log.LogInfo((object)("Runic Agriculture controls after configuration change: " + ControlSummary()));
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				Message(localPlayer, "Runic Agriculture updated: " + text + ".");
			}
			AgricultureControllerBindings agricultureControllerBindings = AgricultureConfig.CurrentControllerBindings();
			if (AgricultureConfig.ControllerEnabled.Value && !agricultureControllerBindings.TryValidate(out var problem))
			{
				ReportControllerProblem(localPlayer, problem);
			}
		}

		internal void ApplyBuildHintsReplacement(KeyHints hints)
		{
			GameObject val = (((Object)(object)hints != (Object)null) ? hints.m_buildHints : null);
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)val == (Object)null || !IsPlantingControlContext(localPlayer))
			{
				_controlBar.Restore();
				return;
			}
			if (!val.activeInHierarchy)
			{
				_controlBar.Restore();
				return;
			}
			bool controller = false;
			try
			{
				controller = ZInput.IsGamepadActive();
			}
			catch (Exception)
			{
			}
			_controlBar.Apply(hints, BuildControlBarContent(localPlayer, controller), AgricultureConfig.ControlBarScale.Value);
		}

		private AgricultureControlBarContent BuildControlBarContent(Player player, bool controller)
		{
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			PlantPattern value = AgricultureConfig.Pattern.Value;
			Piece val = ((player != null) ? player.GetSelectedPiece() : null);
			string text = (((Object)(object)val != (Object)null) ? val.m_name : string.Empty);
			try
			{
				if (!string.IsNullOrEmpty(text) && Localization.instance != null)
				{
					text = Localization.instance.Localize(text);
				}
			}
			catch (Exception)
			{
			}
			if (string.IsNullOrWhiteSpace(text))
			{
				text = "Selected crop";
			}
			string text2 = ((value == PlantPattern.Row) ? 1 : AgricultureConfig.Rows.Value) + " rows × " + AgricultureConfig.Columns.Value + " columns";
			string text3 = ((_lastPreview == null) ? "preview loading" : (_previewValidCount + "/" + _previewTotalCount + " ready" + ((_previewGroundValidCount > _previewValidCount) ? ("; red: " + (_previewGroundValidCount - _previewValidCount) + " missing planting resources") : string.Empty) + (string.IsNullOrEmpty(_previewIssueSummary) ? string.Empty : ("; amber: " + _previewIssueSummary)) + (string.IsNullOrEmpty(_previewLimitSummary) ? string.Empty : ("; " + _previewLimitSummary)) + (string.IsNullOrEmpty(_previewBatchActionSummary) ? string.Empty : ("; " + _previewBatchActionSummary))));
			bool flag = IsReplantPreviewForSelection(player);
			if (!controller)
			{
				return new AgricultureControlBarContent(new AgricultureHintRow(flag ? ShortcutLabel(AgricultureConfig.ConfirmReplant.Value) : "Mouse-1", (flag ? "Replant " : "Plant ") + text + " • " + value.ToString() + " • " + text3), new AgricultureHintRow("Alt + Wheel  ↓ / ↑", "Rows  − / +   " + ((value == PlantPattern.Row) ? "1 (Row pattern)" : AgricultureConfig.Rows.Value.ToString(CultureInfo.InvariantCulture))), new AgricultureHintRow("Shift + Wheel  ↓ / ↑", "Columns  − / +   " + AgricultureConfig.Columns.Value), new AgricultureHintRow("Wheel", "Rotate " + _patternYawDegrees.ToString("0.#", CultureInfo.InvariantCulture) + "°  •  spacing " + AgricultureConfig.Spacing.Value.ToString("0.0", CultureInfo.InvariantCulture) + " m"), new AgricultureHintRow("Alt + Mouse-2  /  Num 2", "Pattern " + value.ToString() + " • " + text2 + " • change / select basic Grid"));
			}
			AgricultureControllerBindings agricultureControllerBindings = AgricultureConfig.CurrentControllerBindings();
			string problem = string.Empty;
			if (!AgricultureConfig.ControllerEnabled.Value || !EnsureControllerActions(player) || !agricultureControllerBindings.TryValidate(out problem))
			{
				string action = (AgricultureConfig.ControllerEnabled.Value ? ("Controller Agriculture bindings unavailable" + (string.IsNullOrWhiteSpace(problem) ? "." : (": " + problem + "."))) : "Controller Agriculture controls are disabled.");
				return new AgricultureControlBarContent(new AgricultureHintRow("Mouse-1", "Plant " + text + " — " + text3), new AgricultureHintRow("Alt + Wheel  ↓ / ↑", "Rows  − / +   " + AgricultureConfig.Rows.Value), new AgricultureHintRow("Shift + Wheel  ↓ / ↑", "Columns  − / +   " + AgricultureConfig.Columns.Value), new AgricultureHintRow("Wheel", "Rotate • spacing " + AgricultureConfig.Spacing.Value.ToString("0.0", CultureInfo.InvariantCulture) + " m"), new AgricultureHintRow("Alt + Shift + Wheel", action));
			}
			if (flag)
			{
				return new AgricultureControlBarContent(new AgricultureHintRow(ValheimAccess.ControllerChordLabel(agricultureControllerBindings, agricultureControllerBindings.Confirm), "Replant " + text + " — " + text3));
			}
			_controllerEditorField = ControllerPatternEditor.Normalize(value, _controllerEditorField);
			string text4 = ControllerEditorFieldLabel(value, _controllerEditorField);
			string key = ValheimAccess.ControllerControlLabel(agricultureControllerBindings.PreviousEditorField) + " / " + ValheimAccess.ControllerControlLabel(agricultureControllerBindings.NextEditorField);
			string key2 = ValheimAccess.ControllerControlLabel(agricultureControllerBindings.DecreaseEditorValue) + " / " + ValheimAccess.ControllerControlLabel(agricultureControllerBindings.IncreaseEditorValue);
			return new AgricultureControlBarContent(new AgricultureHintRow(ValheimAccess.ControllerChordLabel(agricultureControllerBindings, agricultureControllerBindings.Confirm), "Plant " + text + " • " + value.ToString() + " • " + text3), new AgricultureHintRow(ValheimAccess.ControllerChordLabel(agricultureControllerBindings, agricultureControllerBindings.Cycle), "Pattern " + value.ToString() + " " + text2), new AgricultureHintRow(key, "Choose " + text4), new AgricultureHintRow(key2, "Adjust " + text4), new AgricultureHintRow("", "Spacing " + AgricultureConfig.Spacing.Value.ToString("0.0", CultureInfo.InvariantCulture) + " m"));
		}

		internal void TickInput(Player player)
		{
			//IL_003c: 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)
			if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || !((Character)player).IsOwner())
			{
				return;
			}
			AgricultureInputConsumption.BeginSample(Time.frameCount);
			AgricultureControllerCollisionGuard.Poll();
			if (!ValheimAccess.PlayerTakesInput(player))
			{
				return;
			}
			bool keyboardCycle = ValheimAccess.ShortcutDown(AgricultureConfig.CyclePattern.Value);
			bool keyboardConfirmPattern = false;
			bool keyboardConfirmReplant = ValheimAccess.ShortcutDown(AgricultureConfig.ConfirmReplant.Value);
			PatternEditAction patternEditAction = ReadPatternEditAction();
			int num = 0;
			bool flag = IsPlantPiece(player.GetSelectedPiece());
			int num2;
			int num3;
			if (flag)
			{
				num2 = (IsPlantingControlContext(player) ? 1 : 0);
				if (num2 != 0)
				{
					num3 = ((!IsReplantPreviewForSelection(player)) ? 1 : 0);
					goto IL_008d;
				}
			}
			else
			{
				num2 = 0;
			}
			num3 = 0;
			goto IL_008d;
			IL_008d:
			bool flag2 = (byte)num3 != 0;
			PlantPattern? plantPattern = null;
			if (num2 != 0)
			{
				bool flag3 = ValheimAccess.KeyHeld((KeyCode)308, (KeyCode)307);
				bool flag4 = ValheimAccess.KeyHeld((KeyCode)304, (KeyCode)303);
				bool flag5 = ValheimAccess.KeyHeld((KeyCode)306, (KeyCode)305);
				if (flag2)
				{
					float num4 = ValheimAccess.MouseWheel();
					AgricultureWheelTarget agricultureWheelTarget = AgricultureWheelRouter.Resolve(flag3, flag4, flag5, num4);
					if (agricultureWheelTarget != AgricultureWheelTarget.None)
					{
						AgricultureInputConsumption.ConsumeWheel(Time.frameCount);
						if (agricultureWheelTarget == AgricultureWheelTarget.Rotation)
						{
							num = ((num4 > 0f) ? 1 : (-1));
						}
						else
						{
							patternEditAction = AgricultureWheelRouter.ToEditAction(agricultureWheelTarget, num4 > 0f);
						}
					}
					if (flag3 && !flag4 && !flag5 && ValheimAccess.ButtonDown("BuildMenu"))
					{
						AgricultureInputConsumption.ConsumeBuildMenu(Time.frameCount);
						keyboardCycle = true;
					}
					if (!flag3 && !flag4 && !flag5 && TryReadDirectPattern(out var pattern))
					{
						plantPattern = pattern;
					}
				}
				if (!flag3 && !flag4 && !flag5 && ValheimAccess.ButtonDown("Attack"))
				{
					AgricultureInputConsumption.ConsumePlace(Time.frameCount);
					if (IsReplantPreviewForSelection(player))
					{
						keyboardConfirmReplant = true;
					}
					else
					{
						keyboardConfirmPattern = true;
					}
				}
			}
			bool num5 = num2 != 0 && EnsureControllerActions(player);
			AgricultureControllerBindings agricultureControllerBindings = AgricultureConfig.CurrentControllerBindings();
			bool flag6 = num5 && ValheimAccess.ControllerButtonHeldRaw(agricultureControllerBindings.Modifier);
			bool flag7 = flag6 && ValheimAccess.ControllerButtonDownRaw(agricultureControllerBindings.Cycle);
			bool flag8 = flag6 && ValheimAccess.ControllerButtonDownRaw(agricultureControllerBindings.Confirm);
			bool flag9 = num5 && !flag6 && flag2 && ValheimAccess.ControllerButtonDownRaw(agricultureControllerBindings.PreviousEditorField);
			bool flag10 = num5 && !flag6 && flag2 && ValheimAccess.ControllerButtonDownRaw(agricultureControllerBindings.NextEditorField);
			bool flag11 = num5 && !flag6 && flag2 && ValheimAccess.ControllerButtonDownRaw(agricultureControllerBindings.DecreaseEditorValue);
			bool flag12 = num5 && !flag6 && flag2 && ValheimAccess.ControllerButtonDownRaw(agricultureControllerBindings.IncreaseEditorValue);
			RoutedAgricultureAction routedAgricultureAction = AgricultureActionRouter.Resolve(new AgricultureInputFrame(keyboardCycle, keyboardConfirmPattern, keyboardConfirmReplant, flag7, flag8, _lastPreview != null && _lastPreview.IsReplant));
			if (patternEditAction == PatternEditAction.None && num == 0 && routedAgricultureAction == RoutedAgricultureAction.None && !plantPattern.HasValue && !flag9 && !flag10 && !flag11 && !flag12)
			{
				return;
			}
			Trace("input routed to " + ((num != 0) ? "RotatePattern" : ((patternEditAction != PatternEditAction.None) ? patternEditAction.ToString() : routedAgricultureAction.ToString())) + ((flag7 || flag8 || flag9 || flag10 || flag11 || flag12) ? " from controller" : " from keyboard") + ".");
			if (!IsOperational)
			{
				Message(player, _disabledForSession ? "Runic Agriculture is disabled for this session after an error; check LogOutput.log." : "Runic Agriculture is disabled in Configuration Manager.");
			}
			else if (!flag)
			{
				Message(player, "Runic agriculture: select a crop with the cultivator first.");
			}
			else
			{
				if ((flag7 && !TryCaptureControllerGesture(player, agricultureControllerBindings.Cycle)) || (flag8 && !TryCaptureControllerGesture(player, agricultureControllerBindings.Confirm)) || (flag9 && !TryCaptureControllerEditorControl(player, agricultureControllerBindings.PreviousEditorField)) || (flag10 && !TryCaptureControllerEditorControl(player, agricultureControllerBindings.NextEditorField)) || (flag11 && !TryCaptureControllerEditorControl(player, agricultureControllerBindings.DecreaseEditorValue)) || (flag12 && !TryCaptureControllerEditorControl(player, agricultureControllerBindings.IncreaseEditorValue)))
				{
					return;
				}
				if (flag9 || flag10)
				{
					_controllerEditorField = ControllerPatternEditor.Move(AgricultureConfig.Pattern.Value, _controllerEditorField, flag10 ? 1 : (-1));
					return;
				}
				if (flag11 || flag12)
				{
					patternEditAction = ControllerPatternEditor.ToEditAction(ControllerPatternEditor.Normalize(AgricultureConfig.Pattern.Value, _controllerEditorField), flag12);
				}
				if (num != 0)
				{
					RotatePattern(player, num);
					return;
				}
				if (patternEditAction != PatternEditAction.None)
				{
					Piece selectedPiece = player.GetSelectedPiece();
					string b = PrefabIdentity.Of((selectedPiece != null) ? ((Component)selectedPiece).gameObject : null);
					if ((_lastPreview != null && _lastPreview.IsReplant) || (_replant.IsPending && string.Equals(_replant.CropId, b, StringComparison.Ordinal)))
					{
						Message(player, "Runic replant uses its saved harvest positions; shape editing resumes on the next normal planting preview.");
					}
					else
					{
						ApplyPatternEdit(player, patternEditAction);
					}
					return;
				}
				if (plantPattern.HasValue)
				{
					SetPattern(player, plantPattern.Value, "numpad");
					return;
				}
				switch (routedAgricultureAction)
				{
				case RoutedAgricultureAction.CyclePattern:
					CyclePattern(player);
					break;
				case RoutedAgricultureAction.ConfirmReplant:
					ConfirmReplant(player);
					break;
				default:
					ConfirmPattern(player);
					break;
				}
			}
		}

		internal bool IsAreaHarvestRequested(Player player, GameObject targetObject)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			Pickable target = FindPickable(targetObject);
			if (!CanOfferAreaHarvest(player, target))
			{
				return false;
			}
			if (ValheimAccess.ShortcutDown(AgricultureConfig.AreaHarvest.Value))
			{
				return true;
			}
			if (!AgricultureConfig.ControllerEnabled.Value || !EnsureControllerActions(player))
			{
				return false;
			}
			AgricultureControllerBindings agricultureControllerBindings = AgricultureConfig.CurrentControllerBindings();
			int num;
			if (ValheimAccess.ControllerButtonHeldRaw(agricultureControllerBindings.Modifier))
			{
				num = (ValheimAccess.ControllerButtonDownRaw(agricultureControllerBindings.AreaHarvest) ? 1 : 0);
				if (num != 0)
				{
					_controllerHarvestRequestFrame = Time.frameCount;
				}
			}
			else
			{
				num = 0;
			}
			return (byte)num != 0;
		}

		internal string HarvestControlHint()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			string text = ShortcutLabel(AgricultureConfig.AreaHarvest.Value);
			if (!AgricultureConfig.ControllerEnabled.Value)
			{
				return text;
			}
			AgricultureControllerBindings agricultureControllerBindings = AgricultureConfig.CurrentControllerBindings();
			if (!EnsureControllerActions(Player.m_localPlayer) || !agricultureControllerBindings.TryValidate(out var _))
			{
				return text;
			}
			return text + " or " + agricultureControllerBindings.AreaHarvestChord;
		}

		internal void UpdatePreview(Player player)
		{
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			if (!IsOperational || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || !((Character)player).IsOwner())
			{
				_lastPreview = null;
				ClearControlBarPreview();
				_previewPool.Hide();
				return;
			}
			Piece selectedPiece = player.GetSelectedPiece();
			GameObject placementGhost = ValheimAccess.GetPlacementGhost(player);
			if (!IsPlantPiece(selectedPiece) || (Object)(object)placementGhost == (Object)null || !placementGhost.activeInHierarchy)
			{
				_lastPreview = null;
				ClearControlBarPreview();
				_previewPool.Hide();
			}
			else
			{
				if (Time.unscaledTime < _nextPreviewUpdate)
				{
					return;
				}
				float num = MinimumSpacingFor(selectedPiece);
				if (AgricultureConfig.Spacing.Value < num)
				{
					AgricultureConfig.Spacing.Value = num;
				}
				string text = PrefabIdentity.Of(((Component)selectedPiece).gameObject);
				Quaternion rotation = ResolveAlignment(player, selectedPiece, placementGhost.transform.position);
				List<Vector3> list = BuildRequestedPositions(player, selectedPiece, placementGhost.transform.position, rotation, text);
				if (list.Count == 0)
				{
					_nextPreviewUpdate = Time.unscaledTime + 0.1f;
					_lastPreview = null;
					ClearControlBarPreview();
					_previewPool.Hide();
					return;
				}
				bool isReplant = _replant.IsPending && string.Equals(_replant.CropId, text, StringComparison.Ordinal);
				List<RuntimePreviewPosition> list2 = BuildValidatedPreview(player, selectedPiece, list, rotation);
				_lastPreview = new PreviewSnapshot(text, selectedPiece, rotation, list, isReplant);
				_previewTotalCount = list2.Count;
				_previewValidCount = 0;
				_previewGroundValidCount = 0;
				Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal);
				for (int i = 0; i < list2.Count; i++)
				{
					RuntimePreviewPosition runtimePreviewPosition = list2[i];
					if (runtimePreviewPosition.IsGroundValid)
					{
						_previewGroundValidCount++;
						if (runtimePreviewPosition.IsValid)
						{
							_previewValidCount++;
						}
					}
					else
					{
						string key = runtimePreviewPosition.ReasonCode ?? "agriculture.placement-failed";
						dictionary.TryGetValue(key, out var value);
						dictionary[key] = value + 1;
					}
				}
				_previewIssueSummary = PreviewIssueSummary(dictionary);
				_previewBatchActionSummary = BatchActionIssue(player, selectedPiece);
				_previewPool.Show(placementGhost, list2);
				_nextPreviewUpdate = Time.unscaledTime + PreviewRefreshInterval(list2.Count);
			}
		}

		internal bool TryAreaHarvest(Player player, GameObject targetObject)
		{
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_033b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0398: 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_0280: 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_028a: 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_02d9: Unknown result type (might be due to invalid IL or missing references)
			if (!IsOperational || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || !((Character)player).IsOwner())
			{
				return false;
			}
			bool flag = _controllerHarvestRequestFrame == Time.frameCount;
			_controllerHarvestRequestFrame = -1;
			Pickable target = FindPickable(targetObject);
			if (!CanOfferAreaHarvest(player, target))
			{
				Trace("area harvest preserved the original interaction because the targeted Pickable was unavailable or access was denied.");
				return false;
			}
			if (!TryCaptureHarvestCandidate(target, out var aimed))
			{
				if ((Object)(object)target != (Object)null)
				{
					Message(player, "Runic harvest: this Pickable is not currently available.");
				}
				Trace("area harvest preserved the original interaction because the targeted Pickable did not satisfy the live network/availability contract.");
				return false;
			}
			if (flag && !TryCaptureControllerGesture(player, AgricultureConfig.CurrentControllerBindings().AreaHarvest))
			{
				return false;
			}
			string prefabName = aimed.PrefabName;
			string text = FindPlantForMature(prefabName);
			float num = Mathf.Clamp(AgricultureConfig.HarvestRadius.Value, 1f, 8f);
			int num2 = Mathf.Clamp(AgricultureConfig.MaximumHarvest.Value, 1, 25);
			int num3 = Physics.OverlapSphereNonAlloc(((Component)target).transform.position, num, _harvestHits, -1, (QueryTriggerInteraction)2);
			HashSet<Pickable> hashSet = new HashSet<Pickable>();
			List<HarvestPickableCandidate> list = new List<HarvestPickableCandidate>(Math.Min(num3 + 1, _harvestHits.Length + 1));
			hashSet.Add(target);
			list.Add(aimed);
			for (int i = 0; i < Math.Min(num3, _harvestHits.Length); i++)
			{
				Collider val = _harvestHits[i];
				_harvestHits[i] = null;
				Pickable val2 = FindPickable(((Object)(object)val != (Object)null) ? ((Component)val).gameObject : null);
				if (!((Object)(object)val2 == (Object)null) && hashSet.Add(val2) && TryCaptureHarvestCandidate(val2, out var candidate) && HarvestPickableBatchPolicy.IsExactPrefab(aimed.PrefabName, aimed.PrefabHash, candidate.PrefabName, candidate.PrefabHash))
				{
					list.Add(candidate);
				}
			}
			list.Sort(delegate(HarvestPickableCandidate left, HarvestPickableCandidate right)
			{
				//IL_000f: Unknown result type (might be due to invalid IL or missing references)
				//IL_001a: Unknown result type (might be due to invalid IL or missing references)
				//IL_001f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				//IL_002d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0032: Unknown result type (might be due to invalid IL or missing references)
				//IL_003b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0040: Unknown result type (might be due to invalid IL or missing references)
				//IL_0057: Unknown result type (might be due to invalid IL or missing references)
				//IL_0062: Unknown result type (might be due to invalid IL or missing references)
				//IL_0067: Unknown result type (might be due to invalid IL or missing references)
				//IL_006c: 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_007a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0083: 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)
				bool leftIsAimed = left.Pickable == target;
				Vector3 val4 = left.Position - aimed.Position;
				float sqrMagnitude = ((Vector3)(ref val4)).sqrMagnitude;
				ZDOID zdoId = left.ZdoId;
				long userID = ((ZDOID)(ref zdoId)).UserID;
				zdoId = left.ZdoId;
				uint iD = ((ZDOID)(ref zdoId)).ID;
				bool rightIsAimed = right.Pickable == target;
				val4 = right.Position - aimed.Position;
				float sqrMagnitude2 = ((Vector3)(ref val4)).sqrMagnitude;
				zdoId = right.ZdoId;
				long userID2 = ((ZDOID)(ref zdoId)).UserID;
				zdoId = right.ZdoId;
				return HarvestPickableBatchPolicy.Compare(leftIsAimed, sqrMagnitude, userID, iD, rightIsAimed, sqrMagnitude2, userID2, ((ZDOID)(ref zdoId)).ID);
			});
			List<Vector3> list2 = new List<Vector3>(num2);
			int num4 = 0;
			bool flag2 = false;
			for (int num5 = 0; num5 < list.Count; num5++)
			{
				if (num4 >= num2)
				{
					break;
				}
				if (!TryCaptureHarvestCandidate(list[num5].Pickable, out var candidate2) || !HarvestPickableBatchPolicy.IsExactPrefab(aimed.PrefabName, aimed.PrefabHash, candidate2.PrefabName, candidate2.PrefabHash))
				{
					continue;
				}
				Vector3 val3 = candidate2.Position - aimed.Position;
				if (!(((Vector3)(ref val3)).sqrMagnitude > num * num))
				{
					if (!PrivateArea.CheckAccess(candidate2.Position, 0f, false, false))
					{
						flag2 |= candidate2.Pickable == target;
						continue;
					}
					candidate2.Pickable.Interact((Humanoid)(object)player, false, false);
					list2.Add(candidate2.Position);
					num4++;
				}
			}
			if (num4 == 0)
			{
				Message(player, "Runic harvest: no currently available, permitted Pickables.");
				Trace("area harvest found candidates but sent no permitted pick requests; " + (flag2 ? "the aimed Pickable was ward-denied." : "the original interaction remains available."));
				return flag2;
			}
			bool authorized = PrivateArea.CheckAccess(aimed.Position, 0f, false, false);
			if (HarvestPickableBatchPolicy.OffersReplant(AgricultureConfig.OfferReplantPreview.Value, authorized, text))
			{
				_replant.Offer(text, list2);
				Message(player, "Runic harvest: " + num4 + " request(s) sent. Select the matching seed, then confirm with " + ShortcutLabel(AgricultureConfig.ConfirmReplant.Value) + (AgricultureConfig.ControllerEnabled.Value ? (" or " + AgricultureConfig.CurrentControllerBindings().ConfirmChord) : string.Empty) + ".");
			}
			else
			{
				_replant.Clear();
				Message(player, "Runic harvest: " + num4 + " request(s) sent.");
			}
			Trace("area harvest sent " + num4 + " owner-validated pick request(s) for '" + prefabName + "'.");
			return true;
		}

		internal bool CanOfferAreaHarvest(Player player, Pickable target)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			if (!IsOperational || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || !((Character)player).IsOwner() || (Object)(object)target == (Object)null || !TryCaptureHarvestCandidate(target, out var candidate))
			{
				return false;
			}
			return HarvestPickableBatchPolicy.AllowsAreaHarvest(PrivateArea.CheckAccess(candidate.Position, 0f, false, false));
		}

		public void Dispose()
		{
			Interlocked.Exchange(ref _mutationActive, 0);
			_lastPreview = null;
			ClearControlBarPreview();
			_replant.Clear();
			_controllerHarvestRequestFrame = -1;
			RestoreBuildHintsIfOwned();
			AgricultureInputConsumption.Reset();
			AgricultureControllerCollisionGuard.Reset();
			_controlBar.Dispose();
			_previewPool.Dispose();
			NearbySeedContainerIndex.Clear();
		}

		private void CyclePattern(Player player)
		{
			SetPattern(player, PatternEditor.Next(AgricultureConfig.Pattern.Value), "cycle");
		}

		private void SetPattern(Player player, PlantPattern pattern, string source)
		{
			if (Enum.IsDefined(typeof(PlantPattern), pattern))
			{
				AgricultureConfig.Pattern.Value = pattern;
				_controllerEditorField = ControllerPatternEditor.Normalize(pattern, _controllerEditorField);
				Trace("planting pattern selected as " + pattern.ToString() + " from " + source + ".");
			}
		}

		private static PatternEditAction ReadPatternEditAction()
		{
			//IL_0005: 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_002b: 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_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			if (ValheimAccess.ShortcutDown(AgricultureConfig.IncreaseRows.Value))
			{
				return PatternEditAction.IncreaseRows;
			}
			if (ValheimAccess.ShortcutDown(AgricultureConfig.DecreaseRows.Value))
			{
				return PatternEditAction.DecreaseRows;
			}
			if (ValheimAccess.ShortcutDown(AgricultureConfig.IncreaseColumns.Value))
			{
				return PatternEditAction.IncreaseColumns;
			}
			if (ValheimAccess.ShortcutDown(AgricultureConfig.DecreaseColumns.Value))
			{
				return PatternEditAction.DecreaseColumns;
			}
			if (ValheimAccess.ShortcutDown(AgricultureConfig.ToggleShapeSide.Value))
			{
				return PatternEditAction.ToggleSide;
			}
			if (ValheimAccess.ShortcutDown(AgricultureConfig.DecreaseLeftPinch.Value))
			{
				return PatternEditAction.DecreaseLeftPinch;
			}
			if (ValheimAccess.ShortcutDown(AgricultureConfig.IncreaseLeftPinch.Value))
			{
				return PatternEditAction.IncreaseLeftPinch;
			}
			if (ValheimAccess.ShortcutDown(AgricultureConfig.DecreaseRightPinch.Value))
			{
				return PatternEditAction.DecreaseRightPinch;
			}
			if (ValheimAccess.ShortcutDown(AgricultureConfig.IncreaseRightPinch.Value))
			{
				return PatternEditAction.IncreaseRightPinch;
			}
			return PatternEditAction.None;
		}

		private void RotatePattern(Player player, int direction)
		{
			float num = ValheimAccess.PlaceRotationDegrees(player);
			if (float.IsNaN(num) || float.IsInfinity(num) || num <= 0f)
			{
				num = 22.5f;
			}
			_patternYawDegrees = Mathf.Repeat(_patternYawDegrees + ((direction > 0) ? num : (0f - num)), 360f);
			_lastPreview = null;
			_nextPreviewUpdate = 0f;
			Message(player, "Runic shape: rotated to " + _patternYawDegrees.ToString("0.#", CultureInfo.InvariantCulture) + "°.");
		}

		private void ApplyPatternEdit(Player player, PatternEditAction action)
		{
			PlantPattern value = AgricultureConfig.Pattern.Value;
			PatternEditState patternEditState = new PatternEditState(AgricultureConfig.Rows.Value, AgricultureConfig.Columns.Value, AgricultureConfig.MirrorShape.Value, AgricultureConfig.TrapezoidLeftPinch.Value, AgricultureConfig.TrapezoidRightPinch.Value, AgricultureConfig.Spacing.Value);
			PatternEditState patternEditState2 = PatternEditor.Apply(value, patternEditState, action);
			float num = MinimumSpacingFor(player.GetSelectedPiece());
			if (patternEditState2.Spacing < (double)num)
			{
				patternEditState2 = new PatternEditState(patternEditState2.Rows, patternEditState2.Columns, patternEditState2.Mirrored, patternEditState2.LeftPinch, patternEditState2.RightPinch, num);
			}
			if (patternEditState2.Equals(patternEditState))
			{
				string text = ((value == PlantPattern.Row && (action == PatternEditAction.IncreaseRows || action == PatternEditAction.DecreaseRows)) ? "Row has one forward row; use the column controls to change its length." : ((action == PatternEditAction.ToggleSide && !PatternEditor.SupportsMirror(value)) ? (value.ToString() + " is symmetric; side switching applies to RightTriangle, HalfCircle, and Trapezoid.") : (((action == PatternEditAction.DecreaseLeftPinch || action == PatternEditAction.IncreaseLeftPinch || action == PatternEditAction.DecreaseRightPinch || action == PatternEditAction.IncreaseRightPinch) && value != PlantPattern.Trapezoid) ? "Independent taper controls apply only to Trapezoid." : ((action == PatternEditAction.DecreaseSpacing && patternEditState.Spacing <= (double)num) ? ("That crop requires at least " + num.ToString("0.0", CultureInfo.InvariantCulture) + "m spacing.") : "That pattern setting is already at its safe limit."))));
				Message(player, "Runic shape: " + text);
				return;
			}
			if (patternEditState2.Rows != patternEditState.Rows)
			{
				AgricultureConfig.Rows.Value = patternEditState2.Rows;
			}
			else if (patternEditState2.Columns != patternEditState.Columns)
			{
				AgricultureConfig.Columns.Value = patternEditState2.Columns;
			}
			else if (patternEditState2.Mirrored != patternEditState.Mirrored)
			{
				AgricultureConfig.MirrorShape.Value = patternEditState2.Mirrored;
			}
			else if (!patternEditState2.LeftPinch.Equals(patternEditState.LeftPinch))
			{
				AgricultureConfig.TrapezoidLeftPinch.Value = (float)patternEditState2.LeftPinch;
			}
			else if (!patternEditState2.RightPinch.Equals(patternEditState.RightPinch))
			{
				AgricultureConfig.TrapezoidRightPinch.Value = (float)patternEditState2.RightPinch;
			}
			else if (!patternEditState2.Spacing.Equals(patternEditState.Spacing))
			{
				AgricultureConfig.Spacing.Value = (float)patternEditState2.Spacing;
			}
			string text2 = value.ToString() + " " + ((value == PlantPattern.Row) ? (patternEditState2.Columns + " columns") : (patternEditState2.Rows + "x" + patternEditState2.Columns));
			if (PatternEditor.SupportsMirror(value))
			{
				text2 = text2 + ", " + PatternEditor.OrientationLabel(value, patternEditState2.Mirrored).ToLowerInvariant();
			}
			if (value == PlantPattern.Trapezoid)
			{
				text2 = text2 + ", taper L " + Mathf.RoundToInt((float)patternEditState2.LeftPinch * 100f) + "% / R " + Mathf.RoundToInt((float)patternEditState2.RightPinch * 100f) + "%";
			}
			text2 = text2 + ", spacing " + patternEditState2.Spacing.ToString("0.0", CultureInfo.InvariantCulture) + "m";
			Message(player, "Runic shape: " + text2 + ".");
			Trace("live pattern edit " + action.ToString() + " applied to " + value.ToString() + ".");
		}

		private void ConfirmPattern(Player player)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			PreviewSnapshot lastPreview = _lastPreview;
			if (lastPreview == null || lastPreview.IsReplant)
			{
				Message(player, (lastPreview != null) ? "Runic planting: this is a replant preview; use the replant confirmation control." : "Runic planting: no preview is ready. Select a crop and aim at plantable ground.");
				Trace("pattern confirmation produced no mutation because no ordinary preview was ready.");
			}
			else
			{
				ExecuteBatch(player, lastPreview.Piece, lastPreview.CropId, lastPreview.Positions, lastPreview.Rotation);
			}
		}

		private void ConfirmReplant(Player player)
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			Piece selectedPiece = player.GetSelectedPiece();
			if (!IsPlantPiece(selectedPiece))
			{
				Message(player, "Runic replant: select the matching crop with the cultivator first.");
				Trace("replant confirmation ignored because no crop is selected.");
				return;
			}
			string text = PrefabIdentity.Of(((Component)selectedPiece).gameObject);
			if (!_replant.TryConfirm(text, out var positions, out var reasonCode))
			{
				Message(player, FriendlyReason(reasonCode));
				Trace("replant confirmation denied: " + reasonCode + ".");
				return;
			}
			Quaternion rotation = Quaternion.Euler(0f, ((Component)player).transform.eulerAngles.y, 0f);
			if (ExecuteBatch(player, selectedPiece, text, positions, rotation) == 0 && positions.Count > 0)
			{
				_replant.Offer(text, positions);
			}
		}

		private int ExecuteBatch(Player player, Piece piece, string expectedCropId, IReadOnlyList<Vector3> requested, Quaternion rotation)
		{
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: 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_01fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: 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_02bf: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || !((Character)player).IsOwner())
			{
				Deny(player, "agriculture.not-authoritative");
				return 0;
			}
			if (IsPlantPiece(piece))
			{
				Piece selectedPiece = player.GetSelectedPiece();
				if (string.Equals(PrefabIdentity.Of((selectedPiece != null) ? ((Component)selectedPiece).gameObject : null), expectedCropId, StringComparison.Ordinal))
				{
					List<PlacementValidationResult> list = new List<PlacementValidationResult>(requested.Count);
					List<Vector3> list2 = new List<Vector3>(requested.Count);
					float requiredSpacing = _validator.RequiredSpacing(piece);
					for (int i = 0; i < requested.Count; i++)
					{
						RuntimePlacementValidation runtimePlacementValidation = _validator.Validate(player, piece, requested[i], rotation);
						PlacementValidationResult item = ApplyPlannedSpacing(runtimePlacementValidation.Result, runtimePlacementValidation.Position, list2, list, requiredSpacing);
						list2.Add(runtimePlacementValidation.Position);
						list.Add(item);
					}
					BatchPlan batchPlan = BatchPlanner.Plan(list, BuildBudget(player, piece), AgricultureConfig.InvalidPolicy.Value, AgricultureConfig.ResourcePolicy.Value);
					if (batchPlan.Blocked)
					{
						Deny(player, batchPlan.ReasonCode);
						return 0;
					}
					ItemData tool = null;
					if (batchPlan.SuccessfulCount > 0 && !CanStartBatchAction(player, piece, out tool, out var reason))
					{
						Deny(player, reason);
						return 0;
					}
					if (!TryEnterMutation("runic.agriculture/plant-batch", out var lease))
					{
						Message(player, "Runic planting paused: another Agriculture batch is in progress.");
						return 0;
					}
					using (lease)
					{
						int num = 0;
						int num2 = 0;
						string text = null;
						string text2 = null;
						for (int j = 0; j < batchPlan.Decisions.Count; j++)
						{
							BatchDecision batchDecision = batchPlan.Decisions[j];
							if (!batchDecision.ShouldPlace)
							{
								num2++;
								if (string.IsNullOrEmpty(text2))
								{
									text2 = batchDecision.ReasonCode;
								}
								if (batchDecision.ReasonCode == "agriculture.no-seeds" || batchDecision.ReasonCode == "agriculture.no-durability" || batchDecision.ReasonCode == "agriculture.no-stamina")
								{
									text = batchDecision.ReasonCode;
									break;
								}
								continue;
							}
							RuntimePlacementValidation runtimePlacementValidation2 = _validator.Validate(player, piece, list2[j], rotation);
							if (!runtimePlacementValidation2.Result.IsValid)
							{
								num2++;
								if (string.IsNullOrEmpty(text2))
								{
									text2 = runtimePlacementValidation2.Result.ReasonCode;
								}
								text = runtimePlacementValidation2.Result.ReasonCode;
								if (AgricultureConfig.InvalidPolicy.Value == InvalidPositionPolicy.BlockConfirmation)
								{
									break;
								}
								continue;
							}
							if (!CanCommitOne(player, piece, expectedCropId, out var reason2))
							{
								text = reason2;
								break;
							}
							try
							{
								bool consumeResources = PlantingGridPolicy.ConsumesSeedResources(IsFreeBuild(piece), player.NoCostCheat());
								if (!NearbySeedResourceService.TryDebitOne(player, piece, AgricultureConfig.NearbySeedChestRange.Value, consumeResources, out var debit))
								{
									text = "agriculture.no-seeds";
									break;
								}
								using (debit)
								{
									player.PlacePiece(piece, runtimePlacementValidation2.Position, rotation, false);
									debit.Complete();
								}
								_seedBudgetExpiresAt = 0f;
								num++;
							}
							catch (Exception ex)
							{
								text = "agriculture.placement-failed";
								_log.LogError((object)("One planting commit failed after revalidation: " + ex));
								break;
							}
						}
						if (num > 0)
						{
							try
							{
								ChargeBatchActionCosts(player, tool);
							}
							catch (Exception ex2)
							{
								text = "agriculture.placement-failed";
								_log.LogError((object)("The planting batch completed but its one stamina/tool action could not be charged cleanly: " + ex2));
							}
						}
						string text3 = "Runic planting: " + num + " planted";
						if (num2 > 0)
						{
							text3 = text3 + ", " + num2 + " skipped";
						}
						string text4 = ((!string.IsNullOrEmpty(text)) ? text : text2);
						if (!string.I